diff options
Diffstat (limited to 'arch/mips')
-rw-r--r-- | arch/mips/au1000/Kconfig | 1 | ||||
-rw-r--r-- | arch/mips/au1000/common/irq.c | 251 | ||||
-rw-r--r-- | arch/mips/au1000/common/power.c | 7 | ||||
-rw-r--r-- | arch/mips/au1000/pb1200/irqmap.c | 2 | ||||
-rw-r--r-- | arch/mips/configs/mtx1_defconfig | 2 | ||||
-rw-r--r-- | arch/mips/kernel/head.S | 4 | ||||
-rw-r--r-- | arch/mips/kernel/time.c | 47 | ||||
-rw-r--r-- | arch/mips/kernel/traps.c | 164 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-time.c | 9 | ||||
-rw-r--r-- | arch/mips/sibyte/bcm1480/time.c | 2 | ||||
-rw-r--r-- | arch/mips/sibyte/sb1250/time.c | 8 |
11 files changed, 280 insertions, 217 deletions
diff --git a/arch/mips/au1000/Kconfig b/arch/mips/au1000/Kconfig index 29c95d9..a23d415 100644 --- a/arch/mips/au1000/Kconfig +++ b/arch/mips/au1000/Kconfig @@ -137,6 +137,7 @@ config SOC_AU1200 config SOC_AU1X00 bool select 64BIT_PHYS_ADDR + select IRQ_CPU select SYS_HAS_CPU_MIPS32_R1 select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_APM_EMULATION diff --git a/arch/mips/au1000/common/irq.c b/arch/mips/au1000/common/irq.c index c00f308..59e932a9 100644 --- a/arch/mips/au1000/common/irq.c +++ b/arch/mips/au1000/common/irq.c @@ -1,11 +1,10 @@ /* - * BRIEF MODULE DESCRIPTION - * Au1000 interrupt routines. - * * Copyright 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. * ppopov@mvista.com or source@mvista.com * + * Copyright (C) 2007 Ralf Baechle (ralf@linux-mips.org) + * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your @@ -32,6 +31,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> +#include <asm/irq_cpu.h> #include <asm/mipsregs.h> #include <asm/mach-au1x00/au1000.h> #ifdef CONFIG_MIPS_PB1000 @@ -44,7 +44,7 @@ #define EXT_INTC1_REQ1 5 /* IP 5 */ #define MIPS_TIMER_IP 7 /* IP 7 */ -void (*board_init_irq)(void); +void (*board_init_irq)(void) __initdata = NULL; static DEFINE_SPINLOCK(irq_lock); @@ -134,12 +134,14 @@ void restore_au1xxx_intctl(void) inline void local_enable_irq(unsigned int irq_nr) { - if (irq_nr > AU1000_LAST_INTC0_INT) { - au_writel(1 << (irq_nr - 32), IC1_MASKSET); - au_writel(1 << (irq_nr - 32), IC1_WAKESET); + unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE; + + if (bit >= 32) { + au_writel(1 << (bit - 32), IC1_MASKSET); + au_writel(1 << (bit - 32), IC1_WAKESET); } else { - au_writel(1 << irq_nr, IC0_MASKSET); - au_writel(1 << irq_nr, IC0_WAKESET); + au_writel(1 << bit, IC0_MASKSET); + au_writel(1 << bit, IC0_WAKESET); } au_sync(); } @@ -147,12 +149,14 @@ inline void local_enable_irq(unsigned int irq_nr) inline void local_disable_irq(unsigned int irq_nr) { - if (irq_nr > AU1000_LAST_INTC0_INT) { - au_writel(1 << (irq_nr - 32), IC1_MASKCLR); - au_writel(1 << (irq_nr - 32), IC1_WAKECLR); + unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE; + + if (bit >= 32) { + au_writel(1 << (bit - 32), IC1_MASKCLR); + au_writel(1 << (bit - 32), IC1_WAKECLR); } else { - au_writel(1 << irq_nr, IC0_MASKCLR); - au_writel(1 << irq_nr, IC0_WAKECLR); + au_writel(1 << bit, IC0_MASKCLR); + au_writel(1 << bit, IC0_WAKECLR); } au_sync(); } @@ -160,12 +164,14 @@ inline void local_disable_irq(unsigned int irq_nr) static inline void mask_and_ack_rise_edge_irq(unsigned int irq_nr) { - if (irq_nr > AU1000_LAST_INTC0_INT) { - au_writel(1 << (irq_nr - 32), IC1_RISINGCLR); - au_writel(1 << (irq_nr - 32), IC1_MASKCLR); + unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE; + + if (bit >= 32) { + au_writel(1 << (bit - 32), IC1_RISINGCLR); + au_writel(1 << (bit - 32), IC1_MASKCLR); } else { - au_writel(1 << irq_nr, IC0_RISINGCLR); - au_writel(1 << irq_nr, IC0_MASKCLR); + au_writel(1 << bit, IC0_RISINGCLR); + au_writel(1 << bit, IC0_MASKCLR); } au_sync(); } @@ -173,12 +179,14 @@ static inline void mask_and_ack_rise_edge_irq(unsigned int irq_nr) static inline void mask_and_ack_fall_edge_irq(unsigned int irq_nr) { - if (irq_nr > AU1000_LAST_INTC0_INT) { - au_writel(1 << (irq_nr - 32), IC1_FALLINGCLR); - au_writel(1 << (irq_nr - 32), IC1_MASKCLR); + unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE; + + if (bit >= 32) { + au_writel(1 << (bit - 32), IC1_FALLINGCLR); + au_writel(1 << (bit - 32), IC1_MASKCLR); } else { - au_writel(1 << irq_nr, IC0_FALLINGCLR); - au_writel(1 << irq_nr, IC0_MASKCLR); + au_writel(1 << bit, IC0_FALLINGCLR); + au_writel(1 << bit, IC0_MASKCLR); } au_sync(); } @@ -186,17 +194,20 @@ static inline void mask_and_ack_fall_edge_irq(unsigned int irq_nr) static inline void mask_and_ack_either_edge_irq(unsigned int irq_nr) { - /* This may assume that we don't get interrupts from + unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE; + + /* + * This may assume that we don't get interrupts from * both edges at once, or if we do, that we don't care. */ - if (irq_nr > AU1000_LAST_INTC0_INT) { - au_writel(1 << (irq_nr - 32), IC1_FALLINGCLR); - au_writel(1 << (irq_nr - 32), IC1_RISINGCLR); - au_writel(1 << (irq_nr - 32), IC1_MASKCLR); + if (bit >= 32) { + au_writel(1 << (bit - 32), IC1_FALLINGCLR); + au_writel(1 << (bit - 32), IC1_RISINGCLR); + au_writel(1 << (bit - 32), IC1_MASKCLR); } else { - au_writel(1 << irq_nr, IC0_FALLINGCLR); - au_writel(1 << irq_nr, IC0_RISINGCLR); - au_writel(1 << irq_nr, IC0_MASKCLR); + au_writel(1 << bit, IC0_FALLINGCLR); + au_writel(1 << bit, IC0_RISINGCLR); + au_writel(1 << bit, IC0_MASKCLR); } au_sync(); } @@ -213,10 +224,8 @@ static inline void mask_and_ack_level_irq(unsigned int irq_nr) au_sync(); } #endif - return; } - static void end_irq(unsigned int irq_nr) { if (!(irq_desc[irq_nr].status & (IRQ_DISABLED | IRQ_INPROGRESS))) @@ -341,114 +350,118 @@ void startup_match20_interrupt(irq_handler_t handler) } #endif -static void setup_local_irq(unsigned int irq_nr, int type, int int_req) +static void __init setup_local_irq(unsigned int irq_nr, int type, int int_req) { - if (irq_nr > AU1000_MAX_INTR) return; + unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE; + + if (irq_nr > AU1000_MAX_INTR) + return; + /* Config2[n], Config1[n], Config0[n] */ - if (irq_nr > AU1000_LAST_INTC0_INT) { + if (bit >= 32) { switch (type) { case INTC_INT_RISE_EDGE: /* 0:0:1 */ - au_writel(1 << (irq_nr - 32), IC1_CFG2CLR); - au_writel(1 << (irq_nr - 32), IC1_CFG1CLR); - au_writel(1 << (irq_nr - 32), IC1_CFG0SET); + au_writel(1 << (bit - 32), IC1_CFG2CLR); + au_writel(1 << (bit - 32), IC1_CFG1CLR); + au_writel(1 << (bit - 32), IC1_CFG0SET); set_irq_chip(irq_nr, &rise_edge_irq_type); break; case INTC_INT_FALL_EDGE: /* 0:1:0 */ - au_writel(1 << (irq_nr - 32), IC1_CFG2CLR); - au_writel(1 << (irq_nr - 32), IC1_CFG1SET); - au_writel(1 << (irq_nr - 32), IC1_CFG0CLR); + au_writel(1 << (bit - 32), IC1_CFG2CLR); + au_writel(1 << (bit - 32), IC1_CFG1SET); + au_writel(1 << (bit - 32), IC1_CFG0CLR); set_irq_chip(irq_nr, &fall_edge_irq_type); break; case INTC_INT_RISE_AND_FALL_EDGE: /* 0:1:1 */ - au_writel(1 << (irq_nr - 32), IC1_CFG2CLR); - au_writel(1 << (irq_nr - 32), IC1_CFG1SET); - au_writel(1 << (irq_nr - 32), IC1_CFG0SET); + au_writel(1 << (bit - 32), IC1_CFG2CLR); + au_writel(1 << (bit - 32), IC1_CFG1SET); + au_writel(1 << (bit - 32), IC1_CFG0SET); set_irq_chip(irq_nr, &either_edge_irq_type); break; case INTC_INT_HIGH_LEVEL: /* 1:0:1 */ - au_writel(1 << (irq_nr - 32), IC1_CFG2SET); - au_writel(1 << (irq_nr - 32), IC1_CFG1CLR); - au_writel(1 << (irq_nr - 32), IC1_CFG0SET); + au_writel(1 << (bit - 32), IC1_CFG2SET); + au_writel(1 << (bit - 32), IC1_CFG1CLR); + au_writel(1 << (bit - 32), IC1_CFG0SET); set_irq_chip(irq_nr, &level_irq_type); break; case INTC_INT_LOW_LEVEL: /* 1:1:0 */ - au_writel(1 << (irq_nr - 32), IC1_CFG2SET); - au_writel(1 << (irq_nr - 32), IC1_CFG1SET); - au_writel(1 << (irq_nr - 32), IC1_CFG0CLR); + au_writel(1 << (bit - 32), IC1_CFG2SET); + au_writel(1 << (bit - 32), IC1_CFG1SET); + au_writel(1 << (bit - 32), IC1_CFG0CLR); set_irq_chip(irq_nr, &level_irq_type); break; case INTC_INT_DISABLED: /* 0:0:0 */ - au_writel(1 << (irq_nr - 32), IC1_CFG0CLR); - au_writel(1 << (irq_nr - 32), IC1_CFG1CLR); - au_writel(1 << (irq_nr - 32), IC1_CFG2CLR); + au_writel(1 << (bit - 32), IC1_CFG0CLR); + au_writel(1 << (bit - 32), IC1_CFG1CLR); + au_writel(1 << (bit - 32), IC1_CFG2CLR); break; default: /* disable the interrupt */ printk(KERN_WARNING "unexpected int type %d (irq %d)\n", type, irq_nr); - au_writel(1 << (irq_nr - 32), IC1_CFG0CLR); - au_writel(1 << (irq_nr - 32), IC1_CFG1CLR); - au_writel(1 << (irq_nr - 32), IC1_CFG2CLR); + au_writel(1 << (bit - 32), IC1_CFG0CLR); + au_writel(1 << (bit - 32), IC1_CFG1CLR); + au_writel(1 << (bit - 32), IC1_CFG2CLR); return; } if (int_req) /* assign to interrupt request 1 */ - au_writel(1 << (irq_nr - 32), IC1_ASSIGNCLR); + au_writel(1 << (bit - 32), IC1_ASSIGNCLR); else /* assign to interrupt request 0 */ - au_writel(1 << (irq_nr - 32), IC1_ASSIGNSET); - au_writel(1 << (irq_nr - 32), IC1_SRCSET); - au_writel(1 << (irq_nr - 32), IC1_MASKCLR); - au_writel(1 << (irq_nr - 32), IC1_WAKECLR); + au_writel(1 << (bit - 32), IC1_ASSIGNSET); + au_writel(1 << (bit - 32), IC1_SRCSET); + au_writel(1 << (bit - 32), IC1_MASKCLR); + au_writel(1 << (bit - 32), IC1_WAKECLR); } else { switch (type) { case INTC_INT_RISE_EDGE: /* 0:0:1 */ - au_writel(1 << irq_nr, IC0_CFG2CLR); - au_writel(1 << irq_nr, IC0_CFG1CLR); - au_writel(1 << irq_nr, IC0_CFG0SET); + au_writel(1 << bit, IC0_CFG2CLR); + au_writel(1 << bit, IC0_CFG1CLR); + au_writel(1 << bit, IC0_CFG0SET); set_irq_chip(irq_nr, &rise_edge_irq_type); break; case INTC_INT_FALL_EDGE: /* 0:1:0 */ - au_writel(1 << irq_nr, IC0_CFG2CLR); - au_writel(1 << irq_nr, IC0_CFG1SET); - au_writel(1 << irq_nr, IC0_CFG0CLR); + au_writel(1 << bit, IC0_CFG2CLR); + au_writel(1 << bit, IC0_CFG1SET); + au_writel(1 << bit, IC0_CFG0CLR); set_irq_chip(irq_nr, &fall_edge_irq_type); break; case INTC_INT_RISE_AND_FALL_EDGE: /* 0:1:1 */ - au_writel(1 << irq_nr, IC0_CFG2CLR); - au_writel(1 << irq_nr, IC0_CFG1SET); - au_writel(1 << irq_nr, IC0_CFG0SET); + au_writel(1 << bit, IC0_CFG2CLR); + au_writel(1 << bit, IC0_CFG1SET); + au_writel(1 << bit, IC0_CFG0SET); set_irq_chip(irq_nr, &either_edge_irq_type); break; case INTC_INT_HIGH_LEVEL: /* 1:0:1 */ - au_writel(1 << irq_nr, IC0_CFG2SET); - au_writel(1 << irq_nr, IC0_CFG1CLR); - au_writel(1 << irq_nr, IC0_CFG0SET); + au_writel(1 << bit, IC0_CFG2SET); + au_writel(1 << bit, IC0_CFG1CLR); + au_writel(1 << bit, IC0_CFG0SET); set_irq_chip(irq_nr, &level_irq_type); break; case INTC_INT_LOW_LEVEL: /* 1:1:0 */ - au_writel(1 << irq_nr, IC0_CFG2SET); - au_writel(1 << irq_nr, IC0_CFG1SET); - au_writel(1 << irq_nr, IC0_CFG0CLR); + au_writel(1 << bit, IC0_CFG2SET); + au_writel(1 << bit, IC0_CFG1SET); + au_writel(1 << bit, IC0_CFG0CLR); set_irq_chip(irq_nr, &level_irq_type); break; case INTC_INT_DISABLED: /* 0:0:0 */ - au_writel(1 << irq_nr, IC0_CFG0CLR); - au_writel(1 << irq_nr, IC0_CFG1CLR); - au_writel(1 << irq_nr, IC0_CFG2CLR); + au_writel(1 << bit, IC0_CFG0CLR); + au_writel(1 << bit, IC0_CFG1CLR); + au_writel(1 << bit, IC0_CFG2CLR); break; default: /* disable the interrupt */ printk(KERN_WARNING "unexpected int type %d (irq %d)\n", type, irq_nr); - au_writel(1 << irq_nr, IC0_CFG0CLR); - au_writel(1 << irq_nr, IC0_CFG1CLR); - au_writel(1 << irq_nr, IC0_CFG2CLR); + au_writel(1 << bit, IC0_CFG0CLR); + au_writel(1 << bit, IC0_CFG1CLR); + au_writel(1 << bit, IC0_CFG2CLR); return; } if (int_req) /* assign to interrupt request 1 */ - au_writel(1 << irq_nr, IC0_ASSIGNCLR); + au_writel(1 << bit, IC0_ASSIGNCLR); else /* assign to interrupt request 0 */ - au_writel(1 << irq_nr, IC0_ASSIGNSET); - au_writel(1 << irq_nr, IC0_SRCSET); - au_writel(1 << irq_nr, IC0_MASKCLR); - au_writel(1 << irq_nr, IC0_WAKECLR); + au_writel(1 << bit, IC0_ASSIGNSET); + au_writel(1 << bit, IC0_SRCSET); + au_writel(1 << bit, IC0_MASKCLR); + au_writel(1 << bit, IC0_WAKECLR); } au_sync(); } @@ -461,8 +474,8 @@ static void setup_local_irq(unsigned int irq_nr, int type, int int_req) static void intc0_req0_irqdispatch(void) { - int irq = 0; static unsigned long intc0_req0; + unsigned int bit; intc0_req0 |= au_readl(IC0_REQ0INT); @@ -481,25 +494,25 @@ static void intc0_req0_irqdispatch(void) return; } #endif - irq = ffs(intc0_req0); - intc0_req0 &= ~(1 << irq); - do_IRQ(irq); + bit = ffs(intc0_req0); + intc0_req0 &= ~(1 << bit); + do_IRQ(MIPS_CPU_IRQ_BASE + bit); } static void intc0_req1_irqdispatch(void) { - int irq = 0; static unsigned long intc0_req1; + unsigned int bit; intc0_req1 |= au_readl(IC0_REQ1INT); if (!intc0_req1) return; - irq = ffs(intc0_req1); - intc0_req1 &= ~(1 << irq); - do_IRQ(irq); + bit = ffs(intc0_req1); + intc0_req1 &= ~(1 << bit); + do_IRQ(bit); } @@ -509,43 +522,41 @@ static void intc0_req1_irqdispatch(void) */ static void intc1_req0_irqdispatch(void) { - int irq = 0; static unsigned long intc1_req0; + unsigned int bit; intc1_req0 |= au_readl(IC1_REQ0INT); if (!intc1_req0) return; - irq = ffs(intc1_req0); - intc1_req0 &= ~(1 << irq); - irq += 32; - do_IRQ(irq); + bit = ffs(intc1_req0); + intc1_req0 &= ~(1 << bit); + do_IRQ(MIPS_CPU_IRQ_BASE + 32 + bit); } static void intc1_req1_irqdispatch(void) { - int irq = 0; static unsigned long intc1_req1; + unsigned int bit; intc1_req1 |= au_readl(IC1_REQ1INT); if (!intc1_req1) return; - irq = ffs(intc1_req1); - intc1_req1 &= ~(1 << irq); - irq += 32; - do_IRQ(irq); + bit = ffs(intc1_req1); + intc1_req1 &= ~(1 << bit); + do_IRQ(MIPS_CPU_IRQ_BASE + 32 + bit); } asmlinkage void plat_irq_dispatch(void) { - unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM; + unsigned int pending = read_c0_status() & read_c0_cause(); if (pending & CAUSEF_IP7) - do_IRQ(63); + do_IRQ(MIPS_CPU_IRQ_BASE + 7); else if (pending & CAUSEF_IP2) intc0_req0_irqdispatch(); else if (pending & CAUSEF_IP3) @@ -561,17 +572,15 @@ asmlinkage void plat_irq_dispatch(void) void __init arch_init_irq(void) { int i; - unsigned long cp0_status; struct au1xxx_irqmap *imp; extern struct au1xxx_irqmap au1xxx_irq_map[]; extern struct au1xxx_irqmap au1xxx_ic0_map[]; extern int au1xxx_nr_irqs; extern int au1xxx_ic0_nr_irqs; - cp0_status = read_c0_status(); - - /* Initialize interrupt controllers to a safe state. - */ + /* + * Initialize interrupt controllers to a safe state. + */ au_writel(0xffffffff, IC0_CFG0CLR); au_writel(0xffffffff, IC0_CFG1CLR); au_writel(0xffffffff, IC0_CFG2CLR); @@ -594,16 +603,20 @@ void __init arch_init_irq(void) au_writel(0xffffffff, IC1_RISINGCLR); au_writel(0x00000000, IC1_TESTBIT); - /* Initialize IC0, which is fixed per processor. - */ + mips_cpu_irq_init(); + + /* + * Initialize IC0, which is fixed per processor. + */ imp = au1xxx_ic0_map; for (i = 0; i < au1xxx_ic0_nr_irqs; i++) { setup_local_irq(imp->im_irq, imp->im_type, imp->im_request); imp++; } - /* Now set up the irq mapping for the board. - */ + /* + * Now set up the irq mapping for the board. + */ imp = au1xxx_irq_map; for (i = 0; i < au1xxx_nr_irqs; i++) { setup_local_irq(imp->im_irq, imp->im_type, imp->im_request); @@ -615,5 +628,5 @@ void __init arch_init_irq(void) /* Board specific IRQ initialization. */ if (board_init_irq) - (*board_init_irq)(); + board_init_irq(); } diff --git a/arch/mips/au1000/common/power.c b/arch/mips/au1000/common/power.c index 6f57f72..54047d6 100644 --- a/arch/mips/au1000/common/power.c +++ b/arch/mips/au1000/common/power.c @@ -403,9 +403,9 @@ static int pm_do_freq(ctl_table * ctl, int write, struct file *file, } - /* We don't want _any_ interrupts other than - * match20. Otherwise our au1000_calibrate_delay() - * calculation will be off, potentially a lot. + /* + * We don't want _any_ interrupts other than match20. Otherwise our + * au1000_calibrate_delay() calculation will be off, potentially a lot. */ intc0_mask = save_local_and_disable(0); intc1_mask = save_local_and_disable(1); @@ -414,6 +414,7 @@ static int pm_do_freq(ctl_table * ctl, int write, struct file *file, au1000_calibrate_delay(); restore_local_and_enable(0, intc0_mask); restore_local_and_enable(1, intc1_mask); + return retval; } diff --git a/arch/mips/au1000/pb1200/irqmap.c b/arch/mips/au1000/pb1200/irqmap.c index 3bee274..5f48b06 100644 --- a/arch/mips/au1000/pb1200/irqmap.c +++ b/arch/mips/au1000/pb1200/irqmap.c @@ -74,7 +74,7 @@ irqreturn_t pb1200_cascade_handler( int irq, void *dev_id) bcsr->int_status = bisr; for( ; bisr; bisr &= (bisr-1) ) { - extirq_nr = PB1200_INT_BEGIN + au_ffs(bisr); + extirq_nr = PB1200_INT_BEGIN + ffs(bisr); /* Ack and dispatch IRQ */ do_IRQ(extirq_nr); } diff --git a/arch/mips/configs/mtx1_defconfig b/arch/mips/configs/mtx1_defconfig index 0280ef3..b536d7c 100644 --- a/arch/mips/configs/mtx1_defconfig +++ b/arch/mips/configs/mtx1_defconfig @@ -3021,7 +3021,7 @@ CONFIG_MAGIC_SYSRQ=y # CONFIG_DEBUG_FS is not set # CONFIG_HEADERS_CHECK is not set # CONFIG_DEBUG_KERNEL is not set -# CONFIG_CROSSCOMPILE is not set +CONFIG_CROSSCOMPILE=y CONFIG_CMDLINE="" CONFIG_SYS_SUPPORTS_KGDB=y diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S index e46782b..bf164a5 100644 --- a/arch/mips/kernel/head.S +++ b/arch/mips/kernel/head.S @@ -140,7 +140,7 @@ EXPORT(_stext) -#ifndef CONFIG_BOOT_RAW +#ifdef CONFIG_BOOT_RAW /* * Give us a fighting chance of running if execution beings at the * kernel load address. This is needed because this platform does @@ -149,6 +149,8 @@ EXPORT(_stext) __INIT #endif + __INIT_REFOK + NESTED(kernel_entry, 16, sp) # kernel entry point kernel_entry_setup # cpu specific setup diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c index 05b3651..e4b5e64 100644 --- a/arch/mips/kernel/time.c +++ b/arch/mips/kernel/time.c @@ -391,6 +391,50 @@ static void mips_event_handler(struct clock_event_device *dev) { } +/* + * FIXME: This doesn't hold for the relocated E9000 compare interrupt. + */ +static int c0_compare_int_pending(void) +{ + return (read_c0_cause() >> cp0_compare_irq) & 0x100; +} + +static int c0_compare_int_usable(void) +{ + const unsigned int delta = 0x300000; + unsigned int cnt; + + /* + * IP7 already pending? Try to clear it by acking the timer. + */ + if (c0_compare_int_pending()) { + write_c0_compare(read_c0_compare()); + irq_disable_hazard(); + if (c0_compare_int_pending()) + return 0; + } + + cnt = read_c0_count(); + cnt += delta; + write_c0_compare(cnt); + + while ((long)(read_c0_count() - cnt) <= 0) + ; /* Wait for expiry */ + + if (!c0_compare_int_pending()) + return 0; + + write_c0_compare(read_c0_compare()); + irq_disable_hazard(); + if (c0_compare_int_pending()) + return 0; + + /* + * Feels like a real count / compare timer. + */ + return 1; +} + void __cpuinit mips_clockevent_init(void) { uint64_t mips_freq = mips_hpt_frequency; @@ -412,6 +456,9 @@ void __cpuinit mips_clockevent_init(void) return; #endif + if (!c0_compare_int_usable()) + return; + cd = &per_cpu(mips_clockevent_device, cpu); cd->name = "MIPS"; diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 9c0c478..bbf01b8 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -9,9 +9,10 @@ * Copyright (C) 1999 Silicon Graphics, Inc. * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com * Copyright (C) 2000, 01 MIPS Technologies, Inc. - * Copyright (C) 2002, 2003, 2004, 2005 Maciej W. Rozycki + * Copyright (C) 2002, 2003, 2004, 2005, 2007 Maciej W. Rozycki */ #include <linux/bug.h> +#include <linux/compiler.h> #include <linux/init.h> #include <linux/mm.h> #include <linux/module.h> @@ -410,7 +411,7 @@ asmlinkage void do_be(struct pt_regs *regs) } /* - * ll/sc emulation + * ll/sc, rdhwr, sync emulation */ #define OPCODE 0xfc000000 @@ -419,9 +420,11 @@ asmlinkage void do_be(struct pt_regs *regs) #define OFFSET 0x0000ffff #define LL 0xc0000000 #define SC 0xe0000000 +#define SPEC0 0x00000000 #define SPEC3 0x7c000000 #define RD 0x0000f800 #define FUNC 0x0000003f +#define SYNC 0x0000000f #define RDHWR 0x0000003b /* @@ -432,11 +435,10 @@ unsigned long ll_bit; static struct task_struct *ll_task = NULL; -static inline void simulate_ll(struct pt_regs *regs, unsigned int opcode) +static inline int simulate_ll(struct pt_regs *regs, unsigned int opcode) { unsigned long value, __user *vaddr; long offset; - int signal = 0; /* * analyse the ll instruction that just caused a ri exception @@ -451,14 +453,10 @@ static inline void simulate_ll(struct pt_regs *regs, unsigned int opcode) vaddr = (unsigned long __user *) ((unsigned long)(regs->regs[(opcode & BASE) >> 21]) + offset); - if ((unsigned long)vaddr & 3) { - signal = SIGBUS; - goto sig; - } - if (get_user(value, vaddr)) { - signal = SIGSEGV; - goto sig; - } + if ((unsigned long)vaddr & 3) + return SIGBUS; + if (get_user(value, vaddr)) + return SIGSEGV; preempt_disable(); @@ -471,22 +469,16 @@ static inline void simulate_ll(struct pt_regs *regs, unsigned int opcode) preempt_enable(); - compute_return_epc(regs); - regs->regs[(opcode & RT) >> 16] = value; - return; - -sig: - force_sig(signal, current); + return 0; } -static inline void simulate_sc(struct pt_regs *regs, unsigned int opcode) +static inline int simulate_sc(struct pt_regs *regs, unsigned int opcode) { unsigned long __user *vaddr; unsigned long reg; long offset; - int signal = 0; /* * analyse the sc instruction that just caused a ri exception @@ -502,34 +494,25 @@ static inline void simulate_sc(struct pt_regs *regs, unsigned int opcode) ((unsigned long)(regs->regs[(opcode & BASE) >> 21]) + offset); reg = (opcode & RT) >> 16; - if ((unsigned long)vaddr & 3) { - signal = SIGBUS; - goto sig; - } + if ((unsigned long)vaddr & 3) + return SIGBUS; preempt_disable(); if (ll_bit == 0 || ll_task != current) { - compute_return_epc(regs); regs->regs[reg] = 0; preempt_enable(); - return; + return 0; } preempt_enable(); - if (put_user(regs->regs[reg], vaddr)) { - signal = SIGSEGV; - goto sig; - } + if (put_user(regs->regs[reg], vaddr)) + return SIGSEGV; - compute_return_epc(regs); regs->regs[reg] = 1; - return; - -sig: - force_sig(signal, current); + return 0; } /* @@ -539,27 +522,14 @@ sig: * few processors such as NEC's VR4100 throw reserved instruction exceptions * instead, so we're doing the emulation thing in both exception handlers. */ -static inline int simulate_llsc(struct pt_regs *regs) +static int simulate_llsc(struct pt_regs *regs, unsigned int opcode) { - unsigned int opcode; - - if (get_user(opcode, (unsigned int __user *) exception_epc(regs))) - goto out_sigsegv; - - if ((opcode & OPCODE) == LL) { - simulate_ll(regs, opcode); - return 0; - } - if ((opcode & OPCODE) == SC) { - simulate_sc(regs, opcode); - return 0; - } - - return -EFAULT; /* Strange things going on ... */ + if ((opcode & OPCODE) == LL) + return simulate_ll(regs, opcode); + if ((opcode & OPCODE) == SC) + return simulate_sc(regs, opcode); -out_sigsegv: - force_sig(SIGSEGV, current); - return -EFAULT; + return -1; /* Must be something else ... */ } /* @@ -567,16 +537,9 @@ out_sigsegv: * registers not implemented in hardware. The only current use of this * is the thread area pointer. */ -static inline int simulate_rdhwr(struct pt_regs *regs) +static int simulate_rdhwr(struct pt_regs *regs, unsigned int opcode) { struct thread_info *ti = task_thread_info(current); - unsigned int opcode; - - if (get_user(opcode, (unsigned int __user *) exception_epc(regs))) - goto out_sigsegv; - - if (unlikely(compute_return_epc(regs))) - return -EFAULT; if ((opcode & OPCODE) == SPEC3 && (opcode & FUNC) == RDHWR) { int rd = (opcode & RD) >> 11; @@ -586,16 +549,20 @@ static inline int simulate_rdhwr(struct pt_regs *regs) regs->regs[rt] = ti->tp_value; return 0; default: - return -EFAULT; + return -1; } } /* Not ours. */ - return -EFAULT; + return -1; +} -out_sigsegv: - force_sig(SIGSEGV, current); - return -EFAULT; +static int simulate_sync(struct pt_regs *regs, unsigned int opcode) +{ + if ((opcode & OPCODE) == SPEC0 && (opcode & FUNC) == SYNC) + return 0; + + return -1; /* Must be something else ... */ } asmlinkage void do_ov(struct pt_regs *regs) @@ -767,16 +734,35 @@ out_sigsegv: asmlinkage void do_ri(struct pt_regs *regs) { - die_if_kernel("Reserved instruction in kernel code", regs); + unsigned int __user *epc = (unsigned int __user *)exception_epc(regs); + unsigned long old_epc = regs->cp0_epc; + unsigned int opcode = 0; + int status = -1; - if (!cpu_has_llsc) - if (!simulate_llsc(regs)) - return; + die_if_kernel("Reserved instruction in kernel code", regs); - if (!simulate_rdhwr(regs)) + if (unlikely(compute_return_epc(regs) < 0)) return; - force_sig(SIGILL, current); + if (unlikely(get_user(opcode, epc) < 0)) + status = SIGSEGV; + + if (!cpu_has_llsc && status < 0) + status = simulate_llsc(regs, opcode); + + if (status < 0) + status = simulate_rdhwr(regs, opcode); + + if (status < 0) + status = simulate_sync(regs, opcode); + + if (status < 0) + status = SIGILL; + + if (unlikely(status > 0)) { + regs->cp0_epc = old_epc; /* Undo skip-over. */ + force_sig(status, current); + } } /* @@ -808,7 +794,11 @@ static void mt_ase_fp_affinity(void) asmlinkage void do_cpu(struct pt_regs *regs) { + unsigned int __user *epc; + unsigned long old_epc; + unsigned int opcode; unsigned int cpid; + int status; die_if_kernel("do_cpu invoked from kernel context!", regs); @@ -816,14 +806,32 @@ asmlinkage void do_cpu(struct pt_regs *regs) switch (cpid) { case 0: - if (!cpu_has_llsc) - if (!simulate_llsc(regs)) - return; + epc = (unsigned int __user *)exception_epc(regs); + old_epc = regs->cp0_epc; + opcode = 0; + status = -1; - if (!simulate_rdhwr(regs)) + if (unlikely(compute_return_epc(regs) < 0)) return; - break; + if (unlikely(get_user(opcode, epc) < 0)) + status = SIGSEGV; + + if (!cpu_has_llsc && status < 0) + status = simulate_llsc(regs, opcode); + + if (status < 0) + status = simulate_rdhwr(regs, opcode); + + if (status < 0) + status = SIGILL; + + if (unlikely(status > 0)) { + regs->cp0_epc = old_epc; /* Undo skip-over. */ + force_sig(status, current); + } + + return; case 1: if (used_math()) /* Using the FPU again. */ diff --git a/arch/mips/sgi-ip22/ip22-time.c b/arch/mips/sgi-ip22/ip22-time.c index 9b9bffd..10e5054 100644 --- a/arch/mips/sgi-ip22/ip22-time.c +++ b/arch/mips/sgi-ip22/ip22-time.c @@ -192,12 +192,3 @@ void indy_8254timer_irq(void) ArcEnterInteractiveMode(); irq_exit(); } - -void __init plat_timer_setup(struct irqaction *irq) -{ - /* over-write the handler, we use our own way */ - irq->handler = no_action; - - /* setup irqaction */ - setup_irq(SGI_TIMER_IRQ, irq); -} diff --git a/arch/mips/sibyte/bcm1480/time.c b/arch/mips/sibyte/bcm1480/time.c index 40d7126..5b4bfbb 100644 --- a/arch/mips/sibyte/bcm1480/time.c +++ b/arch/mips/sibyte/bcm1480/time.c @@ -84,7 +84,7 @@ static void sibyte_set_mode(enum clock_event_mode mode, void __iomem *timer_cfg, *timer_init; timer_cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); - timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); + timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)); switch (mode) { case CLOCK_EVT_MODE_PERIODIC: diff --git a/arch/mips/sibyte/sb1250/time.c b/arch/mips/sibyte/sb1250/time.c index 38199ad..fe11fed 100644 --- a/arch/mips/sibyte/sb1250/time.c +++ b/arch/mips/sibyte/sb1250/time.c @@ -83,7 +83,7 @@ static void sibyte_set_mode(enum clock_event_mode mode, void __iomem *timer_cfg, *timer_init; timer_cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); - timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); + timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)); switch(mode) { case CLOCK_EVT_MODE_PERIODIC: @@ -111,7 +111,7 @@ sibyte_next_event(unsigned long delta, struct clock_event_device *evt) void __iomem *timer_cfg, *timer_init; timer_cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); - timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); + timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)); __raw_writeq(0, timer_cfg); __raw_writeq(delta, timer_init); @@ -155,7 +155,7 @@ static void sibyte_set_mode(enum clock_event_mode mode, void __iomem *timer_cfg, *timer_init; timer_cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); - timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); + timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)); switch (mode) { case CLOCK_EVT_MODE_PERIODIC: @@ -183,7 +183,7 @@ sibyte_next_event(unsigned long delta, struct clock_event_device *evt) void __iomem *timer_cfg, *timer_init; timer_cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); - timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); + timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)); __raw_writeq(0, timer_cfg); __raw_writeq(delta, timer_init); |