| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 | // SPDX-License-Identifier: GPL-2.0+/* * Enhanced Direct Memory Access (EDMA3) Controller * * (C) Copyright 2014 *     Texas Instruments Incorporated, <www.ti.com> * * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com> */#include <asm/io.h>#include <common.h>#include <dm.h>#include <dma.h>#include <asm/omap_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#define EDMA_FILL_BUFFER_SIZE			512struct ti_edma3_priv {	u32 base;};static u8 edma_fill_buffer[EDMA_FILL_BUFFER_SIZE] __aligned(ARCH_DMA_MINALIGN);/** * 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));}void __edma3_transfer(unsigned long edma3_base_addr, unsigned int edma_slot_num,		      void *dst, void *src, size_t len, size_t s_len){	struct edma3_slot_config        slot;	struct edma3_channel_config     edma_channel;	int                             b_cnt_value = 1;	int                             rem_bytes  = 0;	int                             a_cnt_value = len;	unsigned int                    addr = (unsigned int) (dst);	unsigned int                    max_acnt  = 0x7FFFU;	if (len > s_len) {		b_cnt_value = (len / s_len);		rem_bytes = (len % s_len);		a_cnt_value = s_len;	} else if (len > max_acnt) {		b_cnt_value = (len / max_acnt);		rem_bytes  = (len % max_acnt);		a_cnt_value = max_acnt;	}	slot.opt        = 0;	slot.src        = ((unsigned int) src);	slot.acnt       = a_cnt_value;	slot.bcnt       = b_cnt_value;	slot.ccnt       = 1;	if (len == s_len)		slot.src_bidx = a_cnt_value;	else		slot.src_bidx = 0;	slot.dst_bidx   = a_cnt_value;	slot.src_cidx   = 0;	slot.dst_cidx   = 0;	slot.link       = EDMA3_PARSET_NULL_LINK;	slot.bcntrld    = 0;	slot.opt        = EDMA3_SLOPT_TRANS_COMP_INT_ENB |			  EDMA3_SLOPT_COMP_CODE(0) |			  EDMA3_SLOPT_STATIC | EDMA3_SLOPT_AB_SYNC;	edma3_slot_configure(edma3_base_addr, edma_slot_num, &slot);	edma_channel.slot = edma_slot_num;	edma_channel.chnum = 0;	edma_channel.complete_code = 0;	 /* set event trigger to dst update */	edma_channel.trigger_slot_word = EDMA3_TWORD(dst);	qedma3_start(edma3_base_addr, &edma_channel);	edma3_set_dest_addr(edma3_base_addr, edma_channel.slot, addr);	while (edma3_check_for_transfer(edma3_base_addr, &edma_channel))		;	qedma3_stop(edma3_base_addr, &edma_channel);	if (rem_bytes != 0) {		slot.opt        = 0;		if (len == s_len)			slot.src =				(b_cnt_value * max_acnt) + ((unsigned int) src);		else			slot.src = (unsigned int) src;		slot.acnt       = rem_bytes;		slot.bcnt       = 1;		slot.ccnt       = 1;		slot.src_bidx   = rem_bytes;		slot.dst_bidx   = rem_bytes;		slot.src_cidx   = 0;		slot.dst_cidx   = 0;		slot.link       = EDMA3_PARSET_NULL_LINK;		slot.bcntrld    = 0;		slot.opt        = EDMA3_SLOPT_TRANS_COMP_INT_ENB |				  EDMA3_SLOPT_COMP_CODE(0) |				  EDMA3_SLOPT_STATIC | EDMA3_SLOPT_AB_SYNC;		edma3_slot_configure(edma3_base_addr, edma_slot_num, &slot);		edma_channel.slot = edma_slot_num;		edma_channel.chnum = 0;		edma_channel.complete_code = 0;		/* set event trigger to dst update */		edma_channel.trigger_slot_word = EDMA3_TWORD(dst);		qedma3_start(edma3_base_addr, &edma_channel);		edma3_set_dest_addr(edma3_base_addr, edma_channel.slot, addr +				    (max_acnt * b_cnt_value));		while (edma3_check_for_transfer(edma3_base_addr, &edma_channel))			;		qedma3_stop(edma3_base_addr, &edma_channel);	}}void __edma3_fill(unsigned long edma3_base_addr, unsigned int edma_slot_num,		  void *dst, u8 val, size_t len){	int xfer_len;	int max_xfer = EDMA_FILL_BUFFER_SIZE * 65535;	memset((void *)edma_fill_buffer, val, sizeof(edma_fill_buffer));	while (len) {		xfer_len = len;		if (xfer_len > max_xfer)			xfer_len = max_xfer;		__edma3_transfer(edma3_base_addr, edma_slot_num, dst,				 edma_fill_buffer, xfer_len,				 EDMA_FILL_BUFFER_SIZE);		len -= xfer_len;		dst += xfer_len;	}}#ifndef CONFIG_DMAvoid edma3_transfer(unsigned long edma3_base_addr, unsigned int edma_slot_num,		    void *dst, void *src, size_t len){	__edma3_transfer(edma3_base_addr, edma_slot_num, dst, src, len, len);}void edma3_fill(unsigned long edma3_base_addr, unsigned int edma_slot_num,		void *dst, u8 val, size_t len){	__edma3_fill(edma3_base_addr, edma_slot_num, dst, val, len);}#elsestatic int ti_edma3_transfer(struct udevice *dev, int direction, void *dst,			     void *src, size_t len){	struct ti_edma3_priv *priv = dev_get_priv(dev);	/* enable edma3 clocks */	enable_edma3_clocks();	switch (direction) {	case DMA_MEM_TO_MEM:		__edma3_transfer(priv->base, 1, dst, src, len, len);		break;	default:		pr_err("Transfer type not implemented in DMA driver\n");		break;	}	/* disable edma3 clocks */	disable_edma3_clocks();	return 0;}static int ti_edma3_ofdata_to_platdata(struct udevice *dev){	struct ti_edma3_priv *priv = dev_get_priv(dev);	priv->base = devfdt_get_addr(dev);	return 0;}static int ti_edma3_probe(struct udevice *dev){	struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev);	uc_priv->supported = DMA_SUPPORTS_MEM_TO_MEM;	return 0;}static const struct dma_ops ti_edma3_ops = {	.transfer	= ti_edma3_transfer,};static const struct udevice_id ti_edma3_ids[] = {	{ .compatible = "ti,edma3" },	{ }};U_BOOT_DRIVER(ti_edma3) = {	.name	= "ti_edma3",	.id	= UCLASS_DMA,	.of_match = ti_edma3_ids,	.ops	= &ti_edma3_ops,	.ofdata_to_platdata = ti_edma3_ofdata_to_platdata,	.probe	= ti_edma3_probe,	.priv_auto_alloc_size = sizeof(struct ti_edma3_priv),};#endif /* CONFIG_DMA */
 |