mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-25 14:33:52 +01:00
cpu/sam0_common: Make SPI peripheral DMA compatible
This commit is contained in:
parent
7bb3aa6560
commit
60f4502e6c
@ -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