rk3036: usb: add support of wakeup by GRF linestate change
authorlyz <lyz@rock-chips.com>
Fri, 10 Apr 2015 03:44:49 +0000 (11:44 +0800)
committerlyz <lyz@rock-chips.com>
Fri, 10 Apr 2015 03:44:49 +0000 (11:44 +0800)
arch/arm/boot/dts/rk3036.dtsi
drivers/usb/dwc_otg_310/usbdev_rk3036.c

index 36a626e9e37e7c48adc7f3f525315c6c942c344f..69021c550c211ae31225efe3fb3e32ec04ff1967 100755 (executable)
        dwc_control_usb: dwc-control-usb@20008000 {
                compatible = "rockchip,rk3036-dwc-control-usb";
                reg = <0x20008000 0x4>;
-               interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
-               interrupt-names = "otg_bvalid";
+               interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
+               interrupt-names = "otg_bvalid",
+                                 "otg0_linestate",
+                                 "otg1_linestate";
                clocks = <&clk_gates9 13>;
                clock-names = "hclk_usb_peri";
                rockchip,remote_wakeup;
index 2d507af84da100f6dbadc5f15533b0d4f5fe82e8..006c180848106af3fc5cff2b4359999d0e5873e7 100755 (executable)
@@ -20,7 +20,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 {
@@ -190,7 +190,21 @@ static void usb20otg_power_enable(int enable)
                        gpio_set_value(control_usb->otg_gpios->gpio, 1);
        }
 }
-
+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,
@@ -205,6 +219,7 @@ struct dwc_otg_platform_data usb20otg_pdata_rk3036 = {
        .power_enable = usb20otg_power_enable,
        .dwc_otg_uart_mode = dwc_otg_uart_mode,
        .bc_detect_cb = rk_battery_charger_detect_cb,
+       .phy_power_down = usb20otg_phy_power_down,
 };
 #endif
 
@@ -418,6 +433,52 @@ static irqreturn_t bvalid_irq_handler(int irq, void *dev_id)
        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)
 {
@@ -443,6 +504,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;
 }
 
@@ -466,6 +561,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);