diff options
author | Nick Sanders <nsanders@chromium.org> | 2016-07-26 13:17:09 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-08-17 16:19:07 -0700 |
commit | 6fcd163da5169bfca36ab8c15cfd9d0624acae19 (patch) | |
tree | cc1e3cd999fa3df95547356e8160fd966aa26bc3 /chip/stm32/dma-stm32f4.c | |
parent | 6fad4f8588242cd6202e1177e145073c6aff6b7a (diff) | |
download | chrome-ec-6fcd163da5169bfca36ab8c15cfd9d0624acae19.tar.gz |
stm32f446e-eval: add support for stm32f446
This adds basic support for the stm32f446.
This consists of:
* New DMA model for stm32f4
* New clock domain support.
* MCO oscillator gpio export support.
* Flash support for irregular blocks.
BUG=chromium:608039
TEST=boots w/ correct clock, stm32f0 also boots.
BRANCH=None
Change-Id: I1c5cf6ddca09009c9dac60da8a3d0c5ceedfcf4d
Signed-off-by: Nick Sanders <nsanders@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/363992
Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
Diffstat (limited to 'chip/stm32/dma-stm32f4.c')
-rw-r--r-- | chip/stm32/dma-stm32f4.c | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/chip/stm32/dma-stm32f4.c b/chip/stm32/dma-stm32f4.c new file mode 100644 index 0000000000..135df53b1a --- /dev/null +++ b/chip/stm32/dma-stm32f4.c @@ -0,0 +1,308 @@ +/* Copyright 2016 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "common.h" +#include "console.h" +#include "dma.h" +#include "hooks.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "util.h" + +/* Console output macros */ +#define CPUTS(outstr) cputs(CC_DMA, outstr) +#define CPRINTF(format, args...) cprintf(CC_DMA, format, ## args) + +stm32_dma_regs_t *STM32_DMA_REGS[] = { STM32_DMA1_REGS, STM32_DMA2_REGS }; + +/* Callback data to use when IRQ fires */ +static struct { + void (*cb)(void *); /* Callback function to call */ + void *cb_data; /* Callback data for callback function */ +} dma_irq[STM32_DMAS_TOTAL_COUNT]; + +/** + * Return the IRQ for the DMA stream + * + * @param stream stream number + * @return IRQ for the stream + */ +static int dma_get_irq(enum dma_channel stream) +{ + if (stream < STM32_DMA1_STREAM6) + return STM32_IRQ_DMA1_STREAM0 + stream; + if (stream == STM32_DMA1_STREAM7) + return STM32_IRQ_DMA1_STREAM7; + if (stream < STM32_DMA2_STREAM5) + return STM32_IRQ_DMA2_STREAM0 + stream - STM32_DMA2_STREAM0; + else + return STM32_IRQ_DMA2_STREAM5 + stream - STM32_DMA2_STREAM5; +} + +stm32_dma_regs_t *dma_get_ctrl(enum dma_channel stream) +{ + return STM32_DMA_REGS[stream / STM32_DMAS_COUNT]; +} + +stm32_dma_stream_t *dma_get_channel(enum dma_channel stream) +{ + stm32_dma_regs_t *dma = dma_get_ctrl(stream); + + return &dma->stream[stream % STM32_DMAS_COUNT]; +} + +void dma_disable(enum dma_channel ch) +{ + stm32_dma_stream_t *stream = dma_get_channel(ch); + + if (stream->scr & STM32_DMA_CCR_EN) { + stream->scr &= ~STM32_DMA_CCR_EN; + while (stream->scr & STM32_DMA_CCR_EN) + ; + } +} + +void dma_disable_all(void) +{ + int ch; + + for (ch = 0; ch < STM32_DMAS_TOTAL_COUNT; ch++) + dma_disable(ch); +} + +/** + * Prepare a stream for use and start it + * + * @param stream stream to read + * @param count Number of bytes to transfer + * @param periph Pointer to peripheral data register + * @param memory Pointer to memory address for receive/transmit + * @param flags DMA flags for the control register. + */ +static void prepare_stream(enum dma_channel stream, unsigned count, + void *periph, void *memory, unsigned flags) +{ + stm32_dma_stream_t *dma_stream = dma_get_channel(stream); + uint32_t ccr = STM32_DMA_CCR_PL_VERY_HIGH; + + dma_disable(stream); + dma_clear_isr(stream); + + /* Following the order in DocID026448 Rev 1 (RM0383) p181 */ + dma_stream->spar = (uint32_t)periph; + dma_stream->sm0ar = (uint32_t)memory; + dma_stream->sndtr = count; + dma_stream->scr = ccr; + ccr |= flags & STM32_DMA_CCR_CHANNEL_MASK; + dma_stream->scr = ccr; + dma_stream->sfcr &= ~STM32_DMA_SFCR_DMDIS; + ccr |= flags; + dma_stream->scr = ccr; +} + +void dma_go(stm32_dma_stream_t *stream) +{ + /* Flush data in write buffer so that DMA can get the lastest data */ + asm volatile("dsb;"); + + /* Fire it up */ + stream->scr |= STM32_DMA_CCR_EN; +} + +void dma_prepare_tx(const struct dma_option *option, unsigned count, + const void *memory) +{ + /* + * Cast away const for memory pointer; this is ok because we know + * we're preparing the stream for transmit. + */ + prepare_stream(option->channel, count, option->periph, (void *)memory, + STM32_DMA_CCR_MINC | STM32_DMA_CCR_DIR_M2P | + option->flags); +} + +void dma_start_rx(const struct dma_option *option, unsigned count, + void *memory) +{ + stm32_dma_stream_t *stream = dma_get_channel(option->channel); + + prepare_stream(option->channel, count, option->periph, memory, + STM32_DMA_CCR_MINC | STM32_DMA_CCR_DIR_P2M | + option->flags); + dma_go(stream); +} + +int dma_bytes_done(stm32_dma_stream_t *stream, int orig_count) +{ + if (!(stream->scr & STM32_DMA_CCR_EN)) + return 0; + return orig_count - stream->sndtr; +} + +#ifdef CONFIG_DMA_HELP +void dma_dump(enum dma_channel stream) +{ + stm32_dma_stream_t *dma_stream = dma_get_channel(stream); + + CPRINTF("scr=%x, sndtr=%x, spar=%x, sm0ar=%x, sfcr=%x\n", + dma_stream->scr, dma_stream->sndtr, dma_stream->spar, + dma_stream->sm0ar, dma_stream->sfcr); + CPRINTF("stream %d, isr=%x, ifcr=%x\n", + stream, + STM32_DMA_GET_ISR(stream), + STM32_DMA_GET_IFCR(stream)); +} + +void dma_check(enum dma_channel stream, char *buf) +{ + stm32_dma_stream_t *dma_stream = dma_get_channel(stream); + int count; + int i; + + count = dma_stream->sndtr; + CPRINTF("c=%d\n", count); + udelay(100 * MSEC); + CPRINTF("c=%d\n", dma_stream->sndtr); + for (i = 0; i < count; i++) + CPRINTF("%02x ", buf[i]); + udelay(100 * MSEC); + CPRINTF("c=%d\n", dma_stream->sndtr); + for (i = 0; i < count; i++) + CPRINTF("%02x ", buf[i]); +} + +/* Run a check of memory-to-memory DMA */ +void dma_test(enum dma_channel stream) +{ + stm32_dma_stream_t *dma_stream = dma_get_channel(stream); + uint32_t ctrl; + char periph[32], memory[32]; + unsigned count = sizeof(periph); + int i; + + memset(memory, '\0', sizeof(memory)); + for (i = 0; i < count; i++) + periph[i] = 10 + i; + + dma_clear_isr(stream); + /* Following the order in Doc ID 15965 Rev 5 p194 */ + dma_stream->spar = (uint32_t)periph; + dma_stream->sm0ar = (uint32_t)memory; + dma_stream->sndtr = count; + dma_stream->sfcr &= ~STM32_DMA_SFCR_DMDIS; + ctrl = STM32_DMA_CCR_PL_MEDIUM; + dma_stream->scr = ctrl; + + ctrl |= STM32_DMA_CCR_MINC; + ctrl |= STM32_DMA_CCR_DIR_M2M; + ctrl |= STM32_DMA_CCR_PINC; + + dma_stream->scr = ctrl; + dma_dump(stream); + dma_stream->scr = ctrl | STM32_DMA_CCR_EN; + + for (i = 0; i < count; i++) + CPRINTF("%d/%d ", periph[i], memory[i]); + CPRINTF("\ncount=%d\n", dma_stream->sndtr); + dma_dump(stream); +} +#endif /* CONFIG_DMA_HELP */ + +void dma_init(void) +{ + STM32_RCC_AHB1ENR |= STM32_RCC_HB1_DMA1 | STM32_RCC_HB1_DMA2; +} + +int dma_wait(enum dma_channel stream) +{ + timestamp_t deadline; + + deadline.val = get_time().val + DMA_TRANSFER_TIMEOUT_US; + while ((STM32_DMA_GET_ISR(stream) & STM32_DMA_TCIF) == 0) { + if (deadline.val <= get_time().val) + return EC_ERROR_TIMEOUT; + + udelay(DMA_POLLING_INTERVAL_US); + } + return EC_SUCCESS; +} + +static inline void _dma_wake_callback(void *cb_data) +{ + task_id_t id = (task_id_t)(int)cb_data; + + if (id != TASK_ID_INVALID) + task_set_event(id, TASK_EVENT_DMA_TC, 0); +} + +void dma_enable_tc_interrupt(enum dma_channel stream) +{ + dma_enable_tc_interrupt_callback(stream, _dma_wake_callback, + (void *)(int)task_get_current()); +} + +void dma_enable_tc_interrupt_callback(enum dma_channel stream, + void (*callback)(void *), + void *callback_data) +{ + stm32_dma_stream_t *dma_stream = dma_get_channel(stream); + + dma_irq[stream].cb = callback; + dma_irq[stream].cb_data = callback_data; + + dma_stream->scr |= STM32_DMA_CCR_TCIE; + task_enable_irq(dma_get_irq(stream)); +} + +void dma_disable_tc_interrupt(enum dma_channel stream) +{ + stm32_dma_stream_t *dma_stream = dma_get_channel(stream); + + dma_stream->scr &= ~STM32_DMA_CCR_TCIE; + task_disable_irq(dma_get_irq(stream)); + + dma_irq[stream].cb = NULL; + dma_irq[stream].cb_data = NULL; +} + +void dma_clear_isr(enum dma_channel stream) +{ + STM32_DMA_SET_IFCR(stream, STM32_DMA_ALL); +} + +#ifdef CONFIG_DMA_DEFAULT_HANDLERS +#define STM32_DMA_IDX(dma, x) CONCAT4(STM32_DMA, dma, _STREAM, x) +#define STM32_DMA_FCT(dma, x) CONCAT4(dma_, dma, _event_interrupt_stream_, x) +#define DECLARE_DMA_IRQ(dma, x) \ + void STM32_DMA_FCT(dma, x)(void) \ + { \ + dma_clear_isr(STM32_DMA_IDX(dma, x)); \ + if (dma_irq[STM32_DMA_IDX(dma, x)].cb != NULL) \ + (*dma_irq[STM32_DMA_IDX(dma, x)].cb) \ + (dma_irq[STM32_DMA_IDX(dma, x)].cb_data); \ + } \ + DECLARE_IRQ(CONCAT4(STM32_IRQ_DMA, dma, _STREAM, x), \ + STM32_DMA_FCT(dma, x), 1); + +DECLARE_DMA_IRQ(1, 0); +DECLARE_DMA_IRQ(1, 1); +DECLARE_DMA_IRQ(1, 2); +DECLARE_DMA_IRQ(1, 3); +DECLARE_DMA_IRQ(1, 4); +DECLARE_DMA_IRQ(1, 5); +DECLARE_DMA_IRQ(1, 6); +DECLARE_DMA_IRQ(1, 7); +DECLARE_DMA_IRQ(2, 0); +DECLARE_DMA_IRQ(2, 1); +DECLARE_DMA_IRQ(2, 2); +DECLARE_DMA_IRQ(2, 3); +DECLARE_DMA_IRQ(2, 4); +DECLARE_DMA_IRQ(2, 5); +DECLARE_DMA_IRQ(2, 6); +DECLARE_DMA_IRQ(2, 7); + +#endif /* CONFIG_DMA_DEFAULT_HANDLERS */ + |