/* 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); if (chan->act == 1) chan->act = 0; } void dma_disable_all(void) { int ch; mec1322_dma_regs_t *dma; for (ch = 0; ch < MEC1322_DMAC_COUNT; ch++) { mec1322_dma_chan_t *chan = dma_get_channel(ch); /* Abort any current transfer. */ chan->ctrl |= (1 << 25); /* Disable the channel. */ chan->ctrl &= ~(1 << 0); chan->act = 0; } /* Soft-reset the block. */ dma = MEC1322_DMA_REGS; dma->ctrl |= 0x2; } /** * 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; 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; if (chan->act == 0) return EC_SUCCESS; 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; }