diff options
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/Makefile | 2 | ||||
-rw-r--r-- | drivers/dma/keystone_nav.c | 332 | ||||
-rw-r--r-- | drivers/dma/keystone_nav_cfg.c | 27 | ||||
-rw-r--r-- | drivers/dma/ti-edma3.c | 384 |
4 files changed, 745 insertions, 0 deletions
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index a79c391..4c8fcc2 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -8,3 +8,5 @@ obj-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o obj-$(CONFIG_APBH_DMA) += apbh_dma.o obj-$(CONFIG_FSL_DMA) += fsl_dma.o +obj-$(CONFIG_TI_KSNAV) += keystone_nav.o keystone_nav_cfg.o +obj-$(CONFIG_TI_EDMA3) += ti-edma3.o diff --git a/drivers/dma/keystone_nav.c b/drivers/dma/keystone_nav.c new file mode 100644 index 0000000..77707c2 --- /dev/null +++ b/drivers/dma/keystone_nav.c @@ -0,0 +1,332 @@ +/* + * Multicore Navigator driver for TI Keystone 2 devices. + * + * (C) Copyright 2012-2014 + * Texas Instruments Incorporated, <www.ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <asm/io.h> +#include <asm/ti-common/keystone_nav.h> + +struct qm_config qm_memmap = { + .stat_cfg = CONFIG_KSNAV_QM_QUEUE_STATUS_BASE, + .queue = (void *)CONFIG_KSNAV_QM_MANAGER_QUEUES_BASE, + .mngr_vbusm = CONFIG_KSNAV_QM_BASE_ADDRESS, + .i_lram = CONFIG_KSNAV_QM_LINK_RAM_BASE, + .proxy = (void *)CONFIG_KSNAV_QM_MANAGER_Q_PROXY_BASE, + .status_ram = CONFIG_KSNAV_QM_STATUS_RAM_BASE, + .mngr_cfg = (void *)CONFIG_KSNAV_QM_CONF_BASE, + .intd_cfg = CONFIG_KSNAV_QM_INTD_CONF_BASE, + .desc_mem = (void *)CONFIG_KSNAV_QM_DESC_SETUP_BASE, + .region_num = CONFIG_KSNAV_QM_REGION_NUM, + .pdsp_cmd = CONFIG_KSNAV_QM_PDSP1_CMD_BASE, + .pdsp_ctl = CONFIG_KSNAV_QM_PDSP1_CTRL_BASE, + .pdsp_iram = CONFIG_KSNAV_QM_PDSP1_IRAM_BASE, + .qpool_num = CONFIG_KSNAV_QM_QPOOL_NUM, +}; + +/* + * We are going to use only one type of descriptors - host packet + * descriptors. We staticaly allocate memory for them here + */ +struct qm_host_desc desc_pool[HDESC_NUM] __aligned(sizeof(struct qm_host_desc)); + +static struct qm_config *qm_cfg; + +inline int num_of_desc_to_reg(int num_descr) +{ + int j, num; + + for (j = 0, num = 32; j < 15; j++, num *= 2) { + if (num_descr <= num) + return j; + } + + return 15; +} + +int _qm_init(struct qm_config *cfg) +{ + u32 j; + + qm_cfg = cfg; + + qm_cfg->mngr_cfg->link_ram_base0 = qm_cfg->i_lram; + qm_cfg->mngr_cfg->link_ram_size0 = HDESC_NUM * 8; + qm_cfg->mngr_cfg->link_ram_base1 = 0; + qm_cfg->mngr_cfg->link_ram_size1 = 0; + qm_cfg->mngr_cfg->link_ram_base2 = 0; + + qm_cfg->desc_mem[0].base_addr = (u32)desc_pool; + qm_cfg->desc_mem[0].start_idx = 0; + qm_cfg->desc_mem[0].desc_reg_size = + (((sizeof(struct qm_host_desc) >> 4) - 1) << 16) | + num_of_desc_to_reg(HDESC_NUM); + + memset(desc_pool, 0, sizeof(desc_pool)); + for (j = 0; j < HDESC_NUM; j++) + qm_push(&desc_pool[j], qm_cfg->qpool_num); + + return QM_OK; +} + +int qm_init(void) +{ + return _qm_init(&qm_memmap); +} + +void qm_close(void) +{ + u32 j; + + if (qm_cfg == NULL) + return; + + queue_close(qm_cfg->qpool_num); + + qm_cfg->mngr_cfg->link_ram_base0 = 0; + qm_cfg->mngr_cfg->link_ram_size0 = 0; + qm_cfg->mngr_cfg->link_ram_base1 = 0; + qm_cfg->mngr_cfg->link_ram_size1 = 0; + qm_cfg->mngr_cfg->link_ram_base2 = 0; + + for (j = 0; j < qm_cfg->region_num; j++) { + qm_cfg->desc_mem[j].base_addr = 0; + qm_cfg->desc_mem[j].start_idx = 0; + qm_cfg->desc_mem[j].desc_reg_size = 0; + } + + qm_cfg = NULL; +} + +void qm_push(struct qm_host_desc *hd, u32 qnum) +{ + u32 regd; + + if (!qm_cfg) + return; + + cpu_to_bus((u32 *)hd, sizeof(struct qm_host_desc)/4); + regd = (u32)hd | ((sizeof(struct qm_host_desc) >> 4) - 1); + writel(regd, &qm_cfg->queue[qnum].ptr_size_thresh); +} + +void qm_buff_push(struct qm_host_desc *hd, u32 qnum, + void *buff_ptr, u32 buff_len) +{ + hd->orig_buff_len = buff_len; + hd->buff_len = buff_len; + hd->orig_buff_ptr = (u32)buff_ptr; + hd->buff_ptr = (u32)buff_ptr; + qm_push(hd, qnum); +} + +struct qm_host_desc *qm_pop(u32 qnum) +{ + u32 uhd; + + if (!qm_cfg) + return NULL; + + uhd = readl(&qm_cfg->queue[qnum].ptr_size_thresh) & ~0xf; + if (uhd) + cpu_to_bus((u32 *)uhd, sizeof(struct qm_host_desc)/4); + + return (struct qm_host_desc *)uhd; +} + +struct qm_host_desc *qm_pop_from_free_pool(void) +{ + if (!qm_cfg) + return NULL; + + return qm_pop(qm_cfg->qpool_num); +} + +void queue_close(u32 qnum) +{ + struct qm_host_desc *hd; + + while ((hd = qm_pop(qnum))) + ; +} + +/** + * DMA API + */ + +static int ksnav_rx_disable(struct pktdma_cfg *pktdma) +{ + u32 j, v, k; + + for (j = 0; j < pktdma->rx_ch_num; j++) { + v = readl(&pktdma->rx_ch[j].cfg_a); + if (!(v & CPDMA_CHAN_A_ENABLE)) + continue; + + writel(v | CPDMA_CHAN_A_TDOWN, &pktdma->rx_ch[j].cfg_a); + for (k = 0; k < TDOWN_TIMEOUT_COUNT; k++) { + udelay(100); + v = readl(&pktdma->rx_ch[j].cfg_a); + if (!(v & CPDMA_CHAN_A_ENABLE)) + continue; + } + /* TODO: teardown error on if TDOWN_TIMEOUT_COUNT is reached */ + } + + /* Clear all of the flow registers */ + for (j = 0; j < pktdma->rx_flow_num; j++) { + writel(0, &pktdma->rx_flows[j].control); + writel(0, &pktdma->rx_flows[j].tags); + writel(0, &pktdma->rx_flows[j].tag_sel); + writel(0, &pktdma->rx_flows[j].fdq_sel[0]); + writel(0, &pktdma->rx_flows[j].fdq_sel[1]); + writel(0, &pktdma->rx_flows[j].thresh[0]); + writel(0, &pktdma->rx_flows[j].thresh[1]); + writel(0, &pktdma->rx_flows[j].thresh[2]); + } + + return QM_OK; +} + +static int ksnav_tx_disable(struct pktdma_cfg *pktdma) +{ + u32 j, v, k; + + for (j = 0; j < pktdma->tx_ch_num; j++) { + v = readl(&pktdma->tx_ch[j].cfg_a); + if (!(v & CPDMA_CHAN_A_ENABLE)) + continue; + + writel(v | CPDMA_CHAN_A_TDOWN, &pktdma->tx_ch[j].cfg_a); + for (k = 0; k < TDOWN_TIMEOUT_COUNT; k++) { + udelay(100); + v = readl(&pktdma->tx_ch[j].cfg_a); + if (!(v & CPDMA_CHAN_A_ENABLE)) + continue; + } + /* TODO: teardown error on if TDOWN_TIMEOUT_COUNT is reached */ + } + + return QM_OK; +} + +int ksnav_init(struct pktdma_cfg *pktdma, struct rx_buff_desc *rx_buffers) +{ + u32 j, v; + struct qm_host_desc *hd; + u8 *rx_ptr; + + if (pktdma == NULL || rx_buffers == NULL || + rx_buffers->buff_ptr == NULL || qm_cfg == NULL) + return QM_ERR; + + pktdma->rx_flow = rx_buffers->rx_flow; + + /* init rx queue */ + rx_ptr = rx_buffers->buff_ptr; + + for (j = 0; j < rx_buffers->num_buffs; j++) { + hd = qm_pop(qm_cfg->qpool_num); + if (hd == NULL) + return QM_ERR; + + qm_buff_push(hd, pktdma->rx_free_q, + rx_ptr, rx_buffers->buff_len); + + rx_ptr += rx_buffers->buff_len; + } + + ksnav_rx_disable(pktdma); + + /* configure rx channels */ + v = CPDMA_REG_VAL_MAKE_RX_FLOW_A(1, 1, 0, 0, 0, 0, 0, pktdma->rx_rcv_q); + writel(v, &pktdma->rx_flows[pktdma->rx_flow].control); + writel(0, &pktdma->rx_flows[pktdma->rx_flow].tags); + writel(0, &pktdma->rx_flows[pktdma->rx_flow].tag_sel); + + v = CPDMA_REG_VAL_MAKE_RX_FLOW_D(0, pktdma->rx_free_q, 0, + pktdma->rx_free_q); + + writel(v, &pktdma->rx_flows[pktdma->rx_flow].fdq_sel[0]); + writel(v, &pktdma->rx_flows[pktdma->rx_flow].fdq_sel[1]); + writel(0, &pktdma->rx_flows[pktdma->rx_flow].thresh[0]); + writel(0, &pktdma->rx_flows[pktdma->rx_flow].thresh[1]); + writel(0, &pktdma->rx_flows[pktdma->rx_flow].thresh[2]); + + for (j = 0; j < pktdma->rx_ch_num; j++) + writel(CPDMA_CHAN_A_ENABLE, &pktdma->rx_ch[j].cfg_a); + + /* configure tx channels */ + /* Disable loopback in the tx direction */ + writel(0, &pktdma->global->emulation_control); + + /* Set QM base address, only for K2x devices */ + writel(CONFIG_KSNAV_QM_BASE_ADDRESS, &pktdma->global->qm_base_addr[0]); + + /* Enable all channels. The current state isn't important */ + for (j = 0; j < pktdma->tx_ch_num; j++) { + writel(0, &pktdma->tx_ch[j].cfg_b); + writel(CPDMA_CHAN_A_ENABLE, &pktdma->tx_ch[j].cfg_a); + } + + return QM_OK; +} + +int ksnav_close(struct pktdma_cfg *pktdma) +{ + if (!pktdma) + return QM_ERR; + + ksnav_tx_disable(pktdma); + ksnav_rx_disable(pktdma); + + queue_close(pktdma->rx_free_q); + queue_close(pktdma->rx_rcv_q); + queue_close(pktdma->tx_snd_q); + + return QM_OK; +} + +int ksnav_send(struct pktdma_cfg *pktdma, u32 *pkt, int num_bytes, u32 swinfo2) +{ + struct qm_host_desc *hd; + + hd = qm_pop(qm_cfg->qpool_num); + if (hd == NULL) + return QM_ERR; + + hd->desc_info = num_bytes; + hd->swinfo[2] = swinfo2; + hd->packet_info = qm_cfg->qpool_num; + + qm_buff_push(hd, pktdma->tx_snd_q, pkt, num_bytes); + + return QM_OK; +} + +void *ksnav_recv(struct pktdma_cfg *pktdma, u32 **pkt, int *num_bytes) +{ + struct qm_host_desc *hd; + + hd = qm_pop(pktdma->rx_rcv_q); + if (!hd) + return NULL; + + *pkt = (u32 *)hd->buff_ptr; + *num_bytes = hd->desc_info & 0x3fffff; + + return hd; +} + +void ksnav_release_rxhd(struct pktdma_cfg *pktdma, void *hd) +{ + struct qm_host_desc *_hd = (struct qm_host_desc *)hd; + + _hd->buff_len = _hd->orig_buff_len; + _hd->buff_ptr = _hd->orig_buff_ptr; + + qm_push(_hd, pktdma->rx_free_q); +} diff --git a/drivers/dma/keystone_nav_cfg.c b/drivers/dma/keystone_nav_cfg.c new file mode 100644 index 0000000..bdd30a0 --- /dev/null +++ b/drivers/dma/keystone_nav_cfg.c @@ -0,0 +1,27 @@ +/* + * Multicore Navigator driver for TI Keystone 2 devices. + * + * (C) Copyright 2012-2014 + * Texas Instruments Incorporated, <www.ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/ti-common/keystone_nav.h> + +#ifdef CONFIG_KSNAV_PKTDMA_NETCP +/* NETCP Pktdma */ +struct pktdma_cfg netcp_pktdma = { + .global = (void *)CONFIG_KSNAV_NETCP_PDMA_CTRL_BASE, + .tx_ch = (void *)CONFIG_KSNAV_NETCP_PDMA_TX_BASE, + .tx_ch_num = CONFIG_KSNAV_NETCP_PDMA_TX_CH_NUM, + .rx_ch = (void *)CONFIG_KSNAV_NETCP_PDMA_RX_BASE, + .rx_ch_num = CONFIG_KSNAV_NETCP_PDMA_RX_CH_NUM, + .tx_sched = (u32 *)CONFIG_KSNAV_NETCP_PDMA_SCHED_BASE, + .rx_flows = (void *)CONFIG_KSNAV_NETCP_PDMA_RX_FLOW_BASE, + .rx_flow_num = CONFIG_KSNAV_NETCP_PDMA_RX_FLOW_NUM, + .rx_free_q = CONFIG_KSNAV_NETCP_PDMA_RX_FREE_QUEUE, + .rx_rcv_q = CONFIG_KSNAV_NETCP_PDMA_RX_RCV_QUEUE, + .tx_snd_q = CONFIG_KSNAV_NETCP_PDMA_TX_SND_QUEUE, +}; +#endif diff --git a/drivers/dma/ti-edma3.c b/drivers/dma/ti-edma3.c new file mode 100644 index 0000000..8184ded --- /dev/null +++ b/drivers/dma/ti-edma3.c @@ -0,0 +1,384 @@ +/* + * Enhanced Direct Memory Access (EDMA3) Controller + * + * (C) Copyright 2014 + * Texas Instruments Incorporated, <www.ti.com> + * + * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/io.h> +#include <common.h> +#include <asm/ti-common/ti-edma3.h> + +#define EDMA3_SL_BASE(slot) (0x4000 + ((slot) << 5)) +#define EDMA3_SL_MAX_NUM 512 +#define EDMA3_SLOPT_FIFO_WIDTH_MASK (0x7 << 8) + +#define EDMA3_QCHMAP(ch) 0x0200 + ((ch) << 2) +#define EDMA3_CHMAP_PARSET_MASK 0x1ff +#define EDMA3_CHMAP_PARSET_SHIFT 0x5 +#define EDMA3_CHMAP_TRIGWORD_SHIFT 0x2 + +#define EDMA3_QEMCR 0x314 +#define EDMA3_IPR 0x1068 +#define EDMA3_IPRH 0x106c +#define EDMA3_ICR 0x1070 +#define EDMA3_ICRH 0x1074 +#define EDMA3_QEECR 0x1088 +#define EDMA3_QEESR 0x108c +#define EDMA3_QSECR 0x1094 + +/** + * qedma3_start - start qdma on a channel + * @base: base address of edma + * @cfg: pinter to struct edma3_channel_config where you can set + * the slot number to associate with, the chnum, which corresponds + * your quick channel number 0-7, complete code - transfer complete code + * and trigger slot word - which has to correspond to the word number in + * edma3_slot_layout struct for generating event. + * + */ +void qedma3_start(u32 base, struct edma3_channel_config *cfg) +{ + u32 qchmap; + + /* Clear the pending int bit */ + if (cfg->complete_code < 32) + __raw_writel(1 << cfg->complete_code, base + EDMA3_ICR); + else + __raw_writel(1 << cfg->complete_code, base + EDMA3_ICRH); + + /* Map parameter set and trigger word 7 to quick channel */ + qchmap = ((EDMA3_CHMAP_PARSET_MASK & cfg->slot) + << EDMA3_CHMAP_PARSET_SHIFT) | + (cfg->trigger_slot_word << EDMA3_CHMAP_TRIGWORD_SHIFT); + + __raw_writel(qchmap, base + EDMA3_QCHMAP(cfg->chnum)); + + /* Clear missed event if set*/ + __raw_writel(1 << cfg->chnum, base + EDMA3_QSECR); + __raw_writel(1 << cfg->chnum, base + EDMA3_QEMCR); + + /* Enable qdma channel event */ + __raw_writel(1 << cfg->chnum, base + EDMA3_QEESR); +} + +/** + * edma3_set_dest - set initial DMA destination address in parameter RAM slot + * @base: base address of edma + * @slot: parameter RAM slot being configured + * @dst: physical address of destination (memory, controller FIFO, etc) + * @addressMode: INCR, except in very rare cases + * @width: ignored unless @addressMode is FIFO, else specifies the + * width to use when addressing the fifo (e.g. W8BIT, W32BIT) + * + * Note that the destination address is modified during the DMA transfer + * according to edma3_set_dest_index(). + */ +void edma3_set_dest(u32 base, int slot, u32 dst, enum edma3_address_mode mode, + enum edma3_fifo_width width) +{ + u32 opt; + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + + opt = __raw_readl(&rg->opt); + if (mode == FIFO) + opt = (opt & EDMA3_SLOPT_FIFO_WIDTH_MASK) | + (EDMA3_SLOPT_DST_ADDR_CONST_MODE | + EDMA3_SLOPT_FIFO_WIDTH_SET(width)); + else + opt &= ~EDMA3_SLOPT_DST_ADDR_CONST_MODE; + + __raw_writel(opt, &rg->opt); + __raw_writel(dst, &rg->dst); +} + +/** + * edma3_set_dest_index - configure DMA destination address indexing + * @base: base address of edma + * @slot: parameter RAM slot being configured + * @bidx: byte offset between destination arrays in a frame + * @cidx: byte offset between destination frames in a block + * + * Offsets are specified to support either contiguous or discontiguous + * memory transfers, or repeated access to a hardware register, as needed. + * When accessing hardware registers, both offsets are normally zero. + */ +void edma3_set_dest_index(u32 base, unsigned slot, int bidx, int cidx) +{ + u32 src_dst_bidx; + u32 src_dst_cidx; + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + + src_dst_bidx = __raw_readl(&rg->src_dst_bidx); + src_dst_cidx = __raw_readl(&rg->src_dst_cidx); + + __raw_writel((src_dst_bidx & 0x0000ffff) | (bidx << 16), + &rg->src_dst_bidx); + __raw_writel((src_dst_cidx & 0x0000ffff) | (cidx << 16), + &rg->src_dst_cidx); +} + +/** + * edma3_set_dest_addr - set destination address for slot only + */ +void edma3_set_dest_addr(u32 base, int slot, u32 dst) +{ + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + __raw_writel(dst, &rg->dst); +} + +/** + * edma3_set_src - set initial DMA source address in parameter RAM slot + * @base: base address of edma + * @slot: parameter RAM slot being configured + * @src_port: physical address of source (memory, controller FIFO, etc) + * @mode: INCR, except in very rare cases + * @width: ignored unless @addressMode is FIFO, else specifies the + * width to use when addressing the fifo (e.g. W8BIT, W32BIT) + * + * Note that the source address is modified during the DMA transfer + * according to edma3_set_src_index(). + */ +void edma3_set_src(u32 base, int slot, u32 src, enum edma3_address_mode mode, + enum edma3_fifo_width width) +{ + u32 opt; + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + + opt = __raw_readl(&rg->opt); + if (mode == FIFO) + opt = (opt & EDMA3_SLOPT_FIFO_WIDTH_MASK) | + (EDMA3_SLOPT_DST_ADDR_CONST_MODE | + EDMA3_SLOPT_FIFO_WIDTH_SET(width)); + else + opt &= ~EDMA3_SLOPT_DST_ADDR_CONST_MODE; + + __raw_writel(opt, &rg->opt); + __raw_writel(src, &rg->src); +} + +/** + * edma3_set_src_index - configure DMA source address indexing + * @base: base address of edma + * @slot: parameter RAM slot being configured + * @bidx: byte offset between source arrays in a frame + * @cidx: byte offset between source frames in a block + * + * Offsets are specified to support either contiguous or discontiguous + * memory transfers, or repeated access to a hardware register, as needed. + * When accessing hardware registers, both offsets are normally zero. + */ +void edma3_set_src_index(u32 base, unsigned slot, int bidx, int cidx) +{ + u32 src_dst_bidx; + u32 src_dst_cidx; + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + + src_dst_bidx = __raw_readl(&rg->src_dst_bidx); + src_dst_cidx = __raw_readl(&rg->src_dst_cidx); + + __raw_writel((src_dst_bidx & 0xffff0000) | bidx, + &rg->src_dst_bidx); + __raw_writel((src_dst_cidx & 0xffff0000) | cidx, + &rg->src_dst_cidx); +} + +/** + * edma3_set_src_addr - set source address for slot only + */ +void edma3_set_src_addr(u32 base, int slot, u32 src) +{ + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + __raw_writel(src, &rg->src); +} + +/** + * edma3_set_transfer_params - configure DMA transfer parameters + * @base: base address of edma + * @slot: parameter RAM slot being configured + * @acnt: how many bytes per array (at least one) + * @bcnt: how many arrays per frame (at least one) + * @ccnt: how many frames per block (at least one) + * @bcnt_rld: used only for A-Synchronized transfers; this specifies + * the value to reload into bcnt when it decrements to zero + * @sync_mode: ASYNC or ABSYNC + * + * See the EDMA3 documentation to understand how to configure and link + * transfers using the fields in PaRAM slots. If you are not doing it + * all at once with edma3_write_slot(), you will use this routine + * plus two calls each for source and destination, setting the initial + * address and saying how to index that address. + * + * An example of an A-Synchronized transfer is a serial link using a + * single word shift register. In that case, @acnt would be equal to + * that word size; the serial controller issues a DMA synchronization + * event to transfer each word, and memory access by the DMA transfer + * controller will be word-at-a-time. + * + * An example of an AB-Synchronized transfer is a device using a FIFO. + * In that case, @acnt equals the FIFO width and @bcnt equals its depth. + * The controller with the FIFO issues DMA synchronization events when + * the FIFO threshold is reached, and the DMA transfer controller will + * transfer one frame to (or from) the FIFO. It will probably use + * efficient burst modes to access memory. + */ +void edma3_set_transfer_params(u32 base, int slot, int acnt, + int bcnt, int ccnt, u16 bcnt_rld, + enum edma3_sync_dimension sync_mode) +{ + u32 opt; + u32 link_bcntrld; + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + + link_bcntrld = __raw_readl(&rg->link_bcntrld); + + __raw_writel((bcnt_rld << 16) | (0x0000ffff & link_bcntrld), + &rg->link_bcntrld); + + opt = __raw_readl(&rg->opt); + if (sync_mode == ASYNC) + __raw_writel(opt & ~EDMA3_SLOPT_AB_SYNC, &rg->opt); + else + __raw_writel(opt | EDMA3_SLOPT_AB_SYNC, &rg->opt); + + /* Set the acount, bcount, ccount registers */ + __raw_writel((bcnt << 16) | (acnt & 0xffff), &rg->a_b_cnt); + __raw_writel(0xffff & ccnt, &rg->ccnt); +} + +/** + * edma3_write_slot - write parameter RAM data for slot + * @base: base address of edma + * @slot: number of parameter RAM slot being modified + * @param: data to be written into parameter RAM slot + * + * Use this to assign all parameters of a transfer at once. This + * allows more efficient setup of transfers than issuing multiple + * calls to set up those parameters in small pieces, and provides + * complete control over all transfer options. + */ +void edma3_write_slot(u32 base, int slot, struct edma3_slot_layout *param) +{ + int i; + u32 *p = (u32 *)param; + u32 *addr = (u32 *)(base + EDMA3_SL_BASE(slot)); + + for (i = 0; i < sizeof(struct edma3_slot_layout)/4; i += 4) + __raw_writel(*p++, addr++); +} + +/** + * edma3_read_slot - read parameter RAM data from slot + * @base: base address of edma + * @slot: number of parameter RAM slot being copied + * @param: where to store copy of parameter RAM data + * + * Use this to read data from a parameter RAM slot, perhaps to + * save them as a template for later reuse. + */ +void edma3_read_slot(u32 base, int slot, struct edma3_slot_layout *param) +{ + int i; + u32 *p = (u32 *)param; + u32 *addr = (u32 *)(base + EDMA3_SL_BASE(slot)); + + for (i = 0; i < sizeof(struct edma3_slot_layout)/4; i += 4) + *p++ = __raw_readl(addr++); +} + +void edma3_slot_configure(u32 base, int slot, struct edma3_slot_config *cfg) +{ + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + + __raw_writel(cfg->opt, &rg->opt); + __raw_writel(cfg->src, &rg->src); + __raw_writel((cfg->bcnt << 16) | (cfg->acnt & 0xffff), &rg->a_b_cnt); + __raw_writel(cfg->dst, &rg->dst); + __raw_writel((cfg->dst_bidx << 16) | + (cfg->src_bidx & 0xffff), &rg->src_dst_bidx); + __raw_writel((cfg->bcntrld << 16) | + (cfg->link & 0xffff), &rg->link_bcntrld); + __raw_writel((cfg->dst_cidx << 16) | + (cfg->src_cidx & 0xffff), &rg->src_dst_cidx); + __raw_writel(0xffff & cfg->ccnt, &rg->ccnt); +} + +/** + * edma3_check_for_transfer - check if transfer coplete by checking + * interrupt pending bit. Clear interrupt pending bit if complete. + * @base: base address of edma + * @cfg: pinter to struct edma3_channel_config which was passed + * to qedma3_start when you started qdma channel + * + * Return 0 if complete, 1 if not. + */ +int edma3_check_for_transfer(u32 base, struct edma3_channel_config *cfg) +{ + u32 inum; + u32 ipr_base; + u32 icr_base; + + if (cfg->complete_code < 32) { + ipr_base = base + EDMA3_IPR; + icr_base = base + EDMA3_ICR; + inum = 1 << cfg->complete_code; + } else { + ipr_base = base + EDMA3_IPRH; + icr_base = base + EDMA3_ICRH; + inum = 1 << (cfg->complete_code - 32); + } + + /* check complete interrupt */ + if (!(__raw_readl(ipr_base) & inum)) + return 1; + + /* clean up the pending int bit */ + __raw_writel(inum, icr_base); + + return 0; +} + +/** + * qedma3_stop - stops dma on the channel passed + * @base: base address of edma + * @cfg: pinter to struct edma3_channel_config which was passed + * to qedma3_start when you started qdma channel + */ +void qedma3_stop(u32 base, struct edma3_channel_config *cfg) +{ + /* Disable qdma channel event */ + __raw_writel(1 << cfg->chnum, base + EDMA3_QEECR); + + /* clean up the interrupt indication */ + if (cfg->complete_code < 32) + __raw_writel(1 << cfg->complete_code, base + EDMA3_ICR); + else + __raw_writel(1 << cfg->complete_code, base + EDMA3_ICRH); + + /* Clear missed event if set*/ + __raw_writel(1 << cfg->chnum, base + EDMA3_QSECR); + __raw_writel(1 << cfg->chnum, base + EDMA3_QEMCR); + + /* Clear the channel map */ + __raw_writel(0, base + EDMA3_QCHMAP(cfg->chnum)); +} |