/**************************************************************************** * arch/arm/src/samv7/sam_qspi.c * * Copyright (C) 2015 Gregory Nutt. All rights reserved. * Authors: Gregory Nutt * * 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 NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * 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 OWNER 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. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "up_internal.h" #include "up_arch.h" #include "cache.h" #include "sam_gpio.h" #include "sam_xdmac.h" #include "sam_periphclks.h" #include "sam_qspi.h" #include "chip/sam_pmc.h" #include "chip/sam_xdmac.h" #include "chip/sam_qspi.h" #include "chip/sam_pinmap.h" #ifdef CONFIG_SAMV7_QSPI /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Configuration ************************************************************/ #ifndef CONFIG_SAMV7_QSPI_DLYBS # define CONFIG_SAMV7_QSPI_DLYBS 0 #endif #ifndef CONFIG_SAMV7_QSPI_DLYBCT # define CONFIG_SAMV7_QSPI_DLYBCT 0 #endif /* When QSPI DMA is enabled, small DMA transfers will still be performed by * polling logic. But we need a threshold value to determine what is small. * That value is provided by CONFIG_SAMV7_QSPI_DMATHRESHOLD. */ #ifndef CONFIG_SAMV7_QSPI_DMATHRESHOLD # define CONFIG_SAMV7_QSPI_DMATHRESHOLD 4 #endif #ifndef CONFIG_SAMV7_XDMAC # undef CONFIG_SAMV7_QSPI_DMA #endif #ifdef CONFIG_SAMV7_QSPI_DMA # define SAMV7_QSPI0_DMA true #endif #ifndef CONFIG_SAMV7_QSPI_DMA # undef CONFIG_SAMV7_QSPI_DMADEBUG #endif /* QSPI interrupts are not used */ #undef QSPI_USE_INTERRUPTS /* Clocking *****************************************************************/ /* The QSPI Baud rate clock is generated by dividing the peripheral clock by * a value between 1 and 255 */ #define SAM_QSPI_CLOCK BOARD_MCK_FREQUENCY /* Frequency of the main clock */ /* DMA timeout. The value is not critical; we just don't want the system to * hang in the event that a DMA does not finish. This is set to */ #define DMA_TIMEOUT_MS (800) #define DMA_TIMEOUT_TICKS MSEC2TICK(DMA_TIMEOUT_MS) /* QSPI memory synchronization */ #define MEMORY_SYNC() do { ARM_DSB(); ARM_ISB(); } while (0) /* The SAMV7x QSPI driver insists that transfers be performed in multiples * of 32-bits. * * REVISIT: Why is this done? This logic is here only because it is also * done this way in the Atmel sample code. But I have no idea why. */ #define ALIGN_SHIFT 2 #define ALIGN_MASK 3 #define ALIGN_UP(n) (((n)+ALIGN_MASK) & ~ALIGN_MASK) #define IS_ALIGNED(n) (((uint32_t)(n) & ALIGN_MASK) == 0) /* Debug *******************************************************************/ /* Check if QSPI debug is enabled (non-standard.. no support in * include/debug.h */ #ifndef CONFIG_DEBUG # undef CONFIG_DEBUG_VERBOSE # undef CONFIG_DEBUG_SPI # undef CONFIG_SAMV7_QSPI_DMADEBUG # undef CONFIG_SAMV7_QSPI_REGDEBUG #endif #ifndef CONFIG_DEBUG_DMA # undef CONFIG_SAMV7_QSPI_DMADEBUG #endif #ifdef CONFIG_DEBUG_SPI # define qspidbg lldbg # ifdef CONFIG_DEBUG_VERBOSE # define qspivdbg lldbg # else # define qspivdbg(x...) # endif #else # define qspidbg(x...) # define qspivdbg(x...) #endif #define DMA_INITIAL 0 #define DMA_AFTER_SETUP 1 #define DMA_AFTER_START 2 #define DMA_CALLBACK 3 #define DMA_TIMEOUT 3 #define DMA_END_TRANSFER 4 #define DMA_NSAMPLES 5 /**************************************************************************** * Private Types ****************************************************************************/ /* The state of the QSPI controller. * * NOTE: Currently the SAMV7 supports only a single QSPI peripheral. Logic * here is designed to support multiple QSPI peripherals. */ struct sam_qspidev_s { struct qspi_dev_s qspi; /* Externally visible part of the QSPI interface */ #ifdef QSPI_USE_INTERRUPTS xcpt_t handler; /* Interrupt handler */ #endif uint32_t base; /* QSPI controller register base address */ uint32_t frequency; /* Requested clock frequency */ uint32_t actual; /* Actual clock frequency */ uint8_t mode; /* Mode 0,1,2,3 */ uint8_t nbits; /* Width of word in bits (8 to 16) */ uint8_t intf; /* QSPI controller number (0) */ #ifdef QSPI_USE_INTERRUPTS uint8_t irq; /* Interrupt number */ #endif bool initialized; /* TRUE: Controller has been initialized */ sem_t exclsem; /* Assures mutually exclusive access to QSPI */ #ifdef CONFIG_SAMV7_QSPI_DMA bool candma; /* DMA is supported */ uint8_t rxintf; /* RX hardware interface number */ uint8_t txintf; /* TX hardware interface number */ sem_t dmawait; /* Used to wait for DMA completion */ WDOG_ID dmadog; /* Watchdog that handles DMA timeouts */ int result; /* DMA result */ DMA_HANDLE dmach; /* QSPI DMA handle */ #endif /* Debug stuff */ #ifdef CONFIG_SAMV7_QSPI_DMADEBUG struct sam_dmaregs_s dmaregs[DMA_NSAMPLES]; #endif #ifdef CONFIG_SAMV7_QSPI_REGDEBUG bool wrlast; /* Last was a write */ uint32_t addresslast; /* Last address */ uint32_t valuelast; /* Last value */ int ntimes; /* Number of times */ #endif }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* Helpers */ #ifdef CONFIG_SAMV7_QSPI_REGDEBUG static bool qspi_checkreg(struct sam_qspidev_s *priv, bool wr, uint32_t value, uint32_t address); #else # define qspi_checkreg(priv,wr,value,address) (false) #endif static inline uint32_t qspi_getreg(struct sam_qspidev_s *priv, unsigned int offset); static inline void qspi_putreg(struct sam_qspidev_s *priv, uint32_t value, unsigned int offset); #if defined(CONFIG_DEBUG_SPI) && defined(CONFIG_DEBUG_VERBOSE) static void qspi_dumpregs(struct sam_qspidev_s *priv, const char *msg); #else # define qspi_dumpregs(priv,msg) #endif /* DMA support */ #ifdef CONFIG_SAMV7_QSPI_DMA #ifdef CONFIG_SAMV7_QSPI_DMADEBUG # define qspi_dma_sample(s,i) sam_dmasample((s)->dmach, &(s)->dmaregs[i]) static void qspi_dma_sampleinit(struct sam_qspidev_s *priv); static void qspi_dma_sampledone(struct sam_qspidev_s *priv); #else # define qspi_dma_sample(s,i) # define qspi_dma_sampleinit(s) # define qspi_dma_sampledone(s) #endif static void qspi_dma_callback(DMA_HANDLE handle, void *arg, int result); static inline uintptr_t qspi_regaddr(struct sam_qspidev_s *priv, unsigned int offset); #endif static int qspi_memory_enable(struct sam_qspidev_s *priv, struct qspi_meminfo_s *meminfo); #ifdef CONFIG_SAMV7_QSPI_DMA static int qspi_memory_dma(struct sam_qspidev_s *priv, struct qspi_meminfo_s *meminfo); #endif static int qspi_memory_nodma(struct sam_qspidev_s *priv, struct qspi_meminfo_s *meminfo); static void qspi_memcpy(uint8_t *dest, const uint8_t *src, size_t buflen); /* Interrupts */ #ifdef QSPI_USE_INTERRUPTS static int qspi_interrupt(struct sam_qspidev_s *priv); #ifdef CONFIG_SAMV7_QSPI static int qspi0_interrupt(int irq, void *context); #endif #endif /* QSPI methods */ static int qspi_lock(struct qspi_dev_s *dev, bool lock); static uint32_t qspi_setfrequency(struct qspi_dev_s *dev, uint32_t frequency); static void qspi_setmode(struct qspi_dev_s *dev, enum qspi_mode_e mode); static void qspi_setbits(struct qspi_dev_s *dev, int nbits); static int qspi_command(struct qspi_dev_s *dev, struct qspi_cmdinfo_s *cmdinfo); static int qspi_memory(struct qspi_dev_s *dev, struct qspi_meminfo_s *meminfo); static FAR void *qspi_alloc(FAR struct qspi_dev_s *dev, size_t buflen); static void qspi_free(FAR struct qspi_dev_s *dev, FAR void *buffer); /* Initialization */ static int qspi_hw_initialize(struct sam_qspidev_s *priv); /**************************************************************************** * Private Data ****************************************************************************/ #ifdef CONFIG_SAMV7_QSPI /* QSPI0 driver operations */ static const struct qspi_ops_s g_qspi0ops = { .lock = qspi_lock, .setfrequency = qspi_setfrequency, .setmode = qspi_setmode, .setbits = qspi_setbits, .command = qspi_command, .memory = qspi_memory, .alloc = qspi_alloc, .free = qspi_free, }; /* This is the overall state of the QSPI0 controller */ static struct sam_qspidev_s g_qspi0dev = { .qspi = { .ops = &g_qspi0ops, }, .base = SAM_QSPI_BASE, #ifdef QSPI_USE_INTERRUPTS .handler = qspi0_interrupt, #endif .intf = 0, #ifdef QSPI_USE_INTERRUPTS .irq = SAM_IRQ_QSPI, #endif #ifdef CONFIG_SAMV7_QSPI_DMA .candma = SAMV7_QSPI0_DMA, .rxintf = XDMACH_QSPI_RX, .txintf = XDMACH_QSPI_TX, #endif }; #endif /* CONFIG_SAMV7_QSPI */ /**************************************************************************** * Public Data ****************************************************************************/ /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: qspi_checkreg * * Description: * Check if the current register access is a duplicate of the preceding. * * Input Parameters: * value - The value to be written * address - The address of the register to write to * * Returned Value: * true: This is the first register access of this type. * flase: This is the same as the preceding register access. * ****************************************************************************/ #ifdef CONFIG_SAMV7_QSPI_REGDEBUG static bool qspi_checkreg(struct sam_qspidev_s *priv, bool wr, uint32_t value, uint32_t address) { if (wr == priv->wrlast && /* Same kind of access? */ value == priv->valuelast && /* Same value? */ address == priv->addresslast) /* Same address? */ { /* Yes, then just keep a count of the number of times we did this. */ priv->ntimes++; return false; } else { /* Did we do the previous operation more than once? */ if (priv->ntimes > 0) { /* Yes... show how many times we did it */ lldbg("...[Repeats %d times]...\n", priv->ntimes); } /* Save information about the new access */ priv->wrlast = wr; priv->valuelast = value; priv->addresslast = address; priv->ntimes = 0; } /* Return true if this is the first time that we have done this operation */ return true; } #endif /**************************************************************************** * Name: qspi_getreg * * Description: * Read an QSPI register * ****************************************************************************/ static inline uint32_t qspi_getreg(struct sam_qspidev_s *priv, unsigned int offset) { uint32_t address = priv->base + offset; uint32_t value = getreg32(address); #ifdef CONFIG_SAMV7_QSPI_REGDEBUG if (qspi_checkreg(priv, false, value, address)) { lldbg("%08x->%08x\n", address, value); } #endif return value; } /**************************************************************************** * Name: qspi_putreg * * Description: * Write a value to an QSPI register * ****************************************************************************/ static inline void qspi_putreg(struct sam_qspidev_s *priv, uint32_t value, unsigned int offset) { uint32_t address = priv->base + offset; #ifdef CONFIG_SAMV7_QSPI_REGDEBUG if (qspi_checkreg(priv, true, value, address)) { lldbg("%08x<-%08x\n", address, value); } #endif putreg32(value, address); } /**************************************************************************** * Name: qspi_dumpregs * * Description: * Dump the contents of all QSPI registers * * Input Parameters: * priv - The QSPI controller to dump * msg - Message to print before the register data * * Returned Value: * None * ****************************************************************************/ #if defined(CONFIG_DEBUG_SPI) && defined(CONFIG_DEBUG_VERBOSE) static void qspi_dumpregs(struct sam_qspidev_s *priv, const char *msg) { qspivdbg("%s:\n", msg); qspivdbg(" MR:%08x SR:%08x IMR:%08x SCR:%08x\n", getreg32(priv->base + SAM_QSPI_MR_OFFSET), getreg32(priv->base + SAM_QSPI_SR_OFFSET), getreg32(priv->base + SAM_QSPI_IMR_OFFSET), getreg32(priv->base + SAM_QSPI_SCR_OFFSET)); qspivdbg(" IAR:%08x ICR:%08x IFR:%08x SMR:%08x\n", getreg32(priv->base + SAM_QSPI_IAR_OFFSET), getreg32(priv->base + SAM_QSPI_ICR_OFFSET), getreg32(priv->base + SAM_QSPI_IFR_OFFSET), getreg32(priv->base + SAM_QSPI_SMR_OFFSET)); qspivdbg(" WPCR:%08x WPSR:%08x\n", getreg32(priv->base + SAM_QSPI_WPCR_OFFSET), getreg32(priv->base + SAM_QSPI_WPSR_OFFSET)); } #endif /**************************************************************************** * Name: qspi_dma_sampleinit * * Description: * Initialize sampling of DMA registers (if CONFIG_SAMV7_QSPI_DMADEBUG) * * Input Parameters: * priv - QSPI driver instance * * Returned Value: * None * ****************************************************************************/ #ifdef CONFIG_SAMV7_QSPI_DMADEBUG static void qspi_dma_sampleinit(struct sam_qspidev_s *priv) { /* Put contents of register samples into a known state */ memset(priv->dmaregs, 0xff, DMA_NSAMPLES * sizeof(struct sam_dmaregs_s)); /* Then get the initial samples */ sam_dmasample(priv->dmach, &priv->dmaregs[DMA_INITIAL]); } #endif /**************************************************************************** * Name: qspi_dma_sampledone * * Description: * Dump sampled DMA registers * * Input Parameters: * priv - QSPI driver instance * * Returned Value: * None * ****************************************************************************/ #ifdef CONFIG_SAMV7_QSPI_DMADEBUG static void qspi_dma_sampledone(struct sam_qspidev_s *priv) { /* Sample the final registers */ sam_dmasample(priv->dmach, &priv->dmaregs[DMA_END_TRANSFER]); /* Then dump the sampled DMA registers */ /* Initial register values */ sam_dmadump(priv->dmach, &priv->dmaregs[DMA_INITIAL], "RX: Initial Registers"); /* Register values after DMA setup */ sam_dmadump(priv->dmach, &priv->dmaregs[DMA_AFTER_SETUP], "RX: After DMA Setup"); /* Register values after DMA start */ sam_dmadump(priv->dmach, &priv->dmaregs[DMA_AFTER_START], "RX: After DMA Start"); /* Register values at the time of the TX and RX DMA callbacks * -OR- DMA timeout. * * If the DMA timed out, then there will not be any RX DMA * callback samples. There is probably no TX DMA callback * samples either, but we don't know for sure. */ if (priv->result == -ETIMEDOUT) { sam_dmadump(priv->dmach, &priv->dmaregs[DMA_TIMEOUT], "RX: At DMA timeout"); } else { sam_dmadump(priv->dmach, &priv->dmaregs[DMA_CALLBACK], "RX: At DMA callback"); } sam_dmadump(priv->dmach, &priv->dmaregs[DMA_END_TRANSFER], "RX: At End-of-Transfer"); } #endif /**************************************************************************** * Name: qspi_dma_timeout * * Description: * The watchdog timeout setup when a has expired without completion of a * DMA. * * Input Parameters: * argc - The number of arguments (should be 1) * arg - The argument (state structure reference cast to uint32_t) * * Returned Value: * None * * Assumptions: * Always called from the interrupt level with interrupts disabled. * ****************************************************************************/ #ifdef CONFIG_SAMV7_QSPI_DMA static void qspi_dma_timeout(int argc, uint32_t arg) { struct sam_qspidev_s *priv = (struct sam_qspidev_s *)arg; DEBUGASSERT(priv != NULL); /* Sample DMA registers at the time of the timeout */ qspi_dma_sample(priv, DMA_CALLBACK); /* Report timeout result, perhaps overwriting any failure reports from * the TX callback. */ priv->result = -ETIMEDOUT; /* Then wake up the waiting thread */ sem_post(&priv->dmawait); } #endif /**************************************************************************** * Name: qspi_dma_callback * * Description: * This callback function is invoked at the completion of the QSPI RX DMA. * * Input Parameters: * handle - The DMA handler * arg - A pointer to the chip select structure * result - The result of the DMA transfer * * Returned Value: * None * ****************************************************************************/ #ifdef CONFIG_SAMV7_QSPI_DMA static void qspi_dma_callback(DMA_HANDLE handle, void *arg, int result) { struct sam_qspidev_s *priv = (struct sam_qspidev_s *)arg; DEBUGASSERT(priv != NULL); /* Cancel the watchdog timeout */ (void)wd_cancel(priv->dmadog); /* Sample DMA registers at the time of the callback */ qspi_dma_sample(priv, DMA_CALLBACK); /* Report the result of the transfer only if the TX callback has not already * reported an error. */ if (priv->result == -EBUSY) { /* Save the result of the transfer if no error was previously reported */ priv->result = result; } /* Then wake up the waiting thread */ sem_post(&priv->dmawait); } #endif /**************************************************************************** * Name: qspi_regaddr * * Description: * Return the address of an QSPI register * ****************************************************************************/ #ifdef CONFIG_SAMV7_QSPI_DMA static inline uintptr_t qspi_regaddr(struct sam_qspidev_s *priv, unsigned int offset) { return priv->base + offset; } #endif /**************************************************************************** * Name: qspi_memory_enable * * Description: * Enable the QSPI memory transfer * * Input Parameters: * priv - Device-specific state data * meminfo - Describes the memory transfer to be performed. * * Returned Value: * Zero (OK) on SUCCESS, a negated errno on value of failure * ****************************************************************************/ static int qspi_memory_enable(struct sam_qspidev_s *priv, struct qspi_meminfo_s *meminfo) { uint32_t regval; /* Write the Instruction code register: * * QSPI_ICR_INST(cmd) 8-bit command * QSPI_ICR_OPT(0) No option */ regval = QSPI_ICR_INST(meminfo->cmd) | QSPI_ICR_OPT(0); qspi_putreg(priv, regval, SAM_QSPI_ICR_OFFSET); /* Is memory data scrambled? */ if (QSPIMEM_ISSCRAMBLE(meminfo->flags)) { /* Yes.. set the scramble key */ qspi_putreg(priv, meminfo->key, SAM_QSPI_SKR_OFFSET); /* Enable the scrambler and enable/disable the random value in the * key. */ regval = QSPI_SMR_SCREN; if (!QSPIMEM_ISRANDOM(meminfo->flags)) { /* Disable random value in key */ regval |= QSPI_SMR_RVDIS; } qspi_putreg(priv, 0, SAM_QSPI_SMR_OFFSET); } else { /* Disable the scrambler */ qspi_putreg(priv, 0, SAM_QSPI_SKR_OFFSET); qspi_putreg(priv, 0, SAM_QSPI_SMR_OFFSET); } /* Write Instruction Frame Register: * * QSPI_IFR_WIDTH_? Instruction=single bit/Data depends on meminfo->flags * QSPI_IFR_INSTEN=1 Instruction Enable * QSPI_IFR_ADDREN=1 Address Enable * QSPI_IFR_OPTEN=0 Option Disable * QSPI_IFR_DATAEN=1 Data Enable * QSPI_IFR_OPTL_* Not used (zero) * QSPI_IFR_ADDRL=0/1 Depends on meminfo->addrlen; * QSPI_IFR_TFRTYP_RD/WRMEM Depends on meminfo->flags * QSPI_IFR_CRM=0 Not continuous read * QSPI_IFR_NBDUM Depends on meminfo->dummies */ regval = QSPI_IFR_INSTEN | QSPI_IFR_ADDREN | QSPI_IFR_DATAEN | QSPI_IFR_NBDUM(meminfo->dummies); if (QSPIMEM_ISWRITE(meminfo->flags)) { regval |= QSPI_IFR_TFRTYP_WRMEM | QSPI_IFR_WIDTH_SINGLE; } else { if (QSPIMEM_ISQUADIO(meminfo->flags)) { regval |= QSPI_IFR_TFRTYP_RDMEM | QSPI_IFR_WIDTH_QUADIO; } else if (QSPIMEM_ISDUALIO(meminfo->flags)) { regval |= QSPI_IFR_TFRTYP_RDMEM | QSPI_IFR_WIDTH_DUALIO; } else { regval |= QSPI_IFR_TFRTYP_RDMEM | QSPI_IFR_WIDTH_SINGLE; } } if (meminfo->addrlen == 3) { regval |= QSPI_IFR_ADDRL_24BIT; } else if (meminfo->addrlen == 4) { regval |= QSPI_IFR_ADDRL_32BIT; } else { return -EINVAL; } /* Write the instruction frame value */ qspi_putreg(priv, regval, SAM_QSPI_IFR_OFFSET); (void)qspi_getreg(priv, SAM_QSPI_IFR_OFFSET); return OK; } /**************************************************************************** * Name: qspi_memory_dma * * Description: * Perform one QSPI memory transfer using DMA * * Input Parameters: * priv - Device-specific state data * meminfo - Describes the memory transfer to be performed. * * Returned Value: * Zero (OK) on SUCCESS, a negated errno on value of failure * ****************************************************************************/ #ifdef CONFIG_SAMV7_QSPI_DMA static int qspi_memory_dma(struct sam_qspidev_s *priv, struct qspi_meminfo_s *meminfo) { uintptr_t qspimem = SAM_QSPIMEM_BASE + meminfo->addr; uint32_t dmaflags; int ret; /* Initialize register sampling */ qspi_dma_sampleinit(priv); /* Determine DMA flags and setup the DMA */ dmaflags = DMACH_FLAG_FIFOCFG_LARGEST | DMACH_FLAG_PERIPHAHB_AHB_IF1 | DMACH_FLAG_PERIPHISMEMORY | DMACH_FLAG_PERIPHINCREMENT | DMACH_FLAG_PERIPHCHUNKSIZE_1 | DMACH_FLAG_MEMPID_MAX | DMACH_FLAG_MEMAHB_AHB_IF1 | DMACH_FLAG_MEMINCREMENT | DMACH_FLAG_MEMCHUNKSIZE_1 | DMACH_FLAG_MEMBURST_16; if (QSPIMEM_ISWRITE(meminfo->flags)) { /* Configure TX DMA */ dmaflags |= ((uint32_t)priv->txintf << DMACH_FLAG_PERIPHPID_SHIFT) | DMACH_FLAG_PERIPHWIDTH_8BITS | DMACH_FLAG_MEMWIDTH_8BITS; sam_dmaconfig(priv->dmach, dmaflags); /* Setup the TX DMA (peripheral-to-memory) */ ret = sam_dmatxsetup(priv->dmach, qspimem, (uint32_t)meminfo->buffer, meminfo->buflen); } else { /* Configure RX DMA */ dmaflags |= ((uint32_t)priv->rxintf << DMACH_FLAG_PERIPHPID_SHIFT) | DMACH_FLAG_PERIPHWIDTH_16BITS | DMACH_FLAG_MEMWIDTH_16BITS; sam_dmaconfig(priv->dmach, dmaflags); /* Setup the RX DMA (memory-to-peripheral) */ ret = sam_dmarxsetup(priv->dmach, qspimem, (uint32_t)meminfo->buffer, meminfo->buflen); } if (ret < 0) { qspidbg("ERROR: DMA setup failed: %d\n", ret); return ret; } qspi_dma_sample(priv, DMA_AFTER_SETUP); /* Enable the memory transfer */ qspi_memory_enable(priv, meminfo); /* Start the DMA */ priv->result = -EBUSY; ret = sam_dmastart(priv->dmach, qspi_dma_callback, (void *)priv); if (ret < 0) { qspidbg("ERROR: sam_dmastart failed: %d\n", ret); return ret; } qspi_dma_sample(priv, DMA_AFTER_START); /* Wait for DMA completion. This is done in a loop because there may be * false alarm semaphore counts that cause sam_wait() not fail to wait * or to wake-up prematurely (for example due to the receipt of a signal). * We know that the DMA has completed when the result is anything other * that -EBUSY. */ do { /* Start (or re-start) the watchdog timeout */ ret = wd_start(priv->dmadog, DMA_TIMEOUT_TICKS, (wdentry_t)qspi_dma_timeout, 1, (uint32_t)priv); if (ret != OK) { qspidbg("ERROR: wd_start failed: %d\n", ret); } /* Wait for the DMA complete */ ret = sem_wait(&priv->dmawait); /* Cancel the watchdog timeout */ (void)wd_cancel(priv->dmadog); /* Check if we were awakened by an error of some kind */ if (ret < 0) { /* EINTR is not a failure. That simply means that the wait * was awakened by a signal. */ int errorcode = errno; if (errorcode != EINTR) { DEBUGPANIC(); return -errorcode; } } /* Not that we might be awakened before the wait is over due to * residual counts on the semaphore. So, to handle, that case, * we loop until something changes the DMA result to any value other * than -EBUSY. */ } while (priv->result == -EBUSY); /* Wait until the transmission registers are empty. */ while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_INT_TXEMPTY) == 0); qspi_putreg(priv, QSPI_CR_LASTXFER, SAM_QSPI_CR_OFFSET); while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_INSTRE) == 0); MEMORY_SYNC(); /* Dump the sampled DMA registers */ qspi_dma_sampledone(priv); /* Make sure that the DMA is stopped (it will be stopped automatically * on normal transfers, but not necessarily when the transfer terminates * on an error condition). */ sam_dmastop(priv->dmach); /* Complain if the DMA fails */ if (priv->result) { qspidbg("ERROR: DMA failed with result: %d\n", priv->result); } return priv->result; } #endif /**************************************************************************** * Name: qspi_memory_nodma * * Description: * Perform one QSPI memory transfer without using DMA * * Input Parameters: * priv - Device-specific state data * meminfo - Describes the memory transfer to be performed. * * Returned Value: * Zero (OK) on SUCCESS, a negated errno on value of failure * ****************************************************************************/ static int qspi_memory_nodma(struct sam_qspidev_s *priv, struct qspi_meminfo_s *meminfo) { uintptr_t qspimem = SAM_QSPIMEM_BASE + meminfo->addr; size_t buflen; /* Get the length as an even multiple of 32-bit words. */ buflen = ALIGN_UP(meminfo->buflen); /* Enable the memory transfer */ qspi_memory_enable(priv, meminfo); /* Transfer data to/from QSPI memory */ if (QSPIMEM_ISWRITE(meminfo->flags)) { qspi_memcpy((uint8_t *)qspimem, (const uint8_t *)meminfo->buffer, buflen); } else { qspi_memcpy((uint8_t *)meminfo->buffer, (const uint8_t *)qspimem, buflen); } MEMORY_SYNC(); /* Indicate the end of the transfer as soon as the transmission * registers are empty. */ while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_INT_TXEMPTY) == 0); qspi_putreg(priv, QSPI_CR_LASTXFER, SAM_QSPI_CR_OFFSET); /* Wait for the end of the transfer * * REVISIT: If DMA is not used then large transfers could come through * this path. In that case, there would be a benefit to waiting for an * interrupt to signal the end of the transfer. */ while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_INSTRE) == 0); return OK; } /**************************************************************************** * Name: qspi_memcpy * * Description: * 32-bit version of memcpy. * * Input Parameters: * dest - Destination address of the copy * src - Source address of the copy * buflen - The number of 32-bit words to copy. * * Returned Value: * None * ****************************************************************************/ static void qspi_memcpy(uint8_t *dest, const uint8_t *src, size_t buflen) { /* The size of the SPI transfer is equal to the bus access width. * 8-bit transfers should result in in 8-bit SPI accesses. */ for (; buflen > 0; buflen--) { *dest++ = *src++; } } /**************************************************************************** * Name: qspi_lock * * Description: * On QSPI buses where there are multiple devices, it will be necessary to * lock QSPI to have exclusive access to the buses for a sequence of * transfers. The bus should be locked before the chip is selected. After * locking the QSPI bus, the caller should then also call the setfrequency, * setbits, and setmode methods to make sure that the QSPI is properly * configured for the device. If the QSPI bus is being shared, then it * may have been left in an incompatible state. * * Input Parameters: * dev - Device-specific state data * lock - true: Lock QSPI bus, false: unlock QSPI bus * * Returned Value: * None * ****************************************************************************/ static int qspi_lock(struct qspi_dev_s *dev, bool lock) { struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; qspivdbg("lock=%d\n", lock); if (lock) { /* Take the semaphore (perhaps waiting) */ while (sem_wait(&priv->exclsem) != 0) { /* The only case that an error should occur here is if the wait was awakened * by a signal. */ ASSERT(errno == EINTR); } } else { (void)sem_post(&priv->exclsem); } return OK; } /**************************************************************************** * Name: qspi_setfrequency * * Description: * Set the QSPI frequency. * * Input Parameters: * dev - Device-specific state data * frequency - The QSPI frequency requested * * Returned Value: * Returns the actual frequency selected * ****************************************************************************/ static uint32_t qspi_setfrequency(struct qspi_dev_s *dev, uint32_t frequency) { struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; uint32_t actual; uint32_t scbr; #if CONFIG_SAMV7_QSPI_DLYBS > 0 uint32_t dlybs; #endif #if CONFIG_SAMV7_QSPI_DLYBCT > 0 uint32_t dlybct; #endif uint32_t regval; qspivdbg("frequency=%d\n", frequency); DEBUGASSERT(priv); /* Check if the requested frequency is the same as the frequency selection */ if (priv->frequency == frequency) { /* We are already at this frequency. Return the actual. */ return priv->actual; } /* Configure QSPI to a frequency as close as possible to the requested * frequency. * * QSCK frequency = QSPI_CLK / SCBR, or SCBR = QSPI_CLK / frequency * * Where SCBR can have the range 1 to 256 and the SCR register field holds * SCBR - 1. NOTE that a "ceiling" type of calculation is performed. * 'frequency' is treated as a not-to-exceed value. */ scbr = (frequency + SAM_QSPI_CLOCK - 1) / frequency; /* Make sure that the divider is within range */ if (scbr < 1) { scbr = 1; } else if (scbr > 256) { scbr = 256; } /* Save the new SCBR value (minus one) */ regval = qspi_getreg(priv, SAM_QSPI_SCR_OFFSET); regval &= ~(QSPI_SCR_SCBR_MASK | QSPI_SCR_DLYBS_MASK); regval |= (scbr - 1) << QSPI_SCR_SCBR_SHIFT; /* DLYBS: Delay Before QSCK. This field defines the delay from NPCS valid to the * first valid QSCK transition. When DLYBS equals zero, the NPCS valid to QSCK * transition is 1/2 the QSCK clock period. Otherwise, the following equations * determine the delay: * * Delay Before QSCK = DLYBS / QSPI_CLK * * For a 100 nsec delay (assumes QSPI_CLK is an even multiple of MHz): * * DLYBS == 100 * QSPI_CLK / 1000000000 * == (100 * (QSPI_CLK / 1000000)) / 1000 */ #if CONFIG_SAMV7_QSPI_DLYBS > 0 dlybs = (CONFIG_SAMV7_QSPI_DLYBS * (SAM_QSPI_CLOCK / 1000000)) / 1000; regval |= dlybs << QSPI_SCR_DLYBS_SHIFT; #endif qspi_putreg(priv, regval, SAM_QSPI_SCR_OFFSET); /* DLYBCT: Delay Between Consecutive Transfers. This field defines the delay * between two consecutive transfers with the same peripheral without removing * the chip select. The delay is always inserted after each transfer and * before removing the chip select if needed. * * Delay Between Consecutive Transfers = (32 x DLYBCT) / QSPI_CLK * * For a 500 nsec delay (assumes QSPI_CLK is an even multiple of MHz): * * DLYBCT = 500 * QSPI_CLK / 1000000000 / 32 * = (500 * (QSPI_CLK / 1000000) / 1000 / 32 * * REVISIT: The following logic is conditioned out because for some * inexplicable reason results in hangs -- Even though is it effectively * a no-op for the default case where DLYBCT == 0. */ #if 0 /* REVISIT -- Causes a hang for some reason */ regval = qspi_getreg(priv, SAM_QSPI_MR_OFFSET); regval &= ~QSPI_MR_DLYBCT_MASK; #if CONFIG_SAMV7_QSPI_DLYBCT > 0 dlybct = ((CONFIG_SAMV7_QSPI_DLYBCT * (SAM_QSPI_CLOCK /1000000)) / 1000 / 32; regval |= dlybct << QSPI_MR_DLYBCT_SHIFT; #endif qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET); #endif /* Calculate the new actual frequency */ actual = SAM_QSPI_CLOCK / scbr; qspivdbg("SCBR=%d actual=%d\n", scbr, actual); /* Save the frequency setting */ priv->frequency = frequency; priv->actual = actual; qspidbg("Frequency %d->%d\n", frequency, actual); return actual; } /**************************************************************************** * Name: qspi_setmode * * Description: * Set the QSPI mode. Optional. See enum qspi_mode_e for mode definitions * * Input Parameters: * dev - Device-specific state data * mode - The QSPI mode requested * * Returned Value: * none * ****************************************************************************/ static void qspi_setmode(struct qspi_dev_s *dev, enum qspi_mode_e mode) { struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; uint32_t regval; qspivdbg("mode=%d\n", mode); /* Has the mode changed? */ if (mode != priv->mode) { /* Yes... Set the mode appropriately: * * QSPI CPOL CPHA * MODE * 0 0 0 * 1 0 1 * 2 1 0 * 3 1 1 */ regval = qspi_getreg(priv, SAM_QSPI_SCR_OFFSET); regval &= ~(QSPI_SCR_CPOL | QSPI_SCR_CPHA); switch (mode) { case QSPIDEV_MODE0: /* CPOL=0; CPHA=0 */ break; case QSPIDEV_MODE1: /* CPOL=0; CPHA=1 */ regval |= QSPI_SCR_CPHA; break; case QSPIDEV_MODE2: /* CPOL=1; CPHA=0 */ regval |= QSPI_SCR_CPOL; break; case QSPIDEV_MODE3: /* CPOL=1; CPHA=1 */ regval |= (QSPI_SCR_CPOL | QSPI_SCR_CPHA); break; default: DEBUGASSERT(FALSE); return; } qspi_putreg(priv, regval, SAM_QSPI_SCR_OFFSET); qspivdbg("SCR=%08x\n", regval); /* Save the mode so that subsequent re-configurations will be faster */ priv->mode = mode; } } /**************************************************************************** * Name: qspi_setbits * * Description: * Set the number if bits per word. * * Input Parameters: * dev - Device-specific state data * nbits - The number of bits requests * * Returned Value: * none * ****************************************************************************/ static void qspi_setbits(struct qspi_dev_s *dev, int nbits) { struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; uint32_t regval; qspivdbg("nbits=%d\n", nbits); DEBUGASSERT(priv != NULL); DEBUGASSERT(nbits >= SAM_QSPI_MINBITS && nbits <= SAM_QSPI_MAXBITS); /* Has the number of bits changed? */ if (nbits != priv->nbits) { /* Yes... Set number of bits appropriately */ regval = qspi_getreg(priv, SAM_QSPI_MR_OFFSET); regval &= ~QSPI_MR_NBBITS_MASK; regval |= QSPI_MR_NBBITS(nbits); qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET); qspivdbg("MR=%08x\n", regval); /* Save the selection so the subsequence re-configurations will be faster */ priv->nbits = nbits; } } /**************************************************************************** * Name: qspi_command * * Description: * Perform one QSPI data transfer * * Input Parameters: * dev - Device-specific state data * cmdinfo - Describes the command transfer to be performed. * * Returned Value: * Zero (OK) on SUCCESS, a negated errno on value of failure * ****************************************************************************/ static int qspi_command(struct qspi_dev_s *dev, struct qspi_cmdinfo_s *cmdinfo) { struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; uint32_t regval; uint32_t ifr; DEBUGASSERT(priv != NULL && cmdinfo != NULL); #ifdef CONFIG_DEBUG_SPI qspivdbg("Transfer:\n"); qspivdbg(" flags: %02x\n", cmdinfo->flags); qspivdbg(" cmd: %04x\n", cmdinfo->cmd); if (QSPICMD_ISADDRESS(cmdinfo->flags)) { qspivdbg(" address/length: %08lx %d\n", (unsigned long)cmdinfo->addr, cmdinfo->addrlen); } if (QSPICMD_ISDATA(cmdinfo->flags)) { qspivdbg(" %s Data:\n", QSPICMD_ISWRITE(cmdinfo->flags) ? "Write" : "Read"); qspivdbg(" buffer/length: %p %d\n", cmdinfo->buffer, cmdinfo->buflen); } #endif DEBUGASSERT(cmdinfo->cmd < 256); /* Write the instruction address register */ ifr = 0; if (QSPICMD_ISADDRESS(cmdinfo->flags)) { DEBUGASSERT(cmdinfo->addrlen == 3 || cmdinfo->addrlen == 4); /* Set the addressin the IAR. This is required only if the * instruction frame includes an address, but no data. When data is * preset, the address of the instruction is determined by the address * of QSPI memory accesses, and not by the content of the IAR. */ qspi_putreg(priv, cmdinfo->addr, SAM_QSPI_IAR_OFFSET); /* Set/clear the address enable bit and the address size in the IFR */ ifr |= QSPI_IFR_ADDREN; if (cmdinfo->addrlen == 3) { ifr |= QSPI_IFR_ADDRL_24BIT; } else if (cmdinfo->addrlen == 4) { ifr |= QSPI_IFR_ADDRL_32BIT; } else { return -EINVAL; } } /* Write the Instruction code register: * * QSPI_ICR_INST(cmd) 8-bit command * QSPI_ICR_OPT(0) No option */ regval = QSPI_ICR_INST(cmdinfo->cmd) | QSPI_ICR_OPT(0); qspi_putreg(priv, regval, SAM_QSPI_ICR_OFFSET); /* Does data accompany the command? */ if (QSPICMD_ISDATA(cmdinfo->flags)) { uint16_t buflen; DEBUGASSERT(cmdinfo->buffer != NULL && cmdinfo->buflen > 0); DEBUGASSERT(IS_ALIGNED(cmdinfo->buffer)); /* Get the length as an even multiple of 32-bit words. */ buflen = ALIGN_UP(cmdinfo->buflen); /* Write Instruction Frame Register: * * QSPI_IFR_WIDTH_SINGLE Instruction=single bit/Data single bit * QSPI_IFR_INSTEN=1 Instruction Enable * QSPI_IFR_ADDREN=? (See logic above) * QSPI_IFR_OPTEN=0 Option Disable * QSPI_IFR_DATAEN=1 Data Enable * QSPI_IFR_OPTL_* Not used (zero) * QSPI_IFR_ADDRL=0 Not used (zero) * QSPI_IFR_TFRTYP_WRITE Write transfer into serial memory, OR * QSPI_IFR_TFRTYP_READ Read transfer from serial memory * QSPI_IFR_CRM=0 Not continuous read * QSPI_IFR_NBDUM(0) No dummy cycles */ ifr |= QSPI_IFR_WIDTH_SINGLE | QSPI_IFR_INSTEN | QSPI_IFR_DATAEN | QSPI_IFR_NBDUM(0); /* Read or write operation? */ if (QSPICMD_ISWRITE(cmdinfo->flags)) { /* Set write data operation * * Write the IFR to the hardware. If the instructrion frame * includes data, writing to the IFR does not trigger the * instruction frame transfer. Rather, the instruction frame * is triggered by the first access to QSPI memory. */ ifr |= QSPI_IFR_TFRTYP_WRITE; qspi_putreg(priv, ifr, SAM_QSPI_IFR_OFFSET); /* Read QSPI_IFR (dummy read) to synchronize APB and AHB * accesses. */ (void)qspi_getreg(priv, SAM_QSPI_IFR_OFFSET); /* Copy the data to write to QSPI_RAM */ qspi_memcpy((uint8_t *)SAM_QSPIMEM_BASE, (const uint8_t *)cmdinfo->buffer, buflen); } else { /* Set read data operation * * Write the IFR to the hardware. If the instructrion frame * includes data, writing to the IFR does not trigger the * instruction frame transfer. Rather, the instruction frame * is triggered by the first access to QSPI memory. */ ifr |= QSPI_IFR_TFRTYP_READ; qspi_putreg(priv, ifr, SAM_QSPI_IFR_OFFSET); /* Read QSPI_IFR (dummy read) to synchronize APB and AHB * accesses. */ (void)qspi_getreg(priv, SAM_QSPI_IFR_OFFSET); /* Copy the data from QSPI memory into the user buffer */ qspi_memcpy((uint8_t *)cmdinfo->buffer, (const uint8_t *)SAM_QSPIMEM_BASE, buflen); } MEMORY_SYNC(); /* Indicate the end of the transfer as soon as the transmission * registers are empty. */ while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_INT_TXEMPTY) == 0); qspi_putreg(priv, QSPI_CR_LASTXFER, SAM_QSPI_CR_OFFSET); /* Fall through to INSTRE wait */ } else { /* Write Instruction Frame Register: * * QSPI_IFR_WIDTH_SINGLE Instruction=single bit/Data single bit * QSPI_IFR_INSTEN=1 Instruction Enable * QSPI_IFR_ADDREN=? (See logic above) * QSPI_IFR_OPTEN=0 Option Disable * QSPI_IFR_DATAEN=0 Data Disable * QSPI_IFR_OPTL_* Not used (zero) * QSPI_IFR_ADDRL=0 Not used (zero) * QSPI_IFR_TFRTYP_READ Shouldn't matter * QSPI_IFR_CRM=0 Not continuous read * QSPI_IFR_NBDUM(0) No dummy cycles */ ifr |= QSPI_IFR_WIDTH_SINGLE | QSPI_IFR_INSTEN | QSPI_IFR_TFRTYP_READ | QSPI_IFR_NBDUM(0); qspi_putreg(priv, ifr, SAM_QSPI_IFR_OFFSET); MEMORY_SYNC(); /* If the insruction frame does not include data, writing to the IFR * tiggers sending of the instruction frame. Fall through to INSTRE * wait. * * REVISIT: Setting QSPI_CR_LASTXFER should not be necessary in this * case. However, I see hangs in the following wait for QSPI_SR_INSTRE * if I do not do this. No idea why. */ qspi_putreg(priv, QSPI_CR_LASTXFER, SAM_QSPI_CR_OFFSET); } /* When the command has been sent, Instruction End Status (INTRE) will be * set in the QSPI status register. */ while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_INSTRE) == 0); return OK; } /**************************************************************************** * Name: qspi_memory * * Description: * Perform one QSPI memory transfer * * Input Parameters: * dev - Device-specific state data * meminfo - Describes the memory transfer to be performed. * * Returned Value: * Zero (OK) on SUCCESS, a negated errno on value of failure * ****************************************************************************/ static int qspi_memory(struct qspi_dev_s *dev, struct qspi_meminfo_s *meminfo) { struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; DEBUGASSERT(priv != NULL && meminfo != NULL); #ifdef CONFIG_SAMV7_QSPI_DMA /* Can we perform DMA? Should we perform DMA? */ if (priv->candma && meminfo->buflen > CONFIG_SAMV7_QSPI_DMATHRESHOLD) { return qspi_memory_dma(priv, meminfo); } else #endif { return qspi_memory_nodma(priv, meminfo); } } /**************************************************************************** * Name: qspi_alloc * * Description: * Allocate a buffer suitable for DMA data transfer * * Input Parameters: * dev - Device-specific state data * buflen - Buffer length to allocate in bytes * * Returned Value: * Address of tha allocated memory on success; NULL is returned on any * failure. * ****************************************************************************/ static FAR void *qspi_alloc(FAR struct qspi_dev_s *dev, size_t buflen) { /* Here we exploit the internal knowlege the kmm_malloc() will return memory * aligned to 64-bit addresses. The buffer length must be large enough to * hold the rested buflen in units a 32-bits. */ return kmm_malloc(ALIGN_UP(buflen)); } /**************************************************************************** * Name: QSPI_FREE * * Description: * Free memory returned by QSPI_ALLOC * * Input Parameters: * dev - Device-specific state data * buffer - Buffer previously allocated via QSPI_ALLOC * * Returned Value: * None. * ****************************************************************************/ static void qspi_free(FAR struct qspi_dev_s *dev, FAR void *buffer) { if (buffer) { kmm_free(buffer); } } /**************************************************************************** * Name: qspi_hw_initialize * * Description: * Initialize the QSPI peripheral from hardware reset. * * Input Parameters: * priv - Device state structure. * * Returned Value: * Zero (OK) on SUCCESS, a negated errno on value of failure * ****************************************************************************/ static int qspi_hw_initialize(struct sam_qspidev_s *priv) { uint32_t regval; /* Disable the QSPI */ qspi_putreg(priv, QSPI_CR_QSPIDIS, SAM_QSPI_CR_OFFSET); while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_QSPIENS) != 0); /* Reset the QSPI (twice) */ qspi_putreg(priv, QSPI_CR_SWRST, SAM_QSPI_CR_OFFSET); qspi_putreg(priv, QSPI_CR_SWRST, SAM_QSPI_CR_OFFSET); /* Configure the QSPI * * QSPI_MR_SMM - Serial Memory Mode * QSPI_MR_CSMODE_LASTXFER - CS de-asserted when LASTXFER transferred */ regval = QSPI_MR_SMM; qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET); regval |= QSPI_MR_CSMODE_LASTXFER; qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET); /* Set up the initial QSPI clock mode: * * Mode 0: CPOL=0; CPHA=0 */ regval = qspi_getreg(priv, SAM_QSPI_SCR_OFFSET); regval &= ~(QSPI_SCR_CPOL | QSPI_SCR_CPHA); qspi_putreg(priv, regval, SAM_QSPI_SCR_OFFSET); regval |= QSPI_SCR_SCBR(1); qspi_putreg(priv, regval, SAM_QSPI_SCR_OFFSET); /* 8-bit mode */ regval = qspi_getreg(priv, SAM_QSPI_MR_OFFSET); regval &= ~QSPI_MR_NBBITS_MASK; regval |= QSPI_MR_NBBITS_8BIT; qspi_putreg(priv, regval, SAM_QSPI_MR_OFFSET); priv->nbits = 8; /* Enable QSPI */ qspi_putreg(priv, QSPI_CR_QSPIEN, SAM_QSPI_CR_OFFSET); while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_QSPIENS) == 0); /* Flush any pending transfers */ (void)qspi_getreg(priv, SAM_QSPI_SR_OFFSET); (void)qspi_getreg(priv, SAM_QSPI_RDR_OFFSET); qspi_dumpregs(priv, "After initialization"); return OK; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: sam_qspi_initialize * * Description: * Initialize the selected QSPI port in master mode * * Input Parameter: * intf - Interface number(must be zero) * * Returned Value: * Valid QSPI device structure reference on success; a NULL on failure * ****************************************************************************/ struct qspi_dev_s *sam_qspi_initialize(int intf) { struct sam_qspidev_s *priv; int ret; /* The supported SAM parts have only a single QSPI port */ qspivdbg("intf: %d\n", intf); DEBUGASSERT(intf >= 0 && intf < SAMV7_NQSPI); /* Select the QSPI interface */ #ifdef CONFIG_SAMV7_QSPI if (intf == 0) { /* If this function is called multiple times, the following operatinos * will be performed multiple times. */ /* Select QSPI0 */ priv = &g_qspi0dev; /* Enable clocking to the QSPI peripheral */ sam_qspi_enableclk(); /* Configure multiplexed pins as connected on the board. */ sam_configgpio(GPIO_QSPI_CS); sam_configgpio(GPIO_QSPI_IO0); sam_configgpio(GPIO_QSPI_IO1); sam_configgpio(GPIO_QSPI_IO2); sam_configgpio(GPIO_QSPI_IO3); sam_configgpio(GPIO_QSPI_SCK); } else #endif { qspidbg("ERROR: QSPI%d not supported\n", intf); return NULL; } /* Has the QSPI hardware been initialized? */ if (!priv->initialized) { /* No perform one time initialization */ /* Initialize the QSPI semaphore that enforces mutually exclusive * access to the QSPI registers. */ sem_init(&priv->exclsem, 0, 1); #ifdef CONFIG_SAMV7_QSPI_DMA /* Pre-allocate DMA channels. */ if (priv->candma) { priv->dmach = sam_dmachannel(0, 0); if (!priv->dmach) { qspidbg("ERROR: Failed to allocate the DMA channel\n"); priv->candma = false; } } /* Initialize the QSPI semaphore that is used to wake up the waiting * thread when the DMA transfer completes. */ sem_init(&priv->dmawait, 0, 0); /* Create a watchdog time to catch DMA timeouts */ priv->dmadog = wd_create(); if (priv->dmadog == NULL) { qspidbg("ERROR: Failed to create wdog\n"); goto errout_with_dmahandles; } #endif #ifdef QSPI_USE_INTERRUPTS /* Attach the interrupt handler */ ret = irq_attach(priv->irq, priv->handler); if (ret < 0) { qspidbg("ERROR: Failed to attach irq %d\n", priv->irq); goto errout_with_dmadog; } #endif /* Perform hardware initialization. Puts the QSPI into an active * state. */ ret = qspi_hw_initialize(priv); if (ret < 0) { qspidbg("ERROR: Failed to initialize QSPI hardware\n"); goto errout_with_irq; } /* Enable interrupts at the NVIC */ priv->initialized = true; #ifdef QSPI_USE_INTERRUPTS up_enable_irq(priv->irq); #endif } return &priv->qspi; errout_with_irq: #ifdef QSPI_USE_INTERRUPTS irq_detach(priv->irq); errout_with_dmadog: #endif #ifdef CONFIG_SAMV7_QSPI_DMA wd_delete(priv->dmadog); errout_with_dmahandles: sem_destroy(&priv->dmawait); if (priv->dmach) { sam_dmafree(priv->dmach); priv->dmach = NULL; } #endif sem_destroy(&priv->exclsem); return NULL; } #endif /* CONFIG_SAMV7_QSPI */