18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// (C) 2017-2018 Synopsys, Inc. (www.synopsys.com) 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* 58c2ecf20Sopenharmony_ci * Synopsys DesignWare AXI DMA Controller driver. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/bitops.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 148c2ecf20Sopenharmony_ci#include <linux/dmapool.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 238c2ecf20Sopenharmony_ci#include <linux/property.h> 248c2ecf20Sopenharmony_ci#include <linux/types.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "dw-axi-dmac.h" 278c2ecf20Sopenharmony_ci#include "../dmaengine.h" 288c2ecf20Sopenharmony_ci#include "../virt-dma.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * The set of bus widths supported by the DMA controller. DW AXI DMAC supports 328c2ecf20Sopenharmony_ci * master data bus width up to 512 bits (for both AXI master interfaces), but 338c2ecf20Sopenharmony_ci * it depends on IP block configurarion. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci#define AXI_DMA_BUSWIDTHS \ 368c2ecf20Sopenharmony_ci (DMA_SLAVE_BUSWIDTH_1_BYTE | \ 378c2ecf20Sopenharmony_ci DMA_SLAVE_BUSWIDTH_2_BYTES | \ 388c2ecf20Sopenharmony_ci DMA_SLAVE_BUSWIDTH_4_BYTES | \ 398c2ecf20Sopenharmony_ci DMA_SLAVE_BUSWIDTH_8_BYTES | \ 408c2ecf20Sopenharmony_ci DMA_SLAVE_BUSWIDTH_16_BYTES | \ 418c2ecf20Sopenharmony_ci DMA_SLAVE_BUSWIDTH_32_BYTES | \ 428c2ecf20Sopenharmony_ci DMA_SLAVE_BUSWIDTH_64_BYTES) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic inline void 458c2ecf20Sopenharmony_ciaxi_dma_iowrite32(struct axi_dma_chip *chip, u32 reg, u32 val) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci iowrite32(val, chip->regs + reg); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic inline u32 axi_dma_ioread32(struct axi_dma_chip *chip, u32 reg) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci return ioread32(chip->regs + reg); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic inline void 568c2ecf20Sopenharmony_ciaxi_chan_iowrite32(struct axi_dma_chan *chan, u32 reg, u32 val) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci iowrite32(val, chan->chan_regs + reg); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic inline u32 axi_chan_ioread32(struct axi_dma_chan *chan, u32 reg) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return ioread32(chan->chan_regs + reg); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic inline void 678c2ecf20Sopenharmony_ciaxi_chan_iowrite64(struct axi_dma_chan *chan, u32 reg, u64 val) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci /* 708c2ecf20Sopenharmony_ci * We split one 64 bit write for two 32 bit write as some HW doesn't 718c2ecf20Sopenharmony_ci * support 64 bit access. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci iowrite32(lower_32_bits(val), chan->chan_regs + reg); 748c2ecf20Sopenharmony_ci iowrite32(upper_32_bits(val), chan->chan_regs + reg + 4); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic inline void axi_dma_disable(struct axi_dma_chip *chip) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci u32 val; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci val = axi_dma_ioread32(chip, DMAC_CFG); 828c2ecf20Sopenharmony_ci val &= ~DMAC_EN_MASK; 838c2ecf20Sopenharmony_ci axi_dma_iowrite32(chip, DMAC_CFG, val); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic inline void axi_dma_enable(struct axi_dma_chip *chip) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci u32 val; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci val = axi_dma_ioread32(chip, DMAC_CFG); 918c2ecf20Sopenharmony_ci val |= DMAC_EN_MASK; 928c2ecf20Sopenharmony_ci axi_dma_iowrite32(chip, DMAC_CFG, val); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic inline void axi_dma_irq_disable(struct axi_dma_chip *chip) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci u32 val; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci val = axi_dma_ioread32(chip, DMAC_CFG); 1008c2ecf20Sopenharmony_ci val &= ~INT_EN_MASK; 1018c2ecf20Sopenharmony_ci axi_dma_iowrite32(chip, DMAC_CFG, val); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic inline void axi_dma_irq_enable(struct axi_dma_chip *chip) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci u32 val; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci val = axi_dma_ioread32(chip, DMAC_CFG); 1098c2ecf20Sopenharmony_ci val |= INT_EN_MASK; 1108c2ecf20Sopenharmony_ci axi_dma_iowrite32(chip, DMAC_CFG, val); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic inline void axi_chan_irq_disable(struct axi_dma_chan *chan, u32 irq_mask) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci u32 val; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (likely(irq_mask == DWAXIDMAC_IRQ_ALL)) { 1188c2ecf20Sopenharmony_ci axi_chan_iowrite32(chan, CH_INTSTATUS_ENA, DWAXIDMAC_IRQ_NONE); 1198c2ecf20Sopenharmony_ci } else { 1208c2ecf20Sopenharmony_ci val = axi_chan_ioread32(chan, CH_INTSTATUS_ENA); 1218c2ecf20Sopenharmony_ci val &= ~irq_mask; 1228c2ecf20Sopenharmony_ci axi_chan_iowrite32(chan, CH_INTSTATUS_ENA, val); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic inline void axi_chan_irq_set(struct axi_dma_chan *chan, u32 irq_mask) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci axi_chan_iowrite32(chan, CH_INTSTATUS_ENA, irq_mask); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic inline void axi_chan_irq_sig_set(struct axi_dma_chan *chan, u32 irq_mask) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci axi_chan_iowrite32(chan, CH_INTSIGNAL_ENA, irq_mask); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic inline void axi_chan_irq_clear(struct axi_dma_chan *chan, u32 irq_mask) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci axi_chan_iowrite32(chan, CH_INTCLEAR, irq_mask); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic inline u32 axi_chan_irq_read(struct axi_dma_chan *chan) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci return axi_chan_ioread32(chan, CH_INTSTATUS); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic inline void axi_chan_disable(struct axi_dma_chan *chan) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci u32 val; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci val = axi_dma_ioread32(chan->chip, DMAC_CHEN); 1518c2ecf20Sopenharmony_ci val &= ~(BIT(chan->id) << DMAC_CHAN_EN_SHIFT); 1528c2ecf20Sopenharmony_ci val |= BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT; 1538c2ecf20Sopenharmony_ci axi_dma_iowrite32(chan->chip, DMAC_CHEN, val); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic inline void axi_chan_enable(struct axi_dma_chan *chan) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci u32 val; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci val = axi_dma_ioread32(chan->chip, DMAC_CHEN); 1618c2ecf20Sopenharmony_ci val |= BIT(chan->id) << DMAC_CHAN_EN_SHIFT | 1628c2ecf20Sopenharmony_ci BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT; 1638c2ecf20Sopenharmony_ci axi_dma_iowrite32(chan->chip, DMAC_CHEN, val); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic inline bool axi_chan_is_hw_enable(struct axi_dma_chan *chan) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci u32 val; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci val = axi_dma_ioread32(chan->chip, DMAC_CHEN); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return !!(val & (BIT(chan->id) << DMAC_CHAN_EN_SHIFT)); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void axi_dma_hw_init(struct axi_dma_chip *chip) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci u32 i; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci for (i = 0; i < chip->dw->hdata->nr_channels; i++) { 1808c2ecf20Sopenharmony_ci axi_chan_irq_disable(&chip->dw->chan[i], DWAXIDMAC_IRQ_ALL); 1818c2ecf20Sopenharmony_ci axi_chan_disable(&chip->dw->chan[i]); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic u32 axi_chan_get_xfer_width(struct axi_dma_chan *chan, dma_addr_t src, 1868c2ecf20Sopenharmony_ci dma_addr_t dst, size_t len) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci u32 max_width = chan->chip->dw->hdata->m_data_width; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return __ffs(src | dst | len | BIT(max_width)); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic inline const char *axi_chan_name(struct axi_dma_chan *chan) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci return dma_chan_name(&chan->vc.chan); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic struct axi_dma_desc *axi_desc_get(struct axi_dma_chan *chan) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct dw_axi_dma *dw = chan->chip->dw; 2018c2ecf20Sopenharmony_ci struct axi_dma_desc *desc; 2028c2ecf20Sopenharmony_ci dma_addr_t phys; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci desc = dma_pool_zalloc(dw->desc_pool, GFP_NOWAIT, &phys); 2058c2ecf20Sopenharmony_ci if (unlikely(!desc)) { 2068c2ecf20Sopenharmony_ci dev_err(chan2dev(chan), "%s: not enough descriptors available\n", 2078c2ecf20Sopenharmony_ci axi_chan_name(chan)); 2088c2ecf20Sopenharmony_ci return NULL; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci atomic_inc(&chan->descs_allocated); 2128c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&desc->xfer_list); 2138c2ecf20Sopenharmony_ci desc->vd.tx.phys = phys; 2148c2ecf20Sopenharmony_ci desc->chan = chan; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return desc; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void axi_desc_put(struct axi_dma_desc *desc) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct axi_dma_chan *chan = desc->chan; 2228c2ecf20Sopenharmony_ci struct dw_axi_dma *dw = chan->chip->dw; 2238c2ecf20Sopenharmony_ci struct axi_dma_desc *child, *_next; 2248c2ecf20Sopenharmony_ci unsigned int descs_put = 0; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci list_for_each_entry_safe(child, _next, &desc->xfer_list, xfer_list) { 2278c2ecf20Sopenharmony_ci list_del(&child->xfer_list); 2288c2ecf20Sopenharmony_ci dma_pool_free(dw->desc_pool, child, child->vd.tx.phys); 2298c2ecf20Sopenharmony_ci descs_put++; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci dma_pool_free(dw->desc_pool, desc, desc->vd.tx.phys); 2338c2ecf20Sopenharmony_ci descs_put++; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci atomic_sub(descs_put, &chan->descs_allocated); 2368c2ecf20Sopenharmony_ci dev_vdbg(chan2dev(chan), "%s: %d descs put, %d still allocated\n", 2378c2ecf20Sopenharmony_ci axi_chan_name(chan), descs_put, 2388c2ecf20Sopenharmony_ci atomic_read(&chan->descs_allocated)); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void vchan_desc_put(struct virt_dma_desc *vdesc) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci axi_desc_put(vd_to_axi_desc(vdesc)); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic enum dma_status 2478c2ecf20Sopenharmony_cidma_chan_tx_status(struct dma_chan *dchan, dma_cookie_t cookie, 2488c2ecf20Sopenharmony_ci struct dma_tx_state *txstate) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 2518c2ecf20Sopenharmony_ci enum dma_status ret; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci ret = dma_cookie_status(dchan, cookie, txstate); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (chan->is_paused && ret == DMA_IN_PROGRESS) 2568c2ecf20Sopenharmony_ci ret = DMA_PAUSED; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return ret; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic void write_desc_llp(struct axi_dma_desc *desc, dma_addr_t adr) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci desc->lli.llp = cpu_to_le64(adr); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic void write_chan_llp(struct axi_dma_chan *chan, dma_addr_t adr) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci axi_chan_iowrite64(chan, CH_LLP, adr); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/* Called in chan locked context */ 2728c2ecf20Sopenharmony_cistatic void axi_chan_block_xfer_start(struct axi_dma_chan *chan, 2738c2ecf20Sopenharmony_ci struct axi_dma_desc *first) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci u32 priority = chan->chip->dw->hdata->priority[chan->id]; 2768c2ecf20Sopenharmony_ci u32 reg, irq_mask; 2778c2ecf20Sopenharmony_ci u8 lms = 0; /* Select AXI0 master for LLI fetching */ 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (unlikely(axi_chan_is_hw_enable(chan))) { 2808c2ecf20Sopenharmony_ci dev_err(chan2dev(chan), "%s is non-idle!\n", 2818c2ecf20Sopenharmony_ci axi_chan_name(chan)); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci axi_dma_enable(chan->chip); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci reg = (DWAXIDMAC_MBLK_TYPE_LL << CH_CFG_L_DST_MULTBLK_TYPE_POS | 2898c2ecf20Sopenharmony_ci DWAXIDMAC_MBLK_TYPE_LL << CH_CFG_L_SRC_MULTBLK_TYPE_POS); 2908c2ecf20Sopenharmony_ci axi_chan_iowrite32(chan, CH_CFG_L, reg); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci reg = (DWAXIDMAC_TT_FC_MEM_TO_MEM_DMAC << CH_CFG_H_TT_FC_POS | 2938c2ecf20Sopenharmony_ci priority << CH_CFG_H_PRIORITY_POS | 2948c2ecf20Sopenharmony_ci DWAXIDMAC_HS_SEL_HW << CH_CFG_H_HS_SEL_DST_POS | 2958c2ecf20Sopenharmony_ci DWAXIDMAC_HS_SEL_HW << CH_CFG_H_HS_SEL_SRC_POS); 2968c2ecf20Sopenharmony_ci axi_chan_iowrite32(chan, CH_CFG_H, reg); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci write_chan_llp(chan, first->vd.tx.phys | lms); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci irq_mask = DWAXIDMAC_IRQ_DMA_TRF | DWAXIDMAC_IRQ_ALL_ERR; 3018c2ecf20Sopenharmony_ci axi_chan_irq_sig_set(chan, irq_mask); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* Generate 'suspend' status but don't generate interrupt */ 3048c2ecf20Sopenharmony_ci irq_mask |= DWAXIDMAC_IRQ_SUSPENDED; 3058c2ecf20Sopenharmony_ci axi_chan_irq_set(chan, irq_mask); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci axi_chan_enable(chan); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic void axi_chan_start_first_queued(struct axi_dma_chan *chan) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct axi_dma_desc *desc; 3138c2ecf20Sopenharmony_ci struct virt_dma_desc *vd; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci vd = vchan_next_desc(&chan->vc); 3168c2ecf20Sopenharmony_ci if (!vd) 3178c2ecf20Sopenharmony_ci return; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci desc = vd_to_axi_desc(vd); 3208c2ecf20Sopenharmony_ci dev_vdbg(chan2dev(chan), "%s: started %u\n", axi_chan_name(chan), 3218c2ecf20Sopenharmony_ci vd->tx.cookie); 3228c2ecf20Sopenharmony_ci axi_chan_block_xfer_start(chan, desc); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic void dma_chan_issue_pending(struct dma_chan *dchan) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 3288c2ecf20Sopenharmony_ci unsigned long flags; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 3318c2ecf20Sopenharmony_ci if (vchan_issue_pending(&chan->vc)) 3328c2ecf20Sopenharmony_ci axi_chan_start_first_queued(chan); 3338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int dma_chan_alloc_chan_resources(struct dma_chan *dchan) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* ASSERT: channel is idle */ 3418c2ecf20Sopenharmony_ci if (axi_chan_is_hw_enable(chan)) { 3428c2ecf20Sopenharmony_ci dev_err(chan2dev(chan), "%s is non-idle!\n", 3438c2ecf20Sopenharmony_ci axi_chan_name(chan)); 3448c2ecf20Sopenharmony_ci return -EBUSY; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci dev_vdbg(dchan2dev(dchan), "%s: allocating\n", axi_chan_name(chan)); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci pm_runtime_get(chan->chip->dev); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic void dma_chan_free_chan_resources(struct dma_chan *dchan) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* ASSERT: channel is idle */ 3598c2ecf20Sopenharmony_ci if (axi_chan_is_hw_enable(chan)) 3608c2ecf20Sopenharmony_ci dev_err(dchan2dev(dchan), "%s is non-idle!\n", 3618c2ecf20Sopenharmony_ci axi_chan_name(chan)); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci axi_chan_disable(chan); 3648c2ecf20Sopenharmony_ci axi_chan_irq_disable(chan, DWAXIDMAC_IRQ_ALL); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci vchan_free_chan_resources(&chan->vc); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci dev_vdbg(dchan2dev(dchan), 3698c2ecf20Sopenharmony_ci "%s: free resources, descriptor still allocated: %u\n", 3708c2ecf20Sopenharmony_ci axi_chan_name(chan), atomic_read(&chan->descs_allocated)); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci pm_runtime_put(chan->chip->dev); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci/* 3768c2ecf20Sopenharmony_ci * If DW_axi_dmac sees CHx_CTL.ShadowReg_Or_LLI_Last bit of the fetched LLI 3778c2ecf20Sopenharmony_ci * as 1, it understands that the current block is the final block in the 3788c2ecf20Sopenharmony_ci * transfer and completes the DMA transfer operation at the end of current 3798c2ecf20Sopenharmony_ci * block transfer. 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_cistatic void set_desc_last(struct axi_dma_desc *desc) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci u32 val; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci val = le32_to_cpu(desc->lli.ctl_hi); 3868c2ecf20Sopenharmony_ci val |= CH_CTL_H_LLI_LAST; 3878c2ecf20Sopenharmony_ci desc->lli.ctl_hi = cpu_to_le32(val); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic void write_desc_sar(struct axi_dma_desc *desc, dma_addr_t adr) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci desc->lli.sar = cpu_to_le64(adr); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic void write_desc_dar(struct axi_dma_desc *desc, dma_addr_t adr) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci desc->lli.dar = cpu_to_le64(adr); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic void set_desc_src_master(struct axi_dma_desc *desc) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci u32 val; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* Select AXI0 for source master */ 4058c2ecf20Sopenharmony_ci val = le32_to_cpu(desc->lli.ctl_lo); 4068c2ecf20Sopenharmony_ci val &= ~CH_CTL_L_SRC_MAST; 4078c2ecf20Sopenharmony_ci desc->lli.ctl_lo = cpu_to_le32(val); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic void set_desc_dest_master(struct axi_dma_desc *desc) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci u32 val; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* Select AXI1 for source master if available */ 4158c2ecf20Sopenharmony_ci val = le32_to_cpu(desc->lli.ctl_lo); 4168c2ecf20Sopenharmony_ci if (desc->chan->chip->dw->hdata->nr_masters > 1) 4178c2ecf20Sopenharmony_ci val |= CH_CTL_L_DST_MAST; 4188c2ecf20Sopenharmony_ci else 4198c2ecf20Sopenharmony_ci val &= ~CH_CTL_L_DST_MAST; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci desc->lli.ctl_lo = cpu_to_le32(val); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor * 4258c2ecf20Sopenharmony_cidma_chan_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dst_adr, 4268c2ecf20Sopenharmony_ci dma_addr_t src_adr, size_t len, unsigned long flags) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct axi_dma_desc *first = NULL, *desc = NULL, *prev = NULL; 4298c2ecf20Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 4308c2ecf20Sopenharmony_ci size_t block_ts, max_block_ts, xfer_len; 4318c2ecf20Sopenharmony_ci u32 xfer_width, reg; 4328c2ecf20Sopenharmony_ci u8 lms = 0; /* Select AXI0 master for LLI fetching */ 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: memcpy: src: %pad dst: %pad length: %zd flags: %#lx", 4358c2ecf20Sopenharmony_ci axi_chan_name(chan), &src_adr, &dst_adr, len, flags); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci max_block_ts = chan->chip->dw->hdata->block_size[chan->id]; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci while (len) { 4408c2ecf20Sopenharmony_ci xfer_len = len; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* 4438c2ecf20Sopenharmony_ci * Take care for the alignment. 4448c2ecf20Sopenharmony_ci * Actually source and destination widths can be different, but 4458c2ecf20Sopenharmony_ci * make them same to be simpler. 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_ci xfer_width = axi_chan_get_xfer_width(chan, src_adr, dst_adr, xfer_len); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* 4508c2ecf20Sopenharmony_ci * block_ts indicates the total number of data of width 4518c2ecf20Sopenharmony_ci * to be transferred in a DMA block transfer. 4528c2ecf20Sopenharmony_ci * BLOCK_TS register should be set to block_ts - 1 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ci block_ts = xfer_len >> xfer_width; 4558c2ecf20Sopenharmony_ci if (block_ts > max_block_ts) { 4568c2ecf20Sopenharmony_ci block_ts = max_block_ts; 4578c2ecf20Sopenharmony_ci xfer_len = max_block_ts << xfer_width; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci desc = axi_desc_get(chan); 4618c2ecf20Sopenharmony_ci if (unlikely(!desc)) 4628c2ecf20Sopenharmony_ci goto err_desc_get; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci write_desc_sar(desc, src_adr); 4658c2ecf20Sopenharmony_ci write_desc_dar(desc, dst_adr); 4668c2ecf20Sopenharmony_ci desc->lli.block_ts_lo = cpu_to_le32(block_ts - 1); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci reg = CH_CTL_H_LLI_VALID; 4698c2ecf20Sopenharmony_ci if (chan->chip->dw->hdata->restrict_axi_burst_len) { 4708c2ecf20Sopenharmony_ci u32 burst_len = chan->chip->dw->hdata->axi_rw_burst_len; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci reg |= (CH_CTL_H_ARLEN_EN | 4738c2ecf20Sopenharmony_ci burst_len << CH_CTL_H_ARLEN_POS | 4748c2ecf20Sopenharmony_ci CH_CTL_H_AWLEN_EN | 4758c2ecf20Sopenharmony_ci burst_len << CH_CTL_H_AWLEN_POS); 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci desc->lli.ctl_hi = cpu_to_le32(reg); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci reg = (DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_DST_MSIZE_POS | 4808c2ecf20Sopenharmony_ci DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_SRC_MSIZE_POS | 4818c2ecf20Sopenharmony_ci xfer_width << CH_CTL_L_DST_WIDTH_POS | 4828c2ecf20Sopenharmony_ci xfer_width << CH_CTL_L_SRC_WIDTH_POS | 4838c2ecf20Sopenharmony_ci DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_DST_INC_POS | 4848c2ecf20Sopenharmony_ci DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_SRC_INC_POS); 4858c2ecf20Sopenharmony_ci desc->lli.ctl_lo = cpu_to_le32(reg); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci set_desc_src_master(desc); 4888c2ecf20Sopenharmony_ci set_desc_dest_master(desc); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* Manage transfer list (xfer_list) */ 4918c2ecf20Sopenharmony_ci if (!first) { 4928c2ecf20Sopenharmony_ci first = desc; 4938c2ecf20Sopenharmony_ci } else { 4948c2ecf20Sopenharmony_ci list_add_tail(&desc->xfer_list, &first->xfer_list); 4958c2ecf20Sopenharmony_ci write_desc_llp(prev, desc->vd.tx.phys | lms); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci prev = desc; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* update the length and addresses for the next loop cycle */ 5008c2ecf20Sopenharmony_ci len -= xfer_len; 5018c2ecf20Sopenharmony_ci dst_adr += xfer_len; 5028c2ecf20Sopenharmony_ci src_adr += xfer_len; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* Total len of src/dest sg == 0, so no descriptor were allocated */ 5068c2ecf20Sopenharmony_ci if (unlikely(!first)) 5078c2ecf20Sopenharmony_ci return NULL; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* Set end-of-link to the last link descriptor of list */ 5108c2ecf20Sopenharmony_ci set_desc_last(desc); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci return vchan_tx_prep(&chan->vc, &first->vd, flags); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cierr_desc_get: 5158c2ecf20Sopenharmony_ci if (first) 5168c2ecf20Sopenharmony_ci axi_desc_put(first); 5178c2ecf20Sopenharmony_ci return NULL; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic void axi_chan_dump_lli(struct axi_dma_chan *chan, 5218c2ecf20Sopenharmony_ci struct axi_dma_desc *desc) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci dev_err(dchan2dev(&chan->vc.chan), 5248c2ecf20Sopenharmony_ci "SAR: 0x%llx DAR: 0x%llx LLP: 0x%llx BTS 0x%x CTL: 0x%x:%08x", 5258c2ecf20Sopenharmony_ci le64_to_cpu(desc->lli.sar), 5268c2ecf20Sopenharmony_ci le64_to_cpu(desc->lli.dar), 5278c2ecf20Sopenharmony_ci le64_to_cpu(desc->lli.llp), 5288c2ecf20Sopenharmony_ci le32_to_cpu(desc->lli.block_ts_lo), 5298c2ecf20Sopenharmony_ci le32_to_cpu(desc->lli.ctl_hi), 5308c2ecf20Sopenharmony_ci le32_to_cpu(desc->lli.ctl_lo)); 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic void axi_chan_list_dump_lli(struct axi_dma_chan *chan, 5348c2ecf20Sopenharmony_ci struct axi_dma_desc *desc_head) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct axi_dma_desc *desc; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci axi_chan_dump_lli(chan, desc_head); 5398c2ecf20Sopenharmony_ci list_for_each_entry(desc, &desc_head->xfer_list, xfer_list) 5408c2ecf20Sopenharmony_ci axi_chan_dump_lli(chan, desc); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct virt_dma_desc *vd; 5468c2ecf20Sopenharmony_ci unsigned long flags; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci axi_chan_disable(chan); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* The bad descriptor currently is in the head of vc list */ 5538c2ecf20Sopenharmony_ci vd = vchan_next_desc(&chan->vc); 5548c2ecf20Sopenharmony_ci if (!vd) { 5558c2ecf20Sopenharmony_ci dev_err(chan2dev(chan), "BUG: %s, IRQ with no descriptors\n", 5568c2ecf20Sopenharmony_ci axi_chan_name(chan)); 5578c2ecf20Sopenharmony_ci goto out; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci /* Remove the completed descriptor from issued list */ 5608c2ecf20Sopenharmony_ci list_del(&vd->node); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* WARN about bad descriptor */ 5638c2ecf20Sopenharmony_ci dev_err(chan2dev(chan), 5648c2ecf20Sopenharmony_ci "Bad descriptor submitted for %s, cookie: %d, irq: 0x%08x\n", 5658c2ecf20Sopenharmony_ci axi_chan_name(chan), vd->tx.cookie, status); 5668c2ecf20Sopenharmony_ci axi_chan_list_dump_lli(chan, vd_to_axi_desc(vd)); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci vchan_cookie_complete(vd); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* Try to restart the controller */ 5718c2ecf20Sopenharmony_ci axi_chan_start_first_queued(chan); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ciout: 5748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic void axi_chan_block_xfer_complete(struct axi_dma_chan *chan) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct virt_dma_desc *vd; 5808c2ecf20Sopenharmony_ci unsigned long flags; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 5838c2ecf20Sopenharmony_ci if (unlikely(axi_chan_is_hw_enable(chan))) { 5848c2ecf20Sopenharmony_ci dev_err(chan2dev(chan), "BUG: %s caught DWAXIDMAC_IRQ_DMA_TRF, but channel not idle!\n", 5858c2ecf20Sopenharmony_ci axi_chan_name(chan)); 5868c2ecf20Sopenharmony_ci axi_chan_disable(chan); 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* The completed descriptor currently is in the head of vc list */ 5908c2ecf20Sopenharmony_ci vd = vchan_next_desc(&chan->vc); 5918c2ecf20Sopenharmony_ci /* Remove the completed descriptor from issued list before completing */ 5928c2ecf20Sopenharmony_ci list_del(&vd->node); 5938c2ecf20Sopenharmony_ci vchan_cookie_complete(vd); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* Submit queued descriptors after processing the completed ones */ 5968c2ecf20Sopenharmony_ci axi_chan_start_first_queued(chan); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic irqreturn_t dw_axi_dma_interrupt(int irq, void *dev_id) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct axi_dma_chip *chip = dev_id; 6048c2ecf20Sopenharmony_ci struct dw_axi_dma *dw = chip->dw; 6058c2ecf20Sopenharmony_ci struct axi_dma_chan *chan; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci u32 status, i; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* Disable DMAC inerrupts. We'll enable them after processing chanels */ 6108c2ecf20Sopenharmony_ci axi_dma_irq_disable(chip); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* Poll, clear and process every chanel interrupt status */ 6138c2ecf20Sopenharmony_ci for (i = 0; i < dw->hdata->nr_channels; i++) { 6148c2ecf20Sopenharmony_ci chan = &dw->chan[i]; 6158c2ecf20Sopenharmony_ci status = axi_chan_irq_read(chan); 6168c2ecf20Sopenharmony_ci axi_chan_irq_clear(chan, status); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci dev_vdbg(chip->dev, "%s %u IRQ status: 0x%08x\n", 6198c2ecf20Sopenharmony_ci axi_chan_name(chan), i, status); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (status & DWAXIDMAC_IRQ_ALL_ERR) 6228c2ecf20Sopenharmony_ci axi_chan_handle_err(chan, status); 6238c2ecf20Sopenharmony_ci else if (status & DWAXIDMAC_IRQ_DMA_TRF) 6248c2ecf20Sopenharmony_ci axi_chan_block_xfer_complete(chan); 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* Re-enable interrupts */ 6288c2ecf20Sopenharmony_ci axi_dma_irq_enable(chip); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic int dma_chan_terminate_all(struct dma_chan *dchan) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 6368c2ecf20Sopenharmony_ci unsigned long flags; 6378c2ecf20Sopenharmony_ci LIST_HEAD(head); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci axi_chan_disable(chan); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci vchan_get_all_descriptors(&chan->vc, &head); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci vchan_dma_desc_free_list(&chan->vc, &head); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci dev_vdbg(dchan2dev(dchan), "terminated: %s\n", axi_chan_name(chan)); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci return 0; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic int dma_chan_pause(struct dma_chan *dchan) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 6578c2ecf20Sopenharmony_ci unsigned long flags; 6588c2ecf20Sopenharmony_ci unsigned int timeout = 20; /* timeout iterations */ 6598c2ecf20Sopenharmony_ci u32 val; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci val = axi_dma_ioread32(chan->chip, DMAC_CHEN); 6648c2ecf20Sopenharmony_ci val |= BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT | 6658c2ecf20Sopenharmony_ci BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT; 6668c2ecf20Sopenharmony_ci axi_dma_iowrite32(chan->chip, DMAC_CHEN, val); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci do { 6698c2ecf20Sopenharmony_ci if (axi_chan_irq_read(chan) & DWAXIDMAC_IRQ_SUSPENDED) 6708c2ecf20Sopenharmony_ci break; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci udelay(2); 6738c2ecf20Sopenharmony_ci } while (--timeout); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci axi_chan_irq_clear(chan, DWAXIDMAC_IRQ_SUSPENDED); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci chan->is_paused = true; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci return timeout ? 0 : -EAGAIN; 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci/* Called in chan locked context */ 6858c2ecf20Sopenharmony_cistatic inline void axi_chan_resume(struct axi_dma_chan *chan) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci u32 val; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci val = axi_dma_ioread32(chan->chip, DMAC_CHEN); 6908c2ecf20Sopenharmony_ci val &= ~(BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT); 6918c2ecf20Sopenharmony_ci val |= (BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT); 6928c2ecf20Sopenharmony_ci axi_dma_iowrite32(chan->chip, DMAC_CHEN, val); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci chan->is_paused = false; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic int dma_chan_resume(struct dma_chan *dchan) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 7008c2ecf20Sopenharmony_ci unsigned long flags; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (chan->is_paused) 7058c2ecf20Sopenharmony_ci axi_chan_resume(chan); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci return 0; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic int axi_dma_suspend(struct axi_dma_chip *chip) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci axi_dma_irq_disable(chip); 7158c2ecf20Sopenharmony_ci axi_dma_disable(chip); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci clk_disable_unprepare(chip->core_clk); 7188c2ecf20Sopenharmony_ci clk_disable_unprepare(chip->cfgr_clk); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci return 0; 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistatic int axi_dma_resume(struct axi_dma_chip *chip) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci int ret; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci ret = clk_prepare_enable(chip->cfgr_clk); 7288c2ecf20Sopenharmony_ci if (ret < 0) 7298c2ecf20Sopenharmony_ci return ret; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci ret = clk_prepare_enable(chip->core_clk); 7328c2ecf20Sopenharmony_ci if (ret < 0) 7338c2ecf20Sopenharmony_ci return ret; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci axi_dma_enable(chip); 7368c2ecf20Sopenharmony_ci axi_dma_irq_enable(chip); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci return 0; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic int __maybe_unused axi_dma_runtime_suspend(struct device *dev) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct axi_dma_chip *chip = dev_get_drvdata(dev); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci return axi_dma_suspend(chip); 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic int __maybe_unused axi_dma_runtime_resume(struct device *dev) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct axi_dma_chip *chip = dev_get_drvdata(dev); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci return axi_dma_resume(chip); 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic int parse_device_properties(struct axi_dma_chip *chip) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci struct device *dev = chip->dev; 7588c2ecf20Sopenharmony_ci u32 tmp, carr[DMAC_MAX_CHANNELS]; 7598c2ecf20Sopenharmony_ci int ret; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci ret = device_property_read_u32(dev, "dma-channels", &tmp); 7628c2ecf20Sopenharmony_ci if (ret) 7638c2ecf20Sopenharmony_ci return ret; 7648c2ecf20Sopenharmony_ci if (tmp == 0 || tmp > DMAC_MAX_CHANNELS) 7658c2ecf20Sopenharmony_ci return -EINVAL; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci chip->dw->hdata->nr_channels = tmp; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci ret = device_property_read_u32(dev, "snps,dma-masters", &tmp); 7708c2ecf20Sopenharmony_ci if (ret) 7718c2ecf20Sopenharmony_ci return ret; 7728c2ecf20Sopenharmony_ci if (tmp == 0 || tmp > DMAC_MAX_MASTERS) 7738c2ecf20Sopenharmony_ci return -EINVAL; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci chip->dw->hdata->nr_masters = tmp; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci ret = device_property_read_u32(dev, "snps,data-width", &tmp); 7788c2ecf20Sopenharmony_ci if (ret) 7798c2ecf20Sopenharmony_ci return ret; 7808c2ecf20Sopenharmony_ci if (tmp > DWAXIDMAC_TRANS_WIDTH_MAX) 7818c2ecf20Sopenharmony_ci return -EINVAL; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci chip->dw->hdata->m_data_width = tmp; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci ret = device_property_read_u32_array(dev, "snps,block-size", carr, 7868c2ecf20Sopenharmony_ci chip->dw->hdata->nr_channels); 7878c2ecf20Sopenharmony_ci if (ret) 7888c2ecf20Sopenharmony_ci return ret; 7898c2ecf20Sopenharmony_ci for (tmp = 0; tmp < chip->dw->hdata->nr_channels; tmp++) { 7908c2ecf20Sopenharmony_ci if (carr[tmp] == 0 || carr[tmp] > DMAC_MAX_BLK_SIZE) 7918c2ecf20Sopenharmony_ci return -EINVAL; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci chip->dw->hdata->block_size[tmp] = carr[tmp]; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci ret = device_property_read_u32_array(dev, "snps,priority", carr, 7978c2ecf20Sopenharmony_ci chip->dw->hdata->nr_channels); 7988c2ecf20Sopenharmony_ci if (ret) 7998c2ecf20Sopenharmony_ci return ret; 8008c2ecf20Sopenharmony_ci /* Priority value must be programmed within [0:nr_channels-1] range */ 8018c2ecf20Sopenharmony_ci for (tmp = 0; tmp < chip->dw->hdata->nr_channels; tmp++) { 8028c2ecf20Sopenharmony_ci if (carr[tmp] >= chip->dw->hdata->nr_channels) 8038c2ecf20Sopenharmony_ci return -EINVAL; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci chip->dw->hdata->priority[tmp] = carr[tmp]; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* axi-max-burst-len is optional property */ 8098c2ecf20Sopenharmony_ci ret = device_property_read_u32(dev, "snps,axi-max-burst-len", &tmp); 8108c2ecf20Sopenharmony_ci if (!ret) { 8118c2ecf20Sopenharmony_ci if (tmp > DWAXIDMAC_ARWLEN_MAX + 1) 8128c2ecf20Sopenharmony_ci return -EINVAL; 8138c2ecf20Sopenharmony_ci if (tmp < DWAXIDMAC_ARWLEN_MIN + 1) 8148c2ecf20Sopenharmony_ci return -EINVAL; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci chip->dw->hdata->restrict_axi_burst_len = true; 8178c2ecf20Sopenharmony_ci chip->dw->hdata->axi_rw_burst_len = tmp - 1; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci return 0; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic int dw_probe(struct platform_device *pdev) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct axi_dma_chip *chip; 8268c2ecf20Sopenharmony_ci struct resource *mem; 8278c2ecf20Sopenharmony_ci struct dw_axi_dma *dw; 8288c2ecf20Sopenharmony_ci struct dw_axi_dma_hcfg *hdata; 8298c2ecf20Sopenharmony_ci u32 i; 8308c2ecf20Sopenharmony_ci int ret; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); 8338c2ecf20Sopenharmony_ci if (!chip) 8348c2ecf20Sopenharmony_ci return -ENOMEM; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL); 8378c2ecf20Sopenharmony_ci if (!dw) 8388c2ecf20Sopenharmony_ci return -ENOMEM; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci hdata = devm_kzalloc(&pdev->dev, sizeof(*hdata), GFP_KERNEL); 8418c2ecf20Sopenharmony_ci if (!hdata) 8428c2ecf20Sopenharmony_ci return -ENOMEM; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci chip->dw = dw; 8458c2ecf20Sopenharmony_ci chip->dev = &pdev->dev; 8468c2ecf20Sopenharmony_ci chip->dw->hdata = hdata; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci chip->irq = platform_get_irq(pdev, 0); 8498c2ecf20Sopenharmony_ci if (chip->irq < 0) 8508c2ecf20Sopenharmony_ci return chip->irq; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 8538c2ecf20Sopenharmony_ci chip->regs = devm_ioremap_resource(chip->dev, mem); 8548c2ecf20Sopenharmony_ci if (IS_ERR(chip->regs)) 8558c2ecf20Sopenharmony_ci return PTR_ERR(chip->regs); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci chip->core_clk = devm_clk_get(chip->dev, "core-clk"); 8588c2ecf20Sopenharmony_ci if (IS_ERR(chip->core_clk)) 8598c2ecf20Sopenharmony_ci return PTR_ERR(chip->core_clk); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci chip->cfgr_clk = devm_clk_get(chip->dev, "cfgr-clk"); 8628c2ecf20Sopenharmony_ci if (IS_ERR(chip->cfgr_clk)) 8638c2ecf20Sopenharmony_ci return PTR_ERR(chip->cfgr_clk); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci ret = parse_device_properties(chip); 8668c2ecf20Sopenharmony_ci if (ret) 8678c2ecf20Sopenharmony_ci return ret; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci dw->chan = devm_kcalloc(chip->dev, hdata->nr_channels, 8708c2ecf20Sopenharmony_ci sizeof(*dw->chan), GFP_KERNEL); 8718c2ecf20Sopenharmony_ci if (!dw->chan) 8728c2ecf20Sopenharmony_ci return -ENOMEM; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci ret = devm_request_irq(chip->dev, chip->irq, dw_axi_dma_interrupt, 8758c2ecf20Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, chip); 8768c2ecf20Sopenharmony_ci if (ret) 8778c2ecf20Sopenharmony_ci return ret; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci /* Lli address must be aligned to a 64-byte boundary */ 8808c2ecf20Sopenharmony_ci dw->desc_pool = dmam_pool_create(KBUILD_MODNAME, chip->dev, 8818c2ecf20Sopenharmony_ci sizeof(struct axi_dma_desc), 64, 0); 8828c2ecf20Sopenharmony_ci if (!dw->desc_pool) { 8838c2ecf20Sopenharmony_ci dev_err(chip->dev, "No memory for descriptors dma pool\n"); 8848c2ecf20Sopenharmony_ci return -ENOMEM; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dw->dma.channels); 8888c2ecf20Sopenharmony_ci for (i = 0; i < hdata->nr_channels; i++) { 8898c2ecf20Sopenharmony_ci struct axi_dma_chan *chan = &dw->chan[i]; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci chan->chip = chip; 8928c2ecf20Sopenharmony_ci chan->id = i; 8938c2ecf20Sopenharmony_ci chan->chan_regs = chip->regs + COMMON_REG_LEN + i * CHAN_REG_LEN; 8948c2ecf20Sopenharmony_ci atomic_set(&chan->descs_allocated, 0); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci chan->vc.desc_free = vchan_desc_put; 8978c2ecf20Sopenharmony_ci vchan_init(&chan->vc, &dw->dma); 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci /* Set capabilities */ 9018c2ecf20Sopenharmony_ci dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci /* DMA capabilities */ 9048c2ecf20Sopenharmony_ci dw->dma.chancnt = hdata->nr_channels; 9058c2ecf20Sopenharmony_ci dw->dma.src_addr_widths = AXI_DMA_BUSWIDTHS; 9068c2ecf20Sopenharmony_ci dw->dma.dst_addr_widths = AXI_DMA_BUSWIDTHS; 9078c2ecf20Sopenharmony_ci dw->dma.directions = BIT(DMA_MEM_TO_MEM); 9088c2ecf20Sopenharmony_ci dw->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci dw->dma.dev = chip->dev; 9118c2ecf20Sopenharmony_ci dw->dma.device_tx_status = dma_chan_tx_status; 9128c2ecf20Sopenharmony_ci dw->dma.device_issue_pending = dma_chan_issue_pending; 9138c2ecf20Sopenharmony_ci dw->dma.device_terminate_all = dma_chan_terminate_all; 9148c2ecf20Sopenharmony_ci dw->dma.device_pause = dma_chan_pause; 9158c2ecf20Sopenharmony_ci dw->dma.device_resume = dma_chan_resume; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci dw->dma.device_alloc_chan_resources = dma_chan_alloc_chan_resources; 9188c2ecf20Sopenharmony_ci dw->dma.device_free_chan_resources = dma_chan_free_chan_resources; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci dw->dma.device_prep_dma_memcpy = dma_chan_prep_dma_memcpy; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, chip); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci pm_runtime_enable(chip->dev); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci /* 9278c2ecf20Sopenharmony_ci * We can't just call pm_runtime_get here instead of 9288c2ecf20Sopenharmony_ci * pm_runtime_get_noresume + axi_dma_resume because we need 9298c2ecf20Sopenharmony_ci * driver to work also without Runtime PM. 9308c2ecf20Sopenharmony_ci */ 9318c2ecf20Sopenharmony_ci pm_runtime_get_noresume(chip->dev); 9328c2ecf20Sopenharmony_ci ret = axi_dma_resume(chip); 9338c2ecf20Sopenharmony_ci if (ret < 0) 9348c2ecf20Sopenharmony_ci goto err_pm_disable; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci axi_dma_hw_init(chip); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci pm_runtime_put(chip->dev); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci ret = dmaenginem_async_device_register(&dw->dma); 9418c2ecf20Sopenharmony_ci if (ret) 9428c2ecf20Sopenharmony_ci goto err_pm_disable; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci dev_info(chip->dev, "DesignWare AXI DMA Controller, %d channels\n", 9458c2ecf20Sopenharmony_ci dw->hdata->nr_channels); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci return 0; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cierr_pm_disable: 9508c2ecf20Sopenharmony_ci pm_runtime_disable(chip->dev); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci return ret; 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic int dw_remove(struct platform_device *pdev) 9568c2ecf20Sopenharmony_ci{ 9578c2ecf20Sopenharmony_ci struct axi_dma_chip *chip = platform_get_drvdata(pdev); 9588c2ecf20Sopenharmony_ci struct dw_axi_dma *dw = chip->dw; 9598c2ecf20Sopenharmony_ci struct axi_dma_chan *chan, *_chan; 9608c2ecf20Sopenharmony_ci u32 i; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* Enable clk before accessing to registers */ 9638c2ecf20Sopenharmony_ci clk_prepare_enable(chip->cfgr_clk); 9648c2ecf20Sopenharmony_ci clk_prepare_enable(chip->core_clk); 9658c2ecf20Sopenharmony_ci axi_dma_irq_disable(chip); 9668c2ecf20Sopenharmony_ci for (i = 0; i < dw->hdata->nr_channels; i++) { 9678c2ecf20Sopenharmony_ci axi_chan_disable(&chip->dw->chan[i]); 9688c2ecf20Sopenharmony_ci axi_chan_irq_disable(&chip->dw->chan[i], DWAXIDMAC_IRQ_ALL); 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci axi_dma_disable(chip); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci pm_runtime_disable(chip->dev); 9738c2ecf20Sopenharmony_ci axi_dma_suspend(chip); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci devm_free_irq(chip->dev, chip->irq, chip); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci list_for_each_entry_safe(chan, _chan, &dw->dma.channels, 9788c2ecf20Sopenharmony_ci vc.chan.device_node) { 9798c2ecf20Sopenharmony_ci list_del(&chan->vc.chan.device_node); 9808c2ecf20Sopenharmony_ci tasklet_kill(&chan->vc.task); 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci return 0; 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_cistatic const struct dev_pm_ops dw_axi_dma_pm_ops = { 9878c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL) 9888c2ecf20Sopenharmony_ci}; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_cistatic const struct of_device_id dw_dma_of_id_table[] = { 9918c2ecf20Sopenharmony_ci { .compatible = "snps,axi-dma-1.01a" }, 9928c2ecf20Sopenharmony_ci {} 9938c2ecf20Sopenharmony_ci}; 9948c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, dw_dma_of_id_table); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic struct platform_driver dw_driver = { 9978c2ecf20Sopenharmony_ci .probe = dw_probe, 9988c2ecf20Sopenharmony_ci .remove = dw_remove, 9998c2ecf20Sopenharmony_ci .driver = { 10008c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 10018c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(dw_dma_of_id_table), 10028c2ecf20Sopenharmony_ci .pm = &dw_axi_dma_pm_ops, 10038c2ecf20Sopenharmony_ci }, 10048c2ecf20Sopenharmony_ci}; 10058c2ecf20Sopenharmony_cimodule_platform_driver(dw_driver); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 10088c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Synopsys DesignWare AXI DMA Controller platform driver"); 10098c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>"); 1010