usb: dwc_otg_310: fix usb vbus power controlled by pmic
[firefly-linux-kernel-4.4.55.git] / drivers / usb / dwc_otg_310 / usbdev_rk3126.c
index 4f7326ff6eb952c311c14d30466eaea3c886952e..bd3991183b728ec290a8a39e0883be7ff49aeca0 100755 (executable)
@@ -1,3 +1,4 @@
+#ifdef CONFIG_ARM
 #include "usbdev_rk.h"
 #include "usbdev_grf_regs.h"
 #include "dwc_otg_regs.h"
@@ -8,7 +9,10 @@ static void usb20otg_hw_init(void)
 {
        /* Turn off differential receiver in suspend mode */
        writel(UOC_HIWORD_UPDATE(0, 1, 2),
-                  RK_GRF_VIRT + RK312X_GRF_USBPHY1_CON6);
+              RK_GRF_VIRT + RK312X_GRF_USBPHY0_CON6);
+       /* Set disconnect detection trigger point to 600mv */
+       writel(UOC_HIWORD_UPDATE(0, 0xf, 11),
+              RK_GRF_VIRT + RK312X_GRF_USBPHY0_CON7);
        /* other haredware init,include:
         * DRV_VBUS GPIO init */
        if (gpio_is_valid(control_usb->otg_gpios->gpio)) {
@@ -20,9 +24,10 @@ static void usb20otg_hw_init(void)
 static void usb20otg_phy_suspend(void *pdata, int suspend)
 {
        struct dwc_otg_platform_data *usbpdata = pdata;
+
        if (suspend) {
                /* enable soft control */
-               writel(UOC_HIWORD_UPDATE(0x55, 0x7f, 0),
+               writel(UOC_HIWORD_UPDATE(0x1d1, 0x1ff, 0),
                       RK_GRF_VIRT + RK312X_GRF_UOC0_CON0);
                usbpdata->phy_status = 1;
        } else {
@@ -127,6 +132,9 @@ static int usb20otg_get_status(int id)
                /* id in grf */
                ret = soc_status0 & (0x1 << 8);
                break;
+       case USB_STATUS_UARTMODE:
+               ret = readl(RK_GRF_VIRT + RK312X_GRF_UOC1_CON4) & (1 << 12);
+               break;
        case USB_CHIP_ID:
                ret = control_usb->chip_id;
                break;
@@ -186,13 +194,31 @@ 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);
+               else if (usb20otg_get_status(USB_STATUS_BVABLID))
+                       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);
+               else 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, 14),
+                              RK_GRF_VIRT + RK312X_GRF_UOC1_CON5);
+                       /* enable otg1_linestate irq */
+                       writel(UOC_HIWORD_UPDATE(0x3, 0x3, 12),
+                              RK_GRF_VIRT + RK312X_GRF_UOC0_CON0);
+               }
+       } else if (power_down == PHY_POWER_UP) {
+               ;
        }
 }
-
 struct dwc_otg_platform_data usb20otg_pdata_rk3126 = {
        .phyclk = NULL,
        .ahbclk = NULL,
@@ -206,19 +232,20 @@ struct dwc_otg_platform_data usb20otg_pdata_rk3126 = {
        .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
 
-#ifdef CONFIG_USB20_HOST
+#if defined(CONFIG_USB20_HOST) || defined(CONFIG_USB_EHCI_RK)
 static void usb20host_hw_init(void)
 {
-       /* Switch to DWC HOST */
-       writel(UOC_HIWORD_UPDATE(1, 1, 3),
-                  RK_GRF_VIRT + RK312X_GRF_SOC_CON2);
        /* Turn off differential receiver in suspend mode */
        writel(UOC_HIWORD_UPDATE(0, 1, 2),
-                  RK_GRF_VIRT + RK312X_GRF_USBPHY0_CON6);
+                  RK_GRF_VIRT + RK312X_GRF_USBPHY1_CON6);
+       /* Set disconnect detection trigger point to 600mv */
+       writel(UOC_HIWORD_UPDATE(1, 0xf, 11),
+                  RK_GRF_VIRT + RK312X_GRF_USBPHY1_CON7);
        /* other haredware init,include:
         * DRV_VBUS GPIO init */
        if (gpio_is_valid(control_usb->host_gpios->gpio)) {
@@ -379,6 +406,114 @@ struct dwc_otg_platform_data usb20host_pdata_rk3126 = {
 };
 #endif
 
+#ifdef CONFIG_USB_EHCI_RK
+static void usb20ehci_phy_suspend(void *pdata, int suspend)
+{
+       struct rkehci_platform_data *usbpdata = pdata;
+
+       if (suspend) {
+               /* enable soft control */
+               writel(UOC_HIWORD_UPDATE(0x1d1, 0x1ff, 0),
+                      RK_GRF_VIRT + RK312X_GRF_UOC1_CON5);
+               usbpdata->phy_status = 1;
+       } else {
+               /* exit suspend */
+               writel(UOC_HIWORD_UPDATE(0x0, 0x1, 0),
+                      RK_GRF_VIRT + RK312X_GRF_UOC1_CON5);
+               usbpdata->phy_status = 0;
+       }
+}
+
+static void usb20ehci_soft_reset(void *pdata, enum rkusb_rst_flag rst_type)
+{
+       struct rkehci_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 + RK312X_GRF_UOC1_CON5);
+               reset_control_assert(rst_host_p);
+               udelay(15);
+               writel(UOC_HIWORD_UPDATE(0x2, 0x3, 0),
+                          RK_GRF_VIRT + RK312X_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 usb20ehci_clock_init(void *pdata)
+{
+       struct rkehci_platform_data *usbpdata = pdata;
+       struct clk *ahbclk, *phyclk;
+
+       ahbclk = devm_clk_get(usbpdata->dev, "hclk_host0");
+       if (IS_ERR(ahbclk)) {
+               dev_err(usbpdata->dev, "Failed to get hclk_usb1\n");
+               return;
+       }
+
+       phyclk = devm_clk_get(usbpdata->dev, "clk_usbphy1");
+       if (IS_ERR(phyclk)) {
+               dev_err(usbpdata->dev, "Failed to get clk_usbphy1\n");
+               return;
+       }
+
+       usbpdata->phyclk = phyclk;
+       usbpdata->ahbclk = ahbclk;
+}
+
+static void usb20ehci_clock_enable(void *pdata, int enable)
+{
+       struct rkehci_platform_data *usbpdata = pdata;
+
+       if (enable) {
+               clk_prepare_enable(usbpdata->ahbclk);
+               clk_prepare_enable(usbpdata->phyclk);
+       } else {
+               clk_disable_unprepare(usbpdata->ahbclk);
+               clk_disable_unprepare(usbpdata->phyclk);
+       }
+}
+
+struct rkehci_platform_data usb20ehci_pdata_rk3126 = {
+       .phyclk = NULL,
+       .ahbclk = NULL,
+       .phy_status = 0,
+       .hw_init = usb20host_hw_init,
+       .phy_suspend = usb20ehci_phy_suspend,
+       .soft_reset = usb20ehci_soft_reset,
+       .clock_init = usb20ehci_clock_init,
+       .clock_enable = usb20ehci_clock_enable,
+       .get_status = usb20host_get_status,
+};
+#endif
+
+struct dwc_otg_platform_data usb20ohci_pdata_rk3126;
+
 #ifdef CONFIG_OF
 static const struct of_device_id rk_usb_control_id_table[] = {
        {
@@ -400,7 +535,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 **********/
@@ -421,12 +556,58 @@ 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 + RK312X_GRF_UOC0_CON0);
+
+
+       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 + RK312X_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)
 {
@@ -439,7 +620,7 @@ static int otg_irq_detect_init(struct platform_device *pdev)
                INIT_DELAYED_WORK(&control_usb->usb_det_wakeup_work, do_wakeup);
        }
 
-       /*register otg_bvalid irq */
+       /* register otg_bvalid irq */
        irq = platform_get_irq_byname(pdev, "otg_bvalid");
        if ((irq > 0) && control_usb->usb_irq_wakeup) {
                ret = request_irq(irq, bvalid_irq_handler,
@@ -452,7 +633,41 @@ static int otg_irq_detect_init(struct platform_device *pdev)
                               RK_GRF_VIRT + RK312X_GRF_UOC0_CON0);
                }
        }
-       return ret;
+
+       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 + RK312X_GRF_UOC0_CON0);
+               }
+       }
+
+       /* 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 + RK312X_GRF_UOC1_CON5);
+               }
+       }
+       return 0;
 }
 
 /********** end of rk3126 usb detections **********/
@@ -475,6 +690,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);
@@ -579,11 +796,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);
@@ -636,4 +851,4 @@ MODULE_ALIAS("platform: dwc_control_usb");
 MODULE_AUTHOR("RockChip Inc.");
 MODULE_DESCRIPTION("RockChip Control Module USB Driver");
 MODULE_LICENSE("GPL v2");
-
+#endif