18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
48c2ecf20Sopenharmony_ci * Synopsys DesignWare eDMA core driver
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
138c2ecf20Sopenharmony_ci#include <linux/dmaengine.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/irq.h>
178c2ecf20Sopenharmony_ci#include <linux/dma/edma.h>
188c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "dw-edma-core.h"
218c2ecf20Sopenharmony_ci#include "dw-edma-v0-core.h"
228c2ecf20Sopenharmony_ci#include "../dmaengine.h"
238c2ecf20Sopenharmony_ci#include "../virt-dma.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic inline
268c2ecf20Sopenharmony_cistruct device *dchan2dev(struct dma_chan *dchan)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	return &dchan->dev->device;
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic inline
328c2ecf20Sopenharmony_cistruct device *chan2dev(struct dw_edma_chan *chan)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	return &chan->vc.chan.dev->device;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic inline
388c2ecf20Sopenharmony_cistruct dw_edma_desc *vd2dw_edma_desc(struct virt_dma_desc *vd)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	return container_of(vd, struct dw_edma_desc, vd);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic struct dw_edma_burst *dw_edma_alloc_burst(struct dw_edma_chunk *chunk)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct dw_edma_burst *burst;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	burst = kzalloc(sizeof(*burst), GFP_NOWAIT);
488c2ecf20Sopenharmony_ci	if (unlikely(!burst))
498c2ecf20Sopenharmony_ci		return NULL;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&burst->list);
528c2ecf20Sopenharmony_ci	if (chunk->burst) {
538c2ecf20Sopenharmony_ci		/* Create and add new element into the linked list */
548c2ecf20Sopenharmony_ci		chunk->bursts_alloc++;
558c2ecf20Sopenharmony_ci		list_add_tail(&burst->list, &chunk->burst->list);
568c2ecf20Sopenharmony_ci	} else {
578c2ecf20Sopenharmony_ci		/* List head */
588c2ecf20Sopenharmony_ci		chunk->bursts_alloc = 0;
598c2ecf20Sopenharmony_ci		chunk->burst = burst;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return burst;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct dw_edma_chan *chan = desc->chan;
688c2ecf20Sopenharmony_ci	struct dw_edma *dw = chan->chip->dw;
698c2ecf20Sopenharmony_ci	struct dw_edma_chunk *chunk;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	chunk = kzalloc(sizeof(*chunk), GFP_NOWAIT);
728c2ecf20Sopenharmony_ci	if (unlikely(!chunk))
738c2ecf20Sopenharmony_ci		return NULL;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&chunk->list);
768c2ecf20Sopenharmony_ci	chunk->chan = chan;
778c2ecf20Sopenharmony_ci	/* Toggling change bit (CB) in each chunk, this is a mechanism to
788c2ecf20Sopenharmony_ci	 * inform the eDMA HW block that this is a new linked list ready
798c2ecf20Sopenharmony_ci	 * to be consumed.
808c2ecf20Sopenharmony_ci	 *  - Odd chunks originate CB equal to 0
818c2ecf20Sopenharmony_ci	 *  - Even chunks originate CB equal to 1
828c2ecf20Sopenharmony_ci	 */
838c2ecf20Sopenharmony_ci	chunk->cb = !(desc->chunks_alloc % 2);
848c2ecf20Sopenharmony_ci	chunk->ll_region.paddr = dw->ll_region.paddr + chan->ll_off;
858c2ecf20Sopenharmony_ci	chunk->ll_region.vaddr = dw->ll_region.vaddr + chan->ll_off;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (desc->chunk) {
888c2ecf20Sopenharmony_ci		/* Create and add new element into the linked list */
898c2ecf20Sopenharmony_ci		if (!dw_edma_alloc_burst(chunk)) {
908c2ecf20Sopenharmony_ci			kfree(chunk);
918c2ecf20Sopenharmony_ci			return NULL;
928c2ecf20Sopenharmony_ci		}
938c2ecf20Sopenharmony_ci		desc->chunks_alloc++;
948c2ecf20Sopenharmony_ci		list_add_tail(&chunk->list, &desc->chunk->list);
958c2ecf20Sopenharmony_ci	} else {
968c2ecf20Sopenharmony_ci		/* List head */
978c2ecf20Sopenharmony_ci		chunk->burst = NULL;
988c2ecf20Sopenharmony_ci		desc->chunks_alloc = 0;
998c2ecf20Sopenharmony_ci		desc->chunk = chunk;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return chunk;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic struct dw_edma_desc *dw_edma_alloc_desc(struct dw_edma_chan *chan)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct dw_edma_desc *desc;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
1108c2ecf20Sopenharmony_ci	if (unlikely(!desc))
1118c2ecf20Sopenharmony_ci		return NULL;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	desc->chan = chan;
1148c2ecf20Sopenharmony_ci	if (!dw_edma_alloc_chunk(desc)) {
1158c2ecf20Sopenharmony_ci		kfree(desc);
1168c2ecf20Sopenharmony_ci		return NULL;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return desc;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic void dw_edma_free_burst(struct dw_edma_chunk *chunk)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct dw_edma_burst *child, *_next;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* Remove all the list elements */
1278c2ecf20Sopenharmony_ci	list_for_each_entry_safe(child, _next, &chunk->burst->list, list) {
1288c2ecf20Sopenharmony_ci		list_del(&child->list);
1298c2ecf20Sopenharmony_ci		kfree(child);
1308c2ecf20Sopenharmony_ci		chunk->bursts_alloc--;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/* Remove the list head */
1348c2ecf20Sopenharmony_ci	kfree(child);
1358c2ecf20Sopenharmony_ci	chunk->burst = NULL;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic void dw_edma_free_chunk(struct dw_edma_desc *desc)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct dw_edma_chunk *child, *_next;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (!desc->chunk)
1438c2ecf20Sopenharmony_ci		return;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* Remove all the list elements */
1468c2ecf20Sopenharmony_ci	list_for_each_entry_safe(child, _next, &desc->chunk->list, list) {
1478c2ecf20Sopenharmony_ci		dw_edma_free_burst(child);
1488c2ecf20Sopenharmony_ci		list_del(&child->list);
1498c2ecf20Sopenharmony_ci		kfree(child);
1508c2ecf20Sopenharmony_ci		desc->chunks_alloc--;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* Remove the list head */
1548c2ecf20Sopenharmony_ci	kfree(child);
1558c2ecf20Sopenharmony_ci	desc->chunk = NULL;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic void dw_edma_free_desc(struct dw_edma_desc *desc)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	dw_edma_free_chunk(desc);
1618c2ecf20Sopenharmony_ci	kfree(desc);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic void vchan_free_desc(struct virt_dma_desc *vdesc)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	dw_edma_free_desc(vd2dw_edma_desc(vdesc));
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic int dw_edma_start_transfer(struct dw_edma_chan *chan)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct dw_edma_chunk *child;
1728c2ecf20Sopenharmony_ci	struct dw_edma_desc *desc;
1738c2ecf20Sopenharmony_ci	struct virt_dma_desc *vd;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	vd = vchan_next_desc(&chan->vc);
1768c2ecf20Sopenharmony_ci	if (!vd)
1778c2ecf20Sopenharmony_ci		return 0;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	desc = vd2dw_edma_desc(vd);
1808c2ecf20Sopenharmony_ci	if (!desc)
1818c2ecf20Sopenharmony_ci		return 0;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	child = list_first_entry_or_null(&desc->chunk->list,
1848c2ecf20Sopenharmony_ci					 struct dw_edma_chunk, list);
1858c2ecf20Sopenharmony_ci	if (!child)
1868c2ecf20Sopenharmony_ci		return 0;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	dw_edma_v0_core_start(child, !desc->xfer_sz);
1898c2ecf20Sopenharmony_ci	desc->xfer_sz += child->ll_region.sz;
1908c2ecf20Sopenharmony_ci	dw_edma_free_burst(child);
1918c2ecf20Sopenharmony_ci	list_del(&child->list);
1928c2ecf20Sopenharmony_ci	kfree(child);
1938c2ecf20Sopenharmony_ci	desc->chunks_alloc--;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	return 1;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int dw_edma_device_config(struct dma_chan *dchan,
1998c2ecf20Sopenharmony_ci				 struct dma_slave_config *config)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	memcpy(&chan->config, config, sizeof(*config));
2048c2ecf20Sopenharmony_ci	chan->configured = true;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return 0;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic int dw_edma_device_pause(struct dma_chan *dchan)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
2128c2ecf20Sopenharmony_ci	int err = 0;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (!chan->configured)
2158c2ecf20Sopenharmony_ci		err = -EPERM;
2168c2ecf20Sopenharmony_ci	else if (chan->status != EDMA_ST_BUSY)
2178c2ecf20Sopenharmony_ci		err = -EPERM;
2188c2ecf20Sopenharmony_ci	else if (chan->request != EDMA_REQ_NONE)
2198c2ecf20Sopenharmony_ci		err = -EPERM;
2208c2ecf20Sopenharmony_ci	else
2218c2ecf20Sopenharmony_ci		chan->request = EDMA_REQ_PAUSE;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return err;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic int dw_edma_device_resume(struct dma_chan *dchan)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
2298c2ecf20Sopenharmony_ci	int err = 0;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (!chan->configured) {
2328c2ecf20Sopenharmony_ci		err = -EPERM;
2338c2ecf20Sopenharmony_ci	} else if (chan->status != EDMA_ST_PAUSE) {
2348c2ecf20Sopenharmony_ci		err = -EPERM;
2358c2ecf20Sopenharmony_ci	} else if (chan->request != EDMA_REQ_NONE) {
2368c2ecf20Sopenharmony_ci		err = -EPERM;
2378c2ecf20Sopenharmony_ci	} else {
2388c2ecf20Sopenharmony_ci		chan->status = EDMA_ST_BUSY;
2398c2ecf20Sopenharmony_ci		dw_edma_start_transfer(chan);
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return err;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic int dw_edma_device_terminate_all(struct dma_chan *dchan)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
2488c2ecf20Sopenharmony_ci	int err = 0;
2498c2ecf20Sopenharmony_ci	LIST_HEAD(head);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (!chan->configured) {
2528c2ecf20Sopenharmony_ci		/* Do nothing */
2538c2ecf20Sopenharmony_ci	} else if (chan->status == EDMA_ST_PAUSE) {
2548c2ecf20Sopenharmony_ci		chan->status = EDMA_ST_IDLE;
2558c2ecf20Sopenharmony_ci		chan->configured = false;
2568c2ecf20Sopenharmony_ci	} else if (chan->status == EDMA_ST_IDLE) {
2578c2ecf20Sopenharmony_ci		chan->configured = false;
2588c2ecf20Sopenharmony_ci	} else if (dw_edma_v0_core_ch_status(chan) == DMA_COMPLETE) {
2598c2ecf20Sopenharmony_ci		/*
2608c2ecf20Sopenharmony_ci		 * The channel is in a false BUSY state, probably didn't
2618c2ecf20Sopenharmony_ci		 * receive or lost an interrupt
2628c2ecf20Sopenharmony_ci		 */
2638c2ecf20Sopenharmony_ci		chan->status = EDMA_ST_IDLE;
2648c2ecf20Sopenharmony_ci		chan->configured = false;
2658c2ecf20Sopenharmony_ci	} else if (chan->request > EDMA_REQ_PAUSE) {
2668c2ecf20Sopenharmony_ci		err = -EPERM;
2678c2ecf20Sopenharmony_ci	} else {
2688c2ecf20Sopenharmony_ci		chan->request = EDMA_REQ_STOP;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return err;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic void dw_edma_device_issue_pending(struct dma_chan *dchan)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
2778c2ecf20Sopenharmony_ci	unsigned long flags;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (!chan->configured)
2808c2ecf20Sopenharmony_ci		return;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->vc.lock, flags);
2838c2ecf20Sopenharmony_ci	if (vchan_issue_pending(&chan->vc) && chan->request == EDMA_REQ_NONE &&
2848c2ecf20Sopenharmony_ci	    chan->status == EDMA_ST_IDLE) {
2858c2ecf20Sopenharmony_ci		chan->status = EDMA_ST_BUSY;
2868c2ecf20Sopenharmony_ci		dw_edma_start_transfer(chan);
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->vc.lock, flags);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic enum dma_status
2928c2ecf20Sopenharmony_cidw_edma_device_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
2938c2ecf20Sopenharmony_ci			 struct dma_tx_state *txstate)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
2968c2ecf20Sopenharmony_ci	struct dw_edma_desc *desc;
2978c2ecf20Sopenharmony_ci	struct virt_dma_desc *vd;
2988c2ecf20Sopenharmony_ci	unsigned long flags;
2998c2ecf20Sopenharmony_ci	enum dma_status ret;
3008c2ecf20Sopenharmony_ci	u32 residue = 0;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	ret = dma_cookie_status(dchan, cookie, txstate);
3038c2ecf20Sopenharmony_ci	if (ret == DMA_COMPLETE)
3048c2ecf20Sopenharmony_ci		return ret;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (ret == DMA_IN_PROGRESS && chan->status == EDMA_ST_PAUSE)
3078c2ecf20Sopenharmony_ci		ret = DMA_PAUSED;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (!txstate)
3108c2ecf20Sopenharmony_ci		goto ret_residue;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->vc.lock, flags);
3138c2ecf20Sopenharmony_ci	vd = vchan_find_desc(&chan->vc, cookie);
3148c2ecf20Sopenharmony_ci	if (vd) {
3158c2ecf20Sopenharmony_ci		desc = vd2dw_edma_desc(vd);
3168c2ecf20Sopenharmony_ci		if (desc)
3178c2ecf20Sopenharmony_ci			residue = desc->alloc_sz - desc->xfer_sz;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->vc.lock, flags);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ciret_residue:
3228c2ecf20Sopenharmony_ci	dma_set_residue(txstate, residue);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	return ret;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
3288c2ecf20Sopenharmony_cidw_edma_device_transfer(struct dw_edma_transfer *xfer)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
3318c2ecf20Sopenharmony_ci	enum dma_transfer_direction dir = xfer->direction;
3328c2ecf20Sopenharmony_ci	phys_addr_t src_addr, dst_addr;
3338c2ecf20Sopenharmony_ci	struct scatterlist *sg = NULL;
3348c2ecf20Sopenharmony_ci	struct dw_edma_chunk *chunk;
3358c2ecf20Sopenharmony_ci	struct dw_edma_burst *burst;
3368c2ecf20Sopenharmony_ci	struct dw_edma_desc *desc;
3378c2ecf20Sopenharmony_ci	u32 cnt;
3388c2ecf20Sopenharmony_ci	int i;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (!chan->configured)
3418c2ecf20Sopenharmony_ci		return NULL;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	switch (chan->config.direction) {
3448c2ecf20Sopenharmony_ci	case DMA_DEV_TO_MEM: /* local dma */
3458c2ecf20Sopenharmony_ci		if (dir == DMA_DEV_TO_MEM && chan->dir == EDMA_DIR_READ)
3468c2ecf20Sopenharmony_ci			break;
3478c2ecf20Sopenharmony_ci		return NULL;
3488c2ecf20Sopenharmony_ci	case DMA_MEM_TO_DEV: /* local dma */
3498c2ecf20Sopenharmony_ci		if (dir == DMA_MEM_TO_DEV && chan->dir == EDMA_DIR_WRITE)
3508c2ecf20Sopenharmony_ci			break;
3518c2ecf20Sopenharmony_ci		return NULL;
3528c2ecf20Sopenharmony_ci	default: /* remote dma */
3538c2ecf20Sopenharmony_ci		if (dir == DMA_MEM_TO_DEV && chan->dir == EDMA_DIR_READ)
3548c2ecf20Sopenharmony_ci			break;
3558c2ecf20Sopenharmony_ci		if (dir == DMA_DEV_TO_MEM && chan->dir == EDMA_DIR_WRITE)
3568c2ecf20Sopenharmony_ci			break;
3578c2ecf20Sopenharmony_ci		return NULL;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (xfer->cyclic) {
3618c2ecf20Sopenharmony_ci		if (!xfer->xfer.cyclic.len || !xfer->xfer.cyclic.cnt)
3628c2ecf20Sopenharmony_ci			return NULL;
3638c2ecf20Sopenharmony_ci	} else {
3648c2ecf20Sopenharmony_ci		if (xfer->xfer.sg.len < 1)
3658c2ecf20Sopenharmony_ci			return NULL;
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	desc = dw_edma_alloc_desc(chan);
3698c2ecf20Sopenharmony_ci	if (unlikely(!desc))
3708c2ecf20Sopenharmony_ci		goto err_alloc;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	chunk = dw_edma_alloc_chunk(desc);
3738c2ecf20Sopenharmony_ci	if (unlikely(!chunk))
3748c2ecf20Sopenharmony_ci		goto err_alloc;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	src_addr = chan->config.src_addr;
3778c2ecf20Sopenharmony_ci	dst_addr = chan->config.dst_addr;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (xfer->cyclic) {
3808c2ecf20Sopenharmony_ci		cnt = xfer->xfer.cyclic.cnt;
3818c2ecf20Sopenharmony_ci	} else {
3828c2ecf20Sopenharmony_ci		cnt = xfer->xfer.sg.len;
3838c2ecf20Sopenharmony_ci		sg = xfer->xfer.sg.sgl;
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	for (i = 0; i < cnt; i++) {
3878c2ecf20Sopenharmony_ci		if (!xfer->cyclic && !sg)
3888c2ecf20Sopenharmony_ci			break;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci		if (chunk->bursts_alloc == chan->ll_max) {
3918c2ecf20Sopenharmony_ci			chunk = dw_edma_alloc_chunk(desc);
3928c2ecf20Sopenharmony_ci			if (unlikely(!chunk))
3938c2ecf20Sopenharmony_ci				goto err_alloc;
3948c2ecf20Sopenharmony_ci		}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci		burst = dw_edma_alloc_burst(chunk);
3978c2ecf20Sopenharmony_ci		if (unlikely(!burst))
3988c2ecf20Sopenharmony_ci			goto err_alloc;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		if (xfer->cyclic)
4018c2ecf20Sopenharmony_ci			burst->sz = xfer->xfer.cyclic.len;
4028c2ecf20Sopenharmony_ci		else
4038c2ecf20Sopenharmony_ci			burst->sz = sg_dma_len(sg);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		chunk->ll_region.sz += burst->sz;
4068c2ecf20Sopenharmony_ci		desc->alloc_sz += burst->sz;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		if (dir == DMA_DEV_TO_MEM) {
4098c2ecf20Sopenharmony_ci			burst->sar = src_addr;
4108c2ecf20Sopenharmony_ci			if (xfer->cyclic) {
4118c2ecf20Sopenharmony_ci				burst->dar = xfer->xfer.cyclic.paddr;
4128c2ecf20Sopenharmony_ci			} else {
4138c2ecf20Sopenharmony_ci				burst->dar = dst_addr;
4148c2ecf20Sopenharmony_ci				/* Unlike the typical assumption by other
4158c2ecf20Sopenharmony_ci				 * drivers/IPs the peripheral memory isn't
4168c2ecf20Sopenharmony_ci				 * a FIFO memory, in this case, it's a
4178c2ecf20Sopenharmony_ci				 * linear memory and that why the source
4188c2ecf20Sopenharmony_ci				 * and destination addresses are increased
4198c2ecf20Sopenharmony_ci				 * by the same portion (data length)
4208c2ecf20Sopenharmony_ci				 */
4218c2ecf20Sopenharmony_ci			}
4228c2ecf20Sopenharmony_ci		} else {
4238c2ecf20Sopenharmony_ci			burst->dar = dst_addr;
4248c2ecf20Sopenharmony_ci			if (xfer->cyclic) {
4258c2ecf20Sopenharmony_ci				burst->sar = xfer->xfer.cyclic.paddr;
4268c2ecf20Sopenharmony_ci			} else {
4278c2ecf20Sopenharmony_ci				burst->sar = src_addr;
4288c2ecf20Sopenharmony_ci				/* Unlike the typical assumption by other
4298c2ecf20Sopenharmony_ci				 * drivers/IPs the peripheral memory isn't
4308c2ecf20Sopenharmony_ci				 * a FIFO memory, in this case, it's a
4318c2ecf20Sopenharmony_ci				 * linear memory and that why the source
4328c2ecf20Sopenharmony_ci				 * and destination addresses are increased
4338c2ecf20Sopenharmony_ci				 * by the same portion (data length)
4348c2ecf20Sopenharmony_ci				 */
4358c2ecf20Sopenharmony_ci			}
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		if (!xfer->cyclic) {
4398c2ecf20Sopenharmony_ci			src_addr += sg_dma_len(sg);
4408c2ecf20Sopenharmony_ci			dst_addr += sg_dma_len(sg);
4418c2ecf20Sopenharmony_ci			sg = sg_next(sg);
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	return vchan_tx_prep(&chan->vc, &desc->vd, xfer->flags);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cierr_alloc:
4488c2ecf20Sopenharmony_ci	if (desc)
4498c2ecf20Sopenharmony_ci		dw_edma_free_desc(desc);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	return NULL;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
4558c2ecf20Sopenharmony_cidw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
4568c2ecf20Sopenharmony_ci			     unsigned int len,
4578c2ecf20Sopenharmony_ci			     enum dma_transfer_direction direction,
4588c2ecf20Sopenharmony_ci			     unsigned long flags, void *context)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct dw_edma_transfer xfer;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	xfer.dchan = dchan;
4638c2ecf20Sopenharmony_ci	xfer.direction = direction;
4648c2ecf20Sopenharmony_ci	xfer.xfer.sg.sgl = sgl;
4658c2ecf20Sopenharmony_ci	xfer.xfer.sg.len = len;
4668c2ecf20Sopenharmony_ci	xfer.flags = flags;
4678c2ecf20Sopenharmony_ci	xfer.cyclic = false;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	return dw_edma_device_transfer(&xfer);
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
4738c2ecf20Sopenharmony_cidw_edma_device_prep_dma_cyclic(struct dma_chan *dchan, dma_addr_t paddr,
4748c2ecf20Sopenharmony_ci			       size_t len, size_t count,
4758c2ecf20Sopenharmony_ci			       enum dma_transfer_direction direction,
4768c2ecf20Sopenharmony_ci			       unsigned long flags)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	struct dw_edma_transfer xfer;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	xfer.dchan = dchan;
4818c2ecf20Sopenharmony_ci	xfer.direction = direction;
4828c2ecf20Sopenharmony_ci	xfer.xfer.cyclic.paddr = paddr;
4838c2ecf20Sopenharmony_ci	xfer.xfer.cyclic.len = len;
4848c2ecf20Sopenharmony_ci	xfer.xfer.cyclic.cnt = count;
4858c2ecf20Sopenharmony_ci	xfer.flags = flags;
4868c2ecf20Sopenharmony_ci	xfer.cyclic = true;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	return dw_edma_device_transfer(&xfer);
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic void dw_edma_done_interrupt(struct dw_edma_chan *chan)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	struct dw_edma_desc *desc;
4948c2ecf20Sopenharmony_ci	struct virt_dma_desc *vd;
4958c2ecf20Sopenharmony_ci	unsigned long flags;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	dw_edma_v0_core_clear_done_int(chan);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->vc.lock, flags);
5008c2ecf20Sopenharmony_ci	vd = vchan_next_desc(&chan->vc);
5018c2ecf20Sopenharmony_ci	if (vd) {
5028c2ecf20Sopenharmony_ci		switch (chan->request) {
5038c2ecf20Sopenharmony_ci		case EDMA_REQ_NONE:
5048c2ecf20Sopenharmony_ci			desc = vd2dw_edma_desc(vd);
5058c2ecf20Sopenharmony_ci			if (!desc->chunks_alloc) {
5068c2ecf20Sopenharmony_ci				list_del(&vd->node);
5078c2ecf20Sopenharmony_ci				vchan_cookie_complete(vd);
5088c2ecf20Sopenharmony_ci			}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci			/* Continue transferring if there are remaining chunks or issued requests.
5118c2ecf20Sopenharmony_ci			 */
5128c2ecf20Sopenharmony_ci			chan->status = dw_edma_start_transfer(chan) ? EDMA_ST_BUSY : EDMA_ST_IDLE;
5138c2ecf20Sopenharmony_ci			break;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci		case EDMA_REQ_STOP:
5168c2ecf20Sopenharmony_ci			list_del(&vd->node);
5178c2ecf20Sopenharmony_ci			vchan_cookie_complete(vd);
5188c2ecf20Sopenharmony_ci			chan->request = EDMA_REQ_NONE;
5198c2ecf20Sopenharmony_ci			chan->status = EDMA_ST_IDLE;
5208c2ecf20Sopenharmony_ci			break;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci		case EDMA_REQ_PAUSE:
5238c2ecf20Sopenharmony_ci			chan->request = EDMA_REQ_NONE;
5248c2ecf20Sopenharmony_ci			chan->status = EDMA_ST_PAUSE;
5258c2ecf20Sopenharmony_ci			break;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci		default:
5288c2ecf20Sopenharmony_ci			break;
5298c2ecf20Sopenharmony_ci		}
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->vc.lock, flags);
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	struct virt_dma_desc *vd;
5378c2ecf20Sopenharmony_ci	unsigned long flags;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	dw_edma_v0_core_clear_abort_int(chan);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->vc.lock, flags);
5428c2ecf20Sopenharmony_ci	vd = vchan_next_desc(&chan->vc);
5438c2ecf20Sopenharmony_ci	if (vd) {
5448c2ecf20Sopenharmony_ci		list_del(&vd->node);
5458c2ecf20Sopenharmony_ci		vchan_cookie_complete(vd);
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->vc.lock, flags);
5488c2ecf20Sopenharmony_ci	chan->request = EDMA_REQ_NONE;
5498c2ecf20Sopenharmony_ci	chan->status = EDMA_ST_IDLE;
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_cistatic irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	struct dw_edma_irq *dw_irq = data;
5558c2ecf20Sopenharmony_ci	struct dw_edma *dw = dw_irq->dw;
5568c2ecf20Sopenharmony_ci	unsigned long total, pos, val;
5578c2ecf20Sopenharmony_ci	unsigned long off;
5588c2ecf20Sopenharmony_ci	u32 mask;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	if (write) {
5618c2ecf20Sopenharmony_ci		total = dw->wr_ch_cnt;
5628c2ecf20Sopenharmony_ci		off = 0;
5638c2ecf20Sopenharmony_ci		mask = dw_irq->wr_mask;
5648c2ecf20Sopenharmony_ci	} else {
5658c2ecf20Sopenharmony_ci		total = dw->rd_ch_cnt;
5668c2ecf20Sopenharmony_ci		off = dw->wr_ch_cnt;
5678c2ecf20Sopenharmony_ci		mask = dw_irq->rd_mask;
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	val = dw_edma_v0_core_status_done_int(dw, write ?
5718c2ecf20Sopenharmony_ci							  EDMA_DIR_WRITE :
5728c2ecf20Sopenharmony_ci							  EDMA_DIR_READ);
5738c2ecf20Sopenharmony_ci	val &= mask;
5748c2ecf20Sopenharmony_ci	for_each_set_bit(pos, &val, total) {
5758c2ecf20Sopenharmony_ci		struct dw_edma_chan *chan = &dw->chan[pos + off];
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci		dw_edma_done_interrupt(chan);
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	val = dw_edma_v0_core_status_abort_int(dw, write ?
5818c2ecf20Sopenharmony_ci							   EDMA_DIR_WRITE :
5828c2ecf20Sopenharmony_ci							   EDMA_DIR_READ);
5838c2ecf20Sopenharmony_ci	val &= mask;
5848c2ecf20Sopenharmony_ci	for_each_set_bit(pos, &val, total) {
5858c2ecf20Sopenharmony_ci		struct dw_edma_chan *chan = &dw->chan[pos + off];
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci		dw_edma_abort_interrupt(chan);
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
5948c2ecf20Sopenharmony_ci{
5958c2ecf20Sopenharmony_ci	return dw_edma_interrupt(irq, data, true);
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	return dw_edma_interrupt(irq, data, false);
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_cistatic irqreturn_t dw_edma_interrupt_common(int irq, void *data)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	dw_edma_interrupt(irq, data, true);
6068c2ecf20Sopenharmony_ci	dw_edma_interrupt(irq, data, false);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_cistatic int dw_edma_alloc_chan_resources(struct dma_chan *dchan)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (chan->status != EDMA_ST_IDLE)
6168c2ecf20Sopenharmony_ci		return -EBUSY;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	pm_runtime_get(chan->chip->dev);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	return 0;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic void dw_edma_free_chan_resources(struct dma_chan *dchan)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(5000);
6268c2ecf20Sopenharmony_ci	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
6278c2ecf20Sopenharmony_ci	int ret;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
6308c2ecf20Sopenharmony_ci		ret = dw_edma_device_terminate_all(dchan);
6318c2ecf20Sopenharmony_ci		if (!ret)
6328c2ecf20Sopenharmony_ci			break;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci		if (time_after_eq(jiffies, timeout))
6358c2ecf20Sopenharmony_ci			return;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci		cpu_relax();
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	pm_runtime_put(chan->chip->dev);
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic int dw_edma_channel_setup(struct dw_edma_chip *chip, bool write,
6448c2ecf20Sopenharmony_ci				 u32 wr_alloc, u32 rd_alloc)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	struct dw_edma_region *dt_region;
6478c2ecf20Sopenharmony_ci	struct device *dev = chip->dev;
6488c2ecf20Sopenharmony_ci	struct dw_edma *dw = chip->dw;
6498c2ecf20Sopenharmony_ci	struct dw_edma_chan *chan;
6508c2ecf20Sopenharmony_ci	size_t ll_chunk, dt_chunk;
6518c2ecf20Sopenharmony_ci	struct dw_edma_irq *irq;
6528c2ecf20Sopenharmony_ci	struct dma_device *dma;
6538c2ecf20Sopenharmony_ci	u32 i, j, cnt, ch_cnt;
6548c2ecf20Sopenharmony_ci	u32 alloc, off_alloc;
6558c2ecf20Sopenharmony_ci	int err = 0;
6568c2ecf20Sopenharmony_ci	u32 pos;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt;
6598c2ecf20Sopenharmony_ci	ll_chunk = dw->ll_region.sz;
6608c2ecf20Sopenharmony_ci	dt_chunk = dw->dt_region.sz;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/* Calculate linked list chunk for each channel */
6638c2ecf20Sopenharmony_ci	ll_chunk /= roundup_pow_of_two(ch_cnt);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	/* Calculate linked list chunk for each channel */
6668c2ecf20Sopenharmony_ci	dt_chunk /= roundup_pow_of_two(ch_cnt);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	if (write) {
6698c2ecf20Sopenharmony_ci		i = 0;
6708c2ecf20Sopenharmony_ci		cnt = dw->wr_ch_cnt;
6718c2ecf20Sopenharmony_ci		dma = &dw->wr_edma;
6728c2ecf20Sopenharmony_ci		alloc = wr_alloc;
6738c2ecf20Sopenharmony_ci		off_alloc = 0;
6748c2ecf20Sopenharmony_ci	} else {
6758c2ecf20Sopenharmony_ci		i = dw->wr_ch_cnt;
6768c2ecf20Sopenharmony_ci		cnt = dw->rd_ch_cnt;
6778c2ecf20Sopenharmony_ci		dma = &dw->rd_edma;
6788c2ecf20Sopenharmony_ci		alloc = rd_alloc;
6798c2ecf20Sopenharmony_ci		off_alloc = wr_alloc;
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dma->channels);
6838c2ecf20Sopenharmony_ci	for (j = 0; (alloc || dw->nr_irqs == 1) && j < cnt; j++, i++) {
6848c2ecf20Sopenharmony_ci		chan = &dw->chan[i];
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci		dt_region = devm_kzalloc(dev, sizeof(*dt_region), GFP_KERNEL);
6878c2ecf20Sopenharmony_ci		if (!dt_region)
6888c2ecf20Sopenharmony_ci			return -ENOMEM;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci		chan->vc.chan.private = dt_region;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci		chan->chip = chip;
6938c2ecf20Sopenharmony_ci		chan->id = j;
6948c2ecf20Sopenharmony_ci		chan->dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ;
6958c2ecf20Sopenharmony_ci		chan->configured = false;
6968c2ecf20Sopenharmony_ci		chan->request = EDMA_REQ_NONE;
6978c2ecf20Sopenharmony_ci		chan->status = EDMA_ST_IDLE;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci		chan->ll_off = (ll_chunk * i);
7008c2ecf20Sopenharmony_ci		chan->ll_max = (ll_chunk / EDMA_LL_SZ) - 1;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci		chan->dt_off = (dt_chunk * i);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci		dev_vdbg(dev, "L. List:\tChannel %s[%u] off=0x%.8lx, max_cnt=%u\n",
7058c2ecf20Sopenharmony_ci			 write ? "write" : "read", j,
7068c2ecf20Sopenharmony_ci			 chan->ll_off, chan->ll_max);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci		if (dw->nr_irqs == 1)
7098c2ecf20Sopenharmony_ci			pos = 0;
7108c2ecf20Sopenharmony_ci		else
7118c2ecf20Sopenharmony_ci			pos = off_alloc + (j % alloc);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci		irq = &dw->irq[pos];
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci		if (write)
7168c2ecf20Sopenharmony_ci			irq->wr_mask |= BIT(j);
7178c2ecf20Sopenharmony_ci		else
7188c2ecf20Sopenharmony_ci			irq->rd_mask |= BIT(j);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci		irq->dw = dw;
7218c2ecf20Sopenharmony_ci		memcpy(&chan->msi, &irq->msi, sizeof(chan->msi));
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci		dev_vdbg(dev, "MSI:\t\tChannel %s[%u] addr=0x%.8x%.8x, data=0x%.8x\n",
7248c2ecf20Sopenharmony_ci			 write ? "write" : "read", j,
7258c2ecf20Sopenharmony_ci			 chan->msi.address_hi, chan->msi.address_lo,
7268c2ecf20Sopenharmony_ci			 chan->msi.data);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci		chan->vc.desc_free = vchan_free_desc;
7298c2ecf20Sopenharmony_ci		vchan_init(&chan->vc, dma);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci		dt_region->paddr = dw->dt_region.paddr + chan->dt_off;
7328c2ecf20Sopenharmony_ci		dt_region->vaddr = dw->dt_region.vaddr + chan->dt_off;
7338c2ecf20Sopenharmony_ci		dt_region->sz = dt_chunk;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci		dev_vdbg(dev, "Data:\tChannel %s[%u] off=0x%.8lx\n",
7368c2ecf20Sopenharmony_ci			 write ? "write" : "read", j, chan->dt_off);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci		dw_edma_v0_core_device_config(chan);
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	/* Set DMA channel capabilities */
7428c2ecf20Sopenharmony_ci	dma_cap_zero(dma->cap_mask);
7438c2ecf20Sopenharmony_ci	dma_cap_set(DMA_SLAVE, dma->cap_mask);
7448c2ecf20Sopenharmony_ci	dma_cap_set(DMA_CYCLIC, dma->cap_mask);
7458c2ecf20Sopenharmony_ci	dma_cap_set(DMA_PRIVATE, dma->cap_mask);
7468c2ecf20Sopenharmony_ci	dma->directions = BIT(write ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV);
7478c2ecf20Sopenharmony_ci	dma->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
7488c2ecf20Sopenharmony_ci	dma->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
7498c2ecf20Sopenharmony_ci	dma->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
7508c2ecf20Sopenharmony_ci	dma->chancnt = cnt;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	/* Set DMA channel callbacks */
7538c2ecf20Sopenharmony_ci	dma->dev = chip->dev;
7548c2ecf20Sopenharmony_ci	dma->device_alloc_chan_resources = dw_edma_alloc_chan_resources;
7558c2ecf20Sopenharmony_ci	dma->device_free_chan_resources = dw_edma_free_chan_resources;
7568c2ecf20Sopenharmony_ci	dma->device_config = dw_edma_device_config;
7578c2ecf20Sopenharmony_ci	dma->device_pause = dw_edma_device_pause;
7588c2ecf20Sopenharmony_ci	dma->device_resume = dw_edma_device_resume;
7598c2ecf20Sopenharmony_ci	dma->device_terminate_all = dw_edma_device_terminate_all;
7608c2ecf20Sopenharmony_ci	dma->device_issue_pending = dw_edma_device_issue_pending;
7618c2ecf20Sopenharmony_ci	dma->device_tx_status = dw_edma_device_tx_status;
7628c2ecf20Sopenharmony_ci	dma->device_prep_slave_sg = dw_edma_device_prep_slave_sg;
7638c2ecf20Sopenharmony_ci	dma->device_prep_dma_cyclic = dw_edma_device_prep_dma_cyclic;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	dma_set_max_seg_size(dma->dev, U32_MAX);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	/* Register DMA device */
7688c2ecf20Sopenharmony_ci	err = dma_async_device_register(dma);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	return err;
7718c2ecf20Sopenharmony_ci}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_cistatic inline void dw_edma_dec_irq_alloc(int *nr_irqs, u32 *alloc, u16 cnt)
7748c2ecf20Sopenharmony_ci{
7758c2ecf20Sopenharmony_ci	if (*nr_irqs && *alloc < cnt) {
7768c2ecf20Sopenharmony_ci		(*alloc)++;
7778c2ecf20Sopenharmony_ci		(*nr_irqs)--;
7788c2ecf20Sopenharmony_ci	}
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cistatic inline void dw_edma_add_irq_mask(u32 *mask, u32 alloc, u16 cnt)
7828c2ecf20Sopenharmony_ci{
7838c2ecf20Sopenharmony_ci	while (*mask * alloc < cnt)
7848c2ecf20Sopenharmony_ci		(*mask)++;
7858c2ecf20Sopenharmony_ci}
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_cistatic int dw_edma_irq_request(struct dw_edma_chip *chip,
7888c2ecf20Sopenharmony_ci			       u32 *wr_alloc, u32 *rd_alloc)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	struct device *dev = chip->dev;
7918c2ecf20Sopenharmony_ci	struct dw_edma *dw = chip->dw;
7928c2ecf20Sopenharmony_ci	u32 wr_mask = 1;
7938c2ecf20Sopenharmony_ci	u32 rd_mask = 1;
7948c2ecf20Sopenharmony_ci	int i, err = 0;
7958c2ecf20Sopenharmony_ci	u32 ch_cnt;
7968c2ecf20Sopenharmony_ci	int irq;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	if (dw->nr_irqs < 1)
8018c2ecf20Sopenharmony_ci		return -EINVAL;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	if (dw->nr_irqs == 1) {
8048c2ecf20Sopenharmony_ci		/* Common IRQ shared among all channels */
8058c2ecf20Sopenharmony_ci		irq = dw->ops->irq_vector(dev, 0);
8068c2ecf20Sopenharmony_ci		err = request_irq(irq, dw_edma_interrupt_common,
8078c2ecf20Sopenharmony_ci				  IRQF_SHARED, dw->name, &dw->irq[0]);
8088c2ecf20Sopenharmony_ci		if (err) {
8098c2ecf20Sopenharmony_ci			dw->nr_irqs = 0;
8108c2ecf20Sopenharmony_ci			return err;
8118c2ecf20Sopenharmony_ci		}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci		if (irq_get_msi_desc(irq))
8148c2ecf20Sopenharmony_ci			get_cached_msi_msg(irq, &dw->irq[0].msi);
8158c2ecf20Sopenharmony_ci	} else {
8168c2ecf20Sopenharmony_ci		/* Distribute IRQs equally among all channels */
8178c2ecf20Sopenharmony_ci		int tmp = dw->nr_irqs;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci		while (tmp && (*wr_alloc + *rd_alloc) < ch_cnt) {
8208c2ecf20Sopenharmony_ci			dw_edma_dec_irq_alloc(&tmp, wr_alloc, dw->wr_ch_cnt);
8218c2ecf20Sopenharmony_ci			dw_edma_dec_irq_alloc(&tmp, rd_alloc, dw->rd_ch_cnt);
8228c2ecf20Sopenharmony_ci		}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci		dw_edma_add_irq_mask(&wr_mask, *wr_alloc, dw->wr_ch_cnt);
8258c2ecf20Sopenharmony_ci		dw_edma_add_irq_mask(&rd_mask, *rd_alloc, dw->rd_ch_cnt);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci		for (i = 0; i < (*wr_alloc + *rd_alloc); i++) {
8288c2ecf20Sopenharmony_ci			irq = dw->ops->irq_vector(dev, i);
8298c2ecf20Sopenharmony_ci			err = request_irq(irq,
8308c2ecf20Sopenharmony_ci					  i < *wr_alloc ?
8318c2ecf20Sopenharmony_ci						dw_edma_interrupt_write :
8328c2ecf20Sopenharmony_ci						dw_edma_interrupt_read,
8338c2ecf20Sopenharmony_ci					  IRQF_SHARED, dw->name,
8348c2ecf20Sopenharmony_ci					  &dw->irq[i]);
8358c2ecf20Sopenharmony_ci			if (err) {
8368c2ecf20Sopenharmony_ci				dw->nr_irqs = i;
8378c2ecf20Sopenharmony_ci				return err;
8388c2ecf20Sopenharmony_ci			}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci			if (irq_get_msi_desc(irq))
8418c2ecf20Sopenharmony_ci				get_cached_msi_msg(irq, &dw->irq[i].msi);
8428c2ecf20Sopenharmony_ci		}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci		dw->nr_irqs = i;
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	return err;
8488c2ecf20Sopenharmony_ci}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ciint dw_edma_probe(struct dw_edma_chip *chip)
8518c2ecf20Sopenharmony_ci{
8528c2ecf20Sopenharmony_ci	struct device *dev;
8538c2ecf20Sopenharmony_ci	struct dw_edma *dw;
8548c2ecf20Sopenharmony_ci	u32 wr_alloc = 0;
8558c2ecf20Sopenharmony_ci	u32 rd_alloc = 0;
8568c2ecf20Sopenharmony_ci	int i, err;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	if (!chip)
8598c2ecf20Sopenharmony_ci		return -EINVAL;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	dev = chip->dev;
8628c2ecf20Sopenharmony_ci	if (!dev)
8638c2ecf20Sopenharmony_ci		return -EINVAL;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	dw = chip->dw;
8668c2ecf20Sopenharmony_ci	if (!dw || !dw->irq || !dw->ops || !dw->ops->irq_vector)
8678c2ecf20Sopenharmony_ci		return -EINVAL;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	raw_spin_lock_init(&dw->lock);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	/* Find out how many write channels are supported by hardware */
8728c2ecf20Sopenharmony_ci	dw->wr_ch_cnt = dw_edma_v0_core_ch_count(dw, EDMA_DIR_WRITE);
8738c2ecf20Sopenharmony_ci	if (!dw->wr_ch_cnt)
8748c2ecf20Sopenharmony_ci		return -EINVAL;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	/* Find out how many read channels are supported by hardware */
8778c2ecf20Sopenharmony_ci	dw->rd_ch_cnt = dw_edma_v0_core_ch_count(dw, EDMA_DIR_READ);
8788c2ecf20Sopenharmony_ci	if (!dw->rd_ch_cnt)
8798c2ecf20Sopenharmony_ci		return -EINVAL;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	dev_vdbg(dev, "Channels:\twrite=%d, read=%d\n",
8828c2ecf20Sopenharmony_ci		 dw->wr_ch_cnt, dw->rd_ch_cnt);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	/* Allocate channels */
8858c2ecf20Sopenharmony_ci	dw->chan = devm_kcalloc(dev, dw->wr_ch_cnt + dw->rd_ch_cnt,
8868c2ecf20Sopenharmony_ci				sizeof(*dw->chan), GFP_KERNEL);
8878c2ecf20Sopenharmony_ci	if (!dw->chan)
8888c2ecf20Sopenharmony_ci		return -ENOMEM;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	snprintf(dw->name, sizeof(dw->name), "dw-edma-core:%d", chip->id);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	/* Disable eDMA, only to establish the ideal initial conditions */
8938c2ecf20Sopenharmony_ci	dw_edma_v0_core_off(dw);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	/* Request IRQs */
8968c2ecf20Sopenharmony_ci	err = dw_edma_irq_request(chip, &wr_alloc, &rd_alloc);
8978c2ecf20Sopenharmony_ci	if (err)
8988c2ecf20Sopenharmony_ci		return err;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	/* Setup write channels */
9018c2ecf20Sopenharmony_ci	err = dw_edma_channel_setup(chip, true, wr_alloc, rd_alloc);
9028c2ecf20Sopenharmony_ci	if (err)
9038c2ecf20Sopenharmony_ci		goto err_irq_free;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	/* Setup read channels */
9068c2ecf20Sopenharmony_ci	err = dw_edma_channel_setup(chip, false, wr_alloc, rd_alloc);
9078c2ecf20Sopenharmony_ci	if (err)
9088c2ecf20Sopenharmony_ci		goto err_irq_free;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	/* Power management */
9118c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	/* Turn debugfs on */
9148c2ecf20Sopenharmony_ci	dw_edma_v0_core_debugfs_on(chip);
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	return 0;
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cierr_irq_free:
9198c2ecf20Sopenharmony_ci	for (i = (dw->nr_irqs - 1); i >= 0; i--)
9208c2ecf20Sopenharmony_ci		free_irq(dw->ops->irq_vector(dev, i), &dw->irq[i]);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	dw->nr_irqs = 0;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	return err;
9258c2ecf20Sopenharmony_ci}
9268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_edma_probe);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ciint dw_edma_remove(struct dw_edma_chip *chip)
9298c2ecf20Sopenharmony_ci{
9308c2ecf20Sopenharmony_ci	struct dw_edma_chan *chan, *_chan;
9318c2ecf20Sopenharmony_ci	struct device *dev = chip->dev;
9328c2ecf20Sopenharmony_ci	struct dw_edma *dw = chip->dw;
9338c2ecf20Sopenharmony_ci	int i;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	/* Disable eDMA */
9368c2ecf20Sopenharmony_ci	dw_edma_v0_core_off(dw);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	/* Free irqs */
9398c2ecf20Sopenharmony_ci	for (i = (dw->nr_irqs - 1); i >= 0; i--)
9408c2ecf20Sopenharmony_ci		free_irq(dw->ops->irq_vector(dev, i), &dw->irq[i]);
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	/* Power management */
9438c2ecf20Sopenharmony_ci	pm_runtime_disable(dev);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	/* Deregister eDMA device */
9468c2ecf20Sopenharmony_ci	dma_async_device_unregister(&dw->wr_edma);
9478c2ecf20Sopenharmony_ci	list_for_each_entry_safe(chan, _chan, &dw->wr_edma.channels,
9488c2ecf20Sopenharmony_ci				 vc.chan.device_node) {
9498c2ecf20Sopenharmony_ci		tasklet_kill(&chan->vc.task);
9508c2ecf20Sopenharmony_ci		list_del(&chan->vc.chan.device_node);
9518c2ecf20Sopenharmony_ci	}
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	dma_async_device_unregister(&dw->rd_edma);
9548c2ecf20Sopenharmony_ci	list_for_each_entry_safe(chan, _chan, &dw->rd_edma.channels,
9558c2ecf20Sopenharmony_ci				 vc.chan.device_node) {
9568c2ecf20Sopenharmony_ci		tasklet_kill(&chan->vc.task);
9578c2ecf20Sopenharmony_ci		list_del(&chan->vc.chan.device_node);
9588c2ecf20Sopenharmony_ci	}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	/* Turn debugfs off */
9618c2ecf20Sopenharmony_ci	dw_edma_v0_core_debugfs_off();
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	return 0;
9648c2ecf20Sopenharmony_ci}
9658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_edma_remove);
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
9688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver");
9698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
970