1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939 |
- /* -*- 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 <linux/config.h>
- #include <linux/module.h>
- #include <linux/mm.h>
- #include <linux/kernel_stat.h>
- #include <linux/delay.h>
- #include <linux/mc146818rtc.h>
- #include <linux/cache.h>
- #include <linux/interrupt.h>
- #include <linux/smp_lock.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/bootmem.h>
- #include <linux/completion.h>
- #include <asm/desc.h>
- #include <asm/voyager.h>
- #include <asm/vic.h>
- #include <asm/mtrr.h>
- #include <asm/pgalloc.h>
- #include <asm/tlbflush.h>
- #include <asm/arch_hooks.h>
- #include <linux/irq.h>
- /* 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 enable_vic_irq(unsigned int irq);
- static void disable_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);
- /* 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<<cpu)) {
- #ifdef VOYAGER_DEBUG
- if(!cpu_isset(cpu, cpu_online_map))
- VDEBUG(("CPU%d sending cpi %d to CPU%d not in cpu_online_map\n", hard_smp_processor_id(), cpi, cpu));
- #endif
- send_one_QIC_CPI(cpu, cpi - QIC_CPI_OFFSET);
- }
- }
- }
- static inline void
- wrapper_smp_local_timer_interrupt(struct pt_regs *regs)
- {
- irq_enter();
- smp_local_timer_interrupt(regs);
- irq_exit();
- }
- static inline void
- send_one_CPI(__u8 cpu, __u8 cpi)
- {
- if(voyager_quad_processors & (1<<cpu))
- send_one_QIC_CPI(cpu, cpi - QIC_CPI_OFFSET);
- else
- send_CPI(1<<cpu, cpi);
- }
- static inline void
- send_CPI_allbutself(__u8 cpi)
- {
- __u8 cpu = smp_processor_id();
- __u32 mask = cpus_addr(cpu_online_map)[0] & ~(1 << cpu);
- send_CPI(mask, cpi);
- }
- static inline int
- is_cpu_quad(void)
- {
- __u8 cpumask = inb(VIC_PROC_WHO_AM_I);
- return ((cpumask & QUAD_IDENTIFIER) == QUAD_IDENTIFIER);
- }
- static inline int
- is_cpu_extended(void)
- {
- __u8 cpu = hard_smp_processor_id();
- return(voyager_extended_vic_processors & (1<<cpu));
- }
- static inline int
- is_cpu_vic_boot(void)
- {
- __u8 cpu = hard_smp_processor_id();
- return(voyager_extended_vic_processors
- & voyager_allowed_boot_processors & (1<<cpu));
- }
- static inline void
- ack_CPI(__u8 cpi)
- {
- switch(cpi) {
- case VIC_CPU_BOOT_CPI:
- if(is_cpu_quad() && !is_cpu_vic_boot())
- ack_QIC_CPI(cpi);
- else
- ack_VIC_CPI(cpi);
- break;
- case VIC_SYS_INT:
- case VIC_CMN_INT:
- /* These are slightly strange. Even on the Quad card,
- * They are vectored as VIC CPIs */
- if(is_cpu_quad())
- ack_special_QIC_CPI(cpi);
- else
- ack_VIC_CPI(cpi);
- break;
- default:
- printk("VOYAGER ERROR: CPI%d is in common CPI code\n", cpi);
- break;
- }
- }
- /* local variables */
- /* The VIC IRQ descriptors -- these look almost identical to the
- * 8259 IRQs except that masks and things must be kept per processor
- */
- static struct hw_interrupt_type vic_irq_type = {
- .typename = "VIC-level",
- .startup = startup_vic_irq,
- .shutdown = disable_vic_irq,
- .enable = enable_vic_irq,
- .disable = disable_vic_irq,
- .ack = before_handle_vic_irq,
- .end = after_handle_vic_irq,
- .set_affinity = set_vic_irq_affinity,
- };
- /* used to count up as CPUs are brought on line (starts at 0) */
- static int cpucount = 0;
- /* steal a page from the bottom of memory for the trampoline and
- * squirrel its address away here. This will be in kernel virtual
- * space */
- static __u32 trampoline_base;
- /* The per cpu profile stuff - used in smp_local_timer_interrupt */
- static DEFINE_PER_CPU(int, prof_multiplier) = 1;
- static DEFINE_PER_CPU(int, prof_old_multiplier) = 1;
- static DEFINE_PER_CPU(int, prof_counter) = 1;
- /* the map used to check if a CPU has booted */
- static __u32 cpu_booted_map;
- /* the synchronize flag used to hold all secondary CPUs spinning in
- * a tight loop until the boot sequence is ready for them */
- static cpumask_t smp_commenced_mask = CPU_MASK_NONE;
- /* This is for the new dynamic CPU boot code */
- cpumask_t cpu_callin_map = CPU_MASK_NONE;
- cpumask_t cpu_callout_map = CPU_MASK_NONE;
- EXPORT_SYMBOL(cpu_callout_map);
- cpumask_t cpu_possible_map = CPU_MASK_ALL;
- EXPORT_SYMBOL(cpu_possible_map);
- /* The per processor IRQ masks (these are usually kept in sync) */
- static __u16 vic_irq_mask[NR_CPUS] __cacheline_aligned;
- /* the list of IRQs to be enabled by the VIC_ENABLE_IRQ_CPI */
- static __u16 vic_irq_enable_mask[NR_CPUS] __cacheline_aligned = { 0 };
- /* Lock for enable/disable of VIC interrupts */
- static __cacheline_aligned DEFINE_SPINLOCK(vic_irq_lock);
- /* The boot processor is correctly set up in PC mode when it
- * comes up, but the secondaries need their master/slave 8259
- * pairs initializing correctly */
- /* Interrupt counters (per cpu) and total - used to try to
- * even up the interrupt handling routines */
- static long vic_intr_total = 0;
- static long vic_intr_count[NR_CPUS] __cacheline_aligned = { 0 };
- static unsigned long vic_tick[NR_CPUS] __cacheline_aligned = { 0 };
- /* Since we can only use CPI0, we fake all the other CPIs */
- static unsigned long vic_cpi_mailbox[NR_CPUS] __cacheline_aligned;
- /* debugging routine to read the isr of the cpu's pic */
- static inline __u16
- vic_read_isr(void)
- {
- __u16 isr;
- outb(0x0b, 0xa0);
- isr = inb(0xa0) << 8;
- outb(0x0b, 0x20);
- isr |= inb(0x20);
- return isr;
- }
- static __init void
- qic_setup(void)
- {
- if(!is_cpu_quad()) {
- /* not a quad, no setup */
- return;
- }
- outb(QIC_DEFAULT_MASK0, QIC_MASK_REGISTER0);
- outb(QIC_CPI_ENABLE, QIC_MASK_REGISTER1);
-
- if(is_cpu_extended()) {
- /* the QIC duplicate of the VIC base register */
- outb(VIC_DEFAULT_CPI_BASE, QIC_VIC_CPI_BASE_REGISTER);
- outb(QIC_DEFAULT_CPI_BASE, QIC_CPI_BASE_REGISTER);
- /* FIXME: should set up the QIC timer and memory parity
- * error vectors here */
- }
- }
- static __init void
- vic_setup_pic(void)
- {
- outb(1, VIC_REDIRECT_REGISTER_1);
- /* clear the claim registers for dynamic routing */
- outb(0, VIC_CLAIM_REGISTER_0);
- outb(0, VIC_CLAIM_REGISTER_1);
- outb(0, VIC_PRIORITY_REGISTER);
- /* Set the Primary and Secondary Microchannel vector
- * bases to be the same as the ordinary interrupts
- *
- * FIXME: This would be more efficient using separate
- * vectors. */
- outb(FIRST_EXTERNAL_VECTOR, VIC_PRIMARY_MC_BASE);
- outb(FIRST_EXTERNAL_VECTOR, VIC_SECONDARY_MC_BASE);
- /* Now initiallise the master PIC belonging to this CPU by
- * sending the four ICWs */
- /* ICW1: level triggered, ICW4 needed */
- outb(0x19, 0x20);
- /* ICW2: vector base */
- outb(FIRST_EXTERNAL_VECTOR, 0x21);
- /* ICW3: slave at line 2 */
- outb(0x04, 0x21);
- /* ICW4: 8086 mode */
- outb(0x01, 0x21);
- /* now the same for the slave PIC */
- /* ICW1: level trigger, ICW4 needed */
- outb(0x19, 0xA0);
- /* ICW2: slave vector base */
- outb(FIRST_EXTERNAL_VECTOR + 8, 0xA1);
-
- /* ICW3: slave ID */
- outb(0x02, 0xA1);
- /* ICW4: 8086 mode */
- outb(0x01, 0xA1);
- }
- static void
- do_quad_bootstrap(void)
- {
- if(is_cpu_quad() && is_cpu_vic_boot()) {
- int i;
- unsigned long flags;
- __u8 cpuid = hard_smp_processor_id();
- local_irq_save(flags);
- for(i = 0; i<4; i++) {
- /* FIXME: this would be >>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; i<NR_CPUS; i++) {
- cpu_irq_affinity[i] = ~0;
- }
- cpu_online_map = cpumask_of_cpu(boot_cpu_id);
- /* The boot CPU must be extended */
- voyager_extended_vic_processors = 1<<boot_cpu_id;
- /* initially, all of the first 8 cpu's can boot */
- voyager_allowed_boot_processors = 0xff;
- /* set up everything for just this CPU, we can alter
- * this as we start the other CPUs later */
- /* now get the CPU disposition from the extended CMOS */
- cpus_addr(phys_cpu_present_map)[0] = voyager_extended_cmos_read(VOYAGER_PROCESSOR_PRESENT_MASK);
- cpus_addr(phys_cpu_present_map)[0] |= voyager_extended_cmos_read(VOYAGER_PROCESSOR_PRESENT_MASK + 1) << 8;
- cpus_addr(phys_cpu_present_map)[0] |= voyager_extended_cmos_read(VOYAGER_PROCESSOR_PRESENT_MASK + 2) << 16;
- cpus_addr(phys_cpu_present_map)[0] |= voyager_extended_cmos_read(VOYAGER_PROCESSOR_PRESENT_MASK + 3) << 24;
- printk("VOYAGER SMP: phys_cpu_present_map = 0x%lx\n", cpus_addr(phys_cpu_present_map)[0]);
- /* Here we set up the VIC to enable SMP */
- /* enable the CPIs by writing the base vector to their register */
- outb(VIC_DEFAULT_CPI_BASE, VIC_CPI_BASE_REGISTER);
- outb(1, VIC_REDIRECT_REGISTER_1);
- /* set the claim registers for static routing --- Boot CPU gets
- * all interrupts untill all other CPUs started */
- outb(0xff, VIC_CLAIM_REGISTER_0);
- outb(0xff, VIC_CLAIM_REGISTER_1);
- /* Set the Primary and Secondary Microchannel vector
- * bases to be the same as the ordinary interrupts
- *
- * FIXME: This would be more efficient using separate
- * vectors. */
- outb(FIRST_EXTERNAL_VECTOR, VIC_PRIMARY_MC_BASE);
- outb(FIRST_EXTERNAL_VECTOR, VIC_SECONDARY_MC_BASE);
- /* Finally tell the firmware that we're driving */
- outb(inb(VOYAGER_SUS_IN_CONTROL_PORT) | VOYAGER_IN_CONTROL_FLAG,
- VOYAGER_SUS_IN_CONTROL_PORT);
- current_thread_info()->cpu = 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);
- 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<<cpu) & voyager_quad_processors
- & ~( voyager_extended_vic_processors
- & voyager_allowed_boot_processors);
- /* For the 486, we can't use the 4Mb page table trick, so
- * must map a region of memory */
- #ifdef CONFIG_M486
- int i;
- unsigned long *page_table_copies = (unsigned long *)
- __get_free_page(GFP_KERNEL);
- #endif
- pgd_t orig_swapper_pg_dir0;
- /* This is an area in head.S which was used to set up the
- * initial kernel stack. We need to alter this to give the
- * booting CPU a new stack (taken from its idle process) */
- extern struct {
- __u8 *esp;
- unsigned short ss;
- } stack_start;
- /* This is the format of the CPI IDT gate (in real mode) which
- * we're hijacking to boot the CPU */
- union IDTFormat {
- struct seg {
- __u16 Offset;
- __u16 Segment;
- } idt;
- __u32 val;
- } hijack_source;
- __u32 *hijack_vector;
- __u32 start_phys_address = setup_trampoline();
- /* There's a clever trick to this: The linux trampoline is
- * compiled to begin at absolute location zero, so make the
- * address zero but have the data segment selector compensate
- * for the actual address */
- hijack_source.idt.Offset = start_phys_address & 0x000F;
- hijack_source.idt.Segment = (start_phys_address >> 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;
- 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));
- /* set the original swapper_pg_dir[0] to map 0 to 4Mb transparently
- * (so that the booting CPU can find start_32 */
- orig_swapper_pg_dir0 = swapper_pg_dir[0];
- #ifdef CONFIG_M486
- if(page_table_copies == NULL)
- panic("No free memory for 486 page tables\n");
- for(i = 0; i < PAGE_SIZE/sizeof(unsigned long); i++)
- page_table_copies[i] = (i * PAGE_SIZE)
- | _PAGE_RW | _PAGE_USER | _PAGE_PRESENT;
- ((unsigned long *)swapper_pg_dir)[0] =
- ((virt_to_phys(page_table_copies)) & PAGE_MASK)
- | _PAGE_RW | _PAGE_USER | _PAGE_PRESENT;
- #else
- ((unsigned long *)swapper_pg_dir)[0] =
- (virt_to_phys(pg0) & PAGE_MASK)
- | _PAGE_RW | _PAGE_USER | _PAGE_PRESENT;
- #endif
- 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), VIC_CPU_BOOT_CPI);
- }
- cpu_booted_map = 0;
- local_irq_restore(flags);
- /* now wait for it to become ready (or timeout) */
- for(timeout = 0; timeout < 50000; timeout++) {
- if(cpu_booted_map)
- break;
- udelay(100);
- }
- /* reset the page table */
- swapper_pg_dir[0] = orig_swapper_pg_dir0;
- local_flush_tlb();
- #ifdef CONFIG_M486
- free_page((unsigned long)page_table_copies);
- #endif
-
- if (cpu_booted_map) {
- VDEBUG(("CPU%d: Booted successfully, back in CPU %d\n",
- cpu, smp_processor_id()));
-
- printk("CPU%d: ", cpu);
- print_cpu_info(&cpu_data[cpu]);
- wmb();
- cpu_set(cpu, cpu_callout_map);
- }
- else {
- printk("CPU%d FAILED TO BOOT: ", cpu);
- if (*((volatile unsigned char *)phys_to_virt(start_phys_address))==0xA5)
- printk("Stuck.\n");
- else
- printk("Not responding.\n");
-
- cpucount--;
- }
- }
- void __init
- smp_boot_cpus(void)
- {
- int i;
- /* CAT BUS initialisation must be done after the memory */
- /* FIXME: The L4 has a catbus too, it just needs to be
- * accessed in a totally different way */
- if(voyager_level == 5) {
- voyager_cat_init();
- /* now that the cat has probed the Voyager System Bus, sanity
- * check the cpu map */
- if( ((voyager_quad_processors | voyager_extended_vic_processors)
- & cpus_addr(phys_cpu_present_map)[0]) != cpus_addr(phys_cpu_present_map)[0]) {
- /* should panic */
- printk("\n\n***WARNING*** Sanity check of CPU present map FAILED\n");
- }
- } else if(voyager_level == 4)
- voyager_extended_vic_processors = cpus_addr(phys_cpu_present_map)[0];
- /* this sets up the idle task to run on the current cpu */
- voyager_extended_cpus = 1;
- /* Remove the global_irq_holder setting, it triggers a BUG() on
- * schedule at the moment */
- //global_irq_holder = boot_cpu_id;
- /* FIXME: Need to do something about this but currently only works
- * on CPUs with a tsc which none of mine have.
- smp_tune_scheduling();
- */
- smp_store_cpu_info(boot_cpu_id);
- printk("CPU%d: ", boot_cpu_id);
- print_cpu_info(&cpu_data[boot_cpu_id]);
- if(is_cpu_quad()) {
- /* booting on a Quad CPU */
- printk("VOYAGER SMP: Boot CPU is Quad\n");
- qic_setup();
- do_quad_bootstrap();
- }
- /* enable our own CPIs */
- vic_enable_cpi();
- cpu_set(boot_cpu_id, cpu_online_map);
- cpu_set(boot_cpu_id, cpu_callout_map);
-
- /* loop over all the extended VIC CPUs and boot them. The
- * Quad CPUs must be bootstrapped by their extended VIC cpu */
- for(i = 0; i < NR_CPUS; i++) {
- if(i == boot_cpu_id || !cpu_isset(i, phys_cpu_present_map))
- continue;
- do_boot_cpu(i);
- /* This udelay seems to be needed for the Quad boots
- * don't remove unless you know what you're doing */
- udelay(1000);
- }
- /* we could compute the total bogomips here, but why bother?,
- * Code added from smpboot.c */
- {
- unsigned long bogosum = 0;
- for (i = 0; i < NR_CPUS; i++)
- if (cpu_isset(i, cpu_online_map))
- bogosum += cpu_data[i].loops_per_jiffy;
- printk(KERN_INFO "Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
- cpucount+1,
- bogosum/(500000/HZ),
- (bogosum/(5000/HZ))%100);
- }
- voyager_extended_cpus = hweight32(voyager_extended_vic_processors);
- printk("VOYAGER: Extended (interrupt handling CPUs): %d, non-extended: %d\n", voyager_extended_cpus, num_booting_cpus() - voyager_extended_cpus);
- /* that's it, switch to symmetric mode */
- outb(0, VIC_PRIORITY_REGISTER);
- outb(0, VIC_CLAIM_REGISTER_0);
- outb(0, VIC_CLAIM_REGISTER_1);
-
- VDEBUG(("VOYAGER SMP: Booted with %d CPUs\n", num_booting_cpus()));
- }
- /* Reload the secondary CPUs task structure (this function does not
- * return ) */
- void __init
- initialize_secondary(void)
- {
- #if 0
- // AC kernels only
- set_current(hard_get_current());
- #endif
- /*
- * 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<<irq))
- enable_local_vic_irq(irq);
- }
- vic_irq_enable_mask[cpu] = 0;
- spin_unlock(&vic_irq_lock);
- put_cpu_no_resched();
- }
-
- /*
- * CPU halt call-back
- */
- static void
- smp_stop_cpu_function(void *dummy)
- {
- VDEBUG(("VOYAGER SMP: CPU%d is STOPPING\n", smp_processor_id()));
- cpu_clear(smp_processor_id(), cpu_online_map);
- local_irq_disable();
- for(;;)
- halt();
- }
- static DEFINE_SPINLOCK(call_lock);
- struct call_data_struct {
- void (*func) (void *info);
- void *info;
- volatile unsigned long started;
- volatile unsigned long finished;
- int wait;
- };
- static struct call_data_struct * call_data;
- /* execute a thread on a new CPU. The function to be called must be
- * previously set up. This is used to schedule a function for
- * execution on all CPU's - set up the function then broadcast a
- * function_interrupt CPI to come here on each CPU */
- static void
- smp_call_function_interrupt(void)
- {
- void (*func) (void *info) = call_data->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);
- }
- }
- /* Call this function on all CPUs using the function_interrupt above
- <func> The function to run. This must be fast and non-blocking.
- <info> An arbitrary pointer to pass to the function.
- <retry> If true, keep retrying until ready.
- <wait> 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 <<func>> or are or have executed.
- */
- int
- smp_call_function (void (*func) (void *info), void *info, int retry,
- int wait)
- {
- struct call_data_struct data;
- __u32 mask = cpus_addr(cpu_online_map)[0];
- mask &= ~(1<<smp_processor_id());
- if (!mask)
- return 0;
- /* Can deadlock when called with interrupts disabled */
- WARN_ON(irqs_disabled());
- data.func = func;
- data.info = info;
- data.started = mask;
- data.wait = wait;
- if (wait)
- data.finished = mask;
- spin_lock(&call_lock);
- call_data = &data;
- wmb();
- /* Send a message to all other CPUs and wait for them to respond */
- send_CPI_allbutself(VIC_CALL_FUNCTION_CPI);
- /* Wait for response */
- while (data.started)
- barrier();
- if (wait)
- while (data.finished)
- barrier();
- spin_unlock(&call_lock);
- return 0;
- }
- EXPORT_SYMBOL(smp_call_function);
- /* 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)
- {
- wrapper_smp_local_timer_interrupt(regs);
- }
- /* All of the QUAD interrupt GATES */
- fastcall void
- smp_qic_timer_interrupt(struct pt_regs *regs)
- {
- ack_QIC_CPI(QIC_TIMER_CPI);
- wrapper_smp_local_timer_interrupt(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)
- {
- __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(regs);
- 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();
- }
- 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<<i))
- return i;
- }
- printk("** WARNING ** Illegal cpuid returned by VIC: %d", cpumask);
- return 0;
- }
- /* broadcast a halt to all other CPUs */
- void
- smp_send_stop(void)
- {
- smp_call_function(smp_stop_cpu_function, NULL, 1, 1);
- }
- /* this function is triggered in time.c when a clock tick fires
- * we need to re-broadcast the tick to all CPUs */
- void
- smp_vic_timer_interrupt(struct pt_regs *regs)
- {
- send_CPI_allbutself(VIC_TIMER_CPI);
- smp_local_timer_interrupt(regs);
- }
- /* local (per CPU) timer interrupt. It does both profiling and
- * process statistics/rescheduling.
- *
- * We do profiling in every local tick, statistics/rescheduling
- * happen only every 'profiling multiplier' ticks. The default
- * multiplier is 1 and it can be changed by writing the new multiplier
- * value into /proc/profile.
- */
- void
- smp_local_timer_interrupt(struct pt_regs * regs)
- {
- int cpu = smp_processor_id();
- long weight;
- profile_tick(CPU_PROFILING, regs);
- if (--per_cpu(prof_counter, cpu) <= 0) {
- /*
- * The multiplier may have changed since the last time we got
- * to this point as a result of the user writing to
- * /proc/profile. In this case we need to adjust the APIC
- * timer accordingly.
- *
- * Interrupts are already masked off at this point.
- */
- per_cpu(prof_counter,cpu) = per_cpu(prof_multiplier, cpu);
- if (per_cpu(prof_counter, cpu) !=
- per_cpu(prof_old_multiplier, cpu)) {
- /* FIXME: need to update the vic timer tick here */
- per_cpu(prof_old_multiplier, cpu) =
- per_cpu(prof_counter, cpu);
- }
- update_process_times(user_mode_vm(regs));
- }
- if( ((1<<cpu) & voyager_extended_vic_processors) == 0)
- /* only extended VIC processors participate in
- * interrupt distribution */
- return;
- /*
- * We take the 'long' return path, and there every subsystem
- * grabs the apropriate locks (kernel lock/ irq lock).
- *
- * we might want to decouple profiling from the 'long path',
- * and do the profiling totally in assembly.
- *
- * Currently this isn't too much of an issue (performance wise),
- * we can take more than 100K local irqs per second on a 100 MHz P5.
- */
- if((++vic_tick[cpu] & 0x7) != 0)
- return;
- /* get here every 16 ticks (about every 1/6 of a second) */
- /* Change our priority to give someone else a chance at getting
- * the IRQ. The algorithm goes like this:
- *
- * In the VIC, the dynamically routed interrupt is always
- * handled by the lowest priority eligible (i.e. receiving
- * interrupts) CPU. If >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;
- }
- /* 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++)
- irq_desc[i].handler = &vic_irq_type;
- }
- /* 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<<cpu))
- set_bit(cpi, &vic_cpi_mailbox[cpu]);
- }
- if(cpuset)
- outb((__u8)cpuset, VIC_CPI_Registers[VIC_CPI_LEVEL0]);
- }
- /* Acknowledge receipt of CPI in the QIC, clear in QIC hardware and
- * set the cache line to shared by reading it.
- *
- * DON'T make this inline otherwise the cache line read will be
- * optimised away
- * */
- static int
- ack_QIC_CPI(__u8 cpi) {
- __u8 cpu = hard_smp_processor_id();
- cpi &= 7;
- outb(1<<cpi, QIC_INTERRUPT_CLEAR1);
- return voyager_quad_cpi_addr[cpu]->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)
- {
- enable_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
- enable_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<<irq);
- __u32 processorList = 0;
- unsigned long flags;
- VDEBUG(("VOYAGER: enable_vic_irq(%d) CPU%d affinity 0x%lx\n",
- irq, cpu, cpu_irq_affinity[cpu]));
- spin_lock_irqsave(&vic_irq_lock, flags);
- for_each_online_cpu(real_cpu) {
- if(!(voyager_extended_vic_processors & (1<<real_cpu)))
- continue;
- if(!(cpu_irq_affinity[real_cpu] & mask)) {
- /* irq has no affinity for this CPU, ignore */
- continue;
- }
- if(real_cpu == cpu) {
- enable_local_vic_irq(irq);
- }
- else if(vic_irq_mask[real_cpu] & mask) {
- vic_irq_enable_mask[real_cpu] |= mask;
- processorList |= (1<<real_cpu);
- }
- }
- spin_unlock_irqrestore(&vic_irq_lock, flags);
- if(processorList)
- send_CPI(processorList, VIC_ENABLE_IRQ_CPI);
- }
- static void
- disable_vic_irq(unsigned int irq)
- {
- /* lazy disable, do nothing */
- }
- static void
- enable_local_vic_irq(unsigned int irq)
- {
- __u8 cpu = smp_processor_id();
- __u16 mask = ~(1 << irq);
- __u16 old_mask = vic_irq_mask[cpu];
- vic_irq_mask[cpu] &= mask;
- if(vic_irq_mask[cpu] == old_mask)
- return;
- VDEBUG(("VOYAGER DEBUG: Enabling irq %d in hardware on CPU %d\n",
- irq, cpu));
- if (irq & 8) {
- outb_p(cached_A1(cpu),0xA1);
- (void)inb_p(0xA1);
- }
- else {
- outb_p(cached_21(cpu),0x21);
- (void)inb_p(0x21);
- }
- }
- static void
- disable_local_vic_irq(unsigned int irq)
- {
- __u8 cpu = smp_processor_id();
- __u16 mask = (1 << irq);
- __u16 old_mask = vic_irq_mask[cpu];
- if(irq == 7)
- return;
- vic_irq_mask[cpu] |= mask;
- if(old_mask == vic_irq_mask[cpu])
- return;
- VDEBUG(("VOYAGER DEBUG: Disabling irq %d in hardware on CPU %d\n",
- irq, cpu));
- if (irq & 8) {
- outb_p(cached_A1(cpu),0xA1);
- (void)inb_p(0xA1);
- }
- else {
- outb_p(cached_21(cpu),0x21);
- (void)inb_p(0x21);
- }
- }
- /* The VIC is level triggered, so the ack can only be issued after the
- * interrupt completes. However, we do Voyager lazy interrupt
- * handling here: It is an extremely expensive operation to mask an
- * interrupt in the vic, so we merely set a flag (IRQ_DISABLED). If
- * this interrupt actually comes in, then we mask and ack here to push
- * the interrupt off to another CPU */
- static void
- before_handle_vic_irq(unsigned int irq)
- {
- irq_desc_t *desc = irq_desc + irq;
- __u8 cpu = smp_processor_id();
- _raw_spin_lock(&vic_irq_lock);
- vic_intr_total++;
- vic_intr_count[cpu]++;
- if(!(cpu_irq_affinity[cpu] & (1<<irq))) {
- /* The irq is not in our affinity mask, push it off
- * onto another CPU */
- VDEBUG(("VOYAGER DEBUG: affinity triggered disable of irq %d on cpu %d\n",
- irq, cpu));
- disable_local_vic_irq(irq);
- /* set IRQ_INPROGRESS to prevent the handler in irq.c from
- * actually calling the interrupt routine */
- desc->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<<irq) && !(status & IRQ_REPLAY)) == 0) {
- int i;
- __u8 cpu = smp_processor_id();
- __u8 real_cpu;
- int mask; /* Um... initialize me??? --RR */
- printk("VOYAGER SMP: CPU%d lost interrupt %d\n",
- cpu, irq);
- for_each_cpu(real_cpu, mask) {
- outb(VIC_CPU_MASQUERADE_ENABLE | real_cpu,
- VIC_PROCESSOR_ID);
- isr = vic_read_isr();
- if(isr & (1<<irq)) {
- printk("VOYAGER SMP: CPU%d ack irq %d\n",
- real_cpu, irq);
- ack_vic_irq(irq);
- }
- outb(cpu, VIC_PROCESSOR_ID);
- }
- }
- #endif /* VOYAGER_DEBUG */
- /* as soon as we ack, the interrupt is eligible for
- * receipt by another CPU so everything must be in
- * order here */
- ack_vic_irq(irq);
- if(status & IRQ_REPLAY) {
- /* replay is set if we disable the interrupt
- * in the before_handle_vic_irq() routine, so
- * clear the in progress bit here to allow the
- * next CPU to handle this correctly */
- desc->status &= ~(IRQ_REPLAY | IRQ_INPROGRESS);
- }
- #ifdef VOYAGER_DEBUG
- isr = vic_read_isr();
- if((isr & (1<<irq)) != 0)
- printk("VOYAGER SMP: after_handle_vic_irq() after ack irq=%d, isr=0x%x\n",
- irq, isr);
- #endif /* VOYAGER_DEBUG */
- }
- _raw_spin_unlock(&vic_irq_lock);
- /* All code after this point is out of the main path - the IRQ
- * may be intercepted by another CPU if reasserted */
- }
- /* Linux processor - interrupt affinity manipulations.
- *
- * For each processor, we maintain a 32 bit irq affinity mask.
- * Initially it is set to all 1's so every processor accepts every
- * interrupt. In this call, we change the processor's affinity mask:
- *
- * Change from enable to disable:
- *
- * If the interrupt ever comes in to the processor, we will disable it
- * and ack it to push it off to another CPU, so just accept the mask here.
- *
- * Change from disable to enable:
- *
- * change the mask and then do an interrupt enable CPI to re-enable on
- * the selected processors */
- void
- set_vic_irq_affinity(unsigned int irq, cpumask_t mask)
- {
- /* Only extended processors handle interrupts */
- unsigned long real_mask;
- unsigned long irq_mask = 1 << irq;
- int cpu;
- real_mask = cpus_addr(mask)[0] & voyager_extended_vic_processors;
-
- if(cpus_addr(mask)[0] == 0)
- /* can't have no cpu's to accept the interrupt -- extremely
- * bad things will happen */
- return;
- if(irq == 0)
- /* can't change the affinity of the timer IRQ. This
- * is due to the constraint in the voyager
- * architecture that the CPI also comes in on and IRQ
- * line and we have chosen IRQ0 for this. If you
- * raise the mask on this interrupt, the processor
- * will no-longer be able to accept VIC CPIs */
- return;
- if(irq >= 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 */
- enable_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<<irq)) {
- printk("\tCPU%d: ack irq %d\n",
- cpu, irq);
- local_irq_save(flags);
- outb(VIC_CPU_MASQUERADE_ENABLE | cpu,
- VIC_PROCESSOR_ID);
- ack_vic_irq(irq);
- outb(old_cpu, VIC_PROCESSOR_ID);
- local_irq_restore(flags);
- }
- }
- }
- #endif
- }
- }
- void
- smp_voyager_power_off(void *dummy)
- {
- if(smp_processor_id() == boot_cpu_id)
- voyager_power_off();
- else
- smp_stop_cpu_function(NULL);
- }
- void __init
- smp_prepare_cpus(unsigned int max_cpus)
- {
- /* FIXME: ignore max_cpus for now */
- smp_boot_cpus();
- }
- void __devinit smp_prepare_boot_cpu(void)
- {
- cpu_set(smp_processor_id(), cpu_online_map);
- cpu_set(smp_processor_id(), cpu_callout_map);
- cpu_set(smp_processor_id(), cpu_possible_map);
- }
- int __devinit
- __cpu_up(unsigned int cpu)
- {
- /* This only works at boot for x86. See "rewrite" above. */
- if (cpu_isset(cpu, smp_commenced_mask))
- return -ENOSYS;
- /* In case one didn't come up */
- if (!cpu_isset(cpu, cpu_callin_map))
- return -EIO;
- /* Unleash the CPU! */
- cpu_set(cpu, smp_commenced_mask);
- while (!cpu_isset(cpu, cpu_online_map))
- mb();
- return 0;
- }
- void __init
- smp_cpus_done(unsigned int max_cpus)
- {
- zap_low_mappings();
- }
|