blob: 3916982d42ba9890c5dea5f15a270de3b9b417f3 [file] [log] [blame]
/* ----------------------------------------------------------------------------
* SAM Software Package License
* ----------------------------------------------------------------------------
* Copyright (c) 2014, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
/** \addtogroup qspi_module Working with QSPI
* \ingroup peripherals_module
* The QSPI driver provides the interface to configure and use the QSPI
* peripheral.
*
* The Serial Peripheral Interface (QSPI) circuit is a synchronous serial
* data link that provides communication with external devices in Master
* or Slave Mode.
*
* To use the QSPI, the user has to follow these few steps:
* -# Enable the QSPI pins required by the application (see pio.h).
* -# Configure the QSPI using the \ref QSPI_Configure(). This enables the
* peripheral clock. The mode register is loaded with the given value.
* -# Configure all the necessary chip selects with \ref QSPI_ConfigureNPCS().
* -# Enable the QSPI by calling \ref QSPI_Enable().
* -# Send/receive data using \ref QSPI_Write() and \ref QSPI_Read(). Note that
* \ref QSPI_Read()
* must be called after \ref QSPI_Write() to retrieve the last value read.
* -# Send/receive data using the PDC with the \ref QSPI_WriteBuffer() and
* \ref QSPI_ReadBuffer() functions.
* -# Disable the QSPI by calling \ref QSPI_Disable().
*
* For more accurate information, please look at the QSPI section of the
* Datasheet.
*
* Related files :\n
* \ref qspi.c\n
* \ref qspi.h.\n
*/
/*@{*/
/*@}*/
/**
* \file
*
* Implementation of Serial Peripheral Interface (QSPI) controller.
*
*/
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "chip.h"
#include "stdlib.h"
#include "string.h"
#include <stdint.h>
#define SCRAMBLE_KEY 0x0BADDEAD
/*----------------------------------------------------------------------------
* Internal functions
*----------------------------------------------------------------------------*/
/**
* \brief Configure QSPI/SPI mode
*
* \param pQspi Pointer to a Qspi instance.
*/
__STATIC_INLINE void QSPI_ConfigureMode( Qspi *pQspi, uint8_t dMode )
{
assert(pQspi);
pQspi->QSPI_MR = dMode ;
}
/**
* \brief Configure mode register of QSPI
*
* \param pQspi Pointer to a Qspi instance.
*/
__STATIC_INLINE void QSPI_Configure( Qspi *pQspi, uint32_t dwConfiguration )
{
assert(pQspi);
pQspi->QSPI_MR |= dwConfiguration ;
}
/**
* \brief Configures a instruction address for QSPI in QSPI mode
*
* \param pQspi Pointer to a Qspi instance.
* \param dwAddr Instruction Address
*/
__STATIC_INLINE void QSPI_SetInstAddr( Qspi *pQspi,uint32_t dwAddr )
{
assert(pQspi);
pQspi->QSPI_IAR = dwAddr ;
}
/**
* \brief Configures instruction register with a given command for QSPI
*
* \param pQspi Pointer to a Qspi instance.
* \param dwInst Instruction Code
* \param dwOpt Instruction Code option
*/
__STATIC_INLINE void QSPI_SetInst( Qspi *pQspi, uint8_t dwInst, uint8_t dwOpt )
{
assert(pQspi);
pQspi->QSPI_ICR = (dwInst | QSPI_ICR_OPT(dwOpt) );
}
/**
* \brief Configures instruction frame register of QSPI
*
* \param pQspi Pointer to a Qspi instance.
* \param pInstFrame Instruction Frame configuration
*/
__STATIC_INLINE void QSPI_SetInstFrame( Qspi *pQspi, QspiInstFrame_t *pInstFrame)
{
assert(pQspi);
pQspi->QSPI_IFR = pInstFrame->InstFrame.val;
}
/**
* \brief Reads the Instruction frame of QSPI
*
* \param pQspi Pointer to an Qspi instance.
*/
__STATIC_INLINE uint32_t QSPI_GetInstFrame( Qspi *pQspi )
{
assert(pQspi);
return pQspi->QSPI_IFR;
}
/**
* \brief Read QSPI RDR register for SPI mode
*
* \param pQspi Pointer to an Qspi instance.
*/
__STATIC_INLINE uint16_t QSPI_ReadSPI( Qspi *pQspi )
{
assert(pQspi);
while(!QSPI_GetStatus(pQspi, IsReceived));
return pQspi->QSPI_RDR;
}
/**
* \brief Write to QSPI Tx register in SPI mode
*
* \param pQspi Pointer to an Qspi instance.
* \param wData Data to transmit
*/
__STATIC_INLINE void QSPI_WriteSPI( Qspi *pQspi, uint16_t wData )
{
assert(pQspi);
/* Send data */
while(!QSPI_GetStatus(pQspi, IsTxEmpty));
pQspi->QSPI_TDR = wData ;
while(!QSPI_GetStatus(pQspi, IsTxSent));
}
/**
* \brief Configures QSPI scrambling with a given Key
*
* \param pQspi Pointer to an Qspi instance.
* \param wKey Key for scramble/unscramble
* \param EnableFlag Enable/disable scramble
* \param Random Add random value with given key
*/
__STATIC_INLINE void QSPI_ScrambleData( Qspi *pQspi, uint32_t wKey,
uint8_t EnableFlag, uint8_t Random )
{
assert(pQspi);
assert(EnableFlag < 2);
assert(Random < 2 );
if(EnableFlag) {
pQspi->QSPI_SKR = wKey;
}
pQspi->QSPI_SMR = ( EnableFlag | (Random << 1));
}
/*----------------------------------------------------------------------------
* Exported functions
*----------------------------------------------------------------------------*/
/**
* \brief Enables a QSPI peripheral.
*
* \param pQspi Pointer to a Qspi instance.
*/
void QSPI_Enable( Qspi *pQspi )
{
assert(pQspi);
pQspi->QSPI_CR = QSPI_CR_QSPIEN ;
while(!(pQspi->QSPI_SR & QSPI_SR_QSPIENS));
}
/**
* \brief Disables a QSPI peripheral.
*
* \param pQspi Pointer to a Qspi instance.
*/
void QSPI_Disable( Qspi *pQspi )
{
assert(pQspi);
pQspi->QSPI_CR = QSPI_CR_QSPIDIS ;
while(pQspi->QSPI_SR & QSPI_SR_QSPIENS);
}
/**
* \brief Resets a QSPI peripheral.
*
* \param pQspi Pointer to a Qspi instance.
*/
void QSPI_SwReset( Qspi *pQspi )
{
assert(pQspi);
pQspi->QSPI_CR = QSPI_CR_SWRST ;
}
/**
* \brief Enables one or more interrupt sources of a QSPI peripheral.
*
* \param pQspi Pointer to a Qspi instance.
* \param sources Bitwise OR of selected interrupt sources.
*/
QspidStatus_t QSPI_EnableIt( Qspi *pQspi, uint32_t dwSources )
{
assert(pQspi);
pQspi->QSPI_IER = dwSources ;
return QSPI_SUCCESS;
}
/**
* \brief Disables one or more interrupt sources of a QSPI peripheral.
*
* \param pQspi Pointer to a Qspi instance.
* \param sources Bitwise OR of selected interrupt sources.
*/
QspidStatus_t QSPI_DisableIt( Qspi *pQspi, uint32_t dwSources )
{
assert(pQspi);
pQspi->QSPI_IDR = dwSources ;
return QSPI_SUCCESS;
}
/**
* \brief Return the interrupt mask register.
*
* \return Qspi interrupt mask register.
*/
uint32_t QSPI_GetItMask( Qspi *pQspi )
{
assert(pQspi);
return (pQspi->QSPI_IMR) ;
}
/**
* \brief Returns enabled interrupt status
*
* \return Qspi interrupt mask register.
*/
uint32_t QSPI_GetEnabledItStatus( Qspi *pQspi )
{
assert(pQspi);
return (pQspi->QSPI_IMR & QSPI_GetStatus(pQspi, 0xFFFFFFFF) ) ;
}
/**
* \brief Get the current status register of the given QSPI peripheral.
* \note This resets the internal value of the status register, so further
* read may yield different values.
* \param pQspi Pointer to a Qspi instance.
* \param rStatus Compare status with given status bit
* \return QSPI status register.
*/
uint32_t QSPI_GetStatus( Qspi *pQspi, const QspiStatus_t rStatus )
{
assert(pQspi);
return (pQspi->QSPI_SR & rStatus) ;
}
/**
* \brief Configures peripheral clock of a QSPI/SPI peripheral.
*
* \param pQspi Pointer to an Qspi instance.
* \param dwConfiguration Desired clock configuration.
*/
void QSPI_ConfigureClock( Qspi *pQspi, QspiClockMode_t ClockMode, uint32_t dwClockCfg )
{
assert(pQspi);
pQspi->QSPI_SCR = ClockMode ;
pQspi->QSPI_SCR |= dwClockCfg ;
}
/**
* \brief Configures QSPI/SPI
*
* \param pQspi Pointer to an Qspi instance.
* \param Mode Mode for QSPI or SPI
* \param dwConfiguration Config of SPI or QSPI mode
*/
QspidStatus_t QSPI_ConfigureInterface( Qspid_t *pQspid, QspiMode_t Mode,
uint32_t dwConfiguration)
{
pQspid->pQspiHw = QSPI;
pQspid->qspiId = ID_QSPI;
QSPI_Disable(pQspid->pQspiHw);
QSPI_SwReset(pQspid->pQspiHw);
QSPI_ConfigureMode(pQspid->pQspiHw, Mode);
QSPI_Configure(pQspid->pQspiHw, dwConfiguration);
return QSPI_SUCCESS;
}
/**
* \brief Ends ongoing transfer by releasing CS of QSPI peripheral.
*
* \param pQspi Pointer to an Qspi instance.
*/
QspidStatus_t QSPI_EndTransfer( Qspi *pQspi )
{
assert(pQspi);
while(!QSPI_GetStatus(pQspi, IsTxEmpty));
pQspi->QSPI_CR = QSPI_CR_LASTXFER ;
return QSPI_SUCCESS;
}
/*----------------------------------------------------------------------------
* SPI functions
*----------------------------------------------------------------------------*/
/**
* \brief Reads the data received by a SPI peripheral. This
* method must be called after a successful SPI_Write call.
*
* \param pQspid Pointer to a Qspi instance.
* \param pData Buffer to put read value
* \return Qspi status
*/
QspidStatus_t QSPI_SingleReadSPI( Qspid_t *pQspid, uint16_t* const pData )
{
QspidStatus_t Status = QSPI_UNKNOWN_ERROR;
Qspi *pQspi = pQspid->pQspiHw;
uint32_t NumOfAttempt = 0;
uint16_t Dummy= 0xFF;
for(; ;) {
if( QSPI_GetStatus(pQspi, IsReceived)) {
*pData = QSPI_ReadSPI(pQspi) ;
QSPI_WriteSPI(pQspi, Dummy);
*pData = QSPI_ReadSPI(pQspi) ;
NumOfAttempt = 0;
Status = QSPI_SUCCESS;
} else {
if(NumOfAttempt > 0xFFFF) {
Status = QSPI_READ_ERROR;
TRACE_ERROR(" SPI Read Error \n\r");
break;
} else {
Status = QSPI_READ_ERROR;
NumOfAttempt++;
}
}
}
return Status;
}
/**
* \brief Reads multiple data received by a SPI peripheral. This
* method must be called after a successful SPI_Write call.
*
* \param pQspid Pointer to a Qspi instance.
* \param pData Pointer to read buffer
* \param NumOfBytes Num of bytes to read
*
* \return Qspi status
*/
QspidStatus_t QSPI_MultiReadSPI( Qspid_t *pQspid, uint16_t* const pData,
uint32_t NumOfBytes )
{
QspidStatus_t Status = QSPI_UNKNOWN_ERROR;
Qspi *pQspi = pQspid->pQspiHw;
uint32_t NumOfBytesRead = 0;
uint32_t NumOfAttempt = 0;
uint8_t *pwData = (uint8_t *)pData;
uint16_t Dummy=0xFF;
/* Dummy read and write to discard first bytes recvd and start
receiving new data*/
Dummy = QSPI_ReadSPI(pQspi) ;
QSPI_WriteSPI(pQspi, Dummy);
for(; NumOfBytesRead < NumOfBytes;) {
if( QSPI_GetStatus(pQspi, IsTxSent)) {
*pwData= QSPI_ReadSPI(pQspi) ;
if(pQspi->QSPI_MR & QSPI_MR_NBBITS_Msk) {
pwData += sizeof(uint16_t);
} else {
pwData += sizeof(uint8_t);
}
NumOfBytesRead++;
NumOfAttempt = 0;
Status = QSPI_SUCCESS;
QSPI_WriteSPI(pQspi, Dummy);
} else {
if(NumOfAttempt > 0xFFFF) {
Status = QSPI_READ_ERROR;
TRACE_ERROR(" SPI MultiRead Error \n\r");
break;
} else {
Status = QSPI_READ_ERROR;
NumOfAttempt++;
}
}
}
return Status;
}
/**
* \brief Sends a single data through a SPI peripheral.
*
* \param pQspid Pointer to a Qspi instance.
* \param pData Pointer to Tx data
*
* \return Qspi status
*/
QspidStatus_t QSPI_SingleWriteSPI( Qspid_t *pQspid, uint16_t const *pData )
{
QspidStatus_t Status = QSPI_UNKNOWN_ERROR;
Qspi *pQspi = pQspid->pQspiHw;
uint32_t NumOfAttempt = 0;
for(;;) {
if( QSPI_GetStatus(pQspi, IsTxSent)) {
QSPI_WriteSPI(pQspi, *pData);
NumOfAttempt = 0;
Status = QSPI_SUCCESS;
break;
} else {
Status = QSPI_BUSY_SENDING;
NumOfAttempt++;
if(NumOfAttempt > 0xFFFF) {
Status = QSPI_WRITE_ERROR;
TRACE_ERROR(" SPI Write Error \n\r");
break;
}
}
}
return Status;
}
/**
* \brief Sends multiple data through a SPI peripheral.
*
* \param pQspid Pointer to a Qspi instance.
* \param pData Pointer to a Tx buffer
* \param NumOfBytes Num of data to send.
*/
QspidStatus_t QSPI_MultiWriteSPI( Qspid_t *pQspid, uint16_t const *pData,
uint32_t NumOfBytes )
{
QspidStatus_t Status = QSPI_UNKNOWN_ERROR;
Qspi *pQspi = pQspid->pQspiHw;
uint32_t NumOfBytesWrite = 0;
uint32_t NumOfAttempt = 0;
uint8_t *pwData = (uint8_t *)pData;
uint8_t Addr_Inc = 0;
if(pQspi->QSPI_MR & QSPI_MR_NBBITS_Msk) {
Addr_Inc = sizeof(uint16_t);
} else {
Addr_Inc = sizeof(uint8_t);
}
for(; NumOfBytesWrite < NumOfBytes; NumOfBytesWrite++) {
if( QSPI_GetStatus(pQspi, IsTxEmpty)) {
QSPI_WriteSPI(pQspi, (uint16_t )*pwData);
pwData += Addr_Inc;
NumOfAttempt = 0;
Status = QSPI_SUCCESS;
} else {
Status = QSPI_BUSY_SENDING;
NumOfAttempt++;
if(NumOfAttempt > 0xFFFF) {
Status = QSPI_WRITE_ERROR;
TRACE_ERROR(" SPI Multi Write Error \n\r");
break;
}
}
}
return Status;
}
/*----------------------------------------------------------------------------
* QSPI functions
*----------------------------------------------------------------------------*/
/**
* \brief Send an instruction over QSPI (oly a flash command no data)
*
* \param pQspi Pointer to an Qspi instance.
* \param KeepCfg To keep Instruction fram value or restes to zero
*
* \return Returns 1 if At least one instruction end has been detected since
* the last read of QSPI_SR.; otherwise
* returns 0.
*/
QspidStatus_t QSPI_SendCommand( Qspid_t *pQspid, uint8_t const KeepCfg)
{
QspiInstFrame_t* const pFrame = pQspid->pQspiFrame;
QspiMemCmd_t pCommand = pQspid->qspiCommand;
QspidStatus_t Status = QSPI_UNKNOWN_ERROR;
if( pFrame->InstFrame.bm.bAddrEn)
{
QSPI_SetInstAddr(pQspid->pQspiHw, pFrame->Addr);
}
QSPI_SetInst(pQspid->pQspiHw, (pCommand.Instruction & 0xFF),
( (pCommand.Option >> QSPI_ICR_OPT_Pos) & 0xFF));
QSPI_SetInstFrame(pQspid->pQspiHw, pFrame );
memory_sync();
while(!(pQspid->pQspiHw->QSPI_SR & QSPI_SR_INSTRE));
// poll CR reg to know status if instruction has end
if(!KeepCfg) {
pFrame->InstFrame.val = 0;
}
return Status;
}
/**
* \brief Send instruction over QSPI with data
*
* \param pQspi Pointer to an Qspi instance.
* \param KeepCfg To keep Instruction fram value or restes to zero
*
* \return Returns 1 if At least one instruction end has been detected
* since the last read of QSPI_SR.; otherwise returns 0.
*/
QspidStatus_t QSPI_SendCommandWithData( Qspid_t *pQspid, uint8_t const KeepCfg)
{
QspiInstFrame_t* const pFrame = pQspid->pQspiFrame;
QspiMemCmd_t pCommand = pQspid->qspiCommand;
QspiBuffer_t pBuffer = pQspid->qspiBuffer;
uint32_t *pQspiBuffer = (uint32_t *)QSPIMEM_ADDR;
QspidStatus_t Status = QSPI_UNKNOWN_ERROR;
//assert(pBuffer.pDataRx);
assert(pBuffer.pDataTx);
QSPI_SetInst(pQspid->pQspiHw, (pCommand.Instruction & 0xFF), (pCommand.Option & 0xFF) );
QSPI_SetInstFrame(pQspid->pQspiHw, pFrame );
QSPI_GetInstFrame(pQspid->pQspiHw);
// to synchronize system bus accesses
if(!KeepCfg) {
pFrame->InstFrame.val = 0;
}
memcpy(pQspiBuffer ,pBuffer.pDataTx , pBuffer.TxDataSize );
memory_sync();
QSPI_EndTransfer(pQspid->pQspiHw ) ;
// End transmission after all data has been sent
while(!(pQspid->pQspiHw->QSPI_SR & QSPI_SR_INSTRE));
// poll CR reg to know status if instruction has end
return Status;
}
/**
* \brief Send instruction over QSPI to read data
*
* \param pQspi Pointer to an Qspi instance.
* \param KeepCfg To keep Instruction from value or resets to zero
*
* \return Returns 1 if At least one instruction end has been detected
* since the last read of QSPI_SR.; otherwise returns 0.
*/
QspidStatus_t QSPI_ReadCommand( Qspid_t *pQspid, uint8_t const KeepCfg)
{
QspiInstFrame_t* const pFrame = pQspid->pQspiFrame;
QspiMemCmd_t pCommand = pQspid->qspiCommand;
QspiBuffer_t pBuffer = pQspid->qspiBuffer;
uint32_t *pQspiBuffer = (uint32_t *)QSPIMEM_ADDR;
QspidStatus_t Status = QSPI_UNKNOWN_ERROR;
assert(pBuffer.pDataRx);
QSPI_SetInst(pQspid->pQspiHw, (pCommand.Instruction & 0xFF),
(pCommand.Option & 0xFF) );
QSPI_SetInstFrame(pQspid->pQspiHw, pFrame );
QSPI_GetInstFrame(pQspid->pQspiHw);
// to synchronize system bus accesses
if(!KeepCfg) {
pFrame->InstFrame.val = 0;
}
memcpy(pBuffer.pDataRx , pQspiBuffer, pBuffer.RxDataSize );
memory_sync();
QSPI_EndTransfer(pQspid->pQspiHw ) ;
// End transmission after all data has been sent
while(!(pQspid->pQspiHw->QSPI_SR & QSPI_SR_INSTRE));
// poll CR reg to know status if instruction has end
return Status;
}
/**
* \brief Sends an instruction over QSPI and configures other related address
* like Addr , Frame and synchronise bus access before data read or write
*
* \param pQspi Pointer to an Qspi instance.
* \param KeepCfg To keep Instruction from value or resets to zero
* \param ScrambleFlag Enable or disable scramble on QSPI
*
* \return Returns 1 if At least one instruction end has been detected since
* the last read of QSPI_SR.; otherwise returns 0.
*/
QspidStatus_t QSPI_EnableMemAccess( Qspid_t *pQspid, uint8_t const KeepCfg,
uint8_t ScrambleFlag)
{
QspiInstFrame_t* const pFrame = pQspid->pQspiFrame;
QspiMemCmd_t pCommand = pQspid->qspiCommand;
QspidStatus_t Status = QSPI_UNKNOWN_ERROR;
QSPI_SetInst(pQspid->pQspiHw, (pCommand.Instruction & 0xFF),
(pCommand.Option & 0xFF) );
if(ScrambleFlag) {
QSPI_ScrambleData(pQspid->pQspiHw, SCRAMBLE_KEY, ScrambleFlag, 1);
}
QSPI_SetInstFrame(pQspid->pQspiHw, pFrame );
QSPI_GetInstFrame(pQspid->pQspiHw);
// to synchronize system bus accesses
if(!KeepCfg) {
pFrame->InstFrame.val = 0;
}
Status = QSPI_SUCCESS;
return Status;
}
/**
* \brief Writes or reads the QSPI memory (0x80000000) to transmit or
* receive data from Flash memory
* \param pQspi Pointer to an Qspi instance.
* \param ReadWrite Flag to indicate read/write QSPI memory access
*
* \return Returns 1 if At least one instruction end has been detected since
* the last read of QSPI_SR.; otherwise returns 0.
*/
QspidStatus_t QSPI_ReadWriteMem( Qspid_t *pQspid, Access_t const ReadWrite)
{
QspidStatus_t Status = QSPI_UNKNOWN_ERROR;
QspiInstFrame_t* const pFrame = pQspid->pQspiFrame;
uint32_t *pQspiMem = (uint32_t *)( QSPIMEM_ADDR | pFrame->Addr);
QspiBuffer_t pBuffer = pQspid->qspiBuffer;
assert( ( (ReadWrite > CmdAccess) && (ReadWrite <= WriteAccess) ) ? true: false );
if (ReadWrite == WriteAccess) {
memcpy(pQspiMem, pBuffer.pDataTx , pBuffer.TxDataSize );
} else {
memcpy(pBuffer.pDataRx, pQspiMem, pBuffer.RxDataSize );
}
memory_sync();
QSPI_EndTransfer(pQspid->pQspiHw ) ;
// End transmission after all data has been sent
while(!(pQspid->pQspiHw->QSPI_SR & QSPI_SR_INSTRE));
// poll CR reg to know status if instruction has end
Status = QSPI_SUCCESS;
return Status;
}