1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-25 22:43:50 +01:00

Merge pull request #14261 from bergzand/pr/sam0_common/spi_dma

sam0_common: Make SPI peripheral DMA compatible
This commit is contained in:
benpicco 2020-06-14 18:25:37 +02:00 committed by GitHub
commit 1992f57765
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 221 additions and 9 deletions

View File

@ -74,6 +74,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_3,
.mosi_pad = SPI_PAD_MOSI_0_SCK_1,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM1_DMAC_ID_TX,
.rx_trigger = SERCOM1_DMAC_ID_RX,
#endif
},
{
.dev = &SERCOM2->SPI,
@ -86,6 +90,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_3,
.mosi_pad = SPI_PAD_MOSI_0_SCK_1,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM2_DMAC_ID_TX,
.rx_trigger = SERCOM2_DMAC_ID_RX,
#endif
}
};

View File

@ -103,6 +103,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_0,
.mosi_pad = SPI_PAD_MOSI_2_SCK_3,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM0_DMAC_ID_TX,
.rx_trigger = SERCOM0_DMAC_ID_RX,
#endif
}
};

View File

@ -219,6 +219,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_0,
.mosi_pad = SPI_PAD_MOSI_2_SCK_3,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM4_DMAC_ID_TX,
.rx_trigger = SERCOM4_DMAC_ID_RX,
#endif
}
};

View File

@ -224,6 +224,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_0,
.mosi_pad = SPI_PAD_MOSI_2_SCK_3,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM4_DMAC_ID_TX,
.rx_trigger = SERCOM4_DMAC_ID_RX,
#endif
}
};

View File

@ -256,6 +256,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_0,
.mosi_pad = SPI_PAD_MOSI_2_SCK_3,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM0_DMAC_ID_TX,
.rx_trigger = SERCOM0_DMAC_ID_RX,
#endif
},
{ /* EXT2 */
.dev = &SERCOM1->SPI,
@ -268,6 +272,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_0,
.mosi_pad = SPI_PAD_MOSI_2_SCK_3,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM1_DMAC_ID_TX,
.rx_trigger = SERCOM1_DMAC_ID_RX,
#endif
},
{ /* EXT3 */
.dev = &SERCOM5->SPI,
@ -280,6 +288,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_0,
.mosi_pad = SPI_PAD_MOSI_2_SCK_3,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM5_DMAC_ID_TX,
.rx_trigger = SERCOM5_DMAC_ID_RX,
#endif
}
};

View File

@ -162,6 +162,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_3,
.mosi_pad = SPI_PAD_MOSI_0_SCK_1,
.gclk_src = SAM0_GCLK_48MHZ,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM4_DMAC_ID_TX,
.rx_trigger = SERCOM4_DMAC_ID_RX,
#endif
},
{ /* EXT2, EXT3 */
@ -175,6 +179,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_3,
.mosi_pad = SPI_PAD_MOSI_0_SCK_1,
.gclk_src = SAM0_GCLK_48MHZ,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM6_DMAC_ID_TX,
.rx_trigger = SERCOM6_DMAC_ID_RX,
#endif
}
};

View File

@ -120,6 +120,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_0,
.mosi_pad = SPI_PAD_MOSI_2_SCK_3,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM0_DMAC_ID_TX,
.rx_trigger = SERCOM0_DMAC_ID_RX,
#endif
}
};

View File

@ -230,6 +230,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_0,
.mosi_pad = SPI_PAD_MOSI_2_SCK_3,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM4_DMAC_ID_TX,
.rx_trigger = SERCOM4_DMAC_ID_RX,
#endif
},
{
.dev = &SERCOM5->SPI,
@ -242,6 +246,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_0,
.mosi_pad = SPI_PAD_MOSI_2_SCK_3,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM5_DMAC_ID_TX,
.rx_trigger = SERCOM5_DMAC_ID_RX,
#endif
}
};

View File

@ -95,6 +95,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_0,
.mosi_pad = SPI_PAD_MOSI_2_SCK_3,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM4_DMAC_ID_TX,
.rx_trigger = SERCOM4_DMAC_ID_RX,
#endif
},
{ /* EXT1 & EXT3 Pin Header */
.dev = &(SERCOM5->SPI),
@ -107,6 +111,11 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_0,
.mosi_pad = SPI_PAD_MOSI_2_SCK_3,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
/* The SAML21 doesn't support DMA triggers on SERCOM5 */
.tx_trigger = DMA_TRIGGER_DISABLED,
.rx_trigger = DMA_TRIGGER_DISABLED,
#endif
}
};

View File

@ -103,6 +103,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_0,
.mosi_pad = SPI_PAD_MOSI_2_SCK_3,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM4_DMAC_ID_TX,
.rx_trigger = SERCOM4_DMAC_ID_RX,
#endif
}
};

View File

@ -173,6 +173,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_3,
.mosi_pad = SPI_PAD_MOSI_0_SCK_1,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM1_DMAC_ID_TX,
.rx_trigger = SERCOM1_DMAC_ID_RX,
#endif
}
};

View File

@ -164,6 +164,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_2,
.mosi_pad = SPI_PAD_MOSI_0_SCK_1,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM1_DMAC_ID_TX,
.rx_trigger = SERCOM1_DMAC_ID_RX,
#endif
},
{ /* D0 … D2 (user pins) */
.dev = &SERCOM0->SPI,
@ -176,6 +180,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_2,
.mosi_pad = SPI_PAD_MOSI_0_SCK_1,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM0_DMAC_ID_TX,
.rx_trigger = SERCOM0_DMAC_ID_RX,
#endif
},
};

View File

@ -128,6 +128,10 @@ static const spi_conf_t spi_config[] = {
.miso_pad = SPI_PAD_MISO_0,
.mosi_pad = SPI_PAD_MOSI_2_SCK_3,
.gclk_src = SAM0_GCLK_MAIN,
#ifdef MODULE_PERIPH_DMA
.tx_trigger = SERCOM0_DMAC_ID_TX,
.rx_trigger = SERCOM0_DMAC_ID_RX,
#endif
}
};

View File

@ -39,8 +39,10 @@ extern "C" {
*/
#define PERIPH_SPI_NEEDS_INIT_CS
#define PERIPH_SPI_NEEDS_TRANSFER_BYTE
#ifndef MODULE_PERIPH_DMA
#define PERIPH_SPI_NEEDS_TRANSFER_REG
#define PERIPH_SPI_NEEDS_TRANSFER_REGS
#endif
/** @} */
/**
@ -275,6 +277,10 @@ typedef struct {
spi_misopad_t miso_pad; /**< pad to use for MISO line */
spi_mosipad_t mosi_pad; /**< pad to use for MOSI and CLK line */
uint8_t gclk_src; /**< GCLK source which supplys SERCOM */
#ifdef MODULE_PERIPH_DMA
uint8_t tx_trigger; /**< DMA trigger */
uint8_t rx_trigger; /**< DMA trigger */
#endif
} spi_conf_t;
/** @} */

View File

@ -29,6 +29,7 @@
#include "mutex.h"
#include "assert.h"
#include "periph/spi.h"
#include "pm_layered.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
@ -38,6 +39,18 @@
*/
static mutex_t locks[SPI_NUMOF];
#ifdef MODULE_PERIPH_DMA
struct dma_state {
dma_t tx_dma;
dma_t rx_dma;
};
static struct dma_state _dma_state[SPI_NUMOF];
static DmacDescriptor DMA_DESCRIPTOR_ATTRS tx_desc[SPI_NUMOF];
static DmacDescriptor DMA_DESCRIPTOR_ATTRS rx_desc[SPI_NUMOF];
#endif
/**
* @brief Shortcut for accessing the used SPI SERCOM device
*/
@ -56,6 +69,17 @@ static inline void poweroff(spi_t bus)
sercom_clk_dis(dev(bus));
}
static inline bool _use_dma(spi_t bus)
{
#ifdef MODULE_PERIPH_DMA
return (spi_config[bus].tx_trigger != DMA_TRIGGER_DISABLED) &&
(spi_config[bus].rx_trigger != DMA_TRIGGER_DISABLED);
#else
(void)bus;
return false;
#endif
}
void spi_init(spi_t bus)
{
/* make sure given bus is good */
@ -82,8 +106,23 @@ void spi_init(spi_t bus)
* no synchronization needed, as SERCOM device is not enabled */
dev(bus)->CTRLB.reg = (SERCOM_SPI_CTRLB_CHSIZE(0) | SERCOM_SPI_CTRLB_RXEN);
#ifdef MODULE_PERIPH_DMA
if (_use_dma(bus)) {
_dma_state[bus].rx_dma = dma_acquire_channel();
_dma_state[bus].tx_dma = dma_acquire_channel();
dma_setup(_dma_state[bus].tx_dma,
spi_config[bus].tx_trigger, 0, false);
dma_setup(_dma_state[bus].rx_dma,
spi_config[bus].rx_trigger, 1, true);
dma_prepare(_dma_state[bus].rx_dma, DMAC_BTCTRL_BEATSIZE_BYTE_Val,
(void*)&dev(bus)->DATA.reg, NULL, 1, 0);
dma_prepare(_dma_state[bus].tx_dma, DMAC_BTCTRL_BEATSIZE_BYTE_Val,
NULL, (void*)&dev(bus)->DATA.reg, 0, 0);
}
#endif
/* put device back to sleep */
poweroff(bus);
}
void spi_init_pins(spi_t bus)
@ -164,21 +203,13 @@ void spi_release(spi_t bus)
mutex_unlock(&locks[bus]);
}
void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
const void *out, void *in, size_t len)
static void _blocking_transfer(spi_t bus, const void *out, void *in, size_t len)
{
const uint8_t *out_buf = out;
uint8_t *in_buf = in;
assert(out || in);
if (cs != SPI_CS_UNDEF) {
gpio_clear((gpio_t)cs);
}
for (int i = 0; i < (int)len; i++) {
uint8_t tmp = (out_buf) ? out_buf[i] : 0;
while (!(dev(bus)->INTFLAG.reg & SERCOM_SPI_INTFLAG_DRE)) {}
dev(bus)->DATA.reg = tmp;
while (!(dev(bus)->INTFLAG.reg & SERCOM_SPI_INTFLAG_RXC)) {}
tmp = (uint8_t)dev(bus)->DATA.reg;
@ -186,6 +217,100 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
in_buf[i] = tmp;
}
}
}
#ifdef MODULE_PERIPH_DMA
static void _dma_execute(spi_t bus)
{
#if defined(CPU_FAM_SAMD21)
pm_block(SAMD21_PM_IDLE_1);
#endif
dma_start(_dma_state[bus].rx_dma);
dma_start(_dma_state[bus].tx_dma);
dma_wait(_dma_state[bus].rx_dma);
#if defined(CPU_FAM_SAMD21)
pm_unblock(SAMD21_PM_IDLE_1);
#endif
}
static void _dma_transfer(spi_t bus, const uint8_t *out, uint8_t *in,
size_t len)
{
uint8_t tmp = 0;
const uint8_t *out_addr = out ? out + len : &tmp;
uint8_t *in_addr = in ? in + len : &tmp;
dma_prepare_dst(_dma_state[bus].rx_dma, in_addr, len, in ? true : false);
dma_prepare_src(_dma_state[bus].tx_dma, out_addr, len, out ? true : false);
_dma_execute(bus);
}
static void _dma_transfer_regs(spi_t bus, uint8_t reg, const uint8_t *out,
uint8_t *in, size_t len)
{
uint8_t tmp;
const uint8_t *out_addr = out ? out + len : &tmp;
uint8_t *in_addr = in ? in + len : &tmp;
dma_prepare_dst(_dma_state[bus].rx_dma, &tmp, 1, false);
dma_prepare_src(_dma_state[bus].tx_dma, &reg, 1, false);
dma_append_dst(_dma_state[bus].rx_dma, &rx_desc[bus], in_addr,
len, in ? true : false);
dma_append_src(_dma_state[bus].tx_dma, &tx_desc[bus], out_addr,
len, out ? true : false);
_dma_execute(bus);
}
void spi_transfer_regs(spi_t bus, spi_cs_t cs,
uint8_t reg, const void *out, void *in, size_t len)
{
if (cs != SPI_CS_UNDEF) {
gpio_clear((gpio_t)cs);
}
if (_use_dma(bus)) {
/* The DMA promises not to modify the const out data */
_dma_transfer_regs(bus, reg, out, in, len);
}
else {
_blocking_transfer(bus, &reg, NULL, 1);
_blocking_transfer(bus, out, in, len);
}
if (cs != SPI_CS_UNDEF) {
gpio_set((gpio_t)cs);
}
}
uint8_t spi_transfer_reg(spi_t bus, spi_cs_t cs, uint8_t reg, uint8_t out)
{
uint8_t res;
spi_transfer_regs(bus, cs, reg, &out, &res, 1);
return res;
}
#endif /* MODULE_PERIPH_DMA */
void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
const void *out, void *in, size_t len)
{
assert(out || in);
if (cs != SPI_CS_UNDEF) {
gpio_clear((gpio_t)cs);
}
if (_use_dma(bus)) {
#ifdef MODULE_PERIPH_DMA
/* The DMA promises not to modify the const out data */
_dma_transfer(bus, out, in, len);
#endif
}
else {
_blocking_transfer(bus, out, in, len);
}
if ((!cont) && (cs != SPI_CS_UNDEF)) {
gpio_set((gpio_t)cs);