18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SiFive FU540 Platform DMA driver 48c2ecf20Sopenharmony_ci * Copyright (C) 2019 SiFive 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based partially on: 78c2ecf20Sopenharmony_ci * - drivers/dma/fsl-edma.c 88c2ecf20Sopenharmony_ci * - drivers/dma/dw-edma/ 98c2ecf20Sopenharmony_ci * - drivers/dma/pxa-dma.c 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * See the following sources for further documentation: 128c2ecf20Sopenharmony_ci * - Chapter 12 "Platform DMA Engine (PDMA)" of 138c2ecf20Sopenharmony_ci * SiFive FU540-C000 v1.0 148c2ecf20Sopenharmony_ci * https://static.dev.sifive.com/FU540-C000-v1.0.pdf 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/device.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 218c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 228c2ecf20Sopenharmony_ci#include <linux/of.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "sf-pdma.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#ifndef readq 288c2ecf20Sopenharmony_cistatic inline unsigned long long readq(void __iomem *addr) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci return readl(addr) | (((unsigned long long)readl(addr + 4)) << 32LL); 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci#endif 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#ifndef writeq 358c2ecf20Sopenharmony_cistatic inline void writeq(unsigned long long v, void __iomem *addr) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci writel(lower_32_bits(v), addr); 388c2ecf20Sopenharmony_ci writel(upper_32_bits(v), addr + 4); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci#endif 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic inline struct sf_pdma_chan *to_sf_pdma_chan(struct dma_chan *dchan) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci return container_of(dchan, struct sf_pdma_chan, vchan.chan); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic inline struct sf_pdma_desc *to_sf_pdma_desc(struct virt_dma_desc *vd) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci return container_of(vd, struct sf_pdma_desc, vdesc); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic struct sf_pdma_desc *sf_pdma_alloc_desc(struct sf_pdma_chan *chan) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct sf_pdma_desc *desc; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_NOWAIT); 578c2ecf20Sopenharmony_ci if (!desc) 588c2ecf20Sopenharmony_ci return NULL; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci desc->chan = chan; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return desc; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void sf_pdma_fill_desc(struct sf_pdma_desc *desc, 668c2ecf20Sopenharmony_ci u64 dst, u64 src, u64 size) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci desc->xfer_type = PDMA_FULL_SPEED; 698c2ecf20Sopenharmony_ci desc->xfer_size = size; 708c2ecf20Sopenharmony_ci desc->dst_addr = dst; 718c2ecf20Sopenharmony_ci desc->src_addr = src; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void sf_pdma_disclaim_chan(struct sf_pdma_chan *chan) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct pdma_regs *regs = &chan->regs; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci writel(PDMA_CLEAR_CTRL, regs->ctrl); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor * 828c2ecf20Sopenharmony_cisf_pdma_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dest, dma_addr_t src, 838c2ecf20Sopenharmony_ci size_t len, unsigned long flags) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan); 868c2ecf20Sopenharmony_ci struct sf_pdma_desc *desc; 878c2ecf20Sopenharmony_ci unsigned long iflags; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (chan && (!len || !dest || !src)) { 908c2ecf20Sopenharmony_ci dev_err(chan->pdma->dma_dev.dev, 918c2ecf20Sopenharmony_ci "Please check dma len, dest, src!\n"); 928c2ecf20Sopenharmony_ci return NULL; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci desc = sf_pdma_alloc_desc(chan); 968c2ecf20Sopenharmony_ci if (!desc) 978c2ecf20Sopenharmony_ci return NULL; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci desc->in_use = true; 1008c2ecf20Sopenharmony_ci desc->dirn = DMA_MEM_TO_MEM; 1018c2ecf20Sopenharmony_ci desc->async_tx = vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vchan.lock, iflags); 1048c2ecf20Sopenharmony_ci sf_pdma_fill_desc(desc, dest, src, len); 1058c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vchan.lock, iflags); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return desc->async_tx; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int sf_pdma_slave_config(struct dma_chan *dchan, 1118c2ecf20Sopenharmony_ci struct dma_slave_config *cfg) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci memcpy(&chan->cfg, cfg, sizeof(*cfg)); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int sf_pdma_alloc_chan_resources(struct dma_chan *dchan) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan); 1238c2ecf20Sopenharmony_ci struct pdma_regs *regs = &chan->regs; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci dma_cookie_init(dchan); 1268c2ecf20Sopenharmony_ci writel(PDMA_CLAIM_MASK, regs->ctrl); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void sf_pdma_disable_request(struct sf_pdma_chan *chan) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct pdma_regs *regs = &chan->regs; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci writel(readl(regs->ctrl) & ~PDMA_RUN_MASK, regs->ctrl); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void sf_pdma_free_chan_resources(struct dma_chan *dchan) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan); 1418c2ecf20Sopenharmony_ci unsigned long flags; 1428c2ecf20Sopenharmony_ci LIST_HEAD(head); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vchan.lock, flags); 1458c2ecf20Sopenharmony_ci sf_pdma_disable_request(chan); 1468c2ecf20Sopenharmony_ci kfree(chan->desc); 1478c2ecf20Sopenharmony_ci chan->desc = NULL; 1488c2ecf20Sopenharmony_ci vchan_get_all_descriptors(&chan->vchan, &head); 1498c2ecf20Sopenharmony_ci sf_pdma_disclaim_chan(chan); 1508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vchan.lock, flags); 1518c2ecf20Sopenharmony_ci vchan_dma_desc_free_list(&chan->vchan, &head); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic size_t sf_pdma_desc_residue(struct sf_pdma_chan *chan, 1558c2ecf20Sopenharmony_ci dma_cookie_t cookie) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct virt_dma_desc *vd = NULL; 1588c2ecf20Sopenharmony_ci struct pdma_regs *regs = &chan->regs; 1598c2ecf20Sopenharmony_ci unsigned long flags; 1608c2ecf20Sopenharmony_ci u64 residue = 0; 1618c2ecf20Sopenharmony_ci struct sf_pdma_desc *desc; 1628c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx = NULL; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vchan.lock, flags); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci list_for_each_entry(vd, &chan->vchan.desc_submitted, node) 1678c2ecf20Sopenharmony_ci if (vd->tx.cookie == cookie) 1688c2ecf20Sopenharmony_ci tx = &vd->tx; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (!tx) 1718c2ecf20Sopenharmony_ci goto out; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (cookie == tx->chan->completed_cookie) 1748c2ecf20Sopenharmony_ci goto out; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (cookie == tx->cookie) { 1778c2ecf20Sopenharmony_ci residue = readq(regs->residue); 1788c2ecf20Sopenharmony_ci } else { 1798c2ecf20Sopenharmony_ci vd = vchan_find_desc(&chan->vchan, cookie); 1808c2ecf20Sopenharmony_ci if (!vd) 1818c2ecf20Sopenharmony_ci goto out; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci desc = to_sf_pdma_desc(vd); 1848c2ecf20Sopenharmony_ci residue = desc->xfer_size; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ciout: 1888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vchan.lock, flags); 1898c2ecf20Sopenharmony_ci return residue; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic enum dma_status 1938c2ecf20Sopenharmony_cisf_pdma_tx_status(struct dma_chan *dchan, 1948c2ecf20Sopenharmony_ci dma_cookie_t cookie, 1958c2ecf20Sopenharmony_ci struct dma_tx_state *txstate) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan); 1988c2ecf20Sopenharmony_ci enum dma_status status; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci status = dma_cookie_status(dchan, cookie, txstate); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (txstate && status != DMA_ERROR) 2038c2ecf20Sopenharmony_ci dma_set_residue(txstate, sf_pdma_desc_residue(chan, cookie)); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return status; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int sf_pdma_terminate_all(struct dma_chan *dchan) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan); 2118c2ecf20Sopenharmony_ci unsigned long flags; 2128c2ecf20Sopenharmony_ci LIST_HEAD(head); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vchan.lock, flags); 2158c2ecf20Sopenharmony_ci sf_pdma_disable_request(chan); 2168c2ecf20Sopenharmony_ci kfree(chan->desc); 2178c2ecf20Sopenharmony_ci chan->desc = NULL; 2188c2ecf20Sopenharmony_ci chan->xfer_err = false; 2198c2ecf20Sopenharmony_ci vchan_get_all_descriptors(&chan->vchan, &head); 2208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vchan.lock, flags); 2218c2ecf20Sopenharmony_ci vchan_dma_desc_free_list(&chan->vchan, &head); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void sf_pdma_enable_request(struct sf_pdma_chan *chan) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct pdma_regs *regs = &chan->regs; 2298c2ecf20Sopenharmony_ci u32 v; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci v = PDMA_CLAIM_MASK | 2328c2ecf20Sopenharmony_ci PDMA_ENABLE_DONE_INT_MASK | 2338c2ecf20Sopenharmony_ci PDMA_ENABLE_ERR_INT_MASK | 2348c2ecf20Sopenharmony_ci PDMA_RUN_MASK; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci writel(v, regs->ctrl); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic struct sf_pdma_desc *sf_pdma_get_first_pending_desc(struct sf_pdma_chan *chan) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct virt_dma_chan *vchan = &chan->vchan; 2428c2ecf20Sopenharmony_ci struct virt_dma_desc *vdesc; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (list_empty(&vchan->desc_issued)) 2458c2ecf20Sopenharmony_ci return NULL; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci vdesc = list_first_entry(&vchan->desc_issued, struct virt_dma_desc, node); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return container_of(vdesc, struct sf_pdma_desc, vdesc); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic void sf_pdma_xfer_desc(struct sf_pdma_chan *chan) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct sf_pdma_desc *desc = chan->desc; 2558c2ecf20Sopenharmony_ci struct pdma_regs *regs = &chan->regs; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (!desc) { 2588c2ecf20Sopenharmony_ci dev_err(chan->pdma->dma_dev.dev, "NULL desc.\n"); 2598c2ecf20Sopenharmony_ci return; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci writel(desc->xfer_type, regs->xfer_type); 2638c2ecf20Sopenharmony_ci writeq(desc->xfer_size, regs->xfer_size); 2648c2ecf20Sopenharmony_ci writeq(desc->dst_addr, regs->dst_addr); 2658c2ecf20Sopenharmony_ci writeq(desc->src_addr, regs->src_addr); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci chan->desc = desc; 2688c2ecf20Sopenharmony_ci chan->status = DMA_IN_PROGRESS; 2698c2ecf20Sopenharmony_ci sf_pdma_enable_request(chan); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void sf_pdma_issue_pending(struct dma_chan *dchan) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan); 2758c2ecf20Sopenharmony_ci unsigned long flags; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vchan.lock, flags); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (!chan->desc && vchan_issue_pending(&chan->vchan)) { 2808c2ecf20Sopenharmony_ci /* vchan_issue_pending has made a check that desc in not NULL */ 2818c2ecf20Sopenharmony_ci chan->desc = sf_pdma_get_first_pending_desc(chan); 2828c2ecf20Sopenharmony_ci sf_pdma_xfer_desc(chan); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vchan.lock, flags); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic void sf_pdma_free_desc(struct virt_dma_desc *vdesc) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct sf_pdma_desc *desc; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci desc = to_sf_pdma_desc(vdesc); 2938c2ecf20Sopenharmony_ci desc->in_use = false; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void sf_pdma_donebh_tasklet(struct tasklet_struct *t) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan = from_tasklet(chan, t, done_tasklet); 2998c2ecf20Sopenharmony_ci unsigned long flags; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 3028c2ecf20Sopenharmony_ci if (chan->xfer_err) { 3038c2ecf20Sopenharmony_ci chan->retries = MAX_RETRY; 3048c2ecf20Sopenharmony_ci chan->status = DMA_COMPLETE; 3058c2ecf20Sopenharmony_ci chan->xfer_err = false; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vchan.lock, flags); 3108c2ecf20Sopenharmony_ci list_del(&chan->desc->vdesc.node); 3118c2ecf20Sopenharmony_ci vchan_cookie_complete(&chan->desc->vdesc); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci chan->desc = sf_pdma_get_first_pending_desc(chan); 3148c2ecf20Sopenharmony_ci if (chan->desc) 3158c2ecf20Sopenharmony_ci sf_pdma_xfer_desc(chan); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vchan.lock, flags); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic void sf_pdma_errbh_tasklet(struct tasklet_struct *t) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan = from_tasklet(chan, t, err_tasklet); 3238c2ecf20Sopenharmony_ci struct sf_pdma_desc *desc = chan->desc; 3248c2ecf20Sopenharmony_ci unsigned long flags; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 3278c2ecf20Sopenharmony_ci if (chan->retries <= 0) { 3288c2ecf20Sopenharmony_ci /* fail to recover */ 3298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 3308c2ecf20Sopenharmony_ci dmaengine_desc_get_callback_invoke(desc->async_tx, NULL); 3318c2ecf20Sopenharmony_ci } else { 3328c2ecf20Sopenharmony_ci /* retry */ 3338c2ecf20Sopenharmony_ci chan->retries--; 3348c2ecf20Sopenharmony_ci chan->xfer_err = true; 3358c2ecf20Sopenharmony_ci chan->status = DMA_ERROR; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci sf_pdma_enable_request(chan); 3388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic irqreturn_t sf_pdma_done_isr(int irq, void *dev_id) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan = dev_id; 3458c2ecf20Sopenharmony_ci struct pdma_regs *regs = &chan->regs; 3468c2ecf20Sopenharmony_ci unsigned long flags; 3478c2ecf20Sopenharmony_ci u64 residue; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vchan.lock, flags); 3508c2ecf20Sopenharmony_ci writel((readl(regs->ctrl)) & ~PDMA_DONE_STATUS_MASK, regs->ctrl); 3518c2ecf20Sopenharmony_ci residue = readq(regs->residue); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (!residue) { 3548c2ecf20Sopenharmony_ci tasklet_hi_schedule(&chan->done_tasklet); 3558c2ecf20Sopenharmony_ci } else { 3568c2ecf20Sopenharmony_ci /* submit next trascatioin if possible */ 3578c2ecf20Sopenharmony_ci struct sf_pdma_desc *desc = chan->desc; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci desc->src_addr += desc->xfer_size - residue; 3608c2ecf20Sopenharmony_ci desc->dst_addr += desc->xfer_size - residue; 3618c2ecf20Sopenharmony_ci desc->xfer_size = residue; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci sf_pdma_xfer_desc(chan); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vchan.lock, flags); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic irqreturn_t sf_pdma_err_isr(int irq, void *dev_id) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan = dev_id; 3748c2ecf20Sopenharmony_ci struct pdma_regs *regs = &chan->regs; 3758c2ecf20Sopenharmony_ci unsigned long flags; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 3788c2ecf20Sopenharmony_ci writel((readl(regs->ctrl)) & ~PDMA_ERR_STATUS_MASK, regs->ctrl); 3798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci tasklet_schedule(&chan->err_tasklet); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/** 3878c2ecf20Sopenharmony_ci * sf_pdma_irq_init() - Init PDMA IRQ Handlers 3888c2ecf20Sopenharmony_ci * @pdev: pointer of platform_device 3898c2ecf20Sopenharmony_ci * @pdma: pointer of PDMA engine. Caller should check NULL 3908c2ecf20Sopenharmony_ci * 3918c2ecf20Sopenharmony_ci * Initialize DONE and ERROR interrupt handler for 4 channels. Caller should 3928c2ecf20Sopenharmony_ci * make sure the pointer passed in are non-NULL. This function should be called 3938c2ecf20Sopenharmony_ci * only one time during the device probe. 3948c2ecf20Sopenharmony_ci * 3958c2ecf20Sopenharmony_ci * Context: Any context. 3968c2ecf20Sopenharmony_ci * 3978c2ecf20Sopenharmony_ci * Return: 3988c2ecf20Sopenharmony_ci * * 0 - OK to init all IRQ handlers 3998c2ecf20Sopenharmony_ci * * -EINVAL - Fail to request IRQ 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_cistatic int sf_pdma_irq_init(struct platform_device *pdev, struct sf_pdma *pdma) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci int irq, r, i; 4048c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci for (i = 0; i < pdma->n_chans; i++) { 4078c2ecf20Sopenharmony_ci chan = &pdma->chans[i]; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, i * 2); 4108c2ecf20Sopenharmony_ci if (irq < 0) { 4118c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ch(%d) Can't get done irq.\n", i); 4128c2ecf20Sopenharmony_ci return -EINVAL; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci r = devm_request_irq(&pdev->dev, irq, sf_pdma_done_isr, 0, 4168c2ecf20Sopenharmony_ci dev_name(&pdev->dev), (void *)chan); 4178c2ecf20Sopenharmony_ci if (r) { 4188c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Fail to attach done ISR: %d\n", r); 4198c2ecf20Sopenharmony_ci return -EINVAL; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci chan->txirq = irq; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, (i * 2) + 1); 4258c2ecf20Sopenharmony_ci if (irq < 0) { 4268c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ch(%d) Can't get err irq.\n", i); 4278c2ecf20Sopenharmony_ci return -EINVAL; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci r = devm_request_irq(&pdev->dev, irq, sf_pdma_err_isr, 0, 4318c2ecf20Sopenharmony_ci dev_name(&pdev->dev), (void *)chan); 4328c2ecf20Sopenharmony_ci if (r) { 4338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Fail to attach err ISR: %d\n", r); 4348c2ecf20Sopenharmony_ci return -EINVAL; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci chan->errirq = irq; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci/** 4448c2ecf20Sopenharmony_ci * sf_pdma_setup_chans() - Init settings of each channel 4458c2ecf20Sopenharmony_ci * @pdma: pointer of PDMA engine. Caller should check NULL 4468c2ecf20Sopenharmony_ci * 4478c2ecf20Sopenharmony_ci * Initialize all data structure and register base. Caller should make sure 4488c2ecf20Sopenharmony_ci * the pointer passed in are non-NULL. This function should be called only 4498c2ecf20Sopenharmony_ci * one time during the device probe. 4508c2ecf20Sopenharmony_ci * 4518c2ecf20Sopenharmony_ci * Context: Any context. 4528c2ecf20Sopenharmony_ci * 4538c2ecf20Sopenharmony_ci * Return: none 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_cistatic void sf_pdma_setup_chans(struct sf_pdma *pdma) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci int i; 4588c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pdma->dma_dev.channels); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci for (i = 0; i < pdma->n_chans; i++) { 4638c2ecf20Sopenharmony_ci chan = &pdma->chans[i]; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci chan->regs.ctrl = 4668c2ecf20Sopenharmony_ci SF_PDMA_REG_BASE(i) + PDMA_CTRL; 4678c2ecf20Sopenharmony_ci chan->regs.xfer_type = 4688c2ecf20Sopenharmony_ci SF_PDMA_REG_BASE(i) + PDMA_XFER_TYPE; 4698c2ecf20Sopenharmony_ci chan->regs.xfer_size = 4708c2ecf20Sopenharmony_ci SF_PDMA_REG_BASE(i) + PDMA_XFER_SIZE; 4718c2ecf20Sopenharmony_ci chan->regs.dst_addr = 4728c2ecf20Sopenharmony_ci SF_PDMA_REG_BASE(i) + PDMA_DST_ADDR; 4738c2ecf20Sopenharmony_ci chan->regs.src_addr = 4748c2ecf20Sopenharmony_ci SF_PDMA_REG_BASE(i) + PDMA_SRC_ADDR; 4758c2ecf20Sopenharmony_ci chan->regs.act_type = 4768c2ecf20Sopenharmony_ci SF_PDMA_REG_BASE(i) + PDMA_ACT_TYPE; 4778c2ecf20Sopenharmony_ci chan->regs.residue = 4788c2ecf20Sopenharmony_ci SF_PDMA_REG_BASE(i) + PDMA_REMAINING_BYTE; 4798c2ecf20Sopenharmony_ci chan->regs.cur_dst_addr = 4808c2ecf20Sopenharmony_ci SF_PDMA_REG_BASE(i) + PDMA_CUR_DST_ADDR; 4818c2ecf20Sopenharmony_ci chan->regs.cur_src_addr = 4828c2ecf20Sopenharmony_ci SF_PDMA_REG_BASE(i) + PDMA_CUR_SRC_ADDR; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci chan->pdma = pdma; 4858c2ecf20Sopenharmony_ci chan->pm_state = RUNNING; 4868c2ecf20Sopenharmony_ci chan->slave_id = i; 4878c2ecf20Sopenharmony_ci chan->xfer_err = false; 4888c2ecf20Sopenharmony_ci spin_lock_init(&chan->lock); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci chan->vchan.desc_free = sf_pdma_free_desc; 4918c2ecf20Sopenharmony_ci vchan_init(&chan->vchan, &pdma->dma_dev); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci writel(PDMA_CLEAR_CTRL, chan->regs.ctrl); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci tasklet_setup(&chan->done_tasklet, sf_pdma_donebh_tasklet); 4968c2ecf20Sopenharmony_ci tasklet_setup(&chan->err_tasklet, sf_pdma_errbh_tasklet); 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int sf_pdma_probe(struct platform_device *pdev) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct sf_pdma *pdma; 5038c2ecf20Sopenharmony_ci struct sf_pdma_chan *chan; 5048c2ecf20Sopenharmony_ci struct resource *res; 5058c2ecf20Sopenharmony_ci int len, chans; 5068c2ecf20Sopenharmony_ci int ret; 5078c2ecf20Sopenharmony_ci const enum dma_slave_buswidth widths = 5088c2ecf20Sopenharmony_ci DMA_SLAVE_BUSWIDTH_1_BYTE | DMA_SLAVE_BUSWIDTH_2_BYTES | 5098c2ecf20Sopenharmony_ci DMA_SLAVE_BUSWIDTH_4_BYTES | DMA_SLAVE_BUSWIDTH_8_BYTES | 5108c2ecf20Sopenharmony_ci DMA_SLAVE_BUSWIDTH_16_BYTES | DMA_SLAVE_BUSWIDTH_32_BYTES | 5118c2ecf20Sopenharmony_ci DMA_SLAVE_BUSWIDTH_64_BYTES; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci chans = PDMA_NR_CH; 5148c2ecf20Sopenharmony_ci len = sizeof(*pdma) + sizeof(*chan) * chans; 5158c2ecf20Sopenharmony_ci pdma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); 5168c2ecf20Sopenharmony_ci if (!pdma) 5178c2ecf20Sopenharmony_ci return -ENOMEM; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci pdma->n_chans = chans; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5228c2ecf20Sopenharmony_ci pdma->membase = devm_ioremap_resource(&pdev->dev, res); 5238c2ecf20Sopenharmony_ci if (IS_ERR(pdma->membase)) 5248c2ecf20Sopenharmony_ci return PTR_ERR(pdma->membase); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci ret = sf_pdma_irq_init(pdev, pdma); 5278c2ecf20Sopenharmony_ci if (ret) 5288c2ecf20Sopenharmony_ci return ret; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci sf_pdma_setup_chans(pdma); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci pdma->dma_dev.dev = &pdev->dev; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* Setup capability */ 5358c2ecf20Sopenharmony_ci dma_cap_set(DMA_MEMCPY, pdma->dma_dev.cap_mask); 5368c2ecf20Sopenharmony_ci pdma->dma_dev.copy_align = 2; 5378c2ecf20Sopenharmony_ci pdma->dma_dev.src_addr_widths = widths; 5388c2ecf20Sopenharmony_ci pdma->dma_dev.dst_addr_widths = widths; 5398c2ecf20Sopenharmony_ci pdma->dma_dev.directions = BIT(DMA_MEM_TO_MEM); 5408c2ecf20Sopenharmony_ci pdma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; 5418c2ecf20Sopenharmony_ci pdma->dma_dev.descriptor_reuse = true; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* Setup DMA APIs */ 5448c2ecf20Sopenharmony_ci pdma->dma_dev.device_alloc_chan_resources = 5458c2ecf20Sopenharmony_ci sf_pdma_alloc_chan_resources; 5468c2ecf20Sopenharmony_ci pdma->dma_dev.device_free_chan_resources = 5478c2ecf20Sopenharmony_ci sf_pdma_free_chan_resources; 5488c2ecf20Sopenharmony_ci pdma->dma_dev.device_tx_status = sf_pdma_tx_status; 5498c2ecf20Sopenharmony_ci pdma->dma_dev.device_prep_dma_memcpy = sf_pdma_prep_dma_memcpy; 5508c2ecf20Sopenharmony_ci pdma->dma_dev.device_config = sf_pdma_slave_config; 5518c2ecf20Sopenharmony_ci pdma->dma_dev.device_terminate_all = sf_pdma_terminate_all; 5528c2ecf20Sopenharmony_ci pdma->dma_dev.device_issue_pending = sf_pdma_issue_pending; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pdma); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 5578c2ecf20Sopenharmony_ci if (ret) 5588c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 5598c2ecf20Sopenharmony_ci "Failed to set DMA mask. Fall back to default.\n"); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci ret = dma_async_device_register(&pdma->dma_dev); 5628c2ecf20Sopenharmony_ci if (ret) { 5638c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 5648c2ecf20Sopenharmony_ci "Can't register SiFive Platform DMA. (%d)\n", ret); 5658c2ecf20Sopenharmony_ci return ret; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci return 0; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic int sf_pdma_remove(struct platform_device *pdev) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct sf_pdma *pdma = platform_get_drvdata(pdev); 5748c2ecf20Sopenharmony_ci struct sf_pdma_chan *ch; 5758c2ecf20Sopenharmony_ci int i; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci for (i = 0; i < PDMA_NR_CH; i++) { 5788c2ecf20Sopenharmony_ci ch = &pdma->chans[i]; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci devm_free_irq(&pdev->dev, ch->txirq, ch); 5818c2ecf20Sopenharmony_ci devm_free_irq(&pdev->dev, ch->errirq, ch); 5828c2ecf20Sopenharmony_ci list_del(&ch->vchan.chan.device_node); 5838c2ecf20Sopenharmony_ci tasklet_kill(&ch->vchan.task); 5848c2ecf20Sopenharmony_ci tasklet_kill(&ch->done_tasklet); 5858c2ecf20Sopenharmony_ci tasklet_kill(&ch->err_tasklet); 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci dma_async_device_unregister(&pdma->dma_dev); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic const struct of_device_id sf_pdma_dt_ids[] = { 5948c2ecf20Sopenharmony_ci { .compatible = "sifive,fu540-c000-pdma" }, 5958c2ecf20Sopenharmony_ci {}, 5968c2ecf20Sopenharmony_ci}; 5978c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sf_pdma_dt_ids); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic struct platform_driver sf_pdma_driver = { 6008c2ecf20Sopenharmony_ci .probe = sf_pdma_probe, 6018c2ecf20Sopenharmony_ci .remove = sf_pdma_remove, 6028c2ecf20Sopenharmony_ci .driver = { 6038c2ecf20Sopenharmony_ci .name = "sf-pdma", 6048c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(sf_pdma_dt_ids), 6058c2ecf20Sopenharmony_ci }, 6068c2ecf20Sopenharmony_ci}; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic int __init sf_pdma_init(void) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci return platform_driver_register(&sf_pdma_driver); 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic void __exit sf_pdma_exit(void) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci platform_driver_unregister(&sf_pdma_driver); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci/* do early init */ 6198c2ecf20Sopenharmony_cisubsys_initcall(sf_pdma_init); 6208c2ecf20Sopenharmony_cimodule_exit(sf_pdma_exit); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 6238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SiFive Platform DMA driver"); 6248c2ecf20Sopenharmony_ciMODULE_AUTHOR("Green Wan <green.wan@sifive.com>"); 625