xref: /openbmc/linux/drivers/dma/xilinx/xdma.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
117ce2522SLizhi Hou // SPDX-License-Identifier: GPL-2.0-or-later
217ce2522SLizhi Hou /*
317ce2522SLizhi Hou  * DMA driver for Xilinx DMA/Bridge Subsystem
417ce2522SLizhi Hou  *
517ce2522SLizhi Hou  * Copyright (C) 2017-2020 Xilinx, Inc. All rights reserved.
617ce2522SLizhi Hou  * Copyright (C) 2022, Advanced Micro Devices, Inc.
717ce2522SLizhi Hou  */
817ce2522SLizhi Hou 
917ce2522SLizhi Hou /*
1017ce2522SLizhi Hou  * The DMA/Bridge Subsystem for PCI Express allows for the movement of data
1117ce2522SLizhi Hou  * between Host memory and the DMA subsystem. It does this by operating on
1217ce2522SLizhi Hou  * 'descriptors' that contain information about the source, destination and
1317ce2522SLizhi Hou  * amount of data to transfer. These direct memory transfers can be both in
1417ce2522SLizhi Hou  * the Host to Card (H2C) and Card to Host (C2H) transfers. The DMA can be
1517ce2522SLizhi Hou  * configured to have a single AXI4 Master interface shared by all channels
1617ce2522SLizhi Hou  * or one AXI4-Stream interface for each channel enabled. Memory transfers are
1717ce2522SLizhi Hou  * specified on a per-channel basis in descriptor linked lists, which the DMA
1817ce2522SLizhi Hou  * fetches from host memory and processes. Events such as descriptor completion
1917ce2522SLizhi Hou  * and errors are signaled using interrupts. The core also provides up to 16
2017ce2522SLizhi Hou  * user interrupt wires that generate interrupts to the host.
2117ce2522SLizhi Hou  */
2217ce2522SLizhi Hou 
2317ce2522SLizhi Hou #include <linux/mod_devicetable.h>
2417ce2522SLizhi Hou #include <linux/bitfield.h>
2517ce2522SLizhi Hou #include <linux/dmapool.h>
2617ce2522SLizhi Hou #include <linux/regmap.h>
2717ce2522SLizhi Hou #include <linux/dmaengine.h>
28ecf294a6SLizhi Hou #include <linux/dma/amd_xdma.h>
2917ce2522SLizhi Hou #include <linux/platform_device.h>
3017ce2522SLizhi Hou #include <linux/platform_data/amd_xdma.h>
3117ce2522SLizhi Hou #include <linux/dma-mapping.h>
3217ce2522SLizhi Hou #include <linux/pci.h>
3317ce2522SLizhi Hou #include "../virt-dma.h"
3417ce2522SLizhi Hou #include "xdma-regs.h"
3517ce2522SLizhi Hou 
3617ce2522SLizhi Hou /* mmio regmap config for all XDMA registers */
3717ce2522SLizhi Hou static const struct regmap_config xdma_regmap_config = {
3817ce2522SLizhi Hou 	.reg_bits = 32,
3917ce2522SLizhi Hou 	.val_bits = 32,
4017ce2522SLizhi Hou 	.reg_stride = 4,
4117ce2522SLizhi Hou 	.max_register = XDMA_REG_SPACE_LEN,
4217ce2522SLizhi Hou };
4317ce2522SLizhi Hou 
4417ce2522SLizhi Hou /**
4517ce2522SLizhi Hou  * struct xdma_desc_block - Descriptor block
4617ce2522SLizhi Hou  * @virt_addr: Virtual address of block start
4717ce2522SLizhi Hou  * @dma_addr: DMA address of block start
4817ce2522SLizhi Hou  */
4917ce2522SLizhi Hou struct xdma_desc_block {
5017ce2522SLizhi Hou 	void		*virt_addr;
5117ce2522SLizhi Hou 	dma_addr_t	dma_addr;
5217ce2522SLizhi Hou };
5317ce2522SLizhi Hou 
5417ce2522SLizhi Hou /**
5517ce2522SLizhi Hou  * struct xdma_chan - Driver specific DMA channel structure
5617ce2522SLizhi Hou  * @vchan: Virtual channel
5717ce2522SLizhi Hou  * @xdev_hdl: Pointer to DMA device structure
5817ce2522SLizhi Hou  * @base: Offset of channel registers
5917ce2522SLizhi Hou  * @desc_pool: Descriptor pool
6017ce2522SLizhi Hou  * @busy: Busy flag of the channel
6117ce2522SLizhi Hou  * @dir: Transferring direction of the channel
6217ce2522SLizhi Hou  * @cfg: Transferring config of the channel
6317ce2522SLizhi Hou  * @irq: IRQ assigned to the channel
6417ce2522SLizhi Hou  */
6517ce2522SLizhi Hou struct xdma_chan {
6617ce2522SLizhi Hou 	struct virt_dma_chan		vchan;
6717ce2522SLizhi Hou 	void				*xdev_hdl;
6817ce2522SLizhi Hou 	u32				base;
6917ce2522SLizhi Hou 	struct dma_pool			*desc_pool;
7017ce2522SLizhi Hou 	bool				busy;
7117ce2522SLizhi Hou 	enum dma_transfer_direction	dir;
7217ce2522SLizhi Hou 	struct dma_slave_config		cfg;
7317ce2522SLizhi Hou 	u32				irq;
7417ce2522SLizhi Hou };
7517ce2522SLizhi Hou 
7617ce2522SLizhi Hou /**
7717ce2522SLizhi Hou  * struct xdma_desc - DMA desc structure
7817ce2522SLizhi Hou  * @vdesc: Virtual DMA descriptor
7917ce2522SLizhi Hou  * @chan: DMA channel pointer
8017ce2522SLizhi Hou  * @dir: Transferring direction of the request
8117ce2522SLizhi Hou  * @dev_addr: Physical address on DMA device side
8217ce2522SLizhi Hou  * @desc_blocks: Hardware descriptor blocks
8317ce2522SLizhi Hou  * @dblk_num: Number of hardware descriptor blocks
8417ce2522SLizhi Hou  * @desc_num: Number of hardware descriptors
8517ce2522SLizhi Hou  * @completed_desc_num: Completed hardware descriptors
8617ce2522SLizhi Hou  */
8717ce2522SLizhi Hou struct xdma_desc {
8817ce2522SLizhi Hou 	struct virt_dma_desc		vdesc;
8917ce2522SLizhi Hou 	struct xdma_chan		*chan;
9017ce2522SLizhi Hou 	enum dma_transfer_direction	dir;
9117ce2522SLizhi Hou 	u64				dev_addr;
9217ce2522SLizhi Hou 	struct xdma_desc_block		*desc_blocks;
9317ce2522SLizhi Hou 	u32				dblk_num;
9417ce2522SLizhi Hou 	u32				desc_num;
9517ce2522SLizhi Hou 	u32				completed_desc_num;
9617ce2522SLizhi Hou };
9717ce2522SLizhi Hou 
9817ce2522SLizhi Hou #define XDMA_DEV_STATUS_REG_DMA		BIT(0)
9917ce2522SLizhi Hou #define XDMA_DEV_STATUS_INIT_MSIX	BIT(1)
10017ce2522SLizhi Hou 
10117ce2522SLizhi Hou /**
10217ce2522SLizhi Hou  * struct xdma_device - DMA device structure
10317ce2522SLizhi Hou  * @pdev: Platform device pointer
10417ce2522SLizhi Hou  * @dma_dev: DMA device structure
10517ce2522SLizhi Hou  * @rmap: MMIO regmap for DMA registers
10617ce2522SLizhi Hou  * @h2c_chans: Host to Card channels
10717ce2522SLizhi Hou  * @c2h_chans: Card to Host channels
10817ce2522SLizhi Hou  * @h2c_chan_num: Number of H2C channels
10917ce2522SLizhi Hou  * @c2h_chan_num: Number of C2H channels
11017ce2522SLizhi Hou  * @irq_start: Start IRQ assigned to device
11117ce2522SLizhi Hou  * @irq_num: Number of IRQ assigned to device
11217ce2522SLizhi Hou  * @status: Initialization status
11317ce2522SLizhi Hou  */
11417ce2522SLizhi Hou struct xdma_device {
11517ce2522SLizhi Hou 	struct platform_device	*pdev;
11617ce2522SLizhi Hou 	struct dma_device	dma_dev;
11717ce2522SLizhi Hou 	struct regmap		*rmap;
11817ce2522SLizhi Hou 	struct xdma_chan	*h2c_chans;
11917ce2522SLizhi Hou 	struct xdma_chan	*c2h_chans;
12017ce2522SLizhi Hou 	u32			h2c_chan_num;
12117ce2522SLizhi Hou 	u32			c2h_chan_num;
12217ce2522SLizhi Hou 	u32			irq_start;
12317ce2522SLizhi Hou 	u32			irq_num;
12417ce2522SLizhi Hou 	u32			status;
12517ce2522SLizhi Hou };
12617ce2522SLizhi Hou 
12717ce2522SLizhi Hou #define xdma_err(xdev, fmt, args...)					\
12817ce2522SLizhi Hou 	dev_err(&(xdev)->pdev->dev, fmt, ##args)
12917ce2522SLizhi Hou #define XDMA_CHAN_NUM(_xd) ({						\
13017ce2522SLizhi Hou 	typeof(_xd) (xd) = (_xd);					\
13117ce2522SLizhi Hou 	((xd)->h2c_chan_num + (xd)->c2h_chan_num); })
13217ce2522SLizhi Hou 
13317ce2522SLizhi Hou /* Get the last desc in a desc block */
xdma_blk_last_desc(struct xdma_desc_block * block)13417ce2522SLizhi Hou static inline void *xdma_blk_last_desc(struct xdma_desc_block *block)
13517ce2522SLizhi Hou {
13617ce2522SLizhi Hou 	return block->virt_addr + (XDMA_DESC_ADJACENT - 1) * XDMA_DESC_SIZE;
13717ce2522SLizhi Hou }
13817ce2522SLizhi Hou 
13917ce2522SLizhi Hou /**
14017ce2522SLizhi Hou  * xdma_link_desc_blocks - Link descriptor blocks for DMA transfer
14117ce2522SLizhi Hou  * @sw_desc: Tx descriptor pointer
14217ce2522SLizhi Hou  */
xdma_link_desc_blocks(struct xdma_desc * sw_desc)14317ce2522SLizhi Hou static void xdma_link_desc_blocks(struct xdma_desc *sw_desc)
14417ce2522SLizhi Hou {
14517ce2522SLizhi Hou 	struct xdma_desc_block *block;
14617ce2522SLizhi Hou 	u32 last_blk_desc, desc_control;
14717ce2522SLizhi Hou 	struct xdma_hw_desc *desc;
14817ce2522SLizhi Hou 	int i;
14917ce2522SLizhi Hou 
15017ce2522SLizhi Hou 	desc_control = XDMA_DESC_CONTROL(XDMA_DESC_ADJACENT, 0);
15117ce2522SLizhi Hou 	for (i = 1; i < sw_desc->dblk_num; i++) {
15217ce2522SLizhi Hou 		block = &sw_desc->desc_blocks[i - 1];
15317ce2522SLizhi Hou 		desc = xdma_blk_last_desc(block);
15417ce2522SLizhi Hou 
15517ce2522SLizhi Hou 		if (!(i & XDMA_DESC_BLOCK_MASK)) {
15617ce2522SLizhi Hou 			desc->control = cpu_to_le32(XDMA_DESC_CONTROL_LAST);
15717ce2522SLizhi Hou 			continue;
15817ce2522SLizhi Hou 		}
15917ce2522SLizhi Hou 		desc->control = cpu_to_le32(desc_control);
16017ce2522SLizhi Hou 		desc->next_desc = cpu_to_le64(block[1].dma_addr);
16117ce2522SLizhi Hou 	}
16217ce2522SLizhi Hou 
16317ce2522SLizhi Hou 	/* update the last block */
16417ce2522SLizhi Hou 	last_blk_desc = (sw_desc->desc_num - 1) & XDMA_DESC_ADJACENT_MASK;
16517ce2522SLizhi Hou 	if (((sw_desc->dblk_num - 1) & XDMA_DESC_BLOCK_MASK) > 0) {
16617ce2522SLizhi Hou 		block = &sw_desc->desc_blocks[sw_desc->dblk_num - 2];
16717ce2522SLizhi Hou 		desc = xdma_blk_last_desc(block);
16817ce2522SLizhi Hou 		desc_control = XDMA_DESC_CONTROL(last_blk_desc + 1, 0);
16917ce2522SLizhi Hou 		desc->control = cpu_to_le32(desc_control);
17017ce2522SLizhi Hou 	}
17117ce2522SLizhi Hou 
17217ce2522SLizhi Hou 	block = &sw_desc->desc_blocks[sw_desc->dblk_num - 1];
17317ce2522SLizhi Hou 	desc = block->virt_addr + last_blk_desc * XDMA_DESC_SIZE;
17417ce2522SLizhi Hou 	desc->control = cpu_to_le32(XDMA_DESC_CONTROL_LAST);
17517ce2522SLizhi Hou }
17617ce2522SLizhi Hou 
to_xdma_chan(struct dma_chan * chan)17717ce2522SLizhi Hou static inline struct xdma_chan *to_xdma_chan(struct dma_chan *chan)
17817ce2522SLizhi Hou {
17917ce2522SLizhi Hou 	return container_of(chan, struct xdma_chan, vchan.chan);
18017ce2522SLizhi Hou }
18117ce2522SLizhi Hou 
to_xdma_desc(struct virt_dma_desc * vdesc)18217ce2522SLizhi Hou static inline struct xdma_desc *to_xdma_desc(struct virt_dma_desc *vdesc)
18317ce2522SLizhi Hou {
18417ce2522SLizhi Hou 	return container_of(vdesc, struct xdma_desc, vdesc);
18517ce2522SLizhi Hou }
18617ce2522SLizhi Hou 
18717ce2522SLizhi Hou /**
18817ce2522SLizhi Hou  * xdma_channel_init - Initialize DMA channel registers
18917ce2522SLizhi Hou  * @chan: DMA channel pointer
19017ce2522SLizhi Hou  */
xdma_channel_init(struct xdma_chan * chan)19117ce2522SLizhi Hou static int xdma_channel_init(struct xdma_chan *chan)
19217ce2522SLizhi Hou {
19317ce2522SLizhi Hou 	struct xdma_device *xdev = chan->xdev_hdl;
19417ce2522SLizhi Hou 	int ret;
19517ce2522SLizhi Hou 
19617ce2522SLizhi Hou 	ret = regmap_write(xdev->rmap, chan->base + XDMA_CHAN_CONTROL_W1C,
19717ce2522SLizhi Hou 			   CHAN_CTRL_NON_INCR_ADDR);
19817ce2522SLizhi Hou 	if (ret)
19917ce2522SLizhi Hou 		return ret;
20017ce2522SLizhi Hou 
20117ce2522SLizhi Hou 	ret = regmap_write(xdev->rmap, chan->base + XDMA_CHAN_INTR_ENABLE,
20217ce2522SLizhi Hou 			   CHAN_IM_ALL);
20317ce2522SLizhi Hou 	if (ret)
20417ce2522SLizhi Hou 		return ret;
20517ce2522SLizhi Hou 
20617ce2522SLizhi Hou 	return 0;
20717ce2522SLizhi Hou }
20817ce2522SLizhi Hou 
20917ce2522SLizhi Hou /**
21017ce2522SLizhi Hou  * xdma_free_desc - Free descriptor
21117ce2522SLizhi Hou  * @vdesc: Virtual DMA descriptor
21217ce2522SLizhi Hou  */
xdma_free_desc(struct virt_dma_desc * vdesc)21317ce2522SLizhi Hou static void xdma_free_desc(struct virt_dma_desc *vdesc)
21417ce2522SLizhi Hou {
21517ce2522SLizhi Hou 	struct xdma_desc *sw_desc;
21617ce2522SLizhi Hou 	int i;
21717ce2522SLizhi Hou 
21817ce2522SLizhi Hou 	sw_desc = to_xdma_desc(vdesc);
21917ce2522SLizhi Hou 	for (i = 0; i < sw_desc->dblk_num; i++) {
22017ce2522SLizhi Hou 		if (!sw_desc->desc_blocks[i].virt_addr)
22117ce2522SLizhi Hou 			break;
22217ce2522SLizhi Hou 		dma_pool_free(sw_desc->chan->desc_pool,
22317ce2522SLizhi Hou 			      sw_desc->desc_blocks[i].virt_addr,
22417ce2522SLizhi Hou 			      sw_desc->desc_blocks[i].dma_addr);
22517ce2522SLizhi Hou 	}
22617ce2522SLizhi Hou 	kfree(sw_desc->desc_blocks);
22717ce2522SLizhi Hou 	kfree(sw_desc);
22817ce2522SLizhi Hou }
22917ce2522SLizhi Hou 
23017ce2522SLizhi Hou /**
23117ce2522SLizhi Hou  * xdma_alloc_desc - Allocate descriptor
23217ce2522SLizhi Hou  * @chan: DMA channel pointer
23317ce2522SLizhi Hou  * @desc_num: Number of hardware descriptors
23417ce2522SLizhi Hou  */
23517ce2522SLizhi Hou static struct xdma_desc *
xdma_alloc_desc(struct xdma_chan * chan,u32 desc_num)23617ce2522SLizhi Hou xdma_alloc_desc(struct xdma_chan *chan, u32 desc_num)
23717ce2522SLizhi Hou {
23817ce2522SLizhi Hou 	struct xdma_desc *sw_desc;
23917ce2522SLizhi Hou 	struct xdma_hw_desc *desc;
24017ce2522SLizhi Hou 	dma_addr_t dma_addr;
24117ce2522SLizhi Hou 	u32 dblk_num;
24217ce2522SLizhi Hou 	void *addr;
24317ce2522SLizhi Hou 	int i, j;
24417ce2522SLizhi Hou 
24517ce2522SLizhi Hou 	sw_desc = kzalloc(sizeof(*sw_desc), GFP_NOWAIT);
24617ce2522SLizhi Hou 	if (!sw_desc)
24717ce2522SLizhi Hou 		return NULL;
24817ce2522SLizhi Hou 
24917ce2522SLizhi Hou 	sw_desc->chan = chan;
25017ce2522SLizhi Hou 	sw_desc->desc_num = desc_num;
25117ce2522SLizhi Hou 	dblk_num = DIV_ROUND_UP(desc_num, XDMA_DESC_ADJACENT);
25217ce2522SLizhi Hou 	sw_desc->desc_blocks = kcalloc(dblk_num, sizeof(*sw_desc->desc_blocks),
25317ce2522SLizhi Hou 				       GFP_NOWAIT);
25417ce2522SLizhi Hou 	if (!sw_desc->desc_blocks)
25517ce2522SLizhi Hou 		goto failed;
25617ce2522SLizhi Hou 
25717ce2522SLizhi Hou 	sw_desc->dblk_num = dblk_num;
25817ce2522SLizhi Hou 	for (i = 0; i < sw_desc->dblk_num; i++) {
25917ce2522SLizhi Hou 		addr = dma_pool_alloc(chan->desc_pool, GFP_NOWAIT, &dma_addr);
26017ce2522SLizhi Hou 		if (!addr)
26117ce2522SLizhi Hou 			goto failed;
26217ce2522SLizhi Hou 
26317ce2522SLizhi Hou 		sw_desc->desc_blocks[i].virt_addr = addr;
26417ce2522SLizhi Hou 		sw_desc->desc_blocks[i].dma_addr = dma_addr;
26517ce2522SLizhi Hou 		for (j = 0, desc = addr; j < XDMA_DESC_ADJACENT; j++)
26617ce2522SLizhi Hou 			desc[j].control = cpu_to_le32(XDMA_DESC_CONTROL(1, 0));
26717ce2522SLizhi Hou 	}
26817ce2522SLizhi Hou 
26917ce2522SLizhi Hou 	xdma_link_desc_blocks(sw_desc);
27017ce2522SLizhi Hou 
27117ce2522SLizhi Hou 	return sw_desc;
27217ce2522SLizhi Hou 
27317ce2522SLizhi Hou failed:
27417ce2522SLizhi Hou 	xdma_free_desc(&sw_desc->vdesc);
27517ce2522SLizhi Hou 	return NULL;
27617ce2522SLizhi Hou }
27717ce2522SLizhi Hou 
27817ce2522SLizhi Hou /**
27917ce2522SLizhi Hou  * xdma_xfer_start - Start DMA transfer
280b771baf3SYang Li  * @xchan: DMA channel pointer
28117ce2522SLizhi Hou  */
xdma_xfer_start(struct xdma_chan * xchan)28217ce2522SLizhi Hou static int xdma_xfer_start(struct xdma_chan *xchan)
28317ce2522SLizhi Hou {
28417ce2522SLizhi Hou 	struct virt_dma_desc *vd = vchan_next_desc(&xchan->vchan);
28517ce2522SLizhi Hou 	struct xdma_device *xdev = xchan->xdev_hdl;
28617ce2522SLizhi Hou 	struct xdma_desc_block *block;
28717ce2522SLizhi Hou 	u32 val, completed_blocks;
28817ce2522SLizhi Hou 	struct xdma_desc *desc;
28917ce2522SLizhi Hou 	int ret;
29017ce2522SLizhi Hou 
29117ce2522SLizhi Hou 	/*
29217ce2522SLizhi Hou 	 * check if there is not any submitted descriptor or channel is busy.
29317ce2522SLizhi Hou 	 * vchan lock should be held where this function is called.
29417ce2522SLizhi Hou 	 */
29517ce2522SLizhi Hou 	if (!vd || xchan->busy)
29617ce2522SLizhi Hou 		return -EINVAL;
29717ce2522SLizhi Hou 
29817ce2522SLizhi Hou 	/* clear run stop bit to get ready for transfer */
29917ce2522SLizhi Hou 	ret = regmap_write(xdev->rmap, xchan->base + XDMA_CHAN_CONTROL_W1C,
30017ce2522SLizhi Hou 			   CHAN_CTRL_RUN_STOP);
30117ce2522SLizhi Hou 	if (ret)
30217ce2522SLizhi Hou 		return ret;
30317ce2522SLizhi Hou 
30417ce2522SLizhi Hou 	desc = to_xdma_desc(vd);
30517ce2522SLizhi Hou 	if (desc->dir != xchan->dir) {
30617ce2522SLizhi Hou 		xdma_err(xdev, "incorrect request direction");
30717ce2522SLizhi Hou 		return -EINVAL;
30817ce2522SLizhi Hou 	}
30917ce2522SLizhi Hou 
31017ce2522SLizhi Hou 	/* set DMA engine to the first descriptor block */
31117ce2522SLizhi Hou 	completed_blocks = desc->completed_desc_num / XDMA_DESC_ADJACENT;
31217ce2522SLizhi Hou 	block = &desc->desc_blocks[completed_blocks];
31317ce2522SLizhi Hou 	val = lower_32_bits(block->dma_addr);
31417ce2522SLizhi Hou 	ret = regmap_write(xdev->rmap, xchan->base + XDMA_SGDMA_DESC_LO, val);
31517ce2522SLizhi Hou 	if (ret)
31617ce2522SLizhi Hou 		return ret;
31717ce2522SLizhi Hou 
31817ce2522SLizhi Hou 	val = upper_32_bits(block->dma_addr);
31917ce2522SLizhi Hou 	ret = regmap_write(xdev->rmap, xchan->base + XDMA_SGDMA_DESC_HI, val);
32017ce2522SLizhi Hou 	if (ret)
32117ce2522SLizhi Hou 		return ret;
32217ce2522SLizhi Hou 
32317ce2522SLizhi Hou 	if (completed_blocks + 1 == desc->dblk_num)
32417ce2522SLizhi Hou 		val = (desc->desc_num - 1) & XDMA_DESC_ADJACENT_MASK;
32517ce2522SLizhi Hou 	else
32617ce2522SLizhi Hou 		val = XDMA_DESC_ADJACENT - 1;
32717ce2522SLizhi Hou 	ret = regmap_write(xdev->rmap, xchan->base + XDMA_SGDMA_DESC_ADJ, val);
32817ce2522SLizhi Hou 	if (ret)
32917ce2522SLizhi Hou 		return ret;
33017ce2522SLizhi Hou 
33117ce2522SLizhi Hou 	/* kick off DMA transfer */
33217ce2522SLizhi Hou 	ret = regmap_write(xdev->rmap, xchan->base + XDMA_CHAN_CONTROL,
33317ce2522SLizhi Hou 			   CHAN_CTRL_START);
33417ce2522SLizhi Hou 	if (ret)
33517ce2522SLizhi Hou 		return ret;
33617ce2522SLizhi Hou 
33717ce2522SLizhi Hou 	xchan->busy = true;
33817ce2522SLizhi Hou 	return 0;
33917ce2522SLizhi Hou }
34017ce2522SLizhi Hou 
34117ce2522SLizhi Hou /**
34217ce2522SLizhi Hou  * xdma_alloc_channels - Detect and allocate DMA channels
34317ce2522SLizhi Hou  * @xdev: DMA device pointer
34417ce2522SLizhi Hou  * @dir: Channel direction
34517ce2522SLizhi Hou  */
xdma_alloc_channels(struct xdma_device * xdev,enum dma_transfer_direction dir)34617ce2522SLizhi Hou static int xdma_alloc_channels(struct xdma_device *xdev,
34717ce2522SLizhi Hou 			       enum dma_transfer_direction dir)
34817ce2522SLizhi Hou {
34917ce2522SLizhi Hou 	struct xdma_platdata *pdata = dev_get_platdata(&xdev->pdev->dev);
35017ce2522SLizhi Hou 	struct xdma_chan **chans, *xchan;
35117ce2522SLizhi Hou 	u32 base, identifier, target;
35217ce2522SLizhi Hou 	u32 *chan_num;
35317ce2522SLizhi Hou 	int i, j, ret;
35417ce2522SLizhi Hou 
35517ce2522SLizhi Hou 	if (dir == DMA_MEM_TO_DEV) {
35617ce2522SLizhi Hou 		base = XDMA_CHAN_H2C_OFFSET;
35717ce2522SLizhi Hou 		target = XDMA_CHAN_H2C_TARGET;
35817ce2522SLizhi Hou 		chans = &xdev->h2c_chans;
35917ce2522SLizhi Hou 		chan_num = &xdev->h2c_chan_num;
36017ce2522SLizhi Hou 	} else if (dir == DMA_DEV_TO_MEM) {
36117ce2522SLizhi Hou 		base = XDMA_CHAN_C2H_OFFSET;
36217ce2522SLizhi Hou 		target = XDMA_CHAN_C2H_TARGET;
36317ce2522SLizhi Hou 		chans = &xdev->c2h_chans;
36417ce2522SLizhi Hou 		chan_num = &xdev->c2h_chan_num;
36517ce2522SLizhi Hou 	} else {
36617ce2522SLizhi Hou 		xdma_err(xdev, "invalid direction specified");
36717ce2522SLizhi Hou 		return -EINVAL;
36817ce2522SLizhi Hou 	}
36917ce2522SLizhi Hou 
37017ce2522SLizhi Hou 	/* detect number of available DMA channels */
37117ce2522SLizhi Hou 	for (i = 0, *chan_num = 0; i < pdata->max_dma_channels; i++) {
37217ce2522SLizhi Hou 		ret = regmap_read(xdev->rmap, base + i * XDMA_CHAN_STRIDE,
37317ce2522SLizhi Hou 				  &identifier);
37417ce2522SLizhi Hou 		if (ret)
37517ce2522SLizhi Hou 			return ret;
37617ce2522SLizhi Hou 
37717ce2522SLizhi Hou 		/* check if it is available DMA channel */
37817ce2522SLizhi Hou 		if (XDMA_CHAN_CHECK_TARGET(identifier, target))
37917ce2522SLizhi Hou 			(*chan_num)++;
38017ce2522SLizhi Hou 	}
38117ce2522SLizhi Hou 
38217ce2522SLizhi Hou 	if (!*chan_num) {
38317ce2522SLizhi Hou 		xdma_err(xdev, "does not probe any channel");
38417ce2522SLizhi Hou 		return -EINVAL;
38517ce2522SLizhi Hou 	}
38617ce2522SLizhi Hou 
38717ce2522SLizhi Hou 	*chans = devm_kcalloc(&xdev->pdev->dev, *chan_num, sizeof(**chans),
38817ce2522SLizhi Hou 			      GFP_KERNEL);
38917ce2522SLizhi Hou 	if (!*chans)
39017ce2522SLizhi Hou 		return -ENOMEM;
39117ce2522SLizhi Hou 
39217ce2522SLizhi Hou 	for (i = 0, j = 0; i < pdata->max_dma_channels; i++) {
39317ce2522SLizhi Hou 		ret = regmap_read(xdev->rmap, base + i * XDMA_CHAN_STRIDE,
39417ce2522SLizhi Hou 				  &identifier);
39517ce2522SLizhi Hou 		if (ret)
39617ce2522SLizhi Hou 			return ret;
39717ce2522SLizhi Hou 
39817ce2522SLizhi Hou 		if (!XDMA_CHAN_CHECK_TARGET(identifier, target))
39917ce2522SLizhi Hou 			continue;
40017ce2522SLizhi Hou 
40117ce2522SLizhi Hou 		if (j == *chan_num) {
40217ce2522SLizhi Hou 			xdma_err(xdev, "invalid channel number");
40317ce2522SLizhi Hou 			return -EIO;
40417ce2522SLizhi Hou 		}
40517ce2522SLizhi Hou 
40617ce2522SLizhi Hou 		/* init channel structure and hardware */
40717ce2522SLizhi Hou 		xchan = &(*chans)[j];
40817ce2522SLizhi Hou 		xchan->xdev_hdl = xdev;
40917ce2522SLizhi Hou 		xchan->base = base + i * XDMA_CHAN_STRIDE;
41017ce2522SLizhi Hou 		xchan->dir = dir;
41117ce2522SLizhi Hou 
41217ce2522SLizhi Hou 		ret = xdma_channel_init(xchan);
41317ce2522SLizhi Hou 		if (ret)
41417ce2522SLizhi Hou 			return ret;
41517ce2522SLizhi Hou 		xchan->vchan.desc_free = xdma_free_desc;
41617ce2522SLizhi Hou 		vchan_init(&xchan->vchan, &xdev->dma_dev);
41717ce2522SLizhi Hou 
41817ce2522SLizhi Hou 		j++;
41917ce2522SLizhi Hou 	}
42017ce2522SLizhi Hou 
42117ce2522SLizhi Hou 	dev_info(&xdev->pdev->dev, "configured %d %s channels", j,
42217ce2522SLizhi Hou 		 (dir == DMA_MEM_TO_DEV) ? "H2C" : "C2H");
42317ce2522SLizhi Hou 
42417ce2522SLizhi Hou 	return 0;
42517ce2522SLizhi Hou }
42617ce2522SLizhi Hou 
42717ce2522SLizhi Hou /**
42817ce2522SLizhi Hou  * xdma_issue_pending - Issue pending transactions
42917ce2522SLizhi Hou  * @chan: DMA channel pointer
43017ce2522SLizhi Hou  */
xdma_issue_pending(struct dma_chan * chan)43117ce2522SLizhi Hou static void xdma_issue_pending(struct dma_chan *chan)
43217ce2522SLizhi Hou {
43317ce2522SLizhi Hou 	struct xdma_chan *xdma_chan = to_xdma_chan(chan);
43417ce2522SLizhi Hou 	unsigned long flags;
43517ce2522SLizhi Hou 
43617ce2522SLizhi Hou 	spin_lock_irqsave(&xdma_chan->vchan.lock, flags);
43717ce2522SLizhi Hou 	if (vchan_issue_pending(&xdma_chan->vchan))
43817ce2522SLizhi Hou 		xdma_xfer_start(xdma_chan);
43917ce2522SLizhi Hou 	spin_unlock_irqrestore(&xdma_chan->vchan.lock, flags);
44017ce2522SLizhi Hou }
44117ce2522SLizhi Hou 
44217ce2522SLizhi Hou /**
44317ce2522SLizhi Hou  * xdma_prep_device_sg - prepare a descriptor for a DMA transaction
44417ce2522SLizhi Hou  * @chan: DMA channel pointer
44517ce2522SLizhi Hou  * @sgl: Transfer scatter gather list
44617ce2522SLizhi Hou  * @sg_len: Length of scatter gather list
44717ce2522SLizhi Hou  * @dir: Transfer direction
44817ce2522SLizhi Hou  * @flags: transfer ack flags
44917ce2522SLizhi Hou  * @context: APP words of the descriptor
45017ce2522SLizhi Hou  */
45117ce2522SLizhi Hou static struct dma_async_tx_descriptor *
xdma_prep_device_sg(struct dma_chan * chan,struct scatterlist * sgl,unsigned int sg_len,enum dma_transfer_direction dir,unsigned long flags,void * context)45217ce2522SLizhi Hou xdma_prep_device_sg(struct dma_chan *chan, struct scatterlist *sgl,
45317ce2522SLizhi Hou 		    unsigned int sg_len, enum dma_transfer_direction dir,
45417ce2522SLizhi Hou 		    unsigned long flags, void *context)
45517ce2522SLizhi Hou {
45617ce2522SLizhi Hou 	struct xdma_chan *xdma_chan = to_xdma_chan(chan);
45717ce2522SLizhi Hou 	struct dma_async_tx_descriptor *tx_desc;
45817ce2522SLizhi Hou 	u32 desc_num = 0, i, len, rest;
45917ce2522SLizhi Hou 	struct xdma_desc_block *dblk;
46017ce2522SLizhi Hou 	struct xdma_hw_desc *desc;
46117ce2522SLizhi Hou 	struct xdma_desc *sw_desc;
46217ce2522SLizhi Hou 	u64 dev_addr, *src, *dst;
46317ce2522SLizhi Hou 	struct scatterlist *sg;
46417ce2522SLizhi Hou 	u64 addr;
46517ce2522SLizhi Hou 
46617ce2522SLizhi Hou 	for_each_sg(sgl, sg, sg_len, i)
46717ce2522SLizhi Hou 		desc_num += DIV_ROUND_UP(sg_dma_len(sg), XDMA_DESC_BLEN_MAX);
46817ce2522SLizhi Hou 
46917ce2522SLizhi Hou 	sw_desc = xdma_alloc_desc(xdma_chan, desc_num);
47017ce2522SLizhi Hou 	if (!sw_desc)
47117ce2522SLizhi Hou 		return NULL;
47217ce2522SLizhi Hou 	sw_desc->dir = dir;
47317ce2522SLizhi Hou 
47417ce2522SLizhi Hou 	if (dir == DMA_MEM_TO_DEV) {
47517ce2522SLizhi Hou 		dev_addr = xdma_chan->cfg.dst_addr;
47617ce2522SLizhi Hou 		src = &addr;
47717ce2522SLizhi Hou 		dst = &dev_addr;
47817ce2522SLizhi Hou 	} else {
47917ce2522SLizhi Hou 		dev_addr = xdma_chan->cfg.src_addr;
48017ce2522SLizhi Hou 		src = &dev_addr;
48117ce2522SLizhi Hou 		dst = &addr;
48217ce2522SLizhi Hou 	}
48317ce2522SLizhi Hou 
48417ce2522SLizhi Hou 	dblk = sw_desc->desc_blocks;
48517ce2522SLizhi Hou 	desc = dblk->virt_addr;
48617ce2522SLizhi Hou 	desc_num = 1;
48717ce2522SLizhi Hou 	for_each_sg(sgl, sg, sg_len, i) {
48817ce2522SLizhi Hou 		addr = sg_dma_address(sg);
48917ce2522SLizhi Hou 		rest = sg_dma_len(sg);
49017ce2522SLizhi Hou 
49117ce2522SLizhi Hou 		do {
49217ce2522SLizhi Hou 			len = min_t(u32, rest, XDMA_DESC_BLEN_MAX);
49317ce2522SLizhi Hou 			/* set hardware descriptor */
49417ce2522SLizhi Hou 			desc->bytes = cpu_to_le32(len);
49517ce2522SLizhi Hou 			desc->src_addr = cpu_to_le64(*src);
49617ce2522SLizhi Hou 			desc->dst_addr = cpu_to_le64(*dst);
49717ce2522SLizhi Hou 
49817ce2522SLizhi Hou 			if (!(desc_num & XDMA_DESC_ADJACENT_MASK)) {
49917ce2522SLizhi Hou 				dblk++;
50017ce2522SLizhi Hou 				desc = dblk->virt_addr;
50117ce2522SLizhi Hou 			} else {
50217ce2522SLizhi Hou 				desc++;
50317ce2522SLizhi Hou 			}
50417ce2522SLizhi Hou 
50517ce2522SLizhi Hou 			desc_num++;
50617ce2522SLizhi Hou 			dev_addr += len;
50717ce2522SLizhi Hou 			addr += len;
50817ce2522SLizhi Hou 			rest -= len;
50917ce2522SLizhi Hou 		} while (rest);
51017ce2522SLizhi Hou 	}
51117ce2522SLizhi Hou 
51217ce2522SLizhi Hou 	tx_desc = vchan_tx_prep(&xdma_chan->vchan, &sw_desc->vdesc, flags);
51317ce2522SLizhi Hou 	if (!tx_desc)
51417ce2522SLizhi Hou 		goto failed;
51517ce2522SLizhi Hou 
51617ce2522SLizhi Hou 	return tx_desc;
51717ce2522SLizhi Hou 
51817ce2522SLizhi Hou failed:
51917ce2522SLizhi Hou 	xdma_free_desc(&sw_desc->vdesc);
52017ce2522SLizhi Hou 
52117ce2522SLizhi Hou 	return NULL;
52217ce2522SLizhi Hou }
52317ce2522SLizhi Hou 
52417ce2522SLizhi Hou /**
52517ce2522SLizhi Hou  * xdma_device_config - Configure the DMA channel
52617ce2522SLizhi Hou  * @chan: DMA channel
52717ce2522SLizhi Hou  * @cfg: channel configuration
52817ce2522SLizhi Hou  */
xdma_device_config(struct dma_chan * chan,struct dma_slave_config * cfg)52917ce2522SLizhi Hou static int xdma_device_config(struct dma_chan *chan,
53017ce2522SLizhi Hou 			      struct dma_slave_config *cfg)
53117ce2522SLizhi Hou {
53217ce2522SLizhi Hou 	struct xdma_chan *xdma_chan = to_xdma_chan(chan);
53317ce2522SLizhi Hou 
53417ce2522SLizhi Hou 	memcpy(&xdma_chan->cfg, cfg, sizeof(*cfg));
53517ce2522SLizhi Hou 
53617ce2522SLizhi Hou 	return 0;
53717ce2522SLizhi Hou }
53817ce2522SLizhi Hou 
53917ce2522SLizhi Hou /**
54017ce2522SLizhi Hou  * xdma_free_chan_resources - Free channel resources
54117ce2522SLizhi Hou  * @chan: DMA channel
54217ce2522SLizhi Hou  */
xdma_free_chan_resources(struct dma_chan * chan)54317ce2522SLizhi Hou static void xdma_free_chan_resources(struct dma_chan *chan)
54417ce2522SLizhi Hou {
54517ce2522SLizhi Hou 	struct xdma_chan *xdma_chan = to_xdma_chan(chan);
54617ce2522SLizhi Hou 
54717ce2522SLizhi Hou 	vchan_free_chan_resources(&xdma_chan->vchan);
54817ce2522SLizhi Hou 	dma_pool_destroy(xdma_chan->desc_pool);
54917ce2522SLizhi Hou 	xdma_chan->desc_pool = NULL;
55017ce2522SLizhi Hou }
55117ce2522SLizhi Hou 
55217ce2522SLizhi Hou /**
55317ce2522SLizhi Hou  * xdma_alloc_chan_resources - Allocate channel resources
55417ce2522SLizhi Hou  * @chan: DMA channel
55517ce2522SLizhi Hou  */
xdma_alloc_chan_resources(struct dma_chan * chan)55617ce2522SLizhi Hou static int xdma_alloc_chan_resources(struct dma_chan *chan)
55717ce2522SLizhi Hou {
55817ce2522SLizhi Hou 	struct xdma_chan *xdma_chan = to_xdma_chan(chan);
55917ce2522SLizhi Hou 	struct xdma_device *xdev = xdma_chan->xdev_hdl;
56017ce2522SLizhi Hou 	struct device *dev = xdev->dma_dev.dev;
56117ce2522SLizhi Hou 
56217ce2522SLizhi Hou 	while (dev && !dev_is_pci(dev))
56317ce2522SLizhi Hou 		dev = dev->parent;
56417ce2522SLizhi Hou 	if (!dev) {
56517ce2522SLizhi Hou 		xdma_err(xdev, "unable to find pci device");
56617ce2522SLizhi Hou 		return -EINVAL;
56717ce2522SLizhi Hou 	}
56817ce2522SLizhi Hou 
56917ce2522SLizhi Hou 	xdma_chan->desc_pool = dma_pool_create(dma_chan_name(chan),
57017ce2522SLizhi Hou 					       dev, XDMA_DESC_BLOCK_SIZE,
57117ce2522SLizhi Hou 					       XDMA_DESC_BLOCK_ALIGN, 0);
57217ce2522SLizhi Hou 	if (!xdma_chan->desc_pool) {
57317ce2522SLizhi Hou 		xdma_err(xdev, "unable to allocate descriptor pool");
57417ce2522SLizhi Hou 		return -ENOMEM;
57517ce2522SLizhi Hou 	}
57617ce2522SLizhi Hou 
57717ce2522SLizhi Hou 	return 0;
57817ce2522SLizhi Hou }
57917ce2522SLizhi Hou 
58017ce2522SLizhi Hou /**
58117ce2522SLizhi Hou  * xdma_channel_isr - XDMA channel interrupt handler
58217ce2522SLizhi Hou  * @irq: IRQ number
58317ce2522SLizhi Hou  * @dev_id: Pointer to the DMA channel structure
58417ce2522SLizhi Hou  */
xdma_channel_isr(int irq,void * dev_id)58517ce2522SLizhi Hou static irqreturn_t xdma_channel_isr(int irq, void *dev_id)
58617ce2522SLizhi Hou {
58717ce2522SLizhi Hou 	struct xdma_chan *xchan = dev_id;
58817ce2522SLizhi Hou 	u32 complete_desc_num = 0;
58917ce2522SLizhi Hou 	struct xdma_device *xdev;
59017ce2522SLizhi Hou 	struct virt_dma_desc *vd;
59117ce2522SLizhi Hou 	struct xdma_desc *desc;
59217ce2522SLizhi Hou 	int ret;
59317ce2522SLizhi Hou 
59417ce2522SLizhi Hou 	spin_lock(&xchan->vchan.lock);
59517ce2522SLizhi Hou 
59617ce2522SLizhi Hou 	/* get submitted request */
59717ce2522SLizhi Hou 	vd = vchan_next_desc(&xchan->vchan);
59817ce2522SLizhi Hou 	if (!vd)
59917ce2522SLizhi Hou 		goto out;
60017ce2522SLizhi Hou 
60117ce2522SLizhi Hou 	xchan->busy = false;
60217ce2522SLizhi Hou 	desc = to_xdma_desc(vd);
60317ce2522SLizhi Hou 	xdev = xchan->xdev_hdl;
60417ce2522SLizhi Hou 
60517ce2522SLizhi Hou 	ret = regmap_read(xdev->rmap, xchan->base + XDMA_CHAN_COMPLETED_DESC,
60617ce2522SLizhi Hou 			  &complete_desc_num);
60717ce2522SLizhi Hou 	if (ret)
60817ce2522SLizhi Hou 		goto out;
60917ce2522SLizhi Hou 
61017ce2522SLizhi Hou 	desc->completed_desc_num += complete_desc_num;
61117ce2522SLizhi Hou 	/*
61217ce2522SLizhi Hou 	 * if all data blocks are transferred, remove and complete the request
61317ce2522SLizhi Hou 	 */
61417ce2522SLizhi Hou 	if (desc->completed_desc_num == desc->desc_num) {
61517ce2522SLizhi Hou 		list_del(&vd->node);
61617ce2522SLizhi Hou 		vchan_cookie_complete(vd);
61717ce2522SLizhi Hou 		goto out;
61817ce2522SLizhi Hou 	}
61917ce2522SLizhi Hou 
62017ce2522SLizhi Hou 	if (desc->completed_desc_num > desc->desc_num ||
62117ce2522SLizhi Hou 	    complete_desc_num != XDMA_DESC_BLOCK_NUM * XDMA_DESC_ADJACENT)
62217ce2522SLizhi Hou 		goto out;
62317ce2522SLizhi Hou 
62417ce2522SLizhi Hou 	/* transfer the rest of data */
62517ce2522SLizhi Hou 	xdma_xfer_start(xchan);
62617ce2522SLizhi Hou 
62717ce2522SLizhi Hou out:
62817ce2522SLizhi Hou 	spin_unlock(&xchan->vchan.lock);
62917ce2522SLizhi Hou 	return IRQ_HANDLED;
63017ce2522SLizhi Hou }
63117ce2522SLizhi Hou 
63217ce2522SLizhi Hou /**
63317ce2522SLizhi Hou  * xdma_irq_fini - Uninitialize IRQ
63417ce2522SLizhi Hou  * @xdev: DMA device pointer
63517ce2522SLizhi Hou  */
xdma_irq_fini(struct xdma_device * xdev)63617ce2522SLizhi Hou static void xdma_irq_fini(struct xdma_device *xdev)
63717ce2522SLizhi Hou {
63817ce2522SLizhi Hou 	int i;
63917ce2522SLizhi Hou 
64017ce2522SLizhi Hou 	/* disable interrupt */
64117ce2522SLizhi Hou 	regmap_write(xdev->rmap, XDMA_IRQ_CHAN_INT_EN_W1C, ~0);
64217ce2522SLizhi Hou 
64317ce2522SLizhi Hou 	/* free irq handler */
64417ce2522SLizhi Hou 	for (i = 0; i < xdev->h2c_chan_num; i++)
64517ce2522SLizhi Hou 		free_irq(xdev->h2c_chans[i].irq, &xdev->h2c_chans[i]);
64617ce2522SLizhi Hou 
64717ce2522SLizhi Hou 	for (i = 0; i < xdev->c2h_chan_num; i++)
64817ce2522SLizhi Hou 		free_irq(xdev->c2h_chans[i].irq, &xdev->c2h_chans[i]);
64917ce2522SLizhi Hou }
65017ce2522SLizhi Hou 
65117ce2522SLizhi Hou /**
65217ce2522SLizhi Hou  * xdma_set_vector_reg - configure hardware IRQ registers
65317ce2522SLizhi Hou  * @xdev: DMA device pointer
65417ce2522SLizhi Hou  * @vec_tbl_start: Start of IRQ registers
65517ce2522SLizhi Hou  * @irq_start: Start of IRQ
65617ce2522SLizhi Hou  * @irq_num: Number of IRQ
65717ce2522SLizhi Hou  */
xdma_set_vector_reg(struct xdma_device * xdev,u32 vec_tbl_start,u32 irq_start,u32 irq_num)65817ce2522SLizhi Hou static int xdma_set_vector_reg(struct xdma_device *xdev, u32 vec_tbl_start,
65917ce2522SLizhi Hou 			       u32 irq_start, u32 irq_num)
66017ce2522SLizhi Hou {
66117ce2522SLizhi Hou 	u32 shift, i, val = 0;
66217ce2522SLizhi Hou 	int ret;
66317ce2522SLizhi Hou 
66417ce2522SLizhi Hou 	/* Each IRQ register is 32 bit and contains 4 IRQs */
66517ce2522SLizhi Hou 	while (irq_num > 0) {
66617ce2522SLizhi Hou 		for (i = 0; i < 4; i++) {
66717ce2522SLizhi Hou 			shift = XDMA_IRQ_VEC_SHIFT * i;
66817ce2522SLizhi Hou 			val |= irq_start << shift;
66917ce2522SLizhi Hou 			irq_start++;
67017ce2522SLizhi Hou 			irq_num--;
67196891e90SMiquel Raynal 			if (!irq_num)
67296891e90SMiquel Raynal 				break;
67317ce2522SLizhi Hou 		}
67417ce2522SLizhi Hou 
67517ce2522SLizhi Hou 		/* write IRQ register */
67617ce2522SLizhi Hou 		ret = regmap_write(xdev->rmap, vec_tbl_start, val);
67717ce2522SLizhi Hou 		if (ret)
67817ce2522SLizhi Hou 			return ret;
67917ce2522SLizhi Hou 		vec_tbl_start += sizeof(u32);
68017ce2522SLizhi Hou 		val = 0;
68117ce2522SLizhi Hou 	}
68217ce2522SLizhi Hou 
68317ce2522SLizhi Hou 	return 0;
68417ce2522SLizhi Hou }
68517ce2522SLizhi Hou 
68617ce2522SLizhi Hou /**
68717ce2522SLizhi Hou  * xdma_irq_init - initialize IRQs
68817ce2522SLizhi Hou  * @xdev: DMA device pointer
68917ce2522SLizhi Hou  */
xdma_irq_init(struct xdma_device * xdev)69017ce2522SLizhi Hou static int xdma_irq_init(struct xdma_device *xdev)
69117ce2522SLizhi Hou {
69217ce2522SLizhi Hou 	u32 irq = xdev->irq_start;
693ecf294a6SLizhi Hou 	u32 user_irq_start;
69417ce2522SLizhi Hou 	int i, j, ret;
69517ce2522SLizhi Hou 
69617ce2522SLizhi Hou 	/* return failure if there are not enough IRQs */
69717ce2522SLizhi Hou 	if (xdev->irq_num < XDMA_CHAN_NUM(xdev)) {
69817ce2522SLizhi Hou 		xdma_err(xdev, "not enough irq");
69917ce2522SLizhi Hou 		return -EINVAL;
70017ce2522SLizhi Hou 	}
70117ce2522SLizhi Hou 
70217ce2522SLizhi Hou 	/* setup H2C interrupt handler */
70317ce2522SLizhi Hou 	for (i = 0; i < xdev->h2c_chan_num; i++) {
70417ce2522SLizhi Hou 		ret = request_irq(irq, xdma_channel_isr, 0,
70517ce2522SLizhi Hou 				  "xdma-h2c-channel", &xdev->h2c_chans[i]);
70617ce2522SLizhi Hou 		if (ret) {
70717ce2522SLizhi Hou 			xdma_err(xdev, "H2C channel%d request irq%d failed: %d",
70817ce2522SLizhi Hou 				 i, irq, ret);
70917ce2522SLizhi Hou 			goto failed_init_h2c;
71017ce2522SLizhi Hou 		}
71117ce2522SLizhi Hou 		xdev->h2c_chans[i].irq = irq;
71217ce2522SLizhi Hou 		irq++;
71317ce2522SLizhi Hou 	}
71417ce2522SLizhi Hou 
71517ce2522SLizhi Hou 	/* setup C2H interrupt handler */
71617ce2522SLizhi Hou 	for (j = 0; j < xdev->c2h_chan_num; j++) {
71717ce2522SLizhi Hou 		ret = request_irq(irq, xdma_channel_isr, 0,
71817ce2522SLizhi Hou 				  "xdma-c2h-channel", &xdev->c2h_chans[j]);
71917ce2522SLizhi Hou 		if (ret) {
720*422dbc66SMiquel Raynal 			xdma_err(xdev, "C2H channel%d request irq%d failed: %d",
72117ce2522SLizhi Hou 				 j, irq, ret);
72217ce2522SLizhi Hou 			goto failed_init_c2h;
72317ce2522SLizhi Hou 		}
72417ce2522SLizhi Hou 		xdev->c2h_chans[j].irq = irq;
72517ce2522SLizhi Hou 		irq++;
72617ce2522SLizhi Hou 	}
72717ce2522SLizhi Hou 
72817ce2522SLizhi Hou 	/* config hardware IRQ registers */
72917ce2522SLizhi Hou 	ret = xdma_set_vector_reg(xdev, XDMA_IRQ_CHAN_VEC_NUM, 0,
73017ce2522SLizhi Hou 				  XDMA_CHAN_NUM(xdev));
73117ce2522SLizhi Hou 	if (ret) {
73217ce2522SLizhi Hou 		xdma_err(xdev, "failed to set channel vectors: %d", ret);
73317ce2522SLizhi Hou 		goto failed_init_c2h;
73417ce2522SLizhi Hou 	}
73517ce2522SLizhi Hou 
736ecf294a6SLizhi Hou 	/* config user IRQ registers if needed */
737ecf294a6SLizhi Hou 	user_irq_start = XDMA_CHAN_NUM(xdev);
738ecf294a6SLizhi Hou 	if (xdev->irq_num > user_irq_start) {
739ecf294a6SLizhi Hou 		ret = xdma_set_vector_reg(xdev, XDMA_IRQ_USER_VEC_NUM,
740ecf294a6SLizhi Hou 					  user_irq_start,
741ecf294a6SLizhi Hou 					  xdev->irq_num - user_irq_start);
742ecf294a6SLizhi Hou 		if (ret) {
743ecf294a6SLizhi Hou 			xdma_err(xdev, "failed to set user vectors: %d", ret);
744ecf294a6SLizhi Hou 			goto failed_init_c2h;
745ecf294a6SLizhi Hou 		}
746ecf294a6SLizhi Hou 	}
747ecf294a6SLizhi Hou 
74817ce2522SLizhi Hou 	/* enable interrupt */
74917ce2522SLizhi Hou 	ret = regmap_write(xdev->rmap, XDMA_IRQ_CHAN_INT_EN_W1S, ~0);
75017ce2522SLizhi Hou 	if (ret)
75117ce2522SLizhi Hou 		goto failed_init_c2h;
75217ce2522SLizhi Hou 
75317ce2522SLizhi Hou 	return 0;
75417ce2522SLizhi Hou 
75517ce2522SLizhi Hou failed_init_c2h:
75617ce2522SLizhi Hou 	while (j--)
75717ce2522SLizhi Hou 		free_irq(xdev->c2h_chans[j].irq, &xdev->c2h_chans[j]);
75817ce2522SLizhi Hou failed_init_h2c:
75917ce2522SLizhi Hou 	while (i--)
76017ce2522SLizhi Hou 		free_irq(xdev->h2c_chans[i].irq, &xdev->h2c_chans[i]);
76117ce2522SLizhi Hou 
76217ce2522SLizhi Hou 	return ret;
76317ce2522SLizhi Hou }
76417ce2522SLizhi Hou 
xdma_filter_fn(struct dma_chan * chan,void * param)76517ce2522SLizhi Hou static bool xdma_filter_fn(struct dma_chan *chan, void *param)
76617ce2522SLizhi Hou {
76717ce2522SLizhi Hou 	struct xdma_chan *xdma_chan = to_xdma_chan(chan);
76817ce2522SLizhi Hou 	struct xdma_chan_info *chan_info = param;
76917ce2522SLizhi Hou 
77017ce2522SLizhi Hou 	return chan_info->dir == xdma_chan->dir;
77117ce2522SLizhi Hou }
77217ce2522SLizhi Hou 
77317ce2522SLizhi Hou /**
774ecf294a6SLizhi Hou  * xdma_disable_user_irq - Disable user interrupt
775ecf294a6SLizhi Hou  * @pdev: Pointer to the platform_device structure
776ecf294a6SLizhi Hou  * @irq_num: System IRQ number
777ecf294a6SLizhi Hou  */
xdma_disable_user_irq(struct platform_device * pdev,u32 irq_num)778ecf294a6SLizhi Hou void xdma_disable_user_irq(struct platform_device *pdev, u32 irq_num)
779ecf294a6SLizhi Hou {
780ecf294a6SLizhi Hou 	struct xdma_device *xdev = platform_get_drvdata(pdev);
781ecf294a6SLizhi Hou 	u32 index;
782ecf294a6SLizhi Hou 
783ecf294a6SLizhi Hou 	index = irq_num - xdev->irq_start;
784ecf294a6SLizhi Hou 	if (index < XDMA_CHAN_NUM(xdev) || index >= xdev->irq_num) {
785ecf294a6SLizhi Hou 		xdma_err(xdev, "invalid user irq number");
786ecf294a6SLizhi Hou 		return;
787ecf294a6SLizhi Hou 	}
788ecf294a6SLizhi Hou 	index -= XDMA_CHAN_NUM(xdev);
789ecf294a6SLizhi Hou 
790ecf294a6SLizhi Hou 	regmap_write(xdev->rmap, XDMA_IRQ_USER_INT_EN_W1C, 1 << index);
791ecf294a6SLizhi Hou }
792ecf294a6SLizhi Hou EXPORT_SYMBOL(xdma_disable_user_irq);
793ecf294a6SLizhi Hou 
794ecf294a6SLizhi Hou /**
795ecf294a6SLizhi Hou  * xdma_enable_user_irq - Enable user logic interrupt
796ecf294a6SLizhi Hou  * @pdev: Pointer to the platform_device structure
797ecf294a6SLizhi Hou  * @irq_num: System IRQ number
798ecf294a6SLizhi Hou  */
xdma_enable_user_irq(struct platform_device * pdev,u32 irq_num)799ecf294a6SLizhi Hou int xdma_enable_user_irq(struct platform_device *pdev, u32 irq_num)
800ecf294a6SLizhi Hou {
801ecf294a6SLizhi Hou 	struct xdma_device *xdev = platform_get_drvdata(pdev);
802ecf294a6SLizhi Hou 	u32 index;
803ecf294a6SLizhi Hou 	int ret;
804ecf294a6SLizhi Hou 
805ecf294a6SLizhi Hou 	index = irq_num - xdev->irq_start;
806ecf294a6SLizhi Hou 	if (index < XDMA_CHAN_NUM(xdev) || index >= xdev->irq_num) {
807ecf294a6SLizhi Hou 		xdma_err(xdev, "invalid user irq number");
808ecf294a6SLizhi Hou 		return -EINVAL;
809ecf294a6SLizhi Hou 	}
810ecf294a6SLizhi Hou 	index -= XDMA_CHAN_NUM(xdev);
811ecf294a6SLizhi Hou 
812ecf294a6SLizhi Hou 	ret = regmap_write(xdev->rmap, XDMA_IRQ_USER_INT_EN_W1S, 1 << index);
813ecf294a6SLizhi Hou 	if (ret)
814ecf294a6SLizhi Hou 		return ret;
815ecf294a6SLizhi Hou 
816ecf294a6SLizhi Hou 	return 0;
817ecf294a6SLizhi Hou }
818ecf294a6SLizhi Hou EXPORT_SYMBOL(xdma_enable_user_irq);
819ecf294a6SLizhi Hou 
820ecf294a6SLizhi Hou /**
821ecf294a6SLizhi Hou  * xdma_get_user_irq - Get system IRQ number
822ecf294a6SLizhi Hou  * @pdev: Pointer to the platform_device structure
823ecf294a6SLizhi Hou  * @user_irq_index: User logic IRQ wire index
824ecf294a6SLizhi Hou  *
825ecf294a6SLizhi Hou  * Return: The system IRQ number allocated for the given wire index.
826ecf294a6SLizhi Hou  */
xdma_get_user_irq(struct platform_device * pdev,u32 user_irq_index)827ecf294a6SLizhi Hou int xdma_get_user_irq(struct platform_device *pdev, u32 user_irq_index)
828ecf294a6SLizhi Hou {
829ecf294a6SLizhi Hou 	struct xdma_device *xdev = platform_get_drvdata(pdev);
830ecf294a6SLizhi Hou 
831ecf294a6SLizhi Hou 	if (XDMA_CHAN_NUM(xdev) + user_irq_index >= xdev->irq_num) {
832ecf294a6SLizhi Hou 		xdma_err(xdev, "invalid user irq index");
833ecf294a6SLizhi Hou 		return -EINVAL;
834ecf294a6SLizhi Hou 	}
835ecf294a6SLizhi Hou 
836ecf294a6SLizhi Hou 	return xdev->irq_start + XDMA_CHAN_NUM(xdev) + user_irq_index;
837ecf294a6SLizhi Hou }
838ecf294a6SLizhi Hou EXPORT_SYMBOL(xdma_get_user_irq);
839ecf294a6SLizhi Hou 
840ecf294a6SLizhi Hou /**
84117ce2522SLizhi Hou  * xdma_remove - Driver remove function
84217ce2522SLizhi Hou  * @pdev: Pointer to the platform_device structure
84317ce2522SLizhi Hou  */
xdma_remove(struct platform_device * pdev)84417ce2522SLizhi Hou static int xdma_remove(struct platform_device *pdev)
84517ce2522SLizhi Hou {
84617ce2522SLizhi Hou 	struct xdma_device *xdev = platform_get_drvdata(pdev);
84717ce2522SLizhi Hou 
84817ce2522SLizhi Hou 	if (xdev->status & XDMA_DEV_STATUS_INIT_MSIX)
84917ce2522SLizhi Hou 		xdma_irq_fini(xdev);
85017ce2522SLizhi Hou 
85117ce2522SLizhi Hou 	if (xdev->status & XDMA_DEV_STATUS_REG_DMA)
85217ce2522SLizhi Hou 		dma_async_device_unregister(&xdev->dma_dev);
85317ce2522SLizhi Hou 
85417ce2522SLizhi Hou 	return 0;
85517ce2522SLizhi Hou }
85617ce2522SLizhi Hou 
85717ce2522SLizhi Hou /**
85817ce2522SLizhi Hou  * xdma_probe - Driver probe function
85917ce2522SLizhi Hou  * @pdev: Pointer to the platform_device structure
86017ce2522SLizhi Hou  */
xdma_probe(struct platform_device * pdev)86117ce2522SLizhi Hou static int xdma_probe(struct platform_device *pdev)
86217ce2522SLizhi Hou {
86317ce2522SLizhi Hou 	struct xdma_platdata *pdata = dev_get_platdata(&pdev->dev);
86417ce2522SLizhi Hou 	struct xdma_device *xdev;
86517ce2522SLizhi Hou 	void __iomem *reg_base;
86617ce2522SLizhi Hou 	struct resource *res;
86717ce2522SLizhi Hou 	int ret = -ENODEV;
86817ce2522SLizhi Hou 
86917ce2522SLizhi Hou 	if (pdata->max_dma_channels > XDMA_MAX_CHANNELS) {
87017ce2522SLizhi Hou 		dev_err(&pdev->dev, "invalid max dma channels %d",
87117ce2522SLizhi Hou 			pdata->max_dma_channels);
87217ce2522SLizhi Hou 		return -EINVAL;
87317ce2522SLizhi Hou 	}
87417ce2522SLizhi Hou 
87517ce2522SLizhi Hou 	xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
87617ce2522SLizhi Hou 	if (!xdev)
87717ce2522SLizhi Hou 		return -ENOMEM;
87817ce2522SLizhi Hou 
87917ce2522SLizhi Hou 	platform_set_drvdata(pdev, xdev);
88017ce2522SLizhi Hou 	xdev->pdev = pdev;
88117ce2522SLizhi Hou 
88217ce2522SLizhi Hou 	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
88317ce2522SLizhi Hou 	if (!res) {
88417ce2522SLizhi Hou 		xdma_err(xdev, "failed to get irq resource");
88517ce2522SLizhi Hou 		goto failed;
88617ce2522SLizhi Hou 	}
88717ce2522SLizhi Hou 	xdev->irq_start = res->start;
88817ce2522SLizhi Hou 	xdev->irq_num = res->end - res->start + 1;
88917ce2522SLizhi Hou 
89017ce2522SLizhi Hou 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
89117ce2522SLizhi Hou 	if (!res) {
89217ce2522SLizhi Hou 		xdma_err(xdev, "failed to get io resource");
89317ce2522SLizhi Hou 		goto failed;
89417ce2522SLizhi Hou 	}
89517ce2522SLizhi Hou 
89617ce2522SLizhi Hou 	reg_base = devm_ioremap_resource(&pdev->dev, res);
897a68b48afSMinjie Du 	if (IS_ERR(reg_base)) {
89817ce2522SLizhi Hou 		xdma_err(xdev, "ioremap failed");
89917ce2522SLizhi Hou 		goto failed;
90017ce2522SLizhi Hou 	}
90117ce2522SLizhi Hou 
90217ce2522SLizhi Hou 	xdev->rmap = devm_regmap_init_mmio(&pdev->dev, reg_base,
90317ce2522SLizhi Hou 					   &xdma_regmap_config);
90417ce2522SLizhi Hou 	if (!xdev->rmap) {
90517ce2522SLizhi Hou 		xdma_err(xdev, "config regmap failed: %d", ret);
90617ce2522SLizhi Hou 		goto failed;
90717ce2522SLizhi Hou 	}
90817ce2522SLizhi Hou 	INIT_LIST_HEAD(&xdev->dma_dev.channels);
90917ce2522SLizhi Hou 
91017ce2522SLizhi Hou 	ret = xdma_alloc_channels(xdev, DMA_MEM_TO_DEV);
91117ce2522SLizhi Hou 	if (ret) {
91217ce2522SLizhi Hou 		xdma_err(xdev, "config H2C channels failed: %d", ret);
91317ce2522SLizhi Hou 		goto failed;
91417ce2522SLizhi Hou 	}
91517ce2522SLizhi Hou 
91617ce2522SLizhi Hou 	ret = xdma_alloc_channels(xdev, DMA_DEV_TO_MEM);
91717ce2522SLizhi Hou 	if (ret) {
91817ce2522SLizhi Hou 		xdma_err(xdev, "config C2H channels failed: %d", ret);
91917ce2522SLizhi Hou 		goto failed;
92017ce2522SLizhi Hou 	}
92117ce2522SLizhi Hou 
92217ce2522SLizhi Hou 	dma_cap_set(DMA_SLAVE, xdev->dma_dev.cap_mask);
92317ce2522SLizhi Hou 	dma_cap_set(DMA_PRIVATE, xdev->dma_dev.cap_mask);
92417ce2522SLizhi Hou 
92517ce2522SLizhi Hou 	xdev->dma_dev.dev = &pdev->dev;
92617ce2522SLizhi Hou 	xdev->dma_dev.device_free_chan_resources = xdma_free_chan_resources;
92717ce2522SLizhi Hou 	xdev->dma_dev.device_alloc_chan_resources = xdma_alloc_chan_resources;
92817ce2522SLizhi Hou 	xdev->dma_dev.device_tx_status = dma_cookie_status;
92917ce2522SLizhi Hou 	xdev->dma_dev.device_prep_slave_sg = xdma_prep_device_sg;
93017ce2522SLizhi Hou 	xdev->dma_dev.device_config = xdma_device_config;
93117ce2522SLizhi Hou 	xdev->dma_dev.device_issue_pending = xdma_issue_pending;
93217ce2522SLizhi Hou 	xdev->dma_dev.filter.map = pdata->device_map;
93317ce2522SLizhi Hou 	xdev->dma_dev.filter.mapcnt = pdata->device_map_cnt;
93417ce2522SLizhi Hou 	xdev->dma_dev.filter.fn = xdma_filter_fn;
93517ce2522SLizhi Hou 
93617ce2522SLizhi Hou 	ret = dma_async_device_register(&xdev->dma_dev);
93717ce2522SLizhi Hou 	if (ret) {
93817ce2522SLizhi Hou 		xdma_err(xdev, "failed to register Xilinx XDMA: %d", ret);
93917ce2522SLizhi Hou 		goto failed;
94017ce2522SLizhi Hou 	}
94117ce2522SLizhi Hou 	xdev->status |= XDMA_DEV_STATUS_REG_DMA;
94217ce2522SLizhi Hou 
94317ce2522SLizhi Hou 	ret = xdma_irq_init(xdev);
94417ce2522SLizhi Hou 	if (ret) {
94517ce2522SLizhi Hou 		xdma_err(xdev, "failed to init msix: %d", ret);
94617ce2522SLizhi Hou 		goto failed;
94717ce2522SLizhi Hou 	}
94817ce2522SLizhi Hou 	xdev->status |= XDMA_DEV_STATUS_INIT_MSIX;
94917ce2522SLizhi Hou 
95017ce2522SLizhi Hou 	return 0;
95117ce2522SLizhi Hou 
95217ce2522SLizhi Hou failed:
95317ce2522SLizhi Hou 	xdma_remove(pdev);
95417ce2522SLizhi Hou 
95517ce2522SLizhi Hou 	return ret;
95617ce2522SLizhi Hou }
95717ce2522SLizhi Hou 
95817ce2522SLizhi Hou static const struct platform_device_id xdma_id_table[] = {
95917ce2522SLizhi Hou 	{ "xdma", 0},
96017ce2522SLizhi Hou 	{ },
96117ce2522SLizhi Hou };
96217ce2522SLizhi Hou 
96317ce2522SLizhi Hou static struct platform_driver xdma_driver = {
96417ce2522SLizhi Hou 	.driver		= {
96517ce2522SLizhi Hou 		.name = "xdma",
96617ce2522SLizhi Hou 	},
96717ce2522SLizhi Hou 	.id_table	= xdma_id_table,
96817ce2522SLizhi Hou 	.probe		= xdma_probe,
96917ce2522SLizhi Hou 	.remove		= xdma_remove,
97017ce2522SLizhi Hou };
97117ce2522SLizhi Hou 
97217ce2522SLizhi Hou module_platform_driver(xdma_driver);
97317ce2522SLizhi Hou 
97417ce2522SLizhi Hou MODULE_DESCRIPTION("AMD XDMA driver");
97517ce2522SLizhi Hou MODULE_AUTHOR("XRT Team <runtimeca39d@amd.com>");
97617ce2522SLizhi Hou MODULE_LICENSE("GPL");
977