in hw/mcu/ambiq/apollo3/src/ext/AmbiqSuite/mcu/apollo3/hal/am_hal_iom.c [3244:3739]
uint32_t am_hal_iom_control(void *pHandle, am_hal_iom_request_e eReq, void *pArgs)
{
am_hal_iom_state_t *pIOMState = (am_hal_iom_state_t*)pHandle;
uint32_t status = AM_HAL_STATUS_SUCCESS;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!AM_HAL_IOM_CHK_HANDLE(pHandle))
{
return AM_HAL_STATUS_INVALID_HANDLE;
}
//
// Validate the parameters
//
if (eReq >= AM_HAL_IOM_REQ_MAX)
{
return AM_HAL_STATUS_INVALID_ARG;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
uint32_t ui32Module = pIOMState->ui32Module;
switch (eReq)
{
case AM_HAL_IOM_REQ_FLAG_SETCLR:
if (pArgs)
{
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (*((uint32_t *)pArgs) & AM_HAL_IOM_SC_RESV_MASK)
{
return AM_HAL_STATUS_INVALID_ARG;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
IOMn(ui32Module)->CQSETCLEAR = *((uint32_t *)pArgs);
}
else
{
status = AM_HAL_STATUS_INVALID_ARG;
}
break;
case AM_HAL_IOM_REQ_SPI_LSB:
if (pArgs)
{
IOMn(ui32Module)->MSPICFG_b.SPILSB = *((uint32_t *)pArgs);
}
else
{
status = AM_HAL_STATUS_INVALID_ARG;
}
break;
case AM_HAL_IOM_REQ_SPI_FULLDUPLEX: // Not supported
status = AM_HAL_STATUS_INVALID_OPERATION;
#if 0
if (pArgs)
{
IOMn(ui32Module)->MSPICFG_b.FULLDUP = *((uint32_t *)pArgs);
}
else
{
status = AM_HAL_STATUS_INVALID_ARG;
}
#endif
break;
case AM_HAL_IOM_REQ_SPI_RDTHRESH:
if (pArgs)
{
IOMn(ui32Module)->FIFOTHR_b.FIFORTHR = *((uint32_t *)pArgs);
}
else
{
status = AM_HAL_STATUS_INVALID_ARG;
}
break;
case AM_HAL_IOM_REQ_SPI_WRTHRESH:
if (pArgs)
{
IOMn(ui32Module)->FIFOTHR_b.FIFOWTHR = *((uint32_t *)pArgs);
}
else
{
status = AM_HAL_STATUS_INVALID_ARG;
}
break;
case AM_HAL_IOM_REQ_PAUSE:
// Force CQ to Paused
status = iom_cq_pause(pIOMState);
break;
case AM_HAL_IOM_REQ_UNPAUSE:
// Resume the CQ
IOMn(ui32Module)->CQSETCLEAR = AM_HAL_IOM_SC_UNPAUSE_CQ;
break;
case AM_HAL_IOM_REQ_SET_SEQMODE:
{
am_hal_iom_seq_e eSeq;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pArgs)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (!pIOMState->pNBTxnBuf)
{
// No space for CMDQ
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
eSeq = *((bool *)pArgs) ? AM_HAL_IOM_SEQ_UNDER_CONSTRUCTION: AM_HAL_IOM_SEQ_NONE;
if (eSeq == pIOMState->eSeq)
{
// Nothing to do
return AM_HAL_STATUS_SUCCESS;
}
#if 0 // We should be able to operate on sequence even if there are HP transactions in progress
// Make sure there is no high priority transaction in progress
if (pIOMState->ui32NumHPEntries)
{
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif
switch (pIOMState->eSeq)
{
case AM_HAL_IOM_SEQ_RUNNING:
{
// Force CQ to Pause
status = iom_cq_pause(pIOMState);
break;
}
case AM_HAL_IOM_SEQ_NONE:
{
// Make sure there is no non-blocking transaction in progress
if (pIOMState->ui32NumPendTransactions)
{
status = AM_HAL_STATUS_INVALID_OPERATION;
}
break;
}
default:
;
}
if (status == AM_HAL_STATUS_SUCCESS)
{
// Reset the cmdq
am_hal_cmdq_reset(pIOMState->pCmdQHdl);
pIOMState->ui32LastIdxProcessed = 0;
pIOMState->ui32NumSeqTransactions = 0;
pIOMState->ui32NumPendTransactions = 0;
pIOMState->ui32NumUnSolicited = 0;
pIOMState->eSeq = eSeq;
pIOMState->bAutonomous = true;
}
break;
}
case AM_HAL_IOM_REQ_SEQ_END:
{
uint32_t ui32Status = AM_HAL_STATUS_SUCCESS;
am_hal_cmdq_entry_t *pCQBlock;
uint32_t index;
am_hal_iom_seq_end_t *pLoop = (am_hal_iom_seq_end_t *)pArgs;
uint32_t pause = 0;
uint32_t scUnpause = 0;
uint32_t ui32Critical = 0;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pArgs)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (pLoop->ui32PauseCondition & AM_HAL_IOM_PAUSE_FLAG_RESV)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (pLoop->ui32StatusSetClr & AM_HAL_IOM_SC_RESV_MASK)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (pIOMState->eSeq != AM_HAL_IOM_SEQ_UNDER_CONSTRUCTION)
{
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
if (pIOMState->block)
{
// End the block if the sequence is ending
pIOMState->block = 0;
// Unblock the whole batch of commands in this block
IOMn(pIOMState->ui32Module)->CQSETCLEAR = AM_HAL_IOM_SC_UNPAUSE_BLOCK;
}
if ((pLoop->bLoop) && (!pIOMState->bAutonomous))
{
// Need to insert special element in CQ to cause a callback
// This is to reset internal state
ui32Status = am_hal_cmdq_alloc_block(pIOMState->pCmdQHdl, 1, &pCQBlock, &index);
if (ui32Status != AM_HAL_STATUS_SUCCESS)
{
return ui32Status;
}
else
{
//
// Store the callback function pointer.
//
pIOMState->pfnCallback[index & (AM_HAL_IOM_MAX_PENDING_TRANSACTIONS - 1)] = iom_seq_loopback;
pIOMState->pCallbackCtxt[index & (AM_HAL_IOM_MAX_PENDING_TRANSACTIONS - 1)] = (void *)pIOMState;
// Dummy Entry
pCQBlock->address = (uint32_t)&IOMn(pIOMState->ui32Module)->CQSETCLEAR;
pCQBlock->value = 0;
//
// Need to protect access of ui32NumPendTransactions as it is accessed
// from ISR as well
//
// Start a critical section.
//
ui32Critical = am_hal_interrupt_master_disable();
//
// Post to the CQ.
//
ui32Status = am_hal_cmdq_post_block(pIOMState->pCmdQHdl, true);
if (AM_HAL_STATUS_SUCCESS != ui32Status)
{
am_hal_cmdq_release_block(pIOMState->pCmdQHdl);
}
else
{
uint32_t ui32NumPend = pIOMState->ui32NumPendTransactions++;
if (0 == ui32NumPend)
{
pIOMState->ui32UserIntCfg = IOMn(ui32Module)->INTEN;
IOM_SET_INTEN(ui32Module, AM_HAL_IOM_INT_CQMODE);
// Re-enable the CQ
am_hal_iom_CQEnable(pIOMState);
}
}
//
// End the critical section.
//
am_hal_interrupt_master_set(ui32Critical);
// Use SWFLAG6 to cause a pause
pause = AM_HAL_IOM_PAUSE_FLAG_SEQLOOP;
// Revert back the flag after SW callback unpauses it
scUnpause = AM_HAL_IOM_SC_PAUSE_SEQLOOP;
}
}
// Insert the loopback
ui32Status = am_hal_cmdq_alloc_block(pIOMState->pCmdQHdl, sizeof(am_hal_iom_cq_loop_entry_t) / 8, &pCQBlock, &index);
if (ui32Status != AM_HAL_STATUS_SUCCESS)
{
return ui32Status;
}
else
{
am_hal_iom_cq_loop_entry_t *pLoopEntry = (am_hal_iom_cq_loop_entry_t *)pCQBlock;
pLoopEntry->ui32PAUSENAddr = pLoopEntry->ui32PAUSEN2Addr = (uint32_t)&IOMn(ui32Module)->CQPAUSEEN;
pLoopEntry->ui32SETCLRAddr = (uint32_t)&IOMn(ui32Module)->CQSETCLEAR;
pLoopEntry->ui32PAUSEENVal = get_pause_val(pIOMState, pLoop->ui32PauseCondition | pause);
pLoopEntry->ui32PAUSEEN2Val = AM_HAL_IOM_PAUSE_DEFAULT;
pLoopEntry->ui32SETCLRVal = pLoop->ui32StatusSetClr | scUnpause;
//
// Need to protect access of ui32NumPendTransactions as it is accessed
// from ISR as well
//
// Start a critical section.
//
ui32Critical = am_hal_interrupt_master_disable();
//
// Post to the CQ.
//
if (pLoop->bLoop)
{
ui32Status = am_hal_cmdq_post_loop_block(pIOMState->pCmdQHdl, false);
}
else
{
ui32Status = am_hal_cmdq_post_block(pIOMState->pCmdQHdl, false);
}
if (AM_HAL_STATUS_SUCCESS != ui32Status)
{
am_hal_cmdq_release_block(pIOMState->pCmdQHdl);
}
else
{
uint32_t ui32NumPend = pIOMState->ui32NumPendTransactions++;
pIOMState->eSeq = (pLoop->bLoop) ? AM_HAL_IOM_SEQ_RUNNING : AM_HAL_IOM_SEQ_NONE;
if (0 == ui32NumPend)
{
pIOMState->ui32UserIntCfg = IOMn(ui32Module)->INTEN;
IOM_SET_INTEN(ui32Module, AM_HAL_IOM_INT_CQMODE);
// Re-enable the CQ
am_hal_iom_CQEnable(pIOMState);
}
}
//
// End the critical section.
//
am_hal_interrupt_master_set(ui32Critical);
}
return AM_HAL_STATUS_SUCCESS;
//break;
}
case AM_HAL_IOM_REQ_INIT_HIPRIO:
{
am_hal_iom_hiprio_cfg_t *pHPCfg = (am_hal_iom_hiprio_cfg_t *)pArgs;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pHPCfg)
{
return AM_HAL_STATUS_INVALID_ARG;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
pIOMState->ui32NumHPEntries = pIOMState->ui32LastHPIdxProcessed = 0;
pIOMState->ui32NextHPIdx = pIOMState->ui32LastHPIdxProcessed + 1;
pIOMState->pHPTransactions = (am_hal_iom_dma_entry_t *)pHPCfg->pBuf;
pIOMState->ui32MaxHPTransactions = pHPCfg->size / sizeof(am_hal_iom_dma_entry_t);
break;
}
case AM_HAL_IOM_REQ_START_BLOCK:
// Pause the next block from proceeding till whole block is finished
IOMn(pIOMState->ui32Module)->CQSETCLEAR = AM_HAL_IOM_SC_PAUSE_BLOCK;
pIOMState->block = 1;
pIOMState->ui32NumHPPendingEntries = 0;
break;
case AM_HAL_IOM_REQ_END_BLOCK:
// Unblock the whole batch of commands in this block
IOMn(pIOMState->ui32Module)->CQSETCLEAR = AM_HAL_IOM_SC_UNPAUSE_BLOCK;
pIOMState->block = 0;
if (pIOMState->ui32NumHPPendingEntries)
{
// Now it is okay to let go of the block of HiPrio transactions
status = sched_hiprio(pIOMState, pIOMState->ui32NumHPPendingEntries);
if (status == AM_HAL_STATUS_SUCCESS)
{
pIOMState->ui32NumHPPendingEntries = 0;
}
}
break;
case AM_HAL_IOM_REQ_SET_DCX:
{
am_hal_iom_dcx_cfg_t *pDcxCfg = (am_hal_iom_dcx_cfg_t *)pArgs;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pDcxCfg)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if ((pIOMState->eInterfaceMode != AM_HAL_IOM_SPI_MODE) ||
(pDcxCfg->cs == pDcxCfg->dcx) ||
(pDcxCfg->cs > AM_HAL_IOM_MAX_CS_SPI) ||
((pDcxCfg->dcx != AM_HAL_IOM_DCX_INVALID) && (pDcxCfg->dcx > AM_HAL_IOM_MAX_CS_SPI)))
{
return AM_HAL_STATUS_INVALID_ARG;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
if ( !APOLLO3_GE_B0 )
{
return AM_HAL_STATUS_INVALID_OPERATION;
}
pIOMState->dcx[pDcxCfg->cs] = (pDcxCfg->dcx == AM_HAL_IOM_DCX_INVALID) ? 0 : (IOM0_DCX_DCXEN_Msk | (0x1 << pDcxCfg->dcx));
break;
}
case AM_HAL_IOM_REQ_CQ_RAW:
{
#if (AM_HAL_IOM_CQ == 1)
am_hal_iom_cq_raw_t *pCqRaw = (am_hal_iom_cq_raw_t *)pArgs;
am_hal_cmdq_entry_t *pCQBlock;
am_hal_iom_callback_t pfnCallback1;
uint32_t ui32Critical = 0;
uint32_t index;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pCqRaw)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (!pIOMState->pCmdQHdl)
{
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
//
// Check to see if there is enough room in the CQ
//
if ((pIOMState->ui32NumPendTransactions == AM_HAL_IOM_MAX_PENDING_TRANSACTIONS) ||
(am_hal_cmdq_alloc_block(pIOMState->pCmdQHdl, pCqRaw->numEntries + 3, &pCQBlock, &index)))
{
return AM_HAL_STATUS_OUT_OF_RANGE;
}
pCQBlock->address = (uint32_t)&IOMn(pIOMState->ui32Module)->CQPAUSEEN;
pCQBlock->value = get_pause_val(pIOMState, pCqRaw->ui32PauseCondition);
pCQBlock++;
for (uint32_t i = 0; i < pCqRaw->numEntries; i++, pCQBlock++)
{
pCQBlock->address = pCqRaw->pCQEntry[i].address;
pCQBlock->value = pCqRaw->pCQEntry[i].value;
}
// If there is a need - populate the jump back address
if (pCqRaw->pJmpAddr)
{
*(pCqRaw->pJmpAddr) = (uint32_t)pCQBlock;
}
pCQBlock->address = (uint32_t)&IOMn(pIOMState->ui32Module)->CQPAUSEEN;
pCQBlock->value = AM_HAL_IOM_PAUSE_DEFAULT;
pCQBlock++;
pCQBlock->address = (uint32_t)&IOMn(pIOMState->ui32Module)->CQSETCLEAR;
pCQBlock->value = pCqRaw->ui32StatusSetClr;
pfnCallback1 = pCqRaw->pfnCallback;
if ( !pfnCallback1 && !pIOMState->block && (pIOMState->eSeq == AM_HAL_IOM_SEQ_NONE) &&
(pIOMState->ui32NumUnSolicited >= (pIOMState->ui32MaxPending / 2)) )
{
// Need to schedule a dummy callback, to ensure ui32NumPendTransactions get updated in ISR
pfnCallback1 = iom_dummy_callback;
}
//
// Store the callback function pointer.
//
pIOMState->pfnCallback[index & (AM_HAL_IOM_MAX_PENDING_TRANSACTIONS - 1)] = pfnCallback1;
pIOMState->pCallbackCtxt[index & (AM_HAL_IOM_MAX_PENDING_TRANSACTIONS - 1)] = pCqRaw->pCallbackCtxt;
//
// Need to protect access of ui32NumPendTransactions as it is accessed
// from ISR as well
//
// Start a critical section.
//
ui32Critical = am_hal_interrupt_master_disable();
//
// Register for interrupt only if there is a callback
//
status = am_hal_cmdq_post_block(pIOMState->pCmdQHdl, pfnCallback1);
if (status == AM_HAL_STATUS_SUCCESS)
{
uint32_t ui32NumPend = pIOMState->ui32NumPendTransactions++;
pIOMState->ui32NumSeqTransactions++;
if (pCqRaw->pfnCallback)
{
pIOMState->bAutonomous = false;
pIOMState->ui32NumUnSolicited = 0;
}
else
{
if (pfnCallback1)
{
// This implies we have already scheduled a dummy callback
pIOMState->ui32NumUnSolicited = 0;
}
else
{
pIOMState->ui32NumUnSolicited++;
}
}
if (0 == ui32NumPend)
{
pIOMState->ui32UserIntCfg = IOMn(ui32Module)->INTEN;
IOM_SET_INTEN(ui32Module, AM_HAL_IOM_INT_CQMODE);
// Re-enable the CQ
am_hal_iom_CQEnable(pIOMState);
}
}
else
{
am_hal_cmdq_release_block(pIOMState->pCmdQHdl);
}
//
// End the critical section.
//
am_hal_interrupt_master_set(ui32Critical);
#else // !AM_HAL_IOM_CQ
status = AM_HAL_STATUS_INVALID_ARG;
#endif
break;
}
default:
status = AM_HAL_STATUS_INVALID_ARG;
}
return status;
}