diff options
author | Vic Yang <victoryang@chromium.org> | 2014-06-20 18:27:07 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-06-26 02:42:14 +0000 |
commit | 598c92b2cc8f49796824488adc141f9714b49d20 (patch) | |
tree | f5246ee274fccade35683c97e6d038009a2aceb7 | |
parent | a56f96655663e0c69a0de035ce161baf477dedb6 (diff) | |
download | chrome-ec-598c92b2cc8f49796824488adc141f9714b49d20.tar.gz |
mec1322: DMA driver
This implements the DMA driver using the same DMA interface we are using
now.
BUG=chrome-os-partner:29805
TEST=Along with the following SPI driver, read manufacturer ID from SPI
flash.
BRANCH=None
Change-Id: Ife3c0c8b414568ff1cab7d072901ba2d11142a17
Signed-off-by: Vic Yang <victoryang@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/205067
Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r-- | chip/mec1322/build.mk | 1 | ||||
-rw-r--r-- | chip/mec1322/config_chip.h | 1 | ||||
-rw-r--r-- | chip/mec1322/dma.c | 129 | ||||
-rw-r--r-- | chip/mec1322/registers.h | 75 |
4 files changed, 206 insertions, 0 deletions
diff --git a/chip/mec1322/build.mk b/chip/mec1322/build.mk index 5481581ebe..33338bf9e1 100644 --- a/chip/mec1322/build.mk +++ b/chip/mec1322/build.mk @@ -20,3 +20,4 @@ chip-$(CONFIG_LPC)+=lpc.o chip-$(CONFIG_PWM)+=pwm.o chip-$(CONFIG_WATCHDOG)+=watchdog.o chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o +chip-$(CONFIG_DMA)+=dma.o diff --git a/chip/mec1322/config_chip.h b/chip/mec1322/config_chip.h index 939107ca8c..20c13c7633 100644 --- a/chip/mec1322/config_chip.h +++ b/chip/mec1322/config_chip.h @@ -89,6 +89,7 @@ #define CONFIG_I2C #define CONFIG_LPC #define CONFIG_FPU +#define CONFIG_DMA #undef CONFIG_FLASH diff --git a/chip/mec1322/dma.c b/chip/mec1322/dma.c new file mode 100644 index 0000000000..c064003544 --- /dev/null +++ b/chip/mec1322/dma.c @@ -0,0 +1,129 @@ +/* Copyright (c) 2014 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 CPRINTS(format, args...) cprints(CC_DMA, format, ## args) + +mec1322_dma_chan_t *dma_get_channel(enum dma_channel channel) +{ + mec1322_dma_regs_t *dma = MEC1322_DMA_REGS; + + return &dma->chan[channel]; +} + +void dma_disable(enum dma_channel channel) +{ + mec1322_dma_chan_t *chan = dma_get_channel(channel); + + if (chan->ctrl & (1 << 0)) + chan->ctrl &= ~(1 << 0); +} + +/** + * Prepare a channel for use and start it + * + * @param chan Channel 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, normally: + * MEC1322_DMA_INC_MEM | MEC1322_DMA_TO_DEV for tx + * MEC1322_DMA_INC_MEM for rx + */ +static void prepare_channel(mec1322_dma_chan_t *chan, unsigned count, + void *periph, void *memory, unsigned flags) +{ + int xfer_size = (flags >> 20) & 0x7; + + if (chan->ctrl & (1 << 0)) + chan->ctrl &= ~(1 << 0); + + chan->act |= 0x1; + chan->dev = (uint32_t)periph; + chan->mem_start = MEC1322_RAM_ALIAS((uint32_t)memory); + chan->mem_end = MEC1322_RAM_ALIAS((uint32_t)memory) + xfer_size * count; + chan->ctrl = flags; +} + +void dma_go(mec1322_dma_chan_t *chan) +{ + /* Flush data in write buffer so that DMA can get the lastest data */ + asm volatile("dsb;"); + + /* Fire it up */ + chan->ctrl |= MEC1322_DMA_RUN; +} + +void dma_prepare_tx(const struct dma_option *option, unsigned count, + const void *memory) +{ + mec1322_dma_chan_t *chan = dma_get_channel(option->channel); + + /* + * 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, + MEC1322_DMA_INC_MEM | MEC1322_DMA_TO_DEV | + MEC1322_DMA_DEV(option->channel) | option->flags); +} + +void dma_start_rx(const struct dma_option *option, unsigned count, + void *memory) +{ + mec1322_dma_chan_t *chan = dma_get_channel(option->channel); + + prepare_channel(chan, count, option->periph, memory, + MEC1322_DMA_INC_MEM | MEC1322_DMA_DEV(option->channel) | + option->flags); + dma_go(chan); +} + +int dma_bytes_done(mec1322_dma_chan_t *chan, int orig_count) +{ + int xfer_size = (chan->ctrl >> 20) & 0x7; + + if (!(chan->ctrl & MEC1322_DMA_RUN)) + return 0; + return orig_count - (chan->mem_end - chan->mem_start) / xfer_size; +} + +void dma_init(void) +{ + mec1322_dma_regs_t *dma = MEC1322_DMA_REGS; + dma->ctrl |= 0x1; +} + +int dma_wait(enum dma_channel channel) +{ + mec1322_dma_chan_t *chan = dma_get_channel(channel); + timestamp_t deadline; + + deadline.val = get_time().val + DMA_TRANSFER_TIMEOUT_US; + while (!(chan->int_status & 0x4)) { + if (deadline.val <= get_time().val) + return EC_ERROR_TIMEOUT; + + udelay(DMA_POLLING_INTERVAL_US); + } + return EC_SUCCESS; +} + +void dma_clear_isr(enum dma_channel channel) +{ + mec1322_dma_chan_t *chan = dma_get_channel(channel); + + chan->int_status |= 0x4; +} diff --git a/chip/mec1322/registers.h b/chip/mec1322/registers.h index 396b9efbe7..8447816f78 100644 --- a/chip/mec1322/registers.h +++ b/chip/mec1322/registers.h @@ -10,6 +10,10 @@ #include "common.h" +/* Helper function for RAM address aliasing */ +#define MEC1322_RAM_ALIAS(x) \ + ((x) >= 0x118000 ? (x) - 0x118000 + 0x20000000 : (x)) + /* EC Chip Configuration */ #define MEC1322_CHIP_BASE 0x400fff00 #define MEC1322_CHIP_DEV_ID REG8(MEC1322_CHIP_BASE + 0x20) @@ -293,6 +297,77 @@ static inline uintptr_t gpio_port_base(int port_id) #define MEC1322_HTIMER_COUNT REG16(MEC1322_HTIMER_BASE + 0x8) +/* DMA */ +#define MEC1322_DMA_BASE 0x40002400 + +/* + * Available DMA channels. + * + * On MEC1322, any DMA channel may serve any device. Since we have + * 12 channels and 12 devices, we make each channel dedicated to the + * device of the same number. + */ +enum dma_channel { + /* Channel numbers */ + MEC1322_DMAC_I2C0_SLAVE = 0, + MEC1322_DMAC_I2C0_MASTER = 1, + MEC1322_DMAC_I2C1_SLAVE = 2, + MEC1322_DMAC_I2C1_MASTER = 3, + MEC1322_DMAC_I2C2_SLAVE = 4, + MEC1322_DMAC_I2C2_MASTER = 5, + MEC1322_DMAC_I2C3_SLAVE = 6, + MEC1322_DMAC_I2C3_MASTER = 7, + MEC1322_DMAC_SPI0_TX = 8, + MEC1322_DMAC_SPI0_RX = 9, + MEC1322_DMAC_SPI1_TX = 10, + MEC1322_DMAC_SPI1_RX = 11, + + /* Channel count */ + MEC1322_DMAC_COUNT = 12, +}; + +/* Registers for a single channel of the DMA controller */ +struct mec1322_dma_chan { + uint32_t act; /* Activate */ + uint32_t mem_start; /* Memory start address */ + uint32_t mem_end; /* Memory end address */ + uint32_t dev; /* Device address */ + uint32_t ctrl; /* Control */ + uint32_t int_status; /* Interrupt status */ + uint32_t int_enabled; /* Interrupt enabled */ + uint32_t pad; +}; + +/* Always use mec1322_dma_chan_t so volatile keyword is included! */ +typedef volatile struct mec1322_dma_chan mec1322_dma_chan_t; + +/* Common code and header file must use this */ +typedef mec1322_dma_chan_t dma_chan_t; + +/* Registers for the DMA controller */ +struct mec1322_dma_regs { + uint32_t ctrl; + uint32_t data; + uint32_t pad[2]; + mec1322_dma_chan_t chan[MEC1322_DMAC_COUNT]; +}; + +/* Always use mec1322_dma_regs_t so volatile keyword is included! */ +typedef volatile struct mec1322_dma_regs mec1322_dma_regs_t; + +#define MEC1322_DMA_REGS ((mec1322_dma_regs_t *)MEC1322_DMA_BASE) + +/* Bits for DMA channel regs */ +#define MEC1322_DMA_ACT_EN (1 << 0) +#define MEC1322_DMA_XFER_SIZE(x) ((x) << 20) +#define MEC1322_DMA_INC_DEV (1 << 17) +#define MEC1322_DMA_INC_MEM (1 << 16) +#define MEC1322_DMA_DEV(x) ((x) << 9) +#define MEC1322_DMA_TO_DEV (1 << 8) +#define MEC1322_DMA_DONE (1 << 2) +#define MEC1322_DMA_RUN (1 << 0) + + /* IRQ Numbers */ #define MEC1322_IRQ_I2C_0 0 #define MEC1322_IRQ_I2C_1 1 |