162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
462306a36Sopenharmony_ci * Synopsys DesignWare eDMA core driver
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/dmaengine.h>
1362306a36Sopenharmony_ci#include <linux/err.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/irq.h>
1662306a36Sopenharmony_ci#include <linux/dma/edma.h>
1762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "dw-edma-core.h"
2062306a36Sopenharmony_ci#include "dw-edma-v0-core.h"
2162306a36Sopenharmony_ci#include "dw-hdma-v0-core.h"
2262306a36Sopenharmony_ci#include "../dmaengine.h"
2362306a36Sopenharmony_ci#include "../virt-dma.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic inline
2662306a36Sopenharmony_cistruct device *dchan2dev(struct dma_chan *dchan)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	return &dchan->dev->device;
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic inline
3262306a36Sopenharmony_cistruct device *chan2dev(struct dw_edma_chan *chan)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	return &chan->vc.chan.dev->device;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic inline
3862306a36Sopenharmony_cistruct dw_edma_desc *vd2dw_edma_desc(struct virt_dma_desc *vd)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	return container_of(vd, struct dw_edma_desc, vd);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic inline
4462306a36Sopenharmony_ciu64 dw_edma_get_pci_address(struct dw_edma_chan *chan, phys_addr_t cpu_addr)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct dw_edma_chip *chip = chan->dw->chip;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (chip->ops->pci_address)
4962306a36Sopenharmony_ci		return chip->ops->pci_address(chip->dev, cpu_addr);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	return cpu_addr;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic struct dw_edma_burst *dw_edma_alloc_burst(struct dw_edma_chunk *chunk)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct dw_edma_burst *burst;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	burst = kzalloc(sizeof(*burst), GFP_NOWAIT);
5962306a36Sopenharmony_ci	if (unlikely(!burst))
6062306a36Sopenharmony_ci		return NULL;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	INIT_LIST_HEAD(&burst->list);
6362306a36Sopenharmony_ci	if (chunk->burst) {
6462306a36Sopenharmony_ci		/* Create and add new element into the linked list */
6562306a36Sopenharmony_ci		chunk->bursts_alloc++;
6662306a36Sopenharmony_ci		list_add_tail(&burst->list, &chunk->burst->list);
6762306a36Sopenharmony_ci	} else {
6862306a36Sopenharmony_ci		/* List head */
6962306a36Sopenharmony_ci		chunk->bursts_alloc = 0;
7062306a36Sopenharmony_ci		chunk->burst = burst;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return burst;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct dw_edma_chip *chip = desc->chan->dw->chip;
7962306a36Sopenharmony_ci	struct dw_edma_chan *chan = desc->chan;
8062306a36Sopenharmony_ci	struct dw_edma_chunk *chunk;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	chunk = kzalloc(sizeof(*chunk), GFP_NOWAIT);
8362306a36Sopenharmony_ci	if (unlikely(!chunk))
8462306a36Sopenharmony_ci		return NULL;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	INIT_LIST_HEAD(&chunk->list);
8762306a36Sopenharmony_ci	chunk->chan = chan;
8862306a36Sopenharmony_ci	/* Toggling change bit (CB) in each chunk, this is a mechanism to
8962306a36Sopenharmony_ci	 * inform the eDMA HW block that this is a new linked list ready
9062306a36Sopenharmony_ci	 * to be consumed.
9162306a36Sopenharmony_ci	 *  - Odd chunks originate CB equal to 0
9262306a36Sopenharmony_ci	 *  - Even chunks originate CB equal to 1
9362306a36Sopenharmony_ci	 */
9462306a36Sopenharmony_ci	chunk->cb = !(desc->chunks_alloc % 2);
9562306a36Sopenharmony_ci	if (chan->dir == EDMA_DIR_WRITE) {
9662306a36Sopenharmony_ci		chunk->ll_region.paddr = chip->ll_region_wr[chan->id].paddr;
9762306a36Sopenharmony_ci		chunk->ll_region.vaddr = chip->ll_region_wr[chan->id].vaddr;
9862306a36Sopenharmony_ci	} else {
9962306a36Sopenharmony_ci		chunk->ll_region.paddr = chip->ll_region_rd[chan->id].paddr;
10062306a36Sopenharmony_ci		chunk->ll_region.vaddr = chip->ll_region_rd[chan->id].vaddr;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (desc->chunk) {
10462306a36Sopenharmony_ci		/* Create and add new element into the linked list */
10562306a36Sopenharmony_ci		if (!dw_edma_alloc_burst(chunk)) {
10662306a36Sopenharmony_ci			kfree(chunk);
10762306a36Sopenharmony_ci			return NULL;
10862306a36Sopenharmony_ci		}
10962306a36Sopenharmony_ci		desc->chunks_alloc++;
11062306a36Sopenharmony_ci		list_add_tail(&chunk->list, &desc->chunk->list);
11162306a36Sopenharmony_ci	} else {
11262306a36Sopenharmony_ci		/* List head */
11362306a36Sopenharmony_ci		chunk->burst = NULL;
11462306a36Sopenharmony_ci		desc->chunks_alloc = 0;
11562306a36Sopenharmony_ci		desc->chunk = chunk;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	return chunk;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic struct dw_edma_desc *dw_edma_alloc_desc(struct dw_edma_chan *chan)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct dw_edma_desc *desc;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
12662306a36Sopenharmony_ci	if (unlikely(!desc))
12762306a36Sopenharmony_ci		return NULL;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	desc->chan = chan;
13062306a36Sopenharmony_ci	if (!dw_edma_alloc_chunk(desc)) {
13162306a36Sopenharmony_ci		kfree(desc);
13262306a36Sopenharmony_ci		return NULL;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return desc;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void dw_edma_free_burst(struct dw_edma_chunk *chunk)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct dw_edma_burst *child, *_next;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* Remove all the list elements */
14362306a36Sopenharmony_ci	list_for_each_entry_safe(child, _next, &chunk->burst->list, list) {
14462306a36Sopenharmony_ci		list_del(&child->list);
14562306a36Sopenharmony_ci		kfree(child);
14662306a36Sopenharmony_ci		chunk->bursts_alloc--;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* Remove the list head */
15062306a36Sopenharmony_ci	kfree(child);
15162306a36Sopenharmony_ci	chunk->burst = NULL;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void dw_edma_free_chunk(struct dw_edma_desc *desc)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct dw_edma_chunk *child, *_next;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (!desc->chunk)
15962306a36Sopenharmony_ci		return;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* Remove all the list elements */
16262306a36Sopenharmony_ci	list_for_each_entry_safe(child, _next, &desc->chunk->list, list) {
16362306a36Sopenharmony_ci		dw_edma_free_burst(child);
16462306a36Sopenharmony_ci		list_del(&child->list);
16562306a36Sopenharmony_ci		kfree(child);
16662306a36Sopenharmony_ci		desc->chunks_alloc--;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/* Remove the list head */
17062306a36Sopenharmony_ci	kfree(child);
17162306a36Sopenharmony_ci	desc->chunk = NULL;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic void dw_edma_free_desc(struct dw_edma_desc *desc)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	dw_edma_free_chunk(desc);
17762306a36Sopenharmony_ci	kfree(desc);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic void vchan_free_desc(struct virt_dma_desc *vdesc)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	dw_edma_free_desc(vd2dw_edma_desc(vdesc));
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic int dw_edma_start_transfer(struct dw_edma_chan *chan)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct dw_edma *dw = chan->dw;
18862306a36Sopenharmony_ci	struct dw_edma_chunk *child;
18962306a36Sopenharmony_ci	struct dw_edma_desc *desc;
19062306a36Sopenharmony_ci	struct virt_dma_desc *vd;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	vd = vchan_next_desc(&chan->vc);
19362306a36Sopenharmony_ci	if (!vd)
19462306a36Sopenharmony_ci		return 0;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	desc = vd2dw_edma_desc(vd);
19762306a36Sopenharmony_ci	if (!desc)
19862306a36Sopenharmony_ci		return 0;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	child = list_first_entry_or_null(&desc->chunk->list,
20162306a36Sopenharmony_ci					 struct dw_edma_chunk, list);
20262306a36Sopenharmony_ci	if (!child)
20362306a36Sopenharmony_ci		return 0;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	dw_edma_core_start(dw, child, !desc->xfer_sz);
20662306a36Sopenharmony_ci	desc->xfer_sz += child->ll_region.sz;
20762306a36Sopenharmony_ci	dw_edma_free_burst(child);
20862306a36Sopenharmony_ci	list_del(&child->list);
20962306a36Sopenharmony_ci	kfree(child);
21062306a36Sopenharmony_ci	desc->chunks_alloc--;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	return 1;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic void dw_edma_device_caps(struct dma_chan *dchan,
21662306a36Sopenharmony_ci				struct dma_slave_caps *caps)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
22162306a36Sopenharmony_ci		if (chan->dir == EDMA_DIR_READ)
22262306a36Sopenharmony_ci			caps->directions = BIT(DMA_DEV_TO_MEM);
22362306a36Sopenharmony_ci		else
22462306a36Sopenharmony_ci			caps->directions = BIT(DMA_MEM_TO_DEV);
22562306a36Sopenharmony_ci	} else {
22662306a36Sopenharmony_ci		if (chan->dir == EDMA_DIR_WRITE)
22762306a36Sopenharmony_ci			caps->directions = BIT(DMA_DEV_TO_MEM);
22862306a36Sopenharmony_ci		else
22962306a36Sopenharmony_ci			caps->directions = BIT(DMA_MEM_TO_DEV);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int dw_edma_device_config(struct dma_chan *dchan,
23462306a36Sopenharmony_ci				 struct dma_slave_config *config)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	memcpy(&chan->config, config, sizeof(*config));
23962306a36Sopenharmony_ci	chan->configured = true;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	return 0;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic int dw_edma_device_pause(struct dma_chan *dchan)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
24762306a36Sopenharmony_ci	int err = 0;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (!chan->configured)
25062306a36Sopenharmony_ci		err = -EPERM;
25162306a36Sopenharmony_ci	else if (chan->status != EDMA_ST_BUSY)
25262306a36Sopenharmony_ci		err = -EPERM;
25362306a36Sopenharmony_ci	else if (chan->request != EDMA_REQ_NONE)
25462306a36Sopenharmony_ci		err = -EPERM;
25562306a36Sopenharmony_ci	else
25662306a36Sopenharmony_ci		chan->request = EDMA_REQ_PAUSE;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	return err;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic int dw_edma_device_resume(struct dma_chan *dchan)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
26462306a36Sopenharmony_ci	int err = 0;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (!chan->configured) {
26762306a36Sopenharmony_ci		err = -EPERM;
26862306a36Sopenharmony_ci	} else if (chan->status != EDMA_ST_PAUSE) {
26962306a36Sopenharmony_ci		err = -EPERM;
27062306a36Sopenharmony_ci	} else if (chan->request != EDMA_REQ_NONE) {
27162306a36Sopenharmony_ci		err = -EPERM;
27262306a36Sopenharmony_ci	} else {
27362306a36Sopenharmony_ci		chan->status = EDMA_ST_BUSY;
27462306a36Sopenharmony_ci		dw_edma_start_transfer(chan);
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return err;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic int dw_edma_device_terminate_all(struct dma_chan *dchan)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
28362306a36Sopenharmony_ci	int err = 0;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (!chan->configured) {
28662306a36Sopenharmony_ci		/* Do nothing */
28762306a36Sopenharmony_ci	} else if (chan->status == EDMA_ST_PAUSE) {
28862306a36Sopenharmony_ci		chan->status = EDMA_ST_IDLE;
28962306a36Sopenharmony_ci		chan->configured = false;
29062306a36Sopenharmony_ci	} else if (chan->status == EDMA_ST_IDLE) {
29162306a36Sopenharmony_ci		chan->configured = false;
29262306a36Sopenharmony_ci	} else if (dw_edma_core_ch_status(chan) == DMA_COMPLETE) {
29362306a36Sopenharmony_ci		/*
29462306a36Sopenharmony_ci		 * The channel is in a false BUSY state, probably didn't
29562306a36Sopenharmony_ci		 * receive or lost an interrupt
29662306a36Sopenharmony_ci		 */
29762306a36Sopenharmony_ci		chan->status = EDMA_ST_IDLE;
29862306a36Sopenharmony_ci		chan->configured = false;
29962306a36Sopenharmony_ci	} else if (chan->request > EDMA_REQ_PAUSE) {
30062306a36Sopenharmony_ci		err = -EPERM;
30162306a36Sopenharmony_ci	} else {
30262306a36Sopenharmony_ci		chan->request = EDMA_REQ_STOP;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	return err;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void dw_edma_device_issue_pending(struct dma_chan *dchan)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
31162306a36Sopenharmony_ci	unsigned long flags;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (!chan->configured)
31462306a36Sopenharmony_ci		return;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	spin_lock_irqsave(&chan->vc.lock, flags);
31762306a36Sopenharmony_ci	if (vchan_issue_pending(&chan->vc) && chan->request == EDMA_REQ_NONE &&
31862306a36Sopenharmony_ci	    chan->status == EDMA_ST_IDLE) {
31962306a36Sopenharmony_ci		chan->status = EDMA_ST_BUSY;
32062306a36Sopenharmony_ci		dw_edma_start_transfer(chan);
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->vc.lock, flags);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic enum dma_status
32662306a36Sopenharmony_cidw_edma_device_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
32762306a36Sopenharmony_ci			 struct dma_tx_state *txstate)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
33062306a36Sopenharmony_ci	struct dw_edma_desc *desc;
33162306a36Sopenharmony_ci	struct virt_dma_desc *vd;
33262306a36Sopenharmony_ci	unsigned long flags;
33362306a36Sopenharmony_ci	enum dma_status ret;
33462306a36Sopenharmony_ci	u32 residue = 0;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	ret = dma_cookie_status(dchan, cookie, txstate);
33762306a36Sopenharmony_ci	if (ret == DMA_COMPLETE)
33862306a36Sopenharmony_ci		return ret;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (ret == DMA_IN_PROGRESS && chan->status == EDMA_ST_PAUSE)
34162306a36Sopenharmony_ci		ret = DMA_PAUSED;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (!txstate)
34462306a36Sopenharmony_ci		goto ret_residue;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	spin_lock_irqsave(&chan->vc.lock, flags);
34762306a36Sopenharmony_ci	vd = vchan_find_desc(&chan->vc, cookie);
34862306a36Sopenharmony_ci	if (vd) {
34962306a36Sopenharmony_ci		desc = vd2dw_edma_desc(vd);
35062306a36Sopenharmony_ci		if (desc)
35162306a36Sopenharmony_ci			residue = desc->alloc_sz - desc->xfer_sz;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->vc.lock, flags);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ciret_residue:
35662306a36Sopenharmony_ci	dma_set_residue(txstate, residue);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	return ret;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
36262306a36Sopenharmony_cidw_edma_device_transfer(struct dw_edma_transfer *xfer)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
36562306a36Sopenharmony_ci	enum dma_transfer_direction dir = xfer->direction;
36662306a36Sopenharmony_ci	struct scatterlist *sg = NULL;
36762306a36Sopenharmony_ci	struct dw_edma_chunk *chunk;
36862306a36Sopenharmony_ci	struct dw_edma_burst *burst;
36962306a36Sopenharmony_ci	struct dw_edma_desc *desc;
37062306a36Sopenharmony_ci	u64 src_addr, dst_addr;
37162306a36Sopenharmony_ci	size_t fsz = 0;
37262306a36Sopenharmony_ci	u32 cnt = 0;
37362306a36Sopenharmony_ci	int i;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (!chan->configured)
37662306a36Sopenharmony_ci		return NULL;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/*
37962306a36Sopenharmony_ci	 * Local Root Port/End-point              Remote End-point
38062306a36Sopenharmony_ci	 * +-----------------------+ PCIe bus +----------------------+
38162306a36Sopenharmony_ci	 * |                       |    +-+   |                      |
38262306a36Sopenharmony_ci	 * |    DEV_TO_MEM   Rx Ch <----+ +---+ Tx Ch  DEV_TO_MEM    |
38362306a36Sopenharmony_ci	 * |                       |    | |   |                      |
38462306a36Sopenharmony_ci	 * |    MEM_TO_DEV   Tx Ch +----+ +---> Rx Ch  MEM_TO_DEV    |
38562306a36Sopenharmony_ci	 * |                       |    +-+   |                      |
38662306a36Sopenharmony_ci	 * +-----------------------+          +----------------------+
38762306a36Sopenharmony_ci	 *
38862306a36Sopenharmony_ci	 * 1. Normal logic:
38962306a36Sopenharmony_ci	 * If eDMA is embedded into the DW PCIe RP/EP and controlled from the
39062306a36Sopenharmony_ci	 * CPU/Application side, the Rx channel (EDMA_DIR_READ) will be used
39162306a36Sopenharmony_ci	 * for the device read operations (DEV_TO_MEM) and the Tx channel
39262306a36Sopenharmony_ci	 * (EDMA_DIR_WRITE) - for the write operations (MEM_TO_DEV).
39362306a36Sopenharmony_ci	 *
39462306a36Sopenharmony_ci	 * 2. Inverted logic:
39562306a36Sopenharmony_ci	 * If eDMA is embedded into a Remote PCIe EP and is controlled by the
39662306a36Sopenharmony_ci	 * MWr/MRd TLPs sent from the CPU's PCIe host controller, the Tx
39762306a36Sopenharmony_ci	 * channel (EDMA_DIR_WRITE) will be used for the device read operations
39862306a36Sopenharmony_ci	 * (DEV_TO_MEM) and the Rx channel (EDMA_DIR_READ) - for the write
39962306a36Sopenharmony_ci	 * operations (MEM_TO_DEV).
40062306a36Sopenharmony_ci	 *
40162306a36Sopenharmony_ci	 * It is the client driver responsibility to choose a proper channel
40262306a36Sopenharmony_ci	 * for the DMA transfers.
40362306a36Sopenharmony_ci	 */
40462306a36Sopenharmony_ci	if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
40562306a36Sopenharmony_ci		if ((chan->dir == EDMA_DIR_READ && dir != DMA_DEV_TO_MEM) ||
40662306a36Sopenharmony_ci		    (chan->dir == EDMA_DIR_WRITE && dir != DMA_MEM_TO_DEV))
40762306a36Sopenharmony_ci			return NULL;
40862306a36Sopenharmony_ci	} else {
40962306a36Sopenharmony_ci		if ((chan->dir == EDMA_DIR_WRITE && dir != DMA_DEV_TO_MEM) ||
41062306a36Sopenharmony_ci		    (chan->dir == EDMA_DIR_READ && dir != DMA_MEM_TO_DEV))
41162306a36Sopenharmony_ci			return NULL;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (xfer->type == EDMA_XFER_CYCLIC) {
41562306a36Sopenharmony_ci		if (!xfer->xfer.cyclic.len || !xfer->xfer.cyclic.cnt)
41662306a36Sopenharmony_ci			return NULL;
41762306a36Sopenharmony_ci	} else if (xfer->type == EDMA_XFER_SCATTER_GATHER) {
41862306a36Sopenharmony_ci		if (xfer->xfer.sg.len < 1)
41962306a36Sopenharmony_ci			return NULL;
42062306a36Sopenharmony_ci	} else if (xfer->type == EDMA_XFER_INTERLEAVED) {
42162306a36Sopenharmony_ci		if (!xfer->xfer.il->numf || xfer->xfer.il->frame_size < 1)
42262306a36Sopenharmony_ci			return NULL;
42362306a36Sopenharmony_ci		if (!xfer->xfer.il->src_inc || !xfer->xfer.il->dst_inc)
42462306a36Sopenharmony_ci			return NULL;
42562306a36Sopenharmony_ci	} else {
42662306a36Sopenharmony_ci		return NULL;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	desc = dw_edma_alloc_desc(chan);
43062306a36Sopenharmony_ci	if (unlikely(!desc))
43162306a36Sopenharmony_ci		goto err_alloc;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	chunk = dw_edma_alloc_chunk(desc);
43462306a36Sopenharmony_ci	if (unlikely(!chunk))
43562306a36Sopenharmony_ci		goto err_alloc;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (xfer->type == EDMA_XFER_INTERLEAVED) {
43862306a36Sopenharmony_ci		src_addr = xfer->xfer.il->src_start;
43962306a36Sopenharmony_ci		dst_addr = xfer->xfer.il->dst_start;
44062306a36Sopenharmony_ci	} else {
44162306a36Sopenharmony_ci		src_addr = chan->config.src_addr;
44262306a36Sopenharmony_ci		dst_addr = chan->config.dst_addr;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (dir == DMA_DEV_TO_MEM)
44662306a36Sopenharmony_ci		src_addr = dw_edma_get_pci_address(chan, (phys_addr_t)src_addr);
44762306a36Sopenharmony_ci	else
44862306a36Sopenharmony_ci		dst_addr = dw_edma_get_pci_address(chan, (phys_addr_t)dst_addr);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (xfer->type == EDMA_XFER_CYCLIC) {
45162306a36Sopenharmony_ci		cnt = xfer->xfer.cyclic.cnt;
45262306a36Sopenharmony_ci	} else if (xfer->type == EDMA_XFER_SCATTER_GATHER) {
45362306a36Sopenharmony_ci		cnt = xfer->xfer.sg.len;
45462306a36Sopenharmony_ci		sg = xfer->xfer.sg.sgl;
45562306a36Sopenharmony_ci	} else if (xfer->type == EDMA_XFER_INTERLEAVED) {
45662306a36Sopenharmony_ci		cnt = xfer->xfer.il->numf * xfer->xfer.il->frame_size;
45762306a36Sopenharmony_ci		fsz = xfer->xfer.il->frame_size;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	for (i = 0; i < cnt; i++) {
46162306a36Sopenharmony_ci		if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg)
46262306a36Sopenharmony_ci			break;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		if (chunk->bursts_alloc == chan->ll_max) {
46562306a36Sopenharmony_ci			chunk = dw_edma_alloc_chunk(desc);
46662306a36Sopenharmony_ci			if (unlikely(!chunk))
46762306a36Sopenharmony_ci				goto err_alloc;
46862306a36Sopenharmony_ci		}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		burst = dw_edma_alloc_burst(chunk);
47162306a36Sopenharmony_ci		if (unlikely(!burst))
47262306a36Sopenharmony_ci			goto err_alloc;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		if (xfer->type == EDMA_XFER_CYCLIC)
47562306a36Sopenharmony_ci			burst->sz = xfer->xfer.cyclic.len;
47662306a36Sopenharmony_ci		else if (xfer->type == EDMA_XFER_SCATTER_GATHER)
47762306a36Sopenharmony_ci			burst->sz = sg_dma_len(sg);
47862306a36Sopenharmony_ci		else if (xfer->type == EDMA_XFER_INTERLEAVED)
47962306a36Sopenharmony_ci			burst->sz = xfer->xfer.il->sgl[i % fsz].size;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		chunk->ll_region.sz += burst->sz;
48262306a36Sopenharmony_ci		desc->alloc_sz += burst->sz;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		if (dir == DMA_DEV_TO_MEM) {
48562306a36Sopenharmony_ci			burst->sar = src_addr;
48662306a36Sopenharmony_ci			if (xfer->type == EDMA_XFER_CYCLIC) {
48762306a36Sopenharmony_ci				burst->dar = xfer->xfer.cyclic.paddr;
48862306a36Sopenharmony_ci			} else if (xfer->type == EDMA_XFER_SCATTER_GATHER) {
48962306a36Sopenharmony_ci				src_addr += sg_dma_len(sg);
49062306a36Sopenharmony_ci				burst->dar = sg_dma_address(sg);
49162306a36Sopenharmony_ci				/* Unlike the typical assumption by other
49262306a36Sopenharmony_ci				 * drivers/IPs the peripheral memory isn't
49362306a36Sopenharmony_ci				 * a FIFO memory, in this case, it's a
49462306a36Sopenharmony_ci				 * linear memory and that why the source
49562306a36Sopenharmony_ci				 * and destination addresses are increased
49662306a36Sopenharmony_ci				 * by the same portion (data length)
49762306a36Sopenharmony_ci				 */
49862306a36Sopenharmony_ci			} else if (xfer->type == EDMA_XFER_INTERLEAVED) {
49962306a36Sopenharmony_ci				burst->dar = dst_addr;
50062306a36Sopenharmony_ci			}
50162306a36Sopenharmony_ci		} else {
50262306a36Sopenharmony_ci			burst->dar = dst_addr;
50362306a36Sopenharmony_ci			if (xfer->type == EDMA_XFER_CYCLIC) {
50462306a36Sopenharmony_ci				burst->sar = xfer->xfer.cyclic.paddr;
50562306a36Sopenharmony_ci			} else if (xfer->type == EDMA_XFER_SCATTER_GATHER) {
50662306a36Sopenharmony_ci				dst_addr += sg_dma_len(sg);
50762306a36Sopenharmony_ci				burst->sar = sg_dma_address(sg);
50862306a36Sopenharmony_ci				/* Unlike the typical assumption by other
50962306a36Sopenharmony_ci				 * drivers/IPs the peripheral memory isn't
51062306a36Sopenharmony_ci				 * a FIFO memory, in this case, it's a
51162306a36Sopenharmony_ci				 * linear memory and that why the source
51262306a36Sopenharmony_ci				 * and destination addresses are increased
51362306a36Sopenharmony_ci				 * by the same portion (data length)
51462306a36Sopenharmony_ci				 */
51562306a36Sopenharmony_ci			}  else if (xfer->type == EDMA_XFER_INTERLEAVED) {
51662306a36Sopenharmony_ci				burst->sar = src_addr;
51762306a36Sopenharmony_ci			}
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		if (xfer->type == EDMA_XFER_SCATTER_GATHER) {
52162306a36Sopenharmony_ci			sg = sg_next(sg);
52262306a36Sopenharmony_ci		} else if (xfer->type == EDMA_XFER_INTERLEAVED) {
52362306a36Sopenharmony_ci			struct dma_interleaved_template *il = xfer->xfer.il;
52462306a36Sopenharmony_ci			struct data_chunk *dc = &il->sgl[i % fsz];
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci			src_addr += burst->sz;
52762306a36Sopenharmony_ci			if (il->src_sgl)
52862306a36Sopenharmony_ci				src_addr += dmaengine_get_src_icg(il, dc);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci			dst_addr += burst->sz;
53162306a36Sopenharmony_ci			if (il->dst_sgl)
53262306a36Sopenharmony_ci				dst_addr += dmaengine_get_dst_icg(il, dc);
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return vchan_tx_prep(&chan->vc, &desc->vd, xfer->flags);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cierr_alloc:
53962306a36Sopenharmony_ci	if (desc)
54062306a36Sopenharmony_ci		dw_edma_free_desc(desc);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	return NULL;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
54662306a36Sopenharmony_cidw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
54762306a36Sopenharmony_ci			     unsigned int len,
54862306a36Sopenharmony_ci			     enum dma_transfer_direction direction,
54962306a36Sopenharmony_ci			     unsigned long flags, void *context)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct dw_edma_transfer xfer;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	xfer.dchan = dchan;
55462306a36Sopenharmony_ci	xfer.direction = direction;
55562306a36Sopenharmony_ci	xfer.xfer.sg.sgl = sgl;
55662306a36Sopenharmony_ci	xfer.xfer.sg.len = len;
55762306a36Sopenharmony_ci	xfer.flags = flags;
55862306a36Sopenharmony_ci	xfer.type = EDMA_XFER_SCATTER_GATHER;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return dw_edma_device_transfer(&xfer);
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
56462306a36Sopenharmony_cidw_edma_device_prep_dma_cyclic(struct dma_chan *dchan, dma_addr_t paddr,
56562306a36Sopenharmony_ci			       size_t len, size_t count,
56662306a36Sopenharmony_ci			       enum dma_transfer_direction direction,
56762306a36Sopenharmony_ci			       unsigned long flags)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	struct dw_edma_transfer xfer;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	xfer.dchan = dchan;
57262306a36Sopenharmony_ci	xfer.direction = direction;
57362306a36Sopenharmony_ci	xfer.xfer.cyclic.paddr = paddr;
57462306a36Sopenharmony_ci	xfer.xfer.cyclic.len = len;
57562306a36Sopenharmony_ci	xfer.xfer.cyclic.cnt = count;
57662306a36Sopenharmony_ci	xfer.flags = flags;
57762306a36Sopenharmony_ci	xfer.type = EDMA_XFER_CYCLIC;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	return dw_edma_device_transfer(&xfer);
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
58362306a36Sopenharmony_cidw_edma_device_prep_interleaved_dma(struct dma_chan *dchan,
58462306a36Sopenharmony_ci				    struct dma_interleaved_template *ilt,
58562306a36Sopenharmony_ci				    unsigned long flags)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	struct dw_edma_transfer xfer;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	xfer.dchan = dchan;
59062306a36Sopenharmony_ci	xfer.direction = ilt->dir;
59162306a36Sopenharmony_ci	xfer.xfer.il = ilt;
59262306a36Sopenharmony_ci	xfer.flags = flags;
59362306a36Sopenharmony_ci	xfer.type = EDMA_XFER_INTERLEAVED;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	return dw_edma_device_transfer(&xfer);
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic void dw_edma_done_interrupt(struct dw_edma_chan *chan)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct dw_edma_desc *desc;
60162306a36Sopenharmony_ci	struct virt_dma_desc *vd;
60262306a36Sopenharmony_ci	unsigned long flags;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	spin_lock_irqsave(&chan->vc.lock, flags);
60562306a36Sopenharmony_ci	vd = vchan_next_desc(&chan->vc);
60662306a36Sopenharmony_ci	if (vd) {
60762306a36Sopenharmony_ci		switch (chan->request) {
60862306a36Sopenharmony_ci		case EDMA_REQ_NONE:
60962306a36Sopenharmony_ci			desc = vd2dw_edma_desc(vd);
61062306a36Sopenharmony_ci			if (!desc->chunks_alloc) {
61162306a36Sopenharmony_ci				list_del(&vd->node);
61262306a36Sopenharmony_ci				vchan_cookie_complete(vd);
61362306a36Sopenharmony_ci			}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci			/* Continue transferring if there are remaining chunks or issued requests.
61662306a36Sopenharmony_ci			 */
61762306a36Sopenharmony_ci			chan->status = dw_edma_start_transfer(chan) ? EDMA_ST_BUSY : EDMA_ST_IDLE;
61862306a36Sopenharmony_ci			break;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		case EDMA_REQ_STOP:
62162306a36Sopenharmony_ci			list_del(&vd->node);
62262306a36Sopenharmony_ci			vchan_cookie_complete(vd);
62362306a36Sopenharmony_ci			chan->request = EDMA_REQ_NONE;
62462306a36Sopenharmony_ci			chan->status = EDMA_ST_IDLE;
62562306a36Sopenharmony_ci			break;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci		case EDMA_REQ_PAUSE:
62862306a36Sopenharmony_ci			chan->request = EDMA_REQ_NONE;
62962306a36Sopenharmony_ci			chan->status = EDMA_ST_PAUSE;
63062306a36Sopenharmony_ci			break;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci		default:
63362306a36Sopenharmony_ci			break;
63462306a36Sopenharmony_ci		}
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->vc.lock, flags);
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct virt_dma_desc *vd;
64262306a36Sopenharmony_ci	unsigned long flags;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	spin_lock_irqsave(&chan->vc.lock, flags);
64562306a36Sopenharmony_ci	vd = vchan_next_desc(&chan->vc);
64662306a36Sopenharmony_ci	if (vd) {
64762306a36Sopenharmony_ci		list_del(&vd->node);
64862306a36Sopenharmony_ci		vchan_cookie_complete(vd);
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->vc.lock, flags);
65162306a36Sopenharmony_ci	chan->request = EDMA_REQ_NONE;
65262306a36Sopenharmony_ci	chan->status = EDMA_ST_IDLE;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	struct dw_edma_irq *dw_irq = data;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	return dw_edma_core_handle_int(dw_irq, EDMA_DIR_WRITE,
66062306a36Sopenharmony_ci				       dw_edma_done_interrupt,
66162306a36Sopenharmony_ci				       dw_edma_abort_interrupt);
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct dw_edma_irq *dw_irq = data;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	return dw_edma_core_handle_int(dw_irq, EDMA_DIR_READ,
66962306a36Sopenharmony_ci				       dw_edma_done_interrupt,
67062306a36Sopenharmony_ci				       dw_edma_abort_interrupt);
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic irqreturn_t dw_edma_interrupt_common(int irq, void *data)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	ret |= dw_edma_interrupt_write(irq, data);
67862306a36Sopenharmony_ci	ret |= dw_edma_interrupt_read(irq, data);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	return ret;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic int dw_edma_alloc_chan_resources(struct dma_chan *dchan)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (chan->status != EDMA_ST_IDLE)
68862306a36Sopenharmony_ci		return -EBUSY;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	return 0;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic void dw_edma_free_chan_resources(struct dma_chan *dchan)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(5000);
69662306a36Sopenharmony_ci	int ret;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
69962306a36Sopenharmony_ci		ret = dw_edma_device_terminate_all(dchan);
70062306a36Sopenharmony_ci		if (!ret)
70162306a36Sopenharmony_ci			break;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci		if (time_after_eq(jiffies, timeout))
70462306a36Sopenharmony_ci			return;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci		cpu_relax();
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	struct dw_edma_chip *chip = dw->chip;
71362306a36Sopenharmony_ci	struct device *dev = chip->dev;
71462306a36Sopenharmony_ci	struct dw_edma_chan *chan;
71562306a36Sopenharmony_ci	struct dw_edma_irq *irq;
71662306a36Sopenharmony_ci	struct dma_device *dma;
71762306a36Sopenharmony_ci	u32 i, ch_cnt;
71862306a36Sopenharmony_ci	u32 pos;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt;
72162306a36Sopenharmony_ci	dma = &dw->dma;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	INIT_LIST_HEAD(&dma->channels);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	for (i = 0; i < ch_cnt; i++) {
72662306a36Sopenharmony_ci		chan = &dw->chan[i];
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		chan->dw = dw;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		if (i < dw->wr_ch_cnt) {
73162306a36Sopenharmony_ci			chan->id = i;
73262306a36Sopenharmony_ci			chan->dir = EDMA_DIR_WRITE;
73362306a36Sopenharmony_ci		} else {
73462306a36Sopenharmony_ci			chan->id = i - dw->wr_ch_cnt;
73562306a36Sopenharmony_ci			chan->dir = EDMA_DIR_READ;
73662306a36Sopenharmony_ci		}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci		chan->configured = false;
73962306a36Sopenharmony_ci		chan->request = EDMA_REQ_NONE;
74062306a36Sopenharmony_ci		chan->status = EDMA_ST_IDLE;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		if (chan->dir == EDMA_DIR_WRITE)
74362306a36Sopenharmony_ci			chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ);
74462306a36Sopenharmony_ci		else
74562306a36Sopenharmony_ci			chan->ll_max = (chip->ll_region_rd[chan->id].sz / EDMA_LL_SZ);
74662306a36Sopenharmony_ci		chan->ll_max -= 1;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci		dev_vdbg(dev, "L. List:\tChannel %s[%u] max_cnt=%u\n",
74962306a36Sopenharmony_ci			 chan->dir == EDMA_DIR_WRITE ? "write" : "read",
75062306a36Sopenharmony_ci			 chan->id, chan->ll_max);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci		if (dw->nr_irqs == 1)
75362306a36Sopenharmony_ci			pos = 0;
75462306a36Sopenharmony_ci		else if (chan->dir == EDMA_DIR_WRITE)
75562306a36Sopenharmony_ci			pos = chan->id % wr_alloc;
75662306a36Sopenharmony_ci		else
75762306a36Sopenharmony_ci			pos = wr_alloc + chan->id % rd_alloc;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci		irq = &dw->irq[pos];
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci		if (chan->dir == EDMA_DIR_WRITE)
76262306a36Sopenharmony_ci			irq->wr_mask |= BIT(chan->id);
76362306a36Sopenharmony_ci		else
76462306a36Sopenharmony_ci			irq->rd_mask |= BIT(chan->id);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci		irq->dw = dw;
76762306a36Sopenharmony_ci		memcpy(&chan->msi, &irq->msi, sizeof(chan->msi));
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		dev_vdbg(dev, "MSI:\t\tChannel %s[%u] addr=0x%.8x%.8x, data=0x%.8x\n",
77062306a36Sopenharmony_ci			 chan->dir == EDMA_DIR_WRITE  ? "write" : "read", chan->id,
77162306a36Sopenharmony_ci			 chan->msi.address_hi, chan->msi.address_lo,
77262306a36Sopenharmony_ci			 chan->msi.data);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci		chan->vc.desc_free = vchan_free_desc;
77562306a36Sopenharmony_ci		chan->vc.chan.private = chan->dir == EDMA_DIR_WRITE ?
77662306a36Sopenharmony_ci					&dw->chip->dt_region_wr[chan->id] :
77762306a36Sopenharmony_ci					&dw->chip->dt_region_rd[chan->id];
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		vchan_init(&chan->vc, dma);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		dw_edma_core_ch_config(chan);
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	/* Set DMA channel capabilities */
78562306a36Sopenharmony_ci	dma_cap_zero(dma->cap_mask);
78662306a36Sopenharmony_ci	dma_cap_set(DMA_SLAVE, dma->cap_mask);
78762306a36Sopenharmony_ci	dma_cap_set(DMA_CYCLIC, dma->cap_mask);
78862306a36Sopenharmony_ci	dma_cap_set(DMA_PRIVATE, dma->cap_mask);
78962306a36Sopenharmony_ci	dma_cap_set(DMA_INTERLEAVE, dma->cap_mask);
79062306a36Sopenharmony_ci	dma->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
79162306a36Sopenharmony_ci	dma->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
79262306a36Sopenharmony_ci	dma->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
79362306a36Sopenharmony_ci	dma->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	/* Set DMA channel callbacks */
79662306a36Sopenharmony_ci	dma->dev = chip->dev;
79762306a36Sopenharmony_ci	dma->device_alloc_chan_resources = dw_edma_alloc_chan_resources;
79862306a36Sopenharmony_ci	dma->device_free_chan_resources = dw_edma_free_chan_resources;
79962306a36Sopenharmony_ci	dma->device_caps = dw_edma_device_caps;
80062306a36Sopenharmony_ci	dma->device_config = dw_edma_device_config;
80162306a36Sopenharmony_ci	dma->device_pause = dw_edma_device_pause;
80262306a36Sopenharmony_ci	dma->device_resume = dw_edma_device_resume;
80362306a36Sopenharmony_ci	dma->device_terminate_all = dw_edma_device_terminate_all;
80462306a36Sopenharmony_ci	dma->device_issue_pending = dw_edma_device_issue_pending;
80562306a36Sopenharmony_ci	dma->device_tx_status = dw_edma_device_tx_status;
80662306a36Sopenharmony_ci	dma->device_prep_slave_sg = dw_edma_device_prep_slave_sg;
80762306a36Sopenharmony_ci	dma->device_prep_dma_cyclic = dw_edma_device_prep_dma_cyclic;
80862306a36Sopenharmony_ci	dma->device_prep_interleaved_dma = dw_edma_device_prep_interleaved_dma;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	dma_set_max_seg_size(dma->dev, U32_MAX);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	/* Register DMA device */
81362306a36Sopenharmony_ci	return dma_async_device_register(dma);
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic inline void dw_edma_dec_irq_alloc(int *nr_irqs, u32 *alloc, u16 cnt)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	if (*nr_irqs && *alloc < cnt) {
81962306a36Sopenharmony_ci		(*alloc)++;
82062306a36Sopenharmony_ci		(*nr_irqs)--;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic inline void dw_edma_add_irq_mask(u32 *mask, u32 alloc, u16 cnt)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	while (*mask * alloc < cnt)
82762306a36Sopenharmony_ci		(*mask)++;
82862306a36Sopenharmony_ci}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_cistatic int dw_edma_irq_request(struct dw_edma *dw,
83162306a36Sopenharmony_ci			       u32 *wr_alloc, u32 *rd_alloc)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	struct dw_edma_chip *chip = dw->chip;
83462306a36Sopenharmony_ci	struct device *dev = dw->chip->dev;
83562306a36Sopenharmony_ci	u32 wr_mask = 1;
83662306a36Sopenharmony_ci	u32 rd_mask = 1;
83762306a36Sopenharmony_ci	int i, err = 0;
83862306a36Sopenharmony_ci	u32 ch_cnt;
83962306a36Sopenharmony_ci	int irq;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if (chip->nr_irqs < 1 || !chip->ops->irq_vector)
84462306a36Sopenharmony_ci		return -EINVAL;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	dw->irq = devm_kcalloc(dev, chip->nr_irqs, sizeof(*dw->irq), GFP_KERNEL);
84762306a36Sopenharmony_ci	if (!dw->irq)
84862306a36Sopenharmony_ci		return -ENOMEM;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	if (chip->nr_irqs == 1) {
85162306a36Sopenharmony_ci		/* Common IRQ shared among all channels */
85262306a36Sopenharmony_ci		irq = chip->ops->irq_vector(dev, 0);
85362306a36Sopenharmony_ci		err = request_irq(irq, dw_edma_interrupt_common,
85462306a36Sopenharmony_ci				  IRQF_SHARED, dw->name, &dw->irq[0]);
85562306a36Sopenharmony_ci		if (err) {
85662306a36Sopenharmony_ci			dw->nr_irqs = 0;
85762306a36Sopenharmony_ci			return err;
85862306a36Sopenharmony_ci		}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci		if (irq_get_msi_desc(irq))
86162306a36Sopenharmony_ci			get_cached_msi_msg(irq, &dw->irq[0].msi);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci		dw->nr_irqs = 1;
86462306a36Sopenharmony_ci	} else {
86562306a36Sopenharmony_ci		/* Distribute IRQs equally among all channels */
86662306a36Sopenharmony_ci		int tmp = chip->nr_irqs;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		while (tmp && (*wr_alloc + *rd_alloc) < ch_cnt) {
86962306a36Sopenharmony_ci			dw_edma_dec_irq_alloc(&tmp, wr_alloc, dw->wr_ch_cnt);
87062306a36Sopenharmony_ci			dw_edma_dec_irq_alloc(&tmp, rd_alloc, dw->rd_ch_cnt);
87162306a36Sopenharmony_ci		}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci		dw_edma_add_irq_mask(&wr_mask, *wr_alloc, dw->wr_ch_cnt);
87462306a36Sopenharmony_ci		dw_edma_add_irq_mask(&rd_mask, *rd_alloc, dw->rd_ch_cnt);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci		for (i = 0; i < (*wr_alloc + *rd_alloc); i++) {
87762306a36Sopenharmony_ci			irq = chip->ops->irq_vector(dev, i);
87862306a36Sopenharmony_ci			err = request_irq(irq,
87962306a36Sopenharmony_ci					  i < *wr_alloc ?
88062306a36Sopenharmony_ci						dw_edma_interrupt_write :
88162306a36Sopenharmony_ci						dw_edma_interrupt_read,
88262306a36Sopenharmony_ci					  IRQF_SHARED, dw->name,
88362306a36Sopenharmony_ci					  &dw->irq[i]);
88462306a36Sopenharmony_ci			if (err)
88562306a36Sopenharmony_ci				goto err_irq_free;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci			if (irq_get_msi_desc(irq))
88862306a36Sopenharmony_ci				get_cached_msi_msg(irq, &dw->irq[i].msi);
88962306a36Sopenharmony_ci		}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci		dw->nr_irqs = i;
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	return 0;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_cierr_irq_free:
89762306a36Sopenharmony_ci	for  (i--; i >= 0; i--) {
89862306a36Sopenharmony_ci		irq = chip->ops->irq_vector(dev, i);
89962306a36Sopenharmony_ci		free_irq(irq, &dw->irq[i]);
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	return err;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ciint dw_edma_probe(struct dw_edma_chip *chip)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	struct device *dev;
90862306a36Sopenharmony_ci	struct dw_edma *dw;
90962306a36Sopenharmony_ci	u32 wr_alloc = 0;
91062306a36Sopenharmony_ci	u32 rd_alloc = 0;
91162306a36Sopenharmony_ci	int i, err;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	if (!chip)
91462306a36Sopenharmony_ci		return -EINVAL;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	dev = chip->dev;
91762306a36Sopenharmony_ci	if (!dev || !chip->ops)
91862306a36Sopenharmony_ci		return -EINVAL;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	dw = devm_kzalloc(dev, sizeof(*dw), GFP_KERNEL);
92162306a36Sopenharmony_ci	if (!dw)
92262306a36Sopenharmony_ci		return -ENOMEM;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	dw->chip = chip;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	if (dw->chip->mf == EDMA_MF_HDMA_NATIVE)
92762306a36Sopenharmony_ci		dw_hdma_v0_core_register(dw);
92862306a36Sopenharmony_ci	else
92962306a36Sopenharmony_ci		dw_edma_v0_core_register(dw);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	raw_spin_lock_init(&dw->lock);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt,
93462306a36Sopenharmony_ci			      dw_edma_core_ch_count(dw, EDMA_DIR_WRITE));
93562306a36Sopenharmony_ci	dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt,
93862306a36Sopenharmony_ci			      dw_edma_core_ch_count(dw, EDMA_DIR_READ));
93962306a36Sopenharmony_ci	dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (!dw->wr_ch_cnt && !dw->rd_ch_cnt)
94262306a36Sopenharmony_ci		return -EINVAL;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	dev_vdbg(dev, "Channels:\twrite=%d, read=%d\n",
94562306a36Sopenharmony_ci		 dw->wr_ch_cnt, dw->rd_ch_cnt);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	/* Allocate channels */
94862306a36Sopenharmony_ci	dw->chan = devm_kcalloc(dev, dw->wr_ch_cnt + dw->rd_ch_cnt,
94962306a36Sopenharmony_ci				sizeof(*dw->chan), GFP_KERNEL);
95062306a36Sopenharmony_ci	if (!dw->chan)
95162306a36Sopenharmony_ci		return -ENOMEM;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	snprintf(dw->name, sizeof(dw->name), "dw-edma-core:%s",
95462306a36Sopenharmony_ci		 dev_name(chip->dev));
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	/* Disable eDMA, only to establish the ideal initial conditions */
95762306a36Sopenharmony_ci	dw_edma_core_off(dw);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	/* Request IRQs */
96062306a36Sopenharmony_ci	err = dw_edma_irq_request(dw, &wr_alloc, &rd_alloc);
96162306a36Sopenharmony_ci	if (err)
96262306a36Sopenharmony_ci		return err;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	/* Setup write/read channels */
96562306a36Sopenharmony_ci	err = dw_edma_channel_setup(dw, wr_alloc, rd_alloc);
96662306a36Sopenharmony_ci	if (err)
96762306a36Sopenharmony_ci		goto err_irq_free;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	/* Turn debugfs on */
97062306a36Sopenharmony_ci	dw_edma_core_debugfs_on(dw);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	chip->dw = dw;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	return 0;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_cierr_irq_free:
97762306a36Sopenharmony_ci	for (i = (dw->nr_irqs - 1); i >= 0; i--)
97862306a36Sopenharmony_ci		free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	return err;
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_edma_probe);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ciint dw_edma_remove(struct dw_edma_chip *chip)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	struct dw_edma_chan *chan, *_chan;
98762306a36Sopenharmony_ci	struct device *dev = chip->dev;
98862306a36Sopenharmony_ci	struct dw_edma *dw = chip->dw;
98962306a36Sopenharmony_ci	int i;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	/* Skip removal if no private data found */
99262306a36Sopenharmony_ci	if (!dw)
99362306a36Sopenharmony_ci		return -ENODEV;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	/* Disable eDMA */
99662306a36Sopenharmony_ci	dw_edma_core_off(dw);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	/* Free irqs */
99962306a36Sopenharmony_ci	for (i = (dw->nr_irqs - 1); i >= 0; i--)
100062306a36Sopenharmony_ci		free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* Deregister eDMA device */
100362306a36Sopenharmony_ci	dma_async_device_unregister(&dw->dma);
100462306a36Sopenharmony_ci	list_for_each_entry_safe(chan, _chan, &dw->dma.channels,
100562306a36Sopenharmony_ci				 vc.chan.device_node) {
100662306a36Sopenharmony_ci		tasklet_kill(&chan->vc.task);
100762306a36Sopenharmony_ci		list_del(&chan->vc.chan.device_node);
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	return 0;
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_edma_remove);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
101562306a36Sopenharmony_ciMODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver");
101662306a36Sopenharmony_ciMODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
1017