18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates. 48c2ecf20Sopenharmony_ci * Synopsys DesignWare eDMA core driver 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 138c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/irq.h> 178c2ecf20Sopenharmony_ci#include <linux/dma/edma.h> 188c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "dw-edma-core.h" 218c2ecf20Sopenharmony_ci#include "dw-edma-v0-core.h" 228c2ecf20Sopenharmony_ci#include "../dmaengine.h" 238c2ecf20Sopenharmony_ci#include "../virt-dma.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic inline 268c2ecf20Sopenharmony_cistruct device *dchan2dev(struct dma_chan *dchan) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci return &dchan->dev->device; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic inline 328c2ecf20Sopenharmony_cistruct device *chan2dev(struct dw_edma_chan *chan) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci return &chan->vc.chan.dev->device; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic inline 388c2ecf20Sopenharmony_cistruct dw_edma_desc *vd2dw_edma_desc(struct virt_dma_desc *vd) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return container_of(vd, struct dw_edma_desc, vd); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic struct dw_edma_burst *dw_edma_alloc_burst(struct dw_edma_chunk *chunk) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct dw_edma_burst *burst; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci burst = kzalloc(sizeof(*burst), GFP_NOWAIT); 488c2ecf20Sopenharmony_ci if (unlikely(!burst)) 498c2ecf20Sopenharmony_ci return NULL; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&burst->list); 528c2ecf20Sopenharmony_ci if (chunk->burst) { 538c2ecf20Sopenharmony_ci /* Create and add new element into the linked list */ 548c2ecf20Sopenharmony_ci chunk->bursts_alloc++; 558c2ecf20Sopenharmony_ci list_add_tail(&burst->list, &chunk->burst->list); 568c2ecf20Sopenharmony_ci } else { 578c2ecf20Sopenharmony_ci /* List head */ 588c2ecf20Sopenharmony_ci chunk->bursts_alloc = 0; 598c2ecf20Sopenharmony_ci chunk->burst = burst; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return burst; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct dw_edma_chan *chan = desc->chan; 688c2ecf20Sopenharmony_ci struct dw_edma *dw = chan->chip->dw; 698c2ecf20Sopenharmony_ci struct dw_edma_chunk *chunk; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci chunk = kzalloc(sizeof(*chunk), GFP_NOWAIT); 728c2ecf20Sopenharmony_ci if (unlikely(!chunk)) 738c2ecf20Sopenharmony_ci return NULL; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&chunk->list); 768c2ecf20Sopenharmony_ci chunk->chan = chan; 778c2ecf20Sopenharmony_ci /* Toggling change bit (CB) in each chunk, this is a mechanism to 788c2ecf20Sopenharmony_ci * inform the eDMA HW block that this is a new linked list ready 798c2ecf20Sopenharmony_ci * to be consumed. 808c2ecf20Sopenharmony_ci * - Odd chunks originate CB equal to 0 818c2ecf20Sopenharmony_ci * - Even chunks originate CB equal to 1 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci chunk->cb = !(desc->chunks_alloc % 2); 848c2ecf20Sopenharmony_ci chunk->ll_region.paddr = dw->ll_region.paddr + chan->ll_off; 858c2ecf20Sopenharmony_ci chunk->ll_region.vaddr = dw->ll_region.vaddr + chan->ll_off; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (desc->chunk) { 888c2ecf20Sopenharmony_ci /* Create and add new element into the linked list */ 898c2ecf20Sopenharmony_ci if (!dw_edma_alloc_burst(chunk)) { 908c2ecf20Sopenharmony_ci kfree(chunk); 918c2ecf20Sopenharmony_ci return NULL; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci desc->chunks_alloc++; 948c2ecf20Sopenharmony_ci list_add_tail(&chunk->list, &desc->chunk->list); 958c2ecf20Sopenharmony_ci } else { 968c2ecf20Sopenharmony_ci /* List head */ 978c2ecf20Sopenharmony_ci chunk->burst = NULL; 988c2ecf20Sopenharmony_ci desc->chunks_alloc = 0; 998c2ecf20Sopenharmony_ci desc->chunk = chunk; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return chunk; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic struct dw_edma_desc *dw_edma_alloc_desc(struct dw_edma_chan *chan) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct dw_edma_desc *desc; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_NOWAIT); 1108c2ecf20Sopenharmony_ci if (unlikely(!desc)) 1118c2ecf20Sopenharmony_ci return NULL; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci desc->chan = chan; 1148c2ecf20Sopenharmony_ci if (!dw_edma_alloc_chunk(desc)) { 1158c2ecf20Sopenharmony_ci kfree(desc); 1168c2ecf20Sopenharmony_ci return NULL; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return desc; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void dw_edma_free_burst(struct dw_edma_chunk *chunk) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct dw_edma_burst *child, *_next; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Remove all the list elements */ 1278c2ecf20Sopenharmony_ci list_for_each_entry_safe(child, _next, &chunk->burst->list, list) { 1288c2ecf20Sopenharmony_ci list_del(&child->list); 1298c2ecf20Sopenharmony_ci kfree(child); 1308c2ecf20Sopenharmony_ci chunk->bursts_alloc--; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Remove the list head */ 1348c2ecf20Sopenharmony_ci kfree(child); 1358c2ecf20Sopenharmony_ci chunk->burst = NULL; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void dw_edma_free_chunk(struct dw_edma_desc *desc) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct dw_edma_chunk *child, *_next; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (!desc->chunk) 1438c2ecf20Sopenharmony_ci return; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* Remove all the list elements */ 1468c2ecf20Sopenharmony_ci list_for_each_entry_safe(child, _next, &desc->chunk->list, list) { 1478c2ecf20Sopenharmony_ci dw_edma_free_burst(child); 1488c2ecf20Sopenharmony_ci list_del(&child->list); 1498c2ecf20Sopenharmony_ci kfree(child); 1508c2ecf20Sopenharmony_ci desc->chunks_alloc--; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Remove the list head */ 1548c2ecf20Sopenharmony_ci kfree(child); 1558c2ecf20Sopenharmony_ci desc->chunk = NULL; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void dw_edma_free_desc(struct dw_edma_desc *desc) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci dw_edma_free_chunk(desc); 1618c2ecf20Sopenharmony_ci kfree(desc); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void vchan_free_desc(struct virt_dma_desc *vdesc) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci dw_edma_free_desc(vd2dw_edma_desc(vdesc)); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int dw_edma_start_transfer(struct dw_edma_chan *chan) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct dw_edma_chunk *child; 1728c2ecf20Sopenharmony_ci struct dw_edma_desc *desc; 1738c2ecf20Sopenharmony_ci struct virt_dma_desc *vd; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci vd = vchan_next_desc(&chan->vc); 1768c2ecf20Sopenharmony_ci if (!vd) 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci desc = vd2dw_edma_desc(vd); 1808c2ecf20Sopenharmony_ci if (!desc) 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci child = list_first_entry_or_null(&desc->chunk->list, 1848c2ecf20Sopenharmony_ci struct dw_edma_chunk, list); 1858c2ecf20Sopenharmony_ci if (!child) 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci dw_edma_v0_core_start(child, !desc->xfer_sz); 1898c2ecf20Sopenharmony_ci desc->xfer_sz += child->ll_region.sz; 1908c2ecf20Sopenharmony_ci dw_edma_free_burst(child); 1918c2ecf20Sopenharmony_ci list_del(&child->list); 1928c2ecf20Sopenharmony_ci kfree(child); 1938c2ecf20Sopenharmony_ci desc->chunks_alloc--; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 1; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int dw_edma_device_config(struct dma_chan *dchan, 1998c2ecf20Sopenharmony_ci struct dma_slave_config *config) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci memcpy(&chan->config, config, sizeof(*config)); 2048c2ecf20Sopenharmony_ci chan->configured = true; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int dw_edma_device_pause(struct dma_chan *dchan) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); 2128c2ecf20Sopenharmony_ci int err = 0; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (!chan->configured) 2158c2ecf20Sopenharmony_ci err = -EPERM; 2168c2ecf20Sopenharmony_ci else if (chan->status != EDMA_ST_BUSY) 2178c2ecf20Sopenharmony_ci err = -EPERM; 2188c2ecf20Sopenharmony_ci else if (chan->request != EDMA_REQ_NONE) 2198c2ecf20Sopenharmony_ci err = -EPERM; 2208c2ecf20Sopenharmony_ci else 2218c2ecf20Sopenharmony_ci chan->request = EDMA_REQ_PAUSE; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return err; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int dw_edma_device_resume(struct dma_chan *dchan) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); 2298c2ecf20Sopenharmony_ci int err = 0; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (!chan->configured) { 2328c2ecf20Sopenharmony_ci err = -EPERM; 2338c2ecf20Sopenharmony_ci } else if (chan->status != EDMA_ST_PAUSE) { 2348c2ecf20Sopenharmony_ci err = -EPERM; 2358c2ecf20Sopenharmony_ci } else if (chan->request != EDMA_REQ_NONE) { 2368c2ecf20Sopenharmony_ci err = -EPERM; 2378c2ecf20Sopenharmony_ci } else { 2388c2ecf20Sopenharmony_ci chan->status = EDMA_ST_BUSY; 2398c2ecf20Sopenharmony_ci dw_edma_start_transfer(chan); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return err; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int dw_edma_device_terminate_all(struct dma_chan *dchan) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); 2488c2ecf20Sopenharmony_ci int err = 0; 2498c2ecf20Sopenharmony_ci LIST_HEAD(head); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (!chan->configured) { 2528c2ecf20Sopenharmony_ci /* Do nothing */ 2538c2ecf20Sopenharmony_ci } else if (chan->status == EDMA_ST_PAUSE) { 2548c2ecf20Sopenharmony_ci chan->status = EDMA_ST_IDLE; 2558c2ecf20Sopenharmony_ci chan->configured = false; 2568c2ecf20Sopenharmony_ci } else if (chan->status == EDMA_ST_IDLE) { 2578c2ecf20Sopenharmony_ci chan->configured = false; 2588c2ecf20Sopenharmony_ci } else if (dw_edma_v0_core_ch_status(chan) == DMA_COMPLETE) { 2598c2ecf20Sopenharmony_ci /* 2608c2ecf20Sopenharmony_ci * The channel is in a false BUSY state, probably didn't 2618c2ecf20Sopenharmony_ci * receive or lost an interrupt 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci chan->status = EDMA_ST_IDLE; 2648c2ecf20Sopenharmony_ci chan->configured = false; 2658c2ecf20Sopenharmony_ci } else if (chan->request > EDMA_REQ_PAUSE) { 2668c2ecf20Sopenharmony_ci err = -EPERM; 2678c2ecf20Sopenharmony_ci } else { 2688c2ecf20Sopenharmony_ci chan->request = EDMA_REQ_STOP; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return err; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void dw_edma_device_issue_pending(struct dma_chan *dchan) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); 2778c2ecf20Sopenharmony_ci unsigned long flags; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (!chan->configured) 2808c2ecf20Sopenharmony_ci return; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 2838c2ecf20Sopenharmony_ci if (vchan_issue_pending(&chan->vc) && chan->request == EDMA_REQ_NONE && 2848c2ecf20Sopenharmony_ci chan->status == EDMA_ST_IDLE) { 2858c2ecf20Sopenharmony_ci chan->status = EDMA_ST_BUSY; 2868c2ecf20Sopenharmony_ci dw_edma_start_transfer(chan); 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic enum dma_status 2928c2ecf20Sopenharmony_cidw_edma_device_tx_status(struct dma_chan *dchan, dma_cookie_t cookie, 2938c2ecf20Sopenharmony_ci struct dma_tx_state *txstate) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); 2968c2ecf20Sopenharmony_ci struct dw_edma_desc *desc; 2978c2ecf20Sopenharmony_ci struct virt_dma_desc *vd; 2988c2ecf20Sopenharmony_ci unsigned long flags; 2998c2ecf20Sopenharmony_ci enum dma_status ret; 3008c2ecf20Sopenharmony_ci u32 residue = 0; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ret = dma_cookie_status(dchan, cookie, txstate); 3038c2ecf20Sopenharmony_ci if (ret == DMA_COMPLETE) 3048c2ecf20Sopenharmony_ci return ret; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (ret == DMA_IN_PROGRESS && chan->status == EDMA_ST_PAUSE) 3078c2ecf20Sopenharmony_ci ret = DMA_PAUSED; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (!txstate) 3108c2ecf20Sopenharmony_ci goto ret_residue; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 3138c2ecf20Sopenharmony_ci vd = vchan_find_desc(&chan->vc, cookie); 3148c2ecf20Sopenharmony_ci if (vd) { 3158c2ecf20Sopenharmony_ci desc = vd2dw_edma_desc(vd); 3168c2ecf20Sopenharmony_ci if (desc) 3178c2ecf20Sopenharmony_ci residue = desc->alloc_sz - desc->xfer_sz; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ciret_residue: 3228c2ecf20Sopenharmony_ci dma_set_residue(txstate, residue); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return ret; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor * 3288c2ecf20Sopenharmony_cidw_edma_device_transfer(struct dw_edma_transfer *xfer) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan); 3318c2ecf20Sopenharmony_ci enum dma_transfer_direction dir = xfer->direction; 3328c2ecf20Sopenharmony_ci phys_addr_t src_addr, dst_addr; 3338c2ecf20Sopenharmony_ci struct scatterlist *sg = NULL; 3348c2ecf20Sopenharmony_ci struct dw_edma_chunk *chunk; 3358c2ecf20Sopenharmony_ci struct dw_edma_burst *burst; 3368c2ecf20Sopenharmony_ci struct dw_edma_desc *desc; 3378c2ecf20Sopenharmony_ci u32 cnt; 3388c2ecf20Sopenharmony_ci int i; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (!chan->configured) 3418c2ecf20Sopenharmony_ci return NULL; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci switch (chan->config.direction) { 3448c2ecf20Sopenharmony_ci case DMA_DEV_TO_MEM: /* local dma */ 3458c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM && chan->dir == EDMA_DIR_READ) 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci return NULL; 3488c2ecf20Sopenharmony_ci case DMA_MEM_TO_DEV: /* local dma */ 3498c2ecf20Sopenharmony_ci if (dir == DMA_MEM_TO_DEV && chan->dir == EDMA_DIR_WRITE) 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci return NULL; 3528c2ecf20Sopenharmony_ci default: /* remote dma */ 3538c2ecf20Sopenharmony_ci if (dir == DMA_MEM_TO_DEV && chan->dir == EDMA_DIR_READ) 3548c2ecf20Sopenharmony_ci break; 3558c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM && chan->dir == EDMA_DIR_WRITE) 3568c2ecf20Sopenharmony_ci break; 3578c2ecf20Sopenharmony_ci return NULL; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (xfer->cyclic) { 3618c2ecf20Sopenharmony_ci if (!xfer->xfer.cyclic.len || !xfer->xfer.cyclic.cnt) 3628c2ecf20Sopenharmony_ci return NULL; 3638c2ecf20Sopenharmony_ci } else { 3648c2ecf20Sopenharmony_ci if (xfer->xfer.sg.len < 1) 3658c2ecf20Sopenharmony_ci return NULL; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci desc = dw_edma_alloc_desc(chan); 3698c2ecf20Sopenharmony_ci if (unlikely(!desc)) 3708c2ecf20Sopenharmony_ci goto err_alloc; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci chunk = dw_edma_alloc_chunk(desc); 3738c2ecf20Sopenharmony_ci if (unlikely(!chunk)) 3748c2ecf20Sopenharmony_ci goto err_alloc; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci src_addr = chan->config.src_addr; 3778c2ecf20Sopenharmony_ci dst_addr = chan->config.dst_addr; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (xfer->cyclic) { 3808c2ecf20Sopenharmony_ci cnt = xfer->xfer.cyclic.cnt; 3818c2ecf20Sopenharmony_ci } else { 3828c2ecf20Sopenharmony_ci cnt = xfer->xfer.sg.len; 3838c2ecf20Sopenharmony_ci sg = xfer->xfer.sg.sgl; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci for (i = 0; i < cnt; i++) { 3878c2ecf20Sopenharmony_ci if (!xfer->cyclic && !sg) 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (chunk->bursts_alloc == chan->ll_max) { 3918c2ecf20Sopenharmony_ci chunk = dw_edma_alloc_chunk(desc); 3928c2ecf20Sopenharmony_ci if (unlikely(!chunk)) 3938c2ecf20Sopenharmony_ci goto err_alloc; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci burst = dw_edma_alloc_burst(chunk); 3978c2ecf20Sopenharmony_ci if (unlikely(!burst)) 3988c2ecf20Sopenharmony_ci goto err_alloc; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (xfer->cyclic) 4018c2ecf20Sopenharmony_ci burst->sz = xfer->xfer.cyclic.len; 4028c2ecf20Sopenharmony_ci else 4038c2ecf20Sopenharmony_ci burst->sz = sg_dma_len(sg); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci chunk->ll_region.sz += burst->sz; 4068c2ecf20Sopenharmony_ci desc->alloc_sz += burst->sz; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) { 4098c2ecf20Sopenharmony_ci burst->sar = src_addr; 4108c2ecf20Sopenharmony_ci if (xfer->cyclic) { 4118c2ecf20Sopenharmony_ci burst->dar = xfer->xfer.cyclic.paddr; 4128c2ecf20Sopenharmony_ci } else { 4138c2ecf20Sopenharmony_ci burst->dar = dst_addr; 4148c2ecf20Sopenharmony_ci /* Unlike the typical assumption by other 4158c2ecf20Sopenharmony_ci * drivers/IPs the peripheral memory isn't 4168c2ecf20Sopenharmony_ci * a FIFO memory, in this case, it's a 4178c2ecf20Sopenharmony_ci * linear memory and that why the source 4188c2ecf20Sopenharmony_ci * and destination addresses are increased 4198c2ecf20Sopenharmony_ci * by the same portion (data length) 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci } else { 4238c2ecf20Sopenharmony_ci burst->dar = dst_addr; 4248c2ecf20Sopenharmony_ci if (xfer->cyclic) { 4258c2ecf20Sopenharmony_ci burst->sar = xfer->xfer.cyclic.paddr; 4268c2ecf20Sopenharmony_ci } else { 4278c2ecf20Sopenharmony_ci burst->sar = src_addr; 4288c2ecf20Sopenharmony_ci /* Unlike the typical assumption by other 4298c2ecf20Sopenharmony_ci * drivers/IPs the peripheral memory isn't 4308c2ecf20Sopenharmony_ci * a FIFO memory, in this case, it's a 4318c2ecf20Sopenharmony_ci * linear memory and that why the source 4328c2ecf20Sopenharmony_ci * and destination addresses are increased 4338c2ecf20Sopenharmony_ci * by the same portion (data length) 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (!xfer->cyclic) { 4398c2ecf20Sopenharmony_ci src_addr += sg_dma_len(sg); 4408c2ecf20Sopenharmony_ci dst_addr += sg_dma_len(sg); 4418c2ecf20Sopenharmony_ci sg = sg_next(sg); 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci return vchan_tx_prep(&chan->vc, &desc->vd, xfer->flags); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cierr_alloc: 4488c2ecf20Sopenharmony_ci if (desc) 4498c2ecf20Sopenharmony_ci dw_edma_free_desc(desc); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return NULL; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor * 4558c2ecf20Sopenharmony_cidw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl, 4568c2ecf20Sopenharmony_ci unsigned int len, 4578c2ecf20Sopenharmony_ci enum dma_transfer_direction direction, 4588c2ecf20Sopenharmony_ci unsigned long flags, void *context) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct dw_edma_transfer xfer; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci xfer.dchan = dchan; 4638c2ecf20Sopenharmony_ci xfer.direction = direction; 4648c2ecf20Sopenharmony_ci xfer.xfer.sg.sgl = sgl; 4658c2ecf20Sopenharmony_ci xfer.xfer.sg.len = len; 4668c2ecf20Sopenharmony_ci xfer.flags = flags; 4678c2ecf20Sopenharmony_ci xfer.cyclic = false; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return dw_edma_device_transfer(&xfer); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor * 4738c2ecf20Sopenharmony_cidw_edma_device_prep_dma_cyclic(struct dma_chan *dchan, dma_addr_t paddr, 4748c2ecf20Sopenharmony_ci size_t len, size_t count, 4758c2ecf20Sopenharmony_ci enum dma_transfer_direction direction, 4768c2ecf20Sopenharmony_ci unsigned long flags) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct dw_edma_transfer xfer; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci xfer.dchan = dchan; 4818c2ecf20Sopenharmony_ci xfer.direction = direction; 4828c2ecf20Sopenharmony_ci xfer.xfer.cyclic.paddr = paddr; 4838c2ecf20Sopenharmony_ci xfer.xfer.cyclic.len = len; 4848c2ecf20Sopenharmony_ci xfer.xfer.cyclic.cnt = count; 4858c2ecf20Sopenharmony_ci xfer.flags = flags; 4868c2ecf20Sopenharmony_ci xfer.cyclic = true; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return dw_edma_device_transfer(&xfer); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic void dw_edma_done_interrupt(struct dw_edma_chan *chan) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct dw_edma_desc *desc; 4948c2ecf20Sopenharmony_ci struct virt_dma_desc *vd; 4958c2ecf20Sopenharmony_ci unsigned long flags; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci dw_edma_v0_core_clear_done_int(chan); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 5008c2ecf20Sopenharmony_ci vd = vchan_next_desc(&chan->vc); 5018c2ecf20Sopenharmony_ci if (vd) { 5028c2ecf20Sopenharmony_ci switch (chan->request) { 5038c2ecf20Sopenharmony_ci case EDMA_REQ_NONE: 5048c2ecf20Sopenharmony_ci desc = vd2dw_edma_desc(vd); 5058c2ecf20Sopenharmony_ci if (!desc->chunks_alloc) { 5068c2ecf20Sopenharmony_ci list_del(&vd->node); 5078c2ecf20Sopenharmony_ci vchan_cookie_complete(vd); 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* Continue transferring if there are remaining chunks or issued requests. 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_ci chan->status = dw_edma_start_transfer(chan) ? EDMA_ST_BUSY : EDMA_ST_IDLE; 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci case EDMA_REQ_STOP: 5168c2ecf20Sopenharmony_ci list_del(&vd->node); 5178c2ecf20Sopenharmony_ci vchan_cookie_complete(vd); 5188c2ecf20Sopenharmony_ci chan->request = EDMA_REQ_NONE; 5198c2ecf20Sopenharmony_ci chan->status = EDMA_ST_IDLE; 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci case EDMA_REQ_PAUSE: 5238c2ecf20Sopenharmony_ci chan->request = EDMA_REQ_NONE; 5248c2ecf20Sopenharmony_ci chan->status = EDMA_ST_PAUSE; 5258c2ecf20Sopenharmony_ci break; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci default: 5288c2ecf20Sopenharmony_ci break; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic void dw_edma_abort_interrupt(struct dw_edma_chan *chan) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct virt_dma_desc *vd; 5378c2ecf20Sopenharmony_ci unsigned long flags; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci dw_edma_v0_core_clear_abort_int(chan); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 5428c2ecf20Sopenharmony_ci vd = vchan_next_desc(&chan->vc); 5438c2ecf20Sopenharmony_ci if (vd) { 5448c2ecf20Sopenharmony_ci list_del(&vd->node); 5458c2ecf20Sopenharmony_ci vchan_cookie_complete(vd); 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 5488c2ecf20Sopenharmony_ci chan->request = EDMA_REQ_NONE; 5498c2ecf20Sopenharmony_ci chan->status = EDMA_ST_IDLE; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic irqreturn_t dw_edma_interrupt(int irq, void *data, bool write) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci struct dw_edma_irq *dw_irq = data; 5558c2ecf20Sopenharmony_ci struct dw_edma *dw = dw_irq->dw; 5568c2ecf20Sopenharmony_ci unsigned long total, pos, val; 5578c2ecf20Sopenharmony_ci unsigned long off; 5588c2ecf20Sopenharmony_ci u32 mask; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (write) { 5618c2ecf20Sopenharmony_ci total = dw->wr_ch_cnt; 5628c2ecf20Sopenharmony_ci off = 0; 5638c2ecf20Sopenharmony_ci mask = dw_irq->wr_mask; 5648c2ecf20Sopenharmony_ci } else { 5658c2ecf20Sopenharmony_ci total = dw->rd_ch_cnt; 5668c2ecf20Sopenharmony_ci off = dw->wr_ch_cnt; 5678c2ecf20Sopenharmony_ci mask = dw_irq->rd_mask; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci val = dw_edma_v0_core_status_done_int(dw, write ? 5718c2ecf20Sopenharmony_ci EDMA_DIR_WRITE : 5728c2ecf20Sopenharmony_ci EDMA_DIR_READ); 5738c2ecf20Sopenharmony_ci val &= mask; 5748c2ecf20Sopenharmony_ci for_each_set_bit(pos, &val, total) { 5758c2ecf20Sopenharmony_ci struct dw_edma_chan *chan = &dw->chan[pos + off]; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci dw_edma_done_interrupt(chan); 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci val = dw_edma_v0_core_status_abort_int(dw, write ? 5818c2ecf20Sopenharmony_ci EDMA_DIR_WRITE : 5828c2ecf20Sopenharmony_ci EDMA_DIR_READ); 5838c2ecf20Sopenharmony_ci val &= mask; 5848c2ecf20Sopenharmony_ci for_each_set_bit(pos, &val, total) { 5858c2ecf20Sopenharmony_ci struct dw_edma_chan *chan = &dw->chan[pos + off]; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci dw_edma_abort_interrupt(chan); 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic inline irqreturn_t dw_edma_interrupt_write(int irq, void *data) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci return dw_edma_interrupt(irq, data, true); 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic inline irqreturn_t dw_edma_interrupt_read(int irq, void *data) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci return dw_edma_interrupt(irq, data, false); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic irqreturn_t dw_edma_interrupt_common(int irq, void *data) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci dw_edma_interrupt(irq, data, true); 6068c2ecf20Sopenharmony_ci dw_edma_interrupt(irq, data, false); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic int dw_edma_alloc_chan_resources(struct dma_chan *dchan) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (chan->status != EDMA_ST_IDLE) 6168c2ecf20Sopenharmony_ci return -EBUSY; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci pm_runtime_get(chan->chip->dev); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci return 0; 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic void dw_edma_free_chan_resources(struct dma_chan *dchan) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(5000); 6268c2ecf20Sopenharmony_ci struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); 6278c2ecf20Sopenharmony_ci int ret; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) { 6308c2ecf20Sopenharmony_ci ret = dw_edma_device_terminate_all(dchan); 6318c2ecf20Sopenharmony_ci if (!ret) 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, timeout)) 6358c2ecf20Sopenharmony_ci return; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci cpu_relax(); 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci pm_runtime_put(chan->chip->dev); 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic int dw_edma_channel_setup(struct dw_edma_chip *chip, bool write, 6448c2ecf20Sopenharmony_ci u32 wr_alloc, u32 rd_alloc) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct dw_edma_region *dt_region; 6478c2ecf20Sopenharmony_ci struct device *dev = chip->dev; 6488c2ecf20Sopenharmony_ci struct dw_edma *dw = chip->dw; 6498c2ecf20Sopenharmony_ci struct dw_edma_chan *chan; 6508c2ecf20Sopenharmony_ci size_t ll_chunk, dt_chunk; 6518c2ecf20Sopenharmony_ci struct dw_edma_irq *irq; 6528c2ecf20Sopenharmony_ci struct dma_device *dma; 6538c2ecf20Sopenharmony_ci u32 i, j, cnt, ch_cnt; 6548c2ecf20Sopenharmony_ci u32 alloc, off_alloc; 6558c2ecf20Sopenharmony_ci int err = 0; 6568c2ecf20Sopenharmony_ci u32 pos; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt; 6598c2ecf20Sopenharmony_ci ll_chunk = dw->ll_region.sz; 6608c2ecf20Sopenharmony_ci dt_chunk = dw->dt_region.sz; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* Calculate linked list chunk for each channel */ 6638c2ecf20Sopenharmony_ci ll_chunk /= roundup_pow_of_two(ch_cnt); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* Calculate linked list chunk for each channel */ 6668c2ecf20Sopenharmony_ci dt_chunk /= roundup_pow_of_two(ch_cnt); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (write) { 6698c2ecf20Sopenharmony_ci i = 0; 6708c2ecf20Sopenharmony_ci cnt = dw->wr_ch_cnt; 6718c2ecf20Sopenharmony_ci dma = &dw->wr_edma; 6728c2ecf20Sopenharmony_ci alloc = wr_alloc; 6738c2ecf20Sopenharmony_ci off_alloc = 0; 6748c2ecf20Sopenharmony_ci } else { 6758c2ecf20Sopenharmony_ci i = dw->wr_ch_cnt; 6768c2ecf20Sopenharmony_ci cnt = dw->rd_ch_cnt; 6778c2ecf20Sopenharmony_ci dma = &dw->rd_edma; 6788c2ecf20Sopenharmony_ci alloc = rd_alloc; 6798c2ecf20Sopenharmony_ci off_alloc = wr_alloc; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dma->channels); 6838c2ecf20Sopenharmony_ci for (j = 0; (alloc || dw->nr_irqs == 1) && j < cnt; j++, i++) { 6848c2ecf20Sopenharmony_ci chan = &dw->chan[i]; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci dt_region = devm_kzalloc(dev, sizeof(*dt_region), GFP_KERNEL); 6878c2ecf20Sopenharmony_ci if (!dt_region) 6888c2ecf20Sopenharmony_ci return -ENOMEM; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci chan->vc.chan.private = dt_region; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci chan->chip = chip; 6938c2ecf20Sopenharmony_ci chan->id = j; 6948c2ecf20Sopenharmony_ci chan->dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ; 6958c2ecf20Sopenharmony_ci chan->configured = false; 6968c2ecf20Sopenharmony_ci chan->request = EDMA_REQ_NONE; 6978c2ecf20Sopenharmony_ci chan->status = EDMA_ST_IDLE; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci chan->ll_off = (ll_chunk * i); 7008c2ecf20Sopenharmony_ci chan->ll_max = (ll_chunk / EDMA_LL_SZ) - 1; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci chan->dt_off = (dt_chunk * i); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci dev_vdbg(dev, "L. List:\tChannel %s[%u] off=0x%.8lx, max_cnt=%u\n", 7058c2ecf20Sopenharmony_ci write ? "write" : "read", j, 7068c2ecf20Sopenharmony_ci chan->ll_off, chan->ll_max); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (dw->nr_irqs == 1) 7098c2ecf20Sopenharmony_ci pos = 0; 7108c2ecf20Sopenharmony_ci else 7118c2ecf20Sopenharmony_ci pos = off_alloc + (j % alloc); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci irq = &dw->irq[pos]; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (write) 7168c2ecf20Sopenharmony_ci irq->wr_mask |= BIT(j); 7178c2ecf20Sopenharmony_ci else 7188c2ecf20Sopenharmony_ci irq->rd_mask |= BIT(j); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci irq->dw = dw; 7218c2ecf20Sopenharmony_ci memcpy(&chan->msi, &irq->msi, sizeof(chan->msi)); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci dev_vdbg(dev, "MSI:\t\tChannel %s[%u] addr=0x%.8x%.8x, data=0x%.8x\n", 7248c2ecf20Sopenharmony_ci write ? "write" : "read", j, 7258c2ecf20Sopenharmony_ci chan->msi.address_hi, chan->msi.address_lo, 7268c2ecf20Sopenharmony_ci chan->msi.data); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci chan->vc.desc_free = vchan_free_desc; 7298c2ecf20Sopenharmony_ci vchan_init(&chan->vc, dma); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci dt_region->paddr = dw->dt_region.paddr + chan->dt_off; 7328c2ecf20Sopenharmony_ci dt_region->vaddr = dw->dt_region.vaddr + chan->dt_off; 7338c2ecf20Sopenharmony_ci dt_region->sz = dt_chunk; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci dev_vdbg(dev, "Data:\tChannel %s[%u] off=0x%.8lx\n", 7368c2ecf20Sopenharmony_ci write ? "write" : "read", j, chan->dt_off); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci dw_edma_v0_core_device_config(chan); 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* Set DMA channel capabilities */ 7428c2ecf20Sopenharmony_ci dma_cap_zero(dma->cap_mask); 7438c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, dma->cap_mask); 7448c2ecf20Sopenharmony_ci dma_cap_set(DMA_CYCLIC, dma->cap_mask); 7458c2ecf20Sopenharmony_ci dma_cap_set(DMA_PRIVATE, dma->cap_mask); 7468c2ecf20Sopenharmony_ci dma->directions = BIT(write ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV); 7478c2ecf20Sopenharmony_ci dma->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); 7488c2ecf20Sopenharmony_ci dma->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); 7498c2ecf20Sopenharmony_ci dma->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; 7508c2ecf20Sopenharmony_ci dma->chancnt = cnt; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* Set DMA channel callbacks */ 7538c2ecf20Sopenharmony_ci dma->dev = chip->dev; 7548c2ecf20Sopenharmony_ci dma->device_alloc_chan_resources = dw_edma_alloc_chan_resources; 7558c2ecf20Sopenharmony_ci dma->device_free_chan_resources = dw_edma_free_chan_resources; 7568c2ecf20Sopenharmony_ci dma->device_config = dw_edma_device_config; 7578c2ecf20Sopenharmony_ci dma->device_pause = dw_edma_device_pause; 7588c2ecf20Sopenharmony_ci dma->device_resume = dw_edma_device_resume; 7598c2ecf20Sopenharmony_ci dma->device_terminate_all = dw_edma_device_terminate_all; 7608c2ecf20Sopenharmony_ci dma->device_issue_pending = dw_edma_device_issue_pending; 7618c2ecf20Sopenharmony_ci dma->device_tx_status = dw_edma_device_tx_status; 7628c2ecf20Sopenharmony_ci dma->device_prep_slave_sg = dw_edma_device_prep_slave_sg; 7638c2ecf20Sopenharmony_ci dma->device_prep_dma_cyclic = dw_edma_device_prep_dma_cyclic; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci dma_set_max_seg_size(dma->dev, U32_MAX); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci /* Register DMA device */ 7688c2ecf20Sopenharmony_ci err = dma_async_device_register(dma); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci return err; 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic inline void dw_edma_dec_irq_alloc(int *nr_irqs, u32 *alloc, u16 cnt) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci if (*nr_irqs && *alloc < cnt) { 7768c2ecf20Sopenharmony_ci (*alloc)++; 7778c2ecf20Sopenharmony_ci (*nr_irqs)--; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic inline void dw_edma_add_irq_mask(u32 *mask, u32 alloc, u16 cnt) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci while (*mask * alloc < cnt) 7848c2ecf20Sopenharmony_ci (*mask)++; 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cistatic int dw_edma_irq_request(struct dw_edma_chip *chip, 7888c2ecf20Sopenharmony_ci u32 *wr_alloc, u32 *rd_alloc) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct device *dev = chip->dev; 7918c2ecf20Sopenharmony_ci struct dw_edma *dw = chip->dw; 7928c2ecf20Sopenharmony_ci u32 wr_mask = 1; 7938c2ecf20Sopenharmony_ci u32 rd_mask = 1; 7948c2ecf20Sopenharmony_ci int i, err = 0; 7958c2ecf20Sopenharmony_ci u32 ch_cnt; 7968c2ecf20Sopenharmony_ci int irq; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci if (dw->nr_irqs < 1) 8018c2ecf20Sopenharmony_ci return -EINVAL; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci if (dw->nr_irqs == 1) { 8048c2ecf20Sopenharmony_ci /* Common IRQ shared among all channels */ 8058c2ecf20Sopenharmony_ci irq = dw->ops->irq_vector(dev, 0); 8068c2ecf20Sopenharmony_ci err = request_irq(irq, dw_edma_interrupt_common, 8078c2ecf20Sopenharmony_ci IRQF_SHARED, dw->name, &dw->irq[0]); 8088c2ecf20Sopenharmony_ci if (err) { 8098c2ecf20Sopenharmony_ci dw->nr_irqs = 0; 8108c2ecf20Sopenharmony_ci return err; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (irq_get_msi_desc(irq)) 8148c2ecf20Sopenharmony_ci get_cached_msi_msg(irq, &dw->irq[0].msi); 8158c2ecf20Sopenharmony_ci } else { 8168c2ecf20Sopenharmony_ci /* Distribute IRQs equally among all channels */ 8178c2ecf20Sopenharmony_ci int tmp = dw->nr_irqs; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci while (tmp && (*wr_alloc + *rd_alloc) < ch_cnt) { 8208c2ecf20Sopenharmony_ci dw_edma_dec_irq_alloc(&tmp, wr_alloc, dw->wr_ch_cnt); 8218c2ecf20Sopenharmony_ci dw_edma_dec_irq_alloc(&tmp, rd_alloc, dw->rd_ch_cnt); 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci dw_edma_add_irq_mask(&wr_mask, *wr_alloc, dw->wr_ch_cnt); 8258c2ecf20Sopenharmony_ci dw_edma_add_irq_mask(&rd_mask, *rd_alloc, dw->rd_ch_cnt); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci for (i = 0; i < (*wr_alloc + *rd_alloc); i++) { 8288c2ecf20Sopenharmony_ci irq = dw->ops->irq_vector(dev, i); 8298c2ecf20Sopenharmony_ci err = request_irq(irq, 8308c2ecf20Sopenharmony_ci i < *wr_alloc ? 8318c2ecf20Sopenharmony_ci dw_edma_interrupt_write : 8328c2ecf20Sopenharmony_ci dw_edma_interrupt_read, 8338c2ecf20Sopenharmony_ci IRQF_SHARED, dw->name, 8348c2ecf20Sopenharmony_ci &dw->irq[i]); 8358c2ecf20Sopenharmony_ci if (err) { 8368c2ecf20Sopenharmony_ci dw->nr_irqs = i; 8378c2ecf20Sopenharmony_ci return err; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci if (irq_get_msi_desc(irq)) 8418c2ecf20Sopenharmony_ci get_cached_msi_msg(irq, &dw->irq[i].msi); 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci dw->nr_irqs = i; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci return err; 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ciint dw_edma_probe(struct dw_edma_chip *chip) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci struct device *dev; 8538c2ecf20Sopenharmony_ci struct dw_edma *dw; 8548c2ecf20Sopenharmony_ci u32 wr_alloc = 0; 8558c2ecf20Sopenharmony_ci u32 rd_alloc = 0; 8568c2ecf20Sopenharmony_ci int i, err; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (!chip) 8598c2ecf20Sopenharmony_ci return -EINVAL; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci dev = chip->dev; 8628c2ecf20Sopenharmony_ci if (!dev) 8638c2ecf20Sopenharmony_ci return -EINVAL; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci dw = chip->dw; 8668c2ecf20Sopenharmony_ci if (!dw || !dw->irq || !dw->ops || !dw->ops->irq_vector) 8678c2ecf20Sopenharmony_ci return -EINVAL; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci raw_spin_lock_init(&dw->lock); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci /* Find out how many write channels are supported by hardware */ 8728c2ecf20Sopenharmony_ci dw->wr_ch_cnt = dw_edma_v0_core_ch_count(dw, EDMA_DIR_WRITE); 8738c2ecf20Sopenharmony_ci if (!dw->wr_ch_cnt) 8748c2ecf20Sopenharmony_ci return -EINVAL; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci /* Find out how many read channels are supported by hardware */ 8778c2ecf20Sopenharmony_ci dw->rd_ch_cnt = dw_edma_v0_core_ch_count(dw, EDMA_DIR_READ); 8788c2ecf20Sopenharmony_ci if (!dw->rd_ch_cnt) 8798c2ecf20Sopenharmony_ci return -EINVAL; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci dev_vdbg(dev, "Channels:\twrite=%d, read=%d\n", 8828c2ecf20Sopenharmony_ci dw->wr_ch_cnt, dw->rd_ch_cnt); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci /* Allocate channels */ 8858c2ecf20Sopenharmony_ci dw->chan = devm_kcalloc(dev, dw->wr_ch_cnt + dw->rd_ch_cnt, 8868c2ecf20Sopenharmony_ci sizeof(*dw->chan), GFP_KERNEL); 8878c2ecf20Sopenharmony_ci if (!dw->chan) 8888c2ecf20Sopenharmony_ci return -ENOMEM; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci snprintf(dw->name, sizeof(dw->name), "dw-edma-core:%d", chip->id); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci /* Disable eDMA, only to establish the ideal initial conditions */ 8938c2ecf20Sopenharmony_ci dw_edma_v0_core_off(dw); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci /* Request IRQs */ 8968c2ecf20Sopenharmony_ci err = dw_edma_irq_request(chip, &wr_alloc, &rd_alloc); 8978c2ecf20Sopenharmony_ci if (err) 8988c2ecf20Sopenharmony_ci return err; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci /* Setup write channels */ 9018c2ecf20Sopenharmony_ci err = dw_edma_channel_setup(chip, true, wr_alloc, rd_alloc); 9028c2ecf20Sopenharmony_ci if (err) 9038c2ecf20Sopenharmony_ci goto err_irq_free; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci /* Setup read channels */ 9068c2ecf20Sopenharmony_ci err = dw_edma_channel_setup(chip, false, wr_alloc, rd_alloc); 9078c2ecf20Sopenharmony_ci if (err) 9088c2ecf20Sopenharmony_ci goto err_irq_free; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* Power management */ 9118c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci /* Turn debugfs on */ 9148c2ecf20Sopenharmony_ci dw_edma_v0_core_debugfs_on(chip); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci return 0; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_cierr_irq_free: 9198c2ecf20Sopenharmony_ci for (i = (dw->nr_irqs - 1); i >= 0; i--) 9208c2ecf20Sopenharmony_ci free_irq(dw->ops->irq_vector(dev, i), &dw->irq[i]); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci dw->nr_irqs = 0; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci return err; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_edma_probe); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ciint dw_edma_remove(struct dw_edma_chip *chip) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci struct dw_edma_chan *chan, *_chan; 9318c2ecf20Sopenharmony_ci struct device *dev = chip->dev; 9328c2ecf20Sopenharmony_ci struct dw_edma *dw = chip->dw; 9338c2ecf20Sopenharmony_ci int i; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci /* Disable eDMA */ 9368c2ecf20Sopenharmony_ci dw_edma_v0_core_off(dw); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci /* Free irqs */ 9398c2ecf20Sopenharmony_ci for (i = (dw->nr_irqs - 1); i >= 0; i--) 9408c2ecf20Sopenharmony_ci free_irq(dw->ops->irq_vector(dev, i), &dw->irq[i]); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci /* Power management */ 9438c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* Deregister eDMA device */ 9468c2ecf20Sopenharmony_ci dma_async_device_unregister(&dw->wr_edma); 9478c2ecf20Sopenharmony_ci list_for_each_entry_safe(chan, _chan, &dw->wr_edma.channels, 9488c2ecf20Sopenharmony_ci vc.chan.device_node) { 9498c2ecf20Sopenharmony_ci tasklet_kill(&chan->vc.task); 9508c2ecf20Sopenharmony_ci list_del(&chan->vc.chan.device_node); 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci dma_async_device_unregister(&dw->rd_edma); 9548c2ecf20Sopenharmony_ci list_for_each_entry_safe(chan, _chan, &dw->rd_edma.channels, 9558c2ecf20Sopenharmony_ci vc.chan.device_node) { 9568c2ecf20Sopenharmony_ci tasklet_kill(&chan->vc.task); 9578c2ecf20Sopenharmony_ci list_del(&chan->vc.chan.device_node); 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* Turn debugfs off */ 9618c2ecf20Sopenharmony_ci dw_edma_v0_core_debugfs_off(); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci return 0; 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dw_edma_remove); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 9688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver"); 9698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>"); 970