usb: dwc_otg_310: support vbus controlled by both gpio and pmic
[firefly-linux-kernel-4.4.55.git] / drivers / usb / dwc_otg_310 / usbdev_rk3036.c
index d94e8d2611b61951b8031662f71a0f20112b84f8..02dec704802250ce37e0ad48628d9a348ed32825 100755 (executable)
@@ -1,3 +1,4 @@
+#ifdef CONFIG_ARM
 #include "usbdev_rk.h"
 #include "usbdev_grf_regs.h"
 #include "dwc_otg_regs.h"
@@ -20,7 +21,7 @@ static void usb20otg_phy_suspend(void *pdata, int suspend)
 
        if (suspend) {
                /* enable soft control */
-               writel(UOC_HIWORD_UPDATE(0x55, 0x7f, 0),
+               writel(UOC_HIWORD_UPDATE(0x1d1, 0x1ff, 0),
                       RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
                usbpdata->phy_status = 1;
        } else {
@@ -31,71 +32,45 @@ static void usb20otg_phy_suspend(void *pdata, int suspend)
        }
 }
 
-static void usb20otg_soft_reset(void)
+static void usb20otg_soft_reset(void *pdata, enum rkusb_rst_flag rst_type)
 {
-       writel(UOC_HIWORD_UPDATE(0x1, 0x3, 0),
-              RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
-       writel(UOC_HIWORD_UPDATE(0x1, 0x3, 0),
-              RK_GRF_VIRT + RK3036_GRF_UOC1_CON5);
-       /* cru_set_soft_reset(SOFT_RST_USBPOR, true); */
-       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 9),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON);
-       /* cru_set_soft_reset(SOFT_RST_UTMI0, true); */
-       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 7),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON);
-       /* cru_set_soft_reset(SOFT_RST_UTMI1, true); */
-       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 8),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON);
-
-       udelay(15);
-
-       writel(UOC_HIWORD_UPDATE(0x2, 0x3, 0),
-              RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
-       writel(UOC_HIWORD_UPDATE(0x2, 0x3, 0),
-              RK_GRF_VIRT + RK3036_GRF_UOC1_CON5);
+       struct dwc_otg_platform_data *usbpdata = pdata;
+       struct reset_control *rst_otg_h, *rst_otg_p, *rst_otg_c;
 
-       udelay(1500);
-       /* cru_set_soft_reset(SOFT_RST_USBPOR, false); */
-       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 9),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON);
-       udelay(2);
-       /* cru_set_soft_reset(SOFT_RST_UTMI0, false); */
-       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 7),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON);
-       /* cru_set_soft_reset(SOFT_RST_UTMI1, false); */
-       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 8),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON);
-
-       /* ctrler reset */
-       /* cru_set_soft_reset(SOFT_RST_OTGC0, true); */
-       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 7),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
-       /* cru_set_soft_reset(SOFT_RST_OTGC1, true); */
-       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 10),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
-       udelay(2);
-
-       /* cru_set_soft_reset(SOFT_RST_USBOTG0, true); */
-       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 5),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
-       /* cru_set_soft_reset(SOFT_RST_USBOTG1, true); */
-       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 8),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
-       udelay(2);
-       /*
-          cru_set_soft_reset(SOFT_RST_OTGC0,false);
-          cru_set_soft_reset(SOFT_RST_OTGC1,false);
-          ru_set_soft_reset(SOFT_RST_USBOTG0,false);
-          cru_set_soft_reset(SOFT_RST_USBOTG1,false);
-        */
-       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 7),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
-       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 10),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
-       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 5),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
-       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 8),
-              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
+       rst_otg_h = devm_reset_control_get(usbpdata->dev, "otg_ahb");
+       rst_otg_p = devm_reset_control_get(usbpdata->dev, "otg_phy");
+       rst_otg_c = devm_reset_control_get(usbpdata->dev, "otg_controller");
+       if (IS_ERR(rst_otg_h) || IS_ERR(rst_otg_p) || IS_ERR(rst_otg_c)) {
+               dev_err(usbpdata->dev, "Fail to get reset control from dts\n");
+               return;
+       }
+
+       switch(rst_type) {
+       case RST_POR:
+               /* PHY reset */
+               writel(UOC_HIWORD_UPDATE(0x1, 0x3, 0),
+                          RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
+               reset_control_assert(rst_otg_p);
+               udelay(15);
+               writel(UOC_HIWORD_UPDATE(0x2, 0x3, 0),
+                          RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
+               udelay(1500);
+               reset_control_deassert(rst_otg_p);
+               udelay(2);
+
+               /* Controller reset */
+               reset_control_assert(rst_otg_c);
+               reset_control_assert(rst_otg_h);
+
+               udelay(2);
+
+               reset_control_deassert(rst_otg_c);
+               reset_control_deassert(rst_otg_h);
+               break;
+
+       default:
+               break;
+       }
 }
 
 static void usb20otg_clock_init(void *pdata)
@@ -137,7 +112,7 @@ static void usb20otg_clock_enable(void *pdata, int enable)
 static int usb20otg_get_status(int id)
 {
        int ret = -1;
-       u32 soc_status0 = readl(RK_GRF_VIRT + RK3036_GRF_CPU_STATUS0);
+       u32 soc_status0 = readl(RK_GRF_VIRT + RK3036_GRF_SOC_STATUS0);
 
        switch (id) {
        case USB_STATUS_BVABLID:
@@ -167,20 +142,42 @@ static int usb20otg_get_status(int id)
        return ret;
 }
 
+#ifdef CONFIG_RK_USB_UART
+/**
+ *  dwc_otg_uart_enabled - check if a usb-uart bypass func is enabled in DT
+ *
+ *  Returns true if the status property of node "usb_uart" is set to "okay"
+ *  or "ok", if this property is absent it will use the default status "ok"
+ *  0 otherwise
+ */
+static bool dwc_otg_uart_enabled(void)
+{
+       struct device_node *np;
+
+       np = of_find_node_by_name(NULL, "usb_uart");
+       if (np && of_device_is_available(np))
+               return true;
+
+       return false;
+}
+
 static void dwc_otg_uart_mode(void *pdata, int enter_usb_uart_mode)
 {
-#ifdef CONFIG_RK_USB_UART
-       if (1 == enter_usb_uart_mode) {
+       if ((1 == enter_usb_uart_mode) && dwc_otg_uart_enabled()) {
                /* bypass dm, enter uart mode */
-               writel(UOC_HIWORD_UPDATE(0x3, 0x3, 12),
-                      RK_GRF_VIRT + RK3036_GRF_UOC1_CON4);
+               writel(UOC_HIWORD_UPDATE(0x3, 0x3, 12), RK_GRF_VIRT + 
+                          RK3036_GRF_UOC1_CON4);
        } else if (0 == enter_usb_uart_mode) {
                /* enter usb mode */
-               writel(UOC_HIWORD_UPDATE(0x0, 0x3, 12),
-                      RK_GRF_VIRT + RK3036_GRF_UOC1_CON4);
+               writel(UOC_HIWORD_UPDATE(0x0, 0x3, 12), RK_GRF_VIRT + 
+                          RK3036_GRF_UOC1_CON4);
        }
-#endif
 }
+#else
+static void dwc_otg_uart_mode(void *pdata, int enter_usb_uart_mode)
+{
+}
+#endif
 
 static void usb20otg_power_enable(int enable)
 {
@@ -188,13 +185,32 @@ static void usb20otg_power_enable(int enable)
                /* disable otg_drv power */
                if (gpio_is_valid(control_usb->otg_gpios->gpio))
                        gpio_set_value(control_usb->otg_gpios->gpio, 0);
+
+               rk_battery_charger_detect_cb(USB_OTG_POWER_OFF);
        } else if (1 == enable) {
                /* enable otg_drv power */
                if (gpio_is_valid(control_usb->otg_gpios->gpio))
                        gpio_set_value(control_usb->otg_gpios->gpio, 1);
+
+               if (!usb20otg_get_status(USB_STATUS_BVABLID))
+                       rk_battery_charger_detect_cb(USB_OTG_POWER_ON);
+       }
+}
+static void usb20otg_phy_power_down(int power_down)
+{
+       if (power_down == PHY_POWER_DOWN) {
+               if (control_usb->linestate_wakeup) {
+                       /* enable otg0_linestate irq */
+                       writel(UOC_HIWORD_UPDATE(0x3, 0x3, 12),
+                              RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
+                       /* enable otg1_linestate irq */
+                       writel(UOC_HIWORD_UPDATE(0x3, 0x3, 14),
+                              RK_GRF_VIRT + RK3036_GRF_UOC1_CON5);
+               }
+       } else if (power_down == PHY_POWER_UP) {
+               ;
        }
 }
-
 struct dwc_otg_platform_data usb20otg_pdata_rk3036 = {
        .phyclk = NULL,
        .ahbclk = NULL,
@@ -208,7 +224,8 @@ struct dwc_otg_platform_data usb20otg_pdata_rk3036 = {
        .get_status = usb20otg_get_status,
        .power_enable = usb20otg_power_enable,
        .dwc_otg_uart_mode = dwc_otg_uart_mode,
-       .bc_detect_cb = usb20otg_battery_charger_detect_cb,
+       .bc_detect_cb = rk_battery_charger_detect_cb,
+       .phy_power_down = usb20otg_phy_power_down,
 };
 #endif
 
@@ -240,9 +257,45 @@ static void usb20host_phy_suspend(void *pdata, int suspend)
        }
 }
 
-static void usb20host_soft_reset(void)
+static void usb20host_soft_reset(void *pdata, enum rkusb_rst_flag rst_type)
 {
-       ;
+       struct dwc_otg_platform_data *usbpdata = pdata;
+       struct reset_control *rst_host_h, *rst_host_p, *rst_host_c;
+
+       rst_host_h = devm_reset_control_get(usbpdata->dev, "host_ahb");
+       rst_host_p = devm_reset_control_get(usbpdata->dev, "host_phy");
+       rst_host_c = devm_reset_control_get(usbpdata->dev, "host_controller");
+       if (IS_ERR(rst_host_h) || IS_ERR(rst_host_p) || IS_ERR(rst_host_c)) {
+               dev_err(usbpdata->dev, "Fail to get reset control from dts\n");
+               return;
+       }
+
+       switch(rst_type) {
+       case RST_POR:
+               /* PHY reset */
+               writel(UOC_HIWORD_UPDATE(0x1, 0x3, 0),
+                          RK_GRF_VIRT + RK3036_GRF_UOC1_CON5);
+               reset_control_assert(rst_host_p);
+               udelay(15);
+               writel(UOC_HIWORD_UPDATE(0x2, 0x3, 0),
+                          RK_GRF_VIRT + RK3036_GRF_UOC1_CON5);
+
+               udelay(1500);
+               reset_control_deassert(rst_host_p);
+
+               /* Controller reset */
+               reset_control_assert(rst_host_c);
+               reset_control_assert(rst_host_h);
+
+               udelay(5);
+
+               reset_control_deassert(rst_host_c);
+               reset_control_deassert(rst_host_h);
+               break;
+
+       default:
+               break;
+       }
 }
 
 static void usb20host_clock_init(void *pdata)
@@ -282,7 +335,7 @@ static void usb20host_clock_enable(void *pdata, int enable)
 static int usb20host_get_status(int id)
 {
        int ret = -1;
-       u32 soc_status0 = readl(RK_GRF_VIRT + RK3036_GRF_CPU_STATUS0);
+       u32 soc_status0 = readl(RK_GRF_VIRT + RK3036_GRF_SOC_STATUS0);
 
        switch (id) {
        case USB_STATUS_BVABLID:
@@ -360,7 +413,7 @@ static inline void do_wakeup(struct work_struct *work)
 
 static void usb_battery_charger_detect_work(struct work_struct *work)
 {
-       rk_usb_charger_status = usb_battery_charger_detect(0);
+       rk_battery_charger_detect_cb(usb_battery_charger_detect(1));
 }
 
 /********** handler for bvalid irq **********/
@@ -381,12 +434,57 @@ static irqreturn_t bvalid_irq_handler(int irq, void *dev_id)
                                      HZ / 10);
        }
 
-       rk_usb_charger_status = USB_BC_TYPE_SDP;
        schedule_delayed_work(&control_usb->usb_charger_det_work, HZ / 10);
 
        return IRQ_HANDLED;
 }
 
+/********** Handler for linestate irq **********/
+static irqreturn_t otg0_linestate_irq_handler(int irq, void *dev_id)
+{
+       /*
+        * Here is a chip hwrdware bug, when disable/enable
+        * linestate irq bit the state machine will not reset
+        * So here have to add a delay to wait the linestate filter
+        * timer run out (linestate filter time had been set to 100us)
+        */
+       udelay(200);
+
+       /* clear and disable irq */
+       writel(UOC_HIWORD_UPDATE(0x2, 0x3, 12),
+              RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
+
+
+       if (control_usb->usb_irq_wakeup) {
+               wake_lock_timeout(&control_usb->usb_wakelock,
+                                 WAKE_LOCK_TIMEOUT);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t otg1_linestate_irq_handler(int irq, void *dev_id)
+{
+       /*
+        * Here is a chip hwrdware bug, when disable/enable
+        * linestate irq bit the state machine will not reset
+        * So here have to add a delay to wait the linestate filter
+        * timer run out (linestate filter time had been set to 100us)
+        */
+       udelay(200);
+
+       /* clear and disable irq */
+       writel(UOC_HIWORD_UPDATE(0x2, 0x3, 14),
+              RK_GRF_VIRT + RK3036_GRF_UOC1_CON5);
+
+
+       if (control_usb->usb_irq_wakeup) {
+               wake_lock_timeout(&control_usb->usb_wakelock,
+                                 WAKE_LOCK_TIMEOUT);
+       }
+
+       return IRQ_HANDLED;
+}
 /************* register usb detection irqs **************/
 static int otg_irq_detect_init(struct platform_device *pdev)
 {
@@ -412,6 +510,40 @@ static int otg_irq_detect_init(struct platform_device *pdev)
                               RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
                }
        }
+
+       if (!control_usb->linestate_wakeup)
+               return 0;
+
+       /* Set otg0&1_linestate_filter time to 100us */
+       writel(UOC_HIWORD_UPDATE(0x0, 0xf, 6), RK_GRF_VIRT + 0x1a0);
+
+       /* Register otg0_linestate irq */
+       irq = platform_get_irq_byname(pdev, "otg0_linestate");
+       if (irq > 0) {
+               ret = request_irq(irq, otg0_linestate_irq_handler,
+                                 0, "otg0_linestate", NULL);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "request_irq %d failed!\n", irq);
+               } else {
+                       /* Clear otg0_linestate irq  */
+                       writel(UOC_HIWORD_UPDATE(0x2, 0x3, 12),
+                              RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
+               }
+       }
+
+       /* Register otg1_linestate irq */
+       irq = platform_get_irq_byname(pdev, "otg1_linestate");
+       if (irq > 0) {
+               ret = request_irq(irq, otg1_linestate_irq_handler,
+                                 0, "otg1_linestate", NULL);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "request_irq %d failed!\n", irq);
+               } else {
+                       /* Clear otg1_linestate irq  */
+                       writel(UOC_HIWORD_UPDATE(0x2, 0x3, 14),
+                              RK_GRF_VIRT + RK3036_GRF_UOC1_CON5);
+               }
+       }
        return ret;
 }
 
@@ -435,6 +567,8 @@ static int rk_usb_control_probe(struct platform_device *pdev)
                                                           "rockchip,remote_wakeup");
        control_usb->usb_irq_wakeup = of_property_read_bool(np,
                                                            "rockchip,usb_irq_wakeup");
+       control_usb->linestate_wakeup = of_property_read_bool(np,
+                                                             "rockchip,linestate_wakeup");
 
        INIT_DELAYED_WORK(&control_usb->usb_charger_det_work,
                          usb_battery_charger_detect_work);
@@ -539,11 +673,9 @@ static int dwc_otg_control_usb_probe(struct platform_device *pdev)
        clk_prepare_enable(hclk_usb_peri);
 
 #ifdef CONFIG_USB20_OTG
-       if (usb20otg_get_status(USB_STATUS_BVABLID)) {
-               rk_usb_charger_status = USB_BC_TYPE_SDP;
+       if (usb20otg_get_status(USB_STATUS_BVABLID))
                schedule_delayed_work(&control_usb->usb_charger_det_work,
                                      HZ / 10);
-       }
 #endif
 
        ret = otg_irq_detect_init(pdev);
@@ -596,3 +728,4 @@ MODULE_ALIAS("platform: dwc_control_usb");
 MODULE_AUTHOR("RockChip Inc.");
 MODULE_DESCRIPTION("RockChip Control Module USB Driver");
 MODULE_LICENSE("GPL v2");
+#endif