dwc2_coreintr.c revision 1.7
1/*	$NetBSD: dwc2_coreintr.c,v 1.7 2014/04/03 06:34:58 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.7 2014/04/03 06:34:58 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
68static 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
86/**
87 * dwc2_handle_usb_port_intr - handles OTG PRTINT interrupts.
88 * When the PRTINT interrupt fires, there are certain status bits in the Host
89 * Port that needs to get cleared.
90 *
91 * @hsotg: Programming view of DWC_otg controller
92 */
93static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
94{
95	u32 hprt0 = DWC2_READ_4(hsotg, HPRT0);
96
97	if (hprt0 & HPRT0_ENACHG) {
98		hprt0 &= ~HPRT0_ENA;
99		DWC2_WRITE_4(hsotg, HPRT0, hprt0);
100	}
101
102	/* Clear interrupt */
103	DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_PRTINT);
104}
105#endif
106
107/**
108 * dwc2_handle_mode_mismatch_intr() - Logs a mode mismatch warning message
109 *
110 * @hsotg: Programming view of DWC_otg controller
111 */
112static 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 */
127static 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 */
287static 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 */
322static 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 */
337static 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 */
376static 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 */
396static 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 */
442static 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 */
477irqreturn_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
522out:
523	return retval;
524}
525