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