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)326*3e417f31SMinas Harutyunyan void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg, bool remotewakeup)
327273d576cSSevak Arakelyan {
328273d576cSSevak Arakelyan u32 glpmcfg;
329*3e417f31SMinas Harutyunyan u32 pcgctl;
330*3e417f31SMinas 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)) {
339*3e417f31SMinas Harutyunyan dev_dbg(hsotg->dev, "Exit from L1 state, remotewakeup=%d\n", remotewakeup);
340273d576cSSevak Arakelyan glpmcfg &= ~GLPMCFG_ENBLSLPM;
341*3e417f31SMinas Harutyunyan glpmcfg &= ~GLPMCFG_HIRD_THRES_MASK;
342f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, glpmcfg, GLPMCFG);
343273d576cSSevak Arakelyan
344*3e417f31SMinas Harutyunyan pcgctl = dwc2_readl(hsotg, PCGCTL);
345*3e417f31SMinas Harutyunyan pcgctl &= ~PCGCTL_ENBL_SLEEP_GATING;
346*3e417f31SMinas Harutyunyan dwc2_writel(hsotg, pcgctl, PCGCTL);
347*3e417f31SMinas Harutyunyan
348f25c42b8SGevorg Sahakyan glpmcfg = dwc2_readl(hsotg, GLPMCFG);
349*3e417f31SMinas Harutyunyan if (glpmcfg & GLPMCFG_ENBESL) {
350*3e417f31SMinas Harutyunyan glpmcfg |= GLPMCFG_RSTRSLPSTS;
351*3e417f31SMinas Harutyunyan dwc2_writel(hsotg, glpmcfg, GLPMCFG);
352*3e417f31SMinas Harutyunyan }
353273d576cSSevak Arakelyan
354*3e417f31SMinas Harutyunyan if (remotewakeup) {
355*3e417f31SMinas Harutyunyan if (dwc2_hsotg_wait_bit_set(hsotg, GLPMCFG, GLPMCFG_L1RESUMEOK, 1000)) {
356*3e417f31SMinas Harutyunyan dev_warn(hsotg->dev, "%s: timeout GLPMCFG_L1RESUMEOK\n", __func__);
357*3e417f31SMinas Harutyunyan goto fail;
358273d576cSSevak Arakelyan return;
359273d576cSSevak Arakelyan }
360*3e417f31SMinas Harutyunyan
361*3e417f31SMinas Harutyunyan dctl = dwc2_readl(hsotg, DCTL);
362*3e417f31SMinas Harutyunyan dctl |= DCTL_RMTWKUPSIG;
363*3e417f31SMinas Harutyunyan dwc2_writel(hsotg, dctl, DCTL);
364*3e417f31SMinas Harutyunyan
365*3e417f31SMinas Harutyunyan if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_WKUPINT, 1000)) {
366*3e417f31SMinas Harutyunyan dev_warn(hsotg->dev, "%s: timeout GINTSTS_WKUPINT\n", __func__);
367*3e417f31SMinas Harutyunyan goto fail;
368*3e417f31SMinas Harutyunyan return;
369*3e417f31SMinas Harutyunyan }
370*3e417f31SMinas Harutyunyan }
371*3e417f31SMinas Harutyunyan
372*3e417f31SMinas Harutyunyan glpmcfg = dwc2_readl(hsotg, GLPMCFG);
373*3e417f31SMinas Harutyunyan if (glpmcfg & GLPMCFG_COREL1RES_MASK || glpmcfg & GLPMCFG_SLPSTS ||
374*3e417f31SMinas Harutyunyan glpmcfg & GLPMCFG_L1RESUMEOK) {
375*3e417f31SMinas Harutyunyan goto fail;
376*3e417f31SMinas Harutyunyan return;
377*3e417f31SMinas Harutyunyan }
378*3e417f31SMinas Harutyunyan
379*3e417f31SMinas Harutyunyan /* Inform gadget to exit from L1 */
380*3e417f31SMinas Harutyunyan call_gadget(hsotg, resume);
381*3e417f31SMinas Harutyunyan /* Change to L0 state */
382*3e417f31SMinas Harutyunyan hsotg->lx_state = DWC2_L0;
383*3e417f31SMinas Harutyunyan hsotg->bus_suspended = false;
384*3e417f31SMinas 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) {
410*3e417f31SMinas 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