1/* ========================================================================== 2 * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_cil_intr.c $ 3 * $Revision: 1.1.1.1 $ 4 * $Date: 2009-04-17 06:15:34 $ 5 * $Change: 553126 $ 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_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, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint.d32, 176 op_state_str(_core_if)); 177 //DWC_DEBUGPL(DBG_CIL, "gotgctl=%08x\n", gotgctl.d32 ); 178 179 if (gotgint.b.sesenddet) { 180 DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " 181 "Session End Detected++ (%s)\n", 182 op_state_str(_core_if)); 183 gotgctl.d32 = dwc_read_reg32( &global_regs->gotgctl); 184 185 if (_core_if->op_state == B_HOST) { 186 pcd_start( _core_if ); 187 _core_if->op_state = B_PERIPHERAL; 188 } else { 189 /* If not B_HOST and Device HNP still set. HNP 190 * Did not succeed!*/ 191 if (gotgctl.b.devhnpen) { 192 DWC_DEBUGPL(DBG_ANY, "Session End Detected\n"); 193 DWC_ERROR( "Device Not Connected/Responding!\n" ); 194 } 195 196 /* If Session End Detected the B-Cable has 197 * been disconnected. */ 198 /* Reset PCD and Gadget driver to a 199 * clean state. */ 200 pcd_stop(_core_if); 201 } 202 gotgctl.d32 = 0; 203 gotgctl.b.devhnpen = 1; 204 dwc_modify_reg32( &global_regs->gotgctl, 205 gotgctl.d32, 0); 206 } 207 if (gotgint.b.sesreqsucstschng) { 208 DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " 209 "Session Reqeust Success Status Change++\n"); 210 gotgctl.d32 = dwc_read_reg32( &global_regs->gotgctl); 211 if (gotgctl.b.sesreqscs) { 212 if ((_core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) && 213 (_core_if->core_params->i2c_enable)) { 214 _core_if->srp_success = 1; 215 } 216 else { 217 pcd_resume( _core_if ); 218 /* Clear Session Request */ 219 gotgctl.d32 = 0; 220 gotgctl.b.sesreq = 1; 221 dwc_modify_reg32( &global_regs->gotgctl, 222 gotgctl.d32, 0); 223 } 224 } 225 } 226 if (gotgint.b.hstnegsucstschng) { 227 /* Print statements during the HNP interrupt handling 228 * can cause it to fail.*/ 229 gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl); 230 if (gotgctl.b.hstnegscs) { 231 if (dwc_otg_is_host_mode(_core_if) ) { 232 _core_if->op_state = B_HOST; 233 /* 234 * Need to disable SOF interrupt immediately. 235 * When switching from device to host, the PCD 236 * interrupt handler won't handle the 237 * interrupt if host mode is already set. The 238 * HCD interrupt handler won't get called if 239 * the HCD state is HALT. This means that the 240 * interrupt does not get handled and Linux 241 * complains loudly. 242 */ 243 gintmsk.d32 = 0; 244 gintmsk.b.sofintr = 1; 245 dwc_modify_reg32(&global_regs->gintmsk, 246 gintmsk.d32, 0); 247 pcd_stop(_core_if); 248 /* 249 * Initialize the Core for Host mode. 250 */ 251 hcd_start( _core_if ); 252 _core_if->op_state = B_HOST; 253 } 254 } else { 255 gotgctl.d32 = 0; 256 gotgctl.b.hnpreq = 1; 257 gotgctl.b.devhnpen = 1; 258 dwc_modify_reg32( &global_regs->gotgctl, 259 gotgctl.d32, 0); 260 DWC_DEBUGPL( DBG_ANY, "HNP Failed\n"); 261 DWC_ERROR( "Device Not Connected/Responding\n" ); 262 } 263 } 264 if (gotgint.b.hstnegdet) { 265 /* The disconnect interrupt is set at the same time as 266 * Host Negotiation Detected. During the mode 267 * switch all interrupts are cleared so the disconnect 268 * interrupt handler will not get executed. 269 */ 270 DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " 271 "Host Negotiation Detected++ (%s)\n", 272 (dwc_otg_is_host_mode(_core_if)?"Host":"Device")); 273 if (dwc_otg_is_device_mode(_core_if)){ 274 DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n",_core_if->op_state); 275 hcd_disconnect( _core_if ); 276 pcd_start( _core_if ); 277 _core_if->op_state = A_PERIPHERAL; 278 } else { 279 /* 280 * Need to disable SOF interrupt immediately. When 281 * switching from device to host, the PCD interrupt 282 * handler won't handle the interrupt if host mode is 283 * already set. The HCD interrupt handler won't get 284 * called if the HCD state is HALT. This means that 285 * the interrupt does not get handled and Linux 286 * complains loudly. 287 */ 288 gintmsk.d32 = 0; 289 gintmsk.b.sofintr = 1; 290 dwc_modify_reg32(&global_regs->gintmsk, 291 gintmsk.d32, 0); 292 pcd_stop( _core_if ); 293 hcd_start( _core_if ); 294 _core_if->op_state = A_HOST; 295 } 296 } 297 if (gotgint.b.adevtoutchng) { 298 DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " 299 "A-Device Timeout Change++\n"); 300 } 301 if (gotgint.b.debdone) { 302 DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " 303 "Debounce Done++\n"); 304 } 305 306 /* Clear GOTGINT */ 307 dwc_write_reg32 (&_core_if->core_global_regs->gotgint, gotgint.d32); 308 309 return 1; 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 */ 323int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t *_core_if) 324{ 325 uint32_t count = 0; 326 327 gintsts_data_t gintsts = { .d32 = 0 }; 328 gintmsk_data_t gintmsk = { .d32 = 0 }; 329 gotgctl_data_t gotgctl = { .d32 = 0 }; 330 331 /* 332 * Need to disable SOF interrupt immediately. If switching from device 333 * to host, the PCD interrupt handler won't handle the interrupt if 334 * host mode is already set. The HCD interrupt handler won't get 335 * called if the HCD state is HALT. This means that the interrupt does 336 * not get handled and Linux complains loudly. 337 */ 338 gintmsk.b.sofintr = 1; 339 dwc_modify_reg32(&_core_if->core_global_regs->gintmsk, gintmsk.d32, 0); 340 341 DWC_DEBUGPL(DBG_CIL, " ++Connector ID Status Change Interrupt++ (%s)\n", 342 (dwc_otg_is_host_mode(_core_if)?"Host":"Device")); 343 gotgctl.d32 = dwc_read_reg32(&_core_if->core_global_regs->gotgctl); 344 DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32); 345 DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts); 346 347 /* B-Device connector (Device Mode) */ 348 if (gotgctl.b.conidsts) { 349 /* Wait for switch to device mode. */ 350 while (!dwc_otg_is_device_mode(_core_if) ){ 351 DWC_PRINT("Waiting for Peripheral Mode, Mode=%s\n", 352 (dwc_otg_is_host_mode(_core_if)?"Host":"Peripheral")); 353 MDELAY(100); 354 if (++count > 10000) *(uint32_t*)NULL=0; 355 } 356 _core_if->op_state = B_PERIPHERAL; 357 dwc_otg_core_init(_core_if); 358 dwc_otg_enable_global_interrupts(_core_if); 359 pcd_start( _core_if ); 360 } else { 361 /* A-Device connector (Host Mode) */ 362 while (!dwc_otg_is_host_mode(_core_if) ) { 363 DWC_PRINT("Waiting for Host Mode, Mode=%s\n", 364 (dwc_otg_is_host_mode(_core_if)?"Host":"Peripheral")); 365 MDELAY(100); 366 if (++count > 10000) *(uint32_t*)NULL=0; 367 } 368 _core_if->op_state = A_HOST; 369 /* 370 * Initialize the Core for Host mode. 371 */ 372 dwc_otg_core_init(_core_if); 373 dwc_otg_enable_global_interrupts(_core_if); 374 hcd_start( _core_if ); 375 } 376 377 /* Set flag and clear interrupt */ 378 gintsts.b.conidstschng = 1; 379 dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); 380 381 return 1; 382} 383 384/** 385 * This interrupt indicates that a device is initiating the Session 386 * Request Protocol to request the host to turn on bus power so a new 387 * session can begin. The handler responds by turning on bus power. If 388 * the DWC_otg controller is in low power mode, the handler brings the 389 * controller out of low power mode before turning on bus power. 390 * 391 * @param _core_if Programming view of DWC_otg controller. 392 */ 393int32_t dwc_otg_handle_session_req_intr( dwc_otg_core_if_t *_core_if ) 394{ 395#ifndef DWC_HOST_ONLY // winder 396 hprt0_data_t hprt0; 397#endif 398 gintsts_data_t gintsts; 399 400#ifndef DWC_HOST_ONLY 401 DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n"); 402 403 if (dwc_otg_is_device_mode(_core_if) ) { 404 DWC_PRINT("SRP: Device mode\n"); 405 } else { 406 DWC_PRINT("SRP: Host mode\n"); 407 408 /* Turn on the port power bit. */ 409 hprt0.d32 = dwc_otg_read_hprt0( _core_if ); 410 hprt0.b.prtpwr = 1; 411 dwc_write_reg32(_core_if->host_if->hprt0, hprt0.d32); 412 413 /* Start the Connection timer. So a message can be displayed 414 * if connect does not occur within 10 seconds. */ 415 hcd_session_start( _core_if ); 416 } 417#endif 418 419 /* Clear interrupt */ 420 gintsts.d32 = 0; 421 gintsts.b.sessreqintr = 1; 422 dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); 423 424 return 1; 425} 426 427/** 428 * This interrupt indicates that the DWC_otg controller has detected a 429 * resume or remote wakeup sequence. If the DWC_otg controller is in 430 * low power mode, the handler must brings the controller out of low 431 * power mode. The controller automatically begins resume 432 * signaling. The handler schedules a time to stop resume signaling. 433 */ 434int32_t dwc_otg_handle_wakeup_detected_intr( dwc_otg_core_if_t *_core_if ) 435{ 436 gintsts_data_t gintsts; 437 438 DWC_DEBUGPL(DBG_ANY, "++Resume and Remote Wakeup Detected Interrupt++\n"); 439 440 if (dwc_otg_is_device_mode(_core_if) ) { 441 dctl_data_t dctl = {.d32=0}; 442 DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", 443 dwc_read_reg32( &_core_if->dev_if->dev_global_regs->dsts)); 444#ifdef PARTIAL_POWER_DOWN 445 if (_core_if->hwcfg4.b.power_optimiz) { 446 pcgcctl_data_t power = {.d32=0}; 447 448 power.d32 = dwc_read_reg32( _core_if->pcgcctl ); 449 DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n", power.d32); 450 451 power.b.stoppclk = 0; 452 dwc_write_reg32( _core_if->pcgcctl, power.d32); 453 454 power.b.pwrclmp = 0; 455 dwc_write_reg32( _core_if->pcgcctl, power.d32); 456 457 power.b.rstpdwnmodule = 0; 458 dwc_write_reg32( _core_if->pcgcctl, power.d32); 459 } 460#endif 461 /* Clear the Remote Wakeup Signalling */ 462 dctl.b.rmtwkupsig = 1; 463 dwc_modify_reg32( &_core_if->dev_if->dev_global_regs->dctl, 464 dctl.d32, 0 ); 465 466 if (_core_if->pcd_cb && _core_if->pcd_cb->resume_wakeup) { 467 _core_if->pcd_cb->resume_wakeup( _core_if->pcd_cb->p ); 468 } 469 470 } else { 471 /* 472 * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms 473 * so that OPT tests pass with all PHYs). 474 */ 475 hprt0_data_t hprt0 = {.d32=0}; 476 pcgcctl_data_t pcgcctl = {.d32=0}; 477 /* Restart the Phy Clock */ 478 pcgcctl.b.stoppclk = 1; 479 dwc_modify_reg32(_core_if->pcgcctl, pcgcctl.d32, 0); 480 UDELAY(10); 481 482 /* Now wait for 70 ms. */ 483 hprt0.d32 = dwc_otg_read_hprt0( _core_if ); 484 DWC_DEBUGPL(DBG_ANY,"Resume: HPRT0=%0x\n", hprt0.d32); 485 MDELAY(70); 486 hprt0.b.prtres = 0; /* Resume */ 487 dwc_write_reg32(_core_if->host_if->hprt0, hprt0.d32); 488 DWC_DEBUGPL(DBG_ANY,"Clear Resume: HPRT0=%0x\n", dwc_read_reg32(_core_if->host_if->hprt0)); 489 } 490 491 /* Clear interrupt */ 492 gintsts.d32 = 0; 493 gintsts.b.wkupintr = 1; 494 dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); 495 496 return 1; 497} 498 499/** 500 * This interrupt indicates that a device has been disconnected from 501 * the root port. 502 */ 503int32_t dwc_otg_handle_disconnect_intr( dwc_otg_core_if_t *_core_if) 504{ 505 gintsts_data_t gintsts; 506 507 DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n", 508 (dwc_otg_is_host_mode(_core_if)?"Host":"Device"), 509 op_state_str(_core_if)); 510 511/** @todo Consolidate this if statement. */ 512#ifndef DWC_HOST_ONLY 513 if (_core_if->op_state == B_HOST) { 514 /* If in device mode Disconnect and stop the HCD, then 515 * start the PCD. */ 516 hcd_disconnect( _core_if ); 517 pcd_start( _core_if ); 518 _core_if->op_state = B_PERIPHERAL; 519 } else if (dwc_otg_is_device_mode(_core_if)) { 520 gotgctl_data_t gotgctl = { .d32 = 0 }; 521 gotgctl.d32 = dwc_read_reg32(&_core_if->core_global_regs->gotgctl); 522 if (gotgctl.b.hstsethnpen==1) { 523 /* Do nothing, if HNP in process the OTG 524 * interrupt "Host Negotiation Detected" 525 * interrupt will do the mode switch. 526 */ 527 } else if (gotgctl.b.devhnpen == 0) { 528 /* If in device mode Disconnect and stop the HCD, then 529 * start the PCD. */ 530 hcd_disconnect( _core_if ); 531 pcd_start( _core_if ); 532 _core_if->op_state = B_PERIPHERAL; 533 } else { 534 DWC_DEBUGPL(DBG_ANY,"!a_peripheral && !devhnpen\n"); 535 } 536 } else { 537 if (_core_if->op_state == A_HOST) { 538 /* A-Cable still connected but device disconnected. */ 539 hcd_disconnect( _core_if ); 540 } 541 } 542#endif 543/* Without OTG, we should use the disconnect function!? winder added.*/ 544#if 1 // NO OTG, so host only!! 545 hcd_disconnect( _core_if ); 546#endif 547 548 gintsts.d32 = 0; 549 gintsts.b.disconnect = 1; 550 dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); 551 return 1; 552} 553/** 554 * This interrupt indicates that SUSPEND state has been detected on 555 * the USB. 556 * 557 * For HNP the USB Suspend interrupt signals the change from 558 * "a_peripheral" to "a_host". 559 * 560 * When power management is enabled the core will be put in low power 561 * mode. 562 */ 563int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t *_core_if ) 564{ 565 dsts_data_t dsts; 566 gintsts_data_t gintsts; 567 568 //805141:<IFTW-fchang>.removed DWC_DEBUGPL(DBG_ANY,"USB SUSPEND\n"); 569 570 if (dwc_otg_is_device_mode( _core_if ) ) { 571 /* Check the Device status register to determine if the Suspend 572 * state is active. */ 573 dsts.d32 = dwc_read_reg32( &_core_if->dev_if->dev_global_regs->dsts); 574 DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32); 575 DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d " 576 "HWCFG4.power Optimize=%d\n", 577 dsts.b.suspsts, _core_if->hwcfg4.b.power_optimiz); 578 579 580#ifdef PARTIAL_POWER_DOWN 581/** @todo Add a module parameter for power management. */ 582 583 if (dsts.b.suspsts && _core_if->hwcfg4.b.power_optimiz) { 584 pcgcctl_data_t power = {.d32=0}; 585 DWC_DEBUGPL(DBG_CIL, "suspend\n"); 586 587 power.b.pwrclmp = 1; 588 dwc_write_reg32( _core_if->pcgcctl, power.d32); 589 590 power.b.rstpdwnmodule = 1; 591 dwc_modify_reg32( _core_if->pcgcctl, 0, power.d32); 592 593 power.b.stoppclk = 1; 594 dwc_modify_reg32( _core_if->pcgcctl, 0, power.d32); 595 596 } else { 597 DWC_DEBUGPL(DBG_ANY,"disconnect?\n"); 598 } 599#endif 600 /* PCD callback for suspend. */ 601 pcd_suspend(_core_if); 602 } else { 603 if (_core_if->op_state == A_PERIPHERAL) { 604 DWC_DEBUGPL(DBG_ANY,"a_peripheral->a_host\n"); 605 /* Clear the a_peripheral flag, back to a_host. */ 606 pcd_stop( _core_if ); 607 hcd_start( _core_if ); 608 _core_if->op_state = A_HOST; 609 } 610 } 611 612 /* Clear interrupt */ 613 gintsts.d32 = 0; 614 gintsts.b.usbsuspend = 1; 615 dwc_write_reg32( &_core_if->core_global_regs->gintsts, gintsts.d32); 616 617 return 1; 618} 619 620 621/** 622 * This function returns the Core Interrupt register. 623 */ 624static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t *_core_if) 625{ 626 gintsts_data_t gintsts; 627 gintmsk_data_t gintmsk; 628 gintmsk_data_t gintmsk_common = {.d32=0}; 629 gintmsk_common.b.wkupintr = 1; 630 gintmsk_common.b.sessreqintr = 1; 631 gintmsk_common.b.conidstschng = 1; 632 gintmsk_common.b.otgintr = 1; 633 gintmsk_common.b.modemismatch = 1; 634 gintmsk_common.b.disconnect = 1; 635 gintmsk_common.b.usbsuspend = 1; 636 /** @todo: The port interrupt occurs while in device 637 * mode. Added code to CIL to clear the interrupt for now! 638 */ 639 gintmsk_common.b.portintr = 1; 640 641 gintsts.d32 = dwc_read_reg32(&_core_if->core_global_regs->gintsts); 642 gintmsk.d32 = dwc_read_reg32(&_core_if->core_global_regs->gintmsk); 643#ifdef DEBUG 644 /* if any common interrupts set */ 645 if (gintsts.d32 & gintmsk_common.d32) { 646 DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n", 647 gintsts.d32, gintmsk.d32); 648 } 649#endif 650 651 return ((gintsts.d32 & gintmsk.d32 ) & gintmsk_common.d32); 652 653} 654 655/** 656 * Common interrupt handler. 657 * 658 * The common interrupts are those that occur in both Host and Device mode. 659 * This handler handles the following interrupts: 660 * - Mode Mismatch Interrupt 661 * - Disconnect Interrupt 662 * - OTG Interrupt 663 * - Connector ID Status Change Interrupt 664 * - Session Request Interrupt. 665 * - Resume / Remote Wakeup Detected Interrupt. 666 * 667 */ 668extern int32_t dwc_otg_handle_common_intr( dwc_otg_core_if_t *_core_if ) 669{ 670 int retval = 0; 671 gintsts_data_t gintsts; 672 673 gintsts.d32 = dwc_otg_read_common_intr(_core_if); 674 675 if (gintsts.b.modemismatch) { 676 retval |= dwc_otg_handle_mode_mismatch_intr( _core_if ); 677 } 678 if (gintsts.b.otgintr) { 679 retval |= dwc_otg_handle_otg_intr( _core_if ); 680 } 681 if (gintsts.b.conidstschng) { 682 retval |= dwc_otg_handle_conn_id_status_change_intr( _core_if ); 683 } 684 if (gintsts.b.disconnect) { 685 retval |= dwc_otg_handle_disconnect_intr( _core_if ); 686 } 687 if (gintsts.b.sessreqintr) { 688 retval |= dwc_otg_handle_session_req_intr( _core_if ); 689 } 690 if (gintsts.b.wkupintr) { 691 retval |= dwc_otg_handle_wakeup_detected_intr( _core_if ); 692 } 693 if (gintsts.b.usbsuspend) { 694 retval |= dwc_otg_handle_usb_suspend_intr( _core_if ); 695 } 696 if (gintsts.b.portintr && dwc_otg_is_device_mode(_core_if)) { 697 /* The port interrupt occurs while in device mode with HPRT0 698 * Port Enable/Disable. 699 */ 700 gintsts.d32 = 0; 701 gintsts.b.portintr = 1; 702 dwc_write_reg32(&_core_if->core_global_regs->gintsts, 703 gintsts.d32); 704 retval |= 1; 705 706 } 707 return retval; 708} 709