uint32_t am_hal_iom_control()

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