Clean up DMA module and interface.

Explicitly use enum for dma_channel, instead of ints (since channel is
1-based in the docs and 0-based in the enum).  Add enum values for
other channels like USART.

Use volatile structs instead of typecasting through REG32().

Other assorted code cleanup.

Remove DMA2 partial support, since neither STM32F100R8 nor STM32L151x
has hardware support for it and it cluttered the code.

No functional changes.

BUG=chrome-os-partner:20485
BRANCH=none
TEST=from pit root shell, 'ectool version' still works

Change-Id: Ifdd62fb287c608a684ba8fa16934dfcab83ee609
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/59949
This commit is contained in:
Randall Spangler
2013-06-24 16:57:51 -07:00
committed by ChromeBot
parent 8e72f58ac4
commit ab6546cbaa
4 changed files with 175 additions and 207 deletions

View File

@@ -19,36 +19,58 @@
/* Task IDs for the interrupt handlers to wake up */
static task_id_t id[DMA_NUM_CHANNELS];
/* Registers for the DMA controller */
struct dma_ctlr {
uint32_t isr;
uint32_t ifcr;
dma_channel_t chan[DMA_NUM_CHANNELS];
};
/* Always use dma_ctlr_t so volatile keyword is included! */
typedef volatile struct dma_ctlr dma_ctlr_t;
/**
* Return the IRQ for the DMA channel
*
* @param channel Channel number
* @return IRQ for the channel
*/
static int dma_get_irq(enum dma_channel channel)
{
return STM32_IRQ_DMA_CHANNEL_1 + channel;
}
/**
* Get a pointer to the DMA peripheral controller that owns the channel
*
* @param channel Channel number to get the controller for (DMAC_...)
* @return pointer to DMA channel registers
*/
static dma_ctlr_t *dma_get_ctlr(void)
{
return (dma_ctlr_t *)STM32_DMA1_BASE;
}
/*
* Note, you must decrement the channel value by 1 from what is specified
* in the datasheets, as they index from 1 and this indexes from 0!
*/
struct dma_channel *dma_get_channel(int channel)
dma_channel_t *dma_get_channel(enum dma_channel channel)
{
struct dma_channel *chan;
struct dma_ctlr *dma;
dma_ctlr_t *dma = dma_get_ctlr();
/* Get a pointer to the correct controller and channel */
ASSERT(channel < DMA_NUM_CHANNELS);
if (channel < DMA1_NUM_CHANNELS) {
dma = (struct dma_ctlr *)STM32_DMA1_BASE;
chan = &dma->chan[channel];
} else {
dma = (struct dma_ctlr *)STM32_DMA2_BASE;
chan = &dma->chan[channel - DMA1_NUM_CHANNELS];
}
return chan;
return &dma->chan[channel];
}
void dma_disable(unsigned channel)
void dma_disable(enum dma_channel channel)
{
struct dma_channel *chan;
dma_channel_t *chan = dma_get_channel(channel);
chan = dma_get_channel(channel);
if (REG32(&chan->ccr) & DMA_EN)
REG32(&chan->ccr) &= ~DMA_EN;
if (chan->ccr & DMA_EN)
chan->ccr &= ~DMA_EN;
}
/**
@@ -57,76 +79,72 @@ void dma_disable(unsigned channel)
* @param chan Channel number to read (DMAC_...)
* @param count Number of bytes to transfer
* @param periph Pointer to peripheral data register
* @param memory Pointer to memory address
* @param memory Pointer to memory address for receive/transmit
* @param flags DMA flags for the control register, normally:
DMA_MINC_MASK |
* (DMA_DIR_FROM_MEM_MASK for tx
* 0 for rx)
*/
static void prepare_channel(struct dma_channel *chan, unsigned count,
void *periph, const void *memory, unsigned flags)
static void prepare_channel(dma_channel_t *chan, unsigned count,
void *periph, void *memory, unsigned flags)
{
uint32_t ctrl;
uint32_t ctrl = DMA_PL_VERY_HIGH << DMA_PL_SHIFT;
if (REG32(&chan->ccr) & DMA_EN)
REG32(&chan->ccr) &= ~DMA_EN;
if (chan->ccr & DMA_EN)
chan->ccr &= ~DMA_EN;
/* Following the order in Doc ID 15965 Rev 5 p194 */
REG32(&chan->cpar) = (uint32_t)periph;
REG32(&chan->cmar) = (uint32_t)memory;
REG32(&chan->cndtr) = count;
ctrl = DMA_PL_VERY_HIGH << DMA_PL_SHIFT;
REG32(&chan->ccr) = ctrl;
chan->cpar = (uint32_t)periph;
chan->cmar = (uint32_t)memory;
chan->cndtr = count;
chan->ccr = ctrl;
ctrl |= flags;
REG32(&chan->ccr) = ctrl;
chan->ccr = ctrl;
}
void dma_go(struct dma_channel *chan)
void dma_go(dma_channel_t *chan)
{
/* Fire it up */
REG32(&chan->ccr) |= DMA_EN;
chan->ccr |= DMA_EN;
}
void dma_prepare_tx(const struct dma_option *option, unsigned count,
const void *memory)
{
struct dma_channel *chan = dma_get_channel(option->channel);
dma_channel_t *chan = dma_get_channel(option->channel);
prepare_channel(chan, count, option->periph, memory,
/*
* Cast away const for memory pointer; this is ok because we know
* we're preparing the channel for transmit.
*/
prepare_channel(chan, count, option->periph, (void *)memory,
DMA_MINC_MASK | DMA_DIR_FROM_MEM_MASK | option->flags);
}
int dma_start_rx(const struct dma_option *option, unsigned count,
const void *memory)
void dma_start_rx(const struct dma_option *option, unsigned count,
void *memory)
{
struct dma_channel *chan = dma_get_channel(option->channel);
dma_channel_t *chan = dma_get_channel(option->channel);
prepare_channel(chan, count, option->periph, memory,
DMA_MINC_MASK | option->flags);
dma_go(chan);
return 0;
}
int dma_bytes_done(struct dma_channel *chan, int orig_count)
int dma_bytes_done(dma_channel_t *chan, int orig_count)
{
if (!(REG32(&chan->ccr) & DMA_EN))
if (!(chan->ccr & DMA_EN))
return 0;
return orig_count - REG32(&chan->cndtr);
return orig_count - chan->cndtr;
}
#ifdef CONFIG_DMA_HELP
void dma_dump(unsigned channel)
void dma_dump(enum dma_channel channel)
{
struct dma_channel *chan;
struct dma_ctlr *dma;
dma_ctlr_t *dma = dma_get_ctlr();
dma_channel_t *chan = dma_get_channel(channel);
/* Get a pointer to the correct controller and channel */
ASSERT(channel < DMA_NUM_CHANNELS);
dma = (struct dma_ctlr *)STM32_DMA1_BASE;
chan = dma_get_channel(channel);
CPRINTF("ccr=%x, cndtr=%x, cpar=%x, cmar=%x\n", chan->ccr,
chan->cndtr, chan->cpar, chan->cmar);
CPRINTF("chan %d, isr=%x, ifcr=%x\n",
@@ -135,61 +153,58 @@ void dma_dump(unsigned channel)
(dma->ifcr >> (channel * 4)) & 0xf);
}
void dma_check(int channel, char *buff)
void dma_check(enum dma_channel channel, char *buf)
{
struct dma_channel *chan;
dma_channel_t *chan;
int count;
int i;
chan = dma_get_channel(channel);
count = REG32(&chan->cndtr);
count = chan->cndtr;
CPRINTF("c=%d\n", count);
udelay(1000 * 100);
CPRINTF("c=%d\n",
REG32(&chan->cndtr));
udelay(100 * MSEC);
CPRINTF("c=%d\n", chan->cndtr);
for (i = 0; i < count; i++)
CPRINTF("%02x ", buff[i]);
udelay(1000 * 100);
CPRINTF("c=%d\n",
REG32(&chan->cndtr));
CPRINTF("%02x ", buf[i]);
udelay(100 * MSEC);
CPRINTF("c=%d\n", chan->cndtr);
for (i = 0; i < count; i++)
CPRINTF("%02x ", buff[i]);
CPRINTF("%02x ", buf[i]);
}
/* Run a check of memory-to-memory DMA */
void dma_test(void)
{
dma_channel_t *chan = dma_get_channel(channel);
unsigned channel = 3;
struct dma_channel *chan;
uint32_t ctrl;
char periph[16], memory[16];
unsigned count = sizeof(periph);
int i;
chan = dma_get_channel(channel);
memset(memory, '\0', sizeof(memory));
for (i = 0; i < count; i++)
periph[i] = 10 + i;
/* Following the order in Doc ID 15965 Rev 5 p194 */
REG32(&chan->cpar) = (uint32_t)periph;
REG32(&chan->cmar) = (uint32_t)memory;
REG32(&chan->cndtr) = count;
chan->cpar = (uint32_t)periph;
chan->cmar = (uint32_t)memory;
chan->cndtr = count;
ctrl = DMA_PL_MEDIUM << DMA_PL_SHIFT;
REG32(&chan->ccr) = ctrl;
chan->ccr = ctrl;
ctrl |= DMA_MINC_MASK; /* | DMA_DIR_FROM_MEM_MASK */;
ctrl |= 1 << 14; /* MEM2MEM */
ctrl |= 1 << 6; /* PINC */
/* ctrl |= 2 << 10; */
/* ctrl |= 2 << 8; */
REG32(&chan->ccr) = ctrl;
chan->ccr = ctrl;
ctrl |= DMA_EN;
REG32(&chan->ccr) = ctrl;
chan->ccr = ctrl;
for (i = 0; i < count; i++)
CPRINTF("%d/%d ", periph[i], memory[i]);
CPRINTF("\ncount=%d\n", REG32(&chan->cndtr));
CPRINTF("\ncount=%d\n", chan->cndtr);
}
#endif /* CONFIG_DMA_HELP */
@@ -197,7 +212,7 @@ static void dma_init(void)
{
int i;
/* Enable DMA1, we don't support DMA2 yet */
/* Enable DMA1; current chips don't have DMA2 */
STM32_RCC_AHBENR |= RCC_AHBENR_DMA1EN;
/* Initialize data for interrupt handlers */
@@ -206,106 +221,78 @@ static void dma_init(void)
}
DECLARE_HOOK(HOOK_INIT, dma_init, HOOK_PRIO_INIT_DMA);
int dma_wait(int channel)
int dma_wait(enum dma_channel channel)
{
struct dma_ctlr *dma;
uint32_t mask;
dma_ctlr_t *dma = dma_get_ctlr();
uint32_t mask = DMA_TCIF(channel);
timestamp_t deadline;
dma = dma_get_ctlr(channel);
mask = DMA_TCIF(channel);
deadline.val = get_time().val + DMA_TRANSFER_TIMEOUT_US;
while ((REG32(&dma->isr) & mask) != mask) {
while ((dma->isr & mask) != mask) {
if (deadline.val <= get_time().val)
return -1;
return EC_ERROR_TIMEOUT;
else
udelay(DMA_POLLING_INTERVAL_US);
}
return 0;
return EC_SUCCESS;
}
int dma_get_irq(int channel)
void dma_enable_tc_interrupt(enum dma_channel channel)
{
ASSERT(channel < DMA_NUM_CHANNELS);
if (channel < DMA1_NUM_CHANNELS)
return STM32_IRQ_DMA_CHANNEL_1 + channel;
else
return STM32_IRQ_DMA2_CHANNEL1 + channel -
DMA1_NUM_CHANNELS;
}
dma_channel_t *chan = dma_get_channel(channel);
void dma_enable_tc_interrupt(int channel)
{
struct dma_channel *chan;
chan = dma_get_channel(channel);
/* Storing task ID's so the ISRs knows which task to wake */
/* Store task ID so the ISR knows which task to wake */
id[channel] = task_get_current();
REG32(&chan->ccr) |= DMA_TCIE;
chan->ccr |= DMA_TCIE;
task_enable_irq(dma_get_irq(channel));
}
void dma_disable_tc_interrupt(int channel)
void dma_disable_tc_interrupt(enum dma_channel channel)
{
struct dma_channel *chan;
chan = dma_get_channel(channel);
dma_channel_t *chan = dma_get_channel(channel);
id[channel] = TASK_ID_INVALID;
REG32(&chan->ccr) &= ~DMA_TCIE;
chan->ccr &= ~DMA_TCIE;
task_disable_irq(dma_get_irq(channel));
}
void dma_clear_isr(int channel)
void dma_clear_isr(enum dma_channel channel)
{
struct dma_ctlr *dma;
dma_ctlr_t *dma = dma_get_ctlr();
dma = dma_get_ctlr(channel);
/* Adjusting the channel number if it's from the second DMA */
if (channel > DMA1_NUM_CHANNELS)
channel -= DMA1_NUM_CHANNELS;
REG32(&dma->ifcr) |= 0x0f << (4 * channel);
}
struct dma_ctlr *dma_get_ctlr(int channel)
{
ASSERT(channel < DMA_NUM_CHANNELS);
if (channel < DMA1_NUM_CHANNELS)
return (struct dma_ctlr *)STM32_DMA1_BASE;
else
return (struct dma_ctlr *)STM32_DMA2_BASE;
dma->ifcr |= 0x0f << (4 * channel);
}
static void dma_event_interrupt_channel_4(void)
{
dma_clear_isr(DMAC_I2C2_TX);
if (id[DMAC_I2C2_TX] != TASK_ID_INVALID)
task_wake(id[DMAC_I2C2_TX]);
dma_clear_isr(DMAC_CH4);
if (id[DMAC_CH4] != TASK_ID_INVALID)
task_wake(id[DMAC_CH4]);
}
DECLARE_IRQ(STM32_IRQ_DMA_CHANNEL_4, dma_event_interrupt_channel_4, 3);
static void dma_event_interrupt_channel_5(void)
{
dma_clear_isr(DMAC_I2C2_RX);
if (id[DMAC_I2C2_RX] != TASK_ID_INVALID)
task_wake(id[DMAC_I2C2_RX]);
dma_clear_isr(DMAC_CH5);
if (id[DMAC_CH5] != TASK_ID_INVALID)
task_wake(id[DMAC_CH5]);
}
DECLARE_IRQ(STM32_IRQ_DMA_CHANNEL_5, dma_event_interrupt_channel_5, 3);
static void dma_event_interrupt_channel_6(void)
{
dma_clear_isr(DMAC_I2C1_TX);
if (id[DMAC_I2C1_TX] != TASK_ID_INVALID)
task_wake(id[DMAC_I2C1_TX]);
dma_clear_isr(DMAC_CH6);
if (id[DMAC_CH6] != TASK_ID_INVALID)
task_wake(id[DMAC_CH6]);
}
DECLARE_IRQ(STM32_IRQ_DMA_CHANNEL_6, dma_event_interrupt_channel_6, 3);
static void dma_event_interrupt_channel_7(void)
{
dma_clear_isr(DMAC_I2C1_RX);
if (id[DMAC_I2C1_RX] != TASK_ID_INVALID)
task_wake(id[DMAC_I2C1_RX]);
dma_clear_isr(DMAC_CH7);
if (id[DMAC_CH7] != TASK_ID_INVALID)
task_wake(id[DMAC_CH7]);
}
DECLARE_IRQ(STM32_IRQ_DMA_CHANNEL_7, dma_event_interrupt_channel_7, 3);

View File

@@ -127,7 +127,7 @@ static void i2c_init_port(unsigned int port);
static int i2c_write_raw_slave(int port, void *buf, int len)
{
struct dma_channel *chan;
dma_channel_t *chan;
int rv;
/* we don't want to race with TxE interrupt event */

View File

@@ -121,7 +121,7 @@ static const uint8_t out_preamble[4] = {
* @param nss_mask Bit to check in GPIO register (when high, we abort)
* @return 0 if bytes received, -1 if we hit a timeout or NSS went high
*/
static int wait_for_bytes(struct dma_channel *rxdma, int needed,
static int wait_for_bytes(dma_channel_t *rxdma, int needed,
uint16_t *nss_reg, uint32_t nss_mask)
{
timestamp_t deadline;
@@ -178,7 +178,7 @@ static int wait_for_bytes(struct dma_channel *rxdma, int needed,
* SPI_MSG_HEADER_LEN bytes into out_msg
* @param msg_len Number of message bytes to send
*/
static void reply(struct dma_channel *txdma,
static void reply(dma_channel_t *txdma,
enum ec_status status, char *msg_ptr, int msg_len)
{
char *msg;
@@ -250,7 +250,7 @@ static void setup_for_transaction(void)
static void spi_send_response(struct host_cmd_handler_args *args)
{
enum ec_status result = args->result;
struct dma_channel *txdma;
dma_channel_t *txdma;
/* If we are too late, don't bother */
if (!active)
@@ -277,7 +277,7 @@ static void spi_send_response(struct host_cmd_handler_args *args)
*/
static void spi_send_response_packet(struct host_packet *pkt)
{
struct dma_channel *txdma;
dma_channel_t *txdma;
/* If we are too late, don't bother */
if (!active)
@@ -300,7 +300,7 @@ static void spi_send_response_packet(struct host_packet *pkt)
*/
void spi_event(enum gpio_signal signal)
{
struct dma_channel *rxdma;
dma_channel_t *rxdma;
uint16_t *nss_reg;
uint32_t nss_mask;

View File

@@ -11,37 +11,43 @@
#include "common.h"
/*
* Available DMA channels, numbered from 0
* Available DMA channels, numbered from 0.
*
* Note: The STM datasheet tends to number things from 1. We should ask
* the European elevator engineers to talk to MCU engineer counterparts
* about this. This means that if the datasheet refers to channel n,
* you need to use n-1 in the code.
* you need to use DMAC_CHn (=n-1) in the code.
*
* Also note that channels are overloaded; obviously you can only use one
* function on each channel at a time.
*/
enum {
DMAC_ADC,
DMAC_SPI1_RX,
DMAC_SPI1_TX,
DMAC_SPI2_RX,
DMAC_SPI2_TX,
enum dma_channel {
/* Channel numbers */
DMAC_CH1 = 0,
DMAC_CH2 = 1,
DMAC_CH3 = 2,
DMAC_CH4 = 3,
DMAC_CH5 = 4,
DMAC_CH6 = 5,
DMAC_CH7 = 6,
/*
* The same channels are used for i2c2 and spi, you can't use them at
* the same time or it will cause dma to not work
*/
DMAC_I2C2_TX = 3,
DMAC_I2C2_RX = 4,
DMAC_I2C1_TX = 5,
DMAC_I2C1_RX = 6,
/* Channel functions */
DMAC_ADC = DMAC_CH1,
DMAC_SPI1_RX = DMAC_CH2,
DMAC_SPI1_TX = DMAC_CH3,
DMAC_I2C2_TX = DMAC_CH4,
DMAC_I2C2_RX = DMAC_CH5,
DMAC_USART1_TX = DMAC_CH4,
DMAC_USART1_RX = DMAC_CH5,
DMAC_I2C1_TX = DMAC_CH6,
DMAC_I2C1_RX = DMAC_CH7,
/* DMA1 has 7 channels, DMA2 has 5 */
DMA1_NUM_CHANNELS = 7,
DMA2_NUM_CHANNELS = 5,
DMA_NUM_CHANNELS = DMA1_NUM_CHANNELS + DMA2_NUM_CHANNELS,
/* Only DMA1 (with 7 channels) is present on STM32F100 and STM32L151x */
DMA_NUM_CHANNELS = 7,
};
/* A single channel of the DMA controller */
struct dma_channel {
/* Registers for a single channel of the DMA controller */
struct dma_channel_regs {
uint32_t ccr; /* Control */
uint32_t cndtr; /* Number of data to transfer */
uint32_t cpar; /* Peripheral address */
@@ -49,16 +55,12 @@ struct dma_channel {
uint32_t reserved;
};
/* Registers for the DMA controller */
struct dma_ctlr {
uint32_t isr;
uint32_t ifcr;
struct dma_channel chan[DMA_NUM_CHANNELS];
};
/* Always use dma_channel_t so volatile keyword is included! */
typedef volatile struct dma_channel_regs dma_channel_t;
/* DMA channel options */
struct dma_option {
unsigned channel; /* DMA channel */
enum dma_channel channel; /* DMA channel */
void *periph; /* Pointer to peripheral data register */
unsigned flags; /* DMA flags for the control register. Normally
used to select memory size. */
@@ -93,28 +95,13 @@ enum {
#define DMA_POLLING_INTERVAL_US 100 /* us */
#define DMA_TRANSFER_TIMEOUT_US (100 * MSEC) /* us */
/*
* Certain DMA channels must be used for certain peripherals and transfer
* directions. We provide an easy way for drivers to select the correct
* channel.
*/
/**
* @param spi SPI port to request: STM32_SPI1_PORT or STM32_SPI2_PORT
* @return DMA channel to use for rx / tx on that port
*/
#define DMA_CHANNEL_FOR_SPI_RX(spi) \
((spi) == STM32_SPI1_PORT ? DMAC_SPI1_RX : DMAC_SPI2_RX)
#define DMA_CHANNEL_FOR_SPI_TX(spi) \
((spi) == STM32_SPI1_PORT ? DMAC_SPI1_TX : DMAC_SPI2_TX)
/**
* Get a pointer to a DMA channel.
*
* @param channel Channel number to read (DMAC_...)
* @param channel Channel to read
* @return pointer to DMA channel registers
*/
struct dma_channel *dma_get_channel(int channel);
dma_channel_t *dma_get_channel(enum dma_channel channel);
/**
* Prepare a DMA transfer to transmit data from memory to a peripheral
@@ -137,17 +124,17 @@ void dma_prepare_tx(const struct dma_option *option, unsigned count,
* @param count Number of bytes to transfer
* @param memory Pointer to memory address
*/
int dma_start_rx(const struct dma_option *option, unsigned count,
const void *memory);
void dma_start_rx(const struct dma_option *option, unsigned count,
void *memory);
/**
* Stop a DMA transfer on a channel
*
* Disable the DMA channel and immediate stop all transfers on it.
*
* @param channel Channel number to stop (DMAC_...)
* @param channel Channel to stop
*/
void dma_disable(unsigned channel);
void dma_disable(enum dma_channel channel);
/**
* Get the number of bytes available to read, or number of bytes written
@@ -155,75 +142,69 @@ void dma_disable(unsigned channel);
* Since the DMA controller counts downwards, if we know the starting value
* we can work out how many bytes have been completed so far.
*
* @param chan DMA channel to check (use dma_get_channel())
* @param chan DMA channel to check, from dma_get_channel()
* @param orig_count Original number of bytes requested on channel
* @return number of bytes completed on a channel, or 0 if this channel is
* not enabled
*/
int dma_bytes_done(struct dma_channel *chan, int orig_count);
int dma_bytes_done(dma_channel_t *chan, int orig_count);
/**
* Start a previously-prepared dma channel
*
* @param chan Channel to start (returned from dma_prepare...())
* @param chan Channel to start, from dma_get_channel()
*/
void dma_go(struct dma_channel *chan);
void dma_go(dma_channel_t *chan);
#ifdef CONFIG_DMA_HELP
/**
* Testing: Print out the data transferred by a channel
*
* @param channel Channel number to read (DMAC_...)
* @param channel Channel to read
* @param buff Start of DMA buffer
*/
void dma_check(int channel, char *buff);
void dma_check(enum dma_channel channel, char *buf);
/**
* Dump out imformation about a dma channel
*
* @param channel Channel number to read (DMAC_...)
* @param channel Channel to read
*/
void dma_dump(unsigned channel);
void dma_dump(enum dma_channel channel);
/**
* Testing: Test that DMA works correctly for memory to memory transfers
*/
void dma_test(void);
#endif /* CONFIG_DMA_HELP */
/**
* Clear the DMA interrupt/event flags for a given channel
*
* @param channel Which channel's isr to clear (DMAC_...)
* @param channel Which channel's isr to clear
*/
void dma_clear_isr(int channel);
void dma_clear_isr(enum dma_channel channel);
/**
* Enable "Transfer Complete" interrupt for a DMA channel
*
* @param channel Which channel's interrupts to change (DMAC_...)
* @param channel Which channel's interrupts to change
*/
void dma_enable_tc_interrupt(int channel);
void dma_enable_tc_interrupt(enum dma_channel channel);
/**
* Disable "Transfer Complete" interrupt for a DMA channel
*
* @param channel Which channel's interrupts to change (DMAC_...)
* @param channel Which channel's interrupts to change
*/
void dma_disable_tc_interrupt(int channel);
/**
* Get a pointer to the DMA peripheral controller that owns the channel
*
* @param channel Channel number to get the controller for (DMAC_...)
* @return pointer to DMA channel registers
*/
struct dma_ctlr *dma_get_ctlr(int channel);
void dma_disable_tc_interrupt(enum dma_channel channel);
/**
* Wait for the DMA transfer to complete by polling the transfer complete flag
*
* @param channel» Channel number to wait on (DMAC_...)
* @return -1 for timeout, 0 for sucess
* @param channel Channel number to wait on
* @return EC_ERROR_TIMEOUT for timeout, EC_SUCCESS for success
*/
int dma_wait(int channel);
int dma_wait(enum dma_channel channel);
#endif