# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet v2.5.44 -> 1.811 # arch/i386/kernel/process.c 1.19.1.13 -> 1.34 # include/asm-i386/desc.h 1.9.1.1 -> 1.11 # arch/i386/kernel/timers/Makefile 1.2 -> 1.3 # arch/i386/boot/setup.S 1.8.2.2 -> 1.15 # arch/i386/kernel/irq.c 1.8.1.14 -> 1.19 # arch/i386/Config.help 1.7.1.16 -> 1.20 # arch/i386/kernel/head.S 1.15.1.2 -> 1.18 # include/asm-i386/irq.h 1.3.1.4 -> 1.8 # arch/i386/kernel/traps.c 1.20.1.12 -> 1.34 # arch/i386/kernel/Makefile 1.4.1.22 -> 1.32 # include/asm-i386/segment.h 1.2 -> 1.3 # arch/i386/kernel/entry.S 1.33.1.5 -> 1.39 # arch/i386/mach-generic/Makefile 1.1.1.4 -> 1.6 # arch/i386/kernel/mpparse.c 1.9.1.16 -> 1.27 # arch/i386/config.in 1.27.1.32 -> 1.61 # arch/i386/Makefile 1.4.1.20 -> 1.21 # arch/i386/kernel/trampoline.S 1.4.1.1 -> 1.6 # arch/i386/pci/common.c 1.21.1.12 -> 1.35 # arch/i386/kernel/timers/timer.c 1.3 -> 1.4 # fs/binfmt_elf.c 1.25.1.4 -> 1.30 # arch/i386/kernel/setup.c 1.40.1.22 -> 1.66 # arch/i386/kernel/smpboot.c 1.15.1.21 -> 1.39 # arch/i386/kernel/time.c 1.5.1.16 -> 1.21 # include/asm-i386/smp.h 1.9.1.8 -> 1.19 # arch/i386/boot/compressed/misc.c 1.7.1.2 -> 1.10 # MAINTAINERS 1.68.1.43 -> 1.94 # include/asm-i386/hardirq.h 1.5.1.10 -> 1.15 # arch/i386/kernel/mca.c 1.4.1.4 -> 1.10 # kernel/sched.c 1.51.1.89 -> 1.81 # arch/i386/kernel/timers/timer_pit.c 1.3.1.1 -> 1.6 # drivers/char/sysrq.c 1.9.1.12 -> 1.19 # arch/i386/kernel/i8259.c 1.7.1.11 -> 1.19 # arch/i386/boot/compressed/head.S 1.2 -> 1.3 # include/asm-i386/hw_irq.h 1.9.1.6 -> 1.16 # arch/i386/kernel/i386_ksyms.c 1.18.1.18 -> 1.34 # arch/i386/pci/irq.c 1.12.1.8 -> 1.21 # arch/i386/kernel/apic.c 1.19.1.8 -> 1.24 # (new) -> 1.2 arch/i386/mach-voyager/entry_arch.h # (new) -> 1.2 include/asm-i386/voyager.h # (new) -> 1.2 arch/i386/mach-voyager/irq_vectors.h # (new) -> 1.8 arch/i386/mach-voyager/voyager_basic.c # (new) -> 1.4 arch/i386/mach-voyager/setup_arch_post.h # (new) -> 1.7 arch/i386/mach-voyager/voyager_cat.c # (new) -> 1.4 include/asm-i386/vic.h # (new) -> 1.4 arch/i386/mach-voyager/do_timer.h # (new) -> 1.27 arch/i386/mach-voyager/voyager_smp.c # (new) -> 1.8 arch/i386/mach-voyager/voyager_thread.c # (new) -> 1.9 arch/i386/mach-voyager/Makefile # (new) -> 1.3 arch/i386/mach-voyager/setup.c # (new) -> 1.2 Documentation/voyager.txt # (new) -> 1.2 arch/i386/mach-voyager/setup_arch_pre.h # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/10/18 torvalds@home.transmeta.com 1.808 # Linux v2.5.44 # -------------------------------------------- # 02/10/18 jejb@mulgrave.(none) 1.809 # Merge mulgrave.(none):/home/jejb/BK/voyager-2.5 # into mulgrave.(none):/home/jejb/BK/voyager-new-2.5 # -------------------------------------------- # 02/10/18 jejb@mulgrave.(none) 1.810 # Merge mulgrave.(none):/home/jejb/BK/timer-2.5 # into mulgrave.(none):/home/jejb/BK/voyager+timer-2.5 # -------------------------------------------- # 02/10/18 jejb@mulgrave.(none) 1.811 # [VOYAGER] correct timer definitions # -------------------------------------------- # diff -Nru a/Documentation/voyager.txt b/Documentation/voyager.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/voyager.txt Sat Oct 19 00:52:59 2002 @@ -0,0 +1,95 @@ +Running Linux on the Voyager Architecture +========================================= + +For full details and current project status, see + +http://www.hansenpartnership.com/voyager + +The voyager architecture was designed by NCR in the mid 80s to be a +fully SMP capable RAS computing architecture built around intel's 486 +chip set. The voyager came in three levels of architectural +sophistication: 3,4 and 5 --- 1 and 2 never made it out of prototype. +The linux patches support only the Level 5 voyager architecture (any +machine class 3435 and above). + +The Voyager Architecture +------------------------ + +Voyager machines consist of a Baseboard with a 386 diagnostic +processor, a Power Supply Interface (PSI) a Primary and possibly +Secondary Microchannel bus and between 2 and 20 voyager slots. The +voyager slots can be populated with memory and cpu cards (up to 4GB +memory and from 1 486 to 32 Pentium Pro processors). Internally, the +voyager has a dual arbitrated system bus and a configuration and test +bus (CAT). The voyager bus speed is 40MHz. Therefore (since all +voyager cards are dual ported for each system bus) the maximum +transfer rate is 320Mb/s but only if you have your slot configuration +tuned (only memory cards can communicate with both busses at once, CPU +cards utilise them one at a time). + +Voyager SMP +----------- + +Since voyager was the first intel based SMP system, it is slightly +more primitive than the Intel IO-APIC approach to SMP. Voyager allows +arbitrary interrupt routing (including processor affinity routing) of +all 16 PC type interrupts. However it does this by using a modified +5259 master/slave chip set instead of an APIC bus. Additionally, +voyager supports Cross Processor Interrupts (CPI) equivalent to the +APIC IPIs. There are two routed voyager interrupt lines provided to +each slot. + +Processor Cards +--------------- + +These come in single, dyadic and quad configurations (the quads are +problematic--see later). The maximum configuration is 8 quad cards +for 32 way SMP. + +Quad Processors +--------------- + +Because voyager only supplies two interrupt lines to each Processor +card, the Quad processors have to be configured (and Bootstrapped) in +as a pair of Master/Slave processors. + +In fact, most Quad cards only accept one VIC interrupt line, so they +have one interrupt handling processor (called the VIC extended +processor) and three non-interrupt handling processors. + +Current Status +-------------- + +The System will boot on Mono, Dyad and Quad cards. There was +originally a Quad boot problem which has been fixed by proper gdt +alignment in the initial boot loader. If you still cannot get your +voyager system to boot, email me at: + + + + +The Quad cards now support using the separate Quad CPI vectors instead +of going through the VIC mailbox system. + +The Level 4 architecture (3430 and 3360 Machines) should also work +fine. + +Dump Switch +----------- + +The voyager dump switch sends out a broadcast NMI which the voyager +code intercepts and does a task dump. + +Power Switch +------------ + +The front panel power switch is intercepted by the kernel and should +cause a system shutdown and power off. + +A Note About Mixed CPU Systems +------------------------------ + +Linux isn't designed to handle mixed CPU systems very well. In order +to get everything going you *must* make sure that your lowest +capability CPU is used for booting. Also, mixing CPU classes +(e.g. 486 and 586) is really not going to work very well at all. diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Sat Oct 19 00:52:58 2002 +++ b/MAINTAINERS Sat Oct 19 00:52:58 2002 @@ -958,6 +958,12 @@ W: http://www.ibm.com/linux/ltc/projects/ppc S: Supported +LINUX FOR NCR VOYAGER +P: James Bottomley +M: James.Bottomley@HansenPartnership.com +W: http://www.hansenpartnership.com/voyager +S: Maintained + LINUX FOR POWERPC P: Paul Mackerras M: paulus@samba.org diff -Nru a/arch/i386/Config.help b/arch/i386/Config.help --- a/arch/i386/Config.help Sat Oct 19 00:52:58 2002 +++ b/arch/i386/Config.help Sat Oct 19 00:52:58 2002 @@ -268,6 +268,15 @@ (and especially the web page given there) before attempting to build an MCA bus kernel. +CONFIG_VOYAGER + Voyager is a MCA based 32 way capable SMP architecture proprietary + to NCR Corp. Machine classes 345x/35xx/4100/51xx are voyager based. + + *** WARNING *** + + If you do not specifically know you have a Voyager based machine, + say N here otherwise the kernel you build will not be bootable. + CONFIG_EISA The Extended Industry Standard Architecture (EISA) bus was developed as an open alternative to the IBM MicroChannel bus. diff -Nru a/arch/i386/Makefile b/arch/i386/Makefile --- a/arch/i386/Makefile Sat Oct 19 00:52:58 2002 +++ b/arch/i386/Makefile Sat Oct 19 00:52:58 2002 @@ -46,7 +46,11 @@ ifdef CONFIG_VISWS MACHINE := mach-visws else +ifdef CONFIG_VOYAGER +MACHINE := mach-voyager +else MACHINE := mach-generic +endif endif HEAD := arch/i386/kernel/head.o arch/i386/kernel/init_task.o diff -Nru a/arch/i386/boot/compressed/head.S b/arch/i386/boot/compressed/head.S --- a/arch/i386/boot/compressed/head.S Sat Oct 19 00:52:58 2002 +++ b/arch/i386/boot/compressed/head.S Sat Oct 19 00:52:58 2002 @@ -31,7 +31,7 @@ startup_32: cld cli - movl $(__KERNEL_DS),%eax + movl $(__BOOT_DS),%eax movl %eax,%ds movl %eax,%es movl %eax,%fs @@ -74,7 +74,7 @@ popl %esi # discard address popl %esi # real mode pointer xorl %ebx,%ebx - ljmp $(__KERNEL_CS), $0x100000 + ljmp $(__BOOT_CS), $0x100000 /* * We come here, if we were loaded high. @@ -101,7 +101,7 @@ popl %eax # hcount movl $0x100000,%edi cli # make sure we don't get interrupted - ljmp $(__KERNEL_CS), $0x1000 # and jump to the move routine + ljmp $(__BOOT_CS), $0x1000 # and jump to the move routine /* * Routine (template) for moving the decompressed kernel in place, @@ -124,5 +124,5 @@ movsl movl %ebx,%esi # Restore setup pointer xorl %ebx,%ebx - ljmp $(__KERNEL_CS), $0x100000 + ljmp $(__BOOT_CS), $0x100000 move_routine_end: diff -Nru a/arch/i386/boot/compressed/misc.c b/arch/i386/boot/compressed/misc.c --- a/arch/i386/boot/compressed/misc.c Sat Oct 19 00:52:58 2002 +++ b/arch/i386/boot/compressed/misc.c Sat Oct 19 00:52:58 2002 @@ -299,7 +299,7 @@ struct { long * a; short b; - } stack_start = { & user_stack [STACK_SIZE] , __KERNEL_DS }; + } stack_start = { & user_stack [STACK_SIZE] , __BOOT_DS }; static void setup_normal_output_buffer(void) { diff -Nru a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S --- a/arch/i386/boot/setup.S Sat Oct 19 00:52:58 2002 +++ b/arch/i386/boot/setup.S Sat Oct 19 00:52:58 2002 @@ -475,6 +475,24 @@ movsb popw %ds no_mca: +#ifdef CONFIG_VOYAGER + movb $0xff, 0x40 # flag on config found + movb $0xc0, %al + mov $0xff, %ah + int $0x15 # put voyager config info at es:di + jc no_voyager + movw $0x40, %si # place voyager info in apm table + cld + movw $7, %cx +voyager_rep: + movb %es:(%di), %al + movb %al,(%si) + incw %di + incw %si + decw %cx + jnz voyager_rep +no_voyager: +#endif # Check for PS/2 pointing device movw %cs, %ax # aka SETUPSEG subw $DELTA_INITSEG, %ax # aka INITSEG @@ -718,6 +736,7 @@ A20_ENABLE_LOOPS = 255 # Total loops to try +#ifndef CONFIG_VOYAGER a20_try_loop: # First, see if we are on a system with no A20 gate. @@ -736,11 +755,14 @@ jnz a20_done # Try enabling A20 through the keyboard controller +#endif /* CONFIG_VOYAGER */ a20_kbc: call empty_8042 +#ifndef CONFIG_VOYAGER call a20_test # Just in case the BIOS worked jnz a20_done # but had a delayed reaction. +#endif movb $0xD1, %al # command write outb %al, $0x64 @@ -750,6 +772,7 @@ outb %al, $0x60 call empty_8042 +#ifndef CONFIG_VOYAGER # Wait until a20 really *is* enabled; it can take a fair amount of # time on certain systems; Toshiba Tecras are known to have this # problem. @@ -797,6 +820,7 @@ # If we get here, all is good a20_done: +#endif /* CONFIG_VOYAGER */ # set up gdt and idt lidt idt_48 # load idt with 0,0 xorl %eax, %eax # Compute gdt_base @@ -848,7 +872,7 @@ subw $DELTA_INITSEG, %si shll $4, %esi # Convert to 32-bit pointer # NOTE: For high loaded big kernels we need a -# jmpi 0x100000,__KERNEL_CS +# jmpi 0x100000,__BOOT_CS # # but we yet haven't reloaded the CS register, so the default size # of the target offset still is 16 bit. @@ -859,7 +883,7 @@ .byte 0x66, 0xea # prefix + jmpi-opcode code32: .long 0x1000 # will be set to 0x100000 # for big kernels - .word __KERNEL_CS + .word __BOOT_CS # Here's a bunch of information about your current kernel.. kernel_version: .ascii UTS_RELEASE @@ -963,6 +987,7 @@ .string "INT15 refuses to access high mem, giving up." +#ifndef CONFIG_VOYAGER # This routine tests whether or not A20 is enabled. If so, it # exits with zf = 0. # @@ -993,6 +1018,8 @@ popw %cx ret +#endif /* CONFIG_VOYAGER */ + # This routine checks that the keyboard command queue is empty # (after emptying the output buffers) # @@ -1053,13 +1080,19 @@ # Descriptor tables # -# NOTE: if you think the GDT is large, you can make it smaller by just -# defining the KERNEL_CS and KERNEL_DS entries and shifting the gdt -# address down by GDT_ENTRY_KERNEL_CS*8. This puts bogus entries into -# the GDT, but those wont be used so it's not a problem. +# NOTE: The intel manual says gdt should be sixteen bytes aligned for +# efficiency reasons. However, there are machines which are known not +# to boot with misaligned GDTs, so alter this at your peril! If you alter +# GDT_ENTRY_BOOT_CS (in asm/segment.h) remember to leave at least two +# empty GDT entries (one for NULL and one reserved). +# +# NOTE: On some CPUs, the GDT must be 8 byte aligned. This is +# true for the Voyager Quad CPU card which will not boot without +# This directive. 16 byte aligment is recommended by intel. # + .align 16 gdt: - .fill GDT_ENTRY_KERNEL_CS,8,0 + .fill GDT_ENTRY_BOOT_CS,8,0 .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) .word 0 # base address = 0 @@ -1072,12 +1105,17 @@ .word 0x9200 # data read/write .word 0x00CF # granularity = 4096, 386 # (+5th nibble of limit) +gdt_end: + .align 4 + + .word 0 # alignment byte idt_48: .word 0 # idt limit = 0 .word 0, 0 # idt base = 0L -gdt_48: - .word GDT_ENTRY_KERNEL_CS*8 + 16 - 1 # gdt limit + .word 0 # alignment byte +gdt_48: + .word gdt_end - gdt - 1 # gdt limit .word 0, 0 # gdt base (filled in later) # Include video setup & detection code diff -Nru a/arch/i386/config.in b/arch/i386/config.in --- a/arch/i386/config.in Sat Oct 19 00:52:58 2002 +++ b/arch/i386/config.in Sat Oct 19 00:52:58 2002 @@ -43,6 +43,7 @@ define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n define_bool CONFIG_X86_PPRO_FENCE y define_bool CONFIG_X86_F00F_BUG y + define_bool CONFIG_X86_PIT y else define_bool CONFIG_X86_WP_WORKS_OK y define_bool CONFIG_X86_INVLPG y @@ -59,6 +60,7 @@ define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_PPRO_FENCE y define_bool CONFIG_X86_F00F_BUG y + define_bool CONFIG_X86_PIT y fi if [ "$CONFIG_M586" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 5 @@ -66,12 +68,14 @@ define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_PPRO_FENCE y define_bool CONFIG_X86_F00F_BUG y + define bool CONFIG_X86_PIT y fi if [ "$CONFIG_M586TSC" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_USE_STRING_486 y define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_TSC y + define_bool CONFIG_x86_PIT y define_bool CONFIG_X86_PPRO_FENCE y define_bool CONFIG_X86_F00F_BUG y fi @@ -80,6 +84,7 @@ define_bool CONFIG_X86_USE_STRING_486 y define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_TSC y + define_bool CONFIG_X86_PIT y define_bool CONFIG_X86_GOOD_APIC y define_bool CONFIG_X86_PPRO_FENCE y define_bool CONFIG_X86_F00F_BUG y @@ -274,15 +279,22 @@ # Visual Workstation support is utterly broken. # If you want to see it working mail an VW540 to hch@infradead.org 8) #bool 'SGI Visual Workstation support' CONFIG_VISWS + +if [ "$CONFIG_VISWS" != "y" ]; then + bool 'MCA support' CONFIG_MCA +else + define_bool CONFIG_MCA n +fi + +if [ "$CONFIG_MCA" = "y" ]; then + bool ' Support for the NCR Voyager Architecture' CONFIG_VOYAGER +fi + if [ "$CONFIG_VISWS" = "y" ]; then define_bool CONFIG_X86_VISWS_APIC y define_bool CONFIG_X86_LOCAL_APIC y define_bool CONFIG_PCI y else - if [ "$CONFIG_SMP" = "y" ]; then - define_bool CONFIG_X86_IO_APIC y - define_bool CONFIG_X86_LOCAL_APIC y - fi bool 'PCI support' CONFIG_PCI if [ "$CONFIG_PCI" = "y" ]; then choice ' PCI access mode' \ @@ -305,12 +317,6 @@ bool 'ISA support' CONFIG_ISA dep_bool 'EISA support' CONFIG_EISA $CONFIG_ISA -if [ "$CONFIG_VISWS" != "y" ]; then - bool 'MCA support' CONFIG_MCA -else - define_bool CONFIG_MCA n -fi - bool 'Support for hot-pluggable devices' CONFIG_HOTPLUG if [ "$CONFIG_HOTPLUG" = "y" ] ; then @@ -468,20 +474,35 @@ bool ' Load all symbols for debugging/kksymoops' CONFIG_KALLSYMS fi -if [ "$CONFIG_X86_LOCAL_APIC" = "y" ]; then - define_bool CONFIG_X86_EXTRA_IRQS y - define_bool CONFIG_X86_FIND_SMP_CONFIG y - define_bool CONFIG_X86_MPPARSE y -fi - endmenu source security/Config.in source lib/Config.in -if [ "$CONFIG_SMP" = "y" ]; then - define_bool CONFIG_X86_SMP y - define_bool CONFIG_X86_HT y +if [ "$CONFIG_VOYAGER" = "y" ]; then + define_bool CONFIG_X86_EXTRA_IRQS y + define_bool CONFIG_X86_IO_APIC n + define_bool CONFIG_X86_LOCAL_APIC n + define_bool CONFIG_X86_FIND_SMP_CONFIG y + define_bool CONFIG_X86_TSC n + define_bool CONFIG_X86_PIT y + if [ "$CONFIG_SMP" = "y" ]; then + define_bool CONFIG_X86_TRAMPOLINE y + fi +else + if [ "$CONFIG_SMP" = "y" ]; then + define_bool CONFIG_X86_SMP y + define_bool CONFIG_X86_HT y + define_bool CONFIG_X86_IO_APIC y + define_bool CONFIG_X86_LOCAL_APIC y + define_bool CONFIG_X86_MPPARSE y + define_bool CONFIG_X86_TRAMPOLINE y + fi + define_bool CONFIG_X86_BIOS_REBOOT y fi -define_bool CONFIG_X86_BIOS_REBOOT y +if [ "$CONFIG_X86_LOCAL_APIC" = "y" ]; then + define_bool CONFIG_X86_EXTRA_IRQS y + define_bool CONFIG_X86_FIND_SMP_CONFIG y + define_bool CONFIG_X86_MPPARSE y +fi diff -Nru a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile --- a/arch/i386/kernel/Makefile Sat Oct 19 00:52:58 2002 +++ b/arch/i386/kernel/Makefile Sat Oct 19 00:52:58 2002 @@ -21,7 +21,8 @@ obj-$(CONFIG_APM) += apm.o obj-$(CONFIG_ACPI) += acpi.o obj-$(CONFIG_ACPI_SLEEP) += acpi_wakeup.o -obj-$(CONFIG_X86_SMP) += smp.o smpboot.o trampoline.o +obj-$(CONFIG_X86_SMP) += smp.o smpboot.o +obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o obj-$(CONFIG_X86_MPPARSE) += mpparse.o obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o diff -Nru a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S --- a/arch/i386/kernel/head.S Sat Oct 19 00:52:58 2002 +++ b/arch/i386/kernel/head.S Sat Oct 19 00:52:58 2002 @@ -15,6 +15,7 @@ #include #include #include +#include #define OLD_CL_MAGIC_ADDR 0x90020 #define OLD_CL_MAGIC 0xA33F @@ -46,7 +47,7 @@ * Set segments to known values */ cld - movl $(__KERNEL_DS),%eax + movl $(__BOOT_DS),%eax movl %eax,%ds movl %eax,%es movl %eax,%fs @@ -306,7 +307,7 @@ ENTRY(stack_start) .long init_thread_union+8192 - .long __KERNEL_DS + .long __BOOT_DS /* This is the default interrupt "handler" :-) */ int_msg: @@ -349,12 +350,12 @@ .long idt_table # boot GDT descriptor (later on used by CPU#0): - + .word 0 # 32 bit align gdt_desc.address cpu_gdt_descr: .word GDT_ENTRIES*8-1 .long cpu_gdt_table - .fill NR_CPUS-1,6,0 # space for the other GDT descriptors + .fill NR_CPUS-1,8,0 # space for the other GDT descriptors /* * This is initialized to create an identity-mapping at 0-8M (for bootup @@ -405,10 +406,21 @@ */ .data -ALIGN /* * The Global Descriptor Table contains 28 quadwords, per-CPU. */ +#ifdef CONFIG_SMP +/* + * The boot_gdt_table must mirror the equivalent in setup.S and is + * used only by the trampoline for booting other CPUs + */ + .align L1_CACHE_BYTES +ENTRY(boot_gdt_table) + .fill GDT_ENTRY_BOOT_CS,8,0 + .quad 0x00cf9a000000ffff /* kernel 4GB code at 0x00000000 */ + .quad 0x00cf92000000ffff /* kernel 4GB data at 0x00000000 */ +#endif + .align L1_CACHE_BYTES ENTRY(cpu_gdt_table) .quad 0x0000000000000000 /* NULL descriptor */ .quad 0x0000000000000000 /* 0x0b reserved */ diff -Nru a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c --- a/arch/i386/kernel/irq.c Sat Oct 19 00:52:58 2002 +++ b/arch/i386/kernel/irq.c Sat Oct 19 00:52:58 2002 @@ -167,7 +167,7 @@ if (cpu_online(j)) p += seq_printf(p, "%10u ", nmi_count(j)); seq_putc(p, '\n'); -#if CONFIG_X86_LOCAL_APIC +#ifdef CONFIG_X86_LOCAL_APIC seq_printf(p, "LOC: "); for (j = 0; j < NR_CPUS; j++) if (cpu_online(j)) diff -Nru a/arch/i386/kernel/timers/Makefile b/arch/i386/kernel/timers/Makefile --- a/arch/i386/kernel/timers/Makefile Sat Oct 19 00:52:58 2002 +++ b/arch/i386/kernel/timers/Makefile Sat Oct 19 00:52:58 2002 @@ -4,8 +4,8 @@ obj-y := timer.o -obj-y += timer_tsc.o -obj-y += timer_pit.o -obj-$(CONFIG_X86_CYCLONE) += timer_cyclone.o +obj-$(CONFIG_X86_TSC) += timer_tsc.o +obj-$(CONFIG_X86_PIT) += timer_pit.o +obj-$(CONFIG_X86_CYCLONE) += timer_cyclone.o include $(TOPDIR)/Rules.make diff -Nru a/arch/i386/kernel/timers/timer.c b/arch/i386/kernel/timers/timer.c --- a/arch/i386/kernel/timers/timer.c Sat Oct 19 00:52:58 2002 +++ b/arch/i386/kernel/timers/timer.c Sat Oct 19 00:52:58 2002 @@ -7,8 +7,10 @@ /* list of timers, ordered by preference, NULL terminated */ static struct timer_opts* timers[] = { +#ifdef CONFIG_X86_TSC &timer_tsc, -#ifndef CONFIG_X86_TSC +#endif +#ifdef CONFIG_X86_PIT &timer_pit, #endif NULL, diff -Nru a/arch/i386/kernel/timers/timer_pit.c b/arch/i386/kernel/timers/timer_pit.c --- a/arch/i386/kernel/timers/timer_pit.c Sat Oct 19 00:52:58 2002 +++ b/arch/i386/kernel/timers/timer_pit.c Sat Oct 19 00:52:58 2002 @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include extern spinlock_t i8259A_lock; extern spinlock_t i8253_lock; diff -Nru a/arch/i386/kernel/trampoline.S b/arch/i386/kernel/trampoline.S --- a/arch/i386/kernel/trampoline.S Sat Oct 19 00:52:58 2002 +++ b/arch/i386/kernel/trampoline.S Sat Oct 19 00:52:58 2002 @@ -54,7 +54,7 @@ lmsw %ax # into protected mode jmp flush_instr flush_instr: - ljmpl $__KERNEL_CS, $0x00100000 + ljmpl $__BOOT_CS, $0x00100000 # jump to startup_32 in arch/i386/kernel/head.S idt_48: @@ -67,8 +67,8 @@ # gdt_48: - .word 0x0800 # gdt limit = 2048, 256 GDT entries - .long cpu_gdt_table-__PAGE_OFFSET # gdt base = gdt (first SMP CPU) + .word __BOOT_DS + 7 # gdt limit + .long boot_gdt_table-__PAGE_OFFSET # gdt base = gdt (first SMP CPU) .globl trampoline_end trampoline_end: diff -Nru a/arch/i386/mach-voyager/Makefile b/arch/i386/mach-voyager/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/mach-voyager/Makefile Sat Oct 19 00:52:59 2002 @@ -0,0 +1,17 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +EXTRA_CFLAGS += -I../kernel +export-objs := + +obj-y := setup.o voyager_basic.o voyager_thread.o + +obj-$(CONFIG_SMP) += voyager_smp.o voyager_cat.o + +include $(TOPDIR)/Rules.make diff -Nru a/arch/i386/mach-voyager/do_timer.h b/arch/i386/mach-voyager/do_timer.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/mach-voyager/do_timer.h Sat Oct 19 00:52:59 2002 @@ -0,0 +1,22 @@ +/* defines for inline arch setup functions */ +#include + +static inline void do_timer_interrupt_hook(struct pt_regs *regs) +{ + do_timer(regs); + + voyager_timer_interrupt(regs); +} + +static inline int do_timer_overflow(int count) +{ + /* can't read the ISR, just assume 1 tick + overflow */ + if(count > LATCH || count < 0) { + printk(KERN_ERR "VOYAGER PROBLEM: count is %d, latch is %d\n", count, LATCH); + count = LATCH; + } + count -= LATCH; + + return count; +} diff -Nru a/arch/i386/mach-voyager/entry_arch.h b/arch/i386/mach-voyager/entry_arch.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/mach-voyager/entry_arch.h Sat Oct 19 00:52:58 2002 @@ -0,0 +1,26 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* Copyright (C) 2002 + * + * Author: James.Bottomley@HansenPartnership.com + * + * linux/arch/i386/voyager/entry_arch.h + * + * This file builds the VIC and QIC CPI gates + */ + +/* initialise the voyager interrupt gates + * + * This uses the macros in irq.h to set up assembly jump gates. The + * calls are then redirected to the same routine with smp_ prefixed */ +BUILD_INTERRUPT(vic_sys_interrupt, VIC_SYS_INT) +BUILD_INTERRUPT(vic_cmn_interrupt, VIC_CMN_INT) +BUILD_INTERRUPT(vic_cpi_interrupt, VIC_CPI_LEVEL0); + +/* do all the QIC interrupts */ +BUILD_INTERRUPT(qic_timer_interrupt, QIC_TIMER_CPI); +BUILD_INTERRUPT(qic_invalidate_interrupt, QIC_INVALIDATE_CPI); +BUILD_INTERRUPT(qic_reschedule_interrupt, QIC_RESCHEDULE_CPI); +BUILD_INTERRUPT(qic_enable_irq_interrupt, QIC_ENABLE_IRQ_CPI); +BUILD_INTERRUPT(qic_call_function_interrupt, QIC_CALL_FUNCTION_CPI); + diff -Nru a/arch/i386/mach-voyager/irq_vectors.h b/arch/i386/mach-voyager/irq_vectors.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/mach-voyager/irq_vectors.h Sat Oct 19 00:52:58 2002 @@ -0,0 +1,71 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* Copyright (C) 2002 + * + * Author: James.Bottomley@HansenPartnership.com + * + * linux/arch/i386/voyager/irq_vectors.h + * + * This file provides definitions for the VIC and QIC CPIs + */ + +#ifndef _ASM_IRQ_VECTORS_H +#define _ASM_IRQ_VECTORS_H + +/* + * IDT vectors usable for external interrupt sources start + * at 0x20: + */ +#define FIRST_EXTERNAL_VECTOR 0x20 + +#define SYSCALL_VECTOR 0x80 + +/* + * Vectors 0x20-0x2f are used for ISA interrupts. + */ + +/* These define the CPIs we use in linux */ +#define VIC_CPI_LEVEL0 0 +#define VIC_CPI_LEVEL1 1 +/* now the fake CPIs */ +#define VIC_TIMER_CPI 2 +#define VIC_INVALIDATE_CPI 3 +#define VIC_RESCHEDULE_CPI 4 +#define VIC_ENABLE_IRQ_CPI 5 +#define VIC_CALL_FUNCTION_CPI 6 + +/* Now the QIC CPIs: Since we don't need the two initial levels, + * these are 2 less than the VIC CPIs */ +#define QIC_CPI_OFFSET 1 +#define QIC_TIMER_CPI (VIC_TIMER_CPI - QIC_CPI_OFFSET) +#define QIC_INVALIDATE_CPI (VIC_INVALIDATE_CPI - QIC_CPI_OFFSET) +#define QIC_RESCHEDULE_CPI (VIC_RESCHEDULE_CPI - QIC_CPI_OFFSET) +#define QIC_ENABLE_IRQ_CPI (VIC_ENABLE_IRQ_CPI - QIC_CPI_OFFSET) +#define QIC_CALL_FUNCTION_CPI (VIC_CALL_FUNCTION_CPI - QIC_CPI_OFFSET) + +#define VIC_START_FAKE_CPI VIC_TIMER_CPI +#define VIC_END_FAKE_CPI VIC_CALL_FUNCTION_CPI + +/* this is the SYS_INT CPI. */ +#define VIC_SYS_INT 8 +#define VIC_CMN_INT 15 + +/* This is the boot CPI for alternate processors. It gets overwritten + * by the above once the system has activated all available processors */ +#define VIC_CPU_BOOT_CPI VIC_CPI_LEVEL0 +#define VIC_CPU_BOOT_ERRATA_CPI (VIC_CPI_LEVEL0 + 8) + +#define NR_IRQS 224 + +#ifndef __ASSEMBLY__ +extern asmlinkage void vic_cpi_interrupt(void); +extern asmlinkage void vic_sys_interrupt(void); +extern asmlinkage void vic_cmn_interrupt(void); +extern asmlinkage void qic_timer_interrupt(void); +extern asmlinkage void qic_invalidate_interrupt(void); +extern asmlinkage void qic_reschedule_interrupt(void); +extern asmlinkage void qic_enable_irq_interrupt(void); +extern asmlinkage void qic_call_function_interrupt(void); +#endif /* !__ASSEMBLY__ */ + +#endif /* _ASM_IRQ_VECTORS_H */ diff -Nru a/arch/i386/mach-voyager/setup.c b/arch/i386/mach-voyager/setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/mach-voyager/setup.c Sat Oct 19 00:52:59 2002 @@ -0,0 +1,43 @@ +/* + * Machine specific setup for generic + */ + +#include +#include +#include +#include +#include + +void __init pre_intr_init_hook(void) +{ + init_ISA_irqs(); +} + +/* + * IRQ2 is cascade interrupt to second interrupt controller + */ +static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL}; + +void __init intr_init_hook(void) +{ +#ifdef CONFIG_SMP + smp_intr_init(); +#endif + + setup_irq(2, &irq2); +} + +void __init pre_setup_arch_hook(void) +{ +} + +void __init trap_init_hook(void) +{ +} + +static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL}; + +void __init time_init_hook(void) +{ + setup_irq(0, &irq0); +} diff -Nru a/arch/i386/mach-voyager/setup_arch_post.h b/arch/i386/mach-voyager/setup_arch_post.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/mach-voyager/setup_arch_post.h Sat Oct 19 00:52:59 2002 @@ -0,0 +1,73 @@ +/* Hook for machine specific memory setup. + * + * This is included late in kernel/setup.c so that it can make use of all of + * the static functions. */ + +static inline char * __init machine_specific_memory_setup(void) +{ + char *who; + + who = "NOT VOYAGER"; + + if(voyager_level == 5) { + __u32 addr, length; + int i; + + who = "Voyager-SUS"; + + e820.nr_map = 0; + for(i=0; voyager_memory_detect(i, &addr, &length); i++) { + add_memory_region(addr, length, E820_RAM); + } + return who; + } else if(voyager_level == 4) { + __u32 tom; + __u16 catbase = inb(VOYAGER_SSPB_RELOCATION_PORT)<<8; + /* select the DINO config space */ + outb(VOYAGER_DINO, VOYAGER_CAT_CONFIG_PORT); + /* Read DINO top of memory register */ + tom = ((inb(catbase + 0x4) & 0xf0) << 16) + + ((inb(catbase + 0x5) & 0x7f) << 24); + + if(inb(catbase) != VOYAGER_DINO) { + printk(KERN_ERR "Voyager: Failed to get DINO for L4, setting tom to EXT_MEM_K\n"); + tom = (EXT_MEM_K)<<10; + } + who = "Voyager-TOM"; + add_memory_region(0, 0x9f000, E820_RAM); + /* map from 1M to top of memory */ + add_memory_region(1*1024*1024, tom - 1*1024*1024, E820_RAM); + /* FIXME: Should check the ASICs to see if I need to + * take out the 8M window. Just do it at the moment + * */ + add_memory_region(8*1024*1024, 8*1024*1024, E820_RESERVED); + return who; + } + + who = "BIOS-e820"; + + /* + * Try to copy the BIOS-supplied E820-map. + * + * Otherwise fake a memory map; one section from 0k->640k, + * the next section from 1mb->appropriate_mem_k + */ + sanitize_e820_map(E820_MAP, &E820_MAP_NR); + if (copy_e820_map(E820_MAP, E820_MAP_NR) < 0) { + unsigned long mem_size; + + /* compare results from other methods and take the greater */ + if (ALT_MEM_K < EXT_MEM_K) { + mem_size = EXT_MEM_K; + who = "BIOS-88"; + } else { + mem_size = ALT_MEM_K; + who = "BIOS-e801"; + } + + e820.nr_map = 0; + add_memory_region(0, LOWMEMSIZE(), E820_RAM); + add_memory_region(HIGH_MEMORY, mem_size << 10, E820_RAM); + } + return who; +} diff -Nru a/arch/i386/mach-voyager/setup_arch_pre.h b/arch/i386/mach-voyager/setup_arch_pre.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/mach-voyager/setup_arch_pre.h Sat Oct 19 00:52:59 2002 @@ -0,0 +1,10 @@ +#include +#define VOYAGER_BIOS_INFO ((struct voyager_bios_info *)(PARAM+0x40)) + +/* Hook to call BIOS initialisation function */ + +/* for voyager, pass the voyager BIOS/SUS info area to the detection + * routines */ + +#define ARCH_SETUP voyager_detect(VOYAGER_BIOS_INFO); + diff -Nru a/arch/i386/mach-voyager/voyager_basic.c b/arch/i386/mach-voyager/voyager_basic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/mach-voyager/voyager_basic.c Sat Oct 19 00:52:59 2002 @@ -0,0 +1,312 @@ +/* Copyright (C) 1999,2001 + * + * Author: J.E.J.Bottomley@HansenPartnership.com + * + * linux/arch/i386/kernel/voyager.c + * + * This file contains all the voyager specific routines for getting + * initialisation of the architecture to function. For additional + * features see: + * + * voyager_cat.c - Voyager CAT bus interface + * voyager_smp.c - Voyager SMP hal (emulates linux smp.c) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Power off function, if any + */ +void (*pm_power_off)(void); + +int reboot_thru_bios; + +int voyager_level = 0; + +struct voyager_SUS *voyager_SUS = NULL; + +void +voyager_detect(struct voyager_bios_info *bios) +{ + if(bios->len != 0xff) { + int class = (bios->class_1 << 8) + | (bios->class_2 & 0xff); + + printk("Voyager System detected.\n" + " Class %x, Revision %d.%d\n", + class, bios->major, bios->minor); + if(class == VOYAGER_LEVEL4) + voyager_level = 4; + else if(class < VOYAGER_LEVEL5_AND_ABOVE) + voyager_level = 3; + else + voyager_level = 5; + printk(" Architecture Level %d\n", voyager_level); + if(voyager_level < 4) + printk("\n**WARNING**: Voyager HAL only supports Levels 4 and 5 Architectures at the moment\n\n"); + /* install the power off handler */ + pm_power_off = voyager_power_off; + } else { + printk("\n\n**WARNING**: No Voyager Subsystem Found\n"); + } +} + +void +voyager_system_interrupt(int cpl, void *dev_id, struct pt_regs *regs) +{ + printk("Voyager: detected system interrupt\n"); +} + +/* Routine to read information from the extended CMOS area */ +__u8 +voyager_extended_cmos_read(__u16 addr) +{ + outb(addr & 0xff, 0x74); + outb((addr >> 8) & 0xff, 0x75); + return inb(0x76); +} + +/* internal definitions for the SUS Click Map of memory */ + +#define CLICK_ENTRIES 16 +#define CLICK_SIZE 4096 /* click to byte conversion for Length */ + +typedef struct ClickMap { + struct Entry { + __u32 Address; + __u32 Length; + } Entry[CLICK_ENTRIES]; +} ClickMap_t; + + +/* This routine is pretty much an awful hack to read the bios clickmap by + * mapping it into page 0. There are usually three regions in the map: + * Base Memory + * Extended Memory + * zero length marker for end of map + * + * Returns are 0 for failure and 1 for success on extracting region. + */ +int __init +voyager_memory_detect(int region, __u32 *start, __u32 *length) +{ + int i; + int retval = 0; + __u8 cmos[4]; + ClickMap_t *map; + unsigned long map_addr; + unsigned long old; + + if(region >= CLICK_ENTRIES) { + printk("Voyager: Illegal ClickMap region %d\n", region); + return 0; + } + + for(i = 0; i < sizeof(cmos); i++) + cmos[i] = voyager_extended_cmos_read(VOYAGER_MEMORY_CLICKMAP + i); + + map_addr = *(unsigned long *)cmos; + + /* steal page 0 for this */ + old = pg0[0]; + pg0[0] = ((map_addr & PAGE_MASK) | _PAGE_RW | _PAGE_PRESENT); + local_flush_tlb(); + /* now clear everything out but page 0 */ + map = (ClickMap_t *)(map_addr & (~PAGE_MASK)); + + /* zero length is the end of the clickmap */ + if(map->Entry[region].Length != 0) { + *length = map->Entry[region].Length * CLICK_SIZE; + *start = map->Entry[region].Address; + retval = 1; + } + + /* replace the mapping */ + pg0[0] = old; + local_flush_tlb(); + return retval; +} + +void +voyager_dump() +{ + /* get here via a sysrq */ +#ifdef CONFIG_SMP + voyager_smp_dump(); +#endif +} + +/* voyager specific handling code for timer interrupts. Used to hand + * off the timer tick to the SMP code, since the VIC doesn't have an + * internal timer (The QIC does, but that's another story). */ +void +voyager_timer_interrupt(struct pt_regs *regs) +{ + if((jiffies & 0x3ff) == 0) { + + /* There seems to be something flaky in either + * hardware or software that is resetting the timer 0 + * count to something much higher than it should be + * This seems to occur in the boot sequence, just + * before root is mounted. Therefore, every 10 + * seconds or so, we sanity check the timer zero count + * and kick it back to where it should be. + * + * FIXME: This is the most awful hack yet seen. I + * should work out exactly what is interfering with + * the timer count settings early in the boot sequence + * and swiftly introduce it to something sharp and + * pointy. */ + __u16 val; + extern spinlock_t i8253_lock; + + spin_lock(&i8253_lock); + + outb_p(0x00, 0x43); + val = inb_p(0x40); + val |= inb(0x40) << 8; + spin_unlock(&i8253_lock); + + if(val > LATCH) { + printk("\nVOYAGER: countdown timer value too high (%d), resetting\n\n", val); + spin_lock(&i8253_lock); + outb(0x34,0x43); + outb_p(LATCH & 0xff , 0x40); /* LSB */ + outb(LATCH >> 8 , 0x40); /* MSB */ + spin_unlock(&i8253_lock); + } + } +#ifdef CONFIG_SMP + smp_vic_timer_interrupt(regs); +#endif +} + +void +voyager_power_off(void) +{ + printk("VOYAGER Power Off\n"); + + if(voyager_level == 5) { + voyager_cat_power_off(); + } else if(voyager_level == 4) { + /* This doesn't apparently work on most L4 machines, + * but the specs say to do this to get automatic power + * off. Unfortunately, if it doesn't power off the + * machine, it ends up doing a cold restart, which + * isn't really intended, so comment out the code */ +#if 0 + int port; + + + /* enable the voyager Configuration Space */ + outb((inb(VOYAGER_MC_SETUP) & 0xf0) | 0x8, + VOYAGER_MC_SETUP); + /* the port for the power off flag is an offset from the + floating base */ + port = (inb(VOYAGER_SSPB_RELOCATION_PORT) << 8) + 0x21; + /* set the power off flag */ + outb(inb(port) | 0x1, port); +#endif + } + /* and wait for it to happen */ + for(;;) { + __asm("cli"); + __asm("hlt"); + } +} + +/* copied from process.c */ +static inline void +kb_wait(void) +{ + int i; + + for (i=0; i<0x10000; i++) + if ((inb_p(0x64) & 0x02) == 0) + break; +} + +void +machine_restart(char *cmd) +{ + printk("Voyager Warm Restart\n"); + kb_wait(); + + if(voyager_level == 5) { + /* write magic values to the RTC to inform system that + * shutdown is beginning */ + outb(0x8f, 0x70); + outb(0x5 , 0x71); + + udelay(50); + outb(0xfe,0x64); /* pull reset low */ + } else if(voyager_level == 4) { + __u16 catbase = inb(VOYAGER_SSPB_RELOCATION_PORT)<<8; + __u8 basebd = inb(VOYAGER_MC_SETUP); + + outb(basebd | 0x08, VOYAGER_MC_SETUP); + outb(0x02, catbase + 0x21); + } + for(;;) { + asm("cli"); + asm("hlt"); + } +} + +void +mca_nmi_hook(void) +{ + __u8 dumpval __attribute__((unused)) = inb(0xf823); + __u8 swnmi __attribute__((unused)) = inb(0xf813); + extern void show_stack(unsigned long *); + + /* FIXME: assume dump switch pressed */ + /* check to see if the dump switch was pressed */ + VDEBUG(("VOYAGER: dumpval = 0x%x, swnmi = 0x%x\n", dumpval, swnmi)); + /* clear swnmi */ + outb(0xff, 0xf813); + /* tell SUS to ignore dump */ + if(voyager_level == 5 && voyager_SUS != NULL) { + if(voyager_SUS->SUS_mbox == VOYAGER_DUMP_BUTTON_NMI) { + voyager_SUS->kernel_mbox = VOYAGER_NO_COMMAND; + voyager_SUS->kernel_flags |= VOYAGER_OS_IN_PROGRESS; + udelay(1000); + voyager_SUS->kernel_mbox = VOYAGER_IGNORE_DUMP; + voyager_SUS->kernel_flags &= ~VOYAGER_OS_IN_PROGRESS; + } + } + printk(KERN_ERR "VOYAGER: Dump switch pressed, printing CPU%d tracebacks\n", smp_processor_id()); + show_stack(NULL); + show_state(); +} + + + +void +machine_halt(void) +{ + /* treat a halt like a power off */ + machine_power_off(); +} + +void machine_power_off(void) +{ + if (pm_power_off) + pm_power_off(); +} diff -Nru a/arch/i386/mach-voyager/voyager_cat.c b/arch/i386/mach-voyager/voyager_cat.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/mach-voyager/voyager_cat.c Sat Oct 19 00:52:59 2002 @@ -0,0 +1,1178 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* Copyright (C) 1999,2001 + * + * Author: J.E.J.Bottomley@HansenPartnership.com + * + * linux/arch/i386/kernel/voyager_cat.c + * + * This file contains all the logic for manipulating the CAT bus + * in a level 5 machine. + * + * The CAT bus is a serial configuration and test bus. Its primary + * uses are to probe the initial configuration of the system and to + * diagnose error conditions when a system interrupt occurs. The low + * level interface is fairly primitive, so most of this file consists + * of bit shift manipulations to send and receive packets on the + * serial bus */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef VOYAGER_CAT_DEBUG +#define CDEBUG(x) printk x +#else +#define CDEBUG(x) +#endif + +/* the CAT command port */ +#define CAT_CMD (sspb + 0xe) +/* the CAT data port */ +#define CAT_DATA (sspb + 0xd) + +/* the internal cat functions */ +static void cat_pack(__u8 *msg, __u16 start_bit, __u8 *data, + __u16 num_bits); +static void cat_unpack(__u8 *msg, __u16 start_bit, __u8 *data, + __u16 num_bits); +static void cat_build_header(__u8 *header, const __u16 len, + const __u16 smallest_reg_bits, + const __u16 longest_reg_bits); +static int cat_sendinst(voyager_module_t *modp, voyager_asic_t *asicp, + __u8 reg, __u8 op); +static int cat_getdata(voyager_module_t *modp, voyager_asic_t *asicp, + __u8 reg, __u8 *value); +static int cat_shiftout(__u8 *data, __u16 data_bytes, __u16 header_bytes, + __u8 pad_bits); +static int cat_write(voyager_module_t *modp, voyager_asic_t *asicp, __u8 reg, + __u8 value); +static int cat_read(voyager_module_t *modp, voyager_asic_t *asicp, __u8 reg, + __u8 *value); +static int cat_subread(voyager_module_t *modp, voyager_asic_t *asicp, + __u16 offset, __u16 len, void *buf); +static int cat_senddata(voyager_module_t *modp, voyager_asic_t *asicp, + __u8 reg, __u8 value); +static int cat_disconnect(voyager_module_t *modp, voyager_asic_t *asicp); +static int cat_connect(voyager_module_t *modp, voyager_asic_t *asicp); + +static inline const char * +cat_module_name(int module_id) +{ + switch(module_id) { + case 0x10: + return "Processor Slot 0"; + case 0x11: + return "Processor Slot 1"; + case 0x12: + return "Processor Slot 2"; + case 0x13: + return "Processor Slot 4"; + case 0x14: + return "Memory Slot 0"; + case 0x15: + return "Memory Slot 1"; + case 0x18: + return "Primary Microchannel"; + case 0x19: + return "Secondary Microchannel"; + case 0x1a: + return "Power Supply Interface"; + case 0x1c: + return "Processor Slot 5"; + case 0x1d: + return "Processor Slot 6"; + case 0x1e: + return "Processor Slot 7"; + case 0x1f: + return "Processor Slot 8"; + default: + return "Unknown Module"; + } +} + +static int sspb = 0; /* stores the super port location */ +int voyager_8slot = 0; /* set to true if a 51xx monster */ + +voyager_module_t *voyager_cat_list; + +/* the I/O port assignments for the VIC and QIC */ +static struct resource vic_res = { + "Voyager Interrupt Controller", 0xFC00, 0xFC6F }; +static struct resource qic_res = { + "Quad Interrupt Controller", 0xFC70, 0xFCFF }; + +/* This function is used to pack a data bit stream inside a message. + * It writes num_bits of the data buffer in msg starting at start_bit. + * Note: This function assumes that any unused bit in the data stream + * is set to zero so that the ors will work correctly */ +#define BITS_PER_BYTE 8 +static void +cat_pack(__u8 *msg, const __u16 start_bit, __u8 *data, const __u16 num_bits) +{ + /* compute initial shift needed */ + const __u16 offset = start_bit % BITS_PER_BYTE; + __u16 len = num_bits / BITS_PER_BYTE; + __u16 byte = start_bit / BITS_PER_BYTE; + __u16 residue = (num_bits % BITS_PER_BYTE) + offset; + int i; + + /* adjust if we have more than a byte of residue */ + if(residue >= BITS_PER_BYTE) { + residue -= BITS_PER_BYTE; + len++; + } + + /* clear out the bits. We assume here that if len==0 then + * residue >= offset. This is always true for the catbus + * operations */ + msg[byte] &= 0xff << (BITS_PER_BYTE - offset); + msg[byte++] |= data[0] >> offset; + if(len == 0) + return; + for(i = 1; i < len; i++) + msg[byte++] = (data[i-1] << (BITS_PER_BYTE - offset)) + | (data[i] >> offset); + if(residue != 0) { + __u8 mask = 0xff >> residue; + __u8 last_byte = data[i-1] << (BITS_PER_BYTE - offset) + | (data[i] >> offset); + + last_byte &= ~mask; + msg[byte] &= mask; + msg[byte] |= last_byte; + } + return; +} +/* unpack the data again (same arguments as cat_pack()). data buffer + * must be zero populated. + * + * Function: given a message string move to start_bit and copy num_bits into + * data (starting at bit 0 in data). + */ +static void +cat_unpack(__u8 *msg, const __u16 start_bit, __u8 *data, const __u16 num_bits) +{ + /* compute initial shift needed */ + const __u16 offset = start_bit % BITS_PER_BYTE; + __u16 len = num_bits / BITS_PER_BYTE; + const __u8 last_bits = num_bits % BITS_PER_BYTE; + __u16 byte = start_bit / BITS_PER_BYTE; + int i; + + if(last_bits != 0) + len++; + + /* special case: want < 8 bits from msg and we can get it from + * a single byte of the msg */ + if(len == 0 && BITS_PER_BYTE - offset >= num_bits) { + data[0] = msg[byte] << offset; + data[0] &= 0xff >> (BITS_PER_BYTE - num_bits); + return; + } + for(i = 0; i < len; i++) { + /* this annoying if has to be done just in case a read of + * msg one beyond the array causes a panic */ + if(offset != 0) { + data[i] = msg[byte++] << offset; + data[i] |= msg[byte] >> (BITS_PER_BYTE - offset); + } + else { + data[i] = msg[byte++]; + } + } + /* do we need to truncate the final byte */ + if(last_bits != 0) { + data[i-1] &= 0xff << (BITS_PER_BYTE - last_bits); + } + return; +} + +static void +cat_build_header(__u8 *header, const __u16 len, const __u16 smallest_reg_bits, + const __u16 longest_reg_bits) +{ + int i; + __u16 start_bit = (smallest_reg_bits - 1) % BITS_PER_BYTE; + __u8 *last_byte = &header[len - 1]; + + if(start_bit == 0) + start_bit = 1; /* must have at least one bit in the hdr */ + + for(i=0; i < len; i++) + header[i] = 0; + + for(i = start_bit; i > 0; i--) + *last_byte = ((*last_byte) << 1) + 1; + +} + +static int +cat_sendinst(voyager_module_t *modp, voyager_asic_t *asicp, __u8 reg, __u8 op) +{ + __u8 parity, inst, inst_buf[4] = { 0 }; + __u8 iseq[VOYAGER_MAX_SCAN_PATH], hseq[VOYAGER_MAX_REG_SIZE]; + __u16 ibytes, hbytes, padbits; + int i; + + /* + * Parity is the parity of the register number + 1 (READ_REGISTER + * and WRITE_REGISTER always add '1' to the number of bits == 1) + */ + parity = (__u8)(1 + (reg & 0x01) + + ((__u8)(reg & 0x02) >> 1) + + ((__u8)(reg & 0x04) >> 2) + + ((__u8)(reg & 0x08) >> 3)) % 2; + + inst = ((parity << 7) | (reg << 2) | op); + + outb(VOYAGER_CAT_IRCYC, CAT_CMD); + if(!modp->scan_path_connected) { + if(asicp->asic_id != VOYAGER_CAT_ID) { + printk("**WARNING***: cat_sendinst has disconnected scan path not to CAT asic\n"); + return 1; + } + outb(VOYAGER_CAT_HEADER, CAT_DATA); + outb(inst, CAT_DATA); + if(inb(CAT_DATA) != VOYAGER_CAT_HEADER) { + CDEBUG(("VOYAGER CAT: cat_sendinst failed to get CAT_HEADER\n")); + return 1; + } + return 0; + } + ibytes = modp->inst_bits / BITS_PER_BYTE; + if((padbits = modp->inst_bits % BITS_PER_BYTE) != 0) { + padbits = BITS_PER_BYTE - padbits; + ibytes++; + } + hbytes = modp->largest_reg / BITS_PER_BYTE; + if(modp->largest_reg % BITS_PER_BYTE) + hbytes++; + CDEBUG(("cat_sendinst: ibytes=%d, hbytes=%d\n", ibytes, hbytes)); + /* initialise the instruction sequence to 0xff */ + for(i=0; i < ibytes + hbytes; i++) + iseq[i] = 0xff; + cat_build_header(hseq, hbytes, modp->smallest_reg, modp->largest_reg); + cat_pack(iseq, modp->inst_bits, hseq, hbytes * BITS_PER_BYTE); + inst_buf[0] = inst; + inst_buf[1] = 0xFF >> (modp->largest_reg % BITS_PER_BYTE); + cat_pack(iseq, asicp->bit_location, inst_buf, asicp->ireg_length); +#ifdef VOYAGER_CAT_DEBUG + printk("ins = 0x%x, iseq: ", inst); + for(i=0; i< ibytes + hbytes; i++) + printk("0x%x ", iseq[i]); + printk("\n"); +#endif + if(cat_shiftout(iseq, ibytes, hbytes, padbits)) { + CDEBUG(("VOYAGER CAT: cat_sendinst: cat_shiftout failed\n")); + return 1; + } + CDEBUG(("CAT SHIFTOUT DONE\n")); + return 0; +} + +static int +cat_getdata(voyager_module_t *modp, voyager_asic_t *asicp, __u8 reg, + __u8 *value) +{ + if(!modp->scan_path_connected) { + if(asicp->asic_id != VOYAGER_CAT_ID) { + CDEBUG(("VOYAGER CAT: ERROR: cat_getdata to CAT asic with scan path connected\n")); + return 1; + } + if(reg > VOYAGER_SUBADDRHI) + outb(VOYAGER_CAT_RUN, CAT_CMD); + outb(VOYAGER_CAT_DRCYC, CAT_CMD); + outb(VOYAGER_CAT_HEADER, CAT_DATA); + *value = inb(CAT_DATA); + outb(0xAA, CAT_DATA); + if(inb(CAT_DATA) != VOYAGER_CAT_HEADER) { + CDEBUG(("cat_getdata: failed to get VOYAGER_CAT_HEADER\n")); + return 1; + } + return 0; + } + else { + __u16 sbits = modp->num_asics -1 + asicp->ireg_length; + __u16 sbytes = sbits / BITS_PER_BYTE; + __u16 tbytes; + __u8 string[VOYAGER_MAX_SCAN_PATH], trailer[VOYAGER_MAX_REG_SIZE]; + __u8 padbits; + int i; + + outb(VOYAGER_CAT_DRCYC, CAT_CMD); + + if((padbits = sbits % BITS_PER_BYTE) != 0) { + padbits = BITS_PER_BYTE - padbits; + sbytes++; + } + tbytes = asicp->ireg_length / BITS_PER_BYTE; + if(asicp->ireg_length % BITS_PER_BYTE) + tbytes++; + CDEBUG(("cat_getdata: tbytes = %d, sbytes = %d, padbits = %d\n", + tbytes, sbytes, padbits)); + cat_build_header(trailer, tbytes, 1, asicp->ireg_length); + + + for(i = tbytes - 1; i >= 0; i--) { + outb(trailer[i], CAT_DATA); + string[sbytes + i] = inb(CAT_DATA); + } + + for(i = sbytes - 1; i >= 0; i--) { + outb(0xaa, CAT_DATA); + string[i] = inb(CAT_DATA); + } + *value = 0; + cat_unpack(string, padbits + (tbytes * BITS_PER_BYTE) + asicp->asic_location, value, asicp->ireg_length); +#ifdef VOYAGER_CAT_DEBUG + printk("value=0x%x, string: ", *value); + for(i=0; i< tbytes+sbytes; i++) + printk("0x%x ", string[i]); + printk("\n"); +#endif + + /* sanity check the rest of the return */ + for(i=0; i < tbytes; i++) { + __u8 input = 0; + + cat_unpack(string, padbits + (i * BITS_PER_BYTE), &input, BITS_PER_BYTE); + if(trailer[i] != input) { + CDEBUG(("cat_getdata: failed to sanity check rest of ret(%d) 0x%x != 0x%x\n", i, input, trailer[i])); + return 1; + } + } + CDEBUG(("cat_getdata DONE\n")); + return 0; + } +} + +static int +cat_shiftout(__u8 *data, __u16 data_bytes, __u16 header_bytes, __u8 pad_bits) +{ + int i; + + for(i = data_bytes + header_bytes - 1; i >= header_bytes; i--) + outb(data[i], CAT_DATA); + + for(i = header_bytes - 1; i >= 0; i--) { + __u8 header = 0; + __u8 input; + + outb(data[i], CAT_DATA); + input = inb(CAT_DATA); + CDEBUG(("cat_shiftout: returned 0x%x\n", input)); + cat_unpack(data, ((data_bytes + i) * BITS_PER_BYTE) - pad_bits, + &header, BITS_PER_BYTE); + if(input != header) { + CDEBUG(("VOYAGER CAT: cat_shiftout failed to return header 0x%x != 0x%x\n", input, header)); + return 1; + } + } + return 0; +} + +static int +cat_senddata(voyager_module_t *modp, voyager_asic_t *asicp, + __u8 reg, __u8 value) +{ + outb(VOYAGER_CAT_DRCYC, CAT_CMD); + if(!modp->scan_path_connected) { + if(asicp->asic_id != VOYAGER_CAT_ID) { + CDEBUG(("VOYAGER CAT: ERROR: scan path disconnected when asic != CAT\n")); + return 1; + } + outb(VOYAGER_CAT_HEADER, CAT_DATA); + outb(value, CAT_DATA); + if(inb(CAT_DATA) != VOYAGER_CAT_HEADER) { + CDEBUG(("cat_senddata: failed to get correct header response to sent data\n")); + return 1; + } + if(reg > VOYAGER_SUBADDRHI) { + outb(VOYAGER_CAT_RUN, CAT_CMD); + outb(VOYAGER_CAT_END, CAT_CMD); + outb(VOYAGER_CAT_RUN, CAT_CMD); + } + + return 0; + } + else { + __u16 hbytes = asicp->ireg_length / BITS_PER_BYTE; + __u16 dbytes = (modp->num_asics - 1 + asicp->ireg_length)/BITS_PER_BYTE; + __u8 padbits, dseq[VOYAGER_MAX_SCAN_PATH], + hseq[VOYAGER_MAX_REG_SIZE]; + int i; + + if((padbits = (modp->num_asics - 1 + + asicp->ireg_length) % BITS_PER_BYTE) != 0) { + padbits = BITS_PER_BYTE - padbits; + dbytes++; + } + if(asicp->ireg_length % BITS_PER_BYTE) + hbytes++; + + cat_build_header(hseq, hbytes, 1, asicp->ireg_length); + + for(i = 0; i < dbytes + hbytes; i++) + dseq[i] = 0xff; + CDEBUG(("cat_senddata: dbytes=%d, hbytes=%d, padbits=%d\n", + dbytes, hbytes, padbits)); + cat_pack(dseq, modp->num_asics - 1 + asicp->ireg_length, + hseq, hbytes * BITS_PER_BYTE); + cat_pack(dseq, asicp->asic_location, &value, + asicp->ireg_length); +#ifdef VOYAGER_CAT_DEBUG + printk("dseq "); + for(i=0; i 1) { + /* set auto increment */ + __u8 newval; + + if(cat_read(modp, asicp, VOYAGER_AUTO_INC_REG, &val)) { + CDEBUG(("cat_subaddrsetup: read of VOYAGER_AUTO_INC_REG failed\n")); + return 1; + } + CDEBUG(("cat_subaddrsetup: VOYAGER_AUTO_INC_REG = 0x%x\n", val)); + newval = val | VOYAGER_AUTO_INC; + if(newval != val) { + if(cat_write(modp, asicp, VOYAGER_AUTO_INC_REG, val)) { + CDEBUG(("cat_subaddrsetup: write to VOYAGER_AUTO_INC_REG failed\n")); + return 1; + } + } + } + if(cat_write(modp, asicp, VOYAGER_SUBADDRLO, (__u8)(offset &0xff))) { + CDEBUG(("cat_subaddrsetup: write to SUBADDRLO failed\n")); + return 1; + } + if(asicp->subaddr > VOYAGER_SUBADDR_LO) { + if(cat_write(modp, asicp, VOYAGER_SUBADDRHI, (__u8)(offset >> 8))) { + CDEBUG(("cat_subaddrsetup: write to SUBADDRHI failed\n")); + return 1; + } + cat_read(modp, asicp, VOYAGER_SUBADDRHI, &val); + CDEBUG(("cat_subaddrsetup: offset = %d, hi = %d\n", offset, val)); + } + cat_read(modp, asicp, VOYAGER_SUBADDRLO, &val); + CDEBUG(("cat_subaddrsetup: offset = %d, lo = %d\n", offset, val)); + return 0; +} + +static int +cat_subwrite(voyager_module_t *modp, voyager_asic_t *asicp, __u16 offset, + __u16 len, void *buf) +{ + int i, retval; + + /* FIXME: need special actions for VOYAGER_CAT_ID here */ + if(asicp->asic_id == VOYAGER_CAT_ID) { + CDEBUG(("cat_subwrite: ATTEMPT TO WRITE TO CAT ASIC\n")); + /* FIXME -- This is supposed to be handled better + * There is a problem writing to the cat asic in the + * PSI. The 30us delay seems to work, though */ + udelay(30); + } + + if((retval = cat_subaddrsetup(modp, asicp, offset, len)) != 0) { + printk("cat_subwrite: cat_subaddrsetup FAILED\n"); + return retval; + } + + if(cat_sendinst(modp, asicp, VOYAGER_SUBADDRDATA, VOYAGER_WRITE_CONFIG)) { + printk("cat_subwrite: cat_sendinst FAILED\n"); + return 1; + } + for(i = 0; i < len; i++) { + if(cat_senddata(modp, asicp, 0xFF, ((__u8 *)buf)[i])) { + printk("cat_subwrite: cat_sendata element at %d FAILED\n", i); + return 1; + } + } + return 0; +} +static int +cat_subread(voyager_module_t *modp, voyager_asic_t *asicp, __u16 offset, + __u16 len, void *buf) +{ + int i, retval; + + if((retval = cat_subaddrsetup(modp, asicp, offset, len)) != 0) { + CDEBUG(("cat_subread: cat_subaddrsetup FAILED\n")); + return retval; + } + + if(cat_sendinst(modp, asicp, VOYAGER_SUBADDRDATA, VOYAGER_READ_CONFIG)) { + CDEBUG(("cat_subread: cat_sendinst failed\n")); + return 1; + } + for(i = 0; i < len; i++) { + if(cat_getdata(modp, asicp, 0xFF, + &((__u8 *)buf)[i])) { + CDEBUG(("cat_subread: cat_getdata element %d failed\n", i)); + return 1; + } + } + return 0; +} + + +/* buffer for storing EPROM data read in during initialisation */ +static __initdata __u8 eprom_buf[0xFFFF]; +static voyager_module_t *voyager_initial_module; + +/* Initialise the cat bus components. We assume this is called by the + * boot cpu *after* all memory initialisation has been done (so we can + * use kmalloc) but before smp initialisation, so we can probe the SMP + * configuration and pick up necessary information. */ +void +voyager_cat_init(void) +{ + voyager_module_t **modpp = &voyager_initial_module; + voyager_asic_t **asicpp; + voyager_asic_t *qabc_asic = NULL; + int i, j; + unsigned long qic_addr = 0; + __u8 qabc_data[0x20]; + __u8 num_submodules, val; + voyager_eprom_hdr_t *eprom_hdr = (voyager_eprom_hdr_t *)&eprom_buf[0]; + + __u8 cmos[4]; + unsigned long addr; + + /* initiallise the SUS mailbox */ + for(i=0; iSUS_version); + voyager_SUS->kernel_version = VOYAGER_MAILBOX_VERSION; + voyager_SUS->kernel_flags = VOYAGER_OS_HAS_SYSINT; + } + + /* clear the processor counts */ + voyager_extended_vic_processors = 0; + voyager_quad_processors = 0; + + + + printk("VOYAGER: beginning CAT bus probe\n"); + /* set up the SuperSet Port Block which tells us where the + * CAT communication port is */ + sspb = inb(VOYAGER_SSPB_RELOCATION_PORT) * 0x100; + VDEBUG(("VOYAGER DEBUG: sspb = 0x%x\n", sspb)); + + /* now find out if were 8 slot or normal */ + if((inb(VIC_PROC_WHO_AM_I) & EIGHT_SLOT_IDENTIFIER) + == EIGHT_SLOT_IDENTIFIER) { + voyager_8slot = 1; + printk(KERN_NOTICE "Voyager: Eight slot 51xx configuration detected\n"); + } + + for(i = VOYAGER_MIN_MODULE; + i <= VOYAGER_MAX_MODULE; i++) { + __u8 input; + int asic; + __u16 eprom_size; + __u16 sp_offset; + + outb(VOYAGER_CAT_DESELECT, VOYAGER_CAT_CONFIG_PORT); + outb(i, VOYAGER_CAT_CONFIG_PORT); + + /* check the presence of the module */ + outb(VOYAGER_CAT_RUN, CAT_CMD); + outb(VOYAGER_CAT_IRCYC, CAT_CMD); + outb(VOYAGER_CAT_HEADER, CAT_DATA); + /* stream series of alternating 1's and 0's to stimulate + * response */ + outb(0xAA, CAT_DATA); + input = inb(CAT_DATA); + outb(VOYAGER_CAT_END, CAT_CMD); + if(input != VOYAGER_CAT_HEADER) { + continue; + } + CDEBUG(("VOYAGER DEBUG: found module id 0x%x, %s\n", i, + cat_module_name(i))); + *modpp = kmalloc(sizeof(voyager_module_t), GFP_KERNEL); /*&voyager_module_storage[cat_count++];*/ + if(*modpp == NULL) { + printk("**WARNING** kmalloc failure in cat_init\n"); + continue; + } + memset(*modpp, 0, sizeof(voyager_module_t)); + /* need temporary asic for cat_subread. It will be + * filled in correctly later */ + (*modpp)->asic = kmalloc(sizeof(voyager_asic_t), GFP_KERNEL); /*&voyager_asic_storage[asic_count];*/ + if((*modpp)->asic == NULL) { + printk("**WARNING** kmalloc failure in cat_init\n"); + continue; + } + memset((*modpp)->asic, 0, sizeof(voyager_asic_t)); + (*modpp)->asic->asic_id = VOYAGER_CAT_ID; + (*modpp)->asic->subaddr = VOYAGER_SUBADDR_HI; + (*modpp)->module_addr = i; + (*modpp)->scan_path_connected = 0; + if(i == VOYAGER_PSI) { + /* Exception leg for modules with no EEPROM */ + printk("Module \"%s\"\n", cat_module_name(i)); + continue; + } + + CDEBUG(("cat_init: Reading eeprom for module 0x%x at offset %d\n", i, VOYAGER_XSUM_END_OFFSET)); + outb(VOYAGER_CAT_RUN, CAT_CMD); + cat_disconnect(*modpp, (*modpp)->asic); + if(cat_subread(*modpp, (*modpp)->asic, + VOYAGER_XSUM_END_OFFSET, sizeof(eprom_size), + &eprom_size)) { + printk("**WARNING**: Voyager couldn't read EPROM size for module 0x%x\n", i); + outb(VOYAGER_CAT_END, CAT_CMD); + continue; + } + if(eprom_size > sizeof(eprom_buf)) { + printk("**WARNING**: Voyager insufficient size to read EPROM data, module 0x%x. Need %d\n", i, eprom_size); + outb(VOYAGER_CAT_END, CAT_CMD); + continue; + } + outb(VOYAGER_CAT_END, CAT_CMD); + outb(VOYAGER_CAT_RUN, CAT_CMD); + CDEBUG(("cat_init: module 0x%x, eeprom_size %d\n", i, eprom_size)); + if(cat_subread(*modpp, (*modpp)->asic, 0, + eprom_size, eprom_buf)) { + outb(VOYAGER_CAT_END, CAT_CMD); + continue; + } + outb(VOYAGER_CAT_END, CAT_CMD); + printk("Module \"%s\", version 0x%x, tracer 0x%x, asics %d\n", + cat_module_name(i), eprom_hdr->version_id, + *((__u32 *)eprom_hdr->tracer), eprom_hdr->num_asics); + (*modpp)->ee_size = eprom_hdr->ee_size; + (*modpp)->num_asics = eprom_hdr->num_asics; + asicpp = &((*modpp)->asic); + sp_offset = eprom_hdr->scan_path_offset; + /* All we really care about are the Quad cards. We + * identify them because they are in a processor slot + * and have only four asics */ + if((i < 0x10 || (i>=0x14 && i < 0x1c) || i>0x1f)) { + modpp = &((*modpp)->next); + continue; + } + /* Now we know it's in a processor slot, does it have + * a quad baseboard submodule */ + outb(VOYAGER_CAT_RUN, CAT_CMD); + cat_read(*modpp, (*modpp)->asic, VOYAGER_SUBMODPRESENT, + &num_submodules); + /* lowest two bits, active low */ + num_submodules = ~(0xfc | num_submodules); + CDEBUG(("VOYAGER CAT: %d submodules present\n", num_submodules)); + if(num_submodules == 0) { + /* fill in the dyadic extended processors */ + __u8 cpu = i & 0x07; + + printk("Module \"%s\": Dyadic Processor Card\n", + cat_module_name(i)); + voyager_extended_vic_processors |= (1<asic, VOYAGER_SUBMODSELECT, &val); + CDEBUG(("cat_init: SUBMODSELECT value = 0x%x\n", val)); + val = (val & 0x7c) | VOYAGER_QUAD_BASEBOARD; + cat_write(*modpp, (*modpp)->asic, VOYAGER_SUBMODSELECT, val); + + outb(VOYAGER_CAT_END, CAT_CMD); + + + CDEBUG(("cat_init: Reading eeprom for module 0x%x at offset %d\n", i, VOYAGER_XSUM_END_OFFSET)); + outb(VOYAGER_CAT_RUN, CAT_CMD); + cat_disconnect(*modpp, (*modpp)->asic); + if(cat_subread(*modpp, (*modpp)->asic, + VOYAGER_XSUM_END_OFFSET, sizeof(eprom_size), + &eprom_size)) { + printk("**WARNING**: Voyager couldn't read EPROM size for module 0x%x\n", i); + outb(VOYAGER_CAT_END, CAT_CMD); + continue; + } + if(eprom_size > sizeof(eprom_buf)) { + printk("**WARNING**: Voyager insufficient size to read EPROM data, module 0x%x. Need %d\n", i, eprom_size); + outb(VOYAGER_CAT_END, CAT_CMD); + continue; + } + outb(VOYAGER_CAT_END, CAT_CMD); + outb(VOYAGER_CAT_RUN, CAT_CMD); + CDEBUG(("cat_init: module 0x%x, eeprom_size %d\n", i, eprom_size)); + if(cat_subread(*modpp, (*modpp)->asic, 0, + eprom_size, eprom_buf)) { + outb(VOYAGER_CAT_END, CAT_CMD); + continue; + } + outb(VOYAGER_CAT_END, CAT_CMD); + /* Now do everything for the QBB submodule 1 */ + (*modpp)->ee_size = eprom_hdr->ee_size; + (*modpp)->num_asics = eprom_hdr->num_asics; + asicpp = &((*modpp)->asic); + sp_offset = eprom_hdr->scan_path_offset; + /* get rid of the dummy CAT asic and read the real one */ + kfree((*modpp)->asic); + for(asic=0; asic < (*modpp)->num_asics; asic++) { + int j; + voyager_asic_t *asicp = *asicpp + = kmalloc(sizeof(voyager_asic_t), GFP_KERNEL); /*&voyager_asic_storage[asic_count++];*/ + voyager_sp_table_t *sp_table; + voyager_at_t *asic_table; + voyager_jtt_t *jtag_table; + + if(asicp == NULL) { + printk("**WARNING** kmalloc failure in cat_init\n"); + continue; + } + memset(asicp, 0, sizeof(voyager_asic_t)); + asicpp = &(asicp->next); + asicp->asic_location = asic; + sp_table = (voyager_sp_table_t *)(eprom_buf + sp_offset); + asicp->asic_id = sp_table->asic_id; + asic_table = (voyager_at_t *)(eprom_buf + sp_table->asic_data_offset); + for(j=0; j<4; j++) + asicp->jtag_id[j] = asic_table->jtag_id[j]; + jtag_table = (voyager_jtt_t *)(eprom_buf + asic_table->jtag_offset); + asicp->ireg_length = jtag_table->ireg_len; + asicp->bit_location = (*modpp)->inst_bits; + (*modpp)->inst_bits += asicp->ireg_length; + if(asicp->ireg_length > (*modpp)->largest_reg) + (*modpp)->largest_reg = asicp->ireg_length; + if (asicp->ireg_length < (*modpp)->smallest_reg || + (*modpp)->smallest_reg == 0) + (*modpp)->smallest_reg = asicp->ireg_length; + CDEBUG(("asic 0x%x, ireg_length=%d, bit_location=%d\n", + asicp->asic_id, asicp->ireg_length, + asicp->bit_location)); + if(asicp->asic_id == VOYAGER_QUAD_QABC) { + CDEBUG(("VOYAGER CAT: QABC ASIC found\n")); + qabc_asic = asicp; + } + sp_offset += sizeof(voyager_sp_table_t); + } + CDEBUG(("Module inst_bits = %d, largest_reg = %d, smallest_reg=%d\n", + (*modpp)->inst_bits, (*modpp)->largest_reg, + (*modpp)->smallest_reg)); + /* OK, now we have the QUAD ASICs set up, use them. + * we need to: + * + * 1. Find the Memory area for the Quad CPIs. + * 2. Find the Extended VIC processor + * 3. Configure a second extended VIC processor (This + * cannot be done for the 51xx. + * */ + outb(VOYAGER_CAT_RUN, CAT_CMD); + cat_connect(*modpp, (*modpp)->asic); + CDEBUG(("CAT CONNECTED!!\n")); + cat_subread(*modpp, qabc_asic, 0, sizeof(qabc_data), qabc_data); + qic_addr = qabc_data[5] << 8; + qic_addr = (qic_addr | qabc_data[6]) << 8; + qic_addr = (qic_addr | qabc_data[7]) << 8; + printk("Module \"%s\": Quad Processor Card; CPI 0x%lx, SET=0x%x\n", + cat_module_name(i), qic_addr, qabc_data[8]); +#if 0 /* plumbing fails---FIXME */ + if((qabc_data[8] & 0xf0) == 0) { + /* FIXME: 32 way 8 CPU slot monster cannot be + * plumbed this way---need to check for it */ + + printk("Plumbing second Extended Quad Processor\n"); + /* second VIC line hardwired to Quad CPU 1 */ + qabc_data[8] |= 0x20; + cat_subwrite(*modpp, qabc_asic, 8, 1, &qabc_data[8]); +#ifdef VOYAGER_CAT_DEBUG + /* verify plumbing */ + cat_subread(*modpp, qabc_asic, 8, 1, &qabc_data[8]); + if((qabc_data[8] & 0xf0) == 0) { + CDEBUG(("PLUMBING FAILED: 0x%x\n", qabc_data[8])); + } +#endif + } +#endif + + { + struct resource *res = kmalloc(sizeof(struct resource),GFP_KERNEL); + memset(res, 0, sizeof(struct resource)); + res->name = kmalloc(128, GFP_KERNEL); + sprintf((char *)res->name, "Voyager %s Quad CPI", cat_module_name(i)); + res->start = qic_addr; + res->end = qic_addr + 0x3ff; + request_resource(&iomem_resource, res); + } + + qic_addr = (unsigned long)ioremap(qic_addr, 0x400); + + for(j = 0; j < 4; j++) { + __u8 cpu; + + if(voyager_8slot) { + /* 8 slot has a different mapping, + * each slot has only one vic line, so + * 1 cpu in each slot must be < 8 */ + cpu = (i & 0x07) + j*8; + } else { + cpu = (i & 0x03) + j*4; + } + if( (qabc_data[8] & (1<next); + } + *modpp = NULL; + printk("CAT Bus Initialisation finished: extended procs 0x%x, quad procs 0x%x, allowed vic boot = 0x%x\n", voyager_extended_vic_processors, voyager_quad_processors, voyager_allowed_boot_processors); + request_resource(&ioport_resource, &vic_res); + if(voyager_quad_processors) + request_resource(&ioport_resource, &qic_res); + /* set up the front power switch */ +} + +int +voyager_cat_readb(__u8 module, __u8 asic, int reg) +{ + return 0; +} + +static int +cat_disconnect(voyager_module_t *modp, voyager_asic_t *asicp) +{ + __u8 val; + int err = 0; + + if(!modp->scan_path_connected) + return 0; + if(asicp->asic_id != VOYAGER_CAT_ID) { + CDEBUG(("cat_disconnect: ASIC is not CAT\n")); + return 1; + } + err = cat_read(modp, asicp, VOYAGER_SCANPATH, &val); + if(err) { + CDEBUG(("cat_disconnect: failed to read SCANPATH\n")); + return err; + } + val &= VOYAGER_DISCONNECT_ASIC; + err = cat_write(modp, asicp, VOYAGER_SCANPATH, val); + if(err) { + CDEBUG(("cat_disconnect: failed to write SCANPATH\n")); + return err; + } + outb(VOYAGER_CAT_END, CAT_CMD); + outb(VOYAGER_CAT_RUN, CAT_CMD); + modp->scan_path_connected = 0; + + return 0; +} + +static int +cat_connect(voyager_module_t *modp, voyager_asic_t *asicp) +{ + __u8 val; + int err = 0; + + if(modp->scan_path_connected) + return 0; + if(asicp->asic_id != VOYAGER_CAT_ID) { + CDEBUG(("cat_connect: ASIC is not CAT\n")); + return 1; + } + + err = cat_read(modp, asicp, VOYAGER_SCANPATH, &val); + if(err) { + CDEBUG(("cat_connect: failed to read SCANPATH\n")); + return err; + } + val |= VOYAGER_CONNECT_ASIC; + err = cat_write(modp, asicp, VOYAGER_SCANPATH, val); + if(err) { + CDEBUG(("cat_connect: failed to write SCANPATH\n")); + return err; + } + outb(VOYAGER_CAT_END, CAT_CMD); + outb(VOYAGER_CAT_RUN, CAT_CMD); + modp->scan_path_connected = 1; + + return 0; +} + +void +voyager_cat_power_off(void) +{ + /* Power the machine off by writing to the PSI over the CAT + * bus */ + __u8 data; + voyager_module_t psi = { 0 }; + voyager_asic_t psi_asic = { 0 }; + + psi.asic = &psi_asic; + psi.asic->asic_id = VOYAGER_CAT_ID; + psi.asic->subaddr = VOYAGER_SUBADDR_HI; + psi.module_addr = VOYAGER_PSI; + psi.scan_path_connected = 0; + + outb(VOYAGER_CAT_END, CAT_CMD); + /* Connect the PSI to the CAT Bus */ + outb(VOYAGER_CAT_DESELECT, VOYAGER_CAT_CONFIG_PORT); + outb(VOYAGER_PSI, VOYAGER_CAT_CONFIG_PORT); + outb(VOYAGER_CAT_RUN, CAT_CMD); + cat_disconnect(&psi, &psi_asic); + /* Read the status */ + cat_subread(&psi, &psi_asic, VOYAGER_PSI_GENERAL_REG, 1, &data); + outb(VOYAGER_CAT_END, CAT_CMD); + CDEBUG(("PSI STATUS 0x%x\n", data)); + /* These two writes are power off prep and perform */ + data = PSI_CLEAR; + outb(VOYAGER_CAT_RUN, CAT_CMD); + cat_subwrite(&psi, &psi_asic, VOYAGER_PSI_GENERAL_REG, 1, &data); + outb(VOYAGER_CAT_END, CAT_CMD); + data = PSI_POWER_DOWN; + outb(VOYAGER_CAT_RUN, CAT_CMD); + cat_subwrite(&psi, &psi_asic, VOYAGER_PSI_GENERAL_REG, 1, &data); + outb(VOYAGER_CAT_END, CAT_CMD); +} + +struct voyager_status voyager_status = { 0 }; + +void +voyager_cat_psi(__u8 cmd, __u16 reg, __u8 *data) +{ + voyager_module_t psi = { 0 }; + voyager_asic_t psi_asic = { 0 }; + + psi.asic = &psi_asic; + psi.asic->asic_id = VOYAGER_CAT_ID; + psi.asic->subaddr = VOYAGER_SUBADDR_HI; + psi.module_addr = VOYAGER_PSI; + psi.scan_path_connected = 0; + + outb(VOYAGER_CAT_END, CAT_CMD); + /* Connect the PSI to the CAT Bus */ + outb(VOYAGER_CAT_DESELECT, VOYAGER_CAT_CONFIG_PORT); + outb(VOYAGER_PSI, VOYAGER_CAT_CONFIG_PORT); + outb(VOYAGER_CAT_RUN, CAT_CMD); + cat_disconnect(&psi, &psi_asic); + switch(cmd) { + case VOYAGER_PSI_READ: + cat_read(&psi, &psi_asic, reg, data); + break; + case VOYAGER_PSI_WRITE: + cat_write(&psi, &psi_asic, reg, *data); + break; + case VOYAGER_PSI_SUBREAD: + cat_subread(&psi, &psi_asic, reg, 1, data); + break; + case VOYAGER_PSI_SUBWRITE: + cat_subwrite(&psi, &psi_asic, reg, 1, data); + break; + default: + printk(KERN_ERR "Voyager PSI, unrecognised command %d\n", cmd); + break; + } + outb(VOYAGER_CAT_END, CAT_CMD); +} + +void +voyager_cat_do_common_interrupt(void) +{ + /* This is caused either by a memory parity error or something + * in the PSI */ + __u8 data; + voyager_module_t psi = { 0 }; + voyager_asic_t psi_asic = { 0 }; + struct voyager_psi psi_reg; + int i; + re_read: + psi.asic = &psi_asic; + psi.asic->asic_id = VOYAGER_CAT_ID; + psi.asic->subaddr = VOYAGER_SUBADDR_HI; + psi.module_addr = VOYAGER_PSI; + psi.scan_path_connected = 0; + + outb(VOYAGER_CAT_END, CAT_CMD); + /* Connect the PSI to the CAT Bus */ + outb(VOYAGER_CAT_DESELECT, VOYAGER_CAT_CONFIG_PORT); + outb(VOYAGER_PSI, VOYAGER_CAT_CONFIG_PORT); + outb(VOYAGER_CAT_RUN, CAT_CMD); + cat_disconnect(&psi, &psi_asic); + /* Read the status. NOTE: Need to read *all* the PSI regs here + * otherwise the cmn int will be reasserted */ + for(i = 0; i < sizeof(psi_reg.regs); i++) { + cat_read(&psi, &psi_asic, i, &((__u8 *)&psi_reg.regs)[i]); + } + outb(VOYAGER_CAT_END, CAT_CMD); + if((psi_reg.regs.checkbit & 0x02) == 0) { + psi_reg.regs.checkbit |= 0x02; + cat_write(&psi, &psi_asic, 5, psi_reg.regs.checkbit); + printk("VOYAGER RE-READ PSI\n"); + goto re_read; + } + outb(VOYAGER_CAT_RUN, CAT_CMD); + for(i = 0; i < sizeof(psi_reg.subregs); i++) { + /* This looks strange, but the PSI doesn't do auto increment + * correctly */ + cat_subread(&psi, &psi_asic, VOYAGER_PSI_SUPPLY_REG + i, + 1, &((__u8 *)&psi_reg.subregs)[i]); + } + outb(VOYAGER_CAT_END, CAT_CMD); +#ifdef VOYAGER_CAT_DEBUG + printk("VOYAGER PSI: "); + for(i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int reboot_smp = 0; + +/* TLB state -- visible externally, indexed physically */ +struct tlb_state cpu_tlbstate[NR_CPUS] __cacheline_aligned = {[0 ... NR_CPUS-1] = { &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 }; + +/* Set when the idlers are all forked - Set in main.c but not actually + * used by any other parts of the kernel */ +int smp_threads_ready = 0; + +/* per CPU data structure (for /proc/cpuinfo et al), visible externally + * indexed physically */ +struct cpuinfo_x86 cpu_data[NR_CPUS] __cacheline_aligned; + +/* 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 */ +volatile unsigned long smp_invalidate_needed; + +/* Bitmask of currently online CPUs - used by setup.c for + /proc/cpuinfo, visible externally but still physical */ +unsigned long cpu_online_map = 0; + +/* Bitmask of CPUs present in the system - exported by i386_syms.c, used + * by scheduler but indexed physically */ +unsigned long phys_cpu_present_map = 0; + +/* estimate of time used to flush the SMP-local cache - used in + * processor affinity calculations */ +cycles_t cacheflush_time = 0; + +/* cache decay ticks for scheduler---a fairly useless quantity for the + voyager system with its odd affinity and huge L3 cache */ +unsigned long cache_decay_ticks = 20; + + +/* 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, unsigned long 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); +static inline void wrapper_smp_local_timer_interrupt(struct pt_regs *); + +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 mask; + __u8 cpu; + + for_each_cpu(cpu, mask) { + 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; +} + +/* + * 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 */ +int __init +start_secondary(void *unused) +{ + __u8 cpuid = hard_smp_processor_id(); + /* external functions not defined in the headers */ + extern void calibrate_delay(void); + extern int cpu_idle(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(); + + set_bit(cpuid, &cpu_callin_map); + + /* signal that we're done */ + cpu_booted_map = 1; + + while (!test_bit(cpuid, &smp_commenced_mask)) + rep_nop(); + + local_flush_tlb(); + + set_bit(cpuid, &cpu_online_map); + wmb(); + return cpu_idle(); +} + +static struct task_struct * __init +fork_by_hand(void) +{ + struct pt_regs regs; + /* don't care about the eip and regs settings since we'll + * never reschedule the forked task. */ + return do_fork(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL); +} + + +/* 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_by_hand(); + if(IS_ERR(idle)) + panic("failed fork for CPU%d", cpu); + + init_idle(idle, cpu); + + idle->thread.eip = (unsigned long) start_secondary; + unhash_process(idle); + /* init_tasks (in sched.c) is indexed logically */ +#if 0 + // for AC kernels + stack_start.esp = (THREAD_SIZE + (__u8 *)TSK_TO_KSTACK(idle)); +#else + stack_start.esp = (void *) (1024 + PAGE_SIZE + (char *)idle->thread_info); +#endif + /* 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] = 0x102007; +#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<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. */ +asmlinkage void +smp_vic_sys_interrupt(void) +{ + 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. */ +asmlinkage void +smp_vic_cmn_interrupt(void) +{ + static __u8 in_cmn_int = 0; + static spinlock_t cmn_int_lock = SPIN_LOCK_UNLOCKED; + + /* 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. */ +asmlinkage void +smp_reschedule_interrupt(void) +{ + /* do nothing */ +} + +static struct mm_struct * flush_mm; +static unsigned long flush_va; +static spinlock_t tlbstate_lock = SPIN_LOCK_UNLOCKED; +#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 void inline +leave_mm (unsigned long cpu) +{ + if (cpu_tlbstate[cpu].state == TLBSTATE_OK) + BUG(); + clear_bit(cpu, &cpu_tlbstate[cpu].active_mm->cpu_vm_mask); + load_cr3(swapper_pg_dir); +} + + +/* + * Invalidate call-back + */ +asmlinkage void +smp_invalidate_interrupt(void) +{ + __u8 cpu = get_cpu(); + + if(!test_bit(cpu, &smp_invalidate_needed)) + goto out; + /* 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 == cpu_tlbstate[cpu].active_mm) { + if (cpu_tlbstate[cpu].state == TLBSTATE_OK) { + if (flush_va == FLUSH_ALL) + local_flush_tlb(); + else + __flush_tlb_one(flush_va); + } else + leave_mm(cpu); + } + clear_bit(cpu, &smp_invalidate_needed); + out: + put_cpu_no_resched(); +} + +/* 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 & cpu_online_map) != 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) { + 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 = mm->cpu_vm_mask & ~(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 = mm->cpu_vm_mask & ~(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 = mm->cpu_vm_mask & ~(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(); +} + +/* enable the requested IRQs */ +asmlinkage 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); + } +} + +/* Call this function on all CPUs using the function_interrupt above + 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) +{ + struct call_data_struct data; + __u32 mask = cpu_online_map; + + mask &= ~(1<= 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) + 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 recieve CPI */ +static void +send_CPI(__u32 cpuset, __u8 cpi) +{ + int mask; + __u8 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_cpu(cpu, mask) { + 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) +{ + 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) +{ + int tmpmask; + /* linux doesn't to processor-irq affinity, so enable on + * all CPUs we know about */ + __u8 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_cpu(cpu, tmpmask) { + unsigned long cpu_mask = (1<