/* ---------------------------------------------------------------------------- */ /* Atmel Microcontroller Software Support */ /* SAM Software Package License */ /* ---------------------------------------------------------------------------- */ /* Copyright (c) 2015, Atmel Corporation */ /* */ /* All rights reserved. */ /* */ /* Redistribution and use in source and binary forms, with or without */ /* modification, are permitted provided that the following condition is 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 #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)); } static void do_copy(uint8_t *dst, const uint8_t *src, size_t n, bool aligned) { if (aligned) { while (n > 3) { *(uint32_t *)dst = *(uint32_t *)src; dst += 4; src += 4; n -= 4; } } while (n > 0) { *dst = *src; ++dst; ++src; --n; } } static void copy_to_io(void *dst, const void *src, size_t n) { do_copy(dst, src, n, ((uintptr_t)dst) % 4 == 0); } static void copy_from_io(void *dst, const void *src, size_t n) { do_copy(dst, src, n, ((uintptr_t)src) % 4 == 0); } /*---------------------------------------------------------------------------- * 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, (QspiStatus_t)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;) { if (QSPI_GetStatus(pQspi, IsTxEmpty)) { QSPI_WriteSPI(pQspi, (uint16_t)*pwData); pwData += Addr_Inc; NumOfBytesWrite++; 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; void *pQspiMem = (void *)(QSPIMEM_ADDR | pFrame->Addr); QspiBuffer_t pBuffer = pQspid->qspiBuffer; assert(((ReadWrite > CmdAccess) && (ReadWrite <= WriteAccess)) ? true : false); if (ReadWrite == WriteAccess) { copy_to_io(pQspiMem, pBuffer.pDataTx , pBuffer.TxDataSize); } else { copy_from_io(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; }