patch-2.4.25 linux-2.4.25/arch/mips/vr41xx/common/icu.c

Next file: linux-2.4.25/arch/mips/vr41xx/common/int-handler.S
Previous file: linux-2.4.25/arch/mips/vr41xx/common/giu.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.24/arch/mips/vr41xx/common/icu.c linux-2.4.25/arch/mips/vr41xx/common/icu.c
@@ -1,9 +1,9 @@
 /*
  * FILE NAME
- *	arch/mips/vr41xx/vr4122/common/icu.c
+ *	arch/mips/vr41xx/common/icu.c
  *
  * BRIEF MODULE DESCRIPTION
- *	Interrupt Control Unit routines for the NEC VR4122 and VR4131.
+ *	Interrupt Control Unit routines for the NEC VR4100 series.
  *
  * Author: Yoichi Yuasa
  *         yyuasa@mvista.com or source@mvista.com
@@ -33,13 +33,11 @@
 /*
  * Changes:
  *  MontaVista Software Inc. <yyuasa@mvista.com> or <source@mvista.com>
+ *  - New creation, NEC VR4122 and VR4131 are supported.
  *  - Added support for NEC VR4111 and VR4121.
  *
- *  Paul Mundt <lethal@chaoticdreams.org>
- *  - kgdb support.
- *
- *  MontaVista Software Inc. <yyuasa@mvista.com> or <source@mvista.com>
- *  - New creation, NEC VR4122 and VR4131 are supported.
+ *  Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+ *  - Coped with INTASSIGN of NEC VR4133.
  */
 #include <linux/errno.h>
 #include <linux/init.h>
@@ -47,51 +45,65 @@
 #include <linux/irq.h>
 #include <linux/types.h>
 
-#include <asm/addrspace.h>
 #include <asm/cpu.h>
-#include <asm/gdb-stub.h>
 #include <asm/io.h>
-#include <asm/mipsregs.h>
+#include <asm/irq.h>
+#include <asm/irq_cpu.h>
 #include <asm/vr41xx/vr41xx.h>
 
 extern asmlinkage void vr41xx_handle_interrupt(void);
 
-extern void __init init_generic_irq(void);
-extern void mips_cpu_irq_init(u32 irq_base);
-
 extern void vr41xx_giuint_init(void);
+extern void vr41xx_enable_giuint(int pin);
+extern void vr41xx_disable_giuint(int pin);
+extern void vr41xx_clear_giuint(int pin);
 extern unsigned int giuint_do_IRQ(int pin, struct pt_regs *regs);
 
-static u32 vr41xx_icu1_base = 0;
-static u32 vr41xx_icu2_base = 0;
+static uint32_t icu1_base;
+static uint32_t icu2_base;
 
-#define VR4111_SYSINT1REG	KSEG1ADDR(0x0b000080)
-#define VR4111_SYSINT2REG	KSEG1ADDR(0x0b000200)
+static unsigned char sysint1_assign[16] = {
+	0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char sysint2_assign[16] = {
+	2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 
-#define VR4122_SYSINT1REG	KSEG1ADDR(0x0f000080)
-#define VR4122_SYSINT2REG	KSEG1ADDR(0x0f0000a0)
+#define SYSINT1REG_TYPE1	KSEG1ADDR(0x0b000080)
+#define SYSINT2REG_TYPE1	KSEG1ADDR(0x0b000200)
+
+#define SYSINT1REG_TYPE2	KSEG1ADDR(0x0f000080)
+#define SYSINT2REG_TYPE2	KSEG1ADDR(0x0f0000a0)
 
 #define SYSINT1REG	0x00
+#define INTASSIGN0	0x04
+#define INTASSIGN1	0x06
 #define GIUINTLREG	0x08
 #define MSYSINT1REG	0x0c
 #define MGIUINTLREG	0x14
 #define NMIREG		0x18
 #define SOFTREG		0x1a
+#define INTASSIGN2	0x1c
+#define INTASSIGN3	0x1e
 
 #define SYSINT2REG	0x00
 #define GIUINTHREG	0x02
 #define MSYSINT2REG	0x06
 #define MGIUINTHREG	0x08
 
-#define read_icu1(offset)	readw(vr41xx_icu1_base + (offset))
-#define write_icu1(val, offset)	writew((val), vr41xx_icu1_base + (offset))
+#define SYSINT1_IRQ_TO_PIN(x)	((x) - SYSINT1_IRQ_BASE)	/* Pin 0-15 */
+#define SYSINT2_IRQ_TO_PIN(x)	((x) - SYSINT2_IRQ_BASE)	/* Pin 0-15 */
+
+#define read_icu1(offset)	readw(icu1_base + (offset))
+#define write_icu1(val, offset)	writew((val), icu1_base + (offset))
 
-#define read_icu2(offset)	readw(vr41xx_icu2_base + (offset))
-#define write_icu2(val, offset)	writew((val), vr41xx_icu2_base + (offset))
+#define read_icu2(offset)	readw(icu2_base + (offset))
+#define write_icu2(val, offset)	writew((val), icu2_base + (offset))
 
-static inline u16 set_icu1(u16 offset, u16 set)
+#define INTASSIGN_MAX	4
+#define INTASSIGN_MASK	0x0007
+
+static inline uint16_t set_icu1(uint8_t offset, uint16_t set)
 {
-	u16 res;
+	uint16_t res;
 
 	res = read_icu1(offset);
 	res |= set;
@@ -100,9 +112,9 @@
 	return res;
 }
 
-static inline u16 clear_icu1(u16 offset, u16 clear)
+static inline uint16_t clear_icu1(uint8_t offset, uint16_t clear)
 {
-	u16 res;
+	uint16_t res;
 
 	res = read_icu1(offset);
 	res &= ~clear;
@@ -111,9 +123,9 @@
 	return res;
 }
 
-static inline u16 set_icu2(u16 offset, u16 set)
+static inline uint16_t set_icu2(uint8_t offset, uint16_t set)
 {
-	u16 res;
+	uint16_t res;
 
 	res = read_icu2(offset);
 	res |= set;
@@ -122,9 +134,9 @@
 	return res;
 }
 
-static inline u16 clear_icu2(u16 offset, u16 clear)
+static inline uint16_t clear_icu2(uint8_t offset, uint16_t clear)
 {
-	u16 res;
+	uint16_t res;
 
 	res = read_icu2(offset);
 	res &= ~clear;
@@ -137,17 +149,17 @@
 
 static void enable_sysint1_irq(unsigned int irq)
 {
-	set_icu1(MSYSINT1REG, (u16)1 << (irq - SYSINT1_IRQ_BASE));
+	set_icu1(MSYSINT1REG, (uint16_t)1 << SYSINT1_IRQ_TO_PIN(irq));
 }
 
 static void disable_sysint1_irq(unsigned int irq)
 {
-	clear_icu1(MSYSINT1REG, (u16)1 << (irq - SYSINT1_IRQ_BASE));
+	clear_icu1(MSYSINT1REG, (uint16_t)1 << SYSINT1_IRQ_TO_PIN(irq));
 }
 
 static unsigned int startup_sysint1_irq(unsigned int irq)
 {
-	set_icu1(MSYSINT1REG, (u16)1 << (irq - SYSINT1_IRQ_BASE));
+	set_icu1(MSYSINT1REG, (uint16_t)1 << SYSINT1_IRQ_TO_PIN(irq));
 
 	return 0; /* never anything pending */
 }
@@ -158,35 +170,34 @@
 static void end_sysint1_irq(unsigned int irq)
 {
 	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
-		set_icu1(MSYSINT1REG, (u16)1 << (irq - SYSINT1_IRQ_BASE));
+		set_icu1(MSYSINT1REG, (uint16_t)1 << SYSINT1_IRQ_TO_PIN(irq));
 }
 
 static struct hw_interrupt_type sysint1_irq_type = {
-	"SYSINT1",
-	startup_sysint1_irq,
-	shutdown_sysint1_irq,
-	enable_sysint1_irq,
-	disable_sysint1_irq,
-	ack_sysint1_irq,
-	end_sysint1_irq,
-	NULL
+	.typename	= "SYSINT1",
+	.startup	= startup_sysint1_irq,
+	.shutdown	= shutdown_sysint1_irq,
+	.enable		= enable_sysint1_irq,
+	.disable	= disable_sysint1_irq,
+	.ack		= ack_sysint1_irq,
+	.end		= end_sysint1_irq,
 };
 
 /*=======================================================================*/
 
 static void enable_sysint2_irq(unsigned int irq)
 {
-	set_icu2(MSYSINT2REG, (u16)1 << (irq - SYSINT2_IRQ_BASE));
+	set_icu2(MSYSINT2REG, (uint16_t)1 << SYSINT2_IRQ_TO_PIN(irq));
 }
 
 static void disable_sysint2_irq(unsigned int irq)
 {
-	clear_icu2(MSYSINT2REG, (u16)1 << (irq - SYSINT2_IRQ_BASE));
+	clear_icu2(MSYSINT2REG, (uint16_t)1 << SYSINT2_IRQ_TO_PIN(irq));
 }
 
 static unsigned int startup_sysint2_irq(unsigned int irq)
 {
-	set_icu2(MSYSINT2REG, (u16)1 << (irq - SYSINT2_IRQ_BASE));
+	set_icu2(MSYSINT2REG, (uint16_t)1 << SYSINT2_IRQ_TO_PIN(irq));
 
 	return 0; /* never anything pending */
 }
@@ -197,18 +208,17 @@
 static void end_sysint2_irq(unsigned int irq)
 {
 	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
-		set_icu2(MSYSINT2REG, (u16)1 << (irq - SYSINT2_IRQ_BASE));
+		set_icu2(MSYSINT2REG, (uint16_t)1 << SYSINT2_IRQ_TO_PIN(irq));
 }
 
 static struct hw_interrupt_type sysint2_irq_type = {
-	"SYSINT2",
-	startup_sysint2_irq,
-	shutdown_sysint2_irq,
-	enable_sysint2_irq,
-	disable_sysint2_irq,
-	ack_sysint2_irq,
-	end_sysint2_irq,
-	NULL
+	.typename	= "SYSINT2",
+	.startup	= startup_sysint2_irq,
+	.shutdown	= shutdown_sysint2_irq,
+	.enable		= enable_sysint2_irq,
+	.disable	= disable_sysint2_irq,
+	.ack		= ack_sysint2_irq,
+	.end		= end_sysint2_irq,
 };
 
 /*=======================================================================*/
@@ -217,12 +227,11 @@
 {
 	int pin;
 
-	pin = irq - GIU_IRQ_BASE;
+	pin = GIU_IRQ_TO_PIN(irq);
 	if (pin < 16)
-		set_icu1(MGIUINTLREG, (u16)1 << pin);
+		set_icu1(MGIUINTLREG, (uint16_t)1 << pin);
 	else
-		set_icu2(MGIUINTHREG, (u16)1 << (pin - 16));
-
+		set_icu2(MGIUINTHREG, (uint16_t)1 << (pin - 16));
 	vr41xx_enable_giuint(pin);
 }
 
@@ -230,18 +239,17 @@
 {
 	int pin;
 
-	pin = irq - GIU_IRQ_BASE;
+	pin = GIU_IRQ_TO_PIN(irq);
 	vr41xx_disable_giuint(pin);
-
 	if (pin < 16)
-		clear_icu1(MGIUINTLREG, (u16)1 << pin);
+		clear_icu1(MGIUINTLREG, (uint16_t)1 << pin);
 	else
-		clear_icu2(MGIUINTHREG, (u16)1 << (pin - 16));
+		clear_icu2(MGIUINTHREG, (uint16_t)1 << (pin - 16));
 }
 
 static unsigned int startup_giuint_irq(unsigned int irq)
 {
-	vr41xx_clear_giuint(irq - GIU_IRQ_BASE);
+	vr41xx_clear_giuint(GIU_IRQ_TO_PIN(irq));
 
 	enable_giuint_irq(irq);
 
@@ -254,7 +262,7 @@
 {
 	disable_giuint_irq(irq);
 
-	vr41xx_clear_giuint(irq - GIU_IRQ_BASE);
+	vr41xx_clear_giuint(GIU_IRQ_TO_PIN(irq));
 }
 
 static void end_giuint_irq(unsigned int irq)
@@ -264,14 +272,13 @@
 }
 
 static struct hw_interrupt_type giuint_irq_type = {
-	"GIUINT",
-	startup_giuint_irq,
-	shutdown_giuint_irq,
-	enable_giuint_irq,
-	disable_giuint_irq,
-	ack_giuint_irq,
-	end_giuint_irq,
-	NULL
+	.typename	= "GIUINT",
+	.startup	= startup_giuint_irq,
+	.shutdown	= shutdown_giuint_irq,
+	.enable		= enable_giuint_irq,
+	.disable	= disable_giuint_irq,
+	.ack		= ack_giuint_irq,
+	.end		= end_giuint_irq,
 };
 
 /*=======================================================================*/
@@ -285,13 +292,14 @@
 	switch (current_cpu_data.cputype) {
 	case CPU_VR4111:
 	case CPU_VR4121:
-		vr41xx_icu1_base = VR4111_SYSINT1REG;
-		vr41xx_icu2_base = VR4111_SYSINT2REG;
+		icu1_base = SYSINT1REG_TYPE1;
+		icu2_base = SYSINT2REG_TYPE1;
 		break;
 	case CPU_VR4122:
 	case CPU_VR4131:
-		vr41xx_icu1_base = VR4122_SYSINT1REG;
-		vr41xx_icu2_base = VR4122_SYSINT2REG;
+	case CPU_VR4133:
+		icu1_base = SYSINT1REG_TYPE2;
+		icu2_base = SYSINT2REG_TYPE2;
 		break;
 	default:
 		panic("Unexpected CPU of NEC VR4100 series");
@@ -313,7 +321,11 @@
 			irq_desc[i].handler = &giuint_irq_type;
 	}
 
-	setup_irq(ICU_CASCADE_IRQ, &icu_cascade);
+	setup_irq(INT0_CASCADE_IRQ, &icu_cascade);
+	setup_irq(INT1_CASCADE_IRQ, &icu_cascade);
+	setup_irq(INT2_CASCADE_IRQ, &icu_cascade);
+	setup_irq(INT3_CASCADE_IRQ, &icu_cascade);
+	setup_irq(INT4_CASCADE_IRQ, &icu_cascade);
 }
 
 void __init init_IRQ(void)
@@ -327,31 +339,171 @@
 	vr41xx_giuint_init();
 
 	set_except_vector(0, vr41xx_handle_interrupt);
+}
+
+/*=======================================================================*/
+
+static inline int set_sysint1_assign(unsigned int irq, unsigned char assign)
+{
+	irq_desc_t *desc = irq_desc + irq;
+	uint16_t intassign0, intassign1;
+	unsigned int pin;
+
+	pin = SYSINT1_IRQ_TO_PIN(irq);
+
+	spin_lock_irq(&desc->lock);
 
-#ifdef CONFIG_KGDB
-	printk("Setting debug traps - please connect the remote debugger.\n");
-	set_debug_traps();
-	breakpoint();
-#endif
+	intassign0 = read_icu1(INTASSIGN0);
+	intassign1 = read_icu1(INTASSIGN1);
+
+	switch (pin) {
+	case 0:
+		intassign0 &= ~INTASSIGN_MASK;
+		intassign0 |= (uint16_t)assign;
+		break;
+	case 1:
+		intassign0 &= ~(INTASSIGN_MASK << 3);
+		intassign0 |= (uint16_t)assign << 3;
+		break;
+	case 2:
+		intassign0 &= ~(INTASSIGN_MASK << 6);
+		intassign0 |= (uint16_t)assign << 6;
+		break;
+	case 3:
+		intassign0 &= ~(INTASSIGN_MASK << 9);
+		intassign0 |= (uint16_t)assign << 9;
+		break;
+	case 8:
+		intassign0 &= ~(INTASSIGN_MASK << 12);
+		intassign0 |= (uint16_t)assign << 12;
+		break;
+	case 9:
+		intassign1 &= ~INTASSIGN_MASK;
+		intassign1 |= (uint16_t)assign;
+		break;
+	case 11:
+		intassign1 &= ~(INTASSIGN_MASK << 6);
+		intassign1 |= (uint16_t)assign << 6;
+		break;
+	case 12:
+		intassign1 &= ~(INTASSIGN_MASK << 9);
+		intassign1 |= (uint16_t)assign << 9;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	sysint1_assign[pin] = assign;
+	write_icu1(intassign0, INTASSIGN0);
+	write_icu1(intassign1, INTASSIGN1);
+
+	spin_unlock_irq(&desc->lock);
+
+	return 0;
+}
+
+static inline int set_sysint2_assign(unsigned int irq, unsigned char assign)
+{
+	irq_desc_t *desc = irq_desc + irq;
+	uint16_t intassign2, intassign3;
+	unsigned int pin;
+
+	pin = SYSINT2_IRQ_TO_PIN(irq);
+
+	spin_lock_irq(&desc->lock);
+
+	intassign2 = read_icu1(INTASSIGN2);
+	intassign3 = read_icu1(INTASSIGN3);
+
+	switch (pin) {
+	case 0:
+		intassign2 &= ~INTASSIGN_MASK;
+		intassign2 |= (uint16_t)assign;
+		break;
+	case 1:
+		intassign2 &= ~(INTASSIGN_MASK << 3);
+		intassign2 |= (uint16_t)assign << 3;
+		break;
+	case 3:
+		intassign2 &= ~(INTASSIGN_MASK << 6);
+		intassign2 |= (uint16_t)assign << 6;
+		break;
+	case 4:
+		intassign2 &= ~(INTASSIGN_MASK << 9);
+		intassign2 |= (uint16_t)assign << 9;
+		break;
+	case 5:
+		intassign2 &= ~(INTASSIGN_MASK << 12);
+		intassign2 |= (uint16_t)assign << 12;
+		break;
+	case 6:
+		intassign3 &= ~INTASSIGN_MASK;
+		intassign3 |= (uint16_t)assign;
+		break;
+	case 7:
+		intassign3 &= ~(INTASSIGN_MASK << 3);
+		intassign3 |= (uint16_t)assign << 3;
+		break;
+	case 8:
+		intassign3 &= ~(INTASSIGN_MASK << 6);
+		intassign3 |= (uint16_t)assign << 6;
+		break;
+	case 9:
+		intassign3 &= ~(INTASSIGN_MASK << 9);
+		intassign3 |= (uint16_t)assign << 9;
+		break;
+	case 10:
+		intassign3 &= ~(INTASSIGN_MASK << 12);
+		intassign3 |= (uint16_t)assign << 12;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	sysint2_assign[pin] = assign;
+	write_icu1(intassign2, INTASSIGN2);
+	write_icu1(intassign3, INTASSIGN3);
+
+	spin_unlock_irq(&desc->lock);
+
+	return 0;
+}
+
+int vr41xx_set_intassign(unsigned int irq, unsigned char intassign)
+{
+	int retval = -EINVAL;
+
+	if (current_cpu_data.cputype != CPU_VR4133)
+		return -EINVAL;
+
+	if (intassign > INTASSIGN_MAX)
+		return -EINVAL;
+
+	if (irq >= SYSINT1_IRQ_BASE && irq <= SYSINT1_IRQ_LAST)
+		retval = set_sysint1_assign(irq, intassign);
+	else if (irq >= SYSINT2_IRQ_BASE && irq <= SYSINT2_IRQ_LAST)
+		retval = set_sysint2_assign(irq, intassign);
+
+	return retval;
 }
 
 /*=======================================================================*/
 
-static inline void giuint_irqdispatch(u16 pendl, u16 pendh, struct pt_regs *regs)
+static inline void giuint_irq_dispatch(uint16_t pendl, uint16_t pendh,
+                                       struct pt_regs *regs)
 {
 	int i;
 
 	if (pendl) {
 		for (i = 0; i < 16; i++) {
-			if (pendl & (0x0001 << i)) {
+			if (pendl & ((uint16_t)1 << i)) {
 				giuint_do_IRQ(i, regs);
 				return;
 			}
 		}
-	}
-	else if (pendh) {
+	} else {
 		for (i = 0; i < 16; i++) {
-			if (pendh & (0x0001 << i)) {
+			if (pendh & ((uint16_t)1 << i)) {
 				giuint_do_IRQ(i + 16, regs);
 				return;
 			}
@@ -359,10 +511,10 @@
 	}
 }
 
-asmlinkage void icu_irqdispatch(struct pt_regs *regs)
+asmlinkage void irq_dispatch(unsigned char intnum, struct pt_regs *regs)
 {
-	u16 pend1, pend2, pendl, pendh;
-	u16 mask1, mask2, maskl, maskh;
+	uint16_t pend1, pend2, pendl, pendh;
+	uint16_t mask1, mask2, maskl, maskh;
 	int i;
 
 	pend1 = read_icu1(SYSINT1REG);
@@ -377,31 +529,36 @@
 	pendh = read_icu2(GIUINTHREG);
 	maskh = read_icu2(MGIUINTHREG);
 
-	pend1 &= mask1;
-	pend2 &= mask2;
-	pendl &= maskl;
-	pendh &= maskh;
-
-	if (pend1) {
-		if ((pend1 & 0x01ff) == 0x0100) {
-			giuint_irqdispatch(pendl, pendh, regs);
-		}
-		else {
-			for (i = 0; i < 16; i++) {
-				if (pend1 & (0x0001 << i)) {
-					do_IRQ(SYSINT1_IRQ_BASE + i, regs);
-					break;
+	mask1 &= pend1;
+	mask2 &= pend2;
+	maskl &= pendl;
+	maskh &= pendh;
+
+	if (mask1) {
+		for (i = 0; i < 16; i++) {
+			if (intnum == sysint1_assign[i] &&
+			    (mask1 & ((uint16_t)1 << i))) {
+				if (i == 8 && (maskl | maskh)) {
+					giuint_irq_dispatch(maskl, maskh, regs);
+					return;
+				} else {
+					do_IRQ(SYSINT1_IRQ(i), regs);
+					return;
 				}
 			}
 		}
-		return;
 	}
-	else if (pend2) {
+
+	if (mask2) {
 		for (i = 0; i < 16; i++) {
-			if (pend2 & (0x0001 << i)) {
-				do_IRQ(SYSINT2_IRQ_BASE + i, regs);
-				break;
+			if (intnum == sysint2_assign[i] &&
+			    (mask2 & ((uint16_t)1 << i))) {
+				do_IRQ(SYSINT2_IRQ(i), regs);
+				return;
 			}
 		}
 	}
+
+	printk(KERN_ERR "spurious interrupt: %04x,%04x,%04x,%04x\n", pend1, pend2, pendl, pendh);
+	atomic_inc(&irq_err_count);
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)