From ccfcb12ef76745d2ea47e6b987ada918573a4a8d Mon Sep 17 00:00:00 2001 From: David Sidrane Date: Tue, 14 Jun 2016 07:11:55 -0600 Subject: [PATCH] STM32F7: Add SPI driver. DMA not yet supported. --- arch/arm/src/stm32f7/Kconfig | 31 + arch/arm/src/stm32f7/Make.defs | 4 + arch/arm/src/stm32f7/chip/stm32_spi.h | 258 ++++ arch/arm/src/stm32f7/stm32_spi.c | 1832 +++++++++++++++++++++++++ arch/arm/src/stm32f7/stm32_spi.h | 35 +- 5 files changed, 2143 insertions(+), 17 deletions(-) create mode 100644 arch/arm/src/stm32f7/chip/stm32_spi.h create mode 100644 arch/arm/src/stm32f7/stm32_spi.c diff --git a/arch/arm/src/stm32f7/Kconfig b/arch/arm/src/stm32f7/Kconfig index 47bd03c3e1..72a6935112 100644 --- a/arch/arm/src/stm32f7/Kconfig +++ b/arch/arm/src/stm32f7/Kconfig @@ -1613,6 +1613,25 @@ config STM32F7_SERIALBRK_BSDCOMPAT endmenu # U[S]ART Configuration +menu "SPI Configuration" + depends on STM32F7_SPI + +config STM32F7_SPI_INTERRUPTS + bool "Interrupt driver SPI" + default n + ---help--- + Select to enable interrupt driven SPI support. Non-interrupt-driven, + poll-waiting is recommended if the interrupt rate would be to high in + the interrupt driven case. + +config STM32F7_SPI_DMA + bool "SPI DMA" + default n + ---help--- + Use DMA to improve SPI transfer performance. Cannot be used with STM32F7_SPI_INTERRUPT. + +endmenu # "SPI Configuration" + config STM32F7_CUSTOM_CLOCKCONFIG bool "Custom clock configuration" default n @@ -1628,6 +1647,18 @@ config STM32F7_DTCM_PROCFS will provide statistics about DTCM memory use similar to what you would get from mallinfo() for the user heap. +config STM32F7_DMACAPABLE + bool "Workaround non-DMA capable memory" + depends on ARCH_DMA + default y if !STM32_CCMEXCLUDE + default n if STM32_CCMEXCLUDE + ---help--- + This option enables the DMA interface stm32_dmacapable that can be + used to check if it is possible to do DMA from the selected address. + Drivers then may use this information to determine if they should + attempt the DMA or fall back to a different transfer method. + + if STM32F7_ETHMAC menu "Ethernet MAC configuration" diff --git a/arch/arm/src/stm32f7/Make.defs b/arch/arm/src/stm32f7/Make.defs index a23e141e21..c8ac72f1b9 100644 --- a/arch/arm/src/stm32f7/Make.defs +++ b/arch/arm/src/stm32f7/Make.defs @@ -145,6 +145,10 @@ CHIP_CSRCS += stm32_exti_alarm.c endif endif +ifeq ($(CONFIG_STM32F7_SPI),y) +CHIP_CSRCS += stm32_spi.c +endif + ifeq ($(CONFIG_STM32F7_ETHMAC),y) CHIP_CSRCS += stm32_ethernet.c endif diff --git a/arch/arm/src/stm32f7/chip/stm32_spi.h b/arch/arm/src/stm32f7/chip/stm32_spi.h new file mode 100644 index 0000000000..dbd4d7301e --- /dev/null +++ b/arch/arm/src/stm32f7/chip/stm32_spi.h @@ -0,0 +1,258 @@ +/************************************************************************************ + * arch/arm/src/stm32f7/chip/stm32_spi.h + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * David Sidrane + * + * 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_STC_STM32F7_CHIP_STM32_SPI_H +#define __ARCH_ARM_STC_STM32F7_CHIP_STM32_SPI_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include "chip.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Maximum allowed speed as per data sheet for all SPIs (both pclk1 and pclk2)*/ + +#if defined(CONFIG_STM32F7_STM32F74XX) || defined(CONFIG_STM32F7_STM32F75XX) +# define STM32_SPI_CLK_MAX 50000000UL +#elif defined(CONFIG_STM32F7_STM32F76XX) || defined(CONFIG_STM32F7_STM32F77XX) +# define STM32_SPI_CLK_MAX 54000000UL +#endif + +/* Register Offsets *****************************************************************/ + +#define STM32_SPI_CR1_OFFSET 0x0000 /* SPI Control Register 1 (16-bit) */ +#define STM32_SPI_CR2_OFFSET 0x0004 /* SPI control register 2 (16-bit) */ +#define STM32_SPI_SR_OFFSET 0x0008 /* SPI status register (16-bit) */ +#define STM32_SPI_DR_OFFSET 0x000c /* SPI data register (16-bit) */ +#define STM32_SPI_CRCPR_OFFSET 0x0010 /* SPI CRC polynomial register (16-bit) */ +#define STM32_SPI_RXCRCR_OFFSET 0x0014 /* SPI Rx CRC register (16-bit) */ +#define STM32_SPI_TXCRCR_OFFSET 0x0018 /* SPI Tx CRC register (16-bit) */ +#define STM32_SPI_I2SCFGR_OFFSET 0x001c /* I2S configuration register */ +#define STM32_SPI_I2SPR_OFFSET 0x0020 /* I2S prescaler register */ + +/* Register Addresses ***************************************************************/ + +#if STM32F7_NSPI > 0 +# define STM32_SPI1_CR1 (STM32_SPI1_BASE+STM32_SPI_CR1_OFFSET) +# define STM32_SPI1_CR2 (STM32_SPI1_BASE+STM32_SPI_CR2_OFFSET) +# define STM32_SPI1_SR (STM32_SPI1_BASE+STM32_SPI_SR_OFFSET) +# define STM32_SPI1_DR (STM32_SPI1_BASE+STM32_SPI_DR_OFFSET) +# define STM32_SPI1_CRCPR (STM32_SPI1_BASE+STM32_SPI_CRCPR_OFFSET) +# define STM32_SPI1_RXCRCR (STM32_SPI1_BASE+STM32_SPI_RXCRCR_OFFSET) +# define STM32_SPI1_TXCRCR (STM32_SPI1_BASE+STM32_SPI_TXCRCR_OFFSET) +#endif + +#if STM32F7_NSPI > 1 +# define STM32_SPI2_CR1 (STM32_SPI2_BASE+STM32_SPI_CR1_OFFSET) +# define STM32_SPI2_CR2 (STM32_SPI2_BASE+STM32_SPI_CR2_OFFSET) +# define STM32_SPI2_SR (STM32_SPI2_BASE+STM32_SPI_SR_OFFSET) +# define STM32_SPI2_DR (STM32_SPI2_BASE+STM32_SPI_DR_OFFSET) +# define STM32_SPI2_CRCPR (STM32_SPI2_BASE+STM32_SPI_CRCPR_OFFSET) +# define STM32_SPI2_RXCRCR (STM32_SPI2_BASE+STM32_SPI_RXCRCR_OFFSET) +# define STM32_SPI2_TXCRCR (STM32_SPI2_BASE+STM32_SPI_TXCRCR_OFFSET) +# define STM32_SPI2_I2SCFGR (STM32_SPI2_BASE+STM32_SPI_I2SCFGR_OFFSET) +# define STM32_SPI2_I2SPR (STM32_SPI2_BASE+STM32_SPI_I2SPR_OFFSET) +#endif + +#if STM32F7_NSPI > 2 +# define STM32_SPI3_CR1 (STM32_SPI3_BASE+STM32_SPI_CR1_OFFSET) +# define STM32_SPI3_CR2 (STM32_SPI3_BASE+STM32_SPI_CR2_OFFSET) +# define STM32_SPI3_SR (STM32_SPI3_BASE+STM32_SPI_SR_OFFSET) +# define STM32_SPI3_DR (STM32_SPI3_BASE+STM32_SPI_DR_OFFSET) +# define STM32_SPI3_CRCPR (STM32_SPI3_BASE+STM32_SPI_CRCPR_OFFSET) +# define STM32_SPI3_RXCRCR (STM32_SPI3_BASE+STM32_SPI_RXCRCR_OFFSET) +# define STM32_SPI3_TXCRCR (STM32_SPI3_BASE+STM32_SPI_TXCRCR_OFFSET) +# define STM32_SPI3_I2SCFGR (STM32_SPI3_BASE+STM32_SPI_I2SCFGR_OFFSET) +# define STM32_SPI3_I2SPR (STM32_SPI3_BASE+STM32_SPI_I2SPR_OFFSET) +#endif + +#if STM32F7_NSPI > 3 +# define STM32_SPI4_CR1 (STM32_SPI4_BASE+STM32_SPI_CR1_OFFSET) +# define STM32_SPI4_CR2 (STM32_SPI4_BASE+STM32_SPI_CR2_OFFSET) +# define STM32_SPI4_SR (STM32_SPI4_BASE+STM32_SPI_SR_OFFSET) +# define STM32_SPI4_DR (STM32_SPI4_BASE+STM32_SPI_DR_OFFSET) +# define STM32_SPI4_CRCPR (STM32_SPI4_BASE+STM32_SPI_CRCPR_OFFSET) +# define STM32_SPI4_RXCRCR (STM32_SPI4_BASE+STM32_SPI_RXCRCR_OFFSET) +# define STM32_SPI4_TXCRCR (STM32_SPI4_BASE+STM32_SPI_TXCRCR_OFFSET) +# define STM32_SPI4_I2SCFGR (STM32_SPI4_BASE+STM32_SPI_I2SCFGR_OFFSET) +# define STM32_SPI4_I2SPR (STM32_SPI4_BASE+STM32_SPI_I2SPR_OFFSET) +#endif + +#if STM32F7_NSPI > 4 +# define STM32_SPI5_CR1 (STM32_SPI5_BASE+STM32_SPI_CR1_OFFSET) +# define STM32_SPI5_CR2 (STM32_SPI5_BASE+STM32_SPI_CR2_OFFSET) +# define STM32_SPI5_SR (STM32_SPI5_BASE+STM32_SPI_SR_OFFSET) +# define STM32_SPI5_DR (STM32_SPI5_BASE+STM32_SPI_DR_OFFSET) +# define STM32_SPI5_CRCPR (STM32_SPI5_BASE+STM32_SPI_CRCPR_OFFSET) +# define STM32_SPI5_RXCRCR (STM32_SPI5_BASE+STM32_SPI_RXCRCR_OFFSET) +# define STM32_SPI5_TXCRCR (STM32_SPI5_BASE+STM32_SPI_TXCRCR_OFFSET) +# define STM32_SPI5_I2SCFGR (STM32_SPI5_BASE+STM32_SPI_I2SCFGR_OFFSET) +# define STM32_SPI5_I2SPR (STM32_SPI5_BASE+STM32_SPI_I2SPR_OFFSET) +#endif + +#if STM32F7_NSPI > 5 +# define STM32_SPI6_CR1 (STM32_SPI6_BASE+STM32_SPI_CR1_OFFSET) +# define STM32_SPI6_CR2 (STM32_SPI6_BASE+STM32_SPI_CR2_OFFSET) +# define STM32_SPI6_SR (STM32_SPI6_BASE+STM32_SPI_SR_OFFSET) +# define STM32_SPI6_DR (STM32_SPI6_BASE+STM32_SPI_DR_OFFSET) +# define STM32_SPI6_CRCPR (STM32_SPI6_BASE+STM32_SPI_CRCPR_OFFSET) +# define STM32_SPI6_RXCRCR (STM32_SPI6_BASE+STM32_SPI_RXCRCR_OFFSET) +# define STM32_SPI6_TXCRCR (STM32_SPI6_BASE+STM32_SPI_TXCRCR_OFFSET) +# define STM32_SPI6_I2SCFGR (STM32_SPI6_BASE+STM32_SPI_I2SCFGR_OFFSET) +# define STM32_SPI6_I2SPR (STM32_SPI6_BASE+STM32_SPI_I2SPR_OFFSET) +#endif + +/* Register Bitfield Definitions ****************************************************/ + +/* SPI Control Register 1 */ + +#define SPI_CR1_CPHA (1 << 0) /* Bit 0: Clock Phase */ +#define SPI_CR1_CPOL (1 << 1) /* Bit 1: Clock Polarity */ +#define SPI_CR1_MSTR (1 << 2) /* Bit 2: Master Selection */ +#define SPI_CR1_BR_SHIFT (3) /* Bits 5:3 Baud Rate Control */ +#define SPI_CR1_BR_MASK (7 << SPI_CR1_BR_SHIFT) +# define SPI_CR1_FPCLCKd2 (0 << SPI_CR1_BR_SHIFT) /* 000: fPCLK/2 */ +# define SPI_CR1_FPCLCKd4 (1 << SPI_CR1_BR_SHIFT) /* 001: fPCLK/4 */ +# define SPI_CR1_FPCLCKd8 (2 << SPI_CR1_BR_SHIFT) /* 010: fPCLK/8 */ +# define SPI_CR1_FPCLCKd16 (3 << SPI_CR1_BR_SHIFT) /* 011: fPCLK/16 */ +# define SPI_CR1_FPCLCKd32 (4 << SPI_CR1_BR_SHIFT) /* 100: fPCLK/32 */ +# define SPI_CR1_FPCLCKd64 (5 << SPI_CR1_BR_SHIFT) /* 101: fPCLK/64 */ +# define SPI_CR1_FPCLCKd128 (6 << SPI_CR1_BR_SHIFT) /* 110: fPCLK/128 */ +# define SPI_CR1_FPCLCKd256 (7 << SPI_CR1_BR_SHIFT) /* 111: fPCLK/256 */ +#define SPI_CR1_SPE (1 << 6) /* Bit 6: SPI Enable */ +#define SPI_CR1_LSBFIRST (1 << 7) /* Bit 7: Frame Format */ +#define SPI_CR1_SSI (1 << 8) /* Bit 8: Internal slave select */ +#define SPI_CR1_SSM (1 << 9) /* Bit 9: Software slave management */ +#define SPI_CR1_RXONLY (1 << 10) /* Bit 10: Receive only */ +#define SPI_CR1_CRCL (1 << 11) /* Bit 11: CRC length */ +#define SPI_CR1_CRCNEXT (1 << 12) /* Bit 12: Transmit CRC next */ +#define SPI_CR1_CRCEN (1 << 13) /* Bit 13: Hardware CRC calculation enable */ +#define SPI_CR1_BIDIOE (1 << 14) /* Bit 14: Output enable in bidirectional mode */ +#define SPI_CR1_BIDIMODE (1 << 15) /* Bit 15: Bidirectional data mode enable */ + +/* SPI Control Register 2 */ + +#define SPI_CR2_RXDMAEN (1 << 0) /* Bit 0: Rx Buffer DMA Enable */ +#define SPI_CR2_TXDMAEN (1 << 1) /* Bit 1: Tx Buffer DMA Enable */ +#define SPI_CR2_SSOE (1 << 2) /* Bit 2: SS Output Enable */ +#define SPI_CR2_NSSP (1 << 3) /* Bit 3 NSSP: NSS pulse management */ +#define SPI_CR2_FRF (1 << 4) /* Bit 4: Frame format */ +#define SPI_CR2_ERRIE (1 << 5) /* Bit 5: Error interrupt enable */ +#define SPI_CR2_RXNEIE (1 << 6) /* Bit 6: RX buffer not empty interrupt enable */ +#define SPI_CR2_TXEIE (1 << 7) /* Bit 7: Tx buffer empty interrupt enable */ +#define SPI_CR2_DS_SHIFT (8) /* Bits 8-11: Data size */ +#define SPI_CR2_DS_MASK (0xf << SPI_CR2_DS_SHIFT) +# define SPI_CR2_DS_VAL(bits) (((bits)-1) << SPI_CR2_DS_SHIFT) +# define SPI_CR2_DS_4BIT SPI_CR2_DS_VAL(4) +# define SPI_CR2_DS_5BIT SPI_CR2_DS_VAL(5) +# define SPI_CR2_DS_6BIT SPI_CR2_DS_VAL(6) +# define SPI_CR2_DS_7BIT SPI_CR2_DS_VAL(7) +# define SPI_CR2_DS_8BIT SPI_CR2_DS_VAL(8) +# define SPI_CR2_DS_9BIT SPI_CR2_DS_VAL(9) +# define SPI_CR2_DS_10BIT SPI_CR2_DS_VAL(10) +# define SPI_CR2_DS_11BIT SPI_CR2_DS_VAL(11) +# define SPI_CR2_DS_12BIT SPI_CR2_DS_VAL(12) +# define SPI_CR2_DS_13BIT SPI_CR2_DS_VAL(13) +# define SPI_CR2_DS_14BIT SPI_CR2_DS_VAL(14) +# define SPI_CR2_DS_15BIT SPI_CR2_DS_VAL(15) +# define SPI_CR2_DS_16BIT SPI_CR2_DS_VAL(16) +#define SPI_CR2_FRXTH (1 << 12) /* Bit 12: FIFO reception threshold */ +#define SPI_CR2_LDMARX (1 << 13) /* Bit 13: Last DMA transfer for receptione */ +#define SPI_CR2_LDMATX (1 << 14) /* Bit 14: Last DMA transfer for transmission */ + +/* SPI status register */ + +#define SPI_SR_RXNE (1 << 0) /* Bit 0: Receive buffer not empty */ +#define SPI_SR_TXE (1 << 1) /* Bit 1: Transmit buffer empty */ +#define SPI_SR_CHSIDE (1 << 2) /* Bit 2: Channel side (i2s) */ +#define SPI_SR_UDR (1 << 3) /* Bit 3: Underrun flag (i2s) */ +#define SPI_SR_CRCERR (1 << 4) /* Bit 4: CRC error flag */ +#define SPI_SR_MODF (1 << 5) /* Bit 5: Mode fault */ +#define SPI_SR_OVR (1 << 6) /* Bit 6: Overrun flag */ +#define SPI_SR_BSY (1 << 7) /* Bit 7: Busy flag */ +#define SPI_SR_FRE (1 << 8) /* Bit 8: Frame format error */ +#define SPI_SR_FRLVL_SHIFT (9) /* Bits 9-10: FIFO reception level */ +#define SPI_SR_FRLVL_MASK (0x3 << SPI_SR_FRLVL_SHIFT) +# define SPI_SR_FRLVL_EMPTY (0 << SPI_SR_FRLVL_SHIFT) /* FIFO empty */ +# define SPI_SR_FRLVL_QUARTER (1 << SPI_SR_FRLVL_SHIFT) /* 1/4 FIFO */ +# define SPI_SR_FRLVL_HALF (2 << SPI_SR_FRLVL_SHIFT) /* 1/2 FIFO */ +# define SPI_SR_FRLVL_FULL (3 << SPI_SR_FRLVL_SHIFT) /* FIFO full */ +#define SPI_SR_FTLVL_SHIFT (11) /* Bits 11-12: FIFO transmission level */ +#define SPI_SR_FTLVL_MASK (0x3 << SPI_SR_FTLVL_SHIFT) +# define SPI_SR_FTLVL_EMPTY (0 << SPI_SR_FTLVL_SHIFT) /* FIFO empty */ +# define SPI_SR_FTLVL_QUARTER (1 << SPI_SR_FTLVL_SHIFT) /* 1/4 FIFO */ +# define SPI_SR_FTLVL_HALF (2 << SPI_SR_FTLVL_SHIFT) /* 1/2 FIFO */ +# define SPI_SR_FTLVL_FULL (3 << SPI_SR_FTLVL_SHIFT) /* FIFO full */ + +/* I2S configuration register */ + +#define SPI_I2SCFGR_CHLEN (1 << 0) /* Bit 0: Channel length (number of bits per audio channel) */ +#define SPI_I2SCFGR_DATLEN_SHIFT (1) /* Bit 1-2: Data length to be transferred */ +#define SPI_I2SCFGR_DATLEN_MASK (3 << SPI_I2SCFGR_DATLEN_SHIFT) +# define SPI_I2SCFGR_DATLEN_16BIT (0 << SPI_I2SCFGR_DATLEN_SHIFT) /* 00: 16-bit data length */ +# define SPI_I2SCFGR_DATLEN_8BIT (1 << SPI_I2SCFGR_DATLEN_SHIFT) /* 01: 24-bit data length */ +# define SPI_I2SCFGR_DATLEN_32BIT (2 << SPI_I2SCFGR_DATLEN_SHIFT) /* 10: 32-bit data length */ +#define SPI_I2SCFGR_CKPOL (1 << 3) /* Bit 3: Steady state clock polarity */ +#define SPI_I2SCFGR_I2SSTD_SHIFT (4) /* Bit 4-5: I2S standard selection */ +#define SPI_I2SCFGR_I2SSTD_MASK (3 << SPI_I2SCFGR_I2SSTD_SHIFT) +# define SPI_I2SCFGR_I2SSTD_PHILLIPS (00 << SPI_I2SCFGR_I2SSTD_SHIFT) /* 00: I2S Phillips standard. */ +# define SPI_I2SCFGR_I2SSTD_MSB (1 << SPI_I2SCFGR_I2SSTD_SHIFT) /* 01: MSB justified standard (left justified) */ +# define SPI_I2SCFGR_I2SSTD_LSB (2 << SPI_I2SCFGR_I2SSTD_SHIFT) /* 10: LSB justified standard (right justified) */ +# define SPI_I2SCFGR_I2SSTD_PCM (3 << SPI_I2SCFGR_I2SSTD_SHIFT) /* 11: PCM standard */ +#define SPI_I2SCFGR_PCMSYNC (1 << 7) /* Bit 7: PCM frame synchronization */ +#define SPI_I2SCFGR_I2SCFG_SHIFT (8) /* Bit 8-9: I2S configuration mode */ +#define SPI_I2SCFGR_I2SCFG_MASK (3 << SPI_I2SCFGR_I2SCFG_SHIFT) +# define SPI_I2SCFGR_I2SCFG_STX (0 << SPI_I2SCFGR_I2SCFG_SHIFT) /* 00: Slave - transmit */ +# define SPI_I2SCFGR_I2SCFG_SRX (1 << SPI_I2SCFGR_I2SCFG_SHIFT) /* 01: Slave - receive */ +# define SPI_I2SCFGR_I2SCFG_MTX (2 << SPI_I2SCFGR_I2SCFG_SHIFT) /* 10: Master - transmit */ +# define SPI_I2SCFGR_I2SCFG_MRX (3 << SPI_I2SCFGR_I2SCFG_SHIFT) /* 11: Master - receive */ +#define SPI_I2SCFGR_I2SE (1 << 10) /* Bit 10: I2S Enable */ +#define SPI_I2SCFGR_I2SMOD (1 << 11) /* Bit 11: I2S mode selection */ +#define SPI_I2SCFGR_ASTRTEN (1 << 12) /* Bit 12: Asynchronous start enable */ + +/* I2S prescaler register */ + +#define SPI_I2SPR_I2SDIV_SHIFT (0) /* Bit 0-7: I2S Linear prescaler */ +#define SPI_I2SPR_I2SDIV_MASK (0xff << SPI_I2SPR_I2SDIV_SHIFT) +#define SPI_I2SPR_ODD (1 << 8) /* Bit 8: Odd factor for the prescaler */ +#define SPI_I2SPR_MCKOE (1 << 9) /* Bit 9: Master clock output enable */ + +#endif /* __ARCH_ARM_STC_STM32F7_CHIP_STM32_SPI_H */ diff --git a/arch/arm/src/stm32f7/stm32_spi.c b/arch/arm/src/stm32f7/stm32_spi.c new file mode 100644 index 0000000000..2a442daa94 --- /dev/null +++ b/arch/arm/src/stm32f7/stm32_spi.c @@ -0,0 +1,1832 @@ +/************************************************************************************ + * arm/arm/src/stm32f7/stm32_spi.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * David Sidrane + * + * 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. + * + ************************************************************************************/ + +/************************************************************************************ + * The external functions, stm32_spi1/2/3/4/5/6select and stm32_spi1/2/3/4/5/6status + * must be * provided by board-specific logic. They are implementations of the select + * and status methods of the SPI interface defined by struct spi_ops_s (see + * include/nuttx/spi/spi.h). All other methods (including stm32_spibus_initialize()) + * are provided by common STM32 logic. To use this common SPI logic on your + * board: + * + * 1. Provide logic in stm32_boardinitialize() to configure SPI chip select + * pins. + * 2. Provide stm32_spi1/2/3/4/5/6select() and stm32_spi1/2/3/4/5/6status() + * functions in your board-specific logic. These functions will perform chip + * selection and status operations using GPIOs in the way your board is + * configured. + * 3. Add a calls to stm32_spibus_initialize() in your low level application + * initialization logic + * 4. The handle returned by stm32_spibus_initialize() 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). + * + ****************************************************c*******************************/ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "up_internal.h" +#include "up_arch.h" + +#include "chip.h" +#include "stm32_gpio.h" +#include "stm32_dma.h" +#include "stm32_spi.h" + +#if defined(CONFIG_STM32F7_SPI1) || defined(CONFIG_STM32F7_SPI2) || \ + defined(CONFIG_STM32F7_SPI3) || defined(CONFIG_STM32F7_SPI4) || \ + defined(CONFIG_STM32F7_SPI5) || defined(CONFIG_STM32F7_SPI6) + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Configuration ********************************************************************/ +/* SPI interrupts */ + +#ifdef CONFIG_STM32F7_SPI_INTERRUPTS +# error "Interrupt driven SPI not yet supported" +#endif + +/* Can't have both interrupt driven SPI and SPI DMA */ + +#if defined(CONFIG_STM32F7_SPI_INTERRUPTS) && defined(CONFIG_STM32F7_SPI_DMA) +# error "Cannot enable both interrupt mode and DMA mode for SPI" +#endif + +/* SPI DMA priority */ + +#ifdef CONFIG_STM32F7_SPI_DMA + +# error "SPI DMA not yet supported" + +# if defined(CONFIG_SPI_DMAPRIO) +# define SPI_DMA_PRIO CONFIG_SPI_DMAPRIO +# elif defined(DMA_SCR_PRIMED) +# define SPI_DMA_PRIO DMA_SCR_PRIMED +# else +# error "Unknown STM32 DMA" +# endif + +#if (SPI_DMA_PRIO & ~DMA_SCR_PL_MASK) != 0 +# error "Illegal value for CONFIG_SPI_DMAPRIO" +#endif + + +/* DMA channel configuration */ + +# define SPI_RXDMA16_CONFIG (SPI_DMA_PRIO|DMA_SCR_MSIZE_16BITS|DMA_SCR_PSIZE_16BITS|DMA_SCR_MINC|DMA_SCR_DIR_P2M) +# define SPI_RXDMA8_CONFIG (SPI_DMA_PRIO|DMA_SCR_MSIZE_8BITS |DMA_SCR_PSIZE_8BITS |DMA_SCR_MINC|DMA_SCR_DIR_P2M) +# define SPI_RXDMA16NULL_CONFIG (SPI_DMA_PRIO|DMA_SCR_MSIZE_8BITS |DMA_SCR_PSIZE_16BITS |DMA_SCR_DIR_P2M) +# define SPI_RXDMA8NULL_CONFIG (SPI_DMA_PRIO|DMA_SCR_MSIZE_8BITS |DMA_SCR_PSIZE_8BITS |DMA_SCR_DIR_P2M) +# define SPI_TXDMA16_CONFIG (SPI_DMA_PRIO|DMA_SCR_MSIZE_16BITS|DMA_SCR_PSIZE_16BITS|DMA_SCR_MINC|DMA_SCR_DIR_M2P) +# define SPI_TXDMA8_CONFIG (SPI_DMA_PRIO|DMA_SCR_MSIZE_8BITS |DMA_SCR_PSIZE_8BITS |DMA_SCR_MINC|DMA_SCR_DIR_M2P) +# define SPI_TXDMA16NULL_CONFIG (SPI_DMA_PRIO|DMA_SCR_MSIZE_8BITS |DMA_SCR_PSIZE_16BITS |DMA_SCR_DIR_M2P) +# define SPI_TXDMA8NULL_CONFIG (SPI_DMA_PRIO|DMA_SCR_MSIZE_8BITS |DMA_SCR_PSIZE_8BITS |DMA_SCR_DIR_M2P) +#endif + +/* Debug ****************************************************************************/ +/* Check if SPI debug is enabled */ + +#ifdef CONFIG_DEBUG_SPI +# define spierr llerr +# define spiwarn llwarn +# define spiinfo llinfo +#else +# define spierr(x...) +# define spiwarn(x...) +# define spiinfo(x...) +#endif + +/************************************************************************************ + * Private Types + ************************************************************************************/ + +struct stm32_spidev_s +{ + struct spi_dev_s spidev; /* Externally visible part of the SPI interface */ + uint32_t spibase; /* SPIn base address */ + uint32_t spiclock; /* Clocking for the SPI module */ +#ifdef CONFIG_STM32F7_SPI_INTERRUPTS + uint8_t spiirq; /* SPI IRQ number */ +#endif +#ifdef CONFIG_STM32F7_SPI_DMA + volatile uint8_t rxresult; /* Result of the RX DMA */ + volatile uint8_t txresult; /* Result of the RX DMA */ + uint8_t rxch; /* The RX DMA channel number */ + uint8_t txch; /* The TX DMA channel number */ + DMA_HANDLE rxdma; /* DMA channel handle for RX transfers */ + DMA_HANDLE txdma; /* DMA channel handle for TX transfers */ + sem_t rxsem; /* Wait for RX DMA to complete */ + sem_t txsem; /* Wait for TX DMA to complete */ + uint32_t txccr; /* DMA control register for TX transfers */ + uint32_t rxccr; /* DMA control register for RX transfers */ +#endif + sem_t exclsem; /* Held while chip is selected for mutual exclusion */ + uint32_t frequency; /* Requested clock frequency */ + uint32_t actual; /* Actual clock frequency */ + int8_t nbits; /* Width of word in bits */ + uint8_t mode; /* Mode 0,1,2,3 */ +}; + +/************************************************************************************ + * Private Function Prototypes + ************************************************************************************/ + +/* Helpers */ + +static inline uint16_t spi_getreg(FAR struct stm32_spidev_s *priv, uint8_t offset); +static inline void spi_putreg(FAR struct stm32_spidev_s *priv, uint8_t offset, + uint16_t value); +static inline uint16_t spi_readword(FAR struct stm32_spidev_s *priv); +static inline void spi_writeword(FAR struct stm32_spidev_s *priv, uint16_t byte); +static inline bool spi_9to16bitmode(FAR struct stm32_spidev_s *priv); + +/* DMA support */ + +#ifdef CONFIG_STM32F7_SPI_DMA +static void spi_dmarxwait(FAR struct stm32_spidev_s *priv); +static void spi_dmatxwait(FAR struct stm32_spidev_s *priv); +static inline void spi_dmarxwakeup(FAR struct stm32_spidev_s *priv); +static inline void spi_dmatxwakeup(FAR struct stm32_spidev_s *priv); +static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg); +static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg); +static void spi_dmarxsetup(FAR struct stm32_spidev_s *priv, + FAR void *rxbuffer, FAR void *rxdummy, size_t nwords); +static void spi_dmatxsetup(FAR struct stm32_spidev_s *priv, + FAR const void *txbuffer, FAR const void *txdummy, size_t nwords); +static inline void spi_dmarxstart(FAR struct stm32_spidev_s *priv); +static inline void spi_dmatxstart(FAR struct stm32_spidev_s *priv); +#endif + +/* SPI methods */ + +static int spi_lock(FAR struct spi_dev_s *dev, bool lock); +static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency); +static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode); +static void spi_setbits(FAR struct spi_dev_s *dev, int nbits); +static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd); +static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords); +#ifndef CONFIG_SPI_EXCHANGE +static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *txbuffer, + size_t nwords); +static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *rxbuffer, + size_t nwords); +#endif + +/* Initialization */ + +static void spi_bus_initialize(FAR struct stm32_spidev_s *priv); + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +#ifdef CONFIG_STM32F7_SPI1 +static const struct spi_ops_s g_sp1iops = +{ + .lock = spi_lock, + .select = stm32_spi1select, + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = 0, /* Not supported */ +#endif + .status = stm32_spi1status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = stm32_spi1cmddata, +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = spi_exchange, +#else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = stm32_spi1register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct stm32_spidev_s g_spi1dev = +{ + .spidev = { &g_sp1iops }, + .spibase = STM32_SPI1_BASE, + .spiclock = STM32_PCLK2_FREQUENCY, +#ifdef CONFIG_STM32F7_SPI_INTERRUPTS + .spiirq = STM32_IRQ_SPI1, +#endif +#ifdef CONFIG_STM32F7_SPI_DMA + .rxch = DMACHAN_SPI1_RX, + .txch = DMACHAN_SPI1_TX, +#endif +}; +#endif + +#ifdef CONFIG_STM32F7_SPI2 +static const struct spi_ops_s g_sp2iops = +{ + .lock = spi_lock, + .select = stm32_spi2select, + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, + .status = stm32_spi2status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = stm32_spi2cmddata, +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = spi_exchange, +#else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = stm32_spi2register, /* provided externally */ +#else + .registercallback = 0, /* not implemented */ +#endif +}; + +static struct stm32_spidev_s g_spi2dev = +{ + .spidev = { &g_sp2iops }, + .spibase = STM32_SPI2_BASE, + .spiclock = STM32_PCLK1_FREQUENCY, +#ifdef CONFIG_STM32F7_SPI_INTERRUPTS + .spiirq = STM32_IRQ_SPI2, +#endif +#ifdef CONFIG_STM32F7_SPI_DMA + .rxch = DMACHAN_SPI2_RX, + .txch = DMACHAN_SPI2_TX, +#endif +}; +#endif + +#ifdef CONFIG_STM32F7_SPI3 +static const struct spi_ops_s g_sp3iops = +{ + .lock = spi_lock, + .select = stm32_spi3select, + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, + .status = stm32_spi3status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = stm32_spi3cmddata, +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = spi_exchange, +#else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = stm32_spi3register, /* provided externally */ +#else + .registercallback = 0, /* not implemented */ +#endif +}; + +static struct stm32_spidev_s g_spi3dev = +{ + .spidev = { &g_sp3iops }, + .spibase = STM32_SPI3_BASE, + .spiclock = STM32_PCLK1_FREQUENCY, +#ifdef CONFIG_STM32F7_SPI_INTERRUPTS + .spiirq = STM32_IRQ_SPI3, +#endif +#ifdef CONFIG_STM32F7_SPI_DMA + .rxch = DMACHAN_SPI3_RX, + .txch = DMACHAN_SPI3_TX, +#endif +}; +#endif + +#ifdef CONFIG_STM32F7_SPI4 +static const struct spi_ops_s g_sp4iops = +{ + .lock = spi_lock, + .select = stm32_spi4select, + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, + .status = stm32_spi4status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = stm32_spi4cmddata, +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = spi_exchange, +#else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = stm32_spi4register, /* provided externally */ +#else + .registercallback = 0, /* not implemented */ +#endif +}; + +static struct stm32_spidev_s g_spi4dev = +{ + .spidev = { &g_sp4iops }, + .spibase = STM32_SPI4_BASE, + .spiclock = STM32_PCLK2_FREQUENCY, +#ifdef CONFIG_STM32F7_SPI_INTERRUPTS + .spiirq = STM32_IRQ_SPI4, +#endif +#ifdef CONFIG_STM32F7_SPI_DMA + .rxch = DMACHAN_SPI4_RX, + .txch = DMACHAN_SPI4_TX, +#endif +}; +#endif + +#ifdef CONFIG_STM32F7_SPI5 +static const struct spi_ops_s g_sp5iops = +{ + .lock = spi_lock, + .select = stm32_spi5select, + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, + .status = stm32_spi5status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = stm32_spi5cmddata, +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = spi_exchange, +#else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = stm32_spi5register, /* provided externally */ +#else + .registercallback = 0, /* not implemented */ +#endif +}; + +static struct stm32_spidev_s g_spi5dev = +{ + .spidev = { &g_sp5iops }, + .spibase = STM32_SPI5_BASE, + .spiclock = STM32_PCLK2_FREQUENCY, +#ifdef CONFIG_STM32F7_SPI_INTERRUPTS + .spiirq = STM32_IRQ_SPI5, +#endif +#ifdef CONFIG_STM32F7_SPI_DMA + .rxch = DMACHAN_SPI5_RX, + .txch = DMACHAN_SPI5_TX, +#endif +}; +#endif + +#ifdef CONFIG_STM32F7_SPI6 +static const struct spi_ops_s g_sp6iops = +{ + .lock = spi_lock, + .select = stm32_spi6select, + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, + .status = stm32_spi6status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = stm32_spi3cmddata, +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = spi_exchange, +#else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = stm32_spi6register, /* provided externally */ +#else + .registercallback = 0, /* not implemented */ +#endif +}; + +static struct stm32_spidev_s g_spi6dev = +{ + .spidev = { &g_sp6iops }, + .spibase = STM32_SPI6_BASE, + .spiclock = STM32_PCLK2_FREQUENCY, +#ifdef CONFIG_STM32F7_SPI_INTERRUPTS + .spiirq = STM32_IRQ_SPI6, +#endif +#ifdef CONFIG_STM32F7_SPI_DMA + .rxch = DMACHAN_SPI6_RX, + .txch = DMACHAN_SPI6_TX, +#endif +}; +#endif + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: spi_getreg8 + * + * Description: + * Get the contents of the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * + * Returned Value: + * The contents of the 8-bit register + * + ************************************************************************************/ + +static inline uint8_t spi_getreg8(FAR struct stm32_spidev_s *priv, uint8_t offset) +{ + return getreg8(priv->spibase + offset); +} + +/************************************************************************************ + * Name: spi_putreg8 + * + * Description: + * Write a 8-bit value to the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * value - the 8-bit value to be written + * + ************************************************************************************/ + +static inline void spi_putreg8(FAR struct stm32_spidev_s *priv, uint8_t offset, + uint8_t value) +{ + putreg8(value, priv->spibase + offset); +} + +/************************************************************************************ + * Name: spi_getreg + * + * Description: + * Get the contents of the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * + * Returned Value: + * The contents of the 16-bit register + * + ************************************************************************************/ + +static inline uint16_t spi_getreg(FAR struct stm32_spidev_s *priv, uint8_t offset) +{ + return getreg16(priv->spibase + offset); +} + +/************************************************************************************ + * Name: spi_putreg + * + * Description: + * Write a 16-bit value to the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * value - the 16-bit value to be written + * + * Returned Value: + * The contents of the 16-bit register + * + ************************************************************************************/ + +static inline void spi_putreg(FAR struct stm32_spidev_s *priv, uint8_t offset, + uint16_t value) +{ + putreg16(value, priv->spibase + offset); +} + +/************************************************************************************ + * Name: spi_readword + * + * Description: + * Read one byte from SPI + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * Byte as read + * + ************************************************************************************/ + +static inline uint16_t spi_readword(FAR struct stm32_spidev_s *priv) +{ + /* Wait until the receive buffer is not empty */ + + while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0); + + /* Then return the received byte */ + + return spi_getreg(priv, STM32_SPI_DR_OFFSET); +} + +/************************************************************************************ + * Name: spi_writeword + * + * Description: + * Write one byte to SPI + * + * Input Parameters: + * priv - Device-specific state data + * byte - Byte to send + * + * Returned Value: + * None + * + ************************************************************************************/ + +static inline void spi_writeword(FAR struct stm32_spidev_s *priv, uint16_t word) +{ + /* Wait until the transmit buffer is empty */ + + while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_TXE) == 0); + + /* Then send the byte */ + + spi_putreg(priv, STM32_SPI_DR_OFFSET, word); +} + +/************************************************************************************ + * Name: spi_readbyte + * + * Description: + * Read one byte from SPI + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * Byte as read + * + ************************************************************************************/ + +static inline uint8_t spi_readbyte(FAR struct stm32_spidev_s *priv) +{ + /* Wait until the receive buffer is not empty */ + + while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_RXNE) == 0); + + /* Then return the received byte */ + + return spi_getreg8(priv, STM32_SPI_DR_OFFSET); +} + +/************************************************************************************ + * Name: spi_writebyte + * + * Description: + * Write one 8-bit frame to the SPI FIFO + * + * Input Parameters: + * priv - Device-specific state data + * byte - Byte to send + * + * Returned Value: + * None + * + ************************************************************************************/ + +static inline void spi_writebyte(FAR struct stm32_spidev_s *priv, uint8_t byte) +{ + /* Wait until the transmit buffer is empty */ + + while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_TXE) == 0); + + /* Then send the byte */ + + spi_putreg8(priv, STM32_SPI_DR_OFFSET, byte); +} + +/************************************************************************************ + * Name: spi_9to16bitmode + * + * Description: + * Check if the SPI is operating in more then 8 bit mode + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * true: >8 bit mode-bit mode, false: <= 8-bit mode + * + ************************************************************************************/ + +static inline bool spi_9to16bitmode(FAR struct stm32_spidev_s *priv) +{ + return ((spi_getreg(priv, STM32_SPI_CR2_OFFSET) & SPI_CR2_DS_9BIT) == SPI_CR2_DS_9BIT); +} + +/************************************************************************************ + * Name: spi_dmarxwait + * + * Description: + * Wait for DMA to complete. + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F7_SPI_DMA +static void spi_dmarxwait(FAR struct stm32_spidev_s *priv) +{ + /* Take the semaphore (perhaps waiting). If the result is zero, then the DMA + * must not really have completed??? + */ + + while (sem_wait(&priv->rxsem) != 0 || priv->rxresult == 0) + { + /* The only case that an error should occur here is if the wait was awakened + * by a signal. + */ + + ASSERT(errno == EINTR); + } +} +#endif + +/************************************************************************************ + * Name: spi_dmatxwait + * + * Description: + * Wait for DMA to complete. + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F7_SPI_DMA +static void spi_dmatxwait(FAR struct stm32_spidev_s *priv) +{ + /* Take the semaphore (perhaps waiting). If the result is zero, then the DMA + * must not really have completed??? + */ + + while (sem_wait(&priv->txsem) != 0 || priv->txresult == 0) + { + /* The only case that an error should occur here is if the wait was awakened + * by a signal. + */ + + ASSERT(errno == EINTR); + } +} +#endif + +/************************************************************************************ + * Name: spi_dmarxwakeup + * + * Description: + * Signal that DMA is complete + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F7_SPI_DMA +static inline void spi_dmarxwakeup(FAR struct stm32_spidev_s *priv) +{ + (void)sem_post(&priv->rxsem); +} +#endif + +/************************************************************************************ + * Name: spi_dmatxwakeup + * + * Description: + * Signal that DMA is complete + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F7_SPI_DMA +static inline void spi_dmatxwakeup(FAR struct stm32_spidev_s *priv) +{ + (void)sem_post(&priv->txsem); +} +#endif + +/************************************************************************************ + * Name: spi_dmarxcallback + * + * Description: + * Called when the RX DMA completes + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F7_SPI_DMA +static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)arg; + + /* Wake-up the SPI driver */ + + priv->rxresult = isr | 0x080; /* OR'ed with 0x80 to assure non-zero */ + spi_dmarxwakeup(priv); +} +#endif + +/************************************************************************************ + * Name: spi_dmatxcallback + * + * Description: + * Called when the RX DMA completes + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F7_SPI_DMA +static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)arg; + + /* Wake-up the SPI driver */ + + priv->txresult = isr | 0x080; /* OR'ed with 0x80 to assure non-zero */ + spi_dmatxwakeup(priv); +} +#endif + +/************************************************************************************ + * Name: spi_dmarxsetup + * + * Description: + * Setup to perform RX DMA + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F7_SPI_DMA +static void spi_dmarxsetup(FAR struct stm32_spidev_s *priv, FAR void *rxbuffer, + FAR void *rxdummy, size_t nwords) +{ + /* 8- or 16-bit mode? */ + + if (spi_9to16bitmode(priv)) + { + /* 16-bit mode -- is there a buffer to receive data in? */ + + if (rxbuffer) + { + priv->rxccr = SPI_RXDMA16_CONFIG; + } + else + { + rxbuffer = rxdummy; + priv->rxccr = SPI_RXDMA16NULL_CONFIG; + } + } + else + { + /* 8-bit mode -- is there a buffer to receive data in? */ + + if (rxbuffer) + { + priv->rxccr = SPI_RXDMA8_CONFIG; + } + else + { + rxbuffer = rxdummy; + priv->rxccr = SPI_RXDMA8NULL_CONFIG; + } + } + + /* Configure the RX DMA */ + + stm32_dmasetup(priv->rxdma, priv->spibase + STM32_SPI_DR_OFFSET, + (uint32_t)rxbuffer, nwords, priv->rxccr); +} +#endif + +/************************************************************************************ + * Name: spi_dmatxsetup + * + * Description: + * Setup to perform TX DMA + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F7_SPI_DMA +static void spi_dmatxsetup(FAR struct stm32_spidev_s *priv, FAR const void *txbuffer, + FAR const void *txdummy, size_t nwords) +{ + /* 8- or 16-bit mode? */ + + if (spi_9to16bitmode(priv)) + { + /* 16-bit mode -- is there a buffer to transfer data from? */ + + if (txbuffer) + { + priv->txccr = SPI_TXDMA16_CONFIG; + } + else + { + txbuffer = txdummy; + priv->txccr = SPI_TXDMA16NULL_CONFIG; + } + } + else + { + /* 8-bit mode -- is there a buffer to transfer data from? */ + + if (txbuffer) + { + priv->txccr = SPI_TXDMA8_CONFIG; + } + else + { + txbuffer = txdummy; + priv->txccr = SPI_TXDMA8NULL_CONFIG; + } + } + + /* Setup the TX DMA */ + + stm32_dmasetup(priv->txdma, priv->spibase + STM32_SPI_DR_OFFSET, + (uint32_t)txbuffer, nwords, priv->txccr); +} +#endif + +/************************************************************************************ + * Name: spi_dmarxstart + * + * Description: + * Start RX DMA + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F7_SPI_DMA +static inline void spi_dmarxstart(FAR struct stm32_spidev_s *priv) +{ + priv->rxresult = 0; + stm32_dmastart(priv->rxdma, spi_dmarxcallback, priv, false); +} +#endif + +/************************************************************************************ + * Name: spi_dmatxstart + * + * Description: + * Start TX DMA + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F7_SPI_DMA +static inline void spi_dmatxstart(FAR struct stm32_spidev_s *priv) +{ + priv->txresult = 0; + stm32_dmastart(priv->txdma, spi_dmatxcallback, priv, false); +} +#endif + +/************************************************************************************ + * Name: spi_modifycr1 + * + * Description: + * Clear and set bits in the CR1 register + * + * Input Parameters: + * priv - Device-specific state data + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ************************************************************************************/ + +static void spi_modifycr1(FAR struct stm32_spidev_s *priv, uint16_t setbits, + uint16_t clrbits) +{ + uint16_t cr1; + cr1 = spi_getreg(priv, STM32_SPI_CR1_OFFSET); + cr1 &= ~clrbits; + cr1 |= setbits; + spi_putreg(priv, STM32_SPI_CR1_OFFSET, cr1); +} + +/************************************************************************************ + * Name: spi_modifycr2 + * + * Description: + * Clear and set bits in the CR2 register + * + * Input Parameters: + * priv - Device-specific state data + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ************************************************************************************/ + +static void spi_modifycr2(FAR struct stm32_spidev_s *priv, uint16_t setbits, + uint16_t clrbits) +{ + uint16_t cr2; + cr2 = spi_getreg(priv, STM32_SPI_CR2_OFFSET); + cr2 &= ~clrbits; + cr2 |= setbits; + spi_putreg(priv, STM32_SPI_CR2_OFFSET, cr2); +} + +/************************************************************************************ + * Name: spi_lock + * + * Description: + * On SPI busses where there are multiple devices, it will be necessary to + * lock SPI to have exclusive access to the busses 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 spi bus, false: unlock SPI bus + * + * Returned Value: + * None + * + ************************************************************************************/ + +static int spi_lock(FAR struct spi_dev_s *dev, bool lock) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + + if (lock) + { + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(&priv->exclsem) != 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->exclsem); + } + + return OK; +} + +/************************************************************************************ + * 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(FAR struct spi_dev_s *dev, uint32_t frequency) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + uint16_t setbits; + uint32_t actual; + + /* Limit to max possible (if STM32_SPI_CLK_MAX is defined in board.h) */ + + if (frequency > STM32_SPI_CLK_MAX) + { + frequency = STM32_SPI_CLK_MAX; + } + + /* Has the frequency changed? */ + + if (frequency != priv->frequency) + { + /* Choices are limited by PCLK frequency with a set of divisors */ + + if (frequency >= priv->spiclock >> 1) + { + /* More than fPCLK/2. This is as fast as we can go */ + + setbits = SPI_CR1_FPCLCKd2; /* 000: fPCLK/2 */ + actual = priv->spiclock >> 1; + } + else if (frequency >= priv->spiclock >> 2) + { + /* Between fPCLCK/2 and fPCLCK/4, pick the slower */ + + setbits = SPI_CR1_FPCLCKd4; /* 001: fPCLK/4 */ + actual = priv->spiclock >> 2; + } + else if (frequency >= priv->spiclock >> 3) + { + /* Between fPCLCK/4 and fPCLCK/8, pick the slower */ + + setbits = SPI_CR1_FPCLCKd8; /* 010: fPCLK/8 */ + actual = priv->spiclock >> 3; + } + else if (frequency >= priv->spiclock >> 4) + { + /* Between fPCLCK/8 and fPCLCK/16, pick the slower */ + + setbits = SPI_CR1_FPCLCKd16; /* 011: fPCLK/16 */ + actual = priv->spiclock >> 4; + } + else if (frequency >= priv->spiclock >> 5) + { + /* Between fPCLCK/16 and fPCLCK/32, pick the slower */ + + setbits = SPI_CR1_FPCLCKd32; /* 100: fPCLK/32 */ + actual = priv->spiclock >> 5; + } + else if (frequency >= priv->spiclock >> 6) + { + /* Between fPCLCK/32 and fPCLCK/64, pick the slower */ + + setbits = SPI_CR1_FPCLCKd64; /* 101: fPCLK/64 */ + actual = priv->spiclock >> 6; + } + else if (frequency >= priv->spiclock >> 7) + { + /* Between fPCLCK/64 and fPCLCK/128, pick the slower */ + + setbits = SPI_CR1_FPCLCKd128; /* 110: fPCLK/128 */ + actual = priv->spiclock >> 7; + } + else + { + /* Less than fPCLK/128. This is as slow as we can go */ + + setbits = SPI_CR1_FPCLCKd256; /* 111: fPCLK/256 */ + actual = priv->spiclock >> 8; + } + + spi_modifycr1(priv, 0, SPI_CR1_SPE); + spi_modifycr1(priv, setbits, SPI_CR1_BR_MASK); + spi_modifycr1(priv, SPI_CR1_SPE, 0); + + /* Save the frequency selection so that subsequent reconfigurations will be + * faster. + */ + + spiinfo("Frequency %d->%d\n", frequency, actual); + + priv->frequency = frequency; + priv->actual = actual; + } + + return priv->actual; +} + +/************************************************************************************ + * Name: spi_setmode + * + * Description: + * Set the SPI mode. see enum spi_mode_e for mode definitions + * + * Input Parameters: + * dev - Device-specific state data + * mode - The SPI mode requested + * + * Returned Value: + * Returns the actual frequency selected + * + ************************************************************************************/ + +static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + uint16_t setbits; + uint16_t clrbits; + + spiinfo("mode=%d\n", mode); + + /* Has the mode changed? */ + + if (mode != priv->mode) + { + /* Yes... Set CR1 appropriately */ + + switch (mode) + { + case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */ + setbits = 0; + clrbits = SPI_CR1_CPOL | SPI_CR1_CPHA; + break; + + case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */ + setbits = SPI_CR1_CPHA; + clrbits = SPI_CR1_CPOL; + break; + + case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */ + setbits = SPI_CR1_CPOL; + clrbits = SPI_CR1_CPHA; + break; + + case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */ + setbits = SPI_CR1_CPOL | SPI_CR1_CPHA; + clrbits = 0; + break; + + default: + return; + } + + spi_modifycr1(priv, 0, SPI_CR1_SPE); + spi_modifycr1(priv, setbits, clrbits); + spi_modifycr1(priv, SPI_CR1_SPE, 0); + + /* Save the mode so that subsequent re-configurations will be faster */ + + priv->mode = mode; + } +} + +/************************************************************************************ + * Name: spi_setbits + * + * Description: + * Set the number of bits per word. + * + * Input Parameters: + * dev - Device-specific state data + * nbits - The number of bits requested + * + * Returned Value: + * None + * + ************************************************************************************/ + +static void spi_setbits(FAR struct spi_dev_s *dev, int nbits) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + uint16_t setbitscr1; + uint16_t clrbitscr1; + uint16_t setbitscr2; + uint16_t clrbitscr2; + int savbits = nbits; + + spiinfo("nbits=%d\n", nbits); + + /* Has the number of bits changed? */ + + if (nbits != priv->nbits) + { + /* Yes... Set CR1/2 appropriately */ + /* Negative sign means LSBFIRST, set this in CR1*/ + + if (nbits < 0) + { + setbitscr1 = SPI_CR1_LSBFIRST; + clrbitscr1 = 0; + nbits = -nbits; + } + else + { + setbitscr1 = 0; + clrbitscr1 = SPI_CR1_LSBFIRST; + } + + /* Set the number of bits (valid range 4-16) */ + + if (nbits < 4 || nbits > 16) + { + return; + } + + clrbitscr2 = SPI_CR2_DS_MASK; + setbitscr2 = SPI_CR2_DS_VAL(nbits); + + /* If nbits is <=8, then we are in byte mode and FRXTH shall be set + * (else, transaction will not complete). + */ + + if (nbits < 9) + { + setbitscr2 |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 1 byte */ + } + else + { + clrbitscr2 |= SPI_CR2_FRXTH; /* RX FIFO Threshold = 2 bytes */ + } + + spi_modifycr1(priv, 0, SPI_CR1_SPE); + spi_modifycr1(priv, setbitscr1, clrbitscr1); + spi_modifycr2(priv, setbitscr2, clrbitscr2); + spi_modifycr1(priv, SPI_CR1_SPE, 0); + + /* Save the selection so the subsequence re-configurations will be faster */ + + priv->nbits = savbits; // nbits has been clobbered... save the signed value. + } +} + +/************************************************************************************ + * 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(FAR struct spi_dev_s *dev, uint16_t wd) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + uint32_t regval; + uint16_t ret; + + DEBUGASSERT(priv && priv->spibase); + + /* According to the number of bits, access data register as word or byte + * This is absolutely required because of packing. With nbits <=8 bit frames, + * two bytes are received by a 16-bit read of the data register! + */ + + if (spi_9to16bitmode(priv)) + { + spi_writeword(priv, wd); + ret = spi_readword(priv); + } + else + { + spi_writebyte(priv, (uint8_t)(wd & 0xFF)); + ret = (uint16_t)spi_readbyte(priv); + } + + /* Check and clear any error flags (Reading from the SR clears the error + * flags). + */ + + regval = spi_getreg(priv, STM32_SPI_SR_OFFSET); + + if (spi_9to16bitmode(priv)) + { + spiinfo("Sent: %04x Return: %04x Status: %02x\n", wd, ret, regval); + } + else + { + spiinfo("Sent: %02x Return: %02x Status: %02x\n", wd, ret, regval); + } + + UNUSED(regval); + return ret; +} + +/************************************************************************************ + * Name: spi_exchange (no DMA). aka spi_exchange_nodma + * + * Description: + * Exchange a block of data on SPI without using DMA + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to a buffer in which to receive data + * nwords - the length of data to be exchaned 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 + * + ************************************************************************************/ + +#if !defined(CONFIG_STM32F7_SPI_DMA) || defined(CONFIG_STM32F7_DMACAPABLE) +#if !defined(CONFIG_STM32F7_SPI_DMA) +static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords) +#else +static void spi_exchange_nodma(FAR struct spi_dev_s *dev, FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords) +#endif +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + DEBUGASSERT(priv && priv->spibase); + + spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); + + /* 8- or 16-bit mode? */ + + if (spi_9to16bitmode(priv)) + { + /* 16-bit mode */ + + const uint16_t *src = (const uint16_t *)txbuffer; + uint16_t *dest = (uint16_t *)rxbuffer; + uint16_t word; + + while (nwords-- > 0) + { + /* Get the next word to write. Is there a source buffer? */ + + if (src) + { + word = *src++; + } + else + { + word = 0xffff; + } + + /* Exchange one word */ + + word = spi_send(dev, word); + + /* Is there a buffer to receive the return value? */ + + if (dest) + { + *dest++ = word; + } + } + } + else + { + /* 8-bit mode */ + + const uint8_t *src = (const uint8_t *)txbuffer; + uint8_t *dest = (uint8_t *)rxbuffer; + uint8_t word; + + while (nwords-- > 0) + { + /* Get the next word to write. Is there a source buffer? */ + + if (src) + { + word = *src++; + } + else + { + word = 0xff; + } + + /* Exchange one word */ + + word = (uint8_t)spi_send(dev, (uint16_t)word); + + /* Is there a buffer to receive the return value? */ + + if (dest) + { + *dest++ = word; + } + } + } +} +#endif /* !CONFIG_STM32F7_SPI_DMA || CONFIG_STM32F7_DMACAPABLE */ + +/**************************************************************************** + * Name: spi_exchange (with DMA capability) + * + * Description: + * Exchange a block of data on SPI using DMA + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to a buffer in which to receive data + * nwords - the length of data 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 + * + ************************************************************************************/ + +#ifdef CONFIG_STM32F7_SPI_DMA +static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords) +{ + FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)dev; + +#ifdef CONFIG_STM32F7_DMACAPABLE + if ((txbuffer && !stm32_dmacapable((uint32_t)txbuffer, nwords, priv->txccr)) || + (rxbuffer && !stm32_dmacapable((uint32_t)rxbuffer, nwords, priv->rxccr))) + { + /* Unsupported memory region, fall back to non-DMA method. */ + + spi_exchange_nodma(dev, txbuffer, rxbuffer, nwords); + } + else +#endif + { + static uint16_t rxdummy = 0xffff; + static const uint16_t txdummy = 0xffff; + + spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); + DEBUGASSERT(priv && priv->spibase); + + /* Setup DMAs */ + + spi_dmarxsetup(priv, rxbuffer, &rxdummy, nwords); + spi_dmatxsetup(priv, txbuffer, &txdummy, nwords); + + /* Start the DMAs */ + + spi_dmarxstart(priv); + spi_dmatxstart(priv); + + /* Then wait for each to complete */ + + spi_dmarxwait(priv); + spi_dmatxwait(priv); + } +} +#endif /* CONFIG_STM32F7_SPI_DMA */ + +/**************************************************************************** + * Name: spi_sndblock + * + * Description: + * Send a block of data on SPI + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - 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(FAR struct spi_dev_s *dev, FAR const void *txbuffer, size_t nwords) +{ + spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords); + return spi_exchange(dev, txbuffer, NULL, nwords); +} +#endif + +/************************************************************************************ + * Name: spi_recvblock + * + * Description: + * Receive a block of data from SPI + * + * Input Parameters: + * dev - Device-specific state data + * rxbuffer - 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(FAR struct spi_dev_s *dev, FAR void *rxbuffer, size_t nwords) +{ + spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords); + return spi_exchange(dev, NULL, rxbuffer, nwords); +} +#endif + +/************************************************************************************ + * Name: spi_bus_initialize + * + * Description: + * Initialize the selected SPI bus in its default state (Master, 8-bit, mode 0, etc.) + * + * Input Parameter: + * priv - private SPI device structure + * + * Returned Value: + * None + * + ************************************************************************************/ + +static void spi_bus_initialize(FAR struct stm32_spidev_s *priv) +{ + uint16_t setbits; + uint16_t clrbits; + + /* Configure CR1 and CR2. Default configuration: + * Mode 0: CR1.CPHA=0 and CR1.CPOL=0 + * Master: CR1.MSTR=1 + * 8-bit: CR2.DS=7 + * MSB tranmitted first: CR1.LSBFIRST=0 + * Replace NSS with SSI & SSI=1: CR1.SSI=1 CR1.SSM=1 (prevents MODF error) + * Two lines full duplex: CR1.BIDIMODE=0 CR1.BIDIOIE=(Don't care) and CR1.RXONLY=0 + */ + + clrbits = SPI_CR1_CPHA | SPI_CR1_CPOL | SPI_CR1_BR_MASK | SPI_CR1_LSBFIRST | + SPI_CR1_RXONLY | SPI_CR1_BIDIOE | SPI_CR1_BIDIMODE; + setbits = SPI_CR1_MSTR | SPI_CR1_SSI | SPI_CR1_SSM; + spi_modifycr1(priv, setbits, clrbits); + + clrbits = SPI_CR2_DS_MASK; + setbits = SPI_CR2_DS_8BIT | SPI_CR2_FRXTH; /* FRXTH must be high in 8-bit mode */ + spi_modifycr2(priv, setbits, clrbits); + + priv->frequency = 0; + priv->nbits = 8; + priv->mode = SPIDEV_MODE0; + + /* Select a default frequency of approx. 400KHz */ + + spi_setfrequency((FAR struct spi_dev_s *)priv, 400000); + + /* CRCPOLY configuration */ + + spi_putreg(priv, STM32_SPI_CRCPR_OFFSET, 7); + + /* Initialize the SPI semaphore that enforces mutually exclusive access */ + + sem_init(&priv->exclsem, 0, 1); + + /* Initialize the SPI semaphores that is used to wait for DMA completion */ + +#ifdef CONFIG_STM32F7_SPI_DMA + sem_init(&priv->rxsem, 0, 0); + sem_init(&priv->txsem, 0, 0); + + /* Get DMA channels. NOTE: stm32_dmachannel() will always assign the DMA channel. + * if the channel is not available, then stm32_dmachannel() will block and wait + * until the channel becomes available. WARNING: If you have another device sharing + * a DMA channel with SPI and the code never releases that channel, then the call + * to stm32_dmachannel() will hang forever in this function! Don't let your + * design do that! + */ + + priv->rxdma = stm32_dmachannel(priv->rxch); + priv->txdma = stm32_dmachannel(priv->txch); + DEBUGASSERT(priv->rxdma && priv->txdma); + + spi_putreg(priv, STM32_SPI_CR2_OFFSET, SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN); +#endif + + /* Enable spi */ + + spi_modifycr1(priv, SPI_CR1_SPE, 0); +} + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: stm32_spibus_initialize + * + * Description: + * Initialize the selected SPI bus + * + * Input Parameter: + * Port number (for hardware that has mutiple SPI interfaces) + * + * Returned Value: + * Valid SPI device structure reference on succcess; a NULL on failure + * + ************************************************************************************/ + +FAR struct spi_dev_s *stm32_spibus_initialize(int bus) +{ + FAR struct stm32_spidev_s *priv = NULL; + + irqstate_t flags = enter_critical_section(); + +#ifdef CONFIG_STM32F7_SPI1 + if (bus == 1) + { + /* Select SPI1 */ + + priv = &g_spi1dev; + + /* Only configure if the bus is not already configured */ + + if ((spi_getreg(priv, STM32_SPI_CR1_OFFSET) & SPI_CR1_SPE) == 0) + { + /* Configure SPI1 pins: SCK, MISO, and MOSI */ + + stm32_configgpio(GPIO_SPI1_SCK); + stm32_configgpio(GPIO_SPI1_MISO); + stm32_configgpio(GPIO_SPI1_MOSI); + + /* Set up default configuration: Master, 8-bit, etc. */ + + spi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_STM32F7_SPI2 + if (bus == 2) + { + /* Select SPI2 */ + + priv = &g_spi2dev; + + /* Only configure if the bus is not already configured */ + + if ((spi_getreg(priv, STM32_SPI_CR1_OFFSET) & SPI_CR1_SPE) == 0) + { + /* Configure SPI2 pins: SCK, MISO, and MOSI */ + + stm32_configgpio(GPIO_SPI2_SCK); + stm32_configgpio(GPIO_SPI2_MISO); + stm32_configgpio(GPIO_SPI2_MOSI); + + /* Set up default configuration: Master, 8-bit, etc. */ + + spi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_STM32F7_SPI3 + if (bus == 3) + { + /* Select SPI3 */ + + priv = &g_spi3dev; + + /* Only configure if the bus is not already configured */ + + if ((spi_getreg(priv, STM32_SPI_CR1_OFFSET) & SPI_CR1_SPE) == 0) + { + /* Configure SPI3 pins: SCK, MISO, and MOSI */ + + stm32_configgpio(GPIO_SPI3_SCK); + stm32_configgpio(GPIO_SPI3_MISO); + stm32_configgpio(GPIO_SPI3_MOSI); + + /* Set up default configuration: Master, 8-bit, etc. */ + + spi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_STM32F7_SPI4 + if (bus == 4) + { + /* Select SPI4 */ + + priv = &g_spi4dev; + + /* Only configure if the bus is not already configured */ + + if ((spi_getreg(priv, STM32_SPI_CR1_OFFSET) & SPI_CR1_SPE) == 0) + { + /* Configure SPI4 pins: SCK, MISO, and MOSI */ + + stm32_configgpio(GPIO_SPI4_SCK); + stm32_configgpio(GPIO_SPI4_MISO); + stm32_configgpio(GPIO_SPI4_MOSI); + + /* Set up default configuration: Master, 8-bit, etc. */ + + spi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_STM32F7_SPI5 + if (bus == 5) + { + /* Select SPI5 */ + + priv = &g_spi5dev; + + /* Only configure if the bus is not already configured */ + + if ((spi_getreg(priv, STM32_SPI_CR1_OFFSET) & SPI_CR1_SPE) == 0) + { + /* Configure SPI5 pins: SCK, MISO, and MOSI */ + + stm32_configgpio(GPIO_SPI5_SCK); + stm32_configgpio(GPIO_SPI5_MISO); + stm32_configgpio(GPIO_SPI5_MOSI); + + /* Set up default configuration: Master, 8-bit, etc. */ + + spi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_STM32F7_SPI6 + if (bus == 6) + { + /* Select SPI6 */ + + priv = &g_spi6dev; + + /* Only configure if the bus is not already configured */ + + if ((spi_getreg(priv, STM32_SPI_CR1_OFFSET) & SPI_CR1_SPE) == 0) + { + /* Configure SPI6 pins: SCK, MISO, and MOSI */ + + stm32_configgpio(GPIO_SPI6_SCK); + stm32_configgpio(GPIO_SPI6_MISO); + stm32_configgpio(GPIO_SPI6_MOSI); + + /* Set up default configuration: Master, 8-bit, etc. */ + + spi_bus_initialize(priv); + } + } + else +#endif + { + spierr("ERROR: Unsupported SPI bus: %d\n", bus); + return NULL; + } + + leave_critical_section(flags); + return (FAR struct spi_dev_s *)priv; +} + +#endif /* CONFIG_STM32F7_SPI1 || CONFIG_STM32F7_SPI2 || CONFIG_STM32F7_SPI3 || \ + * CONFIG_STM32F7_SPI4 || CONFIG_STM32F7_SPI5 || CONFIG_STM32F7_SPI6 */ diff --git a/arch/arm/src/stm32f7/stm32_spi.h b/arch/arm/src/stm32f7/stm32_spi.h index 2d606d2f45..2a2aa91881 100644 --- a/arch/arm/src/stm32f7/stm32_spi.h +++ b/arch/arm/src/stm32f7/stm32_spi.h @@ -2,7 +2,8 @@ * arch/arm/src/stm32f7/stm32_spi.h * * Copyright (C) 2016 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Authors: Gregory Nutt + * David Sidrane * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,8 +34,8 @@ * ************************************************************************************/ -#ifndef __ARCH_ARM_STC_STM32F7_STM32_SPI_H -#define __ARCH_ARM_STC_STM32F7_STM32_SPI_H +#ifndef __ARCH_ARM_SRC_STM32F7_STM32_SPI_H +#define __ARCH_ARM_SRC_STM32F7_STM32_SPI_H /************************************************************************************ * Included Files @@ -45,7 +46,7 @@ #include #include "chip.h" -//#include "chip/stm32_spi.h" +#include "chip/stm32_spi.h" /************************************************************************************ * Public Functions @@ -110,37 +111,37 @@ FAR struct spi_dev_s *stm32_spibus_initialize(int bus); * ************************************************************************************/ -#ifdef CONFIG_STM32_SPI1 +#ifdef CONFIG_STM32F7_SPI1 void stm32_spi1select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool selected); uint8_t stm32_spi1status(FAR struct spi_dev_s *dev, enum spi_dev_e devid); int stm32_spi1cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd); #endif -#ifdef CONFIG_STM32_SPI2 +#ifdef CONFIG_STM32F7_SPI2 void stm32_spi2select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool selected); uint8_t stm32_spi2status(FAR struct spi_dev_s *dev, enum spi_dev_e devid); int stm32_spi2cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd); #endif -#ifdef CONFIG_STM32_SPI3 +#ifdef CONFIG_STM32F7_SPI3 void stm32_spi3select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool selected); uint8_t stm32_spi3status(FAR struct spi_dev_s *dev, enum spi_dev_e devid); int stm32_spi3cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd); #endif -#ifdef CONFIG_STM32_SPI4 +#ifdef CONFIG_STM32F7_SPI4 void stm32_spi4select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool selected); uint8_t stm32_spi4status(FAR struct spi_dev_s *dev, enum spi_dev_e devid); int stm32_spi4cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd); #endif -#ifdef CONFIG_STM32_SPI5 +#ifdef CONFIG_STM32F7_SPI5 void stm32_spi5select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool selected); uint8_t stm32_spi5status(FAR struct spi_dev_s *dev, enum spi_dev_e devid); int stm32_spi5cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd); #endif -#ifdef CONFIG_STM32_SPI6 +#ifdef CONFIG_STM32F7_SPI6 void stm32_spi6select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool selected); uint8_t stm32_spi6status(FAR struct spi_dev_s *dev, enum spi_dev_e devid); int stm32_spi6cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd); @@ -167,32 +168,32 @@ int stm32_spi6cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd) ****************************************************************************/ #ifdef CONFIG_SPI_CALLBACK -#ifdef CONFIG_STM32_SPI1 +#ifdef CONFIG_STM32F7_SPI1 int stm32_spi1register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, FAR void *arg); #endif -#ifdef CONFIG_STM32_SPI2 +#ifdef CONFIG_STM32F7_SPI2 int stm32_spi2register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, FAR void *arg); #endif -#ifdef CONFIG_STM32_SPI3 +#ifdef CONFIG_STM32F7_SPI3 int stm32_spi3register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, FAR void *arg); #endif -#ifdef CONFIG_STM32_SPI4 +#ifdef CONFIG_STM32F7_SPI4 int stm32_spi4register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, FAR void *arg); #endif -#ifdef CONFIG_STM32_SPI5 +#ifdef CONFIG_STM32F7_SPI5 int stm32_spi5register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, FAR void *arg); #endif -#ifdef CONFIG_STM32_SPI6 +#ifdef CONFIG_STM32F7_SPI6 int stm32_spi6register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, FAR void *arg); #endif @@ -204,5 +205,5 @@ int stm32_spi6register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, #endif #endif /* __ASSEMBLY__ */ -#endif /* __ARCH_ARM_STC_STM32F7_STM32_SPI_H */ +#endif /* __ARCH_ARM_SRC_STM32F7_STM32_SPI_H */