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 47inline 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 */ 62int32_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 */ 79static 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 */ 89static 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 */ 99static 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 */ 110static 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 */ 121static 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 */ 131static 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 */ 141static 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 */ 151static 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 */ 165int32_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 313void w_conn_id_status_change(void *p) 314{ 315 dwc_otg_core_if_t *core_if = p; 316 317#else 318 319void 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 */ 376int32_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 */ 416int32_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) 450void w_wakeup_detected(void *p) 451{ 452 dwc_otg_core_if_t* core_if = p; 453 454#else 455 456void 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 */ 488int32_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 */ 546int32_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 */ 602int32_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 */ 663static 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 */ 707int32_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} 751