diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2014-02-20 09:59:54 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2014-02-20 09:59:54 -0600 |
commit | 2ffd874a9f6a20142aeab550483a444d36c968e4 (patch) | |
tree | 42c2c6a860b2d1c6e5f9321ef016ba23500e885c | |
parent | 8a3770294f1d76d264494d67c9b4ef91275ee344 (diff) | |
download | nuttx-2ffd874a9f6a20142aeab550483a444d36c968e4.tar.gz nuttx-2ffd874a9f6a20142aeab550483a444d36c968e4.tar.bz2 nuttx-2ffd874a9f6a20142aeab550483a444d36c968e4.zip |
SAMD20: SPI driver is code-complete, but untested
-rw-r--r-- | nuttx/ChangeLog | 2 | ||||
-rw-r--r-- | nuttx/arch/arm/src/samd/chip/sam_spi.h | 17 | ||||
-rw-r--r-- | nuttx/arch/arm/src/samd/sam_lowputc.c | 4 | ||||
-rw-r--r-- | nuttx/arch/arm/src/samd/sam_sercom.h | 4 | ||||
-rw-r--r-- | nuttx/arch/arm/src/samd/sam_serial.c | 2 | ||||
-rw-r--r-- | nuttx/arch/arm/src/samd/sam_spi.c | 394 | ||||
-rw-r--r-- | nuttx/configs/samd20-xplained/include/board.h | 99 |
7 files changed, 440 insertions, 82 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index 3e6236588..2e57c8f86 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -6626,4 +6626,6 @@ a lot of missing logic (2014-2-19). * arch/arm/src/lm/lm_lowputc.c and lm_serial.c: Several errors are unmasked with UARTs > UART2 are enabled. From gosha (2014-2-19). + * arch/arm/src/samd/sam_spi.c: The SPI driver is code complete, + but untested (2014-2-19). diff --git a/nuttx/arch/arm/src/samd/chip/sam_spi.h b/nuttx/arch/arm/src/samd/chip/sam_spi.h index c5d78a90d..71202790e 100644 --- a/nuttx/arch/arm/src/samd/chip/sam_spi.h +++ b/nuttx/arch/arm/src/samd/chip/sam_spi.h @@ -145,15 +145,18 @@ # define SPI_CTRLA_MODE_MASTER (3 << SPI_CTRLA_MODE_SHIFT) /* SPI master operation */ #define SPI_CTRLA_RUNSTDBY (1 << 7) /* Bit 7: Run in standby */ #define SPI_CTRLA_IBON (1 << 8) /* Bit 8: Immediate BUFOVF notification */ -#define SPI_CTRLA_DOPO (1 << 16) /* Bit 16: Data out pinout */ -# define SPI_CTRLA_DOPAD0 (0) -# define SPI_CTRLA_DOPAD2 SPI_CTRLA_DOPO +#define SPI_CTRLA_DOPO_SHIFT (16) /* Bit 16-17: Data out pinout */ +#define SPI_CTRLA_DOPO_MASK (3 << SPI_CTRLA_DOPO_SHIFT) /* Bit 16-17: Data out pinout */ +# define SPI_CTRLA_DOPO_DOPAD012 (0 << SPI_CTRLA_DOPO_SHIFT) /* D0=PAD0 SCK=PAD1 SS=PAD2 */ +# define SPI_CTRLA_DOPO_DOPAD231 (1 << SPI_CTRLA_DOPO_SHIFT) /* D0=PAD2 SCK=PAD3 SS=PAD1 */ +# define SPI_CTRLA_DOPO_DOPAD312 (2 << SPI_CTRLA_DOPO_SHIFT) /* D0=PAD3 SCK=PAD1 SS=PAD2 */ +# define SPI_CTRLA_DOPO_DOPAD031 (3 << SPI_CTRLA_DOPO_SHIFT) /* D0=PAD0 SCK=PAD3 SS=PAD1 */ #define SPI_CTRLA_DIPO_SHIFT (20) /* Bits 20-21: Data in pinout */ #define SPI_CTRLA_DIPO_MASK (3 << SPI_CTRLA_DIPO_SHIFT) -# define SPI_CTRLA_DIPAD0 (0 << SPI_CTRLA_DIPO_SHIFT) /* SERCOM PAD[0] for DI */ -# define SPI_CTRLA_DIPAD1 (1 << SPI_CTRLA_DIPO_SHIFT) /* SERCOM PAD[1] for DI */ -# define SPI_CTRLA_DIPAD2 (2 << SPI_CTRLA_DIPO_SHIFT) /* SERCOM PAD[2] for DI */ -# define SPI_CTRLA_DIPAD3 (3 << SPI_CTRLA_DIPO_SHIFT) /* SERCOM PAD[3] for DI */ +# define SPI_CTRLA_DIPAD0 (0 << SPI_CTRLA_DIPO_SHIFT) /* SERCOM PAD0 for DI */ +# define SPI_CTRLA_DIPAD1 (1 << SPI_CTRLA_DIPO_SHIFT) /* SERCOM PAD1 for DI */ +# define SPI_CTRLA_DIPAD2 (2 << SPI_CTRLA_DIPO_SHIFT) /* SERCOM PAD2 for DI */ +# define SPI_CTRLA_DIPAD3 (3 << SPI_CTRLA_DIPO_SHIFT) /* SERCOM PAD3 for DI */ #define SPI_CTRLA_FORM_SHIFT (24) /* Bits 24-27: Frame format */ #define SPI_CTRLA_FORM_MASK (7 << SPI_CTRLA_FORM_SHIFT) # define SPI_CTRLA_FORM_SPI (0 << SPI_CTRLA_FORM_SHIFT) /* SPI frame (no address) */ diff --git a/nuttx/arch/arm/src/samd/sam_lowputc.c b/nuttx/arch/arm/src/samd/sam_lowputc.c index 1b22d558b..e2548e70b 100644 --- a/nuttx/arch/arm/src/samd/sam_lowputc.c +++ b/nuttx/arch/arm/src/samd/sam_lowputc.c @@ -56,6 +56,8 @@ #include "sam_config.h" +#include <arch/board/board.h> + #include "chip/sam_pm.h" #include "chip/sam_gclk.h" #include "chip/sam_usart.h" @@ -301,7 +303,7 @@ int sam_usart_internal(const struct sam_usart_config_s * const config) /* Configure the GCLKs for the SERCOM module */ sercom_coreclk_configure(config->sercom, config->gclkgen, false); - sercom_slowclk_configure(config->gclkgen); + sercom_slowclk_configure(BOARD_SERCOM_SLOW_GCLKGEN); /* Set USART configuration according to the board configuration */ diff --git a/nuttx/arch/arm/src/samd/sam_sercom.h b/nuttx/arch/arm/src/samd/sam_sercom.h index 77dd9363c..1a179e974 100644 --- a/nuttx/arch/arm/src/samd/sam_sercom.h +++ b/nuttx/arch/arm/src/samd/sam_sercom.h @@ -44,7 +44,9 @@ #include <stdbool.h> +#include "up_arch.h" #include "sam_config.h" +#include "chip/sam_pm.h" /**************************************************************************** * Pre-processor Definitions @@ -85,7 +87,7 @@ extern "C" * ****************************************************************************/ -static inline int sercom_enable(int sercom) +static inline void sercom_enable(int sercom) { uint32_t regval; diff --git a/nuttx/arch/arm/src/samd/sam_serial.c b/nuttx/arch/arm/src/samd/sam_serial.c index 4665befb1..65092a0db 100644 --- a/nuttx/arch/arm/src/samd/sam_serial.c +++ b/nuttx/arch/arm/src/samd/sam_serial.c @@ -561,7 +561,7 @@ static int sam_interrupt(struct uart_dev_s *dev) uint8_t intflag; uint8_t inten; - /* Get the set of pending USART usarts (we are only interested in the + /* Get the set of pending USART interrupts (we are only interested in the * unmasked interrupts). */ diff --git a/nuttx/arch/arm/src/samd/sam_spi.c b/nuttx/arch/arm/src/samd/sam_spi.c index 58be9f423..2b81ee797 100644 --- a/nuttx/arch/arm/src/samd/sam_spi.c +++ b/nuttx/arch/arm/src/samd/sam_spi.c @@ -114,7 +114,9 @@ struct sam_spidev_s /* Fixed configuration */ uint8_t sercom; /* Identifies the SERCOM peripheral */ +#if 0 /* Not used */ uint8_t irq; /* SERCOM IRQ number */ +#endif uint8_t gclkgen; /* Source GCLK generator */ port_pinset_t pad0; /* Pin configuration for PAD0 */ port_pinset_t pad1; /* Pin configuration for PAD1 */ @@ -123,6 +125,9 @@ struct sam_spidev_s uint32_t muxconfig; /* Pad multiplexing configuration */ uint32_t srcfreq; /* Source clock frequency */ uintptr_t base; /* SERCOM base address */ +#if 0 /* Not used */ + xcpt_t handler; /* SERCOM interrupt handler */ +#endif /* Dynamic configuration */ @@ -151,29 +156,54 @@ struct sam_spidev_s /* Helpers */ #ifdef CONFIG_SAMD_SPI_REGDEBUG -static bool spi_checkreg(struct sam_spidev_s *spi, bool wr, +static bool spi_checkreg(struct sam_spidev_s *priv, bool wr, uint32_t regval, uint32_t regaddr); #else -# define spi_checkreg(spi,wr,regval,regaddr) (false) +# define spi_checkreg(priv,wr,regval,regaddr) (false) #endif -static uint8_t spi_getreg8(struct sam_spidev_s *spi, +static uint8_t spi_getreg8(struct sam_spidev_s *priv, unsigned int offset); -static void spi_putreg8(struct sam_spidev_s *spi, uint8_t regval, +static void spi_putreg8(struct sam_spidev_s *priv, uint8_t regval, unsigned int offset); -static uint16_t spi_getreg16(struct sam_spidev_s *spi, +static uint16_t spi_getreg16(struct sam_spidev_s *priv, unsigned int offset); -static void spi_putreg16(struct sam_spidev_s *spi, uint16_t regval, +static void spi_putreg16(struct sam_spidev_s *priv, uint16_t regval, unsigned int offset); -static uint32_t spi_getreg32(struct sam_spidev_s *spi, +static uint32_t spi_getreg32(struct sam_spidev_s *priv, unsigned int offset); -static void spi_putreg32(struct sam_spidev_s *spi, uint32_t regval, +static void spi_putreg32(struct sam_spidev_s *priv, 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); +static void spi_dumpregs(struct sam_spidev_s *priv, const char *msg); #else -# define spi_dumpregs(spi,msg) +# define spi_dumpregs(priv,msg) +#endif + +/* Interrupt handling */ + +#if 0 /* Not used */ +static int spi_interrupt(struct sam_spidev_s *dev); + +#ifdef SAMD_HAVE_USART0 +static int spi0_interrupt(int irq, void *context); +#endif +#ifdef SAMD_HAVE_USART1 +static int spi1_interrupt(int irq, void *context); +#endif +#ifdef SAMD_HAVE_USART2 +static int spi2_interrupt(int irq, void *context); +#endif +#ifdef SAMD_HAVE_USART3 +static int spi3_interrupt(int irq, void *context); +#endif +#ifdef SAMD_HAVE_USART4 +static int spi4_interrupt(int irq, void *context); +#endif +#ifdef SAMD_HAVE_USART5 +static int spi5_interrupt(int irq, void *context); +#endif #endif /* SPI methods */ @@ -234,7 +264,9 @@ static struct sam_spidev_s g_spi0dev = { .ops = &g_spi0ops, .sercom = 0, +#if 0 /* Not used */ .irq = SAM_IRQ_SERCOM0, +#endif .gclkgen = (BOARD_SERCOM0_GCLKGEN >> GCLK_CLKCTRL_GEN_SHIFT), .pad0 = BOARD_SERCOM0_PINMAP_PAD0, .pad1 = BOARD_SERCOM0_PINMAP_PAD1, @@ -243,6 +275,9 @@ static struct sam_spidev_s g_spi0dev = .muxconfig = BOARD_SERCOM0_MUXCONFIG, .srcfreq = BOARD_SERCOM0_FREQUENCY, .base = SAM_SERCOM0_BASE, +#if 0 /* Not used */ + .handler = spi0_interrupt, +#endif #ifndef CONFIG_SPI_OWNBUS .spilock = SEM_INITIALIZER(1), #endif @@ -281,7 +316,9 @@ static struct sam_spidev_s g_spi1dev = { .ops = &g_spi1ops, .sercom = 1, +#if 0 /* Not used */ .irq = SAM_IRQ_SERCOM1, +#endif .gclkgen = (BOARD_SERCOM1_GCLKGEN >> GCLK_CLKCTRL_GEN_SHIFT), .pad0 = BOARD_SERCOM1_PINMAP_PAD0, .pad1 = BOARD_SERCOM1_PINMAP_PAD1, @@ -290,6 +327,9 @@ static struct sam_spidev_s g_spi1dev = .muxconfig = BOARD_SERCOM1_MUXCONFIG, .srcfreq = BOARD_SERCOM1_FREQUENCY, .base = SAM_SERCOM1_BASE, +#if 0 /* Not used */ + .handler = spi1_interrupt, +#endif #ifndef CONFIG_SPI_OWNBUS .spilock = SEM_INITIALIZER(1), #endif @@ -328,7 +368,9 @@ static struct sam_spidev_s g_spi2dev = { .ops = &g_spi1ops, .sercom = 2, +#if 0 /* Not used */ .irq = SAM_IRQ_SERCOM2, +#endif .gclkgen = (BOARD_SERCOM2_GCLKGEN >> GCLK_CLKCTRL_GEN_SHIFT), .pad0 = BOARD_SERCOM2_PINMAP_PAD0, .pad1 = BOARD_SERCOM2_PINMAP_PAD1, @@ -337,6 +379,9 @@ static struct sam_spidev_s g_spi2dev = .muxconfig = BOARD_SERCOM2_MUXCONFIG, .srcfreq = BOARD_SERCOM2_FREQUENCY, .base = SAM_SERCOM2_BASE, +#if 0 /* Not used */ + .handler = spi2_interrupt, +#endif #ifndef CONFIG_SPI_OWNBUS .spilock = SEM_INITIALIZER(1), #endif @@ -375,7 +420,9 @@ static struct sam_spidev_s g_spi3dev = { .ops = &g_spi3ops, .sercom = 3, +#if 0 /* Not used */ .irq = SAM_IRQ_SERCOM3, +#endif .gclkgen = (BOARD_SERCOM3_GCLKGEN >> GCLK_CLKCTRL_GEN_SHIFT), .pad0 = BOARD_SERCOM3_PINMAP_PAD0, .pad1 = BOARD_SERCOM3_PINMAP_PAD1, @@ -384,6 +431,9 @@ static struct sam_spidev_s g_spi3dev = .muxconfig = BOARD_SERCOM3_MUXCONFIG, .srcfreq = BOARD_SERCOM3_FREQUENCY, .base = SAM_SERCOM3_BASE, +#if 0 /* Not used */ + .handler = spi3_interrupt, +#endif #ifndef CONFIG_SPI_OWNBUS .spilock = SEM_INITIALIZER(1), #endif @@ -422,7 +472,9 @@ static struct sam_spidev_s g_spi4dev = { .ops = &g_spi4ops, .sercom = 4, +#if 0 /* Not used */ .irq = SAM_IRQ_SERCOM4, +#endif .gclkgen = (BOARD_SERCOM4_GCLKGEN >> GCLK_CLKCTRL_GEN_SHIFT), .pad0 = BOARD_SERCOM4_PINMAP_PAD0, .pad1 = BOARD_SERCOM4_PINMAP_PAD1, @@ -431,6 +483,9 @@ static struct sam_spidev_s g_spi4dev = .muxconfig = BOARD_SERCOM4_MUXCONFIG, .srcfreq = BOARD_SERCOM4_FREQUENCY, .base = SAM_SERCOM4_BASE, +#if 0 /* Not used */ + .handler = spi4_interrupt, +#endif #ifndef CONFIG_SPI_OWNBUS .spilock = SEM_INITIALIZER(1), #endif @@ -469,7 +524,9 @@ static struct sam_spidev_s g_spi5dev = { .ops = &g_spi5ops, .sercom = 5, +#if 0 /* Not used */ .irq = SAM_IRQ_SERCOM5, +#endif .gclkgen = (BOARD_SERCOM5_GCLKGEN >> GCLK_CLKCTRL_GEN_SHIFT), .pad0 = BOARD_SERCOM5_PINMAP_PAD0, .pad1 = BOARD_SERCOM5_PINMAP_PAD1, @@ -478,6 +535,9 @@ static struct sam_spidev_s g_spi5dev = .muxconfig = BOARD_SERCOM5_MUXCONFIG, .srcfreq = BOARD_SERCOM5_FREQUENCY, .base = SAM_SERCOM5_BASE, +#if 0 /* Not used */ + .handler = spi5_interrupt, +#endif #ifndef CONFIG_SPI_OWNBUS .spilock = SEM_INITIALIZER(1), #endif @@ -718,6 +778,115 @@ static void spi_dumpregs(struct sam_spidev_s *priv, const char *msg) #endif /**************************************************************************** + * Name: spi_interrupt + * + * Description: + * This is the USART interrupt handler. It will be invoked when an + * interrupt received on the 'irq' It should call uart_transmitchars or + * uart_receivechar to perform the appropriate data transfers. The + * interrupt handling logic must be able to map the 'irq' number into the + * approprite sam_spidev_s structure in order to call these functions. + * + ****************************************************************************/ + +#if 0 /* Not used */ +static int spi_interrupt(struct sam_spidev_s *dev) +{ + struct sam_dev_s *priv = (struct sam_dev_s*)dev->priv;; + uint8_t pending; + uint8_t intflag; + uint8_t inten; + + /* Get the set of pending SPI interrupts (we are only interested in the + * unmasked interrupts). + */ + + intflag = sam_serialin8(priv, SAM_USART_INTFLAG_OFFSET); + inten = sam_serialin8(priv, SAM_USART_INTENCLR_OFFSET); + pending = intflag & inten; + + /* Handle an incoming, receive byte. The RXC flag is set when there is + * unread data in DATA register. This flag is cleared by reading the DATA + * register (or by disabling the receiver). + */ + + if ((pending & USART_INT_RXC) != 0) + { + /* Received data ready... process incoming SPI ata */ +#warning Missing logic + } + + /* Handle outgoing, transmit bytes. The DRE flag is set when the DATA + * register is empty and ready to be written. This flag is cleared by + * writing new data to the DATA register. If there is no further data to + * be transmitted, the serial driver will disable TX interrupts, prohibit + * further interrupts until TX interrupts are re-enabled. + */ + + if ((pending & USART_INT_DRE) != 0) + { + /* Transmit data register empty ... process outgoing bytes */ +#warning Missing logic + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: spiN_interrupt + * + * Description: + * Handle each SERCOM USART interrupt by calling the common interrupt + * handling logic with the USART-specific state. + * + ****************************************************************************/ + +#if 0 /* Not used */ +#ifdef SAMD_HAVE_USART0 +static int spi0_interrupt(int irq, void *context) +{ + return spi_interrupt(&g_spi0dev); +} +#endif + +#ifdef SAMD_HAVE_USART1 +static int spi1_interrupt(int irq, void *context) +{ + return spi_interrupt(&g_spi1dev); +} +#endif + +#ifdef SAMD_HAVE_USART2 +static int spi2_interrupt(int irq, void *context) +{ + return spi_interrupt(&g_spi2dev); +} +#endif + +#ifdef SAMD_HAVE_USART3 +static int spi3_interrupt(int irq, void *context) +{ + return spi_interrupt(&g_spi3dev); +} +#endif + +#ifdef SAMD_HAVE_USART4 +static int spi4_interrupt(int irq, void *context) +{ + return spi_interrupt(&g_spi4dev); +} +#endif + +#ifdef SAMD_HAVE_USART5 +static int spi5_interrupt(int irq, void *context) +{ + return spi_interrupt(&g_spi5dev); +} +#endif +#endif + +/**************************************************************************** * Name: spi_lock * * Description: @@ -784,10 +953,23 @@ static int spi_lock(struct spi_dev_s *dev, bool lock) 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 maxfreq; uint32_t actual; + uint32_t baud; spivdbg("sercom=%d frequency=%d\n", priv->sercom, frequency); + /* Check if the configured BAUD is within the valid range */ + + maxfreq = (priv->srcfreq >> 1); + if (frequency > maxfreq) + { + /* Set the frequency to the maximum */ + + spidbg("ERROR: Cannot realize frequency: %ld\n", (long)frequency); + frequency = maxfreq; + } + /* Check if the requested frequency is the same as the frequency selection */ #ifndef CONFIG_SPI_OWNBUS @@ -799,15 +981,33 @@ static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency) } #endif - /* Configure SPI to a frequency as close as possible to the requested - * frequency. + /* For synchronous mode, the BAUAD rate (Fbaud) is generated from the + * source clock frequency (Fref) as follows: + * + * Fbaud = Fref / (2 * (BAUD + 1)) + * + * Or + * + * BAUD = (Fref / (2 * Fbaud)) - 1 + * + * Where BAUD <= 255 */ -#warning Missing logic + + baud = ((priv->srcfreq + frequency) / (frequency << 1)) - 1; + + /* Verify that the resulting if BAUD divisor is within range */ + + if (baud > 255) + { + spidbg("ERROR: BAUD is out of range: %ld\n", (long)baud); + baud = 255; + } + + spi_putreg8(priv, (uint8_t)baud, SAM_SPI_BAUD_OFFSET); /* Calculate the new actual frequency */ -#warning Missing logic - spivdbg("actual=%d\n", offset, regval, actual); + actual = priv->srcfreq / ((baud + 1) << 1); /* Save the frequency setting */ @@ -816,7 +1016,7 @@ static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency) priv->actual = actual; #endif - spidbg("Frequency %d->%d\n", frequency, actual); + spivdbg("Frequency %d->%d\n", frequency, actual); return actual; } @@ -909,13 +1109,6 @@ static void spi_setbits(struct spi_dev_s *dev, int nbits) 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 @@ -1001,12 +1194,28 @@ 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; + const uint16_t *ptx16 = NULL; + const uint8_t *ptx8 = NULL; + uint16_t *prx16 = NULL; + uint8_t *prx8 = NULL; uint16_t data; spivdbg("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); + /* Set up data receive and transmit pointers */ + + wide = ; + if (priv->nbits > 8) + { + ptx16 = (const uint16_t *)txbuffer; + prx16 = (uint16_t *)rxbuffer; + } + else + { + ptx8 = (const uint8_t *)txbuffer; + prx8 = (uint8_t *)rxbuffer; + } + /* Loop, sending each word in the user-provided data buffer. * * Note 1: Right now, this only deals with 8-bit words. If the SPI @@ -1040,13 +1249,17 @@ static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer, { /* Get the data to send (0xff if there is no data source) */ - if (txptr) + if (ptx8) + { + data = (uint16_t)*ptx8++; + } + else if (ptx16) { - data = (uint32_t)*txptr++; + data = *ptx16++; } else { - data = 0xffff; + data = 0x01ff; } /* Wait for any previous data written to the DATA register to be @@ -1063,14 +1276,33 @@ static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer, while ((spi_getreg32(priv, SAM_SPI_INTFLAG_OFFSET) & SPI_INT_RXC) == 0); + /* Check for data overflow. The BUFOVF bit provides the status of the + * next DATA to be read. On buffer overflow, the corresponding DATA + * will be 0. + */ + + data = spi_getreg16(priv, SAM_SPI_STATUS_OFFSET); + if (data & SPI_STATUS_BUFOVF) != 0) + { + spidbg("ERROR: Buffer overflow!\n"); + + /* Clear the buffer overflow flag */ + + spi_putreg(priv, data, SAM_SPI_STATUS_OFFSET); + } + /* 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) + if (prx8) { - *rxptr++ = (uint8_t)data; + *prx8++ = (uint8_t)data; + } + else if (prx16) + { + *prx16++ = (uint16_t)data; } } } @@ -1132,6 +1364,19 @@ static void spi_recvblock(struct spi_dev_s *dev, void *buffer, size_t nwords) #endif /**************************************************************************** + * Name: spi_wait_synchronization + * + * Description: + * Wait until the SERCOM SPI reports that it is synchronized. + * + ****************************************************************************/ + +static void spi_wait_synchronization(struct sam_spidev_s *priv) +{ + while ((getreg16(priv->base + SAM_USART_STATUS_OFFSET) & USART_STATUS_SYNCBUSY) != 0); +} + +/**************************************************************************** * Name: spi_pad_configure * * Description: @@ -1186,10 +1431,9 @@ 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 + uint32_t baud; + int ret; /* Get the port state structure */ @@ -1247,36 +1491,84 @@ struct spi_dev_s *up_spiinitialize(int port) return NULL; } - /* Enable clocking to the SPI SERCOM */ + /* Enable clocking to the SERCOM module in PM */ - flags = irqsave(); -#warning Missing logic + flags = irqsave(); + sercom_enable(priv->sercom); - /* Configure multiplexed pins as connected on the board. */ -#warning Missing logic + /* Configure the GCLKs for the SERCOM module */ - /* Execute a software reset of the SPI SERCOM */ -#warning Missing logic + sercom_coreclk_configure(priv->sercom, priv->gclkgen, false); + sercom_slowclk_configure(BOARD_SERCOM_SLOW_GCLKGEN); - /* Configure the SPI SERCOM */ -#warning Missing logic + /* Set the SERCOM in SPI master mode */ - /* And enable the SPI */ -#warning Missing logic + regval = spi_getreg32(priv, SAM_SPI_CTRLA_OFFSET); + regval &= ~SPI_CTRLA_MODE_MASK; + regval |= SPI_CTRLA_MODE_MASTER; + spi_putreg32(priv, regval, SAM_SPI_CTRLA_OFFSET); - spi_dumpregs(priv, "After initialization"); + /* Configure pads */ + + spi_pad_configure(priv); + + /* Set an initial baud value. This will be changed by the upper-half + * driver as soon as it starts. + */ + + (void)spi_setfrequency((struct spi_dev_s *)priv, 400000); + + /* Set MSB first data order and the configured pad mux setting, + * Note that SPI mode 0 are assumed initially. + */ + + regval = (SPI_DATA_ORDER_MSB | priv->muxconfig); + spi_putreg8(priv, regval, SAM_SPI_CTRLA_OFFSET); + + /* Enable the receiver. Note that 8-bit data width is assumed initially */ + + regval = SPI_CTRLB_RXEN; + spi_putreg8(priv, regval, SAM_SPI_CTRLB_OFFSET); #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. + priv->nbits = nbits; +#endif + + /* Wait until the synchronization is complete */ + + spi_wait_synchronization(priv); + + /* Enable SPI */ + + regval = spi_getreg32(priv, SAM_SPI_CTRLA_OFFSET); + regval |= SPI_CTRLA_ENABLE; + spi_putreg32(priv, regval, SAM_SPI_CTRLA_OFFSET); + + /* Disable all interrupts at the SPI source and clear all pending + * status that we can. */ - 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); + spi_putreg8(priv, SPI_INT_ALL, SAM_SPI_INTENCLR_OFFSET); + spi_putreg8(priv, SPI_INT_ALL, SAM_SPI_INTFLAG_OFFSET); + spi_putreg16(priv, SPI_STATUS_CLRALL, SAM_SPI_STATUS_OFFSET); + +#if 0 /* Not used */ + /* Attach and enable the SERCOM interrupt handler */ + + ret = irq_attach(priv->irq, priv->handler); + if (ret < 0) + { + spidbg("ERROR: Failed to attach interrupt: %d\n", irq); + return NULL; + } + + /* Enable SERCOM interrupts at the NVIC */ + + up_enable_irq(priv->irq); #endif + spi_dumpregs(priv, "After initialization"); + irqrestore(flags); return (struct spi_dev_s *)priv; } diff --git a/nuttx/configs/samd20-xplained/include/board.h b/nuttx/configs/samd20-xplained/include/board.h index 7b04891a0..a17d630ad 100644 --- a/nuttx/configs/samd20-xplained/include/board.h +++ b/nuttx/configs/samd20-xplained/include/board.h @@ -355,7 +355,65 @@ #endif /* SERCOM definitions ***************************************************************/ -/* SERCOM4 is available on connectors EXT1, EXT2, and EXT3 +/* This is the source clock generator for the GCLK_SERCOM_SLOW clock that is common + * to all SERCOM modules. + */ + +#define BOARD_SERCOM_SLOW_GCLKGEN (GCLK_CLKCTRL_GEN0 >> GCLK_CLKCTRL_GEN_SHIFT) + +/* SERCOM0 SPI is available on EXT1 + * + * PIN PORT SERCOM FUNCTION + * --- ------------------ ----------- + * 15 PA5 SERCOM0 PAD1 SPI SS + * 16 PA6 SERCOM0 PAD2 SPI MOSI + * 17 PA4 SERCOM0 PAD0 SPI MISO + * 18 PA7 SERCOM0 PAD3 SPI SCK + */ + +#define BOARD_SERCOM0_CLKGEN GCLK_CLKCTRL_GEN0 +#define BOARD_SERCOM0_MUX_SETTING (SPI_CTRLA_DOPO_DOPAD231 | SPI_CTRLA_DIPAD0) +#define BOARD_SERCOM0_PINMUX_PAD0 PINMUX_PA04D_SERCOM0_PAD0 /* SPI_MISO */ +#define BOARD_SERCOM0_PINMUX_PAD1 0 /* microSD_SS */ +#define BOARD_SERCOM0_PINMUX_PAD2 PINMUX_PA06D_SERCOM0_PAD2 /* SPI_MOSI */ +#define BOARD_SERCOM0_PINMUX_PAD3 PINMUX_PA07D_SERCOM0_PAD3 /* SPI_SCK */ + +/* SERCOM1 SPI is available on EXT2 + * + * PIN PORT SERCOM FUNCTION + * --- ------------------ ----------- + * 15 PA17 SERCOM1 PAD1 SPI SS + * 16 PA18 SERCOM1 PAD2 SPI MOSI + * 17 PA16 SERCOM1 PAD0 SPI MISO + * 18 PA19 SERCOM1 PAD3 SPI SCK + */ + +#define BOARD_SERCOM1_CLKGEN GCLK_CLKCTRL_GEN0 +#define BOARD_SERCOM1_MUX_SETTING (SPI_CTRLA_DOPO_DOPAD231 | SPI_CTRLA_DIPAD0) +#define BOARD_SERCOM1_PINMUX_PAD0 PINMUX_PA16C_SERCOM1_PAD0 /* SPI_MISO */ +#define BOARD_SERCOM1_PINMUX_PAD1 0 /* microSD_SS */ +#define BOARD_SERCOM1_PINMUX_PAD2 PINMUX_PA18C_SERCOM1_PAD2 /* SPI_MOSI */ +#define BOARD_SERCOM1_PINMUX_PAD3 PINMUX_PA19C_SERCOM1_PAD3 /* SPI_SCK */ + +/* The SAMD20 Xplained Pro contains an Embedded Debugger (EDBG) that can be + * used to program and debug the ATSAMD20J18A using Serial Wire Debug (SWD). + * The Embedded debugger also include a Virtual COM port interface over + * SERCOM3. Virtual COM port connections: + * + * PA24 SERCOM3 / USART TXD + * PA25 SERCOM3 / USART RXD + */ + +#define BOARD_SERCOM3_GCLKGEN GCLK_CLKCTRL_GEN0 +#define BOARD_SERCOM3_MUXCONFIG (USART_CTRLA_RXPAD3 | USART_CTRLA_TXPAD2) +#define BOARD_SERCOM3_PINMAP_PAD0 0 +#define BOARD_SERCOM3_PINMAP_PAD1 0 +#define BOARD_SERCOM3_PINMAP_PAD2 PORT_SERCOM3_PAD2_1 /* USART TX */ +#define BOARD_SERCOM3_PINMAP_PAD3 PORT_SERCOM3_PAD3_1 /* USART RX */ + +#define BOARD_SERCOM3_FREQUENCY BOARD_GCLK0_FREQUENCY + +/* The SERCOM4 USART is available on connectors EXT1, EXT2, and EXT3 * * PIN EXT1 EXT2 EXT3 GPIO Function * ---- ---- ---- ---- ------------------ @@ -374,43 +432,42 @@ #if defined(CONFIG_SAMD20_XPLAINED_USART4_EXT1) # define BOARD_SERCOM4_MUXCONFIG (USART_CTRLA_RXPAD1 | USART_CTRLA_TXPAD0) -# define BOARD_SERCOM4_PINMAP_PAD0 PORT_SERCOM4_PAD0_3 -# define BOARD_SERCOM4_PINMAP_PAD1 PORT_SERCOM4_PAD1_3 +# define BOARD_SERCOM4_PINMAP_PAD0 PORT_SERCOM4_PAD0_3 /* USART TX */ +# define BOARD_SERCOM4_PINMAP_PAD1 PORT_SERCOM4_PAD1_3 /* USART RX */ # define BOARD_SERCOM4_PINMAP_PAD2 0 # define BOARD_SERCOM4_PINMAP_PAD3 0 #elif defined(CONFIG_SAMD20_XPLAINED_USART4_EXT2) # define BOARD_SERCOM4_MUXCONFIG (USART_CTRLA_RXPAD1 | USART_CTRLA_TXPAD0) -# define BOARD_SERCOM4_PINMAP_PAD0 PORT_SERCOM4_PAD0_1 -# define BOARD_SERCOM4_PINMAP_PAD1 PORT_SERCOM4_PAD1_1 +# define BOARD_SERCOM4_PINMAP_PAD0 PORT_SERCOM4_PAD0_1 /* USART TX */ +# define BOARD_SERCOM4_PINMAP_PAD1 PORT_SERCOM4_PAD1_1 /* USART RX */ # define BOARD_SERCOM4_PINMAP_PAD2 0 # define BOARD_SERCOM4_PINMAP_PAD3 0 #else /* if defined(CONFIG_SAMD20_XPLAINED_USART4_EXT3) */ # define BOARD_SERCOM4_MUXCONFIG (USART_CTRLA_RXPAD3 | USART_CTRLA_TXPAD2) # define BOARD_SERCOM4_PINMAP_PAD0 0 # define BOARD_SERCOM4_PINMAP_PAD1 0 -# define BOARD_SERCOM4_PINMAP_PAD2 PORT_SERCOM4_PAD2_3 -# define BOARD_SERCOM4_PINMAP_PAD3 PORT_SERCOM4_PAD3_3 +# define BOARD_SERCOM4_PINMAP_PAD2 PORT_SERCOM4_PAD2_3 /* USART TX */ +# define BOARD_SERCOM4_PINMAP_PAD3 PORT_SERCOM4_PAD3_3 /* USART RX */ #endif #define BOARD_SERCOM4_FREQUENCY BOARD_GCLK0_FREQUENCY -/* The SAMD20 Xplained Pro contains an Embedded Debugger (EDBG) that can be - * used to program and debug the ATSAMD20J18A using Serial Wire Debug (SWD). - * The Embedded debugger also include a Virtual COM port interface over - * SERCOM3. Virtual COM port connections: +/* SERCOM5 SPI is available on EXT3 * - * PA24 SERCOM3 / USART TXD - * PA25 SERCOM3 / USART RXD + * PIN PORT SERCOM FUNCTION + * --- ------------------ ----------- + * 15 PB17 SERCOM5 PAD1 SPI SS + * 16 PB22 SERCOM5 PAD2 SPI MOSI + * 17 PB16 SERCOM5 PAD0 SPI MISO + * 18 PB23 SERCOM5 PAD3 SPI SCK */ -#define BOARD_SERCOM3_GCLKGEN GCLK_CLKCTRL_GEN0 -#define BOARD_SERCOM3_MUXCONFIG (USART_CTRLA_RXPAD3 | USART_CTRLA_TXPAD2) -#define BOARD_SERCOM3_PINMAP_PAD0 0 -#define BOARD_SERCOM3_PINMAP_PAD1 0 -#define BOARD_SERCOM3_PINMAP_PAD2 PORT_SERCOM3_PAD2_1 -#define BOARD_SERCOM3_PINMAP_PAD3 PORT_SERCOM3_PAD3_1 - -#define BOARD_SERCOM3_FREQUENCY BOARD_GCLK0_FREQUENCY +#define BOARD_SERCOM5_CLKGEN GCLK_CLKCTRL_GEN0 +#define BOARD_SERCOM5_MUX_SETTING (SPI_CTRLA_DOPO_DOPAD231 | SPI_CTRLA_DIPAD0) +#define BOARD_SERCOM5_PINMUX_PAD0 PINMUX_PB16C_SERCOM5_PAD0 /* SPI_MISO */ +#define BOARD_SERCOM5_PINMUX_PAD1 0 /* microSD_SS */ +#define BOARD_SERCOM5_PINMUX_PAD2 PINMUX_PB22D_SERCOM5_PAD2 /* SPI_MOSI */ +#define BOARD_SERCOM5_PINMUX_PAD3 PINMUX_PB23D_SERCOM5_PAD3 /* SPI_SCK */ /* LED definitions ******************************************************************/ /* There are three LEDs on board the SAMD20 Xplained Pro board: The EDBG |