diff options
Diffstat (limited to 'arch/alpha/kernel')
34 files changed, 758 insertions, 1026 deletions
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile index fa9e06371..309a308a4 100644 --- a/arch/alpha/kernel/Makefile +++ b/arch/alpha/kernel/Makefile @@ -13,10 +13,21 @@ $(CC) -D__ASSEMBLY__ $(AFLAGS) -c -o $*.o $< O_TARGET := kernel.o -O_OBJS := entry.o traps.o process.o osf_sys.o irq.o signal.o setup.o \ - ptrace.o time.o semaphore.o i8259.o +O_OBJS := entry.o traps.o process.o osf_sys.o irq.o irq_alpha.o \ + signal.o setup.o ptrace.o time.o semaphore.o OX_OBJS := alpha_ksyms.o +L_TARGET := rest.a +L_OBJS := irq_i8259.o irq_srm.o \ + es1888.o smc37c669.o smc37c93x.o ns87312.o + +ifdef CONFIG_SMP +O_OBJS += smp.o irq_smp.o +endif + +ifdef CONFIG_PCI +L_OBJS += pci.o pci_iommu.o +endif ifdef CONFIG_ALPHA_GENERIC @@ -25,13 +36,9 @@ O_OBJS += core_apecs.o core_cia.o core_irongate.o core_lca.o core_mcpcia.o \ sys_alcor.o sys_cabriolet.o sys_dp264.o sys_eb64p.o sys_eiger.o \ sys_jensen.o sys_miata.o sys_mikasa.o sys_nautilus.o \ sys_noritake.o sys_rawhide.o sys_ruffian.o sys_rx164.o \ - sys_sable.o sys_sio.o sys_sx164.o sys_takara.o sys_rx164.o \ - es1888.o smc37c669.o smc37c93x.o ns87312.o pci.o pci_iommu.o -else + sys_sable.o sys_sio.o sys_sx164.o sys_takara.o sys_rx164.o -ifdef CONFIG_PCI -O_OBJS += pci.o pci_iommu.o -endif +else # Core logic support ifdef CONFIG_ALPHA_APECS @@ -67,10 +74,10 @@ ifneq ($(CONFIG_ALPHA_ALCOR)$(CONFIG_ALPHA_XLT),) O_OBJS += sys_alcor.o endif ifneq ($(CONFIG_ALPHA_CABRIOLET)$(CONFIG_ALPHA_EB164)$(CONFIG_ALPHA_EB66P)$(CONFIG_ALPHA_LX164)$(CONFIG_ALPHA_PC164),) -O_OBJS += sys_cabriolet.o ns87312.o +O_OBJS += sys_cabriolet.o endif ifdef CONFIG_ALPHA_DP264 -O_OBJS += sys_dp264.o es1888.o smc37c669.o +O_OBJS += sys_dp264.o endif ifneq ($(CONFIG_ALPHA_EB64P)$(CONFIG_ALPHA_EB66),) O_OBJS += sys_eb64p.o @@ -82,7 +89,7 @@ ifdef CONFIG_ALPHA_JENSEN O_OBJS += sys_jensen.o endif ifdef CONFIG_ALPHA_MIATA -O_OBJS += sys_miata.o es1888.o smc37c669.o +O_OBJS += sys_miata.o endif ifdef CONFIG_ALPHA_MIKASA O_OBJS += sys_mikasa.o @@ -106,25 +113,18 @@ ifdef CONFIG_ALPHA_SABLE O_OBJS += sys_sable.o endif ifneq ($(CONFIG_ALPHA_BOOK1)$(CONFIG_ALPHA_AVANTI)$(CONFIG_ALPHA_NONAME)$(CONFIG_ALPHA_P2K)$(CONFIG_ALPHA_XL),) -O_OBJS += sys_sio.o ns87312.o +O_OBJS += sys_sio.o endif ifdef CONFIG_ALPHA_SX164 -O_OBJS += sys_sx164.o smc37c669.o +O_OBJS += sys_sx164.o endif ifdef CONFIG_ALPHA_TAKARA -O_OBJS += sys_takara.o ns87312.o -endif - -# Device support -ifneq ($(CONFIG_ALPHA_PC164)$(CONFIG_ALPHA_LX164),) -O_OBJS += smc37c93x.o +O_OBJS += sys_takara.o endif endif # GENERIC -ifdef CONFIG_SMP -O_OBJS += smp.o -endif +O_OBJS += $(L_TARGET) all: kernel.o head.o diff --git a/arch/alpha/kernel/core_apecs.c b/arch/alpha/kernel/core_apecs.c index 9ea4f53e9..51e5b1174 100644 --- a/arch/alpha/kernel/core_apecs.c +++ b/arch/alpha/kernel/core_apecs.c @@ -385,7 +385,7 @@ apecs_init_arch(void) * Window 1 is direct access 1GB at 1GB * Window 2 is scatter-gather 8MB at 8MB (for isa) */ - hose->sg_isa = iommu_arena_new(0x00800000, 0x00800000, PAGE_SIZE); + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); hose->sg_pci = NULL; __direct_map_base = 0x40000000; __direct_map_size = 0x40000000; diff --git a/arch/alpha/kernel/core_cia.c b/arch/alpha/kernel/core_cia.c index 36b9a1fa9..b8a7f18ee 100644 --- a/arch/alpha/kernel/core_cia.c +++ b/arch/alpha/kernel/core_cia.c @@ -405,10 +405,12 @@ cia_init_arch(void) * ??? We ought to scale window 1 with memory. */ - /* NetBSD hints that page tables must be aligned to 32K due - to a hardware bug. No description of what models affected. */ - hose->sg_isa = iommu_arena_new(0x00800000, 0x00800000, 32768); - hose->sg_pci = iommu_arena_new(0x40000000, 0x08000000, 32768); + /* ??? NetBSD hints that page tables must be aligned to 32K, + possibly due to a hardware bug. This is over-aligned + from the 8K alignment one would expect for an 8MB window. + No description of what CIA revisions affected. */ + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0x8000); + hose->sg_pci = iommu_arena_new(hose, 0x40000000, 0x08000000, 0); __direct_map_base = 0x80000000; __direct_map_size = 0x80000000; diff --git a/arch/alpha/kernel/core_lca.c b/arch/alpha/kernel/core_lca.c index 2bee78a1b..8039c0d14 100644 --- a/arch/alpha/kernel/core_lca.c +++ b/arch/alpha/kernel/core_lca.c @@ -307,7 +307,7 @@ lca_init_arch(void) * Window 0 is direct access 1GB at 1GB * Window 1 is scatter-gather 8MB at 8MB (for isa) */ - hose->sg_isa = iommu_arena_new(0x00800000, 0x00800000, PAGE_SIZE); + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); hose->sg_pci = NULL; __direct_map_base = 0x40000000; __direct_map_size = 0x40000000; diff --git a/arch/alpha/kernel/core_mcpcia.c b/arch/alpha/kernel/core_mcpcia.c index ac97ce1fe..7564d83e9 100644 --- a/arch/alpha/kernel/core_mcpcia.c +++ b/arch/alpha/kernel/core_mcpcia.c @@ -404,8 +404,8 @@ mcpcia_startup_hose(struct pci_controler *hose) * ??? We ought to scale window 1 with memory. */ - hose->sg_isa = iommu_arena_new(0x00800000, 0x00800000, PAGE_SIZE); - hose->sg_pci = iommu_arena_new(0x40000000, 0x08000000, PAGE_SIZE); + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); + hose->sg_pci = iommu_arena_new(hose, 0x40000000, 0x08000000, 0); __direct_map_base = 0x80000000; __direct_map_size = 0x80000000; diff --git a/arch/alpha/kernel/core_pyxis.c b/arch/alpha/kernel/core_pyxis.c index 80ee8ba7f..f6106c475 100644 --- a/arch/alpha/kernel/core_pyxis.c +++ b/arch/alpha/kernel/core_pyxis.c @@ -36,7 +36,6 @@ */ #define DEBUG_CONFIG 0 - #if DEBUG_CONFIG # define DBG_CNF(args) printk args #else @@ -304,7 +303,7 @@ pyxis_enable_irq(unsigned int irq) pyxis_update_irq_hw(cached_irq_mask |= 1UL << (irq - 16)); } -static inline void +static void pyxis_disable_irq(unsigned int irq) { pyxis_update_irq_hw(cached_irq_mask &= ~(1UL << (irq - 16))); @@ -318,6 +317,13 @@ pyxis_startup_irq(unsigned int irq) } static void +pyxis_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + pyxis_enable_irq(irq); +} + +static void pyxis_mask_and_ack_irq(unsigned int irq) { unsigned long bit = 1UL << (irq - 16); @@ -340,7 +346,7 @@ static struct hw_interrupt_type pyxis_irq_type = { enable: pyxis_enable_irq, disable: pyxis_disable_irq, ack: pyxis_mask_and_ack_irq, - end: pyxis_enable_irq, + end: pyxis_end_irq, }; void @@ -382,7 +388,7 @@ init_pyxis_irqs(unsigned long ignore_mask) for (i = 16; i < 48; ++i) { if ((ignore_mask >> i) & 1) continue; - irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; irq_desc[i].handler = &pyxis_irq_type; } @@ -427,6 +433,8 @@ pyxis_broken_pci_tbi(struct pci_controler *hose, ctrl = *(vuip)PYXIS_CTRL; *(vuip)PYXIS_CTRL = ctrl | 4; mb(); + *(vuip)PYXIS_CTRL; + mb(); /* Read from PCI dense memory space at TBI_ADDR, skipping 64k on each read. This forces SG TLB misses. It appears that @@ -441,6 +449,8 @@ pyxis_broken_pci_tbi(struct pci_controler *hose, mb(); *(vuip)PYXIS_CTRL = ctrl; mb(); + *(vuip)PYXIS_CTRL; + mb(); __restore_flags(flags); } @@ -473,31 +483,31 @@ pyxis_init_arch(void) struct pci_controler *hose; unsigned int temp; -#if 0 - printk("pyxis_init: PYXIS_ERR_MASK 0x%x\n", *(vuip)PYXIS_ERR_MASK); - printk("pyxis_init: PYXIS_ERR 0x%x\n", *(vuip)PYXIS_ERR); - printk("pyxis_init: PYXIS_INT_REQ 0x%lx\n", *(vulp)PYXIS_INT_REQ); - printk("pyxis_init: PYXIS_INT_MASK 0x%lx\n", *(vulp)PYXIS_INT_MASK); - printk("pyxis_init: PYXIS_INT_ROUTE 0x%lx\n", *(vulp)PYXIS_INT_ROUTE); - printk("pyxis_init: PYXIS_INT_HILO 0x%lx\n", *(vulp)PYXIS_INT_HILO); - printk("pyxis_init: PYXIS_INT_CNFG 0x%x\n", *(vuip)PYXIS_INT_CNFG); - printk("pyxis_init: PYXIS_RT_COUNT 0x%lx\n", *(vulp)PYXIS_RT_COUNT); -#endif - - /* - * Set up error reporting. Make sure CPU_PE is OFF in the mask. - */ + /* Set up error reporting. Make sure CPU_PE is OFF in the mask. */ temp = *(vuip)PYXIS_ERR_MASK; - temp &= ~4; - *(vuip)PYXIS_ERR_MASK = temp; - mb(); - *(vuip)PYXIS_ERR_MASK; /* re-read to force write */ + *(vuip)PYXIS_ERR_MASK = temp & ~4; + /* Enable master/target abort. */ temp = *(vuip)PYXIS_ERR; - temp |= 0x180; /* master/target abort */ - *(vuip)PYXIS_ERR = temp; + *(vuip)PYXIS_ERR = temp | 0x180; + + /* Clear the PYXIS_CFG register, which gets used for PCI Config + Space accesses. That is the way we want to use it, and we do + not want to depend on what ARC or SRM might have left behind. */ + *(vuip)PYXIS_CFG = 0; + + /* Zero the HAEs. */ + *(vuip)PYXIS_HAE_MEM = 0; + *(vuip)PYXIS_HAE_IO = 0; + + /* Finally, check that the PYXIS_CTRL1 has IOA_BEN set for + enabling byte/word PCI bus space(s) access. */ + temp = *(vuip)PYXIS_CTRL1; + *(vuip)PYXIS_CTRL1 = temp | 1; + + /* Syncronize with all previous changes. */ mb(); - *(vuip)PYXIS_ERR; /* re-read to force write */ + *(vuip)PYXIS_REV; /* * Create our single hose. @@ -524,10 +534,41 @@ pyxis_init_arch(void) * address range. */ - /* NetBSD hints that page tables must be aligned to 32K due - to a hardware bug. No description of what models affected. */ - hose->sg_isa = iommu_arena_new(0x00800000, 0x00800000, 32768); - hose->sg_pci = iommu_arena_new(0xc0000000, 0x08000000, 32768); +#if 1 + /* ??? There's some bit of syncronization wrt writing new tlb + entries that's missing. Sometimes it works, sometimes invalid + tlb machine checks, sometimes hard lockup. And this just within + the boot sequence. + + I've tried extra memory barriers, extra alignment, pyxis + register reads, tlb flushes, and loopback tlb accesses. + + I guess the pyxis revision in the sx164 is just too buggy... */ + + hose->sg_isa = hose->sg_pci = NULL; + __direct_map_base = 0x40000000; + __direct_map_size = 0x80000000; + + *(vuip)PYXIS_W0_BASE = 0x40000000 | 1; + *(vuip)PYXIS_W0_MASK = (0x40000000 - 1) & 0xfff00000; + *(vuip)PYXIS_T0_BASE = 0; + + *(vuip)PYXIS_W1_BASE = 0x80000000 | 1; + *(vuip)PYXIS_W1_MASK = (0x40000000 - 1) & 0xfff00000; + *(vuip)PYXIS_T1_BASE = 0; + + *(vuip)PYXIS_W2_BASE = 0; + *(vuip)PYXIS_W3_BASE = 0; + + alpha_mv.mv_pci_tbi = NULL; + mb(); +#else + /* ??? NetBSD hints that page tables must be aligned to 32K, + possibly due to a hardware bug. This is over-aligned + from the 8K alignment one would expect for an 8MB window. + No description of what CIA revisions affected. */ + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0x08000); + hose->sg_pci = iommu_arena_new(hose, 0xc0000000, 0x08000000, 0x20000); __direct_map_base = 0x40000000; __direct_map_size = 0x80000000; @@ -553,36 +594,7 @@ pyxis_init_arch(void) pyxis_enable_broken_tbi(hose->sg_pci); alpha_mv.mv_pci_tbi(hose, 0, -1); - - /* - * Next, clear the PYXIS_CFG register, which gets used - * for PCI Config Space accesses. That is the way - * we want to use it, and we do not want to depend on - * what ARC or SRM might have left behind... - */ - temp = *(vuip)PYXIS_CFG; - if (temp != 0) { - *(vuip)PYXIS_CFG = 0; - mb(); - *(vuip)PYXIS_CFG; /* re-read to force write */ - } - - /* Zero the HAE. */ - *(vuip)PYXIS_HAE_MEM = 0U; mb(); - *(vuip)PYXIS_HAE_MEM; /* re-read to force write */ - *(vuip)PYXIS_HAE_IO = 0; mb(); - *(vuip)PYXIS_HAE_IO; /* re-read to force write */ - - /* - * Finally, check that the PYXIS_CTRL1 has IOA_BEN set for - * enabling byte/word PCI bus space(s) access. - */ - temp = *(vuip) PYXIS_CTRL1; - if (!(temp & 1)) { - *(vuip)PYXIS_CTRL1 = temp | 1; - mb(); - *(vuip)PYXIS_CTRL1; /* re-read */ - } +#endif } static inline void diff --git a/arch/alpha/kernel/core_tsunami.c b/arch/alpha/kernel/core_tsunami.c index 1452b6336..1fa5a8088 100644 --- a/arch/alpha/kernel/core_tsunami.c +++ b/arch/alpha/kernel/core_tsunami.c @@ -343,13 +343,9 @@ tsunami_init_one_pchip(tsunami_pchip *pchip, int index) * because of an idiot-syncrasy of the CYPRESS chip. It may * respond to a PCI bus address in the last 1MB of the 4GB * address range. - * - * Note that the TLB lookup logic uses bitwise concatenation, - * not addition, so the required arena alignment is based on - * the size of the window. */ - hose->sg_isa = iommu_arena_new(0x00800000, 0x00800000, 0x00800000>>10); - hose->sg_pci = iommu_arena_new(0xc0000000, 0x08000000, 0x08000000>>10); + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); + hose->sg_pci = iommu_arena_new(hose, 0xc0000000, 0x08000000, 0); __direct_map_base = 0x40000000; __direct_map_size = 0x80000000; diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index 613a633ba..bc8ca101a 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -17,304 +17,85 @@ #include <linux/kernel_stat.h> #include <linux/signal.h> #include <linux/sched.h> +#include <linux/interrupt.h> #include <linux/malloc.h> #include <linux/random.h> #include <linux/init.h> -#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/proc_fs.h> #include <asm/system.h> #include <asm/io.h> -#include <asm/dma.h> #include <asm/bitops.h> -#include <asm/machvec.h> - -#include "proto.h" -#include "irq_impl.h" - -#define vulp volatile unsigned long * -#define vuip volatile unsigned int * - -/* Only uniprocessor needs this IRQ/BH locking depth, on SMP it lives - in the per-cpu structure for cache reasons. */ -#ifndef CONFIG_SMP -int __local_irq_count; -int __local_bh_count; -unsigned long __irq_attempt[NR_IRQS]; -#endif - -#ifdef CONFIG_ALPHA_GENERIC -#define ACTUAL_NR_IRQS alpha_mv.nr_irqs -#else -#define ACTUAL_NR_IRQS NR_IRQS -#endif - -/* Hack minimum IPL during interupt processing for broken hardware. */ - -#ifdef CONFIG_ALPHA_BROKEN_IRQ_MASK -int __min_ipl; -#endif +#include <asm/uaccess.h> /* - * Performance counter hook. A module can override this to - * do something useful. + * Controller mappings for all interrupt sources: */ +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { + [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED} +}; -static void -dummy_perf(unsigned long vector, struct pt_regs *regs) -{ - printk(KERN_CRIT "Performance counter interrupt!\n"); -} - -void (*perf_irq)(unsigned long, struct pt_regs *) = dummy_perf; - -/* - * Dispatch device interrupts. - */ - -/* - * Handle ISA interrupt via the PICs. - */ - -#if defined(CONFIG_ALPHA_GENERIC) -# define IACK_SC alpha_mv.iack_sc -#elif defined(CONFIG_ALPHA_APECS) -# define IACK_SC APECS_IACK_SC -#elif defined(CONFIG_ALPHA_LCA) -# define IACK_SC LCA_IACK_SC -#elif defined(CONFIG_ALPHA_CIA) -# define IACK_SC CIA_IACK_SC -#elif defined(CONFIG_ALPHA_PYXIS) -# define IACK_SC PYXIS_IACK_SC -#elif defined(CONFIG_ALPHA_TSUNAMI) -# define IACK_SC TSUNAMI_IACK_SC -#elif defined(CONFIG_ALPHA_POLARIS) -# define IACK_SC POLARIS_IACK_SC -#elif defined(CONFIG_ALPHA_IRONGATE) -# define IACK_SC IRONGATE_IACK_SC -#endif - -#if defined(IACK_SC) -void -isa_device_interrupt(unsigned long vector, struct pt_regs *regs) -{ - /* - * Generate a PCI interrupt acknowledge cycle. The PIC will - * respond with the interrupt vector of the highest priority - * interrupt that is pending. The PALcode sets up the - * interrupts vectors such that irq level L generates vector L. - */ - int j = *(vuip) IACK_SC; - j &= 0xff; - if (j == 7) { - if (!(inb(0x20) & 0x80)) { - /* It's only a passive release... */ - return; - } - } - handle_irq(j, regs); -} -#endif -#if defined(CONFIG_ALPHA_GENERIC) || !defined(IACK_SC) -void -isa_no_iack_sc_device_interrupt(unsigned long vector, struct pt_regs *regs) -{ - unsigned long pic; - - /* - * It seems to me that the probability of two or more *device* - * interrupts occurring at almost exactly the same time is - * pretty low. So why pay the price of checking for - * additional interrupts here if the common case can be - * handled so much easier? - */ - /* - * The first read of gives you *all* interrupting lines. - * Therefore, read the mask register and and out those lines - * not enabled. Note that some documentation has 21 and a1 - * write only. This is not true. - */ - pic = inb(0x20) | (inb(0xA0) << 8); /* read isr */ - pic &= 0xFFFB; /* mask out cascade & hibits */ +static void register_irq_proc(unsigned int irq); - while (pic) { - int j = ffz(~pic); - pic &= pic - 1; - handle_irq(j, regs); - } -} -#endif +unsigned long irq_err_count; /* - * Handle interrupts from the SRM, assuming no additional weirdness. + * Special irq handlers. */ -static inline void -srm_enable_irq(unsigned int irq) -{ - cserve_ena(irq - 16); -} - -static void -srm_disable_irq(unsigned int irq) -{ - cserve_dis(irq - 16); -} - -static unsigned int -srm_startup_irq(unsigned int irq) -{ - srm_enable_irq(irq); - return 0; -} - -static struct hw_interrupt_type srm_irq_type = { - typename: "SRM", - startup: srm_startup_irq, - shutdown: srm_disable_irq, - enable: srm_enable_irq, - disable: srm_disable_irq, - ack: srm_disable_irq, - end: srm_enable_irq, -}; - -void -srm_device_interrupt(unsigned long vector, struct pt_regs * regs) -{ - int irq = (vector - 0x800) >> 4; - handle_irq(irq, regs); -} - -void __init -init_srm_irqs(long max, unsigned long ignore_mask) -{ - long i; - - for (i = 16; i < max; ++i) { - if (i < 64 && ((ignore_mask >> i) & 1)) - continue; - irq_desc[i].status = IRQ_DISABLED; - irq_desc[i].handler = &srm_irq_type; - } -} +void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } /* - * The not-handled irq handler. + * Generic no controller code */ -static void -noirq_enable_disable(unsigned int irq) -{ -} - -static unsigned int -noirq_startup(unsigned int irq) -{ - return 0; -} +static void no_irq_enable_disable(unsigned int irq) { } +static unsigned int no_irq_startup(unsigned int irq) { return 0; } static void -noirq_ack(unsigned int irq) +no_irq_ack(unsigned int irq) { - printk(KERN_CRIT "Unexpected IRQ %u\n", irq); + irq_err_count++; + printk(KERN_CRIT "Unexpected IRQ trap at vector %u\n", irq); } -static struct hw_interrupt_type no_irq_type = { +struct hw_interrupt_type no_irq_type = { typename: "none", - startup: noirq_startup, - shutdown: noirq_enable_disable, - enable: noirq_enable_disable, - disable: noirq_enable_disable, - ack: noirq_ack, - end: noirq_enable_disable, -}; - -/* - * The special RTC interrupt type. The interrupt itself was - * processed by PALcode, and comes in via entInt vector 1. - */ - -static struct hw_interrupt_type rtc_irq_type = { - typename: "RTC", - startup: noirq_startup, - shutdown: noirq_enable_disable, - enable: noirq_enable_disable, - disable: noirq_enable_disable, - ack: noirq_enable_disable, - end: noirq_enable_disable, -}; - -void __init -init_rtc_irq(void) -{ - irq_desc[RTC_IRQ].status = IRQ_DISABLED; - irq_desc[RTC_IRQ].handler = &rtc_irq_type; -} - -/* - * Special irq handlers. - */ - -void -no_action(int cpl, void *dev_id, struct pt_regs *regs) -{ -} - -/* - * Common irq handlers. - */ - -struct irqaction isa_cascade_irqaction = { - handler: no_action, - name: "isa-cascade" -}; - -struct irqaction timer_cascade_irqaction = { - handler: no_action, - name: "timer-cascade" -}; - -struct irqaction halt_switch_irqaction = { - handler: no_action, - name: "halt-switch" -}; - - -spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED; -irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { - [0 ... NR_IRQS-1] = { 0, &no_irq_type, } + startup: no_irq_startup, + shutdown: no_irq_enable_disable, + enable: no_irq_enable_disable, + disable: no_irq_enable_disable, + ack: no_irq_ack, + end: no_irq_enable_disable, }; int handle_IRQ_event(unsigned int irq, struct pt_regs *regs, struct irqaction *action) { - int status, cpu = smp_processor_id(); - int old_ipl, ipl; + int status; + int cpu = smp_processor_id(); kstat.irqs[cpu][irq]++; irq_enter(cpu, irq); status = 1; /* Force the "do bottom halves" bit */ - old_ipl = ipl = getipl(); do { - int new_ipl = IPL_MIN; - if (action->flags & SA_INTERRUPT) - new_ipl = IPL_MAX; - if (new_ipl != ipl) { - setipl(new_ipl); - ipl = new_ipl; - } + if (!(action->flags & SA_INTERRUPT)) + __sti(); + else + __cli(); status |= action->flags; action->handler(irq, action->dev_id, regs); action = action->next; } while (action); - if (ipl != old_ipl) - setipl(old_ipl); - if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); + __cli(); + irq_exit(cpu, irq); return status; @@ -326,17 +107,18 @@ handle_IRQ_event(unsigned int irq, struct pt_regs *regs, * hardware disable after having gotten the irq * controller lock. */ -void +void inline disable_irq_nosync(unsigned int irq) { + irq_desc_t *desc = irq_desc + irq; unsigned long flags; - spin_lock_irqsave(&irq_controller_lock, flags); - if (!irq_desc[irq].depth++) { - irq_desc[irq].status |= IRQ_DISABLED | IRQ_MASKED; - irq_desc[irq].handler->disable(irq); + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); } - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(&desc->lock, flags); } /* @@ -358,32 +140,29 @@ disable_irq(unsigned int irq) void enable_irq(unsigned int irq) { + irq_desc_t *desc = irq_desc + irq; unsigned long flags; - spin_lock_irqsave(&irq_controller_lock, flags); - switch (irq_desc[irq].depth) { - case 1: - { - unsigned int status = irq_desc[irq].status; - - status &= ~(IRQ_DISABLED | IRQ_MASKED); + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { - status |= IRQ_REPLAY; - /* ??? We can't re-send on (most?) alpha hw. - hw_resend_irq(irq_desc[irq].handler,irq); */ + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); } - irq_desc[irq].status = status; - irq_desc[irq].handler->enable(irq); + desc->handler->enable(irq); /* fall-through */ - } + } default: - irq_desc[irq].depth--; + desc->depth--; break; case 0: printk(KERN_ERR "enable_irq() unbalanced from %p\n", __builtin_return_address(0)); } - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(&desc->lock, flags); } int @@ -392,6 +171,7 @@ setup_irq(unsigned int irq, struct irqaction * new) int shared = 0; struct irqaction *old, **p; unsigned long flags; + irq_desc_t *desc = irq_desc + irq; /* * Some drivers like serial.c use request_irq() heavily, @@ -413,12 +193,12 @@ setup_irq(unsigned int irq, struct irqaction * new) /* * The following block of code has to be executed atomically */ - spin_lock_irqsave(&irq_controller_lock,flags); - p = &irq_desc[irq].action; + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ if (!(old->flags & new->flags & SA_SHIRQ)) { - spin_unlock_irqrestore(&irq_controller_lock,flags); + spin_unlock_irqrestore(&desc->lock,flags); return -EBUSY; } @@ -433,14 +213,181 @@ setup_irq(unsigned int irq, struct irqaction * new) *p = new; if (!shared) { - irq_desc[irq].depth = 0; - irq_desc[irq].status &= ~(IRQ_DISABLED | IRQ_MASKED); - irq_desc[irq].handler->startup(irq); + desc->depth = 0; + desc->status &= ~IRQ_DISABLED; + desc->handler->startup(irq); } - spin_unlock_irqrestore(&irq_controller_lock,flags); + spin_unlock_irqrestore(&desc->lock,flags); + + register_irq_proc(irq); return 0; } +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir [NR_IRQS]; +static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; + +static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; + +#define HEX_DIGITS 16 + +static int +irq_affinity_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%016lx\n", irq_affinity[(long)data]); +} + +static unsigned int +parse_hex_value (const char *buffer, + unsigned long count, unsigned long *ret) +{ + unsigned char hexnum [HEX_DIGITS]; + unsigned long value; + int i; + + if (!count) + return -EINVAL; + if (count > HEX_DIGITS) + count = HEX_DIGITS; + if (copy_from_user(hexnum, buffer, count)) + return -EFAULT; + + /* + * Parse the first 8 characters as a hex string, any non-hex char + * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same. + */ + value = 0; + + for (i = 0; i < count; i++) { + unsigned int c = hexnum[i]; + + switch (c) { + case '0' ... '9': c -= '0'; break; + case 'a' ... 'f': c -= 'a'-10; break; + case 'A' ... 'F': c -= 'A'-10; break; + default: + goto out; + } + value = (value << 4) | c; + } +out: + *ret = value; + return 0; +} + +static int +irq_affinity_write_proc(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int irq = (long) data, full_count = count, err; + unsigned long new_value; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = parse_hex_value(buffer, count, &new_value); + +#if CONFIG_SMP + /* + * Do not allow disabling IRQs completely - it's a too easy + * way to make the system unusable accidentally :-) At least + * one online CPU still has to be targeted. + */ + if (!(new_value & cpu_present_mask)) + return -EINVAL; +#endif + + irq_affinity[irq] = new_value; + irq_desc[irq].handler->set_affinity(irq, new_value); + + return full_count; +} + +static int +prof_cpu_mask_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long *mask = (unsigned long *) data; + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", *mask); +} + +static int +prof_cpu_mask_write_proc(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned long *mask = (unsigned long *) data, full_count = count, err; + unsigned long new_value; + + err = parse_hex_value(buffer, count, &new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +#define MAX_NAMELEN 10 + +static void +register_irq_proc (unsigned int irq) +{ + struct proc_dir_entry *entry; + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type)) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0700, irq_dir[irq]); + + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + + smp_affinity_entry[irq] = entry; +} + +unsigned long prof_cpu_mask = ~0UL; + +void +init_irq_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", 0); + + /* create /proc/irq/prof_cpu_mask */ + entry = create_proc_entry("prof_cpu_mask", 0700, root_irq_dir); + + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < NR_IRQS; i++) { + if (irq_desc[i].handler == &no_irq_type) + continue; + register_irq_proc(i); + } +} + int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char * devname, void *dev_id) @@ -488,6 +435,7 @@ request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), void free_irq(unsigned int irq, void *dev_id) { + irq_desc_t *desc; struct irqaction **p; unsigned long flags; @@ -496,8 +444,9 @@ free_irq(unsigned int irq, void *dev_id) return; } - spin_lock_irqsave(&irq_controller_lock,flags); - p = &irq_desc[irq].action; + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; for (;;) { struct irqaction * action = *p; if (action) { @@ -508,26 +457,29 @@ free_irq(unsigned int irq, void *dev_id) /* Found - now remove it from the list of entries. */ *pp = action->next; - if (!irq_desc[irq].action) { - irq_desc[irq].status |= IRQ_DISABLED|IRQ_MASKED; - irq_desc[irq].handler->shutdown(irq); + if (!desc->action) { + desc->status |= IRQ_DISABLED; + desc->handler->shutdown(irq); } - spin_unlock_irqrestore(&irq_controller_lock,flags); + spin_unlock_irqrestore(&desc->lock,flags); +#ifdef CONFIG_SMP /* Wait to make sure it's not being used on another CPU. */ - while (irq_desc[irq].status & IRQ_INPROGRESS) + while (desc->status & IRQ_INPROGRESS) barrier(); +#endif kfree(action); return; } printk(KERN_ERR "Trying to free free IRQ%d\n",irq); - spin_unlock_irqrestore(&irq_controller_lock,flags); + spin_unlock_irqrestore(&desc->lock,flags); return; } } -int get_irq_list(char *buf) +int +get_irq_list(char *buf) { int i, j; struct irqaction * action; @@ -576,246 +528,10 @@ int get_irq_list(char *buf) cpu_data[cpu_logical_map(j)].smp_local_irq_count); p += sprintf(p, "\n"); #endif + p += sprintf(p, "ERR: %10lu\n", irq_err_count); return p - buf; } -#ifdef CONFIG_SMP -/* Who has global_irq_lock. */ -int global_irq_holder = NO_PROC_ID; - -/* This protects IRQ's. */ -spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED; - -/* Global IRQ locking depth. */ -atomic_t global_irq_count = ATOMIC_INIT(0); - -static void *previous_irqholder = NULL; - -#define MAXCOUNT 100000000 - -static void show(char * str, void *where); - -static inline void -wait_on_irq(int cpu, void *where) -{ - int count = MAXCOUNT; - - for (;;) { - - /* - * Wait until all interrupts are gone. Wait - * for bottom half handlers unless we're - * already executing in one.. - */ - if (!atomic_read(&global_irq_count)) { - if (local_bh_count(cpu) - || !spin_is_locked(&global_bh_lock)) - break; - } - - /* Duh, we have to loop. Release the lock to avoid deadlocks */ - spin_unlock(&global_irq_lock); - - for (;;) { - if (!--count) { - show("wait_on_irq", where); - count = MAXCOUNT; - } - __sti(); - udelay(1); /* make sure to run pending irqs */ - __cli(); - - if (atomic_read(&global_irq_count)) - continue; - if (spin_is_locked(&global_irq_lock)) - continue; - if (!local_bh_count(cpu) - && spin_is_locked(&global_bh_lock)) - continue; - if (spin_trylock(&global_irq_lock)) - break; - } - } -} - -static inline void -get_irqlock(int cpu, void* where) -{ - if (!spin_trylock(&global_irq_lock)) { - /* Do we already hold the lock? */ - if (cpu == global_irq_holder) - return; - /* Uhhuh.. Somebody else got it. Wait. */ - spin_lock(&global_irq_lock); - } - - /* - * Ok, we got the lock bit. - * But that's actually just the easy part.. Now - * we need to make sure that nobody else is running - * in an interrupt context. - */ - wait_on_irq(cpu, where); - - /* - * Finally. - */ -#if DEBUG_SPINLOCK - global_irq_lock.task = current; - global_irq_lock.previous = where; -#endif - global_irq_holder = cpu; - previous_irqholder = where; -} - -void -__global_cli(void) -{ - int cpu = smp_processor_id(); - void *where = __builtin_return_address(0); - - /* - * Maximize ipl. If ipl was previously 0 and if this thread - * is not in an irq, then take global_irq_lock. - */ - if (swpipl(IPL_MAX) == IPL_MIN && !local_irq_count(cpu)) - get_irqlock(cpu, where); -} - -void -__global_sti(void) -{ - int cpu = smp_processor_id(); - - if (!local_irq_count(cpu)) - release_irqlock(cpu); - __sti(); -} - -/* - * SMP flags value to restore to: - * 0 - global cli - * 1 - global sti - * 2 - local cli - * 3 - local sti - */ -unsigned long -__global_save_flags(void) -{ - int retval; - int local_enabled; - unsigned long flags; - int cpu = smp_processor_id(); - - __save_flags(flags); - local_enabled = (!(flags & 7)); - /* default to local */ - retval = 2 + local_enabled; - - /* Check for global flags if we're not in an interrupt. */ - if (!local_irq_count(cpu)) { - if (local_enabled) - retval = 1; - if (global_irq_holder == cpu) - retval = 0; - } - return retval; -} - -void -__global_restore_flags(unsigned long flags) -{ - switch (flags) { - case 0: - __global_cli(); - break; - case 1: - __global_sti(); - break; - case 2: - __cli(); - break; - case 3: - __sti(); - break; - default: - printk(KERN_ERR "global_restore_flags: %08lx (%p)\n", - flags, __builtin_return_address(0)); - } -} - -static void -show(char * str, void *where) -{ -#if 0 - int i; - unsigned long *stack; -#endif - int cpu = smp_processor_id(); - - printk("\n%s, CPU %d: %p\n", str, cpu, where); - printk("irq: %d [%d %d]\n", - atomic_read(&global_irq_count), - cpu_data[0].irq_count, - cpu_data[1].irq_count); - - printk("bh: %d [%d %d]\n", - spin_is_locked(&global_bh_lock) ? 1 : 0, - cpu_data[0].bh_count, - cpu_data[1].bh_count); -#if 0 - stack = (unsigned long *) &str; - for (i = 40; i ; i--) { - unsigned long x = *++stack; - if (x > (unsigned long) &init_task_union && - x < (unsigned long) &vsprintf) { - printk("<[%08lx]> ", x); - } - } -#endif -} - -/* - * From its use, I infer that synchronize_irq() stalls a thread until - * the effects of a command to an external device are known to have - * taken hold. Typically, the command is to stop sending interrupts. - * The strategy here is wait until there is at most one processor - * (this one) in an irq. The memory barrier serializes the write to - * the device and the subsequent accesses of global_irq_count. - * --jmartin - */ -#define DEBUG_SYNCHRONIZE_IRQ 0 - -void -synchronize_irq(void) -{ -#if 0 - /* Joe's version. */ - int cpu = smp_processor_id(); - int local_count; - int global_count; - int countdown = 1<<24; - void *where = __builtin_return_address(0); - - mb(); - do { - local_count = local_irq_count(cpu); - global_count = atomic_read(&global_irq_count); - if (DEBUG_SYNCHRONIZE_IRQ && (--countdown == 0)) { - printk("%d:%d/%d\n", cpu, local_count, global_count); - show("synchronize_irq", where); - break; - } - } while (global_count != local_count); -#else - /* Jay's version. */ - if (atomic_read(&global_irq_count)) { - cli(); - sti(); - } -#endif -} -#endif /* CONFIG_SMP */ /* * do_IRQ handles all normal device IRQ's (the special @@ -836,39 +552,26 @@ handle_irq(int irq, struct pt_regs * regs) * handled by some other CPU. (or is disabled) */ int cpu = smp_processor_id(); - irq_desc_t *desc; + irq_desc_t *desc = irq_desc + irq; struct irqaction * action; unsigned int status; if ((unsigned) irq > ACTUAL_NR_IRQS) { + irq_err_count++; printk(KERN_CRIT "device_interrupt: illegal interrupt %d\n", irq); return; } irq_attempt(cpu, irq)++; - desc = irq_desc + irq; - spin_lock_irq(&irq_controller_lock); /* mask also the RTC */ + spin_lock_irq(&desc->lock); /* mask also the higher prio events */ desc->handler->ack(irq); - status = desc->status; - -#ifndef CONFIG_SMP - /* Look for broken irq masking. */ - if (status & IRQ_MASKED) { - static unsigned long last_printed; - if (time_after(jiffies, last_printed+HZ)) { - printk(KERN_CRIT "Mask didn't work for irq %d!\n", irq); - last_printed = jiffies; - } - } -#endif - /* * REPLAY is when Linux resends an IRQ that was dropped earlier. * WAITING is used by probe to mark irqs that are being tested. */ - status &= ~(IRQ_REPLAY | IRQ_WAITING); - status |= IRQ_PENDING | IRQ_MASKED; /* we _want_ to handle it */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + status |= IRQ_PENDING; /* we _want_ to handle it */ /* * If the IRQ is disabled for whatever reason, we cannot @@ -881,7 +584,6 @@ handle_irq(int irq, struct pt_regs * regs) status |= IRQ_INPROGRESS; /* we are handling it */ } desc->status = status; - spin_unlock(&irq_controller_lock); /* * If there is no IRQ handler or it was disabled, exit early. @@ -890,7 +592,7 @@ handle_irq(int irq, struct pt_regs * regs) * will take care of it. */ if (!action) - return; + goto out; /* * Edge triggered interrupts need to remember pending events. @@ -902,22 +604,23 @@ handle_irq(int irq, struct pt_regs * regs) * SMP environment. */ for (;;) { + spin_unlock(&desc->lock); handle_IRQ_event(irq, regs, action); - spin_lock(&irq_controller_lock); + spin_lock(&desc->lock); if (!(desc->status & IRQ_PENDING) || (desc->status & IRQ_LEVEL)) break; desc->status &= ~IRQ_PENDING; - spin_unlock(&irq_controller_lock); - } - status = desc->status & ~IRQ_INPROGRESS; - if (!(status & IRQ_DISABLED)) { - status &= ~IRQ_MASKED; - desc->handler->end(irq); } - desc->status = status; - spin_unlock(&irq_controller_lock); + desc->status &= ~IRQ_INPROGRESS; +out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + desc->handler->end(irq); + spin_unlock(&desc->lock); } /* @@ -932,17 +635,20 @@ unsigned long probe_irq_on(void) { int i; + irq_desc_t *desc; unsigned long delay; unsigned long val; /* Something may have generated an irq long ago and we want to flush such a longstanding irq before considering it as spurious. */ - spin_lock_irq(&irq_controller_lock); - for (i = NR_IRQS-1; i >= 0; i--) + for (i = NR_IRQS-1; i >= 0; i--) { + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); if (!irq_desc[i].action) - if(irq_desc[i].handler->startup(i)) - irq_desc[i].status |= IRQ_PENDING; - spin_unlock_irq(&irq_controller_lock); + irq_desc[i].handler->startup(i); + spin_unlock_irq(&desc->lock); + } /* Wait for longstanding interrupts to trigger. */ for (delay = jiffies + HZ/50; time_after(delay, jiffies); ) @@ -951,15 +657,17 @@ probe_irq_on(void) /* enable any unassigned irqs (we must startup again here because if a longstanding irq happened in the previous stage, it may have masked itself) first, enable any unassigned irqs. */ - spin_lock_irq(&irq_controller_lock); for (i = NR_IRQS-1; i >= 0; i--) { - if (!irq_desc[i].action) { - irq_desc[i].status |= IRQ_AUTODETECT | IRQ_WAITING; - if(irq_desc[i].handler->startup(i)) - irq_desc[i].status |= IRQ_PENDING; + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); + if (!desc->action) { + desc->status |= IRQ_AUTODETECT | IRQ_WAITING; + if (desc->handler->startup(i)) + desc->status |= IRQ_PENDING; } + spin_unlock_irq(&desc->lock); } - spin_unlock_irq(&irq_controller_lock); /* * Wait for spurious interrupts to trigger @@ -971,24 +679,24 @@ probe_irq_on(void) * Now filter out any obviously spurious interrupts */ val = 0; - spin_lock_irq(&irq_controller_lock); for (i=0; i<NR_IRQS; i++) { - unsigned int status = irq_desc[i].status; - - if (!(status & IRQ_AUTODETECT)) - continue; - - /* It triggered already - consider it spurious. */ - if (!(status & IRQ_WAITING)) { - irq_desc[i].status = status & ~IRQ_AUTODETECT; - irq_desc[i].handler->shutdown(i); - continue; + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + /* It triggered already - consider it spurious. */ + if (!(status & IRQ_WAITING)) { + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } else + if (i < 32) + val |= 1 << i; } - - if (i < 64) - val |= 1 << i; + spin_unlock_irq(&desc->lock); } - spin_unlock_irq(&irq_controller_lock); return val; } @@ -997,26 +705,29 @@ probe_irq_on(void) * Return a mask of triggered interrupts (this * can handle only legacy ISA interrupts). */ -unsigned int probe_irq_mask(unsigned long val) +unsigned int +probe_irq_mask(unsigned long val) { int i; unsigned int mask; mask = 0; - spin_lock_irq(&irq_controller_lock); for (i = 0; i < 16; i++) { - unsigned int status = irq_desc[i].status; + irq_desc_t *desc = irq_desc + i; + unsigned int status; - if (!(status & IRQ_AUTODETECT)) - continue; + spin_lock_irq(&desc->lock); + status = desc->status; - if (!(status & IRQ_WAITING)) - mask |= 1 << i; + if (status & IRQ_AUTODETECT) { + if (!(status & IRQ_WAITING)) + mask |= 1 << i; - irq_desc[i].status = status & ~IRQ_AUTODETECT; - irq_desc[i].handler->shutdown(i); + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } + spin_unlock_irq(&desc->lock); } - spin_unlock_irq(&irq_controller_lock); return mask & val; } @@ -1034,183 +745,26 @@ probe_irq_off(unsigned long val) nr_irqs = 0; irq_found = 0; - spin_lock_irq(&irq_controller_lock); for (i=0; i<NR_IRQS; i++) { - unsigned int status = irq_desc[i].status; + irq_desc_t *desc = irq_desc + i; + unsigned int status; - if (!(status & IRQ_AUTODETECT)) - continue; + spin_lock_irq(&desc->lock); + status = desc->status; - if (!(status & IRQ_WAITING)) { - if (!nr_irqs) - irq_found = i; - nr_irqs++; + if (status & IRQ_AUTODETECT) { + if (!(status & IRQ_WAITING)) { + if (!nr_irqs) + irq_found = i; + nr_irqs++; + } + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); } - irq_desc[i].status = status & ~IRQ_AUTODETECT; - irq_desc[i].handler->shutdown(i); + spin_unlock_irq(&desc->lock); } - spin_unlock_irq(&irq_controller_lock); if (nr_irqs > 1) irq_found = -irq_found; return irq_found; } - - -/* - * The main interrupt entry point. - */ - -asmlinkage void -do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr, - unsigned long a3, unsigned long a4, unsigned long a5, - struct pt_regs regs) -{ - switch (type) { - case 0: -#ifdef CONFIG_SMP - handle_ipi(®s); - return; -#else - printk(KERN_CRIT "Interprocessor interrupt? " - "You must be kidding!\n"); -#endif - break; - case 1: -#ifdef CONFIG_SMP - cpu_data[smp_processor_id()].smp_local_irq_count++; - smp_percpu_timer_interrupt(®s); - if (smp_processor_id() == boot_cpuid) -#endif - handle_irq(RTC_IRQ, ®s); - return; - case 2: - alpha_mv.machine_check(vector, la_ptr, ®s); - return; - case 3: - alpha_mv.device_interrupt(vector, ®s); - return; - case 4: - perf_irq(vector, ®s); - return; - default: - printk(KERN_CRIT "Hardware intr %ld %lx? Huh?\n", - type, vector); - } - printk("PC = %016lx PS=%04lx\n", regs.pc, regs.ps); -} - -void __init -common_init_isa_dma(void) -{ - outb(0, DMA1_RESET_REG); - outb(0, DMA2_RESET_REG); - outb(0, DMA1_CLR_MASK_REG); - outb(0, DMA2_CLR_MASK_REG); -} - -void __init -init_IRQ(void) -{ - wrent(entInt, 0); - alpha_mv.init_irq(); -} - - -/* - */ -#define MCHK_K_TPERR 0x0080 -#define MCHK_K_TCPERR 0x0082 -#define MCHK_K_HERR 0x0084 -#define MCHK_K_ECC_C 0x0086 -#define MCHK_K_ECC_NC 0x0088 -#define MCHK_K_OS_BUGCHECK 0x008A -#define MCHK_K_PAL_BUGCHECK 0x0090 - -#ifndef CONFIG_SMP -struct mcheck_info __mcheck_info; -#endif - -void -process_mcheck_info(unsigned long vector, unsigned long la_ptr, - struct pt_regs *regs, const char *machine, - int expected) -{ - struct el_common *mchk_header; - const char *reason; - - /* - * See if the machine check is due to a badaddr() and if so, - * ignore it. - */ - -#if DEBUG_MCHECK > 0 - printk(KERN_CRIT "%s machine check %s\n", machine, - expected ? "expected." : "NOT expected!!!"); -#endif - - if (expected) { - int cpu = smp_processor_id(); - mcheck_expected(cpu) = 0; - mcheck_taken(cpu) = 1; - return; - } - - mchk_header = (struct el_common *)la_ptr; - - printk(KERN_CRIT "%s machine check: vector=0x%lx pc=0x%lx code=0x%lx\n", - machine, vector, regs->pc, mchk_header->code); - - switch ((unsigned int) mchk_header->code) { - /* Machine check reasons. Defined according to PALcode sources. */ - case 0x80: reason = "tag parity error"; break; - case 0x82: reason = "tag control parity error"; break; - case 0x84: reason = "generic hard error"; break; - case 0x86: reason = "correctable ECC error"; break; - case 0x88: reason = "uncorrectable ECC error"; break; - case 0x8A: reason = "OS-specific PAL bugcheck"; break; - case 0x90: reason = "callsys in kernel mode"; break; - case 0x96: reason = "i-cache read retryable error"; break; - case 0x98: reason = "processor detected hard error"; break; - - /* System specific (these are for Alcor, at least): */ - case 0x202: reason = "system detected hard error"; break; - case 0x203: reason = "system detected uncorrectable ECC error"; break; - case 0x204: reason = "SIO SERR occurred on PCI bus"; break; - case 0x205: reason = "parity error detected by CIA"; break; - case 0x206: reason = "SIO IOCHK occurred on ISA bus"; break; - case 0x207: reason = "non-existent memory error"; break; - case 0x208: reason = "MCHK_K_DCSR"; break; - case 0x209: reason = "PCI SERR detected"; break; - case 0x20b: reason = "PCI data parity error detected"; break; - case 0x20d: reason = "PCI address parity error detected"; break; - case 0x20f: reason = "PCI master abort error"; break; - case 0x211: reason = "PCI target abort error"; break; - case 0x213: reason = "scatter/gather PTE invalid error"; break; - case 0x215: reason = "flash ROM write error"; break; - case 0x217: reason = "IOA timeout detected"; break; - case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break; - case 0x21b: reason = "EISA fail-safe timer timeout"; break; - case 0x21d: reason = "EISA bus time-out"; break; - case 0x21f: reason = "EISA software generated NMI"; break; - case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break; - default: reason = "unknown"; break; - } - - printk(KERN_CRIT "machine check type: %s%s\n", - reason, mchk_header->retry ? " (retryable)" : ""); - - dik_show_regs(regs, NULL); - -#if DEBUG_MCHECK > 1 - { - /* Dump the logout area to give all info. */ - unsigned long *ptr = (unsigned long *)la_ptr; - long i; - for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { - printk(KERN_CRIT " +%8lx %016lx %016lx\n", - i*sizeof(long), ptr[i], ptr[i+1]); - } - } -#endif -} diff --git a/arch/alpha/kernel/irq_impl.h b/arch/alpha/kernel/irq_impl.h index ff8067a00..9818476f2 100644 --- a/arch/alpha/kernel/irq_impl.h +++ b/arch/alpha/kernel/irq_impl.h @@ -19,6 +19,7 @@ extern void isa_no_iack_sc_device_interrupt(unsigned long, struct pt_regs *); extern void srm_device_interrupt(unsigned long, struct pt_regs *); extern void pyxis_device_interrupt(unsigned long, struct pt_regs *); +extern struct irqaction timer_irqaction; extern struct irqaction isa_cascade_irqaction; extern struct irqaction timer_cascade_irqaction; extern struct irqaction halt_switch_irqaction; @@ -33,27 +34,37 @@ extern void i8259a_enable_irq(unsigned int); extern void i8259a_disable_irq(unsigned int); extern void i8259a_mask_and_ack_irq(unsigned int); extern unsigned int i8259a_startup_irq(unsigned int); +extern void i8259a_end_irq(unsigned int); extern struct hw_interrupt_type i8259a_irq_type; extern void init_i8259a_irqs(void); -extern void no_action(int cpl, void *dev_id, struct pt_regs *regs); extern void handle_irq(int irq, struct pt_regs * regs); +extern unsigned long prof_cpu_mask; + static inline void alpha_do_profile(unsigned long pc) { - if (prof_buffer && current->pid) { - extern char _stext; - - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - /* - * Don't ignore out-of-bounds PC values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (pc > prof_len - 1) - pc = prof_len - 1; - atomic_inc((atomic_t *)&prof_buffer[pc]); - } + extern char _stext; + + if (!prof_buffer) + return; + + /* + * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. + * (default is all CPUs.) + */ + if (!((1<<smp_processor_id()) & prof_cpu_mask)) + return; + + pc -= (unsigned long) &_stext; + pc >>= prof_shift; + /* + * Don't ignore out-of-bounds PC values silently, + * put them into the last histogram slot, so if + * present, they will show up as a sharp peak. + */ + if (pc > prof_len - 1) + pc = prof_len - 1; + atomic_inc((atomic_t *)&prof_buffer[pc]); } diff --git a/arch/alpha/kernel/machvec_impl.h b/arch/alpha/kernel/machvec_impl.h index 421591104..cc55781cb 100644 --- a/arch/alpha/kernel/machvec_impl.h +++ b/arch/alpha/kernel/machvec_impl.h @@ -44,7 +44,6 @@ mv_switch_mm: ev4_switch_mm, \ mv_activate_mm: ev4_activate_mm, \ mv_flush_tlb_current: ev4_flush_tlb_current, \ - mv_flush_tlb_other: ev4_flush_tlb_other, \ mv_flush_tlb_current_page: ev4_flush_tlb_current_page #define DO_EV5_MMU \ @@ -52,7 +51,6 @@ mv_switch_mm: ev5_switch_mm, \ mv_activate_mm: ev5_activate_mm, \ mv_flush_tlb_current: ev5_flush_tlb_current, \ - mv_flush_tlb_other: ev5_flush_tlb_other, \ mv_flush_tlb_current_page: ev5_flush_tlb_current_page #define DO_EV6_MMU \ @@ -60,7 +58,6 @@ mv_switch_mm: ev5_switch_mm, \ mv_activate_mm: ev5_activate_mm, \ mv_flush_tlb_current: ev5_flush_tlb_current, \ - mv_flush_tlb_other: ev5_flush_tlb_other, \ mv_flush_tlb_current_page: ev5_flush_tlb_current_page #define IO_LITE(UP,low) \ diff --git a/arch/alpha/kernel/pci_impl.h b/arch/alpha/kernel/pci_impl.h index f91978732..3aee6ec34 100644 --- a/arch/alpha/kernel/pci_impl.h +++ b/arch/alpha/kernel/pci_impl.h @@ -123,6 +123,24 @@ static inline u8 bridge_swizzle(u8 pin, u8 slot) _ctl_; }) +/* A PCI IOMMU allocation arena. There are typically two of these + regions per bus. */ +/* ??? The 8400 has a 32-byte pte entry, and the entire table apparently + lives directly on the host bridge (no tlb?). We don't support this + machine, but if we ever did, we'd need to parameterize all this quite + a bit further. Probably with per-bus operation tables. */ + +struct pci_iommu_arena +{ + spinlock_t lock; + struct pci_controler *hose; + unsigned long *ptes; + dma_addr_t dma_base; + unsigned int size; + unsigned int next_entry; +}; + + /* The hose list. */ extern struct pci_controler *hose_head, **hose_tail; extern struct pci_controler *pci_isa_hose; @@ -132,8 +150,9 @@ extern u8 common_swizzle(struct pci_dev *, u8 *); extern struct pci_controler *alloc_pci_controler(void); extern struct resource *alloc_resource(void); -extern struct pci_iommu_arena *iommu_arena_new(dma_addr_t, unsigned long, - unsigned long); +extern struct pci_iommu_arena *iommu_arena_new(struct pci_controler *, + dma_addr_t, unsigned long, + unsigned long); extern long iommu_arena_alloc(struct pci_iommu_arena *arena, long n); extern const char *const pci_io_names[]; diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c index f5a9bd990..9e7e13e9b 100644 --- a/arch/alpha/kernel/pci_iommu.c +++ b/arch/alpha/kernel/pci_iommu.c @@ -27,6 +27,8 @@ # define DBGA2(args...) #endif +#define DEBUG_NODIRECT 0 + static inline unsigned long mk_iommu_pte(unsigned long paddr) @@ -41,23 +43,29 @@ calc_npages(long bytes) } struct pci_iommu_arena * -iommu_arena_new(dma_addr_t base, unsigned long window_size, - unsigned long align) +iommu_arena_new(struct pci_controler *hose, dma_addr_t base, + unsigned long window_size, unsigned long align) { - unsigned long entries, mem_size, mem_pages; + unsigned long mem_size; struct pci_iommu_arena *arena; - entries = window_size >> PAGE_SHIFT; - mem_size = entries * sizeof(unsigned long); - mem_pages = calc_npages(mem_size); + mem_size = window_size / (PAGE_SIZE / sizeof(unsigned long)); + + /* Note that the TLB lookup logic uses bitwise concatenation, + not addition, so the required arena alignment is based on + the size of the window. Retain the align parameter so that + particular systems can over-align the arena. */ + if (align < mem_size) + align = mem_size; arena = alloc_bootmem(sizeof(*arena)); - arena->ptes = __alloc_bootmem(mem_pages * PAGE_SIZE, align, 0); + arena->ptes = __alloc_bootmem(mem_size, align, 0); spin_lock_init(&arena->lock); + arena->hose = hose; arena->dma_base = base; arena->size = window_size; - arena->alloc_hint = 0; + arena->next_entry = 0; return arena; } @@ -74,20 +82,22 @@ iommu_arena_alloc(struct pci_iommu_arena *arena, long n) /* Search forward for the first sequence of N empty ptes. */ beg = arena->ptes; end = beg + (arena->size >> PAGE_SHIFT); - p = beg + arena->alloc_hint; + p = beg + arena->next_entry; i = 0; while (i < n && p < end) i = (*p++ == 0 ? i + 1 : 0); - if (p >= end) { - /* Failure. Assume the hint was wrong and go back to + if (i < n) { + /* Reached the end. Flush the TLB and restart the search from the beginning. */ + alpha_mv.mv_pci_tbi(arena->hose, 0, -1); + p = beg; i = 0; while (i < n && p < end) i = (*p++ == 0 ? i + 1 : 0); - if (p >= end) { + if (i < n) { spin_unlock_irqrestore(&arena->lock, flags); return -1; } @@ -100,7 +110,7 @@ iommu_arena_alloc(struct pci_iommu_arena *arena, long n) for (p = p - n, i = 0; i < n; ++i) p[i] = ~1UL; - arena->alloc_hint = p - beg + n; + arena->next_entry = p - beg + n; spin_unlock_irqrestore(&arena->lock, flags); return p - beg; @@ -115,7 +125,6 @@ iommu_arena_free(struct pci_iommu_arena *arena, long ofs, long n) p = arena->ptes + ofs; for (i = 0; i < n; ++i) p[i] = 0; - arena->alloc_hint = ofs; } /* Map a single buffer of the indicate size for PCI DMA in streaming @@ -138,6 +147,7 @@ pci_map_single(struct pci_dev *pdev, void *cpu_addr, long size, int direction) paddr = virt_to_phys(cpu_addr); +#if !DEBUG_NODIRECT /* First check to see if we can use the direct map window. */ if (paddr + size + __direct_map_base - 1 <= max_dma && paddr + size <= __direct_map_size) { @@ -148,6 +158,7 @@ pci_map_single(struct pci_dev *pdev, void *cpu_addr, long size, int direction) return ret; } +#endif /* If the machine doesn't define a pci_tbi routine, we have to assume it doesn't support sg mapping. */ @@ -199,6 +210,7 @@ pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, long size, if (direction == PCI_DMA_NONE) BUG(); +#if !DEBUG_NODIRECT if (dma_addr >= __direct_map_base && dma_addr < __direct_map_base + __direct_map_size) { /* Nothing to do. */ @@ -208,6 +220,7 @@ pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, long size, return; } +#endif arena = hose->sg_pci; if (!arena || dma_addr < arena->dma_base) @@ -224,10 +237,9 @@ pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, long size, npages = calc_npages((dma_addr & ~PAGE_MASK) + size); iommu_arena_free(arena, dma_ofs, npages); - alpha_mv.mv_pci_tbi(hose, dma_addr, dma_addr + size - 1); - DBGA2("pci_unmap_single: sg [%x,%lx] np %ld from %p\n", - dma_addr, size, npages, __builtin_return_address(0)); + DBGA("pci_unmap_single: sg [%x,%lx] np %ld from %p\n", + dma_addr, size, npages, __builtin_return_address(0)); } @@ -347,6 +359,7 @@ sg_fill(struct scatterlist *leader, struct scatterlist *end, unsigned long *ptes; long npages, dma_ofs, i; +#if !DEBUG_NODIRECT /* If everything is physically contiguous, and the addresses fall into the direct-map window, use it. */ if (leader->dma_address == 0 @@ -360,6 +373,7 @@ sg_fill(struct scatterlist *leader, struct scatterlist *end, return 0; } +#endif /* Otherwise, we'll use the iommu to make the pages virtually contiguous. */ @@ -376,56 +390,38 @@ sg_fill(struct scatterlist *leader, struct scatterlist *end, DBGA(" sg_fill: [%p,%lx] -> sg %x np %ld\n", leader->address, size, out->dma_address, npages); + /* All virtually contiguous. We need to find the length of each + physically contiguous subsegment to fill in the ptes. */ ptes = &arena->ptes[dma_ofs]; sg = leader; - if (0 && leader->dma_address == 0) { - /* All physically contiguous. We already have the - length, all we need is to fill in the ptes. */ - - paddr = virt_to_phys(sg->address) & PAGE_MASK; - for (i = 0; i < npages; ++i, paddr += PAGE_SIZE) - *ptes++ = mk_iommu_pte(paddr); - -#if DEBUG_ALLOC > 0 - DBGA(" (0) [%p,%x] np %ld\n", - sg->address, sg->length, npages); - for (++sg; sg < end && (int) sg->dma_address < 0; ++sg) - DBGA(" (%ld) [%p,%x] cont\n", - sg - leader, sg->address, sg->length); -#endif - } else { - /* All virtually contiguous. We need to find the - length of each physically contiguous subsegment - to fill in the ptes. */ - do { - struct scatterlist *last_sg = sg; + do { + struct scatterlist *last_sg = sg; - size = sg->length; - paddr = virt_to_phys(sg->address); + size = sg->length; + paddr = virt_to_phys(sg->address); - while (sg+1 < end && (int) sg[1].dma_address == -1) { - size += sg[1].length; - sg++; - } + while (sg+1 < end && (int) sg[1].dma_address == -1) { + size += sg[1].length; + sg++; + } - npages = calc_npages((paddr & ~PAGE_MASK) + size); + npages = calc_npages((paddr & ~PAGE_MASK) + size); - paddr &= PAGE_MASK; - for (i = 0; i < npages; ++i, paddr += PAGE_SIZE) - *ptes++ = mk_iommu_pte(paddr); + paddr &= PAGE_MASK; + for (i = 0; i < npages; ++i, paddr += PAGE_SIZE) + *ptes++ = mk_iommu_pte(paddr); #if DEBUG_ALLOC > 0 - DBGA(" (%ld) [%p,%x] np %ld\n", + DBGA(" (%ld) [%p,%x] np %ld\n", + last_sg - leader, last_sg->address, + last_sg->length, npages); + while (++last_sg <= sg) { + DBGA(" (%ld) [%p,%x] cont\n", last_sg - leader, last_sg->address, - last_sg->length, npages); - while (++last_sg <= sg) { - DBGA(" (%ld) [%p,%x] cont\n", - last_sg - leader, last_sg->address, - last_sg->length); - } + last_sg->length); + } #endif - } while (++sg < end && (int) sg->dma_address < 0); - } + } while (++sg < end && (int) sg->dma_address < 0); return 1; } @@ -472,13 +468,9 @@ pci_map_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents, /* Third, iterate over the scatterlist leaders and allocate dma space as needed. */ for (out = sg; sg < end; ++sg) { - int ret; - if ((int) sg->dma_address < 0) continue; - - ret = sg_fill(sg, end, out, arena, max_dma); - if (ret < 0) + if (sg_fill(sg, end, out, arena, max_dma) < 0) goto error; out++; } @@ -517,7 +509,6 @@ pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents, struct pci_iommu_arena *arena; struct scatterlist *end; dma_addr_t max_dma; - dma_addr_t fstart, fend; if (direction == PCI_DMA_NONE) BUG(); @@ -531,42 +522,32 @@ pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents, if (!arena || arena->dma_base + arena->size > max_dma) arena = hose->sg_isa; - fstart = -1; - fend = 0; for (end = sg + nents; sg < end; ++sg) { unsigned long addr, size; + long npages, ofs; addr = sg->dma_address; size = sg->dma_length; - if (!size) break; +#if !DEBUG_NODIRECT if (addr >= __direct_map_base && addr < __direct_map_base + __direct_map_size) { /* Nothing to do. */ DBGA(" (%ld) direct [%lx,%lx]\n", sg - end + nents, addr, size); - } else { - long npages, ofs; - dma_addr_t tend; - - DBGA(" (%ld) sg [%lx,%lx]\n", - sg - end + nents, addr, size); + continue; + } +#endif - npages = calc_npages((addr & ~PAGE_MASK) + size); - ofs = (addr - arena->dma_base) >> PAGE_SHIFT; - iommu_arena_free(arena, ofs, npages); + DBGA(" (%ld) sg [%lx,%lx]\n", + sg - end + nents, addr, size); - tend = addr + size - 1; - if (fstart > addr) - fstart = addr; - if (fend < tend) - fend = tend; - } + npages = calc_npages((addr & ~PAGE_MASK) + size); + ofs = (addr - arena->dma_base) >> PAGE_SHIFT; + iommu_arena_free(arena, ofs, npages); } - if (fend) - alpha_mv.mv_pci_tbi(hose, fstart, fend); DBGA("pci_unmap_sg: %d entries\n", nents - (end - sg)); } @@ -580,6 +561,7 @@ pci_dma_supported(struct pci_dev *pdev, dma_addr_t mask) struct pci_controler *hose; struct pci_iommu_arena *arena; +#if !DEBUG_NODIRECT /* If there exists a direct map, and the mask fits either MAX_DMA_ADDRESS defined such that GFP_DMA does something useful, or the total system memory as shifted by the @@ -588,6 +570,7 @@ pci_dma_supported(struct pci_dev *pdev, dma_addr_t mask) && (__direct_map_base + MAX_DMA_ADDRESS-IDENT_ADDR-1 <= mask || __direct_map_base + (max_low_pfn<<PAGE_SHIFT)-1 <= mask)) return 1; +#endif /* Check that we have a scatter-gather arena that fits. */ hose = pdev ? pdev->sysdata : pci_isa_hose; diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h index a8859059b..8e7bbcfaf 100644 --- a/arch/alpha/kernel/proto.h +++ b/arch/alpha/kernel/proto.h @@ -10,7 +10,6 @@ struct pt_regs; struct task_struct; struct pci_dev; struct pci_controler; -struct irqaction; /* core_apecs.c */ extern struct pci_ops apecs_pci_ops; @@ -81,14 +80,13 @@ extern void setup_smp(void); extern int smp_info(char *buffer); extern void handle_ipi(struct pt_regs *); extern void smp_percpu_timer_interrupt(struct pt_regs *); -extern unsigned long cpu_present_mask; /* bios32.c */ /* extern void reset_for_srm(void); */ /* time.c */ extern void timer_interrupt(int irq, void *dev, struct pt_regs * regs); -extern void common_init_rtc(struct irqaction *); +extern void common_init_rtc(void); extern unsigned long est_cycle_freq; /* smc37c93x.c */ diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index 1311d939b..615914535 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -487,6 +487,7 @@ setup_arch(char **cmdline_p) #ifdef __SMP__ setup_smp(); #endif + paging_init(); } static char sys_unknown[] = "Unknown"; diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c index cc5d8b16b..ff12b9e8d 100644 --- a/arch/alpha/kernel/signal.c +++ b/arch/alpha/kernel/signal.c @@ -437,6 +437,8 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, err |= __copy_to_user(frame->extramask, &set->sig[1], sizeof(frame->extramask)); } + if (err) + goto give_sigsegv; /* Set up to return from userspace. If provided, use a stub already in userspace. */ @@ -499,6 +501,8 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, sw, set->sig[0], oldsp); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + goto give_sigsegv; /* Set up to return from userspace. If provided, use a stub already in userspace. */ diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index be1a6440e..ab1fb9ab2 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -972,6 +972,33 @@ flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) flush_tlb_mm(mm); } +static void +ipi_flush_icache_page(void *x) +{ + struct mm_struct *mm = (struct mm_struct *) x; + if (mm == current->active_mm) + __load_new_mm_context(mm); +} + +void +flush_icache_page(struct vm_area_struct *vma, struct page *page) +{ + struct mm_struct *mm = vma->vm_mm; + + if ((vma->vm_flags & VM_EXEC) == 0) + return; + + mm->context = 0; + if (mm == current->active_mm) { + __load_new_mm_context(mm); + if (atomic_read(&mm->mm_users) <= 1) + return; + } + + if (smp_call_function(ipi_flush_icache_page, mm, 1, 1)) { + printk(KERN_CRIT "flush_icache_page: timed out\n"); + } +} int smp_info(char *buffer) diff --git a/arch/alpha/kernel/sys_alcor.c b/arch/alpha/kernel/sys_alcor.c index 5498c72ec..867d762f8 100644 --- a/arch/alpha/kernel/sys_alcor.c +++ b/arch/alpha/kernel/sys_alcor.c @@ -48,7 +48,7 @@ alcor_enable_irq(unsigned int irq) alcor_update_irq_hw(cached_irq_mask |= 1UL << (irq - 16)); } -static inline void +static void alcor_disable_irq(unsigned int irq) { alcor_update_irq_hw(cached_irq_mask &= ~(1UL << (irq - 16))); @@ -81,6 +81,13 @@ alcor_isa_mask_and_ack_irq(unsigned int irq) *(vuip)GRU_INT_CLEAR = 0; mb(); } +static void +alcor_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + alcor_enable_irq(irq); +} + static struct hw_interrupt_type alcor_irq_type = { typename: "ALCOR", startup: alcor_startup_irq, @@ -88,7 +95,7 @@ static struct hw_interrupt_type alcor_irq_type = { enable: alcor_enable_irq, disable: alcor_disable_irq, ack: alcor_mask_and_ack_irq, - end: alcor_enable_irq, + end: alcor_end_irq, }; static void @@ -134,13 +141,12 @@ alcor_init_irq(void) on while IRQ probing. */ if (i >= 16+20 && i <= 16+30) continue; - irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; irq_desc[i].handler = &alcor_irq_type; } i8259a_irq_type.ack = alcor_isa_mask_and_ack_irq; init_i8259a_irqs(); - init_rtc_irq(); common_init_isa_dma(); setup_irq(16+31, &isa_cascade_irqaction); diff --git a/arch/alpha/kernel/sys_cabriolet.c b/arch/alpha/kernel/sys_cabriolet.c index acea58d1e..8e016ada0 100644 --- a/arch/alpha/kernel/sys_cabriolet.c +++ b/arch/alpha/kernel/sys_cabriolet.c @@ -65,6 +65,13 @@ cabriolet_startup_irq(unsigned int irq) return 0; /* never anything pending */ } +static void +cabriolet_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + cabriolet_enable_irq(irq); +} + static struct hw_interrupt_type cabriolet_irq_type = { typename: "CABRIOLET", startup: cabriolet_startup_irq, @@ -72,7 +79,7 @@ static struct hw_interrupt_type cabriolet_irq_type = { enable: cabriolet_enable_irq, disable: cabriolet_disable_irq, ack: cabriolet_disable_irq, - end: cabriolet_enable_irq, + end: cabriolet_end_irq, }; static void @@ -103,7 +110,6 @@ static void __init cabriolet_init_irq(void) { init_i8259a_irqs(); - init_rtc_irq(); if (alpha_using_srm) { alpha_mv.device_interrupt = srm_device_interrupt; @@ -117,7 +123,7 @@ cabriolet_init_irq(void) outb(0xff, 0x806); for (i = 16; i < 35; ++i) { - irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; irq_desc[i].handler = &cabriolet_irq_type; } } diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c index 7414b8cc2..63abc109f 100644 --- a/arch/alpha/kernel/sys_dp264.c +++ b/arch/alpha/kernel/sys_dp264.c @@ -35,6 +35,10 @@ /* Note mask bit is true for ENABLED irqs. */ static unsigned long cached_irq_mask; +/* dp264 boards handle at max four CPUs */ +static unsigned long cpu_irq_affinity[4]; + +spinlock_t dp264_irq_lock = SPIN_LOCK_UNLOCKED; static void tsunami_update_irq_hw(unsigned long mask, unsigned long isa_enable) @@ -50,9 +54,14 @@ tsunami_update_irq_hw(unsigned long mask, unsigned long isa_enable) mask0 = mask1 = mask2 = mask3 = mask; maskB = mask | isa_enable; if (bcpu == 0) mask0 = maskB; - if (bcpu == 1) mask1 = maskB; - if (bcpu == 2) mask2 = maskB; - if (bcpu == 3) mask3 = maskB; + else if (bcpu == 1) mask1 = maskB; + else if (bcpu == 2) mask2 = maskB; + else if (bcpu == 3) mask3 = maskB; + + mask0 &= cpu_irq_affinity[0]; + mask1 &= cpu_irq_affinity[1]; + mask2 &= cpu_irq_affinity[2]; + mask3 &= cpu_irq_affinity[3]; dim0 = &cchip->dim0.csr; dim1 = &cchip->dim1.csr; @@ -73,10 +82,12 @@ tsunami_update_irq_hw(unsigned long mask, unsigned long isa_enable) *dim2; *dim3; #else - volatile unsigned long *dimB = &cchip->dim1.csr; + volatile unsigned long *dimB; if (bcpu == 0) dimB = &cchip->dim0.csr; - if (bcpu == 2) dimB = &cchip->dim2.csr; - if (bcpu == 3) dimB = &cchip->dim3.csr; + else if (bcpu == 1) dimB = &cchip->dim1.csr; + else if (bcpu == 2) dimB = &cchip->dim2.csr; + else if (bcpu == 3) dimB = &cchip->dim3.csr; + *dimB = mask | isa_enable; mb(); *dimB; @@ -95,18 +106,22 @@ clipper_update_irq_hw(unsigned long mask) tsunami_update_irq_hw(mask, 1UL << 55); } -static inline void +static void dp264_enable_irq(unsigned int irq) { + spin_lock(&dp264_irq_lock); cached_irq_mask |= 1UL << irq; dp264_update_irq_hw(cached_irq_mask); + spin_unlock(&dp264_irq_lock); } static void dp264_disable_irq(unsigned int irq) { + spin_lock(&dp264_irq_lock); cached_irq_mask &= ~(1UL << irq); dp264_update_irq_hw(cached_irq_mask); + spin_unlock(&dp264_irq_lock); } static unsigned int @@ -116,18 +131,29 @@ dp264_startup_irq(unsigned int irq) return 0; /* never anything pending */ } -static inline void +static void +dp264_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + dp264_enable_irq(irq); +} + +static void clipper_enable_irq(unsigned int irq) { + spin_lock(&dp264_irq_lock); cached_irq_mask |= 1UL << irq; clipper_update_irq_hw(cached_irq_mask); + spin_unlock(&dp264_irq_lock); } static void clipper_disable_irq(unsigned int irq) { + spin_lock(&dp264_irq_lock); cached_irq_mask &= ~(1UL << irq); clipper_update_irq_hw(cached_irq_mask); + spin_unlock(&dp264_irq_lock); } static unsigned int @@ -137,6 +163,47 @@ clipper_startup_irq(unsigned int irq) return 0; /* never anything pending */ } +static void +clipper_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + clipper_enable_irq(irq); +} + +static void +cpu_set_irq_affinity(unsigned int irq, unsigned long affinity) +{ + int cpu; + + for (cpu = 0; cpu < 4; cpu++) { + unsigned long aff = cpu_irq_affinity[cpu]; + if (affinity & (1UL << cpu)) + aff |= 1UL << irq; + else + aff &= ~(1UL << irq); + cpu_irq_affinity[cpu] = aff; + } + +} + +static void +dp264_set_affinity(unsigned int irq, unsigned long affinity) +{ + spin_lock(&dp264_irq_lock); + cpu_set_irq_affinity(irq, affinity); + dp264_update_irq_hw(cached_irq_mask); + spin_unlock(&dp264_irq_lock); +} + +static void +clipper_set_affinity(unsigned int irq, unsigned long affinity) +{ + spin_lock(&dp264_irq_lock); + cpu_set_irq_affinity(irq, affinity); + clipper_update_irq_hw(cached_irq_mask); + spin_unlock(&dp264_irq_lock); +} + static struct hw_interrupt_type dp264_irq_type = { typename: "DP264", startup: dp264_startup_irq, @@ -144,7 +211,8 @@ static struct hw_interrupt_type dp264_irq_type = { enable: dp264_enable_irq, disable: dp264_disable_irq, ack: dp264_disable_irq, - end: dp264_enable_irq, + end: dp264_end_irq, + set_affinity: dp264_set_affinity, }; static struct hw_interrupt_type clipper_irq_type = { @@ -154,7 +222,8 @@ static struct hw_interrupt_type clipper_irq_type = { enable: clipper_enable_irq, disable: clipper_disable_irq, ack: clipper_disable_irq, - end: clipper_enable_irq, + end: clipper_end_irq, + set_affinity: clipper_set_affinity, }; static void @@ -249,6 +318,8 @@ init_tsunami_irqs(struct hw_interrupt_type * ops) static void __init dp264_init_irq(void) { + int cpu; + outb(0, DMA1_RESET_REG); outb(0, DMA2_RESET_REG); outb(DMA_MODE_CASCADE, DMA2_MODE_REG); @@ -257,10 +328,12 @@ dp264_init_irq(void) if (alpha_using_srm) alpha_mv.device_interrupt = dp264_srm_device_interrupt; + /* this is single threaded by design so no need of any smp lock */ + for (cpu = 0; cpu < 4; cpu++) + cpu_irq_affinity[cpu] = ~0UL; dp264_update_irq_hw(0UL); init_i8259a_irqs(); - init_rtc_irq(); init_tsunami_irqs(&dp264_irq_type); } @@ -278,7 +351,6 @@ clipper_init_irq(void) clipper_update_irq_hw(0UL); init_i8259a_irqs(); - init_rtc_irq(); init_tsunami_irqs(&clipper_irq_type); } diff --git a/arch/alpha/kernel/sys_eb64p.c b/arch/alpha/kernel/sys_eb64p.c index 0820bd5a0..b3e9e3867 100644 --- a/arch/alpha/kernel/sys_eb64p.c +++ b/arch/alpha/kernel/sys_eb64p.c @@ -48,7 +48,7 @@ eb64p_enable_irq(unsigned int irq) eb64p_update_irq_hw(irq, cached_irq_mask &= ~(1 << irq)); } -static inline void +static void eb64p_disable_irq(unsigned int irq) { eb64p_update_irq_hw(irq, cached_irq_mask |= 1 << irq); @@ -61,6 +61,13 @@ eb64p_startup_irq(unsigned int irq) return 0; /* never anything pending */ } +static void +eb64p_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + eb64p_enable_irq(irq); +} + static struct hw_interrupt_type eb64p_irq_type = { typename: "EB64P", startup: eb64p_startup_irq, @@ -68,7 +75,7 @@ static struct hw_interrupt_type eb64p_irq_type = { enable: eb64p_enable_irq, disable: eb64p_disable_irq, ack: eb64p_disable_irq, - end: eb64p_enable_irq, + end: eb64p_end_irq, }; static void @@ -119,10 +126,9 @@ eb64p_init_irq(void) outb(0xff, 0x27); init_i8259a_irqs(); - init_rtc_irq(); for (i = 16; i < 32; ++i) { - irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; irq_desc[i].handler = &eb64p_irq_type; } diff --git a/arch/alpha/kernel/sys_eiger.c b/arch/alpha/kernel/sys_eiger.c index c4d120799..9e4fbe569 100644 --- a/arch/alpha/kernel/sys_eiger.c +++ b/arch/alpha/kernel/sys_eiger.c @@ -76,6 +76,13 @@ eiger_startup_irq(unsigned int irq) return 0; /* never anything pending */ } +static void +eiger_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + eiger_enable_irq(irq); +} + static struct hw_interrupt_type eiger_irq_type = { typename: "EIGER", startup: eiger_startup_irq, @@ -83,7 +90,7 @@ static struct hw_interrupt_type eiger_irq_type = { enable: eiger_enable_irq, disable: eiger_disable_irq, ack: eiger_disable_irq, - end: eiger_enable_irq, + end: eiger_end_irq, }; static void @@ -147,10 +154,9 @@ eiger_init_irq(void) eiger_update_irq_hw(i, -1); init_i8259a_irqs(); - init_rtc_irq(); for (i = 16; i < 128; ++i) { - irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; irq_desc[i].handler = &eiger_irq_type; } } diff --git a/arch/alpha/kernel/sys_jensen.c b/arch/alpha/kernel/sys_jensen.c index 98dea0902..2f5400632 100644 --- a/arch/alpha/kernel/sys_jensen.c +++ b/arch/alpha/kernel/sys_jensen.c @@ -71,7 +71,7 @@ static struct hw_interrupt_type jensen_local_irq_type = { enable: i8259a_enable_irq, disable: i8259a_disable_irq, ack: jensen_local_ack, - end: i8259a_enable_irq, + end: i8259a_end_irq, }; static void @@ -110,7 +110,6 @@ static void __init jensen_init_irq(void) { init_i8259a_irqs(); - init_rtc_irq(); irq_desc[1].handler = &jensen_local_irq_type; irq_desc[4].handler = &jensen_local_irq_type; diff --git a/arch/alpha/kernel/sys_miata.c b/arch/alpha/kernel/sys_miata.c index 06457a0e8..0191ef91b 100644 --- a/arch/alpha/kernel/sys_miata.c +++ b/arch/alpha/kernel/sys_miata.c @@ -70,7 +70,6 @@ miata_init_irq(void) #endif init_i8259a_irqs(); - init_rtc_irq(); /* Not interested in the bogus interrupts (3,10), Fan Fault (0), NMI (1), or EIDE (9). diff --git a/arch/alpha/kernel/sys_mikasa.c b/arch/alpha/kernel/sys_mikasa.c index 936fa4f17..a66731154 100644 --- a/arch/alpha/kernel/sys_mikasa.c +++ b/arch/alpha/kernel/sys_mikasa.c @@ -48,7 +48,7 @@ mikasa_enable_irq(unsigned int irq) mikasa_update_irq_hw(cached_irq_mask |= 1 << (irq - 16)); } -static inline void +static void mikasa_disable_irq(unsigned int irq) { mikasa_update_irq_hw(cached_irq_mask &= ~(1 << (irq - 16))); @@ -61,6 +61,13 @@ mikasa_startup_irq(unsigned int irq) return 0; } +static void +mikasa_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + mikasa_enable_irq(irq); +} + static struct hw_interrupt_type mikasa_irq_type = { typename: "MIKASA", startup: mikasa_startup_irq, @@ -68,7 +75,7 @@ static struct hw_interrupt_type mikasa_irq_type = { enable: mikasa_enable_irq, disable: mikasa_disable_irq, ack: mikasa_disable_irq, - end: mikasa_enable_irq, + end: mikasa_end_irq, }; static void @@ -108,12 +115,11 @@ mikasa_init_irq(void) mikasa_update_irq_hw(0); for (i = 16; i < 32; ++i) { - irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; irq_desc[i].handler = &mikasa_irq_type; } init_i8259a_irqs(); - init_rtc_irq(); common_init_isa_dma(); } diff --git a/arch/alpha/kernel/sys_nautilus.c b/arch/alpha/kernel/sys_nautilus.c index 621df72f8..32653e3a7 100644 --- a/arch/alpha/kernel/sys_nautilus.c +++ b/arch/alpha/kernel/sys_nautilus.c @@ -54,7 +54,6 @@ static void __init nautilus_init_irq(void) { init_i8259a_irqs(); - init_rtc_irq(); common_init_isa_dma(); } diff --git a/arch/alpha/kernel/sys_noritake.c b/arch/alpha/kernel/sys_noritake.c index 31eac0da4..523d29853 100644 --- a/arch/alpha/kernel/sys_noritake.c +++ b/arch/alpha/kernel/sys_noritake.c @@ -45,13 +45,13 @@ noritake_update_irq_hw(int irq, int mask) outw(mask, port); } -static inline void +static void noritake_enable_irq(unsigned int irq) { noritake_update_irq_hw(irq, cached_irq_mask |= 1 << (irq - 16)); } -static inline void +static void noritake_disable_irq(unsigned int irq) { noritake_update_irq_hw(irq, cached_irq_mask &= ~(1 << (irq - 16))); @@ -135,12 +135,11 @@ noritake_init_irq(void) outw(0, 0x54c); for (i = 16; i < 48; ++i) { - irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; irq_desc[i].handler = &noritake_irq_type; } init_i8259a_irqs(); - init_rtc_irq(); common_init_isa_dma(); } diff --git a/arch/alpha/kernel/sys_rawhide.c b/arch/alpha/kernel/sys_rawhide.c index f956626a4..3ccce8640 100644 --- a/arch/alpha/kernel/sys_rawhide.c +++ b/arch/alpha/kernel/sys_rawhide.c @@ -41,6 +41,7 @@ static unsigned int hose_irq_masks[4] = { 0xff0000, 0xfe0000, 0xff0000, 0xff0000 }; static unsigned int cached_irq_masks[4]; +spinlock_t rawhide_irq_lock = SPIN_LOCK_UNLOCKED; static inline void rawhide_update_irq_hw(int hose, int mask) @@ -50,7 +51,7 @@ rawhide_update_irq_hw(int hose, int mask) *(vuip)MCPCIA_INT_MASK0(MCPCIA_HOSE2MID(hose)); } -static void +static inline void rawhide_enable_irq(unsigned int irq) { unsigned int mask, hose; @@ -59,9 +60,11 @@ rawhide_enable_irq(unsigned int irq) hose = irq / 24; irq -= hose * 24; + spin_lock(&rawhide_irq_lock); mask = cached_irq_masks[hose] |= 1 << irq; mask |= hose_irq_masks[hose]; rawhide_update_irq_hw(hose, mask); + spin_unlock(&rawhide_irq_lock); } static void @@ -73,9 +76,11 @@ rawhide_disable_irq(unsigned int irq) hose = irq / 24; irq -= hose * 24; + spin_lock(&rawhide_irq_lock); mask = cached_irq_masks[hose] &= ~(1 << irq); mask |= hose_irq_masks[hose]; rawhide_update_irq_hw(hose, mask); + spin_unlock(&rawhide_irq_lock); } @@ -86,6 +91,13 @@ rawhide_startup_irq(unsigned int irq) return 0; } +static void +rawhide_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + rawhide_enable_irq(irq); +} + static struct hw_interrupt_type rawhide_irq_type = { typename: "RAWHIDE", startup: rawhide_startup_irq, @@ -93,7 +105,7 @@ static struct hw_interrupt_type rawhide_irq_type = { enable: rawhide_enable_irq, disable: rawhide_disable_irq, ack: rawhide_disable_irq, - end: rawhide_enable_irq, + end: rawhide_end_irq, }; static void @@ -138,12 +150,11 @@ rawhide_init_irq(void) } for (i = 16; i < 128; ++i) { - irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; irq_desc[i].handler = &rawhide_irq_type; } init_i8259a_irqs(); - init_rtc_irq(); common_init_isa_dma(); } diff --git a/arch/alpha/kernel/sys_ruffian.c b/arch/alpha/kernel/sys_ruffian.c index ae89e81b2..01d80ab97 100644 --- a/arch/alpha/kernel/sys_ruffian.c +++ b/arch/alpha/kernel/sys_ruffian.c @@ -64,7 +64,7 @@ ruffian_init_irq(void) } static void __init -ruffian_init_rtc(struct irqaction *action) +ruffian_init_rtc(void) { /* Ruffian does not have the RTC connected to the CPU timer interrupt. Instead, it uses the PIT connected to IRQ 0. */ @@ -78,7 +78,7 @@ ruffian_init_rtc(struct irqaction *action) outb(0x31, 0x42); outb(0x13, 0x42); - setup_irq(0, action); + setup_irq(0, &timer_irqaction); } static void diff --git a/arch/alpha/kernel/sys_rx164.c b/arch/alpha/kernel/sys_rx164.c index d5299d008..2d5927c84 100644 --- a/arch/alpha/kernel/sys_rx164.c +++ b/arch/alpha/kernel/sys_rx164.c @@ -65,6 +65,13 @@ rx164_startup_irq(unsigned int irq) return 0; } +static void +rx164_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + rx164_enable_irq(irq); +} + static struct hw_interrupt_type rx164_irq_type = { typename: "RX164", startup: rx164_startup_irq, @@ -72,7 +79,7 @@ static struct hw_interrupt_type rx164_irq_type = { enable: rx164_enable_irq, disable: rx164_disable_irq, ack: rx164_disable_irq, - end: rx164_enable_irq, + end: rx164_end_irq, }; static void @@ -109,12 +116,11 @@ rx164_init_irq(void) rx164_update_irq_hw(0); for (i = 16; i < 40; ++i) { - irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; irq_desc[i].handler = &rx164_irq_type; } init_i8259a_irqs(); - init_rtc_irq(); common_init_isa_dma(); setup_irq(16+20, &isa_cascade_irqaction); diff --git a/arch/alpha/kernel/sys_sable.c b/arch/alpha/kernel/sys_sable.c index e2a69b5c7..b27d757b2 100644 --- a/arch/alpha/kernel/sys_sable.c +++ b/arch/alpha/kernel/sys_sable.c @@ -30,6 +30,7 @@ #include "pci_impl.h" #include "machvec_impl.h" +spinlock_t sable_irq_lock = SPIN_LOCK_UNLOCKED; /* * For SABLE, which is really baroque, we manage 40 IRQ's, but the @@ -137,8 +138,10 @@ sable_enable_irq(unsigned int irq) unsigned long bit, mask; bit = sable_irq_swizzle.irq_to_mask[irq]; + spin_lock(&sable_irq_lock); mask = sable_irq_swizzle.shadow_mask &= ~(1UL << bit); sable_update_irq_hw(bit, mask); + spin_unlock(&sable_irq_lock); } static void @@ -147,8 +150,10 @@ sable_disable_irq(unsigned int irq) unsigned long bit, mask; bit = sable_irq_swizzle.irq_to_mask[irq]; + spin_lock(&sable_irq_lock); mask = sable_irq_swizzle.shadow_mask |= 1UL << bit; sable_update_irq_hw(bit, mask); + spin_unlock(&sable_irq_lock); } static unsigned int @@ -159,14 +164,23 @@ sable_startup_irq(unsigned int irq) } static void +sable_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + sable_enable_irq(irq); +} + +static void sable_mask_and_ack_irq(unsigned int irq) { unsigned long bit, mask; bit = sable_irq_swizzle.irq_to_mask[irq]; + spin_lock(&sable_irq_lock); mask = sable_irq_swizzle.shadow_mask |= 1UL << bit; sable_update_irq_hw(bit, mask); sable_ack_irq_hw(bit); + spin_unlock(&sable_irq_lock); } static struct hw_interrupt_type sable_irq_type = { @@ -176,7 +190,7 @@ static struct hw_interrupt_type sable_irq_type = { enable: sable_enable_irq, disable: sable_disable_irq, ack: sable_mask_and_ack_irq, - end: sable_enable_irq, + end: sable_end_irq, }; static void @@ -204,11 +218,10 @@ sable_init_irq(void) outb(0x44, 0x535); /* enable cascades in master */ for (i = 0; i < 40; ++i) { - irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; irq_desc[i].handler = &sable_irq_type; } - init_rtc_irq(); common_init_isa_dma(); } diff --git a/arch/alpha/kernel/sys_sio.c b/arch/alpha/kernel/sys_sio.c index 0230ec6d9..e378408f2 100644 --- a/arch/alpha/kernel/sys_sio.c +++ b/arch/alpha/kernel/sys_sio.c @@ -42,7 +42,6 @@ sio_init_irq(void) alpha_mv.device_interrupt = srm_device_interrupt; init_i8259a_irqs(); - init_rtc_irq(); common_init_isa_dma(); } diff --git a/arch/alpha/kernel/sys_sx164.c b/arch/alpha/kernel/sys_sx164.c index 806241a32..bad57d35e 100644 --- a/arch/alpha/kernel/sys_sx164.c +++ b/arch/alpha/kernel/sys_sx164.c @@ -43,7 +43,6 @@ sx164_init_irq(void) alpha_mv.device_interrupt = srm_device_interrupt; init_i8259a_irqs(); - init_rtc_irq(); /* Not interested in the bogus interrupts (0,3,4,5,40-47), NMI (1), or HALT (2). */ diff --git a/arch/alpha/kernel/sys_takara.c b/arch/alpha/kernel/sys_takara.c index 360f135b6..a0bc57125 100644 --- a/arch/alpha/kernel/sys_takara.c +++ b/arch/alpha/kernel/sys_takara.c @@ -66,6 +66,13 @@ takara_startup_irq(unsigned int irq) return 0; /* never anything pending */ } +static void +takara_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + takara_enable_irq(irq); +} + static struct hw_interrupt_type takara_irq_type = { typename: "TAKARA", startup: takara_startup_irq, @@ -73,7 +80,7 @@ static struct hw_interrupt_type takara_irq_type = { enable: takara_enable_irq, disable: takara_disable_irq, ack: takara_disable_irq, - end: takara_enable_irq, + end: takara_end_irq, }; static void @@ -126,7 +133,6 @@ takara_init_irq(void) long i; init_i8259a_irqs(); - init_rtc_irq(); if (alpha_using_srm) { alpha_mv.device_interrupt = takara_srm_device_interrupt; @@ -146,7 +152,7 @@ takara_init_irq(void) takara_update_irq_hw(i, -1); for (i = 16; i < 128; ++i) { - irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; irq_desc[i].handler = &takara_irq_type; } diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index d7b5cee8c..291682e33 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c @@ -163,7 +163,7 @@ static inline unsigned long mktime(unsigned int year, unsigned int mon, } void -common_init_rtc(struct irqaction *action) +common_init_rtc() { unsigned char x; @@ -192,18 +192,12 @@ common_init_rtc(struct irqaction *action) outb(0x31, 0x42); outb(0x13, 0x42); - setup_irq(RTC_IRQ, action); + init_rtc_irq(); } void time_init(void) { - static struct irqaction timer_irqaction = { - handler: timer_interrupt, - flags: SA_INTERRUPT, - name: "timer", - }; - unsigned int year, mon, day, hour, min, sec, cc1, cc2; unsigned long cycle_freq, one_percent; long diff; @@ -292,7 +286,9 @@ time_init(void) state.partial_tick = 0L; /* Startup the timer source. */ - alpha_mv.init_rtc(&timer_irqaction); + alpha_mv.init_rtc(); + + do_get_fast_time = do_gettimeofday; } /* |