18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Renesas SuperH DMA Engine support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * base is drivers/dma/flsdma.c
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
88c2ecf20Sopenharmony_ci * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
98c2ecf20Sopenharmony_ci * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
108c2ecf20Sopenharmony_ci * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * - DMA of SuperH does not have Hardware DMA chain mode.
138c2ecf20Sopenharmony_ci * - MAX DMA size is 16MB.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/delay.h>
188c2ecf20Sopenharmony_ci#include <linux/dmaengine.h>
198c2ecf20Sopenharmony_ci#include <linux/err.h>
208c2ecf20Sopenharmony_ci#include <linux/init.h>
218c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
228c2ecf20Sopenharmony_ci#include <linux/kdebug.h>
238c2ecf20Sopenharmony_ci#include <linux/module.h>
248c2ecf20Sopenharmony_ci#include <linux/notifier.h>
258c2ecf20Sopenharmony_ci#include <linux/of.h>
268c2ecf20Sopenharmony_ci#include <linux/of_device.h>
278c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
288c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
298c2ecf20Sopenharmony_ci#include <linux/rculist.h>
308c2ecf20Sopenharmony_ci#include <linux/sh_dma.h>
318c2ecf20Sopenharmony_ci#include <linux/slab.h>
328c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include "../dmaengine.h"
358c2ecf20Sopenharmony_ci#include "shdma.h"
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* DMA registers */
388c2ecf20Sopenharmony_ci#define SAR	0x00	/* Source Address Register */
398c2ecf20Sopenharmony_ci#define DAR	0x04	/* Destination Address Register */
408c2ecf20Sopenharmony_ci#define TCR	0x08	/* Transfer Count Register */
418c2ecf20Sopenharmony_ci#define CHCR	0x0C	/* Channel Control Register */
428c2ecf20Sopenharmony_ci#define DMAOR	0x40	/* DMA Operation Register */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define TEND	0x18 /* USB-DMAC */
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define SH_DMAE_DRV_NAME "sh-dma-engine"
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* Default MEMCPY transfer size = 2^2 = 4 bytes */
498c2ecf20Sopenharmony_ci#define LOG2_DEFAULT_XFER_SIZE	2
508c2ecf20Sopenharmony_ci#define SH_DMA_SLAVE_NUMBER 256
518c2ecf20Sopenharmony_ci#define SH_DMA_TCR_MAX (16 * 1024 * 1024 - 1)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/*
548c2ecf20Sopenharmony_ci * Used for write-side mutual exclusion for the global device list,
558c2ecf20Sopenharmony_ci * read-side synchronization by way of RCU, and per-controller data.
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sh_dmae_lock);
588c2ecf20Sopenharmony_cistatic LIST_HEAD(sh_dmae_devices);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/*
618c2ecf20Sopenharmony_ci * Different DMAC implementations provide different ways to clear DMA channels:
628c2ecf20Sopenharmony_ci * (1) none - no CHCLR registers are available
638c2ecf20Sopenharmony_ci * (2) one CHCLR register per channel - 0 has to be written to it to clear
648c2ecf20Sopenharmony_ci *     channel buffers
658c2ecf20Sopenharmony_ci * (3) one CHCLR per several channels - 1 has to be written to the bit,
668c2ecf20Sopenharmony_ci *     corresponding to the specific channel to reset it
678c2ecf20Sopenharmony_ci */
688c2ecf20Sopenharmony_cistatic void channel_clear(struct sh_dmae_chan *sh_dc)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
718c2ecf20Sopenharmony_ci	const struct sh_dmae_channel *chan_pdata = shdev->pdata->channel +
728c2ecf20Sopenharmony_ci		sh_dc->shdma_chan.id;
738c2ecf20Sopenharmony_ci	u32 val = shdev->pdata->chclr_bitwise ? 1 << chan_pdata->chclr_bit : 0;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	__raw_writel(val, shdev->chan_reg + chan_pdata->chclr_offset);
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic void sh_dmae_writel(struct sh_dmae_chan *sh_dc, u32 data, u32 reg)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	__raw_writel(data, sh_dc->base + reg);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic u32 sh_dmae_readl(struct sh_dmae_chan *sh_dc, u32 reg)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	return __raw_readl(sh_dc->base + reg);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic u16 dmaor_read(struct sh_dmae_device *shdev)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	void __iomem *addr = shdev->chan_reg + DMAOR;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (shdev->pdata->dmaor_is_32bit)
938c2ecf20Sopenharmony_ci		return __raw_readl(addr);
948c2ecf20Sopenharmony_ci	else
958c2ecf20Sopenharmony_ci		return __raw_readw(addr);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic void dmaor_write(struct sh_dmae_device *shdev, u16 data)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	void __iomem *addr = shdev->chan_reg + DMAOR;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (shdev->pdata->dmaor_is_32bit)
1038c2ecf20Sopenharmony_ci		__raw_writel(data, addr);
1048c2ecf20Sopenharmony_ci	else
1058c2ecf20Sopenharmony_ci		__raw_writew(data, addr);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic void chcr_write(struct sh_dmae_chan *sh_dc, u32 data)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	__raw_writel(data, sh_dc->base + shdev->chcr_offset);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic u32 chcr_read(struct sh_dmae_chan *sh_dc)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return __raw_readl(sh_dc->base + shdev->chcr_offset);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/*
1238c2ecf20Sopenharmony_ci * Reset DMA controller
1248c2ecf20Sopenharmony_ci *
1258c2ecf20Sopenharmony_ci * SH7780 has two DMAOR register
1268c2ecf20Sopenharmony_ci */
1278c2ecf20Sopenharmony_cistatic void sh_dmae_ctl_stop(struct sh_dmae_device *shdev)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	unsigned short dmaor;
1308c2ecf20Sopenharmony_ci	unsigned long flags;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sh_dmae_lock, flags);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	dmaor = dmaor_read(shdev);
1358c2ecf20Sopenharmony_ci	dmaor_write(shdev, dmaor & ~(DMAOR_NMIF | DMAOR_AE | DMAOR_DME));
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sh_dmae_lock, flags);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int sh_dmae_rst(struct sh_dmae_device *shdev)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	unsigned short dmaor;
1438c2ecf20Sopenharmony_ci	unsigned long flags;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sh_dmae_lock, flags);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	dmaor = dmaor_read(shdev) & ~(DMAOR_NMIF | DMAOR_AE | DMAOR_DME);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (shdev->pdata->chclr_present) {
1508c2ecf20Sopenharmony_ci		int i;
1518c2ecf20Sopenharmony_ci		for (i = 0; i < shdev->pdata->channel_num; i++) {
1528c2ecf20Sopenharmony_ci			struct sh_dmae_chan *sh_chan = shdev->chan[i];
1538c2ecf20Sopenharmony_ci			if (sh_chan)
1548c2ecf20Sopenharmony_ci				channel_clear(sh_chan);
1558c2ecf20Sopenharmony_ci		}
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	dmaor_write(shdev, dmaor | shdev->pdata->dmaor_init);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	dmaor = dmaor_read(shdev);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sh_dmae_lock, flags);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (dmaor & (DMAOR_AE | DMAOR_NMIF)) {
1658c2ecf20Sopenharmony_ci		dev_warn(shdev->shdma_dev.dma_dev.dev, "Can't initialize DMAOR.\n");
1668c2ecf20Sopenharmony_ci		return -EIO;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci	if (shdev->pdata->dmaor_init & ~dmaor)
1698c2ecf20Sopenharmony_ci		dev_warn(shdev->shdma_dev.dma_dev.dev,
1708c2ecf20Sopenharmony_ci			 "DMAOR=0x%x hasn't latched the initial value 0x%x.\n",
1718c2ecf20Sopenharmony_ci			 dmaor, shdev->pdata->dmaor_init);
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic bool dmae_is_busy(struct sh_dmae_chan *sh_chan)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	u32 chcr = chcr_read(sh_chan);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if ((chcr & (CHCR_DE | CHCR_TE)) == CHCR_DE)
1808c2ecf20Sopenharmony_ci		return true; /* working */
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return false; /* waiting */
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan, u32 chcr)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
1888c2ecf20Sopenharmony_ci	const struct sh_dmae_pdata *pdata = shdev->pdata;
1898c2ecf20Sopenharmony_ci	int cnt = ((chcr & pdata->ts_low_mask) >> pdata->ts_low_shift) |
1908c2ecf20Sopenharmony_ci		((chcr & pdata->ts_high_mask) >> pdata->ts_high_shift);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (cnt >= pdata->ts_shift_num)
1938c2ecf20Sopenharmony_ci		cnt = 0;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	return pdata->ts_shift[cnt];
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic u32 log2size_to_chcr(struct sh_dmae_chan *sh_chan, int l2size)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
2018c2ecf20Sopenharmony_ci	const struct sh_dmae_pdata *pdata = shdev->pdata;
2028c2ecf20Sopenharmony_ci	int i;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	for (i = 0; i < pdata->ts_shift_num; i++)
2058c2ecf20Sopenharmony_ci		if (pdata->ts_shift[i] == l2size)
2068c2ecf20Sopenharmony_ci			break;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (i == pdata->ts_shift_num)
2098c2ecf20Sopenharmony_ci		i = 0;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	return ((i << pdata->ts_low_shift) & pdata->ts_low_mask) |
2128c2ecf20Sopenharmony_ci		((i << pdata->ts_high_shift) & pdata->ts_high_mask);
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic void dmae_set_reg(struct sh_dmae_chan *sh_chan, struct sh_dmae_regs *hw)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	sh_dmae_writel(sh_chan, hw->sar, SAR);
2188c2ecf20Sopenharmony_ci	sh_dmae_writel(sh_chan, hw->dar, DAR);
2198c2ecf20Sopenharmony_ci	sh_dmae_writel(sh_chan, hw->tcr >> sh_chan->xmit_shift, TCR);
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic void dmae_start(struct sh_dmae_chan *sh_chan)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
2258c2ecf20Sopenharmony_ci	u32 chcr = chcr_read(sh_chan);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (shdev->pdata->needs_tend_set)
2288c2ecf20Sopenharmony_ci		sh_dmae_writel(sh_chan, 0xFFFFFFFF, TEND);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	chcr |= CHCR_DE | shdev->chcr_ie_bit;
2318c2ecf20Sopenharmony_ci	chcr_write(sh_chan, chcr & ~CHCR_TE);
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic void dmae_init(struct sh_dmae_chan *sh_chan)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	/*
2378c2ecf20Sopenharmony_ci	 * Default configuration for dual address memory-memory transfer.
2388c2ecf20Sopenharmony_ci	 */
2398c2ecf20Sopenharmony_ci	u32 chcr = DM_INC | SM_INC | RS_AUTO | log2size_to_chcr(sh_chan,
2408c2ecf20Sopenharmony_ci						   LOG2_DEFAULT_XFER_SIZE);
2418c2ecf20Sopenharmony_ci	sh_chan->xmit_shift = calc_xmit_shift(sh_chan, chcr);
2428c2ecf20Sopenharmony_ci	chcr_write(sh_chan, chcr);
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	/* If DMA is active, cannot set CHCR. TODO: remove this superfluous check */
2488c2ecf20Sopenharmony_ci	if (dmae_is_busy(sh_chan))
2498c2ecf20Sopenharmony_ci		return -EBUSY;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	sh_chan->xmit_shift = calc_xmit_shift(sh_chan, val);
2528c2ecf20Sopenharmony_ci	chcr_write(sh_chan, val);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	return 0;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
2608c2ecf20Sopenharmony_ci	const struct sh_dmae_pdata *pdata = shdev->pdata;
2618c2ecf20Sopenharmony_ci	const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->shdma_chan.id];
2628c2ecf20Sopenharmony_ci	void __iomem *addr = shdev->dmars;
2638c2ecf20Sopenharmony_ci	unsigned int shift = chan_pdata->dmars_bit;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (dmae_is_busy(sh_chan))
2668c2ecf20Sopenharmony_ci		return -EBUSY;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (pdata->no_dmars)
2698c2ecf20Sopenharmony_ci		return 0;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/* in the case of a missing DMARS resource use first memory window */
2728c2ecf20Sopenharmony_ci	if (!addr)
2738c2ecf20Sopenharmony_ci		addr = shdev->chan_reg;
2748c2ecf20Sopenharmony_ci	addr += chan_pdata->dmars;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	__raw_writew((__raw_readw(addr) & (0xff00 >> shift)) | (val << shift),
2778c2ecf20Sopenharmony_ci		     addr);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return 0;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic void sh_dmae_start_xfer(struct shdma_chan *schan,
2838c2ecf20Sopenharmony_ci			       struct shdma_desc *sdesc)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
2868c2ecf20Sopenharmony_ci						    shdma_chan);
2878c2ecf20Sopenharmony_ci	struct sh_dmae_desc *sh_desc = container_of(sdesc,
2888c2ecf20Sopenharmony_ci					struct sh_dmae_desc, shdma_desc);
2898c2ecf20Sopenharmony_ci	dev_dbg(sh_chan->shdma_chan.dev, "Queue #%d to %d: %u@%x -> %x\n",
2908c2ecf20Sopenharmony_ci		sdesc->async_tx.cookie, sh_chan->shdma_chan.id,
2918c2ecf20Sopenharmony_ci		sh_desc->hw.tcr, sh_desc->hw.sar, sh_desc->hw.dar);
2928c2ecf20Sopenharmony_ci	/* Get the ld start address from ld_queue */
2938c2ecf20Sopenharmony_ci	dmae_set_reg(sh_chan, &sh_desc->hw);
2948c2ecf20Sopenharmony_ci	dmae_start(sh_chan);
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic bool sh_dmae_channel_busy(struct shdma_chan *schan)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
3008c2ecf20Sopenharmony_ci						    shdma_chan);
3018c2ecf20Sopenharmony_ci	return dmae_is_busy(sh_chan);
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic void sh_dmae_setup_xfer(struct shdma_chan *schan,
3058c2ecf20Sopenharmony_ci			       int slave_id)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
3088c2ecf20Sopenharmony_ci						    shdma_chan);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (slave_id >= 0) {
3118c2ecf20Sopenharmony_ci		const struct sh_dmae_slave_config *cfg =
3128c2ecf20Sopenharmony_ci			sh_chan->config;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci		dmae_set_dmars(sh_chan, cfg->mid_rid);
3158c2ecf20Sopenharmony_ci		dmae_set_chcr(sh_chan, cfg->chcr);
3168c2ecf20Sopenharmony_ci	} else {
3178c2ecf20Sopenharmony_ci		dmae_init(sh_chan);
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci/*
3228c2ecf20Sopenharmony_ci * Find a slave channel configuration from the contoller list by either a slave
3238c2ecf20Sopenharmony_ci * ID in the non-DT case, or by a MID/RID value in the DT case
3248c2ecf20Sopenharmony_ci */
3258c2ecf20Sopenharmony_cistatic const struct sh_dmae_slave_config *dmae_find_slave(
3268c2ecf20Sopenharmony_ci	struct sh_dmae_chan *sh_chan, int match)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
3298c2ecf20Sopenharmony_ci	const struct sh_dmae_pdata *pdata = shdev->pdata;
3308c2ecf20Sopenharmony_ci	const struct sh_dmae_slave_config *cfg;
3318c2ecf20Sopenharmony_ci	int i;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (!sh_chan->shdma_chan.dev->of_node) {
3348c2ecf20Sopenharmony_ci		if (match >= SH_DMA_SLAVE_NUMBER)
3358c2ecf20Sopenharmony_ci			return NULL;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)
3388c2ecf20Sopenharmony_ci			if (cfg->slave_id == match)
3398c2ecf20Sopenharmony_ci				return cfg;
3408c2ecf20Sopenharmony_ci	} else {
3418c2ecf20Sopenharmony_ci		for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)
3428c2ecf20Sopenharmony_ci			if (cfg->mid_rid == match) {
3438c2ecf20Sopenharmony_ci				sh_chan->shdma_chan.slave_id = i;
3448c2ecf20Sopenharmony_ci				return cfg;
3458c2ecf20Sopenharmony_ci			}
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	return NULL;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic int sh_dmae_set_slave(struct shdma_chan *schan,
3528c2ecf20Sopenharmony_ci			     int slave_id, dma_addr_t slave_addr, bool try)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
3558c2ecf20Sopenharmony_ci						    shdma_chan);
3568c2ecf20Sopenharmony_ci	const struct sh_dmae_slave_config *cfg = dmae_find_slave(sh_chan, slave_id);
3578c2ecf20Sopenharmony_ci	if (!cfg)
3588c2ecf20Sopenharmony_ci		return -ENXIO;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (!try) {
3618c2ecf20Sopenharmony_ci		sh_chan->config = cfg;
3628c2ecf20Sopenharmony_ci		sh_chan->slave_addr = slave_addr ? : cfg->addr;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return 0;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic void dmae_halt(struct sh_dmae_chan *sh_chan)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
3718c2ecf20Sopenharmony_ci	u32 chcr = chcr_read(sh_chan);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	chcr &= ~(CHCR_DE | CHCR_TE | shdev->chcr_ie_bit);
3748c2ecf20Sopenharmony_ci	chcr_write(sh_chan, chcr);
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic int sh_dmae_desc_setup(struct shdma_chan *schan,
3788c2ecf20Sopenharmony_ci			      struct shdma_desc *sdesc,
3798c2ecf20Sopenharmony_ci			      dma_addr_t src, dma_addr_t dst, size_t *len)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct sh_dmae_desc *sh_desc = container_of(sdesc,
3828c2ecf20Sopenharmony_ci					struct sh_dmae_desc, shdma_desc);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	if (*len > schan->max_xfer_len)
3858c2ecf20Sopenharmony_ci		*len = schan->max_xfer_len;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	sh_desc->hw.sar = src;
3888c2ecf20Sopenharmony_ci	sh_desc->hw.dar = dst;
3898c2ecf20Sopenharmony_ci	sh_desc->hw.tcr = *len;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return 0;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic void sh_dmae_halt(struct shdma_chan *schan)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
3978c2ecf20Sopenharmony_ci						    shdma_chan);
3988c2ecf20Sopenharmony_ci	dmae_halt(sh_chan);
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic bool sh_dmae_chan_irq(struct shdma_chan *schan, int irq)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
4048c2ecf20Sopenharmony_ci						    shdma_chan);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	if (!(chcr_read(sh_chan) & CHCR_TE))
4078c2ecf20Sopenharmony_ci		return false;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	/* DMA stop */
4108c2ecf20Sopenharmony_ci	dmae_halt(sh_chan);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	return true;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic size_t sh_dmae_get_partial(struct shdma_chan *schan,
4168c2ecf20Sopenharmony_ci				  struct shdma_desc *sdesc)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
4198c2ecf20Sopenharmony_ci						    shdma_chan);
4208c2ecf20Sopenharmony_ci	struct sh_dmae_desc *sh_desc = container_of(sdesc,
4218c2ecf20Sopenharmony_ci					struct sh_dmae_desc, shdma_desc);
4228c2ecf20Sopenharmony_ci	return sh_desc->hw.tcr -
4238c2ecf20Sopenharmony_ci		(sh_dmae_readl(sh_chan, TCR) << sh_chan->xmit_shift);
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci/* Called from error IRQ or NMI */
4278c2ecf20Sopenharmony_cistatic bool sh_dmae_reset(struct sh_dmae_device *shdev)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	bool ret;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	/* halt the dma controller */
4328c2ecf20Sopenharmony_ci	sh_dmae_ctl_stop(shdev);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/* We cannot detect, which channel caused the error, have to reset all */
4358c2ecf20Sopenharmony_ci	ret = shdma_reset(&shdev->shdma_dev);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	sh_dmae_rst(shdev);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	return ret;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic irqreturn_t sh_dmae_err(int irq, void *data)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = data;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (!(dmaor_read(shdev) & DMAOR_AE))
4478c2ecf20Sopenharmony_ci		return IRQ_NONE;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	sh_dmae_reset(shdev);
4508c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic bool sh_dmae_desc_completed(struct shdma_chan *schan,
4548c2ecf20Sopenharmony_ci				   struct shdma_desc *sdesc)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct sh_dmae_chan *sh_chan = container_of(schan,
4578c2ecf20Sopenharmony_ci					struct sh_dmae_chan, shdma_chan);
4588c2ecf20Sopenharmony_ci	struct sh_dmae_desc *sh_desc = container_of(sdesc,
4598c2ecf20Sopenharmony_ci					struct sh_dmae_desc, shdma_desc);
4608c2ecf20Sopenharmony_ci	u32 sar_buf = sh_dmae_readl(sh_chan, SAR);
4618c2ecf20Sopenharmony_ci	u32 dar_buf = sh_dmae_readl(sh_chan, DAR);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	return	(sdesc->direction == DMA_DEV_TO_MEM &&
4648c2ecf20Sopenharmony_ci		 (sh_desc->hw.dar + sh_desc->hw.tcr) == dar_buf) ||
4658c2ecf20Sopenharmony_ci		(sdesc->direction != DMA_DEV_TO_MEM &&
4668c2ecf20Sopenharmony_ci		 (sh_desc->hw.sar + sh_desc->hw.tcr) == sar_buf);
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic bool sh_dmae_nmi_notify(struct sh_dmae_device *shdev)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	/* Fast path out if NMIF is not asserted for this controller */
4728c2ecf20Sopenharmony_ci	if ((dmaor_read(shdev) & DMAOR_NMIF) == 0)
4738c2ecf20Sopenharmony_ci		return false;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	return sh_dmae_reset(shdev);
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic int sh_dmae_nmi_handler(struct notifier_block *self,
4798c2ecf20Sopenharmony_ci			       unsigned long cmd, void *data)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev;
4828c2ecf20Sopenharmony_ci	int ret = NOTIFY_DONE;
4838c2ecf20Sopenharmony_ci	bool triggered;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	/*
4868c2ecf20Sopenharmony_ci	 * Only concern ourselves with NMI events.
4878c2ecf20Sopenharmony_ci	 *
4888c2ecf20Sopenharmony_ci	 * Normally we would check the die chain value, but as this needs
4898c2ecf20Sopenharmony_ci	 * to be architecture independent, check for NMI context instead.
4908c2ecf20Sopenharmony_ci	 */
4918c2ecf20Sopenharmony_ci	if (!in_nmi())
4928c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	rcu_read_lock();
4958c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(shdev, &sh_dmae_devices, node) {
4968c2ecf20Sopenharmony_ci		/*
4978c2ecf20Sopenharmony_ci		 * Only stop if one of the controllers has NMIF asserted,
4988c2ecf20Sopenharmony_ci		 * we do not want to interfere with regular address error
4998c2ecf20Sopenharmony_ci		 * handling or NMI events that don't concern the DMACs.
5008c2ecf20Sopenharmony_ci		 */
5018c2ecf20Sopenharmony_ci		triggered = sh_dmae_nmi_notify(shdev);
5028c2ecf20Sopenharmony_ci		if (triggered == true)
5038c2ecf20Sopenharmony_ci			ret = NOTIFY_OK;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci	rcu_read_unlock();
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	return ret;
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic struct notifier_block sh_dmae_nmi_notifier __read_mostly = {
5118c2ecf20Sopenharmony_ci	.notifier_call	= sh_dmae_nmi_handler,
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/* Run before NMI debug handler and KGDB */
5148c2ecf20Sopenharmony_ci	.priority	= 1,
5158c2ecf20Sopenharmony_ci};
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic int sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id,
5188c2ecf20Sopenharmony_ci					int irq, unsigned long flags)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	const struct sh_dmae_channel *chan_pdata = &shdev->pdata->channel[id];
5218c2ecf20Sopenharmony_ci	struct shdma_dev *sdev = &shdev->shdma_dev;
5228c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(sdev->dma_dev.dev);
5238c2ecf20Sopenharmony_ci	struct sh_dmae_chan *sh_chan;
5248c2ecf20Sopenharmony_ci	struct shdma_chan *schan;
5258c2ecf20Sopenharmony_ci	int err;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	sh_chan = devm_kzalloc(sdev->dma_dev.dev, sizeof(struct sh_dmae_chan),
5288c2ecf20Sopenharmony_ci			       GFP_KERNEL);
5298c2ecf20Sopenharmony_ci	if (!sh_chan)
5308c2ecf20Sopenharmony_ci		return -ENOMEM;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	schan = &sh_chan->shdma_chan;
5338c2ecf20Sopenharmony_ci	schan->max_xfer_len = SH_DMA_TCR_MAX + 1;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	shdma_chan_probe(sdev, schan, id);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	sh_chan->base = shdev->chan_reg + chan_pdata->offset;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	/* set up channel irq */
5408c2ecf20Sopenharmony_ci	if (pdev->id >= 0)
5418c2ecf20Sopenharmony_ci		snprintf(sh_chan->dev_id, sizeof(sh_chan->dev_id),
5428c2ecf20Sopenharmony_ci			 "sh-dmae%d.%d", pdev->id, id);
5438c2ecf20Sopenharmony_ci	else
5448c2ecf20Sopenharmony_ci		snprintf(sh_chan->dev_id, sizeof(sh_chan->dev_id),
5458c2ecf20Sopenharmony_ci			 "sh-dma%d", id);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	err = shdma_request_irq(schan, irq, flags, sh_chan->dev_id);
5488c2ecf20Sopenharmony_ci	if (err) {
5498c2ecf20Sopenharmony_ci		dev_err(sdev->dma_dev.dev,
5508c2ecf20Sopenharmony_ci			"DMA channel %d request_irq error %d\n",
5518c2ecf20Sopenharmony_ci			id, err);
5528c2ecf20Sopenharmony_ci		goto err_no_irq;
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	shdev->chan[id] = sh_chan;
5568c2ecf20Sopenharmony_ci	return 0;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cierr_no_irq:
5598c2ecf20Sopenharmony_ci	/* remove from dmaengine device node */
5608c2ecf20Sopenharmony_ci	shdma_chan_remove(schan);
5618c2ecf20Sopenharmony_ci	return err;
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic void sh_dmae_chan_remove(struct sh_dmae_device *shdev)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	struct shdma_chan *schan;
5678c2ecf20Sopenharmony_ci	int i;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	shdma_for_each_chan(schan, &shdev->shdma_dev, i) {
5708c2ecf20Sopenharmony_ci		BUG_ON(!schan);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci		shdma_chan_remove(schan);
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
5778c2ecf20Sopenharmony_cistatic int sh_dmae_runtime_suspend(struct device *dev)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = dev_get_drvdata(dev);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	sh_dmae_ctl_stop(shdev);
5828c2ecf20Sopenharmony_ci	return 0;
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_cistatic int sh_dmae_runtime_resume(struct device *dev)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = dev_get_drvdata(dev);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	return sh_dmae_rst(shdev);
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci#endif
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
5948c2ecf20Sopenharmony_cistatic int sh_dmae_suspend(struct device *dev)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = dev_get_drvdata(dev);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	sh_dmae_ctl_stop(shdev);
5998c2ecf20Sopenharmony_ci	return 0;
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic int sh_dmae_resume(struct device *dev)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = dev_get_drvdata(dev);
6058c2ecf20Sopenharmony_ci	int i, ret;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	ret = sh_dmae_rst(shdev);
6088c2ecf20Sopenharmony_ci	if (ret < 0)
6098c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to reset!\n");
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	for (i = 0; i < shdev->pdata->channel_num; i++) {
6128c2ecf20Sopenharmony_ci		struct sh_dmae_chan *sh_chan = shdev->chan[i];
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci		if (!sh_chan->shdma_chan.desc_num)
6158c2ecf20Sopenharmony_ci			continue;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		if (sh_chan->shdma_chan.slave_id >= 0) {
6188c2ecf20Sopenharmony_ci			const struct sh_dmae_slave_config *cfg = sh_chan->config;
6198c2ecf20Sopenharmony_ci			dmae_set_dmars(sh_chan, cfg->mid_rid);
6208c2ecf20Sopenharmony_ci			dmae_set_chcr(sh_chan, cfg->chcr);
6218c2ecf20Sopenharmony_ci		} else {
6228c2ecf20Sopenharmony_ci			dmae_init(sh_chan);
6238c2ecf20Sopenharmony_ci		}
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	return 0;
6278c2ecf20Sopenharmony_ci}
6288c2ecf20Sopenharmony_ci#endif
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic const struct dev_pm_ops sh_dmae_pm = {
6318c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(sh_dmae_suspend, sh_dmae_resume)
6328c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(sh_dmae_runtime_suspend, sh_dmae_runtime_resume,
6338c2ecf20Sopenharmony_ci			   NULL)
6348c2ecf20Sopenharmony_ci};
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic dma_addr_t sh_dmae_slave_addr(struct shdma_chan *schan)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	struct sh_dmae_chan *sh_chan = container_of(schan,
6398c2ecf20Sopenharmony_ci					struct sh_dmae_chan, shdma_chan);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	/*
6428c2ecf20Sopenharmony_ci	 * Implicit BUG_ON(!sh_chan->config)
6438c2ecf20Sopenharmony_ci	 * This is an exclusive slave DMA operation, may only be called after a
6448c2ecf20Sopenharmony_ci	 * successful slave configuration.
6458c2ecf20Sopenharmony_ci	 */
6468c2ecf20Sopenharmony_ci	return sh_chan->slave_addr;
6478c2ecf20Sopenharmony_ci}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic struct shdma_desc *sh_dmae_embedded_desc(void *buf, int i)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	return &((struct sh_dmae_desc *)buf)[i].shdma_desc;
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_cistatic const struct shdma_ops sh_dmae_shdma_ops = {
6558c2ecf20Sopenharmony_ci	.desc_completed = sh_dmae_desc_completed,
6568c2ecf20Sopenharmony_ci	.halt_channel = sh_dmae_halt,
6578c2ecf20Sopenharmony_ci	.channel_busy = sh_dmae_channel_busy,
6588c2ecf20Sopenharmony_ci	.slave_addr = sh_dmae_slave_addr,
6598c2ecf20Sopenharmony_ci	.desc_setup = sh_dmae_desc_setup,
6608c2ecf20Sopenharmony_ci	.set_slave = sh_dmae_set_slave,
6618c2ecf20Sopenharmony_ci	.setup_xfer = sh_dmae_setup_xfer,
6628c2ecf20Sopenharmony_ci	.start_xfer = sh_dmae_start_xfer,
6638c2ecf20Sopenharmony_ci	.embedded_desc = sh_dmae_embedded_desc,
6648c2ecf20Sopenharmony_ci	.chan_irq = sh_dmae_chan_irq,
6658c2ecf20Sopenharmony_ci	.get_partial = sh_dmae_get_partial,
6668c2ecf20Sopenharmony_ci};
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cistatic int sh_dmae_probe(struct platform_device *pdev)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	const enum dma_slave_buswidth widths =
6718c2ecf20Sopenharmony_ci		DMA_SLAVE_BUSWIDTH_1_BYTE   | DMA_SLAVE_BUSWIDTH_2_BYTES |
6728c2ecf20Sopenharmony_ci		DMA_SLAVE_BUSWIDTH_4_BYTES  | DMA_SLAVE_BUSWIDTH_8_BYTES |
6738c2ecf20Sopenharmony_ci		DMA_SLAVE_BUSWIDTH_16_BYTES | DMA_SLAVE_BUSWIDTH_32_BYTES;
6748c2ecf20Sopenharmony_ci	const struct sh_dmae_pdata *pdata;
6758c2ecf20Sopenharmony_ci	unsigned long chan_flag[SH_DMAE_MAX_CHANNELS] = {};
6768c2ecf20Sopenharmony_ci	int chan_irq[SH_DMAE_MAX_CHANNELS];
6778c2ecf20Sopenharmony_ci	unsigned long irqflags = 0;
6788c2ecf20Sopenharmony_ci	int err, errirq, i, irq_cnt = 0, irqres = 0, irq_cap = 0;
6798c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev;
6808c2ecf20Sopenharmony_ci	struct dma_device *dma_dev;
6818c2ecf20Sopenharmony_ci	struct resource *chan, *dmars, *errirq_res, *chanirq_res;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	if (pdev->dev.of_node)
6848c2ecf20Sopenharmony_ci		pdata = of_device_get_match_data(&pdev->dev);
6858c2ecf20Sopenharmony_ci	else
6868c2ecf20Sopenharmony_ci		pdata = dev_get_platdata(&pdev->dev);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	/* get platform data */
6898c2ecf20Sopenharmony_ci	if (!pdata || !pdata->channel_num)
6908c2ecf20Sopenharmony_ci		return -ENODEV;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	chan = platform_get_resource(pdev, IORESOURCE_MEM, 0);
6938c2ecf20Sopenharmony_ci	/* DMARS area is optional */
6948c2ecf20Sopenharmony_ci	dmars = platform_get_resource(pdev, IORESOURCE_MEM, 1);
6958c2ecf20Sopenharmony_ci	/*
6968c2ecf20Sopenharmony_ci	 * IRQ resources:
6978c2ecf20Sopenharmony_ci	 * 1. there always must be at least one IRQ IO-resource. On SH4 it is
6988c2ecf20Sopenharmony_ci	 *    the error IRQ, in which case it is the only IRQ in this resource:
6998c2ecf20Sopenharmony_ci	 *    start == end. If it is the only IRQ resource, all channels also
7008c2ecf20Sopenharmony_ci	 *    use the same IRQ.
7018c2ecf20Sopenharmony_ci	 * 2. DMA channel IRQ resources can be specified one per resource or in
7028c2ecf20Sopenharmony_ci	 *    ranges (start != end)
7038c2ecf20Sopenharmony_ci	 * 3. iff all events (channels and, optionally, error) on this
7048c2ecf20Sopenharmony_ci	 *    controller use the same IRQ, only one IRQ resource can be
7058c2ecf20Sopenharmony_ci	 *    specified, otherwise there must be one IRQ per channel, even if
7068c2ecf20Sopenharmony_ci	 *    some of them are equal
7078c2ecf20Sopenharmony_ci	 * 4. if all IRQs on this controller are equal or if some specific IRQs
7088c2ecf20Sopenharmony_ci	 *    specify IORESOURCE_IRQ_SHAREABLE in their resources, they will be
7098c2ecf20Sopenharmony_ci	 *    requested with the IRQF_SHARED flag
7108c2ecf20Sopenharmony_ci	 */
7118c2ecf20Sopenharmony_ci	errirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
7128c2ecf20Sopenharmony_ci	if (!chan || !errirq_res)
7138c2ecf20Sopenharmony_ci		return -ENODEV;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	shdev = devm_kzalloc(&pdev->dev, sizeof(struct sh_dmae_device),
7168c2ecf20Sopenharmony_ci			     GFP_KERNEL);
7178c2ecf20Sopenharmony_ci	if (!shdev)
7188c2ecf20Sopenharmony_ci		return -ENOMEM;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	dma_dev = &shdev->shdma_dev.dma_dev;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	shdev->chan_reg = devm_ioremap_resource(&pdev->dev, chan);
7238c2ecf20Sopenharmony_ci	if (IS_ERR(shdev->chan_reg))
7248c2ecf20Sopenharmony_ci		return PTR_ERR(shdev->chan_reg);
7258c2ecf20Sopenharmony_ci	if (dmars) {
7268c2ecf20Sopenharmony_ci		shdev->dmars = devm_ioremap_resource(&pdev->dev, dmars);
7278c2ecf20Sopenharmony_ci		if (IS_ERR(shdev->dmars))
7288c2ecf20Sopenharmony_ci			return PTR_ERR(shdev->dmars);
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	dma_dev->src_addr_widths = widths;
7328c2ecf20Sopenharmony_ci	dma_dev->dst_addr_widths = widths;
7338c2ecf20Sopenharmony_ci	dma_dev->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM);
7348c2ecf20Sopenharmony_ci	dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	if (!pdata->slave_only)
7378c2ecf20Sopenharmony_ci		dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask);
7388c2ecf20Sopenharmony_ci	if (pdata->slave && pdata->slave_num)
7398c2ecf20Sopenharmony_ci		dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	/* Default transfer size of 32 bytes requires 32-byte alignment */
7428c2ecf20Sopenharmony_ci	dma_dev->copy_align = LOG2_DEFAULT_XFER_SIZE;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	shdev->shdma_dev.ops = &sh_dmae_shdma_ops;
7458c2ecf20Sopenharmony_ci	shdev->shdma_dev.desc_size = sizeof(struct sh_dmae_desc);
7468c2ecf20Sopenharmony_ci	err = shdma_init(&pdev->dev, &shdev->shdma_dev,
7478c2ecf20Sopenharmony_ci			      pdata->channel_num);
7488c2ecf20Sopenharmony_ci	if (err < 0)
7498c2ecf20Sopenharmony_ci		goto eshdma;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	/* platform data */
7528c2ecf20Sopenharmony_ci	shdev->pdata = pdata;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	if (pdata->chcr_offset)
7558c2ecf20Sopenharmony_ci		shdev->chcr_offset = pdata->chcr_offset;
7568c2ecf20Sopenharmony_ci	else
7578c2ecf20Sopenharmony_ci		shdev->chcr_offset = CHCR;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (pdata->chcr_ie_bit)
7608c2ecf20Sopenharmony_ci		shdev->chcr_ie_bit = pdata->chcr_ie_bit;
7618c2ecf20Sopenharmony_ci	else
7628c2ecf20Sopenharmony_ci		shdev->chcr_ie_bit = CHCR_IE;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, shdev);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
7678c2ecf20Sopenharmony_ci	err = pm_runtime_get_sync(&pdev->dev);
7688c2ecf20Sopenharmony_ci	if (err < 0)
7698c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "%s(): GET = %d\n", __func__, err);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	spin_lock_irq(&sh_dmae_lock);
7728c2ecf20Sopenharmony_ci	list_add_tail_rcu(&shdev->node, &sh_dmae_devices);
7738c2ecf20Sopenharmony_ci	spin_unlock_irq(&sh_dmae_lock);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	/* reset dma controller - only needed as a test */
7768c2ecf20Sopenharmony_ci	err = sh_dmae_rst(shdev);
7778c2ecf20Sopenharmony_ci	if (err)
7788c2ecf20Sopenharmony_ci		goto rst_err;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_CPU_SH4) || IS_ENABLED(CONFIG_ARCH_RENESAS)) {
7818c2ecf20Sopenharmony_ci		chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci		if (!chanirq_res)
7848c2ecf20Sopenharmony_ci			chanirq_res = errirq_res;
7858c2ecf20Sopenharmony_ci		else
7868c2ecf20Sopenharmony_ci			irqres++;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci		if (chanirq_res == errirq_res ||
7898c2ecf20Sopenharmony_ci		    (errirq_res->flags & IORESOURCE_BITS) == IORESOURCE_IRQ_SHAREABLE)
7908c2ecf20Sopenharmony_ci			irqflags = IRQF_SHARED;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci		errirq = errirq_res->start;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci		err = devm_request_irq(&pdev->dev, errirq, sh_dmae_err,
7958c2ecf20Sopenharmony_ci				       irqflags, "DMAC Address Error", shdev);
7968c2ecf20Sopenharmony_ci		if (err) {
7978c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
7988c2ecf20Sopenharmony_ci				"DMA failed requesting irq #%d, error %d\n",
7998c2ecf20Sopenharmony_ci				errirq, err);
8008c2ecf20Sopenharmony_ci			goto eirq_err;
8018c2ecf20Sopenharmony_ci		}
8028c2ecf20Sopenharmony_ci	} else {
8038c2ecf20Sopenharmony_ci		chanirq_res = errirq_res;
8048c2ecf20Sopenharmony_ci	}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	if (chanirq_res->start == chanirq_res->end &&
8078c2ecf20Sopenharmony_ci	    !platform_get_resource(pdev, IORESOURCE_IRQ, 1)) {
8088c2ecf20Sopenharmony_ci		/* Special case - all multiplexed */
8098c2ecf20Sopenharmony_ci		for (; irq_cnt < pdata->channel_num; irq_cnt++) {
8108c2ecf20Sopenharmony_ci			if (irq_cnt < SH_DMAE_MAX_CHANNELS) {
8118c2ecf20Sopenharmony_ci				chan_irq[irq_cnt] = chanirq_res->start;
8128c2ecf20Sopenharmony_ci				chan_flag[irq_cnt] = IRQF_SHARED;
8138c2ecf20Sopenharmony_ci			} else {
8148c2ecf20Sopenharmony_ci				irq_cap = 1;
8158c2ecf20Sopenharmony_ci				break;
8168c2ecf20Sopenharmony_ci			}
8178c2ecf20Sopenharmony_ci		}
8188c2ecf20Sopenharmony_ci	} else {
8198c2ecf20Sopenharmony_ci		do {
8208c2ecf20Sopenharmony_ci			for (i = chanirq_res->start; i <= chanirq_res->end; i++) {
8218c2ecf20Sopenharmony_ci				if (irq_cnt >= SH_DMAE_MAX_CHANNELS) {
8228c2ecf20Sopenharmony_ci					irq_cap = 1;
8238c2ecf20Sopenharmony_ci					break;
8248c2ecf20Sopenharmony_ci				}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci				if ((errirq_res->flags & IORESOURCE_BITS) ==
8278c2ecf20Sopenharmony_ci				    IORESOURCE_IRQ_SHAREABLE)
8288c2ecf20Sopenharmony_ci					chan_flag[irq_cnt] = IRQF_SHARED;
8298c2ecf20Sopenharmony_ci				else
8308c2ecf20Sopenharmony_ci					chan_flag[irq_cnt] = 0;
8318c2ecf20Sopenharmony_ci				dev_dbg(&pdev->dev,
8328c2ecf20Sopenharmony_ci					"Found IRQ %d for channel %d\n",
8338c2ecf20Sopenharmony_ci					i, irq_cnt);
8348c2ecf20Sopenharmony_ci				chan_irq[irq_cnt++] = i;
8358c2ecf20Sopenharmony_ci			}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci			if (irq_cnt >= SH_DMAE_MAX_CHANNELS)
8388c2ecf20Sopenharmony_ci				break;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci			chanirq_res = platform_get_resource(pdev,
8418c2ecf20Sopenharmony_ci						IORESOURCE_IRQ, ++irqres);
8428c2ecf20Sopenharmony_ci		} while (irq_cnt < pdata->channel_num && chanirq_res);
8438c2ecf20Sopenharmony_ci	}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	/* Create DMA Channel */
8468c2ecf20Sopenharmony_ci	for (i = 0; i < irq_cnt; i++) {
8478c2ecf20Sopenharmony_ci		err = sh_dmae_chan_probe(shdev, i, chan_irq[i], chan_flag[i]);
8488c2ecf20Sopenharmony_ci		if (err)
8498c2ecf20Sopenharmony_ci			goto chan_probe_err;
8508c2ecf20Sopenharmony_ci	}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	if (irq_cap)
8538c2ecf20Sopenharmony_ci		dev_notice(&pdev->dev, "Attempting to register %d DMA "
8548c2ecf20Sopenharmony_ci			   "channels when a maximum of %d are supported.\n",
8558c2ecf20Sopenharmony_ci			   pdata->channel_num, SH_DMAE_MAX_CHANNELS);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	pm_runtime_put(&pdev->dev);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	err = dma_async_device_register(&shdev->shdma_dev.dma_dev);
8608c2ecf20Sopenharmony_ci	if (err < 0)
8618c2ecf20Sopenharmony_ci		goto edmadevreg;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	return err;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ciedmadevreg:
8668c2ecf20Sopenharmony_ci	pm_runtime_get(&pdev->dev);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_cichan_probe_err:
8698c2ecf20Sopenharmony_ci	sh_dmae_chan_remove(shdev);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_cieirq_err:
8728c2ecf20Sopenharmony_cirst_err:
8738c2ecf20Sopenharmony_ci	spin_lock_irq(&sh_dmae_lock);
8748c2ecf20Sopenharmony_ci	list_del_rcu(&shdev->node);
8758c2ecf20Sopenharmony_ci	spin_unlock_irq(&sh_dmae_lock);
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	pm_runtime_put(&pdev->dev);
8788c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	shdma_cleanup(&shdev->shdma_dev);
8818c2ecf20Sopenharmony_cieshdma:
8828c2ecf20Sopenharmony_ci	synchronize_rcu();
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	return err;
8858c2ecf20Sopenharmony_ci}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_cistatic int sh_dmae_remove(struct platform_device *pdev)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	struct sh_dmae_device *shdev = platform_get_drvdata(pdev);
8908c2ecf20Sopenharmony_ci	struct dma_device *dma_dev = &shdev->shdma_dev.dma_dev;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	dma_async_device_unregister(dma_dev);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	spin_lock_irq(&sh_dmae_lock);
8958c2ecf20Sopenharmony_ci	list_del_rcu(&shdev->node);
8968c2ecf20Sopenharmony_ci	spin_unlock_irq(&sh_dmae_lock);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	sh_dmae_chan_remove(shdev);
9018c2ecf20Sopenharmony_ci	shdma_cleanup(&shdev->shdma_dev);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	synchronize_rcu();
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	return 0;
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_cistatic struct platform_driver sh_dmae_driver = {
9098c2ecf20Sopenharmony_ci	.driver		= {
9108c2ecf20Sopenharmony_ci		.pm	= &sh_dmae_pm,
9118c2ecf20Sopenharmony_ci		.name	= SH_DMAE_DRV_NAME,
9128c2ecf20Sopenharmony_ci	},
9138c2ecf20Sopenharmony_ci	.remove		= sh_dmae_remove,
9148c2ecf20Sopenharmony_ci};
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_cistatic int __init sh_dmae_init(void)
9178c2ecf20Sopenharmony_ci{
9188c2ecf20Sopenharmony_ci	/* Wire up NMI handling */
9198c2ecf20Sopenharmony_ci	int err = register_die_notifier(&sh_dmae_nmi_notifier);
9208c2ecf20Sopenharmony_ci	if (err)
9218c2ecf20Sopenharmony_ci		return err;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	return platform_driver_probe(&sh_dmae_driver, sh_dmae_probe);
9248c2ecf20Sopenharmony_ci}
9258c2ecf20Sopenharmony_cimodule_init(sh_dmae_init);
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_cistatic void __exit sh_dmae_exit(void)
9288c2ecf20Sopenharmony_ci{
9298c2ecf20Sopenharmony_ci	platform_driver_unregister(&sh_dmae_driver);
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	unregister_die_notifier(&sh_dmae_nmi_notifier);
9328c2ecf20Sopenharmony_ci}
9338c2ecf20Sopenharmony_cimodule_exit(sh_dmae_exit);
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>");
9368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Renesas SH DMA Engine driver");
9378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
9388c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" SH_DMAE_DRV_NAME);
939