diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /arch/mips/sgi-ip32 | |
parent | Initial commit. (diff) | |
download | linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip |
Adding upstream version 5.10.209.upstream/5.10.209upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | arch/mips/sgi-ip32/Makefile | 8 | ||||
-rw-r--r-- | arch/mips/sgi-ip32/Platform | 10 | ||||
-rw-r--r-- | arch/mips/sgi-ip32/crime.c | 103 | ||||
-rw-r--r-- | arch/mips/sgi-ip32/ip32-berr.c | 38 | ||||
-rw-r--r-- | arch/mips/sgi-ip32/ip32-dma.c | 37 | ||||
-rw-r--r-- | arch/mips/sgi-ip32/ip32-irq.c | 499 | ||||
-rw-r--r-- | arch/mips/sgi-ip32/ip32-memory.c | 47 | ||||
-rw-r--r-- | arch/mips/sgi-ip32/ip32-platform.c | 138 | ||||
-rw-r--r-- | arch/mips/sgi-ip32/ip32-reset.c | 152 | ||||
-rw-r--r-- | arch/mips/sgi-ip32/ip32-setup.c | 100 |
10 files changed, 1132 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip32/Makefile b/arch/mips/sgi-ip32/Makefile new file mode 100644 index 000000000..de0222466 --- /dev/null +++ b/arch/mips/sgi-ip32/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the SGI specific kernel interface routines +# under Linux. +# + +obj-y += ip32-berr.o ip32-irq.o ip32-platform.o ip32-setup.o ip32-reset.o \ + crime.o ip32-memory.o ip32-dma.o diff --git a/arch/mips/sgi-ip32/Platform b/arch/mips/sgi-ip32/Platform new file mode 100644 index 000000000..f58a7a02b --- /dev/null +++ b/arch/mips/sgi-ip32/Platform @@ -0,0 +1,10 @@ +# +# SGI-IP32 (O2) +# +# Set the load address to >= 80069000 if you want to leave space for symmon, +# 0xffffffff80004000 for production kernels. Note that the value must be aligned to +# a multiple of the kernel stack size or the handling of the current variable +# will break. +# +cflags-$(CONFIG_SGI_IP32) += -I$(srctree)/arch/mips/include/asm/mach-ip32 +load-$(CONFIG_SGI_IP32) += 0xffffffff80004000 diff --git a/arch/mips/sgi-ip32/crime.c b/arch/mips/sgi-ip32/crime.c new file mode 100644 index 000000000..a8e0c776c --- /dev/null +++ b/arch/mips/sgi-ip32/crime.c @@ -0,0 +1,103 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001, 2003 Keith M Wesolowski + * Copyright (C) 2005 Ilya A. Volynets <ilya@total-knowledge.com> + */ +#include <linux/types.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/export.h> +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <asm/mipsregs.h> +#include <asm/page.h> +#include <asm/ip32/crime.h> +#include <asm/ip32/mace.h> + +struct sgi_crime __iomem *crime; +struct sgi_mace __iomem *mace; + +EXPORT_SYMBOL_GPL(mace); + +void __init crime_init(void) +{ + unsigned int id, rev; + const int field = 2 * sizeof(unsigned long); + + set_io_port_base((unsigned long) ioremap(MACEPCI_LOW_IO, 0x2000000)); + crime = ioremap(CRIME_BASE, sizeof(struct sgi_crime)); + mace = ioremap(MACE_BASE, sizeof(struct sgi_mace)); + + id = crime->id; + rev = id & CRIME_ID_REV; + id = (id & CRIME_ID_IDBITS) >> 4; + printk(KERN_INFO "CRIME id %1x rev %d at 0x%0*lx\n", + id, rev, field, (unsigned long) CRIME_BASE); +} + +irqreturn_t crime_memerr_intr(unsigned int irq, void *dev_id) +{ + unsigned long stat, addr; + int fatal = 0; + + stat = crime->mem_error_stat & CRIME_MEM_ERROR_STAT_MASK; + addr = crime->mem_error_addr & CRIME_MEM_ERROR_ADDR_MASK; + + printk("CRIME memory error at 0x%08lx ST 0x%08lx<", addr, stat); + + if (stat & CRIME_MEM_ERROR_INV) + printk("INV,"); + if (stat & CRIME_MEM_ERROR_ECC) { + unsigned long ecc_syn = + crime->mem_ecc_syn & CRIME_MEM_ERROR_ECC_SYN_MASK; + unsigned long ecc_gen = + crime->mem_ecc_chk & CRIME_MEM_ERROR_ECC_CHK_MASK; + printk("ECC,SYN=0x%08lx,GEN=0x%08lx,", ecc_syn, ecc_gen); + } + if (stat & CRIME_MEM_ERROR_MULTIPLE) { + fatal = 1; + printk("MULTIPLE,"); + } + if (stat & CRIME_MEM_ERROR_HARD_ERR) { + fatal = 1; + printk("HARD,"); + } + if (stat & CRIME_MEM_ERROR_SOFT_ERR) + printk("SOFT,"); + if (stat & CRIME_MEM_ERROR_CPU_ACCESS) + printk("CPU,"); + if (stat & CRIME_MEM_ERROR_VICE_ACCESS) + printk("VICE,"); + if (stat & CRIME_MEM_ERROR_GBE_ACCESS) + printk("GBE,"); + if (stat & CRIME_MEM_ERROR_RE_ACCESS) + printk("RE,REID=0x%02lx,", (stat & CRIME_MEM_ERROR_RE_ID)>>8); + if (stat & CRIME_MEM_ERROR_MACE_ACCESS) + printk("MACE,MACEID=0x%02lx,", stat & CRIME_MEM_ERROR_MACE_ID); + + crime->mem_error_stat = 0; + + if (fatal) { + printk("FATAL>\n"); + panic("Fatal memory error."); + } else + printk("NONFATAL>\n"); + + return IRQ_HANDLED; +} + +irqreturn_t crime_cpuerr_intr(unsigned int irq, void *dev_id) +{ + unsigned long stat = crime->cpu_error_stat & CRIME_CPU_ERROR_MASK; + unsigned long addr = crime->cpu_error_addr & CRIME_CPU_ERROR_ADDR_MASK; + + addr <<= 2; + printk("CRIME CPU error at 0x%09lx status 0x%08lx\n", addr, stat); + crime->cpu_error_stat = 0; + + return IRQ_HANDLED; +} diff --git a/arch/mips/sgi-ip32/ip32-berr.c b/arch/mips/sgi-ip32/ip32-berr.c new file mode 100644 index 000000000..c860f95ab --- /dev/null +++ b/arch/mips/sgi-ip32/ip32-berr.c @@ -0,0 +1,38 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1994, 1995, 1996, 1999, 2000 by Ralf Baechle + * Copyright (C) 1999, 2000 by Silicon Graphics + * Copyright (C) 2002 Maciej W. Rozycki + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/sched/debug.h> +#include <linux/sched/signal.h> +#include <asm/traps.h> +#include <linux/uaccess.h> +#include <asm/addrspace.h> +#include <asm/ptrace.h> +#include <asm/tlbdebug.h> + +static int ip32_be_handler(struct pt_regs *regs, int is_fixup) +{ + int data = regs->cp0_cause & 4; + + if (is_fixup) + return MIPS_BE_FIXUP; + + printk("Got %cbe at 0x%lx\n", data ? 'd' : 'i', regs->cp0_epc); + show_regs(regs); + dump_tlb_all(); + while(1); + force_sig(SIGBUS); +} + +void __init ip32_be_init(void) +{ + board_be_handler = ip32_be_handler; +} diff --git a/arch/mips/sgi-ip32/ip32-dma.c b/arch/mips/sgi-ip32/ip32-dma.c new file mode 100644 index 000000000..20c6da9d7 --- /dev/null +++ b/arch/mips/sgi-ip32/ip32-dma.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2006 Ralf Baechle <ralf@linux-mips.org> + */ +#include <linux/dma-direct.h> +#include <asm/ip32/crime.h> + +/* + * Few notes. + * 1. CPU sees memory as two chunks: 0-256M@0x0, and the rest @0x40000000+256M + * 2. PCI sees memory as one big chunk @0x0 (or we could use 0x40000000 for + * native-endian) + * 3. All other devices see memory as one big chunk at 0x40000000 + * 4. Non-PCI devices will pass NULL as struct device* + * + * Thus we translate differently, depending on device. + */ + +#define RAM_OFFSET_MASK 0x3fffffffUL + +dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) +{ + dma_addr_t dma_addr = paddr & RAM_OFFSET_MASK; + + if (!dev) + dma_addr += CRIME_HI_MEM_BASE; + return dma_addr; +} + +phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dma_addr) +{ + phys_addr_t paddr = dma_addr & RAM_OFFSET_MASK; + + if (dma_addr >= 256*1024*1024) + paddr += CRIME_HI_MEM_BASE; + return paddr; +} diff --git a/arch/mips/sgi-ip32/ip32-irq.c b/arch/mips/sgi-ip32/ip32-irq.c new file mode 100644 index 000000000..1bbd5bfb5 --- /dev/null +++ b/arch/mips/sgi-ip32/ip32-irq.c @@ -0,0 +1,499 @@ +/* + * Code to handle IP32 IRQs + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2000 Harald Koerfgen + * Copyright (C) 2001 Keith M Wesolowski + */ +#include <linux/init.h> +#include <linux/kernel_stat.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/bitops.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/random.h> +#include <linux/sched.h> +#include <linux/sched/debug.h> + +#include <asm/irq_cpu.h> +#include <asm/mipsregs.h> +#include <asm/signal.h> +#include <asm/time.h> +#include <asm/ip32/crime.h> +#include <asm/ip32/mace.h> +#include <asm/ip32/ip32_ints.h> + +/* issue a PIO read to make sure no PIO writes are pending */ +static inline void flush_crime_bus(void) +{ + crime->control; +} + +static inline void flush_mace_bus(void) +{ + mace->perif.ctrl.misc; +} + +/* + * O2 irq map + * + * IP0 -> software (ignored) + * IP1 -> software (ignored) + * IP2 -> (irq0) C crime 1.1 all interrupts; crime 1.5 ??? + * IP3 -> (irq1) X unknown + * IP4 -> (irq2) X unknown + * IP5 -> (irq3) X unknown + * IP6 -> (irq4) X unknown + * IP7 -> (irq5) 7 CPU count/compare timer (system timer) + * + * crime: (C) + * + * CRIME_INT_STAT 31:0: + * + * 0 -> 8 Video in 1 + * 1 -> 9 Video in 2 + * 2 -> 10 Video out + * 3 -> 11 Mace ethernet + * 4 -> S SuperIO sub-interrupt + * 5 -> M Miscellaneous sub-interrupt + * 6 -> A Audio sub-interrupt + * 7 -> 15 PCI bridge errors + * 8 -> 16 PCI SCSI aic7xxx 0 + * 9 -> 17 PCI SCSI aic7xxx 1 + * 10 -> 18 PCI slot 0 + * 11 -> 19 unused (PCI slot 1) + * 12 -> 20 unused (PCI slot 2) + * 13 -> 21 unused (PCI shared 0) + * 14 -> 22 unused (PCI shared 1) + * 15 -> 23 unused (PCI shared 2) + * 16 -> 24 GBE0 (E) + * 17 -> 25 GBE1 (E) + * 18 -> 26 GBE2 (E) + * 19 -> 27 GBE3 (E) + * 20 -> 28 CPU errors + * 21 -> 29 Memory errors + * 22 -> 30 RE empty edge (E) + * 23 -> 31 RE full edge (E) + * 24 -> 32 RE idle edge (E) + * 25 -> 33 RE empty level + * 26 -> 34 RE full level + * 27 -> 35 RE idle level + * 28 -> 36 unused (software 0) (E) + * 29 -> 37 unused (software 1) (E) + * 30 -> 38 unused (software 2) - crime 1.5 CPU SysCorError (E) + * 31 -> 39 VICE + * + * S, M, A: Use the MACE ISA interrupt register + * MACE_ISA_INT_STAT 31:0 + * + * 0-7 -> 40-47 Audio + * 8 -> 48 RTC + * 9 -> 49 Keyboard + * 10 -> X Keyboard polled + * 11 -> 51 Mouse + * 12 -> X Mouse polled + * 13-15 -> 53-55 Count/compare timers + * 16-19 -> 56-59 Parallel (16 E) + * 20-25 -> 60-62 Serial 1 (22 E) + * 26-31 -> 66-71 Serial 2 (28 E) + * + * Note that this means IRQs 12-14, 50, and 52 do not exist. This is a + * different IRQ map than IRIX uses, but that's OK as Linux irq handling + * is quite different anyway. + */ + +/* Some initial interrupts to set up */ +extern irqreturn_t crime_memerr_intr(int irq, void *dev_id); +extern irqreturn_t crime_cpuerr_intr(int irq, void *dev_id); + +/* + * This is for pure CRIME interrupts - ie not MACE. The advantage? + * We get to split the register in half and do faster lookups. + */ + +static uint64_t crime_mask; + +static inline void crime_enable_irq(struct irq_data *d) +{ + unsigned int bit = d->irq - CRIME_IRQ_BASE; + + crime_mask |= 1 << bit; + crime->imask = crime_mask; +} + +static inline void crime_disable_irq(struct irq_data *d) +{ + unsigned int bit = d->irq - CRIME_IRQ_BASE; + + crime_mask &= ~(1 << bit); + crime->imask = crime_mask; + flush_crime_bus(); +} + +static struct irq_chip crime_level_interrupt = { + .name = "IP32 CRIME", + .irq_mask = crime_disable_irq, + .irq_unmask = crime_enable_irq, +}; + +static void crime_edge_mask_and_ack_irq(struct irq_data *d) +{ + unsigned int bit = d->irq - CRIME_IRQ_BASE; + uint64_t crime_int; + + /* Edge triggered interrupts must be cleared. */ + crime_int = crime->hard_int; + crime_int &= ~(1 << bit); + crime->hard_int = crime_int; + + crime_disable_irq(d); +} + +static struct irq_chip crime_edge_interrupt = { + .name = "IP32 CRIME", + .irq_ack = crime_edge_mask_and_ack_irq, + .irq_mask = crime_disable_irq, + .irq_mask_ack = crime_edge_mask_and_ack_irq, + .irq_unmask = crime_enable_irq, +}; + +/* + * This is for MACE PCI interrupts. We can decrease bus traffic by masking + * as close to the source as possible. This also means we can take the + * next chunk of the CRIME register in one piece. + */ + +static unsigned long macepci_mask; + +static void enable_macepci_irq(struct irq_data *d) +{ + macepci_mask |= MACEPCI_CONTROL_INT(d->irq - MACEPCI_SCSI0_IRQ); + mace->pci.control = macepci_mask; + crime_mask |= 1 << (d->irq - CRIME_IRQ_BASE); + crime->imask = crime_mask; +} + +static void disable_macepci_irq(struct irq_data *d) +{ + crime_mask &= ~(1 << (d->irq - CRIME_IRQ_BASE)); + crime->imask = crime_mask; + flush_crime_bus(); + macepci_mask &= ~MACEPCI_CONTROL_INT(d->irq - MACEPCI_SCSI0_IRQ); + mace->pci.control = macepci_mask; + flush_mace_bus(); +} + +static struct irq_chip ip32_macepci_interrupt = { + .name = "IP32 MACE PCI", + .irq_mask = disable_macepci_irq, + .irq_unmask = enable_macepci_irq, +}; + +/* This is used for MACE ISA interrupts. That means bits 4-6 in the + * CRIME register. + */ + +#define MACEISA_AUDIO_INT (MACEISA_AUDIO_SW_INT | \ + MACEISA_AUDIO_SC_INT | \ + MACEISA_AUDIO1_DMAT_INT | \ + MACEISA_AUDIO1_OF_INT | \ + MACEISA_AUDIO2_DMAT_INT | \ + MACEISA_AUDIO2_MERR_INT | \ + MACEISA_AUDIO3_DMAT_INT | \ + MACEISA_AUDIO3_MERR_INT) +#define MACEISA_MISC_INT (MACEISA_RTC_INT | \ + MACEISA_KEYB_INT | \ + MACEISA_KEYB_POLL_INT | \ + MACEISA_MOUSE_INT | \ + MACEISA_MOUSE_POLL_INT | \ + MACEISA_TIMER0_INT | \ + MACEISA_TIMER1_INT | \ + MACEISA_TIMER2_INT) +#define MACEISA_SUPERIO_INT (MACEISA_PARALLEL_INT | \ + MACEISA_PAR_CTXA_INT | \ + MACEISA_PAR_CTXB_INT | \ + MACEISA_PAR_MERR_INT | \ + MACEISA_SERIAL1_INT | \ + MACEISA_SERIAL1_TDMAT_INT | \ + MACEISA_SERIAL1_TDMAPR_INT | \ + MACEISA_SERIAL1_TDMAME_INT | \ + MACEISA_SERIAL1_RDMAT_INT | \ + MACEISA_SERIAL1_RDMAOR_INT | \ + MACEISA_SERIAL2_INT | \ + MACEISA_SERIAL2_TDMAT_INT | \ + MACEISA_SERIAL2_TDMAPR_INT | \ + MACEISA_SERIAL2_TDMAME_INT | \ + MACEISA_SERIAL2_RDMAT_INT | \ + MACEISA_SERIAL2_RDMAOR_INT) + +static unsigned long maceisa_mask; + +static void enable_maceisa_irq(struct irq_data *d) +{ + unsigned int crime_int = 0; + + pr_debug("maceisa enable: %u\n", d->irq); + + switch (d->irq) { + case MACEISA_AUDIO_SW_IRQ ... MACEISA_AUDIO3_MERR_IRQ: + crime_int = MACE_AUDIO_INT; + break; + case MACEISA_RTC_IRQ ... MACEISA_TIMER2_IRQ: + crime_int = MACE_MISC_INT; + break; + case MACEISA_PARALLEL_IRQ ... MACEISA_SERIAL2_RDMAOR_IRQ: + crime_int = MACE_SUPERIO_INT; + break; + } + pr_debug("crime_int %08x enabled\n", crime_int); + crime_mask |= crime_int; + crime->imask = crime_mask; + maceisa_mask |= 1 << (d->irq - MACEISA_AUDIO_SW_IRQ); + mace->perif.ctrl.imask = maceisa_mask; +} + +static void disable_maceisa_irq(struct irq_data *d) +{ + unsigned int crime_int = 0; + + maceisa_mask &= ~(1 << (d->irq - MACEISA_AUDIO_SW_IRQ)); + if (!(maceisa_mask & MACEISA_AUDIO_INT)) + crime_int |= MACE_AUDIO_INT; + if (!(maceisa_mask & MACEISA_MISC_INT)) + crime_int |= MACE_MISC_INT; + if (!(maceisa_mask & MACEISA_SUPERIO_INT)) + crime_int |= MACE_SUPERIO_INT; + crime_mask &= ~crime_int; + crime->imask = crime_mask; + flush_crime_bus(); + mace->perif.ctrl.imask = maceisa_mask; + flush_mace_bus(); +} + +static void mask_and_ack_maceisa_irq(struct irq_data *d) +{ + unsigned long mace_int; + + /* edge triggered */ + mace_int = mace->perif.ctrl.istat; + mace_int &= ~(1 << (d->irq - MACEISA_AUDIO_SW_IRQ)); + mace->perif.ctrl.istat = mace_int; + + disable_maceisa_irq(d); +} + +static struct irq_chip ip32_maceisa_level_interrupt = { + .name = "IP32 MACE ISA", + .irq_mask = disable_maceisa_irq, + .irq_unmask = enable_maceisa_irq, +}; + +static struct irq_chip ip32_maceisa_edge_interrupt = { + .name = "IP32 MACE ISA", + .irq_ack = mask_and_ack_maceisa_irq, + .irq_mask = disable_maceisa_irq, + .irq_mask_ack = mask_and_ack_maceisa_irq, + .irq_unmask = enable_maceisa_irq, +}; + +/* This is used for regular non-ISA, non-PCI MACE interrupts. That means + * bits 0-3 and 7 in the CRIME register. + */ + +static void enable_mace_irq(struct irq_data *d) +{ + unsigned int bit = d->irq - CRIME_IRQ_BASE; + + crime_mask |= (1 << bit); + crime->imask = crime_mask; +} + +static void disable_mace_irq(struct irq_data *d) +{ + unsigned int bit = d->irq - CRIME_IRQ_BASE; + + crime_mask &= ~(1 << bit); + crime->imask = crime_mask; + flush_crime_bus(); +} + +static struct irq_chip ip32_mace_interrupt = { + .name = "IP32 MACE", + .irq_mask = disable_mace_irq, + .irq_unmask = enable_mace_irq, +}; + +static void ip32_unknown_interrupt(void) +{ + printk("Unknown interrupt occurred!\n"); + printk("cp0_status: %08x\n", read_c0_status()); + printk("cp0_cause: %08x\n", read_c0_cause()); + printk("CRIME intr mask: %016lx\n", crime->imask); + printk("CRIME intr status: %016lx\n", crime->istat); + printk("CRIME hardware intr register: %016lx\n", crime->hard_int); + printk("MACE ISA intr mask: %08lx\n", mace->perif.ctrl.imask); + printk("MACE ISA intr status: %08lx\n", mace->perif.ctrl.istat); + printk("MACE PCI control register: %08x\n", mace->pci.control); + + printk("Register dump:\n"); + show_regs(get_irq_regs()); + + printk("Please mail this report to linux-mips@linux-mips.org\n"); + printk("Spinning..."); + while(1) ; +} + +/* CRIME 1.1 appears to deliver all interrupts to this one pin. */ +/* change this to loop over all edge-triggered irqs, exception masked out ones */ +static void ip32_irq0(void) +{ + uint64_t crime_int; + int irq = 0; + + /* + * Sanity check interrupt numbering enum. + * MACE got 32 interrupts and there are 32 MACE ISA interrupts daisy + * chained. + */ + BUILD_BUG_ON(CRIME_VICE_IRQ - MACE_VID_IN1_IRQ != 31); + BUILD_BUG_ON(MACEISA_SERIAL2_RDMAOR_IRQ - MACEISA_AUDIO_SW_IRQ != 31); + + crime_int = crime->istat & crime_mask; + + /* crime sometime delivers spurious interrupts, ignore them */ + if (unlikely(crime_int == 0)) + return; + + irq = MACE_VID_IN1_IRQ + __ffs(crime_int); + + if (crime_int & CRIME_MACEISA_INT_MASK) { + unsigned long mace_int = mace->perif.ctrl.istat; + irq = __ffs(mace_int & maceisa_mask) + MACEISA_AUDIO_SW_IRQ; + } + + pr_debug("*irq %u*\n", irq); + do_IRQ(irq); +} + +static void ip32_irq1(void) +{ + ip32_unknown_interrupt(); +} + +static void ip32_irq2(void) +{ + ip32_unknown_interrupt(); +} + +static void ip32_irq3(void) +{ + ip32_unknown_interrupt(); +} + +static void ip32_irq4(void) +{ + ip32_unknown_interrupt(); +} + +static void ip32_irq5(void) +{ + do_IRQ(MIPS_CPU_IRQ_BASE + 7); +} + +asmlinkage void plat_irq_dispatch(void) +{ + unsigned int pending = read_c0_status() & read_c0_cause(); + + if (likely(pending & IE_IRQ0)) + ip32_irq0(); + else if (unlikely(pending & IE_IRQ1)) + ip32_irq1(); + else if (unlikely(pending & IE_IRQ2)) + ip32_irq2(); + else if (unlikely(pending & IE_IRQ3)) + ip32_irq3(); + else if (unlikely(pending & IE_IRQ4)) + ip32_irq4(); + else if (likely(pending & IE_IRQ5)) + ip32_irq5(); +} + +void __init arch_init_irq(void) +{ + unsigned int irq; + + /* Install our interrupt handler, then clear and disable all + * CRIME and MACE interrupts. */ + crime->imask = 0; + crime->hard_int = 0; + crime->soft_int = 0; + mace->perif.ctrl.istat = 0; + mace->perif.ctrl.imask = 0; + + mips_cpu_irq_init(); + for (irq = CRIME_IRQ_BASE; irq <= IP32_IRQ_MAX; irq++) { + switch (irq) { + case MACE_VID_IN1_IRQ ... MACE_PCI_BRIDGE_IRQ: + irq_set_chip_and_handler_name(irq, + &ip32_mace_interrupt, + handle_level_irq, + "level"); + break; + + case MACEPCI_SCSI0_IRQ ... MACEPCI_SHARED2_IRQ: + irq_set_chip_and_handler_name(irq, + &ip32_macepci_interrupt, + handle_level_irq, + "level"); + break; + + case CRIME_CPUERR_IRQ: + case CRIME_MEMERR_IRQ: + irq_set_chip_and_handler_name(irq, + &crime_level_interrupt, + handle_level_irq, + "level"); + break; + + case CRIME_GBE0_IRQ ... CRIME_GBE3_IRQ: + case CRIME_RE_EMPTY_E_IRQ ... CRIME_RE_IDLE_E_IRQ: + case CRIME_SOFT0_IRQ ... CRIME_SOFT2_IRQ: + case CRIME_VICE_IRQ: + irq_set_chip_and_handler_name(irq, + &crime_edge_interrupt, + handle_edge_irq, + "edge"); + break; + + case MACEISA_PARALLEL_IRQ: + case MACEISA_SERIAL1_TDMAPR_IRQ: + case MACEISA_SERIAL2_TDMAPR_IRQ: + irq_set_chip_and_handler_name(irq, + &ip32_maceisa_edge_interrupt, + handle_edge_irq, + "edge"); + break; + + default: + irq_set_chip_and_handler_name(irq, + &ip32_maceisa_level_interrupt, + handle_level_irq, + "level"); + break; + } + } + if (request_irq(CRIME_MEMERR_IRQ, crime_memerr_intr, 0, + "CRIME memory error", NULL)) + pr_err("Failed to register CRIME memory error interrupt\n"); + if (request_irq(CRIME_CPUERR_IRQ, crime_cpuerr_intr, 0, + "CRIME CPU error", NULL)) + pr_err("Failed to register CRIME CPU error interrupt\n"); + +#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) + change_c0_status(ST0_IM, ALLINTS); +} diff --git a/arch/mips/sgi-ip32/ip32-memory.c b/arch/mips/sgi-ip32/ip32-memory.c new file mode 100644 index 000000000..0f53fed39 --- /dev/null +++ b/arch/mips/sgi-ip32/ip32-memory.c @@ -0,0 +1,47 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2003 Keith M Wesolowski + * Copyright (C) 2005 Ilya A. Volynets (Total Knowledge) + */ +#include <linux/types.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/memblock.h> +#include <linux/mm.h> + +#include <asm/ip32/crime.h> +#include <asm/bootinfo.h> +#include <asm/page.h> + +extern void crime_init(void); + +void __init prom_meminit(void) +{ + u64 base, size; + int bank; + + crime_init(); + + for (bank=0; bank < CRIME_MAXBANKS; bank++) { + u64 bankctl = crime->bank_ctrl[bank]; + base = (bankctl & CRIME_MEM_BANK_CONTROL_ADDR) << 25; + if (bank != 0 && base == 0) + continue; + size = (bankctl & CRIME_MEM_BANK_CONTROL_SDRAM_SIZE) ? 128 : 32; + size <<= 20; + if (base + size > (256 << 20)) + base += CRIME_HI_MEM_BASE; + + printk("CRIME MC: bank %u base 0x%016Lx size %LuMiB\n", + bank, base, size >> 20); + memblock_add(base, size); + } +} + + +void __init prom_free_prom_memory(void) +{ +} diff --git a/arch/mips/sgi-ip32/ip32-platform.c b/arch/mips/sgi-ip32/ip32-platform.c new file mode 100644 index 000000000..c3909bd8d --- /dev/null +++ b/arch/mips/sgi-ip32/ip32-platform.c @@ -0,0 +1,138 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2007 Ralf Baechle (ralf@linux-mips.org) + */ +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/serial_8250.h> +#include <linux/rtc/ds1685.h> + +#include <asm/ip32/mace.h> +#include <asm/ip32/ip32_ints.h> + +extern void ip32_prepare_poweroff(void); + +#define MACEISA_SERIAL1_OFFS offsetof(struct sgi_mace, isa.serial1) +#define MACEISA_SERIAL2_OFFS offsetof(struct sgi_mace, isa.serial2) + +#define MACE_PORT(offset,_irq) \ +{ \ + .mapbase = MACE_BASE + offset, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_MEM, \ + .flags = UPF_SKIP_TEST|UPF_IOREMAP, \ + .regshift = 8, \ +} + +static struct plat_serial8250_port uart8250_data[] = { + MACE_PORT(MACEISA_SERIAL1_OFFS, MACEISA_SERIAL1_IRQ), + MACE_PORT(MACEISA_SERIAL2_OFFS, MACEISA_SERIAL2_IRQ), + { }, +}; + +static struct platform_device uart8250_device = { + .name = "serial8250", + .id = PLAT8250_DEV_PLATFORM, + .dev = { + .platform_data = uart8250_data, + }, +}; + +static int __init uart8250_init(void) +{ + return platform_device_register(&uart8250_device); +} + +device_initcall(uart8250_init); + +static __init int meth_devinit(void) +{ + struct platform_device *pd; + int ret; + + pd = platform_device_alloc("meth", -1); + if (!pd) + return -ENOMEM; + + ret = platform_device_add(pd); + if (ret) + platform_device_put(pd); + + return ret; +} + +device_initcall(meth_devinit); + +static __init int sgio2audio_devinit(void) +{ + struct platform_device *pd; + int ret; + + pd = platform_device_alloc("sgio2audio", -1); + if (!pd) + return -ENOMEM; + + ret = platform_device_add(pd); + if (ret) + platform_device_put(pd); + + return ret; +} + +device_initcall(sgio2audio_devinit); + +static __init int sgio2btns_devinit(void) +{ + return IS_ERR(platform_device_register_simple("sgibtns", -1, NULL, 0)); +} + +device_initcall(sgio2btns_devinit); + +#define MACE_RTC_RES_START (MACE_BASE + offsetof(struct sgi_mace, isa.rtc)) +#define MACE_RTC_RES_END (MACE_RTC_RES_START + 32767) + +static struct resource ip32_rtc_resources[] = { + { + .start = MACEISA_RTC_IRQ, + .end = MACEISA_RTC_IRQ, + .flags = IORESOURCE_IRQ + }, { + .start = MACE_RTC_RES_START, + .end = MACE_RTC_RES_END, + .flags = IORESOURCE_MEM, + } +}; + +/* RTC registers on IP32 are each padded by 256 bytes (0x100). */ +static struct ds1685_rtc_platform_data +ip32_rtc_platform_data[] = { + { + .regstep = 0x100, + .bcd_mode = true, + .no_irq = false, + .uie_unsupported = false, + .access_type = ds1685_reg_direct, + .plat_prepare_poweroff = ip32_prepare_poweroff, + }, +}; + +struct platform_device ip32_rtc_device = { + .name = "rtc-ds1685", + .id = -1, + .dev = { + .platform_data = ip32_rtc_platform_data, + }, + .num_resources = ARRAY_SIZE(ip32_rtc_resources), + .resource = ip32_rtc_resources, +}; + +static __init int sgio2_rtc_devinit(void) +{ + return platform_device_register(&ip32_rtc_device); +} + +device_initcall(sgio2_rtc_devinit); diff --git a/arch/mips/sgi-ip32/ip32-reset.c b/arch/mips/sgi-ip32/ip32-reset.c new file mode 100644 index 000000000..20d863734 --- /dev/null +++ b/arch/mips/sgi-ip32/ip32-reset.c @@ -0,0 +1,152 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001 Keith M Wesolowski + * Copyright (C) 2001 Paul Mundt + * Copyright (C) 2003 Guido Guenther <agx@sigxcpu.org> + */ + +#include <linux/compiler.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/sched/signal.h> +#include <linux/notifier.h> +#include <linux/delay.h> +#include <linux/rtc/ds1685.h> +#include <linux/interrupt.h> +#include <linux/pm.h> + +#include <asm/addrspace.h> +#include <asm/irq.h> +#include <asm/reboot.h> +#include <asm/wbflush.h> +#include <asm/ip32/mace.h> +#include <asm/ip32/crime.h> +#include <asm/ip32/ip32_ints.h> + +#define POWERDOWN_TIMEOUT 120 +/* + * Blink frequency during reboot grace period and when panicked. + */ +#define POWERDOWN_FREQ (HZ / 4) +#define PANIC_FREQ (HZ / 8) + +extern struct platform_device ip32_rtc_device; + +static struct timer_list power_timer, blink_timer; +static unsigned long blink_timer_timeout; +static int has_panicked, shutting_down; + +static __noreturn void ip32_poweroff(void *data) +{ + void (*poweroff_func)(struct platform_device *) = + symbol_get(ds1685_rtc_poweroff); + +#ifdef CONFIG_MODULES + /* If the first __symbol_get failed, our module wasn't loaded. */ + if (!poweroff_func) { + request_module("rtc-ds1685"); + poweroff_func = symbol_get(ds1685_rtc_poweroff); + } +#endif + + if (!poweroff_func) + pr_emerg("RTC not available for power-off. Spinning forever ...\n"); + else { + (*poweroff_func)((struct platform_device *)data); + symbol_put(ds1685_rtc_poweroff); + } + + unreachable(); +} + +static void ip32_machine_restart(char *cmd) __noreturn; +static void ip32_machine_restart(char *cmd) +{ + msleep(20); + crime->control = CRIME_CONTROL_HARD_RESET; + unreachable(); +} + +static void blink_timeout(struct timer_list *unused) +{ + unsigned long led = mace->perif.ctrl.misc ^ MACEISA_LED_RED; + mace->perif.ctrl.misc = led; + mod_timer(&blink_timer, jiffies + blink_timer_timeout); +} + +static void ip32_machine_halt(void) +{ + ip32_poweroff(&ip32_rtc_device); +} + +static void power_timeout(struct timer_list *unused) +{ + ip32_poweroff(&ip32_rtc_device); +} + +void ip32_prepare_poweroff(void) +{ + if (has_panicked) + return; + + if (shutting_down || kill_cad_pid(SIGINT, 1)) { + /* No init process or button pressed twice. */ + ip32_poweroff(&ip32_rtc_device); + } + + shutting_down = 1; + blink_timer_timeout = POWERDOWN_FREQ; + blink_timeout(&blink_timer); + + timer_setup(&power_timer, power_timeout, 0); + power_timer.expires = jiffies + POWERDOWN_TIMEOUT * HZ; + add_timer(&power_timer); +} + +static int panic_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + unsigned long led; + + if (has_panicked) + return NOTIFY_DONE; + has_panicked = 1; + + /* turn off the green LED */ + led = mace->perif.ctrl.misc | MACEISA_LED_GREEN; + mace->perif.ctrl.misc = led; + + blink_timer_timeout = PANIC_FREQ; + blink_timeout(&blink_timer); + + return NOTIFY_DONE; +} + +static struct notifier_block panic_block = { + .notifier_call = panic_event, +}; + +static __init int ip32_reboot_setup(void) +{ + /* turn on the green led only */ + unsigned long led = mace->perif.ctrl.misc; + led |= MACEISA_LED_RED; + led &= ~MACEISA_LED_GREEN; + mace->perif.ctrl.misc = led; + + _machine_restart = ip32_machine_restart; + _machine_halt = ip32_machine_halt; + pm_power_off = ip32_machine_halt; + + timer_setup(&blink_timer, blink_timeout, 0); + atomic_notifier_chain_register(&panic_notifier_list, &panic_block); + + return 0; +} + +subsys_initcall(ip32_reboot_setup); diff --git a/arch/mips/sgi-ip32/ip32-setup.c b/arch/mips/sgi-ip32/ip32-setup.c new file mode 100644 index 000000000..8019dae17 --- /dev/null +++ b/arch/mips/sgi-ip32/ip32-setup.c @@ -0,0 +1,100 @@ +/* + * IP32 basic setup + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2000 Harald Koerfgen + * Copyright (C) 2002, 2003, 2005 Ilya A. Volynets + * Copyright (C) 2006 Ralf Baechle <ralf@linux-mips.org> + */ +#include <linux/console.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/param.h> +#include <linux/sched.h> + +#include <asm/bootinfo.h> +#include <asm/mipsregs.h> +#include <asm/mmu_context.h> +#include <asm/sgialib.h> +#include <asm/time.h> +#include <asm/traps.h> +#include <asm/io.h> +#include <asm/ip32/crime.h> +#include <asm/ip32/mace.h> +#include <asm/ip32/ip32_ints.h> + +extern void ip32_be_init(void); +extern void crime_init(void); + +#ifdef CONFIG_SGI_O2MACE_ETH +/* + * This is taken care of in here 'cause they say using Arc later on is + * problematic + */ +extern char o2meth_eaddr[8]; +static inline unsigned char str2hexnum(unsigned char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + return 0; /* foo */ +} + +static inline void str2eaddr(unsigned char *ea, unsigned char *str) +{ + int i; + + for (i = 0; i < 6; i++) { + unsigned char num; + + if(*str == ':') + str++; + num = str2hexnum(*str++) << 4; + num |= (str2hexnum(*str++)); + ea[i] = num; + } +} +#endif + +/* An arbitrary time; this can be decreased if reliability looks good */ +#define WAIT_MS 10 + +void __init plat_time_init(void) +{ + printk(KERN_INFO "Calibrating system timer... "); + write_c0_count(0); + crime->timer = 0; + while (crime->timer < CRIME_MASTER_FREQ * WAIT_MS / 1000) ; + mips_hpt_frequency = read_c0_count() * 1000 / WAIT_MS; + printk("%d MHz CPU detected\n", mips_hpt_frequency * 2 / 1000000); +} + +void __init plat_mem_setup(void) +{ + board_be_init = ip32_be_init; + +#ifdef CONFIG_SGI_O2MACE_ETH + { + char *mac = ArcGetEnvironmentVariable("eaddr"); + str2eaddr(o2meth_eaddr, mac); + } +#endif + +#if defined(CONFIG_SERIAL_CORE_CONSOLE) + { + char* con = ArcGetEnvironmentVariable("console"); + if (con && *con == 'd') { + static char options[8] __initdata; + char *baud = ArcGetEnvironmentVariable("dbaud"); + if (baud) + strcpy(options, baud); + add_preferred_console("ttyS", *(con + 1) == '2' ? 1 : 0, + baud ? options : NULL); + } + } +#endif +} |