v4.19.13 snapshot.
diff --git a/arch/sh/drivers/dma/Kconfig b/arch/sh/drivers/dma/Kconfig
new file mode 100644
index 0000000..d0de378
--- /dev/null
+++ b/arch/sh/drivers/dma/Kconfig
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: GPL-2.0
+menu "DMA support"
+
+
+config SH_DMA
+	bool "SuperH on-chip DMA controller (DMAC) support"
+	depends on CPU_SH3 || CPU_SH4
+	default n
+
+config SH_DMA_IRQ_MULTI
+	bool
+	depends on SH_DMA
+	default y if CPU_SUBTYPE_SH7750  || CPU_SUBTYPE_SH7751  || \
+		     CPU_SUBTYPE_SH7750S || CPU_SUBTYPE_SH7750R || \
+		     CPU_SUBTYPE_SH7751R || CPU_SUBTYPE_SH7091  || \
+		     CPU_SUBTYPE_SH7763  || CPU_SUBTYPE_SH7780  || \
+		     CPU_SUBTYPE_SH7785  || CPU_SUBTYPE_SH7760
+
+config SH_DMA_API
+	depends on SH_DMA
+	bool "SuperH DMA API support"
+	default n
+	help
+	  SH_DMA_API always enabled DMA API of used SuperH.
+	  If you want to use DMA ENGINE, you must not enable this.
+	  Please enable DMA_ENGINE and SH_DMAE.
+
+config NR_ONCHIP_DMA_CHANNELS
+	int
+	depends on SH_DMA
+	default "4" if CPU_SUBTYPE_SH7750  || CPU_SUBTYPE_SH7751  || \
+		       CPU_SUBTYPE_SH7750S || CPU_SUBTYPE_SH7091
+	default "8" if CPU_SUBTYPE_SH7750R || CPU_SUBTYPE_SH7751R || \
+		       CPU_SUBTYPE_SH7760
+	default "12" if CPU_SUBTYPE_SH7723 || CPU_SUBTYPE_SH7780  || \
+			CPU_SUBTYPE_SH7785 || CPU_SUBTYPE_SH7724
+	default "6"
+	help
+	  This allows you to specify the number of channels that the on-chip
+	  DMAC supports. This will be 4 for SH7750/SH7751/Sh7750S/SH7091 and 8 for the
+	  SH7750R/SH7751R/SH7760, 12 for the SH7723/SH7780/SH7785/SH7724, default is 6.
+
+config SH_DMABRG
+	bool "SH7760 DMABRG support"
+	depends on CPU_SUBTYPE_SH7760
+	help
+	  The DMABRG does data transfers from main memory to Audio/USB units
+	  of the SH7760.
+	  Say Y if you want to use Audio/USB DMA on your SH7760 board.
+
+config PVR2_DMA
+	tristate "PowerVR 2 DMAC support"
+	depends on SH_DREAMCAST && SH_DMA
+	help
+	  Selecting this will enable support for the PVR2 DMA controller.
+	  As this chains off of the on-chip DMAC, that must also be
+	  enabled by default.
+
+	  This is primarily used by the pvr2fb framebuffer driver for
+	  certain optimizations, but is not necessary for functionality.
+
+	  If in doubt, say N.
+
+config G2_DMA
+	tristate "G2 Bus DMA support"
+	depends on SH_DREAMCAST
+	select SH_DMA_API
+	help
+	  This enables support for the DMA controller for the Dreamcast's
+	  G2 bus. Drivers that want this will generally enable this on
+	  their own.
+
+	  If in doubt, say N.
+
+endmenu
diff --git a/arch/sh/drivers/dma/Makefile b/arch/sh/drivers/dma/Makefile
new file mode 100644
index 0000000..d88c948
--- /dev/null
+++ b/arch/sh/drivers/dma/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the SuperH DMA specific kernel interface routines under Linux.
+#
+
+obj-$(CONFIG_SH_DMA_API)	+= dma-sh.o dma-api.o dma-sysfs.o
+obj-$(CONFIG_PVR2_DMA)		+= dma-pvr2.o
+obj-$(CONFIG_G2_DMA)		+= dma-g2.o
+obj-$(CONFIG_SH_DMABRG)		+= dmabrg.o
diff --git a/arch/sh/drivers/dma/dma-api.c b/arch/sh/drivers/dma/dma-api.c
new file mode 100644
index 0000000..b05be59
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-api.c
@@ -0,0 +1,420 @@
+/*
+ * arch/sh/drivers/dma/dma-api.c
+ *
+ * SuperH-specific DMA management API
+ *
+ * Copyright (C) 2003, 2004, 2005  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <asm/dma.h>
+
+DEFINE_SPINLOCK(dma_spin_lock);
+static LIST_HEAD(registered_dmac_list);
+
+struct dma_info *get_dma_info(unsigned int chan)
+{
+	struct dma_info *info;
+
+	/*
+	 * Look for each DMAC's range to determine who the owner of
+	 * the channel is.
+	 */
+	list_for_each_entry(info, &registered_dmac_list, list) {
+		if ((chan <  info->first_vchannel_nr) ||
+		    (chan >= info->first_vchannel_nr + info->nr_channels))
+			continue;
+
+		return info;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(get_dma_info);
+
+struct dma_info *get_dma_info_by_name(const char *dmac_name)
+{
+	struct dma_info *info;
+
+	list_for_each_entry(info, &registered_dmac_list, list) {
+		if (dmac_name && (strcmp(dmac_name, info->name) != 0))
+			continue;
+		else
+			return info;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(get_dma_info_by_name);
+
+static unsigned int get_nr_channels(void)
+{
+	struct dma_info *info;
+	unsigned int nr = 0;
+
+	if (unlikely(list_empty(&registered_dmac_list)))
+		return nr;
+
+	list_for_each_entry(info, &registered_dmac_list, list)
+		nr += info->nr_channels;
+
+	return nr;
+}
+
+struct dma_channel *get_dma_channel(unsigned int chan)
+{
+	struct dma_info *info = get_dma_info(chan);
+	struct dma_channel *channel;
+	int i;
+
+	if (unlikely(!info))
+		return ERR_PTR(-EINVAL);
+
+	for (i = 0; i < info->nr_channels; i++) {
+		channel = &info->channels[i];
+		if (channel->vchan == chan)
+			return channel;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(get_dma_channel);
+
+int get_dma_residue(unsigned int chan)
+{
+	struct dma_info *info = get_dma_info(chan);
+	struct dma_channel *channel = get_dma_channel(chan);
+
+	if (info->ops->get_residue)
+		return info->ops->get_residue(channel);
+
+	return 0;
+}
+EXPORT_SYMBOL(get_dma_residue);
+
+static int search_cap(const char **haystack, const char *needle)
+{
+	const char **p;
+
+	for (p = haystack; *p; p++)
+		if (strcmp(*p, needle) == 0)
+			return 1;
+
+	return 0;
+}
+
+/**
+ * request_dma_bycap - Allocate a DMA channel based on its capabilities
+ * @dmac: List of DMA controllers to search
+ * @caps: List of capabilities
+ *
+ * Search all channels of all DMA controllers to find a channel which
+ * matches the requested capabilities. The result is the channel
+ * number if a match is found, or %-ENODEV if no match is found.
+ *
+ * Note that not all DMA controllers export capabilities, in which
+ * case they can never be allocated using this API, and so
+ * request_dma() must be used specifying the channel number.
+ */
+int request_dma_bycap(const char **dmac, const char **caps, const char *dev_id)
+{
+	unsigned int found = 0;
+	struct dma_info *info;
+	const char **p;
+	int i;
+
+	BUG_ON(!dmac || !caps);
+
+	list_for_each_entry(info, &registered_dmac_list, list)
+		if (strcmp(*dmac, info->name) == 0) {
+			found = 1;
+			break;
+		}
+
+	if (!found)
+		return -ENODEV;
+
+	for (i = 0; i < info->nr_channels; i++) {
+		struct dma_channel *channel = &info->channels[i];
+
+		if (unlikely(!channel->caps))
+			continue;
+
+		for (p = caps; *p; p++) {
+			if (!search_cap(channel->caps, *p))
+				break;
+			if (request_dma(channel->chan, dev_id) == 0)
+				return channel->chan;
+		}
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(request_dma_bycap);
+
+int dmac_search_free_channel(const char *dev_id)
+{
+	struct dma_channel *channel = { 0 };
+	struct dma_info *info = get_dma_info(0);
+	int i;
+
+	for (i = 0; i < info->nr_channels; i++) {
+		channel = &info->channels[i];
+		if (unlikely(!channel))
+			return -ENODEV;
+
+		if (atomic_read(&channel->busy) == 0)
+			break;
+	}
+
+	if (info->ops->request) {
+		int result = info->ops->request(channel);
+		if (result)
+			return result;
+
+		atomic_set(&channel->busy, 1);
+		return channel->chan;
+	}
+
+	return -ENOSYS;
+}
+
+int request_dma(unsigned int chan, const char *dev_id)
+{
+	struct dma_channel *channel = { 0 };
+	struct dma_info *info = get_dma_info(chan);
+	int result;
+
+	channel = get_dma_channel(chan);
+	if (atomic_xchg(&channel->busy, 1))
+		return -EBUSY;
+
+	strlcpy(channel->dev_id, dev_id, sizeof(channel->dev_id));
+
+	if (info->ops->request) {
+		result = info->ops->request(channel);
+		if (result)
+			atomic_set(&channel->busy, 0);
+
+		return result;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(request_dma);
+
+void free_dma(unsigned int chan)
+{
+	struct dma_info *info = get_dma_info(chan);
+	struct dma_channel *channel = get_dma_channel(chan);
+
+	if (info->ops->free)
+		info->ops->free(channel);
+
+	atomic_set(&channel->busy, 0);
+}
+EXPORT_SYMBOL(free_dma);
+
+void dma_wait_for_completion(unsigned int chan)
+{
+	struct dma_info *info = get_dma_info(chan);
+	struct dma_channel *channel = get_dma_channel(chan);
+
+	if (channel->flags & DMA_TEI_CAPABLE) {
+		wait_event(channel->wait_queue,
+			   (info->ops->get_residue(channel) == 0));
+		return;
+	}
+
+	while (info->ops->get_residue(channel))
+		cpu_relax();
+}
+EXPORT_SYMBOL(dma_wait_for_completion);
+
+int register_chan_caps(const char *dmac, struct dma_chan_caps *caps)
+{
+	struct dma_info *info;
+	unsigned int found = 0;
+	int i;
+
+	list_for_each_entry(info, &registered_dmac_list, list)
+		if (strcmp(dmac, info->name) == 0) {
+			found = 1;
+			break;
+		}
+
+	if (unlikely(!found))
+		return -ENODEV;
+
+	for (i = 0; i < info->nr_channels; i++, caps++) {
+		struct dma_channel *channel;
+
+		if ((info->first_channel_nr + i) != caps->ch_num)
+			return -EINVAL;
+
+		channel = &info->channels[i];
+		channel->caps = caps->caplist;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(register_chan_caps);
+
+void dma_configure_channel(unsigned int chan, unsigned long flags)
+{
+	struct dma_info *info = get_dma_info(chan);
+	struct dma_channel *channel = get_dma_channel(chan);
+
+	if (info->ops->configure)
+		info->ops->configure(channel, flags);
+}
+EXPORT_SYMBOL(dma_configure_channel);
+
+int dma_xfer(unsigned int chan, unsigned long from,
+	     unsigned long to, size_t size, unsigned int mode)
+{
+	struct dma_info *info = get_dma_info(chan);
+	struct dma_channel *channel = get_dma_channel(chan);
+
+	channel->sar	= from;
+	channel->dar	= to;
+	channel->count	= size;
+	channel->mode	= mode;
+
+	return info->ops->xfer(channel);
+}
+EXPORT_SYMBOL(dma_xfer);
+
+int dma_extend(unsigned int chan, unsigned long op, void *param)
+{
+	struct dma_info *info = get_dma_info(chan);
+	struct dma_channel *channel = get_dma_channel(chan);
+
+	if (info->ops->extend)
+		return info->ops->extend(channel, op, param);
+
+	return -ENOSYS;
+}
+EXPORT_SYMBOL(dma_extend);
+
+static int dma_proc_show(struct seq_file *m, void *v)
+{
+	struct dma_info *info = v;
+
+	if (list_empty(&registered_dmac_list))
+		return 0;
+
+	/*
+	 * Iterate over each registered DMAC
+	 */
+	list_for_each_entry(info, &registered_dmac_list, list) {
+		int i;
+
+		/*
+		 * Iterate over each channel
+		 */
+		for (i = 0; i < info->nr_channels; i++) {
+			struct dma_channel *channel = info->channels + i;
+
+			if (!(channel->flags & DMA_CONFIGURED))
+				continue;
+
+			seq_printf(m, "%2d: %14s    %s\n", i,
+				   info->name, channel->dev_id);
+		}
+	}
+
+	return 0;
+}
+
+int register_dmac(struct dma_info *info)
+{
+	unsigned int total_channels, i;
+
+	INIT_LIST_HEAD(&info->list);
+
+	printk(KERN_INFO "DMA: Registering %s handler (%d channel%s).\n",
+	       info->name, info->nr_channels, info->nr_channels > 1 ? "s" : "");
+
+	BUG_ON((info->flags & DMAC_CHANNELS_CONFIGURED) && !info->channels);
+
+	info->pdev = platform_device_register_simple(info->name, -1,
+						     NULL, 0);
+	if (IS_ERR(info->pdev))
+		return PTR_ERR(info->pdev);
+
+	/*
+	 * Don't touch pre-configured channels
+	 */
+	if (!(info->flags & DMAC_CHANNELS_CONFIGURED)) {
+		unsigned int size;
+
+		size = sizeof(struct dma_channel) * info->nr_channels;
+
+		info->channels = kzalloc(size, GFP_KERNEL);
+		if (!info->channels)
+			return -ENOMEM;
+	}
+
+	total_channels = get_nr_channels();
+	info->first_vchannel_nr = total_channels;
+	for (i = 0; i < info->nr_channels; i++) {
+		struct dma_channel *chan = &info->channels[i];
+
+		atomic_set(&chan->busy, 0);
+
+		chan->chan  = info->first_channel_nr + i;
+		chan->vchan = info->first_channel_nr + i + total_channels;
+
+		memcpy(chan->dev_id, "Unused", 7);
+
+		if (info->flags & DMAC_CHANNELS_TEI_CAPABLE)
+			chan->flags |= DMA_TEI_CAPABLE;
+
+		init_waitqueue_head(&chan->wait_queue);
+		dma_create_sysfs_files(chan, info);
+	}
+
+	list_add(&info->list, &registered_dmac_list);
+
+	return 0;
+}
+EXPORT_SYMBOL(register_dmac);
+
+void unregister_dmac(struct dma_info *info)
+{
+	unsigned int i;
+
+	for (i = 0; i < info->nr_channels; i++)
+		dma_remove_sysfs_files(info->channels + i, info);
+
+	if (!(info->flags & DMAC_CHANNELS_CONFIGURED))
+		kfree(info->channels);
+
+	list_del(&info->list);
+	platform_device_unregister(info->pdev);
+}
+EXPORT_SYMBOL(unregister_dmac);
+
+static int __init dma_api_init(void)
+{
+	printk(KERN_NOTICE "DMA: Registering DMA API.\n");
+	return proc_create_single("dma", 0, NULL, dma_proc_show) ? 0 : -ENOMEM;
+}
+subsys_initcall(dma_api_init);
+
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
+MODULE_DESCRIPTION("DMA API for SuperH");
+MODULE_LICENSE("GPL");
diff --git a/arch/sh/drivers/dma/dma-g2.c b/arch/sh/drivers/dma/dma-g2.c
new file mode 100644
index 0000000..e1ab6eb
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-g2.c
@@ -0,0 +1,200 @@
+/*
+ * arch/sh/drivers/dma/dma-g2.c
+ *
+ * G2 bus DMA support
+ *
+ * Copyright (C) 2003 - 2006  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <asm/cacheflush.h>
+#include <mach/sysasic.h>
+#include <mach/dma.h>
+#include <asm/dma.h>
+
+struct g2_channel {
+	unsigned long g2_addr;		/* G2 bus address */
+	unsigned long root_addr;	/* Root bus (SH-4) address */
+	unsigned long size;		/* Size (in bytes), 32-byte aligned */
+	unsigned long direction;	/* Transfer direction */
+	unsigned long ctrl;		/* Transfer control */
+	unsigned long chan_enable;	/* Channel enable */
+	unsigned long xfer_enable;	/* Transfer enable */
+	unsigned long xfer_stat;	/* Transfer status */
+} __attribute__ ((aligned(32)));
+
+struct g2_status {
+	unsigned long g2_addr;
+	unsigned long root_addr;
+	unsigned long size;
+	unsigned long status;
+} __attribute__ ((aligned(16)));
+
+struct g2_dma_info {
+	struct g2_channel channel[G2_NR_DMA_CHANNELS];
+	unsigned long pad1[G2_NR_DMA_CHANNELS];
+	unsigned long wait_state;
+	unsigned long pad2[10];
+	unsigned long magic;
+	struct g2_status status[G2_NR_DMA_CHANNELS];
+} __attribute__ ((aligned(256)));
+
+static volatile struct g2_dma_info *g2_dma = (volatile struct g2_dma_info *)0xa05f7800;
+
+#define g2_bytes_remaining(i) \
+	((g2_dma->channel[i].size - \
+	  g2_dma->status[i].size) & 0x0fffffff)
+
+static irqreturn_t g2_dma_interrupt(int irq, void *dev_id)
+{
+	int i;
+
+	for (i = 0; i < G2_NR_DMA_CHANNELS; i++) {
+		if (g2_dma->status[i].status & 0x20000000) {
+			unsigned int bytes = g2_bytes_remaining(i);
+
+			if (likely(bytes == 0)) {
+				struct dma_info *info = dev_id;
+				struct dma_channel *chan = info->channels + i;
+
+				wake_up(&chan->wait_queue);
+
+				return IRQ_HANDLED;
+			}
+		}
+	}
+
+	return IRQ_NONE;
+}
+
+static int g2_enable_dma(struct dma_channel *chan)
+{
+	unsigned int chan_nr = chan->chan;
+
+	g2_dma->channel[chan_nr].chan_enable = 1;
+	g2_dma->channel[chan_nr].xfer_enable = 1;
+
+	return 0;
+}
+
+static int g2_disable_dma(struct dma_channel *chan)
+{
+	unsigned int chan_nr = chan->chan;
+
+	g2_dma->channel[chan_nr].chan_enable = 0;
+	g2_dma->channel[chan_nr].xfer_enable = 0;
+
+	return 0;
+}
+
+static int g2_xfer_dma(struct dma_channel *chan)
+{
+	unsigned int chan_nr = chan->chan;
+
+	if (chan->sar & 31) {
+		printk("g2dma: unaligned source 0x%lx\n", chan->sar);
+		return -EINVAL;
+	}
+
+	if (chan->dar & 31) {
+		printk("g2dma: unaligned dest 0x%lx\n", chan->dar);
+		return -EINVAL;
+	}
+
+	/* Align the count */
+	if (chan->count & 31)
+		chan->count = (chan->count + (32 - 1)) & ~(32 - 1);
+
+	/* Fixup destination */
+	chan->dar += 0xa0800000;
+
+	/* Fixup direction */
+	chan->mode = !chan->mode;
+
+	flush_icache_range((unsigned long)chan->sar, chan->count);
+
+	g2_disable_dma(chan);
+
+	g2_dma->channel[chan_nr].g2_addr   = chan->dar & 0x1fffffe0;
+	g2_dma->channel[chan_nr].root_addr = chan->sar & 0x1fffffe0;
+	g2_dma->channel[chan_nr].size	   = (chan->count & ~31) | 0x80000000;
+	g2_dma->channel[chan_nr].direction = chan->mode;
+
+	/*
+	 * bit 0 - ???
+	 * bit 1 - if set, generate a hardware event on transfer completion
+	 * bit 2 - ??? something to do with suspend?
+	 */
+	g2_dma->channel[chan_nr].ctrl	= 5; /* ?? */
+
+	g2_enable_dma(chan);
+
+	/* debug cruft */
+	pr_debug("count, sar, dar, mode, ctrl, chan, xfer: %ld, 0x%08lx, "
+		 "0x%08lx, %ld, %ld, %ld, %ld\n",
+		 g2_dma->channel[chan_nr].size,
+		 g2_dma->channel[chan_nr].root_addr,
+		 g2_dma->channel[chan_nr].g2_addr,
+		 g2_dma->channel[chan_nr].direction,
+		 g2_dma->channel[chan_nr].ctrl,
+		 g2_dma->channel[chan_nr].chan_enable,
+		 g2_dma->channel[chan_nr].xfer_enable);
+
+	return 0;
+}
+
+static int g2_get_residue(struct dma_channel *chan)
+{
+	return g2_bytes_remaining(chan->chan);
+}
+
+static struct dma_ops g2_dma_ops = {
+	.xfer		= g2_xfer_dma,
+	.get_residue	= g2_get_residue,
+};
+
+static struct dma_info g2_dma_info = {
+	.name		= "g2_dmac",
+	.nr_channels	= 4,
+	.ops		= &g2_dma_ops,
+	.flags		= DMAC_CHANNELS_TEI_CAPABLE,
+};
+
+static int __init g2_dma_init(void)
+{
+	int ret;
+
+	ret = request_irq(HW_EVENT_G2_DMA, g2_dma_interrupt, 0,
+			  "g2 DMA handler", &g2_dma_info);
+	if (unlikely(ret))
+		return -EINVAL;
+
+	/* Magic */
+	g2_dma->wait_state	= 27;
+	g2_dma->magic		= 0x4659404f;
+
+	ret = register_dmac(&g2_dma_info);
+	if (unlikely(ret != 0))
+		free_irq(HW_EVENT_G2_DMA, &g2_dma_info);
+
+	return ret;
+}
+
+static void __exit g2_dma_exit(void)
+{
+	free_irq(HW_EVENT_G2_DMA, &g2_dma_info);
+	unregister_dmac(&g2_dma_info);
+}
+
+subsys_initcall(g2_dma_init);
+module_exit(g2_dma_exit);
+
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
+MODULE_DESCRIPTION("G2 bus DMA driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/sh/drivers/dma/dma-pvr2.c b/arch/sh/drivers/dma/dma-pvr2.c
new file mode 100644
index 0000000..706a343
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-pvr2.c
@@ -0,0 +1,108 @@
+/*
+ * arch/sh/drivers/dma/dma-pvr2.c
+ *
+ * NEC PowerVR 2 (Dreamcast) DMA support
+ *
+ * Copyright (C) 2003, 2004  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <mach/sysasic.h>
+#include <mach/dma.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+
+static unsigned int xfer_complete;
+static int count;
+
+static irqreturn_t pvr2_dma_interrupt(int irq, void *dev_id)
+{
+	if (get_dma_residue(PVR2_CASCADE_CHAN)) {
+		printk(KERN_WARNING "DMA: SH DMAC did not complete transfer "
+		       "on channel %d, waiting..\n", PVR2_CASCADE_CHAN);
+		dma_wait_for_completion(PVR2_CASCADE_CHAN);
+	}
+
+	if (count++ < 10)
+		pr_debug("Got a pvr2 dma interrupt for channel %d\n",
+			 irq - HW_EVENT_PVR2_DMA);
+
+	xfer_complete = 1;
+
+	return IRQ_HANDLED;
+}
+
+static int pvr2_request_dma(struct dma_channel *chan)
+{
+	if (__raw_readl(PVR2_DMA_MODE) != 0)
+		return -EBUSY;
+
+	__raw_writel(0, PVR2_DMA_LMMODE0);
+
+	return 0;
+}
+
+static int pvr2_get_dma_residue(struct dma_channel *chan)
+{
+	return xfer_complete == 0;
+}
+
+static int pvr2_xfer_dma(struct dma_channel *chan)
+{
+	if (chan->sar || !chan->dar)
+		return -EINVAL;
+
+	xfer_complete = 0;
+
+	__raw_writel(chan->dar, PVR2_DMA_ADDR);
+	__raw_writel(chan->count, PVR2_DMA_COUNT);
+	__raw_writel(chan->mode & DMA_MODE_MASK, PVR2_DMA_MODE);
+
+	return 0;
+}
+
+static struct irqaction pvr2_dma_irq = {
+	.name		= "pvr2 DMA handler",
+	.handler	= pvr2_dma_interrupt,
+};
+
+static struct dma_ops pvr2_dma_ops = {
+	.request	= pvr2_request_dma,
+	.get_residue	= pvr2_get_dma_residue,
+	.xfer		= pvr2_xfer_dma,
+};
+
+static struct dma_info pvr2_dma_info = {
+	.name		= "pvr2_dmac",
+	.nr_channels	= 1,
+	.ops		= &pvr2_dma_ops,
+	.flags		= DMAC_CHANNELS_TEI_CAPABLE,
+};
+
+static int __init pvr2_dma_init(void)
+{
+	setup_irq(HW_EVENT_PVR2_DMA, &pvr2_dma_irq);
+	request_dma(PVR2_CASCADE_CHAN, "pvr2 cascade");
+
+	return register_dmac(&pvr2_dma_info);
+}
+
+static void __exit pvr2_dma_exit(void)
+{
+	free_dma(PVR2_CASCADE_CHAN);
+	free_irq(HW_EVENT_PVR2_DMA, 0);
+	unregister_dmac(&pvr2_dma_info);
+}
+
+subsys_initcall(pvr2_dma_init);
+module_exit(pvr2_dma_exit);
+
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
+MODULE_DESCRIPTION("NEC PowerVR 2 DMA driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/sh/drivers/dma/dma-sh.c b/arch/sh/drivers/dma/dma-sh.c
new file mode 100644
index 0000000..afde2a7
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-sh.c
@@ -0,0 +1,417 @@
+/*
+ * arch/sh/drivers/dma/dma-sh.c
+ *
+ * SuperH On-chip DMAC Support
+ *
+ * Copyright (C) 2000 Takashi YOSHII
+ * Copyright (C) 2003, 2004 Paul Mundt
+ * Copyright (C) 2005 Andriy Skulysh
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <mach-dreamcast/mach/dma.h>
+#include <asm/dma.h>
+#include <asm/dma-register.h>
+#include <cpu/dma-register.h>
+#include <cpu/dma.h>
+
+/*
+ * Define the default configuration for dual address memory-memory transfer.
+ * The 0x400 value represents auto-request, external->external.
+ */
+#define RS_DUAL	(DM_INC | SM_INC | RS_AUTO | TS_INDEX2VAL(XMIT_SZ_32BIT))
+
+static unsigned long dma_find_base(unsigned int chan)
+{
+	unsigned long base = SH_DMAC_BASE0;
+
+#ifdef SH_DMAC_BASE1
+	if (chan >= 6)
+		base = SH_DMAC_BASE1;
+#endif
+
+	return base;
+}
+
+static unsigned long dma_base_addr(unsigned int chan)
+{
+	unsigned long base = dma_find_base(chan);
+
+	/* Normalize offset calculation */
+	if (chan >= 9)
+		chan -= 6;
+	if (chan >= 4)
+		base += 0x10;
+
+	return base + (chan * 0x10);
+}
+
+#ifdef CONFIG_SH_DMA_IRQ_MULTI
+static inline unsigned int get_dmte_irq(unsigned int chan)
+{
+	return chan >= 6 ? DMTE6_IRQ : DMTE0_IRQ;
+}
+#else
+
+static unsigned int dmte_irq_map[] = {
+	DMTE0_IRQ, DMTE0_IRQ + 1, DMTE0_IRQ + 2, DMTE0_IRQ + 3,
+
+#ifdef DMTE4_IRQ
+	DMTE4_IRQ, DMTE4_IRQ + 1,
+#endif
+
+#ifdef DMTE6_IRQ
+	DMTE6_IRQ, DMTE6_IRQ + 1,
+#endif
+
+#ifdef DMTE8_IRQ
+	DMTE8_IRQ, DMTE9_IRQ, DMTE10_IRQ, DMTE11_IRQ,
+#endif
+};
+
+static inline unsigned int get_dmte_irq(unsigned int chan)
+{
+	return dmte_irq_map[chan];
+}
+#endif
+
+/*
+ * We determine the correct shift size based off of the CHCR transmit size
+ * for the given channel. Since we know that it will take:
+ *
+ *	info->count >> ts_shift[transmit_size]
+ *
+ * iterations to complete the transfer.
+ */
+static unsigned int ts_shift[] = TS_SHIFT;
+
+static inline unsigned int calc_xmit_shift(struct dma_channel *chan)
+{
+	u32 chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
+	int cnt = ((chcr & CHCR_TS_LOW_MASK) >> CHCR_TS_LOW_SHIFT) |
+		((chcr & CHCR_TS_HIGH_MASK) >> CHCR_TS_HIGH_SHIFT);
+
+	return ts_shift[cnt];
+}
+
+/*
+ * The transfer end interrupt must read the chcr register to end the
+ * hardware interrupt active condition.
+ * Besides that it needs to waken any waiting process, which should handle
+ * setting up the next transfer.
+ */
+static irqreturn_t dma_tei(int irq, void *dev_id)
+{
+	struct dma_channel *chan = dev_id;
+	u32 chcr;
+
+	chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
+
+	if (!(chcr & CHCR_TE))
+		return IRQ_NONE;
+
+	chcr &= ~(CHCR_IE | CHCR_DE);
+	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
+
+	wake_up(&chan->wait_queue);
+
+	return IRQ_HANDLED;
+}
+
+static int sh_dmac_request_dma(struct dma_channel *chan)
+{
+	if (unlikely(!(chan->flags & DMA_TEI_CAPABLE)))
+		return 0;
+
+	return request_irq(get_dmte_irq(chan->chan), dma_tei, IRQF_SHARED,
+			   chan->dev_id, chan);
+}
+
+static void sh_dmac_free_dma(struct dma_channel *chan)
+{
+	free_irq(get_dmte_irq(chan->chan), chan);
+}
+
+static int
+sh_dmac_configure_channel(struct dma_channel *chan, unsigned long chcr)
+{
+	if (!chcr)
+		chcr = RS_DUAL | CHCR_IE;
+
+	if (chcr & CHCR_IE) {
+		chcr &= ~CHCR_IE;
+		chan->flags |= DMA_TEI_CAPABLE;
+	} else {
+		chan->flags &= ~DMA_TEI_CAPABLE;
+	}
+
+	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
+
+	chan->flags |= DMA_CONFIGURED;
+	return 0;
+}
+
+static void sh_dmac_enable_dma(struct dma_channel *chan)
+{
+	int irq;
+	u32 chcr;
+
+	chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
+	chcr |= CHCR_DE;
+
+	if (chan->flags & DMA_TEI_CAPABLE)
+		chcr |= CHCR_IE;
+
+	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
+
+	if (chan->flags & DMA_TEI_CAPABLE) {
+		irq = get_dmte_irq(chan->chan);
+		enable_irq(irq);
+	}
+}
+
+static void sh_dmac_disable_dma(struct dma_channel *chan)
+{
+	int irq;
+	u32 chcr;
+
+	if (chan->flags & DMA_TEI_CAPABLE) {
+		irq = get_dmte_irq(chan->chan);
+		disable_irq(irq);
+	}
+
+	chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
+	chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE);
+	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
+}
+
+static int sh_dmac_xfer_dma(struct dma_channel *chan)
+{
+	/*
+	 * If we haven't pre-configured the channel with special flags, use
+	 * the defaults.
+	 */
+	if (unlikely(!(chan->flags & DMA_CONFIGURED)))
+		sh_dmac_configure_channel(chan, 0);
+
+	sh_dmac_disable_dma(chan);
+
+	/*
+	 * Single-address mode usage note!
+	 *
+	 * It's important that we don't accidentally write any value to SAR/DAR
+	 * (this includes 0) that hasn't been directly specified by the user if
+	 * we're in single-address mode.
+	 *
+	 * In this case, only one address can be defined, anything else will
+	 * result in a DMA address error interrupt (at least on the SH-4),
+	 * which will subsequently halt the transfer.
+	 *
+	 * Channel 2 on the Dreamcast is a special case, as this is used for
+	 * cascading to the PVR2 DMAC. In this case, we still need to write
+	 * SAR and DAR, regardless of value, in order for cascading to work.
+	 */
+	if (chan->sar || (mach_is_dreamcast() &&
+			  chan->chan == PVR2_CASCADE_CHAN))
+		__raw_writel(chan->sar, (dma_base_addr(chan->chan) + SAR));
+	if (chan->dar || (mach_is_dreamcast() &&
+			  chan->chan == PVR2_CASCADE_CHAN))
+		__raw_writel(chan->dar, (dma_base_addr(chan->chan) + DAR));
+
+	__raw_writel(chan->count >> calc_xmit_shift(chan),
+		(dma_base_addr(chan->chan) + TCR));
+
+	sh_dmac_enable_dma(chan);
+
+	return 0;
+}
+
+static int sh_dmac_get_dma_residue(struct dma_channel *chan)
+{
+	if (!(__raw_readl(dma_base_addr(chan->chan) + CHCR) & CHCR_DE))
+		return 0;
+
+	return __raw_readl(dma_base_addr(chan->chan) + TCR)
+		 << calc_xmit_shift(chan);
+}
+
+/*
+ * DMAOR handling
+ */
+#if defined(CONFIG_CPU_SUBTYPE_SH7723)	|| \
+    defined(CONFIG_CPU_SUBTYPE_SH7724)	|| \
+    defined(CONFIG_CPU_SUBTYPE_SH7780)	|| \
+    defined(CONFIG_CPU_SUBTYPE_SH7785)
+#define NR_DMAOR	2
+#else
+#define NR_DMAOR	1
+#endif
+
+/*
+ * DMAOR bases are broken out amongst channel groups. DMAOR0 manages
+ * channels 0 - 5, DMAOR1 6 - 11 (optional).
+ */
+#define dmaor_read_reg(n)		__raw_readw(dma_find_base((n)*6))
+#define dmaor_write_reg(n, data)	__raw_writew(data, dma_find_base(n)*6)
+
+static inline int dmaor_reset(int no)
+{
+	unsigned long dmaor = dmaor_read_reg(no);
+
+	/* Try to clear the error flags first, incase they are set */
+	dmaor &= ~(DMAOR_NMIF | DMAOR_AE);
+	dmaor_write_reg(no, dmaor);
+
+	dmaor |= DMAOR_INIT;
+	dmaor_write_reg(no, dmaor);
+
+	/* See if we got an error again */
+	if ((dmaor_read_reg(no) & (DMAOR_AE | DMAOR_NMIF))) {
+		printk(KERN_ERR "dma-sh: Can't initialize DMAOR.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * DMAE handling
+ */
+#ifdef CONFIG_CPU_SH4
+
+#if defined(DMAE1_IRQ)
+#define NR_DMAE		2
+#else
+#define NR_DMAE		1
+#endif
+
+static const char *dmae_name[] = {
+	"DMAC Address Error0",
+	"DMAC Address Error1"
+};
+
+#ifdef CONFIG_SH_DMA_IRQ_MULTI
+static inline unsigned int get_dma_error_irq(int n)
+{
+	return get_dmte_irq(n * 6);
+}
+#else
+
+static unsigned int dmae_irq_map[] = {
+	DMAE0_IRQ,
+
+#ifdef DMAE1_IRQ
+	DMAE1_IRQ,
+#endif
+};
+
+static inline unsigned int get_dma_error_irq(int n)
+{
+	return dmae_irq_map[n];
+}
+#endif
+
+static irqreturn_t dma_err(int irq, void *dummy)
+{
+	int i;
+
+	for (i = 0; i < NR_DMAOR; i++)
+		dmaor_reset(i);
+
+	disable_irq(irq);
+
+	return IRQ_HANDLED;
+}
+
+static int dmae_irq_init(void)
+{
+	int n;
+
+	for (n = 0; n < NR_DMAE; n++) {
+		int i = request_irq(get_dma_error_irq(n), dma_err,
+				    IRQF_SHARED, dmae_name[n], (void *)dmae_name[n]);
+		if (unlikely(i < 0)) {
+			printk(KERN_ERR "%s request_irq fail\n", dmae_name[n]);
+			return i;
+		}
+	}
+
+	return 0;
+}
+
+static void dmae_irq_free(void)
+{
+	int n;
+
+	for (n = 0; n < NR_DMAE; n++)
+		free_irq(get_dma_error_irq(n), NULL);
+}
+#else
+static inline int dmae_irq_init(void)
+{
+	return 0;
+}
+
+static void dmae_irq_free(void)
+{
+}
+#endif
+
+static struct dma_ops sh_dmac_ops = {
+	.request	= sh_dmac_request_dma,
+	.free		= sh_dmac_free_dma,
+	.get_residue	= sh_dmac_get_dma_residue,
+	.xfer		= sh_dmac_xfer_dma,
+	.configure	= sh_dmac_configure_channel,
+};
+
+static struct dma_info sh_dmac_info = {
+	.name		= "sh_dmac",
+	.nr_channels	= CONFIG_NR_ONCHIP_DMA_CHANNELS,
+	.ops		= &sh_dmac_ops,
+	.flags		= DMAC_CHANNELS_TEI_CAPABLE,
+};
+
+static int __init sh_dmac_init(void)
+{
+	struct dma_info *info = &sh_dmac_info;
+	int i, rc;
+
+	/*
+	 * Initialize DMAE, for parts that support it.
+	 */
+	rc = dmae_irq_init();
+	if (unlikely(rc != 0))
+		return rc;
+
+	/*
+	 * Initialize DMAOR, and clean up any error flags that may have
+	 * been set.
+	 */
+	for (i = 0; i < NR_DMAOR; i++) {
+		rc = dmaor_reset(i);
+		if (unlikely(rc != 0))
+			return rc;
+	}
+
+	return register_dmac(info);
+}
+
+static void __exit sh_dmac_exit(void)
+{
+	dmae_irq_free();
+	unregister_dmac(&sh_dmac_info);
+}
+
+subsys_initcall(sh_dmac_init);
+module_exit(sh_dmac_exit);
+
+MODULE_AUTHOR("Takashi YOSHII, Paul Mundt, Andriy Skulysh");
+MODULE_DESCRIPTION("SuperH On-Chip DMAC Support");
+MODULE_LICENSE("GPL");
diff --git a/arch/sh/drivers/dma/dma-sysfs.c b/arch/sh/drivers/dma/dma-sysfs.c
new file mode 100644
index 0000000..4b15fed
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-sysfs.c
@@ -0,0 +1,167 @@
+/*
+ * arch/sh/drivers/dma/dma-sysfs.c
+ *
+ * sysfs interface for SH DMA API
+ *
+ * Copyright (C) 2004 - 2006  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <asm/dma.h>
+
+static struct bus_type dma_subsys = {
+	.name = "dma",
+	.dev_name = "dma",
+};
+
+static ssize_t dma_show_devices(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	ssize_t len = 0;
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		struct dma_info *info = get_dma_info(i);
+		struct dma_channel *channel = get_dma_channel(i);
+
+		if (unlikely(!info) || !channel)
+			continue;
+
+		len += sprintf(buf + len, "%2d: %14s    %s\n",
+			       channel->chan, info->name,
+			       channel->dev_id);
+	}
+
+	return len;
+}
+
+static DEVICE_ATTR(devices, S_IRUGO, dma_show_devices, NULL);
+
+static int __init dma_subsys_init(void)
+{
+	int ret;
+
+	ret = subsys_system_register(&dma_subsys, NULL);
+	if (unlikely(ret))
+		return ret;
+
+	return device_create_file(dma_subsys.dev_root, &dev_attr_devices);
+}
+postcore_initcall(dma_subsys_init);
+
+static ssize_t dma_show_dev_id(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct dma_channel *channel = to_dma_channel(dev);
+	return sprintf(buf, "%s\n", channel->dev_id);
+}
+
+static ssize_t dma_store_dev_id(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct dma_channel *channel = to_dma_channel(dev);
+	strcpy(channel->dev_id, buf);
+	return count;
+}
+
+static DEVICE_ATTR(dev_id, S_IRUGO | S_IWUSR, dma_show_dev_id, dma_store_dev_id);
+
+static ssize_t dma_store_config(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct dma_channel *channel = to_dma_channel(dev);
+	unsigned long config;
+
+	config = simple_strtoul(buf, NULL, 0);
+	dma_configure_channel(channel->vchan, config);
+
+	return count;
+}
+
+static DEVICE_ATTR(config, S_IWUSR, NULL, dma_store_config);
+
+static ssize_t dma_show_mode(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct dma_channel *channel = to_dma_channel(dev);
+	return sprintf(buf, "0x%08x\n", channel->mode);
+}
+
+static ssize_t dma_store_mode(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct dma_channel *channel = to_dma_channel(dev);
+	channel->mode = simple_strtoul(buf, NULL, 0);
+	return count;
+}
+
+static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, dma_show_mode, dma_store_mode);
+
+#define dma_ro_attr(field, fmt)						\
+static ssize_t dma_show_##field(struct device *dev,		\
+				struct device_attribute *attr, char *buf)\
+{									\
+	struct dma_channel *channel = to_dma_channel(dev);		\
+	return sprintf(buf, fmt, channel->field);			\
+}									\
+static DEVICE_ATTR(field, S_IRUGO, dma_show_##field, NULL);
+
+dma_ro_attr(count, "0x%08x\n");
+dma_ro_attr(flags, "0x%08lx\n");
+
+int dma_create_sysfs_files(struct dma_channel *chan, struct dma_info *info)
+{
+	struct device *dev = &chan->dev;
+	char name[16];
+	int ret;
+
+	dev->id  = chan->vchan;
+	dev->bus = &dma_subsys;
+
+	ret = device_register(dev);
+	if (ret)
+		return ret;
+
+	ret |= device_create_file(dev, &dev_attr_dev_id);
+	ret |= device_create_file(dev, &dev_attr_count);
+	ret |= device_create_file(dev, &dev_attr_mode);
+	ret |= device_create_file(dev, &dev_attr_flags);
+	ret |= device_create_file(dev, &dev_attr_config);
+
+	if (unlikely(ret)) {
+		dev_err(&info->pdev->dev, "Failed creating attrs\n");
+		return ret;
+	}
+
+	snprintf(name, sizeof(name), "dma%d", chan->chan);
+	return sysfs_create_link(&info->pdev->dev.kobj, &dev->kobj, name);
+}
+
+void dma_remove_sysfs_files(struct dma_channel *chan, struct dma_info *info)
+{
+	struct device *dev = &chan->dev;
+	char name[16];
+
+	device_remove_file(dev, &dev_attr_dev_id);
+	device_remove_file(dev, &dev_attr_count);
+	device_remove_file(dev, &dev_attr_mode);
+	device_remove_file(dev, &dev_attr_flags);
+	device_remove_file(dev, &dev_attr_config);
+
+	snprintf(name, sizeof(name), "dma%d", chan->chan);
+	sysfs_remove_link(&info->pdev->dev.kobj, name);
+
+	device_unregister(dev);
+}
diff --git a/arch/sh/drivers/dma/dmabrg.c b/arch/sh/drivers/dma/dmabrg.c
new file mode 100644
index 0000000..e5a57a1
--- /dev/null
+++ b/arch/sh/drivers/dma/dmabrg.c
@@ -0,0 +1,197 @@
+/*
+ * SH7760 DMABRG IRQ handling
+ *
+ * (c) 2007 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau@msc-ge.com>
+ *  licensed under the GPLv2.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <asm/dma.h>
+#include <asm/dmabrg.h>
+#include <asm/io.h>
+
+/*
+ * The DMABRG is a special DMA unit within the SH7760. It does transfers
+ * from USB-SRAM/Audio units to main memory (and also the LCDC; but that
+ * part is sensibly placed  in the LCDC  registers and requires no irqs)
+ * It has 3 IRQ lines which trigger 10 events, and works independently
+ * from the traditional SH DMAC (although it blocks usage of DMAC 0)
+ *
+ * BRGIRQID   | component | dir | meaning      | source
+ * -----------------------------------------------------
+ *     0      | USB-DMA   | ... | xfer done    | DMABRGI1
+ *     1      | USB-UAE   | ... | USB addr err.| DMABRGI0
+ *     2      | HAC0/SSI0 | play| all done     | DMABRGI1
+ *     3      | HAC0/SSI0 | play| half done    | DMABRGI2
+ *     4      | HAC0/SSI0 | rec | all done     | DMABRGI1
+ *     5      | HAC0/SSI0 | rec | half done    | DMABRGI2
+ *     6      | HAC1/SSI1 | play| all done     | DMABRGI1
+ *     7      | HAC1/SSI1 | play| half done    | DMABRGI2
+ *     8      | HAC1/SSI1 | rec | all done     | DMABRGI1
+ *     9      | HAC1/SSI1 | rec | half done    | DMABRGI2
+ *
+ * all can be enabled/disabled in the DMABRGCR register,
+ * as well as checked if they occurred.
+ *
+ * DMABRGI0 services  USB  DMA  Address  errors,  but it still must be
+ * enabled/acked in the DMABRGCR register.  USB-DMA complete indicator
+ * is grouped together with the audio buffer end indicators, too bad...
+ *
+ * DMABRGCR:	Bits 31-24: audio-dma ENABLE flags,
+ *		Bits 23-16: audio-dma STATUS flags,
+ *		Bits  9-8:  USB error/xfer ENABLE,
+ *		Bits  1-0:  USB error/xfer STATUS.
+ *	Ack an IRQ by writing 0 to the STATUS flag.
+ *	Mask IRQ by writing 0 to ENABLE flag.
+ *
+ * Usage is almost like with any other IRQ:
+ *  dmabrg_request_irq(BRGIRQID, handler, data)
+ *  dmabrg_free_irq(BRGIRQID)
+ *
+ * handler prototype:  void brgirqhandler(void *data)
+ */
+
+#define DMARSRA		0xfe090000
+#define DMAOR		0xffa00040
+#define DMACHCR0	0xffa0000c
+#define DMABRGCR	0xfe3c0000
+
+#define DMAOR_BRG	0x0000c000
+#define DMAOR_DMEN	0x00000001
+
+#define DMABRGI0	68
+#define DMABRGI1	69
+#define DMABRGI2	70
+
+struct dmabrg_handler {
+	void (*handler)(void *);
+	void *data;
+} *dmabrg_handlers;
+
+static inline void dmabrg_call_handler(int i)
+{
+	dmabrg_handlers[i].handler(dmabrg_handlers[i].data);
+}
+
+/*
+ * main DMABRG irq handler. It acks irqs and then
+ * handles every set and unmasked bit sequentially.
+ * No locking and no validity checks; it should be
+ * as fast as possible (audio!)
+ */
+static irqreturn_t dmabrg_irq(int irq, void *data)
+{
+	unsigned long dcr;
+	unsigned int i;
+
+	dcr = __raw_readl(DMABRGCR);
+	__raw_writel(dcr & ~0x00ff0003, DMABRGCR);	/* ack all */
+	dcr &= dcr >> 8;	/* ignore masked */
+
+	/* USB stuff, get it out of the way first */
+	if (dcr & 1)
+		dmabrg_call_handler(DMABRGIRQ_USBDMA);
+	if (dcr & 2)
+		dmabrg_call_handler(DMABRGIRQ_USBDMAERR);
+
+	/* Audio */
+	dcr >>= 16;
+	while (dcr) {
+		i = __ffs(dcr);
+		dcr &= dcr - 1;
+		dmabrg_call_handler(i + DMABRGIRQ_A0TXF);
+	}
+	return IRQ_HANDLED;
+}
+
+static void dmabrg_disable_irq(unsigned int dmairq)
+{
+	unsigned long dcr;
+	dcr = __raw_readl(DMABRGCR);
+	dcr &= ~(1 << ((dmairq > 1) ? dmairq + 22 : dmairq + 8));
+	__raw_writel(dcr, DMABRGCR);
+}
+
+static void dmabrg_enable_irq(unsigned int dmairq)
+{
+	unsigned long dcr;
+	dcr = __raw_readl(DMABRGCR);
+	dcr |= (1 << ((dmairq > 1) ? dmairq + 22 : dmairq + 8));
+	__raw_writel(dcr, DMABRGCR);
+}
+
+int dmabrg_request_irq(unsigned int dmairq, void(*handler)(void*),
+		       void *data)
+{
+	if ((dmairq > 9) || !handler)
+		return -ENOENT;
+	if (dmabrg_handlers[dmairq].handler)
+		return -EBUSY;
+
+	dmabrg_handlers[dmairq].handler = handler;
+	dmabrg_handlers[dmairq].data = data;
+	
+	dmabrg_enable_irq(dmairq);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dmabrg_request_irq);
+
+void dmabrg_free_irq(unsigned int dmairq)
+{
+	if (likely(dmairq < 10)) {
+		dmabrg_disable_irq(dmairq);
+		dmabrg_handlers[dmairq].handler = NULL;
+		dmabrg_handlers[dmairq].data = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(dmabrg_free_irq);
+
+static int __init dmabrg_init(void)
+{
+	unsigned long or;
+	int ret;
+
+	dmabrg_handlers = kcalloc(10, sizeof(struct dmabrg_handler),
+				  GFP_KERNEL);
+	if (!dmabrg_handlers)
+		return -ENOMEM;
+
+#ifdef CONFIG_SH_DMA
+	/* request DMAC channel 0 before anyone else can get it */
+	ret = request_dma(0, "DMAC 0 (DMABRG)");
+	if (ret < 0)
+		printk(KERN_INFO "DMABRG: DMAC ch0 not reserved!\n");
+#endif
+
+	__raw_writel(0, DMABRGCR);
+	__raw_writel(0, DMACHCR0);
+	__raw_writel(0x94000000, DMARSRA);	/* enable DMABRG in DMAC 0 */
+
+	/* enable DMABRG mode, enable the DMAC */
+	or = __raw_readl(DMAOR);
+	__raw_writel(or | DMAOR_BRG | DMAOR_DMEN, DMAOR);
+
+	ret = request_irq(DMABRGI0, dmabrg_irq, 0,
+			"DMABRG USB address error", NULL);
+	if (ret)
+		goto out0;
+
+	ret = request_irq(DMABRGI1, dmabrg_irq, 0,
+			"DMABRG Transfer End", NULL);
+	if (ret)
+		goto out1;
+
+	ret = request_irq(DMABRGI2, dmabrg_irq, 0,
+			"DMABRG Transfer Half", NULL);
+	if (ret == 0)
+		return ret;
+
+	free_irq(DMABRGI1, NULL);
+out1:	free_irq(DMABRGI0, NULL);
+out0:	kfree(dmabrg_handlers);
+	return ret;
+}
+subsys_initcall(dmabrg_init);