[PATCH v2 12/14] usb: dwc2: Add dwc2_handle_gpwrdn_intr() handler

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxx>
Signed-off-by: John Youn <johnyoun@xxxxxxxxxxxx>
---
 drivers/usb/dwc2/core.h      |   3 ++
 drivers/usb/dwc2/core_intr.c | 110 +++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc2/hcd.c       |   2 +-
 3 files changed, 114 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index a3561e1..263ad53 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -1245,6 +1245,7 @@ static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg)
 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);
@@ -1260,6 +1261,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)
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index 45c9b57..86cbbb9 100644
--- a/drivers/usb/dwc2/core_intr.c
+++ b/drivers/usb/dwc2/core_intr.c
@@ -520,6 +520,109 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg)
 		return 0;
 }
 
+/**
+ * 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);
+	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__);
+		/* clear interrupt */
+		dwc2_writel(GPWRDN_DISCONN_DET, hsotg->regs + GPWRDN);
+
+		/* 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) {
+		/* clear interrupt */
+		dwc2_writel(GPWRDN_LNSTSCHG, hsotg->regs + GPWRDN);
+		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)) {
+		/* clear interrupt */
+		dwc2_writel(GPWRDN_RST_DET, hsotg->regs + GPWRDN);
+		dev_dbg(hsotg->dev, "%s: GPWRDN_RST_DET\n", __func__);
+
+		if (!linestate)
+			dwc2_exit_hibernation(hsotg, 0, 1, 0);
+	}
+}
+
 /*
  * Common interrupt handler
  *
@@ -550,6 +653,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)
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 53d0cca..0b889a0 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -2221,7 +2221,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;
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux