From 491b58e1cafbe41a1c01c4d1b3f7b0bd5f929b38 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 12 Mar 2013 09:40:24 -0700 Subject: [PATCH] Revert "msm: power: Revert LGE power patches" This reverts commit 2f24cb771c35f6f8826ffb595b9eb0d3c049c417. --- arch/arm/mach-msm/lge/mako/board-mako-pmic.c | 2 +- drivers/mfd/pm8018-core.c | 27 +- drivers/mfd/pm8038-core.c | 27 +- drivers/mfd/pm8921-core.c | 50 +- drivers/power/Kconfig | 19 + drivers/power/Makefile | 2 + drivers/power/battery_temp_ctrl.c | 281 ++++++++++++ drivers/power/bq51051b_charger.c | 277 +++++++++++ drivers/power/pm8921-bms.c | 272 ++++++++--- drivers/power/pm8921-charger.c | 432 ++++++++++++++++-- drivers/power/power_supply_sysfs.c | 5 +- drivers/usb/otg/msm_otg.c | 10 +- include/linux/mfd/pm8xxx/core.h | 31 ++ include/linux/mfd/pm8xxx/pm8921-bms.h | 8 + include/linux/mfd/pm8xxx/pm8921-charger.h | 7 + .../linux/platform_data/battery_temp_ctrl.h | 44 ++ include/linux/power/bq51051b_charger.h | 34 ++ include/linux/power_supply.h | 3 + 18 files changed, 1374 insertions(+), 157 deletions(-) create mode 100644 drivers/power/battery_temp_ctrl.c create mode 100644 drivers/power/bq51051b_charger.c create mode 100644 include/linux/platform_data/battery_temp_ctrl.h create mode 100644 include/linux/power/bq51051b_charger.h diff --git a/arch/arm/mach-msm/lge/mako/board-mako-pmic.c b/arch/arm/mach-msm/lge/mako/board-mako-pmic.c index bdb4fc17dd57..8f07a8691268 100644 --- a/arch/arm/mach-msm/lge/mako/board-mako-pmic.c +++ b/arch/arm/mach-msm/lge/mako/board-mako-pmic.c @@ -488,7 +488,7 @@ apq8064_pm8xxx_ccadc_pdata = { static struct pm8921_bms_platform_data apq8064_pm8921_bms_pdata __devinitdata = { - .battery_type = BATT_UNKNOWN, //FIXME Define correct type + .battery_type = BATT_LGE, .r_sense = 10, .v_cutoff = 3500, .max_voltage_uv = MAX_VOLTAGE_MV * 1000, diff --git a/drivers/mfd/pm8018-core.c b/drivers/mfd/pm8018-core.c index 03455abd26b8..ab457388ed81 100644 --- a/drivers/mfd/pm8018-core.c +++ b/drivers/mfd/pm8018-core.c @@ -45,7 +45,6 @@ #define PM8018_REVISION_MASK 0x000F #define REG_PM8018_PON_CNTRL_3 0x01D -#define PM8018_RESTART_REASON_MASK 0x07 #define SINGLE_IRQ_RESOURCE(_name, _irq) \ { \ @@ -61,6 +60,7 @@ struct pm8018 { struct mfd_cell *mfd_regulators; struct pm8xxx_regulator_core_platform_data *regulator_cdata; u32 rev_registers; + u8 restart_reason; }; static int pm8018_readb(const struct device *dev, u16 addr, u8 *val) @@ -125,6 +125,14 @@ static int pm8018_get_revision(const struct device *dev) return pmic->rev_registers & PM8018_REVISION_MASK; } +static u8 pm8018_restart_reason(const struct device *dev) +{ + const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev); + const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data; + + return pmic->restart_reason; +} + static struct pm8xxx_drvdata pm8018_drvdata = { .pmic_readb = pm8018_readb, .pmic_writeb = pm8018_writeb, @@ -133,6 +141,7 @@ static struct pm8xxx_drvdata pm8018_drvdata = { .pmic_read_irq_stat = pm8018_read_irq_stat, .pmic_get_version = pm8018_get_version, .pmic_get_revision = pm8018_get_revision, + .pmic_restart_reason = pm8018_restart_reason, }; static const struct resource gpio_cell_resources[] __devinitconst = { @@ -516,17 +525,6 @@ bail: return ret; } -static const char * const pm8018_restart_reason[] = { - [0] = "Unknown", - [1] = "Triggered from CBL (external charger)", - [2] = "Triggered from KPD (power key press)", - [3] = "Triggered from CHG (usb charger insertion)", - [4] = "Triggered from SMPL (sudden momentary power loss)", - [5] = "Triggered from RTC (real time clock)", - [6] = "Triggered by Hard Reset", - [7] = "Triggered by General Purpose Trigger", -}; - static const char * const pm8018_rev_names[] = { [PM8XXX_REVISION_8018_TEST] = "test", [PM8XXX_REVISION_8018_1p0] = "1.0", @@ -594,8 +592,9 @@ static int __devinit pm8018_probe(struct platform_device *pdev) pr_err("Cannot read restart reason rc=%d\n", rc); goto err_read_rev; } - val &= PM8018_RESTART_REASON_MASK; - pr_info("PMIC Restart Reason: %s\n", pm8018_restart_reason[val]); + val &= PM8XXX_RESTART_REASON_MASK; + pr_info("PMIC Restart Reason: %s\n", pm8xxx_restart_reason_str[val]); + pmic->restart_reason = val; rc = pm8018_add_subdevices(pdata, pmic); if (rc) { diff --git a/drivers/mfd/pm8038-core.c b/drivers/mfd/pm8038-core.c index c80f49617630..773a240ae9dd 100644 --- a/drivers/mfd/pm8038-core.c +++ b/drivers/mfd/pm8038-core.c @@ -49,7 +49,6 @@ #define PM8038_REVISION_MASK 0x000F #define REG_PM8038_PON_CNTRL_3 0x01D -#define PM8038_RESTART_REASON_MASK 0x07 #define SINGLE_IRQ_RESOURCE(_name, _irq) \ { \ @@ -65,6 +64,7 @@ struct pm8038 { struct mfd_cell *mfd_regulators; struct pm8xxx_regulator_core_platform_data *regulator_cdata; u32 rev_registers; + u8 restart_reason; }; static int pm8038_readb(const struct device *dev, u16 addr, u8 *val) @@ -129,6 +129,14 @@ static int pm8038_get_revision(const struct device *dev) return pmic->rev_registers & PM8038_REVISION_MASK; } +static u8 pm8038_restart_reason(const struct device *dev) +{ + const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev); + const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data; + + return pmic->restart_reason; +} + static struct pm8xxx_drvdata pm8038_drvdata = { .pmic_readb = pm8038_readb, .pmic_writeb = pm8038_writeb, @@ -137,6 +145,7 @@ static struct pm8xxx_drvdata pm8038_drvdata = { .pmic_read_irq_stat = pm8038_read_irq_stat, .pmic_get_version = pm8038_get_version, .pmic_get_revision = pm8038_get_revision, + .pmic_restart_reason = pm8038_restart_reason, }; static const struct resource gpio_cell_resources[] __devinitconst = { @@ -723,17 +732,6 @@ bail: return ret; } -static const char * const pm8038_restart_reason[] = { - [0] = "Unknown", - [1] = "Triggered from CBL (external charger)", - [2] = "Triggered from KPD (power key press)", - [3] = "Triggered from CHG (usb charger insertion)", - [4] = "Triggered from SMPL (sudden momentary power loss)", - [5] = "Triggered from RTC (real time clock)", - [6] = "Triggered by Hard Reset", - [7] = "Triggered by General Purpose Trigger", -}; - static const char * const pm8038_rev_names[] = { [PM8XXX_REVISION_8038_TEST] = "test", [PM8XXX_REVISION_8038_1p0] = "1.0", @@ -802,8 +800,9 @@ static int __devinit pm8038_probe(struct platform_device *pdev) pr_err("Cannot read restart reason rc=%d\n", rc); goto err_read_rev; } - val &= PM8038_RESTART_REASON_MASK; - pr_info("PMIC Restart Reason: %s\n", pm8038_restart_reason[val]); + val &= PM8XXX_RESTART_REASON_MASK; + pr_info("PMIC Restart Reason: %s\n", pm8xxx_restart_reason_str[val]); + pmic->restart_reason = val; rc = pm8038_add_subdevices(pdata, pmic); if (rc) { diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index 52b779c006fb..6ece94905b55 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -63,6 +63,7 @@ struct pm8921 { struct mfd_cell *mfd_regulators; struct pm8xxx_regulator_core_platform_data *regulator_cdata; u32 rev_registers; + u8 restart_reason; }; static int pm8921_readb(const struct device *dev, u16 addr, u8 *val) @@ -133,6 +134,14 @@ static int pm8921_get_revision(const struct device *dev) return pmic->rev_registers & PM8921_REVISION_MASK; } +static u8 pm8921_restart_reason(const struct device *dev) +{ + const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); + const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; + + return pmic->restart_reason; +} + static struct pm8xxx_drvdata pm8921_drvdata = { .pmic_readb = pm8921_readb, .pmic_writeb = pm8921_writeb, @@ -141,6 +150,7 @@ static struct pm8xxx_drvdata pm8921_drvdata = { .pmic_read_irq_stat = pm8921_read_irq_stat, .pmic_get_version = pm8921_get_version, .pmic_get_revision = pm8921_get_revision, + .pmic_restart_reason = pm8921_restart_reason, }; static struct resource gpio_cell_resources[] = { @@ -612,6 +622,18 @@ pm8921_add_subdevices(const struct pm8921_platform_data *pdata, } } + if (pdata->pwrkey_pdata) { + pwrkey_cell.platform_data = pdata->pwrkey_pdata; + pwrkey_cell.pdata_size = + sizeof(struct pm8xxx_pwrkey_platform_data); + ret = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL, + irq_base); + if (ret) { + pr_err("Failed to add pwrkey subdevice ret=%d\n", ret); + goto bail; + } + } + if (pdata->mpp_pdata) { if (version == PM8XXX_VERSION_8917) { mpp_cell_resources[0].end = mpp_cell_resources[0].end @@ -643,18 +665,6 @@ pm8921_add_subdevices(const struct pm8921_platform_data *pdata, } } - if (pdata->pwrkey_pdata) { - pwrkey_cell.platform_data = pdata->pwrkey_pdata; - pwrkey_cell.pdata_size = - sizeof(struct pm8xxx_pwrkey_platform_data); - ret = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL, - irq_base); - if (ret) { - pr_err("Failed to add pwrkey subdevice ret=%d\n", ret); - goto bail; - } - } - if (pdata->keypad_pdata) { keypad_cell.platform_data = pdata->keypad_pdata; keypad_cell.pdata_size = @@ -815,17 +825,6 @@ bail: return ret; } -static const char * const pm8921_restart_reason[] = { - [0] = "Unknown", - [1] = "Triggered from CBL (external charger)", - [2] = "Triggered from KPD (power key press)", - [3] = "Triggered from CHG (usb charger insertion)", - [4] = "Triggered from SMPL (sudden momentary power loss)", - [5] = "Triggered from RTC (real time clock)", - [6] = "Triggered by Hard Reset", - [7] = "Triggered by General Purpose Trigger", -}; - static const char * const pm8921_rev_names[] = { [PM8XXX_REVISION_8921_TEST] = "test", [PM8XXX_REVISION_8921_1p0] = "1.0", @@ -918,8 +917,9 @@ static int __devinit pm8921_probe(struct platform_device *pdev) pr_err("Cannot read restart reason rc=%d\n", rc); goto err_read_rev; } - val &= PM8921_RESTART_REASON_MASK; - pr_info("PMIC Restart Reason: %s\n", pm8921_restart_reason[val]); + val &= PM8XXX_RESTART_REASON_MASK; + pr_info("PMIC Restart Reason: %s\n", pm8xxx_restart_reason_str[val]); + pmic->restart_reason = val; rc = pm8921_add_subdevices(pdata, pmic); if (rc) { diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index fb9951219053..51f40255f36a 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -435,4 +435,23 @@ config AB8500_BATTERY_THERM_ON_BATCTRL Say Y to enable battery temperature measurements using thermistor connected on BATCTRL ADC. +config WIRELESS_CHARGER + bool "Wireless Charger Configuration" + default n + help + wireless charger configuration + +config BQ51051B_CHARGER + bool "TI BQ51051B Wireless Charging control Driver" + depends on WIRELESS_CHARGER + help + TI BQ51051B wireless charging control driver + +config BATTERY_TEMP_CONTROL + default n + bool "Battery Temperature Control Configuration" + help + Say Y to enable battery temperature control for safty. + It controls charging current depending on battery temperature. + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 007d75bce4a6..d59e8564c3fc 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -58,3 +58,5 @@ obj-$(CONFIG_PM8921_BMS) += pm8921-bms.o obj-$(CONFIG_PM8921_CHARGER) += pm8921-charger.o obj-$(CONFIG_LTC4088_CHARGER) += ltc4088-charger.o obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o +obj-$(CONFIG_BQ51051B_CHARGER) += bq51051b_charger.o +obj-$(CONFIG_BATTERY_TEMP_CONTROL) += battery_temp_ctrl.o diff --git a/drivers/power/battery_temp_ctrl.c b/drivers/power/battery_temp_ctrl.c new file mode 100644 index 000000000000..a38f42c305f3 --- /dev/null +++ b/drivers/power/battery_temp_ctrl.c @@ -0,0 +1,281 @@ + /* + * Copyright(c) 2012, LG Electronics Inc. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct batt_temp_chip { + struct delayed_work monitor_work; + struct batt_temp_pdata *pdata; +}; + +#define INIT_VAL 1000 +static int fake_temp = INIT_VAL; +static ssize_t batt_temp_fake_temp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int level; + + if (!count) + return -EINVAL; + + level = simple_strtoul(buf, NULL, 10); + if (level > 1000) + fake_temp = 1000-level; + else + fake_temp = level; + + pr_info("%s: fake_temp = %d\n", __func__, fake_temp); + return count; +} +DEVICE_ATTR(fake_temp, 0664, NULL, batt_temp_fake_temp_store); + +static int determin_batt_temp_state(struct batt_temp_pdata *pdata, + int batt_temp, int batt_mvolt) +{ + static int temp_state = TEMP_LEVEL_NORMAL; + + if (pdata->is_ext_power()) { + pr_debug("%s : is_ext_power = true\n", __func__); + + if (batt_temp >= pdata->temp_level[TEMP_LEVEL_POWER_OFF]) { + temp_state = TEMP_LEVEL_POWER_OFF; + } else if (batt_temp >= pdata->temp_level[TEMP_LEVEL_HOT_STOPCHARGING]) { + temp_state = TEMP_LEVEL_HOT_STOPCHARGING; + } else if (batt_temp >= pdata->temp_level[TEMP_LEVEL_DECREASING]) { + if (batt_mvolt > pdata->thr_mvolt + || temp_state == TEMP_LEVEL_HOT_STOPCHARGING) + temp_state = TEMP_LEVEL_HOT_STOPCHARGING; + else + temp_state = TEMP_LEVEL_DECREASING; + } else if (batt_temp > pdata->temp_level[TEMP_LEVEL_HOT_RECHARGING]) { + if (temp_state == TEMP_LEVEL_HOT_STOPCHARGING + || temp_state == TEMP_LEVEL_DECREASING) + temp_state = TEMP_LEVEL_HOT_STOPCHARGING; + else if (temp_state == TEMP_LEVEL_DECREASING) + temp_state = TEMP_LEVEL_DECREASING; + else + temp_state = TEMP_LEVEL_NORMAL; + } else if (batt_temp >= pdata->temp_level[TEMP_LEVEL_COLD_RECHARGING]) { + if (temp_state == TEMP_LEVEL_HOT_STOPCHARGING + || temp_state == TEMP_LEVEL_DECREASING) + temp_state = TEMP_LEVEL_HOT_RECHARGING; + else if (temp_state == TEMP_LEVEL_COLD_STOPCHARGING) + temp_state = TEMP_LEVEL_COLD_RECHARGING; + else + temp_state = TEMP_LEVEL_NORMAL; + } else if (batt_temp <= pdata->temp_level[TEMP_LEVEL_COLD_STOPCHARGING]) { + temp_state = TEMP_LEVEL_COLD_STOPCHARGING; + } else if (batt_temp < pdata->temp_level[TEMP_LEVEL_COLD_RECHARGING]) { + if (temp_state == TEMP_LEVEL_COLD_STOPCHARGING) + temp_state = TEMP_LEVEL_COLD_STOPCHARGING; + else + temp_state = TEMP_LEVEL_NORMAL; + } else if (batt_temp <= pdata->temp_level[TEMP_LEVEL_HOT_RECHARGING]) { + if (temp_state == TEMP_LEVEL_HOT_STOPCHARGING) + temp_state = TEMP_LEVEL_HOT_RECHARGING; + else if (temp_state == TEMP_LEVEL_COLD_STOPCHARGING) + temp_state = TEMP_LEVEL_COLD_RECHARGING; + else + temp_state = TEMP_LEVEL_NORMAL; + } else { + temp_state = TEMP_LEVEL_NORMAL; + } + } else { + pr_debug("%s : is_ext_power = false\n", __func__); + + if (batt_temp >= pdata->temp_level[TEMP_LEVEL_POWER_OFF]) + temp_state = TEMP_LEVEL_POWER_OFF; + else if (batt_temp >= pdata->temp_level[TEMP_LEVEL_WARNINGOVERHEAT]) + temp_state = TEMP_LEVEL_WARNINGOVERHEAT; + else + temp_state = TEMP_LEVEL_NORMAL; + } + + return temp_state; +} + +static void batt_temp_monitor_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct batt_temp_chip *chip = container_of(dwork, + struct batt_temp_chip, monitor_work); + struct batt_temp_pdata *pdata = chip->pdata; + static struct power_supply *psy = NULL; + int batt_temp = 0; + int batt_mvolt = 0; + int temp_state = TEMP_LEVEL_NORMAL; + static int temp_old_state = TEMP_LEVEL_NORMAL; + union power_supply_propval ret = {0,}; + int rc; + + if (!psy) { + psy = power_supply_get_by_name("battery"); + if (!psy) { + pr_err("%s: failed to get power supply\n", __func__); + return; + } + } + + rc = psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &ret); + if (rc) { + pr_err("%s: failed to get voltage\n", __func__); + return; + } + batt_mvolt = ret.intval/1000; + + rc = psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &ret); + if (rc) { + pr_err("%s: failed to get temperature\n", __func__); + return; + } + + if (fake_temp != INIT_VAL) + batt_temp = fake_temp; + else + batt_temp = ret.intval; + + temp_state = determin_batt_temp_state(pdata, batt_temp, batt_mvolt); + pr_debug("%s: batt_temp = %d batt_mvolt = %d state = %d\n", __func__, + batt_temp, batt_mvolt, temp_state); + + switch (temp_state) { + case TEMP_LEVEL_POWER_OFF: + case TEMP_LEVEL_HOT_STOPCHARGING: + case TEMP_LEVEL_COLD_STOPCHARGING: + pr_info("%s: stop charging!! state = %d temp = %d mvolt = %d \n", + __func__, temp_state, batt_temp, batt_mvolt); + pdata->set_chg_i_limit(pdata->i_restore); + pdata->disable_charging(); + pdata->set_health_state(POWER_SUPPLY_HEALTH_OVERHEAT, 0); + break; + case TEMP_LEVEL_WARNINGOVERHEAT: + pdata->set_health_state(POWER_SUPPLY_HEALTH_OVERHEAT, 0); + break; + case TEMP_LEVEL_DECREASING: + pr_info("%s: decrease current!! state = %d temp = %d mvolt = %d \n", + __func__, temp_state, batt_temp, batt_mvolt); + pdata->set_chg_i_limit(pdata->i_decrease); + pdata->set_health_state(POWER_SUPPLY_HEALTH_GOOD, + pdata->i_decrease); + break; + case TEMP_LEVEL_COLD_RECHARGING: + case TEMP_LEVEL_HOT_RECHARGING: + pr_info("%s: restart charging!! state = %d temp = %d mvolt = %d \n", + __func__, temp_state, batt_temp, batt_mvolt); + pdata->set_chg_i_limit(pdata->i_restore); + pdata->enable_charging(); + pdata->set_health_state(POWER_SUPPLY_HEALTH_GOOD, + pdata->i_restore); + break; + case TEMP_LEVEL_NORMAL: + if (temp_old_state != TEMP_LEVEL_NORMAL) { + pdata->set_chg_i_limit(pdata->i_restore); + pdata->enable_charging(); + } + pdata->set_health_state(POWER_SUPPLY_HEALTH_GOOD, 0); + default: + break; + } + + temp_old_state=temp_state; + power_supply_changed(psy); + schedule_delayed_work(&chip->monitor_work, + round_jiffies_relative(msecs_to_jiffies(pdata->update_time))); + +} + +static int batt_temp_ctrl_suspend(struct device *dev) +{ + struct batt_temp_chip *chip = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&chip->monitor_work); + return 0; +} + +static int batt_temp_ctrl_resume(struct device *dev) +{ + struct batt_temp_chip *chip = dev_get_drvdata(dev); + + schedule_delayed_work(&chip->monitor_work, + round_jiffies_relative(msecs_to_jiffies + (30000))); + + return 0; +} + +static int batt_temp_ctrl_probe(struct platform_device *pdev) +{ + struct batt_temp_chip *chip; + struct batt_temp_pdata *pdata = pdev->dev.platform_data; + + pr_info("%s\n", __func__); + + if (!pdata) { + pr_err("%s: no pdata\n", __func__); + return -ENODEV; + } + + chip = kzalloc(sizeof(struct batt_temp_chip), GFP_KERNEL); + if (!chip) { + pr_err("%s: out of memory\n", __func__); + return -ENOMEM; + } + + chip->pdata = pdata; + platform_set_drvdata(pdev, chip); + + device_create_file(&pdev->dev, &dev_attr_fake_temp); + + INIT_DELAYED_WORK(&chip->monitor_work, + batt_temp_monitor_work); + schedule_delayed_work(&chip->monitor_work, + round_jiffies_relative(msecs_to_jiffies + (pdata->update_time))); + return 0; +} + +static const struct dev_pm_ops batt_temp_ops = { + .suspend = batt_temp_ctrl_suspend, + .resume = batt_temp_ctrl_resume, +}; + +static struct platform_driver this_driver = { + .probe = batt_temp_ctrl_probe, + .driver = { + .name = "batt_temp_ctrl", + .owner = THIS_MODULE, + .pm = &batt_temp_ops, + }, +}; + +int __init batt_temp_ctrl_init(void) +{ + pr_info("batt_temp_ctrl_init \n"); + return platform_driver_register(&this_driver); +} + +late_initcall(batt_temp_ctrl_init); + +MODULE_DESCRIPTION("Battery Temperature Control Driver"); +MODULE_AUTHOR("ChoongRyeol Lee "); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/bq51051b_charger.c b/drivers/power/bq51051b_charger.c new file mode 100644 index 000000000000..b6dbe2f593a1 --- /dev/null +++ b/drivers/power/bq51051b_charger.c @@ -0,0 +1,277 @@ +/* + * BQ51051B Wireless Charging(WLC) control driver + * + * Copyright (C) 2012 LG Electronics, Inc + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct bq51051b_wlc_chip { + struct device *dev; + struct power_supply wireless_psy; + struct work_struct wireless_interrupt_work; + struct wake_lock wireless_chip_wake_lock; + unsigned int active_n_gpio; + int (*wlc_is_plugged)(void); +}; + +static const struct platform_device_id bq51051b_id[] = { + {BQ51051B_WLC_DEV_NAME, 0}, + {}, +}; + +static struct bq51051b_wlc_chip *the_chip; +static bool wireless_charging; +static bool wireless_charge_done; + +static void bms_notify(struct bq51051b_wlc_chip *chip, int value) +{ + if (value) + pm8921_bms_charging_began(); + else + pm8921_bms_charging_end(0); +} + +static enum power_supply_property pm_power_props_wireless[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, +}; + +static char *pm_power_supplied_to[] = { + "battery", +}; + +static int pm_power_get_property_wireless(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + /* Check if called before init */ + if (!the_chip) + return -EINVAL; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; //always battery_on + case POWER_SUPPLY_PROP_ONLINE: + val->intval = wireless_charging; + break; + default: + return -EINVAL; + } + return 0; +} + +static void wireless_set(struct bq51051b_wlc_chip *chip) +{ + WLC_DBG_INFO("wireless_set\n"); + + wake_lock(&chip->wireless_chip_wake_lock); + + wireless_charging = true; + wireless_charge_done = false; + + bms_notify(chip, 1); + + power_supply_changed(&chip->wireless_psy); + set_wireless_power_supply_control(wireless_charging); +} + +static void wireless_reset(struct bq51051b_wlc_chip *chip) +{ + WLC_DBG_INFO("wireless_reset\n"); + + wireless_charging = false; + wireless_charge_done = false; + + bms_notify(chip, 0); + + power_supply_changed(&chip->wireless_psy); + set_wireless_power_supply_control(wireless_charging); + + wake_unlock(&chip->wireless_chip_wake_lock); +} + +static void wireless_interrupt_worker(struct work_struct *work) +{ + struct bq51051b_wlc_chip *chip = + container_of(work, struct bq51051b_wlc_chip, + wireless_interrupt_work); + + if (chip->wlc_is_plugged()) + wireless_set(chip); + else + wireless_reset(chip); +} + +static irqreturn_t wireless_interrupt_handler(int irq, void *data) +{ + int chg_state; + struct bq51051b_wlc_chip *chip = data; + + chg_state = chip->wlc_is_plugged(); + WLC_DBG_INFO("\nwireless is plugged state = %d\n\n", chg_state); + schedule_work(&chip->wireless_interrupt_work); + return IRQ_HANDLED; +} + +static int __devinit bq51051b_wlc_hw_init(struct bq51051b_wlc_chip *chip) +{ + int ret; + WLC_DBG_INFO("hw_init"); + + /* active_n pin must be monitoring the bq51051b status */ + ret = request_irq(gpio_to_irq(chip->active_n_gpio), + wireless_interrupt_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "wireless_charger", chip); + if (ret < 0) { + pr_err("wlc: wireless_charger request irq failed\n"); + return ret; + } + enable_irq_wake(gpio_to_irq(chip->active_n_gpio)); + + return 0; +} + +static int bq51051b_wlc_resume(struct device *dev) +{ + return 0; +} + +static int bq51051b_wlc_suspend(struct device *dev) +{ + return 0; +} + +static int __devinit bq51051b_wlc_probe(struct platform_device *pdev) +{ + int rc = 0; + struct bq51051b_wlc_chip *chip; + const struct bq51051b_wlc_platform_data *pdata = + pdev->dev.platform_data; + + WLC_DBG_INFO("probe\n"); + + if (!pdata) { + pr_err("wlc: missing platform data\n"); + return -ENODEV; + } + + chip = kzalloc(sizeof(struct bq51051b_wlc_chip), GFP_KERNEL); + if (!chip) { + pr_err("wlc: Cannot allocate bq51051b_wlc_chip\n"); + return -ENOMEM; + } + + chip->dev = &pdev->dev; + + chip->active_n_gpio = pdata->active_n_gpio; + chip->wlc_is_plugged = pdata->wlc_is_plugged; + + rc = bq51051b_wlc_hw_init(chip); + if (rc) { + pr_err("wlc: couldn't init hardware rc = %d\n", rc); + goto free_chip; + } + + chip->wireless_psy.name = "wireless"; + chip->wireless_psy.type = POWER_SUPPLY_TYPE_WIRELESS; + chip->wireless_psy.supplied_to = pm_power_supplied_to; + chip->wireless_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to); + chip->wireless_psy.properties = pm_power_props_wireless; + chip->wireless_psy.num_properties = ARRAY_SIZE(pm_power_props_wireless); + chip->wireless_psy.get_property = pm_power_get_property_wireless; + + rc = power_supply_register(chip->dev, &chip->wireless_psy); + if (rc < 0) { + pr_err("wlc: power_supply_register wireless failed rx = %d\n", + rc); + goto free_chip; + } + + platform_set_drvdata(pdev, chip); + the_chip = chip; + + INIT_WORK(&chip->wireless_interrupt_work, wireless_interrupt_worker); + wake_lock_init(&chip->wireless_chip_wake_lock, WAKE_LOCK_SUSPEND, + "bq51051b_wireless_chip"); + + /* For Booting Wireless_charging and For Power Charging Logo In Wireless Charging */ + if (chip->wlc_is_plugged()) + wireless_set(chip); + + return 0; + +free_chip: + kfree(chip); + return rc; +} + +static int __devexit bq51051b_wlc_remove(struct platform_device *pdev) +{ + struct bq51051b_wlc_chip *chip = platform_get_drvdata(pdev); + + WLC_DBG_INFO("remove\n"); + wake_lock_destroy(&chip->wireless_chip_wake_lock); + the_chip = NULL; + platform_set_drvdata(pdev, NULL); + power_supply_unregister(&chip->wireless_psy); + free_irq(gpio_to_irq(chip->active_n_gpio), chip); + gpio_free(chip->active_n_gpio); + kfree(chip); + return 0; +} + +static const struct dev_pm_ops bq51051b_pm_ops = { + .suspend = bq51051b_wlc_suspend, + .resume = bq51051b_wlc_resume, +}; + +static struct platform_driver bq51051b_wlc_driver = { + .probe = bq51051b_wlc_probe, + .remove = __devexit_p(bq51051b_wlc_remove), + .id_table = bq51051b_id, + .driver = { + .name = BQ51051B_WLC_DEV_NAME, + .owner = THIS_MODULE, + .pm = &bq51051b_pm_ops, + }, +}; + +static int __init bq51051b_wlc_init(void) +{ + return platform_driver_register(&bq51051b_wlc_driver); +} + +static void __exit bq51051b_wlc_exit(void) +{ + platform_driver_unregister(&bq51051b_wlc_driver); +} + +late_initcall(bq51051b_wlc_init); +module_exit(bq51051b_wlc_exit); + +MODULE_AUTHOR("Kyungtae Oh "); +MODULE_DESCRIPTION("BQ51051B Wireless Charger Control Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c index c19015f64909..247b721910d2 100644 --- a/drivers/power/pm8921-bms.c +++ b/drivers/power/pm8921-bms.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,9 @@ #define TEMP_IAVG_STORAGE 0x105 #define TEMP_IAVG_STORAGE_USE_MASK 0x0F +#define PON_CNTRL_6 0x018 +#define WD_BIT BIT(7) + enum pmic_bms_interrupts { PM8921_BMS_SBI_WRITE_OK, PM8921_BMS_CC_THR, @@ -134,7 +138,7 @@ struct pm8921_bms_chip { unsigned long tm_sec; int enable_fcc_learning; int shutdown_soc; - int shutdown_iavg_ua; + int shutdown_iavg_ma; struct delayed_work calculate_soc_delayed_work; struct timespec t_soc_queried; int shutdown_soc_valid_limit; @@ -147,6 +151,16 @@ struct pm8921_bms_chip { int ibat_at_cv_ua; int soc_at_cv; int prev_chg_soc; + int last_reported_soc; + int eoc_check_soc; + int soc_adjusted; + int bms_support_wlc; + int wlc_term_ua; + int wlc_max_voltage_uv; + int (*wlc_is_plugged)(void); + int vbat_at_cv; + int (*is_warm_reset)(void); + int first_fixed_iavg_ma; }; /* @@ -157,7 +171,7 @@ static DEFINE_MUTEX(soc_invalidation_mutex); static int shutdown_soc_invalid; static struct pm8921_bms_chip *the_chip; -#define DEFAULT_RBATT_MOHMS 128 +#define DEFAULT_RBATT_MOHMS 200 #define DEFAULT_OCV_MICROVOLTS 3900000 #define DEFAULT_CHARGE_CYCLES 0 @@ -346,13 +360,21 @@ static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr, return 0; } -static int usb_chg_plugged_in(void) +static int usb_chg_plugged_in(struct pm8921_bms_chip *chip) { int val = pm8921_is_usb_chg_plugged_in(); - /* treat as if usb is not present in case of error */ - if (val == -EINVAL) - val = 0; + /* if the charger driver was not initialized, use the restart reason */ + if (val == -EINVAL) { + if (pm8xxx_restart_reason(chip->dev->parent) + == PM8XXX_RESTART_CHG) + val = 1; + else + val = 0; + } + + if (chip->bms_support_wlc) + val |= chip->wlc_is_plugged(); return val; } @@ -814,28 +836,26 @@ static int interpolate_ocv(struct pm8921_bms_chip *chip, } static int interpolate_pc(struct pm8921_bms_chip *chip, - int batt_temp, int ocv) + int batt_temp_degc, int ocv) { int i, j, pcj, pcj_minus_one, pc; int rows = chip->pc_temp_ocv_lut->rows; int cols = chip->pc_temp_ocv_lut->cols; - /* batt_temp is in tenths of degC - convert it to degC for lookups */ - batt_temp = batt_temp/10; - if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) { - pr_debug("batt_temp %d < known temp range for pc\n", batt_temp); - batt_temp = chip->pc_temp_ocv_lut->temp[0]; + if (batt_temp_degc < chip->pc_temp_ocv_lut->temp[0]) { + pr_debug("batt_temp %d < known temp range\n", batt_temp_degc); + batt_temp_degc = chip->pc_temp_ocv_lut->temp[0]; } - if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) { - pr_debug("batt_temp %d > known temp range for pc\n", batt_temp); - batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1]; + if (batt_temp_degc > chip->pc_temp_ocv_lut->temp[cols - 1]) { + pr_debug("batt_temp %d > known temp range\n", batt_temp_degc); + batt_temp_degc = chip->pc_temp_ocv_lut->temp[cols - 1]; } for (j = 0; j < cols; j++) - if (batt_temp <= chip->pc_temp_ocv_lut->temp[j]) + if (batt_temp_degc <= chip->pc_temp_ocv_lut->temp[j]) break; - if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) { + if (batt_temp_degc == chip->pc_temp_ocv_lut->temp[j]) { /* found an exact match for temp in the table */ if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j]) return chip->pc_temp_ocv_lut->percent[0]; @@ -858,7 +878,7 @@ static int interpolate_pc(struct pm8921_bms_chip *chip, } /* - * batt_temp is within temperature for + * batt_temp_degc is within temperature for * column j-1 and j */ if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j]) @@ -898,7 +918,7 @@ static int interpolate_pc(struct pm8921_bms_chip *chip, chip->pc_temp_ocv_lut->temp[j-1], pcj, chip->pc_temp_ocv_lut->temp[j], - batt_temp); + batt_temp_degc); return pc; } } @@ -910,7 +930,7 @@ static int interpolate_pc(struct pm8921_bms_chip *chip, return pcj_minus_one; pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%", - ocv, batt_temp); + ocv, batt_temp_degc); return 100; } @@ -944,7 +964,7 @@ int override_mode_simultaneous_battery_voltage_and_current(int *ibat_ua, mutex_unlock(&the_chip->bms_output_lock); - usb_chg = usb_chg_plugged_in(); + usb_chg = usb_chg_plugged_in(the_chip); convert_vbatt_raw_to_uv(the_chip, usb_chg, vbat_raw, vbat_uv); convert_vsense_to_uv(the_chip, vsense_raw, &vsense_uv); @@ -970,6 +990,40 @@ static void adjust_pon_ocv_raw(struct pm8921_bms_chip *chip, raw->last_good_ocv_raw -= MBG_TRANSIENT_ERROR_RAW; } +#define SEL_ALT_OREG_BIT BIT(2) +static int ocv_ir_compensation(struct pm8921_bms_chip *chip, int ocv) +{ + int compensated_ocv; + int ibatt_ua; + int rbatt_mohm = chip->default_rbatt_mohm + chip->rconn_mohm; + + pm_bms_masked_write(chip, BMS_TEST1, + SEL_ALT_OREG_BIT, SEL_ALT_OREG_BIT); + + /* since the SEL_ALT_OREG_BIT is set this will give us VSENSE_OCV */ + pm8921_bms_get_battery_current(&ibatt_ua); + compensated_ocv = ocv + div_s64((s64)ibatt_ua * rbatt_mohm, 1000); + pr_info("comp ocv = %d, ocv = %d, ibatt_ua = %d, rbatt_mohm = %d\n", + compensated_ocv, ocv, ibatt_ua, rbatt_mohm); + + pm_bms_masked_write(chip, BMS_TEST1, SEL_ALT_OREG_BIT, 0); + return compensated_ocv; +} + +static bool is_warm_restart(struct pm8921_bms_chip *chip) +{ + u8 reg; + int rc; + + rc = pm8xxx_readb(chip->dev->parent, PON_CNTRL_6, ®); + if (rc) { + pr_err("err reading pon 6 rc = %d\n", rc); + return false; + } + + return reg & WD_BIT; +} + static int read_soc_params_raw(struct pm8921_bms_chip *chip, struct pm8921_soc_params *raw) { @@ -985,14 +1039,22 @@ static int read_soc_params_raw(struct pm8921_bms_chip *chip, pm_bms_unlock_output_data(chip); mutex_unlock(&chip->bms_output_lock); - usb_chg = usb_chg_plugged_in(); + usb_chg = usb_chg_plugged_in(chip); if (chip->prev_last_good_ocv_raw == 0) { chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw; adjust_pon_ocv_raw(chip, raw); convert_vbatt_raw_to_uv(chip, usb_chg, raw->last_good_ocv_raw, &raw->last_good_ocv_uv); + raw->last_good_ocv_uv = ocv_ir_compensation(chip, + raw->last_good_ocv_uv); chip->last_ocv_uv = raw->last_good_ocv_uv; + + if (is_warm_restart(chip)) { + shutdown_soc_invalid = 1; + pr_info("discard shutdown soc! cc_raw = 0x%x\n", raw->cc); + } + pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv); } else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) { chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw; @@ -1120,7 +1182,7 @@ static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp, { int pc, scalefactor; - pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000); + pc = interpolate_pc(chip, batt_temp / 10, ocv_uv / 1000); pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n", pc, ocv_uv, batt_temp); @@ -1335,13 +1397,13 @@ static int calculate_unusable_charge_uah(struct pm8921_bms_chip *chip, /* * if we are called first time fill all the - * samples with the the shutdown_iavg_ua + * samples with the the shutdown_iavg_ma */ - if (firsttime && chip->shutdown_iavg_ua != 0) { - pr_emerg("Using shutdown_iavg_ua = %d in all samples\n", - chip->shutdown_iavg_ua); + if (firsttime && chip->shutdown_iavg_ma != 0) { + pr_emerg("Using shutdown_iavg_ma = %d in all samples\n", + chip->shutdown_iavg_ma); for (i = 0; i < IAVG_SAMPLES; i++) - iavg_samples[i] = chip->shutdown_iavg_ua; + iavg_samples[i] = chip->shutdown_iavg_ma; iavg_index = 0; iavg_num_samples = IAVG_SAMPLES; @@ -1588,20 +1650,40 @@ static int charging_adjustments(struct pm8921_bms_chip *chip, int fcc_uah, int cc_uah, int uuc_uah) { int chg_soc; + int max_vol; + int eoc_current; + + max_vol = chip->max_voltage_uv; + eoc_current = -chip->chg_term_ua; + + if (chip->bms_support_wlc && chip->wlc_is_plugged()) { + max_vol = chip->wlc_max_voltage_uv; + eoc_current = -chip->wlc_term_ua; + } if (chip->soc_at_cv == -EINVAL) { /* In constant current charging return the calc soc */ - if (vbat_uv <= chip->max_voltage_uv) + if (vbat_uv <= max_vol) pr_debug("CC CHG SOC %d\n", soc); /* Note the CC to CV point */ - if (vbat_uv >= chip->max_voltage_uv) { + if (vbat_uv >= max_vol) { chip->soc_at_cv = soc; chip->prev_chg_soc = soc; chip->ibat_at_cv_ua = ibat_ua; + chip->vbat_at_cv = vbat_uv; pr_debug("CC_TO_CV ibat_ua = %d CHG SOC %d\n", ibat_ua, soc); } + else if(soc >= 95) + { + chip->soc_at_cv = soc; + chip->prev_chg_soc = soc; + chip->ibat_at_cv_ua = ibat_ua; + chip->vbat_at_cv = vbat_uv; + pr_debug("Force CC_TO_CV ibat_ua = %d CHG SOC %d\n", + ibat_ua, soc); + } return soc; } @@ -1614,15 +1696,25 @@ static int charging_adjustments(struct pm8921_bms_chip *chip, * if voltage lessened (possibly because of a system load) * keep reporting the prev chg soc */ - if (vbat_uv <= chip->max_voltage_uv) { + if (vbat_uv <= chip->vbat_at_cv) { pr_debug("vbat %d < max = %d CC CHG SOC %d\n", - vbat_uv, chip->max_voltage_uv, chip->prev_chg_soc); + vbat_uv, chip->vbat_at_cv, chip->prev_chg_soc); + return chip->prev_chg_soc; + } + + if (chip->bms_support_wlc + && chip->wlc_is_plugged() + && chip->prev_chg_soc < 99 + && ibat_ua > eoc_current) { + pr_info("ibat < eco_current ! soc = %d \n", chip->prev_chg_soc); return chip->prev_chg_soc; } chg_soc = linear_interpolate(chip->soc_at_cv, chip->ibat_at_cv_ua, - 100, -100000, + 100, eoc_current, ibat_ua); + if (chg_soc > 100) + chg_soc = 100; /* always report a higher soc */ if (chg_soc > chip->prev_chg_soc) { @@ -1761,17 +1853,13 @@ static int adjust_soc(struct pm8921_bms_chip *chip, int soc, * soc = 0 should happen only when soc_est == 0 */ if (soc_new == 0 && soc_est != 0) - soc_new = 1; + soc_new = 2; soc = soc_new; out: - pr_debug("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, " - "soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, " - "pc_new = %d, soc_new = %d, rbatt = %d, m = %d\n", - ibat_ua, vbat_uv, ocv_est_uv, pc_est, - soc_est, n, delta_ocv_uv, chip->last_ocv_uv, - pc_new, soc_new, rbatt, m); + pr_info("ibat_ua = %d, vbat_uv = %d, soc = %d, batt_temp=%d\n", + ibat_ua, vbat_uv, soc, batt_temp); return soc; } @@ -1814,14 +1902,14 @@ static void read_shutdown_soc_and_iavg(struct pm8921_bms_chip *chip) if (rc) { pr_err("failed to read addr = %d %d assuming %d\n", TEMP_IAVG_STORAGE, rc, IAVG_START); - chip->shutdown_iavg_ua = IAVG_START; + chip->shutdown_iavg_ma = IAVG_START; } else { temp &= TEMP_IAVG_STORAGE_USE_MASK; if (temp == 0) { - chip->shutdown_iavg_ua = IAVG_START; + chip->shutdown_iavg_ma = IAVG_START; } else { - chip->shutdown_iavg_ua = IAVG_START + chip->shutdown_iavg_ma = IAVG_START + IAVG_STEP_SIZE_MA * (temp + 1); } } @@ -1835,7 +1923,7 @@ static void read_shutdown_soc_and_iavg(struct pm8921_bms_chip *chip) if (chip->shutdown_soc == 0) { pr_debug("No shutdown soc available\n"); shutdown_soc_invalid = 1; - chip->shutdown_iavg_ua = 0; + chip->shutdown_iavg_ma = 0; } else if (chip->shutdown_soc == SOC_ZERO) { chip->shutdown_soc = 0; } @@ -1844,12 +1932,16 @@ static void read_shutdown_soc_and_iavg(struct pm8921_bms_chip *chip) if (chip->ignore_shutdown_soc) { shutdown_soc_invalid = 1; chip->shutdown_soc = 0; - chip->shutdown_iavg_ua = 0; + chip->shutdown_iavg_ma = 0; + } + + if (chip->first_fixed_iavg_ma && !chip->ignore_shutdown_soc) { + chip->shutdown_iavg_ma = chip->first_fixed_iavg_ma; } pr_debug("shutdown_soc = %d shutdown_iavg = %d shutdown_soc_invalid = %d\n", chip->shutdown_soc, - chip->shutdown_iavg_ua, + chip->shutdown_iavg_ma, shutdown_soc_invalid); } @@ -1918,6 +2010,45 @@ static bool is_shutdown_soc_within_limits(struct pm8921_bms_chip *chip, int soc) return 1; } + +static int is_eoc_adjust(struct pm8921_bms_chip *chip, int soc) +{ + int batt_state = pm8921_get_batt_state(); + int ret = 0; + + if (soc != 100) + return 0; + + switch (batt_state) { + case POWER_SUPPLY_STATUS_CHARGING: + if (chip->start_percent != -EINVAL + && chip->start_percent != 100) + ret = 1; + break; + case POWER_SUPPLY_STATUS_DISCHARGING: + case POWER_SUPPLY_STATUS_NOT_CHARGING: + if (chip->soc_adjusted == 1) + ret = 1; + break; + default: + break; + } + + return ret; +} + +static int is_recharging(struct pm8921_bms_chip *chip, int soc) +{ + if (soc == -EINVAL) + return 0; + if ((pm8921_get_batt_state() == POWER_SUPPLY_STATUS_FULL) + && (soc < 100) + && (pm8921_get_batt_health() + != POWER_SUPPLY_HEALTH_OVERHEAT)) + return 1; + return 0; +} + /* * Remaining Usable Charge = remaining_charge (charge at ocv instance) * - coloumb counter charge @@ -2039,16 +2170,20 @@ static int calculate_state_of_charge(struct pm8921_bms_chip *chip, chip->pon_ocv_uv = chip->last_ocv_uv; chip->last_ocv_uv = new_ocv; - remaining_charge_uah = new_rc_uah; unusable_charge_uah = new_ucc_uah; rbatt = new_rbatt; + if ((new_rc_uah - remaining_charge_uah) > fcc_uah*5/100) + remaining_charge_uah = new_rc_uah - fcc_uah*1/100; + else + remaining_charge_uah = new_rc_uah; + remaining_usable_charge_uah = remaining_charge_uah - cc_uah - unusable_charge_uah; - soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100), - (fcc_uah - unusable_charge_uah)); + soc = (remaining_usable_charge_uah * 100)/ + (fcc_uah - unusable_charge_uah); pr_debug("DONE for shutdown_soc = %d soc is %d, adjusted ocv to %duV\n", shutdown_soc, soc, chip->last_ocv_uv); @@ -2090,6 +2225,13 @@ static void calculate_soc_work(struct work_struct *work) soc = calculate_state_of_charge(chip, &raw, batt_temp, last_chargecycles); + + if (chip->eoc_check_soc + && is_recharging(chip, chip->last_reported_soc)) { + pm8921_force_start_charging(); + pr_info("Recharging is started\n"); + } + mutex_unlock(&chip->last_ocv_uv_mutex); schedule_delayed_work(&chip->calculate_soc_delayed_work, @@ -2164,13 +2306,22 @@ static int report_state_of_charge(struct pm8921_bms_chip *chip) } /* last_soc < soc ... scale and catch up */ - if (last_soc != -EINVAL && last_soc < soc && soc != 100) + if (last_soc != -EINVAL && soc != 100 + && (last_soc < soc || the_chip->start_percent != -EINVAL)) soc = scale_soc_while_chg(chip, delta_time_us, soc, last_soc); + if (chip->eoc_check_soc && is_eoc_adjust(chip, soc)) { + soc = soc - 1; + chip->soc_adjusted = 1; + } else { + chip->soc_adjusted = 0; + } + last_soc = soc; backup_soc_and_iavg(chip, batt_temp, last_soc); pr_debug("Reported SOC = %d\n", last_soc); chip->t_soc_queried = now; + chip->last_reported_soc = last_soc; return last_soc; } @@ -2251,7 +2402,8 @@ static void calib_hkadc(struct pm8921_bms_chip *chip) } voltage = xoadc_reading_to_microvolt(result.adc_code); - usb_chg = usb_chg_plugged_in(); + usb_chg = usb_chg_plugged_in(chip); + pr_debug("result 0.625V = 0x%x, voltage = %duV adc_meas = %lld " "usb_chg = %d\n", result.adc_code, voltage, result.measurement, @@ -2738,7 +2890,8 @@ static void check_initial_ocv(struct pm8921_bms_chip *chip) */ ocv_uv = 0; pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &ocv_raw); - usb_chg = usb_chg_plugged_in(); + usb_chg = usb_chg_plugged_in(chip); + rc = convert_vbatt_raw_to_uv(chip, usb_chg, ocv_raw, &ocv_uv); if (rc || ocv_uv == 0) { rc = adc_based_ocv(chip, &ocv_uv); @@ -2779,6 +2932,8 @@ static int set_battery_data(struct pm8921_bms_chip *chip) goto desay; else if (chip->batt_type == BATT_PALLADIUM) goto palladium; + else if (chip->batt_type == BATT_LGE) + goto lge; battery_id = read_battery_id(chip); if (battery_id < 0) { @@ -2817,6 +2972,8 @@ desay: chip->default_rbatt_mohm = desay_5200_data.default_rbatt_mohm; chip->delta_rbatt_mohm = desay_5200_data.delta_rbatt_mohm; return 0; +lge: + return 0; } enum bms_request_operation { @@ -3197,6 +3354,17 @@ static int __devinit pm8921_bms_probe(struct platform_device *pdev) chip->batt_id_channel = pdata->bms_cdata.batt_id_channel; chip->revision = pm8xxx_get_revision(chip->dev->parent); chip->enable_fcc_learning = pdata->enable_fcc_learning; + chip->last_reported_soc = -EINVAL; + chip->eoc_check_soc = pdata->eoc_check_soc; + chip->soc_adjusted = 0; + chip->bms_support_wlc = pdata->bms_support_wlc; + if (chip->bms_support_wlc) { + chip->wlc_term_ua = pdata->wlc_term_ua; + chip->wlc_max_voltage_uv = pdata->wlc_max_voltage_uv; + chip->wlc_is_plugged = pdata->wlc_is_plugged; + } + chip->vbat_at_cv = -EINVAL; + chip->first_fixed_iavg_ma = pdata->first_fixed_iavg_ma; mutex_init(&chip->calib_mutex); INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work); diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c index fc5ecb7d214e..691171ec53fa 100644 --- a/drivers/power/pm8921-charger.c +++ b/drivers/power/pm8921-charger.c @@ -274,6 +274,7 @@ struct pm8921_chg_chip { struct delayed_work update_heartbeat_work; struct delayed_work eoc_work; struct delayed_work unplug_check_work; + struct delayed_work unplug_usbcheck_work; struct delayed_work vin_collapse_check_work; struct wake_lock eoc_wake_lock; enum pm8921_chg_cold_thr cold_thr; @@ -284,6 +285,10 @@ struct pm8921_chg_chip { bool has_dc_supply; u8 active_path; int recent_reported_soc; + unsigned int ext_warm_i_limit; + int ext_batt_health; + int ext_batt_temp_monitor; + int eoc_check_soc; }; /* user space parameter to limit usb current */ @@ -301,6 +306,10 @@ static struct pm8921_chg_chip *the_chip; static struct pm8xxx_adc_arb_btm_param btm_config; +#ifdef CONFIG_WIRELESS_CHARGER +static int wireless_charging; +#endif + static int pm_chg_masked_write(struct pm8921_chg_chip *chip, u16 addr, u8 mask, u8 val) { @@ -539,6 +548,7 @@ static int pm_chg_vddmax_set(struct pm8921_chg_chip *chip, int voltage) ret |= __pm_chg_vddmax_set(chip, current_mv); } } + ret |= __pm_chg_vddmax_set(chip, voltage); return ret; } @@ -1286,16 +1296,36 @@ static int pm_power_set_property_usb(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { + struct pm8921_chg_chip *chip = the_chip; + /* Check if called before init */ - if (!the_chip) + if (!chip) return -EINVAL; switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + if (val->intval && + val->intval <= (USB_WALL_THRESHOLD_MA*1000)) { + usb_target_ma = 0; + } else { + usb_target_ma = val->intval/1000; + if (!delayed_work_pending(&chip->unplug_check_work)) { + schedule_delayed_work( + &chip->unplug_check_work, + round_jiffies_relative(msecs_to_jiffies + (UNPLUG_CHECK_WAIT_PERIOD_MS))); + } + } + pr_info("usb_target_ma %d\n", usb_target_ma); + break; + case POWER_SUPPLY_PROP_PRESENT: + case POWER_SUPPLY_PROP_ONLINE: + break; case POWER_SUPPLY_PROP_SCOPE: if (val->intval == POWER_SUPPLY_SCOPE_SYSTEM) - return switch_usb_to_host_mode(the_chip); + return switch_usb_to_host_mode(chip); if (val->intval == POWER_SUPPLY_SCOPE_DEVICE) - return switch_usb_to_charge_mode(the_chip); + return switch_usb_to_charge_mode(chip); else return -EINVAL; break; @@ -1338,10 +1368,12 @@ static int pm_power_get_property_usb(struct power_supply *psy, return 0; /* USB charging */ - if (usb_target_ma < USB_WALL_THRESHOLD_MA) + if (usb_target_ma == 0) + val->intval = the_chip->usb_present; + else if (usb_target_ma <= USB_WALL_THRESHOLD_MA) val->intval = is_usb_chg_plugged_in(the_chip); else - return 0; + return 0; break; case POWER_SUPPLY_PROP_SCOPE: @@ -1458,15 +1490,19 @@ static int get_prop_batt_health(struct pm8921_chg_chip *chip) { int temp; - temp = pm_chg_get_rt_status(chip, BATTTEMP_HOT_IRQ); - if (temp) - return POWER_SUPPLY_HEALTH_OVERHEAT; + if (chip->ext_batt_temp_monitor) { + return chip->ext_batt_health; + } else { + temp = pm_chg_get_rt_status(chip, BATTTEMP_HOT_IRQ); + if (temp) + return POWER_SUPPLY_HEALTH_OVERHEAT; - temp = pm_chg_get_rt_status(chip, BATTTEMP_COLD_IRQ); - if (temp) - return POWER_SUPPLY_HEALTH_COLD; + temp = pm_chg_get_rt_status(chip, BATTTEMP_COLD_IRQ); + if (temp) + return POWER_SUPPLY_HEALTH_COLD; - return POWER_SUPPLY_HEALTH_GOOD; + return POWER_SUPPLY_HEALTH_GOOD; + } } static int get_prop_charge_type(struct pm8921_chg_chip *chip) @@ -1514,10 +1550,25 @@ static int get_prop_batt_status(struct pm8921_chg_chip *chip) if (!pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ) || !pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ) || pm_chg_get_rt_status(chip, CHGHOT_IRQ) - || pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ)) + || (!chip->eoc_check_soc && + pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ)) + || (chip->ext_batt_temp_monitor && + (chip->ext_batt_health == POWER_SUPPLY_HEALTH_OVERHEAT))) batt_state = POWER_SUPPLY_STATUS_NOT_CHARGING; } + + if (chip->eoc_check_soc) { + if (get_prop_batt_capacity(chip) == 100) { + if (batt_state == POWER_SUPPLY_STATUS_CHARGING) + batt_state = POWER_SUPPLY_STATUS_FULL; + } else { + if (batt_state == POWER_SUPPLY_STATUS_FULL) + batt_state = POWER_SUPPLY_STATUS_CHARGING; + } + } + + pr_debug("batt_state = %d fsm_state = %d \n",batt_state, fsm_state); return batt_state; } @@ -1551,6 +1602,12 @@ static int pm_batt_power_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_STATUS: +#ifdef CONFIG_WIRELESS_CHARGER + if(wireless_charging) { + val->intval = 1; //POWER_SUPPLY_STATUS_CHARGING + break; + } +#endif val->intval = get_prop_batt_status(chip); break; case POWER_SUPPLY_PROP_CHARGE_TYPE: @@ -1737,6 +1794,32 @@ int pm8921_charger_enable(bool enable) } EXPORT_SYMBOL(pm8921_charger_enable); +int pm8921_force_start_charging(void) +{ + int rc; + + if (!the_chip) { + pr_err("called before init\n"); + return -EINVAL; + } + + if (the_chip->eoc_check_soc) { + rc = pm_chg_vbatdet_set(the_chip, + the_chip->max_voltage_mv); + if (rc) { + pr_err("failed to set vbatdet\n"); + return rc; + } + } + + rc = pm_chg_auto_enable(the_chip, 1); + if (rc) + pr_err("Failed rc=%d\n", rc); + + return rc; +} +EXPORT_SYMBOL(pm8921_force_start_charging); + int pm8921_is_usb_chg_plugged_in(void) { if (!the_chip) { @@ -1814,6 +1897,11 @@ int pm8921_set_max_battery_charge_current(int ma) pr_err("called before init\n"); return -EINVAL; } + + if (thermal_mitigation != 0 && the_chip->thermal_mitigation) + ma = min((unsigned int)ma, + the_chip->thermal_mitigation[thermal_mitigation]); + return pm_chg_ibatmax_set(the_chip, ma); } EXPORT_SYMBOL(pm8921_set_max_battery_charge_current); @@ -1961,13 +2049,82 @@ int pm8921_set_usb_power_supply_type(enum power_supply_type type) if (type < POWER_SUPPLY_TYPE_USB) return -EINVAL; - the_chip->usb_psy.type = type; power_supply_changed(&the_chip->usb_psy); power_supply_changed(&the_chip->dc_psy); return 0; } EXPORT_SYMBOL_GPL(pm8921_set_usb_power_supply_type); +#ifdef CONFIG_WIRELESS_CHARGER +int set_wireless_power_supply_control(int value) +{ + if (!the_chip) { + pr_err("called before init\n"); + return -EINVAL; + } + + wireless_charging = value; + power_supply_changed(&the_chip->batt_psy); + + return 0; +} +EXPORT_SYMBOL(set_wireless_power_supply_control); +#endif + +int pm8921_set_ext_battery_health(int health, int i_limit) +{ + if (!the_chip) { + pr_err("called before init\n"); + return -EINVAL; + } + + the_chip->ext_batt_health = health; + the_chip->ext_warm_i_limit = i_limit; + + pr_debug("health = %d i_decrease = %d\n", the_chip->ext_batt_health, + the_chip->ext_warm_i_limit); + return 0; +} +EXPORT_SYMBOL(pm8921_set_ext_battery_health); + +int pm8921_get_batt_state(void) +{ + int batt_state = POWER_SUPPLY_STATUS_DISCHARGING; + int fsm_state; + int i; + + if (!the_chip) { + pr_err("called before init\n"); + return -EINVAL; + } + + fsm_state = pm_chg_get_fsm_state(the_chip); + + for (i = 0; i < ARRAY_SIZE(map); i++) + if (map[i].fsm_state == fsm_state) + batt_state = map[i].batt_state; + + pr_debug("batt_state = %d fsm_state = %d \n",batt_state, fsm_state); + return batt_state; +} +EXPORT_SYMBOL(pm8921_get_batt_state); + +int pm8921_get_batt_health(void) +{ + int batt_health; + + if (!the_chip) { + pr_err("called before init\n"); + return -EINVAL; + } + + batt_health = get_prop_batt_health(the_chip); + + pr_debug("batt health = %d\n", batt_health); + return batt_health; +} +EXPORT_SYMBOL(pm8921_get_batt_health); + int pm8921_batt_temperature(void) { if (!the_chip) { @@ -2428,11 +2585,15 @@ static irqreturn_t vbatdet_low_irq_handler(int irq, void *data) high_transition = pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ); if (high_transition) { - /* enable auto charging */ - pm_chg_auto_enable(chip, !charging_disabled); - pr_info("batt fell below resume voltage %s\n", - charging_disabled ? "" : "charger enabled"); + if (!chip->eoc_check_soc + || pm_chg_get_fsm_state(data) == FSM_STATE_ON_BAT_3) { + /* enable auto charging */ + pm_chg_auto_enable(chip, !charging_disabled); + pr_info("batt fell below resume voltage %s\n", + charging_disabled ? "" : "charger enabled"); + } } + pr_debug("fsm_state=%d\n", pm_chg_get_fsm_state(data)); power_supply_changed(&chip->batt_psy); @@ -2570,29 +2731,43 @@ static void unplug_check_worker(struct work_struct *work) rc = pm8xxx_readb(chip->dev->parent, PBL_ACCESS1, &active_path); if (rc) { - pr_err("Failed to read PBL_ACCESS1 rc=%d\n", rc); - return; + pr_warn("Failed to read PBL_ACCESS1 rc=%d\n", rc); + //return; } chip->active_path = active_path; - - active_chg_plugged_in = is_active_chg_plugged_in(chip, active_path); + if (the_chip->usb_present) { + active_path = USB_ACTIVE_BIT; + active_chg_plugged_in = the_chip->usb_present; + } else { + active_chg_plugged_in = is_active_chg_plugged_in(chip, + active_path); + } pr_debug("active_path = 0x%x, active_chg_plugged_in = %d\n", active_path, active_chg_plugged_in); if (active_path & USB_ACTIVE_BIT) { pr_debug("USB charger active\n"); pm_chg_iusbmax_get(chip, &usb_ma); - if (usb_ma == 500 && !usb_target_ma) { - pr_debug("Stopping Unplug Check Worker USB == 500mA\n"); - disable_input_voltage_regulation(chip); - return; - } + if (!usb_target_ma) { + if (usb_ma > 500) { + usb_ma = 500; + __pm8921_charger_vbus_draw(usb_ma); + pr_info("usb_now=%d, usb_target = %d\n", + usb_ma, 500); + goto check_again_later; + } else if (usb_ma == 500) { + pr_info("Stopping Unplug Check Worker" + " USB == 500mA\n"); + disable_input_voltage_regulation(chip); + return; + } - if (usb_ma <= 100) { - pr_debug( - "Unenumerated or suspended usb_ma = %d skip\n", - usb_ma); - goto check_again_later; + if (usb_ma <= 100) { + pr_debug( + "Unenumerated or suspended usb_ma = %d" + " skip\n", usb_ma); + goto check_again_later; + } } } else if (active_path & DC_ACTIVE_BIT) { pr_debug("DC charger active\n"); @@ -2604,7 +2779,7 @@ static void unplug_check_worker(struct work_struct *work) /* No charger active */ if (!(is_usb_chg_plugged_in(chip) && !(is_dc_chg_plugged_in(chip)))) { - pr_debug( + pr_info( "Stop: chg removed reg_loop = %d, fsm = %d ibat = %d\n", pm_chg_get_regulation_loop(chip), pm_chg_get_fsm_state(chip), @@ -2623,7 +2798,7 @@ static void unplug_check_worker(struct work_struct *work) usb_target_ma = usb_ma; /* end AICL here */ __pm8921_charger_vbus_draw(usb_ma); - pr_debug("usb_now=%d, usb_target = %d\n", + pr_info("VIN: usb_now=%d, usb_target = %d\n", usb_ma, usb_target_ma); } } @@ -2657,8 +2832,13 @@ static void unplug_check_worker(struct work_struct *work) } } } - - active_chg_plugged_in = is_active_chg_plugged_in(chip, active_path); + if(the_chip->usb_present) { + active_path = USB_ACTIVE_BIT; + active_chg_plugged_in =the_chip->usb_present; + } else { + active_chg_plugged_in = is_active_chg_plugged_in(chip, + active_path); + } pr_debug("active_path = 0x%x, active_chg = %d\n", active_path, active_chg_plugged_in); chg_gone = pm_chg_get_rt_status(chip, CHG_GONE_IRQ); @@ -2674,12 +2854,13 @@ static void unplug_check_worker(struct work_struct *work) if (usb_ma < usb_target_ma) { increase_usb_ma_value(&usb_ma); __pm8921_charger_vbus_draw(usb_ma); - pr_debug("usb_now=%d, usb_target = %d\n", + pr_info("usb_now=%d, usb_target = %d\n", usb_ma, usb_target_ma); } else { usb_target_ma = usb_ma; } } + check_again_later: /* schedule to check again later */ schedule_delayed_work(&chip->unplug_check_work, @@ -2702,6 +2883,15 @@ static irqreturn_t fastchg_irq_handler(int irq, void *data) { struct pm8921_chg_chip *chip = data; int high_transition; + int rc; + + if (chip->eoc_check_soc) { + rc = pm_chg_vbatdet_set(chip, + chip->max_voltage_mv + - chip->resume_voltage_delta); + if (rc) + pr_err("failed to set vbatdet rc=%d\n", rc); + } high_transition = pm_chg_get_rt_status(chip, FASTCHG_IRQ); if (high_transition && !delayed_work_pending(&chip->eoc_work)) { @@ -2710,6 +2900,7 @@ static irqreturn_t fastchg_irq_handler(int irq, void *data) round_jiffies_relative(msecs_to_jiffies (EOC_CHECK_PERIOD_MS))); } + power_supply_changed(&chip->batt_psy); bms_notify_check(chip); return IRQ_HANDLED; @@ -2768,6 +2959,28 @@ static irqreturn_t batttemp_cold_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static void unplug_usbcheck_work(struct work_struct *work) +{ + int usb_vin; + struct pm8xxx_adc_chan_result vchg; + struct delayed_work *dwork = to_delayed_work(work); + struct pm8921_chg_chip *chip = container_of(dwork, + struct pm8921_chg_chip, unplug_usbcheck_work); + + pm8xxx_adc_read(CHANNEL_USBIN, &vchg); + usb_vin = vchg.physical; + pr_info("usb_vin : %d, max_voltage_mv=%d\n", usb_vin, chip->max_voltage_mv); + + if ((usb_vin/1000 <= chip->max_voltage_mv) && + (usb_vin/1000 > PM8921_CHG_VDDMAX_MIN)){ + pr_info(" Turn off USB ovp \n"); + unplug_ovp_fet_open(chip); + } + power_supply_changed(&chip->batt_psy); + power_supply_changed(&chip->usb_psy); + power_supply_changed(&chip->dc_psy); +} + static irqreturn_t chg_gone_irq_handler(int irq, void *data) { struct pm8921_chg_chip *chip = data; @@ -2776,11 +2989,20 @@ static irqreturn_t chg_gone_irq_handler(int irq, void *data) usb_chg_plugged_in = is_usb_chg_plugged_in(chip); chg_gone = pm_chg_get_rt_status(chip, CHG_GONE_IRQ); - pr_debug("chg_gone=%d, usb_valid = %d\n", chg_gone, usb_chg_plugged_in); - pr_debug("Chg gone fsm_state=%d\n", pm_chg_get_fsm_state(data)); + pr_info("chg_gone=%d, usb_valid = %d\n", chg_gone, usb_chg_plugged_in); + pr_info("Chg gone fsm_state=%d\n", pm_chg_get_fsm_state(data)); + + if (chg_gone && usb_chg_plugged_in) { + pr_info("schedule to check again here\n"); + /* schedule to check again later */ + schedule_delayed_work(&chip->unplug_usbcheck_work, + round_jiffies_relative(msecs_to_jiffies + (UNPLUG_CHECK_WAIT_PERIOD_MS))); + } power_supply_changed(&chip->batt_psy); power_supply_changed(&chip->usb_psy); + power_supply_changed(&chip->dc_psy); return IRQ_HANDLED; } /* @@ -3126,6 +3348,7 @@ static void eoc_worker(struct work_struct *work) struct pm8921_chg_chip, eoc_work); static int count; int end; + int percent_soc; pm_chg_failed_clear(chip, 1); end = is_charging_finished(chip); @@ -3154,6 +3377,12 @@ static void eoc_worker(struct work_struct *work) count = 0; } + if (chip->eoc_check_soc) { + percent_soc = get_prop_batt_capacity(chip); + if (percent_soc == 100) + count = CONSECUTIVE_COUNT; + } + if (count == CONSECUTIVE_COUNT) { count = 0; pr_info("End of Charging\n"); @@ -3200,6 +3429,9 @@ static void set_appropriate_battery_current(struct pm8921_chg_chip *chip) if (chip->is_bat_warm) chg_current = min(chg_current, chip->warm_bat_chg_current); + if (chip->ext_warm_i_limit && chip->ext_batt_temp_monitor) + chg_current = min(chg_current, chip->ext_warm_i_limit); + if (thermal_mitigation != 0 && chip->thermal_mitigation) chg_current = min(chg_current, chip->thermal_mitigation[thermal_mitigation]); @@ -3424,14 +3656,14 @@ static void __devinit determine_initial_state(struct pm8921_chg_chip *chip) pm8921_chg_enable_irq(chip, USBIN_VALID_IRQ); pm8921_chg_enable_irq(chip, BATT_REMOVED_IRQ); pm8921_chg_enable_irq(chip, BATT_INSERTED_IRQ); - pm8921_chg_enable_irq(chip, USBIN_OV_IRQ); - pm8921_chg_enable_irq(chip, USBIN_UV_IRQ); pm8921_chg_enable_irq(chip, DCIN_OV_IRQ); pm8921_chg_enable_irq(chip, DCIN_UV_IRQ); pm8921_chg_enable_irq(chip, CHGFAIL_IRQ); pm8921_chg_enable_irq(chip, FASTCHG_IRQ); pm8921_chg_enable_irq(chip, VBATDET_LOW_IRQ); - pm8921_chg_enable_irq(chip, BAT_TEMP_OK_IRQ); + + if (!chip->ext_batt_temp_monitor) + pm8921_chg_enable_irq(chip, BAT_TEMP_OK_IRQ); spin_lock_irqsave(&vbus_lock, flags); if (usb_chg_current) { @@ -4067,6 +4299,105 @@ static void create_debugfs_entries(struct pm8921_chg_chip *chip) } } +int pm8921_stop_chg_disable_irq(void) +{ + + struct pm8921_chg_chip *chip = the_chip; + + pm8921_chg_disable_irq(chip, ATCFAIL_IRQ); + pm8921_chg_disable_irq(chip, CHGHOT_IRQ); + pm8921_chg_disable_irq(chip, ATCDONE_IRQ); + pm8921_chg_disable_irq(chip, FASTCHG_IRQ); + pm8921_chg_disable_irq(chip, CHGDONE_IRQ); + pm8921_chg_disable_irq(chip, VBATDET_IRQ); + pm8921_chg_disable_irq(chip, VBATDET_LOW_IRQ); + + return 1; +} +int pm8921_start_chg_enable_irq(void) +{ + + struct pm8921_chg_chip *chip = the_chip; + + pm8921_chg_enable_irq(chip, ATCFAIL_IRQ); + pm8921_chg_enable_irq(chip, CHGHOT_IRQ); + pm8921_chg_enable_irq(chip, ATCDONE_IRQ); + pm8921_chg_enable_irq(chip, FASTCHG_IRQ); + pm8921_chg_enable_irq(chip, CHGDONE_IRQ); + pm8921_chg_enable_irq(chip, VBATDET_IRQ); + pm8921_chg_enable_irq(chip, VBATDET_LOW_IRQ); + + return 1; +} + +static ssize_t pm8921_chg_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int fsm_state, is_charging, r; + bool b_chg_ok = false; + + if (!the_chip) { + pr_err("called before init\n"); + return -EINVAL; + } + + fsm_state = pm_chg_get_fsm_state(the_chip); + is_charging = is_battery_charging(fsm_state); + + if (is_charging) { + b_chg_ok = true; + r = sprintf(buf, "%d\n", b_chg_ok); + pr_info("pm8921_chg_status_show , true ! buf = %s, is_charging = %d\n", + buf, is_charging); + } else { + b_chg_ok = false; + r = sprintf(buf, "%d\n", b_chg_ok); + pr_info("pm8921_chg_status_show , false ! buf = %s, is_charging = %d\n", + buf, is_charging); + } + + return r; +} + +static ssize_t pm8921_chg_status_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0, batt_status = 0; + struct pm8921_chg_chip *chip = the_chip; + + if (!count) + return -EINVAL; + + batt_status = get_prop_batt_status(chip); + + if (strncmp(buf, "0", 1) == 0) { + /* stop charging */ + pr_info("pm8921_chg_status_store : stop charging start\n"); + if (batt_status == POWER_SUPPLY_STATUS_CHARGING) { + ret = pm8921_stop_chg_disable_irq(); + pm_chg_auto_enable(chip, 0); + pm_chg_charge_dis(chip,1); + pr_info("pm8921_chg_status_store : stop charging end\n"); + } + } else if (strncmp(buf, "1", 1) == 0) { + /* start charging */ + pr_info("pm8921_chg_status_store : start charging start\n"); + if (batt_status != POWER_SUPPLY_STATUS_CHARGING) { + ret = pm8921_start_chg_enable_irq(); + pm_chg_auto_enable(chip, 1); + pm_chg_charge_dis(chip,0); + pr_info("pm8921_chg_status_store : start charging end\n"); + } + } + + if(ret == 0) + return -EINVAL; + + return ret; +} +DEVICE_ATTR(charge, 0664, pm8921_chg_status_show, pm8921_chg_status_store); + static int pm8921_charger_suspend_noirq(struct device *dev) { int rc; @@ -4199,7 +4530,11 @@ static int __devinit pm8921_charger_probe(struct platform_device *pdev) chip->hot_thr = pdata->hot_thr; chip->rconn_mohm = pdata->rconn_mohm; chip->led_src_config = pdata->led_src_config; - chip->has_dc_supply = pdata->has_dc_supply; + chip->has_dc_supply = pdata->has_dc_supply; + chip->ext_batt_temp_monitor = pdata->ext_batt_temp_monitor; + chip->eoc_check_soc = pdata->eoc_check_soc; + if (chip->ext_batt_temp_monitor) + chip->ext_batt_health = POWER_SUPPLY_HEALTH_GOOD; rc = pm8921_chg_hw_init(chip); if (rc) { @@ -4256,6 +4591,7 @@ static int __devinit pm8921_charger_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&chip->vin_collapse_check_work, vin_collapse_check_worker); INIT_DELAYED_WORK(&chip->unplug_check_work, unplug_check_worker); + INIT_DELAYED_WORK(&chip->unplug_usbcheck_work, unplug_usbcheck_work); rc = request_irqs(chip, pdev); if (rc) { @@ -4266,7 +4602,8 @@ static int __devinit pm8921_charger_probe(struct platform_device *pdev) enable_irq_wake(chip->pmic_chg_irq[USBIN_VALID_IRQ]); enable_irq_wake(chip->pmic_chg_irq[USBIN_OV_IRQ]); enable_irq_wake(chip->pmic_chg_irq[USBIN_UV_IRQ]); - enable_irq_wake(chip->pmic_chg_irq[BAT_TEMP_OK_IRQ]); + if (!chip->ext_batt_temp_monitor) + enable_irq_wake(chip->pmic_chg_irq[BAT_TEMP_OK_IRQ]); enable_irq_wake(chip->pmic_chg_irq[VBATDET_LOW_IRQ]); enable_irq_wake(chip->pmic_chg_irq[FASTCHG_IRQ]); /* @@ -4300,6 +4637,12 @@ static int __devinit pm8921_charger_probe(struct platform_device *pdev) /* determine what state the charger is in */ determine_initial_state(chip); + rc = device_create_file(&pdev->dev, &dev_attr_charge); + if (rc) { + pr_err("Couldn't device_create_file charge! rc=%d\n",rc); + goto free_irq; + } + if (chip->update_time) { INIT_DELAYED_WORK(&chip->update_heartbeat_work, update_heartbeat); @@ -4326,6 +4669,7 @@ static int __devexit pm8921_charger_remove(struct platform_device *pdev) { struct pm8921_chg_chip *chip = platform_get_drvdata(pdev); + device_remove_file(&pdev->dev, &dev_attr_charge); free_irqs(chip); platform_set_drvdata(pdev, NULL); the_chip = NULL; diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 4368e7d61316..27757d952bea 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -45,7 +45,10 @@ static ssize_t power_supply_show_property(struct device *dev, char *buf) { static char *type_text[] = { "Unknown", "Battery", "UPS", "Mains", "USB", - "USB_DCP", "USB_CDP", "USB_ACA" + "USB_DCP", "USB_CDP", "USB_ACA", +#ifdef CONFIG_WIRELESS_CHARGER + "Wireless" +#endif }; static char *status_text[] = { "Unknown", "Charging", "Discharging", "Not charging", "Full" diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index f6b5e71ac134..73ca6934debb 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -1114,6 +1114,8 @@ static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA) return 0; } /* Set max current limit */ + dev_info(motg->phy.dev, "current: %d -> %d (mA)\n", + motg->cur_power, mA); if (power_supply_set_current_limit(psy, 1000*mA)) goto psy_not_supported; @@ -1148,12 +1150,8 @@ static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA) dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA); - /* - * Use Power Supply API if supported, otherwise fallback - * to legacy pm8921 API. - */ - if (msm_otg_notify_power_supply(motg, mA)) - pm8921_charger_vbus_draw(mA); + pm8921_charger_vbus_draw(mA); + msm_otg_notify_power_supply(motg, mA); motg->cur_power = mA; } diff --git a/include/linux/mfd/pm8xxx/core.h b/include/linux/mfd/pm8xxx/core.h index 6e754b82058a..282593c38cb1 100644 --- a/include/linux/mfd/pm8xxx/core.h +++ b/include/linux/mfd/pm8xxx/core.h @@ -75,6 +75,27 @@ enum pm8xxx_version { #define PM8XXX_REVISION_8917_TEST 0 #define PM8XXX_REVISION_8917_1p0 1 +#define PM8XXX_RESTART_UNKNOWN 0 +#define PM8XXX_RESTART_CBL 1 +#define PM8XXX_RESTART_KPD 2 +#define PM8XXX_RESTART_CHG 3 +#define PM8XXX_RESTART_SMPL 4 +#define PM8XXX_RESTART_RTC 5 +#define PM8XXX_RESTART_HARD_RESET 6 +#define PM8XXX_RESTART_GEN_PURPOSE 7 +#define PM8XXX_RESTART_REASON_MASK 0x07 + +static const char * const pm8xxx_restart_reason_str[] = { + [0] = "Unknown", + [1] = "Triggered from CBL (external charger)", + [2] = "Triggered from KPD (power key press)", + [3] = "Triggered from CHG (usb charger insertion)", + [4] = "Triggered from SMPL (sudden momentary power loss)", + [5] = "Triggered from RTC (real time clock)", + [6] = "Triggered by Hard Reset", + [7] = "Triggered by General Purpose Trigger", +}; + struct pm8xxx_drvdata { int (*pmic_readb) (const struct device *dev, u16 addr, u8 *val); @@ -88,6 +109,8 @@ struct pm8xxx_drvdata { int irq); enum pm8xxx_version (*pmic_get_version) (const struct device *dev); int (*pmic_get_revision) (const struct device *dev); + u8 (*pmic_restart_reason) + (const struct device *dev); void *pm_chip_data; }; @@ -156,4 +179,12 @@ static inline int pm8xxx_get_revision(const struct device *dev) return dd->pmic_get_revision(dev); } +static inline u8 pm8xxx_restart_reason(const struct device *dev) +{ + struct pm8xxx_drvdata *dd = dev_get_drvdata(dev); + + if (!dd) + return -EINVAL; + return dd->pmic_restart_reason(dev); +} #endif diff --git a/include/linux/mfd/pm8xxx/pm8921-bms.h b/include/linux/mfd/pm8xxx/pm8921-bms.h index a73a2849b6d2..722ecda7dd35 100644 --- a/include/linux/mfd/pm8xxx/pm8921-bms.h +++ b/include/linux/mfd/pm8xxx/pm8921-bms.h @@ -108,6 +108,7 @@ enum battery_type { BATT_UNKNOWN = 0, BATT_PALLADIUM, BATT_DESAY, + BATT_LGE, }; /** @@ -134,11 +135,18 @@ struct pm8921_bms_platform_data { int ignore_shutdown_soc; int adjust_soc_low_threshold; int chg_term_ua; + int eoc_check_soc; + int bms_support_wlc; + int wlc_term_ua; + int wlc_max_voltage_uv; + int (*wlc_is_plugged)(void); + int first_fixed_iavg_ma; }; #if defined(CONFIG_PM8921_BMS) || defined(CONFIG_PM8921_BMS_MODULE) extern struct pm8921_bms_battery_data palladium_1500_data; extern struct pm8921_bms_battery_data desay_5200_data; +extern struct pm8921_bms_battery_data lge_2100_mako_data; /** * pm8921_bms_get_vsense_avg - return the voltage across the sense * resitor in microvolts diff --git a/include/linux/mfd/pm8xxx/pm8921-charger.h b/include/linux/mfd/pm8xxx/pm8921-charger.h index 62ec59764823..b86efbd9d5ff 100644 --- a/include/linux/mfd/pm8xxx/pm8921-charger.h +++ b/include/linux/mfd/pm8xxx/pm8921-charger.h @@ -138,6 +138,7 @@ struct pm8921_charger_platform_data { unsigned int max_bat_chg_current; unsigned int cool_bat_chg_current; unsigned int warm_bat_chg_current; + int ext_batt_temp_monitor; unsigned int cool_bat_voltage; unsigned int warm_bat_voltage; unsigned int (*get_batt_capacity_percent) (void); @@ -157,6 +158,7 @@ struct pm8921_charger_platform_data { enum pm8921_chg_hot_thr hot_thr; int rconn_mohm; enum pm8921_chg_led_src_config led_src_config; + int eoc_check_soc; }; enum pm8921_charger_source { @@ -285,6 +287,11 @@ int pm8921_usb_ovp_set_hystersis(enum pm8921_usb_debounce_time ms); * */ int pm8921_usb_ovp_disable(int disable); + +int pm8921_get_batt_state(void); +int pm8921_force_start_charging(void); +int pm8921_get_batt_health(void); + /** * pm8921_is_batfet_closed - battery fet status * diff --git a/include/linux/platform_data/battery_temp_ctrl.h b/include/linux/platform_data/battery_temp_ctrl.h new file mode 100644 index 000000000000..64ded399e897 --- /dev/null +++ b/include/linux/platform_data/battery_temp_ctrl.h @@ -0,0 +1,44 @@ +/* +* Copyright(c) 2012, LG Electronics Inc. All rights reserved. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +*/ + +#ifndef __BATTERY_TEMP_CTRL_H +#define __BATTERY_TEMP_CTRL_H + +struct batt_temp_pdata { + int (*set_chg_i_limit)(int max_current); + int (*get_chg_i_limit)(void); + int (*set_health_state)(int state, int i_value); + int (*enable_charging)(void); + int (*disable_charging)(void); + int (*is_ext_power)(void); + int update_time; + int *temp_level; + int temp_nums; + int thr_mvolt; + int i_decrease; + int i_restore; +}; + +enum { + TEMP_LEVEL_POWER_OFF = 0, + TEMP_LEVEL_WARNINGOVERHEAT, + TEMP_LEVEL_HOT_STOPCHARGING, + TEMP_LEVEL_DECREASING, + TEMP_LEVEL_HOT_RECHARGING, + TEMP_LEVEL_COLD_RECHARGING, + TEMP_LEVEL_WARNING_COLD, + TEMP_LEVEL_COLD_STOPCHARGING, + TEMP_LEVEL_NORMAL +}; +#endif diff --git a/include/linux/power/bq51051b_charger.h b/include/linux/power/bq51051b_charger.h new file mode 100644 index 000000000000..c2f0d254b3e8 --- /dev/null +++ b/include/linux/power/bq51051b_charger.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012, Kyungtae Oh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __LINUX_POWER_BQ51051B_CHARGER_H__ +#define __LINUX_POWER_BQ51051B_CHARGER_H__ + +#define BQ51051B_WLC_DEV_NAME "bq51051b_wlc" + +#define WLC_DEG + +#ifdef WLC_DEG +#define WLC_DBG_INFO(fmt, args...) \ + pr_info("wlc: %s: " fmt, __func__, ##args) +#define WLC_DBG(fmt, args...) \ + pr_debug("wlc: %s: " fmt, __func__, ##args) +#else +#define WLC_DBG_INFO(fmt, args...) do { } while(0) +#define WLC_DBG(fmt, arges...) do { } while(0) +#endif + +struct bq51051b_wlc_platform_data { + unsigned int chg_state_gpio; + unsigned int active_n_gpio; + unsigned int wireless_charging; + int (*wlc_is_plugged)(void); +}; +#endif diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index cb56293dc7bd..035544cf2968 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -140,6 +140,9 @@ enum power_supply_type { POWER_SUPPLY_TYPE_USB_DCP, /* Dedicated Charging Port */ POWER_SUPPLY_TYPE_USB_CDP, /* Charging Downstream Port */ POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */ +#ifdef CONFIG_WIRELESS_CHARGER + POWER_SUPPLY_TYPE_WIRELESS, +#endif }; union power_supply_propval {