/***************************************************************************** ** FILE NAME : dwc_otg_cil_intr.c ** PROJECT : USB Host and Device driver ** MODULES : USB Host and Device driver ** SRC VERSION : 2.0 ** DATE : 1/March/2008 ** AUTHOR : Chen, Howard based on Synopsys Original ** DESCRIPTION : The Core Interface Layer provides basic services for accessing and ** managing the DWC_otg hardware. These services are used by both the ** Host Controller Driver and the Peripheral Controller Driver. ** ** The CIL manages the memory map for the core so that the HCD and PCD ** don't have to do this separately. It also handles basic tasks like ** reading/writing the registers and data FIFOs in the controller. ** Some of the data access functions provide encapsulation of several ** operations required to perform a task, such as writing multiple ** registers to start a transfer. Finally, the CIL performs basic ** services that are not specific to either the host or device modes ** of operation. These services include management of the OTG Host ** Negotiation Protocol (HNP) and Session Request Protocol (SRP). A ** Diagnostic API is also provided to allow testing of the controller ** hardware. ** FUNCTIONS : ** COMPILER : gcc ** REFERENCE : ** COPYRIGHT : ** Version Control Section ** ** $Author$ ** $Date$ ** $Revisions$ ** $Log$ Revision history *****************************************************************************/ /*! \file dwc_otg_cil_intr.c \brief This file contains the Common Interrupt handlers. */ #include #include "ifxusb_version.h" #include #include "dwc_otg_plat.h" #include "dwc_otg_regs.h" #include "dwc_otg_cil.h" #include "dwc_otg_driver.h" #ifndef container_of #define container_of list_entry #endif /** This function will log a debug message * * @param _core_if Programming view of DWC_otg controller. */ int32_t dwc_otg_handle_mode_mismatch_intr (dwc_otg_core_if_t *_core_if) { gintsts_data_t gintsts; DWC_WARN("Mode Mismatch Interrupt: currently in %s mode\n", dwc_otg_mode(_core_if) ? "Host" : "Device"); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.modemismatch = 1; dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /* Start the HCD. Helper function for using the HCD callbacks. * * @param _core_if Programming view of DWC_otg controller. */ #ifdef DWC_IS_HOST static inline void hcd_start( dwc_otg_core_if_t *_core_if ) { if (_core_if->hcd_cb && _core_if->hcd_cb->start) { _core_if->hcd_cb->start( _core_if->hcd_cb->p ); } } /* Stop the HCD. Helper function for using the HCD callbacks. * * @param _core_if Programming view of DWC_otg controller. */ static inline void hcd_stop( dwc_otg_core_if_t *_core_if ) { if (_core_if->hcd_cb && _core_if->hcd_cb->stop) { _core_if->hcd_cb->stop( _core_if->hcd_cb->p ); } } /* Disconnect the HCD. Helper function for using the HCD callbacks. * * @param _core_if Programming view of DWC_otg controller. */ static inline void hcd_disconnect( dwc_otg_core_if_t *_core_if ) { if (_core_if->hcd_cb && _core_if->hcd_cb->disconnect) { _core_if->hcd_cb->disconnect( _core_if->hcd_cb->p ); } } /* Inform the HCD the a New Session has begun. Helper function for * using the HCD callbacks. * * @param _core_if Programming view of DWC_otg controller. */ static inline void hcd_session_start( dwc_otg_core_if_t *_core_if ) { if (_core_if->hcd_cb && _core_if->hcd_cb->session_start) { _core_if->hcd_cb->session_start( _core_if->hcd_cb->p ); } } #endif #ifdef DWC_IS_DEVICE /* Start the PCD. Helper function for using the PCD callbacks. * * @param _core_if Programming view of DWC_otg controller. */ static inline void pcd_start( dwc_otg_core_if_t *_core_if ) { if (_core_if->pcd_cb && _core_if->pcd_cb->start ) { _core_if->pcd_cb->start( _core_if->pcd_cb->p ); } } /* Stop the PCD. Helper function for using the PCD callbacks. * * @param _core_if Programming view of DWC_otg controller. */ static inline void pcd_stop( dwc_otg_core_if_t *_core_if ) { if (_core_if->pcd_cb && _core_if->pcd_cb->stop ) { _core_if->pcd_cb->stop( _core_if->pcd_cb->p ); } } /* Suspend the PCD. Helper function for using the PCD callbacks. * * @param _core_if Programming view of DWC_otg controller. */ static inline void pcd_suspend( dwc_otg_core_if_t *_core_if ) { if (_core_if->pcd_cb && _core_if->pcd_cb->suspend ) { _core_if->pcd_cb->suspend( _core_if->pcd_cb->p ); } } /* Resume the PCD. Helper function for using the PCD callbacks. * * @param _core_if Programming view of DWC_otg controller. */ static inline void pcd_resume( dwc_otg_core_if_t *_core_if ) { if (_core_if->pcd_cb && _core_if->pcd_cb->resume_wakeup ) { _core_if->pcd_cb->resume_wakeup( _core_if->pcd_cb->p ); } } #endif /* * This function handles the OTG Interrupts. It reads the OTG * Interrupt Register (GOTGINT) to determine what interrupt has * occurred. * * @param _core_if Programming view of DWC_otg controller. */ int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t *_core_if) { dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs; gotgint_data_t gotgint; gotgint.d32 = dwc_read_reg32( &global_regs->gotgint); /* Clear GOTGINT */ dwc_write_reg32 (&_core_if->core_global_regs->gotgint, gotgint.d32); return 1; } /* * This function handles the Connector ID Status Change Interrupt. It * reads the OTG Interrupt Register (GOTCTL) to determine whether this * is a Device to Host Mode transition or a Host Mode to Device * Transition. * * This only occurs when the cable is connected/removed from the PHY * connector. * * @param _core_if Programming view of DWC_otg controller. */ int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t *_core_if) { gintsts_data_t gintsts = { .d32 = 0 }; /* Set flag and clear interrupt */ gintsts.b.conidstschng = 1; dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /* * This interrupt indicates that a device is initiating the Session * Request Protocol to request the host to turn on bus power so a new * session can begin. The handler responds by turning on bus power. If * the DWC_otg controller is in low power mode, the handler brings the * controller out of low power mode before turning on bus power. * * @param _core_if Programming view of DWC_otg controller. */ int32_t dwc_otg_handle_session_req_intr( dwc_otg_core_if_t *_core_if ) { /* Clear interrupt */ gintsts_data_t gintsts = { .d32 = 0 }; gintsts.b.sessreqintr = 1; dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /* * This interrupt indicates that the DWC_otg controller has detected a * resume or remote wakeup sequence. If the DWC_otg controller is in * low power mode, the handler must brings the controller out of low * power mode. The controller automatically begins resume * signaling. The handler schedules a time to stop resume signaling. */ int32_t dwc_otg_handle_wakeup_detected_intr( dwc_otg_core_if_t *_core_if ) { gintsts_data_t gintsts; DWC_DEBUGPL(DBG_ANY, "++Resume and Remote Wakeup Detected Interrupt++\n"); #ifdef DWC_IS_DEVICE { dctl_data_t dctl = {.d32=0}; DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dwc_read_reg32( &_core_if->dev_if->dev_global_regs->dsts)); /* Clear the Remote Wakeup Signalling */ dctl.b.rmtwkupsig = 1; dwc_modify_reg32( &_core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0 ); } #endif #ifdef DWC_IS_HOST { /* * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms * so that OPT tests pass with all PHYs). */ hprt0_data_t hprt0 = {.d32=0}; pcgcctl_data_t pcgcctl = {.d32=0}; /* Restart the Phy Clock */ pcgcctl.b.stoppclk = 1; dwc_modify_reg32(_core_if->pcgcctl, pcgcctl.d32, 0); UDELAY(10); /* Now wait for 70 ms. */ hprt0.d32 = dwc_otg_read_hprt0( _core_if ); DWC_DEBUGPL(DBG_ANY,"Resume: HPRT0=%0x\n", hprt0.d32); MDELAY(70); hprt0.b.prtres = 0; /* Resume */ dwc_write_reg32(_core_if->host_if->hprt0, hprt0.d32); DWC_DEBUGPL(DBG_ANY,"Clear Resume: HPRT0=%0x\n", dwc_read_reg32(_core_if->host_if->hprt0)); } #endif /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.wkupintr = 1; dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /* * This interrupt indicates that a device has been disconnected from * the root port. */ int32_t dwc_otg_handle_disconnect_intr( dwc_otg_core_if_t *_core_if) { gintsts_data_t gintsts; #ifdef DWC_IS_HOST hcd_disconnect( _core_if ); #endif gintsts.d32 = 0; gintsts.b.disconnect = 1; dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /* * This interrupt indicates that SUSPEND state has been detected on * the USB. * * For HNP the USB Suspend interrupt signals the change from * "a_peripheral" to "a_host". * * When power management is enabled the core will be put in low power * mode. */ int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t *_core_if ) { gintsts_data_t gintsts; #ifdef DWC_IS_DEVICE dsts_data_t dsts; #endif DWC_WARN("USB SUSPEND RECEIVED!\n"); #ifdef DWC_IS_DEVICE dsts.d32 = dwc_read_reg32( &_core_if->dev_if->dev_global_regs->dsts); DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32); DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d " "HWCFG4.power Optimize=%d\n", dsts.b.suspsts, _core_if->hwcfg4.b.power_optimiz); /* PCD callback for suspend. */ /* pcd_suspend(_core_if); -- 3M fixed WHQL issue */ #endif /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.usbsuspend = 1; dwc_write_reg32( &_core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /* * This function returns the Core Interrupt register. */ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t *_core_if) { gintsts_data_t gintsts; gintmsk_data_t gintmsk; gintmsk_data_t gintmsk_common = {.d32=0}; gintmsk_common.b.wkupintr = 1; gintmsk_common.b.sessreqintr = 1; gintmsk_common.b.conidstschng = 1; gintmsk_common.b.otgintr = 1; gintmsk_common.b.modemismatch = 1; gintmsk_common.b.disconnect = 1; gintmsk_common.b.usbsuspend = 1; gintmsk_common.b.portintr = 1; gintsts.d32 = dwc_read_reg32(&_core_if->core_global_regs->gintsts); gintmsk.d32 = dwc_read_reg32(&_core_if->core_global_regs->gintmsk); #ifdef DEBUG /* if any common interrupts set */ if (gintsts.d32 & gintmsk_common.d32) { DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n", gintsts.d32, gintmsk.d32); } #endif return ((gintsts.d32 & gintmsk.d32 ) & gintmsk_common.d32); } struct tasklet_struct dwc_otg_common_irq_tasklet [7]; /* * Common interrupt handler. * * The common interrupts are those that occur in both Host and Device mode. * This handler handles the following interrupts: * - Mode Mismatch Interrupt * - Disconnect Interrupt * - OTG Interrupt * - Connector ID Status Change Interrupt * - Session Request Interrupt. * - Resume / Remote Wakeup Detected Interrupt. * */ extern int32_t dwc_otg_handle_common_intr( dwc_otg_core_if_t *_core_if ) { int retval = 0; gintsts_data_t gintsts; gintsts.d32 = dwc_otg_read_common_intr(_core_if); if (gintsts.b.modemismatch) retval |= dwc_otg_handle_mode_mismatch_intr( _core_if ); if (gintsts.b.otgintr) retval |= dwc_otg_handle_otg_intr( _core_if ); if (gintsts.b.conidstschng) retval |= dwc_otg_handle_conn_id_status_change_intr( _core_if ); if (gintsts.b.disconnect) retval |= dwc_otg_handle_disconnect_intr( _core_if ); if (gintsts.b.sessreqintr) retval |= dwc_otg_handle_session_req_intr( _core_if ); if (gintsts.b.wkupintr) retval |= dwc_otg_handle_wakeup_detected_intr( _core_if ); if (gintsts.b.usbsuspend) retval |= dwc_otg_handle_usb_suspend_intr( _core_if ); #ifdef DWC_IS_DEVICE if (gintsts.b.portintr) { /* The port interrupt occurs while in device mode with HPRT0 * Port Enable/Disable. */ gintsts.d32 = 0; gintsts.b.portintr = 1; dwc_write_reg32(&_core_if->core_global_regs->gintsts, gintsts.d32); retval |= 1; } #endif return retval; }