162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * SiFive FU540 Platform DMA driver
462306a36Sopenharmony_ci * Copyright (C) 2019 SiFive
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Based partially on:
762306a36Sopenharmony_ci * - drivers/dma/fsl-edma.c
862306a36Sopenharmony_ci * - drivers/dma/dw-edma/
962306a36Sopenharmony_ci * - drivers/dma/pxa-dma.c
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * See the following sources for further documentation:
1262306a36Sopenharmony_ci * - Chapter 12 "Platform DMA Engine (PDMA)" of
1362306a36Sopenharmony_ci *   SiFive FU540-C000 v1.0
1462306a36Sopenharmony_ci *   https://static.dev.sifive.com/FU540-C000-v1.0.pdf
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/device.h>
1862306a36Sopenharmony_ci#include <linux/kernel.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
2162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2262306a36Sopenharmony_ci#include <linux/of.h>
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "sf-pdma.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#ifndef readq
2862306a36Sopenharmony_cistatic inline unsigned long long readq(void __iomem *addr)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	return readl(addr) | (((unsigned long long)readl(addr + 4)) << 32LL);
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci#endif
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#ifndef writeq
3562306a36Sopenharmony_cistatic inline void writeq(unsigned long long v, void __iomem *addr)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	writel(lower_32_bits(v), addr);
3862306a36Sopenharmony_ci	writel(upper_32_bits(v), addr + 4);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci#endif
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic inline struct sf_pdma_chan *to_sf_pdma_chan(struct dma_chan *dchan)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	return container_of(dchan, struct sf_pdma_chan, vchan.chan);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic inline struct sf_pdma_desc *to_sf_pdma_desc(struct virt_dma_desc *vd)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	return container_of(vd, struct sf_pdma_desc, vdesc);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic struct sf_pdma_desc *sf_pdma_alloc_desc(struct sf_pdma_chan *chan)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct sf_pdma_desc *desc;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
5762306a36Sopenharmony_ci	if (!desc)
5862306a36Sopenharmony_ci		return NULL;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	desc->chan = chan;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return desc;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void sf_pdma_fill_desc(struct sf_pdma_desc *desc,
6662306a36Sopenharmony_ci			      u64 dst, u64 src, u64 size)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	desc->xfer_type = PDMA_FULL_SPEED;
6962306a36Sopenharmony_ci	desc->xfer_size = size;
7062306a36Sopenharmony_ci	desc->dst_addr = dst;
7162306a36Sopenharmony_ci	desc->src_addr = src;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void sf_pdma_disclaim_chan(struct sf_pdma_chan *chan)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	writel(PDMA_CLEAR_CTRL, regs->ctrl);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
8262306a36Sopenharmony_cisf_pdma_prep_dma_memcpy(struct dma_chan *dchan,	dma_addr_t dest, dma_addr_t src,
8362306a36Sopenharmony_ci			size_t len, unsigned long flags)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
8662306a36Sopenharmony_ci	struct sf_pdma_desc *desc;
8762306a36Sopenharmony_ci	unsigned long iflags;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (chan && (!len || !dest || !src)) {
9062306a36Sopenharmony_ci		dev_err(chan->pdma->dma_dev.dev,
9162306a36Sopenharmony_ci			"Please check dma len, dest, src!\n");
9262306a36Sopenharmony_ci		return NULL;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	desc = sf_pdma_alloc_desc(chan);
9662306a36Sopenharmony_ci	if (!desc)
9762306a36Sopenharmony_ci		return NULL;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	desc->dirn = DMA_MEM_TO_MEM;
10062306a36Sopenharmony_ci	desc->async_tx = vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	spin_lock_irqsave(&chan->vchan.lock, iflags);
10362306a36Sopenharmony_ci	sf_pdma_fill_desc(desc, dest, src, len);
10462306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->vchan.lock, iflags);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return desc->async_tx;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int sf_pdma_slave_config(struct dma_chan *dchan,
11062306a36Sopenharmony_ci				struct dma_slave_config *cfg)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	memcpy(&chan->cfg, cfg, sizeof(*cfg));
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic int sf_pdma_alloc_chan_resources(struct dma_chan *dchan)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
12262306a36Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	dma_cookie_init(dchan);
12562306a36Sopenharmony_ci	writel(PDMA_CLAIM_MASK, regs->ctrl);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void sf_pdma_disable_request(struct sf_pdma_chan *chan)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	writel(readl(regs->ctrl) & ~PDMA_RUN_MASK, regs->ctrl);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic void sf_pdma_free_chan_resources(struct dma_chan *dchan)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
14062306a36Sopenharmony_ci	unsigned long flags;
14162306a36Sopenharmony_ci	LIST_HEAD(head);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	spin_lock_irqsave(&chan->vchan.lock, flags);
14462306a36Sopenharmony_ci	sf_pdma_disable_request(chan);
14562306a36Sopenharmony_ci	kfree(chan->desc);
14662306a36Sopenharmony_ci	chan->desc = NULL;
14762306a36Sopenharmony_ci	vchan_get_all_descriptors(&chan->vchan, &head);
14862306a36Sopenharmony_ci	sf_pdma_disclaim_chan(chan);
14962306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->vchan.lock, flags);
15062306a36Sopenharmony_ci	vchan_dma_desc_free_list(&chan->vchan, &head);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic size_t sf_pdma_desc_residue(struct sf_pdma_chan *chan,
15462306a36Sopenharmony_ci				   dma_cookie_t cookie)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct virt_dma_desc *vd = NULL;
15762306a36Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
15862306a36Sopenharmony_ci	unsigned long flags;
15962306a36Sopenharmony_ci	u64 residue = 0;
16062306a36Sopenharmony_ci	struct sf_pdma_desc *desc;
16162306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx = NULL;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	spin_lock_irqsave(&chan->vchan.lock, flags);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	list_for_each_entry(vd, &chan->vchan.desc_submitted, node)
16662306a36Sopenharmony_ci		if (vd->tx.cookie == cookie)
16762306a36Sopenharmony_ci			tx = &vd->tx;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (!tx)
17062306a36Sopenharmony_ci		goto out;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (cookie == tx->chan->completed_cookie)
17362306a36Sopenharmony_ci		goto out;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (cookie == tx->cookie) {
17662306a36Sopenharmony_ci		residue = readq(regs->residue);
17762306a36Sopenharmony_ci	} else {
17862306a36Sopenharmony_ci		vd = vchan_find_desc(&chan->vchan, cookie);
17962306a36Sopenharmony_ci		if (!vd)
18062306a36Sopenharmony_ci			goto out;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		desc = to_sf_pdma_desc(vd);
18362306a36Sopenharmony_ci		residue = desc->xfer_size;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ciout:
18762306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->vchan.lock, flags);
18862306a36Sopenharmony_ci	return residue;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic enum dma_status
19262306a36Sopenharmony_cisf_pdma_tx_status(struct dma_chan *dchan,
19362306a36Sopenharmony_ci		  dma_cookie_t cookie,
19462306a36Sopenharmony_ci		  struct dma_tx_state *txstate)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
19762306a36Sopenharmony_ci	enum dma_status status;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	status = dma_cookie_status(dchan, cookie, txstate);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (txstate && status != DMA_ERROR)
20262306a36Sopenharmony_ci		dma_set_residue(txstate, sf_pdma_desc_residue(chan, cookie));
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return status;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int sf_pdma_terminate_all(struct dma_chan *dchan)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
21062306a36Sopenharmony_ci	unsigned long flags;
21162306a36Sopenharmony_ci	LIST_HEAD(head);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	spin_lock_irqsave(&chan->vchan.lock, flags);
21462306a36Sopenharmony_ci	sf_pdma_disable_request(chan);
21562306a36Sopenharmony_ci	kfree(chan->desc);
21662306a36Sopenharmony_ci	chan->desc = NULL;
21762306a36Sopenharmony_ci	chan->xfer_err = false;
21862306a36Sopenharmony_ci	vchan_get_all_descriptors(&chan->vchan, &head);
21962306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->vchan.lock, flags);
22062306a36Sopenharmony_ci	vchan_dma_desc_free_list(&chan->vchan, &head);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return 0;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic void sf_pdma_enable_request(struct sf_pdma_chan *chan)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
22862306a36Sopenharmony_ci	u32 v;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	v = PDMA_CLAIM_MASK |
23162306a36Sopenharmony_ci		PDMA_ENABLE_DONE_INT_MASK |
23262306a36Sopenharmony_ci		PDMA_ENABLE_ERR_INT_MASK |
23362306a36Sopenharmony_ci		PDMA_RUN_MASK;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	writel(v, regs->ctrl);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic struct sf_pdma_desc *sf_pdma_get_first_pending_desc(struct sf_pdma_chan *chan)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct virt_dma_chan *vchan = &chan->vchan;
24162306a36Sopenharmony_ci	struct virt_dma_desc *vdesc;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (list_empty(&vchan->desc_issued))
24462306a36Sopenharmony_ci		return NULL;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	vdesc = list_first_entry(&vchan->desc_issued, struct virt_dma_desc, node);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	return container_of(vdesc, struct sf_pdma_desc, vdesc);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic void sf_pdma_xfer_desc(struct sf_pdma_chan *chan)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct sf_pdma_desc *desc = chan->desc;
25462306a36Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (!desc) {
25762306a36Sopenharmony_ci		dev_err(chan->pdma->dma_dev.dev, "NULL desc.\n");
25862306a36Sopenharmony_ci		return;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	writel(desc->xfer_type, regs->xfer_type);
26262306a36Sopenharmony_ci	writeq(desc->xfer_size, regs->xfer_size);
26362306a36Sopenharmony_ci	writeq(desc->dst_addr, regs->dst_addr);
26462306a36Sopenharmony_ci	writeq(desc->src_addr, regs->src_addr);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	chan->desc = desc;
26762306a36Sopenharmony_ci	chan->status = DMA_IN_PROGRESS;
26862306a36Sopenharmony_ci	sf_pdma_enable_request(chan);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void sf_pdma_issue_pending(struct dma_chan *dchan)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
27462306a36Sopenharmony_ci	unsigned long flags;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	spin_lock_irqsave(&chan->vchan.lock, flags);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (!chan->desc && vchan_issue_pending(&chan->vchan)) {
27962306a36Sopenharmony_ci		/* vchan_issue_pending has made a check that desc in not NULL */
28062306a36Sopenharmony_ci		chan->desc = sf_pdma_get_first_pending_desc(chan);
28162306a36Sopenharmony_ci		sf_pdma_xfer_desc(chan);
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->vchan.lock, flags);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic void sf_pdma_free_desc(struct virt_dma_desc *vdesc)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct sf_pdma_desc *desc;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	desc = to_sf_pdma_desc(vdesc);
29262306a36Sopenharmony_ci	kfree(desc);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic void sf_pdma_donebh_tasklet(struct tasklet_struct *t)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct sf_pdma_chan *chan = from_tasklet(chan, t, done_tasklet);
29862306a36Sopenharmony_ci	unsigned long flags;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
30162306a36Sopenharmony_ci	if (chan->xfer_err) {
30262306a36Sopenharmony_ci		chan->retries = MAX_RETRY;
30362306a36Sopenharmony_ci		chan->status = DMA_COMPLETE;
30462306a36Sopenharmony_ci		chan->xfer_err = false;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	spin_lock_irqsave(&chan->vchan.lock, flags);
30962306a36Sopenharmony_ci	list_del(&chan->desc->vdesc.node);
31062306a36Sopenharmony_ci	vchan_cookie_complete(&chan->desc->vdesc);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	chan->desc = sf_pdma_get_first_pending_desc(chan);
31362306a36Sopenharmony_ci	if (chan->desc)
31462306a36Sopenharmony_ci		sf_pdma_xfer_desc(chan);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->vchan.lock, flags);
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic void sf_pdma_errbh_tasklet(struct tasklet_struct *t)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct sf_pdma_chan *chan = from_tasklet(chan, t, err_tasklet);
32262306a36Sopenharmony_ci	struct sf_pdma_desc *desc = chan->desc;
32362306a36Sopenharmony_ci	unsigned long flags;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
32662306a36Sopenharmony_ci	if (chan->retries <= 0) {
32762306a36Sopenharmony_ci		/* fail to recover */
32862306a36Sopenharmony_ci		spin_unlock_irqrestore(&chan->lock, flags);
32962306a36Sopenharmony_ci		dmaengine_desc_get_callback_invoke(desc->async_tx, NULL);
33062306a36Sopenharmony_ci	} else {
33162306a36Sopenharmony_ci		/* retry */
33262306a36Sopenharmony_ci		chan->retries--;
33362306a36Sopenharmony_ci		chan->xfer_err = true;
33462306a36Sopenharmony_ci		chan->status = DMA_ERROR;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		sf_pdma_enable_request(chan);
33762306a36Sopenharmony_ci		spin_unlock_irqrestore(&chan->lock, flags);
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic irqreturn_t sf_pdma_done_isr(int irq, void *dev_id)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	struct sf_pdma_chan *chan = dev_id;
34462306a36Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
34562306a36Sopenharmony_ci	u64 residue;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	spin_lock(&chan->vchan.lock);
34862306a36Sopenharmony_ci	writel((readl(regs->ctrl)) & ~PDMA_DONE_STATUS_MASK, regs->ctrl);
34962306a36Sopenharmony_ci	residue = readq(regs->residue);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (!residue) {
35262306a36Sopenharmony_ci		tasklet_hi_schedule(&chan->done_tasklet);
35362306a36Sopenharmony_ci	} else {
35462306a36Sopenharmony_ci		/* submit next trascatioin if possible */
35562306a36Sopenharmony_ci		struct sf_pdma_desc *desc = chan->desc;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci		desc->src_addr += desc->xfer_size - residue;
35862306a36Sopenharmony_ci		desc->dst_addr += desc->xfer_size - residue;
35962306a36Sopenharmony_ci		desc->xfer_size = residue;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		sf_pdma_xfer_desc(chan);
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	spin_unlock(&chan->vchan.lock);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return IRQ_HANDLED;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic irqreturn_t sf_pdma_err_isr(int irq, void *dev_id)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct sf_pdma_chan *chan = dev_id;
37262306a36Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	spin_lock(&chan->lock);
37562306a36Sopenharmony_ci	writel((readl(regs->ctrl)) & ~PDMA_ERR_STATUS_MASK, regs->ctrl);
37662306a36Sopenharmony_ci	spin_unlock(&chan->lock);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	tasklet_schedule(&chan->err_tasklet);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return IRQ_HANDLED;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci/**
38462306a36Sopenharmony_ci * sf_pdma_irq_init() - Init PDMA IRQ Handlers
38562306a36Sopenharmony_ci * @pdev: pointer of platform_device
38662306a36Sopenharmony_ci * @pdma: pointer of PDMA engine. Caller should check NULL
38762306a36Sopenharmony_ci *
38862306a36Sopenharmony_ci * Initialize DONE and ERROR interrupt handler for 4 channels. Caller should
38962306a36Sopenharmony_ci * make sure the pointer passed in are non-NULL. This function should be called
39062306a36Sopenharmony_ci * only one time during the device probe.
39162306a36Sopenharmony_ci *
39262306a36Sopenharmony_ci * Context: Any context.
39362306a36Sopenharmony_ci *
39462306a36Sopenharmony_ci * Return:
39562306a36Sopenharmony_ci * * 0		- OK to init all IRQ handlers
39662306a36Sopenharmony_ci * * -EINVAL	- Fail to request IRQ
39762306a36Sopenharmony_ci */
39862306a36Sopenharmony_cistatic int sf_pdma_irq_init(struct platform_device *pdev, struct sf_pdma *pdma)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	int irq, r, i;
40162306a36Sopenharmony_ci	struct sf_pdma_chan *chan;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	for (i = 0; i < pdma->n_chans; i++) {
40462306a36Sopenharmony_ci		chan = &pdma->chans[i];
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		irq = platform_get_irq(pdev, i * 2);
40762306a36Sopenharmony_ci		if (irq < 0)
40862306a36Sopenharmony_ci			return -EINVAL;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		r = devm_request_irq(&pdev->dev, irq, sf_pdma_done_isr, 0,
41162306a36Sopenharmony_ci				     dev_name(&pdev->dev), (void *)chan);
41262306a36Sopenharmony_ci		if (r) {
41362306a36Sopenharmony_ci			dev_err(&pdev->dev, "Fail to attach done ISR: %d\n", r);
41462306a36Sopenharmony_ci			return -EINVAL;
41562306a36Sopenharmony_ci		}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		chan->txirq = irq;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		irq = platform_get_irq(pdev, (i * 2) + 1);
42062306a36Sopenharmony_ci		if (irq < 0)
42162306a36Sopenharmony_ci			return -EINVAL;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		r = devm_request_irq(&pdev->dev, irq, sf_pdma_err_isr, 0,
42462306a36Sopenharmony_ci				     dev_name(&pdev->dev), (void *)chan);
42562306a36Sopenharmony_ci		if (r) {
42662306a36Sopenharmony_ci			dev_err(&pdev->dev, "Fail to attach err ISR: %d\n", r);
42762306a36Sopenharmony_ci			return -EINVAL;
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		chan->errirq = irq;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	return 0;
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci/**
43762306a36Sopenharmony_ci * sf_pdma_setup_chans() - Init settings of each channel
43862306a36Sopenharmony_ci * @pdma: pointer of PDMA engine. Caller should check NULL
43962306a36Sopenharmony_ci *
44062306a36Sopenharmony_ci * Initialize all data structure and register base. Caller should make sure
44162306a36Sopenharmony_ci * the pointer passed in are non-NULL. This function should be called only
44262306a36Sopenharmony_ci * one time during the device probe.
44362306a36Sopenharmony_ci *
44462306a36Sopenharmony_ci * Context: Any context.
44562306a36Sopenharmony_ci *
44662306a36Sopenharmony_ci * Return: none
44762306a36Sopenharmony_ci */
44862306a36Sopenharmony_cistatic void sf_pdma_setup_chans(struct sf_pdma *pdma)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	int i;
45162306a36Sopenharmony_ci	struct sf_pdma_chan *chan;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	INIT_LIST_HEAD(&pdma->dma_dev.channels);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	for (i = 0; i < pdma->n_chans; i++) {
45662306a36Sopenharmony_ci		chan = &pdma->chans[i];
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		chan->regs.ctrl =
45962306a36Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_CTRL;
46062306a36Sopenharmony_ci		chan->regs.xfer_type =
46162306a36Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_XFER_TYPE;
46262306a36Sopenharmony_ci		chan->regs.xfer_size =
46362306a36Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_XFER_SIZE;
46462306a36Sopenharmony_ci		chan->regs.dst_addr =
46562306a36Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_DST_ADDR;
46662306a36Sopenharmony_ci		chan->regs.src_addr =
46762306a36Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_SRC_ADDR;
46862306a36Sopenharmony_ci		chan->regs.act_type =
46962306a36Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_ACT_TYPE;
47062306a36Sopenharmony_ci		chan->regs.residue =
47162306a36Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_REMAINING_BYTE;
47262306a36Sopenharmony_ci		chan->regs.cur_dst_addr =
47362306a36Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_CUR_DST_ADDR;
47462306a36Sopenharmony_ci		chan->regs.cur_src_addr =
47562306a36Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_CUR_SRC_ADDR;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		chan->pdma = pdma;
47862306a36Sopenharmony_ci		chan->pm_state = RUNNING;
47962306a36Sopenharmony_ci		chan->slave_id = i;
48062306a36Sopenharmony_ci		chan->xfer_err = false;
48162306a36Sopenharmony_ci		spin_lock_init(&chan->lock);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		chan->vchan.desc_free = sf_pdma_free_desc;
48462306a36Sopenharmony_ci		vchan_init(&chan->vchan, &pdma->dma_dev);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci		writel(PDMA_CLEAR_CTRL, chan->regs.ctrl);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		tasklet_setup(&chan->done_tasklet, sf_pdma_donebh_tasklet);
48962306a36Sopenharmony_ci		tasklet_setup(&chan->err_tasklet, sf_pdma_errbh_tasklet);
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic int sf_pdma_probe(struct platform_device *pdev)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct sf_pdma *pdma;
49662306a36Sopenharmony_ci	int ret, n_chans;
49762306a36Sopenharmony_ci	const enum dma_slave_buswidth widths =
49862306a36Sopenharmony_ci		DMA_SLAVE_BUSWIDTH_1_BYTE | DMA_SLAVE_BUSWIDTH_2_BYTES |
49962306a36Sopenharmony_ci		DMA_SLAVE_BUSWIDTH_4_BYTES | DMA_SLAVE_BUSWIDTH_8_BYTES |
50062306a36Sopenharmony_ci		DMA_SLAVE_BUSWIDTH_16_BYTES | DMA_SLAVE_BUSWIDTH_32_BYTES |
50162306a36Sopenharmony_ci		DMA_SLAVE_BUSWIDTH_64_BYTES;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	ret = of_property_read_u32(pdev->dev.of_node, "dma-channels", &n_chans);
50462306a36Sopenharmony_ci	if (ret) {
50562306a36Sopenharmony_ci		/* backwards-compatibility for no dma-channels property */
50662306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "set number of channels to default value: 4\n");
50762306a36Sopenharmony_ci		n_chans = PDMA_MAX_NR_CH;
50862306a36Sopenharmony_ci	} else if (n_chans > PDMA_MAX_NR_CH) {
50962306a36Sopenharmony_ci		dev_err(&pdev->dev, "the number of channels exceeds the maximum\n");
51062306a36Sopenharmony_ci		return -EINVAL;
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	pdma = devm_kzalloc(&pdev->dev, struct_size(pdma, chans, n_chans),
51462306a36Sopenharmony_ci			    GFP_KERNEL);
51562306a36Sopenharmony_ci	if (!pdma)
51662306a36Sopenharmony_ci		return -ENOMEM;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	pdma->n_chans = n_chans;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	pdma->membase = devm_platform_ioremap_resource(pdev, 0);
52162306a36Sopenharmony_ci	if (IS_ERR(pdma->membase))
52262306a36Sopenharmony_ci		return PTR_ERR(pdma->membase);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	ret = sf_pdma_irq_init(pdev, pdma);
52562306a36Sopenharmony_ci	if (ret)
52662306a36Sopenharmony_ci		return ret;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	sf_pdma_setup_chans(pdma);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	pdma->dma_dev.dev = &pdev->dev;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Setup capability */
53362306a36Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, pdma->dma_dev.cap_mask);
53462306a36Sopenharmony_ci	pdma->dma_dev.copy_align = 2;
53562306a36Sopenharmony_ci	pdma->dma_dev.src_addr_widths = widths;
53662306a36Sopenharmony_ci	pdma->dma_dev.dst_addr_widths = widths;
53762306a36Sopenharmony_ci	pdma->dma_dev.directions = BIT(DMA_MEM_TO_MEM);
53862306a36Sopenharmony_ci	pdma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
53962306a36Sopenharmony_ci	pdma->dma_dev.descriptor_reuse = true;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	/* Setup DMA APIs */
54262306a36Sopenharmony_ci	pdma->dma_dev.device_alloc_chan_resources =
54362306a36Sopenharmony_ci		sf_pdma_alloc_chan_resources;
54462306a36Sopenharmony_ci	pdma->dma_dev.device_free_chan_resources =
54562306a36Sopenharmony_ci		sf_pdma_free_chan_resources;
54662306a36Sopenharmony_ci	pdma->dma_dev.device_tx_status = sf_pdma_tx_status;
54762306a36Sopenharmony_ci	pdma->dma_dev.device_prep_dma_memcpy = sf_pdma_prep_dma_memcpy;
54862306a36Sopenharmony_ci	pdma->dma_dev.device_config = sf_pdma_slave_config;
54962306a36Sopenharmony_ci	pdma->dma_dev.device_terminate_all = sf_pdma_terminate_all;
55062306a36Sopenharmony_ci	pdma->dma_dev.device_issue_pending = sf_pdma_issue_pending;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	platform_set_drvdata(pdev, pdma);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
55562306a36Sopenharmony_ci	if (ret)
55662306a36Sopenharmony_ci		dev_warn(&pdev->dev,
55762306a36Sopenharmony_ci			 "Failed to set DMA mask. Fall back to default.\n");
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	ret = dma_async_device_register(&pdma->dma_dev);
56062306a36Sopenharmony_ci	if (ret) {
56162306a36Sopenharmony_ci		dev_err(&pdev->dev,
56262306a36Sopenharmony_ci			"Can't register SiFive Platform DMA. (%d)\n", ret);
56362306a36Sopenharmony_ci		return ret;
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	return 0;
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic int sf_pdma_remove(struct platform_device *pdev)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	struct sf_pdma *pdma = platform_get_drvdata(pdev);
57262306a36Sopenharmony_ci	struct sf_pdma_chan *ch;
57362306a36Sopenharmony_ci	int i;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	for (i = 0; i < pdma->n_chans; i++) {
57662306a36Sopenharmony_ci		ch = &pdma->chans[i];
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci		devm_free_irq(&pdev->dev, ch->txirq, ch);
57962306a36Sopenharmony_ci		devm_free_irq(&pdev->dev, ch->errirq, ch);
58062306a36Sopenharmony_ci		list_del(&ch->vchan.chan.device_node);
58162306a36Sopenharmony_ci		tasklet_kill(&ch->vchan.task);
58262306a36Sopenharmony_ci		tasklet_kill(&ch->done_tasklet);
58362306a36Sopenharmony_ci		tasklet_kill(&ch->err_tasklet);
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	dma_async_device_unregister(&pdma->dma_dev);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	return 0;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cistatic const struct of_device_id sf_pdma_dt_ids[] = {
59262306a36Sopenharmony_ci	{ .compatible = "sifive,fu540-c000-pdma" },
59362306a36Sopenharmony_ci	{ .compatible = "sifive,pdma0" },
59462306a36Sopenharmony_ci	{},
59562306a36Sopenharmony_ci};
59662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sf_pdma_dt_ids);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic struct platform_driver sf_pdma_driver = {
59962306a36Sopenharmony_ci	.probe		= sf_pdma_probe,
60062306a36Sopenharmony_ci	.remove		= sf_pdma_remove,
60162306a36Sopenharmony_ci	.driver		= {
60262306a36Sopenharmony_ci		.name	= "sf-pdma",
60362306a36Sopenharmony_ci		.of_match_table = sf_pdma_dt_ids,
60462306a36Sopenharmony_ci	},
60562306a36Sopenharmony_ci};
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic int __init sf_pdma_init(void)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	return platform_driver_register(&sf_pdma_driver);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic void __exit sf_pdma_exit(void)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	platform_driver_unregister(&sf_pdma_driver);
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci/* do early init */
61862306a36Sopenharmony_cisubsys_initcall(sf_pdma_init);
61962306a36Sopenharmony_cimodule_exit(sf_pdma_exit);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
62262306a36Sopenharmony_ciMODULE_DESCRIPTION("SiFive Platform DMA driver");
62362306a36Sopenharmony_ciMODULE_AUTHOR("Green Wan <green.wan@sifive.com>");
624