685 lines
20 KiB
C
685 lines
20 KiB
C
/*
|
|
* Copyright (c) 2016 Linaro Limited.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
*/
|
|
|
|
|
|
#include <device.h>
|
|
#include <drivers/dma.h>
|
|
#include <errno.h>
|
|
#include <init.h>
|
|
#include <stdio.h>
|
|
#include <soc.h>
|
|
#include <string.h>
|
|
#include <sys/util.h>
|
|
|
|
#define LOG_LEVEL CONFIG_DMA_LOG_LEVEL
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(dma_stm32f4x);
|
|
|
|
#include <clock_control/stm32_clock_control.h>
|
|
|
|
#define DMA_STM32_MAX_STREAMS 8 /* Number of streams per controller */
|
|
#define DMA_STM32_MAX_DEVS 2 /* Number of controllers */
|
|
#define DMA_STM32_1 0 /* First DMA controller */
|
|
#define DMA_STM32_2 1 /* Second DMA controller */
|
|
|
|
#define DMA_STM32_IRQ_PRI CONFIG_DMA_0_IRQ_PRI
|
|
|
|
struct dma_stm32_stream_reg {
|
|
/* Shared registers */
|
|
u32_t lisr;
|
|
u32_t hisr;
|
|
u32_t lifcr;
|
|
u32_t hifcr;
|
|
|
|
/* Per stream registers */
|
|
u32_t scr;
|
|
u32_t sndtr;
|
|
u32_t spar;
|
|
u32_t sm0ar;
|
|
u32_t sm1ar;
|
|
u32_t sfcr;
|
|
};
|
|
|
|
struct dma_stm32_stream {
|
|
u32_t direction;
|
|
struct device *dev;
|
|
struct dma_stm32_stream_reg regs;
|
|
bool busy;
|
|
void *callback_arg;
|
|
void (*dma_callback)(void *arg, u32_t id,
|
|
int error_code);
|
|
};
|
|
|
|
static struct dma_stm32_device {
|
|
u32_t base;
|
|
struct device *clk;
|
|
struct dma_stm32_stream stream[DMA_STM32_MAX_STREAMS];
|
|
bool mem2mem;
|
|
} device_data[DMA_STM32_MAX_DEVS];
|
|
|
|
struct dma_stm32_config {
|
|
struct stm32_pclken pclken;
|
|
void (*config)(struct dma_stm32_device *);
|
|
};
|
|
|
|
/* DMA burst length */
|
|
#define BURST_TRANS_LENGTH_1 0
|
|
|
|
/* DMA direction */
|
|
#define DMA_STM32_DEV_TO_MEM 0
|
|
#define DMA_STM32_MEM_TO_DEV 1
|
|
#define DMA_STM32_MEM_TO_MEM 2
|
|
|
|
/* DMA priority level */
|
|
#define DMA_STM32_PRIORITY_LOW 0
|
|
#define DMA_STM32_PRIORITY_MEDIUM 1
|
|
#define DMA_STM32_PRIORITY_HIGH 2
|
|
#define DMA_STM32_PRIORITY_VERY_HIGH 3
|
|
|
|
/* DMA FIFO threshold selection */
|
|
#define DMA_STM32_FIFO_THRESHOLD_1QUARTERFULL 0
|
|
#define DMA_STM32_FIFO_THRESHOLD_HALFFULL 1
|
|
#define DMA_STM32_FIFO_THRESHOLD_3QUARTERSFULL 2
|
|
#define DMA_STM32_FIFO_THRESHOLD_FULL 3
|
|
|
|
/* Maximum data sent in single transfer (Bytes) */
|
|
#define DMA_STM32_MAX_DATA_ITEMS 0xffff
|
|
|
|
#define DMA_STM32_1_BASE 0x40026000
|
|
#define DMA_STM32_2_BASE 0x40026400
|
|
|
|
/* Shared registers */
|
|
#define DMA_STM32_LISR 0x00 /* DMA low int status reg */
|
|
#define DMA_STM32_HISR 0x04 /* DMA high int status reg */
|
|
#define DMA_STM32_LIFCR 0x08 /* DMA low int flag clear reg */
|
|
#define DMA_STM32_HIFCR 0x0c /* DMA high int flag clear reg */
|
|
#define DMA_STM32_FEI BIT(0) /* FIFO error interrupt */
|
|
#define RESERVED_1 BIT(1)
|
|
#define DMA_STM32_DMEI BIT(2) /* Direct mode error interrupt */
|
|
#define DMA_STM32_TEI BIT(3) /* Transfer error interrupt */
|
|
#define DMA_STM32_HTI BIT(4) /* Transfer half complete interrupt */
|
|
#define DMA_STM32_TCI BIT(5) /* Transfer complete interrupt */
|
|
|
|
/* DMA Stream x Configuration Register */
|
|
#define DMA_STM32_SCR(x) (0x10 + 0x18 * (x))
|
|
#define DMA_STM32_SCR_EN BIT(0) /* Stream Enable */
|
|
#define DMA_STM32_SCR_DMEIE BIT(1) /* Direct Mode Err Int En */
|
|
#define DMA_STM32_SCR_TEIE BIT(2) /* Transfer Error Int En */
|
|
#define DMA_STM32_SCR_HTIE BIT(3) /* Transfer 1/2 Comp Int En */
|
|
#define DMA_STM32_SCR_TCIE BIT(4) /* Transfer Comp Int En */
|
|
#define DMA_STM32_SCR_PFCTRL BIT(5) /* Peripheral Flow Controller */
|
|
#define DMA_STM32_SCR_DIR_MASK GENMASK(7, 6) /* Transfer direction */
|
|
#define DMA_STM32_SCR_CIRC BIT(8) /* Circular mode */
|
|
#define DMA_STM32_SCR_PINC BIT(9) /* Peripheral increment mode */
|
|
#define DMA_STM32_SCR_MINC BIT(10) /* Memory increment mode */
|
|
#define DMA_STM32_SCR_PSIZE_MASK GENMASK(12, 11) /* Periph data size */
|
|
#define DMA_STM32_SCR_MSIZE_MASK GENMASK(14, 13) /* Memory data size */
|
|
#define DMA_STM32_SCR_PINCOS BIT(15) /* Periph inc offset size */
|
|
#define DMA_STM32_SCR_PL_MASK GENMASK(17, 16) /* Priority level */
|
|
#define DMA_STM32_SCR_DBM BIT(18) /* Double Buffer Mode */
|
|
#define DMA_STM32_SCR_CT BIT(19) /* Target in double buffer */
|
|
#define DMA_STM32_SCR_PBURST_MASK GENMASK(22, 21) /* Periph burst size */
|
|
#define DMA_STM32_SCR_MBURST_MASK GENMASK(24, 23) /* Memory burst size */
|
|
/* Setting MACROS */
|
|
#define DMA_STM32_SCR_DIR(n) ((n & 0x3) << 6)
|
|
#define DMA_STM32_SCR_PSIZE(n) ((n & 0x3) << 11)
|
|
#define DMA_STM32_SCR_MSIZE(n) ((n & 0x3) << 13)
|
|
#define DMA_STM32_SCR_PL(n) ((n & 0x3) << 16)
|
|
#define DMA_STM32_SCR_PBURST(n) ((n & 0x3) << 21)
|
|
#define DMA_STM32_SCR_MBURST(n) ((n & 0x3) << 23)
|
|
#define DMA_STM32_SCR_REQ(n) ((n & 0x7) << 25)
|
|
/* Getting MACROS */
|
|
#define DMA_STM32_SCR_PSIZE_GET(n) ((n & DMA_STM32_SCR_PSIZE_MASK) >> 11)
|
|
#define DMA_STM32_SCR_CFG_MASK (DMA_STM32_SCR_PINC \
|
|
| DMA_STM32_SCR_MINC \
|
|
| DMA_STM32_SCR_PINCOS \
|
|
| DMA_STM32_SCR_PL_MASK)
|
|
#define DMA_STM32_SCR_IRQ_MASK (DMA_STM32_SCR_TCIE \
|
|
| DMA_STM32_SCR_TEIE \
|
|
| DMA_STM32_SCR_DMEIE)
|
|
|
|
/* DMA stream x number of data register (len) */
|
|
#define DMA_STM32_SNDTR(x) (0x14 + 0x18 * (x))
|
|
|
|
/* DMA stream peripheral address register (source) */
|
|
#define DMA_STM32_SPAR(x) (0x18 + 0x18 * (x))
|
|
|
|
/* DMA stream x memory 0 address register (destination) */
|
|
#define DMA_STM32_SM0AR(x) (0x1c + 0x18 * (x))
|
|
|
|
/* DMA stream x memory 1 address register (destination - double buffer) */
|
|
#define DMA_STM32_SM1AR(x) (0x20 + 0x18 * (x))
|
|
|
|
/* DMA stream x FIFO control register */
|
|
#define DMA_STM32_SFCR(x) (0x24 + 0x18 * (x))
|
|
#define DMA_STM32_SFCR_FTH_MASK GENMASK(1, 0) /* FIFO threshold */
|
|
#define DMA_STM32_SFCR_DMDIS BIT(2) /* Direct mode disable */
|
|
#define DMA_STM32_SFCR_STAT_MASK GENMASK(5, 3) /* FIFO status */
|
|
#define RESERVED_6 BIT(6) /* Reserved */
|
|
#define DMA_STM32_SFCR_FEIE BIT(7) /* FIFO error interrupt enable */
|
|
/* Setting MACROS */
|
|
#define DMA_STM32_SFCR_FTH(n) (n & DMA_STM32_SFCR_FTH_MASK)
|
|
#define DMA_STM32_SFCR_MASK (DMA_STM32_SFCR_FEIE \
|
|
| DMA_STM32_SFCR_DMDIS)
|
|
|
|
#define LOG_U32 __attribute((__unused__)) u32_t
|
|
|
|
static void dma_stm32_1_config(struct dma_stm32_device *ddata);
|
|
static void dma_stm32_2_config(struct dma_stm32_device *ddata);
|
|
|
|
static u32_t dma_stm32_read(struct dma_stm32_device *ddata, u32_t reg)
|
|
{
|
|
return sys_read32(ddata->base + reg);
|
|
}
|
|
|
|
static void dma_stm32_write(struct dma_stm32_device *ddata,
|
|
u32_t reg, u32_t val)
|
|
{
|
|
sys_write32(val, ddata->base + reg);
|
|
}
|
|
|
|
static void dma_stm32_dump_reg(struct dma_stm32_device *ddata, u32_t id)
|
|
{
|
|
LOG_INF("Using stream: %d\n", id);
|
|
LOG_INF("SCR: 0x%x \t(config)\n",
|
|
dma_stm32_read(ddata, DMA_STM32_SCR(id)));
|
|
LOG_INF("SNDTR: 0x%x \t(length)\n",
|
|
dma_stm32_read(ddata, DMA_STM32_SNDTR(id)));
|
|
LOG_INF("SPAR: 0x%x \t(source)\n",
|
|
dma_stm32_read(ddata, DMA_STM32_SPAR(id)));
|
|
LOG_INF("SM0AR: 0x%x \t(destination)\n",
|
|
dma_stm32_read(ddata, DMA_STM32_SM0AR(id)));
|
|
LOG_INF("SM1AR: 0x%x \t(destination (double buffer mode))\n",
|
|
dma_stm32_read(ddata, DMA_STM32_SM1AR(id)));
|
|
LOG_INF("SFCR: 0x%x \t(fifo control)\n",
|
|
dma_stm32_read(ddata, DMA_STM32_SFCR(id)));
|
|
}
|
|
|
|
static u32_t dma_stm32_irq_status(struct dma_stm32_device *ddata,
|
|
u32_t id)
|
|
{
|
|
u32_t irqs;
|
|
|
|
if (id & 4) {
|
|
irqs = dma_stm32_read(ddata, DMA_STM32_HISR);
|
|
} else {
|
|
irqs = dma_stm32_read(ddata, DMA_STM32_LISR);
|
|
}
|
|
|
|
return (irqs >> (((id & 2) << 3) | ((id & 1) * 6U)));
|
|
}
|
|
|
|
static void dma_stm32_irq_clear(struct dma_stm32_device *ddata,
|
|
u32_t id, u32_t irqs)
|
|
{
|
|
irqs = irqs << (((id & 2) << 3) | ((id & 1) * 6U));
|
|
|
|
if (id & 4) {
|
|
dma_stm32_write(ddata, DMA_STM32_HIFCR, irqs);
|
|
} else {
|
|
dma_stm32_write(ddata, DMA_STM32_LIFCR, irqs);
|
|
}
|
|
}
|
|
|
|
static void dma_stm32_irq_handler(void *arg, u32_t id)
|
|
{
|
|
struct device *dev = arg;
|
|
struct dma_stm32_device *ddata = dev->driver_data;
|
|
struct dma_stm32_stream *stream = &ddata->stream[id];
|
|
u32_t irqstatus, config, sfcr;
|
|
|
|
irqstatus = dma_stm32_irq_status(ddata, id);
|
|
config = dma_stm32_read(ddata, DMA_STM32_SCR(id));
|
|
sfcr = dma_stm32_read(ddata, DMA_STM32_SFCR(id));
|
|
|
|
/* Silently ignore spurious transfer half complete IRQ */
|
|
if (irqstatus & DMA_STM32_HTI) {
|
|
dma_stm32_irq_clear(ddata, id, DMA_STM32_HTI);
|
|
return;
|
|
}
|
|
|
|
stream->busy = false;
|
|
|
|
if ((irqstatus & DMA_STM32_TCI) && (config & DMA_STM32_SCR_TCIE)) {
|
|
dma_stm32_irq_clear(ddata, id, DMA_STM32_TCI);
|
|
|
|
stream->dma_callback(stream->callback_arg, id, 0);
|
|
} else {
|
|
LOG_ERR("Internal error: IRQ status: 0x%x\n", irqstatus);
|
|
dma_stm32_irq_clear(ddata, id, irqstatus);
|
|
|
|
stream->dma_callback(stream->callback_arg, id, -EIO);
|
|
}
|
|
}
|
|
|
|
static int dma_stm32_disable_stream(struct dma_stm32_device *ddata,
|
|
u32_t id)
|
|
{
|
|
u32_t config;
|
|
int count = 0;
|
|
int ret = 0;
|
|
|
|
for (;;) {
|
|
config = dma_stm32_read(ddata, DMA_STM32_SCR(id));
|
|
/* Stream already disabled */
|
|
if (!(config & DMA_STM32_SCR_EN)) {
|
|
return 0;
|
|
}
|
|
|
|
/* Try to disable stream */
|
|
dma_stm32_write(ddata, DMA_STM32_SCR(id),
|
|
config &= ~DMA_STM32_SCR_EN);
|
|
|
|
/* After trying for 5 seconds, give up */
|
|
k_sleep(K_SECONDS(5));
|
|
if (count++ > (5 * 1000) / 50) {
|
|
LOG_ERR("DMA error: Stream in use\n");
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int dma_stm32_config_devcpy(struct device *dev, u32_t id,
|
|
struct dma_config *config)
|
|
|
|
{
|
|
struct dma_stm32_device *ddata = dev->driver_data;
|
|
struct dma_stm32_stream_reg *regs = &ddata->stream[id].regs;
|
|
u32_t src_bus_width = dma_width_index(config->source_data_size);
|
|
u32_t dst_bus_width = dma_width_index(config->dest_data_size);
|
|
u32_t src_burst_size = dma_burst_index(config->source_burst_length);
|
|
u32_t dst_burst_size = dma_burst_index(config->dest_burst_length);
|
|
enum dma_channel_direction direction = config->channel_direction;
|
|
|
|
switch (direction) {
|
|
case MEMORY_TO_PERIPHERAL:
|
|
regs->scr = DMA_STM32_SCR_DIR(DMA_STM32_MEM_TO_DEV) |
|
|
DMA_STM32_SCR_PSIZE(dst_bus_width) |
|
|
DMA_STM32_SCR_MSIZE(src_bus_width) |
|
|
DMA_STM32_SCR_PBURST(dst_burst_size) |
|
|
DMA_STM32_SCR_MBURST(src_burst_size) |
|
|
DMA_STM32_SCR_REQ(config->dma_slot) |
|
|
DMA_STM32_SCR_TCIE | DMA_STM32_SCR_TEIE |
|
|
DMA_STM32_SCR_MINC;
|
|
break;
|
|
case PERIPHERAL_TO_MEMORY:
|
|
regs->scr = DMA_STM32_SCR_DIR(DMA_STM32_DEV_TO_MEM) |
|
|
DMA_STM32_SCR_PSIZE(src_bus_width) |
|
|
DMA_STM32_SCR_MSIZE(dst_bus_width) |
|
|
DMA_STM32_SCR_PBURST(src_burst_size) |
|
|
DMA_STM32_SCR_MBURST(dst_burst_size) |
|
|
DMA_STM32_SCR_REQ(config->dma_slot) |
|
|
DMA_STM32_SCR_TCIE | DMA_STM32_SCR_TEIE |
|
|
DMA_STM32_SCR_MINC;
|
|
break;
|
|
default:
|
|
LOG_ERR("DMA error: Direction not supported: %d",
|
|
direction);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (src_burst_size == BURST_TRANS_LENGTH_1 &&
|
|
dst_burst_size == BURST_TRANS_LENGTH_1) {
|
|
/* Enable 'direct' mode error IRQ, disable 'FIFO' error IRQ */
|
|
regs->scr |= DMA_STM32_SCR_DMEIE;
|
|
regs->sfcr &= ~DMA_STM32_SFCR_MASK;
|
|
} else {
|
|
/* Enable 'FIFO' error IRQ, disable 'direct' mode error IRQ */
|
|
regs->sfcr |= DMA_STM32_SFCR_MASK;
|
|
regs->scr &= ~DMA_STM32_SCR_DMEIE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dma_stm32_config_memcpy(struct device *dev, u32_t id,
|
|
struct dma_config *config)
|
|
{
|
|
struct dma_stm32_device *ddata = dev->driver_data;
|
|
struct dma_stm32_stream_reg *regs = &ddata->stream[id].regs;
|
|
u32_t src_bus_width = dma_width_index(config->source_data_size);
|
|
u32_t dst_bus_width = dma_width_index(config->dest_data_size);
|
|
u32_t src_burst_size = dma_burst_index(config->source_burst_length);
|
|
u32_t dst_burst_size = dma_burst_index(config->dest_burst_length);
|
|
|
|
regs->scr = DMA_STM32_SCR_DIR(DMA_STM32_MEM_TO_MEM) |
|
|
DMA_STM32_SCR_PSIZE(src_bus_width) |
|
|
DMA_STM32_SCR_MSIZE(dst_bus_width) |
|
|
DMA_STM32_SCR_PBURST(src_burst_size) |
|
|
DMA_STM32_SCR_MBURST(dst_burst_size) |
|
|
DMA_STM32_SCR_MINC | /* Memory increment mode */
|
|
DMA_STM32_SCR_PINC | /* Peripheral increment mode */
|
|
DMA_STM32_SCR_TCIE | /* Transfer comp IRQ enable */
|
|
DMA_STM32_SCR_TEIE; /* Transfer error IRQ enable */
|
|
|
|
regs->sfcr = DMA_STM32_SFCR_DMDIS | /* Direct mode disable */
|
|
DMA_STM32_SFCR_FTH(DMA_STM32_FIFO_THRESHOLD_FULL) |
|
|
DMA_STM32_SFCR_FEIE; /* FIFI error IRQ enable */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dma_stm32_config(struct device *dev, u32_t id,
|
|
struct dma_config *config)
|
|
{
|
|
struct dma_stm32_device *ddata = dev->driver_data;
|
|
struct dma_stm32_stream *stream = &ddata->stream[id];
|
|
struct dma_stm32_stream_reg *regs = &ddata->stream[id].regs;
|
|
int ret;
|
|
|
|
if (id >= DMA_STM32_MAX_STREAMS) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (stream->busy) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (config->head_block->block_size > DMA_STM32_MAX_DATA_ITEMS) {
|
|
LOG_ERR("DMA error: Data size too big: %d\n",
|
|
config->head_block->block_size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (MEMORY_TO_MEMORY == config->channel_direction && !ddata->mem2mem) {
|
|
LOG_ERR("DMA error: Memcopy not supported for device %s",
|
|
dev->config->name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
stream->busy = true;
|
|
stream->dma_callback = config->dma_callback;
|
|
stream->direction = config->channel_direction;
|
|
stream->callback_arg = config->callback_arg;
|
|
|
|
if (stream->direction == MEMORY_TO_PERIPHERAL) {
|
|
regs->sm0ar = (u32_t)config->head_block->source_address;
|
|
regs->spar = (u32_t)config->head_block->dest_address;
|
|
} else {
|
|
regs->spar = (u32_t)config->head_block->source_address;
|
|
regs->sm0ar = (u32_t)config->head_block->dest_address;
|
|
}
|
|
|
|
if (stream->direction == MEMORY_TO_MEMORY) {
|
|
ret = dma_stm32_config_memcpy(dev, id, config);
|
|
} else {
|
|
ret = dma_stm32_config_devcpy(dev, id, config);
|
|
}
|
|
|
|
regs->sndtr = config->head_block->block_size;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int dma_stm32_reload(struct device *dev, u32_t id,
|
|
u32_t src, u32_t dst, size_t size)
|
|
{
|
|
struct dma_stm32_device *ddata = dev->driver_data;
|
|
struct dma_stm32_stream_reg *regs = &ddata->stream[id].regs;
|
|
struct dma_stm32_stream *stream = &ddata->stream[id];
|
|
|
|
if (id >= DMA_STM32_MAX_STREAMS) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (stream->direction) {
|
|
case MEMORY_TO_PERIPHERAL:
|
|
regs->sm0ar = src;
|
|
regs->spar = dst;
|
|
break;
|
|
|
|
case MEMORY_TO_MEMORY:
|
|
case PERIPHERAL_TO_MEMORY:
|
|
regs->spar = src;
|
|
regs->sm0ar = dst;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
regs->sndtr = size;
|
|
return 0;
|
|
}
|
|
|
|
static int dma_stm32_start(struct device *dev, u32_t id)
|
|
{
|
|
struct dma_stm32_device *ddata = dev->driver_data;
|
|
struct dma_stm32_stream_reg *regs = &ddata->stream[id].regs;
|
|
u32_t irqstatus;
|
|
int ret;
|
|
|
|
if (id >= DMA_STM32_MAX_STREAMS) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = dma_stm32_disable_stream(ddata, id);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
dma_stm32_write(ddata, DMA_STM32_SCR(id), regs->scr);
|
|
dma_stm32_write(ddata, DMA_STM32_SPAR(id), regs->spar);
|
|
dma_stm32_write(ddata, DMA_STM32_SM0AR(id), regs->sm0ar);
|
|
dma_stm32_write(ddata, DMA_STM32_SFCR(id), regs->sfcr);
|
|
dma_stm32_write(ddata, DMA_STM32_SM1AR(id), regs->sm1ar);
|
|
dma_stm32_write(ddata, DMA_STM32_SNDTR(id), regs->sndtr);
|
|
|
|
/* Clear remanent IRQs from previous transfers */
|
|
irqstatus = dma_stm32_irq_status(ddata, id);
|
|
if (irqstatus) {
|
|
dma_stm32_irq_clear(ddata, id, irqstatus);
|
|
}
|
|
|
|
dma_stm32_dump_reg(ddata, id);
|
|
|
|
/* Push the start button */
|
|
dma_stm32_write(ddata, DMA_STM32_SCR(id),
|
|
regs->scr | DMA_STM32_SCR_EN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dma_stm32_stop(struct device *dev, u32_t id)
|
|
{
|
|
struct dma_stm32_device *ddata = dev->driver_data;
|
|
struct dma_stm32_stream *stream = &ddata->stream[id];
|
|
u32_t scr, sfcr, irqstatus;
|
|
int ret;
|
|
|
|
if (id >= DMA_STM32_MAX_STREAMS) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Disable all IRQs */
|
|
scr = dma_stm32_read(ddata, DMA_STM32_SCR(id));
|
|
scr &= ~DMA_STM32_SCR_IRQ_MASK;
|
|
dma_stm32_write(ddata, DMA_STM32_SCR(id), scr);
|
|
|
|
sfcr = dma_stm32_read(ddata, DMA_STM32_SFCR(id));
|
|
sfcr &= ~DMA_STM32_SFCR_FEIE;
|
|
dma_stm32_write(ddata, DMA_STM32_SFCR(id), sfcr);
|
|
|
|
/* Disable stream */
|
|
ret = dma_stm32_disable_stream(ddata, id);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
/* Clear remanent IRQs from previous transfers */
|
|
irqstatus = dma_stm32_irq_status(ddata, id);
|
|
if (irqstatus) {
|
|
dma_stm32_irq_clear(ddata, id, irqstatus);
|
|
}
|
|
|
|
/* Finally, flag stream as free */
|
|
stream->busy = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dma_stm32_get_status(struct device *dev, u32_t id,
|
|
struct dma_status *stat)
|
|
{
|
|
struct dma_stm32_device *ddata = dev->driver_data;
|
|
|
|
if (id >= DMA_STM32_MAX_STREAMS || stat == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
stat->dir = ddata->stream[id].direction;
|
|
stat->busy = ddata->stream[id].busy;
|
|
stat->pending_length = dma_stm32_read(ddata, DMA_STM32_SNDTR(id));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dma_stm32_init(struct device *dev)
|
|
{
|
|
struct dma_stm32_device *ddata = dev->driver_data;
|
|
const struct dma_stm32_config *cdata = dev->config->config_info;
|
|
int i;
|
|
|
|
for (i = 0; i < DMA_STM32_MAX_STREAMS; i++) {
|
|
ddata->stream[i].dev = dev;
|
|
ddata->stream[i].busy = false;
|
|
}
|
|
|
|
/* Enable DMA clock */
|
|
ddata->clk = device_get_binding(STM32_CLOCK_CONTROL_NAME);
|
|
|
|
__ASSERT_NO_MSG(ddata->clk);
|
|
|
|
if (clock_control_on(ddata->clk,
|
|
(clock_control_subsys_t *) &cdata->pclken) != 0) {
|
|
LOG_ERR("Could not enable DMA clock\n");
|
|
return -EIO;
|
|
}
|
|
|
|
/* Set controller specific configuration */
|
|
cdata->config(ddata);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dma_driver_api dma_funcs = {
|
|
.reload = dma_stm32_reload,
|
|
.config = dma_stm32_config,
|
|
.start = dma_stm32_start,
|
|
.stop = dma_stm32_stop,
|
|
.get_status = dma_stm32_get_status,
|
|
};
|
|
|
|
const struct dma_stm32_config dma_stm32_1_cdata = {
|
|
.pclken = { .bus = STM32_CLOCK_BUS_AHB1,
|
|
.enr = LL_AHB1_GRP1_PERIPH_DMA1 },
|
|
.config = dma_stm32_1_config,
|
|
};
|
|
|
|
DEVICE_AND_API_INIT(dma_stm32_1, CONFIG_DMA_1_NAME, &dma_stm32_init,
|
|
&device_data[DMA_STM32_1], &dma_stm32_1_cdata,
|
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
|
(void *)&dma_funcs);
|
|
|
|
static const struct dma_stm32_config dma_stm32_2_cdata = {
|
|
.pclken = { .bus = STM32_CLOCK_BUS_AHB1,
|
|
.enr = LL_AHB1_GRP1_PERIPH_DMA2 },
|
|
.config = dma_stm32_2_config,
|
|
};
|
|
|
|
DEVICE_AND_API_INIT(dma_stm32_2, CONFIG_DMA_2_NAME, &dma_stm32_init,
|
|
&device_data[DMA_STM32_2], &dma_stm32_2_cdata,
|
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
|
(void *)&dma_funcs);
|
|
|
|
static void dma_stm32_irq_0(void *arg) { dma_stm32_irq_handler(arg, 0); }
|
|
static void dma_stm32_irq_1(void *arg) { dma_stm32_irq_handler(arg, 1); }
|
|
static void dma_stm32_irq_2(void *arg) { dma_stm32_irq_handler(arg, 2); }
|
|
static void dma_stm32_irq_3(void *arg) { dma_stm32_irq_handler(arg, 3); }
|
|
static void dma_stm32_irq_4(void *arg) { dma_stm32_irq_handler(arg, 4); }
|
|
static void dma_stm32_irq_5(void *arg) { dma_stm32_irq_handler(arg, 5); }
|
|
static void dma_stm32_irq_6(void *arg) { dma_stm32_irq_handler(arg, 6); }
|
|
static void dma_stm32_irq_7(void *arg) { dma_stm32_irq_handler(arg, 7); }
|
|
|
|
static void dma_stm32_1_config(struct dma_stm32_device *ddata)
|
|
{
|
|
ddata->base = DMA_STM32_1_BASE;
|
|
ddata->mem2mem = false;
|
|
|
|
IRQ_CONNECT(DMA1_Stream0_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_0, DEVICE_GET(dma_stm32_1), 0);
|
|
irq_enable(DMA1_Stream0_IRQn);
|
|
|
|
IRQ_CONNECT(DMA1_Stream1_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_1, DEVICE_GET(dma_stm32_1), 0);
|
|
irq_enable(DMA1_Stream1_IRQn);
|
|
|
|
IRQ_CONNECT(DMA1_Stream2_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_2, DEVICE_GET(dma_stm32_1), 0);
|
|
irq_enable(DMA1_Stream2_IRQn);
|
|
|
|
IRQ_CONNECT(DMA1_Stream3_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_3, DEVICE_GET(dma_stm32_1), 0);
|
|
irq_enable(DMA1_Stream3_IRQn);
|
|
|
|
IRQ_CONNECT(DMA1_Stream4_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_4, DEVICE_GET(dma_stm32_1), 0);
|
|
irq_enable(DMA1_Stream4_IRQn);
|
|
|
|
IRQ_CONNECT(DMA1_Stream5_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_5, DEVICE_GET(dma_stm32_1), 0);
|
|
irq_enable(DMA1_Stream5_IRQn);
|
|
|
|
IRQ_CONNECT(DMA1_Stream6_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_6, DEVICE_GET(dma_stm32_1), 0);
|
|
irq_enable(DMA1_Stream6_IRQn);
|
|
|
|
IRQ_CONNECT(DMA1_Stream7_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_7, DEVICE_GET(dma_stm32_1), 0);
|
|
irq_enable(DMA1_Stream7_IRQn);
|
|
}
|
|
|
|
static void dma_stm32_2_config(struct dma_stm32_device *ddata)
|
|
{
|
|
ddata->base = DMA_STM32_2_BASE;
|
|
ddata->mem2mem = true;
|
|
|
|
IRQ_CONNECT(DMA2_Stream0_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_0, DEVICE_GET(dma_stm32_2), 0);
|
|
irq_enable(DMA2_Stream0_IRQn);
|
|
|
|
IRQ_CONNECT(DMA2_Stream1_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_1, DEVICE_GET(dma_stm32_2), 0);
|
|
irq_enable(DMA2_Stream1_IRQn);
|
|
|
|
IRQ_CONNECT(DMA2_Stream2_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_2, DEVICE_GET(dma_stm32_2), 0);
|
|
irq_enable(DMA2_Stream2_IRQn);
|
|
|
|
IRQ_CONNECT(DMA2_Stream3_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_3, DEVICE_GET(dma_stm32_2), 0);
|
|
irq_enable(DMA2_Stream3_IRQn);
|
|
|
|
IRQ_CONNECT(DMA2_Stream4_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_4, DEVICE_GET(dma_stm32_2), 0);
|
|
irq_enable(DMA2_Stream4_IRQn);
|
|
|
|
IRQ_CONNECT(DMA2_Stream5_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_5, DEVICE_GET(dma_stm32_2), 0);
|
|
irq_enable(DMA2_Stream5_IRQn);
|
|
|
|
IRQ_CONNECT(DMA2_Stream6_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_6, DEVICE_GET(dma_stm32_2), 0);
|
|
irq_enable(DMA2_Stream6_IRQn);
|
|
|
|
IRQ_CONNECT(DMA2_Stream7_IRQn, DMA_STM32_IRQ_PRI,
|
|
dma_stm32_irq_7, DEVICE_GET(dma_stm32_2), 0);
|
|
irq_enable(DMA2_Stream7_IRQn);
|
|
}
|