You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2024/01/23 20:16:41 UTC

(nuttx) branch master updated: samv7: add support for SPI mode in USART peripheral

This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 55ec92e181 samv7: add support for SPI mode in USART peripheral
55ec92e181 is described below

commit 55ec92e181165e3f34c12c7a3e719709c38409bd
Author: Michal Lenc <mi...@seznam.cz>
AuthorDate: Mon Jan 22 12:34:28 2024 +0100

    samv7: add support for SPI mode in USART peripheral
    
    USART peripheral can work in SPI mode as well. This commit adds support
    for such functionality. Only 1 slave device is supported by the
    peripheral therefore board level does not have to ensure correct CS
    setup.
    
    The usage of the peripheral is the same as with other SPI drivers.
    
    Signed-off-by: Michal Lenc <mi...@seznam.cz>
---
 Documentation/platforms/arm/samv7/index.rst |  26 +-
 arch/arm/src/samv7/Kconfig                  |  49 ++
 arch/arm/src/samv7/Make.defs                |   4 +
 arch/arm/src/samv7/sam_serial_spi.c         | 839 ++++++++++++++++++++++++++++
 arch/arm/src/samv7/sam_serial_spi.h         | 109 ++++
 5 files changed, 1021 insertions(+), 6 deletions(-)

diff --git a/Documentation/platforms/arm/samv7/index.rst b/Documentation/platforms/arm/samv7/index.rst
index 79acccde8c..e8a0ceed2f 100644
--- a/Documentation/platforms/arm/samv7/index.rst
+++ b/Documentation/platforms/arm/samv7/index.rst
@@ -284,14 +284,28 @@ The peripheral implements four timer counter modules, each supporting three inde
 Universal Synchronous Asynchronous Receiver Transceiver (USART)
 ---------------------------------------------------------------
 
-The MCU supports both UART and USART controllers. USART can be also used in RS-485 mode (enabled
-by ``CONFIG_SAMV7_USARTx_RS485MODE`` option) or can be used with RX DMA support. For this purpose it
-is required to configure idle bus timeout value in ``CONFIG_SAMV7_SERIAL_DMA_TIMEOUT``. This option
-ensures data are read from the DMA buffer even if it is not full yet. TX DMA support is not implemented
-as well as entire DMA support for UART peripheral.
+The MCU supports both UART and USART controllers. USART peripheral can be used with RX DMA support.
+For this purpose it is required to configure idle bus timeout value in ``CONFIG_SAMV7_SERIAL_DMA_TIMEOUT``.
+This option ensures data are read from the DMA buffer even if it is not full yet. TX DMA support is not
+implemented as well as entire DMA support for UART peripheral.
+
+There are several modes in which USART peripheral can operate (ISO7816, IrDA, RS485, SPI, LIN and LON).
+Currently RS485 and SPI master are supported by NuttX.
+
+RS-485 mode is enabled by ``CONFIG_SAMV7_USARTx_RS485MODE`` option (``CONFIG_SAMV7_USART2_SERIALDRIVER``
+has to be true). In this case RTS pin is set to logical 1 before the serial driver is opened and board
+specific logic is required to set it to logical zero. This has to be done in board initialization. Once
+the driver is opened for the first time, architecture layer takes care of correct settings of RTS pin.
+There is no additional requirement for driver initialization, the process is the same as with serial mode.
+
+SPI master (host) mode is enabled by ``CONFIG_SAMV7_USARTx_SPI_MASTER`` option. In this mode USART emulates
+SPI peripheral with one slave (client) device (more slaves are not supported by the peripheral). The interface
+with the driver is the same as with other SPI drivers but BSP layer does not have to support chip selection.
+Command/data transfers are also not supported by the peripheral. Driver for SPI master mode is initialized
+by :c:func:`sam_serial_spi_initialize` with port number as an argument.
 
 USART/UART can be also used to emulate 1 wire interface. SAMv7 MCUs do not have build in support for
-1 wire therefore external hardware as TX/RX connection or optical isolation might be required. Selecting
+1 wire, therefore external hardware as TX/RX connection or optical isolation might be required. Selecting
 ``CONFIG_SAMV7_UARTx_1WIREDRIVER`` enables 1 wire driver and sets USART/UART peripheral to this mode.
 Output pins are configured as if serial mode was selected plus TX is open drain. SAMv7 part of the driver
 is initialized by :c:func:`sam_1wireinitialize` with port number as an argument.
diff --git a/arch/arm/src/samv7/Kconfig b/arch/arm/src/samv7/Kconfig
index 84bfec41ec..d072fe81f5 100644
--- a/arch/arm/src/samv7/Kconfig
+++ b/arch/arm/src/samv7/Kconfig
@@ -360,6 +360,10 @@ config SAMV7_1WIREDRIVER
 	bool
 	default n
 
+config SAMV7_USART_IS_SPI_MASTER
+	bool
+	default n
+
 config SAMV7_HAVE_USART0
 	bool
 	default n
@@ -1339,6 +1343,21 @@ config SAMV7_USART0_1WIREDRIVER
 	bool "1-Wire driver"
 	select SAMV7_1WIREDRIVER
 
+config SAMV7_USART0_SPI_MASTER
+	bool "USART0 as SPI Master driver"
+	select SAMV7_USART_IS_SPI_MASTER
+	---help---
+		Switch USART0 to SPI master (host) mode. In this mode USART is used
+		to emulate SPI peripheral. Pin configuration is following:
+
+			TXD - MOSI
+			RXD - MISO
+			SCK - SCK
+			RTS - NSS (chip select)
+
+		There is only one chip select therefore SPI master can be used to
+		communicate only with one slave device.
+
 endchoice # "USART0 Driver Configuration"
 
 if SAMV7_USART0_SERIALDRIVER
@@ -1376,6 +1395,21 @@ config SAMV7_USART1_1WIREDRIVER
 	bool "1-Wire driver"
 	select SAMV7_1WIREDRIVER
 
+config SAMV7_USART1_SPI_MASTER
+	bool "USART1 as SPI Master driver"
+	select SAMV7_USART_IS_SPI_MASTER
+	---help---
+		Switch USART1 to SPI master (host) mode. In this mode USART is used
+		to emulate SPI peripheral. Pin configuration is following:
+
+			TXD - MOSI
+			RXD - MISO
+			SCK - SCK
+			RTS - NSS (chip select)
+
+		There is only one chip select therefore SPI master can be used to
+		communicate only with one slave device.
+
 endchoice # "USART1 Driver Configuration"
 
 if SAMV7_USART1_SERIALDRIVER
@@ -1413,6 +1447,21 @@ config SAMV7_USART2_1WIREDRIVER
 	bool "1-Wire driver"
 	select SAMV7_1WIREDRIVER
 
+config SAMV7_USART2_SPI_MASTER
+	bool "USART2 as SPI Master driver"
+	select SAMV7_USART_IS_SPI_MASTER
+	---help---
+		Switch USART2 to SPI master (host) mode. In this mode USART is used
+		to emulate SPI peripheral. Pin configuration is following:
+
+			TXD - MOSI
+			RXD - MISO
+			SCK - SCK
+			RTS - NSS (chip select)
+
+		There is only one chip select therefore SPI master can be used to
+		communicate only with one slave device.
+
 endchoice # "USART2 Driver Configuration"
 
 if SAMV7_USART2_SERIALDRIVER
diff --git a/arch/arm/src/samv7/Make.defs b/arch/arm/src/samv7/Make.defs
index e0dfec2484..7fb0d80960 100644
--- a/arch/arm/src/samv7/Make.defs
+++ b/arch/arm/src/samv7/Make.defs
@@ -67,6 +67,10 @@ ifeq ($(CONFIG_SAMV7_1WIREDRIVER),y)
 CHIP_CSRCS += sam_1wire.c
 endif
 
+ifeq ($(CONFIG_SAMV7_USART_IS_SPI_MASTER),y)
+CHIP_CSRCS += sam_serial_spi.c
+endif
+
 ifeq ($(CONFIG_SAMV7_SPI_MASTER),y)
 CHIP_CSRCS += sam_spi.c
 endif
diff --git a/arch/arm/src/samv7/sam_serial_spi.c b/arch/arm/src/samv7/sam_serial_spi.c
new file mode 100644
index 0000000000..a8bbd1efa4
--- /dev/null
+++ b/arch/arm/src/samv7/sam_serial_spi.c
@@ -0,0 +1,839 @@
+/****************************************************************************
+ * arch/arm/src/samv7/sam_serial_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 <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/signal.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/spi/spi.h>
+
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "sam_config.h"
+
+#include "hardware/sam_pinmap.h"
+#include "hardware/sam_uart.h"
+#include "sam_gpio.h"
+#include "sam_periphclks.h"
+#include "sam_serial_spi.h"
+
+#ifdef CONFIG_SAMV7_USART_IS_SPI_MASTER
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define FAST_USART_CLOCK   BOARD_MCK_FREQUENCY
+#define SLOW_USART_CLOCK   (BOARD_MCK_FREQUENCY >> 3)
+
+#define SERIAL_SPI_MAX_DIVIDER 65534
+#define SERIAL_SPI_MIN_DIVIDER 6
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct sam_serial_spi_pins_s
+{
+  uint32_t mosi;
+  uint32_t miso;
+  uint32_t sck;
+  uint32_t nss;
+};
+
+struct sam_serial_spi_s
+{
+  struct sam_serial_spi_pins_s pins;
+  uint32_t base;               /* SPI 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 or 9) */
+  mutex_t spilock;             /* Assures mutually exclusive access to SPI */
+  bool initialized;            /* TRUE: Controller has been initialized */
+};
+
+/* The overall state of one SPI controller */
+
+struct sam_spidev_s
+{
+  const struct spi_ops_s *ops;
+  struct sam_serial_spi_s  *priv;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static inline uint32_t serial_getreg(struct sam_serial_spi_s *priv,
+                                     int offset);
+static inline void serial_putreg(struct sam_serial_spi_s *priv, int offset,
+                                 uint32_t value);
+static inline void serial_flush(struct sam_serial_spi_s *priv);
+
+/* SPI master methods */
+
+static int  serial_spi_lock(struct spi_dev_s *dev, bool lock);
+static void serial_spi_select(struct spi_dev_s *dev, uint32_t devid,
+                            bool selected);
+static uint32_t serial_spi_setfrequency(struct spi_dev_s *dev,
+                                      uint32_t frequency);
+static void serial_spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode);
+static void serial_spi_setbits(struct spi_dev_s *dev, int nbits);
+static uint8_t serial_spi_status(struct spi_dev_s *dev, uint32_t devid);
+static uint32_t serial_spi_send(struct spi_dev_s *dev, uint32_t wd);
+static void serial_spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+                              void *rxbuffer, size_t nwords);
+#ifndef CONFIG_SPI_EXCHANGE
+static void serial_spi_sndblock(struct spi_dev_s *dev, const void *buffer,
+                                size_t nwords);
+static void serial_spi_recvblock(struct spi_dev_s *dev, void *buffer,
+                                 size_t nwords);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* SERIAL_SPI driver operations */
+
+static const struct spi_ops_s g_spiops =
+{
+  .lock              = serial_spi_lock,
+  .select            = serial_spi_select,
+  .setfrequency      = serial_spi_setfrequency,
+  .setmode           = serial_spi_setmode,
+  .setbits           = serial_spi_setbits,
+  .status            = serial_spi_status,
+#ifdef CONFIG_SPI_CMDDATA
+  .cmddata           = NULL,
+#endif
+  .send              = serial_spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+  .exchange          = serial_spi_exchange,
+#else
+  .sndblock          = serial_spi_sndblock,
+  .recvblock         = serial_spi_recvblock,
+#endif
+  .registercallback  = NULL,
+};
+
+/* This is the overall state of the SPI0 controller */
+
+#ifdef CONFIG_SAMV7_USART0_SPI_MASTER
+static struct sam_serial_spi_s sam_serial0spi_priv =
+{
+  .pins         =
+  {
+    .mosi       = GPIO_USART0_TXD,
+    .miso       = GPIO_USART0_RXD,
+    .sck        = GPIO_USART0_SCK,
+    .nss        = GPIO_USART0_RTS,
+  },
+  .base         = SAM_USART0_BASE,
+  .actual       = 0,
+  .mode         = 0,
+  .nbits        = 0,
+  .spilock      = NXMUTEX_INITIALIZER,
+  .initialized  = false,
+};
+#endif
+
+#ifdef CONFIG_SAMV7_USART1_SPI_MASTER
+static struct sam_serial_spi_s sam_serial1spi_priv =
+{
+  .pins         =
+  {
+    .mosi       = GPIO_USART1_TXD,
+    .miso       = GPIO_USART1_RXD,
+    .sck        = GPIO_USART1_SCK,
+    .nss        = GPIO_USART1_RTS,
+  },
+  .base         = SAM_USART1_BASE,
+  .actual       = 0,
+  .mode         = 0,
+  .nbits        = 0,
+  .spilock      = NXMUTEX_INITIALIZER,
+  .initialized  = false,
+};
+#endif
+
+#ifdef CONFIG_SAMV7_USART2_SPI_MASTER
+static struct sam_serial_spi_s sam_serial2spi_priv =
+{
+  .pins         =
+  {
+    .mosi       = GPIO_USART2_TXD,
+    .miso       = GPIO_USART2_RXD,
+    .sck        = GPIO_USART2_SCK,
+    .nss        = GPIO_USART2_RTS,
+  },
+  .base         = SAM_USART2_BASE,
+  .actual       = 0,
+  .mode         = 0,
+  .nbits        = 0,
+  .spilock      = NXMUTEX_INITIALIZER,
+  .initialized  = false,
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static inline uint32_t serial_getreg(struct sam_serial_spi_s *priv,
+                                     int offset)
+{
+  return getreg32(priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: sam_serialout
+ ****************************************************************************/
+
+static inline void serial_putreg(struct sam_serial_spi_s *priv, int offset,
+                                 uint32_t value)
+{
+  putreg32(value, priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: serial_flush
+ ****************************************************************************/
+
+static inline void serial_flush(struct sam_serial_spi_s *priv)
+{
+  uint32_t status;
+
+  /* Make sure the no TX activity is in progress... waiting if necessary */
+
+  status = serial_getreg(priv, SAM_UART_SR_OFFSET);
+  while ((status & UART_INT_TXRDY) == 0)
+    {
+      nxsig_usleep(100);
+      status = serial_getreg(priv, SAM_UART_SR_OFFSET);
+    }
+
+  /* Then make sure that there is no pending RX data .. reading as
+   * discarding as necessary.
+   */
+
+  while ((serial_getreg(priv, SAM_UART_SR_OFFSET) & UART_INT_RXRDY) != 0)
+    {
+       serial_getreg(priv, SAM_UART_RHR_OFFSET);
+    }
+}
+
+/****************************************************************************
+ * Name: serial_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 serial_spi_lock(struct spi_dev_s *dev, bool lock)
+{
+  struct sam_spidev_s *spi = (struct sam_spidev_s *)dev;
+  struct sam_serial_spi_s *priv = (struct sam_serial_spi_s *)spi->priv;
+  int ret;
+
+  spiinfo("lock=%d\n", lock);
+  if (lock)
+    {
+      ret = nxmutex_lock(&priv->spilock);
+    }
+  else
+    {
+      ret = nxmutex_unlock(&priv->spilock);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: serial_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
+ *   devid    - Device ID
+ *   selected - true if CS is selected
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void serial_spi_select(struct spi_dev_s *dev, uint32_t devid,
+                              bool selected)
+{
+  struct sam_spidev_s *spi = (struct sam_spidev_s *)dev;
+  struct sam_serial_spi_s *priv = (struct sam_serial_spi_s *)spi->priv;
+
+  /* There is only one CS. */
+
+  spiinfo("Chip select %d\n", selected);
+
+  if (selected)
+    {
+      serial_putreg(priv, SAM_UART_CR_OFFSET, UART_CR_FCS);
+    }
+  else
+    {
+      serial_putreg(priv, SAM_UART_CR_OFFSET, UART_CR_RCS);
+    }
+}
+
+/****************************************************************************
+ * Name: serial_spi_setfrequency
+ *
+ * Description:
+ *   Set the SPI frequency.
+ *
+ * Input Parameters:
+ *   dev -       Device-specific state data
+ *   frequency - The SPI frequency requested
+ *
+ * Returned Value:
+ *   Returns the actual frequency selected
+ *
+ ****************************************************************************/
+
+static uint32_t serial_spi_setfrequency(struct spi_dev_s *dev,
+                                        uint32_t frequency)
+{
+  struct sam_spidev_s *spi = (struct sam_spidev_s *)dev;
+  struct sam_serial_spi_s *priv = (struct sam_serial_spi_s *)spi->priv;
+  uint32_t intpart;
+  uint32_t regval;
+  uint32_t selected_clk;
+  uint32_t actual;
+
+  spiinfo("frequency=%ld\n", frequency);
+
+  if (priv->frequency == frequency)
+    {
+      return priv->actual;
+    }
+
+  /* Disable receiver and transmitter */
+
+  serial_putreg(priv, SAM_UART_CR_OFFSET, UART_CR_RXDIS | UART_CR_TXDIS);
+
+  /* Configure the SPI frequency/baud rate:
+   *
+   *   divisor = selected clock / baud rate
+   */
+
+  selected_clk = FAST_USART_CLOCK;
+  intpart = (selected_clk + (frequency >> 1)) / frequency;
+
+  if ((intpart & ~UART_BRGR_CD_MASK) != 0)
+    {
+      /* Use the divided USART clock */
+
+      selected_clk = SLOW_USART_CLOCK;
+      intpart = (selected_clk + (frequency >> 1)) /
+                frequency;
+
+      /* Re-select the clock source */
+
+      regval  = serial_getreg(priv, SAM_UART_MR_OFFSET);
+      regval &= ~UART_MR_USCLKS_MASK;
+      regval |= UART_MR_USCLKS_MCKDIV;
+      serial_putreg(priv, SAM_UART_MR_OFFSET, regval);
+
+      /* Value written in UART_BRGR_CD must be even to ensure a
+       * 50:50 mark of the SCK pin. This applies only if
+       * UART_MR_USCLKS_MCKDIV is selected
+       */
+
+      if (intpart % 2 != 0)
+        {
+          intpart += 1;
+        }
+    }
+
+  /* Value written in UART_BRGR_CD greater or equal to 6. */
+
+  if (intpart < SERIAL_SPI_MIN_DIVIDER)
+    {
+      intpart = SERIAL_SPI_MIN_DIVIDER;
+    }
+  else if (intpart > SERIAL_SPI_MAX_DIVIDER)
+    {
+      intpart = SERIAL_SPI_MAX_DIVIDER;
+    }
+
+  regval = UART_BRGR_CD(intpart);
+
+  serial_putreg(priv, SAM_UART_BRGR_OFFSET, regval);
+
+  /* Enable receiver & transmitter */
+
+  serial_putreg(priv, SAM_UART_CR_OFFSET, UART_CR_RXEN | UART_CR_TXEN);
+
+  actual = selected_clk / intpart;
+
+  spiinfo("Frequency %ld->%ld\n", priv->frequency, actual);
+
+  priv->frequency = frequency;
+  priv->actual    = actual;
+
+  return actual;
+}
+
+/****************************************************************************
+ * Name: serial_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 serial_spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode)
+{
+  struct sam_spidev_s *spi = (struct sam_spidev_s *)dev;
+  struct sam_serial_spi_s *priv = (struct sam_serial_spi_s *)spi->priv;
+  uint32_t regval;
+
+  spiinfo("mode=%d\n", mode);
+
+  /* Perform operation only if mode has changed. */
+
+  if (mode != priv->mode)
+    {
+      /* Yes. Set the mode:
+       *
+       * MODE
+       * SPI  CPOL NCPHA
+       *  0    0    1
+       *  1    0    0
+       *  2    1    1
+       *  3    1    0
+       */
+
+      regval  = serial_getreg(priv, SAM_UART_MR_OFFSET);
+      regval &= ~(UART_MR_CPOL | UART_MR_CPHA);
+
+      switch (mode)
+        {
+        case SPIDEV_MODE0: /* CPOL=0; CPHA=1 */
+          regval |= UART_MR_CPHA;
+          break;
+
+        case SPIDEV_MODE1: /* CPOL=0; CPHA=0 */
+          break;
+
+        case SPIDEV_MODE2: /* CPOL=1; CPHA=1 */
+          regval |= UART_MR_CPOL | UART_MR_CPHA;
+          break;
+
+        case SPIDEV_MODE3: /* CPOL=1; CPHA=0 */
+          regval |= UART_MR_CPOL;
+          break;
+
+        default:
+          DEBUGASSERT(FALSE);
+          return;
+        }
+
+      serial_putreg(priv, SAM_UART_MR_OFFSET, regval);
+
+      /* Save the mode so that subsequent re-configurations will be faster */
+
+      priv->mode = mode;
+    }
+}
+
+/****************************************************************************
+ * Name: serial_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 serial_spi_setbits(struct spi_dev_s *dev, int nbits)
+{
+  /*  Only 8 bit transfer is supported. */
+
+  spiinfo("Only 8 bit transfer is supported on USART SPI.\n");
+}
+
+/****************************************************************************
+ * Name: serial_spi_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.
+ *
+ ****************************************************************************/
+
+static uint8_t serial_spi_status(struct spi_dev_s *dev, uint32_t devid)
+{
+  return 0;
+}
+
+/****************************************************************************
+ * Name: serial_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 serial_spi_send(struct spi_dev_s *dev, uint32_t wd)
+{
+  uint8_t txbyte;
+  uint8_t rxbyte;
+
+  txbyte = (uint8_t)wd;
+  rxbyte = (uint8_t)0;
+  serial_spi_exchange(dev, &txbyte, &rxbyte, 1);
+
+  spierr("Sent %02x received %02x\n", txbyte, rxbyte);
+  return (uint32_t)rxbyte;
+}
+
+/****************************************************************************
+ * Name: serial_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 serial_spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+                                void *rxbuffer, size_t nwords)
+{
+  struct sam_spidev_s *spi = (struct sam_spidev_s *)dev;
+  struct sam_serial_spi_s *priv = (struct sam_serial_spi_s *)spi->priv;
+  uint32_t data;
+  uint32_t status;
+  uint8_t *rxptr;
+  uint8_t *txptr;
+
+  spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
+
+  /* Set up working pointers */
+
+  rxptr  = (uint8_t *)rxbuffer;
+  txptr  = (uint8_t *)txbuffer;
+
+  /* Make sure that any previous transfer is flushed from the hardware */
+
+  serial_flush(priv);
+
+  for (; nwords > 0; nwords--)
+    {
+      /* Get the data to send (0xff if there is no data source). */
+
+      if (txptr)
+        {
+          data = (uint32_t)*txptr++;
+        }
+      else
+        {
+          data = 0xffff;
+        }
+
+      /* Wait for any previous data written to the TDR to be transferred
+       * to the serializer.
+       */
+
+      status = serial_getreg(priv, SAM_UART_SR_OFFSET);
+      while ((status & UART_INT_TXRDY) == 0)
+        {
+          nxsig_usleep(100);
+          status = serial_getreg(priv, SAM_UART_SR_OFFSET);
+        }
+
+      /* Write the data to transmitted to the Transmit Data Register (TDR) */
+
+      serial_putreg(priv, SAM_UART_THR_OFFSET, data);
+
+      /* Wait for the read data to be available in the RDR. */
+
+      status = serial_getreg(priv, SAM_UART_SR_OFFSET);
+      while ((status & UART_INT_RXRDY) == 0)
+        {
+          nxsig_usleep(100);
+          status = serial_getreg(priv, SAM_UART_SR_OFFSET);
+        }
+
+      /* Read the received data from the SPI Data Register. */
+
+      data = serial_getreg(priv, SAM_UART_RHR_OFFSET);
+      if (rxptr)
+        {
+          *rxptr++ = (uint8_t)data;
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: serial_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 serial_spi_sndblock(struct spi_dev_s *dev, const void *buffer,
+                                size_t nwords)
+{
+  /* spi_exchange can do this. */
+
+  serial_spi_exchange(dev, buffer, NULL, nwords);
+}
+
+/****************************************************************************
+ * Name: serial_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 serial_spi_recvblock(struct spi_dev_s *dev, void *buffer,
+                                 size_t nwords)
+{
+  /* spi_exchange can do this. */
+
+  serial_spi_exchange(dev, NULL, buffer, nwords);
+}
+#endif  /* CONFIG_SPI_EXCHANGE */
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sam_serial_spi_initialize
+ *
+ * Description:
+ *   Initialize the selected SPI port in master mode
+ *
+ * Input Parameters:
+ *   port - USART interface to be used (0-2)
+ *
+ * Returned Value:
+ *   Valid SPI device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct spi_dev_s *sam_serial_spi_initialize(int port)
+{
+  struct sam_serial_spi_s *priv;
+  struct sam_spidev_s *spi;
+  irqstate_t flags;
+  uint32_t regval;
+
+  spiinfo("Initializing USART%d as SPI\n", port);
+  DEBUGASSERT(port >= 0 && port < SAMV7_NUSART);
+
+  switch (port)
+    {
+#ifdef CONFIG_SAMV7_USART0_SPI_MASTER
+      case 0:
+        sam_usart0_enableclk();
+        priv = &sam_serial0spi_priv;
+        break;
+#endif
+#ifdef CONFIG_SAMV7_USART1_SPI_MASTER
+      case 1:
+        sam_usart1_enableclk();
+        priv = &sam_serial1spi_priv;
+        break;
+#endif
+#ifdef CONFIG_SAMV7_USART2_SPI_MASTER
+      case 2:
+        sam_usart2_enableclk();
+        priv = &sam_serial2spi_priv;
+        break;
+#endif
+      default:
+        spierr("ERROR: Incorrect port number %d\n", port);
+        return NULL;
+    }
+
+  spi = kmm_zalloc(sizeof(struct sam_spidev_s));
+  if (!spi)
+    {
+      spierr("ERROR: Could not allocate sam_spics_s structure!\n");
+      return NULL;
+    }
+
+  /* Select the SPI operations */
+
+  spi->ops  = &g_spiops;
+  spi->priv = priv;
+
+  if (!priv->initialized)
+    {
+      flags = enter_critical_section();
+
+      sam_configgpio(priv->pins.mosi);
+      sam_configgpio(priv->pins.miso);
+      sam_configgpio(priv->pins.sck);
+      sam_configgpio(priv->pins.nss);
+
+      /* Disable write protection */
+
+      serial_putreg(priv, SAM_UART_WPMR_OFFSET, USART_WPMR_WPKEY);
+
+      serial_putreg(priv, SAM_UART_MR_OFFSET, 0);
+      serial_putreg(priv, SAM_UART_RTOR_OFFSET, 0);
+      serial_putreg(priv, SAM_UART_TTGR_OFFSET, 0);
+
+      /* Reset and disable receiver and transmitter */
+
+      serial_putreg(priv, SAM_UART_CR_OFFSET,
+                    (UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS |
+                    UART_CR_TXDIS));
+
+      /* Reset status bits */
+
+      serial_putreg(priv, SAM_UART_CR_OFFSET, UART_CR_RSTSTA);
+
+      leave_critical_section(flags);
+
+      /* Configure mode register. */
+
+      regval = UART_MR_MODE_SPIMSTR | UART_MR_CLKO | UART_MR_CHRL_8BITS;
+      serial_putreg(priv, SAM_UART_MR_OFFSET, regval);
+
+      /* Enable receiver & transmitter */
+
+      serial_putreg(priv, SAM_UART_CR_OFFSET, (UART_CR_RXEN | UART_CR_TXEN));
+
+      spi->priv->mode       = 0;
+      spi->priv->nbits      = 8;
+      spi->priv->frequency  = 0;
+      spi->priv->actual     = 0;
+
+      priv->initialized = true;
+    }
+
+  return (struct spi_dev_s *)spi;
+}
+
+#endif /* CONFIG_SAMV7_USART_IS_SPI_MASTER */
diff --git a/arch/arm/src/samv7/sam_serial_spi.h b/arch/arm/src/samv7/sam_serial_spi.h
new file mode 100644
index 0000000000..15ec169ef4
--- /dev/null
+++ b/arch/arm/src/samv7/sam_serial_spi.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+ * arch/arm/src/samv7/sam_serial_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_SERIAL_SPI_H
+#define __ARCH_ARM_SRC_SAMV7_SAM_SERIAL_SPI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "chip.h"
+#include "sam_config.h"
+
+#ifdef CONFIG_SAMV7_USART_IS_SPI_MASTER
+
+/****************************************************************************
+ * 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_serial_spi_initialize
+ *
+ * Description:
+ *   Initialize the selected SPI port in master mode
+ *
+ * Input Parameters:
+ *   port - USART interface to be used (0-2)
+ *
+ * Returned Value:
+ *   Valid SPI device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct spi_dev_s;
+struct spi_dev_s *sam_serial_spi_initialize(int port);
+
+/****************************************************************************
+ * Name: sam_serial_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_serial_status(struct spi_dev_s *dev, uint32_t devid);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* CONFIG_SAMV7_USART_IS_SPI_MASTER */
+#endif /* __ARCH_ARM_SRC_SAMV7_SAM_SERIAL_SPI_H */