blob: d33420418ba0bb7a071f7636eaca80fcbed8cc26 [file] [log] [blame]
/*
* Copyright (c) 2015, Freescale Semiconductor, Inc.
* Copyright 2016-2019 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_qspi.h"
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.qspi"
#endif
/*******************************************************************************
* Definitations
******************************************************************************/
enum _qspi_transfer_state
{
kQSPI_TxBusy = 0x0U, /*!< QSPI is busy */
kQSPI_TxIdle, /*!< Transfer is done. */
kQSPI_TxError /*!< Transfer error occurred. */
};
#define QSPI_AHB_BUFFER_REG(base, index) (((volatile uint32_t *)&((base)->BUF0CR))[(index)])
#if (!defined(FSL_FEATURE_QSPI_HAS_NO_SOCCR_REG)) || !FSL_FEATURE_QSPI_HAS_NO_SOCCR_REG
#ifndef QuadSPI_SOCCR_DQS_LOOPBACK_EN_MASK
#define QuadSPI_SOCCR_DQS_LOOPBACK_EN_MASK (0x100U)
#endif
#ifndef QuadSPI_SOCCR_DQS_LOOPBACK_FROM_PAD_MASK
#define QuadSPI_SOCCR_DQS_LOOPBACK_FROM_PAD_MASK (0x200U)
#endif
#ifndef QuadSPI_SOCCR_DQS_PHASE_SEL_MASK
#define QuadSPI_SOCCR_DQS_PHASE_SEL_MASK (0xC00U)
#define QuadSPI_SOCCR_DQS_PHASE_SEL_SHIFT (10U)
#define QuadSPI_SOCCR_DQS_PHASE_SEL(x) \
(((uint32_t)(((uint32_t)(x)) << QuadSPI_SOCCR_DQS_PHASE_SEL_SHIFT)) & QuadSPI_SOCCR_DQS_PHASE_SEL_MASK)
#endif
#ifndef QuadSPI_SOCCR_DQS_INV_EN_MASK
#define QuadSPI_SOCCR_DQS_INV_EN_MASK (0x1000U)
#define QuadSPI_SOCCR_DQS_INV_EN_SHIFT (12U)
#define QuadSPI_SOCCR_DQS_INV_EN(x) \
(((uint32_t)(((uint32_t)(x)) << QuadSPI_SOCCR_DQS_INV_EN_SHIFT)) & QuadSPI_SOCCR_DQS_INV_EN_MASK)
#endif
#ifndef QuadSPI_SOCCR_DQS_IFA_DELAY_CHAIN_SEL_MASK
#define QuadSPI_SOCCR_DQS_IFA_DELAY_CHAIN_SEL_MASK (0x7F0000U)
#define QuadSPI_SOCCR_DQS_IFA_DELAY_CHAIN_SEL_SHIFT (16U)
#define QuadSPI_SOCCR_DQS_IFA_DELAY_CHAIN_SEL(x) \
(((uint32_t)(((uint32_t)(x)) << QuadSPI_SOCCR_DQS_IFA_DELAY_CHAIN_SEL_SHIFT)) & \
QuadSPI_SOCCR_DQS_IFA_DELAY_CHAIN_SEL_MASK)
#endif
#endif /* FSL_FEATURE_QSPI_HAS_NO_SOCCR_REG */
/*******************************************************************************
* Prototypes
******************************************************************************/
/*******************************************************************************
* Variables
******************************************************************************/
/* Base pointer array */
static QuadSPI_Type *const s_qspiBases[] = QuadSPI_BASE_PTRS;
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/* Clock name array */
static const clock_ip_name_t s_qspiClock[] = QSPI_CLOCKS;
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
/*******************************************************************************
* Code
******************************************************************************/
/*!
* brief Get the instance number for QSPI.
*
* param base QSPI base pointer.
*/
uint32_t QSPI_GetInstance(QuadSPI_Type *base)
{
uint32_t instance;
/* Find the instance index from base address mappings. */
for (instance = 0; instance < ARRAY_SIZE(s_qspiBases); instance++)
{
if (s_qspiBases[instance] == base)
{
break;
}
}
assert(instance < ARRAY_SIZE(s_qspiBases));
return instance;
}
/*!
* brief Initializes the QSPI module and internal state.
*
* This function enables the clock for QSPI and also configures the QSPI with the
* input configure parameters. Users should call this function before any QSPI operations.
*
* param base Pointer to QuadSPI Type.
* param config QSPI configure structure.
* param srcClock_Hz QSPI source clock frequency in Hz.
*/
void QSPI_Init(QuadSPI_Type *base, qspi_config_t *config, uint32_t srcClock_Hz)
{
uint32_t i = 0;
uint32_t val = 0;
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/* Enable QSPI clock */
CLOCK_EnableClock(s_qspiClock[QSPI_GetInstance(base)]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
/* Do software reset to QSPI module */
QSPI_SoftwareReset(base);
/* Clear the FIFO region */
QSPI_ClearFifo(base, (uint32_t)kQSPI_AllFifo);
/* Configure QSPI */
QSPI_Enable(base, false);
#if !defined(FSL_FEATURE_QSPI_CLOCK_CONTROL_EXTERNAL) || (!FSL_FEATURE_QSPI_CLOCK_CONTROL_EXTERNAL)
/* Set qspi clock source */
base->SOCCR = config->clockSource;
/* Read MCR value, mask SCLKCFG field */
val = base->MCR;
val &= ~QuadSPI_MCR_SCLKCFG_MASK;
/* To avoid the configured baudrate exceeds the expected baudrate value, which may possibly put the
QSPI work under unsupported frequency, set the divider higher when there is reminder, use ceiling
operation, ceiling(a/b) = (a-1)/b + 1. */
val |= QuadSPI_MCR_SCLKCFG((srcClock_Hz - 1U) / config->baudRate);
base->MCR = val;
#endif /* FSL_FEATURE_QSPI_CLOCK_CONTROL_EXTERNAL */
/* Set AHB buffer size and buffer master */
for (i = 0; i < (uint32_t)FSL_FEATURE_QSPI_AHB_BUFFER_COUNT; i++)
{
val = QuadSPI_BUF0CR_MSTRID(config->AHBbufferMaster[i]) | QuadSPI_BUF0CR_ADATSZ(config->AHBbufferSize[i] / 8U);
QSPI_AHB_BUFFER_REG(base, i) = val;
}
if (config->enableAHBbuffer3AllMaster)
{
base->BUF3CR |= QuadSPI_BUF3CR_ALLMST_MASK;
}
else
{
base->BUF3CR &= ~QuadSPI_BUF3CR_ALLMST_MASK;
}
/* Set watermark */
base->RBCT &= ~QuadSPI_RBCT_WMRK_MASK;
base->RBCT |= QuadSPI_RBCT_WMRK((uint32_t)config->rxWatermark - 1U);
#if !defined(FSL_FEATURE_QSPI_HAS_NO_TXDMA) || (!FSL_FEATURE_QSPI_HAS_NO_TXDMA)
base->TBCT &= ~QuadSPI_TBCT_WMRK_MASK;
base->TBCT |= QuadSPI_TBCT_WMRK((uint32_t)config->txWatermark - 1U);
#endif /* FSL_FEATURE_QSPI_HAS_NO_TXDMA */
/* Enable QSPI module */
if (config->enableQspi)
{
QSPI_Enable(base, true);
}
}
/*!
* brief Gets default settings for QSPI.
*
* param config QSPI configuration structure.
*/
void QSPI_GetDefaultQspiConfig(qspi_config_t *config)
{
/* Initializes the configure structure to zero. */
(void)memset(config, 0, sizeof(*config));
config->clockSource = 2U;
config->baudRate = 24000000U;
config->AHBbufferMaster[0] = 0xE;
config->AHBbufferMaster[1] = 0xE;
config->AHBbufferMaster[2] = 0xE;
config->enableAHBbuffer3AllMaster = true;
config->txWatermark = 8U;
config->rxWatermark = 8U;
config->enableQspi = true;
}
/*!
* brief Deinitializes the QSPI module.
*
* Clears the QSPI state and QSPI module registers.
* param base Pointer to QuadSPI Type.
*/
void QSPI_Deinit(QuadSPI_Type *base)
{
QSPI_Enable(base, false);
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
CLOCK_DisableClock(s_qspiClock[QSPI_GetInstance(base)]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
}
/*!
* brief Configures the serial flash parameter.
*
* This function configures the serial flash relevant parameters, such as the size, command, and so on.
* The flash configuration value cannot have a default value. The user needs to configure it according to the
* QSPI features.
*
* param base Pointer to QuadSPI Type.
* param config Flash configuration parameters.
*/
void QSPI_SetFlashConfig(QuadSPI_Type *base, qspi_flash_config_t *config)
{
uint32_t address = FSL_FEATURE_QSPI_AMBA_BASE + config->flashA1Size;
uint32_t val = 0;
uint32_t i = 0;
/* Disable module */
QSPI_Enable(base, false);
/* Config the serial flash size */
base->SFA1AD = address;
address += config->flashA2Size;
base->SFA2AD = address;
#if defined(FSL_FEATURE_QSPI_SUPPORT_PARALLEL_MODE) && (FSL_FEATURE_QSPI_SUPPORT_PARALLEL_MODE)
address += config->flashB1Size;
base->SFB1AD = address;
address += config->flashB2Size;
base->SFB2AD = address;
#endif /* FSL_FEATURE_QSPI_SUPPORT_PARALLEL_MODE */
#if !defined(FSL_FEATURE_QSPI_HAS_NO_SFACR) || (!FSL_FEATURE_QSPI_HAS_NO_SFACR)
/* Set Word Addressable feature */
val = QuadSPI_SFACR_WA(config->enableWordAddress) | QuadSPI_SFACR_CAS(config->cloumnspace);
base->SFACR = val;
#endif /* FSL_FEATURE_QSPI_HAS_NO_SFACR */
/* Config look up table */
base->LUTKEY = 0x5AF05AF0U;
base->LCKCR = 0x2U;
for (i = 0; i < (uint32_t)FSL_FEATURE_QSPI_LUT_DEPTH; i++)
{
base->LUT[i] = config->lookuptable[i];
}
base->LUTKEY = 0x5AF05AF0U;
base->LCKCR = 0x1U;
#if !defined(FSL_FEATURE_QSPI_HAS_NO_TDH) || (!FSL_FEATURE_QSPI_HAS_NO_TDH)
/* Config flash timing */
val = QuadSPI_FLSHCR_TCSS(config->CSHoldTime) | QuadSPI_FLSHCR_TDH(config->dataHoldTime) |
QuadSPI_FLSHCR_TCSH(config->CSSetupTime);
#else
val = QuadSPI_FLSHCR_TCSS(config->CSHoldTime) | QuadSPI_FLSHCR_TCSH(config->CSSetupTime);
#endif /* FSL_FEATURE_QSPI_HAS_NO_TDH */
base->FLSHCR = val;
/* Set flash endianness */
base->MCR &= ~QuadSPI_MCR_END_CFG_MASK;
base->MCR |= QuadSPI_MCR_END_CFG(config->endian);
/* Enable QSPI again */
QSPI_Enable(base, true);
}
#if (!defined(FSL_FEATURE_QSPI_HAS_NO_SOCCR_REG)) || !FSL_FEATURE_QSPI_HAS_NO_SOCCR_REG
/*!
* @brief Configures the serial flash DQS parameter.
*
* This function configures the serial flash DQS relevant parameters, such as the delay chain tap number, .
* DQS shift phase, whether need to inverse and the rxc sample clock selection.
*
* @param base Pointer to QuadSPI Type.
* @param config Dqs configuration parameters.
*/
void QSPI_SetDqsConfig(QuadSPI_Type *base, qspi_dqs_config_t *config)
{
uint32_t soccrVal;
uint32_t mcrVal;
/* Disable module */
QSPI_Enable(base, false);
mcrVal = base->MCR;
mcrVal &= ~(QuadSPI_MCR_DQS_EN_MASK | QuadSPI_MCR_DQS_LAT_EN_MASK);
/* Enable DQS. */
mcrVal |= QuadSPI_MCR_DQS_EN_MASK;
/* Configure DQS phase, inverse and loopback atrribute */
soccrVal = base->SOCCR;
soccrVal &=
~(QuadSPI_SOCCR_DQS_LOOPBACK_EN_MASK | QuadSPI_SOCCR_DQS_LOOPBACK_FROM_PAD_MASK |
QuadSPI_SOCCR_DQS_PHASE_SEL_MASK | QuadSPI_SOCCR_DQS_INV_EN_MASK | QuadSPI_SOCCR_DQS_IFA_DELAY_CHAIN_SEL_MASK
#if defined(QuadSPI_SOCCR_DQS_IFB_DELAY_CHAIN_SEL_MASK)
| QuadSPI_SOCCR_DQS_IFB_DELAY_CHAIN_SEL_MASK
#endif
);
soccrVal |= QuadSPI_SOCCR_DQS_PHASE_SEL(config->shift);
switch (config->rxSampleClock)
{
case kQSPI_ReadSampleClkInternalLoopback:
soccrVal |= QuadSPI_SOCCR_DQS_LOOPBACK_EN_MASK;
break;
case kQSPI_ReadSampleClkExternalInputFromDqsPad:
mcrVal |= QuadSPI_MCR_DQS_LAT_EN_MASK;
break;
case kQSPI_ReadSampleClkLoopbackFromDqsPad:
soccrVal |= QuadSPI_SOCCR_DQS_LOOPBACK_FROM_PAD_MASK;
break;
default:
assert(false);
break;
}
soccrVal |= (QuadSPI_SOCCR_DQS_INV_EN(config->enableDQSClkInverse) |
QuadSPI_SOCCR_DQS_IFA_DELAY_CHAIN_SEL(config->portADelayTapNum)
#if defined(QuadSPI_SOCCR_DQS_IFB_DELAY_CHAIN_SEL_MASK)
| QuadSPI_SOCCR_DQS_IFB_DELAY_CHAIN_SEL(config->portBDelayTapNum)
#endif
);
base->MCR = mcrVal;
base->SOCCR = soccrVal;
/* Enable QSPI again */
QSPI_Enable(base, true);
}
#endif /* FSL_FEATURE_QSPI_HAS_NO_SOCCR_REG */
/*!
* brief Software reset for the QSPI logic.
*
* This function sets the software reset flags for both AHB and buffer domain and
* resets both AHB buffer and also IP FIFOs.
*
* param base Pointer to QuadSPI Type.
*/
void QSPI_SoftwareReset(QuadSPI_Type *base)
{
uint32_t i = 0;
/* Reset AHB domain and buffer domian */
base->MCR |= (QuadSPI_MCR_SWRSTHD_MASK | QuadSPI_MCR_SWRSTSD_MASK);
/* Wait several time for the reset to finish, this method came from IC team */
for (i = 0; i < 100U; i++)
{
__NOP();
}
/* Disable QSPI module */
QSPI_Enable(base, false);
/* Clear the reset flags */
base->MCR &= ~(QuadSPI_MCR_SWRSTHD_MASK | QuadSPI_MCR_SWRSTSD_MASK);
/* Enable QSPI module */
QSPI_Enable(base, true);
}
/*!
* brief Gets the Rx data register address used for DMA operation.
*
* This function returns the Rx data register address or Rx buffer address
* according to the Rx read area settings.
*
* param base Pointer to QuadSPI Type.
* return QSPI Rx data register address.
*/
uint32_t QSPI_GetRxDataRegisterAddress(QuadSPI_Type *base)
{
/* From RDBR */
if (0U != (base->RBCT & QuadSPI_RBCT_RXBRD_MASK))
{
return (uint32_t)(&(base->RBDR[0]));
}
else
{
/* From ARDB */
return FSL_FEATURE_QSPI_ARDB_BASE;
}
}
/*! brief Executes IP commands located in LUT table.
*
* param base Pointer to QuadSPI Type.
* param index IP command located in which LUT table index.
*/
void QSPI_ExecuteIPCommand(QuadSPI_Type *base, uint32_t index)
{
while (0U != (QSPI_GetStatusFlags(base) & ((uint32_t)kQSPI_Busy | (uint32_t)kQSPI_IPAccess)))
{
}
QSPI_ClearCommandSequence(base, kQSPI_IPSeq);
/* Write the seqid bit */
base->IPCR = ((base->IPCR & (~QuadSPI_IPCR_SEQID_MASK)) | QuadSPI_IPCR_SEQID(index / 4U));
}
/*! brief Executes AHB commands located in LUT table.
*
* param base Pointer to QuadSPI Type.
* param index AHB command located in which LUT table index.
*/
void QSPI_ExecuteAHBCommand(QuadSPI_Type *base, uint32_t index)
{
while (0U != (QSPI_GetStatusFlags(base) & ((uint32_t)kQSPI_Busy | (uint32_t)kQSPI_AHBAccess)))
{
}
QSPI_ClearCommandSequence(base, kQSPI_BufferSeq);
base->BFGENCR = ((base->BFGENCR & (~QuadSPI_BFGENCR_SEQID_MASK)) | QuadSPI_BFGENCR_SEQID(index / 4U));
}
/*! brief Updates the LUT table.
*
* param base Pointer to QuadSPI Type.
* param index Which LUT index needs to be located. It should be an integer divided by 4.
* param cmd Command sequence array.
*/
void QSPI_UpdateLUT(QuadSPI_Type *base, uint32_t index, uint32_t *cmd)
{
uint8_t i = 0;
/* Unlock the LUT */
base->LUTKEY = 0x5AF05AF0U;
base->LCKCR = 0x2U;
/* Write data into LUT */
for (i = 0; i < 4U; i++)
{
base->LUT[index + i] = *cmd;
cmd++;
}
/* Lcok LUT again */
base->LUTKEY = 0x5AF05AF0U;
base->LCKCR = 0x1U;
}
#if defined(FSL_FEATURE_QSPI_SOCCR_HAS_CLR_LPCAC) && (FSL_FEATURE_QSPI_SOCCR_HAS_CLR_LPCAC)
/*! brief Clears the QSPI cache.
*
* param base Pointer to QuadSPI Type.
*/
void QSPI_ClearCache(QuadSPI_Type *base)
{
uint32_t soccrVal;
soccrVal = base->SOCCR;
/* Write 1 to clear cache. */
base->SOCCR = (soccrVal | QuadSPI_SOCCR_CLR_LPCAC_MASK);
/* Write 0 to after cache is cleared. */
base->SOCCR = (soccrVal & (~QuadSPI_SOCCR_CLR_LPCAC_MASK));
}
#endif
/*! brief Set the RX buffer readout area.
*
* This function can set the RX buffer readout, from AHB bus or IP Bus.
* param base QSPI base address.
* param area QSPI Rx buffer readout area. AHB bus buffer or IP bus buffer.
*/
void QSPI_SetReadDataArea(QuadSPI_Type *base, qspi_read_area_t area)
{
base->RBCT &= ~QuadSPI_RBCT_RXBRD_MASK;
base->RBCT |= QuadSPI_RBCT_RXBRD(area);
}
/*!
* brief Receives data from data FIFO.
*
* param base QSPI base pointer
* return The data in the FIFO.
*/
uint32_t QSPI_ReadData(QuadSPI_Type *base)
{
if (0U != (base->RBCT & QuadSPI_RBCT_RXBRD_MASK))
{
return base->RBDR[0];
}
else
{
/* Data from ARDB. */
return *((uint32_t *)FSL_FEATURE_QSPI_ARDB_BASE);
}
}
/*!
* brief Sends a buffer of data bytes using a blocking method.
* note This function blocks via polling until all bytes have been sent.
* param base QSPI base pointer
* param buffer The data bytes to send
* param size The number of data bytes to send
*/
void QSPI_WriteBlocking(QuadSPI_Type *base, uint32_t *buffer, size_t size)
{
assert(size >= 16U);
uint32_t i = 0;
for (i = 0; i < size / 4U; i++)
{
/* Check if the buffer is full */
while (0U != (QSPI_GetStatusFlags(base) & (uint32_t)kQSPI_TxBufferFull))
{
}
base->TBDR = *buffer++;
}
}
/*!
* brief Receives a buffer of data bytes using a blocking method.
* note This function blocks via polling until all bytes have been sent. Users shall notice that
* this receive size shall not bigger than 64 bytes. As this interface is used to read flash status registers.
* For flash contents read, please use AHB bus read, this is much more efficiency.
*
* param base QSPI base pointer
* param buffer The data bytes to send
* param size The number of data bytes to receive
*/
void QSPI_ReadBlocking(QuadSPI_Type *base, uint32_t *buffer, size_t size)
{
uint32_t i = 0;
uint32_t j = 0;
uint32_t temp = 0;
uint32_t level = (base->RBCT & QuadSPI_RBCT_WMRK_MASK) + 1U;
while (i < size / 4U)
{
/* Check if there is data */
if ((size / 4U - i) < level)
{
do
{
temp = (base->RBSR & QuadSPI_RBSR_RDBFL_MASK) >> QuadSPI_RBSR_RDBFL_SHIFT;
} while (0U == temp);
}
else
{
while ((QSPI_GetStatusFlags(base) & (uint32_t)kQSPI_RxWatermark) == 0U)
{
}
}
level = (level < (size / 4U - i)) ? level : (size / 4U - i);
/* Data from RBDR */
if (0U != (base->RBCT & QuadSPI_RBCT_RXBRD_MASK))
{
for (j = 0; j < level; j++)
{
buffer[i + j] = base->RBDR[j];
}
}
else
{
/* Data from ARDB. */
for (j = 0; j < level; j++)
{
buffer[i + j] = ((uint32_t *)FSL_FEATURE_QSPI_ARDB_BASE)[j];
}
}
i += level;
/* Clear the Buffer */
QSPI_ClearErrorFlag(base, (uint32_t)kQSPI_RxBufferDrain);
}
}