/* -*- mode: c; c-basic-offset: 8 -*- */ /* Copyright (C) 1999,2001 * * Author: J.E.J.Bottomley@HansenPartnership.com * * linux/arch/i386/kernel/voyager_smp.c * * This file provides all the same external entries as smp.c but uses * the voyager hal to provide the functionality */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* TLB state -- visible externally, indexed physically */ DEFINE_PER_CPU(struct tlb_state, cpu_tlbstate) ____cacheline_aligned = { &init_mm, 0 }; /* CPU IRQ affinity -- set to all ones initially */ static unsigned long cpu_irq_affinity[NR_CPUS] __cacheline_aligned = { [0 ... NR_CPUS-1] = ~0UL }; /* per CPU data structure (for /proc/cpuinfo et al), visible externally * indexed physically */ struct cpuinfo_x86 cpu_data[NR_CPUS] __cacheline_aligned; EXPORT_SYMBOL(cpu_data); /* physical ID of the CPU used to boot the system */ unsigned char boot_cpu_id; /* The memory line addresses for the Quad CPIs */ struct voyager_qic_cpi *voyager_quad_cpi_addr[NR_CPUS] __cacheline_aligned; /* The masks for the Extended VIC processors, filled in by cat_init */ __u32 voyager_extended_vic_processors = 0; /* Masks for the extended Quad processors which cannot be VIC booted */ __u32 voyager_allowed_boot_processors = 0; /* The mask for the Quad Processors (both extended and non-extended) */ __u32 voyager_quad_processors = 0; /* Total count of live CPUs, used in process.c to display * the CPU information and in irq.c for the per CPU irq * activity count. Finally exported by i386_ksyms.c */ static int voyager_extended_cpus = 1; /* Have we found an SMP box - used by time.c to do the profiling interrupt for timeslicing; do not set to 1 until the per CPU timer interrupt is active */ int smp_found_config = 0; /* Used for the invalidate map that's also checked in the spinlock */ static volatile unsigned long smp_invalidate_needed; /* Bitmask of currently online CPUs - used by setup.c for /proc/cpuinfo, visible externally but still physical */ cpumask_t cpu_online_map = CPU_MASK_NONE; EXPORT_SYMBOL(cpu_online_map); /* Bitmask of CPUs present in the system - exported by i386_syms.c, used * by scheduler but indexed physically */ cpumask_t phys_cpu_present_map = CPU_MASK_NONE; /* The internal functions */ static void send_CPI(__u32 cpuset, __u8 cpi); static void ack_CPI(__u8 cpi); static int ack_QIC_CPI(__u8 cpi); static void ack_special_QIC_CPI(__u8 cpi); static void ack_VIC_CPI(__u8 cpi); static void send_CPI_allbutself(__u8 cpi); static void mask_vic_irq(unsigned int irq); static void unmask_vic_irq(unsigned int irq); static unsigned int startup_vic_irq(unsigned int irq); static void enable_local_vic_irq(unsigned int irq); static void disable_local_vic_irq(unsigned int irq); static void before_handle_vic_irq(unsigned int irq); static void after_handle_vic_irq(unsigned int irq); static void set_vic_irq_affinity(unsigned int irq, cpumask_t mask); static void ack_vic_irq(unsigned int irq); static void vic_enable_cpi(void); static void do_boot_cpu(__u8 cpuid); static void do_quad_bootstrap(void); int hard_smp_processor_id(void); int safe_smp_processor_id(void); /* Inline functions */ static inline void send_one_QIC_CPI(__u8 cpu, __u8 cpi) { voyager_quad_cpi_addr[cpu]->qic_cpi[cpi].cpi = (smp_processor_id() << 16) + cpi; } static inline void send_QIC_CPI(__u32 cpuset, __u8 cpi) { int cpu; for_each_online_cpu(cpu) { if(cpuset & (1<>3 &0x7 on the 32 way */ if(((cpuid >> 2) & 0x03) == i) /* don't lower our own mask! */ continue; /* masquerade as local Quad CPU */ outb(QIC_CPUID_ENABLE | i, QIC_PROCESSOR_ID); /* enable the startup CPI */ outb(QIC_BOOT_CPI_MASK, QIC_MASK_REGISTER1); /* restore cpu id */ outb(0, QIC_PROCESSOR_ID); } local_irq_restore(flags); } } /* Set up all the basic stuff: read the SMP config and make all the * SMP information reflect only the boot cpu. All others will be * brought on-line later. */ void __init find_smp_config(void) { int i; boot_cpu_id = hard_smp_processor_id(); printk("VOYAGER SMP: Boot cpu is %d\n", boot_cpu_id); /* initialize the CPU structures (moved from smp_boot_cpus) */ for(i=0; icpu = boot_cpu_id; write_pda(cpu_number, boot_cpu_id); } /* * The bootstrap kernel entry code has set these up. Save them * for a given CPU, id is physical */ void __init smp_store_cpu_info(int id) { struct cpuinfo_x86 *c=&cpu_data[id]; *c = boot_cpu_data; identify_cpu(c); } /* set up the trampoline and return the physical address of the code */ static __u32 __init setup_trampoline(void) { /* these two are global symbols in trampoline.S */ extern __u8 trampoline_end[]; extern __u8 trampoline_data[]; memcpy((__u8 *)trampoline_base, trampoline_data, trampoline_end - trampoline_data); return virt_to_phys((__u8 *)trampoline_base); } /* Routine initially called when a non-boot CPU is brought online */ static void __init start_secondary(void *unused) { __u8 cpuid = hard_smp_processor_id(); /* external functions not defined in the headers */ extern void calibrate_delay(void); secondary_cpu_init(); /* OK, we're in the routine */ ack_CPI(VIC_CPU_BOOT_CPI); /* setup the 8259 master slave pair belonging to this CPU --- * we won't actually receive any until the boot CPU * relinquishes it's static routing mask */ vic_setup_pic(); qic_setup(); if(is_cpu_quad() && !is_cpu_vic_boot()) { /* clear the boot CPI */ __u8 dummy; dummy = voyager_quad_cpi_addr[cpuid]->qic_cpi[VIC_CPU_BOOT_CPI].cpi; printk("read dummy %d\n", dummy); } /* lower the mask to receive CPIs */ vic_enable_cpi(); VDEBUG(("VOYAGER SMP: CPU%d, stack at about %p\n", cpuid, &cpuid)); /* enable interrupts */ local_irq_enable(); /* get our bogomips */ calibrate_delay(); /* save our processor parameters */ smp_store_cpu_info(cpuid); /* if we're a quad, we may need to bootstrap other CPUs */ do_quad_bootstrap(); /* FIXME: this is rather a poor hack to prevent the CPU * activating softirqs while it's supposed to be waiting for * permission to proceed. Without this, the new per CPU stuff * in the softirqs will fail */ local_irq_disable(); cpu_set(cpuid, cpu_callin_map); /* signal that we're done */ cpu_booted_map = 1; while (!cpu_isset(cpuid, smp_commenced_mask)) rep_nop(); local_irq_enable(); local_flush_tlb(); cpu_set(cpuid, cpu_online_map); wmb(); cpu_idle(); } /* Routine to kick start the given CPU and wait for it to report ready * (or timeout in startup). When this routine returns, the requested * CPU is either fully running and configured or known to be dead. * * We call this routine sequentially 1 CPU at a time, so no need for * locking */ static void __init do_boot_cpu(__u8 cpu) { struct task_struct *idle; int timeout; unsigned long flags; int quad_boot = (1<> 4) & 0xFFFF; cpucount++; idle = fork_idle(cpu); if(IS_ERR(idle)) panic("failed fork for CPU%d", cpu); idle->thread.eip = (unsigned long) start_secondary; /* init_tasks (in sched.c) is indexed logically */ stack_start.esp = (void *) idle->thread.esp; /* Pre-allocate and initialize the CPU's GDT and PDA so it doesn't have to do any memory allocation during the delicate CPU-bringup phase. */ if (!init_gdt(cpu, idle)) { printk(KERN_INFO "Couldn't allocate GDT/PDA for CPU %d\n", cpu); cpucount--; return; } irq_ctx_init(cpu); /* Note: Don't modify initial ss override */ VDEBUG(("VOYAGER SMP: Booting CPU%d at 0x%lx[%x:%x], stack %p\n", cpu, (unsigned long)hijack_source.val, hijack_source.idt.Segment, hijack_source.idt.Offset, stack_start.esp)); /* init lowmem identity mapping */ clone_pgd_range(swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS, min_t(unsigned long, KERNEL_PGD_PTRS, USER_PGD_PTRS)); flush_tlb_all(); if(quad_boot) { printk("CPU %d: non extended Quad boot\n", cpu); hijack_vector = (__u32 *)phys_to_virt((VIC_CPU_BOOT_CPI + QIC_DEFAULT_CPI_BASE)*4); *hijack_vector = hijack_source.val; } else { printk("CPU%d: extended VIC boot\n", cpu); hijack_vector = (__u32 *)phys_to_virt((VIC_CPU_BOOT_CPI + VIC_DEFAULT_CPI_BASE)*4); *hijack_vector = hijack_source.val; /* VIC errata, may also receive interrupt at this address */ hijack_vector = (__u32 *)phys_to_virt((VIC_CPU_BOOT_ERRATA_CPI + VIC_DEFAULT_CPI_BASE)*4); *hijack_vector = hijack_source.val; } /* All non-boot CPUs start with interrupts fully masked. Need * to lower the mask of the CPI we're about to send. We do * this in the VIC by masquerading as the processor we're * about to boot and lowering its interrupt mask */ local_irq_save(flags); if(quad_boot) { send_one_QIC_CPI(cpu, VIC_CPU_BOOT_CPI); } else { outb(VIC_CPU_MASQUERADE_ENABLE | cpu, VIC_PROCESSOR_ID); /* here we're altering registers belonging to `cpu' */ outb(VIC_BOOT_INTERRUPT_MASK, 0x21); /* now go back to our original identity */ outb(boot_cpu_id, VIC_PROCESSOR_ID); /* and boot the CPU */ send_CPI((1<cpu); /* * We don't actually need to load the full TSS, * basically just the stack pointer and the eip. */ asm volatile( "movl %0,%%esp\n\t" "jmp *%1" : :"r" (current->thread.esp),"r" (current->thread.eip)); } /* handle a Voyager SYS_INT -- If we don't, the base board will * panic the system. * * System interrupts occur because some problem was detected on the * various busses. To find out what you have to probe all the * hardware via the CAT bus. FIXME: At the moment we do nothing. */ fastcall void smp_vic_sys_interrupt(struct pt_regs *regs) { ack_CPI(VIC_SYS_INT); printk("Voyager SYSTEM INTERRUPT\n"); } /* Handle a voyager CMN_INT; These interrupts occur either because of * a system status change or because a single bit memory error * occurred. FIXME: At the moment, ignore all this. */ fastcall void smp_vic_cmn_interrupt(struct pt_regs *regs) { static __u8 in_cmn_int = 0; static DEFINE_SPINLOCK(cmn_int_lock); /* common ints are broadcast, so make sure we only do this once */ _raw_spin_lock(&cmn_int_lock); if(in_cmn_int) goto unlock_end; in_cmn_int++; _raw_spin_unlock(&cmn_int_lock); VDEBUG(("Voyager COMMON INTERRUPT\n")); if(voyager_level == 5) voyager_cat_do_common_interrupt(); _raw_spin_lock(&cmn_int_lock); in_cmn_int = 0; unlock_end: _raw_spin_unlock(&cmn_int_lock); ack_CPI(VIC_CMN_INT); } /* * Reschedule call back. Nothing to do, all the work is done * automatically when we return from the interrupt. */ static void smp_reschedule_interrupt(void) { /* do nothing */ } static struct mm_struct * flush_mm; static unsigned long flush_va; static DEFINE_SPINLOCK(tlbstate_lock); #define FLUSH_ALL 0xffffffff /* * We cannot call mmdrop() because we are in interrupt context, * instead update mm->cpu_vm_mask. * * We need to reload %cr3 since the page tables may be going * away from under us.. */ static inline void leave_mm (unsigned long cpu) { if (per_cpu(cpu_tlbstate, cpu).state == TLBSTATE_OK) BUG(); cpu_clear(cpu, per_cpu(cpu_tlbstate, cpu).active_mm->cpu_vm_mask); load_cr3(swapper_pg_dir); } /* * Invalidate call-back */ static void smp_invalidate_interrupt(void) { __u8 cpu = smp_processor_id(); if (!test_bit(cpu, &smp_invalidate_needed)) return; /* This will flood messages. Don't uncomment unless you see * Problems with cross cpu invalidation VDEBUG(("VOYAGER SMP: CPU%d received INVALIDATE_CPI\n", smp_processor_id())); */ if (flush_mm == per_cpu(cpu_tlbstate, cpu).active_mm) { if (per_cpu(cpu_tlbstate, cpu).state == TLBSTATE_OK) { if (flush_va == FLUSH_ALL) local_flush_tlb(); else __flush_tlb_one(flush_va); } else leave_mm(cpu); } smp_mb__before_clear_bit(); clear_bit(cpu, &smp_invalidate_needed); smp_mb__after_clear_bit(); } /* All the new flush operations for 2.4 */ /* This routine is called with a physical cpu mask */ static void flush_tlb_others (unsigned long cpumask, struct mm_struct *mm, unsigned long va) { int stuck = 50000; if (!cpumask) BUG(); if ((cpumask & cpus_addr(cpu_online_map)[0]) != cpumask) BUG(); if (cpumask & (1 << smp_processor_id())) BUG(); if (!mm) BUG(); spin_lock(&tlbstate_lock); flush_mm = mm; flush_va = va; atomic_set_mask(cpumask, &smp_invalidate_needed); /* * We have to send the CPI only to * CPUs affected. */ send_CPI(cpumask, VIC_INVALIDATE_CPI); while (smp_invalidate_needed) { mb(); if(--stuck == 0) { printk("***WARNING*** Stuck doing invalidate CPI (CPU%d)\n", smp_processor_id()); break; } } /* Uncomment only to debug invalidation problems VDEBUG(("VOYAGER SMP: Completed invalidate CPI (CPU%d)\n", cpu)); */ flush_mm = NULL; flush_va = 0; spin_unlock(&tlbstate_lock); } void flush_tlb_current_task(void) { struct mm_struct *mm = current->mm; unsigned long cpu_mask; preempt_disable(); cpu_mask = cpus_addr(mm->cpu_vm_mask)[0] & ~(1 << smp_processor_id()); local_flush_tlb(); if (cpu_mask) flush_tlb_others(cpu_mask, mm, FLUSH_ALL); preempt_enable(); } void flush_tlb_mm (struct mm_struct * mm) { unsigned long cpu_mask; preempt_disable(); cpu_mask = cpus_addr(mm->cpu_vm_mask)[0] & ~(1 << smp_processor_id()); if (current->active_mm == mm) { if (current->mm) local_flush_tlb(); else leave_mm(smp_processor_id()); } if (cpu_mask) flush_tlb_others(cpu_mask, mm, FLUSH_ALL); preempt_enable(); } void flush_tlb_page(struct vm_area_struct * vma, unsigned long va) { struct mm_struct *mm = vma->vm_mm; unsigned long cpu_mask; preempt_disable(); cpu_mask = cpus_addr(mm->cpu_vm_mask)[0] & ~(1 << smp_processor_id()); if (current->active_mm == mm) { if(current->mm) __flush_tlb_one(va); else leave_mm(smp_processor_id()); } if (cpu_mask) flush_tlb_others(cpu_mask, mm, va); preempt_enable(); } EXPORT_SYMBOL(flush_tlb_page); /* enable the requested IRQs */ static void smp_enable_irq_interrupt(void) { __u8 irq; __u8 cpu = get_cpu(); VDEBUG(("VOYAGER SMP: CPU%d enabling irq mask 0x%x\n", cpu, vic_irq_enable_mask[cpu])); spin_lock(&vic_irq_lock); for(irq = 0; irq < 16; irq++) { if(vic_irq_enable_mask[cpu] & (1<func; void *info = call_data->info; /* must take copy of wait because call_data may be replaced * unless the function is waiting for us to finish */ int wait = call_data->wait; __u8 cpu = smp_processor_id(); /* * Notify initiating CPU that I've grabbed the data and am * about to execute the function */ mb(); if(!test_and_clear_bit(cpu, &call_data->started)) { /* If the bit wasn't set, this could be a replay */ printk(KERN_WARNING "VOYAGER SMP: CPU %d received call funtion with no call pending\n", cpu); return; } /* * At this point the info structure may be out of scope unless wait==1 */ irq_enter(); (*func)(info); irq_exit(); if (wait) { mb(); clear_bit(cpu, &call_data->finished); } } static int __smp_call_function_mask (void (*func) (void *info), void *info, int retry, int wait, __u32 mask) { struct call_data_struct data; mask &= ~(1< The function to run. This must be fast and non-blocking. An arbitrary pointer to pass to the function. If true, keep retrying until ready. If true, wait until function has completed on other CPUs. [RETURNS] 0 on success, else a negative status code. Does not return until remote CPUs are nearly ready to execute <> or are or have executed. */ int smp_call_function(void (*func) (void *info), void *info, int retry, int wait) { __u32 mask = cpus_addr(cpu_online_map)[0]; return __smp_call_function_mask(func, info, retry, wait, mask); } EXPORT_SYMBOL(smp_call_function); /* * smp_call_function_single - Run a function on another CPU * @func: The function to run. This must be fast and non-blocking. * @info: An arbitrary pointer to pass to the function. * @nonatomic: Currently unused. * @wait: If true, wait until function has completed on other CPUs. * * Retrurns 0 on success, else a negative status code. * * Does not return until the remote CPU is nearly ready to execute * or is or has executed. */ int smp_call_function_single(int cpu, void (*func) (void *info), void *info, int nonatomic, int wait) { __u32 mask = 1 << cpu; return __smp_call_function_mask(func, info, nonatomic, wait, mask); } EXPORT_SYMBOL(smp_call_function_single); /* Sorry about the name. In an APIC based system, the APICs * themselves are programmed to send a timer interrupt. This is used * by linux to reschedule the processor. Voyager doesn't have this, * so we use the system clock to interrupt one processor, which in * turn, broadcasts a timer CPI to all the others --- we receive that * CPI here. We don't use this actually for counting so losing * ticks doesn't matter * * FIXME: For those CPU's which actually have a local APIC, we could * try to use it to trigger this interrupt instead of having to * broadcast the timer tick. Unfortunately, all my pentium DYADs have * no local APIC, so I can't do this * * This function is currently a placeholder and is unused in the code */ fastcall void smp_apic_timer_interrupt(struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); wrapper_smp_local_timer_interrupt(); set_irq_regs(old_regs); } /* All of the QUAD interrupt GATES */ fastcall void smp_qic_timer_interrupt(struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); ack_QIC_CPI(QIC_TIMER_CPI); wrapper_smp_local_timer_interrupt(); set_irq_regs(old_regs); } fastcall void smp_qic_invalidate_interrupt(struct pt_regs *regs) { ack_QIC_CPI(QIC_INVALIDATE_CPI); smp_invalidate_interrupt(); } fastcall void smp_qic_reschedule_interrupt(struct pt_regs *regs) { ack_QIC_CPI(QIC_RESCHEDULE_CPI); smp_reschedule_interrupt(); } fastcall void smp_qic_enable_irq_interrupt(struct pt_regs *regs) { ack_QIC_CPI(QIC_ENABLE_IRQ_CPI); smp_enable_irq_interrupt(); } fastcall void smp_qic_call_function_interrupt(struct pt_regs *regs) { ack_QIC_CPI(QIC_CALL_FUNCTION_CPI); smp_call_function_interrupt(); } fastcall void smp_vic_cpi_interrupt(struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); __u8 cpu = smp_processor_id(); if(is_cpu_quad()) ack_QIC_CPI(VIC_CPI_LEVEL0); else ack_VIC_CPI(VIC_CPI_LEVEL0); if(test_and_clear_bit(VIC_TIMER_CPI, &vic_cpi_mailbox[cpu])) wrapper_smp_local_timer_interrupt(); if(test_and_clear_bit(VIC_INVALIDATE_CPI, &vic_cpi_mailbox[cpu])) smp_invalidate_interrupt(); if(test_and_clear_bit(VIC_RESCHEDULE_CPI, &vic_cpi_mailbox[cpu])) smp_reschedule_interrupt(); if(test_and_clear_bit(VIC_ENABLE_IRQ_CPI, &vic_cpi_mailbox[cpu])) smp_enable_irq_interrupt(); if(test_and_clear_bit(VIC_CALL_FUNCTION_CPI, &vic_cpi_mailbox[cpu])) smp_call_function_interrupt(); set_irq_regs(old_regs); } static void do_flush_tlb_all(void* info) { unsigned long cpu = smp_processor_id(); __flush_tlb_all(); if (per_cpu(cpu_tlbstate, cpu).state == TLBSTATE_LAZY) leave_mm(cpu); } /* flush the TLB of every active CPU in the system */ void flush_tlb_all(void) { on_each_cpu(do_flush_tlb_all, 0, 1, 1); } /* used to set up the trampoline for other CPUs when the memory manager * is sorted out */ void __init smp_alloc_memory(void) { trampoline_base = (__u32)alloc_bootmem_low_pages(PAGE_SIZE); if(__pa(trampoline_base) >= 0x93000) BUG(); } /* send a reschedule CPI to one CPU by physical CPU number*/ void smp_send_reschedule(int cpu) { send_one_CPI(cpu, VIC_RESCHEDULE_CPI); } int hard_smp_processor_id(void) { __u8 i; __u8 cpumask = inb(VIC_PROC_WHO_AM_I); if((cpumask & QUAD_IDENTIFIER) == QUAD_IDENTIFIER) return cpumask & 0x1F; for(i = 0; i < 8; i++) { if(cpumask & (1<1 eligible CPUs are equal lowest, the * lowest processor number gets it. * * The priority of a CPU is controlled by a special per-CPU * VIC priority register which is 3 bits wide 0 being lowest * and 7 highest priority.. * * Therefore we subtract the average number of interrupts from * the number we've fielded. If this number is negative, we * lower the activity count and if it is positive, we raise * it. * * I'm afraid this still leads to odd looking interrupt counts: * the totals are all roughly equal, but the individual ones * look rather skewed. * * FIXME: This algorithm is total crap when mixed with SMP * affinity code since we now try to even up the interrupt * counts when an affinity binding is keeping them on a * particular CPU*/ weight = (vic_intr_count[cpu]*voyager_extended_cpus - vic_intr_total) >> 4; weight += 4; if(weight > 7) weight = 7; if(weight < 0) weight = 0; outb((__u8)weight, VIC_PRIORITY_REGISTER); #ifdef VOYAGER_DEBUG if((vic_tick[cpu] & 0xFFF) == 0) { /* print this message roughly every 25 secs */ printk("VOYAGER SMP: vic_tick[%d] = %lu, weight = %ld\n", cpu, vic_tick[cpu], weight); } #endif } /* setup the profiling timer */ int setup_profiling_timer(unsigned int multiplier) { int i; if ( (!multiplier)) return -EINVAL; /* * Set the new multiplier for each CPU. CPUs don't start using the * new values until the next timer interrupt in which they do process * accounting. */ for (i = 0; i < NR_CPUS; ++i) per_cpu(prof_multiplier, i) = multiplier; return 0; } /* This is a bit of a mess, but forced on us by the genirq changes * there's no genirq handler that really does what voyager wants * so hack it up with the simple IRQ handler */ static void fastcall handle_vic_irq(unsigned int irq, struct irq_desc *desc) { before_handle_vic_irq(irq); handle_simple_irq(irq, desc); after_handle_vic_irq(irq); } /* The CPIs are handled in the per cpu 8259s, so they must be * enabled to be received: FIX: enabling the CPIs in the early * boot sequence interferes with bug checking; enable them later * on in smp_init */ #define VIC_SET_GATE(cpi, vector) \ set_intr_gate((cpi) + VIC_DEFAULT_CPI_BASE, (vector)) #define QIC_SET_GATE(cpi, vector) \ set_intr_gate((cpi) + QIC_DEFAULT_CPI_BASE, (vector)) void __init smp_intr_init(void) { int i; /* initialize the per cpu irq mask to all disabled */ for(i = 0; i < NR_CPUS; i++) vic_irq_mask[i] = 0xFFFF; VIC_SET_GATE(VIC_CPI_LEVEL0, vic_cpi_interrupt); VIC_SET_GATE(VIC_SYS_INT, vic_sys_interrupt); VIC_SET_GATE(VIC_CMN_INT, vic_cmn_interrupt); QIC_SET_GATE(QIC_TIMER_CPI, qic_timer_interrupt); QIC_SET_GATE(QIC_INVALIDATE_CPI, qic_invalidate_interrupt); QIC_SET_GATE(QIC_RESCHEDULE_CPI, qic_reschedule_interrupt); QIC_SET_GATE(QIC_ENABLE_IRQ_CPI, qic_enable_irq_interrupt); QIC_SET_GATE(QIC_CALL_FUNCTION_CPI, qic_call_function_interrupt); /* now put the VIC descriptor into the first 48 IRQs * * This is for later: first 16 correspond to PC IRQs; next 16 * are Primary MC IRQs and final 16 are Secondary MC IRQs */ for(i = 0; i < 48; i++) set_irq_chip_and_handler(i, &vic_chip, handle_vic_irq); } /* send a CPI at level cpi to a set of cpus in cpuset (set 1 bit per * processor to receive CPI */ static void send_CPI(__u32 cpuset, __u8 cpi) { int cpu; __u32 quad_cpuset = (cpuset & voyager_quad_processors); if(cpi < VIC_START_FAKE_CPI) { /* fake CPI are only used for booting, so send to the * extended quads as well---Quads must be VIC booted */ outb((__u8)(cpuset), VIC_CPI_Registers[cpi]); return; } if(quad_cpuset) send_QIC_CPI(quad_cpuset, cpi); cpuset &= ~quad_cpuset; cpuset &= 0xff; /* only first 8 CPUs vaild for VIC CPI */ if(cpuset == 0) return; for_each_online_cpu(cpu) { if(cpuset & (1<qic_cpi[cpi].cpi; } static void ack_special_QIC_CPI(__u8 cpi) { switch(cpi) { case VIC_CMN_INT: outb(QIC_CMN_INT, QIC_INTERRUPT_CLEAR0); break; case VIC_SYS_INT: outb(QIC_SYS_INT, QIC_INTERRUPT_CLEAR0); break; } /* also clear at the VIC, just in case (nop for non-extended proc) */ ack_VIC_CPI(cpi); } /* Acknowledge receipt of CPI in the VIC (essentially an EOI) */ static void ack_VIC_CPI(__u8 cpi) { #ifdef VOYAGER_DEBUG unsigned long flags; __u16 isr; __u8 cpu = smp_processor_id(); local_irq_save(flags); isr = vic_read_isr(); if((isr & (1<<(cpi &7))) == 0) { printk("VOYAGER SMP: CPU%d lost CPI%d\n", cpu, cpi); } #endif /* send specific EOI; the two system interrupts have * bit 4 set for a separate vector but behave as the * corresponding 3 bit intr */ outb_p(0x60|(cpi & 7),0x20); #ifdef VOYAGER_DEBUG if((vic_read_isr() & (1<<(cpi &7))) != 0) { printk("VOYAGER SMP: CPU%d still asserting CPI%d\n", cpu, cpi); } local_irq_restore(flags); #endif } /* cribbed with thanks from irq.c */ #define __byte(x,y) (((unsigned char *)&(y))[x]) #define cached_21(cpu) (__byte(0,vic_irq_mask[cpu])) #define cached_A1(cpu) (__byte(1,vic_irq_mask[cpu])) static unsigned int startup_vic_irq(unsigned int irq) { unmask_vic_irq(irq); return 0; } /* The enable and disable routines. This is where we run into * conflicting architectural philosophy. Fundamentally, the voyager * architecture does not expect to have to disable interrupts globally * (the IRQ controllers belong to each CPU). The processor masquerade * which is used to start the system shouldn't be used in a running OS * since it will cause great confusion if two separate CPUs drive to * the same IRQ controller (I know, I've tried it). * * The solution is a variant on the NCR lazy SPL design: * * 1) To disable an interrupt, do nothing (other than set the * IRQ_DISABLED flag). This dares the interrupt actually to arrive. * * 2) If the interrupt dares to come in, raise the local mask against * it (this will result in all the CPU masks being raised * eventually). * * 3) To enable the interrupt, lower the mask on the local CPU and * broadcast an Interrupt enable CPI which causes all other CPUs to * adjust their masks accordingly. */ static void unmask_vic_irq(unsigned int irq) { /* linux doesn't to processor-irq affinity, so enable on * all CPUs we know about */ int cpu = smp_processor_id(), real_cpu; __u16 mask = (1<status |= IRQ_REPLAY | IRQ_INPROGRESS; } else if(desc->status & IRQ_DISABLED) { /* Damn, the interrupt actually arrived, do the lazy * disable thing. The interrupt routine in irq.c will * not handle a IRQ_DISABLED interrupt, so nothing more * need be done here */ VDEBUG(("VOYAGER DEBUG: lazy disable of irq %d on CPU %d\n", irq, cpu)); disable_local_vic_irq(irq); desc->status |= IRQ_REPLAY; } else { desc->status &= ~IRQ_REPLAY; } _raw_spin_unlock(&vic_irq_lock); } /* Finish the VIC interrupt: basically mask */ static void after_handle_vic_irq(unsigned int irq) { irq_desc_t *desc = irq_desc + irq; _raw_spin_lock(&vic_irq_lock); { unsigned int status = desc->status & ~IRQ_INPROGRESS; #ifdef VOYAGER_DEBUG __u16 isr; #endif desc->status = status; if ((status & IRQ_DISABLED)) disable_local_vic_irq(irq); #ifdef VOYAGER_DEBUG /* DEBUG: before we ack, check what's in progress */ isr = vic_read_isr(); if((isr & (1<status &= ~(IRQ_REPLAY | IRQ_INPROGRESS); } #ifdef VOYAGER_DEBUG isr = vic_read_isr(); if((isr & (1<= 32) /* You can only have 32 interrupts in a voyager system * (and 32 only if you have a secondary microchannel * bus) */ return; for_each_online_cpu(cpu) { unsigned long cpu_mask = 1 << cpu; if(cpu_mask & real_mask) { /* enable the interrupt for this cpu */ cpu_irq_affinity[cpu] |= irq_mask; } else { /* disable the interrupt for this cpu */ cpu_irq_affinity[cpu] &= ~irq_mask; } } /* this is magic, we now have the correct affinity maps, so * enable the interrupt. This will send an enable CPI to * those cpu's who need to enable it in their local masks, * causing them to correct for the new affinity . If the * interrupt is currently globally disabled, it will simply be * disabled again as it comes in (voyager lazy disable). If * the affinity map is tightened to disable the interrupt on a * cpu, it will be pushed off when it comes in */ unmask_vic_irq(irq); } static void ack_vic_irq(unsigned int irq) { if (irq & 8) { outb(0x62,0x20); /* Specific EOI to cascade */ outb(0x60|(irq & 7),0xA0); } else { outb(0x60 | (irq & 7),0x20); } } /* enable the CPIs. In the VIC, the CPIs are delivered by the 8259 * but are not vectored by it. This means that the 8259 mask must be * lowered to receive them */ static __init void vic_enable_cpi(void) { __u8 cpu = smp_processor_id(); /* just take a copy of the current mask (nop for boot cpu) */ vic_irq_mask[cpu] = vic_irq_mask[boot_cpu_id]; enable_local_vic_irq(VIC_CPI_LEVEL0); enable_local_vic_irq(VIC_CPI_LEVEL1); /* for sys int and cmn int */ enable_local_vic_irq(7); if(is_cpu_quad()) { outb(QIC_DEFAULT_MASK0, QIC_MASK_REGISTER0); outb(QIC_CPI_ENABLE, QIC_MASK_REGISTER1); VDEBUG(("VOYAGER SMP: QIC ENABLE CPI: CPU%d: MASK 0x%x\n", cpu, QIC_CPI_ENABLE)); } VDEBUG(("VOYAGER SMP: ENABLE CPI: CPU%d: MASK 0x%x\n", cpu, vic_irq_mask[cpu])); } void voyager_smp_dump() { int old_cpu = smp_processor_id(), cpu; /* dump the interrupt masks of each processor */ for_each_online_cpu(cpu) { __u16 imr, isr, irr; unsigned long flags; local_irq_save(flags); outb(VIC_CPU_MASQUERADE_ENABLE | cpu, VIC_PROCESSOR_ID); imr = (inb(0xa1) << 8) | inb(0x21); outb(0x0a, 0xa0); irr = inb(0xa0) << 8; outb(0x0a, 0x20); irr |= inb(0x20); outb(0x0b, 0xa0); isr = inb(0xa0) << 8; outb(0x0b, 0x20); isr |= inb(0x20); outb(old_cpu, VIC_PROCESSOR_ID); local_irq_restore(flags); printk("\tCPU%d: mask=0x%x, IMR=0x%x, IRR=0x%x, ISR=0x%x\n", cpu, vic_irq_mask[cpu], imr, irr, isr); #if 0 /* These lines are put in to try to unstick an un ack'd irq */ if(isr != 0) { int irq; for(irq=0; irq<16; irq++) { if(isr & (1<cpu = hard_smp_processor_id(); write_pda(cpu_number, hard_smp_processor_id()); }