18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Dmaengine driver base library for DMA controllers, found on SH-based SoCs
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * extracted from shdma.c
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
88c2ecf20Sopenharmony_ci * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
98c2ecf20Sopenharmony_ci * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
108c2ecf20Sopenharmony_ci * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/shdma-base.h>
158c2ecf20Sopenharmony_ci#include <linux/dmaengine.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "../dmaengine.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* DMA descriptor control */
268c2ecf20Sopenharmony_cienum shdma_desc_status {
278c2ecf20Sopenharmony_ci	DESC_IDLE,
288c2ecf20Sopenharmony_ci	DESC_PREPARED,
298c2ecf20Sopenharmony_ci	DESC_SUBMITTED,
308c2ecf20Sopenharmony_ci	DESC_COMPLETED,	/* completed, have to call callback */
318c2ecf20Sopenharmony_ci	DESC_WAITING,	/* callback called, waiting for ack / re-submit */
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define NR_DESCS_PER_CHANNEL 32
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define to_shdma_chan(c) container_of(c, struct shdma_chan, dma_chan)
378c2ecf20Sopenharmony_ci#define to_shdma_dev(d) container_of(d, struct shdma_dev, dma_dev)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/*
408c2ecf20Sopenharmony_ci * For slave DMA we assume, that there is a finite number of DMA slaves in the
418c2ecf20Sopenharmony_ci * system, and that each such slave can only use a finite number of channels.
428c2ecf20Sopenharmony_ci * We use slave channel IDs to make sure, that no such slave channel ID is
438c2ecf20Sopenharmony_ci * allocated more than once.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_cistatic unsigned int slave_num = 256;
468c2ecf20Sopenharmony_cimodule_param(slave_num, uint, 0444);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* A bitmask with slave_num bits */
498c2ecf20Sopenharmony_cistatic unsigned long *shdma_slave_used;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* Called under spin_lock_irq(&schan->chan_lock") */
528c2ecf20Sopenharmony_cistatic void shdma_chan_xfer_ld_queue(struct shdma_chan *schan)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
558c2ecf20Sopenharmony_ci	const struct shdma_ops *ops = sdev->ops;
568c2ecf20Sopenharmony_ci	struct shdma_desc *sdesc;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	/* DMA work check */
598c2ecf20Sopenharmony_ci	if (ops->channel_busy(schan))
608c2ecf20Sopenharmony_ci		return;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/* Find the first not transferred descriptor */
638c2ecf20Sopenharmony_ci	list_for_each_entry(sdesc, &schan->ld_queue, node)
648c2ecf20Sopenharmony_ci		if (sdesc->mark == DESC_SUBMITTED) {
658c2ecf20Sopenharmony_ci			ops->start_xfer(schan, sdesc);
668c2ecf20Sopenharmony_ci			break;
678c2ecf20Sopenharmony_ci		}
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic dma_cookie_t shdma_tx_submit(struct dma_async_tx_descriptor *tx)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct shdma_desc *chunk, *c, *desc =
738c2ecf20Sopenharmony_ci		container_of(tx, struct shdma_desc, async_tx);
748c2ecf20Sopenharmony_ci	struct shdma_chan *schan = to_shdma_chan(tx->chan);
758c2ecf20Sopenharmony_ci	dma_async_tx_callback callback = tx->callback;
768c2ecf20Sopenharmony_ci	dma_cookie_t cookie;
778c2ecf20Sopenharmony_ci	bool power_up;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	spin_lock_irq(&schan->chan_lock);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	power_up = list_empty(&schan->ld_queue);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	cookie = dma_cookie_assign(tx);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/* Mark all chunks of this descriptor as submitted, move to the queue */
868c2ecf20Sopenharmony_ci	list_for_each_entry_safe(chunk, c, desc->node.prev, node) {
878c2ecf20Sopenharmony_ci		/*
888c2ecf20Sopenharmony_ci		 * All chunks are on the global ld_free, so, we have to find
898c2ecf20Sopenharmony_ci		 * the end of the chain ourselves
908c2ecf20Sopenharmony_ci		 */
918c2ecf20Sopenharmony_ci		if (chunk != desc && (chunk->mark == DESC_IDLE ||
928c2ecf20Sopenharmony_ci				      chunk->async_tx.cookie > 0 ||
938c2ecf20Sopenharmony_ci				      chunk->async_tx.cookie == -EBUSY ||
948c2ecf20Sopenharmony_ci				      &chunk->node == &schan->ld_free))
958c2ecf20Sopenharmony_ci			break;
968c2ecf20Sopenharmony_ci		chunk->mark = DESC_SUBMITTED;
978c2ecf20Sopenharmony_ci		if (chunk->chunks == 1) {
988c2ecf20Sopenharmony_ci			chunk->async_tx.callback = callback;
998c2ecf20Sopenharmony_ci			chunk->async_tx.callback_param = tx->callback_param;
1008c2ecf20Sopenharmony_ci		} else {
1018c2ecf20Sopenharmony_ci			/* Callback goes to the last chunk */
1028c2ecf20Sopenharmony_ci			chunk->async_tx.callback = NULL;
1038c2ecf20Sopenharmony_ci		}
1048c2ecf20Sopenharmony_ci		chunk->cookie = cookie;
1058c2ecf20Sopenharmony_ci		list_move_tail(&chunk->node, &schan->ld_queue);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		dev_dbg(schan->dev, "submit #%d@%p on %d\n",
1088c2ecf20Sopenharmony_ci			tx->cookie, &chunk->async_tx, schan->id);
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (power_up) {
1128c2ecf20Sopenharmony_ci		int ret;
1138c2ecf20Sopenharmony_ci		schan->pm_state = SHDMA_PM_BUSY;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci		ret = pm_runtime_get(schan->dev);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		spin_unlock_irq(&schan->chan_lock);
1188c2ecf20Sopenharmony_ci		if (ret < 0)
1198c2ecf20Sopenharmony_ci			dev_err(schan->dev, "%s(): GET = %d\n", __func__, ret);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		pm_runtime_barrier(schan->dev);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		spin_lock_irq(&schan->chan_lock);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci		/* Have we been reset, while waiting? */
1268c2ecf20Sopenharmony_ci		if (schan->pm_state != SHDMA_PM_ESTABLISHED) {
1278c2ecf20Sopenharmony_ci			struct shdma_dev *sdev =
1288c2ecf20Sopenharmony_ci				to_shdma_dev(schan->dma_chan.device);
1298c2ecf20Sopenharmony_ci			const struct shdma_ops *ops = sdev->ops;
1308c2ecf20Sopenharmony_ci			dev_dbg(schan->dev, "Bring up channel %d\n",
1318c2ecf20Sopenharmony_ci				schan->id);
1328c2ecf20Sopenharmony_ci			/*
1338c2ecf20Sopenharmony_ci			 * TODO: .xfer_setup() might fail on some platforms.
1348c2ecf20Sopenharmony_ci			 * Make it int then, on error remove chunks from the
1358c2ecf20Sopenharmony_ci			 * queue again
1368c2ecf20Sopenharmony_ci			 */
1378c2ecf20Sopenharmony_ci			ops->setup_xfer(schan, schan->slave_id);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci			if (schan->pm_state == SHDMA_PM_PENDING)
1408c2ecf20Sopenharmony_ci				shdma_chan_xfer_ld_queue(schan);
1418c2ecf20Sopenharmony_ci			schan->pm_state = SHDMA_PM_ESTABLISHED;
1428c2ecf20Sopenharmony_ci		}
1438c2ecf20Sopenharmony_ci	} else {
1448c2ecf20Sopenharmony_ci		/*
1458c2ecf20Sopenharmony_ci		 * Tell .device_issue_pending() not to run the queue, interrupts
1468c2ecf20Sopenharmony_ci		 * will do it anyway
1478c2ecf20Sopenharmony_ci		 */
1488c2ecf20Sopenharmony_ci		schan->pm_state = SHDMA_PM_PENDING;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	spin_unlock_irq(&schan->chan_lock);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return cookie;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/* Called with desc_lock held */
1578c2ecf20Sopenharmony_cistatic struct shdma_desc *shdma_get_desc(struct shdma_chan *schan)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct shdma_desc *sdesc;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	list_for_each_entry(sdesc, &schan->ld_free, node)
1628c2ecf20Sopenharmony_ci		if (sdesc->mark != DESC_PREPARED) {
1638c2ecf20Sopenharmony_ci			BUG_ON(sdesc->mark != DESC_IDLE);
1648c2ecf20Sopenharmony_ci			list_del(&sdesc->node);
1658c2ecf20Sopenharmony_ci			return sdesc;
1668c2ecf20Sopenharmony_ci		}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	return NULL;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int shdma_setup_slave(struct shdma_chan *schan, dma_addr_t slave_addr)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
1748c2ecf20Sopenharmony_ci	const struct shdma_ops *ops = sdev->ops;
1758c2ecf20Sopenharmony_ci	int ret, match;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (schan->dev->of_node) {
1788c2ecf20Sopenharmony_ci		match = schan->hw_req;
1798c2ecf20Sopenharmony_ci		ret = ops->set_slave(schan, match, slave_addr, true);
1808c2ecf20Sopenharmony_ci		if (ret < 0)
1818c2ecf20Sopenharmony_ci			return ret;
1828c2ecf20Sopenharmony_ci	} else {
1838c2ecf20Sopenharmony_ci		match = schan->real_slave_id;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (schan->real_slave_id < 0 || schan->real_slave_id >= slave_num)
1878c2ecf20Sopenharmony_ci		return -EINVAL;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (test_and_set_bit(schan->real_slave_id, shdma_slave_used))
1908c2ecf20Sopenharmony_ci		return -EBUSY;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	ret = ops->set_slave(schan, match, slave_addr, false);
1938c2ecf20Sopenharmony_ci	if (ret < 0) {
1948c2ecf20Sopenharmony_ci		clear_bit(schan->real_slave_id, shdma_slave_used);
1958c2ecf20Sopenharmony_ci		return ret;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	schan->slave_id = schan->real_slave_id;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return 0;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic int shdma_alloc_chan_resources(struct dma_chan *chan)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct shdma_chan *schan = to_shdma_chan(chan);
2068c2ecf20Sopenharmony_ci	struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
2078c2ecf20Sopenharmony_ci	const struct shdma_ops *ops = sdev->ops;
2088c2ecf20Sopenharmony_ci	struct shdma_desc *desc;
2098c2ecf20Sopenharmony_ci	struct shdma_slave *slave = chan->private;
2108c2ecf20Sopenharmony_ci	int ret, i;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/*
2138c2ecf20Sopenharmony_ci	 * This relies on the guarantee from dmaengine that alloc_chan_resources
2148c2ecf20Sopenharmony_ci	 * never runs concurrently with itself or free_chan_resources.
2158c2ecf20Sopenharmony_ci	 */
2168c2ecf20Sopenharmony_ci	if (slave) {
2178c2ecf20Sopenharmony_ci		/* Legacy mode: .private is set in filter */
2188c2ecf20Sopenharmony_ci		schan->real_slave_id = slave->slave_id;
2198c2ecf20Sopenharmony_ci		ret = shdma_setup_slave(schan, 0);
2208c2ecf20Sopenharmony_ci		if (ret < 0)
2218c2ecf20Sopenharmony_ci			goto esetslave;
2228c2ecf20Sopenharmony_ci	} else {
2238c2ecf20Sopenharmony_ci		/* Normal mode: real_slave_id was set by filter */
2248c2ecf20Sopenharmony_ci		schan->slave_id = -EINVAL;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	schan->desc = kcalloc(NR_DESCS_PER_CHANNEL,
2288c2ecf20Sopenharmony_ci			      sdev->desc_size, GFP_KERNEL);
2298c2ecf20Sopenharmony_ci	if (!schan->desc) {
2308c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2318c2ecf20Sopenharmony_ci		goto edescalloc;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci	schan->desc_num = NR_DESCS_PER_CHANNEL;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	for (i = 0; i < NR_DESCS_PER_CHANNEL; i++) {
2368c2ecf20Sopenharmony_ci		desc = ops->embedded_desc(schan->desc, i);
2378c2ecf20Sopenharmony_ci		dma_async_tx_descriptor_init(&desc->async_tx,
2388c2ecf20Sopenharmony_ci					     &schan->dma_chan);
2398c2ecf20Sopenharmony_ci		desc->async_tx.tx_submit = shdma_tx_submit;
2408c2ecf20Sopenharmony_ci		desc->mark = DESC_IDLE;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci		list_add(&desc->node, &schan->ld_free);
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return NR_DESCS_PER_CHANNEL;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ciedescalloc:
2488c2ecf20Sopenharmony_ci	if (slave)
2498c2ecf20Sopenharmony_ciesetslave:
2508c2ecf20Sopenharmony_ci		clear_bit(slave->slave_id, shdma_slave_used);
2518c2ecf20Sopenharmony_ci	chan->private = NULL;
2528c2ecf20Sopenharmony_ci	return ret;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci/*
2568c2ecf20Sopenharmony_ci * This is the standard shdma filter function to be used as a replacement to the
2578c2ecf20Sopenharmony_ci * "old" method, using the .private pointer.
2588c2ecf20Sopenharmony_ci * You always have to pass a valid slave id as the argument, old drivers that
2598c2ecf20Sopenharmony_ci * pass ERR_PTR(-EINVAL) as a filter parameter and set it up in dma_slave_config
2608c2ecf20Sopenharmony_ci * need to be updated so we can remove the slave_id field from dma_slave_config.
2618c2ecf20Sopenharmony_ci * parameter. If this filter is used, the slave driver, after calling
2628c2ecf20Sopenharmony_ci * dma_request_channel(), will also have to call dmaengine_slave_config() with
2638c2ecf20Sopenharmony_ci * .direction, and either .src_addr or .dst_addr set.
2648c2ecf20Sopenharmony_ci *
2658c2ecf20Sopenharmony_ci * NOTE: this filter doesn't support multiple DMAC drivers with the DMA_SLAVE
2668c2ecf20Sopenharmony_ci * capability! If this becomes a requirement, hardware glue drivers, using this
2678c2ecf20Sopenharmony_ci * services would have to provide their own filters, which first would check
2688c2ecf20Sopenharmony_ci * the device driver, similar to how other DMAC drivers, e.g., sa11x0-dma.c, do
2698c2ecf20Sopenharmony_ci * this, and only then, in case of a match, call this common filter.
2708c2ecf20Sopenharmony_ci * NOTE 2: This filter function is also used in the DT case by shdma_of_xlate().
2718c2ecf20Sopenharmony_ci * In that case the MID-RID value is used for slave channel filtering and is
2728c2ecf20Sopenharmony_ci * passed to this function in the "arg" parameter.
2738c2ecf20Sopenharmony_ci */
2748c2ecf20Sopenharmony_cibool shdma_chan_filter(struct dma_chan *chan, void *arg)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct shdma_chan *schan;
2778c2ecf20Sopenharmony_ci	struct shdma_dev *sdev;
2788c2ecf20Sopenharmony_ci	int slave_id = (long)arg;
2798c2ecf20Sopenharmony_ci	int ret;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	/* Only support channels handled by this driver. */
2828c2ecf20Sopenharmony_ci	if (chan->device->device_alloc_chan_resources !=
2838c2ecf20Sopenharmony_ci	    shdma_alloc_chan_resources)
2848c2ecf20Sopenharmony_ci		return false;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	schan = to_shdma_chan(chan);
2878c2ecf20Sopenharmony_ci	sdev = to_shdma_dev(chan->device);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/*
2908c2ecf20Sopenharmony_ci	 * For DT, the schan->slave_id field is generated by the
2918c2ecf20Sopenharmony_ci	 * set_slave function from the slave ID that is passed in
2928c2ecf20Sopenharmony_ci	 * from xlate. For the non-DT case, the slave ID is
2938c2ecf20Sopenharmony_ci	 * directly passed into the filter function by the driver
2948c2ecf20Sopenharmony_ci	 */
2958c2ecf20Sopenharmony_ci	if (schan->dev->of_node) {
2968c2ecf20Sopenharmony_ci		ret = sdev->ops->set_slave(schan, slave_id, 0, true);
2978c2ecf20Sopenharmony_ci		if (ret < 0)
2988c2ecf20Sopenharmony_ci			return false;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci		schan->real_slave_id = schan->slave_id;
3018c2ecf20Sopenharmony_ci		return true;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (slave_id < 0) {
3058c2ecf20Sopenharmony_ci		/* No slave requested - arbitrary channel */
3068c2ecf20Sopenharmony_ci		dev_warn(sdev->dma_dev.dev, "invalid slave ID passed to dma_request_slave\n");
3078c2ecf20Sopenharmony_ci		return true;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (slave_id >= slave_num)
3118c2ecf20Sopenharmony_ci		return false;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	ret = sdev->ops->set_slave(schan, slave_id, 0, true);
3148c2ecf20Sopenharmony_ci	if (ret < 0)
3158c2ecf20Sopenharmony_ci		return false;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	schan->real_slave_id = slave_id;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	return true;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(shdma_chan_filter);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic dma_async_tx_callback __ld_cleanup(struct shdma_chan *schan, bool all)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct shdma_desc *desc, *_desc;
3268c2ecf20Sopenharmony_ci	/* Is the "exposed" head of a chain acked? */
3278c2ecf20Sopenharmony_ci	bool head_acked = false;
3288c2ecf20Sopenharmony_ci	dma_cookie_t cookie = 0;
3298c2ecf20Sopenharmony_ci	dma_async_tx_callback callback = NULL;
3308c2ecf20Sopenharmony_ci	struct dmaengine_desc_callback cb;
3318c2ecf20Sopenharmony_ci	unsigned long flags;
3328c2ecf20Sopenharmony_ci	LIST_HEAD(cyclic_list);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	memset(&cb, 0, sizeof(cb));
3358c2ecf20Sopenharmony_ci	spin_lock_irqsave(&schan->chan_lock, flags);
3368c2ecf20Sopenharmony_ci	list_for_each_entry_safe(desc, _desc, &schan->ld_queue, node) {
3378c2ecf20Sopenharmony_ci		struct dma_async_tx_descriptor *tx = &desc->async_tx;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		BUG_ON(tx->cookie > 0 && tx->cookie != desc->cookie);
3408c2ecf20Sopenharmony_ci		BUG_ON(desc->mark != DESC_SUBMITTED &&
3418c2ecf20Sopenharmony_ci		       desc->mark != DESC_COMPLETED &&
3428c2ecf20Sopenharmony_ci		       desc->mark != DESC_WAITING);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci		/*
3458c2ecf20Sopenharmony_ci		 * queue is ordered, and we use this loop to (1) clean up all
3468c2ecf20Sopenharmony_ci		 * completed descriptors, and to (2) update descriptor flags of
3478c2ecf20Sopenharmony_ci		 * any chunks in a (partially) completed chain
3488c2ecf20Sopenharmony_ci		 */
3498c2ecf20Sopenharmony_ci		if (!all && desc->mark == DESC_SUBMITTED &&
3508c2ecf20Sopenharmony_ci		    desc->cookie != cookie)
3518c2ecf20Sopenharmony_ci			break;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		if (tx->cookie > 0)
3548c2ecf20Sopenharmony_ci			cookie = tx->cookie;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci		if (desc->mark == DESC_COMPLETED && desc->chunks == 1) {
3578c2ecf20Sopenharmony_ci			if (schan->dma_chan.completed_cookie != desc->cookie - 1)
3588c2ecf20Sopenharmony_ci				dev_dbg(schan->dev,
3598c2ecf20Sopenharmony_ci					"Completing cookie %d, expected %d\n",
3608c2ecf20Sopenharmony_ci					desc->cookie,
3618c2ecf20Sopenharmony_ci					schan->dma_chan.completed_cookie + 1);
3628c2ecf20Sopenharmony_ci			schan->dma_chan.completed_cookie = desc->cookie;
3638c2ecf20Sopenharmony_ci		}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		/* Call callback on the last chunk */
3668c2ecf20Sopenharmony_ci		if (desc->mark == DESC_COMPLETED && tx->callback) {
3678c2ecf20Sopenharmony_ci			desc->mark = DESC_WAITING;
3688c2ecf20Sopenharmony_ci			dmaengine_desc_get_callback(tx, &cb);
3698c2ecf20Sopenharmony_ci			callback = tx->callback;
3708c2ecf20Sopenharmony_ci			dev_dbg(schan->dev, "descriptor #%d@%p on %d callback\n",
3718c2ecf20Sopenharmony_ci				tx->cookie, tx, schan->id);
3728c2ecf20Sopenharmony_ci			BUG_ON(desc->chunks != 1);
3738c2ecf20Sopenharmony_ci			break;
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		if (tx->cookie > 0 || tx->cookie == -EBUSY) {
3778c2ecf20Sopenharmony_ci			if (desc->mark == DESC_COMPLETED) {
3788c2ecf20Sopenharmony_ci				BUG_ON(tx->cookie < 0);
3798c2ecf20Sopenharmony_ci				desc->mark = DESC_WAITING;
3808c2ecf20Sopenharmony_ci			}
3818c2ecf20Sopenharmony_ci			head_acked = async_tx_test_ack(tx);
3828c2ecf20Sopenharmony_ci		} else {
3838c2ecf20Sopenharmony_ci			switch (desc->mark) {
3848c2ecf20Sopenharmony_ci			case DESC_COMPLETED:
3858c2ecf20Sopenharmony_ci				desc->mark = DESC_WAITING;
3868c2ecf20Sopenharmony_ci				fallthrough;
3878c2ecf20Sopenharmony_ci			case DESC_WAITING:
3888c2ecf20Sopenharmony_ci				if (head_acked)
3898c2ecf20Sopenharmony_ci					async_tx_ack(&desc->async_tx);
3908c2ecf20Sopenharmony_ci			}
3918c2ecf20Sopenharmony_ci		}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		dev_dbg(schan->dev, "descriptor %p #%d completed.\n",
3948c2ecf20Sopenharmony_ci			tx, tx->cookie);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci		if (((desc->mark == DESC_COMPLETED ||
3978c2ecf20Sopenharmony_ci		      desc->mark == DESC_WAITING) &&
3988c2ecf20Sopenharmony_ci		     async_tx_test_ack(&desc->async_tx)) || all) {
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci			if (all || !desc->cyclic) {
4018c2ecf20Sopenharmony_ci				/* Remove from ld_queue list */
4028c2ecf20Sopenharmony_ci				desc->mark = DESC_IDLE;
4038c2ecf20Sopenharmony_ci				list_move(&desc->node, &schan->ld_free);
4048c2ecf20Sopenharmony_ci			} else {
4058c2ecf20Sopenharmony_ci				/* reuse as cyclic */
4068c2ecf20Sopenharmony_ci				desc->mark = DESC_SUBMITTED;
4078c2ecf20Sopenharmony_ci				list_move_tail(&desc->node, &cyclic_list);
4088c2ecf20Sopenharmony_ci			}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci			if (list_empty(&schan->ld_queue)) {
4118c2ecf20Sopenharmony_ci				dev_dbg(schan->dev, "Bring down channel %d\n", schan->id);
4128c2ecf20Sopenharmony_ci				pm_runtime_put(schan->dev);
4138c2ecf20Sopenharmony_ci				schan->pm_state = SHDMA_PM_ESTABLISHED;
4148c2ecf20Sopenharmony_ci			} else if (schan->pm_state == SHDMA_PM_PENDING) {
4158c2ecf20Sopenharmony_ci				shdma_chan_xfer_ld_queue(schan);
4168c2ecf20Sopenharmony_ci			}
4178c2ecf20Sopenharmony_ci		}
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (all && !callback)
4218c2ecf20Sopenharmony_ci		/*
4228c2ecf20Sopenharmony_ci		 * Terminating and the loop completed normally: forgive
4238c2ecf20Sopenharmony_ci		 * uncompleted cookies
4248c2ecf20Sopenharmony_ci		 */
4258c2ecf20Sopenharmony_ci		schan->dma_chan.completed_cookie = schan->dma_chan.cookie;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	list_splice_tail(&cyclic_list, &schan->ld_queue);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&schan->chan_lock, flags);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	dmaengine_desc_callback_invoke(&cb, NULL);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	return callback;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci/*
4378c2ecf20Sopenharmony_ci * shdma_chan_ld_cleanup - Clean up link descriptors
4388c2ecf20Sopenharmony_ci *
4398c2ecf20Sopenharmony_ci * Clean up the ld_queue of DMA channel.
4408c2ecf20Sopenharmony_ci */
4418c2ecf20Sopenharmony_cistatic void shdma_chan_ld_cleanup(struct shdma_chan *schan, bool all)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	while (__ld_cleanup(schan, all))
4448c2ecf20Sopenharmony_ci		;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci/*
4488c2ecf20Sopenharmony_ci * shdma_free_chan_resources - Free all resources of the channel.
4498c2ecf20Sopenharmony_ci */
4508c2ecf20Sopenharmony_cistatic void shdma_free_chan_resources(struct dma_chan *chan)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct shdma_chan *schan = to_shdma_chan(chan);
4538c2ecf20Sopenharmony_ci	struct shdma_dev *sdev = to_shdma_dev(chan->device);
4548c2ecf20Sopenharmony_ci	const struct shdma_ops *ops = sdev->ops;
4558c2ecf20Sopenharmony_ci	LIST_HEAD(list);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* Protect against ISR */
4588c2ecf20Sopenharmony_ci	spin_lock_irq(&schan->chan_lock);
4598c2ecf20Sopenharmony_ci	ops->halt_channel(schan);
4608c2ecf20Sopenharmony_ci	spin_unlock_irq(&schan->chan_lock);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	/* Now no new interrupts will occur */
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	/* Prepared and not submitted descriptors can still be on the queue */
4658c2ecf20Sopenharmony_ci	if (!list_empty(&schan->ld_queue))
4668c2ecf20Sopenharmony_ci		shdma_chan_ld_cleanup(schan, true);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	if (schan->slave_id >= 0) {
4698c2ecf20Sopenharmony_ci		/* The caller is holding dma_list_mutex */
4708c2ecf20Sopenharmony_ci		clear_bit(schan->slave_id, shdma_slave_used);
4718c2ecf20Sopenharmony_ci		chan->private = NULL;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	schan->real_slave_id = 0;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	spin_lock_irq(&schan->chan_lock);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	list_splice_init(&schan->ld_free, &list);
4798c2ecf20Sopenharmony_ci	schan->desc_num = 0;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	spin_unlock_irq(&schan->chan_lock);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	kfree(schan->desc);
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci/**
4878c2ecf20Sopenharmony_ci * shdma_add_desc - get, set up and return one transfer descriptor
4888c2ecf20Sopenharmony_ci * @schan:	DMA channel
4898c2ecf20Sopenharmony_ci * @flags:	DMA transfer flags
4908c2ecf20Sopenharmony_ci * @dst:	destination DMA address, incremented when direction equals
4918c2ecf20Sopenharmony_ci *		DMA_DEV_TO_MEM or DMA_MEM_TO_MEM
4928c2ecf20Sopenharmony_ci * @src:	source DMA address, incremented when direction equals
4938c2ecf20Sopenharmony_ci *		DMA_MEM_TO_DEV or DMA_MEM_TO_MEM
4948c2ecf20Sopenharmony_ci * @len:	DMA transfer length
4958c2ecf20Sopenharmony_ci * @first:	if NULL, set to the current descriptor and cookie set to -EBUSY
4968c2ecf20Sopenharmony_ci * @direction:	needed for slave DMA to decide which address to keep constant,
4978c2ecf20Sopenharmony_ci *		equals DMA_MEM_TO_MEM for MEMCPY
4988c2ecf20Sopenharmony_ci * Returns 0 or an error
4998c2ecf20Sopenharmony_ci * Locks: called with desc_lock held
5008c2ecf20Sopenharmony_ci */
5018c2ecf20Sopenharmony_cistatic struct shdma_desc *shdma_add_desc(struct shdma_chan *schan,
5028c2ecf20Sopenharmony_ci	unsigned long flags, dma_addr_t *dst, dma_addr_t *src, size_t *len,
5038c2ecf20Sopenharmony_ci	struct shdma_desc **first, enum dma_transfer_direction direction)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
5068c2ecf20Sopenharmony_ci	const struct shdma_ops *ops = sdev->ops;
5078c2ecf20Sopenharmony_ci	struct shdma_desc *new;
5088c2ecf20Sopenharmony_ci	size_t copy_size = *len;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	if (!copy_size)
5118c2ecf20Sopenharmony_ci		return NULL;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/* Allocate the link descriptor from the free list */
5148c2ecf20Sopenharmony_ci	new = shdma_get_desc(schan);
5158c2ecf20Sopenharmony_ci	if (!new) {
5168c2ecf20Sopenharmony_ci		dev_err(schan->dev, "No free link descriptor available\n");
5178c2ecf20Sopenharmony_ci		return NULL;
5188c2ecf20Sopenharmony_ci	}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	ops->desc_setup(schan, new, *src, *dst, &copy_size);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	if (!*first) {
5238c2ecf20Sopenharmony_ci		/* First desc */
5248c2ecf20Sopenharmony_ci		new->async_tx.cookie = -EBUSY;
5258c2ecf20Sopenharmony_ci		*first = new;
5268c2ecf20Sopenharmony_ci	} else {
5278c2ecf20Sopenharmony_ci		/* Other desc - invisible to the user */
5288c2ecf20Sopenharmony_ci		new->async_tx.cookie = -EINVAL;
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	dev_dbg(schan->dev,
5328c2ecf20Sopenharmony_ci		"chaining (%zu/%zu)@%pad -> %pad with %p, cookie %d\n",
5338c2ecf20Sopenharmony_ci		copy_size, *len, src, dst, &new->async_tx,
5348c2ecf20Sopenharmony_ci		new->async_tx.cookie);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	new->mark = DESC_PREPARED;
5378c2ecf20Sopenharmony_ci	new->async_tx.flags = flags;
5388c2ecf20Sopenharmony_ci	new->direction = direction;
5398c2ecf20Sopenharmony_ci	new->partial = 0;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	*len -= copy_size;
5428c2ecf20Sopenharmony_ci	if (direction == DMA_MEM_TO_MEM || direction == DMA_MEM_TO_DEV)
5438c2ecf20Sopenharmony_ci		*src += copy_size;
5448c2ecf20Sopenharmony_ci	if (direction == DMA_MEM_TO_MEM || direction == DMA_DEV_TO_MEM)
5458c2ecf20Sopenharmony_ci		*dst += copy_size;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	return new;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci/*
5518c2ecf20Sopenharmony_ci * shdma_prep_sg - prepare transfer descriptors from an SG list
5528c2ecf20Sopenharmony_ci *
5538c2ecf20Sopenharmony_ci * Common routine for public (MEMCPY) and slave DMA. The MEMCPY case is also
5548c2ecf20Sopenharmony_ci * converted to scatter-gather to guarantee consistent locking and a correct
5558c2ecf20Sopenharmony_ci * list manipulation. For slave DMA direction carries the usual meaning, and,
5568c2ecf20Sopenharmony_ci * logically, the SG list is RAM and the addr variable contains slave address,
5578c2ecf20Sopenharmony_ci * e.g., the FIFO I/O register. For MEMCPY direction equals DMA_MEM_TO_MEM
5588c2ecf20Sopenharmony_ci * and the SG list contains only one element and points at the source buffer.
5598c2ecf20Sopenharmony_ci */
5608c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *shdma_prep_sg(struct shdma_chan *schan,
5618c2ecf20Sopenharmony_ci	struct scatterlist *sgl, unsigned int sg_len, dma_addr_t *addr,
5628c2ecf20Sopenharmony_ci	enum dma_transfer_direction direction, unsigned long flags, bool cyclic)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	struct scatterlist *sg;
5658c2ecf20Sopenharmony_ci	struct shdma_desc *first = NULL, *new = NULL /* compiler... */;
5668c2ecf20Sopenharmony_ci	LIST_HEAD(tx_list);
5678c2ecf20Sopenharmony_ci	int chunks = 0;
5688c2ecf20Sopenharmony_ci	unsigned long irq_flags;
5698c2ecf20Sopenharmony_ci	int i;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	for_each_sg(sgl, sg, sg_len, i)
5728c2ecf20Sopenharmony_ci		chunks += DIV_ROUND_UP(sg_dma_len(sg), schan->max_xfer_len);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	/* Have to lock the whole loop to protect against concurrent release */
5758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&schan->chan_lock, irq_flags);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	/*
5788c2ecf20Sopenharmony_ci	 * Chaining:
5798c2ecf20Sopenharmony_ci	 * first descriptor is what user is dealing with in all API calls, its
5808c2ecf20Sopenharmony_ci	 *	cookie is at first set to -EBUSY, at tx-submit to a positive
5818c2ecf20Sopenharmony_ci	 *	number
5828c2ecf20Sopenharmony_ci	 * if more than one chunk is needed further chunks have cookie = -EINVAL
5838c2ecf20Sopenharmony_ci	 * the last chunk, if not equal to the first, has cookie = -ENOSPC
5848c2ecf20Sopenharmony_ci	 * all chunks are linked onto the tx_list head with their .node heads
5858c2ecf20Sopenharmony_ci	 *	only during this function, then they are immediately spliced
5868c2ecf20Sopenharmony_ci	 *	back onto the free list in form of a chain
5878c2ecf20Sopenharmony_ci	 */
5888c2ecf20Sopenharmony_ci	for_each_sg(sgl, sg, sg_len, i) {
5898c2ecf20Sopenharmony_ci		dma_addr_t sg_addr = sg_dma_address(sg);
5908c2ecf20Sopenharmony_ci		size_t len = sg_dma_len(sg);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci		if (!len)
5938c2ecf20Sopenharmony_ci			goto err_get_desc;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci		do {
5968c2ecf20Sopenharmony_ci			dev_dbg(schan->dev, "Add SG #%d@%p[%zu], dma %pad\n",
5978c2ecf20Sopenharmony_ci				i, sg, len, &sg_addr);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci			if (direction == DMA_DEV_TO_MEM)
6008c2ecf20Sopenharmony_ci				new = shdma_add_desc(schan, flags,
6018c2ecf20Sopenharmony_ci						&sg_addr, addr, &len, &first,
6028c2ecf20Sopenharmony_ci						direction);
6038c2ecf20Sopenharmony_ci			else
6048c2ecf20Sopenharmony_ci				new = shdma_add_desc(schan, flags,
6058c2ecf20Sopenharmony_ci						addr, &sg_addr, &len, &first,
6068c2ecf20Sopenharmony_ci						direction);
6078c2ecf20Sopenharmony_ci			if (!new)
6088c2ecf20Sopenharmony_ci				goto err_get_desc;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci			new->cyclic = cyclic;
6118c2ecf20Sopenharmony_ci			if (cyclic)
6128c2ecf20Sopenharmony_ci				new->chunks = 1;
6138c2ecf20Sopenharmony_ci			else
6148c2ecf20Sopenharmony_ci				new->chunks = chunks--;
6158c2ecf20Sopenharmony_ci			list_add_tail(&new->node, &tx_list);
6168c2ecf20Sopenharmony_ci		} while (len);
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (new != first)
6208c2ecf20Sopenharmony_ci		new->async_tx.cookie = -ENOSPC;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	/* Put them back on the free list, so, they don't get lost */
6238c2ecf20Sopenharmony_ci	list_splice_tail(&tx_list, &schan->ld_free);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&schan->chan_lock, irq_flags);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	return &first->async_tx;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_cierr_get_desc:
6308c2ecf20Sopenharmony_ci	list_for_each_entry(new, &tx_list, node)
6318c2ecf20Sopenharmony_ci		new->mark = DESC_IDLE;
6328c2ecf20Sopenharmony_ci	list_splice(&tx_list, &schan->ld_free);
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&schan->chan_lock, irq_flags);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	return NULL;
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *shdma_prep_memcpy(
6408c2ecf20Sopenharmony_ci	struct dma_chan *chan, dma_addr_t dma_dest, dma_addr_t dma_src,
6418c2ecf20Sopenharmony_ci	size_t len, unsigned long flags)
6428c2ecf20Sopenharmony_ci{
6438c2ecf20Sopenharmony_ci	struct shdma_chan *schan = to_shdma_chan(chan);
6448c2ecf20Sopenharmony_ci	struct scatterlist sg;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	if (!chan || !len)
6478c2ecf20Sopenharmony_ci		return NULL;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	BUG_ON(!schan->desc_num);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	sg_init_table(&sg, 1);
6528c2ecf20Sopenharmony_ci	sg_set_page(&sg, pfn_to_page(PFN_DOWN(dma_src)), len,
6538c2ecf20Sopenharmony_ci		    offset_in_page(dma_src));
6548c2ecf20Sopenharmony_ci	sg_dma_address(&sg) = dma_src;
6558c2ecf20Sopenharmony_ci	sg_dma_len(&sg) = len;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	return shdma_prep_sg(schan, &sg, 1, &dma_dest, DMA_MEM_TO_MEM,
6588c2ecf20Sopenharmony_ci			     flags, false);
6598c2ecf20Sopenharmony_ci}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *shdma_prep_slave_sg(
6628c2ecf20Sopenharmony_ci	struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
6638c2ecf20Sopenharmony_ci	enum dma_transfer_direction direction, unsigned long flags, void *context)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	struct shdma_chan *schan = to_shdma_chan(chan);
6668c2ecf20Sopenharmony_ci	struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
6678c2ecf20Sopenharmony_ci	const struct shdma_ops *ops = sdev->ops;
6688c2ecf20Sopenharmony_ci	int slave_id = schan->slave_id;
6698c2ecf20Sopenharmony_ci	dma_addr_t slave_addr;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	if (!chan)
6728c2ecf20Sopenharmony_ci		return NULL;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	BUG_ON(!schan->desc_num);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	/* Someone calling slave DMA on a generic channel? */
6778c2ecf20Sopenharmony_ci	if (slave_id < 0 || !sg_len) {
6788c2ecf20Sopenharmony_ci		dev_warn(schan->dev, "%s: bad parameter: len=%d, id=%d\n",
6798c2ecf20Sopenharmony_ci			 __func__, sg_len, slave_id);
6808c2ecf20Sopenharmony_ci		return NULL;
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	slave_addr = ops->slave_addr(schan);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	return shdma_prep_sg(schan, sgl, sg_len, &slave_addr,
6868c2ecf20Sopenharmony_ci			     direction, flags, false);
6878c2ecf20Sopenharmony_ci}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci#define SHDMA_MAX_SG_LEN 32
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *shdma_prep_dma_cyclic(
6928c2ecf20Sopenharmony_ci	struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
6938c2ecf20Sopenharmony_ci	size_t period_len, enum dma_transfer_direction direction,
6948c2ecf20Sopenharmony_ci	unsigned long flags)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	struct shdma_chan *schan = to_shdma_chan(chan);
6978c2ecf20Sopenharmony_ci	struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
6988c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *desc;
6998c2ecf20Sopenharmony_ci	const struct shdma_ops *ops = sdev->ops;
7008c2ecf20Sopenharmony_ci	unsigned int sg_len = buf_len / period_len;
7018c2ecf20Sopenharmony_ci	int slave_id = schan->slave_id;
7028c2ecf20Sopenharmony_ci	dma_addr_t slave_addr;
7038c2ecf20Sopenharmony_ci	struct scatterlist *sgl;
7048c2ecf20Sopenharmony_ci	int i;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	if (!chan)
7078c2ecf20Sopenharmony_ci		return NULL;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	BUG_ON(!schan->desc_num);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if (sg_len > SHDMA_MAX_SG_LEN) {
7128c2ecf20Sopenharmony_ci		dev_err(schan->dev, "sg length %d exceeds limit %d",
7138c2ecf20Sopenharmony_ci				sg_len, SHDMA_MAX_SG_LEN);
7148c2ecf20Sopenharmony_ci		return NULL;
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	/* Someone calling slave DMA on a generic channel? */
7188c2ecf20Sopenharmony_ci	if (slave_id < 0 || (buf_len < period_len)) {
7198c2ecf20Sopenharmony_ci		dev_warn(schan->dev,
7208c2ecf20Sopenharmony_ci			"%s: bad parameter: buf_len=%zu, period_len=%zu, id=%d\n",
7218c2ecf20Sopenharmony_ci			__func__, buf_len, period_len, slave_id);
7228c2ecf20Sopenharmony_ci		return NULL;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	slave_addr = ops->slave_addr(schan);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	/*
7288c2ecf20Sopenharmony_ci	 * Allocate the sg list dynamically as it would consumer too much stack
7298c2ecf20Sopenharmony_ci	 * space.
7308c2ecf20Sopenharmony_ci	 */
7318c2ecf20Sopenharmony_ci	sgl = kmalloc_array(sg_len, sizeof(*sgl), GFP_KERNEL);
7328c2ecf20Sopenharmony_ci	if (!sgl)
7338c2ecf20Sopenharmony_ci		return NULL;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	sg_init_table(sgl, sg_len);
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	for (i = 0; i < sg_len; i++) {
7388c2ecf20Sopenharmony_ci		dma_addr_t src = buf_addr + (period_len * i);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci		sg_set_page(&sgl[i], pfn_to_page(PFN_DOWN(src)), period_len,
7418c2ecf20Sopenharmony_ci			    offset_in_page(src));
7428c2ecf20Sopenharmony_ci		sg_dma_address(&sgl[i]) = src;
7438c2ecf20Sopenharmony_ci		sg_dma_len(&sgl[i]) = period_len;
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	desc = shdma_prep_sg(schan, sgl, sg_len, &slave_addr,
7478c2ecf20Sopenharmony_ci			     direction, flags, true);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	kfree(sgl);
7508c2ecf20Sopenharmony_ci	return desc;
7518c2ecf20Sopenharmony_ci}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_cistatic int shdma_terminate_all(struct dma_chan *chan)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	struct shdma_chan *schan = to_shdma_chan(chan);
7568c2ecf20Sopenharmony_ci	struct shdma_dev *sdev = to_shdma_dev(chan->device);
7578c2ecf20Sopenharmony_ci	const struct shdma_ops *ops = sdev->ops;
7588c2ecf20Sopenharmony_ci	unsigned long flags;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	spin_lock_irqsave(&schan->chan_lock, flags);
7618c2ecf20Sopenharmony_ci	ops->halt_channel(schan);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	if (ops->get_partial && !list_empty(&schan->ld_queue)) {
7648c2ecf20Sopenharmony_ci		/* Record partial transfer */
7658c2ecf20Sopenharmony_ci		struct shdma_desc *desc = list_first_entry(&schan->ld_queue,
7668c2ecf20Sopenharmony_ci							   struct shdma_desc, node);
7678c2ecf20Sopenharmony_ci		desc->partial = ops->get_partial(schan, desc);
7688c2ecf20Sopenharmony_ci	}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&schan->chan_lock, flags);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	shdma_chan_ld_cleanup(schan, true);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	return 0;
7758c2ecf20Sopenharmony_ci}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_cistatic int shdma_config(struct dma_chan *chan,
7788c2ecf20Sopenharmony_ci			struct dma_slave_config *config)
7798c2ecf20Sopenharmony_ci{
7808c2ecf20Sopenharmony_ci	struct shdma_chan *schan = to_shdma_chan(chan);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	/*
7838c2ecf20Sopenharmony_ci	 * So far only .slave_id is used, but the slave drivers are
7848c2ecf20Sopenharmony_ci	 * encouraged to also set a transfer direction and an address.
7858c2ecf20Sopenharmony_ci	 */
7868c2ecf20Sopenharmony_ci	if (!config)
7878c2ecf20Sopenharmony_ci		return -EINVAL;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	/*
7908c2ecf20Sopenharmony_ci	 * overriding the slave_id through dma_slave_config is deprecated,
7918c2ecf20Sopenharmony_ci	 * but possibly some out-of-tree drivers still do it.
7928c2ecf20Sopenharmony_ci	 */
7938c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(config->slave_id &&
7948c2ecf20Sopenharmony_ci			 config->slave_id != schan->real_slave_id))
7958c2ecf20Sopenharmony_ci		schan->real_slave_id = config->slave_id;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	/*
7988c2ecf20Sopenharmony_ci	 * We could lock this, but you shouldn't be configuring the
7998c2ecf20Sopenharmony_ci	 * channel, while using it...
8008c2ecf20Sopenharmony_ci	 */
8018c2ecf20Sopenharmony_ci	return shdma_setup_slave(schan,
8028c2ecf20Sopenharmony_ci				 config->direction == DMA_DEV_TO_MEM ?
8038c2ecf20Sopenharmony_ci				 config->src_addr : config->dst_addr);
8048c2ecf20Sopenharmony_ci}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_cistatic void shdma_issue_pending(struct dma_chan *chan)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	struct shdma_chan *schan = to_shdma_chan(chan);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	spin_lock_irq(&schan->chan_lock);
8118c2ecf20Sopenharmony_ci	if (schan->pm_state == SHDMA_PM_ESTABLISHED)
8128c2ecf20Sopenharmony_ci		shdma_chan_xfer_ld_queue(schan);
8138c2ecf20Sopenharmony_ci	else
8148c2ecf20Sopenharmony_ci		schan->pm_state = SHDMA_PM_PENDING;
8158c2ecf20Sopenharmony_ci	spin_unlock_irq(&schan->chan_lock);
8168c2ecf20Sopenharmony_ci}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_cistatic enum dma_status shdma_tx_status(struct dma_chan *chan,
8198c2ecf20Sopenharmony_ci					dma_cookie_t cookie,
8208c2ecf20Sopenharmony_ci					struct dma_tx_state *txstate)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	struct shdma_chan *schan = to_shdma_chan(chan);
8238c2ecf20Sopenharmony_ci	enum dma_status status;
8248c2ecf20Sopenharmony_ci	unsigned long flags;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	shdma_chan_ld_cleanup(schan, false);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	spin_lock_irqsave(&schan->chan_lock, flags);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	status = dma_cookie_status(chan, cookie, txstate);
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	/*
8338c2ecf20Sopenharmony_ci	 * If we don't find cookie on the queue, it has been aborted and we have
8348c2ecf20Sopenharmony_ci	 * to report error
8358c2ecf20Sopenharmony_ci	 */
8368c2ecf20Sopenharmony_ci	if (status != DMA_COMPLETE) {
8378c2ecf20Sopenharmony_ci		struct shdma_desc *sdesc;
8388c2ecf20Sopenharmony_ci		status = DMA_ERROR;
8398c2ecf20Sopenharmony_ci		list_for_each_entry(sdesc, &schan->ld_queue, node)
8408c2ecf20Sopenharmony_ci			if (sdesc->cookie == cookie) {
8418c2ecf20Sopenharmony_ci				status = DMA_IN_PROGRESS;
8428c2ecf20Sopenharmony_ci				break;
8438c2ecf20Sopenharmony_ci			}
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&schan->chan_lock, flags);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	return status;
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci/* Called from error IRQ or NMI */
8528c2ecf20Sopenharmony_cibool shdma_reset(struct shdma_dev *sdev)
8538c2ecf20Sopenharmony_ci{
8548c2ecf20Sopenharmony_ci	const struct shdma_ops *ops = sdev->ops;
8558c2ecf20Sopenharmony_ci	struct shdma_chan *schan;
8568c2ecf20Sopenharmony_ci	unsigned int handled = 0;
8578c2ecf20Sopenharmony_ci	int i;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	/* Reset all channels */
8608c2ecf20Sopenharmony_ci	shdma_for_each_chan(schan, sdev, i) {
8618c2ecf20Sopenharmony_ci		struct shdma_desc *sdesc;
8628c2ecf20Sopenharmony_ci		LIST_HEAD(dl);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci		if (!schan)
8658c2ecf20Sopenharmony_ci			continue;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci		spin_lock(&schan->chan_lock);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci		/* Stop the channel */
8708c2ecf20Sopenharmony_ci		ops->halt_channel(schan);
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci		list_splice_init(&schan->ld_queue, &dl);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci		if (!list_empty(&dl)) {
8758c2ecf20Sopenharmony_ci			dev_dbg(schan->dev, "Bring down channel %d\n", schan->id);
8768c2ecf20Sopenharmony_ci			pm_runtime_put(schan->dev);
8778c2ecf20Sopenharmony_ci		}
8788c2ecf20Sopenharmony_ci		schan->pm_state = SHDMA_PM_ESTABLISHED;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci		spin_unlock(&schan->chan_lock);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci		/* Complete all  */
8838c2ecf20Sopenharmony_ci		list_for_each_entry(sdesc, &dl, node) {
8848c2ecf20Sopenharmony_ci			struct dma_async_tx_descriptor *tx = &sdesc->async_tx;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci			sdesc->mark = DESC_IDLE;
8878c2ecf20Sopenharmony_ci			dmaengine_desc_get_callback_invoke(tx, NULL);
8888c2ecf20Sopenharmony_ci		}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci		spin_lock(&schan->chan_lock);
8918c2ecf20Sopenharmony_ci		list_splice(&dl, &schan->ld_free);
8928c2ecf20Sopenharmony_ci		spin_unlock(&schan->chan_lock);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci		handled++;
8958c2ecf20Sopenharmony_ci	}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	return !!handled;
8988c2ecf20Sopenharmony_ci}
8998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(shdma_reset);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_cistatic irqreturn_t chan_irq(int irq, void *dev)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	struct shdma_chan *schan = dev;
9048c2ecf20Sopenharmony_ci	const struct shdma_ops *ops =
9058c2ecf20Sopenharmony_ci		to_shdma_dev(schan->dma_chan.device)->ops;
9068c2ecf20Sopenharmony_ci	irqreturn_t ret;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	spin_lock(&schan->chan_lock);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	ret = ops->chan_irq(schan, irq) ? IRQ_WAKE_THREAD : IRQ_NONE;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	spin_unlock(&schan->chan_lock);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	return ret;
9158c2ecf20Sopenharmony_ci}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_cistatic irqreturn_t chan_irqt(int irq, void *dev)
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci	struct shdma_chan *schan = dev;
9208c2ecf20Sopenharmony_ci	const struct shdma_ops *ops =
9218c2ecf20Sopenharmony_ci		to_shdma_dev(schan->dma_chan.device)->ops;
9228c2ecf20Sopenharmony_ci	struct shdma_desc *sdesc;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	spin_lock_irq(&schan->chan_lock);
9258c2ecf20Sopenharmony_ci	list_for_each_entry(sdesc, &schan->ld_queue, node) {
9268c2ecf20Sopenharmony_ci		if (sdesc->mark == DESC_SUBMITTED &&
9278c2ecf20Sopenharmony_ci		    ops->desc_completed(schan, sdesc)) {
9288c2ecf20Sopenharmony_ci			dev_dbg(schan->dev, "done #%d@%p\n",
9298c2ecf20Sopenharmony_ci				sdesc->async_tx.cookie, &sdesc->async_tx);
9308c2ecf20Sopenharmony_ci			sdesc->mark = DESC_COMPLETED;
9318c2ecf20Sopenharmony_ci			break;
9328c2ecf20Sopenharmony_ci		}
9338c2ecf20Sopenharmony_ci	}
9348c2ecf20Sopenharmony_ci	/* Next desc */
9358c2ecf20Sopenharmony_ci	shdma_chan_xfer_ld_queue(schan);
9368c2ecf20Sopenharmony_ci	spin_unlock_irq(&schan->chan_lock);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	shdma_chan_ld_cleanup(schan, false);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
9418c2ecf20Sopenharmony_ci}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ciint shdma_request_irq(struct shdma_chan *schan, int irq,
9448c2ecf20Sopenharmony_ci			   unsigned long flags, const char *name)
9458c2ecf20Sopenharmony_ci{
9468c2ecf20Sopenharmony_ci	int ret = devm_request_threaded_irq(schan->dev, irq, chan_irq,
9478c2ecf20Sopenharmony_ci					    chan_irqt, flags, name, schan);
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	schan->irq = ret < 0 ? ret : irq;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	return ret;
9528c2ecf20Sopenharmony_ci}
9538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(shdma_request_irq);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_civoid shdma_chan_probe(struct shdma_dev *sdev,
9568c2ecf20Sopenharmony_ci			   struct shdma_chan *schan, int id)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	schan->pm_state = SHDMA_PM_ESTABLISHED;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	/* reference struct dma_device */
9618c2ecf20Sopenharmony_ci	schan->dma_chan.device = &sdev->dma_dev;
9628c2ecf20Sopenharmony_ci	dma_cookie_init(&schan->dma_chan);
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	schan->dev = sdev->dma_dev.dev;
9658c2ecf20Sopenharmony_ci	schan->id = id;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	if (!schan->max_xfer_len)
9688c2ecf20Sopenharmony_ci		schan->max_xfer_len = PAGE_SIZE;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	spin_lock_init(&schan->chan_lock);
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	/* Init descripter manage list */
9738c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&schan->ld_queue);
9748c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&schan->ld_free);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	/* Add the channel to DMA device channel list */
9778c2ecf20Sopenharmony_ci	list_add_tail(&schan->dma_chan.device_node,
9788c2ecf20Sopenharmony_ci			&sdev->dma_dev.channels);
9798c2ecf20Sopenharmony_ci	sdev->schan[id] = schan;
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(shdma_chan_probe);
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_civoid shdma_chan_remove(struct shdma_chan *schan)
9848c2ecf20Sopenharmony_ci{
9858c2ecf20Sopenharmony_ci	list_del(&schan->dma_chan.device_node);
9868c2ecf20Sopenharmony_ci}
9878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(shdma_chan_remove);
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ciint shdma_init(struct device *dev, struct shdma_dev *sdev,
9908c2ecf20Sopenharmony_ci		    int chan_num)
9918c2ecf20Sopenharmony_ci{
9928c2ecf20Sopenharmony_ci	struct dma_device *dma_dev = &sdev->dma_dev;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	/*
9958c2ecf20Sopenharmony_ci	 * Require all call-backs for now, they can trivially be made optional
9968c2ecf20Sopenharmony_ci	 * later as required
9978c2ecf20Sopenharmony_ci	 */
9988c2ecf20Sopenharmony_ci	if (!sdev->ops ||
9998c2ecf20Sopenharmony_ci	    !sdev->desc_size ||
10008c2ecf20Sopenharmony_ci	    !sdev->ops->embedded_desc ||
10018c2ecf20Sopenharmony_ci	    !sdev->ops->start_xfer ||
10028c2ecf20Sopenharmony_ci	    !sdev->ops->setup_xfer ||
10038c2ecf20Sopenharmony_ci	    !sdev->ops->set_slave ||
10048c2ecf20Sopenharmony_ci	    !sdev->ops->desc_setup ||
10058c2ecf20Sopenharmony_ci	    !sdev->ops->slave_addr ||
10068c2ecf20Sopenharmony_ci	    !sdev->ops->channel_busy ||
10078c2ecf20Sopenharmony_ci	    !sdev->ops->halt_channel ||
10088c2ecf20Sopenharmony_ci	    !sdev->ops->desc_completed)
10098c2ecf20Sopenharmony_ci		return -EINVAL;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	sdev->schan = kcalloc(chan_num, sizeof(*sdev->schan), GFP_KERNEL);
10128c2ecf20Sopenharmony_ci	if (!sdev->schan)
10138c2ecf20Sopenharmony_ci		return -ENOMEM;
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dma_dev->channels);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	/* Common and MEMCPY operations */
10188c2ecf20Sopenharmony_ci	dma_dev->device_alloc_chan_resources
10198c2ecf20Sopenharmony_ci		= shdma_alloc_chan_resources;
10208c2ecf20Sopenharmony_ci	dma_dev->device_free_chan_resources = shdma_free_chan_resources;
10218c2ecf20Sopenharmony_ci	dma_dev->device_prep_dma_memcpy = shdma_prep_memcpy;
10228c2ecf20Sopenharmony_ci	dma_dev->device_tx_status = shdma_tx_status;
10238c2ecf20Sopenharmony_ci	dma_dev->device_issue_pending = shdma_issue_pending;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	/* Compulsory for DMA_SLAVE fields */
10268c2ecf20Sopenharmony_ci	dma_dev->device_prep_slave_sg = shdma_prep_slave_sg;
10278c2ecf20Sopenharmony_ci	dma_dev->device_prep_dma_cyclic = shdma_prep_dma_cyclic;
10288c2ecf20Sopenharmony_ci	dma_dev->device_config = shdma_config;
10298c2ecf20Sopenharmony_ci	dma_dev->device_terminate_all = shdma_terminate_all;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	dma_dev->dev = dev;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	return 0;
10348c2ecf20Sopenharmony_ci}
10358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(shdma_init);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_civoid shdma_cleanup(struct shdma_dev *sdev)
10388c2ecf20Sopenharmony_ci{
10398c2ecf20Sopenharmony_ci	kfree(sdev->schan);
10408c2ecf20Sopenharmony_ci}
10418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(shdma_cleanup);
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_cistatic int __init shdma_enter(void)
10448c2ecf20Sopenharmony_ci{
10458c2ecf20Sopenharmony_ci	shdma_slave_used = kcalloc(DIV_ROUND_UP(slave_num, BITS_PER_LONG),
10468c2ecf20Sopenharmony_ci				   sizeof(long),
10478c2ecf20Sopenharmony_ci				   GFP_KERNEL);
10488c2ecf20Sopenharmony_ci	if (!shdma_slave_used)
10498c2ecf20Sopenharmony_ci		return -ENOMEM;
10508c2ecf20Sopenharmony_ci	return 0;
10518c2ecf20Sopenharmony_ci}
10528c2ecf20Sopenharmony_cimodule_init(shdma_enter);
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_cistatic void __exit shdma_exit(void)
10558c2ecf20Sopenharmony_ci{
10568c2ecf20Sopenharmony_ci	kfree(shdma_slave_used);
10578c2ecf20Sopenharmony_ci}
10588c2ecf20Sopenharmony_cimodule_exit(shdma_exit);
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
10618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SH-DMA driver base library");
10628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
1063