These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / usb / dwc2 / core_intr.c
index 6cf0478..27daa42 100644 (file)
@@ -80,15 +80,15 @@ static const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg)
  */
 static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
 {
-       u32 hprt0 = readl(hsotg->regs + HPRT0);
+       u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0);
 
        if (hprt0 & HPRT0_ENACHG) {
                hprt0 &= ~HPRT0_ENA;
-               writel(hprt0, hsotg->regs + HPRT0);
+               dwc2_writel(hprt0, hsotg->regs + HPRT0);
        }
 
        /* Clear interrupt */
-       writel(GINTSTS_PRTINT, hsotg->regs + GINTSTS);
+       dwc2_writel(GINTSTS_PRTINT, hsotg->regs + GINTSTS);
 }
 
 /**
@@ -102,7 +102,7 @@ static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg)
                 dwc2_is_host_mode(hsotg) ? "Host" : "Device");
 
        /* Clear interrupt */
-       writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS);
+       dwc2_writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS);
 }
 
 /**
@@ -117,8 +117,8 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
        u32 gotgctl;
        u32 gintmsk;
 
-       gotgint = readl(hsotg->regs + GOTGINT);
-       gotgctl = readl(hsotg->regs + GOTGCTL);
+       gotgint = dwc2_readl(hsotg->regs + GOTGINT);
+       gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
        dev_dbg(hsotg->dev, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint,
                dwc2_op_state_str(hsotg));
 
@@ -126,10 +126,10 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                dev_dbg(hsotg->dev,
                        " ++OTG Interrupt: Session End Detected++ (%s)\n",
                        dwc2_op_state_str(hsotg));
-               gotgctl = readl(hsotg->regs + GOTGCTL);
+               gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
 
                if (dwc2_is_device_mode(hsotg))
-                       s3c_hsotg_disconnect(hsotg);
+                       dwc2_hsotg_disconnect(hsotg);
 
                if (hsotg->op_state == OTG_STATE_B_HOST) {
                        hsotg->op_state = OTG_STATE_B_PERIPHERAL;
@@ -152,15 +152,15 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                        hsotg->lx_state = DWC2_L0;
                }
 
-               gotgctl = readl(hsotg->regs + GOTGCTL);
+               gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
                gotgctl &= ~GOTGCTL_DEVHNPEN;
-               writel(gotgctl, hsotg->regs + GOTGCTL);
+               dwc2_writel(gotgctl, hsotg->regs + GOTGCTL);
        }
 
        if (gotgint & GOTGINT_SES_REQ_SUC_STS_CHNG) {
                dev_dbg(hsotg->dev,
                        " ++OTG Interrupt: Session Request Success Status Change++\n");
-               gotgctl = readl(hsotg->regs + GOTGCTL);
+               gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
                if (gotgctl & GOTGCTL_SESREQSCS) {
                        if (hsotg->core_params->phy_type ==
                                        DWC2_PHY_TYPE_PARAM_FS
@@ -168,9 +168,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                                hsotg->srp_success = 1;
                        } else {
                                /* Clear Session Request */
-                               gotgctl = readl(hsotg->regs + GOTGCTL);
+                               gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
                                gotgctl &= ~GOTGCTL_SESREQ;
-                               writel(gotgctl, hsotg->regs + GOTGCTL);
+                               dwc2_writel(gotgctl, hsotg->regs + GOTGCTL);
                        }
                }
        }
@@ -180,7 +180,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                 * Print statements during the HNP interrupt handling
                 * can cause it to fail
                 */
-               gotgctl = readl(hsotg->regs + GOTGCTL);
+               gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
                /*
                 * WA for 3.00a- HW is not setting cur_mode, even sometimes
                 * this does not help
@@ -200,9 +200,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                                 * interrupt does not get handled and Linux
                                 * complains loudly.
                                 */
-                               gintmsk = readl(hsotg->regs + GINTMSK);
+                               gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
                                gintmsk &= ~GINTSTS_SOF;
-                               writel(gintmsk, hsotg->regs + GINTMSK);
+                               dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
 
                                /*
                                 * Call callback function with spin lock
@@ -216,9 +216,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                                hsotg->op_state = OTG_STATE_B_HOST;
                        }
                } else {
-                       gotgctl = readl(hsotg->regs + GOTGCTL);
+                       gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
                        gotgctl &= ~(GOTGCTL_HNPREQ | GOTGCTL_DEVHNPEN);
-                       writel(gotgctl, hsotg->regs + GOTGCTL);
+                       dwc2_writel(gotgctl, hsotg->regs + GOTGCTL);
                        dev_dbg(hsotg->dev, "HNP Failed\n");
                        dev_err(hsotg->dev,
                                "Device Not Connected/Responding\n");
@@ -244,9 +244,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                        hsotg->op_state = OTG_STATE_A_PERIPHERAL;
                } else {
                        /* Need to disable SOF interrupt immediately */
-                       gintmsk = readl(hsotg->regs + GINTMSK);
+                       gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
                        gintmsk &= ~GINTSTS_SOF;
-                       writel(gintmsk, hsotg->regs + GINTMSK);
+                       dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
                        spin_unlock(&hsotg->lock);
                        dwc2_hcd_start(hsotg);
                        spin_lock(&hsotg->lock);
@@ -261,7 +261,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
                dev_dbg(hsotg->dev, " ++OTG Interrupt: Debounce Done++\n");
 
        /* Clear GOTGINT */
-       writel(gotgint, hsotg->regs + GOTGINT);
+       dwc2_writel(gotgint, hsotg->regs + GOTGINT);
 }
 
 /**
@@ -276,11 +276,11 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
  */
 static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
 {
-       u32 gintmsk = readl(hsotg->regs + GINTMSK);
+       u32 gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
 
        /* Need to disable SOF interrupt immediately */
        gintmsk &= ~GINTSTS_SOF;
-       writel(gintmsk, hsotg->regs + GINTMSK);
+       dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
 
        dev_dbg(hsotg->dev, " ++Connector ID Status Change Interrupt++  (%s)\n",
                dwc2_is_host_mode(hsotg) ? "Host" : "Device");
@@ -297,7 +297,7 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
        }
 
        /* Clear interrupt */
-       writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
+       dwc2_writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
 }
 
 /**
@@ -313,16 +313,28 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
  */
 static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
 {
-       dev_dbg(hsotg->dev, "++Session Request Interrupt++\n");
+       int ret;
+
+       dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
+                                                       hsotg->lx_state);
 
        /* Clear interrupt */
-       writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
+       dwc2_writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
 
-       /*
-        * Report disconnect if there is any previous session established
-        */
-       if (dwc2_is_device_mode(hsotg))
-               s3c_hsotg_disconnect(hsotg);
+       if (dwc2_is_device_mode(hsotg)) {
+               if (hsotg->lx_state == DWC2_L2) {
+                       ret = dwc2_exit_hibernation(hsotg, true);
+                       if (ret && (ret != -ENOTSUPP))
+                               dev_err(hsotg->dev,
+                                       "exit hibernation failed\n");
+               }
+
+               /*
+                * Report disconnect if there is any previous session
+                * established
+                */
+               dwc2_hsotg_disconnect(hsotg);
+       }
 }
 
 /*
@@ -334,27 +346,38 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
  */
 static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
 {
+       int ret;
        dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
        dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
 
        if (dwc2_is_device_mode(hsotg)) {
-               dev_dbg(hsotg->dev, "DSTS=0x%0x\n", readl(hsotg->regs + DSTS));
+               dev_dbg(hsotg->dev, "DSTS=0x%0x\n",
+                       dwc2_readl(hsotg->regs + DSTS));
                if (hsotg->lx_state == DWC2_L2) {
-                       u32 dctl = readl(hsotg->regs + DCTL);
+                       u32 dctl = dwc2_readl(hsotg->regs + DCTL);
 
                        /* Clear Remote Wakeup Signaling */
                        dctl &= ~DCTL_RMTWKUPSIG;
-                       writel(dctl, hsotg->regs + DCTL);
+                       dwc2_writel(dctl, hsotg->regs + DCTL);
+                       ret = dwc2_exit_hibernation(hsotg, true);
+                       if (ret && (ret != -ENOTSUPP))
+                               dev_err(hsotg->dev, "exit hibernation failed\n");
+
+                       call_gadget(hsotg, resume);
                }
                /* Change to L0 state */
                hsotg->lx_state = DWC2_L0;
        } else {
+               if (hsotg->core_params->hibernation) {
+                       dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
+                       return;
+               }
                if (hsotg->lx_state != DWC2_L1) {
-                       u32 pcgcctl = readl(hsotg->regs + PCGCTL);
+                       u32 pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
 
                        /* Restart the Phy Clock */
                        pcgcctl &= ~PCGCTL_STOPPCLK;
-                       writel(pcgcctl, hsotg->regs + PCGCTL);
+                       dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
                        mod_timer(&hsotg->wkp_timer,
                                  jiffies + msecs_to_jiffies(71));
                } else {
@@ -364,7 +387,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
        }
 
        /* Clear interrupt */
-       writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
+       dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
 }
 
 /*
@@ -380,10 +403,7 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
        if (hsotg->op_state == OTG_STATE_A_HOST)
                dwc2_hcd_disconnect(hsotg);
 
-       /* Change to L3 (OFF) state */
-       hsotg->lx_state = DWC2_L3;
-
-       writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS);
+       dwc2_writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS);
 }
 
 /*
@@ -397,6 +417,7 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
 static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
 {
        u32 dsts;
+       int ret;
 
        dev_dbg(hsotg->dev, "USB SUSPEND\n");
 
@@ -405,16 +426,49 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
                 * Check the Device status register to determine if the Suspend
                 * state is active
                 */
-               dsts = readl(hsotg->regs + DSTS);
+               dsts = dwc2_readl(hsotg->regs + DSTS);
                dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dsts);
                dev_dbg(hsotg->dev,
                        "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n",
                        !!(dsts & DSTS_SUSPSTS),
                        hsotg->hw_params.power_optimized);
+               if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) {
+                       /* Ignore suspend request before enumeration */
+                       if (!dwc2_is_device_connected(hsotg)) {
+                               dev_dbg(hsotg->dev,
+                                               "ignore suspend request before enumeration\n");
+                               goto clear_int;
+                       }
+
+                       ret = dwc2_enter_hibernation(hsotg);
+                       if (ret) {
+                               if (ret != -ENOTSUPP)
+                                       dev_err(hsotg->dev,
+                                                       "enter hibernation failed\n");
+                               goto skip_power_saving;
+                       }
+
+                       udelay(100);
+
+                       /* Ask phy to be suspended */
+                       if (!IS_ERR_OR_NULL(hsotg->uphy))
+                               usb_phy_set_suspend(hsotg->uphy, true);
+skip_power_saving:
+                       /*
+                        * Change to L2 (suspend) state before releasing
+                        * spinlock
+                        */
+                       hsotg->lx_state = DWC2_L2;
+
+                       /* Call gadget suspend callback */
+                       call_gadget(hsotg, suspend);
+               }
        } else {
                if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) {
                        dev_dbg(hsotg->dev, "a_peripheral->a_host\n");
 
+                       /* Change to L2 (suspend) state */
+                       hsotg->lx_state = DWC2_L2;
                        /* Clear the a_peripheral flag, back to a_host */
                        spin_unlock(&hsotg->lock);
                        dwc2_hcd_start(hsotg);
@@ -423,11 +477,9 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
                }
        }
 
-       /* Change to L2 (suspend) state */
-       hsotg->lx_state = DWC2_L2;
-
+clear_int:
        /* Clear interrupt */
-       writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
+       dwc2_writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
 }
 
 #define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT |         \
@@ -445,9 +497,9 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg)
        u32 gahbcfg;
        u32 gintmsk_common = GINTMSK_COMMON;
 
-       gintsts = readl(hsotg->regs + GINTSTS);
-       gintmsk = readl(hsotg->regs + GINTMSK);
-       gahbcfg = readl(hsotg->regs + GAHBCFG);
+       gintsts = dwc2_readl(hsotg->regs + GINTSTS);
+       gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+       gahbcfg = dwc2_readl(hsotg->regs + GAHBCFG);
 
        /* If any common interrupts set */
        if (gintsts & gintmsk_common)
@@ -522,4 +574,3 @@ out:
        spin_unlock(&hsotg->lock);
        return retval;
 }
-EXPORT_SYMBOL_GPL(dwc2_handle_common_intr);