162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for the TXx9 SoC DMA Controller
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2009 Atsushi Nemoto
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/interrupt.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/scatterlist.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "dmaengine.h"
1762306a36Sopenharmony_ci#include "txx9dmac.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic struct txx9dmac_chan *to_txx9dmac_chan(struct dma_chan *chan)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	return container_of(chan, struct txx9dmac_chan, chan);
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic struct txx9dmac_cregs __iomem *__dma_regs(const struct txx9dmac_chan *dc)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	return dc->ch_regs;
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic struct txx9dmac_cregs32 __iomem *__dma_regs32(
3062306a36Sopenharmony_ci	const struct txx9dmac_chan *dc)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	return dc->ch_regs;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define channel64_readq(dc, name) \
3662306a36Sopenharmony_ci	__raw_readq(&(__dma_regs(dc)->name))
3762306a36Sopenharmony_ci#define channel64_writeq(dc, name, val) \
3862306a36Sopenharmony_ci	__raw_writeq((val), &(__dma_regs(dc)->name))
3962306a36Sopenharmony_ci#define channel64_readl(dc, name) \
4062306a36Sopenharmony_ci	__raw_readl(&(__dma_regs(dc)->name))
4162306a36Sopenharmony_ci#define channel64_writel(dc, name, val) \
4262306a36Sopenharmony_ci	__raw_writel((val), &(__dma_regs(dc)->name))
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define channel32_readl(dc, name) \
4562306a36Sopenharmony_ci	__raw_readl(&(__dma_regs32(dc)->name))
4662306a36Sopenharmony_ci#define channel32_writel(dc, name, val) \
4762306a36Sopenharmony_ci	__raw_writel((val), &(__dma_regs32(dc)->name))
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define channel_readq(dc, name) channel64_readq(dc, name)
5062306a36Sopenharmony_ci#define channel_writeq(dc, name, val) channel64_writeq(dc, name, val)
5162306a36Sopenharmony_ci#define channel_readl(dc, name) \
5262306a36Sopenharmony_ci	(is_dmac64(dc) ? \
5362306a36Sopenharmony_ci	 channel64_readl(dc, name) : channel32_readl(dc, name))
5462306a36Sopenharmony_ci#define channel_writel(dc, name, val) \
5562306a36Sopenharmony_ci	(is_dmac64(dc) ? \
5662306a36Sopenharmony_ci	 channel64_writel(dc, name, val) : channel32_writel(dc, name, val))
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic dma_addr_t channel64_read_CHAR(const struct txx9dmac_chan *dc)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	if (sizeof(__dma_regs(dc)->CHAR) == sizeof(u64))
6162306a36Sopenharmony_ci		return channel64_readq(dc, CHAR);
6262306a36Sopenharmony_ci	else
6362306a36Sopenharmony_ci		return channel64_readl(dc, CHAR);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void channel64_write_CHAR(const struct txx9dmac_chan *dc, dma_addr_t val)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	if (sizeof(__dma_regs(dc)->CHAR) == sizeof(u64))
6962306a36Sopenharmony_ci		channel64_writeq(dc, CHAR, val);
7062306a36Sopenharmony_ci	else
7162306a36Sopenharmony_ci		channel64_writel(dc, CHAR, val);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void channel64_clear_CHAR(const struct txx9dmac_chan *dc)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci#if defined(CONFIG_32BIT) && !defined(CONFIG_PHYS_ADDR_T_64BIT)
7762306a36Sopenharmony_ci	channel64_writel(dc, CHAR, 0);
7862306a36Sopenharmony_ci	channel64_writel(dc, __pad_CHAR, 0);
7962306a36Sopenharmony_ci#else
8062306a36Sopenharmony_ci	channel64_writeq(dc, CHAR, 0);
8162306a36Sopenharmony_ci#endif
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic dma_addr_t channel_read_CHAR(const struct txx9dmac_chan *dc)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	if (is_dmac64(dc))
8762306a36Sopenharmony_ci		return channel64_read_CHAR(dc);
8862306a36Sopenharmony_ci	else
8962306a36Sopenharmony_ci		return channel32_readl(dc, CHAR);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void channel_write_CHAR(const struct txx9dmac_chan *dc, dma_addr_t val)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	if (is_dmac64(dc))
9562306a36Sopenharmony_ci		channel64_write_CHAR(dc, val);
9662306a36Sopenharmony_ci	else
9762306a36Sopenharmony_ci		channel32_writel(dc, CHAR, val);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic struct txx9dmac_regs __iomem *__txx9dmac_regs(
10162306a36Sopenharmony_ci	const struct txx9dmac_dev *ddev)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	return ddev->regs;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic struct txx9dmac_regs32 __iomem *__txx9dmac_regs32(
10762306a36Sopenharmony_ci	const struct txx9dmac_dev *ddev)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	return ddev->regs;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci#define dma64_readl(ddev, name) \
11362306a36Sopenharmony_ci	__raw_readl(&(__txx9dmac_regs(ddev)->name))
11462306a36Sopenharmony_ci#define dma64_writel(ddev, name, val) \
11562306a36Sopenharmony_ci	__raw_writel((val), &(__txx9dmac_regs(ddev)->name))
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#define dma32_readl(ddev, name) \
11862306a36Sopenharmony_ci	__raw_readl(&(__txx9dmac_regs32(ddev)->name))
11962306a36Sopenharmony_ci#define dma32_writel(ddev, name, val) \
12062306a36Sopenharmony_ci	__raw_writel((val), &(__txx9dmac_regs32(ddev)->name))
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci#define dma_readl(ddev, name) \
12362306a36Sopenharmony_ci	(__is_dmac64(ddev) ? \
12462306a36Sopenharmony_ci	dma64_readl(ddev, name) : dma32_readl(ddev, name))
12562306a36Sopenharmony_ci#define dma_writel(ddev, name, val) \
12662306a36Sopenharmony_ci	(__is_dmac64(ddev) ? \
12762306a36Sopenharmony_ci	dma64_writel(ddev, name, val) : dma32_writel(ddev, name, val))
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic struct device *chan2dev(struct dma_chan *chan)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	return &chan->dev->device;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_cistatic struct device *chan2parent(struct dma_chan *chan)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	return chan->dev->device.parent;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic struct txx9dmac_desc *
13962306a36Sopenharmony_citxd_to_txx9dmac_desc(struct dma_async_tx_descriptor *txd)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	return container_of(txd, struct txx9dmac_desc, txd);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic dma_addr_t desc_read_CHAR(const struct txx9dmac_chan *dc,
14562306a36Sopenharmony_ci				 const struct txx9dmac_desc *desc)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	return is_dmac64(dc) ? desc->hwdesc.CHAR : desc->hwdesc32.CHAR;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic void desc_write_CHAR(const struct txx9dmac_chan *dc,
15162306a36Sopenharmony_ci			    struct txx9dmac_desc *desc, dma_addr_t val)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	if (is_dmac64(dc))
15462306a36Sopenharmony_ci		desc->hwdesc.CHAR = val;
15562306a36Sopenharmony_ci	else
15662306a36Sopenharmony_ci		desc->hwdesc32.CHAR = val;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#define TXX9_DMA_MAX_COUNT	0x04000000
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci#define TXX9_DMA_INITIAL_DESC_COUNT	64
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic struct txx9dmac_desc *txx9dmac_first_active(struct txx9dmac_chan *dc)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	return list_entry(dc->active_list.next,
16662306a36Sopenharmony_ci			  struct txx9dmac_desc, desc_node);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic struct txx9dmac_desc *txx9dmac_last_active(struct txx9dmac_chan *dc)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	return list_entry(dc->active_list.prev,
17262306a36Sopenharmony_ci			  struct txx9dmac_desc, desc_node);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic struct txx9dmac_desc *txx9dmac_first_queued(struct txx9dmac_chan *dc)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	return list_entry(dc->queue.next, struct txx9dmac_desc, desc_node);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic struct txx9dmac_desc *txx9dmac_last_child(struct txx9dmac_desc *desc)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	if (!list_empty(&desc->tx_list))
18362306a36Sopenharmony_ci		desc = list_entry(desc->tx_list.prev, typeof(*desc), desc_node);
18462306a36Sopenharmony_ci	return desc;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic dma_cookie_t txx9dmac_tx_submit(struct dma_async_tx_descriptor *tx);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic struct txx9dmac_desc *txx9dmac_desc_alloc(struct txx9dmac_chan *dc,
19062306a36Sopenharmony_ci						 gfp_t flags)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct txx9dmac_dev *ddev = dc->ddev;
19362306a36Sopenharmony_ci	struct txx9dmac_desc *desc;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	desc = kzalloc(sizeof(*desc), flags);
19662306a36Sopenharmony_ci	if (!desc)
19762306a36Sopenharmony_ci		return NULL;
19862306a36Sopenharmony_ci	INIT_LIST_HEAD(&desc->tx_list);
19962306a36Sopenharmony_ci	dma_async_tx_descriptor_init(&desc->txd, &dc->chan);
20062306a36Sopenharmony_ci	desc->txd.tx_submit = txx9dmac_tx_submit;
20162306a36Sopenharmony_ci	/* txd.flags will be overwritten in prep funcs */
20262306a36Sopenharmony_ci	desc->txd.flags = DMA_CTRL_ACK;
20362306a36Sopenharmony_ci	desc->txd.phys = dma_map_single(chan2parent(&dc->chan), &desc->hwdesc,
20462306a36Sopenharmony_ci					ddev->descsize, DMA_TO_DEVICE);
20562306a36Sopenharmony_ci	return desc;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic struct txx9dmac_desc *txx9dmac_desc_get(struct txx9dmac_chan *dc)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct txx9dmac_desc *desc, *_desc;
21162306a36Sopenharmony_ci	struct txx9dmac_desc *ret = NULL;
21262306a36Sopenharmony_ci	unsigned int i = 0;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	spin_lock_bh(&dc->lock);
21562306a36Sopenharmony_ci	list_for_each_entry_safe(desc, _desc, &dc->free_list, desc_node) {
21662306a36Sopenharmony_ci		if (async_tx_test_ack(&desc->txd)) {
21762306a36Sopenharmony_ci			list_del(&desc->desc_node);
21862306a36Sopenharmony_ci			ret = desc;
21962306a36Sopenharmony_ci			break;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci		dev_dbg(chan2dev(&dc->chan), "desc %p not ACKed\n", desc);
22262306a36Sopenharmony_ci		i++;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci	spin_unlock_bh(&dc->lock);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	dev_vdbg(chan2dev(&dc->chan), "scanned %u descriptors on freelist\n",
22762306a36Sopenharmony_ci		 i);
22862306a36Sopenharmony_ci	if (!ret) {
22962306a36Sopenharmony_ci		ret = txx9dmac_desc_alloc(dc, GFP_ATOMIC);
23062306a36Sopenharmony_ci		if (ret) {
23162306a36Sopenharmony_ci			spin_lock_bh(&dc->lock);
23262306a36Sopenharmony_ci			dc->descs_allocated++;
23362306a36Sopenharmony_ci			spin_unlock_bh(&dc->lock);
23462306a36Sopenharmony_ci		} else
23562306a36Sopenharmony_ci			dev_err(chan2dev(&dc->chan),
23662306a36Sopenharmony_ci				"not enough descriptors available\n");
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci	return ret;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic void txx9dmac_sync_desc_for_cpu(struct txx9dmac_chan *dc,
24262306a36Sopenharmony_ci				       struct txx9dmac_desc *desc)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct txx9dmac_dev *ddev = dc->ddev;
24562306a36Sopenharmony_ci	struct txx9dmac_desc *child;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	list_for_each_entry(child, &desc->tx_list, desc_node)
24862306a36Sopenharmony_ci		dma_sync_single_for_cpu(chan2parent(&dc->chan),
24962306a36Sopenharmony_ci				child->txd.phys, ddev->descsize,
25062306a36Sopenharmony_ci				DMA_TO_DEVICE);
25162306a36Sopenharmony_ci	dma_sync_single_for_cpu(chan2parent(&dc->chan),
25262306a36Sopenharmony_ci			desc->txd.phys, ddev->descsize,
25362306a36Sopenharmony_ci			DMA_TO_DEVICE);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/*
25762306a36Sopenharmony_ci * Move a descriptor, including any children, to the free list.
25862306a36Sopenharmony_ci * `desc' must not be on any lists.
25962306a36Sopenharmony_ci */
26062306a36Sopenharmony_cistatic void txx9dmac_desc_put(struct txx9dmac_chan *dc,
26162306a36Sopenharmony_ci			      struct txx9dmac_desc *desc)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	if (desc) {
26462306a36Sopenharmony_ci		struct txx9dmac_desc *child;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		txx9dmac_sync_desc_for_cpu(dc, desc);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		spin_lock_bh(&dc->lock);
26962306a36Sopenharmony_ci		list_for_each_entry(child, &desc->tx_list, desc_node)
27062306a36Sopenharmony_ci			dev_vdbg(chan2dev(&dc->chan),
27162306a36Sopenharmony_ci				 "moving child desc %p to freelist\n",
27262306a36Sopenharmony_ci				 child);
27362306a36Sopenharmony_ci		list_splice_init(&desc->tx_list, &dc->free_list);
27462306a36Sopenharmony_ci		dev_vdbg(chan2dev(&dc->chan), "moving desc %p to freelist\n",
27562306a36Sopenharmony_ci			 desc);
27662306a36Sopenharmony_ci		list_add(&desc->desc_node, &dc->free_list);
27762306a36Sopenharmony_ci		spin_unlock_bh(&dc->lock);
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci/*----------------------------------------------------------------------*/
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic void txx9dmac_dump_regs(struct txx9dmac_chan *dc)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	if (is_dmac64(dc))
28662306a36Sopenharmony_ci		dev_err(chan2dev(&dc->chan),
28762306a36Sopenharmony_ci			"  CHAR: %#llx SAR: %#llx DAR: %#llx CNTR: %#x"
28862306a36Sopenharmony_ci			" SAIR: %#x DAIR: %#x CCR: %#x CSR: %#x\n",
28962306a36Sopenharmony_ci			(u64)channel64_read_CHAR(dc),
29062306a36Sopenharmony_ci			channel64_readq(dc, SAR),
29162306a36Sopenharmony_ci			channel64_readq(dc, DAR),
29262306a36Sopenharmony_ci			channel64_readl(dc, CNTR),
29362306a36Sopenharmony_ci			channel64_readl(dc, SAIR),
29462306a36Sopenharmony_ci			channel64_readl(dc, DAIR),
29562306a36Sopenharmony_ci			channel64_readl(dc, CCR),
29662306a36Sopenharmony_ci			channel64_readl(dc, CSR));
29762306a36Sopenharmony_ci	else
29862306a36Sopenharmony_ci		dev_err(chan2dev(&dc->chan),
29962306a36Sopenharmony_ci			"  CHAR: %#x SAR: %#x DAR: %#x CNTR: %#x"
30062306a36Sopenharmony_ci			" SAIR: %#x DAIR: %#x CCR: %#x CSR: %#x\n",
30162306a36Sopenharmony_ci			channel32_readl(dc, CHAR),
30262306a36Sopenharmony_ci			channel32_readl(dc, SAR),
30362306a36Sopenharmony_ci			channel32_readl(dc, DAR),
30462306a36Sopenharmony_ci			channel32_readl(dc, CNTR),
30562306a36Sopenharmony_ci			channel32_readl(dc, SAIR),
30662306a36Sopenharmony_ci			channel32_readl(dc, DAIR),
30762306a36Sopenharmony_ci			channel32_readl(dc, CCR),
30862306a36Sopenharmony_ci			channel32_readl(dc, CSR));
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic void txx9dmac_reset_chan(struct txx9dmac_chan *dc)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	channel_writel(dc, CCR, TXX9_DMA_CCR_CHRST);
31462306a36Sopenharmony_ci	if (is_dmac64(dc)) {
31562306a36Sopenharmony_ci		channel64_clear_CHAR(dc);
31662306a36Sopenharmony_ci		channel_writeq(dc, SAR, 0);
31762306a36Sopenharmony_ci		channel_writeq(dc, DAR, 0);
31862306a36Sopenharmony_ci	} else {
31962306a36Sopenharmony_ci		channel_writel(dc, CHAR, 0);
32062306a36Sopenharmony_ci		channel_writel(dc, SAR, 0);
32162306a36Sopenharmony_ci		channel_writel(dc, DAR, 0);
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci	channel_writel(dc, CNTR, 0);
32462306a36Sopenharmony_ci	channel_writel(dc, SAIR, 0);
32562306a36Sopenharmony_ci	channel_writel(dc, DAIR, 0);
32662306a36Sopenharmony_ci	channel_writel(dc, CCR, 0);
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci/* Called with dc->lock held and bh disabled */
33062306a36Sopenharmony_cistatic void txx9dmac_dostart(struct txx9dmac_chan *dc,
33162306a36Sopenharmony_ci			     struct txx9dmac_desc *first)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct txx9dmac_slave *ds = dc->chan.private;
33462306a36Sopenharmony_ci	u32 sai, dai;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	dev_vdbg(chan2dev(&dc->chan), "dostart %u %p\n",
33762306a36Sopenharmony_ci		 first->txd.cookie, first);
33862306a36Sopenharmony_ci	/* ASSERT:  channel is idle */
33962306a36Sopenharmony_ci	if (channel_readl(dc, CSR) & TXX9_DMA_CSR_XFACT) {
34062306a36Sopenharmony_ci		dev_err(chan2dev(&dc->chan),
34162306a36Sopenharmony_ci			"BUG: Attempted to start non-idle channel\n");
34262306a36Sopenharmony_ci		txx9dmac_dump_regs(dc);
34362306a36Sopenharmony_ci		/* The tasklet will hopefully advance the queue... */
34462306a36Sopenharmony_ci		return;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (is_dmac64(dc)) {
34862306a36Sopenharmony_ci		channel64_writel(dc, CNTR, 0);
34962306a36Sopenharmony_ci		channel64_writel(dc, CSR, 0xffffffff);
35062306a36Sopenharmony_ci		if (ds) {
35162306a36Sopenharmony_ci			if (ds->tx_reg) {
35262306a36Sopenharmony_ci				sai = ds->reg_width;
35362306a36Sopenharmony_ci				dai = 0;
35462306a36Sopenharmony_ci			} else {
35562306a36Sopenharmony_ci				sai = 0;
35662306a36Sopenharmony_ci				dai = ds->reg_width;
35762306a36Sopenharmony_ci			}
35862306a36Sopenharmony_ci		} else {
35962306a36Sopenharmony_ci			sai = 8;
36062306a36Sopenharmony_ci			dai = 8;
36162306a36Sopenharmony_ci		}
36262306a36Sopenharmony_ci		channel64_writel(dc, SAIR, sai);
36362306a36Sopenharmony_ci		channel64_writel(dc, DAIR, dai);
36462306a36Sopenharmony_ci		/* All 64-bit DMAC supports SMPCHN */
36562306a36Sopenharmony_ci		channel64_writel(dc, CCR, dc->ccr);
36662306a36Sopenharmony_ci		/* Writing a non zero value to CHAR will assert XFACT */
36762306a36Sopenharmony_ci		channel64_write_CHAR(dc, first->txd.phys);
36862306a36Sopenharmony_ci	} else {
36962306a36Sopenharmony_ci		channel32_writel(dc, CNTR, 0);
37062306a36Sopenharmony_ci		channel32_writel(dc, CSR, 0xffffffff);
37162306a36Sopenharmony_ci		if (ds) {
37262306a36Sopenharmony_ci			if (ds->tx_reg) {
37362306a36Sopenharmony_ci				sai = ds->reg_width;
37462306a36Sopenharmony_ci				dai = 0;
37562306a36Sopenharmony_ci			} else {
37662306a36Sopenharmony_ci				sai = 0;
37762306a36Sopenharmony_ci				dai = ds->reg_width;
37862306a36Sopenharmony_ci			}
37962306a36Sopenharmony_ci		} else {
38062306a36Sopenharmony_ci			sai = 4;
38162306a36Sopenharmony_ci			dai = 4;
38262306a36Sopenharmony_ci		}
38362306a36Sopenharmony_ci		channel32_writel(dc, SAIR, sai);
38462306a36Sopenharmony_ci		channel32_writel(dc, DAIR, dai);
38562306a36Sopenharmony_ci		if (txx9_dma_have_SMPCHN()) {
38662306a36Sopenharmony_ci			channel32_writel(dc, CCR, dc->ccr);
38762306a36Sopenharmony_ci			/* Writing a non zero value to CHAR will assert XFACT */
38862306a36Sopenharmony_ci			channel32_writel(dc, CHAR, first->txd.phys);
38962306a36Sopenharmony_ci		} else {
39062306a36Sopenharmony_ci			channel32_writel(dc, CHAR, first->txd.phys);
39162306a36Sopenharmony_ci			channel32_writel(dc, CCR, dc->ccr);
39262306a36Sopenharmony_ci		}
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/*----------------------------------------------------------------------*/
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic void
39962306a36Sopenharmony_citxx9dmac_descriptor_complete(struct txx9dmac_chan *dc,
40062306a36Sopenharmony_ci			     struct txx9dmac_desc *desc)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct dmaengine_desc_callback cb;
40362306a36Sopenharmony_ci	struct dma_async_tx_descriptor *txd = &desc->txd;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	dev_vdbg(chan2dev(&dc->chan), "descriptor %u %p complete\n",
40662306a36Sopenharmony_ci		 txd->cookie, desc);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	dma_cookie_complete(txd);
40962306a36Sopenharmony_ci	dmaengine_desc_get_callback(txd, &cb);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	txx9dmac_sync_desc_for_cpu(dc, desc);
41262306a36Sopenharmony_ci	list_splice_init(&desc->tx_list, &dc->free_list);
41362306a36Sopenharmony_ci	list_move(&desc->desc_node, &dc->free_list);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	dma_descriptor_unmap(txd);
41662306a36Sopenharmony_ci	/*
41762306a36Sopenharmony_ci	 * The API requires that no submissions are done from a
41862306a36Sopenharmony_ci	 * callback, so we don't need to drop the lock here
41962306a36Sopenharmony_ci	 */
42062306a36Sopenharmony_ci	dmaengine_desc_callback_invoke(&cb, NULL);
42162306a36Sopenharmony_ci	dma_run_dependencies(txd);
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic void txx9dmac_dequeue(struct txx9dmac_chan *dc, struct list_head *list)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct txx9dmac_dev *ddev = dc->ddev;
42762306a36Sopenharmony_ci	struct txx9dmac_desc *desc;
42862306a36Sopenharmony_ci	struct txx9dmac_desc *prev = NULL;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	BUG_ON(!list_empty(list));
43162306a36Sopenharmony_ci	do {
43262306a36Sopenharmony_ci		desc = txx9dmac_first_queued(dc);
43362306a36Sopenharmony_ci		if (prev) {
43462306a36Sopenharmony_ci			desc_write_CHAR(dc, prev, desc->txd.phys);
43562306a36Sopenharmony_ci			dma_sync_single_for_device(chan2parent(&dc->chan),
43662306a36Sopenharmony_ci				prev->txd.phys, ddev->descsize,
43762306a36Sopenharmony_ci				DMA_TO_DEVICE);
43862306a36Sopenharmony_ci		}
43962306a36Sopenharmony_ci		prev = txx9dmac_last_child(desc);
44062306a36Sopenharmony_ci		list_move_tail(&desc->desc_node, list);
44162306a36Sopenharmony_ci		/* Make chain-completion interrupt happen */
44262306a36Sopenharmony_ci		if ((desc->txd.flags & DMA_PREP_INTERRUPT) &&
44362306a36Sopenharmony_ci		    !txx9dmac_chan_INTENT(dc))
44462306a36Sopenharmony_ci			break;
44562306a36Sopenharmony_ci	} while (!list_empty(&dc->queue));
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic void txx9dmac_complete_all(struct txx9dmac_chan *dc)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct txx9dmac_desc *desc, *_desc;
45162306a36Sopenharmony_ci	LIST_HEAD(list);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/*
45462306a36Sopenharmony_ci	 * Submit queued descriptors ASAP, i.e. before we go through
45562306a36Sopenharmony_ci	 * the completed ones.
45662306a36Sopenharmony_ci	 */
45762306a36Sopenharmony_ci	list_splice_init(&dc->active_list, &list);
45862306a36Sopenharmony_ci	if (!list_empty(&dc->queue)) {
45962306a36Sopenharmony_ci		txx9dmac_dequeue(dc, &dc->active_list);
46062306a36Sopenharmony_ci		txx9dmac_dostart(dc, txx9dmac_first_active(dc));
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	list_for_each_entry_safe(desc, _desc, &list, desc_node)
46462306a36Sopenharmony_ci		txx9dmac_descriptor_complete(dc, desc);
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic void txx9dmac_dump_desc(struct txx9dmac_chan *dc,
46862306a36Sopenharmony_ci			       struct txx9dmac_hwdesc *desc)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	if (is_dmac64(dc)) {
47162306a36Sopenharmony_ci#ifdef TXX9_DMA_USE_SIMPLE_CHAIN
47262306a36Sopenharmony_ci		dev_crit(chan2dev(&dc->chan),
47362306a36Sopenharmony_ci			 "  desc: ch%#llx s%#llx d%#llx c%#x\n",
47462306a36Sopenharmony_ci			 (u64)desc->CHAR, desc->SAR, desc->DAR, desc->CNTR);
47562306a36Sopenharmony_ci#else
47662306a36Sopenharmony_ci		dev_crit(chan2dev(&dc->chan),
47762306a36Sopenharmony_ci			 "  desc: ch%#llx s%#llx d%#llx c%#x"
47862306a36Sopenharmony_ci			 " si%#x di%#x cc%#x cs%#x\n",
47962306a36Sopenharmony_ci			 (u64)desc->CHAR, desc->SAR, desc->DAR, desc->CNTR,
48062306a36Sopenharmony_ci			 desc->SAIR, desc->DAIR, desc->CCR, desc->CSR);
48162306a36Sopenharmony_ci#endif
48262306a36Sopenharmony_ci	} else {
48362306a36Sopenharmony_ci		struct txx9dmac_hwdesc32 *d = (struct txx9dmac_hwdesc32 *)desc;
48462306a36Sopenharmony_ci#ifdef TXX9_DMA_USE_SIMPLE_CHAIN
48562306a36Sopenharmony_ci		dev_crit(chan2dev(&dc->chan),
48662306a36Sopenharmony_ci			 "  desc: ch%#x s%#x d%#x c%#x\n",
48762306a36Sopenharmony_ci			 d->CHAR, d->SAR, d->DAR, d->CNTR);
48862306a36Sopenharmony_ci#else
48962306a36Sopenharmony_ci		dev_crit(chan2dev(&dc->chan),
49062306a36Sopenharmony_ci			 "  desc: ch%#x s%#x d%#x c%#x"
49162306a36Sopenharmony_ci			 " si%#x di%#x cc%#x cs%#x\n",
49262306a36Sopenharmony_ci			 d->CHAR, d->SAR, d->DAR, d->CNTR,
49362306a36Sopenharmony_ci			 d->SAIR, d->DAIR, d->CCR, d->CSR);
49462306a36Sopenharmony_ci#endif
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic void txx9dmac_handle_error(struct txx9dmac_chan *dc, u32 csr)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct txx9dmac_desc *bad_desc;
50162306a36Sopenharmony_ci	struct txx9dmac_desc *child;
50262306a36Sopenharmony_ci	u32 errors;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/*
50562306a36Sopenharmony_ci	 * The descriptor currently at the head of the active list is
50662306a36Sopenharmony_ci	 * borked. Since we don't have any way to report errors, we'll
50762306a36Sopenharmony_ci	 * just have to scream loudly and try to carry on.
50862306a36Sopenharmony_ci	 */
50962306a36Sopenharmony_ci	dev_crit(chan2dev(&dc->chan), "Abnormal Chain Completion\n");
51062306a36Sopenharmony_ci	txx9dmac_dump_regs(dc);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	bad_desc = txx9dmac_first_active(dc);
51362306a36Sopenharmony_ci	list_del_init(&bad_desc->desc_node);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/* Clear all error flags and try to restart the controller */
51662306a36Sopenharmony_ci	errors = csr & (TXX9_DMA_CSR_ABCHC |
51762306a36Sopenharmony_ci			TXX9_DMA_CSR_CFERR | TXX9_DMA_CSR_CHERR |
51862306a36Sopenharmony_ci			TXX9_DMA_CSR_DESERR | TXX9_DMA_CSR_SORERR);
51962306a36Sopenharmony_ci	channel_writel(dc, CSR, errors);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (list_empty(&dc->active_list) && !list_empty(&dc->queue))
52262306a36Sopenharmony_ci		txx9dmac_dequeue(dc, &dc->active_list);
52362306a36Sopenharmony_ci	if (!list_empty(&dc->active_list))
52462306a36Sopenharmony_ci		txx9dmac_dostart(dc, txx9dmac_first_active(dc));
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	dev_crit(chan2dev(&dc->chan),
52762306a36Sopenharmony_ci		 "Bad descriptor submitted for DMA! (cookie: %d)\n",
52862306a36Sopenharmony_ci		 bad_desc->txd.cookie);
52962306a36Sopenharmony_ci	txx9dmac_dump_desc(dc, &bad_desc->hwdesc);
53062306a36Sopenharmony_ci	list_for_each_entry(child, &bad_desc->tx_list, desc_node)
53162306a36Sopenharmony_ci		txx9dmac_dump_desc(dc, &child->hwdesc);
53262306a36Sopenharmony_ci	/* Pretend the descriptor completed successfully */
53362306a36Sopenharmony_ci	txx9dmac_descriptor_complete(dc, bad_desc);
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic void txx9dmac_scan_descriptors(struct txx9dmac_chan *dc)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	dma_addr_t chain;
53962306a36Sopenharmony_ci	struct txx9dmac_desc *desc, *_desc;
54062306a36Sopenharmony_ci	struct txx9dmac_desc *child;
54162306a36Sopenharmony_ci	u32 csr;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (is_dmac64(dc)) {
54462306a36Sopenharmony_ci		chain = channel64_read_CHAR(dc);
54562306a36Sopenharmony_ci		csr = channel64_readl(dc, CSR);
54662306a36Sopenharmony_ci		channel64_writel(dc, CSR, csr);
54762306a36Sopenharmony_ci	} else {
54862306a36Sopenharmony_ci		chain = channel32_readl(dc, CHAR);
54962306a36Sopenharmony_ci		csr = channel32_readl(dc, CSR);
55062306a36Sopenharmony_ci		channel32_writel(dc, CSR, csr);
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci	/* For dynamic chain, we should look at XFACT instead of NCHNC */
55362306a36Sopenharmony_ci	if (!(csr & (TXX9_DMA_CSR_XFACT | TXX9_DMA_CSR_ABCHC))) {
55462306a36Sopenharmony_ci		/* Everything we've submitted is done */
55562306a36Sopenharmony_ci		txx9dmac_complete_all(dc);
55662306a36Sopenharmony_ci		return;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci	if (!(csr & TXX9_DMA_CSR_CHNEN))
55962306a36Sopenharmony_ci		chain = 0;	/* last descriptor of this chain */
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	dev_vdbg(chan2dev(&dc->chan), "scan_descriptors: char=%#llx\n",
56262306a36Sopenharmony_ci		 (u64)chain);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	list_for_each_entry_safe(desc, _desc, &dc->active_list, desc_node) {
56562306a36Sopenharmony_ci		if (desc_read_CHAR(dc, desc) == chain) {
56662306a36Sopenharmony_ci			/* This one is currently in progress */
56762306a36Sopenharmony_ci			if (csr & TXX9_DMA_CSR_ABCHC)
56862306a36Sopenharmony_ci				goto scan_done;
56962306a36Sopenharmony_ci			return;
57062306a36Sopenharmony_ci		}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci		list_for_each_entry(child, &desc->tx_list, desc_node)
57362306a36Sopenharmony_ci			if (desc_read_CHAR(dc, child) == chain) {
57462306a36Sopenharmony_ci				/* Currently in progress */
57562306a36Sopenharmony_ci				if (csr & TXX9_DMA_CSR_ABCHC)
57662306a36Sopenharmony_ci					goto scan_done;
57762306a36Sopenharmony_ci				return;
57862306a36Sopenharmony_ci			}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		/*
58162306a36Sopenharmony_ci		 * No descriptors so far seem to be in progress, i.e.
58262306a36Sopenharmony_ci		 * this one must be done.
58362306a36Sopenharmony_ci		 */
58462306a36Sopenharmony_ci		txx9dmac_descriptor_complete(dc, desc);
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ciscan_done:
58762306a36Sopenharmony_ci	if (csr & TXX9_DMA_CSR_ABCHC) {
58862306a36Sopenharmony_ci		txx9dmac_handle_error(dc, csr);
58962306a36Sopenharmony_ci		return;
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	dev_err(chan2dev(&dc->chan),
59362306a36Sopenharmony_ci		"BUG: All descriptors done, but channel not idle!\n");
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	/* Try to continue after resetting the channel... */
59662306a36Sopenharmony_ci	txx9dmac_reset_chan(dc);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	if (!list_empty(&dc->queue)) {
59962306a36Sopenharmony_ci		txx9dmac_dequeue(dc, &dc->active_list);
60062306a36Sopenharmony_ci		txx9dmac_dostart(dc, txx9dmac_first_active(dc));
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic void txx9dmac_chan_tasklet(struct tasklet_struct *t)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	int irq;
60762306a36Sopenharmony_ci	u32 csr;
60862306a36Sopenharmony_ci	struct txx9dmac_chan *dc;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	dc = from_tasklet(dc, t, tasklet);
61162306a36Sopenharmony_ci	csr = channel_readl(dc, CSR);
61262306a36Sopenharmony_ci	dev_vdbg(chan2dev(&dc->chan), "tasklet: status=%x\n", csr);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	spin_lock(&dc->lock);
61562306a36Sopenharmony_ci	if (csr & (TXX9_DMA_CSR_ABCHC | TXX9_DMA_CSR_NCHNC |
61662306a36Sopenharmony_ci		   TXX9_DMA_CSR_NTRNFC))
61762306a36Sopenharmony_ci		txx9dmac_scan_descriptors(dc);
61862306a36Sopenharmony_ci	spin_unlock(&dc->lock);
61962306a36Sopenharmony_ci	irq = dc->irq;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	enable_irq(irq);
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic irqreturn_t txx9dmac_chan_interrupt(int irq, void *dev_id)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct txx9dmac_chan *dc = dev_id;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	dev_vdbg(chan2dev(&dc->chan), "interrupt: status=%#x\n",
62962306a36Sopenharmony_ci			channel_readl(dc, CSR));
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	tasklet_schedule(&dc->tasklet);
63262306a36Sopenharmony_ci	/*
63362306a36Sopenharmony_ci	 * Just disable the interrupts. We'll turn them back on in the
63462306a36Sopenharmony_ci	 * softirq handler.
63562306a36Sopenharmony_ci	 */
63662306a36Sopenharmony_ci	disable_irq_nosync(irq);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	return IRQ_HANDLED;
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic void txx9dmac_tasklet(struct tasklet_struct *t)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	int irq;
64462306a36Sopenharmony_ci	u32 csr;
64562306a36Sopenharmony_ci	struct txx9dmac_chan *dc;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	struct txx9dmac_dev *ddev = from_tasklet(ddev, t, tasklet);
64862306a36Sopenharmony_ci	u32 mcr;
64962306a36Sopenharmony_ci	int i;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	mcr = dma_readl(ddev, MCR);
65262306a36Sopenharmony_ci	dev_vdbg(ddev->chan[0]->dma.dev, "tasklet: mcr=%x\n", mcr);
65362306a36Sopenharmony_ci	for (i = 0; i < TXX9_DMA_MAX_NR_CHANNELS; i++) {
65462306a36Sopenharmony_ci		if ((mcr >> (24 + i)) & 0x11) {
65562306a36Sopenharmony_ci			dc = ddev->chan[i];
65662306a36Sopenharmony_ci			csr = channel_readl(dc, CSR);
65762306a36Sopenharmony_ci			dev_vdbg(chan2dev(&dc->chan), "tasklet: status=%x\n",
65862306a36Sopenharmony_ci				 csr);
65962306a36Sopenharmony_ci			spin_lock(&dc->lock);
66062306a36Sopenharmony_ci			if (csr & (TXX9_DMA_CSR_ABCHC | TXX9_DMA_CSR_NCHNC |
66162306a36Sopenharmony_ci				   TXX9_DMA_CSR_NTRNFC))
66262306a36Sopenharmony_ci				txx9dmac_scan_descriptors(dc);
66362306a36Sopenharmony_ci			spin_unlock(&dc->lock);
66462306a36Sopenharmony_ci		}
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci	irq = ddev->irq;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	enable_irq(irq);
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic irqreturn_t txx9dmac_interrupt(int irq, void *dev_id)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	struct txx9dmac_dev *ddev = dev_id;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	dev_vdbg(ddev->chan[0]->dma.dev, "interrupt: status=%#x\n",
67662306a36Sopenharmony_ci			dma_readl(ddev, MCR));
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	tasklet_schedule(&ddev->tasklet);
67962306a36Sopenharmony_ci	/*
68062306a36Sopenharmony_ci	 * Just disable the interrupts. We'll turn them back on in the
68162306a36Sopenharmony_ci	 * softirq handler.
68262306a36Sopenharmony_ci	 */
68362306a36Sopenharmony_ci	disable_irq_nosync(irq);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	return IRQ_HANDLED;
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci/*----------------------------------------------------------------------*/
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_cistatic dma_cookie_t txx9dmac_tx_submit(struct dma_async_tx_descriptor *tx)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	struct txx9dmac_desc *desc = txd_to_txx9dmac_desc(tx);
69362306a36Sopenharmony_ci	struct txx9dmac_chan *dc = to_txx9dmac_chan(tx->chan);
69462306a36Sopenharmony_ci	dma_cookie_t cookie;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	spin_lock_bh(&dc->lock);
69762306a36Sopenharmony_ci	cookie = dma_cookie_assign(tx);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	dev_vdbg(chan2dev(tx->chan), "tx_submit: queued %u %p\n",
70062306a36Sopenharmony_ci		 desc->txd.cookie, desc);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	list_add_tail(&desc->desc_node, &dc->queue);
70362306a36Sopenharmony_ci	spin_unlock_bh(&dc->lock);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	return cookie;
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
70962306a36Sopenharmony_citxx9dmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
71062306a36Sopenharmony_ci		size_t len, unsigned long flags)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	struct txx9dmac_chan *dc = to_txx9dmac_chan(chan);
71362306a36Sopenharmony_ci	struct txx9dmac_dev *ddev = dc->ddev;
71462306a36Sopenharmony_ci	struct txx9dmac_desc *desc;
71562306a36Sopenharmony_ci	struct txx9dmac_desc *first;
71662306a36Sopenharmony_ci	struct txx9dmac_desc *prev;
71762306a36Sopenharmony_ci	size_t xfer_count;
71862306a36Sopenharmony_ci	size_t offset;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	dev_vdbg(chan2dev(chan), "prep_dma_memcpy d%#llx s%#llx l%#zx f%#lx\n",
72162306a36Sopenharmony_ci		 (u64)dest, (u64)src, len, flags);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	if (unlikely(!len)) {
72462306a36Sopenharmony_ci		dev_dbg(chan2dev(chan), "prep_dma_memcpy: length is zero!\n");
72562306a36Sopenharmony_ci		return NULL;
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	prev = first = NULL;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	for (offset = 0; offset < len; offset += xfer_count) {
73162306a36Sopenharmony_ci		xfer_count = min_t(size_t, len - offset, TXX9_DMA_MAX_COUNT);
73262306a36Sopenharmony_ci		/*
73362306a36Sopenharmony_ci		 * Workaround for ERT-TX49H2-033, ERT-TX49H3-020,
73462306a36Sopenharmony_ci		 * ERT-TX49H4-016 (slightly conservative)
73562306a36Sopenharmony_ci		 */
73662306a36Sopenharmony_ci		if (__is_dmac64(ddev)) {
73762306a36Sopenharmony_ci			if (xfer_count > 0x100 &&
73862306a36Sopenharmony_ci			    (xfer_count & 0xff) >= 0xfa &&
73962306a36Sopenharmony_ci			    (xfer_count & 0xff) <= 0xff)
74062306a36Sopenharmony_ci				xfer_count -= 0x20;
74162306a36Sopenharmony_ci		} else {
74262306a36Sopenharmony_ci			if (xfer_count > 0x80 &&
74362306a36Sopenharmony_ci			    (xfer_count & 0x7f) >= 0x7e &&
74462306a36Sopenharmony_ci			    (xfer_count & 0x7f) <= 0x7f)
74562306a36Sopenharmony_ci				xfer_count -= 0x20;
74662306a36Sopenharmony_ci		}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci		desc = txx9dmac_desc_get(dc);
74962306a36Sopenharmony_ci		if (!desc) {
75062306a36Sopenharmony_ci			txx9dmac_desc_put(dc, first);
75162306a36Sopenharmony_ci			return NULL;
75262306a36Sopenharmony_ci		}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		if (__is_dmac64(ddev)) {
75562306a36Sopenharmony_ci			desc->hwdesc.SAR = src + offset;
75662306a36Sopenharmony_ci			desc->hwdesc.DAR = dest + offset;
75762306a36Sopenharmony_ci			desc->hwdesc.CNTR = xfer_count;
75862306a36Sopenharmony_ci			txx9dmac_desc_set_nosimple(ddev, desc, 8, 8,
75962306a36Sopenharmony_ci					dc->ccr | TXX9_DMA_CCR_XFACT);
76062306a36Sopenharmony_ci		} else {
76162306a36Sopenharmony_ci			desc->hwdesc32.SAR = src + offset;
76262306a36Sopenharmony_ci			desc->hwdesc32.DAR = dest + offset;
76362306a36Sopenharmony_ci			desc->hwdesc32.CNTR = xfer_count;
76462306a36Sopenharmony_ci			txx9dmac_desc_set_nosimple(ddev, desc, 4, 4,
76562306a36Sopenharmony_ci					dc->ccr | TXX9_DMA_CCR_XFACT);
76662306a36Sopenharmony_ci		}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci		/*
76962306a36Sopenharmony_ci		 * The descriptors on tx_list are not reachable from
77062306a36Sopenharmony_ci		 * the dc->queue list or dc->active_list after a
77162306a36Sopenharmony_ci		 * submit.  If we put all descriptors on active_list,
77262306a36Sopenharmony_ci		 * calling of callback on the completion will be more
77362306a36Sopenharmony_ci		 * complex.
77462306a36Sopenharmony_ci		 */
77562306a36Sopenharmony_ci		if (!first) {
77662306a36Sopenharmony_ci			first = desc;
77762306a36Sopenharmony_ci		} else {
77862306a36Sopenharmony_ci			desc_write_CHAR(dc, prev, desc->txd.phys);
77962306a36Sopenharmony_ci			dma_sync_single_for_device(chan2parent(&dc->chan),
78062306a36Sopenharmony_ci					prev->txd.phys, ddev->descsize,
78162306a36Sopenharmony_ci					DMA_TO_DEVICE);
78262306a36Sopenharmony_ci			list_add_tail(&desc->desc_node, &first->tx_list);
78362306a36Sopenharmony_ci		}
78462306a36Sopenharmony_ci		prev = desc;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	/* Trigger interrupt after last block */
78862306a36Sopenharmony_ci	if (flags & DMA_PREP_INTERRUPT)
78962306a36Sopenharmony_ci		txx9dmac_desc_set_INTENT(ddev, prev);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	desc_write_CHAR(dc, prev, 0);
79262306a36Sopenharmony_ci	dma_sync_single_for_device(chan2parent(&dc->chan),
79362306a36Sopenharmony_ci			prev->txd.phys, ddev->descsize,
79462306a36Sopenharmony_ci			DMA_TO_DEVICE);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	first->txd.flags = flags;
79762306a36Sopenharmony_ci	first->len = len;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	return &first->txd;
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
80362306a36Sopenharmony_citxx9dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
80462306a36Sopenharmony_ci		unsigned int sg_len, enum dma_transfer_direction direction,
80562306a36Sopenharmony_ci		unsigned long flags, void *context)
80662306a36Sopenharmony_ci{
80762306a36Sopenharmony_ci	struct txx9dmac_chan *dc = to_txx9dmac_chan(chan);
80862306a36Sopenharmony_ci	struct txx9dmac_dev *ddev = dc->ddev;
80962306a36Sopenharmony_ci	struct txx9dmac_slave *ds = chan->private;
81062306a36Sopenharmony_ci	struct txx9dmac_desc *prev;
81162306a36Sopenharmony_ci	struct txx9dmac_desc *first;
81262306a36Sopenharmony_ci	unsigned int i;
81362306a36Sopenharmony_ci	struct scatterlist *sg;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	dev_vdbg(chan2dev(chan), "prep_dma_slave\n");
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	BUG_ON(!ds || !ds->reg_width);
81862306a36Sopenharmony_ci	if (ds->tx_reg)
81962306a36Sopenharmony_ci		BUG_ON(direction != DMA_MEM_TO_DEV);
82062306a36Sopenharmony_ci	else
82162306a36Sopenharmony_ci		BUG_ON(direction != DMA_DEV_TO_MEM);
82262306a36Sopenharmony_ci	if (unlikely(!sg_len))
82362306a36Sopenharmony_ci		return NULL;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	prev = first = NULL;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	for_each_sg(sgl, sg, sg_len, i) {
82862306a36Sopenharmony_ci		struct txx9dmac_desc *desc;
82962306a36Sopenharmony_ci		dma_addr_t mem;
83062306a36Sopenharmony_ci		u32 sai, dai;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci		desc = txx9dmac_desc_get(dc);
83362306a36Sopenharmony_ci		if (!desc) {
83462306a36Sopenharmony_ci			txx9dmac_desc_put(dc, first);
83562306a36Sopenharmony_ci			return NULL;
83662306a36Sopenharmony_ci		}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci		mem = sg_dma_address(sg);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci		if (__is_dmac64(ddev)) {
84162306a36Sopenharmony_ci			if (direction == DMA_MEM_TO_DEV) {
84262306a36Sopenharmony_ci				desc->hwdesc.SAR = mem;
84362306a36Sopenharmony_ci				desc->hwdesc.DAR = ds->tx_reg;
84462306a36Sopenharmony_ci			} else {
84562306a36Sopenharmony_ci				desc->hwdesc.SAR = ds->rx_reg;
84662306a36Sopenharmony_ci				desc->hwdesc.DAR = mem;
84762306a36Sopenharmony_ci			}
84862306a36Sopenharmony_ci			desc->hwdesc.CNTR = sg_dma_len(sg);
84962306a36Sopenharmony_ci		} else {
85062306a36Sopenharmony_ci			if (direction == DMA_MEM_TO_DEV) {
85162306a36Sopenharmony_ci				desc->hwdesc32.SAR = mem;
85262306a36Sopenharmony_ci				desc->hwdesc32.DAR = ds->tx_reg;
85362306a36Sopenharmony_ci			} else {
85462306a36Sopenharmony_ci				desc->hwdesc32.SAR = ds->rx_reg;
85562306a36Sopenharmony_ci				desc->hwdesc32.DAR = mem;
85662306a36Sopenharmony_ci			}
85762306a36Sopenharmony_ci			desc->hwdesc32.CNTR = sg_dma_len(sg);
85862306a36Sopenharmony_ci		}
85962306a36Sopenharmony_ci		if (direction == DMA_MEM_TO_DEV) {
86062306a36Sopenharmony_ci			sai = ds->reg_width;
86162306a36Sopenharmony_ci			dai = 0;
86262306a36Sopenharmony_ci		} else {
86362306a36Sopenharmony_ci			sai = 0;
86462306a36Sopenharmony_ci			dai = ds->reg_width;
86562306a36Sopenharmony_ci		}
86662306a36Sopenharmony_ci		txx9dmac_desc_set_nosimple(ddev, desc, sai, dai,
86762306a36Sopenharmony_ci					dc->ccr | TXX9_DMA_CCR_XFACT);
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci		if (!first) {
87062306a36Sopenharmony_ci			first = desc;
87162306a36Sopenharmony_ci		} else {
87262306a36Sopenharmony_ci			desc_write_CHAR(dc, prev, desc->txd.phys);
87362306a36Sopenharmony_ci			dma_sync_single_for_device(chan2parent(&dc->chan),
87462306a36Sopenharmony_ci					prev->txd.phys,
87562306a36Sopenharmony_ci					ddev->descsize,
87662306a36Sopenharmony_ci					DMA_TO_DEVICE);
87762306a36Sopenharmony_ci			list_add_tail(&desc->desc_node, &first->tx_list);
87862306a36Sopenharmony_ci		}
87962306a36Sopenharmony_ci		prev = desc;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/* Trigger interrupt after last block */
88362306a36Sopenharmony_ci	if (flags & DMA_PREP_INTERRUPT)
88462306a36Sopenharmony_ci		txx9dmac_desc_set_INTENT(ddev, prev);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	desc_write_CHAR(dc, prev, 0);
88762306a36Sopenharmony_ci	dma_sync_single_for_device(chan2parent(&dc->chan),
88862306a36Sopenharmony_ci			prev->txd.phys, ddev->descsize,
88962306a36Sopenharmony_ci			DMA_TO_DEVICE);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	first->txd.flags = flags;
89262306a36Sopenharmony_ci	first->len = 0;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	return &first->txd;
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cistatic int txx9dmac_terminate_all(struct dma_chan *chan)
89862306a36Sopenharmony_ci{
89962306a36Sopenharmony_ci	struct txx9dmac_chan *dc = to_txx9dmac_chan(chan);
90062306a36Sopenharmony_ci	struct txx9dmac_desc *desc, *_desc;
90162306a36Sopenharmony_ci	LIST_HEAD(list);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	dev_vdbg(chan2dev(chan), "terminate_all\n");
90462306a36Sopenharmony_ci	spin_lock_bh(&dc->lock);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	txx9dmac_reset_chan(dc);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	/* active_list entries will end up before queued entries */
90962306a36Sopenharmony_ci	list_splice_init(&dc->queue, &list);
91062306a36Sopenharmony_ci	list_splice_init(&dc->active_list, &list);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	spin_unlock_bh(&dc->lock);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	/* Flush all pending and queued descriptors */
91562306a36Sopenharmony_ci	list_for_each_entry_safe(desc, _desc, &list, desc_node)
91662306a36Sopenharmony_ci		txx9dmac_descriptor_complete(dc, desc);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	return 0;
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_cistatic enum dma_status
92262306a36Sopenharmony_citxx9dmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
92362306a36Sopenharmony_ci		   struct dma_tx_state *txstate)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	struct txx9dmac_chan *dc = to_txx9dmac_chan(chan);
92662306a36Sopenharmony_ci	enum dma_status ret;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	ret = dma_cookie_status(chan, cookie, txstate);
92962306a36Sopenharmony_ci	if (ret == DMA_COMPLETE)
93062306a36Sopenharmony_ci		return DMA_COMPLETE;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	spin_lock_bh(&dc->lock);
93362306a36Sopenharmony_ci	txx9dmac_scan_descriptors(dc);
93462306a36Sopenharmony_ci	spin_unlock_bh(&dc->lock);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	return dma_cookie_status(chan, cookie, txstate);
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_cistatic void txx9dmac_chain_dynamic(struct txx9dmac_chan *dc,
94062306a36Sopenharmony_ci				   struct txx9dmac_desc *prev)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	struct txx9dmac_dev *ddev = dc->ddev;
94362306a36Sopenharmony_ci	struct txx9dmac_desc *desc;
94462306a36Sopenharmony_ci	LIST_HEAD(list);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	prev = txx9dmac_last_child(prev);
94762306a36Sopenharmony_ci	txx9dmac_dequeue(dc, &list);
94862306a36Sopenharmony_ci	desc = list_entry(list.next, struct txx9dmac_desc, desc_node);
94962306a36Sopenharmony_ci	desc_write_CHAR(dc, prev, desc->txd.phys);
95062306a36Sopenharmony_ci	dma_sync_single_for_device(chan2parent(&dc->chan),
95162306a36Sopenharmony_ci				   prev->txd.phys, ddev->descsize,
95262306a36Sopenharmony_ci				   DMA_TO_DEVICE);
95362306a36Sopenharmony_ci	if (!(channel_readl(dc, CSR) & TXX9_DMA_CSR_CHNEN) &&
95462306a36Sopenharmony_ci	    channel_read_CHAR(dc) == prev->txd.phys)
95562306a36Sopenharmony_ci		/* Restart chain DMA */
95662306a36Sopenharmony_ci		channel_write_CHAR(dc, desc->txd.phys);
95762306a36Sopenharmony_ci	list_splice_tail(&list, &dc->active_list);
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic void txx9dmac_issue_pending(struct dma_chan *chan)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	struct txx9dmac_chan *dc = to_txx9dmac_chan(chan);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	spin_lock_bh(&dc->lock);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (!list_empty(&dc->active_list))
96762306a36Sopenharmony_ci		txx9dmac_scan_descriptors(dc);
96862306a36Sopenharmony_ci	if (!list_empty(&dc->queue)) {
96962306a36Sopenharmony_ci		if (list_empty(&dc->active_list)) {
97062306a36Sopenharmony_ci			txx9dmac_dequeue(dc, &dc->active_list);
97162306a36Sopenharmony_ci			txx9dmac_dostart(dc, txx9dmac_first_active(dc));
97262306a36Sopenharmony_ci		} else if (txx9_dma_have_SMPCHN()) {
97362306a36Sopenharmony_ci			struct txx9dmac_desc *prev = txx9dmac_last_active(dc);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci			if (!(prev->txd.flags & DMA_PREP_INTERRUPT) ||
97662306a36Sopenharmony_ci			    txx9dmac_chan_INTENT(dc))
97762306a36Sopenharmony_ci				txx9dmac_chain_dynamic(dc, prev);
97862306a36Sopenharmony_ci		}
97962306a36Sopenharmony_ci	}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	spin_unlock_bh(&dc->lock);
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic int txx9dmac_alloc_chan_resources(struct dma_chan *chan)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	struct txx9dmac_chan *dc = to_txx9dmac_chan(chan);
98762306a36Sopenharmony_ci	struct txx9dmac_slave *ds = chan->private;
98862306a36Sopenharmony_ci	struct txx9dmac_desc *desc;
98962306a36Sopenharmony_ci	int i;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	dev_vdbg(chan2dev(chan), "alloc_chan_resources\n");
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	/* ASSERT:  channel is idle */
99462306a36Sopenharmony_ci	if (channel_readl(dc, CSR) & TXX9_DMA_CSR_XFACT) {
99562306a36Sopenharmony_ci		dev_dbg(chan2dev(chan), "DMA channel not idle?\n");
99662306a36Sopenharmony_ci		return -EIO;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	dma_cookie_init(chan);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	dc->ccr = TXX9_DMA_CCR_IMMCHN | TXX9_DMA_CCR_INTENE | CCR_LE;
100262306a36Sopenharmony_ci	txx9dmac_chan_set_SMPCHN(dc);
100362306a36Sopenharmony_ci	if (!txx9_dma_have_SMPCHN() || (dc->ccr & TXX9_DMA_CCR_SMPCHN))
100462306a36Sopenharmony_ci		dc->ccr |= TXX9_DMA_CCR_INTENC;
100562306a36Sopenharmony_ci	if (chan->device->device_prep_dma_memcpy) {
100662306a36Sopenharmony_ci		if (ds)
100762306a36Sopenharmony_ci			return -EINVAL;
100862306a36Sopenharmony_ci		dc->ccr |= TXX9_DMA_CCR_XFSZ_X8;
100962306a36Sopenharmony_ci	} else {
101062306a36Sopenharmony_ci		if (!ds ||
101162306a36Sopenharmony_ci		    (ds->tx_reg && ds->rx_reg) || (!ds->tx_reg && !ds->rx_reg))
101262306a36Sopenharmony_ci			return -EINVAL;
101362306a36Sopenharmony_ci		dc->ccr |= TXX9_DMA_CCR_EXTRQ |
101462306a36Sopenharmony_ci			TXX9_DMA_CCR_XFSZ(__ffs(ds->reg_width));
101562306a36Sopenharmony_ci		txx9dmac_chan_set_INTENT(dc);
101662306a36Sopenharmony_ci	}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	spin_lock_bh(&dc->lock);
101962306a36Sopenharmony_ci	i = dc->descs_allocated;
102062306a36Sopenharmony_ci	while (dc->descs_allocated < TXX9_DMA_INITIAL_DESC_COUNT) {
102162306a36Sopenharmony_ci		spin_unlock_bh(&dc->lock);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci		desc = txx9dmac_desc_alloc(dc, GFP_KERNEL);
102462306a36Sopenharmony_ci		if (!desc) {
102562306a36Sopenharmony_ci			dev_info(chan2dev(chan),
102662306a36Sopenharmony_ci				"only allocated %d descriptors\n", i);
102762306a36Sopenharmony_ci			spin_lock_bh(&dc->lock);
102862306a36Sopenharmony_ci			break;
102962306a36Sopenharmony_ci		}
103062306a36Sopenharmony_ci		txx9dmac_desc_put(dc, desc);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci		spin_lock_bh(&dc->lock);
103362306a36Sopenharmony_ci		i = ++dc->descs_allocated;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci	spin_unlock_bh(&dc->lock);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	dev_dbg(chan2dev(chan),
103862306a36Sopenharmony_ci		"alloc_chan_resources allocated %d descriptors\n", i);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	return i;
104162306a36Sopenharmony_ci}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_cistatic void txx9dmac_free_chan_resources(struct dma_chan *chan)
104462306a36Sopenharmony_ci{
104562306a36Sopenharmony_ci	struct txx9dmac_chan *dc = to_txx9dmac_chan(chan);
104662306a36Sopenharmony_ci	struct txx9dmac_dev *ddev = dc->ddev;
104762306a36Sopenharmony_ci	struct txx9dmac_desc *desc, *_desc;
104862306a36Sopenharmony_ci	LIST_HEAD(list);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	dev_dbg(chan2dev(chan), "free_chan_resources (descs allocated=%u)\n",
105162306a36Sopenharmony_ci			dc->descs_allocated);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	/* ASSERT:  channel is idle */
105462306a36Sopenharmony_ci	BUG_ON(!list_empty(&dc->active_list));
105562306a36Sopenharmony_ci	BUG_ON(!list_empty(&dc->queue));
105662306a36Sopenharmony_ci	BUG_ON(channel_readl(dc, CSR) & TXX9_DMA_CSR_XFACT);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	spin_lock_bh(&dc->lock);
105962306a36Sopenharmony_ci	list_splice_init(&dc->free_list, &list);
106062306a36Sopenharmony_ci	dc->descs_allocated = 0;
106162306a36Sopenharmony_ci	spin_unlock_bh(&dc->lock);
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	list_for_each_entry_safe(desc, _desc, &list, desc_node) {
106462306a36Sopenharmony_ci		dev_vdbg(chan2dev(chan), "  freeing descriptor %p\n", desc);
106562306a36Sopenharmony_ci		dma_unmap_single(chan2parent(chan), desc->txd.phys,
106662306a36Sopenharmony_ci				 ddev->descsize, DMA_TO_DEVICE);
106762306a36Sopenharmony_ci		kfree(desc);
106862306a36Sopenharmony_ci	}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	dev_vdbg(chan2dev(chan), "free_chan_resources done\n");
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci/*----------------------------------------------------------------------*/
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_cistatic void txx9dmac_off(struct txx9dmac_dev *ddev)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	dma_writel(ddev, MCR, 0);
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_cistatic int __init txx9dmac_chan_probe(struct platform_device *pdev)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	struct txx9dmac_chan_platform_data *cpdata =
108362306a36Sopenharmony_ci			dev_get_platdata(&pdev->dev);
108462306a36Sopenharmony_ci	struct platform_device *dmac_dev = cpdata->dmac_dev;
108562306a36Sopenharmony_ci	struct txx9dmac_platform_data *pdata = dev_get_platdata(&dmac_dev->dev);
108662306a36Sopenharmony_ci	struct txx9dmac_chan *dc;
108762306a36Sopenharmony_ci	int err;
108862306a36Sopenharmony_ci	int ch = pdev->id % TXX9_DMA_MAX_NR_CHANNELS;
108962306a36Sopenharmony_ci	int irq;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL);
109262306a36Sopenharmony_ci	if (!dc)
109362306a36Sopenharmony_ci		return -ENOMEM;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	dc->dma.dev = &pdev->dev;
109662306a36Sopenharmony_ci	dc->dma.device_alloc_chan_resources = txx9dmac_alloc_chan_resources;
109762306a36Sopenharmony_ci	dc->dma.device_free_chan_resources = txx9dmac_free_chan_resources;
109862306a36Sopenharmony_ci	dc->dma.device_terminate_all = txx9dmac_terminate_all;
109962306a36Sopenharmony_ci	dc->dma.device_tx_status = txx9dmac_tx_status;
110062306a36Sopenharmony_ci	dc->dma.device_issue_pending = txx9dmac_issue_pending;
110162306a36Sopenharmony_ci	if (pdata && pdata->memcpy_chan == ch) {
110262306a36Sopenharmony_ci		dc->dma.device_prep_dma_memcpy = txx9dmac_prep_dma_memcpy;
110362306a36Sopenharmony_ci		dma_cap_set(DMA_MEMCPY, dc->dma.cap_mask);
110462306a36Sopenharmony_ci	} else {
110562306a36Sopenharmony_ci		dc->dma.device_prep_slave_sg = txx9dmac_prep_slave_sg;
110662306a36Sopenharmony_ci		dma_cap_set(DMA_SLAVE, dc->dma.cap_mask);
110762306a36Sopenharmony_ci		dma_cap_set(DMA_PRIVATE, dc->dma.cap_mask);
110862306a36Sopenharmony_ci	}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	INIT_LIST_HEAD(&dc->dma.channels);
111162306a36Sopenharmony_ci	dc->ddev = platform_get_drvdata(dmac_dev);
111262306a36Sopenharmony_ci	if (dc->ddev->irq < 0) {
111362306a36Sopenharmony_ci		irq = platform_get_irq(pdev, 0);
111462306a36Sopenharmony_ci		if (irq < 0)
111562306a36Sopenharmony_ci			return irq;
111662306a36Sopenharmony_ci		tasklet_setup(&dc->tasklet, txx9dmac_chan_tasklet);
111762306a36Sopenharmony_ci		dc->irq = irq;
111862306a36Sopenharmony_ci		err = devm_request_irq(&pdev->dev, dc->irq,
111962306a36Sopenharmony_ci			txx9dmac_chan_interrupt, 0, dev_name(&pdev->dev), dc);
112062306a36Sopenharmony_ci		if (err)
112162306a36Sopenharmony_ci			return err;
112262306a36Sopenharmony_ci	} else
112362306a36Sopenharmony_ci		dc->irq = -1;
112462306a36Sopenharmony_ci	dc->ddev->chan[ch] = dc;
112562306a36Sopenharmony_ci	dc->chan.device = &dc->dma;
112662306a36Sopenharmony_ci	list_add_tail(&dc->chan.device_node, &dc->chan.device->channels);
112762306a36Sopenharmony_ci	dma_cookie_init(&dc->chan);
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	if (is_dmac64(dc))
113062306a36Sopenharmony_ci		dc->ch_regs = &__txx9dmac_regs(dc->ddev)->CHAN[ch];
113162306a36Sopenharmony_ci	else
113262306a36Sopenharmony_ci		dc->ch_regs = &__txx9dmac_regs32(dc->ddev)->CHAN[ch];
113362306a36Sopenharmony_ci	spin_lock_init(&dc->lock);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	INIT_LIST_HEAD(&dc->active_list);
113662306a36Sopenharmony_ci	INIT_LIST_HEAD(&dc->queue);
113762306a36Sopenharmony_ci	INIT_LIST_HEAD(&dc->free_list);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	txx9dmac_reset_chan(dc);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	platform_set_drvdata(pdev, dc);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	err = dma_async_device_register(&dc->dma);
114462306a36Sopenharmony_ci	if (err)
114562306a36Sopenharmony_ci		return err;
114662306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "TXx9 DMA Channel (dma%d%s%s)\n",
114762306a36Sopenharmony_ci		dc->dma.dev_id,
114862306a36Sopenharmony_ci		dma_has_cap(DMA_MEMCPY, dc->dma.cap_mask) ? " memcpy" : "",
114962306a36Sopenharmony_ci		dma_has_cap(DMA_SLAVE, dc->dma.cap_mask) ? " slave" : "");
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	return 0;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic int txx9dmac_chan_remove(struct platform_device *pdev)
115562306a36Sopenharmony_ci{
115662306a36Sopenharmony_ci	struct txx9dmac_chan *dc = platform_get_drvdata(pdev);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	dma_async_device_unregister(&dc->dma);
116062306a36Sopenharmony_ci	if (dc->irq >= 0) {
116162306a36Sopenharmony_ci		devm_free_irq(&pdev->dev, dc->irq, dc);
116262306a36Sopenharmony_ci		tasklet_kill(&dc->tasklet);
116362306a36Sopenharmony_ci	}
116462306a36Sopenharmony_ci	dc->ddev->chan[pdev->id % TXX9_DMA_MAX_NR_CHANNELS] = NULL;
116562306a36Sopenharmony_ci	return 0;
116662306a36Sopenharmony_ci}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_cistatic int __init txx9dmac_probe(struct platform_device *pdev)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct txx9dmac_platform_data *pdata = dev_get_platdata(&pdev->dev);
117162306a36Sopenharmony_ci	struct resource *io;
117262306a36Sopenharmony_ci	struct txx9dmac_dev *ddev;
117362306a36Sopenharmony_ci	u32 mcr;
117462306a36Sopenharmony_ci	int err;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
117762306a36Sopenharmony_ci	if (!io)
117862306a36Sopenharmony_ci		return -EINVAL;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	ddev = devm_kzalloc(&pdev->dev, sizeof(*ddev), GFP_KERNEL);
118162306a36Sopenharmony_ci	if (!ddev)
118262306a36Sopenharmony_ci		return -ENOMEM;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	if (!devm_request_mem_region(&pdev->dev, io->start, resource_size(io),
118562306a36Sopenharmony_ci				     dev_name(&pdev->dev)))
118662306a36Sopenharmony_ci		return -EBUSY;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	ddev->regs = devm_ioremap(&pdev->dev, io->start, resource_size(io));
118962306a36Sopenharmony_ci	if (!ddev->regs)
119062306a36Sopenharmony_ci		return -ENOMEM;
119162306a36Sopenharmony_ci	ddev->have_64bit_regs = pdata->have_64bit_regs;
119262306a36Sopenharmony_ci	if (__is_dmac64(ddev))
119362306a36Sopenharmony_ci		ddev->descsize = sizeof(struct txx9dmac_hwdesc);
119462306a36Sopenharmony_ci	else
119562306a36Sopenharmony_ci		ddev->descsize = sizeof(struct txx9dmac_hwdesc32);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	/* force dma off, just in case */
119862306a36Sopenharmony_ci	txx9dmac_off(ddev);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	ddev->irq = platform_get_irq(pdev, 0);
120162306a36Sopenharmony_ci	if (ddev->irq >= 0) {
120262306a36Sopenharmony_ci		tasklet_setup(&ddev->tasklet, txx9dmac_tasklet);
120362306a36Sopenharmony_ci		err = devm_request_irq(&pdev->dev, ddev->irq,
120462306a36Sopenharmony_ci			txx9dmac_interrupt, 0, dev_name(&pdev->dev), ddev);
120562306a36Sopenharmony_ci		if (err)
120662306a36Sopenharmony_ci			return err;
120762306a36Sopenharmony_ci	}
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	mcr = TXX9_DMA_MCR_MSTEN | MCR_LE;
121062306a36Sopenharmony_ci	if (pdata && pdata->memcpy_chan >= 0)
121162306a36Sopenharmony_ci		mcr |= TXX9_DMA_MCR_FIFUM(pdata->memcpy_chan);
121262306a36Sopenharmony_ci	dma_writel(ddev, MCR, mcr);
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	platform_set_drvdata(pdev, ddev);
121562306a36Sopenharmony_ci	return 0;
121662306a36Sopenharmony_ci}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_cistatic int txx9dmac_remove(struct platform_device *pdev)
121962306a36Sopenharmony_ci{
122062306a36Sopenharmony_ci	struct txx9dmac_dev *ddev = platform_get_drvdata(pdev);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	txx9dmac_off(ddev);
122362306a36Sopenharmony_ci	if (ddev->irq >= 0) {
122462306a36Sopenharmony_ci		devm_free_irq(&pdev->dev, ddev->irq, ddev);
122562306a36Sopenharmony_ci		tasklet_kill(&ddev->tasklet);
122662306a36Sopenharmony_ci	}
122762306a36Sopenharmony_ci	return 0;
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_cistatic void txx9dmac_shutdown(struct platform_device *pdev)
123162306a36Sopenharmony_ci{
123262306a36Sopenharmony_ci	struct txx9dmac_dev *ddev = platform_get_drvdata(pdev);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	txx9dmac_off(ddev);
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cistatic int txx9dmac_suspend_noirq(struct device *dev)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	struct txx9dmac_dev *ddev = dev_get_drvdata(dev);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	txx9dmac_off(ddev);
124262306a36Sopenharmony_ci	return 0;
124362306a36Sopenharmony_ci}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_cistatic int txx9dmac_resume_noirq(struct device *dev)
124662306a36Sopenharmony_ci{
124762306a36Sopenharmony_ci	struct txx9dmac_dev *ddev = dev_get_drvdata(dev);
124862306a36Sopenharmony_ci	struct txx9dmac_platform_data *pdata = dev_get_platdata(dev);
124962306a36Sopenharmony_ci	u32 mcr;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	mcr = TXX9_DMA_MCR_MSTEN | MCR_LE;
125262306a36Sopenharmony_ci	if (pdata && pdata->memcpy_chan >= 0)
125362306a36Sopenharmony_ci		mcr |= TXX9_DMA_MCR_FIFUM(pdata->memcpy_chan);
125462306a36Sopenharmony_ci	dma_writel(ddev, MCR, mcr);
125562306a36Sopenharmony_ci	return 0;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_cistatic const struct dev_pm_ops txx9dmac_dev_pm_ops = {
126062306a36Sopenharmony_ci	.suspend_noirq = txx9dmac_suspend_noirq,
126162306a36Sopenharmony_ci	.resume_noirq = txx9dmac_resume_noirq,
126262306a36Sopenharmony_ci};
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_cistatic struct platform_driver txx9dmac_chan_driver = {
126562306a36Sopenharmony_ci	.remove		= txx9dmac_chan_remove,
126662306a36Sopenharmony_ci	.driver = {
126762306a36Sopenharmony_ci		.name	= "txx9dmac-chan",
126862306a36Sopenharmony_ci	},
126962306a36Sopenharmony_ci};
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_cistatic struct platform_driver txx9dmac_driver = {
127262306a36Sopenharmony_ci	.remove		= txx9dmac_remove,
127362306a36Sopenharmony_ci	.shutdown	= txx9dmac_shutdown,
127462306a36Sopenharmony_ci	.driver = {
127562306a36Sopenharmony_ci		.name	= "txx9dmac",
127662306a36Sopenharmony_ci		.pm	= &txx9dmac_dev_pm_ops,
127762306a36Sopenharmony_ci	},
127862306a36Sopenharmony_ci};
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_cistatic int __init txx9dmac_init(void)
128162306a36Sopenharmony_ci{
128262306a36Sopenharmony_ci	int rc;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	rc = platform_driver_probe(&txx9dmac_driver, txx9dmac_probe);
128562306a36Sopenharmony_ci	if (!rc) {
128662306a36Sopenharmony_ci		rc = platform_driver_probe(&txx9dmac_chan_driver,
128762306a36Sopenharmony_ci					   txx9dmac_chan_probe);
128862306a36Sopenharmony_ci		if (rc)
128962306a36Sopenharmony_ci			platform_driver_unregister(&txx9dmac_driver);
129062306a36Sopenharmony_ci	}
129162306a36Sopenharmony_ci	return rc;
129262306a36Sopenharmony_ci}
129362306a36Sopenharmony_cimodule_init(txx9dmac_init);
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_cistatic void __exit txx9dmac_exit(void)
129662306a36Sopenharmony_ci{
129762306a36Sopenharmony_ci	platform_driver_unregister(&txx9dmac_chan_driver);
129862306a36Sopenharmony_ci	platform_driver_unregister(&txx9dmac_driver);
129962306a36Sopenharmony_ci}
130062306a36Sopenharmony_cimodule_exit(txx9dmac_exit);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
130362306a36Sopenharmony_ciMODULE_DESCRIPTION("TXx9 DMA Controller driver");
130462306a36Sopenharmony_ciMODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
130562306a36Sopenharmony_ciMODULE_ALIAS("platform:txx9dmac");
130662306a36Sopenharmony_ciMODULE_ALIAS("platform:txx9dmac-chan");
1307