in hw/mcu/ambiq/apollo3/src/ext/AmbiqSuite/mcu/apollo3/hal/am_hal_mspi.c [1618:2205]
uint32_t am_hal_mspi_control(void *pHandle,
am_hal_mspi_request_e eRequest,
void *pConfig)
{
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
uint32_t ui32Module;
uint32_t ui32Status = AM_HAL_STATUS_SUCCESS;
#ifndef AM_HAL_DISABLE_API_VALIDATION
//
// Check the handle.
//
if ( !AM_HAL_MSPI_CHK_HANDLE(pHandle) )
{
return AM_HAL_STATUS_INVALID_HANDLE;
}
//
// Validate the parameters
//
if (eRequest > AM_HAL_MSPI_REQ_MAX)
{
return AM_HAL_STATUS_INVALID_ARG;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
ui32Module = pMSPIState->ui32Module;
switch(eRequest)
{
case AM_HAL_MSPI_REQ_APBCLK:
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pConfig)
{
return AM_HAL_STATUS_INVALID_ARG;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
//
// Enable/Disable APBCLK.
//
MSPIn(ui32Module)->MSPICFG_b.APBCLK = *((uint32_t *)pConfig);
break;
case AM_HAL_MSPI_REQ_FLAG_SETCLR:
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pConfig)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (*((uint32_t *)pConfig) & AM_HAL_MSPI_SC_RESV_MASK)
{
return AM_HAL_STATUS_INVALID_ARG;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
MSPIn(ui32Module)->CQSETCLEAR = *((uint32_t *)pConfig);
break;
case AM_HAL_MSPI_REQ_LINK_IOM:
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pConfig)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (( *((uint32_t *)pConfig) >= AM_REG_IOM_NUM_MODULES ) && ( *((uint32_t *)pConfig) != AM_HAL_MSPI_LINK_IOM_NONE ))
{
return AM_HAL_STATUS_INVALID_ARG;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
//
// Set the Linked IOM
//
MSPIn(ui32Module)->MSPICFG_b.IOMSEL = *((uint32_t *)pConfig);
break;
case AM_HAL_MSPI_REQ_SCRAMB_DIS:
//
// Disable scrambling.
//
MSPIn(ui32Module)->SCRAMBLING_b.SCRENABLE = 0;
break;
case AM_HAL_MSPI_REQ_SCRAMB_EN:
//
// Enable scrambling.
//
MSPIn(ui32Module)->SCRAMBLING_b.SCRENABLE = 1;
break;
case AM_HAL_MSPI_REQ_XIPACK:
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pConfig)
{
return AM_HAL_STATUS_INVALID_ARG;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
//
// Enable/Disable XIPACK.
//
MSPIn(ui32Module)->FLASH_b.XIPACK = *((uint32_t *)pConfig);
break;
case AM_HAL_MSPI_REQ_XIP_DIS:
//
// Disable XIP.
//
MSPIn(ui32Module)->FLASH_b.XIPEN = 0;
break;
case AM_HAL_MSPI_REQ_XIP_EN:
//
// Enable XIP.
//
MSPIn(ui32Module)->FLASH_b.XIPEN = 1;
break;
case AM_HAL_MSPI_REQ_DEVICE_CONFIG:
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pConfig)
{
return AM_HAL_STATUS_INVALID_ARG;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
{
am_hal_mspi_device_e deviceConfig;
deviceConfig = *((am_hal_mspi_device_e *)pConfig);
//
// Configure the MSPI for a specific device configuration.
// This function sets the following registers/fields:
// CFG.DEVCFG
// CFG.SEPIO
// FLASH.XIPMIXED
// PADCFG
// PADOUTEN
//
ui32Status = mspi_device_configure(pHandle, deviceConfig);
if (AM_HAL_STATUS_SUCCESS != ui32Status)
{
return ui32Status;
}
}
break;
case AM_HAL_MSPI_REQ_CLOCK_CONFIG:
{
am_hal_mspi_clock_e eClockFreq;
eClockFreq = *((am_hal_mspi_clock_e *)pConfig);
//
// Set the clock divisor to get the desired MSPI clock frequency.
//
MSPIn(ui32Module)->MSPICFG_b.CLKDIV = eClockFreq;
//
// Adjust the clock edge configuration depending upon the clock frequency.
//
if ( eClockFreq == AM_HAL_MSPI_CLK_48MHZ )
{
MSPIn(ui32Module)->MSPICFG_b.TXNEG = 1;
MSPIn(ui32Module)->MSPICFG_b.RXNEG = 0;
MSPIn(ui32Module)->MSPICFG_b.RXCAP = 1;
}
else
{
MSPIn(ui32Module)->MSPICFG_b.TXNEG = 0;
MSPIn(ui32Module)->MSPICFG_b.RXNEG = 0;
MSPIn(ui32Module)->MSPICFG_b.RXCAP = 1;
}
pMSPIState->eClockFreq = eClockFreq;
}
break;
case AM_HAL_MSPI_REQ_PAUSE:
ui32Status = mspi_cq_pause(pMSPIState);
break;
case AM_HAL_MSPI_REQ_UNPAUSE:
// Resume the CQ
MSPIn(ui32Module)->CQSETCLEAR = AM_HAL_MSPI_SC_UNPAUSE_CQ;
break;
case AM_HAL_MSPI_REQ_SET_SEQMODE:
{
am_hal_mspi_seq_e eSeq;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pConfig)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (!pMSPIState->pTCB)
{
// No space for CMDQ
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
eSeq = *((bool *)pConfig) ? AM_HAL_MSPI_SEQ_UNDER_CONSTRUCTION: AM_HAL_MSPI_SEQ_NONE;
if (eSeq == pMSPIState->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 (pMSPIState->ui32NumHPEntries)
{
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif
switch (pMSPIState->eSeq)
{
case AM_HAL_MSPI_SEQ_RUNNING:
{
ui32Status = mspi_cq_pause(pMSPIState);
break;
}
case AM_HAL_MSPI_SEQ_NONE:
{
// Make sure there is no non-blocking transaction in progress
if (pMSPIState->ui32NumCQEntries)
{
return AM_HAL_STATUS_INVALID_OPERATION;
}
break;
}
default:
;
}
if (ui32Status == AM_HAL_STATUS_SUCCESS)
{
// Reset the cmdq
am_hal_cmdq_reset(pMSPIState->CQ.pCmdQHdl);
pMSPIState->ui32LastIdxProcessed = 0;
pMSPIState->ui32NumTransactions = 0;
pMSPIState->ui32NumCQEntries = 0;
pMSPIState->eSeq = eSeq;
pMSPIState->bAutonomous = true;
pMSPIState->ui32NumUnSolicited = 0;
}
break;
}
case AM_HAL_MSPI_REQ_SEQ_END:
{
uint32_t ui32Status = AM_HAL_STATUS_SUCCESS;
am_hal_cmdq_entry_t *pCQBlock;
uint32_t index;
am_hal_mspi_seq_end_t *pLoop = (am_hal_mspi_seq_end_t *)pConfig;
uint32_t pause = 0;
uint32_t scUnpause = 0;
uint32_t ui32Critical = 0;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pConfig)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (pLoop->ui32PauseCondition & AM_HAL_MSPI_PAUSE_FLAG_RESV)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (pLoop->ui32StatusSetClr & AM_HAL_MSPI_SC_RESV_MASK)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (pMSPIState->eSeq != AM_HAL_MSPI_SEQ_UNDER_CONSTRUCTION)
{
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
if (pMSPIState->block)
{
// End the block if the sequence is ending
pMSPIState->block = 0;
// Unblock the whole batch of commands in this block
MSPIn(ui32Module)->CQSETCLEAR = AM_HAL_MSPI_SC_UNPAUSE_BLOCK;
}
if ((pLoop->bLoop) && (!pMSPIState->bAutonomous))
{
// Need to insert special element in CQ to cause a callback
// This is to reset internal state
ui32Status = am_hal_cmdq_alloc_block(pMSPIState->CQ.pCmdQHdl, 1, &pCQBlock, &index);
if (ui32Status != AM_HAL_STATUS_SUCCESS)
{
return ui32Status;
}
else
{
//
// Store the callback function pointer.
//
pMSPIState->pfnCallback[index & (AM_HAL_MSPI_MAX_CQ_ENTRIES - 1)] = mspi_seq_loopback;
pMSPIState->pCallbackCtxt[index & (AM_HAL_MSPI_MAX_CQ_ENTRIES - 1)] = (void *)pMSPIState;
// Dummy Entry
pCQBlock->address = (uint32_t)&MSPIn(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(pMSPIState->CQ.pCmdQHdl, true);
if (AM_HAL_STATUS_SUCCESS != ui32Status)
{
//
// End the critical section.
//
am_hal_interrupt_master_set(ui32Critical);
am_hal_cmdq_release_block(pMSPIState->CQ.pCmdQHdl);
return ui32Status;
}
else
{
uint32_t ui32NumPend = pMSPIState->ui32NumCQEntries++;
//
// End the critical section.
//
am_hal_interrupt_master_set(ui32Critical);
if (ui32NumPend == 0)
{
//
// Enable the Command Queue
//
ui32Status = mspi_cq_enable(pHandle);
if (AM_HAL_STATUS_SUCCESS != ui32Status)
{
return ui32Status;
}
}
}
// Use SWFLAG6 to cause a pause
pause = AM_HAL_MSPI_PAUSE_FLAG_SEQLOOP;
// Revert back the flag after SW callback unpauses it
scUnpause = AM_HAL_MSPI_SC_PAUSE_SEQLOOP;
}
}
// Insert the loopback
ui32Status = am_hal_cmdq_alloc_block(pMSPIState->CQ.pCmdQHdl, sizeof(am_hal_mspi_cq_loop_entry_t) / 8, &pCQBlock, &index);
if (ui32Status != AM_HAL_STATUS_SUCCESS)
{
return ui32Status;
}
else
{
am_hal_mspi_cq_loop_entry_t *pLoopEntry = (am_hal_mspi_cq_loop_entry_t *)pCQBlock;
pLoopEntry->ui32PAUSENAddr = pLoopEntry->ui32PAUSEN2Addr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
pLoopEntry->ui32SETCLRAddr = (uint32_t)&MSPIn(ui32Module)->CQSETCLEAR;
pLoopEntry->ui32PAUSEENVal = get_pause_val(pMSPIState, pLoop->ui32PauseCondition | pause);
pLoopEntry->ui32PAUSEEN2Val = AM_HAL_MSPI_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(pMSPIState->CQ.pCmdQHdl, false);
}
else
{
ui32Status = am_hal_cmdq_post_block(pMSPIState->CQ.pCmdQHdl, false);
}
if (AM_HAL_STATUS_SUCCESS != ui32Status)
{
//
// End the critical section.
//
am_hal_interrupt_master_set(ui32Critical);
am_hal_cmdq_release_block(pMSPIState->CQ.pCmdQHdl);
}
else
{
uint32_t ui32NumPend = pMSPIState->ui32NumCQEntries++;
pMSPIState->eSeq = (pLoop->bLoop) ? AM_HAL_MSPI_SEQ_RUNNING : AM_HAL_MSPI_SEQ_NONE;
//
// End the critical section.
//
am_hal_interrupt_master_set(ui32Critical);
if (ui32NumPend == 0)
{
//
// Enable the Command Queue
//
ui32Status = mspi_cq_enable(pHandle);
if (AM_HAL_STATUS_SUCCESS != ui32Status)
{
return ui32Status;
}
}
}
}
return ui32Status;
//break;
}
case AM_HAL_MSPI_REQ_INIT_HIPRIO:
{
am_hal_mspi_hiprio_cfg_t *pHPCfg = (am_hal_mspi_hiprio_cfg_t *)pConfig;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pConfig)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (pMSPIState->pHPTransactions)
{
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
pMSPIState->ui32NumHPEntries = pMSPIState->ui32LastHPIdxProcessed = 0;
pMSPIState->ui32NextHPIdx = pMSPIState->ui32LastHPIdxProcessed + 1;
pMSPIState->pHPTransactions = (am_hal_mspi_dma_entry_t *)pHPCfg->pBuf;
pMSPIState->ui32MaxHPTransactions = pHPCfg->size / sizeof(am_hal_mspi_dma_entry_t);
break;
}
case AM_HAL_MSPI_REQ_START_BLOCK:
// Pause the next block from proceeding till whole block is finished
MSPIn(ui32Module)->CQSETCLEAR = AM_HAL_MSPI_SC_PAUSE_BLOCK;
pMSPIState->block = 1;
pMSPIState->ui32NumHPPendingEntries = 0;
break;
case AM_HAL_MSPI_REQ_END_BLOCK:
// Unblock the whole batch of commands in this block
MSPIn(ui32Module)->CQSETCLEAR = AM_HAL_MSPI_SC_UNPAUSE_BLOCK;
pMSPIState->block = 0;
if (pMSPIState->ui32NumHPPendingEntries)
{
// Now it is okay to let go of the block of HiPrio transactions
ui32Status = sched_hiprio(pMSPIState, pMSPIState->ui32NumHPPendingEntries);
if (ui32Status == AM_HAL_STATUS_SUCCESS)
{
pMSPIState->ui32NumHPPendingEntries = 0;
}
}
break;
case AM_HAL_MSPI_REQ_CQ_RAW:
{
#if MSPI_USE_CQ
am_hal_mspi_cq_raw_t *pCqRaw = (am_hal_mspi_cq_raw_t *)pConfig;
am_hal_cmdq_entry_t *pCQBlock;
uint32_t ui32Critical = 0;
uint32_t ui32NumPend;
uint32_t index;
am_hal_mspi_callback_t pfnCallback1;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!pCqRaw)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (!pMSPIState->CQ.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 ((pMSPIState->ui32NumCQEntries == AM_HAL_MSPI_MAX_CQ_ENTRIES) ||
(am_hal_cmdq_alloc_block(pMSPIState->CQ.pCmdQHdl, pCqRaw->numEntries + 3, &pCQBlock, &index)))
{
return AM_HAL_STATUS_OUT_OF_RANGE;
}
pCQBlock->address = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
pCQBlock->value = get_pause_val(pMSPIState, pCqRaw->ui32PauseCondition);
pCQBlock++;
// Copy the CQ Entry contents
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)&MSPIn(ui32Module)->CQPAUSE;
pCQBlock->value = AM_HAL_MSPI_PAUSE_DEFAULT;
pCQBlock++;
pCQBlock->address = (uint32_t)&MSPIn(ui32Module)->CQSETCLEAR;
pCQBlock->value = pCqRaw->ui32StatusSetClr;
pfnCallback1 = pCqRaw->pfnCallback;
if ( !pfnCallback1 && !pMSPIState->block && (pMSPIState->eSeq == AM_HAL_MSPI_SEQ_NONE) &&
(pMSPIState->ui32NumUnSolicited >= (pMSPIState->ui32MaxPending / 2)) )
{
// Need to schedule a dummy callback, to ensure ui32NumCQEntries get updated in ISR
pfnCallback1 = mspi_dummy_callback;
}
//
// Store the callback function pointer.
//
pMSPIState->pfnCallback[index & (AM_HAL_MSPI_MAX_CQ_ENTRIES - 1)] = pfnCallback1;
pMSPIState->pCallbackCtxt[index & (AM_HAL_MSPI_MAX_CQ_ENTRIES - 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();
//
// Post the transaction to the CQ.
// Register for interrupt only if there is a callback
//
ui32Status = am_hal_cmdq_post_block(pMSPIState->CQ.pCmdQHdl, pfnCallback1);
if (AM_HAL_STATUS_SUCCESS != ui32Status)
{
//
// End the critical section.
//
am_hal_interrupt_master_set(ui32Critical);
am_hal_cmdq_release_block(pMSPIState->CQ.pCmdQHdl);
}
else
{
ui32NumPend = pMSPIState->ui32NumCQEntries++;
pMSPIState->ui32NumTransactions++;
if (pCqRaw->pfnCallback)
{
pMSPIState->bAutonomous = false;
pMSPIState->ui32NumUnSolicited = 0;
}
else
{
if (pfnCallback1)
{
// This implies we have already scheduled a dummy callback
pMSPIState->ui32NumUnSolicited = 0;
}
else
{
pMSPIState->ui32NumUnSolicited++;
}
}
//
// End the critical section.
//
am_hal_interrupt_master_set(ui32Critical);
if (ui32NumPend == 0)
{
//
// Enable the Command Queue
//
ui32Status = mspi_cq_enable(pHandle);
if (AM_HAL_STATUS_SUCCESS != ui32Status)
{
return ui32Status;
}
}
}
#else // !AM_HAL_MSPI_CQ
ui32Status = AM_HAL_STATUS_INVALID_ARG;
#endif
break;
}
default:
return AM_HAL_STATUS_INVALID_ARG;
}
//
// Return the status.
//
return ui32Status;
}