From 768aba20ad9c994bb359600b4d10fbb1034a2577 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 25 Aug 2015 15:23:59 -0600 Subject: [PATCH] SAMV71 QSPI: Use new QSPI interface. Can't use SPI interface as planned; the hardware architectue is too different --- arch/arm/src/samv7/chip/sam_qspi.h | 2 +- arch/arm/src/samv7/sam_qspi.c | 802 ++++++++--------------------- arch/arm/src/samv7/sam_qspi.h | 3 +- 3 files changed, 216 insertions(+), 591 deletions(-) diff --git a/arch/arm/src/samv7/chip/sam_qspi.h b/arch/arm/src/samv7/chip/sam_qspi.h index 29d785704e..67b1a73784 100644 --- a/arch/arm/src/samv7/chip/sam_qspi.h +++ b/arch/arm/src/samv7/chip/sam_qspi.h @@ -174,7 +174,7 @@ #define QSPI_INT_OVRES (1 << 3) /* Bit 3: Overrun Error Interrupt */ #define QSPI_INT_CSR (1 << 8) /* Bit 8: Chip Select Rise Interrupt */ #define QSPI_SR_CSS (1 << 9) /* Bit 9: Chip Select Status Interrupt */ -#define QSPI_SR_INTSTRE (1 << 10) /* Bit 10: Instruction End Status Interrupt */ +#define QSPI_SR_INSTRE (1 << 10) /* Bit 10: Instruction End Status Interrupt */ #define QSPI_SR_QSPIENS (1 << 24) /* Bit 24: QSPI Enable Status (SR only) */ #define QSPI_INT_ALL (0x0000070f) diff --git a/arch/arm/src/samv7/sam_qspi.c b/arch/arm/src/samv7/sam_qspi.c index 01fb96935e..de847601e7 100644 --- a/arch/arm/src/samv7/sam_qspi.c +++ b/arch/arm/src/samv7/sam_qspi.c @@ -54,10 +54,11 @@ #include #include #include -#include +#include #include "up_internal.h" #include "up_arch.h" +#include "cache.h" #include "sam_gpio.h" #include "sam_xdmac.h" @@ -95,6 +96,8 @@ # undef CONFIG_SAMV7_QSPI_DMADEBUG #endif +#define MEMORY_SYNC() do { ARM_DSB();ARM_ISB(); } while (0) + /* Clocking *****************************************************************/ /* The QSPI Baud rate clock is generated by dividing the peripheral clock by * a value between 1 and 255 @@ -153,18 +156,16 @@ struct sam_qspidev_s { - struct spi_dev_s spidev; /* Externally visible part of the QSPI interface */ + struct qspi_dev_s qspi; /* Externally visible part of the QSPI interface */ uint32_t base; /* QSPI controller register base address */ -#ifndef CONFIG_SPI_OWNBUS uint32_t frequency; /* Requested clock frequency */ uint32_t actual; /* Actual clock frequency */ uint8_t mode; /* Mode 0,1,2,3 */ -#endif uint8_t nbits; /* Width of word in bits (8 to 16) */ uint8_t intf; /* QSPI controller number (0) */ bool initialized; /* TRUE: Controller has been initialized */ - sem_t spisem; /* Assures mutually exclusive access to QSPI */ + sem_t exclsem; /* Assures mutually exclusive access to QSPI */ #ifdef CONFIG_SAMV7_QSPI_DMA bool candma; /* DMA is supported */ @@ -245,30 +246,17 @@ static inline uintptr_t qspi_regaddr(struct sam_qspidev_s *priv, unsigned int offset); #endif -/* QSPI master methods */ +/* QSPI methods */ -#ifndef CONFIG_SPI_OWNBUS -static int qspi_lock(struct spi_dev_s *dev, bool lock); -#endif -static void qspi_select(struct spi_dev_s *dev, enum spi_dev_e devid, - bool selected); -static uint32_t qspi_setfrequency(struct spi_dev_s *dev, uint32_t frequency); -static void qspi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode); -static void qspi_setbits(struct spi_dev_s *dev, int nbits); -static uint8_t qspi_status(struct spi_dev_s *dev, enum spi_dev_e devid); -static uint16_t qspi_send(struct spi_dev_s *dev, uint16_t ch); -#ifdef CONFIG_SAMV7_QSPI_DMA -static void qspi_exchange_nodma(struct spi_dev_s *dev, - const void *txbuffer, void *rxbuffer, size_t nwords); -#endif -static void qspi_exchange(struct spi_dev_s *dev, const void *txbuffer, - void *rxbuffer, size_t nwords); -#ifndef CONFIG_SPI_EXCHANGE -static void qspi_sndblock(struct spi_dev_s *dev, - const void *buffer, size_t nwords); -static void qspi_recvblock(struct spi_dev_s *dev, void *buffer, - size_t nwords); -#endif +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, uint16_t cmd); +static int qspi_command_write(struct qspi_dev_s *dev, uint16_t cmd, + const void *buffer, size_t buflen); +static int qspi_command_read(struct qspi_dev_s *dev, uint16_t cmd, + void *buffer, size_t buflen); /**************************************************************************** * Private Data @@ -277,34 +265,22 @@ static void qspi_recvblock(struct spi_dev_s *dev, void *buffer, #ifdef CONFIG_SAMV7_QSPI /* QSPI0 driver operations */ -static const struct spi_ops_s g_qspi0ops = +static const struct qspi_ops_s g_qspi0ops = { -#ifndef CONFIG_SPI_OWNBUS .lock = qspi_lock, -#endif - .select = qspi_select, .setfrequency = qspi_setfrequency, .setmode = qspi_setmode, .setbits = qspi_setbits, - .status = qspi_status, -#ifdef CONFIG_SPI_CMDDATA - .cmddata = qspi_cmddata, -#endif - .send = qspi_send, -#ifdef CONFIG_SPI_EXCHANGE - .exchange = qspi_exchange, -#else - .sndblock = qspi_sndblock, - .recvblock = qspi_recvblock, -#endif - .registercallback = 0, /* Not implemented */ + .command = qspi_command, + .command_write = qspi_command_write, + .command_read = qspi_command_read, }; /* This is the overall state of the QSPI0 controller */ static struct sam_qspidev_s g_qspi0dev = { - .spidev = + .qspi = { .ops = &g_qspi0ops, }, @@ -758,8 +734,7 @@ static inline uintptr_t qspi_regaddr(struct sam_qspidev_s *priv, * ****************************************************************************/ -#ifndef CONFIG_SPI_OWNBUS -static int qspi_lock(struct spi_dev_s *dev, bool lock) +static int qspi_lock(struct qspi_dev_s *dev, bool lock) { struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; @@ -768,7 +743,7 @@ static int qspi_lock(struct spi_dev_s *dev, bool lock) { /* Take the semaphore (perhaps waiting) */ - while (sem_wait(&priv->spisem) != 0) + while (sem_wait(&priv->exclsem) != 0) { /* The only case that an error should occur here is if the wait was awakened * by a signal. @@ -779,47 +754,11 @@ static int qspi_lock(struct spi_dev_s *dev, bool lock) } else { - (void)sem_post(&priv->spisem); + (void)sem_post(&priv->exclsem); } return OK; } -#endif - -/**************************************************************************** - * Name: qspi_select - * - * Description: - * This function does not actually set the chip select line. Rather, it - * simply maps the device ID into a chip select number and retains that - * chip select number for later use. - * - * Input Parameters: - * dev - Device-specific state data - * frequency - The QSPI frequency requested - * - * Returned Value: - * Returns the actual frequency selected - * - ****************************************************************************/ - - static void qspi_select(struct spi_dev_s *dev, enum spi_dev_e devid, - bool selected) - { - struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; - - /* Are we selecting or de-selecting the device? */ - - spivdbg("selected=%d\n", selected); - if (selected) - { -#warning Missing logic - } - else - { -#warning Missing logic - } -} /**************************************************************************** * Name: qspi_setfrequency @@ -836,7 +775,7 @@ static int qspi_lock(struct spi_dev_s *dev, bool lock) * ****************************************************************************/ -static uint32_t qspi_setfrequency(struct spi_dev_s *dev, uint32_t frequency) +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; @@ -846,17 +785,16 @@ static uint32_t qspi_setfrequency(struct spi_dev_s *dev, uint32_t frequency) uint32_t regval; spivdbg("frequency=%d\n", frequency); + DEBUGASSERT(priv); /* Check if the requested frequency is the same as the frequency selection */ -#ifndef CONFIG_SPI_OWNBUS if (priv->frequency == frequency) { /* We are already at this frequency. Return the actual. */ return priv->actual; } -#endif /* Configure QSPI to a frequency as close as possible to the requested frequency. * @@ -876,7 +814,7 @@ static uint32_t qspi_setfrequency(struct spi_dev_s *dev, uint32_t frequency) scbr = (scbr + 1) & ~1; - /* Save the new scbr value */ + /* Save the new SCBR value */ regval = qspi_getreg(priv, SAM_QSPI_SCR_OFFSET); regval &= ~(QSPI_SCR_SCBR_MASK | QSPI_SCR_DLYBS_MASK); @@ -924,10 +862,8 @@ static uint32_t qspi_setfrequency(struct spi_dev_s *dev, uint32_t frequency) /* Save the frequency setting */ -#ifndef CONFIG_SPI_OWNBUS priv->frequency = frequency; priv->actual = actual; -#endif spidbg("Frequency %d->%d\n", frequency, actual); return actual; @@ -937,7 +873,7 @@ static uint32_t qspi_setfrequency(struct spi_dev_s *dev, uint32_t frequency) * Name: qspi_setmode * * Description: - * Set the QSPI mode. Optional. See enum spi_mode_e for mode definitions + * Set the QSPI mode. Optional. See enum qspi_mode_e for mode definitions * * Input Parameters: * dev - Device-specific state data @@ -948,7 +884,7 @@ static uint32_t qspi_setfrequency(struct spi_dev_s *dev, uint32_t frequency) * ****************************************************************************/ -static void qspi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) +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; @@ -957,10 +893,8 @@ static void qspi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) /* Has the mode changed? */ -#ifndef CONFIG_SPI_OWNBUS if (mode != priv->mode) { -#endif /* Yes... Set the mode appropriately: * * QSPI CPOL NCPHA @@ -976,18 +910,18 @@ static void qspi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) switch (mode) { - case SPIDEV_MODE0: /* CPOL=0; NCPHA=1 */ + case QSPIDEV_MODE0: /* CPOL=0; NCPHA=1 */ regval |= QSPI_SCR_NCPHA; break; - case SPIDEV_MODE1: /* CPOL=0; NCPHA=0 */ + case QSPIDEV_MODE1: /* CPOL=0; NCPHA=0 */ break; - case SPIDEV_MODE2: /* CPOL=1; NCPHA=1 */ + case QSPIDEV_MODE2: /* CPOL=1; NCPHA=1 */ regval |= (QSPI_SCR_CPOL | QSPI_SCR_NCPHA); break; - case SPIDEV_MODE3: /* CPOL=1; NCPHA=0 */ + case QSPIDEV_MODE3: /* CPOL=1; NCPHA=0 */ regval |= QSPI_SCR_CPOL; break; @@ -1001,10 +935,8 @@ static void qspi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) /* Save the mode so that subsequent re-configurations will be faster */ -#ifndef CONFIG_SPI_OWNBUS priv->mode = mode; } -#endif } /**************************************************************************** @@ -1022,7 +954,7 @@ static void qspi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) * ****************************************************************************/ -static void qspi_setbits(struct spi_dev_s *dev, int nbits) +static void qspi_setbits(struct qspi_dev_s *dev, int nbits) { struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; uint32_t regval; @@ -1033,9 +965,7 @@ static void qspi_setbits(struct spi_dev_s *dev, int nbits) /* Has the number of bits changed? */ -#ifndef CONFIG_SPI_OWNBUS if (nbits != priv->nbits) -#endif { /* Yes... Set number of bits appropriately */ @@ -1053,530 +983,231 @@ static void qspi_setbits(struct spi_dev_s *dev, int nbits) } /**************************************************************************** - * Name: qspi_status + * Name: qspi_command * * Description: - * Return status information associated with the QSPI device. - * - * Input Parameters: - * dev - SPI device info - * devid - Identifies the (logical) device - * - * Returned Values: - * Bit-encoded SPI status (see include/nuttx/spi/spi.h. - * - ****************************************************************************/ - -uint8_t qspi_status(struct spi_dev_s *dev, enum spi_dev_e devid) -{ -#warning Missing logic - return 0; -} - -/**************************************************************************** - * Name: qspi_send - * - * Description: - * Exchange one word on QSPI + * Send a command to the QSPI device * * Input Parameters: * dev - Device-specific state data - * wd - The word to send. the size of the data is determined by the + * cmd - The command to send. the size of the data is determined by the * number of bits selected for the QSPI interface. * * Returned Value: - * response + * Zero (OK) on SUCCESS, a negated errno on value of failure * ****************************************************************************/ -static uint16_t qspi_send(struct spi_dev_s *dev, uint16_t wd) +static int qspi_command(struct qspi_dev_s *dev, uint16_t cmd) { - uint8_t txbyte; - uint8_t rxbyte; + struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; + uint32_t regval; - /* qspi_exchange can do this. Note: right now, this only deals with 8-bit - * words. If the QSPI interface were configured for words of other sizes, - * this would fail. + spivdbg("cmd: %04x\n"); + DEBUGASSERT(priv != NULL && cmd < 256); + + /* Write the the Instruction code register: + * + * QSPI_ICR_INST(cmd) 8-bit command + * QSPI_ICR_OPT(0) No option */ - txbyte = (uint8_t)wd; - rxbyte = (uint8_t)0; - qspi_exchange(dev, &txbyte, &rxbyte, 1); + regval = QSPI_ICR_INST(cmd) | QSPI_ICR_OPT(0); + qspi_putreg(priv, regval, SAM_QSPI_ICR_OFFSET); - spivdbg("Sent %02x received %02x\n", txbyte, rxbyte); - return (uint16_t)rxbyte; + /* Write Instruction Frame Register: + * + * QSPI_IFR_WIDTH_SINGLE Instruction=single bit + * QSPI_IFR_INSTEN=1 Instruction Enable + * QSPI_IFR_ADDREN=0 Address Disable + * 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 + */ + + regval = QSPI_IFR_WIDTH_SINGLE | QSPI_IFR_INSTEN | QSPI_IFR_TFRTYP_READ | + QSPI_IFR_NBDUM(0); + qspi_putreg(priv, regval, SAM_QSPI_IFR_OFFSET); + + MEMORY_SYNC(); + + /* 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_exchange (and qspi_exchange_nodma) + * Name: qspi_command_write * * Description: - * Exchange a block of data from QSPI. There are two versions of this - * function: (1) One that is enabled only when CONFIG_SAMV7_QSPI_DMA=y - * that performs DMA QSPI transfers, but only when a larger block of - * data is being transferred. And (2) another version that does polled - * QSPI transfers. When CONFIG_SAMV7_QSPI_DMA=n the latter is the only - * version avaialable; when CONFIG_SAMV7_QSPI_DMA=y, this version is only - * used for short QSPI transfers and gets renamed as qspi_exchange_nodma). + * Send a command then send a block of data. * * Input Parameters: - * dev - Device-specific state data - * txbuffer - A pointer to the buffer of data to be sent - * rxbuffer - A pointer to the buffer in which to receive data - * nwords - the length of data that to be exchanged in units of words. - * The wordsize is determined by the number of bits-per-word - * selected for the QSPI interface. If nbits <= 8, the data is - * packed into uint8_t's; if nbits >8, the data is packed into - * uint16_t's - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef CONFIG_SAMV7_QSPI_DMA -static void qspi_exchange_nodma(struct spi_dev_s *dev, const void *txbuffer, - void *rxbuffer, size_t nwords) -#else -static void qspi_exchange(struct spi_dev_s *dev, const void *txbuffer, - void *rxbuffer, size_t nwords) -#endif -{ - struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; - uint32_t data; - uint16_t *rxptr16; - uint16_t *txptr16; - uint8_t *rxptr8; - uint8_t *txptr8; - - spivdbg("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); - - /* Set up working pointers */ - - if (priv->nbits > 8) - { - rxptr16 = (uint16_t*)rxbuffer; - txptr16 = (uint16_t*)txbuffer; - rxptr8 = NULL; - txptr8 = NULL; - } - else - { - rxptr16 = NULL; - txptr16 = NULL; - rxptr8 = (uint8_t*)rxbuffer; - txptr8 = (uint8_t*)txbuffer; - } - - /* Make sure that any previous transfer is flushed from the hardware */ - - qspi_flush(priv); - - /* Loop, sending each word in the user-provided data buffer. - * - * Note 1: Good QSPI performance would require that we implement DMA - * transfers! - * Note 2: This loop might be made more efficient. Would logic - * like the following improve the throughput? Or would it - * just add the risk of overruns? - * - * Get word 1; - * Send word 1; Now word 1 is "in flight" - * nwords--; - * for ( ; nwords > 0; nwords--) - * { - * Get word N. - * Wait for TDRE meaning that word N-1 has moved to the shift - * register. - * Disable interrupts to keep the following atomic - * Send word N. Now both work N-1 and N are "in flight" - * Wait for RDRF meaning that word N-1 is available - * Read word N-1. - * Re-enable interrupts. - * Save word N-1. - * } - * Wait for RDRF meaning that the final word is available - * Read the final word. - * Save the final word. - */ - - for ( ; nwords > 0; nwords--) - { - /* Get the data to send (0xff if there is no data source). */ - - if (txptr8) - { - data = (uint32_t)*txptr8++; - } - else if (txptr16) - { - data = (uint32_t)*txptr16++; - } - else - { - data = 0xffff; - } - - /* Do we need to set the LASTXFER bit in the TDR value too? */ - -#ifdef CONFIG_SPI_VARSELECT - if (nwords == 1) - { - data |= QSPI_TDR_LASTXFER; - } -#endif - - /* Wait for any previous data written to the TDR to be transferred - * to the serializer. - */ - - while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_INT_TDRE) == 0); - - /* Write the data to transmitted to the Transmit Data Register (TDR) */ - - qspi_putreg(priv, data, SAM_QSPI_TDR_OFFSET); - - /* Wait for the read data to be available in the RDR. - * TODO: Data transfer rates would be improved using the RX FIFO - * (and also DMA) - */ - - while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_INT_RDRF) == 0); - - /* Read the received data from the QSPI Data Register. */ - - data = qspi_getreg(priv, SAM_QSPI_RDR_OFFSET); - if (rxptr8) - { - *rxptr8++ = (uint8_t)data; - } - else if (rxptr16) - { - *rxptr16++ = (uint16_t)data; - } - } -} - -#ifdef CONFIG_SAMV7_QSPI_DMA -static void qspi_exchange(struct spi_dev_s *dev, const void *txbuffer, - void *rxbuffer, size_t nwords) -{ - struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; - uint32_t rxflags; - uint32_t txflags; - uint32_t txdummy; - uint32_t rxdummy; - uint32_t regaddr; - uint32_t memaddr; - uint32_t width; - size_t nbytes; - int ret; - - /* Convert the number of word to a number of bytes */ - - nbytes = (priv->nbits > 8) ? nwords << 1 : nwords; - - /* If we cannot do DMA -OR- if this is a small QSPI transfer, then let - * qspi_exchange_nodma() do the work. - */ - - if (!priv->candma || nbytes <= CONFIG_SAMV7_QSPI_DMATHRESHOLD) - { - qspi_exchange_nodma(dev, txbuffer, rxbuffer, nwords); - return; - } - - spivdbg("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); - - priv = (struct sam_qspidev_s *)dev; - DEBUGASSERT(priv); - - /* Make sure that any previous transfer is flushed from the hardware */ - - qspi_flush(priv); - - /* Sample initial DMA registers */ - - qspi_dma_sampleinit(priv); - - /* Select the source and destination width bits */ - - if (priv->nbits > 8) - { - width = (DMACH_FLAG_PERIPHWIDTH_16BITS | DMACH_FLAG_MEMWIDTH_16BITS); - } - else - { - width = (DMACH_FLAG_PERIPHWIDTH_8BITS | DMACH_FLAG_MEMWIDTH_8BITS); - } - - /* Configure the DMA channels. There are four different cases: - * - * 1) A true exchange with the memory address incrementing on both - * RX and TX channels, - * 2) A read operation with the memory address incrementing only on - * the receive channel, - * 3) A write operation where the memory address increments only on - * the receive channel, and - * 4) A corner case where there the memory address does not increment - * on either channel. This case might be used in certain cases - * where you want to assure that certain number of clocks are - * provided on the QSPI bus. - */ - - /* Configure the RX DMA channel */ - - rxflags = DMACH_FLAG_FIFOCFG_LARGEST | - ((uint32_t)priv->rxintf << DMACH_FLAG_PERIPHPID_SHIFT) | - DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHISPERIPH | - DMACH_FLAG_PERIPHCHUNKSIZE_1 | - ((uint32_t)(15) << DMACH_FLAG_MEMPID_SHIFT) | - DMACH_FLAG_MEMCHUNKSIZE_1; - - /* Set the source and destination width bits */ - - rxflags |= width; - - /* Handle the case where there is no sink buffer */ - - if (!rxbuffer) - { - /* No sink data buffer. Point to our dummy buffer and leave - * the rxflags so that no address increment is performed. - */ - - rxbuffer = (void *)&rxdummy; - } - else - { - /* A receive buffer is available. - * - * Invalidate the RX buffer memory to force re-fetching from RAM when - * the DMA completes - */ - - sam_cmcc_invalidate((uintptr_t)rxbuffer, (uintptr_t)rxbuffer + nbytes); - - /* Use normal RX memory incrementing. */ - - rxflags |= DMACH_FLAG_MEMINCREMENT; - } - - /* Configure the TX DMA channel */ - - txflags = DMACH_FLAG_FIFOCFG_LARGEST | - ((uint32_t)priv->txintf << DMACH_FLAG_PERIPHPID_SHIFT) | - DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHISPERIPH | - DMACH_FLAG_PERIPHCHUNKSIZE_1 | - ((uint32_t)(15) << DMACH_FLAG_MEMPID_SHIFT) | - DMACH_FLAG_MEMCHUNKSIZE_1; - - /* Set the source and destination width bits */ - - txflags |= width; - - /* Handle the case where there is no source buffer */ - - if (!txbuffer) - { - /* No source data buffer. Point to our dummy buffer and leave - * the txflags so that no address increment is performed. - */ - - txdummy = 0xffffffff; - txbuffer = (const void *)&txdummy; - } - else - { - /* Source data is available. Use normal TX memory incrementing. */ - - txflags |= DMACH_FLAG_MEMINCREMENT; - } - - /* Then configure the DMA channels to make it so */ - - sam_dmaconfig(priv->rxdma, rxflags); - sam_dmaconfig(priv->txdma, txflags); - - /* Configure the RX side of the exchange transfer */ - - regaddr = qspi_regaddr(priv, SAM_QSPI_RDR_OFFSET); - memaddr = (uintptr_t)rxbuffer; - - ret = sam_dmarxsetup(priv->rxdma, regaddr, memaddr, nwords); - if (ret < 0) - { - dmadbg("ERROR: sam_dmarxsetup failed: %d\n", ret); - return; - } - - qspi_rxdma_sample(priv, DMA_AFTER_SETUP); - - /* Configure the TX side of the exchange transfer */ - - regaddr = qspi_regaddr(priv, SAM_QSPI_TDR_OFFSET); - memaddr = (uintptr_t)txbuffer; - - ret = sam_dmatxsetup(priv->txdma, regaddr, memaddr, nwords); - if (ret < 0) - { - dmadbg("ERROR: sam_dmatxsetup failed: %d\n", ret); - return; - } - - qspi_txdma_sample(priv, DMA_AFTER_SETUP); - - /* Start the DMA transfer */ - - priv->result = -EBUSY; - ret = sam_dmastart(priv->rxdma, qspi_rxcallback, (void *)priv); - if (ret < 0) - { - dmadbg("ERROR: RX sam_dmastart failed: %d\n", ret); - return; - } - - qspi_rxdma_sample(priv, DMA_AFTER_START); - - ret = sam_dmastart(priv->txdma, qspi_txcallback, (void *)priv); - if (ret < 0) - { - dmadbg("ERROR: RX sam_dmastart failed: %d\n", ret); - sam_dmastop(priv->rxdma); - return; - } - - qspi_txdma_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_dmatimeout, 1, (uint32_t)priv); - if (ret != OK) - { - spidbg("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; - } - } - - /* 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); - - /* 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->rxdma); - sam_dmastop(priv->txdma); - - /* All we can do is complain if the DMA fails */ - - if (priv->result) - { - spidbg("ERROR: DMA failed with result: %d\n", priv->result); - } -} -#endif /* CONFIG_SAMV7_QSPI_DMA */ - -/*************************************************************************** - * Name: qspi_sndblock - * - * Description: - * Send a block of data on QSPI - * - * Input Parameters: - * dev - Device-specific state data + * dev - Device-specific state data + * cmd - The command to send. the size of the data is determined by + * the number of bits selected for the QSPI interface. * buffer - A pointer to the buffer of data to be sent - * nwords - the length of data to send from the buffer in number of words. + * buflen - the length of data to send from the buffer in number of words. * The wordsize is determined by the number of bits-per-word * selected for the QSPI interface. If nbits <= 8, the data is * packed into uint8_t's; if nbits >8, the data is packed into * uint16_t's * * Returned Value: - * None + * Zero (OK) on SUCCESS, a negated errno on value of failure * ****************************************************************************/ -#ifndef CONFIG_SPI_EXCHANGE -static void qspi_sndblock(struct spi_dev_s *dev, const void *buffer, - size_t nwords) +static int qspi_command_write(struct qspi_dev_s *dev, uint16_t cmd, + const void *buffer, size_t buflen) { - /* qspi_exchange can do this. */ + struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; + uint32_t regval; - qspi_exchange(dev, buffer, NULL, nwords); + spivdbg("cmd: %04x buflen: %lu\n", cmd, (unsigned long)buflen); + DEBUGASSERT(priv != NULL && cmd < 256 && buffer != NULL;); + + /* Make sure that the length is an even multiple of 32-bit words. */ + + DEBUGASSERT((buflen & 3) == 0); + buflen = (buflen + 3) & ~3; + + /* Write the the Instruction code register: + * + * QSPI_ICR_INST(cmd) 8-bit command + * QSPI_ICR_OPT(0) No option + */ + + regval = QSPI_ICR_INST(cmd) | QSPI_ICR_OPT(0); + qspi_putreg(priv, regval, SAM_QSPI_ICR_OFFSET); + + /* Write Instruction Frame Register: + * + * QSPI_IFR_WIDTH_SINGLE Instruction=single bit + * QSPI_IFR_INSTEN=1 Instruction Enable + * QSPI_IFR_ADDREN=0 Address Disable + * 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 + * QSPI_IFR_CRM=0 Not continuous read + * QSPI_IFR_NBDUM(0) No dummy cycles + */ + + regval = QSPI_IFR_WIDTH_SINGLE | QSPI_IFR_INSTEN | QSPI_IFR_DATAEN | + QSPI_IFR_TFRTYP_WRITE | QSPI_IFR_NBDUM(0); + qspi_putreg(priv, regval, SAM_QSPI_IFR_OFFSET); + + (void)qspi_getreg(priv, SAM_QSPI_IFR_OFFSET); + + /* Copy the data to write to QSPI_RAM */ + + memcpy((void *)SAM_QSPIMEM_BASE, buffer, 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 */ + + while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_INSTRE) == 0); + return OK; } -#endif /**************************************************************************** - * Name: qspi_recvblock + * Name: qspi_command_read * * Description: - * Revice a block of data from QSPI + * Receive a block of data from QSPI. Required. * * Input Parameters: - * dev - Device-specific state data + * dev - Device-specific state data + * cmd - The command to send. the size of the data is determined by + * the number of bits selected for the QSPI interface. * buffer - A pointer to the buffer in which to receive data - * nwords - the length of data that can be received in the buffer in number - * of words. The wordsize is determined by the number of bits-per-word - * selected for the QSPI interface. If nbits <= 8, the data is - * packed into uint8_t's; if nbits >8, the data is packed into - * uint16_t's + * buflen - the length of data that can be received in the buffer in number + * of words. The wordsize is determined by the number of bits- + * per-word selected for the QSPI interface. If nbits <= 8, the + * data is packed into uint8_t's; if nbits >8, the data is packed + * into uint16_t's * * Returned Value: - * None + * Zero (OK) on SUCCESS, a negated errno on value of failure * ****************************************************************************/ -#ifndef CONFIG_SPI_EXCHANGE -static void qspi_recvblock(struct spi_dev_s *dev, void *buffer, size_t nwords) +static int qspi_command_read(struct qspi_dev_s *dev, uint16_t cmd, + void *buffer, size_t buflen) { - /* qspi_exchange can do this. */ + struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; + uint32_t regval; - qspi_exchange(dev, NULL, buffer, nwords); + spivdbg("cmd: %04x buflen: %lu\n", cmd, (unsigned long)buflen); + DEBUGASSERT(priv != NULL && cmd < 256 && buffer != NULL;); + + /* Make sure that the length is an even multiple of 32-bit words. */ + + DEBUGASSERT((buflen & 3) == 0); + buflen = (buflen + 3) & ~3; + + /* Write the the Instruction code register: + * + * QSPI_ICR_INST(cmd) 8-bit command + * QSPI_ICR_OPT(0) No option + */ + + regval = QSPI_ICR_INST(cmd) | QSPI_ICR_OPT(0); + qspi_putreg(priv, regval, SAM_QSPI_ICR_OFFSET); + + /* Write Instruction Frame Register: + * + * QSPI_IFR_WIDTH_SINGLE Instruction=single bit + * QSPI_IFR_INSTEN=1 Instruction Enable + * QSPI_IFR_ADDREN=0 Address Disable + * 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_READ Read transfer from serial memory + * QSPI_IFR_CRM=0 Not continuous read + * QSPI_IFR_NBDUM(0) No dummy cycles + */ + + regval = QSPI_IFR_WIDTH_SINGLE | QSPI_IFR_INSTEN | QSPI_IFR_DATAEN | + QSPI_IFR_TFRTYP_READ | QSPI_IFR_NBDUM(0); + qspi_putreg(priv, regval, SAM_QSPI_IFR_OFFSET); + + (void)qspi_getreg(priv, SAM_QSPI_IFR_OFFSET); + + /* Copy the data from QSPI memory into the user buffer */ + + memcpy(buffer, (const void *)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); + + /* Wait for the end of the transfer */ + + while ((qspi_getreg(priv, SAM_QSPI_SR_OFFSET) & QSPI_SR_INSTRE) == 0); + return OK; } -#endif /**************************************************************************** * Public Functions @@ -1596,13 +1227,11 @@ static void qspi_recvblock(struct spi_dev_s *dev, void *buffer, size_t nwords) * ****************************************************************************/ -FAR struct spi_dev_s *sam_qspi_initialize(int intf) +FAR struct qspi_dev_s *sam_qspi_initialize(int intf) { FAR struct sam_qspidev_s *priv; irqstate_t flags; -#ifndef CONFIG_SPI_OWNBUS uint32_t regval; -#endif /* The support SAM parts have only a single QSPI port */ @@ -1700,14 +1329,12 @@ FAR struct spi_dev_s *sam_qspi_initialize(int intf) (void)qspi_getreg(priv, SAM_QSPI_SR_OFFSET); (void)qspi_getreg(priv, SAM_QSPI_RDR_OFFSET); -#ifndef CONFIG_SPI_OWNBUS /* Initialize the QSPI semaphore that enforces mutually exclusive * access to the QSPI registers. */ - sem_init(&priv->spisem, 0, 1); + sem_init(&priv->exclsem, 0, 1); priv->initialized = true; -#endif #ifdef CONFIG_SAMV7_QSPI_DMA /* Initialize the QSPI semaphore that is used to wake up the waiting @@ -1725,10 +1352,8 @@ FAR struct spi_dev_s *sam_qspi_initialize(int intf) qspi_dumpregs(priv, "After initialization"); } -#ifndef CONFIG_SPI_OWNBUS - /* Set to mode=0 and nbits=8 and impossible frequency. It is only - * critical to do this if CONFIG_SPI_OWNBUS is not defined because in - * that case, the QSPI will only be reconfigured if there is a change. + /* Set to mode=0 and nbits=8 and impossible frequency in order to + * force an update. */ regval = qspi_getreg(priv, SAM_QSPI_SCR_OFFSET); @@ -1743,8 +1368,7 @@ FAR struct spi_dev_s *sam_qspi_initialize(int intf) priv->nbits = 8; spivdbg("SCR=%08x\n", regval); -#endif - return &priv->spidev; + return &priv->qspi; } #endif /* CONFIG_SAMV7_QSPI */ diff --git a/arch/arm/src/samv7/sam_qspi.h b/arch/arm/src/samv7/sam_qspi.h index 7888c5dde1..85d2e57d65 100644 --- a/arch/arm/src/samv7/sam_qspi.h +++ b/arch/arm/src/samv7/sam_qspi.h @@ -95,7 +95,8 @@ extern "C" * ****************************************************************************/ -FAR struct spi_dev_s *sam_qspi_initialize(int intf); +struct qspi_dev_s; +FAR struct qspi_dev_s *sam_qspi_initialize(int intf); #undef EXTERN #if defined(__cplusplus)