move files to files-3.7
[lede.git] / target / linux / ramips / files-3.7 / drivers / usb / dwc_otg / dwc_otg_cil_intr.c
1 /* ==========================================================================
2  * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil_intr.c $
3  * $Revision: 1.2 $
4  * $Date: 2008-11-21 05:39:15 $
5  * $Change: 1065567 $
6  *
7  * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
8  * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
9  * otherwise expressly agreed to in writing between Synopsys and you.
10  *
11  * The Software IS NOT an item of Licensed Software or Licensed Product under
12  * any End User Software License Agreement or Agreement for Licensed Product
13  * with Synopsys or any supplement thereto. You are permitted to use and
14  * redistribute this Software in source and binary forms, with or without
15  * modification, provided that redistributions of source code must retain this
16  * notice. You may not view, use, disclose, copy or distribute this file or
17  * any information contained herein except pursuant to this license grant from
18  * Synopsys. If you do not agree with this notice, including the disclaimer
19  * below, then you are not authorized to use the Software.
20  *
21  * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31  * DAMAGE.
32  * ========================================================================== */
33
34 /** @file
35  *
36  * The Core Interface Layer provides basic services for accessing and
37  * managing the DWC_otg hardware. These services are used by both the
38  * Host Controller Driver and the Peripheral Controller Driver.
39  *
40  * This file contains the Common Interrupt handlers.
41  */
42 #include "linux/dwc_otg_plat.h"
43 #include "dwc_otg_regs.h"
44 #include "dwc_otg_cil.h"
45
46 #ifdef DEBUG
47 inline const char *op_state_str(dwc_otg_core_if_t *core_if)
48 {
49         return (core_if->op_state==A_HOST?"a_host":
50                 (core_if->op_state==A_SUSPEND?"a_suspend":
51                  (core_if->op_state==A_PERIPHERAL?"a_peripheral":
52                   (core_if->op_state==B_PERIPHERAL?"b_peripheral":
53                    (core_if->op_state==B_HOST?"b_host":
54                     "unknown")))));
55 }
56 #endif
57
58 /** This function will log a debug message
59  *
60  * @param core_if Programming view of DWC_otg controller.
61  */
62 int32_t dwc_otg_handle_mode_mismatch_intr (dwc_otg_core_if_t *core_if)
63 {
64         gintsts_data_t gintsts;
65         DWC_WARN("Mode Mismatch Interrupt: currently in %s mode\n",
66                  dwc_otg_mode(core_if) ? "Host" : "Device");
67
68         /* Clear interrupt */
69         gintsts.d32 = 0;
70         gintsts.b.modemismatch = 1;
71         dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
72         return 1;
73 }
74
75 /** Start the HCD.  Helper function for using the HCD callbacks.
76  *
77  * @param core_if Programming view of DWC_otg controller.
78  */
79 static inline void hcd_start(dwc_otg_core_if_t *core_if)
80 {
81         if (core_if->hcd_cb && core_if->hcd_cb->start) {
82                 core_if->hcd_cb->start(core_if->hcd_cb->p);
83         }
84 }
85 /** Stop the HCD.  Helper function for using the HCD callbacks.
86  *
87  * @param core_if Programming view of DWC_otg controller.
88  */
89 static inline void hcd_stop(dwc_otg_core_if_t *core_if)
90 {
91         if (core_if->hcd_cb && core_if->hcd_cb->stop) {
92                 core_if->hcd_cb->stop(core_if->hcd_cb->p);
93         }
94 }
95 /** Disconnect the HCD.  Helper function for using the HCD callbacks.
96  *
97  * @param core_if Programming view of DWC_otg controller.
98  */
99 static inline void hcd_disconnect(dwc_otg_core_if_t *core_if)
100 {
101         if (core_if->hcd_cb && core_if->hcd_cb->disconnect) {
102                 core_if->hcd_cb->disconnect(core_if->hcd_cb->p);
103         }
104 }
105 /** Inform the HCD the a New Session has begun.  Helper function for
106  * using the HCD callbacks.
107  *
108  * @param core_if Programming view of DWC_otg controller.
109  */
110 static inline void hcd_session_start(dwc_otg_core_if_t *core_if)
111 {
112         if (core_if->hcd_cb && core_if->hcd_cb->session_start) {
113                 core_if->hcd_cb->session_start(core_if->hcd_cb->p);
114         }
115 }
116
117 /** Start the PCD.  Helper function for using the PCD callbacks.
118  *
119  * @param core_if Programming view of DWC_otg controller.
120  */
121 static inline void pcd_start(dwc_otg_core_if_t *core_if)
122 {
123         if (core_if->pcd_cb && core_if->pcd_cb->start) {
124                 core_if->pcd_cb->start(core_if->pcd_cb->p);
125         }
126 }
127 /** Stop the PCD.  Helper function for using the PCD callbacks.
128  *
129  * @param core_if Programming view of DWC_otg controller.
130  */
131 static inline void pcd_stop(dwc_otg_core_if_t *core_if)
132 {
133         if (core_if->pcd_cb && core_if->pcd_cb->stop) {
134                 core_if->pcd_cb->stop(core_if->pcd_cb->p);
135         }
136 }
137 /** Suspend the PCD.  Helper function for using the PCD callbacks.
138  *
139  * @param core_if Programming view of DWC_otg controller.
140  */
141 static inline void pcd_suspend(dwc_otg_core_if_t *core_if)
142 {
143         if (core_if->pcd_cb && core_if->pcd_cb->suspend) {
144                 core_if->pcd_cb->suspend(core_if->pcd_cb->p);
145         }
146 }
147 /** Resume the PCD.  Helper function for using the PCD callbacks.
148  *
149  * @param core_if Programming view of DWC_otg controller.
150  */
151 static inline void pcd_resume(dwc_otg_core_if_t *core_if)
152 {
153         if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
154                 core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
155         }
156 }
157
158 /**
159  * This function handles the OTG Interrupts. It reads the OTG
160  * Interrupt Register (GOTGINT) to determine what interrupt has
161  * occurred.
162  *
163  * @param core_if Programming view of DWC_otg controller.
164  */
165 int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t *core_if)
166 {
167         dwc_otg_core_global_regs_t *global_regs =
168                 core_if->core_global_regs;
169         gotgint_data_t gotgint;
170         gotgctl_data_t gotgctl;
171         gintmsk_data_t gintmsk;
172
173         gotgint.d32 = dwc_read_reg32(&global_regs->gotgint);
174         gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
175         DWC_DEBUGPL(DBG_CIL, "gotgctl=%08x\n", gotgctl.d32);
176
177         if (gotgint.b.sesenddet) {
178                 DWC_DEBUGPL(DBG_ANY, "OTG Interrupt: "
179                             "Session End Detected++ (%s)\n",
180                             op_state_str(core_if));
181                 gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
182
183                 if (core_if->op_state == B_HOST) {
184                         pcd_start(core_if);
185                         core_if->op_state = B_PERIPHERAL;
186                 } else {
187                         /* If not B_HOST and Device HNP still set. HNP
188                          * Did not succeed!*/
189                         if (gotgctl.b.devhnpen) {
190                                 DWC_DEBUGPL(DBG_ANY, "Session End Detected\n");
191                                 DWC_ERROR("Device Not Connected/Responding!\n");
192                         }
193
194                         /* If Session End Detected the B-Cable has
195                          * been disconnected. */
196                         /* Reset PCD and Gadget driver to a
197                          * clean state. */
198                         pcd_stop(core_if);
199                 }
200                 gotgctl.d32 = 0;
201                 gotgctl.b.devhnpen = 1;
202                 dwc_modify_reg32(&global_regs->gotgctl,
203                                   gotgctl.d32, 0);
204         }
205         if (gotgint.b.sesreqsucstschng) {
206                 DWC_DEBUGPL(DBG_ANY, " OTG Interrupt: "
207                             "Session Reqeust Success Status Change++\n");
208                 gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
209                 if (gotgctl.b.sesreqscs) {
210                         if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) &&
211                             (core_if->core_params->i2c_enable)) {
212                                 core_if->srp_success = 1;
213                         }
214                         else {
215                                 pcd_resume(core_if);
216                                 /* Clear Session Request */
217                                 gotgctl.d32 = 0;
218                                 gotgctl.b.sesreq = 1;
219                                 dwc_modify_reg32(&global_regs->gotgctl,
220                                                   gotgctl.d32, 0);
221                         }
222                 }
223         }
224         if (gotgint.b.hstnegsucstschng) {
225                 /* Print statements during the HNP interrupt handling
226                  * can cause it to fail.*/
227                 gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
228                 if (gotgctl.b.hstnegscs) {
229                         if (dwc_otg_is_host_mode(core_if)) {
230                                 core_if->op_state = B_HOST;
231                                 /*
232                                  * Need to disable SOF interrupt immediately.
233                                  * When switching from device to host, the PCD
234                                  * interrupt handler won't handle the
235                                  * interrupt if host mode is already set. The
236                                  * HCD interrupt handler won't get called if
237                                  * the HCD state is HALT. This means that the
238                                  * interrupt does not get handled and Linux
239                                  * complains loudly.
240                                  */
241                                 gintmsk.d32 = 0;
242                                 gintmsk.b.sofintr = 1;
243                                 dwc_modify_reg32(&global_regs->gintmsk,
244                                                  gintmsk.d32, 0);
245                                 pcd_stop(core_if);
246                                 /*
247                                  * Initialize the Core for Host mode.
248                                  */
249                                 hcd_start(core_if);
250                                 core_if->op_state = B_HOST;
251                         }
252                 } else {
253                         gotgctl.d32 = 0;
254                         gotgctl.b.hnpreq = 1;
255                         gotgctl.b.devhnpen = 1;
256                         dwc_modify_reg32(&global_regs->gotgctl,
257                                           gotgctl.d32, 0);
258                         DWC_DEBUGPL(DBG_ANY, "HNP Failed\n");
259                         DWC_ERROR("Device Not Connected/Responding\n");
260                 }
261         }
262         if (gotgint.b.hstnegdet) {
263                 /* The disconnect interrupt is set at the same time as
264                  * Host Negotiation Detected.  During the mode
265                  * switch all interrupts are cleared so the disconnect
266                  * interrupt handler will not get executed.
267                  */
268                 DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
269                             "Host Negotiation Detected++ (%s)\n",
270                             (dwc_otg_is_host_mode(core_if)?"Host":"Device"));
271                 if (dwc_otg_is_device_mode(core_if)){
272                         DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n", core_if->op_state);
273                         hcd_disconnect(core_if);
274                         pcd_start(core_if);
275                         core_if->op_state = A_PERIPHERAL;
276                 } else {
277                         /*
278                          * Need to disable SOF interrupt immediately. When
279                          * switching from device to host, the PCD interrupt
280                          * handler won't handle the interrupt if host mode is
281                          * already set. The HCD interrupt handler won't get
282                          * called if the HCD state is HALT. This means that
283                          * the interrupt does not get handled and Linux
284                          * complains loudly.
285                          */
286                         gintmsk.d32 = 0;
287                         gintmsk.b.sofintr = 1;
288                         dwc_modify_reg32(&global_regs->gintmsk,
289                                          gintmsk.d32, 0);
290                         pcd_stop(core_if);
291                         hcd_start(core_if);
292                         core_if->op_state = A_HOST;
293                 }
294         }
295         if (gotgint.b.adevtoutchng) {
296                 DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
297                             "A-Device Timeout Change++\n");
298         }
299         if (gotgint.b.debdone) {
300                 DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
301                             "Debounce Done++\n");
302         }
303
304         /* Clear GOTGINT */
305         dwc_write_reg32 (&core_if->core_global_regs->gotgint, gotgint.d32);
306
307         return 1;
308 }
309
310
311 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
312
313 void w_conn_id_status_change(void *p)
314 {
315         dwc_otg_core_if_t *core_if = p;
316
317 #else
318
319 void w_conn_id_status_change(struct work_struct *p)
320 {
321         dwc_otg_core_if_t *core_if = container_of(p, dwc_otg_core_if_t, w_conn_id);
322
323 #endif
324
325
326         uint32_t count = 0;
327         gotgctl_data_t gotgctl = { .d32 = 0 };
328
329         gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl);
330         DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32);
331         DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts);
332
333         /* B-Device connector (Device Mode) */
334         if (gotgctl.b.conidsts) {
335                 /* Wait for switch to device mode. */
336                 while (!dwc_otg_is_device_mode(core_if)){
337                         DWC_PRINT("Waiting for Peripheral Mode, Mode=%s\n",
338                                   (dwc_otg_is_host_mode(core_if)?"Host":"Peripheral"));
339                         MDELAY(100);
340                         if (++count > 10000) *(uint32_t*)NULL=0;
341                 }
342                 core_if->op_state = B_PERIPHERAL;
343                 dwc_otg_core_init(core_if);
344                 dwc_otg_enable_global_interrupts(core_if);
345                 pcd_start(core_if);
346         } else {
347                 /* A-Device connector (Host Mode) */
348                 while (!dwc_otg_is_host_mode(core_if)) {
349                         DWC_PRINT("Waiting for Host Mode, Mode=%s\n",
350                                   (dwc_otg_is_host_mode(core_if)?"Host":"Peripheral"));
351                         MDELAY(100);
352                         if (++count > 10000) *(uint32_t*)NULL=0;
353                 }
354                 core_if->op_state = A_HOST;
355                 /*
356                  * Initialize the Core for Host mode.
357                  */
358                 dwc_otg_core_init(core_if);
359                 dwc_otg_enable_global_interrupts(core_if);
360                 hcd_start(core_if);
361         }
362 }
363
364
365 /**
366  * This function handles the Connector ID Status Change Interrupt.  It
367  * reads the OTG Interrupt Register (GOTCTL) to determine whether this
368  * is a Device to Host Mode transition or a Host Mode to Device
369  * Transition.
370  *
371  * This only occurs when the cable is connected/removed from the PHY
372  * connector.
373  *
374  * @param core_if Programming view of DWC_otg controller.
375  */
376 int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t *core_if)
377 {
378
379         /*
380          * Need to disable SOF interrupt immediately. If switching from device
381          * to host, the PCD interrupt handler won't handle the interrupt if
382          * host mode is already set. The HCD interrupt handler won't get
383          * called if the HCD state is HALT. This means that the interrupt does
384          * not get handled and Linux complains loudly.
385          */
386         gintmsk_data_t gintmsk = { .d32 = 0 };
387         gintsts_data_t gintsts = { .d32 = 0 };
388
389         gintmsk.b.sofintr = 1;
390         dwc_modify_reg32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
391
392         DWC_DEBUGPL(DBG_CIL, " ++Connector ID Status Change Interrupt++  (%s)\n",
393                     (dwc_otg_is_host_mode(core_if)?"Host":"Device"));
394
395         /*
396          * Need to schedule a work, as there are possible DELAY function calls
397         */
398         queue_work(core_if->wq_otg, &core_if->w_conn_id);
399
400         /* Set flag and clear interrupt */
401         gintsts.b.conidstschng = 1;
402         dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
403
404         return 1;
405 }
406
407 /**
408  * This interrupt indicates that a device is initiating the Session
409  * Request Protocol to request the host to turn on bus power so a new
410  * session can begin. The handler responds by turning on bus power. If
411  * the DWC_otg controller is in low power mode, the handler brings the
412  * controller out of low power mode before turning on bus power.
413  *
414  * @param core_if Programming view of DWC_otg controller.
415  */
416 int32_t dwc_otg_handle_session_req_intr(dwc_otg_core_if_t *core_if)
417 {
418         gintsts_data_t gintsts;
419
420 #ifndef DWC_HOST_ONLY
421         hprt0_data_t hprt0;
422         DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n");
423
424         if (dwc_otg_is_device_mode(core_if)) {
425                 DWC_PRINT("SRP: Device mode\n");
426         } else {
427                 DWC_PRINT("SRP: Host mode\n");
428
429                 /* Turn on the port power bit. */
430                 hprt0.d32 = dwc_otg_read_hprt0(core_if);
431                 hprt0.b.prtpwr = 1;
432                 dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);
433
434                 /* Start the Connection timer. So a message can be displayed
435                  * if connect does not occur within 10 seconds. */
436                 hcd_session_start(core_if);
437         }
438 #endif
439
440         /* Clear interrupt */
441         gintsts.d32 = 0;
442         gintsts.b.sessreqintr = 1;
443         dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
444
445         return 1;
446 }
447
448
449 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
450 void w_wakeup_detected(void *p)
451 {
452         dwc_otg_core_if_t* core_if = p;
453
454 #else
455
456 void w_wakeup_detected(struct work_struct *p)
457 {
458         struct delayed_work *dw = container_of(p, struct delayed_work, work);
459         dwc_otg_core_if_t *core_if = container_of(dw, dwc_otg_core_if_t, w_wkp);
460
461 #endif
462         /*
463          * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms
464          * so that OPT tests pass with all PHYs).
465          */
466         hprt0_data_t hprt0 = {.d32=0};
467 #if 0
468         pcgcctl_data_t pcgcctl = {.d32=0};
469         /* Restart the Phy Clock */
470         pcgcctl.b.stoppclk = 1;
471         dwc_modify_reg32(core_if->pcgcctl, pcgcctl.d32, 0);
472         UDELAY(10);
473 #endif //0
474         hprt0.d32 = dwc_otg_read_hprt0(core_if);
475         DWC_DEBUGPL(DBG_ANY,"Resume: HPRT0=%0x\n", hprt0.d32);
476 //      MDELAY(70);
477         hprt0.b.prtres = 0; /* Resume */
478         dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);
479         DWC_DEBUGPL(DBG_ANY,"Clear Resume: HPRT0=%0x\n", dwc_read_reg32(core_if->host_if->hprt0));
480 }
481 /**
482  * This interrupt indicates that the DWC_otg controller has detected a
483  * resume or remote wakeup sequence. If the DWC_otg controller is in
484  * low power mode, the handler must brings the controller out of low
485  * power mode. The controller automatically begins resume
486  * signaling. The handler schedules a time to stop resume signaling.
487  */
488 int32_t dwc_otg_handle_wakeup_detected_intr(dwc_otg_core_if_t *core_if)
489 {
490         gintsts_data_t gintsts;
491
492         DWC_DEBUGPL(DBG_ANY, "++Resume and Remote Wakeup Detected Interrupt++\n");
493
494         if (dwc_otg_is_device_mode(core_if)) {
495                 dctl_data_t dctl = {.d32=0};
496                 DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n",
497                             dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts));
498 #ifdef PARTIAL_POWER_DOWN
499                 if (core_if->hwcfg4.b.power_optimiz) {
500                         pcgcctl_data_t power = {.d32=0};
501
502                         power.d32 = dwc_read_reg32(core_if->pcgcctl);
503                         DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n", power.d32);
504
505                         power.b.stoppclk = 0;
506                         dwc_write_reg32(core_if->pcgcctl, power.d32);
507
508                         power.b.pwrclmp = 0;
509                         dwc_write_reg32(core_if->pcgcctl, power.d32);
510
511                         power.b.rstpdwnmodule = 0;
512                         dwc_write_reg32(core_if->pcgcctl, power.d32);
513                 }
514 #endif
515                 /* Clear the Remote Wakeup Signalling */
516                 dctl.b.rmtwkupsig = 1;
517                 dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dctl,
518                                   dctl.d32, 0);
519
520                 if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
521                         core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
522                 }
523
524         } else {
525                 pcgcctl_data_t pcgcctl = {.d32=0};
526
527                 /* Restart the Phy Clock */
528                 pcgcctl.b.stoppclk = 1;
529                 dwc_modify_reg32(core_if->pcgcctl, pcgcctl.d32, 0);
530
531                 queue_delayed_work(core_if->wq_otg, &core_if->w_wkp, ((70 * HZ / 1000) + 1));
532         }
533
534         /* Clear interrupt */
535         gintsts.d32 = 0;
536         gintsts.b.wkupintr = 1;
537         dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
538
539         return 1;
540 }
541
542 /**
543  * This interrupt indicates that a device has been disconnected from
544  * the root port.
545  */
546 int32_t dwc_otg_handle_disconnect_intr(dwc_otg_core_if_t *core_if)
547 {
548         gintsts_data_t gintsts;
549
550         DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n",
551                     (dwc_otg_is_host_mode(core_if)?"Host":"Device"),
552                     op_state_str(core_if));
553
554 /** @todo Consolidate this if statement. */
555 #ifndef DWC_HOST_ONLY
556         if (core_if->op_state == B_HOST) {
557                 /* If in device mode Disconnect and stop the HCD, then
558                  * start the PCD. */
559                 hcd_disconnect(core_if);
560                 pcd_start(core_if);
561                 core_if->op_state = B_PERIPHERAL;
562         } else if (dwc_otg_is_device_mode(core_if)) {
563                 gotgctl_data_t gotgctl = { .d32 = 0 };
564                 gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl);
565                 if (gotgctl.b.hstsethnpen==1) {
566                         /* Do nothing, if HNP in process the OTG
567                          * interrupt "Host Negotiation Detected"
568                          * interrupt will do the mode switch.
569                          */
570                 } else if (gotgctl.b.devhnpen == 0) {
571                         /* If in device mode Disconnect and stop the HCD, then
572                          * start the PCD. */
573                         hcd_disconnect(core_if);
574                         pcd_start(core_if);
575                         core_if->op_state = B_PERIPHERAL;
576                 } else {
577                         DWC_DEBUGPL(DBG_ANY,"!a_peripheral && !devhnpen\n");
578                 }
579         } else {
580                 if (core_if->op_state == A_HOST) {
581                         /* A-Cable still connected but device disconnected. */
582                         hcd_disconnect(core_if);
583                 }
584         }
585 #endif
586
587         gintsts.d32 = 0;
588         gintsts.b.disconnect = 1;
589         dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
590         return 1;
591 }
592 /**
593  * This interrupt indicates that SUSPEND state has been detected on
594  * the USB.
595  *
596  * For HNP the USB Suspend interrupt signals the change from
597  * "a_peripheral" to "a_host".
598  *
599  * When power management is enabled the core will be put in low power
600  * mode.
601  */
602 int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t *core_if)
603 {
604         dsts_data_t dsts;
605         gintsts_data_t gintsts;
606
607         DWC_DEBUGPL(DBG_ANY,"USB SUSPEND\n");
608
609         if (dwc_otg_is_device_mode(core_if)) {
610                 /* Check the Device status register to determine if the Suspend
611                  * state is active. */
612                 dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts);
613                 DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32);
614                 DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d "
615                             "HWCFG4.power Optimize=%d\n",
616                             dsts.b.suspsts, core_if->hwcfg4.b.power_optimiz);
617
618
619 #ifdef PARTIAL_POWER_DOWN
620 /** @todo Add a module parameter for power management. */
621
622                 if (dsts.b.suspsts && core_if->hwcfg4.b.power_optimiz) {
623                         pcgcctl_data_t power = {.d32=0};
624                         DWC_DEBUGPL(DBG_CIL, "suspend\n");
625
626                         power.b.pwrclmp = 1;
627                         dwc_write_reg32(core_if->pcgcctl, power.d32);
628
629                         power.b.rstpdwnmodule = 1;
630                         dwc_modify_reg32(core_if->pcgcctl, 0, power.d32);
631
632                         power.b.stoppclk = 1;
633                         dwc_modify_reg32(core_if->pcgcctl, 0, power.d32);
634
635                 } else {
636                         DWC_DEBUGPL(DBG_ANY,"disconnect?\n");
637                 }
638 #endif
639                 /* PCD callback for suspend. */
640                 pcd_suspend(core_if);
641         } else {
642                 if (core_if->op_state == A_PERIPHERAL) {
643                         DWC_DEBUGPL(DBG_ANY,"a_peripheral->a_host\n");
644                         /* Clear the a_peripheral flag, back to a_host. */
645                         pcd_stop(core_if);
646                         hcd_start(core_if);
647                         core_if->op_state = A_HOST;
648                 }
649         }
650
651         /* Clear interrupt */
652         gintsts.d32 = 0;
653         gintsts.b.usbsuspend = 1;
654         dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32);
655
656         return 1;
657 }
658
659
660 /**
661  * This function returns the Core Interrupt register.
662  */
663 static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t *core_if)
664 {
665         gintsts_data_t gintsts;
666         gintmsk_data_t gintmsk;
667         gintmsk_data_t gintmsk_common = {.d32=0};
668         gintmsk_common.b.wkupintr = 1;
669         gintmsk_common.b.sessreqintr = 1;
670         gintmsk_common.b.conidstschng = 1;
671         gintmsk_common.b.otgintr = 1;
672         gintmsk_common.b.modemismatch = 1;
673         gintmsk_common.b.disconnect = 1;
674         gintmsk_common.b.usbsuspend = 1;
675         /** @todo: The port interrupt occurs while in device
676          * mode. Added code to CIL to clear the interrupt for now!
677          */
678         gintmsk_common.b.portintr = 1;
679
680         gintsts.d32 = dwc_read_reg32(&core_if->core_global_regs->gintsts);
681         gintmsk.d32 = dwc_read_reg32(&core_if->core_global_regs->gintmsk);
682 #ifdef DEBUG
683         /* if any common interrupts set */
684         if (gintsts.d32 & gintmsk_common.d32) {
685                 DWC_DEBUGPL(DBG_ANY, "gintsts=%08x  gintmsk=%08x\n",
686                             gintsts.d32, gintmsk.d32);
687         }
688 #endif
689
690         return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
691
692 }
693
694 /**
695  * Common interrupt handler.
696  *
697  * The common interrupts are those that occur in both Host and Device mode.
698  * This handler handles the following interrupts:
699  * - Mode Mismatch Interrupt
700  * - Disconnect Interrupt
701  * - OTG Interrupt
702  * - Connector ID Status Change Interrupt
703  * - Session Request Interrupt.
704  * - Resume / Remote Wakeup Detected Interrupt.
705  *
706  */
707 int32_t dwc_otg_handle_common_intr(dwc_otg_core_if_t *core_if)
708 {
709         int retval = 0;
710         gintsts_data_t gintsts;
711
712         gintsts.d32 = dwc_otg_read_common_intr(core_if);
713
714         if (gintsts.b.modemismatch) {
715                 retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
716         }
717         if (gintsts.b.otgintr) {
718                 retval |= dwc_otg_handle_otg_intr(core_if);
719         }
720         if (gintsts.b.conidstschng) {
721                 retval |= dwc_otg_handle_conn_id_status_change_intr(core_if);
722         }
723         if (gintsts.b.disconnect) {
724                 retval |= dwc_otg_handle_disconnect_intr(core_if);
725         }
726         if (gintsts.b.sessreqintr) {
727                 retval |= dwc_otg_handle_session_req_intr(core_if);
728         }
729         if (gintsts.b.wkupintr) {
730                 retval |= dwc_otg_handle_wakeup_detected_intr(core_if);
731         }
732         if (gintsts.b.usbsuspend) {
733                 retval |= dwc_otg_handle_usb_suspend_intr(core_if);
734         }
735         if (gintsts.b.portintr && dwc_otg_is_device_mode(core_if)) {
736                 /* The port interrupt occurs while in device mode with HPRT0
737                  * Port Enable/Disable.
738                  */
739                 gintsts.d32 = 0;
740                 gintsts.b.portintr = 1;
741                 dwc_write_reg32(&core_if->core_global_regs->gintsts,
742                                 gintsts.d32);
743                 retval |= 1;
744
745         }
746
747         S3C2410X_CLEAR_EINTPEND();
748
749         return retval;
750 }