summaryrefslogtreecommitdiff
path: root/nuttx/arch/arm/src/samd
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-02-19 16:52:12 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-02-19 16:52:12 -0600
commite88925ec0969724cfb854aa7bb75508866c0778c (patch)
tree1d7442d93df84efa90d082d5a0225db1dc2240ed /nuttx/arch/arm/src/samd
parent910ed53c5d0a166ed6853694a276be6e9680a35a (diff)
downloadnuttx-e88925ec0969724cfb854aa7bb75508866c0778c.tar.gz
nuttx-e88925ec0969724cfb854aa7bb75508866c0778c.tar.bz2
nuttx-e88925ec0969724cfb854aa7bb75508866c0778c.zip
SAMD20: Add a framework for an SPI driver
Diffstat (limited to 'nuttx/arch/arm/src/samd')
-rw-r--r--nuttx/arch/arm/src/samd/sam_config.h22
-rw-r--r--nuttx/arch/arm/src/samd/sam_lowputc.c14
-rw-r--r--nuttx/arch/arm/src/samd/sam_lowputc.h4
-rw-r--r--nuttx/arch/arm/src/samd/sam_serial.c4
-rw-r--r--nuttx/arch/arm/src/samd/sam_spi.c1249
-rw-r--r--nuttx/arch/arm/src/samd/sam_spi.h268
6 files changed, 1548 insertions, 13 deletions
diff --git a/nuttx/arch/arm/src/samd/sam_config.h b/nuttx/arch/arm/src/samd/sam_config.h
index 7d7e335c7..fbbb74205 100644
--- a/nuttx/arch/arm/src/samd/sam_config.h
+++ b/nuttx/arch/arm/src/samd/sam_config.h
@@ -117,11 +117,11 @@
/* Are any USARTs enabled? */
-#undef HAVE_USART
+#undef SAMD_HAVE_USART
#if defined(SAMD_HAVE_USART0) || defined(SAMD_HAVE_USART1) || \
defined(SAMD_HAVE_USART2) || defined(SAMD_HAVE_USART3) || \
defined(SAMD_HAVE_USART4) || defined(SAMD_HAVE_USART5)
-# define HAVE_USART 1
+# define SAMD_HAVE_USART 1
#endif
/* Is there a serial console? There should be at most one defined. It could be on
@@ -219,6 +219,15 @@
# undef CONFIG_SAMD_SERCOM5_ISSPI
#endif
+/* Are any SERCOMs configured for SPI? */
+
+#undef SAMD_HAVE_SPI
+#if defined(SAMD_HAVE_SPI0) || defined(SAMD_HAVE_SPI1) || \
+ defined(SAMD_HAVE_SPI2) || defined(SAMD_HAVE_SPI3) || \
+ defined(SAMD_HAVE_SPI4) || defined(SAMD_HAVE_SPI5)
+# define SAMD_HAVE_SPI 1
+#endif
+
/* Are any SERCOM peripherals are configured as I2C peripherals? */
#define SAMD_HAVE_I2C0 1
@@ -258,6 +267,15 @@
# undef CONFIG_SAMD_SERCOM5_ISI2C
#endif
+/* Are any SERCOMs configured for I2C? */
+
+#undef SAMD_HAVE_I2C
+#if defined(SAMD_HAVE_I2C0) || defined(SAMD_HAVE_I2C1) || \
+ defined(SAMD_HAVE_I2C2) || defined(SAMD_HAVE_I2C3) || \
+ defined(SAMD_HAVE_I2C4) || defined(SAMD_HAVE_I2C5)
+# define SAMD_HAVE_I2C 1
+#endif
+
/************************************************************************************
* Public Types
************************************************************************************/
diff --git a/nuttx/arch/arm/src/samd/sam_lowputc.c b/nuttx/arch/arm/src/samd/sam_lowputc.c
index 271d23cb7..f80a3bb43 100644
--- a/nuttx/arch/arm/src/samd/sam_lowputc.c
+++ b/nuttx/arch/arm/src/samd/sam_lowputc.c
@@ -87,7 +87,7 @@
*
****************************************************************************/
-#ifdef HAVE_USART
+#ifdef SAMD_HAVE_USART
static void
sam_wait_synchronization(const struct sam_usart_config_s * const config)
{
@@ -103,7 +103,7 @@ sam_wait_synchronization(const struct sam_usart_config_s * const config)
*
****************************************************************************/
-#ifdef HAVE_USART
+#ifdef SAMD_HAVE_USART
static inline int
sam_usart_configure(const struct sam_usart_config_s * const config)
{
@@ -251,7 +251,7 @@ sam_usart_configure(const struct sam_usart_config_s * const config)
*
****************************************************************************/
-#ifdef HAVE_USART
+#ifdef SAMD_HAVE_USART
static inline void
sam_pad_configure(const struct sam_usart_config_s * const config)
{
@@ -289,7 +289,7 @@ sam_pad_configure(const struct sam_usart_config_s * const config)
*
*****************************************************************************/
-#ifdef HAVE_USART
+#ifdef SAMD_HAVE_USART
int sam_usart_internal(const struct sam_usart_config_s * const config)
{
uint32_t regval;
@@ -328,7 +328,7 @@ int sam_usart_internal(const struct sam_usart_config_s * const config)
*
****************************************************************************/
-#ifdef HAVE_USART
+#ifdef SAMD_HAVE_USART
static inline void
sam_usart_enable(const struct sam_usart_config_s * const config)
{
@@ -381,7 +381,7 @@ void sam_lowsetup(void)
*
*****************************************************************************/
-#ifdef HAVE_USART
+#ifdef SAMD_HAVE_USART
int sam_usart_initialize(const struct sam_usart_config_s * const config)
{
irqstate_t flags;
@@ -411,7 +411,7 @@ int sam_usart_initialize(const struct sam_usart_config_s * const config)
*
*****************************************************************************/
-#ifdef HAVE_USART
+#ifdef SAMD_HAVE_USART
void sam_usart_reset(const struct sam_usart_config_s * const config)
{
uintptr_t regaddr = config->base + SAM_USART_CTRLA_OFFSET;
diff --git a/nuttx/arch/arm/src/samd/sam_lowputc.h b/nuttx/arch/arm/src/samd/sam_lowputc.h
index 1158890eb..e4defc9cc 100644
--- a/nuttx/arch/arm/src/samd/sam_lowputc.h
+++ b/nuttx/arch/arm/src/samd/sam_lowputc.h
@@ -91,7 +91,7 @@ void sam_lowsetup(void);
*
****************************************************************************/
-#ifdef HAVE_USART
+#ifdef SAMD_HAVE_USART
struct sam_usart_config_s;
int sam_usart_initialize(const struct sam_usart_config_s * const config);
#endif
@@ -105,7 +105,7 @@ int sam_usart_initialize(const struct sam_usart_config_s * const config);
*
****************************************************************************/
-#ifdef HAVE_USART
+#ifdef SAMD_HAVE_USART
struct sam_usart_config_s;
void sam_usart_reset(const struct sam_usart_config_s * const config);
#endif
diff --git a/nuttx/arch/arm/src/samd/sam_serial.c b/nuttx/arch/arm/src/samd/sam_serial.c
index 3f8e9b1ba..4665befb1 100644
--- a/nuttx/arch/arm/src/samd/sam_serial.c
+++ b/nuttx/arch/arm/src/samd/sam_serial.c
@@ -66,7 +66,7 @@
#include "sam_lowputc.h"
#include "sam_serial.h"
-#ifdef HAVE_USART
+#ifdef SAMD_HAVE_USART
/****************************************************************************
* Definitions
@@ -1082,5 +1082,5 @@ int up_putc(int ch)
}
#endif /* USE_SERIALDRIVER */
-#endif /* HAVE_USART */
+#endif /* SAMD_HAVE_USART */
diff --git a/nuttx/arch/arm/src/samd/sam_spi.c b/nuttx/arch/arm/src/samd/sam_spi.c
new file mode 100644
index 000000000..ebde7bbcf
--- /dev/null
+++ b/nuttx/arch/arm/src/samd/sam_spi.c
@@ -0,0 +1,1249 @@
+/****************************************************************************
+ * arch/arm/src/samd/sam_spi.c
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Authors: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * 1. "Atmel SAM D20J / SAM D20G / SAM D20E ARM-Based Microcontroller
+ * Datasheet", 42129J–SAM–12/2013
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <semaphore.h>
+#include <wdog.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+#include <nuttx/arch.h>
+#include <nuttx/clock.h>
+#include <nuttx/spi/spi.h>
+
+#include "up_internal.h"
+#include "up_arch.h"
+
+#include "chip.h"
+#include "chip/sam_port.h"
+#include "chip/sam_pinmap.h"
+#include "chip/sam_spi.h"
+
+#include "sam_sercom.h"
+#include "sam_spi.h"
+
+#ifdef SAMD_HAVE_SPI
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Clocking *****************************************************************/
+
+/* Debug *******************************************************************/
+/* Check if SPI debug is enabled (non-standard.. no support in
+ * include/debug.h
+ */
+
+#ifndef CONFIG_DEBUG
+# undef CONFIG_DEBUG_VERBOSE
+# undef CONFIG_DEBUG_SPI
+# undef CONFIG_SAMD_SPI_REGDEBUG
+#endif
+
+#ifdef CONFIG_DEBUG_SPI
+# define spidbg lldbg
+# ifdef CONFIG_DEBUG_VERBOSE
+# define spivdbg lldbg
+# else
+# define spivdbg(x...)
+# endif
+#else
+# define spidbg(x...)
+# define spivdbg(x...)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The state of the one SPI chip select */
+
+struct sam_spidev_s
+{
+ const struct spi_ops_s *ops; /* Externally visible part of the SPI interface */
+
+ /* Fixed configuration */
+
+ uint8_t sercom; /* Identifies the SERCOM peripheral */
+ uint8_t irq; /* SERCOM IRQ number */
+ uint8_t gclkgen; /* Source GCLK generator */
+ port_pinset_t pad0; /* Pin configuration for PAD0 */
+ port_pinset_t pad1; /* Pin configuration for PAD1 */
+ port_pinset_t pad2; /* Pin configuration for PAD2 */
+ port_pinset_t pad3; /* Pin configuration for PAD3 */
+ uint32_t muxconfig; /* Pad multiplexing configuration */
+ uint32_t frequency; /* Source clock frequency */
+ uintptr_t base; /* SERCOM base address */
+
+ /* Dynamic configuration */
+
+#ifndef CONFIG_SPI_OWNBUS
+ sem_t spilock; /* Used to managed exclusive access to the bus */
+ uint32_t frequency; /* Requested clock frequency */
+ uint32_t actual; /* Actual clock frequency */
+ uint8_t nbits; /* Width of word in bits (8 to 16) */
+ uint8_t mode; /* Mode 0,1,2,3 */
+#endif
+
+ /* Debug stuff */
+
+#ifdef CONFIG_SAMD_SPI_REGDEBUG
+ bool wrlast; /* Last was a write */
+ uint32_t addresslast; /* Last address */
+ uint32_t valuelast; /* Last value */
+ int ntimes; /* Number of times */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helpers */
+
+#ifdef CONFIG_SAMD_SPI_REGDEBUG
+static bool spi_checkreg(struct sam_spidev_s *spi, bool wr,
+ uint32_t regval, uint32_t regaddr);
+#else
+# define spi_checkreg(spi,wr,regval,regaddr) (false)
+#endif
+
+static inline uint8_t spi_getreg8(struct sam_spidev_s *spi,
+ unsigned int offset);
+static inline void spi_putreg8(struct sam_spidev_s *spi, uint8_t regval,
+ unsigned int offset);
+static inline uint16_t spi_getreg16(struct sam_spidev_s *spi,
+ unsigned int offset);
+static inline void spi_putreg16(struct sam_spidev_s *spi, uint16_t regval,
+ unsigned int offset);
+static inline uint32_t spi_getreg32(struct sam_spidev_s *spi,
+ unsigned int offset);
+static inline void spi_putreg32(struct sam_spidev_s *spi, uint32_t regval,
+ unsigned int offset);
+
+#if defined(CONFIG_DEBUG_SPI) && defined(CONFIG_DEBUG_VERBOSE)
+static void spi_dumpregs(struct sam_spidev_s *spi, const char *msg);
+#else
+# define spi_dumpregs(spi,msg)
+#endif
+
+/* SPI methods */
+
+#ifndef CONFIG_SPI_OWNBUS
+static int spi_lock(struct spi_dev_s *dev, bool lock);
+#endif
+static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency);
+static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode);
+static void spi_setbits(struct spi_dev_s *dev, int nbits);
+static uint16_t spi_send(struct spi_dev_s *dev, uint16_t ch);
+static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+ void *rxbuffer, size_t nwords);
+#ifndef CONFIG_SPI_EXCHANGE
+static void spi_sndblock(struct spi_dev_s *dev,
+ const void *buffer, size_t nwords);
+static void spi_recvblock(struct spi_dev_s *dev, void *buffer,
+ size_t nwords);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef SAMD_HAVE_SPI0
+/* SPI0 driver operations */
+
+static const struct spi_ops_s g_spi0ops =
+{
+#ifndef CONFIG_SPI_OWNBUS
+ .lock = spi_lock,
+#endif
+ .select = sam_spi0select,
+ .setfrequency = spi_setfrequency,
+ .setmode = spi_setmode,
+ .setbits = spi_setbits,
+ .status = sam_spi0status,
+#ifdef CONFIG_SPI_CMDDATA
+ .cmddata = sam_spi0cmddata,
+#endif
+ .send = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+ .exchange = spi_exchange,
+#else
+ .sndblock = spi_sndblock,
+ .recvblock = spi_recvblock,
+#endif
+ .registercallback = 0, /* Not implemented */
+};
+
+/* This is the overall state of the SPI0 controller */
+
+static struct sam_spidev_s g_spi0dev =
+{
+ .ops = &g_spi0ops,
+ .sercom = 0,
+ .irq = SAM_IRQ_SERCOM0,
+ .gclkgen = (BOARD_SERCOM0_GCLKGEN >> GCLK_CLKCTRL_GEN_SHIFT),
+ .pad0 = BOARD_SERCOM0_PINMAP_PAD0,
+ .pad1 = BOARD_SERCOM0_PINMAP_PAD1,
+ .pad2 = BOARD_SERCOM0_PINMAP_PAD2,
+ .pad3 = BOARD_SERCOM0_PINMAP_PAD3,
+ .muxconfig = BOARD_SERCOM0_MUXCONFIG,
+ .frequency = BOARD_SERCOM0_FREQUENCY,
+ .base = SAM_SERCOM0_BASE,
+#ifndef CONFIG_SPI_OWNBUS
+ .spilock = SEM_INITIALIZER(1),
+#endif
+};
+#endif
+
+#ifdef SAMD_HAVE_SPI1
+/* SPI1 driver operations */
+
+static const struct spi_ops_s g_spi1ops =
+{
+#ifndef CONFIG_SPI_OWNBUS
+ .lock = spi_lock,
+#endif
+ .select = sam_spi1select,
+ .setfrequency = spi_setfrequency,
+ .setmode = spi_setmode,
+ .setbits = spi_setbits,
+ .status = sam_spi1status,
+#ifdef CONFIG_SPI_CMDDATA
+ .cmddata = sam_spi1cmddata,
+#endif
+ .send = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+ .exchange = spi_exchange,
+#else
+ .sndblock = spi_sndblock,
+ .recvblock = spi_recvblock,
+#endif
+ .registercallback = 0, /* Not implemented */
+};
+
+/* This is the overall state of the SPI1 controller */
+
+static struct sam_spidev_s g_spi1dev =
+{
+ .ops = &g_spi1ops,
+ .sercom = 1,
+ .irq = SAM_IRQ_SERCOM1,
+ .gclkgen = (BOARD_SERCOM1_GCLKGEN >> GCLK_CLKCTRL_GEN_SHIFT),
+ .pad0 = BOARD_SERCOM1_PINMAP_PAD0,
+ .pad1 = BOARD_SERCOM1_PINMAP_PAD1,
+ .pad2 = BOARD_SERCOM1_PINMAP_PAD2,
+ .pad3 = BOARD_SERCOM1_PINMAP_PAD3,
+ .muxconfig = BOARD_SERCOM1_MUXCONFIG,
+ .frequency = BOARD_SERCOM1_FREQUENCY,
+ .base = SAM_SERCOM1_BASE,
+#ifndef CONFIG_SPI_OWNBUS
+ .spilock = SEM_INITIALIZER(1),
+#endif
+};
+#endif
+
+#ifdef SAMD_HAVE_SPI2
+/* SPI2 driver operations */
+
+static const struct spi_ops_s g_spi2ops =
+{
+#ifndef CONFIG_SPI_OWNBUS
+ .lock = spi_lock,
+#endif
+ .select = sam_spi0select,
+ .setfrequency = spi_setfrequency,
+ .setmode = spi_setmode,
+ .setbits = spi_setbits,
+ .status = sam_spi0status,
+#ifdef CONFIG_SPI_CMDDATA
+ .cmddata = sam_spi0cmddata,
+#endif
+ .send = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+ .exchange = spi_exchange,
+#else
+ .sndblock = spi_sndblock,
+ .recvblock = spi_recvblock,
+#endif
+ .registercallback = 0, /* Not implemented */
+};
+
+/* This is the overall state of the SPI2 controller */
+
+static struct sam_spidev_s g_spi2dev =
+{
+ .ops = &g_spi1ops,
+ .sercom = 2,
+ .irq = SAM_IRQ_SERCOM2,
+ .gclkgen = (BOARD_SERCOM2_GCLKGEN >> GCLK_CLKCTRL_GEN_SHIFT),
+ .pad0 = BOARD_SERCOM2_PINMAP_PAD0,
+ .pad1 = BOARD_SERCOM2_PINMAP_PAD1,
+ .pad2 = BOARD_SERCOM2_PINMAP_PAD2,
+ .pad3 = BOARD_SERCOM2_PINMAP_PAD3,
+ .muxconfig = BOARD_SERCOM2_MUXCONFIG,
+ .frequency = BOARD_SERCOM2_FREQUENCY,
+ .base = SAM_SERCOM2_BASE,
+#ifndef CONFIG_SPI_OWNBUS
+ .spilock = SEM_INITIALIZER(1),
+#endif
+};
+#endif
+
+#ifdef SAMD_HAVE_SPI3
+/* SPI3 driver operations */
+
+static const struct spi_ops_s g_spi3ops =
+{
+#ifndef CONFIG_SPI_OWNBUS
+ .lock = spi_lock,
+#endif
+ .select = sam_spi3select,
+ .setfrequency = spi_setfrequency,
+ .setmode = spi_setmode,
+ .setbits = spi_setbits,
+ .status = sam_spi3status,
+#ifdef CONFIG_SPI_CMDDATA
+ .cmddata = sam_spi3cmddata,
+#endif
+ .send = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+ .exchange = spi_exchange,
+#else
+ .sndblock = spi_sndblock,
+ .recvblock = spi_recvblock,
+#endif
+ .registercallback = 0, /* Not implemented */
+};
+
+/* This is the overall state of the SPI3 controller */
+
+static struct sam_spidev_s g_spi3dev =
+{
+ .ops = &g_spi3ops,
+ .sercom = 3,
+ .irq = SAM_IRQ_SERCOM3,
+ .gclkgen = (BOARD_SERCOM3_GCLKGEN >> GCLK_CLKCTRL_GEN_SHIFT),
+ .pad0 = BOARD_SERCOM3_PINMAP_PAD0,
+ .pad1 = BOARD_SERCOM3_PINMAP_PAD1,
+ .pad2 = BOARD_SERCOM3_PINMAP_PAD2,
+ .pad3 = BOARD_SERCOM3_PINMAP_PAD3,
+ .muxconfig = BOARD_SERCOM3_MUXCONFIG,
+ .frequency = BOARD_SERCOM3_FREQUENCY,
+ .base = SAM_SERCOM3_BASE,
+#ifndef CONFIG_SPI_OWNBUS
+ .spilock = SEM_INITIALIZER(1),
+#endif
+};
+#endif
+
+#ifdef SAMD_HAVE_SPI4
+/* SPI4 driver operations */
+
+static const struct spi_ops_s g_spi4ops =
+{
+#ifndef CONFIG_SPI_OWNBUS
+ .lock = spi_lock,
+#endif
+ .select = sam_spi4select,
+ .setfrequency = spi_setfrequency,
+ .setmode = spi_setmode,
+ .setbits = spi_setbits,
+ .status = sam_spi4status,
+#ifdef CONFIG_SPI_CMDDATA
+ .cmddata = sam_spi4cmddata,
+#endif
+ .send = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+ .exchange = spi_exchange,
+#else
+ .sndblock = spi_sndblock,
+ .recvblock = spi_recvblock,
+#endif
+ .registercallback = 0, /* Not implemented */
+};
+
+/* This is the overall state of the SPI4 controller */
+
+static struct sam_spidev_s g_spi4dev =
+{
+ .ops = &g_spi4ops,
+ .sercom = 4,
+ .irq = SAM_IRQ_SERCOM4,
+ .gclkgen = (BOARD_SERCOM4_GCLKGEN >> GCLK_CLKCTRL_GEN_SHIFT),
+ .pad0 = BOARD_SERCOM4_PINMAP_PAD0,
+ .pad1 = BOARD_SERCOM4_PINMAP_PAD1,
+ .pad2 = BOARD_SERCOM4_PINMAP_PAD2,
+ .pad3 = BOARD_SERCOM4_PINMAP_PAD3,
+ .muxconfig = BOARD_SERCOM4_MUXCONFIG,
+ .frequency = BOARD_SERCOM4_FREQUENCY,
+ .base = SAM_SERCOM4_BASE,
+#ifndef CONFIG_SPI_OWNBUS
+ .spilock = SEM_INITIALIZER(1),
+#endif
+};
+#endif
+
+#ifdef SAMD_HAVE_SPI5
+/* SPI5 driver operations */
+
+static const struct spi_ops_s g_spi5ops =
+{
+#ifndef CONFIG_SPI_OWNBUS
+ .lock = spi_lock,
+#endif
+ .select = sam_spi5select,
+ .setfrequency = spi_setfrequency,
+ .setmode = spi_setmode,
+ .setbits = spi_setbits,
+ .status = sam_spi5status,
+#ifdef CONFIG_SPI_CMDDATA
+ .cmddata = sam_spi5cmddata,
+#endif
+ .send = spi_send,
+#ifdef CONFIG_SPI_EXCHANGE
+ .exchange = spi_exchange,
+#else
+ .sndblock = spi_sndblock,
+ .recvblock = spi_recvblock,
+#endif
+ .registercallback = 0, /* Not implemented */
+};
+
+/* This is the overall state of the SPI5 controller */
+
+static struct sam_spidev_s g_spi5dev =
+{
+ .ops = &g_spi5ops,
+ .sercom = 5,
+ .irq = SAM_IRQ_SERCOM5,
+ .gclkgen = (BOARD_SERCOM5_GCLKGEN >> GCLK_CLKCTRL_GEN_SHIFT),
+ .pad0 = BOARD_SERCOM5_PINMAP_PAD0,
+ .pad1 = BOARD_SERCOM5_PINMAP_PAD1,
+ .pad2 = BOARD_SERCOM5_PINMAP_PAD2,
+ .pad3 = BOARD_SERCOM5_PINMAP_PAD3,
+ .muxconfig = BOARD_SERCOM5_MUXCONFIG,
+ .frequency = BOARD_SERCOM5_FREQUENCY,
+ .base = SAM_SERCOM5_BASE,
+#ifndef CONFIG_SPI_OWNBUS
+ .spilock = SEM_INITIALIZER(1),
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spi_checkreg
+ *
+ * Description:
+ * Check if the current register access is a duplicate of the preceding.
+ *
+ * Input Parameters:
+ * regval - The value to be written
+ * regaddr - The address of the register to write to
+ *
+ * Returned Value:
+ * true: This is the first register access of this type.
+ * flase: This is the same as the preceding register access.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMD_SPI_REGDEBUG
+static bool spi_checkreg(struct sam_spidev_s *priv, bool wr, uint32_t regval,
+ uint32_t regaddr)
+{
+ if (wr == priv->wrlast && /* Same kind of access? */
+ regval == priv->valuelast && /* Same value? */
+ regaddr == priv->addresslast) /* Same address? */
+ {
+ /* Yes, then just keep a count of the number of times we did this. */
+
+ priv->ntimes++;
+ return false;
+ }
+ else
+ {
+ /* Did we do the previous operation more than once? */
+
+ if (priv->ntimes > 0)
+ {
+ /* Yes... show how many times we did it */
+
+ lldbg("...[Repeats %d times]...\n", priv->ntimes);
+ }
+
+ /* Save information about the new access */
+
+ priv->wrlast = wr;
+ priv->valuelast = regval;
+ priv->addresslast = regaddr;
+ priv->ntimes = 0;
+ }
+
+ /* Return true if this is the first time that we have done this operation */
+
+ return true;
+}
+#endif
+
+/****************************************************************************
+ * Name: spi_getreg8
+ *
+ * Description:
+ * Read an SPI register
+ *
+ ****************************************************************************/
+
+static inline uint8_t spi_getreg8(struct sam_spidev_s *priv,
+ unsigned int offset)
+{
+ uintptr_t regaddr = priv->base + offset;
+ uint8_t regval = getreg8(regaddr);
+
+#ifdef CONFIG_SAMD_SPI_REGDEBUG
+ if (spi_checkreg(priv, false, (uint32_t)regval, regaddr))
+ {
+ lldbg("%08x->%02x\n", regaddr, regval);
+ }
+#endif
+
+ return regval;
+}
+
+/****************************************************************************
+ * Name: spi_putreg8
+ *
+ * Description:
+ * Write a value to an SPI register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg8(struct sam_spidev_s *priv, uint8_t regval,
+ unsigned int offset)
+{
+ uintptr_t regaddr = priv->base + offset;
+
+#ifdef CONFIG_SAMD_SPI_REGDEBUG
+ if (spi_checkreg(priv, true, (uint32_t)regval, regaddr))
+ {
+ lldbg("%08x<-%02x\n", regaddr, regval);
+ }
+#endif
+
+ putreg8(regval, regaddr);
+}
+
+/****************************************************************************
+ * Name: spi_getreg16
+ *
+ * Description:
+ * Read an SPI register
+ *
+ ****************************************************************************/
+
+static inline uint16_t spi_getreg16(struct sam_spidev_s *priv,
+ unsigned int offset)
+{
+ uintptr_t regaddr = priv->base + offset;
+ uint16_t regval = getreg16(regaddr);
+
+#ifdef CONFIG_SAMD_SPI_REGDEBUG
+ if (spi_checkreg(priv, false, (uint32_t)regval, regaddr))
+ {
+ lldbg("%08x->%04x\n", regaddr, regval);
+ }
+#endif
+
+ return regval;
+}
+
+/****************************************************************************
+ * Name: spi_putreg16
+ *
+ * Description:
+ * Write a value to an SPI register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg16(struct sam_spidev_s *priv, uint16_t regval,
+ unsigned int offset)
+{
+ uintptr_t regaddr = priv->base + offset;
+
+#ifdef CONFIG_SAMD_SPI_REGDEBUG
+ if (spi_checkreg(priv, true, (uint32_t)regval, regaddr))
+ {
+ lldbg("%08x<-%04x\n", regaddr, regval);
+ }
+#endif
+
+ putreg16(regval, regaddr);
+}
+
+/****************************************************************************
+ * Name: spi_getreg32
+ *
+ * Description:
+ * Read an SPI register
+ *
+ ****************************************************************************/
+
+static inline uint32_t spi_getreg32(struct sam_spidev_s *priv,
+ unsigned int offset)
+{
+ uintptr_t regaddr = priv->base + offset;
+ uint32_t regval = getreg32(regaddr);
+
+#ifdef CONFIG_SAMD_SPI_REGDEBUG
+ if (spi_checkreg(priv, false, regval, regaddr))
+ {
+ lldbg("%08x->%08x\n", regaddr, regval);
+ }
+#endif
+
+ return regval;
+}
+
+/****************************************************************************
+ * Name: spi_putreg32
+ *
+ * Description:
+ * Write a value to an SPI register
+ *
+ ****************************************************************************/
+
+static inline void spi_putreg32(struct sam_spidev_s *priv, uint32_t regval,
+ unsigned int offset)
+{
+ uintptr_t regaddr = priv->base + offset;
+
+#ifdef CONFIG_SAMD_SPI_REGDEBUG
+ if (spi_checkreg(priv, true, regval, regaddr))
+ {
+ lldbg("%08x<-%08x\n", regaddr, regval);
+ }
+#endif
+
+ putreg32(regval, regaddr);
+}
+
+/****************************************************************************
+ * Name: spi_dumpregs
+ *
+ * Description:
+ * Dump the contents of all SPI registers
+ *
+ * Input Parameters:
+ * priv - The SPI controller to dump
+ * msg - Message to print before the register data
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_SPI) && defined(CONFIG_DEBUG_VERBOSE)
+static void spi_dumpregs(struct sam_spidev_s *priv, const char *msg)
+{
+ spivdbg("%s:\n", msg);
+ spivdbg(" CTRLA:%08x CTRLB:%08x DBGCTRL:%08x\n",
+ getreg32(priv->base + SAM_SPI_CTRLA_OFFSET),
+ getreg32(priv->base + SAM_SPI_CTRLB_OFFSET),
+ getreg32(priv->base + SAM_SPI_DBGCTRL_OFFSET));
+ spivdbg(" BAUD:%08x INTEN:%08x INTFLAG:%08x\n",
+ getreg32(priv->base + SAM_SPI_BAUD_OFFSET),
+ getreg32(priv->base + SAM_SPI_INTENCLR_OFFSET),
+ getreg32(priv->base + SAM_SPI_INTFLAG_OFFSET));
+ spivdbg(" STATUS:%08x ADDR:%08x\n",
+ getreg32(priv->base + SAM_SPI_STATUS_OFFSET),
+ getreg32(priv->base + SAM_SPI_ADDR_OFFSET));
+}
+#endif
+
+/****************************************************************************
+ * Name: 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 buss is being shared, then it
+ * may have been left in an incompatible state.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * lock - true: Lock priv bus, false: unlock SPI bus
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SPI_OWNBUS
+static int spi_lock(struct spi_dev_s *dev, bool lock)
+{
+ struct sam_spidev_s *priv = (struct sam_spidev_s *)dev;
+
+ spivdbg("lock=%d\n", lock);
+ if (lock)
+ {
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(&priv->spilock) != 0)
+ {
+ /* The only case that an error should occur here is if the wait was awakened
+ * by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+ }
+ else
+ {
+ (void)sem_post(&priv->spilock);
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: 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 spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency)
+{
+ struct sam_spidev_s *priv =(struct sam_spidev_s *)dev;
+ uint32_t actual;
+
+ spivdbg("sercom=%d frequency=%d\n", priv->sercom, frequency);
+
+ /* 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 SPI to a frequency as close as possible to the requested
+ * frequency.
+ */
+#warning Missing logic
+
+ /* Calculate the new actual frequency */
+#warning Missing logic
+
+ spivdbg("actual=%d\n", offset, regval, actual);
+
+ /* Save the frequency setting */
+
+#ifndef CONFIG_SPI_OWNBUS
+ priv->frequency = frequency;
+ priv->actual = actual;
+#endif
+
+ spidbg("Frequency %d->%d\n", frequency, actual);
+ return actual;
+}
+
+/****************************************************************************
+ * Name: 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 spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode)
+{
+ struct sam_spidev_s *priv = (struct sam_spidev_s *)dev;
+ uint32_t regval;
+
+ spivdbg("sercom=%d mode=%d\n", priv->sercom, mode);
+
+ /* Has the mode changed? */
+
+#ifndef CONFIG_SPI_OWNBUS
+ if (mode != priv->mode)
+#endif
+ {
+ /* Yes... Set the mode appropriately */
+
+ regval = spi_regetreg(SAM_SPI_CTRLA_OFFSET);
+ regval &= ~(SPI_CTRLA_CPOL | SPI_CTRLA_CPHA);
+
+ switch (mode)
+ {
+ case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
+ break;
+
+ case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
+ regval |= SPI_CTRLA_CPHA;
+ break;
+
+ case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
+ regval |= SPI_CTRLA_CPOL;
+ break;
+
+ case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
+ regval |= (SPI_CTRLA_CPOL | SPI_CTRLA_CPHA);
+ break;
+
+ default:
+ DEBUGASSERT(FALSE);
+ return;
+ }
+
+ spi_putreg32(priv, regval, offset);
+
+ /* Save the mode so that subsequent re-configurations will be faster */
+
+#ifndef CONFIG_SPI_OWNBUS
+ priv->mode = mode;
+#endif
+ }
+}
+
+/****************************************************************************
+ * Name: spi_setbits
+ *
+ * Description:
+ * Set the number if bits per word.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * nbits - The number of bits requests
+ *
+ * Returned Value:
+ * none
+ *
+ ****************************************************************************/
+
+static void spi_setbits(struct spi_dev_s *dev, int nbits)
+{
+ struct sam_spidev_s *priv = (struct sam_spidev_s *)dev;
+ uint32_t regval;
+ unsigned int offset;
+
+ spivdbg("sercom=%d nbits=%d\n", priv->sercom, nbits);
+ DEBUGASSERT(priv && nbits > 7 && nbits < 10);
+
+ /* NOTE: The logic in spi_send and in spi_exchange only handles 8-bit
+ * data at the present time. So the following extra assertion is a
+ * reminder that we have to fix that someday.
+ */
+
+ DEBUGASSERT(nbits == 8); /* Temporary -- FIX ME */
+
+ /* Has the number of bits changed? */
+
+#ifndef CONFIG_SPI_OWNBUS
+ if (nbits != priv->nbits)
+#endif
+ {
+ /* Yes... Set number of bits appropriately */
+
+ regval = spi_getreg32(priv, SAM_SPI_CTRLB_OFFSET);
+ regval &= ~SPI_CTRLB_CHSIZE_MASK;
+
+ if (nbits == 9)
+ {
+ regval |= SPI_CTRLB_CHSIZE_9BITS;
+ }
+
+ spi_putreg32(priv, regval, SAM_SPI_CTRLB_OFFSET);
+
+ /* Save the selection so the subsequence re-configurations will be faster */
+
+#ifndef CONFIG_SPI_OWNBUS
+ priv->nbits = nbits;
+#endif
+ }
+}
+
+/****************************************************************************
+ * Name: 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 uint16_t spi_send(struct spi_dev_s *dev, uint16_t wd)
+{
+ uint8_t txbyte;
+ uint8_t rxbyte;
+
+ /* spi_exchange can do this. Note: right now, this only deals with 8-bit
+ * words. If the SPI interface were configured for words of other sizes,
+ * this would fail.
+ */
+
+ txbyte = (uint8_t)wd;
+ rxbyte = (uint8_t)0;
+ spi_exchange(dev, &txbyte, &rxbyte, 1);
+
+ spivdbg("Sent %02x received %02x\n", txbyte, rxbyte);
+ return (uint16_t)rxbyte;
+}
+
+/****************************************************************************
+ * Name: 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 recieve 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 spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
+ void *rxbuffer, size_t nwords)
+{
+ struct sam_spidev_s *priv = (struct sam_spidev_s *)dev;
+ cont uint8_t *txptr = (const uint8_t *)txbuffer;
+ uint8_t *rxptr = (uint8_t *)rxbuffer;
+ uint16_t data;
+
+ spivdbg("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
+
+ /* Loop, sending each word in the user-provided data buffer.
+ *
+ * Note 1: Right now, this only deals with 8-bit words. If the SPI
+ * interface were configured for words of other sizes, this
+ * would fail.
+ * 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 DRE:: 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 RXC: meaning that word N-1 is available
+ * Read word N-1.
+ * Re-enable interrupts.
+ * Save word N-1.
+ * }
+ * Wait for RXC: 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 (txptr)
+ {
+ data = (uint32_t)*txptr++;
+ }
+ else
+ {
+ data = 0xffff;
+ }
+
+ /* Wait for any previous data written to the DATA register to be
+ * transferred to the serializer.
+ */
+
+ while ((spi_getreg32(priv, SAM_SPI_INTFLAG_OFFSET) & SPI_INT_DRE) == 0);
+
+ /* Write the data to transmitted to the DATA Register (TDR) */
+
+ spi_putreg32(priv, data, SAM_SPI_DATA_OFFSET);
+
+ /* Wait for the read data to be available in the DATA register. */
+
+ while ((spi_getreg32(priv, SAM_SPI_INTFLAG_OFFSET) & SPI_INT_RXC) == 0);
+
+ /* Read the received data from the SPI DATA Register..
+ * TODO: The following only works if nbits <= 8.
+ */
+
+ data = spi_getreg16(priv, SAM_SPI_DATA_OFFSET);
+ if (rxptr)
+ {
+ *rxptr++ = (uint8_t)data;
+ }
+ }
+}
+
+/***************************************************************************
+ * Name: 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 spi_sndblock(struct spi_dev_s *dev, const void *buffer, size_t nwords)
+{
+ /* spi_exchange can do this. */
+
+ spi_exchange(dev, buffer, NULL, nwords);
+}
+#endif
+
+/****************************************************************************
+ * Name: 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 recieve 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
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SPI_EXCHANGE
+static void spi_recvblock(struct spi_dev_s *dev, void *buffer, size_t nwords)
+{
+ /* spi_exchange can do this. */
+
+ spi_exchange(dev, NULL, buffer, nwords);
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_spiinitialize
+ *
+ * Description:
+ * Initialize the selected SPI port
+ *
+ * Input Parameter:
+ * port - SPI "port" number (i.e., SERCOM number)
+ *
+ * Returned Value:
+ * Valid SPI device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct spi_dev_s *up_spiinitialize(int port)
+{
+ struct sam_spidev_s *priv;
+ irqstate_t flags;
+#ifndef CONFIG_SPI_OWNBUS
+ uint32_t regval;
+ unsigned int offset;
+#endif
+
+ /* Get the port state structure */
+
+ spivdbg("port: %d \n", port);
+
+#ifdef SAMD_HAVE_SPI0
+ if (port == 0)
+ {
+ priv = &g_spi0dev;
+ }
+ else
+#endif
+
+#ifdef SAMD_HAVE_SPI1
+ if (port == 1)
+ {
+ priv = &g_spi1dev;
+ }
+ else
+#endif
+
+#ifdef SAMD_HAVE_SPI2
+ if (port == 2)
+ {
+ priv = &g_spi2dev;
+ }
+ else
+#endif
+
+#ifdef SAMD_HAVE_SPI3
+ if (port == 3)
+ {
+ priv = &g_spi3dev;
+ }
+ else
+#endif
+
+#ifdef SAMD_HAVE_SPI4
+ if (port == 4)
+ {
+ priv = &g_spi4dev;
+ }
+ else
+#endif
+
+#ifdef SAMD_HAVE_SPI5
+ if (port == 5)
+ {
+ priv = &g_spi5dev;
+ }
+ else
+#endif
+ {
+ spidbg("ERROR: Unsupported port: %d\n", port);
+ return NULL;
+ }
+
+ /* Enable clocking to the SPI SERCOM */
+
+ flags = irqsave();
+#warning Missing logic
+
+ /* Configure multiplexed pins as connected on the board. */
+#warning Missing logic
+
+ /* Execute a software reset of the SPI SERCOM */
+#warning Missing logic
+
+ /* Configure the SPI SERCOM */
+#warning Missing logic
+
+ /* And enable the SPI */
+#warning Missing logic
+
+ spi_dumpregs(priv, "After initialization");
+
+#ifndef CONFIG_SPI_OWNBUS
+ /* Set to mode=0 and nbits=8 and some initial frequency. It is only
+ * critical to do this if CONFIG_SPI_OWNBUS is not defined because in
+ * that case, the SPI will only be reconfigured if there is a change.
+ */
+
+ spi_setmode((struct spi_dev_s *)priv, SPIDEV_MODE0);
+ spi_setfrequency((struct spi_dev_s *)priv, 400000);
+ spi_setbits((struct spi_dev_s *)priv, 8);
+#endif
+
+ return (struct spi_dev_s *)priv;
+}
+
+#endif /* SAMD_HAVE_SPI */
diff --git a/nuttx/arch/arm/src/samd/sam_spi.h b/nuttx/arch/arm/src/samd/sam_spi.h
new file mode 100644
index 000000000..db4c6b133
--- /dev/null
+++ b/nuttx/arch/arm/src/samd/sam_spi.h
@@ -0,0 +1,268 @@
+/****************************************************************************
+ * arch/arm/src/samd/sam_spi.h
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_SAMD_SAM_SPI_H
+#define __ARCH_ARM_SRC_SAMD_SAM_SPI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "sam_config.h"
+
+#ifdef SAMD_HAVE_SPI
+
+/****************************************************************************
+ * 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_spi[n]select, sam_spi[n]status, and sam_spi[n]cmddata
+ *
+ * Description:
+ * These external functions must be provided by board-specific logic. They
+ * include:
+ *
+ * o sam_spi[n]select is a functions to manage the board-specific chip
+ * selects
+ * o sam_spi[n]status and sam_spi[n]cmddata: Implementations of the status
+ * and cmddata methods of the SPI interface defined by struct spi_ops_
+ * (see include/nuttx/spi/spi.h). All other methods including
+ * up_spiinitialize()) are provided by common SAMD logic.
+ *
+ * Where [n] is the SERCOM number for the SPI module.
+ *
+ * To use this common SPI logic on your board:
+ *
+ * 1. Provide logic in sam_boardinitialize() to configure SPI chip select
+ * pins.
+ * 2. Provide sam_spi[n]select() and sam_spi[n]status() functions in your
+ * board-specific logic. These functions will perform chip selection
+ * and status operations using GPIOs in the way your board is configured.
+ * 2. If CONFIG_SPI_CMDDATA is defined in the NuttX configuration, provide
+ * sam_spi[n]cmddata() functions in your board-specific logic. This
+ * function will perform cmd/data selection operations using GPIOs in
+ * the way your board is configured.
+ * 3. Add a call to up_spiinitialize() in your low level application
+ * initialization logic
+ * 4. The handle returned by up_spiinitialize() may then be used to bind
+ * the SPI driver to higher level logic (e.g., calling
+ * mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ * the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+struct spi_dev_s;
+enum spi_dev_e;
+
+/****************************************************************************
+ * Name: sam_spi[n]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
+ * GPIO 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 GPIO 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 Values:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef SAMD_HAVE_SPI0
+void sam_spi0select(enum spi_dev_e devid, bool selected);
+#endif
+
+#ifdef SAMD_HAVE_SPI1
+void sam_spi1select(enum spi_dev_e devid, bool selected);
+#endif
+
+#ifdef SAMD_HAVE_SPI2
+void sam_spi2select(enum spi_dev_e devid, bool selected);
+#endif
+
+#ifdef SAMD_HAVE_SPI3
+void sam_spi3select(enum spi_dev_e devid, bool selected);
+#endif
+
+#ifdef SAMD_HAVE_SPI4
+void sam_spi4select(enum spi_dev_e devid, bool selected);
+#endif
+
+#ifdef SAMD_HAVE_SPI5
+void sam_spi5select(enum spi_dev_e devid, bool selected);
+#endif
+
+/****************************************************************************
+ * Name: sam_spi[n]status
+ *
+ * Description:
+ * Return status information associated with the SPI device.
+ *
+ * Input Parameters:
+ * dev - SPI device info
+ * devid - Identifies the (logical) device
+ *
+ * Returned Values:
+ * Bit-encoded SPI status (see include/nuttx/spi/spi.h.
+ *
+ ****************************************************************************/
+
+#ifdef SAMD_HAVE_SPI0
+uint8_t sam_spi0status(FAR struct spi_dev_s *dev, enum spi_dev_e devid);
+#endif
+
+#ifdef SAMD_HAVE_SPI1
+uint8_t sam_spi1status(FAR struct spi_dev_s *dev, enum spi_dev_e devid);
+#endif
+
+#ifdef SAMD_HAVE_SPI2
+uint8_t sam_spi2status(FAR struct spi_dev_s *dev, enum spi_dev_e devid);
+#endif
+
+#ifdef SAMD_HAVE_SPI3
+uint8_t sam_spi3status(FAR struct spi_dev_s *dev, enum spi_dev_e devid);
+#endif
+
+#ifdef SAMD_HAVE_SPI4
+uint8_t sam_spi4status(FAR struct spi_dev_s *dev, enum spi_dev_e devid);
+#endif
+
+#ifdef SAMD_HAVE_SPI5
+uint8_t sam_spi5status(FAR struct spi_dev_s *dev, enum spi_dev_e devid);
+#endif
+
+/****************************************************************************
+ * Name: sam_spi[n]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 GPIO 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 Values:
+ * Zero on success; a negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_CMDDATA
+#ifdef SAMD_HAVE_SPI0
+int sam_spi0cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd);
+#endif
+
+#ifdef SAMD_HAVE_SPI1
+int sam_spi1cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd);
+#endif
+
+#ifdef SAMD_HAVE_SPI2
+int sam_spi2cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd);
+#endif
+
+#ifdef SAMD_HAVE_SPI3
+int sam_spi3cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd);
+#endif
+
+#ifdef SAMD_HAVE_SPI4
+int sam_spi4cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd);
+#endif
+
+#ifdef SAMD_HAVE_SPI5
+int sam_spi5cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd);
+#endif
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* SAMD_HAVE_SPI */
+#endif /* __ARCH_ARM_SRC_SAMD_SAM_SPI_H */