ab8500-bm: Charge only mode fixes for the ab9540
authorLee Jones <lee.jones@linaro.org>
Wed, 13 Feb 2013 11:39:19 +0000 (11:39 +0000)
committerLee Jones <lee.jones@linaro.org>
Thu, 7 Mar 2013 04:35:40 +0000 (12:35 +0800)
Fix for charging not getting enabled in charge only mode by
external charger.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
drivers/power/ab8500_charger.c
drivers/power/abx500_chargalg.c
drivers/power/pm2301_charger.c
include/linux/mfd/abx500/ux500_chargalg.h

index 3eb23cf..f1d7123 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/device.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
+#include <linux/notifier.h>
 #include <linux/slab.h>
 #include <linux/platform_device.h>
 #include <linux/power_supply.h>
 #define AB8500_SW_CONTROL_FALLBACK     0x03
 /* Wait for enumeration before charing in us */
 #define WAIT_ACA_RID_ENUMERATION       (5 * 1000)
+/*External charger control*/
+#define AB8500_SYS_CHARGER_CONTROL_REG         0x52
+#define EXTERNAL_CHARGER_DISABLE_REG_VAL       0x03
+#define EXTERNAL_CHARGER_ENABLE_REG_VAL                0x07
 
 /* UsbLineStatus register - usb types */
 enum ab8500_charger_link_status {
@@ -1678,6 +1683,29 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
        return ret;
 }
 
+static int ab8500_external_charger_prepare(struct notifier_block *charger_nb,
+                               unsigned long event, void *data)
+{
+       int ret;
+       struct device *dev = data;
+       /*Toggle External charger control pin*/
+       ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
+                                 AB8500_SYS_CHARGER_CONTROL_REG,
+                                 EXTERNAL_CHARGER_DISABLE_REG_VAL);
+       if (ret < 0) {
+               dev_err(dev, "write reg failed %d\n", ret);
+               goto out;
+       }
+       ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
+                                 AB8500_SYS_CHARGER_CONTROL_REG,
+                                 EXTERNAL_CHARGER_ENABLE_REG_VAL);
+       if (ret < 0)
+               dev_err(dev, "Write reg failed %d\n", ret);
+
+out:
+       return ret;
+}
+
 /**
  * ab8500_charger_usb_check_enable() - enable usb charging
  * @charger:   pointer to the ux500_charger structure
@@ -3221,6 +3249,10 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
 #define ab8500_charger_resume       NULL
 #endif
 
+static struct notifier_block charger_nb = {
+       .notifier_call = ab8500_external_charger_prepare,
+};
+
 static int ab8500_charger_remove(struct platform_device *pdev)
 {
        struct ab8500_charger *di = platform_get_drvdata(pdev);
@@ -3250,6 +3282,11 @@ static int ab8500_charger_remove(struct platform_device *pdev)
        /* Delete the work queue */
        destroy_workqueue(di->charger_wq);
 
+       /* Unregister external charger enable notifier */
+       if (!di->ac_chg.enabled)
+               blocking_notifier_chain_unregister(
+                       &charger_notifier_list, &charger_nb);
+
        flush_scheduled_work();
        if (di->usb_chg.enabled)
                power_supply_unregister(&di->usb_chg.psy);
@@ -3331,6 +3368,11 @@ static int ab8500_charger_probe(struct platform_device *pdev)
        di->ac_chg.enabled = di->bm->ac_enabled;
        di->ac_chg.external = false;
 
+       /*notifier for external charger enabling*/
+       if (!di->ac_chg.enabled)
+               blocking_notifier_chain_register(
+                       &charger_notifier_list, &charger_nb);
+
        /* USB supply */
        /* power_supply base class */
        di->usb_chg.psy.name = "ab8500_usb";
index 8ab65a3..a876976 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/mfd/abx500.h>
 #include <linux/mfd/abx500/ux500_chargalg.h>
 #include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/notifier.h>
 
 /* Watchdog kick interval */
 #define CHG_WD_INTERVAL                        (6 * HZ)
@@ -243,6 +244,9 @@ struct abx500_chargalg {
        struct kobject chargalg_kobject;
 };
 
+/*External charger prepare notifier*/
+BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
+
 /* Main battery properties */
 static enum power_supply_property abx500_chargalg_props[] = {
        POWER_SUPPLY_PROP_STATUS,
@@ -503,6 +507,8 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
 static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
        int vset, int iset)
 {
+       static int abx500_chargalg_ex_ac_enable_toggle;
+
        if (!di->ac_chg || !di->ac_chg->ops.enable)
                return -ENXIO;
 
@@ -515,6 +521,14 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
        di->chg_info.ac_iset = iset;
        di->chg_info.ac_vset = vset;
 
+       /* Enable external charger */
+       if (enable && di->ac_chg->external &&
+           !abx500_chargalg_ex_ac_enable_toggle) {
+               blocking_notifier_call_chain(&charger_notifier_list,
+                                            0, di->dev);
+               abx500_chargalg_ex_ac_enable_toggle++;
+       }
+
        return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
 }
 
index b560fa5..45ef3b9 100644 (file)
@@ -1059,6 +1059,13 @@ static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
        ret = pm2xxx_charger_detection(pm2, &val);
 
        if ((ret == 0) && val) {
+               /*
+                * When boot is due to AC charger plug-in,
+                * read interrupt registers
+                */
+               pm2xxx_reg_read(pm2, PM2XXX_REG_INT1, &val);
+               pm2xxx_reg_read(pm2, PM2XXX_REG_INT2, &val);
+               pm2xxx_reg_read(pm2, PM2XXX_REG_INT4, &val);
                pm2->ac.charger_connected = 1;
                pm2->ac_conn = true;
                power_supply_changed(&pm2->ac_chg.psy);
index 110d12f..fa831f1 100644 (file)
@@ -41,4 +41,6 @@ struct ux500_charger {
        bool external;
 };
 
+extern struct blocking_notifier_head charger_notifier_list;
+
 #endif