[POWERPC] cpm2: CPM2 interrupt controller fix

This contains important fixes for the CPM2 PIC code. Eliminated
CPM_IRQ_OFFSET, pulling the respective interrupt numbers from the interrupt
mapping. Updated devicetree files to reflect that. Changed direct
IC-related IO accesses to the IO accessors. Fixed all the sense values to
keep coherency with ipic. In the current code, CPM2 stuff will have no IRQs
and hence could be hardly usable.

Signed-off-by: Vitaly Bordug <vbordug@ru.mvista.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
diff --git a/arch/powerpc/sysdev/cpm2_pic.c b/arch/powerpc/sysdev/cpm2_pic.c
index e2739eb..eabfe06 100644
--- a/arch/powerpc/sysdev/cpm2_pic.c
+++ b/arch/powerpc/sysdev/cpm2_pic.c
@@ -36,9 +36,20 @@
 #include <asm/mpc8260.h>
 #include <asm/io.h>
 #include <asm/prom.h>
+#include <asm/fs_pd.h>
 
 #include "cpm2_pic.h"
 
+/* External IRQS */
+#define CPM2_IRQ_EXT1		19
+#define CPM2_IRQ_EXT7		25
+
+/* Port C IRQS */
+#define CPM2_IRQ_PORTC15	48
+#define CPM2_IRQ_PORTC0		63
+
+static intctl_cpm2_t *cpm2_intctl;
+
 static struct device_node *cpm2_pic_node;
 static struct irq_host *cpm2_pic_host;
 #define NR_MASK_WORDS   ((NR_IRQS + 31) / 32)
@@ -68,68 +79,55 @@
 	24, 25, 26, 27, 28, 29, 30, 31,
 };
 
-static void cpm2_mask_irq(unsigned int irq_nr)
+static void cpm2_mask_irq(unsigned int virq)
 {
 	int	bit, word;
-	volatile uint	*simr;
-
-	irq_nr -= CPM_IRQ_OFFSET;
+	unsigned int irq_nr = virq_to_hw(virq);
 
 	bit = irq_to_siubit[irq_nr];
 	word = irq_to_siureg[irq_nr];
 
-	simr = &(cpm2_intctl->ic_simrh);
 	ppc_cached_irq_mask[word] &= ~(1 << bit);
-	simr[word] = ppc_cached_irq_mask[word];
+	out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
 }
 
-static void cpm2_unmask_irq(unsigned int irq_nr)
+static void cpm2_unmask_irq(unsigned int virq)
 {
 	int	bit, word;
-	volatile uint	*simr;
-
-	irq_nr -= CPM_IRQ_OFFSET;
+	unsigned int irq_nr = virq_to_hw(virq);
 
 	bit = irq_to_siubit[irq_nr];
 	word = irq_to_siureg[irq_nr];
 
-	simr = &(cpm2_intctl->ic_simrh);
 	ppc_cached_irq_mask[word] |= 1 << bit;
-	simr[word] = ppc_cached_irq_mask[word];
+	out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
 }
 
-static void cpm2_mask_and_ack(unsigned int irq_nr)
+static void cpm2_ack(unsigned int virq)
 {
 	int	bit, word;
-	volatile uint	*simr, *sipnr;
-
-	irq_nr -= CPM_IRQ_OFFSET;
+	unsigned int irq_nr = virq_to_hw(virq);
 
 	bit = irq_to_siubit[irq_nr];
 	word = irq_to_siureg[irq_nr];
 
-	simr = &(cpm2_intctl->ic_simrh);
-	sipnr = &(cpm2_intctl->ic_sipnrh);
-	ppc_cached_irq_mask[word] &= ~(1 << bit);
-	simr[word] = ppc_cached_irq_mask[word];
-	sipnr[word] = 1 << bit;
+	out_be32(&cpm2_intctl->ic_sipnrh + word, 1 << bit);
 }
 
-static void cpm2_end_irq(unsigned int irq_nr)
+static void cpm2_end_irq(unsigned int virq)
 {
 	int	bit, word;
-	volatile uint	*simr;
+	unsigned int irq_nr = virq_to_hw(virq);
 
 	if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
 			&& irq_desc[irq_nr].action) {
 
-		irq_nr -= CPM_IRQ_OFFSET;
 		bit = irq_to_siubit[irq_nr];
 		word = irq_to_siureg[irq_nr];
 
-		simr = &(cpm2_intctl->ic_simrh);
 		ppc_cached_irq_mask[word] |= 1 << bit;
-		simr[word] = ppc_cached_irq_mask[word];
+		out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
+
 		/*
 		 * Work around large numbers of spurious IRQs on PowerPC 82xx
 		 * systems.
@@ -138,13 +136,59 @@
 	}
 }
 
+static int cpm2_set_irq_type(unsigned int virq, unsigned int flow_type)
+{
+	unsigned int src = virq_to_hw(virq);
+	struct irq_desc *desc = get_irq_desc(virq);
+	unsigned int vold, vnew, edibit;
+
+	if (flow_type == IRQ_TYPE_NONE)
+		flow_type = IRQ_TYPE_LEVEL_LOW;
+
+	if (flow_type & IRQ_TYPE_EDGE_RISING) {
+		printk(KERN_ERR "CPM2 PIC: sense type 0x%x not supported\n",
+			flow_type);
+		return -EINVAL;
+	}
+
+	desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
+	desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
+	if (flow_type & IRQ_TYPE_LEVEL_LOW)  {
+		desc->status |= IRQ_LEVEL;
+		desc->handle_irq = handle_level_irq;
+	} else
+		desc->handle_irq = handle_edge_irq;
+
+	/* internal IRQ senses are LEVEL_LOW
+	 * EXT IRQ and Port C IRQ senses are programmable
+	 */
+	if (src >= CPM2_IRQ_EXT1 && src <= CPM2_IRQ_EXT7)
+			edibit = (14 - (src - CPM2_IRQ_EXT1));
+	else
+		if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0)
+			edibit = (31 - (src - CPM2_IRQ_PORTC15));
+		else
+			return (flow_type & IRQ_TYPE_LEVEL_LOW) ? 0 : -EINVAL;
+
+	vold = in_be32(&cpm2_intctl->ic_siexr);
+
+	if ((flow_type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_FALLING)
+		vnew = vold | (1 << edibit);
+	else
+		vnew = vold & ~(1 << edibit);
+
+	if (vold != vnew)
+		out_be32(&cpm2_intctl->ic_siexr, vnew);
+	return 0;
+}
+
 static struct irq_chip cpm2_pic = {
 	.typename = " CPM2 SIU ",
-	.enable = cpm2_unmask_irq,
-	.disable = cpm2_mask_irq,
+	.mask = cpm2_mask_irq,
 	.unmask = cpm2_unmask_irq,
-	.mask_ack = cpm2_mask_and_ack,
-	.end = cpm2_end_irq,
+	.ack = cpm2_ack,
+	.eoi = cpm2_end_irq,
+	.set_type = cpm2_set_irq_type,
 };
 
 unsigned int cpm2_get_irq(void)
@@ -154,17 +198,17 @@
 
        /* For CPM2, read the SIVEC register and shift the bits down
          * to get the irq number.         */
-        bits = cpm2_intctl->ic_sivec;
+        bits = in_be32(&cpm2_intctl->ic_sivec);
         irq = bits >> 26;
 
 	if (irq == 0)
 		return(-1);
-	return irq+CPM_IRQ_OFFSET;
+	return irq_linear_revmap(cpm2_pic_host, irq);
 }
 
 static int cpm2_pic_host_match(struct irq_host *h, struct device_node *node)
 {
-	return cpm2_pic_node == NULL || cpm2_pic_node == node;
+	return cpm2_pic_node == node;
 }
 
 static int cpm2_pic_host_map(struct irq_host *h, unsigned int virq,
@@ -177,39 +221,21 @@
 	return 0;
 }
 
-static void cpm2_host_unmap(struct irq_host *h, unsigned int virq)
-{
-	/* Make sure irq is masked in hardware */
-	cpm2_mask_irq(virq);
-
-	/* remove chip and handler */
-	set_irq_chip_and_handler(virq, NULL, NULL);
-}
-
 static int cpm2_pic_host_xlate(struct irq_host *h, struct device_node *ct,
 			    u32 *intspec, unsigned int intsize,
 			    irq_hw_number_t *out_hwirq, unsigned int *out_flags)
 {
-	static const unsigned char map_cpm2_senses[4] = {
-		IRQ_TYPE_LEVEL_LOW,
-		IRQ_TYPE_LEVEL_HIGH,
-		IRQ_TYPE_EDGE_FALLING,
-		IRQ_TYPE_EDGE_RISING,
-	};
-
 	*out_hwirq = intspec[0];
-	if (intsize > 1 && intspec[1] < 4)
-		*out_flags = map_cpm2_senses[intspec[1]];
+	if (intsize > 1)
+		*out_flags = intspec[1];
 	else
 		*out_flags = IRQ_TYPE_NONE;
-
 	return 0;
 }
 
 static struct irq_host_ops cpm2_pic_host_ops = {
 	.match = cpm2_pic_host_match,
 	.map = cpm2_pic_host_map,
-	.unmap = cpm2_host_unmap,
 	.xlate = cpm2_pic_host_xlate,
 };
 
@@ -217,32 +243,34 @@
 {
 	int i;
 
+	cpm2_intctl = cpm2_map(im_intctl);
+
 	/* Clear the CPM IRQ controller, in case it has any bits set
 	 * from the bootloader
 	 */
 
 	/* Mask out everything */
 
-	cpm2_intctl->ic_simrh = 0x00000000;
-	cpm2_intctl->ic_simrl = 0x00000000;
+	out_be32(&cpm2_intctl->ic_simrh, 0x00000000);
+	out_be32(&cpm2_intctl->ic_simrl, 0x00000000);
 
 	wmb();
 
 	/* Ack everything */
-	cpm2_intctl->ic_sipnrh = 0xffffffff;
-	cpm2_intctl->ic_sipnrl = 0xffffffff;
+	out_be32(&cpm2_intctl->ic_sipnrh, 0xffffffff);
+	out_be32(&cpm2_intctl->ic_sipnrl, 0xffffffff);
 	wmb();
 
 	/* Dummy read of the vector */
-	i = cpm2_intctl->ic_sivec;
+	i = in_be32(&cpm2_intctl->ic_sivec);
 	rmb();
 
 	/* Initialize the default interrupt mapping priorities,
 	 * in case the boot rom changed something on us.
 	 */
-	cpm2_intctl->ic_sicr = 0;
-	cpm2_intctl->ic_scprrh = 0x05309770;
-	cpm2_intctl->ic_scprrl = 0x05309770;
+	out_be16(&cpm2_intctl->ic_sicr, 0);
+	out_be32(&cpm2_intctl->ic_scprrh, 0x05309770);
+	out_be32(&cpm2_intctl->ic_scprrl, 0x05309770);
 
 	/* create a legacy host */
 	cpm2_pic_node = of_node_get(node);
diff --git a/arch/powerpc/sysdev/cpm2_pic.h b/arch/powerpc/sysdev/cpm2_pic.h
index 2840616..30e5828 100644
--- a/arch/powerpc/sysdev/cpm2_pic.h
+++ b/arch/powerpc/sysdev/cpm2_pic.h
@@ -1,8 +1,6 @@
 #ifndef _PPC_KERNEL_CPM2_H
 #define _PPC_KERNEL_CPM2_H
 
-extern intctl_cpm2_t *cpm2_intctl;
-
 extern unsigned int cpm2_get_irq(void);
 
 extern void cpm2_pic_init(struct device_node*);