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