162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// (C) 2017-2018 Synopsys, Inc. (www.synopsys.com) 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* 562306a36Sopenharmony_ci * Synopsys DesignWare AXI DMA Controller driver. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/bitops.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/dmaengine.h> 1462306a36Sopenharmony_ci#include <linux/dmapool.h> 1562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1662306a36Sopenharmony_ci#include <linux/err.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/iopoll.h> 2062306a36Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include <linux/of_dma.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2762306a36Sopenharmony_ci#include <linux/property.h> 2862306a36Sopenharmony_ci#include <linux/reset.h> 2962306a36Sopenharmony_ci#include <linux/slab.h> 3062306a36Sopenharmony_ci#include <linux/types.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "dw-axi-dmac.h" 3362306a36Sopenharmony_ci#include "../dmaengine.h" 3462306a36Sopenharmony_ci#include "../virt-dma.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * The set of bus widths supported by the DMA controller. DW AXI DMAC supports 3862306a36Sopenharmony_ci * master data bus width up to 512 bits (for both AXI master interfaces), but 3962306a36Sopenharmony_ci * it depends on IP block configuration. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci#define AXI_DMA_BUSWIDTHS \ 4262306a36Sopenharmony_ci (DMA_SLAVE_BUSWIDTH_1_BYTE | \ 4362306a36Sopenharmony_ci DMA_SLAVE_BUSWIDTH_2_BYTES | \ 4462306a36Sopenharmony_ci DMA_SLAVE_BUSWIDTH_4_BYTES | \ 4562306a36Sopenharmony_ci DMA_SLAVE_BUSWIDTH_8_BYTES | \ 4662306a36Sopenharmony_ci DMA_SLAVE_BUSWIDTH_16_BYTES | \ 4762306a36Sopenharmony_ci DMA_SLAVE_BUSWIDTH_32_BYTES | \ 4862306a36Sopenharmony_ci DMA_SLAVE_BUSWIDTH_64_BYTES) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define AXI_DMA_FLAG_HAS_APB_REGS BIT(0) 5162306a36Sopenharmony_ci#define AXI_DMA_FLAG_HAS_RESETS BIT(1) 5262306a36Sopenharmony_ci#define AXI_DMA_FLAG_USE_CFG2 BIT(2) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic inline void 5562306a36Sopenharmony_ciaxi_dma_iowrite32(struct axi_dma_chip *chip, u32 reg, u32 val) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci iowrite32(val, chip->regs + reg); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic inline u32 axi_dma_ioread32(struct axi_dma_chip *chip, u32 reg) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci return ioread32(chip->regs + reg); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic inline void 6662306a36Sopenharmony_ciaxi_chan_iowrite32(struct axi_dma_chan *chan, u32 reg, u32 val) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci iowrite32(val, chan->chan_regs + reg); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic inline u32 axi_chan_ioread32(struct axi_dma_chan *chan, u32 reg) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return ioread32(chan->chan_regs + reg); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic inline void 7762306a36Sopenharmony_ciaxi_chan_iowrite64(struct axi_dma_chan *chan, u32 reg, u64 val) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci /* 8062306a36Sopenharmony_ci * We split one 64 bit write for two 32 bit write as some HW doesn't 8162306a36Sopenharmony_ci * support 64 bit access. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci iowrite32(lower_32_bits(val), chan->chan_regs + reg); 8462306a36Sopenharmony_ci iowrite32(upper_32_bits(val), chan->chan_regs + reg + 4); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic inline void axi_chan_config_write(struct axi_dma_chan *chan, 8862306a36Sopenharmony_ci struct axi_dma_chan_config *config) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci u32 cfg_lo, cfg_hi; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci cfg_lo = (config->dst_multblk_type << CH_CFG_L_DST_MULTBLK_TYPE_POS | 9362306a36Sopenharmony_ci config->src_multblk_type << CH_CFG_L_SRC_MULTBLK_TYPE_POS); 9462306a36Sopenharmony_ci if (chan->chip->dw->hdata->reg_map_8_channels && 9562306a36Sopenharmony_ci !chan->chip->dw->hdata->use_cfg2) { 9662306a36Sopenharmony_ci cfg_hi = config->tt_fc << CH_CFG_H_TT_FC_POS | 9762306a36Sopenharmony_ci config->hs_sel_src << CH_CFG_H_HS_SEL_SRC_POS | 9862306a36Sopenharmony_ci config->hs_sel_dst << CH_CFG_H_HS_SEL_DST_POS | 9962306a36Sopenharmony_ci config->src_per << CH_CFG_H_SRC_PER_POS | 10062306a36Sopenharmony_ci config->dst_per << CH_CFG_H_DST_PER_POS | 10162306a36Sopenharmony_ci config->prior << CH_CFG_H_PRIORITY_POS; 10262306a36Sopenharmony_ci } else { 10362306a36Sopenharmony_ci cfg_lo |= config->src_per << CH_CFG2_L_SRC_PER_POS | 10462306a36Sopenharmony_ci config->dst_per << CH_CFG2_L_DST_PER_POS; 10562306a36Sopenharmony_ci cfg_hi = config->tt_fc << CH_CFG2_H_TT_FC_POS | 10662306a36Sopenharmony_ci config->hs_sel_src << CH_CFG2_H_HS_SEL_SRC_POS | 10762306a36Sopenharmony_ci config->hs_sel_dst << CH_CFG2_H_HS_SEL_DST_POS | 10862306a36Sopenharmony_ci config->prior << CH_CFG2_H_PRIORITY_POS; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci axi_chan_iowrite32(chan, CH_CFG_L, cfg_lo); 11162306a36Sopenharmony_ci axi_chan_iowrite32(chan, CH_CFG_H, cfg_hi); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic inline void axi_dma_disable(struct axi_dma_chip *chip) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci u32 val; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci val = axi_dma_ioread32(chip, DMAC_CFG); 11962306a36Sopenharmony_ci val &= ~DMAC_EN_MASK; 12062306a36Sopenharmony_ci axi_dma_iowrite32(chip, DMAC_CFG, val); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic inline void axi_dma_enable(struct axi_dma_chip *chip) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci u32 val; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci val = axi_dma_ioread32(chip, DMAC_CFG); 12862306a36Sopenharmony_ci val |= DMAC_EN_MASK; 12962306a36Sopenharmony_ci axi_dma_iowrite32(chip, DMAC_CFG, val); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic inline void axi_dma_irq_disable(struct axi_dma_chip *chip) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci u32 val; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci val = axi_dma_ioread32(chip, DMAC_CFG); 13762306a36Sopenharmony_ci val &= ~INT_EN_MASK; 13862306a36Sopenharmony_ci axi_dma_iowrite32(chip, DMAC_CFG, val); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic inline void axi_dma_irq_enable(struct axi_dma_chip *chip) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci u32 val; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci val = axi_dma_ioread32(chip, DMAC_CFG); 14662306a36Sopenharmony_ci val |= INT_EN_MASK; 14762306a36Sopenharmony_ci axi_dma_iowrite32(chip, DMAC_CFG, val); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic inline void axi_chan_irq_disable(struct axi_dma_chan *chan, u32 irq_mask) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci u32 val; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (likely(irq_mask == DWAXIDMAC_IRQ_ALL)) { 15562306a36Sopenharmony_ci axi_chan_iowrite32(chan, CH_INTSTATUS_ENA, DWAXIDMAC_IRQ_NONE); 15662306a36Sopenharmony_ci } else { 15762306a36Sopenharmony_ci val = axi_chan_ioread32(chan, CH_INTSTATUS_ENA); 15862306a36Sopenharmony_ci val &= ~irq_mask; 15962306a36Sopenharmony_ci axi_chan_iowrite32(chan, CH_INTSTATUS_ENA, val); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic inline void axi_chan_irq_set(struct axi_dma_chan *chan, u32 irq_mask) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci axi_chan_iowrite32(chan, CH_INTSTATUS_ENA, irq_mask); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic inline void axi_chan_irq_sig_set(struct axi_dma_chan *chan, u32 irq_mask) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci axi_chan_iowrite32(chan, CH_INTSIGNAL_ENA, irq_mask); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic inline void axi_chan_irq_clear(struct axi_dma_chan *chan, u32 irq_mask) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci axi_chan_iowrite32(chan, CH_INTCLEAR, irq_mask); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic inline u32 axi_chan_irq_read(struct axi_dma_chan *chan) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci return axi_chan_ioread32(chan, CH_INTSTATUS); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic inline void axi_chan_disable(struct axi_dma_chan *chan) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci u32 val; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci val = axi_dma_ioread32(chan->chip, DMAC_CHEN); 18862306a36Sopenharmony_ci val &= ~(BIT(chan->id) << DMAC_CHAN_EN_SHIFT); 18962306a36Sopenharmony_ci if (chan->chip->dw->hdata->reg_map_8_channels) 19062306a36Sopenharmony_ci val |= BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT; 19162306a36Sopenharmony_ci else 19262306a36Sopenharmony_ci val |= BIT(chan->id) << DMAC_CHAN_EN2_WE_SHIFT; 19362306a36Sopenharmony_ci axi_dma_iowrite32(chan->chip, DMAC_CHEN, val); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic inline void axi_chan_enable(struct axi_dma_chan *chan) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci u32 val; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci val = axi_dma_ioread32(chan->chip, DMAC_CHEN); 20162306a36Sopenharmony_ci if (chan->chip->dw->hdata->reg_map_8_channels) 20262306a36Sopenharmony_ci val |= BIT(chan->id) << DMAC_CHAN_EN_SHIFT | 20362306a36Sopenharmony_ci BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT; 20462306a36Sopenharmony_ci else 20562306a36Sopenharmony_ci val |= BIT(chan->id) << DMAC_CHAN_EN_SHIFT | 20662306a36Sopenharmony_ci BIT(chan->id) << DMAC_CHAN_EN2_WE_SHIFT; 20762306a36Sopenharmony_ci axi_dma_iowrite32(chan->chip, DMAC_CHEN, val); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic inline bool axi_chan_is_hw_enable(struct axi_dma_chan *chan) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci u32 val; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci val = axi_dma_ioread32(chan->chip, DMAC_CHEN); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return !!(val & (BIT(chan->id) << DMAC_CHAN_EN_SHIFT)); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void axi_dma_hw_init(struct axi_dma_chip *chip) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci int ret; 22262306a36Sopenharmony_ci u32 i; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci for (i = 0; i < chip->dw->hdata->nr_channels; i++) { 22562306a36Sopenharmony_ci axi_chan_irq_disable(&chip->dw->chan[i], DWAXIDMAC_IRQ_ALL); 22662306a36Sopenharmony_ci axi_chan_disable(&chip->dw->chan[i]); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(chip->dev, DMA_BIT_MASK(64)); 22962306a36Sopenharmony_ci if (ret) 23062306a36Sopenharmony_ci dev_warn(chip->dev, "Unable to set coherent mask\n"); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic u32 axi_chan_get_xfer_width(struct axi_dma_chan *chan, dma_addr_t src, 23462306a36Sopenharmony_ci dma_addr_t dst, size_t len) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci u32 max_width = chan->chip->dw->hdata->m_data_width; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return __ffs(src | dst | len | BIT(max_width)); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic inline const char *axi_chan_name(struct axi_dma_chan *chan) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci return dma_chan_name(&chan->vc.chan); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic struct axi_dma_desc *axi_desc_alloc(u32 num) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct axi_dma_desc *desc; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_NOWAIT); 25162306a36Sopenharmony_ci if (!desc) 25262306a36Sopenharmony_ci return NULL; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci desc->hw_desc = kcalloc(num, sizeof(*desc->hw_desc), GFP_NOWAIT); 25562306a36Sopenharmony_ci if (!desc->hw_desc) { 25662306a36Sopenharmony_ci kfree(desc); 25762306a36Sopenharmony_ci return NULL; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return desc; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic struct axi_dma_lli *axi_desc_get(struct axi_dma_chan *chan, 26462306a36Sopenharmony_ci dma_addr_t *addr) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct axi_dma_lli *lli; 26762306a36Sopenharmony_ci dma_addr_t phys; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci lli = dma_pool_zalloc(chan->desc_pool, GFP_NOWAIT, &phys); 27062306a36Sopenharmony_ci if (unlikely(!lli)) { 27162306a36Sopenharmony_ci dev_err(chan2dev(chan), "%s: not enough descriptors available\n", 27262306a36Sopenharmony_ci axi_chan_name(chan)); 27362306a36Sopenharmony_ci return NULL; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci atomic_inc(&chan->descs_allocated); 27762306a36Sopenharmony_ci *addr = phys; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return lli; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic void axi_desc_put(struct axi_dma_desc *desc) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct axi_dma_chan *chan = desc->chan; 28562306a36Sopenharmony_ci int count = atomic_read(&chan->descs_allocated); 28662306a36Sopenharmony_ci struct axi_dma_hw_desc *hw_desc; 28762306a36Sopenharmony_ci int descs_put; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci for (descs_put = 0; descs_put < count; descs_put++) { 29062306a36Sopenharmony_ci hw_desc = &desc->hw_desc[descs_put]; 29162306a36Sopenharmony_ci dma_pool_free(chan->desc_pool, hw_desc->lli, hw_desc->llp); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci kfree(desc->hw_desc); 29562306a36Sopenharmony_ci kfree(desc); 29662306a36Sopenharmony_ci atomic_sub(descs_put, &chan->descs_allocated); 29762306a36Sopenharmony_ci dev_vdbg(chan2dev(chan), "%s: %d descs put, %d still allocated\n", 29862306a36Sopenharmony_ci axi_chan_name(chan), descs_put, 29962306a36Sopenharmony_ci atomic_read(&chan->descs_allocated)); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic void vchan_desc_put(struct virt_dma_desc *vdesc) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci axi_desc_put(vd_to_axi_desc(vdesc)); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic enum dma_status 30862306a36Sopenharmony_cidma_chan_tx_status(struct dma_chan *dchan, dma_cookie_t cookie, 30962306a36Sopenharmony_ci struct dma_tx_state *txstate) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 31262306a36Sopenharmony_ci struct virt_dma_desc *vdesc; 31362306a36Sopenharmony_ci enum dma_status status; 31462306a36Sopenharmony_ci u32 completed_length; 31562306a36Sopenharmony_ci unsigned long flags; 31662306a36Sopenharmony_ci u32 completed_blocks; 31762306a36Sopenharmony_ci size_t bytes = 0; 31862306a36Sopenharmony_ci u32 length; 31962306a36Sopenharmony_ci u32 len; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci status = dma_cookie_status(dchan, cookie, txstate); 32262306a36Sopenharmony_ci if (status == DMA_COMPLETE || !txstate) 32362306a36Sopenharmony_ci return status; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci vdesc = vchan_find_desc(&chan->vc, cookie); 32862306a36Sopenharmony_ci if (vdesc) { 32962306a36Sopenharmony_ci length = vd_to_axi_desc(vdesc)->length; 33062306a36Sopenharmony_ci completed_blocks = vd_to_axi_desc(vdesc)->completed_blocks; 33162306a36Sopenharmony_ci len = vd_to_axi_desc(vdesc)->hw_desc[0].len; 33262306a36Sopenharmony_ci completed_length = completed_blocks * len; 33362306a36Sopenharmony_ci bytes = length - completed_length; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 33762306a36Sopenharmony_ci dma_set_residue(txstate, bytes); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return status; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic void write_desc_llp(struct axi_dma_hw_desc *desc, dma_addr_t adr) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci desc->lli->llp = cpu_to_le64(adr); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic void write_chan_llp(struct axi_dma_chan *chan, dma_addr_t adr) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci axi_chan_iowrite64(chan, CH_LLP, adr); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic void dw_axi_dma_set_byte_halfword(struct axi_dma_chan *chan, bool set) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci u32 offset = DMAC_APB_BYTE_WR_CH_EN; 35562306a36Sopenharmony_ci u32 reg_width, val; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (!chan->chip->apb_regs) { 35862306a36Sopenharmony_ci dev_dbg(chan->chip->dev, "apb_regs not initialized\n"); 35962306a36Sopenharmony_ci return; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci reg_width = __ffs(chan->config.dst_addr_width); 36362306a36Sopenharmony_ci if (reg_width == DWAXIDMAC_TRANS_WIDTH_16) 36462306a36Sopenharmony_ci offset = DMAC_APB_HALFWORD_WR_CH_EN; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci val = ioread32(chan->chip->apb_regs + offset); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (set) 36962306a36Sopenharmony_ci val |= BIT(chan->id); 37062306a36Sopenharmony_ci else 37162306a36Sopenharmony_ci val &= ~BIT(chan->id); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci iowrite32(val, chan->chip->apb_regs + offset); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci/* Called in chan locked context */ 37662306a36Sopenharmony_cistatic void axi_chan_block_xfer_start(struct axi_dma_chan *chan, 37762306a36Sopenharmony_ci struct axi_dma_desc *first) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci u32 priority = chan->chip->dw->hdata->priority[chan->id]; 38062306a36Sopenharmony_ci struct axi_dma_chan_config config = {}; 38162306a36Sopenharmony_ci u32 irq_mask; 38262306a36Sopenharmony_ci u8 lms = 0; /* Select AXI0 master for LLI fetching */ 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (unlikely(axi_chan_is_hw_enable(chan))) { 38562306a36Sopenharmony_ci dev_err(chan2dev(chan), "%s is non-idle!\n", 38662306a36Sopenharmony_ci axi_chan_name(chan)); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci axi_dma_enable(chan->chip); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci config.dst_multblk_type = DWAXIDMAC_MBLK_TYPE_LL; 39462306a36Sopenharmony_ci config.src_multblk_type = DWAXIDMAC_MBLK_TYPE_LL; 39562306a36Sopenharmony_ci config.tt_fc = DWAXIDMAC_TT_FC_MEM_TO_MEM_DMAC; 39662306a36Sopenharmony_ci config.prior = priority; 39762306a36Sopenharmony_ci config.hs_sel_dst = DWAXIDMAC_HS_SEL_HW; 39862306a36Sopenharmony_ci config.hs_sel_src = DWAXIDMAC_HS_SEL_HW; 39962306a36Sopenharmony_ci switch (chan->direction) { 40062306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 40162306a36Sopenharmony_ci dw_axi_dma_set_byte_halfword(chan, true); 40262306a36Sopenharmony_ci config.tt_fc = chan->config.device_fc ? 40362306a36Sopenharmony_ci DWAXIDMAC_TT_FC_MEM_TO_PER_DST : 40462306a36Sopenharmony_ci DWAXIDMAC_TT_FC_MEM_TO_PER_DMAC; 40562306a36Sopenharmony_ci if (chan->chip->apb_regs) 40662306a36Sopenharmony_ci config.dst_per = chan->id; 40762306a36Sopenharmony_ci else 40862306a36Sopenharmony_ci config.dst_per = chan->hw_handshake_num; 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 41162306a36Sopenharmony_ci config.tt_fc = chan->config.device_fc ? 41262306a36Sopenharmony_ci DWAXIDMAC_TT_FC_PER_TO_MEM_SRC : 41362306a36Sopenharmony_ci DWAXIDMAC_TT_FC_PER_TO_MEM_DMAC; 41462306a36Sopenharmony_ci if (chan->chip->apb_regs) 41562306a36Sopenharmony_ci config.src_per = chan->id; 41662306a36Sopenharmony_ci else 41762306a36Sopenharmony_ci config.src_per = chan->hw_handshake_num; 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci default: 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci axi_chan_config_write(chan, &config); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci write_chan_llp(chan, first->hw_desc[0].llp | lms); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci irq_mask = DWAXIDMAC_IRQ_DMA_TRF | DWAXIDMAC_IRQ_ALL_ERR; 42762306a36Sopenharmony_ci axi_chan_irq_sig_set(chan, irq_mask); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* Generate 'suspend' status but don't generate interrupt */ 43062306a36Sopenharmony_ci irq_mask |= DWAXIDMAC_IRQ_SUSPENDED; 43162306a36Sopenharmony_ci axi_chan_irq_set(chan, irq_mask); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci axi_chan_enable(chan); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic void axi_chan_start_first_queued(struct axi_dma_chan *chan) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct axi_dma_desc *desc; 43962306a36Sopenharmony_ci struct virt_dma_desc *vd; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci vd = vchan_next_desc(&chan->vc); 44262306a36Sopenharmony_ci if (!vd) 44362306a36Sopenharmony_ci return; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci desc = vd_to_axi_desc(vd); 44662306a36Sopenharmony_ci dev_vdbg(chan2dev(chan), "%s: started %u\n", axi_chan_name(chan), 44762306a36Sopenharmony_ci vd->tx.cookie); 44862306a36Sopenharmony_ci axi_chan_block_xfer_start(chan, desc); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic void dma_chan_issue_pending(struct dma_chan *dchan) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 45462306a36Sopenharmony_ci unsigned long flags; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 45762306a36Sopenharmony_ci if (vchan_issue_pending(&chan->vc)) 45862306a36Sopenharmony_ci axi_chan_start_first_queued(chan); 45962306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic void dw_axi_dma_synchronize(struct dma_chan *dchan) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci vchan_synchronize(&chan->vc); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic int dma_chan_alloc_chan_resources(struct dma_chan *dchan) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* ASSERT: channel is idle */ 47462306a36Sopenharmony_ci if (axi_chan_is_hw_enable(chan)) { 47562306a36Sopenharmony_ci dev_err(chan2dev(chan), "%s is non-idle!\n", 47662306a36Sopenharmony_ci axi_chan_name(chan)); 47762306a36Sopenharmony_ci return -EBUSY; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* LLI address must be aligned to a 64-byte boundary */ 48162306a36Sopenharmony_ci chan->desc_pool = dma_pool_create(dev_name(chan2dev(chan)), 48262306a36Sopenharmony_ci chan->chip->dev, 48362306a36Sopenharmony_ci sizeof(struct axi_dma_lli), 48462306a36Sopenharmony_ci 64, 0); 48562306a36Sopenharmony_ci if (!chan->desc_pool) { 48662306a36Sopenharmony_ci dev_err(chan2dev(chan), "No memory for descriptors\n"); 48762306a36Sopenharmony_ci return -ENOMEM; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci dev_vdbg(dchan2dev(dchan), "%s: allocating\n", axi_chan_name(chan)); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci pm_runtime_get(chan->chip->dev); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic void dma_chan_free_chan_resources(struct dma_chan *dchan) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* ASSERT: channel is idle */ 50162306a36Sopenharmony_ci if (axi_chan_is_hw_enable(chan)) 50262306a36Sopenharmony_ci dev_err(dchan2dev(dchan), "%s is non-idle!\n", 50362306a36Sopenharmony_ci axi_chan_name(chan)); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci axi_chan_disable(chan); 50662306a36Sopenharmony_ci axi_chan_irq_disable(chan, DWAXIDMAC_IRQ_ALL); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci vchan_free_chan_resources(&chan->vc); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci dma_pool_destroy(chan->desc_pool); 51162306a36Sopenharmony_ci chan->desc_pool = NULL; 51262306a36Sopenharmony_ci dev_vdbg(dchan2dev(dchan), 51362306a36Sopenharmony_ci "%s: free resources, descriptor still allocated: %u\n", 51462306a36Sopenharmony_ci axi_chan_name(chan), atomic_read(&chan->descs_allocated)); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci pm_runtime_put(chan->chip->dev); 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic void dw_axi_dma_set_hw_channel(struct axi_dma_chan *chan, bool set) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct axi_dma_chip *chip = chan->chip; 52262306a36Sopenharmony_ci unsigned long reg_value, val; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (!chip->apb_regs) { 52562306a36Sopenharmony_ci dev_err(chip->dev, "apb_regs not initialized\n"); 52662306a36Sopenharmony_ci return; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* 53062306a36Sopenharmony_ci * An unused DMA channel has a default value of 0x3F. 53162306a36Sopenharmony_ci * Lock the DMA channel by assign a handshake number to the channel. 53262306a36Sopenharmony_ci * Unlock the DMA channel by assign 0x3F to the channel. 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci if (set) 53562306a36Sopenharmony_ci val = chan->hw_handshake_num; 53662306a36Sopenharmony_ci else 53762306a36Sopenharmony_ci val = UNUSED_CHANNEL; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci reg_value = lo_hi_readq(chip->apb_regs + DMAC_APB_HW_HS_SEL_0); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* Channel is already allocated, set handshake as per channel ID */ 54262306a36Sopenharmony_ci /* 64 bit write should handle for 8 channels */ 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci reg_value &= ~(DMA_APB_HS_SEL_MASK << 54562306a36Sopenharmony_ci (chan->id * DMA_APB_HS_SEL_BIT_SIZE)); 54662306a36Sopenharmony_ci reg_value |= (val << (chan->id * DMA_APB_HS_SEL_BIT_SIZE)); 54762306a36Sopenharmony_ci lo_hi_writeq(reg_value, chip->apb_regs + DMAC_APB_HW_HS_SEL_0); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci/* 55362306a36Sopenharmony_ci * If DW_axi_dmac sees CHx_CTL.ShadowReg_Or_LLI_Last bit of the fetched LLI 55462306a36Sopenharmony_ci * as 1, it understands that the current block is the final block in the 55562306a36Sopenharmony_ci * transfer and completes the DMA transfer operation at the end of current 55662306a36Sopenharmony_ci * block transfer. 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_cistatic void set_desc_last(struct axi_dma_hw_desc *desc) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci u32 val; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci val = le32_to_cpu(desc->lli->ctl_hi); 56362306a36Sopenharmony_ci val |= CH_CTL_H_LLI_LAST; 56462306a36Sopenharmony_ci desc->lli->ctl_hi = cpu_to_le32(val); 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic void write_desc_sar(struct axi_dma_hw_desc *desc, dma_addr_t adr) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci desc->lli->sar = cpu_to_le64(adr); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic void write_desc_dar(struct axi_dma_hw_desc *desc, dma_addr_t adr) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci desc->lli->dar = cpu_to_le64(adr); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic void set_desc_src_master(struct axi_dma_hw_desc *desc) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci u32 val; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* Select AXI0 for source master */ 58262306a36Sopenharmony_ci val = le32_to_cpu(desc->lli->ctl_lo); 58362306a36Sopenharmony_ci val &= ~CH_CTL_L_SRC_MAST; 58462306a36Sopenharmony_ci desc->lli->ctl_lo = cpu_to_le32(val); 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic void set_desc_dest_master(struct axi_dma_hw_desc *hw_desc, 58862306a36Sopenharmony_ci struct axi_dma_desc *desc) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci u32 val; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Select AXI1 for source master if available */ 59362306a36Sopenharmony_ci val = le32_to_cpu(hw_desc->lli->ctl_lo); 59462306a36Sopenharmony_ci if (desc->chan->chip->dw->hdata->nr_masters > 1) 59562306a36Sopenharmony_ci val |= CH_CTL_L_DST_MAST; 59662306a36Sopenharmony_ci else 59762306a36Sopenharmony_ci val &= ~CH_CTL_L_DST_MAST; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci hw_desc->lli->ctl_lo = cpu_to_le32(val); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic int dw_axi_dma_set_hw_desc(struct axi_dma_chan *chan, 60362306a36Sopenharmony_ci struct axi_dma_hw_desc *hw_desc, 60462306a36Sopenharmony_ci dma_addr_t mem_addr, size_t len) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci unsigned int data_width = BIT(chan->chip->dw->hdata->m_data_width); 60762306a36Sopenharmony_ci unsigned int reg_width; 60862306a36Sopenharmony_ci unsigned int mem_width; 60962306a36Sopenharmony_ci dma_addr_t device_addr; 61062306a36Sopenharmony_ci size_t axi_block_ts; 61162306a36Sopenharmony_ci size_t block_ts; 61262306a36Sopenharmony_ci u32 ctllo, ctlhi; 61362306a36Sopenharmony_ci u32 burst_len; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci axi_block_ts = chan->chip->dw->hdata->block_size[chan->id]; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci mem_width = __ffs(data_width | mem_addr | len); 61862306a36Sopenharmony_ci if (mem_width > DWAXIDMAC_TRANS_WIDTH_32) 61962306a36Sopenharmony_ci mem_width = DWAXIDMAC_TRANS_WIDTH_32; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (!IS_ALIGNED(mem_addr, 4)) { 62262306a36Sopenharmony_ci dev_err(chan->chip->dev, "invalid buffer alignment\n"); 62362306a36Sopenharmony_ci return -EINVAL; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci switch (chan->direction) { 62762306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 62862306a36Sopenharmony_ci reg_width = __ffs(chan->config.dst_addr_width); 62962306a36Sopenharmony_ci device_addr = chan->config.dst_addr; 63062306a36Sopenharmony_ci ctllo = reg_width << CH_CTL_L_DST_WIDTH_POS | 63162306a36Sopenharmony_ci mem_width << CH_CTL_L_SRC_WIDTH_POS | 63262306a36Sopenharmony_ci DWAXIDMAC_CH_CTL_L_NOINC << CH_CTL_L_DST_INC_POS | 63362306a36Sopenharmony_ci DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_SRC_INC_POS; 63462306a36Sopenharmony_ci block_ts = len >> mem_width; 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 63762306a36Sopenharmony_ci reg_width = __ffs(chan->config.src_addr_width); 63862306a36Sopenharmony_ci device_addr = chan->config.src_addr; 63962306a36Sopenharmony_ci ctllo = reg_width << CH_CTL_L_SRC_WIDTH_POS | 64062306a36Sopenharmony_ci mem_width << CH_CTL_L_DST_WIDTH_POS | 64162306a36Sopenharmony_ci DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_DST_INC_POS | 64262306a36Sopenharmony_ci DWAXIDMAC_CH_CTL_L_NOINC << CH_CTL_L_SRC_INC_POS; 64362306a36Sopenharmony_ci block_ts = len >> reg_width; 64462306a36Sopenharmony_ci break; 64562306a36Sopenharmony_ci default: 64662306a36Sopenharmony_ci return -EINVAL; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (block_ts > axi_block_ts) 65062306a36Sopenharmony_ci return -EINVAL; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci hw_desc->lli = axi_desc_get(chan, &hw_desc->llp); 65362306a36Sopenharmony_ci if (unlikely(!hw_desc->lli)) 65462306a36Sopenharmony_ci return -ENOMEM; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci ctlhi = CH_CTL_H_LLI_VALID; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (chan->chip->dw->hdata->restrict_axi_burst_len) { 65962306a36Sopenharmony_ci burst_len = chan->chip->dw->hdata->axi_rw_burst_len; 66062306a36Sopenharmony_ci ctlhi |= CH_CTL_H_ARLEN_EN | CH_CTL_H_AWLEN_EN | 66162306a36Sopenharmony_ci burst_len << CH_CTL_H_ARLEN_POS | 66262306a36Sopenharmony_ci burst_len << CH_CTL_H_AWLEN_POS; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci hw_desc->lli->ctl_hi = cpu_to_le32(ctlhi); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (chan->direction == DMA_MEM_TO_DEV) { 66862306a36Sopenharmony_ci write_desc_sar(hw_desc, mem_addr); 66962306a36Sopenharmony_ci write_desc_dar(hw_desc, device_addr); 67062306a36Sopenharmony_ci } else { 67162306a36Sopenharmony_ci write_desc_sar(hw_desc, device_addr); 67262306a36Sopenharmony_ci write_desc_dar(hw_desc, mem_addr); 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci hw_desc->lli->block_ts_lo = cpu_to_le32(block_ts - 1); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci ctllo |= DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_DST_MSIZE_POS | 67862306a36Sopenharmony_ci DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_SRC_MSIZE_POS; 67962306a36Sopenharmony_ci hw_desc->lli->ctl_lo = cpu_to_le32(ctllo); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci set_desc_src_master(hw_desc); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci hw_desc->len = len; 68462306a36Sopenharmony_ci return 0; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic size_t calculate_block_len(struct axi_dma_chan *chan, 68862306a36Sopenharmony_ci dma_addr_t dma_addr, size_t buf_len, 68962306a36Sopenharmony_ci enum dma_transfer_direction direction) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci u32 data_width, reg_width, mem_width; 69262306a36Sopenharmony_ci size_t axi_block_ts, block_len; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci axi_block_ts = chan->chip->dw->hdata->block_size[chan->id]; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci switch (direction) { 69762306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 69862306a36Sopenharmony_ci data_width = BIT(chan->chip->dw->hdata->m_data_width); 69962306a36Sopenharmony_ci mem_width = __ffs(data_width | dma_addr | buf_len); 70062306a36Sopenharmony_ci if (mem_width > DWAXIDMAC_TRANS_WIDTH_32) 70162306a36Sopenharmony_ci mem_width = DWAXIDMAC_TRANS_WIDTH_32; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci block_len = axi_block_ts << mem_width; 70462306a36Sopenharmony_ci break; 70562306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 70662306a36Sopenharmony_ci reg_width = __ffs(chan->config.src_addr_width); 70762306a36Sopenharmony_ci block_len = axi_block_ts << reg_width; 70862306a36Sopenharmony_ci break; 70962306a36Sopenharmony_ci default: 71062306a36Sopenharmony_ci block_len = 0; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci return block_len; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic struct dma_async_tx_descriptor * 71762306a36Sopenharmony_cidw_axi_dma_chan_prep_cyclic(struct dma_chan *dchan, dma_addr_t dma_addr, 71862306a36Sopenharmony_ci size_t buf_len, size_t period_len, 71962306a36Sopenharmony_ci enum dma_transfer_direction direction, 72062306a36Sopenharmony_ci unsigned long flags) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 72362306a36Sopenharmony_ci struct axi_dma_hw_desc *hw_desc = NULL; 72462306a36Sopenharmony_ci struct axi_dma_desc *desc = NULL; 72562306a36Sopenharmony_ci dma_addr_t src_addr = dma_addr; 72662306a36Sopenharmony_ci u32 num_periods, num_segments; 72762306a36Sopenharmony_ci size_t axi_block_len; 72862306a36Sopenharmony_ci u32 total_segments; 72962306a36Sopenharmony_ci u32 segment_len; 73062306a36Sopenharmony_ci unsigned int i; 73162306a36Sopenharmony_ci int status; 73262306a36Sopenharmony_ci u64 llp = 0; 73362306a36Sopenharmony_ci u8 lms = 0; /* Select AXI0 master for LLI fetching */ 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci num_periods = buf_len / period_len; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci axi_block_len = calculate_block_len(chan, dma_addr, buf_len, direction); 73862306a36Sopenharmony_ci if (axi_block_len == 0) 73962306a36Sopenharmony_ci return NULL; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci num_segments = DIV_ROUND_UP(period_len, axi_block_len); 74262306a36Sopenharmony_ci segment_len = DIV_ROUND_UP(period_len, num_segments); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci total_segments = num_periods * num_segments; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci desc = axi_desc_alloc(total_segments); 74762306a36Sopenharmony_ci if (unlikely(!desc)) 74862306a36Sopenharmony_ci goto err_desc_get; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci chan->direction = direction; 75162306a36Sopenharmony_ci desc->chan = chan; 75262306a36Sopenharmony_ci chan->cyclic = true; 75362306a36Sopenharmony_ci desc->length = 0; 75462306a36Sopenharmony_ci desc->period_len = period_len; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci for (i = 0; i < total_segments; i++) { 75762306a36Sopenharmony_ci hw_desc = &desc->hw_desc[i]; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci status = dw_axi_dma_set_hw_desc(chan, hw_desc, src_addr, 76062306a36Sopenharmony_ci segment_len); 76162306a36Sopenharmony_ci if (status < 0) 76262306a36Sopenharmony_ci goto err_desc_get; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci desc->length += hw_desc->len; 76562306a36Sopenharmony_ci /* Set end-of-link to the linked descriptor, so that cyclic 76662306a36Sopenharmony_ci * callback function can be triggered during interrupt. 76762306a36Sopenharmony_ci */ 76862306a36Sopenharmony_ci set_desc_last(hw_desc); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci src_addr += segment_len; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci llp = desc->hw_desc[0].llp; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* Managed transfer list */ 77662306a36Sopenharmony_ci do { 77762306a36Sopenharmony_ci hw_desc = &desc->hw_desc[--total_segments]; 77862306a36Sopenharmony_ci write_desc_llp(hw_desc, llp | lms); 77962306a36Sopenharmony_ci llp = hw_desc->llp; 78062306a36Sopenharmony_ci } while (total_segments); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci dw_axi_dma_set_hw_channel(chan, true); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci return vchan_tx_prep(&chan->vc, &desc->vd, flags); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cierr_desc_get: 78762306a36Sopenharmony_ci if (desc) 78862306a36Sopenharmony_ci axi_desc_put(desc); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci return NULL; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic struct dma_async_tx_descriptor * 79462306a36Sopenharmony_cidw_axi_dma_chan_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl, 79562306a36Sopenharmony_ci unsigned int sg_len, 79662306a36Sopenharmony_ci enum dma_transfer_direction direction, 79762306a36Sopenharmony_ci unsigned long flags, void *context) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 80062306a36Sopenharmony_ci struct axi_dma_hw_desc *hw_desc = NULL; 80162306a36Sopenharmony_ci struct axi_dma_desc *desc = NULL; 80262306a36Sopenharmony_ci u32 num_segments, segment_len; 80362306a36Sopenharmony_ci unsigned int loop = 0; 80462306a36Sopenharmony_ci struct scatterlist *sg; 80562306a36Sopenharmony_ci size_t axi_block_len; 80662306a36Sopenharmony_ci u32 len, num_sgs = 0; 80762306a36Sopenharmony_ci unsigned int i; 80862306a36Sopenharmony_ci dma_addr_t mem; 80962306a36Sopenharmony_ci int status; 81062306a36Sopenharmony_ci u64 llp = 0; 81162306a36Sopenharmony_ci u8 lms = 0; /* Select AXI0 master for LLI fetching */ 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (unlikely(!is_slave_direction(direction) || !sg_len)) 81462306a36Sopenharmony_ci return NULL; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci mem = sg_dma_address(sgl); 81762306a36Sopenharmony_ci len = sg_dma_len(sgl); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci axi_block_len = calculate_block_len(chan, mem, len, direction); 82062306a36Sopenharmony_ci if (axi_block_len == 0) 82162306a36Sopenharmony_ci return NULL; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci for_each_sg(sgl, sg, sg_len, i) 82462306a36Sopenharmony_ci num_sgs += DIV_ROUND_UP(sg_dma_len(sg), axi_block_len); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci desc = axi_desc_alloc(num_sgs); 82762306a36Sopenharmony_ci if (unlikely(!desc)) 82862306a36Sopenharmony_ci goto err_desc_get; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci desc->chan = chan; 83162306a36Sopenharmony_ci desc->length = 0; 83262306a36Sopenharmony_ci chan->direction = direction; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci for_each_sg(sgl, sg, sg_len, i) { 83562306a36Sopenharmony_ci mem = sg_dma_address(sg); 83662306a36Sopenharmony_ci len = sg_dma_len(sg); 83762306a36Sopenharmony_ci num_segments = DIV_ROUND_UP(sg_dma_len(sg), axi_block_len); 83862306a36Sopenharmony_ci segment_len = DIV_ROUND_UP(sg_dma_len(sg), num_segments); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci do { 84162306a36Sopenharmony_ci hw_desc = &desc->hw_desc[loop++]; 84262306a36Sopenharmony_ci status = dw_axi_dma_set_hw_desc(chan, hw_desc, mem, segment_len); 84362306a36Sopenharmony_ci if (status < 0) 84462306a36Sopenharmony_ci goto err_desc_get; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci desc->length += hw_desc->len; 84762306a36Sopenharmony_ci len -= segment_len; 84862306a36Sopenharmony_ci mem += segment_len; 84962306a36Sopenharmony_ci } while (len >= segment_len); 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* Set end-of-link to the last link descriptor of list */ 85362306a36Sopenharmony_ci set_desc_last(&desc->hw_desc[num_sgs - 1]); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci /* Managed transfer list */ 85662306a36Sopenharmony_ci do { 85762306a36Sopenharmony_ci hw_desc = &desc->hw_desc[--num_sgs]; 85862306a36Sopenharmony_ci write_desc_llp(hw_desc, llp | lms); 85962306a36Sopenharmony_ci llp = hw_desc->llp; 86062306a36Sopenharmony_ci } while (num_sgs); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci dw_axi_dma_set_hw_channel(chan, true); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci return vchan_tx_prep(&chan->vc, &desc->vd, flags); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cierr_desc_get: 86762306a36Sopenharmony_ci if (desc) 86862306a36Sopenharmony_ci axi_desc_put(desc); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci return NULL; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic struct dma_async_tx_descriptor * 87462306a36Sopenharmony_cidma_chan_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dst_adr, 87562306a36Sopenharmony_ci dma_addr_t src_adr, size_t len, unsigned long flags) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 87862306a36Sopenharmony_ci size_t block_ts, max_block_ts, xfer_len; 87962306a36Sopenharmony_ci struct axi_dma_hw_desc *hw_desc = NULL; 88062306a36Sopenharmony_ci struct axi_dma_desc *desc = NULL; 88162306a36Sopenharmony_ci u32 xfer_width, reg, num; 88262306a36Sopenharmony_ci u64 llp = 0; 88362306a36Sopenharmony_ci u8 lms = 0; /* Select AXI0 master for LLI fetching */ 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: memcpy: src: %pad dst: %pad length: %zd flags: %#lx", 88662306a36Sopenharmony_ci axi_chan_name(chan), &src_adr, &dst_adr, len, flags); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci max_block_ts = chan->chip->dw->hdata->block_size[chan->id]; 88962306a36Sopenharmony_ci xfer_width = axi_chan_get_xfer_width(chan, src_adr, dst_adr, len); 89062306a36Sopenharmony_ci num = DIV_ROUND_UP(len, max_block_ts << xfer_width); 89162306a36Sopenharmony_ci desc = axi_desc_alloc(num); 89262306a36Sopenharmony_ci if (unlikely(!desc)) 89362306a36Sopenharmony_ci goto err_desc_get; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci desc->chan = chan; 89662306a36Sopenharmony_ci num = 0; 89762306a36Sopenharmony_ci desc->length = 0; 89862306a36Sopenharmony_ci while (len) { 89962306a36Sopenharmony_ci xfer_len = len; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci hw_desc = &desc->hw_desc[num]; 90262306a36Sopenharmony_ci /* 90362306a36Sopenharmony_ci * Take care for the alignment. 90462306a36Sopenharmony_ci * Actually source and destination widths can be different, but 90562306a36Sopenharmony_ci * make them same to be simpler. 90662306a36Sopenharmony_ci */ 90762306a36Sopenharmony_ci xfer_width = axi_chan_get_xfer_width(chan, src_adr, dst_adr, xfer_len); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci /* 91062306a36Sopenharmony_ci * block_ts indicates the total number of data of width 91162306a36Sopenharmony_ci * to be transferred in a DMA block transfer. 91262306a36Sopenharmony_ci * BLOCK_TS register should be set to block_ts - 1 91362306a36Sopenharmony_ci */ 91462306a36Sopenharmony_ci block_ts = xfer_len >> xfer_width; 91562306a36Sopenharmony_ci if (block_ts > max_block_ts) { 91662306a36Sopenharmony_ci block_ts = max_block_ts; 91762306a36Sopenharmony_ci xfer_len = max_block_ts << xfer_width; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci hw_desc->lli = axi_desc_get(chan, &hw_desc->llp); 92162306a36Sopenharmony_ci if (unlikely(!hw_desc->lli)) 92262306a36Sopenharmony_ci goto err_desc_get; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci write_desc_sar(hw_desc, src_adr); 92562306a36Sopenharmony_ci write_desc_dar(hw_desc, dst_adr); 92662306a36Sopenharmony_ci hw_desc->lli->block_ts_lo = cpu_to_le32(block_ts - 1); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci reg = CH_CTL_H_LLI_VALID; 92962306a36Sopenharmony_ci if (chan->chip->dw->hdata->restrict_axi_burst_len) { 93062306a36Sopenharmony_ci u32 burst_len = chan->chip->dw->hdata->axi_rw_burst_len; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci reg |= (CH_CTL_H_ARLEN_EN | 93362306a36Sopenharmony_ci burst_len << CH_CTL_H_ARLEN_POS | 93462306a36Sopenharmony_ci CH_CTL_H_AWLEN_EN | 93562306a36Sopenharmony_ci burst_len << CH_CTL_H_AWLEN_POS); 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci hw_desc->lli->ctl_hi = cpu_to_le32(reg); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci reg = (DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_DST_MSIZE_POS | 94062306a36Sopenharmony_ci DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_SRC_MSIZE_POS | 94162306a36Sopenharmony_ci xfer_width << CH_CTL_L_DST_WIDTH_POS | 94262306a36Sopenharmony_ci xfer_width << CH_CTL_L_SRC_WIDTH_POS | 94362306a36Sopenharmony_ci DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_DST_INC_POS | 94462306a36Sopenharmony_ci DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_SRC_INC_POS); 94562306a36Sopenharmony_ci hw_desc->lli->ctl_lo = cpu_to_le32(reg); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci set_desc_src_master(hw_desc); 94862306a36Sopenharmony_ci set_desc_dest_master(hw_desc, desc); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci hw_desc->len = xfer_len; 95162306a36Sopenharmony_ci desc->length += hw_desc->len; 95262306a36Sopenharmony_ci /* update the length and addresses for the next loop cycle */ 95362306a36Sopenharmony_ci len -= xfer_len; 95462306a36Sopenharmony_ci dst_adr += xfer_len; 95562306a36Sopenharmony_ci src_adr += xfer_len; 95662306a36Sopenharmony_ci num++; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci /* Set end-of-link to the last link descriptor of list */ 96062306a36Sopenharmony_ci set_desc_last(&desc->hw_desc[num - 1]); 96162306a36Sopenharmony_ci /* Managed transfer list */ 96262306a36Sopenharmony_ci do { 96362306a36Sopenharmony_ci hw_desc = &desc->hw_desc[--num]; 96462306a36Sopenharmony_ci write_desc_llp(hw_desc, llp | lms); 96562306a36Sopenharmony_ci llp = hw_desc->llp; 96662306a36Sopenharmony_ci } while (num); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci return vchan_tx_prep(&chan->vc, &desc->vd, flags); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_cierr_desc_get: 97162306a36Sopenharmony_ci if (desc) 97262306a36Sopenharmony_ci axi_desc_put(desc); 97362306a36Sopenharmony_ci return NULL; 97462306a36Sopenharmony_ci} 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic int dw_axi_dma_chan_slave_config(struct dma_chan *dchan, 97762306a36Sopenharmony_ci struct dma_slave_config *config) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci memcpy(&chan->config, config, sizeof(*config)); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci return 0; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_cistatic void axi_chan_dump_lli(struct axi_dma_chan *chan, 98762306a36Sopenharmony_ci struct axi_dma_hw_desc *desc) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci if (!desc->lli) { 99062306a36Sopenharmony_ci dev_err(dchan2dev(&chan->vc.chan), "NULL LLI\n"); 99162306a36Sopenharmony_ci return; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci dev_err(dchan2dev(&chan->vc.chan), 99562306a36Sopenharmony_ci "SAR: 0x%llx DAR: 0x%llx LLP: 0x%llx BTS 0x%x CTL: 0x%x:%08x", 99662306a36Sopenharmony_ci le64_to_cpu(desc->lli->sar), 99762306a36Sopenharmony_ci le64_to_cpu(desc->lli->dar), 99862306a36Sopenharmony_ci le64_to_cpu(desc->lli->llp), 99962306a36Sopenharmony_ci le32_to_cpu(desc->lli->block_ts_lo), 100062306a36Sopenharmony_ci le32_to_cpu(desc->lli->ctl_hi), 100162306a36Sopenharmony_ci le32_to_cpu(desc->lli->ctl_lo)); 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cistatic void axi_chan_list_dump_lli(struct axi_dma_chan *chan, 100562306a36Sopenharmony_ci struct axi_dma_desc *desc_head) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci int count = atomic_read(&chan->descs_allocated); 100862306a36Sopenharmony_ci int i; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci for (i = 0; i < count; i++) 101162306a36Sopenharmony_ci axi_chan_dump_lli(chan, &desc_head->hw_desc[i]); 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cistatic noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci struct virt_dma_desc *vd; 101762306a36Sopenharmony_ci unsigned long flags; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci axi_chan_disable(chan); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci /* The bad descriptor currently is in the head of vc list */ 102462306a36Sopenharmony_ci vd = vchan_next_desc(&chan->vc); 102562306a36Sopenharmony_ci if (!vd) { 102662306a36Sopenharmony_ci dev_err(chan2dev(chan), "BUG: %s, IRQ with no descriptors\n", 102762306a36Sopenharmony_ci axi_chan_name(chan)); 102862306a36Sopenharmony_ci goto out; 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci /* Remove the completed descriptor from issued list */ 103162306a36Sopenharmony_ci list_del(&vd->node); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci /* WARN about bad descriptor */ 103462306a36Sopenharmony_ci dev_err(chan2dev(chan), 103562306a36Sopenharmony_ci "Bad descriptor submitted for %s, cookie: %d, irq: 0x%08x\n", 103662306a36Sopenharmony_ci axi_chan_name(chan), vd->tx.cookie, status); 103762306a36Sopenharmony_ci axi_chan_list_dump_lli(chan, vd_to_axi_desc(vd)); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci vchan_cookie_complete(vd); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci /* Try to restart the controller */ 104262306a36Sopenharmony_ci axi_chan_start_first_queued(chan); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ciout: 104562306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 104662306a36Sopenharmony_ci} 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_cistatic void axi_chan_block_xfer_complete(struct axi_dma_chan *chan) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci int count = atomic_read(&chan->descs_allocated); 105162306a36Sopenharmony_ci struct axi_dma_hw_desc *hw_desc; 105262306a36Sopenharmony_ci struct axi_dma_desc *desc; 105362306a36Sopenharmony_ci struct virt_dma_desc *vd; 105462306a36Sopenharmony_ci unsigned long flags; 105562306a36Sopenharmony_ci u64 llp; 105662306a36Sopenharmony_ci int i; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 105962306a36Sopenharmony_ci if (unlikely(axi_chan_is_hw_enable(chan))) { 106062306a36Sopenharmony_ci dev_err(chan2dev(chan), "BUG: %s caught DWAXIDMAC_IRQ_DMA_TRF, but channel not idle!\n", 106162306a36Sopenharmony_ci axi_chan_name(chan)); 106262306a36Sopenharmony_ci axi_chan_disable(chan); 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci /* The completed descriptor currently is in the head of vc list */ 106662306a36Sopenharmony_ci vd = vchan_next_desc(&chan->vc); 106762306a36Sopenharmony_ci if (!vd) { 106862306a36Sopenharmony_ci dev_err(chan2dev(chan), "BUG: %s, IRQ with no descriptors\n", 106962306a36Sopenharmony_ci axi_chan_name(chan)); 107062306a36Sopenharmony_ci goto out; 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci if (chan->cyclic) { 107462306a36Sopenharmony_ci desc = vd_to_axi_desc(vd); 107562306a36Sopenharmony_ci if (desc) { 107662306a36Sopenharmony_ci llp = lo_hi_readq(chan->chan_regs + CH_LLP); 107762306a36Sopenharmony_ci for (i = 0; i < count; i++) { 107862306a36Sopenharmony_ci hw_desc = &desc->hw_desc[i]; 107962306a36Sopenharmony_ci if (hw_desc->llp == llp) { 108062306a36Sopenharmony_ci axi_chan_irq_clear(chan, hw_desc->lli->status_lo); 108162306a36Sopenharmony_ci hw_desc->lli->ctl_hi |= CH_CTL_H_LLI_VALID; 108262306a36Sopenharmony_ci desc->completed_blocks = i; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (((hw_desc->len * (i + 1)) % desc->period_len) == 0) 108562306a36Sopenharmony_ci vchan_cyclic_callback(vd); 108662306a36Sopenharmony_ci break; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci axi_chan_enable(chan); 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci } else { 109362306a36Sopenharmony_ci /* Remove the completed descriptor from issued list before completing */ 109462306a36Sopenharmony_ci list_del(&vd->node); 109562306a36Sopenharmony_ci vchan_cookie_complete(vd); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci /* Submit queued descriptors after processing the completed ones */ 109862306a36Sopenharmony_ci axi_chan_start_first_queued(chan); 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ciout: 110262306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic irqreturn_t dw_axi_dma_interrupt(int irq, void *dev_id) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci struct axi_dma_chip *chip = dev_id; 110862306a36Sopenharmony_ci struct dw_axi_dma *dw = chip->dw; 110962306a36Sopenharmony_ci struct axi_dma_chan *chan; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci u32 status, i; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci /* Disable DMAC interrupts. We'll enable them after processing channels */ 111462306a36Sopenharmony_ci axi_dma_irq_disable(chip); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci /* Poll, clear and process every channel interrupt status */ 111762306a36Sopenharmony_ci for (i = 0; i < dw->hdata->nr_channels; i++) { 111862306a36Sopenharmony_ci chan = &dw->chan[i]; 111962306a36Sopenharmony_ci status = axi_chan_irq_read(chan); 112062306a36Sopenharmony_ci axi_chan_irq_clear(chan, status); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci dev_vdbg(chip->dev, "%s %u IRQ status: 0x%08x\n", 112362306a36Sopenharmony_ci axi_chan_name(chan), i, status); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci if (status & DWAXIDMAC_IRQ_ALL_ERR) 112662306a36Sopenharmony_ci axi_chan_handle_err(chan, status); 112762306a36Sopenharmony_ci else if (status & DWAXIDMAC_IRQ_DMA_TRF) 112862306a36Sopenharmony_ci axi_chan_block_xfer_complete(chan); 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci /* Re-enable interrupts */ 113262306a36Sopenharmony_ci axi_dma_irq_enable(chip); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci return IRQ_HANDLED; 113562306a36Sopenharmony_ci} 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_cistatic int dma_chan_terminate_all(struct dma_chan *dchan) 113862306a36Sopenharmony_ci{ 113962306a36Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 114062306a36Sopenharmony_ci u32 chan_active = BIT(chan->id) << DMAC_CHAN_EN_SHIFT; 114162306a36Sopenharmony_ci unsigned long flags; 114262306a36Sopenharmony_ci u32 val; 114362306a36Sopenharmony_ci int ret; 114462306a36Sopenharmony_ci LIST_HEAD(head); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci axi_chan_disable(chan); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val, 114962306a36Sopenharmony_ci !(val & chan_active), 1000, 50000); 115062306a36Sopenharmony_ci if (ret == -ETIMEDOUT) 115162306a36Sopenharmony_ci dev_warn(dchan2dev(dchan), 115262306a36Sopenharmony_ci "%s failed to stop\n", axi_chan_name(chan)); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci if (chan->direction != DMA_MEM_TO_MEM) 115562306a36Sopenharmony_ci dw_axi_dma_set_hw_channel(chan, false); 115662306a36Sopenharmony_ci if (chan->direction == DMA_MEM_TO_DEV) 115762306a36Sopenharmony_ci dw_axi_dma_set_byte_halfword(chan, false); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci vchan_get_all_descriptors(&chan->vc, &head); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci chan->cyclic = false; 116462306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci vchan_dma_desc_free_list(&chan->vc, &head); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci dev_vdbg(dchan2dev(dchan), "terminated: %s\n", axi_chan_name(chan)); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci return 0; 117162306a36Sopenharmony_ci} 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_cistatic int dma_chan_pause(struct dma_chan *dchan) 117462306a36Sopenharmony_ci{ 117562306a36Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 117662306a36Sopenharmony_ci unsigned long flags; 117762306a36Sopenharmony_ci unsigned int timeout = 20; /* timeout iterations */ 117862306a36Sopenharmony_ci u32 val; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci if (chan->chip->dw->hdata->reg_map_8_channels) { 118362306a36Sopenharmony_ci val = axi_dma_ioread32(chan->chip, DMAC_CHEN); 118462306a36Sopenharmony_ci val |= BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT | 118562306a36Sopenharmony_ci BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT; 118662306a36Sopenharmony_ci axi_dma_iowrite32(chan->chip, DMAC_CHEN, val); 118762306a36Sopenharmony_ci } else { 118862306a36Sopenharmony_ci val = axi_dma_ioread32(chan->chip, DMAC_CHSUSPREG); 118962306a36Sopenharmony_ci val |= BIT(chan->id) << DMAC_CHAN_SUSP2_SHIFT | 119062306a36Sopenharmony_ci BIT(chan->id) << DMAC_CHAN_SUSP2_WE_SHIFT; 119162306a36Sopenharmony_ci axi_dma_iowrite32(chan->chip, DMAC_CHSUSPREG, val); 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci do { 119562306a36Sopenharmony_ci if (axi_chan_irq_read(chan) & DWAXIDMAC_IRQ_SUSPENDED) 119662306a36Sopenharmony_ci break; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci udelay(2); 119962306a36Sopenharmony_ci } while (--timeout); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci axi_chan_irq_clear(chan, DWAXIDMAC_IRQ_SUSPENDED); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci chan->is_paused = true; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci return timeout ? 0 : -EAGAIN; 120862306a36Sopenharmony_ci} 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci/* Called in chan locked context */ 121162306a36Sopenharmony_cistatic inline void axi_chan_resume(struct axi_dma_chan *chan) 121262306a36Sopenharmony_ci{ 121362306a36Sopenharmony_ci u32 val; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci if (chan->chip->dw->hdata->reg_map_8_channels) { 121662306a36Sopenharmony_ci val = axi_dma_ioread32(chan->chip, DMAC_CHEN); 121762306a36Sopenharmony_ci val &= ~(BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT); 121862306a36Sopenharmony_ci val |= (BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT); 121962306a36Sopenharmony_ci axi_dma_iowrite32(chan->chip, DMAC_CHEN, val); 122062306a36Sopenharmony_ci } else { 122162306a36Sopenharmony_ci val = axi_dma_ioread32(chan->chip, DMAC_CHSUSPREG); 122262306a36Sopenharmony_ci val &= ~(BIT(chan->id) << DMAC_CHAN_SUSP2_SHIFT); 122362306a36Sopenharmony_ci val |= (BIT(chan->id) << DMAC_CHAN_SUSP2_WE_SHIFT); 122462306a36Sopenharmony_ci axi_dma_iowrite32(chan->chip, DMAC_CHSUSPREG, val); 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci chan->is_paused = false; 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic int dma_chan_resume(struct dma_chan *dchan) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); 123362306a36Sopenharmony_ci unsigned long flags; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci spin_lock_irqsave(&chan->vc.lock, flags); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci if (chan->is_paused) 123862306a36Sopenharmony_ci axi_chan_resume(chan); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->vc.lock, flags); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci return 0; 124362306a36Sopenharmony_ci} 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_cistatic int axi_dma_suspend(struct axi_dma_chip *chip) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci axi_dma_irq_disable(chip); 124862306a36Sopenharmony_ci axi_dma_disable(chip); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci clk_disable_unprepare(chip->core_clk); 125162306a36Sopenharmony_ci clk_disable_unprepare(chip->cfgr_clk); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci return 0; 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_cistatic int axi_dma_resume(struct axi_dma_chip *chip) 125762306a36Sopenharmony_ci{ 125862306a36Sopenharmony_ci int ret; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci ret = clk_prepare_enable(chip->cfgr_clk); 126162306a36Sopenharmony_ci if (ret < 0) 126262306a36Sopenharmony_ci return ret; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci ret = clk_prepare_enable(chip->core_clk); 126562306a36Sopenharmony_ci if (ret < 0) 126662306a36Sopenharmony_ci return ret; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci axi_dma_enable(chip); 126962306a36Sopenharmony_ci axi_dma_irq_enable(chip); 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci return 0; 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic int __maybe_unused axi_dma_runtime_suspend(struct device *dev) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci struct axi_dma_chip *chip = dev_get_drvdata(dev); 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci return axi_dma_suspend(chip); 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic int __maybe_unused axi_dma_runtime_resume(struct device *dev) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci struct axi_dma_chip *chip = dev_get_drvdata(dev); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci return axi_dma_resume(chip); 128662306a36Sopenharmony_ci} 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_cistatic struct dma_chan *dw_axi_dma_of_xlate(struct of_phandle_args *dma_spec, 128962306a36Sopenharmony_ci struct of_dma *ofdma) 129062306a36Sopenharmony_ci{ 129162306a36Sopenharmony_ci struct dw_axi_dma *dw = ofdma->of_dma_data; 129262306a36Sopenharmony_ci struct axi_dma_chan *chan; 129362306a36Sopenharmony_ci struct dma_chan *dchan; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci dchan = dma_get_any_slave_channel(&dw->dma); 129662306a36Sopenharmony_ci if (!dchan) 129762306a36Sopenharmony_ci return NULL; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci chan = dchan_to_axi_dma_chan(dchan); 130062306a36Sopenharmony_ci chan->hw_handshake_num = dma_spec->args[0]; 130162306a36Sopenharmony_ci return dchan; 130262306a36Sopenharmony_ci} 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_cistatic int parse_device_properties(struct axi_dma_chip *chip) 130562306a36Sopenharmony_ci{ 130662306a36Sopenharmony_ci struct device *dev = chip->dev; 130762306a36Sopenharmony_ci u32 tmp, carr[DMAC_MAX_CHANNELS]; 130862306a36Sopenharmony_ci int ret; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci ret = device_property_read_u32(dev, "dma-channels", &tmp); 131162306a36Sopenharmony_ci if (ret) 131262306a36Sopenharmony_ci return ret; 131362306a36Sopenharmony_ci if (tmp == 0 || tmp > DMAC_MAX_CHANNELS) 131462306a36Sopenharmony_ci return -EINVAL; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci chip->dw->hdata->nr_channels = tmp; 131762306a36Sopenharmony_ci if (tmp <= DMA_REG_MAP_CH_REF) 131862306a36Sopenharmony_ci chip->dw->hdata->reg_map_8_channels = true; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci ret = device_property_read_u32(dev, "snps,dma-masters", &tmp); 132162306a36Sopenharmony_ci if (ret) 132262306a36Sopenharmony_ci return ret; 132362306a36Sopenharmony_ci if (tmp == 0 || tmp > DMAC_MAX_MASTERS) 132462306a36Sopenharmony_ci return -EINVAL; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci chip->dw->hdata->nr_masters = tmp; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci ret = device_property_read_u32(dev, "snps,data-width", &tmp); 132962306a36Sopenharmony_ci if (ret) 133062306a36Sopenharmony_ci return ret; 133162306a36Sopenharmony_ci if (tmp > DWAXIDMAC_TRANS_WIDTH_MAX) 133262306a36Sopenharmony_ci return -EINVAL; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci chip->dw->hdata->m_data_width = tmp; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci ret = device_property_read_u32_array(dev, "snps,block-size", carr, 133762306a36Sopenharmony_ci chip->dw->hdata->nr_channels); 133862306a36Sopenharmony_ci if (ret) 133962306a36Sopenharmony_ci return ret; 134062306a36Sopenharmony_ci for (tmp = 0; tmp < chip->dw->hdata->nr_channels; tmp++) { 134162306a36Sopenharmony_ci if (carr[tmp] == 0 || carr[tmp] > DMAC_MAX_BLK_SIZE) 134262306a36Sopenharmony_ci return -EINVAL; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci chip->dw->hdata->block_size[tmp] = carr[tmp]; 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci ret = device_property_read_u32_array(dev, "snps,priority", carr, 134862306a36Sopenharmony_ci chip->dw->hdata->nr_channels); 134962306a36Sopenharmony_ci if (ret) 135062306a36Sopenharmony_ci return ret; 135162306a36Sopenharmony_ci /* Priority value must be programmed within [0:nr_channels-1] range */ 135262306a36Sopenharmony_ci for (tmp = 0; tmp < chip->dw->hdata->nr_channels; tmp++) { 135362306a36Sopenharmony_ci if (carr[tmp] >= chip->dw->hdata->nr_channels) 135462306a36Sopenharmony_ci return -EINVAL; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci chip->dw->hdata->priority[tmp] = carr[tmp]; 135762306a36Sopenharmony_ci } 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci /* axi-max-burst-len is optional property */ 136062306a36Sopenharmony_ci ret = device_property_read_u32(dev, "snps,axi-max-burst-len", &tmp); 136162306a36Sopenharmony_ci if (!ret) { 136262306a36Sopenharmony_ci if (tmp > DWAXIDMAC_ARWLEN_MAX + 1) 136362306a36Sopenharmony_ci return -EINVAL; 136462306a36Sopenharmony_ci if (tmp < DWAXIDMAC_ARWLEN_MIN + 1) 136562306a36Sopenharmony_ci return -EINVAL; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci chip->dw->hdata->restrict_axi_burst_len = true; 136862306a36Sopenharmony_ci chip->dw->hdata->axi_rw_burst_len = tmp; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci return 0; 137262306a36Sopenharmony_ci} 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_cistatic int dw_probe(struct platform_device *pdev) 137562306a36Sopenharmony_ci{ 137662306a36Sopenharmony_ci struct axi_dma_chip *chip; 137762306a36Sopenharmony_ci struct dw_axi_dma *dw; 137862306a36Sopenharmony_ci struct dw_axi_dma_hcfg *hdata; 137962306a36Sopenharmony_ci struct reset_control *resets; 138062306a36Sopenharmony_ci unsigned int flags; 138162306a36Sopenharmony_ci u32 i; 138262306a36Sopenharmony_ci int ret; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); 138562306a36Sopenharmony_ci if (!chip) 138662306a36Sopenharmony_ci return -ENOMEM; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL); 138962306a36Sopenharmony_ci if (!dw) 139062306a36Sopenharmony_ci return -ENOMEM; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci hdata = devm_kzalloc(&pdev->dev, sizeof(*hdata), GFP_KERNEL); 139362306a36Sopenharmony_ci if (!hdata) 139462306a36Sopenharmony_ci return -ENOMEM; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci chip->dw = dw; 139762306a36Sopenharmony_ci chip->dev = &pdev->dev; 139862306a36Sopenharmony_ci chip->dw->hdata = hdata; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci chip->irq = platform_get_irq(pdev, 0); 140162306a36Sopenharmony_ci if (chip->irq < 0) 140262306a36Sopenharmony_ci return chip->irq; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci chip->regs = devm_platform_ioremap_resource(pdev, 0); 140562306a36Sopenharmony_ci if (IS_ERR(chip->regs)) 140662306a36Sopenharmony_ci return PTR_ERR(chip->regs); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci flags = (uintptr_t)of_device_get_match_data(&pdev->dev); 140962306a36Sopenharmony_ci if (flags & AXI_DMA_FLAG_HAS_APB_REGS) { 141062306a36Sopenharmony_ci chip->apb_regs = devm_platform_ioremap_resource(pdev, 1); 141162306a36Sopenharmony_ci if (IS_ERR(chip->apb_regs)) 141262306a36Sopenharmony_ci return PTR_ERR(chip->apb_regs); 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci if (flags & AXI_DMA_FLAG_HAS_RESETS) { 141662306a36Sopenharmony_ci resets = devm_reset_control_array_get_exclusive(&pdev->dev); 141762306a36Sopenharmony_ci if (IS_ERR(resets)) 141862306a36Sopenharmony_ci return PTR_ERR(resets); 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci ret = reset_control_deassert(resets); 142162306a36Sopenharmony_ci if (ret) 142262306a36Sopenharmony_ci return ret; 142362306a36Sopenharmony_ci } 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci chip->dw->hdata->use_cfg2 = !!(flags & AXI_DMA_FLAG_USE_CFG2); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci chip->core_clk = devm_clk_get(chip->dev, "core-clk"); 142862306a36Sopenharmony_ci if (IS_ERR(chip->core_clk)) 142962306a36Sopenharmony_ci return PTR_ERR(chip->core_clk); 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci chip->cfgr_clk = devm_clk_get(chip->dev, "cfgr-clk"); 143262306a36Sopenharmony_ci if (IS_ERR(chip->cfgr_clk)) 143362306a36Sopenharmony_ci return PTR_ERR(chip->cfgr_clk); 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci ret = parse_device_properties(chip); 143662306a36Sopenharmony_ci if (ret) 143762306a36Sopenharmony_ci return ret; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci dw->chan = devm_kcalloc(chip->dev, hdata->nr_channels, 144062306a36Sopenharmony_ci sizeof(*dw->chan), GFP_KERNEL); 144162306a36Sopenharmony_ci if (!dw->chan) 144262306a36Sopenharmony_ci return -ENOMEM; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci ret = devm_request_irq(chip->dev, chip->irq, dw_axi_dma_interrupt, 144562306a36Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, chip); 144662306a36Sopenharmony_ci if (ret) 144762306a36Sopenharmony_ci return ret; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci INIT_LIST_HEAD(&dw->dma.channels); 145062306a36Sopenharmony_ci for (i = 0; i < hdata->nr_channels; i++) { 145162306a36Sopenharmony_ci struct axi_dma_chan *chan = &dw->chan[i]; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci chan->chip = chip; 145462306a36Sopenharmony_ci chan->id = i; 145562306a36Sopenharmony_ci chan->chan_regs = chip->regs + COMMON_REG_LEN + i * CHAN_REG_LEN; 145662306a36Sopenharmony_ci atomic_set(&chan->descs_allocated, 0); 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci chan->vc.desc_free = vchan_desc_put; 145962306a36Sopenharmony_ci vchan_init(&chan->vc, &dw->dma); 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci /* Set capabilities */ 146362306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); 146462306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); 146562306a36Sopenharmony_ci dma_cap_set(DMA_CYCLIC, dw->dma.cap_mask); 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci /* DMA capabilities */ 146862306a36Sopenharmony_ci dw->dma.max_burst = hdata->axi_rw_burst_len; 146962306a36Sopenharmony_ci dw->dma.src_addr_widths = AXI_DMA_BUSWIDTHS; 147062306a36Sopenharmony_ci dw->dma.dst_addr_widths = AXI_DMA_BUSWIDTHS; 147162306a36Sopenharmony_ci dw->dma.directions = BIT(DMA_MEM_TO_MEM); 147262306a36Sopenharmony_ci dw->dma.directions |= BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); 147362306a36Sopenharmony_ci dw->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci dw->dma.dev = chip->dev; 147662306a36Sopenharmony_ci dw->dma.device_tx_status = dma_chan_tx_status; 147762306a36Sopenharmony_ci dw->dma.device_issue_pending = dma_chan_issue_pending; 147862306a36Sopenharmony_ci dw->dma.device_terminate_all = dma_chan_terminate_all; 147962306a36Sopenharmony_ci dw->dma.device_pause = dma_chan_pause; 148062306a36Sopenharmony_ci dw->dma.device_resume = dma_chan_resume; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci dw->dma.device_alloc_chan_resources = dma_chan_alloc_chan_resources; 148362306a36Sopenharmony_ci dw->dma.device_free_chan_resources = dma_chan_free_chan_resources; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci dw->dma.device_prep_dma_memcpy = dma_chan_prep_dma_memcpy; 148662306a36Sopenharmony_ci dw->dma.device_synchronize = dw_axi_dma_synchronize; 148762306a36Sopenharmony_ci dw->dma.device_config = dw_axi_dma_chan_slave_config; 148862306a36Sopenharmony_ci dw->dma.device_prep_slave_sg = dw_axi_dma_chan_prep_slave_sg; 148962306a36Sopenharmony_ci dw->dma.device_prep_dma_cyclic = dw_axi_dma_chan_prep_cyclic; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci /* 149262306a36Sopenharmony_ci * Synopsis DesignWare AxiDMA datasheet mentioned Maximum 149362306a36Sopenharmony_ci * supported blocks is 1024. Device register width is 4 bytes. 149462306a36Sopenharmony_ci * Therefore, set constraint to 1024 * 4. 149562306a36Sopenharmony_ci */ 149662306a36Sopenharmony_ci dw->dma.dev->dma_parms = &dw->dma_parms; 149762306a36Sopenharmony_ci dma_set_max_seg_size(&pdev->dev, MAX_BLOCK_SIZE); 149862306a36Sopenharmony_ci platform_set_drvdata(pdev, chip); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci pm_runtime_enable(chip->dev); 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci /* 150362306a36Sopenharmony_ci * We can't just call pm_runtime_get here instead of 150462306a36Sopenharmony_ci * pm_runtime_get_noresume + axi_dma_resume because we need 150562306a36Sopenharmony_ci * driver to work also without Runtime PM. 150662306a36Sopenharmony_ci */ 150762306a36Sopenharmony_ci pm_runtime_get_noresume(chip->dev); 150862306a36Sopenharmony_ci ret = axi_dma_resume(chip); 150962306a36Sopenharmony_ci if (ret < 0) 151062306a36Sopenharmony_ci goto err_pm_disable; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci axi_dma_hw_init(chip); 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci pm_runtime_put(chip->dev); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci ret = dmaenginem_async_device_register(&dw->dma); 151762306a36Sopenharmony_ci if (ret) 151862306a36Sopenharmony_ci goto err_pm_disable; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci /* Register with OF helpers for DMA lookups */ 152162306a36Sopenharmony_ci ret = of_dma_controller_register(pdev->dev.of_node, 152262306a36Sopenharmony_ci dw_axi_dma_of_xlate, dw); 152362306a36Sopenharmony_ci if (ret < 0) 152462306a36Sopenharmony_ci dev_warn(&pdev->dev, 152562306a36Sopenharmony_ci "Failed to register OF DMA controller, fallback to MEM_TO_MEM mode\n"); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci dev_info(chip->dev, "DesignWare AXI DMA Controller, %d channels\n", 152862306a36Sopenharmony_ci dw->hdata->nr_channels); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci return 0; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_cierr_pm_disable: 153362306a36Sopenharmony_ci pm_runtime_disable(chip->dev); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci return ret; 153662306a36Sopenharmony_ci} 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_cistatic int dw_remove(struct platform_device *pdev) 153962306a36Sopenharmony_ci{ 154062306a36Sopenharmony_ci struct axi_dma_chip *chip = platform_get_drvdata(pdev); 154162306a36Sopenharmony_ci struct dw_axi_dma *dw = chip->dw; 154262306a36Sopenharmony_ci struct axi_dma_chan *chan, *_chan; 154362306a36Sopenharmony_ci u32 i; 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci /* Enable clk before accessing to registers */ 154662306a36Sopenharmony_ci clk_prepare_enable(chip->cfgr_clk); 154762306a36Sopenharmony_ci clk_prepare_enable(chip->core_clk); 154862306a36Sopenharmony_ci axi_dma_irq_disable(chip); 154962306a36Sopenharmony_ci for (i = 0; i < dw->hdata->nr_channels; i++) { 155062306a36Sopenharmony_ci axi_chan_disable(&chip->dw->chan[i]); 155162306a36Sopenharmony_ci axi_chan_irq_disable(&chip->dw->chan[i], DWAXIDMAC_IRQ_ALL); 155262306a36Sopenharmony_ci } 155362306a36Sopenharmony_ci axi_dma_disable(chip); 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci pm_runtime_disable(chip->dev); 155662306a36Sopenharmony_ci axi_dma_suspend(chip); 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci devm_free_irq(chip->dev, chip->irq, chip); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci of_dma_controller_free(chip->dev->of_node); 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci list_for_each_entry_safe(chan, _chan, &dw->dma.channels, 156362306a36Sopenharmony_ci vc.chan.device_node) { 156462306a36Sopenharmony_ci list_del(&chan->vc.chan.device_node); 156562306a36Sopenharmony_ci tasklet_kill(&chan->vc.task); 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci return 0; 156962306a36Sopenharmony_ci} 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_cistatic const struct dev_pm_ops dw_axi_dma_pm_ops = { 157262306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL) 157362306a36Sopenharmony_ci}; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_cistatic const struct of_device_id dw_dma_of_id_table[] = { 157662306a36Sopenharmony_ci { 157762306a36Sopenharmony_ci .compatible = "snps,axi-dma-1.01a" 157862306a36Sopenharmony_ci }, { 157962306a36Sopenharmony_ci .compatible = "intel,kmb-axi-dma", 158062306a36Sopenharmony_ci .data = (void *)AXI_DMA_FLAG_HAS_APB_REGS, 158162306a36Sopenharmony_ci }, { 158262306a36Sopenharmony_ci .compatible = "starfive,jh7110-axi-dma", 158362306a36Sopenharmony_ci .data = (void *)(AXI_DMA_FLAG_HAS_RESETS | AXI_DMA_FLAG_USE_CFG2), 158462306a36Sopenharmony_ci }, 158562306a36Sopenharmony_ci {} 158662306a36Sopenharmony_ci}; 158762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, dw_dma_of_id_table); 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_cistatic struct platform_driver dw_driver = { 159062306a36Sopenharmony_ci .probe = dw_probe, 159162306a36Sopenharmony_ci .remove = dw_remove, 159262306a36Sopenharmony_ci .driver = { 159362306a36Sopenharmony_ci .name = KBUILD_MODNAME, 159462306a36Sopenharmony_ci .of_match_table = dw_dma_of_id_table, 159562306a36Sopenharmony_ci .pm = &dw_axi_dma_pm_ops, 159662306a36Sopenharmony_ci }, 159762306a36Sopenharmony_ci}; 159862306a36Sopenharmony_cimodule_platform_driver(dw_driver); 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 160162306a36Sopenharmony_ciMODULE_DESCRIPTION("Synopsys DesignWare AXI DMA Controller platform driver"); 160262306a36Sopenharmony_ciMODULE_AUTHOR("Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>"); 1603