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:
commit
1992f57765
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
/** @} */
|
||||
|
||||
|
||||
@ -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, ®, 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, ®, 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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user