Revert "Revert "MALI: midgard: support sharing regulator with other devices""
[firefly-linux-kernel-4.4.55.git] / drivers / power / rk818_charger.c
index ce201980b281884998ba2870cd7e5183a0b86683..538952e61d54ee4d61a3f00957b6a020af1ab64b 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/power_supply.h>
 #include <linux/power/rk_usbbc.h>
 #include <linux/regmap.h>
+#include <linux/rk_keys.h>
 #include <linux/rtc.h>
 #include <linux/timer.h>
 #include <linux/workqueue.h>
@@ -79,8 +80,6 @@ module_param_named(dbg_level, dbg_enable, int, 0644);
 
 #define DRIVER_VERSION         "1.0"
 
-extern void rk_send_wakeup_key(void);
-
 static const u16 chrg_vol_sel_array[] = {
        4050, 4100, 4150, 4200, 4250, 4300, 4350
 };
@@ -114,6 +113,7 @@ struct charger_platform_data {
        bool support_dc_det;
        int virtual_power;
        int sample_res;
+       int otg5v_suspend_enable;
        bool extcon;
 };
 
@@ -151,8 +151,11 @@ struct rk818_charger {
        u8 chrg_input;
        u8 chrg_current;
        u8 res_div;
-       u8 plug_in_irq;
-       u8 plug_out_irq;
+       u8 sleep_set_off_reg1;
+       u8 plugin_trigger;
+       u8 plugout_trigger;
+       int plugin_irq;
+       int plugout_irq;
 };
 
 static int rk818_reg_read(struct rk818_charger *cg, u8 reg)
@@ -520,17 +523,28 @@ static void rk818_cg_set_otg_state(struct rk818_charger *cg, int state)
 {
        switch (state) {
        case USB_OTG_POWER_ON:
-               rk818_reg_set_bits(cg, RK818_INT_STS_MSK_REG2,
-                                  PLUG_IN_MSK, PLUG_IN_MSK);
-               rk818_reg_set_bits(cg, RK818_INT_STS_MSK_REG2,
-                                  PLUG_OUT_MSK, PLUG_OUT_MSK);
-               rk818_reg_set_bits(cg, RK818_DCDC_EN_REG,
-                                  OTG_EN_MASK, OTG_EN_MASK);
+               if (cg->otg_in) {
+                       CG_INFO("otg5v is on yet, ignore..\n");
+               } else {
+                       cg->otg_in = 1;
+                       disable_irq(cg->plugin_irq);
+                       disable_irq(cg->plugout_irq);
+                       rk818_reg_set_bits(cg, RK818_DCDC_EN_REG,
+                                          OTG_EN_MASK, OTG_EN_MASK);
+                       CG_INFO("enable otg5v\n");
+               }
                break;
        case USB_OTG_POWER_OFF:
-               rk818_reg_clear_bits(cg, RK818_INT_STS_MSK_REG2, PLUG_IN_MSK);
-               rk818_reg_clear_bits(cg, RK818_INT_STS_MSK_REG2, PLUG_OUT_MSK);
-               rk818_reg_clear_bits(cg, RK818_DCDC_EN_REG, OTG_EN_MASK);
+               if (!cg->otg_in) {
+                       CG_INFO("otg5v is off yet, ignore..\n");
+               } else {
+                       cg->otg_in = 0;
+                       enable_irq(cg->plugin_irq);
+                       enable_irq(cg->plugout_irq);
+                       rk818_reg_clear_bits(cg, RK818_DCDC_EN_REG,
+                                            OTG_EN_MASK);
+                       CG_INFO("disable otg5v\n");
+               }
                break;
        default:
                dev_err(cg->dev, "error otg type\n");
@@ -570,10 +584,8 @@ static void rk818_cg_dc_det_worker(struct work_struct *work)
                CG_INFO("detect dc charger out..\n");
                rk818_cg_set_chrg_param(cg, DC_TYPE_NONE_CHARGER);
                /* check otg supply, power on anyway */
-               if (cg->otg_in) {
-                       CG_INFO("disable charge, enable otg\n");
+               if (cg->otg_in)
                        rk818_cg_set_otg_state(cg, USB_OTG_POWER_ON);
-               }
        }
 
        rk_send_wakeup_key();
@@ -730,18 +742,13 @@ static void rk818_cg_bc_evt_worker(struct work_struct *work)
                rk818_cg_set_chrg_param(cg, USB_TYPE_CDP_CHARGER);
                break;
        case USB_OTG_POWER_ON:
-               cg->otg_in = 1;
-               if (cg->pdata->power_dc2otg && cg->dc_in) {
+               if (cg->pdata->power_dc2otg && cg->dc_in)
                        CG_INFO("otg power from dc adapter\n");
-               } else {
+               else
                        rk818_cg_set_otg_state(cg, USB_OTG_POWER_ON);
-                       CG_INFO("disable charge, enable otg\n");
-               }
                break;
        case USB_OTG_POWER_OFF:
-               cg->otg_in = 0;
                rk818_cg_set_otg_state(cg, USB_OTG_POWER_OFF);
-               CG_INFO("enable charge, disable otg\n");
                break;
        default:
                break;
@@ -757,13 +764,13 @@ static void rk818_cg_irq_delay_work(struct work_struct *work)
        struct rk818_charger *cg = container_of(work,
                        struct rk818_charger, irq_work.work);
 
-       if (cg->plug_in_irq) {
+       if (cg->plugin_trigger) {
                CG_INFO("pmic: plug in\n");
-               cg->plug_in_irq = 0;
+               cg->plugin_trigger = 0;
                rk_send_wakeup_key();
-       } else if (cg->plug_out_irq) {
+       } else if (cg->plugout_trigger) {
                CG_INFO("pmic: plug out\n");
-               cg->plug_out_irq = 0;
+               cg->plugout_trigger = 0;
                rk818_cg_set_chrg_param(cg, USB_TYPE_NONE_CHARGER);
                rk818_cg_set_chrg_param(cg, DC_TYPE_NONE_CHARGER);
                rk_send_wakeup_key();
@@ -778,7 +785,7 @@ static irqreturn_t rk818_plug_in_isr(int irq, void *cg)
        struct rk818_charger *icg;
 
        icg = (struct rk818_charger *)cg;
-       icg->plug_in_irq = 1;
+       icg->plugin_trigger = 1;
        queue_delayed_work(icg->usb_charger_wq, &icg->irq_work,
                           msecs_to_jiffies(10));
 
@@ -790,7 +797,7 @@ static irqreturn_t rk818_plug_out_isr(int irq, void *cg)
        struct rk818_charger *icg;
 
        icg = (struct rk818_charger *)cg;
-       icg->plug_out_irq = 1;
+       icg->plugout_trigger = 1;
        queue_delayed_work(icg->usb_charger_wq, &icg->irq_work,
                           msecs_to_jiffies(10));
 
@@ -848,6 +855,9 @@ static int rk818_cg_init_irqs(struct rk818_charger *cg)
                return ret;
        }
 
+       cg->plugin_irq = plug_in_irq;
+       cg->plugout_irq = plug_out_irq;
+
        INIT_DELAYED_WORK(&cg->irq_work, rk818_cg_irq_delay_work);
 
        return 0;
@@ -924,20 +934,15 @@ static void rk818_cg_host_evt_worker(struct work_struct *work)
        struct extcon_dev *edev = cg->cable_edev;
 
        /* Determine cable/charger type */
-       if (extcon_get_cable_state_(edev, EXTCON_USB_HOST) > 0) {
+       if (extcon_get_cable_state_(edev, EXTCON_USB_VBUS_EN) > 0) {
                CG_INFO("receive type-c notifier event: OTG ON...\n");
-               cg->otg_in = 1;
-               if (cg->dc_in && cg->pdata->power_dc2otg) {
+               if (cg->dc_in && cg->pdata->power_dc2otg)
                        CG_INFO("otg power from dc adapter\n");
-               } else {
+               else
                        rk818_cg_set_otg_state(cg, USB_OTG_POWER_ON);
-                       CG_INFO("disable charge, enable otg\n");
-               }
-       } else if (extcon_get_cable_state_(edev, EXTCON_USB_HOST) == 0) {
+       } else if (extcon_get_cable_state_(edev, EXTCON_USB_VBUS_EN) == 0) {
                CG_INFO("receive type-c notifier event: OTG OFF...\n");
-               cg->otg_in = 0;
                rk818_cg_set_otg_state(cg, USB_OTG_POWER_OFF);
-               CG_INFO("enble charge, disable otg\n");
        }
 
        rk818_cg_pr_info(cg);
@@ -962,6 +967,7 @@ static void rk818_cg_charger_evt_worker(struct work_struct *work)
        if (charger != USB_TYPE_UNKNOWN_CHARGER) {
                CG_INFO("receive type-c notifier event: %s...\n",
                        event[charger]);
+               cg->usb_charger = charger;
                rk818_cg_set_chrg_param(cg, charger);
                rk818_cg_pr_info(cg);
        }
@@ -1022,7 +1028,7 @@ static long rk818_cg_init_usb(struct rk818_charger *cg)
                /* Register host */
                INIT_DELAYED_WORK(&cg->host_work, rk818_cg_host_evt_worker);
                cg->cable_host_nb.notifier_call = rk818_cg_host_evt_notifier;
-               ret = extcon_register_notifier(edev, EXTCON_USB_HOST,
+               ret = extcon_register_notifier(edev, EXTCON_USB_VBUS_EN,
                                               &cg->cable_host_nb);
                if (ret < 0) {
                        dev_err(dev, "failed to register notifier for HOST\n");
@@ -1049,13 +1055,16 @@ static long rk818_cg_init_usb(struct rk818_charger *cg)
                                                   &cg->cable_cg_nb);
                        extcon_unregister_notifier(edev, EXTCON_CHG_USB_CDP,
                                                   &cg->cable_cg_nb);
-                       extcon_unregister_notifier(edev, EXTCON_USB_HOST,
+                       extcon_unregister_notifier(edev, EXTCON_USB_VBUS_EN,
                                                   &cg->cable_host_nb);
                        return ret;
                }
 
                cg->cable_edev = edev;
 
+               schedule_delayed_work(&cg->host_work, 0);
+               schedule_delayed_work(&cg->usb_work, 0);
+
                CG_INFO("register typec extcon evt notifier\n");
        } else {
                INIT_DELAYED_WORK(&cg->usb_work, rk818_cg_bc_evt_worker);
@@ -1167,6 +1176,13 @@ static int rk818_cg_parse_dt(struct rk818_charger *cg)
                dev_err(dev, "sample_res missing!\n");
        }
 
+       ret = of_property_read_u32(np, "otg5v_suspend_enable",
+                                  &pdata->otg5v_suspend_enable);
+       if (ret < 0) {
+               pdata->otg5v_suspend_enable = 1;
+               dev_err(dev, "otg5v_suspend_enable missing!\n");
+       }
+
        if (!is_battery_exist(cg))
                pdata->virtual_power = 1;
 
@@ -1284,7 +1300,7 @@ static void rk818_charger_shutdown(struct platform_device *pdev)
                                           &cg->cable_cg_nb);
                extcon_unregister_notifier(cg->cable_edev, EXTCON_CHG_USB_CDP,
                                           &cg->cable_cg_nb);
-               extcon_unregister_notifier(cg->cable_edev, EXTCON_USB_HOST,
+               extcon_unregister_notifier(cg->cable_edev, EXTCON_USB_VBUS_EN,
                                           &cg->cable_host_nb);
                extcon_unregister_notifier(cg->cable_edev, EXTCON_USB,
                                           &cg->cable_discnt_nb);
@@ -1299,8 +1315,47 @@ static void rk818_charger_shutdown(struct platform_device *pdev)
                cg->ac_in, cg->usb_in, cg->dc_in, cg->otg_in);
 }
 
+static int rk818_charger_suspend(struct platform_device *pdev,
+                                pm_message_t state)
+{
+       struct rk818_charger *cg = platform_get_drvdata(pdev);
+
+       cg->sleep_set_off_reg1 = rk818_reg_read(cg, RK818_SLEEP_SET_OFF_REG1);
+
+       /* enable sleep boost5v and otg5v */
+       if (cg->pdata->otg5v_suspend_enable) {
+               if ((cg->otg_in && !cg->dc_in) ||
+                   (cg->otg_in && cg->dc_in && !cg->pdata->power_dc2otg)) {
+                       rk818_reg_clear_bits(cg, RK818_SLEEP_SET_OFF_REG1,
+                                            OTG_BOOST_SLP_OFF);
+                       CG_INFO("suspend: otg 5v on\n");
+                       return 0;
+               }
+       }
+
+       /* disable sleep otg5v */
+       rk818_reg_set_bits(cg, RK818_SLEEP_SET_OFF_REG1,
+                          OTG_SLP_SET_OFF, OTG_SLP_SET_OFF);
+       CG_INFO("suspend: otg 5v off\n");
+
+       return 0;
+}
+
+static int rk818_charger_resume(struct platform_device *pdev)
+{
+       struct rk818_charger *cg = platform_get_drvdata(pdev);
+
+       /* resume sleep boost5v and otg5v */
+       rk818_reg_set_bits(cg, RK818_SLEEP_SET_OFF_REG1,
+                          OTG_BOOST_SLP_OFF, cg->sleep_set_off_reg1);
+
+       return 0;
+}
+
 static struct platform_driver rk818_charger_driver = {
        .probe = rk818_charger_probe,
+       .suspend = rk818_charger_suspend,
+       .resume = rk818_charger_resume,
        .shutdown = rk818_charger_shutdown,
        .driver = {
                .name   = "rk818-charger",