summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVic Yang <victoryang@chromium.org>2014-06-20 18:27:07 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-06-26 02:42:14 +0000
commit598c92b2cc8f49796824488adc141f9714b49d20 (patch)
treef5246ee274fccade35683c97e6d038009a2aceb7
parenta56f96655663e0c69a0de035ce161baf477dedb6 (diff)
downloadchrome-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.mk1
-rw-r--r--chip/mec1322/config_chip.h1
-rw-r--r--chip/mec1322/dma.c129
-rw-r--r--chip/mec1322/registers.h75
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