3427 lines
111 KiB
C
3427 lines
111 KiB
C
//*****************************************************************************
|
|
//
|
|
// am_hal_mspi.c
|
|
//! @file
|
|
//!
|
|
//! @brief Functions for interfacing with the MSPI.
|
|
//!
|
|
//! @addtogroup mspi3p Multi-bit SPI (MSPI)
|
|
//! @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"
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// Private Types.
|
|
//
|
|
//*****************************************************************************
|
|
|
|
#define AM_HAL_MAGIC_MSPI 0xBEBEBE
|
|
#define AM_HAL_MSPI_CHK_HANDLE(h) ((h) && ((am_hal_handle_prefix_t *)(h))->s.bInit && (((am_hal_handle_prefix_t *)(h))->s.magic == AM_HAL_MAGIC_MSPI))
|
|
#define AM_HAL_MSPI_HW_IDX_MAX (AM_REG_MSPI_CQCURIDX_CQCURIDX_M >> AM_REG_MSPI_CQCURIDX_CQCURIDX_S) // 8 bit value
|
|
#define AM_HAL_MSPI_MAX_CQ_ENTRIES (256)
|
|
#define AM_HAL_MSPI_OCTAL_MODULE (1)
|
|
|
|
|
|
|
|
// For MSPI - Need to Set the flag for unpausing
|
|
#define AM_HAL_MSPI_SC_PAUSE_CQ AM_HAL_MSPI_SC_PAUSE(AM_HAL_MSPI_PAUSE_FLAG_CQ)
|
|
#define AM_HAL_MSPI_SC_PAUSE_SEQLOOP AM_HAL_MSPI_SC_PAUSE(AM_HAL_MSPI_PAUSE_FLAG_SEQLOOP)
|
|
#define AM_HAL_MSPI_SC_UNPAUSE_CQ AM_HAL_MSPI_SC_UNPAUSE(AM_HAL_MSPI_PAUSE_FLAG_CQ)
|
|
#define AM_HAL_MSPI_SC_UNPAUSE_SEQLOOP AM_HAL_MSPI_SC_UNPAUSE(AM_HAL_MSPI_PAUSE_FLAG_SEQLOOP)
|
|
#define AM_HAL_MSPI_SC_PAUSE_BLOCK AM_HAL_MSPI_SC_PAUSE(AM_HAL_MSPI_PAUSE_FLAG_BLOCK)
|
|
#define AM_HAL_MSPI_SC_UNPAUSE_BLOCK AM_HAL_MSPI_SC_UNPAUSE(AM_HAL_MSPI_PAUSE_FLAG_BLOCK)
|
|
|
|
|
|
// Max time to wait when attempting to pause the command queue
|
|
#define AM_HAL_MSPI_MAX_PAUSE_DELAY (100*1000) // 100ms
|
|
|
|
//
|
|
// MSPI interface mode and chip enable selection.
|
|
// This is an internal extension to am_hal_mspi_device_e
|
|
//
|
|
typedef enum
|
|
{
|
|
AM_HAL_MSPI_FLASH_DUAL_CE0_1_1_2 = AM_HAL_MSPI_FLASH_QUADPAIRED_SERIAL + 1,
|
|
AM_HAL_MSPI_FLASH_DUAL_CE1_1_1_2,
|
|
AM_HAL_MSPI_FLASH_DUAL_CE0_1_2_2,
|
|
AM_HAL_MSPI_FLASH_DUAL_CE1_1_2_2,
|
|
AM_HAL_MSPI_FLASH_QUAD_CE0_1_1_4,
|
|
AM_HAL_MSPI_FLASH_QUAD_CE1_1_1_4,
|
|
AM_HAL_MSPI_FLASH_QUAD_CE0_1_4_4,
|
|
AM_HAL_MSPI_FLASH_QUAD_CE1_1_4_4,
|
|
AM_HAL_MSPI_FLASH_SERIAL_CE0_3WIRE,
|
|
AM_HAL_MSPI_FLASH_SERIAL_CE1_3WIRE,
|
|
} mspi_device_e;
|
|
|
|
//
|
|
// Command Queue entry structure for DMA transfer.
|
|
//
|
|
typedef struct
|
|
{
|
|
uint32_t ui32PAUSENAddr;
|
|
uint32_t ui32PAUSEENVal;
|
|
uint32_t ui32PAUSEN2Addr;
|
|
uint32_t ui32PAUSEEN2Val;
|
|
uint32_t ui32DMATARGADDRAddr;
|
|
uint32_t ui32DMATARGADDRVal;
|
|
uint32_t ui32DMADEVADDRAddr;
|
|
uint32_t ui32DMADEVADDRVal;
|
|
uint32_t ui32DMATOTCOUNTAddr;
|
|
uint32_t ui32DMATOTCOUNTVal;
|
|
uint32_t ui32DMACFG1Addr;
|
|
uint32_t ui32DMACFG1Val;
|
|
// Need to insert couple of Dummy's
|
|
uint32_t ui32DummyAddr1;
|
|
uint32_t ui32DummyVal1;
|
|
uint32_t ui32DummyAddr2;
|
|
uint32_t ui32DummyVal2;
|
|
// Need to disable the DMA to prepare for next reconfig
|
|
// Need to have this following the DMAEN for CMDQ
|
|
uint32_t ui32DMACFG2Addr;
|
|
uint32_t ui32DMACFG2Val;
|
|
uint32_t ui32SETCLRAddr;
|
|
uint32_t ui32SETCLRVal;
|
|
} am_hal_mspi_cq_dma_entry_t;
|
|
|
|
//
|
|
// structure for Hi Prio DMA transfer.
|
|
//
|
|
typedef struct
|
|
{
|
|
uint32_t ui32DMATARGADDRVal;
|
|
uint32_t ui32DMADEVADDRVal;
|
|
uint32_t ui32DMATOTCOUNTVal;
|
|
uint32_t ui32DMACFG1Val;
|
|
am_hal_mspi_callback_t pfnCallback;
|
|
void *pCallbackCtxt;
|
|
} am_hal_mspi_dma_entry_t;
|
|
|
|
//
|
|
// Command Queue entry structure for Sequence Repeat
|
|
//
|
|
typedef struct
|
|
{
|
|
uint32_t ui32PAUSENAddr;
|
|
uint32_t ui32PAUSEENVal;
|
|
uint32_t ui32PAUSEN2Addr;
|
|
uint32_t ui32PAUSEEN2Val;
|
|
uint32_t ui32SETCLRAddr;
|
|
uint32_t ui32SETCLRVal;
|
|
} am_hal_mspi_cq_loop_entry_t;
|
|
|
|
//
|
|
// Command Queue entry structure for PIO transfer.
|
|
//
|
|
typedef struct
|
|
{
|
|
uint32_t ui32ADDRAddr;
|
|
uint32_t ui32ADDRVal;
|
|
uint32_t ui32INSTRAddr;
|
|
uint32_t ui32INSTRVal;
|
|
uint32_t ui32CTRLAddr;
|
|
uint32_t ui32CTRLVal;
|
|
} am_hal_mspi_cq_pio_entry_t;
|
|
|
|
typedef struct
|
|
{
|
|
bool bValid;
|
|
uint32_t regCFG;
|
|
uint32_t regMSPICFG;
|
|
uint32_t regPADCFG;
|
|
uint32_t regPADOUTEN;
|
|
uint32_t regFLASH;
|
|
uint32_t regSCRAMBLING;
|
|
uint32_t regCQCFG;
|
|
uint32_t regCQADDR;
|
|
uint32_t regCQPAUSE;
|
|
uint32_t regCQCURIDX;
|
|
uint32_t regCQENDIDX;
|
|
uint32_t regINTEN;
|
|
// TODO: May be no need to preserve these values, as they are constants anyways?
|
|
uint32_t regDMABCOUNT;
|
|
uint32_t regDMATHRESH;
|
|
} am_hal_mspi_register_state_t;
|
|
|
|
//
|
|
// Command Queue control structure.
|
|
//
|
|
typedef struct
|
|
{
|
|
void *pCmdQHdl;
|
|
} am_hal_mspi_CQ_t;
|
|
|
|
typedef enum
|
|
{
|
|
AM_HAL_MSPI_SEQ_NONE,
|
|
AM_HAL_MSPI_SEQ_UNDER_CONSTRUCTION,
|
|
AM_HAL_MSPI_SEQ_RUNNING,
|
|
} am_hal_mspi_seq_e;
|
|
|
|
//
|
|
// MSPI State structure.
|
|
//
|
|
typedef struct
|
|
{
|
|
//
|
|
// Handle validation prefix.
|
|
//
|
|
am_hal_handle_prefix_t prefix;
|
|
|
|
//
|
|
// Physical module number.
|
|
//
|
|
uint32_t ui32Module;
|
|
|
|
//
|
|
// Selected flash device configuration.
|
|
//
|
|
am_hal_mspi_device_e eDeviceConfig;
|
|
|
|
//
|
|
// Clock frequency
|
|
//
|
|
am_hal_mspi_clock_e eClockFreq;
|
|
|
|
//
|
|
// Endianess of the FIFO interface.
|
|
//
|
|
bool bBigEndian;
|
|
|
|
//
|
|
// Delay timeout value.
|
|
//
|
|
uint32_t waitTimeout;
|
|
// DMA Transfer Control Buffer size in words.
|
|
uint32_t ui32TCBSize;
|
|
|
|
// DMA Transfer Control Buffer
|
|
uint32_t *pTCB;
|
|
|
|
uint32_t ui32LastIdxProcessed;
|
|
uint32_t ui32NumCQEntries;
|
|
uint32_t ui32TxnInt;
|
|
|
|
//
|
|
// Stores the CQ callbacks.
|
|
//
|
|
am_hal_mspi_callback_t pfnCallback[AM_HAL_MSPI_MAX_CQ_ENTRIES];
|
|
|
|
void *pCallbackCtxt[AM_HAL_MSPI_MAX_CQ_ENTRIES];
|
|
//
|
|
// Command Queue.
|
|
//
|
|
am_hal_mspi_CQ_t CQ;
|
|
// To support sequence
|
|
am_hal_mspi_seq_e eSeq;
|
|
bool bAutonomous;
|
|
uint32_t ui32NumTransactions;
|
|
volatile bool bRestart;
|
|
uint32_t block;
|
|
// To support high priority transactions - out of band
|
|
// High Priority DMA transactions
|
|
volatile bool bHP;
|
|
uint32_t ui32NumHPEntries;
|
|
uint32_t ui32NumHPPendingEntries;
|
|
uint32_t ui32MaxHPTransactions;
|
|
uint32_t ui32NextHPIdx;
|
|
uint32_t ui32LastHPIdxProcessed;
|
|
am_hal_mspi_dma_entry_t *pHPTransactions;
|
|
// Max pending transactions based on NB Buffer size
|
|
uint32_t ui32MaxPending;
|
|
// Number of back to back transactions with no callbacks
|
|
uint32_t ui32NumUnSolicited;
|
|
|
|
//
|
|
// MSPI Capabilities.
|
|
//
|
|
am_hal_mspi_capabilities_t capabilities;
|
|
|
|
// Power Save-Restore register state
|
|
am_hal_mspi_register_state_t registerState;
|
|
} am_hal_mspi_state_t;
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// Global Variables.
|
|
//
|
|
//*****************************************************************************
|
|
am_hal_mspi_state_t g_MSPIState[AM_REG_MSPI_NUM_MODULES];
|
|
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// Internal Functions.
|
|
//
|
|
//*****************************************************************************
|
|
static uint32_t
|
|
get_pause_val(am_hal_mspi_state_t *pMSPIState, uint32_t pause)
|
|
{
|
|
uint32_t retval;
|
|
switch (pMSPIState->block)
|
|
{
|
|
case 1:
|
|
// Pause the CQ till the whole block is built
|
|
retval = pause | AM_HAL_MSPI_CQP_PAUSE_DEFAULT | AM_HAL_MSPI_PAUSE_FLAG_BLOCK;
|
|
pMSPIState->block = 2;
|
|
break;
|
|
case 2:
|
|
// No pausing allowed
|
|
retval = AM_HAL_MSPI_PAUSE_DEFAULT;
|
|
break;
|
|
default: // case 0
|
|
retval = pause | AM_HAL_MSPI_CQP_PAUSE_DEFAULT;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
uint32_t
|
|
build_dma_cmdlist(am_hal_mspi_state_t *pMSPIState,
|
|
am_hal_mspi_trans_e eMode,
|
|
void *pCQEntry,
|
|
void *pTransaction)
|
|
{
|
|
uint32_t ui32Module = pMSPIState->ui32Module;
|
|
|
|
switch(eMode)
|
|
{
|
|
case AM_HAL_MSPI_TRANS_PIO:
|
|
{
|
|
am_hal_mspi_cq_pio_entry_t *pPIOEntry = (am_hal_mspi_cq_pio_entry_t*)pCQEntry;
|
|
am_hal_mspi_pio_transfer_t *pPIOTrans = (am_hal_mspi_pio_transfer_t*)pTransaction;
|
|
|
|
//
|
|
// Perform some sanity checks on the transaction.
|
|
//
|
|
if (pPIOTrans->ui32NumBytes > 65535)
|
|
{
|
|
return AM_HAL_STATUS_OUT_OF_RANGE;
|
|
}
|
|
|
|
//
|
|
// Command to set the CTRL register.
|
|
//
|
|
pPIOEntry->ui32ADDRAddr = (uint32_t)&MSPIn(ui32Module)->ADDR;
|
|
pPIOEntry->ui32ADDRVal = _VAL2FLD(MSPI0_ADDR_ADDR, pPIOTrans->ui32DeviceAddr);
|
|
pPIOEntry->ui32INSTRAddr = (uint32_t)&MSPIn(ui32Module)->INSTR;
|
|
pPIOEntry->ui32INSTRVal = _VAL2FLD(MSPI0_INSTR_INSTR, pPIOTrans->ui16DeviceInstr);
|
|
pPIOEntry->ui32CTRLAddr = (uint32_t)&MSPIn(ui32Module)->CTRL;
|
|
pPIOEntry->ui32CTRLVal =
|
|
_VAL2FLD(MSPI0_CTRL_XFERBYTES, pPIOTrans->ui32NumBytes) | // Set the number of bytes to transfer.
|
|
_VAL2FLD(MSPI0_CTRL_ENDCX, pPIOTrans->bDCX) | // Enable DCX if selected.
|
|
_VAL2FLD(MSPI0_CTRL_PIOSCRAMBLE, pPIOTrans->bScrambling) | // Set the scrambling if selected.
|
|
_VAL2FLD(MSPI0_CTRL_TXRX, pPIOTrans->eDirection) | // Set transmit or receive operation.
|
|
_VAL2FLD(MSPI0_CTRL_SENDI, pPIOTrans->bSendInstr) | // Enable sending the instruction.
|
|
_VAL2FLD(MSPI0_CTRL_SENDA, pPIOTrans->bSendAddr) | // Enable sending the address.
|
|
_VAL2FLD(MSPI0_CTRL_ENTURN, pPIOTrans->bTurnaround) | // Set the turn-around if needed.
|
|
_VAL2FLD(MSPI0_CTRL_BIGENDIAN, pMSPIState->bBigEndian) | // Set the FIFO endian format.
|
|
_VAL2FLD(MSPI0_CTRL_CONT, pPIOTrans->bContinue) | // Continuation transfer.
|
|
_VAL2FLD(MSPI0_CTRL_ENWLAT, pPIOTrans->bEnWRLatency) | // Enable write latency counter.
|
|
_VAL2FLD(MSPI0_CTRL_QUADCMD, pPIOTrans->bQuadCmd) | // Set the Quad Command if indicated.
|
|
_VAL2FLD(MSPI0_CTRL_START, 1); // Start the transfer.
|
|
|
|
}
|
|
break;
|
|
case AM_HAL_MSPI_TRANS_DMA:
|
|
{
|
|
am_hal_mspi_cq_dma_entry_t *pDMAEntry = (am_hal_mspi_cq_dma_entry_t *)pCQEntry;
|
|
am_hal_mspi_dma_transfer_t *pDMATrans = (am_hal_mspi_dma_transfer_t *)pTransaction;
|
|
|
|
//
|
|
// Perform some sanity checks on the transaction.
|
|
//
|
|
if (pDMATrans->ui32TransferCount > AM_HAL_MSPI_MAX_TRANS_SIZE)
|
|
{
|
|
return AM_HAL_STATUS_OUT_OF_RANGE;
|
|
}
|
|
if (pMSPIState->block && (pDMATrans->ui32PauseCondition != 0))
|
|
{
|
|
// Paused operations not allowed in block mode
|
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
|
}
|
|
|
|
//
|
|
// Command to set the DMACFG to disable DMA.
|
|
// Need to make sure we disable DMA before we can reprogram
|
|
//
|
|
pDMAEntry->ui32DMACFG2Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
|
|
pDMAEntry->ui32DMACFG2Val = _VAL2FLD(MSPI0_DMACFG_DMAEN, 0);
|
|
|
|
//
|
|
// Command to set the DMATARGADDR
|
|
//
|
|
pDMAEntry->ui32DMATARGADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMATARGADDR;
|
|
pDMAEntry->ui32DMATARGADDRVal = pDMATrans->ui32SRAMAddress;
|
|
|
|
//
|
|
// Command to set the DMADEVADDR
|
|
//
|
|
pDMAEntry->ui32DMADEVADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMADEVADDR;
|
|
pDMAEntry->ui32DMADEVADDRVal = pDMATrans->ui32DeviceAddress;
|
|
|
|
//
|
|
// Command to set the DMATOTALCOUNT
|
|
//
|
|
pDMAEntry->ui32DMATOTCOUNTAddr = (uint32_t)&MSPIn(ui32Module)->DMATOTCOUNT;
|
|
pDMAEntry->ui32DMATOTCOUNTVal = pDMATrans->ui32TransferCount;
|
|
|
|
//
|
|
// Command to set the DMACFG to start DMA.
|
|
//
|
|
pDMAEntry->ui32DMACFG1Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
|
|
pDMAEntry->ui32DMACFG1Val =
|
|
_VAL2FLD(MSPI0_DMACFG_DMAPWROFF, 0) | // DMA Auto Power-off not supported!
|
|
_VAL2FLD(MSPI0_DMACFG_DMAPRI, pDMATrans->ui8Priority) |
|
|
_VAL2FLD(MSPI0_DMACFG_DMADIR, pDMATrans->eDirection) |
|
|
_VAL2FLD(MSPI0_DMACFG_DMAEN, 3);
|
|
pDMAEntry->ui32DummyAddr1 = (uint32_t)&MSPIn(ui32Module)->CQSETCLEAR;
|
|
pDMAEntry->ui32DummyVal1 = 0;
|
|
pDMAEntry->ui32DummyAddr2 = (uint32_t)&MSPIn(ui32Module)->CQSETCLEAR;
|
|
pDMAEntry->ui32DummyVal2 = 0;
|
|
pDMAEntry->ui32PAUSENAddr = pDMAEntry->ui32PAUSEN2Addr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
|
|
pDMAEntry->ui32PAUSEENVal = get_pause_val(pMSPIState, pDMATrans->ui32PauseCondition);
|
|
pDMAEntry->ui32PAUSEEN2Val = AM_HAL_MSPI_PAUSE_DEFAULT;
|
|
pDMAEntry->ui32SETCLRVal = pDMATrans->ui32StatusSetClr;
|
|
pDMAEntry->ui32SETCLRAddr = (uint32_t)&MSPIn(ui32Module)->CQSETCLEAR;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief Writes data to the MSPI FIFO.
|
|
//!
|
|
//! @param ui32Module - Selects the MSPI module to use (zero or one).
|
|
//! @param pui32Data - Pointer to an array of the data to be written.
|
|
//! @param ui32NumBytes - Number of BYTES to copy into the FIFO.
|
|
//!
|
|
//! This function copies data from the array \e pui32Data into the MSPI FIFO.
|
|
//!
|
|
//! @return HAL status of the operation.
|
|
//
|
|
//*****************************************************************************
|
|
static uint32_t
|
|
mspi_fifo_write(uint32_t ui32Module, uint32_t *pui32Data,
|
|
uint32_t ui32NumBytes, uint32_t ui32Timeout)
|
|
{
|
|
uint32_t ui32Index;
|
|
uint32_t ui32Status = AM_HAL_STATUS_SUCCESS;
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if ( ui32Module >= AM_REG_MSPI_NUM_MODULES )
|
|
{
|
|
return AM_HAL_STATUS_OUT_OF_RANGE;
|
|
}
|
|
|
|
//
|
|
// Loop over the words in the array until we have the correct number of
|
|
// bytes.
|
|
//
|
|
for ( ui32Index = 0; (4 * ui32Index) < ui32NumBytes; ui32Index++ )
|
|
{
|
|
//
|
|
// Write the word to the FIFO.
|
|
//
|
|
MSPIn(ui32Module)->TXFIFO = pui32Data[ui32Index];
|
|
|
|
//
|
|
// Wait for the word to go out if there is no room in the FIFO.
|
|
//
|
|
ui32Status = am_hal_flash_delay_status_check(ui32Timeout,
|
|
(uint32_t)&MSPIn(ui32Module)->TXENTRIES,
|
|
MSPI0_TXENTRIES_TXENTRIES_Msk,
|
|
_VAL2FLD(MSPI0_TXENTRIES_TXENTRIES, AM_HAL_MSPI_MAX_FIFO_SIZE),
|
|
false);
|
|
}
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return ui32Status;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief Reads data from the MSPI FIFO.
|
|
//!
|
|
//! @param ui32Module - Selects the IOM module to use (zero or one).
|
|
//! @param pui32Data - Pointer to an array where the FIFO data will be copied.
|
|
//! @param ui32NumBytes - Number of bytes to copy into array.
|
|
//!
|
|
//! This function copies data from the MSPI FIFO into the array \e pui32Data.
|
|
//!
|
|
//! @return HAL status of the operation.
|
|
//
|
|
//*****************************************************************************
|
|
static uint32_t
|
|
mspi_fifo_read(uint32_t ui32Module, uint32_t *pui32Data,
|
|
uint32_t ui32NumBytes, uint32_t ui32Timeout)
|
|
{
|
|
am_hal_mspi_buffer(4) sTempBuffer;
|
|
uint32_t i, ui32NumWords, ui32Leftovers;
|
|
uint32_t ui32Status;
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if ( ui32Module >= AM_REG_MSPI_NUM_MODULES )
|
|
{
|
|
return AM_HAL_STATUS_OUT_OF_RANGE;
|
|
}
|
|
|
|
//
|
|
// Figure out how many whole words we're reading from the fifo, and how
|
|
// many bytes will be left over when we're done.
|
|
//
|
|
ui32NumWords = ui32NumBytes / 4;
|
|
ui32Leftovers = ui32NumBytes - (ui32NumWords * 4);
|
|
|
|
//
|
|
// Copy out as many full words as we can.
|
|
//
|
|
for ( i = 0; i < ui32NumWords; i++ )
|
|
{
|
|
//
|
|
// Wait for additinal entries in the MSPI RX FIFO.
|
|
//
|
|
ui32Status = am_hal_flash_delay_status_check(ui32Timeout,
|
|
(uint32_t)&MSPIn(ui32Module)->RXENTRIES,
|
|
MSPI0_RXENTRIES_RXENTRIES_Msk,
|
|
_VAL2FLD(MSPI0_RXENTRIES_RXENTRIES, 0),
|
|
false);
|
|
|
|
//
|
|
// Check for timeout
|
|
//
|
|
if (AM_HAL_STATUS_SUCCESS != ui32Status)
|
|
{
|
|
return ui32Status;
|
|
}
|
|
|
|
//
|
|
// Copy data out of the FIFO, one word at a time.
|
|
//
|
|
pui32Data[i] = MSPIn(ui32Module)->RXFIFO;
|
|
}
|
|
|
|
//
|
|
// If there were leftovers, we'll copy them carefully. Pull the last word
|
|
// from the fifo (there should only be one) into a temporary buffer. Also,
|
|
// create an 8-bit pointer to help us copy the remaining bytes one at a
|
|
// time.
|
|
//
|
|
// Note: If the data buffer we were given was truly a word pointer like the
|
|
// definition requests, we wouldn't need to do this. It's possible to call
|
|
// this function with a re-cast or packed pointer instead though. If that
|
|
// happens, we want to be careful not to overwrite any data that might be
|
|
// sitting just past the end of the destination array.
|
|
//
|
|
if ( ui32Leftovers )
|
|
{
|
|
//
|
|
// Wait for additinal entries in the MSPI RX FIFO.
|
|
//
|
|
ui32Status = am_hal_flash_delay_status_check(ui32Timeout,
|
|
(uint32_t)&MSPIn(ui32Module)->RXENTRIES,
|
|
MSPI0_RXENTRIES_RXENTRIES_Msk,
|
|
_VAL2FLD(MSPI0_RXENTRIES_RXENTRIES, 0),
|
|
false);
|
|
|
|
//
|
|
// Check for timeout
|
|
//
|
|
if (AM_HAL_STATUS_SUCCESS != ui32Status)
|
|
{
|
|
return ui32Status;
|
|
}
|
|
|
|
//
|
|
// Read the next word from the RX FIFO.
|
|
//
|
|
sTempBuffer.words[0] = MSPIn(ui32Module)->RXFIFO;
|
|
uint8_t *pui8Data;
|
|
pui8Data = (uint8_t *) (&pui32Data[i]);
|
|
|
|
//
|
|
// If we had leftover bytes, copy them out one byte at a time.
|
|
//
|
|
for ( int j = 0; j < ui32Leftovers; j++ )
|
|
{
|
|
pui8Data[j] = sTempBuffer.bytes[j];
|
|
}
|
|
}
|
|
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief Initializes the MSPI Command Queue.
|
|
//!
|
|
//! @param handle - handle for the interface.
|
|
//! @param ui32Length - length of the SRAM Command Queue buffer in words.
|
|
//! @param pTCB - pointer to the SRAM to use for the Command Queue.
|
|
//!
|
|
//! This function initializes the global command queue structure.
|
|
//!
|
|
//! @return HAL status of the operation.
|
|
//
|
|
//
|
|
//*****************************************************************************
|
|
static uint32_t
|
|
mspi_cq_init(uint32_t ui32Module, uint32_t ui32Length,
|
|
uint32_t *pTCB)
|
|
{
|
|
am_hal_cmdq_cfg_t cqCfg;
|
|
|
|
cqCfg.pCmdQBuf = pTCB;
|
|
cqCfg.cmdQSize = ui32Length / 2;
|
|
cqCfg.priority = AM_HAL_CMDQ_PRIO_HI;
|
|
return am_hal_cmdq_init((am_hal_cmdq_if_e)(AM_HAL_CMDQ_IF_MSPI0 + ui32Module),
|
|
&cqCfg, &g_MSPIState[ui32Module].CQ.pCmdQHdl);
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief Terminates the MSPI Command Queue.
|
|
//!
|
|
//! @param ui32Module - MSPI instance.
|
|
//!
|
|
//! This function resets the global command queue structure.
|
|
//!
|
|
//! @return HAL status of the operation.
|
|
//
|
|
//
|
|
//*****************************************************************************
|
|
static uint32_t
|
|
mspi_cq_term(void *pHandle)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Module = pMSPIState->ui32Module;
|
|
|
|
if (g_MSPIState[ui32Module].CQ.pCmdQHdl)
|
|
{
|
|
am_hal_cmdq_term(g_MSPIState[ui32Module].CQ.pCmdQHdl, true);
|
|
g_MSPIState[ui32Module].CQ.pCmdQHdl = NULL;
|
|
}
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief Adds a transaction the MSPI Command Queue.
|
|
//!
|
|
//! @param handle - handle for the interface.
|
|
//! @param pTransaction - transaction to add to the CQ
|
|
//! @param pfnCallback - pointer the callback function to be executed when
|
|
//! transaction is complete.
|
|
//! @param pCallbackCtxt- pointer to the state/context to pass to callback
|
|
//! function.
|
|
//!
|
|
//! This function copies data from the IOM FIFO into the array \e pui32Data.
|
|
//! This is how input data from SPI or I2C transactions may be retrieved.
|
|
//!
|
|
//!
|
|
//! @return HAL status of the operation.
|
|
//
|
|
//
|
|
//*****************************************************************************
|
|
static uint32_t
|
|
mspi_cq_add_transaction(void *pHandle,
|
|
void *pTransaction,
|
|
am_hal_mspi_trans_e eMode,
|
|
am_hal_mspi_callback_t pfnCallback,
|
|
void *pCallbackCtxt)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
am_hal_cmdq_entry_t *pCQBlock;
|
|
uint32_t index;
|
|
uint32_t size = 1;
|
|
am_hal_mspi_CQ_t *pCQ = &pMSPIState->CQ;
|
|
uint32_t ui32Status = AM_HAL_STATUS_SUCCESS;
|
|
|
|
//
|
|
// Determine the transfer mode and set up accordingly.
|
|
//
|
|
switch(eMode)
|
|
{
|
|
case AM_HAL_MSPI_TRANS_PIO:
|
|
size = sizeof(am_hal_mspi_cq_pio_entry_t);
|
|
break;
|
|
case AM_HAL_MSPI_TRANS_DMA:
|
|
size = sizeof(am_hal_mspi_cq_dma_entry_t);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check to see if there is enough room in the CQ
|
|
//
|
|
if (pMSPIState->ui32NumCQEntries == AM_HAL_MSPI_MAX_CQ_ENTRIES)
|
|
{
|
|
return AM_HAL_STATUS_OUT_OF_RANGE;
|
|
}
|
|
ui32Status = am_hal_cmdq_alloc_block(pCQ->pCmdQHdl, size / 8, &pCQBlock, &index);
|
|
if (ui32Status != AM_HAL_STATUS_SUCCESS)
|
|
{
|
|
return ui32Status;
|
|
}
|
|
|
|
ui32Status = build_dma_cmdlist(pMSPIState, eMode, pCQBlock, pTransaction);
|
|
if (ui32Status != AM_HAL_STATUS_SUCCESS)
|
|
{
|
|
am_hal_cmdq_release_block(pCQ->pCmdQHdl);
|
|
return ui32Status;
|
|
}
|
|
|
|
//
|
|
// Because we set AM_HAL_IOM_CQUPD_INT_FLAG, an interrupt will occur once
|
|
// we reach this point in the Command Queue. In the service routine, we'll
|
|
// look for the appropriate callback.
|
|
//
|
|
// If ENDIDX has been reached, the CQ will pause here. Otherwise will
|
|
// continue with the next CQ entry.
|
|
//
|
|
|
|
//
|
|
// Store the callback function pointer.
|
|
//
|
|
pMSPIState->pfnCallback[index & (AM_HAL_MSPI_MAX_CQ_ENTRIES - 1)] = pfnCallback;
|
|
pMSPIState->pCallbackCtxt[index & (AM_HAL_MSPI_MAX_CQ_ENTRIES - 1)] = pCallbackCtxt;
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return ui32Status;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief Enable the Command Queue operation.
|
|
//!
|
|
//! @param handle - handle for the interface.
|
|
//!
|
|
//! This function enables Command Queue operation.
|
|
//!
|
|
//!
|
|
//! @return HAL status of the operation.
|
|
//
|
|
//
|
|
//*****************************************************************************
|
|
static uint32_t
|
|
mspi_cq_enable(void *pHandle)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
|
|
//
|
|
// Enable the Command Queue operation
|
|
//
|
|
return am_hal_cmdq_enable(pMSPIState->CQ.pCmdQHdl);
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief Disable the Command Queue operation.
|
|
//!
|
|
//! @param handle - handle for the interface.
|
|
//!
|
|
//! This function disables the Command Queue operation.
|
|
//!
|
|
//!
|
|
//! @return HAL status of the operation.
|
|
//
|
|
//
|
|
//*****************************************************************************
|
|
static uint32_t
|
|
mspi_cq_disable(void *pHandle)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
|
|
//
|
|
// Disable the Command Queue operation
|
|
//
|
|
return am_hal_cmdq_disable(pMSPIState->CQ.pCmdQHdl);
|
|
}
|
|
|
|
static uint32_t
|
|
mspi_cq_pause(am_hal_mspi_state_t *pMSPIState)
|
|
{
|
|
uint32_t status = AM_HAL_STATUS_SUCCESS;
|
|
uint32_t ui32usMaxDelay = AM_HAL_MSPI_MAX_PAUSE_DELAY;
|
|
|
|
MSPIn(pMSPIState->ui32Module)->CQSETCLEAR = AM_HAL_MSPI_SC_PAUSE_CQ;
|
|
// It is possible that CQ is disabled once the last transaction is processed
|
|
while ( MSPIn(pMSPIState->ui32Module)->CQCFG_b.CQEN )
|
|
{
|
|
// Need to make sure we're paused at a designated pause point
|
|
if ( MSPIn(pMSPIState->ui32Module)->CQSTAT_b.CQPAUSED && (MSPIn(pMSPIState->ui32Module)->CQPAUSE & AM_HAL_MSPI_PAUSE_FLAG_CQ))
|
|
{
|
|
break;
|
|
}
|
|
if ( ui32usMaxDelay-- )
|
|
{
|
|
//
|
|
// Call the BOOTROM cycle function to delay for about 1 microsecond.
|
|
//
|
|
am_hal_flash_delay( FLASH_CYCLES_US(1) );
|
|
}
|
|
else
|
|
{
|
|
return AM_HAL_STATUS_TIMEOUT;
|
|
}
|
|
}
|
|
if (status == AM_HAL_STATUS_SUCCESS)
|
|
{
|
|
// Now that CQ is guaranteed to not progress further - we need to still wait in case the current CQ entry
|
|
// resulted in a DMA state....need to make sure we finish the current DMA
|
|
status = am_hal_flash_delay_status_check(AM_HAL_MSPI_MAX_PAUSE_DELAY,
|
|
(uint32_t)&MSPIn(pMSPIState->ui32Module)->DMASTAT,
|
|
MSPI0_DMASTAT_DMATIP_Msk,
|
|
_VAL2FLD(MSPI0_DMASTAT_DMATIP, 0),
|
|
true);
|
|
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
program_dma(void *pHandle)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Module = pMSPIState->ui32Module;
|
|
uint32_t index = (pMSPIState->ui32LastHPIdxProcessed + 1) % pMSPIState->ui32MaxHPTransactions;
|
|
am_hal_mspi_dma_entry_t *pDMAEntry = &pMSPIState->pHPTransactions[index];
|
|
|
|
// Need to make sure we disable DMA before we can reprogram
|
|
MSPIn(ui32Module)->DMACFG = _VAL2FLD(MSPI0_DMACFG_DMAEN, 0);
|
|
//
|
|
// set the DMATARGADDR
|
|
//
|
|
MSPIn(ui32Module)->DMATARGADDR = pDMAEntry->ui32DMATARGADDRVal;
|
|
|
|
//
|
|
// set the DMADEVADDR
|
|
//
|
|
MSPIn(ui32Module)->DMADEVADDR = pDMAEntry->ui32DMADEVADDRVal;
|
|
|
|
//
|
|
// set the DMATOTALCOUNT
|
|
//
|
|
MSPIn(ui32Module)->DMATOTCOUNT = pDMAEntry->ui32DMATOTCOUNTVal;
|
|
|
|
//
|
|
// set the DMACFG to start DMA.
|
|
//
|
|
MSPIn(ui32Module)->DMACFG = pDMAEntry->ui32DMACFG1Val;
|
|
}
|
|
|
|
|
|
static uint32_t
|
|
sched_hiprio(am_hal_mspi_state_t *pMSPIState, uint32_t numTrans)
|
|
{
|
|
uint32_t ui32NumPend;
|
|
uint32_t ui32Status = AM_HAL_STATUS_SUCCESS;
|
|
//
|
|
// Start a critical section.
|
|
//
|
|
AM_CRITICAL_BEGIN
|
|
|
|
ui32NumPend = pMSPIState->ui32NumHPEntries;
|
|
pMSPIState->ui32NumHPEntries += numTrans;
|
|
|
|
//
|
|
// End the critical section.
|
|
//
|
|
AM_CRITICAL_END
|
|
|
|
|
|
if (0 == ui32NumPend)
|
|
{
|
|
// Force CQ to Pause
|
|
ui32Status = mspi_cq_pause(pMSPIState);
|
|
if (ui32Status != AM_HAL_STATUS_SUCCESS)
|
|
{
|
|
return ui32Status;
|
|
}
|
|
pMSPIState->ui32TxnInt = 0;
|
|
// Clear DMACMP interrupt
|
|
MSPIn(pMSPIState->ui32Module)->INTCLR = AM_HAL_MSPI_INT_DMACMP;
|
|
// Enable DMACMP interrupt
|
|
MSPIn(pMSPIState->ui32Module)->INTEN |= AM_HAL_MSPI_INT_DMACMP;
|
|
pMSPIState->bHP = true;
|
|
//
|
|
// Program the DMA
|
|
//
|
|
program_dma(pMSPIState);
|
|
}
|
|
return ui32Status;
|
|
}
|
|
|
|
static uint32_t
|
|
mspi_add_hp_transaction(void *pHandle,
|
|
am_hal_mspi_dma_transfer_t *pDMATrans,
|
|
am_hal_mspi_callback_t pfnCallback,
|
|
void *pCallbackCtxt)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
am_hal_mspi_dma_entry_t *pDMAEntry;
|
|
|
|
uint32_t index = pMSPIState->ui32NextHPIdx % pMSPIState->ui32MaxHPTransactions;
|
|
|
|
//
|
|
// Check to see if there is enough room in the queue
|
|
//
|
|
if ( pMSPIState->ui32NumHPEntries == pMSPIState->ui32MaxHPTransactions )
|
|
{
|
|
return AM_HAL_STATUS_OUT_OF_RANGE;
|
|
}
|
|
|
|
pDMAEntry = &pMSPIState->pHPTransactions[index];
|
|
pDMAEntry->ui32DMATARGADDRVal = pDMATrans->ui32SRAMAddress;
|
|
pDMAEntry->ui32DMADEVADDRVal = pDMATrans->ui32DeviceAddress;
|
|
pDMAEntry->ui32DMATOTCOUNTVal = pDMATrans->ui32TransferCount;
|
|
pDMAEntry->ui32DMACFG1Val =
|
|
_VAL2FLD(MSPI0_DMACFG_DMAPWROFF, 0) | // DMA Auto Power-off not supported!
|
|
_VAL2FLD(MSPI0_DMACFG_DMAPRI, pDMATrans->ui8Priority) |
|
|
_VAL2FLD(MSPI0_DMACFG_DMADIR, pDMATrans->eDirection) |
|
|
_VAL2FLD(MSPI0_DMACFG_DMAEN, 3);
|
|
pDMAEntry->pfnCallback = pfnCallback;
|
|
pDMAEntry->pCallbackCtxt = pCallbackCtxt;
|
|
|
|
pMSPIState->ui32NextHPIdx++;
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
} // am_hal_mspi_DmaAddTransaction()
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief Determine the virtual device configuration
|
|
//!
|
|
//! @param handle - handle for the interface.
|
|
//! @param eMSPIDevice - external device configuration for MSPI
|
|
//!
|
|
//! @return virtual device value.
|
|
//
|
|
//
|
|
//*****************************************************************************
|
|
//
|
|
// MSPI interface mode and chip enable selection.
|
|
// This is an internal extension to am_hal_mspi_device_e
|
|
//
|
|
static uint32_t
|
|
mspi_virtual_device(mspi_device_info_t *pMSPIDeviceInfo, uint32_t *pVirtDevice)
|
|
{
|
|
//
|
|
// Check that the Device Config is in the proper range.
|
|
//
|
|
if (pMSPIDeviceInfo->eDeviceConfig > AM_HAL_MSPI_FLASH_MAX)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
|
|
switch(pMSPIDeviceInfo->eXipMixedMode)
|
|
{
|
|
case AM_HAL_MSPI_XIPMIXED_NORMAL:
|
|
{
|
|
// if Serial CE0 or CE1, check for separate I/O.
|
|
if ( (AM_HAL_MSPI_FLASH_SERIAL_CE0 == pMSPIDeviceInfo->eDeviceConfig) ||
|
|
(AM_HAL_MSPI_FLASH_SERIAL_CE1 == pMSPIDeviceInfo->eDeviceConfig) )
|
|
{
|
|
// if serial mode, but not separate I/O , then calculate 3WIRE mode value.
|
|
if ( !pMSPIDeviceInfo->bSeparateIO )
|
|
{
|
|
*pVirtDevice = (uint32_t)pMSPIDeviceInfo->eDeviceConfig +
|
|
AM_HAL_MSPI_FLASH_SERIAL_CE0_3WIRE -
|
|
AM_HAL_MSPI_FLASH_SERIAL_CE0;
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise return the original eDeviceConfig.
|
|
*pVirtDevice = pMSPIDeviceInfo->eDeviceConfig;
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Otherwise return the original eDeviceConfig.
|
|
*pVirtDevice = pMSPIDeviceInfo->eDeviceConfig;
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AM_HAL_MSPI_XIPMIXED_D2:
|
|
if ( (AM_HAL_MSPI_FLASH_SERIAL_CE0 == pMSPIDeviceInfo->eDeviceConfig) ||
|
|
(AM_HAL_MSPI_FLASH_SERIAL_CE1 == pMSPIDeviceInfo->eDeviceConfig) )
|
|
{
|
|
*pVirtDevice = (uint32_t)pMSPIDeviceInfo->eDeviceConfig +
|
|
AM_HAL_MSPI_FLASH_DUAL_CE0_1_1_2 -
|
|
AM_HAL_MSPI_FLASH_SERIAL_CE0;
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
break;
|
|
|
|
case AM_HAL_MSPI_XIPMIXED_AD2:
|
|
if ( (AM_HAL_MSPI_FLASH_SERIAL_CE0 == pMSPIDeviceInfo->eDeviceConfig) ||
|
|
(AM_HAL_MSPI_FLASH_SERIAL_CE1 == pMSPIDeviceInfo->eDeviceConfig) )
|
|
{
|
|
*pVirtDevice = (uint32_t)pMSPIDeviceInfo->eDeviceConfig +
|
|
AM_HAL_MSPI_FLASH_DUAL_CE0_1_2_2 -
|
|
AM_HAL_MSPI_FLASH_SERIAL_CE0;
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
break;
|
|
|
|
case AM_HAL_MSPI_XIPMIXED_D4:
|
|
if ( (AM_HAL_MSPI_FLASH_SERIAL_CE0 == pMSPIDeviceInfo->eDeviceConfig) ||
|
|
(AM_HAL_MSPI_FLASH_SERIAL_CE1 == pMSPIDeviceInfo->eDeviceConfig) )
|
|
{
|
|
*pVirtDevice = (uint32_t)pMSPIDeviceInfo->eDeviceConfig +
|
|
AM_HAL_MSPI_FLASH_QUAD_CE0_1_1_4 -
|
|
AM_HAL_MSPI_FLASH_SERIAL_CE0;
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
break;
|
|
|
|
case AM_HAL_MSPI_XIPMIXED_AD4:
|
|
if ( (AM_HAL_MSPI_FLASH_SERIAL_CE0 == pMSPIDeviceInfo->eDeviceConfig) ||
|
|
(AM_HAL_MSPI_FLASH_SERIAL_CE1 == pMSPIDeviceInfo->eDeviceConfig) )
|
|
{
|
|
*pVirtDevice = (uint32_t)pMSPIDeviceInfo->eDeviceConfig +
|
|
AM_HAL_MSPI_FLASH_QUAD_CE0_1_4_4 -
|
|
AM_HAL_MSPI_FLASH_SERIAL_CE0;
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! @brief Configure the device config, seperate I/O, mixed mode, and internal
|
|
//! PADs based on the virtual device configuration passed in.
|
|
//!
|
|
//! @param handle - handle for the interface.
|
|
//! @param eMSPIDevice - external device configuration for MSPI
|
|
//!
|
|
//! @return HAL status of the operation.
|
|
//
|
|
//
|
|
//*****************************************************************************
|
|
static uint32_t
|
|
mspi_device_configure(void *pHandle, uint32_t ui32MSPIDevice)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Module = pMSPIState->ui32Module;
|
|
|
|
switch ( ui32MSPIDevice )
|
|
{
|
|
case AM_HAL_MSPI_FLASH_SERIAL_CE0:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_SERIAL0;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 1;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 0;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x103;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_SERIAL_CE1:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_SERIAL1;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 1;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 0;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x130;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_DUAL_CE0:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_DUAL0;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 0;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x103;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_DUAL_CE1:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_DUAL1;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 0;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x130;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_QUAD_CE0:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_QUAD0;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 0;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x10F;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_QUAD_CE1:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_QUAD1;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 0;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x1F0;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_OCTAL_CE0:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_OCTAL0;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 0;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x1FF;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_OCTAL_CE1:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_OCTAL1;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 0;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x1FF;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_QUADPAIRED:
|
|
case AM_HAL_MSPI_FLASH_QUADPAIRED_SERIAL:
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_DUAL_CE0_1_1_2:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_SERIAL0;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 1;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x103;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_DUAL_CE1_1_1_2:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_SERIAL1;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 1;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x130;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_DUAL_CE0_1_2_2:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_SERIAL0;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 3;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x103;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_DUAL_CE1_1_2_2:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_SERIAL1;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 3;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x130;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_QUAD_CE0_1_1_4:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_SERIAL0;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 5;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x10F;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_QUAD_CE1_1_1_4:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_SERIAL1;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 5;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x1F0;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_QUAD_CE0_1_4_4:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_SERIAL0;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 7;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x10F;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_QUAD_CE1_1_4_4:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_SERIAL1;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 7;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
MSPIn(ui32Module)->PADOUTEN = 0x1F0;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_SERIAL_CE0_3WIRE:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_SERIAL0;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 0;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
// Enable both D0 and D1 - as D1 might be getting used for DCX
|
|
MSPIn(ui32Module)->PADOUTEN = 0x103;
|
|
break;
|
|
case AM_HAL_MSPI_FLASH_SERIAL_CE1_3WIRE:
|
|
MSPIn(ui32Module)->CFG_b.DEVCFG = MSPI0_CFG_DEVCFG_SERIAL1;
|
|
MSPIn(ui32Module)->CFG_b.SEPIO = 0;
|
|
MSPIn(ui32Module)->FLASH_b.XIPMIXED = 0;
|
|
MSPIn(ui32Module)->PADCFG = 0;
|
|
// Enable both D0 and D1 - as D1 might be getting used for DCX
|
|
MSPIn(ui32Module)->PADOUTEN = 0x130;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void mspi_dummy_callback(void *pCallbackCtxt, uint32_t status)
|
|
{
|
|
// Dummy - Do nothing
|
|
}
|
|
|
|
static void mspi_seq_loopback(void *pCallbackCtxt, uint32_t status)
|
|
{
|
|
// Reset the state to allow serving callbacks for next set
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pCallbackCtxt;
|
|
pMSPIState->ui32NumCQEntries = pMSPIState->ui32NumTransactions + 1;
|
|
pMSPIState->ui32LastIdxProcessed = 0;
|
|
pMSPIState->bRestart = true;
|
|
// Now resume the CQ - to finish loopback
|
|
// Resume the CQ
|
|
MSPIn(pMSPIState->ui32Module)->CQSETCLEAR = AM_HAL_MSPI_SC_UNPAUSE_SEQLOOP;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// External Functions.
|
|
//
|
|
//*****************************************************************************
|
|
|
|
//
|
|
// MSPI initialization function
|
|
//
|
|
uint32_t
|
|
am_hal_mspi_initialize(uint32_t ui32Module, void **ppHandle)
|
|
{
|
|
// Compile time check to ensure ENTRY_SIZE macros are defined correctly
|
|
// incorrect definition will cause divide by 0 error at build time
|
|
am_ct_assert((sizeof(am_hal_mspi_cq_dma_entry_t) + 8) == AM_HAL_MSPI_CQ_ENTRY_SIZE);
|
|
am_ct_assert(sizeof(am_hal_mspi_dma_entry_t) == AM_HAL_MSPI_HIPRIO_ENTRY_SIZE);
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check that the request module is in range.
|
|
//
|
|
if (ui32Module >= AM_REG_MSPI_NUM_MODULES )
|
|
{
|
|
return AM_HAL_STATUS_OUT_OF_RANGE;
|
|
}
|
|
|
|
//
|
|
// Check for valid arguements.
|
|
//
|
|
if (!ppHandle)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
|
|
//
|
|
// Check if the handle is unallocated.
|
|
//
|
|
if (g_MSPIState[ui32Module].prefix.s.bInit)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
//
|
|
// Initialize the handle.
|
|
//
|
|
g_MSPIState[ui32Module].prefix.s.bInit = true;
|
|
g_MSPIState[ui32Module].prefix.s.magic = AM_HAL_MAGIC_MSPI;
|
|
g_MSPIState[ui32Module].ui32Module = ui32Module;
|
|
|
|
//
|
|
// Return the handle.
|
|
//
|
|
*ppHandle = (void *)&g_MSPIState[ui32Module];
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// MSPI Disable function
|
|
//
|
|
uint32_t
|
|
am_hal_mspi_disable(void *pHandle)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Status;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!AM_HAL_MSPI_CHK_HANDLE(pHandle))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
if (!pMSPIState->prefix.s.bEnable)
|
|
{
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (pMSPIState->pTCB)
|
|
{
|
|
//
|
|
// Disable the Command Queue.
|
|
//
|
|
ui32Status = mspi_cq_disable(pHandle);
|
|
if (AM_HAL_STATUS_SUCCESS != ui32Status)
|
|
{
|
|
return ui32Status;
|
|
}
|
|
|
|
//
|
|
// Reset the Command Queue.
|
|
//
|
|
mspi_cq_term(pHandle);
|
|
}
|
|
|
|
pMSPIState->prefix.s.bEnable = false;
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// MSPI Deinitialize function
|
|
//
|
|
uint32_t
|
|
am_hal_mspi_deinitialize(void *pHandle)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!AM_HAL_MSPI_CHK_HANDLE(pHandle))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
if (pMSPIState->prefix.s.bEnable)
|
|
{
|
|
am_hal_mspi_disable(pHandle);
|
|
}
|
|
|
|
//
|
|
// Reset the handle.
|
|
//
|
|
pMSPIState->prefix.s.bInit = false;
|
|
pMSPIState->ui32Module = 0;
|
|
pMSPIState->eDeviceConfig = (am_hal_mspi_device_e)0;
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// MSPI device configuration function
|
|
//
|
|
uint32_t
|
|
am_hal_mspi_device_configure(void *pHandle,
|
|
am_hal_mspi_dev_config_t *pConfig)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Module;
|
|
uint32_t ui32Config = 0;
|
|
|
|
ui32Module = pMSPIState->ui32Module;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!AM_HAL_MSPI_CHK_HANDLE(pHandle))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
//
|
|
// Configure not allowed in Enabled state
|
|
//
|
|
if (pMSPIState->prefix.s.bEnable)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
|
}
|
|
|
|
//
|
|
// Check if Device Configuration is allowed. Only Module #1 supports Octal/Paired.
|
|
//
|
|
if ((pConfig->eDeviceConfig > AM_HAL_MSPI_FLASH_QUAD_CE1) &&
|
|
(ui32Module != AM_HAL_MSPI_OCTAL_MODULE))
|
|
{
|
|
return AM_HAL_STATUS_OUT_OF_RANGE;
|
|
}
|
|
//
|
|
// Check for DMA to/from DTCM.
|
|
//
|
|
if ( ((uint32_t)pConfig->pTCB >= AM_HAL_FLASH_DTCM_START) &&
|
|
((uint32_t)pConfig->pTCB <= AM_HAL_FLASH_DTCM_END) )
|
|
{
|
|
return AM_HAL_STATUS_OUT_OF_RANGE;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
ui32Module = pMSPIState->ui32Module;
|
|
//
|
|
// Set the external flash device configuration.
|
|
//
|
|
if ( pConfig->eXipMixedMode == AM_HAL_MSPI_XIPMIXED_NORMAL )
|
|
{
|
|
ui32Config = _VAL2FLD(MSPI0_CFG_DEVCFG, pConfig->eDeviceConfig);
|
|
}
|
|
else if ((pConfig->eDeviceConfig == AM_HAL_MSPI_FLASH_DUAL_CE0 ||
|
|
(pConfig->eDeviceConfig == AM_HAL_MSPI_FLASH_QUAD_CE0)) )
|
|
{
|
|
ui32Config = _VAL2FLD(MSPI0_CFG_DEVCFG, AM_HAL_MSPI_FLASH_SERIAL_CE0);
|
|
}
|
|
else if ((pConfig->eDeviceConfig == AM_HAL_MSPI_FLASH_DUAL_CE1 ||
|
|
(pConfig->eDeviceConfig == AM_HAL_MSPI_FLASH_QUAD_CE1)) )
|
|
{
|
|
ui32Config = _VAL2FLD(MSPI0_CFG_DEVCFG, AM_HAL_MSPI_FLASH_SERIAL_CE1);
|
|
}
|
|
else
|
|
{
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
|
|
//
|
|
// If separate MOSI/MISO, then configure.
|
|
//
|
|
if ( pConfig->bSeparateIO )
|
|
{
|
|
ui32Config |= _VAL2FLD(MSPI0_CFG_SEPIO, 1);
|
|
}
|
|
|
|
//
|
|
// Set the clock polarity and phase based on SPI mode.
|
|
//
|
|
switch(pConfig->eSpiMode)
|
|
{
|
|
case AM_HAL_MSPI_SPI_MODE_0: // CPOL = 0; CPHA = 0
|
|
ui32Config |= _VAL2FLD(MSPI0_CFG_CPOL, 0) |
|
|
_VAL2FLD(MSPI0_CFG_CPHA, 0);
|
|
break;
|
|
case AM_HAL_MSPI_SPI_MODE_2: // CPOL = 1; CPHA = 0
|
|
ui32Config |= _VAL2FLD(MSPI0_CFG_CPOL, 1) |
|
|
_VAL2FLD(MSPI0_CFG_CPHA, 0);
|
|
break;
|
|
case AM_HAL_MSPI_SPI_MODE_1: // CPOL = 0; CPHA = 1
|
|
ui32Config |= _VAL2FLD(MSPI0_CFG_CPOL, 0) |
|
|
_VAL2FLD(MSPI0_CFG_CPHA, 1);
|
|
break;
|
|
case AM_HAL_MSPI_SPI_MODE_3: // CPOL = 1; CPHA = 1
|
|
ui32Config |= _VAL2FLD(MSPI0_CFG_CPOL, 1) |
|
|
_VAL2FLD(MSPI0_CFG_CPHA, 1);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set the number of turn-around cycles.
|
|
//
|
|
ui32Config |= _VAL2FLD(MSPI0_CFG_TURNAROUND, pConfig->ui8TurnAround);
|
|
|
|
//
|
|
// Set the address configuration.
|
|
//
|
|
ui32Config |= _VAL2FLD(MSPI0_CFG_ASIZE, pConfig->eAddrCfg);
|
|
|
|
//
|
|
// Set the instruction configuration.
|
|
//
|
|
ui32Config |= _VAL2FLD(MSPI0_CFG_ISIZE, pConfig->eInstrCfg);
|
|
|
|
//
|
|
// Set the write latency value.
|
|
//
|
|
ui32Config |= _VAL2FLD(MSPI0_CFG_WRITELATENCY, pConfig->ui8WriteLatency);
|
|
|
|
//
|
|
// Set the configuration in the MSPI peripheral.
|
|
//
|
|
MSPIn(ui32Module)->CFG = ui32Config;
|
|
|
|
//
|
|
// Set the clock divisor to get the desired MSPI clock frequency.
|
|
//
|
|
MSPIn(ui32Module)->MSPICFG_b.CLKDIV = pConfig->eClockFreq;
|
|
|
|
//
|
|
// Adjust the clock edge configuration depending upon the clock frequency.
|
|
//
|
|
if ( pConfig->eClockFreq == AM_HAL_MSPI_CLK_48MHZ )
|
|
{
|
|
MSPIn(ui32Module)->MSPICFG_b.TXNEG = 1;
|
|
MSPIn(ui32Module)->MSPICFG_b.RXNEG = 0;
|
|
MSPIn(ui32Module)->MSPICFG_b.RXCAP = 1;
|
|
MSPIn(ui32Module)->MSPIDDR = 0;
|
|
}
|
|
else
|
|
{
|
|
MSPIn(ui32Module)->MSPICFG_b.TXNEG = 0;
|
|
MSPIn(ui32Module)->MSPICFG_b.RXNEG = 0;
|
|
MSPIn(ui32Module)->MSPICFG_b.RXCAP = 1;
|
|
MSPIn(ui32Module)->MSPIDDR = 0;
|
|
}
|
|
|
|
//
|
|
// Set the APBCLK for continuous operation.
|
|
//
|
|
MSPIn(ui32Module)->MSPICFG_b.APBCLK = 0;
|
|
|
|
//
|
|
// Reset the register storage for next write.
|
|
//
|
|
ui32Config = 0;
|
|
|
|
//
|
|
// Set whether to send an instruction.
|
|
//
|
|
if ( pConfig->bSendInstr )
|
|
{
|
|
ui32Config |= _VAL2FLD(MSPI0_FLASH_XIPSENDI, 1);
|
|
}
|
|
|
|
//
|
|
// Set whether to send an address.
|
|
//
|
|
if ( pConfig->bSendAddr )
|
|
{
|
|
ui32Config |= _VAL2FLD(MSPI0_FLASH_XIPSENDA, 1);
|
|
}
|
|
|
|
//
|
|
// Set whether to enable the TX to RX turnaround.
|
|
//
|
|
if ( pConfig->bTurnaround )
|
|
{
|
|
ui32Config |= _VAL2FLD(MSPI0_FLASH_XIPENTURN, 1);
|
|
}
|
|
|
|
//
|
|
// Set to Little Endian mode by default.
|
|
//
|
|
ui32Config |= _VAL2FLD(MSPI0_FLASH_XIPBIGENDIAN, pMSPIState->bBigEndian);
|
|
|
|
//
|
|
// Set the XIP ACK value to default to 1's during latency period.
|
|
//
|
|
ui32Config |= _VAL2FLD(MSPI0_FLASH_XIPACK, MSPI0_FLASH_XIPACK_TERMINATE);
|
|
|
|
|
|
//
|
|
// Set the XIPENWLAT mode.
|
|
//
|
|
ui32Config |= _VAL2FLD(MSPI0_FLASH_XIPENWLAT, pConfig->bEnWriteLatency);
|
|
|
|
//
|
|
// Set the configuration in the MSPI peripheral.
|
|
//
|
|
MSPIn(ui32Module)->FLASH = ui32Config;
|
|
|
|
//
|
|
// Reset the register storage for next write.
|
|
//
|
|
ui32Config = 0;
|
|
|
|
//
|
|
// Set the read instruction.
|
|
//
|
|
ui32Config |= _VAL2FLD(MSPI0_XIPINSTR_READINSTR, pConfig->ui8ReadInstr);
|
|
|
|
//
|
|
// Set the write instruction.
|
|
//
|
|
ui32Config |= _VAL2FLD(MSPI0_XIPINSTR_WRITEINSTR, pConfig->ui8WriteInstr);
|
|
|
|
//
|
|
// Set the XIPMIXED mode
|
|
//
|
|
ui32Config |= _VAL2FLD(MSPI0_FLASH_XIPMIXED, pConfig->eXipMixedMode);
|
|
|
|
//
|
|
// Set the configuration in the MSPI peripheral.
|
|
//
|
|
MSPIn(ui32Module)->XIPINSTR = ui32Config;
|
|
|
|
//
|
|
// Reset the register storage for next write.
|
|
//
|
|
ui32Config = 0;
|
|
|
|
//
|
|
// Set up the DMA Boundary configuration.
|
|
//
|
|
ui32Config |= _VAL2FLD(MSPI0_DMABOUNDARY_DMABOUND, pConfig->eDMABoundary);
|
|
|
|
//
|
|
// Set the DMA time limit.
|
|
//
|
|
ui32Config |= _VAL2FLD(MSPI0_DMABOUNDARY_DMATIMELIMIT, pConfig->ui16DMATimeLimit);
|
|
|
|
//
|
|
// Set the DMA
|
|
//
|
|
// Set the configuration in the MSPI peripheral.
|
|
//
|
|
MSPIn(ui32Module)->DMABOUNDARY = ui32Config;
|
|
|
|
g_MSPIState[ui32Module].pTCB = pConfig->pTCB;
|
|
g_MSPIState[ui32Module].ui32TCBSize = pConfig->ui32TCBSize;
|
|
|
|
if (pConfig->pTCB)
|
|
{
|
|
// set the DMABCOUNT
|
|
MSPIn(ui32Module)->DMABCOUNT = AM_HAL_MSPI_DEFAULT_BURST_COUNT;
|
|
|
|
if ( pConfig->eClockFreq == AM_HAL_MSPI_CLK_48MHZ )
|
|
{
|
|
|
|
// set the DMATHRESHs asymmetrically for 48MHz.
|
|
MSPIn(ui32Module)->DMATHRESH_b.DMATXTHRESH = AM_HAL_MSPI_MAX_FIFO_SIZE - AM_HAL_MSPI_HIGH_SPEED_OFFSET;
|
|
MSPIn(ui32Module)->DMATHRESH_b.DMARXTHRESH = AM_HAL_MSPI_HIGH_SPEED_OFFSET;
|
|
}
|
|
else
|
|
{
|
|
// set the DMATHRESHs symmetrically for all other clock speeds.
|
|
MSPIn(ui32Module)->DMATHRESH_b.DMATXTHRESH = (AM_HAL_MSPI_DEFAULT_BURST_COUNT >> 2);
|
|
MSPIn(ui32Module)->DMATHRESH_b.DMARXTHRESH = (AM_HAL_MSPI_DEFAULT_BURST_COUNT >> 2);
|
|
}
|
|
// Worst case minimum CQ entries that can be accomodated in provided buffer
|
|
// Need to account for the wrap
|
|
g_MSPIState[ui32Module].ui32MaxPending = ((pConfig->ui32TCBSize - 8) * 4 / AM_HAL_MSPI_CQ_ENTRY_SIZE);
|
|
if (g_MSPIState[ui32Module].ui32MaxPending > AM_HAL_MSPI_MAX_CQ_ENTRIES)
|
|
{
|
|
g_MSPIState[ui32Module].ui32MaxPending = AM_HAL_MSPI_MAX_CQ_ENTRIES;
|
|
}
|
|
}
|
|
//
|
|
// Reset the register storage for next write.
|
|
//
|
|
ui32Config = 0;
|
|
|
|
//
|
|
// Set the scrambling start and end addresses aligned to 64K region.
|
|
//
|
|
MSPIn(ui32Module)->SCRAMBLING =
|
|
_VAL2FLD(MSPI0_SCRAMBLING_SCRSTART, pConfig->scramblingStartAddr >> 16) |
|
|
_VAL2FLD(MSPI0_SCRAMBLING_SCREND, pConfig->scramblingEndAddr >> 16);
|
|
|
|
//
|
|
// Set the selected IOM to disable.
|
|
//
|
|
MSPIn(ui32Module)->MSPICFG_b.IOMSEL = AM_HAL_MSPI_LINK_NONE;
|
|
|
|
{
|
|
mspi_device_info_t MSPIDeviceInfo;
|
|
uint32_t ui32DeviceConfig;
|
|
uint32_t ui32Status;
|
|
|
|
//
|
|
// Determine the virtual device configuration.
|
|
//
|
|
MSPIDeviceInfo.eDeviceConfig = pConfig->eDeviceConfig;
|
|
MSPIDeviceInfo.eXipMixedMode = pConfig->eXipMixedMode;
|
|
MSPIDeviceInfo.bSeparateIO = pConfig->bSeparateIO;
|
|
ui32Status = mspi_virtual_device(&MSPIDeviceInfo, &ui32DeviceConfig);
|
|
if (AM_HAL_STATUS_SUCCESS != ui32Status)
|
|
{
|
|
return ui32Status;
|
|
}
|
|
|
|
//
|
|
// Configure the MSPI for a specific device configuration.
|
|
// This function sets the following registers/fields:
|
|
// CFG.DEVCFG
|
|
// CFG.SEPIO
|
|
// FLASH.XIPMIXED
|
|
// PADCFG
|
|
// PADOUTEN
|
|
//
|
|
mspi_device_configure(pHandle, ui32DeviceConfig);
|
|
}
|
|
|
|
//
|
|
// Set the default endianess for the FIFO.
|
|
//
|
|
pMSPIState->bBigEndian = false;
|
|
|
|
//
|
|
// Store the clock frequency for later SW workarounds.
|
|
//
|
|
pMSPIState->eClockFreq = pConfig->eClockFreq;
|
|
|
|
//
|
|
// Set the default maximum delay timeout value.
|
|
//
|
|
pMSPIState->waitTimeout = 10000;
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// MSPI device configuration function
|
|
//
|
|
uint32_t
|
|
am_hal_mspi_enable(void *pHandle)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!AM_HAL_MSPI_CHK_HANDLE(pHandle))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
|
|
if (pMSPIState->pTCB)
|
|
{
|
|
pMSPIState->ui32LastIdxProcessed = 0;
|
|
pMSPIState->ui32NumCQEntries = 0;
|
|
|
|
|
|
//
|
|
// Initialize the Command Queue service with memory supplied by the application.
|
|
//
|
|
mspi_cq_init(pMSPIState->ui32Module, pMSPIState->ui32TCBSize, pMSPIState->pTCB);
|
|
// Initialize Flags used to force CQ Pause
|
|
MSPIn(pMSPIState->ui32Module)->CQSETCLEAR = AM_HAL_MSPI_SC_UNPAUSE_CQ | AM_HAL_MSPI_SC_PAUSE_SEQLOOP;
|
|
pMSPIState->pHPTransactions = NULL;
|
|
pMSPIState->bHP = false;
|
|
pMSPIState->ui32NumHPPendingEntries = 0;
|
|
pMSPIState->block = 0;
|
|
pMSPIState->ui32NumHPEntries = 0;
|
|
pMSPIState->eSeq = AM_HAL_MSPI_SEQ_NONE;
|
|
pMSPIState->ui32NumTransactions = 0;
|
|
pMSPIState->bAutonomous = true;
|
|
pMSPIState->ui32NumUnSolicited = 0;
|
|
}
|
|
|
|
pMSPIState->prefix.s.bEnable = true;
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// MSPI device specific control function.
|
|
//
|
|
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_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_LINK_MSPI:
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
if (!pConfig)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
if (( *((uint32_t *)pConfig) >= AM_REG_MSPI_NUM_MODULES ) && ( *((uint32_t *)pConfig) != AM_HAL_MSPI_LINK_NONE ))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
//
|
|
// Set the Linked MSPI
|
|
//
|
|
MSPIn(ui32Module)->MSPICFG_b.IOMSEL = *((uint32_t *)pConfig) + AM_HAL_MSPI_LINK_BASE;
|
|
break;
|
|
|
|
case AM_HAL_MSPI_REQ_DCX_DIS:
|
|
//
|
|
// Disable DCX.
|
|
//
|
|
MSPIn(ui32Module)->FLASH_b.XIPENDCX = 0;
|
|
break;
|
|
|
|
case AM_HAL_MSPI_REQ_DCX_EN:
|
|
//
|
|
// Enable DCX.
|
|
//
|
|
MSPIn(ui32Module)->FLASH_b.XIPENDCX = 1;
|
|
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_DDR_DIS:
|
|
//
|
|
// Disable DDR.
|
|
//
|
|
MSPIn(ui32Module)->MSPIDDR_b.EMULATEDDR = 0;
|
|
break;
|
|
|
|
case AM_HAL_MSPI_REQ_DDR_EN:
|
|
//
|
|
// DDR emulation is only supported for 24MHz/48MHz.
|
|
//
|
|
if (pMSPIState->eClockFreq > AM_HAL_MSPI_CLK_24MHZ)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
//
|
|
// Enable DDR.
|
|
//
|
|
MSPIn(ui32Module)->MSPIDDR_b.EMULATEDDR = 1;
|
|
break;
|
|
|
|
case AM_HAL_MSPI_REQ_DQS:
|
|
{
|
|
uint32_t ui32Status = AM_HAL_STATUS_SUCCESS;
|
|
am_hal_mspi_dqs_t *pDQS = (am_hal_mspi_dqs_t *)pConfig;
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
if (!pConfig)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
#endif
|
|
MSPIn(ui32Module)->MSPIDDR_b.ENABLEDQS = pDQS->bDQSEnable;
|
|
MSPIn(ui32Module)->MSPIDDR_b.OVERRIDERXDQSDELAY = pDQS->bOverrideRXDQSDelay;
|
|
MSPIn(ui32Module)->MSPIDDR_b.RXDQSDELAY = pDQS->ui8RxDQSDelay;
|
|
MSPIn(ui32Module)->MSPIDDR_b.OVERRIDEDDRCLKOUTDELAY = pDQS->bOverrideTXDQSDelay;
|
|
MSPIn(ui32Module)->MSPIDDR_b.TXDQSDELAY = pDQS->ui8TxDQSDelay;
|
|
MSPIn(ui32Module)->MSPIDDR_b.DQSSYNCNEG = pDQS->bDQSSyncNeg;
|
|
return ui32Status;
|
|
}
|
|
//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;
|
|
}
|
|
if (pMSPIState->prefix.s.bEnable)
|
|
{
|
|
return AM_HAL_STATUS_IN_USE;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
{
|
|
uint32_t ui32DeviceConfig;
|
|
uint32_t ui32Status;
|
|
|
|
//
|
|
// Determine the virtual device configuration.
|
|
//
|
|
ui32Status = mspi_virtual_device((mspi_device_info_t *)pConfig, &ui32DeviceConfig);
|
|
if (AM_HAL_STATUS_SUCCESS != ui32Status)
|
|
{
|
|
return ui32Status;
|
|
}
|
|
|
|
//
|
|
// 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, ui32DeviceConfig);
|
|
if (AM_HAL_STATUS_SUCCESS != ui32Status)
|
|
{
|
|
return ui32Status;
|
|
}
|
|
}
|
|
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:
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return ui32Status;
|
|
}
|
|
|
|
//
|
|
// MSPI get capabilities
|
|
//
|
|
uint32_t am_hal_mspi_capabilities_get(void *pHandle,
|
|
am_hal_mspi_capabilities_t **pCapabilities)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Module;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!AM_HAL_MSPI_CHK_HANDLE(pHandle))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
ui32Module = pMSPIState->ui32Module;
|
|
//
|
|
// copy the pointer the MSPI instance capabilities into the passed pointer
|
|
//
|
|
*pCapabilities = &g_MSPIState[ui32Module].capabilities;
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// MSPI blocking transfer function
|
|
//
|
|
uint32_t am_hal_mspi_blocking_transfer(void *pHandle,
|
|
am_hal_mspi_pio_transfer_t *pTransaction,
|
|
uint32_t ui32Timeout)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Module;
|
|
uint32_t ui32Control = 0;
|
|
uint32_t ui32Status = AM_HAL_STATUS_SUCCESS;
|
|
uint32_t intMask;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!AM_HAL_MSPI_CHK_HANDLE(pHandle))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
//
|
|
// Check that the interface is enabled.
|
|
//
|
|
if (!pMSPIState->prefix.s.bEnable)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
ui32Module = pMSPIState->ui32Module;
|
|
|
|
// Make sure there is no non-blocking transaction in progress
|
|
if (pMSPIState->ui32NumCQEntries || pMSPIState->ui32NumHPEntries)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
|
}
|
|
if (pMSPIState->eSeq == AM_HAL_MSPI_SEQ_RUNNING)
|
|
{
|
|
// Dynamic additions to sequence not allowed
|
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
|
}
|
|
|
|
//
|
|
// Set the number of bytes to transfer.
|
|
//
|
|
ui32Control |= _VAL2FLD(MSPI0_CTRL_XFERBYTES, pTransaction->ui32NumBytes);
|
|
|
|
//
|
|
// Set the PIO default to scrambling disabled.
|
|
//
|
|
ui32Control |= _VAL2FLD(MSPI0_CTRL_PIOSCRAMBLE, pTransaction->bScrambling);
|
|
|
|
//
|
|
// Set transmit or receive operation.
|
|
//
|
|
ui32Control |= _VAL2FLD(MSPI0_CTRL_TXRX, pTransaction->eDirection);
|
|
|
|
//
|
|
// Set the indication to send an instruction and set the instruction value if
|
|
// we have a valid instruction.
|
|
//
|
|
if ( pTransaction->bSendInstr )
|
|
{
|
|
ui32Control |= _VAL2FLD(MSPI0_CTRL_SENDI, 1);
|
|
MSPIn(ui32Module)->INSTR =
|
|
_VAL2FLD(MSPI0_INSTR_INSTR, pTransaction->ui16DeviceInstr);
|
|
}
|
|
|
|
//
|
|
// Set the inidication to send an address and set the address value if we have
|
|
// a valid address.
|
|
//
|
|
if ( pTransaction->bSendAddr )
|
|
{
|
|
ui32Control |= _VAL2FLD(MSPI0_CTRL_SENDA, 1);
|
|
MSPIn(ui32Module)->ADDR =
|
|
_VAL2FLD(MSPI0_ADDR_ADDR, pTransaction->ui32DeviceAddr);
|
|
}
|
|
|
|
//
|
|
// Set the turn-around if needed.
|
|
//
|
|
if ( pTransaction->bTurnaround )
|
|
{
|
|
ui32Control |= _VAL2FLD(MSPI0_CTRL_ENTURN, 1);
|
|
}
|
|
|
|
//
|
|
// Set the default FIFO Little Endian format.
|
|
//
|
|
ui32Control |= _VAL2FLD(MSPI0_CTRL_BIGENDIAN, pMSPIState->bBigEndian);
|
|
|
|
//
|
|
// Set for default of no continuation.
|
|
//
|
|
ui32Control |= _VAL2FLD(MSPI0_CTRL_CONT, pTransaction->bContinue);
|
|
|
|
//
|
|
// Set the write latency counter enable if needed.
|
|
//
|
|
ui32Control |= _VAL2FLD(MSPI0_CTRL_ENWLAT, pTransaction->bEnWRLatency);
|
|
|
|
//
|
|
// Set the Quad Command if this is transmit and the device is configured
|
|
// for Dual Quad mode.
|
|
//
|
|
if ( pTransaction->bQuadCmd )
|
|
{
|
|
ui32Control |= _VAL2FLD(MSPI0_CTRL_QUADCMD, 1);
|
|
}
|
|
|
|
//
|
|
// Enabled DCX if requested.
|
|
//
|
|
if ( pTransaction->bDCX)
|
|
{
|
|
ui32Control |= _VAL2FLD(MSPI0_CTRL_ENDCX, 1);
|
|
}
|
|
|
|
//
|
|
// Start the Transfer.
|
|
//
|
|
ui32Control |= _VAL2FLD(MSPI0_CTRL_START, 1);
|
|
|
|
// Disable all interrupts
|
|
intMask = MSPIn(ui32Module)->INTEN;
|
|
MSPIn(ui32Module)->INTEN = 0;
|
|
MSPIn(ui32Module)->INTCLR = AM_HAL_MSPI_INT_ALL;
|
|
|
|
//
|
|
// Initiate the Transfer.
|
|
//
|
|
MSPIn(ui32Module)->CTRL = ui32Control;
|
|
|
|
//
|
|
// Read or Feed the FIFOs.
|
|
//
|
|
if ( AM_HAL_MSPI_RX == pTransaction->eDirection )
|
|
{
|
|
ui32Status = mspi_fifo_read(ui32Module, pTransaction->pui32Buffer,
|
|
pTransaction->ui32NumBytes, pMSPIState->waitTimeout);
|
|
}
|
|
else if ( AM_HAL_MSPI_TX == pTransaction->eDirection )
|
|
{
|
|
ui32Status = mspi_fifo_write(ui32Module, pTransaction->pui32Buffer,
|
|
pTransaction->ui32NumBytes, pMSPIState->waitTimeout );
|
|
}
|
|
|
|
//
|
|
// Check status.
|
|
//
|
|
if (AM_HAL_STATUS_SUCCESS != ui32Status)
|
|
{
|
|
//
|
|
// Restore interrupts
|
|
//
|
|
MSPIn(ui32Module)->INTCLR = AM_HAL_MSPI_INT_ALL;
|
|
MSPIn(ui32Module)->INTEN = intMask;
|
|
return ui32Status;
|
|
}
|
|
|
|
//
|
|
// Wait for the command to complete.
|
|
//
|
|
ui32Status = am_hal_flash_delay_status_check(ui32Timeout,
|
|
(uint32_t)&MSPIn(ui32Module)->CTRL,
|
|
MSPI0_CTRL_STATUS_Msk,
|
|
_VAL2FLD(MSPI0_CTRL_STATUS, 1),
|
|
true);
|
|
|
|
//
|
|
// Restore interrupts
|
|
//
|
|
MSPIn(ui32Module)->INTCLR = AM_HAL_MSPI_INT_ALL;
|
|
MSPIn(ui32Module)->INTEN = intMask;
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return ui32Status;
|
|
}
|
|
|
|
//
|
|
// MSPI Non-Blocking transfer function
|
|
//
|
|
uint32_t am_hal_mspi_nonblocking_transfer(void *pHandle,
|
|
void *pTransfer,
|
|
am_hal_mspi_trans_e eMode,
|
|
am_hal_mspi_callback_t pfnCallback,
|
|
void *pCallbackCtxt)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Status = AM_HAL_STATUS_SUCCESS;
|
|
uint32_t ui32NumPend;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!AM_HAL_MSPI_CHK_HANDLE(pHandle))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
if (!pMSPIState->pTCB)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
|
}
|
|
//
|
|
// Check that the interface is enabled.
|
|
//
|
|
if (!pMSPIState->prefix.s.bEnable)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
|
}
|
|
|
|
#if 0 // We should be able to queue up the CQ even if high priority transaction is in progress
|
|
if (pMSPIState->ui32NumHPEntries)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
|
}
|
|
#endif
|
|
if (pMSPIState->eSeq == AM_HAL_MSPI_SEQ_RUNNING)
|
|
{
|
|
// Dynamic additions to sequence not allowed
|
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
|
}
|
|
//
|
|
// Check for DMA to/from DTCM.
|
|
//
|
|
if ( AM_HAL_MSPI_TRANS_DMA == eMode)
|
|
{
|
|
am_hal_mspi_dma_transfer_t *pDMATransaction = (am_hal_mspi_dma_transfer_t *)pTransfer;
|
|
if ( (pDMATransaction->ui32SRAMAddress >= AM_HAL_FLASH_DTCM_START) &&
|
|
(pDMATransaction->ui32SRAMAddress <= AM_HAL_FLASH_DTCM_END) )
|
|
{
|
|
return AM_HAL_STATUS_OUT_OF_RANGE;
|
|
}
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
am_hal_mspi_callback_t pfnCallback1 = 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;
|
|
}
|
|
|
|
//
|
|
// DMA defaults to using the Command Queue
|
|
//
|
|
ui32Status = mspi_cq_add_transaction(pHandle, pTransfer, eMode, pfnCallback1, pCallbackCtxt);
|
|
|
|
if (AM_HAL_STATUS_SUCCESS != ui32Status)
|
|
{
|
|
return ui32Status;
|
|
}
|
|
else
|
|
{
|
|
|
|
uint32_t ui32Critical = 0;
|
|
|
|
//
|
|
// Start a critical section.
|
|
//
|
|
ui32Critical = am_hal_interrupt_master_disable();
|
|
|
|
//
|
|
// Post the transaction to the CQ.
|
|
//
|
|
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 (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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return ui32Status;
|
|
}
|
|
|
|
//
|
|
// MSPI status function
|
|
//
|
|
uint32_t am_hal_mspi_status_get(void *pHandle,
|
|
am_hal_mspi_status_t *pStatus )
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Module;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!AM_HAL_MSPI_CHK_HANDLE(pHandle))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
ui32Module = pMSPIState->ui32Module;
|
|
//
|
|
// Get the Command Complete status.
|
|
//
|
|
// TODO: Need to implement.
|
|
|
|
//
|
|
// Get the FIFO status.
|
|
//
|
|
// TODO: Need to implement.
|
|
|
|
//
|
|
// Get the DMA status.
|
|
//
|
|
pStatus->bErr = ((MSPIn(ui32Module)->DMASTAT & MSPI0_DMASTAT_DMAERR_Msk) > 0);
|
|
pStatus->bCmp = ((MSPIn(ui32Module)->DMASTAT & MSPI0_DMASTAT_DMACPL_Msk) > 0);
|
|
pStatus->bTIP = ((MSPIn(ui32Module)->DMASTAT & MSPI0_DMASTAT_DMATIP_Msk) > 0);
|
|
|
|
//
|
|
// Get the CQ status.
|
|
//
|
|
// TODO: Need to implement.
|
|
pStatus->ui32NumCQEntries = pMSPIState->ui32NumCQEntries;
|
|
|
|
//
|
|
// Get the scrambling status.
|
|
//
|
|
// TODO: Need to implement.
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// MSPI enable interrupts function
|
|
//
|
|
uint32_t am_hal_mspi_interrupt_enable(void *pHandle,
|
|
uint32_t ui32IntMask)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Module;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!AM_HAL_MSPI_CHK_HANDLE(pHandle))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
ui32Module = pMSPIState->ui32Module;
|
|
//
|
|
// Set the interrupt enables according to the mask.
|
|
//
|
|
MSPIn(ui32Module)->INTEN |= ui32IntMask;
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// MSPI disable interrupts function
|
|
//
|
|
uint32_t am_hal_mspi_interrupt_disable(void *pHandle,
|
|
uint32_t ui32IntMask)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Module;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!AM_HAL_MSPI_CHK_HANDLE(pHandle))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
ui32Module = pMSPIState->ui32Module;
|
|
//
|
|
// Clear the interrupt enables according to the mask.
|
|
//
|
|
MSPIn(ui32Module)->INTEN &= ~ui32IntMask;
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// MSPI interrupt status function
|
|
//
|
|
uint32_t am_hal_mspi_interrupt_status_get(void *pHandle,
|
|
uint32_t *pui32Status,
|
|
bool bEnabledOnly)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Module;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!AM_HAL_MSPI_CHK_HANDLE(pHandle))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
ui32Module = pMSPIState->ui32Module;
|
|
//
|
|
// if requested, only return the interrupts that are enabled.
|
|
//
|
|
if ( bEnabledOnly )
|
|
{
|
|
uint32_t ui32RetVal = MSPIn(ui32Module)->INTSTAT;
|
|
*pui32Status = ui32RetVal & MSPIn(ui32Module)->INTEN;
|
|
}
|
|
else
|
|
{
|
|
*pui32Status = MSPIn(ui32Module)->INTSTAT;
|
|
}
|
|
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// MSPI interrupt clear
|
|
//
|
|
uint32_t am_hal_mspi_interrupt_clear(void *pHandle,
|
|
uint32_t ui32IntMask)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Module;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if ( !AM_HAL_MSPI_CHK_HANDLE(pHandle) )
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
ui32Module = pMSPIState->ui32Module;
|
|
//
|
|
// clear the requested interrupts.
|
|
//
|
|
MSPIn(ui32Module)->INTCLR = ui32IntMask;
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// MSPI interrupt service routine
|
|
//
|
|
uint32_t am_hal_mspi_interrupt_service(void *pHandle, uint32_t ui32IntStatus)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
uint32_t ui32Module;
|
|
uint32_t ui32Status;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!AM_HAL_MSPI_CHK_HANDLE(pHandle))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
ui32Module = pMSPIState->ui32Module;
|
|
//
|
|
// Add a delay to help make the service function work.
|
|
// TODO - why do we need this?
|
|
//
|
|
// am_hal_flash_delay(FLASH_CYCLES_US(10));
|
|
|
|
if (pMSPIState->bHP)
|
|
{
|
|
#if 0
|
|
if (ui32IntStatus & AM_HAL_MSPI_INT_CQUPD)
|
|
{
|
|
while(1);
|
|
}
|
|
#endif
|
|
//
|
|
// Accumulate the INTSTAT for this transaction
|
|
//
|
|
pMSPIState->ui32TxnInt |= ui32IntStatus;
|
|
|
|
//
|
|
// We need to wait for the DMA complete as well
|
|
//
|
|
if (pMSPIState->ui32TxnInt & (AM_HAL_MSPI_INT_DMACMP | AM_HAL_MSPI_INT_ERR))
|
|
{
|
|
uint32_t index;
|
|
|
|
//
|
|
// Wait for the command completion.
|
|
// In Apollo3P - we cannot rely on CMDCMP, as hardware may be splitting
|
|
// the transactions internally, and each split transaction generates CMDCMP
|
|
// DMATIP is guaranteed to deassert only once the bus transaction is done
|
|
//
|
|
// TODO - We are waiting for DMATIP indefinetely in the ISR
|
|
// May need to re-evaluate
|
|
while (MSPIn(pMSPIState->ui32Module)->DMASTAT_b.DMATIP);
|
|
pMSPIState->ui32TxnInt |= MSPIn(ui32Module)->INTSTAT;
|
|
|
|
//
|
|
// Clear the interrupt status
|
|
//
|
|
MSPIn(ui32Module)->INTCLR = AM_HAL_MSPI_INT_ALL;
|
|
|
|
//
|
|
// Need to determine the error, call the callback with proper status
|
|
//
|
|
if (pMSPIState->ui32TxnInt & AM_HAL_MSPI_INT_ERR)
|
|
{
|
|
ui32Status = AM_HAL_STATUS_FAIL;
|
|
|
|
//
|
|
// Disable DMA
|
|
//
|
|
MSPIn(ui32Module)->DMACFG_b.DMAEN = 0;
|
|
|
|
//
|
|
// Must reset xfer block
|
|
//
|
|
MSPIn(ui32Module)->MSPICFG_b.IPRSTN = 0; // in reset
|
|
MSPIn(ui32Module)->MSPICFG_b.IPRSTN = 1; // back out -- clears current transfer
|
|
}
|
|
else
|
|
{
|
|
ui32Status = AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
pMSPIState->ui32LastHPIdxProcessed++;
|
|
pMSPIState->ui32NumHPEntries--;
|
|
index = pMSPIState->ui32LastHPIdxProcessed % pMSPIState->ui32MaxHPTransactions;
|
|
am_hal_mspi_dma_entry_t *pDMAEntry = &pMSPIState->pHPTransactions[index];
|
|
|
|
//
|
|
// Call the callback
|
|
//
|
|
if ( pDMAEntry->pfnCallback != NULL )
|
|
{
|
|
pDMAEntry->pfnCallback(pDMAEntry->pCallbackCtxt, ui32Status);
|
|
pDMAEntry->pfnCallback = NULL;
|
|
}
|
|
|
|
//
|
|
// Post next transaction if queue is not empty
|
|
//
|
|
if (pMSPIState->ui32NumHPEntries)
|
|
{
|
|
pMSPIState->ui32TxnInt = 0;
|
|
program_dma(pMSPIState);
|
|
}
|
|
else
|
|
{
|
|
pMSPIState->bHP = false;
|
|
// Unpause the CQ
|
|
//
|
|
// Command to set the DMACFG to disable DMA.
|
|
// Need to make sure we disable DMA before we can reprogram
|
|
//
|
|
MSPIn(ui32Module)->DMACFG = _VAL2FLD(MSPI0_DMACFG_DMAEN, 0);
|
|
// Restore interrupts
|
|
MSPIn(ui32Module)->INTEN &= ~(AM_HAL_MSPI_INT_DMACMP);
|
|
// Resume the CQ
|
|
MSPIn(ui32Module)->CQSETCLEAR = AM_HAL_MSPI_SC_UNPAUSE_CQ;
|
|
}
|
|
}
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
//
|
|
// Need to check if there is an ongoing transaction
|
|
// This is needed because we may get interrupts even for the XIP transactions
|
|
//
|
|
if (pMSPIState->ui32NumCQEntries)
|
|
{
|
|
am_hal_cmdq_status_t status;
|
|
uint32_t index;
|
|
am_hal_mspi_CQ_t *pCQ = &g_MSPIState[ui32Module].CQ;
|
|
|
|
//
|
|
// Get the current and last indexes.
|
|
//
|
|
if (pCQ->pCmdQHdl)
|
|
{
|
|
ui32Status = am_hal_cmdq_get_status(pCQ->pCmdQHdl, &status);
|
|
|
|
if (AM_HAL_STATUS_SUCCESS == ui32Status)
|
|
{
|
|
// For Sequence - this can be updated in the callback
|
|
pMSPIState->bRestart = false;
|
|
//
|
|
// Figure out which callbacks need to be handled.
|
|
//
|
|
while (!pMSPIState->bRestart && (pMSPIState->ui32LastIdxProcessed != status.lastIdxProcessed))
|
|
{
|
|
|
|
pMSPIState->ui32LastIdxProcessed++;
|
|
pMSPIState->ui32NumCQEntries--;
|
|
index = pMSPIState->ui32LastIdxProcessed & (AM_HAL_MSPI_MAX_CQ_ENTRIES - 1);
|
|
if ( pMSPIState->pfnCallback[index] != NULL )
|
|
{
|
|
pMSPIState->pfnCallback[index](pMSPIState->pCallbackCtxt[index], AM_HAL_STATUS_SUCCESS);
|
|
if (pMSPIState->eSeq != AM_HAL_MSPI_SEQ_RUNNING)
|
|
{
|
|
pMSPIState->pfnCallback[index] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// For Sequence - this can be updated in the callback
|
|
if (!pMSPIState->bRestart)
|
|
{
|
|
//
|
|
// Process one extra callback if there was an error.
|
|
//
|
|
if ( (ui32IntStatus & AM_HAL_MSPI_INT_ERR) || (status.bErr) )
|
|
{
|
|
pMSPIState->ui32LastIdxProcessed++;
|
|
pMSPIState->ui32NumCQEntries--;
|
|
index = pMSPIState->ui32LastIdxProcessed & (AM_HAL_MSPI_MAX_CQ_ENTRIES - 1);
|
|
if ( pMSPIState->pfnCallback[index] != NULL )
|
|
{
|
|
pMSPIState->pfnCallback[index](pMSPIState->pCallbackCtxt[index], AM_HAL_STATUS_FAIL);
|
|
if (pMSPIState->eSeq != AM_HAL_MSPI_SEQ_RUNNING)
|
|
{
|
|
pMSPIState->pfnCallback[index] = NULL;
|
|
}
|
|
}
|
|
// Disable CQ
|
|
ui32Status = mspi_cq_disable(pMSPIState);
|
|
if (AM_HAL_STATUS_SUCCESS != ui32Status)
|
|
{
|
|
return ui32Status;
|
|
}
|
|
// Disable DMA
|
|
MSPIn(ui32Module)->DMACFG_b.DMAEN = 0;
|
|
|
|
// Must reset xfer block
|
|
MSPIn(ui32Module)->MSPICFG_b.IPRSTN = 0; // in reset
|
|
MSPIn(ui32Module)->MSPICFG_b.IPRSTN = 1; // back out -- clears current transfer
|
|
|
|
// Clear the CQ error.
|
|
MSPIn(ui32Module)->CQSTAT |= _VAL2FLD(MSPI0_CQSTAT_CQERR, 0);
|
|
am_hal_cmdq_error_resume(pCQ->pCmdQHdl);
|
|
if (pMSPIState->ui32NumCQEntries)
|
|
{
|
|
// Re-enable CQ
|
|
ui32Status = mspi_cq_enable(pMSPIState);
|
|
if (AM_HAL_STATUS_SUCCESS != ui32Status)
|
|
{
|
|
return ui32Status;
|
|
}
|
|
}
|
|
}
|
|
if (pMSPIState->ui32NumCQEntries == 0)
|
|
{
|
|
// Disable CQ
|
|
ui32Status = mspi_cq_disable(pMSPIState);
|
|
if (AM_HAL_STATUS_SUCCESS != ui32Status)
|
|
{
|
|
return ui32Status;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pMSPIState->ui32NumCQEntries == 0)
|
|
{
|
|
// Disable DMA
|
|
MSPIn(ui32Module)->DMACFG_b.DMAEN = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// MSPI power control function
|
|
//
|
|
uint32_t am_hal_mspi_power_control(void *pHandle,
|
|
am_hal_sysctrl_power_state_e ePowerState,
|
|
bool bRetainState)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
|
|
#ifndef AM_HAL_DISABLE_API_VALIDATION
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!AM_HAL_MSPI_CHK_HANDLE(pHandle))
|
|
{
|
|
return AM_HAL_STATUS_INVALID_HANDLE;
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
//
|
|
// Decode the requested power state and update MSPI operation accordingly.
|
|
//
|
|
switch (ePowerState)
|
|
{
|
|
case AM_HAL_SYSCTRL_WAKE:
|
|
|
|
if (bRetainState && !pMSPIState->registerState.bValid)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
|
}
|
|
|
|
//
|
|
// Enable power control.
|
|
//
|
|
am_hal_pwrctrl_periph_enable((am_hal_pwrctrl_periph_e)(AM_HAL_PWRCTRL_PERIPH_MSPI0 + pMSPIState->ui32Module));
|
|
|
|
if (bRetainState)
|
|
{
|
|
//
|
|
// Restore MSPI registers
|
|
//
|
|
MSPIn(pMSPIState->ui32Module)->CFG = pMSPIState->registerState.regCFG;
|
|
MSPIn(pMSPIState->ui32Module)->MSPICFG = pMSPIState->registerState.regMSPICFG;
|
|
MSPIn(pMSPIState->ui32Module)->PADCFG = pMSPIState->registerState.regPADCFG;
|
|
MSPIn(pMSPIState->ui32Module)->PADOUTEN = pMSPIState->registerState.regPADOUTEN;
|
|
MSPIn(pMSPIState->ui32Module)->FLASH = pMSPIState->registerState.regFLASH;
|
|
MSPIn(pMSPIState->ui32Module)->SCRAMBLING = pMSPIState->registerState.regSCRAMBLING;
|
|
MSPIn(pMSPIState->ui32Module)->CQCFG = pMSPIState->registerState.regCQCFG;
|
|
MSPIn(pMSPIState->ui32Module)->CQADDR = pMSPIState->registerState.regCQADDR;
|
|
MSPIn(pMSPIState->ui32Module)->CQPAUSE = pMSPIState->registerState.regCQPAUSE;
|
|
MSPIn(pMSPIState->ui32Module)->CQCURIDX = pMSPIState->registerState.regCQCURIDX;
|
|
MSPIn(pMSPIState->ui32Module)->CQENDIDX = pMSPIState->registerState.regCQENDIDX;
|
|
MSPIn(pMSPIState->ui32Module)->INTEN = pMSPIState->registerState.regINTEN;
|
|
|
|
// TODO: May be we can just set these values, as they are constants anyways?
|
|
MSPIn(pMSPIState->ui32Module)->DMABCOUNT = pMSPIState->registerState.regDMABCOUNT;
|
|
MSPIn(pMSPIState->ui32Module)->DMATHRESH = pMSPIState->registerState.regDMATHRESH;
|
|
|
|
pMSPIState->registerState.bValid = false;
|
|
}
|
|
break;
|
|
|
|
case AM_HAL_SYSCTRL_NORMALSLEEP:
|
|
case AM_HAL_SYSCTRL_DEEPSLEEP:
|
|
// Make sure MPSI is not active currently
|
|
if (pMSPIState->prefix.s.bEnable &&
|
|
((MSPIn(pMSPIState->ui32Module)->DMASTAT_b.DMATIP) ||
|
|
pMSPIState->ui32NumHPPendingEntries))
|
|
{
|
|
return AM_HAL_STATUS_IN_USE;
|
|
}
|
|
if (bRetainState)
|
|
{
|
|
//
|
|
// Save MSPI Registers
|
|
//
|
|
pMSPIState->registerState.regCFG = MSPIn(pMSPIState->ui32Module)->CFG;
|
|
pMSPIState->registerState.regMSPICFG = MSPIn(pMSPIState->ui32Module)->MSPICFG;
|
|
pMSPIState->registerState.regPADCFG = MSPIn(pMSPIState->ui32Module)->PADCFG;
|
|
pMSPIState->registerState.regPADOUTEN = MSPIn(pMSPIState->ui32Module)->PADOUTEN;
|
|
pMSPIState->registerState.regFLASH = MSPIn(pMSPIState->ui32Module)->FLASH;
|
|
pMSPIState->registerState.regSCRAMBLING = MSPIn(pMSPIState->ui32Module)->SCRAMBLING;
|
|
pMSPIState->registerState.regCQADDR = MSPIn(pMSPIState->ui32Module)->CQADDR;
|
|
pMSPIState->registerState.regCQPAUSE = MSPIn(pMSPIState->ui32Module)->CQPAUSE;
|
|
pMSPIState->registerState.regCQCURIDX = MSPIn(pMSPIState->ui32Module)->CQCURIDX;
|
|
pMSPIState->registerState.regCQENDIDX = MSPIn(pMSPIState->ui32Module)->CQENDIDX;
|
|
pMSPIState->registerState.regINTEN = MSPIn(pMSPIState->ui32Module)->INTEN;
|
|
|
|
// TODO: May be no need to store these values, as they are constants anyways?
|
|
pMSPIState->registerState.regDMABCOUNT = MSPIn(pMSPIState->ui32Module)->DMABCOUNT;
|
|
pMSPIState->registerState.regDMATHRESH = MSPIn(pMSPIState->ui32Module)->DMATHRESH;
|
|
|
|
//
|
|
// Set the CQCFG last
|
|
//
|
|
pMSPIState->registerState.regCQCFG = MSPIn(pMSPIState->ui32Module)->CQCFG;
|
|
pMSPIState->registerState.bValid = true;
|
|
}
|
|
|
|
//
|
|
// Disable all the interrupts.
|
|
//
|
|
am_hal_mspi_interrupt_disable(pHandle, MSPI0_INTEN_SCRERR_Msk |
|
|
MSPI0_INTEN_CQERR_Msk |
|
|
MSPI0_INTEN_CQPAUSED_Msk |
|
|
MSPI0_INTEN_CQUPD_Msk |
|
|
MSPI0_INTEN_CQCMP_Msk |
|
|
MSPI0_INTEN_DERR_Msk |
|
|
MSPI0_INTEN_DCMP_Msk |
|
|
MSPI0_INTEN_RXF_Msk |
|
|
MSPI0_INTEN_RXO_Msk |
|
|
MSPI0_INTEN_RXU_Msk |
|
|
MSPI0_INTEN_TXO_Msk |
|
|
MSPI0_INTEN_TXE_Msk |
|
|
MSPI0_INTEN_CMDCMP_Msk);
|
|
|
|
//
|
|
// Disable power control.
|
|
//
|
|
am_hal_pwrctrl_periph_disable((am_hal_pwrctrl_periph_e)(AM_HAL_PWRCTRL_PERIPH_MSPI0 + pMSPIState->ui32Module));
|
|
break;
|
|
|
|
default:
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return AM_HAL_STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// MSPI High Priority transfer function
|
|
//
|
|
uint32_t am_hal_mspi_highprio_transfer(void *pHandle,
|
|
am_hal_mspi_dma_transfer_t *pTransfer,
|
|
am_hal_mspi_trans_e eMode,
|
|
am_hal_mspi_callback_t pfnCallback,
|
|
void *pCallbackCtxt)
|
|
{
|
|
am_hal_mspi_state_t *pMSPIState = (am_hal_mspi_state_t *)pHandle;
|
|
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;
|
|
}
|
|
if (!pMSPIState->pTCB)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
|
}
|
|
if (!pMSPIState->pHPTransactions)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_OPERATION;
|
|
}
|
|
if (pTransfer->ui32PauseCondition != 0)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
if (pTransfer->ui32StatusSetClr != 0)
|
|
{
|
|
return AM_HAL_STATUS_INVALID_ARG;
|
|
}
|
|
//
|
|
// Check for DMA to/from DTCM.
|
|
//
|
|
if ( AM_HAL_MSPI_TRANS_DMA == eMode)
|
|
{
|
|
am_hal_mspi_dma_transfer_t *pDMATransaction = (am_hal_mspi_dma_transfer_t *)pTransfer;
|
|
if ( (pDMATransaction->ui32SRAMAddress >= AM_HAL_FLASH_DTCM_START) &&
|
|
(pDMATransaction->ui32SRAMAddress <= AM_HAL_FLASH_DTCM_END) )
|
|
{
|
|
return AM_HAL_STATUS_OUT_OF_RANGE;
|
|
}
|
|
}
|
|
#endif // AM_HAL_DISABLE_API_VALIDATION
|
|
|
|
ui32Status = mspi_add_hp_transaction(pHandle, pTransfer, pfnCallback, pCallbackCtxt);
|
|
|
|
if (ui32Status == AM_HAL_STATUS_SUCCESS)
|
|
{
|
|
if (!(pMSPIState->block))
|
|
{
|
|
ui32Status = sched_hiprio(pMSPIState, 1);
|
|
}
|
|
else
|
|
{
|
|
pMSPIState->ui32NumHPPendingEntries++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the status.
|
|
//
|
|
return ui32Status;
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// End Doxygen group.
|
|
//! @}
|
|
//
|
|
//*****************************************************************************
|