| /**************************************************************************** |
| * arch/xtensa/src/esp32/esp32_spi.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 <debug.h> |
| #include <sys/param.h> |
| #include <sys/types.h> |
| #include <inttypes.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <stdlib.h> |
| #include <time.h> |
| #include <assert.h> |
| |
| #include <nuttx/arch.h> |
| #include <nuttx/irq.h> |
| #include <nuttx/clock.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/semaphore.h> |
| #include <nuttx/kmalloc.h> |
| #include <nuttx/spi/spi.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" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_ESP32_SPI2 |
| # if defined(CONFIG_ESP32_SPI2_MASTER_IO_RW) |
| # define ESP32_SPI2_IO ESP32_SPI_IO_RW |
| # elif defined(CONFIG_ESP32_SPI2_MASTER_IO_RO) |
| # define ESP32_SPI2_IO ESP32_SPI_IO_R |
| # elif defined(CONFIG_ESP32_SPI2_MASTER_IO_WO) |
| # define ESP32_SPI2_IO ESP32_SPI_IO_W |
| # endif |
| #endif |
| |
| #ifdef CONFIG_ESP32_SPI3 |
| # if defined(CONFIG_ESP32_SPI3_MASTER_IO_RW) |
| # define ESP32_SPI3_IO ESP32_SPI_IO_RW |
| # elif defined(CONFIG_ESP32_SPI3_MASTER_IO_RO) |
| # define ESP32_SPI3_IO ESP32_SPI_IO_R |
| # elif defined(CONFIG_ESP32_SPI3_MASTER_IO_WO) |
| # define ESP32_SPI3_IO ESP32_SPI_IO_W |
| # endif |
| #endif |
| |
| /* SPI DMA RX/TX description number */ |
| |
| #define SPI_DMADESC_NUM (CONFIG_SPI_DMADESC_NUM) |
| |
| /* SPI DMA channel number */ |
| |
| #define SPI_DMA_CHANNEL_MAX (2) |
| |
| /* 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) |
| |
| /* SPI Default speed (limited by clock divider) */ |
| |
| #define SPI_FREQ_DEFAULT (400000) |
| |
| /* Helper for applying the mask for a given register field. |
| * Mask is determined by the macros suffixed with _V and _S from the |
| * peripheral register description. |
| */ |
| |
| #define VALUE_MASK(_val, _field) ((_val & (_field##_V)) << (_field##_S)) |
| |
| /* SPI Maximum buffer size in bytes */ |
| |
| #define SPI_MAX_BUF_SIZE (64) |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* SPI Device hardware configuration */ |
| |
| struct esp32_spi_config_s |
| { |
| uint32_t id; /* SPI instance */ |
| |
| uint32_t clk_freq; /* SPI clock frequency */ |
| 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; /* SPI 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_spi_priv_s |
| { |
| /* Externally visible part of the SPI interface */ |
| |
| struct spi_dev_s spi_dev; |
| |
| /* Port configuration */ |
| |
| const struct esp32_spi_config_s *config; |
| |
| int refs; /* Reference count */ |
| |
| /* Held while chip is selected for mutual exclusion */ |
| |
| mutex_t lock; |
| |
| /* Interrupt wait semaphore */ |
| |
| sem_t sem_isr; |
| |
| int cpuint; /* SPI interrupt ID */ |
| uint8_t cpu; /* CPU ID */ |
| |
| uint32_t frequency; /* Requested clock frequency */ |
| uint32_t actual; /* Actual clock frequency */ |
| |
| enum spi_mode_e mode; /* Actual SPI hardware mode */ |
| |
| /* Actual SPI send/receive bits once transmission */ |
| |
| uint8_t nbits; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static int esp32_spi_lock(struct spi_dev_s *dev, bool lock); |
| #ifndef CONFIG_ESP32_SPI_UDCS |
| static void esp32_spi_select(struct spi_dev_s *dev, |
| uint32_t devid, bool selected); |
| #endif |
| static uint32_t esp32_spi_setfrequency(struct spi_dev_s *dev, |
| uint32_t frequency); |
| static void esp32_spi_setmode(struct spi_dev_s *dev, |
| enum spi_mode_e mode); |
| static void esp32_spi_setbits(struct spi_dev_s *dev, int nbits); |
| #ifdef CONFIG_SPI_HWFEATURES |
| static int esp32_spi_hwfeatures(struct spi_dev_s *dev, |
| spi_hwfeatures_t features); |
| #endif |
| static uint32_t esp32_spi_send(struct spi_dev_s *dev, uint32_t wd); |
| static void esp32_spi_exchange(struct spi_dev_s *dev, |
| const void *txbuffer, |
| void *rxbuffer, size_t nwords); |
| #ifndef CONFIG_SPI_EXCHANGE |
| static void esp32_spi_sndblock(struct spi_dev_s *dev, |
| const void *txbuffer, |
| size_t nwords); |
| static void esp32_spi_recvblock(struct spi_dev_s *dev, |
| void *rxbuffer, |
| size_t nwords); |
| #endif |
| #ifdef CONFIG_SPI_TRIGGER |
| static int esp32_spi_trigger(struct spi_dev_s *dev); |
| #endif |
| static void esp32_spi_init(struct spi_dev_s *dev); |
| static void esp32_spi_deinit(struct spi_dev_s *dev); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_ESP32_SPI2 |
| static const struct esp32_spi_config_s esp32_spi2_config = |
| { |
| .id = 2, |
| .clk_freq = SPI_FREQ_DEFAULT, |
| .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_ops_s esp32_spi2_ops = |
| { |
| .lock = esp32_spi_lock, |
| #ifdef CONFIG_ESP32_SPI_UDCS |
| .select = esp32_spi2_select, |
| #else |
| .select = esp32_spi_select, |
| #endif |
| .setfrequency = esp32_spi_setfrequency, |
| .setmode = esp32_spi_setmode, |
| .setbits = esp32_spi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = esp32_spi_hwfeatures, |
| #endif |
| .status = esp32_spi2_status, |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = esp32_spi2_cmddata, |
| #endif |
| .send = esp32_spi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = esp32_spi_exchange, |
| #else |
| .sndblock = esp32_spi_sndblock, |
| .recvblock = esp32_spi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_TRIGGER |
| .trigger = esp32_spi_trigger, |
| #endif |
| .registercallback = NULL, |
| }; |
| |
| static struct esp32_spi_priv_s esp32_spi2_priv = |
| { |
| .spi_dev = |
| { |
| .ops = &esp32_spi2_ops |
| }, |
| .config = &esp32_spi2_config, |
| .lock = NXMUTEX_INITIALIZER, |
| .sem_isr = SEM_INITIALIZER(0), |
| }; |
| #endif /* CONFIG_ESP32_SPI2 */ |
| |
| #ifdef CONFIG_ESP32_SPI3 |
| static const struct esp32_spi_config_s esp32_spi3_config = |
| { |
| .id = 3, |
| .clk_freq = SPI_FREQ_DEFAULT, |
| .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_ops_s esp32_spi3_ops = |
| { |
| .lock = esp32_spi_lock, |
| #ifdef CONFIG_ESP32_SPI_UDCS |
| .select = esp32_spi3_select, |
| #else |
| .select = esp32_spi_select, |
| #endif |
| .setfrequency = esp32_spi_setfrequency, |
| .setmode = esp32_spi_setmode, |
| .setbits = esp32_spi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = esp32_spi_hwfeatures, |
| #endif |
| .status = esp32_spi3_status, |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = esp32_spi3_cmddata, |
| #endif |
| .send = esp32_spi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = esp32_spi_exchange, |
| #else |
| .sndblock = esp32_spi_sndblock, |
| .recvblock = esp32_spi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_TRIGGER |
| .trigger = esp32_spi_trigger, |
| #endif |
| .registercallback = NULL, |
| }; |
| |
| static struct esp32_spi_priv_s esp32_spi3_priv = |
| { |
| .spi_dev = |
| { |
| .ops = &esp32_spi3_ops |
| }, |
| .config = &esp32_spi3_config, |
| .lock = NXMUTEX_INITIALIZER, |
| .sem_isr = SEM_INITIALIZER(0), |
| }; |
| #endif /* CONFIG_ESP32_SPI3 */ |
| |
| /* SPI DMA RX/TX description */ |
| |
| struct esp32_dmadesc_s s_dma_rxdesc[SPI_DMA_CHANNEL_MAX][SPI_DMADESC_NUM]; |
| struct esp32_dmadesc_s s_dma_txdesc[SPI_DMA_CHANNEL_MAX][SPI_DMADESC_NUM]; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: esp32_spi_set_regbits |
| * |
| * Description: |
| * Set the bits of the SPI register |
| * |
| * Input Parameters: |
| * addr - Address of the register of interest |
| * bits - Bits to be set |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void esp32_spi_set_regbits(uint32_t addr, uint32_t bits) |
| { |
| uint32_t tmp = getreg32(addr); |
| |
| putreg32(tmp | bits, addr); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_reset_regbits |
| * |
| * Description: |
| * Clear the bits of the SPI register |
| * |
| * Input Parameters: |
| * addr - Address of the register of interest |
| * bits - Bits to be cleared |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void esp32_spi_reset_regbits(uint32_t addr, uint32_t bits) |
| { |
| uint32_t tmp = getreg32(addr); |
| |
| putreg32(tmp & (~bits), addr); |
| } |
| |
| /**************************************************************************** |
| * 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_spi_priv_s *priv) |
| { |
| bool mapped = false; |
| const struct esp32_spi_config_s *cfg = priv->config; |
| |
| if (cfg->id == 2) |
| { |
| if ((!(cfg->flags & ESP32_SPI_IO_W) || |
| cfg->mosi_pin == SPI2_IOMUX_MOSIPIN) && |
| |
| #ifndef CONFIG_ESP32_SPI_SWCS |
| cfg->cs_pin == SPI2_IOMUX_CSPIN && |
| #endif |
| (!(cfg->flags & ESP32_SPI_IO_R) || |
| cfg->miso_pin == SPI2_IOMUX_MISOPIN) && |
| |
| cfg->clk_pin == SPI2_IOMUX_CLKPIN) |
| { |
| mapped = true; |
| } |
| } |
| else if (cfg->id == 3) |
| { |
| if ((!(cfg->flags & ESP32_SPI_IO_W) || |
| cfg->mosi_pin == SPI3_IOMUX_MOSIPIN) && |
| |
| #ifndef CONFIG_ESP32_SPI_SWCS |
| cfg->cs_pin == SPI3_IOMUX_CSPIN && |
| #endif |
| (!(cfg->flags & ESP32_SPI_IO_R) || |
| cfg->miso_pin == SPI3_IOMUX_MISOPIN) && |
| |
| cfg->clk_pin == SPI3_IOMUX_CLKPIN) |
| { |
| mapped = true; |
| } |
| } |
| |
| return mapped; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_lock |
| * |
| * Description: |
| * Lock or unlock the SPI device |
| * |
| * Input Parameters: |
| * priv - Private SPI device structure |
| * lock - true: Lock spi bus, false: unlock SPI bus |
| * |
| * Returned Value: |
| * The result of lock or unlock the SPI device |
| * |
| ****************************************************************************/ |
| |
| static int esp32_spi_lock(struct spi_dev_s *dev, bool lock) |
| { |
| int ret; |
| struct esp32_spi_priv_s *priv = (struct esp32_spi_priv_s *)dev; |
| |
| if (lock) |
| { |
| ret = nxmutex_lock(&priv->lock); |
| } |
| else |
| { |
| ret = nxmutex_unlock(&priv->lock); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_sem_waitdone |
| * |
| * Description: |
| * Wait for a transfer to complete |
| * |
| ****************************************************************************/ |
| |
| static int esp32_spi_sem_waitdone(struct esp32_spi_priv_s *priv) |
| { |
| return nxsem_tickwait_uninterruptible(&priv->sem_isr, SEC2TICK(10)); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_select |
| * |
| * Description: |
| * Enable/disable the SPI chip select. The implementation of this method |
| * must include handshaking: If a device is selected, it must hold off |
| * all other attempts to select the device until the device is deselected. |
| * |
| * If disable ESP32_SPI_SWCS, driver will use hardware CS so that when |
| * once transmission is started, hardware select the device and when this |
| * transmission is done, hardware deselect the device automatically. And |
| * the function will do nothing. |
| * |
| * Input Parameters: |
| * priv - Private SPI device structure |
| * devid - Identifies the device to select |
| * selected - true: slave selected, false: slave de-selected |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_ESP32_SPI_UDCS |
| static void esp32_spi_select(struct spi_dev_s *dev, |
| uint32_t devid, bool selected) |
| { |
| #ifdef CONFIG_ESP32_SPI_SWCS |
| struct esp32_spi_priv_s *priv = (struct esp32_spi_priv_s *)dev; |
| |
| esp32_gpiowrite(priv->config->cs_pin, !selected); |
| #endif |
| |
| spiinfo("devid: %08" PRIx32 " CS: %s\n", |
| devid, selected ? "select" : "free"); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: esp32_spi_setfrequency |
| * |
| * Description: |
| * Set the SPI frequency. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * frequency - The SPI frequency requested |
| * |
| * Returned Value: |
| * Returns the actual frequency selected |
| * |
| ****************************************************************************/ |
| |
| static uint32_t esp32_spi_setfrequency(struct spi_dev_s *dev, |
| uint32_t frequency) |
| { |
| uint32_t reg_val; |
| struct esp32_spi_priv_s *priv = (struct esp32_spi_priv_s *)dev; |
| const uint32_t duty_cycle = 128; |
| |
| if (priv->frequency == frequency) |
| { |
| /* We are already at this frequency. Return the actual. */ |
| |
| return priv->actual; |
| } |
| |
| if (frequency > ((APB_CLK_FREQ / 4) * 3)) |
| { |
| reg_val = SPI_CLK_EQU_SYSCLK_M; |
| priv->actual = APB_CLK_FREQ; |
| } |
| else |
| { |
| int pre; |
| int n; |
| int h; |
| int l; |
| int bestn = -1; |
| int bestpre = -1; |
| int besterr = 0; |
| int errval; |
| |
| for (n = 2; n <= 64; n++) |
| { |
| pre = ((APB_CLK_FREQ / n) + (frequency / 2)) / frequency; |
| |
| if (pre <= 0) |
| { |
| pre = 1; |
| } |
| |
| if (pre > 8192) |
| { |
| pre = 8192; |
| } |
| |
| errval = abs(APB_CLK_FREQ / (pre * n) - frequency); |
| if (bestn == -1 || errval <= besterr) |
| { |
| besterr = errval; |
| bestn = n; |
| bestpre = pre; |
| } |
| } |
| |
| n = bestn; |
| pre = bestpre; |
| l = n; |
| h = (duty_cycle * n + 127) / 256; |
| if (h <= 0) |
| { |
| h = 1; |
| } |
| |
| reg_val = ((l - 1) << SPI_CLKCNT_L_S) | |
| ((h - 1) << SPI_CLKCNT_H_S) | |
| ((n - 1) << SPI_CLKCNT_N_S) | |
| ((pre - 1) << SPI_CLKDIV_PRE_S); |
| |
| priv->actual = APB_CLK_FREQ / (n * pre); |
| } |
| |
| priv->frequency = frequency; |
| |
| putreg32(reg_val, SPI_CLOCK_REG(priv->config->id)); |
| |
| spiinfo("frequency=%d, actual=%d\n", priv->frequency, priv->actual); |
| |
| return priv->actual; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_setmode |
| * |
| * Description: |
| * Set the SPI mode. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * mode - The requested SPI mode |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spi_setmode(struct spi_dev_s *dev, |
| enum spi_mode_e mode) |
| { |
| uint32_t ck_idle_edge; |
| uint32_t ck_out_edge; |
| uint32_t delay_mode; |
| struct esp32_spi_priv_s *priv = (struct esp32_spi_priv_s *)dev; |
| |
| spiinfo("mode=%d\n", mode); |
| |
| /* Has the mode changed? */ |
| |
| if (mode != priv->mode) |
| { |
| switch (mode) |
| { |
| case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */ |
| ck_idle_edge = 0; |
| ck_out_edge = 0; |
| delay_mode = 0; |
| break; |
| |
| case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */ |
| ck_idle_edge = 0; |
| ck_out_edge = 1; |
| delay_mode = 2; |
| break; |
| |
| case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */ |
| ck_idle_edge = 1; |
| ck_out_edge = 1; |
| delay_mode = 2; |
| break; |
| |
| case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */ |
| ck_idle_edge = 1; |
| ck_out_edge = 0; |
| delay_mode = 0; |
| break; |
| |
| default: |
| return; |
| } |
| |
| const uint32_t id = priv->config->id; |
| |
| esp32_spi_reset_regbits(SPI_PIN_REG(id), SPI_CK_IDLE_EDGE_M); |
| esp32_spi_set_regbits(SPI_PIN_REG(id), |
| VALUE_MASK(ck_idle_edge, SPI_CK_IDLE_EDGE)); |
| |
| esp32_spi_reset_regbits(SPI_USER_REG(id), SPI_CK_OUT_EDGE_M); |
| esp32_spi_set_regbits(SPI_USER_REG(id), |
| VALUE_MASK(ck_out_edge, SPI_CK_OUT_EDGE)); |
| |
| esp32_spi_reset_regbits(SPI_CTRL2_REG(id), |
| SPI_MISO_DELAY_MODE_M | |
| SPI_MISO_DELAY_NUM_M | |
| SPI_MOSI_DELAY_NUM_M | |
| SPI_MOSI_DELAY_MODE_M); |
| esp32_spi_set_regbits(SPI_CTRL2_REG(id), |
| VALUE_MASK(delay_mode, SPI_MISO_DELAY_MODE) | |
| VALUE_MASK(delay_mode, SPI_MOSI_DELAY_MODE)); |
| |
| priv->mode = mode; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_setbits |
| * |
| * Description: |
| * Set the number of bits per word. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * nbits - The number of bits in an SPI word. |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spi_setbits(struct spi_dev_s *dev, int nbits) |
| { |
| struct esp32_spi_priv_s *priv = (struct esp32_spi_priv_s *)dev; |
| |
| spiinfo("nbits=%d\n", nbits); |
| |
| priv->nbits = nbits; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_hwfeatures |
| * |
| * Description: |
| * Set hardware-specific feature flags. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * features - H/W feature flags |
| * |
| * Returned Value: |
| * Zero (OK) if the selected H/W features are enabled; A negated errno |
| * value if any H/W feature is not supportable. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_SPI_HWFEATURES |
| static int esp32_spi_hwfeatures(struct spi_dev_s *dev, |
| spi_hwfeatures_t features) |
| { |
| /* Other H/W features are not supported */ |
| |
| return (features == 0) ? OK : -ENOSYS; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: esp32_spi_dma_exchange |
| * |
| * Description: |
| * Exchange a block of data from SPI by DMA. |
| * |
| * Input Parameters: |
| * priv - SPI private state data |
| * txbuffer - A pointer to the buffer of data to be sent |
| * rxbuffer - A pointer to the buffer in which to receive data |
| * nwords - the length of data that to be exchanged in units of words. |
| * The wordsize is determined by the number of bits-per-word |
| * selected for the SPI interface. If nbits <= 8, the data is |
| * packed into uint8_t's; if nbits >8, the data is packed into |
| * uint16_t's |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spi_dma_exchange(struct esp32_spi_priv_s *priv, |
| const void *txbuffer, |
| void *rxbuffer, |
| uint32_t nwords) |
| { |
| const uint32_t total = nwords * (priv->nbits / 8); |
| uint32_t bytes = total; |
| uint8_t *tp; |
| uint8_t *rp; |
| uint32_t n; |
| uint32_t regval; |
| struct esp32_dmadesc_s *dma_tx_desc; |
| struct esp32_dmadesc_s *dma_rx_desc; |
| #ifdef CONFIG_ESP32_SPIRAM |
| uint8_t *alloctp = NULL; |
| uint8_t *allocrp = NULL; |
| #endif |
| |
| /* Define these constants outside transfer loop to avoid wasting CPU time |
| * with register offset calculation. |
| */ |
| |
| const uint32_t id = priv->config->id; |
| const uint8_t dma_desc_idx = priv->config->dma_chan - 1; |
| const uintptr_t spi_dma_in_link_reg = SPI_DMA_IN_LINK_REG(id); |
| const uintptr_t spi_dma_out_link_reg = SPI_DMA_OUT_LINK_REG(id); |
| const uintptr_t spi_slave_reg = SPI_SLAVE_REG(id); |
| const uintptr_t spi_dma_conf_reg = SPI_DMA_CONF_REG(id); |
| const uintptr_t spi_mosi_dlen_reg = SPI_MOSI_DLEN_REG(id); |
| const uintptr_t spi_miso_dlen_reg = SPI_MISO_DLEN_REG(id); |
| const uintptr_t spi_user_reg = SPI_USER_REG(id); |
| const uintptr_t spi_cmd_reg = SPI_CMD_REG(id); |
| |
| DEBUGASSERT((txbuffer != NULL) || (rxbuffer != NULL)); |
| |
| /* If the buffer comes from PSRAM, allocate a new one from DRAM */ |
| |
| #ifdef CONFIG_ESP32_SPIRAM |
| if (esp32_ptr_extram(txbuffer)) |
| { |
| # ifdef CONFIG_MM_KERNEL_HEAP |
| alloctp = kmm_malloc(total); |
| # elif defined(CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP) |
| alloctp = xtensa_imm_malloc(total); |
| # endif |
| |
| DEBUGASSERT(alloctp != NULL); |
| memcpy(alloctp, txbuffer, total); |
| tp = alloctp; |
| } |
| else |
| #endif |
| { |
| tp = (uint8_t *)txbuffer; |
| } |
| |
| #ifdef CONFIG_ESP32_SPIRAM |
| if (esp32_ptr_extram(rxbuffer)) |
| { |
| # ifdef CONFIG_MM_KERNEL_HEAP |
| allocrp = kmm_malloc(total); |
| # elif defined(CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP) |
| allocrp = xtensa_imm_malloc(total); |
| # endif |
| |
| DEBUGASSERT(allocrp != NULL); |
| rp = allocrp; |
| } |
| else |
| #endif |
| { |
| rp = (uint8_t *)rxbuffer; |
| } |
| |
| if (tp == NULL) |
| { |
| tp = rp; |
| } |
| |
| dma_tx_desc = s_dma_txdesc[dma_desc_idx]; |
| dma_rx_desc = s_dma_rxdesc[dma_desc_idx]; |
| |
| esp32_spi_reset_regbits(spi_slave_reg, SPI_TRANS_DONE_M); |
| esp32_spi_set_regbits(spi_slave_reg, SPI_INT_EN_M); |
| |
| while (bytes != 0) |
| { |
| putreg32(0, spi_dma_in_link_reg); |
| putreg32(0, spi_dma_out_link_reg); |
| |
| esp32_spi_set_regbits(spi_slave_reg, SPI_SYNC_RESET_M); |
| esp32_spi_reset_regbits(spi_slave_reg, SPI_SYNC_RESET_M); |
| |
| esp32_spi_set_regbits(spi_dma_conf_reg, SPI_DMA_RESET_MASK); |
| esp32_spi_reset_regbits(spi_dma_conf_reg, SPI_DMA_RESET_MASK); |
| |
| n = esp32_dma_init(dma_tx_desc, SPI_DMADESC_NUM, tp, bytes); |
| |
| regval = VALUE_MASK((uintptr_t)dma_tx_desc, SPI_OUTLINK_ADDR); |
| regval |= SPI_OUTLINK_START_M; |
| putreg32(regval, spi_dma_out_link_reg); |
| putreg32((n * 8 - 1), spi_mosi_dlen_reg); |
| esp32_spi_set_regbits(spi_user_reg, SPI_USR_MOSI_M); |
| |
| tp += n; |
| |
| if (rp != NULL) |
| { |
| esp32_dma_init(dma_rx_desc, SPI_DMADESC_NUM, rp, bytes); |
| |
| regval = VALUE_MASK((uintptr_t)dma_rx_desc, SPI_INLINK_ADDR); |
| regval |= SPI_INLINK_START_M; |
| putreg32(regval, spi_dma_in_link_reg); |
| putreg32((n * 8 - 1), spi_miso_dlen_reg); |
| esp32_spi_set_regbits(spi_user_reg, SPI_USR_MISO_M); |
| |
| rp += n; |
| } |
| else |
| { |
| esp32_spi_reset_regbits(spi_user_reg, SPI_USR_MISO_M); |
| } |
| |
| esp32_spi_set_regbits(spi_cmd_reg, SPI_USR_M); |
| |
| esp32_spi_sem_waitdone(priv); |
| |
| bytes -= n; |
| } |
| |
| esp32_spi_reset_regbits(spi_slave_reg, SPI_INT_EN_M); |
| |
| #ifdef CONFIG_ESP32_SPIRAM |
| if (allocrp) |
| { |
| memcpy(rxbuffer, allocrp, total); |
| # ifdef CONFIG_MM_KERNEL_HEAP |
| kmm_free(allocrp); |
| # elif defined(CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP) |
| xtensa_imm_free(allocrp); |
| # endif |
| } |
| #endif |
| |
| #ifdef CONFIG_ESP32_SPIRAM |
| if (alloctp) |
| { |
| # ifdef CONFIG_MM_KERNEL_HEAP |
| kmm_free(alloctp); |
| # elif defined(CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP) |
| xtensa_imm_free(alloctp); |
| # endif |
| } |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_poll_send |
| * |
| * Description: |
| * Send one word on SPI by polling mode. |
| * |
| * Input Parameters: |
| * priv - SPI private state data |
| * wd - The word to send. The size of the data is determined by the |
| * number of bits selected for the SPI interface. |
| * |
| * Returned Value: |
| * Received value. |
| * |
| ****************************************************************************/ |
| |
| static uint32_t esp32_spi_poll_send(struct esp32_spi_priv_s *priv, |
| uint32_t wd) |
| { |
| uint32_t val; |
| const uintptr_t spi_miso_dlen_reg = SPI_MISO_DLEN_REG(priv->config->id); |
| const uintptr_t spi_mosi_dlen_reg = SPI_MOSI_DLEN_REG(priv->config->id); |
| const uintptr_t spi_w0_reg = SPI_W0_REG(priv->config->id); |
| const uintptr_t spi_cmd_reg = SPI_CMD_REG(priv->config->id); |
| |
| putreg32((priv->nbits - 1), spi_miso_dlen_reg); |
| putreg32((priv->nbits - 1), spi_mosi_dlen_reg); |
| |
| putreg32(wd, spi_w0_reg); |
| |
| esp32_spi_set_regbits(spi_cmd_reg, SPI_USR_M); |
| |
| while ((getreg32(spi_cmd_reg) & SPI_USR_M) != 0) |
| { |
| ; |
| } |
| |
| val = getreg32(spi_w0_reg); |
| |
| spiinfo("send=0x%" PRIx32 " and recv=0x%" PRIx32 "\n", wd, val); |
| |
| return val; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_send |
| * |
| * Description: |
| * Send one word on SPI. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * wd - The word to send. The size of the data is determined by the |
| * number of bits selected for the SPI interface. |
| * |
| * Returned Value: |
| * Received value. |
| * |
| ****************************************************************************/ |
| |
| static uint32_t esp32_spi_send(struct spi_dev_s *dev, uint32_t wd) |
| { |
| struct esp32_spi_priv_s *priv = (struct esp32_spi_priv_s *)dev; |
| |
| return esp32_spi_poll_send(priv, wd); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_poll_exchange |
| * |
| * Description: |
| * Exchange a block of data from SPI. |
| * |
| * Input Parameters: |
| * priv - SPI private state data |
| * txbuffer - A pointer to the buffer of data to be sent |
| * rxbuffer - A pointer to the buffer in which to receive data |
| * nwords - The length of data that to be exchanged in units of words. |
| * The wordsize is determined by the number of bits-per-word |
| * selected for the SPI interface. If nbits <= 8, the data is |
| * packed into uint8_t's; if nbits >8, the data is packed into |
| * uint16_t's |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spi_poll_exchange(struct esp32_spi_priv_s *priv, |
| const void *txbuffer, |
| void *rxbuffer, |
| size_t nwords) |
| { |
| const uintptr_t spi_user_reg = SPI_USER_REG(priv->config->id); |
| const uintptr_t spi_w0_reg = SPI_W0_REG(priv->config->id); |
| const uintptr_t spi_cmd_reg = SPI_CMD_REG(priv->config->id); |
| const uintptr_t spi_miso_dlen_reg = SPI_MISO_DLEN_REG(priv->config->id); |
| const uintptr_t spi_mosi_dlen_reg = SPI_MOSI_DLEN_REG(priv->config->id); |
| const uint32_t total_bytes = nwords * (priv->nbits / 8); |
| uintptr_t bytes_remaining = total_bytes; |
| uint8_t *tp = (uint8_t *)txbuffer; |
| uint8_t *rp = (uint8_t *)rxbuffer; |
| |
| while (bytes_remaining != 0) |
| { |
| /* Initialize data_buf_reg with the address of the first data buffer |
| * register (W0). |
| */ |
| |
| uintptr_t data_buf_reg = spi_w0_reg; |
| uint32_t transfer_size = MIN(SPI_MAX_BUF_SIZE, bytes_remaining); |
| |
| /* Write data words to data buffer registers. |
| * SPI peripheral contains 16 registers (W0 - W15). |
| */ |
| |
| for (int i = 0 ; i < transfer_size; i += sizeof(uint32_t)) |
| { |
| uint32_t w_wd = UINT32_MAX; |
| |
| if (tp != NULL) |
| { |
| memcpy(&w_wd, tp, sizeof(uint32_t)); |
| |
| tp += sizeof(uintptr_t); |
| } |
| |
| putreg32(w_wd, data_buf_reg); |
| |
| spiinfo("send=0x%" PRIx32 " data_reg=0x%" PRIx32 "\n", |
| w_wd, data_buf_reg); |
| |
| /* Update data_buf_reg to point to the next data buffer register. */ |
| |
| data_buf_reg += sizeof(uintptr_t); |
| } |
| |
| esp32_spi_set_regbits(spi_user_reg, SPI_USR_MOSI_M); |
| |
| if (rp == NULL) |
| { |
| esp32_spi_reset_regbits(spi_user_reg, SPI_USR_MISO_M); |
| } |
| else |
| { |
| esp32_spi_set_regbits(spi_user_reg, SPI_USR_MISO_M); |
| } |
| |
| putreg32((transfer_size * 8) - 1, spi_mosi_dlen_reg); |
| putreg32((transfer_size * 8) - 1, spi_miso_dlen_reg); |
| |
| /* Trigger start of user-defined transaction for master. */ |
| |
| esp32_spi_set_regbits(spi_cmd_reg, SPI_USR_M); |
| |
| /* Wait for the user-defined transaction to finish. */ |
| |
| while ((getreg32(spi_cmd_reg) & SPI_USR_M) != 0) |
| { |
| ; |
| } |
| |
| if (rp != NULL) |
| { |
| /* Set data_buf_reg with the address of the first data buffer |
| * register (W0). |
| */ |
| |
| data_buf_reg = spi_w0_reg; |
| |
| /* Read received data words from SPI data buffer registers. */ |
| |
| for (int i = 0 ; i < transfer_size; i += sizeof(uint32_t)) |
| { |
| uint32_t r_wd = getreg32(data_buf_reg); |
| |
| spiinfo("recv=0x%" PRIx32 " data_reg=0x%" PRIx32 "\n", |
| r_wd, data_buf_reg); |
| |
| memcpy(rp, &r_wd, sizeof(uint32_t)); |
| |
| rp += sizeof(uintptr_t); |
| |
| /* Update data_buf_reg to point to the next data buffer |
| * register. |
| */ |
| |
| data_buf_reg += sizeof(uintptr_t); |
| } |
| } |
| |
| bytes_remaining -= transfer_size; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_exchange |
| * |
| * Description: |
| * Exchange a block of data from SPI. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * txbuffer - A pointer to the buffer of data to be sent |
| * rxbuffer - A pointer to the buffer in which to receive data |
| * nwords - The length of data that to be exchanged in units of words. |
| * The wordsize is determined by the number of bits-per-word |
| * selected for the SPI interface. If nbits <= 8, the data is |
| * packed into uint8_t's; if nbits >8, the data is packed into |
| * uint16_t's |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spi_exchange(struct spi_dev_s *dev, |
| const void *txbuffer, |
| void *rxbuffer, |
| size_t nwords) |
| { |
| struct esp32_spi_priv_s *priv = (struct esp32_spi_priv_s *)dev; |
| |
| #ifdef CONFIG_ESP32_SPI_DMATHRESHOLD |
| size_t thld = CONFIG_ESP32_SPI_DMATHRESHOLD; |
| #else |
| size_t thld = 0; |
| #endif |
| |
| if (priv->config->use_dma && nwords > thld) |
| { |
| esp32_spi_dma_exchange(priv, txbuffer, rxbuffer, nwords); |
| } |
| else |
| { |
| esp32_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords); |
| } |
| } |
| |
| #ifndef CONFIG_SPI_EXCHANGE |
| |
| /**************************************************************************** |
| * Name: esp32_spi_sndblock |
| * |
| * Description: |
| * Send a block of data on SPI. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * txbuffer - A pointer to the buffer of data to be sent |
| * nwords - The length of data to send from the buffer in number of |
| * words. The wordsize is determined by the number of |
| * bits-per-word selected for the SPI interface. If nbits <= 8, |
| * the data is packed into uint8_t's; if nbits >8, the data is |
| * packed into uint16_t's |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spi_sndblock(struct spi_dev_s *dev, |
| const void *txbuffer, |
| size_t nwords) |
| { |
| spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords); |
| |
| esp32_spi_exchange(dev, txbuffer, NULL, nwords); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_recvblock |
| * |
| * Description: |
| * Receive a block of data from SPI. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * rxbuffer - A pointer to the buffer in which to receive data |
| * nwords - The length of data that can be received in the buffer in |
| * number of words. The wordsize is determined by the number of |
| * bits-per-word selected for the SPI interface. If nbits <= 8, |
| * the data is packed into uint8_t's; if nbits >8, the data is |
| * packed into uint16_t's |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spi_recvblock(struct spi_dev_s *dev, |
| void *rxbuffer, |
| size_t nwords) |
| { |
| spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords); |
| |
| esp32_spi_exchange(dev, NULL, rxbuffer, nwords); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: esp32_spi_trigger |
| * |
| * Description: |
| * Trigger a previously configured DMA transfer. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * |
| * Returned Value: |
| * OK - Trigger was fired |
| * -ENOSYS - Trigger not fired due to lack of DMA or low level support |
| * -EIO - Trigger not fired because not previously primed |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_SPI_TRIGGER |
| static int esp32_spi_trigger(struct spi_dev_s *dev) |
| { |
| return -ENOSYS; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: esp32_spi_init |
| * |
| * Description: |
| * Initialize ESP32 SPI hardware interface |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spi_init(struct spi_dev_s *dev) |
| { |
| struct esp32_spi_priv_s *priv = (struct esp32_spi_priv_s *)dev; |
| const struct esp32_spi_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_W) |
| { |
| esp32_gpiowrite(config->mosi_pin, 1); |
| } |
| |
| if (config->flags & ESP32_SPI_IO_R) |
| { |
| esp32_gpiowrite(config->miso_pin, 1); |
| } |
| |
| #ifdef CONFIG_ESP32_SPI_SWCS |
| esp32_configgpio(config->cs_pin, OUTPUT); |
| esp32_gpio_matrix_out(config->cs_pin, SIG_GPIO_OUT_IDX, 0, 0); |
| #endif |
| |
| if (esp32_spi_iomux(priv)) |
| { |
| #ifndef CONFIG_ESP32_SPI_SWCS |
| esp32_configgpio(config->cs_pin, OUTPUT_FUNCTION_2); |
| esp32_gpio_matrix_out(config->cs_pin, SIG_GPIO_OUT_IDX, 0, 0); |
| #endif |
| |
| esp32_configgpio(config->clk_pin, OUTPUT_FUNCTION_2); |
| esp32_gpio_matrix_out(config->clk_pin, SIG_GPIO_OUT_IDX, 0, 0); |
| |
| if (config->flags & ESP32_SPI_IO_W) |
| { |
| esp32_configgpio(config->mosi_pin, OUTPUT_FUNCTION_2); |
| esp32_gpio_matrix_out(config->mosi_pin, SIG_GPIO_OUT_IDX, 0, 0); |
| } |
| |
| if (config->flags & ESP32_SPI_IO_R) |
| { |
| esp32_configgpio(config->miso_pin, INPUT_FUNCTION_2 | PULLUP); |
| esp32_gpio_matrix_out(config->miso_pin, SIG_GPIO_OUT_IDX, 0, 0); |
| } |
| } |
| else |
| { |
| #ifndef CONFIG_ESP32_SPI_SWCS |
| esp32_configgpio(config->cs_pin, OUTPUT_FUNCTION_3); |
| esp32_gpio_matrix_out(config->cs_pin, config->cs_outsig, 0, 0); |
| #endif |
| |
| esp32_configgpio(config->clk_pin, OUTPUT_FUNCTION_3); |
| esp32_gpio_matrix_out(config->clk_pin, config->clk_outsig, 0, 0); |
| |
| if (config->flags & ESP32_SPI_IO_W) |
| { |
| esp32_configgpio(config->mosi_pin, OUTPUT_FUNCTION_3); |
| esp32_gpio_matrix_out(config->mosi_pin, config->mosi_outsig, 0, 0); |
| } |
| |
| if (config->flags & ESP32_SPI_IO_R) |
| { |
| esp32_configgpio(config->miso_pin, INPUT_FUNCTION_3 | PULLUP); |
| esp32_gpio_matrix_in(config->miso_pin, config->miso_insig, 0); |
| } |
| } |
| |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, config->clk_bit); |
| modifyreg32(DPORT_PERIP_RST_EN_REG, config->rst_bit, 0); |
| |
| regval = SPI_DOUTDIN_M | SPI_USR_MISO_M | SPI_USR_MOSI_M | SPI_CS_HOLD_M; |
| putreg32(regval, SPI_USER_REG(config->id)); |
| putreg32(0, SPI_USER1_REG(config->id)); |
| putreg32(0, SPI_SLAVE_REG(config->id)); |
| putreg32(SPI_CS1_DIS_M | SPI_CS2_DIS_M, SPI_PIN_REG(config->id)); |
| |
| #ifdef CONFIG_ESP32_SPI_SWCS |
| esp32_spi_set_regbits(SPI_PIN_REG(config->id), SPI_CS0_DIS_M); |
| #endif |
| |
| putreg32(0, SPI_CTRL_REG(config->id)); |
| putreg32(VALUE_MASK(0, SPI_HOLD_TIME), SPI_CTRL2_REG(config->id)); |
| |
| if (config->use_dma) |
| { |
| 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)); |
| |
| regval = SPI_OUT_DATA_BURST_EN_M | |
| SPI_INDSCR_BURST_EN_M | |
| SPI_OUTDSCR_BURST_EN_M; |
| putreg32(regval, SPI_DMA_CONF_REG(config->id)); |
| } |
| |
| esp32_spi_setfrequency(dev, config->clk_freq); |
| esp32_spi_setbits(dev, 8); |
| esp32_spi_setmode(dev, config->mode); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_deinit |
| * |
| * Description: |
| * Deinitialize ESP32 SPI hardware interface |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_spi_deinit(struct spi_dev_s *dev) |
| { |
| struct esp32_spi_priv_s *priv = (struct esp32_spi_priv_s *)dev; |
| |
| if (priv->config->use_dma) |
| { |
| 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->rst_bit); |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, priv->config->clk_bit, 0); |
| |
| priv->frequency = 0; |
| priv->actual = 0; |
| priv->mode = SPIDEV_MODE0; |
| priv->nbits = 0; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spi_interrupt |
| * |
| * Description: |
| * Common SPI DMA interrupt handler. |
| * |
| * Input Parameters: |
| * irq - Number of the IRQ that generated the interrupt |
| * context - Interrupt register state save info |
| * arg - SPI controller private data |
| * |
| * Returned Value: |
| * Standard interrupt return value. |
| * |
| ****************************************************************************/ |
| |
| static int esp32_spi_interrupt(int irq, void *context, void *arg) |
| { |
| struct esp32_spi_priv_s *priv = (struct esp32_spi_priv_s *)arg; |
| |
| esp32_spi_reset_regbits(SPI_SLAVE_REG(priv->config->id), SPI_TRANS_DONE_M); |
| nxsem_post(&priv->sem_isr); |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spibus_initialize |
| * |
| * Description: |
| * Initialize the selected SPI bus. |
| * |
| * Input Parameters: |
| * port - Port number (for hardware that has multiple SPI interfaces) |
| * |
| * Returned Value: |
| * Valid SPI device structure reference on success; NULL on failure. |
| * |
| ****************************************************************************/ |
| |
| struct spi_dev_s *esp32_spibus_initialize(int port) |
| { |
| int ret; |
| struct spi_dev_s *spi_dev; |
| struct esp32_spi_priv_s *priv; |
| |
| switch (port) |
| { |
| #ifdef CONFIG_ESP32_SPI2 |
| case ESP32_SPI2: |
| priv = &esp32_spi2_priv; |
| break; |
| #endif |
| #ifdef CONFIG_ESP32_SPI3 |
| case ESP32_SPI3: |
| priv = &esp32_spi3_priv; |
| break; |
| #endif |
| default: |
| return NULL; |
| } |
| |
| spi_dev = (struct spi_dev_s *)priv; |
| |
| nxmutex_lock(&priv->lock); |
| if (priv->refs != 0) |
| { |
| priv->refs++; |
| nxmutex_unlock(&priv->lock); |
| return spi_dev; |
| } |
| |
| if (priv->config->use_dma) |
| { |
| /* 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); |
| if (priv->cpuint < 0) |
| { |
| nxmutex_unlock(&priv->lock); |
| return NULL; |
| } |
| |
| ret = irq_attach(priv->config->irq, esp32_spi_interrupt, priv); |
| if (ret != OK) |
| { |
| esp32_teardown_irq(priv->cpu, |
| priv->config->periph, |
| priv->cpuint); |
| nxmutex_unlock(&priv->lock); |
| return NULL; |
| } |
| |
| up_enable_irq(priv->config->irq); |
| } |
| |
| esp32_spi_init(spi_dev); |
| priv->refs++; |
| |
| nxmutex_unlock(&priv->lock); |
| return spi_dev; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_spibus_uninitialize |
| * |
| * Description: |
| * Uninitialize an SPI bus. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success. Otherwise -1 (ERROR). |
| * |
| ****************************************************************************/ |
| |
| int esp32_spibus_uninitialize(struct spi_dev_s *dev) |
| { |
| struct esp32_spi_priv_s *priv = (struct esp32_spi_priv_s *)dev; |
| |
| DEBUGASSERT(dev); |
| |
| if (priv->refs == 0) |
| { |
| return ERROR; |
| } |
| |
| nxmutex_lock(&priv->lock); |
| if (--priv->refs != 0) |
| { |
| nxmutex_unlock(&priv->lock); |
| return OK; |
| } |
| |
| if (priv->config->use_dma) |
| { |
| up_disable_irq(priv->config->irq); |
| esp32_teardown_irq(priv->cpu, |
| priv->config->periph, |
| priv->cpuint); |
| } |
| |
| esp32_spi_deinit(dev); |
| nxmutex_unlock(&priv->lock); |
| |
| return OK; |
| } |
| |
| #endif /* CONFIG_ESP32_SPI */ |