diff --git a/boards/common/arduino-mkr/include/periph_conf.h b/boards/common/arduino-mkr/include/periph_conf.h index 1f86af3d97..25f52b025c 100644 --- a/boards/common/arduino-mkr/include/periph_conf.h +++ b/boards/common/arduino-mkr/include/periph_conf.h @@ -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 } }; diff --git a/boards/common/saml1x/include/periph_conf.h b/boards/common/saml1x/include/periph_conf.h index ba5337606f..4eeb305e03 100644 --- a/boards/common/saml1x/include/periph_conf.h +++ b/boards/common/saml1x/include/periph_conf.h @@ -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 } }; diff --git a/boards/feather-m0/include/periph_conf.h b/boards/feather-m0/include/periph_conf.h index e5a5f65d64..ce3f6c37b8 100644 --- a/boards/feather-m0/include/periph_conf.h +++ b/boards/feather-m0/include/periph_conf.h @@ -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 } }; diff --git a/boards/hamilton/include/periph_conf.h b/boards/hamilton/include/periph_conf.h index 27a33bf37b..7cfda8f8db 100644 --- a/boards/hamilton/include/periph_conf.h +++ b/boards/hamilton/include/periph_conf.h @@ -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 } }; diff --git a/boards/samd21-xpro/include/periph_conf.h b/boards/samd21-xpro/include/periph_conf.h index 8e477a186d..7d451ea421 100644 --- a/boards/samd21-xpro/include/periph_conf.h +++ b/boards/samd21-xpro/include/periph_conf.h @@ -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 } }; diff --git a/boards/same54-xpro/include/periph_conf.h b/boards/same54-xpro/include/periph_conf.h index 840da19bb8..be145dc676 100644 --- a/boards/same54-xpro/include/periph_conf.h +++ b/boards/same54-xpro/include/periph_conf.h @@ -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 } }; diff --git a/boards/saml21-xpro/include/periph_conf.h b/boards/saml21-xpro/include/periph_conf.h index b4e5cea27e..2524a192ae 100644 --- a/boards/saml21-xpro/include/periph_conf.h +++ b/boards/saml21-xpro/include/periph_conf.h @@ -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 } }; diff --git a/boards/samr21-xpro/include/periph_conf.h b/boards/samr21-xpro/include/periph_conf.h index 54807ec70d..f3a0b7233b 100644 --- a/boards/samr21-xpro/include/periph_conf.h +++ b/boards/samr21-xpro/include/periph_conf.h @@ -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 } }; diff --git a/boards/samr30-xpro/include/periph_conf.h b/boards/samr30-xpro/include/periph_conf.h index 4d7d6ee1e9..e53c323bbc 100644 --- a/boards/samr30-xpro/include/periph_conf.h +++ b/boards/samr30-xpro/include/periph_conf.h @@ -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 } }; diff --git a/boards/samr34-xpro/include/periph_conf.h b/boards/samr34-xpro/include/periph_conf.h index d305bc49d8..3d391e4d4d 100644 --- a/boards/samr34-xpro/include/periph_conf.h +++ b/boards/samr34-xpro/include/periph_conf.h @@ -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 } }; diff --git a/boards/sensebox_samd21/include/periph_conf.h b/boards/sensebox_samd21/include/periph_conf.h index 546119261b..050f61d970 100644 --- a/boards/sensebox_samd21/include/periph_conf.h +++ b/boards/sensebox_samd21/include/periph_conf.h @@ -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 } }; diff --git a/boards/serpente/include/periph_conf.h b/boards/serpente/include/periph_conf.h index 7382d5d63d..0fc26888d4 100644 --- a/boards/serpente/include/periph_conf.h +++ b/boards/serpente/include/periph_conf.h @@ -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 }, }; diff --git a/boards/sodaq-one/include/periph_conf.h b/boards/sodaq-one/include/periph_conf.h index 3cd8064bcb..767d67b97f 100644 --- a/boards/sodaq-one/include/periph_conf.h +++ b/boards/sodaq-one/include/periph_conf.h @@ -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 } }; diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index 0824396a47..495a031399 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -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; /** @} */ diff --git a/cpu/sam0_common/periph/spi.c b/cpu/sam0_common/periph/spi.c index 5848ef2341..b7dfc1cd85 100644 --- a/cpu/sam0_common/periph/spi.c +++ b/cpu/sam0_common/periph/spi.c @@ -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);