OSDN Git Service

usb: dwc2: Add dwc2_handle_gpwrdn_intr() handler
authorVardan Mikayelyan <mvardan@synopsys.com>
Fri, 16 Feb 2018 10:11:35 +0000 (14:11 +0400)
committerFelipe Balbi <felipe.balbi@linux.intel.com>
Tue, 13 Mar 2018 08:47:57 +0000 (10:47 +0200)
The GPWRDN interrupts are those that occur in both Host and
Device mode while core is in hibernated state.

Export dwc2_core_init to be able to use it in GPWRDN_IDSTS
interrupt handler.

Here we have duplicated init functions in host and gadget sides
so I have left things as it was(used corresponing functions for
host and gadget), maybe in the future we'll resolve this problem
and will use dwc2_core_init for both sides.

Signed-off-by: Vardan Mikayelyan <mvardan@synopsys.com>
Signed-off-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Artur Petrosyan <arturp@synopsys.com>
Signed-off-by: Minas Harutyunyan <hminas@synopsys.com>
Signed-off-by: Grigor Tovmasyan <tovmasya@synopsys.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
drivers/usb/dwc2/core.h
drivers/usb/dwc2/core_intr.c
drivers/usb/dwc2/hcd.c

index cc78564..ca3c703 100644 (file)
@@ -1278,6 +1278,7 @@ int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us);
 void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
 void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
 void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
+int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup);
 int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg);
 int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg);
 int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg);
@@ -1293,6 +1294,8 @@ static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
 static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
 static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
 static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
+static inline int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
+{ return 0; }
 static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
 { return 0; }
 static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
index d015815..2982a15 100644 (file)
@@ -643,6 +643,116 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg)
 }
 
 /*
+ * GPWRDN interrupt handler.
+ *
+ * The GPWRDN interrupts are those that occur in both Host and
+ * Device mode while core is in hibernated state.
+ */
+static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg)
+{
+       u32 gpwrdn;
+       int linestate;
+
+       gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+       /* clear all interrupt */
+       dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+       linestate = (gpwrdn & GPWRDN_LINESTATE_MASK) >> GPWRDN_LINESTATE_SHIFT;
+       dev_dbg(hsotg->dev,
+               "%s: dwc2_handle_gpwrdwn_intr called gpwrdn= %08x\n", __func__,
+               gpwrdn);
+
+       if ((gpwrdn & GPWRDN_DISCONN_DET) &&
+           (gpwrdn & GPWRDN_DISCONN_DET_MSK) && !linestate) {
+               u32 gpwrdn_tmp;
+
+               dev_dbg(hsotg->dev, "%s: GPWRDN_DISCONN_DET\n", __func__);
+
+               /* Switch-on voltage to the core */
+               gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+               gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH;
+               dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+               udelay(10);
+
+               /* Reset core */
+               gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+               gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN;
+               dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+               udelay(10);
+
+               /* Disable Power Down Clamp */
+               gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+               gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP;
+               dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+               udelay(10);
+
+               /* Deassert reset core */
+               gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+               gpwrdn_tmp |= GPWRDN_PWRDNRSTN;
+               dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+               udelay(10);
+
+               /* Disable PMU interrupt */
+               gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+               gpwrdn_tmp &= ~GPWRDN_PMUINTSEL;
+               dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+
+               /* De-assert Wakeup Logic */
+               gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+               gpwrdn_tmp &= ~GPWRDN_PMUACTV;
+               dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+
+               hsotg->hibernated = 0;
+
+               if (gpwrdn & GPWRDN_IDSTS) {
+                       hsotg->op_state = OTG_STATE_B_PERIPHERAL;
+                       dwc2_core_init(hsotg, false);
+                       dwc2_enable_global_interrupts(hsotg);
+                       dwc2_hsotg_core_init_disconnected(hsotg, false);
+                       dwc2_hsotg_core_connect(hsotg);
+               } else {
+                       hsotg->op_state = OTG_STATE_A_HOST;
+
+                       /* Initialize the Core for Host mode */
+                       dwc2_core_init(hsotg, false);
+                       dwc2_enable_global_interrupts(hsotg);
+                       dwc2_hcd_start(hsotg);
+               }
+       }
+
+       if ((gpwrdn & GPWRDN_LNSTSCHG) &&
+           (gpwrdn & GPWRDN_LNSTSCHG_MSK) && linestate) {
+               dev_dbg(hsotg->dev, "%s: GPWRDN_LNSTSCHG\n", __func__);
+               if (hsotg->hw_params.hibernation &&
+                   hsotg->hibernated) {
+                       if (gpwrdn & GPWRDN_IDSTS) {
+                               dwc2_exit_hibernation(hsotg, 0, 0, 0);
+                               call_gadget(hsotg, resume);
+                       } else {
+                               dwc2_exit_hibernation(hsotg, 1, 0, 1);
+                       }
+               }
+       }
+       if ((gpwrdn & GPWRDN_RST_DET) && (gpwrdn & GPWRDN_RST_DET_MSK)) {
+               dev_dbg(hsotg->dev, "%s: GPWRDN_RST_DET\n", __func__);
+               if (!linestate && (gpwrdn & GPWRDN_BSESSVLD))
+                       dwc2_exit_hibernation(hsotg, 0, 1, 0);
+       }
+       if ((gpwrdn & GPWRDN_STS_CHGINT) &&
+           (gpwrdn & GPWRDN_STS_CHGINT_MSK) && linestate) {
+               dev_dbg(hsotg->dev, "%s: GPWRDN_STS_CHGINT\n", __func__);
+               if (hsotg->hw_params.hibernation &&
+                   hsotg->hibernated) {
+                       if (gpwrdn & GPWRDN_IDSTS) {
+                               dwc2_exit_hibernation(hsotg, 0, 0, 0);
+                               call_gadget(hsotg, resume);
+                       } else {
+                               dwc2_exit_hibernation(hsotg, 1, 0, 1);
+                       }
+               }
+       }
+}
+
+/*
  * Common interrupt handler
  *
  * The common interrupts are those that occur in both Host and Device mode.
@@ -672,6 +782,13 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev)
        if (gintsts & ~GINTSTS_PRTINT)
                retval = IRQ_HANDLED;
 
+       /* In case of hibernated state gintsts must not work */
+       if (hsotg->hibernated) {
+               dwc2_handle_gpwrdn_intr(hsotg);
+               retval = IRQ_HANDLED;
+               goto out;
+       }
+
        if (gintsts & GINTSTS_MODEMIS)
                dwc2_handle_mode_mismatch_intr(hsotg);
        if (gintsts & GINTSTS_OTGINT)
index f045ee3..4fbd0d3 100644 (file)
@@ -2241,7 +2241,7 @@ static int dwc2_hcd_endpoint_reset(struct dwc2_hsotg *hsotg,
  * @hsotg:         Programming view of the DWC_otg controller
  * @initial_setup: If true then this is the first init for this instance.
  */
-static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
+int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
 {
        u32 usbcfg, otgctl;
        int retval;