sh: dma-sh updates for multi IRQ and new SH-4A CPUs.

This adds DMA support for newer SH-4A CPUs, particularly SH7763/64/80/85.

This also enables multi IRQ support for platforms that have multiple
vectors bound to the same IRQ source.

Signed-off-by: Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
Signed-off-by: Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
diff --git a/arch/sh/drivers/dma/dma-sh.c b/arch/sh/drivers/dma/dma-sh.c
index 50887a5..ab7b18d 100644
--- a/arch/sh/drivers/dma/dma-sh.c
+++ b/arch/sh/drivers/dma/dma-sh.c
@@ -17,28 +17,23 @@
 #include <mach-dreamcast/mach/dma.h>
 #include <asm/dma.h>
 #include <asm/io.h>
-#include "dma-sh.h"
+#include <asm/dma-sh.h>
 
-static int dmte_irq_map[] = {
-	DMTE0_IRQ,
-	DMTE1_IRQ,
-	DMTE2_IRQ,
-	DMTE3_IRQ,
-#if defined(CONFIG_CPU_SUBTYPE_SH7720)  ||	\
-    defined(CONFIG_CPU_SUBTYPE_SH7721)  ||	\
-    defined(CONFIG_CPU_SUBTYPE_SH7751R) ||	\
-    defined(CONFIG_CPU_SUBTYPE_SH7760)  ||	\
-    defined(CONFIG_CPU_SUBTYPE_SH7709)  ||	\
-    defined(CONFIG_CPU_SUBTYPE_SH7780)
-	DMTE4_IRQ,
-	DMTE5_IRQ,
+#if defined(CONFIG_CPU_SUBTYPE_SH7763)	|| \
+		defined(CONFIG_CPU_SUBTYPE_SH7764)	|| \
+		defined(CONFIG_CPU_SUBTYPE_SH7780)	|| \
+		defined(CONFIG_CPU_SUBTYPE_SH7785)
+#define DMAC_IRQ_MULTI	1
 #endif
-#if defined(CONFIG_CPU_SUBTYPE_SH7751R) ||	\
-    defined(CONFIG_CPU_SUBTYPE_SH7760)  ||	\
-    defined(CONFIG_CPU_SUBTYPE_SH7780)
-	DMTE6_IRQ,
-	DMTE7_IRQ,
+
+#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"
 };
 
 static inline unsigned int get_dmte_irq(unsigned int chan)
@@ -46,7 +41,14 @@
 	unsigned int irq = 0;
 	if (chan < ARRAY_SIZE(dmte_irq_map))
 		irq = dmte_irq_map[chan];
+
+#if defined(DMAC_IRQ_MULTI)
+	if (irq > DMTE6_IRQ)
+		return DMTE6_IRQ;
+	return DMTE0_IRQ;
+#else
 	return irq;
+#endif
 }
 
 /*
@@ -59,7 +61,7 @@
  */
 static inline unsigned int calc_xmit_shift(struct dma_channel *chan)
 {
-	u32 chcr = ctrl_inl(CHCR[chan->chan]);
+	u32 chcr = ctrl_inl(dma_base_addr[chan->chan] + CHCR);
 
 	return ts_shift[(chcr & CHCR_TS_MASK)>>CHCR_TS_SHIFT];
 }
@@ -75,13 +77,13 @@
 	struct dma_channel *chan = dev_id;
 	u32 chcr;
 
-	chcr = ctrl_inl(CHCR[chan->chan]);
+	chcr = ctrl_inl(dma_base_addr[chan->chan] + CHCR);
 
 	if (!(chcr & CHCR_TE))
 		return IRQ_NONE;
 
 	chcr &= ~(CHCR_IE | CHCR_DE);
-	ctrl_outl(chcr, CHCR[chan->chan]);
+	ctrl_outl(chcr, (dma_base_addr[chan->chan] + CHCR));
 
 	wake_up(&chan->wait_queue);
 
@@ -94,7 +96,12 @@
 		return 0;
 
 	return request_irq(get_dmte_irq(chan->chan), dma_tei,
-			   IRQF_DISABLED, chan->dev_id, chan);
+#if defined(DMAC_IRQ_MULTI)
+				IRQF_SHARED,
+#else
+				IRQF_DISABLED,
+#endif
+				chan->dev_id, chan);
 }
 
 static void sh_dmac_free_dma(struct dma_channel *chan)
@@ -115,7 +122,7 @@
 		chan->flags &= ~DMA_TEI_CAPABLE;
 	}
 
-	ctrl_outl(chcr, CHCR[chan->chan]);
+	ctrl_outl(chcr, (dma_base_addr[chan->chan] + CHCR));
 
 	chan->flags |= DMA_CONFIGURED;
 	return 0;
@@ -126,13 +133,13 @@
 	int irq;
 	u32 chcr;
 
-	chcr = ctrl_inl(CHCR[chan->chan]);
+	chcr = ctrl_inl(dma_base_addr[chan->chan] + CHCR);
 	chcr |= CHCR_DE;
 
 	if (chan->flags & DMA_TEI_CAPABLE)
 		chcr |= CHCR_IE;
 
-	ctrl_outl(chcr, CHCR[chan->chan]);
+	ctrl_outl(chcr, (dma_base_addr[chan->chan] + CHCR));
 
 	if (chan->flags & DMA_TEI_CAPABLE) {
 		irq = get_dmte_irq(chan->chan);
@@ -150,9 +157,9 @@
 		disable_irq(irq);
 	}
 
-	chcr = ctrl_inl(CHCR[chan->chan]);
+	chcr = ctrl_inl(dma_base_addr[chan->chan] + CHCR);
 	chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE);
-	ctrl_outl(chcr, CHCR[chan->chan]);
+	ctrl_outl(chcr, (dma_base_addr[chan->chan] + CHCR));
 }
 
 static int sh_dmac_xfer_dma(struct dma_channel *chan)
@@ -183,12 +190,13 @@
 	 */
 	if (chan->sar || (mach_is_dreamcast() &&
 			  chan->chan == PVR2_CASCADE_CHAN))
-		ctrl_outl(chan->sar, SAR[chan->chan]);
+		ctrl_outl(chan->sar, (dma_base_addr[chan->chan]+SAR));
 	if (chan->dar || (mach_is_dreamcast() &&
 			  chan->chan == PVR2_CASCADE_CHAN))
-		ctrl_outl(chan->dar, DAR[chan->chan]);
+		ctrl_outl(chan->dar, (dma_base_addr[chan->chan] + DAR));
 
-	ctrl_outl(chan->count >> calc_xmit_shift(chan), DMATCR[chan->chan]);
+	ctrl_outl(chan->count >> calc_xmit_shift(chan),
+		(dma_base_addr[chan->chan] + TCR));
 
 	sh_dmac_enable_dma(chan);
 
@@ -197,36 +205,26 @@
 
 static int sh_dmac_get_dma_residue(struct dma_channel *chan)
 {
-	if (!(ctrl_inl(CHCR[chan->chan]) & CHCR_DE))
+	if (!(ctrl_inl(dma_base_addr[chan->chan] + CHCR) & CHCR_DE))
 		return 0;
 
-	return ctrl_inl(DMATCR[chan->chan]) << calc_xmit_shift(chan);
+	return ctrl_inl(dma_base_addr[chan->chan] + TCR)
+		 << calc_xmit_shift(chan);
 }
 
-#if defined(CONFIG_CPU_SUBTYPE_SH7720) || \
-    defined(CONFIG_CPU_SUBTYPE_SH7721) || \
-    defined(CONFIG_CPU_SUBTYPE_SH7780) || \
-    defined(CONFIG_CPU_SUBTYPE_SH7709)
-#define dmaor_read_reg()	ctrl_inw(DMAOR)
-#define dmaor_write_reg(data)	ctrl_outw(data, DMAOR)
-#else
-#define dmaor_read_reg()	ctrl_inl(DMAOR)
-#define dmaor_write_reg(data)	ctrl_outl(data, DMAOR)
-#endif
-
-static inline int dmaor_reset(void)
+static inline int dmaor_reset(int no)
 {
-	unsigned long dmaor = dmaor_read_reg();
+	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(dmaor);
+	dmaor_write_reg(no, dmaor);
 
 	dmaor |= DMAOR_INIT;
-	dmaor_write_reg(dmaor);
+	dmaor_write_reg(no, dmaor);
 
 	/* See if we got an error again */
-	if ((dmaor_read_reg() & (DMAOR_AE | DMAOR_NMIF))) {
+	if ((dmaor_read_reg(no) & (DMAOR_AE | DMAOR_NMIF))) {
 		printk(KERN_ERR "dma-sh: Can't initialize DMAOR.\n");
 		return -EINVAL;
 	}
@@ -237,10 +235,33 @@
 #if defined(CONFIG_CPU_SH4)
 static irqreturn_t dma_err(int irq, void *dummy)
 {
-	dmaor_reset();
+#if defined(DMAC_IRQ_MULTI)
+	int cnt = 0;
+	switch (irq) {
+#if defined(DMTE6_IRQ) && defined(DMAE1_IRQ)
+	case DMTE6_IRQ:
+		cnt++;
+#endif
+	case DMTE0_IRQ:
+		if (dmaor_read_reg(cnt) & (DMAOR_NMIF | DMAOR_AE)) {
+			disable_irq(irq);
+			/* DMA multi and error IRQ */
+			return IRQ_HANDLED;
+		}
+	default:
+		return IRQ_NONE;
+	}
+#else
+	dmaor_reset(0);
+#if defined(CONFIG_CPU_SUBTYPE_SH7723)	|| \
+		defined(CONFIG_CPU_SUBTYPE_SH7780)	|| \
+		defined(CONFIG_CPU_SUBTYPE_SH7785)
+	dmaor_reset(1);
+#endif
 	disable_irq(irq);
 
 	return IRQ_HANDLED;
+#endif
 }
 #endif
 
@@ -259,24 +280,57 @@
 	.flags		= DMAC_CHANNELS_TEI_CAPABLE,
 };
 
+static unsigned int get_dma_error_irq(int n)
+{
+#if defined(DMAC_IRQ_MULTI)
+	return (n == 0) ? get_dmte_irq(0) : get_dmte_irq(6);
+#else
+	return (n == 0) ? DMAE0_IRQ :
+#if defined(DMAE1_IRQ)
+				DMAE1_IRQ;
+#else
+				-1;
+#endif
+#endif
+}
+
 static int __init sh_dmac_init(void)
 {
 	struct dma_info *info = &sh_dmac_info;
 	int i;
 
 #ifdef CONFIG_CPU_SH4
-	i = request_irq(DMAE_IRQ, dma_err, IRQF_DISABLED, "DMAC Address Error", 0);
-	if (unlikely(i < 0))
-		return i;
+	int n;
+
+	for (n = 0; n < NR_DMAE; n++) {
+		i = request_irq(get_dma_error_irq(n), dma_err,
+#if defined(DMAC_IRQ_MULTI)
+				IRQF_SHARED,
+#else
+				IRQF_DISABLED,
 #endif
+				dmae_name[n], (void *)dmae_name[n]);
+		if (unlikely(i < 0)) {
+			printk(KERN_ERR "%s request_irq fail\n", dmae_name[n]);
+			return i;
+		}
+	}
+#endif /* CONFIG_CPU_SH4 */
 
 	/*
 	 * Initialize DMAOR, and clean up any error flags that may have
 	 * been set.
 	 */
-	i = dmaor_reset();
+	i = dmaor_reset(0);
 	if (unlikely(i != 0))
 		return i;
+#if defined(CONFIG_CPU_SUBTYPE_SH7723)	|| \
+		defined(CONFIG_CPU_SUBTYPE_SH7780)	|| \
+		defined(CONFIG_CPU_SUBTYPE_SH7785)
+	i = dmaor_reset(1);
+	if (unlikely(i != 0))
+		return i;
+#endif
 
 	return register_dmac(info);
 }
@@ -284,8 +338,12 @@
 static void __exit sh_dmac_exit(void)
 {
 #ifdef CONFIG_CPU_SH4
-	free_irq(DMAE_IRQ, 0);
-#endif
+	int n;
+
+	for (n = 0; n < NR_DMAE; n++) {
+		free_irq(get_dma_error_irq(n), (void *)dmae_name[n]);
+	}
+#endif /* CONFIG_CPU_SH4 */
 	unregister_dmac(&sh_dmac_info);
 }