diff options
Diffstat (limited to 'arch/ia64/sn/sn1')
-rw-r--r-- | arch/ia64/sn/sn1/Makefile | 19 | ||||
-rw-r--r-- | arch/ia64/sn/sn1/discontig.c | 188 | ||||
-rw-r--r-- | arch/ia64/sn/sn1/iomv.c | 100 | ||||
-rw-r--r-- | arch/ia64/sn/sn1/irq.c | 233 | ||||
-rw-r--r-- | arch/ia64/sn/sn1/llsc4.c | 944 | ||||
-rw-r--r-- | arch/ia64/sn/sn1/llsc4.h | 104 | ||||
-rw-r--r-- | arch/ia64/sn/sn1/mm.c | 399 | ||||
-rw-r--r-- | arch/ia64/sn/sn1/setup.c | 46 | ||||
-rw-r--r-- | arch/ia64/sn/sn1/smp.c | 186 | ||||
-rw-r--r-- | arch/ia64/sn/sn1/sn1_asm.S | 6 | ||||
-rw-r--r-- | arch/ia64/sn/sn1/synergy.c | 204 |
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, ¶ms, 1, 0) != 0) + panic("Unable to do ptc_global - timed out"); + + sn1_ptc_local(¶ms); +} +#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); + } +} |