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