From 65c9c4c6b01fe6febf516586489679770a0d8443 Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Fri, 16 Feb 2018 14:11:35 +0400 Subject: usb: dwc2: Add dwc2_handle_gpwrdn_intr() handler 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 Signed-off-by: John Youn Signed-off-by: Artur Petrosyan Signed-off-by: Minas Harutyunyan Signed-off-by: Grigor Tovmasyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core_intr.c | 117 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) (limited to 'drivers/usb/dwc2/core_intr.c') diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index d01581594ce5..2982a155734d 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -642,6 +642,116 @@ 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); + /* 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 * @@ -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) -- cgit v1.2.3