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