18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SiFive FU540 Platform DMA driver
48c2ecf20Sopenharmony_ci * Copyright (C) 2019 SiFive
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Based partially on:
78c2ecf20Sopenharmony_ci * - drivers/dma/fsl-edma.c
88c2ecf20Sopenharmony_ci * - drivers/dma/dw-edma/
98c2ecf20Sopenharmony_ci * - drivers/dma/pxa-dma.c
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * See the following sources for further documentation:
128c2ecf20Sopenharmony_ci * - Chapter 12 "Platform DMA Engine (PDMA)" of
138c2ecf20Sopenharmony_ci *   SiFive FU540-C000 v1.0
148c2ecf20Sopenharmony_ci *   https://static.dev.sifive.com/FU540-C000-v1.0.pdf
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/device.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h>
218c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
228c2ecf20Sopenharmony_ci#include <linux/of.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "sf-pdma.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#ifndef readq
288c2ecf20Sopenharmony_cistatic inline unsigned long long readq(void __iomem *addr)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	return readl(addr) | (((unsigned long long)readl(addr + 4)) << 32LL);
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci#endif
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#ifndef writeq
358c2ecf20Sopenharmony_cistatic inline void writeq(unsigned long long v, void __iomem *addr)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	writel(lower_32_bits(v), addr);
388c2ecf20Sopenharmony_ci	writel(upper_32_bits(v), addr + 4);
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci#endif
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic inline struct sf_pdma_chan *to_sf_pdma_chan(struct dma_chan *dchan)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	return container_of(dchan, struct sf_pdma_chan, vchan.chan);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic inline struct sf_pdma_desc *to_sf_pdma_desc(struct virt_dma_desc *vd)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	return container_of(vd, struct sf_pdma_desc, vdesc);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic struct sf_pdma_desc *sf_pdma_alloc_desc(struct sf_pdma_chan *chan)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	struct sf_pdma_desc *desc;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
578c2ecf20Sopenharmony_ci	if (!desc)
588c2ecf20Sopenharmony_ci		return NULL;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	desc->chan = chan;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return desc;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic void sf_pdma_fill_desc(struct sf_pdma_desc *desc,
668c2ecf20Sopenharmony_ci			      u64 dst, u64 src, u64 size)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	desc->xfer_type = PDMA_FULL_SPEED;
698c2ecf20Sopenharmony_ci	desc->xfer_size = size;
708c2ecf20Sopenharmony_ci	desc->dst_addr = dst;
718c2ecf20Sopenharmony_ci	desc->src_addr = src;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic void sf_pdma_disclaim_chan(struct sf_pdma_chan *chan)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	writel(PDMA_CLEAR_CTRL, regs->ctrl);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
828c2ecf20Sopenharmony_cisf_pdma_prep_dma_memcpy(struct dma_chan *dchan,	dma_addr_t dest, dma_addr_t src,
838c2ecf20Sopenharmony_ci			size_t len, unsigned long flags)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
868c2ecf20Sopenharmony_ci	struct sf_pdma_desc *desc;
878c2ecf20Sopenharmony_ci	unsigned long iflags;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (chan && (!len || !dest || !src)) {
908c2ecf20Sopenharmony_ci		dev_err(chan->pdma->dma_dev.dev,
918c2ecf20Sopenharmony_ci			"Please check dma len, dest, src!\n");
928c2ecf20Sopenharmony_ci		return NULL;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	desc = sf_pdma_alloc_desc(chan);
968c2ecf20Sopenharmony_ci	if (!desc)
978c2ecf20Sopenharmony_ci		return NULL;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	desc->in_use = true;
1008c2ecf20Sopenharmony_ci	desc->dirn = DMA_MEM_TO_MEM;
1018c2ecf20Sopenharmony_ci	desc->async_tx = vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->vchan.lock, iflags);
1048c2ecf20Sopenharmony_ci	sf_pdma_fill_desc(desc, dest, src, len);
1058c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->vchan.lock, iflags);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return desc->async_tx;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int sf_pdma_slave_config(struct dma_chan *dchan,
1118c2ecf20Sopenharmony_ci				struct dma_slave_config *cfg)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	memcpy(&chan->cfg, cfg, sizeof(*cfg));
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return 0;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int sf_pdma_alloc_chan_resources(struct dma_chan *dchan)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
1238c2ecf20Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	dma_cookie_init(dchan);
1268c2ecf20Sopenharmony_ci	writel(PDMA_CLAIM_MASK, regs->ctrl);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return 0;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic void sf_pdma_disable_request(struct sf_pdma_chan *chan)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	writel(readl(regs->ctrl) & ~PDMA_RUN_MASK, regs->ctrl);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic void sf_pdma_free_chan_resources(struct dma_chan *dchan)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
1418c2ecf20Sopenharmony_ci	unsigned long flags;
1428c2ecf20Sopenharmony_ci	LIST_HEAD(head);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->vchan.lock, flags);
1458c2ecf20Sopenharmony_ci	sf_pdma_disable_request(chan);
1468c2ecf20Sopenharmony_ci	kfree(chan->desc);
1478c2ecf20Sopenharmony_ci	chan->desc = NULL;
1488c2ecf20Sopenharmony_ci	vchan_get_all_descriptors(&chan->vchan, &head);
1498c2ecf20Sopenharmony_ci	sf_pdma_disclaim_chan(chan);
1508c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->vchan.lock, flags);
1518c2ecf20Sopenharmony_ci	vchan_dma_desc_free_list(&chan->vchan, &head);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic size_t sf_pdma_desc_residue(struct sf_pdma_chan *chan,
1558c2ecf20Sopenharmony_ci				   dma_cookie_t cookie)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct virt_dma_desc *vd = NULL;
1588c2ecf20Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
1598c2ecf20Sopenharmony_ci	unsigned long flags;
1608c2ecf20Sopenharmony_ci	u64 residue = 0;
1618c2ecf20Sopenharmony_ci	struct sf_pdma_desc *desc;
1628c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *tx = NULL;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->vchan.lock, flags);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	list_for_each_entry(vd, &chan->vchan.desc_submitted, node)
1678c2ecf20Sopenharmony_ci		if (vd->tx.cookie == cookie)
1688c2ecf20Sopenharmony_ci			tx = &vd->tx;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (!tx)
1718c2ecf20Sopenharmony_ci		goto out;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (cookie == tx->chan->completed_cookie)
1748c2ecf20Sopenharmony_ci		goto out;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (cookie == tx->cookie) {
1778c2ecf20Sopenharmony_ci		residue = readq(regs->residue);
1788c2ecf20Sopenharmony_ci	} else {
1798c2ecf20Sopenharmony_ci		vd = vchan_find_desc(&chan->vchan, cookie);
1808c2ecf20Sopenharmony_ci		if (!vd)
1818c2ecf20Sopenharmony_ci			goto out;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci		desc = to_sf_pdma_desc(vd);
1848c2ecf20Sopenharmony_ci		residue = desc->xfer_size;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ciout:
1888c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->vchan.lock, flags);
1898c2ecf20Sopenharmony_ci	return residue;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic enum dma_status
1938c2ecf20Sopenharmony_cisf_pdma_tx_status(struct dma_chan *dchan,
1948c2ecf20Sopenharmony_ci		  dma_cookie_t cookie,
1958c2ecf20Sopenharmony_ci		  struct dma_tx_state *txstate)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
1988c2ecf20Sopenharmony_ci	enum dma_status status;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	status = dma_cookie_status(dchan, cookie, txstate);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (txstate && status != DMA_ERROR)
2038c2ecf20Sopenharmony_ci		dma_set_residue(txstate, sf_pdma_desc_residue(chan, cookie));
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return status;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic int sf_pdma_terminate_all(struct dma_chan *dchan)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
2118c2ecf20Sopenharmony_ci	unsigned long flags;
2128c2ecf20Sopenharmony_ci	LIST_HEAD(head);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->vchan.lock, flags);
2158c2ecf20Sopenharmony_ci	sf_pdma_disable_request(chan);
2168c2ecf20Sopenharmony_ci	kfree(chan->desc);
2178c2ecf20Sopenharmony_ci	chan->desc = NULL;
2188c2ecf20Sopenharmony_ci	chan->xfer_err = false;
2198c2ecf20Sopenharmony_ci	vchan_get_all_descriptors(&chan->vchan, &head);
2208c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->vchan.lock, flags);
2218c2ecf20Sopenharmony_ci	vchan_dma_desc_free_list(&chan->vchan, &head);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return 0;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic void sf_pdma_enable_request(struct sf_pdma_chan *chan)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
2298c2ecf20Sopenharmony_ci	u32 v;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	v = PDMA_CLAIM_MASK |
2328c2ecf20Sopenharmony_ci		PDMA_ENABLE_DONE_INT_MASK |
2338c2ecf20Sopenharmony_ci		PDMA_ENABLE_ERR_INT_MASK |
2348c2ecf20Sopenharmony_ci		PDMA_RUN_MASK;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	writel(v, regs->ctrl);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic struct sf_pdma_desc *sf_pdma_get_first_pending_desc(struct sf_pdma_chan *chan)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	struct virt_dma_chan *vchan = &chan->vchan;
2428c2ecf20Sopenharmony_ci	struct virt_dma_desc *vdesc;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (list_empty(&vchan->desc_issued))
2458c2ecf20Sopenharmony_ci		return NULL;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	vdesc = list_first_entry(&vchan->desc_issued, struct virt_dma_desc, node);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	return container_of(vdesc, struct sf_pdma_desc, vdesc);
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic void sf_pdma_xfer_desc(struct sf_pdma_chan *chan)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	struct sf_pdma_desc *desc = chan->desc;
2558c2ecf20Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (!desc) {
2588c2ecf20Sopenharmony_ci		dev_err(chan->pdma->dma_dev.dev, "NULL desc.\n");
2598c2ecf20Sopenharmony_ci		return;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	writel(desc->xfer_type, regs->xfer_type);
2638c2ecf20Sopenharmony_ci	writeq(desc->xfer_size, regs->xfer_size);
2648c2ecf20Sopenharmony_ci	writeq(desc->dst_addr, regs->dst_addr);
2658c2ecf20Sopenharmony_ci	writeq(desc->src_addr, regs->src_addr);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	chan->desc = desc;
2688c2ecf20Sopenharmony_ci	chan->status = DMA_IN_PROGRESS;
2698c2ecf20Sopenharmony_ci	sf_pdma_enable_request(chan);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic void sf_pdma_issue_pending(struct dma_chan *dchan)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan = to_sf_pdma_chan(dchan);
2758c2ecf20Sopenharmony_ci	unsigned long flags;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->vchan.lock, flags);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (!chan->desc && vchan_issue_pending(&chan->vchan)) {
2808c2ecf20Sopenharmony_ci		/* vchan_issue_pending has made a check that desc in not NULL */
2818c2ecf20Sopenharmony_ci		chan->desc = sf_pdma_get_first_pending_desc(chan);
2828c2ecf20Sopenharmony_ci		sf_pdma_xfer_desc(chan);
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->vchan.lock, flags);
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic void sf_pdma_free_desc(struct virt_dma_desc *vdesc)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	struct sf_pdma_desc *desc;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	desc = to_sf_pdma_desc(vdesc);
2938c2ecf20Sopenharmony_ci	desc->in_use = false;
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic void sf_pdma_donebh_tasklet(struct tasklet_struct *t)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan = from_tasklet(chan, t, done_tasklet);
2998c2ecf20Sopenharmony_ci	unsigned long flags;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
3028c2ecf20Sopenharmony_ci	if (chan->xfer_err) {
3038c2ecf20Sopenharmony_ci		chan->retries = MAX_RETRY;
3048c2ecf20Sopenharmony_ci		chan->status = DMA_COMPLETE;
3058c2ecf20Sopenharmony_ci		chan->xfer_err = false;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->vchan.lock, flags);
3108c2ecf20Sopenharmony_ci	list_del(&chan->desc->vdesc.node);
3118c2ecf20Sopenharmony_ci	vchan_cookie_complete(&chan->desc->vdesc);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	chan->desc = sf_pdma_get_first_pending_desc(chan);
3148c2ecf20Sopenharmony_ci	if (chan->desc)
3158c2ecf20Sopenharmony_ci		sf_pdma_xfer_desc(chan);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->vchan.lock, flags);
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic void sf_pdma_errbh_tasklet(struct tasklet_struct *t)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan = from_tasklet(chan, t, err_tasklet);
3238c2ecf20Sopenharmony_ci	struct sf_pdma_desc *desc = chan->desc;
3248c2ecf20Sopenharmony_ci	unsigned long flags;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
3278c2ecf20Sopenharmony_ci	if (chan->retries <= 0) {
3288c2ecf20Sopenharmony_ci		/* fail to recover */
3298c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chan->lock, flags);
3308c2ecf20Sopenharmony_ci		dmaengine_desc_get_callback_invoke(desc->async_tx, NULL);
3318c2ecf20Sopenharmony_ci	} else {
3328c2ecf20Sopenharmony_ci		/* retry */
3338c2ecf20Sopenharmony_ci		chan->retries--;
3348c2ecf20Sopenharmony_ci		chan->xfer_err = true;
3358c2ecf20Sopenharmony_ci		chan->status = DMA_ERROR;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		sf_pdma_enable_request(chan);
3388c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chan->lock, flags);
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic irqreturn_t sf_pdma_done_isr(int irq, void *dev_id)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan = dev_id;
3458c2ecf20Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
3468c2ecf20Sopenharmony_ci	unsigned long flags;
3478c2ecf20Sopenharmony_ci	u64 residue;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->vchan.lock, flags);
3508c2ecf20Sopenharmony_ci	writel((readl(regs->ctrl)) & ~PDMA_DONE_STATUS_MASK, regs->ctrl);
3518c2ecf20Sopenharmony_ci	residue = readq(regs->residue);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if (!residue) {
3548c2ecf20Sopenharmony_ci		tasklet_hi_schedule(&chan->done_tasklet);
3558c2ecf20Sopenharmony_ci	} else {
3568c2ecf20Sopenharmony_ci		/* submit next trascatioin if possible */
3578c2ecf20Sopenharmony_ci		struct sf_pdma_desc *desc = chan->desc;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci		desc->src_addr += desc->xfer_size - residue;
3608c2ecf20Sopenharmony_ci		desc->dst_addr += desc->xfer_size - residue;
3618c2ecf20Sopenharmony_ci		desc->xfer_size = residue;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci		sf_pdma_xfer_desc(chan);
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->vchan.lock, flags);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic irqreturn_t sf_pdma_err_isr(int irq, void *dev_id)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan = dev_id;
3748c2ecf20Sopenharmony_ci	struct pdma_regs *regs = &chan->regs;
3758c2ecf20Sopenharmony_ci	unsigned long flags;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chan->lock, flags);
3788c2ecf20Sopenharmony_ci	writel((readl(regs->ctrl)) & ~PDMA_ERR_STATUS_MASK, regs->ctrl);
3798c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chan->lock, flags);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	tasklet_schedule(&chan->err_tasklet);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci/**
3878c2ecf20Sopenharmony_ci * sf_pdma_irq_init() - Init PDMA IRQ Handlers
3888c2ecf20Sopenharmony_ci * @pdev: pointer of platform_device
3898c2ecf20Sopenharmony_ci * @pdma: pointer of PDMA engine. Caller should check NULL
3908c2ecf20Sopenharmony_ci *
3918c2ecf20Sopenharmony_ci * Initialize DONE and ERROR interrupt handler for 4 channels. Caller should
3928c2ecf20Sopenharmony_ci * make sure the pointer passed in are non-NULL. This function should be called
3938c2ecf20Sopenharmony_ci * only one time during the device probe.
3948c2ecf20Sopenharmony_ci *
3958c2ecf20Sopenharmony_ci * Context: Any context.
3968c2ecf20Sopenharmony_ci *
3978c2ecf20Sopenharmony_ci * Return:
3988c2ecf20Sopenharmony_ci * * 0		- OK to init all IRQ handlers
3998c2ecf20Sopenharmony_ci * * -EINVAL	- Fail to request IRQ
4008c2ecf20Sopenharmony_ci */
4018c2ecf20Sopenharmony_cistatic int sf_pdma_irq_init(struct platform_device *pdev, struct sf_pdma *pdma)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	int irq, r, i;
4048c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	for (i = 0; i < pdma->n_chans; i++) {
4078c2ecf20Sopenharmony_ci		chan = &pdma->chans[i];
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci		irq = platform_get_irq(pdev, i * 2);
4108c2ecf20Sopenharmony_ci		if (irq < 0) {
4118c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "ch(%d) Can't get done irq.\n", i);
4128c2ecf20Sopenharmony_ci			return -EINVAL;
4138c2ecf20Sopenharmony_ci		}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci		r = devm_request_irq(&pdev->dev, irq, sf_pdma_done_isr, 0,
4168c2ecf20Sopenharmony_ci				     dev_name(&pdev->dev), (void *)chan);
4178c2ecf20Sopenharmony_ci		if (r) {
4188c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Fail to attach done ISR: %d\n", r);
4198c2ecf20Sopenharmony_ci			return -EINVAL;
4208c2ecf20Sopenharmony_ci		}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		chan->txirq = irq;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci		irq = platform_get_irq(pdev, (i * 2) + 1);
4258c2ecf20Sopenharmony_ci		if (irq < 0) {
4268c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "ch(%d) Can't get err irq.\n", i);
4278c2ecf20Sopenharmony_ci			return -EINVAL;
4288c2ecf20Sopenharmony_ci		}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci		r = devm_request_irq(&pdev->dev, irq, sf_pdma_err_isr, 0,
4318c2ecf20Sopenharmony_ci				     dev_name(&pdev->dev), (void *)chan);
4328c2ecf20Sopenharmony_ci		if (r) {
4338c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Fail to attach err ISR: %d\n", r);
4348c2ecf20Sopenharmony_ci			return -EINVAL;
4358c2ecf20Sopenharmony_ci		}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		chan->errirq = irq;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	return 0;
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci/**
4448c2ecf20Sopenharmony_ci * sf_pdma_setup_chans() - Init settings of each channel
4458c2ecf20Sopenharmony_ci * @pdma: pointer of PDMA engine. Caller should check NULL
4468c2ecf20Sopenharmony_ci *
4478c2ecf20Sopenharmony_ci * Initialize all data structure and register base. Caller should make sure
4488c2ecf20Sopenharmony_ci * the pointer passed in are non-NULL. This function should be called only
4498c2ecf20Sopenharmony_ci * one time during the device probe.
4508c2ecf20Sopenharmony_ci *
4518c2ecf20Sopenharmony_ci * Context: Any context.
4528c2ecf20Sopenharmony_ci *
4538c2ecf20Sopenharmony_ci * Return: none
4548c2ecf20Sopenharmony_ci */
4558c2ecf20Sopenharmony_cistatic void sf_pdma_setup_chans(struct sf_pdma *pdma)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	int i;
4588c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&pdma->dma_dev.channels);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	for (i = 0; i < pdma->n_chans; i++) {
4638c2ecf20Sopenharmony_ci		chan = &pdma->chans[i];
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci		chan->regs.ctrl =
4668c2ecf20Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_CTRL;
4678c2ecf20Sopenharmony_ci		chan->regs.xfer_type =
4688c2ecf20Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_XFER_TYPE;
4698c2ecf20Sopenharmony_ci		chan->regs.xfer_size =
4708c2ecf20Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_XFER_SIZE;
4718c2ecf20Sopenharmony_ci		chan->regs.dst_addr =
4728c2ecf20Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_DST_ADDR;
4738c2ecf20Sopenharmony_ci		chan->regs.src_addr =
4748c2ecf20Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_SRC_ADDR;
4758c2ecf20Sopenharmony_ci		chan->regs.act_type =
4768c2ecf20Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_ACT_TYPE;
4778c2ecf20Sopenharmony_ci		chan->regs.residue =
4788c2ecf20Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_REMAINING_BYTE;
4798c2ecf20Sopenharmony_ci		chan->regs.cur_dst_addr =
4808c2ecf20Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_CUR_DST_ADDR;
4818c2ecf20Sopenharmony_ci		chan->regs.cur_src_addr =
4828c2ecf20Sopenharmony_ci			SF_PDMA_REG_BASE(i) + PDMA_CUR_SRC_ADDR;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci		chan->pdma = pdma;
4858c2ecf20Sopenharmony_ci		chan->pm_state = RUNNING;
4868c2ecf20Sopenharmony_ci		chan->slave_id = i;
4878c2ecf20Sopenharmony_ci		chan->xfer_err = false;
4888c2ecf20Sopenharmony_ci		spin_lock_init(&chan->lock);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		chan->vchan.desc_free = sf_pdma_free_desc;
4918c2ecf20Sopenharmony_ci		vchan_init(&chan->vchan, &pdma->dma_dev);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci		writel(PDMA_CLEAR_CTRL, chan->regs.ctrl);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		tasklet_setup(&chan->done_tasklet, sf_pdma_donebh_tasklet);
4968c2ecf20Sopenharmony_ci		tasklet_setup(&chan->err_tasklet, sf_pdma_errbh_tasklet);
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic int sf_pdma_probe(struct platform_device *pdev)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	struct sf_pdma *pdma;
5038c2ecf20Sopenharmony_ci	struct sf_pdma_chan *chan;
5048c2ecf20Sopenharmony_ci	struct resource *res;
5058c2ecf20Sopenharmony_ci	int len, chans;
5068c2ecf20Sopenharmony_ci	int ret;
5078c2ecf20Sopenharmony_ci	const enum dma_slave_buswidth widths =
5088c2ecf20Sopenharmony_ci		DMA_SLAVE_BUSWIDTH_1_BYTE | DMA_SLAVE_BUSWIDTH_2_BYTES |
5098c2ecf20Sopenharmony_ci		DMA_SLAVE_BUSWIDTH_4_BYTES | DMA_SLAVE_BUSWIDTH_8_BYTES |
5108c2ecf20Sopenharmony_ci		DMA_SLAVE_BUSWIDTH_16_BYTES | DMA_SLAVE_BUSWIDTH_32_BYTES |
5118c2ecf20Sopenharmony_ci		DMA_SLAVE_BUSWIDTH_64_BYTES;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	chans = PDMA_NR_CH;
5148c2ecf20Sopenharmony_ci	len = sizeof(*pdma) + sizeof(*chan) * chans;
5158c2ecf20Sopenharmony_ci	pdma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
5168c2ecf20Sopenharmony_ci	if (!pdma)
5178c2ecf20Sopenharmony_ci		return -ENOMEM;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	pdma->n_chans = chans;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
5228c2ecf20Sopenharmony_ci	pdma->membase = devm_ioremap_resource(&pdev->dev, res);
5238c2ecf20Sopenharmony_ci	if (IS_ERR(pdma->membase))
5248c2ecf20Sopenharmony_ci		return PTR_ERR(pdma->membase);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	ret = sf_pdma_irq_init(pdev, pdma);
5278c2ecf20Sopenharmony_ci	if (ret)
5288c2ecf20Sopenharmony_ci		return ret;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	sf_pdma_setup_chans(pdma);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	pdma->dma_dev.dev = &pdev->dev;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* Setup capability */
5358c2ecf20Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, pdma->dma_dev.cap_mask);
5368c2ecf20Sopenharmony_ci	pdma->dma_dev.copy_align = 2;
5378c2ecf20Sopenharmony_ci	pdma->dma_dev.src_addr_widths = widths;
5388c2ecf20Sopenharmony_ci	pdma->dma_dev.dst_addr_widths = widths;
5398c2ecf20Sopenharmony_ci	pdma->dma_dev.directions = BIT(DMA_MEM_TO_MEM);
5408c2ecf20Sopenharmony_ci	pdma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
5418c2ecf20Sopenharmony_ci	pdma->dma_dev.descriptor_reuse = true;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/* Setup DMA APIs */
5448c2ecf20Sopenharmony_ci	pdma->dma_dev.device_alloc_chan_resources =
5458c2ecf20Sopenharmony_ci		sf_pdma_alloc_chan_resources;
5468c2ecf20Sopenharmony_ci	pdma->dma_dev.device_free_chan_resources =
5478c2ecf20Sopenharmony_ci		sf_pdma_free_chan_resources;
5488c2ecf20Sopenharmony_ci	pdma->dma_dev.device_tx_status = sf_pdma_tx_status;
5498c2ecf20Sopenharmony_ci	pdma->dma_dev.device_prep_dma_memcpy = sf_pdma_prep_dma_memcpy;
5508c2ecf20Sopenharmony_ci	pdma->dma_dev.device_config = sf_pdma_slave_config;
5518c2ecf20Sopenharmony_ci	pdma->dma_dev.device_terminate_all = sf_pdma_terminate_all;
5528c2ecf20Sopenharmony_ci	pdma->dma_dev.device_issue_pending = sf_pdma_issue_pending;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, pdma);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
5578c2ecf20Sopenharmony_ci	if (ret)
5588c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev,
5598c2ecf20Sopenharmony_ci			 "Failed to set DMA mask. Fall back to default.\n");
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	ret = dma_async_device_register(&pdma->dma_dev);
5628c2ecf20Sopenharmony_ci	if (ret) {
5638c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
5648c2ecf20Sopenharmony_ci			"Can't register SiFive Platform DMA. (%d)\n", ret);
5658c2ecf20Sopenharmony_ci		return ret;
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	return 0;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic int sf_pdma_remove(struct platform_device *pdev)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct sf_pdma *pdma = platform_get_drvdata(pdev);
5748c2ecf20Sopenharmony_ci	struct sf_pdma_chan *ch;
5758c2ecf20Sopenharmony_ci	int i;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	for (i = 0; i < PDMA_NR_CH; i++) {
5788c2ecf20Sopenharmony_ci		ch = &pdma->chans[i];
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci		devm_free_irq(&pdev->dev, ch->txirq, ch);
5818c2ecf20Sopenharmony_ci		devm_free_irq(&pdev->dev, ch->errirq, ch);
5828c2ecf20Sopenharmony_ci		list_del(&ch->vchan.chan.device_node);
5838c2ecf20Sopenharmony_ci		tasklet_kill(&ch->vchan.task);
5848c2ecf20Sopenharmony_ci		tasklet_kill(&ch->done_tasklet);
5858c2ecf20Sopenharmony_ci		tasklet_kill(&ch->err_tasklet);
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	dma_async_device_unregister(&pdma->dma_dev);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	return 0;
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic const struct of_device_id sf_pdma_dt_ids[] = {
5948c2ecf20Sopenharmony_ci	{ .compatible = "sifive,fu540-c000-pdma" },
5958c2ecf20Sopenharmony_ci	{},
5968c2ecf20Sopenharmony_ci};
5978c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sf_pdma_dt_ids);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cistatic struct platform_driver sf_pdma_driver = {
6008c2ecf20Sopenharmony_ci	.probe		= sf_pdma_probe,
6018c2ecf20Sopenharmony_ci	.remove		= sf_pdma_remove,
6028c2ecf20Sopenharmony_ci	.driver		= {
6038c2ecf20Sopenharmony_ci		.name	= "sf-pdma",
6048c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(sf_pdma_dt_ids),
6058c2ecf20Sopenharmony_ci	},
6068c2ecf20Sopenharmony_ci};
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic int __init sf_pdma_init(void)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	return platform_driver_register(&sf_pdma_driver);
6118c2ecf20Sopenharmony_ci}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_cistatic void __exit sf_pdma_exit(void)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	platform_driver_unregister(&sf_pdma_driver);
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci/* do early init */
6198c2ecf20Sopenharmony_cisubsys_initcall(sf_pdma_init);
6208c2ecf20Sopenharmony_cimodule_exit(sf_pdma_exit);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
6238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SiFive Platform DMA driver");
6248c2ecf20Sopenharmony_ciMODULE_AUTHOR("Green Wan <green.wan@sifive.com>");
625