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