drivers/dma/uniphier-mdmac.c

Source file repositories/reference/linux-study-clean/drivers/dma/uniphier-mdmac.c

File Facts

System
Linux kernel
Corpus path
drivers/dma/uniphier-mdmac.c
Extension
.c
Size
13297 bytes
Lines
507
Domain
Driver Families
Bucket
drivers/dma
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct uniphier_mdmac_desc {
	struct virt_dma_desc vd;
	struct scatterlist *sgl;
	unsigned int sg_len;
	unsigned int sg_cur;
	enum dma_transfer_direction dir;
};

struct uniphier_mdmac_chan {
	struct virt_dma_chan vc;
	struct uniphier_mdmac_device *mdev;
	struct uniphier_mdmac_desc *md;
	void __iomem *reg_ch_base;
	unsigned int chan_id;
};

struct uniphier_mdmac_device {
	struct dma_device ddev;
	struct clk *clk;
	void __iomem *reg_base;
	struct uniphier_mdmac_chan channels[];
};

static struct uniphier_mdmac_chan *
to_uniphier_mdmac_chan(struct virt_dma_chan *vc)
{
	return container_of(vc, struct uniphier_mdmac_chan, vc);
}

static struct uniphier_mdmac_desc *
to_uniphier_mdmac_desc(struct virt_dma_desc *vd)
{
	return container_of(vd, struct uniphier_mdmac_desc, vd);
}

/* mc->vc.lock must be held by caller */
static struct uniphier_mdmac_desc *
uniphier_mdmac_next_desc(struct uniphier_mdmac_chan *mc)
{
	struct virt_dma_desc *vd;

	vd = vchan_next_desc(&mc->vc);
	if (!vd) {
		mc->md = NULL;
		return NULL;
	}

	list_del(&vd->node);

	mc->md = to_uniphier_mdmac_desc(vd);

	return mc->md;
}

/* mc->vc.lock must be held by caller */
static void uniphier_mdmac_handle(struct uniphier_mdmac_chan *mc,
				  struct uniphier_mdmac_desc *md)
{
	struct uniphier_mdmac_device *mdev = mc->mdev;
	struct scatterlist *sg;
	u32 irq_flag = UNIPHIER_MDMAC_CH_IRQ__DONE;
	u32 src_mode, src_addr, dest_mode, dest_addr, chunk_size;

	sg = &md->sgl[md->sg_cur];

	if (md->dir == DMA_MEM_TO_DEV) {
		src_mode = UNIPHIER_MDMAC_CH_MODE__ADDR_INC;
		src_addr = sg_dma_address(sg);
		dest_mode = UNIPHIER_MDMAC_CH_MODE__ADDR_FIXED;
		dest_addr = 0;
	} else {
		src_mode = UNIPHIER_MDMAC_CH_MODE__ADDR_FIXED;
		src_addr = 0;
		dest_mode = UNIPHIER_MDMAC_CH_MODE__ADDR_INC;
		dest_addr = sg_dma_address(sg);
	}

	chunk_size = sg_dma_len(sg);

	writel(src_mode, mc->reg_ch_base + UNIPHIER_MDMAC_CH_SRC_MODE);
	writel(dest_mode, mc->reg_ch_base + UNIPHIER_MDMAC_CH_DEST_MODE);
	writel(src_addr, mc->reg_ch_base + UNIPHIER_MDMAC_CH_SRC_ADDR);
	writel(dest_addr, mc->reg_ch_base + UNIPHIER_MDMAC_CH_DEST_ADDR);
	writel(chunk_size, mc->reg_ch_base + UNIPHIER_MDMAC_CH_SIZE);

	/* write 1 to clear */
	writel(irq_flag, mc->reg_ch_base + UNIPHIER_MDMAC_CH_IRQ_REQ);

	writel(irq_flag, mc->reg_ch_base + UNIPHIER_MDMAC_CH_IRQ_EN);

Annotation

Implementation Notes