xref: /openbmc/linux/drivers/usb/dwc2/core_intr.c (revision 3e417f31)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2197ba5f4SPaul Zimmerman /*
3197ba5f4SPaul Zimmerman  * core_intr.c - DesignWare HS OTG Controller common interrupt handling
4197ba5f4SPaul Zimmerman  *
5197ba5f4SPaul Zimmerman  * Copyright (C) 2004-2013 Synopsys, Inc.
6197ba5f4SPaul Zimmerman  */
7197ba5f4SPaul Zimmerman 
8197ba5f4SPaul Zimmerman /*
9197ba5f4SPaul Zimmerman  * This file contains the common interrupt handlers
10197ba5f4SPaul Zimmerman  */
11197ba5f4SPaul Zimmerman #include <linux/kernel.h>
12197ba5f4SPaul Zimmerman #include <linux/module.h>
13197ba5f4SPaul Zimmerman #include <linux/moduleparam.h>
14197ba5f4SPaul Zimmerman #include <linux/spinlock.h>
15197ba5f4SPaul Zimmerman #include <linux/interrupt.h>
16197ba5f4SPaul Zimmerman #include <linux/dma-mapping.h>
17197ba5f4SPaul Zimmerman #include <linux/io.h>
18197ba5f4SPaul Zimmerman #include <linux/slab.h>
19197ba5f4SPaul Zimmerman #include <linux/usb.h>
20197ba5f4SPaul Zimmerman 
21197ba5f4SPaul Zimmerman #include <linux/usb/hcd.h>
22197ba5f4SPaul Zimmerman #include <linux/usb/ch11.h>
23197ba5f4SPaul Zimmerman 
24197ba5f4SPaul Zimmerman #include "core.h"
25197ba5f4SPaul Zimmerman #include "hcd.h"
26197ba5f4SPaul Zimmerman 
dwc2_op_state_str(struct dwc2_hsotg * hsotg)27197ba5f4SPaul Zimmerman static const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg)
28197ba5f4SPaul Zimmerman {
29197ba5f4SPaul Zimmerman 	switch (hsotg->op_state) {
30197ba5f4SPaul Zimmerman 	case OTG_STATE_A_HOST:
31197ba5f4SPaul Zimmerman 		return "a_host";
32197ba5f4SPaul Zimmerman 	case OTG_STATE_A_SUSPEND:
33197ba5f4SPaul Zimmerman 		return "a_suspend";
34197ba5f4SPaul Zimmerman 	case OTG_STATE_A_PERIPHERAL:
35197ba5f4SPaul Zimmerman 		return "a_peripheral";
36197ba5f4SPaul Zimmerman 	case OTG_STATE_B_PERIPHERAL:
37197ba5f4SPaul Zimmerman 		return "b_peripheral";
38197ba5f4SPaul Zimmerman 	case OTG_STATE_B_HOST:
39197ba5f4SPaul Zimmerman 		return "b_host";
40197ba5f4SPaul Zimmerman 	default:
41197ba5f4SPaul Zimmerman 		return "unknown";
42197ba5f4SPaul Zimmerman 	}
43197ba5f4SPaul Zimmerman }
44197ba5f4SPaul Zimmerman 
45197ba5f4SPaul Zimmerman /**
4693571adbSDinh Nguyen  * dwc2_handle_usb_port_intr - handles OTG PRTINT interrupts.
4793571adbSDinh Nguyen  * When the PRTINT interrupt fires, there are certain status bits in the Host
4893571adbSDinh Nguyen  * Port that needs to get cleared.
4993571adbSDinh Nguyen  *
5093571adbSDinh Nguyen  * @hsotg: Programming view of DWC_otg controller
5193571adbSDinh Nguyen  */
dwc2_handle_usb_port_intr(struct dwc2_hsotg * hsotg)5293571adbSDinh Nguyen static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
5393571adbSDinh Nguyen {
54f25c42b8SGevorg Sahakyan 	u32 hprt0 = dwc2_readl(hsotg, HPRT0);
5593571adbSDinh Nguyen 
5693571adbSDinh Nguyen 	if (hprt0 & HPRT0_ENACHG) {
5793571adbSDinh Nguyen 		hprt0 &= ~HPRT0_ENA;
58f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, hprt0, HPRT0);
5993571adbSDinh Nguyen 	}
6093571adbSDinh Nguyen }
6193571adbSDinh Nguyen 
6293571adbSDinh Nguyen /**
63197ba5f4SPaul Zimmerman  * dwc2_handle_mode_mismatch_intr() - Logs a mode mismatch warning message
64197ba5f4SPaul Zimmerman  *
65197ba5f4SPaul Zimmerman  * @hsotg: Programming view of DWC_otg controller
66197ba5f4SPaul Zimmerman  */
dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg * hsotg)67197ba5f4SPaul Zimmerman static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg)
68197ba5f4SPaul Zimmerman {
69197ba5f4SPaul Zimmerman 	/* Clear interrupt */
70f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, GINTSTS_MODEMIS, GINTSTS);
7129539019SDouglas Anderson 
7229539019SDouglas Anderson 	dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
7329539019SDouglas Anderson 		 dwc2_is_host_mode(hsotg) ? "Host" : "Device");
74197ba5f4SPaul Zimmerman }
75197ba5f4SPaul Zimmerman 
76197ba5f4SPaul Zimmerman /**
77197ba5f4SPaul Zimmerman  * dwc2_handle_otg_intr() - Handles the OTG Interrupts. It reads the OTG
78197ba5f4SPaul Zimmerman  * Interrupt Register (GOTGINT) to determine what interrupt has occurred.
79197ba5f4SPaul Zimmerman  *
80197ba5f4SPaul Zimmerman  * @hsotg: Programming view of DWC_otg controller
81197ba5f4SPaul Zimmerman  */
dwc2_handle_otg_intr(struct dwc2_hsotg * hsotg)82197ba5f4SPaul Zimmerman static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
83197ba5f4SPaul Zimmerman {
84197ba5f4SPaul Zimmerman 	u32 gotgint;
85197ba5f4SPaul Zimmerman 	u32 gotgctl;
86197ba5f4SPaul Zimmerman 	u32 gintmsk;
87197ba5f4SPaul Zimmerman 
88f25c42b8SGevorg Sahakyan 	gotgint = dwc2_readl(hsotg, GOTGINT);
89f25c42b8SGevorg Sahakyan 	gotgctl = dwc2_readl(hsotg, GOTGCTL);
90197ba5f4SPaul Zimmerman 	dev_dbg(hsotg->dev, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint,
91197ba5f4SPaul Zimmerman 		dwc2_op_state_str(hsotg));
92197ba5f4SPaul Zimmerman 
93197ba5f4SPaul Zimmerman 	if (gotgint & GOTGINT_SES_END_DET) {
94197ba5f4SPaul Zimmerman 		dev_dbg(hsotg->dev,
95197ba5f4SPaul Zimmerman 			" ++OTG Interrupt: Session End Detected++ (%s)\n",
96197ba5f4SPaul Zimmerman 			dwc2_op_state_str(hsotg));
97f25c42b8SGevorg Sahakyan 		gotgctl = dwc2_readl(hsotg, GOTGCTL);
98197ba5f4SPaul Zimmerman 
994ace06e8SMarek Szyprowski 		if (dwc2_is_device_mode(hsotg))
1001f91b4ccSFelipe Balbi 			dwc2_hsotg_disconnect(hsotg);
1014ace06e8SMarek Szyprowski 
102197ba5f4SPaul Zimmerman 		if (hsotg->op_state == OTG_STATE_B_HOST) {
103197ba5f4SPaul Zimmerman 			hsotg->op_state = OTG_STATE_B_PERIPHERAL;
104197ba5f4SPaul Zimmerman 		} else {
105197ba5f4SPaul Zimmerman 			/*
106197ba5f4SPaul Zimmerman 			 * If not B_HOST and Device HNP still set, HNP did
107197ba5f4SPaul Zimmerman 			 * not succeed!
108197ba5f4SPaul Zimmerman 			 */
109197ba5f4SPaul Zimmerman 			if (gotgctl & GOTGCTL_DEVHNPEN) {
110197ba5f4SPaul Zimmerman 				dev_dbg(hsotg->dev, "Session End Detected\n");
111197ba5f4SPaul Zimmerman 				dev_err(hsotg->dev,
112197ba5f4SPaul Zimmerman 					"Device Not Connected/Responding!\n");
113197ba5f4SPaul Zimmerman 			}
114197ba5f4SPaul Zimmerman 
115197ba5f4SPaul Zimmerman 			/*
116197ba5f4SPaul Zimmerman 			 * If Session End Detected the B-Cable has been
117197ba5f4SPaul Zimmerman 			 * disconnected
118197ba5f4SPaul Zimmerman 			 */
119197ba5f4SPaul Zimmerman 			/* Reset to a clean state */
120197ba5f4SPaul Zimmerman 			hsotg->lx_state = DWC2_L0;
121197ba5f4SPaul Zimmerman 		}
122197ba5f4SPaul Zimmerman 
123f25c42b8SGevorg Sahakyan 		gotgctl = dwc2_readl(hsotg, GOTGCTL);
124197ba5f4SPaul Zimmerman 		gotgctl &= ~GOTGCTL_DEVHNPEN;
125f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, gotgctl, GOTGCTL);
126197ba5f4SPaul Zimmerman 	}
127197ba5f4SPaul Zimmerman 
128197ba5f4SPaul Zimmerman 	if (gotgint & GOTGINT_SES_REQ_SUC_STS_CHNG) {
129197ba5f4SPaul Zimmerman 		dev_dbg(hsotg->dev,
130197ba5f4SPaul Zimmerman 			" ++OTG Interrupt: Session Request Success Status Change++\n");
131f25c42b8SGevorg Sahakyan 		gotgctl = dwc2_readl(hsotg, GOTGCTL);
132197ba5f4SPaul Zimmerman 		if (gotgctl & GOTGCTL_SESREQSCS) {
133ab283202SJohn Youn 			if (hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS &&
13495832c00SJohn Youn 			    hsotg->params.i2c_enable) {
135197ba5f4SPaul Zimmerman 				hsotg->srp_success = 1;
136197ba5f4SPaul Zimmerman 			} else {
137197ba5f4SPaul Zimmerman 				/* Clear Session Request */
138f25c42b8SGevorg Sahakyan 				gotgctl = dwc2_readl(hsotg, GOTGCTL);
139197ba5f4SPaul Zimmerman 				gotgctl &= ~GOTGCTL_SESREQ;
140f25c42b8SGevorg Sahakyan 				dwc2_writel(hsotg, gotgctl, GOTGCTL);
141197ba5f4SPaul Zimmerman 			}
142197ba5f4SPaul Zimmerman 		}
143197ba5f4SPaul Zimmerman 	}
144197ba5f4SPaul Zimmerman 
145197ba5f4SPaul Zimmerman 	if (gotgint & GOTGINT_HST_NEG_SUC_STS_CHNG) {
146197ba5f4SPaul Zimmerman 		/*
147197ba5f4SPaul Zimmerman 		 * Print statements during the HNP interrupt handling
148197ba5f4SPaul Zimmerman 		 * can cause it to fail
149197ba5f4SPaul Zimmerman 		 */
150f25c42b8SGevorg Sahakyan 		gotgctl = dwc2_readl(hsotg, GOTGCTL);
151197ba5f4SPaul Zimmerman 		/*
152197ba5f4SPaul Zimmerman 		 * WA for 3.00a- HW is not setting cur_mode, even sometimes
153197ba5f4SPaul Zimmerman 		 * this does not help
154197ba5f4SPaul Zimmerman 		 */
155197ba5f4SPaul Zimmerman 		if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a)
156197ba5f4SPaul Zimmerman 			udelay(100);
157197ba5f4SPaul Zimmerman 		if (gotgctl & GOTGCTL_HSTNEGSCS) {
158197ba5f4SPaul Zimmerman 			if (dwc2_is_host_mode(hsotg)) {
159197ba5f4SPaul Zimmerman 				hsotg->op_state = OTG_STATE_B_HOST;
160197ba5f4SPaul Zimmerman 				/*
161197ba5f4SPaul Zimmerman 				 * Need to disable SOF interrupt immediately.
162197ba5f4SPaul Zimmerman 				 * When switching from device to host, the PCD
163197ba5f4SPaul Zimmerman 				 * interrupt handler won't handle the interrupt
164197ba5f4SPaul Zimmerman 				 * if host mode is already set. The HCD
165197ba5f4SPaul Zimmerman 				 * interrupt handler won't get called if the
166197ba5f4SPaul Zimmerman 				 * HCD state is HALT. This means that the
167197ba5f4SPaul Zimmerman 				 * interrupt does not get handled and Linux
168197ba5f4SPaul Zimmerman 				 * complains loudly.
169197ba5f4SPaul Zimmerman 				 */
170f25c42b8SGevorg Sahakyan 				gintmsk = dwc2_readl(hsotg, GINTMSK);
171197ba5f4SPaul Zimmerman 				gintmsk &= ~GINTSTS_SOF;
172f25c42b8SGevorg Sahakyan 				dwc2_writel(hsotg, gintmsk, GINTMSK);
173197ba5f4SPaul Zimmerman 
174197ba5f4SPaul Zimmerman 				/*
175197ba5f4SPaul Zimmerman 				 * Call callback function with spin lock
176197ba5f4SPaul Zimmerman 				 * released
177197ba5f4SPaul Zimmerman 				 */
178197ba5f4SPaul Zimmerman 				spin_unlock(&hsotg->lock);
179197ba5f4SPaul Zimmerman 
180197ba5f4SPaul Zimmerman 				/* Initialize the Core for Host mode */
181197ba5f4SPaul Zimmerman 				dwc2_hcd_start(hsotg);
182197ba5f4SPaul Zimmerman 				spin_lock(&hsotg->lock);
183197ba5f4SPaul Zimmerman 				hsotg->op_state = OTG_STATE_B_HOST;
184197ba5f4SPaul Zimmerman 			}
185197ba5f4SPaul Zimmerman 		} else {
186f25c42b8SGevorg Sahakyan 			gotgctl = dwc2_readl(hsotg, GOTGCTL);
187197ba5f4SPaul Zimmerman 			gotgctl &= ~(GOTGCTL_HNPREQ | GOTGCTL_DEVHNPEN);
188f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, gotgctl, GOTGCTL);
189197ba5f4SPaul Zimmerman 			dev_dbg(hsotg->dev, "HNP Failed\n");
190197ba5f4SPaul Zimmerman 			dev_err(hsotg->dev,
191197ba5f4SPaul Zimmerman 				"Device Not Connected/Responding\n");
192197ba5f4SPaul Zimmerman 		}
193197ba5f4SPaul Zimmerman 	}
194197ba5f4SPaul Zimmerman 
195197ba5f4SPaul Zimmerman 	if (gotgint & GOTGINT_HST_NEG_DET) {
196197ba5f4SPaul Zimmerman 		/*
197197ba5f4SPaul Zimmerman 		 * The disconnect interrupt is set at the same time as
198197ba5f4SPaul Zimmerman 		 * Host Negotiation Detected. During the mode switch all
199197ba5f4SPaul Zimmerman 		 * interrupts are cleared so the disconnect interrupt
200197ba5f4SPaul Zimmerman 		 * handler will not get executed.
201197ba5f4SPaul Zimmerman 		 */
202197ba5f4SPaul Zimmerman 		dev_dbg(hsotg->dev,
203197ba5f4SPaul Zimmerman 			" ++OTG Interrupt: Host Negotiation Detected++ (%s)\n",
204197ba5f4SPaul Zimmerman 			(dwc2_is_host_mode(hsotg) ? "Host" : "Device"));
205197ba5f4SPaul Zimmerman 		if (dwc2_is_device_mode(hsotg)) {
206197ba5f4SPaul Zimmerman 			dev_dbg(hsotg->dev, "a_suspend->a_peripheral (%d)\n",
207197ba5f4SPaul Zimmerman 				hsotg->op_state);
208197ba5f4SPaul Zimmerman 			spin_unlock(&hsotg->lock);
2096a659531SDouglas Anderson 			dwc2_hcd_disconnect(hsotg, false);
210197ba5f4SPaul Zimmerman 			spin_lock(&hsotg->lock);
211197ba5f4SPaul Zimmerman 			hsotg->op_state = OTG_STATE_A_PERIPHERAL;
212197ba5f4SPaul Zimmerman 		} else {
213197ba5f4SPaul Zimmerman 			/* Need to disable SOF interrupt immediately */
214f25c42b8SGevorg Sahakyan 			gintmsk = dwc2_readl(hsotg, GINTMSK);
215197ba5f4SPaul Zimmerman 			gintmsk &= ~GINTSTS_SOF;
216f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, gintmsk, GINTMSK);
217197ba5f4SPaul Zimmerman 			spin_unlock(&hsotg->lock);
218197ba5f4SPaul Zimmerman 			dwc2_hcd_start(hsotg);
219197ba5f4SPaul Zimmerman 			spin_lock(&hsotg->lock);
220197ba5f4SPaul Zimmerman 			hsotg->op_state = OTG_STATE_A_HOST;
221197ba5f4SPaul Zimmerman 		}
222197ba5f4SPaul Zimmerman 	}
223197ba5f4SPaul Zimmerman 
224197ba5f4SPaul Zimmerman 	if (gotgint & GOTGINT_A_DEV_TOUT_CHG)
225197ba5f4SPaul Zimmerman 		dev_dbg(hsotg->dev,
226197ba5f4SPaul Zimmerman 			" ++OTG Interrupt: A-Device Timeout Change++\n");
227197ba5f4SPaul Zimmerman 	if (gotgint & GOTGINT_DBNCE_DONE)
228197ba5f4SPaul Zimmerman 		dev_dbg(hsotg->dev, " ++OTG Interrupt: Debounce Done++\n");
229197ba5f4SPaul Zimmerman 
230197ba5f4SPaul Zimmerman 	/* Clear GOTGINT */
231f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, gotgint, GOTGINT);
232197ba5f4SPaul Zimmerman }
233197ba5f4SPaul Zimmerman 
234197ba5f4SPaul Zimmerman /**
235197ba5f4SPaul Zimmerman  * dwc2_handle_conn_id_status_change_intr() - Handles the Connector ID Status
236197ba5f4SPaul Zimmerman  * Change Interrupt
237197ba5f4SPaul Zimmerman  *
238197ba5f4SPaul Zimmerman  * @hsotg: Programming view of DWC_otg controller
239197ba5f4SPaul Zimmerman  *
240197ba5f4SPaul Zimmerman  * Reads the OTG Interrupt Register (GOTCTL) to determine whether this is a
241197ba5f4SPaul Zimmerman  * Device to Host Mode transition or a Host to Device Mode transition. This only
242197ba5f4SPaul Zimmerman  * occurs when the cable is connected/removed from the PHY connector.
243197ba5f4SPaul Zimmerman  */
dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg * hsotg)244197ba5f4SPaul Zimmerman static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
245197ba5f4SPaul Zimmerman {
24629539019SDouglas Anderson 	u32 gintmsk;
24729539019SDouglas Anderson 
24829539019SDouglas Anderson 	/* Clear interrupt */
249f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, GINTSTS_CONIDSTSCHNG, GINTSTS);
250197ba5f4SPaul Zimmerman 
251197ba5f4SPaul Zimmerman 	/* Need to disable SOF interrupt immediately */
252f25c42b8SGevorg Sahakyan 	gintmsk = dwc2_readl(hsotg, GINTMSK);
253197ba5f4SPaul Zimmerman 	gintmsk &= ~GINTSTS_SOF;
254f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, gintmsk, GINTMSK);
255197ba5f4SPaul Zimmerman 
256197ba5f4SPaul Zimmerman 	dev_dbg(hsotg->dev, " ++Connector ID Status Change Interrupt++  (%s)\n",
257197ba5f4SPaul Zimmerman 		dwc2_is_host_mode(hsotg) ? "Host" : "Device");
258197ba5f4SPaul Zimmerman 
259197ba5f4SPaul Zimmerman 	/*
260197ba5f4SPaul Zimmerman 	 * Need to schedule a work, as there are possible DELAY function calls.
261197ba5f4SPaul Zimmerman 	 */
262d8bc3bf8SLukas Wunner 	if (hsotg->wq_otg)
263197ba5f4SPaul Zimmerman 		queue_work(hsotg->wq_otg, &hsotg->wf_otg);
264197ba5f4SPaul Zimmerman }
265197ba5f4SPaul Zimmerman 
266197ba5f4SPaul Zimmerman /**
267197ba5f4SPaul Zimmerman  * dwc2_handle_session_req_intr() - This interrupt indicates that a device is
268197ba5f4SPaul Zimmerman  * initiating the Session Request Protocol to request the host to turn on bus
269197ba5f4SPaul Zimmerman  * power so a new session can begin
270197ba5f4SPaul Zimmerman  *
271197ba5f4SPaul Zimmerman  * @hsotg: Programming view of DWC_otg controller
272197ba5f4SPaul Zimmerman  *
273197ba5f4SPaul Zimmerman  * This handler responds by turning on bus power. If the DWC_otg controller is
274197ba5f4SPaul Zimmerman  * in low power mode, this handler brings the controller out of low power mode
275197ba5f4SPaul Zimmerman  * before turning on bus power.
276197ba5f4SPaul Zimmerman  */
dwc2_handle_session_req_intr(struct dwc2_hsotg * hsotg)277197ba5f4SPaul Zimmerman static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
278197ba5f4SPaul Zimmerman {
27921795c82SMian Yousaf Kaukab 	int ret;
28042b32b16SArtur Petrosyan 	u32 hprt0;
28121795c82SMian Yousaf Kaukab 
282197ba5f4SPaul Zimmerman 	/* Clear interrupt */
283f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, GINTSTS_SESSREQINT, GINTSTS);
2844ace06e8SMarek Szyprowski 
28529539019SDouglas Anderson 	dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
28629539019SDouglas Anderson 		hsotg->lx_state);
28729539019SDouglas Anderson 
28821795c82SMian Yousaf Kaukab 	if (dwc2_is_device_mode(hsotg)) {
2899b4965d7SArtur Petrosyan 		if (hsotg->lx_state == DWC2_L2) {
2909b4965d7SArtur Petrosyan 			if (hsotg->in_ppd) {
291c9c394abSArtur Petrosyan 				ret = dwc2_exit_partial_power_down(hsotg, 0,
292c9c394abSArtur Petrosyan 								   true);
293c9c394abSArtur Petrosyan 				if (ret)
29421795c82SMian Yousaf Kaukab 					dev_err(hsotg->dev,
29541ba9b9bSVardan Mikayelyan 						"exit power_down failed\n");
29621795c82SMian Yousaf Kaukab 			}
29721795c82SMian Yousaf Kaukab 
2989b4965d7SArtur Petrosyan 			/* Exit gadget mode clock gating. */
2999b4965d7SArtur Petrosyan 			if (hsotg->params.power_down ==
300f047361fSMinas Harutyunyan 			    DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended &&
301f047361fSMinas Harutyunyan 			    !hsotg->params.no_clock_gating)
3029b4965d7SArtur Petrosyan 				dwc2_gadget_exit_clock_gating(hsotg, 0);
3039b4965d7SArtur Petrosyan 		}
3049b4965d7SArtur Petrosyan 
3054ace06e8SMarek Szyprowski 		/*
30621795c82SMian Yousaf Kaukab 		 * Report disconnect if there is any previous session
30721795c82SMian Yousaf Kaukab 		 * established
3084ace06e8SMarek Szyprowski 		 */
3091f91b4ccSFelipe Balbi 		dwc2_hsotg_disconnect(hsotg);
31042b32b16SArtur Petrosyan 	} else {
31142b32b16SArtur Petrosyan 		/* Turn on the port power bit. */
31242b32b16SArtur Petrosyan 		hprt0 = dwc2_read_hprt0(hsotg);
31342b32b16SArtur Petrosyan 		hprt0 |= HPRT0_PWR;
31442b32b16SArtur Petrosyan 		dwc2_writel(hsotg, hprt0, HPRT0);
31542b32b16SArtur Petrosyan 		/* Connect hcd after port power is set. */
31642b32b16SArtur Petrosyan 		dwc2_hcd_connect(hsotg);
317197ba5f4SPaul Zimmerman 	}
31821795c82SMian Yousaf Kaukab }
319197ba5f4SPaul Zimmerman 
320273d576cSSevak Arakelyan /**
321273d576cSSevak Arakelyan  * dwc2_wakeup_from_lpm_l1 - Exit the device from LPM L1 state
322273d576cSSevak Arakelyan  *
323273d576cSSevak Arakelyan  * @hsotg: Programming view of DWC_otg controller
324273d576cSSevak Arakelyan  *
325273d576cSSevak Arakelyan  */
dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg * hsotg,bool remotewakeup)3263e417f31SMinas Harutyunyan void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg, bool remotewakeup)
327273d576cSSevak Arakelyan {
328273d576cSSevak Arakelyan 	u32 glpmcfg;
3293e417f31SMinas Harutyunyan 	u32 pcgctl;
3303e417f31SMinas Harutyunyan 	u32 dctl;
331273d576cSSevak Arakelyan 
332273d576cSSevak Arakelyan 	if (hsotg->lx_state != DWC2_L1) {
333273d576cSSevak Arakelyan 		dev_err(hsotg->dev, "Core isn't in DWC2_L1 state\n");
334273d576cSSevak Arakelyan 		return;
335273d576cSSevak Arakelyan 	}
336273d576cSSevak Arakelyan 
337f25c42b8SGevorg Sahakyan 	glpmcfg = dwc2_readl(hsotg, GLPMCFG);
338273d576cSSevak Arakelyan 	if (dwc2_is_device_mode(hsotg)) {
3393e417f31SMinas Harutyunyan 		dev_dbg(hsotg->dev, "Exit from L1 state, remotewakeup=%d\n", remotewakeup);
340273d576cSSevak Arakelyan 		glpmcfg &= ~GLPMCFG_ENBLSLPM;
3413e417f31SMinas Harutyunyan 		glpmcfg &= ~GLPMCFG_HIRD_THRES_MASK;
342f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, glpmcfg, GLPMCFG);
343273d576cSSevak Arakelyan 
3443e417f31SMinas Harutyunyan 		pcgctl = dwc2_readl(hsotg, PCGCTL);
3453e417f31SMinas Harutyunyan 		pcgctl &= ~PCGCTL_ENBL_SLEEP_GATING;
3463e417f31SMinas Harutyunyan 		dwc2_writel(hsotg, pcgctl, PCGCTL);
3473e417f31SMinas Harutyunyan 
348f25c42b8SGevorg Sahakyan 		glpmcfg = dwc2_readl(hsotg, GLPMCFG);
3493e417f31SMinas Harutyunyan 		if (glpmcfg & GLPMCFG_ENBESL) {
3503e417f31SMinas Harutyunyan 			glpmcfg |= GLPMCFG_RSTRSLPSTS;
3513e417f31SMinas Harutyunyan 			dwc2_writel(hsotg, glpmcfg, GLPMCFG);
3523e417f31SMinas Harutyunyan 		}
353273d576cSSevak Arakelyan 
3543e417f31SMinas Harutyunyan 		if (remotewakeup) {
3553e417f31SMinas Harutyunyan 			if (dwc2_hsotg_wait_bit_set(hsotg, GLPMCFG, GLPMCFG_L1RESUMEOK, 1000)) {
3563e417f31SMinas Harutyunyan 				dev_warn(hsotg->dev, "%s: timeout GLPMCFG_L1RESUMEOK\n", __func__);
3573e417f31SMinas Harutyunyan 				goto fail;
358273d576cSSevak Arakelyan 				return;
359273d576cSSevak Arakelyan 			}
3603e417f31SMinas Harutyunyan 
3613e417f31SMinas Harutyunyan 			dctl = dwc2_readl(hsotg, DCTL);
3623e417f31SMinas Harutyunyan 			dctl |= DCTL_RMTWKUPSIG;
3633e417f31SMinas Harutyunyan 			dwc2_writel(hsotg, dctl, DCTL);
3643e417f31SMinas Harutyunyan 
3653e417f31SMinas Harutyunyan 			if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_WKUPINT, 1000)) {
3663e417f31SMinas Harutyunyan 				dev_warn(hsotg->dev, "%s: timeout GINTSTS_WKUPINT\n", __func__);
3673e417f31SMinas Harutyunyan 				goto fail;
3683e417f31SMinas Harutyunyan 				return;
3693e417f31SMinas Harutyunyan 			}
3703e417f31SMinas Harutyunyan 		}
3713e417f31SMinas Harutyunyan 
3723e417f31SMinas Harutyunyan 		glpmcfg = dwc2_readl(hsotg, GLPMCFG);
3733e417f31SMinas Harutyunyan 		if (glpmcfg & GLPMCFG_COREL1RES_MASK || glpmcfg & GLPMCFG_SLPSTS ||
3743e417f31SMinas Harutyunyan 		    glpmcfg & GLPMCFG_L1RESUMEOK) {
3753e417f31SMinas Harutyunyan 			goto fail;
3763e417f31SMinas Harutyunyan 			return;
3773e417f31SMinas Harutyunyan 		}
3783e417f31SMinas Harutyunyan 
3793e417f31SMinas Harutyunyan 		/* Inform gadget to exit from L1 */
3803e417f31SMinas Harutyunyan 		call_gadget(hsotg, resume);
3813e417f31SMinas Harutyunyan 		/* Change to L0 state */
3823e417f31SMinas Harutyunyan 		hsotg->lx_state = DWC2_L0;
3833e417f31SMinas Harutyunyan 		hsotg->bus_suspended = false;
3843e417f31SMinas Harutyunyan fail:		dwc2_gadget_init_lpm(hsotg);
385273d576cSSevak Arakelyan 	} else {
386273d576cSSevak Arakelyan 		/* TODO */
387273d576cSSevak Arakelyan 		dev_err(hsotg->dev, "Host side LPM is not supported.\n");
388273d576cSSevak Arakelyan 		return;
389273d576cSSevak Arakelyan 	}
390273d576cSSevak Arakelyan }
391273d576cSSevak Arakelyan 
392197ba5f4SPaul Zimmerman /*
393197ba5f4SPaul Zimmerman  * This interrupt indicates that the DWC_otg controller has detected a
394197ba5f4SPaul Zimmerman  * resume or remote wakeup sequence. If the DWC_otg controller is in
395197ba5f4SPaul Zimmerman  * low power mode, the handler must brings the controller out of low
396197ba5f4SPaul Zimmerman  * power mode. The controller automatically begins resume signaling.
397197ba5f4SPaul Zimmerman  * The handler schedules a time to stop resume signaling.
398197ba5f4SPaul Zimmerman  */
dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg * hsotg)399197ba5f4SPaul Zimmerman static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
400197ba5f4SPaul Zimmerman {
401f81f46e1SGregory Herrero 	int ret;
40229539019SDouglas Anderson 
40329539019SDouglas Anderson 	/* Clear interrupt */
404f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, GINTSTS_WKUPINT, GINTSTS);
40529539019SDouglas Anderson 
406197ba5f4SPaul Zimmerman 	dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
407197ba5f4SPaul Zimmerman 	dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
408197ba5f4SPaul Zimmerman 
409273d576cSSevak Arakelyan 	if (hsotg->lx_state == DWC2_L1) {
4103e417f31SMinas Harutyunyan 		dwc2_wakeup_from_lpm_l1(hsotg, false);
411273d576cSSevak Arakelyan 		return;
412273d576cSSevak Arakelyan 	}
413273d576cSSevak Arakelyan 
414197ba5f4SPaul Zimmerman 	if (dwc2_is_device_mode(hsotg)) {
41595c8bc36SAntti Seppälä 		dev_dbg(hsotg->dev, "DSTS=0x%0x\n",
416f25c42b8SGevorg Sahakyan 			dwc2_readl(hsotg, DSTS));
4175d240efdSArtur Petrosyan 		if (hsotg->lx_state == DWC2_L2) {
4185d240efdSArtur Petrosyan 			if (hsotg->in_ppd) {
419f25c42b8SGevorg Sahakyan 				u32 dctl = dwc2_readl(hsotg, DCTL);
420197ba5f4SPaul Zimmerman 				/* Clear Remote Wakeup Signaling */
421197ba5f4SPaul Zimmerman 				dctl &= ~DCTL_RMTWKUPSIG;
422f25c42b8SGevorg Sahakyan 				dwc2_writel(hsotg, dctl, DCTL);
423c9c394abSArtur Petrosyan 				ret = dwc2_exit_partial_power_down(hsotg, 1,
424c9c394abSArtur Petrosyan 								   true);
425c9c394abSArtur Petrosyan 				if (ret)
426c9c394abSArtur Petrosyan 					dev_err(hsotg->dev,
427c9c394abSArtur Petrosyan 						"exit partial_power_down failed\n");
4288c935deaSFabrice Gasnier 				call_gadget(hsotg, resume);
4295d240efdSArtur Petrosyan 			}
4305d240efdSArtur Petrosyan 
4315d240efdSArtur Petrosyan 			/* Exit gadget mode clock gating. */
4325d240efdSArtur Petrosyan 			if (hsotg->params.power_down ==
433f047361fSMinas Harutyunyan 			    DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended &&
434f047361fSMinas Harutyunyan 			    !hsotg->params.no_clock_gating)
4355d240efdSArtur Petrosyan 				dwc2_gadget_exit_clock_gating(hsotg, 0);
4368c935deaSFabrice Gasnier 		} else {
4378c935deaSFabrice Gasnier 			/* Change to L0 state */
4388c935deaSFabrice Gasnier 			hsotg->lx_state = DWC2_L0;
4398c935deaSFabrice Gasnier 		}
440197ba5f4SPaul Zimmerman 	} else {
441b77b0d00SArtur Petrosyan 		if (hsotg->lx_state == DWC2_L2) {
442b77b0d00SArtur Petrosyan 			if (hsotg->in_ppd) {
443b77b0d00SArtur Petrosyan 				ret = dwc2_exit_partial_power_down(hsotg, 1,
444b77b0d00SArtur Petrosyan 								   true);
445b77b0d00SArtur Petrosyan 				if (ret)
446b77b0d00SArtur Petrosyan 					dev_err(hsotg->dev,
447b77b0d00SArtur Petrosyan 						"exit partial_power_down failed\n");
448b77b0d00SArtur Petrosyan 			}
449c40cf770SDouglas Anderson 
4505d240efdSArtur Petrosyan 			if (hsotg->params.power_down ==
451f047361fSMinas Harutyunyan 			    DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended &&
452f047361fSMinas Harutyunyan 			    !hsotg->params.no_clock_gating)
4535d240efdSArtur Petrosyan 				dwc2_host_exit_clock_gating(hsotg, 1);
4545d240efdSArtur Petrosyan 
455c40cf770SDouglas Anderson 			/*
456c40cf770SDouglas Anderson 			 * If we've got this quirk then the PHY is stuck upon
457c40cf770SDouglas Anderson 			 * wakeup.  Assert reset.  This will propagate out and
458c40cf770SDouglas Anderson 			 * eventually we'll re-enumerate the device.  Not great
459c40cf770SDouglas Anderson 			 * but the best we can do.  We can't call phy_reset()
460c40cf770SDouglas Anderson 			 * at interrupt time but there's no hurry, so we'll
461c40cf770SDouglas Anderson 			 * schedule it for later.
462c40cf770SDouglas Anderson 			 */
463c40cf770SDouglas Anderson 			if (hsotg->reset_phy_on_wake)
464c40cf770SDouglas Anderson 				dwc2_host_schedule_phy_reset(hsotg);
465c40cf770SDouglas Anderson 
466197ba5f4SPaul Zimmerman 			mod_timer(&hsotg->wkp_timer,
467197ba5f4SPaul Zimmerman 				  jiffies + msecs_to_jiffies(71));
468197ba5f4SPaul Zimmerman 		} else {
469197ba5f4SPaul Zimmerman 			/* Change to L0 state */
470197ba5f4SPaul Zimmerman 			hsotg->lx_state = DWC2_L0;
471197ba5f4SPaul Zimmerman 		}
472197ba5f4SPaul Zimmerman 	}
473197ba5f4SPaul Zimmerman }
474197ba5f4SPaul Zimmerman 
475197ba5f4SPaul Zimmerman /*
476197ba5f4SPaul Zimmerman  * This interrupt indicates that a device has been disconnected from the
477197ba5f4SPaul Zimmerman  * root port
478197ba5f4SPaul Zimmerman  */
dwc2_handle_disconnect_intr(struct dwc2_hsotg * hsotg)479197ba5f4SPaul Zimmerman static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
480197ba5f4SPaul Zimmerman {
481f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, GINTSTS_DISCONNINT, GINTSTS);
48229539019SDouglas Anderson 
483197ba5f4SPaul Zimmerman 	dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n",
484197ba5f4SPaul Zimmerman 		dwc2_is_host_mode(hsotg) ? "Host" : "Device",
485197ba5f4SPaul Zimmerman 		dwc2_op_state_str(hsotg));
486197ba5f4SPaul Zimmerman 
487509d612bSYunzhi Li 	if (hsotg->op_state == OTG_STATE_A_HOST)
4886a659531SDouglas Anderson 		dwc2_hcd_disconnect(hsotg, false);
489197ba5f4SPaul Zimmerman }
490197ba5f4SPaul Zimmerman 
491197ba5f4SPaul Zimmerman /*
492197ba5f4SPaul Zimmerman  * This interrupt indicates that SUSPEND state has been detected on the USB.
493197ba5f4SPaul Zimmerman  *
494197ba5f4SPaul Zimmerman  * For HNP the USB Suspend interrupt signals the change from "a_peripheral"
495197ba5f4SPaul Zimmerman  * to "a_host".
496197ba5f4SPaul Zimmerman  *
497197ba5f4SPaul Zimmerman  * When power management is enabled the core will be put in low power mode.
498197ba5f4SPaul Zimmerman  */
dwc2_handle_usb_suspend_intr(struct dwc2_hsotg * hsotg)499197ba5f4SPaul Zimmerman static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
500197ba5f4SPaul Zimmerman {
501197ba5f4SPaul Zimmerman 	u32 dsts;
502f81f46e1SGregory Herrero 	int ret;
503197ba5f4SPaul Zimmerman 
50429539019SDouglas Anderson 	/* Clear interrupt */
505f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, GINTSTS_USBSUSP, GINTSTS);
50629539019SDouglas Anderson 
507197ba5f4SPaul Zimmerman 	dev_dbg(hsotg->dev, "USB SUSPEND\n");
508197ba5f4SPaul Zimmerman 
509197ba5f4SPaul Zimmerman 	if (dwc2_is_device_mode(hsotg)) {
510197ba5f4SPaul Zimmerman 		/*
511197ba5f4SPaul Zimmerman 		 * Check the Device status register to determine if the Suspend
512197ba5f4SPaul Zimmerman 		 * state is active
513197ba5f4SPaul Zimmerman 		 */
514f25c42b8SGevorg Sahakyan 		dsts = dwc2_readl(hsotg, DSTS);
51597861781SVardan Mikayelyan 		dev_dbg(hsotg->dev, "%s: DSTS=0x%0x\n", __func__, dsts);
516197ba5f4SPaul Zimmerman 		dev_dbg(hsotg->dev,
51797861781SVardan Mikayelyan 			"DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d HWCFG4.Hibernation=%d\n",
518197ba5f4SPaul Zimmerman 			!!(dsts & DSTS_SUSPSTS),
51997861781SVardan Mikayelyan 			hsotg->hw_params.power_optimized,
52097861781SVardan Mikayelyan 			hsotg->hw_params.hibernation);
52197861781SVardan Mikayelyan 
522f81f46e1SGregory Herrero 		/* Ignore suspend request before enumeration */
523f81f46e1SGregory Herrero 		if (!dwc2_is_device_connected(hsotg)) {
524f81f46e1SGregory Herrero 			dev_dbg(hsotg->dev,
525f81f46e1SGregory Herrero 				"ignore suspend request before enumeration\n");
52629539019SDouglas Anderson 			return;
527f81f46e1SGregory Herrero 		}
52897861781SVardan Mikayelyan 		if (dsts & DSTS_SUSPSTS) {
5290112b7ceSArtur Petrosyan 			switch (hsotg->params.power_down) {
5300112b7ceSArtur Petrosyan 			case DWC2_POWER_DOWN_PARAM_PARTIAL:
53141ba9b9bSVardan Mikayelyan 				ret = dwc2_enter_partial_power_down(hsotg);
5320fdf3c5eSArtur Petrosyan 				if (ret)
533f81f46e1SGregory Herrero 					dev_err(hsotg->dev,
5340fdf3c5eSArtur Petrosyan 						"enter partial_power_down failed\n");
5350fdf3c5eSArtur Petrosyan 
536f81f46e1SGregory Herrero 				udelay(100);
537f81f46e1SGregory Herrero 
538f81f46e1SGregory Herrero 				/* Ask phy to be suspended */
539f81f46e1SGregory Herrero 				if (!IS_ERR_OR_NULL(hsotg->uphy))
540f81f46e1SGregory Herrero 					usb_phy_set_suspend(hsotg->uphy, true);
5410112b7ceSArtur Petrosyan 				break;
5420112b7ceSArtur Petrosyan 			case DWC2_POWER_DOWN_PARAM_HIBERNATION:
54397861781SVardan Mikayelyan 				ret = dwc2_enter_hibernation(hsotg, 0);
5440fdf3c5eSArtur Petrosyan 				if (ret)
54597861781SVardan Mikayelyan 					dev_err(hsotg->dev,
5460fdf3c5eSArtur Petrosyan 						"enter hibernation failed\n");
5470112b7ceSArtur Petrosyan 				break;
5480112b7ceSArtur Petrosyan 			case DWC2_POWER_DOWN_PARAM_NONE:
549cbe1e903SArtur Petrosyan 				/*
5500112b7ceSArtur Petrosyan 				 * If neither hibernation nor partial power down are supported,
551cbe1e903SArtur Petrosyan 				 * clock gating is used to save power.
552cbe1e903SArtur Petrosyan 				 */
553c4a0f7a6SMarek Szyprowski 				if (!hsotg->params.no_clock_gating)
554cbe1e903SArtur Petrosyan 					dwc2_gadget_enter_clock_gating(hsotg);
55597861781SVardan Mikayelyan 			}
5560fdf3c5eSArtur Petrosyan 
5573eb42df3SGregory Herrero 			/*
5583eb42df3SGregory Herrero 			 * Change to L2 (suspend) state before releasing
5593eb42df3SGregory Herrero 			 * spinlock
5603eb42df3SGregory Herrero 			 */
5613eb42df3SGregory Herrero 			hsotg->lx_state = DWC2_L2;
5623eb42df3SGregory Herrero 
563f81f46e1SGregory Herrero 			/* Call gadget suspend callback */
564f81f46e1SGregory Herrero 			call_gadget(hsotg, suspend);
565f81f46e1SGregory Herrero 		}
566197ba5f4SPaul Zimmerman 	} else {
567197ba5f4SPaul Zimmerman 		if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) {
568197ba5f4SPaul Zimmerman 			dev_dbg(hsotg->dev, "a_peripheral->a_host\n");
569197ba5f4SPaul Zimmerman 
5703eb42df3SGregory Herrero 			/* Change to L2 (suspend) state */
5713eb42df3SGregory Herrero 			hsotg->lx_state = DWC2_L2;
572197ba5f4SPaul Zimmerman 			/* Clear the a_peripheral flag, back to a_host */
573197ba5f4SPaul Zimmerman 			spin_unlock(&hsotg->lock);
574197ba5f4SPaul Zimmerman 			dwc2_hcd_start(hsotg);
575197ba5f4SPaul Zimmerman 			spin_lock(&hsotg->lock);
576197ba5f4SPaul Zimmerman 			hsotg->op_state = OTG_STATE_A_HOST;
577197ba5f4SPaul Zimmerman 		}
578197ba5f4SPaul Zimmerman 	}
579197ba5f4SPaul Zimmerman }
580197ba5f4SPaul Zimmerman 
581d2521849SSevak Arakelyan /**
582d2521849SSevak Arakelyan  * dwc2_handle_lpm_intr - GINTSTS_LPMTRANRCVD Interrupt handler
583d2521849SSevak Arakelyan  *
584d2521849SSevak Arakelyan  * @hsotg: Programming view of DWC_otg controller
585d2521849SSevak Arakelyan  *
586d2521849SSevak Arakelyan  */
dwc2_handle_lpm_intr(struct dwc2_hsotg * hsotg)587d2521849SSevak Arakelyan static void dwc2_handle_lpm_intr(struct dwc2_hsotg *hsotg)
588d2521849SSevak Arakelyan {
589d2521849SSevak Arakelyan 	u32 glpmcfg;
590d2521849SSevak Arakelyan 	u32 pcgcctl;
591d2521849SSevak Arakelyan 	u32 hird;
592d2521849SSevak Arakelyan 	u32 hird_thres;
593d2521849SSevak Arakelyan 	u32 hird_thres_en;
594d2521849SSevak Arakelyan 	u32 enslpm;
595d2521849SSevak Arakelyan 
596d2521849SSevak Arakelyan 	/* Clear interrupt */
597f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, GINTSTS_LPMTRANRCVD, GINTSTS);
598d2521849SSevak Arakelyan 
599f25c42b8SGevorg Sahakyan 	glpmcfg = dwc2_readl(hsotg, GLPMCFG);
600d2521849SSevak Arakelyan 
601d2521849SSevak Arakelyan 	if (!(glpmcfg & GLPMCFG_LPMCAP)) {
602d2521849SSevak Arakelyan 		dev_err(hsotg->dev, "Unexpected LPM interrupt\n");
603d2521849SSevak Arakelyan 		return;
604d2521849SSevak Arakelyan 	}
605d2521849SSevak Arakelyan 
606d2521849SSevak Arakelyan 	hird = (glpmcfg & GLPMCFG_HIRD_MASK) >> GLPMCFG_HIRD_SHIFT;
607d2521849SSevak Arakelyan 	hird_thres = (glpmcfg & GLPMCFG_HIRD_THRES_MASK &
608d2521849SSevak Arakelyan 			~GLPMCFG_HIRD_THRES_EN) >> GLPMCFG_HIRD_THRES_SHIFT;
609d2521849SSevak Arakelyan 	hird_thres_en = glpmcfg & GLPMCFG_HIRD_THRES_EN;
610376f0401SSevak Arakelyan 	enslpm = glpmcfg & GLPMCFG_ENBLSLPM;
611d2521849SSevak Arakelyan 
612d2521849SSevak Arakelyan 	if (dwc2_is_device_mode(hsotg)) {
613d2521849SSevak Arakelyan 		dev_dbg(hsotg->dev, "HIRD_THRES_EN = %d\n", hird_thres_en);
614d2521849SSevak Arakelyan 
615d2521849SSevak Arakelyan 		if (hird_thres_en && hird >= hird_thres) {
616d2521849SSevak Arakelyan 			dev_dbg(hsotg->dev, "L1 with utmi_l1_suspend_n\n");
617d2521849SSevak Arakelyan 		} else if (enslpm) {
618d2521849SSevak Arakelyan 			dev_dbg(hsotg->dev, "L1 with utmi_sleep_n\n");
619d2521849SSevak Arakelyan 		} else {
620d2521849SSevak Arakelyan 			dev_dbg(hsotg->dev, "Entering Sleep with L1 Gating\n");
621d2521849SSevak Arakelyan 
622f25c42b8SGevorg Sahakyan 			pcgcctl = dwc2_readl(hsotg, PCGCTL);
623d2521849SSevak Arakelyan 			pcgcctl |= PCGCTL_ENBL_SLEEP_GATING;
624f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, pcgcctl, PCGCTL);
625d2521849SSevak Arakelyan 		}
626d2521849SSevak Arakelyan 		/**
627d2521849SSevak Arakelyan 		 * Examine prt_sleep_sts after TL1TokenTetry period max (10 us)
628d2521849SSevak Arakelyan 		 */
629d2521849SSevak Arakelyan 		udelay(10);
630d2521849SSevak Arakelyan 
631f25c42b8SGevorg Sahakyan 		glpmcfg = dwc2_readl(hsotg, GLPMCFG);
632d2521849SSevak Arakelyan 
633d2521849SSevak Arakelyan 		if (glpmcfg & GLPMCFG_SLPSTS) {
634d2521849SSevak Arakelyan 			/* Save the current state */
635d2521849SSevak Arakelyan 			hsotg->lx_state = DWC2_L1;
636d2521849SSevak Arakelyan 			dev_dbg(hsotg->dev,
637d2521849SSevak Arakelyan 				"Core is in L1 sleep glpmcfg=%08x\n", glpmcfg);
638c655557cSGrigor Tovmasyan 
639c655557cSGrigor Tovmasyan 			/* Inform gadget that we are in L1 state */
640c655557cSGrigor Tovmasyan 			call_gadget(hsotg, suspend);
641d2521849SSevak Arakelyan 		}
642d2521849SSevak Arakelyan 	}
643d2521849SSevak Arakelyan }
644d2521849SSevak Arakelyan 
645197ba5f4SPaul Zimmerman #define GINTMSK_COMMON	(GINTSTS_WKUPINT | GINTSTS_SESSREQINT |		\
646197ba5f4SPaul Zimmerman 			 GINTSTS_CONIDSTSCHNG | GINTSTS_OTGINT |	\
647197ba5f4SPaul Zimmerman 			 GINTSTS_MODEMIS | GINTSTS_DISCONNINT |		\
648376f0401SSevak Arakelyan 			 GINTSTS_USBSUSP | GINTSTS_PRTINT |		\
649376f0401SSevak Arakelyan 			 GINTSTS_LPMTRANRCVD)
650197ba5f4SPaul Zimmerman 
651197ba5f4SPaul Zimmerman /*
652197ba5f4SPaul Zimmerman  * This function returns the Core Interrupt register
653197ba5f4SPaul Zimmerman  */
dwc2_read_common_intr(struct dwc2_hsotg * hsotg)654197ba5f4SPaul Zimmerman static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg)
655197ba5f4SPaul Zimmerman {
656197ba5f4SPaul Zimmerman 	u32 gintsts;
657197ba5f4SPaul Zimmerman 	u32 gintmsk;
658197ba5f4SPaul Zimmerman 	u32 gahbcfg;
659197ba5f4SPaul Zimmerman 	u32 gintmsk_common = GINTMSK_COMMON;
660197ba5f4SPaul Zimmerman 
661f25c42b8SGevorg Sahakyan 	gintsts = dwc2_readl(hsotg, GINTSTS);
662f25c42b8SGevorg Sahakyan 	gintmsk = dwc2_readl(hsotg, GINTMSK);
663f25c42b8SGevorg Sahakyan 	gahbcfg = dwc2_readl(hsotg, GAHBCFG);
664197ba5f4SPaul Zimmerman 
665197ba5f4SPaul Zimmerman 	/* If any common interrupts set */
666197ba5f4SPaul Zimmerman 	if (gintsts & gintmsk_common)
667197ba5f4SPaul Zimmerman 		dev_dbg(hsotg->dev, "gintsts=%08x  gintmsk=%08x\n",
668197ba5f4SPaul Zimmerman 			gintsts, gintmsk);
669197ba5f4SPaul Zimmerman 
670197ba5f4SPaul Zimmerman 	if (gahbcfg & GAHBCFG_GLBL_INTR_EN)
671197ba5f4SPaul Zimmerman 		return gintsts & gintmsk & gintmsk_common;
672197ba5f4SPaul Zimmerman 	else
673197ba5f4SPaul Zimmerman 		return 0;
674197ba5f4SPaul Zimmerman }
675197ba5f4SPaul Zimmerman 
67624d209dbSArtur Petrosyan /**
67724d209dbSArtur Petrosyan  * dwc_handle_gpwrdn_disc_det() - Handles the gpwrdn disconnect detect.
67824d209dbSArtur Petrosyan  * Exits hibernation without restoring registers.
67924d209dbSArtur Petrosyan  *
68024d209dbSArtur Petrosyan  * @hsotg: Programming view of DWC_otg controller
68124d209dbSArtur Petrosyan  * @gpwrdn: GPWRDN register
68224d209dbSArtur Petrosyan  */
dwc_handle_gpwrdn_disc_det(struct dwc2_hsotg * hsotg,u32 gpwrdn)68324d209dbSArtur Petrosyan static inline void dwc_handle_gpwrdn_disc_det(struct dwc2_hsotg *hsotg,
68424d209dbSArtur Petrosyan 					      u32 gpwrdn)
68524d209dbSArtur Petrosyan {
68624d209dbSArtur Petrosyan 	u32 gpwrdn_tmp;
68724d209dbSArtur Petrosyan 
68824d209dbSArtur Petrosyan 	/* Switch-on voltage to the core */
68924d209dbSArtur Petrosyan 	gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
69024d209dbSArtur Petrosyan 	gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH;
69124d209dbSArtur Petrosyan 	dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
69224d209dbSArtur Petrosyan 	udelay(5);
69324d209dbSArtur Petrosyan 
69424d209dbSArtur Petrosyan 	/* Reset core */
69524d209dbSArtur Petrosyan 	gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
69624d209dbSArtur Petrosyan 	gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN;
69724d209dbSArtur Petrosyan 	dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
69824d209dbSArtur Petrosyan 	udelay(5);
69924d209dbSArtur Petrosyan 
70024d209dbSArtur Petrosyan 	/* Disable Power Down Clamp */
70124d209dbSArtur Petrosyan 	gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
70224d209dbSArtur Petrosyan 	gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP;
70324d209dbSArtur Petrosyan 	dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
70424d209dbSArtur Petrosyan 	udelay(5);
70524d209dbSArtur Petrosyan 
70624d209dbSArtur Petrosyan 	/* Deassert reset core */
70724d209dbSArtur Petrosyan 	gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
70824d209dbSArtur Petrosyan 	gpwrdn_tmp |= GPWRDN_PWRDNRSTN;
70924d209dbSArtur Petrosyan 	dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
71024d209dbSArtur Petrosyan 	udelay(5);
71124d209dbSArtur Petrosyan 
71224d209dbSArtur Petrosyan 	/* Disable PMU interrupt */
71324d209dbSArtur Petrosyan 	gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
71424d209dbSArtur Petrosyan 	gpwrdn_tmp &= ~GPWRDN_PMUINTSEL;
71524d209dbSArtur Petrosyan 	dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
71624d209dbSArtur Petrosyan 
71724d209dbSArtur Petrosyan 	/* De-assert Wakeup Logic */
71824d209dbSArtur Petrosyan 	gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
71924d209dbSArtur Petrosyan 	gpwrdn_tmp &= ~GPWRDN_PMUACTV;
72024d209dbSArtur Petrosyan 	dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
72124d209dbSArtur Petrosyan 
72224d209dbSArtur Petrosyan 	hsotg->hibernated = 0;
72324d209dbSArtur Petrosyan 	hsotg->bus_suspended = 0;
72424d209dbSArtur Petrosyan 
72524d209dbSArtur Petrosyan 	if (gpwrdn & GPWRDN_IDSTS) {
72624d209dbSArtur Petrosyan 		hsotg->op_state = OTG_STATE_B_PERIPHERAL;
72724d209dbSArtur Petrosyan 		dwc2_core_init(hsotg, false);
72824d209dbSArtur Petrosyan 		dwc2_enable_global_interrupts(hsotg);
72924d209dbSArtur Petrosyan 		dwc2_hsotg_core_init_disconnected(hsotg, false);
73024d209dbSArtur Petrosyan 		dwc2_hsotg_core_connect(hsotg);
73124d209dbSArtur Petrosyan 	} else {
73224d209dbSArtur Petrosyan 		hsotg->op_state = OTG_STATE_A_HOST;
73324d209dbSArtur Petrosyan 
73424d209dbSArtur Petrosyan 		/* Initialize the Core for Host mode */
73524d209dbSArtur Petrosyan 		dwc2_core_init(hsotg, false);
73624d209dbSArtur Petrosyan 		dwc2_enable_global_interrupts(hsotg);
73724d209dbSArtur Petrosyan 		dwc2_hcd_start(hsotg);
73824d209dbSArtur Petrosyan 	}
73924d209dbSArtur Petrosyan }
74024d209dbSArtur Petrosyan 
741197ba5f4SPaul Zimmerman /*
74265c9c4c6SVardan Mikayelyan  * GPWRDN interrupt handler.
74365c9c4c6SVardan Mikayelyan  *
74465c9c4c6SVardan Mikayelyan  * The GPWRDN interrupts are those that occur in both Host and
74565c9c4c6SVardan Mikayelyan  * Device mode while core is in hibernated state.
74665c9c4c6SVardan Mikayelyan  */
dwc2_handle_gpwrdn_intr(struct dwc2_hsotg * hsotg)7474111d5f8SArtur Petrosyan static int dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg)
74865c9c4c6SVardan Mikayelyan {
74965c9c4c6SVardan Mikayelyan 	u32 gpwrdn;
75065c9c4c6SVardan Mikayelyan 	int linestate;
7514111d5f8SArtur Petrosyan 	int ret = 0;
75265c9c4c6SVardan Mikayelyan 
753f25c42b8SGevorg Sahakyan 	gpwrdn = dwc2_readl(hsotg, GPWRDN);
75465c9c4c6SVardan Mikayelyan 	/* clear all interrupt */
755f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, gpwrdn, GPWRDN);
75665c9c4c6SVardan Mikayelyan 	linestate = (gpwrdn & GPWRDN_LINESTATE_MASK) >> GPWRDN_LINESTATE_SHIFT;
75765c9c4c6SVardan Mikayelyan 	dev_dbg(hsotg->dev,
75865c9c4c6SVardan Mikayelyan 		"%s: dwc2_handle_gpwrdwn_intr called gpwrdn= %08x\n", __func__,
75965c9c4c6SVardan Mikayelyan 		gpwrdn);
76065c9c4c6SVardan Mikayelyan 
76165c9c4c6SVardan Mikayelyan 	if ((gpwrdn & GPWRDN_DISCONN_DET) &&
76265c9c4c6SVardan Mikayelyan 	    (gpwrdn & GPWRDN_DISCONN_DET_MSK) && !linestate) {
76365c9c4c6SVardan Mikayelyan 		dev_dbg(hsotg->dev, "%s: GPWRDN_DISCONN_DET\n", __func__);
76424d209dbSArtur Petrosyan 		/*
76524d209dbSArtur Petrosyan 		 * Call disconnect detect function to exit from
76624d209dbSArtur Petrosyan 		 * hibernation
76724d209dbSArtur Petrosyan 		 */
76824d209dbSArtur Petrosyan 		dwc_handle_gpwrdn_disc_det(hsotg, gpwrdn);
76924d209dbSArtur Petrosyan 	} else if ((gpwrdn & GPWRDN_LNSTSCHG) &&
77065c9c4c6SVardan Mikayelyan 		   (gpwrdn & GPWRDN_LNSTSCHG_MSK) && linestate) {
77165c9c4c6SVardan Mikayelyan 		dev_dbg(hsotg->dev, "%s: GPWRDN_LNSTSCHG\n", __func__);
77265c9c4c6SVardan Mikayelyan 		if (hsotg->hw_params.hibernation &&
77365c9c4c6SVardan Mikayelyan 		    hsotg->hibernated) {
77465c9c4c6SVardan Mikayelyan 			if (gpwrdn & GPWRDN_IDSTS) {
7754111d5f8SArtur Petrosyan 				ret = dwc2_exit_hibernation(hsotg, 0, 0, 0);
7764111d5f8SArtur Petrosyan 				if (ret)
7774111d5f8SArtur Petrosyan 					dev_err(hsotg->dev,
7784111d5f8SArtur Petrosyan 						"exit hibernation failed.\n");
77965c9c4c6SVardan Mikayelyan 				call_gadget(hsotg, resume);
78065c9c4c6SVardan Mikayelyan 			} else {
7814111d5f8SArtur Petrosyan 				ret = dwc2_exit_hibernation(hsotg, 1, 0, 1);
7824111d5f8SArtur Petrosyan 				if (ret)
7834111d5f8SArtur Petrosyan 					dev_err(hsotg->dev,
7844111d5f8SArtur Petrosyan 						"exit hibernation failed.\n");
78565c9c4c6SVardan Mikayelyan 			}
78665c9c4c6SVardan Mikayelyan 		}
78724d209dbSArtur Petrosyan 	} else if ((gpwrdn & GPWRDN_RST_DET) &&
78824d209dbSArtur Petrosyan 		   (gpwrdn & GPWRDN_RST_DET_MSK)) {
78965c9c4c6SVardan Mikayelyan 		dev_dbg(hsotg->dev, "%s: GPWRDN_RST_DET\n", __func__);
7904111d5f8SArtur Petrosyan 		if (!linestate) {
7914111d5f8SArtur Petrosyan 			ret = dwc2_exit_hibernation(hsotg, 0, 1, 0);
7924111d5f8SArtur Petrosyan 			if (ret)
7934111d5f8SArtur Petrosyan 				dev_err(hsotg->dev,
7944111d5f8SArtur Petrosyan 					"exit hibernation failed.\n");
7954111d5f8SArtur Petrosyan 		}
79624d209dbSArtur Petrosyan 	} else if ((gpwrdn & GPWRDN_STS_CHGINT) &&
79724d209dbSArtur Petrosyan 		   (gpwrdn & GPWRDN_STS_CHGINT_MSK)) {
79865c9c4c6SVardan Mikayelyan 		dev_dbg(hsotg->dev, "%s: GPWRDN_STS_CHGINT\n", __func__);
79924d209dbSArtur Petrosyan 		/*
80024d209dbSArtur Petrosyan 		 * As GPWRDN_STS_CHGINT exit from hibernation flow is
80124d209dbSArtur Petrosyan 		 * the same as in GPWRDN_DISCONN_DET flow. Call
80224d209dbSArtur Petrosyan 		 * disconnect detect helper function to exit from
80324d209dbSArtur Petrosyan 		 * hibernation.
80424d209dbSArtur Petrosyan 		 */
80524d209dbSArtur Petrosyan 		dwc_handle_gpwrdn_disc_det(hsotg, gpwrdn);
80665c9c4c6SVardan Mikayelyan 	}
8074111d5f8SArtur Petrosyan 
8084111d5f8SArtur Petrosyan 	return ret;
80965c9c4c6SVardan Mikayelyan }
81065c9c4c6SVardan Mikayelyan 
81165c9c4c6SVardan Mikayelyan /*
812197ba5f4SPaul Zimmerman  * Common interrupt handler
813197ba5f4SPaul Zimmerman  *
814197ba5f4SPaul Zimmerman  * The common interrupts are those that occur in both Host and Device mode.
815197ba5f4SPaul Zimmerman  * This handler handles the following interrupts:
816197ba5f4SPaul Zimmerman  * - Mode Mismatch Interrupt
817197ba5f4SPaul Zimmerman  * - OTG Interrupt
818197ba5f4SPaul Zimmerman  * - Connector ID Status Change Interrupt
819197ba5f4SPaul Zimmerman  * - Disconnect Interrupt
820197ba5f4SPaul Zimmerman  * - Session Request Interrupt
821197ba5f4SPaul Zimmerman  * - Resume / Remote Wakeup Detected Interrupt
822197ba5f4SPaul Zimmerman  * - Suspend Interrupt
823197ba5f4SPaul Zimmerman  */
dwc2_handle_common_intr(int irq,void * dev)824197ba5f4SPaul Zimmerman irqreturn_t dwc2_handle_common_intr(int irq, void *dev)
825197ba5f4SPaul Zimmerman {
826197ba5f4SPaul Zimmerman 	struct dwc2_hsotg *hsotg = dev;
827197ba5f4SPaul Zimmerman 	u32 gintsts;
828197ba5f4SPaul Zimmerman 	irqreturn_t retval = IRQ_NONE;
829197ba5f4SPaul Zimmerman 
830cf54772bSRobert Baldyga 	spin_lock(&hsotg->lock);
831cf54772bSRobert Baldyga 
832197ba5f4SPaul Zimmerman 	if (!dwc2_is_controller_alive(hsotg)) {
833197ba5f4SPaul Zimmerman 		dev_warn(hsotg->dev, "Controller is dead\n");
834197ba5f4SPaul Zimmerman 		goto out;
835197ba5f4SPaul Zimmerman 	}
836197ba5f4SPaul Zimmerman 
837c7c24e7aSArtur Petrosyan 	/* Reading current frame number value in device or host modes. */
838c7c24e7aSArtur Petrosyan 	if (dwc2_is_device_mode(hsotg))
839f25c42b8SGevorg Sahakyan 		hsotg->frame_number = (dwc2_readl(hsotg, DSTS)
840c7c24e7aSArtur Petrosyan 				       & DSTS_SOFFN_MASK) >> DSTS_SOFFN_SHIFT;
841c7c24e7aSArtur Petrosyan 	else
842f25c42b8SGevorg Sahakyan 		hsotg->frame_number = (dwc2_readl(hsotg, HFNUM)
843c7c24e7aSArtur Petrosyan 				       & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
844c7c24e7aSArtur Petrosyan 
845197ba5f4SPaul Zimmerman 	gintsts = dwc2_read_common_intr(hsotg);
846197ba5f4SPaul Zimmerman 	if (gintsts & ~GINTSTS_PRTINT)
847197ba5f4SPaul Zimmerman 		retval = IRQ_HANDLED;
848197ba5f4SPaul Zimmerman 
84965c9c4c6SVardan Mikayelyan 	/* In case of hibernated state gintsts must not work */
85065c9c4c6SVardan Mikayelyan 	if (hsotg->hibernated) {
85165c9c4c6SVardan Mikayelyan 		dwc2_handle_gpwrdn_intr(hsotg);
85265c9c4c6SVardan Mikayelyan 		retval = IRQ_HANDLED;
85365c9c4c6SVardan Mikayelyan 		goto out;
85465c9c4c6SVardan Mikayelyan 	}
85565c9c4c6SVardan Mikayelyan 
856197ba5f4SPaul Zimmerman 	if (gintsts & GINTSTS_MODEMIS)
857197ba5f4SPaul Zimmerman 		dwc2_handle_mode_mismatch_intr(hsotg);
858197ba5f4SPaul Zimmerman 	if (gintsts & GINTSTS_OTGINT)
859197ba5f4SPaul Zimmerman 		dwc2_handle_otg_intr(hsotg);
860197ba5f4SPaul Zimmerman 	if (gintsts & GINTSTS_CONIDSTSCHNG)
861197ba5f4SPaul Zimmerman 		dwc2_handle_conn_id_status_change_intr(hsotg);
862197ba5f4SPaul Zimmerman 	if (gintsts & GINTSTS_DISCONNINT)
863197ba5f4SPaul Zimmerman 		dwc2_handle_disconnect_intr(hsotg);
864197ba5f4SPaul Zimmerman 	if (gintsts & GINTSTS_SESSREQINT)
865197ba5f4SPaul Zimmerman 		dwc2_handle_session_req_intr(hsotg);
866197ba5f4SPaul Zimmerman 	if (gintsts & GINTSTS_WKUPINT)
867197ba5f4SPaul Zimmerman 		dwc2_handle_wakeup_detected_intr(hsotg);
868197ba5f4SPaul Zimmerman 	if (gintsts & GINTSTS_USBSUSP)
869197ba5f4SPaul Zimmerman 		dwc2_handle_usb_suspend_intr(hsotg);
870d2521849SSevak Arakelyan 	if (gintsts & GINTSTS_LPMTRANRCVD)
871d2521849SSevak Arakelyan 		dwc2_handle_lpm_intr(hsotg);
872197ba5f4SPaul Zimmerman 
873197ba5f4SPaul Zimmerman 	if (gintsts & GINTSTS_PRTINT) {
874197ba5f4SPaul Zimmerman 		/*
875197ba5f4SPaul Zimmerman 		 * The port interrupt occurs while in device mode with HPRT0
876197ba5f4SPaul Zimmerman 		 * Port Enable/Disable
877197ba5f4SPaul Zimmerman 		 */
878197ba5f4SPaul Zimmerman 		if (dwc2_is_device_mode(hsotg)) {
879197ba5f4SPaul Zimmerman 			dev_dbg(hsotg->dev,
880197ba5f4SPaul Zimmerman 				" --Port interrupt received in Device mode--\n");
88193571adbSDinh Nguyen 			dwc2_handle_usb_port_intr(hsotg);
88293571adbSDinh Nguyen 			retval = IRQ_HANDLED;
883197ba5f4SPaul Zimmerman 		}
884197ba5f4SPaul Zimmerman 	}
885197ba5f4SPaul Zimmerman 
886197ba5f4SPaul Zimmerman out:
887cf54772bSRobert Baldyga 	spin_unlock(&hsotg->lock);
888197ba5f4SPaul Zimmerman 	return retval;
889197ba5f4SPaul Zimmerman }
890