diff options
author | Paul Zimmerman <Paul.Zimmerman@synopsys.com> | 2014-01-13 16:50:09 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-01-13 17:44:01 -0500 |
commit | 197ba5f406cc29000c70de98eb40d7243b9f9f03 (patch) | |
tree | fed25bc3806e864ad6ff7c2cbd4145189c65f828 /drivers/usb/dwc2/core_intr.c | |
parent | 276d30eab931eae0ce5c5504b8ff4ab4c97b207b (diff) |
Move DWC2 driver out of staging
The DWC2 driver should now be in good enough shape to move out of
staging. I have stress tested it overnight on RPI running mass
storage and Ethernet transfers in parallel, and for several days
on our proprietary PCI-based platform.
Signed-off-by: Paul Zimmerman <paulz@synopsys.com>
Cc: Felipe Balbi <balbi@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/dwc2/core_intr.c')
-rw-r--r-- | drivers/usb/dwc2/core_intr.c | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c new file mode 100644 index 000000000000..8205799e6db3 --- /dev/null +++ b/drivers/usb/dwc2/core_intr.c | |||
@@ -0,0 +1,492 @@ | |||
1 | /* | ||
2 | * core_intr.c - DesignWare HS OTG Controller common interrupt handling | ||
3 | * | ||
4 | * Copyright (C) 2004-2013 Synopsys, Inc. | ||
5 | * | ||
6 | * Redistribution and use in source and binary forms, with or without | ||
7 | * modification, are permitted provided that the following conditions | ||
8 | * are met: | ||
9 | * 1. Redistributions of source code must retain the above copyright | ||
10 | * notice, this list of conditions, and the following disclaimer, | ||
11 | * without modification. | ||
12 | * 2. Redistributions in binary form must reproduce the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer in the | ||
14 | * documentation and/or other materials provided with the distribution. | ||
15 | * 3. The names of the above-listed copyright holders may not be used | ||
16 | * to endorse or promote products derived from this software without | ||
17 | * specific prior written permission. | ||
18 | * | ||
19 | * ALTERNATIVELY, this software may be distributed under the terms of the | ||
20 | * GNU General Public License ("GPL") as published by the Free Software | ||
21 | * Foundation; either version 2 of the License, or (at your option) any | ||
22 | * later version. | ||
23 | * | ||
24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS | ||
25 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | ||
26 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
27 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||
28 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
29 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
30 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
31 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
32 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
33 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
34 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
35 | */ | ||
36 | |||
37 | /* | ||
38 | * This file contains the common interrupt handlers | ||
39 | */ | ||
40 | #include <linux/kernel.h> | ||
41 | #include <linux/module.h> | ||
42 | #include <linux/moduleparam.h> | ||
43 | #include <linux/spinlock.h> | ||
44 | #include <linux/interrupt.h> | ||
45 | #include <linux/dma-mapping.h> | ||
46 | #include <linux/io.h> | ||
47 | #include <linux/slab.h> | ||
48 | #include <linux/usb.h> | ||
49 | |||
50 | #include <linux/usb/hcd.h> | ||
51 | #include <linux/usb/ch11.h> | ||
52 | |||
53 | #include "core.h" | ||
54 | #include "hcd.h" | ||
55 | |||
56 | static const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg) | ||
57 | { | ||
58 | switch (hsotg->op_state) { | ||
59 | case OTG_STATE_A_HOST: | ||
60 | return "a_host"; | ||
61 | case OTG_STATE_A_SUSPEND: | ||
62 | return "a_suspend"; | ||
63 | case OTG_STATE_A_PERIPHERAL: | ||
64 | return "a_peripheral"; | ||
65 | case OTG_STATE_B_PERIPHERAL: | ||
66 | return "b_peripheral"; | ||
67 | case OTG_STATE_B_HOST: | ||
68 | return "b_host"; | ||
69 | default: | ||
70 | return "unknown"; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * dwc2_handle_mode_mismatch_intr() - Logs a mode mismatch warning message | ||
76 | * | ||
77 | * @hsotg: Programming view of DWC_otg controller | ||
78 | */ | ||
79 | static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg) | ||
80 | { | ||
81 | dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n", | ||
82 | dwc2_is_host_mode(hsotg) ? "Host" : "Device"); | ||
83 | |||
84 | /* Clear interrupt */ | ||
85 | writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS); | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * dwc2_handle_otg_intr() - Handles the OTG Interrupts. It reads the OTG | ||
90 | * Interrupt Register (GOTGINT) to determine what interrupt has occurred. | ||
91 | * | ||
92 | * @hsotg: Programming view of DWC_otg controller | ||
93 | */ | ||
94 | static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) | ||
95 | { | ||
96 | u32 gotgint; | ||
97 | u32 gotgctl; | ||
98 | u32 gintmsk; | ||
99 | |||
100 | gotgint = readl(hsotg->regs + GOTGINT); | ||
101 | gotgctl = readl(hsotg->regs + GOTGCTL); | ||
102 | dev_dbg(hsotg->dev, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint, | ||
103 | dwc2_op_state_str(hsotg)); | ||
104 | |||
105 | if (gotgint & GOTGINT_SES_END_DET) { | ||
106 | dev_dbg(hsotg->dev, | ||
107 | " ++OTG Interrupt: Session End Detected++ (%s)\n", | ||
108 | dwc2_op_state_str(hsotg)); | ||
109 | gotgctl = readl(hsotg->regs + GOTGCTL); | ||
110 | |||
111 | if (hsotg->op_state == OTG_STATE_B_HOST) { | ||
112 | hsotg->op_state = OTG_STATE_B_PERIPHERAL; | ||
113 | } else { | ||
114 | /* | ||
115 | * If not B_HOST and Device HNP still set, HNP did | ||
116 | * not succeed! | ||
117 | */ | ||
118 | if (gotgctl & GOTGCTL_DEVHNPEN) { | ||
119 | dev_dbg(hsotg->dev, "Session End Detected\n"); | ||
120 | dev_err(hsotg->dev, | ||
121 | "Device Not Connected/Responding!\n"); | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * If Session End Detected the B-Cable has been | ||
126 | * disconnected | ||
127 | */ | ||
128 | /* Reset to a clean state */ | ||
129 | hsotg->lx_state = DWC2_L0; | ||
130 | } | ||
131 | |||
132 | gotgctl = readl(hsotg->regs + GOTGCTL); | ||
133 | gotgctl &= ~GOTGCTL_DEVHNPEN; | ||
134 | writel(gotgctl, hsotg->regs + GOTGCTL); | ||
135 | } | ||
136 | |||
137 | if (gotgint & GOTGINT_SES_REQ_SUC_STS_CHNG) { | ||
138 | dev_dbg(hsotg->dev, | ||
139 | " ++OTG Interrupt: Session Request Success Status Change++\n"); | ||
140 | gotgctl = readl(hsotg->regs + GOTGCTL); | ||
141 | if (gotgctl & GOTGCTL_SESREQSCS) { | ||
142 | if (hsotg->core_params->phy_type == | ||
143 | DWC2_PHY_TYPE_PARAM_FS | ||
144 | && hsotg->core_params->i2c_enable > 0) { | ||
145 | hsotg->srp_success = 1; | ||
146 | } else { | ||
147 | /* Clear Session Request */ | ||
148 | gotgctl = readl(hsotg->regs + GOTGCTL); | ||
149 | gotgctl &= ~GOTGCTL_SESREQ; | ||
150 | writel(gotgctl, hsotg->regs + GOTGCTL); | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | |||
155 | if (gotgint & GOTGINT_HST_NEG_SUC_STS_CHNG) { | ||
156 | /* | ||
157 | * Print statements during the HNP interrupt handling | ||
158 | * can cause it to fail | ||
159 | */ | ||
160 | gotgctl = readl(hsotg->regs + GOTGCTL); | ||
161 | /* | ||
162 | * WA for 3.00a- HW is not setting cur_mode, even sometimes | ||
163 | * this does not help | ||
164 | */ | ||
165 | if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a) | ||
166 | udelay(100); | ||
167 | if (gotgctl & GOTGCTL_HSTNEGSCS) { | ||
168 | if (dwc2_is_host_mode(hsotg)) { | ||
169 | hsotg->op_state = OTG_STATE_B_HOST; | ||
170 | /* | ||
171 | * Need to disable SOF interrupt immediately. | ||
172 | * When switching from device to host, the PCD | ||
173 | * interrupt handler won't handle the interrupt | ||
174 | * if host mode is already set. The HCD | ||
175 | * interrupt handler won't get called if the | ||
176 | * HCD state is HALT. This means that the | ||
177 | * interrupt does not get handled and Linux | ||
178 | * complains loudly. | ||
179 | */ | ||
180 | gintmsk = readl(hsotg->regs + GINTMSK); | ||
181 | gintmsk &= ~GINTSTS_SOF; | ||
182 | writel(gintmsk, hsotg->regs + GINTMSK); | ||
183 | |||
184 | /* | ||
185 | * Call callback function with spin lock | ||
186 | * released | ||
187 | */ | ||
188 | spin_unlock(&hsotg->lock); | ||
189 | |||
190 | /* Initialize the Core for Host mode */ | ||
191 | dwc2_hcd_start(hsotg); | ||
192 | spin_lock(&hsotg->lock); | ||
193 | hsotg->op_state = OTG_STATE_B_HOST; | ||
194 | } | ||
195 | } else { | ||
196 | gotgctl = readl(hsotg->regs + GOTGCTL); | ||
197 | gotgctl &= ~(GOTGCTL_HNPREQ | GOTGCTL_DEVHNPEN); | ||
198 | writel(gotgctl, hsotg->regs + GOTGCTL); | ||
199 | dev_dbg(hsotg->dev, "HNP Failed\n"); | ||
200 | dev_err(hsotg->dev, | ||
201 | "Device Not Connected/Responding\n"); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | if (gotgint & GOTGINT_HST_NEG_DET) { | ||
206 | /* | ||
207 | * The disconnect interrupt is set at the same time as | ||
208 | * Host Negotiation Detected. During the mode switch all | ||
209 | * interrupts are cleared so the disconnect interrupt | ||
210 | * handler will not get executed. | ||
211 | */ | ||
212 | dev_dbg(hsotg->dev, | ||
213 | " ++OTG Interrupt: Host Negotiation Detected++ (%s)\n", | ||
214 | (dwc2_is_host_mode(hsotg) ? "Host" : "Device")); | ||
215 | if (dwc2_is_device_mode(hsotg)) { | ||
216 | dev_dbg(hsotg->dev, "a_suspend->a_peripheral (%d)\n", | ||
217 | hsotg->op_state); | ||
218 | spin_unlock(&hsotg->lock); | ||
219 | dwc2_hcd_disconnect(hsotg); | ||
220 | spin_lock(&hsotg->lock); | ||
221 | hsotg->op_state = OTG_STATE_A_PERIPHERAL; | ||
222 | } else { | ||
223 | /* Need to disable SOF interrupt immediately */ | ||
224 | gintmsk = readl(hsotg->regs + GINTMSK); | ||
225 | gintmsk &= ~GINTSTS_SOF; | ||
226 | writel(gintmsk, hsotg->regs + GINTMSK); | ||
227 | spin_unlock(&hsotg->lock); | ||
228 | dwc2_hcd_start(hsotg); | ||
229 | spin_lock(&hsotg->lock); | ||
230 | hsotg->op_state = OTG_STATE_A_HOST; | ||
231 | } | ||
232 | } | ||
233 | |||
234 | if (gotgint & GOTGINT_A_DEV_TOUT_CHG) | ||
235 | dev_dbg(hsotg->dev, | ||
236 | " ++OTG Interrupt: A-Device Timeout Change++\n"); | ||
237 | if (gotgint & GOTGINT_DBNCE_DONE) | ||
238 | dev_dbg(hsotg->dev, " ++OTG Interrupt: Debounce Done++\n"); | ||
239 | |||
240 | /* Clear GOTGINT */ | ||
241 | writel(gotgint, hsotg->regs + GOTGINT); | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * dwc2_handle_conn_id_status_change_intr() - Handles the Connector ID Status | ||
246 | * Change Interrupt | ||
247 | * | ||
248 | * @hsotg: Programming view of DWC_otg controller | ||
249 | * | ||
250 | * Reads the OTG Interrupt Register (GOTCTL) to determine whether this is a | ||
251 | * Device to Host Mode transition or a Host to Device Mode transition. This only | ||
252 | * occurs when the cable is connected/removed from the PHY connector. | ||
253 | */ | ||
254 | static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg) | ||
255 | { | ||
256 | u32 gintmsk = readl(hsotg->regs + GINTMSK); | ||
257 | |||
258 | /* Need to disable SOF interrupt immediately */ | ||
259 | gintmsk &= ~GINTSTS_SOF; | ||
260 | writel(gintmsk, hsotg->regs + GINTMSK); | ||
261 | |||
262 | dev_dbg(hsotg->dev, " ++Connector ID Status Change Interrupt++ (%s)\n", | ||
263 | dwc2_is_host_mode(hsotg) ? "Host" : "Device"); | ||
264 | |||
265 | /* | ||
266 | * Need to schedule a work, as there are possible DELAY function calls. | ||
267 | * Release lock before scheduling workq as it holds spinlock during | ||
268 | * scheduling. | ||
269 | */ | ||
270 | spin_unlock(&hsotg->lock); | ||
271 | queue_work(hsotg->wq_otg, &hsotg->wf_otg); | ||
272 | spin_lock(&hsotg->lock); | ||
273 | |||
274 | /* Clear interrupt */ | ||
275 | writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS); | ||
276 | } | ||
277 | |||
278 | /** | ||
279 | * dwc2_handle_session_req_intr() - This interrupt indicates that a device is | ||
280 | * initiating the Session Request Protocol to request the host to turn on bus | ||
281 | * power so a new session can begin | ||
282 | * | ||
283 | * @hsotg: Programming view of DWC_otg controller | ||
284 | * | ||
285 | * This handler responds by turning on bus power. If the DWC_otg controller is | ||
286 | * in low power mode, this handler brings the controller out of low power mode | ||
287 | * before turning on bus power. | ||
288 | */ | ||
289 | static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) | ||
290 | { | ||
291 | dev_dbg(hsotg->dev, "++Session Request Interrupt++\n"); | ||
292 | |||
293 | /* Clear interrupt */ | ||
294 | writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS); | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | * This interrupt indicates that the DWC_otg controller has detected a | ||
299 | * resume or remote wakeup sequence. If the DWC_otg controller is in | ||
300 | * low power mode, the handler must brings the controller out of low | ||
301 | * power mode. The controller automatically begins resume signaling. | ||
302 | * The handler schedules a time to stop resume signaling. | ||
303 | */ | ||
304 | static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) | ||
305 | { | ||
306 | dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n"); | ||
307 | dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state); | ||
308 | |||
309 | if (dwc2_is_device_mode(hsotg)) { | ||
310 | dev_dbg(hsotg->dev, "DSTS=0x%0x\n", readl(hsotg->regs + DSTS)); | ||
311 | if (hsotg->lx_state == DWC2_L2) { | ||
312 | u32 dctl = readl(hsotg->regs + DCTL); | ||
313 | |||
314 | /* Clear Remote Wakeup Signaling */ | ||
315 | dctl &= ~DCTL_RMTWKUPSIG; | ||
316 | writel(dctl, hsotg->regs + DCTL); | ||
317 | } | ||
318 | /* Change to L0 state */ | ||
319 | hsotg->lx_state = DWC2_L0; | ||
320 | } else { | ||
321 | if (hsotg->lx_state != DWC2_L1) { | ||
322 | u32 pcgcctl = readl(hsotg->regs + PCGCTL); | ||
323 | |||
324 | /* Restart the Phy Clock */ | ||
325 | pcgcctl &= ~PCGCTL_STOPPCLK; | ||
326 | writel(pcgcctl, hsotg->regs + PCGCTL); | ||
327 | mod_timer(&hsotg->wkp_timer, | ||
328 | jiffies + msecs_to_jiffies(71)); | ||
329 | } else { | ||
330 | /* Change to L0 state */ | ||
331 | hsotg->lx_state = DWC2_L0; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | /* Clear interrupt */ | ||
336 | writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS); | ||
337 | } | ||
338 | |||
339 | /* | ||
340 | * This interrupt indicates that a device has been disconnected from the | ||
341 | * root port | ||
342 | */ | ||
343 | static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg) | ||
344 | { | ||
345 | dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n", | ||
346 | dwc2_is_host_mode(hsotg) ? "Host" : "Device", | ||
347 | dwc2_op_state_str(hsotg)); | ||
348 | |||
349 | /* Change to L3 (OFF) state */ | ||
350 | hsotg->lx_state = DWC2_L3; | ||
351 | |||
352 | writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS); | ||
353 | } | ||
354 | |||
355 | /* | ||
356 | * This interrupt indicates that SUSPEND state has been detected on the USB. | ||
357 | * | ||
358 | * For HNP the USB Suspend interrupt signals the change from "a_peripheral" | ||
359 | * to "a_host". | ||
360 | * | ||
361 | * When power management is enabled the core will be put in low power mode. | ||
362 | */ | ||
363 | static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) | ||
364 | { | ||
365 | u32 dsts; | ||
366 | |||
367 | dev_dbg(hsotg->dev, "USB SUSPEND\n"); | ||
368 | |||
369 | if (dwc2_is_device_mode(hsotg)) { | ||
370 | /* | ||
371 | * Check the Device status register to determine if the Suspend | ||
372 | * state is active | ||
373 | */ | ||
374 | dsts = readl(hsotg->regs + DSTS); | ||
375 | dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dsts); | ||
376 | dev_dbg(hsotg->dev, | ||
377 | "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n", | ||
378 | !!(dsts & DSTS_SUSPSTS), | ||
379 | hsotg->hw_params.power_optimized); | ||
380 | } else { | ||
381 | if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) { | ||
382 | dev_dbg(hsotg->dev, "a_peripheral->a_host\n"); | ||
383 | |||
384 | /* Clear the a_peripheral flag, back to a_host */ | ||
385 | spin_unlock(&hsotg->lock); | ||
386 | dwc2_hcd_start(hsotg); | ||
387 | spin_lock(&hsotg->lock); | ||
388 | hsotg->op_state = OTG_STATE_A_HOST; | ||
389 | } | ||
390 | } | ||
391 | |||
392 | /* Change to L2 (suspend) state */ | ||
393 | hsotg->lx_state = DWC2_L2; | ||
394 | |||
395 | /* Clear interrupt */ | ||
396 | writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS); | ||
397 | } | ||
398 | |||
399 | #define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT | \ | ||
400 | GINTSTS_CONIDSTSCHNG | GINTSTS_OTGINT | \ | ||
401 | GINTSTS_MODEMIS | GINTSTS_DISCONNINT | \ | ||
402 | GINTSTS_USBSUSP | GINTSTS_PRTINT) | ||
403 | |||
404 | /* | ||
405 | * This function returns the Core Interrupt register | ||
406 | */ | ||
407 | static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg) | ||
408 | { | ||
409 | u32 gintsts; | ||
410 | u32 gintmsk; | ||
411 | u32 gahbcfg; | ||
412 | u32 gintmsk_common = GINTMSK_COMMON; | ||
413 | |||
414 | gintsts = readl(hsotg->regs + GINTSTS); | ||
415 | gintmsk = readl(hsotg->regs + GINTMSK); | ||
416 | gahbcfg = readl(hsotg->regs + GAHBCFG); | ||
417 | |||
418 | /* If any common interrupts set */ | ||
419 | if (gintsts & gintmsk_common) | ||
420 | dev_dbg(hsotg->dev, "gintsts=%08x gintmsk=%08x\n", | ||
421 | gintsts, gintmsk); | ||
422 | |||
423 | if (gahbcfg & GAHBCFG_GLBL_INTR_EN) | ||
424 | return gintsts & gintmsk & gintmsk_common; | ||
425 | else | ||
426 | return 0; | ||
427 | } | ||
428 | |||
429 | /* | ||
430 | * Common interrupt handler | ||
431 | * | ||
432 | * The common interrupts are those that occur in both Host and Device mode. | ||
433 | * This handler handles the following interrupts: | ||
434 | * - Mode Mismatch Interrupt | ||
435 | * - OTG Interrupt | ||
436 | * - Connector ID Status Change Interrupt | ||
437 | * - Disconnect Interrupt | ||
438 | * - Session Request Interrupt | ||
439 | * - Resume / Remote Wakeup Detected Interrupt | ||
440 | * - Suspend Interrupt | ||
441 | */ | ||
442 | irqreturn_t dwc2_handle_common_intr(int irq, void *dev) | ||
443 | { | ||
444 | struct dwc2_hsotg *hsotg = dev; | ||
445 | u32 gintsts; | ||
446 | irqreturn_t retval = IRQ_NONE; | ||
447 | |||
448 | if (!dwc2_is_controller_alive(hsotg)) { | ||
449 | dev_warn(hsotg->dev, "Controller is dead\n"); | ||
450 | goto out; | ||
451 | } | ||
452 | |||
453 | spin_lock(&hsotg->lock); | ||
454 | |||
455 | gintsts = dwc2_read_common_intr(hsotg); | ||
456 | if (gintsts & ~GINTSTS_PRTINT) | ||
457 | retval = IRQ_HANDLED; | ||
458 | |||
459 | if (gintsts & GINTSTS_MODEMIS) | ||
460 | dwc2_handle_mode_mismatch_intr(hsotg); | ||
461 | if (gintsts & GINTSTS_OTGINT) | ||
462 | dwc2_handle_otg_intr(hsotg); | ||
463 | if (gintsts & GINTSTS_CONIDSTSCHNG) | ||
464 | dwc2_handle_conn_id_status_change_intr(hsotg); | ||
465 | if (gintsts & GINTSTS_DISCONNINT) | ||
466 | dwc2_handle_disconnect_intr(hsotg); | ||
467 | if (gintsts & GINTSTS_SESSREQINT) | ||
468 | dwc2_handle_session_req_intr(hsotg); | ||
469 | if (gintsts & GINTSTS_WKUPINT) | ||
470 | dwc2_handle_wakeup_detected_intr(hsotg); | ||
471 | if (gintsts & GINTSTS_USBSUSP) | ||
472 | dwc2_handle_usb_suspend_intr(hsotg); | ||
473 | |||
474 | if (gintsts & GINTSTS_PRTINT) { | ||
475 | /* | ||
476 | * The port interrupt occurs while in device mode with HPRT0 | ||
477 | * Port Enable/Disable | ||
478 | */ | ||
479 | if (dwc2_is_device_mode(hsotg)) { | ||
480 | dev_dbg(hsotg->dev, | ||
481 | " --Port interrupt received in Device mode--\n"); | ||
482 | gintsts = GINTSTS_PRTINT; | ||
483 | writel(gintsts, hsotg->regs + GINTSTS); | ||
484 | retval = 1; | ||
485 | } | ||
486 | } | ||
487 | |||
488 | spin_unlock(&hsotg->lock); | ||
489 | out: | ||
490 | return retval; | ||
491 | } | ||
492 | EXPORT_SYMBOL_GPL(dwc2_handle_common_intr); | ||