diff --git a/arch/arm/include/samv7/chip.h b/arch/arm/include/samv7/chip.h index 086b9c24ee..9609de91e9 100644 --- a/arch/arm/include/samv7/chip.h +++ b/arch/arm/include/samv7/chip.h @@ -182,7 +182,8 @@ # define SAMV7_NUSART 0 /* No USARTs */ # define SAMV7_NUART 5 /* 5 UARTs */ # define SAMV7_NQSPI 0 /* No Quad SPI */ -# define SAMV7_NSPI 1 /* No SPI, QSPI functions in SPI mode only */ +# define SAMV7_NQSPI_SPI 1 /* QSPI functions in SPI mode only */ +# define SAMV7_NSPI 0 /* No SPI */ # define SAMV7_NTWIHS 2 /* 2 TWIHS */ # define SAMV7_NHSMCI4 0 /* No 4-bit HSMCI port */ # define SAMV7_NCAN 1 /* 1 CAN port */ diff --git a/arch/arm/src/samv7/Kconfig b/arch/arm/src/samv7/Kconfig index 38cd8ad1be..e3f3f14dab 100644 --- a/arch/arm/src/samv7/Kconfig +++ b/arch/arm/src/samv7/Kconfig @@ -394,6 +394,17 @@ config SAMV7_QSPI select ARCH_USE_MPU select ARM_MPU +if SAMV7_QSPI + +config SAMV7_QSPI_SPI_MODE + bool "SPI Mode" + default n + depends on SAMV7_QSPI_IS_SPI + ---help--- + Use Quad SPI in SPI mode + +endif + config SAMV7_RTC bool "Real Time Clock (RTC)" default n diff --git a/arch/arm/src/samv7/Make.defs b/arch/arm/src/samv7/Make.defs index c41cef1403..f7515d6ae3 100644 --- a/arch/arm/src/samv7/Make.defs +++ b/arch/arm/src/samv7/Make.defs @@ -123,7 +123,9 @@ ifeq ($(CONFIG_SAMV7_SPI_SLAVE),y) CHIP_CSRCS += sam_spi_slave.c endif -ifeq ($(CONFIG_SAMV7_QSPI),y) +ifeq ($(CONFIG_SAMV7_QSPI_SPI_MODE),y) +CHIP_CSRCS += sam_qspi_spi.c +else ifeq ($(CONFIG_SAMV7_QSPI),y) CHIP_CSRCS += sam_qspi.c endif diff --git a/arch/arm/src/samv7/hardware/sam_qspi.h b/arch/arm/src/samv7/hardware/sam_qspi.h index cc39cb2b57..a6030e3740 100644 --- a/arch/arm/src/samv7/hardware/sam_qspi.h +++ b/arch/arm/src/samv7/hardware/sam_qspi.h @@ -30,7 +30,7 @@ #include "hardware/sam_memorymap.h" -#if SAMV7_NQSPI > 0 +#if SAMV7_NQSPI > 0 || SAMV7_NQSPI_SPI > 0 /**************************************************************************** * Pre-processor Definitions @@ -112,6 +112,7 @@ /* QSPI Mode Register */ +#define QSPI_MR_SPI (0 << 0) /* Bit 0: SPI Master Mode */ #define QSPI_MR_SMM (1 << 0) /* Bit 0: Serial Memory Mode */ #define QSPI_MR_LLB (1 << 1) /* Bit 1: Local Loopback Enable */ #define QSPI_MR_WDRBT (1 << 2) /* Bit 2: Wait Data Read Before Transfer */ diff --git a/arch/arm/src/samv7/sam_qspi.c b/arch/arm/src/samv7/sam_qspi.c index cc04543841..bffa1ba381 100644 --- a/arch/arm/src/samv7/sam_qspi.c +++ b/arch/arm/src/samv7/sam_qspi.c @@ -55,7 +55,7 @@ #include "hardware/sam_qspi.h" #include "hardware/sam_pinmap.h" -#ifdef CONFIG_SAMV7_QSPI +#if defined(CONFIG_SAMV7_QSPI) && !defined(CONFIG_SAMV7_QSPI_SPI_MODE) /**************************************************************************** * Pre-processor Definitions diff --git a/arch/arm/src/samv7/sam_qspi_spi.c b/arch/arm/src/samv7/sam_qspi_spi.c new file mode 100644 index 0000000000..9fe93745f5 --- /dev/null +++ b/arch/arm/src/samv7/sam_qspi_spi.c @@ -0,0 +1,864 @@ +/**************************************************************************** + * arch/arm/src/samv7/sam_qspi_spi.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "arm_internal.h" +#include "arm_arch.h" + +#include "sam_gpio.h" +#include "sam_xdmac.h" +#include "sam_periphclks.h" +#include "sam_qspi_spi.h" +#include "hardware/sam_pmc.h" +#include "hardware/sam_xdmac.h" +#include "hardware/sam_qspi.h" +#include "hardware/sam_pinmap.h" + +#ifdef CONFIG_SAMV7_QSPI_SPI_MODE + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Clocking *****************************************************************/ + +/* The SPI 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 */ + +struct sam_spics_s +{ + struct spi_dev_s spidev; /* Externally visible part of the SPI interface */ + 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) */ +}; + +/* Type of board-specific SPI status function */ + +typedef void (*select_t)(uint32_t devid, bool selected); + +/* The overall state of one SPI controller */ + +struct sam_spidev_s +{ + uint32_t base; /* SPI controller register base address */ + sem_t spisem; /* Assures mutually exclusive access to SPI */ + select_t select; /* SPI select call-out */ + bool initialized; /* TRUE: Controller has been initialized */ + bool escape_lastxfer; /* Don't set LASTXFER-Bit in the next transfer */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline uint32_t qspi_getreg(struct sam_spidev_s *spi, + unsigned int offset); +static inline void qspi_putreg(struct sam_spidev_s *spi, uint32_t value, + unsigned int offset); + +static inline void qspi_flush(struct sam_spidev_s *spi); + +/* SPI master methods */ + +static int qspi_spi_lock(struct spi_dev_s *dev, bool lock); +static void qspi_spi_select(struct spi_dev_s *dev, uint32_t devid, + bool selected); +static uint32_t qspi_spi_setfrequency(struct spi_dev_s *dev, + uint32_t frequency); +static void qspi_spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode); +static void qspi_spi_setbits(struct spi_dev_s *dev, int nbits); +static uint32_t qspi_spi_send(struct spi_dev_s *dev, uint32_t wd); +static void qspi_spi_exchange(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords); +#ifndef CONFIG_SPI_EXCHANGE +static void qspi_spi_sndblock(struct spi_dev_s *dev, const void *buffer, + size_t nwords); +static void qspi_spi_recvblock(struct spi_dev_s *dev, void *buffer, + size_t nwords); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* QSPI_SPI driver operations */ + +static const struct spi_ops_s g_spiops = +{ + .lock = qspi_spi_lock, + .select = qspi_spi_select, + .setfrequency = qspi_spi_setfrequency, + .setmode = qspi_spi_setmode, + .setbits = qspi_spi_setbits, + .status = sam_qspi_status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = sam_qspi_cmddata, +#endif + .send = qspi_spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = qspi_spi_exchange, +#else + .sndblock = qspi_spi_sndblock, + .recvblock = qspi_spi_recvblock, +#endif + .registercallback = 0, /* Not implemented */ +}; + +/* This is the overall state of the SPI0 controller */ + +static struct sam_spidev_s g_spidev = +{ + .base = SAM_QSPI_BASE, + .select = sam_qspi_select, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: qspi_spi_getreg + * + * Description: + * Read a QSPI register + * + ****************************************************************************/ + +static inline uint32_t qspi_getreg(struct sam_spidev_s *spi, + unsigned int offset) +{ + uint32_t address = spi->base + offset; + uint32_t value = getreg32(address); + + return value; +} + +/**************************************************************************** + * Name: qspi_putreg + * + * Description: + * Write a value to a QSPI register + * + ****************************************************************************/ + +static inline void qspi_putreg(struct sam_spidev_s *spi, uint32_t value, + unsigned int offset) +{ + uint32_t address = spi->base + offset; + + putreg32(value, address); +} + +/**************************************************************************** + * Name: qspi_flush + * + * Description: + * Make sure that there are now dangling SPI transfer in progress + * + * Input Parameters: + * spi - SPI controller state + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void qspi_flush(struct sam_spidev_s *spi) +{ + /* Make sure the no TX activity is in progress... waiting if necessary */ + + while ((qspi_getreg(spi, SAM_QSPI_SR_OFFSET) & QSPI_INT_TXEMPTY) == 0); + + /* Then make sure that there is no pending RX data .. reading as + * discarding as necessary. + */ + + while ((qspi_getreg(spi, SAM_QSPI_SR_OFFSET) & QSPI_INT_RDRF) != 0) + { + qspi_getreg(spi, SAM_QSPI_RDR_OFFSET); + } +} + +/**************************************************************************** + * Name: qspi_spi_lock + * + * Description: + * On SPI buses where there are multiple devices, it will be necessary to + * lock SPI 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 SPI bus, the caller should then also call the setfrequency, + * setbits, and setmode methods to make sure that the SPI is properly + * configured for the device. If the SPI bus is being shared, then it + * may have been left in an incompatible state. + * + * Input Parameters: + * dev - Device-specific state data + * lock - true: Lock spi bus, false: unlock SPI bus + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int qspi_spi_lock(struct spi_dev_s *dev, bool lock) +{ + struct sam_spidev_s *spi = &g_spidev; + int ret; + + spiinfo("lock=%d\n", lock); + if (lock) + { + ret = nxsem_wait_uninterruptible(&spi->spisem); + } + else + { + ret = nxsem_post(&spi->spisem); + } + + return ret; +} + +/**************************************************************************** + * Name: qspi_spi_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 SPI frequency requested + * + * Returned Value: + * Returns the actual frequency selected + * + ****************************************************************************/ + +static void qspi_spi_select(struct spi_dev_s *dev, uint32_t devid, + bool selected) +{ + struct sam_spidev_s *spi = &g_spidev; + + /* QSPI has just one CS so there is no need to perform any operation */ + + spi->select(devid, selected); +} + +/**************************************************************************** + * Name: qspi_spi_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_spi_setfrequency(struct spi_dev_s *dev, + uint32_t frequency) +{ + struct sam_spics_s *spics = (struct sam_spics_s *)dev; + struct sam_spidev_s *spi = &g_spidev; + uint32_t actual; + uint32_t scbr; + uint32_t dlybct; + uint32_t regval; + + spiinfo("frequency=%ld\n", frequency); + + /* Check if the requested frequency is the same as the frequency + * selection + */ + + if (spics->frequency == frequency) + { + /* We are already at this frequency. Return the actual. */ + + return spics->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(spi, SAM_QSPI_SCR_OFFSET); + regval &= ~(QSPI_SCR_SCBR_MASK | QSPI_SCR_DLYBS_MASK); + regval |= (scbr - 1) << QSPI_SCR_SCBR_SHIFT; + + qspi_putreg(spi, 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) / SPI_CLK + * + * For a 5uS delay: + * + * DLYBCT = SPI_CLK * 0.000005 / 32 = SPI_CLK / 200000 / 32 + */ + + dlybct = SAM_QSPI_CLOCK / 200000 / 32; + regval = qspi_getreg(spi, SAM_QSPI_MR_OFFSET); + regval &= ~(QSPI_MR_DLYBCT_MASK); + regval |= dlybct << QSPI_MR_DLYBCT_SHIFT; + qspi_putreg(spi, regval, SAM_QSPI_MR_OFFSET); + + /* Calculate the new actual frequency */ + + actual = SAM_QSPI_CLOCK / scbr; + spiinfo("SCBR=%ld actual=%ld\n", scbr, actual); + + /* Save the frequency setting */ + + spics->frequency = frequency; + spics->actual = actual; + + spiinfo("Frequency %ld->%ld\n", frequency, actual); + return actual; +} + +/**************************************************************************** + * Name: qspi_spi_setmode + * + * Description: + * Set the SPI mode. Optional. See enum spi_mode_e for mode definitions + * + * Input Parameters: + * dev - Device-specific state data + * mode - The SPI mode requested + * + * Returned Value: + * none + * + ****************************************************************************/ + +static void qspi_spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) +{ + struct sam_spics_s *spics = (struct sam_spics_s *)dev; + struct sam_spidev_s *spi = &g_spidev; + uint32_t regval; + + spiinfo("mode=%d\n", mode); + + /* Has the mode changed? */ + + if (mode != spics->mode) + { + /* Yes... Set the mode appropriately: + * + * MODE + * SPI CPOL NCPHA + * 0 0 1 + * 1 0 0 + * 2 1 1 + * 3 1 0 + */ + + regval = qspi_getreg(spi, SAM_QSPI_SCR_OFFSET); + regval &= ~(QSPI_SCR_CPOL | QSPI_SCR_CPHA); + + switch (mode) + { + case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */ + break; + + case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */ + regval |= QSPI_SCR_CPHA; + break; + + case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */ + regval |= QSPI_SCR_CPOL; + break; + + case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */ + regval |= (QSPI_SCR_CPOL | QSPI_SCR_CPHA); + break; + + default: + DEBUGASSERT(FALSE); + return; + } + + qspi_putreg(spi, regval, SAM_QSPI_SCR_OFFSET); + + /* Save the mode so that subsequent re-configurations will be faster */ + + spics->mode = mode; + } +} + +/**************************************************************************** + * Name: qspi_spi_setbits + * + * Description: + * Set the number if bits per word. + * + * Input Parameters: + * dev - Device-specific state data + * nbits - The number of bits requested + * + * Returned Value: + * none + * + ****************************************************************************/ + +static void qspi_spi_setbits(struct spi_dev_s *dev, int nbits) +{ + struct sam_spics_s *spics = (struct sam_spics_s *)dev; + struct sam_spidev_s *spi = &g_spidev; + uint32_t regval; + + spiinfo("nbits=%d\n", nbits); + DEBUGASSERT(nbits > 7 && nbits < 17); + + /* Has the number of bits changed? */ + + if (nbits != spics->nbits) + { + /* Yes... Set number of bits appropriately */ + + regval = qspi_getreg(spi, SAM_QSPI_MR_OFFSET); + regval &= ~QSPI_MR_NBBITS_MASK; + regval |= QSPI_MR_NBBITS(nbits); + qspi_putreg(spi, regval, SAM_QSPI_MR_OFFSET); + + /* Save the selection so that subsequent re-configurations will be + * faster. + */ + + spics->nbits = nbits; + } +} + +/**************************************************************************** + * Name: qspi_spi_send + * + * Description: + * Exchange one word on SPI + * + * Input Parameters: + * dev - Device-specific state data + * wd - The word to send. the size of the data is determined by the + * number of bits selected for the SPI interface. + * + * Returned Value: + * response + * + ****************************************************************************/ + +static uint32_t qspi_spi_send(struct spi_dev_s *dev, uint32_t wd) +{ + struct sam_spics_s *spics = (struct sam_spics_s *)dev; + if (spics->nbits <= 8) + { + uint8_t txbyte; + uint8_t rxbyte; + + txbyte = (uint8_t)wd; + rxbyte = (uint8_t)0; + qspi_spi_exchange(dev, &txbyte, &rxbyte, 1); + + spiinfo("Sent %02x received %02x\n", txbyte, rxbyte); + return (uint32_t)rxbyte; + } + else + { + uint16_t txword; + uint16_t rxword; + + txword = (uint16_t)wd; + rxword = (uint16_t)0; + qspi_spi_exchange(dev, &txword, &rxword, 1); + + spiinfo("Sent %02x received %02x\n", txword, rxword); + return (uint32_t)rxword; + } +} + +/**************************************************************************** + * Name: qspi_spi_exchange + * + * Description: + * Exchange a block of data from SPI. + * + * 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 SPI 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 + * + ****************************************************************************/ + +static void qspi_spi_exchange(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords) +{ + struct sam_spics_s *spics = (struct sam_spics_s *)dev; + struct sam_spidev_s *spi = &g_spidev; + uint32_t data; + uint16_t *rxptr16; + uint16_t *txptr16; + uint8_t *rxptr8; + uint8_t *txptr8; + + spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); + + /* Set up working pointers */ + + if (spics->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(spi); + + /* Loop, sending each word in the user-provided data buffer. + * + * Note 1: Good SPI 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; + } + + /* Wait for any previous data written to the TDR to be transferred + * to the serializer. + */ + + while ((qspi_getreg(spi, SAM_QSPI_SR_OFFSET) & QSPI_INT_TDRE) == 0); + + /* Write the data to transmitted to the Transmit Data Register (TDR) */ + + qspi_putreg(spi, 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(spi, SAM_QSPI_SR_OFFSET) & QSPI_INT_RDRF) == 0); + + /* Read the received data from the SPI Data Register. */ + + data = qspi_getreg(spi, SAM_QSPI_RDR_OFFSET); + if (rxptr8) + { + *rxptr8++ = (uint8_t)data; + } + else if (rxptr16) + { + *rxptr16++ = (uint16_t)data; + } + } +} + +/**************************************************************************** + * Name: qspi_spi_sndblock + * + * Description: + * Send a block of data on SPI + * + * Input Parameters: + * dev - Device-specific state data + * 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. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI 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 + * + ****************************************************************************/ + +#ifndef CONFIG_SPI_EXCHANGE +static void qspi_spi_sndblock(struct spi_dev_s *dev, const void *buffer, + size_t nwords) +{ + /* spi_exchange can do this. */ + + qspi_spi_exchange(dev, buffer, NULL, nwords); +} + +/**************************************************************************** + * Name: qspi_spi_recvblock + * + * Description: + * Revice a block of data from SPI + * + * Input Parameters: + * dev - Device-specific state data + * 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 SPI 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 + * + ****************************************************************************/ + +static void qspi_spi_recvblock(struct spi_dev_s *dev, void *buffer, + size_t nwords) +{ + /* spi_exchange can do this. */ + + qspi_spi_exchange(dev, NULL, buffer, nwords); +} +#endif /* CONFIG_SPI_EXCHANGE */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_qspi_spi_initialize + * + * Description: + * Initialize the selected SPI port in master mode + * + * Input Parameters: + * cs - Chip select number (identifying the "logical" SPI port) + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +FAR struct spi_dev_s *sam_qspi_spi_initialize(int intf) +{ + FAR struct sam_spidev_s *spi; + FAR struct sam_spics_s *spics; + irqstate_t flags; + uint32_t regval; + + /* The supported SAM parts have only a single QSPI port */ + + spiinfo("intf: %d\n", intf); + DEBUGASSERT(intf >= 0 && intf < SAMV7_NQSPI_SPI); + + /* Allocate a new state structure for this chip select. */ + + spics = (struct sam_spics_s *)kmm_zalloc(sizeof(struct sam_spics_s)); + if (!spics) + { + spierr("ERROR: Failed to allocate a chip select structure\n"); + return NULL; + } + + /* Select the SPI operations */ + + spics->spidev.ops = &g_spiops; + + /* Get the SPI device structure associated with the chip select */ + + spi = &g_spidev; + + /* Has the SPI hardware been initialized? */ + + if (!spi->initialized) + { + flags = enter_critical_section(); + sam_qspi_enableclk(); + + /* Configure multiplexed pins as connected on the board. Chip + * select pins must be selected by board-specific logic. + */ + + sam_configgpio(GPIO_QSPI_IO0); /* MOSI */ + sam_configgpio(GPIO_QSPI_IO1); /* MISO */ + sam_configgpio(GPIO_QSPI_SCK); + + /* Disable write protection */ + + qspi_putreg(spi, SAM_QSPI_WPCR_OFFSET, QSPI_WPCR_WPKEY); + + /* Disable QSPI before configuring it */ + + qspi_putreg(spi, QSPI_CR_QSPIDIS, SAM_QSPI_CR_OFFSET); + + /* Execute a software reset of the QSPI (twice) */ + + qspi_putreg(spi, QSPI_CR_SWRST, SAM_QSPI_CR_OFFSET); + qspi_putreg(spi, QSPI_CR_SWRST, SAM_QSPI_CR_OFFSET); + leave_critical_section(flags); + + /* Configure the QSPI mode register - select SPI mode */ + + qspi_putreg(spi, QSPI_MR_SPI, SAM_QSPI_MR_OFFSET); + + /* And enable the SPI */ + + qspi_putreg(spi, QSPI_CR_QSPIEN, SAM_QSPI_CR_OFFSET); + up_mdelay(20); + + /* Flush any pending transfers */ + + qspi_getreg(spi, SAM_QSPI_SR_OFFSET); + qspi_getreg(spi, SAM_QSPI_RDR_OFFSET); + + /* Initialize the SPI semaphore that enforces mutually exclusive + * access to the SPI registers. + */ + + nxsem_init(&spi->spisem, 0, 1); + spi->escape_lastxfer = false; + spi->initialized = true; + } + + /* Set to mode=0 and nbits=8 and impossible frequency. The SPI will only + * be reconfigured if there is a change. + */ + + regval = qspi_getreg(spi, SAM_QSPI_SCR_OFFSET); + regval &= ~(QSPI_SCR_CPOL | QSPI_SCR_CPHA); + qspi_putreg(spi, regval, SAM_QSPI_SCR_OFFSET); + spics->mode = 0; + + regval = qspi_getreg(spi, SAM_QSPI_MR_OFFSET); + regval |= QSPI_MR_NBBITS_8BIT; + qspi_putreg(spi, regval, SAM_QSPI_MR_OFFSET); + spics->nbits = 8; + + spics->frequency = 0; + + return &spics->spidev; +} +#endif /* CONFIG_SAMV7_QSPI_SPI_MODE */ diff --git a/arch/arm/src/samv7/sam_qspi_spi.h b/arch/arm/src/samv7/sam_qspi_spi.h new file mode 100644 index 0000000000..858e20b710 --- /dev/null +++ b/arch/arm/src/samv7/sam_qspi_spi.h @@ -0,0 +1,165 @@ +/**************************************************************************** + * arch/arm/src/samv7/sam_qspi_spi.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMV7_SAM_QSPI_SPI_H +#define __ARCH_ARM_SRC_SAMV7_SAM_QSPI_SPI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "chip.h" +#include "sam_config.h" + +#ifdef CONFIG_SAMV7_QSPI_SPI_MODE + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_qspi_spi_initialize + * + * Description: + * Initialize the selected QSPI port in SPI master mode + * + * Input Parameters: + * intf - Interface number(must be zero) + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct spi_dev_s; +FAR struct spi_dev_s *sam_qspi_spi_initialize(int intf); + +/**************************************************************************** + * Name: sam_qspi_select + * + * Description: + * PIO chip select pins may be programmed by the board specific logic in + * one of two different ways. First, the pins may be programmed as SPI + * peripherals. In that case, the pins are completely controlled by the + * SPI driver. This method still needs to be provided, but it may be only + * a stub. + * + * An alternative way to program the PIO chip select pins is as a normal + * PIO output. In that case, the automatic control of the CS pins is + * bypassed and this function must provide control of the chip select. + * NOTE: In this case, the PIO output pin does *not* have to be the + * same as the NPCS pin normal associated with the chip select number. + * + * Input Parameters: + * dev - SPI device info + * devid - Identifies the (logical) device + * selected - TRUE:Select the device, FALSE:De-select the device + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sam_qspi_select(uint32_t devid, bool selected); + +/**************************************************************************** + * Name: sam_qspi_status + * + * Description: + * Return status information associated with the SPI device. + * + * Input Parameters: + * devid - Identifies the (logical) device + * + * Returned Value: + * Bit-encoded SPI status (see include/nuttx/spi/spi.h. + * + ****************************************************************************/ + +uint8_t sam_qspi_status(FAR struct spi_dev_s *dev, uint32_t devid); + +/**************************************************************************** + * Name: sam_qspi_cmddata + * + * Description: + * Some SPI devices require an additional control to determine if the SPI + * data being sent is a command or is data. If CONFIG_SPI_CMDDATA then + * this function will be called to different be command and data transfers. + * + * This is often needed, for example, by LCD drivers. Some LCD hardware + * may be configured to use 9-bit data transfers with the 9th bit + * indicating command or data. That same hardware may be configurable, + * instead, to use 8-bit data but to require an additional, board- + * specific PIO control to distinguish command and data. This function + * would be needed in that latter case. + * + * Input Parameters: + * dev - SPI device info + * devid - Identifies the (logical) device + * + * Returned Value: + * Zero on success; a negated errno on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_CMDDATA +int sam_qspi_cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif /* CONFIG_SPI_CMDDATA */ + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_SAMV7_QSPI_SPI_MODE */ +#endif /* __ARCH_ARM_SRC_SAMV7_SAM_QSPI_SPI_H */