usb: can be wakeup by linestate change interrupt from GRF
authorlyz <lyz@rock-chips.com>
Mon, 2 Mar 2015 03:23:43 +0000 (11:23 +0800)
committerlyz <lyz@rock-chips.com>
Fri, 6 Mar 2015 09:07:04 +0000 (17:07 +0800)
arch/arm/boot/dts/rk312x.dtsi
drivers/hid/usbhid/hid-core.c
drivers/usb/dwc_otg_310/dwc_otg_driver.c
drivers/usb/dwc_otg_310/usbdev_rk.h
drivers/usb/dwc_otg_310/usbdev_rk3126.c

index ea52b54ff41f95b7dcc131db3201e6499d9586c4..4e0bc5ae0d2f781a81a161fb37b3a1eaaab62d88 100755 (executable)
        dwc_control_usb: dwc-control-usb@20008000 {
                compatible = "rockchip,rk3126-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 ada164e1b3a1fd909c8c6a437dc2cc5cb424a845..0463968b8a31b49d31b215e2240d82d01a28b2a0 100644 (file)
@@ -1183,6 +1183,7 @@ static int usbhid_start(struct hid_device *hid)
                usbhid_set_leds(hid);
                device_set_wakeup_enable(&dev->dev, 1);
        }
+       device_set_wakeup_enable(&dev->dev, 1);
        return 0;
 
 fail:
index 21b05a8eeb42d7164e060bae9d1868be9f378217..69f212720015f23a2a328909fb0e0346b4490dc1 100755 (executable)
@@ -1597,11 +1597,7 @@ void rk_usb_power_up(void)
        struct dwc_otg_platform_data *pldata_otg;
        struct dwc_otg_platform_data *pldata_host;
        struct rkehci_platform_data *pldata_ehci;
-       if (cpu_is_rk312x()) {
-               pldata_otg = &usb20otg_pdata_rk3126;
-               if (usb_to_uart_status)
-                       pldata_otg->dwc_otg_uart_mode(pldata_otg, PHY_UART_MODE);
-       }
+
        if (cpu_is_rk3288()) {
 #ifdef CONFIG_RK_USB_UART
                /* enable USB bypass UART function  */
@@ -1638,6 +1634,15 @@ void rk_usb_power_up(void)
                }
 #endif
 
+       } else {
+               dwc_otg_device_t *otg_dev = g_otgdev;
+
+               if (!otg_dev)
+                       return;
+
+               pldata_otg = otg_dev->pldata;
+               if (pldata_otg && pldata_otg->phy_power_down)
+                       pldata_otg->phy_power_down(PHY_POWER_UP);
        }
 }
 
@@ -1647,11 +1652,6 @@ void rk_usb_power_down(void)
        struct dwc_otg_platform_data *pldata_host;
        struct rkehci_platform_data *pldata_ehci;
 
-       if (cpu_is_rk312x()) {
-               pldata_otg = &usb20otg_pdata_rk3126;
-               usb_to_uart_status = pldata_otg->get_status(USB_STATUS_UARTMODE);
-               pldata_otg->dwc_otg_uart_mode(pldata_otg, PHY_USB_MODE);
-       }
        if (cpu_is_rk3288()) {
 #ifdef CONFIG_RK_USB_UART
                /* disable USB bypass UART function */
@@ -1695,6 +1695,15 @@ void rk_usb_power_down(void)
                                               RK3288_GRF_UOC1_CON0);
                }
 #endif
+       } else {
+               dwc_otg_device_t *otg_dev = g_otgdev;
+
+               if (!otg_dev)
+                       return;
+
+               pldata_otg = otg_dev->pldata;
+               if (pldata_otg && pldata_otg->phy_power_down)
+                       pldata_otg->phy_power_down(PHY_POWER_DOWN);
        }
 }
 
index 16ef6d78c54b246fd0a5ff0128bb711c0bedb72d..32c88addbf3b7711ef6eff711dbb43dadf4b01a0 100755 (executable)
@@ -34,6 +34,9 @@
 #define PHY_USB_MODE    (0)
 #define PHY_UART_MODE   (1)
 
+#define PHY_POWER_DOWN (0)
+#define PHY_POWER_UP   (1)
+
 #define USB_STATUS_BVABLID    (1)
 #define USB_STATUS_DPDM       (2)
 #define USB_STATUS_ID         (3)
@@ -89,6 +92,7 @@ struct dwc_otg_platform_data {
        void (*dwc_otg_uart_mode) (void *pdata, int enter_usb_uart_mode);
        void (*bc_detect_cb) (int bc_type);
        int (*get_status) (int id);
+       void (*phy_power_down)(int power_down);
 };
 
 struct rkehci_platform_data {
@@ -128,6 +132,7 @@ struct dwc_otg_control_usb {
        struct wake_lock usb_wakelock;
        int remote_wakeup;
        int usb_irq_wakeup;
+       int linestate_wakeup;
        int chip_id;
 };
 
index d71e8c3f7134605ab60aaeb461e54d537604421a..eaeca4324c5282ee23f68731d97b8ed18303331f 100755 (executable)
@@ -23,9 +23,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 {
@@ -198,7 +199,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, 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,
@@ -213,6 +228,7 @@ struct dwc_otg_platform_data usb20otg_pdata_rk3126 = {
        .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
 
@@ -540,6 +556,53 @@ 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 + 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)
 {
@@ -552,7 +615,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,
@@ -565,7 +628,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 **********/
@@ -588,6 +685,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);