| /**************************************************************************** |
| * arch/xtensa/src/esp32/esp32_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> |
| |
| #ifdef CONFIG_ESP32_SPI |
| |
| #include <sys/param.h> |
| #include <sys/types.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| #include <time.h> |
| |
| #include <nuttx/arch.h> |
| #include <nuttx/irq.h> |
| #include <nuttx/clock.h> |
| #include <nuttx/semaphore.h> |
| #include <nuttx/spinlock.h> |
| #include <nuttx/spi/spi.h> |
| #include <nuttx/spi/slave.h> |
| |
| #include <arch/board/board.h> |
| |
| #include "esp32_spi.h" |
| #include "esp32_gpio.h" |
| #include "esp32_irq.h" |
| #include "esp32_dma.h" |
| |
| #include "xtensa.h" |
| #include "hardware/esp32_gpio_sigmap.h" |
| #include "hardware/esp32_dport.h" |
| #include "hardware/esp32_spi.h" |
| #include "hardware/esp32_soc.h" |
| #include "hardware/esp32_pinmap.h" |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_ESP32_SPI2 |
| # if defined(CONFIG_ESP32_SPI2_SLAVE_IO_RW) |
| # define ESP32_SPI2_IO ESP32_SPI_IO_RW |
| # elif defined(CONFIG_ESP32_SPI2_SLAVE_IO_RO) |
| # define ESP32_SPI2_IO ESP32_SPI_IO_R |
| # elif defined(CONFIG_ESP32_SPI2_SLAVE_IO_WO) |
| # define ESP32_SPI2_IO ESP32_SPI_IO_W |
| # endif |
| #endif |
| |
| #ifdef CONFIG_ESP32_SPI3 |
| # if defined(CONFIG_ESP32_SPI3_SLAVE_IO_RW) |
| # define ESP32_SPI3_IO ESP32_SPI_IO_RW |
| # elif defined(CONFIG_ESP32_SPI3_SLAVE_IO_RO) |
| # define ESP32_SPI3_IO ESP32_SPI_IO_R |
| # elif defined(CONFIG_ESP32_SPI3_SLAVE_IO_WO) |
| # define ESP32_SPI3_IO ESP32_SPI_IO_W |
| # endif |
| #endif |
| |
| #if defined(CONFIG_ESP32_SPI2_SLAVE_IO_RW) || \ |
| defined(CONFIG_ESP32_SPI2_SLAVE_IO_WO) || \ |
| defined(CONFIG_ESP32_SPI3_SLAVE_IO_RW) || \ |
| defined(CONFIG_ESP32_SPI3_SLAVE_IO_WO) |
| # define ESP32_SPI_SLAVE_HAS_TX |
| #endif |
| |
| #define SPI_SLAVE_BUFSIZE (CONFIG_SPI_SLAVE_BUFSIZE) |
| |
| /* SPI DMA channel number */ |
| |
| #define SPI_DMA_CHANNEL_MAX (2) |
| |
| /* SPI DMA RX/TX description number */ |
| |
| #if SPI_SLAVE_BUFSIZE % ESP32_DMA_BUFLEN_MAX |
| # define SPI_DMADESC_NUM (SPI_SLAVE_BUFSIZE / ESP32_DMA_BUFLEN_MAX + 1) |
| #else |
| # define SPI_DMADESC_NUM (SPI_SLAVE_BUFSIZE / ESP32_DMA_BUFLEN_MAX) |
| #endif |
| |
| /* SPI DMA reset before exchange */ |
| |
| #define SPI_DMA_RESET_MASK (SPI_AHBM_RST_M | SPI_AHBM_FIFO_RST_M | \ |
| SPI_OUT_RST_M | SPI_IN_RST_M) |
| |
| #define WORDS2BYTES(_priv, _wn) (_wn * ((_priv)->nbits / 8)) |
| #define BYTES2WORDS(_priv, _bn) (_bn / ((_priv)->nbits / 8)) |
| |
| /* SPI Device hardware configuration */ |
| |
| struct esp32_spislv_config_s |
| { |
| uint32_t reg_base; /* SPI register base address */ |
| |
| enum spi_mode_e mode; /* SPI default mode */ |
| |
| uint8_t cs_pin; /* GPIO configuration for CS */ |
| uint8_t mosi_pin; /* GPIO configuration for MOSI */ |
| uint8_t miso_pin; /* GPIO configuration for MISO */ |
| uint8_t clk_pin; /* GPIO configuration for CLK */ |
| |
| uint8_t periph; /* peripher ID */ |
| uint8_t irq; /* Interrupt ID */ |
| |
| uint32_t clk_bit; /* Clock enable bit */ |
| uint32_t rst_bit; /* I2C reset bit */ |
| |
| bool use_dma; /* Use DMA */ |
| uint8_t dma_chan_s; /* DMA channel register shift */ |
| uint8_t dma_chan; /* DMA channel */ |
| uint32_t dma_clk_bit; /* DMA clock enable bit */ |
| uint32_t dma_rst_bit; /* DMA reset bit */ |
| |
| uint32_t cs_insig; /* SPI CS input signal index */ |
| uint32_t cs_outsig; /* SPI CS output signal index */ |
| uint32_t mosi_insig; /* SPI MOSI input signal index */ |
| uint32_t mosi_outsig; /* SPI MOSI output signal index */ |
| uint32_t miso_insig; /* SPI MISO input signal index */ |
| uint32_t miso_outsig; /* SPI MISO output signal index */ |
| uint32_t clk_insig; /* SPI CLK input signal index */ |
| uint32_t clk_outsig; /* SPI CLK output signal index */ |
| |
| uint32_t flags; /* SPI supports features */ |
| }; |
| |
| struct esp32_spislv_priv_s |
| { |
| /* Externally visible part of the SPI slave controller interface */ |
| |
| struct spi_slave_ctrlr_s ctrlr; |
| |
| /* Externally visible part of the SPI interface */ |
| |
| struct spi_slave_dev_s *dev; |
| |
| const struct esp32_spislv_config_s *config; /* Port configuration */ |
| |
| uint8_t cpu; /* CPU ID */ |
| int cpuint; /* SPI interrupt ID */ |
| |
| enum spi_mode_e mode; /* Actual SPI hardware mode */ |
| uint8_t nbits; /* Actual SPI send/receive bits once transmission */ |
| int refs; /* Check if it is initialized */ |
| |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| uint32_t txlen; /* Location of next RX value */ |
| |
| /* SPI slave TX queue buffer */ |
| |
| uint8_t txbuffer[SPI_SLAVE_BUFSIZE]; |
| |
| bool txen; /* Enable TX */ |
| #endif |
| |
| uint32_t rxlen; /* Location of next RX value */ |
| |
| /* SPI slave RX queue buffer */ |
| |
| uint8_t rxbuffer[SPI_SLAVE_BUFSIZE]; |
| |
| bool process; /* If SPI Slave process */ |
| |
| /* Copy from config to speed up checking */ |
| |
| bool dma_chan; |
| |
| spinlock_t lock; /* Device specific lock. */ |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static void esp32_spislv_setmode(struct spi_slave_ctrlr_s *ctrlr, |
| enum spi_mode_e mode); |
| static void esp32_spislv_setbits(struct spi_slave_ctrlr_s *ctrlr, |
| int nbits); |
| static int esp32_spislv_interrupt(int irq, void *context, void *arg); |
| static void esp32_spislv_initialize(struct spi_slave_ctrlr_s *ctrlr); |
| static void esp32_spislv_bind(struct spi_slave_ctrlr_s *ctrlr, |
| struct spi_slave_dev_s *dev, |
| enum spi_slave_mode_e mode, |
| int nbits); |
| static void esp32_spislv_unbind(struct spi_slave_ctrlr_s *ctrlr); |
| static int esp32_spislv_enqueue(struct spi_slave_ctrlr_s *ctrlr, |
| const void *data, |
| size_t nwords); |
| static bool esp32_spislv_qfull(struct spi_slave_ctrlr_s *ctrlr); |
| static void esp32_spislv_qflush(struct spi_slave_ctrlr_s *ctrlr); |
| static size_t esp32_spislv_qpoll(struct spi_slave_ctrlr_s *ctrlr); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_ESP32_SPI2 |
| static const struct esp32_spislv_config_s esp32_spi2_config = |
| { |
| .reg_base = REG_SPI_BASE(2), |
| .mode = SPIDEV_MODE0, |
| .cs_pin = CONFIG_ESP32_SPI2_CSPIN, |
| .mosi_pin = CONFIG_ESP32_SPI2_MOSIPIN, |
| .miso_pin = CONFIG_ESP32_SPI2_MISOPIN, |
| .clk_pin = CONFIG_ESP32_SPI2_CLKPIN, |
| .periph = ESP32_PERIPH_SPI2, |
| .irq = ESP32_IRQ_SPI2, |
| .clk_bit = DPORT_SPI_CLK_EN_2, |
| .rst_bit = DPORT_SPI_RST_2, |
| #ifdef CONFIG_ESP32_SPI2_DMA |
| .use_dma = true, |
| #else |
| .use_dma = false, |
| #endif |
| .dma_chan_s = 2, |
| .dma_chan = 1, |
| .dma_clk_bit = DPORT_SPI_DMA_CLK_EN, |
| .dma_rst_bit = DPORT_SPI_DMA_RST, |
| .cs_insig = HSPICS0_IN_IDX, |
| .cs_outsig = HSPICS0_OUT_IDX, |
| .mosi_insig = HSPID_IN_IDX, |
| .mosi_outsig = HSPID_OUT_IDX, |
| .miso_insig = HSPIQ_IN_IDX, |
| .miso_outsig = HSPIQ_OUT_IDX, |
| .clk_insig = HSPICLK_IN_IDX, |
| .clk_outsig = HSPICLK_OUT_IDX, |
| .flags = ESP32_SPI2_IO |
| }; |
| |
| static const struct spi_slave_ctrlrops_s esp32_spi2slv_ops = |
| { |
| .bind = esp32_spislv_bind, |
| .unbind = esp32_spislv_unbind, |
| .enqueue = esp32_spislv_enqueue, |
| .qfull = esp32_spislv_qfull, |
| .qflush = esp32_spislv_qflush, |
| .qpoll = esp32_spislv_qpoll |
| }; |
| |
| static struct esp32_spislv_priv_s esp32_spi2slv_priv = |
| { |
| .ctrlr = |
| { |
| .ops = &esp32_spi2slv_ops |
| }, |
| .config = &esp32_spi2_config, |
| .mode = SPIDEV_MODE3 |
| }; |
| #endif /* CONFIG_ESP32_SPI2 */ |
| |
| #ifdef CONFIG_ESP32_SPI3 |
| static const struct esp32_spislv_config_s esp32_spi3_config = |
| { |
| .reg_base = REG_SPI_BASE(3), |
| .mode = SPIDEV_MODE0, |
| .cs_pin = CONFIG_ESP32_SPI3_CSPIN, |
| .mosi_pin = CONFIG_ESP32_SPI3_MOSIPIN, |
| .miso_pin = CONFIG_ESP32_SPI3_MISOPIN, |
| .clk_pin = CONFIG_ESP32_SPI3_CLKPIN, |
| .periph = ESP32_PERIPH_SPI3, |
| .irq = ESP32_IRQ_SPI3, |
| .clk_bit = DPORT_SPI_CLK_EN, |
| .rst_bit = DPORT_SPI_RST, |
| #ifdef CONFIG_ESP32_SPI3_DMA |
| .use_dma = true, |
| #else |
| .use_dma = false, |
| #endif |
| .dma_chan_s = 4, |
| .dma_chan = 2, |
| .dma_clk_bit = DPORT_SPI_DMA_CLK_EN, |
| .dma_rst_bit = DPORT_SPI_DMA_RST, |
| .cs_insig = VSPICS0_IN_IDX, |
| .cs_outsig = VSPICS0_OUT_IDX, |
| .mosi_insig = VSPID_IN_IDX, |
| .mosi_outsig = VSPID_OUT_IDX, |
| .miso_insig = VSPIQ_IN_IDX, |
| .miso_outsig = VSPIQ_OUT_IDX, |
| .clk_insig = VSPICLK_IN_IDX, |
| .clk_outsig = VSPICLK_OUT_MUX_IDX, |
| .flags = ESP32_SPI3_IO |
| }; |
| |
| static const struct spi_slave_ctrlrops_s esp32_spi3slv_ops = |
| { |
| .bind = esp32_spislv_bind, |
| .unbind = esp32_spislv_unbind, |
| .enqueue = esp32_spislv_enqueue, |
| .qfull = esp32_spislv_qfull, |
| .qflush = esp32_spislv_qflush, |
| .qpoll = esp32_spislv_qpoll |
| }; |
| |
| static struct esp32_spislv_priv_s esp32_spi3slv_priv = |
| { |
| .ctrlr = |
| { |
| .ops = &esp32_spi3slv_ops |
| }, |
| .config = &esp32_spi3_config, |
| .mode = SPIDEV_MODE3 |
| }; |
| #endif /* CONFIG_ESP32_SPI3 */ |
| |
| /* SPI DMA RX/TX description */ |
| |
| struct esp32_dmadesc_s s_rx_desc[SPI_DMA_CHANNEL_MAX][SPI_DMADESC_NUM]; |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| struct esp32_dmadesc_s s_tx_desc[SPI_DMA_CHANNEL_MAX][SPI_DMADESC_NUM]; |
| #endif |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: esp32_spi_set_reg |
| * |
| * Description: |
| * Set the contents of the SPI register at offset |
| * |
| * Input Parameters: |
| * priv - Private SPI device structure |
| * offset - Offset to the register of interest |
| * value - Value to be written |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void esp32_spi_set_reg(struct esp32_spislv_priv_s *priv, |
| int offset, |
| uint32_t value) |
| { |
| putreg32(value, priv->config->reg_base + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_get_reg |
| * |
| * 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 register |
| * |
| ****************************************************************************/ |
| |
| static inline uint32_t esp32_spi_get_reg(struct esp32_spislv_priv_s *priv, |
| int offset) |
| { |
| return getreg32(priv->config->reg_base + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_set_regbits |
| * |
| * Description: |
| * Set the bits of the SPI register at offset |
| * |
| * Input Parameters: |
| * priv - Private SPI device structure |
| * offset - Offset to the register of interest |
| * bits - Bits to be set |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void esp32_spi_set_regbits(struct esp32_spislv_priv_s *priv, |
| int offset, |
| uint32_t bits) |
| { |
| uint32_t tmp = getreg32(priv->config->reg_base + offset); |
| |
| putreg32(tmp | bits, priv->config->reg_base + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_reset_regbits |
| * |
| * Description: |
| * Clear the bits of the SPI register at offset |
| * |
| * Input Parameters: |
| * priv - Private SPI device structure |
| * offset - Offset to the register of interest |
| * bits - Bits to be cleared |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void esp32_spi_reset_regbits(struct esp32_spislv_priv_s *priv, |
| int offset, |
| uint32_t bits) |
| { |
| uint32_t tmp = getreg32(priv->config->reg_base + offset); |
| |
| putreg32(tmp & (~bits), priv->config->reg_base + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_iomux |
| * |
| * Description: |
| * Check if the option SPI GPIO pins can use IOMUX directly |
| * |
| * Input Parameters: |
| * priv - Private SPI device structure |
| * |
| * Returned Value: |
| * True if can use IOMUX or false if can't. |
| * |
| ****************************************************************************/ |
| |
| static inline bool esp32_spi_iomux(struct esp32_spislv_priv_s *priv) |
| { |
| bool mapped = false; |
| const struct esp32_spislv_config_s *cfg = priv->config; |
| |
| if (REG_SPI_BASE(2) == cfg->reg_base) |
| { |
| if ((!(cfg->flags & ESP32_SPI_IO_W) || |
| cfg->mosi_pin == SPI2_IOMUX_MOSIPIN) && |
| |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| (!(cfg->flags & ESP32_SPI_IO_R) || |
| cfg->miso_pin == SPI2_IOMUX_MISOPIN) && |
| #endif |
| |
| cfg->cs_pin == SPI2_IOMUX_CSPIN && |
| cfg->clk_pin == SPI2_IOMUX_CLKPIN) |
| { |
| mapped = true; |
| } |
| } |
| else if (REG_SPI_BASE(3) == cfg->reg_base) |
| { |
| if ((!(cfg->flags & ESP32_SPI_IO_W) || |
| cfg->mosi_pin == SPI3_IOMUX_MOSIPIN) && |
| |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| (!(cfg->flags & ESP32_SPI_IO_R) || |
| cfg->miso_pin == SPI3_IOMUX_MISOPIN) && |
| #endif |
| |
| cfg->cs_pin == SPI3_IOMUX_CSPIN && |
| cfg->clk_pin == SPI3_IOMUX_CLKPIN) |
| { |
| mapped = true; |
| } |
| } |
| |
| return mapped; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_setmode |
| * |
| * Description: |
| * Set the SPI Slave mode. |
| * |
| * Input Parameters: |
| * ctrlr - SPI Slave controller interface instance |
| * mode - Requested SPI Slave mode |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spislv_setmode(struct spi_slave_ctrlr_s *ctrlr, |
| enum spi_mode_e mode) |
| { |
| uint32_t ck_idle_edge; |
| uint32_t ck_in_edge; |
| uint32_t miso_delay_mode; |
| uint32_t miso_delay_num; |
| uint32_t mosi_delay_mode; |
| uint32_t mosi_delay_num; |
| struct esp32_spislv_priv_s *priv = (struct esp32_spislv_priv_s *)ctrlr; |
| |
| spiinfo("mode=%d\n", mode); |
| |
| /* Has the mode changed? */ |
| |
| if (mode != priv->mode) |
| { |
| switch (mode) |
| { |
| case SPISLAVE_MODE0: /* CPOL=0; CPHA=0 */ |
| if (priv->dma_chan) |
| { |
| ck_idle_edge = 0; |
| ck_in_edge = 1; |
| miso_delay_mode = 0; |
| miso_delay_num = 1; |
| mosi_delay_mode = 0; |
| mosi_delay_num = 1; |
| } |
| else |
| { |
| ck_idle_edge = 1; |
| ck_in_edge = 0; |
| miso_delay_mode = 0; |
| miso_delay_num = 0; |
| mosi_delay_mode = 2; |
| mosi_delay_num = 2; |
| } |
| break; |
| |
| case SPISLAVE_MODE1: /* CPOL=0; CPHA=1 */ |
| ck_idle_edge = 1; |
| ck_in_edge = 1; |
| miso_delay_mode = 2; |
| miso_delay_num = 0; |
| mosi_delay_mode = 0; |
| mosi_delay_num = 0; |
| break; |
| |
| case SPISLAVE_MODE2: /* CPOL=1; CPHA=0 */ |
| if (priv->dma_chan) |
| { |
| ck_idle_edge = 1; |
| ck_in_edge = 0; |
| miso_delay_mode = 0; |
| miso_delay_num = 1; |
| mosi_delay_mode = 0; |
| mosi_delay_num = 1; |
| } |
| else |
| { |
| ck_idle_edge = 0; |
| ck_in_edge = 1; |
| miso_delay_mode = 0; |
| miso_delay_num = 0; |
| mosi_delay_mode = 1; |
| mosi_delay_num = 2; |
| } |
| break; |
| |
| case SPISLAVE_MODE3: /* CPOL=1; CPHA=1 */ |
| ck_idle_edge = 0; |
| ck_in_edge = 0; |
| miso_delay_mode = 1; |
| miso_delay_num = 0; |
| mosi_delay_mode = 0; |
| mosi_delay_num = 0; |
| break; |
| |
| default: |
| return; |
| } |
| |
| esp32_spi_reset_regbits(priv, |
| SPI_PIN_OFFSET, |
| SPI_CK_IDLE_EDGE_M); |
| esp32_spi_set_regbits(priv, |
| SPI_PIN_OFFSET, |
| (ck_idle_edge << SPI_CK_IDLE_EDGE_S)); |
| |
| esp32_spi_reset_regbits(priv, |
| SPI_USER_OFFSET, |
| SPI_CK_I_EDGE_M); |
| esp32_spi_set_regbits(priv, |
| SPI_USER_OFFSET, |
| (ck_in_edge << SPI_CK_I_EDGE_S)); |
| |
| esp32_spi_reset_regbits(priv, |
| SPI_CTRL2_OFFSET, |
| SPI_MISO_DELAY_MODE_M | |
| SPI_MISO_DELAY_NUM_M | |
| SPI_MOSI_DELAY_NUM_M | |
| SPI_MOSI_DELAY_MODE_M); |
| esp32_spi_set_regbits(priv, |
| SPI_CTRL2_OFFSET, |
| (miso_delay_mode << SPI_MISO_DELAY_MODE_S) | |
| (miso_delay_num << SPI_MISO_DELAY_NUM_S) | |
| (mosi_delay_mode << SPI_MOSI_DELAY_MODE_S) | |
| (mosi_delay_num << SPI_MOSI_DELAY_NUM_S)); |
| |
| priv->mode = mode; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_setbits |
| * |
| * Description: |
| * Set the number of bits per word. |
| * |
| * Input Parameters: |
| * ctrlr - SPI Slave controller interface instance |
| * nbits - The number of bits in an SPI word |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spislv_setbits(struct spi_slave_ctrlr_s *ctrlr, |
| int nbits) |
| { |
| struct esp32_spislv_priv_s *priv = (struct esp32_spislv_priv_s *)ctrlr; |
| |
| spiinfo("nbits=%d\n", nbits); |
| |
| if (nbits != priv->nbits) |
| { |
| priv->nbits = nbits; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_io_interrupt |
| * |
| * Description: |
| * Common I/O interrupt handler |
| * |
| * Input Parameters: |
| * arg - I/O controller private data |
| * |
| * Returned Value: |
| * Standard interrupt return value. |
| * |
| ****************************************************************************/ |
| |
| static int esp32_io_interrupt(int irq, void *context, void *arg) |
| { |
| struct esp32_spislv_priv_s *priv = (struct esp32_spislv_priv_s *)arg; |
| |
| if (priv->process == true) |
| { |
| priv->process = false; |
| SPIS_DEV_SELECT(priv->dev, false); |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_tx |
| * |
| * Description: |
| * Process SPI slave TX. |
| * |
| * DMA mode : Initialize register to prepare for TX |
| * Non-DMA mode: Fill data to TX register |
| * |
| * Input Parameters: |
| * priv - Private SPI device structure |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| static void esp32_spislv_tx(struct esp32_spislv_priv_s *priv) |
| { |
| int i; |
| uint32_t regval; |
| |
| if (priv->dma_chan) |
| { |
| esp32_dma_init(s_tx_desc[priv->dma_chan - 1], SPI_DMADESC_NUM, |
| priv->txbuffer, priv->txlen); |
| |
| regval = (uint32_t)s_tx_desc[priv->dma_chan - 1] & SPI_OUTLINK_ADDR_V; |
| esp32_spi_set_reg(priv, SPI_DMA_OUT_LINK_OFFSET, |
| regval | SPI_OUTLINK_START_M); |
| esp32_spi_set_reg(priv, SPI_SLV_WRBUF_DLEN_OFFSET, |
| priv->txlen * 8 - 1); |
| |
| esp32_spi_set_regbits(priv, SPI_USER_OFFSET, SPI_USR_MISO_M); |
| } |
| else |
| { |
| for (i = 0; i < priv->txlen; i += 4) |
| { |
| esp32_spi_set_reg(priv, SPI_W8_OFFSET + i, |
| *(uint32_t *)(&priv->txbuffer[i])); |
| } |
| |
| esp32_spi_set_regbits(priv, SPI_USER_OFFSET, SPI_USR_MISO_M); |
| } |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_rx |
| * |
| * Description: |
| * Process SPI slave RX. Process SPI slave device receive callback by |
| * calling SPIS_DEV_RECEIVE and prepare for next RX. |
| * |
| * DMA mode : Initialize register to prepare for RX |
| * |
| * Input Parameters: |
| * priv - Private SPI device structure |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spislv_rx(struct esp32_spislv_priv_s *priv) |
| { |
| uint32_t tmp; |
| uint32_t recv_n; |
| uint32_t regval; |
| |
| tmp = SPIS_DEV_RECEIVE(priv->dev, priv->rxbuffer, |
| BYTES2WORDS(priv, priv->rxlen)); |
| recv_n = WORDS2BYTES(priv, tmp); |
| |
| if (priv->dma_chan) |
| { |
| DEBUGASSERT((recv_n % 4) == 0); |
| } |
| |
| if (recv_n < priv->rxlen) |
| { |
| /* If upper layer does not receive all data of receive |
| * buffer, move the rest data to head of the buffer |
| */ |
| |
| priv->rxlen -= recv_n; |
| memmove(priv->rxbuffer, priv->rxbuffer + recv_n, priv->rxlen); |
| } |
| else |
| { |
| priv->rxlen = 0; |
| } |
| |
| if (priv->dma_chan) |
| { |
| tmp = SPI_SLAVE_BUFSIZE - priv->rxlen; |
| if (tmp) |
| { |
| /* Start to receive next block of data */ |
| |
| esp32_dma_init(s_rx_desc[priv->dma_chan - 1], SPI_DMADESC_NUM, |
| priv->rxbuffer + priv->rxlen, tmp); |
| |
| regval = (uint32_t)s_rx_desc[priv->dma_chan - 1] & |
| SPI_INLINK_ADDR_V; |
| esp32_spi_set_reg(priv, SPI_DMA_IN_LINK_OFFSET, |
| regval | SPI_INLINK_START_M); |
| esp32_spi_set_reg(priv, SPI_SLV_RDBUF_DLEN_OFFSET, tmp * 8 - 1); |
| |
| esp32_spi_set_regbits(priv, SPI_USER_OFFSET, SPI_USR_MOSI_M); |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_interrupt |
| * |
| * Description: |
| * Common SPI interrupt handler |
| * |
| * Input Parameters: |
| * arg - SPI controller private data |
| * |
| * Returned Value: |
| * Standard interrupt return value. |
| * |
| ****************************************************************************/ |
| |
| static int esp32_spislv_interrupt(int irq, void *context, void *arg) |
| { |
| struct esp32_spislv_priv_s *priv = (struct esp32_spislv_priv_s *)arg; |
| uint32_t n; |
| uint32_t tmp; |
| int i; |
| |
| esp32_spi_reset_regbits(priv, SPI_SLAVE_OFFSET, SPI_TRANS_DONE_M); |
| |
| if (priv->dma_chan) |
| { |
| esp32_spi_set_regbits(priv, SPI_DMA_CONF_OFFSET, SPI_DMA_RESET_MASK); |
| esp32_spi_reset_regbits(priv, SPI_DMA_CONF_OFFSET, SPI_DMA_RESET_MASK); |
| } |
| |
| if (priv->process == false) |
| { |
| SPIS_DEV_SELECT(priv->dev, true); |
| priv->process = true; |
| } |
| |
| /* Read and calculate read bytes */ |
| |
| n = (esp32_spi_get_reg(priv, SPI_SLV_RD_BIT_OFFSET) + 1) / 8; |
| |
| /* RX process */ |
| |
| if (!priv->dma_chan) |
| { |
| /* With DMA, software should copy data from register |
| * to receive buffer |
| */ |
| |
| for (i = 0; i < n; i += 4) |
| { |
| tmp = esp32_spi_get_reg(priv, SPI_W0_OFFSET + i); |
| memcpy(priv->rxbuffer + priv->rxlen + i, &tmp, 4); |
| } |
| } |
| |
| priv->rxlen += n; |
| |
| esp32_spislv_rx(priv); |
| |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| /* TX process */ |
| |
| if (priv->txen) |
| { |
| if (n < priv->txlen) |
| { |
| priv->txlen -= n; |
| memmove(priv->txbuffer, priv->txbuffer + n, priv->txlen); |
| } |
| else |
| { |
| priv->txlen = 0; |
| priv->txen = false; |
| } |
| } |
| |
| if (priv->txlen) |
| { |
| esp32_spislv_tx(priv); |
| priv->txen = true; |
| } |
| #endif |
| |
| if (priv->process == true && esp32_gpioread(priv->config->cs_pin)) |
| { |
| priv->process = false; |
| SPIS_DEV_SELECT(priv->dev, false); |
| |
| esp32_spi_reset_regbits(priv, SPI_SLAVE_OFFSET, SPI_TRANS_DONE_M); |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_initialize |
| * |
| * Description: |
| * Initialize ESP32 SPI Slave hardware interface |
| * |
| * Input Parameters: |
| * ctrlr - SPI Slave controller interface instance |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spislv_initialize(struct spi_slave_ctrlr_s *ctrlr) |
| { |
| struct esp32_spislv_priv_s *priv = (struct esp32_spislv_priv_s *)ctrlr; |
| const struct esp32_spislv_config_s *config = priv->config; |
| uint32_t regval; |
| |
| esp32_gpiowrite(config->cs_pin, 1); |
| esp32_gpiowrite(config->clk_pin, 1); |
| |
| if (config->flags & ESP32_SPI_IO_R) |
| { |
| esp32_gpiowrite(config->mosi_pin, 1); |
| } |
| |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| if (config->flags & ESP32_SPI_IO_W) |
| { |
| esp32_gpiowrite(config->miso_pin, 1); |
| } |
| #endif |
| |
| if (esp32_spi_iomux(priv)) |
| { |
| esp32_configgpio(config->cs_pin, INPUT_FUNCTION_2 | PULLUP); |
| esp32_configgpio(config->clk_pin, INPUT_FUNCTION_2 | PULLUP); |
| |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| if (config->flags & ESP32_SPI_IO_W) |
| { |
| esp32_configgpio(config->miso_pin, OUTPUT_FUNCTION_2); |
| } |
| #endif |
| |
| if (config->flags & ESP32_SPI_IO_R) |
| { |
| esp32_configgpio(config->mosi_pin, INPUT_FUNCTION_2 | PULLUP); |
| } |
| } |
| else |
| { |
| esp32_configgpio(config->cs_pin, INPUT_FUNCTION_3 | PULLUP); |
| esp32_gpio_matrix_out(config->cs_pin, config->cs_outsig, 0, 0); |
| esp32_gpio_matrix_in(config->cs_pin, config->cs_insig, 0); |
| |
| esp32_configgpio(config->clk_pin, INPUT_FUNCTION_3 | PULLUP); |
| esp32_gpio_matrix_out(config->clk_pin, config->clk_outsig, 0, 0); |
| esp32_gpio_matrix_in(config->clk_pin, config->clk_insig, 0); |
| |
| if (config->flags & ESP32_SPI_IO_R) |
| { |
| esp32_configgpio(config->mosi_pin, INPUT_FUNCTION_3 | PULLUP); |
| esp32_gpio_matrix_out(config->mosi_pin, config->mosi_outsig, 0, 0); |
| esp32_gpio_matrix_in(config->mosi_pin, config->mosi_insig, 0); |
| } |
| |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| if (config->flags & ESP32_SPI_IO_W) |
| { |
| esp32_configgpio(config->miso_pin, OUTPUT_FUNCTION_3); |
| esp32_gpio_matrix_out(config->miso_pin, config->miso_outsig, 0, 0); |
| esp32_gpio_matrix_in(config->miso_pin, config->miso_insig, 0); |
| } |
| #endif |
| } |
| |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, config->clk_bit); |
| modifyreg32(DPORT_PERIP_RST_EN_REG, config->rst_bit, 0); |
| |
| esp32_spi_set_reg(priv, SPI_USER_OFFSET, SPI_DOUTDIN_M | |
| SPI_USR_MOSI_M | |
| SPI_USR_MISO_M | |
| SPI_USR_MISO_HIGHPART_M); |
| esp32_spi_set_reg(priv, SPI_USER1_OFFSET, 0); |
| esp32_spi_set_reg(priv, SPI_PIN_OFFSET, SPI_CS1_DIS_M | SPI_CS2_DIS_M); |
| esp32_spi_set_reg(priv, SPI_CTRL_OFFSET, 0); |
| esp32_spi_set_reg(priv, SPI_CTRL2_OFFSET, 0); |
| esp32_spi_set_reg(priv, SPI_USER2_OFFSET, 0); |
| esp32_spi_set_reg(priv, SPI_CLOCK_OFFSET, 0); |
| esp32_spi_set_reg(priv, SPI_SLAVE_OFFSET, SPI_SLAVE_MODE_M | |
| SPI_SLV_WR_RD_BUF_EN_M | |
| SPI_INT_EN_M); |
| |
| if (priv->dma_chan) |
| { |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, config->dma_clk_bit); |
| modifyreg32(DPORT_PERIP_RST_EN_REG, config->dma_rst_bit, 0); |
| |
| modifyreg32(DPORT_SPI_DMA_CHAN_SEL_REG, 0, |
| (config->dma_chan << config->dma_chan_s)); |
| |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| esp32_spi_set_reg(priv, SPI_DMA_CONF_OFFSET, SPI_OUT_DATA_BURST_EN_M | |
| SPI_INDSCR_BURST_EN_M | |
| SPI_OUTDSCR_BURST_EN_M); |
| #else |
| esp32_spi_set_reg(priv, SPI_DMA_CONF_OFFSET, SPI_INDSCR_BURST_EN_M); |
| #endif |
| |
| esp32_dma_init(s_rx_desc[priv->dma_chan - 1], SPI_DMADESC_NUM, |
| priv->rxbuffer, SPI_SLAVE_BUFSIZE); |
| |
| regval = (uint32_t)s_rx_desc[priv->dma_chan - 1] & SPI_INLINK_ADDR_V; |
| esp32_spi_set_reg(priv, SPI_DMA_IN_LINK_OFFSET, |
| regval | SPI_INLINK_START_M); |
| esp32_spi_set_reg(priv, SPI_SLV_RDBUF_DLEN_OFFSET, |
| SPI_SLAVE_BUFSIZE * 8 - 1); |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| esp32_spi_set_reg(priv, SPI_SLV_WRBUF_DLEN_OFFSET, |
| SPI_SLAVE_BUFSIZE * 8 - 1); |
| #endif |
| esp32_spi_set_regbits(priv, SPI_USER_OFFSET, SPI_USR_MOSI_M); |
| } |
| else |
| { |
| /* TX/RX hardware fill can cache 4 words = 32 bytes = 256 bits */ |
| |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| esp32_spi_set_reg(priv, SPI_SLV_WRBUF_DLEN_OFFSET, 256 - 1); |
| #endif |
| esp32_spi_set_reg(priv, SPI_SLV_RDBUF_DLEN_OFFSET, 256 - 1); |
| } |
| |
| esp32_spislv_setmode(ctrlr, config->mode); |
| esp32_spislv_setbits(ctrlr, 8); |
| |
| esp32_spi_set_regbits(priv, SPI_SLAVE_OFFSET, SPI_SYNC_RESET_M); |
| esp32_spi_reset_regbits(priv, SPI_SLAVE_OFFSET, SPI_SYNC_RESET_M); |
| |
| esp32_gpioirqenable(ESP32_PIN2IRQ(config->cs_pin), RISING); |
| |
| esp32_spi_reset_regbits(priv, SPI_SLAVE_OFFSET, SPI_TRANS_DONE_M); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_deinit |
| * |
| * Description: |
| * Deinitialize ESP32 SPI Slave hardware interface |
| * |
| * Input Parameters: |
| * ctrlr - SPI Slave controller interface instance |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spislv_deinit(struct spi_slave_ctrlr_s *ctrlr) |
| { |
| struct esp32_spislv_priv_s *priv = (struct esp32_spislv_priv_s *)ctrlr; |
| |
| esp32_gpioirqdisable(ESP32_PIN2IRQ(priv->config->cs_pin)); |
| esp32_spi_reset_regbits(priv, SPI_SLAVE_OFFSET, SPI_INT_EN_M); |
| |
| if (priv->dma_chan) |
| { |
| modifyreg32(DPORT_PERIP_RST_EN_REG, 0, priv->config->dma_rst_bit); |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, priv->config->dma_clk_bit, 0); |
| } |
| |
| modifyreg32(DPORT_PERIP_RST_EN_REG, 0, priv->config->clk_bit); |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, priv->config->clk_bit, 0); |
| |
| priv->mode = SPIDEV_MODE0; |
| priv->nbits = 0; |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| priv->txlen = 0; |
| priv->txen = false; |
| #endif |
| priv->rxlen = 0; |
| priv->process = false; |
| priv->dma_chan = false; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_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 |
| * |
| * Returned Value: |
| * none |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spislv_bind(struct spi_slave_ctrlr_s *ctrlr, |
| struct spi_slave_dev_s *dev, |
| enum spi_slave_mode_e mode, |
| int nbits) |
| { |
| struct esp32_spislv_priv_s *priv = (struct esp32_spislv_priv_s *)ctrlr; |
| irqstate_t flags; |
| |
| spiinfo("dev=%p mode=%d nbits=%d\n", dev, mode, nbits); |
| |
| DEBUGASSERT(priv != NULL && priv->dev == NULL && dev != NULL); |
| |
| flags = spin_lock_irqsave(&priv->lock); |
| |
| priv->dev = dev; |
| |
| SPIS_DEV_SELECT(dev, false); |
| |
| SPIS_DEV_CMDDATA(dev, false); |
| |
| priv->rxlen = 0; |
| |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| priv->txlen = 0; |
| priv->txen = false; |
| #endif |
| |
| esp32_spislv_initialize(ctrlr); |
| |
| esp32_spislv_setmode(ctrlr, mode); |
| esp32_spislv_setbits(ctrlr, nbits); |
| |
| up_enable_irq(priv->config->irq); |
| |
| esp32_spi_set_regbits(priv, SPI_CMD_OFFSET, SPI_USR_M); |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_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 esp32_spislv_unbind(struct spi_slave_ctrlr_s *ctrlr) |
| { |
| struct esp32_spislv_priv_s *priv = (struct esp32_spislv_priv_s *)ctrlr; |
| irqstate_t flags; |
| |
| DEBUGASSERT(priv != NULL); |
| |
| spiinfo("Unbinding %p\n", priv->dev); |
| |
| DEBUGASSERT(priv->dev != NULL); |
| |
| flags = spin_lock_irqsave(&priv->lock); |
| |
| up_disable_irq(priv->config->irq); |
| |
| esp32_gpioirqdisable(ESP32_PIN2IRQ(priv->config->cs_pin)); |
| esp32_spi_reset_regbits(priv, SPI_SLAVE_OFFSET, SPI_INT_EN_M); |
| if (priv->dma_chan) |
| { |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, priv->config->dma_clk_bit, 0); |
| } |
| |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, priv->config->clk_bit, 0); |
| |
| priv->dev = NULL; |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_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 anyin-process or currently "committed" transfers |
| * |
| * Input Parameters: |
| * ctrlr - SPI slave controller interface instance |
| * data - Command/data mode data value to be shifted out. The width of |
| * the data must be the same as the nbits parameter previously |
| * provided to the bind() methods. |
| * |
| * 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 esp32_spislv_enqueue(struct spi_slave_ctrlr_s *ctrlr, |
| const void *data, |
| size_t nwords) |
| { |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| struct esp32_spislv_priv_s *priv = (struct esp32_spislv_priv_s *)ctrlr; |
| size_t n = WORDS2BYTES(priv, nwords); |
| size_t bufsize; |
| irqstate_t flags; |
| int ret; |
| |
| spiinfo("spi_enqueue(ctrlr=%p, data=%p, nwords=%d)\n", |
| ctrlr, data, nwords); |
| DEBUGASSERT(priv != NULL && priv->dev != NULL); |
| |
| flags = spin_lock_irqsave(&priv->lock); |
| |
| bufsize = SPI_SLAVE_BUFSIZE - priv->txlen; |
| if (!bufsize) |
| { |
| ret = -ENOSPC; |
| } |
| else |
| { |
| n = MIN(n, bufsize); |
| memcpy(priv->txbuffer + priv->txlen, data, n); |
| priv->txlen += n; |
| ret = OK; |
| |
| if (priv->process == false) |
| { |
| esp32_spislv_tx(priv); |
| priv->txen = true; |
| } |
| } |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| |
| return ret; |
| #else |
| return -ENOSYS; |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_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 wueue is full |
| * |
| ****************************************************************************/ |
| |
| static bool esp32_spislv_qfull(struct spi_slave_ctrlr_s *ctrlr) |
| { |
| struct esp32_spislv_priv_s *priv = (struct esp32_spislv_priv_s *)ctrlr; |
| irqstate_t flags; |
| bool ret = 0; |
| |
| DEBUGASSERT(priv != NULL && priv->dev != NULL); |
| |
| spiinfo("spi_qfull(ctrlr=%p)\n", ctrlr); |
| |
| flags = spin_lock_irqsave(&priv->lock); |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| ret = priv->txlen == SPI_SLAVE_BUFSIZE; |
| #else |
| ret = false; |
| #endif |
| spin_unlock_irqrestore(&priv->lock, flags); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_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 esp32_spislv_qflush(struct spi_slave_ctrlr_s *ctrlr) |
| { |
| struct esp32_spislv_priv_s *priv = (struct esp32_spislv_priv_s *)ctrlr; |
| irqstate_t flags; |
| |
| DEBUGASSERT(priv != NULL && priv->dev != NULL); |
| |
| flags = spin_lock_irqsave(&priv->lock); |
| priv->rxlen = 0; |
| #ifdef ESP32_SPI_SLAVE_HAS_TX |
| priv->txlen = 0; |
| priv->txen = false; |
| #endif |
| spin_unlock_irqrestore(&priv->lock, flags); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_qpoll |
| * |
| * Description: |
| * Tell the controller to output all the receive queue data. |
| * |
| * Input Parameters: |
| * ctrlr - SPI slave controller interface instance |
| * |
| * Returned Value: |
| * Number of units of width "nbits" left in the rx queue. If the device |
| * accepted all the data, the return value will be 0 |
| * |
| ****************************************************************************/ |
| |
| static size_t esp32_spislv_qpoll(struct spi_slave_ctrlr_s *ctrlr) |
| { |
| struct esp32_spislv_priv_s *priv = (struct esp32_spislv_priv_s *)ctrlr; |
| irqstate_t flags; |
| uint32_t n; |
| |
| DEBUGASSERT(priv != NULL && priv->dev != NULL); |
| |
| flags = spin_lock_irqsave(&priv->lock); |
| |
| esp32_spislv_rx(priv); |
| n = priv->rxlen; |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| |
| return n; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_ctrlr_initialize |
| * |
| * Description: |
| * Initialize the selected SPI slave bus |
| * |
| * Input Parameters: |
| * Port number (for hardware that has multiple SPI slave interfaces) |
| * |
| * Returned Value: |
| * Valid SPI slave device structure reference on success; a NULL on failure |
| * |
| ****************************************************************************/ |
| |
| struct spi_slave_ctrlr_s *esp32_spislv_ctrlr_initialize(int port) |
| { |
| int ret; |
| struct spi_slave_ctrlr_s *spislv_dev; |
| struct esp32_spislv_priv_s *priv; |
| irqstate_t flags; |
| |
| switch (port) |
| { |
| #ifdef CONFIG_ESP32_SPI2 |
| case 2: |
| priv = &esp32_spi2slv_priv; |
| break; |
| #endif |
| #ifdef CONFIG_ESP32_SPI3 |
| case 3: |
| priv = &esp32_spi3slv_priv; |
| break; |
| #endif |
| default: |
| return NULL; |
| } |
| |
| spislv_dev = (struct spi_slave_ctrlr_s *)priv; |
| |
| flags = spin_lock_irqsave(&priv->lock); |
| |
| if ((volatile int)priv->refs != 0) |
| { |
| spin_unlock_irqrestore(&priv->lock, flags); |
| |
| return spislv_dev; |
| } |
| |
| if (priv->config->use_dma) |
| { |
| priv->dma_chan = priv->config->dma_chan; |
| } |
| else |
| { |
| priv->dma_chan = 0; |
| } |
| |
| DEBUGVERIFY(irq_attach(ESP32_PIN2IRQ(priv->config->cs_pin), |
| esp32_io_interrupt, |
| priv)); |
| |
| /* Set up to receive peripheral interrupts on the current CPU */ |
| |
| priv->cpu = up_cpu_index(); |
| priv->cpuint = esp32_setup_irq(priv->cpu, priv->config->periph, |
| 1, ESP32_CPUINT_LEVEL); |
| |
| ret = irq_attach(priv->config->irq, esp32_spislv_interrupt, priv); |
| if (ret != OK) |
| { |
| esp32_teardown_irq(priv->cpu, priv->config->periph, priv->cpuint); |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| |
| return NULL; |
| } |
| |
| priv->refs++; |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| |
| return spislv_dev; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spislv_ctrlr_uninitialize |
| * |
| * Description: |
| * Uninitialize an SPI slave bus |
| * |
| * Input Parameters: |
| * ctrlr - SPI slave controller interface instance |
| * |
| * Returned Value: |
| * OK if success or fail |
| * |
| ****************************************************************************/ |
| |
| int esp32_spislv_ctrlr_uninitialize(struct spi_slave_ctrlr_s *ctrlr) |
| { |
| irqstate_t flags; |
| struct esp32_spislv_priv_s *priv = (struct esp32_spislv_priv_s *)ctrlr; |
| |
| DEBUGASSERT(ctrlr); |
| |
| if (priv->refs == 0) |
| { |
| return ERROR; |
| } |
| |
| flags = spin_lock_irqsave(&priv->lock); |
| |
| if (--priv->refs) |
| { |
| spin_unlock_irqrestore(&priv->lock, flags); |
| return OK; |
| } |
| |
| up_disable_irq(priv->config->irq); |
| esp32_teardown_irq(priv->cpu, priv->config->periph, priv->cpuint); |
| esp32_spislv_deinit(ctrlr); |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| |
| return OK; |
| } |
| |
| #endif /* CONFIG_ESP32_SPI */ |