void w_conn_id_status_change(void *p) { dwc_otg_core_if_t *core_if = p; uint32_t count = 0; gotgctl_data_t gotgctl = {.d32 = 0 }; gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl); DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32); DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts); /* B-Device connector (Device Mode) */ if (gotgctl.b.conidsts) { /* Wait for switch to device mode. */ while (!dwc_otg_is_device_mode(core_if)) { DWC_PRINTF("Waiting for Peripheral Mode, Mode=%s\n", (dwc_otg_is_host_mode(core_if) ? "Host" : "Peripheral")); dwc_mdelay(100); if (++count > 10000) break; } DWC_ASSERT(++count < 10000, "Connection id status change timed out"); core_if->op_state = B_PERIPHERAL; #if defined(CONFIG_BATTERY_NXE2000) otgid_power_control_by_dwc(0); #elif defined(CONFIG_KP_AXP22) axp_otg_power_control(0); #endif dwc_otg_set_prtpower(core_if, 0); core_if->host_flag = 0; dwc_otg_core_init(core_if); dwc_otg_enable_global_interrupts(core_if); cil_pcd_start(core_if); } else { /* A-Device connector (Host Mode) */ while (!dwc_otg_is_host_mode(core_if)) { DWC_PRINTF("Waiting for Host Mode, Mode=%s\n", (dwc_otg_is_host_mode(core_if) ? "Host" : "Peripheral")); dwc_mdelay(100); if (++count > 10000) break; } DWC_ASSERT(++count < 10000, "Connection id status change timed out"); core_if->op_state = A_HOST; /* * Initialize the Core for Host mode. */ core_if->host_flag = 1; dwc_otg_core_init(core_if); dwc_otg_enable_global_interrupts(core_if); cil_hcd_start(core_if); } } /** * 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) { /* * Need to disable SOF interrupt immediately. If switching from device * to host, the PCD interrupt handler won't handle the interrupt if * host mode is already set. The HCD interrupt handler won't get * called if the HCD state is HALT. This means that the interrupt does * not get handled and Linux complains loudly. */ gintmsk_data_t gintmsk = {.d32 = 0 }; gintsts_data_t gintsts = {.d32 = 0 }; gintmsk.b.sofintr = 1; DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0); DWC_DEBUGPL(DBG_CIL, " ++Connector ID Status Change Interrupt++ (%s)\n", (dwc_otg_is_host_mode(core_if) ? "Host" : "Device")); DWC_SPINUNLOCK(core_if->lock); /* * Need to schedule a work, as there are possible DELAY function calls * Release lock before scheduling workq as it holds spinlock during scheduling */ DWC_WORKQ_SCHEDULE(core_if->wq_otg, w_conn_id_status_change, core_if, "connection id status change"); DWC_SPINLOCK(core_if->lock); /* 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) { gintsts_data_t gintsts; #ifndef DWC_HOST_ONLY DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n"); if (dwc_otg_is_device_mode(core_if)) { DWC_PRINTF("SRP: Device mode\n"); } else { hprt0_data_t hprt0; DWC_PRINTF("SRP: Host mode\n"); /* Turn on the port power bit. */ hprt0.d32 = dwc_otg_read_hprt0(core_if); hprt0.b.prtpwr = 1; DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); /* Start the Connection timer. So a message can be displayed * if connect does not occur within 10 seconds. */ cil_hcd_session_start(core_if); } #endif /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.sessreqintr = 1; DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); return 1; } void w_wakeup_detected(void *p) { dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) p; /* * 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 }; #if 0 pcgcctl_data_t pcgcctl = {.d32 = 0 }; /* Restart the Phy Clock */ pcgcctl.b.stoppclk = 1; DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0); dwc_udelay(10); #endif //0 hprt0.d32 = dwc_otg_read_hprt0(core_if); DWC_DEBUGPL(DBG_ANY, "Resume: HPRT0=%0x\n", hprt0.d32); // dwc_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)); cil_hcd_resume(core_if); /** Change to L0 state*/ core_if->lx_state = DWC_OTG_L0; } /** * 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"); DWC_PRINTF("%s lxstate = %d\n", __func__, core_if->lx_state); if (dwc_otg_is_device_mode(core_if)) { 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)); if (core_if->lx_state == DWC_OTG_L2) { #ifdef PARTIAL_POWER_DOWN if (core_if->hwcfg4.b.power_optimiz) { pcgcctl_data_t power = {.d32 = 0 }; power.d32 = DWC_READ_REG32(core_if->pcgcctl); DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n", power.d32); power.b.stoppclk = 0; DWC_WRITE_REG32(core_if->pcgcctl, power.d32); power.b.pwrclmp = 0; DWC_WRITE_REG32(core_if->pcgcctl, power.d32); power.b.rstpdwnmodule = 0; DWC_WRITE_REG32(core_if->pcgcctl, power.d32); } #endif /* Clear the Remote Wakeup Signaling */ dctl.b.rmtwkupsig = 1; DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs-> dctl, dctl.d32, 0); DWC_SPINUNLOCK(core_if->lock); if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); } DWC_SPINLOCK(core_if->lock); } else { glpmcfg_data_t lpmcfg; lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); lpmcfg.b.hird_thres &= (~(1 << 4)); DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32); } /** Change to L0 state*/ core_if->lx_state = DWC_OTG_L0; } else { if (core_if->lx_state != DWC_OTG_L1) { pcgcctl_data_t pcgcctl = {.d32 = 0 }; /* Restart the Phy Clock */ pcgcctl.b.stoppclk = 1; DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0); DWC_TIMER_SCHEDULE(core_if->wkp_timer, 71); } else { /** Change to L0 state*/ core_if->lx_state = DWC_OTG_L0; } }
void w_conn_id_status_change(void *p) { dwc_otg_core_if_t *core_if = p; uint32_t count = 0; gotgctl_data_t gotgctl = {.d32 = 0 }; gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl); DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32); DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts); /* B-Device connector (Device Mode) */ if (gotgctl.b.conidsts) { /* Wait for switch to device mode. */ while (!dwc_otg_is_device_mode(core_if)) { DWC_PRINTF("Waiting for Peripheral Mode, Mode=%s\n", (dwc_otg_is_host_mode(core_if) ? "Host" : "Peripheral")); dwc_mdelay(100); if (++count > 10000) break; } DWC_ASSERT(++count < 10000, "Connection id status change timed out"); #ifdef CONFIG_USB_OTG_UTILS if (core_if->xceiver->otg->set_vbus) core_if->xceiver->otg->set_vbus(core_if->xceiver->otg, false); #endif core_if->op_state = B_PERIPHERAL; dwc_otg_core_init(core_if); dwc_otg_enable_global_interrupts(core_if); cil_pcd_start(core_if); } else { /* A-Device connector (Host Mode) */ while (!dwc_otg_is_host_mode(core_if)) { DWC_PRINTF("Waiting for Host Mode, Mode=%s\n", (dwc_otg_is_host_mode(core_if) ? "Host" : "Peripheral")); dwc_mdelay(100); if (++count > 10000) break; } DWC_ASSERT(++count < 10000, "Connection id status change timed out"); core_if->op_state = A_HOST; /* * Initialize the Core for Host mode. */ dwc_otg_core_init(core_if); dwc_otg_enable_global_interrupts(core_if); cil_hcd_start(core_if); } } /** * 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) { /* * Need to disable SOF interrupt immediately. If switching from device * to host, the PCD interrupt handler won't handle the interrupt if * host mode is already set. The HCD interrupt handler won't get * called if the HCD state is HALT. This means that the interrupt does * not get handled and Linux complains loudly. */ gintmsk_data_t gintmsk = {.d32 = 0 }; gintsts_data_t gintsts = {.d32 = 0 }; gintmsk.b.sofintr = 1; dwc_modify_reg32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0); DWC_DEBUGPL(DBG_CIL, " ++Connector ID Status Change Interrupt++ (%s)\n", (dwc_otg_is_host_mode(core_if) ? "Host" : "Device")); /* * Need to schedule a work, as there are possible DELAY function calls */ DWC_WORKQ_SCHEDULE(core_if->wq_otg, w_conn_id_status_change, core_if, "connection id status change"); /* 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) { hprt0_data_t hprt0; gintsts_data_t gintsts; #ifndef DWC_HOST_ONLY DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n"); if (dwc_otg_is_device_mode(core_if)) { DWC_PRINTF("SRP: Device mode\n"); } else { DWC_PRINTF("SRP: Host mode\n"); /* Turn on the port power bit. */ hprt0.d32 = dwc_otg_read_hprt0(core_if); hprt0.b.prtpwr = 1; dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); #ifdef CONFIG_USB_OTG if (core_if->core_params->otg_supp_enable) { /* Schedule a work item to init the core */ DWC_WORKQ_SCHEDULE(core_if->wq_otg, w_init_core, core_if, "SRP detected"); } else #endif { /* Start the Connection timer. So a message can be displayed * if connect does not occur within connection wait timeout */ cil_hcd_session_start(core_if); } } #endif /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.sessreqintr = 1; dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32); return 1; } void w_wakeup_detected(void *p) { dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *)p; /* * 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 }; #if 0 pcgcctl_data_t pcgcctl = {.d32 = 0 }; /* Restart the Phy Clock */ pcgcctl.b.stoppclk = 1; dwc_modify_reg32(core_if->pcgcctl, pcgcctl.d32, 0); dwc_udelay(10); #endif /*0 */ hprt0.d32 = dwc_otg_read_hprt0(core_if); DWC_DEBUGPL(DBG_ANY, "Resume: HPRT0=%0x\n", hprt0.d32); /* dwc_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)); cil_hcd_resume(core_if); /** Change to L0 state*/ core_if->lx_state = DWC_OTG_L0; } void w_a_periph_done(void *p) { dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *)p; if (core_if->op_state == A_PERIPHERAL) { /* Clear the a_peripheral flag, back to a_host. */ cil_pcd_stop(core_if); cil_hcd_start(core_if); core_if->op_state = A_HOST; } } void w_peri_suspend_powersaving(void *p) { dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *)p; if (core_if) { /* Clear DWC Core interrupts before PHY suspend, this is because to prevent DWC core Hangs while accessing some registers with PHY suspended if at all we get some core interrupts when phy in suspend*/ /* Clear any pending OTG Interrupts */ dwc_write_reg32(&core_if->core_global_regs->gotgint, 0xFFFFFFFF); /* Clear any pending interrupts */ dwc_write_reg32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF); dwc_otg_disable_global_interrupts(core_if); /* Suspend trasceiver */ usb_phy_set_suspend(core_if->xceiver, 1); } }