uint32_t am_hal_mspi_control()

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;
}