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