vos/ambiq-hal-sys/ambiq-sparkfun-sdk/mcu/apollo3p/hal/am_hal_cmdq.c

864 lines
32 KiB
C
Raw Normal View History

2022-10-24 06:45:43 +00:00
//*****************************************************************************
//
// am_hal_cmdq.c
//! @file
//!
//! @brief Functions for support command queue operations.
//!
//! @addtogroup
//! @ingroup apollo3phal
//! @{
//
//*****************************************************************************
//*****************************************************************************
//
// Copyright (c) 2020, Ambiq Micro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision 2.4.2 of the AmbiqSuite Development Package.
//
//*****************************************************************************
#include <stdint.h>
#include <stdbool.h>
#include "am_mcu_apollo.h"
#define AM_HAL_MAGIC_CMDQ 0xCDCDCD
#define AM_HAL_CMDQ_CHK_HANDLE(h) ((h) && ((am_hal_handle_prefix_t *)(h))->s.bInit && (((am_hal_handle_prefix_t *)(h))->s.magic == AM_HAL_MAGIC_CMDQ))
// Make sure certain register assumptions are valid - else throw compile error
// Make sure the max HWIDX value is same for BLEIF, MSPI & IOM
// Make sure the CQCFG structure is same for BLEIF, MSPI & IOM
#if ((IOM0_CQCURIDX_CQCURIDX_Msk != MSPI0_CQCURIDX_CQCURIDX_Msk) || \
(IOM0_CQCURIDX_CQCURIDX_Pos != MSPI0_CQCURIDX_CQCURIDX_Pos) || \
(IOM0_CQCFG_CQEN_Pos != MSPI0_CQCFG_CQEN_Pos) || \
(IOM0_CQCFG_CQEN_Msk != MSPI0_CQCFG_CQEN_Msk) || \
(IOM0_CQCFG_CQPRI_Pos != MSPI0_CQCFG_CQPRI_Pos) || \
(IOM0_CQCFG_CQPRI_Msk != MSPI0_CQCFG_CQPRI_Msk) || \
(IOM0_CQCURIDX_CQCURIDX_Msk != BLEIF_CQCURIDX_CQCURIDX_Msk) || \
(IOM0_CQCURIDX_CQCURIDX_Pos != BLEIF_CQCURIDX_CQCURIDX_Pos) || \
(IOM0_CQCFG_CQEN_Pos != BLEIF_CQCFG_CQEN_Pos) || \
(IOM0_CQCFG_CQEN_Msk != BLEIF_CQCFG_CQEN_Msk) || \
(IOM0_CQCFG_CQPRI_Pos != BLEIF_CQCFG_CQPRI_Pos) || \
(IOM0_CQCFG_CQPRI_Msk != BLEIF_CQCFG_CQPRI_Msk) \
)
#error "MSPI and IOM HWIDX, CQCFG implementation needs to match for current CMDQ HAL implementation"
#endif
#define AM_HAL_CMDQ_HW_IDX_MAX (IOM0_CQCURIDX_CQCURIDX_Msk >> IOM0_CQCURIDX_CQCURIDX_Pos) // 8 bit value
#define AM_HAL_CMDQ_ENABLE_CQ(cfgReg) {AM_REGVAL((cfgReg)) |= _VAL2FLD(IOM0_CQCFG_CQEN, IOM0_CQCFG_CQEN_EN); }
#define AM_HAL_CMDQ_DISABLE_CQ(cfgReg) {AM_REGVAL((cfgReg)) &= ~_VAL2FLD(IOM0_CQCFG_CQEN, IOM0_CQCFG_CQEN_EN); }
#define AM_HAL_CMDQ_INIT_CQCFG(cfgReg, pri, enable) {AM_REGVAL((cfgReg)) = _VAL2FLD(IOM0_CQCFG_CQPRI, (pri)) | _VAL2FLD(IOM0_CQCFG_CQEN, (enable)); }
// Need to set the lsb of the CQ entry address for hardware to raise a CQUPD interrupt when processing this entry
#define AM_HAL_CMDQ_ENABLE_CQUPD_INT 0x1
typedef struct
{
volatile uint32_t* regCQCfg;
volatile uint32_t* regCQAddr;
volatile uint32_t* regCurIdx;
volatile uint32_t* regEndIdx;
volatile uint32_t* regCQPause;
uint32_t bitMaskCQPauseIdx;
volatile uint32_t* regCQStat;
// Different hardware blocks have different bit assignments for status flags
uint32_t bitMaskCQStatTIP;
uint32_t bitMaskCQStatErr;
uint32_t bitMaskCQStatPaused;
} am_hal_cmdq_registers_t;
typedef struct
{
am_hal_handle_prefix_t prefix;
uint32_t cmdQBufStart;
uint32_t cmdQBufEnd;
uint32_t cmdQHead;
uint32_t cmdQTail;
uint32_t cmdQNextTail;
uint32_t cmdQSize;
uint32_t curIdx;
uint32_t endIdx;
const am_hal_cmdq_registers_t *pReg;
uint32_t rawSeqStart;
} am_hal_cmdq_t;
// Global variables
static am_hal_cmdq_t gAmHalCmdq[AM_HAL_CMDQ_IF_MAX];
static const am_hal_cmdq_registers_t gAmHalCmdQReg[AM_HAL_CMDQ_IF_MAX] =
{
// AM_HAL_CMDQ_IF_IOM0
{
&IOM0->CQCFG, &IOM0->CQADDR,
&IOM0->CQCURIDX, &IOM0->CQENDIDX,
&IOM0->CQPAUSEEN, IOM0_CQPAUSEEN_CQPEN_IDXEQ,
&IOM0->CQSTAT, IOM0_CQSTAT_CQTIP_Msk,
IOM0_CQSTAT_CQERR_Msk, IOM0_CQSTAT_CQPAUSED_Msk
},
// AM_HAL_CMDQ_IF_IOM1
{
&IOM1->CQCFG, &IOM1->CQADDR,
&IOM1->CQCURIDX, &IOM1->CQENDIDX,
&IOM1->CQPAUSEEN, IOM0_CQPAUSEEN_CQPEN_IDXEQ,
&IOM1->CQSTAT, IOM0_CQSTAT_CQTIP_Msk,
IOM0_CQSTAT_CQERR_Msk, IOM0_CQSTAT_CQPAUSED_Msk
},
// AM_HAL_CMDQ_IF_IOM2
{
&IOM2->CQCFG, &IOM2->CQADDR,
&IOM2->CQCURIDX, &IOM2->CQENDIDX,
&IOM2->CQPAUSEEN, IOM0_CQPAUSEEN_CQPEN_IDXEQ,
&IOM2->CQSTAT, IOM0_CQSTAT_CQTIP_Msk,
IOM0_CQSTAT_CQERR_Msk, IOM0_CQSTAT_CQPAUSED_Msk
},
// AM_HAL_CMDQ_IF_IOM3
{
&IOM3->CQCFG, &IOM3->CQADDR,
&IOM3->CQCURIDX, &IOM3->CQENDIDX,
&IOM3->CQPAUSEEN, IOM0_CQPAUSEEN_CQPEN_IDXEQ,
&IOM3->CQSTAT, IOM0_CQSTAT_CQTIP_Msk,
IOM0_CQSTAT_CQERR_Msk, IOM0_CQSTAT_CQPAUSED_Msk
},
// AM_HAL_CMDQ_IF_IOM4
{
&IOM4->CQCFG, &IOM4->CQADDR,
&IOM4->CQCURIDX, &IOM4->CQENDIDX,
&IOM4->CQPAUSEEN, IOM0_CQPAUSEEN_CQPEN_IDXEQ,
&IOM4->CQSTAT, IOM0_CQSTAT_CQTIP_Msk,
IOM0_CQSTAT_CQERR_Msk, IOM0_CQSTAT_CQPAUSED_Msk
},
// AM_HAL_CMDQ_IF_IOM5
{
&IOM5->CQCFG, &IOM5->CQADDR,
&IOM5->CQCURIDX, &IOM5->CQENDIDX,
&IOM5->CQPAUSEEN, IOM0_CQPAUSEEN_CQPEN_IDXEQ,
&IOM5->CQSTAT, IOM0_CQSTAT_CQTIP_Msk,
IOM0_CQSTAT_CQERR_Msk, IOM0_CQSTAT_CQPAUSED_Msk
},
// AM_HAL_CMDQ_IF_MSPI0
{
&MSPI0->CQCFG, &MSPI0->CQADDR,
&MSPI0->CQCURIDX, &MSPI0->CQENDIDX,
&MSPI0->CQPAUSE, MSPI0_CQPAUSE_CQMASK_CQIDX,
&MSPI0->CQSTAT, MSPI0_CQSTAT_CQTIP_Msk,
MSPI0_CQSTAT_CQERR_Msk, MSPI0_CQSTAT_CQPAUSED_Msk
},
// AM_HAL_CMDQ_IF_MSPI1
{
&MSPI1->CQCFG, &MSPI1->CQADDR,
&MSPI1->CQCURIDX, &MSPI1->CQENDIDX,
&MSPI1->CQPAUSE, MSPI0_CQPAUSE_CQMASK_CQIDX,
&MSPI1->CQSTAT, MSPI0_CQSTAT_CQTIP_Msk,
MSPI0_CQSTAT_CQERR_Msk, MSPI0_CQSTAT_CQPAUSED_Msk
},
// AM_HAL_CMDQ_IF_MSPI2
{
&MSPI2->CQCFG, &MSPI2->CQADDR,
&MSPI2->CQCURIDX, &MSPI2->CQENDIDX,
&MSPI2->CQPAUSE, MSPI0_CQPAUSE_CQMASK_CQIDX,
&MSPI2->CQSTAT, MSPI0_CQSTAT_CQTIP_Msk,
MSPI0_CQSTAT_CQERR_Msk, MSPI0_CQSTAT_CQPAUSED_Msk
},
// AM_HAL_CMDQ_IF_BLEIF
{
&BLEIF->CQCFG, &BLEIF->CQADDR,
&BLEIF->CQCURIDX, &BLEIF->CQENDIDX,
&BLEIF->CQPAUSEEN, BLEIF_CQPAUSEEN_CQPEN_CNTEQ,
&BLEIF->CQSTAT, BLEIF_CQSTAT_CQTIP_Msk,
BLEIF_CQSTAT_CQERR_Msk, BLEIF_CQSTAT_CQPAUSED_Msk
},
};
// Sync up with the current hardware indices and pointers
static void
update_indices(am_hal_cmdq_t *pCmdQ)
{
int32_t hwCurIdx;
//
// Start a critical section.
//
AM_CRITICAL_BEGIN
hwCurIdx = AM_REGVAL(pCmdQ->pReg->regCurIdx) & AM_HAL_CMDQ_HW_IDX_MAX;
// Derive the 32b values from the current hardware index values
// It is guaranteed that pCmdQ->endIdx is <= pCmdQ->curIdx + AM_HAL_CMDQ_HW_IDX_MAX - 1
pCmdQ->curIdx = (pCmdQ->endIdx & ~AM_HAL_CMDQ_HW_IDX_MAX) | hwCurIdx;
if (AM_HAL_U32_SMALLER(pCmdQ->endIdx, pCmdQ->curIdx))
{
pCmdQ->curIdx -= (AM_HAL_CMDQ_HW_IDX_MAX + 1);
}
pCmdQ->cmdQHead = AM_REGVAL(pCmdQ->pReg->regCQAddr);
//
// End the critical section.
//
AM_CRITICAL_END
}
//*****************************************************************************
//
//! @brief Initialize a Command Queue
//!
//! Initializes the command queue data structure for the given interface
//!
//! @param hwIf identifies the underlying hardware interface
//! @param cmdQSize Size of supplied memory in multiple of 8 Bytes
//! @param pCmdQBuf Command Queue Buffer
//! @param ppHandle Return Parameter - handle for the command queue
//!
//! @return Returns 0 on success
//
//*****************************************************************************
uint32_t am_hal_cmdq_init(am_hal_cmdq_if_e hwIf, am_hal_cmdq_cfg_t *pCfg, void **ppHandle)
{
am_hal_cmdq_t *pCmdQ;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (hwIf >= AM_HAL_CMDQ_IF_MAX)
{
return AM_HAL_STATUS_OUT_OF_RANGE;
}
if (!pCfg || !pCfg->pCmdQBuf || !ppHandle || (pCfg->cmdQSize < 2))
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (gAmHalCmdq[hwIf].prefix.s.bInit)
{
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
pCmdQ = &gAmHalCmdq[hwIf];
pCmdQ->cmdQSize = pCfg->cmdQSize * sizeof(am_hal_cmdq_entry_t);
pCmdQ->cmdQTail = pCmdQ->cmdQNextTail = pCmdQ->cmdQHead = pCmdQ->cmdQBufStart = (uint32_t)pCfg->pCmdQBuf;
pCmdQ->cmdQBufEnd = (uint32_t)pCfg->pCmdQBuf + pCfg->cmdQSize * sizeof(am_hal_cmdq_entry_t);
pCmdQ->prefix.s.bInit = true;
pCmdQ->prefix.s.bEnable = false;
pCmdQ->prefix.s.magic = AM_HAL_MAGIC_CMDQ;
pCmdQ->pReg = &gAmHalCmdQReg[hwIf];
pCmdQ->curIdx = 0;
pCmdQ->endIdx = 0;
AM_REGVAL(pCmdQ->pReg->regCurIdx) = 0;
AM_REGVAL(pCmdQ->pReg->regEndIdx) = 0;
AM_REGVAL(pCmdQ->pReg->regCQPause) |= pCmdQ->pReg->bitMaskCQPauseIdx;
// Initialize the hardware registers
AM_REGVAL(pCmdQ->pReg->regCQAddr) = (uint32_t)pCfg->pCmdQBuf;
AM_HAL_CMDQ_INIT_CQCFG(pCmdQ->pReg->regCQCfg, pCfg->priority, false);
*ppHandle = pCmdQ;
return AM_HAL_STATUS_SUCCESS;
}
//*****************************************************************************
//
//! @brief Enable a Command Queue
//!
//! Enables the command queue for the given interface
//!
//! @param pHandle handle for the command queue
//!
//! @return Returns 0 on success
//
//*****************************************************************************
uint32_t am_hal_cmdq_enable(void *pHandle)
{
am_hal_cmdq_t *pCmdQ = (am_hal_cmdq_t *)pHandle;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!AM_HAL_CMDQ_CHK_HANDLE(pHandle))
{
return AM_HAL_STATUS_INVALID_HANDLE;
}
if (pCmdQ->prefix.s.bEnable)
{
return AM_HAL_STATUS_SUCCESS;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
AM_HAL_CMDQ_ENABLE_CQ(pCmdQ->pReg->regCQCfg);
pCmdQ->prefix.s.bEnable = true;
return AM_HAL_STATUS_SUCCESS;
}
//*****************************************************************************
//
//! @brief Disable a Command Queue
//!
//! Disables the command queue for the given interface
//!
//! @param pHandle handle for the command queue
//!
//! @return Returns 0 on success
//
//*****************************************************************************
uint32_t am_hal_cmdq_disable(void *pHandle)
{
am_hal_cmdq_t *pCmdQ = (am_hal_cmdq_t *)pHandle;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!AM_HAL_CMDQ_CHK_HANDLE(pHandle))
{
return AM_HAL_STATUS_INVALID_HANDLE;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
if (!pCmdQ->prefix.s.bEnable)
{
return AM_HAL_STATUS_SUCCESS;
}
AM_HAL_CMDQ_DISABLE_CQ(pCmdQ->pReg->regCQCfg);
pCmdQ->prefix.s.bEnable = false;
return AM_HAL_STATUS_SUCCESS;
}
//*****************************************************************************
//
//! @brief Allocate a block of commands for posting to a command queue
//!
//! Allocates a contiguous block of command queue entries from the available
//! space in command queue
//!
//! @param pHandle handle for the command queue
//! @param numCmd Size of the command block (each block being 8 bytes)
//! @param ppBlock - Return parameter - Pointer to contiguous block of commands,
//! which can be posted
//! @param pIdx - Return parameter - monotonically increasing transaction index
//!
//! This function will take care of determining that enough space is available
//! to create the desired block. It also takes care of necessary wrap-around
//!
//! @return Returns 0 on success
//
//*****************************************************************************
uint32_t am_hal_cmdq_alloc_block(void *pHandle, uint32_t numCmd, am_hal_cmdq_entry_t **ppBlock, uint32_t *pIdx)
{
am_hal_cmdq_t *pCmdQ = (am_hal_cmdq_t *)pHandle;
am_hal_cmdq_entry_t *pCmdQEntry;
uint32_t blockAddr;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!AM_HAL_CMDQ_CHK_HANDLE(pHandle))
{
return AM_HAL_STATUS_INVALID_HANDLE;
}
if (!ppBlock || !pIdx)
{
return AM_HAL_STATUS_INVALID_ARG;
}
if (pCmdQ->cmdQTail != pCmdQ->cmdQNextTail)
{
// Previously allocated block has not been posted/aborted yet
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
update_indices(pCmdQ);
// We need to not use the hwIdx completely, as otherwise we can not distinguish between
// Empty and full case
if (AM_HAL_U32_SMALLER((pCmdQ->curIdx + AM_HAL_CMDQ_HW_IDX_MAX - 1), (pCmdQ->endIdx)))
{
return AM_HAL_STATUS_OUT_OF_RANGE;
}
// Determine if we can allocate the block, and if so, where
if (pCmdQ->cmdQTail >= pCmdQ->cmdQHead)
{
// Choices: Following last block if there is enough space before wrap
// Otherwise, need to allocate block from the top of the memory
// For a sequence - we'll always come to this case, as the sequence is not started before building
// Need space for 2 more entries - one for updating curIdx, other for CQ Wrap
if ((pCmdQ->cmdQTail + (numCmd + 2)*sizeof(am_hal_cmdq_entry_t)) <= pCmdQ->cmdQBufEnd)
{
// Enough space in the queue without wrap
blockAddr = pCmdQ->cmdQTail;
}
else
{
// Need to wrap
// Need space for 1 more entry - for updating curIdx
if ((pCmdQ->cmdQBufStart + (numCmd + 1) * sizeof(am_hal_cmdq_entry_t)) < pCmdQ->cmdQHead)
{
// Initialize the tail of CmdQ for Wrap
pCmdQEntry = (am_hal_cmdq_entry_t *)pCmdQ->cmdQTail;
pCmdQEntry->address = (uint32_t)pCmdQ->pReg->regCQAddr;
pCmdQEntry->value = pCmdQ->cmdQBufStart;
blockAddr = pCmdQ->cmdQBufStart;
}
else
{
return AM_HAL_STATUS_OUT_OF_RANGE;
}
}
}
else
{
// Need space for 1 more entry - for updating curIdx
if ((pCmdQ->cmdQTail + (numCmd + 1) * sizeof(am_hal_cmdq_entry_t)) < pCmdQ->cmdQHead)
{
blockAddr = pCmdQ->cmdQTail;
}
else
{
return AM_HAL_STATUS_OUT_OF_RANGE;
}
}
*ppBlock = (am_hal_cmdq_entry_t *)blockAddr;
*pIdx = ++pCmdQ->endIdx;
pCmdQ->cmdQNextTail = blockAddr + numCmd * sizeof(am_hal_cmdq_entry_t);
return AM_HAL_STATUS_SUCCESS;
}
//*****************************************************************************
//
//! @brief Release a block of commands previously allocated
//!
//! Releases the contiguous block of command queue entries previously allocated
//! without posting
//!
//! @param pHandle handle for the command queue
//!
//! This function will internally handles the curIdx/endIdx manipulation.
//! It also takes care of necessary wrap-around
//!
//! @return Returns 0 on success
//
//*****************************************************************************
uint32_t am_hal_cmdq_release_block(void *pHandle)
{
am_hal_cmdq_t *pCmdQ = (am_hal_cmdq_t *)pHandle;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!AM_HAL_CMDQ_CHK_HANDLE(pHandle))
{
return AM_HAL_STATUS_INVALID_HANDLE;
}
if (pCmdQ->cmdQTail == pCmdQ->cmdQNextTail)
{
// No block has been allocated
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
// Free up the block
pCmdQ->cmdQNextTail = pCmdQ->cmdQTail;
pCmdQ->endIdx--;
return AM_HAL_STATUS_SUCCESS;
}
//*****************************************************************************
//
//! @brief Post the last block allocated
//!
//! Post the contiguous block of command queue entries previously allocated
//!
//! @param pHandle handle for the command queue
//! @param bInt Whether the UPD interrupt is desired once the block is processed
//!
//! @return Returns 0 on success
//
//*****************************************************************************
uint32_t am_hal_cmdq_post_block(void *pHandle, bool bInt)
{
am_hal_cmdq_t *pCmdQ = (am_hal_cmdq_t *)pHandle;
am_hal_cmdq_entry_t *pCmdQEntry;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!AM_HAL_CMDQ_CHK_HANDLE(pHandle))
{
return AM_HAL_STATUS_INVALID_HANDLE;
}
if (pCmdQ->cmdQTail == pCmdQ->cmdQNextTail)
{
// No block has been allocated
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
// CmdQ entries have already been populated. Just need to inform hardware of the new endIdx
// Fill up the index update entry
pCmdQEntry = (am_hal_cmdq_entry_t *)pCmdQ->cmdQNextTail;
pCmdQEntry->address = ((uint32_t)pCmdQ->pReg->regCurIdx) | (bInt ? AM_HAL_CMDQ_ENABLE_CQUPD_INT : 0);
pCmdQEntry->value = pCmdQ->endIdx;
// cmdQNextTail should now point to the first entry after the allocated block
pCmdQ->cmdQTail = pCmdQ->cmdQNextTail = (uint32_t)(pCmdQEntry + 1);
AM_REGVAL(pCmdQ->pReg->regEndIdx) = pCmdQ->endIdx & AM_HAL_CMDQ_HW_IDX_MAX;
return AM_HAL_STATUS_SUCCESS;
}
//*****************************************************************************
//
//! @brief Get Command Queue status
//!
//! Get the current state of the Command queue
//!
//! @param pHandle handle for the command queue
//! @param pStatus Return Parameter - status information
//!
//! @return Returns 0 on success
//
//*****************************************************************************
uint32_t am_hal_cmdq_get_status(void *pHandle, am_hal_cmdq_status_t *pStatus)
{
am_hal_cmdq_t *pCmdQ = (am_hal_cmdq_t *)pHandle;
uint32_t status;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!AM_HAL_CMDQ_CHK_HANDLE(pHandle))
{
return AM_HAL_STATUS_INVALID_HANDLE;
}
if (!pStatus)
{
return AM_HAL_STATUS_INVALID_ARG;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
update_indices(pCmdQ);
pStatus->lastIdxProcessed = pCmdQ->curIdx;
pStatus->lastIdxAllocated = pCmdQ->endIdx;
pStatus->lastIdxPosted = pCmdQ->endIdx - ((pCmdQ->cmdQNextTail == pCmdQ->cmdQTail) ? 0 : 1);
status = AM_REGVAL(pCmdQ->pReg->regCQStat);
pStatus->bTIP = status & pCmdQ->pReg->bitMaskCQStatTIP;
pStatus->bPaused = status & pCmdQ->pReg->bitMaskCQStatPaused;
pStatus->bErr = status & pCmdQ->pReg->bitMaskCQStatErr;
return AM_HAL_STATUS_SUCCESS;
}
//*****************************************************************************
//
//! @brief Terminate a Command Queue
//!
//! Terminates the command queue data structure
//!
//! @param pHandle handle for the command queue
//!
//! @return Returns 0 on success
//
//*****************************************************************************
uint32_t am_hal_cmdq_term(void *pHandle, bool bForce)
{
am_hal_cmdq_t *pCmdQ = (am_hal_cmdq_t *)pHandle;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!AM_HAL_CMDQ_CHK_HANDLE(pHandle))
{
return AM_HAL_STATUS_INVALID_HANDLE;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
update_indices(pCmdQ);
if (!bForce && (pCmdQ->curIdx != pCmdQ->endIdx))
{
return AM_HAL_STATUS_IN_USE;
}
pCmdQ->prefix.s.bInit = false;
// Disable Command Queue
AM_HAL_CMDQ_DISABLE_CQ(pCmdQ->pReg->regCQCfg);
AM_REGVAL(pCmdQ->pReg->regCQPause) &= ~pCmdQ->pReg->bitMaskCQPauseIdx;
return AM_HAL_STATUS_SUCCESS;
}
//*****************************************************************************
//
//! @brief Clear the CQ error and resume with the next transaction.
//! The CQ is left disabled after this call
//! It is the responsibility of the caller to re-enable the CQ
//!
//! @param pHandle handle for the command queue
//!
//! @return Returns 0 on success
//
//*****************************************************************************
uint32_t am_hal_cmdq_error_resume(void *pHandle)
{
am_hal_cmdq_t *pCmdQ = (am_hal_cmdq_t *)pHandle;
am_hal_cmdq_entry_t *pCQAddr;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!AM_HAL_CMDQ_CHK_HANDLE(pHandle))
{
return AM_HAL_STATUS_INVALID_HANDLE;
}
if (!pCmdQ->prefix.s.bEnable)
{
return AM_HAL_STATUS_SUCCESS;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
// First Disable the Command Queue
AM_HAL_CMDQ_DISABLE_CQ(pCmdQ->pReg->regCQCfg);
// Need to identify end of block for the transaction where hardware is stuck
// Move the CQADDR to the last entry in the block which will update the curIdx
// and then move on.
pCQAddr = (am_hal_cmdq_entry_t *)AM_REGVAL(pCmdQ->pReg->regCQAddr);
while ((pCQAddr->address & ~AM_HAL_CMDQ_ENABLE_CQUPD_INT) != (uint32_t)(pCmdQ->pReg->regCurIdx))
{
// Is this element changing the CQ Address itself?
if (pCQAddr->address == (uint32_t)(pCmdQ->pReg->regCQAddr))
{
pCQAddr = (am_hal_cmdq_entry_t *)pCQAddr->value;
}
else
{
++pCQAddr;
}
}
// The pCQAddr now points to the address of the command which will update the curIdx
// Disable update interrupt, as we would have already handled this error
*(&pCQAddr->address) = (uint32_t)pCmdQ->pReg->regCurIdx;
AM_REGVAL(pCmdQ->pReg->regCQAddr) = (uint32_t)pCQAddr;
pCmdQ->prefix.s.bEnable = false;
return AM_HAL_STATUS_SUCCESS;
}
//*****************************************************************************
//
//! @brief Pause the CQ after finishing the current transaction.
//! The CQ is in paused state after this function returns, at the beginning of next transaction
//!
//! @param pHandle handle for the command queue
//! @param pSETCLRAddr Points to the SETCLR register for the module
//! @param ui32CQPauseSETCLR Value to be written to Pause the CQ
//! @param ui32CQUnpauseSETCLR Value to be written to unpause the CQ
//! @param ui32usMaxDelay Max time to wait (in uS)
//!
//! @return Returns 0 on success
//
//*****************************************************************************
uint32_t am_hal_cmdq_pause(void *pHandle, uint32_t *pSETCLRAddr, uint32_t ui32CQPauseSETCLR, uint32_t ui32CQUnpauseSETCLR, uint32_t ui32usMaxDelay)
{
am_hal_cmdq_t *pCmdQ = (am_hal_cmdq_t *)pHandle;
uint32_t cqAddr;
am_hal_cmdq_entry_t *pCQAddr;
am_hal_cmdq_entry_t cqEntry;
uint32_t status = AM_HAL_STATUS_SUCCESS;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!AM_HAL_CMDQ_CHK_HANDLE(pHandle))
{
return AM_HAL_STATUS_INVALID_HANDLE;
}
if (!pCmdQ->prefix.s.bEnable)
{
return AM_HAL_STATUS_SUCCESS;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
// First Pause the Command Queue
*pSETCLRAddr = ui32CQPauseSETCLR;
status = am_hal_flash_delay_status_change(ui32usMaxDelay, (uint32_t)pCmdQ->pReg->regCQStat, pCmdQ->pReg->bitMaskCQStatPaused, pCmdQ->pReg->bitMaskCQStatPaused);
if (status != AM_HAL_STATUS_SUCCESS)
{
return status;
}
// Now seek for the end of current transaction
cqAddr = AM_REGVAL(pCmdQ->pReg->regCQAddr);
if (cqAddr == pCmdQ->cmdQNextTail)
{
// Already at the end
// No need to do anything else
}
else
{
// Need to identify end of block for the transaction
pCQAddr = (am_hal_cmdq_entry_t *)cqAddr;
while ((pCQAddr->address & ~AM_HAL_CMDQ_ENABLE_CQUPD_INT) != (uint32_t)(pCmdQ->pReg->regCurIdx))
{
if ( (uint32_t) ++pCQAddr >= pCmdQ->cmdQBufEnd )
{
// This should not happen
return AM_HAL_STATUS_FAIL;
}
}
// The pCQAddr now points to the address of the command which will update the curIdx
// We need to resume the CQ till it finishes this entry
// For that we'll temporarily replace the next entry to cause a Pause
// Backup the current content
cqEntry = *(++pCQAddr);
pCQAddr->address = (uint32_t)pSETCLRAddr;
pCQAddr->value = ui32CQPauseSETCLR;
// Wait for it to execute this new entry, or get paused for some other condition
do
{
// Resume the CQ
*pSETCLRAddr = ui32CQUnpauseSETCLR;
// Ensure CQ sees it
am_hal_flash_delay(3);
// Now wait for it to be paused again
status = am_hal_flash_delay_status_change(ui32usMaxDelay, (uint32_t)pCmdQ->pReg->regCQStat, pCmdQ->pReg->bitMaskCQStatPaused, pCmdQ->pReg->bitMaskCQStatPaused);
if (status != AM_HAL_STATUS_SUCCESS)
{
return status;
}
// Try Setting the PAUSE condition while in same position
*pSETCLRAddr = ui32CQPauseSETCLR;
// Ensure CQ sees it
am_hal_flash_delay(3);
status = am_hal_flash_delay_status_change(ui32usMaxDelay, (uint32_t)pCmdQ->pReg->regCQStat, pCmdQ->pReg->bitMaskCQStatPaused, pCmdQ->pReg->bitMaskCQStatPaused);
if (status != AM_HAL_STATUS_SUCCESS)
{
return status;
}
if (cqAddr == AM_REGVAL(pCmdQ->pReg->regCQAddr))
{
// CQ no longer moving
break;
}
else
{
cqAddr = AM_REGVAL(pCmdQ->pReg->regCQAddr);
}
#if 0
// Now that it is paused - check if we have reached our entry - or it paused somewhere else
cqAddr = AM_REGVAL(pCmdQ->pReg->regCQAddr);
if (cqAddr != (uint32_t)(pCQAddr + 1))
{
// It paused due to some other reason
// Try Setting the PAUSE condition while in same position
*pSETCLRAddr = ui32CQPauseSETCLR;
// Ensure CQ sees it
am_hal_flash_delay(3);
status = am_hal_flash_delay_status_change(ui32usMaxDelay, (uint32_t)pCmdQ->pReg->regCQStat, pCmdQ->pReg->bitMaskCQStatPaused, pCmdQ->pReg->bitMaskCQStatPaused);
if (status != AM_HAL_STATUS_SUCCESS)
{
return status;
}
if (AM_REGVAL(pCmdQ->pReg->regCQAddr) == cqAddr)
{
// CQ did not move after we set the PAUSE - so it is at a designated pause place
// Safe to return now
break;
}
else
{
// CQ is moving...need to retry
}
}
else
{
// Reached the desired place
break;
}
#endif
} while(1);
// Now let's revert the CQ content and set the CQADDR to correct place for it to resume later
// when the CQ is unpaused
*pCQAddr = cqEntry;
if (AM_REGVAL(pCmdQ->pReg->regCQAddr) == (uint32_t)(pCQAddr + 1))
{
AM_REGVAL(pCmdQ->pReg->regCQAddr) = (uint32_t)pCQAddr;
}
}
return AM_HAL_STATUS_SUCCESS;
}
//*****************************************************************************
//
//! @brief Reset the Command Queue
//!
//! Reset the Command Queue & associated data structures
//! This will force the CQ reset
//! Caller needs to ensure CQ is in steady state before this is done
//! This also disables the CQ
//!
//! @param pHandle handle for the command queue
//!
//! @return Returns 0 on success
//
//*****************************************************************************
uint32_t am_hal_cmdq_reset(void *pHandle)
{
am_hal_cmdq_t *pCmdQ = (am_hal_cmdq_t *)pHandle;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!AM_HAL_CMDQ_CHK_HANDLE(pHandle))
{
return AM_HAL_STATUS_INVALID_HANDLE;
}
if (pCmdQ->prefix.s.bEnable)
{
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
AM_HAL_CMDQ_DISABLE_CQ(pCmdQ->pReg->regCQCfg);
pCmdQ->cmdQTail = pCmdQ->cmdQNextTail = pCmdQ->cmdQHead = pCmdQ->cmdQBufStart;
pCmdQ->curIdx = 0;
pCmdQ->endIdx = 0;
AM_REGVAL(pCmdQ->pReg->regCurIdx) = 0;
AM_REGVAL(pCmdQ->pReg->regEndIdx) = 0;
// Initialize the hardware registers
AM_REGVAL(pCmdQ->pReg->regCQAddr) = pCmdQ->cmdQBufStart;
pCmdQ->prefix.s.bEnable = false;
return AM_HAL_STATUS_SUCCESS;
}
//*****************************************************************************
//
//! @brief Post the last block allocated with the additional wrap to start
//!
//! Post the contiguous block of command queue entries previously allocated
//! with the additional wrap to start
//!
//! @param pHandle handle for the command queue
//! @param bInt Whether the UPD interrupt is desired once the block is processed
//!
//! @return Returns 0 on success
//
//*****************************************************************************
uint32_t am_hal_cmdq_post_loop_block(void *pHandle, bool bInt)
{
am_hal_cmdq_t *pCmdQ = (am_hal_cmdq_t *)pHandle;
am_hal_cmdq_entry_t *pCmdQEntry;
#ifndef AM_HAL_DISABLE_API_VALIDATION
if (!AM_HAL_CMDQ_CHK_HANDLE(pHandle))
{
return AM_HAL_STATUS_INVALID_HANDLE;
}
if (pCmdQ->cmdQTail == pCmdQ->cmdQNextTail)
{
// No block has been allocated
return AM_HAL_STATUS_INVALID_OPERATION;
}
#endif // AM_HAL_DISABLE_API_VALIDATION
// CmdQ entries have already been populated. Just need to inform hardware of the new endIdx
// Reset the index to 0
pCmdQEntry = (am_hal_cmdq_entry_t *)pCmdQ->cmdQNextTail;
pCmdQEntry->address = (uint32_t)pCmdQ->pReg->regCurIdx;
pCmdQEntry->value = 0;
pCmdQEntry++;
// Fill up the loopback entry
// At the alloc time, we were guaranteed one extra entry for loopback
pCmdQEntry->address = (uint32_t)pCmdQ->pReg->regCQAddr | (bInt ? AM_HAL_CMDQ_ENABLE_CQUPD_INT : 0);
pCmdQEntry->value = pCmdQ->cmdQBufStart;
// cmdQNextTail should now point to the first entry after the allocated block
pCmdQ->cmdQTail = pCmdQ->cmdQNextTail = (uint32_t)(pCmdQEntry + 1);
// Since we are not updating the curIdx - this will cause CQ to run indefinetely
AM_REGVAL(pCmdQ->pReg->regEndIdx) = pCmdQ->endIdx & AM_HAL_CMDQ_HW_IDX_MAX;
return AM_HAL_STATUS_SUCCESS;
}