diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-03-25 23:40:36 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-03-25 23:40:36 +0000 |
commit | 7206675c40394c78a90e74812bbdbf8cf3cca1be (patch) | |
tree | 251895cf5a0008e2b4ce438cb01ad4d55fb5b97b /arch/mips/sgi/kernel | |
parent | beb116954b9b7f3bb56412b2494b562f02b864b1 (diff) |
Import of Linux/MIPS 2.1.14.2
Diffstat (limited to 'arch/mips/sgi/kernel')
-rw-r--r-- | arch/mips/sgi/kernel/.cvsignore | 1 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/Makefile | 30 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/indyIRQ.S | 138 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/indy_hpc.c | 114 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/indy_int.c | 579 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/indy_mc.c | 153 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/indy_timer.c | 298 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/reset.c | 14 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/setup.c | 83 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/system.c | 168 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/time.c | 14 |
11 files changed, 1592 insertions, 0 deletions
diff --git a/arch/mips/sgi/kernel/.cvsignore b/arch/mips/sgi/kernel/.cvsignore new file mode 100644 index 000000000..4671378ae --- /dev/null +++ b/arch/mips/sgi/kernel/.cvsignore @@ -0,0 +1 @@ +.depend diff --git a/arch/mips/sgi/kernel/Makefile b/arch/mips/sgi/kernel/Makefile new file mode 100644 index 000000000..c79023b23 --- /dev/null +++ b/arch/mips/sgi/kernel/Makefile @@ -0,0 +1,30 @@ +# $Id: Makefile,v 1.5 1996/06/08 12:08:38 dm Exp $ +# Makefile for the SGI specific kernel interface routines +# under Linux. +# +# 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... + +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +OBJS = indy_mc.o indy_hpc.o indy_int.o system.o indy_timer.o indyIRQ.o \ + reset.o setup.o time.o + +all: sgikern.a + +sgikern.a: $(OBJS) + $(AR) rcs sgikern.a $(OBJS) + sync + +indyIRQ.o: indyIRQ.S + +dep: + $(CPP) -M *.c > .depend + +include $(TOPDIR)/Rules.make diff --git a/arch/mips/sgi/kernel/indyIRQ.S b/arch/mips/sgi/kernel/indyIRQ.S new file mode 100644 index 000000000..a7058d3ee --- /dev/null +++ b/arch/mips/sgi/kernel/indyIRQ.S @@ -0,0 +1,138 @@ +/* $Id: indyIRQ.S,v 1.5 1996/06/29 12:41:12 dm Exp $ + * indyIRQ.S: Interrupt exception dispatch code for FullHouse and + * Guiness. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + */ + +#include <asm/asm.h> +#include <asm/mipsconfig.h> +#include <asm/mipsregs.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> + + /* A lot of complication here is taken away because: + * + * 1) We handle one interrupt and return, sitting in a loop + * and moving across all the pending IRQ bits in the cause + * register is _NOT_ the answer, the common case is one + * pending IRQ so optimize in that direction. + * + * 2) We need not check against bits in the status register + * IRQ mask, that would make this routine slow as hell. + * + * 3) Linux only thinks in terms of all IRQs on or all IRQs + * off, nothing in between like BSD spl() brain-damage. + * + * Furthermore, the IRQs on the INDY look basically (barring + * software IRQs which we don't use at all) like: + * + * MIPS IRQ Source + * -------- ------ + * 0 Software (ignored) + * 1 Software (ignored) + * 2 Local IRQ level zero + * 3 Local IRQ level one + * 4 8254 Timer zero + * 5 8254 Timer one + * 6 Bus Error + * 7 R4k timer (what we use) + * + * We handle the IRQ according to _our_ priority which is: + * + * Highest ---- R4k Timer + * Local IRQ zero + * Local IRQ one + * Bus Error + * 8254 Timer zero + * Lowest ---- 8254 Timer one + * + * then we just return, if multiple IRQs are pending then + * we will just take another exception, big deal. + */ + + .text + .set noreorder + .set noat + .align 5 + NESTED(indyIRQ, PT_SIZE, sp) + SAVE_ALL + CLI + .set at + mfc0 s0, CP0_CAUSE # get irq mask + lui s3, %hi(intr_count) + + /* First we check for r4k counter/timer IRQ. */ + andi a0, s0, CAUSEF_IP7 + beq a0, zero, 1f + andi a0, s0, CAUSEF_IP2 # delay slot, check local level zero + + /* Wheee, a timer interrupt. */ + lw s7, %lo(intr_count)(s3) + move a0, sp + addiu t0, s7, 1 + jal indy_timer_interrupt + sw t0, %lo(intr_count)(s3) # delay slot, set intr_count + + j ret_from_sys_call + sw s7, %lo(intr_count)(s3) # delay slot, restore intr_count + +1: + beq a0, zero, 1f + andi a0, s0, CAUSEF_IP3 # delay slot, check local level one + + /* Wheee, local level zero interrupt. */ + lw s7, %lo(intr_count)(s3) + move a0, sp + addiu t0, s7, 1 + + jal indy_local0_irqdispatch + sw t0, %lo(intr_count)(s3) + + j ret_from_sys_call + sw s7, %lo(intr_count)(s3) # delay slot, restore intr_count + +1: + beq a0, zero, 1f + andi a0, s0, CAUSEF_IP6 # delay slot, check bus error + + /* Wheee, local level one interrupt. */ + lw s7, %lo(intr_count)(s3) + move a0, sp + addiu t0, s7, 1 + jal indy_local1_irqdispatch + sw t0, %lo(intr_count)(s3) + + j ret_from_sys_call + sw s7, %lo(intr_count)(s3) + +1: + beq a0, zero, 1f + lw s7, %lo(intr_count)(s3) + + /* Wheee, an asynchronous bus error... */ + addiu t0, s7, 1 + move a0, sp + jal indy_buserror_irq + sw t0, %lo(intr_count)(s3) + + j ret_from_sys_call + sw s7, %lo(intr_count)(s3) + +1: + /* Here by mistake? This is possible, what can happen + * is that by the time we take the exception the IRQ + * pin goes low, so just leave if this is the case. + */ + andi a0, s0, (CAUSEF_IP4 | CAUSEF_IP5) + beq a0, zero, 1f + addiu t0, s7, 1 + + /* Must be one of the 8254 timers... */ + move a0, sp + jal indy_8254timer_irq + sw t0, %lo(intr_count)(s3) +1: + j ret_from_sys_call + sw s7, %lo(intr_count)(s3) + END(indyIRQ) diff --git a/arch/mips/sgi/kernel/indy_hpc.c b/arch/mips/sgi/kernel/indy_hpc.c new file mode 100644 index 000000000..30f3fe36b --- /dev/null +++ b/arch/mips/sgi/kernel/indy_hpc.c @@ -0,0 +1,114 @@ +/* $Id: indy_hpc.c,v 1.4 1996/06/29 07:06:50 dm Exp $ + * indy_hpc.c: Routines for generic manipulation of the HPC controllers. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + */ + +#include <asm/segment.h> +#include <asm/ptrace.h> +#include <asm/processor.h> +#include <asm/sgihpc.h> +#include <asm/sgint23.h> +#include <asm/sgialib.h> + +/* #define DEBUG_SGIHPC */ + +struct hpc3_regs *hpc3c0, *hpc3c1; +struct hpc3_miscregs *hpc3mregs; + +/* We need software copies of these because they are write only. */ +static unsigned long write1, write2; + +/* Machine specific identifier knobs. */ +int sgi_has_ioc2 = 0; +int sgi_guiness = 0; +int sgi_boardid; + +void sgihpc_write1_modify(int set, int clear) +{ + write1 |= set; + write1 &= ~clear; + hpc3mregs->write1 = write1; +} + +void sgihpc_write2_modify(int set, int clear) +{ + write2 |= set; + write2 &= ~clear; + hpc3mregs->write2 = write2; +} + +void sgihpc_init(void) +{ + unsigned long sid, crev, brev; + + hpc3c0 = (struct hpc3_regs *) (KSEG1 + HPC3_CHIP0_PBASE); + hpc3c1 = (struct hpc3_regs *) (KSEG1 + HPC3_CHIP1_PBASE); + hpc3mregs = (struct hpc3_miscregs *) (KSEG1 + HPC3_MREGS_PBASE); + sid = hpc3mregs->sysid; + + sid &= 0xff; + crev = (sid & 0xe0) >> 5; + brev = (sid & 0x1e) >> 1; + +#ifdef DEBUG_SGIHPC + prom_printf("sgihpc_init: crev<%2x> brev<%2x>\n", crev, brev); + prom_printf("sgihpc_init: "); +#endif + + if(sid & 1) { +#ifdef DEBUG_SGIHPC + prom_printf("GUINESS "); +#endif + sgi_guiness = 1; + } else { +#ifdef DEBUG_SGIHPC + prom_printf("FULLHOUSE "); +#endif + sgi_guiness = 0; + } + sgi_boardid = brev; + +#ifdef DEBUG_SGIHPC + prom_printf("sgi_boardid<%d> ", sgi_boardid); +#endif + + if(crev == 1) { + if((sid & 1) || (brev >= 2)) { +#ifdef DEBUG_SGIHPC + prom_printf("IOC2 "); +#endif + sgi_has_ioc2 = 1; + } else { +#ifdef DEBUG_SGIHPC + prom_printf("IOC1 revision 1 "); +#endif + } + } else { +#ifdef DEBUG_SGIHPC + prom_printf("IOC1 revision 0 "); +#endif + } +#ifdef DEBUG_SGIHPC + prom_printf("\n"); +#endif + + write1 = (HPC3_WRITE1_PRESET | + HPC3_WRITE1_KMRESET | + HPC3_WRITE1_ERESET | + HPC3_WRITE1_LC0OFF); + + write2 = (HPC3_WRITE2_EASEL | + HPC3_WRITE2_NTHRESH | + HPC3_WRITE2_TPSPEED | + HPC3_WRITE2_EPSEL | + HPC3_WRITE2_U0AMODE | + HPC3_WRITE2_U1AMODE); + + if(!sgi_guiness) + write1 |= HPC3_WRITE1_GRESET; + hpc3mregs->write1 = write1; + hpc3mregs->write2 = write2; + + hpc3c0->pbus_piocfgs[0][6] |= HPC3_PIOPCFG_HW; +} diff --git a/arch/mips/sgi/kernel/indy_int.c b/arch/mips/sgi/kernel/indy_int.c new file mode 100644 index 000000000..45600d2de --- /dev/null +++ b/arch/mips/sgi/kernel/indy_int.c @@ -0,0 +1,579 @@ +/* $Id: indy_int.c,v 1.12 1996/08/07 02:54:11 dm Exp $ + * indy_int.c: Routines for generic manipulation of the INT[23] ASIC + * found on INDY workstations.. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + */ +#include <linux/config.h> + +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/timex.h> +#include <linux/malloc.h> +#include <linux/random.h> + +#include <asm/bitops.h> +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mipsregs.h> +#include <asm/system.h> +#include <asm/vector.h> + +#include <asm/segment.h> +#include <asm/ptrace.h> +#include <asm/processor.h> +#include <asm/sgi.h> +#include <asm/sgihpc.h> +#include <asm/sgint23.h> +#include <asm/sgialib.h> + +/* #define DEBUG_SGINT */ + +struct sgi_int2_regs *sgi_i2regs; +struct sgi_int3_regs *sgi_i3regs; +struct sgi_ioc_ints *ioc_icontrol; +struct sgi_ioc_timers *ioc_timers; +volatile unsigned char *ioc_tclear; + +static char lc0msk_to_irqnr[256]; +static char lc1msk_to_irqnr[256]; +static char lc2msk_to_irqnr[256]; +static char lc3msk_to_irqnr[256]; + +extern asmlinkage void indyIRQ(void); + +#ifdef CONFIG_REMOTE_DEBUG +extern void rs_kgdb_hook(int); +#endif + +unsigned long spurious_count = 0; + +/* Local IRQ's are layed out logically like this: + * + * 0 --> 7 == local 0 interrupts + * 8 --> 15 == local 1 interrupts + * 16 --> 23 == vectored level 2 interrupts + * 24 --> 31 == vectored level 3 interrupts (not used) + */ +void disable_local_irq(unsigned int irq_nr) +{ + unsigned long flags; + + save_flags(flags); + cli(); + switch(irq_nr) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + ioc_icontrol->imask0 &= ~(1 << irq_nr); + break; + + case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: + ioc_icontrol->imask1 &= ~(1 << (irq_nr - 8)); + break; + + case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: + ioc_icontrol->cmeimask0 &= ~(1 << (irq_nr - 16)); + break; + + default: + /* This way we'll see if anyone would ever want vectored + * level 3 interrupts. Highly unlikely. + */ + printk("Yeeee, got passed irq_nr %d at disable_irq\n", irq_nr); + panic("INVALID IRQ level!"); + }; + restore_flags(flags); +} + +void enable_local_irq(unsigned int irq_nr) +{ + unsigned long flags; + save_flags(flags); + cli(); + switch(irq_nr) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + ioc_icontrol->imask0 |= (1 << irq_nr); + break; + + case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: + ioc_icontrol->imask1 |= (1 << (irq_nr - 8)); + break; + + case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: + enable_local_irq(7); + ioc_icontrol->cmeimask0 |= (1 << (irq_nr - 16)); + break; + + default: + printk("Yeeee, got passed irq_nr %d at disable_irq\n", irq_nr); + panic("INVALID IRQ level!"); + }; + restore_flags(flags); +} + +void disable_gio_irq(unsigned int irq_nr) +{ + /* XXX TODO XXX */ +} + +void enable_gio_irq(unsigned int irq_nr) +{ + /* XXX TODO XXX */ +} + +void disable_hpcdma_irq(unsigned int irq_nr) +{ + /* XXX TODO XXX */ +} + +void enable_hpcdma_irq(unsigned int irq_nr) +{ + /* XXX TODO XXX */ +} + +void disable_irq(unsigned int irq_nr) +{ + unsigned int n = irq_nr; + if(n >= SGINT_END) { + printk("whee, invalid irq_nr %d\n", irq_nr); + panic("IRQ, you lose..."); + } + if(n >= SGINT_LOCAL0 && n < SGINT_GIO) { + disable_local_irq(n - SGINT_LOCAL0); + } else if(n >= SGINT_GIO && n < SGINT_HPCDMA) { + disable_gio_irq(n - SGINT_GIO); + } else if(n >= SGINT_HPCDMA && n < SGINT_END) { + disable_hpcdma_irq(n - SGINT_HPCDMA); + } else { + panic("how did I get here?"); + } +} + +void enable_irq(unsigned int irq_nr) +{ + unsigned int n = irq_nr; + if(n >= SGINT_END) { + printk("whee, invalid irq_nr %d\n", irq_nr); + panic("IRQ, you lose..."); + } + if(n >= SGINT_LOCAL0 && n < SGINT_GIO) { + enable_local_irq(n - SGINT_LOCAL0); + } else if(n >= SGINT_GIO && n < SGINT_HPCDMA) { + enable_gio_irq(n - SGINT_GIO); + } else if(n >= SGINT_HPCDMA && n < SGINT_END) { + enable_hpcdma_irq(n - SGINT_HPCDMA); + } else { + panic("how did I get here?"); + } +} + +static void local_unex(int irq, void *data, struct pt_regs *regs) +{ + printk("Whee: unexpected local IRQ at %08lx\n", + (unsigned long) regs->cp0_epc); + printk("DUMP: stat0<%x> stat1<%x> vmeistat<%x>\n", + ioc_icontrol->istat0, ioc_icontrol->istat1, + ioc_icontrol->vmeistat); +} + +static struct irqaction *local_irq_action[24] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +int setup_indy_irq(int irq, struct irqaction * new) +{ + printk("setup_indy_irq: Yeee, don't know how to setup irq<%d> for %s %p\n", + irq, new->name, new->handler); + return 0; +} + +static struct irqaction r4ktimer_action = { + NULL, 0, 0, "R4000 timer/counter", NULL, NULL, +}; + +static struct irqaction indy_berr_action = { + NULL, 0, 0, "IP22 Bus Error", NULL, NULL, +}; + +static struct irqaction *irq_action[16] = { + NULL, NULL, NULL, NULL, + NULL, NULL, &indy_berr_action, &r4ktimer_action, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +int get_irq_list(char *buf) +{ + int i, len = 0; + int num = 0; + struct irqaction * action; + + for (i = 0 ; i < 16 ; i++, num++) { + action = irq_action[i]; + if (!action) + continue; + len += sprintf(buf+len, "%2d: %8d %c %s", + num, kstat.interrupts[num], + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ",%s %s", + (action->flags & SA_INTERRUPT) ? " +" : "", + action->name); + } + len += sprintf(buf+len, " [on-chip]\n"); + } + for (i = 0 ; i < 24 ; i++, num++) { + action = local_irq_action[i]; + if (!action) + continue; + len += sprintf(buf+len, "%2d: %8d %c %s", + num, kstat.interrupts[num], + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ",%s %s", + (action->flags & SA_INTERRUPT) ? " +" : "", + action->name); + } + len += sprintf(buf+len, " [local]\n"); + } + return len; +} + +/* + * do_IRQ handles IRQ's that have been installed without the + * SA_INTERRUPT flag: it uses the full signal-handling return + * and runs with other interrupts enabled. All relatively slow + * IRQ's should use this format: notably the keyboard/timer + * routines. + */ +asmlinkage void do_IRQ(int irq, struct pt_regs * regs) +{ + struct irqaction * action = *(irq + irq_action); + kstat.interrupts[irq]++; + printk("Got irq %d, press a key.", irq); + prom_getchar(); + romvec->imode(); + while (action) { + if (action->flags & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + action->handler(irq, action->dev_id, regs); + action = action->next; + } +} + +/* + * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return + * stuff - the handler is also running with interrupts disabled unless + * it explicitly enables them later. + */ +asmlinkage void do_fast_IRQ(int irq) +{ + struct irqaction * action = *(irq + irq_action); + + printk("Got irq %d, press a key.", irq); + prom_getchar(); + romvec->imode(); + kstat.interrupts[irq]++; + while (action) { + if (action->flags & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + action->handler(irq, action->dev_id, NULL); + action = action->next; + } +} + +int request_local_irq(unsigned int lirq, void (*func)(int, void *, struct pt_regs *), + unsigned long iflags, const char *dname, void *devid) +{ + struct irqaction *action; + + lirq -= SGINT_LOCAL0; + if(lirq >= 24 || !func) + return -EINVAL; + + action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if(!action) + return -ENOMEM; + + action->handler = func; + action->flags = iflags; + action->mask = 0; + action->name = dname; + action->dev_id = devid; + action->next = 0; + local_irq_action[lirq] = action; + enable_irq(lirq + SGINT_LOCAL0); + return 0; +} + +void free_local_irq(unsigned int lirq, void *dev_id) +{ + struct irqaction *action; + + lirq -= SGINT_LOCAL0; + if(lirq >= 24) { + printk("Aieee: trying to free bogus local irq %d\n", + lirq + SGINT_LOCAL0); + return; + } + action = local_irq_action[lirq]; + local_irq_action[lirq] = NULL; + disable_irq(lirq + SGINT_LOCAL0); + kfree(action); +} + +int request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char * devname, + void *dev_id) +{ + int retval; + struct irqaction * action; + + if (irq >= SGINT_END) + return -EINVAL; + if (!handler) + return -EINVAL; + + if((irq >= SGINT_LOCAL0) && (irq < SGINT_GIO)) + return request_local_irq(irq, handler, irqflags, devname, dev_id); + + action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_indy_irq(irq, action); + + if (retval) + kfree(action); + return retval; +} + +void free_irq(unsigned int irq, void *dev_id) +{ + struct irqaction * action, **p; + unsigned long flags; + + if (irq >= SGINT_END) { + printk("Trying to free IRQ%d\n",irq); + return; + } + if((irq >= SGINT_LOCAL0) && (irq < SGINT_GIO)) { + free_local_irq(irq, dev_id); + return; + } + for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) { + if (action->dev_id != dev_id) + continue; + + /* Found it - now free it */ + save_flags(flags); + cli(); + *p = action->next; + restore_flags(flags); + kfree(action); + return; + } + printk("Trying to free free IRQ%d\n",irq); +} + +void init_IRQ(void) +{ + int i; + + for (i = 0; i < 16 ; i++) + set_int_vector(i, 0); + irq_setup(); +} + +void indy_local0_irqdispatch(struct pt_regs *regs) +{ + struct irqaction *action; + unsigned char mask = ioc_icontrol->istat0; + unsigned char mask2 = 0; + int irq; + + mask &= ioc_icontrol->imask0; + if(mask & ISTAT0_LIO2) { + mask2 = ioc_icontrol->vmeistat; + mask2 &= ioc_icontrol->cmeimask0; + irq = lc2msk_to_irqnr[mask2]; + action = local_irq_action[irq]; + } else { + irq = lc0msk_to_irqnr[mask]; + action = local_irq_action[irq]; + } +#if 0 + printk("local0_dispatch: got irq %d mask %2x mask2 %2x\n", + irq, mask, mask2); + prom_getchar(); +#endif + kstat.interrupts[irq + 16]++; + action->handler(irq, action->dev_id, regs); +} + +void indy_local1_irqdispatch(struct pt_regs *regs) +{ + struct irqaction *action; + unsigned char mask = ioc_icontrol->istat1; + unsigned char mask2 = 0; + int irq; + + mask &= ioc_icontrol->imask1; + if(mask & ISTAT1_LIO3) { + printk("WHee: Got an LIO3 irq, winging it...\n"); + mask2 = ioc_icontrol->vmeistat; + mask2 &= ioc_icontrol->cmeimask1; + irq = lc3msk_to_irqnr[ioc_icontrol->vmeistat]; + action = local_irq_action[irq]; + } else { + irq = lc1msk_to_irqnr[mask]; + action = local_irq_action[irq]; + } +#if 0 + printk("local1_dispatch: got irq %d mask %2x mask2 %2x\n", + irq, mask, mask2); + prom_getchar(); +#endif + kstat.interrupts[irq + 24]++; + action->handler(irq, action->dev_id, regs); +} + +void indy_buserror_irq(struct pt_regs *regs) +{ + kstat.interrupts[6]++; + printk("Got a bus error IRQ, shouldn't happen yet\n"); + show_regs(regs); + printk("Spinning...\n"); + while(1) + ; +} + +/* Misc. crap just to keep the kernel linking... */ +unsigned long probe_irq_on (void) +{ + return 0; +} + +int probe_irq_off (unsigned long irqs) +{ + return 0; +} + +void sgint_init(void) +{ + int i; +#ifdef CONFIG_REMOTE_DEBUG + char *ctype; +#endif + + sgi_i2regs = (struct sgi_int2_regs *) (KSEG1 + SGI_INT2_BASE); + sgi_i3regs = (struct sgi_int3_regs *) (KSEG1 + SGI_INT3_BASE); + + /* Init local mask --> irq tables. */ + for(i = 0; i < 256; i++) { + if(i & 0x80) { + lc0msk_to_irqnr[i] = 7; + lc1msk_to_irqnr[i] = 15; + lc2msk_to_irqnr[i] = 23; + lc3msk_to_irqnr[i] = 31; + } else if(i & 0x40) { + lc0msk_to_irqnr[i] = 6; + lc1msk_to_irqnr[i] = 14; + lc2msk_to_irqnr[i] = 22; + lc3msk_to_irqnr[i] = 30; + } else if(i & 0x20) { + lc0msk_to_irqnr[i] = 5; + lc1msk_to_irqnr[i] = 13; + lc2msk_to_irqnr[i] = 21; + lc3msk_to_irqnr[i] = 29; + } else if(i & 0x10) { + lc0msk_to_irqnr[i] = 4; + lc1msk_to_irqnr[i] = 12; + lc2msk_to_irqnr[i] = 20; + lc3msk_to_irqnr[i] = 28; + } else if(i & 0x08) { + lc0msk_to_irqnr[i] = 3; + lc1msk_to_irqnr[i] = 11; + lc2msk_to_irqnr[i] = 19; + lc3msk_to_irqnr[i] = 27; + } else if(i & 0x04) { + lc0msk_to_irqnr[i] = 2; + lc1msk_to_irqnr[i] = 10; + lc2msk_to_irqnr[i] = 18; + lc3msk_to_irqnr[i] = 26; + } else if(i & 0x02) { + lc0msk_to_irqnr[i] = 1; + lc1msk_to_irqnr[i] = 9; + lc2msk_to_irqnr[i] = 17; + lc3msk_to_irqnr[i] = 25; + } else if(i & 0x01) { + lc0msk_to_irqnr[i] = 0; + lc1msk_to_irqnr[i] = 8; + lc2msk_to_irqnr[i] = 16; + lc3msk_to_irqnr[i] = 24; + } else { + lc0msk_to_irqnr[i] = 0; + lc1msk_to_irqnr[i] = 0; + lc2msk_to_irqnr[i] = 0; + lc3msk_to_irqnr[i] = 0; + } + } + + ioc_icontrol = &sgi_i3regs->ints; + ioc_timers = &sgi_i3regs->timers; + ioc_tclear = &sgi_i3regs->tclear; + + /* Mask out all interrupts. */ + ioc_icontrol->imask0 = 0; + ioc_icontrol->imask1 = 0; + ioc_icontrol->cmeimask0 = 0; + ioc_icontrol->cmeimask1 = 0; + + /* Now safe to set the exception vector. */ + set_except_vector(0, indyIRQ); + +#ifdef CONFIG_REMOTE_DEBUG + ctype = prom_getcmdline(); + for(i = 0; i < strlen(ctype); i++) { + if(ctype[i]=='k' && ctype[i+1]=='g' && + ctype[i+2]=='d' && ctype[i+3]=='b' && + ctype[i+4]=='=' && ctype[i+5]=='t' && + ctype[i+6]=='t' && ctype[i+7]=='y' && + ctype[i+8]=='d' && + (ctype[i+9] == '1' || ctype[i+9] == '2')) { + printk("KGDB: Using serial line /dev/ttyd%d for " + "session\n", (ctype[i+9] - '0')); + if(ctype[i+9]=='1') + rs_kgdb_hook(1); + else if(ctype[i+9]=='2') + rs_kgdb_hook(0); + else { + printk("KGDB: whoops bogon tty line " + "requested, disabling session\n"); + } + + } + } +#endif +} diff --git a/arch/mips/sgi/kernel/indy_mc.c b/arch/mips/sgi/kernel/indy_mc.c new file mode 100644 index 000000000..b2f35018c --- /dev/null +++ b/arch/mips/sgi/kernel/indy_mc.c @@ -0,0 +1,153 @@ +/* $Id: indy_mc.c,v 1.5 1996/06/29 07:06:51 dm Exp $ + * indy_mc.c: Routines for manipulating the INDY memory controller. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + */ + +#include <asm/segment.h> +#include <asm/ptrace.h> +#include <asm/processor.h> +#include <asm/sgimc.h> +#include <asm/sgihpc.h> +#include <asm/sgialib.h> + +/* #define DEBUG_SGIMC */ + +struct sgimc_misc_ctrl *mcmisc_regs; +unsigned long *rpsscounter; +struct sgimc_dma_ctrl *dmactrlregs; + +static inline char *mconfig_string(unsigned long val) +{ + switch(val & SGIMC_MCONFIG_RMASK) { + case SGIMC_MCONFIG_FOURMB: + return "4MB"; + + case SGIMC_MCONFIG_EIGHTMB: + return "8MB"; + + case SGIMC_MCONFIG_SXTEENMB: + return "16MB"; + + case SGIMC_MCONFIG_TTWOMB: + return "32MB"; + + case SGIMC_MCONFIG_SFOURMB: + return "64MB"; + + case SGIMC_MCONFIG_OTEIGHTMB: + return "128MB"; + + default: + return "wheee, unknown"; + }; +} + +void sgimc_init(void) +{ + unsigned long tmpreg; + + mcmisc_regs = (struct sgimc_misc_ctrl *)(KSEG1+0x1fa00000); + rpsscounter = (unsigned long *) (KSEG1 + 0x1fa01004); + dmactrlregs = (struct sgimc_dma_ctrl *) (KSEG1+0x1fa02000); + + printk("MC: SGI memory controller Revision %d\n", + (int) mcmisc_regs->systemid & SGIMC_SYSID_MASKREV); + +#if 0 /* XXX Until I figure out what this bit really indicates XXX */ + /* XXX Is this systemid bit reliable? */ + if(mcmisc_regs->systemid & SGIMC_SYSID_EPRESENT) { + EISA_bus = 1; + printk("with EISA\n"); + } else { + EISA_bus = 0; + printk("no EISA\n"); + } +#endif + +#ifdef DEBUG_SGIMC + prom_printf("sgimc_init: memconfig0<%s> mconfig1<%s>\n", + mconfig_string(mcmisc_regs->mconfig0), + mconfig_string(mcmisc_regs->mconfig1)); + + prom_printf("mcdump: cpuctrl0<%08lx> cpuctrl1<%08lx>\n", + mcmisc_regs->cpuctrl0, mcmisc_regs->cpuctrl1); + prom_printf("mcdump: divider<%08lx>, gioparm<%04x>\n", + mcmisc_regs->divider, mcmisc_regs->gioparm); +#endif + + /* Place the MC into a known state. This must be done before + * interrupts are first enabled etc. + */ + + /* Step 1: The CPU/GIO error status registers will not latch + * up a new error status until the register has been + * cleared by the cpu. These status registers are + * cleared by writing any value to them. + */ + mcmisc_regs->cstat = mcmisc_regs->gstat = 0; + + /* Step 2: Enable all parity checking in cpu control register + * zero. + */ + tmpreg = mcmisc_regs->cpuctrl0; + tmpreg |= (SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM | + SGIMC_CCTRL0_R4KNOCHKPARR); + mcmisc_regs->cpuctrl0 = tmpreg; + + /* Step 3: Setup the MC write buffer depth, this is controlled + * in cpu control register 1 in the lower 4 bits. + */ + tmpreg = mcmisc_regs->cpuctrl1; + tmpreg &= ~0xf; + tmpreg |= 0xd; + mcmisc_regs->cpuctrl1 = tmpreg; + + /* Step 4: Initialize the RPSS divider register to run as fast + * as it can correctly operate. The register is laid + * out as follows: + * + * ---------------------------------------- + * | RESERVED | INCREMENT | DIVIDER | + * ---------------------------------------- + * 31 16 15 8 7 0 + * + * DIVIDER determines how often a 'tick' happens, + * INCREMENT determines by how the RPSS increment + * registers value increases at each 'tick'. Thus, + * for IP22 we get INCREMENT=1, DIVIDER=1 == 0x101 + */ + mcmisc_regs->divider = 0x101; + + /* Step 5: Initialize GIO64 arbitrator configuration register. + * + * NOTE: If you dork with startup code the HPC init code in + * sgihpc_init() must run before us because of how we + * need to know Guiness vs. FullHouse and the board + * revision on this machine. You have been warned. + */ + + /* First the basic invariants across all gio64 implementations. */ + tmpreg = SGIMC_GIOPARM_HPC64; /* All 1st HPC's interface at 64bits. */ + tmpreg |= SGIMC_GIOPARM_ONEBUS; /* Only one physical GIO bus exists. */ + + if(sgi_guiness) { + /* Guiness specific settings. */ + tmpreg |= SGIMC_GIOPARM_EISA64; /* MC talks to EISA at 64bits */ + tmpreg |= SGIMC_GIOPARM_MASTEREISA; /* EISA bus can act as master */ + } else { + /* Fullhouse specific settings. */ + if(sgi_boardid < 2) { + tmpreg |= SGIMC_GIOPARM_HPC264; /* 2nd HPC at 64bits */ + tmpreg |= SGIMC_GIOPARM_PLINEEXP0; /* exp0 pipelines */ + tmpreg |= SGIMC_GIOPARM_MASTEREXP1;/* exp1 masters */ + tmpreg |= SGIMC_GIOPARM_RTIMEEXP0; /* exp0 is realtime */ + } else { + tmpreg |= SGIMC_GIOPARM_HPC264; /* 2nd HPC 64bits */ + tmpreg |= SGIMC_GIOPARM_PLINEEXP0; /* exp[01] pipelined */ + tmpreg |= SGIMC_GIOPARM_PLINEEXP1; + tmpreg |= SGIMC_GIOPARM_MASTEREISA;/* EISA masters */ + } + } + mcmisc_regs->gioparm = tmpreg; /* poof */ +} diff --git a/arch/mips/sgi/kernel/indy_timer.c b/arch/mips/sgi/kernel/indy_timer.c new file mode 100644 index 000000000..9aa88cb18 --- /dev/null +++ b/arch/mips/sgi/kernel/indy_timer.c @@ -0,0 +1,298 @@ +/* $Id: indy_timer.c,v 1.10 1996/08/07 02:54:11 dm Exp $ + * indy_timer.c: Setting up the clock on the INDY 8254 controller. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/kernel_stat.h> + +#include <asm/bootinfo.h> +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/ptrace.h> +#include <asm/system.h> +#include <asm/sgi.h> +#include <asm/sgialib.h> +#include <asm/sgihpc.h> +#include <asm/sgint23.h> + +/* The layout of registers for the INDY Dallas 1286 clock chipset. */ +struct indy_clock { + volatile unsigned int hsec; + volatile unsigned int sec; + volatile unsigned int min; + volatile unsigned int malarm; + volatile unsigned int hr; + volatile unsigned int halarm; + volatile unsigned int day; + volatile unsigned int dalarm; + volatile unsigned int date; + volatile unsigned int month; + volatile unsigned int year; + volatile unsigned int cmd; + volatile unsigned int whsec; + volatile unsigned int wsec; + volatile unsigned int _unused0[50]; +}; + +#define INDY_CLOCK_REGS ((struct indy_clock *)(KSEG1ADDR(0x1fbe0000))) + +/* Because of a bug in the i8254 timer we need to use the onchip r4k + * counter as our system wide timer interrupt running at 100HZ. + */ +static unsigned long r4k_offset; /* Amount to increment compare reg each time */ +static unsigned long r4k_cur; /* What counter should be at next timer irq */ + +static inline void ack_r4ktimer(unsigned long newval) +{ + write_32bit_cp0_register(CP0_COMPARE, newval); +} + +static int set_rtc_mmss(unsigned long nowtime) +{ + struct indy_clock *clock = INDY_CLOCK_REGS; + int retval = 0; + int real_seconds, real_minutes, clock_minutes; + +#define FROB_FROM_CLOCK(x) (((x) & 0xf) | ((((x) & 0xf0) >> 4) * 10)); +#define FROB_TO_CLOCK(x) ((((((x) & 0xff) / 10)<<4) | (((x) & 0xff) % 10)) & 0xff) + + clock->cmd &= ~(0x80); + clock_minutes = clock->min; + clock->cmd |= (0x80); + + clock_minutes = FROB_FROM_CLOCK(clock_minutes); + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + + if(((abs(real_minutes - clock_minutes) + 15)/30) & 1) + real_minutes += 30; /* correct for half hour time zone */ + + real_minutes %= 60; + if(abs(real_minutes - clock_minutes) < 30) { + /* Force clock oscillator to be on. */ + clock->month &= ~(0x80); + + /* Write real_seconds and real_minutes into the Dallas. */ + clock->cmd &= ~(0x80); + clock->sec = real_seconds; + clock->min = real_minutes; + clock->cmd |= (0x80); + } else + return -1; + +#undef FROB_FROM_CLOCK +#undef FROB_TO_CLOCK + + return retval; +} + +static long last_rtc_update = 0; + +void indy_timer_interrupt(struct pt_regs *regs) +{ + /* Ack timer and compute new compare. */ + r4k_cur = (read_32bit_cp0_register(CP0_COUNT) + r4k_offset); + ack_r4ktimer(r4k_cur); + kstat.interrupts[7]++; + do_timer(regs); + + /* We update the Dallas time of day approx. every 11 minutes, + * because of how the numbers work out we need to make + * absolutely sure we do this update within 500ms before the + * next second starts, thus the following code. + */ + if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec > 500000 - (tick >> 1) && + xtime.tv_usec < 500000 + (tick >> 1)) + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ +} + +static inline unsigned long dosample(volatile unsigned char *tcwp, + volatile unsigned char *tc2p) +{ + unsigned long ct0, ct1; + unsigned char msb, lsb; + + /* Start the counter. */ + *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MRGEN); + *tc2p = (SGINT_TCSAMP_COUNTER & 0xff); + *tc2p = (SGINT_TCSAMP_COUNTER >> 8); + + /* Get initial counter invariant */ + ct0 = read_32bit_cp0_register(CP0_COUNT); + + /* Latch and spin until top byte of counter2 is zero */ + *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT); + ct1 = read_32bit_cp0_register(CP0_COUNT); + lsb = *tc2p; + msb = *tc2p; + while(msb) { + *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT); + ct1 = read_32bit_cp0_register(CP0_COUNT); + lsb = *tc2p; + msb = *tc2p; + } + + /* Stop the counter. */ + *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MSWST); + + /* Return the difference, this is how far the r4k counter increments + * for every one HZ. + */ + return ct1 - ct0; +} + +/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. + * + * [For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10.] + * + * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) + */ +static inline unsigned long mktime(unsigned int year, unsigned int mon, + unsigned int day, unsigned int hour, + unsigned int min, unsigned int sec) +{ + if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + return ((( + (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} + +unsigned long get_indy_time(void) +{ + struct indy_clock *clock = INDY_CLOCK_REGS; + unsigned int year, mon, day, hour, min, sec; + + /* Freeze it. */ + clock->cmd &= ~(0x80); + + /* Read regs. */ + sec = clock->sec; + min = clock->min; + hour = (clock->hr & 0x3f); + day = (clock->date & 0x3f); + mon = (clock->month & 0x1f); + year = clock->year; + + /* Unfreeze clock. */ + clock->cmd |= 0x80; + + /* Frob the bits. */ +#define FROB1(x) (((x) & 0xf) + ((((x) & 0xf0) >> 4) * 10)); +#define FROB2(x) (((x) & 0xf) + (((((x) & 0xf0) >> 4) & 0x3) * 10)); + + /* XXX Should really check that secs register is the same + * XXX as when we first read it and if not go back and + * XXX read the regs above again. + */ + sec = FROB1(sec); min = FROB1(min); day = FROB1(day); + mon = FROB1(mon); year = FROB1(year); + hour = FROB2(hour); + +#undef FROB1 +#undef FROB2 + + /* Wheee... */ + if(year < 45) + year += 30; + if ((year += 1940) < 1970) + year += 100; + + return mktime(year, mon, day, hour, min, sec); +} + +#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) + +void indy_timer_init(void) +{ + struct sgi_ioc_timers *p; + volatile unsigned char *tcwp, *tc2p; + + /* Figure out the r4k offset, the algorithm is very simple + * and works in _all_ cases as long as the 8254 counter + * register itself works ok (as an interrupt driving timer + * it does not because of bug, this is why we are using + * the onchip r4k counter/compare register to serve this + * purpose, but for r4k_offset calculation it will work + * ok for us). There are other very complicated ways + * of performing this calculation but this one works just + * fine so I am not going to futz around. ;-) + */ + p = ioc_timers; + tcwp = &p->tcword; + tc2p = &p->tcnt2; + + printk("calculating r4koff... "); + r4k_offset = dosample(tcwp, tc2p); /* First sample. */ + dosample(tcwp, tc2p); /* Eat one... */ + r4k_offset += dosample(tcwp, tc2p); /* Second sample. */ + r4k_offset = (r4k_offset >> 1); /* Get average. */ + r4k_offset = HZ * r4k_offset; /* Multiply by HZ */ + + printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset); + + r4k_cur = (read_32bit_cp0_register(CP0_COUNT) + r4k_offset); + write_32bit_cp0_register(CP0_COMPARE, r4k_cur); + set_cp0_status(ST0_IM, ALLINTS); + sti(); + + /* Read time from the dallas chipset. */ + xtime.tv_sec = get_indy_time(); + xtime.tv_usec = 0; +} + +void indy_8254timer_irq(void) +{ + kstat.interrupts[4]++; + printk("indy_8254timer_irq: Whoops, should not have gotten this IRQ\n"); + prom_getchar(); + prom_imode(); +} + +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + + save_flags(flags); cli(); + *tv = xtime; + restore_flags(flags); +} + +void do_settimeofday(struct timeval *tv) +{ + cli(); + xtime = *tv; + time_state = TIME_BAD; + time_maxerror = MAXPHASE; + time_esterror = MAXPHASE; + sti(); +} + diff --git a/arch/mips/sgi/kernel/reset.c b/arch/mips/sgi/kernel/reset.c new file mode 100644 index 000000000..4e7455952 --- /dev/null +++ b/arch/mips/sgi/kernel/reset.c @@ -0,0 +1,14 @@ +/* + * linux/arch/mips/sgi/kernel/process.c + * + * Reset a SGI. + */ +#include <asm/io.h> +#include <asm/system.h> + +void +sgi_hard_reset_now(void) +{ + for(;;) + prom_imode(); +} diff --git a/arch/mips/sgi/kernel/setup.c b/arch/mips/sgi/kernel/setup.c new file mode 100644 index 000000000..61b67df75 --- /dev/null +++ b/arch/mips/sgi/kernel/setup.c @@ -0,0 +1,83 @@ +/* $Id: setup.c,v 1.11 1996/06/29 07:06:51 dm Exp $ + * setup.c: SGI specific setup, including init of the feature struct. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + */ +#ifndef __GOGOGO__ +#error "... about to fuckup your Indy?" +#endif +#include <linux/kernel.h> +#include <linux/sched.h> + +#include <asm/vector.h> +#include <asm/sgialib.h> +#include <asm/sgi.h> +#include <asm/sgimc.h> +#include <asm/sgihpc.h> +#include <asm/sgint23.h> + +extern int serial_console; /* in console.c, of course */ + +struct feature sgi_feature = { +}; + +static void sgi_irq_setup(void) +{ + sgint_init(); +} + +#if 0 +extern void register_console(void (*proc)(const char *)); + +static void sgi_print(const char *p) +{ + char c; + + while((c = *p++) != 0) { + if(c == '\n') + prom_putchar('\r'); + prom_putchar(c); + } +} +#endif + +void sgi_setup(void) +{ + char *ctype; + + irq_setup = sgi_irq_setup; + feature = &sgi_feature; + hard_reset_now = acn_hard_reset_now; + + /* register_console(sgi_print); */ + + sgi_sysinit(); + + /* Init the INDY HPC I/O controller. Need to call this before + * fucking with the memory controller because it needs to know the + * boardID and whether this is a Guiness or a FullHouse machine. + */ + sgihpc_init(); + + /* Init INDY memory controller. */ + sgimc_init(); + + /* ARCS console environment variable is set to "g?" for + * graphics console, it is set to "d" for the first serial + * line and "d2" for the second serial line. + */ + ctype = prom_getenv("console"); + serial_console = 0; + if(*ctype == 'd') { + if(*(ctype+1)=='2') + serial_console = 1; + else + serial_console = 2; + if(!serial_console) { + prom_printf("Weird console env setting %s\n", ctype); + prom_printf("Press a key to reboot.\n"); + prom_getchar(); + prom_imode(); + } + } +} diff --git a/arch/mips/sgi/kernel/system.c b/arch/mips/sgi/kernel/system.c new file mode 100644 index 000000000..051ad09c5 --- /dev/null +++ b/arch/mips/sgi/kernel/system.c @@ -0,0 +1,168 @@ +/* $Id: system.c,v 1.2 1996/06/10 16:38:32 dm Exp $ + * system.c: Probe the system type using ARCS prom interface library. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + */ +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> + +#include <asm/sgi.h> +#include <asm/sgialib.h> +#include <asm/bootinfo.h> + +#ifndef __GOGOGO__ +#error "... You're fearless, aren't you?" +#endif + +enum sgi_mach sgimach; + +struct smatch { + char *name; + int type; +}; + +static struct smatch sgi_mtable[] = { + { "SGI-IP4", ip4 }, + { "SGI-IP5", ip5 }, + { "SGI-IP6", ip6 }, + { "SGI-IP7", ip7 }, + { "SGI-IP9", ip9 }, + { "SGI-IP12", ip12 }, + { "SGI-IP15", ip15 }, + { "SGI-IP17", ip17 }, + { "SGI-IP19", ip19 }, + { "SGI-IP20", ip20 }, + { "SGI-IP21", ip21 }, + { "SGI-IP22", ip22 }, + { "SGI-IP25", ip25 }, + { "SGI-IP26", ip26 }, + { "SGI-IP28", ip28 }, + { "SGI-IP30", ip30 }, + { "SGI-IP32", ip32 } +}; + +#define NUM_MACHS 17 /* for now */ + +static struct smatch sgi_cputable[] = { + { "MIPS-R2000", CPU_R2000 }, + { "MIPS-R3000", CPU_R3000 }, + { "MIPS-R3000A", CPU_R3000A }, + { "MIPS-R4000", CPU_R4000SC }, + { "MIPS-R4400", CPU_R4400SC }, + { "MIPS-R4600", CPU_R4600 }, + { "MIPS-R8000", CPU_R8000 }, + { "MIPS-R5000", CPU_R5000 }, + { "MIPS-R5000A", CPU_R5000A } +}; + +#define NUM_CPUS 9 /* for now */ + +static enum sgi_mach string_to_mach(char *s) +{ + int i; + + for(i = 0; i < NUM_MACHS; i++) { + if(!strcmp(s, sgi_mtable[i].name)) + return (enum sgi_mach) sgi_mtable[i].type; + } + prom_printf("\nYeee, could not determine SGI architecture type <%s>\n", s); + prom_printf("press a key to reboot\n"); + prom_getchar(); + romvec->imode(); + return (enum sgi_mach) 0; +} + +static int string_to_cpu(char *s) +{ + int i; + + for(i = 0; i < NUM_CPUS; i++) { + if(!strcmp(s, sgi_cputable[i].name)) + return sgi_mtable[i].type; + } + prom_printf("\nYeee, could not determine MIPS cpu type <%s>\n", s); + prom_printf("press a key to reboot\n"); + prom_getchar(); + romvec->imode(); + return 0; +} + +void sgi_sysinit(void) +{ + pcomponent *p, *toplev, *cpup = 0; + int cputype = -1; + + + /* The root component tells us what machine architecture we + * have here. + */ + p = prom_getchild(PROM_NULL_COMPONENT); + printk("ARCH: %s\n", p->iname); + sgimach = string_to_mach(p->iname); + + /* Now scan for cpu(s). */ + toplev = p = prom_getchild(p); + while(p) { + int ncpus = 0; + + if(p->type == Cpu) { + if(++ncpus > 1) { + prom_printf("\nYeee, SGI MP not ready yet\n"); + prom_printf("press a key to reboot\n"); + prom_getchar(); + romvec->imode(); + } + printk("CPU: %s ", p->iname); + cpup = p; + cputype = string_to_cpu(cpup->iname); + } + p = prom_getsibling(p); + } + if(cputype == -1) { + prom_printf("\nYeee, could not find cpu ARCS component\n"); + prom_printf("press a key to reboot\n"); + prom_getchar(); + romvec->imode(); + } + p = prom_getchild(cpup); + while(p) { + switch(p->class) { + case processor: + switch(p->type) { + case Fpu: + printk("FPU<%s> ", p->iname); + break; + + default: + break; + }; + break; + + case cache: + switch(p->type) { + case picache: + printk("ICACHE "); + break; + + case pdcache: + printk("DCACHE "); + break; + + case sccache: + printk("SCACHE "); + break; + + default: + break; + + }; + break; + + default: + break; + }; + p = prom_getsibling(p); + } + printk("\n"); +} diff --git a/arch/mips/sgi/kernel/time.c b/arch/mips/sgi/kernel/time.c new file mode 100644 index 000000000..1f5137c27 --- /dev/null +++ b/arch/mips/sgi/kernel/time.c @@ -0,0 +1,14 @@ +/* $Id: time.c,v 1.1 1996/06/08 12:07:08 dm Exp $ + * time.c: Generic SGI time_init() code, this will dispatch to the + * appropriate per-architecture time/counter init code. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + */ + +extern void indy_timer_init(void); + +void time_init(void) +{ + /* XXX assume INDY for now XXX */ + indy_timer_init(); +} |