2021-03-30 01:23:02 -05:00

1506 lines
42 KiB
C

/****************************************************************************
* 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 <sys/types.h>
#include <inttypes.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/spi/spi.h>
#include <arch/board/board.h>
#include "esp32_spi.h"
#include "esp32_gpio.h"
#include "esp32_cpuint.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
****************************************************************************/
/* 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
#ifndef MIN
# define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
/****************************************************************************
* Private Types
****************************************************************************/
/* SPI Device hardware configuration */
struct esp32_spi_config_s
{
uint32_t reg_base; /* SPI register base address */
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 cpu; /* CPU ID */
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 */
};
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; /* Referernce count */
/* Held while chip is selected for mutual exclusion */
sem_t exclsem;
/* Interrupt wait semaphore */
sem_t sem_isr;
int cpuint; /* SPI interrupt 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(FAR struct spi_dev_s *dev, bool lock);
#ifndef CONFIG_ESP32_SPI_UDCS
static void esp32_spi_select(FAR struct spi_dev_s *dev,
uint32_t devid, bool selected);
#endif
static uint32_t esp32_spi_setfrequency(FAR struct spi_dev_s *dev,
uint32_t frequency);
static void esp32_spi_setmode(FAR struct spi_dev_s *dev,
enum spi_mode_e mode);
static void esp32_spi_setbits(FAR struct spi_dev_s *dev, int nbits);
#ifdef CONFIG_SPI_HWFEATURES
static int esp32_spi_hwfeatures(FAR struct spi_dev_s *dev,
spi_hwfeatures_t features);
#endif
static uint32_t esp32_spi_send(FAR struct spi_dev_s *dev, uint32_t wd);
static void esp32_spi_exchange(FAR struct spi_dev_s *dev,
FAR const void *txbuffer,
FAR void *rxbuffer, size_t nwords);
#ifndef CONFIG_SPI_EXCHANGE
static void esp32_spi_sndblock(FAR struct spi_dev_s *dev,
FAR const void *txbuffer,
size_t nwords);
static void esp32_spi_recvblock(FAR struct spi_dev_s *dev,
FAR void *rxbuffer,
size_t nwords);
#endif
#ifdef CONFIG_SPI_TRIGGER
static int esp32_spi_trigger(FAR struct spi_dev_s *dev);
#endif
static void esp32_spi_init(FAR struct spi_dev_s *dev);
static void esp32_spi_deinit(FAR struct spi_dev_s *dev);
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CONFIG_ESP32_SPI2
static const struct esp32_spi_config_s esp32_spi2_config =
{
.reg_base = REG_SPI_BASE(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,
.cpu = 0,
.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
};
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
};
#endif /* CONFIG_ESP32_SPI2 */
#ifdef CONFIG_ESP32_SPI3
static const struct esp32_spi_config_s esp32_spi3_config =
{
.reg_base = REG_SPI_BASE(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,
.cpu = 0,
.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
};
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
};
#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_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_spi_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_spi_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_spi_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_spi_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_spi_priv_s *priv)
{
bool mapped = false;
const struct esp32_spi_config_s *cfg = priv->config;
if (REG_SPI_BASE(2) == cfg->reg_base)
{
if (cfg->mosi_pin == SPI2_IOMUX_MOSIPIN &&
#ifndef CONFIG_ESP32_SPI_SWCS
cfg->cs_pin == SPI2_IOMUX_CSPIN &&
#endif
cfg->miso_pin == SPI2_IOMUX_MISOPIN &&
cfg->clk_pin == SPI2_IOMUX_CLKPIN)
{
mapped = true;
}
}
else if (REG_SPI_BASE(3) == cfg->reg_base)
{
if (cfg->mosi_pin == SPI3_IOMUX_MOSIPIN &&
#ifndef CONFIG_ESP32_SPI_SWCS
cfg->cs_pin == SPI3_IOMUX_CSPIN &&
#endif
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(FAR struct spi_dev_s *dev, bool lock)
{
int ret;
FAR struct esp32_spi_priv_s *priv = (FAR struct esp32_spi_priv_s *)dev;
if (lock)
{
ret = nxsem_wait_uninterruptible(&priv->exclsem);
}
else
{
ret = nxsem_post(&priv->exclsem);
}
return ret;
}
/****************************************************************************
* Name: esp32_spi_sem_waitdone
*
* Description:
* Wait for a transfer to complete
*
****************************************************************************/
static int esp32_spi_sem_waitdone(FAR struct esp32_spi_priv_s *priv)
{
int ret;
struct timespec abstime;
clock_gettime(CLOCK_REALTIME, &abstime);
abstime.tv_sec += 10;
abstime.tv_nsec += 0;
ret = nxsem_timedwait_uninterruptible(&priv->sem_isr, &abstime);
return ret;
}
/****************************************************************************
* 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(FAR struct spi_dev_s *dev,
uint32_t devid, bool selected)
{
#ifdef CONFIG_ESP32_SPI_SWCS
FAR struct esp32_spi_priv_s *priv = (FAR struct esp32_spi_priv_s *)dev;
bool value = selected ? false : true;
esp32_gpiowrite(priv->config->cs_pin, value);
#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(FAR struct spi_dev_s *dev,
uint32_t frequency)
{
uint32_t reg_val;
FAR struct esp32_spi_priv_s *priv = (FAR 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;
esp32_spi_set_reg(priv, SPI_CLOCK_OFFSET, reg_val);
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 SPI mode requested
*
* Returned Value:
* none
*
****************************************************************************/
static void esp32_spi_setmode(FAR struct spi_dev_s *dev,
enum spi_mode_e mode)
{
uint32_t ck_idle_edge;
uint32_t ck_out_edge;
uint32_t delay_mode;
FAR struct esp32_spi_priv_s *priv = (FAR 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;
}
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_OUT_EDGE_M);
esp32_spi_set_regbits(priv, SPI_USER_OFFSET,
(ck_out_edge << SPI_CK_OUT_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,
(delay_mode << SPI_MISO_DELAY_MODE_S) |
(delay_mode << SPI_MOSI_DELAY_MODE_S));
priv->mode = mode;
}
}
/****************************************************************************
* Name: esp32_spi_setbits
*
* Description:
* Set the number if 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(FAR struct spi_dev_s *dev, int nbits)
{
FAR struct esp32_spi_priv_s *priv = (FAR struct esp32_spi_priv_s *)dev;
spiinfo("nbits=%d\n", nbits);
priv->nbits = nbits;
esp32_spi_set_reg(priv, SPI_MISO_DLEN_OFFSET,
(priv->nbits - 1) << SPI_USR_MISO_DBITLEN_S);
esp32_spi_set_reg(priv, SPI_MOSI_DLEN_OFFSET,
(priv->nbits - 1) << SPI_USR_MOSI_DBITLEN_S);
}
/****************************************************************************
* 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(FAR 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(FAR struct esp32_spi_priv_s *priv,
FAR const void *txbuffer,
FAR 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;
#ifdef CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP
uint8_t *alloctp;
uint8_t *allocrp;
#endif
DEBUGASSERT((txbuffer != NULL) || (rxbuffer != NULL));
/* If the buffer comes from PSRAM, allocate a new one from DRAM */
#ifdef CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP
if (esp32_ptr_extram(txbuffer))
{
alloctp = xtensa_imm_malloc(total);
DEBUGASSERT(alloctp != NULL);
memcpy(alloctp, txbuffer, total);
tp = alloctp;
}
else
#endif
{
tp = (uint8_t *)txbuffer;
}
#ifdef CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP
if (esp32_ptr_extram(rxbuffer))
{
allocrp = xtensa_imm_malloc(total);
DEBUGASSERT(allocrp != NULL);
rp = allocrp;
}
else
#endif
{
rp = (uint8_t *)rxbuffer;
}
if (!tp)
{
tp = rp;
}
esp32_spi_reset_regbits(priv, SPI_SLAVE_OFFSET, SPI_TRANS_DONE_M);
esp32_spi_set_regbits(priv, SPI_SLAVE_OFFSET, SPI_INT_EN_M);
while (bytes)
{
esp32_spi_set_reg(priv, SPI_DMA_IN_LINK_OFFSET, 0);
esp32_spi_set_reg(priv, SPI_DMA_OUT_LINK_OFFSET, 0);
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_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);
n = esp32_dma_init(s_dma_txdesc[priv->config->dma_chan - 1],
SPI_DMADESC_NUM, tp, bytes);
regval = (uintptr_t)s_dma_txdesc[priv->config->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_MOSI_DLEN_OFFSET, bytes * 8 - 1);
esp32_spi_set_regbits(priv, SPI_USER_OFFSET, SPI_USR_MOSI_M);
tp += n;
if (rp)
{
esp32_dma_init(s_dma_rxdesc[priv->config->dma_chan - 1],
SPI_DMADESC_NUM, rp, bytes);
regval = (uintptr_t)s_dma_rxdesc[priv->config->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_MISO_DLEN_OFFSET, bytes * 8 - 1);
esp32_spi_set_regbits(priv, SPI_USER_OFFSET, SPI_USR_MISO_M);
rp += n;
}
else
{
esp32_spi_reset_regbits(priv, SPI_USER_OFFSET, SPI_USR_MISO_M);
}
esp32_spi_set_regbits(priv, SPI_CMD_OFFSET, SPI_USR_M);
esp32_spi_sem_waitdone(priv);
bytes -= n;
}
esp32_spi_reset_regbits(priv, SPI_SLAVE_OFFSET, SPI_INT_EN_M);
#ifdef CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP
if (esp32_ptr_extram(rxbuffer))
{
memcpy(rxbuffer, allocrp, total);
xtensa_imm_free(allocrp);
}
#endif
#ifdef CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP
if (esp32_ptr_extram(txbuffer))
{
xtensa_imm_free(alloctp);
}
#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(FAR struct esp32_spi_priv_s *priv,
uint32_t wd)
{
uint32_t val;
esp32_spi_set_reg(priv, SPI_W0_OFFSET, wd);
esp32_spi_set_regbits(priv, SPI_CMD_OFFSET, SPI_USR_M);
while (esp32_spi_get_reg(priv, SPI_CMD_OFFSET) & SPI_USR_M)
{
;
}
val = esp32_spi_get_reg(priv, SPI_W0_OFFSET);
spiinfo("send=%x and recv=%x\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(FAR struct spi_dev_s *dev, uint32_t wd)
{
FAR struct esp32_spi_priv_s *priv = (FAR 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(FAR struct esp32_spi_priv_s *priv,
FAR const void *txbuffer,
FAR void *rxbuffer,
size_t nwords)
{
int i;
for (i = 0 ; i < nwords; i++)
{
uint32_t w_wd = 0xffff;
uint32_t r_wd;
if (txbuffer)
{
if (priv->nbits == 8)
{
w_wd = ((uint8_t *)txbuffer)[i];
}
else
{
w_wd = ((uint16_t *)txbuffer)[i];
}
}
r_wd = esp32_spi_poll_send(priv, w_wd);
if (rxbuffer)
{
if (priv->nbits == 8)
{
((uint8_t *)rxbuffer)[i] = r_wd;
}
else
{
((uint16_t *)rxbuffer)[i] = r_wd;
}
}
}
}
/****************************************************************************
* 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(FAR struct spi_dev_s *dev,
FAR const void *txbuffer,
FAR void *rxbuffer,
size_t nwords)
{
FAR struct esp32_spi_priv_s *priv = (FAR 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
* buffer - 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(FAR struct spi_dev_s *dev,
FAR 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
* buffer - 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(FAR struct spi_dev_s *dev,
FAR 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(FAR 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(FAR struct spi_dev_s *dev)
{
FAR struct esp32_spi_priv_s *priv = (FAR struct esp32_spi_priv_s *)dev;
const struct esp32_spi_config_s *config = priv->config;
/* Initialize the SPI semaphore that enforces mutually exclusive access */
nxsem_init(&priv->exclsem, 0, 1);
esp32_gpiowrite(config->cs_pin, 1);
esp32_gpiowrite(config->mosi_pin, 1);
esp32_gpiowrite(config->miso_pin, 1);
esp32_gpiowrite(config->clk_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->mosi_pin, OUTPUT_FUNCTION_2);
esp32_gpio_matrix_out(config->mosi_pin, SIG_GPIO_OUT_IDX, 0, 0);
esp32_configgpio(config->miso_pin, INPUT_FUNCTION_2 | PULLUP);
esp32_gpio_matrix_out(config->miso_pin, SIG_GPIO_OUT_IDX, 0, 0);
esp32_configgpio(config->clk_pin, OUTPUT_FUNCTION_2);
esp32_gpio_matrix_out(config->clk_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->mosi_pin, OUTPUT_FUNCTION_3);
esp32_gpio_matrix_out(config->mosi_pin, config->mosi_outsig, 0, 0);
esp32_configgpio(config->miso_pin, INPUT_FUNCTION_3 | PULLUP);
esp32_gpio_matrix_in(config->miso_pin, config->miso_insig, 0);
esp32_configgpio(config->clk_pin, OUTPUT_FUNCTION_3);
esp32_gpio_matrix_out(config->clk_pin, config->clk_outsig, 0, 0);
}
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_MISO_M |
SPI_USR_MOSI_M |
SPI_CS_HOLD_M);
esp32_spi_set_reg(priv, SPI_USER1_OFFSET, 0);
esp32_spi_set_reg(priv, SPI_SLAVE_OFFSET, 0);
esp32_spi_set_reg(priv, SPI_PIN_OFFSET, SPI_CS1_DIS_M | SPI_CS2_DIS_M);
#ifdef CONFIG_ESP32_SPI_SWCS
esp32_spi_set_regbits(priv, SPI_PIN_OFFSET, SPI_CS0_DIS_M);
#endif
esp32_spi_set_reg(priv, SPI_CTRL_OFFSET, 0);
esp32_spi_set_reg(priv, SPI_CTRL2_OFFSET, (0 << SPI_HOLD_TIME_S));
if (config->use_dma)
{
nxsem_init(&priv->sem_isr, 0, 0);
nxsem_set_protocol(&priv->sem_isr, SEM_PRIO_NONE);
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));
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);
}
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(FAR struct spi_dev_s *dev)
{
FAR struct esp32_spi_priv_s *priv = (FAR struct esp32_spi_priv_s *)dev;
if (priv->config->use_dma)
{
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->frequency = 0;
priv->actual = 0;
priv->mode = SPIDEV_MODE0;
priv->nbits = 0;
}
/****************************************************************************
* Name: esp32_spi_interrupt
*
* Description:
* Common SPI DMA interrupt handler
*
* Input Parameters:
* arg - SPI controller private data
*
* Returned Value:
* Standard interrupt return value.
*
****************************************************************************/
static int esp32_spi_interrupt(int irq, void *context, FAR void *arg)
{
FAR struct esp32_spi_priv_s *priv = (FAR struct esp32_spi_priv_s *)arg;
esp32_spi_reset_regbits(priv, SPI_SLAVE_OFFSET, SPI_TRANS_DONE_M);
nxsem_post(&priv->sem_isr);
return 0;
}
/****************************************************************************
* Name: esp32_spibus_initialize
*
* Description:
* Initialize the selected SPI bus
*
* Input Parameters:
* Port number (for hardware that has multiple SPI interfaces)
*
* Returned Value:
* Valid SPI device structure reference on success; a NULL on failure
*
****************************************************************************/
FAR struct spi_dev_s *esp32_spibus_initialize(int port)
{
int ret;
FAR struct spi_dev_s *spi_dev;
FAR struct esp32_spi_priv_s *priv;
irqstate_t flags;
switch (port)
{
#ifdef CONFIG_ESP32_SPI2
case 2:
priv = &esp32_spi2_priv;
break;
#endif
#ifdef CONFIG_ESP32_SPI3
case 3:
priv = &esp32_spi3_priv;
break;
#endif
default:
return NULL;
}
spi_dev = (FAR struct spi_dev_s *)priv;
flags = enter_critical_section();
if ((volatile int)priv->refs != 0)
{
leave_critical_section(flags);
return spi_dev;
}
if (priv->config->use_dma)
{
priv->cpuint = esp32_alloc_levelint(1);
if (priv->cpuint < 0)
{
leave_critical_section(flags);
return NULL;
}
up_disable_irq(priv->cpuint);
esp32_attach_peripheral(priv->config->cpu,
priv->config->periph,
priv->cpuint);
ret = irq_attach(priv->config->irq, esp32_spi_interrupt, priv);
if (ret != OK)
{
esp32_detach_peripheral(priv->config->cpu,
priv->config->periph,
priv->cpuint);
esp32_free_cpuint(priv->cpuint);
leave_critical_section(flags);
return NULL;
}
up_enable_irq(priv->cpuint);
}
esp32_spi_init(spi_dev);
priv->refs++;
leave_critical_section(flags);
return spi_dev;
}
/****************************************************************************
* Name: esp32_spibus_uninitialize
*
* Description:
* Uninitialize an SPI bus
*
****************************************************************************/
int esp32_spibus_uninitialize(FAR struct spi_dev_s *dev)
{
irqstate_t flags;
FAR struct esp32_spi_priv_s *priv = (FAR struct esp32_spi_priv_s *)dev;
DEBUGASSERT(dev);
if (priv->refs == 0)
{
return ERROR;
}
flags = enter_critical_section();
if (--priv->refs)
{
leave_critical_section(flags);
return OK;
}
leave_critical_section(flags);
if (priv->config->use_dma)
{
up_disable_irq(priv->cpuint);
esp32_detach_peripheral(priv->config->cpu,
priv->config->periph,
priv->cpuint);
esp32_free_cpuint(priv->cpuint);
nxsem_destroy(&priv->sem_isr);
}
esp32_spi_deinit(dev);
nxsem_destroy(&priv->exclsem);
return OK;
}
#endif /* CONFIG_ESP32_SPI */