diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-08-05 17:04:01 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-08-05 17:04:01 -0300 |
commit | 57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch) | |
tree | 5e910f0e82173f4ef4f51111366a3f1299037a7b /arch/mips/sgi-ip27 |
Initial import
Diffstat (limited to 'arch/mips/sgi-ip27')
-rw-r--r-- | arch/mips/sgi-ip27/Kconfig | 46 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/Makefile | 11 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/Platform | 19 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/TODO | 19 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-berr.c | 93 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-console.c | 39 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-hubio.c | 185 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-init.c | 221 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-irq-pci.c | 265 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-irq.c | 208 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-klconfig.c | 134 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-klnuma.c | 131 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-memory.c | 484 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-nmi.c | 244 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-reset.c | 81 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-smp.c | 238 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-timer.c | 242 | ||||
-rw-r--r-- | arch/mips/sgi-ip27/ip27-xtalk.c | 135 |
18 files changed, 2795 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip27/Kconfig b/arch/mips/sgi-ip27/Kconfig new file mode 100644 index 000000000..4d8705a65 --- /dev/null +++ b/arch/mips/sgi-ip27/Kconfig @@ -0,0 +1,46 @@ +choice + prompt "Node addressing mode" + depends on SGI_IP27 + default SGI_SN_M_MODE + +config SGI_SN_M_MODE + bool "IP27 M-Mode" + help + The nodes of Origin, Onyx, Fuel and Tezro systems can be configured + in either N-Modes which allows for more nodes or M-Mode which allows + for more memory. Your hardware is almost certainly running in + M-Mode, so choose M-mode here. + +config SGI_SN_N_MODE + bool "IP27 N-Mode" + help + The nodes of Origin, Onyx, Fuel and Tezro systems can be configured + in either N-Modes which allows for more nodes or M-Mode which allows + for more memory. Your hardware is almost certainly running in + M-Mode, so choose M-mode here. + +endchoice + +config MAPPED_KERNEL + bool "Mapped kernel support" + depends on SGI_IP27 + help + Change the way a Linux kernel is loaded into memory on a MIPS64 + machine. This is required in order to support text replication on + NUMA. If you need to understand it, read the source code. + +config REPLICATE_KTEXT + bool "Kernel text replication support" + depends on SGI_IP27 + select MAPPED_KERNEL + help + Say Y here to enable replicating the kernel text across multiple + nodes in a NUMA cluster. This trades memory for speed. + +config REPLICATE_EXHANDLERS + bool "Exception handler replication support" + depends on SGI_IP27 + help + Say Y here to enable replicating the kernel exception handlers + across multiple nodes in a NUMA cluster. This trades memory for + speed. diff --git a/arch/mips/sgi-ip27/Makefile b/arch/mips/sgi-ip27/Makefile new file mode 100644 index 000000000..da8f6816d --- /dev/null +++ b/arch/mips/sgi-ip27/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the IP27 specific kernel interface routines under Linux. +# + +obj-y := ip27-berr.o ip27-irq.o ip27-init.o ip27-klconfig.o ip27-klnuma.o \ + ip27-memory.o ip27-nmi.o ip27-reset.o ip27-timer.o ip27-hubio.o \ + ip27-xtalk.o + +obj-$(CONFIG_EARLY_PRINTK) += ip27-console.o +obj-$(CONFIG_PCI) += ip27-irq-pci.o +obj-$(CONFIG_SMP) += ip27-smp.o diff --git a/arch/mips/sgi-ip27/Platform b/arch/mips/sgi-ip27/Platform new file mode 100644 index 000000000..1fb9c2ea7 --- /dev/null +++ b/arch/mips/sgi-ip27/Platform @@ -0,0 +1,19 @@ +# +# SGI-IP27 (Origin200/2000) +# +# Set the load address to >= 0xc000000000300000 if you want to leave space for +# symmon, 0xc00000000001c000 for production kernels. Note that the value must +# be 16kb aligned or the handling of the current variable will break. +# +ifdef CONFIG_SGI_IP27 +platform-$(CONFIG_SGI_IP27) += sgi-ip27/ +cflags-$(CONFIG_SGI_IP27) += -I$(srctree)/arch/mips/include/asm/mach-ip27 +ifdef CONFIG_MAPPED_KERNEL +load-$(CONFIG_SGI_IP27) += 0xc00000004001c000 +OBJCOPYFLAGS := --change-addresses=0x3fffffff80000000 +dataoffset-$(CONFIG_SGI_IP27) += 0x01000000 +else +load-$(CONFIG_SGI_IP27) += 0xa80000000001c000 +OBJCOPYFLAGS := --change-addresses=0x57ffffff80000000 +endif +endif diff --git a/arch/mips/sgi-ip27/TODO b/arch/mips/sgi-ip27/TODO new file mode 100644 index 000000000..160857ff1 --- /dev/null +++ b/arch/mips/sgi-ip27/TODO @@ -0,0 +1,19 @@ +1. Need to figure out why PCI writes to the IOC3 hang, and if it is okay +not to write to the IOC3 ever. +2. Need to figure out RRB allocation in bridge_startup(). +3. Need to figure out why address swaizzling is needed in inw/outw for +Qlogic scsi controllers. +4. Need to integrate ip27-klconfig.c:find_lboard and +ip27-init.c:find_lbaord_real. DONE +5. Is it okay to set calias space on all nodes as 0, instead of 8k as +in irix? +6. Investigate why things do not work without the setup_test() call +being invoked on all nodes in ip27-memory.c. +8. Too many do_page_faults invoked - investigate. +9. start_thread must turn off UX64 ... and define tlb_refill_debug. +10. Need a bad pmd table, bad pte table. __bad_pmd_table/__bad_pagetable +does not agree with pgd_bad/pmd_bad. +11. All intrs (ip27_do_irq handlers) are targeted at cpu A on the node. +This might need to change later. Only the timer intr is set up to be +received on both Cpu A and B. (ip27_do_irq()/bridge_startup()) +13. Cache flushing (specially the SMP version) has to be investigated. diff --git a/arch/mips/sgi-ip27/ip27-berr.c b/arch/mips/sgi-ip27/ip27-berr.c new file mode 100644 index 000000000..692778da9 --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-berr.c @@ -0,0 +1,93 @@ +/* + * 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/module.h> +#include <linux/signal.h> /* for SIGBUS */ +#include <linux/sched.h> /* schow_regs(), force_sig() */ + +#include <asm/module.h> +#include <asm/sn/addrs.h> +#include <asm/sn/arch.h> +#include <asm/sn/sn0/hub.h> +#include <asm/tlbdebug.h> +#include <asm/traps.h> +#include <asm/uaccess.h> + +static void dump_hub_information(unsigned long errst0, unsigned long errst1) +{ + static char *err_type[2][8] = { + { NULL, "Uncached Partial Read PRERR", "DERR", "Read Timeout", + NULL, NULL, NULL, NULL }, + { "WERR", "Uncached Partial Write", "PWERR", "Write Timeout", + NULL, NULL, NULL, NULL } + }; + int wrb = errst1 & PI_ERR_ST1_WRBRRB_MASK; + + if (!(errst0 & PI_ERR_ST0_VALID_MASK)) { + printk("Hub does not contain valid error information\n"); + return; + } + + + printk("Hub has valid error information:\n"); + if (errst0 & PI_ERR_ST0_OVERRUN_MASK) + printk("Overrun is set. Error stack may contain additional " + "information.\n"); + printk("Hub error address is %08lx\n", + (errst0 & PI_ERR_ST0_ADDR_MASK) >> (PI_ERR_ST0_ADDR_SHFT - 3)); + printk("Incoming message command 0x%lx\n", + (errst0 & PI_ERR_ST0_CMD_MASK) >> PI_ERR_ST0_CMD_SHFT); + printk("Supplemental field of incoming message is 0x%lx\n", + (errst0 & PI_ERR_ST0_SUPPL_MASK) >> PI_ERR_ST0_SUPPL_SHFT); + printk("T5 Rn (for RRB only) is 0x%lx\n", + (errst0 & PI_ERR_ST0_REQNUM_MASK) >> PI_ERR_ST0_REQNUM_SHFT); + printk("Error type is %s\n", err_type[wrb] + [(errst0 & PI_ERR_ST0_TYPE_MASK) >> PI_ERR_ST0_TYPE_SHFT] + ? : "invalid"); +} + +int ip27_be_handler(struct pt_regs *regs, int is_fixup) +{ + unsigned long errst0, errst1; + int data = regs->cp0_cause & 4; + int cpu = LOCAL_HUB_L(PI_CPU_NUM); + + if (is_fixup) + return MIPS_BE_FIXUP; + + printk("Slice %c got %cbe at 0x%lx\n", 'A' + cpu, data ? 'd' : 'i', + regs->cp0_epc); + printk("Hub information:\n"); + printk("ERR_INT_PEND = 0x%06llx\n", LOCAL_HUB_L(PI_ERR_INT_PEND)); + errst0 = LOCAL_HUB_L(cpu ? PI_ERR_STATUS0_B : PI_ERR_STATUS0_A); + errst1 = LOCAL_HUB_L(cpu ? PI_ERR_STATUS1_B : PI_ERR_STATUS1_A); + dump_hub_information(errst0, errst1); + show_regs(regs); + dump_tlb_all(); + while(1); + force_sig(SIGBUS, current); +} + +void __init ip27_be_init(void) +{ + /* XXX Initialize all the Hub & Bridge error handling here. */ + int cpu = LOCAL_HUB_L(PI_CPU_NUM); + int cpuoff = cpu << 8; + + board_be_handler = ip27_be_handler; + + LOCAL_HUB_S(PI_ERR_INT_PEND, + cpu ? PI_ERR_CLEAR_ALL_B : PI_ERR_CLEAR_ALL_A); + LOCAL_HUB_S(PI_ERR_INT_MASK_A + cpuoff, 0); + LOCAL_HUB_S(PI_ERR_STACK_ADDR_A + cpuoff, 0); + LOCAL_HUB_S(PI_ERR_STACK_SIZE, 0); /* Disable error stack */ + LOCAL_HUB_S(PI_SYSAD_ERRCHK_EN, PI_SYSAD_CHECK_ALL); +} diff --git a/arch/mips/sgi-ip27/ip27-console.c b/arch/mips/sgi-ip27/ip27-console.c new file mode 100644 index 000000000..45fdfbcbd --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-console.c @@ -0,0 +1,39 @@ +/* + * 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, 2002 Ralf Baechle + */ + +#include <asm/page.h> +#include <asm/sn/addrs.h> +#include <asm/sn/sn0/hub.h> +#include <asm/sn/klconfig.h> +#include <asm/sn/ioc3.h> +#include <asm/sn/sn_private.h> + +#include <linux/serial.h> +#include <linux/serial_core.h> + +#define IOC3_CLK (22000000 / 3) +#define IOC3_FLAGS (0) + +static inline struct ioc3_uartregs *console_uart(void) +{ + struct ioc3 *ioc3; + nasid_t nasid; + + nasid = (master_nasid == INVALID_NASID) ? get_nasid() : master_nasid; + ioc3 = (struct ioc3 *)KL_CONFIG_CH_CONS_INFO(nasid)->memory_base; + + return &ioc3->sregs.uarta; +} + +void prom_putchar(char c) +{ + struct ioc3_uartregs *uart = console_uart(); + + while ((uart->iu_lsr & 0x20) == 0); + uart->iu_thr = c; +} diff --git a/arch/mips/sgi-ip27/ip27-hubio.c b/arch/mips/sgi-ip27/ip27-hubio.c new file mode 100644 index 000000000..328ceb3c8 --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-hubio.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc. + * Copyright (C) 2004 Christoph Hellwig. + * Released under GPL v2. + * + * Support functions for the HUB ASIC - mostly PIO mapping related. + */ + +#include <linux/bitops.h> +#include <linux/string.h> +#include <linux/mmzone.h> +#include <asm/sn/addrs.h> +#include <asm/sn/arch.h> +#include <asm/sn/hub.h> + + +static int force_fire_and_forget = 1; + +/** + * hub_pio_map - establish a HUB PIO mapping + * + * @hub: hub to perform PIO mapping on + * @widget: widget ID to perform PIO mapping for + * @xtalk_addr: xtalk_address that needs to be mapped + * @size: size of the PIO mapping + * + **/ +unsigned long hub_pio_map(cnodeid_t cnode, xwidgetnum_t widget, + unsigned long xtalk_addr, size_t size) +{ + nasid_t nasid = COMPACT_TO_NASID_NODEID(cnode); + unsigned i; + + /* use small-window mapping if possible */ + if ((xtalk_addr % SWIN_SIZE) + size <= SWIN_SIZE) + return NODE_SWIN_BASE(nasid, widget) + (xtalk_addr % SWIN_SIZE); + + if ((xtalk_addr % BWIN_SIZE) + size > BWIN_SIZE) { + printk(KERN_WARNING "PIO mapping at hub %d widget %d addr 0x%lx" + " too big (%ld)\n", + nasid, widget, xtalk_addr, size); + return 0; + } + + xtalk_addr &= ~(BWIN_SIZE-1); + for (i = 0; i < HUB_NUM_BIG_WINDOW; i++) { + if (test_and_set_bit(i, hub_data(cnode)->h_bigwin_used)) + continue; + + /* + * The code below does a PIO write to setup an ITTE entry. + * + * We need to prevent other CPUs from seeing our updated + * memory shadow of the ITTE (in the piomap) until the ITTE + * entry is actually set up; otherwise, another CPU might + * attempt a PIO prematurely. + * + * Also, the only way we can know that an entry has been + * received by the hub and can be used by future PIO reads/ + * writes is by reading back the ITTE entry after writing it. + * + * For these two reasons, we PIO read back the ITTE entry + * after we write it. + */ + IIO_ITTE_PUT(nasid, i, HUB_PIO_MAP_TO_MEM, widget, xtalk_addr); + (void) HUB_L(IIO_ITTE_GET(nasid, i)); + + return NODE_BWIN_BASE(nasid, widget) + (xtalk_addr % BWIN_SIZE); + } + + printk(KERN_WARNING "unable to establish PIO mapping for at" + " hub %d widget %d addr 0x%lx\n", + nasid, widget, xtalk_addr); + return 0; +} + + +/* + * hub_setup_prb(nasid, prbnum, credits, conveyor) + * + * Put a PRB into fire-and-forget mode if conveyor isn't set. Otherwise, + * put it into conveyor belt mode with the specified number of credits. + */ +static void hub_setup_prb(nasid_t nasid, int prbnum, int credits) +{ + iprb_t prb; + int prb_offset; + + /* + * Get the current register value. + */ + prb_offset = IIO_IOPRB(prbnum); + prb.iprb_regval = REMOTE_HUB_L(nasid, prb_offset); + + /* + * Clear out some fields. + */ + prb.iprb_ovflow = 1; + prb.iprb_bnakctr = 0; + prb.iprb_anakctr = 0; + + /* + * Enable or disable fire-and-forget mode. + */ + prb.iprb_ff = force_fire_and_forget ? 1 : 0; + + /* + * Set the appropriate number of PIO cresits for the widget. + */ + prb.iprb_xtalkctr = credits; + + /* + * Store the new value to the register. + */ + REMOTE_HUB_S(nasid, prb_offset, prb.iprb_regval); +} + +/** + * hub_set_piomode - set pio mode for a given hub + * + * @nasid: physical node ID for the hub in question + * + * Put the hub into either "PIO conveyor belt" mode or "fire-and-forget" mode. + * To do this, we have to make absolutely sure that no PIOs are in progress + * so we turn off access to all widgets for the duration of the function. + * + * XXX - This code should really check what kind of widget we're talking + * to. Bridges can only handle three requests, but XG will do more. + * How many can crossbow handle to widget 0? We're assuming 1. + * + * XXX - There is a bug in the crossbow that link reset PIOs do not + * return write responses. The easiest solution to this problem is to + * leave widget 0 (xbow) in fire-and-forget mode at all times. This + * only affects pio's to xbow registers, which should be rare. + **/ +static void hub_set_piomode(nasid_t nasid) +{ + hubreg_t ii_iowa; + hubii_wcr_t ii_wcr; + unsigned i; + + ii_iowa = REMOTE_HUB_L(nasid, IIO_OUTWIDGET_ACCESS); + REMOTE_HUB_S(nasid, IIO_OUTWIDGET_ACCESS, 0); + + ii_wcr.wcr_reg_value = REMOTE_HUB_L(nasid, IIO_WCR); + + if (ii_wcr.iwcr_dir_con) { + /* + * Assume a bridge here. + */ + hub_setup_prb(nasid, 0, 3); + } else { + /* + * Assume a crossbow here. + */ + hub_setup_prb(nasid, 0, 1); + } + + /* + * XXX - Here's where we should take the widget type into + * when account assigning credits. + */ + for (i = HUB_WIDGET_ID_MIN; i <= HUB_WIDGET_ID_MAX; i++) + hub_setup_prb(nasid, i, 3); + + REMOTE_HUB_S(nasid, IIO_OUTWIDGET_ACCESS, ii_iowa); +} + +/* + * hub_pio_init - PIO-related hub initialization + * + * @hub: hubinfo structure for our hub + */ +void hub_pio_init(cnodeid_t cnode) +{ + nasid_t nasid = COMPACT_TO_NASID_NODEID(cnode); + unsigned i; + + /* initialize big window piomaps for this hub */ + bitmap_zero(hub_data(cnode)->h_bigwin_used, HUB_NUM_BIG_WINDOW); + for (i = 0; i < HUB_NUM_BIG_WINDOW; i++) + IIO_ITTE_DISABLE(nasid, i); + + hub_set_piomode(nasid); +} diff --git a/arch/mips/sgi-ip27/ip27-init.c b/arch/mips/sgi-ip27/ip27-init.c new file mode 100644 index 000000000..570098bfd --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-init.c @@ -0,0 +1,221 @@ +/* + * 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 - 2001 by Kanoj Sarcar (kanoj@sgi.com) + * Copyright (C) 2000 - 2001 by Silicon Graphics, Inc. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/cpumask.h> +#include <asm/cpu.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/time.h> +#include <asm/sn/types.h> +#include <asm/sn/sn0/addrs.h> +#include <asm/sn/sn0/hubni.h> +#include <asm/sn/sn0/hubio.h> +#include <asm/sn/klconfig.h> +#include <asm/sn/ioc3.h> +#include <asm/mipsregs.h> +#include <asm/sn/gda.h> +#include <asm/sn/hub.h> +#include <asm/sn/intr.h> +#include <asm/current.h> +#include <asm/processor.h> +#include <asm/mmu_context.h> +#include <asm/thread_info.h> +#include <asm/sn/launch.h> +#include <asm/sn/sn_private.h> +#include <asm/sn/sn0/ip27.h> +#include <asm/sn/mapped_kernel.h> + +#define CPU_NONE (cpuid_t)-1 + +static DECLARE_BITMAP(hub_init_mask, MAX_COMPACT_NODES); +nasid_t master_nasid = INVALID_NASID; + +cnodeid_t nasid_to_compact_node[MAX_NASIDS]; +nasid_t compact_to_nasid_node[MAX_COMPACT_NODES]; +cnodeid_t cpuid_to_compact_node[MAXCPUS]; + +EXPORT_SYMBOL(nasid_to_compact_node); + +struct cpuinfo_ip27 sn_cpu_info[NR_CPUS]; +EXPORT_SYMBOL_GPL(sn_cpu_info); + +extern void pcibr_setup(cnodeid_t); + +extern void xtalk_probe_node(cnodeid_t nid); + +static void per_hub_init(cnodeid_t cnode) +{ + struct hub_data *hub = hub_data(cnode); + nasid_t nasid = COMPACT_TO_NASID_NODEID(cnode); + int i; + + cpumask_set_cpu(smp_processor_id(), &hub->h_cpus); + + if (test_and_set_bit(cnode, hub_init_mask)) + return; + /* + * Set CRB timeout at 5ms, (< PI timeout of 10ms) + */ + REMOTE_HUB_S(nasid, IIO_ICTP, 0x800); + REMOTE_HUB_S(nasid, IIO_ICTO, 0xff); + + hub_rtc_init(cnode); + xtalk_probe_node(cnode); + +#ifdef CONFIG_REPLICATE_EXHANDLERS + /* + * If this is not a headless node initialization, + * copy over the caliased exception handlers. + */ + if (get_compact_nodeid() == cnode) { + extern char except_vec2_generic, except_vec3_generic; + extern void build_tlb_refill_handler(void); + + memcpy((void *)(CKSEG0 + 0x100), &except_vec2_generic, 0x80); + memcpy((void *)(CKSEG0 + 0x180), &except_vec3_generic, 0x80); + build_tlb_refill_handler(); + memcpy((void *)(CKSEG0 + 0x100), (void *) CKSEG0, 0x80); + memcpy((void *)(CKSEG0 + 0x180), &except_vec3_generic, 0x100); + __flush_cache_all(); + } +#endif + + /* + * Some interrupts are reserved by hardware or by software convention. + * Mark these as reserved right away so they won't be used accidentally + * later. + */ + for (i = 0; i <= BASE_PCI_IRQ; i++) { + __set_bit(i, hub->irq_alloc_mask); + LOCAL_HUB_CLR_INTR(INT_PEND0_BASELVL + i); + } + + __set_bit(IP_PEND0_6_63, hub->irq_alloc_mask); + LOCAL_HUB_S(PI_INT_PEND_MOD, IP_PEND0_6_63); + + for (i = NI_BRDCAST_ERR_A; i <= MSC_PANIC_INTR; i++) { + __set_bit(i, hub->irq_alloc_mask); + LOCAL_HUB_CLR_INTR(INT_PEND1_BASELVL + i); + } +} + +void per_cpu_init(void) +{ + int cpu = smp_processor_id(); + int slice = LOCAL_HUB_L(PI_CPU_NUM); + cnodeid_t cnode = get_compact_nodeid(); + struct hub_data *hub = hub_data(cnode); + struct slice_data *si = hub->slice + slice; + int i; + + if (test_and_set_bit(slice, &hub->slice_map)) + return; + + clear_c0_status(ST0_IM); + + per_hub_init(cnode); + + for (i = 0; i < LEVELS_PER_SLICE; i++) + si->level_to_irq[i] = -1; + + /* + * We use this so we can find the local hub's data as fast as only + * possible. + */ + cpu_data[cpu].data = si; + + cpu_time_init(); + install_ipi(); + + /* Install our NMI handler if symmon hasn't installed one. */ + install_cpu_nmi_handler(cputoslice(cpu)); + + set_c0_status(SRB_DEV0 | SRB_DEV1); +} + +/* + * get_nasid() returns the physical node id number of the caller. + */ +nasid_t +get_nasid(void) +{ + return (nasid_t)((LOCAL_HUB_L(NI_STATUS_REV_ID) & NSRI_NODEID_MASK) + >> NSRI_NODEID_SHFT); +} + +/* + * Map the physical node id to a virtual node id (virtual node ids are contiguous). + */ +cnodeid_t get_compact_nodeid(void) +{ + return NASID_TO_COMPACT_NODEID(get_nasid()); +} + +static inline void ioc3_eth_init(void) +{ + struct ioc3 *ioc3; + nasid_t nid; + + nid = get_nasid(); + ioc3 = (struct ioc3 *) KL_CONFIG_CH_CONS_INFO(nid)->memory_base; + + ioc3->eier = 0; +} + +extern void ip27_reboot_setup(void); + +void __init plat_mem_setup(void) +{ + hubreg_t p, e, n_mode; + nasid_t nid; + + ip27_reboot_setup(); + + /* + * hub_rtc init and cpu clock intr enabled for later calibrate_delay. + */ + nid = get_nasid(); + printk("IP27: Running on node %d.\n", nid); + + p = LOCAL_HUB_L(PI_CPU_PRESENT_A) & 1; + e = LOCAL_HUB_L(PI_CPU_ENABLE_A) & 1; + printk("Node %d has %s primary CPU%s.\n", nid, + p ? "a" : "no", + e ? ", CPU is running" : ""); + + p = LOCAL_HUB_L(PI_CPU_PRESENT_B) & 1; + e = LOCAL_HUB_L(PI_CPU_ENABLE_B) & 1; + printk("Node %d has %s secondary CPU%s.\n", nid, + p ? "a" : "no", + e ? ", CPU is running" : ""); + + /* + * Try to catch kernel missconfigurations and give user an + * indication what option to select. + */ + n_mode = LOCAL_HUB_L(NI_STATUS_REV_ID) & NSRI_MORENODES_MASK; + printk("Machine is in %c mode.\n", n_mode ? 'N' : 'M'); +#ifdef CONFIG_SGI_SN_N_MODE + if (!n_mode) + panic("Kernel compiled for M mode."); +#else + if (n_mode) + panic("Kernel compiled for N mode."); +#endif + + ioc3_eth_init(); + per_cpu_init(); + + set_io_port_base(IO_BASE); +} diff --git a/arch/mips/sgi-ip27/ip27-irq-pci.c b/arch/mips/sgi-ip27/ip27-irq-pci.c new file mode 100644 index 000000000..2a1c40784 --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-irq-pci.c @@ -0,0 +1,265 @@ +/* + * ip27-irq.c: Highlevel interrupt handling for IP27 architecture. + * + * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 1999 - 2001 Kanoj Sarcar + */ + +#undef DEBUG + +#include <linux/irq.h> +#include <linux/errno.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/smp.h> +#include <linux/random.h> +#include <linux/kernel.h> +#include <linux/kernel_stat.h> +#include <linux/delay.h> +#include <linux/bitops.h> + +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <asm/mipsregs.h> + +#include <asm/processor.h> +#include <asm/pci/bridge.h> +#include <asm/sn/addrs.h> +#include <asm/sn/agent.h> +#include <asm/sn/arch.h> +#include <asm/sn/hub.h> +#include <asm/sn/intr.h> + +/* + * Linux has a controller-independent x86 interrupt architecture. + * every controller has a 'controller-template', that is used + * by the main code to do the right thing. Each driver-visible + * interrupt source is transparently wired to the appropriate + * controller. Thus drivers need not be aware of the + * interrupt-controller. + * + * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, + * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. + * (IO-APICs assumed to be messaging to Pentium local-APICs) + * + * the code is designed to be easily extended with new/different + * interrupt controllers, without having to do assembly magic. + */ + +extern struct bridge_controller *irq_to_bridge[]; +extern int irq_to_slot[]; + +/* + * use these macros to get the encoded nasid and widget id + * from the irq value + */ +#define IRQ_TO_BRIDGE(i) irq_to_bridge[(i)] +#define SLOT_FROM_PCI_IRQ(i) irq_to_slot[i] + +static inline int alloc_level(int cpu, int irq) +{ + struct hub_data *hub = hub_data(cpu_to_node(cpu)); + struct slice_data *si = cpu_data[cpu].data; + int level; + + level = find_first_zero_bit(hub->irq_alloc_mask, LEVELS_PER_SLICE); + if (level >= LEVELS_PER_SLICE) + panic("Cpu %d flooded with devices", cpu); + + __set_bit(level, hub->irq_alloc_mask); + si->level_to_irq[level] = irq; + + return level; +} + +static inline int find_level(cpuid_t *cpunum, int irq) +{ + int cpu, i; + + for_each_online_cpu(cpu) { + struct slice_data *si = cpu_data[cpu].data; + + for (i = BASE_PCI_IRQ; i < LEVELS_PER_SLICE; i++) + if (si->level_to_irq[i] == irq) { + *cpunum = cpu; + + return i; + } + } + + panic("Could not identify cpu/level for irq %d", irq); +} + +static int intr_connect_level(int cpu, int bit) +{ + nasid_t nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu)); + struct slice_data *si = cpu_data[cpu].data; + + set_bit(bit, si->irq_enable_mask); + + if (!cputoslice(cpu)) { + REMOTE_HUB_S(nasid, PI_INT_MASK0_A, si->irq_enable_mask[0]); + REMOTE_HUB_S(nasid, PI_INT_MASK1_A, si->irq_enable_mask[1]); + } else { + REMOTE_HUB_S(nasid, PI_INT_MASK0_B, si->irq_enable_mask[0]); + REMOTE_HUB_S(nasid, PI_INT_MASK1_B, si->irq_enable_mask[1]); + } + + return 0; +} + +static int intr_disconnect_level(int cpu, int bit) +{ + nasid_t nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu)); + struct slice_data *si = cpu_data[cpu].data; + + clear_bit(bit, si->irq_enable_mask); + + if (!cputoslice(cpu)) { + REMOTE_HUB_S(nasid, PI_INT_MASK0_A, si->irq_enable_mask[0]); + REMOTE_HUB_S(nasid, PI_INT_MASK1_A, si->irq_enable_mask[1]); + } else { + REMOTE_HUB_S(nasid, PI_INT_MASK0_B, si->irq_enable_mask[0]); + REMOTE_HUB_S(nasid, PI_INT_MASK1_B, si->irq_enable_mask[1]); + } + + return 0; +} + +/* Startup one of the (PCI ...) IRQs routes over a bridge. */ +static unsigned int startup_bridge_irq(struct irq_data *d) +{ + struct bridge_controller *bc; + bridgereg_t device; + bridge_t *bridge; + int pin, swlevel; + cpuid_t cpu; + + pin = SLOT_FROM_PCI_IRQ(d->irq); + bc = IRQ_TO_BRIDGE(d->irq); + bridge = bc->base; + + pr_debug("bridge_startup(): irq= 0x%x pin=%d\n", d->irq, pin); + /* + * "map" irq to a swlevel greater than 6 since the first 6 bits + * of INT_PEND0 are taken + */ + swlevel = find_level(&cpu, d->irq); + bridge->b_int_addr[pin].addr = (0x20000 | swlevel | (bc->nasid << 8)); + bridge->b_int_enable |= (1 << pin); + bridge->b_int_enable |= 0x7ffffe00; /* more stuff in int_enable */ + + /* + * Enable sending of an interrupt clear packt to the hub on a high to + * low transition of the interrupt pin. + * + * IRIX sets additional bits in the address which are documented as + * reserved in the bridge docs. + */ + bridge->b_int_mode |= (1UL << pin); + + /* + * We assume the bridge to have a 1:1 mapping between devices + * (slots) and intr pins. + */ + device = bridge->b_int_device; + device &= ~(7 << (pin*3)); + device |= (pin << (pin*3)); + bridge->b_int_device = device; + + bridge->b_wid_tflush; + + intr_connect_level(cpu, swlevel); + + return 0; /* Never anything pending. */ +} + +/* Shutdown one of the (PCI ...) IRQs routes over a bridge. */ +static void shutdown_bridge_irq(struct irq_data *d) +{ + struct bridge_controller *bc = IRQ_TO_BRIDGE(d->irq); + bridge_t *bridge = bc->base; + int pin, swlevel; + cpuid_t cpu; + + pr_debug("bridge_shutdown: irq 0x%x\n", d->irq); + pin = SLOT_FROM_PCI_IRQ(d->irq); + + /* + * map irq to a swlevel greater than 6 since the first 6 bits + * of INT_PEND0 are taken + */ + swlevel = find_level(&cpu, d->irq); + intr_disconnect_level(cpu, swlevel); + + bridge->b_int_enable &= ~(1 << pin); + bridge->b_wid_tflush; +} + +static inline void enable_bridge_irq(struct irq_data *d) +{ + cpuid_t cpu; + int swlevel; + + swlevel = find_level(&cpu, d->irq); /* Criminal offence */ + intr_connect_level(cpu, swlevel); +} + +static inline void disable_bridge_irq(struct irq_data *d) +{ + cpuid_t cpu; + int swlevel; + + swlevel = find_level(&cpu, d->irq); /* Criminal offence */ + intr_disconnect_level(cpu, swlevel); +} + +static struct irq_chip bridge_irq_type = { + .name = "bridge", + .irq_startup = startup_bridge_irq, + .irq_shutdown = shutdown_bridge_irq, + .irq_mask = disable_bridge_irq, + .irq_unmask = enable_bridge_irq, +}; + +void register_bridge_irq(unsigned int irq) +{ + irq_set_chip_and_handler(irq, &bridge_irq_type, handle_level_irq); +} + +int request_bridge_irq(struct bridge_controller *bc) +{ + int irq = allocate_irqno(); + int swlevel, cpu; + nasid_t nasid; + + if (irq < 0) + return irq; + + /* + * "map" irq to a swlevel greater than 6 since the first 6 bits + * of INT_PEND0 are taken + */ + cpu = bc->irq_cpu; + swlevel = alloc_level(cpu, irq); + if (unlikely(swlevel < 0)) { + free_irqno(irq); + + return -EAGAIN; + } + + /* Make sure it's not already pending when we connect it. */ + nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu)); + REMOTE_HUB_CLR_INTR(nasid, swlevel); + + intr_connect_level(cpu, swlevel); + + register_bridge_irq(irq); + + return irq; +} diff --git a/arch/mips/sgi-ip27/ip27-irq.c b/arch/mips/sgi-ip27/ip27-irq.c new file mode 100644 index 000000000..3fbaef97a --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-irq.c @@ -0,0 +1,208 @@ +/* + * ip27-irq.c: Highlevel interrupt handling for IP27 architecture. + * + * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 1999 - 2001 Kanoj Sarcar + */ + +#undef DEBUG + +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/errno.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/smp.h> +#include <linux/random.h> +#include <linux/kernel.h> +#include <linux/kernel_stat.h> +#include <linux/delay.h> +#include <linux/bitops.h> + +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <asm/mipsregs.h> + +#include <asm/processor.h> +#include <asm/sn/addrs.h> +#include <asm/sn/agent.h> +#include <asm/sn/arch.h> +#include <asm/sn/hub.h> +#include <asm/sn/intr.h> + +/* + * Linux has a controller-independent x86 interrupt architecture. + * every controller has a 'controller-template', that is used + * by the main code to do the right thing. Each driver-visible + * interrupt source is transparently wired to the appropriate + * controller. Thus drivers need not be aware of the + * interrupt-controller. + * + * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, + * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. + * (IO-APICs assumed to be messaging to Pentium local-APICs) + * + * the code is designed to be easily extended with new/different + * interrupt controllers, without having to do assembly magic. + */ + +extern asmlinkage void ip27_irq(void); + +/* + * Find first bit set + */ +static int ms1bit(unsigned long x) +{ + int b = 0, s; + + s = 16; if (x >> 16 == 0) s = 0; b += s; x >>= s; + s = 8; if (x >> 8 == 0) s = 0; b += s; x >>= s; + s = 4; if (x >> 4 == 0) s = 0; b += s; x >>= s; + s = 2; if (x >> 2 == 0) s = 0; b += s; x >>= s; + s = 1; if (x >> 1 == 0) s = 0; b += s; + + return b; +} + +/* + * This code is unnecessarily complex, because we do + * intr enabling. Basically, once we grab the set of intrs we need + * to service, we must mask _all_ these interrupts; firstly, to make + * sure the same intr does not intr again, causing recursion that + * can lead to stack overflow. Secondly, we can not just mask the + * one intr we are do_IRQing, because the non-masked intrs in the + * first set might intr again, causing multiple servicings of the + * same intr. This effect is mostly seen for intercpu intrs. + * Kanoj 05.13.00 + */ + +static void ip27_do_irq_mask0(void) +{ + int irq, swlevel; + hubreg_t pend0, mask0; + cpuid_t cpu = smp_processor_id(); + int pi_int_mask0 = + (cputoslice(cpu) == 0) ? PI_INT_MASK0_A : PI_INT_MASK0_B; + + /* copied from Irix intpend0() */ + pend0 = LOCAL_HUB_L(PI_INT_PEND0); + mask0 = LOCAL_HUB_L(pi_int_mask0); + + pend0 &= mask0; /* Pick intrs we should look at */ + if (!pend0) + return; + + swlevel = ms1bit(pend0); +#ifdef CONFIG_SMP + if (pend0 & (1UL << CPU_RESCHED_A_IRQ)) { + LOCAL_HUB_CLR_INTR(CPU_RESCHED_A_IRQ); + scheduler_ipi(); + } else if (pend0 & (1UL << CPU_RESCHED_B_IRQ)) { + LOCAL_HUB_CLR_INTR(CPU_RESCHED_B_IRQ); + scheduler_ipi(); + } else if (pend0 & (1UL << CPU_CALL_A_IRQ)) { + LOCAL_HUB_CLR_INTR(CPU_CALL_A_IRQ); + smp_call_function_interrupt(); + } else if (pend0 & (1UL << CPU_CALL_B_IRQ)) { + LOCAL_HUB_CLR_INTR(CPU_CALL_B_IRQ); + smp_call_function_interrupt(); + } else +#endif + { + /* "map" swlevel to irq */ + struct slice_data *si = cpu_data[cpu].data; + + irq = si->level_to_irq[swlevel]; + do_IRQ(irq); + } + + LOCAL_HUB_L(PI_INT_PEND0); +} + +static void ip27_do_irq_mask1(void) +{ + int irq, swlevel; + hubreg_t pend1, mask1; + cpuid_t cpu = smp_processor_id(); + int pi_int_mask1 = (cputoslice(cpu) == 0) ? PI_INT_MASK1_A : PI_INT_MASK1_B; + struct slice_data *si = cpu_data[cpu].data; + + /* copied from Irix intpend0() */ + pend1 = LOCAL_HUB_L(PI_INT_PEND1); + mask1 = LOCAL_HUB_L(pi_int_mask1); + + pend1 &= mask1; /* Pick intrs we should look at */ + if (!pend1) + return; + + swlevel = ms1bit(pend1); + /* "map" swlevel to irq */ + irq = si->level_to_irq[swlevel]; + LOCAL_HUB_CLR_INTR(swlevel); + do_IRQ(irq); + + LOCAL_HUB_L(PI_INT_PEND1); +} + +static void ip27_prof_timer(void) +{ + panic("CPU %d got a profiling interrupt", smp_processor_id()); +} + +static void ip27_hub_error(void) +{ + panic("CPU %d got a hub error interrupt", smp_processor_id()); +} + +asmlinkage void plat_irq_dispatch(void) +{ + unsigned long pending = read_c0_cause() & read_c0_status(); + extern unsigned int rt_timer_irq; + + if (pending & CAUSEF_IP4) + do_IRQ(rt_timer_irq); + else if (pending & CAUSEF_IP2) /* PI_INT_PEND_0 or CC_PEND_{A|B} */ + ip27_do_irq_mask0(); + else if (pending & CAUSEF_IP3) /* PI_INT_PEND_1 */ + ip27_do_irq_mask1(); + else if (pending & CAUSEF_IP5) + ip27_prof_timer(); + else if (pending & CAUSEF_IP6) + ip27_hub_error(); +} + +void __init arch_init_irq(void) +{ +} + +void install_ipi(void) +{ + int slice = LOCAL_HUB_L(PI_CPU_NUM); + int cpu = smp_processor_id(); + struct slice_data *si = cpu_data[cpu].data; + struct hub_data *hub = hub_data(cpu_to_node(cpu)); + int resched, call; + + resched = CPU_RESCHED_A_IRQ + slice; + __set_bit(resched, hub->irq_alloc_mask); + __set_bit(resched, si->irq_enable_mask); + LOCAL_HUB_CLR_INTR(resched); + + call = CPU_CALL_A_IRQ + slice; + __set_bit(call, hub->irq_alloc_mask); + __set_bit(call, si->irq_enable_mask); + LOCAL_HUB_CLR_INTR(call); + + if (slice == 0) { + LOCAL_HUB_S(PI_INT_MASK0_A, si->irq_enable_mask[0]); + LOCAL_HUB_S(PI_INT_MASK1_A, si->irq_enable_mask[1]); + } else { + LOCAL_HUB_S(PI_INT_MASK0_B, si->irq_enable_mask[0]); + LOCAL_HUB_S(PI_INT_MASK1_B, si->irq_enable_mask[1]); + } +} diff --git a/arch/mips/sgi-ip27/ip27-klconfig.c b/arch/mips/sgi-ip27/ip27-klconfig.c new file mode 100644 index 000000000..c873d62ff --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-klconfig.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/param.h> +#include <linux/timex.h> +#include <linux/mm.h> + +#include <asm/sn/klconfig.h> +#include <asm/sn/arch.h> +#include <asm/sn/gda.h> + +klinfo_t *find_component(lboard_t *brd, klinfo_t *kli, unsigned char struct_type) +{ + int index, j; + + if (kli == (klinfo_t *)NULL) { + index = 0; + } else { + for (j = 0; j < KLCF_NUM_COMPS(brd); j++) + if (kli == KLCF_COMP(brd, j)) + break; + index = j; + if (index == KLCF_NUM_COMPS(brd)) { + printk("find_component: Bad pointer: 0x%p\n", kli); + return (klinfo_t *)NULL; + } + index++; /* next component */ + } + + for (; index < KLCF_NUM_COMPS(brd); index++) { + kli = KLCF_COMP(brd, index); + if (KLCF_COMP_TYPE(kli) == struct_type) + return kli; + } + + /* Didn't find it. */ + return (klinfo_t *)NULL; +} + +klinfo_t *find_first_component(lboard_t *brd, unsigned char struct_type) +{ + return find_component(brd, (klinfo_t *)NULL, struct_type); +} + +lboard_t *find_lboard(lboard_t *start, unsigned char brd_type) +{ + /* Search all boards stored on this node. */ + while (start) { + if (start->brd_type == brd_type) + return start; + start = KLCF_NEXT(start); + } + /* Didn't find it. */ + return (lboard_t *)NULL; +} + +lboard_t *find_lboard_class(lboard_t *start, unsigned char brd_type) +{ + /* Search all boards stored on this node. */ + while (start) { + if (KLCLASS(start->brd_type) == KLCLASS(brd_type)) + return start; + start = KLCF_NEXT(start); + } + + /* Didn't find it. */ + return (lboard_t *)NULL; +} + +cnodeid_t get_cpu_cnode(cpuid_t cpu) +{ + return CPUID_TO_COMPACT_NODEID(cpu); +} + +klcpu_t *nasid_slice_to_cpuinfo(nasid_t nasid, int slice) +{ + lboard_t *brd; + klcpu_t *acpu; + + if (!(brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_IP27))) + return (klcpu_t *)NULL; + + if (!(acpu = (klcpu_t *)find_first_component(brd, KLSTRUCT_CPU))) + return (klcpu_t *)NULL; + + do { + if ((acpu->cpu_info.physid) == slice) + return acpu; + } while ((acpu = (klcpu_t *)find_component(brd, (klinfo_t *)acpu, + KLSTRUCT_CPU))); + return (klcpu_t *)NULL; +} + +klcpu_t *sn_get_cpuinfo(cpuid_t cpu) +{ + nasid_t nasid; + int slice; + klcpu_t *acpu; + gda_t *gdap = GDA; + cnodeid_t cnode; + + if (!(cpu < MAXCPUS)) { + printk("sn_get_cpuinfo: illegal cpuid 0x%lx\n", cpu); + return NULL; + } + + cnode = get_cpu_cnode(cpu); + if (cnode == INVALID_CNODEID) + return NULL; + + if ((nasid = gdap->g_nasidtable[cnode]) == INVALID_NASID) + return NULL; + + for (slice = 0; slice < CPUS_PER_NODE; slice++) { + acpu = nasid_slice_to_cpuinfo(nasid, slice); + if (acpu && acpu->cpu_info.virtid == cpu) + return acpu; + } + return NULL; +} + +int get_cpu_slice(cpuid_t cpu) +{ + klcpu_t *acpu; + + if ((acpu = sn_get_cpuinfo(cpu)) == NULL) + return -1; + return acpu->cpu_info.physid; +} diff --git a/arch/mips/sgi-ip27/ip27-klnuma.c b/arch/mips/sgi-ip27/ip27-klnuma.c new file mode 100644 index 000000000..bda90cf87 --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-klnuma.c @@ -0,0 +1,131 @@ +/* + * Ported from IRIX to Linux by Kanoj Sarcar, 06/08/00. + * Copyright 2000 - 2001 Silicon Graphics, Inc. + * Copyright 2000 - 2001 Kanoj Sarcar (kanoj@sgi.com) + */ +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/mmzone.h> +#include <linux/kernel.h> +#include <linux/nodemask.h> +#include <linux/string.h> + +#include <asm/page.h> +#include <asm/sections.h> +#include <asm/sn/types.h> +#include <asm/sn/arch.h> +#include <asm/sn/gda.h> +#include <asm/sn/hub.h> +#include <asm/sn/mapped_kernel.h> +#include <asm/sn/sn_private.h> + +static cpumask_t ktext_repmask; + +/* + * XXX - This needs to be much smarter about where it puts copies of the + * kernel. For example, we should never put a copy on a headless node, + * and we should respect the topology of the machine. + */ +void __init setup_replication_mask(void) +{ + /* Set only the master cnode's bit. The master cnode is always 0. */ + cpumask_clear(&ktext_repmask); + cpumask_set_cpu(0, &ktext_repmask); + +#ifdef CONFIG_REPLICATE_KTEXT +#ifndef CONFIG_MAPPED_KERNEL +#error Kernel replication works with mapped kernel support. No calias support. +#endif + { + cnodeid_t cnode; + + for_each_online_node(cnode) { + if (cnode == 0) + continue; + /* Advertise that we have a copy of the kernel */ + cpumask_set_cpu(cnode, &ktext_repmask); + } + } +#endif + /* Set up a GDA pointer to the replication mask. */ + GDA->g_ktext_repmask = &ktext_repmask; +} + + +static __init void set_ktext_source(nasid_t client_nasid, nasid_t server_nasid) +{ + kern_vars_t *kvp; + + kvp = &hub_data(client_nasid)->kern_vars; + + KERN_VARS_ADDR(client_nasid) = (unsigned long)kvp; + + kvp->kv_magic = KV_MAGIC; + kvp->kv_ro_nasid = server_nasid; + kvp->kv_rw_nasid = master_nasid; + kvp->kv_ro_baseaddr = NODE_CAC_BASE(server_nasid); + kvp->kv_rw_baseaddr = NODE_CAC_BASE(master_nasid); + printk("REPLICATION: ON nasid %d, ktext from nasid %d, kdata from nasid %d\n", client_nasid, server_nasid, master_nasid); +} + +/* XXX - When the BTE works, we should use it instead of this. */ +static __init void copy_kernel(nasid_t dest_nasid) +{ + unsigned long dest_kern_start, source_start, source_end, kern_size; + + source_start = (unsigned long) _stext; + source_end = (unsigned long) _etext; + kern_size = source_end - source_start; + + dest_kern_start = CHANGE_ADDR_NASID(MAPPED_KERN_RO_TO_K0(source_start), + dest_nasid); + memcpy((void *)dest_kern_start, (void *)source_start, kern_size); +} + +void __init replicate_kernel_text() +{ + cnodeid_t cnode; + nasid_t client_nasid; + nasid_t server_nasid; + + server_nasid = master_nasid; + + /* Record where the master node should get its kernel text */ + set_ktext_source(master_nasid, master_nasid); + + for_each_online_node(cnode) { + if (cnode == 0) + continue; + client_nasid = COMPACT_TO_NASID_NODEID(cnode); + + /* Check if this node should get a copy of the kernel */ + if (cpumask_test_cpu(cnode, &ktext_repmask)) { + server_nasid = client_nasid; + copy_kernel(server_nasid); + } + + /* Record where this node should get its kernel text */ + set_ktext_source(client_nasid, server_nasid); + } +} + +/* + * Return pfn of first free page of memory on a node. PROM may allocate + * data structures on the first couple of pages of the first slot of each + * node. If this is the case, getfirstfree(node) > getslotstart(node, 0). + */ +unsigned long node_getfirstfree(cnodeid_t cnode) +{ + unsigned long loadbase = REP_BASE; + nasid_t nasid = COMPACT_TO_NASID_NODEID(cnode); + unsigned long offset; + +#ifdef CONFIG_MAPPED_KERNEL + loadbase += 16777216; +#endif + offset = PAGE_ALIGN((unsigned long)(&_end)) - loadbase; + if ((cnode == 0) || (cpumask_test_cpu(cnode, &ktext_repmask))) + return TO_NODE(nasid, offset) >> PAGE_SHIFT; + else + return KDM_TO_PHYS(PAGE_ALIGN(SYMMON_STK_ADDR(nasid, 0))) >> PAGE_SHIFT; +} diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c new file mode 100644 index 000000000..8d0eb2643 --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-memory.c @@ -0,0 +1,484 @@ +/* + * 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, 05 by Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 2000 by Silicon Graphics, Inc. + * Copyright (C) 2004 by Christoph Hellwig + * + * On SGI IP27 the ARC memory configuration data is completly bogus but + * alternate easier to use mechanisms are available. + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/memblock.h> +#include <linux/mm.h> +#include <linux/mmzone.h> +#include <linux/module.h> +#include <linux/nodemask.h> +#include <linux/swap.h> +#include <linux/bootmem.h> +#include <linux/pfn.h> +#include <linux/highmem.h> +#include <asm/page.h> +#include <asm/pgalloc.h> +#include <asm/sections.h> + +#include <asm/sn/arch.h> +#include <asm/sn/hub.h> +#include <asm/sn/klconfig.h> +#include <asm/sn/sn_private.h> + + +#define SLOT_PFNSHIFT (SLOT_SHIFT - PAGE_SHIFT) +#define PFN_NASIDSHFT (NASID_SHFT - PAGE_SHIFT) + +struct node_data *__node_data[MAX_COMPACT_NODES]; + +EXPORT_SYMBOL(__node_data); + +static int fine_mode; + +static int is_fine_dirmode(void) +{ + return ((LOCAL_HUB_L(NI_STATUS_REV_ID) & NSRI_REGIONSIZE_MASK) >> NSRI_REGIONSIZE_SHFT) & REGIONSIZE_FINE; +} + +static hubreg_t get_region(cnodeid_t cnode) +{ + if (fine_mode) + return COMPACT_TO_NASID_NODEID(cnode) >> NASID_TO_FINEREG_SHFT; + else + return COMPACT_TO_NASID_NODEID(cnode) >> NASID_TO_COARSEREG_SHFT; +} + +static hubreg_t region_mask; + +static void gen_region_mask(hubreg_t *region_mask) +{ + cnodeid_t cnode; + + (*region_mask) = 0; + for_each_online_node(cnode) { + (*region_mask) |= 1ULL << get_region(cnode); + } +} + +#define rou_rflag rou_flags + +static int router_distance; + +static void router_recurse(klrou_t *router_a, klrou_t *router_b, int depth) +{ + klrou_t *router; + lboard_t *brd; + int port; + + if (router_a->rou_rflag == 1) + return; + + if (depth >= router_distance) + return; + + router_a->rou_rflag = 1; + + for (port = 1; port <= MAX_ROUTER_PORTS; port++) { + if (router_a->rou_port[port].port_nasid == INVALID_NASID) + continue; + + brd = (lboard_t *)NODE_OFFSET_TO_K0( + router_a->rou_port[port].port_nasid, + router_a->rou_port[port].port_offset); + + if (brd->brd_type == KLTYPE_ROUTER) { + router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd), brd->brd_compts[0]); + if (router == router_b) { + if (depth < router_distance) + router_distance = depth; + } + else + router_recurse(router, router_b, depth + 1); + } + } + + router_a->rou_rflag = 0; +} + +unsigned char __node_distances[MAX_COMPACT_NODES][MAX_COMPACT_NODES]; +EXPORT_SYMBOL(__node_distances); + +static int __init compute_node_distance(nasid_t nasid_a, nasid_t nasid_b) +{ + klrou_t *router, *router_a = NULL, *router_b = NULL; + lboard_t *brd, *dest_brd; + cnodeid_t cnode; + nasid_t nasid; + int port; + + /* Figure out which routers nodes in question are connected to */ + for_each_online_node(cnode) { + nasid = COMPACT_TO_NASID_NODEID(cnode); + + if (nasid == -1) continue; + + brd = find_lboard_class((lboard_t *)KL_CONFIG_INFO(nasid), + KLTYPE_ROUTER); + + if (!brd) + continue; + + do { + if (brd->brd_flags & DUPLICATE_BOARD) + continue; + + router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd), brd->brd_compts[0]); + router->rou_rflag = 0; + + for (port = 1; port <= MAX_ROUTER_PORTS; port++) { + if (router->rou_port[port].port_nasid == INVALID_NASID) + continue; + + dest_brd = (lboard_t *)NODE_OFFSET_TO_K0( + router->rou_port[port].port_nasid, + router->rou_port[port].port_offset); + + if (dest_brd->brd_type == KLTYPE_IP27) { + if (dest_brd->brd_nasid == nasid_a) + router_a = router; + if (dest_brd->brd_nasid == nasid_b) + router_b = router; + } + } + + } while ((brd = find_lboard_class(KLCF_NEXT(brd), KLTYPE_ROUTER))); + } + + if (router_a == NULL) { + printk("node_distance: router_a NULL\n"); + return -1; + } + if (router_b == NULL) { + printk("node_distance: router_b NULL\n"); + return -1; + } + + if (nasid_a == nasid_b) + return 0; + + if (router_a == router_b) + return 1; + + router_distance = 100; + router_recurse(router_a, router_b, 2); + + return router_distance; +} + +static void __init init_topology_matrix(void) +{ + nasid_t nasid, nasid2; + cnodeid_t row, col; + + for (row = 0; row < MAX_COMPACT_NODES; row++) + for (col = 0; col < MAX_COMPACT_NODES; col++) + __node_distances[row][col] = -1; + + for_each_online_node(row) { + nasid = COMPACT_TO_NASID_NODEID(row); + for_each_online_node(col) { + nasid2 = COMPACT_TO_NASID_NODEID(col); + __node_distances[row][col] = + compute_node_distance(nasid, nasid2); + } + } +} + +static void __init dump_topology(void) +{ + nasid_t nasid; + cnodeid_t cnode; + lboard_t *brd, *dest_brd; + int port; + int router_num = 0; + klrou_t *router; + cnodeid_t row, col; + + printk("************** Topology ********************\n"); + + printk(" "); + for_each_online_node(col) + printk("%02d ", col); + printk("\n"); + for_each_online_node(row) { + printk("%02d ", row); + for_each_online_node(col) + printk("%2d ", node_distance(row, col)); + printk("\n"); + } + + for_each_online_node(cnode) { + nasid = COMPACT_TO_NASID_NODEID(cnode); + + if (nasid == -1) continue; + + brd = find_lboard_class((lboard_t *)KL_CONFIG_INFO(nasid), + KLTYPE_ROUTER); + + if (!brd) + continue; + + do { + if (brd->brd_flags & DUPLICATE_BOARD) + continue; + printk("Router %d:", router_num); + router_num++; + + router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd), brd->brd_compts[0]); + + for (port = 1; port <= MAX_ROUTER_PORTS; port++) { + if (router->rou_port[port].port_nasid == INVALID_NASID) + continue; + + dest_brd = (lboard_t *)NODE_OFFSET_TO_K0( + router->rou_port[port].port_nasid, + router->rou_port[port].port_offset); + + if (dest_brd->brd_type == KLTYPE_IP27) + printk(" %d", dest_brd->brd_nasid); + if (dest_brd->brd_type == KLTYPE_ROUTER) + printk(" r"); + } + printk("\n"); + + } while ( (brd = find_lboard_class(KLCF_NEXT(brd), KLTYPE_ROUTER)) ); + } +} + +static unsigned long __init slot_getbasepfn(cnodeid_t cnode, int slot) +{ + nasid_t nasid = COMPACT_TO_NASID_NODEID(cnode); + + return ((unsigned long)nasid << PFN_NASIDSHFT) | (slot << SLOT_PFNSHIFT); +} + +static unsigned long __init slot_psize_compute(cnodeid_t node, int slot) +{ + nasid_t nasid; + lboard_t *brd; + klmembnk_t *banks; + unsigned long size; + + nasid = COMPACT_TO_NASID_NODEID(node); + /* Find the node board */ + brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_IP27); + if (!brd) + return 0; + + /* Get the memory bank structure */ + banks = (klmembnk_t *) find_first_component(brd, KLSTRUCT_MEMBNK); + if (!banks) + return 0; + + /* Size in _Megabytes_ */ + size = (unsigned long)banks->membnk_bnksz[slot/4]; + + /* hack for 128 dimm banks */ + if (size <= 128) { + if (slot % 4 == 0) { + size <<= 20; /* size in bytes */ + return size >> PAGE_SHIFT; + } else + return 0; + } else { + size /= 4; + size <<= 20; + return size >> PAGE_SHIFT; + } +} + +static void __init mlreset(void) +{ + int i; + + master_nasid = get_nasid(); + fine_mode = is_fine_dirmode(); + + /* + * Probe for all CPUs - this creates the cpumask and sets up the + * mapping tables. We need to do this as early as possible. + */ +#ifdef CONFIG_SMP + cpu_node_probe(); +#endif + + init_topology_matrix(); + dump_topology(); + + gen_region_mask(®ion_mask); + + setup_replication_mask(); + + /* + * Set all nodes' calias sizes to 8k + */ + for_each_online_node(i) { + nasid_t nasid; + + nasid = COMPACT_TO_NASID_NODEID(i); + + /* + * Always have node 0 in the region mask, otherwise + * CALIAS accesses get exceptions since the hub + * thinks it is a node 0 address. + */ + REMOTE_HUB_S(nasid, PI_REGION_PRESENT, (region_mask | 1)); +#ifdef CONFIG_REPLICATE_EXHANDLERS + REMOTE_HUB_S(nasid, PI_CALIAS_SIZE, PI_CALIAS_SIZE_8K); +#else + REMOTE_HUB_S(nasid, PI_CALIAS_SIZE, PI_CALIAS_SIZE_0); +#endif + +#ifdef LATER + /* + * Set up all hubs to have a big window pointing at + * widget 0. Memory mode, widget 0, offset 0 + */ + REMOTE_HUB_S(nasid, IIO_ITTE(SWIN0_BIGWIN), + ((HUB_PIO_MAP_TO_MEM << IIO_ITTE_IOSP_SHIFT) | + (0 << IIO_ITTE_WIDGET_SHIFT))); +#endif + } +} + +static void __init szmem(void) +{ + unsigned long slot_psize, slot0sz = 0, nodebytes; /* Hack to detect problem configs */ + int slot; + cnodeid_t node; + + for_each_online_node(node) { + nodebytes = 0; + for (slot = 0; slot < MAX_MEM_SLOTS; slot++) { + slot_psize = slot_psize_compute(node, slot); + if (slot == 0) + slot0sz = slot_psize; + /* + * We need to refine the hack when we have replicated + * kernel text. + */ + nodebytes += (1LL << SLOT_SHIFT); + + if (!slot_psize) + continue; + + if ((nodebytes >> PAGE_SHIFT) * (sizeof(struct page)) > + (slot0sz << PAGE_SHIFT)) { + printk("Ignoring slot %d onwards on node %d\n", + slot, node); + slot = MAX_MEM_SLOTS; + continue; + } + memblock_add_node(PFN_PHYS(slot_getbasepfn(node, slot)), + PFN_PHYS(slot_psize), node); + } + } +} + +static void __init node_mem_init(cnodeid_t node) +{ + unsigned long slot_firstpfn = slot_getbasepfn(node, 0); + unsigned long slot_freepfn = node_getfirstfree(node); + unsigned long bootmap_size; + unsigned long start_pfn, end_pfn; + + get_pfn_range_for_nid(node, &start_pfn, &end_pfn); + + /* + * Allocate the node data structures on the node first. + */ + __node_data[node] = __va(slot_freepfn << PAGE_SHIFT); + memset(__node_data[node], 0, PAGE_SIZE); + + NODE_DATA(node)->bdata = &bootmem_node_data[node]; + NODE_DATA(node)->node_start_pfn = start_pfn; + NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn; + + cpumask_clear(&hub_data(node)->h_cpus); + + slot_freepfn += PFN_UP(sizeof(struct pglist_data) + + sizeof(struct hub_data)); + + bootmap_size = init_bootmem_node(NODE_DATA(node), slot_freepfn, + start_pfn, end_pfn); + free_bootmem_with_active_regions(node, end_pfn); + reserve_bootmem_node(NODE_DATA(node), slot_firstpfn << PAGE_SHIFT, + ((slot_freepfn - slot_firstpfn) << PAGE_SHIFT) + bootmap_size, + BOOTMEM_DEFAULT); + sparse_memory_present_with_active_regions(node); +} + +/* + * A node with nothing. We use it to avoid any special casing in + * cpumask_of_node + */ +static struct node_data null_node = { + .hub = { + .h_cpus = CPU_MASK_NONE + } +}; + +/* + * Currently, the intranode memory hole support assumes that each slot + * contains at least 32 MBytes of memory. We assume all bootmem data + * fits on the first slot. + */ +void __init prom_meminit(void) +{ + cnodeid_t node; + + mlreset(); + szmem(); + + for (node = 0; node < MAX_COMPACT_NODES; node++) { + if (node_online(node)) { + node_mem_init(node); + continue; + } + __node_data[node] = &null_node; + } +} + +void __init prom_free_prom_memory(void) +{ + /* We got nothing to free here ... */ +} + +extern void setup_zero_pages(void); + +void __init paging_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES] = {0, }; + unsigned node; + + pagetable_init(); + + for_each_online_node(node) { + unsigned long start_pfn, end_pfn; + + get_pfn_range_for_nid(node, &start_pfn, &end_pfn); + + if (end_pfn > max_low_pfn) + max_low_pfn = end_pfn; + } + zones_size[ZONE_NORMAL] = max_low_pfn; + free_area_init_nodes(zones_size); +} + +void __init mem_init(void) +{ + high_memory = (void *) __va(get_num_physpages() << PAGE_SHIFT); + free_all_bootmem(); + setup_zero_pages(); /* This comes from node 0 */ + mem_init_print_info(NULL); +} diff --git a/arch/mips/sgi-ip27/ip27-nmi.c b/arch/mips/sgi-ip27/ip27-nmi.c new file mode 100644 index 000000000..a2358b444 --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-nmi.c @@ -0,0 +1,244 @@ +#include <linux/kernel.h> +#include <linux/mmzone.h> +#include <linux/nodemask.h> +#include <linux/spinlock.h> +#include <linux/smp.h> +#include <linux/atomic.h> +#include <asm/sn/types.h> +#include <asm/sn/addrs.h> +#include <asm/sn/nmi.h> +#include <asm/sn/arch.h> +#include <asm/sn/sn0/hub.h> + +#if 0 +#define NODE_NUM_CPUS(n) CNODE_NUM_CPUS(n) +#else +#define NODE_NUM_CPUS(n) CPUS_PER_NODE +#endif + +#define CNODEID_NONE (cnodeid_t)-1 + +typedef unsigned long machreg_t; + +static arch_spinlock_t nmi_lock = __ARCH_SPIN_LOCK_UNLOCKED; + +/* + * Lets see what else we need to do here. Set up sp, gp? + */ +void nmi_dump(void) +{ + void cont_nmi_dump(void); + + cont_nmi_dump(); +} + +void install_cpu_nmi_handler(int slice) +{ + nmi_t *nmi_addr; + + nmi_addr = (nmi_t *)NMI_ADDR(get_nasid(), slice); + if (nmi_addr->call_addr) + return; + nmi_addr->magic = NMI_MAGIC; + nmi_addr->call_addr = (void *)nmi_dump; + nmi_addr->call_addr_c = + (void *)(~((unsigned long)(nmi_addr->call_addr))); + nmi_addr->call_parm = 0; +} + +/* + * Copy the cpu registers which have been saved in the IP27prom format + * into the eframe format for the node under consideration. + */ + +void nmi_cpu_eframe_save(nasid_t nasid, int slice) +{ + struct reg_struct *nr; + int i; + + /* Get the pointer to the current cpu's register set. */ + nr = (struct reg_struct *) + (TO_UNCAC(TO_NODE(nasid, IP27_NMI_KREGS_OFFSET)) + + slice * IP27_NMI_KREGS_CPU_SIZE); + + printk("NMI nasid %d: slice %d\n", nasid, slice); + + /* + * Saved main processor registers + */ + for (i = 0; i < 32; ) { + if ((i % 4) == 0) + printk("$%2d :", i); + printk(" %016lx", nr->gpr[i]); + + i++; + if ((i % 4) == 0) + printk("\n"); + } + + printk("Hi : (value lost)\n"); + printk("Lo : (value lost)\n"); + + /* + * Saved cp0 registers + */ + printk("epc : %016lx %pS\n", nr->epc, (void *) nr->epc); + printk("%s\n", print_tainted()); + printk("ErrEPC: %016lx %pS\n", nr->error_epc, (void *) nr->error_epc); + printk("ra : %016lx %pS\n", nr->gpr[31], (void *) nr->gpr[31]); + printk("Status: %08lx ", nr->sr); + + if (nr->sr & ST0_KX) + printk("KX "); + if (nr->sr & ST0_SX) + printk("SX "); + if (nr->sr & ST0_UX) + printk("UX "); + + switch (nr->sr & ST0_KSU) { + case KSU_USER: + printk("USER "); + break; + case KSU_SUPERVISOR: + printk("SUPERVISOR "); + break; + case KSU_KERNEL: + printk("KERNEL "); + break; + default: + printk("BAD_MODE "); + break; + } + + if (nr->sr & ST0_ERL) + printk("ERL "); + if (nr->sr & ST0_EXL) + printk("EXL "); + if (nr->sr & ST0_IE) + printk("IE "); + printk("\n"); + + printk("Cause : %08lx\n", nr->cause); + printk("PrId : %08x\n", read_c0_prid()); + printk("BadVA : %016lx\n", nr->badva); + printk("CErr : %016lx\n", nr->cache_err); + printk("NMI_SR: %016lx\n", nr->nmi_sr); + + printk("\n"); +} + +void nmi_dump_hub_irq(nasid_t nasid, int slice) +{ + hubreg_t mask0, mask1, pend0, pend1; + + if (slice == 0) { /* Slice A */ + mask0 = REMOTE_HUB_L(nasid, PI_INT_MASK0_A); + mask1 = REMOTE_HUB_L(nasid, PI_INT_MASK1_A); + } else { /* Slice B */ + mask0 = REMOTE_HUB_L(nasid, PI_INT_MASK0_B); + mask1 = REMOTE_HUB_L(nasid, PI_INT_MASK1_B); + } + + pend0 = REMOTE_HUB_L(nasid, PI_INT_PEND0); + pend1 = REMOTE_HUB_L(nasid, PI_INT_PEND1); + + printk("PI_INT_MASK0: %16Lx PI_INT_MASK1: %16Lx\n", mask0, mask1); + printk("PI_INT_PEND0: %16Lx PI_INT_PEND1: %16Lx\n", pend0, pend1); + printk("\n\n"); +} + +/* + * Copy the cpu registers which have been saved in the IP27prom format + * into the eframe format for the node under consideration. + */ +void nmi_node_eframe_save(cnodeid_t cnode) +{ + nasid_t nasid; + int slice; + + /* Make sure that we have a valid node */ + if (cnode == CNODEID_NONE) + return; + + nasid = COMPACT_TO_NASID_NODEID(cnode); + if (nasid == INVALID_NASID) + return; + + /* Save the registers into eframe for each cpu */ + for (slice = 0; slice < NODE_NUM_CPUS(slice); slice++) { + nmi_cpu_eframe_save(nasid, slice); + nmi_dump_hub_irq(nasid, slice); + } +} + +/* + * Save the nmi cpu registers for all cpus in the system. + */ +void +nmi_eframes_save(void) +{ + cnodeid_t cnode; + + for_each_online_node(cnode) + nmi_node_eframe_save(cnode); +} + +void +cont_nmi_dump(void) +{ +#ifndef REAL_NMI_SIGNAL + static atomic_t nmied_cpus = ATOMIC_INIT(0); + + atomic_inc(&nmied_cpus); +#endif + /* + * Only allow 1 cpu to proceed + */ + arch_spin_lock(&nmi_lock); + +#ifdef REAL_NMI_SIGNAL + /* + * Wait up to 15 seconds for the other cpus to respond to the NMI. + * If a cpu has not responded after 10 sec, send it 1 additional NMI. + * This is for 2 reasons: + * - sometimes a MMSC fail to NMI all cpus. + * - on 512p SN0 system, the MMSC will only send NMIs to + * half the cpus. Unfortunately, we don't know which cpus may be + * NMIed - it depends on how the site chooses to configure. + * + * Note: it has been measure that it takes the MMSC up to 2.3 secs to + * send NMIs to all cpus on a 256p system. + */ + for (i=0; i < 1500; i++) { + for_each_online_node(node) + if (NODEPDA(node)->dump_count == 0) + break; + if (node == MAX_NUMNODES) + break; + if (i == 1000) { + for_each_online_node(node) + if (NODEPDA(node)->dump_count == 0) { + cpu = cpumask_first(cpumask_of_node(node)); + for (n=0; n < CNODE_NUM_CPUS(node); cpu++, n++) { + CPUMASK_SETB(nmied_cpus, cpu); + /* + * cputonasid, cputoslice + * needs kernel cpuid + */ + SEND_NMI((cputonasid(cpu)), (cputoslice(cpu))); + } + } + + } + udelay(10000); + } +#else + while (atomic_read(&nmied_cpus) != num_online_cpus()); +#endif + + /* + * Save the nmi cpu registers for all cpu in the eframe format. + */ + nmi_eframes_save(); + LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET); +} diff --git a/arch/mips/sgi-ip27/ip27-reset.c b/arch/mips/sgi-ip27/ip27-reset.c new file mode 100644 index 000000000..e44a15d4f --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-reset.c @@ -0,0 +1,81 @@ +/* + * 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. + * + * Reset an IP27. + * + * Copyright (C) 1997, 1998, 1999, 2000, 06 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + */ +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/smp.h> +#include <linux/mmzone.h> +#include <linux/nodemask.h> +#include <linux/pm.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/reboot.h> +#include <asm/sgialib.h> +#include <asm/sn/addrs.h> +#include <asm/sn/arch.h> +#include <asm/sn/gda.h> +#include <asm/sn/sn0/hub.h> + +void machine_restart(char *command) __noreturn; +void machine_halt(void) __noreturn; +void machine_power_off(void) __noreturn; + +#define noreturn while(1); /* Silence gcc. */ + +/* XXX How to pass the reboot command to the firmware??? */ +static void ip27_machine_restart(char *command) +{ +#if 0 + int i; +#endif + + printk("Reboot started from CPU %d\n", smp_processor_id()); +#ifdef CONFIG_SMP + smp_send_stop(); +#endif +#if 0 + for_each_online_node(i) + REMOTE_HUB_S(COMPACT_TO_NASID_NODEID(i), PROMOP_REG, + PROMOP_REBOOT); +#else + LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET); +#endif + noreturn; +} + +static void ip27_machine_halt(void) +{ + int i; + +#ifdef CONFIG_SMP + smp_send_stop(); +#endif + for_each_online_node(i) + REMOTE_HUB_S(COMPACT_TO_NASID_NODEID(i), PROMOP_REG, + PROMOP_RESTART); + LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET); + noreturn; +} + +static void ip27_machine_power_off(void) +{ + /* To do ... */ + noreturn; +} + +void ip27_reboot_setup(void) +{ + _machine_restart = ip27_machine_restart; + _machine_halt = ip27_machine_halt; + pm_power_off = ip27_machine_power_off; +} diff --git a/arch/mips/sgi-ip27/ip27-smp.c b/arch/mips/sgi-ip27/ip27-smp.c new file mode 100644 index 000000000..f9ae6a8fa --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-smp.c @@ -0,0 +1,238 @@ +/* + * 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 - 2001 by Kanoj Sarcar (kanoj@sgi.com) + * Copyright (C) 2000 - 2001 by Silicon Graphics, Inc. + */ +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/nodemask.h> +#include <asm/page.h> +#include <asm/processor.h> +#include <asm/sn/arch.h> +#include <asm/sn/gda.h> +#include <asm/sn/intr.h> +#include <asm/sn/klconfig.h> +#include <asm/sn/launch.h> +#include <asm/sn/mapped_kernel.h> +#include <asm/sn/sn_private.h> +#include <asm/sn/types.h> +#include <asm/sn/sn0/hubpi.h> +#include <asm/sn/sn0/hubio.h> +#include <asm/sn/sn0/ip27.h> + +/* + * Takes as first input the PROM assigned cpu id, and the kernel + * assigned cpu id as the second. + */ +static void alloc_cpupda(cpuid_t cpu, int cpunum) +{ + cnodeid_t node = get_cpu_cnode(cpu); + nasid_t nasid = COMPACT_TO_NASID_NODEID(node); + + cputonasid(cpunum) = nasid; + sn_cpu_info[cpunum].p_nodeid = node; + cputoslice(cpunum) = get_cpu_slice(cpu); +} + +static nasid_t get_actual_nasid(lboard_t *brd) +{ + klhub_t *hub; + + if (!brd) + return INVALID_NASID; + + /* find out if we are a completely disabled brd. */ + hub = (klhub_t *)find_first_component(brd, KLSTRUCT_HUB); + if (!hub) + return INVALID_NASID; + if (!(hub->hub_info.flags & KLINFO_ENABLE)) /* disabled node brd */ + return hub->hub_info.physid; + else + return brd->brd_nasid; +} + +static int do_cpumask(cnodeid_t cnode, nasid_t nasid, int highest) +{ + static int tot_cpus_found = 0; + lboard_t *brd; + klcpu_t *acpu; + int cpus_found = 0; + cpuid_t cpuid; + + brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_IP27); + + do { + acpu = (klcpu_t *)find_first_component(brd, KLSTRUCT_CPU); + while (acpu) { + cpuid = acpu->cpu_info.virtid; + /* cnode is not valid for completely disabled brds */ + if (get_actual_nasid(brd) == brd->brd_nasid) + cpuid_to_compact_node[cpuid] = cnode; + if (cpuid > highest) + highest = cpuid; + /* Only let it join in if it's marked enabled */ + if ((acpu->cpu_info.flags & KLINFO_ENABLE) && + (tot_cpus_found != NR_CPUS)) { + set_cpu_possible(cpuid, true); + alloc_cpupda(cpuid, tot_cpus_found); + cpus_found++; + tot_cpus_found++; + } + acpu = (klcpu_t *)find_component(brd, (klinfo_t *)acpu, + KLSTRUCT_CPU); + } + brd = KLCF_NEXT(brd); + if (!brd) + break; + + brd = find_lboard(brd, KLTYPE_IP27); + } while (brd); + + return highest; +} + +void cpu_node_probe(void) +{ + int i, highest = 0; + gda_t *gdap = GDA; + + /* + * Initialize the arrays to invalid nodeid (-1) + */ + for (i = 0; i < MAX_COMPACT_NODES; i++) + compact_to_nasid_node[i] = INVALID_NASID; + for (i = 0; i < MAX_NASIDS; i++) + nasid_to_compact_node[i] = INVALID_CNODEID; + for (i = 0; i < MAXCPUS; i++) + cpuid_to_compact_node[i] = INVALID_CNODEID; + + /* + * MCD - this whole "compact node" stuff can probably be dropped, + * as we can handle sparse numbering now + */ + nodes_clear(node_online_map); + for (i = 0; i < MAX_COMPACT_NODES; i++) { + nasid_t nasid = gdap->g_nasidtable[i]; + if (nasid == INVALID_NASID) + break; + compact_to_nasid_node[i] = nasid; + nasid_to_compact_node[nasid] = i; + node_set_online(num_online_nodes()); + highest = do_cpumask(i, nasid, highest); + } + + printk("Discovered %d cpus on %d nodes\n", highest + 1, num_online_nodes()); +} + +static __init void intr_clear_all(nasid_t nasid) +{ + int i; + + REMOTE_HUB_S(nasid, PI_INT_MASK0_A, 0); + REMOTE_HUB_S(nasid, PI_INT_MASK0_B, 0); + REMOTE_HUB_S(nasid, PI_INT_MASK1_A, 0); + REMOTE_HUB_S(nasid, PI_INT_MASK1_B, 0); + + for (i = 0; i < 128; i++) + REMOTE_HUB_CLR_INTR(nasid, i); +} + +static void ip27_send_ipi_single(int destid, unsigned int action) +{ + int irq; + + switch (action) { + case SMP_RESCHEDULE_YOURSELF: + irq = CPU_RESCHED_A_IRQ; + break; + case SMP_CALL_FUNCTION: + irq = CPU_CALL_A_IRQ; + break; + default: + panic("sendintr"); + } + + irq += cputoslice(destid); + + /* + * Convert the compact hub number to the NASID to get the correct + * part of the address space. Then set the interrupt bit associated + * with the CPU we want to send the interrupt to. + */ + REMOTE_HUB_SEND_INTR(COMPACT_TO_NASID_NODEID(cpu_to_node(destid)), irq); +} + +static void ip27_send_ipi_mask(const struct cpumask *mask, unsigned int action) +{ + unsigned int i; + + for_each_cpu(i, mask) + ip27_send_ipi_single(i, action); +} + +static void ip27_init_secondary(void) +{ + per_cpu_init(); +} + +static void ip27_smp_finish(void) +{ + extern void hub_rt_clock_event_init(void); + + hub_rt_clock_event_init(); + local_irq_enable(); +} + +/* + * Launch a slave into smp_bootstrap(). It doesn't take an argument, and we + * set sp to the kernel stack of the newly created idle process, gp to the proc + * struct so that current_thread_info() will work. + */ +static void ip27_boot_secondary(int cpu, struct task_struct *idle) +{ + unsigned long gp = (unsigned long)task_thread_info(idle); + unsigned long sp = __KSTK_TOS(idle); + + LAUNCH_SLAVE(cputonasid(cpu), cputoslice(cpu), + (launch_proc_t)MAPPED_KERN_RW_TO_K0(smp_bootstrap), + 0, (void *) sp, (void *) gp); +} + +static void __init ip27_smp_setup(void) +{ + cnodeid_t cnode; + + for_each_online_node(cnode) { + if (cnode == 0) + continue; + intr_clear_all(COMPACT_TO_NASID_NODEID(cnode)); + } + + replicate_kernel_text(); + + /* + * Assumption to be fixed: we're always booted on logical / physical + * processor 0. While we're always running on logical processor 0 + * this still means this is physical processor zero; it might for + * example be disabled in the firmware. + */ + alloc_cpupda(0, 0); +} + +static void __init ip27_prepare_cpus(unsigned int max_cpus) +{ + /* We already did everything necessary earlier */ +} + +struct plat_smp_ops ip27_smp_ops = { + .send_ipi_single = ip27_send_ipi_single, + .send_ipi_mask = ip27_send_ipi_mask, + .init_secondary = ip27_init_secondary, + .smp_finish = ip27_smp_finish, + .boot_secondary = ip27_boot_secondary, + .smp_setup = ip27_smp_setup, + .prepare_cpus = ip27_prepare_cpus, +}; diff --git a/arch/mips/sgi-ip27/ip27-timer.c b/arch/mips/sgi-ip27/ip27-timer.c new file mode 100644 index 000000000..a6d10f607 --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-timer.c @@ -0,0 +1,242 @@ +/* + * Copytight (C) 1999, 2000, 05, 06 Ralf Baechle (ralf@linux-mips.org) + * Copytight (C) 1999, 2000 Silicon Graphics, Inc. + */ +#include <linux/bcd.h> +#include <linux/clockchips.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/sched_clock.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/param.h> +#include <linux/smp.h> +#include <linux/time.h> +#include <linux/timex.h> +#include <linux/mm.h> +#include <linux/platform_device.h> + +#include <asm/time.h> +#include <asm/pgtable.h> +#include <asm/sgialib.h> +#include <asm/sn/ioc3.h> +#include <asm/sn/klconfig.h> +#include <asm/sn/arch.h> +#include <asm/sn/addrs.h> +#include <asm/sn/sn_private.h> +#include <asm/sn/sn0/ip27.h> +#include <asm/sn/sn0/hub.h> + +#define TICK_SIZE (tick_nsec / 1000) + +/* Includes for ioc3_init(). */ +#include <asm/sn/types.h> +#include <asm/sn/sn0/addrs.h> +#include <asm/sn/sn0/hubni.h> +#include <asm/sn/sn0/hubio.h> +#include <asm/pci/bridge.h> + +static void enable_rt_irq(struct irq_data *d) +{ +} + +static void disable_rt_irq(struct irq_data *d) +{ +} + +static struct irq_chip rt_irq_type = { + .name = "SN HUB RT timer", + .irq_mask = disable_rt_irq, + .irq_unmask = enable_rt_irq, +}; + +static int rt_next_event(unsigned long delta, struct clock_event_device *evt) +{ + unsigned int cpu = smp_processor_id(); + int slice = cputoslice(cpu); + unsigned long cnt; + + cnt = LOCAL_HUB_L(PI_RT_COUNT); + cnt += delta; + LOCAL_HUB_S(PI_RT_COMPARE_A + PI_COUNT_OFFSET * slice, cnt); + + return LOCAL_HUB_L(PI_RT_COUNT) >= cnt ? -ETIME : 0; +} + +static void rt_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + /* Nothing to do ... */ +} + +unsigned int rt_timer_irq; + +static DEFINE_PER_CPU(struct clock_event_device, hub_rt_clockevent); +static DEFINE_PER_CPU(char [11], hub_rt_name); + +static irqreturn_t hub_rt_counter_handler(int irq, void *dev_id) +{ + unsigned int cpu = smp_processor_id(); + struct clock_event_device *cd = &per_cpu(hub_rt_clockevent, cpu); + int slice = cputoslice(cpu); + + /* + * Ack + */ + LOCAL_HUB_S(PI_RT_PEND_A + PI_COUNT_OFFSET * slice, 0); + cd->event_handler(cd); + + return IRQ_HANDLED; +} + +struct irqaction hub_rt_irqaction = { + .handler = hub_rt_counter_handler, + .flags = IRQF_PERCPU | IRQF_TIMER, + .name = "hub-rt", +}; + +/* + * This is a hack; we really need to figure these values out dynamically + * + * Since 800 ns works very well with various HUB frequencies, such as + * 360, 380, 390 and 400 MHZ, we use 800 ns rtc cycle time. + * + * Ralf: which clock rate is used to feed the counter? + */ +#define NSEC_PER_CYCLE 800 +#define CYCLES_PER_SEC (NSEC_PER_SEC / NSEC_PER_CYCLE) + +void hub_rt_clock_event_init(void) +{ + unsigned int cpu = smp_processor_id(); + struct clock_event_device *cd = &per_cpu(hub_rt_clockevent, cpu); + unsigned char *name = per_cpu(hub_rt_name, cpu); + int irq = rt_timer_irq; + + sprintf(name, "hub-rt %d", cpu); + cd->name = name; + cd->features = CLOCK_EVT_FEAT_ONESHOT; + clockevent_set_clock(cd, CYCLES_PER_SEC); + cd->max_delta_ns = clockevent_delta2ns(0xfffffffffffff, cd); + cd->min_delta_ns = clockevent_delta2ns(0x300, cd); + cd->rating = 200; + cd->irq = irq; + cd->cpumask = cpumask_of(cpu); + cd->set_next_event = rt_next_event; + cd->set_mode = rt_set_mode; + clockevents_register_device(cd); +} + +static void __init hub_rt_clock_event_global_init(void) +{ + int irq; + + do { + smp_wmb(); + irq = rt_timer_irq; + if (irq) + break; + + irq = allocate_irqno(); + if (irq < 0) + panic("Allocation of irq number for timer failed"); + } while (xchg(&rt_timer_irq, irq)); + + irq_set_chip_and_handler(irq, &rt_irq_type, handle_percpu_irq); + setup_irq(irq, &hub_rt_irqaction); +} + +static cycle_t hub_rt_read(struct clocksource *cs) +{ + return REMOTE_HUB_L(cputonasid(0), PI_RT_COUNT); +} + +struct clocksource hub_rt_clocksource = { + .name = "HUB-RT", + .rating = 200, + .read = hub_rt_read, + .mask = CLOCKSOURCE_MASK(52), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static u64 notrace hub_rt_read_sched_clock(void) +{ + return REMOTE_HUB_L(cputonasid(0), PI_RT_COUNT); +} + +static void __init hub_rt_clocksource_init(void) +{ + struct clocksource *cs = &hub_rt_clocksource; + + clocksource_register_hz(cs, CYCLES_PER_SEC); + + sched_clock_register(hub_rt_read_sched_clock, 52, CYCLES_PER_SEC); +} + +void __init plat_time_init(void) +{ + hub_rt_clocksource_init(); + hub_rt_clock_event_global_init(); + hub_rt_clock_event_init(); +} + +void cpu_time_init(void) +{ + lboard_t *board; + klcpu_t *cpu; + int cpuid; + + /* Don't use ARCS. ARCS is fragile. Klconfig is simple and sane. */ + board = find_lboard(KL_CONFIG_INFO(get_nasid()), KLTYPE_IP27); + if (!board) + panic("Can't find board info for myself."); + + cpuid = LOCAL_HUB_L(PI_CPU_NUM) ? IP27_CPU0_INDEX : IP27_CPU1_INDEX; + cpu = (klcpu_t *) KLCF_COMP(board, cpuid); + if (!cpu) + panic("No information about myself?"); + + printk("CPU %d clock is %dMHz.\n", smp_processor_id(), cpu->cpu_speed); + + set_c0_status(SRB_TIMOCLK); +} + +void hub_rtc_init(cnodeid_t cnode) +{ + + /* + * We only need to initialize the current node. + * If this is not the current node then it is a cpuless + * node and timeouts will not happen there. + */ + if (get_compact_nodeid() == cnode) { + LOCAL_HUB_S(PI_RT_EN_A, 1); + LOCAL_HUB_S(PI_RT_EN_B, 1); + LOCAL_HUB_S(PI_PROF_EN_A, 0); + LOCAL_HUB_S(PI_PROF_EN_B, 0); + LOCAL_HUB_S(PI_RT_COUNT, 0); + LOCAL_HUB_S(PI_RT_PEND_A, 0); + LOCAL_HUB_S(PI_RT_PEND_B, 0); + } +} + +static int __init sgi_ip27_rtc_devinit(void) +{ + struct resource res; + + memset(&res, 0, sizeof(res)); + res.start = XPHYSADDR(KL_CONFIG_CH_CONS_INFO(master_nasid)->memory_base + + IOC3_BYTEBUS_DEV0); + res.end = res.start + 32767; + res.flags = IORESOURCE_MEM; + + return IS_ERR(platform_device_register_simple("rtc-m48t35", -1, + &res, 1)); +} + +/* + * kludge make this a device_initcall after ioc3 resource conflicts + * are resolved + */ +late_initcall(sgi_ip27_rtc_devinit); diff --git a/arch/mips/sgi-ip27/ip27-xtalk.c b/arch/mips/sgi-ip27/ip27-xtalk.c new file mode 100644 index 000000000..20f582a21 --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-xtalk.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 1999, 2000 Silcon Graphics, Inc. + * Copyright (C) 2004 Christoph Hellwig. + * Released under GPL v2. + * + * Generic XTALK initialization code + */ + +#include <linux/kernel.h> +#include <linux/smp.h> +#include <asm/sn/types.h> +#include <asm/sn/klconfig.h> +#include <asm/sn/hub.h> +#include <asm/pci/bridge.h> +#include <asm/xtalk/xtalk.h> + + +#define XBOW_WIDGET_PART_NUM 0x0 +#define XXBOW_WIDGET_PART_NUM 0xd000 /* Xbow in Xbridge */ +#define BASE_XBOW_PORT 8 /* Lowest external port */ + +extern int bridge_probe(nasid_t nasid, int widget, int masterwid); + +static int probe_one_port(nasid_t nasid, int widget, int masterwid) +{ + widgetreg_t widget_id; + xwidget_part_num_t partnum; + + widget_id = *(volatile widgetreg_t *) + (RAW_NODE_SWIN_BASE(nasid, widget) + WIDGET_ID); + partnum = XWIDGET_PART_NUM(widget_id); + + printk(KERN_INFO "Cpu %d, Nasid 0x%x, widget 0x%x (partnum 0x%x) is ", + smp_processor_id(), nasid, widget, partnum); + + switch (partnum) { + case BRIDGE_WIDGET_PART_NUM: + case XBRIDGE_WIDGET_PART_NUM: + bridge_probe(nasid, widget, masterwid); + break; + default: + break; + } + + return 0; +} + +static int xbow_probe(nasid_t nasid) +{ + lboard_t *brd; + klxbow_t *xbow_p; + unsigned masterwid, i; + + printk("is xbow\n"); + + /* + * found xbow, so may have multiple bridges + * need to probe xbow + */ + brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_MIDPLANE8); + if (!brd) + return -ENODEV; + + xbow_p = (klxbow_t *)find_component(brd, NULL, KLSTRUCT_XBOW); + if (!xbow_p) + return -ENODEV; + + /* + * Okay, here's a xbow. Lets arbitrate and find + * out if we should initialize it. Set enabled + * hub connected at highest or lowest widget as + * master. + */ +#ifdef WIDGET_A + i = HUB_WIDGET_ID_MAX + 1; + do { + i--; + } while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) || + (!XBOW_PORT_IS_ENABLED(xbow_p, i))); +#else + i = HUB_WIDGET_ID_MIN - 1; + do { + i++; + } while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) || + (!XBOW_PORT_IS_ENABLED(xbow_p, i))); +#endif + + masterwid = i; + if (nasid != XBOW_PORT_NASID(xbow_p, i)) + return 1; + + for (i = HUB_WIDGET_ID_MIN; i <= HUB_WIDGET_ID_MAX; i++) { + if (XBOW_PORT_IS_ENABLED(xbow_p, i) && + XBOW_PORT_TYPE_IO(xbow_p, i)) + probe_one_port(nasid, i, masterwid); + } + + return 0; +} + +void xtalk_probe_node(cnodeid_t nid) +{ + volatile u64 hubreg; + nasid_t nasid; + xwidget_part_num_t partnum; + widgetreg_t widget_id; + + nasid = COMPACT_TO_NASID_NODEID(nid); + hubreg = REMOTE_HUB_L(nasid, IIO_LLP_CSR); + + /* check whether the link is up */ + if (!(hubreg & IIO_LLP_CSR_IS_UP)) + return; + + widget_id = *(volatile widgetreg_t *) + (RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID); + partnum = XWIDGET_PART_NUM(widget_id); + + printk(KERN_INFO "Cpu %d, Nasid 0x%x: partnum 0x%x is ", + smp_processor_id(), nasid, partnum); + + switch (partnum) { + case BRIDGE_WIDGET_PART_NUM: + bridge_probe(nasid, 0x8, 0xa); + break; + case XBOW_WIDGET_PART_NUM: + case XXBOW_WIDGET_PART_NUM: + xbow_probe(nasid); + break; + default: + printk(" unknown widget??\n"); + break; + } +} |