bool CHNLSM_ISR(struct WMD_DEV_CONTEXT *pDevContext, bool *pfSchedDPC, u16 *pwIntrVal) { struct CFG_HOSTRES resources; u32 numMbxMsg; u32 mbxValue; DBG_Trace(DBG_ENTER, "CHNLSM_ISR(0x%x)\n", pDevContext); CFG_GetHostResources((struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); HW_MBOX_NumMsgGet(resources.dwMboxBase, MBOX_DSP2ARM, &numMbxMsg); if (numMbxMsg > 0) { HW_MBOX_MsgRead(resources.dwMboxBase, MBOX_DSP2ARM, &mbxValue); HW_MBOX_EventAck(resources.dwMboxBase, MBOX_DSP2ARM, HW_MBOX_U0_ARM, HW_MBOX_INT_NEW_MSG); DBG_Trace(DBG_LEVEL3, "Read %x from Mailbox\n", mbxValue); *pwIntrVal = (u16) mbxValue; } /* Set *pfSchedDPC to true; */ *pfSchedDPC = true; return true; }
/* * ======== WMD_CHNL_Destroy ======== * Purpose: * Close all open channels, and destroy the channel manager. */ DSP_STATUS WMD_CHNL_Destroy(struct CHNL_MGR *hChnlMgr) { DSP_STATUS status = DSP_SOK; struct CHNL_MGR *pChnlMgr = hChnlMgr; u32 iChnl; if (MEM_IsValidHandle(hChnlMgr, CHNL_MGRSIGNATURE)) { /* Close all open channels: */ for (iChnl = 0; iChnl < pChnlMgr->cChannels; iChnl++) { status = WMD_CHNL_Close(pChnlMgr->apChannel[iChnl]); if (DSP_FAILED(status)) DBG_Trace(DBG_LEVEL7, "Error in CHNL_Close " "status 0x%x\n", status); } /* release critical section */ if (pChnlMgr->hCSObj) SYNC_DeleteCS(pChnlMgr->hCSObj); /* Free channel manager object: */ kfree(pChnlMgr->apChannel); /* Set hChnlMgr to NULL in device object. */ DEV_SetChnlMgr(pChnlMgr->hDevObject, NULL); /* Free this Chnl Mgr object: */ MEM_FreeObject(hChnlMgr); } else { status = DSP_EHANDLE; } return status; }
DSP_STATUS CHNLSM_DisableInterrupt(struct WMD_DEV_CONTEXT *pDevContext) { struct CFG_HOSTRES resources; DBG_Trace(DBG_ENTER, "CHNLSM_DisableInterrupt(0x%x)\n", pDevContext); CFG_GetHostResources((struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); HW_MBOX_EventDisable(resources.dwMboxBase, MBOX_DSP2ARM, MBOX_ARM, HW_MBOX_INT_NEW_MSG); return DSP_SOK; }
DSP_STATUS CHNLSM_EnableInterrupt(struct WMD_DEV_CONTEXT *pDevContext) { DSP_STATUS status = DSP_SOK; u32 numMbxMsg; u32 mbxValue; struct CFG_HOSTRES resources; u32 devType; struct IO_MGR *hIOMgr; DBG_Trace(DBG_ENTER, "CHNLSM_EnableInterrupt(0x%x)\n", pDevContext); /* Read the messages in the mailbox until the message queue is empty */ CFG_GetHostResources((struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); DEV_GetDevType(pDevContext->hDevObject, &devType); status = DEV_GetIOMgr(pDevContext->hDevObject, &hIOMgr); if (devType == DSP_UNIT) { HW_MBOX_NumMsgGet(resources.dwMboxBase, MBOX_DSP2ARM, &numMbxMsg); while (numMbxMsg != 0) { HW_MBOX_MsgRead(resources.dwMboxBase, MBOX_DSP2ARM, &mbxValue); numMbxMsg--; } /* clear the DSP mailbox as well...*/ HW_MBOX_NumMsgGet(resources.dwMboxBase, MBOX_ARM2DSP, &numMbxMsg); while (numMbxMsg != 0) { HW_MBOX_MsgRead(resources.dwMboxBase, MBOX_ARM2DSP, &mbxValue); numMbxMsg--; udelay(10); HW_MBOX_EventAck(resources.dwMboxBase, MBOX_ARM2DSP, HW_MBOX_U1_DSP1, HW_MBOX_INT_NEW_MSG); } /* Enable the new message events on this IRQ line */ HW_MBOX_EventEnable(resources.dwMboxBase, MBOX_DSP2ARM, MBOX_ARM, HW_MBOX_INT_NEW_MSG); } return status; }
DSP_STATUS sm_interrupt_dsp(struct WMD_DEV_CONTEXT *pDevContext, u16 wMbVal) { DSP_STATUS status = DSP_SOK; if (!pDevContext->mbox) return DSP_SOK; status = omap_mbox_msg_send(pDevContext->mbox, wMbVal, (void *)pDevContext); if (status) { pr_err("omap_mbox_msg_send Fail and status = %d\n", status); status = DSP_EFAIL; } DBG_Trace(DBG_LEVEL3, "writing %x to Mailbox\n", wMbVal); return status; }
/* * ======== WMD_DEH_Notify ======== * DEH error notification function. Informs user about the error. */ void WMD_DEH_Notify(struct DEH_MGR *hDehMgr, u32 ulEventMask, u32 dwErrInfo) { struct DEH_MGR *pDehMgr = (struct DEH_MGR *)hDehMgr; struct WMD_DEV_CONTEXT *pDevContext; u32 memPhysical = 0; u32 HW_MMU_MAX_TLB_COUNT = 31; extern u32 faultAddr; u32 cnt = 0; if (MEM_IsValidHandle(pDehMgr, SIGNATURE)) { printk(KERN_INFO "WMD_DEH_Notify: ********** DEVICE EXCEPTION " "**********\n"); pDevContext = (struct WMD_DEV_CONTEXT *)pDehMgr->hWmdContext; switch (ulEventMask) { case DSP_SYSERROR: /* reset errInfo structure before use */ pDehMgr->errInfo.dwErrMask = DSP_SYSERROR; pDehMgr->errInfo.dwVal1 = 0L; pDehMgr->errInfo.dwVal2 = 0L; pDehMgr->errInfo.dwVal3 = 0L; pDehMgr->errInfo.dwVal1 = dwErrInfo; printk(KERN_ERR "WMD_DEH_Notify: DSP_SYSERROR, errInfo " "= 0x%x\n", dwErrInfo); dump_dl_modules(pDevContext); dump_dsp_stack(pDevContext); break; case DSP_MMUFAULT: /* MMU fault routine should have set err info * structure */ pDehMgr->errInfo.dwErrMask = DSP_MMUFAULT; printk(KERN_INFO "WMD_DEH_Notify: DSP_MMUFAULT," "errInfo = 0x%x\n", dwErrInfo); printk(KERN_INFO "WMD_DEH_Notify: DSP_MMUFAULT, High " "Address = 0x%x\n", (unsigned int)pDehMgr->errInfo.dwVal1); printk(KERN_INFO "WMD_DEH_Notify: DSP_MMUFAULT, Low " "Address = 0x%x\n", (unsigned int)pDehMgr->errInfo.dwVal2); printk(KERN_INFO "WMD_DEH_Notify: DSP_MMUFAULT, fault " "address = 0x%x\n", (unsigned int)faultAddr); PrintDspTraceBuffer(pDevContext); dump_dl_modules(pDevContext); dummyVaAddr = (u32)MEM_Calloc(sizeof(char) * 0x1000, MEM_PAGED); memPhysical = VirtToPhys(PG_ALIGN_LOW((u32)dummyVaAddr, PG_SIZE_4K)); pDevContext = (struct WMD_DEV_CONTEXT *) pDehMgr->hWmdContext; /* Reset the dynamic mmu index to fixed count if it * exceeds 31. So that the dynmmuindex is always * between the range of standard/fixed entries * and 31. */ if (pDevContext->numTLBEntries > HW_MMU_MAX_TLB_COUNT) { pDevContext->numTLBEntries = pDevContext-> fixedTLBEntries; } HW_MMU_TLBAdd(pDevContext->dwDSPMmuBase, memPhysical, faultAddr, HW_PAGE_SIZE_4KB, 1, &mapAttrs, HW_SET, HW_SET); /* * Send a GP Timer interrupt to DSP * The DSP expects a GP timer interrupt after an * MMU-Fault Request GPTimer */ if (timer) { omap_dm_timer_enable(timer); /* Enable overflow interrupt */ omap_dm_timer_set_int_enable(timer, GPTIMER_IRQ_OVERFLOW); /* * Set counter value to overflow counter after * one tick and start timer */ omap_dm_timer_set_load_start(timer, 0, 0xfffffffe); /* Wait 80us for timer to overflow */ udelay(80); /* Check interrupt status and */ /* wait for interrupt */ cnt = 0; while (!(omap_dm_timer_read_status(timer) & GPTIMER_IRQ_OVERFLOW)) { if (cnt++ >= GPTIMER_IRQ_WAIT_MAX_CNT) { pr_err("%s: GPTimer interrupt" " failed\n", __func__); break; } } } /* Clear MMU interrupt */ HW_MMU_EventAck(pDevContext->dwDSPMmuBase, HW_MMU_TRANSLATION_FAULT); dump_dsp_stack(hDehMgr->hWmdContext); if (timer) omap_dm_timer_disable(timer); break; #ifdef CONFIG_BRIDGE_NTFY_PWRERR case DSP_PWRERROR: /* reset errInfo structure before use */ pDehMgr->errInfo.dwErrMask = DSP_PWRERROR; pDehMgr->errInfo.dwVal1 = 0L; pDehMgr->errInfo.dwVal2 = 0L; pDehMgr->errInfo.dwVal3 = 0L; pDehMgr->errInfo.dwVal1 = dwErrInfo; printk(KERN_ERR "WMD_DEH_Notify: DSP_PWRERROR, errInfo " "= 0x%x\n", dwErrInfo); break; #endif /* CONFIG_BRIDGE_NTFY_PWRERR */ #ifdef CONFIG_BRIDGE_WDT3 case DSP_WDTOVERFLOW: pDehMgr->errInfo.dwErrMask = DSP_WDTOVERFLOW; pDehMgr->errInfo.dwVal1 = 0L; pDehMgr->errInfo.dwVal2 = 0L; pDehMgr->errInfo.dwVal3 = 0L; pr_err("WMD_DEH_Notify: DSP_WDTOVERFLOW \n "); break; #endif default: DBG_Trace(DBG_LEVEL6, "WMD_DEH_Notify: Unknown Error, errInfo = " "0x%x\n", dwErrInfo); break; } /* Filter subsequent notifications when an error occurs */ if (pDevContext->dwBrdState != BRD_ERROR) { NTFY_Notify(pDehMgr->hNtfy, ulEventMask); #ifdef CONFIG_BRIDGE_RECOVERY bridge_recover_schedule(); #endif } /* Set the Board state as ERROR */ pDevContext->dwBrdState = BRD_ERROR; /* Disable all the clocks that were enabled by DSP */ (void)DSP_PeripheralClocks_Disable(pDevContext, NULL); #ifdef CONFIG_BRIDGE_WDT3 /* * Avoid the subsequent WDT if it happens once, * also If MMU fault occurs */ dsp_wdt_enable(false); #endif } }
DSP_STATUS CHNLSM_InterruptDSP2(struct WMD_DEV_CONTEXT *pDevContext, u16 wMbVal) { struct CFG_HOSTRES resources; DSP_STATUS status = DSP_SOK; unsigned long timeout; u32 temp; status = CFG_GetHostResources((struct CFG_DEVNODE *) DRV_GetFirstDevExtension(), &resources); if (DSP_FAILED(status)) return DSP_EFAIL; if (pDevContext->dwBrdState == BRD_DSP_HIBERNATION || pDevContext->dwBrdState == BRD_HIBERNATION) { #ifdef CONFIG_BRIDGE_DVFS struct dspbridge_platform_data *pdata = omap_dspbridge_dev->dev.platform_data; /* * When Smartreflex is ON, DSP requires at least OPP level 3 * to operate reliably. So boost lower OPP levels to OPP3. */ if (pdata->dsp_set_min_opp) (*pdata->dsp_set_min_opp)(min_active_opp); #endif /* Restart the peripheral clocks */ DSP_PeripheralClocks_Enable(pDevContext, NULL); /* * 2:0 AUTO_IVA2_DPLL - Enabling IVA2 DPLL auto control * in CM_AUTOIDLE_PLL_IVA2 register */ *(REG_UWORD32 *)(resources.dwCmBase + 0x34) = 0x1; /* * 7:4 IVA2_DPLL_FREQSEL - IVA2 internal frq set to * 0.75 MHz - 1.0 MHz * 2:0 EN_IVA2_DPLL - Enable IVA2 DPLL in lock mode */ temp = *(REG_UWORD32 *)(resources.dwCmBase + 0x4); temp = (temp & 0xFFFFFF08) | 0x37; *(REG_UWORD32 *)(resources.dwCmBase + 0x4) = temp; /* * This delay is needed to avoid mailbox timed out * issue experienced while SmartReflex is ON. * TODO: Instead of 1 ms calculate proper value. */ mdelay(1); /* Restore mailbox settings */ HW_MBOX_restoreSettings(resources.dwMboxBase); /* Access MMU SYS CONFIG register to generate a short wakeup */ temp = *(REG_UWORD32 *)(resources.dwDmmuBase + 0x10); pDevContext->dwBrdState = BRD_RUNNING; } timeout = jiffies + msecs_to_jiffies(1); while (fifo_full((void __iomem *) resources.dwMboxBase, 0)) { if (time_after(jiffies, timeout)) { pr_err("dspbridge: timed out waiting for mailbox\n"); return WMD_E_TIMEOUT; } } DBG_Trace(DBG_LEVEL3, "writing %x to Mailbox\n", wMbVal); HW_MBOX_MsgWrite(resources.dwMboxBase, MBOX_ARM2DSP, wMbVal); return DSP_SOK; }
DSP_STATUS CHNLSM_InterruptDSP2(struct WMD_DEV_CONTEXT *pDevContext, u16 wMbVal) { #ifndef CONFIG_DISABLE_BRIDGE_PM #ifndef CONFIG_DISABLE_BRIDGE_DVFS u32 opplevel; #endif #endif struct CFG_HOSTRES resources; unsigned long timeout; u32 temp; DSP_STATUS status = DSP_SOK; /* We are waiting indefinitely here. This needs to be fixed in the * second phase */ CFG_GetHostResources((struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); if (pDevContext->dwBrdState == BRD_DSP_HIBERNATION || pDevContext->dwBrdState == BRD_HIBERNATION) { #ifndef CONFIG_DISABLE_BRIDGE_PM #ifndef CONFIG_DISABLE_BRIDGE_DVFS #ifndef CONFIG_OMAP3_PM opplevel = omap_pm_dsp_get_opp(); /* If OPP is at minimum level, increase it before waking up * the DSP */ if (opplevel == 1) { omap_pm_dsp_set_min_opp(opplevel+1); DBG_Trace(DBG_LEVEL7, "CHNLSM_InterruptDSP:Setting " "the vdd1 constraint level to %d before " "waking DSP \n", (opplevel + 1)); } #else opplevel = constraint_get_level(dsp_constraint_handle); /* If OPP is at minimum level, increase it before waking up * the DSP */ if (opplevel == 1) { if (constraint_set(dsp_constraint_handle, (opplevel+1)) != 0) { DBG_Trace(DBG_LEVEL7, "CHNLSM_InterruptDSP: " "Constraint set failed\n"); return DSP_EFAIL; } DBG_Trace(DBG_LEVEL7, "CHNLSM_InterruptDSP:Setting " "the vdd1 constraint level to %d before " "waking DSP \n", (opplevel + 1)); } #endif #endif #endif /* Restart the IVA clock that was disabled while * the DSP initiated Hibernation. */ if ((pDevContext->dwBrdState == BRD_DSP_HIBERNATION) || (pDevContext->dwBrdState == BRD_HIBERNATION)) { status = CLK_Enable(SERVICESCLK_iva2_ck); if (DSP_FAILED(status)) return status; } /* Read MMU register to invoke short wakeup of DSP */ temp = (u32) *((REG_UWORD32 *) ((u32) (resources.dwDmmuBase) + 0x10)); /* Restore mailbox settings */ HW_MBOX_restoreSettings(resources.dwMboxBase); DBG_Trace(DBG_LEVEL6, "MailBoxSettings: SYSCONFIG = 0x%x\n", mboxsetting.sysconfig); DBG_Trace(DBG_LEVEL6, "MailBoxSettings: IRQENABLE0 = 0x%x\n", mboxsetting.irqEnable0); DBG_Trace(DBG_LEVEL6, "MailBoxSettings: IRQENABLE1 = 0x%x\n", mboxsetting.irqEnable1); /* Restart the peripheral clocks that were disabled only * in DSP initiated Hibernation case.*/ if (pDevContext->dwBrdState == BRD_DSP_HIBERNATION) DSP_PeripheralClocks_Enable(pDevContext, NULL); pDevContext->dwBrdState = BRD_RUNNING; } timeout = jiffies + msecs_to_jiffies(35); while (HW_MBOX_IsFull(resources.dwMboxBase, MBOX_ARM2DSP)) { if (time_after(jiffies, timeout)) { printk(KERN_ERR "dspbridge: " "timed out waiting for mailbox\n"); return WMD_E_TIMEOUT; } } DBG_Trace(DBG_LEVEL3, "writing %x to Mailbox\n", wMbVal); HW_MBOX_MsgWrite(resources.dwMboxBase, MBOX_ARM2DSP, wMbVal); return DSP_SOK; }
/* * ======== WMD_CHNL_GetIOC ======== * Optionally wait for I/O completion on a channel. Dequeue an I/O * completion record, which contains information about the completed * I/O request. * Note: Ensures Channel Invariant (see notes above). */ DSP_STATUS WMD_CHNL_GetIOC(struct CHNL_OBJECT *hChnl, u32 dwTimeOut, OUT struct CHNL_IOC *pIOC) { DSP_STATUS status = DSP_SOK; struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; struct CHNL_IRP *pChirp; DSP_STATUS statSync; bool fDequeueIOC = true; struct CHNL_IOC ioc = { NULL, 0, 0, 0, 0 }; u8 *pHostSysBuf = NULL; DBG_Trace(DBG_ENTER, "> WMD_CHNL_GetIOC pChnl %p CHNL_IsOutput %x " "uChnlType %x\n", pChnl, CHNL_IsOutput(pChnl->uMode), pChnl->uChnlType); /* Check args: */ if (pIOC == NULL) { status = DSP_EPOINTER; } else if (!MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { status = DSP_EHANDLE; } else if (dwTimeOut == CHNL_IOCNOWAIT) { if (LST_IsEmpty(pChnl->pIOCompletions)) status = CHNL_E_NOIOC; } if (DSP_FAILED(status)) goto func_end; ioc.status = CHNL_IOCSTATCOMPLETE; if (dwTimeOut != CHNL_IOCNOWAIT && LST_IsEmpty(pChnl->pIOCompletions)) { if (dwTimeOut == CHNL_IOCINFINITE) dwTimeOut = SYNC_INFINITE; statSync = SYNC_WaitOnEvent(pChnl->hSyncEvent, dwTimeOut); if (statSync == DSP_ETIMEOUT) { /* No response from DSP */ ioc.status |= CHNL_IOCSTATTIMEOUT; fDequeueIOC = false; } else if (statSync == DSP_EFAIL) { /* This can occur when the user mode thread is * aborted (^C), or when _VWIN32_WaitSingleObject() * fails due to unkown causes. */ /* Even though Wait failed, there may be something in * the Q: */ if (LST_IsEmpty(pChnl->pIOCompletions)) { ioc.status |= CHNL_IOCSTATCANCEL; fDequeueIOC = false; } } } /* See comment in AddIOReq */ SYNC_EnterCS(pChnl->pChnlMgr->hCSObj); disable_irq(MAILBOX_IRQ); if (fDequeueIOC) { /* Dequeue IOC and set pIOC; */ DBC_Assert(!LST_IsEmpty(pChnl->pIOCompletions)); pChirp = (struct CHNL_IRP *)LST_GetHead(pChnl->pIOCompletions); /* Update pIOC from channel state and chirp: */ if (pChirp) { pChnl->cIOCs--; /* If this is a zero-copy channel, then set IOC's pBuf * to the DSP's address. This DSP address will get * translated to user's virtual addr later. */ { pHostSysBuf = pChirp->pHostSysBuf; ioc.pBuf = pChirp->pHostUserBuf; } ioc.cBytes = pChirp->cBytes; ioc.cBufSize = pChirp->cBufSize; ioc.dwArg = pChirp->dwArg; ioc.status |= pChirp->status; /* Place the used chirp on the free list: */ LST_PutTail(pChnl->pFreeList, (struct LST_ELEM *) pChirp); } else { ioc.pBuf = NULL; ioc.cBytes = 0; } } else { ioc.pBuf = NULL; ioc.cBytes = 0; ioc.dwArg = 0; ioc.cBufSize = 0; } /* Ensure invariant: If any IOC's are queued for this channel... */ if (!LST_IsEmpty(pChnl->pIOCompletions)) { /* Since DSPStream_Reclaim() does not take a timeout * parameter, we pass the stream's timeout value to * WMD_CHNL_GetIOC. We cannot determine whether or not * we have waited in User mode. Since the stream's timeout * value may be non-zero, we still have to set the event. * Therefore, this optimization is taken out. * * if (dwTimeOut == CHNL_IOCNOWAIT) { * ... ensure event is set.. * SYNC_SetEvent(pChnl->hSyncEvent); * } */ SYNC_SetEvent(pChnl->hSyncEvent); } else { /* else, if list is empty, ensure event is reset. */ SYNC_ResetEvent(pChnl->hSyncEvent); } enable_irq(MAILBOX_IRQ); SYNC_LeaveCS(pChnl->pChnlMgr->hCSObj); if (fDequeueIOC && (pChnl->uChnlType == CHNL_PCPY && pChnl->uId > 1)) { if (!(ioc.pBuf < (void *) USERMODE_ADDR)) goto func_cont; /* If the addr is in user mode, then copy it */ if (!pHostSysBuf || !ioc.pBuf) { status = DSP_EPOINTER; DBG_Trace(DBG_LEVEL7, "System buffer NULL in IO completion.\n"); goto func_cont; } if (!CHNL_IsInput(pChnl->uMode)) goto func_cont1; /*pHostUserBuf */ status = copy_to_user(ioc.pBuf, pHostSysBuf, ioc.cBytes); #ifndef RES_CLEANUP_DISABLE if (status) { if (current->flags & PF_EXITING) { DBG_Trace(DBG_LEVEL7, "\n2current->flags == PF_EXITING, " " current->flags;0x%x\n", current->flags); status = 0; } else { DBG_Trace(DBG_LEVEL7, "\n2current->flags != PF_EXITING, " " current->flags;0x%x\n", current->flags); } } #endif if (status) { DBG_Trace(DBG_LEVEL7, "Error copying kernel buffer to user, %d" " bytes remaining. in_interupt %d\n", status, in_interrupt()); status = DSP_EPOINTER; } func_cont1: MEM_Free(pHostSysBuf); } func_cont: /* Update User's IOC block: */ *pIOC = ioc; func_end: DBG_Trace(DBG_ENTER, "< WMD_CHNL_GetIOC pChnl %p\n", pChnl); return status; }
/* * ======== WMD_CHNL_AddIOReq ======== * Enqueue an I/O request for data transfer on a channel to the DSP. * The direction (mode) is specified in the channel object. Note the DSP * address is specified for channels opened in direct I/O mode. */ DSP_STATUS WMD_CHNL_AddIOReq(struct CHNL_OBJECT *hChnl, void *pHostBuf, u32 cBytes, u32 cBufSize, OPTIONAL u32 dwDspAddr, u32 dwArg) { DSP_STATUS status = DSP_SOK; struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; struct CHNL_IRP *pChirp = NULL; u32 dwState; bool fIsEOS; struct CHNL_MGR *pChnlMgr = pChnl->pChnlMgr; u8 *pHostSysBuf = NULL; bool fSchedDPC = false; u16 wMbVal = 0; DBG_Trace(DBG_ENTER, "> WMD_CHNL_AddIOReq pChnl %p CHNL_IsOutput %x uChnlType " "%x Id %d\n", pChnl, CHNL_IsOutput(pChnl->uMode), pChnl->uChnlType, pChnl->uId); fIsEOS = (cBytes == 0) ? true : false; if (pChnl->uChnlType == CHNL_PCPY && pChnl->uId > 1 && pHostBuf) { if (!(pHostBuf < (void *)USERMODE_ADDR)) { pHostSysBuf = pHostBuf; goto func_cont; } /* if addr in user mode, then copy to kernel space */ pHostSysBuf = MEM_Alloc(cBufSize, MEM_NONPAGED); if (pHostSysBuf == NULL) { status = DSP_EMEMORY; DBG_Trace(DBG_LEVEL7, "No memory to allocate kernel buffer\n"); goto func_cont; } if (CHNL_IsOutput(pChnl->uMode)) { status = copy_from_user(pHostSysBuf, pHostBuf, cBufSize); if (status) { DBG_Trace(DBG_LEVEL7, "Error copying user buffer to " "kernel, %d bytes remaining.\n", status); MEM_Free(pHostSysBuf); pHostSysBuf = NULL; status = DSP_EPOINTER; } } } func_cont: /* Validate args: */ if (pHostBuf == NULL) { status = DSP_EPOINTER; } else if (!MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { status = DSP_EHANDLE; } else if (fIsEOS && CHNL_IsInput(pChnl->uMode)) { status = CHNL_E_NOEOS; } else { /* Check the channel state: only queue chirp if channel state * allows */ dwState = pChnl->dwState; if (dwState != CHNL_STATEREADY) { if (dwState & CHNL_STATECANCEL) { status = CHNL_E_CANCELLED; } else if ((dwState & CHNL_STATEEOS) && CHNL_IsOutput(pChnl->uMode)) { status = CHNL_E_EOS; } else { /* No other possible states left: */ DBC_Assert(0); } } } /* Mailbox IRQ is disabled to avoid race condition with DMA/ZCPY * channels. DPCCS is held to avoid race conditions with PCPY channels. * If DPC is scheduled in process context (IO_Schedule) and any * non-mailbox interrupt occurs, that DPC will run and break CS. Hence * we disable ALL DPCs. We will try to disable ONLY IO DPC later. */ SYNC_EnterCS(pChnlMgr->hCSObj); disable_irq(MAILBOX_IRQ); if (pChnl->uChnlType == CHNL_PCPY) { /* This is a processor-copy channel. */ if (DSP_SUCCEEDED(status) && CHNL_IsOutput(pChnl->uMode)) { /* Check buffer size on output channels for fit. */ if (cBytes > IO_BufSize(pChnl->pChnlMgr->hIOMgr)) status = CHNL_E_BUFSIZE; } } if (DSP_SUCCEEDED(status)) { /* Get a free chirp: */ pChirp = (struct CHNL_IRP *)LST_GetHead(pChnl->pFreeList); if (pChirp == NULL) status = CHNL_E_NOIORPS; } if (DSP_SUCCEEDED(status)) { /* Enqueue the chirp on the chnl's IORequest queue: */ pChirp->pHostUserBuf = pChirp->pHostSysBuf = pHostBuf; if (pChnl->uChnlType == CHNL_PCPY && pChnl->uId > 1) pChirp->pHostSysBuf = pHostSysBuf; if (DSP_SUCCEEDED(status)) { /* Note: for dma chans dwDspAddr contains dsp address * of SM buffer.*/ DBC_Assert(pChnlMgr->uWordSize != 0); /* DSP address */ pChirp->uDspAddr = dwDspAddr / pChnlMgr->uWordSize; pChirp->cBytes = cBytes; pChirp->cBufSize = cBufSize; /* Only valid for output channel */ pChirp->dwArg = dwArg; pChirp->status = (fIsEOS ? CHNL_IOCSTATEOS : CHNL_IOCSTATCOMPLETE); LST_PutTail(pChnl->pIORequests, (struct LST_ELEM *) pChirp); pChnl->cIOReqs++; DBC_Assert(pChnl->cIOReqs <= pChnl->cChirps); /* If end of stream, update the channel state to prevent * more IOR's: */ if (fIsEOS) pChnl->dwState |= CHNL_STATEEOS; { /* Legacy DSM Processor-Copy */ DBC_Assert(pChnl->uChnlType == CHNL_PCPY); /* Request IO from the DSP */ IO_RequestChnl(pChnlMgr->hIOMgr, pChnl, (CHNL_IsInput(pChnl->uMode) ? IO_INPUT : IO_OUTPUT), &wMbVal); fSchedDPC = true; } } } enable_irq(MAILBOX_IRQ); SYNC_LeaveCS(pChnlMgr->hCSObj); if (wMbVal != 0) IO_IntrDSP2(pChnlMgr->hIOMgr, wMbVal); if (fSchedDPC == true) { /* Schedule a DPC, to do the actual data transfer: */ IO_Schedule(pChnlMgr->hIOMgr); } DBG_Trace(DBG_ENTER, "< WMD_CHNL_AddIOReq pChnl %p\n", pChnl); return status; }
DSP_STATUS CHNLSM_InterruptDSP2(struct WMD_DEV_CONTEXT *pDevContext, u16 wMbVal) { #ifdef CONFIG_BRIDGE_DVFS struct dspbridge_platform_data *pdata = omap_dspbridge_dev->dev.platform_data; u32 opplevel = 0; #endif struct CFG_HOSTRES resources; DSP_STATUS status = DSP_SOK; unsigned long timeout; u32 temp; status = CFG_GetHostResources((struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); if (DSP_FAILED(status)) return DSP_EFAIL; #ifdef CONFIG_BRIDGE_DVFS if (pDevContext->dwBrdState == BRD_DSP_HIBERNATION || pDevContext->dwBrdState == BRD_HIBERNATION) { if (pdata->dsp_get_opp) opplevel = (*pdata->dsp_get_opp)(); if (opplevel == 1) { if (pdata->dsp_set_min_opp) (*pdata->dsp_set_min_opp)(opplevel+1); } } #endif if (pDevContext->dwBrdState == BRD_DSP_HIBERNATION || pDevContext->dwBrdState == BRD_HIBERNATION) { /* Restart the IVA clock that was disabled while * the DSP initiated Hibernation. */ status = CLK_Enable(SERVICESCLK_iva2_ck); if (DSP_FAILED(status)) return status; /* Restore mailbox settings */ /* Restart the peripheral clocks that were disabled only * in DSP initiated Hibernation case.*/ if (pDevContext->dwBrdState == BRD_DSP_HIBERNATION) { DSP_PeripheralClocks_Enable(pDevContext, NULL); /* Enabling Dpll in lock mode*/ temp = (u32) *((REG_UWORD32 *) ((u32) (resources.dwCmBase) + 0x34)); temp = (temp & 0xFFFFFFFE) | 0x1; *((REG_UWORD32 *) ((u32) (resources.dwCmBase) + 0x34)) = (u32) temp; temp = (u32) *((REG_UWORD32 *) ((u32) (resources.dwCmBase) + 0x4)); temp = (temp & 0xFFFFFC8) | 0x37; *((REG_UWORD32 *) ((u32) (resources.dwCmBase) + 0x4)) = (u32) temp; } HW_MBOX_restoreSettings(resources.dwMboxBase); /* Access MMU SYS CONFIG register to generate a short wakeup */ temp = (u32) *((REG_UWORD32 *) ((u32) (resources.dwDmmuBase) + 0x10)); pDevContext->dwBrdState = BRD_RUNNING; } timeout = jiffies + msecs_to_jiffies(1); while (fifo_full((void __iomem *) resources.dwMboxBase, 0)) { if (time_after(jiffies, timeout)) { printk(KERN_ERR "dspbridge: timed out waiting for mailbox\n"); return WMD_E_TIMEOUT; } } DBG_Trace(DBG_LEVEL3, "writing %x to Mailbox\n", wMbVal); HW_MBOX_MsgWrite(resources.dwMboxBase, MBOX_ARM2DSP, wMbVal); return DSP_SOK; }