| /**************************************************************************** |
| * arch/arm/src/stm32h7/stm32_spi_slave.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <sys/types.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <semaphore.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| #include <string.h> |
| |
| #include <nuttx/irq.h> |
| #include <nuttx/arch.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/spi/spi.h> |
| #include <nuttx/spi/slave.h> |
| #include <nuttx/power/pm.h> |
| |
| #include <arch/board/board.h> |
| |
| #include "arm_internal.h" |
| #include "chip.h" |
| #include "stm32_rcc.h" |
| #include "stm32_gpio.h" |
| #include "stm32_spi.h" |
| #include "stm32_dma.h" |
| |
| #if defined(CONFIG_STM32H7_SPI1_SLAVE) || \ |
| defined(CONFIG_STM32H7_SPI2_SLAVE) || \ |
| defined(CONFIG_STM32H7_SPI3_SLAVE) || \ |
| defined(CONFIG_STM32H7_SPI4_SLAVE) || \ |
| defined(CONFIG_STM32H7_SPI5_SLAVE) || \ |
| defined(CONFIG_STM32H7_SPI6_SLAVE) |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Configuration ************************************************************/ |
| |
| /* SPI interrupts */ |
| |
| #ifdef CONFIG_STM32H7_SPI_INTERRUPTS |
| # error "Interrupt driven SPI not yet supported" |
| #endif |
| |
| /* Can't have both interrupt driven SPI and SPI DMA */ |
| |
| #if defined(CONFIG_STM32H7_SPI_INTERRUPTS) && defined(CONFIG_STM32H7_SPI_DMA) |
| # error "Cannot enable both interrupt mode and DMA mode for SPI" |
| #endif |
| |
| /* SPI DMA priority */ |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| |
| # if defined(CONFIG_SPI_DMAPRIO) |
| # define SPI_DMA_PRIO CONFIG_SPI_DMAPRIO |
| # elif defined(DMA_SCR_PRIMED) |
| # define SPI_DMA_PRIO DMA_SCR_PRILO |
| # 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 | DMA_SCR_CIRC) |
| # define SPI_RXDMA8_CONFIG (SPI_DMA_PRIO | DMA_SCR_MSIZE_8BITS | \ |
| DMA_SCR_PSIZE_8BITS | DMA_SCR_MINC| \ |
| DMA_SCR_DIR_P2M | DMA_SCR_CIRC) |
| # 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) |
| #endif |
| |
| /* Kernel clock configuration |
| * TODO: |
| * - support for all kernel clock configuration |
| */ |
| |
| #if defined(CONFIG_STM32H7_SPI1_SLAVE) || defined(CONFIG_STM32H7_SPI2_SLAVE) || \ |
| defined(CONFIG_STM32H7_SPI3_SLAVE) |
| # if STM32_RCC_D2CCIP1R_SPI123SRC == RCC_D2CCIP1R_SPI123SEL_PLL1 |
| # define SPI123_KERNEL_CLOCK_FREQ STM32_PLL1Q_FREQUENCY |
| # else |
| # error Not supported yet |
| # endif |
| # if SPI123_KERNEL_CLOCK_FREQ > 200000000 |
| # error Not supported SPI123 frequency |
| # endif |
| #endif /* SPI123 */ |
| |
| #if defined(CONFIG_STM32H7_SPI4_SLAVE) || defined(CONFIG_STM32H7_SPI5_SLAVE) |
| # if STM32_RCC_D2CCIP1R_SPI45SRC == RCC_D2CCIP1R_SPI45SEL_APB |
| # define SPI45_KERNEL_CLOCK_FREQ STM32_PCLK2_FREQUENCY |
| # else |
| # error Not supported yet |
| # endif |
| # if SPI45_KERNEL_CLOCK_FREQ > 100000000 |
| # error Not supported SPI45 frequency |
| # endif |
| #endif /* SPI45 */ |
| |
| #if defined(CONFIG_STM32H7_SPI6_SLAVE) |
| # if STM32_RCC_D3CCIPR_SPI6SRC == RCC_D3CCIPR_SPI6SEL_PCLK4 |
| # define SPI6_KERNEL_CLOCK_FREQ STM32_PCLK4_FREQUENCY |
| # else |
| # error Not supported yet |
| # endif |
| # if SPI6_KERNEL_CLOCK_FREQ > 100000000 |
| # error Not supported SPI6 frequency |
| # endif |
| #endif /* SPI6 */ |
| |
| #if defined (CONFIG_STM32H7_SPI_SLAVE_QSIZE) |
| # if CONFIG_STM32H7_SPI_SLAVE_QSIZE > 65535 |
| # error CONFIG_STM32H7_SPI_SLAVE_QSIZE too large |
| # endif |
| #endif |
| |
| /* SPI6 is in D3 domain and is not yet supported. Remove this when the proper |
| * support is in place |
| */ |
| |
| #if defined (CONFIG_STM32H7_SPI6_SLAVE) |
| # error SPI6 slave not supported yet |
| #endif |
| |
| #define DMA_BUFFER_MASK (ARMV7M_DCACHE_LINESIZE - 1) |
| #define DMA_ALIGN_UP(n) (((n) + DMA_BUFFER_MASK) & ~DMA_BUFFER_MASK) |
| #define DMA_ALIGN_DOWN(n) ((n) & ~DMA_BUFFER_MASK) |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| enum spi_config_e |
| { |
| FULL_DUPLEX = 0, |
| SIMPLEX_TX, |
| SIMPLEX_RX, |
| HALF_DUPLEX |
| }; |
| |
| struct stm32_spidev_s |
| { |
| /* Externally visible part of the SPI slave interface */ |
| |
| struct spi_slave_ctrlr_s ctrlr; |
| struct spi_slave_dev_s *dev; /* Bound SPI slave device interface */ |
| uint32_t spibase; /* SPIn base address */ |
| uint32_t spiclock; /* Clocking for the SPI module */ |
| uint8_t irq; /* SPI IRQ number */ |
| uint32_t nss_pin; /* Chip select pin configuration */ |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| volatile uint8_t rxresult; /* Result of the RX DMA */ |
| volatile uint8_t txresult; /* Result of the TX DMA */ |
| uint32_t rxch; /* The RX DMA channel number */ |
| uint32_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 */ |
| bool dmarunning; /* DMA is started */ |
| #endif |
| bool initialized; /* Has SPI interface been initialized */ |
| mutex_t lock; /* Held while chip is selected for mutual |
| * exclusion */ |
| int8_t nbits; /* Width of word in bits */ |
| uint8_t mode; /* Mode 0,1,2,3 */ |
| uint8_t bus; /* SPI bus number */ |
| bool nss; /* True: Chip selected */ |
| #ifdef CONFIG_PM |
| struct pm_callback_s pm_cb; /* PM callbacks */ |
| #endif |
| enum spi_config_e config; /* full/half duplex, simplex rx/tx */ |
| |
| /* Output queue */ |
| |
| uint16_t ohead; /* Location of next value in out queue */ |
| uint16_t otail; /* Index of first value in out queue */ |
| uint8_t *outq; |
| |
| /* Input queue */ |
| |
| uint16_t ihead; /* Location of next unread value */ |
| #ifndef CONFIG_STM32H7_SPI_DMA |
| uint16_t itail; /* Index of next free memory pointer */ |
| #endif |
| uint8_t *inq; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| /* Helpers */ |
| |
| static inline uint32_t spi_getreg(struct stm32_spidev_s *priv, |
| uint32_t offset); |
| static inline void spi_putreg(struct stm32_spidev_s *priv, |
| uint32_t offset, uint32_t value); |
| static inline void spi_modifyreg(struct stm32_spidev_s *priv, |
| uint32_t offset, uint32_t clrbits, |
| uint32_t setbits); |
| static inline uint32_t spi_readword(struct stm32_spidev_s *priv); |
| static inline void spi_writeword(struct stm32_spidev_s *priv, |
| uint32_t byte); |
| static inline bool spi_9to16bitmode(struct stm32_spidev_s *priv); |
| #ifdef CONFIG_DEBUG_SPI_INFO |
| static inline void spi_dumpregs(struct stm32_spidev_s *priv); |
| #endif |
| |
| /* DMA support */ |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| 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(struct stm32_spidev_s *priv, |
| size_t nwords); |
| static void spi_dmatxsetup(struct stm32_spidev_s *priv, |
| size_t nwords); |
| static inline void spi_dmarxstart(struct stm32_spidev_s *priv); |
| static inline void spi_dmatxstart(struct stm32_spidev_s *priv); |
| #endif |
| |
| /* Interrupt handler detecting nss status changes */ |
| |
| static int spi_nssinterrupt(int irq, void *context, void *arg); |
| |
| /* SPI slave methods */ |
| |
| static void spi_bind(struct spi_slave_ctrlr_s *ctrlr, |
| struct spi_slave_dev_s *dev, |
| enum spi_slave_mode_e mode, |
| int nbits); |
| static void spi_unbind(struct spi_slave_ctrlr_s *ctrlr); |
| static int spi_enqueue(struct spi_slave_ctrlr_s *ctrlr, |
| const void *data, |
| size_t len); |
| static bool spi_qfull(struct spi_slave_ctrlr_s *ctrlr); |
| static void spi_qflush(struct spi_slave_ctrlr_s *ctrlr); |
| static size_t spi_qpoll(struct spi_slave_ctrlr_s *ctrlr); |
| |
| /* Initialization */ |
| |
| static void spi_slave_initialize(struct stm32_spidev_s *priv); |
| |
| /* PM interface */ |
| |
| #ifdef CONFIG_PM |
| static int spi_pm_prepare(struct pm_callback_s *cb, int domain, |
| enum pm_state_e pmstate); |
| #endif |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* SPI slave controller driver operations */ |
| |
| static const struct spi_slave_ctrlrops_s g_ctrlr_ops = |
| { |
| .bind = spi_bind, |
| .unbind = spi_unbind, |
| .enqueue = spi_enqueue, |
| .qfull = spi_qfull, |
| .qflush = spi_qflush, |
| .qpoll = spi_qpoll, |
| }; |
| |
| #define SPI_SLAVE_OUTQ(x) spi##x##_outq |
| #define SPI_SLAVE_INQ(x) spi##x##_inq |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| #define SPI_SLAVE_INIT_DMA(x) \ |
| .rxch = DMAMAP_SPI##x##_RX, \ |
| .txch = DMAMAP_SPI##x##_TX, \ |
| .rxsem = SEM_INITIALIZER(0), \ |
| .txsem = SEM_INITIALIZER(0), \ |
| .outq = SPI_SLAVE_OUTQ(x), \ |
| .inq = SPI_SLAVE_INQ(x), |
| #else |
| #define SPI_SLAVE_INIT_DMA(x) |
| #endif |
| |
| #ifdef CONFIG_PM |
| #define SPI_SLAVE_INIT_PM_PREPARE .pm_cb.prepare = spi_pm_prepare, |
| #else |
| #define SPI_SLAVE_INIT_PM_PREPARE |
| #endif |
| |
| #define SPI_SLAVE_INIT(x) \ |
| { \ |
| .ctrlr.ops = &g_ctrlr_ops, \ |
| .dev = NULL, \ |
| .spibase = STM32_SPI##x##_BASE, \ |
| .spiclock = SPI45_KERNEL_CLOCK_FREQ, \ |
| .irq = STM32_IRQ_SPI##x, \ |
| SPI_SLAVE_INIT_DMA(x) \ |
| .initialized = false, \ |
| .lock = NXMUTEX_INITIALIZER, \ |
| SPI_SLAVE_INIT_PM_PREPARE \ |
| .config = CONFIG_STM32H7_SPI##x##_COMMTYPE, \ |
| } |
| |
| #ifdef CONFIG_STM32H7_SPI1_SLAVE |
| |
| static |
| uint8_t SPI_SLAVE_OUTQ(1)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)] |
| aligned_data(ARMV7M_DCACHE_LINESIZE); |
| static |
| uint8_t SPI_SLAVE_INQ(1)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)] |
| aligned_data(ARMV7M_DCACHE_LINESIZE); |
| static struct stm32_spidev_s g_spi1ctrlr = SPI_SLAVE_INIT(1); |
| |
| #endif |
| |
| #ifdef CONFIG_STM32H7_SPI2_SLAVE |
| |
| static |
| uint8_t SPI_SLAVE_OUTQ(2)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)] |
| aligned_data(ARMV7M_DCACHE_LINESIZE); |
| static |
| uint8_t SPI_SLAVE_INQ(2)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)] |
| aligned_data(ARMV7M_DCACHE_LINESIZE); |
| static struct stm32_spidev_s g_spi2ctrlr = SPI_SLAVE_INIT(2); |
| |
| #endif |
| |
| #ifdef CONFIG_STM32H7_SPI3_SLAVE |
| |
| static |
| uint8_t SPI_SLAVE_OUTQ(3)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)] |
| aligned_data(ARMV7M_DCACHE_LINESIZE); |
| static |
| uint8_t SPI_SLAVE_INQ(3)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)] |
| aligned_data(ARMV7M_DCACHE_LINESIZE); |
| static struct stm32_spidev_s g_spi3ctrlr = SPI_SLAVE_INIT(3); |
| |
| #endif |
| |
| #ifdef CONFIG_STM32H7_SPI4_SLAVE |
| |
| static |
| uint8_t SPI_SLAVE_OUTQ(4)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)] |
| aligned_data(ARMV7M_DCACHE_LINESIZE); |
| static |
| uint8_t SPI_SLAVE_INQ(4)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)] |
| aligned_data(ARMV7M_DCACHE_LINESIZE); |
| static struct stm32_spidev_s g_spi4ctrlr = SPI_SLAVE_INIT(4); |
| |
| #endif |
| |
| #ifdef CONFIG_STM32H7_SPI5_SLAVE |
| |
| static |
| uint8_t SPI_SLAVE_OUTQ(5)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)] |
| aligned_data(ARMV7M_DCACHE_LINESIZE); |
| static |
| uint8_t SPI_SLAVE_INQ(5)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)] |
| aligned_data(ARMV7M_DCACHE_LINESIZE); |
| static struct stm32_spidev_s g_spi5ctrlr = SPI_SLAVE_INIT(5); |
| |
| #endif |
| |
| #ifdef CONFIG_STM32H7_SPI6_SLAVE |
| |
| /* TODO: these needs to be located in SRAM3 for SPI6 */ |
| |
| static |
| uint8_t SPI_SLAVE_OUTQ(6)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)] |
| aligned_data(ARMV7M_DCACHE_LINESIZE); |
| static |
| uint8_t SPI_SLAVE_INQ(6)[DMA_ALIGN_UP(CONFIG_STM32H7_SPI_SLAVE_QSIZE)] |
| aligned_data(ARMV7M_DCACHE_LINESIZE); |
| static struct stm32_spidev_s g_spi6ctrlr = SPI_SLAVE_INIT(6); |
| |
| #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(struct stm32_spidev_s *priv, |
| uint32_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(struct stm32_spidev_s *priv, |
| uint32_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 uint32_t spi_getreg(struct stm32_spidev_s *priv, |
| uint32_t offset) |
| { |
| return getreg32(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 |
| * |
| ****************************************************************************/ |
| |
| static inline void spi_putreg(struct stm32_spidev_s *priv, |
| uint32_t offset, uint32_t value) |
| { |
| putreg32(value, priv->spibase + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: spi_modifyreg |
| * |
| * Description: |
| * Write a 32-bit value to the SPI register at offset |
| * |
| * Input Parameters: |
| * priv - private SPI device structure |
| * offset - offset to the register of interest |
| * clrbits - the bits to clear |
| * setbits - the bits to set |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void spi_modifyreg(struct stm32_spidev_s *priv, |
| uint32_t offset, uint32_t clrbits, |
| uint32_t setbits) |
| { |
| modifyreg32(priv->spibase + offset, clrbits, setbits); |
| } |
| |
| /**************************************************************************** |
| * Name: spi_readword |
| * |
| * Description: |
| * Read one byte from SPI |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * |
| * Returned Value: |
| * Byte as read |
| * |
| ****************************************************************************/ |
| |
| static inline uint32_t spi_readword(struct stm32_spidev_s *priv) |
| { |
| /* Can't receive in tx only mode */ |
| |
| if (priv->config == SIMPLEX_TX) |
| { |
| return 0; |
| } |
| |
| /* Wait until the receive buffer is not empty */ |
| |
| while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_RXP) == 0); |
| |
| /* Then return the received byte */ |
| |
| return spi_getreg(priv, STM32_SPI_RXDR_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(struct stm32_spidev_s *priv, |
| uint32_t word) |
| { |
| /* Can't transmit in rx only mode */ |
| |
| if (priv->config == SIMPLEX_RX) |
| { |
| return; |
| } |
| |
| /* Wait until the transmit buffer is empty */ |
| |
| while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_TXP) == 0); |
| |
| /* Then send the byte */ |
| |
| spi_putreg(priv, STM32_SPI_TXDR_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(struct stm32_spidev_s *priv) |
| { |
| /* Can't receive in tx only mode */ |
| |
| if (priv->config == SIMPLEX_TX) |
| { |
| return 0; |
| } |
| |
| /* Wait until the receive buffer is not empty */ |
| |
| while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_RXP) == 0); |
| |
| /* Then return the received byte */ |
| |
| return spi_getreg8(priv, STM32_SPI_RXDR_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(struct stm32_spidev_s *priv, |
| uint8_t byte) |
| { |
| /* Can't transmit in rx only mode */ |
| |
| if (priv->config == SIMPLEX_RX) |
| { |
| return; |
| } |
| |
| /* Wait until the transmit buffer is empty */ |
| |
| while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_TXP) == 0); |
| |
| /* Then send the byte */ |
| |
| spi_putreg8(priv, STM32_SPI_TXDR_OFFSET, byte); |
| } |
| |
| /**************************************************************************** |
| * Name: spi_dumpregs |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_DEBUG_SPI_INFO |
| static void spi_dumpregs(struct stm32_spidev_s *priv) |
| { |
| spiinfo("CR1: 0x%08x CFG1: 0x%08x CFG2: 0x%08x\n", |
| spi_getreg(priv, STM32_SPI_CR1_OFFSET), |
| spi_getreg(priv, STM32_SPI_CFG1_OFFSET), |
| spi_getreg(priv, STM32_SPI_CFG2_OFFSET)); |
| spiinfo("IER: 0x%08x SR: 0x%08x I2SCFGR: 0x%08x\n", |
| spi_getreg(priv, STM32_SPI_IER_OFFSET), |
| spi_getreg(priv, STM32_SPI_SR_OFFSET), |
| spi_getreg(priv, STM32_SPI_I2SCFGR_OFFSET)); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * 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(struct stm32_spidev_s *priv) |
| { |
| uint32_t regval = spi_getreg(priv, STM32_SPI_CFG1_OFFSET); |
| |
| return ((regval & SPI_CFG1_CRCSIZE_9BIT) == SPI_CFG1_CRCSIZE_9BIT); |
| } |
| |
| /**************************************************************************** |
| * Name: spi_dmarxcallback |
| * |
| * Description: |
| * Called when the RX DMA completes |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg) |
| { |
| struct stm32_spidev_s *priv = (struct stm32_spidev_s *)arg; |
| |
| /* Wake-up the SPI driver */ |
| |
| priv->rxresult = isr | 0x080; /* OR'ed with 0x80 to assure non-zero */ |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: spi_dmatxcallback |
| * |
| * Description: |
| * Called when the RX DMA completes |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg) |
| { |
| struct stm32_spidev_s *priv = (struct stm32_spidev_s *)arg; |
| |
| /* Wake-up the SPI driver */ |
| |
| priv->txresult = isr | 0x080; /* OR'ed with 0x80 to assure non-zero */ |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: spi_dmarxsetup |
| * |
| * Description: |
| * Setup to perform RX DMA |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| static void spi_dmarxsetup(struct stm32_spidev_s *priv, size_t nwords) |
| { |
| stm32_dmacfg_t dmacfg; |
| |
| /* Can't receive in tx only mode */ |
| |
| if (priv->config == SIMPLEX_TX) |
| { |
| return; |
| } |
| |
| /* 8- or 16-bit mode? */ |
| |
| if (spi_9to16bitmode(priv)) |
| { |
| /* 16-bit mode */ |
| |
| priv->rxccr = SPI_RXDMA16_CONFIG; |
| } |
| else |
| { |
| /* 8-bit mode -- is there a buffer to receive data in? */ |
| |
| priv->rxccr = SPI_RXDMA8_CONFIG; |
| } |
| |
| /* Configure the RX DMA */ |
| |
| /* This just transfers continuously to circular buffer */ |
| |
| dmacfg.paddr = priv->spibase + STM32_SPI_RXDR_OFFSET; |
| dmacfg.maddr = (uint32_t)priv->inq; |
| dmacfg.ndata = CONFIG_STM32H7_SPI_SLAVE_QSIZE; |
| dmacfg.cfg1 = priv->rxccr; |
| dmacfg.cfg2 = 0; |
| |
| stm32_dmasetup(priv->rxdma, &dmacfg); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: spi_dmatxsetup |
| * |
| * Description: |
| * Setup to perform TX DMA |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| static void spi_dmatxsetup(struct stm32_spidev_s *priv, size_t nwords) |
| { |
| /* TODO: set up dma to transfer out the new data from priv->outq, |
| * which is set up in spi_enqueue |
| * When the transfer is complete, update outq indexies accordingly |
| */ |
| |
| #if 0 |
| stm32_dmacfg_t dmacfg; |
| |
| /* Can't transmit in rx only mode */ |
| |
| if (priv->config == SIMPLEX_RX) |
| { |
| return; |
| } |
| |
| /* 8- or 16-bit mode? */ |
| |
| if (spi_9to16bitmode(priv)) |
| { |
| /* 16-bit mode */ |
| |
| priv->txccr = SPI_TXDMA16_CONFIG; |
| } |
| else |
| { |
| /* 8-bit mode */ |
| |
| priv->txccr = SPI_TXDMA8_CONFIG; |
| } |
| |
| dmacfg.paddr = priv->spibase + STM32_SPI_TXDR_OFFSET; |
| dmacfg.maddr = (uint32_t)priv->outq[0]; |
| dmacfg.ndata = nwords; |
| dmacfg.cfg1 = priv->txccr; |
| dmacfg.cfg2 = 0; |
| |
| /* Setup the TX DMA */ |
| |
| stm32_dmasetup(priv->txdma, &dmacfg); |
| |
| #endif // 0 |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: spi_dmarxstart |
| * |
| * Description: |
| * Start RX DMA. NB! This must be called before spi_dmatxstart |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| static void spi_dmarxstart(struct stm32_spidev_s *priv) |
| { |
| /* Can't receive in tx only mode */ |
| |
| if (priv->config == SIMPLEX_TX) |
| { |
| return; |
| } |
| |
| priv->rxresult = 0; |
| |
| spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, 0, SPI_CFG1_RXDMAEN); |
| stm32_dmastart(priv->rxdma, spi_dmarxcallback, priv, false); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: spi_dmatxstart |
| * |
| * Description: |
| * Start TX DMA. NB! This must be called after spi_dmarxstart |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| static void spi_dmatxstart(struct stm32_spidev_s *priv) |
| { |
| /* Can't transmit in rx only mode */ |
| |
| if (priv->config == SIMPLEX_RX) |
| { |
| return; |
| } |
| |
| priv->txresult = 0; |
| stm32_dmastart(priv->txdma, spi_dmatxcallback, priv, false); |
| spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, 0, SPI_CFG1_TXDMAEN); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: spi_lock |
| * |
| * Description: |
| * Take or release the semaphore that enforces mutually exclusive access to |
| * SPI resources, handling any exceptional conditions |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * lock - true: Lock spi bus, false: unlock SPI bus |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static int spi_lock(struct spi_slave_ctrlr_s *ctrlr, bool lock) |
| { |
| struct stm32_spidev_s *priv = (struct stm32_spidev_s *)ctrlr; |
| int ret; |
| |
| if (lock) |
| { |
| /* Take the mutex (perhaps waiting) */ |
| |
| ret = nxmutex_lock(&priv->lock); |
| } |
| else |
| { |
| ret = nxmutex_unlock(&priv->lock); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: spi_enable |
| * |
| * Description: |
| * Enable/disable the SPI peripheral |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * state - true: enable, false: disable |
| * |
| ****************************************************************************/ |
| |
| static inline void spi_enable(struct stm32_spidev_s *priv, bool state) |
| { |
| if (state == true) |
| { |
| /* Enable SPI */ |
| |
| spi_modifyreg(priv, STM32_SPI_CR1_OFFSET, 0, SPI_CR1_SPE); |
| } |
| else |
| { |
| /* Disable SPI */ |
| |
| spi_modifyreg(priv, STM32_SPI_CR1_OFFSET, SPI_CR1_SPE, 0); |
| } |
| } |
| |
| /**************************************************************************** |
| * 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(struct spi_slave_ctrlr_s *ctrlr, |
| enum spi_mode_e mode) |
| { |
| struct stm32_spidev_s *priv = (struct stm32_spidev_s *)ctrlr; |
| uint32_t setbits = 0; |
| uint32_t clrbits = 0; |
| |
| 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_CFG2_CPOL | SPI_CFG2_CPHA; |
| break; |
| |
| case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */ |
| setbits = SPI_CFG2_CPHA; |
| clrbits = SPI_CFG2_CPOL; |
| break; |
| |
| case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */ |
| setbits = SPI_CFG2_CPOL; |
| clrbits = SPI_CFG2_CPHA; |
| break; |
| |
| case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */ |
| setbits = SPI_CFG2_CPOL | SPI_CFG2_CPHA; |
| clrbits = 0; |
| break; |
| |
| default: |
| return; |
| } |
| |
| /* Change SPI mode */ |
| |
| spi_modifyreg(priv, STM32_SPI_CFG2_OFFSET, clrbits, setbits); |
| |
| /* 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(struct spi_slave_ctrlr_s *ctrlr, int nbits) |
| { |
| struct stm32_spidev_s *priv = (struct stm32_spidev_s *)ctrlr; |
| uint32_t setbits = 0; |
| uint32_t clrbits = 0; |
| |
| spiinfo("nbits=%d\n", nbits); |
| |
| /* Has the number of bits changed? */ |
| |
| if (nbits != priv->nbits) |
| { |
| /* Yes... Set CFG1 appropriately */ |
| |
| /* Set the number of bits (valid range 4-32) */ |
| |
| if (nbits < 4 || nbits > 32) |
| { |
| return; |
| } |
| |
| clrbits = SPI_CFG1_DSIZE_MASK; |
| setbits = SPI_CFG1_DSIZE_VAL(nbits); |
| |
| /* REVISIT: FIFO threshold level */ |
| |
| /* If nbits is <=8, then we are in byte mode and FRXTH shall be set |
| * (else, transaction will not complete). |
| */ |
| |
| if (nbits < 9) |
| { |
| setbits |= SPI_CFG1_FTHLV_1DATA; /* RX FIFO Threshold = 1 byte */ |
| } |
| else |
| { |
| setbits |= SPI_CFG1_FTHLV_2DATA; /* RX FIFO Threshold = 2 byte */ |
| } |
| |
| spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, clrbits, setbits); |
| |
| priv->nbits = nbits; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: spi_bind |
| * |
| * Description: |
| * Bind the SPI slave device interface to the SPI slave controller |
| * interface and configure the SPI interface. Upon return, the SPI |
| * slave controller driver is fully operational and ready to perform |
| * transfers. |
| * |
| * Input Parameters: |
| * ctrlr - SPI Slave controller interface instance |
| * dev - SPI Slave device interface instance |
| * mode - The SPI Slave mode requested |
| * nbits - The number of bits requested. |
| * If value is greater than 0, then it implies MSB first |
| * If value is less than 0, then it implies LSB first with -nbits |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void spi_bind(struct spi_slave_ctrlr_s *ctrlr, |
| struct spi_slave_dev_s *dev, enum spi_slave_mode_e mode, |
| int nbits) |
| { |
| struct stm32_spidev_s *priv = (struct stm32_spidev_s *)ctrlr; |
| uint32_t nss_gpio; |
| |
| spiinfo("dev=%p mode=%d nbits=%d\n", sdv, mode, nbits); |
| |
| DEBUGASSERT(priv != NULL && priv->dev == NULL && dev != NULL); |
| |
| /* Get exclusive access to the SPI device */ |
| |
| spi_lock(ctrlr, true); |
| |
| /* Make sure the spi is disabled */ |
| |
| spi_enable(priv, false); |
| |
| /* Make sure the dma is disabled */ |
| |
| spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, |
| SPI_CFG1_RXDMAEN | SPI_CFG1_TXDMAEN, 0); |
| |
| /* invalidate the whole rx buffer */ |
| |
| up_flush_dcache((uintptr_t)priv->inq, |
| (uintptr_t)priv->inq + CONFIG_STM32H7_SPI_SLAVE_QSIZE); |
| |
| /* Bind the SPI slave device interface instance to the SPI slave |
| * controller interface. |
| */ |
| |
| priv->dev = dev; |
| |
| /* Initialize the circular buffer head */ |
| |
| priv->ihead = 0; |
| |
| /* Setup to begin normal SPI operation */ |
| |
| spi_setmode(ctrlr, mode); |
| spi_setbits(ctrlr, nbits); |
| |
| /* First, configure NSS as GPIO EXTI input */ |
| |
| nss_gpio = priv->nss_pin & (GPIO_PORT_MASK | GPIO_PIN_MASK); |
| nss_gpio |= (GPIO_INPUT | GPIO_FLOAT | GPIO_EXTI); |
| stm32_configgpio(nss_gpio); |
| |
| /* Bind to NSS interrupt */ |
| |
| stm32_gpiosetevent(priv->nss_pin, false, true, false, |
| spi_nssinterrupt, priv); |
| |
| #ifdef CONFIG_PM |
| /* Register to receive power management callbacks */ |
| |
| ret = pm_register(&priv->pm_cb); |
| DEBUGASSERT(ret == OK); |
| UNUSED(ret); |
| #endif |
| |
| spi_lock(ctrlr, false); |
| } |
| |
| /**************************************************************************** |
| * Name: spi_nssinterrupt |
| * |
| * Description: |
| * SPI slave NSS interrupt handler |
| * |
| * Input Parameters: |
| * irq - not used |
| * context - not used |
| * arg - pointer to controller |
| * |
| * Returned Value: |
| * OK |
| * |
| ****************************************************************************/ |
| |
| static int spi_nssinterrupt(int irq, void *context, void *arg) |
| { |
| struct stm32_spidev_s *priv = (struct stm32_spidev_s *)arg; |
| |
| /* If the pin is low, just enable rising edge interrupt */ |
| |
| if (!stm32_gpioread(priv->nss_pin)) |
| { |
| /* Bind to NSS rising edge interrupt */ |
| |
| stm32_gpiosetevent(priv->nss_pin, true, false, false, |
| spi_nssinterrupt, priv); |
| return OK; |
| } |
| |
| /* Disable NSS interrupt */ |
| |
| stm32_gpiosetevent(priv->nss_pin, false, false, false, |
| NULL, priv); |
| |
| /* Re-configure nss pin */ |
| |
| stm32_configgpio(priv->nss_pin); |
| |
| /* Enable spi peripheral */ |
| |
| spi_enable(priv, true); |
| |
| /* Flush SPI read FIFO */ |
| |
| while ((spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_RXPLVL_MASK) != 0) |
| { |
| spi_getreg(priv, STM32_SPI_RXDR_OFFSET); |
| } |
| |
| /* Disable spi before dma setup */ |
| |
| spi_enable(priv, false); |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| |
| /* Setup DMAs */ |
| |
| spi_dmarxsetup(priv, 0); |
| spi_dmatxsetup(priv, 0); |
| |
| /* Start the DMAs */ |
| |
| spi_dmarxstart(priv); |
| spi_dmatxstart(priv); |
| |
| priv->dmarunning = true; |
| #endif |
| |
| /* Enable spi peripheral */ |
| |
| spi_enable(priv, true); |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: spi_unbind |
| * |
| * Description: |
| * Un-bind the SPI slave device interface from the SPI slave controller |
| * interface. Reset the SPI interface and restore the SPI slave |
| * controller driver to its initial state. |
| * |
| * Input Parameters: |
| * ctrlr - SPI Slave controller interface instance |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void spi_unbind(struct spi_slave_ctrlr_s *ctrlr) |
| { |
| struct stm32_spidev_s *priv = (struct stm32_spidev_s *)ctrlr; |
| |
| DEBUGASSERT(priv != NULL); |
| spiinfo("Unbinding %p\n", priv->dev); |
| |
| DEBUGASSERT(priv->dev != NULL); |
| |
| /* Get exclusive access to the SPI device */ |
| |
| spi_lock(ctrlr, true); |
| |
| /* Unbind the SPI slave interface */ |
| |
| priv->dev = NULL; |
| |
| /* Disable DMA */ |
| |
| spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, |
| SPI_CFG1_RXDMAEN | SPI_CFG1_TXDMAEN, 0); |
| |
| /* Disable the SPI peripheral */ |
| |
| spi_enable(priv, false); |
| |
| spi_lock(ctrlr, false); |
| } |
| |
| /**************************************************************************** |
| * Name: spi_enqueue |
| * |
| * Description: |
| * Enqueue the next value to be shifted out from the interface. This adds |
| * the word the controller driver for a subsequent transfer but has no |
| * effect on any in-process or currently "committed" transfers. |
| * |
| * Input Parameters: |
| * ctrlr - SPI Slave controller interface instance |
| * data - Pointer to the command/data mode data to be shifted out. |
| * The data width must be aligned to the nbits parameter which was |
| * previously provided to the bind() method. |
| * len - Number of units of "nbits" wide to enqueue, |
| * "nbits" being the data width previously provided to the bind() |
| * method. |
| * |
| * Returned Value: |
| * Zero if the word was successfully queue; A negated errno valid is |
| * returned on any failure to enqueue the word (such as if the queue is |
| * full). |
| * |
| ****************************************************************************/ |
| |
| static int spi_enqueue(struct spi_slave_ctrlr_s *ctrlr, |
| const void *data, size_t len) |
| { |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: spi_qfull |
| * |
| * Description: |
| * Return true if the queue is full or false if there is space to add an |
| * additional word to the queue. |
| * |
| * Input Parameters: |
| * ctrlr - SPI Slave controller interface instance |
| * |
| * Returned Value: |
| * true if the output queue is full, false otherwise. |
| * |
| ****************************************************************************/ |
| |
| static bool spi_qfull(struct spi_slave_ctrlr_s *ctrlr) |
| { |
| return false; |
| } |
| |
| /**************************************************************************** |
| * Name: spi_qflush |
| * |
| * Description: |
| * Discard all saved values in the output queue. On return from this |
| * function the output queue will be empty. Any in-progress or otherwise |
| * "committed" output values may not be flushed. |
| * |
| * Input Parameters: |
| * ctrlr - SPI Slave controller interface instance |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void spi_qflush(struct spi_slave_ctrlr_s *ctrlr) |
| { |
| struct stm32_spidev_s *priv = (struct stm32_spidev_s *)ctrlr; |
| irqstate_t flags; |
| |
| DEBUGASSERT(priv != NULL && priv->dev != NULL); |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| if (!priv->dmarunning) |
| { |
| return; |
| } |
| #endif |
| |
| /* Get exclusive access to the SPI device */ |
| |
| spi_lock(ctrlr, true); |
| flags = enter_critical_section(); |
| |
| /* Flush the input buffers */ |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| priv->ihead = |
| CONFIG_STM32H7_SPI_SLAVE_QSIZE - stm32_dmaresidual(priv->rxdma); |
| #else |
| priv->ihead = 0; |
| priv->itail = 0; |
| #endif |
| |
| /* Flush the output buffers */ |
| |
| priv->ohead = 0; |
| priv->otail = 0; |
| leave_critical_section(flags); |
| spi_lock(ctrlr, false); |
| } |
| |
| /**************************************************************************** |
| * Name: spi_rx_buffer_free |
| * |
| * Description: |
| * Handle buffer wrapping around & invalidate the previous buffer cache |
| * |
| * Input Parameters: |
| * ptr - pointer to the rx buffer |
| * start - start index of just received data |
| * end - end index of just received data |
| * |
| * Returned Value: |
| * new start index; 0 if buffer wrapped around, end otherwise |
| * |
| ****************************************************************************/ |
| |
| static inline int spi_rx_buffer_free(uint8_t *ptr, int start, int end) |
| { |
| /* The priv->ihead can only be greater than qsize if the device driver |
| * returns garbage |
| */ |
| |
| if (end >= CONFIG_STM32H7_SPI_SLAVE_QSIZE) |
| { |
| end = 0; |
| up_invalidate_dcache((uintptr_t)&ptr[start], |
| (uintptr_t)&ptr[CONFIG_STM32H7_SPI_SLAVE_QSIZE]); |
| } |
| else |
| { |
| up_invalidate_dcache((uintptr_t)&ptr[start], |
| (uintptr_t)&ptr[end]); |
| } |
| |
| return end; |
| } |
| |
| /**************************************************************************** |
| * Name: spi_qpoll |
| * |
| * Description: |
| * Tell the controller to output all the receive queue data. |
| * |
| * Input Parameters: |
| * ctrlr - SPI Slave controller interface instance |
| * |
| * Returned Value: |
| * Number of bytes left in the rx queue. If the device accepted all the |
| * |
| ****************************************************************************/ |
| |
| static size_t spi_qpoll(struct spi_slave_ctrlr_s *ctrlr) |
| { |
| struct stm32_spidev_s *priv = (struct stm32_spidev_s *)ctrlr; |
| int itail; |
| int ihead; |
| uint16_t bytes_left; |
| |
| DEBUGASSERT(priv != NULL && priv->dev != NULL); |
| DEBUGASSERT(priv->ihead < CONFIG_STM32H7_SPI_SLAVE_QSIZE); |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| if (!priv->dmarunning) |
| { |
| return 0; |
| } |
| #endif |
| |
| /* Get exclusive access to the SPI device */ |
| |
| spi_lock(ctrlr, true); |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| itail = CONFIG_STM32H7_SPI_SLAVE_QSIZE - stm32_dmaresidual(priv->rxdma); |
| #else |
| #error Support only simplex mode rx with dma |
| #endif |
| |
| /* Receive all data between last read index and the current index */ |
| |
| ihead = priv->ihead; |
| if (ihead > itail) |
| { |
| /* Receive the end of receive buffer */ |
| |
| priv->ihead += SPIS_DEV_RECEIVE(priv->dev, |
| (const uint16_t *)&priv->inq[ihead], |
| CONFIG_STM32H7_SPI_SLAVE_QSIZE - |
| ihead); |
| |
| /* Invalidate dcache and wrap around the priv->ihead */ |
| |
| priv->ihead = spi_rx_buffer_free(priv->inq, ihead, priv->ihead); |
| } |
| |
| ihead = priv->ihead; |
| if (ihead < itail) |
| { |
| /* Receive the data between ihead and itail */ |
| |
| priv->ihead += SPIS_DEV_RECEIVE(priv->dev, |
| (const uint16_t *)&priv->inq[ihead], |
| itail - ihead); |
| |
| /* Invalidate dcache and wrap around the priv->ihead */ |
| |
| priv->ihead = spi_rx_buffer_free(priv->inq, ihead, priv->ihead); |
| } |
| |
| /* Calculate the number of bytes left in the buffer */ |
| |
| bytes_left = itail < priv->ihead |
| ? CONFIG_STM32H7_SPI_SLAVE_QSIZE - priv->ihead + itail |
| : itail - priv->ihead; |
| |
| spi_lock(ctrlr, false); |
| |
| return bytes_left; |
| } |
| |
| /**************************************************************************** |
| * Name: spi_pm_prepare |
| * |
| * Description: |
| * Request the driver to prepare for a new power state. This is a |
| * warning that the system is about to enter into a new power state. The |
| * driver should begin whatever operations that may be required to enter |
| * power state. The driver may abort the state change mode by returning |
| * a non-zero value from the callback function. |
| * |
| * Input Parameters: |
| * cb - Returned to the driver. The driver version of the callback |
| * structure may include additional, driver-specific state |
| * data at the end of the structure. |
| * domain - Identifies the activity domain of the state change |
| * pmstate - Identifies the new PM state |
| * |
| * Returned Value: |
| * 0 (OK) means the event was successfully processed and that the driver |
| * is prepared for the PM state change. Non-zero means that the driver |
| * is not prepared to perform the tasks needed achieve this power setting |
| * and will cause the state change to be aborted. NOTE: The prepare |
| * method will also be recalled when reverting from lower back to higher |
| * power consumption modes (say because another driver refused a lower |
| * power state change). Drivers are not permitted to return non-zero |
| * values when reverting back to higher power consumption modes! |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_PM |
| static int spi_pm_prepare(struct pm_callback_s *cb, int domain, |
| enum pm_state_e pmstate) |
| { |
| struct stm32_spidev_s *priv = |
| (struct stm32_spidev_s *)((char *)cb - |
| offsetof(struct stm32_spidev_s, pm_cb)); |
| |
| /* Logic to prepare for a reduced power state goes here. */ |
| |
| switch (pmstate) |
| { |
| case PM_NORMAL: |
| case PM_IDLE: |
| break; |
| |
| case PM_STANDBY: |
| case PM_SLEEP: |
| |
| /* Check if exclusive lock for SPI bus is held. */ |
| |
| /* If exclusive lock is held, do not allow entry to deeper PM states */ |
| |
| if (nxmutex_is_locked(&priv->lock)) |
| { |
| return -EBUSY; |
| } |
| |
| break; |
| |
| default: |
| |
| /* Should not get here */ |
| |
| break; |
| } |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: spi_slave_initialize |
| * |
| * Description: |
| * Initialize the selected SPI bus for slave operations |
| * |
| * Input Parameters: |
| * priv - private SPI device structure |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void spi_slave_initialize(struct stm32_spidev_s *priv) |
| { |
| uint32_t setbits = 0; |
| uint32_t clrbits = 0; |
| #ifdef CONFIG_PM |
| int ret; |
| #endif |
| |
| /* Configure CR1, CFG1 and CFG2. Default configuration: |
| * Mode 0: CFG2.CPHA=0 and CFG2.CPOL=0 |
| * Master: CFG2.MSTR=1 |
| * 8-bit: CFG1.DSIZE=7 |
| * MSB transmitted first: CFG2.LSBFRST=0 |
| * Replace NSS with SSI & SSI=1: CR1.SSI=1 CFG2.SSM=1 (prevent MODF err) |
| * Two lines full duplex: CFG2.COMM=0 |
| */ |
| |
| /* CR1 */ |
| |
| clrbits = SPI_CR1_SPE; |
| spi_modifyreg(priv, STM32_SPI_CR1_OFFSET, clrbits, setbits); |
| |
| /* CFG1 */ |
| |
| clrbits = SPI_CFG1_DSIZE_MASK; |
| setbits = SPI_CFG1_DSIZE_8BIT | SPI_CFG1_FTHLV_1DATA; /* REVISIT: FTHLV */ |
| spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, clrbits, setbits); |
| |
| /* CFG2 */ |
| |
| clrbits = SPI_CFG2_CPHA | SPI_CFG2_CPOL | SPI_CFG2_LSBFRST | |
| SPI_CFG2_COMM_MASK; |
| setbits = SPI_CFG2_SSM; |
| |
| switch (priv->config) |
| { |
| default: |
| case FULL_DUPLEX: |
| setbits |= SPI_CFG2_COMM_FULL; |
| break; |
| case SIMPLEX_TX: |
| setbits |= SPI_CFG2_COMM_STX; |
| break; |
| case SIMPLEX_RX: |
| setbits |= SPI_CFG2_COMM_SRX; |
| break; |
| case HALF_DUPLEX: |
| setbits |= SPI_CFG2_COMM_HALF; |
| break; |
| } |
| |
| spi_modifyreg(priv, STM32_SPI_CFG2_OFFSET, clrbits, setbits); |
| |
| priv->nbits = 8; |
| priv->mode = SPIDEV_MODE0; |
| |
| /* CRCPOLY configuration */ |
| |
| spi_putreg(priv, STM32_SPI_CRCPOLY_OFFSET, 7); |
| |
| #ifdef CONFIG_STM32H7_SPI_DMA |
| /* DMA will be started in the interrupt handler, synchronized to the master |
| * nss |
| */ |
| |
| priv->dmarunning = false; |
| |
| if (priv->config != SIMPLEX_TX) |
| { |
| priv->rxdma = stm32_dmachannel(priv->rxch); |
| DEBUGASSERT(priv->rxdma); |
| } |
| |
| if (priv->config != SIMPLEX_RX) |
| { |
| priv->txdma = stm32_dmachannel(priv->txch); |
| DEBUGASSERT(priv->txdma); |
| } |
| |
| #endif |
| |
| #ifdef CONFIG_DEBUG_SPI_INFO |
| /* Dump registers after initialization */ |
| |
| spi_dumpregs(priv); |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: stm32_spi_slave_initialize |
| * |
| * Description: |
| * Initialize the selected SPI port(bus) to operate as spi slave |
| * |
| * Input Parameters: |
| * Port number (for hardware that has multiple SPI interfaces) |
| * |
| * Returned Value: |
| * Valid SPI device structure reference on success; a NULL on failure |
| * |
| ****************************************************************************/ |
| |
| /* Helper macros to avoid duplicating all the code */ |
| |
| #define GPIO_SPI_SCK(x) GPIO_SPI##x##_SCK |
| #define GPIO_SPI_MISO(x) GPIO_SPI##x##_MISO |
| #define GPIO_SPI_MOSI(x) GPIO_SPI##x##_MOSI |
| #define GPIO_SPI_NSS(x) GPIO_SPI##x##_NSS |
| |
| #define SPI_SLAVE_INIT_BUS(x) \ |
| priv = &g_spi##x##ctrlr; \ |
| \ |
| /* Only configure if the bus is not already configured */ \ |
| \ |
| if (!priv->initialized) \ |
| { \ |
| /* Configure SPI5 pins: SCK, MISO, MOSI and NSS */ \ |
| \ |
| stm32_configgpio(GPIO_SPI_SCK(x)); \ |
| stm32_configgpio(GPIO_SPI_MISO(x)); \ |
| stm32_configgpio(GPIO_SPI_MOSI(x)); \ |
| priv->nss_pin = GPIO_SPI_NSS(x); \ |
| \ |
| /* Set up default configuration: 8-bit, etc. */ \ |
| \ |
| spi_slave_initialize(priv); \ |
| priv->initialized = true; \ |
| } |
| |
| struct spi_slave_ctrlr_s *stm32_spi_slave_initialize(int bus) |
| { |
| struct stm32_spidev_s *priv = NULL; |
| irqstate_t flags = enter_critical_section(); |
| |
| #ifdef CONFIG_STM32H7_SPI1_SLAVE |
| if (bus == 1) |
| { |
| SPI_SLAVE_INIT_BUS(1); |
| } |
| else |
| #endif |
| |
| #ifdef CONFIG_STM32H7_SPI2_SLAVE |
| if (bus == 2) |
| { |
| SPI_SLAVE_INIT_BUS(2); |
| } |
| else |
| #endif |
| |
| #ifdef CONFIG_STM32H7_SPI3_SLAVE |
| if (bus == 3) |
| { |
| SPI_SLAVE_INIT_BUS(3); |
| } |
| else |
| #endif |
| |
| #ifdef CONFIG_STM32H7_SPI4_SLAVE |
| if (bus == 4) |
| { |
| SPI_SLAVE_INIT_BUS(4); |
| } |
| else |
| #endif |
| |
| #ifdef CONFIG_STM32H7_SPI5_SLAVE |
| if (bus == 5) |
| { |
| SPI_SLAVE_INIT_BUS(5); |
| } |
| else |
| #endif |
| |
| #ifdef CONFIG_STM32H7_SPI6_SLAVE |
| if (bus == 6) |
| { |
| SPI_SLAVE_INIT_BUS(6); |
| } |
| else |
| #endif |
| { |
| spierr("ERROR: Unsupported SPI bus: %d\n", bus); |
| leave_critical_section(flags); |
| return NULL; |
| } |
| |
| /* Initialize the SPI operations */ |
| |
| priv->ctrlr.ops = &g_ctrlr_ops; |
| |
| leave_critical_section(flags); |
| return (struct spi_slave_ctrlr_s *)priv; |
| } |
| |
| #endif /* CONFIG_STM32H7_SPI1..6_SLAVE */ |