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