162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * AMD Cryptographic Coprocessor (CCP) driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016,2019 Advanced Micro Devices, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Gary R Hook <gary.hook@amd.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1362306a36Sopenharmony_ci#include <linux/dmaengine.h>
1462306a36Sopenharmony_ci#include <linux/spinlock.h>
1562306a36Sopenharmony_ci#include <linux/mutex.h>
1662306a36Sopenharmony_ci#include <linux/ccp.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "ccp-dev.h"
1962306a36Sopenharmony_ci#include "../../dma/dmaengine.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define CCP_DMA_WIDTH(_mask)		\
2262306a36Sopenharmony_ci({					\
2362306a36Sopenharmony_ci	u64 mask = _mask + 1;		\
2462306a36Sopenharmony_ci	(mask == 0) ? 64 : fls64(mask);	\
2562306a36Sopenharmony_ci})
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* The CCP as a DMA provider can be configured for public or private
2862306a36Sopenharmony_ci * channels. Default is specified in the vdata for the device (PCI ID).
2962306a36Sopenharmony_ci * This module parameter will override for all channels on all devices:
3062306a36Sopenharmony_ci *   dma_chan_attr = 0x2 to force all channels public
3162306a36Sopenharmony_ci *                 = 0x1 to force all channels private
3262306a36Sopenharmony_ci *                 = 0x0 to defer to the vdata setting
3362306a36Sopenharmony_ci *                 = any other value: warning, revert to 0x0
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_cistatic unsigned int dma_chan_attr = CCP_DMA_DFLT;
3662306a36Sopenharmony_cimodule_param(dma_chan_attr, uint, 0444);
3762306a36Sopenharmony_ciMODULE_PARM_DESC(dma_chan_attr, "Set DMA channel visibility: 0 (default) = device defaults, 1 = make private, 2 = make public");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic unsigned int dmaengine = 1;
4062306a36Sopenharmony_cimodule_param(dmaengine, uint, 0444);
4162306a36Sopenharmony_ciMODULE_PARM_DESC(dmaengine, "Register services with the DMA subsystem (any non-zero value, default: 1)");
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic unsigned int ccp_get_dma_chan_attr(struct ccp_device *ccp)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	switch (dma_chan_attr) {
4662306a36Sopenharmony_ci	case CCP_DMA_DFLT:
4762306a36Sopenharmony_ci		return ccp->vdata->dma_chan_attr;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	case CCP_DMA_PRIV:
5062306a36Sopenharmony_ci		return DMA_PRIVATE;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	case CCP_DMA_PUB:
5362306a36Sopenharmony_ci		return 0;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	default:
5662306a36Sopenharmony_ci		dev_info_once(ccp->dev, "Invalid value for dma_chan_attr: %d\n",
5762306a36Sopenharmony_ci			      dma_chan_attr);
5862306a36Sopenharmony_ci		return ccp->vdata->dma_chan_attr;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic void ccp_free_cmd_resources(struct ccp_device *ccp,
6362306a36Sopenharmony_ci				   struct list_head *list)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct ccp_dma_cmd *cmd, *ctmp;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	list_for_each_entry_safe(cmd, ctmp, list, entry) {
6862306a36Sopenharmony_ci		list_del(&cmd->entry);
6962306a36Sopenharmony_ci		kmem_cache_free(ccp->dma_cmd_cache, cmd);
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void ccp_free_desc_resources(struct ccp_device *ccp,
7462306a36Sopenharmony_ci				    struct list_head *list)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct ccp_dma_desc *desc, *dtmp;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	list_for_each_entry_safe(desc, dtmp, list, entry) {
7962306a36Sopenharmony_ci		ccp_free_cmd_resources(ccp, &desc->active);
8062306a36Sopenharmony_ci		ccp_free_cmd_resources(ccp, &desc->pending);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		list_del(&desc->entry);
8362306a36Sopenharmony_ci		kmem_cache_free(ccp->dma_desc_cache, desc);
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void ccp_free_chan_resources(struct dma_chan *dma_chan)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
9062306a36Sopenharmony_ci						 dma_chan);
9162306a36Sopenharmony_ci	unsigned long flags;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s - chan=%p\n", __func__, chan);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->complete);
9862306a36Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->active);
9962306a36Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->pending);
10062306a36Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->created);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic void ccp_cleanup_desc_resources(struct ccp_device *ccp,
10662306a36Sopenharmony_ci				       struct list_head *list)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct ccp_dma_desc *desc, *dtmp;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	list_for_each_entry_safe_reverse(desc, dtmp, list, entry) {
11162306a36Sopenharmony_ci		if (!async_tx_test_ack(&desc->tx_desc))
11262306a36Sopenharmony_ci			continue;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		dev_dbg(ccp->dev, "%s - desc=%p\n", __func__, desc);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		ccp_free_cmd_resources(ccp, &desc->active);
11762306a36Sopenharmony_ci		ccp_free_cmd_resources(ccp, &desc->pending);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		list_del(&desc->entry);
12062306a36Sopenharmony_ci		kmem_cache_free(ccp->dma_desc_cache, desc);
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void ccp_do_cleanup(unsigned long data)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct ccp_dma_chan *chan = (struct ccp_dma_chan *)data;
12762306a36Sopenharmony_ci	unsigned long flags;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s - chan=%s\n", __func__,
13062306a36Sopenharmony_ci		dma_chan_name(&chan->dma_chan));
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	ccp_cleanup_desc_resources(chan->ccp, &chan->complete);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic int ccp_issue_next_cmd(struct ccp_dma_desc *desc)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct ccp_dma_cmd *cmd;
14262306a36Sopenharmony_ci	int ret;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	cmd = list_first_entry(&desc->pending, struct ccp_dma_cmd, entry);
14562306a36Sopenharmony_ci	list_move(&cmd->entry, &desc->active);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	dev_dbg(desc->ccp->dev, "%s - tx %d, cmd=%p\n", __func__,
14862306a36Sopenharmony_ci		desc->tx_desc.cookie, cmd);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	ret = ccp_enqueue_cmd(&cmd->ccp_cmd);
15162306a36Sopenharmony_ci	if (!ret || (ret == -EINPROGRESS) || (ret == -EBUSY))
15262306a36Sopenharmony_ci		return 0;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	dev_dbg(desc->ccp->dev, "%s - error: ret=%d, tx %d, cmd=%p\n", __func__,
15562306a36Sopenharmony_ci		ret, desc->tx_desc.cookie, cmd);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return ret;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic void ccp_free_active_cmd(struct ccp_dma_desc *desc)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct ccp_dma_cmd *cmd;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	cmd = list_first_entry_or_null(&desc->active, struct ccp_dma_cmd,
16562306a36Sopenharmony_ci				       entry);
16662306a36Sopenharmony_ci	if (!cmd)
16762306a36Sopenharmony_ci		return;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	dev_dbg(desc->ccp->dev, "%s - freeing tx %d cmd=%p\n",
17062306a36Sopenharmony_ci		__func__, desc->tx_desc.cookie, cmd);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	list_del(&cmd->entry);
17362306a36Sopenharmony_ci	kmem_cache_free(desc->ccp->dma_cmd_cache, cmd);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic struct ccp_dma_desc *__ccp_next_dma_desc(struct ccp_dma_chan *chan,
17762306a36Sopenharmony_ci						struct ccp_dma_desc *desc)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	/* Move current DMA descriptor to the complete list */
18062306a36Sopenharmony_ci	if (desc)
18162306a36Sopenharmony_ci		list_move(&desc->entry, &chan->complete);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* Get the next DMA descriptor on the active list */
18462306a36Sopenharmony_ci	desc = list_first_entry_or_null(&chan->active, struct ccp_dma_desc,
18562306a36Sopenharmony_ci					entry);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return desc;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic struct ccp_dma_desc *ccp_handle_active_desc(struct ccp_dma_chan *chan,
19162306a36Sopenharmony_ci						   struct ccp_dma_desc *desc)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx_desc;
19462306a36Sopenharmony_ci	unsigned long flags;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Loop over descriptors until one is found with commands */
19762306a36Sopenharmony_ci	do {
19862306a36Sopenharmony_ci		if (desc) {
19962306a36Sopenharmony_ci			/* Remove the DMA command from the list and free it */
20062306a36Sopenharmony_ci			ccp_free_active_cmd(desc);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci			if (!list_empty(&desc->pending)) {
20362306a36Sopenharmony_ci				/* No errors, keep going */
20462306a36Sopenharmony_ci				if (desc->status != DMA_ERROR)
20562306a36Sopenharmony_ci					return desc;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci				/* Error, free remaining commands and move on */
20862306a36Sopenharmony_ci				ccp_free_cmd_resources(desc->ccp,
20962306a36Sopenharmony_ci						       &desc->pending);
21062306a36Sopenharmony_ci			}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci			tx_desc = &desc->tx_desc;
21362306a36Sopenharmony_ci		} else {
21462306a36Sopenharmony_ci			tx_desc = NULL;
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		spin_lock_irqsave(&chan->lock, flags);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		if (desc) {
22062306a36Sopenharmony_ci			if (desc->status != DMA_ERROR)
22162306a36Sopenharmony_ci				desc->status = DMA_COMPLETE;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci			dev_dbg(desc->ccp->dev,
22462306a36Sopenharmony_ci				"%s - tx %d complete, status=%u\n", __func__,
22562306a36Sopenharmony_ci				desc->tx_desc.cookie, desc->status);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci			dma_cookie_complete(tx_desc);
22862306a36Sopenharmony_ci			dma_descriptor_unmap(tx_desc);
22962306a36Sopenharmony_ci		}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		desc = __ccp_next_dma_desc(chan, desc);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		spin_unlock_irqrestore(&chan->lock, flags);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		if (tx_desc) {
23662306a36Sopenharmony_ci			dmaengine_desc_get_callback_invoke(tx_desc, NULL);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci			dma_run_dependencies(tx_desc);
23962306a36Sopenharmony_ci		}
24062306a36Sopenharmony_ci	} while (desc);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return NULL;
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic struct ccp_dma_desc *__ccp_pending_to_active(struct ccp_dma_chan *chan)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct ccp_dma_desc *desc;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (list_empty(&chan->pending))
25062306a36Sopenharmony_ci		return NULL;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	desc = list_empty(&chan->active)
25362306a36Sopenharmony_ci		? list_first_entry(&chan->pending, struct ccp_dma_desc, entry)
25462306a36Sopenharmony_ci		: NULL;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	list_splice_tail_init(&chan->pending, &chan->active);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	return desc;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic void ccp_cmd_callback(void *data, int err)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct ccp_dma_desc *desc = data;
26462306a36Sopenharmony_ci	struct ccp_dma_chan *chan;
26562306a36Sopenharmony_ci	int ret;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (err == -EINPROGRESS)
26862306a36Sopenharmony_ci		return;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	chan = container_of(desc->tx_desc.chan, struct ccp_dma_chan,
27162306a36Sopenharmony_ci			    dma_chan);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s - tx %d callback, err=%d\n",
27462306a36Sopenharmony_ci		__func__, desc->tx_desc.cookie, err);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (err)
27762306a36Sopenharmony_ci		desc->status = DMA_ERROR;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	while (true) {
28062306a36Sopenharmony_ci		/* Check for DMA descriptor completion */
28162306a36Sopenharmony_ci		desc = ccp_handle_active_desc(chan, desc);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		/* Don't submit cmd if no descriptor or DMA is paused */
28462306a36Sopenharmony_ci		if (!desc || (chan->status == DMA_PAUSED))
28562306a36Sopenharmony_ci			break;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci		ret = ccp_issue_next_cmd(desc);
28862306a36Sopenharmony_ci		if (!ret)
28962306a36Sopenharmony_ci			break;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		desc->status = DMA_ERROR;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	tasklet_schedule(&chan->cleanup_tasklet);
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic dma_cookie_t ccp_tx_submit(struct dma_async_tx_descriptor *tx_desc)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct ccp_dma_desc *desc = container_of(tx_desc, struct ccp_dma_desc,
30062306a36Sopenharmony_ci						 tx_desc);
30162306a36Sopenharmony_ci	struct ccp_dma_chan *chan;
30262306a36Sopenharmony_ci	dma_cookie_t cookie;
30362306a36Sopenharmony_ci	unsigned long flags;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	chan = container_of(tx_desc->chan, struct ccp_dma_chan, dma_chan);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	cookie = dma_cookie_assign(tx_desc);
31062306a36Sopenharmony_ci	list_move_tail(&desc->entry, &chan->pending);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s - added tx descriptor %d to pending list\n",
31562306a36Sopenharmony_ci		__func__, cookie);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	return cookie;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic struct ccp_dma_cmd *ccp_alloc_dma_cmd(struct ccp_dma_chan *chan)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct ccp_dma_cmd *cmd;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	cmd = kmem_cache_alloc(chan->ccp->dma_cmd_cache, GFP_NOWAIT);
32562306a36Sopenharmony_ci	if (cmd)
32662306a36Sopenharmony_ci		memset(cmd, 0, sizeof(*cmd));
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return cmd;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic struct ccp_dma_desc *ccp_alloc_dma_desc(struct ccp_dma_chan *chan,
33262306a36Sopenharmony_ci					       unsigned long flags)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct ccp_dma_desc *desc;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	desc = kmem_cache_zalloc(chan->ccp->dma_desc_cache, GFP_NOWAIT);
33762306a36Sopenharmony_ci	if (!desc)
33862306a36Sopenharmony_ci		return NULL;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	dma_async_tx_descriptor_init(&desc->tx_desc, &chan->dma_chan);
34162306a36Sopenharmony_ci	desc->tx_desc.flags = flags;
34262306a36Sopenharmony_ci	desc->tx_desc.tx_submit = ccp_tx_submit;
34362306a36Sopenharmony_ci	desc->ccp = chan->ccp;
34462306a36Sopenharmony_ci	INIT_LIST_HEAD(&desc->entry);
34562306a36Sopenharmony_ci	INIT_LIST_HEAD(&desc->pending);
34662306a36Sopenharmony_ci	INIT_LIST_HEAD(&desc->active);
34762306a36Sopenharmony_ci	desc->status = DMA_IN_PROGRESS;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return desc;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic struct ccp_dma_desc *ccp_create_desc(struct dma_chan *dma_chan,
35362306a36Sopenharmony_ci					    struct scatterlist *dst_sg,
35462306a36Sopenharmony_ci					    unsigned int dst_nents,
35562306a36Sopenharmony_ci					    struct scatterlist *src_sg,
35662306a36Sopenharmony_ci					    unsigned int src_nents,
35762306a36Sopenharmony_ci					    unsigned long flags)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
36062306a36Sopenharmony_ci						 dma_chan);
36162306a36Sopenharmony_ci	struct ccp_device *ccp = chan->ccp;
36262306a36Sopenharmony_ci	struct ccp_dma_desc *desc;
36362306a36Sopenharmony_ci	struct ccp_dma_cmd *cmd;
36462306a36Sopenharmony_ci	struct ccp_cmd *ccp_cmd;
36562306a36Sopenharmony_ci	struct ccp_passthru_nomap_engine *ccp_pt;
36662306a36Sopenharmony_ci	unsigned int src_offset, src_len;
36762306a36Sopenharmony_ci	unsigned int dst_offset, dst_len;
36862306a36Sopenharmony_ci	unsigned int len;
36962306a36Sopenharmony_ci	unsigned long sflags;
37062306a36Sopenharmony_ci	size_t total_len;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (!dst_sg || !src_sg)
37362306a36Sopenharmony_ci		return NULL;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (!dst_nents || !src_nents)
37662306a36Sopenharmony_ci		return NULL;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	desc = ccp_alloc_dma_desc(chan, flags);
37962306a36Sopenharmony_ci	if (!desc)
38062306a36Sopenharmony_ci		return NULL;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	total_len = 0;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	src_len = sg_dma_len(src_sg);
38562306a36Sopenharmony_ci	src_offset = 0;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	dst_len = sg_dma_len(dst_sg);
38862306a36Sopenharmony_ci	dst_offset = 0;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	while (true) {
39162306a36Sopenharmony_ci		if (!src_len) {
39262306a36Sopenharmony_ci			src_nents--;
39362306a36Sopenharmony_ci			if (!src_nents)
39462306a36Sopenharmony_ci				break;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci			src_sg = sg_next(src_sg);
39762306a36Sopenharmony_ci			if (!src_sg)
39862306a36Sopenharmony_ci				break;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci			src_len = sg_dma_len(src_sg);
40162306a36Sopenharmony_ci			src_offset = 0;
40262306a36Sopenharmony_ci			continue;
40362306a36Sopenharmony_ci		}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		if (!dst_len) {
40662306a36Sopenharmony_ci			dst_nents--;
40762306a36Sopenharmony_ci			if (!dst_nents)
40862306a36Sopenharmony_ci				break;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci			dst_sg = sg_next(dst_sg);
41162306a36Sopenharmony_ci			if (!dst_sg)
41262306a36Sopenharmony_ci				break;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci			dst_len = sg_dma_len(dst_sg);
41562306a36Sopenharmony_ci			dst_offset = 0;
41662306a36Sopenharmony_ci			continue;
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		len = min(dst_len, src_len);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		cmd = ccp_alloc_dma_cmd(chan);
42262306a36Sopenharmony_ci		if (!cmd)
42362306a36Sopenharmony_ci			goto err;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		ccp_cmd = &cmd->ccp_cmd;
42662306a36Sopenharmony_ci		ccp_cmd->ccp = chan->ccp;
42762306a36Sopenharmony_ci		ccp_pt = &ccp_cmd->u.passthru_nomap;
42862306a36Sopenharmony_ci		ccp_cmd->flags = CCP_CMD_MAY_BACKLOG;
42962306a36Sopenharmony_ci		ccp_cmd->flags |= CCP_CMD_PASSTHRU_NO_DMA_MAP;
43062306a36Sopenharmony_ci		ccp_cmd->engine = CCP_ENGINE_PASSTHRU;
43162306a36Sopenharmony_ci		ccp_pt->bit_mod = CCP_PASSTHRU_BITWISE_NOOP;
43262306a36Sopenharmony_ci		ccp_pt->byte_swap = CCP_PASSTHRU_BYTESWAP_NOOP;
43362306a36Sopenharmony_ci		ccp_pt->src_dma = sg_dma_address(src_sg) + src_offset;
43462306a36Sopenharmony_ci		ccp_pt->dst_dma = sg_dma_address(dst_sg) + dst_offset;
43562306a36Sopenharmony_ci		ccp_pt->src_len = len;
43662306a36Sopenharmony_ci		ccp_pt->final = 1;
43762306a36Sopenharmony_ci		ccp_cmd->callback = ccp_cmd_callback;
43862306a36Sopenharmony_ci		ccp_cmd->data = desc;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		list_add_tail(&cmd->entry, &desc->pending);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		dev_dbg(ccp->dev,
44362306a36Sopenharmony_ci			"%s - cmd=%p, src=%pad, dst=%pad, len=%llu\n", __func__,
44462306a36Sopenharmony_ci			cmd, &ccp_pt->src_dma,
44562306a36Sopenharmony_ci			&ccp_pt->dst_dma, ccp_pt->src_len);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		total_len += len;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		src_len -= len;
45062306a36Sopenharmony_ci		src_offset += len;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		dst_len -= len;
45362306a36Sopenharmony_ci		dst_offset += len;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	desc->len = total_len;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (list_empty(&desc->pending))
45962306a36Sopenharmony_ci		goto err;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	dev_dbg(ccp->dev, "%s - desc=%p\n", __func__, desc);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	spin_lock_irqsave(&chan->lock, sflags);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	list_add_tail(&desc->entry, &chan->created);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, sflags);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	return desc;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cierr:
47262306a36Sopenharmony_ci	ccp_free_cmd_resources(ccp, &desc->pending);
47362306a36Sopenharmony_ci	kmem_cache_free(ccp->dma_desc_cache, desc);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	return NULL;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *ccp_prep_dma_memcpy(
47962306a36Sopenharmony_ci	struct dma_chan *dma_chan, dma_addr_t dst, dma_addr_t src, size_t len,
48062306a36Sopenharmony_ci	unsigned long flags)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
48362306a36Sopenharmony_ci						 dma_chan);
48462306a36Sopenharmony_ci	struct ccp_dma_desc *desc;
48562306a36Sopenharmony_ci	struct scatterlist dst_sg, src_sg;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	dev_dbg(chan->ccp->dev,
48862306a36Sopenharmony_ci		"%s - src=%pad, dst=%pad, len=%zu, flags=%#lx\n",
48962306a36Sopenharmony_ci		__func__, &src, &dst, len, flags);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	sg_init_table(&dst_sg, 1);
49262306a36Sopenharmony_ci	sg_dma_address(&dst_sg) = dst;
49362306a36Sopenharmony_ci	sg_dma_len(&dst_sg) = len;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	sg_init_table(&src_sg, 1);
49662306a36Sopenharmony_ci	sg_dma_address(&src_sg) = src;
49762306a36Sopenharmony_ci	sg_dma_len(&src_sg) = len;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	desc = ccp_create_desc(dma_chan, &dst_sg, 1, &src_sg, 1, flags);
50062306a36Sopenharmony_ci	if (!desc)
50162306a36Sopenharmony_ci		return NULL;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	return &desc->tx_desc;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *ccp_prep_dma_interrupt(
50762306a36Sopenharmony_ci	struct dma_chan *dma_chan, unsigned long flags)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
51062306a36Sopenharmony_ci						 dma_chan);
51162306a36Sopenharmony_ci	struct ccp_dma_desc *desc;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	desc = ccp_alloc_dma_desc(chan, flags);
51462306a36Sopenharmony_ci	if (!desc)
51562306a36Sopenharmony_ci		return NULL;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	return &desc->tx_desc;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic void ccp_issue_pending(struct dma_chan *dma_chan)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
52362306a36Sopenharmony_ci						 dma_chan);
52462306a36Sopenharmony_ci	struct ccp_dma_desc *desc;
52562306a36Sopenharmony_ci	unsigned long flags;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s\n", __func__);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	desc = __ccp_pending_to_active(chan);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* If there was nothing active, start processing */
53662306a36Sopenharmony_ci	if (desc)
53762306a36Sopenharmony_ci		ccp_cmd_callback(desc, 0);
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic enum dma_status ccp_tx_status(struct dma_chan *dma_chan,
54162306a36Sopenharmony_ci				     dma_cookie_t cookie,
54262306a36Sopenharmony_ci				     struct dma_tx_state *state)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
54562306a36Sopenharmony_ci						 dma_chan);
54662306a36Sopenharmony_ci	struct ccp_dma_desc *desc;
54762306a36Sopenharmony_ci	enum dma_status ret;
54862306a36Sopenharmony_ci	unsigned long flags;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (chan->status == DMA_PAUSED) {
55162306a36Sopenharmony_ci		ret = DMA_PAUSED;
55262306a36Sopenharmony_ci		goto out;
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	ret = dma_cookie_status(dma_chan, cookie, state);
55662306a36Sopenharmony_ci	if (ret == DMA_COMPLETE) {
55762306a36Sopenharmony_ci		spin_lock_irqsave(&chan->lock, flags);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		/* Get status from complete chain, if still there */
56062306a36Sopenharmony_ci		list_for_each_entry(desc, &chan->complete, entry) {
56162306a36Sopenharmony_ci			if (desc->tx_desc.cookie != cookie)
56262306a36Sopenharmony_ci				continue;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci			ret = desc->status;
56562306a36Sopenharmony_ci			break;
56662306a36Sopenharmony_ci		}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		spin_unlock_irqrestore(&chan->lock, flags);
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ciout:
57262306a36Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s - %u\n", __func__, ret);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	return ret;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic int ccp_pause(struct dma_chan *dma_chan)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
58062306a36Sopenharmony_ci						 dma_chan);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	chan->status = DMA_PAUSED;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/*TODO: Wait for active DMA to complete before returning? */
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	return 0;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic int ccp_resume(struct dma_chan *dma_chan)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
59262306a36Sopenharmony_ci						 dma_chan);
59362306a36Sopenharmony_ci	struct ccp_dma_desc *desc;
59462306a36Sopenharmony_ci	unsigned long flags;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	desc = list_first_entry_or_null(&chan->active, struct ccp_dma_desc,
59962306a36Sopenharmony_ci					entry);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/* Indicate the channel is running again */
60462306a36Sopenharmony_ci	chan->status = DMA_IN_PROGRESS;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/* If there was something active, re-start */
60762306a36Sopenharmony_ci	if (desc)
60862306a36Sopenharmony_ci		ccp_cmd_callback(desc, 0);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return 0;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic int ccp_terminate_all(struct dma_chan *dma_chan)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	struct ccp_dma_chan *chan = container_of(dma_chan, struct ccp_dma_chan,
61662306a36Sopenharmony_ci						 dma_chan);
61762306a36Sopenharmony_ci	unsigned long flags;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	dev_dbg(chan->ccp->dev, "%s\n", __func__);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	/*TODO: Wait for active DMA to complete before continuing */
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	/*TODO: Purge the complete list? */
62662306a36Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->active);
62762306a36Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->pending);
62862306a36Sopenharmony_ci	ccp_free_desc_resources(chan->ccp, &chan->created);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	return 0;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic void ccp_dma_release(struct ccp_device *ccp)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	struct ccp_dma_chan *chan;
63862306a36Sopenharmony_ci	struct dma_chan *dma_chan;
63962306a36Sopenharmony_ci	unsigned int i;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	for (i = 0; i < ccp->cmd_q_count; i++) {
64262306a36Sopenharmony_ci		chan = ccp->ccp_dma_chan + i;
64362306a36Sopenharmony_ci		dma_chan = &chan->dma_chan;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci		tasklet_kill(&chan->cleanup_tasklet);
64662306a36Sopenharmony_ci		list_del_rcu(&dma_chan->device_node);
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_cistatic void ccp_dma_release_channels(struct ccp_device *ccp)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct ccp_dma_chan *chan;
65362306a36Sopenharmony_ci	struct dma_chan *dma_chan;
65462306a36Sopenharmony_ci	unsigned int i;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	for (i = 0; i < ccp->cmd_q_count; i++) {
65762306a36Sopenharmony_ci		chan = ccp->ccp_dma_chan + i;
65862306a36Sopenharmony_ci		dma_chan = &chan->dma_chan;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		if (dma_chan->client_count)
66162306a36Sopenharmony_ci			dma_release_channel(dma_chan);
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ciint ccp_dmaengine_register(struct ccp_device *ccp)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	struct ccp_dma_chan *chan;
66862306a36Sopenharmony_ci	struct dma_device *dma_dev = &ccp->dma_dev;
66962306a36Sopenharmony_ci	struct dma_chan *dma_chan;
67062306a36Sopenharmony_ci	char *dma_cmd_cache_name;
67162306a36Sopenharmony_ci	char *dma_desc_cache_name;
67262306a36Sopenharmony_ci	unsigned int i;
67362306a36Sopenharmony_ci	int ret;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	if (!dmaengine)
67662306a36Sopenharmony_ci		return 0;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	ccp->ccp_dma_chan = devm_kcalloc(ccp->dev, ccp->cmd_q_count,
67962306a36Sopenharmony_ci					 sizeof(*(ccp->ccp_dma_chan)),
68062306a36Sopenharmony_ci					 GFP_KERNEL);
68162306a36Sopenharmony_ci	if (!ccp->ccp_dma_chan)
68262306a36Sopenharmony_ci		return -ENOMEM;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	dma_cmd_cache_name = devm_kasprintf(ccp->dev, GFP_KERNEL,
68562306a36Sopenharmony_ci					    "%s-dmaengine-cmd-cache",
68662306a36Sopenharmony_ci					    ccp->name);
68762306a36Sopenharmony_ci	if (!dma_cmd_cache_name)
68862306a36Sopenharmony_ci		return -ENOMEM;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	ccp->dma_cmd_cache = kmem_cache_create(dma_cmd_cache_name,
69162306a36Sopenharmony_ci					       sizeof(struct ccp_dma_cmd),
69262306a36Sopenharmony_ci					       sizeof(void *),
69362306a36Sopenharmony_ci					       SLAB_HWCACHE_ALIGN, NULL);
69462306a36Sopenharmony_ci	if (!ccp->dma_cmd_cache)
69562306a36Sopenharmony_ci		return -ENOMEM;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	dma_desc_cache_name = devm_kasprintf(ccp->dev, GFP_KERNEL,
69862306a36Sopenharmony_ci					     "%s-dmaengine-desc-cache",
69962306a36Sopenharmony_ci					     ccp->name);
70062306a36Sopenharmony_ci	if (!dma_desc_cache_name) {
70162306a36Sopenharmony_ci		ret = -ENOMEM;
70262306a36Sopenharmony_ci		goto err_cache;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	ccp->dma_desc_cache = kmem_cache_create(dma_desc_cache_name,
70662306a36Sopenharmony_ci						sizeof(struct ccp_dma_desc),
70762306a36Sopenharmony_ci						sizeof(void *),
70862306a36Sopenharmony_ci						SLAB_HWCACHE_ALIGN, NULL);
70962306a36Sopenharmony_ci	if (!ccp->dma_desc_cache) {
71062306a36Sopenharmony_ci		ret = -ENOMEM;
71162306a36Sopenharmony_ci		goto err_cache;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	dma_dev->dev = ccp->dev;
71562306a36Sopenharmony_ci	dma_dev->src_addr_widths = CCP_DMA_WIDTH(dma_get_mask(ccp->dev));
71662306a36Sopenharmony_ci	dma_dev->dst_addr_widths = CCP_DMA_WIDTH(dma_get_mask(ccp->dev));
71762306a36Sopenharmony_ci	dma_dev->directions = DMA_MEM_TO_MEM;
71862306a36Sopenharmony_ci	dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
71962306a36Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask);
72062306a36Sopenharmony_ci	dma_cap_set(DMA_INTERRUPT, dma_dev->cap_mask);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* The DMA channels for this device can be set to public or private,
72362306a36Sopenharmony_ci	 * and overridden by the module parameter dma_chan_attr.
72462306a36Sopenharmony_ci	 * Default: according to the value in vdata (dma_chan_attr=0)
72562306a36Sopenharmony_ci	 * dma_chan_attr=0x1: all channels private (override vdata)
72662306a36Sopenharmony_ci	 * dma_chan_attr=0x2: all channels public (override vdata)
72762306a36Sopenharmony_ci	 */
72862306a36Sopenharmony_ci	if (ccp_get_dma_chan_attr(ccp) == DMA_PRIVATE)
72962306a36Sopenharmony_ci		dma_cap_set(DMA_PRIVATE, dma_dev->cap_mask);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	INIT_LIST_HEAD(&dma_dev->channels);
73262306a36Sopenharmony_ci	for (i = 0; i < ccp->cmd_q_count; i++) {
73362306a36Sopenharmony_ci		chan = ccp->ccp_dma_chan + i;
73462306a36Sopenharmony_ci		dma_chan = &chan->dma_chan;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci		chan->ccp = ccp;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci		spin_lock_init(&chan->lock);
73962306a36Sopenharmony_ci		INIT_LIST_HEAD(&chan->created);
74062306a36Sopenharmony_ci		INIT_LIST_HEAD(&chan->pending);
74162306a36Sopenharmony_ci		INIT_LIST_HEAD(&chan->active);
74262306a36Sopenharmony_ci		INIT_LIST_HEAD(&chan->complete);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci		tasklet_init(&chan->cleanup_tasklet, ccp_do_cleanup,
74562306a36Sopenharmony_ci			     (unsigned long)chan);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		dma_chan->device = dma_dev;
74862306a36Sopenharmony_ci		dma_cookie_init(dma_chan);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci		list_add_tail(&dma_chan->device_node, &dma_dev->channels);
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	dma_dev->device_free_chan_resources = ccp_free_chan_resources;
75462306a36Sopenharmony_ci	dma_dev->device_prep_dma_memcpy = ccp_prep_dma_memcpy;
75562306a36Sopenharmony_ci	dma_dev->device_prep_dma_interrupt = ccp_prep_dma_interrupt;
75662306a36Sopenharmony_ci	dma_dev->device_issue_pending = ccp_issue_pending;
75762306a36Sopenharmony_ci	dma_dev->device_tx_status = ccp_tx_status;
75862306a36Sopenharmony_ci	dma_dev->device_pause = ccp_pause;
75962306a36Sopenharmony_ci	dma_dev->device_resume = ccp_resume;
76062306a36Sopenharmony_ci	dma_dev->device_terminate_all = ccp_terminate_all;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	ret = dma_async_device_register(dma_dev);
76362306a36Sopenharmony_ci	if (ret)
76462306a36Sopenharmony_ci		goto err_reg;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	return 0;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cierr_reg:
76962306a36Sopenharmony_ci	ccp_dma_release(ccp);
77062306a36Sopenharmony_ci	kmem_cache_destroy(ccp->dma_desc_cache);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cierr_cache:
77362306a36Sopenharmony_ci	kmem_cache_destroy(ccp->dma_cmd_cache);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	return ret;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_civoid ccp_dmaengine_unregister(struct ccp_device *ccp)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	struct dma_device *dma_dev = &ccp->dma_dev;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	if (!dmaengine)
78362306a36Sopenharmony_ci		return;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	ccp_dma_release_channels(ccp);
78662306a36Sopenharmony_ci	dma_async_device_unregister(dma_dev);
78762306a36Sopenharmony_ci	ccp_dma_release(ccp);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	kmem_cache_destroy(ccp->dma_desc_cache);
79062306a36Sopenharmony_ci	kmem_cache_destroy(ccp->dma_cmd_cache);
79162306a36Sopenharmony_ci}
792