162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
462306a36Sopenharmony_ci * Synopsys DesignWare eDMA v0 core
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/bitfield.h>
1062306a36Sopenharmony_ci#include <linux/irqreturn.h>
1162306a36Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "dw-edma-core.h"
1462306a36Sopenharmony_ci#include "dw-edma-v0-core.h"
1562306a36Sopenharmony_ci#include "dw-edma-v0-regs.h"
1662306a36Sopenharmony_ci#include "dw-edma-v0-debugfs.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cienum dw_edma_control {
1962306a36Sopenharmony_ci	DW_EDMA_V0_CB					= BIT(0),
2062306a36Sopenharmony_ci	DW_EDMA_V0_TCB					= BIT(1),
2162306a36Sopenharmony_ci	DW_EDMA_V0_LLP					= BIT(2),
2262306a36Sopenharmony_ci	DW_EDMA_V0_LIE					= BIT(3),
2362306a36Sopenharmony_ci	DW_EDMA_V0_RIE					= BIT(4),
2462306a36Sopenharmony_ci	DW_EDMA_V0_CCS					= BIT(8),
2562306a36Sopenharmony_ci	DW_EDMA_V0_LLE					= BIT(9),
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic inline struct dw_edma_v0_regs __iomem *__dw_regs(struct dw_edma *dw)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	return dw->chip->reg_base;
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define SET_32(dw, name, value)				\
3462306a36Sopenharmony_ci	writel(value, &(__dw_regs(dw)->name))
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define GET_32(dw, name)				\
3762306a36Sopenharmony_ci	readl(&(__dw_regs(dw)->name))
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define SET_RW_32(dw, dir, name, value)			\
4062306a36Sopenharmony_ci	do {						\
4162306a36Sopenharmony_ci		if ((dir) == EDMA_DIR_WRITE)		\
4262306a36Sopenharmony_ci			SET_32(dw, wr_##name, value);	\
4362306a36Sopenharmony_ci		else					\
4462306a36Sopenharmony_ci			SET_32(dw, rd_##name, value);	\
4562306a36Sopenharmony_ci	} while (0)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define GET_RW_32(dw, dir, name)			\
4862306a36Sopenharmony_ci	((dir) == EDMA_DIR_WRITE			\
4962306a36Sopenharmony_ci	  ? GET_32(dw, wr_##name)			\
5062306a36Sopenharmony_ci	  : GET_32(dw, rd_##name))
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define SET_BOTH_32(dw, name, value)			\
5362306a36Sopenharmony_ci	do {						\
5462306a36Sopenharmony_ci		SET_32(dw, wr_##name, value);		\
5562306a36Sopenharmony_ci		SET_32(dw, rd_##name, value);		\
5662306a36Sopenharmony_ci	} while (0)
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define SET_64(dw, name, value)				\
5962306a36Sopenharmony_ci	writeq(value, &(__dw_regs(dw)->name))
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define GET_64(dw, name)				\
6262306a36Sopenharmony_ci	readq(&(__dw_regs(dw)->name))
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define SET_RW_64(dw, dir, name, value)			\
6562306a36Sopenharmony_ci	do {						\
6662306a36Sopenharmony_ci		if ((dir) == EDMA_DIR_WRITE)		\
6762306a36Sopenharmony_ci			SET_64(dw, wr_##name, value);	\
6862306a36Sopenharmony_ci		else					\
6962306a36Sopenharmony_ci			SET_64(dw, rd_##name, value);	\
7062306a36Sopenharmony_ci	} while (0)
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#define GET_RW_64(dw, dir, name)			\
7362306a36Sopenharmony_ci	((dir) == EDMA_DIR_WRITE			\
7462306a36Sopenharmony_ci	  ? GET_64(dw, wr_##name)			\
7562306a36Sopenharmony_ci	  : GET_64(dw, rd_##name))
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define SET_BOTH_64(dw, name, value)			\
7862306a36Sopenharmony_ci	do {						\
7962306a36Sopenharmony_ci		SET_64(dw, wr_##name, value);		\
8062306a36Sopenharmony_ci		SET_64(dw, rd_##name, value);		\
8162306a36Sopenharmony_ci	} while (0)
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define SET_COMPAT(dw, name, value)			\
8462306a36Sopenharmony_ci	writel(value, &(__dw_regs(dw)->type.unroll.name))
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define SET_RW_COMPAT(dw, dir, name, value)		\
8762306a36Sopenharmony_ci	do {						\
8862306a36Sopenharmony_ci		if ((dir) == EDMA_DIR_WRITE)		\
8962306a36Sopenharmony_ci			SET_COMPAT(dw, wr_##name, value); \
9062306a36Sopenharmony_ci		else					\
9162306a36Sopenharmony_ci			SET_COMPAT(dw, rd_##name, value); \
9262306a36Sopenharmony_ci	} while (0)
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic inline struct dw_edma_v0_ch_regs __iomem *
9562306a36Sopenharmony_ci__dw_ch_regs(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	if (dw->chip->mf == EDMA_MF_EDMA_LEGACY)
9862306a36Sopenharmony_ci		return &(__dw_regs(dw)->type.legacy.ch);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (dir == EDMA_DIR_WRITE)
10162306a36Sopenharmony_ci		return &__dw_regs(dw)->type.unroll.ch[ch].wr;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return &__dw_regs(dw)->type.unroll.ch[ch].rd;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic inline void writel_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
10762306a36Sopenharmony_ci			     u32 value, void __iomem *addr)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) {
11062306a36Sopenharmony_ci		u32 viewport_sel;
11162306a36Sopenharmony_ci		unsigned long flags;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci		raw_spin_lock_irqsave(&dw->lock, flags);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		viewport_sel = FIELD_PREP(EDMA_V0_VIEWPORT_MASK, ch);
11662306a36Sopenharmony_ci		if (dir == EDMA_DIR_READ)
11762306a36Sopenharmony_ci			viewport_sel |= BIT(31);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		writel(viewport_sel,
12062306a36Sopenharmony_ci		       &(__dw_regs(dw)->type.legacy.viewport_sel));
12162306a36Sopenharmony_ci		writel(value, addr);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&dw->lock, flags);
12462306a36Sopenharmony_ci	} else {
12562306a36Sopenharmony_ci		writel(value, addr);
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic inline u32 readl_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
13062306a36Sopenharmony_ci			   const void __iomem *addr)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	u32 value;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) {
13562306a36Sopenharmony_ci		u32 viewport_sel;
13662306a36Sopenharmony_ci		unsigned long flags;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		raw_spin_lock_irqsave(&dw->lock, flags);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		viewport_sel = FIELD_PREP(EDMA_V0_VIEWPORT_MASK, ch);
14162306a36Sopenharmony_ci		if (dir == EDMA_DIR_READ)
14262306a36Sopenharmony_ci			viewport_sel |= BIT(31);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci		writel(viewport_sel,
14562306a36Sopenharmony_ci		       &(__dw_regs(dw)->type.legacy.viewport_sel));
14662306a36Sopenharmony_ci		value = readl(addr);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&dw->lock, flags);
14962306a36Sopenharmony_ci	} else {
15062306a36Sopenharmony_ci		value = readl(addr);
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return value;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci#define SET_CH_32(dw, dir, ch, name, value) \
15762306a36Sopenharmony_ci	writel_ch(dw, dir, ch, value, &(__dw_ch_regs(dw, dir, ch)->name))
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#define GET_CH_32(dw, dir, ch, name) \
16062306a36Sopenharmony_ci	readl_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name))
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/* eDMA management callbacks */
16362306a36Sopenharmony_cistatic void dw_edma_v0_core_off(struct dw_edma *dw)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	SET_BOTH_32(dw, int_mask,
16662306a36Sopenharmony_ci		    EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK);
16762306a36Sopenharmony_ci	SET_BOTH_32(dw, int_clear,
16862306a36Sopenharmony_ci		    EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK);
16962306a36Sopenharmony_ci	SET_BOTH_32(dw, engine_en, 0);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	u32 num_ch;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (dir == EDMA_DIR_WRITE)
17762306a36Sopenharmony_ci		num_ch = FIELD_GET(EDMA_V0_WRITE_CH_COUNT_MASK,
17862306a36Sopenharmony_ci				   GET_32(dw, ctrl));
17962306a36Sopenharmony_ci	else
18062306a36Sopenharmony_ci		num_ch = FIELD_GET(EDMA_V0_READ_CH_COUNT_MASK,
18162306a36Sopenharmony_ci				   GET_32(dw, ctrl));
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (num_ch > EDMA_V0_MAX_NR_CH)
18462306a36Sopenharmony_ci		num_ch = EDMA_V0_MAX_NR_CH;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return (u16)num_ch;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct dw_edma *dw = chan->dw;
19262306a36Sopenharmony_ci	u32 tmp;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	tmp = FIELD_GET(EDMA_V0_CH_STATUS_MASK,
19562306a36Sopenharmony_ci			GET_CH_32(dw, chan->dir, chan->id, ch_control1));
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (tmp == 1)
19862306a36Sopenharmony_ci		return DMA_IN_PROGRESS;
19962306a36Sopenharmony_ci	else if (tmp == 3)
20062306a36Sopenharmony_ci		return DMA_COMPLETE;
20162306a36Sopenharmony_ci	else
20262306a36Sopenharmony_ci		return DMA_ERROR;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct dw_edma *dw = chan->dw;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	SET_RW_32(dw, chan->dir, int_clear,
21062306a36Sopenharmony_ci		  FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)));
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct dw_edma *dw = chan->dw;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	SET_RW_32(dw, chan->dir, int_clear,
21862306a36Sopenharmony_ci		  FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)));
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	return FIELD_GET(EDMA_V0_DONE_INT_MASK,
22462306a36Sopenharmony_ci			 GET_RW_32(dw, dir, int_status));
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	return FIELD_GET(EDMA_V0_ABORT_INT_MASK,
23062306a36Sopenharmony_ci			 GET_RW_32(dw, dir, int_status));
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic irqreturn_t
23462306a36Sopenharmony_cidw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
23562306a36Sopenharmony_ci			   dw_edma_handler_t done, dw_edma_handler_t abort)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct dw_edma *dw = dw_irq->dw;
23862306a36Sopenharmony_ci	unsigned long total, pos, val;
23962306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
24062306a36Sopenharmony_ci	struct dw_edma_chan *chan;
24162306a36Sopenharmony_ci	unsigned long off;
24262306a36Sopenharmony_ci	u32 mask;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (dir == EDMA_DIR_WRITE) {
24562306a36Sopenharmony_ci		total = dw->wr_ch_cnt;
24662306a36Sopenharmony_ci		off = 0;
24762306a36Sopenharmony_ci		mask = dw_irq->wr_mask;
24862306a36Sopenharmony_ci	} else {
24962306a36Sopenharmony_ci		total = dw->rd_ch_cnt;
25062306a36Sopenharmony_ci		off = dw->wr_ch_cnt;
25162306a36Sopenharmony_ci		mask = dw_irq->rd_mask;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	val = dw_edma_v0_core_status_done_int(dw, dir);
25562306a36Sopenharmony_ci	val &= mask;
25662306a36Sopenharmony_ci	for_each_set_bit(pos, &val, total) {
25762306a36Sopenharmony_ci		chan = &dw->chan[pos + off];
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		dw_edma_v0_core_clear_done_int(chan);
26062306a36Sopenharmony_ci		done(chan);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		ret = IRQ_HANDLED;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	val = dw_edma_v0_core_status_abort_int(dw, dir);
26662306a36Sopenharmony_ci	val &= mask;
26762306a36Sopenharmony_ci	for_each_set_bit(pos, &val, total) {
26862306a36Sopenharmony_ci		chan = &dw->chan[pos + off];
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		dw_edma_v0_core_clear_abort_int(chan);
27162306a36Sopenharmony_ci		abort(chan);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		ret = IRQ_HANDLED;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return ret;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
28062306a36Sopenharmony_ci				     u32 control, u32 size, u64 sar, u64 dar)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	ptrdiff_t ofs = i * sizeof(struct dw_edma_v0_lli);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
28562306a36Sopenharmony_ci		struct dw_edma_v0_lli *lli = chunk->ll_region.vaddr.mem + ofs;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci		lli->control = control;
28862306a36Sopenharmony_ci		lli->transfer_size = size;
28962306a36Sopenharmony_ci		lli->sar.reg = sar;
29062306a36Sopenharmony_ci		lli->dar.reg = dar;
29162306a36Sopenharmony_ci	} else {
29262306a36Sopenharmony_ci		struct dw_edma_v0_lli __iomem *lli = chunk->ll_region.vaddr.io + ofs;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		writel(control, &lli->control);
29562306a36Sopenharmony_ci		writel(size, &lli->transfer_size);
29662306a36Sopenharmony_ci		writeq(sar, &lli->sar.reg);
29762306a36Sopenharmony_ci		writeq(dar, &lli->dar.reg);
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic void dw_edma_v0_write_ll_link(struct dw_edma_chunk *chunk,
30262306a36Sopenharmony_ci				     int i, u32 control, u64 pointer)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	ptrdiff_t ofs = i * sizeof(struct dw_edma_v0_lli);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
30762306a36Sopenharmony_ci		struct dw_edma_v0_llp *llp = chunk->ll_region.vaddr.mem + ofs;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		llp->control = control;
31062306a36Sopenharmony_ci		llp->llp.reg = pointer;
31162306a36Sopenharmony_ci	} else {
31262306a36Sopenharmony_ci		struct dw_edma_v0_llp __iomem *llp = chunk->ll_region.vaddr.io + ofs;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		writel(control, &llp->control);
31562306a36Sopenharmony_ci		writeq(pointer, &llp->llp.reg);
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct dw_edma_burst *child;
32262306a36Sopenharmony_ci	struct dw_edma_chan *chan = chunk->chan;
32362306a36Sopenharmony_ci	u32 control = 0, i = 0;
32462306a36Sopenharmony_ci	int j;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (chunk->cb)
32762306a36Sopenharmony_ci		control = DW_EDMA_V0_CB;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	j = chunk->bursts_alloc;
33062306a36Sopenharmony_ci	list_for_each_entry(child, &chunk->burst->list, list) {
33162306a36Sopenharmony_ci		j--;
33262306a36Sopenharmony_ci		if (!j) {
33362306a36Sopenharmony_ci			control |= DW_EDMA_V0_LIE;
33462306a36Sopenharmony_ci			if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
33562306a36Sopenharmony_ci				control |= DW_EDMA_V0_RIE;
33662306a36Sopenharmony_ci		}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci		dw_edma_v0_write_ll_data(chunk, i++, control, child->sz,
33962306a36Sopenharmony_ci					 child->sar, child->dar);
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	control = DW_EDMA_V0_LLP | DW_EDMA_V0_TCB;
34362306a36Sopenharmony_ci	if (!chunk->cb)
34462306a36Sopenharmony_ci		control |= DW_EDMA_V0_CB;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr);
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic void dw_edma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	/*
35262306a36Sopenharmony_ci	 * In case of remote eDMA engine setup, the DW PCIe RP/EP internal
35362306a36Sopenharmony_ci	 * configuration registers and application memory are normally accessed
35462306a36Sopenharmony_ci	 * over different buses. Ensure LL-data reaches the memory before the
35562306a36Sopenharmony_ci	 * doorbell register is toggled by issuing the dummy-read from the remote
35662306a36Sopenharmony_ci	 * LL memory in a hope that the MRd TLP will return only after the
35762306a36Sopenharmony_ci	 * last MWr TLP is completed
35862306a36Sopenharmony_ci	 */
35962306a36Sopenharmony_ci	if (!(chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
36062306a36Sopenharmony_ci		readl(chunk->ll_region.vaddr.io);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct dw_edma_chan *chan = chunk->chan;
36662306a36Sopenharmony_ci	struct dw_edma *dw = chan->dw;
36762306a36Sopenharmony_ci	u32 tmp;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	dw_edma_v0_core_write_chunk(chunk);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (first) {
37262306a36Sopenharmony_ci		/* Enable engine */
37362306a36Sopenharmony_ci		SET_RW_32(dw, chan->dir, engine_en, BIT(0));
37462306a36Sopenharmony_ci		if (dw->chip->mf == EDMA_MF_HDMA_COMPAT) {
37562306a36Sopenharmony_ci			switch (chan->id) {
37662306a36Sopenharmony_ci			case 0:
37762306a36Sopenharmony_ci				SET_RW_COMPAT(dw, chan->dir, ch0_pwr_en,
37862306a36Sopenharmony_ci					      BIT(0));
37962306a36Sopenharmony_ci				break;
38062306a36Sopenharmony_ci			case 1:
38162306a36Sopenharmony_ci				SET_RW_COMPAT(dw, chan->dir, ch1_pwr_en,
38262306a36Sopenharmony_ci					      BIT(0));
38362306a36Sopenharmony_ci				break;
38462306a36Sopenharmony_ci			case 2:
38562306a36Sopenharmony_ci				SET_RW_COMPAT(dw, chan->dir, ch2_pwr_en,
38662306a36Sopenharmony_ci					      BIT(0));
38762306a36Sopenharmony_ci				break;
38862306a36Sopenharmony_ci			case 3:
38962306a36Sopenharmony_ci				SET_RW_COMPAT(dw, chan->dir, ch3_pwr_en,
39062306a36Sopenharmony_ci					      BIT(0));
39162306a36Sopenharmony_ci				break;
39262306a36Sopenharmony_ci			case 4:
39362306a36Sopenharmony_ci				SET_RW_COMPAT(dw, chan->dir, ch4_pwr_en,
39462306a36Sopenharmony_ci					      BIT(0));
39562306a36Sopenharmony_ci				break;
39662306a36Sopenharmony_ci			case 5:
39762306a36Sopenharmony_ci				SET_RW_COMPAT(dw, chan->dir, ch5_pwr_en,
39862306a36Sopenharmony_ci					      BIT(0));
39962306a36Sopenharmony_ci				break;
40062306a36Sopenharmony_ci			case 6:
40162306a36Sopenharmony_ci				SET_RW_COMPAT(dw, chan->dir, ch6_pwr_en,
40262306a36Sopenharmony_ci					      BIT(0));
40362306a36Sopenharmony_ci				break;
40462306a36Sopenharmony_ci			case 7:
40562306a36Sopenharmony_ci				SET_RW_COMPAT(dw, chan->dir, ch7_pwr_en,
40662306a36Sopenharmony_ci					      BIT(0));
40762306a36Sopenharmony_ci				break;
40862306a36Sopenharmony_ci			}
40962306a36Sopenharmony_ci		}
41062306a36Sopenharmony_ci		/* Interrupt unmask - done, abort */
41162306a36Sopenharmony_ci		tmp = GET_RW_32(dw, chan->dir, int_mask);
41262306a36Sopenharmony_ci		tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
41362306a36Sopenharmony_ci		tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
41462306a36Sopenharmony_ci		SET_RW_32(dw, chan->dir, int_mask, tmp);
41562306a36Sopenharmony_ci		/* Linked list error */
41662306a36Sopenharmony_ci		tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
41762306a36Sopenharmony_ci		tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id));
41862306a36Sopenharmony_ci		SET_RW_32(dw, chan->dir, linked_list_err_en, tmp);
41962306a36Sopenharmony_ci		/* Channel control */
42062306a36Sopenharmony_ci		SET_CH_32(dw, chan->dir, chan->id, ch_control1,
42162306a36Sopenharmony_ci			  (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));
42262306a36Sopenharmony_ci		/* Linked list */
42362306a36Sopenharmony_ci		/* llp is not aligned on 64bit -> keep 32bit accesses */
42462306a36Sopenharmony_ci		SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
42562306a36Sopenharmony_ci			  lower_32_bits(chunk->ll_region.paddr));
42662306a36Sopenharmony_ci		SET_CH_32(dw, chan->dir, chan->id, llp.msb,
42762306a36Sopenharmony_ci			  upper_32_bits(chunk->ll_region.paddr));
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	dw_edma_v0_sync_ll_data(chunk);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* Doorbell */
43362306a36Sopenharmony_ci	SET_RW_32(dw, chan->dir, doorbell,
43462306a36Sopenharmony_ci		  FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id));
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct dw_edma *dw = chan->dw;
44062306a36Sopenharmony_ci	u32 tmp = 0;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	/* MSI done addr - low, high */
44362306a36Sopenharmony_ci	SET_RW_32(dw, chan->dir, done_imwr.lsb, chan->msi.address_lo);
44462306a36Sopenharmony_ci	SET_RW_32(dw, chan->dir, done_imwr.msb, chan->msi.address_hi);
44562306a36Sopenharmony_ci	/* MSI abort addr - low, high */
44662306a36Sopenharmony_ci	SET_RW_32(dw, chan->dir, abort_imwr.lsb, chan->msi.address_lo);
44762306a36Sopenharmony_ci	SET_RW_32(dw, chan->dir, abort_imwr.msb, chan->msi.address_hi);
44862306a36Sopenharmony_ci	/* MSI data - low, high */
44962306a36Sopenharmony_ci	switch (chan->id) {
45062306a36Sopenharmony_ci	case 0:
45162306a36Sopenharmony_ci	case 1:
45262306a36Sopenharmony_ci		tmp = GET_RW_32(dw, chan->dir, ch01_imwr_data);
45362306a36Sopenharmony_ci		break;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	case 2:
45662306a36Sopenharmony_ci	case 3:
45762306a36Sopenharmony_ci		tmp = GET_RW_32(dw, chan->dir, ch23_imwr_data);
45862306a36Sopenharmony_ci		break;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	case 4:
46162306a36Sopenharmony_ci	case 5:
46262306a36Sopenharmony_ci		tmp = GET_RW_32(dw, chan->dir, ch45_imwr_data);
46362306a36Sopenharmony_ci		break;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	case 6:
46662306a36Sopenharmony_ci	case 7:
46762306a36Sopenharmony_ci		tmp = GET_RW_32(dw, chan->dir, ch67_imwr_data);
46862306a36Sopenharmony_ci		break;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	if (chan->id & BIT(0)) {
47262306a36Sopenharmony_ci		/* Channel odd {1, 3, 5, 7} */
47362306a36Sopenharmony_ci		tmp &= EDMA_V0_CH_EVEN_MSI_DATA_MASK;
47462306a36Sopenharmony_ci		tmp |= FIELD_PREP(EDMA_V0_CH_ODD_MSI_DATA_MASK,
47562306a36Sopenharmony_ci				  chan->msi.data);
47662306a36Sopenharmony_ci	} else {
47762306a36Sopenharmony_ci		/* Channel even {0, 2, 4, 6} */
47862306a36Sopenharmony_ci		tmp &= EDMA_V0_CH_ODD_MSI_DATA_MASK;
47962306a36Sopenharmony_ci		tmp |= FIELD_PREP(EDMA_V0_CH_EVEN_MSI_DATA_MASK,
48062306a36Sopenharmony_ci				  chan->msi.data);
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	switch (chan->id) {
48462306a36Sopenharmony_ci	case 0:
48562306a36Sopenharmony_ci	case 1:
48662306a36Sopenharmony_ci		SET_RW_32(dw, chan->dir, ch01_imwr_data, tmp);
48762306a36Sopenharmony_ci		break;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	case 2:
49062306a36Sopenharmony_ci	case 3:
49162306a36Sopenharmony_ci		SET_RW_32(dw, chan->dir, ch23_imwr_data, tmp);
49262306a36Sopenharmony_ci		break;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	case 4:
49562306a36Sopenharmony_ci	case 5:
49662306a36Sopenharmony_ci		SET_RW_32(dw, chan->dir, ch45_imwr_data, tmp);
49762306a36Sopenharmony_ci		break;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	case 6:
50062306a36Sopenharmony_ci	case 7:
50162306a36Sopenharmony_ci		SET_RW_32(dw, chan->dir, ch67_imwr_data, tmp);
50262306a36Sopenharmony_ci		break;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci/* eDMA debugfs callbacks */
50762306a36Sopenharmony_cistatic void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	dw_edma_v0_debugfs_on(dw);
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic const struct dw_edma_core_ops dw_edma_v0_core = {
51362306a36Sopenharmony_ci	.off = dw_edma_v0_core_off,
51462306a36Sopenharmony_ci	.ch_count = dw_edma_v0_core_ch_count,
51562306a36Sopenharmony_ci	.ch_status = dw_edma_v0_core_ch_status,
51662306a36Sopenharmony_ci	.handle_int = dw_edma_v0_core_handle_int,
51762306a36Sopenharmony_ci	.start = dw_edma_v0_core_start,
51862306a36Sopenharmony_ci	.ch_config = dw_edma_v0_core_ch_config,
51962306a36Sopenharmony_ci	.debugfs_on = dw_edma_v0_core_debugfs_on,
52062306a36Sopenharmony_ci};
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_civoid dw_edma_v0_core_register(struct dw_edma *dw)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	dw->core = &dw_edma_v0_core;
52562306a36Sopenharmony_ci}
526