tizen 2.4 release
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / drivers / usb / gadget / 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: #32 $
4  * $Date: 2012/08/10 $
5  * $Change: 2047372 $
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 "dwc_os.h"
43 #include "dwc_otg_regs.h"
44 #include "dwc_otg_cil.h"
45 #include "dwc_otg_driver.h"
46 #include "dwc_otg_pcd.h"
47 #include "dwc_otg_hcd.h"
48
49 #ifdef DEBUG
50 inline const char *op_state_str(dwc_otg_core_if_t * core_if)
51 {
52         return (core_if->op_state == A_HOST ? "a_host" :
53                 (core_if->op_state == A_SUSPEND ? "a_suspend" :
54                  (core_if->op_state == A_PERIPHERAL ? "a_peripheral" :
55                   (core_if->op_state == B_PERIPHERAL ? "b_peripheral" :
56                    (core_if->op_state == B_HOST ? "b_host" : "unknown")))));
57 }
58 #endif
59
60 /** This function will log a debug message
61  *
62  * @param core_if Programming view of DWC_otg controller.
63  */
64 int32_t dwc_otg_handle_mode_mismatch_intr(dwc_otg_core_if_t * core_if)
65 {
66         gintsts_data_t gintsts;
67         DWC_WARN("Mode Mismatch Interrupt: currently in %s mode\n",
68                  dwc_otg_mode(core_if) ? "Host" : "Device");
69
70         /* Clear interrupt */
71         gintsts.d32 = 0;
72         gintsts.b.modemismatch = 1;
73         DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
74         return 1;
75 }
76
77 /**
78  * This function handles the OTG Interrupts. It reads the OTG
79  * Interrupt Register (GOTGINT) to determine what interrupt has
80  * occurred.
81  *
82  * @param core_if Programming view of DWC_otg controller.
83  */
84 int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t * core_if)
85 {
86         dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
87         gotgint_data_t gotgint;
88         gotgctl_data_t gotgctl;
89         gintmsk_data_t gintmsk;
90         gpwrdn_data_t gpwrdn;
91
92         gotgint.d32 = DWC_READ_REG32(&global_regs->gotgint);
93         gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
94         DWC_DEBUGPL(DBG_CIL, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint.d32,
95                     op_state_str(core_if));
96
97         if (gotgint.b.sesenddet) {
98                 DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
99                             "Session End Detected++ (%s)\n",
100                             op_state_str(core_if));
101                 gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
102
103                 if (core_if->op_state == B_HOST) {
104                         cil_pcd_start(core_if);
105                         core_if->op_state = B_PERIPHERAL;
106                 } else {
107                         /* If not B_HOST and Device HNP still set. HNP
108                          * Did not succeed!*/
109                         if (gotgctl.b.devhnpen) {
110                                 DWC_DEBUGPL(DBG_ANY, "Session End Detected\n");
111                                 __DWC_ERROR("Device Not Connected/Responding!\n");
112                         }
113
114                         /* If Session End Detected the B-Cable has
115                          * been disconnected. */
116                         /* Reset PCD and Gadget driver to a
117                          * clean state. */
118                         core_if->lx_state = DWC_OTG_L0;
119                         DWC_SPINUNLOCK(core_if->lock);
120                         cil_pcd_stop(core_if);
121                         DWC_SPINLOCK(core_if->lock);
122
123                         if (core_if->adp_enable) {
124                                 if (core_if->power_down == 2) {
125                                         gpwrdn.d32 = 0;
126                                         gpwrdn.b.pwrdnswtch = 1;
127                                         DWC_MODIFY_REG32(&core_if->
128                                                          core_global_regs->
129                                                          gpwrdn, gpwrdn.d32, 0);
130                                 }
131
132                                 gpwrdn.d32 = 0;
133                                 gpwrdn.b.pmuintsel = 1;
134                                 gpwrdn.b.pmuactv = 1;
135                                 DWC_MODIFY_REG32(&core_if->core_global_regs->
136                                                  gpwrdn, 0, gpwrdn.d32);
137
138                                 dwc_otg_adp_sense_start(core_if);
139                         }
140                 }
141
142                 gotgctl.d32 = 0;
143                 gotgctl.b.devhnpen = 1;
144                 DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
145         }
146         if (gotgint.b.sesreqsucstschng) {
147                 DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
148                             "Session Reqeust Success Status Change++\n");
149                 gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
150                 if (gotgctl.b.sesreqscs) {
151
152                         if ((core_if->core_params->phy_type ==
153                              DWC_PHY_TYPE_PARAM_FS) && (core_if->core_params->i2c_enable)) {
154                                 core_if->srp_success = 1;
155                         } else {
156                                 DWC_SPINUNLOCK(core_if->lock);
157                                 cil_pcd_resume(core_if);
158                                 DWC_SPINLOCK(core_if->lock);
159                                 /* Clear Session Request */
160                                 gotgctl.d32 = 0;
161                                 gotgctl.b.sesreq = 1;
162                                 DWC_MODIFY_REG32(&global_regs->gotgctl,
163                                                  gotgctl.d32, 0);
164                         }
165                 }
166         }
167         if (gotgint.b.hstnegsucstschng) {
168                 /* Print statements during the HNP interrupt handling
169                  * can cause it to fail.*/
170                 gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
171                 /* WA for 3.00a- HW is not setting cur_mode, even sometimes
172                  * this does not help*/
173                 if (core_if->snpsid >= OTG_CORE_REV_3_00a)
174                         dwc_udelay(100);
175                 if (gotgctl.b.hstnegscs) {
176                         if (dwc_otg_is_host_mode(core_if)) {
177                                 core_if->op_state = B_HOST;
178                                 /*
179                                  * Need to disable SOF interrupt immediately.
180                                  * When switching from device to host, the PCD
181                                  * interrupt handler won't handle the
182                                  * interrupt if host mode is already set. The
183                                  * HCD interrupt handler won't get called if
184                                  * the HCD state is HALT. This means that the
185                                  * interrupt does not get handled and Linux
186                                  * complains loudly.
187                                  */
188                                 gintmsk.d32 = 0;
189                                 gintmsk.b.sofintr = 1;
190                                 DWC_MODIFY_REG32(&global_regs->gintmsk,
191                                                  gintmsk.d32, 0);
192                                 /* Call callback function with spin lock released */
193                                 DWC_SPINUNLOCK(core_if->lock);
194                                 cil_pcd_stop(core_if);
195                                 /*
196                                  * Initialize the Core for Host mode.
197                                  */
198                                 cil_hcd_start(core_if);
199                                 DWC_SPINLOCK(core_if->lock);
200                                 core_if->op_state = B_HOST;
201                         }
202                 } else {
203                         gotgctl.d32 = 0;
204                         gotgctl.b.hnpreq = 1;
205                         gotgctl.b.devhnpen = 1;
206                         DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
207                         DWC_DEBUGPL(DBG_ANY, "HNP Failed\n");
208                         __DWC_ERROR("Device Not Connected/Responding\n");
209                 }
210         }
211         if (gotgint.b.hstnegdet) {
212                 /* The disconnect interrupt is set at the same time as
213                  * Host Negotiation Detected.  During the mode
214                  * switch all interrupts are cleared so the disconnect
215                  * interrupt handler will not get executed.
216                  */
217                 DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
218                             "Host Negotiation Detected++ (%s)\n",
219                             (dwc_otg_is_host_mode(core_if) ? "Host" :
220                              "Device"));
221                 if (dwc_otg_is_device_mode(core_if)) {
222                         DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n",
223                                     core_if->op_state);
224                         DWC_SPINUNLOCK(core_if->lock);
225                         cil_hcd_disconnect(core_if);
226                         cil_pcd_start(core_if);
227                         DWC_SPINLOCK(core_if->lock);
228                         core_if->op_state = A_PERIPHERAL;
229                 } else {
230                         /*
231                          * Need to disable SOF interrupt immediately. When
232                          * switching from device to host, the PCD interrupt
233                          * handler won't handle the interrupt if host mode is
234                          * already set. The HCD interrupt handler won't get
235                          * called if the HCD state is HALT. This means that
236                          * the interrupt does not get handled and Linux
237                          * complains loudly.
238                          */
239                         gintmsk.d32 = 0;
240                         gintmsk.b.sofintr = 1;
241                         DWC_MODIFY_REG32(&global_regs->gintmsk, gintmsk.d32, 0);
242                         DWC_SPINUNLOCK(core_if->lock);
243                         cil_pcd_stop(core_if);
244                         cil_hcd_start(core_if);
245                         DWC_SPINLOCK(core_if->lock);
246                         core_if->op_state = A_HOST;
247                 }
248         }
249         if (gotgint.b.adevtoutchng) {
250                 DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
251                             "A-Device Timeout Change++\n");
252         }
253         if (gotgint.b.debdone) {
254                 DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " "Debounce Done++\n");
255         }
256
257         /* Clear GOTGINT */
258         DWC_WRITE_REG32(&core_if->core_global_regs->gotgint, gotgint.d32);
259
260         return 1;
261 }
262
263 void w_conn_id_status_change(void *p)
264 {
265         dwc_otg_core_if_t *core_if = p;
266         uint32_t count = 0;
267         gotgctl_data_t gotgctl = {.d32 = 0 };
268
269         gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
270         DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32);
271         DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts);
272
273         /* B-Device connector (Device Mode) */
274         if (gotgctl.b.conidsts) {
275                 /* Wait for switch to device mode. */
276                 while (!dwc_otg_is_device_mode(core_if)) {
277                         DWC_PRINTF("Waiting for Peripheral Mode, Mode=%s\n",
278                                    (dwc_otg_is_host_mode(core_if) ? "Host" :
279                                     "Peripheral"));
280                         dwc_mdelay(100);
281                         if (++count > 10000)
282                                 break;
283                 }
284                 DWC_ASSERT(++count < 10000,
285                            "Connection id status change timed out");
286                 core_if->op_state = B_PERIPHERAL;
287                 dwc_otg_core_init(core_if);
288                 dwc_otg_enable_global_interrupts(core_if);
289                 cil_pcd_start(core_if);
290         } else {
291                 /* A-Device connector (Host Mode) */
292                 while (!dwc_otg_is_host_mode(core_if)) {
293                         DWC_PRINTF("Waiting for Host Mode, Mode=%s\n",
294                                    (dwc_otg_is_host_mode(core_if) ? "Host" :
295                                     "Peripheral"));
296                         dwc_mdelay(100);
297                         if (++count > 10000)
298                                 break;
299                 }
300                 DWC_ASSERT(++count < 10000,
301                            "Connection id status change timed out");
302                 core_if->op_state = A_HOST;
303                 /*
304                  * Initialize the Core for Host mode.
305                  */
306                 dwc_otg_core_init(core_if);
307                 dwc_otg_enable_global_interrupts(core_if);
308                 cil_hcd_start(core_if);
309         }
310 }
311
312 /**
313  * This function handles the Connector ID Status Change Interrupt.  It
314  * reads the OTG Interrupt Register (GOTCTL) to determine whether this
315  * is a Device to Host Mode transition or a Host Mode to Device
316  * Transition. 
317  *
318  * This only occurs when the cable is connected/removed from the PHY
319  * connector.
320  *
321  * @param core_if Programming view of DWC_otg controller.
322  */
323 int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t * core_if)
324 {
325
326         /*
327          * Need to disable SOF interrupt immediately. If switching from device
328          * to host, the PCD interrupt handler won't handle the interrupt if
329          * host mode is already set. The HCD interrupt handler won't get
330          * called if the HCD state is HALT. This means that the interrupt does
331          * not get handled and Linux complains loudly.
332          */
333         gintmsk_data_t gintmsk = {.d32 = 0 };
334         gintsts_data_t gintsts = {.d32 = 0 };
335
336         gintmsk.b.sofintr = 1;
337         DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
338
339         DWC_DEBUGPL(DBG_CIL,
340                     " ++Connector ID Status Change Interrupt++  (%s)\n",
341                     (dwc_otg_is_host_mode(core_if) ? "Host" : "Device"));
342
343         DWC_SPINUNLOCK(core_if->lock);
344
345         /*
346          * Need to schedule a work, as there are possible DELAY function calls
347          * Release lock before scheduling workq as it holds spinlock during scheduling
348          */
349
350         DWC_WORKQ_SCHEDULE(core_if->wq_otg, w_conn_id_status_change,
351                            core_if, "connection id status change");
352         DWC_SPINLOCK(core_if->lock);
353
354         /* Set flag and clear interrupt */
355         gintsts.b.conidstschng = 1;
356         DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
357
358         return 1;
359 }
360
361 /**
362  * This interrupt indicates that a device is initiating the Session
363  * Request Protocol to request the host to turn on bus power so a new
364  * session can begin. The handler responds by turning on bus power. If
365  * the DWC_otg controller is in low power mode, the handler brings the
366  * controller out of low power mode before turning on bus power.
367  *
368  * @param core_if Programming view of DWC_otg controller.
369  */
370 int32_t dwc_otg_handle_session_req_intr(dwc_otg_core_if_t * core_if)
371 {
372         gintsts_data_t gintsts;
373
374 #ifndef DWC_HOST_ONLY
375         DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n");
376
377         if (dwc_otg_is_device_mode(core_if)) {
378                 DWC_PRINTF("SRP: Device mode\n");
379         } else {
380                 hprt0_data_t hprt0;
381                 DWC_PRINTF("SRP: Host mode\n");
382
383                 /* Turn on the port power bit. */
384                 hprt0.d32 = dwc_otg_read_hprt0(core_if);
385                 hprt0.b.prtpwr = 1;
386                 DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
387
388                 /* Start the Connection timer. So a message can be displayed
389                  * if connect does not occur within 10 seconds. */
390                 cil_hcd_session_start(core_if);
391         }
392 #endif
393
394         /* Clear interrupt */
395         gintsts.d32 = 0;
396         gintsts.b.sessreqintr = 1;
397         DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
398
399         return 1;
400 }
401
402 void w_wakeup_detected(void *p)
403 {
404         dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) p;
405         /*
406          * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms
407          * so that OPT tests pass with all PHYs).
408          */
409         hprt0_data_t hprt0 = {.d32 = 0 };
410 #if 0
411         pcgcctl_data_t pcgcctl = {.d32 = 0 };
412         /* Restart the Phy Clock */
413         pcgcctl.b.stoppclk = 1;
414         DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
415         dwc_udelay(10);
416 #endif //0
417         hprt0.d32 = dwc_otg_read_hprt0(core_if);
418         DWC_DEBUGPL(DBG_ANY, "Resume: HPRT0=%0x\n", hprt0.d32);
419 //      dwc_mdelay(70);
420         hprt0.b.prtres = 0;     /* Resume */
421         DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
422         DWC_DEBUGPL(DBG_ANY, "Clear Resume: HPRT0=%0x\n",
423                     DWC_READ_REG32(core_if->host_if->hprt0));
424
425         cil_hcd_resume(core_if);
426
427         /** Change to L0 state*/
428         core_if->lx_state = DWC_OTG_L0;
429 }
430
431 /**
432  * This interrupt indicates that the DWC_otg controller has detected a
433  * resume or remote wakeup sequence. If the DWC_otg controller is in
434  * low power mode, the handler must brings the controller out of low
435  * power mode. The controller automatically begins resume
436  * signaling. The handler schedules a time to stop resume signaling.
437  */
438 int32_t dwc_otg_handle_wakeup_detected_intr(dwc_otg_core_if_t * core_if)
439 {
440         gintsts_data_t gintsts;
441
442         DWC_DEBUGPL(DBG_ANY,
443                     "++Resume and Remote Wakeup Detected Interrupt++\n");
444
445         DWC_PRINTF("%s lxstate = %d\n", __func__, core_if->lx_state);
446
447         if (dwc_otg_is_device_mode(core_if)) {
448                 dctl_data_t dctl = {.d32 = 0 };
449                 DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n",
450                             DWC_READ_REG32(&core_if->dev_if->dev_global_regs->
451                                            dsts));
452                 if (core_if->lx_state == DWC_OTG_L2) {
453 #ifdef PARTIAL_POWER_DOWN
454                         if (core_if->hwcfg4.b.power_optimiz) {
455                                 pcgcctl_data_t power = {.d32 = 0 };
456
457                                 power.d32 = DWC_READ_REG32(core_if->pcgcctl);
458                                 DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n",
459                                             power.d32);
460
461                                 power.b.stoppclk = 0;
462                                 DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
463
464                                 power.b.pwrclmp = 0;
465                                 DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
466
467                                 power.b.rstpdwnmodule = 0;
468                                 DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
469                         }
470 #endif
471                         /* Clear the Remote Wakeup Signaling */
472                         dctl.b.rmtwkupsig = 1;
473                         DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
474                                          dctl, dctl.d32, 0);
475
476                         DWC_SPINUNLOCK(core_if->lock);
477                         if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
478                                 core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
479                         }
480                         DWC_SPINLOCK(core_if->lock);
481                 } else {
482                         glpmcfg_data_t lpmcfg;
483                         lpmcfg.d32 =
484                             DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
485                         lpmcfg.b.hird_thres &= (~(1 << 4));
486                         DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg,
487                                         lpmcfg.d32);
488                 }
489                 /** Change to L0 state*/
490                 core_if->lx_state = DWC_OTG_L0;
491         } else {
492                 if (core_if->lx_state != DWC_OTG_L1) {
493                         pcgcctl_data_t pcgcctl = {.d32 = 0 };
494
495                         /* Restart the Phy Clock */
496                         pcgcctl.b.stoppclk = 1;
497                         DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
498                         DWC_TIMER_SCHEDULE(core_if->wkp_timer, 71);
499                 } else {
500                         /** Change to L0 state*/
501                         core_if->lx_state = DWC_OTG_L0;
502                 }
503         }
504
505         /* Clear interrupt */
506         gintsts.d32 = 0;
507         gintsts.b.wkupintr = 1;
508         DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
509
510         return 1;
511 }
512
513 /** This interrupt indicates that restore command after Hibernation
514  * was completed by the core. */
515 int32_t dwc_otg_handle_restore_done_intr(dwc_otg_core_if_t * core_if)
516 {
517         pcgcctl_data_t pcgcctl;
518         DWC_DEBUGPL(DBG_ANY, "++Restore Done Interrupt++\n");
519
520         //TODO De-assert restore signal. 8.a
521         pcgcctl.d32 = DWC_READ_REG32(core_if->pcgcctl);
522         if (pcgcctl.b.restoremode == 1) {
523                 gintmsk_data_t gintmsk = {.d32 = 0 };
524                 /*
525                  * If restore mode is Remote Wakeup,
526                  * unmask Remote Wakeup interrupt.
527                  */
528                 gintmsk.b.wkupintr = 1;
529                 DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk,
530                                  0, gintmsk.d32);
531         }
532
533         return 1;
534 }
535
536 #ifndef DWC_DEVICE_ONLY
537 /**
538  * This interrupt indicates that a device has been disconnected from
539  * the root port.
540  */
541 int32_t otg_cable_disconnect(dwc_otg_core_if_t * core_if)
542 {
543         
544         cil_hcd_disconnect(core_if);
545         cil_hcd_stop(core_if);
546         return 1;
547 }
548 #endif
549
550 /**
551  * This interrupt indicates that a device has been disconnected from
552  * the root port.
553  */
554 int32_t dwc_otg_handle_disconnect_intr(dwc_otg_core_if_t * core_if)
555 {
556         gintsts_data_t gintsts;
557
558         DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n",
559                     (dwc_otg_is_host_mode(core_if) ? "Host" : "Device"),
560                     op_state_str(core_if));
561
562 /** @todo Consolidate this if statement. */
563 #ifndef DWC_HOST_ONLY
564         if (core_if->op_state == B_HOST) {
565                 /* If in device mode Disconnect and stop the HCD, then
566                  * start the PCD. */
567                 DWC_SPINUNLOCK(core_if->lock);
568                 cil_hcd_disconnect(core_if);
569                 cil_pcd_start(core_if);
570                 DWC_SPINLOCK(core_if->lock);
571                 core_if->op_state = B_PERIPHERAL;
572         } else if (dwc_otg_is_device_mode(core_if)) {
573                 gotgctl_data_t gotgctl = {.d32 = 0 };
574                 gotgctl.d32 =
575                     DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
576                 if (gotgctl.b.hstsethnpen == 1) {
577                         /* Do nothing, if HNP in process the OTG
578                          * interrupt "Host Negotiation Detected"
579                          * interrupt will do the mode switch.
580                          */
581                 } else if (gotgctl.b.devhnpen == 0) {
582                         /* If in device mode Disconnect and stop the HCD, then
583                          * start the PCD. */
584                         DWC_SPINUNLOCK(core_if->lock);
585                         cil_hcd_disconnect(core_if);
586                         cil_pcd_start(core_if);
587                         DWC_SPINLOCK(core_if->lock);
588                         core_if->op_state = B_PERIPHERAL;
589                 } else {
590                         DWC_DEBUGPL(DBG_ANY, "!a_peripheral && !devhnpen\n");
591                 }
592         } else {
593                 if (core_if->op_state == A_HOST) {
594                         /* A-Cable still connected but device disconnected. */
595                         cil_hcd_disconnect(core_if);
596                         if (core_if->adp_enable) {
597                                 gpwrdn_data_t gpwrdn = {.d32 = 0 };
598                                 cil_hcd_stop(core_if);
599                                 /* Enable Power Down Logic */
600                                 gpwrdn.b.pmuintsel = 1;
601                                 gpwrdn.b.pmuactv = 1;
602                                 DWC_MODIFY_REG32(&core_if->core_global_regs->
603                                                  gpwrdn, 0, gpwrdn.d32);
604                                 dwc_otg_adp_probe_start(core_if);
605
606                                 /* Power off the core */
607                                 if (core_if->power_down == 2) {
608                                         gpwrdn.d32 = 0;
609                                         gpwrdn.b.pwrdnswtch = 1;
610                                         DWC_MODIFY_REG32
611                                             (&core_if->core_global_regs->gpwrdn,
612                                              gpwrdn.d32, 0);
613                                 }
614                         }
615                 }
616         }
617 #endif
618         /* Change to L3(OFF) state */
619         core_if->lx_state = DWC_OTG_L3;
620
621         gintsts.d32 = 0;
622         gintsts.b.disconnect = 1;
623         DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
624         return 1;
625 }
626
627 /**
628  * This interrupt indicates that SUSPEND state has been detected on
629  * the USB.
630  *
631  * For HNP the USB Suspend interrupt signals the change from
632  * "a_peripheral" to "a_host".
633  *
634  * When power management is enabled the core will be put in low power
635  * mode.
636  */
637 int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t * core_if)
638 {
639         dsts_data_t dsts;
640         gintsts_data_t gintsts;
641
642         DWC_DEBUGPL(DBG_ANY, "USB SUSPEND\n");
643
644         if (dwc_otg_is_device_mode(core_if)) {
645                 /* Check the Device status register to determine if the Suspend
646                  * state is active. */
647                 dsts.d32 =
648                     DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
649                 DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32);
650                 DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d "
651                             "HWCFG4.power Optimize=%d\n",
652                             dsts.b.suspsts, core_if->hwcfg4.b.power_optimiz);
653                         
654 #ifdef PARTIAL_POWER_DOWN
655 /** @todo Add a module parameter for power management. */
656
657                 if (dsts.b.suspsts && core_if->hwcfg4.b.power_optimiz) {
658                         pcgcctl_data_t power = {.d32 = 0 };
659                         DWC_DEBUGPL(DBG_CIL, "suspend\n");
660
661                         power.b.pwrclmp = 1;
662                         DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
663
664                         power.b.rstpdwnmodule = 1;
665                         DWC_MODIFY_REG32(core_if->pcgcctl, 0, power.d32);
666
667                         power.b.stoppclk = 1;
668                         DWC_MODIFY_REG32(core_if->pcgcctl, 0, power.d32);
669
670                 } else {
671                         DWC_DEBUGPL(DBG_ANY, "disconnect?\n");
672                 }
673 #endif
674                 /* PCD callback for suspend. Release the lock inside of callback function */
675                 cil_pcd_suspend(core_if);
676         } else {
677                 if (core_if->op_state == A_PERIPHERAL) {
678                         DWC_DEBUGPL(DBG_ANY, "a_peripheral->a_host\n");
679                         /* Clear the a_peripheral flag, back to a_host. */
680                         DWC_SPINUNLOCK(core_if->lock);
681                         cil_pcd_stop(core_if);
682                         cil_hcd_start(core_if);
683                         DWC_SPINLOCK(core_if->lock);
684                         core_if->op_state = A_HOST;
685                 }
686         }
687
688         /* Change to L2(suspend) state */
689         core_if->lx_state = DWC_OTG_L2;
690
691
692         /* Clear interrupt */
693         gintsts.d32 = 0;
694         gintsts.b.usbsuspend = 1;
695         DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
696
697         return 1;
698 }
699
700 #ifdef CONFIG_USB_DWC_OTG_LPM
701 /**
702  * This function hadles LPM transaction received interrupt.
703  */
704 static int32_t dwc_otg_handle_lpm_intr(dwc_otg_core_if_t * core_if)
705 {
706         glpmcfg_data_t lpmcfg;
707         gintsts_data_t gintsts;
708
709         if (!core_if->core_params->lpm_enable) {
710                 DWC_PRINTF("Unexpected LPM interrupt\n");
711         }
712
713         lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
714         DWC_PRINTF("LPM config register = 0x%08x\n", lpmcfg.d32);
715
716         if (dwc_otg_is_host_mode(core_if)) {
717                 cil_hcd_sleep(core_if);
718         } else {
719                 lpmcfg.b.hird_thres |= (1 << 4);
720                 DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg,
721                                 lpmcfg.d32);
722         }
723
724         /* Examine prt_sleep_sts after TL1TokenTetry period max (10 us) */
725         dwc_udelay(10);
726         lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
727         if (lpmcfg.b.prt_sleep_sts) {
728                 /* Save the current state */
729                 core_if->lx_state = DWC_OTG_L1;
730         }
731
732         /* Clear interrupt  */
733         gintsts.d32 = 0;
734         gintsts.b.lpmtranrcvd = 1;
735         DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
736         return 1;
737 }
738 #endif /* CONFIG_USB_DWC_OTG_LPM */
739
740 /**
741  * This function returns the Core Interrupt register.
742  */
743 static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if)
744 {
745         gahbcfg_data_t gahbcfg = {.d32 = 0 };
746         gintsts_data_t gintsts;
747         gintmsk_data_t gintmsk;
748         gintmsk_data_t gintmsk_common = {.d32 = 0 };
749         gintmsk_common.b.wkupintr = 1;
750         gintmsk_common.b.sessreqintr = 1;
751         gintmsk_common.b.conidstschng = 1;
752         gintmsk_common.b.otgintr = 1;
753         gintmsk_common.b.modemismatch = 1;
754         gintmsk_common.b.disconnect = 1;
755         gintmsk_common.b.usbsuspend = 1;
756 #ifdef CONFIG_USB_DWC_OTG_LPM
757         gintmsk_common.b.lpmtranrcvd = 1;
758 #endif
759         gintmsk_common.b.restoredone = 1;
760         /** @todo: The port interrupt occurs while in device
761          * mode. Added code to CIL to clear the interrupt for now!
762          */
763         gintmsk_common.b.portintr = 1;
764
765         gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
766         gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
767         gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
768
769 #ifdef DEBUG
770         /* if any common interrupts set */
771         if (gintsts.d32 & gintmsk_common.d32) {
772                 DWC_DEBUGPL(DBG_ANY, "gintsts=%08x  gintmsk=%08x\n",
773                             gintsts.d32, gintmsk.d32);
774         }
775 #endif
776         if (gahbcfg.b.glblintrmsk)
777                 return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
778         else
779                 return 0;
780
781 }
782
783 /* MACRO for clearing interupt bits in GPWRDN register */
784 #define CLEAR_GPWRDN_INTR(__core_if,__intr) \
785 do { \
786                 gpwrdn_data_t gpwrdn = {.d32=0}; \
787                 gpwrdn.b.__intr = 1; \
788                 DWC_MODIFY_REG32(&__core_if->core_global_regs->gpwrdn, \
789                 0, gpwrdn.d32); \
790 } while (0)
791
792 /**
793  * Common interrupt handler.
794  *
795  * The common interrupts are those that occur in both Host and Device mode.
796  * This handler handles the following interrupts:
797  * - Mode Mismatch Interrupt
798  * - Disconnect Interrupt
799  * - OTG Interrupt
800  * - Connector ID Status Change Interrupt
801  * - Session Request Interrupt.
802  * - Resume / Remote Wakeup Detected Interrupt.
803  * - LPM Transaction Received Interrupt
804  * - ADP Transaction Received Interrupt
805  *
806  */
807 int32_t dwc_otg_handle_common_intr(dwc_otg_core_if_t *core_if)
808 {/**should notice the param of this function**/
809         int retval = 0;
810         gintsts_data_t gintsts;
811
812         if (core_if->lock)
813                 DWC_SPINLOCK(core_if->lock);
814
815         gintsts.d32 = dwc_otg_read_common_intr(core_if);
816         if (gintsts.b.modemismatch) {
817                 retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
818         }
819         if (gintsts.b.otgintr) {
820                 retval |= dwc_otg_handle_otg_intr(core_if);
821         }
822         if (gintsts.b.conidstschng) {
823                 retval |=
824                     dwc_otg_handle_conn_id_status_change_intr(core_if);
825         }
826         if (gintsts.b.disconnect) {
827                 retval |= dwc_otg_handle_disconnect_intr(core_if);
828         }
829         if (gintsts.b.sessreqintr) {
830                 retval |= dwc_otg_handle_session_req_intr(core_if);
831         }
832         if (gintsts.b.wkupintr) {
833                 retval |= dwc_otg_handle_wakeup_detected_intr(core_if);
834         }
835         if (gintsts.b.usbsuspend) {
836                 retval |= dwc_otg_handle_usb_suspend_intr(core_if);
837         }
838 #ifdef CONFIG_USB_DWC_OTG_LPM
839         if (gintsts.b.lpmtranrcvd) {
840                 retval |= dwc_otg_handle_lpm_intr(core_if);
841         }
842 #endif
843         if (gintsts.b.portintr && dwc_otg_is_device_mode(core_if)) {
844                 /* The port interrupt occurs while in device mode with HPRT0
845                  * Port Enable/Disable.
846                  */
847                 gintsts.d32 = 0;
848                 gintsts.b.portintr = 1;
849                 dwc_write_reg32(&core_if->core_global_regs->gintsts,
850                                 gintsts.d32);
851                 retval |= 1;
852         }
853         if (core_if->lock)
854                 DWC_SPINUNLOCK(core_if->lock);
855
856         return retval;
857 }