blob: d8c24e43f08df8fc88c361a6ca4ee37fb954450d [file] [log] [blame]
/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_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_ESP32S3_SPI
#include <assert.h>
#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 <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/clock.h>
#include <nuttx/mutex.h>
#include <nuttx/spi/spi.h>
#include <arch/board/board.h>
#include "esp32s3_spi.h"
#include "esp32s3_irq.h"
#include "esp32s3_gpio.h"
#ifdef CONFIG_ESP32S3_SPI_DMA
#include "esp32s3_dma.h"
#include "hardware/esp32s3_dma.h"
#endif
#include "xtensa.h"
#include "hardware/esp32s3_gpio_sigmap.h"
#include "hardware/esp32s3_pinmap.h"
#include "hardware/esp32s3_spi.h"
#include "hardware/esp32s3_soc.h"
#include "hardware/esp32s3_system.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Check if Chip-Select pin will be controlled via software */
#ifdef CONFIG_ESP32S3_SPI_SWCS
# define SPI_HAVE_SWCS 1
#else
# define SPI_HAVE_SWCS 0
#endif
#ifdef CONFIG_ESP32S3_SPI_DMA
/* QSPI DMA RX/TX number of descriptors */
# if (CONFIG_ESP32S3_SPI_DMA_BUFSIZE % ESP32S3_DMA_BUFLEN_MAX) > 0
# define SPI_DMA_DESC_NUM (CONFIG_ESP32S3_SPI_DMA_BUFSIZE / ESP32S3_DMA_BUFLEN_MAX + 1)
# else
# define SPI_DMA_DESC_NUM (CONFIG_ESP32S3_SPI_DMA_BUFSIZE / ESP32S3_DMA_BUFLEN_MAX)
# endif
/* SPI DMA reset before exchange */
#define SPI_DMA_RESET_MASK (SPI_DMA_AFIFO_RST_M | SPI_RX_AFIFO_RST_M)
#endif
/* SPI default frequency (limited by clock divider) */
#define SPI_DEFAULT_FREQ (400000)
/* SPI default width */
#define SPI_DEFAULT_WIDTH (8)
/* SPI default mode */
#define SPI_DEFAULT_MODE (SPIDEV_MODE0)
/* 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 esp32s3_spi_config_s
{
uint32_t clk_freq; /* SPI default clock frequency */
uint32_t width; /* SPI default width */
enum spi_mode_e mode; /* SPI default mode */
uint8_t id; /* ESP32-S3 SPI device ID: SPIx {2,3} */
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 */
#ifdef CONFIG_ESP32S3_SPI_DMA
uint8_t periph; /* Peripheral ID */
uint8_t irq; /* Interrupt ID */
#endif
uint32_t clk_bit; /* Clock enable bit */
uint32_t rst_bit; /* SPI reset bit */
#ifdef CONFIG_ESP32S3_SPI_DMA
uint32_t dma_clk_bit; /* DMA clock enable bit */
uint32_t dma_rst_bit; /* DMA reset bit */
uint8_t dma_periph; /* DMA peripheral */
#endif
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 */
};
struct esp32s3_spi_priv_s
{
/* Externally visible part of the SPI interface */
struct spi_dev_s spi_dev;
/* Port configuration */
const struct esp32s3_spi_config_s *config;
int refs; /* Reference count */
mutex_t lock; /* Held while chip is selected for mutual exclusion */
#ifdef CONFIG_ESP32S3_SPI_DMA
sem_t sem_isr; /* Interrupt wait semaphore */
int cpu; /* CPU ID */
int cpuint; /* SPI interrupt ID */
int32_t dma_channel; /* Channel assigned by the GDMA driver */
/* DMA RX/TX description */
struct esp32s3_dmadesc_s *dma_rxdesc;
struct esp32s3_dmadesc_s *dma_txdesc;
#endif
uint32_t frequency; /* Requested clock frequency */
uint32_t actual; /* Actual clock frequency */
enum spi_mode_e mode; /* Actual SPI hardware mode */
uint8_t nbits; /* Actual SPI send/receive bits once transmission */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int esp32s3_spi_lock(struct spi_dev_s *dev, bool lock);
#ifndef CONFIG_ESP32S3_SPI_UDCS
static void esp32s3_spi_select(struct spi_dev_s *dev,
uint32_t devid, bool selected);
#endif
static uint32_t esp32s3_spi_setfrequency(struct spi_dev_s *dev,
uint32_t frequency);
static void esp32s3_spi_setmode(struct spi_dev_s *dev,
enum spi_mode_e mode);
static void esp32s3_spi_setbits(struct spi_dev_s *dev, int nbits);
#ifdef CONFIG_SPI_HWFEATURES
static int esp32s3_spi_hwfeatures(struct spi_dev_s *dev,
spi_hwfeatures_t features);
#endif
static uint32_t esp32s3_spi_send(struct spi_dev_s *dev, uint32_t wd);
static void esp32s3_spi_exchange(struct spi_dev_s *dev,
const void *txbuffer,
void *rxbuffer, size_t nwords);
#ifdef CONFIG_ESP32S3_SPI_DMA
static int esp32s3_spi_interrupt(int irq, void *context, void *arg);
static int esp32s3_spi_sem_waitdone(struct esp32s3_spi_priv_s *priv);
static void esp32s3_spi_dma_exchange(struct esp32s3_spi_priv_s *priv,
const void *txbuffer,
void *rxbuffer,
uint32_t nwords);
#endif
static void esp32s3_spi_poll_exchange(struct esp32s3_spi_priv_s *priv,
const void *txbuffer,
void *rxbuffer,
size_t nwords);
#ifndef CONFIG_SPI_EXCHANGE
static void esp32s3_spi_sndblock(struct spi_dev_s *dev,
const void *txbuffer,
size_t nwords);
static void esp32s3_spi_recvblock(struct spi_dev_s *dev,
void *rxbuffer,
size_t nwords);
#endif
#ifdef CONFIG_SPI_TRIGGER
static int esp32s3_spi_trigger(struct spi_dev_s *dev);
#endif
#ifdef CONFIG_ESP32S3_SPI_DMA
static int esp32s3_spi_dma_init(struct spi_dev_s *dev);
#endif
static int esp32s3_spi_init(struct spi_dev_s *dev);
static void esp32s3_spi_deinit(struct spi_dev_s *dev);
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CONFIG_ESP32S3_SPI2
static const struct esp32s3_spi_config_s esp32s3_spi2_config =
{
.clk_freq = SPI_DEFAULT_FREQ,
.width = SPI_DEFAULT_WIDTH,
.id = 2,
.mode = SPI_DEFAULT_MODE,
.cs_pin = CONFIG_ESP32S3_SPI2_CSPIN,
.mosi_pin = CONFIG_ESP32S3_SPI2_MOSIPIN,
.miso_pin = CONFIG_ESP32S3_SPI2_MISOPIN,
.clk_pin = CONFIG_ESP32S3_SPI2_CLKPIN,
#ifdef CONFIG_ESP32S3_SPI_DMA
.periph = ESP32S3_PERIPH_SPI2,
.irq = ESP32S3_IRQ_SPI2,
#endif
.clk_bit = SYSTEM_SPI2_CLK_EN,
.rst_bit = SYSTEM_SPI2_RST,
#ifdef CONFIG_ESP32S3_SPI_DMA
.dma_clk_bit = SYSTEM_SPI2_DMA_CLK_EN,
.dma_rst_bit = SYSTEM_SPI2_DMA_RST,
.dma_periph = ESP32S3_DMA_PERIPH_SPI2,
#endif
.cs_insig = FSPICS0_IN_IDX,
.cs_outsig = FSPICS0_OUT_IDX,
.mosi_insig = FSPID_IN_IDX,
.mosi_outsig = FSPID_OUT_IDX,
.miso_insig = FSPIQ_IN_IDX,
.miso_outsig = FSPIQ_OUT_IDX,
.clk_insig = FSPICLK_IN_IDX,
.clk_outsig = FSPICLK_OUT_IDX
};
static const struct spi_ops_s esp32s3_spi2_ops =
{
.lock = esp32s3_spi_lock,
#ifdef CONFIG_ESP32S3_SPI_UDCS
.select = esp32s3_spi2_select,
#else
.select = esp32s3_spi_select,
#endif
.setfrequency = esp32s3_spi_setfrequency,
.setmode = esp32s3_spi_setmode,
.setbits = esp32s3_spi_setbits,
#ifdef CONFIG_SPI_HWFEATURES
.hwfeatures = esp32s3_spi_hwfeatures,
#endif
.status = esp32s3_spi2_status,
#ifdef CONFIG_SPI_CMDDATA
.cmddata = esp32s3_spi2_cmddata,
#endif
.send = esp32s3_spi_send,
#ifdef CONFIG_SPI_EXCHANGE
.exchange = esp32s3_spi_exchange,
#else
.sndblock = esp32s3_spi_sndblock,
.recvblock = esp32s3_spi_recvblock,
#endif
#ifdef CONFIG_SPI_TRIGGER
.trigger = esp32s3_spi_trigger,
#endif
.registercallback = NULL,
};
#ifdef CONFIG_ESP32S3_SPI_DMA
/* SPI DMA RX/TX description */
static struct esp32s3_dmadesc_s esp32s3_spi2_dma_txdesc[SPI_DMA_DESC_NUM];
static struct esp32s3_dmadesc_s esp32s3_spi2_dma_rxdesc[SPI_DMA_DESC_NUM];
#endif
static struct esp32s3_spi_priv_s esp32s3_spi2_priv =
{
.spi_dev =
{
.ops = &esp32s3_spi2_ops
},
.config = &esp32s3_spi2_config,
.refs = 0,
.lock = NXMUTEX_INITIALIZER,
#ifdef CONFIG_ESP32S3_SPI_DMA
.sem_isr = SEM_INITIALIZER(0),
.cpuint = -ENOMEM,
.dma_channel = -1,
.dma_rxdesc = esp32s3_spi2_dma_rxdesc,
.dma_txdesc = esp32s3_spi2_dma_txdesc,
#endif
.frequency = 0,
.actual = 0,
.mode = 0,
.nbits = 0
};
#endif /* CONFIG_ESP32S3_SPI2 */
#ifdef CONFIG_ESP32S3_SPI3
static const struct esp32s3_spi_config_s esp32s3_spi3_config =
{
.clk_freq = SPI_DEFAULT_FREQ,
.width = SPI_DEFAULT_WIDTH,
.id = 3,
.mode = SPI_DEFAULT_MODE,
.cs_pin = CONFIG_ESP32S3_SPI3_CSPIN,
.mosi_pin = CONFIG_ESP32S3_SPI3_MOSIPIN,
.miso_pin = CONFIG_ESP32S3_SPI3_MISOPIN,
.clk_pin = CONFIG_ESP32S3_SPI3_CLKPIN,
#ifdef CONFIG_ESP32S3_SPI_DMA
.periph = ESP32S3_PERIPH_SPI3,
.irq = ESP32S3_IRQ_SPI3,
#endif
.clk_bit = SYSTEM_SPI3_CLK_EN,
.rst_bit = SYSTEM_SPI3_RST,
#ifdef CONFIG_ESP32S3_SPI_DMA
.dma_clk_bit = SYSTEM_SPI3_DMA_CLK_EN,
.dma_rst_bit = SYSTEM_SPI3_DMA_RST,
.dma_periph = ESP32S3_DMA_PERIPH_SPI3,
#endif
.cs_insig = SPI3_CS0_IN_IDX,
.cs_outsig = SPI3_CS0_OUT_IDX,
.mosi_insig = SPI3_D_IN_IDX,
.mosi_outsig = SPI3_D_OUT_IDX,
.miso_insig = SPI3_Q_IN_IDX,
.miso_outsig = SPI3_Q_OUT_IDX,
.clk_insig = SPI3_CLK_IN_IDX,
.clk_outsig = SPI3_CLK_OUT_IDX
};
static const struct spi_ops_s esp32s3_spi3_ops =
{
.lock = esp32s3_spi_lock,
#ifdef CONFIG_ESP32S3_SPI_UDCS
.select = esp32s3_spi3_select,
#else
.select = esp32s3_spi_select,
#endif
.setfrequency = esp32s3_spi_setfrequency,
.setmode = esp32s3_spi_setmode,
.setbits = esp32s3_spi_setbits,
#ifdef CONFIG_SPI_HWFEATURES
.hwfeatures = esp32s3_spi_hwfeatures,
#endif
.status = esp32s3_spi3_status,
#ifdef CONFIG_SPI_CMDDATA
.cmddata = esp32s3_spi3_cmddata,
#endif
.send = esp32s3_spi_send,
#ifdef CONFIG_SPI_EXCHANGE
.exchange = esp32s3_spi_exchange,
#else
.sndblock = esp32s3_spi_sndblock,
.recvblock = esp32s3_spi_recvblock,
#endif
#ifdef CONFIG_SPI_TRIGGER
.trigger = esp32s3_spi_trigger,
#endif
.registercallback = NULL,
};
#ifdef CONFIG_ESP32S3_SPI_DMA
/* SPI DMA RX/TX description */
static struct esp32s3_dmadesc_s esp32s3_spi3_dma_txdesc[SPI_DMA_DESC_NUM];
static struct esp32s3_dmadesc_s esp32s3_spi3_dma_rxdesc[SPI_DMA_DESC_NUM];
#endif
static struct esp32s3_spi_priv_s esp32s3_spi3_priv =
{
.spi_dev =
{
.ops = &esp32s3_spi3_ops
},
.config = &esp32s3_spi3_config,
.refs = 0,
.lock = NXMUTEX_INITIALIZER,
#ifdef CONFIG_ESP32S3_SPI_DMA
.sem_isr = SEM_INITIALIZER(0),
.cpuint = -ENOMEM,
.dma_channel = -1,
.dma_rxdesc = esp32s3_spi3_dma_rxdesc,
.dma_txdesc = esp32s3_spi3_dma_txdesc,
#endif
.frequency = 0,
.actual = 0,
.mode = 0,
.nbits = 0
};
#endif /* CONFIG_ESP32S3_SPI3 */
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: esp32s3_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 esp32s3_spi_set_regbits(uint32_t addr, uint32_t bits)
{
uint32_t tmp = getreg32(addr);
putreg32(tmp | bits, addr);
}
/****************************************************************************
* Name: esp32s3_spi_clr_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 esp32s3_spi_clr_regbits(uint32_t addr, uint32_t bits)
{
uint32_t tmp = getreg32(addr);
putreg32(tmp & ~bits, addr);
}
/****************************************************************************
* Name: esp32s3_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 esp32s3_spi_iomux(struct esp32s3_spi_priv_s *priv)
{
bool mapped = false;
const struct esp32s3_spi_config_s *cfg = priv->config;
/* We only need to check SPI2, SPI3 doesn't support IOMUX */
if (cfg->id == 2)
{
if (cfg->mosi_pin == SPI2_IOMUX_MOSIPIN &&
#ifndef CONFIG_ESP32S3_SPI_SWCS
cfg->cs_pin == SPI2_IOMUX_CSPIN &&
#endif
cfg->miso_pin == SPI2_IOMUX_MISOPIN &&
cfg->clk_pin == SPI2_IOMUX_CLKPIN)
{
mapped = true;
}
}
return mapped;
}
/****************************************************************************
* Name: esp32s3_spi_lock
*
* Description:
* Lock or unlock the SPI device.
*
* Input Parameters:
* dev - Device-specific state data
* lock - true: Lock SPI bus, false: unlock SPI bus
*
* Returned Value:
* The result of lock or unlock the SPI device.
*
****************************************************************************/
static int esp32s3_spi_lock(struct spi_dev_s *dev, bool lock)
{
int ret;
struct esp32s3_spi_priv_s *priv = (struct esp32s3_spi_priv_s *)dev;
if (lock)
{
ret = nxmutex_lock(&priv->lock);
}
else
{
ret = nxmutex_unlock(&priv->lock);
}
return ret;
}
/****************************************************************************
* Name: esp32s3_spi_sem_waitdone
*
* Description:
* Wait for a transfer to complete.
*
* Input Parameters:
* priv - SPI private state data
*
* Returned Value:
* None.
*
****************************************************************************/
#ifdef CONFIG_ESP32S3_SPI_DMA
static int esp32s3_spi_sem_waitdone(struct esp32s3_spi_priv_s *priv)
{
return nxsem_tickwait_uninterruptible(&priv->sem_isr, SEC2TICK(10));
}
#endif
/****************************************************************************
* Name: esp32s3_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 ESP32S3_SPI_SWCS is disabled, the driver will use hardware CS so that
* once transmission is started the hardware selects the device and when
* this transmission is done hardware deselects the device automatically.
* So, this function will do nothing.
*
* Input Parameters:
* dev - Device-specific state data
* devid - Identifies the device to select
* selected - true: slave selected, false: slave de-selected
*
* Returned Value:
* None.
*
****************************************************************************/
#ifndef CONFIG_ESP32S3_SPI_UDCS
static void esp32s3_spi_select(struct spi_dev_s *dev,
uint32_t devid, bool selected)
{
#if SPI_HAVE_SWCS
struct esp32s3_spi_priv_s *priv = (struct esp32s3_spi_priv_s *)dev;
esp32s3_gpiowrite(priv->config->cs_pin, !selected);
#endif
spiinfo("devid: %08" PRIx32 " CS: %s\n",
devid, selected ? "select" : "free");
}
#endif
/****************************************************************************
* Name: esp32s3_spi_setfrequency
*
* Description:
* Set the SPI frequency.
*
* Input Parameters:
* dev - Device-specific state data
* frequency - The requested SPI frequency
*
* Returned Value:
* Returns the current selected frequency.
*
****************************************************************************/
static uint32_t esp32s3_spi_setfrequency(struct spi_dev_s *dev,
uint32_t frequency)
{
uint32_t reg_val;
struct esp32s3_spi_priv_s *priv = (struct esp32s3_spi_priv_s *)dev;
const uint32_t duty_cycle = 128;
if (priv->frequency == frequency)
{
/* Requested frequency is the same as the current frequency. */
return priv->actual;
}
/* In HW, n, h and l fields range from 1 to 64, pre ranges from 1 to 8K.
* The value written to register is one lower than the used value.
*/
if (frequency > ((APB_CLK_FREQ / 4) * 3))
{
/* Using APB frequency directly will give us the best result here. */
reg_val = SPI_CLK_EQU_SYSCLK_M;
priv->actual = APB_CLK_FREQ;
}
else
{
/* For best duty cycle resolution, we want n to be as close to 32 as
* possible, but we also need a pre/n combo that gets us as close as
* possible to the intended frequency. To do this, we bruteforce n and
* calculate the best pre to go along with that. If there's a choice
* between pre/n combos that give the same result, use the one with the
* higher n.
*/
int32_t pre;
int32_t n;
int32_t h;
int32_t l;
int32_t bestn = -1;
int32_t bestpre = -1;
int32_t besterr = 0;
int32_t errval;
/* Start at n = 2. We need to be able to set h/l so we have at least
* one high and one low pulse.
*/
for (n = 2; n <= 64; n++)
{
/* Effectively, this does:
* pre = round((APB_CLK_FREQ / n) / frequency)
*/
pre = ((APB_CLK_FREQ / n) + (frequency / 2)) / frequency;
if (pre <= 0)
{
pre = 1;
}
if (pre > 16)
{
pre = 16;
}
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;
/* Effectively, this does:
* h = round((duty_cycle * n) / 256)
*/
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=%" PRIu32 ", actual=%" PRIu32 "\n",
priv->frequency, priv->actual);
return priv->actual;
}
/****************************************************************************
* Name: esp32s3_spi_setmode
*
* Description:
* Set the SPI mode.
*
* Input Parameters:
* dev - Device-specific state data
* mode - The requested SPI mode
*
* Returned Value:
* None.
*
****************************************************************************/
static void esp32s3_spi_setmode(struct spi_dev_s *dev,
enum spi_mode_e mode)
{
struct esp32s3_spi_priv_s *priv = (struct esp32s3_spi_priv_s *)dev;
spiinfo("mode=%d\n", mode);
/* Has the mode changed? */
if (mode != priv->mode)
{
uint32_t ck_idle_edge;
uint32_t ck_out_edge;
switch (mode)
{
case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
ck_idle_edge = 0;
ck_out_edge = 0;
break;
case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
ck_idle_edge = 0;
ck_out_edge = 1;
break;
case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
ck_idle_edge = 1;
ck_out_edge = 1;
break;
case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
ck_idle_edge = 1;
ck_out_edge = 0;
break;
default:
spierr("Invalid mode: %d\n", mode);
DEBUGPANIC();
return;
}
esp32s3_spi_clr_regbits(SPI_MISC_REG(priv->config->id),
SPI_CK_IDLE_EDGE_M);
esp32s3_spi_set_regbits(SPI_MISC_REG(priv->config->id),
VALUE_MASK(ck_idle_edge, SPI_CK_IDLE_EDGE));
esp32s3_spi_clr_regbits(SPI_USER_REG(priv->config->id),
SPI_CK_OUT_EDGE_M);
esp32s3_spi_set_regbits(SPI_USER_REG(priv->config->id),
VALUE_MASK(ck_out_edge, SPI_CK_OUT_EDGE));
priv->mode = mode;
}
}
/****************************************************************************
* Name: esp32s3_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 esp32s3_spi_setbits(struct spi_dev_s *dev, int nbits)
{
struct esp32s3_spi_priv_s *priv = (struct esp32s3_spi_priv_s *)dev;
spiinfo("nbits=%d\n", nbits);
priv->nbits = nbits;
}
/****************************************************************************
* Name: esp32s3_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 esp32s3_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: esp32s3_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.
*
****************************************************************************/
#ifdef CONFIG_ESP32S3_SPI_DMA
static void esp32s3_spi_dma_exchange(struct esp32s3_spi_priv_s *priv,
const void *txbuffer,
void *rxbuffer,
uint32_t nwords)
{
const uint32_t total = nwords * (priv->nbits / 8);
const int32_t channel = priv->dma_channel;
uint32_t bytes = total;
uint32_t n;
uint8_t *tp;
uint8_t *rp;
DEBUGASSERT((txbuffer != NULL) || (rxbuffer != NULL));
spiinfo("nwords=%" PRIu32 "\n", nwords);
tp = (uint8_t *)txbuffer;
rp = (uint8_t *)rxbuffer;
if (tp == NULL)
{
tp = rp;
}
esp32s3_spi_set_regbits(SPI_DMA_INT_CLR_REG(priv->config->id),
SPI_TRANS_DONE_INT_CLR_M);
esp32s3_spi_set_regbits(SPI_DMA_INT_ENA_REG(priv->config->id),
SPI_TRANS_DONE_INT_ENA_M);
while (bytes != 0)
{
/* Reset SPI DMA TX FIFO */
SET_GDMA_CH_BITS(DMA_OUT_CONF0_CH0_REG, priv->dma_channel,
DMA_OUT_RST_CH0);
CLR_GDMA_CH_BITS(DMA_OUT_CONF0_CH0_REG, priv->dma_channel,
DMA_OUT_RST_CH0);
/* Enable SPI DMA TX */
esp32s3_spi_set_regbits(SPI_DMA_CONF_REG(priv->config->id),
SPI_DMA_TX_ENA_M);
n = esp32s3_dma_setup(priv->dma_txdesc, SPI_DMA_DESC_NUM,
tp, bytes, true, priv->dma_channel);
esp32s3_dma_load(priv->dma_txdesc, channel, true);
esp32s3_dma_enable(channel, true);
putreg32((n * 8 - 1), SPI_MS_DLEN_REG(priv->config->id));
esp32s3_spi_set_regbits(SPI_USER_REG(priv->config->id),
SPI_USR_MOSI_M);
tp += n;
if (rp != NULL)
{
/* Reset SPI DMA RX FIFO */
SET_GDMA_CH_BITS(DMA_IN_CONF0_CH0_REG, priv->dma_channel,
DMA_IN_RST_CH0);
CLR_GDMA_CH_BITS(DMA_IN_CONF0_CH0_REG, priv->dma_channel,
DMA_IN_RST_CH0);
/* Enable SPI DMA RX */
esp32s3_spi_set_regbits(SPI_DMA_CONF_REG(priv->config->id),
SPI_DMA_RX_ENA_M);
esp32s3_dma_setup(priv->dma_rxdesc, SPI_DMA_DESC_NUM,
rp, bytes, false, priv->dma_channel);
esp32s3_dma_load(priv->dma_rxdesc, channel, false);
esp32s3_dma_enable(channel, false);
esp32s3_spi_set_regbits(SPI_USER_REG(priv->config->id),
SPI_USR_MISO_M);
rp += n;
}
else
{
esp32s3_spi_clr_regbits(SPI_USER_REG(priv->config->id),
SPI_USR_MISO_M);
}
/* Trigger start of user-defined transaction for master. */
esp32s3_spi_set_regbits(SPI_CMD_REG(priv->config->id),
SPI_UPDATE_M);
while ((getreg32(SPI_CMD_REG(priv->config->id)) & SPI_UPDATE_M) != 0)
{
;
}
esp32s3_spi_set_regbits(SPI_CMD_REG(priv->config->id), SPI_USR_M);
esp32s3_spi_sem_waitdone(priv);
bytes -= n;
}
esp32s3_spi_clr_regbits(SPI_DMA_INT_ENA_REG(priv->config->id),
SPI_TRANS_DONE_INT_ENA_M);
}
#endif
/****************************************************************************
* Name: esp32s3_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 esp32s3_spi_poll_send(struct esp32s3_spi_priv_s *priv,
uint32_t wd)
{
#ifdef CONFIG_ESP32S3_SPI_DMA
esp32s3_spi_clr_regbits(SPI_DMA_CONF_REG(priv->config->id),
SPI_DMA_TX_ENA_M);
esp32s3_spi_clr_regbits(SPI_DMA_CONF_REG(priv->config->id),
SPI_DMA_RX_ENA_M);
#endif
uint32_t val;
putreg32((priv->nbits - 1), SPI_MS_DLEN_REG(priv->config->id));
putreg32(wd, SPI_W0_REG(priv->config->id));
/* Trigger start of user-defined transaction for master. */
esp32s3_spi_set_regbits(SPI_CMD_REG(priv->config->id), SPI_UPDATE_M);
while ((getreg32(SPI_CMD_REG(priv->config->id)) & SPI_UPDATE_M) != 0)
{
;
}
esp32s3_spi_set_regbits(SPI_CMD_REG(priv->config->id), SPI_USR_M);
/* Wait for the user-defined transaction to finish. */
while ((getreg32(SPI_CMD_REG(priv->config->id)) & SPI_USR_M) != 0)
{
;
}
val = getreg32(SPI_W0_REG(priv->config->id));
spiinfo("send=0x%" PRIx32 " and recv=0x%" PRIx32 "\n", wd, val);
return val;
}
/****************************************************************************
* Name: esp32s3_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 esp32s3_spi_send(struct spi_dev_s *dev, uint32_t wd)
{
struct esp32s3_spi_priv_s *priv = (struct esp32s3_spi_priv_s *)dev;
return esp32s3_spi_poll_send(priv, wd);
}
/****************************************************************************
* Name: esp32s3_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 esp32s3_spi_poll_exchange(struct esp32s3_spi_priv_s *priv,
const void *txbuffer,
void *rxbuffer,
size_t nwords)
{
#ifdef CONFIG_ESP32S3_SPI_DMA
esp32s3_spi_clr_regbits(SPI_DMA_CONF_REG(priv->config->id),
SPI_DMA_TX_ENA_M);
esp32s3_spi_clr_regbits(SPI_DMA_CONF_REG(priv->config->id),
SPI_DMA_RX_ENA_M);
#endif
const uint32_t total_bytes = nwords * (priv->nbits / 8);
uintptr_t bytes_remaining = total_bytes;
const uint8_t *tp = (const 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(priv->config->id);
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(uintptr_t))
{
uint32_t w_wd = UINT32_MAX;
if (tp != NULL)
{
memcpy(&w_wd, tp, sizeof(uintptr_t));
tp += sizeof(uintptr_t);
}
putreg32(w_wd, data_buf_reg);
spiinfo("send=0x%" PRIx32 " data_reg=0x%" PRIxPTR "\n",
w_wd, data_buf_reg);
/* Update data_buf_reg to point to the next data buffer register. */
data_buf_reg += sizeof(uintptr_t);
}
esp32s3_spi_set_regbits(SPI_USER_REG(priv->config->id),
SPI_USR_MOSI_M);
if (rp == NULL)
{
esp32s3_spi_clr_regbits(SPI_USER_REG(priv->config->id),
SPI_USR_MISO_M);
}
else
{
esp32s3_spi_set_regbits(SPI_USER_REG(priv->config->id),
SPI_USR_MISO_M);
}
putreg32((transfer_size * 8) - 1,
SPI_MS_DLEN_REG(priv->config->id));
/* Trigger start of user-defined transaction for master. */
esp32s3_spi_set_regbits(SPI_CMD_REG(priv->config->id),
SPI_UPDATE_M);
while ((getreg32(SPI_CMD_REG(priv->config->id)) & SPI_UPDATE_M) != 0)
{
;
}
esp32s3_spi_set_regbits(SPI_CMD_REG(priv->config->id), SPI_USR_M);
/* Wait for the user-defined transaction to finish. */
while ((getreg32(SPI_CMD_REG(priv->config->id)) & 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(priv->config->id);
/* Read received data words from SPI data buffer registers. */
for (int i = 0 ; i < transfer_size; i += sizeof(uintptr_t))
{
uint32_t r_wd = getreg32(data_buf_reg);
spiinfo("recv=0x%" PRIx32 " data_reg=0x%" PRIxPTR "\n",
r_wd, data_buf_reg);
memcpy(rp, &r_wd, sizeof(uintptr_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: esp32s3_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 esp32s3_spi_exchange(struct spi_dev_s *dev,
const void *txbuffer,
void *rxbuffer,
size_t nwords)
{
struct esp32s3_spi_priv_s *priv = (struct esp32s3_spi_priv_s *)dev;
#ifdef CONFIG_ESP32S3_SPI_DMA
size_t thld = CONFIG_ESP32S3_SPI_DMATHRESHOLD;
if ((nwords * priv->nbits) / 8 > thld)
{
esp32s3_spi_dma_exchange(priv, txbuffer, rxbuffer, nwords);
}
else
#endif
{
esp32s3_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords);
}
}
#ifndef CONFIG_SPI_EXCHANGE
/****************************************************************************
* Name: esp32s3_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 esp32s3_spi_sndblock(struct spi_dev_s *dev,
const void *txbuffer,
size_t nwords)
{
spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords);
esp32s3_spi_exchange(dev, txbuffer, NULL, nwords);
}
/****************************************************************************
* Name: esp32s3_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 esp32s3_spi_recvblock(struct spi_dev_s *dev,
void *rxbuffer,
size_t nwords)
{
spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords);
esp32s3_spi_exchange(dev, NULL, rxbuffer, nwords);
}
#endif
/****************************************************************************
* Name: esp32s3_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 esp32s3_spi_trigger(struct spi_dev_s *dev)
{
return -ENOSYS;
}
#endif
#ifdef CONFIG_ESP32S3_SPI_DMA
/****************************************************************************
* Name: esp32s3_spi_dma_init
*
* Description:
* Initialize ESP32-S3 SPI connection to GDMA engine.
*
* Input Parameters:
* dev - Device-specific state data
*
* Returned Value:
* OK on success; A negated errno value on failure.
*
****************************************************************************/
static int esp32s3_spi_dma_init(struct spi_dev_s *dev)
{
struct esp32s3_spi_priv_s *priv = (struct esp32s3_spi_priv_s *)dev;
/* Enable GDMA clock for the SPI peripheral */
modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, 0, priv->config->dma_clk_bit);
/* Reset GDMA for the SPI peripheral */
modifyreg32(SYSTEM_PERIP_RST_EN0_REG, priv->config->dma_rst_bit, 0);
/* Request a GDMA channel for SPI peripheral */
priv->dma_channel = esp32s3_dma_request(priv->config->dma_periph, 1, 1,
true);
if (priv->dma_channel < 0)
{
spierr("Failed to allocate GDMA channel\n");
return ERROR;
}
/* Disable segment transaction mode for SPI Master */
putreg32((SPI_SLV_RX_SEG_TRANS_CLR_EN_M | SPI_SLV_TX_SEG_TRANS_CLR_EN_M),
SPI_DMA_CONF_REG(priv->config->id));
return OK;
}
/****************************************************************************
* Name: esp32s3_spi_dma_deinit
*
* Description:
* Deinitialize ESP32-S3 SPI GDMA engine.
*
* Input Parameters:
* dev - Device-specific state data
*
* Returned Value:
* None.
*
****************************************************************************/
void esp32s3_spi_dma_deinit(struct spi_dev_s *dev)
{
struct esp32s3_spi_priv_s *priv = (struct esp32s3_spi_priv_s *)dev;
/* Release a DMA channel from peripheral */
esp32s3_dma_release(priv->dma_channel);
/* Disable DMA clock for the SPI peripheral */
modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, priv->config->dma_clk_bit, 0);
}
#endif
/****************************************************************************
* Name: esp32s3_spi_init
*
* Description:
* Initialize ESP32-S3 SPI hardware interface.
*
* Input Parameters:
* dev - Device-specific state data
*
* Returned Value:
* OK on success; A negated errno value on failure.
*
****************************************************************************/
static int esp32s3_spi_init(struct spi_dev_s *dev)
{
struct esp32s3_spi_priv_s *priv = (struct esp32s3_spi_priv_s *)dev;
const struct esp32s3_spi_config_s *config = priv->config;
uint32_t regval;
esp32s3_gpiowrite(config->cs_pin, true);
esp32s3_gpiowrite(config->mosi_pin, true);
esp32s3_gpiowrite(config->miso_pin, true);
esp32s3_gpiowrite(config->clk_pin, true);
#if SPI_HAVE_SWCS
esp32s3_configgpio(config->cs_pin, OUTPUT_FUNCTION_1);
esp32s3_gpio_matrix_out(config->cs_pin, SIG_GPIO_OUT_IDX, 0, 0);
#endif
/* SPI3 doesn't have IOMUX, if SPI3 is enabled use GPIO Matrix for both */
if (esp32s3_spi_iomux(priv))
{
#if !SPI_HAVE_SWCS
esp32s3_configgpio(config->cs_pin, OUTPUT_FUNCTION_5);
esp32s3_gpio_matrix_out(config->cs_pin, SIG_GPIO_OUT_IDX, 0, 0);
#endif
esp32s3_configgpio(config->mosi_pin, OUTPUT_FUNCTION_5);
esp32s3_gpio_matrix_out(config->mosi_pin, SIG_GPIO_OUT_IDX, 0, 0);
esp32s3_configgpio(config->miso_pin, INPUT_FUNCTION_5 | PULLUP);
esp32s3_gpio_matrix_out(config->miso_pin, SIG_GPIO_OUT_IDX, 0, 0);
esp32s3_configgpio(config->clk_pin, OUTPUT_FUNCTION_5);
esp32s3_gpio_matrix_out(config->clk_pin, SIG_GPIO_OUT_IDX, 0, 0);
}
else
{
#if !SPI_HAVE_SWCS
esp32s3_configgpio(config->cs_pin, OUTPUT);
esp32s3_gpio_matrix_out(config->cs_pin, config->cs_outsig, 0, 0);
#endif
esp32s3_configgpio(config->mosi_pin, OUTPUT);
esp32s3_gpio_matrix_out(config->mosi_pin, config->mosi_outsig, 0, 0);
esp32s3_configgpio(config->miso_pin, INPUT | PULLUP);
esp32s3_gpio_matrix_in(config->miso_pin, config->miso_insig, 0);
esp32s3_configgpio(config->clk_pin, OUTPUT);
esp32s3_gpio_matrix_out(config->clk_pin, config->clk_outsig, 0, 0);
}
modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, 0, config->clk_bit);
modifyreg32(SYSTEM_PERIP_RST_EN0_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(priv->config->id));
putreg32(0, SPI_USER1_REG(priv->config->id));
putreg32(0, SPI_SLAVE_REG(priv->config->id));
putreg32(SPI_CS1_DIS_M | SPI_CS2_DIS_M,
SPI_MISC_REG(priv->config->id));
regval = SPI_CLK_EN_M | SPI_MST_CLK_ACTIVE_M | SPI_MST_CLK_SEL_M;
putreg32(regval, SPI_CLK_GATE_REG(priv->config->id));
#if SPI_HAVE_SWCS
esp32s3_spi_set_regbits(SPI_MISC_REG(priv->config->id), SPI_CS0_DIS_M);
#endif
putreg32(0, SPI_CTRL_REG(priv->config->id));
putreg32(VALUE_MASK(0, SPI_CS_HOLD_TIME),
SPI_USER1_REG(priv->config->id));
#ifdef CONFIG_ESP32S3_SPI_DMA
if (esp32s3_spi_dma_init(dev) != OK)
{
modifyreg32(SYSTEM_PERIP_RST_EN0_REG, 0, priv->config->clk_bit);
modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, priv->config->clk_bit, 0);
return ERROR;
}
#endif
esp32s3_spi_setfrequency(dev, config->clk_freq);
esp32s3_spi_setbits(dev, config->width);
esp32s3_spi_setmode(dev, config->mode);
return OK;
}
/****************************************************************************
* Name: esp32s3_spi_deinit
*
* Description:
* Deinitialize ESP32-S3 SPI hardware interface.
*
* Input Parameters:
* dev - Device-specific state data
*
* Returned Value:
* None.
*
****************************************************************************/
static void esp32s3_spi_deinit(struct spi_dev_s *dev)
{
struct esp32s3_spi_priv_s *priv = (struct esp32s3_spi_priv_s *)dev;
#ifdef CONFIG_ESP32S3_SPI_DMA
esp32s3_spi_dma_deinit(dev);
#endif
modifyreg32(SYSTEM_PERIP_RST_EN0_REG, 0, priv->config->clk_bit);
modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, priv->config->clk_bit, 0);
priv->frequency = 0;
priv->actual = 0;
priv->mode = SPIDEV_MODE0;
priv->nbits = 0;
}
/****************************************************************************
* Name: esp32s3_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.
*
****************************************************************************/
#ifdef CONFIG_ESP32S3_SPI_DMA
static int esp32s3_spi_interrupt(int irq, void *context, void *arg)
{
struct esp32s3_spi_priv_s *priv = (struct esp32s3_spi_priv_s *)arg;
/* Write 1 to clear interrupt bit */
esp32s3_spi_set_regbits(SPI_DMA_INT_CLR_REG(priv->config->id),
SPI_TRANS_DONE_INT_CLR_M);
nxsem_post(&priv->sem_isr);
return 0;
}
#endif
/****************************************************************************
* Name: esp32s3_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 *esp32s3_spibus_initialize(int port)
{
struct spi_dev_s *spi_dev;
struct esp32s3_spi_priv_s *priv;
switch (port)
{
#ifdef CONFIG_ESP32S3_SPI2
case ESP32S3_SPI2:
priv = &esp32s3_spi2_priv;
break;
#endif
#ifdef CONFIG_ESP32S3_SPI3
case ESP32S3_SPI3:
priv = &esp32s3_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;
}
#ifdef CONFIG_ESP32S3_SPI_DMA
/* If a CPU Interrupt was previously allocated, then deallocate it */
if (priv->cpuint != -ENOMEM)
{
/* Disable the provided CPU Interrupt to configure it. */
up_disable_irq(priv->config->irq);
esp32s3_teardown_irq(priv->cpu, priv->config->periph, priv->cpuint);
irq_detach(priv->config->irq);
priv->cpuint = -ENOMEM;
priv->cpu = -ENODEV;
}
/* Set up to receive peripheral interrupts on the current CPU */
priv->cpu = up_cpu_index();
priv->cpuint = esp32s3_setup_irq(priv->cpu, priv->config->periph,
ESP32S3_INT_PRIO_DEF,
ESP32S3_CPUINT_LEVEL);
if (priv->cpuint < 0)
{
/* Failed to allocate a CPU interrupt of this type. */
nxmutex_unlock(&priv->lock);
return NULL;
}
/* Attach and enable the IRQ */
if (irq_attach(priv->config->irq, esp32s3_spi_interrupt, priv) != OK)
{
/* Failed to attach IRQ, so CPU interrupt must be freed. */
esp32s3_teardown_irq(priv->cpu, priv->config->periph, priv->cpuint);
priv->cpuint = -ENOMEM;
nxmutex_unlock(&priv->lock);
return NULL;
}
/* Enable the CPU interrupt that is linked to the SPI device. */
up_enable_irq(priv->config->irq);
#endif
if (esp32s3_spi_init(spi_dev) != OK)
{
#ifdef CONFIG_ESP32S3_SPI_DMA
up_disable_irq(priv->config->irq);
esp32s3_teardown_irq(priv->cpu, priv->config->periph, priv->cpuint);
irq_detach(priv->config->irq);
priv->cpuint = -ENOMEM;
#endif
nxmutex_unlock(&priv->lock);
return NULL;
}
priv->refs++;
nxmutex_unlock(&priv->lock);
return spi_dev;
}
/****************************************************************************
* Name: esp32s3_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 esp32s3_spibus_uninitialize(struct spi_dev_s *dev)
{
struct esp32s3_spi_priv_s *priv = (struct esp32s3_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;
}
#ifdef CONFIG_ESP32S3_SPI_DMA
up_disable_irq(priv->config->irq);
esp32s3_teardown_irq(priv->cpu, priv->config->periph, priv->cpuint);
irq_detach(priv->config->irq);
priv->cpuint = -ENOMEM;
#endif
esp32s3_spi_deinit(dev);
nxmutex_unlock(&priv->lock);
return OK;
}
#endif /* CONFIG_ESP32S3_SPI */