Commit f9c3403f authored by Cai Huoqing's avatar Cai Huoqing Committed by Vinod Koul
Browse files

dmaengine: dw-edma: Create a new dw_edma_core_ops structure to abstract controller operation



The structure dw_edma_core_ops has a set of the pointers
abstracting out the DW eDMA vX and DW HDMA Native controllers.
And use dw_edma_v0_core_register to set up operation.

Signed-off-by: default avatarCai Huoqing <cai.huoqing@linux.dev>
Reviewed-by: default avatarSerge Semin <fancer.lancer@gmail.com>
Reviewed-by: default avatarManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Tested-by: default avatarSerge Semin <fancer.lancer@gmail.com>
Link: https://lore.kernel.org/r/20230520050854.73160-3-cai.huoqing@linux.dev


Signed-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent 48751755
Loading
Loading
Loading
Loading
+25 −57
Original line number Diff line number Diff line
@@ -183,6 +183,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc)

static int dw_edma_start_transfer(struct dw_edma_chan *chan)
{
	struct dw_edma *dw = chan->dw;
	struct dw_edma_chunk *child;
	struct dw_edma_desc *desc;
	struct virt_dma_desc *vd;
@@ -200,7 +201,7 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)
	if (!child)
		return 0;

	dw_edma_v0_core_start(child, !desc->xfer_sz);
	dw_edma_core_start(dw, child, !desc->xfer_sz);
	desc->xfer_sz += child->ll_region.sz;
	dw_edma_free_burst(child);
	list_del(&child->list);
@@ -287,7 +288,7 @@ static int dw_edma_device_terminate_all(struct dma_chan *dchan)
		chan->configured = false;
	} else if (chan->status == EDMA_ST_IDLE) {
		chan->configured = false;
	} else if (dw_edma_v0_core_ch_status(chan) == DMA_COMPLETE) {
	} else if (dw_edma_core_ch_status(chan) == DMA_COMPLETE) {
		/*
		 * The channel is in a false BUSY state, probably didn't
		 * receive or lost an interrupt
@@ -599,8 +600,6 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
	struct virt_dma_desc *vd;
	unsigned long flags;

	dw_edma_v0_core_clear_done_int(chan);

	spin_lock_irqsave(&chan->vc.lock, flags);
	vd = vchan_next_desc(&chan->vc);
	if (vd) {
@@ -641,8 +640,6 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
	struct virt_dma_desc *vd;
	unsigned long flags;

	dw_edma_v0_core_clear_abort_int(chan);

	spin_lock_irqsave(&chan->vc.lock, flags);
	vd = vchan_next_desc(&chan->vc);
	if (vd) {
@@ -654,63 +651,32 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
	chan->status = EDMA_ST_IDLE;
}

static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
{
	struct dw_edma_irq *dw_irq = data;
	struct dw_edma *dw = dw_irq->dw;
	unsigned long total, pos, val;
	unsigned long off;
	u32 mask;

	if (write) {
		total = dw->wr_ch_cnt;
		off = 0;
		mask = dw_irq->wr_mask;
	} else {
		total = dw->rd_ch_cnt;
		off = dw->wr_ch_cnt;
		mask = dw_irq->rd_mask;
	}

	val = dw_edma_v0_core_status_done_int(dw, write ?
							  EDMA_DIR_WRITE :
							  EDMA_DIR_READ);
	val &= mask;
	for_each_set_bit(pos, &val, total) {
		struct dw_edma_chan *chan = &dw->chan[pos + off];

		dw_edma_done_interrupt(chan);
	}

	val = dw_edma_v0_core_status_abort_int(dw, write ?
							   EDMA_DIR_WRITE :
							   EDMA_DIR_READ);
	val &= mask;
	for_each_set_bit(pos, &val, total) {
		struct dw_edma_chan *chan = &dw->chan[pos + off];

		dw_edma_abort_interrupt(chan);
	}

	return IRQ_HANDLED;
}

static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
{
	return dw_edma_interrupt(irq, data, true);
	return dw_edma_core_handle_int(dw_irq, EDMA_DIR_WRITE,
				       dw_edma_done_interrupt,
				       dw_edma_abort_interrupt);
}

static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
{
	return dw_edma_interrupt(irq, data, false);
	struct dw_edma_irq *dw_irq = data;

	return dw_edma_core_handle_int(dw_irq, EDMA_DIR_READ,
				       dw_edma_done_interrupt,
				       dw_edma_abort_interrupt);
}

static irqreturn_t dw_edma_interrupt_common(int irq, void *data)
{
	dw_edma_interrupt(irq, data, true);
	dw_edma_interrupt(irq, data, false);
	irqreturn_t ret = IRQ_NONE;

	ret |= dw_edma_interrupt_write(irq, data);
	ret |= dw_edma_interrupt_read(irq, data);

	return IRQ_HANDLED;
	return ret;
}

static int dw_edma_alloc_chan_resources(struct dma_chan *dchan)
@@ -811,7 +777,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)

		vchan_init(&chan->vc, dma);

		dw_edma_v0_core_device_config(chan);
		dw_edma_core_ch_config(chan);
	}

	/* Set DMA channel capabilities */
@@ -956,14 +922,16 @@ int dw_edma_probe(struct dw_edma_chip *chip)

	dw->chip = chip;

	dw_edma_v0_core_register(dw);

	raw_spin_lock_init(&dw->lock);

	dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt,
			      dw_edma_v0_core_ch_count(dw, EDMA_DIR_WRITE));
			      dw_edma_core_ch_count(dw, EDMA_DIR_WRITE));
	dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH);

	dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt,
			      dw_edma_v0_core_ch_count(dw, EDMA_DIR_READ));
			      dw_edma_core_ch_count(dw, EDMA_DIR_READ));
	dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH);

	if (!dw->wr_ch_cnt && !dw->rd_ch_cnt)
@@ -982,7 +950,7 @@ int dw_edma_probe(struct dw_edma_chip *chip)
		 dev_name(chip->dev));

	/* Disable eDMA, only to establish the ideal initial conditions */
	dw_edma_v0_core_off(dw);
	dw_edma_core_off(dw);

	/* Request IRQs */
	err = dw_edma_irq_request(dw, &wr_alloc, &rd_alloc);
@@ -995,7 +963,7 @@ int dw_edma_probe(struct dw_edma_chip *chip)
		goto err_irq_free;

	/* Turn debugfs on */
	dw_edma_v0_core_debugfs_on(dw);
	dw_edma_core_debugfs_on(dw);

	chip->dw = dw;

@@ -1021,7 +989,7 @@ int dw_edma_remove(struct dw_edma_chip *chip)
		return -ENODEV;

	/* Disable eDMA */
	dw_edma_v0_core_off(dw);
	dw_edma_core_off(dw);

	/* Free irqs */
	for (i = (dw->nr_irqs - 1); i >= 0; i--)
+58 −0
Original line number Diff line number Diff line
@@ -111,6 +111,21 @@ struct dw_edma {
	raw_spinlock_t			lock;		/* Only for legacy */

	struct dw_edma_chip             *chip;

	const struct dw_edma_core_ops	*core;
};

typedef void (*dw_edma_handler_t)(struct dw_edma_chan *);

struct dw_edma_core_ops {
	void (*off)(struct dw_edma *dw);
	u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir);
	enum dma_status (*ch_status)(struct dw_edma_chan *chan);
	irqreturn_t (*handle_int)(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
				  dw_edma_handler_t done, dw_edma_handler_t abort);
	void (*start)(struct dw_edma_chunk *chunk, bool first);
	void (*ch_config)(struct dw_edma_chan *chan);
	void (*debugfs_on)(struct dw_edma *dw);
};

struct dw_edma_sg {
@@ -148,4 +163,47 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan)
	return vc2dw_edma_chan(to_virt_chan(dchan));
}

static inline
void dw_edma_core_off(struct dw_edma *dw)
{
	dw->core->off(dw);
}

static inline
u16 dw_edma_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
{
	return dw->core->ch_count(dw, dir);
}

static inline
enum dma_status dw_edma_core_ch_status(struct dw_edma_chan *chan)
{
	return chan->dw->core->ch_status(chan);
}

static inline irqreturn_t
dw_edma_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
			dw_edma_handler_t done, dw_edma_handler_t abort)
{
	return dw_irq->dw->core->handle_int(dw_irq, dir, done, abort);
}

static inline
void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first)
{
	dw->core->start(chunk, first);
}

static inline
void dw_edma_core_ch_config(struct dw_edma_chan *chan)
{
	chan->dw->core->ch_config(chan);
}

static inline
void dw_edma_core_debugfs_on(struct dw_edma *dw)
{
	dw->core->debugfs_on(dw);
}

#endif /* _DW_EDMA_CORE_H */
+72 −13
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@
 */

#include <linux/bitfield.h>

#include <linux/irqreturn.h>
#include <linux/io-64-nonatomic-lo-hi.h>

#include "dw-edma-core.h"
@@ -160,7 +160,7 @@ static inline u32 readl_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
	readl_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name))

/* eDMA management callbacks */
void dw_edma_v0_core_off(struct dw_edma *dw)
static void dw_edma_v0_core_off(struct dw_edma *dw)
{
	SET_BOTH_32(dw, int_mask,
		    EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK);
@@ -169,7 +169,7 @@ void dw_edma_v0_core_off(struct dw_edma *dw)
	SET_BOTH_32(dw, engine_en, 0);
}

u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
static u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
{
	u32 num_ch;

@@ -186,7 +186,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
	return (u16)num_ch;
}

enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
static enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
{
	struct dw_edma *dw = chan->dw;
	u32 tmp;
@@ -202,7 +202,7 @@ enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
		return DMA_ERROR;
}

void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
static void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
{
	struct dw_edma *dw = chan->dw;

@@ -210,7 +210,7 @@ void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
		  FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)));
}

void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
static void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
{
	struct dw_edma *dw = chan->dw;

@@ -218,18 +218,64 @@ void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
		  FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)));
}

u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
static u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
{
	return FIELD_GET(EDMA_V0_DONE_INT_MASK,
			 GET_RW_32(dw, dir, int_status));
}

u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
static u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
{
	return FIELD_GET(EDMA_V0_ABORT_INT_MASK,
			 GET_RW_32(dw, dir, int_status));
}

static irqreturn_t
dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
			   dw_edma_handler_t done, dw_edma_handler_t abort)
{
	struct dw_edma *dw = dw_irq->dw;
	unsigned long total, pos, val;
	irqreturn_t ret = IRQ_NONE;
	struct dw_edma_chan *chan;
	unsigned long off;
	u32 mask;

	if (dir == EDMA_DIR_WRITE) {
		total = dw->wr_ch_cnt;
		off = 0;
		mask = dw_irq->wr_mask;
	} else {
		total = dw->rd_ch_cnt;
		off = dw->wr_ch_cnt;
		mask = dw_irq->rd_mask;
	}

	val = dw_edma_v0_core_status_done_int(dw, dir);
	val &= mask;
	for_each_set_bit(pos, &val, total) {
		chan = &dw->chan[pos + off];

		dw_edma_v0_core_clear_done_int(chan);
		done(chan);

		ret = IRQ_HANDLED;
	}

	val = dw_edma_v0_core_status_abort_int(dw, dir);
	val &= mask;
	for_each_set_bit(pos, &val, total) {
		chan = &dw->chan[pos + off];

		dw_edma_v0_core_clear_abort_int(chan);
		abort(chan);

		ret = IRQ_HANDLED;
	}

	return ret;
}

static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
				     u32 control, u32 size, u64 sar, u64 dar)
{
@@ -300,7 +346,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
	dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr);
}

void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
{
	struct dw_edma_chan *chan = chunk->chan;
	struct dw_edma *dw = chan->dw;
@@ -371,7 +417,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
		  FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id));
}

int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan)
{
	struct dw_edma *dw = chan->dw;
	u32 tmp = 0;
@@ -438,12 +484,25 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
		SET_RW_32(dw, chan->dir, ch67_imwr_data, tmp);
		break;
	}

	return 0;
}

/* eDMA debugfs callbacks */
void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
{
	dw_edma_v0_debugfs_on(dw);
}

static const struct dw_edma_core_ops dw_edma_v0_core = {
	.off = dw_edma_v0_core_off,
	.ch_count = dw_edma_v0_core_ch_count,
	.ch_status = dw_edma_v0_core_ch_status,
	.handle_int = dw_edma_v0_core_handle_int,
	.start = dw_edma_v0_core_start,
	.ch_config = dw_edma_v0_core_ch_config,
	.debugfs_on = dw_edma_v0_core_debugfs_on,
};

void dw_edma_v0_core_register(struct dw_edma *dw)
{
	dw->core = &dw_edma_v0_core;
}
+2 −12
Original line number Diff line number Diff line
@@ -11,17 +11,7 @@

#include <linux/dma/edma.h>

/* eDMA management callbacks */
void dw_edma_v0_core_off(struct dw_edma *chan);
u16 dw_edma_v0_core_ch_count(struct dw_edma *chan, enum dw_edma_dir dir);
enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan);
void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan);
void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan);
u32 dw_edma_v0_core_status_done_int(struct dw_edma *chan, enum dw_edma_dir dir);
u32 dw_edma_v0_core_status_abort_int(struct dw_edma *chan, enum dw_edma_dir dir);
void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first);
int dw_edma_v0_core_device_config(struct dw_edma_chan *chan);
/* eDMA debug fs callbacks */
void dw_edma_v0_core_debugfs_on(struct dw_edma *dw);
/* eDMA core register */
void dw_edma_v0_core_register(struct dw_edma *dw);

#endif /* _DW_EDMA_V0_CORE_H */