diff options
author | Syed Rafiuddin <srafiuddin@nvidia.com> | 2013-05-10 20:42:51 +0530 |
---|---|---|
committer | Riham Haidar <rhaidar@nvidia.com> | 2013-06-12 15:37:14 -0700 |
commit | 68e9930f230b8b2bbbf1d931ad3fbe89d80135c5 (patch) | |
tree | d286af85f35d250e00b0291bf4528b218ee1d04c | |
parent | 74bed38e4163793332ea7acb44fcf12d8e16a695 (diff) |
power: bq2419x: disable otg for 30s when fault happens
disable OTG mode when over temprature and over current
interrupt occurs and re-enable OTG mode after 30sec.
Bug 1285652
Change-Id: I20aeddc61a26e1e9930d99170e313fc2610e8dbe
Signed-off-by: Syed Rafiuddin <srafiuddin@nvidia.com>
Reviewed-on: http://git-master/r/226994
(cherry picked from commit 4e8d08beb0a8659862018eda355c845167adac9d)
Reviewed-on: http://git-master/r/228770
Reviewed-by: Riham Haidar <rhaidar@nvidia.com>
Tested-by: Riham Haidar <rhaidar@nvidia.com>
-rw-r--r-- | drivers/power/bq2419x-charger.c | 73 |
1 files changed, 69 insertions, 4 deletions
diff --git a/drivers/power/bq2419x-charger.c b/drivers/power/bq2419x-charger.c index 4046c9330e66..426b2fac750f 100644 --- a/drivers/power/bq2419x-charger.c +++ b/drivers/power/bq2419x-charger.c @@ -42,6 +42,8 @@ #include <linux/rtc.h> #include <linux/alarmtimer.h> +#define BQ2419x_OTG_ENABLE_TIME 30*HZ + /* input current limit */ static const unsigned int iinlim[] = { 100, 150, 500, 900, 1200, 1500, 2000, 3000, @@ -69,6 +71,7 @@ struct bq2419x_chip { struct power_supply ac; struct power_supply usb; struct mutex mutex; + struct mutex otg_mutex; int ac_online; int usb_online; int in_current_limit; @@ -90,10 +93,12 @@ struct bq2419x_chip { struct task_struct *bq_kworker_task; struct kthread_work bq_wdt_work; struct rtc_device *rtc; + struct delayed_work otg_reset_work; int stop_thread; int suspended; int chg_restart_timeout; int chg_restart_time; + int is_otg_connected; }; static enum power_supply_property bq2419x_psy_props[] = { @@ -142,10 +147,13 @@ static int bq2419x_vbus_enable(struct regulator_dev *rdev) dev_info(bq2419x->dev, "VBUS enabled, charging disabled\n"); + mutex_lock(&bq2419x->otg_mutex); + bq2419x->is_otg_connected = true; ret = regmap_update_bits(bq2419x->regmap, BQ2419X_PWR_ON_REG, BQ2419X_ENABLE_CHARGE_MASK, BQ2419X_ENABLE_VBUS); if (ret < 0) dev_err(bq2419x->dev, "PWR_ON_REG update failed %d", ret); + mutex_unlock(&bq2419x->otg_mutex); return ret; } @@ -156,11 +164,13 @@ static int bq2419x_vbus_disable(struct regulator_dev *rdev) int ret; dev_info(bq2419x->dev, "VBUS disabled, charging enabled\n"); + + mutex_lock(&bq2419x->otg_mutex); + bq2419x->is_otg_connected = false; ret = bq2419x_charger_enable(bq2419x); - if (ret < 0) { + if (ret < 0) dev_err(bq2419x->dev, "Charger enable failed %d", ret); - return ret; - } + mutex_unlock(&bq2419x->otg_mutex); return ret; } @@ -213,6 +223,27 @@ static int bq2419x_usb_get_property(struct power_supply *psy, return 0; } +static void bq2419x_otg_reset_work_handler(struct work_struct *work) +{ + int ret; + struct bq2419x_chip *bq2419x = container_of(to_delayed_work(work), + struct bq2419x_chip, otg_reset_work); + + if (!mutex_is_locked(&bq2419x->otg_mutex)) { + mutex_lock(&bq2419x->otg_mutex); + if (bq2419x->is_otg_connected) { + ret = regmap_update_bits(bq2419x->regmap, + BQ2419X_PWR_ON_REG, + BQ2419X_ENABLE_CHARGE_MASK, + BQ2419X_ENABLE_VBUS); + if (ret < 0) + dev_err(bq2419x->dev, + "PWR_ON_REG update failed %d", ret); + } + mutex_unlock(&bq2419x->otg_mutex); + } +} + static int bq2419x_init(struct bq2419x_chip *bq2419x) { int val = 0; @@ -590,8 +621,21 @@ static irqreturn_t bq2419x_irq(int irq, void *data) } } - if (val & BQ2419x_FAULT_BOOST_FAULT) + if (val & BQ2419x_FAULT_BOOST_FAULT) { dev_err(bq2419x->dev, "Charging Fault: VBUS Overloaded\n"); + mutex_lock(&bq2419x->otg_mutex); + if (bq2419x->is_otg_connected) { + ret = bq2419x_charger_enable(bq2419x); + if (ret < 0) { + dev_err(bq2419x->dev, + "Charger enable failed %d", ret); + return ret; + } + schedule_delayed_work(&bq2419x->otg_reset_work, + BQ2419x_OTG_ENABLE_TIME); + } + mutex_unlock(&bq2419x->otg_mutex); + } switch (val & BQ2419x_FAULT_CHRG_FAULT_MASK) { case BQ2419x_FAULT_CHRG_INPUT: @@ -601,6 +645,18 @@ static irqreturn_t bq2419x_irq(int irq, void *data) case BQ2419x_FAULT_CHRG_THERMAL: dev_err(bq2419x->dev, "Charging Fault: Thermal shutdown\n"); check_chg_state = 1; + mutex_lock(&bq2419x->otg_mutex); + if (bq2419x->is_otg_connected) { + ret = bq2419x_charger_enable(bq2419x); + if (ret < 0) { + dev_err(bq2419x->dev, + "Charger enable failed %d", ret); + return ret; + } + schedule_delayed_work(&bq2419x->otg_reset_work, + BQ2419x_OTG_ENABLE_TIME); + } + mutex_unlock(&bq2419x->otg_mutex); break; case BQ2419x_FAULT_CHRG_SAFTY: dev_err(bq2419x->dev, @@ -912,8 +968,10 @@ static int __devinit bq2419x_probe(struct i2c_client *client, bq2419x->irq = client->irq; bq2419x->rtc = alarmtimer_get_rtcdev(); mutex_init(&bq2419x->mutex); + mutex_init(&bq2419x->otg_mutex); bq2419x->suspended = 0; bq2419x->chg_restart_timeout = 0; + bq2419x->is_otg_connected = 0; ret = bq2419x_show_chip_version(bq2419x); if (ret < 0) { @@ -969,6 +1027,9 @@ static int __devinit bq2419x_probe(struct i2c_client *client, return ret; } + INIT_DELAYED_WORK(&bq2419x->otg_reset_work, + bq2419x_otg_reset_work_handler); + ret = bq2419x_fault_clear_sts(bq2419x); if (ret < 0) { dev_err(bq2419x->dev, "fault clear status failed %d\n", ret); @@ -1006,6 +1067,7 @@ scrub_psy: scrub_chg_reg: regulator_unregister(bq2419x->chg_rdev); mutex_destroy(&bq2419x->mutex); + mutex_destroy(&bq2419x->otg_mutex); return ret; } @@ -1024,6 +1086,8 @@ static int __devexit bq2419x_remove(struct i2c_client *client) power_supply_unregister(&bq2419x->ac); regulator_unregister(bq2419x->chg_rdev); mutex_destroy(&bq2419x->mutex); + mutex_destroy(&bq2419x->otg_mutex); + cancel_delayed_work_sync(&bq2419x->otg_reset_work); return 0; } @@ -1069,6 +1133,7 @@ static void bq2419x_shutdown(struct i2c_client *client) ret = bq2419x_wakealarm(bq2419x, alarm_time); if (ret < 0) dev_err(bq2419x->dev, "RTC wake alarm config failed %d\n", ret); + cancel_delayed_work_sync(&bq2419x->otg_reset_work); } #ifdef CONFIG_PM_SLEEP |