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