18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * AMD Cryptographic Coprocessor (CCP) driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016,2019 Advanced Micro Devices, Inc.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Gary R Hook <gary.hook@amd.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
138c2ecf20Sopenharmony_ci#include <linux/dmaengine.h>
148c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
158c2ecf20Sopenharmony_ci#include <linux/mutex.h>
168c2ecf20Sopenharmony_ci#include <linux/ccp.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "ccp-dev.h"
198c2ecf20Sopenharmony_ci#include "../../dma/dmaengine.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define CCP_DMA_WIDTH(_mask)		\
228c2ecf20Sopenharmony_ci({					\
238c2ecf20Sopenharmony_ci	u64 mask = _mask + 1;		\
248c2ecf20Sopenharmony_ci	(mask == 0) ? 64 : fls64(mask);	\
258c2ecf20Sopenharmony_ci})
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* The CCP as a DMA provider can be configured for public or private
288c2ecf20Sopenharmony_ci * channels. Default is specified in the vdata for the device (PCI ID).
298c2ecf20Sopenharmony_ci * This module parameter will override for all channels on all devices:
308c2ecf20Sopenharmony_ci *   dma_chan_attr = 0x2 to force all channels public
318c2ecf20Sopenharmony_ci *                 = 0x1 to force all channels private
328c2ecf20Sopenharmony_ci *                 = 0x0 to defer to the vdata setting
338c2ecf20Sopenharmony_ci *                 = any other value: warning, revert to 0x0
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_cistatic unsigned int dma_chan_attr = CCP_DMA_DFLT;
368c2ecf20Sopenharmony_cimodule_param(dma_chan_attr, uint, 0444);
378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma_chan_attr, "Set DMA channel visibility: 0 (default) = device defaults, 1 = make private, 2 = make public");
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic unsigned int dmaengine = 1;
408c2ecf20Sopenharmony_cimodule_param(dmaengine, uint, 0444);
418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dmaengine, "Register services with the DMA subsystem (any non-zero value, default: 1)");
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic unsigned int ccp_get_dma_chan_attr(struct ccp_device *ccp)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	switch (dma_chan_attr) {
468c2ecf20Sopenharmony_ci	case CCP_DMA_DFLT:
478c2ecf20Sopenharmony_ci		return ccp->vdata->dma_chan_attr;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	case CCP_DMA_PRIV:
508c2ecf20Sopenharmony_ci		return DMA_PRIVATE;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	case CCP_DMA_PUB:
538c2ecf20Sopenharmony_ci		return 0;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	default:
568c2ecf20Sopenharmony_ci		dev_info_once(ccp->dev, "Invalid value for dma_chan_attr: %d\n",
578c2ecf20Sopenharmony_ci			      dma_chan_attr);
588c2ecf20Sopenharmony_ci		return ccp->vdata->dma_chan_attr;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic void ccp_free_cmd_resources(struct ccp_device *ccp,
638c2ecf20Sopenharmony_ci				   struct list_head *list)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct ccp_dma_cmd *cmd, *ctmp;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cmd, ctmp, list, entry) {
688c2ecf20Sopenharmony_ci		list_del(&cmd->entry);
698c2ecf20Sopenharmony_ci		kmem_cache_free(ccp->dma_cmd_cache, cmd);
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic void ccp_free_desc_resources(struct ccp_device *ccp,
748c2ecf20Sopenharmony_ci				    struct list_head *list)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct ccp_dma_desc *desc, *dtmp;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	list_for_each_entry_safe(desc, dtmp, list, entry) {
798c2ecf20Sopenharmony_ci		ccp_free_cmd_resources(ccp, &desc->active);
808c2ecf20Sopenharmony_ci		ccp_free_cmd_resources(ccp, &desc->pending);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci		list_del(&desc->entry);
838c2ecf20Sopenharmony_ci		kmem_cache_free(ccp->dma_desc_cache, desc);
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic void ccp_free_chan_resources(struct dma_chan *dma_chan)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
908c2ecf20Sopenharmony_ci						 dma_chan);
918c2ecf20Sopenharmony_ci	unsigned long flags;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s - chan=%p\n", __func__, chan);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->complete);
988c2ecf20Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->active);
998c2ecf20Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->pending);
1008c2ecf20Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->created);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic void ccp_cleanup_desc_resources(struct ccp_device *ccp,
1068c2ecf20Sopenharmony_ci				       struct list_head *list)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct ccp_dma_desc *desc, *dtmp;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	list_for_each_entry_safe_reverse(desc, dtmp, list, entry) {
1118c2ecf20Sopenharmony_ci		if (!async_tx_test_ack(&desc->tx_desc))
1128c2ecf20Sopenharmony_ci			continue;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		dev_dbg(ccp->dev, "%s - desc=%p\n", __func__, desc);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci		ccp_free_cmd_resources(ccp, &desc->active);
1178c2ecf20Sopenharmony_ci		ccp_free_cmd_resources(ccp, &desc->pending);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		list_del(&desc->entry);
1208c2ecf20Sopenharmony_ci		kmem_cache_free(ccp->dma_desc_cache, desc);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic void ccp_do_cleanup(unsigned long data)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan = (struct ccp_dma_chan *)data;
1278c2ecf20Sopenharmony_ci	unsigned long flags;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s - chan=%s\n", __func__,
1308c2ecf20Sopenharmony_ci		dma_chan_name(&chan->dma_chan));
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	ccp_cleanup_desc_resources(chan->ccp, &chan->complete);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic int ccp_issue_next_cmd(struct ccp_dma_desc *desc)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct ccp_dma_cmd *cmd;
1428c2ecf20Sopenharmony_ci	int ret;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	cmd = list_first_entry(&desc->pending, struct ccp_dma_cmd, entry);
1458c2ecf20Sopenharmony_ci	list_move(&cmd->entry, &desc->active);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	dev_dbg(desc->ccp->dev, "%s - tx %d, cmd=%p\n", __func__,
1488c2ecf20Sopenharmony_ci		desc->tx_desc.cookie, cmd);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	ret = ccp_enqueue_cmd(&cmd->ccp_cmd);
1518c2ecf20Sopenharmony_ci	if (!ret || (ret == -EINPROGRESS) || (ret == -EBUSY))
1528c2ecf20Sopenharmony_ci		return 0;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	dev_dbg(desc->ccp->dev, "%s - error: ret=%d, tx %d, cmd=%p\n", __func__,
1558c2ecf20Sopenharmony_ci		ret, desc->tx_desc.cookie, cmd);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return ret;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic void ccp_free_active_cmd(struct ccp_dma_desc *desc)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct ccp_dma_cmd *cmd;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	cmd = list_first_entry_or_null(&desc->active, struct ccp_dma_cmd,
1658c2ecf20Sopenharmony_ci				       entry);
1668c2ecf20Sopenharmony_ci	if (!cmd)
1678c2ecf20Sopenharmony_ci		return;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	dev_dbg(desc->ccp->dev, "%s - freeing tx %d cmd=%p\n",
1708c2ecf20Sopenharmony_ci		__func__, desc->tx_desc.cookie, cmd);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	list_del(&cmd->entry);
1738c2ecf20Sopenharmony_ci	kmem_cache_free(desc->ccp->dma_cmd_cache, cmd);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic struct ccp_dma_desc *__ccp_next_dma_desc(struct ccp_dma_chan *chan,
1778c2ecf20Sopenharmony_ci						struct ccp_dma_desc *desc)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	/* Move current DMA descriptor to the complete list */
1808c2ecf20Sopenharmony_ci	if (desc)
1818c2ecf20Sopenharmony_ci		list_move(&desc->entry, &chan->complete);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* Get the next DMA descriptor on the active list */
1848c2ecf20Sopenharmony_ci	desc = list_first_entry_or_null(&chan->active, struct ccp_dma_desc,
1858c2ecf20Sopenharmony_ci					entry);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return desc;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic struct ccp_dma_desc *ccp_handle_active_desc(struct ccp_dma_chan *chan,
1918c2ecf20Sopenharmony_ci						   struct ccp_dma_desc *desc)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *tx_desc;
1948c2ecf20Sopenharmony_ci	unsigned long flags;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* Loop over descriptors until one is found with commands */
1978c2ecf20Sopenharmony_ci	do {
1988c2ecf20Sopenharmony_ci		if (desc) {
1998c2ecf20Sopenharmony_ci			/* Remove the DMA command from the list and free it */
2008c2ecf20Sopenharmony_ci			ccp_free_active_cmd(desc);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci			if (!list_empty(&desc->pending)) {
2038c2ecf20Sopenharmony_ci				/* No errors, keep going */
2048c2ecf20Sopenharmony_ci				if (desc->status != DMA_ERROR)
2058c2ecf20Sopenharmony_ci					return desc;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci				/* Error, free remaining commands and move on */
2088c2ecf20Sopenharmony_ci				ccp_free_cmd_resources(desc->ccp,
2098c2ecf20Sopenharmony_ci						       &desc->pending);
2108c2ecf20Sopenharmony_ci			}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci			tx_desc = &desc->tx_desc;
2138c2ecf20Sopenharmony_ci		} else {
2148c2ecf20Sopenharmony_ci			tx_desc = NULL;
2158c2ecf20Sopenharmony_ci		}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		spin_lock_irqsave(&chan->lock, flags);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		if (desc) {
2208c2ecf20Sopenharmony_ci			if (desc->status != DMA_ERROR)
2218c2ecf20Sopenharmony_ci				desc->status = DMA_COMPLETE;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci			dev_dbg(desc->ccp->dev,
2248c2ecf20Sopenharmony_ci				"%s - tx %d complete, status=%u\n", __func__,
2258c2ecf20Sopenharmony_ci				desc->tx_desc.cookie, desc->status);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci			dma_cookie_complete(tx_desc);
2288c2ecf20Sopenharmony_ci			dma_descriptor_unmap(tx_desc);
2298c2ecf20Sopenharmony_ci		}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		desc = __ccp_next_dma_desc(chan, desc);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chan->lock, flags);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		if (tx_desc) {
2368c2ecf20Sopenharmony_ci			dmaengine_desc_get_callback_invoke(tx_desc, NULL);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci			dma_run_dependencies(tx_desc);
2398c2ecf20Sopenharmony_ci		}
2408c2ecf20Sopenharmony_ci	} while (desc);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return NULL;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic struct ccp_dma_desc *__ccp_pending_to_active(struct ccp_dma_chan *chan)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct ccp_dma_desc *desc;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	if (list_empty(&chan->pending))
2508c2ecf20Sopenharmony_ci		return NULL;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	desc = list_empty(&chan->active)
2538c2ecf20Sopenharmony_ci		? list_first_entry(&chan->pending, struct ccp_dma_desc, entry)
2548c2ecf20Sopenharmony_ci		: NULL;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	list_splice_tail_init(&chan->pending, &chan->active);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	return desc;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic void ccp_cmd_callback(void *data, int err)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct ccp_dma_desc *desc = data;
2648c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan;
2658c2ecf20Sopenharmony_ci	int ret;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (err == -EINPROGRESS)
2688c2ecf20Sopenharmony_ci		return;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	chan = container_of(desc->tx_desc.chan, struct ccp_dma_chan,
2718c2ecf20Sopenharmony_ci			    dma_chan);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s - tx %d callback, err=%d\n",
2748c2ecf20Sopenharmony_ci		__func__, desc->tx_desc.cookie, err);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (err)
2778c2ecf20Sopenharmony_ci		desc->status = DMA_ERROR;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	while (true) {
2808c2ecf20Sopenharmony_ci		/* Check for DMA descriptor completion */
2818c2ecf20Sopenharmony_ci		desc = ccp_handle_active_desc(chan, desc);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci		/* Don't submit cmd if no descriptor or DMA is paused */
2848c2ecf20Sopenharmony_ci		if (!desc || (chan->status == DMA_PAUSED))
2858c2ecf20Sopenharmony_ci			break;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		ret = ccp_issue_next_cmd(desc);
2888c2ecf20Sopenharmony_ci		if (!ret)
2898c2ecf20Sopenharmony_ci			break;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci		desc->status = DMA_ERROR;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	tasklet_schedule(&chan->cleanup_tasklet);
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic dma_cookie_t ccp_tx_submit(struct dma_async_tx_descriptor *tx_desc)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct ccp_dma_desc *desc = container_of(tx_desc, struct ccp_dma_desc,
3008c2ecf20Sopenharmony_ci						 tx_desc);
3018c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan;
3028c2ecf20Sopenharmony_ci	dma_cookie_t cookie;
3038c2ecf20Sopenharmony_ci	unsigned long flags;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	chan = container_of(tx_desc->chan, struct ccp_dma_chan, dma_chan);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	cookie = dma_cookie_assign(tx_desc);
3108c2ecf20Sopenharmony_ci	list_del(&desc->entry);
3118c2ecf20Sopenharmony_ci	list_add_tail(&desc->entry, &chan->pending);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s - added tx descriptor %d to pending list\n",
3168c2ecf20Sopenharmony_ci		__func__, cookie);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return cookie;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic struct ccp_dma_cmd *ccp_alloc_dma_cmd(struct ccp_dma_chan *chan)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct ccp_dma_cmd *cmd;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	cmd = kmem_cache_alloc(chan->ccp->dma_cmd_cache, GFP_NOWAIT);
3268c2ecf20Sopenharmony_ci	if (cmd)
3278c2ecf20Sopenharmony_ci		memset(cmd, 0, sizeof(*cmd));
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return cmd;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic struct ccp_dma_desc *ccp_alloc_dma_desc(struct ccp_dma_chan *chan,
3338c2ecf20Sopenharmony_ci					       unsigned long flags)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct ccp_dma_desc *desc;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	desc = kmem_cache_zalloc(chan->ccp->dma_desc_cache, GFP_NOWAIT);
3388c2ecf20Sopenharmony_ci	if (!desc)
3398c2ecf20Sopenharmony_ci		return NULL;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	dma_async_tx_descriptor_init(&desc->tx_desc, &chan->dma_chan);
3428c2ecf20Sopenharmony_ci	desc->tx_desc.flags = flags;
3438c2ecf20Sopenharmony_ci	desc->tx_desc.tx_submit = ccp_tx_submit;
3448c2ecf20Sopenharmony_ci	desc->ccp = chan->ccp;
3458c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&desc->entry);
3468c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&desc->pending);
3478c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&desc->active);
3488c2ecf20Sopenharmony_ci	desc->status = DMA_IN_PROGRESS;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return desc;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic struct ccp_dma_desc *ccp_create_desc(struct dma_chan *dma_chan,
3548c2ecf20Sopenharmony_ci					    struct scatterlist *dst_sg,
3558c2ecf20Sopenharmony_ci					    unsigned int dst_nents,
3568c2ecf20Sopenharmony_ci					    struct scatterlist *src_sg,
3578c2ecf20Sopenharmony_ci					    unsigned int src_nents,
3588c2ecf20Sopenharmony_ci					    unsigned long flags)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
3618c2ecf20Sopenharmony_ci						 dma_chan);
3628c2ecf20Sopenharmony_ci	struct ccp_device *ccp = chan->ccp;
3638c2ecf20Sopenharmony_ci	struct ccp_dma_desc *desc;
3648c2ecf20Sopenharmony_ci	struct ccp_dma_cmd *cmd;
3658c2ecf20Sopenharmony_ci	struct ccp_cmd *ccp_cmd;
3668c2ecf20Sopenharmony_ci	struct ccp_passthru_nomap_engine *ccp_pt;
3678c2ecf20Sopenharmony_ci	unsigned int src_offset, src_len;
3688c2ecf20Sopenharmony_ci	unsigned int dst_offset, dst_len;
3698c2ecf20Sopenharmony_ci	unsigned int len;
3708c2ecf20Sopenharmony_ci	unsigned long sflags;
3718c2ecf20Sopenharmony_ci	size_t total_len;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (!dst_sg || !src_sg)
3748c2ecf20Sopenharmony_ci		return NULL;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (!dst_nents || !src_nents)
3778c2ecf20Sopenharmony_ci		return NULL;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	desc = ccp_alloc_dma_desc(chan, flags);
3808c2ecf20Sopenharmony_ci	if (!desc)
3818c2ecf20Sopenharmony_ci		return NULL;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	total_len = 0;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	src_len = sg_dma_len(src_sg);
3868c2ecf20Sopenharmony_ci	src_offset = 0;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	dst_len = sg_dma_len(dst_sg);
3898c2ecf20Sopenharmony_ci	dst_offset = 0;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	while (true) {
3928c2ecf20Sopenharmony_ci		if (!src_len) {
3938c2ecf20Sopenharmony_ci			src_nents--;
3948c2ecf20Sopenharmony_ci			if (!src_nents)
3958c2ecf20Sopenharmony_ci				break;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci			src_sg = sg_next(src_sg);
3988c2ecf20Sopenharmony_ci			if (!src_sg)
3998c2ecf20Sopenharmony_ci				break;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci			src_len = sg_dma_len(src_sg);
4028c2ecf20Sopenharmony_ci			src_offset = 0;
4038c2ecf20Sopenharmony_ci			continue;
4048c2ecf20Sopenharmony_ci		}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci		if (!dst_len) {
4078c2ecf20Sopenharmony_ci			dst_nents--;
4088c2ecf20Sopenharmony_ci			if (!dst_nents)
4098c2ecf20Sopenharmony_ci				break;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci			dst_sg = sg_next(dst_sg);
4128c2ecf20Sopenharmony_ci			if (!dst_sg)
4138c2ecf20Sopenharmony_ci				break;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci			dst_len = sg_dma_len(dst_sg);
4168c2ecf20Sopenharmony_ci			dst_offset = 0;
4178c2ecf20Sopenharmony_ci			continue;
4188c2ecf20Sopenharmony_ci		}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		len = min(dst_len, src_len);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		cmd = ccp_alloc_dma_cmd(chan);
4238c2ecf20Sopenharmony_ci		if (!cmd)
4248c2ecf20Sopenharmony_ci			goto err;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci		ccp_cmd = &cmd->ccp_cmd;
4278c2ecf20Sopenharmony_ci		ccp_cmd->ccp = chan->ccp;
4288c2ecf20Sopenharmony_ci		ccp_pt = &ccp_cmd->u.passthru_nomap;
4298c2ecf20Sopenharmony_ci		ccp_cmd->flags = CCP_CMD_MAY_BACKLOG;
4308c2ecf20Sopenharmony_ci		ccp_cmd->flags |= CCP_CMD_PASSTHRU_NO_DMA_MAP;
4318c2ecf20Sopenharmony_ci		ccp_cmd->engine = CCP_ENGINE_PASSTHRU;
4328c2ecf20Sopenharmony_ci		ccp_pt->bit_mod = CCP_PASSTHRU_BITWISE_NOOP;
4338c2ecf20Sopenharmony_ci		ccp_pt->byte_swap = CCP_PASSTHRU_BYTESWAP_NOOP;
4348c2ecf20Sopenharmony_ci		ccp_pt->src_dma = sg_dma_address(src_sg) + src_offset;
4358c2ecf20Sopenharmony_ci		ccp_pt->dst_dma = sg_dma_address(dst_sg) + dst_offset;
4368c2ecf20Sopenharmony_ci		ccp_pt->src_len = len;
4378c2ecf20Sopenharmony_ci		ccp_pt->final = 1;
4388c2ecf20Sopenharmony_ci		ccp_cmd->callback = ccp_cmd_callback;
4398c2ecf20Sopenharmony_ci		ccp_cmd->data = desc;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		list_add_tail(&cmd->entry, &desc->pending);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		dev_dbg(ccp->dev,
4448c2ecf20Sopenharmony_ci			"%s - cmd=%p, src=%pad, dst=%pad, len=%llu\n", __func__,
4458c2ecf20Sopenharmony_ci			cmd, &ccp_pt->src_dma,
4468c2ecf20Sopenharmony_ci			&ccp_pt->dst_dma, ccp_pt->src_len);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		total_len += len;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		src_len -= len;
4518c2ecf20Sopenharmony_ci		src_offset += len;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		dst_len -= len;
4548c2ecf20Sopenharmony_ci		dst_offset += len;
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	desc->len = total_len;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	if (list_empty(&desc->pending))
4608c2ecf20Sopenharmony_ci		goto err;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	dev_dbg(ccp->dev, "%s - desc=%p\n", __func__, desc);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, sflags);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	list_add_tail(&desc->entry, &chan->created);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, sflags);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	return desc;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cierr:
4738c2ecf20Sopenharmony_ci	ccp_free_cmd_resources(ccp, &desc->pending);
4748c2ecf20Sopenharmony_ci	kmem_cache_free(ccp->dma_desc_cache, desc);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	return NULL;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *ccp_prep_dma_memcpy(
4808c2ecf20Sopenharmony_ci	struct dma_chan *dma_chan, dma_addr_t dst, dma_addr_t src, size_t len,
4818c2ecf20Sopenharmony_ci	unsigned long flags)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
4848c2ecf20Sopenharmony_ci						 dma_chan);
4858c2ecf20Sopenharmony_ci	struct ccp_dma_desc *desc;
4868c2ecf20Sopenharmony_ci	struct scatterlist dst_sg, src_sg;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	dev_dbg(chan->ccp->dev,
4898c2ecf20Sopenharmony_ci		"%s - src=%pad, dst=%pad, len=%zu, flags=%#lx\n",
4908c2ecf20Sopenharmony_ci		__func__, &src, &dst, len, flags);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	sg_init_table(&dst_sg, 1);
4938c2ecf20Sopenharmony_ci	sg_dma_address(&dst_sg) = dst;
4948c2ecf20Sopenharmony_ci	sg_dma_len(&dst_sg) = len;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	sg_init_table(&src_sg, 1);
4978c2ecf20Sopenharmony_ci	sg_dma_address(&src_sg) = src;
4988c2ecf20Sopenharmony_ci	sg_dma_len(&src_sg) = len;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	desc = ccp_create_desc(dma_chan, &dst_sg, 1, &src_sg, 1, flags);
5018c2ecf20Sopenharmony_ci	if (!desc)
5028c2ecf20Sopenharmony_ci		return NULL;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	return &desc->tx_desc;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *ccp_prep_dma_interrupt(
5088c2ecf20Sopenharmony_ci	struct dma_chan *dma_chan, unsigned long flags)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
5118c2ecf20Sopenharmony_ci						 dma_chan);
5128c2ecf20Sopenharmony_ci	struct ccp_dma_desc *desc;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	desc = ccp_alloc_dma_desc(chan, flags);
5158c2ecf20Sopenharmony_ci	if (!desc)
5168c2ecf20Sopenharmony_ci		return NULL;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	return &desc->tx_desc;
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic void ccp_issue_pending(struct dma_chan *dma_chan)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
5248c2ecf20Sopenharmony_ci						 dma_chan);
5258c2ecf20Sopenharmony_ci	struct ccp_dma_desc *desc;
5268c2ecf20Sopenharmony_ci	unsigned long flags;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s\n", __func__);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	desc = __ccp_pending_to_active(chan);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	/* If there was nothing active, start processing */
5378c2ecf20Sopenharmony_ci	if (desc)
5388c2ecf20Sopenharmony_ci		ccp_cmd_callback(desc, 0);
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic enum dma_status ccp_tx_status(struct dma_chan *dma_chan,
5428c2ecf20Sopenharmony_ci				     dma_cookie_t cookie,
5438c2ecf20Sopenharmony_ci				     struct dma_tx_state *state)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
5468c2ecf20Sopenharmony_ci						 dma_chan);
5478c2ecf20Sopenharmony_ci	struct ccp_dma_desc *desc;
5488c2ecf20Sopenharmony_ci	enum dma_status ret;
5498c2ecf20Sopenharmony_ci	unsigned long flags;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	if (chan->status == DMA_PAUSED) {
5528c2ecf20Sopenharmony_ci		ret = DMA_PAUSED;
5538c2ecf20Sopenharmony_ci		goto out;
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	ret = dma_cookie_status(dma_chan, cookie, state);
5578c2ecf20Sopenharmony_ci	if (ret == DMA_COMPLETE) {
5588c2ecf20Sopenharmony_ci		spin_lock_irqsave(&chan->lock, flags);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci		/* Get status from complete chain, if still there */
5618c2ecf20Sopenharmony_ci		list_for_each_entry(desc, &chan->complete, entry) {
5628c2ecf20Sopenharmony_ci			if (desc->tx_desc.cookie != cookie)
5638c2ecf20Sopenharmony_ci				continue;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci			ret = desc->status;
5668c2ecf20Sopenharmony_ci			break;
5678c2ecf20Sopenharmony_ci		}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chan->lock, flags);
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ciout:
5738c2ecf20Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s - %u\n", __func__, ret);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	return ret;
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic int ccp_pause(struct dma_chan *dma_chan)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
5818c2ecf20Sopenharmony_ci						 dma_chan);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	chan->status = DMA_PAUSED;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	/*TODO: Wait for active DMA to complete before returning? */
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	return 0;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_cistatic int ccp_resume(struct dma_chan *dma_chan)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
5938c2ecf20Sopenharmony_ci						 dma_chan);
5948c2ecf20Sopenharmony_ci	struct ccp_dma_desc *desc;
5958c2ecf20Sopenharmony_ci	unsigned long flags;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	desc = list_first_entry_or_null(&chan->active, struct ccp_dma_desc,
6008c2ecf20Sopenharmony_ci					entry);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	/* Indicate the channel is running again */
6058c2ecf20Sopenharmony_ci	chan->status = DMA_IN_PROGRESS;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	/* If there was something active, re-start */
6088c2ecf20Sopenharmony_ci	if (desc)
6098c2ecf20Sopenharmony_ci		ccp_cmd_callback(desc, 0);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	return 0;
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cistatic int ccp_terminate_all(struct dma_chan *dma_chan)
6158c2ecf20Sopenharmony_ci{
6168c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
6178c2ecf20Sopenharmony_ci						 dma_chan);
6188c2ecf20Sopenharmony_ci	unsigned long flags;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s\n", __func__);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	/*TODO: Wait for active DMA to complete before continuing */
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	/*TODO: Purge the complete list? */
6278c2ecf20Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->active);
6288c2ecf20Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->pending);
6298c2ecf20Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->created);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	return 0;
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic void ccp_dma_release(struct ccp_device *ccp)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan;
6398c2ecf20Sopenharmony_ci	struct dma_chan *dma_chan;
6408c2ecf20Sopenharmony_ci	unsigned int i;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	for (i = 0; i < ccp->cmd_q_count; i++) {
6438c2ecf20Sopenharmony_ci		chan = ccp->ccp_dma_chan + i;
6448c2ecf20Sopenharmony_ci		dma_chan = &chan->dma_chan;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci		tasklet_kill(&chan->cleanup_tasklet);
6478c2ecf20Sopenharmony_ci		list_del_rcu(&dma_chan->device_node);
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic void ccp_dma_release_channels(struct ccp_device *ccp)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan;
6548c2ecf20Sopenharmony_ci	struct dma_chan *dma_chan;
6558c2ecf20Sopenharmony_ci	unsigned int i;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	for (i = 0; i < ccp->cmd_q_count; i++) {
6588c2ecf20Sopenharmony_ci		chan = ccp->ccp_dma_chan + i;
6598c2ecf20Sopenharmony_ci		dma_chan = &chan->dma_chan;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci		if (dma_chan->client_count)
6628c2ecf20Sopenharmony_ci			dma_release_channel(dma_chan);
6638c2ecf20Sopenharmony_ci	}
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ciint ccp_dmaengine_register(struct ccp_device *ccp)
6678c2ecf20Sopenharmony_ci{
6688c2ecf20Sopenharmony_ci	struct ccp_dma_chan *chan;
6698c2ecf20Sopenharmony_ci	struct dma_device *dma_dev = &ccp->dma_dev;
6708c2ecf20Sopenharmony_ci	struct dma_chan *dma_chan;
6718c2ecf20Sopenharmony_ci	char *dma_cmd_cache_name;
6728c2ecf20Sopenharmony_ci	char *dma_desc_cache_name;
6738c2ecf20Sopenharmony_ci	unsigned int i;
6748c2ecf20Sopenharmony_ci	int ret;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	if (!dmaengine)
6778c2ecf20Sopenharmony_ci		return 0;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	ccp->ccp_dma_chan = devm_kcalloc(ccp->dev, ccp->cmd_q_count,
6808c2ecf20Sopenharmony_ci					 sizeof(*(ccp->ccp_dma_chan)),
6818c2ecf20Sopenharmony_ci					 GFP_KERNEL);
6828c2ecf20Sopenharmony_ci	if (!ccp->ccp_dma_chan)
6838c2ecf20Sopenharmony_ci		return -ENOMEM;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	dma_cmd_cache_name = devm_kasprintf(ccp->dev, GFP_KERNEL,
6868c2ecf20Sopenharmony_ci					    "%s-dmaengine-cmd-cache",
6878c2ecf20Sopenharmony_ci					    ccp->name);
6888c2ecf20Sopenharmony_ci	if (!dma_cmd_cache_name)
6898c2ecf20Sopenharmony_ci		return -ENOMEM;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	ccp->dma_cmd_cache = kmem_cache_create(dma_cmd_cache_name,
6928c2ecf20Sopenharmony_ci					       sizeof(struct ccp_dma_cmd),
6938c2ecf20Sopenharmony_ci					       sizeof(void *),
6948c2ecf20Sopenharmony_ci					       SLAB_HWCACHE_ALIGN, NULL);
6958c2ecf20Sopenharmony_ci	if (!ccp->dma_cmd_cache)
6968c2ecf20Sopenharmony_ci		return -ENOMEM;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	dma_desc_cache_name = devm_kasprintf(ccp->dev, GFP_KERNEL,
6998c2ecf20Sopenharmony_ci					     "%s-dmaengine-desc-cache",
7008c2ecf20Sopenharmony_ci					     ccp->name);
7018c2ecf20Sopenharmony_ci	if (!dma_desc_cache_name) {
7028c2ecf20Sopenharmony_ci		ret = -ENOMEM;
7038c2ecf20Sopenharmony_ci		goto err_cache;
7048c2ecf20Sopenharmony_ci	}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	ccp->dma_desc_cache = kmem_cache_create(dma_desc_cache_name,
7078c2ecf20Sopenharmony_ci						sizeof(struct ccp_dma_desc),
7088c2ecf20Sopenharmony_ci						sizeof(void *),
7098c2ecf20Sopenharmony_ci						SLAB_HWCACHE_ALIGN, NULL);
7108c2ecf20Sopenharmony_ci	if (!ccp->dma_desc_cache) {
7118c2ecf20Sopenharmony_ci		ret = -ENOMEM;
7128c2ecf20Sopenharmony_ci		goto err_cache;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	dma_dev->dev = ccp->dev;
7168c2ecf20Sopenharmony_ci	dma_dev->src_addr_widths = CCP_DMA_WIDTH(dma_get_mask(ccp->dev));
7178c2ecf20Sopenharmony_ci	dma_dev->dst_addr_widths = CCP_DMA_WIDTH(dma_get_mask(ccp->dev));
7188c2ecf20Sopenharmony_ci	dma_dev->directions = DMA_MEM_TO_MEM;
7198c2ecf20Sopenharmony_ci	dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
7208c2ecf20Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask);
7218c2ecf20Sopenharmony_ci	dma_cap_set(DMA_INTERRUPT, dma_dev->cap_mask);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	/* The DMA channels for this device can be set to public or private,
7248c2ecf20Sopenharmony_ci	 * and overridden by the module parameter dma_chan_attr.
7258c2ecf20Sopenharmony_ci	 * Default: according to the value in vdata (dma_chan_attr=0)
7268c2ecf20Sopenharmony_ci	 * dma_chan_attr=0x1: all channels private (override vdata)
7278c2ecf20Sopenharmony_ci	 * dma_chan_attr=0x2: all channels public (override vdata)
7288c2ecf20Sopenharmony_ci	 */
7298c2ecf20Sopenharmony_ci	if (ccp_get_dma_chan_attr(ccp) == DMA_PRIVATE)
7308c2ecf20Sopenharmony_ci		dma_cap_set(DMA_PRIVATE, dma_dev->cap_mask);
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dma_dev->channels);
7338c2ecf20Sopenharmony_ci	for (i = 0; i < ccp->cmd_q_count; i++) {
7348c2ecf20Sopenharmony_ci		chan = ccp->ccp_dma_chan + i;
7358c2ecf20Sopenharmony_ci		dma_chan = &chan->dma_chan;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci		chan->ccp = ccp;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci		spin_lock_init(&chan->lock);
7408c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&chan->created);
7418c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&chan->pending);
7428c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&chan->active);
7438c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&chan->complete);
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci		tasklet_init(&chan->cleanup_tasklet, ccp_do_cleanup,
7468c2ecf20Sopenharmony_ci			     (unsigned long)chan);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci		dma_chan->device = dma_dev;
7498c2ecf20Sopenharmony_ci		dma_cookie_init(dma_chan);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci		list_add_tail(&dma_chan->device_node, &dma_dev->channels);
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	dma_dev->device_free_chan_resources = ccp_free_chan_resources;
7558c2ecf20Sopenharmony_ci	dma_dev->device_prep_dma_memcpy = ccp_prep_dma_memcpy;
7568c2ecf20Sopenharmony_ci	dma_dev->device_prep_dma_interrupt = ccp_prep_dma_interrupt;
7578c2ecf20Sopenharmony_ci	dma_dev->device_issue_pending = ccp_issue_pending;
7588c2ecf20Sopenharmony_ci	dma_dev->device_tx_status = ccp_tx_status;
7598c2ecf20Sopenharmony_ci	dma_dev->device_pause = ccp_pause;
7608c2ecf20Sopenharmony_ci	dma_dev->device_resume = ccp_resume;
7618c2ecf20Sopenharmony_ci	dma_dev->device_terminate_all = ccp_terminate_all;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	ret = dma_async_device_register(dma_dev);
7648c2ecf20Sopenharmony_ci	if (ret)
7658c2ecf20Sopenharmony_ci		goto err_reg;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	return 0;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cierr_reg:
7708c2ecf20Sopenharmony_ci	ccp_dma_release(ccp);
7718c2ecf20Sopenharmony_ci	kmem_cache_destroy(ccp->dma_desc_cache);
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_cierr_cache:
7748c2ecf20Sopenharmony_ci	kmem_cache_destroy(ccp->dma_cmd_cache);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	return ret;
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_civoid ccp_dmaengine_unregister(struct ccp_device *ccp)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	struct dma_device *dma_dev = &ccp->dma_dev;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	if (!dmaengine)
7848c2ecf20Sopenharmony_ci		return;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	ccp_dma_release_channels(ccp);
7878c2ecf20Sopenharmony_ci	dma_async_device_unregister(dma_dev);
7888c2ecf20Sopenharmony_ci	ccp_dma_release(ccp);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	kmem_cache_destroy(ccp->dma_desc_cache);
7918c2ecf20Sopenharmony_ci	kmem_cache_destroy(ccp->dma_cmd_cache);
7928c2ecf20Sopenharmony_ci}
793