xref: /netbsd-src/sys/external/bsd/dwc2/dist/dwc2_coreintr.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: dwc2_coreintr.c,v 1.5 2013/10/05 06:51:43 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.5 2013/10/05 06:51:43 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 #ifdef DEBUG
71 	switch (hsotg->op_state) {
72 	case OTG_STATE_A_HOST:
73 		return "a_host";
74 	case OTG_STATE_A_SUSPEND:
75 		return "a_suspend";
76 	case OTG_STATE_A_PERIPHERAL:
77 		return "a_peripheral";
78 	case OTG_STATE_B_PERIPHERAL:
79 		return "b_peripheral";
80 	case OTG_STATE_B_HOST:
81 		return "b_host";
82 	default:
83 		return "unknown";
84 	}
85 #else
86 	return "";
87 #endif
88 }
89 #endif
90 
91 /**
92  * dwc2_handle_mode_mismatch_intr() - Logs a mode mismatch warning message
93  *
94  * @hsotg: Programming view of DWC_otg controller
95  */
96 static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg)
97 {
98 	dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
99 		 dwc2_is_host_mode(hsotg) ? "Host" : "Device");
100 
101 	/* Clear interrupt */
102 	DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_MODEMIS);
103 }
104 
105 /**
106  * dwc2_handle_otg_intr() - Handles the OTG Interrupts. It reads the OTG
107  * Interrupt Register (GOTGINT) to determine what interrupt has occurred.
108  *
109  * @hsotg: Programming view of DWC_otg controller
110  */
111 static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
112 {
113 	u32 gotgint;
114 	u32 gotgctl;
115 	u32 gintmsk;
116 
117 	gotgint = DWC2_READ_4(hsotg, GOTGINT);
118 	gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
119 	dev_dbg(hsotg->dev, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint,
120 		dwc2_op_state_str(hsotg));
121 
122 	if (gotgint & GOTGINT_SES_END_DET) {
123 		dev_dbg(hsotg->dev,
124 			" ++OTG Interrupt: Session End Detected++ (%s)\n",
125 			dwc2_op_state_str(hsotg));
126 		gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
127 
128 		if (hsotg->op_state == OTG_STATE_B_HOST) {
129 			hsotg->op_state = OTG_STATE_B_PERIPHERAL;
130 		} else {
131 			/*
132 			 * If not B_HOST and Device HNP still set, HNP did
133 			 * not succeed!
134 			 */
135 			if (gotgctl & GOTGCTL_DEVHNPEN) {
136 				dev_dbg(hsotg->dev, "Session End Detected\n");
137 				dev_err(hsotg->dev,
138 					"Device Not Connected/Responding!\n");
139 			}
140 
141 			/*
142 			 * If Session End Detected the B-Cable has been
143 			 * disconnected
144 			 */
145 			/* Reset to a clean state */
146 			hsotg->lx_state = DWC2_L0;
147 		}
148 
149 		gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
150 		gotgctl &= ~GOTGCTL_DEVHNPEN;
151 		DWC2_WRITE_4(hsotg, GOTGCTL, gotgctl);
152 	}
153 
154 	if (gotgint & GOTGINT_SES_REQ_SUC_STS_CHNG) {
155 		dev_dbg(hsotg->dev,
156 			" ++OTG Interrupt: Session Request Success Status Change++\n");
157 		gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
158 		if (gotgctl & GOTGCTL_SESREQSCS) {
159 			if (hsotg->core_params->phy_type ==
160 					DWC2_PHY_TYPE_PARAM_FS
161 			    && hsotg->core_params->i2c_enable > 0) {
162 				hsotg->srp_success = 1;
163 			} else {
164 				/* Clear Session Request */
165 				gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
166 				gotgctl &= ~GOTGCTL_SESREQ;
167 				DWC2_WRITE_4(hsotg, GOTGCTL, gotgctl);
168 			}
169 		}
170 	}
171 
172 	if (gotgint & GOTGINT_HST_NEG_SUC_STS_CHNG) {
173 		/*
174 		 * Print statements during the HNP interrupt handling
175 		 * can cause it to fail
176 		 */
177 		gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
178 		/*
179 		 * WA for 3.00a- HW is not setting cur_mode, even sometimes
180 		 * this does not help
181 		 */
182 		if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a)
183 			udelay(100);
184 		if (gotgctl & GOTGCTL_HSTNEGSCS) {
185 			if (dwc2_is_host_mode(hsotg)) {
186 				hsotg->op_state = OTG_STATE_B_HOST;
187 				/*
188 				 * Need to disable SOF interrupt immediately.
189 				 * When switching from device to host, the PCD
190 				 * interrupt handler won't handle the interrupt
191 				 * if host mode is already set. The HCD
192 				 * interrupt handler won't get called if the
193 				 * HCD state is HALT. This means that the
194 				 * interrupt does not get handled and Linux
195 				 * complains loudly.
196 				 */
197 				gintmsk = DWC2_READ_4(hsotg, GINTMSK);
198 				gintmsk &= ~GINTSTS_SOF;
199 				DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
200 
201 				/*
202 				 * Call callback function with spin lock
203 				 * released
204 				 */
205 				spin_unlock(&hsotg->lock);
206 
207 				/* Initialize the Core for Host mode */
208 				dwc2_hcd_start(hsotg);
209 				spin_lock(&hsotg->lock);
210 				hsotg->op_state = OTG_STATE_B_HOST;
211 			}
212 		} else {
213 			gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
214 			gotgctl &= ~(GOTGCTL_HNPREQ | GOTGCTL_DEVHNPEN);
215 			DWC2_WRITE_4(hsotg, GOTGCTL, gotgctl);
216 			dev_dbg(hsotg->dev, "HNP Failed\n");
217 			dev_err(hsotg->dev,
218 				"Device Not Connected/Responding\n");
219 		}
220 	}
221 
222 	if (gotgint & GOTGINT_HST_NEG_DET) {
223 		/*
224 		 * The disconnect interrupt is set at the same time as
225 		 * Host Negotiation Detected. During the mode switch all
226 		 * interrupts are cleared so the disconnect interrupt
227 		 * handler will not get executed.
228 		 */
229 		dev_dbg(hsotg->dev,
230 			" ++OTG Interrupt: Host Negotiation Detected++ (%s)\n",
231 			(dwc2_is_host_mode(hsotg) ? "Host" : "Device"));
232 		if (dwc2_is_device_mode(hsotg)) {
233 			dev_dbg(hsotg->dev, "a_suspend->a_peripheral (%d)\n",
234 				hsotg->op_state);
235 			spin_unlock(&hsotg->lock);
236 			dwc2_hcd_disconnect(hsotg);
237 			spin_lock(&hsotg->lock);
238 			hsotg->op_state = OTG_STATE_A_PERIPHERAL;
239 		} else {
240 			/* Need to disable SOF interrupt immediately */
241 			gintmsk = DWC2_READ_4(hsotg, GINTMSK);
242 			gintmsk &= ~GINTSTS_SOF;
243 			DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
244 			spin_unlock(&hsotg->lock);
245 			dwc2_hcd_start(hsotg);
246 			spin_lock(&hsotg->lock);
247 			hsotg->op_state = OTG_STATE_A_HOST;
248 		}
249 	}
250 
251 	if (gotgint & GOTGINT_A_DEV_TOUT_CHG)
252 		dev_dbg(hsotg->dev,
253 			" ++OTG Interrupt: A-Device Timeout Change++\n");
254 	if (gotgint & GOTGINT_DBNCE_DONE)
255 		dev_dbg(hsotg->dev, " ++OTG Interrupt: Debounce Done++\n");
256 
257 	/* Clear GOTGINT */
258 	DWC2_WRITE_4(hsotg, GOTGINT, gotgint);
259 }
260 
261 /**
262  * dwc2_handle_conn_id_status_change_intr() - Handles the Connector ID Status
263  * Change Interrupt
264  *
265  * @hsotg: Programming view of DWC_otg controller
266  *
267  * Reads the OTG Interrupt Register (GOTCTL) to determine whether this is a
268  * Device to Host Mode transition or a Host to Device Mode transition. This only
269  * occurs when the cable is connected/removed from the PHY connector.
270  */
271 static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
272 {
273 	u32 gintmsk = DWC2_READ_4(hsotg, GINTMSK);
274 
275 	/* Need to disable SOF interrupt immediately */
276 	gintmsk &= ~GINTSTS_SOF;
277 	DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
278 
279 	dev_dbg(hsotg->dev, " ++Connector ID Status Change Interrupt++  (%s)\n",
280 		dwc2_is_host_mode(hsotg) ? "Host" : "Device");
281 
282 	/*
283 	 * Need to schedule a work, as there are possible DELAY function calls.
284 	 * Release lock before scheduling workq as it holds spinlock during
285 	 * scheduling.
286 	 */
287 	spin_unlock(&hsotg->lock);
288 	workqueue_enqueue(hsotg->wq_otg, &hsotg->wf_otg, NULL);
289 	spin_lock(&hsotg->lock);
290 
291 	/* Clear interrupt */
292 	DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_CONIDSTSCHNG);
293 }
294 
295 /**
296  * dwc2_handle_session_req_intr() - This interrupt indicates that a device is
297  * initiating the Session Request Protocol to request the host to turn on bus
298  * power so a new session can begin
299  *
300  * @hsotg: Programming view of DWC_otg controller
301  *
302  * This handler responds by turning on bus power. If the DWC_otg controller is
303  * in low power mode, this handler brings the controller out of low power mode
304  * before turning on bus power.
305  */
306 static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
307 {
308 	dev_dbg(hsotg->dev, "++Session Request Interrupt++\n");
309 
310 	/* Clear interrupt */
311 	DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_SESSREQINT);
312 }
313 
314 /*
315  * This interrupt indicates that the DWC_otg controller has detected a
316  * resume or remote wakeup sequence. If the DWC_otg controller is in
317  * low power mode, the handler must brings the controller out of low
318  * power mode. The controller automatically begins resume signaling.
319  * The handler schedules a time to stop resume signaling.
320  */
321 static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
322 {
323 	dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
324 	dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
325 
326 	if (dwc2_is_device_mode(hsotg)) {
327 		dev_dbg(hsotg->dev, "DSTS=0x%0x\n", DWC2_READ_4(hsotg, DSTS));
328 		if (hsotg->lx_state == DWC2_L2) {
329 			u32 dctl = DWC2_READ_4(hsotg, DCTL);
330 
331 			/* Clear Remote Wakeup Signaling */
332 			dctl &= ~DCTL_RMTWKUPSIG;
333 			DWC2_WRITE_4(hsotg, DCTL, dctl);
334 		}
335 		/* Change to L0 state */
336 		hsotg->lx_state = DWC2_L0;
337 	} else {
338 		if (hsotg->lx_state != DWC2_L1) {
339 			u32 pcgcctl = DWC2_READ_4(hsotg, PCGCTL);
340 
341 			/* Restart the Phy Clock */
342 			pcgcctl &= ~PCGCTL_STOPPCLK;
343 			DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
344 			callout_reset(&hsotg->wkp_timer, mstohz(71),
345 			    dwc2_wakeup_detected, hsotg);
346 		} else {
347 			/* Change to L0 state */
348 			hsotg->lx_state = DWC2_L0;
349 		}
350 	}
351 
352 	/* Clear interrupt */
353 	DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_WKUPINT);
354 }
355 
356 /*
357  * This interrupt indicates that a device has been disconnected from the
358  * root port
359  */
360 static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
361 {
362 	dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n",
363 		dwc2_is_host_mode(hsotg) ? "Host" : "Device",
364 		dwc2_op_state_str(hsotg));
365 
366 	/* Change to L3 (OFF) state */
367 	hsotg->lx_state = DWC2_L3;
368 
369 	DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_DISCONNINT);
370 }
371 
372 /*
373  * This interrupt indicates that SUSPEND state has been detected on the USB.
374  *
375  * For HNP the USB Suspend interrupt signals the change from "a_peripheral"
376  * to "a_host".
377  *
378  * When power management is enabled the core will be put in low power mode.
379  */
380 static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
381 {
382 	u32 dsts;
383 
384 	dev_dbg(hsotg->dev, "USB SUSPEND\n");
385 
386 	if (dwc2_is_device_mode(hsotg)) {
387 		/*
388 		 * Check the Device status register to determine if the Suspend
389 		 * state is active
390 		 */
391 		dsts = DWC2_READ_4(hsotg, DSTS);
392 		dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dsts);
393 		dev_dbg(hsotg->dev,
394 			"DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n",
395 			!!(dsts & DSTS_SUSPSTS),
396 			hsotg->hw_params.power_optimized);
397 	} else {
398 		if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) {
399 			dev_dbg(hsotg->dev, "a_peripheral->a_host\n");
400 
401 			/* Clear the a_peripheral flag, back to a_host */
402 			spin_unlock(&hsotg->lock);
403 			dwc2_hcd_start(hsotg);
404 			spin_lock(&hsotg->lock);
405 			hsotg->op_state = OTG_STATE_A_HOST;
406 		}
407 	}
408 
409 	/* Change to L2 (suspend) state */
410 	hsotg->lx_state = DWC2_L2;
411 
412 	/* Clear interrupt */
413 	DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_USBSUSP);
414 }
415 
416 #define GINTMSK_COMMON	(GINTSTS_WKUPINT | GINTSTS_SESSREQINT |		\
417 			 GINTSTS_CONIDSTSCHNG | GINTSTS_OTGINT |	\
418 			 GINTSTS_MODEMIS | GINTSTS_DISCONNINT |		\
419 			 GINTSTS_USBSUSP | GINTSTS_PRTINT)
420 
421 /*
422  * This function returns the Core Interrupt register
423  */
424 static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg)
425 {
426 	u32 gintsts;
427 	u32 gintmsk;
428 	u32 gahbcfg;
429 	u32 gintmsk_common = GINTMSK_COMMON;
430 
431 	gintsts = DWC2_READ_4(hsotg, GINTSTS);
432 	gintmsk = DWC2_READ_4(hsotg, GINTMSK);
433 	gahbcfg = DWC2_READ_4(hsotg, GAHBCFG);
434 
435 #ifdef DEBUG
436 	/* If any common interrupts set */
437 	if (gintsts & gintmsk_common)
438 		dev_dbg(hsotg->dev, "gintsts=%08x  gintmsk=%08x\n",
439 			gintsts, gintmsk);
440 #endif
441 
442 	if (gahbcfg & GAHBCFG_GLBL_INTR_EN)
443 		return gintsts & gintmsk & gintmsk_common;
444 	else
445 		return 0;
446 }
447 
448 /*
449  * Common interrupt handler
450  *
451  * The common interrupts are those that occur in both Host and Device mode.
452  * This handler handles the following interrupts:
453  * - Mode Mismatch Interrupt
454  * - OTG Interrupt
455  * - Connector ID Status Change Interrupt
456  * - Disconnect Interrupt
457  * - Session Request Interrupt
458  * - Resume / Remote Wakeup Detected Interrupt
459  * - Suspend Interrupt
460  */
461 irqreturn_t dwc2_handle_common_intr(void *dev)
462 {
463 	struct dwc2_hsotg *hsotg = dev;
464 	u32 gintsts;
465 	irqreturn_t retval = IRQ_NONE;
466 
467 	if (dwc2_check_core_status(hsotg) < 0) {
468 		dev_warn(hsotg->dev, "Controller is disconnected\n");
469 		goto out;
470 	}
471 
472 	KASSERT(mutex_owned(&hsotg->lock));
473 
474 	gintsts = dwc2_read_common_intr(hsotg);
475 	if (gintsts & ~GINTSTS_PRTINT)
476 		retval = IRQ_HANDLED;
477 
478 	if (gintsts & GINTSTS_MODEMIS)
479 		dwc2_handle_mode_mismatch_intr(hsotg);
480 	if (gintsts & GINTSTS_OTGINT)
481 		dwc2_handle_otg_intr(hsotg);
482 	if (gintsts & GINTSTS_CONIDSTSCHNG)
483 		dwc2_handle_conn_id_status_change_intr(hsotg);
484 	if (gintsts & GINTSTS_DISCONNINT)
485 		dwc2_handle_disconnect_intr(hsotg);
486 	if (gintsts & GINTSTS_SESSREQINT)
487 		dwc2_handle_session_req_intr(hsotg);
488 	if (gintsts & GINTSTS_WKUPINT)
489 		dwc2_handle_wakeup_detected_intr(hsotg);
490 	if (gintsts & GINTSTS_USBSUSP)
491 		dwc2_handle_usb_suspend_intr(hsotg);
492 
493 	if (gintsts & GINTSTS_PRTINT) {
494 		/*
495 		 * The port interrupt occurs while in device mode with HPRT0
496 		 * Port Enable/Disable
497 		 */
498 		if (dwc2_is_device_mode(hsotg)) {
499 			dev_dbg(hsotg->dev,
500 				" --Port interrupt received in Device mode--\n");
501 			gintsts = GINTSTS_PRTINT;
502 			DWC2_WRITE_4(hsotg, GINTSTS, gintsts);
503 			retval = IRQ_HANDLED;
504 		}
505 	}
506 
507 out:
508 	return retval;
509 }
510