summaryrefslogtreecommitdiffstats
path: root/arch/ia64/sn/sn1
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ia64/sn/sn1')
-rw-r--r--arch/ia64/sn/sn1/Makefile19
-rw-r--r--arch/ia64/sn/sn1/discontig.c188
-rw-r--r--arch/ia64/sn/sn1/iomv.c100
-rw-r--r--arch/ia64/sn/sn1/irq.c233
-rw-r--r--arch/ia64/sn/sn1/llsc4.c944
-rw-r--r--arch/ia64/sn/sn1/llsc4.h104
-rw-r--r--arch/ia64/sn/sn1/mm.c399
-rw-r--r--arch/ia64/sn/sn1/setup.c46
-rw-r--r--arch/ia64/sn/sn1/smp.c186
-rw-r--r--arch/ia64/sn/sn1/sn1_asm.S6
-rw-r--r--arch/ia64/sn/sn1/synergy.c204
11 files changed, 2404 insertions, 25 deletions
diff --git a/arch/ia64/sn/sn1/Makefile b/arch/ia64/sn/sn1/Makefile
index fbb8e83ab..f995c8dc4 100644
--- a/arch/ia64/sn/sn1/Makefile
+++ b/arch/ia64/sn/sn1/Makefile
@@ -5,20 +5,27 @@
# Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com)
#
-CFLAGS := $(CFLAGS) -DCONFIG_SGI_SN1 -DSN1 -DSN -DSOFTSDV \
- -DLANGUAGE_C=1 -D_LANGUAGE_C=1
-AFLAGS := $(AFLAGS) -DCONFIG_SGI_SN1 -DSN1 -DSOFTSDV
+EXTRA_CFLAGS := -DSN -DLANGUAGE_C=1 -D_LANGUAGE_C=1 -I. -DBRINGUP \
+ -DDIRECT_L1_CONSOLE -DNUMA_BASE -DSIMULATED_KLGRAPH \
+ -DNUMA_MIGR_CONTROL -DLITTLE_ENDIAN -DREAL_HARDWARE \
+ -DNEW_INTERRUPTS -DCONFIG_IA64_SGI_IO
.S.s:
- $(CPP) $(AFLAGS) -o $*.s $<
+ $(CPP) $(AFLAGS) $(AFLAGS_KERNEL) -o $*.s $<
.S.o:
- $(CC) $(AFLAGS) -c -o $*.o $<
+ $(CC) $(AFLAGS) $(AFLAGS_KERNEL) -c -o $*.o $<
all: sn1.a
O_TARGET = sn1.a
O_HEADERS =
-O_OBJS = irq.o setup.o
+O_OBJS = irq.o setup.o iomv.o mm.o smp.o synergy.o sn1_asm.o \
+ discontig.o
+
+ifeq ($(CONFIG_IA64_SGI_AUTOTEST),y)
+O_OBJS += llsc4.o
+endif
+
ifeq ($(CONFIG_IA64_GENERIC),y)
O_OBJS += machvec.o
diff --git a/arch/ia64/sn/sn1/discontig.c b/arch/ia64/sn/sn1/discontig.c
new file mode 100644
index 000000000..7251ee066
--- /dev/null
+++ b/arch/ia64/sn/sn1/discontig.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2000, Silicon Graphics, sprasad@engr.sgi.com
+ * Copyright 2000, Kanoj Sarcar, kanoj@sgi.com
+ */
+
+/*
+ * Contains common definitions and globals for NUMA platform
+ * support. For now, SN-IA64 and SN-MIPS are the NUMA platforms.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/bootmem.h>
+#include <asm/sn/mmzone.h>
+#include <asm/efi.h>
+
+extern int numnodes ;
+
+plat_pg_data_t plat_node_data[MAXNODES];
+bootmem_data_t bdata[MAXNODES];
+int chunktonid[MAXCHUNKS];
+int nasid_map[MAXNASIDS];
+
+void __init
+init_chunktonid(void)
+{
+ memset(chunktonid, -1, sizeof(chunktonid)) ;
+}
+
+void __init
+init_nodeidmap(void)
+{
+ memset(nasid_map, -1, sizeof(nasid_map)) ;
+}
+
+int cnodeid_map[MAXNODES] ;
+void __init
+init_cnodeidmap(void)
+{
+ memset(cnodeid_map, -1, sizeof(cnodeid_map)) ;
+}
+
+int
+numa_debug(void)
+{
+ panic("NUMA debug\n");
+ return(0);
+}
+
+int __init
+build_cnodeid_map(void)
+{
+ int i,j ;
+
+ for (i=0,j=0;i<MAXNASIDS;i++) {
+ if (nasid_map[i] >= 0)
+ cnodeid_map[j++] = i ;
+ }
+ return j ;
+}
+
+/*
+ * Since efi_memmap_walk merges contiguous banks, this code will need
+ * to find all the nasids covered by the input memory descriptor.
+ */
+static int __init
+build_nasid_map(unsigned long start, unsigned long end, void *arg)
+{
+ unsigned long vaddr = start;
+ int nasid = GetNasId(__pa(vaddr));
+
+ while (vaddr < end) {
+ if (nasid < MAXNASIDS)
+ nasid_map[nasid] = 0;
+ else
+ panic("build_nasid_map");
+ vaddr = (unsigned long)__va((unsigned long)(++nasid) <<
+ SN1_NODE_ADDR_SHIFT);
+ }
+ return 0;
+}
+
+void __init
+fix_nasid_map(void)
+{
+ int i ;
+ int j ;
+
+ /* For every nasid */
+ for (j=0;j<MAXNASIDS;j++) {
+ for (i=0;i<MAXNODES;i++) {
+ if (CNODEID_TO_NASID(i) == j)
+ break ;
+ }
+ if (i<MAXNODES)
+ nasid_map[j] = i ;
+ }
+}
+
+static void __init
+dump_bootmem_info(void)
+{
+ int i;
+ struct bootmem_data *bdata ;
+
+ printk("CNODE INFO ....\n") ;
+ for (i=0;i<numnodes;i++) {
+ printk("%d ", CNODEID_TO_NASID(i)) ;
+ }
+ printk("\n") ;
+
+ printk("BOOT MEM INFO ....\n") ;
+ printk("Node Start LowPfn BootmemMap\n") ;
+ for (i=0;i<numnodes;i++) {
+ bdata = NODE_DATA(i)->bdata ;
+ printk("%d 0x%016lx 0x%016lx 0x%016lx\n", i,
+ bdata->node_boot_start, bdata->node_low_pfn,
+ (unsigned long)bdata->node_bootmem_map) ;
+ }
+}
+
+void __init
+discontig_mem_init(void)
+{
+ extern void setup_sn1_bootmem(int);
+ int maxnodes ;
+
+ init_chunktonid() ;
+ init_nodeidmap() ;
+ init_cnodeidmap() ;
+ efi_memmap_walk(build_nasid_map, 0) ;
+ maxnodes = build_cnodeid_map() ;
+ fix_nasid_map() ;
+#ifdef CONFIG_DISCONTIGMEM
+ setup_sn1_bootmem(maxnodes) ;
+#endif
+ numnodes = maxnodes;
+ dump_bootmem_info() ;
+}
+
+void __init
+discontig_paging_init(void)
+{
+ int i;
+ unsigned long max_dma, zones_size[MAX_NR_ZONES];
+ void dump_node_data(void);
+
+ max_dma = virt_to_phys((void *) MAX_DMA_ADDRESS) >> PAGE_SHIFT;
+ for (i = 0; i < numnodes; i++) {
+ extern void free_unused_memmap_node(int);
+ unsigned long startpfn = __pa((void *)NODE_START(i)) >> PAGE_SHIFT;
+ unsigned long numpfn = NODE_SIZE(i) >> PAGE_SHIFT;
+ memset(zones_size, 0, sizeof(zones_size));
+
+ if ((startpfn + numpfn) < max_dma) {
+ zones_size[ZONE_DMA] = numpfn;
+ } else if (startpfn > max_dma) {
+ zones_size[ZONE_NORMAL] = numpfn;
+ } else {
+ zones_size[ZONE_DMA] = (max_dma - startpfn);
+ zones_size[ZONE_NORMAL] = numpfn - zones_size[ZONE_DMA];
+ }
+ free_area_init_node(i, NODE_DATA(i), NULL, zones_size, startpfn<<PAGE_SHIFT, 0);
+ free_unused_memmap_node(i);
+ }
+ dump_node_data();
+}
+
+
+void
+dump_node_data(void)
+{
+ int i;
+
+ printk("NODE DATA ....\n") ;
+ printk("Node, Start, Size, MemMap, BitMap, StartP, Mapnr, Size, Id\n") ;
+ for (i=0;i<numnodes;i++) {
+ printk("%d, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, %d\n",
+ CNODEID_TO_NASID(i), NODE_START(i), NODE_SIZE(i),
+ (long)NODE_MEM_MAP(i), (long)NODE_DATA(i)->valid_addr_bitmap,
+ NODE_DATA(i)->node_start_paddr,
+ NODE_DATA(i)->node_start_mapnr,
+ NODE_DATA(i)->node_size,
+ NODE_DATA(i)->node_id) ;
+ }
+}
+
diff --git a/arch/ia64/sn/sn1/iomv.c b/arch/ia64/sn/sn1/iomv.c
new file mode 100644
index 000000000..1d90a924f
--- /dev/null
+++ b/arch/ia64/sn/sn1/iomv.c
@@ -0,0 +1,100 @@
+/*
+ * 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 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Jack Steiner (steiner@sgi.com)
+ * Copyright (C) 2000 Kanoj Sarcar (kanoj@sgi.com)
+ */
+
+#include <asm/io.h>
+#include <linux/pci.h>
+
+static inline void *
+sn1_io_addr(unsigned long port)
+{
+ if (!IS_RUNNING_ON_SIMULATOR()) {
+ return( (void *) (port | __IA64_UNCACHED_OFFSET));
+ } else {
+ unsigned long io_base;
+ unsigned long addr;
+
+ /*
+ * word align port, but need more than 10 bits
+ * for accessing registers in bedrock local block
+ * (so we don't do port&0xfff)
+ */
+ if (port == 0x1f6 || port == 0x1f7
+ || port == 0x3f6 || port == 0x3f7
+ || port == 0x1f0 || port == 0x1f1
+ || port == 0x1f3 || port == 0x1f4
+ || port == 0x1f2 || port == 0x1f5) {
+ io_base = __IA64_UNCACHED_OFFSET | 0x00000FFFFC000000;
+ addr = io_base | ((port >> 2) << 12) | (port & 0xfff);
+ } else {
+ addr = __ia64_get_io_port_base() | ((port >> 2) << 2);
+ }
+ return(void *) addr;
+ }
+}
+
+unsigned int
+sn1_inb (unsigned long port)
+{
+ volatile unsigned char *addr = sn1_io_addr(port);
+ unsigned char ret;
+
+ ret = *addr;
+ __ia64_mf_a();
+ return ret;
+}
+
+unsigned int
+sn1_inw (unsigned long port)
+{
+ volatile unsigned short *addr = sn1_io_addr(port);
+ unsigned short ret;
+
+ ret = *addr;
+ __ia64_mf_a();
+ return ret;
+}
+
+unsigned int
+sn1_inl (unsigned long port)
+{
+ volatile unsigned int *addr = sn1_io_addr(port);
+ unsigned int ret;
+
+ ret = *addr;
+ __ia64_mf_a();
+ return ret;
+}
+
+void
+sn1_outb (unsigned char val, unsigned long port)
+{
+ volatile unsigned char *addr = sn1_io_addr(port);
+
+ *addr = val;
+ __ia64_mf_a();
+}
+
+void
+sn1_outw (unsigned short val, unsigned long port)
+{
+ volatile unsigned short *addr = sn1_io_addr(port);
+
+ *addr = val;
+ __ia64_mf_a();
+}
+
+void
+sn1_outl (unsigned int val, unsigned long port)
+{
+ volatile unsigned int *addr = sn1_io_addr(port);
+
+ *addr = val;
+ __ia64_mf_a();
+}
diff --git a/arch/ia64/sn/sn1/irq.c b/arch/ia64/sn/sn1/irq.c
index a8270fd2a..b487f88d4 100644
--- a/arch/ia64/sn/sn1/irq.c
+++ b/arch/ia64/sn/sn1/irq.c
@@ -1,8 +1,57 @@
-#include <linux/kernel.h>
+/*
+ * Platform dependent support for SGI SN1
+ *
+ * Copyright (C) 2000 Silicon Graphics
+ * Copyright (C) 2000 Jack Steiner (steiner@sgi.com)
+ * Copyright (C) 2000 Alan Mayer (ajm@sgi.com)
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
#include <linux/sched.h>
+#include <asm/current.h>
#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <linux/devfs_fs_kernel.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/types.h>
+#include <asm/sn/pci/bridge.h>
+#include <asm/sn/pci/pciio.h>
+#include <asm/sn/pci/pciio_private.h>
+#include <asm/sn/sn_cpuid.h>
+#include <asm/sn/sn1/bedrock.h>
+#include <asm/sn/intr.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/sn1/addrs.h>
+#include <asm/sn/iobus.h>
+#include <asm/sn/sn1/arch.h>
+#include <asm/sn/synergy.h>
+
+
+int bit_pos_to_irq(int bit);
+int irq_to_bit_pos(int irq);
+void add_interrupt_randomness(int irq);
+void * kmalloc(size_t size, int flags);
+void kfree(const void *);
+int sgi_pci_intr_support (unsigned int, device_desc_t *, devfs_handle_t *, pciio_intr_line_t *, devfs_handle_t *);
+pciio_intr_t pciio_intr_alloc(devfs_handle_t, device_desc_t, pciio_intr_line_t, devfs_handle_t);
+int request_irq(unsigned int, void (*)(int, void *, struct pt_regs *), unsigned long, const char *, void *);
+
+/* This should be dynamically allocated, at least part of it. */
+/* For the time being, though, we'll statically allocate it */
+/* because kmalloc hasn't been initiallized at the time this */
+/* array is initiallized. One way to do it would be to statically */
+/* allocate the data for node 0, then let other nodes, as they */
+/* need it, dynamically allocate their own data space. */
-#include <asm/ptrace.h>
+struct sn1_cnode_action_list *sn1_node_actions[MAX_COMPACT_NODES];
+struct sn1_cnode_action_list sn1_actions[MAX_COMPACT_NODES][256];
+
+
+extern int numnodes;
static unsigned int
sn1_startup_irq(unsigned int irq)
@@ -25,20 +74,192 @@ sn1_enable_irq(unsigned int irq)
{
}
+static void
+sn1_ack_irq(unsigned int irq)
+{
+}
+
+static void
+sn1_end_irq(unsigned int irq)
+{
+}
+
+static void
+sn1_set_affinity_irq(unsigned int irq, unsigned long mask)
+{
+}
+
+
+static void
+sn1_handle_irq(int irq, void *dummy, struct pt_regs *regs)
+{
+ int bit, cnode;
+ struct sn1_cnode_action_list *alp;
+ struct sn1_intr_action *ap;
+ void (*handler)(int, void *, struct pt_regs *);
+ unsigned long flags = 0;
+ int cpuid = smp_processor_id();
+
+
+ bit = irq_to_bit_pos(irq);
+ LOCAL_HUB_CLR_INTR(bit);
+ cnode = cpuid_to_cnodeid(cpuid);
+ alp = sn1_node_actions[cnode];
+ ap = alp[irq].action_list;
+ if (ap == NULL) {
+ return;
+ }
+ while (ap) {
+ flags |= ap->flags;
+ handler = ap->handler;
+ (*handler)(irq,ap->intr_arg,regs);
+ ap = ap->next;
+ }
+ if ((flags & SA_SAMPLE_RANDOM) != 0)
+ add_interrupt_randomness(irq);
+
+ return;
+}
+
struct hw_interrupt_type irq_type_sn1 = {
"sn1_irq",
sn1_startup_irq,
sn1_shutdown_irq,
sn1_enable_irq,
- sn1_disable_irq
+ sn1_disable_irq,
+ sn1_ack_irq,
+ sn1_end_irq,
+ sn1_set_affinity_irq
+};
+
+struct irqaction sn1_irqaction = {
+ sn1_handle_irq,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ NULL,
};
void
sn1_irq_init (void)
{
- int i;
+ int i,j;
+
+ for (i = 0; i <= NR_IRQS; ++i) {
+ if (irq_desc[i].handler == &no_irq_type) {
+ irq_desc[i].handler = &irq_type_sn1;
+ if (i >=71 && i <= 181) {
+ irq_desc[i].action = &sn1_irqaction;
+ }
+ }
+ }
+
+ for (i = 0; i < numnodes; i++) {
+ sn1_node_actions[i] = sn1_actions[i];
+ memset(sn1_node_actions[i], 0,
+ sizeof(struct sn1_cnode_action_list) *
+ (IA64_MAX_VECTORED_IRQ + 1));
+ for (j=0; j<IA64_MAX_VECTORED_IRQ+1; j++) {
+ spin_lock_init(&sn1_node_actions[i][j].action_list_lock);
+ }
+ }
+}
+
+
+int
+sn1_request_irq (unsigned int requested_irq, void (*handler)(int, void *, struct pt_regs *),
+ unsigned long irqflags, const char * devname, void *dev_id)
+{
+ devfs_handle_t curr_dev;
+ devfs_handle_t dev;
+ pciio_intr_t intr_handle;
+ pciio_intr_line_t line;
+ device_desc_t dev_desc;
+ int cpuid, bit, cnode;
+ struct sn1_intr_action *ap, *new_ap;
+ struct sn1_cnode_action_list *alp;
+ int irq;
- for (i = IA64_MIN_VECTORED_IRQ; i <= IA64_MAX_VECTORED_IRQ; ++i) {
- irq_desc[i].handler = &irq_type_sn1;
+ if ( (requested_irq & 0xff) == 0 ) {
+ int ret;
+
+ sgi_pci_intr_support(requested_irq,
+ &dev_desc, &dev, &line, &curr_dev);
+ intr_handle = pciio_intr_alloc(curr_dev, NULL, line, curr_dev);
+ bit = intr_handle->pi_irq;
+ cpuid = intr_handle->pi_cpu;
+ irq = bit_pos_to_irq(bit);
+ cnode = cpuid_to_cnodeid(cpuid);
+ new_ap = (struct sn1_intr_action *)kmalloc(
+ sizeof(struct sn1_intr_action), GFP_KERNEL);
+ irq_desc[irq].status = 0;
+ new_ap->handler = handler;
+ new_ap->intr_arg = dev_id;
+ new_ap->flags = irqflags;
+ new_ap->next = NULL;
+ alp = sn1_node_actions[cnode];
+
+ spin_lock(&alp[irq].action_list_lock);
+ ap = alp[irq].action_list;
+ /* check action list for "share" consistency */
+ while (ap){
+ if (!(ap->flags & irqflags & SA_SHIRQ) ) {
+ return(-EBUSY);
+ spin_unlock(&alp[irq].action_list_lock);
+ }
+ ap = ap->next;
+ }
+ ap = alp[irq].action_list;
+ if (ap) {
+ while (ap->next) {
+ ap = ap->next;
+ }
+ ap->next = new_ap;
+ } else {
+ alp[irq].action_list = new_ap;
+ }
+ ret = pciio_intr_connect(intr_handle, (intr_func_t)handler, dev_id, NULL);
+ if (ret) { /* connect failed, undo what we did. */
+ new_ap = alp[irq].action_list;
+ if (new_ap == ap) {
+ alp[irq].action_list = NULL;
+ kfree(ap);
+ } else {
+ while (new_ap->next && new_ap->next != ap) {
+ new_ap = new_ap->next;
+ }
+ if (new_ap->next == ap) {
+ new_ap->next = ap->next;
+ kfree(ap);
+ }
+ }
+ }
+
+ spin_unlock(&alp[irq].action_list_lock);
+ return(ret);
+ } else {
+ return(request_irq(requested_irq, handler, irqflags, devname, dev_id));
}
}
+
+#if !defined(CONFIG_IA64_SGI_IO)
+void
+sn1_pci_fixup(int arg)
+{
+}
+#endif
+
+int
+bit_pos_to_irq(int bit) {
+#define BIT_TO_IRQ 64
+
+ return bit + BIT_TO_IRQ;
+}
+
+int
+irq_to_bit_pos(int irq) {
+#define IRQ_TO_BIT 64
+
+ return irq - IRQ_TO_BIT;
+}
diff --git a/arch/ia64/sn/sn1/llsc4.c b/arch/ia64/sn/sn1/llsc4.c
new file mode 100644
index 000000000..98b98a906
--- /dev/null
+++ b/arch/ia64/sn/sn1/llsc4.c
@@ -0,0 +1,944 @@
+/*
+ *
+ * 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 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Jack Steiner (steiner@sgi.com)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+#include <linux/kernel_stat.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <asm/efi.h>
+#include <asm/page.h>
+#include <linux/threads.h>
+
+extern void bringup_set_led_bits(u8 bits, u8 mask);
+
+#include "llsc4.h"
+
+
+#ifdef STANDALONE
+#include "lock.h"
+#endif
+
+#ifdef INTTEST
+static int inttest=0;
+#endif
+
+
+/*
+ * Test parameter table for AUTOTEST
+ */
+typedef struct {
+ int passes;
+ int linecount;
+ int linepad;
+} autotest_table_t;
+
+autotest_table_t autotest_table[] = {
+ {1000000, 2, 0x2b4 },
+ {1000000, 16, 0, },
+ {1000000, 16, 4, },
+ {1000000, 128, 0x44 },
+ {1000000, 128, 0x84 },
+ {1000000, 128, 0x200 },
+ {1000000, 128, 0x204 },
+ {1000000, 128, 0x2b4 },
+ {1000000, 2, 8*MB+0x2b4 },
+ {1000000, 16, 8*MB+0 },
+ {1000000, 16, 8*MB+4 },
+ {1000000, 128, 8*MB+0x44 },
+ {1000000, 128, 8*MB+0x84 },
+ {1000000, 128, 8*MB+0x200 },
+ {1000000, 128, 8*MB+0x204 },
+ {1000000, 128, 8*MB+0x2b4 },
+ {0}};
+
+/*
+ * Array of virtual addresses available for test purposes.
+ */
+
+typedef struct {
+ long vstart;
+ long vend;
+ long nextaddr;
+ int wrapcount;
+} memmap_t;
+
+memmap_t memmap[MAXCHUNKS];
+int memmapx=0;
+
+typedef struct {
+ void *addr;
+ long data[16];
+ long data_fc[16];
+} capture_line_t;
+
+typedef struct {
+ int size;
+ void *blockaddr;
+ void *shadaddr;
+ long blockdata[16];
+ long shaddata[16];
+ long blockdata_fc[16];
+ long shaddata_fc[16];
+ long synerr;
+} capture_t;
+
+/*
+ * PORTING NOTE: revisit this statement. On hardware we put mbase at 0 and
+ * the rest of the tables have to start at 1MB to skip PROM tables.
+ */
+#define THREADPRIVATE(t) ((threadprivate_t*)(((long)mbase)+1024*1024+t*((sizeof(threadprivate_t)+511)/512*512)))
+
+#define k_capture mbase->sk_capture
+#define k_go mbase->sk_go
+#define k_linecount mbase->sk_linecount
+#define k_passes mbase->sk_passes
+#define k_napticks mbase->sk_napticks
+#define k_stop_on_error mbase->sk_stop_on_error
+#define k_verbose mbase->sk_verbose
+#define k_threadprivate mbase->sk_threadprivate
+#define k_blocks mbase->sk_blocks
+#define k_iter_msg mbase->sk_iter_msg
+#define k_vv mbase->sk_vv
+#define k_linepad mbase->sk_linepad
+#define k_options mbase->sk_options
+#define k_testnumber mbase->sk_testnumber
+#define k_currentpass mbase->sk_currentpass
+
+static long blocks[MAX_LINECOUNT]; /* addresses of data blocks */
+static control_t *mbase;
+static vint initialized=0;
+
+static unsigned int ran_conf_llsc(int);
+static int rerr(capture_t *, char *, void *, void *, int, int, int, int, int, int);
+static void dumpline(void *, char *, char *, void *, void *, int);
+static int checkstop(int, int, uint);
+static void spin(int);
+static void capturedata(capture_t *, uint, void *, void *, int);
+static int randn(uint max, uint *seed);
+static uint zrandom (uint *zranseed);
+static int set_lock(uint *, uint);
+static int clr_lock(uint *, uint);
+static void Speedo(void);
+
+int autotest_enabled=0;
+static int autotest_explicit_flush=0;
+static int llsctest_number=-1;
+static int errstop_enabled=0;
+static int fail_enabled=0;
+static int selective_trigger=0;
+
+static int __init autotest_enable(char *str)
+{
+ autotest_enabled = 1;
+ return 1;
+}
+static int __init set_llscxflush(char *str)
+{
+ autotest_explicit_flush = 1;
+ return 1;
+}
+static int __init set_llscselt(char *str)
+{
+ selective_trigger = 1;
+ return 1;
+}
+static int __init set_llsctest(char *str)
+{
+ llsctest_number = simple_strtol(str, &str, 10);
+ if (llsctest_number < 0 || llsctest_number > 15)
+ llsctest_number = -1;
+ return 1;
+}
+static int __init set_llscerrstop(char *str)
+{
+ errstop_enabled = 1;
+ return 1;
+}
+static int __init set_llscfail(char *str)
+{
+ fail_enabled = 8;
+ return 1;
+}
+
+static void print_params(void)
+{
+ printk ("********* Enter AUTOTEST facility on master cpu *************\n");
+ printk (" Test options:\n");
+ printk (" llsctest=<n>\t%d\tTest number to run (all = -1)\n", llsctest_number);
+ printk (" llscerrstop \t%s\tStop on error\n", errstop_enabled ? "on" : "off");
+ printk (" llscxflush \t%s\tEnable explicit FC in test\n", autotest_explicit_flush ? "on" : "off");
+ printk (" llscfail \t%s\tForce a failure to test the trigger & error messages\n", fail_enabled ? "on" : "off");
+ printk (" llscselt \t%s\tSelective triger on failures\n", selective_trigger ? "on" : "off");
+ printk ("\n");
+}
+__setup("autotest", autotest_enable);
+__setup("llsctest=", set_llsctest);
+__setup("llscerrstop", set_llscerrstop);
+__setup("llscxflush", set_llscxflush);
+__setup("llscfail", set_llscfail);
+__setup("llscselt", set_llscselt);
+
+
+extern inline void
+flush_buddy(void *p)
+{
+ long lp;
+
+ if (autotest_explicit_flush) {
+ lp = (long)p;
+ lp ^= 0x40;
+ asm volatile ("fc %0" :: "r"(lp) : "memory");
+ ia64_sync_i();
+ ia64_srlz_d();
+ }
+}
+
+static int
+set_lock(uint *lock, uint id)
+{
+ uint old;
+ flush_buddy(lock);
+ old = cmpxchg_acq(lock, 0, id);
+ return (old == 0);
+}
+
+static int
+clr_lock(uint *lock, uint id)
+{
+ uint old;
+ flush_buddy(lock);
+ old = cmpxchg_rel(lock, id, 0);
+ return (old == id);
+}
+
+static void
+zero_lock(uint *lock)
+{
+ flush_buddy(lock);
+ *lock = 0;
+}
+
+/*------------------------------------------------------------------------+
+| Routine : ran_conf_llsc - ll/sc shared data test |
+| Description: This test checks the coherency of shared data |
++------------------------------------------------------------------------*/
+static unsigned int
+ran_conf_llsc(int thread)
+{
+ private_t pval;
+ share_t sval, sval2;
+ uint vv, linei, slinei, sharei, pass;
+ long t;
+ lock_t lockpat;
+ share_t *sharecopy;
+ long verbose, napticks, passes, linecount, lcount;
+ dataline_t *linep, *slinep;
+ int s, seed;
+ threadprivate_t *tp;
+ uint iter_msg, iter_msg_i=0;
+ int vv_mask;
+ int correct_errors;
+ int errs=0;
+ int stillbad;
+ capture_t capdata;
+ private_t *privp;
+ share_t *sharep;
+
+
+ linecount = k_linecount;
+ napticks = k_napticks;
+ verbose = k_verbose;
+ passes = k_passes;
+ iter_msg = k_iter_msg;
+ seed = (thread + 1) * 647;
+ tp = THREADPRIVATE(thread);
+ vv_mask = (k_vv>>((thread%16)*4)) & 0xf;
+ correct_errors = k_options&0xff;
+
+ memset (&tp->private, 0, sizeof(tp->private));
+ memset (&capdata, 0, sizeof(capdata));
+
+ for (pass = 1; passes == 0 || pass < passes; pass++) {
+ lockpat = (pass & 0x0fffffff) + (thread <<28);
+ tp->threadpasses = pass;
+ if (checkstop(thread, pass, lockpat))
+ return 0;
+ iter_msg_i++;
+ if (iter_msg && iter_msg_i > iter_msg) {
+ printk("Thread %d, Pass %d\n", thread, pass);
+ iter_msg_i = 0;
+ }
+ lcount = 0;
+
+ /*
+ * Select line to perform operations on.
+ */
+ linei = randn(linecount, &seed);
+ sharei = randn(2, &seed);
+ slinei = (linei + (linecount/2))%linecount; /* I dont like this - fix later */
+
+ linep = (dataline_t *)blocks[linei];
+ slinep = (dataline_t *)blocks[slinei];
+ if (sharei == 0)
+ sharecopy = &slinep->share0;
+ else
+ sharecopy = &slinep->share1;
+
+
+ vv = randn(4, &seed);
+ if ((vv_mask & (1<<vv)) == 0)
+ continue;
+
+ if (napticks) {
+ t = randn(napticks, &seed);
+ udelay(t);
+ }
+ privp = &linep->private[thread];
+ sharep = &linep->share[sharei];
+
+ switch(vv) {
+ case 0:
+ /* Read and verify private count on line. */
+ pval = *privp;
+ if (verbose)
+ printk("Line:%3d, Thread:%d:%d. Val: %x\n", linei, thread, vv, tp->private[linei]);
+ if (pval != tp->private[linei]) {
+ capturedata(&capdata, pass, privp, NULL, sizeof(*privp));
+ stillbad = (*privp != tp->private[linei]);
+ if (rerr(&capdata, "Private count", linep, slinep, thread, pass, linei, tp->private[linei], pval, stillbad)) {
+ return 1;
+ }
+ if (correct_errors) {
+ flush_buddy(privp);
+ tp->private[linei] = *privp;
+ }
+ errs++;
+ }
+ break;
+
+ case 1:
+ /* Read, verify, and increment private count on line. */
+ pval = *privp;
+ if (verbose)
+ printk("Line:%3d, Thread:%d:%d. Val: %x\n", linei, thread, vv, tp->private[linei]);
+ if (pval != tp->private[linei]) {
+ capturedata(&capdata, pass, privp, NULL, sizeof(*privp));
+ stillbad = (*privp != tp->private[linei]);
+ if (rerr(&capdata, "Private count & inc", linep, slinep, thread, pass, linei, tp->private[linei], pval, stillbad)) {
+ return 1;
+ }
+ errs++;
+ }
+ pval++;
+ flush_buddy(privp);
+ *privp = pval;
+ tp->private[linei] = pval;
+ break;
+
+ case 2:
+ /* Lock line, read and verify shared data. */
+ if (verbose)
+ printk("Line:%3d, Thread:%d:%d. Val: %x\n", linei, thread, vv, *sharecopy);
+ lcount = 0;
+ while (LOCK(sharei) != 1) {
+ if (checkstop(thread, pass, lockpat))
+ return 0;
+ if (lcount++>1000000) {
+ capturedata(&capdata, pass, LOCKADDR(sharei), NULL, sizeof(lock_t));
+ stillbad = (GETLOCK(sharei) != 0);
+ rerr(&capdata, "Shared data lock", linep, slinep, thread, pass, linei, 0, GETLOCK(sharei), stillbad);
+ return 1;
+ }
+ if ((lcount&0x3fff) == 0)
+ udelay(1000);
+ }
+
+ sval = *sharep;
+ sval2 = *sharecopy;
+ if (pass > 12 && thread == 0 && fail_enabled == 1)
+ sval++;
+ if (sval != sval2) {
+ capturedata(&capdata, pass, sharep, sharecopy, sizeof(*sharecopy));
+ stillbad = (*sharep != *sharecopy);
+ if (!stillbad && *sharep != sval && *sharecopy == sval2)
+ stillbad = 2;
+ if (rerr(&capdata, "Shared data", linep, slinep, thread, pass, linei, sval2, sval, stillbad)) {
+ return 1;
+ }
+ if (correct_errors)
+ *sharep = *sharecopy;
+ errs++;
+ }
+
+
+ if ( (s=UNLOCK(sharei)) != 1) {
+ capturedata(&capdata, pass, LOCKADDR(sharei), NULL, 4);
+ stillbad = (GETLOCK(sharei) != lockpat);
+ if (rerr(&capdata, "Shared data unlock", linep, slinep, thread, pass, linei, lockpat, GETLOCK(sharei), stillbad))
+ return 1;
+ if (correct_errors)
+ ZEROLOCK(sharei);
+ errs++;
+ }
+ break;
+
+ case 3:
+ /* Lock line, read and verify shared data, modify shared data. */
+ if (verbose)
+ printk("Line:%3d, Thread:%d:%d. Val: %x\n", linei, thread, vv, *sharecopy);
+ lcount = 0;
+ while (LOCK(sharei) != 1) {
+ if (checkstop(thread, pass, lockpat))
+ return 0;
+ if (lcount++>1000000) {
+ capturedata(&capdata, pass, LOCKADDR(sharei), NULL, sizeof(lock_t));
+ stillbad = (GETLOCK(sharei) != 0);
+ rerr(&capdata, "Shared data lock & inc", linep, slinep, thread, pass, linei, 0, GETLOCK(sharei), stillbad);
+ return 1;
+ }
+ if ((lcount&0x3fff) == 0)
+ udelay(1000);
+ }
+ sval = *sharep;
+ sval2 = *sharecopy;
+ if (sval != sval2) {
+ capturedata(&capdata, pass, sharep, sharecopy, sizeof(*sharecopy));
+ stillbad = (*sharep != *sharecopy);
+ if (!stillbad && *sharep != sval && *sharecopy == sval2)
+ stillbad = 2;
+ if (rerr(&capdata, "Shared data & inc", linep, slinep, thread, pass, linei, sval2, sval, stillbad)) {
+ return 1;
+ }
+ errs++;
+ }
+
+ flush_buddy(sharep);
+ *sharep = lockpat;
+ flush_buddy(sharecopy);
+ *sharecopy = lockpat;
+
+
+ if ( (s=UNLOCK(sharei)) != 1) {
+ capturedata(&capdata, pass, LOCKADDR(sharei), NULL, 4);
+ stillbad = (GETLOCK(sharei) != lockpat);
+ if (rerr(&capdata, "Shared data & inc unlock", linep, slinep, thread, pass, linei, thread, GETLOCK(sharei), stillbad))
+ return 1;
+ if (correct_errors)
+ ZEROLOCK(sharei);
+ errs++;
+ }
+ break;
+ }
+ }
+
+ return (errs > 0);
+}
+
+static void
+trigger_la(long val)
+{
+ long *p;
+
+ p = (long*)0xc0000a0001000020L; /* PI_CPU_NUM */
+ *p = val;
+}
+
+static long
+getsynerr(void)
+{
+ long err, *errp;
+
+ errp = (long*)0xc0000e0000000340L; /* SYN_ERR */
+ err = *errp;
+ if (err)
+ *errp = -1L;
+ return (err & ~0x60);
+}
+
+static int
+rerr(capture_t *cap, char *msg, void *lp, void *slp, int thread, int pass, int linei, int exp, int found, int stillbad)
+{
+ int cpu;
+ long synerr;
+ int selt;
+
+
+ selt = selective_trigger && stillbad > 1 &&
+ memcmp(cap->blockdata, cap->blockdata_fc, 128) != 0 &&
+ memcmp(cap->shaddata, cap->shaddata_fc, 128) == 0;
+ if (selt) {
+ trigger_la(pass);
+ } else if (selective_trigger) {
+ k_go = ST_STOP;
+ return k_stop_on_error;;
+ }
+
+ spin(1);
+ printk ("\nDataError!: %-20s, test %ld, thread %d, line:%d, pass %d (0x%x), time %ld expected:%x, found:%x\n",
+ msg, k_testnumber, thread, linei, pass, pass, jiffies, exp, found);
+
+ dumpline (lp, "Corrupted data", "D ", cap->blockaddr, cap->blockdata, cap->size);
+ if (memcmp(cap->blockdata, cap->blockdata_fc, 128))
+ dumpline (lp, "Corrupted data", "DF", cap->blockaddr, cap->blockdata_fc, cap->size);
+
+ if (cap->shadaddr) {
+ dumpline (slp, "Shadow data", "S ", cap->shadaddr, cap->shaddata, cap->size);
+ if (memcmp(cap->shaddata, cap->shaddata_fc, 128))
+ dumpline (slp, "Shadow data", "SF", cap->shadaddr, cap->shaddata_fc, cap->size);
+ }
+
+ printk("Threadpasses: ");
+ for (cpu=0; cpu<MAXCPUS; cpu++)
+ if (k_threadprivate[cpu]->threadpasses)
+ printk(" %d:0x%x", cpu, k_threadprivate[cpu]->threadpasses);
+
+
+ printk("\nData was %sfixed by flushcache\n", (stillbad == 1 ? "**** NOT **** " : " "));
+ synerr = getsynerr();
+ if (synerr)
+ printk("SYNERR: Thread %d, Synerr: 0x%lx\n", thread, synerr);
+ spin(2);
+ printk("\n\n");
+
+ if (errstop_enabled) {
+ local_irq_disable();
+ while(1);
+ }
+ return k_stop_on_error;
+}
+
+
+static void
+dumpline(void *lp, char *str1, char *str2, void *addr, void *data, int size)
+{
+ long *p;
+ int i, off;
+
+ printk("%s at 0x%lx, size %d, block starts at 0x%lx\n", str1, (long)addr, size, (long)lp);
+ p = (long*) data;
+ for (i=0; i<16; i++, p++) {
+ if (i==0) printk("%2s", str2);
+ if (i==8) printk(" ");
+ printk(" %016lx", *p);
+ if ((i&7)==7) printk("\n");
+ }
+ printk(" ");
+ off = (((long)addr) ^ size) & 63L;
+ for (i=0; i<off+size; i++) {
+ printk("%s", (i>=off) ? "--" : " ");
+ if ((i%8) == 7)
+ printk(" ");
+ }
+
+ off = ((long)addr) & 127;
+ printk(" (line %d)\n", off/64+1);
+}
+
+
+static int
+randn(uint max, uint *seedp)
+{
+ if (max == 1)
+ return(0);
+ else
+ return((int)(zrandom(seedp)>>10) % max);
+}
+
+
+static int
+checkstop(int thread, int pass, uint lockpat)
+{
+ long synerr;
+
+ if (k_go == ST_RUN)
+ return 0;
+ if (k_go == ST_STOP)
+ return 1;
+
+ if (errstop_enabled) {
+ local_irq_disable();
+ while(1);
+ }
+ synerr = getsynerr();
+ spin(2);
+ if (k_go == ST_STOP)
+ return 1;
+ if (synerr)
+ printk("SYNERR: Thread %d, Synerr: 0x%lx\n", thread, synerr);
+ return 1;
+}
+
+
+static void
+spin(int j)
+{
+ udelay(j * 500000);
+}
+
+static void
+capturedata(capture_t *cap, uint pass, void *blockaddr, void *shadaddr, int size)
+{
+
+ if (!selective_trigger)
+ trigger_la (pass);
+
+ memcpy (cap->blockdata, CACHEALIGN(blockaddr), 128);
+ if (shadaddr)
+ memcpy (cap->shaddata, CACHEALIGN(shadaddr), 128);
+
+ if (k_stop_on_error) {
+ k_go = ST_ERRSTOP;
+ }
+
+ cap->size = size;
+ cap->blockaddr = blockaddr;
+ cap->shadaddr = shadaddr;
+
+ asm volatile ("fc %0" :: "r"(blockaddr) : "memory");
+ ia64_sync_i();
+ ia64_srlz_d();
+ memcpy (cap->blockdata_fc, CACHEALIGN(blockaddr), 128);
+
+ if (shadaddr) {
+ asm volatile ("fc %0" :: "r"(shadaddr) : "memory");
+ ia64_sync_i();
+ ia64_srlz_d();
+ memcpy (cap->shaddata_fc, CACHEALIGN(shadaddr), 128);
+ }
+}
+
+int zranmult = 0x48c27395;
+
+static uint
+zrandom (uint *seedp)
+{
+ *seedp = (*seedp * zranmult) & 0x7fffffff;
+ return (*seedp);
+}
+
+
+void
+set_autotest_params(void)
+{
+ static int testnumber=-1;
+
+ if (llsctest_number >= 0) {
+ testnumber = llsctest_number;
+ } else {
+ testnumber++;
+ if (autotest_table[testnumber].passes == 0)
+ testnumber = 0;
+ }
+ k_passes = autotest_table[testnumber].passes;
+ k_linepad = autotest_table[testnumber].linepad;
+ k_linecount = autotest_table[testnumber].linecount;
+ k_testnumber = testnumber;
+
+ if (IS_RUNNING_ON_SIMULATOR()) {
+ printk ("llsc start test %ld\n", k_testnumber);
+ k_passes = 1000;
+ }
+}
+
+
+static void
+set_leds(int errs)
+{
+ unsigned char leds=0;
+
+ /*
+ * Leds are:
+ * ppppeee-
+ * where
+ * pppp = test number
+ * eee = error count but top bit is stick
+ */
+
+ leds = ((errs&7)<<1) | ((k_testnumber&15)<<4) | (errs ? 0x08 : 0);
+ bringup_set_led_bits(leds, 0xfe);
+}
+
+static void
+setup_block_addresses(void)
+{
+ int i, stride, memmapi;
+
+ stride = LINESTRIDE;
+ memmapi = 0;
+ for (i=0; i<memmapx; i++) {
+ memmap[i].nextaddr = memmap[i].vstart;
+ memmap[i].wrapcount = 0;
+ }
+
+ for (i=0; i<k_linecount; i++) {
+ blocks[i] = memmap[memmapi].nextaddr;
+ memmap[memmapi].nextaddr += stride;
+ if (memmap[memmapi].nextaddr + sizeof(dataline_t) >= memmap[memmapi].vend) {
+ memmap[memmapi].wrapcount++;
+ memmap[memmapi].nextaddr = memmap[memmapi].vstart +
+ memmap[memmapi].wrapcount * sizeof(dataline_t);
+ }
+
+ memset((void*)blocks[i], 0, sizeof(dataline_t));
+
+ if (stride > 16384) {
+ memmapi++;
+ if (memmapi == memmapx)
+ memmapi = 0;
+ }
+ }
+
+}
+
+static void
+set_thread_state(int cpuid, int state)
+{
+ if (k_threadprivate[cpuid]->threadstate == TS_KILLED) {
+ bringup_set_led_bits(0xfe, 0xfe);
+ while(1);
+ }
+ k_threadprivate[cpuid]->threadstate = state;
+}
+
+static int
+build_mem_map(unsigned long start, unsigned long end, void *arg)
+{
+ long lstart;
+ /*
+ * HACK - skip the kernel on the first node
+ */
+
+ printk ("LLSC memmap: start 0x%lx, end 0x%lx, (0x%lx - 0x%lx)\n",
+ start, end, (long) virt_to_page(start), (long) virt_to_page(end-PAGE_SIZE));
+
+ while (end > start && (PageReserved(virt_to_page(end-PAGE_SIZE)) || virt_to_page(end-PAGE_SIZE)->count.counter > 0))
+ end -= PAGE_SIZE;
+
+ lstart = end;
+ while (lstart > start && (!PageReserved(virt_to_page(lstart-PAGE_SIZE)) && virt_to_page(lstart-PAGE_SIZE)->count.counter == 0))
+ lstart -= PAGE_SIZE;
+
+ printk (" memmap: start 0x%lx, end 0x%lx\n", lstart, end);
+ if (lstart >= end)
+ return 0;
+
+ memmap[memmapx].vstart = lstart;
+ memmap[memmapx].vend = end;
+ memmapx++;
+ return 0;
+}
+
+void int_test(void);
+
+int
+llsc_main (int cpuid, long mbasex)
+{
+ int i, cpu, is_master, repeatcnt=0;
+ unsigned int preverr=0, errs=0, pass=0;
+ int automode=0;
+
+#ifdef INTTEST
+ if (inttest)
+ int_test();
+#endif
+
+ if (!autotest_enabled)
+ return 0;
+
+#ifdef CONFIG_SMP
+ is_master = !smp_processor_id();
+#else
+ is_master = 1;
+#endif
+
+
+ if (is_master) {
+ print_params();
+ if(!IS_RUNNING_ON_SIMULATOR())
+ spin(10);
+ mbase = (control_t*)mbasex;
+ k_currentpass = 0;
+ k_go = ST_IDLE;
+ k_passes = DEF_PASSES;
+ k_napticks = DEF_NAPTICKS;
+ k_stop_on_error = DEF_STOP_ON_ERROR;
+ k_verbose = DEF_VERBOSE;
+ k_linecount = DEF_LINECOUNT;
+ k_iter_msg = DEF_ITER_MSG;
+ k_vv = DEF_VV;
+ k_linepad = DEF_LINEPAD;
+ k_blocks = (void*)blocks;
+ efi_memmap_walk(build_mem_map, 0);
+
+#ifdef CONFIG_IA64_SGI_AUTOTEST
+ automode = 1;
+#endif
+
+ for (i=0; i<MAXCPUS; i++) {
+ k_threadprivate[i] = THREADPRIVATE(i);
+ memset(k_threadprivate[i], 0, sizeof(*k_threadprivate[i]));
+ }
+ initialized = 1;
+ } else {
+ while (initialized == 0)
+ udelay(100);
+ }
+
+loop:
+ if (is_master) {
+ if (automode) {
+ if (!preverr || repeatcnt++ > 5) {
+ set_autotest_params();
+ repeatcnt = 0;
+ }
+ } else {
+ while (k_go == ST_IDLE);
+ }
+
+ k_go = ST_INIT;
+ if (k_linecount > MAX_LINECOUNT) k_linecount = MAX_LINECOUNT;
+ k_linecount = k_linecount & ~1;
+ setup_block_addresses();
+
+ k_currentpass = pass++;
+ k_go = ST_RUN;
+ if (fail_enabled)
+ fail_enabled--;
+
+ } else {
+ while (k_go != ST_RUN || k_currentpass != pass);
+ pass++;
+ }
+
+
+ set_leds(errs);
+ set_thread_state(cpuid, TS_RUNNING);
+
+ errs += ran_conf_llsc(cpuid);
+ preverr = (k_go == ST_ERRSTOP);
+
+ set_leds(errs);
+ set_thread_state(cpuid, TS_STOPPED);
+
+ if (is_master) {
+ Speedo();
+ for (i=0, cpu=0; cpu<MAXCPUS; cpu++) {
+ while (k_threadprivate[cpu]->threadstate == TS_RUNNING) {
+ i++;
+ if (i == 10000) {
+ k_go = ST_STOP;
+ printk (" llsc master stopping test number %ld\n", k_testnumber);
+ }
+ if (i > 100000) {
+ k_threadprivate[cpu]->threadstate = TS_KILLED;
+ printk (" llsc: master killing cpuid %d, running test number %ld\n",
+ cpu, k_testnumber);
+ }
+ udelay(1000);
+ }
+ }
+ }
+
+ goto loop;
+}
+
+
+static void
+Speedo(void)
+{
+ static int i = 0;
+
+ switch (++i%4) {
+ case 0:
+ printk("|\b");
+ break;
+ case 1:
+ printk("\\\b");
+ break;
+ case 2:
+ printk("-\b");
+ break;
+ case 3:
+ printk("/\b");
+ break;
+ }
+}
+
+#ifdef INTTEST
+
+/* ========================================================================================================
+ *
+ * Some test code to verify that interrupts work
+ *
+ * Add the following to the arch/ia64/kernel/smp.c after the comment "Reschedule callback"
+ * if (zzzprint_resched) printk(" cpu %d got interrupt\n", smp_processor_id());
+ *
+ * Enable the code in arch/ia64/sn/sn1/smp.c to print sending IPIs.
+ *
+ */
+
+static int __init set_inttest(char *str)
+{
+ inttest = 1;
+ autotest_enabled = 1;
+
+ return 1;
+}
+
+__setup("inttest=", set_inttest);
+
+int zzzprint_resched=0;
+
+void
+int_test() {
+ int mycpu, cpu;
+ static volatile int control_cpu=0;
+
+ mycpu = smp_processor_id();
+ zzzprint_resched = 2;
+
+ printk("Testing cross interrupts\n");
+
+ while (control_cpu != smp_num_cpus) {
+ if (mycpu == cpu_logical_map(control_cpu)) {
+ for (cpu=0; cpu<smp_num_cpus; cpu++) {
+ printk("Sending interrupt from %d to %d\n", mycpu, cpu_logical_map(cpu));
+ udelay(IS_RUNNING_ON_SIMULATOR ? 10000 : 400000);
+ smp_send_reschedule(cpu_logical_map(cpu));
+ udelay(IS_RUNNING_ON_SIMULATOR ? 10000 : 400000);
+ smp_send_reschedule(cpu_logical_map(cpu));
+ udelay(IS_RUNNING_ON_SIMULATOR ? 10000 : 400000);
+ }
+ control_cpu++;
+ }
+ }
+
+ zzzprint_resched = 1;
+
+ if (mycpu == cpu_logical_map(smp_num_cpus-1)) {
+ printk("\nTight loop of cpu %d sending ints to cpu 0 (every 100 us)\n", mycpu);
+ udelay(IS_RUNNING_ON_SIMULATOR ? 1000 : 1000000);
+ __cli();
+ while (1) {
+ smp_send_reschedule(0);
+ udelay(100);
+ }
+
+ }
+
+ while(1);
+}
+#endif
diff --git a/arch/ia64/sn/sn1/llsc4.h b/arch/ia64/sn/sn1/llsc4.h
new file mode 100644
index 000000000..b305caf2b
--- /dev/null
+++ b/arch/ia64/sn/sn1/llsc4.h
@@ -0,0 +1,104 @@
+/*
+ *
+ * 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 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Jack Steiner (steiner@sgi.com)
+ */
+
+#ifdef STANDALONE
+#include "lock.h"
+#endif
+
+
+#define DEF_NAPTICKS 0
+#define DEF_PASSES 0
+#define DEF_AUTO_PASSES 1000000
+#define DEF_STOP_ON_ERROR 1
+#define DEF_VERBOSE 0
+#define DEF_LINECOUNT 2
+#define DEF_ITER_MSG 0
+#define DEF_VV 0xffffffff
+#define DEF_LINEPAD 0x234
+
+
+
+#define MAXCPUS 16
+#define CACHELINE 64
+#define MAX_LINECOUNT 1024
+#define K 1024
+#define MB (K*K)
+
+
+#define uint unsigned int
+#define ushort unsigned short
+#define vint volatile int
+#define vlong volatile long
+
+#define LOCKADDR(i) &linep->lock[(i)]
+#define LOCK(i) set_lock(LOCKADDR(i), lockpat)
+#define UNLOCK(i) clr_lock(LOCKADDR(i), lockpat)
+#define GETLOCK(i) *LOCKADDR(i)
+#define ZEROLOCK(i) zero_lock(LOCKADDR(i))
+
+#define CACHEALIGN(a) ((void*)((long)(a) & ~127L))
+
+typedef uint lock_t;
+typedef uint share_t;
+typedef uint private_t;
+
+typedef struct {
+ lock_t lock[2];
+ share_t share[2];
+ private_t private[MAXCPUS];
+ share_t share0;
+ share_t share1;
+} dataline_t ;
+
+
+#define LINEPAD k_linepad
+#define LINESTRIDE (((sizeof(dataline_t)+CACHELINE-1)/CACHELINE)*CACHELINE + LINEPAD)
+
+
+typedef struct {
+ vint threadstate;
+ uint threadpasses;
+ private_t private[MAX_LINECOUNT];
+} threadprivate_t;
+
+typedef struct {
+ vlong sk_go; /* 0=idle, 1=init, 2=run */
+ long sk_linecount;
+ long sk_passes;
+ long sk_napticks;
+ long sk_stop_on_error;
+ long sk_verbose;
+ long sk_iter_msg;
+ long sk_vv;
+ long sk_linepad;
+ long sk_options;
+ long sk_testnumber;
+ vlong sk_currentpass;
+ void *sk_blocks;
+ threadprivate_t *sk_threadprivate[MAXCPUS];
+} control_t;
+
+/* Run state (k_go) constants */
+#define ST_IDLE 0
+#define ST_INIT 1
+#define ST_RUN 2
+#define ST_STOP 3
+#define ST_ERRSTOP 4
+
+
+/* Threadstate constants */
+#define TS_STOPPED 0
+#define TS_RUNNING 1
+#define TS_KILLED 2
+
+
+
+int llsc_main (int cpuid, long mbasex);
+
diff --git a/arch/ia64/sn/sn1/mm.c b/arch/ia64/sn/sn1/mm.c
new file mode 100644
index 000000000..e0010782c
--- /dev/null
+++ b/arch/ia64/sn/sn1/mm.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright, 2000, Silicon Graphics.
+ * Copyright Srinivasa Thirumalachar (sprasad@engr.sgi.com)
+ * Copyright 2000 Kanoj Sarcar (kanoj@sgi.com)
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/bootmem.h>
+#include <asm/page.h>
+#include <asm/efi.h>
+#include <asm/sn/mmzone_sn1.h>
+
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+# define MAX(a,b) ((a) > (b) ? (a) : (b))
+
+/*
+ * Note that the nodemem[] data structure does not support arbitrary
+ * memory types and memory descriptors inside the node. For example,
+ * you can not have multiple efi-mem-type segments in the node and
+ * expect the OS not to use specific mem-types. Currently, the
+ * assumption is that "start" is the start of virtual/physical memory
+ * on the node. PROM can reserve some memory _only_ at the beginning.
+ * This is tracked via the "usable" field, that maintains where the
+ * os can start using memory from on a node (ie end of PROM memory).
+ * setup_node_bootmem() is passed the above "usable" value, and is
+ * expected to make bootmem calls that ensure lower memory is not used.
+ * Note that the bootmem for a node is initialized on the entire node,
+ * without regards to any holes - then we reserve the holes in
+ * setup_sn1_bootmem(), to make sure the holes are not handed out by
+ * alloc_bootmem, as well as the corresponding mem_map entries are not
+ * considered allocatable by the page_alloc routines.
+ */
+struct nodemem_s {
+ u64 start ;
+ u64 end ;
+ u64 hole[SN1_MAX_BANK_PER_NODE] ;
+ u64 usable;
+} nodemem[MAXNODES] ;
+static int nodemem_valid = 0;
+
+static int __init
+free_unused_memmap_hole(int nid, unsigned long start, unsigned long end)
+{
+ struct page * page, *pageend;
+ unsigned long count = 0;
+
+ if (start >= end)
+ return 0 ;
+
+ /*
+ * Get the memmap ptrs to the start and end of the holes.
+ * virt_to_page(start) will panic, if start is in hole.
+ * Can we do virt_to_page(end), if end is on the next node?
+ */
+
+ page = virt_to_page(start-1);
+ page++ ;
+ pageend = virt_to_page(end) ;
+
+ printk("hpage=0x%lx, hpageend=0x%lx\n", (u64)page, (u64)pageend) ;
+ free_bootmem_node(NODE_DATA(nid), __pa(page), (u64)pageend - (u64)page);
+
+ return count ;
+}
+
+void
+free_unused_memmap_node(int nid)
+{
+ u64 i = 0 ;
+ u64 holestart = -1 ;
+
+ do {
+ holestart = nodemem[nid].hole[i] ;
+ i++ ;
+ while ((i < SN1_MAX_BANK_PER_NODE) &&
+ (nodemem[nid].hole[i] == (u64)-1))
+ i++ ;
+ if (i < SN1_MAX_BANK_PER_NODE)
+ free_unused_memmap_hole(nid, holestart,
+ nodemem[nid].start + (i<<SN1_BANK_ADDR_SHIFT));
+ } while (i<SN1_MAX_BANK_PER_NODE);
+}
+
+/*
+ * Since efi_memmap_walk merges contiguous banks, this code will need
+ * to find all the nasid/banks covered by the input memory descriptor.
+ */
+static int __init
+build_nodemem_map(unsigned long start, unsigned long end, void *arg)
+{
+ unsigned long vaddr = start;
+ unsigned long nvaddr;
+ int nasid = GetNasId(__pa(vaddr));
+ int cnodeid, bankid;
+
+ while (vaddr < end) {
+ cnodeid = NASID_TO_CNODEID(nasid);
+ bankid = GetBankId(__pa(vaddr));
+ nodemem[cnodeid].start = MIN(nodemem[cnodeid].start, vaddr);
+ nodemem[cnodeid].usable = MIN(nodemem[cnodeid].usable, vaddr);
+ nvaddr = (unsigned long)__va((unsigned long)(++nasid) <<
+ SN1_NODE_ADDR_SHIFT);
+ nodemem[cnodeid].end = MAX(nodemem[cnodeid].end, MIN(end, nvaddr));
+ while ((bankid < SN1_MAX_BANK_PER_NODE) &&
+ (vaddr < nodemem[cnodeid].end)) {
+ nvaddr = nodemem[cnodeid].start +
+ ((unsigned long)(bankid + 1) << SN1_BANK_ADDR_SHIFT);
+ nodemem[cnodeid].hole[bankid++] = MIN(nvaddr, end);
+ vaddr = nvaddr;
+ }
+ }
+
+ return 0;
+}
+
+static int __init
+pgtbl_size_ok(int nid)
+{
+ unsigned long numpfn, bank0size, nodesize ;
+
+ nodesize = nodemem[nid].end - nodemem[nid].start ;
+ numpfn = nodesize >> PAGE_SHIFT;
+
+ bank0size = nodemem[nid].hole[0] - nodemem[nid].start ;
+ /* If nid == master node && no kernel text replication */
+ bank0size -= 0xA00000 ; /* Kernel text + stuff */
+ bank0size -= ((numpfn + 7) >> 3);
+
+ if ((numpfn * sizeof(mem_map_t)) > bank0size) {
+ printk("nid = %d, ns=0x%lx, npfn=0x%lx, bank0size=0x%lx\n",
+ nid, nodesize, numpfn, bank0size) ;
+ return 0 ;
+ }
+
+ return 1 ;
+}
+
+static void __init
+check_pgtbl_size(int nid)
+{
+ int bank = SN1_MAX_BANK_PER_NODE - 1 ;
+
+ /* Find highest bank with valid memory */
+ while ((nodemem[nid].hole[bank] == -1) && (bank))
+ bank-- ;
+
+ while (!pgtbl_size_ok(nid)) {
+ /* Remove that bank of memory */
+ /* Collect some numbers later */
+ printk("Ignoring node %d bank %d\n", nid, bank) ;
+ nodemem[nid].hole[bank--] = -1 ;
+ /* Get to the next populated bank */
+ while ((nodemem[nid].hole[bank] == -1) && (bank))
+ bank-- ;
+ printk("Using only upto bank %d on node %d\n", bank,nid) ;
+ nodemem[nid].end = nodemem[nid].hole[bank] ;
+ if (!bank) break ;
+ }
+}
+
+void dump_nodemem_map(int) ;
+
+#ifdef CONFIG_DISCONTIGMEM
+
+extern bootmem_data_t bdata[] ;
+static int curnodeid ;
+
+static int __init
+setup_node_bootmem(unsigned long start, unsigned long end, unsigned long nodefree)
+{
+ extern char _end;
+ int i;
+ unsigned long kernelend = PAGE_ALIGN((unsigned long)(&_end));
+ unsigned long pkernelend = __pa(kernelend);
+ unsigned long bootmap_start, bootmap_size;
+ unsigned long pstart, pend;
+
+ pstart = __pa(start) ;
+ pend = __pa(end) ;
+
+ /* If we are past a node mem boundary, on simulated dig numa
+ * increment current node id. */
+
+ curnodeid = NASID_TO_CNODEID(GetNasId(pstart)) ;
+
+ /*
+ * Make sure we are being passed page aligned addresses.
+ */
+ if ((start & (PAGE_SIZE - 1)) || (end & (PAGE_SIZE - 1)))
+ panic("setup_node_bootmem:align");
+
+
+ /* For now, just go to the lower CHUNK alignment so that
+ * chunktonid of 0-8MB and other lower mem pages get initted. */
+
+ pstart &= CHUNKMASK ;
+ pend = (pend+CHUNKSZ-1) & CHUNKMASK;
+
+ /* If pend == 0, both addrs below 8 MB, special case it
+ * FIX: CHUNKNUM(pend-1) broken if pend == 0
+ * both addrs within 8MB */
+
+ if (pend == 0) {
+ chunktonid[0] = 0;
+ return 0;
+ }
+
+ /* Fill up the chunktonid array first. */
+
+ for (i = PCHUNKNUM(pstart); i <= PCHUNKNUM(pend-1); i++)
+ chunktonid[i] = curnodeid;
+
+ /* This check is bogus for now till MAXCHUNKS is properly
+ * defined to say if it includes holes or not. */
+
+ if ((CHUNKTONID(PCHUNKNUM(pend)) > MAXCHUNKS) ||
+ (PCHUNKNUM(pstart) >= PCHUNKNUM(pend))) {
+ printk("Ign 0x%lx-0x%lx, ", __pa(start), __pa(end));
+ return(0);
+ }
+
+ /* This routine gets called many times in node 0.
+ * The first one to reach here would be the one after
+ * kernelend to end of first node. */
+
+ NODE_DATA(curnodeid)->bdata = &(bdata[curnodeid]);
+
+ if (curnodeid == 0) {
+ /* for master node, forcibly assign these values
+ * This gets called many times on dig but we
+ * want these exact values
+ * Also on softsdv, the memdesc for 0 is missing */
+ NODE_START(curnodeid) = PAGE_OFFSET;
+ NODE_SIZE(curnodeid) = (end - PAGE_OFFSET);
+ } else {
+ /* This gets called only once for non zero nodes
+ * If it does not, then NODE_STARt should be
+ * LOCAL_BASE(nid) */
+
+ NODE_START(curnodeid) = start;
+ NODE_SIZE(curnodeid) = (end - start);
+ }
+
+ /* if end < kernelend do not do anything below this */
+ if (pend < pkernelend)
+ return 0 ;
+
+ /*
+ * Handle the node that contains kernel text/data. It would
+ * be nice if the loader loads the kernel at a "chunk", ie
+ * not in memory that the kernel will ignore (else free_initmem
+ * has to worry about not freeing memory that the kernel ignores).
+ * Note that we assume the space from the node start to
+ * KERNEL_START can not hold all the bootmem data, but from kernel
+ * end to node end can.
+ */
+
+ /* TBD: This may be bogus in light of the above check. */
+
+ if ((pstart < pkernelend) && (pend >= pkernelend)) {
+ bootmap_start = pkernelend;
+ } else {
+ bootmap_start = __pa(start); /* chunk & page aligned */
+ }
+
+ /*
+ * Low memory is reserved for PROM use on SN1. The current node
+ * memory model is [PROM mem ... kernel ... free], where the
+ * first two components are optional on a node.
+ */
+ if (bootmap_start < __pa(nodefree))
+ bootmap_start = __pa(nodefree);
+
+/* XXX TBD */
+/* For curnodeid of 0, this gets called many times because of many
+ * < 8MB segments. start gets bumped each time. We want to fix it
+ * to 0 now.
+ */
+ if (curnodeid == 0)
+ start=PAGE_OFFSET;
+/*
+ * This makes sure that in free_area_init_core - paging_init
+ * idx is the entire node page range and for loop goes thro
+ * all pages. test_bit for kernel pages should remain reserved
+ * because free available mem takes care of kernel_start and end
+ */
+
+ bootmap_size = init_bootmem_node(NODE_DATA(curnodeid),
+ (bootmap_start >> PAGE_SHIFT),
+ (__pa(start) >> PAGE_SHIFT), (__pa(end) >> PAGE_SHIFT));
+
+ free_bootmem_node(NODE_DATA(curnodeid), bootmap_start + bootmap_size,
+ __pa(end) - (bootmap_start + bootmap_size));
+
+ return(0);
+}
+
+void
+setup_sn1_bootmem(int maxnodes)
+{
+ int i;
+
+ for (i=0;i<MAXNODES;i++) {
+ nodemem[i].usable = nodemem[i].start = -1 ;
+ nodemem[i].end = 0 ;
+ memset(&nodemem[i].hole, -1, sizeof(nodemem[i].hole)) ;
+ }
+ efi_memmap_walk(build_nodemem_map, 0) ;
+
+ /*
+ * Run thru all the nodes, adjusting their starts. This is needed
+ * because efi_memmap_walk() might not process certain mds that
+ * are marked reserved for PROM at node low memory.
+ */
+ for (i = 0; i < maxnodes; i++)
+ nodemem[i].start = ((nodemem[i].start >> SN1_NODE_ADDR_SHIFT) <<
+ SN1_NODE_ADDR_SHIFT);
+ nodemem_valid = 1 ;
+
+ /* After building the nodemem map, check if the page table
+ * will fit in the first bank of each node. If not change
+ * the node end addr till it fits. We dont want to do this
+ * in mm/page_alloc.c
+ */
+
+ for (i=0;i<maxnodes;i++)
+ check_pgtbl_size(i) ;
+
+ for (i=0;i<maxnodes;i++)
+ setup_node_bootmem(nodemem[i].start, nodemem[i].end, nodemem[i].usable);
+
+ /*
+ * Mark the holes as reserved, so the corresponding mem_map
+ * entries will not be marked allocatable in free_all_bootmem*().
+ */
+ for (i = 0; i < maxnodes; i++) {
+ int j = 0 ;
+ u64 holestart = -1 ;
+
+ do {
+ holestart = nodemem[i].hole[j++];
+ while ((j < SN1_MAX_BANK_PER_NODE) &&
+ (nodemem[i].hole[j] == (u64)-1))
+ j++;
+ if (j < SN1_MAX_BANK_PER_NODE)
+ reserve_bootmem_node(NODE_DATA(i),
+ __pa(holestart), (nodemem[i].start +
+ ((long)j << SN1_BANK_ADDR_SHIFT) -
+ holestart));
+ } while (j < SN1_MAX_BANK_PER_NODE);
+ }
+
+ dump_nodemem_map(maxnodes) ;
+}
+#endif
+
+/*
+ * This used to be invoked from an SN1 specific hack in efi_memmap_walk.
+ * It tries to ignore banks which the kernel is ignoring because bank 0
+ * is too small to hold the memmap entries for this bank.
+ * The current SN1 efi_memmap_walk callbacks do not need this. That
+ * leaves the generic ia64 callbacks find_max_pfn, count_pages and
+ * count_reserved_pages, of which the first can probably get by without
+ * this, the last two probably need this, although they also can probably
+ * get by.
+ */
+int
+sn1_bank_ignore(u64 start, u64 end)
+{
+ int nid = NASID_TO_CNODEID(GetNasId(__pa(end))) ;
+ int bank = GetBankId(__pa(end)) ;
+
+ if (!nodemem_valid)
+ return 0 ;
+
+ if (nodemem[nid].hole[bank] == -1)
+ return 1 ;
+ else
+ return 0 ;
+}
+
+void
+dump_nodemem_map(int maxnodes)
+{
+ int i,j;
+
+ printk("NODEMEM_S info ....\n") ;
+ printk("Node start end usable\n");
+ for (i=0;i<maxnodes;i++) {
+ printk("%d 0x%lx 0x%lx 0x%lx\n",
+ i, nodemem[i].start, nodemem[i].end, nodemem[i].usable);
+ printk("Holes -> ") ;
+ for (j=0;j<SN1_MAX_BANK_PER_NODE;j++)
+ printk("0x%lx ", nodemem[i].hole[j]) ;
+ printk("\n");
+ }
+}
+
diff --git a/arch/ia64/sn/sn1/setup.c b/arch/ia64/sn/sn1/setup.c
index 7b397bb6b..3bfce39e7 100644
--- a/arch/ia64/sn/sn1/setup.c
+++ b/arch/ia64/sn/sn1/setup.c
@@ -14,13 +14,14 @@
#include <linux/timex.h>
#include <linux/sched.h>
#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <asm/sn/mmzone_sn1.h>
#include <asm/io.h>
#include <asm/machvec.h>
#include <asm/system.h>
#include <asm/processor.h>
-
/*
* The format of "screen_info" is strange, and due to early i386-setup
* code. This is just enough to make the console code think we're on a
@@ -50,29 +51,48 @@ char drive_info[4*16];
unsigned long
sn1_map_nr (unsigned long addr)
{
+#ifdef CONFIG_DISCONTIGMEM
return MAP_NR_SN1(addr);
+#else
+ return MAP_NR_DENSE(addr);
+#endif
}
-void
+void __init
sn1_setup(char **cmdline_p)
{
-
+ extern void init_sn1_smp_config(void);
ROOT_DEV = to_kdev_t(0x0301); /* default to first IDE drive */
+ init_sn1_smp_config();
+#ifdef ZZZ
#if !defined (CONFIG_IA64_SOFTSDV_HACKS)
- /*
- * Program the timer to deliver timer ticks. 0x40 is the I/O port
- * address of PIT counter 0, 0x43 is the I/O port address of the
- * PIT control word.
- */
- request_region(0x40,0x20,"timer");
- outb(0x34, 0x43); /* Control word */
- outb(LATCH & 0xff , 0x40); /* LSB */
- outb(LATCH >> 8, 0x40); /* MSB */
- printk("PIT: LATCH at 0x%x%x for %d HZ\n", LATCH >> 8, LATCH & 0xff, HZ);
+ /*
+ * Program the timer to deliver timer ticks. 0x40 is the I/O port
+ * address of PIT counter 0, 0x43 is the I/O port address of the
+ * PIT control word.
+ */
+ request_region(0x40,0x20,"timer");
+ outb(0x34, 0x43); /* Control word */
+ outb(LATCH & 0xff , 0x40); /* LSB */
+ outb(LATCH >> 8, 0x40); /* MSB */
+ printk("PIT: LATCH at 0x%x%x for %d HZ\n", LATCH >> 8, LATCH & 0xff, HZ);
+#endif
#endif
#ifdef CONFIG_SMP
init_smp_config();
#endif
screen_info = sn1_screen_info;
}
+
+int
+IS_RUNNING_ON_SIMULATOR(void)
+{
+#ifdef CONFIG_IA64_SGI_SN1_SIM
+ long sn;
+ asm("mov %0=cpuid[%1]" : "=r"(sn) : "r"(2));
+ return(sn == SNMAGIC);
+#else
+ return(0);
+#endif
+}
diff --git a/arch/ia64/sn/sn1/smp.c b/arch/ia64/sn/sn1/smp.c
new file mode 100644
index 000000000..a1e26a549
--- /dev/null
+++ b/arch/ia64/sn/sn1/smp.c
@@ -0,0 +1,186 @@
+/*
+ * SN1 Platform specific SMP Support
+ *
+ * Copyright (C) 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 Jack Steiner <steiner@sgi.com>
+ */
+
+
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/threads.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+
+#include <asm/sn/mmzone_sn1.h>
+#include <asm/sal.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <asm/current.h>
+#include <asm/sn/sn_cpuid.h>
+
+
+
+
+/*
+ * The following structure is used to pass params thru smp_call_function
+ * to other cpus for flushing TLB ranges.
+ */
+typedef struct {
+ unsigned long start;
+ unsigned long end;
+ unsigned long nbits;
+} ptc_params_t;
+
+
+/*
+ * The following table/struct is for remembering PTC coherency domains. It
+ * is also used to translate sapicid into cpuids. We dont want to start
+ * cpus unless we know their cache domain.
+ */
+#ifdef PTC_NOTYET
+sn_sapicid_info_t sn_sapicid_info[NR_CPUS];
+#endif
+
+
+
+#ifdef PTC_NOTYET
+/*
+ * NOTE: This is probably not good enough, but I dont want to try to make
+ * it better until I get some statistics on a running system.
+ * At a minimum, we should only send IPIs to 1 processor in each TLB domain
+ * & have it issue a ptc.g on it's own FSB. Also, serialize per FSB, not
+ * globally.
+ *
+ * More likely, we will have to do some work to reduce the frequency of calls to
+ * this routine.
+ */
+
+static void
+sn1_ptc_local(void *arg)
+{
+ ptc_params_t *params = arg;
+ unsigned long start, end, nbits;
+
+ start = params->start;
+ end = params->end;
+ nbits = params->nbits;
+
+ do {
+ __asm__ __volatile__ ("ptc.l %0,%1" :: "r"(start), "r"(nbits<<2) : "memory");
+ start += (1UL << nbits);
+ } while (start < end);
+}
+
+
+void
+sn1_ptc_global (unsigned long start, unsigned long end, unsigned long nbits)
+{
+ ptc_params_t params;
+
+ params.start = start;
+ params.end = end;
+ params.nbits = nbits;
+
+ if (smp_call_function(sn1_ptc_local, &params, 1, 0) != 0)
+ panic("Unable to do ptc_global - timed out");
+
+ sn1_ptc_local(&params);
+}
+#endif
+
+
+
+
+void
+sn1_send_IPI(int cpuid, int vector, int delivery_mode, int redirect)
+{
+ long *p, nasid, slice;
+ static int off[4] = {0x1800080, 0x1800088, 0x1a00080, 0x1a00088};
+
+ /*
+ * ZZZ - Replace with standard macros when available.
+ */
+ nasid = cpuid_to_nasid(cpuid);
+ slice = cpuid_to_slice(cpuid);
+ p = (long*)(0xc0000a0000000000LL | (nasid<<33) | off[slice]);
+
+#if defined(ZZZBRINGUP)
+ {
+ static int count=0;
+ if (count++ < 10) printk("ZZ sendIPI 0x%x->0x%x, vec %d, nasid 0x%lx, slice %ld, adr 0x%lx\n",
+ smp_processor_id(), cpuid, vector, nasid, slice, (long)p);
+ }
+#endif
+ mb();
+ *p = (delivery_mode << 8) | (vector & 0xff);
+
+}
+
+
+#ifdef CONFIG_SMP
+
+static void __init
+process_sal_ptc_domain_info(ia64_sal_ptc_domain_info_t *di, int domain)
+{
+#ifdef PTC_NOTYET
+ ia64_sal_ptc_domain_proc_entry_t *pe;
+ int i, sapicid, cpuid;
+
+ pe = __va(di->proc_list);
+ for (i=0; i<di->proc_count; i++, pe++) {
+ sapicid = id_eid_to_sapicid(pe->id, pe->eid);
+ cpuid = cpu_logical_id(sapicid);
+ sn_sapicid_info[cpuid].domain = domain;
+ sn_sapicid_info[cpuid].sapicid = sapicid;
+ }
+#endif
+}
+
+
+static void __init
+process_sal_desc_ptc(ia64_sal_desc_ptc_t *ptc)
+{
+ ia64_sal_ptc_domain_info_t *di;
+ int i;
+
+ di = __va(ptc->domain_info);
+ for (i=0; i<ptc->num_domains; i++, di++) {
+ process_sal_ptc_domain_info(di, i);
+ }
+}
+
+
+void __init
+init_sn1_smp_config(void)
+{
+
+ if (!ia64_ptc_domain_info) {
+ printk("SMP: Can't find PTC domain info. Forcing UP mode\n");
+ smp_num_cpus = 1;
+ return;
+ }
+
+#ifdef PTC_NOTYET
+ memset (sn_sapicid_info, -1, sizeof(sn_sapicid_info));
+ process_sal_desc_ptc(ia64_ptc_domain_info);
+#endif
+
+}
+
+#else /* CONFIG_SMP */
+
+void __init
+init_sn1_smp_config(void)
+{
+
+#ifdef PTC_NOTYET
+ sn_sapicid_info[0].sapicid = hard_processor_sapicid();
+#endif
+}
+
+#endif /* CONFIG_SMP */
diff --git a/arch/ia64/sn/sn1/sn1_asm.S b/arch/ia64/sn/sn1/sn1_asm.S
new file mode 100644
index 000000000..3419d9374
--- /dev/null
+++ b/arch/ia64/sn/sn1/sn1_asm.S
@@ -0,0 +1,6 @@
+
+/*
+ * Copyright (C) 2000 Silicon Graphics
+ * Copyright (C) 2000 Jack Steiner (steiner@sgi.com)
+ */
+
diff --git a/arch/ia64/sn/sn1/synergy.c b/arch/ia64/sn/sn1/synergy.c
new file mode 100644
index 000000000..76b583c73
--- /dev/null
+++ b/arch/ia64/sn/sn1/synergy.c
@@ -0,0 +1,204 @@
+
+/*
+ * SN1 Platform specific synergy Support
+ *
+ * Copyright (C) 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 Alan Mayer (ajm@sgi.com)
+ */
+
+
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include <asm/ptrace.h>
+#include <linux/devfs_fs_kernel.h>
+#include <asm/smp.h>
+#include <asm/sn/sn_cpuid.h>
+#include <asm/sn/sn1/bedrock.h>
+#include <asm/sn/intr.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/synergy.h>
+
+int bit_pos_to_irq(int bit);
+void setclear_mask_b(int irq, int cpuid, int set);
+void setclear_mask_a(int irq, int cpuid, int set);
+void * kmalloc(size_t size, int flags);
+
+extern struct sn1_cnode_action_list *sn1_node_actions[];
+
+
+void
+synergy_intr_alloc(int bit, int cpuid) {
+ return;
+}
+
+int
+synergy_intr_connect(int bit,
+ int cpuid)
+{
+ int irq;
+ unsigned is_b;
+int nasid;
+
+nasid = cpuid_to_nasid(cpuid);
+ irq = bit_pos_to_irq(bit);
+
+ is_b = (cpuid_to_slice(cpuid)) & 1;
+ if (is_b) {
+ setclear_mask_b(irq,cpuid,1);
+ setclear_mask_a(irq,cpuid, 0);
+ } else {
+ setclear_mask_a(irq, cpuid, 1);
+ setclear_mask_b(irq, cpuid, 0);
+ }
+ return 0;
+}
+void
+setclear_mask_a(int irq, int cpuid, int set)
+{
+ int synergy;
+ int nasid;
+ int reg_num;
+ unsigned long mask;
+ unsigned long addr;
+ unsigned long reg;
+ unsigned long val;
+ int my_cnode, my_synergy;
+ int target_cnode, target_synergy;
+
+ /*
+ * Perform some idiot checks ..
+ */
+ if ( (irq < 0) || (irq > 255) ||
+ (cpuid < 0) || (cpuid > 512) ) {
+ printk("clear_mask_a: Invalid parameter irq %d cpuid %d\n", irq, cpuid);
+ return;
+ }
+
+ target_cnode = cpuid_to_cnodeid(cpuid);
+ target_synergy = cpuid_to_synergy(cpuid);
+ my_cnode = cpuid_to_cnodeid(smp_processor_id());
+ my_synergy = cpuid_to_synergy(smp_processor_id());
+
+ reg_num = irq / 64;
+ mask = 1;
+ mask <<= (irq % 64);
+ switch (reg_num) {
+ case 0:
+ reg = VEC_MASK0A;
+ addr = VEC_MASK0A_ADDR;
+ break;
+ case 1:
+ reg = VEC_MASK1A;
+ addr = VEC_MASK1A_ADDR;
+ break;
+ case 2:
+ reg = VEC_MASK2A;
+ addr = VEC_MASK2A_ADDR;
+ break;
+ case 3:
+ reg = VEC_MASK3A;
+ addr = VEC_MASK3A_ADDR;
+ break;
+ default:
+ reg = addr = 0;
+ break;
+ }
+ if (my_cnode == target_cnode && my_synergy == target_synergy) {
+ // local synergy
+ val = READ_LOCAL_SYNERGY_REG(addr);
+ if (set) {
+ val |= mask;
+ } else {
+ val &= ~mask;
+ }
+ WRITE_LOCAL_SYNERGY_REG(addr, val);
+ val = READ_LOCAL_SYNERGY_REG(addr);
+ } else { /* remote synergy */
+ synergy = cpuid_to_synergy(cpuid);
+ nasid = cpuid_to_nasid(cpuid);
+ val = REMOTE_SYNERGY_LOAD(nasid, synergy, reg);
+ if (set) {
+ val |= mask;
+ } else {
+ val &= ~mask;
+ }
+ REMOTE_SYNERGY_STORE(nasid, synergy, reg, val);
+ }
+}
+
+void
+setclear_mask_b(int irq, int cpuid, int set)
+{
+ int synergy;
+ int nasid;
+ int reg_num;
+ unsigned long mask;
+ unsigned long addr;
+ unsigned long reg;
+ unsigned long val;
+ int my_cnode, my_synergy;
+ int target_cnode, target_synergy;
+
+ /*
+ * Perform some idiot checks ..
+ */
+ if ( (irq < 0) || (irq > 255) ||
+ (cpuid < 0) || (cpuid > 512) ) {
+ printk("clear_mask_b: Invalid parameter irq %d cpuid %d\n", irq, cpuid);
+ return;
+ }
+
+ target_cnode = cpuid_to_cnodeid(cpuid);
+ target_synergy = cpuid_to_synergy(cpuid);
+ my_cnode = cpuid_to_cnodeid(smp_processor_id());
+ my_synergy = cpuid_to_synergy(smp_processor_id());
+
+ reg_num = irq / 64;
+ mask = 1;
+ mask <<= (irq % 64);
+ switch (reg_num) {
+ case 0:
+ reg = VEC_MASK0B;
+ addr = VEC_MASK0B_ADDR;
+ break;
+ case 1:
+ reg = VEC_MASK1B;
+ addr = VEC_MASK1B_ADDR;
+ break;
+ case 2:
+ reg = VEC_MASK2B;
+ addr = VEC_MASK2B_ADDR;
+ break;
+ case 3:
+ reg = VEC_MASK3B;
+ addr = VEC_MASK3B_ADDR;
+ break;
+ default:
+ reg = addr = 0;
+ break;
+ }
+ if (my_cnode == target_cnode && my_synergy == target_synergy) {
+ // local synergy
+ val = READ_LOCAL_SYNERGY_REG(addr);
+ if (set) {
+ val |= mask;
+ } else {
+ val &= ~mask;
+ }
+ WRITE_LOCAL_SYNERGY_REG(addr, val);
+ val = READ_LOCAL_SYNERGY_REG(addr);
+ } else { /* remote synergy */
+ synergy = cpuid_to_synergy(cpuid);
+ nasid = cpuid_to_nasid(cpuid);
+ val = REMOTE_SYNERGY_LOAD(nasid, synergy, reg);
+ if (set) {
+ val |= mask;
+ } else {
+ val &= ~mask;
+ }
+ REMOTE_SYNERGY_STORE(nasid, synergy, reg, val);
+ }
+}