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