18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * DMA Engine support for Tsi721 PCIExpress-to-SRIO bridge 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2011-2014 Integrated Device Technology, Inc. 68c2ecf20Sopenharmony_ci * Alexandre Bounine <alexandre.bounine@idt.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/ioport.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/pci.h> 168c2ecf20Sopenharmony_ci#include <linux/rio.h> 178c2ecf20Sopenharmony_ci#include <linux/rio_drv.h> 188c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/kfifo.h> 218c2ecf20Sopenharmony_ci#include <linux/sched.h> 228c2ecf20Sopenharmony_ci#include <linux/delay.h> 238c2ecf20Sopenharmony_ci#include "../../dma/dmaengine.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "tsi721.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI 288c2ecf20Sopenharmony_cistatic irqreturn_t tsi721_bdma_msix(int irq, void *ptr); 298c2ecf20Sopenharmony_ci#endif 308c2ecf20Sopenharmony_cistatic int tsi721_submit_sg(struct tsi721_tx_desc *desc); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic unsigned int dma_desc_per_channel = 128; 338c2ecf20Sopenharmony_cimodule_param(dma_desc_per_channel, uint, S_IRUGO); 348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma_desc_per_channel, 358c2ecf20Sopenharmony_ci "Number of DMA descriptors per channel (default: 128)"); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic unsigned int dma_txqueue_sz = 16; 388c2ecf20Sopenharmony_cimodule_param(dma_txqueue_sz, uint, S_IRUGO); 398c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma_txqueue_sz, 408c2ecf20Sopenharmony_ci "DMA Transactions Queue Size (default: 16)"); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic u8 dma_sel = 0x7f; 438c2ecf20Sopenharmony_cimodule_param(dma_sel, byte, S_IRUGO); 448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma_sel, 458c2ecf20Sopenharmony_ci "DMA Channel Selection Mask (default: 0x7f = all)"); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic inline struct tsi721_bdma_chan *to_tsi721_chan(struct dma_chan *chan) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci return container_of(chan, struct tsi721_bdma_chan, dchan); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic inline struct tsi721_device *to_tsi721(struct dma_device *ddev) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci return container_of(ddev, struct rio_mport, dma)->priv; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic inline 588c2ecf20Sopenharmony_cistruct tsi721_tx_desc *to_tsi721_desc(struct dma_async_tx_descriptor *txd) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci return container_of(txd, struct tsi721_tx_desc, txd); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int tsi721_bdma_ch_init(struct tsi721_bdma_chan *bdma_chan, int bd_num) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct tsi721_dma_desc *bd_ptr; 668c2ecf20Sopenharmony_ci struct device *dev = bdma_chan->dchan.device->dev; 678c2ecf20Sopenharmony_ci u64 *sts_ptr; 688c2ecf20Sopenharmony_ci dma_addr_t bd_phys; 698c2ecf20Sopenharmony_ci dma_addr_t sts_phys; 708c2ecf20Sopenharmony_ci int sts_size; 718c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI 728c2ecf20Sopenharmony_ci struct tsi721_device *priv = to_tsi721(bdma_chan->dchan.device); 738c2ecf20Sopenharmony_ci#endif 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci tsi_debug(DMA, &bdma_chan->dchan.dev->device, "DMAC%d", bdma_chan->id); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * Allocate space for DMA descriptors 798c2ecf20Sopenharmony_ci * (add an extra element for link descriptor) 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci bd_ptr = dma_alloc_coherent(dev, 828c2ecf20Sopenharmony_ci (bd_num + 1) * sizeof(struct tsi721_dma_desc), 838c2ecf20Sopenharmony_ci &bd_phys, GFP_ATOMIC); 848c2ecf20Sopenharmony_ci if (!bd_ptr) 858c2ecf20Sopenharmony_ci return -ENOMEM; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci bdma_chan->bd_num = bd_num; 888c2ecf20Sopenharmony_ci bdma_chan->bd_phys = bd_phys; 898c2ecf20Sopenharmony_ci bdma_chan->bd_base = bd_ptr; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci tsi_debug(DMA, &bdma_chan->dchan.dev->device, 928c2ecf20Sopenharmony_ci "DMAC%d descriptors @ %p (phys = %pad)", 938c2ecf20Sopenharmony_ci bdma_chan->id, bd_ptr, &bd_phys); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* Allocate space for descriptor status FIFO */ 968c2ecf20Sopenharmony_ci sts_size = ((bd_num + 1) >= TSI721_DMA_MINSTSSZ) ? 978c2ecf20Sopenharmony_ci (bd_num + 1) : TSI721_DMA_MINSTSSZ; 988c2ecf20Sopenharmony_ci sts_size = roundup_pow_of_two(sts_size); 998c2ecf20Sopenharmony_ci sts_ptr = dma_alloc_coherent(dev, 1008c2ecf20Sopenharmony_ci sts_size * sizeof(struct tsi721_dma_sts), 1018c2ecf20Sopenharmony_ci &sts_phys, GFP_ATOMIC); 1028c2ecf20Sopenharmony_ci if (!sts_ptr) { 1038c2ecf20Sopenharmony_ci /* Free space allocated for DMA descriptors */ 1048c2ecf20Sopenharmony_ci dma_free_coherent(dev, 1058c2ecf20Sopenharmony_ci (bd_num + 1) * sizeof(struct tsi721_dma_desc), 1068c2ecf20Sopenharmony_ci bd_ptr, bd_phys); 1078c2ecf20Sopenharmony_ci bdma_chan->bd_base = NULL; 1088c2ecf20Sopenharmony_ci return -ENOMEM; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci bdma_chan->sts_phys = sts_phys; 1128c2ecf20Sopenharmony_ci bdma_chan->sts_base = sts_ptr; 1138c2ecf20Sopenharmony_ci bdma_chan->sts_size = sts_size; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci tsi_debug(DMA, &bdma_chan->dchan.dev->device, 1168c2ecf20Sopenharmony_ci "DMAC%d desc status FIFO @ %p (phys = %pad) size=0x%x", 1178c2ecf20Sopenharmony_ci bdma_chan->id, sts_ptr, &sts_phys, sts_size); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* Initialize DMA descriptors ring using added link descriptor */ 1208c2ecf20Sopenharmony_ci bd_ptr[bd_num].type_id = cpu_to_le32(DTYPE3 << 29); 1218c2ecf20Sopenharmony_ci bd_ptr[bd_num].next_lo = cpu_to_le32((u64)bd_phys & 1228c2ecf20Sopenharmony_ci TSI721_DMAC_DPTRL_MASK); 1238c2ecf20Sopenharmony_ci bd_ptr[bd_num].next_hi = cpu_to_le32((u64)bd_phys >> 32); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Setup DMA descriptor pointers */ 1268c2ecf20Sopenharmony_ci iowrite32(((u64)bd_phys >> 32), 1278c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_DPTRH); 1288c2ecf20Sopenharmony_ci iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK), 1298c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_DPTRL); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Setup descriptor status FIFO */ 1328c2ecf20Sopenharmony_ci iowrite32(((u64)sts_phys >> 32), 1338c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_DSBH); 1348c2ecf20Sopenharmony_ci iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK), 1358c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_DSBL); 1368c2ecf20Sopenharmony_ci iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size), 1378c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_DSSZ); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Clear interrupt bits */ 1408c2ecf20Sopenharmony_ci iowrite32(TSI721_DMAC_INT_ALL, 1418c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_INT); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci ioread32(bdma_chan->regs + TSI721_DMAC_INT); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI 1468c2ecf20Sopenharmony_ci /* Request interrupt service if we are in MSI-X mode */ 1478c2ecf20Sopenharmony_ci if (priv->flags & TSI721_USING_MSIX) { 1488c2ecf20Sopenharmony_ci int rc, idx; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci idx = TSI721_VECT_DMA0_DONE + bdma_chan->id; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci rc = request_irq(priv->msix[idx].vector, tsi721_bdma_msix, 0, 1538c2ecf20Sopenharmony_ci priv->msix[idx].irq_name, (void *)bdma_chan); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (rc) { 1568c2ecf20Sopenharmony_ci tsi_debug(DMA, &bdma_chan->dchan.dev->device, 1578c2ecf20Sopenharmony_ci "Unable to get MSI-X for DMAC%d-DONE", 1588c2ecf20Sopenharmony_ci bdma_chan->id); 1598c2ecf20Sopenharmony_ci goto err_out; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci idx = TSI721_VECT_DMA0_INT + bdma_chan->id; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci rc = request_irq(priv->msix[idx].vector, tsi721_bdma_msix, 0, 1658c2ecf20Sopenharmony_ci priv->msix[idx].irq_name, (void *)bdma_chan); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (rc) { 1688c2ecf20Sopenharmony_ci tsi_debug(DMA, &bdma_chan->dchan.dev->device, 1698c2ecf20Sopenharmony_ci "Unable to get MSI-X for DMAC%d-INT", 1708c2ecf20Sopenharmony_ci bdma_chan->id); 1718c2ecf20Sopenharmony_ci free_irq( 1728c2ecf20Sopenharmony_ci priv->msix[TSI721_VECT_DMA0_DONE + 1738c2ecf20Sopenharmony_ci bdma_chan->id].vector, 1748c2ecf20Sopenharmony_ci (void *)bdma_chan); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cierr_out: 1788c2ecf20Sopenharmony_ci if (rc) { 1798c2ecf20Sopenharmony_ci /* Free space allocated for DMA descriptors */ 1808c2ecf20Sopenharmony_ci dma_free_coherent(dev, 1818c2ecf20Sopenharmony_ci (bd_num + 1) * sizeof(struct tsi721_dma_desc), 1828c2ecf20Sopenharmony_ci bd_ptr, bd_phys); 1838c2ecf20Sopenharmony_ci bdma_chan->bd_base = NULL; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* Free space allocated for status descriptors */ 1868c2ecf20Sopenharmony_ci dma_free_coherent(dev, 1878c2ecf20Sopenharmony_ci sts_size * sizeof(struct tsi721_dma_sts), 1888c2ecf20Sopenharmony_ci sts_ptr, sts_phys); 1898c2ecf20Sopenharmony_ci bdma_chan->sts_base = NULL; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return -EIO; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI_MSI */ 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Toggle DMA channel initialization */ 1978c2ecf20Sopenharmony_ci iowrite32(TSI721_DMAC_CTL_INIT, bdma_chan->regs + TSI721_DMAC_CTL); 1988c2ecf20Sopenharmony_ci ioread32(bdma_chan->regs + TSI721_DMAC_CTL); 1998c2ecf20Sopenharmony_ci bdma_chan->wr_count = bdma_chan->wr_count_next = 0; 2008c2ecf20Sopenharmony_ci bdma_chan->sts_rdptr = 0; 2018c2ecf20Sopenharmony_ci udelay(10); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic int tsi721_bdma_ch_free(struct tsi721_bdma_chan *bdma_chan) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci u32 ch_stat; 2098c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI 2108c2ecf20Sopenharmony_ci struct tsi721_device *priv = to_tsi721(bdma_chan->dchan.device); 2118c2ecf20Sopenharmony_ci#endif 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (!bdma_chan->bd_base) 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* Check if DMA channel still running */ 2178c2ecf20Sopenharmony_ci ch_stat = ioread32(bdma_chan->regs + TSI721_DMAC_STS); 2188c2ecf20Sopenharmony_ci if (ch_stat & TSI721_DMAC_STS_RUN) 2198c2ecf20Sopenharmony_ci return -EFAULT; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Put DMA channel into init state */ 2228c2ecf20Sopenharmony_ci iowrite32(TSI721_DMAC_CTL_INIT, bdma_chan->regs + TSI721_DMAC_CTL); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI 2258c2ecf20Sopenharmony_ci if (priv->flags & TSI721_USING_MSIX) { 2268c2ecf20Sopenharmony_ci free_irq(priv->msix[TSI721_VECT_DMA0_DONE + 2278c2ecf20Sopenharmony_ci bdma_chan->id].vector, (void *)bdma_chan); 2288c2ecf20Sopenharmony_ci free_irq(priv->msix[TSI721_VECT_DMA0_INT + 2298c2ecf20Sopenharmony_ci bdma_chan->id].vector, (void *)bdma_chan); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI_MSI */ 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* Free space allocated for DMA descriptors */ 2348c2ecf20Sopenharmony_ci dma_free_coherent(bdma_chan->dchan.device->dev, 2358c2ecf20Sopenharmony_ci (bdma_chan->bd_num + 1) * sizeof(struct tsi721_dma_desc), 2368c2ecf20Sopenharmony_ci bdma_chan->bd_base, bdma_chan->bd_phys); 2378c2ecf20Sopenharmony_ci bdma_chan->bd_base = NULL; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* Free space allocated for status FIFO */ 2408c2ecf20Sopenharmony_ci dma_free_coherent(bdma_chan->dchan.device->dev, 2418c2ecf20Sopenharmony_ci bdma_chan->sts_size * sizeof(struct tsi721_dma_sts), 2428c2ecf20Sopenharmony_ci bdma_chan->sts_base, bdma_chan->sts_phys); 2438c2ecf20Sopenharmony_ci bdma_chan->sts_base = NULL; 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic void 2488c2ecf20Sopenharmony_citsi721_bdma_interrupt_enable(struct tsi721_bdma_chan *bdma_chan, int enable) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci if (enable) { 2518c2ecf20Sopenharmony_ci /* Clear pending BDMA channel interrupts */ 2528c2ecf20Sopenharmony_ci iowrite32(TSI721_DMAC_INT_ALL, 2538c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_INT); 2548c2ecf20Sopenharmony_ci ioread32(bdma_chan->regs + TSI721_DMAC_INT); 2558c2ecf20Sopenharmony_ci /* Enable BDMA channel interrupts */ 2568c2ecf20Sopenharmony_ci iowrite32(TSI721_DMAC_INT_ALL, 2578c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_INTE); 2588c2ecf20Sopenharmony_ci } else { 2598c2ecf20Sopenharmony_ci /* Disable BDMA channel interrupts */ 2608c2ecf20Sopenharmony_ci iowrite32(0, bdma_chan->regs + TSI721_DMAC_INTE); 2618c2ecf20Sopenharmony_ci /* Clear pending BDMA channel interrupts */ 2628c2ecf20Sopenharmony_ci iowrite32(TSI721_DMAC_INT_ALL, 2638c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_INT); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic bool tsi721_dma_is_idle(struct tsi721_bdma_chan *bdma_chan) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci u32 sts; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci sts = ioread32(bdma_chan->regs + TSI721_DMAC_STS); 2738c2ecf20Sopenharmony_ci return ((sts & TSI721_DMAC_STS_RUN) == 0); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_civoid tsi721_bdma_handler(struct tsi721_bdma_chan *bdma_chan) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci /* Disable BDMA channel interrupts */ 2798c2ecf20Sopenharmony_ci iowrite32(0, bdma_chan->regs + TSI721_DMAC_INTE); 2808c2ecf20Sopenharmony_ci if (bdma_chan->active) 2818c2ecf20Sopenharmony_ci tasklet_hi_schedule(&bdma_chan->tasklet); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI 2858c2ecf20Sopenharmony_ci/** 2868c2ecf20Sopenharmony_ci * tsi721_omsg_msix - MSI-X interrupt handler for BDMA channels 2878c2ecf20Sopenharmony_ci * @irq: Linux interrupt number 2888c2ecf20Sopenharmony_ci * @ptr: Pointer to interrupt-specific data (BDMA channel structure) 2898c2ecf20Sopenharmony_ci * 2908c2ecf20Sopenharmony_ci * Handles BDMA channel interrupts signaled using MSI-X. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_cistatic irqreturn_t tsi721_bdma_msix(int irq, void *ptr) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct tsi721_bdma_chan *bdma_chan = ptr; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (bdma_chan->active) 2978c2ecf20Sopenharmony_ci tasklet_hi_schedule(&bdma_chan->tasklet); 2988c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI_MSI */ 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/* Must be called with the spinlock held */ 3038c2ecf20Sopenharmony_cistatic void tsi721_start_dma(struct tsi721_bdma_chan *bdma_chan) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci if (!tsi721_dma_is_idle(bdma_chan)) { 3068c2ecf20Sopenharmony_ci tsi_err(&bdma_chan->dchan.dev->device, 3078c2ecf20Sopenharmony_ci "DMAC%d Attempt to start non-idle channel", 3088c2ecf20Sopenharmony_ci bdma_chan->id); 3098c2ecf20Sopenharmony_ci return; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (bdma_chan->wr_count == bdma_chan->wr_count_next) { 3138c2ecf20Sopenharmony_ci tsi_err(&bdma_chan->dchan.dev->device, 3148c2ecf20Sopenharmony_ci "DMAC%d Attempt to start DMA with no BDs ready %d", 3158c2ecf20Sopenharmony_ci bdma_chan->id, task_pid_nr(current)); 3168c2ecf20Sopenharmony_ci return; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci tsi_debug(DMA, &bdma_chan->dchan.dev->device, "DMAC%d (wrc=%d) %d", 3208c2ecf20Sopenharmony_ci bdma_chan->id, bdma_chan->wr_count_next, 3218c2ecf20Sopenharmony_ci task_pid_nr(current)); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci iowrite32(bdma_chan->wr_count_next, 3248c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_DWRCNT); 3258c2ecf20Sopenharmony_ci ioread32(bdma_chan->regs + TSI721_DMAC_DWRCNT); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci bdma_chan->wr_count = bdma_chan->wr_count_next; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int 3318c2ecf20Sopenharmony_citsi721_desc_fill_init(struct tsi721_tx_desc *desc, 3328c2ecf20Sopenharmony_ci struct tsi721_dma_desc *bd_ptr, 3338c2ecf20Sopenharmony_ci struct scatterlist *sg, u32 sys_size) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci u64 rio_addr; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (!bd_ptr) 3388c2ecf20Sopenharmony_ci return -EINVAL; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* Initialize DMA descriptor */ 3418c2ecf20Sopenharmony_ci bd_ptr->type_id = cpu_to_le32((DTYPE1 << 29) | 3428c2ecf20Sopenharmony_ci (desc->rtype << 19) | desc->destid); 3438c2ecf20Sopenharmony_ci bd_ptr->bcount = cpu_to_le32(((desc->rio_addr & 0x3) << 30) | 3448c2ecf20Sopenharmony_ci (sys_size << 26)); 3458c2ecf20Sopenharmony_ci rio_addr = (desc->rio_addr >> 2) | 3468c2ecf20Sopenharmony_ci ((u64)(desc->rio_addr_u & 0x3) << 62); 3478c2ecf20Sopenharmony_ci bd_ptr->raddr_lo = cpu_to_le32(rio_addr & 0xffffffff); 3488c2ecf20Sopenharmony_ci bd_ptr->raddr_hi = cpu_to_le32(rio_addr >> 32); 3498c2ecf20Sopenharmony_ci bd_ptr->t1.bufptr_lo = cpu_to_le32( 3508c2ecf20Sopenharmony_ci (u64)sg_dma_address(sg) & 0xffffffff); 3518c2ecf20Sopenharmony_ci bd_ptr->t1.bufptr_hi = cpu_to_le32((u64)sg_dma_address(sg) >> 32); 3528c2ecf20Sopenharmony_ci bd_ptr->t1.s_dist = 0; 3538c2ecf20Sopenharmony_ci bd_ptr->t1.s_size = 0; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int 3598c2ecf20Sopenharmony_citsi721_desc_fill_end(struct tsi721_dma_desc *bd_ptr, u32 bcount, bool interrupt) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci if (!bd_ptr) 3628c2ecf20Sopenharmony_ci return -EINVAL; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* Update DMA descriptor */ 3658c2ecf20Sopenharmony_ci if (interrupt) 3668c2ecf20Sopenharmony_ci bd_ptr->type_id |= cpu_to_le32(TSI721_DMAD_IOF); 3678c2ecf20Sopenharmony_ci bd_ptr->bcount |= cpu_to_le32(bcount & TSI721_DMAD_BCOUNT1); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic void tsi721_dma_tx_err(struct tsi721_bdma_chan *bdma_chan, 3738c2ecf20Sopenharmony_ci struct tsi721_tx_desc *desc) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *txd = &desc->txd; 3768c2ecf20Sopenharmony_ci dma_async_tx_callback callback = txd->callback; 3778c2ecf20Sopenharmony_ci void *param = txd->callback_param; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci list_move(&desc->desc_node, &bdma_chan->free_list); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (callback) 3828c2ecf20Sopenharmony_ci callback(param); 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic void tsi721_clr_stat(struct tsi721_bdma_chan *bdma_chan) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci u32 srd_ptr; 3888c2ecf20Sopenharmony_ci u64 *sts_ptr; 3898c2ecf20Sopenharmony_ci int i, j; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* Check and clear descriptor status FIFO entries */ 3928c2ecf20Sopenharmony_ci srd_ptr = bdma_chan->sts_rdptr; 3938c2ecf20Sopenharmony_ci sts_ptr = bdma_chan->sts_base; 3948c2ecf20Sopenharmony_ci j = srd_ptr * 8; 3958c2ecf20Sopenharmony_ci while (sts_ptr[j]) { 3968c2ecf20Sopenharmony_ci for (i = 0; i < 8 && sts_ptr[j]; i++, j++) 3978c2ecf20Sopenharmony_ci sts_ptr[j] = 0; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci ++srd_ptr; 4008c2ecf20Sopenharmony_ci srd_ptr %= bdma_chan->sts_size; 4018c2ecf20Sopenharmony_ci j = srd_ptr * 8; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci iowrite32(srd_ptr, bdma_chan->regs + TSI721_DMAC_DSRP); 4058c2ecf20Sopenharmony_ci bdma_chan->sts_rdptr = srd_ptr; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci/* Must be called with the channel spinlock held */ 4098c2ecf20Sopenharmony_cistatic int tsi721_submit_sg(struct tsi721_tx_desc *desc) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct dma_chan *dchan = desc->txd.chan; 4128c2ecf20Sopenharmony_ci struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); 4138c2ecf20Sopenharmony_ci u32 sys_size; 4148c2ecf20Sopenharmony_ci u64 rio_addr; 4158c2ecf20Sopenharmony_ci dma_addr_t next_addr; 4168c2ecf20Sopenharmony_ci u32 bcount; 4178c2ecf20Sopenharmony_ci struct scatterlist *sg; 4188c2ecf20Sopenharmony_ci unsigned int i; 4198c2ecf20Sopenharmony_ci int err = 0; 4208c2ecf20Sopenharmony_ci struct tsi721_dma_desc *bd_ptr = NULL; 4218c2ecf20Sopenharmony_ci u32 idx, rd_idx; 4228c2ecf20Sopenharmony_ci u32 add_count = 0; 4238c2ecf20Sopenharmony_ci struct device *ch_dev = &dchan->dev->device; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (!tsi721_dma_is_idle(bdma_chan)) { 4268c2ecf20Sopenharmony_ci tsi_err(ch_dev, "DMAC%d ERR: Attempt to use non-idle channel", 4278c2ecf20Sopenharmony_ci bdma_chan->id); 4288c2ecf20Sopenharmony_ci return -EIO; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* 4328c2ecf20Sopenharmony_ci * Fill DMA channel's hardware buffer descriptors. 4338c2ecf20Sopenharmony_ci * (NOTE: RapidIO destination address is limited to 64 bits for now) 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci rio_addr = desc->rio_addr; 4368c2ecf20Sopenharmony_ci next_addr = -1; 4378c2ecf20Sopenharmony_ci bcount = 0; 4388c2ecf20Sopenharmony_ci sys_size = dma_to_mport(dchan->device)->sys_size; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci rd_idx = ioread32(bdma_chan->regs + TSI721_DMAC_DRDCNT); 4418c2ecf20Sopenharmony_ci rd_idx %= (bdma_chan->bd_num + 1); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci idx = bdma_chan->wr_count_next % (bdma_chan->bd_num + 1); 4448c2ecf20Sopenharmony_ci if (idx == bdma_chan->bd_num) { 4458c2ecf20Sopenharmony_ci /* wrap around link descriptor */ 4468c2ecf20Sopenharmony_ci idx = 0; 4478c2ecf20Sopenharmony_ci add_count++; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci tsi_debug(DMA, ch_dev, "DMAC%d BD ring status: rdi=%d wri=%d", 4518c2ecf20Sopenharmony_ci bdma_chan->id, rd_idx, idx); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci for_each_sg(desc->sg, sg, desc->sg_len, i) { 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci tsi_debug(DMAV, ch_dev, "DMAC%d sg%d/%d addr: 0x%llx len: %d", 4568c2ecf20Sopenharmony_ci bdma_chan->id, i, desc->sg_len, 4578c2ecf20Sopenharmony_ci (unsigned long long)sg_dma_address(sg), sg_dma_len(sg)); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (sg_dma_len(sg) > TSI721_BDMA_MAX_BCOUNT) { 4608c2ecf20Sopenharmony_ci tsi_err(ch_dev, "DMAC%d SG entry %d is too large", 4618c2ecf20Sopenharmony_ci bdma_chan->id, i); 4628c2ecf20Sopenharmony_ci err = -EINVAL; 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* 4678c2ecf20Sopenharmony_ci * If this sg entry forms contiguous block with previous one, 4688c2ecf20Sopenharmony_ci * try to merge it into existing DMA descriptor 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci if (next_addr == sg_dma_address(sg) && 4718c2ecf20Sopenharmony_ci bcount + sg_dma_len(sg) <= TSI721_BDMA_MAX_BCOUNT) { 4728c2ecf20Sopenharmony_ci /* Adjust byte count of the descriptor */ 4738c2ecf20Sopenharmony_ci bcount += sg_dma_len(sg); 4748c2ecf20Sopenharmony_ci goto entry_done; 4758c2ecf20Sopenharmony_ci } else if (next_addr != -1) { 4768c2ecf20Sopenharmony_ci /* Finalize descriptor using total byte count value */ 4778c2ecf20Sopenharmony_ci tsi721_desc_fill_end(bd_ptr, bcount, 0); 4788c2ecf20Sopenharmony_ci tsi_debug(DMAV, ch_dev, "DMAC%d prev desc final len: %d", 4798c2ecf20Sopenharmony_ci bdma_chan->id, bcount); 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci desc->rio_addr = rio_addr; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (i && idx == rd_idx) { 4858c2ecf20Sopenharmony_ci tsi_debug(DMAV, ch_dev, 4868c2ecf20Sopenharmony_ci "DMAC%d HW descriptor ring is full @ %d", 4878c2ecf20Sopenharmony_ci bdma_chan->id, i); 4888c2ecf20Sopenharmony_ci desc->sg = sg; 4898c2ecf20Sopenharmony_ci desc->sg_len -= i; 4908c2ecf20Sopenharmony_ci break; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci bd_ptr = &((struct tsi721_dma_desc *)bdma_chan->bd_base)[idx]; 4948c2ecf20Sopenharmony_ci err = tsi721_desc_fill_init(desc, bd_ptr, sg, sys_size); 4958c2ecf20Sopenharmony_ci if (err) { 4968c2ecf20Sopenharmony_ci tsi_err(ch_dev, "Failed to build desc: err=%d", err); 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci tsi_debug(DMAV, ch_dev, "DMAC%d bd_ptr = %p did=%d raddr=0x%llx", 5018c2ecf20Sopenharmony_ci bdma_chan->id, bd_ptr, desc->destid, desc->rio_addr); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci next_addr = sg_dma_address(sg); 5048c2ecf20Sopenharmony_ci bcount = sg_dma_len(sg); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci add_count++; 5078c2ecf20Sopenharmony_ci if (++idx == bdma_chan->bd_num) { 5088c2ecf20Sopenharmony_ci /* wrap around link descriptor */ 5098c2ecf20Sopenharmony_ci idx = 0; 5108c2ecf20Sopenharmony_ci add_count++; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cientry_done: 5148c2ecf20Sopenharmony_ci if (sg_is_last(sg)) { 5158c2ecf20Sopenharmony_ci tsi721_desc_fill_end(bd_ptr, bcount, 0); 5168c2ecf20Sopenharmony_ci tsi_debug(DMAV, ch_dev, 5178c2ecf20Sopenharmony_ci "DMAC%d last desc final len: %d", 5188c2ecf20Sopenharmony_ci bdma_chan->id, bcount); 5198c2ecf20Sopenharmony_ci desc->sg_len = 0; 5208c2ecf20Sopenharmony_ci } else { 5218c2ecf20Sopenharmony_ci rio_addr += sg_dma_len(sg); 5228c2ecf20Sopenharmony_ci next_addr += sg_dma_len(sg); 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (!err) 5278c2ecf20Sopenharmony_ci bdma_chan->wr_count_next += add_count; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return err; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic void tsi721_advance_work(struct tsi721_bdma_chan *bdma_chan, 5338c2ecf20Sopenharmony_ci struct tsi721_tx_desc *desc) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci int err; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci tsi_debug(DMA, &bdma_chan->dchan.dev->device, "DMAC%d", bdma_chan->id); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (!tsi721_dma_is_idle(bdma_chan)) 5408c2ecf20Sopenharmony_ci return; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* 5438c2ecf20Sopenharmony_ci * If there is no data transfer in progress, fetch new descriptor from 5448c2ecf20Sopenharmony_ci * the pending queue. 5458c2ecf20Sopenharmony_ci */ 5468c2ecf20Sopenharmony_ci if (!desc && !bdma_chan->active_tx && !list_empty(&bdma_chan->queue)) { 5478c2ecf20Sopenharmony_ci desc = list_first_entry(&bdma_chan->queue, 5488c2ecf20Sopenharmony_ci struct tsi721_tx_desc, desc_node); 5498c2ecf20Sopenharmony_ci list_del_init((&desc->desc_node)); 5508c2ecf20Sopenharmony_ci bdma_chan->active_tx = desc; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (desc) { 5548c2ecf20Sopenharmony_ci err = tsi721_submit_sg(desc); 5558c2ecf20Sopenharmony_ci if (!err) 5568c2ecf20Sopenharmony_ci tsi721_start_dma(bdma_chan); 5578c2ecf20Sopenharmony_ci else { 5588c2ecf20Sopenharmony_ci tsi721_dma_tx_err(bdma_chan, desc); 5598c2ecf20Sopenharmony_ci tsi_debug(DMA, &bdma_chan->dchan.dev->device, 5608c2ecf20Sopenharmony_ci "DMAC%d ERR: tsi721_submit_sg failed with err=%d", 5618c2ecf20Sopenharmony_ci bdma_chan->id, err); 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci tsi_debug(DMA, &bdma_chan->dchan.dev->device, "DMAC%d Exit", 5668c2ecf20Sopenharmony_ci bdma_chan->id); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic void tsi721_dma_tasklet(unsigned long data) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci struct tsi721_bdma_chan *bdma_chan = (struct tsi721_bdma_chan *)data; 5728c2ecf20Sopenharmony_ci u32 dmac_int, dmac_sts; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci dmac_int = ioread32(bdma_chan->regs + TSI721_DMAC_INT); 5758c2ecf20Sopenharmony_ci tsi_debug(DMA, &bdma_chan->dchan.dev->device, "DMAC%d_INT = 0x%x", 5768c2ecf20Sopenharmony_ci bdma_chan->id, dmac_int); 5778c2ecf20Sopenharmony_ci /* Clear channel interrupts */ 5788c2ecf20Sopenharmony_ci iowrite32(dmac_int, bdma_chan->regs + TSI721_DMAC_INT); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if (dmac_int & TSI721_DMAC_INT_ERR) { 5818c2ecf20Sopenharmony_ci int i = 10000; 5828c2ecf20Sopenharmony_ci struct tsi721_tx_desc *desc; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci desc = bdma_chan->active_tx; 5858c2ecf20Sopenharmony_ci dmac_sts = ioread32(bdma_chan->regs + TSI721_DMAC_STS); 5868c2ecf20Sopenharmony_ci tsi_err(&bdma_chan->dchan.dev->device, 5878c2ecf20Sopenharmony_ci "DMAC%d_STS = 0x%x did=%d raddr=0x%llx", 5888c2ecf20Sopenharmony_ci bdma_chan->id, dmac_sts, desc->destid, desc->rio_addr); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* Re-initialize DMA channel if possible */ 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if ((dmac_sts & TSI721_DMAC_STS_ABORT) == 0) 5938c2ecf20Sopenharmony_ci goto err_out; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci tsi721_clr_stat(bdma_chan); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci spin_lock(&bdma_chan->lock); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* Put DMA channel into init state */ 6008c2ecf20Sopenharmony_ci iowrite32(TSI721_DMAC_CTL_INIT, 6018c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_CTL); 6028c2ecf20Sopenharmony_ci do { 6038c2ecf20Sopenharmony_ci udelay(1); 6048c2ecf20Sopenharmony_ci dmac_sts = ioread32(bdma_chan->regs + TSI721_DMAC_STS); 6058c2ecf20Sopenharmony_ci i--; 6068c2ecf20Sopenharmony_ci } while ((dmac_sts & TSI721_DMAC_STS_ABORT) && i); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (dmac_sts & TSI721_DMAC_STS_ABORT) { 6098c2ecf20Sopenharmony_ci tsi_err(&bdma_chan->dchan.dev->device, 6108c2ecf20Sopenharmony_ci "Failed to re-initiate DMAC%d", bdma_chan->id); 6118c2ecf20Sopenharmony_ci spin_unlock(&bdma_chan->lock); 6128c2ecf20Sopenharmony_ci goto err_out; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* Setup DMA descriptor pointers */ 6168c2ecf20Sopenharmony_ci iowrite32(((u64)bdma_chan->bd_phys >> 32), 6178c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_DPTRH); 6188c2ecf20Sopenharmony_ci iowrite32(((u64)bdma_chan->bd_phys & TSI721_DMAC_DPTRL_MASK), 6198c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_DPTRL); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci /* Setup descriptor status FIFO */ 6228c2ecf20Sopenharmony_ci iowrite32(((u64)bdma_chan->sts_phys >> 32), 6238c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_DSBH); 6248c2ecf20Sopenharmony_ci iowrite32(((u64)bdma_chan->sts_phys & TSI721_DMAC_DSBL_MASK), 6258c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_DSBL); 6268c2ecf20Sopenharmony_ci iowrite32(TSI721_DMAC_DSSZ_SIZE(bdma_chan->sts_size), 6278c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_DSSZ); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* Clear interrupt bits */ 6308c2ecf20Sopenharmony_ci iowrite32(TSI721_DMAC_INT_ALL, 6318c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_INT); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci ioread32(bdma_chan->regs + TSI721_DMAC_INT); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci bdma_chan->wr_count = bdma_chan->wr_count_next = 0; 6368c2ecf20Sopenharmony_ci bdma_chan->sts_rdptr = 0; 6378c2ecf20Sopenharmony_ci udelay(10); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci desc = bdma_chan->active_tx; 6408c2ecf20Sopenharmony_ci desc->status = DMA_ERROR; 6418c2ecf20Sopenharmony_ci dma_cookie_complete(&desc->txd); 6428c2ecf20Sopenharmony_ci list_add(&desc->desc_node, &bdma_chan->free_list); 6438c2ecf20Sopenharmony_ci bdma_chan->active_tx = NULL; 6448c2ecf20Sopenharmony_ci if (bdma_chan->active) 6458c2ecf20Sopenharmony_ci tsi721_advance_work(bdma_chan, NULL); 6468c2ecf20Sopenharmony_ci spin_unlock(&bdma_chan->lock); 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci if (dmac_int & TSI721_DMAC_INT_STFULL) { 6508c2ecf20Sopenharmony_ci tsi_err(&bdma_chan->dchan.dev->device, 6518c2ecf20Sopenharmony_ci "DMAC%d descriptor status FIFO is full", 6528c2ecf20Sopenharmony_ci bdma_chan->id); 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (dmac_int & (TSI721_DMAC_INT_DONE | TSI721_DMAC_INT_IOFDONE)) { 6568c2ecf20Sopenharmony_ci struct tsi721_tx_desc *desc; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci tsi721_clr_stat(bdma_chan); 6598c2ecf20Sopenharmony_ci spin_lock(&bdma_chan->lock); 6608c2ecf20Sopenharmony_ci desc = bdma_chan->active_tx; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (desc->sg_len == 0) { 6638c2ecf20Sopenharmony_ci dma_async_tx_callback callback = NULL; 6648c2ecf20Sopenharmony_ci void *param = NULL; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci desc->status = DMA_COMPLETE; 6678c2ecf20Sopenharmony_ci dma_cookie_complete(&desc->txd); 6688c2ecf20Sopenharmony_ci if (desc->txd.flags & DMA_PREP_INTERRUPT) { 6698c2ecf20Sopenharmony_ci callback = desc->txd.callback; 6708c2ecf20Sopenharmony_ci param = desc->txd.callback_param; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci list_add(&desc->desc_node, &bdma_chan->free_list); 6738c2ecf20Sopenharmony_ci bdma_chan->active_tx = NULL; 6748c2ecf20Sopenharmony_ci if (bdma_chan->active) 6758c2ecf20Sopenharmony_ci tsi721_advance_work(bdma_chan, NULL); 6768c2ecf20Sopenharmony_ci spin_unlock(&bdma_chan->lock); 6778c2ecf20Sopenharmony_ci if (callback) 6788c2ecf20Sopenharmony_ci callback(param); 6798c2ecf20Sopenharmony_ci } else { 6808c2ecf20Sopenharmony_ci if (bdma_chan->active) 6818c2ecf20Sopenharmony_ci tsi721_advance_work(bdma_chan, 6828c2ecf20Sopenharmony_ci bdma_chan->active_tx); 6838c2ecf20Sopenharmony_ci spin_unlock(&bdma_chan->lock); 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_cierr_out: 6878c2ecf20Sopenharmony_ci /* Re-Enable BDMA channel interrupts */ 6888c2ecf20Sopenharmony_ci iowrite32(TSI721_DMAC_INT_ALL, bdma_chan->regs + TSI721_DMAC_INTE); 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic dma_cookie_t tsi721_tx_submit(struct dma_async_tx_descriptor *txd) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci struct tsi721_tx_desc *desc = to_tsi721_desc(txd); 6948c2ecf20Sopenharmony_ci struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(txd->chan); 6958c2ecf20Sopenharmony_ci dma_cookie_t cookie; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* Check if the descriptor is detached from any lists */ 6988c2ecf20Sopenharmony_ci if (!list_empty(&desc->desc_node)) { 6998c2ecf20Sopenharmony_ci tsi_err(&bdma_chan->dchan.dev->device, 7008c2ecf20Sopenharmony_ci "DMAC%d wrong state of descriptor %p", 7018c2ecf20Sopenharmony_ci bdma_chan->id, txd); 7028c2ecf20Sopenharmony_ci return -EIO; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci spin_lock_bh(&bdma_chan->lock); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (!bdma_chan->active) { 7088c2ecf20Sopenharmony_ci spin_unlock_bh(&bdma_chan->lock); 7098c2ecf20Sopenharmony_ci return -ENODEV; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci cookie = dma_cookie_assign(txd); 7138c2ecf20Sopenharmony_ci desc->status = DMA_IN_PROGRESS; 7148c2ecf20Sopenharmony_ci list_add_tail(&desc->desc_node, &bdma_chan->queue); 7158c2ecf20Sopenharmony_ci tsi721_advance_work(bdma_chan, NULL); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci spin_unlock_bh(&bdma_chan->lock); 7188c2ecf20Sopenharmony_ci return cookie; 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic int tsi721_alloc_chan_resources(struct dma_chan *dchan) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); 7248c2ecf20Sopenharmony_ci struct tsi721_tx_desc *desc; 7258c2ecf20Sopenharmony_ci int i; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci tsi_debug(DMA, &dchan->dev->device, "DMAC%d", bdma_chan->id); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (bdma_chan->bd_base) 7308c2ecf20Sopenharmony_ci return dma_txqueue_sz; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* Initialize BDMA channel */ 7338c2ecf20Sopenharmony_ci if (tsi721_bdma_ch_init(bdma_chan, dma_desc_per_channel)) { 7348c2ecf20Sopenharmony_ci tsi_err(&dchan->dev->device, "Unable to initialize DMAC%d", 7358c2ecf20Sopenharmony_ci bdma_chan->id); 7368c2ecf20Sopenharmony_ci return -ENODEV; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* Allocate queue of transaction descriptors */ 7408c2ecf20Sopenharmony_ci desc = kcalloc(dma_txqueue_sz, sizeof(struct tsi721_tx_desc), 7418c2ecf20Sopenharmony_ci GFP_ATOMIC); 7428c2ecf20Sopenharmony_ci if (!desc) { 7438c2ecf20Sopenharmony_ci tsi721_bdma_ch_free(bdma_chan); 7448c2ecf20Sopenharmony_ci return -ENOMEM; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci bdma_chan->tx_desc = desc; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci for (i = 0; i < dma_txqueue_sz; i++) { 7508c2ecf20Sopenharmony_ci dma_async_tx_descriptor_init(&desc[i].txd, dchan); 7518c2ecf20Sopenharmony_ci desc[i].txd.tx_submit = tsi721_tx_submit; 7528c2ecf20Sopenharmony_ci desc[i].txd.flags = DMA_CTRL_ACK; 7538c2ecf20Sopenharmony_ci list_add(&desc[i].desc_node, &bdma_chan->free_list); 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci dma_cookie_init(dchan); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci bdma_chan->active = true; 7598c2ecf20Sopenharmony_ci tsi721_bdma_interrupt_enable(bdma_chan, 1); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci return dma_txqueue_sz; 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic void tsi721_sync_dma_irq(struct tsi721_bdma_chan *bdma_chan) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci struct tsi721_device *priv = to_tsi721(bdma_chan->dchan.device); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI 7698c2ecf20Sopenharmony_ci if (priv->flags & TSI721_USING_MSIX) { 7708c2ecf20Sopenharmony_ci synchronize_irq(priv->msix[TSI721_VECT_DMA0_DONE + 7718c2ecf20Sopenharmony_ci bdma_chan->id].vector); 7728c2ecf20Sopenharmony_ci synchronize_irq(priv->msix[TSI721_VECT_DMA0_INT + 7738c2ecf20Sopenharmony_ci bdma_chan->id].vector); 7748c2ecf20Sopenharmony_ci } else 7758c2ecf20Sopenharmony_ci#endif 7768c2ecf20Sopenharmony_ci synchronize_irq(priv->pdev->irq); 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic void tsi721_free_chan_resources(struct dma_chan *dchan) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci tsi_debug(DMA, &dchan->dev->device, "DMAC%d", bdma_chan->id); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (!bdma_chan->bd_base) 7868c2ecf20Sopenharmony_ci return; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci tsi721_bdma_interrupt_enable(bdma_chan, 0); 7898c2ecf20Sopenharmony_ci bdma_chan->active = false; 7908c2ecf20Sopenharmony_ci tsi721_sync_dma_irq(bdma_chan); 7918c2ecf20Sopenharmony_ci tasklet_kill(&bdma_chan->tasklet); 7928c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bdma_chan->free_list); 7938c2ecf20Sopenharmony_ci kfree(bdma_chan->tx_desc); 7948c2ecf20Sopenharmony_ci tsi721_bdma_ch_free(bdma_chan); 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic 7988c2ecf20Sopenharmony_cienum dma_status tsi721_tx_status(struct dma_chan *dchan, dma_cookie_t cookie, 7998c2ecf20Sopenharmony_ci struct dma_tx_state *txstate) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); 8028c2ecf20Sopenharmony_ci enum dma_status status; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci spin_lock_bh(&bdma_chan->lock); 8058c2ecf20Sopenharmony_ci status = dma_cookie_status(dchan, cookie, txstate); 8068c2ecf20Sopenharmony_ci spin_unlock_bh(&bdma_chan->lock); 8078c2ecf20Sopenharmony_ci return status; 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic void tsi721_issue_pending(struct dma_chan *dchan) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci tsi_debug(DMA, &dchan->dev->device, "DMAC%d", bdma_chan->id); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci spin_lock_bh(&bdma_chan->lock); 8178c2ecf20Sopenharmony_ci if (tsi721_dma_is_idle(bdma_chan) && bdma_chan->active) { 8188c2ecf20Sopenharmony_ci tsi721_advance_work(bdma_chan, NULL); 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci spin_unlock_bh(&bdma_chan->lock); 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic 8248c2ecf20Sopenharmony_cistruct dma_async_tx_descriptor *tsi721_prep_rio_sg(struct dma_chan *dchan, 8258c2ecf20Sopenharmony_ci struct scatterlist *sgl, unsigned int sg_len, 8268c2ecf20Sopenharmony_ci enum dma_transfer_direction dir, unsigned long flags, 8278c2ecf20Sopenharmony_ci void *tinfo) 8288c2ecf20Sopenharmony_ci{ 8298c2ecf20Sopenharmony_ci struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); 8308c2ecf20Sopenharmony_ci struct tsi721_tx_desc *desc; 8318c2ecf20Sopenharmony_ci struct rio_dma_ext *rext = tinfo; 8328c2ecf20Sopenharmony_ci enum dma_rtype rtype; 8338c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *txd = NULL; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (!sgl || !sg_len) { 8368c2ecf20Sopenharmony_ci tsi_err(&dchan->dev->device, "DMAC%d No SG list", 8378c2ecf20Sopenharmony_ci bdma_chan->id); 8388c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci tsi_debug(DMA, &dchan->dev->device, "DMAC%d %s", bdma_chan->id, 8428c2ecf20Sopenharmony_ci (dir == DMA_DEV_TO_MEM)?"READ":"WRITE"); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) 8458c2ecf20Sopenharmony_ci rtype = NREAD; 8468c2ecf20Sopenharmony_ci else if (dir == DMA_MEM_TO_DEV) { 8478c2ecf20Sopenharmony_ci switch (rext->wr_type) { 8488c2ecf20Sopenharmony_ci case RDW_ALL_NWRITE: 8498c2ecf20Sopenharmony_ci rtype = ALL_NWRITE; 8508c2ecf20Sopenharmony_ci break; 8518c2ecf20Sopenharmony_ci case RDW_ALL_NWRITE_R: 8528c2ecf20Sopenharmony_ci rtype = ALL_NWRITE_R; 8538c2ecf20Sopenharmony_ci break; 8548c2ecf20Sopenharmony_ci case RDW_LAST_NWRITE_R: 8558c2ecf20Sopenharmony_ci default: 8568c2ecf20Sopenharmony_ci rtype = LAST_NWRITE_R; 8578c2ecf20Sopenharmony_ci break; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci } else { 8608c2ecf20Sopenharmony_ci tsi_err(&dchan->dev->device, 8618c2ecf20Sopenharmony_ci "DMAC%d Unsupported DMA direction option", 8628c2ecf20Sopenharmony_ci bdma_chan->id); 8638c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci spin_lock_bh(&bdma_chan->lock); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (!list_empty(&bdma_chan->free_list)) { 8698c2ecf20Sopenharmony_ci desc = list_first_entry(&bdma_chan->free_list, 8708c2ecf20Sopenharmony_ci struct tsi721_tx_desc, desc_node); 8718c2ecf20Sopenharmony_ci list_del_init(&desc->desc_node); 8728c2ecf20Sopenharmony_ci desc->destid = rext->destid; 8738c2ecf20Sopenharmony_ci desc->rio_addr = rext->rio_addr; 8748c2ecf20Sopenharmony_ci desc->rio_addr_u = 0; 8758c2ecf20Sopenharmony_ci desc->rtype = rtype; 8768c2ecf20Sopenharmony_ci desc->sg_len = sg_len; 8778c2ecf20Sopenharmony_ci desc->sg = sgl; 8788c2ecf20Sopenharmony_ci txd = &desc->txd; 8798c2ecf20Sopenharmony_ci txd->flags = flags; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci spin_unlock_bh(&bdma_chan->lock); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci if (!txd) { 8858c2ecf20Sopenharmony_ci tsi_debug(DMA, &dchan->dev->device, 8868c2ecf20Sopenharmony_ci "DMAC%d free TXD is not available", bdma_chan->id); 8878c2ecf20Sopenharmony_ci return ERR_PTR(-EBUSY); 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci return txd; 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic int tsi721_terminate_all(struct dma_chan *dchan) 8948c2ecf20Sopenharmony_ci{ 8958c2ecf20Sopenharmony_ci struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); 8968c2ecf20Sopenharmony_ci struct tsi721_tx_desc *desc, *_d; 8978c2ecf20Sopenharmony_ci LIST_HEAD(list); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci tsi_debug(DMA, &dchan->dev->device, "DMAC%d", bdma_chan->id); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci spin_lock_bh(&bdma_chan->lock); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci bdma_chan->active = false; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci while (!tsi721_dma_is_idle(bdma_chan)) { 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci udelay(5); 9088c2ecf20Sopenharmony_ci#if (0) 9098c2ecf20Sopenharmony_ci /* make sure to stop the transfer */ 9108c2ecf20Sopenharmony_ci iowrite32(TSI721_DMAC_CTL_SUSP, 9118c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_CTL); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci /* Wait until DMA channel stops */ 9148c2ecf20Sopenharmony_ci do { 9158c2ecf20Sopenharmony_ci dmac_int = ioread32(bdma_chan->regs + TSI721_DMAC_INT); 9168c2ecf20Sopenharmony_ci } while ((dmac_int & TSI721_DMAC_INT_SUSP) == 0); 9178c2ecf20Sopenharmony_ci#endif 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci if (bdma_chan->active_tx) 9218c2ecf20Sopenharmony_ci list_add(&bdma_chan->active_tx->desc_node, &list); 9228c2ecf20Sopenharmony_ci list_splice_init(&bdma_chan->queue, &list); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci list_for_each_entry_safe(desc, _d, &list, desc_node) 9258c2ecf20Sopenharmony_ci tsi721_dma_tx_err(bdma_chan, desc); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci spin_unlock_bh(&bdma_chan->lock); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci return 0; 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic void tsi721_dma_stop(struct tsi721_bdma_chan *bdma_chan) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci if (!bdma_chan->active) 9358c2ecf20Sopenharmony_ci return; 9368c2ecf20Sopenharmony_ci spin_lock_bh(&bdma_chan->lock); 9378c2ecf20Sopenharmony_ci if (!tsi721_dma_is_idle(bdma_chan)) { 9388c2ecf20Sopenharmony_ci int timeout = 100000; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci /* stop the transfer in progress */ 9418c2ecf20Sopenharmony_ci iowrite32(TSI721_DMAC_CTL_SUSP, 9428c2ecf20Sopenharmony_ci bdma_chan->regs + TSI721_DMAC_CTL); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* Wait until DMA channel stops */ 9458c2ecf20Sopenharmony_ci while (!tsi721_dma_is_idle(bdma_chan) && --timeout) 9468c2ecf20Sopenharmony_ci udelay(1); 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci spin_unlock_bh(&bdma_chan->lock); 9508c2ecf20Sopenharmony_ci} 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_civoid tsi721_dma_stop_all(struct tsi721_device *priv) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci int i; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci for (i = 0; i < TSI721_DMA_MAXCH; i++) { 9578c2ecf20Sopenharmony_ci if ((i != TSI721_DMACH_MAINT) && (dma_sel & (1 << i))) 9588c2ecf20Sopenharmony_ci tsi721_dma_stop(&priv->bdma[i]); 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ciint tsi721_register_dma(struct tsi721_device *priv) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci int i; 9658c2ecf20Sopenharmony_ci int nr_channels = 0; 9668c2ecf20Sopenharmony_ci int err; 9678c2ecf20Sopenharmony_ci struct rio_mport *mport = &priv->mport; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mport->dma.channels); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci for (i = 0; i < TSI721_DMA_MAXCH; i++) { 9728c2ecf20Sopenharmony_ci struct tsi721_bdma_chan *bdma_chan = &priv->bdma[i]; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci if ((i == TSI721_DMACH_MAINT) || (dma_sel & (1 << i)) == 0) 9758c2ecf20Sopenharmony_ci continue; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci bdma_chan->regs = priv->regs + TSI721_DMAC_BASE(i); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci bdma_chan->dchan.device = &mport->dma; 9808c2ecf20Sopenharmony_ci bdma_chan->dchan.cookie = 1; 9818c2ecf20Sopenharmony_ci bdma_chan->dchan.chan_id = i; 9828c2ecf20Sopenharmony_ci bdma_chan->id = i; 9838c2ecf20Sopenharmony_ci bdma_chan->active = false; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci spin_lock_init(&bdma_chan->lock); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci bdma_chan->active_tx = NULL; 9888c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bdma_chan->queue); 9898c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bdma_chan->free_list); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci tasklet_init(&bdma_chan->tasklet, tsi721_dma_tasklet, 9928c2ecf20Sopenharmony_ci (unsigned long)bdma_chan); 9938c2ecf20Sopenharmony_ci list_add_tail(&bdma_chan->dchan.device_node, 9948c2ecf20Sopenharmony_ci &mport->dma.channels); 9958c2ecf20Sopenharmony_ci nr_channels++; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci mport->dma.chancnt = nr_channels; 9998c2ecf20Sopenharmony_ci dma_cap_zero(mport->dma.cap_mask); 10008c2ecf20Sopenharmony_ci dma_cap_set(DMA_PRIVATE, mport->dma.cap_mask); 10018c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, mport->dma.cap_mask); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci mport->dma.dev = &priv->pdev->dev; 10048c2ecf20Sopenharmony_ci mport->dma.device_alloc_chan_resources = tsi721_alloc_chan_resources; 10058c2ecf20Sopenharmony_ci mport->dma.device_free_chan_resources = tsi721_free_chan_resources; 10068c2ecf20Sopenharmony_ci mport->dma.device_tx_status = tsi721_tx_status; 10078c2ecf20Sopenharmony_ci mport->dma.device_issue_pending = tsi721_issue_pending; 10088c2ecf20Sopenharmony_ci mport->dma.device_prep_slave_sg = tsi721_prep_rio_sg; 10098c2ecf20Sopenharmony_ci mport->dma.device_terminate_all = tsi721_terminate_all; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci err = dma_async_device_register(&mport->dma); 10128c2ecf20Sopenharmony_ci if (err) 10138c2ecf20Sopenharmony_ci tsi_err(&priv->pdev->dev, "Failed to register DMA device"); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci return err; 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_civoid tsi721_unregister_dma(struct tsi721_device *priv) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci struct rio_mport *mport = &priv->mport; 10218c2ecf20Sopenharmony_ci struct dma_chan *chan, *_c; 10228c2ecf20Sopenharmony_ci struct tsi721_bdma_chan *bdma_chan; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci tsi721_dma_stop_all(priv); 10258c2ecf20Sopenharmony_ci dma_async_device_unregister(&mport->dma); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci list_for_each_entry_safe(chan, _c, &mport->dma.channels, 10288c2ecf20Sopenharmony_ci device_node) { 10298c2ecf20Sopenharmony_ci bdma_chan = to_tsi721_chan(chan); 10308c2ecf20Sopenharmony_ci if (bdma_chan->active) { 10318c2ecf20Sopenharmony_ci tsi721_bdma_interrupt_enable(bdma_chan, 0); 10328c2ecf20Sopenharmony_ci bdma_chan->active = false; 10338c2ecf20Sopenharmony_ci tsi721_sync_dma_irq(bdma_chan); 10348c2ecf20Sopenharmony_ci tasklet_kill(&bdma_chan->tasklet); 10358c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bdma_chan->free_list); 10368c2ecf20Sopenharmony_ci kfree(bdma_chan->tx_desc); 10378c2ecf20Sopenharmony_ci tsi721_bdma_ch_free(bdma_chan); 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci list_del(&chan->device_node); 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci} 1043