diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/mips/sgi-ip22 |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/mips/sgi-ip22')
-rw-r--r-- | arch/mips/sgi-ip22/Makefile | 11 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-berr.c | 114 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-eisa.c | 308 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-hpc.c | 63 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-int.c | 410 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-irq.S | 118 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-mc.c | 208 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-nvram.c | 119 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-reset.c | 247 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-setup.c | 144 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-time.c | 214 |
11 files changed, 1956 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip22/Makefile b/arch/mips/sgi-ip22/Makefile new file mode 100644 index 00000000000..eb0820fe50b --- /dev/null +++ b/arch/mips/sgi-ip22/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the SGI specific kernel interface routines +# under Linux. +# + +obj-y += ip22-mc.o ip22-hpc.o ip22-int.o ip22-irq.o ip22-berr.o \ + ip22-time.o ip22-nvram.o ip22-reset.o ip22-setup.o + +obj-$(CONFIG_EISA) += ip22-eisa.o + +EXTRA_AFLAGS := $(CFLAGS) diff --git a/arch/mips/sgi-ip22/ip22-berr.c b/arch/mips/sgi-ip22/ip22-berr.c new file mode 100644 index 00000000000..a28dc780007 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-berr.c @@ -0,0 +1,114 @@ +/* + * ip22-berr.c: Bus error handling. + * + * Copyright (C) 2002, 2003 Ladislav Michl (ladis@linux-mips.org) + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> + +#include <asm/addrspace.h> +#include <asm/system.h> +#include <asm/traps.h> +#include <asm/branch.h> +#include <asm/sgi/mc.h> +#include <asm/sgi/hpc3.h> +#include <asm/sgi/ioc.h> +#include <asm/sgi/ip22.h> + + +static unsigned int cpu_err_stat; /* Status reg for CPU */ +static unsigned int gio_err_stat; /* Status reg for GIO */ +static unsigned int cpu_err_addr; /* Error address reg for CPU */ +static unsigned int gio_err_addr; /* Error address reg for GIO */ +static unsigned int extio_stat; +static unsigned int hpc3_berr_stat; /* Bus error interrupt status */ + +static void save_and_clear_buserr(void) +{ + /* save status registers */ + cpu_err_addr = sgimc->cerr; + cpu_err_stat = sgimc->cstat; + gio_err_addr = sgimc->gerr; + gio_err_stat = sgimc->gstat; + extio_stat = ip22_is_fullhouse() ? sgioc->extio : (sgint->errstat << 4); + hpc3_berr_stat = hpc3c0->bestat; + + sgimc->cstat = sgimc->gstat = 0; +} + +#define GIO_ERRMASK 0xff00 +#define CPU_ERRMASK 0x3f00 + +static void print_buserr(void) +{ + if (extio_stat & EXTIO_MC_BUSERR) + printk(KERN_ERR "MC Bus Error\n"); + if (extio_stat & EXTIO_HPC3_BUSERR) + printk(KERN_ERR "HPC3 Bus Error 0x%x:<id=0x%x,%s,lane=0x%x>\n", + hpc3_berr_stat, + (hpc3_berr_stat & HPC3_BESTAT_PIDMASK) >> + HPC3_BESTAT_PIDSHIFT, + (hpc3_berr_stat & HPC3_BESTAT_CTYPE) ? "PIO" : "DMA", + hpc3_berr_stat & HPC3_BESTAT_BLMASK); + if (extio_stat & EXTIO_EISA_BUSERR) + printk(KERN_ERR "EISA Bus Error\n"); + if (cpu_err_stat & CPU_ERRMASK) + printk(KERN_ERR "CPU error 0x%x<%s%s%s%s%s%s> @ 0x%08x\n", + cpu_err_stat, + cpu_err_stat & SGIMC_CSTAT_RD ? "RD " : "", + cpu_err_stat & SGIMC_CSTAT_PAR ? "PAR " : "", + cpu_err_stat & SGIMC_CSTAT_ADDR ? "ADDR " : "", + cpu_err_stat & SGIMC_CSTAT_SYSAD_PAR ? "SYSAD " : "", + cpu_err_stat & SGIMC_CSTAT_SYSCMD_PAR ? "SYSCMD " : "", + cpu_err_stat & SGIMC_CSTAT_BAD_DATA ? "BAD_DATA " : "", + cpu_err_addr); + if (gio_err_stat & GIO_ERRMASK) + printk(KERN_ERR "GIO error 0x%x:<%s%s%s%s%s%s%s%s> @ 0x%08x\n", + gio_err_stat, + gio_err_stat & SGIMC_GSTAT_RD ? "RD " : "", + gio_err_stat & SGIMC_GSTAT_WR ? "WR " : "", + gio_err_stat & SGIMC_GSTAT_TIME ? "TIME " : "", + gio_err_stat & SGIMC_GSTAT_PROM ? "PROM " : "", + gio_err_stat & SGIMC_GSTAT_ADDR ? "ADDR " : "", + gio_err_stat & SGIMC_GSTAT_BC ? "BC " : "", + gio_err_stat & SGIMC_GSTAT_PIO_RD ? "PIO_RD " : "", + gio_err_stat & SGIMC_GSTAT_PIO_WR ? "PIO_WR " : "", + gio_err_addr); +} + +/* + * MC sends an interrupt whenever bus or parity errors occur. In addition, + * if the error happened during a CPU read, it also asserts the bus error + * pin on the R4K. Code in bus error handler save the MC bus error registers + * and then clear the interrupt when this happens. + */ + +void ip22_be_interrupt(int irq, struct pt_regs *regs) +{ + const int field = 2 * sizeof(unsigned long); + + save_and_clear_buserr(); + print_buserr(); + printk(KERN_ALERT "%s bus error, epc == %0*lx, ra == %0*lx\n", + (regs->cp0_cause & 4) ? "Data" : "Instruction", + field, regs->cp0_epc, field, regs->regs[31]); + /* Assume it would be too dangerous to continue ... */ + die_if_kernel("Oops", regs); + force_sig(SIGBUS, current); +} + +static int ip22_be_handler(struct pt_regs *regs, int is_fixup) +{ + save_and_clear_buserr(); + if (is_fixup) + return MIPS_BE_FIXUP; + print_buserr(); + return MIPS_BE_FATAL; +} + +void __init ip22_be_init(void) +{ + board_be_handler = ip22_be_handler; +} diff --git a/arch/mips/sgi-ip22/ip22-eisa.c b/arch/mips/sgi-ip22/ip22-eisa.c new file mode 100644 index 00000000000..0ab4abf65d5 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-eisa.c @@ -0,0 +1,308 @@ +/* + * Basic EISA bus support for the SGI Indigo-2. + * + * (C) 2002 Pascal Dameme <netinet@freesurf.fr> + * and Marc Zyngier <mzyngier@freesurf.fr> + * + * This code is released under both the GPL version 2 and BSD + * licenses. Either license may be used. + * + * This code offers a very basic support for this EISA bus present in + * the SGI Indigo-2. It currently only supports PIO (forget about DMA + * for the time being). This is enough for a low-end ethernet card, + * but forget about your favorite SCSI card... + * + * TODO : + * - Fix bugs... + * - Add ISA support + * - Add DMA (yeah, right...). + * - Fix more bugs. + */ + +#include <linux/config.h> +#include <linux/eisa.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <asm/irq.h> +#include <asm/mipsregs.h> +#include <asm/addrspace.h> +#include <asm/processor.h> +#include <asm/sgi/ioc.h> +#include <asm/sgi/mc.h> +#include <asm/sgi/ip22.h> + +#define EISA_MAX_SLOTS 4 +#define EISA_MAX_IRQ 16 + +#define EISA_TO_PHYS(x) (0x00080000 | (x)) +#define EISA_TO_KSEG1(x) ((void *) KSEG1ADDR(EISA_TO_PHYS((x)))) + +#define EIU_MODE_REG 0x0009ffc0 +#define EIU_STAT_REG 0x0009ffc4 +#define EIU_PREMPT_REG 0x0009ffc8 +#define EIU_QUIET_REG 0x0009ffcc +#define EIU_INTRPT_ACK 0x00090004 + +#define EISA_DMA1_STATUS 8 +#define EISA_INT1_CTRL 0x20 +#define EISA_INT1_MASK 0x21 +#define EISA_INT2_CTRL 0xA0 +#define EISA_INT2_MASK 0xA1 +#define EISA_DMA2_STATUS 0xD0 +#define EISA_DMA2_WRITE_SINGLE 0xD4 +#define EISA_EXT_NMI_RESET_CTRL 0x461 +#define EISA_INT1_EDGE_LEVEL 0x4D0 +#define EISA_INT2_EDGE_LEVEL 0x4D1 +#define EISA_VENDOR_ID_OFFSET 0xC80 + +#define EIU_WRITE_32(x,y) { *((u32 *) KSEG1ADDR(x)) = (u32) (y); mb(); } +#define EIU_READ_8(x) *((u8 *) KSEG1ADDR(x)) +#define EISA_WRITE_8(x,y) { *((u8 *) EISA_TO_KSEG1(x)) = (u8) (y); mb(); } +#define EISA_READ_8(x) *((u8 *) EISA_TO_KSEG1(x)) + +static char *decode_eisa_sig(u8 * sig) +{ + static char sig_str[8]; + u16 rev; + + if (sig[0] & 0x80) + return NULL; + + sig_str[0] = ((sig[0] >> 2) & 0x1f) + ('A' - 1); + sig_str[1] = (((sig[0] & 3) << 3) | (sig[1] >> 5)) + ('A' - 1); + sig_str[2] = (sig[1] & 0x1f) + ('A' - 1); + rev = (sig[2] << 8) | sig[3]; + sprintf(sig_str + 3, "%04X", rev); + + return sig_str; +} + +static void ip22_eisa_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + u8 eisa_irq; + u8 dma1, dma2; + + eisa_irq = EIU_READ_8(EIU_INTRPT_ACK); + dma1 = EISA_READ_8(EISA_DMA1_STATUS); + dma2 = EISA_READ_8(EISA_DMA2_STATUS); + + if (eisa_irq >= EISA_MAX_IRQ) { + /* Oops, Bad Stuff Happened... */ + printk(KERN_ERR "eisa_irq %d out of bound\n", eisa_irq); + + EISA_WRITE_8(EISA_INT2_CTRL, 0x20); + EISA_WRITE_8(EISA_INT1_CTRL, 0x20); + } else + do_IRQ(eisa_irq, regs); +} + +static void enable_eisa1_irq(unsigned int irq) +{ + unsigned long flags; + u8 mask; + + local_irq_save(flags); + + mask = EISA_READ_8(EISA_INT1_MASK); + mask &= ~((u8) (1 << irq)); + EISA_WRITE_8(EISA_INT1_MASK, mask); + + local_irq_restore(flags); +} + +static unsigned int startup_eisa1_irq(unsigned int irq) +{ + u8 edge; + + /* Only use edge interrupts for EISA */ + + edge = EISA_READ_8(EISA_INT1_EDGE_LEVEL); + edge &= ~((u8) (1 << irq)); + EISA_WRITE_8(EISA_INT1_EDGE_LEVEL, edge); + + enable_eisa1_irq(irq); + return 0; +} + +static void disable_eisa1_irq(unsigned int irq) +{ + u8 mask; + + mask = EISA_READ_8(EISA_INT1_MASK); + mask |= ((u8) (1 << irq)); + EISA_WRITE_8(EISA_INT1_MASK, mask); +} + +#define shutdown_eisa1_irq disable_eisa1_irq + +static void mask_and_ack_eisa1_irq(unsigned int irq) +{ + disable_eisa1_irq(irq); + + EISA_WRITE_8(EISA_INT1_CTRL, 0x20); +} + +static void end_eisa1_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) + enable_eisa1_irq(irq); +} + +static struct hw_interrupt_type ip22_eisa1_irq_type = { + .typename = "IP22 EISA", + .startup = startup_eisa1_irq, + .shutdown = shutdown_eisa1_irq, + .enable = enable_eisa1_irq, + .disable = disable_eisa1_irq, + .ack = mask_and_ack_eisa1_irq, + .end = end_eisa1_irq, +}; + +static void enable_eisa2_irq(unsigned int irq) +{ + unsigned long flags; + u8 mask; + + local_irq_save(flags); + + mask = EISA_READ_8(EISA_INT2_MASK); + mask &= ~((u8) (1 << (irq - 8))); + EISA_WRITE_8(EISA_INT2_MASK, mask); + + local_irq_restore(flags); +} + +static unsigned int startup_eisa2_irq(unsigned int irq) +{ + u8 edge; + + /* Only use edge interrupts for EISA */ + + edge = EISA_READ_8(EISA_INT2_EDGE_LEVEL); + edge &= ~((u8) (1 << (irq - 8))); + EISA_WRITE_8(EISA_INT2_EDGE_LEVEL, edge); + + enable_eisa2_irq(irq); + return 0; +} + +static void disable_eisa2_irq(unsigned int irq) +{ + u8 mask; + + mask = EISA_READ_8(EISA_INT2_MASK); + mask |= ((u8) (1 << (irq - 8))); + EISA_WRITE_8(EISA_INT2_MASK, mask); +} + +#define shutdown_eisa2_irq disable_eisa2_irq + +static void mask_and_ack_eisa2_irq(unsigned int irq) +{ + disable_eisa2_irq(irq); + + EISA_WRITE_8(EISA_INT2_CTRL, 0x20); + EISA_WRITE_8(EISA_INT1_CTRL, 0x20); +} + +static void end_eisa2_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) + enable_eisa2_irq(irq); +} + +static struct hw_interrupt_type ip22_eisa2_irq_type = { + .typename = "IP22 EISA", + .startup = startup_eisa2_irq, + .shutdown = shutdown_eisa2_irq, + .enable = enable_eisa2_irq, + .disable = disable_eisa2_irq, + .ack = mask_and_ack_eisa2_irq, + .end = end_eisa2_irq, +}; + +static struct irqaction eisa_action = { + .handler = ip22_eisa_intr, + .name = "EISA", +}; + +static struct irqaction cascade_action = { + .handler = no_action, + .name = "EISA cascade", +}; + +int __init ip22_eisa_init(void) +{ + int i, c; + char *str; + u8 *slot_addr; + + if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) { + printk(KERN_INFO "EISA: bus not present.\n"); + return 1; + } + + printk(KERN_INFO "EISA: Probing bus...\n"); + for (c = 0, i = 1; i <= EISA_MAX_SLOTS; i++) { + slot_addr = + (u8 *) EISA_TO_KSEG1((0x1000 * i) + + EISA_VENDOR_ID_OFFSET); + if ((str = decode_eisa_sig(slot_addr))) { + printk(KERN_INFO "EISA: slot %d : %s detected.\n", + i, str); + c++; + } + } + printk(KERN_INFO "EISA: Detected %d card%s.\n", c, c < 2 ? "" : "s"); +#ifdef CONFIG_ISA + printk(KERN_INFO "ISA support compiled in.\n"); +#endif + + /* Warning : BlackMagicAhead(tm). + Please wave your favorite dead chicken over the busses */ + + /* First say hello to the EIU */ + EIU_WRITE_32(EIU_PREMPT_REG, 0x0000FFFF); + EIU_WRITE_32(EIU_QUIET_REG, 1); + EIU_WRITE_32(EIU_MODE_REG, 0x40f3c07F); + + /* Now be nice to the EISA chipset */ + EISA_WRITE_8(EISA_EXT_NMI_RESET_CTRL, 1); + for (i = 0; i < 10000; i++); /* Wait long enough for the dust to settle */ + EISA_WRITE_8(EISA_EXT_NMI_RESET_CTRL, 0); + EISA_WRITE_8(EISA_INT1_CTRL, 0x11); + EISA_WRITE_8(EISA_INT2_CTRL, 0x11); + EISA_WRITE_8(EISA_INT1_MASK, 0); + EISA_WRITE_8(EISA_INT2_MASK, 8); + EISA_WRITE_8(EISA_INT1_MASK, 4); + EISA_WRITE_8(EISA_INT2_MASK, 2); + EISA_WRITE_8(EISA_INT1_MASK, 1); + EISA_WRITE_8(EISA_INT2_MASK, 1); + EISA_WRITE_8(EISA_INT1_MASK, 0xfb); + EISA_WRITE_8(EISA_INT2_MASK, 0xff); + EISA_WRITE_8(EISA_DMA2_WRITE_SINGLE, 0); + + for (i = SGINT_EISA; i < (SGINT_EISA + EISA_MAX_IRQ); i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = 0; + irq_desc[i].depth = 1; + if (i < (SGINT_EISA + 8)) + irq_desc[i].handler = &ip22_eisa1_irq_type; + else + irq_desc[i].handler = &ip22_eisa2_irq_type; + } + + /* Cannot use request_irq because of kmalloc not being ready at such + * an early stage. Yes, I've been bitten... */ + setup_irq(SGI_EISA_IRQ, &eisa_action); + setup_irq(SGINT_EISA + 2, &cascade_action); + + EISA_bus = 1; + return 0; +} diff --git a/arch/mips/sgi-ip22/ip22-hpc.c b/arch/mips/sgi-ip22/ip22-hpc.c new file mode 100644 index 00000000000..c0afeccb08c --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-hpc.c @@ -0,0 +1,63 @@ +/* + * ip22-hpc.c: Routines for generic manipulation of the HPC controllers. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1998 Ralf Baechle + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/types.h> + +#include <asm/io.h> +#include <asm/sgi/hpc3.h> +#include <asm/sgi/ioc.h> +#include <asm/sgi/ip22.h> + +struct hpc3_regs *hpc3c0, *hpc3c1; + +EXPORT_SYMBOL(hpc3c0); +EXPORT_SYMBOL(hpc3c1); + +struct sgioc_regs *sgioc; + +EXPORT_SYMBOL(sgioc); + +/* We need software copies of these because they are write only. */ +u8 sgi_ioc_reset, sgi_ioc_write; + +extern char *system_type; + +void __init sgihpc_init(void) +{ + /* ioremap can't fail */ + hpc3c0 = (struct hpc3_regs *) + ioremap(HPC3_CHIP0_BASE, sizeof(struct hpc3_regs)); + hpc3c1 = (struct hpc3_regs *) + ioremap(HPC3_CHIP1_BASE, sizeof(struct hpc3_regs)); + /* IOC lives in PBUS PIO channel 6 */ + sgioc = (struct sgioc_regs *)hpc3c0->pbus_extregs[6]; + + hpc3c0->pbus_piocfg[6][0] |= HPC3_PIOCFG_DS16; + if (ip22_is_fullhouse()) { + /* Full House comes with INT2 which lives in PBUS PIO + * channel 4 */ + sgint = (struct sgint_regs *)hpc3c0->pbus_extregs[4]; + system_type = "SGI Indigo2"; + } else { + /* Guiness comes with INT3 which is part of IOC */ + sgint = &sgioc->int3; + system_type = "SGI Indy"; + } + + sgi_ioc_reset = (SGIOC_RESET_PPORT | SGIOC_RESET_KBDMOUSE | + SGIOC_RESET_EISA | SGIOC_RESET_ISDN | + SGIOC_RESET_LC0OFF); + + sgi_ioc_write = (SGIOC_WRITE_EASEL | SGIOC_WRITE_NTHRESH | + SGIOC_WRITE_TPSPEED | SGIOC_WRITE_EPSEL | + SGIOC_WRITE_U0AMODE | SGIOC_WRITE_U1AMODE); + + sgioc->reset = sgi_ioc_reset; + sgioc->write = sgi_ioc_write; +} diff --git a/arch/mips/sgi-ip22/ip22-int.c b/arch/mips/sgi-ip22/ip22-int.c new file mode 100644 index 00000000000..ea2844d29e6 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-int.c @@ -0,0 +1,410 @@ +/* + * ip22-int.c: Routines for generic manipulation of the INT[23] ASIC + * found on INDY and Indigo2 workstations. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) + * - Indigo2 changes + * - Interrupt handling fixes + * Copyright (C) 2001, 2003 Ladislav Michl (ladis@linux-mips.org) + */ +#include <linux/config.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/irq.h> + +#include <asm/mipsregs.h> +#include <asm/addrspace.h> + +#include <asm/sgi/ioc.h> +#include <asm/sgi/hpc3.h> +#include <asm/sgi/ip22.h> + +/* #define DEBUG_SGINT */ + +/* So far nothing hangs here */ +#undef USE_LIO3_IRQ + +struct sgint_regs *sgint; + +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); +extern int ip22_eisa_init(void); + +static void enable_local0_irq(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + /* don't allow mappable interrupt to be enabled from setup_irq, + * we have our own way to do so */ + if (irq != SGI_MAP_0_IRQ) + sgint->imask0 |= (1 << (irq - SGINT_LOCAL0)); + local_irq_restore(flags); +} + +static unsigned int startup_local0_irq(unsigned int irq) +{ + enable_local0_irq(irq); + return 0; /* Never anything pending */ +} + +static void disable_local0_irq(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + sgint->imask0 &= ~(1 << (irq - SGINT_LOCAL0)); + local_irq_restore(flags); +} + +#define shutdown_local0_irq disable_local0_irq +#define mask_and_ack_local0_irq disable_local0_irq + +static void end_local0_irq (unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_local0_irq(irq); +} + +static struct hw_interrupt_type ip22_local0_irq_type = { + .typename = "IP22 local 0", + .startup = startup_local0_irq, + .shutdown = shutdown_local0_irq, + .enable = enable_local0_irq, + .disable = disable_local0_irq, + .ack = mask_and_ack_local0_irq, + .end = end_local0_irq, +}; + +static void enable_local1_irq(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + /* don't allow mappable interrupt to be enabled from setup_irq, + * we have our own way to do so */ + if (irq != SGI_MAP_1_IRQ) + sgint->imask1 |= (1 << (irq - SGINT_LOCAL1)); + local_irq_restore(flags); +} + +static unsigned int startup_local1_irq(unsigned int irq) +{ + enable_local1_irq(irq); + return 0; /* Never anything pending */ +} + +void disable_local1_irq(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + sgint->imask1 &= ~(1 << (irq - SGINT_LOCAL1)); + local_irq_restore(flags); +} + +#define shutdown_local1_irq disable_local1_irq +#define mask_and_ack_local1_irq disable_local1_irq + +static void end_local1_irq (unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_local1_irq(irq); +} + +static struct hw_interrupt_type ip22_local1_irq_type = { + .typename = "IP22 local 1", + .startup = startup_local1_irq, + .shutdown = shutdown_local1_irq, + .enable = enable_local1_irq, + .disable = disable_local1_irq, + .ack = mask_and_ack_local1_irq, + .end = end_local1_irq, +}; + +static void enable_local2_irq(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + sgint->imask0 |= (1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0)); + sgint->cmeimask0 |= (1 << (irq - SGINT_LOCAL2)); + local_irq_restore(flags); +} + +static unsigned int startup_local2_irq(unsigned int irq) +{ + enable_local2_irq(irq); + return 0; /* Never anything pending */ +} + +void disable_local2_irq(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + sgint->cmeimask0 &= ~(1 << (irq - SGINT_LOCAL2)); + if (!sgint->cmeimask0) + sgint->imask0 &= ~(1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0)); + local_irq_restore(flags); +} + +#define shutdown_local2_irq disable_local2_irq +#define mask_and_ack_local2_irq disable_local2_irq + +static void end_local2_irq (unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_local2_irq(irq); +} + +static struct hw_interrupt_type ip22_local2_irq_type = { + .typename = "IP22 local 2", + .startup = startup_local2_irq, + .shutdown = shutdown_local2_irq, + .enable = enable_local2_irq, + .disable = disable_local2_irq, + .ack = mask_and_ack_local2_irq, + .end = end_local2_irq, +}; + +static void enable_local3_irq(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + sgint->imask1 |= (1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1)); + sgint->cmeimask1 |= (1 << (irq - SGINT_LOCAL3)); + local_irq_restore(flags); +} + +static unsigned int startup_local3_irq(unsigned int irq) +{ + enable_local3_irq(irq); + return 0; /* Never anything pending */ +} + +void disable_local3_irq(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + sgint->cmeimask1 &= ~(1 << (irq - SGINT_LOCAL3)); + if (!sgint->cmeimask1) + sgint->imask1 &= ~(1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1)); + local_irq_restore(flags); +} + +#define shutdown_local3_irq disable_local3_irq +#define mask_and_ack_local3_irq disable_local3_irq + +static void end_local3_irq (unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_local3_irq(irq); +} + +static struct hw_interrupt_type ip22_local3_irq_type = { + .typename = "IP22 local 3", + .startup = startup_local3_irq, + .shutdown = shutdown_local3_irq, + .enable = enable_local3_irq, + .disable = disable_local3_irq, + .ack = mask_and_ack_local3_irq, + .end = end_local3_irq, +}; + +void indy_local0_irqdispatch(struct pt_regs *regs) +{ + u8 mask = sgint->istat0 & sgint->imask0; + u8 mask2; + int irq; + + if (mask & SGINT_ISTAT0_LIO2) { + mask2 = sgint->vmeistat & sgint->cmeimask0; + irq = lc2msk_to_irqnr[mask2]; + } else + irq = lc0msk_to_irqnr[mask]; + + /* if irq == 0, then the interrupt has already been cleared */ + if (irq) + do_IRQ(irq, regs); + return; +} + +void indy_local1_irqdispatch(struct pt_regs *regs) +{ + u8 mask = sgint->istat1 & sgint->imask1; + u8 mask2; + int irq; + + if (mask & SGINT_ISTAT1_LIO3) { + mask2 = sgint->vmeistat & sgint->cmeimask1; + irq = lc3msk_to_irqnr[mask2]; + } else + irq = lc1msk_to_irqnr[mask]; + + /* if irq == 0, then the interrupt has already been cleared */ + if (irq) + do_IRQ(irq, regs); + return; +} + +extern void ip22_be_interrupt(int irq, struct pt_regs *regs); + +void indy_buserror_irq(struct pt_regs *regs) +{ + int irq = SGI_BUSERR_IRQ; + + irq_enter(); + kstat_this_cpu.irqs[irq]++; + ip22_be_interrupt(irq, regs); + irq_exit(); +} + +static struct irqaction local0_cascade = { + .handler = no_action, + .flags = SA_INTERRUPT, + .name = "local0 cascade", +}; + +static struct irqaction local1_cascade = { + .handler = no_action, + .flags = SA_INTERRUPT, + .name = "local1 cascade", +}; + +static struct irqaction buserr = { + .handler = no_action, + .flags = SA_INTERRUPT, + .name = "Bus Error", +}; + +static struct irqaction map0_cascade = { + .handler = no_action, + .flags = SA_INTERRUPT, + .name = "mapable0 cascade", +}; + +#ifdef USE_LIO3_IRQ +static struct irqaction map1_cascade = { + .handler = no_action, + .flags = SA_INTERRUPT, + .name = "mapable1 cascade", +}; +#define SGI_INTERRUPTS SGINT_END +#else +#define SGI_INTERRUPTS SGINT_LOCAL3 +#endif + +extern void mips_cpu_irq_init(unsigned int irq_base); + +void __init arch_init_irq(void) +{ + int i; + + /* Init local mask --> irq tables. */ + for (i = 0; i < 256; i++) { + if (i & 0x80) { + lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 7; + lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 7; + lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 7; + lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 7; + } else if (i & 0x40) { + lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 6; + lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 6; + lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 6; + lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 6; + } else if (i & 0x20) { + lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 5; + lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 5; + lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 5; + lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 5; + } else if (i & 0x10) { + lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 4; + lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 4; + lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 4; + lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 4; + } else if (i & 0x08) { + lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 3; + lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 3; + lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 3; + lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 3; + } else if (i & 0x04) { + lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 2; + lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 2; + lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 2; + lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 2; + } else if (i & 0x02) { + lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 1; + lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 1; + lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 1; + lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 1; + } else if (i & 0x01) { + lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 0; + lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 0; + lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 0; + lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 0; + } else { + lc0msk_to_irqnr[i] = 0; + lc1msk_to_irqnr[i] = 0; + lc2msk_to_irqnr[i] = 0; + lc3msk_to_irqnr[i] = 0; + } + } + + /* Mask out all interrupts. */ + sgint->imask0 = 0; + sgint->imask1 = 0; + sgint->cmeimask0 = 0; + sgint->cmeimask1 = 0; + + set_except_vector(0, indyIRQ); + + /* init CPU irqs */ + mips_cpu_irq_init(SGINT_CPU); + + for (i = SGINT_LOCAL0; i < SGI_INTERRUPTS; i++) { + hw_irq_controller *handler; + + if (i < SGINT_LOCAL1) + handler = &ip22_local0_irq_type; + else if (i < SGINT_LOCAL2) + handler = &ip22_local1_irq_type; + else if (i < SGINT_LOCAL3) + handler = &ip22_local2_irq_type; + else + handler = &ip22_local3_irq_type; + + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = 0; + irq_desc[i].depth = 1; + irq_desc[i].handler = handler; + } + + /* vector handler. this register the IRQ as non-sharable */ + setup_irq(SGI_LOCAL_0_IRQ, &local0_cascade); + setup_irq(SGI_LOCAL_1_IRQ, &local1_cascade); + setup_irq(SGI_BUSERR_IRQ, &buserr); + + /* cascade in cascade. i love Indy ;-) */ + setup_irq(SGI_MAP_0_IRQ, &map0_cascade); +#ifdef USE_LIO3_IRQ + setup_irq(SGI_MAP_1_IRQ, &map1_cascade); +#endif + +#ifdef CONFIG_EISA + if (ip22_is_fullhouse()) /* Only Indigo-2 has EISA stuff */ + ip22_eisa_init (); +#endif +} diff --git a/arch/mips/sgi-ip22/ip22-irq.S b/arch/mips/sgi-ip22/ip22-irq.S new file mode 100644 index 00000000000..6ccbd9e1d96 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-irq.S @@ -0,0 +1,118 @@ +/* + * ip22-irq.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/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 + + /* 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. */ + jal indy_r4k_timer_interrupt + move a0, sp # delay slot + j ret_from_irq + nop # delay slot + +1: + beq a0, zero, 1f + andi a0, s0, CAUSEF_IP3 # delay slot, check local level one + + /* Wheee, local level zero interrupt. */ + jal indy_local0_irqdispatch + move a0, sp # delay slot + + j ret_from_irq + nop # delay slot + +1: + beq a0, zero, 1f + andi a0, s0, CAUSEF_IP6 # delay slot, check bus error + + /* Wheee, local level one interrupt. */ + jal indy_local1_irqdispatch + move a0, sp # delay slot + j ret_from_irq + nop # delay slot + +1: + beq a0, zero, 1f + andi a0, s0, (CAUSEF_IP4 | CAUSEF_IP5) # delay slot + + /* Wheee, an asynchronous bus error... */ + jal indy_buserror_irq + move a0, sp # delay slot + j ret_from_irq + nop # delay slot + +1: + /* Here by mistake? It is possible, that by the time we take + * the exception the IRQ pin goes low, so just leave if this + * is the case. + */ + beq a0, zero, 1f + nop # delay slot + + /* Must be one of the 8254 timers... */ + jal indy_8254timer_irq + move a0, sp # delay slot +1: + j ret_from_irq + nop # delay slot + END(indyIRQ) diff --git a/arch/mips/sgi-ip22/ip22-mc.c b/arch/mips/sgi-ip22/ip22-mc.c new file mode 100644 index 00000000000..b58bd522262 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-mc.c @@ -0,0 +1,208 @@ +/* + * ip22-mc.c: Routines for manipulating SGI Memory Controller. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) - Indigo2 changes + * Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org) + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> + +#include <asm/io.h> +#include <asm/bootinfo.h> +#include <asm/sgialib.h> +#include <asm/sgi/mc.h> +#include <asm/sgi/hpc3.h> +#include <asm/sgi/ip22.h> + +struct sgimc_regs *sgimc; |