summaryrefslogtreecommitdiffstats
path: root/arch/ia64/sn
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2001-01-11 04:02:40 +0000
committerRalf Baechle <ralf@linux-mips.org>2001-01-11 04:02:40 +0000
commite47f00743fc4776491344f2c618cc8dc2c23bcbc (patch)
tree13e03a113a82a184c51c19c209867cfd3a59b3b9 /arch/ia64/sn
parentb2ad5f821b1381492d792ca10b1eb7a107b48f14 (diff)
Merge with Linux 2.4.0.
Diffstat (limited to 'arch/ia64/sn')
-rw-r--r--arch/ia64/sn/Makefile13
-rw-r--r--arch/ia64/sn/fprom/Makefile30
-rw-r--r--arch/ia64/sn/fprom/README85
-rw-r--r--arch/ia64/sn/fprom/fpmem.c200
-rw-r--r--arch/ia64/sn/fprom/fpmem.h35
-rw-r--r--arch/ia64/sn/fprom/fprom.lds96
-rw-r--r--arch/ia64/sn/fprom/fpromasm.S314
-rw-r--r--arch/ia64/sn/fprom/fw-emu.c492
-rw-r--r--arch/ia64/sn/fprom/main.c110
-rw-r--r--arch/ia64/sn/fprom/runsim372
-rw-r--r--arch/ia64/sn/io/Makefile32
-rw-r--r--arch/ia64/sn/io/alenlist.c900
-rw-r--r--arch/ia64/sn/io/cdl.c230
-rw-r--r--arch/ia64/sn/io/devsupport.c1291
-rw-r--r--arch/ia64/sn/io/eeprom.c1457
-rw-r--r--arch/ia64/sn/io/hcl.c1506
-rw-r--r--arch/ia64/sn/io/hcl_util.c159
-rw-r--r--arch/ia64/sn/io/hubdev.c126
-rw-r--r--arch/ia64/sn/io/hubspc.c447
-rw-r--r--arch/ia64/sn/io/invent.c197
-rw-r--r--arch/ia64/sn/io/io.c1311
-rw-r--r--arch/ia64/sn/io/ip37.c127
-rw-r--r--arch/ia64/sn/io/klconflib.c1334
-rw-r--r--arch/ia64/sn/io/klgraph.c971
-rw-r--r--arch/ia64/sn/io/klgraph_hack.c847
-rw-r--r--arch/ia64/sn/io/l1.c2974
-rw-r--r--arch/ia64/sn/io/l1_command.c1356
-rw-r--r--arch/ia64/sn/io/labelcl.c665
-rw-r--r--arch/ia64/sn/io/mem_refcnt.c233
-rw-r--r--arch/ia64/sn/io/ml_SN_init.c661
-rw-r--r--arch/ia64/sn/io/ml_SN_intr.c1730
-rw-r--r--arch/ia64/sn/io/ml_iograph.c1582
-rw-r--r--arch/ia64/sn/io/module.c311
-rw-r--r--arch/ia64/sn/io/pci.c306
-rw-r--r--arch/ia64/sn/io/pci_bus_cvlink.c591
-rw-r--r--arch/ia64/sn/io/pci_dma.c334
-rw-r--r--arch/ia64/sn/io/pcibr.c9572
-rw-r--r--arch/ia64/sn/io/pciio.c1562
-rw-r--r--arch/ia64/sn/io/sgi_if.c72
-rw-r--r--arch/ia64/sn/io/sgi_io_init.c312
-rw-r--r--arch/ia64/sn/io/sgi_io_sim.c161
-rw-r--r--arch/ia64/sn/io/stubs.c256
-rw-r--r--arch/ia64/sn/io/xbow.c1866
-rw-r--r--arch/ia64/sn/io/xswitch.c267
-rw-r--r--arch/ia64/sn/io/xtalk.c1137
-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
-rw-r--r--arch/ia64/sn/tools/make_textsym138
57 files changed, 41163 insertions, 34 deletions
diff --git a/arch/ia64/sn/Makefile b/arch/ia64/sn/Makefile
index b35ce21ff..1575cda30 100644
--- a/arch/ia64/sn/Makefile
+++ b/arch/ia64/sn/Makefile
@@ -5,15 +5,10 @@
# 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
-
-.S.s:
- $(CPP) $(AFLAGS) -o $*.s $<
-.S.o:
- $(CC) $(AFLAGS) -c -o $*.o $<
-
+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
all: sn.a
O_TARGET = sn.a
diff --git a/arch/ia64/sn/fprom/Makefile b/arch/ia64/sn/fprom/Makefile
new file mode 100644
index 000000000..2192f6ea8
--- /dev/null
+++ b/arch/ia64/sn/fprom/Makefile
@@ -0,0 +1,30 @@
+#
+# 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) Jack Steiner (steiner@sgi.com)
+#
+
+TOPDIR=../../../..
+HPATH = $(TOPDIR)/include
+
+LIB = ../../lib/lib.a
+
+OBJ=fpromasm.o main.o fw-emu.o fpmem.o
+
+fprom: $(OBJ)
+ $(LD) -static -Tfprom.lds -o fprom $(OBJ) $(LIB)
+
+.S.o:
+ $(CC) -D__ASSEMBLY__ $(AFLAGS) $(AFLAGS_KERNEL) -c -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) $(CFLAGS_KERNEL) -c -o $*.o $<
+
+clean:
+ rm -f *.o fprom
+
+
+include $(TOPDIR)/Rules.make
+
diff --git a/arch/ia64/sn/fprom/README b/arch/ia64/sn/fprom/README
new file mode 100644
index 000000000..263c2a8b4
--- /dev/null
+++ b/arch/ia64/sn/fprom/README
@@ -0,0 +1,85 @@
+This directory contains the files required to build
+the fake PROM image that is currently being used to
+boot IA64 kernels running under the SGI Medusa kernel.
+
+The FPROM currently provides the following functions:
+
+ - PAL emulation for all PAL calls we've made so far.
+ - SAL emulation for all SAL calls we've made so far.
+ - EFI emulation for all EFI calls we've made so far.
+ - builds the "ia64_bootparam" structure that is
+ passed to the kernel from SAL. This structure
+ shows the cpu & memory configurations.
+ - supports medusa boottime options for changing
+ the number of cpus present
+ - supports medusa boottime options for changing
+ the memory configuration.
+
+
+
+At some point, this fake PROM will be replaced by the
+real PROM.
+
+
+
+
+To build a fake PROM, cd to this directory & type:
+
+ make
+
+This will (or should) build a fake PROM named "fprom".
+
+
+
+
+Use this fprom image when booting the Medusa simulator. The
+control file used to boot Medusa should include the
+following lines:
+
+ load fprom
+ load vmlinux
+ sr pc 0x100000
+ sr g 9 <address of kernel _start function> #(currently 0xe000000000520000)
+
+NOTE: There is a script "runsim" in this directory that can be used to
+simplify setting up an environment for running under Medusa.
+
+
+
+
+The following parameters may be passed to the fake PROM to
+control the PAL/SAL/EFI parameters passed to the kernel:
+
+ GR[8] = # of cpus
+ GR[9] = address of primary entry point into the kernel
+ GR[20] = memory configuration for node 0
+ GR[21] = memory configuration for node 1
+ GR[22] = memory configuration for node 2
+ GR[23] = memory configuration for node 3
+
+
+Registers GR[20] - GR[23] contain information to specify the
+amount of memory present on nodes 0-3.
+
+ - if nothing is specified (all registers are 0), the configuration
+ defaults to 8 MB on node 0.
+
+ - a mem config entry for node N is passed in GR[20+N]
+
+ - a mem config entry consists of 8 hex digits. Each digit gives the
+ amount of physical memory available on the node starting at
+ 1GB*<dn>, where dn is the digit number. The amount of memory
+ is 8MB*2**<d>. (If <d> = 0, the memory size is 0).
+
+ SN1 doesnt support dimms this small but small memory systems
+ boot faster on Medusa.
+
+
+
+An example helps a lot. The following specifies that node 0 has
+physical memory 0 to 8MB and 1GB to 1GB+32MB, and that node 1 has
+64MB starting at address 0 of the node which is 8GB.
+
+ gr[20] = 0x21 # 0 to 8MB, 1GB to 1GB+32MB
+ gr[21] = 0x4 # 8GB to 8GB+64MB
+
diff --git a/arch/ia64/sn/fprom/fpmem.c b/arch/ia64/sn/fprom/fpmem.c
new file mode 100644
index 000000000..14f62bfc3
--- /dev/null
+++ b/arch/ia64/sn/fprom/fpmem.c
@@ -0,0 +1,200 @@
+/*
+ *
+ * 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)
+ */
+
+
+/*
+ * FPROM EFI memory descriptor build routines
+ *
+ * - Routines to build the EFI memory descriptor map
+ * - Should also be usable by the SGI SN1 prom to convert
+ * klconfig to efi_memmap
+ */
+
+#include <asm/efi.h>
+#include "fpmem.h"
+
+/*
+ * args points to a layout in memory like this
+ *
+ * 32 bit 32 bit
+ *
+ * numnodes numcpus
+ *
+ * 16 bit 16 bit 32 bit
+ * nasid0 cpuconf membankdesc0
+ * nasid1 cpuconf membankdesc1
+ * .
+ * .
+ * .
+ * .
+ * .
+ */
+
+sn_memmap_t *sn_memmap ;
+sn_config_t *sn_config ;
+
+/*
+ * There is a hole in the node 0 address space. Dont put it
+ * in the memory map
+ */
+#define NODE0_HOLE_SIZE (20*MB)
+#define NODE0_HOLE_END (4UL*GB)
+
+#define MB (1024*1024)
+#define GB (1024*MB)
+#define KERNEL_SIZE (4*MB)
+#define PROMRESERVED_SIZE (1*MB)
+#define MD_BANK_SHFT 30
+
+#define TO_NODE(_n, _x) (((long)_n<<33L) | (long)_x)
+
+/*
+ * For SN, this may not take an arg and gets the numnodes from
+ * the prom variable or by traversing klcfg or promcfg
+ */
+int
+GetNumNodes(void)
+{
+ return sn_config->nodes;
+}
+
+int
+GetNumCpus(void)
+{
+ return sn_config->cpus;
+}
+
+/* For SN1, get the index th nasid */
+
+int
+GetNasid(int index)
+{
+ return sn_memmap[index].nasid ;
+}
+
+node_memmap_t
+GetMemBankInfo(int index)
+{
+ return sn_memmap[index].node_memmap ;
+}
+
+int
+IsCpuPresent(int cnode, int cpu)
+{
+ return sn_memmap[cnode].cpuconfig & (1<<cpu);
+}
+
+
+/*
+ * Made this into an explicit case statement so that
+ * we can assign specific properties to banks like bank0
+ * actually disabled etc.
+ */
+
+int
+IsBankPresent(int index, node_memmap_t nmemmap)
+{
+ switch (index) {
+ case 0:return nmemmap.b0;
+ case 1:return nmemmap.b1;
+ case 2:return nmemmap.b2;
+ case 3:return nmemmap.b3;
+ case 4:return nmemmap.b4;
+ case 5:return nmemmap.b5;
+ case 6:return nmemmap.b6;
+ case 7:return nmemmap.b7;
+ default:return -1 ;
+ }
+}
+
+int
+GetBankSize(int index, node_memmap_t nmemmap)
+{
+ switch (index) {
+ case 0:
+ case 1:return nmemmap.b01size;
+ case 2:
+ case 3:return nmemmap.b23size;
+ case 4:
+ case 5:return nmemmap.b45size;
+ case 6:
+ case 7:return nmemmap.b67size;
+ default:return -1 ;
+ }
+}
+
+void
+build_mem_desc(efi_memory_desc_t *md, int type, long paddr, long numbytes)
+{
+ md->type = type;
+ md->phys_addr = paddr;
+ md->virt_addr = 0;
+ md->num_pages = numbytes >> 12;
+ md->attribute = EFI_MEMORY_WB;
+}
+
+int
+build_efi_memmap(void *md, int mdsize)
+{
+ int numnodes = GetNumNodes() ;
+ int cnode,bank ;
+ int nasid ;
+ node_memmap_t membank_info ;
+ int bsize;
+ int count = 0 ;
+ long paddr, hole, numbytes;
+
+
+ for (cnode=0;cnode<numnodes;cnode++) {
+ nasid = GetNasid(cnode) ;
+ membank_info = GetMemBankInfo(cnode) ;
+ for (bank=0;bank<SN1_MAX_BANK_PER_NODE;bank++) {
+ if (IsBankPresent(bank, membank_info)) {
+ bsize = GetBankSize(bank, membank_info) ;
+ paddr = TO_NODE(nasid, (long)bank<<MD_BANK_SHFT);
+ numbytes = BankSizeBytes(bsize);
+
+ /*
+ * Check for the node 0 hole. Since banks cant
+ * span the hole, we only need to check if the end of
+ * the range is the end of the hole.
+ */
+ if (paddr+numbytes == NODE0_HOLE_END)
+ numbytes -= NODE0_HOLE_SIZE;
+ /*
+ * UGLY hack - we must skip overr the kernel and
+ * PROM runtime services but we dont exactly where it is.
+ * So lets just reserve 0-12MB.
+ */
+ if (bank == 0) {
+ hole = (cnode == 0) ? KERNEL_SIZE : PROMRESERVED_SIZE;
+ numbytes -= hole;
+ build_mem_desc(md, EFI_RUNTIME_SERVICES_DATA, paddr, hole);
+ paddr += hole;
+ count++ ;
+ md += mdsize;
+ }
+ build_mem_desc(md, EFI_CONVENTIONAL_MEMORY, paddr, numbytes);
+
+ md += mdsize ;
+ count++ ;
+ }
+ }
+ }
+ return count ;
+}
+
+void
+build_init(unsigned long args)
+{
+ sn_config = (sn_config_t *) (args);
+ sn_memmap = (sn_memmap_t *)(args + 8) ; /* SN equiv for this is */
+ /* init to klconfig start */
+}
diff --git a/arch/ia64/sn/fprom/fpmem.h b/arch/ia64/sn/fprom/fpmem.h
new file mode 100644
index 000000000..bbab73638
--- /dev/null
+++ b/arch/ia64/sn/fprom/fpmem.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * 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 <asm/sn/mmzone_sn1.h>
+
+typedef struct sn_memmap_s
+{
+ short nasid ;
+ short cpuconfig;
+ node_memmap_t node_memmap ;
+} sn_memmap_t ;
+
+typedef struct sn_config_s
+{
+ int cpus;
+ int nodes;
+ sn_memmap_t memmap[1]; /* start of array */
+} sn_config_t;
+
+
+extern void build_init(unsigned long);
+extern int build_efi_memmap(void *, int);
+extern int GetNumNodes(void);
+extern int GetNumCpus(void);
+extern int IsCpuPresent(int, int);
+extern int GetNasid(int);
diff --git a/arch/ia64/sn/fprom/fprom.lds b/arch/ia64/sn/fprom/fprom.lds
new file mode 100644
index 000000000..8f416ec83
--- /dev/null
+++ b/arch/ia64/sn/fprom/fprom.lds
@@ -0,0 +1,96 @@
+
+OUTPUT_FORMAT("elf64-ia64-little")
+OUTPUT_ARCH(ia64)
+ENTRY(_start)
+SECTIONS
+{
+ v = 0x0000000000000000 ; /* this symbol is here to make debugging with kdb easier... */
+
+ . = (0x000000000000000 + 0x100000) ;
+
+ _text = .;
+ .text : AT(ADDR(.text) - 0x0000000000000000 )
+ {
+ *(__ivt_section)
+ /* these are not really text pages, but the zero page needs to be in a fixed location: */
+ *(__special_page_section)
+ __start_gate_section = .;
+ *(__gate_section)
+ __stop_gate_section = .;
+ *(.text)
+ }
+
+ /* Global data */
+ _data = .;
+
+ .rodata : AT(ADDR(.rodata) - 0x0000000000000000 )
+ { *(.rodata) }
+ .opd : AT(ADDR(.opd) - 0x0000000000000000 )
+ { *(.opd) }
+ .data : AT(ADDR(.data) - 0x0000000000000000 )
+ { *(.data) *(.gnu.linkonce.d*) CONSTRUCTORS }
+
+ __gp = ALIGN (8) + 0x200000;
+
+ .got : AT(ADDR(.got) - 0x0000000000000000 )
+ { *(.got.plt) *(.got) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : AT(ADDR(.sdata) - 0x0000000000000000 )
+ { *(.sdata) }
+ _edata = .;
+ _bss = .;
+ .sbss : AT(ADDR(.sbss) - 0x0000000000000000 )
+ { *(.sbss) *(.scommon) }
+ .bss : AT(ADDR(.bss) - 0x0000000000000000 )
+ { *(.bss) *(COMMON) }
+ . = ALIGN(64 / 8);
+ _end = .;
+
+ /* Sections to be discarded */
+ /DISCARD/ : {
+ *(.text.exit)
+ *(.data.exit)
+ }
+
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+ /* Discard them for now since Intel SoftSDV cannot handle them.
+ .comment 0 : { *(.comment) }
+ .note 0 : { *(.note) }
+ */
+ /DISCARD/ : { *(.comment) }
+ /DISCARD/ : { *(.note) }
+}
diff --git a/arch/ia64/sn/fprom/fpromasm.S b/arch/ia64/sn/fprom/fpromasm.S
new file mode 100644
index 000000000..332a9a85c
--- /dev/null
+++ b/arch/ia64/sn/fprom/fpromasm.S
@@ -0,0 +1,314 @@
+/*
+ *
+ * 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.
+ *
+ * (Code copied from or=ther files)
+ * Copyright (C) 1998-2000 Hewlett-Packard Co
+ * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ * Copyright (C) 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Jack Steiner (steiner@sgi.com)
+ */
+
+
+
+#define __ASSEMBLY__ 1
+#include "asm/processor.h"
+
+/*
+ * This file contains additional set up code that is needed to get going on
+ * Medusa. This code should disappear once real hw is available.
+ *
+ * On entry to this routine, the following register values are assumed:
+ *
+ * gr[8] - BSP cpu
+ * pr[9] - kernel entry address
+ *
+ * NOTE:
+ * This FPROM may be loaded/executed at an address different from the
+ * address that it was linked at. The FPROM is linked to run on node 0
+ * at address 0x100000. If the code in loaded into another node, it
+ * must be loaded at offset 0x100000 of the node. In addition, the
+ * FPROM does the following things:
+ * - determine the base address of the node it is loaded on
+ * - add the node base to _gp.
+ * - add the node base to all addresses derived from "movl"
+ * instructions. (I couldnt get GPREL addressing to work)
+ * (maybe newer versions of the tools will support this)
+ * - scan the .got section and add the node base to all
+ * pointers in this section.
+ * - add the node base to all physical addresses in the
+ * SAL/PAL/EFI table built by the C code. (This is done
+ * in the C code - not here)
+ * - add the node base to the TLB entries for vmlinux
+ */
+
+#define KERNEL_BASE 0xe000000000000000
+#define PAGESIZE_256M 28
+
+/*
+ * ar.k0 gets set to IOPB_PA value, on 460gx chipset it should
+ * be 0x00000ffffc000000, but on snia we use the (inverse swizzled)
+ * IOSPEC_BASE value
+ */
+#define IOPB_PA 0x00000a0000000000 /* inv swizzle IOSPEC_BASE */
+
+#define RR_RID 8
+
+
+
+// ====================================================================================
+ .text
+ .align 16
+ .global _start
+ .proc _start
+_start:
+
+// Setup psr and rse for system init
+ mov psr.l = r0;;
+ srlz.d;;
+ invala
+ mov ar.rsc = r0;;
+ loadrs
+ ;;
+
+// Set CALIAS size to zero. We dont use it.
+ movl r24=0x80000a0001000028;; // BR_PI_CALIAS_SIZE
+ st8 [r24]=r0
+
+// Isolate node number we are running on.
+ mov r6 = ip;;
+ shr r5 = r6,33;; // r5 = node number
+ shl r6 = r5,33 // r6 = base memory address of node
+
+// Set & relocate gp.
+ movl r1= __gp;; // Add base memory address
+ add r1 = r1,r6 // Relocate to boot node
+
+// Lets figure out who we are & put it in the LID register.
+// The BR_PI_SELF_CPU_NUM register gives us a value of 0-3.
+// This identifies the cpu on the node.
+// Merge the cpu number with the NASID to generate the LID.
+ movl r24=0x80000a0001000020;; // BR_PI_SELF_CPU_NUM
+ ld8 r25=[r24] // Fetch PI_SELF
+ movl r27=0x80000a0001600000;; // Fetch REVID to get local NASID
+ ld8 r27=[r27];;
+ extr.u r27=r27,32,8
+ shl r26=r25,16;; // Align local cpu# to lid.eid
+ shl r27=r27,24;; // Align NASID to lid.id
+ or r26=r26,r27;; // build the LID
+ mov cr.lid=r26 // Now put in in the LID register
+
+ movl r2=FPSR_DEFAULT;;
+ mov ar.fpsr=r2
+ movl sp = bootstacke-16;;
+ add sp = sp,r6 // Relocate to boot node
+
+// Save the NASID that we are loaded on.
+ movl r2=base_nasid;; // Save base_nasid for C code
+ add r2 = r2,r6;; // Relocate to boot node
+ st8 [r2]=r5 // Uncond st8 - same on all cpus
+
+// Save the kernel entry address. It is passed in r9 on one of
+// the cpus.
+ movl r2=bsp_entry_pc
+ cmp.ne p6,p0=r9,r0;;
+ add r2 = r2,r6;; // Relocate to boot node
+(p6) st8 [r2]=r9 // Uncond st8 - same on all cpus
+
+
+// The following can ONLY be done by 1 cpu. Lets set a lock - the
+// cpu that gets it does the initilization. The rest just spin waiting
+// til initilization is complete.
+ movl r22 = initlock;;
+ add r22 = r22,r6 // Relocate to boot node
+ mov r23 = 1;;
+ xchg8 r23 = [r22],r23;;
+ cmp.eq p6,p0 = 0,r23
+(p6) br.cond.spnt.few init
+1: ld4 r23 = [r22];;
+ cmp.eq p6,p0 = 1,r23
+(p6) br.cond.sptk 1b
+ br initx
+
+// Add base address of node memory to each pointer in the .got section.
+init: movl r16 = _GLOBAL_OFFSET_TABLE_;;
+ add r16 = r16,r6;; // Relocate to boot node
+1: ld8 r17 = [r16];;
+ cmp.eq p6,p7=0,r17
+(p6) br.cond.sptk.few.clr 2f;;
+ add r17 = r17,r6;; // Relocate to boot node
+ st8 [r16] = r17,8
+ br 1b
+2:
+ mov r23 = 2;; // All done, release the spinning cpus
+ st4 [r22] = r23
+initx:
+
+//
+// I/O-port space base address:
+//
+ movl r2 = IOPB_PA;;
+ mov ar.k0 = r2
+
+
+// Now call main & pass it the current LID value.
+ alloc r0=ar.pfs,0,0,2,0
+ mov r32=r26
+ mov r33=r8;;
+ br.call.sptk.few rp=fmain
+
+// Initialize Region Registers
+//
+ mov r10 = r0
+ mov r2 = (13<<2)
+ mov r3 = r0;;
+1: cmp4.gtu p6,p7 = 7, r3
+ dep r10 = r3, r10, 61, 3
+ dep r2 = r3, r2, RR_RID, 4;;
+(p7) dep r2 = 0, r2, 0, 1;;
+(p6) dep r2 = -1, r2, 0, 1;;
+ mov rr[r10] = r2
+ add r3 = 1, r3;;
+ srlz.d;;
+ cmp4.gtu p6,p0 = 8, r3
+(p6) br.cond.sptk.few.clr 1b
+
+//
+// Return value indicates if we are the BSP or AP.
+// 1 = BSP, 0 = AP
+ mov cr.tpr=r0;;
+ cmp.eq p6,p0=r8,r0
+(p6) br.cond.spnt slave
+
+//
+// Initialize the protection key registers with only pkr[0] = valid.
+//
+// Should be initialized in accordance with the OS.
+//
+ mov r2 = 1
+ mov r3 = r0;;
+ mov pkr[r3] = r2;;
+ srlz.d;;
+ mov r2 = r0
+
+1: add r3 = r3, r0, 1;; // increment PKR
+ cmp.gtu p6, p0 = 16, r3;;
+(p6) mov pkr[r3] = r2
+(p6) br.cond.sptk.few.clr 1b
+
+ mov ar.rnat = r0 // clear RNAT register
+
+//
+// Setup system address translation for kernel
+//
+// Note: The setup of Kernel Virtual address space can be done by the
+// C code of the boot loader.
+//
+//
+
+#define LINUX_PAGE_OFFSET 0xe000000000000000
+#define ITIR(key, ps) ((key<<8) | (ps<<2))
+#define ITRGR(ed,ar,ma) ((ed<<52) | (ar<<9) | (ma<<2) | 0x61)
+
+#define AR_RX 1 // RX permission
+#define AR_RW 4 // RW permission
+#define MA_WB 0 // WRITEBACK memory attribute
+
+#define TLB_PAGESIZE 28 // Use 256MB pages for now.
+ mov r16=r5
+
+//
+// text section
+//
+ movl r2 = LINUX_PAGE_OFFSET;; // Set up IFA with VPN of linux
+ mov cr.ifa = r2
+ movl r3 = ITIR(0,TLB_PAGESIZE);; // Set ITIR to default pagesize
+ mov cr.itir = r3
+
+ shl r4 = r16,33;; // physical addr of start of node
+ movl r5 = ITRGR(1,AR_RX,MA_WB);; // TLB attributes
+ or r10=r4,r5;;
+
+ itr.i itr[r0] = r10;; // Dropin ITR entry
+ srlz.i;;
+
+//
+// data section
+//
+ movl r2 = LINUX_PAGE_OFFSET;; // Set up IFA with VPN of linux
+ mov cr.ifa = r2
+ movl r3 = ITIR(0,TLB_PAGESIZE);; // Set ITIR to default pagesize
+ mov cr.itir = r3
+
+ shl r4 = r16,33;; // physical addr of start of node
+ movl r5 = ITRGR(1,AR_RW,MA_WB);; // TLB attributes
+ or r10=r4,r5;;
+
+ itr.d dtr[r0] = r10;; // Dropin DTR entry
+ srlz.d;;
+
+
+
+
+//
+// Turn on address translation, interrupt collection, psr.ed, protection key.
+// Interrupts (PSR.i) are still off here.
+//
+
+ movl r3 = ( IA64_PSR_BN | \
+ IA64_PSR_AC | \
+ IA64_PSR_IT | \
+ IA64_PSR_DB | \
+ IA64_PSR_DA | \
+ IA64_PSR_RT | \
+ IA64_PSR_DT | \
+ IA64_PSR_IC \
+ )
+ ;;
+ mov cr.ipsr = r3
+
+//
+// Go to kernel C startup routines
+// Need to do a "rfi" in order set "it" and "ed" bits in the PSR.
+// This is the only way to set them.
+
+ movl r2=bsp_entry_pc;;
+ add r2 = r2,r6;; // Relocate to boot node
+ ld8 r2=[r2];;
+ mov cr.iip = r2
+ srlz.d;;
+ rfi;;
+ .endp _start
+
+// Slave processors come here to spin til they get an interrupt. Then they launch themselves to
+// the place ap_entry points. No initialization is necessary - the kernel makes no
+// assumptions about state on this entry.
+// Note: should verify that the interrupt we got was really the ap_wakeup
+// interrupt but this should not be an issue on medusa
+slave:
+ nop.i 0x8beef // Medusa - put cpu to sleep til interrupt occurs
+ mov r8=cr.irr0;; // Check for interrupt pending.
+ cmp.eq p6,p0=r8,r0
+(p6) br.cond.sptk slave;;
+
+ mov r8=cr.ivr;; // Got one. Must read ivr to accept it
+ srlz.d;;
+ mov cr.eoi=r0;; // must write eoi to clear
+ movl r8=ap_entry;; // now jump to kernel entry
+ add r8 = r8,r6;; // Relocate to boot node
+ ld8 r9=[r8],8;;
+ ld8 r1=[r8]
+ mov b0=r9;;
+ br b0
+
+// Here is the kernel stack used for the fake PROM
+ .bss
+ .align 16384
+bootstack:
+ .skip 16384
+bootstacke:
+initlock:
+ data4
diff --git a/arch/ia64/sn/fprom/fw-emu.c b/arch/ia64/sn/fprom/fw-emu.c
new file mode 100644
index 000000000..2d85befca
--- /dev/null
+++ b/arch/ia64/sn/fprom/fw-emu.c
@@ -0,0 +1,492 @@
+/*
+ * PAL & SAL emulation.
+ *
+ * Copyright (C) 1998-2000 Hewlett-Packard Co
+ * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ *
+ * Copyright (C) 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Jack Steiner (steiner@sgi.com)
+ */
+#include <asm/efi.h>
+#include <asm/pal.h>
+#include <asm/sal.h>
+#include <asm/processor.h>
+#include <asm/acpi-ext.h>
+#include "fpmem.h"
+
+#define MB (1024*1024UL)
+#define GB (MB*1024UL)
+
+#define FPROM_BUG() do {while (1);} while (0)
+#define MAX_NODES 128
+#define MAX_LSAPICS 512
+#define MAX_CPUS 512
+#define MAX_CPUS_NODE 4
+#define CPUS_PER_NODE 4
+#define CPUS_PER_FSB 2
+#define CPUS_PER_FSB_MASK (CPUS_PER_FSB-1)
+
+#define NUM_EFI_DESCS 2
+
+typedef union ia64_nasid_va {
+ struct {
+ unsigned long off : 33; /* intra-region offset */
+ unsigned long nasid : 7; /* NASID */
+ unsigned long off2 : 21; /* fill */
+ unsigned long reg : 3; /* region number */
+ } f;
+ unsigned long l;
+ void *p;
+} ia64_nasid_va;
+
+typedef struct {
+ unsigned long pc;
+ unsigned long gp;
+} func_ptr_t;
+
+#define IS_VIRTUAL_MODE() ({struct ia64_psr psr; asm("mov %0=psr" : "=r"(psr)); psr.dt;})
+#define ADDR_OF(p) (IS_VIRTUAL_MODE() ? ((void*)((long)(p)+PAGE_OFFSET)) : ((void*) (p)))
+#define __fwtab_pa(n,x) ({ia64_nasid_va _v; _v.l = (long) (x); _v.f.nasid = (x) ? (n) : 0; _v.f.reg = 0; _v.l;})
+
+/*
+ * The following variables are passed thru registersfrom the configuration file and
+ * are set via the _start function.
+ */
+long base_nasid;
+long num_cpus;
+long bsp_entry_pc=0;
+long num_nodes;
+long app_entry_pc;
+int bsp_lid;
+func_ptr_t ap_entry;
+
+
+static char fw_mem[( sizeof(efi_system_table_t)
+ + sizeof(efi_runtime_services_t)
+ + NUM_EFI_DESCS*sizeof(efi_config_table_t)
+ + sizeof(struct ia64_sal_systab)
+ + sizeof(struct ia64_sal_desc_entry_point)
+ + sizeof(struct ia64_sal_desc_ap_wakeup)
+ + sizeof(acpi_rsdp_t)
+ + sizeof(acpi_rsdt_t)
+ + sizeof(acpi_sapic_t)
+ + MAX_LSAPICS*(sizeof(acpi_entry_lsapic_t))
+ + (1+8*MAX_NODES)*(sizeof(efi_memory_desc_t))
+ + sizeof(ia64_sal_desc_ptc_t) +
+ + MAX_NODES*sizeof(ia64_sal_ptc_domain_info_t) +
+ + MAX_CPUS*sizeof(ia64_sal_ptc_domain_proc_entry_t) +
+ + 1024)] __attribute__ ((aligned (8)));
+
+/*
+ * Very ugly, but we need this in the simulator only. Once we run on
+ * real hw, this can all go away.
+ */
+extern void pal_emulator_static (void);
+
+asm ("
+ .text
+ .proc pal_emulator_static
+pal_emulator_static:
+ mov r8=-1
+ cmp.eq p6,p7=6,r28 /* PAL_PTCE_INFO */
+(p7) br.cond.sptk.few 1f
+ ;;
+ mov r8=0 /* status = 0 */
+ movl r9=0x500000000 /* tc.base */
+ movl r10=0x0000000200000003 /* count[0], count[1] */
+ movl r11=0x1000000000002000 /* stride[0], stride[1] */
+ br.cond.sptk.few rp
+
+1: cmp.eq p6,p7=14,r28 /* PAL_FREQ_RATIOS */
+(p7) br.cond.sptk.few 1f
+ mov r8=0 /* status = 0 */
+ movl r9 =0x100000064 /* proc_ratio (1/100) */
+ movl r10=0x100000100 /* bus_ratio<<32 (1/256) */
+ movl r11=0x10000000a /* itc_ratio<<32 (1/100) */
+
+1: cmp.eq p6,p7=22,r28 /* PAL_MC_DRAIN */
+(p7) br.cond.sptk.few 1f
+ mov r8=0
+ br.cond.sptk.few rp
+
+1: cmp.eq p6,p7=23,r28 /* PAL_MC_EXPECTED */
+(p7) br.cond.sptk.few 1f
+ mov r8=0
+ br.cond.sptk.few rp
+
+1: br.cond.sptk.few rp
+ .endp pal_emulator_static\n");
+
+
+static efi_status_t
+efi_get_time (efi_time_t *tm, efi_time_cap_t *tc)
+{
+ if (tm) {
+ memset(tm, 0, sizeof(*tm));
+ tm->year = 2000;
+ tm->month = 2;
+ tm->day = 13;
+ tm->hour = 10;
+ tm->minute = 11;
+ tm->second = 12;
+ }
+
+ if (tc) {
+ tc->resolution = 10;
+ tc->accuracy = 12;
+ tc->sets_to_zero = 1;
+ }
+
+ return EFI_SUCCESS;
+}
+
+static void
+efi_reset_system (int reset_type, efi_status_t status, unsigned long data_size, efi_char16_t *data)
+{
+ while(1); /* Is there a pseudo-op to stop medusa */
+}
+
+static efi_status_t
+efi_success (void)
+{
+ return EFI_SUCCESS;
+}
+
+static efi_status_t
+efi_unimplemented (void)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static long
+sal_emulator (long index, unsigned long in1, unsigned long in2,
+ unsigned long in3, unsigned long in4, unsigned long in5,
+ unsigned long in6, unsigned long in7)
+{
+ register long r9 asm ("r9") = 0;
+ register long r10 asm ("r10") = 0;
+ register long r11 asm ("r11") = 0;
+ long status;
+
+ /*
+ * Don't do a "switch" here since that gives us code that
+ * isn't self-relocatable.
+ */
+ status = 0;
+ if (index == SAL_FREQ_BASE) {
+ switch (in1) {
+ case SAL_FREQ_BASE_PLATFORM:
+ r9 = 500000000;
+ break;
+
+ case SAL_FREQ_BASE_INTERVAL_TIMER:
+ /*
+ * Is this supposed to be the cr.itc frequency
+ * or something platform specific? The SAL
+ * doc ain't exactly clear on this...
+ */
+ r9 = 700000000;
+ break;
+
+ case SAL_FREQ_BASE_REALTIME_CLOCK:
+ r9 = 1;
+ break;
+
+ default:
+ status = -1;
+ break;
+ }
+ } else if (index == SAL_SET_VECTORS) {
+ if (in1 == SAL_VECTOR_OS_BOOT_RENDEZ) {
+ func_ptr_t *fp;
+ fp = ADDR_OF(&ap_entry);
+ fp->pc = in2;
+ fp->gp = in3;
+ } else {
+ status = -1;
+ }
+ ;
+ } else if (index == SAL_GET_STATE_INFO) {
+ ;
+ } else if (index == SAL_GET_STATE_INFO_SIZE) {
+ ;
+ } else if (index == SAL_CLEAR_STATE_INFO) {
+ ;
+ } else if (index == SAL_MC_RENDEZ) {
+ ;
+ } else if (index == SAL_MC_SET_PARAMS) {
+ ;
+ } else if (index == SAL_CACHE_FLUSH) {
+ ;
+ } else if (index == SAL_CACHE_INIT) {
+ ;
+ } else if (index == SAL_UPDATE_PAL) {
+ ;
+ } else {
+ status = -1;
+ }
+ asm volatile ("" :: "r"(r9), "r"(r10), "r"(r11));
+ return status;
+}
+
+
+/*
+ * This is here to work around a bug in egcs-1.1.1b that causes the
+ * compiler to crash (seems like a bug in the new alias analysis code.
+ */
+void *
+id (long addr)
+{
+ return (void *) addr;
+}
+
+
+/*
+ * Fix the addresses in a function pointer by adding base node address
+ * to pc & gp.
+ */
+void
+fix_function_pointer(void *fp)
+{
+ func_ptr_t *_fp;
+
+ _fp = fp;
+ _fp->pc = __fwtab_pa(base_nasid, _fp->pc);
+ _fp->gp = __fwtab_pa(base_nasid, _fp->gp);
+}
+
+
+void
+sys_fw_init (const char *args, int arglen, int bsp)
+{
+ /*
+ * Use static variables to keep from overflowing the RSE stack
+ */
+ static efi_system_table_t *efi_systab;
+ static efi_runtime_services_t *efi_runtime;
+ static efi_config_table_t *efi_tables;
+ static ia64_sal_desc_ptc_t *sal_ptc;
+ static ia64_sal_ptc_domain_info_t *sal_ptcdi;
+ static ia64_sal_ptc_domain_proc_entry_t *sal_ptclid;
+ static acpi_rsdp_t *acpi_systab;
+ static acpi_rsdt_t *acpi_rsdt;
+ static acpi_sapic_t *acpi_sapic;
+ static acpi_entry_lsapic_t *acpi_lsapic;
+ static struct ia64_sal_systab *sal_systab;
+ static efi_memory_desc_t *efi_memmap, *md;
+ static unsigned long *pal_desc, *sal_desc;
+ static struct ia64_sal_desc_entry_point *sal_ed;
+ static struct ia64_boot_param *bp;
+ static struct ia64_sal_desc_ap_wakeup *sal_apwake;
+ static unsigned char checksum = 0;
+ static char *cp, *cmd_line, *vendor;
+ static int mdsize, domain, last_domain ;
+ static int cnode, nasid, cpu, num_memmd, cpus_found;
+
+ /*
+ * Pass the parameter base address to the build_efi_xxx routines.
+ */
+ build_init(8LL*GB*base_nasid);
+
+ num_nodes = GetNumNodes();
+ num_cpus = GetNumCpus();
+
+
+ memset(fw_mem, 0, sizeof(fw_mem));
+
+ pal_desc = (unsigned long *) &pal_emulator_static;
+ sal_desc = (unsigned long *) &sal_emulator;
+ fix_function_pointer(&pal_emulator_static);
+ fix_function_pointer(&sal_emulator);
+
+ /* Align this to 16 bytes, probably EFI does this */
+ mdsize = (sizeof(efi_memory_desc_t) + 15) & ~15 ;
+
+ cp = fw_mem;
+ efi_systab = (void *) cp; cp += sizeof(*efi_systab);
+ efi_runtime = (void *) cp; cp += sizeof(*efi_runtime);
+ efi_tables = (void *) cp; cp += NUM_EFI_DESCS*sizeof(*efi_tables);
+ sal_systab = (void *) cp; cp += sizeof(*sal_systab);
+ sal_ed = (void *) cp; cp += sizeof(*sal_ed);
+ sal_ptc = (void *) cp; cp += sizeof(*sal_ptc);
+ sal_apwake = (void *) cp; cp += sizeof(*sal_apwake);
+ acpi_systab = (void *) cp; cp += sizeof(*acpi_systab);
+ acpi_rsdt = (void *) cp; cp += sizeof(*acpi_rsdt);
+ acpi_sapic = (void *) cp; cp += sizeof(*acpi_sapic);
+ acpi_lsapic = (void *) cp; cp += num_cpus*sizeof(*acpi_lsapic);
+ vendor = (char *) cp; cp += 32;
+ efi_memmap = (void *) cp; cp += 8*32*sizeof(*efi_memmap);
+ sal_ptcdi = (void *) cp; cp += CPUS_PER_FSB*(1+num_nodes)*sizeof(*sal_ptcdi);
+ sal_ptclid = (void *) cp; cp += ((3+num_cpus)*sizeof(*sal_ptclid)+7)/8*8;
+ cmd_line = (void *) cp;
+
+ if (args) {
+ if (arglen >= 1024)
+ arglen = 1023;
+ memcpy(cmd_line, args, arglen);
+ } else {
+ arglen = 0;
+ }
+ cmd_line[arglen] = '\0';
+#ifdef BRINGUP
+ /* for now, just bring up bash */
+ strcpy(cmd_line, "init=/bin/bash");
+#else
+ strcpy(cmd_line, "");
+#endif
+
+ memset(efi_systab, 0, sizeof(efi_systab));
+ efi_systab->hdr.signature = EFI_SYSTEM_TABLE_SIGNATURE;
+ efi_systab->hdr.revision = EFI_SYSTEM_TABLE_REVISION;
+ efi_systab->hdr.headersize = sizeof(efi_systab->hdr);
+ efi_systab->fw_vendor = __fwtab_pa(base_nasid, vendor);
+ efi_systab->fw_revision = 1;
+ efi_systab->runtime = __fwtab_pa(base_nasid, efi_runtime);
+ efi_systab->nr_tables = 2;
+ efi_systab->tables = __fwtab_pa(base_nasid, efi_tables);
+ memcpy(vendor, "S\0i\0l\0i\0c\0o\0n\0-\0G\0r\0a\0p\0h\0i\0c\0s\0\0", 32);
+
+ efi_runtime->hdr.signature = EFI_RUNTIME_SERVICES_SIGNATURE;
+ efi_runtime->hdr.revision = EFI_RUNTIME_SERVICES_REVISION;
+ efi_runtime->hdr.headersize = sizeof(efi_runtime->hdr);
+ efi_runtime->get_time = __fwtab_pa(base_nasid, &efi_get_time);
+ efi_runtime->set_time = __fwtab_pa(base_nasid, &efi_unimplemented);
+ efi_runtime->get_wakeup_time = __fwtab_pa(base_nasid, &efi_unimplemented);
+ efi_runtime->set_wakeup_time = __fwtab_pa(base_nasid, &efi_unimplemented);
+ efi_runtime->set_virtual_address_map = __fwtab_pa(base_nasid, &efi_success);
+ efi_runtime->get_variable = __fwtab_pa(base_nasid, &efi_unimplemented);
+ efi_runtime->get_next_variable = __fwtab_pa(base_nasid, &efi_unimplemented);
+ efi_runtime->set_variable = __fwtab_pa(base_nasid, &efi_unimplemented);
+ efi_runtime->get_next_high_mono_count = __fwtab_pa(base_nasid, &efi_unimplemented);
+ efi_runtime->reset_system = __fwtab_pa(base_nasid, &efi_reset_system);
+
+ efi_tables->guid = SAL_SYSTEM_TABLE_GUID;
+ efi_tables->table = __fwtab_pa(base_nasid, sal_systab);
+ efi_tables++;
+ efi_tables->guid = ACPI_TABLE_GUID;
+ efi_tables->table = __fwtab_pa(base_nasid, acpi_systab);
+ fix_function_pointer(&efi_unimplemented);
+ fix_function_pointer(&efi_get_time);
+ fix_function_pointer(&efi_success);
+ fix_function_pointer(&efi_reset_system);
+
+ /* fill in the ACPI system table: */
+ memcpy(acpi_systab->signature, "RSD PTR ", 8);
+ acpi_systab->rsdt = (acpi_rsdt_t*)__fwtab_pa(base_nasid, acpi_rsdt);
+
+ memcpy(acpi_rsdt->header.signature, "RSDT",4);
+ acpi_rsdt->header.length = sizeof(acpi_rsdt_t);
+ memcpy(acpi_rsdt->header.oem_id, "SGI", 3);
+ memcpy(acpi_rsdt->header.oem_table_id, "SN1", 3);
+ acpi_rsdt->header.oem_revision = 0x00010001;
+ acpi_rsdt->entry_ptrs[0] = __fwtab_pa(base_nasid, acpi_sapic);
+
+ memcpy(acpi_sapic->header.signature, "SPIC ", 4);
+ acpi_sapic->header.length = sizeof(acpi_sapic_t)+num_cpus*sizeof(acpi_entry_lsapic_t);
+ for (cnode=0; cnode<num_nodes; cnode++) {
+ nasid = GetNasid(cnode);
+ for(cpu=0; cpu<CPUS_PER_NODE; cpu++) {
+ if (!IsCpuPresent(cnode, cpu))
+ continue;
+ acpi_lsapic->type = ACPI_ENTRY_LOCAL_SAPIC;
+ acpi_lsapic->length = sizeof(acpi_entry_lsapic_t);
+ acpi_lsapic->acpi_processor_id = cnode*4+cpu;
+ acpi_lsapic->flags = LSAPIC_ENABLED|LSAPIC_PRESENT;
+ acpi_lsapic->eid = cpu;
+ acpi_lsapic->id = nasid;
+ acpi_lsapic++;
+ }
+ }
+
+
+ /* fill in the SAL system table: */
+ memcpy(sal_systab->signature, "SST_", 4);
+ sal_systab->size = sizeof(*sal_systab);
+ sal_systab->sal_rev_minor = 1;
+ sal_systab->sal_rev_major = 0;
+ sal_systab->entry_count = 3;
+
+ strcpy(sal_systab->oem_id, "SGI");
+ strcpy(sal_systab->product_id, "SN1");
+
+ /* fill in an entry point: */
+ sal_ed->type = SAL_DESC_ENTRY_POINT;
+ sal_ed->pal_proc = __fwtab_pa(base_nasid, pal_desc[0]);
+ sal_ed->sal_proc = __fwtab_pa(base_nasid, sal_desc[0]);
+ sal_ed->gp = __fwtab_pa(base_nasid, sal_desc[1]);
+
+ /* kludge the PTC domain info */
+ sal_ptc->type = SAL_DESC_PTC;
+ sal_ptc->num_domains = 0;
+ sal_ptc->domain_info = __fwtab_pa(base_nasid, sal_ptcdi);
+ cpus_found = 0;
+ last_domain = -1;
+ sal_ptcdi--;
+ for (cnode=0; cnode<num_nodes; cnode++) {
+ nasid = GetNasid(cnode);
+ for(cpu=0; cpu<CPUS_PER_NODE; cpu++) {
+ if (IsCpuPresent(cnode, cpu)) {
+ domain = cnode*CPUS_PER_NODE + cpu/CPUS_PER_FSB;
+ if (domain != last_domain) {
+ sal_ptc->num_domains++;
+ sal_ptcdi++;
+ sal_ptcdi->proc_count = 0;
+ sal_ptcdi->proc_list = __fwtab_pa(base_nasid, sal_ptclid);
+ last_domain = domain;
+ }
+ sal_ptcdi->proc_count++;
+ sal_ptclid->id = nasid;
+ sal_ptclid->eid = cpu;
+ sal_ptclid++;
+ cpus_found++;
+ }
+ }
+ }
+
+ if (cpus_found != num_cpus)
+ FPROM_BUG();
+
+ /* Make the AP WAKEUP entry */
+ sal_apwake->type = SAL_DESC_AP_WAKEUP;
+ sal_apwake->mechanism = IA64_SAL_AP_EXTERNAL_INT;
+ sal_apwake->vector = 18;
+
+ for (cp = (char *) sal_systab; cp < (char *) efi_memmap; ++cp)
+ checksum += *cp;
+
+ sal_systab->checksum = -checksum;
+
+ md = &efi_memmap[0];
+ num_memmd = build_efi_memmap((void *)md, mdsize) ;
+
+ bp = id(ZERO_PAGE_ADDR + (((long)base_nasid)<<33));
+ bp->efi_systab = __fwtab_pa(base_nasid, &fw_mem);
+ bp->efi_memmap = __fwtab_pa(base_nasid, efi_memmap);
+ bp->efi_memmap_size = num_memmd*mdsize;
+ bp->efi_memdesc_size = mdsize;
+ bp->efi_memdesc_version = 0x101;
+ bp->command_line = __fwtab_pa(base_nasid, cmd_line);
+ bp->console_info.num_cols = 80;
+ bp->console_info.num_rows = 25;
+ bp->console_info.orig_x = 0;
+ bp->console_info.orig_y = 24;
+ bp->num_pci_vectors = 0;
+ bp->fpswa = 0;
+
+ /*
+ * Now pick the BSP & store it LID value in
+ * a global variable. Note if BSP is greater than last cpu,
+ * pick the last cpu.
+ */
+ for (cnode=0; cnode<num_nodes; cnode++) {
+ for(cpu=0; cpu<CPUS_PER_NODE; cpu++) {
+ if (!IsCpuPresent(cnode, cpu))
+ continue;
+ bsp_lid = (GetNasid(cnode)<<24) | (cpu<<16);
+ if (bsp-- > 0)
+ continue;
+ return;
+ }
+ }
+}
diff --git a/arch/ia64/sn/fprom/main.c b/arch/ia64/sn/fprom/main.c
new file mode 100644
index 000000000..45632c2ce
--- /dev/null
+++ b/arch/ia64/sn/fprom/main.c
@@ -0,0 +1,110 @@
+/*
+ *
+ * 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/types.h>
+#include <asm/bitops.h>
+
+void bedrock_init(int);
+void synergy_init(int, int);
+void sys_fw_init (const char *args, int arglen, int bsp);
+
+volatile int bootmaster=0; /* Used to pick bootmaster */
+volatile int nasidmaster[128]={0}; /* Used to pick node/synergy masters */
+int init_done=0;
+extern int bsp_lid;
+
+#define get_bit(b,p) (((*p)>>(b))&1)
+
+int
+fmain(int lid, int bsp) {
+ int syn, nasid, cpu;
+
+ /*
+ * First lets figure out who we are. This is done from the
+ * LID passed to us.
+ */
+ nasid = (lid>>24);
+ syn = (lid>>17)&1;
+ cpu = (lid>>16)&1;
+
+ /*
+ * Now pick a synergy master to initialize synergy registers.
+ */
+ if (test_and_set_bit(syn, &nasidmaster[nasid]) == 0) {
+ synergy_init(nasid, syn);
+ test_and_set_bit(syn+2, &nasidmaster[nasid]);
+ } else
+ while (get_bit(syn+2, &nasidmaster[nasid]) == 0);
+
+ /*
+ * Now pick a nasid master to initialize Bedrock registers.
+ */
+ if (test_and_set_bit(8, &nasidmaster[nasid]) == 0) {
+ bedrock_init(nasid);
+ test_and_set_bit(9, &nasidmaster[nasid]);
+ } else
+ while (get_bit(9, &nasidmaster[nasid]) == 0);
+
+
+ /*
+ * Now pick a BSP & finish init.
+ */
+ if (test_and_set_bit(0, &bootmaster) == 0) {
+ sys_fw_init(0, 0, bsp);
+ test_and_set_bit(1, &bootmaster);
+ } else
+ while (get_bit(1, &bootmaster) == 0);
+
+ return (lid == bsp_lid);
+}
+
+
+void
+bedrock_init(int nasid)
+{
+ nasid = nasid; /* to quiet gcc */
+}
+
+
+void
+synergy_init(int nasid, int syn)
+{
+ long *base;
+ long off;
+
+ /*
+ * Enable all FSB flashed interrupts.
+ * ZZZ - I'd really like defines for this......
+ */
+ base = (long*)0x80000e0000000000LL; /* base of synergy regs */
+ for (off = 0x2a0; off < 0x2e0; off+=8) /* offset for VEC_MASK_{0-3}_A/B */
+ *(base+off/8) = -1LL;
+
+ /*
+ * Set the NASID in the FSB_CONFIG register.
+ */
+ base = (long*)0x80000e0000000450LL;
+ *base = (long)((nasid<<16)|(syn<<9));
+}
+
+
+/* Why isnt there a bcopy/memcpy in lib64.a */
+
+void*
+memcpy(void * dest, const void *src, size_t count)
+{
+ char *s, *se, *d;
+
+ for(d=dest, s=(char*)src, se=s+count; s<se; s++, d++)
+ *d = *s;
+ return dest;
+}
diff --git a/arch/ia64/sn/fprom/runsim b/arch/ia64/sn/fprom/runsim
new file mode 100644
index 000000000..496967e4d
--- /dev/null
+++ b/arch/ia64/sn/fprom/runsim
@@ -0,0 +1,372 @@
+#!/bin/sh
+
+# Script for running PROMs and LINUX kernwls on medusa.
+# Type "sim -H" for instructions.
+
+MEDUSA=${MEDUSA:-/home/rickc/official_medusa/medusa}
+
+# ------------------ err -----------------------
+err() {
+ echo "ERROR - $1"
+ exit 1
+}
+
+# ---------------- help ----------------------
+help() {
+cat <<END
+Script for running a PROM or LINUX kernel under medusa.
+This script creates a control file, creates links to the appropriate
+linux/prom files, and/or calls medusa to make simulation runs.
+
+Usage:
+ Initial setup:
+ sim [-c <config_file>] <-p> | <-k> [<work_dir>]
+ -p Create PROM control file & links
+ -k Create LINUX control file & links
+ -c<cf> Control file name [Default: cf]
+ <work_dir> Path to directory that contains the linux or PROM files.
+ The directory can be any of the following:
+ (linux simulations)
+ worktree
+ worktree/linux
+ any directory with vmlinux, vmlinux.sym & fprom files
+ (prom simulations)
+ worktree
+ worktree/stand/arcs/IP37prom/dev
+ any directory with fw.bin & fw.sim files
+
+ Simulations:
+ sim [-X <n>] [-o <output>] [-M] [<config_file>]
+ -c<cf> Control file name [Default: cf]
+ -M Pipe output thru fmtmedusa
+ -o Output filename (copy of all commands/output) [Default: simout]
+ -X Specifies number of instructions to execute [Default: 0]
+ (Used only in auto test mode - not described here)
+
+Examples:
+ sim -p <promtree> # create control file (cf) & links for prom simulations
+ sim -k <linuxtree> # create control file (cf) & links for linux simulations
+ sim -p -c cfprom # create a prom control file (cfprom) only. No links are made.
+
+ sim # run medusa using previously created links &
+ # control file (cf).
+END
+exit 1
+}
+
+# ----------------------- create control file header --------------------
+create_cf_header() {
+cat <<END >>$CF
+#
+# Template for a control file for running linux kernels under medusa.
+# You probably want to make mods here but this is a good starting point.
+#
+
+# Preferences
+setenv cpu_stepping A
+setenv exceptionPrint off
+setenv interrupt_messages off
+setenv lastPCsize 100000
+setenv low_power_mode on
+setenv partialIntelChipSet on
+setenv printIntelMessages off
+setenv prom_write_action halt
+setenv prom_write_messages on
+setenv step_quantum 100
+setenv swizzling on
+setenv tsconsole on
+setenv uart_echo on
+symbols on
+
+# IDE disk params
+setenv diskCylinders 611
+setenv bootDrive C
+setenv diskHeads 16
+setenv diskPath idedisk
+setenv diskPresent 1
+setenv diskSpt 63
+
+# Hardware config
+setenv coherency_type nasid
+setenv cpu_cache_type default
+setenv synergy_cache_type syn_cac_64m_8w
+
+# Numalink config
+setenv route_enable on
+setenv network_type xbar # Select [xbar|router]
+setenv network_warning 0xff
+
+END
+}
+
+
+# ------------------ create control file entries for linux simulations -------------
+create_cf_linux() {
+cat <<END >>$CF
+# Kernel specific options
+setenv mca_on_memory_failure off
+setenv LOADPC 0x00100000 # FPROM load address/entry point (8 digits!)
+sr g 9 0xe000000000520000 # Kernel entry point
+setenv symbol_table vmlinux.sym
+load fprom
+load vmlinux
+
+# Useful breakpoints to always have set. Add more if desired.
+break 0xe000000000505e00 all # dispatch_to_fault_handler
+break panic all # stop on panic
+break die_if_kernel all # may as well stop
+
+END
+}
+
+# ------------------ create control file entries for prom simulations ---------------
+create_cf_prom() {
+ SYM2=""
+ ADDR="0x80000000ff800000"
+ [ "$EMBEDDED_LINUX" != "0" ] || SYM2="setenv symbol_table2 vmlinux.sym"
+ [ "$SIZE" = "8MB" ] || ADDR="0x80000000ffc00000"
+ cat <<END >>$CF
+# PROM specific options
+setenv mca_on_memory_failure on
+setenv LOADPC 0x80000000ffffffb0
+setenv promFile fw.bin
+setenv promAddr $ADDR
+setenv symbol_table fw.sym
+$SYM2
+
+# Useful breakpoints to always have set. Add more if desired.
+break Pr_ivt_gexx all
+break Pr_ivt_brk all
+break Pr_PROM_Panic_Spin all
+break Pr_PROM_Panic all
+break Pr_PROM_C_Panic all
+break Pr_fled_die all
+break Pr_ResetNow all
+break Pr_zzzbkpt all
+
+END
+}
+
+
+# ------------------ create control file entries for memory configuration -------------
+create_cf_memory() {
+cat <<END >>$CF
+# CPU/Memory map format:
+# setenv nodeN_memory_config 0xBSBSBSBS
+# B=banksize (0=unused, 1=64M, 2=128M, .., 5-1G, c=8M, d=16M, e=32M)
+# S=bank enable (0=both disable, 3=both enable, 2=bank1 enable, 1=bank0 enable)
+# rightmost digits are for bank 0, the lowest address.
+# setenv nodeN_nasid <nasid>
+# specifies the NASID for the node. This is used ONLY if booting the kernel.
+# On PROM configurations, set to 0 - PROM will change it later.
+# setenv nodeN_cpu_config <cpu_mask>
+# Set bit number N to 1 to enable cpu N. Ex., a value of 5 enables cpu 0 & 2.
+#
+# Repeat the above 3 commands for each node.
+#
+# For kernel, default to 32MB. Although this is not a valid hardware configuration,
+# it runs faster on medusa. For PROM, 64MB is smallest allowed value.
+
+setenv node0_cpu_config 0x1 # Enable only cpu 0 on the node
+END
+
+if [ $LINUX -eq 1 ] ; then
+cat <<END >>$CF
+setenv node0_nasid 0 # cnode 0 has NASID 0
+setenv node0_memory_config 0xe1 # 32MB
+END
+else
+cat <<END >>$CF
+setenv node0_memory_config 0x11 # 64MB
+END
+fi
+}
+
+# -------------------- set links to linux files -------------------------
+set_linux_links() {
+ if [ -d $D/linux/arch ] ; then
+ D=$D/linux
+ elif [ -d $D/arch -o -e vmlinux.sym ] ; then
+ D=$D
+ else
+ err "cant determine directory for linux binaries"
+ fi
+ rm -rf vmlinux vmlinux.sym fprom
+ ln -s $D/vmlinux vmlinux
+ ln -s $D/vmlinux.sym vmlinux.sym
+ if [ -d $D/arch ] ; then
+ ln -s $D/arch/ia64/sn/fprom/fprom fprom
+ else
+ ln -s $D/fprom fprom
+ fi
+ echo " .. Created links to linux files"
+}
+
+# -------------------- set links to prom files -------------------------
+set_prom_links() {
+ if [ -d $D/stand ] ; then
+ D=$D/stand/arcs/IP37prom/dev
+ elif [ -d $D/sal ] ; then
+ D=$D
+ else
+ err "cant determine directory for PROM binaries"
+ fi
+ SETUP="$D/../../../../.setup"
+ grep -q '^ *setenv *PROMSIZE *8MB' $SETUP
+ if [ $? -eq 0 ] ; then
+ SIZE="8MB"
+ else
+ SIZE="4MB"
+ fi
+ grep -q '^ *setenv *LAUNCH_VMLINUX' $SETUP
+ EMBEDDED_LINUX=$?
+ rm -f fw.bin fw.map fw.sym vmlinux vmlinux.sym fprom
+ SDIR="SN1IA${SIZE}.O"
+ BIN="SN1IAip37prom${SIZE}"
+ ln -s $D/$SDIR/$BIN.bin fw.bin
+ ln -s $D/$SDIR/$BIN.map fw.map
+ ln -s $D/$SDIR/$BIN.sym fw.sym
+ echo " .. Created links to $SIZE prom files"
+ if [ $EMBEDDED_LINUX -eq 0 ] ; then
+ ln -s $D/linux/vmlinux vmlinux
+ ln -s $D/linux/vmlinux.sym vmlinux.sym
+ if [ -d linux/arch ] ; then
+ ln -s $D/linux/arch/ia64/sn/fprom/fprom fprom
+ else
+ ln -s $D/linux/fprom fprom
+ fi
+ echo " .. Created links to embedded linux files in prom tree"
+ fi
+}
+
+# --------------- start of shell script --------------------------------
+OUT="simout"
+FMTMED=0
+STEPCNT=0
+PROM=0
+LINUX=0
+NCF="cf"
+while getopts "HMX:c:o:pk" c ; do
+ case ${c} in
+ H) help;;
+ M) FMTMED=1;;
+ X) STEPCNT=${OPTARG};;
+ c) NCF=${OPTARG};;
+ k) PROM=0;LINUX=1;;
+ p) PROM=1;LINUX=0;;
+ o) OUT=${OPTARG};;
+ \?) exit 1;;
+ esac
+done
+shift `expr ${OPTIND} - 1`
+
+# Check if command is for creating control file and/or links to images.
+if [ $PROM -eq 1 -o $LINUX -eq 1 ] ; then
+ CF=$NCF
+ [ ! -f $CF ] || err "wont overwrite an existing control file ($CF)"
+ if [ $# -gt 0 ] ; then
+ D=$1
+ [ -d $D ] || err "cannot find directory $D"
+ [ $PROM -eq 0 ] || set_prom_links
+ [ $LINUX -eq 0 ] || set_linux_links
+ fi
+ create_cf_header
+ [ $PROM -eq 0 ] || create_cf_prom
+ [ $LINUX -eq 0 ] || create_cf_linux
+ create_cf_memory
+ echo " .. Basic control file created (in $CF). You might want to edit"
+ echo " this file (at least, look at it)."
+ exit 0
+fi
+
+# Verify that the control file exists
+CF=${1:-$NCF}
+[ -f $CF ] || err "No control file exists. For help, type: $0 -H"
+
+# Build the .cf files from the user control file. The .cf file is
+# identical except that the actual start & load addresses are inserted
+# into the file. In addition, the FPROM commands for configuring memory
+# and LIDs are generated.
+
+rm -f .cf .cf1 .cf2
+awk '
+function strtonum(n) {
+ if (substr(n,1,2) != "0x")
+ return int(n)
+ n = substr(n,3)
+ r=0
+ while (length(n) > 0) {
+ r = r*16+(index("0123456789abcdef", substr(n,1,1))-1)
+ n = substr(n,2)
+ }
+ return r
+ }
+/^#/ {next}
+/^$/ {next}
+/^setenv *LOADPC/ {loadpc = $3; next}
+/^setenv *node._cpu_config/ {n=int(substr($2,5,1)); cpuconf[n] = strtonum($3); print; next}
+/^setenv *node._memory_config/ {n=int(substr($2,5,1)); memconf[n] = strtonum($3); print; next}
+/^setenv *node._nasid/ {n=int(substr($2,5,1)); nasid[n] = strtonum($3); print; next}
+ {print}
+END {
+ # Generate the memmap info that starts at the beginning of
+ # the node the kernel was loaded on.
+ loadnasid = nasid[0]
+ cnode = 0
+ for (i=0; i<128; i++) {
+ if (memconf[i] != "") {
+ printf "sm 0x%x%08x 0x%x%04x%04x\n",
+ 2*loadnasid, 8*cnodes+8, memconf[i], cpuconf[i], nasid[i]
+ cnodes++
+ cpus += substr("0112122312232334", cpuconf[i]+1,1)
+ }
+ }
+ printf "sm 0x%x00000000 0x%x%08x\n", 2*loadnasid, cnodes, cpus
+ printf "setenv number_of_nodes %d\n", cnodes
+
+ # Now set the starting PC for each cpu.
+ cnode = 0
+ lowcpu=-1
+ for (i=0; i<128; i++) {
+ if (memconf[i] != "") {
+ printf "setnode %d\n", cnode
+ conf = cpuconf[i]
+ for (j=0; j<4; j++) {
+ if (conf != int(conf/2)*2) {
+ printf "setcpu %d\n", j
+ if (length(loadpc) == 18)
+ printf "sr pc %s\n", loadpc
+ else
+ printf "sr pc 0x%x%s\n", 2*loadnasid, substr(loadpc,3)
+ if (lowcpu == -1)
+ lowcpu = j
+ }
+ conf = int(conf/2)
+ }
+ cnode++
+ }
+ }
+ printf "setnode 0\n"
+ printf "setcpu %d\n", lowcpu
+ }
+' <$CF >.cf
+
+# Now build the .cf1 & .cf2 control files.
+CF2_LINES="^sm |^break |^run |^si |^quit |^symbols "
+egrep "$CF2_LINES" .cf >.cf2
+egrep -v "$CF2_LINES" .cf >.cf1
+if [ $STEPCNT -ne 0 ] ; then
+ echo "s $STEPCNT" >>.cf2
+ echo "lastpc 1000" >>.cf2
+ echo "q" >>.cf2
+fi
+echo "script-on $OUT" >>.cf2
+
+# Now start medusa....
+if [ $FMTMED -ne 0 ] ; then
+ $MEDUSA -system mpsn1 -c .cf1 -i .cf2 | fmtmedusa
+elif [ $STEPCNT -eq 0 ] ; then
+ $MEDUSA -system mpsn1 -c .cf1 -i .cf2
+else
+ $MEDUSA -system mpsn1 -c .cf1 -i .cf2 2>&1
+fi
diff --git a/arch/ia64/sn/io/Makefile b/arch/ia64/sn/io/Makefile
new file mode 100644
index 000000000..887896bbd
--- /dev/null
+++ b/arch/ia64/sn/io/Makefile
@@ -0,0 +1,32 @@
+#
+# 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) Jack Steiner (steiner@sgi.com)
+#
+#
+# Makefile for the linux kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+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
+O_TARGET := sgiio.o
+O_OBJS := stubs.o sgi_if.o pciio.o pcibr.o xtalk.o xbow.o xswitch.o hubspc.o \
+ klgraph_hack.o io.o hubdev.o \
+ hcl.o labelcl.o invent.o klgraph.o klconflib.o sgi_io_sim.o \
+ module.o sgi_io_init.o klgraph_hack.o ml_SN_init.o \
+ ml_SN_intr.o ip37.o \
+ ml_iograph.o hcl_util.o cdl.o \
+ mem_refcnt.o devsupport.o alenlist.o pci_bus_cvlink.o \
+ eeprom.o pci.o pci_dma.o l1.o l1_command.o
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/ia64/sn/io/alenlist.c b/arch/ia64/sn/io/alenlist.c
new file mode 100644
index 000000000..7d8e0e158
--- /dev/null
+++ b/arch/ia64/sn/io/alenlist.c
@@ -0,0 +1,900 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+/* Implementation of Address/Length Lists. */
+
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/alenlist.h>
+#include <asm/sn/mmzone_sn1.h>
+
+/*
+ * Logically, an Address/Length List is a list of Pairs, where each pair
+ * holds an Address and a Length, all in some Address Space. In this
+ * context, "Address Space" is a particular Crosstalk Widget address
+ * space, a PCI device address space, a VME bus address space, a
+ * physical memory address space, etc.
+ *
+ * The main use for these Lists is to provide a single mechanism that
+ * describes where in an address space a DMA occurs. This allows the
+ * various I/O Bus support layers to provide a single interface for
+ * DMA mapping and DMA translation without regard to how the DMA target
+ * was specified by upper layers. The upper layers commonly specify a
+ * DMA target via a buf structure page list, a kernel virtual address,
+ * a user virtual address, a vector of addresses (a la uio and iov),
+ * or possibly a pfn list.
+ *
+ * Address/Length Lists also enable drivers to take advantage of their
+ * inate scatter/gather capabilities in systems where some address
+ * translation may be required between bus adapters. The driver forms
+ * a List that represents physical memory targets. This list is passed
+ * to the various adapters, which apply various translations. The final
+ * list that's returned to the driver is in terms of its local address
+ * address space -- addresses which can be passed off to a scatter/gather
+ * capable DMA controller.
+ *
+ * The current implementation is intended to be useful both in kernels
+ * that support interrupt threads (INTR_KTHREAD) and in systems that do
+ * not support interrupt threads. Of course, in the latter case, some
+ * interfaces can be called only within a suspendable context.
+ *
+ * Basic operations on Address/Length Lists include:
+ * alenlist_create Create a list
+ * alenlist_clear Clear a list
+ * alenlist_destroy Destroy a list
+ * alenlist_append Append a Pair to the end of a list
+ * alenlist_replace Replace a Pair in the middle of a list
+ * alenlist_get Get an Address/Length Pair from a list
+ * alenlist_size Return the number of Pairs in a list
+ * alenlist_concat Append one list to the end of another
+ * alenlist_clone Create a new copy of a list
+ *
+ * Operations that convert from upper-level specifications to Address/
+ * Length Lists currently include:
+ * kvaddr_to_alenlist Convert from a kernel virtual address
+ * uvaddr_to_alenlist Convert from a user virtual address
+ * buf_to_alenlist Convert from a buf structure
+ * alenlist_done Tell system that we're done with an alenlist
+ * obtained from a conversion.
+ * Additional convenience operations:
+ * alenpair_init Create a list and initialize it with a Pair
+ * alenpair_get Peek at the first pair on a List
+ *
+ * A supporting type for Address/Length Lists is an alenlist_cursor_t. A
+ * cursor marks a position in a List, and determines which Pair is fetched
+ * by alenlist_get.
+ * alenlist_cursor_create Allocate and initialize a cursor
+ * alenlist_cursor_destroy Free space consumed by a cursor
+ * alenlist_cursor_init (Re-)Initialize a cursor to point
+ * to the start of a list
+ * alenlist_cursor_clone Clone a cursor (at the current offset)
+ * alenlist_cursor_offset Return the number of bytes into
+ * a list that this cursor marks
+ * Multiple cursors can point at various points into a List. Also, each
+ * list maintains one "internal cursor" which may be updated by alenlist_clear
+ * and alenlist_get. If calling code simply wishes to scan sequentially
+ * through a list starting at the beginning, and if it is the only user of
+ * a list, it can rely on this internal cursor rather than managing a
+ * separate explicit cursor.
+ *
+ * The current implementation allows callers to allocate both cursors and
+ * the lists as local stack (structure) variables. This allows for some
+ * extra efficiency at the expense of forward binary compatibility. It
+ * is recommended that customer drivers refrain from local allocation.
+ * In fact, we likely will choose to move the structures out of the public
+ * header file into a private place in order to discourage this usage.
+ *
+ * Currently, no locking is provided by the alenlist implementation.
+ *
+ * Implementation notes:
+ * For efficiency, Pairs are grouped into "chunks" of, say, 32 Pairs
+ * and a List consists of some number of these chunks. Chunks are completely
+ * invisible to calling code. Chunks should be large enough to hold most
+ * standard-sized DMA's, but not so large that they consume excessive space.
+ *
+ * It is generally expected that Lists will be constructed at one time and
+ * scanned at a later time. It is NOT expected that drivers will scan
+ * a List while the List is simultaneously extended, although this is
+ * theoretically possible with sufficient upper-level locking.
+ *
+ * In order to support demands of Real-Time drivers and in order to support
+ * swapping under low-memory conditions, we support the concept of a
+ * "pre-allocated fixed-sized List". After creating a List with
+ * alenlist_create, a driver may explicitly grow the list (via "alenlist_grow")
+ * to a specific number of Address/Length pairs. It is guaranteed that future
+ * operations involving this list will never automatically grow the list
+ * (i.e. if growth is ever required, the operation will fail). Additionally,
+ * operations that use alenlist's (e.g. DMA operations) accept a flag which
+ * causes processing to take place "in-situ"; that is, the input alenlist
+ * entries are replaced with output alenlist entries. The combination of
+ * pre-allocated Lists and in-situ processing allows us to avoid the
+ * potential deadlock scenario where we sleep (waiting for memory) in the
+ * swap out path.
+ *
+ * For debugging, we track the number of allocated Lists in alenlist_count
+ * the number of allocated chunks in alenlist_chunk_count, and the number
+ * of allocate cursors in alenlist_cursor_count. We also provide a debug
+ * routine, alenlist_show, which dumps the contents of an Address/Length List.
+ *
+ * Currently, Lists are formed by drivers on-demand. Eventually, we may
+ * associate an alenlist with a buf structure and keep it up to date as
+ * we go along. In that case, buf_to_alenlist simply returns a pointer
+ * to the existing List, and increments the Lists's reference count.
+ * alenlist_done would decrement the reference count and destroys the List
+ * if it was the last reference.
+ *
+ * Eventually alenlist's may allow better support for user-level scatter/
+ * gather operations (e.g. via readv/writev): With proper support, we
+ * could potentially handle a vector of reads with a single scatter/gather
+ * DMA operation. This could be especially useful on NUMA systems where
+ * there's more of a reason for users to use vector I/O operations.
+ *
+ * Eventually, alenlist's may replace kaio lists, vhand page lists,
+ * buffer cache pfdat lists, DMA page lists, etc.
+ */
+
+/* Opaque data types */
+
+/* An Address/Length pair. */
+typedef struct alen_s {
+ alenaddr_t al_addr;
+ size_t al_length;
+} alen_t;
+
+/*
+ * Number of elements in one chunk of an Address/Length List.
+ *
+ * This size should be sufficient to hold at least an "average" size
+ * DMA request. Must be at least 1, and should be a power of 2,
+ * for efficiency.
+ */
+#define ALEN_CHUNK_SZ ((512*1024)/NBPP)
+
+/*
+ * A fixed-size set of Address/Length Pairs. Chunks of Pairs are strung together
+ * to form a complete Address/Length List. Chunking is entirely hidden within the
+ * alenlist implementation, and it simply makes allocation and growth of lists more
+ * efficient.
+ */
+typedef struct alenlist_chunk_s {
+ alen_t alc_pair[ALEN_CHUNK_SZ];/* list of addr/len pairs */
+ struct alenlist_chunk_s *alc_next; /* point to next chunk of pairs */
+} *alenlist_chunk_t;
+
+/*
+ * An Address/Length List. An Address/Length List is allocated with alenlist_create.
+ * Alternatively, a list can be allocated on the stack (local variable of type
+ * alenlist_t) and initialized with alenpair_init or with a combination of
+ * alenlist_clear and alenlist_append, etc. Code which statically allocates these
+ * structures loses forward binary compatibility!
+ *
+ * A statically allocated List is sufficiently large to hold ALEN_CHUNK_SZ pairs.
+ */
+struct alenlist_s {
+ unsigned short al_flags;
+ unsigned short al_logical_size; /* logical size of list, in pairs */
+ unsigned short al_actual_size; /* actual size of list, in pairs */
+ struct alenlist_chunk_s *al_last_chunk; /* pointer to last logical chunk */
+ struct alenlist_cursor_s al_cursor; /* internal cursor */
+ struct alenlist_chunk_s al_chunk; /* initial set of pairs */
+ alenaddr_t al_compaction_address; /* used to compact pairs */
+};
+
+/* al_flags field */
+#define AL_FIXED_SIZE 0x1 /* List is pre-allocated, and of fixed size */
+
+
+zone_t *alenlist_zone = NULL;
+zone_t *alenlist_chunk_zone = NULL;
+zone_t *alenlist_cursor_zone = NULL;
+
+#if DEBUG
+int alenlist_count=0; /* Currently allocated Lists */
+int alenlist_chunk_count = 0; /* Currently allocated chunks */
+int alenlist_cursor_count = 0; /* Currently allocate cursors */
+#define INCR_COUNT(ptr) atomicAddInt((ptr), 1);
+#define DECR_COUNT(ptr) atomicAddInt((ptr), -1);
+#else
+#define INCR_COUNT(ptr)
+#define DECR_COUNT(ptr)
+#endif /* DEBUG */
+
+#if DEBUG
+static void alenlist_show(alenlist_t);
+#endif /* DEBUG */
+
+/*
+ * Initialize Address/Length List management. One time initialization.
+ */
+void
+alenlist_init(void)
+{
+ alenlist_zone = kmem_zone_init(sizeof(struct alenlist_s), "alenlist");
+ alenlist_chunk_zone = kmem_zone_init(sizeof(struct alenlist_chunk_s), "alchunk");
+ alenlist_cursor_zone = kmem_zone_init(sizeof(struct alenlist_cursor_s), "alcursor");
+#if DEBUG
+ idbg_addfunc("alenshow", alenlist_show);
+#endif /* DEBUG */
+}
+
+
+/*
+ * Initialize an Address/Length List cursor.
+ */
+static void
+do_cursor_init(alenlist_t alenlist, alenlist_cursor_t cursorp)
+{
+ cursorp->al_alenlist = alenlist;
+ cursorp->al_offset = 0;
+ cursorp->al_chunk = &alenlist->al_chunk;
+ cursorp->al_index = 0;
+ cursorp->al_bcount = 0;
+}
+
+
+/*
+ * Create an Address/Length List, and clear it.
+ * Set the cursor to the beginning.
+ */
+alenlist_t
+alenlist_create(unsigned flags)
+{
+ alenlist_t alenlist;
+
+ alenlist = kmem_zone_alloc(alenlist_zone, flags & AL_NOSLEEP ? VM_NOSLEEP : 0);
+ if (alenlist) {
+ INCR_COUNT(&alenlist_count);
+
+ alenlist->al_flags = 0;
+ alenlist->al_logical_size = 0;
+ alenlist->al_actual_size = ALEN_CHUNK_SZ;
+ alenlist->al_last_chunk = &alenlist->al_chunk;
+ alenlist->al_chunk.alc_next = NULL;
+ do_cursor_init(alenlist, &alenlist->al_cursor);
+ }
+
+ return(alenlist);
+}
+
+
+/*
+ * Grow an Address/Length List so that all resources needed to contain
+ * the specified number of Pairs are pre-allocated. An Address/Length
+ * List that has been explicitly "grown" will never *automatically*
+ * grow, shrink, or be destroyed.
+ *
+ * Pre-allocation is useful for Real-Time drivers and for drivers that
+ * may be used along the swap-out path and therefore cannot afford to
+ * sleep until memory is freed.
+ *
+ * The cursor is set to the beginning of the list.
+ */
+int
+alenlist_grow(alenlist_t alenlist, size_t npairs)
+{
+ /*
+ * This interface should be used relatively rarely, so
+ * the implementation is kept simple: We clear the List,
+ * then append npairs bogus entries. Finally, we mark
+ * the list as FIXED_SIZE and re-initialize the internal
+ * cursor.
+ */
+
+ /*
+ * Temporarily mark as non-fixed size, since we're about
+ * to shrink and expand it.
+ */
+ alenlist->al_flags &= ~AL_FIXED_SIZE;
+
+ /* Free whatever was in the alenlist. */
+ alenlist_clear(alenlist);
+
+ /* Allocate everything that we need via automatic expansion. */
+ while (npairs--)
+ if (alenlist_append(alenlist, 0, 0, AL_NOCOMPACT) == ALENLIST_FAILURE)
+ return(ALENLIST_FAILURE);
+
+ /* Now, mark as FIXED_SIZE */
+ alenlist->al_flags |= AL_FIXED_SIZE;
+
+ /* Clear out bogus entries */
+ alenlist_clear(alenlist);
+
+ /* Initialize internal cursor to the beginning */
+ do_cursor_init(alenlist, &alenlist->al_cursor);
+
+ return(ALENLIST_SUCCESS);
+}
+
+
+/*
+ * Clear an Address/Length List so that it holds no pairs.
+ */
+void
+alenlist_clear(alenlist_t alenlist)
+{
+ alenlist_chunk_t chunk, freechunk;
+
+ /*
+ * If this List is not FIXED_SIZE, free all the
+ * extra chunks.
+ */
+ if (!(alenlist->al_flags & AL_FIXED_SIZE)) {
+ /* First, free any extension alenlist chunks */
+ chunk = alenlist->al_chunk.alc_next;
+ while (chunk) {
+ freechunk = chunk;
+ chunk = chunk->alc_next;
+ kmem_zone_free(alenlist_chunk_zone, freechunk);
+ DECR_COUNT(&alenlist_chunk_count);
+ }
+ alenlist->al_actual_size = ALEN_CHUNK_SZ;
+ alenlist->al_chunk.alc_next = NULL;
+ }
+
+ alenlist->al_logical_size = 0;
+ alenlist->al_last_chunk = &alenlist->al_chunk;
+ do_cursor_init(alenlist, &alenlist->al_cursor);
+}
+
+
+/*
+ * Create and initialize an Address/Length Pair.
+ * This is intended for degenerate lists, consisting of a single
+ * address/length pair.
+ */
+alenlist_t
+alenpair_init( alenaddr_t address,
+ size_t length)
+{
+ alenlist_t alenlist;
+
+ alenlist = alenlist_create(0);
+
+ alenlist->al_logical_size = 1;
+ ASSERT(alenlist->al_last_chunk == &alenlist->al_chunk);
+ alenlist->al_chunk.alc_pair[0].al_length = length;
+ alenlist->al_chunk.alc_pair[0].al_addr = address;
+
+ return(alenlist);
+}
+
+/*
+ * Return address/length from a degenerate (1-pair) List, or
+ * first pair from a larger list. Does NOT update the internal cursor,
+ * so this is an easy way to peek at a start address.
+ */
+int
+alenpair_get( alenlist_t alenlist,
+ alenaddr_t *address,
+ size_t *length)
+{
+ if (alenlist->al_logical_size == 0)
+ return(ALENLIST_FAILURE);
+
+ *length = alenlist->al_chunk.alc_pair[0].al_length;
+ *address = alenlist->al_chunk.alc_pair[0].al_addr;
+ return(ALENLIST_SUCCESS);
+}
+
+
+/*
+ * Destroy an Address/Length List.
+ */
+void
+alenlist_destroy(alenlist_t alenlist)
+{
+ if (alenlist == NULL)
+ return;
+
+ /*
+ * Turn off FIXED_SIZE so this List can be
+ * automatically shrunk.
+ */
+ alenlist->al_flags &= ~AL_FIXED_SIZE;
+
+ /* Free extension chunks first */
+ if (alenlist->al_chunk.alc_next)
+ alenlist_clear(alenlist);
+
+ /* Now, free the alenlist itself */
+ kmem_zone_free(alenlist_zone, alenlist);
+ DECR_COUNT(&alenlist_count);
+}
+
+/*
+ * Release an Address/Length List.
+ * This is in preparation for a day when alenlist's may be longer-lived, and
+ * perhaps associated with a buf structure. We'd add a reference count, and
+ * this routine would decrement the count. For now, we create alenlist's on
+ * on demand and free them when done. If the driver is not explicitly managing
+ * a List for its own use, it should call alenlist_done rather than alenlist_destroy.
+ */
+void
+alenlist_done(alenlist_t alenlist)
+{
+ alenlist_destroy(alenlist);
+}
+
+
+/*
+ * Append another address/length to the end of an Address/Length List,
+ * growing the list if permitted and necessary.
+ *
+ * Returns: SUCCESS/FAILURE
+ */
+int
+alenlist_append( alenlist_t alenlist, /* append to this list */
+ alenaddr_t address, /* address to append */
+ size_t length, /* length to append */
+ unsigned flags)
+{
+ alen_t *alenp;
+ int index, last_index;
+
+ index = alenlist->al_logical_size % ALEN_CHUNK_SZ;
+
+ if ((alenlist->al_logical_size > 0)) {
+ /*
+ * See if we can compact this new pair in with the previous entry.
+ * al_compaction_address holds that value that we'd need to see
+ * in order to compact.
+ */
+ if (!(flags & AL_NOCOMPACT) &&
+ (alenlist->al_compaction_address == address)) {
+ last_index = (alenlist->al_logical_size-1) % ALEN_CHUNK_SZ;
+ alenp = &(alenlist->al_last_chunk->alc_pair[last_index]);
+ alenp->al_length += length;
+ alenlist->al_compaction_address += length;
+ return(ALENLIST_SUCCESS);
+ }
+
+ /*
+ * If we're out of room in this chunk, move to a new chunk.
+ */
+ if (index == 0) {
+ if (alenlist->al_flags & AL_FIXED_SIZE) {
+ alenlist->al_last_chunk = alenlist->al_last_chunk->alc_next;
+
+ /* If we're out of space in a FIXED_SIZE List, quit. */
+ if (alenlist->al_last_chunk == NULL) {
+ ASSERT(alenlist->al_logical_size == alenlist->al_actual_size);
+ return(ALENLIST_FAILURE);
+ }
+ } else {
+ alenlist_chunk_t new_chunk;
+
+ new_chunk = kmem_zone_alloc(alenlist_chunk_zone,
+ flags & AL_NOSLEEP ? VM_NOSLEEP : 0);
+
+ if (new_chunk == NULL)
+ return(ALENLIST_FAILURE);
+
+ alenlist->al_last_chunk->alc_next = new_chunk;
+ new_chunk->alc_next = NULL;
+ alenlist->al_last_chunk = new_chunk;
+ alenlist->al_actual_size += ALEN_CHUNK_SZ;
+ INCR_COUNT(&alenlist_chunk_count);
+ }
+ }
+ }
+
+ alenp = &(alenlist->al_last_chunk->alc_pair[index]);
+ alenp->al_addr = address;
+ alenp->al_length = length;
+
+ alenlist->al_logical_size++;
+ alenlist->al_compaction_address = address + length;
+
+ return(ALENLIST_SUCCESS);
+}
+
+
+/*
+ * Replace an item in an Address/Length List. Cursor is updated so
+ * that alenlist_get will get the next item in the list. This interface
+ * is not very useful for drivers; but it is useful to bus providers
+ * that need to translate between address spaced in situ. The old Address
+ * and Length are returned.
+ */
+/* ARGSUSED */
+int
+alenlist_replace( alenlist_t alenlist, /* in: replace in this list */
+ alenlist_cursor_t cursorp, /* inout: which item to replace */
+ alenaddr_t *addrp, /* inout: address */
+ size_t *lengthp, /* inout: length */
+ unsigned flags)
+{
+ alen_t *alenp;
+ alenlist_chunk_t chunk;
+ unsigned int index;
+ size_t length;
+ alenaddr_t addr;
+
+ if ((addrp == NULL) || (lengthp == NULL))
+ return(ALENLIST_FAILURE);
+
+ if (alenlist->al_logical_size == 0)
+ return(ALENLIST_FAILURE);
+
+ addr = *addrp;
+ length = *lengthp;
+
+ /*
+ * If no cursor explicitly specified, use the Address/Length List's
+ * internal cursor.
+ */
+ if (cursorp == NULL)
+ cursorp = &alenlist->al_cursor;
+
+ chunk = cursorp->al_chunk;
+ index = cursorp->al_index;
+
+ ASSERT(cursorp->al_alenlist == alenlist);
+ if (cursorp->al_alenlist != alenlist)
+ return(ALENLIST_FAILURE);
+
+ alenp = &chunk->alc_pair[index];
+
+ /* Return old values */
+ *addrp = alenp->al_length;
+ *lengthp = alenp->al_addr;
+
+ /* Set up new values */
+ alenp->al_length = length;
+ alenp->al_addr = addr;
+
+ /* Update cursor to point to next item */
+ cursorp->al_bcount = length;
+
+ return(ALENLIST_SUCCESS);
+}
+
+
+/*
+ * Initialize a cursor in order to walk an alenlist.
+ * An alenlist_cursor always points to the last thing that was obtained
+ * from the list. If al_chunk is NULL, then nothing has yet been obtained.
+ *
+ * Note: There is an "internal cursor" associated with every Address/Length List.
+ * For users that scan sequentially through a List, it is more efficient to
+ * simply use the internal cursor. The caller must insure that no other users
+ * will simultaneously scan the List. The caller can reposition the internal
+ * cursor by calling alenlist_cursor_init with a NULL cursorp.
+ */
+int
+alenlist_cursor_init(alenlist_t alenlist, size_t offset, alenlist_cursor_t cursorp)
+{
+ size_t byte_count;
+
+ if (cursorp == NULL)
+ cursorp = &alenlist->al_cursor;
+
+ /* Get internal cursor's byte count for use as a hint.
+ *
+ * If the internal cursor points passed the point that we're interested in,
+ * we need to seek forward from the beginning. Otherwise, we can seek forward
+ * from the internal cursor.
+ */
+ if ((offset > 0) &&
+ ((byte_count = alenlist_cursor_offset(alenlist, (alenlist_cursor_t)NULL)) <= offset)) {
+ offset -= byte_count;
+ alenlist_cursor_clone(alenlist, NULL, cursorp);
+ } else
+ do_cursor_init(alenlist, cursorp);
+
+ /* We could easily speed this up, but it shouldn't be used very often. */
+ while (offset != 0) {
+ alenaddr_t addr;
+ size_t length;
+
+ if (alenlist_get(alenlist, cursorp, offset, &addr, &length, 0) != ALENLIST_SUCCESS)
+ return(ALENLIST_FAILURE);
+ offset -= length;
+ }
+ return(ALENLIST_SUCCESS);
+}
+
+
+/*
+ * Copy a cursor. The source cursor is either an internal alenlist cursor
+ * or an explicit cursor.
+ */
+int
+alenlist_cursor_clone( alenlist_t alenlist,
+ alenlist_cursor_t cursorp_in,
+ alenlist_cursor_t cursorp_out)
+{
+ ASSERT(cursorp_out);
+
+ if (alenlist && cursorp_in)
+ if (alenlist != cursorp_in->al_alenlist)
+ return(ALENLIST_FAILURE);
+
+ if (alenlist)
+ *cursorp_out = alenlist->al_cursor; /* small structure copy */
+ else if (cursorp_in)
+ *cursorp_out = *cursorp_in; /* small structure copy */
+ else
+ return(ALENLIST_FAILURE); /* no source */
+
+ return(ALENLIST_SUCCESS);
+}
+
+/*
+ * Return the number of bytes passed so far according to the specified cursor.
+ * If cursorp is NULL, use the alenlist's internal cursor.
+ */
+size_t
+alenlist_cursor_offset(alenlist_t alenlist, alenlist_cursor_t cursorp)
+{
+ ASSERT(!alenlist || !cursorp || (alenlist == cursorp->al_alenlist));
+
+ if (cursorp == NULL) {
+ ASSERT(alenlist);
+ cursorp = &alenlist->al_cursor;
+ }
+
+ return(cursorp->al_offset);
+}
+
+/*
+ * Allocate and initialize an Address/Length List cursor.
+ */
+alenlist_cursor_t
+alenlist_cursor_create(alenlist_t alenlist, unsigned flags)
+{
+ alenlist_cursor_t cursorp;
+
+ ASSERT(alenlist != NULL);
+ cursorp = kmem_zone_alloc(alenlist_cursor_zone, flags & AL_NOSLEEP ? VM_NOSLEEP : 0);
+ if (cursorp) {
+ INCR_COUNT(&alenlist_cursor_count);
+ alenlist_cursor_init(alenlist, 0, cursorp);
+ }
+ return(cursorp);
+}
+
+/*
+ * Free an Address/Length List cursor.
+ */
+void
+alenlist_cursor_destroy(alenlist_cursor_t cursorp)
+{
+ DECR_COUNT(&alenlist_cursor_count);
+ kmem_zone_free(alenlist_cursor_zone, cursorp);
+}
+
+
+/*
+ * Fetch an address/length pair from an Address/Length List. Update
+ * the "cursor" so that next time this routine is called, we'll get
+ * the next address range. Never return a length that exceeds maxlength
+ * (if non-zero). If maxlength is a power of 2, never return a length
+ * that crosses a maxlength boundary. [This may seem strange at first,
+ * but it's what many drivers want.]
+ *
+ * Returns: SUCCESS/FAILURE
+ */
+int
+alenlist_get( alenlist_t alenlist, /* in: get from this list */
+ alenlist_cursor_t cursorp, /* inout: which item to get */
+ size_t maxlength, /* in: at most this length */
+ alenaddr_t *addrp, /* out: address */
+ size_t *lengthp, /* out: length */
+ unsigned flags)
+{
+ alen_t *alenp;
+ alenlist_chunk_t chunk;
+ unsigned int index;
+ size_t bcount;
+ size_t length;
+
+ /*
+ * If no cursor explicitly specified, use the Address/Length List's
+ * internal cursor.
+ */
+ if (cursorp == NULL) {
+ if (alenlist->al_logical_size == 0)
+ return(ALENLIST_FAILURE);
+ cursorp = &alenlist->al_cursor;
+ }
+
+ chunk = cursorp->al_chunk;
+ index = cursorp->al_index;
+ bcount = cursorp->al_bcount;
+
+ ASSERT(cursorp->al_alenlist == alenlist);
+ if (cursorp->al_alenlist != alenlist)
+ return(ALENLIST_FAILURE);
+
+ alenp = &chunk->alc_pair[index];
+ length = alenp->al_length - bcount;
+
+ /* Bump up to next pair, if we're done with this pair. */
+ if (length == 0) {
+ cursorp->al_bcount = bcount = 0;
+ cursorp->al_index = index = (index + 1) % ALEN_CHUNK_SZ;
+
+ /* Bump up to next chunk, if we're done with this chunk. */
+ if (index == 0) {
+ if (cursorp->al_chunk == alenlist->al_last_chunk)
+ return(ALENLIST_FAILURE);
+ chunk = chunk->alc_next;
+ ASSERT(chunk != NULL);
+ } else {
+ /* If in last chunk, don't go beyond end. */
+ if (cursorp->al_chunk == alenlist->al_last_chunk) {
+ int last_size = alenlist->al_logical_size % ALEN_CHUNK_SZ;
+ if (last_size && (index >= last_size))
+ return(ALENLIST_FAILURE);
+ }
+ }
+
+ alenp = &chunk->alc_pair[index];
+ length = alenp->al_length;
+ }
+
+ /* Constrain what we return according to maxlength */
+ if (maxlength) {
+ size_t maxlen1 = maxlength - 1;
+
+ if ((maxlength & maxlen1) == 0) /* power of 2 */
+ maxlength -=
+ ((alenp->al_addr + cursorp->al_bcount) & maxlen1);
+
+ length = MIN(maxlength, length);
+ }
+
+ /* Update the cursor, if desired. */
+ if (!(flags & AL_LEAVE_CURSOR)) {
+ cursorp->al_bcount += length;
+ cursorp->al_chunk = chunk;
+ }
+
+ *lengthp = length;
+ *addrp = alenp->al_addr + bcount;
+
+ return(ALENLIST_SUCCESS);
+}
+
+
+/*
+ * Return the number of pairs in the specified Address/Length List.
+ * (For FIXED_SIZE Lists, this returns the logical size of the List,
+ * not the actual capacity of the List.)
+ */
+int
+alenlist_size(alenlist_t alenlist)
+{
+ return(alenlist->al_logical_size);
+}
+
+
+/*
+ * Concatenate two Address/Length Lists.
+ */
+void
+alenlist_concat(alenlist_t from,
+ alenlist_t to)
+{
+ struct alenlist_cursor_s cursor;
+ alenaddr_t addr;
+ size_t length;
+
+ alenlist_cursor_init(from, 0, &cursor);
+
+ while(alenlist_get(from, &cursor, (size_t)0, &addr, &length, 0) == ALENLIST_SUCCESS)
+ alenlist_append(to, addr, length, 0);
+}
+
+/*
+ * Create a copy of a list.
+ * (Not all attributes of the old list are cloned. For instance, if
+ * a FIXED_SIZE list is cloned, the resulting list is NOT FIXED_SIZE.)
+ */
+alenlist_t
+alenlist_clone(alenlist_t old_list, unsigned flags)
+{
+ alenlist_t new_list;
+
+ new_list = alenlist_create(flags);
+ if (new_list != NULL)
+ alenlist_concat(old_list, new_list);
+
+ return(new_list);
+}
+
+
+/*
+ * Convert a kernel virtual address to a Physical Address/Length List.
+ */
+alenlist_t
+kvaddr_to_alenlist(alenlist_t alenlist, caddr_t kvaddr, size_t length, unsigned flags)
+{
+ alenaddr_t paddr;
+ long offset;
+ size_t piece_length;
+ int created_alenlist;
+
+ if (length <=0)
+ return(NULL);
+
+ /* If caller supplied a List, use it. Otherwise, allocate one. */
+ if (alenlist == NULL) {
+ alenlist = alenlist_create(0);
+ created_alenlist = 1;
+ } else {
+ alenlist_clear(alenlist);
+ created_alenlist = 0;
+ }
+
+ paddr = kvtophys(kvaddr);
+ offset = poff(kvaddr);
+
+ /* Handle first page */
+ piece_length = MIN(NBPP - offset, length);
+ if (alenlist_append(alenlist, paddr, piece_length, flags) == ALENLIST_FAILURE)
+ goto failure;
+ length -= piece_length;
+ kvaddr += piece_length;
+
+ /* Handle middle pages */
+ while (length >= NBPP) {
+ paddr = kvtophys(kvaddr);
+ if (alenlist_append(alenlist, paddr, NBPP, flags) == ALENLIST_FAILURE)
+ goto failure;
+ length -= NBPP;
+ kvaddr += NBPP;
+ }
+
+ /* Handle last page */
+ if (length) {
+ ASSERT(length < NBPP);
+ paddr = kvtophys(kvaddr);
+ if (alenlist_append(alenlist, paddr, length, flags) == ALENLIST_FAILURE)
+ goto failure;
+ }
+
+ alenlist_cursor_init(alenlist, 0, NULL);
+ return(alenlist);
+
+failure:
+ if (created_alenlist)
+ alenlist_destroy(alenlist);
+ return(NULL);
+}
+
+
+#if DEBUG
+static void
+alenlist_show(alenlist_t alenlist)
+{
+ struct alenlist_cursor_s cursor;
+ alenaddr_t addr;
+ size_t length;
+ int i = 0;
+
+ alenlist_cursor_init(alenlist, 0, &cursor);
+
+ qprintf("Address/Length List@0x%x:\n", alenlist);
+ qprintf("logical size=0x%x actual size=0x%x last_chunk at 0x%x\n",
+ alenlist->al_logical_size, alenlist->al_actual_size,
+ alenlist->al_last_chunk);
+ qprintf("cursor: chunk=0x%x index=%d offset=0x%x\n",
+ alenlist->al_cursor.al_chunk,
+ alenlist->al_cursor.al_index,
+ alenlist->al_cursor.al_bcount);
+ while(alenlist_get(alenlist, &cursor, (size_t)0, &addr, &length, 0) == ALENLIST_SUCCESS)
+ qprintf("%d:\t0x%lx 0x%lx\n", ++i, addr, length);
+}
+#endif /* DEBUG */
diff --git a/arch/ia64/sn/io/cdl.c b/arch/ia64/sn/io/cdl.c
new file mode 100644
index 000000000..eb854b207
--- /dev/null
+++ b/arch/ia64/sn/io/cdl.c
@@ -0,0 +1,230 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <asm/sn/sgi.h>
+#include <asm/io.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/pci/bridge.h>
+#include "asm/sn/ioerror_handling.h"
+#include <asm/sn/xtalk/xbow.h>
+
+#ifdef BRINGUP
+/* these get called directly in cdl_add_connpt in fops bypass hack */
+extern int pcibr_attach(devfs_handle_t);
+extern int xbow_attach(devfs_handle_t);
+#endif /* BRINGUP */
+
+/*
+ * cdl: Connection and Driver List
+ *
+ * We are not porting this to Linux. Devices are registered via
+ * the normal Linux PCI layer. This is a very simplified version
+ * of cdl that will allow us to register and call our very own
+ * IO Infrastructure Drivers e.g. pcibr.
+ */
+
+struct cdl {
+ int part_num;
+ int mfg_num;
+ int (*attach) (devfs_handle_t);
+} dummy_reg;
+
+typedef struct cdl *cdl_p;
+
+#define MAX_SGI_IO_INFRA_DRVR 4
+struct cdl sgi_infrastructure_drivers[MAX_SGI_IO_INFRA_DRVR] =
+{
+ { XBRIDGE_WIDGET_PART_NUM, XBRIDGE_WIDGET_MFGR_NUM, pcibr_attach /* &pcibr_fops */},
+ { BRIDGE_WIDGET_PART_NUM, BRIDGE_WIDGET_MFGR_NUM, pcibr_attach /* &pcibr_fops */},
+ { XXBOW_WIDGET_PART_NUM, XXBOW_WIDGET_MFGR_NUM, xbow_attach /* &xbow_fops */},
+ { XBOW_WIDGET_PART_NUM, XBOW_WIDGET_MFGR_NUM, xbow_attach /* &xbow_fops */},
+};
+
+/*
+ * cdl_new: Called by pciio and xtalk.
+ */
+cdl_p
+cdl_new(char *name, char *k1str, char *k2str)
+{
+ /*
+ * Just return a dummy pointer.
+ */
+ return((cdl_p)&dummy_reg);
+}
+
+/*
+ * cdl_del: Do nothing.
+ */
+void
+cdl_del(cdl_p reg)
+{
+ printk("SGI IO INFRASTRUCTURE - cdl_del not supported.\n");
+}
+
+/*
+ * cdl_add_driver: The driver part number and manufacturers number
+ * are statically initialized above.
+ *
+ Do nothing.
+ */
+int
+cdl_add_driver(cdl_p reg, int key1, int key2, char *prefix, int flags)
+{
+ return 0;
+}
+
+/*
+ * cdl_del_driver: Not supported.
+ */
+void
+cdl_del_driver(cdl_p reg,
+ char *prefix)
+{
+
+ printk("SGI IO INFRASTRUCTURE - cdl_del_driver not supported.\n");
+}
+
+/*
+ * cdl_add_connpt: We found a device and it's connect point. Call the
+ * attach routine of that driver.
+ *
+ * May need support for pciba registration here ...
+ *
+ * This routine use to create /hw/.id/pci/.../.. that links to
+ * /hw/module/006c06/Pbrick/xtalk/15/pci/<slotnum> .. do we still need
+ * it? The specified driver attach routine does not reference these
+ * vertices.
+ */
+int
+cdl_add_connpt(cdl_p reg, int part_num, int mfg_num,
+ devfs_handle_t connpt)
+{
+ int i;
+
+ /*
+ * Find the driver entry point and call the attach routine.
+ */
+ for (i = 0; i < MAX_SGI_IO_INFRA_DRVR; i++) {
+
+ if ( (part_num == sgi_infrastructure_drivers[i].part_num) &&
+ ( mfg_num == sgi_infrastructure_drivers[i].mfg_num) ) {
+ /*
+ * Call the device attach routines.
+ */
+ if (sgi_infrastructure_drivers[i].attach) {
+ return(sgi_infrastructure_drivers[i].attach(connpt));
+ }
+#ifdef BRINGUP
+ /*
+ * XXX HACK ALERT bypassing fops for now..
+ */
+ else {
+ printk("cdl_add_connpt: NEED FOPS FOR OUR DRIVERS!!\n");
+ printk("cdl_add_connpt: part_num= 0x%x mfg_num= 0x%x\n",
+ part_num, mfg_num);
+ return(-1);
+ }
+#endif /* BRINGUP */
+ } else {
+ continue;
+ }
+
+ printk("**** cdl_add_connpt: driver not found for part_num %d mfg_num %d ****\n", part_num, mfg_num);
+
+ return(-1);
+ }
+ if ( (i == MAX_SGI_IO_INFRA_DRVR) )
+ printk("**** cdl_add_connpt: Driver not found for part_num 0x%x mfg_num 0x%x ****\n", part_num, mfg_num);
+
+ return (0);
+}
+
+/*
+ * cdl_del_connpt: Not implemented.
+ */
+void
+cdl_del_connpt(cdl_p reg, int key1, int key2, devfs_handle_t connpt)
+{
+
+ printk("SGI IO INFRASTRUCTURE - cdl_del_cdl_del_connpt not supported.\n");
+}
+
+/*
+ * cdl_iterate: Not Implemented.
+ */
+void
+cdl_iterate(cdl_p reg,
+ char *prefix,
+ cdl_iter_f * func)
+{
+
+ printk("SGI IO INFRASTRUCTURE - cdl_iterate not supported.\n");
+}
+
+async_attach_t
+async_attach_new(void)
+{
+
+ printk("SGI IO INFRASTRUCTURE - async_attach_new not supported.\n");
+ return(0);
+}
+
+void
+async_attach_free(async_attach_t aa)
+{
+ printk("SGI IO INFRASTRUCTURE - async_attach_free not supported.\n");
+}
+
+async_attach_t
+async_attach_get_info(devfs_handle_t vhdl)
+{
+
+ printk("SGI IO INFRASTRUCTURE - async_attach_get_info not supported.\n");
+ return(0);
+}
+
+void
+async_attach_add_info(devfs_handle_t vhdl, async_attach_t aa)
+{
+ printk("SGI IO INFRASTRUCTURE - async_attach_add_info not supported.\n");
+
+}
+
+void
+async_attach_del_info(devfs_handle_t vhdl)
+{
+
+ printk("SGI IO INFRASTRUCTURE - async_attach_del_info not supported.\n");
+
+}
+
+void async_attach_signal_start(async_attach_t aa)
+{
+
+ printk("SGI IO INFRASTRUCTURE - async_attach_signal_start not supported.\n");
+
+}
+
+void async_attach_signal_done(async_attach_t aa)
+{
+
+ printk("SGI IO INFRASTRUCTURE - async_attach_signal_done not supported.\n");
+
+}
+
+void async_attach_waitall(async_attach_t aa)
+{
+
+ printk("SGI IO INFRASTRUCTURE - async_attach_waitall not supported.\n");
+
+}
+
diff --git a/arch/ia64/sn/io/devsupport.c b/arch/ia64/sn/io/devsupport.c
new file mode 100644
index 000000000..760e596ca
--- /dev/null
+++ b/arch/ia64/sn/io/devsupport.c
@@ -0,0 +1,1291 @@
+#define ilvt_t int
+
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/iobus.h>
+#include <asm/sn/iograph.h>
+
+/*
+ * Interfaces in this file are all platform-independent AND IObus-independent.
+ * Be aware that there may be macro equivalents to each of these hiding in
+ * header files which supercede these functions.
+ */
+
+/* =====Generic iobus support===== */
+
+/* String table to hold names of interrupts. */
+#ifdef notyet
+static struct string_table device_desc_string_table;
+#endif
+
+/* One time initialization for device descriptor support. */
+static void
+device_desc_init(void)
+{
+#ifdef notyet
+ string_table_init(&device_desc_string_table);
+#endif
+ FIXME("device_desc_init");
+}
+
+
+/* Drivers use these interfaces to manage device descriptors */
+static device_desc_t
+device_desc_alloc(void)
+{
+#ifdef notyet
+ device_desc_t device_desc;
+
+ device_desc = (device_desc_t)kmem_zalloc(sizeof(struct device_desc_s), 0);
+ device_desc->intr_target = GRAPH_VERTEX_NONE;
+
+ ASSERT(device_desc->intr_policy == 0);
+ device_desc->intr_swlevel = -1;
+ ASSERT(device_desc->intr_name == NULL);
+ ASSERT(device_desc->flags == 0);
+
+ ASSERT(!(device_desc->flags & D_IS_ASSOC));
+ return(device_desc);
+#else
+ FIXME("device_desc_alloc");
+ return((device_desc_t)0);
+#endif
+}
+
+void
+device_desc_free(device_desc_t device_desc)
+{
+#ifdef notyet
+ if (!(device_desc->flags & D_IS_ASSOC)) /* sanity */
+ kfree(device_desc);
+#endif
+ FIXME("device_desc_free");
+}
+
+device_desc_t
+device_desc_dup(devfs_handle_t dev)
+{
+#ifdef notyet
+ device_desc_t orig_device_desc, new_device_desc;
+
+
+ new_device_desc = device_desc_alloc();
+ orig_device_desc = device_desc_default_get(dev);
+ if (orig_device_desc)
+ *new_device_desc = *orig_device_desc;/* small structure copy */
+ else {
+ device_driver_t driver;
+ ilvl_t pri;
+ /*
+ * Use the driver's thread priority in
+ * case the device thread priority has not
+ * been given.
+ */
+ if (driver = device_driver_getbydev(dev)) {
+ pri = device_driver_thread_pri_get(driver);
+ device_desc_intr_swlevel_set(new_device_desc,pri);
+ }
+ }
+ new_device_desc->flags &= ~D_IS_ASSOC;
+ return(new_device_desc);
+#else
+ FIXME("device_desc_dup");
+ return((device_desc_t)0);
+#endif
+}
+
+device_desc_t
+device_desc_default_get(devfs_handle_t dev)
+{
+#ifdef notyet
+ graph_error_t rc;
+ device_desc_t device_desc;
+
+ rc = hwgraph_info_get_LBL(dev, INFO_LBL_DEVICE_DESC, (arbitrary_info_t *)&device_desc);
+
+ if (rc == GRAPH_SUCCESS)
+ return(device_desc);
+ else
+ return(NULL);
+#else
+ FIXME("device_desc_default_get");
+ return((device_desc_t)0);
+#endif
+}
+
+void
+device_desc_default_set(devfs_handle_t dev, device_desc_t new_device_desc)
+{
+#ifdef notyet
+ graph_error_t rc;
+ device_desc_t old_device_desc = NULL;
+
+ if (new_device_desc) {
+ new_device_desc->flags |= D_IS_ASSOC;
+ rc = hwgraph_info_add_LBL(dev, INFO_LBL_DEVICE_DESC,
+ (arbitrary_info_t)new_device_desc);
+ if (rc == GRAPH_DUP) {
+ rc = hwgraph_info_replace_LBL(dev, INFO_LBL_DEVICE_DESC,
+ (arbitrary_info_t)new_device_desc,
+ (arbitrary_info_t *)&old_device_desc);
+
+ ASSERT(rc == GRAPH_SUCCESS);
+ }
+ hwgraph_info_export_LBL(dev, INFO_LBL_DEVICE_DESC,
+ sizeof(struct device_desc_s));
+ } else {
+ rc = hwgraph_info_remove_LBL(dev, INFO_LBL_DEVICE_DESC,
+ (arbitrary_info_t *)&old_device_desc);
+ }
+
+ if (old_device_desc) {
+ ASSERT(old_device_desc->flags & D_IS_ASSOC);
+ old_device_desc->flags &= ~D_IS_ASSOC;
+ device_desc_free(old_device_desc);
+ }
+#endif
+ FIXME("device_desc_default_set");
+}
+
+devfs_handle_t
+device_desc_intr_target_get(device_desc_t device_desc)
+{
+#ifdef notyet
+ return(device_desc->intr_target);
+#else
+ FIXME("device_desc_intr_target_get");
+ return((devfs_handle_t)0);
+#endif
+}
+
+int
+device_desc_intr_policy_get(device_desc_t device_desc)
+{
+#ifdef notyet
+ return(device_desc->intr_policy);
+#else
+ FIXME("device_desc_intr_policy_get");
+ return(0);
+#endif
+}
+
+ilvl_t
+device_desc_intr_swlevel_get(device_desc_t device_desc)
+{
+#ifdef notyet
+ return(device_desc->intr_swlevel);
+#else
+ FIXME("device_desc_intr_swlevel_get");
+ return((ilvl_t)0);
+#endif
+}
+
+char *
+device_desc_intr_name_get(device_desc_t device_desc)
+{
+#ifdef notyet
+ return(device_desc->intr_name);
+#else
+ FIXME("device_desc_intr_name_get");
+ return(NULL);
+#endif
+}
+
+int
+device_desc_flags_get(device_desc_t device_desc)
+{
+#ifdef notyet
+ return(device_desc->flags);
+#else
+ FIXME("device_desc_flags_get");
+ return(0);
+#endif
+}
+
+void
+device_desc_intr_target_set(device_desc_t device_desc, devfs_handle_t target)
+{
+ if ( device_desc != (device_desc_t)0 )
+ device_desc->intr_target = target;
+}
+
+void
+device_desc_intr_policy_set(device_desc_t device_desc, int policy)
+{
+ if ( device_desc != (device_desc_t)0 )
+ device_desc->intr_policy = policy;
+}
+
+void
+device_desc_intr_swlevel_set(device_desc_t device_desc, ilvl_t swlevel)
+{
+ if ( device_desc != (device_desc_t)0 )
+ device_desc->intr_swlevel = swlevel;
+}
+
+void
+device_desc_intr_name_set(device_desc_t device_desc, char *name)
+{
+#ifdef notyet
+ if ( device_desc != (device_desc_t)0 )
+ device_desc->intr_name = string_table_insert(&device_desc_string_table, name);
+#else
+ FIXME("device_desc_intr_name_set");
+#endif
+}
+
+void
+device_desc_flags_set(device_desc_t device_desc, int flags)
+{
+ if ( device_desc != (device_desc_t)0 )
+ device_desc->flags = flags;
+}
+
+
+
+/*============= device admin registry routines ===================== */
+
+/* Linked list of <admin-name,admin-val> pairs */
+typedef struct dev_admin_list_s {
+ struct dev_admin_list_s *admin_next; /* next entry in the
+ * list
+ */
+ char *admin_name; /* info label */
+ char *admin_val; /* actual info */
+} dev_admin_list_t;
+
+/* Device/Driver administration registry */
+typedef struct dev_admin_registry_s {
+ mrlock_t reg_lock; /* To allow
+ * exclusive
+ * access
+ */
+ dev_admin_list_t *reg_first; /* first entry in
+ * the list
+ */
+ dev_admin_list_t **reg_last; /* pointer to the
+ * next to last entry
+ * in the last which
+ * is also the place
+ * where the new
+ * entry gets
+ * inserted
+ */
+} dev_admin_registry_t;
+
+/*
+** device_driver_s associates a device driver prefix with device switch entries.
+*/
+struct device_driver_s {
+ struct device_driver_s *dd_next; /* next element on hash chain */
+ struct device_driver_s *dd_prev; /* previous element on hash chain */
+ char *dd_prefix; /* driver prefix string */
+ struct bdevsw *dd_bdevsw; /* driver's bdevsw */
+ struct cdevsw *dd_cdevsw; /* driver's cdevsw */
+
+ /* driver administration specific data structures need to
+ * maintain the list of <driver-paramater,value> pairs
+ */
+ dev_admin_registry_t dd_dev_admin_registry;
+ ilvl_t dd_thread_pri; /* default thread priority for
+ * all this driver's
+ * threads.
+ */
+
+};
+
+#define NEW(_p) (_p = kmalloc(sizeof(*_p), GFP_KERNEL))
+#define FREE(_p) (kmem_free(_p))
+
+/*
+ * helpful lock macros
+ */
+
+#define DEV_ADMIN_REGISTRY_INITLOCK(lockp,name) mrinit(lockp,name)
+#define DEV_ADMIN_REGISTRY_RDLOCK(lockp) mraccess(lockp)
+#define DEV_ADMIN_REGISTRY_WRLOCK(lockp) mrupdate(lockp)
+#define DEV_ADMIN_REGISTRY_UNLOCK(lockp) mrunlock(lockp)
+
+/* Initialize the registry
+ */
+static void
+dev_admin_registry_init(dev_admin_registry_t *registry)
+{
+#ifdef notyet
+ if ( registry != (dev_admin_registry_t *)0 )
+ DEV_ADMIN_REGISTRY_INITLOCK(&registry->reg_lock,
+ "dev_admin_registry_lock");
+ registry->reg_first = NULL;
+ registry->reg_last = &registry->reg_first;
+ }
+#else
+ FIXME("dev_admin_registry_init");
+#endif
+}
+
+/*
+ * add an <name , value > entry to the dev admin registry.
+ * if the name already exists in the registry then change the
+ * value iff the new value differs from the old value.
+ * if the name doesn't exist a new list entry is created and put
+ * at the end.
+ */
+static void
+dev_admin_registry_add(dev_admin_registry_t *registry,
+ char *name,
+ char *val)
+{
+#ifdef notyet
+ dev_admin_list_t *reg_entry;
+ dev_admin_list_t *scan = 0;
+
+ DEV_ADMIN_REGISTRY_WRLOCK(&registry->reg_lock);
+
+ /* check if the name already exists in the registry */
+ scan = registry->reg_first;
+
+ while (scan) {
+ if (strcmp(scan->admin_name,name) == 0) {
+ /* name is there in the registry */
+ if (strcmp(scan->admin_val,val)) {
+ /* old value != new value
+ * reallocate memory and copy the new value
+ */
+ FREE(scan->admin_val);
+ scan->admin_val =
+ (char *)kern_calloc(1,strlen(val)+1);
+ strcpy(scan->admin_val,val);
+ goto out;
+ }
+ goto out; /* old value == new value */
+ }
+ scan = scan->admin_next;
+ }
+
+ /* name is not there in the registry.
+ * allocate memory for the new registry entry
+ */
+ NEW(reg_entry);
+
+ reg_entry->admin_next = 0;
+ reg_entry->admin_name = (char *)kern_calloc(1,strlen(name)+1);
+ strcpy(reg_entry->admin_name,name);
+ reg_entry->admin_val = (char *)kern_calloc(1,strlen(val)+1);
+ strcpy(reg_entry->admin_val,val);
+
+ /* add the entry at the end of the registry */
+
+ *(registry->reg_last) = reg_entry;
+ registry->reg_last = &reg_entry->admin_next;
+
+out: DEV_ADMIN_REGISTRY_UNLOCK(&registry->reg_lock);
+#endif
+ FIXME("dev_admin_registry_add");
+}
+/*
+ * check if there is an info corr. to a particular
+ * name starting from the cursor position in the
+ * registry
+ */
+static char *
+dev_admin_registry_find(dev_admin_registry_t *registry,char *name)
+{
+#ifdef notyet
+ dev_admin_list_t *scan = 0;
+
+ DEV_ADMIN_REGISTRY_RDLOCK(&registry->reg_lock);
+ scan = registry->reg_first;
+
+ while (scan) {
+ if (strcmp(scan->admin_name,name) == 0) {
+ DEV_ADMIN_REGISTRY_UNLOCK(&registry->reg_lock);
+ return scan->admin_val;
+ }
+ scan = scan->admin_next;
+ }
+ DEV_ADMIN_REGISTRY_UNLOCK(&registry->reg_lock);
+ return 0;
+#else
+ FIXME("dev_admin_registry_find");
+ return(NULL);
+#endif
+}
+/*============= MAIN DEVICE/ DRIVER ADMINISTRATION INTERFACE================ */
+/*
+ * return any labelled info associated with a device.
+ * called by any kernel code including device drivers.
+ */
+char *
+device_admin_info_get(devfs_handle_t dev_vhdl,
+ char *info_lbl)
+{
+#ifdef notyet
+ char *info = 0;
+
+ /* return value need not be GRAPH_SUCCESS as the labelled
+ * info may not be present
+ */
+ (void)hwgraph_info_get_LBL(dev_vhdl,info_lbl,
+ (arbitrary_info_t *)&info);
+
+
+ return info;
+#else
+ FIXME("device_admin_info_get");
+ return(NULL);
+#endif
+}
+
+/*
+ * set labelled info associated with a device.
+ * called by hwgraph infrastructure . may also be called
+ * by device drivers etc.
+ */
+int
+device_admin_info_set(devfs_handle_t dev_vhdl,
+ char *dev_info_lbl,
+ char *dev_info_val)
+{
+#ifdef notyet
+ graph_error_t rv;
+ arbitrary_info_t old_info;
+
+ /* Handle the labelled info
+ * intr_target
+ * sw_level
+ * in a special way. These are part of device_desc_t
+ * Right now this is the only case where we have
+ * a set of related device_admin attributes which
+ * are grouped together.
+ * In case there is a need for another set we need to
+ * take a more generic approach to solving this.
+ * Basically a registry should be implemented. This
+ * registry is initialized with the callbacks for the
+ * attributes which need to handled in a special way
+ * For example:
+ * Consider
+ * device_desc
+ * intr_target
+ * intr_swlevel
+ * register "do_intr_target" for intr_target
+ * register "do_intr_swlevel" for intr_swlevel.
+ * When the device_admin interface layer gets an <attr,val> pair
+ * it looks in the registry to see if there is a function registered to
+ * handle "attr. If not follow the default path of setting the <attr,val>
+ * as labelled information hanging off the vertex.
+ * In the above example:
+ * "do_intr_target" does what is being done below for the ADMIN_LBL_INTR_TARGET
+ * case
+ */
+ if (!strcmp(dev_info_lbl,ADMIN_LBL_INTR_TARGET) ||
+ !strcmp(dev_info_lbl,ADMIN_LBL_INTR_SWLEVEL)) {
+
+ device_desc_t device_desc;
+
+ /* Check if there is a default device descriptor
+ * information for this vertex. If not dup one .
+ */
+ if (!(device_desc = device_desc_default_get(dev_vhdl))) {
+ device_desc = device_desc_dup(dev_vhdl);
+ device_desc_default_set(dev_vhdl,device_desc);
+
+ }
+ if (!strcmp(dev_info_lbl,ADMIN_LBL_INTR_TARGET)) {
+ /* Check if a target cpu has been specified
+ * for this device by a device administration
+ * directive
+ */
+#ifdef DEBUG
+ printf(ADMIN_LBL_INTR_TARGET
+ " dev = 0x%x "
+ "dev_admin_info = %s"
+ " target = 0x%x\n",
+ dev_vhdl,
+ dev_info_lbl,
+ hwgraph_path_to_vertex(dev_info_val));
+#endif
+
+ device_desc->intr_target =
+ hwgraph_path_to_vertex(dev_info_val);
+ } else if (!strcmp(dev_info_lbl,ADMIN_LBL_INTR_SWLEVEL)) {
+ /* Check if the ithread priority level has been
+ * specified for this device by a device administration
+ * directive
+ */
+#ifdef DEBUG
+ printf(ADMIN_LBL_INTR_SWLEVEL
+ " dev = 0x%x "
+ "dev_admin_info = %s"
+ " sw level = 0x%x\n",
+ dev_vhdl,
+ dev_info_lbl,
+ atoi(dev_info_val));
+#endif
+ device_desc->intr_swlevel = atoi(dev_info_val);
+ }
+
+ }
+ if (!dev_info_val)
+ rv = hwgraph_info_remove_LBL(dev_vhdl,
+ dev_info_lbl,
+ &old_info);
+ else {
+
+ rv = hwgraph_info_add_LBL(dev_vhdl,
+ dev_info_lbl,
+ (arbitrary_info_t)dev_info_val);
+
+ if (rv == GRAPH_DUP) {
+ rv = hwgraph_info_replace_LBL(dev_vhdl,
+ dev_info_lbl,
+ (arbitrary_info_t)dev_info_val,
+ &old_info);
+ }
+ }
+ ASSERT(rv == GRAPH_SUCCESS);
+#endif
+ FIXME("device_admin_info_set");
+ return 0;
+}
+
+/*
+ * return labelled info associated with a device driver
+ * called by kernel code including device drivers
+ */
+char *
+device_driver_admin_info_get(char *driver_prefix,
+ char *driver_info_lbl)
+{
+#ifdef notyet
+ device_driver_t driver;
+
+ driver = device_driver_get(driver_prefix);
+ return (dev_admin_registry_find(&driver->dd_dev_admin_registry,
+ driver_info_lbl));
+#else
+ FIXME("device_driver_admin_info_get");
+ return(NULL);
+#endif
+}
+
+/*
+ * set labelled info associated with a device driver.
+ * called by hwgraph infrastructure . may also be called
+ * from drivers etc.
+ */
+int
+device_driver_admin_info_set(char *driver_prefix,
+ char *driver_info_lbl,
+ char *driver_info_val)
+{
+#ifdef notyet
+ device_driver_t driver;
+
+ driver = device_driver_get(driver_prefix);
+ dev_admin_registry_add(&driver->dd_dev_admin_registry,
+ driver_info_lbl,
+ driver_info_val);
+#endif
+ FIXME("device_driver_admin_info_set");
+ return 0;
+}
+/*================== device / driver admin support routines================*/
+
+/* static tables created by lboot */
+extern dev_admin_info_t dev_admin_table[];
+extern dev_admin_info_t drv_admin_table[];
+extern int dev_admin_table_size;
+extern int drv_admin_table_size;
+
+/* Extend the device admin table to allow the kernel startup code to
+ * provide some device specific administrative hints
+ */
+#define ADMIN_TABLE_CHUNK 100
+static dev_admin_info_t extended_dev_admin_table[ADMIN_TABLE_CHUNK];
+static int extended_dev_admin_table_size = 0;
+static mrlock_t extended_dev_admin_table_lock;
+
+/* Initialize the extended device admin table */
+void
+device_admin_table_init(void)
+{
+#ifdef notyet
+ extended_dev_admin_table_size = 0;
+ mrinit(&extended_dev_admin_table_lock,
+ "extended_dev_admin_table_lock");
+#endif
+ FIXME("device_admin_table_init");
+}
+/* Add <device-name , parameter-name , parameter-value> triple to
+ * the extended device administration info table. This is helpful
+ * for kernel startup code to put some hints before the hwgraph
+ * is setup
+ */
+void
+device_admin_table_update(char *name,char *label,char *value)
+{
+#ifdef notyet
+ dev_admin_info_t *p;
+
+ mrupdate(&extended_dev_admin_table_lock);
+
+ /* Safety check that we haven't exceeded array limits */
+ ASSERT(extended_dev_admin_table_size < ADMIN_TABLE_CHUNK);
+
+ if (extended_dev_admin_table_size == ADMIN_TABLE_CHUNK)
+ goto out;
+
+ /* Get the pointer to the entry in the table where we are
+ * going to put the new information
+ */
+ p = &extended_dev_admin_table[extended_dev_admin_table_size++];
+
+ /* Allocate memory for the strings and copy them in */
+ p->dai_name = (char *)kern_calloc(1,strlen(name)+1);
+ strcpy(p->dai_name,name);
+ p->dai_param_name = (char *)kern_calloc(1,strlen(label)+1);
+ strcpy(p->dai_param_name,label);
+ p->dai_param_val = (char *)kern_calloc(1,strlen(value)+1);
+ strcpy(p->dai_param_val,value);
+
+out: mrunlock(&extended_dev_admin_table_lock);
+#endif
+ FIXME("device_admin_table_update");
+}
+/* Extend the device driver admin table to allow the kernel startup code to
+ * provide some device driver specific administrative hints
+ */
+
+static dev_admin_info_t extended_drv_admin_table[ADMIN_TABLE_CHUNK];
+static int extended_drv_admin_table_size = 0;
+mrlock_t extended_drv_admin_table_lock;
+
+/* Initialize the extended device driver admin table */
+void
+device_driver_admin_table_init(void)
+{
+#ifdef notyet
+ extended_drv_admin_table_size = 0;
+ mrinit(&extended_drv_admin_table_lock,
+ "extended_drv_admin_table_lock");
+#endif
+ FIXME("device_driver_admin_table_init");
+}
+/* Add <device-driver prefix , parameter-name , parameter-value> triple to
+ * the extended device administration info table. This is helpful
+ * for kernel startup code to put some hints before the hwgraph
+ * is setup
+ */
+void
+device_driver_admin_table_update(char *name,char *label,char *value)
+{
+#ifdef notyet
+ dev_admin_info_t *p;
+
+ mrupdate(&extended_dev_admin_table_lock);
+
+ /* Safety check that we haven't exceeded array limits */
+ ASSERT(extended_drv_admin_table_size < ADMIN_TABLE_CHUNK);
+
+ if (extended_drv_admin_table_size == ADMIN_TABLE_CHUNK)
+ goto out;
+
+ /* Get the pointer to the entry in the table where we are
+ * going to put the new information
+ */
+ p = &extended_drv_admin_table[extended_drv_admin_table_size++];
+
+ /* Allocate memory for the strings and copy them in */
+ p->dai_name = (char *)kern_calloc(1,strlen(name)+1);
+ strcpy(p->dai_name,name);
+ p->dai_param_name = (char *)kern_calloc(1,strlen(label)+1);
+ strcpy(p->dai_param_name,label);
+ p->dai_param_val = (char *)kern_calloc(1,strlen(value)+1);
+ strcpy(p->dai_param_val,value);
+
+out: mrunlock(&extended_drv_admin_table_lock);
+#endif
+ FIXME("device_driver_admin_table_update");
+}
+/*
+ * keeps on adding the labelled info for each new (lbl,value) pair
+ * that it finds in the static dev admin table ( created by lboot)
+ * and the extended dev admin table ( created if at all by the kernel startup
+ * code) corresponding to a device in the hardware graph.
+ */
+void
+device_admin_info_update(devfs_handle_t dev_vhdl)
+{
+#ifdef notyet
+ int i = 0;
+ dev_admin_info_t *scan;
+ devfs_handle_t scan_vhdl;
+
+ /* Check the static device administration info table */
+ scan = dev_admin_table;
+ while (i < dev_admin_table_size) {
+
+ scan_vhdl = hwgraph_path_to_dev(scan->dai_name);
+ if (scan_vhdl == dev_vhdl) {
+ device_admin_info_set(dev_vhdl,
+ scan->dai_param_name,
+ scan->dai_param_val);
+ }
+ if (scan_vhdl != NODEV)
+ hwgraph_vertex_unref(scan_vhdl);
+ scan++;i++;
+
+ }
+ i = 0;
+ /* Check the extended device administration info table */
+ scan = extended_dev_admin_table;
+ while (i < extended_dev_admin_table_size) {
+ scan_vhdl = hwgraph_path_to_dev(scan->dai_name);
+ if (scan_vhdl == dev_vhdl) {
+ device_admin_info_set(dev_vhdl,
+ scan->dai_param_name,
+ scan->dai_param_val);
+ }
+ if (scan_vhdl != NODEV)
+ hwgraph_vertex_unref(scan_vhdl);
+ scan++;i++;
+
+ }
+
+
+#endif
+ FIXME("device_admin_info_update");
+}
+
+/* looks up the static drv admin table ( created by the lboot) and the extended
+ * drv admin table (created if at all by the kernel startup code)
+ * for this driver specific administration info and adds it to the admin info
+ * associated with this device driver's object
+ */
+void
+device_driver_admin_info_update(device_driver_t driver)
+{
+#ifdef notyet
+ int i = 0;
+ dev_admin_info_t *scan;
+
+ /* Check the static device driver administration info table */
+ scan = drv_admin_table;
+ while (i < drv_admin_table_size) {
+
+ if (strcmp(scan->dai_name,driver->dd_prefix) == 0) {
+ dev_admin_registry_add(&driver->dd_dev_admin_registry,
+ scan->dai_param_name,
+ scan->dai_param_val);
+ }
+ scan++;i++;
+ }
+ i = 0;
+ /* Check the extended device driver administration info table */
+ scan = extended_drv_admin_table;
+ while (i < extended_drv_admin_table_size) {
+
+ if (strcmp(scan->dai_name,driver->dd_prefix) == 0) {
+ dev_admin_registry_add(&driver->dd_dev_admin_registry,
+ scan->dai_param_name,
+ scan->dai_param_val);
+ }
+ scan++;i++;
+ }
+#endif
+ FIXME("device_driver_admin_info_update");
+}
+
+/* =====Device Driver Support===== */
+
+
+
+/*
+** Generic device driver support routines for use by kernel modules that
+** deal with device drivers (but NOT for use by the drivers themselves).
+** EVERY registered driver currently in the system -- static or loadable --
+** has an entry in the device_driver_hash table. A pointer to such an entry
+** serves as a generic device driver handle.
+*/
+
+#define DEVICE_DRIVER_HASH_SIZE 32
+#ifdef notyet
+lock_t device_driver_lock[DEVICE_DRIVER_HASH_SIZE];
+device_driver_t device_driver_hash[DEVICE_DRIVER_HASH_SIZE];
+static struct string_table driver_prefix_string_table;
+#endif
+
+/*
+** Initialize device driver infrastructure.
+*/
+void
+device_driver_init(void)
+{
+#ifdef notyet
+ int i;
+ extern void alenlist_init(void);
+ extern void hwgraph_init(void);
+ extern void device_desc_init(void);
+
+ ASSERT(DEVICE_DRIVER_NONE == NULL);
+ alenlist_init();
+ hwgraph_init();
+ device_desc_init();
+
+ string_table_init(&driver_prefix_string_table);
+
+ for (i=0; i<DEVICE_DRIVER_HASH_SIZE; i++) {
+ spinlock_init(&device_driver_lock[i], "devdrv");
+ device_driver_hash[i] = NULL;
+ }
+
+ /* Initialize static drivers from master.c table */
+ for (i=0; i<static_devsw_count; i++) {
+ device_driver_t driver;
+ static_device_driver_desc_t desc;
+ int pri;
+
+ desc = &static_device_driver_table[i];
+ driver = device_driver_get(desc->sdd_prefix);
+ if (!driver)
+ driver = device_driver_alloc(desc->sdd_prefix);
+ pri = device_driver_sysgen_thread_pri_get(desc->sdd_prefix);
+ device_driver_thread_pri_set(driver, pri);
+ device_driver_devsw_put(driver, desc->sdd_bdevsw, desc->sdd_cdevsw);
+ }
+#endif
+ FIXME("device_driver_init");
+}
+
+/*
+** Hash a prefix string into a hash table chain.
+*/
+static int
+driver_prefix_hash(char *prefix)
+{
+#ifdef notyet
+ int accum = 0;
+ char nextchar;
+
+ while (nextchar = *prefix++)
+ accum = accum ^ nextchar;
+
+ return(accum % DEVICE_DRIVER_HASH_SIZE);
+#else
+ FIXME("driver_prefix_hash");
+ return(0);
+#endif
+}
+
+
+/*
+** Allocate a driver handle.
+** Returns the driver handle, or NULL if the driver prefix
+** already has a handle.
+**
+** Upper layers prevent races among device_driver_alloc,
+** device_driver_free, and device_driver_get*.
+*/
+device_driver_t
+device_driver_alloc(char *prefix)
+{
+#ifdef notyet
+ int which_hash;
+ device_driver_t new_driver;
+ int s;
+
+ which_hash = driver_prefix_hash(prefix);
+
+ new_driver = kern_calloc(1, sizeof(*new_driver));
+ ASSERT(new_driver != NULL);
+ new_driver->dd_prev = NULL;
+ new_driver->dd_prefix = string_table_insert(&driver_prefix_string_table, prefix);
+ new_driver->dd_bdevsw = NULL;
+ new_driver->dd_cdevsw = NULL;
+
+ dev_admin_registry_init(&new_driver->dd_dev_admin_registry);
+ device_driver_admin_info_update(new_driver);
+
+ s = mutex_spinlock(&device_driver_lock[which_hash]);
+
+#if DEBUG
+ {
+ device_driver_t drvscan;
+
+ /* Make sure we haven't already added a driver with this prefix */
+ drvscan = device_driver_hash[which_hash];
+ while (drvscan &&
+ strcmp(drvscan->dd_prefix, prefix)) {
+ drvscan = drvscan->dd_next;
+ }
+
+ ASSERT(!drvscan);
+ }
+#endif /* DEBUG */
+
+
+ /* Add new_driver to front of hash chain. */
+ new_driver->dd_next = device_driver_hash[which_hash];
+ if (new_driver->dd_next)
+ new_driver->dd_next->dd_prev = new_driver;
+ device_driver_hash[which_hash] = new_driver;
+
+ mutex_spinunlock(&device_driver_lock[which_hash], s);
+
+ return(new_driver);
+#else
+ FIXME("device_driver_alloc");
+ return((device_driver_t)0);
+#endif
+}
+
+/*
+** Free a driver handle.
+**
+** Statically loaded drivers should never device_driver_free.
+** Dynamically loaded drivers device_driver_free when either an
+** unloaded driver is unregistered, or when an unregistered driver
+** is unloaded.
+*/
+void
+device_driver_free(device_driver_t driver)
+{
+#ifdef notyet
+ int which_hash;
+ int s;
+
+ if (!driver)
+ return;
+
+ which_hash = driver_prefix_hash(driver->dd_prefix);
+
+ s = mutex_spinlock(&device_driver_lock[which_hash]);
+
+#if DEBUG
+ {
+ device_driver_t drvscan;
+
+ /* Make sure we're dealing with the right list */
+ drvscan = device_driver_hash[which_hash];
+ while (drvscan && (drvscan != driver))
+ drvscan = drvscan->dd_next;
+
+ ASSERT(drvscan);
+ }
+#endif /* DEBUG */
+
+ if (driver->dd_next)
+ driver->dd_next->dd_prev = driver->dd_prev;
+
+ if (driver->dd_prev)
+ driver->dd_prev->dd_next = driver->dd_next;
+ else
+ device_driver_hash[which_hash] = driver->dd_next;
+
+ mutex_spinunlock(&device_driver_lock[which_hash], s);
+
+ driver->dd_next = NULL; /* sanity */
+ driver->dd_prev = NULL; /* sanity */
+ driver->dd_prefix = NULL; /* sanity */
+
+ if (driver->dd_bdevsw) {
+ driver->dd_bdevsw->d_driver = NULL;
+ driver->dd_bdevsw = NULL;
+ }
+
+ if (driver->dd_cdevsw) {
+ if (driver->dd_cdevsw->d_str) {
+ str_free_mux_node(driver);
+ }
+ driver->dd_cdevsw->d_driver = NULL;
+ driver->dd_cdevsw = NULL;
+ }
+
+ kern_free(driver);
+#endif
+ FIXME("device_driver_free");
+}
+
+
+/*
+** Given a device driver prefix, return a handle to the caller.
+*/
+device_driver_t
+device_driver_get(char *prefix)
+{
+#ifdef notyet
+ int which_hash;
+ device_driver_t drvscan;
+ int s;
+
+ if (prefix == NULL)
+ return(NULL);
+
+ which_hash = driver_prefix_hash(prefix);
+
+ s = mutex_spinlock(&device_driver_lock[which_hash]);
+
+ drvscan = device_driver_hash[which_hash];
+ while (drvscan && strcmp(drvscan->dd_prefix, prefix))
+ drvscan = drvscan->dd_next;
+
+ mutex_spinunlock(&device_driver_lock[which_hash], s);
+
+ return(drvscan);
+#else
+ FIXME("device_driver_get");
+ return((device_driver_t)0);
+#endif
+}
+
+
+/*
+** Given a block or char special file devfs_handle_t, find the
+** device driver that controls it.
+*/
+device_driver_t
+device_driver_getbydev(devfs_handle_t device)
+{
+#ifdef notyet
+ struct bdevsw *my_bdevsw;
+ struct cdevsw *my_cdevsw;
+
+ my_cdevsw = get_cdevsw(device);
+ if (my_cdevsw != NULL)
+ return(my_cdevsw->d_driver);
+
+ my_bdevsw = get_bdevsw(device);
+ if (my_bdevsw != NULL)
+ return(my_bdevsw->d_driver);
+
+#endif
+ FIXME("device_driver_getbydev");
+ return((device_driver_t)0);
+}
+
+
+/*
+** Associate a driver with bdevsw/cdevsw pointers.
+**
+** Statically loaded drivers are permanently and automatically associated
+** with the proper bdevsw/cdevsw. Dynamically loaded drivers associate
+** themselves when the driver is registered, and disassociate when the
+** driver unregisters.
+**
+** Returns 0 on success, -1 on failure (devsw already associated with driver)
+*/
+int
+device_driver_devsw_put(device_driver_t driver,
+ struct bdevsw *my_bdevsw,
+ struct cdevsw *my_cdevsw)
+{
+#ifdef notyet
+ int i;
+
+ if (!driver)
+ return(-1);
+
+ /* Trying to re-register data? */
+ if (((my_bdevsw != NULL) && (driver->dd_bdevsw != NULL)) ||
+ ((my_cdevsw != NULL) && (driver->dd_cdevsw != NULL)))
+ return(-1);
+
+ if (my_bdevsw != NULL) {
+ driver->dd_bdevsw = my_bdevsw;
+ my_bdevsw->d_driver = driver;
+ for (i = 0; i < bdevmax; i++) {
+ if (driver->dd_bdevsw->d_flags == bdevsw[i].d_flags) {
+ bdevsw[i].d_driver = driver;
+ break;
+ }
+ }
+ }
+
+ if (my_cdevsw != NULL) {
+ driver->dd_cdevsw = my_cdevsw;
+ my_cdevsw->d_driver = driver;
+ for (i = 0; i < cdevmax; i++) {
+ if (driver->dd_cdevsw->d_flags == cdevsw[i].d_flags) {
+ cdevsw[i].d_driver = driver;
+ break;
+ }
+ }
+ }
+#endif
+ FIXME("device_driver_devsw_put");
+ return(0);
+}
+
+
+/*
+** Given a driver, return the corresponding bdevsw and cdevsw pointers.
+*/
+void
+device_driver_devsw_get( device_driver_t driver,
+ struct bdevsw **bdevswp,
+ struct cdevsw **cdevswp)
+{
+ if (!driver) {
+ *bdevswp = NULL;
+ *cdevswp = NULL;
+ } else {
+ *bdevswp = driver->dd_bdevsw;
+ *cdevswp = driver->dd_cdevsw;
+ }
+}
+
+/*
+ * device_driver_thread_pri_set
+ * Given a driver try to set its thread priority.
+ * Returns 0 on success , -1 on failure.
+ */
+int
+device_driver_thread_pri_set(device_driver_t driver,ilvl_t pri)
+{
+ if (!driver)
+ return(-1);
+ driver->dd_thread_pri = pri;
+ return(0);
+}
+/*
+ * device_driver_thread_pri_get
+ * Given a driver return the driver thread priority.
+ * If the driver is NULL return invalid driver thread
+ * priority.
+ */
+ilvl_t
+device_driver_thread_pri_get(device_driver_t driver)
+{
+ if (driver)
+ return(driver->dd_thread_pri);
+ else
+ return(DRIVER_THREAD_PRI_INVALID);
+}
+/*
+** Given a device driver, return it's handle (prefix).
+*/
+void
+device_driver_name_get(device_driver_t driver, char *buffer, int length)
+{
+ if (driver == NULL)
+ return;
+
+ strncpy(buffer, driver->dd_prefix, length);
+}
+
+
+/*
+** Associate a pointer-sized piece of information with a device.
+*/
+void
+device_info_set(devfs_handle_t device, void *info)
+{
+#ifdef notyet
+ hwgraph_fastinfo_set(device, (arbitrary_info_t)info);
+#endif
+ FIXME("device_info_set");
+}
+
+
+/*
+** Retrieve a pointer-sized piece of information associated with a device.
+*/
+void *
+device_info_get(devfs_handle_t device)
+{
+#ifdef notyet
+ return((void *)hwgraph_fastinfo_get(device));
+#else
+ FIXME("device_info_get");
+ return(NULL);
+#endif
+}
+
+/*
+ * Find the thread priority for a device, from the various
+ * sysgen files.
+ */
+int
+device_driver_sysgen_thread_pri_get(char *dev_prefix)
+{
+#ifdef notyet
+ int pri;
+ char *pri_s;
+ char *class;
+
+ extern default_intr_pri;
+ extern disk_intr_pri;
+ extern serial_intr_pri;
+ extern parallel_intr_pri;
+ extern tape_intr_pri;
+ extern graphics_intr_pri;
+ extern network_intr_pri;
+ extern scsi_intr_pri;
+ extern audio_intr_pri;
+ extern video_intr_pri;
+ extern external_intr_pri;
+ extern tserialio_intr_pri;
+
+ /* Check if there is a thread priority specified for
+ * this driver's thread thru admin hints. If so
+ * use that value. Otherwise set it to its default
+ * class value, otherwise set it to the default
+ * value.
+ */
+
+ if (pri_s = device_driver_admin_info_get(dev_prefix,
+ ADMIN_LBL_THREAD_PRI)) {
+ pri = atoi(pri_s);
+ } else if (class = device_driver_admin_info_get(dev_prefix,
+ ADMIN_LBL_THREAD_CLASS)) {
+ if (strcmp(class, "disk") == 0)
+ pri = disk_intr_pri;
+ else if (strcmp(class, "serial") == 0)
+ pri = serial_intr_pri;
+ else if (strcmp(class, "parallel") == 0)
+ pri = parallel_intr_pri;
+ else if (strcmp(class, "tape") == 0)
+ pri = tape_intr_pri;
+ else if (strcmp(class, "graphics") == 0)
+ pri = graphics_intr_pri;
+ else if (strcmp(class, "network") == 0)
+ pri = network_intr_pri;
+ else if (strcmp(class, "scsi") == 0)
+ pri = scsi_intr_pri;
+ else if (strcmp(class, "audio") == 0)
+ pri = audio_intr_pri;
+ else if (strcmp(class, "video") == 0)
+ pri = video_intr_pri;
+ else if (strcmp(class, "external") == 0)
+ pri = external_intr_pri;
+ else if (strcmp(class, "tserialio") == 0)
+ pri = tserialio_intr_pri;
+ else
+ pri = default_intr_pri;
+ } else
+ pri = default_intr_pri;
+
+ if (pri > 255)
+ pri = 255;
+ else if (pri < 0)
+ pri = 0;
+ return pri;
+#else
+ FIXME("device_driver_sysgen_thread_pri_get");
+ return(-1);
+#endif
+}
diff --git a/arch/ia64/sn/io/eeprom.c b/arch/ia64/sn/io/eeprom.c
new file mode 100644
index 000000000..6a22aac93
--- /dev/null
+++ b/arch/ia64/sn/io/eeprom.c
@@ -0,0 +1,1457 @@
+/*
+ *
+ * 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)
+ */
+
+
+/*
+ * WARNING: There is more than one copy of this file in different isms.
+ * All copies must be kept exactly in sync.
+ * Do not modify this file without also updating the following:
+ *
+ * irix/kern/io/eeprom.c
+ * stand/arcs/lib/libsk/ml/eeprom.c
+ * stand/arcs/lib/libkl/io/eeprom.c
+ *
+ * (from time to time they might not be in sync but that's due to bringup
+ * activity - this comment is to remind us that they eventually have to
+ * get back together)
+ *
+ * eeprom.c
+ *
+ * access to board-mounted EEPROMs via the L1 system controllers
+ *
+ */
+
+/**************************************************************************
+ * *
+ * Copyright (C) 1999 Silicon Graphics, Inc. *
+ * *
+ * These coded instructions, statements, and computer programs contain *
+ * unpublished proprietary information of Silicon Graphics, Inc., and *
+ * are protected by Federal copyright law. They may not be disclosed *
+ * to third parties or copied or duplicated in any form, in whole or *
+ * in part, without the prior written consent of Silicon Graphics, Inc. *
+ * *
+ **************************************************************************
+ */
+
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/hcl_util.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/eeprom.h>
+#include <asm/sn/ksys/i2c.h>
+#include <asm/sn/cmn_err.h>
+/* #include <sys/SN/SN1/ip27log.h> */
+#include <asm/sn/router.h>
+#include <asm/sn/module.h>
+#include <asm/sn/ksys/l1.h>
+#include <asm/sn/nodepda.h>
+#include <asm/sn/clksupport.h>
+
+#if defined(EEPROM_DEBUG)
+#define db_printf(x) printk x
+#else
+#define db_printf(x) printk x
+#endif
+
+#define BCOPY(x,y,z) memcpy(y,x,z)
+
+#define UNDERSCORE 0 /* don't convert underscores to hyphens */
+#define HYPHEN 1 /* convert underscores to hyphens */
+
+void copy_ascii_field( char *to, char *from, int length,
+ int change_underscore );
+uint64_t generate_unique_id( char *sn, int sn_len );
+uchar_t char_to_base36( char c );
+int nicify( char *dst, eeprom_brd_record_t *src );
+static void int64_to_hex_string( char *out, uint64_t val );
+
+// extern int router_lock( net_vec_t, int, int );
+// extern int router_unlock( net_vec_t );
+#define ROUTER_LOCK(p) // router_lock(p, 10000, 3000000)
+#define ROUTER_UNLOCK(p) // router_unlock(p)
+
+#define IP27LOG_OVNIC "OverrideNIC"
+
+
+/* the following function converts an EEPROM record to a close facsimile
+ * of the string returned by reading a Dallas Semiconductor NIC (see
+ * one of the many incarnations of nic.c for details on that driver)
+ */
+int nicify( char *dst, eeprom_brd_record_t *src )
+{
+ int field_len;
+ uint64_t unique_id;
+ char *cur_dst = dst;
+ eeprom_board_ia_t *board;
+
+ board = src->board_ia;
+ ASSERT( board ); /* there should always be a board info area */
+
+ /* copy part number */
+ strcpy( cur_dst, "Part:" );
+ cur_dst += strlen( cur_dst );
+ ASSERT( (board->part_num_tl & FIELD_FORMAT_MASK)
+ == FIELD_FORMAT_ASCII );
+ field_len = board->part_num_tl & FIELD_LENGTH_MASK;
+ copy_ascii_field( cur_dst, board->part_num, field_len, HYPHEN );
+ cur_dst += field_len;
+
+ /* copy product name */
+ strcpy( cur_dst, ";Name:" );
+ cur_dst += strlen( cur_dst );
+ ASSERT( (board->product_tl & FIELD_FORMAT_MASK) == FIELD_FORMAT_ASCII );
+ field_len = board->product_tl & FIELD_LENGTH_MASK;
+ copy_ascii_field( cur_dst, board->product, field_len, UNDERSCORE );
+ cur_dst += field_len;
+
+ /* copy serial number */
+ strcpy( cur_dst, ";Serial:" );
+ cur_dst += strlen( cur_dst );
+ ASSERT( (board->serial_num_tl & FIELD_FORMAT_MASK)
+ == FIELD_FORMAT_ASCII );
+ field_len = board->serial_num_tl & FIELD_LENGTH_MASK;
+ copy_ascii_field( cur_dst, board->serial_num, field_len,
+ HYPHEN);
+
+ cur_dst += field_len;
+
+ /* copy revision */
+ strcpy( cur_dst, ";Revision:");
+ cur_dst += strlen( cur_dst );
+ ASSERT( (board->board_rev_tl & FIELD_FORMAT_MASK)
+ == FIELD_FORMAT_ASCII );
+ field_len = board->board_rev_tl & FIELD_LENGTH_MASK;
+ copy_ascii_field( cur_dst, board->board_rev, field_len, HYPHEN );
+ cur_dst += field_len;
+
+ /* EEPROMs don't have equivalents for the Group, Capability and
+ * Variety fields, so we pad these with 0's
+ */
+ strcpy( cur_dst, ";Group:ff;Capability:ffffffff;Variety:ff" );
+ cur_dst += strlen( cur_dst );
+
+ /* use the board serial number to "fake" a laser id */
+ strcpy( cur_dst, ";Laser:" );
+ cur_dst += strlen( cur_dst );
+ unique_id = generate_unique_id( board->serial_num,
+ board->serial_num_tl & FIELD_LENGTH_MASK );
+ int64_to_hex_string( cur_dst, unique_id );
+ strcat( dst, ";" );
+
+ return 1;
+}
+
+
+/* These functions borrow heavily from chars2* in nic.c
+ */
+void copy_ascii_field( char *to, char *from, int length,
+ int change_underscore )
+{
+ int i;
+ for( i = 0; i < length; i++ ) {
+
+ /* change underscores to hyphens if requested */
+ if( from[i] == '_' && change_underscore == HYPHEN )
+ to[i] = '-';
+
+ /* ; and ; are separators, so mustn't appear within
+ * a field */
+ else if( from[i] == ':' || from[i] == ';' )
+ to[i] = '?';
+
+ /* I'm not sure why or if ASCII character 0xff would
+ * show up in an EEPROM field, but the NIC parsing
+ * routines wouldn't like it if it did... so we
+ * get rid of it, just in case. */
+ else if( (unsigned char)from[i] == (unsigned char)0xff )
+ to[i] = ' ';
+
+ /* unprintable characters are replaced with . */
+ else if( from[i] < ' ' || from[i] >= 0x7f )
+ to[i] = '.';
+
+ /* otherwise, just copy the character */
+ else
+ to[i] = from[i];
+ }
+
+ if( i == 0 ) {
+ to[i] = ' '; /* return at least a space... */
+ i++;
+ }
+ to[i] = 0; /* terminating null */
+}
+
+/* Note that int64_to_hex_string currently only has a big-endian
+ * implementation.
+ */
+#ifdef _MIPSEB
+static void int64_to_hex_string( char *out, uint64_t val )
+{
+ int i;
+ uchar_t table[] = "0123456789abcdef";
+ uchar_t *byte_ptr = (uchar_t *)&val;
+ for( i = 0; i < sizeof(uint64_t); i++ ) {
+ out[i*2] = table[ ((*byte_ptr) >> 4) & 0x0f ];
+ out[i*2+1] = table[ (*byte_ptr) & 0x0f ];
+ byte_ptr++;
+ }
+ out[i*2] = '\0';
+}
+
+#else /* little endian */
+
+static void int64_to_hex_string( char *out, uint64_t val )
+{
+
+
+ printk("int64_to_hex_string needs a little-endian implementation.\n");
+}
+#endif /* _MIPSEB */
+
+/* Convert a standard ASCII serial number to a unique integer
+ * id number by treating the serial number string as though
+ * it were a base 36 number
+ */
+uint64_t generate_unique_id( char *sn, int sn_len )
+{
+ int uid = 0;
+ int i;
+
+ #define VALID_BASE36(c) ((c >= '0' && c <='9') \
+ || (c >= 'A' && c <='Z') \
+ || (c >= 'a' && c <='z'))
+
+ for( i = 0; i < sn_len; i++ ) {
+ if( !VALID_BASE36(sn[i]) )
+ continue;
+ uid *= 36;
+ uid += char_to_base36( sn[i] );
+ }
+
+ if( uid == 0 )
+ return rtc_time();
+
+ return uid;
+}
+
+uchar_t char_to_base36( char c )
+{
+ uchar_t val;
+
+ if( c >= '0' && c <= '9' )
+ val = (c - '0');
+
+ else if( c >= 'A' && c <= 'Z' )
+ val = (c - 'A' + 10);
+
+ else if( c >= 'a' && c <= 'z' )
+ val = (c - 'a' + 10);
+
+ else val = 0;
+
+ return val;
+}
+
+
+/* given a pointer to the three-byte little-endian EEPROM representation
+ * of date-of-manufacture, this function translates to a big-endian
+ * integer format
+ */
+int eeprom_xlate_board_mfr_date( uchar_t *src )
+{
+ int rval = 0;
+ rval += *src; src++;
+ rval += ((int)(*src) << 8); src ++;
+ rval += ((int)(*src) << 16);
+ return rval;
+}
+
+
+int eeprom_str( char *nic_str, nasid_t nasid, int component )
+{
+ eeprom_brd_record_t eep;
+ eeprom_board_ia_t board;
+ eeprom_chassis_ia_t chassis;
+ int r;
+
+ if( (component & C_DIMM) == C_DIMM ) {
+ /* this function isn't applicable to DIMMs */
+ return EEP_PARAM;
+ }
+ else {
+ eep.board_ia = &board;
+ eep.spd = NULL;
+ if( !(component & SUBORD_MASK) )
+ eep.chassis_ia = &chassis; /* only main boards have a chassis
+ * info area */
+ else
+ eep.chassis_ia = NULL;
+ }
+
+ switch( component & BRICK_MASK ) {
+ case C_BRICK:
+ r = cbrick_eeprom_read( &eep, nasid, component );
+ break;
+ case IO_BRICK:
+ r = iobrick_eeprom_read( &eep, nasid, component );
+ break;
+ default:
+ return EEP_PARAM; /* must be an invalid component */
+ }
+ if( r )
+ return r;
+ if( !nicify( nic_str, &eep ) )
+ return EEP_NICIFY;
+
+ return EEP_OK;
+}
+
+int vector_eeprom_str( char *nic_str, nasid_t nasid,
+ int component, net_vec_t path )
+{
+ eeprom_brd_record_t eep;
+ eeprom_board_ia_t board;
+ eeprom_chassis_ia_t chassis;
+ int r;
+
+ eep.board_ia = &board;
+ if( !(component & SUBORD_MASK) )
+ eep.chassis_ia = &chassis; /* only main boards have a chassis
+ * info area */
+ else
+ eep.chassis_ia = NULL;
+
+ if( !(component & VECTOR) )
+ return EEP_PARAM;
+
+ if( (r = vector_eeprom_read( &eep, nasid, path, component )) )
+ return r;
+
+ if( !nicify( nic_str, &eep ) )
+ return EEP_NICIFY;
+
+ return EEP_OK;
+}
+
+
+int is_iobrick( int nasid, int widget_num )
+{
+ uint32_t wid_reg;
+ int part_num, mfg_num;
+
+ /* Read the widget's WIDGET_ID register to get
+ * its part number and mfg number
+ */
+ wid_reg = *(volatile int32_t *)
+ (NODE_SWIN_BASE( nasid, widget_num ) + WIDGET_ID);
+
+ part_num = (wid_reg & WIDGET_PART_NUM) >> WIDGET_PART_NUM_SHFT;
+ mfg_num = (wid_reg & WIDGET_MFG_NUM) >> WIDGET_MFG_NUM_SHFT;
+
+ /* Is this the "xbow part" of an XBridge? If so, this
+ * widget is definitely part of an I/O brick.
+ */
+ if( part_num == XXBOW_WIDGET_PART_NUM &&
+ mfg_num == XXBOW_WIDGET_MFGR_NUM )
+
+ return 1;
+
+ /* Is this a "bridge part" of an XBridge? If so, once
+ * again, we know this widget is part of an I/O brick.
+ */
+ if( part_num == XBRIDGE_WIDGET_PART_NUM &&
+ mfg_num == XBRIDGE_WIDGET_MFGR_NUM )
+
+ return 1;
+
+ return 0;
+}
+
+
+int cbrick_uid_get( nasid_t nasid, uint64_t *uid )
+{
+#if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
+ return EEP_L1;
+#else
+ char uid_str[32];
+ char msg[BRL1_QSIZE];
+ int subch, len;
+ l1sc_t sc;
+ l1sc_t *scp;
+ int local = (nasid == get_nasid());
+
+ if ( IS_RUNNING_ON_SIMULATOR() )
+ return EEP_L1;
+
+ /* If the promlog variable pointed to by IP27LOG_OVNIC is set,
+ * use that value for the cbrick UID rather than the EEPROM
+ * serial number.
+ */
+#ifdef LOG_GETENV
+ if( ip27log_getenv( nasid, IP27LOG_OVNIC, uid_str, NULL, 0 ) >= 0 )
+ {
+ /* We successfully read IP27LOG_OVNIC, so return it as the UID. */
+ db_printf(( "cbrick_uid_get:"
+ "Overriding UID with environment variable %s\n",
+ IP27LOG_OVNIC ));
+ *uid = strtoull( uid_str, NULL, 0 );
+ return EEP_OK;
+ }
+#endif
+
+ /* If this brick is retrieving its own uid, use the local l1sc_t to
+ * arbitrate access to the l1; otherwise, set up a new one.
+ */
+ if( local ) {
+ scp = get_l1sc();
+ }
+ else {
+ scp = &sc;
+ sc_init( &sc, nasid, BRL1_LOCALUART );
+ }
+
+ /* fill in msg with the opcode & params */
+ BZERO( msg, BRL1_QSIZE );
+ if( (subch = sc_open( scp, L1_ADDR_LOCAL )) < 0 )
+ return EEP_L1;
+
+ if( (len = sc_construct_msg( scp, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_SER_NUM, 0 )) < 0 )
+ {
+ sc_close( scp, subch );
+ return( EEP_L1 );
+ }
+
+ /* send the request to the L1 */
+ if( sc_command( scp, subch, msg, msg, &len ) ) {
+ sc_close( scp, subch );
+ return( EEP_L1 );
+ }
+
+ /* free up subchannel */
+ sc_close(scp, subch);
+
+ /* check response */
+ if( sc_interpret_resp( msg, 2, L1_ARG_ASCII, uid_str ) < 0 )
+ {
+ return( EEP_L1 );
+ }
+
+ *uid = generate_unique_id( uid_str, strlen( uid_str ) );
+
+ return EEP_OK;
+#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
+}
+
+
+int rbrick_uid_get( nasid_t nasid, net_vec_t path, uint64_t *uid )
+{
+#if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
+ return EEP_L1;
+#else
+ char uid_str[32];
+ char msg[BRL1_QSIZE];
+ int subch, len;
+ l1sc_t sc;
+
+ if ( IS_RUNNING_ON_SIMULATOR() )
+ return EEP_L1;
+
+#ifdef BRINGUP
+#define FAIL \
+ { \
+ *uid = rtc_time(); \
+ printk( "rbrick_uid_get failed; using current time as uid\n" ); \
+ return EEP_OK; \
+ }
+#endif /* BRINGUP */
+
+ ROUTER_LOCK(path);
+ sc_init( &sc, nasid, path );
+
+ /* fill in msg with the opcode & params */
+ BZERO( msg, BRL1_QSIZE );
+ if( (subch = sc_open( &sc, L1_ADDR_LOCAL )) < 0 ) {
+ ROUTER_UNLOCK(path);
+ FAIL;
+ }
+
+ if( (len = sc_construct_msg( &sc, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_SER_NUM, 0 )) < 0 )
+ {
+ ROUTER_UNLOCK(path);
+ sc_close( &sc, subch );
+ FAIL;
+ }
+
+ /* send the request to the L1 */
+ if( sc_command( &sc, subch, msg, msg, &len ) ) {
+ ROUTER_UNLOCK(path);
+ sc_close( &sc, subch );
+ FAIL;
+ }
+
+ /* free up subchannel */
+ ROUTER_UNLOCK(path);
+ sc_close(&sc, subch);
+
+ /* check response */
+ if( sc_interpret_resp( msg, 2, L1_ARG_ASCII, uid_str ) < 0 )
+ {
+ FAIL;
+ }
+
+ *uid = generate_unique_id( uid_str, strlen( uid_str ) );
+
+ return EEP_OK;
+#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
+}
+
+int iobrick_uid_get( nasid_t nasid, uint64_t *uid )
+{
+ eeprom_brd_record_t eep;
+ eeprom_board_ia_t board;
+ eeprom_chassis_ia_t chassis;
+ int r;
+
+ eep.board_ia = &board;
+ eep.chassis_ia = &chassis;
+ eep.spd = NULL;
+
+ r = iobrick_eeprom_read( &eep, nasid, IO_BRICK );
+ if( r != EEP_OK ) {
+ *uid = rtc_time();
+ return r;
+ }
+
+ *uid = generate_unique_id( board.serial_num,
+ board.serial_num_tl & FIELD_LENGTH_MASK );
+
+ return EEP_OK;
+}
+
+
+int ibrick_mac_addr_get( nasid_t nasid, char *eaddr )
+{
+ eeprom_brd_record_t eep;
+ eeprom_board_ia_t board;
+ eeprom_chassis_ia_t chassis;
+ int r;
+ char *tmp;
+
+ eep.board_ia = &board;
+ eep.chassis_ia = &chassis;
+ eep.spd = NULL;
+
+ r = iobrick_eeprom_read( &eep, nasid, IO_BRICK );
+ if( (r != EEP_OK) || (board.mac_addr[0] == '\0') ) {
+ db_printf(( "ibrick_mac_addr_get: "
+ "Couldn't read MAC address from EEPROM\n" ));
+ return EEP_L1;
+ }
+ else {
+ /* successfully read info area */
+ int ix;
+ tmp = board.mac_addr;
+ for( ix = 0; ix < (board.mac_addr_tl & FIELD_LENGTH_MASK); ix++ )
+ {
+ *eaddr++ = *tmp++;
+ }
+ *eaddr = '\0';
+ }
+
+ return EEP_OK;
+}
+
+
+/*
+ * eeprom_vertex_info_set
+ *
+ * Given a vertex handle, a component designation, a starting nasid
+ * and (in the case of a router) a vector path to the component, this
+ * function will read the EEPROM and attach the resulting information
+ * to the vertex in the same string format as that provided by the
+ * Dallas Semiconductor NIC drivers. If the vertex already has the
+ * string, this function just returns the string.
+ */
+
+extern char *nic_vertex_info_get( devfs_handle_t );
+extern void nic_vmc_check( devfs_handle_t, char * );
+#ifdef BRINGUP
+/* the following were lifted from nic.c - change later? */
+#define MAX_INFO 2048
+#define NEWSZ(ptr,sz) ((ptr) = kern_malloc((sz)))
+#define DEL(ptr) (kern_free((ptr)))
+#endif /* BRINGUP */
+
+char *eeprom_vertex_info_set( int component, int nasid, devfs_handle_t v,
+ net_vec_t path )
+{
+ char *info_tmp;
+ int info_len;
+ char *info;
+
+ /* see if this vertex is already marked */
+ info_tmp = nic_vertex_info_get(v);
+ if (info_tmp) return info_tmp;
+
+ /* get a temporary place for the data */
+ NEWSZ(info_tmp, MAX_INFO);
+ if (!info_tmp) return NULL;
+
+ /* read the EEPROM */
+ if( component & R_BRICK ) {
+ if( RBRICK_EEPROM_STR( info_tmp, nasid, path ) != EEP_OK )
+ return NULL;
+ }
+ else {
+ if( eeprom_str( info_tmp, nasid, component ) != EEP_OK )
+ return NULL;
+ }
+
+ /* allocate a smaller final place */
+ info_len = strlen(info_tmp)+1;
+ NEWSZ(info, info_len);
+ if (info) {
+ strcpy(info, info_tmp);
+ DEL(info_tmp);
+ } else {
+ info = info_tmp;
+ }
+
+ /* add info to the vertex */
+ hwgraph_info_add_LBL(v, INFO_LBL_NIC,
+ (arbitrary_info_t) info);
+
+ /* see if someone else got there first */
+ info_tmp = nic_vertex_info_get(v);
+ if (info != info_tmp) {
+ DEL(info);
+ return info_tmp;
+ }
+
+ /* export the data */
+ hwgraph_info_export_LBL(v, INFO_LBL_NIC, info_len);
+
+ /* trigger all matching callbacks */
+ nic_vmc_check(v, info);
+
+ return info;
+}
+
+
+/*********************************************************************
+ *
+ * stubs for use until the Bedrock/L1 link is available
+ *
+ */
+
+#include <asm/sn/nic.h>
+
+/* #define EEPROM_TEST */
+
+/* fake eeprom reading functions (replace when the BR/L1 communication
+ * channel is in working order)
+ */
+
+
+/* generate a charater in [0-9A-Z]; if an "extra" character is
+ * specified (such as '_'), include it as one of the possibilities.
+ */
+char random_eeprom_ch( char extra )
+{
+ char ch;
+ int modval = 36;
+ if( extra )
+ modval++;
+
+ ch = rtc_time() % modval;
+
+ if( ch < 10 )
+ ch += '0';
+ else if( ch >= 10 && ch < 36 )
+ ch += ('A' - 10);
+ else
+ ch = extra;
+
+ return ch;
+}
+
+/* create a part number of the form xxx-xxxx-xxx.
+ * It may be important later to generate different
+ * part numbers depending on the component we're
+ * supposed to be "reading" from, so the component
+ * paramter is provided.
+ */
+void fake_a_part_number( char *buf, int component )
+{
+ int i;
+ switch( component ) {
+
+ /* insert component-specific routines here */
+
+ case C_BRICK:
+ strcpy( buf, "030-1266-001" );
+ break;
+ default:
+ for( i = 0; i < 12; i++ ) {
+ if( i == 3 || i == 8 )
+ buf[i] = '-';
+ else
+ buf[i] = random_eeprom_ch(0);
+ }
+ }
+}
+
+
+/* create a six-character serial number */
+void fake_a_serial_number( char *buf, uint64_t ser )
+{
+ int i;
+ static const char hexchars[] = "0123456789ABCDEF";
+
+ if (ser) {
+ for( i = 5; i >=0; i-- ) {
+ buf[i] = hexchars[ser & 0xf];
+ ser >>= 4;
+ }
+ }
+ else {
+ for( i = 0; i < 6; i++ )
+ buf[i] = random_eeprom_ch(0);
+ }
+}
+
+
+void fake_a_product_name( uchar_t *format, char* buf, int component )
+{
+ switch( component & BRICK_MASK ) {
+
+ case C_BRICK:
+ if( component & SUBORD_MASK ) {
+ strcpy( buf, "C_BRICK_SUB" );
+ *format = 0xCB;
+ }
+ else {
+ strcpy( buf, "IP35" );
+ *format = 0xC4;
+ }
+ break;
+
+ case R_BRICK:
+ if( component & SUBORD_MASK ) {
+ strcpy( buf, "R_BRICK_SUB" );
+ *format = 0xCB;
+ }
+ else {
+ strcpy( buf, "R_BRICK" );
+ *format = 0xC7;
+ }
+ break;
+
+ case IO_BRICK:
+ if( component & SUBORD_MASK ) {
+ strcpy( buf, "IO_BRICK_SUB" );
+ *format = 0xCC;
+ }
+ else {
+ strcpy( buf, "IO_BRICK" );
+ *format = 0xC8;
+ }
+ break;
+
+ default:
+ strcpy( buf, "UNK_DEVICE" );
+ *format = 0xCA;
+ }
+}
+
+
+
+int fake_an_eeprom_record( eeprom_brd_record_t *buf, int component,
+ uint64_t ser )
+{
+ eeprom_board_ia_t *board;
+ eeprom_chassis_ia_t *chassis;
+ int i, cs;
+
+ board = buf->board_ia;
+ chassis = buf->chassis_ia;
+
+ if( !(component & SUBORD_MASK) ) {
+ if( !chassis )
+ return EEP_PARAM;
+ chassis->format = 0;
+ chassis->length = 5;
+ chassis->type = 0x17;
+
+ chassis->part_num_tl = 0xCC;
+ fake_a_part_number( chassis->part_num, component );
+ chassis->serial_num_tl = 0xC6;
+ fake_a_serial_number( chassis->serial_num, ser );
+
+ cs = chassis->format + chassis->length + chassis->type
+ + chassis->part_num_tl + chassis->serial_num_tl;
+ for( i = 0; i < (chassis->part_num_tl & FIELD_LENGTH_MASK); i++ )
+ cs += chassis->part_num[i];
+ for( i = 0; i < (chassis->serial_num_tl & FIELD_LENGTH_MASK); i++ )
+ cs += chassis->serial_num[i];
+ chassis->checksum = 256 - (cs % 256);
+ }
+
+ if( !board )
+ return EEP_PARAM;
+ board->format = 0;
+ board->length = 10;
+ board->language = 0;
+ board->mfg_date = 1789200; /* noon, 5/26/99 */
+ board->manuf_tl = 0xC3;
+ strcpy( board->manuf, "SGI" );
+
+ fake_a_product_name( &(board->product_tl), board->product, component );
+
+ board->serial_num_tl = 0xC6;
+ fake_a_serial_number( board->serial_num, ser );
+
+ board->part_num_tl = 0xCC;
+ fake_a_part_number( board->part_num, component );
+
+ board->board_rev_tl = 0xC2;
+ board->board_rev[0] = '0';
+ board->board_rev[1] = '1';
+
+ board->eeprom_size_tl = 0x01;
+ board->eeprom_size = 1;
+
+ board->temp_waiver_tl = 0xC2;
+ board->temp_waiver[0] = '0';
+ board->temp_waiver[1] = '1';
+
+ cs = board->format + board->length + board->language
+ + (board->mfg_date & 0xFF)
+ + (board->mfg_date & 0xFF00)
+ + (board->mfg_date & 0xFF0000)
+ + board->manuf_tl + board->product_tl + board->serial_num_tl
+ + board->part_num_tl + board->board_rev_tl
+ + board->board_rev[0] + board->board_rev[1]
+ + board->eeprom_size_tl + board->eeprom_size + board->temp_waiver_tl
+ + board->temp_waiver[0] + board->temp_waiver[1];
+ for( i = 0; i < (board->manuf_tl & FIELD_LENGTH_MASK); i++ )
+ cs += board->manuf[i];
+ for( i = 0; i < (board->product_tl & FIELD_LENGTH_MASK); i++ )
+ cs += board->product[i];
+ for( i = 0; i < (board->serial_num_tl & FIELD_LENGTH_MASK); i++ )
+ cs += board->serial_num[i];
+ for( i = 0; i < (board->part_num_tl & FIELD_LENGTH_MASK); i++ )
+ cs += board->part_num[i];
+
+ board->checksum = 256 - (cs % 256);
+
+ return EEP_OK;
+}
+
+#define EEPROM_CHUNKSIZE 64
+
+#if defined(EEPROM_DEBUG)
+#define RETURN_ERROR \
+{ \
+ printk( "read_ia error return, component 0x%x, line %d" \
+ ", address 0x%x, ia code 0x%x\n", \
+ l1_compt, __LINE__, sc->subch[subch].target, ia_code ); \
+ return EEP_L1; \
+}
+
+#else
+#define RETURN_ERROR return(EEP_L1)
+#endif
+
+int read_ia( l1sc_t *sc, int subch, int l1_compt,
+ int ia_code, char *eep_record )
+{
+#if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
+ return EEP_L1;
+#else
+ char msg[BRL1_QSIZE]; /* message buffer */
+ int len; /* number of bytes used in message buffer */
+ int ia_len = EEPROM_CHUNKSIZE; /* remaining bytes in info area */
+ int offset = 0; /* current offset into info area */
+
+ if ( IS_RUNNING_ON_SIMULATOR() )
+ return EEP_L1;
+
+ BZERO( msg, BRL1_QSIZE );
+
+ /* retrieve EEPROM data in 64-byte chunks
+ */
+
+ while( ia_len )
+ {
+ /* fill in msg with opcode & params */
+ if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_EEPROM, 8,
+ L1_ARG_INT, l1_compt,
+ L1_ARG_INT, ia_code,
+ L1_ARG_INT, offset,
+ L1_ARG_INT, ia_len )) < 0 )
+ {
+ RETURN_ERROR;
+ }
+
+ /* send the request to the L1 */
+
+ if( sc_command( sc, subch, msg, msg, &len ) ) {
+ RETURN_ERROR;
+ }
+
+ /* check response */
+ if( sc_interpret_resp( msg, 5,
+ L1_ARG_INT, &ia_len,
+ L1_ARG_UNKNOWN, &len, eep_record ) < 0 )
+ {
+ RETURN_ERROR;
+ }
+
+ if( ia_len > EEPROM_CHUNKSIZE )
+ ia_len = EEPROM_CHUNKSIZE;
+
+ eep_record += EEPROM_CHUNKSIZE;
+ offset += EEPROM_CHUNKSIZE;
+ }
+
+ return EEP_OK;
+#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
+}
+
+
+int read_spd( l1sc_t *sc, int subch, int l1_compt,
+ eeprom_spd_u *spd )
+{
+#if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
+ return EEP_L1;
+#else
+ char msg[BRL1_QSIZE]; /* message buffer */
+ int len; /* number of bytes used in message buffer */
+ int spd_len = EEPROM_CHUNKSIZE; /* remaining bytes in spd record */
+ int offset = 0; /* current offset into spd record */
+ char *spd_p = spd->bytes; /* "thumb" for writing to spd */
+
+ if ( IS_RUNNING_ON_SIMULATOR() )
+ return EEP_L1;
+
+ BZERO( msg, BRL1_QSIZE );
+
+ /* retrieve EEPROM data in 64-byte chunks
+ */
+
+ while( spd_len )
+ {
+ /* fill in msg with opcode & params */
+ if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_EEPROM, 8,
+ L1_ARG_INT, l1_compt,
+ L1_ARG_INT, L1_EEP_SPD,
+ L1_ARG_INT, offset,
+ L1_ARG_INT, spd_len )) < 0 )
+ {
+ return( EEP_L1 );
+ }
+
+ /* send the request to the L1 */
+ if( sc_command( sc, subch, msg, msg, &len ) ) {
+ return( EEP_L1 );
+ }
+
+ /* check response */
+ if( sc_interpret_resp( msg, 5,
+ L1_ARG_INT, &spd_len,
+ L1_ARG_UNKNOWN, &len, spd_p ) < 0 )
+ {
+ return( EEP_L1 );
+ }
+
+ if( spd_len > EEPROM_CHUNKSIZE )
+ spd_len = EEPROM_CHUNKSIZE;
+
+ spd_p += EEPROM_CHUNKSIZE;
+ offset += EEPROM_CHUNKSIZE;
+ }
+ return EEP_OK;
+#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
+}
+
+
+int read_chassis_ia( l1sc_t *sc, int subch, int l1_compt,
+ eeprom_chassis_ia_t *ia )
+{
+ char eep_record[512]; /* scratch area for building up info area */
+ char *eep_rec_p = eep_record; /* thumb for moving through eep_record */
+ int checksum = 0; /* use to verify eeprom record checksum */
+ int i;
+
+ /* Read in info area record from the L1.
+ */
+ if( read_ia( sc, subch, l1_compt, L1_EEP_CHASSIS, eep_record )
+ != EEP_OK )
+ {
+ return EEP_L1;
+ }
+
+ /* Now we've got the whole info area. Transfer it to the data structure.
+ */
+
+ eep_rec_p = eep_record;
+ ia->format = *eep_rec_p++;
+ ia->length = *eep_rec_p++;
+ if( ia->length == 0 ) {
+ /* since we're using 8*ia->length-1 as an array index later, make
+ * sure it's sane.
+ */
+ db_printf(( "read_chassis_ia: eeprom length byte of ZERO\n" ));
+ return EEP_L1;
+ }
+ ia->type = *eep_rec_p++;
+
+ ia->part_num_tl = *eep_rec_p++;
+
+ (void)BCOPY( eep_rec_p, ia->part_num, (ia->part_num_tl & FIELD_LENGTH_MASK) );
+ eep_rec_p += (ia->part_num_tl & FIELD_LENGTH_MASK);
+
+ ia->serial_num_tl = *eep_rec_p++;
+
+ BCOPY( eep_rec_p, ia->serial_num,
+ (ia->serial_num_tl & FIELD_LENGTH_MASK) );
+ eep_rec_p += (ia->serial_num_tl & FIELD_LENGTH_MASK);
+
+ ia->checksum = eep_record[(8 * ia->length) - 1];
+
+ /* verify checksum */
+ eep_rec_p = eep_record;
+ checksum = 0;
+ for( i = 0; i < (8 * ia->length); i++ ) {
+ checksum += *eep_rec_p++;
+ }
+
+ if( (checksum & 0xff) != 0 )
+ {
+ db_printf(( "read_chassis_ia: bad checksum\n" ));
+ db_printf(( "read_chassis_ia: target 0x%x uart 0x%x\n",
+ sc->subch[subch].target, sc->uart ));
+ return EEP_BAD_CHECKSUM;
+ }
+
+ return EEP_OK;
+}
+
+
+int read_board_ia( l1sc_t *sc, int subch, int l1_compt,
+ eeprom_board_ia_t *ia )
+{
+ char eep_record[512]; /* scratch area for building up info area */
+ char *eep_rec_p = eep_record; /* thumb for moving through eep_record */
+ int checksum = 0; /* running checksum total */
+ int i;
+
+ BZERO( ia, sizeof( eeprom_board_ia_t ) );
+
+ /* Read in info area record from the L1.
+ */
+ if( read_ia( sc, subch, l1_compt, L1_EEP_BOARD, eep_record )
+ != EEP_OK )
+ {
+ db_printf(( "read_board_ia: error reading info area from L1\n" ));
+ return EEP_L1;
+ }
+
+ /* Now we've got the whole info area. Transfer it to the data structure.
+ */
+
+ eep_rec_p = eep_record;
+ ia->format = *eep_rec_p++;
+ ia->length = *eep_rec_p++;
+ if( ia->length == 0 ) {
+ /* since we're using 8*ia->length-1 as an array index later, make
+ * sure it's sane.
+ */
+ db_printf(( "read_board_ia: eeprom length byte of ZERO\n" ));
+ return EEP_L1;
+ }
+ ia->language = *eep_rec_p++;
+
+ ia->mfg_date = eeprom_xlate_board_mfr_date( (uchar_t *)eep_rec_p );
+ eep_rec_p += 3;
+
+ ia->manuf_tl = *eep_rec_p++;
+
+ BCOPY( eep_rec_p, ia->manuf, (ia->manuf_tl & FIELD_LENGTH_MASK) );
+ eep_rec_p += (ia->manuf_tl & FIELD_LENGTH_MASK);
+
+ ia->product_tl = *eep_rec_p++;
+
+ BCOPY( eep_rec_p, ia->product, (ia->product_tl & FIELD_LENGTH_MASK) );
+ eep_rec_p += (ia->product_tl & FIELD_LENGTH_MASK);
+
+ ia->serial_num_tl = *eep_rec_p++;
+
+ BCOPY(eep_rec_p, ia->serial_num, (ia->serial_num_tl & FIELD_LENGTH_MASK));
+ eep_rec_p += (ia->serial_num_tl & FIELD_LENGTH_MASK);
+
+ ia->part_num_tl = *eep_rec_p++;
+
+ BCOPY( eep_rec_p, ia->part_num, (ia->part_num_tl & FIELD_LENGTH_MASK) );
+ eep_rec_p += (ia->part_num_tl & FIELD_LENGTH_MASK);
+
+ eep_rec_p++; /* we do not use the FRU file id */
+
+ ia->board_rev_tl = *eep_rec_p++;
+
+ BCOPY( eep_rec_p, ia->board_rev, (ia->board_rev_tl & FIELD_LENGTH_MASK) );
+ eep_rec_p += (ia->board_rev_tl & FIELD_LENGTH_MASK);
+
+ ia->eeprom_size_tl = *eep_rec_p++;
+ ia->eeprom_size = *eep_rec_p++;
+
+ ia->temp_waiver_tl = *eep_rec_p++;
+
+ BCOPY( eep_rec_p, ia->temp_waiver,
+ (ia->temp_waiver_tl & FIELD_LENGTH_MASK) );
+ eep_rec_p += (ia->temp_waiver_tl & FIELD_LENGTH_MASK);
+
+ /* if there's more, we must be reading a main board; get
+ * additional fields
+ */
+ if( ((unsigned char)*eep_rec_p != (unsigned char)EEPROM_EOF) ) {
+
+ ia->ekey_G_tl = *eep_rec_p++;
+ BCOPY( eep_rec_p, (char *)&ia->ekey_G,
+ ia->ekey_G_tl & FIELD_LENGTH_MASK );
+ eep_rec_p += (ia->ekey_G_tl & FIELD_LENGTH_MASK);
+
+ ia->ekey_P_tl = *eep_rec_p++;
+ BCOPY( eep_rec_p, (char *)&ia->ekey_P,
+ ia->ekey_P_tl & FIELD_LENGTH_MASK );
+ eep_rec_p += (ia->ekey_P_tl & FIELD_LENGTH_MASK);
+
+ ia->ekey_Y_tl = *eep_rec_p++;
+ BCOPY( eep_rec_p, (char *)&ia->ekey_Y,
+ ia->ekey_Y_tl & FIELD_LENGTH_MASK );
+ eep_rec_p += (ia->ekey_Y_tl & FIELD_LENGTH_MASK);
+
+ /*
+ * need to get a couple more fields if this is an I brick
+ */
+ if( ((unsigned char)*eep_rec_p != (unsigned char)EEPROM_EOF) ) {
+
+ ia->mac_addr_tl = *eep_rec_p++;
+ BCOPY( eep_rec_p, ia->mac_addr,
+ ia->mac_addr_tl & FIELD_LENGTH_MASK );
+ eep_rec_p += (ia->mac_addr_tl & FIELD_LENGTH_MASK);
+
+ ia->ieee1394_cfg_tl = *eep_rec_p++;
+ BCOPY( eep_rec_p, ia->ieee1394_cfg,
+ ia->ieee1394_cfg_tl & FIELD_LENGTH_MASK );
+
+ }
+ }
+
+ ia->checksum = eep_record[(ia->length * 8) - 1];
+
+ /* verify checksum */
+ eep_rec_p = eep_record;
+ checksum = 0;
+ for( i = 0; i < (8 * ia->length); i++ ) {
+ checksum += *eep_rec_p++;
+ }
+
+ if( (checksum & 0xff) != 0 )
+ {
+ db_printf(( "read_board_ia: bad checksum\n" ));
+ db_printf(( "read_board_ia: target 0x%x uart 0x%x\n",
+ sc->subch[subch].target, sc->uart ));
+ return EEP_BAD_CHECKSUM;
+ }
+
+ return EEP_OK;
+}
+
+
+int _cbrick_eeprom_read( eeprom_brd_record_t *buf, l1sc_t *scp,
+ int component )
+{
+#if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
+ return EEP_L1;
+#else
+ int r;
+ uint64_t uid = 0;
+ char uid_str[32];
+ int l1_compt, subch;
+
+ if ( IS_RUNNING_ON_SIMULATOR() )
+ return EEP_L1;
+
+ /* make sure we're targeting a cbrick */
+ if( !(component & C_BRICK) )
+ return EEP_PARAM;
+
+ /* If the promlog variable pointed to by IP27LOG_OVNIC is set,
+ * use that value for the cbrick UID rather than the EEPROM
+ * serial number.
+ */
+#ifdef LOG_GETENV
+ if( ip27log_getenv( scp->nasid, IP27LOG_OVNIC, uid_str, "0", 0 ) >= 0 )
+ {
+ db_printf(( "_cbrick_eeprom_read: "
+ "Overriding UID with environment variable %s\n",
+ IP27LOG_OVNIC ));
+ uid = strtoull( uid_str, NULL, 0 );
+ }
+#endif
+
+ if( (subch = sc_open( scp, L1_ADDR_LOCAL )) < 0 )
+ return EEP_L1;
+
+ switch( component )
+ {
+ case C_BRICK:
+ /* c-brick motherboard */
+ l1_compt = L1_EEP_NODE;
+ r = read_chassis_ia( scp, subch, l1_compt, buf->chassis_ia );
+ if( r != EEP_OK ) {
+ sc_close( scp, subch );
+ db_printf(( "_cbrick_eeprom_read: using a fake eeprom record\n" ));
+ return fake_an_eeprom_record( buf, component, uid );
+ }
+ if( uid ) {
+ /* If IP27LOG_OVNIC is set, we want to put that value
+ * in as our UID. */
+ fake_a_serial_number( buf->chassis_ia->serial_num, uid );
+ buf->chassis_ia->serial_num_tl = 6;
+ }
+ break;
+
+ case C_PIMM:
+ /* one of the PIMM boards */
+ l1_compt = L1_EEP_PIMM( component & COMPT_MASK );
+ break;
+
+ case C_DIMM:
+ /* one of the DIMMs */
+ l1_compt = L1_EEP_DIMM( component & COMPT_MASK );
+ r = read_spd( scp, subch, l1_compt, buf->spd );
+ sc_close( scp, subch );
+ return r;
+
+ default:
+ /* unsupported board type */
+ sc_close( scp, subch );
+ return EEP_PARAM;
+ }
+
+ r = read_board_ia( scp, subch, l1_compt, buf->board_ia );
+ sc_close( scp, subch );
+ if( r != EEP_OK )
+ {
+ db_printf(( "_cbrick_eeprom_read: using a fake eeprom record\n" ));
+ return fake_an_eeprom_record( buf, component, uid );
+ }
+ return EEP_OK;
+#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
+}
+
+
+int cbrick_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid,
+ int component )
+{
+#if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
+ return EEP_L1;
+#else
+ l1sc_t *scp;
+ int local = (nasid == get_nasid());
+
+ if ( IS_RUNNING_ON_SIMULATOR() )
+ return EEP_L1;
+
+ /* If this brick is retrieving its own uid, use the local l1sc_t to
+ * arbitrate access to the l1; otherwise, set up a new one (prom) or
+ * use an existing remote l1sc_t (kernel)
+ */
+ if( local ) {
+ scp = get_l1sc();
+ }
+ else {
+ elsc_t *get_elsc(void);
+ scp = get_elsc();
+ }
+
+ return _cbrick_eeprom_read( buf, scp, component );
+#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
+}
+
+
+int iobrick_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid,
+ int component )
+{
+#if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
+ return EEP_L1;
+#else
+ int r;
+ int l1_compt, subch;
+ l1sc_t *scp;
+ int local = (nasid == get_nasid());
+
+ if ( IS_RUNNING_ON_SIMULATOR() )
+ return EEP_L1;
+
+ /* make sure we're talking to an applicable brick */
+ if( !(component & IO_BRICK) ) {
+ return EEP_PARAM;
+ }
+
+ /* If we're talking to this c-brick's attached io brick, use
+ * the local l1sc_t; otherwise, set up a new one (prom) or
+ * use an existing remote l1sc_t (kernel)
+ */
+ if( local ) {
+ scp = get_l1sc();
+ }
+ else {
+ elsc_t *get_elsc(void);
+ scp = get_elsc();
+ }
+
+ if( (subch = sc_open( scp, L1_ADDR_LOCALIO )) < 0 )
+ return EEP_L1;
+
+
+ switch( component )
+ {
+ case IO_BRICK:
+ /* IO brick motherboard */
+ l1_compt = L1_EEP_LOGIC;
+ r = read_chassis_ia( scp, subch, l1_compt, buf->chassis_ia );
+
+ if( r != EEP_OK ) {
+ sc_close( scp, subch );
+#ifdef BRINGUP /* Once EEPROMs are universally available, remove this */
+ r = fake_an_eeprom_record( buf, component, rtc_time() );
+#endif /* BRINGUP */
+ return r;
+ }
+ break;
+
+ case IO_POWER:
+ /* IO brick power board */
+ l1_compt = L1_EEP_POWER;
+ break;
+
+ default:
+ /* unsupported board type */
+ sc_close( scp, subch );
+ return EEP_PARAM;
+ }
+
+ r = read_board_ia( scp, subch, l1_compt, buf->board_ia );
+ sc_close( scp, subch );
+ if( r != EEP_OK ) {
+ return r;
+ }
+ return EEP_OK;
+#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
+}
+
+
+int vector_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid,
+ net_vec_t path, int component )
+{
+#if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
+ return EEP_L1;
+#else
+ int r;
+ uint64_t uid = 0;
+ int l1_compt, subch;
+ l1sc_t sc;
+
+ if ( IS_RUNNING_ON_SIMULATOR() )
+ return EEP_L1;
+
+ /* make sure we're targeting an applicable brick */
+ if( !(component & VECTOR) )
+ return EEP_PARAM;
+
+ switch( component & BRICK_MASK )
+ {
+ case R_BRICK:
+ ROUTER_LOCK( path );
+ sc_init( &sc, nasid, path );
+
+ if( (subch = sc_open( &sc, L1_ADDR_LOCAL )) < 0 )
+ {
+ db_printf(( "vector_eeprom_read: couldn't open subch\n" ));
+ ROUTER_UNLOCK(path);
+ return EEP_L1;
+ }
+ switch( component )
+ {
+ case R_BRICK:
+ /* r-brick motherboard */
+ l1_compt = L1_EEP_LOGIC;
+ r = read_chassis_ia( &sc, subch, l1_compt, buf->chassis_ia );
+ if( r != EEP_OK ) {
+ sc_close( &sc, subch );
+ ROUTER_UNLOCK( path );
+ printk( "vector_eeprom_read: couldn't get rbrick eeprom info;"
+ " using current time as uid\n" );
+ uid = rtc_time();
+ db_printf(("vector_eeprom_read: using a fake eeprom record\n"));
+ return fake_an_eeprom_record( buf, component, uid );
+ }
+ break;
+
+ case R_POWER:
+ /* r-brick power board */
+ l1_compt = L1_EEP_POWER;
+ break;
+
+ default:
+ /* unsupported board type */
+ sc_close( &sc, subch );
+ ROUTER_UNLOCK( path );
+ return EEP_PARAM;
+ }
+ r = read_board_ia( &sc, subch, l1_compt, buf->board_ia );
+ sc_close( &sc, subch );
+ ROUTER_UNLOCK( path );
+ if( r != EEP_OK ) {
+ db_printf(( "vector_eeprom_read: using a fake eeprom record\n" ));
+ return fake_an_eeprom_record( buf, component, uid );
+ }
+ return EEP_OK;
+
+ case C_BRICK:
+ sc_init( &sc, nasid, path );
+ return _cbrick_eeprom_read( buf, &sc, component );
+
+ default:
+ /* unsupported brick type */
+ return EEP_PARAM;
+ }
+#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
+}
diff --git a/arch/ia64/sn/io/hcl.c b/arch/ia64/sn/io/hcl.c
new file mode 100644
index 000000000..295456feb
--- /dev/null
+++ b/arch/ia64/sn/io/hcl.c
@@ -0,0 +1,1506 @@
+/* $Id$
+ *
+ * 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.
+ *
+ * hcl - SGI's Hardware Graph compatibility layer.
+ *
+ * Copyright (C) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/sn/sgi.h>
+#include <linux/devfs_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <asm/io.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+
+#define HCL_NAME "SGI-HWGRAPH COMPATIBILITY DRIVER"
+#define HCL_TEMP_NAME "HCL_TEMP_NAME_USED_FOR_HWGRAPH_VERTEX_CREATE"
+#define HCL_TEMP_NAME_LEN 44
+#define HCL_VERSION "1.0"
+devfs_handle_t hwgraph_root = NULL;
+
+/*
+ * Debug flag definition.
+ */
+#define OPTION_NONE 0x00
+#define HCL_DEBUG_NONE 0x00000
+#define HCL_DEBUG_ALL 0x0ffff
+#if defined(CONFIG_HCL_DEBUG)
+static unsigned int hcl_debug_init __initdata = HCL_DEBUG_NONE;
+#endif
+static unsigned int hcl_debug = HCL_DEBUG_NONE;
+static unsigned int boot_options = OPTION_NONE;
+
+/*
+ * Some Global definitions.
+ */
+spinlock_t hcl_spinlock;
+devfs_handle_t hcl_handle = NULL;
+
+/*
+ * HCL device driver.
+ * The purpose of this device driver is to provide a facility
+ * for User Level Apps e.g. hinv, ioconfig etc. an ioctl path
+ * to manipulate label entries without having to implement
+ * system call interfaces. This methodology will enable us to
+ * make this feature module loadable.
+ */
+static int hcl_open(struct inode * inode, struct file * filp)
+{
+ if (hcl_debug) {
+ printk("HCL: hcl_open called.\n");
+ }
+
+ return(0);
+
+}
+
+static int hcl_close(struct inode * inode, struct file * filp)
+{
+
+ if (hcl_debug) {
+ printk("HCL: hcl_close called.\n");
+ }
+
+ return(0);
+
+}
+
+static int hcl_ioctl(struct inode * inode, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+
+ if (hcl_debug) {
+ printk("HCL: hcl_ioctl called.\n");
+ }
+
+ switch (cmd) {
+ default:
+ if (hcl_debug) {
+ printk("HCL: hcl_ioctl cmd = 0x%x\n", cmd);
+ }
+ }
+
+ return(0);
+
+}
+
+struct file_operations hcl_fops = {
+ NULL, /* lseek - default */
+ NULL, /* read - general block-dev read */
+ NULL, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* poll */
+ hcl_ioctl, /* ioctl */
+ NULL, /* mmap */
+ hcl_open, /* open */
+ NULL, /* flush */
+ hcl_close, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL /* lock */
+};
+
+
+/*
+ * init_hcl() - Boot time initialization. Ensure that it is called
+ * after devfs has been initialized.
+ *
+ * For now this routine is being called out of devfs/base.c. Actually
+ * Not a bad place to be ..
+ *
+ */
+#ifdef MODULE
+int init_module (void)
+#else
+int __init init_hcl(void)
+#endif
+{
+ extern void string_table_init(struct string_table *);
+ extern struct string_table label_string_table;
+ int rv = 0;
+
+ printk ("\n%s: v%s Colin Ngam (cngam@sgi.com)\n",
+ HCL_NAME, HCL_VERSION);
+#if defined(CONFIG_HCL_DEBUG) && !defined(MODULE)
+ hcl_debug = hcl_debug_init;
+ printk ("%s: hcl_debug: 0x%0x\n", HCL_NAME, hcl_debug);
+#endif
+ printk ("\n%s: boot_options: 0x%0x\n", HCL_NAME, boot_options);
+ spin_lock_init(&hcl_spinlock);
+
+ /*
+ * Create the hwgraph_root on devfs.
+ */
+ rv = hwgraph_path_add(NULL, "hw", &hwgraph_root);
+ if (rv)
+ printk ("init_hcl: Failed to create hwgraph_root. Error = %d.\n", rv);
+
+ /*
+ * Create the hcl driver to support inventory entry manipulations.
+ * By default, it is expected that devfs is mounted on /dev.
+ *
+ */
+ hcl_handle = hwgraph_register(hwgraph_root, ".hcl",
+ 0, DEVFS_FL_AUTO_DEVNUM,
+ 0, 0,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0,
+ &hcl_fops, NULL);
+
+ if (hcl_handle == NULL) {
+ panic("HCL: Unable to create HCL Driver in init_hcl().\n");
+ return(0);
+ }
+
+ /*
+ * Initialize the HCL string table.
+ */
+ string_table_init(&label_string_table);
+
+ return(0);
+
+}
+
+
+/*
+ * hcl_setup() - Process boot time parameters if given.
+ * "hcl="
+ * This routine gets called only if "hcl=" is given in the
+ * boot line and before init_hcl().
+ *
+ * We currently do not have any boot options .. when we do,
+ * functionalities can be added here.
+ *
+ */
+static int __init hcl_setup(char *str)
+{
+ while ( (*str != '\0') && !isspace (*str) )
+ {
+ printk("HCL: Boot time parameter %s\n", str);
+#ifdef CONFIG_HCL_DEBUG
+ if (strncmp (str, "all", 3) == 0) {
+ hcl_debug_init |= HCL_DEBUG_ALL;
+ str += 3;
+ } else
+ return 0;
+#endif
+ if (*str != ',') return 0;
+ ++str;
+ }
+
+ return 1;
+
+}
+
+__setup("hcl=", hcl_setup);
+
+
+/*
+ * Set device specific "fast information".
+ *
+ */
+void
+hwgraph_fastinfo_set(devfs_handle_t de, arbitrary_info_t fastinfo)
+{
+
+ if (hcl_debug) {
+ printk("HCL: hwgraph_fastinfo_set handle 0x%p fastinfo %ld\n",
+ de, fastinfo);
+ }
+
+ labelcl_info_replace_IDX(de, HWGRAPH_FASTINFO, fastinfo, NULL);
+
+}
+
+
+/*
+ * Get device specific "fast information".
+ *
+ */
+arbitrary_info_t
+hwgraph_fastinfo_get(devfs_handle_t de)
+{
+ arbitrary_info_t fastinfo;
+ int rv;
+
+ if (!de) {
+ printk(KERN_WARNING "HCL: hwgraph_fastinfo_get handle given is NULL.\n");
+ return(-1);
+ }
+
+ rv = labelcl_info_get_IDX(de, HWGRAPH_FASTINFO, &fastinfo);
+ if (rv == 0)
+ return(fastinfo);
+
+ return(0);
+}
+
+
+/*
+ * hwgraph_connectpt_set - Sets the connect point handle in de to the
+ * given connect_de handle. By default, the connect point of the
+ * devfs node is the parent. This effectively changes this assumption.
+ */
+int
+hwgraph_connectpt_set(devfs_handle_t de, devfs_handle_t connect_de)
+{
+ int rv;
+
+ if (!de)
+ return(-1);
+
+ rv = labelcl_info_connectpt_set(de, connect_de);
+
+ return(rv);
+}
+
+
+/*
+ * hwgraph_connectpt_get: Returns the entry's connect point in the devfs
+ * tree.
+ */
+devfs_handle_t
+hwgraph_connectpt_get(devfs_handle_t de)
+{
+ int rv;
+ arbitrary_info_t info;
+ devfs_handle_t connect;
+
+ rv = labelcl_info_get_IDX(de, HWGRAPH_CONNECTPT, &info);
+ if (rv != 0) {
+ return(NULL);
+ }
+
+ connect = (devfs_handle_t)info;
+ return(connect);
+
+}
+
+
+/*
+ * hwgraph_mk_dir - Creates a directory entry with devfs.
+ * Note that a directory entry in devfs can have children
+ * but it cannot be a char|block special file.
+ */
+devfs_handle_t
+hwgraph_mk_dir(devfs_handle_t de, const char *name,
+ unsigned int namelen, void *info)
+{
+
+ int rv;
+ labelcl_info_t *labelcl_info = NULL;
+ devfs_handle_t new_devfs_handle = NULL;
+ devfs_handle_t parent = NULL;
+
+ /*
+ * Create the device info structure for hwgraph compatiblity support.
+ */
+ labelcl_info = labelcl_info_create();
+ if (!labelcl_info)
+ return(NULL);
+
+ /*
+ * Create a devfs entry.
+ */
+ new_devfs_handle = devfs_mk_dir(de, name, (void *)labelcl_info);
+ if (!new_devfs_handle) {
+ labelcl_info_destroy(labelcl_info);
+ return(NULL);
+ }
+
+ /*
+ * Get the parent handle.
+ */
+ parent = devfs_get_parent (new_devfs_handle);
+
+ /*
+ * To provide the same semantics as the hwgraph, set the connect point.
+ */
+ rv = hwgraph_connectpt_set(new_devfs_handle, parent);
+ if (!rv) {
+ /*
+ * We need to clean up!
+ */
+ }
+
+ /*
+ * If the caller provides a private data pointer, save it in the
+ * labelcl info structure(fastinfo). This can be retrieved via
+ * hwgraph_fastinfo_get()
+ */
+ if (info)
+ hwgraph_fastinfo_set(new_devfs_handle, (arbitrary_info_t)info);
+
+ return(new_devfs_handle);
+
+}
+
+/*
+ * hwgraph_vertex_create - Create a vertex by giving it a temp name.
+ */
+
+/*
+ * hwgraph_path_add - Create a directory node with the given path starting
+ * from the given devfs_handle_t.
+ */
+extern char * dev_to_name(devfs_handle_t, char *, uint);
+int
+hwgraph_path_add(devfs_handle_t fromv,
+ char *path,
+ devfs_handle_t *new_de)
+{
+
+ unsigned int namelen = strlen(path);
+ int rv;
+
+ /*
+ * We need to handle the case when fromv is NULL ..
+ * in this case we need to create the path from the
+ * hwgraph root!
+ */
+ if (fromv == NULL)
+ fromv = hwgraph_root;
+
+ /*
+ * check the entry doesn't already exist, if it does
+ * then we simply want new_de to point to it (otherwise
+ * we'll overwrite the existing labelcl_info struct)
+ */
+ rv = hwgraph_edge_get(fromv, path, new_de);
+ if (rv) { /* couldn't find entry so we create it */
+ *new_de = hwgraph_mk_dir(fromv, path, namelen, NULL);
+ if (new_de == NULL)
+ return(-1);
+ else
+ return(0);
+ }
+ else
+ return(0);
+
+}
+
+/*
+ * hwgraph_register - Creates a file entry with devfs.
+ * Note that a file entry cannot have children .. it is like a
+ * char|block special vertex in hwgraph.
+ */
+devfs_handle_t
+hwgraph_register(devfs_handle_t de, const char *name,
+ unsigned int namelen, unsigned int flags,
+ unsigned int major, unsigned int minor,
+ umode_t mode, uid_t uid, gid_t gid,
+ struct file_operations *fops,
+ void *info)
+{
+
+ int rv;
+ void *labelcl_info = NULL;
+ devfs_handle_t new_devfs_handle = NULL;
+ devfs_handle_t parent = NULL;
+
+ /*
+ * Create the labelcl info structure for hwgraph compatiblity support.
+ */
+ labelcl_info = labelcl_info_create();
+ if (!labelcl_info)
+ return(NULL);
+
+ /*
+ * Create a devfs entry.
+ */
+ new_devfs_handle = devfs_register(de, name, flags, major,
+ minor, mode, fops, labelcl_info);
+ if (!new_devfs_handle) {
+ labelcl_info_destroy((labelcl_info_t *)labelcl_info);
+ return(NULL);
+ }
+
+ /*
+ * Get the parent handle.
+ */
+ if (de == NULL)
+ parent = devfs_get_parent (new_devfs_handle);
+ else
+ parent = de;
+
+ /*
+ * To provide the same semantics as the hwgraph, set the connect point.
+ */
+ rv = hwgraph_connectpt_set(new_devfs_handle, parent);
+ if (rv) {
+ /*
+ * We need to clean up!
+ */
+ printk("HCL: Unable to set the connect point to it's parent 0x%p\n",
+ new_devfs_handle);
+ }
+
+ /*
+ * If the caller provides a private data pointer, save it in the
+ * labelcl info structure(fastinfo). This can be retrieved via
+ * hwgraph_fastinfo_get()
+ */
+ if (info)
+ hwgraph_fastinfo_set(new_devfs_handle, (arbitrary_info_t)info);
+
+ return(new_devfs_handle);
+
+}
+
+
+/*
+ * hwgraph_mk_symlink - Create a symbolic link.
+ */
+int
+hwgraph_mk_symlink(devfs_handle_t de, const char *name, unsigned int namelen,
+ unsigned int flags, const char *link, unsigned int linklen,
+ devfs_handle_t *handle, void *info)
+{
+
+ void *labelcl_info = NULL;
+ int status = 0;
+ devfs_handle_t new_devfs_handle = NULL;
+
+ /*
+ * Create the labelcl info structure for hwgraph compatiblity support.
+ */
+ labelcl_info = labelcl_info_create();
+ if (!labelcl_info)
+ return(-1);
+
+ /*
+ * Create a symbolic link devfs entry.
+ */
+ status = devfs_mk_symlink(de, name, flags, link,
+ &new_devfs_handle, labelcl_info);
+ if ( (!new_devfs_handle) || (!status) ){
+ labelcl_info_destroy((labelcl_info_t *)labelcl_info);
+ return(-1);
+ }
+
+ /*
+ * If the caller provides a private data pointer, save it in the
+ * labelcl info structure(fastinfo). This can be retrieved via
+ * hwgraph_fastinfo_get()
+ */
+ if (info)
+ hwgraph_fastinfo_set(new_devfs_handle, (arbitrary_info_t)info);
+
+ *handle = new_devfs_handle;
+ return(0);
+
+}
+
+/*
+ * hwgraph_vertex_get_next - this routine returns the next sibbling for the
+ * device entry given in de. If there are no more sibbling, NULL
+ * is returned in next_sibbling.
+ *
+ * Currently we do not have any protection against de being deleted
+ * while it's handle is being held.
+ */
+int
+hwgraph_vertex_get_next(devfs_handle_t *next_sibbling, devfs_handle_t *de)
+{
+ *next_sibbling = devfs_get_next_sibling (*de);
+
+ if (*next_sibbling != NULL)
+ *de = *next_sibbling;
+ return (0);
+}
+
+
+/*
+ * hwgraph_vertex_destroy - Destroy the devfs entry
+ */
+int
+hwgraph_vertex_destroy(devfs_handle_t de)
+{
+
+ void *labelcl_info = NULL;
+
+ labelcl_info = devfs_get_info(de);
+ devfs_unregister(de);
+
+ if (labelcl_info)
+ labelcl_info_destroy((labelcl_info_t *)labelcl_info);
+
+ return(0);
+}
+
+/*
+** See if a vertex has an outgoing edge with a specified name.
+** Vertices in the hwgraph *implicitly* contain these edges:
+** "." refers to "current vertex"
+** ".." refers to "connect point vertex"
+** "char" refers to current vertex (character device access)
+** "block" refers to current vertex (block device access)
+*/
+
+/*
+ * hwgraph_edge_add - This routines has changed from the original conext.
+ * All it does now is to create a symbolic link from "from" to "to".
+ */
+/* ARGSUSED */
+int
+hwgraph_edge_add(devfs_handle_t from, devfs_handle_t to, char *name)
+{
+
+ char *path;
+ int name_start;
+ devfs_handle_t handle = NULL;
+ int rv;
+
+ path = kmalloc(1024, GFP_KERNEL);
+ name_start = devfs_generate_path (to, path, 1024);
+
+ /*
+ * Otherwise, just create a symlink to the vertex.
+ * In this case the vertex was previous created with a REAL pathname.
+ */
+ rv = devfs_mk_symlink (from, (const char *)name,
+ DEVFS_FL_DEFAULT, (const char *)&path[name_start],
+ &handle, NULL);
+
+ name_start = devfs_generate_path (handle, path, 1024);
+ return(rv);
+
+
+}
+/* ARGSUSED */
+int
+hwgraph_edge_get(devfs_handle_t from, char *name, devfs_handle_t *toptr)
+{
+
+ int namelen = 0;
+ devfs_handle_t target_handle = NULL;
+
+ if (name == NULL)
+ return(-1);
+
+ if (toptr == NULL)
+ return(-1);
+
+ /*
+ * If the name is "." just return the current devfs entry handle.
+ */
+ if (!strcmp(name, HWGRAPH_EDGELBL_DOT)) {
+ if (toptr) {
+ *toptr = from;
+ }
+ } else if (!strcmp(name, HWGRAPH_EDGELBL_DOTDOT)) {
+ /*
+ * Hmmm .. should we return the connect point or parent ..
+ * see in hwgraph, the concept of parent is the connectpt!
+ *
+ * Maybe we should see whether the connectpt is set .. if
+ * not just return the parent!
+ */
+ target_handle = hwgraph_connectpt_get(from);
+ if (target_handle) {
+ /*
+ * Just return the connect point.
+ */
+ *toptr = target_handle;
+ return(0);
+ }
+ target_handle = devfs_get_parent(from);
+ *toptr = target_handle;
+
+ } else {
+ /*
+ * Call devfs to get the devfs entry.
+ */
+ namelen = (int) strlen(name);
+ target_handle = devfs_find_handle (from, name, 0, 0,
+ 0, 1); /* Yes traverse symbolic links */
+ if (target_handle == NULL)
+ return(-1);
+ else
+ *toptr = target_handle;
+ }
+
+ return(0);
+}
+
+
+/*
+ * hwgraph_edge_get_next - Retrieves the next sibbling given the current
+ * entry number "placeptr".
+ *
+ * Allow the caller to retrieve walk through the sibblings of "source"
+ * devfs_handle_t. The implicit edges "." and ".." is returned first
+ * followed by each of the real children.
+ *
+ * We may end up returning garbage if another thread perform any deletion
+ * in this directory before "placeptr".
+ *
+ */
+/* ARGSUSED */
+int
+hwgraph_edge_get_next(devfs_handle_t source, char *name, devfs_handle_t *target,
+ uint *placeptr)
+
+{
+
+ uint which_place;
+ unsigned int namelen = 0;
+ const char *tempname = NULL;
+
+ if (placeptr == NULL)
+ return(-1);
+
+ which_place = *placeptr;
+
+again:
+ if (which_place <= HWGRAPH_RESERVED_PLACES) {
+ if (which_place == EDGE_PLACE_WANT_CURRENT) {
+ /*
+ * Looking for "."
+ * Return the current devfs handle.
+ */
+ if (name != NULL)
+ strcpy(name, HWGRAPH_EDGELBL_DOT);
+
+ if (target != NULL) {
+ *target = source;
+ /* XXX should incr "source" ref count here if we
+ * ever implement ref counts */
+ }
+
+ } else if (which_place == EDGE_PLACE_WANT_CONNECTPT) {
+ /*
+ * Looking for the connect point or parent.
+ * If the connect point is set .. it returns the connect point.
+ * Otherwise, it returns the parent .. will we support
+ * connect point?
+ */
+ devfs_handle_t connect_point = hwgraph_connectpt_get(source);
+
+ if (connect_point == NULL) {
+ /*
+ * No connectpoint set .. either the User
+ * explicitly NULL it or this node was not
+ * created via hcl.
+ */
+ which_place++;
+ goto again;
+ }
+
+ if (name != NULL)
+ strcpy(name, HWGRAPH_EDGELBL_DOTDOT);
+
+ if (target != NULL)
+ *target = connect_point;
+
+ } else if (which_place == EDGE_PLACE_WANT_REAL_EDGES) {
+ /*
+ * return first "real" entry in directory, and increment
+ * placeptr. Next time around we should have
+ * which_place > HWGRAPH_RESERVED_EDGES so we'll fall through
+ * this nested if block.
+ */
+ *target = devfs_get_first_child(source);
+ if (*target && name) {
+ tempname = devfs_get_name(*target, &namelen);
+ if (tempname && namelen)
+ strcpy(name, tempname);
+ }
+
+ *placeptr = which_place + 1;
+ return (0);
+ }
+
+ *placeptr = which_place+1;
+ return(0);
+ }
+
+ /*
+ * walk linked list, (which_place - HWGRAPH_RESERVED_PLACES) times
+ */
+ {
+ devfs_handle_t curr;
+ int i = 0;
+
+ for (curr=devfs_get_first_child(source), i= i+HWGRAPH_RESERVED_PLACES;
+ curr!=NULL && i<which_place;
+ curr=devfs_get_next_sibling(curr), i++)
+ ;
+ *target = curr;
+ *placeptr = which_place + 1;
+ if (curr && name) {
+ tempname = devfs_get_name(*target, &namelen);
+ printk("hwgraph_edge_get_next: Component name = %s, length = %d\n", tempname, namelen);
+ if (tempname && namelen)
+ strcpy(name, tempname);
+ }
+ }
+ if (target == NULL)
+ return(-1);
+ else
+ return(0);
+}
+
+/*
+ * hwgraph_info_add_LBL - Adds a new label for the device. Mark the info_desc
+ * of the label as INFO_DESC_PRIVATE and store the info in the label.
+ */
+/* ARGSUSED */
+int
+hwgraph_info_add_LBL( devfs_handle_t de,
+ char *name,
+ arbitrary_info_t info)
+{
+ return(labelcl_info_add_LBL(de, name, INFO_DESC_PRIVATE, info));
+}
+
+/*
+ * hwgraph_info_remove_LBL - Remove the label entry for the device.
+ */
+/* ARGSUSED */
+int
+hwgraph_info_remove_LBL( devfs_handle_t de,
+ char *name,
+ arbitrary_info_t *old_info)
+{
+ return(labelcl_info_remove_LBL(de, name, NULL, old_info));
+}
+
+/*
+ * hwgraph_info_replace_LBL - replaces an existing label with
+ * a new label info value.
+ */
+/* ARGSUSED */
+int
+hwgraph_info_replace_LBL( devfs_handle_t de,
+ char *name,
+ arbitrary_info_t info,
+ arbitrary_info_t *old_info)
+{
+ return(labelcl_info_replace_LBL(de, name,
+ INFO_DESC_PRIVATE, info,
+ NULL, old_info));
+}
+/*
+ * hwgraph_info_get_LBL - Get and return the info value in the label of the
+ * device.
+ */
+/* ARGSUSED */
+int
+hwgraph_info_get_LBL( devfs_handle_t de,
+ char *name,
+ arbitrary_info_t *infop)
+{
+ return(labelcl_info_get_LBL(de, name, NULL, infop));
+}
+
+/*
+ * hwgraph_info_get_exported_LBL - Retrieve the info_desc and info pointer
+ * of the given label for the device. The weird thing is that the label
+ * that matches the name is return irrespective of the info_desc value!
+ * Do not understand why the word "exported" is used!
+ */
+/* ARGSUSED */
+int
+hwgraph_info_get_exported_LBL( devfs_handle_t de,
+ char *name,
+ int *export_info,
+ arbitrary_info_t *infop)
+{
+ int rc;
+ arb_info_desc_t info_desc;
+
+ rc = labelcl_info_get_LBL(de, name, &info_desc, infop);
+ if (rc == 0)
+ *export_info = (int)info_desc;
+
+ return(rc);
+}
+
+/*
+ * hwgraph_info_get_next_LBL - Returns the next label info given the
+ * current label entry in place.
+ *
+ * Once again this has no locking or reference count for protection.
+ *
+ */
+/* ARGSUSED */
+int
+hwgraph_info_get_next_LBL( devfs_handle_t de,
+ char *buf,
+ arbitrary_info_t *infop,
+ labelcl_info_place_t *place)
+{
+ return(labelcl_info_get_next_LBL(de, buf, NULL, infop, place));
+}
+
+/*
+ * hwgraph_info_export_LBL - Retrieve the specified label entry and modify
+ * the info_desc field with the given value in nbytes.
+ */
+/* ARGSUSED */
+int
+hwgraph_info_export_LBL(devfs_handle_t de, char *name, int nbytes)
+{
+ arbitrary_info_t info;
+ int rc;
+
+ if (nbytes == 0)
+ nbytes = INFO_DESC_EXPORT;
+
+ if (nbytes < 0)
+ return(-1);
+
+ rc = labelcl_info_get_LBL(de, name, NULL, &info);
+ if (rc != 0)
+ return(rc);
+
+ rc = labelcl_info_replace_LBL(de, name,
+ nbytes, info, NULL, NULL);
+
+ return(rc);
+}
+
+/*
+ * hwgraph_info_unexport_LBL - Retrieve the given label entry and change the
+ * label info_descr filed to INFO_DESC_PRIVATE.
+ */
+/* ARGSUSED */
+int
+hwgraph_info_unexport_LBL(devfs_handle_t de, char *name)
+{
+ arbitrary_info_t info;
+ int rc;
+
+ rc = labelcl_info_get_LBL(de, name, NULL, &info);
+ if (rc != 0)
+ return(rc);
+
+ rc = labelcl_info_replace_LBL(de, name,
+ INFO_DESC_PRIVATE, info, NULL, NULL);
+
+ return(rc);
+}
+
+/*
+ * hwgraph_path_lookup - return the handle for the given path.
+ *
+ */
+int
+hwgraph_path_lookup( devfs_handle_t start_vertex_handle,
+ char *lookup_path,
+ devfs_handle_t *vertex_handle_ptr,
+ char **remainder)
+{
+ *vertex_handle_ptr = devfs_find_handle(start_vertex_handle, /* start dir */
+ lookup_path, /* path */
+ 0, /* major */
+ 0, /* minor */
+ 0, /* char | block */
+ 1); /* traverse symlinks */
+ if (*vertex_handle_ptr == NULL)
+ return(-1);
+ else
+ return(0);
+}
+
+/*
+ * hwgraph_traverse - Find and return the devfs handle starting from de.
+ *
+ */
+graph_error_t
+hwgraph_traverse(devfs_handle_t de, char *path, devfs_handle_t *found)
+{
+ /*
+ * get the directory entry (path should end in a directory)
+ */
+
+ *found = devfs_find_handle(de, /* start dir */
+ path, /* path */
+ 0, /* major */
+ 0, /* minor */
+ 0, /* char | block */
+ 1); /* traverse symlinks */
+ if (*found == NULL)
+ return(GRAPH_NOT_FOUND);
+ else
+ return(GRAPH_SUCCESS);
+}
+
+/*
+ * hwgraph_path_to_vertex - Return the devfs entry handle for the given
+ * pathname .. assume traverse symlinks too!.
+ */
+devfs_handle_t
+hwgraph_path_to_vertex(char *path)
+{
+ return(devfs_find_handle(NULL, /* start dir */
+ path, /* path */
+ 0, /* major */
+ 0, /* minor */
+ 0, /* char | block */
+ 1)); /* traverse symlinks */
+}
+
+/*
+ * hwgraph_path_to_dev - Returns the devfs_handle_t of the given path ..
+ * We only deal with devfs handle and not devfs_handle_t.
+*/
+devfs_handle_t
+hwgraph_path_to_dev(char *path)
+{
+ devfs_handle_t de;
+
+ de = hwgraph_path_to_vertex(path);
+ return(de);
+}
+
+/*
+ * hwgraph_block_device_get - return the handle of the block device file.
+ * The assumption here is that de is a directory.
+*/
+devfs_handle_t
+hwgraph_block_device_get(devfs_handle_t de)
+{
+ return(devfs_find_handle(de, /* start dir */
+ "block", /* path */
+ 0, /* major */
+ 0, /* minor */
+ DEVFS_SPECIAL_BLK, /* char | block */
+ 1)); /* traverse symlinks */
+}
+
+/*
+ * hwgraph_char_device_get - return the handle of the char device file.
+ * The assumption here is that de is a directory.
+*/
+devfs_handle_t
+hwgraph_char_device_get(devfs_handle_t de)
+{
+ return(devfs_find_handle(de, /* start dir */
+ "char", /* path */
+ 0, /* major */
+ 0, /* minor */
+ DEVFS_SPECIAL_CHR, /* char | block */
+ 1)); /* traverse symlinks */
+}
+
+/*
+ * hwgraph_cdevsw_get - returns the fops of the given devfs entry.
+ */
+struct file_operations *
+hwgraph_cdevsw_get(devfs_handle_t de)
+{
+ return(devfs_get_ops(de));
+}
+
+/*
+ * hwgraph_bdevsw_get - returns the fops of the given devfs entry.
+*/
+struct file_operations *
+hwgraph_bdevsw_get(devfs_handle_t de)
+{
+ return(devfs_get_ops(de));
+}
+
+/*
+** Inventory is now associated with a vertex in the graph. For items that
+** belong in the inventory but have no vertex
+** (e.g. old non-graph-aware drivers), we create a bogus vertex under the
+** INFO_LBL_INVENT name.
+**
+** For historical reasons, we prevent exact duplicate entries from being added
+** to a single vertex.
+*/
+
+/*
+ * hwgraph_inventory_add - Adds an inventory entry into de.
+ */
+int
+hwgraph_inventory_add( devfs_handle_t de,
+ int class,
+ int type,
+ major_t controller,
+ minor_t unit,
+ int state)
+{
+ inventory_t *pinv = NULL, *old_pinv = NULL, *last_pinv = NULL;
+ int rv;
+
+ /*
+ * Add our inventory data to the list of inventory data
+ * associated with this vertex.
+ */
+again:
+ /* GRAPH_LOCK_UPDATE(&invent_lock); */
+ rv = labelcl_info_get_LBL(de,
+ INFO_LBL_INVENT,
+ NULL, (arbitrary_info_t *)&old_pinv);
+ if ((rv != LABELCL_SUCCESS) && (rv != LABELCL_NOT_FOUND))
+ goto failure;
+
+ /*
+ * Seek to end of inventory items associated with this
+ * vertex. Along the way, make sure we're not duplicating
+ * an inventory item (for compatibility with old add_to_inventory)
+ */
+ for (;old_pinv; last_pinv = old_pinv, old_pinv = old_pinv->inv_next) {
+ if ((int)class != -1 && old_pinv->inv_class != class)
+ continue;
+ if ((int)type != -1 && old_pinv->inv_type != type)
+ continue;
+ if ((int)state != -1 && old_pinv->inv_state != state)
+ continue;
+ if ((int)controller != -1
+ && old_pinv->inv_controller != controller)
+ continue;
+ if ((int)unit != -1 && old_pinv->inv_unit != unit)
+ continue;
+
+ /* exact duplicate of previously-added inventory item */
+ rv = LABELCL_DUP;
+ goto failure;
+ }
+
+ /* Not a duplicate, so we know that we need to add something. */
+ if (pinv == NULL) {
+ /* Release lock while we wait for memory. */
+ /* GRAPH_LOCK_DONE_UPDATE(&invent_lock); */
+ pinv = (inventory_t *)kmalloc(sizeof(inventory_t), GFP_KERNEL);
+ replace_in_inventory(pinv, class, type, controller, unit, state);
+ goto again;
+ }
+
+ pinv->inv_next = NULL;
+ if (last_pinv) {
+ last_pinv->inv_next = pinv;
+ } else {
+ rv = labelcl_info_add_LBL(de, INFO_LBL_INVENT,
+ sizeof(inventory_t), (arbitrary_info_t)pinv);
+
+ if (!rv)
+ goto failure;
+ }
+
+ /* GRAPH_LOCK_DONE_UPDATE(&invent_lock); */
+ return(0);
+
+failure:
+ /* GRAPH_LOCK_DONE_UPDATE(&invent_lock); */
+ if (pinv)
+ kfree(pinv);
+ return(rv);
+}
+
+
+/*
+ * hwgraph_inventory_remove - Removes an inventory entry.
+ *
+ * Remove an inventory item associated with a vertex. It is the caller's
+ * responsibility to make sure that there are no races between removing
+ * inventory from a vertex and simultaneously removing that vertex.
+*/
+int
+hwgraph_inventory_remove( devfs_handle_t de,
+ int class,
+ int type,
+ major_t controller,
+ minor_t unit,
+ int state)
+{
+ inventory_t *pinv = NULL, *last_pinv = NULL, *next_pinv = NULL;
+ labelcl_error_t rv;
+
+ /*
+ * We never remove stuff from ".invent" ..
+ */
+ if (!de)
+ return (-1);
+
+ /*
+ * Remove our inventory data to the list of inventory data
+ * associated with this vertex.
+ */
+ /* GRAPH_LOCK_UPDATE(&invent_lock); */
+ rv = labelcl_info_get_LBL(de,
+ INFO_LBL_INVENT,
+ NULL, (arbitrary_info_t *)&pinv);
+ if (rv != LABELCL_SUCCESS)
+ goto failure;
+
+ /*
+ * Search through inventory items associated with this
+ * vertex, looking for a match.
+ */
+ for (;pinv; pinv = next_pinv) {
+ next_pinv = pinv->inv_next;
+
+ if(((int)class == -1 || pinv->inv_class == class) &&
+ ((int)type == -1 || pinv->inv_type == type) &&
+ ((int)state == -1 || pinv->inv_state == state) &&
+ ((int)controller == -1 || pinv->inv_controller == controller) &&
+ ((int)unit == -1 || pinv->inv_unit == unit)) {
+
+ /* Found a matching inventory item. Remove it. */
+ if (last_pinv) {
+ last_pinv->inv_next = pinv->inv_next;
+ } else {
+ rv = hwgraph_info_replace_LBL(de, INFO_LBL_INVENT, (arbitrary_info_t)pinv->inv_next, NULL);
+ if (rv != LABELCL_SUCCESS)
+ goto failure;
+ }
+
+ pinv->inv_next = NULL; /* sanity */
+ kfree(pinv);
+ } else
+ last_pinv = pinv;
+ }
+
+ if (last_pinv == NULL) {
+ rv = hwgraph_info_remove_LBL(de, INFO_LBL_INVENT, NULL);
+ if (rv != LABELCL_SUCCESS)
+ goto failure;
+ }
+
+ rv = LABELCL_SUCCESS;
+
+failure:
+ /* GRAPH_LOCK_DONE_UPDATE(&invent_lock); */
+ return(rv);
+}
+
+/*
+ * hwgraph_inventory_get_next - Get next inventory item associated with the
+ * specified vertex.
+ *
+ * No locking is really needed. We don't yet have the ability
+ * to remove inventory items, and new items are always added to
+ * the end of a vertex' inventory list.
+ *
+ * However, a devfs entry can be removed!
+*/
+int
+hwgraph_inventory_get_next(devfs_handle_t de, invplace_t *place, inventory_t **ppinv)
+{
+ inventory_t *pinv;
+ labelcl_error_t rv;
+
+ if (de == NULL)
+ return(LABELCL_BAD_PARAM);
+
+ if (place->invplace_vhdl == NULL) {
+ place->invplace_vhdl = de;
+ place->invplace_inv = NULL;
+ }
+
+ if (de != place->invplace_vhdl)
+ return(LABELCL_BAD_PARAM);
+
+ if (place->invplace_inv == NULL) {
+ /* Just starting on this vertex */
+ rv = labelcl_info_get_LBL(de, INFO_LBL_INVENT,
+ NULL, (arbitrary_info_t *)&pinv);
+ if (rv != LABELCL_SUCCESS)
+ return(LABELCL_NOT_FOUND);
+
+ } else {
+ /* Advance to next item on this vertex */
+ pinv = place->invplace_inv->inv_next;
+ }
+ place->invplace_inv = pinv;
+ *ppinv = pinv;
+
+ return(LABELCL_SUCCESS);
+}
+
+/*
+ * hwgraph_controller_num_get - Returns the controller number in the inventory
+ * entry.
+ */
+int
+hwgraph_controller_num_get(devfs_handle_t device)
+{
+ inventory_t *pinv;
+ invplace_t invplace = { NULL, NULL, NULL };
+ int val = -1;
+ if ((pinv = device_inventory_get_next(device, &invplace)) != NULL) {
+ val = (pinv->inv_class == INV_NETWORK)? pinv->inv_unit: pinv->inv_controller;
+ }
+#ifdef DEBUG
+ /*
+ * It does not make any sense to call this on vertexes with multiple
+ * inventory structs chained together
+ */
+ if ( device_inventory_get_next(device, &invplace) != NULL ) {
+ printk("Should panic here ... !\n");
+#endif
+ return (val);
+}
+
+/*
+ * hwgraph_controller_num_set - Sets the controller number in the inventory
+ * entry.
+ */
+void
+hwgraph_controller_num_set(devfs_handle_t device, int contr_num)
+{
+ inventory_t *pinv;
+ invplace_t invplace = { NULL, NULL, NULL };
+ if ((pinv = device_inventory_get_next(device, &invplace)) != NULL) {
+ if (pinv->inv_class == INV_NETWORK)
+ pinv->inv_unit = contr_num;
+ else {
+ if (pinv->inv_class == INV_FCNODE)
+ pinv = device_inventory_get_next(device, &invplace);
+ if (pinv != NULL)
+ pinv->inv_controller = contr_num;
+ }
+ }
+#ifdef DEBUG
+ /*
+ * It does not make any sense to call this on vertexes with multiple
+ * inventory structs chained together
+ */
+ if(pinv != NULL)
+ ASSERT(device_inventory_get_next(device, &invplace) == NULL);
+#endif
+}
+
+/*
+ * Find the canonical name for a given vertex by walking back through
+ * connectpt's until we hit the hwgraph root vertex (or until we run
+ * out of buffer space or until something goes wrong).
+ *
+ * COMPATIBILITY FUNCTIONALITY
+ * Walks back through 'parents', not necessarily the same as connectpts.
+ *
+ * Need to resolve the fact that devfs does not return the path from
+ * "/" but rather it just stops right before /dev ..
+ */
+int
+hwgraph_vertex_name_get(devfs_handle_t vhdl, char *buf, uint buflen)
+{
+ char *locbuf;
+ int pos;
+
+ if (buflen < 1)
+ return(-1); /* XXX should be GRAPH_BAD_PARAM ? */
+
+ locbuf = kmalloc(buflen, GFP_KERNEL);
+
+ pos = devfs_generate_path(vhdl, locbuf, buflen);
+ if (pos < 0) {
+ kfree(locbuf);
+ return pos;
+ }
+
+ strcpy(buf, &locbuf[pos]);
+ kfree(locbuf);
+ return 0;
+}
+
+/*
+** vertex_to_name converts a vertex into a canonical name by walking
+** back through connect points until we hit the hwgraph root (or until
+** we run out of buffer space).
+**
+** Usually returns a pointer to the original buffer, filled in as
+** appropriate. If the buffer is too small to hold the entire name,
+** or if anything goes wrong while determining the name, vertex_to_name
+** returns "UnknownDevice".
+*/
+
+#define DEVNAME_UNKNOWN "UnknownDevice"
+
+char *
+vertex_to_name(devfs_handle_t vhdl, char *buf, uint buflen)
+{
+ if (hwgraph_vertex_name_get(vhdl, buf, buflen) == GRAPH_SUCCESS)
+ return(buf);
+ else
+ return(DEVNAME_UNKNOWN);
+}
+
+#ifdef IRIX
+/*
+** Return the compact node id of the node that ultimately "owns" the specified
+** vertex. In order to do this, we walk back through masters and connect points
+** until we reach a vertex that represents a node.
+*/
+cnodeid_t
+master_node_get(devfs_handle_t vhdl)
+{
+ cnodeid_t cnodeid;
+ devfs_handle_t master;
+
+ for (;;) {
+ cnodeid = nodevertex_to_cnodeid(vhdl);
+ if (cnodeid != CNODEID_NONE)
+ return(cnodeid);
+
+ master = device_master_get(vhdl);
+
+ /* Check for exceptional cases */
+ if (master == vhdl) {
+ /* Since we got a reference to the "master" thru
+ * device_master_get() we should decrement
+ * its reference count by 1
+ */
+ hwgraph_vertex_unref(master);
+ return(CNODEID_NONE);
+ }
+
+ if (master == GRAPH_VERTEX_NONE) {
+ master = hwgraph_connectpt_get(vhdl);
+ if ((master == GRAPH_VERTEX_NONE) ||
+ (master == vhdl)) {
+ if (master == vhdl)
+ /* Since we got a reference to the
+ * "master" thru
+ * hwgraph_connectpt_get() we should
+ * decrement its reference count by 1
+ */
+ hwgraph_vertex_unref(master);
+ return(CNODEID_NONE);
+ }
+ }
+
+ vhdl = master;
+ /* Decrement the reference to "master" which was got
+ * either thru device_master_get() or hwgraph_connectpt_get()
+ * above.
+ */
+ hwgraph_vertex_unref(master);
+ }
+}
+
+/*
+ * Using the canonical path name to get hold of the desired vertex handle will
+ * not work on multi-hub sn0 nodes. Hence, we use the following (slightly
+ * convoluted) algorithm.
+ *
+ * - Start at the vertex corresponding to the driver (provided as input parameter)
+ * - Loop till you reach a vertex which has EDGE_LBL_MEMORY
+ * - If EDGE_LBL_CONN exists, follow that up.
+ * else if EDGE_LBL_MASTER exists, follow that up.
+ * else follow EDGE_LBL_DOTDOT up.
+ *
+ * * We should be at desired hub/heart vertex now *
+ * - Follow EDGE_LBL_CONN to the widget vertex.
+ *
+ * - return vertex handle of this widget.
+ */
+devfs_handle_t
+mem_vhdl_get(devfs_handle_t drv_vhdl)
+{
+devfs_handle_t cur_vhdl, cur_upper_vhdl;
+devfs_handle_t tmp_mem_vhdl, mem_vhdl;
+graph_error_t loop_rv;
+
+ /* Initializations */
+ cur_vhdl = drv_vhdl;
+ loop_rv = ~GRAPH_SUCCESS;
+
+ /* Loop till current vertex has EDGE_LBL_MEMORY */
+ while (loop_rv != GRAPH_SUCCESS) {
+
+ if ((hwgraph_edge_get(cur_vhdl, EDGE_LBL_CONN, &cur_upper_vhdl)) == GRAPH_SUCCESS) {
+
+ } else if ((hwgraph_edge_get(cur_vhdl, EDGE_LBL_MASTER, &cur_upper_vhdl)) == GRAPH_SUCCESS) {
+ } else { /* Follow HWGRAPH_EDGELBL_DOTDOT up */
+ (void) hwgraph_edge_get(cur_vhdl, HWGRAPH_EDGELBL_DOTDOT, &cur_upper_vhdl);
+ }
+
+ cur_vhdl = cur_upper_vhdl;
+
+#if DEBUG && HWG_DEBUG
+ printf("Current vhdl %d \n", cur_vhdl);
+#endif /* DEBUG */
+
+ loop_rv = hwgraph_edge_get(cur_vhdl, EDGE_LBL_MEMORY, &tmp_mem_vhdl);
+ }
+
+ /* We should be at desired hub/heart vertex now */
+ if ((hwgraph_edge_get(cur_vhdl, EDGE_LBL_CONN, &mem_vhdl)) != GRAPH_SUCCESS)
+ return (GRAPH_VERTEX_NONE);
+
+ return (mem_vhdl);
+}
+#endif /* IRIX */
+
+
+/*
+** Add a char device -- if the driver supports it -- at a specified vertex.
+*/
+graph_error_t
+hwgraph_char_device_add( devfs_handle_t from,
+ char *path,
+ char *prefix,
+ devfs_handle_t *devhdl)
+{
+ devfs_handle_t xx = NULL;
+
+ printk("FIXME: hwgraph_char_device_add() called. Use hwgraph_register.\n");
+ *devhdl = xx; // Must set devhdl
+ return(GRAPH_SUCCESS);
+}
+
+graph_error_t
+hwgraph_edge_remove(devfs_handle_t from, char *name, devfs_handle_t *toptr)
+{
+ printk("FIXME: hwgraph_edge_remove\n");
+ return(GRAPH_ILLEGAL_REQUEST);
+}
+
+graph_error_t
+hwgraph_vertex_unref(devfs_handle_t vhdl)
+{
+ printk("FIXME: hwgraph_vertex_unref\n");
+ return(GRAPH_ILLEGAL_REQUEST);
+}
+
+
+EXPORT_SYMBOL(hwgraph_mk_dir);
+EXPORT_SYMBOL(hwgraph_path_add);
+EXPORT_SYMBOL(hwgraph_char_device_add);
+EXPORT_SYMBOL(hwgraph_register);
+EXPORT_SYMBOL(hwgraph_vertex_destroy);
+
+EXPORT_SYMBOL(hwgraph_fastinfo_get);
+EXPORT_SYMBOL(hwgraph_edge_get);
+
+EXPORT_SYMBOL(hwgraph_fastinfo_set);
+EXPORT_SYMBOL(hwgraph_connectpt_set);
+EXPORT_SYMBOL(hwgraph_connectpt_get);
+EXPORT_SYMBOL(hwgraph_edge_get_next);
+EXPORT_SYMBOL(hwgraph_info_add_LBL);
+EXPORT_SYMBOL(hwgraph_info_remove_LBL);
+EXPORT_SYMBOL(hwgraph_info_replace_LBL);
+EXPORT_SYMBOL(hwgraph_info_get_LBL);
+EXPORT_SYMBOL(hwgraph_info_get_exported_LBL);
+EXPORT_SYMBOL(hwgraph_info_get_next_LBL);
+EXPORT_SYMBOL(hwgraph_info_export_LBL);
+EXPORT_SYMBOL(hwgraph_info_unexport_LBL);
+EXPORT_SYMBOL(hwgraph_path_lookup);
+EXPORT_SYMBOL(hwgraph_traverse);
+EXPORT_SYMBOL(hwgraph_path_to_vertex);
+EXPORT_SYMBOL(hwgraph_path_to_dev);
+EXPORT_SYMBOL(hwgraph_block_device_get);
+EXPORT_SYMBOL(hwgraph_char_device_get);
+EXPORT_SYMBOL(hwgraph_cdevsw_get);
+EXPORT_SYMBOL(hwgraph_bdevsw_get);
+EXPORT_SYMBOL(hwgraph_vertex_name_get);
diff --git a/arch/ia64/sn/io/hcl_util.c b/arch/ia64/sn/io/hcl_util.c
new file mode 100644
index 000000000..d11f49cdd
--- /dev/null
+++ b/arch/ia64/sn/io/hcl_util.c
@@ -0,0 +1,159 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/devfs_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <asm/sn/sgi.h>
+#include <asm/io.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/hcl_util.h>
+#include <asm/sn/nodepda.h>
+
+static devfs_handle_t hwgraph_all_cnodes = GRAPH_VERTEX_NONE;
+extern devfs_handle_t hwgraph_root;
+
+
+/*
+** Return the "master" for a given vertex. A master vertex is a
+** controller or adapter or other piece of hardware that the given
+** vertex passes through on the way to the rest of the system.
+*/
+devfs_handle_t
+device_master_get(devfs_handle_t vhdl)
+{
+ graph_error_t rc;
+ devfs_handle_t master;
+
+ rc = hwgraph_edge_get(vhdl, EDGE_LBL_MASTER, &master);
+ if (rc == GRAPH_SUCCESS)
+ return(master);
+ else
+ return(GRAPH_VERTEX_NONE);
+}
+
+/*
+** Set the master for a given vertex.
+** Returns 0 on success, non-0 indicates failure
+*/
+int
+device_master_set(devfs_handle_t vhdl, devfs_handle_t master)
+{
+ graph_error_t rc;
+
+ rc = hwgraph_edge_add(vhdl, master, EDGE_LBL_MASTER);
+ return(rc != GRAPH_SUCCESS);
+}
+
+
+/*
+** Return the compact node id of the node that ultimately "owns" the specified
+** vertex. In order to do this, we walk back through masters and connect points
+** until we reach a vertex that represents a node.
+*/
+cnodeid_t
+master_node_get(devfs_handle_t vhdl)
+{
+ cnodeid_t cnodeid;
+ devfs_handle_t master;
+
+ for (;;) {
+ cnodeid = nodevertex_to_cnodeid(vhdl);
+ if (cnodeid != CNODEID_NONE)
+ return(cnodeid);
+
+ master = device_master_get(vhdl);
+
+ /* Check for exceptional cases */
+ if (master == vhdl) {
+ /* Since we got a reference to the "master" thru
+ * device_master_get() we should decrement
+ * its reference count by 1
+ */
+ return(CNODEID_NONE);
+ }
+
+ if (master == GRAPH_VERTEX_NONE) {
+ master = hwgraph_connectpt_get(vhdl);
+ if ((master == GRAPH_VERTEX_NONE) ||
+ (master == vhdl)) {
+ return(CNODEID_NONE);
+ }
+ }
+
+ vhdl = master;
+ }
+}
+
+/*
+** If the specified device represents a node, return its
+** compact node ID; otherwise, return CNODEID_NONE.
+*/
+cnodeid_t
+nodevertex_to_cnodeid(devfs_handle_t vhdl)
+{
+ int rv = 0;
+ arbitrary_info_t cnodeid = CNODEID_NONE;
+
+ rv = labelcl_info_get_LBL(vhdl, INFO_LBL_CNODEID, NULL, &cnodeid);
+
+ return((cnodeid_t)cnodeid);
+}
+
+void
+mark_nodevertex_as_node(devfs_handle_t vhdl, cnodeid_t cnodeid)
+{
+ if (cnodeid == CNODEID_NONE)
+ return;
+
+ cnodeid_to_vertex(cnodeid) = vhdl;
+ labelcl_info_add_LBL(vhdl, INFO_LBL_CNODEID, INFO_DESC_EXPORT,
+ (arbitrary_info_t)cnodeid);
+
+ {
+ char cnodeid_buffer[10];
+
+ if (hwgraph_all_cnodes == GRAPH_VERTEX_NONE) {
+ (void)hwgraph_path_add( hwgraph_root,
+ EDGE_LBL_NODENUM,
+ &hwgraph_all_cnodes);
+ }
+
+ sprintf(cnodeid_buffer, "%d", cnodeid);
+ (void)hwgraph_edge_add( hwgraph_all_cnodes,
+ vhdl,
+ cnodeid_buffer);
+ }
+}
+
+
+/*
+** dev_to_name converts a devfs_handle_t into a canonical name. If the devfs_handle_t
+** represents a vertex in the hardware graph, it is converted in the
+** normal way for vertices. If the devfs_handle_t is an old devfs_handle_t (one which
+** does not represent a hwgraph vertex), we synthesize a name based
+** on major/minor number.
+**
+** Usually returns a pointer to the original buffer, filled in as
+** appropriate. If the buffer is too small to hold the entire name,
+** or if anything goes wrong while determining the name, dev_to_name
+** returns "UnknownDevice".
+*/
+char *
+dev_to_name(devfs_handle_t dev, char *buf, uint buflen)
+{
+ return(vertex_to_name(dev, buf, buflen));
+}
+
+
diff --git a/arch/ia64/sn/io/hubdev.c b/arch/ia64/sn/io/hubdev.c
new file mode 100644
index 000000000..64379f081
--- /dev/null
+++ b/arch/ia64/sn/io/hubdev.c
@@ -0,0 +1,126 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/sn1/hubdev.h>
+#include <asm/sn/sn_private.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+
+struct hubdev_callout {
+ int (*attach_method)(devfs_handle_t);
+ struct hubdev_callout *fp;
+};
+
+typedef struct hubdev_callout hubdev_callout_t;
+
+mutex_t hubdev_callout_mutex;
+hubdev_callout_t *hubdev_callout_list = NULL;
+
+void
+hubdev_init(void)
+{
+ mutex_init(&hubdev_callout_mutex, MUTEX_DEFAULT, "hubdev");
+ hubdev_callout_list = NULL;
+}
+
+void
+hubdev_register(int (*attach_method)(devfs_handle_t))
+{
+ hubdev_callout_t *callout;
+
+ ASSERT(attach_method);
+
+ callout = (hubdev_callout_t *)kmem_zalloc(sizeof(hubdev_callout_t), KM_SLEEP);
+ ASSERT(callout);
+
+ mutex_lock(&hubdev_callout_mutex, PZERO);
+ /*
+ * Insert at the front of the list
+ */
+ callout->fp = hubdev_callout_list;
+ hubdev_callout_list = callout;
+ callout->attach_method = attach_method;
+ mutex_unlock(&hubdev_callout_mutex);
+}
+
+int
+hubdev_unregister(int (*attach_method)(devfs_handle_t))
+{
+ hubdev_callout_t **p;
+
+ ASSERT(attach_method);
+
+ mutex_lock(&hubdev_callout_mutex, PZERO);
+ /*
+ * Remove registry element containing attach_method
+ */
+ for (p = &hubdev_callout_list; *p != NULL; p = &(*p)->fp) {
+ if ((*p)->attach_method == attach_method) {
+ hubdev_callout_t* victim = *p;
+ *p = (*p)->fp;
+ kfree(victim);
+ mutex_unlock(&hubdev_callout_mutex);
+ return (0);
+ }
+ }
+ mutex_unlock(&hubdev_callout_mutex);
+ return (ENOENT);
+}
+
+
+int
+hubdev_docallouts(devfs_handle_t hub)
+{
+ hubdev_callout_t *p;
+ int errcode;
+
+ mutex_lock(&hubdev_callout_mutex, PZERO);
+
+ for (p = hubdev_callout_list; p != NULL; p = p->fp) {
+ ASSERT(p->attach_method);
+ errcode = (*p->attach_method)(hub);
+ if (errcode != 0) {
+ mutex_unlock(&hubdev_callout_mutex);
+ return (errcode);
+ }
+ }
+ mutex_unlock(&hubdev_callout_mutex);
+ return (0);
+}
+
+/*
+ * Given a hub vertex, return the base address of the Hspec space
+ * for that hub.
+ */
+caddr_t
+hubdev_prombase_get(devfs_handle_t hub)
+{
+ hubinfo_t hinfo = NULL;
+
+ hubinfo_get(hub, &hinfo);
+ ASSERT(hinfo);
+
+ return ((caddr_t)NODE_RBOOT_BASE(hinfo->h_nasid));
+}
+
+cnodeid_t
+hubdev_cnodeid_get(devfs_handle_t hub)
+{
+ hubinfo_t hinfo = NULL;
+ hubinfo_get(hub, &hinfo);
+ ASSERT(hinfo);
+
+ return hinfo->h_cnodeid;
+}
diff --git a/arch/ia64/sn/io/hubspc.c b/arch/ia64/sn/io/hubspc.c
new file mode 100644
index 000000000..a6a229b96
--- /dev/null
+++ b/arch/ia64/sn/io/hubspc.c
@@ -0,0 +1,447 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+/*
+ * hubspc.c - Hub Memory Space Management Driver
+ * This driver implements the managers for the following
+ * memory resources:
+ * 1) reference counters
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <linux/devfs_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <asm/io.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/mem_refcnt.h>
+#include <asm/sn/agent.h>
+#include <asm/sn/addrs.h>
+
+
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+#include <asm/sn/sn1/ip27config.h>
+#include <asm/sn/sn1/hubdev.h>
+#include <asm/sn/ksys/elsc.h>
+#endif
+
+#include <asm/sn/hubspc.h>
+
+
+/* Uncomment the following line for tracing */
+/* #define HUBSPC_DEBUG 1 */
+
+int hubspc_devflag = D_MP;
+
+extern void *device_info_get(devfs_handle_t device);
+extern void device_info_set(devfs_handle_t device, void *info);
+
+
+
+/***********************************************************************/
+/* CPU Prom Space */
+/***********************************************************************/
+
+typedef struct cpuprom_info {
+ devfs_handle_t prom_dev;
+ devfs_handle_t nodevrtx;
+ struct cpuprom_info *next;
+}cpuprom_info_t;
+
+static cpuprom_info_t *cpuprom_head;
+lock_t cpuprom_spinlock;
+#define PROM_LOCK() mutex_spinlock(&cpuprom_spinlock)
+#define PROM_UNLOCK(s) mutex_spinunlock(&cpuprom_spinlock, (s))
+
+/*
+ * Add prominfo to the linked list maintained.
+ */
+void
+prominfo_add(devfs_handle_t hub, devfs_handle_t prom)
+{
+ cpuprom_info_t *info;
+ int s;
+
+ info = kmalloc(sizeof(cpuprom_info_t), GFP_KERNEL);
+ ASSERT(info);
+ info->prom_dev = prom;
+ info->nodevrtx = hub;
+
+
+ s = PROM_LOCK();
+ info->next = cpuprom_head;
+ cpuprom_head = info;
+ PROM_UNLOCK(s);
+}
+
+void
+prominfo_del(devfs_handle_t prom)
+{
+ int s;
+ cpuprom_info_t *info;
+ cpuprom_info_t **prev;
+
+ s = PROM_LOCK();
+ prev = &cpuprom_head;
+ while ( (info = *prev) ) {
+ if (info->prom_dev == prom) {
+ *prev = info->next;
+ PROM_UNLOCK(s);
+ return;
+ }
+
+ prev = &info->next;
+ }
+ PROM_UNLOCK(s);
+ ASSERT(0);
+}
+
+devfs_handle_t
+prominfo_nodeget(devfs_handle_t prom)
+{
+ int s;
+ cpuprom_info_t *info;
+
+ s = PROM_LOCK();
+ info = cpuprom_head;
+ while (info) {
+ if(info->prom_dev == prom) {
+ PROM_UNLOCK(s);
+ return info->nodevrtx;
+ }
+ info = info->next;
+ }
+ PROM_UNLOCK(s);
+ return 0;
+}
+
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+#define SN_PROMVERSION INV_IP35PROM
+#endif
+
+/* Add "detailed" labelled inventory information to the
+ * prom vertex
+ */
+void
+cpuprom_detailed_inventory_info_add(devfs_handle_t prom_dev,devfs_handle_t node)
+{
+ invent_miscinfo_t *cpuprom_inventory_info;
+ extern invent_generic_t *klhwg_invent_alloc(cnodeid_t cnode,
+ int class, int size);
+ cnodeid_t cnode = hubdev_cnodeid_get(node);
+
+ /* Allocate memory for the extra inventory information
+ * for the prom
+ */
+ cpuprom_inventory_info = (invent_miscinfo_t *)
+ klhwg_invent_alloc(cnode, INV_PROM, sizeof(invent_miscinfo_t));
+
+ ASSERT(cpuprom_inventory_info);
+
+ /* Set the enabled flag so that the hinv interprets this
+ * information
+ */
+ cpuprom_inventory_info->im_gen.ig_flag = INVENT_ENABLED;
+ cpuprom_inventory_info->im_type = SN_PROMVERSION;
+ /* Store prom revision into inventory information */
+ cpuprom_inventory_info->im_rev = IP27CONFIG.pvers_rev;
+ cpuprom_inventory_info->im_version = IP27CONFIG.pvers_vers;
+
+
+ /* Store this info as labelled information hanging off the
+ * prom device vertex
+ */
+ hwgraph_info_add_LBL(prom_dev, INFO_LBL_DETAIL_INVENT,
+ (arbitrary_info_t) cpuprom_inventory_info);
+ /* Export this information so that user programs can get to
+ * this by using attr_get()
+ */
+ hwgraph_info_export_LBL(prom_dev, INFO_LBL_DETAIL_INVENT,
+ sizeof(invent_miscinfo_t));
+}
+
+int
+cpuprom_attach(devfs_handle_t node)
+{
+ devfs_handle_t prom_dev;
+
+ hwgraph_char_device_add(node, EDGE_LBL_PROM, "hubspc_", &prom_dev);
+#ifdef HUBSPC_DEBUG
+ printf("hubspc: prom_attach hub: 0x%x prom: 0x%x\n", node, prom_dev);
+#endif /* HUBSPC_DEBUG */
+ device_inventory_add(prom_dev, INV_PROM, SN_PROMVERSION,
+ (major_t)0, (minor_t)0, 0);
+
+ /* Add additional inventory info about the cpu prom like
+ * revision & version numbers etc.
+ */
+ cpuprom_detailed_inventory_info_add(prom_dev,node);
+ device_info_set(prom_dev, (void*)(ulong)HUBSPC_PROM);
+ prominfo_add(node, prom_dev);
+
+ return (0);
+}
+
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+#define FPROM_CONFIG_ADDR MD_JUNK_BUS_TIMING
+#define FPROM_ENABLE_MASK MJT_FPROM_ENABLE_MASK
+#define FPROM_ENABLE_SHFT MJT_FPROM_ENABLE_SHFT
+#define FPROM_SETUP_MASK MJT_FPROM_SETUP_MASK
+#define FPROM_SETUP_SHFT MJT_FPROM_SETUP_SHFT
+#endif
+
+/*ARGSUSED*/
+int
+cpuprom_map(devfs_handle_t dev, vhandl_t *vt, off_t addr, size_t len)
+{
+ int errcode;
+ caddr_t kvaddr;
+ devfs_handle_t node;
+ cnodeid_t cnode;
+
+ node = prominfo_nodeget(dev);
+
+ if (!node)
+ return EIO;
+
+
+ kvaddr = hubdev_prombase_get(node);
+ cnode = hubdev_cnodeid_get(node);
+#ifdef HUBSPC_DEBUG
+ printf("cpuprom_map: hubnode %d kvaddr 0x%x\n", node, kvaddr);
+#endif
+
+ if (len > RBOOT_SIZE)
+ len = RBOOT_SIZE;
+ /*
+ * Map in the prom space
+ */
+ errcode = v_mapphys(vt, kvaddr, len);
+
+ if (errcode == 0 ){
+ /*
+ * Set the MD configuration registers suitably.
+ */
+ nasid_t nasid;
+ uint64_t value;
+ volatile hubreg_t *regaddr;
+
+ nasid = COMPACT_TO_NASID_NODEID(cnode);
+ regaddr = REMOTE_HUB_ADDR(nasid, FPROM_CONFIG_ADDR);
+ value = HUB_L(regaddr);
+ value &= ~(FPROM_SETUP_MASK | FPROM_ENABLE_MASK);
+ {
+ value |= (((long)CONFIG_FPROM_SETUP << FPROM_SETUP_SHFT) |
+ ((long)CONFIG_FPROM_ENABLE << FPROM_ENABLE_SHFT));
+ }
+ HUB_S(regaddr, value);
+
+ }
+ return (errcode);
+}
+
+/*ARGSUSED*/
+int
+cpuprom_unmap(devfs_handle_t dev, vhandl_t *vt)
+{
+ return 0;
+}
+
+/***********************************************************************/
+/* Base Hub Space Driver */
+/***********************************************************************/
+
+// extern int l1_attach( devfs_handle_t );
+
+/*
+ * hubspc_init
+ * Registration of the hubspc devices with the hub manager
+ */
+void
+hubspc_init(void)
+{
+ /*
+ * Register with the hub manager
+ */
+
+ /* The reference counters */
+ hubdev_register(mem_refcnt_attach);
+
+ /* Prom space */
+ hubdev_register(cpuprom_attach);
+
+#if defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
+ /* L1 system controller link */
+ if ( !IS_RUNNING_ON_SIMULATOR() ) {
+ /* initialize the L1 link */
+ void l1_cons_init( l1sc_t *sc );
+ elsc_t *get_elsc(void);
+
+ l1_cons_init((l1sc_t *)get_elsc());
+ }
+#endif
+
+#ifdef HUBSPC_DEBUG
+ printf("hubspc_init: Completed\n");
+#endif /* HUBSPC_DEBUG */
+ /* Initialize spinlocks */
+ spinlock_init(&cpuprom_spinlock, "promlist");
+}
+
+/* ARGSUSED */
+int
+hubspc_open(devfs_handle_t *devp, mode_t oflag, int otyp, cred_t *crp)
+{
+ int errcode = 0;
+
+ switch ((hubspc_subdevice_t)(ulong)device_info_get(*devp)) {
+ case HUBSPC_REFCOUNTERS:
+ errcode = mem_refcnt_open(devp, oflag, otyp, crp);
+ break;
+
+ case HUBSPC_PROM:
+ /* Check if the user has proper access rights to
+ * read/write the prom space.
+ */
+ if (!cap_able(CAP_DEVICE_MGT)) {
+ errcode = EPERM;
+ }
+ break;
+
+ default:
+ errcode = ENODEV;
+ }
+
+#ifdef HUBSPC_DEBUG
+ printf("hubspc_open: Completed open for type %d\n",
+ (hubspc_subdevice_t)(ulong)device_info_get(*devp));
+#endif /* HUBSPC_DEBUG */
+
+ return (errcode);
+}
+
+
+/* ARGSUSED */
+int
+hubspc_close(devfs_handle_t dev, int oflag, int otyp, cred_t *crp)
+{
+ int errcode = 0;
+
+ switch ((hubspc_subdevice_t)(ulong)device_info_get(dev)) {
+ case HUBSPC_REFCOUNTERS:
+ errcode = mem_refcnt_close(dev, oflag, otyp, crp);
+ break;
+
+ case HUBSPC_PROM:
+ break;
+ default:
+ errcode = ENODEV;
+ }
+
+#ifdef HUBSPC_DEBUG
+ printf("hubspc_close: Completed close for type %d\n",
+ (hubspc_subdevice_t)(ulong)device_info_get(dev));
+#endif /* HUBSPC_DEBUG */
+
+ return (errcode);
+}
+
+/* ARGSUSED */
+int
+hubspc_map(devfs_handle_t dev, vhandl_t *vt, off_t off, size_t len, uint prot)
+{
+ /*REFERENCED*/
+ hubspc_subdevice_t subdevice;
+ int errcode = 0;
+
+ /* check validity of request */
+ if( len == 0 ) {
+ return ENXIO;
+ }
+
+ subdevice = (hubspc_subdevice_t)(ulong)device_info_get(dev);
+
+#ifdef HUBSPC_DEBUG
+ printf("hubspc_map: subdevice: %d vaddr: 0x%x phyaddr: 0x%x len: 0x%x\n",
+ subdevice, v_getaddr(vt), off, len);
+#endif /* HUBSPC_DEBUG */
+
+ switch ((hubspc_subdevice_t)(ulong)device_info_get(dev)) {
+ case HUBSPC_REFCOUNTERS:
+ errcode = mem_refcnt_mmap(dev, vt, off, len, prot);
+ break;
+
+ case HUBSPC_PROM:
+ errcode = cpuprom_map(dev, vt, off, len);
+ break;
+ default:
+ errcode = ENODEV;
+ }
+
+#ifdef HUBSPC_DEBUG
+ printf("hubspc_map finished: spctype: %d vaddr: 0x%x len: 0x%x\n",
+ (hubspc_subdevice_t)(ulong)device_info_get(dev), v_getaddr(vt), len);
+#endif /* HUBSPC_DEBUG */
+
+ return errcode;
+}
+
+/* ARGSUSED */
+int
+hubspc_unmap(devfs_handle_t dev, vhandl_t *vt)
+{
+ int errcode = 0;
+
+ switch ((hubspc_subdevice_t)(ulong)device_info_get(dev)) {
+ case HUBSPC_REFCOUNTERS:
+ errcode = mem_refcnt_unmap(dev, vt);
+ break;
+
+ case HUBSPC_PROM:
+ errcode = cpuprom_unmap(dev, vt);
+ break;
+
+ default:
+ errcode = ENODEV;
+ }
+ return errcode;
+
+}
+
+/* ARGSUSED */
+int
+hubspc_ioctl(devfs_handle_t dev,
+ int cmd,
+ void *arg,
+ int mode,
+ cred_t *cred_p,
+ int *rvalp)
+{
+ int errcode = 0;
+
+ switch ((hubspc_subdevice_t)(ulong)device_info_get(dev)) {
+ case HUBSPC_REFCOUNTERS:
+ errcode = mem_refcnt_ioctl(dev, cmd, arg, mode, cred_p, rvalp);
+ break;
+
+ case HUBSPC_PROM:
+ break;
+
+ default:
+ errcode = ENODEV;
+ }
+ return errcode;
+
+}
diff --git a/arch/ia64/sn/io/invent.c b/arch/ia64/sn/io/invent.c
new file mode 100644
index 000000000..011294d84
--- /dev/null
+++ b/arch/ia64/sn/io/invent.c
@@ -0,0 +1,197 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+/*
+ * Hardware Inventory
+ *
+ * See sys/sn/invent.h for an explanation of the hardware inventory contents.
+ *
+ */
+#include <linux/types.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+
+void
+inventinit(void)
+{
+}
+
+/*
+ * For initializing/updating an inventory entry.
+ */
+void
+replace_in_inventory(
+ inventory_t *pinv, int class, int type,
+ int controller, int unit, int state)
+{
+ pinv->inv_class = class;
+ pinv->inv_type = type;
+ pinv->inv_controller = controller;
+ pinv->inv_unit = unit;
+ pinv->inv_state = state;
+}
+
+/*
+ * Inventory addition
+ *
+ * XXX NOTE: Currently must be called after dynamic memory allocator is
+ * initialized.
+ *
+ */
+void
+add_to_inventory(int class, int type, int controller, int unit, int state)
+{
+ (void)device_inventory_add((devfs_handle_t)GRAPH_VERTEX_NONE, class, type,
+ controller, unit, state);
+}
+
+
+/*
+ * Inventory retrieval
+ *
+ * These two routines are intended to prevent the caller from having to know
+ * the internal structure of the inventory table.
+ *
+ */
+inventory_t *
+get_next_inventory(invplace_t *place)
+{
+ inventory_t *pinv;
+ devfs_handle_t device = place->invplace_vhdl;
+ int rv;
+
+ while ((pinv = device_inventory_get_next(device, place)) == NULL) {
+ /*
+ * We've exhausted inventory items on the last device.
+ * Advance to next device.
+ */
+ rv = hwgraph_vertex_get_next(&device, &place->invplace_vplace);
+ if (rv != LABELCL_SUCCESS)
+ return(NULL);
+ place->invplace_vhdl = device;
+ place->invplace_inv = NULL; /* Start from beginning invent on this device */
+ }
+
+ return(pinv);
+}
+
+/* ARGSUSED */
+int
+get_sizeof_inventory(int abi)
+{
+ return sizeof(inventory_t);
+}
+
+/*
+ * Hardware inventory scanner.
+ *
+ * Calls fun() for every entry in inventory list unless fun() returns something
+ * other than 0.
+ */
+int
+scaninvent(int (*fun)(inventory_t *, void *), void *arg)
+{
+ inventory_t *ie;
+ invplace_t iplace = { NULL,NULL, NULL };
+ int rc;
+
+ ie = 0;
+ rc = 0;
+ while ( (ie = (inventory_t *)get_next_inventory(&iplace)) ) {
+ rc = (*fun)(ie, arg);
+ if (rc)
+ break;
+ }
+ return rc;
+}
+
+/*
+ * Find a particular inventory object
+ *
+ * pinv can be a pointer to an inventory entry and the search will begin from
+ * there, or it can be 0 in which case the search starts at the beginning.
+ * A -1 for any of the other arguments is a wildcard (i.e. it always matches).
+ */
+inventory_t *
+find_inventory(inventory_t *pinv, int class, int type, int controller,
+ int unit, int state)
+{
+ invplace_t iplace = { NULL,NULL, NULL };
+
+ while ((pinv = (inventory_t *)get_next_inventory(&iplace)) != NULL) {
+ if (class != -1 && pinv->inv_class != class)
+ continue;
+ if (type != -1 && pinv->inv_type != type)
+ continue;
+
+ /* XXXX - perhaps the "state" entry should be ignored so an
+ * an existing entry can be updated. See vino_init() and
+ * ml/IP22.c:add_ioboard() for an example.
+ */
+ if (state != -1 && pinv->inv_state != state)
+ continue;
+ if (controller != -1
+ && pinv->inv_controller != controller)
+ continue;
+ if (unit != -1 && pinv->inv_unit != unit)
+ continue;
+ break;
+ }
+
+ return(pinv);
+}
+
+
+/*
+** Retrieve inventory data associated with a device.
+*/
+inventory_t *
+device_inventory_get_next( devfs_handle_t device,
+ invplace_t *invplace)
+{
+ inventory_t *pinv;
+ int rv;
+
+ rv = hwgraph_inventory_get_next(device, invplace, &pinv);
+ if (rv == LABELCL_SUCCESS)
+ return(pinv);
+ else
+ return(NULL);
+}
+
+
+/*
+** Associate canonical inventory information with a device (and
+** add it to the general inventory).
+*/
+void
+device_inventory_add( devfs_handle_t device,
+ int class,
+ int type,
+ major_t controller,
+ minor_t unit,
+ int state)
+{
+ hwgraph_inventory_add(device, class, type, controller, unit, state);
+}
+
+int
+device_controller_num_get(devfs_handle_t device)
+{
+ return (hwgraph_controller_num_get(device));
+}
+
+void
+device_controller_num_set(devfs_handle_t device, int contr_num)
+{
+ hwgraph_controller_num_set(device, contr_num);
+}
diff --git a/arch/ia64/sn/io/io.c b/arch/ia64/sn/io/io.c
new file mode 100644
index 000000000..1e436baad
--- /dev/null
+++ b/arch/ia64/sn/io/io.c
@@ -0,0 +1,1311 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <asm/sn/types.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/cmn_err.h>
+#include <asm/sn/iobus.h>
+#include <asm/sn/iograph.h>
+#include <asm/param.h>
+#include <asm/sn/pio.h>
+#include <asm/sn/xtalk/xwidget.h>
+#include <asm/sn/sn_private.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/hcl_util.h>
+#include <asm/sn/agent.h>
+#include <asm/sn/intr.h>
+#include <asm/sn/xtalk/xtalkaddrs.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/io.h>
+#include <asm/sn/sn_cpuid.h>
+
+extern xtalk_provider_t hub_provider;
+
+#ifndef CONFIG_IA64_SGI_IO
+/* Global variables */
+extern pdaindr_t pdaindr[MAXCPUS];
+#endif
+
+/*
+ * Perform any initializations needed to support hub-based I/O.
+ * Called once during startup.
+ */
+void
+hubio_init(void)
+{
+#if 0
+ /* This isn't needed unless we port the entire sio driver ... */
+ extern void early_brl1_port_init( void );
+ early_brl1_port_init();
+#endif
+}
+
+/*
+ * Implementation of hub iobus operations.
+ *
+ * Hub provides a crosstalk "iobus" on IP27 systems. These routines
+ * provide a platform-specific implementation of xtalk used by all xtalk
+ * cards on IP27 systems.
+ *
+ * Called from corresponding xtalk_* routines.
+ */
+
+
+/* PIO MANAGEMENT */
+/* For mapping system virtual address space to xtalk space on a specified widget */
+
+/*
+ * Setup pio structures needed for a particular hub.
+ */
+static void
+hub_pio_init(devfs_handle_t hubv)
+{
+ xwidgetnum_t widget;
+ hubinfo_t hubinfo;
+ nasid_t nasid;
+ int bigwin;
+ hub_piomap_t hub_piomap;
+
+ hubinfo_get(hubv, &hubinfo);
+ nasid = hubinfo->h_nasid;
+
+ /* Initialize small window piomaps for this hub */
+ for (widget=0; widget <= HUB_WIDGET_ID_MAX; widget++) {
+ hub_piomap = hubinfo_swin_piomap_get(hubinfo, (int)widget);
+ hub_piomap->hpio_xtalk_info.xp_target = widget;
+ hub_piomap->hpio_xtalk_info.xp_xtalk_addr = 0;
+ hub_piomap->hpio_xtalk_info.xp_mapsz = SWIN_SIZE;
+ hub_piomap->hpio_xtalk_info.xp_kvaddr = (caddr_t)NODE_SWIN_BASE(nasid, widget);
+ hub_piomap->hpio_hub = hubv;
+ hub_piomap->hpio_flags = HUB_PIOMAP_IS_VALID;
+ }
+
+ /* Initialize big window piomaps for this hub */
+ for (bigwin=0; bigwin < HUB_NUM_BIG_WINDOW; bigwin++) {
+ hub_piomap = hubinfo_bwin_piomap_get(hubinfo, bigwin);
+ hub_piomap->hpio_xtalk_info.xp_mapsz = BWIN_SIZE;
+ hub_piomap->hpio_hub = hubv;
+ hub_piomap->hpio_holdcnt = 0;
+ hub_piomap->hpio_flags = HUB_PIOMAP_IS_BIGWINDOW;
+ IIO_ITTE_DISABLE(nasid, bigwin);
+ }
+#ifdef BRINGUP
+ hub_set_piomode(nasid, HUB_PIO_CONVEYOR);
+#else
+ /* Set all the xwidgets in fire-and-forget mode
+ * by default
+ */
+ hub_set_piomode(nasid, HUB_PIO_FIRE_N_FORGET);
+#endif /* BRINGUP */
+
+ sv_init(&hubinfo->h_bwwait, SV_FIFO, "bigwin");
+ spinlock_init(&hubinfo->h_bwlock, "bigwin");
+}
+
+/*
+ * Create a caddr_t-to-xtalk_addr mapping.
+ *
+ * Use a small window if possible (that's the usual case), but
+ * manage big windows if needed. Big window mappings can be
+ * either FIXED or UNFIXED -- we keep at least 1 big window available
+ * for UNFIXED mappings.
+ *
+ * Returns an opaque pointer-sized type which can be passed to
+ * other hub_pio_* routines on success, or NULL if the request
+ * cannot be satisfied.
+ */
+/* ARGSUSED */
+hub_piomap_t
+hub_piomap_alloc(devfs_handle_t dev, /* set up mapping for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ iopaddr_t xtalk_addr, /* map for this xtalk_addr range */
+ size_t byte_count,
+ size_t byte_count_max, /* maximum size of a mapping */
+ unsigned flags) /* defined in sys/pio.h */
+{
+ xwidget_info_t widget_info = xwidget_info_get(dev);
+ xwidgetnum_t widget = xwidget_info_id_get(widget_info);
+ devfs_handle_t hubv = xwidget_info_master_get(widget_info);
+ hubinfo_t hubinfo;
+ hub_piomap_t bw_piomap;
+ int bigwin, free_bw_index;
+ nasid_t nasid;
+ volatile hubreg_t junk;
+ int s;
+
+ /* sanity check */
+ if (byte_count_max > byte_count)
+ return(NULL);
+
+ hubinfo_get(hubv, &hubinfo);
+
+ /* If xtalk_addr range is mapped by a small window, we don't have
+ * to do much
+ */
+ if (xtalk_addr + byte_count <= SWIN_SIZE)
+ return(hubinfo_swin_piomap_get(hubinfo, (int)widget));
+
+ /* We need to use a big window mapping. */
+
+ /*
+ * TBD: Allow requests that would consume multiple big windows --
+ * split the request up and use multiple mapping entries.
+ * For now, reject requests that span big windows.
+ */
+ if ((xtalk_addr % BWIN_SIZE) + byte_count > BWIN_SIZE)
+ return(NULL);
+
+
+ /* Round xtalk address down for big window alignement */
+ xtalk_addr = xtalk_addr & ~(BWIN_SIZE-1);
+
+ /*
+ * Check to see if an existing big window mapping will suffice.
+ */
+tryagain:
+ free_bw_index = -1;
+ s = mutex_spinlock(&hubinfo->h_bwlock);
+ for (bigwin=0; bigwin < HUB_NUM_BIG_WINDOW; bigwin++) {
+ bw_piomap = hubinfo_bwin_piomap_get(hubinfo, bigwin);
+
+ /* If mapping is not valid, skip it */
+ if (!(bw_piomap->hpio_flags & HUB_PIOMAP_IS_VALID)) {
+ free_bw_index = bigwin;
+ continue;
+ }
+
+ /*
+ * If mapping is UNFIXED, skip it. We don't allow sharing
+ * of UNFIXED mappings, because this would allow starvation.
+ */
+ if (!(bw_piomap->hpio_flags & HUB_PIOMAP_IS_FIXED))
+ continue;
+
+ if ( xtalk_addr == bw_piomap->hpio_xtalk_info.xp_xtalk_addr &&
+ widget == bw_piomap->hpio_xtalk_info.xp_target) {
+ bw_piomap->hpio_holdcnt++;
+ mutex_spinunlock(&hubinfo->h_bwlock, s);
+ return(bw_piomap);
+ }
+ }
+
+ /*
+ * None of the existing big window mappings will work for us --
+ * we need to establish a new mapping.
+ */
+
+ /* Insure that we don't consume all big windows with FIXED mappings */
+ if (flags & PIOMAP_FIXED) {
+ if (hubinfo->h_num_big_window_fixed < HUB_NUM_BIG_WINDOW-1) {
+ ASSERT(free_bw_index >= 0);
+ hubinfo->h_num_big_window_fixed++;
+ } else {
+ bw_piomap = NULL;
+ goto done;
+ }
+ } else /* PIOMAP_UNFIXED */ {
+ if (free_bw_index < 0) {
+ if (flags & PIOMAP_NOSLEEP) {
+ bw_piomap = NULL;
+ goto done;
+ }
+
+ sv_wait(&hubinfo->h_bwwait, PZERO, &hubinfo->h_bwlock, s);
+ goto tryagain;
+ }
+ }
+
+
+ /* OK! Allocate big window free_bw_index for this mapping. */
+ /*
+ * The code below does a PIO write to setup an ITTE entry.
+ * We need to prevent other CPUs from seeing our updated memory
+ * shadow of the ITTE (in the piomap) until the ITTE entry is
+ * actually set up; otherwise, another CPU might attempt a PIO
+ * prematurely.
+ *
+ * Also, the only way we can know that an entry has been received
+ * by the hub and can be used by future PIO reads/writes is by
+ * reading back the ITTE entry after writing it.
+ *
+ * For these two reasons, we PIO read back the ITTE entry after
+ * we write it.
+ */
+
+ nasid = hubinfo->h_nasid;
+ IIO_ITTE_PUT(nasid, free_bw_index, HUB_PIO_MAP_TO_MEM, widget, xtalk_addr);
+ junk = HUB_L(IIO_ITTE_GET(nasid, free_bw_index));
+
+ bw_piomap = hubinfo_bwin_piomap_get(hubinfo, free_bw_index);
+ bw_piomap->hpio_xtalk_info.xp_dev = dev;
+ bw_piomap->hpio_xtalk_info.xp_target = widget;
+ bw_piomap->hpio_xtalk_info.xp_xtalk_addr = xtalk_addr;
+ bw_piomap->hpio_xtalk_info.xp_kvaddr = (caddr_t)NODE_BWIN_BASE(nasid, free_bw_index);
+ bw_piomap->hpio_holdcnt++;
+ bw_piomap->hpio_bigwin_num = free_bw_index;
+
+ if (flags & PIOMAP_FIXED)
+ bw_piomap->hpio_flags |= HUB_PIOMAP_IS_VALID | HUB_PIOMAP_IS_FIXED;
+ else
+ bw_piomap->hpio_flags |= HUB_PIOMAP_IS_VALID;
+
+done:
+ mutex_spinunlock(&hubinfo->h_bwlock, s);
+ return(bw_piomap);
+}
+
+/*
+ * hub_piomap_free destroys a caddr_t-to-xtalk pio mapping and frees
+ * any associated mapping resources.
+ *
+ * If this * piomap was handled with a small window, or if it was handled
+ * in a big window that's still in use by someone else, then there's
+ * nothing to do. On the other hand, if this mapping was handled
+ * with a big window, AND if we were the final user of that mapping,
+ * then destroy the mapping.
+ */
+void
+hub_piomap_free(hub_piomap_t hub_piomap)
+{
+ devfs_handle_t hubv;
+ hubinfo_t hubinfo;
+ nasid_t nasid;
+ int s;
+
+ /*
+ * Small windows are permanently mapped to corresponding widgets,
+ * so there're no resources to free.
+ */
+ if (!(hub_piomap->hpio_flags & HUB_PIOMAP_IS_BIGWINDOW))
+ return;
+
+ ASSERT(hub_piomap->hpio_flags & HUB_PIOMAP_IS_VALID);
+ ASSERT(hub_piomap->hpio_holdcnt > 0);
+
+ hubv = hub_piomap->hpio_hub;
+ hubinfo_get(hubv, &hubinfo);
+ nasid = hubinfo->h_nasid;
+
+ s = mutex_spinlock(&hubinfo->h_bwlock);
+
+ /*
+ * If this is the last hold on this mapping, free it.
+ */
+ if (--hub_piomap->hpio_holdcnt == 0) {
+ IIO_ITTE_DISABLE(nasid, hub_piomap->hpio_bigwin_num );
+
+ if (hub_piomap->hpio_flags & HUB_PIOMAP_IS_FIXED) {
+ hub_piomap->hpio_flags &= ~(HUB_PIOMAP_IS_VALID | HUB_PIOMAP_IS_FIXED);
+ hubinfo->h_num_big_window_fixed--;
+ ASSERT(hubinfo->h_num_big_window_fixed >= 0);
+ } else
+ hub_piomap->hpio_flags &= ~HUB_PIOMAP_IS_VALID;
+
+ (void)sv_signal(&hubinfo->h_bwwait);
+ }
+
+ mutex_spinunlock(&hubinfo->h_bwlock, s);
+}
+
+/*
+ * Establish a mapping to a given xtalk address range using the resources
+ * allocated earlier.
+ */
+caddr_t
+hub_piomap_addr(hub_piomap_t hub_piomap, /* mapping resources */
+ iopaddr_t xtalk_addr, /* map for this xtalk address */
+ size_t byte_count) /* map this many bytes */
+{
+ /* Verify that range can be mapped using the specified piomap */
+ if (xtalk_addr < hub_piomap->hpio_xtalk_info.xp_xtalk_addr)
+ return(0);
+
+ if (xtalk_addr + byte_count >
+ ( hub_piomap->hpio_xtalk_info.xp_xtalk_addr +
+ hub_piomap->hpio_xtalk_info.xp_mapsz))
+ return(0);
+
+ if (hub_piomap->hpio_flags & HUB_PIOMAP_IS_VALID)
+ return(hub_piomap->hpio_xtalk_info.xp_kvaddr +
+ (xtalk_addr % hub_piomap->hpio_xtalk_info.xp_mapsz));
+ else
+ return(0);
+}
+
+
+/*
+ * Driver indicates that it's done with PIO's from an earlier piomap_addr.
+ */
+/* ARGSUSED */
+void
+hub_piomap_done(hub_piomap_t hub_piomap) /* done with these mapping resources */
+{
+ /* Nothing to do */
+}
+
+
+/*
+ * For translations that require no mapping resources, supply a kernel virtual
+ * address that maps to the specified xtalk address range.
+ */
+/* ARGSUSED */
+caddr_t
+hub_piotrans_addr( devfs_handle_t dev, /* translate to this device */
+ device_desc_t dev_desc, /* device descriptor */
+ iopaddr_t xtalk_addr, /* Crosstalk address */
+ size_t byte_count, /* map this many bytes */
+ unsigned flags) /* (currently unused) */
+{
+ xwidget_info_t widget_info = xwidget_info_get(dev);
+ xwidgetnum_t widget = xwidget_info_id_get(widget_info);
+ devfs_handle_t hubv = xwidget_info_master_get(widget_info);
+ hub_piomap_t hub_piomap;
+ hubinfo_t hubinfo;
+
+ hubinfo_get(hubv, &hubinfo);
+
+ if (xtalk_addr + byte_count <= SWIN_SIZE) {
+ hub_piomap = hubinfo_swin_piomap_get(hubinfo, (int)widget);
+ return(hub_piomap_addr(hub_piomap, xtalk_addr, byte_count));
+ } else
+ return(0);
+}
+
+
+/* DMA MANAGEMENT */
+/* Mapping from crosstalk space to system physical space */
+
+/*
+ * There's not really very much to do here, since crosstalk maps
+ * directly to system physical space. It's quite possible that this
+ * DMA layer will be bypassed in performance kernels.
+ */
+
+
+/* ARGSUSED */
+static void
+hub_dma_init(devfs_handle_t hubv)
+{
+}
+
+
+/*
+ * Allocate resources needed to set up DMA mappings up to a specified size
+ * on a specified adapter.
+ *
+ * We don't actually use the adapter ID for anything. It's just the adapter
+ * that the lower level driver plans to use for DMA.
+ */
+/* ARGSUSED */
+hub_dmamap_t
+hub_dmamap_alloc( devfs_handle_t dev, /* set up mappings for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ size_t byte_count_max, /* max size of a mapping */
+ unsigned flags) /* defined in dma.h */
+{
+ hub_dmamap_t dmamap;
+ xwidget_info_t widget_info = xwidget_info_get(dev);
+ xwidgetnum_t widget = xwidget_info_id_get(widget_info);
+ devfs_handle_t hubv = xwidget_info_master_get(widget_info);
+
+ dmamap = kern_malloc(sizeof(struct hub_dmamap_s));
+ dmamap->hdma_xtalk_info.xd_dev = dev;
+ dmamap->hdma_xtalk_info.xd_target = widget;
+ dmamap->hdma_hub = hubv;
+ dmamap->hdma_flags = HUB_DMAMAP_IS_VALID;
+ if (flags & XTALK_FIXED)
+ dmamap->hdma_flags |= HUB_DMAMAP_IS_FIXED;
+
+ return(dmamap);
+}
+
+/*
+ * Destroy a DMA mapping from crosstalk space to system address space.
+ * There is no actual mapping hardware to destroy, but we at least mark
+ * the dmamap INVALID and free the space that it took.
+ */
+void
+hub_dmamap_free(hub_dmamap_t hub_dmamap)
+{
+ hub_dmamap->hdma_flags &= ~HUB_DMAMAP_IS_VALID;
+ kern_free(hub_dmamap);
+}
+
+/*
+ * Establish a DMA mapping using the resources allocated in a previous dmamap_alloc.
+ * Return an appropriate crosstalk address range that maps to the specified physical
+ * address range.
+ */
+/* ARGSUSED */
+extern iopaddr_t
+hub_dmamap_addr( hub_dmamap_t dmamap, /* use these mapping resources */
+ paddr_t paddr, /* map for this address */
+ size_t byte_count) /* map this many bytes */
+{
+ devfs_handle_t vhdl;
+
+ ASSERT(dmamap->hdma_flags & HUB_DMAMAP_IS_VALID);
+
+ if (dmamap->hdma_flags & HUB_DMAMAP_USED) {
+ /* If the map is FIXED, re-use is OK. */
+ if (!(dmamap->hdma_flags & HUB_DMAMAP_IS_FIXED)) {
+ vhdl = dmamap->hdma_xtalk_info.xd_dev;
+#if defined(SUPPORT_PRINTING_V_FORMAT)
+ cmn_err(CE_WARN, "%v: hub_dmamap_addr re-uses dmamap.\n",vhdl);
+#else
+ cmn_err(CE_WARN, "0x%p: hub_dmamap_addr re-uses dmamap.\n", &vhdl);
+#endif
+ }
+ } else {
+ dmamap->hdma_flags |= HUB_DMAMAP_USED;
+ }
+
+ /* There isn't actually any DMA mapping hardware on the hub. */
+ return(paddr);
+}
+
+/*
+ * Establish a DMA mapping using the resources allocated in a previous dmamap_alloc.
+ * Return an appropriate crosstalk address list that maps to the specified physical
+ * address list.
+ */
+/* ARGSUSED */
+alenlist_t
+hub_dmamap_list(hub_dmamap_t hub_dmamap, /* use these mapping resources */
+ alenlist_t palenlist, /* map this area of memory */
+ unsigned flags)
+{
+ devfs_handle_t vhdl;
+
+ ASSERT(hub_dmamap->hdma_flags & HUB_DMAMAP_IS_VALID);
+
+ if (hub_dmamap->hdma_flags & HUB_DMAMAP_USED) {
+ /* If the map is FIXED, re-use is OK. */
+ if (!(hub_dmamap->hdma_flags & HUB_DMAMAP_IS_FIXED)) {
+ vhdl = hub_dmamap->hdma_xtalk_info.xd_dev;
+#if defined(SUPPORT_PRINTING_V_FORMAT)
+ cmn_err(CE_WARN,"%v: hub_dmamap_list re-uses dmamap\n",vhdl);
+#else
+ cmn_err(CE_WARN,"0x%p: hub_dmamap_list re-uses dmamap\n", &vhdl);
+#endif
+ }
+ } else {
+ hub_dmamap->hdma_flags |= HUB_DMAMAP_USED;
+ }
+
+ /* There isn't actually any DMA mapping hardware on the hub. */
+ return(palenlist);
+}
+
+/*
+ * Driver indicates that it has completed whatever DMA it may have started
+ * after an earlier dmamap_addr or dmamap_list call.
+ */
+void
+hub_dmamap_done(hub_dmamap_t hub_dmamap) /* done with these mapping resources */
+{
+ devfs_handle_t vhdl;
+
+ if (hub_dmamap->hdma_flags & HUB_DMAMAP_USED) {
+ hub_dmamap->hdma_flags &= ~HUB_DMAMAP_USED;
+ } else {
+ /* If the map is FIXED, re-done is OK. */
+ if (!(hub_dmamap->hdma_flags & HUB_DMAMAP_IS_FIXED)) {
+ vhdl = hub_dmamap->hdma_xtalk_info.xd_dev;
+#if defined(SUPPORT_PRINTING_V_FORMAT)
+ cmn_err(CE_WARN, "%v: hub_dmamap_done already done with dmamap\n",vhdl);
+#else
+ cmn_err(CE_WARN, "0x%p: hub_dmamap_done already done with dmamap\n", &vhdl);
+#endif
+ }
+ }
+}
+
+/*
+ * Translate a single system physical address into a crosstalk address.
+ */
+/* ARGSUSED */
+iopaddr_t
+hub_dmatrans_addr( devfs_handle_t dev, /* translate for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ paddr_t paddr, /* system physical address */
+ size_t byte_count, /* length */
+ unsigned flags) /* defined in dma.h */
+{
+ /* no translation needed */
+ return(paddr);
+}
+
+/*
+ * Translate a list of IP27 addresses and lengths into a list of crosstalk
+ * addresses and lengths. No actual hardware mapping takes place; the hub
+ * has no DMA mapping registers -- crosstalk addresses map directly.
+ */
+/* ARGSUSED */
+alenlist_t
+hub_dmatrans_list( devfs_handle_t dev, /* translate for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ alenlist_t palenlist, /* system address/length list */
+ unsigned flags) /* defined in dma.h */
+{
+ /* no translation needed */
+ return(palenlist);
+}
+
+/*ARGSUSED*/
+void
+hub_dmamap_drain( hub_dmamap_t map)
+{
+ /* XXX- flush caches, if cache coherency WAR is needed */
+}
+
+/*ARGSUSED*/
+void
+hub_dmaaddr_drain( devfs_handle_t vhdl,
+ paddr_t addr,
+ size_t bytes)
+{
+ /* XXX- flush caches, if cache coherency WAR is needed */
+}
+
+/*ARGSUSED*/
+void
+hub_dmalist_drain( devfs_handle_t vhdl,
+ alenlist_t list)
+{
+ /* XXX- flush caches, if cache coherency WAR is needed */
+}
+
+
+
+/* INTERRUPT MANAGEMENT */
+
+/* ARGSUSED */
+static void
+hub_intr_init(devfs_handle_t hubv)
+{
+}
+
+/*
+ * hub_device_desc_update
+ * Update the passed in device descriptor with the actual the
+ * target cpu number and interrupt priority level.
+ * NOTE : These might be the same as the ones passed in thru
+ * the descriptor.
+ */
+static void
+hub_device_desc_update(device_desc_t dev_desc,
+ ilvl_t intr_swlevel,
+ cpuid_t cpu)
+{
+ char cpuname[40];
+
+ /* Store the interrupt priority level in the device descriptor */
+ device_desc_intr_swlevel_set(dev_desc, intr_swlevel);
+
+ /* Convert the cpuid to the vertex handle in the hwgraph and
+ * save it in the device descriptor.
+ */
+ sprintf(cpuname,"/hw/cpunum/%ld",cpu);
+ device_desc_intr_target_set(dev_desc,
+ hwgraph_path_to_dev(cpuname));
+}
+
+int allocate_my_bit = INTRCONNECT_ANYBIT;
+
+/*
+ * Allocate resources required for an interrupt as specified in dev_desc.
+ * Returns a hub interrupt handle on success, or 0 on failure.
+ */
+hub_intr_t
+hub_intr_alloc( devfs_handle_t dev, /* which crosstalk device */
+ device_desc_t dev_desc, /* device descriptor */
+ devfs_handle_t owner_dev) /* owner of this interrupt, if known */
+{
+ cpuid_t cpu; /* cpu to receive interrupt */
+ int cpupicked = 0;
+ int bit; /* interrupt vector */
+ /*REFERENCED*/
+ int intr_resflags;
+ hub_intr_t intr_hdl;
+ cnodeid_t nodeid; /* node to receive interrupt */
+ /*REFERENCED*/
+ nasid_t nasid; /* nasid to receive interrupt */
+ struct xtalk_intr_s *xtalk_info;
+ iopaddr_t xtalk_addr; /* xtalk addr on hub to set intr */
+ xwidget_info_t xwidget_info; /* standard crosstalk widget info handle */
+ char *intr_name = NULL;
+ ilvl_t intr_swlevel;
+ extern int default_intr_pri;
+#ifdef CONFIG_IA64_SGI_SN1
+ extern void synergy_intr_alloc(int, int);
+#endif
+
+ /*
+ * If caller didn't explicily specify a device descriptor, see if there's
+ * a default descriptor associated with the device.
+ */
+ if (!dev_desc)
+ dev_desc = device_desc_default_get(dev);
+
+ if (dev_desc) {
+ intr_name = device_desc_intr_name_get(dev_desc);
+ intr_swlevel = device_desc_intr_swlevel_get(dev_desc);
+ if (dev_desc->flags & D_INTR_ISERR) {
+ intr_resflags = II_ERRORINT;
+ } else if (!(dev_desc->flags & D_INTR_NOTHREAD)) {
+ intr_resflags = II_THREADED;
+ } else {
+ /* Neither an error nor a thread. */
+ intr_resflags = 0;
+ }
+ } else {
+ intr_swlevel = default_intr_pri;
+ intr_resflags = II_THREADED;
+ }
+
+ /* XXX - Need to determine if the interrupt should be threaded. */
+
+ /* If the cpu has not been picked already then choose a candidate
+ * interrupt target and reserve the interrupt bit
+ */
+#if defined(NEW_INTERRUPTS)
+ if (!cpupicked) {
+ cpu = intr_heuristic(dev,dev_desc,allocate_my_bit,
+ intr_resflags,owner_dev,
+ intr_name,&bit);
+ }
+#endif
+
+ /* At this point we SHOULD have a valid cpu */
+ if (cpu == CPU_NONE) {
+#if defined(SUPPORT_PRINTING_V_FORMAT)
+ cmn_err(CE_WARN,
+ "%v hub_intr_alloc could not allocate interrupt\n",
+ owner_dev);
+#else
+ cmn_err(CE_WARN,
+ "0x%p hub_intr_alloc could not allocate interrupt\n",
+ &owner_dev);
+#endif
+ return(0);
+
+ }
+
+ /* If the cpu has been picked already (due to the bridge data
+ * corruption bug) then try to reserve an interrupt bit .
+ */
+#if defined(NEW_INTERRUPTS)
+ if (cpupicked) {
+ bit = intr_reserve_level(cpu, allocate_my_bit,
+ intr_resflags,
+ owner_dev, intr_name);
+ if (bit < 0) {
+#if defined(SUPPORT_PRINTING_V_FORMAT)
+ cmn_err(CE_WARN,
+ "Could not reserve an interrupt bit for cpu "
+ " %d and dev %v\n",
+ cpu,owner_dev);
+#else
+ cmn_err(CE_WARN,
+ "Could not reserve an interrupt bit for cpu "
+ " %d and dev 0x%x\n",
+ cpu, &owner_dev);
+#endif
+
+ return(0);
+ }
+ }
+#endif /* NEW_INTERRUPTS */
+
+ nodeid = cpuid_to_cnodeid(cpu);
+ nasid = cpuid_to_nasid(cpu);
+ xtalk_addr = HUBREG_AS_XTALKADDR(nasid, PIREG(PI_INT_PEND_MOD, cpuid_to_subnode(cpu)));
+
+ /*
+ * Allocate an interrupt handle, and fill it in. There are two
+ * pieces to an interrupt handle: the piece needed by generic
+ * xtalk code which is used by crosstalk device drivers, and
+ * the piece needed by low-level IP27 hardware code.
+ */
+ intr_hdl = kmem_alloc_node(sizeof(struct hub_intr_s), KM_NOSLEEP, nodeid);
+ ASSERT_ALWAYS(intr_hdl);
+
+ /*
+ * Fill in xtalk information for generic xtalk interfaces that
+ * operate on xtalk_intr_hdl's.
+ */
+ xtalk_info = &intr_hdl->i_xtalk_info;
+ xtalk_info->xi_dev = dev;
+ xtalk_info->xi_vector = bit;
+ xtalk_info->xi_addr = xtalk_addr;
+ xtalk_info->xi_flags = (intr_resflags == II_THREADED) ?
+ 0 : XTALK_INTR_NOTHREAD;
+
+ /*
+ * Regardless of which CPU we ultimately interrupt, a given crosstalk
+ * widget always handles interrupts (and PIO and DMA) through its
+ * designated "master" crosstalk provider.
+ */
+ xwidget_info = xwidget_info_get(dev);
+ if (xwidget_info)
+ xtalk_info->xi_target = xwidget_info_masterid_get(xwidget_info);
+
+ /* Fill in low level hub information for hub_* interrupt interface */
+ intr_hdl->i_swlevel = intr_swlevel;
+ intr_hdl->i_cpuid = cpu;
+ intr_hdl->i_bit = bit;
+ intr_hdl->i_flags = HUB_INTR_IS_ALLOCED;
+
+ /* Store the actual interrupt priority level & interrupt target
+ * cpu back in the device descriptor.
+ */
+ hub_device_desc_update(dev_desc, intr_swlevel, cpu);
+#ifdef CONFIG_IA64_SGI_SN1
+ synergy_intr_alloc((int)bit, (int)cpu);
+#endif
+ return(intr_hdl);
+}
+
+
+/*
+ * Free resources consumed by intr_alloc.
+ */
+void
+hub_intr_free(hub_intr_t intr_hdl)
+{
+ cpuid_t cpu = intr_hdl->i_cpuid;
+ int bit = intr_hdl->i_bit;
+ xtalk_intr_t xtalk_info;
+
+ if (intr_hdl->i_flags & HUB_INTR_IS_CONNECTED) {
+ /* Setting the following fields in the xtalk interrupt info
+ * clears the interrupt target register in the xtalk user
+ */
+ xtalk_info = &intr_hdl->i_xtalk_info;
+ xtalk_info->xi_dev = NODEV;
+ xtalk_info->xi_vector = 0;
+ xtalk_info->xi_addr = 0;
+ hub_intr_disconnect(intr_hdl);
+ }
+
+ if (intr_hdl->i_flags & HUB_INTR_IS_ALLOCED)
+ kfree(intr_hdl);
+
+#if defined(NEW_INTERRUPTS)
+ intr_unreserve_level(cpu, bit);
+#endif
+}
+
+
+/*
+ * Associate resources allocated with a previous hub_intr_alloc call with the
+ * described handler, arg, name, etc.
+ */
+/*ARGSUSED*/
+int
+hub_intr_connect( hub_intr_t intr_hdl, /* xtalk intr resource handle */
+ intr_func_t intr_func, /* xtalk intr handler */
+ void *intr_arg, /* arg to intr handler */
+ xtalk_intr_setfunc_t setfunc, /* func to set intr hw */
+ void *setfunc_arg, /* arg to setfunc */
+ void *thread) /* intr thread to use */
+{
+ int rv;
+ cpuid_t cpu = intr_hdl->i_cpuid;
+ int bit = intr_hdl->i_bit;
+#ifdef CONFIG_IA64_SGI_SN1
+ extern int synergy_intr_connect(int, int);
+#endif
+
+ ASSERT(intr_hdl->i_flags & HUB_INTR_IS_ALLOCED);
+
+#if defined(NEW_INTERRUPTS)
+ rv = intr_connect_level(cpu, bit, intr_hdl->i_swlevel,
+ intr_func, intr_arg, NULL);
+ if (rv < 0)
+ return(rv);
+
+#endif
+ intr_hdl->i_xtalk_info.xi_setfunc = setfunc;
+ intr_hdl->i_xtalk_info.xi_sfarg = setfunc_arg;
+
+ if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl);
+
+ intr_hdl->i_flags |= HUB_INTR_IS_CONNECTED;
+#ifdef CONFIG_IA64_SGI_SN1
+ return(synergy_intr_connect((int)bit, (int)cpu));
+#endif
+}
+
+
+/*
+ * Disassociate handler with the specified interrupt.
+ */
+void
+hub_intr_disconnect(hub_intr_t intr_hdl)
+{
+ /*REFERENCED*/
+ int rv;
+ cpuid_t cpu = intr_hdl->i_cpuid;
+ int bit = intr_hdl->i_bit;
+ xtalk_intr_setfunc_t setfunc;
+
+ setfunc = intr_hdl->i_xtalk_info.xi_setfunc;
+
+ /* TBD: send disconnected interrupts somewhere harmless */
+ if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl);
+
+#if defined(NEW_INTERRUPTS)
+ rv = intr_disconnect_level(cpu, bit);
+ ASSERT(rv == 0);
+#endif
+
+ intr_hdl->i_flags &= ~HUB_INTR_IS_CONNECTED;
+}
+
+
+/*
+ * Return a hwgraph vertex that represents the CPU currently
+ * targeted by an interrupt.
+ */
+devfs_handle_t
+hub_intr_cpu_get(hub_intr_t intr_hdl)
+{
+ cpuid_t cpuid = intr_hdl->i_cpuid;
+ ASSERT(cpuid != CPU_NONE);
+
+ return(cpuid_to_vertex(cpuid));
+}
+
+
+
+/* CONFIGURATION MANAGEMENT */
+
+/*
+ * Perform initializations that allow this hub to start crosstalk support.
+ */
+void
+hub_provider_startup(devfs_handle_t hubv)
+{
+ hub_pio_init(hubv);
+ hub_dma_init(hubv);
+ hub_intr_init(hubv);
+}
+
+/*
+ * Shutdown crosstalk support from a hub.
+ */
+void
+hub_provider_shutdown(devfs_handle_t hub)
+{
+ /* TBD */
+ xtalk_provider_unregister(hub);
+}
+
+/*
+ * Check that an address is in teh real small window widget 0 space
+ * or else in the big window we're using to emulate small window 0
+ * in the kernel.
+ */
+int
+hub_check_is_widget0(void *addr)
+{
+ nasid_t nasid = NASID_GET(addr);
+
+ if (((__psunsigned_t)addr >= RAW_NODE_SWIN_BASE(nasid, 0)) &&
+ ((__psunsigned_t)addr < RAW_NODE_SWIN_BASE(nasid, 1)))
+ return 1;
+ return 0;
+}
+
+
+/*
+ * Check that two addresses use the same widget
+ */
+int
+hub_check_window_equiv(void *addra, void *addrb)
+{
+ if (hub_check_is_widget0(addra) && hub_check_is_widget0(addrb))
+ return 1;
+
+ /* XXX - Assume this is really a small window address */
+ if (WIDGETID_GET((__psunsigned_t)addra) ==
+ WIDGETID_GET((__psunsigned_t)addrb))
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * Determine whether two PCI addresses actually refer to the same device.
+ * This only works if both addresses are in small windows. It's used to
+ * determine whether prom addresses refer to particular PCI devices.
+ */
+/*
+ * XXX - This won't work as written if we ever have more than two nodes
+ * on a crossbow. In that case, we'll need an array or partners.
+ */
+int
+hub_check_pci_equiv(void *addra, void *addrb)
+{
+ nasid_t nasida, nasidb;
+
+ /*
+ * This is for a permanent workaround that causes us to use a
+ * big window in place of small window 0.
+ */
+ if (!hub_check_window_equiv(addra, addrb))
+ return 0;
+
+ /* If the offsets aren't the same, forget it. */
+ if (SWIN_WIDGETADDR((__psunsigned_t)addra) !=
+ (SWIN_WIDGETADDR((__psunsigned_t)addrb)))
+ return 0;
+
+ /* Now, check the nasids */
+ nasida = NASID_GET(addra);
+ nasidb = NASID_GET(addrb);
+
+ ASSERT(NASID_TO_COMPACT_NODEID(nasida) != INVALID_NASID);
+ ASSERT(NASID_TO_COMPACT_NODEID(nasidb) != INVALID_NASID);
+
+ /*
+ * Either the NASIDs must be the same or they must be crossbow
+ * partners (on the same crossbow).
+ */
+ return (check_nasid_equiv(nasida, nasidb));
+}
+
+/*
+ * hub_setup_prb(nasid, prbnum, credits, conveyor)
+ *
+ * Put a PRB into fire-and-forget mode if conveyor isn't set. Otehrwise,
+ * put it into conveyor belt mode with the specified number of credits.
+ */
+void
+hub_setup_prb(nasid_t nasid, int prbnum, int credits, int conveyor)
+{
+ iprb_t prb;
+ int prb_offset;
+#ifdef IRIX
+ extern int force_fire_and_forget;
+ extern volatile int ignore_conveyor_override;
+
+ if (force_fire_and_forget && !ignore_conveyor_override)
+ if (conveyor == HUB_PIO_CONVEYOR)
+ conveyor = HUB_PIO_FIRE_N_FORGET;
+#endif
+
+ /*
+ * Get the current register value.
+ */
+ prb_offset = IIO_IOPRB(prbnum);
+ prb.iprb_regval = REMOTE_HUB_L(nasid, prb_offset);
+
+ /*
+ * Clear out some fields.
+ */
+ prb.iprb_ovflow = 1;
+ prb.iprb_bnakctr = 0;
+ prb.iprb_anakctr = 0;
+
+ /*
+ * Enable or disable fire-and-forget mode.
+ */
+ prb.iprb_ff = ((conveyor == HUB_PIO_CONVEYOR) ? 0 : 1);
+
+ /*
+ * Set the appropriate number of PIO cresits for the widget.
+ */
+ prb.iprb_xtalkctr = credits;
+
+ /*
+ * Store the new value to the register.
+ */
+ REMOTE_HUB_S(nasid, prb_offset, prb.iprb_regval);
+}
+
+/*
+ * hub_set_piomode()
+ *
+ * Put the hub into either "PIO conveyor belt" mode or "fire-and-forget"
+ * mode. To do this, we have to make absolutely sure that no PIOs
+ * are in progress so we turn off access to all widgets for the duration
+ * of the function.
+ *
+ * XXX - This code should really check what kind of widget we're talking
+ * to. Bridges can only handle three requests, but XG will do more.
+ * How many can crossbow handle to widget 0? We're assuming 1.
+ *
+ * XXX - There is a bug in the crossbow that link reset PIOs do not
+ * return write responses. The easiest solution to this problem is to
+ * leave widget 0 (xbow) in fire-and-forget mode at all times. This
+ * only affects pio's to xbow registers, which should be rare.
+ */
+void
+hub_set_piomode(nasid_t nasid, int conveyor)
+{
+ hubreg_t ii_iowa;
+ int direct_connect;
+ hubii_wcr_t ii_wcr;
+ int prbnum;
+ int s, cons_lock = 0;
+
+ ASSERT(NASID_TO_COMPACT_NODEID(nasid) != INVALID_CNODEID);
+ if (nasid == get_console_nasid()) {
+ PUTBUF_LOCK(s);
+ cons_lock = 1;
+ }
+
+ ii_iowa = REMOTE_HUB_L(nasid, IIO_OUTWIDGET_ACCESS);
+ REMOTE_HUB_S(nasid, IIO_OUTWIDGET_ACCESS, 0);
+
+ ii_wcr.wcr_reg_value = REMOTE_HUB_L(nasid, IIO_WCR);
+ direct_connect = ii_wcr.iwcr_dir_con;
+
+ if (direct_connect) {
+ /*
+ * Assume a bridge here.
+ */
+ hub_setup_prb(nasid, 0, 3, conveyor);
+ } else {
+ /*
+ * Assume a crossbow here.
+ */
+ hub_setup_prb(nasid, 0, 1, conveyor);
+ }
+
+ for (prbnum = HUB_WIDGET_ID_MIN; prbnum <= HUB_WIDGET_ID_MAX; prbnum++) {
+ /*
+ * XXX - Here's where we should take the widget type into
+ * when account assigning credits.
+ */
+ /* Always set the PRBs in fire-and-forget mode */
+ hub_setup_prb(nasid, prbnum, 3, conveyor);
+ }
+
+#ifdef IRIX
+ /*
+ * In direct connect mode, disable access to all widgets but 0.
+ * Later, the prom will do this for us.
+ */
+ if (direct_connect)
+ ii_iowa = 1;
+#endif
+
+ REMOTE_HUB_S(nasid, IIO_OUTWIDGET_ACCESS, ii_iowa);
+
+ if (cons_lock)
+ PUTBUF_UNLOCK(s);
+}
+/* Interface to allow special drivers to set hub specific
+ * device flags.
+ * Return 0 on failure , 1 on success
+ */
+int
+hub_widget_flags_set(nasid_t nasid,
+ xwidgetnum_t widget_num,
+ hub_widget_flags_t flags)
+{
+
+ ASSERT((flags & HUB_WIDGET_FLAGS) == flags);
+
+ if (flags & HUB_PIO_CONVEYOR) {
+ hub_setup_prb(nasid,widget_num,
+ 3,HUB_PIO_CONVEYOR); /* set the PRB in conveyor
+ * belt mode with 3 credits
+ */
+ } else if (flags & HUB_PIO_FIRE_N_FORGET) {
+ hub_setup_prb(nasid,widget_num,
+ 3,HUB_PIO_FIRE_N_FORGET); /* set the PRB in fire
+ * and forget mode
+ */
+ }
+
+ return 1;
+}
+/* Interface to allow special drivers to set hub specific
+ * device flags.
+ * Return 0 on failure , 1 on success
+ */
+int
+hub_device_flags_set(devfs_handle_t widget_vhdl,
+ hub_widget_flags_t flags)
+{
+ xwidget_info_t widget_info = xwidget_info_get(widget_vhdl);
+ xwidgetnum_t widget_num = xwidget_info_id_get(widget_info);
+ devfs_handle_t hub_vhdl = xwidget_info_master_get(widget_info);
+ hubinfo_t hub_info = 0;
+ nasid_t nasid;
+ int s,rv;
+
+ /* Use the nasid from the hub info hanging off the hub vertex
+ * and widget number from the widget vertex
+ */
+ hubinfo_get(hub_vhdl, &hub_info);
+ /* Being over cautious by grabbing a lock */
+ s = mutex_spinlock(&hub_info->h_bwlock);
+ nasid = hub_info->h_nasid;
+ rv = hub_widget_flags_set(nasid,widget_num,flags);
+ mutex_spinunlock(&hub_info->h_bwlock, s);
+
+ return rv;
+}
+
+#if ((defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)) && defined(BRINGUP))
+/* BRINGUP: This ought to be useful for IP27 too but, for now,
+ * make it SN1 only because `ii_ixtt_u_t' is not in IP27/hubio.h
+ * (or anywhere else :-).
+ */
+int
+hubii_ixtt_set(devfs_handle_t widget_vhdl, ii_ixtt_u_t *ixtt)
+{
+ xwidget_info_t widget_info = xwidget_info_get(widget_vhdl);
+ devfs_handle_t hub_vhdl = xwidget_info_master_get(widget_info);
+ hubinfo_t hub_info = 0;
+ nasid_t nasid;
+ int s;
+
+ /* Use the nasid from the hub info hanging off the hub vertex
+ * and widget number from the widget vertex
+ */
+ hubinfo_get(hub_vhdl, &hub_info);
+ /* Being over cautious by grabbing a lock */
+ s = mutex_spinlock(&hub_info->h_bwlock);
+ nasid = hub_info->h_nasid;
+
+ REMOTE_HUB_S(nasid, IIO_IXTT, ixtt->ii_ixtt_regval);
+
+ mutex_spinunlock(&hub_info->h_bwlock, s);
+ return 0;
+}
+
+int
+hubii_ixtt_get(devfs_handle_t widget_vhdl, ii_ixtt_u_t *ixtt)
+{
+ xwidget_info_t widget_info = xwidget_info_get(widget_vhdl);
+ devfs_handle_t hub_vhdl = xwidget_info_master_get(widget_info);
+ hubinfo_t hub_info = 0;
+ nasid_t nasid;
+ int s;
+
+ /* Use the nasid from the hub info hanging off the hub vertex
+ * and widget number from the widget vertex
+ */
+ hubinfo_get(hub_vhdl, &hub_info);
+ /* Being over cautious by grabbing a lock */
+ s = mutex_spinlock(&hub_info->h_bwlock);
+ nasid = hub_info->h_nasid;
+
+ ixtt->ii_ixtt_regval = REMOTE_HUB_L(nasid, IIO_IXTT);
+
+ mutex_spinunlock(&hub_info->h_bwlock, s);
+ return 0;
+}
+#endif /* CONFIG_IA64_SGI_SN1 */
+
+/*
+ * hub_device_inquiry
+ * Find out the xtalk widget related information stored in this
+ * hub's II.
+ */
+void
+hub_device_inquiry(devfs_handle_t xbus_vhdl, xwidgetnum_t widget)
+{
+ devfs_handle_t xconn, hub_vhdl;
+ char widget_name[8];
+ hubreg_t ii_iidem,ii_iiwa, ii_iowa;
+ hubinfo_t hubinfo;
+ nasid_t nasid;
+ int d;
+
+ sprintf(widget_name, "%d", widget);
+ if (hwgraph_traverse(xbus_vhdl, widget_name, &xconn)
+ != GRAPH_SUCCESS)
+ return;
+
+ hub_vhdl = device_master_get(xconn);
+ if (hub_vhdl == GRAPH_VERTEX_NONE)
+ return;
+
+ hubinfo_get(hub_vhdl, &hubinfo);
+ if (!hubinfo)
+ return;
+
+ nasid = hubinfo->h_nasid;
+
+ ii_iidem = REMOTE_HUB_L(nasid, IIO_IIDEM);
+ ii_iiwa = REMOTE_HUB_L(nasid, IIO_IIWA);
+ ii_iowa = REMOTE_HUB_L(nasid, IIO_IOWA);
+
+#if defined(SUPPORT_PRINTING_V_FORMAT)
+ cmn_err(CE_CONT, "Inquiry Info for %v\n", xconn);
+#else
+ cmn_err(CE_CONT, "Inquiry Info for 0x%p\n", &xconn);
+#endif
+
+ cmn_err(CE_CONT,"\tDevices shutdown [ ");
+
+ for (d = 0 ; d <= 7 ; d++)
+ if (!(ii_iidem & (IIO_IIDEM_WIDGETDEV_MASK(widget,d))))
+ cmn_err(CE_CONT, " %d", d);
+
+ cmn_err(CE_CONT,"]\n");
+
+ cmn_err(CE_CONT,
+ "\tInbound access ? %s\n",
+ ii_iiwa & IIO_IIWA_WIDGET(widget) ? "yes" : "no");
+
+ cmn_err(CE_CONT,
+ "\tOutbound access ? %s\n",
+ ii_iowa & IIO_IOWA_WIDGET(widget) ? "yes" : "no");
+
+}
+
+/*
+ * A pointer to this structure hangs off of every hub hwgraph vertex.
+ * The generic xtalk layer may indirect through it to get to this specific
+ * crosstalk bus provider.
+ */
+xtalk_provider_t hub_provider = {
+ (xtalk_piomap_alloc_f *) hub_piomap_alloc,
+ (xtalk_piomap_free_f *) hub_piomap_free,
+ (xtalk_piomap_addr_f *) hub_piomap_addr,
+ (xtalk_piomap_done_f *) hub_piomap_done,
+ (xtalk_piotrans_addr_f *) hub_piotrans_addr,
+
+ (xtalk_dmamap_alloc_f *) hub_dmamap_alloc,
+ (xtalk_dmamap_free_f *) hub_dmamap_free,
+ (xtalk_dmamap_addr_f *) hub_dmamap_addr,
+ (xtalk_dmamap_list_f *) hub_dmamap_list,
+ (xtalk_dmamap_done_f *) hub_dmamap_done,
+ (xtalk_dmatrans_addr_f *) hub_dmatrans_addr,
+ (xtalk_dmatrans_list_f *) hub_dmatrans_list,
+ (xtalk_dmamap_drain_f *) hub_dmamap_drain,
+ (xtalk_dmaaddr_drain_f *) hub_dmaaddr_drain,
+ (xtalk_dmalist_drain_f *) hub_dmalist_drain,
+
+ (xtalk_intr_alloc_f *) hub_intr_alloc,
+ (xtalk_intr_free_f *) hub_intr_free,
+ (xtalk_intr_connect_f *) hub_intr_connect,
+ (xtalk_intr_disconnect_f *) hub_intr_disconnect,
+ (xtalk_intr_cpu_get_f *) hub_intr_cpu_get,
+
+ (xtalk_provider_startup_f *) hub_provider_startup,
+ (xtalk_provider_shutdown_f *) hub_provider_shutdown,
+};
+
diff --git a/arch/ia64/sn/io/ip37.c b/arch/ia64/sn/io/ip37.c
new file mode 100644
index 000000000..38ea6993b
--- /dev/null
+++ b/arch/ia64/sn/io/ip37.c
@@ -0,0 +1,127 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+/*
+ * ip37.c
+ * Support for IP35/IP37 machines
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+#include <asm/sn/sgi.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/sn1/hubdev.h>
+#include <asm/sn/pci/bridge.h> /* for bridge_t */
+
+
+xwidgetnum_t
+hub_widget_id(nasid_t nasid)
+{
+ hubii_wcr_t ii_wcr; /* the control status register */
+
+ ii_wcr.wcr_reg_value = REMOTE_HUB_L(nasid,IIO_WCR);
+
+ printk("hub_widget_id: Found Hub Widget ID 0x%x from Register 0x%p\n", ii_wcr.wcr_fields_s.wcr_widget_id, REMOTE_HUB_ADDR(nasid, IIO_WCR));
+
+ printk("hub_widget_id: Found Hub Widget 0x%lx wcr_reg_value 0x%lx\n", REMOTE_HUB_L(nasid,IIO_WCR), ii_wcr.wcr_reg_value);
+
+ return ii_wcr.wcr_fields_s.wcr_widget_id;
+}
+
+/*
+ * get_nasid() returns the physical node id number of the caller.
+ */
+nasid_t
+get_nasid(void)
+{
+ return (nasid_t)((LOCAL_HUB_L(LB_REV_ID) & LRI_NODEID_MASK) >> LRI_NODEID_SHFT);
+}
+
+int
+get_slice(void)
+{
+ return LOCAL_HUB_L(PI_CPU_NUM);
+}
+
+int
+is_fine_dirmode(void)
+{
+ return (((LOCAL_HUB_L(LB_REV_ID) & LRI_SYSTEM_SIZE_MASK)
+ >> LRI_SYSTEM_SIZE_SHFT) == SYSTEM_SIZE_SMALL);
+
+}
+
+hubreg_t
+get_hub_chiprev(nasid_t nasid)
+{
+
+ printk("get_hub_chiprev: Hub Chip Rev 0x%lx\n",
+ (REMOTE_HUB_L(nasid, LB_REV_ID) & LRI_REV_MASK) >> LRI_REV_SHFT);
+ return ((REMOTE_HUB_L(nasid, LB_REV_ID) & LRI_REV_MASK)
+ >> LRI_REV_SHFT);
+}
+
+int
+verify_snchip_rev(void)
+{
+ int hub_chip_rev;
+ int i;
+ static int min_hub_rev = 0;
+ nasid_t nasid;
+ static int first_time = 1;
+ extern int maxnodes;
+
+
+ if (first_time) {
+ for (i = 0; i < maxnodes; i++) {
+ nasid = COMPACT_TO_NASID_NODEID(i);
+ hub_chip_rev = get_hub_chiprev(nasid);
+
+ if ((hub_chip_rev < min_hub_rev) || (i == 0))
+ min_hub_rev = hub_chip_rev;
+ }
+
+
+ first_time = 0;
+ }
+
+ return min_hub_rev;
+
+}
+
+#ifdef SN1_USE_POISON_BITS
+int
+hub_bte_poison_ok(void)
+{
+ /*
+ * For now, assume poisoning is ok. If it turns out there are chip
+ * bugs that prevent its use in early revs, there is some neat code
+ * to steal from the IP27 equivalent of this code.
+ */
+
+#ifdef BRINGUP /* temp disable BTE poisoning - might be sw bugs in this area */
+ return 0;
+#else
+ return 1;
+#endif
+}
+#endif /* SN1_USE_POISON_BITS */
+
+
+void
+ni_reset_port(void)
+{
+ LOCAL_HUB_S(NI_RESET_ENABLE, NRE_RESETOK);
+ LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET);
+}
+
+#endif /* CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 */
diff --git a/arch/ia64/sn/io/klconflib.c b/arch/ia64/sn/io/klconflib.c
new file mode 100644
index 000000000..6fd745a0b
--- /dev/null
+++ b/arch/ia64/sn/io/klconflib.c
@@ -0,0 +1,1334 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/ctype.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+
+#include <asm/sn/agent.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/nodepda.h>
+#include <asm/sn/module.h>
+#include <asm/sn/router.h>
+#include <asm/sn/xtalk/xbow.h>
+
+#define printf printk
+int hasmetarouter;
+
+#define LDEBUG 0
+#define NIC_UNKNOWN ((nic_t) -1)
+
+#undef DEBUG_KLGRAPH
+#ifdef DEBUG_KLGRAPH
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif /* DEBUG_KLGRAPH */
+
+static void sort_nic_names(lboard_t *) ;
+
+lboard_t *
+find_lboard(lboard_t *start, unsigned char brd_type)
+{
+ /* Search all boards stored on this node. */
+ while (start) {
+ if (start->brd_type == brd_type)
+ return start;
+ start = KLCF_NEXT(start);
+ }
+
+ /* Didn't find it. */
+ return (lboard_t *)NULL;
+}
+
+lboard_t *
+find_lboard_class(lboard_t *start, unsigned char brd_type)
+{
+ /* Search all boards stored on this node. */
+ while (start) {
+ if (KLCLASS(start->brd_type) == KLCLASS(brd_type))
+ return start;
+ start = KLCF_NEXT(start);
+ }
+
+ /* Didn't find it. */
+ return (lboard_t *)NULL;
+}
+
+klinfo_t *
+find_component(lboard_t *brd, klinfo_t *kli, unsigned char struct_type)
+{
+ int index, j;
+
+ if (kli == (klinfo_t *)NULL) {
+ index = 0;
+ } else {
+ for (j = 0; j < KLCF_NUM_COMPS(brd); j++) {
+ if (kli == KLCF_COMP(brd, j))
+ break;
+ }
+ index = j;
+ if (index == KLCF_NUM_COMPS(brd)) {
+ printf("find_component: Bad pointer: 0x%p\n", kli);
+ return (klinfo_t *)NULL;
+ }
+ index++; /* next component */
+ }
+
+ for (; index < KLCF_NUM_COMPS(brd); index++) {
+ kli = KLCF_COMP(brd, index);
+ DBG("find_component: brd %p kli %p request type = 0x%x kli type 0x%x\n", brd, kli, kli->struct_type, KLCF_COMP_TYPE(kli));
+ if (KLCF_COMP_TYPE(kli) == struct_type)
+ return kli;
+ }
+
+ /* Didn't find it. */
+ return (klinfo_t *)NULL;
+}
+
+klinfo_t *
+find_first_component(lboard_t *brd, unsigned char struct_type)
+{
+ return find_component(brd, (klinfo_t *)NULL, struct_type);
+}
+
+lboard_t *
+find_lboard_modslot(lboard_t *start, moduleid_t mod, slotid_t slot)
+{
+ /* Search all boards stored on this node. */
+ while (start) {
+ if (MODULE_MATCH(start->brd_module, mod) &&
+ (start->brd_slot == slot))
+ return start;
+ start = KLCF_NEXT(start);
+ }
+
+ /* Didn't find it. */
+ return (lboard_t *)NULL;
+}
+
+lboard_t *
+find_lboard_module(lboard_t *start, moduleid_t mod)
+{
+ /* Search all boards stored on this node. */
+ while (start) {
+ if (MODULE_MATCH(start->brd_module, mod))
+ return start;
+ start = KLCF_NEXT(start);
+ }
+
+ /* Didn't find it. */
+ return (lboard_t *)NULL;
+}
+
+lboard_t *
+find_lboard_module_class(lboard_t *start, moduleid_t mod,
+ unsigned char brd_type)
+{
+ while (start) {
+
+ DBG("find_lboard_module_class: lboard 0x%p, start->brd_module 0x%x, mod 0x%x, start->brd_type 0x%x, brd_type 0x%x\n", start, start->brd_module, mod, start->brd_type, brd_type);
+
+ if (MODULE_MATCH(start->brd_module, mod) &&
+ (KLCLASS(start->brd_type) == KLCLASS(brd_type)))
+ return start;
+ start = KLCF_NEXT(start);
+ }
+
+ /* Didn't find it. */
+ return (lboard_t *)NULL;
+}
+
+#ifndef CONFIG_IA64_SGI_IO
+#define tolower(c) (isupper(c) ? (c) - 'A' + 'a' : (c))
+#define toupper(c) (islower(c) ? (c) - 'a' + 'A' : (c))
+#endif
+
+
+/*
+ * Convert a NIC name to a name for use in the hardware graph.
+ */
+void
+nic_name_convert(char *old_name, char *new_name)
+{
+ int i;
+ char c;
+ char *compare_ptr;
+
+ if ((old_name[0] == '\0') || (old_name[1] == '\0')) {
+ strcpy(new_name, EDGE_LBL_XWIDGET);
+ } else {
+ for (i = 0; i < strlen(old_name); i++) {
+ c = old_name[i];
+
+ if (isalpha(c))
+ new_name[i] = tolower(c);
+ else if (isdigit(c))
+ new_name[i] = c;
+ else
+ new_name[i] = '_';
+ }
+ new_name[i] = '\0';
+ }
+
+ /* XXX -
+ * Since a bunch of boards made it out with weird names like
+ * IO6-fibbbed and IO6P2, we need to look for IO6 in a name and
+ * replace it with "baseio" to avoid confusion in the field.
+ * We also have to make sure we don't report media_io instead of
+ * baseio.
+ */
+
+ /* Skip underscores at the beginning of the name */
+ for (compare_ptr = new_name; (*compare_ptr) == '_'; compare_ptr++)
+ ;
+
+ /*
+ * Check for some names we need to replace. Early boards
+ * had junk following the name so check only the first
+ * characters.
+ */
+ if (!strncmp(new_name, "io6", 3) ||
+ !strncmp(new_name, "mio", 3) ||
+ !strncmp(new_name, "media_io", 8))
+ strcpy(new_name, "baseio");
+#if !defined(CONFIG_SGI_IP35) && !defined(CONFIG_IA64_SGI_SN1) && !defined(CONFIG_IA64_GENERIC)
+ else if (!strncmp(new_name, "ip29", 4))
+ strcpy(new_name,SN00_MOTHERBOARD);
+#endif
+ else if (!strncmp(new_name, "divo", 4))
+ strcpy(new_name, "divo") ;
+
+}
+
+/* Check if the given board corresponds to the global
+ * master io6
+ */
+int
+is_master_baseio(nasid_t nasid,moduleid_t module,slotid_t slot)
+{
+ lboard_t *board;
+
+#if CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 || CONFIG_IA64_GENERIC
+/* BRINGUP: If this works then look for callers of is_master_baseio()
+ * (e.g. iograph.c) and let them pass in a slot if they want
+ */
+ board = find_lboard_module((lboard_t *)KL_CONFIG_INFO(nasid), module);
+#else
+ board = find_lboard_modslot((lboard_t *)KL_CONFIG_INFO(nasid),
+ module, slot);
+#endif
+
+#ifndef _STANDALONE
+ {
+ cnodeid_t cnode = NASID_TO_COMPACT_NODEID(nasid);
+
+ if (!board && (NODEPDA(cnode)->xbow_peer != INVALID_NASID))
+#if CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 || CONFIG_IA64_GENERIC
+ board = find_lboard_module((lboard_t *)
+ KL_CONFIG_INFO(NODEPDA(cnode)->xbow_peer),
+ module);
+#else
+ board = find_lboard_modslot((lboard_t *)
+ KL_CONFIG_INFO(NODEPDA(cnode)->xbow_peer),
+ module, slot);
+#endif
+ }
+#endif
+ if (!board)
+ return(0);
+ return(board->brd_flags & GLOBAL_MASTER_IO6);
+}
+/*
+ * Find the lboard structure and get the board name.
+ * If we can't find the structure or it's too low a revision,
+ * use default name.
+ */
+lboard_t *
+get_board_name(nasid_t nasid, moduleid_t mod, slotid_t slot, char *name)
+{
+ lboard_t *brd;
+
+ brd = find_lboard_modslot((lboard_t *)KL_CONFIG_INFO(nasid),
+ mod, slot);
+
+#ifndef _STANDALONE
+ {
+ cnodeid_t cnode = NASID_TO_COMPACT_NODEID(nasid);
+
+ if (!brd && (NODEPDA(cnode)->xbow_peer != INVALID_NASID))
+ brd = find_lboard_modslot((lboard_t *)
+ KL_CONFIG_INFO(NODEPDA(cnode)->xbow_peer),
+ mod, slot);
+ }
+#endif
+
+ if (!brd || (brd->brd_sversion < 2)) {
+ strcpy(name, EDGE_LBL_XWIDGET);
+ } else {
+ nic_name_convert(brd->brd_name, name);
+ }
+
+ /*
+ * PV # 540860
+ * If the name is not 'baseio' or SN00 MOTHERBOARD
+ * get the lowest of all the names in the nic string.
+ * This is needed for boards like divo, which can have
+ * a bunch of daughter cards, but would like to be called
+ * divo. We could do this for baseio and SN00 MOTHERBOARD
+ * but it has some special case names that we would not
+ * like to disturb at this point.
+ */
+
+ /* gfx boards don't need any of this name scrambling */
+ if (brd && (KLCLASS(brd->brd_type) == KLCLASS_GFX)) {
+ return(brd);
+ }
+
+ if (!(!strcmp(name, "baseio") )) {
+ if (brd) {
+ sort_nic_names(brd) ;
+ /* Convert to small case, '-' to '_' etc */
+ nic_name_convert(brd->brd_name, name) ;
+ }
+ }
+
+ return(brd);
+}
+
+int
+get_cpu_slice(cpuid_t cpu)
+{
+ klcpu_t *acpu;
+ if ((acpu = get_cpuinfo(cpu)) == NULL)
+ return -1;
+ return acpu->cpu_info.physid;
+}
+
+
+/*
+ * get_actual_nasid
+ *
+ * Completely disabled brds have their klconfig on
+ * some other nasid as they have no memory. But their
+ * actual nasid is hidden in the klconfig. Use this
+ * routine to get it. Works for normal boards too.
+ */
+nasid_t
+get_actual_nasid(lboard_t *brd)
+{
+ klhub_t *hub ;
+
+ if (!brd)
+ return INVALID_NASID ;
+
+ /* find out if we are a completely disabled brd. */
+
+ hub = (klhub_t *)find_first_component(brd, KLSTRUCT_HUB);
+ if (!hub)
+ return INVALID_NASID ;
+ if (!(hub->hub_info.flags & KLINFO_ENABLE)) /* disabled node brd */
+ return hub->hub_info.physid ;
+ else
+ return brd->brd_nasid ;
+}
+
+int
+xbow_port_io_enabled(nasid_t nasid, int link)
+{
+ lboard_t *brd;
+ klxbow_t *xbow_p;
+
+ /*
+ * look for boards that might contain an xbow or xbridge
+ */
+#if SN0
+ brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_MIDPLANE8);
+#else
+ brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_PBRICK_XBOW);
+#endif
+ if (brd == NULL) return 0;
+
+ if ((xbow_p = (klxbow_t *)find_component(brd, NULL, KLSTRUCT_XBOW))
+ == NULL)
+ return 0;
+
+ if (!XBOW_PORT_TYPE_IO(xbow_p, link) || !XBOW_PORT_IS_ENABLED(xbow_p, link))
+ return 0;
+
+ printf("xbow_port_io_enabled: brd 0x%p xbow_p 0x%p \n", brd, xbow_p);
+
+ return 1;
+}
+
+void
+board_to_path(lboard_t *brd, char *path)
+{
+ moduleid_t modnum;
+ char *board_name;
+#if !defined(CONFIG_SGI_IP35) && !defined(CONFIG_IA64_SGI_SN1) && !defined(CONFIG_IA64_GENERIC)
+ slotid_t slot;
+ char slot_name[SLOTNUM_MAXLENGTH];
+#endif
+
+ ASSERT(brd);
+
+ switch (KLCLASS(brd->brd_type)) {
+
+ case KLCLASS_NODE:
+ board_name = EDGE_LBL_NODE;
+ break;
+ case KLCLASS_ROUTER:
+ if (brd->brd_type == KLTYPE_META_ROUTER) {
+ board_name = EDGE_LBL_META_ROUTER;
+ hasmetarouter++;
+ } else
+ board_name = EDGE_LBL_ROUTER;
+ break;
+ case KLCLASS_MIDPLANE:
+ board_name = EDGE_LBL_MIDPLANE;
+ break;
+ case KLCLASS_IO:
+ board_name = EDGE_LBL_IO;
+ break;
+ case KLCLASS_IOBRICK:
+ if (brd->brd_type == KLTYPE_PBRICK)
+ board_name = EDGE_LBL_PBRICK;
+ else if (brd->brd_type == KLTYPE_IBRICK)
+ board_name = EDGE_LBL_IBRICK;
+ else if (brd->brd_type == KLTYPE_XBRICK)
+ board_name = EDGE_LBL_XBRICK;
+ else
+ board_name = EDGE_LBL_IOBRICK;
+ break;
+ default:
+ board_name = EDGE_LBL_UNKNOWN;
+ }
+
+ modnum = brd->brd_module;
+
+#if defined(SN0)
+ slot = brd->brd_slot;
+ get_slotname(slot, slot_name);
+
+ ASSERT(modnum >= 0);
+
+ sprintf(path, "%H/" EDGE_LBL_SLOT "/%s/%s",
+ modnum, slot_name, board_name);
+#else
+ ASSERT(modnum != MODULE_UNKNOWN && modnum != INVALID_MODULE);
+#ifdef BRINGUP /* fix IP35 hwgraph */
+ sprintf(path, EDGE_LBL_MODULE "/%x/%s", modnum, board_name);
+#else
+ sprintf(path, "%H/%s", modnum, board_name);
+#endif
+#endif
+}
+
+/*
+ * Get the module number for a NASID.
+ */
+moduleid_t
+get_module_id(nasid_t nasid)
+{
+ lboard_t *brd;
+
+ brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_IP27);
+
+ if (!brd)
+ return INVALID_MODULE;
+ else
+ return brd->brd_module;
+}
+
+
+#ifndef CONFIG_IA64_SGI_IO
+#if 1
+/*
+ * find_gfxpipe(#)
+ *
+ * XXXmacko
+ * This is only used by graphics drivers, and should be moved
+ * over to gfx/kern/graphics/SN0 as soon as it's convenient.
+ */
+static klgfx_t *graphics_pipe_list = NULL;
+static devfs_handle_t hwgraph_all_gfxids = GRAPH_VERTEX_NONE;
+
+void
+setup_gfxpipe_link(devfs_handle_t vhdl,int pipenum)
+{
+ char idbuf[8];
+ extern graph_hdl_t hwgraph;
+
+ graph_info_add_LBL(hwgraph, vhdl, INFO_LBL_GFXID, INFO_DESC_EXPORT,
+ (arbitrary_info_t)pipenum);
+ if (hwgraph_all_gfxids == GRAPH_VERTEX_NONE)
+ hwgraph_path_add(hwgraph_root, EDGE_LBL_GFX, &hwgraph_all_gfxids);
+ sprintf(idbuf, "%d", pipenum);
+ hwgraph_edge_add(hwgraph_all_gfxids, vhdl, idbuf);
+
+}
+#endif
+
+/*
+ * find the pipenum'th logical graphics pipe (KLCLASS_GFX)
+ */
+lboard_t *
+find_gfxpipe(int pipenum)
+{
+ gda_t *gdap;
+ cnodeid_t cnode;
+ nasid_t nasid;
+ lboard_t *lb;
+ klgfx_t *kg,**pkg;
+ int i;
+
+ gdap = (gda_t *)GDA_ADDR(get_nasid());
+ if (gdap->g_magic != GDA_MAGIC)
+ return NULL;
+
+ if (!graphics_pipe_list) {
+ /* for all nodes */
+ for (cnode = 0; cnode < MAX_COMPACT_NODES; cnode ++) {
+ nasid = gdap->g_nasidtable[cnode];
+ if (nasid == INVALID_NASID)
+ continue;
+ lb = KL_CONFIG_INFO(nasid) ;
+ while (lb = find_lboard_class(lb, KLCLASS_GFX)) {
+ moduleid_t kgm, pkgm;
+ int kgs, pkgs;
+
+#if defined(DEBUG) && (defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1 || defined(CONFIG_IA64_GENERIC))) && defined(BRINGUP)
+ printf("find_gfxpipe(): PIPE: %s mod %M slot %d\n",lb?lb->brd_name:"!LBRD",
+ lb->brd_module,lb->brd_slot);
+#endif
+ /* insert lb into list */
+ if (!(kg = (klgfx_t*)find_first_component(lb,KLSTRUCT_GFX))) {
+ lb = KLCF_NEXT(lb);
+ continue;
+ }
+ /* set moduleslot now that we have brd_module set */
+ kg->moduleslot = (lb->brd_module << 8) | SLOTNUM_GETSLOT(lb->brd_slot);
+ /* make sure board has device flag set */
+ kg->gfx_info.flags |= KLINFO_DEVICE;
+ if (kg->cookie < KLGFX_COOKIE) {
+ kg->gfx_next_pipe = NULL;
+ kg->cookie = KLGFX_COOKIE;
+ }
+
+ kgm = kg->moduleslot>>8;
+ kgs = kg->moduleslot&0xff;
+ pkg = &graphics_pipe_list;
+ while (*pkg) {
+ pkgm = (*pkg)->moduleslot>>8;
+ pkgs = (*pkg)->moduleslot&0xff;
+
+ if (!(MODULE_CMP(kgm, pkgm) > 0 ||
+ (MODULE_CMP(kgm, pkgm) == 0 &&
+ kgs > pkgs)))
+ break;
+
+ pkg = &(*pkg)->gfx_next_pipe;
+ }
+ kg->gfx_next_pipe = *pkg;
+ *pkg = kg;
+ lb = KLCF_NEXT(lb);
+ }
+ }
+#ifdef FIND_GFXPIPE_DEBUG
+ i = 0;
+ kg = graphics_pipe_list;
+ while (kg) {
+ lboard_t *lb;
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+ lb = find_lboard_class(KL_CONFIG_INFO(kg->gfx_info.nasid), KLCLASS_GFX);
+#else
+#error Need to figure out how to find graphics boards ...
+#endif
+#if defined(SUPPORT_PRINTING_M_FORMAT)
+ printf("find_gfxpipe(): %s pipe %d mod %M slot %d\n",lb?lb->brd_name:"!LBRD",i,
+ (kg->moduleslot>>8),(kg->moduleslot&0xff));
+#else
+ printf("find_gfxpipe(): %s pipe %d mod 0x%x slot %d\n",lb?lb->brd_name:"!LBRD",i,
+ (kg->moduleslot>>8),(kg->moduleslot&0xff));
+#endif
+ kg = kg->gfx_next_pipe;
+ i++;
+ }
+#endif
+ }
+
+ i = 0;
+ kg = graphics_pipe_list;
+ while (kg && (i < pipenum)) {
+ kg = kg->gfx_next_pipe;
+ i++;
+ }
+
+ if (!kg) return NULL;
+
+#if defined(SN0)
+ return find_lboard_modslot(KL_CONFIG_INFO(kg->gfx_info.nasid),
+ (kg->moduleslot>>8),
+ SLOTNUM_XTALK_CLASS|(kg->moduleslot&0xff));
+#elif defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+ return find_lboard_class(KL_CONFIG_INFO(kg->gfx_info.nasid), KLCLASS_GFX);
+#else
+#error Need to figure out how to find graphics boards ...
+#endif
+}
+#endif
+
+
+#define MHZ 1000000
+
+#ifndef CONFIG_IA64_SGI_IO
+uint
+cpu_cycles_adjust(uint orig_cycles)
+{
+ klcpu_t *acpu;
+ uint speed;
+
+ acpu = nasid_slice_to_cpuinfo(get_nasid(), get_slice());
+
+ if (acpu == NULL) return orig_cycles;
+
+ /*
+ * cpu cycles seem to be half of the real value, hack and mult by 2
+ * for now.
+ */
+ speed = (orig_cycles * 2) / MHZ;
+
+ /*
+ * if the cpu thinks its running at some random speed nowhere close
+ * the programmed speed, do nothing.
+ */
+ if ((speed < (acpu->cpu_speed - 2)) || (speed > (acpu->cpu_speed + 2)))
+ return orig_cycles;
+ return (acpu->cpu_speed * MHZ/2);
+}
+#endif /* CONFIG_IA64_SGI_IO */
+
+/* Get the canonical hardware graph name for the given pci component
+ * on the given io board.
+ */
+void
+device_component_canonical_name_get(lboard_t *brd,
+ klinfo_t *component,
+ char *name)
+{
+ moduleid_t modnum;
+ slotid_t slot;
+ char board_name[20];
+#ifdef SN0
+ char slot_name[SLOTNUM_MAXLENGTH];
+#endif
+
+ ASSERT(brd);
+
+ /* Get the module number of this board */
+ modnum = brd->brd_module;
+
+ /* Convert the [ CLASS | TYPE ] kind of slotid
+ * into a string
+ */
+ slot = brd->brd_slot;
+#ifdef SN0
+ get_slotname(slot, slot_name);
+
+ ASSERT(modnum >= 0);
+#else
+ ASSERT(modnum != MODULE_UNKNOWN && modnum != INVALID_MODULE);
+#endif
+
+ /* Get the io board name */
+ if (!brd || (brd->brd_sversion < 2)) {
+ strcpy(name, EDGE_LBL_XWIDGET);
+ } else {
+ nic_name_convert(brd->brd_name, board_name);
+ }
+
+ /* Give out the canonical name of the pci device*/
+#ifdef SN0
+ sprintf(name,
+ "/hw/"EDGE_LBL_MODULE "/%M/"EDGE_LBL_SLOT"/%s/%s/"
+ EDGE_LBL_PCI"/%d",
+ modnum, slot_name, board_name,KLCF_BRIDGE_W_ID(component));
+#elif defined (CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+ sprintf(name,
+ "/dev/hw/"EDGE_LBL_MODULE "/%x/"EDGE_LBL_SLOT"/%s/"
+ EDGE_LBL_PCI"/%d",
+ modnum, board_name,KLCF_BRIDGE_W_ID(component));
+#endif
+
+}
+
+/*
+ * Get the serial number of the main component of a board
+ * Returns 0 if a valid serial number is found
+ * 1 otherwise.
+ * Assumptions: Nic manufacturing string has the following format
+ * *Serial:<serial_number>;*
+ */
+static int
+component_serial_number_get(lboard_t *board,
+ klconf_off_t mfg_nic_offset,
+ char *serial_number,
+ char *key_pattern)
+{
+
+ char *mfg_nic_string;
+ char *serial_string,*str;
+ int i;
+ char *serial_pattern = "Serial:";
+
+ /* We have an error on a null mfg nic offset */
+ if (!mfg_nic_offset)
+ return(1);
+ /* Get the hub's manufacturing nic information
+ * which is in the form of a pre-formatted string
+ */
+ mfg_nic_string =
+ (char *)NODE_OFFSET_TO_K0(NASID_GET(board),
+ mfg_nic_offset);
+ /* There is no manufacturing nic info */
+ if (!mfg_nic_string)
+ return(1);
+
+ str = mfg_nic_string;
+ /* Look for the key pattern first (if it is specified)
+ * and then print the serial number corresponding to that.
+ */
+ if (strcmp(key_pattern,"") &&
+ !(str = strstr(mfg_nic_string,key_pattern)))
+ return(1);
+
+ /* There is no serial number info in the manufacturing
+ * nic info
+ */
+ if (!(serial_string = strstr(str,serial_pattern)))
+ return(1);
+
+ serial_string = serial_string + strlen(serial_pattern);
+ /* Copy the serial number information from the klconfig */
+ i = 0;
+ while (serial_string[i] != ';') {
+ serial_number[i] = serial_string[i];
+ i++;
+ }
+ serial_number[i] = 0;
+
+ return(0);
+}
+/*
+ * Get the serial number of a board
+ * Returns 0 if a valid serial number is found
+ * 1 otherwise.
+ */
+
+int
+board_serial_number_get(lboard_t *board,char *serial_number)
+{
+ ASSERT(board && serial_number);
+ if (!board || !serial_number)
+ return(1);
+
+ strcpy(serial_number,"");
+ switch(KLCLASS(board->brd_type)) {
+ case KLCLASS_CPU: { /* Node board */
+ klhub_t *hub;
+
+ /* Get the hub component information */
+ hub = (klhub_t *)find_first_component(board,
+ KLSTRUCT_HUB);
+ /* If we don't have a hub component on an IP27
+ * then we have a weird klconfig.
+ */
+ if (!hub)
+ return(1);
+ /* Get the serial number information from
+ * the hub's manufacturing nic info
+ */
+ if (component_serial_number_get(board,
+ hub->hub_mfg_nic,
+ serial_number,
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+ "IP35"))
+#else
+ "IP27"))
+ /* Try with IP31 key if IP27 key fails */
+ if (component_serial_number_get(board,
+ hub->hub_mfg_nic,
+ serial_number,
+ "IP31"))
+#endif /* CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 */
+ return(1);
+ break;
+ }
+ case KLCLASS_IO: { /* IO board */
+ if (KLTYPE(board->brd_type) == KLTYPE_TPU) {
+ /* Special case for TPU boards */
+ kltpu_t *tpu;
+
+ /* Get the tpu component information */
+ tpu = (kltpu_t *)find_first_component(board,
+ KLSTRUCT_TPU);
+ /* If we don't have a tpu component on a tpu board
+ * then we have a weird klconfig.
+ */
+ if (!tpu)
+ return(1);
+ /* Get the serial number information from
+ * the tpu's manufacturing nic info
+ */
+ if (component_serial_number_get(board,
+ tpu->tpu_mfg_nic,
+ serial_number,
+ ""))
+ return(1);
+ break;
+ } else if ((KLTYPE(board->brd_type) == KLTYPE_GSN_A) ||
+ (KLTYPE(board->brd_type) == KLTYPE_GSN_B)) {
+ /* Special case for GSN boards */
+ klgsn_t *gsn;
+
+ /* Get the gsn component information */
+ gsn = (klgsn_t *)find_first_component(board,
+ ((KLTYPE(board->brd_type) == KLTYPE_GSN_A) ?
+ KLSTRUCT_GSN_A : KLSTRUCT_GSN_B));
+ /* If we don't have a gsn component on a gsn board
+ * then we have a weird klconfig.
+ */
+ if (!gsn)
+ return(1);
+ /* Get the serial number information from
+ * the gsn's manufacturing nic info
+ */
+ if (component_serial_number_get(board,
+ gsn->gsn_mfg_nic,
+ serial_number,
+ ""))
+ return(1);
+ break;
+ } else {
+ klbri_t *bridge;
+
+ /* Get the bridge component information */
+ bridge = (klbri_t *)find_first_component(board,
+ KLSTRUCT_BRI);
+ /* If we don't have a bridge component on an IO board
+ * then we have a weird klconfig.
+ */
+ if (!bridge)
+ return(1);
+ /* Get the serial number information from
+ * the bridge's manufacturing nic info
+ */
+ if (component_serial_number_get(board,
+ bridge->bri_mfg_nic,
+ serial_number,
+ ""))
+ return(1);
+ break;
+ }
+ }
+ case KLCLASS_ROUTER: { /* Router board */
+ klrou_t *router;
+
+ /* Get the router component information */
+ router = (klrou_t *)find_first_component(board,
+ KLSTRUCT_ROU);
+ /* If we don't have a router component on a router board
+ * then we have a weird klconfig.
+ */
+ if (!router)
+ return(1);
+ /* Get the serial number information from
+ * the router's manufacturing nic info
+ */
+ if (component_serial_number_get(board,
+ router->rou_mfg_nic,
+ serial_number,
+ ""))
+ return(1);
+ break;
+ }
+ case KLCLASS_GFX: { /* Gfx board */
+ klgfx_t *graphics;
+
+ /* Get the graphics component information */
+ graphics = (klgfx_t *)find_first_component(board, KLSTRUCT_GFX);
+ /* If we don't have a gfx component on a gfx board
+ * then we have a weird klconfig.
+ */
+ if (!graphics)
+ return(1);
+ /* Get the serial number information from
+ * the graphics's manufacturing nic info
+ */
+ if (component_serial_number_get(board,
+ graphics->gfx_mfg_nic,
+ serial_number,
+ ""))
+ return(1);
+ break;
+ }
+ default:
+ strcpy(serial_number,"");
+ break;
+ }
+ return(0);
+}
+
+#include "asm/sn/sn_private.h"
+#ifndef CONFIG_IA64_SGI_IO
+/*
+ * Given a physical address get the name of memory dimm bank
+ * in a hwgraph name format.
+ */
+void
+membank_pathname_get(paddr_t paddr,char *name)
+{
+ cnodeid_t cnode;
+ char slotname[SLOTNUM_MAXLENGTH];
+
+ cnode = paddr_cnode(paddr);
+ /* Make sure that we have a valid name buffer */
+ if (!name)
+ return;
+
+ name[0] = 0;
+ /* Make sure that the cnode is valid */
+ if ((cnode == CNODEID_NONE) || (cnode > numnodes))
+ return;
+ /* Given a slotid(class:type) get the slotname */
+#if defined (SN0)
+ get_slotname(NODE_SLOTID(cnode),slotname);
+ sprintf(name,
+ "/hw/"EDGE_LBL_MODULE"/%M/"EDGE_LBL_SLOT"/%s/"EDGE_LBL_NODE
+ "/"EDGE_LBL_MEMORY"/dimm_bank/%d",
+ NODE_MODULEID(cnode),slotname,paddr_dimm(paddr));
+#elif defined (CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+ sprintf(name,
+ "/dev/hw/"EDGE_LBL_MODULE"/%M/"EDGE_LBL_NODE
+ "/"EDGE_LBL_MEMORY"/dimm_bank/%d",
+ NODE_MODULEID(cnode),paddr_dimm(paddr));
+#endif
+}
+
+
+
+int
+membank_check_mixed_hidensity(nasid_t nasid)
+{
+ lboard_t *brd;
+ klmembnk_t *mem;
+ int min_size = 1024, max_size = 0;
+ int bank, mem_size;
+
+ brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_IP27);
+ ASSERT(brd);
+
+ mem = (klmembnk_t *)find_first_component(brd, KLSTRUCT_MEMBNK);
+ ASSERT(mem);
+
+
+ for (mem_size = 0, bank = 0; bank < MD_MEM_BANKS; bank++) {
+ mem_size = KLCONFIG_MEMBNK_SIZE(mem, bank);
+ if (mem_size < min_size)
+ min_size = mem_size;
+ if (mem_size > max_size)
+ max_size = mem_size;
+ }
+
+ if ((max_size == 512) && (max_size != min_size))
+ return 1;
+
+ return 0;
+}
+
+
+int
+mem_mixed_hidensity_banks(void)
+{
+ cnodeid_t cnode;
+ nasid_t nasid;
+
+ for (cnode = 0; cnode < maxnodes; cnode++) {
+ nasid = COMPACT_TO_NASID_NODEID(cnode);
+ if (nasid == INVALID_NASID)
+ continue;
+ if (membank_check_mixed_hidensity(nasid))
+ return 1;
+ }
+ return 0;
+
+}
+#endif /* CONFIG_IA64_SGI_IO */
+
+xwidgetnum_t
+nodevertex_widgetnum_get(devfs_handle_t node_vtx)
+{
+ hubinfo_t hubinfo_p;
+
+ hwgraph_info_get_LBL(node_vtx, INFO_LBL_NODE_INFO,
+ (arbitrary_info_t *) &hubinfo_p);
+ return(hubinfo_p->h_widgetid);
+}
+
+devfs_handle_t
+nodevertex_xbow_peer_get(devfs_handle_t node_vtx)
+{
+ hubinfo_t hubinfo_p;
+ nasid_t xbow_peer_nasid;
+ cnodeid_t xbow_peer;
+
+ hwgraph_info_get_LBL(node_vtx, INFO_LBL_NODE_INFO,
+ (arbitrary_info_t *) &hubinfo_p);
+ xbow_peer_nasid = hubinfo_p->h_nodepda->xbow_peer;
+ if(xbow_peer_nasid == INVALID_NASID)
+ return ( (devfs_handle_t)-1);
+ xbow_peer = NASID_TO_COMPACT_NODEID(xbow_peer_nasid);
+ return(NODEPDA(xbow_peer)->node_vertex);
+}
+
+/* NIC Sorting Support */
+
+#define MAX_NICS_PER_STRING 32
+#define MAX_NIC_NAME_LEN 32
+
+static char *
+get_nic_string(lboard_t *lb)
+{
+ int i;
+ klinfo_t *k = NULL ;
+ klconf_off_t mfg_off = 0 ;
+ char *mfg_nic = NULL ;
+
+ for (i = 0; i < KLCF_NUM_COMPS(lb); i++) {
+ k = KLCF_COMP(lb, i) ;
+ switch(k->struct_type) {
+ case KLSTRUCT_BRI:
+ mfg_off = ((klbri_t *)k)->bri_mfg_nic ;
+ break ;
+
+ case KLSTRUCT_HUB:
+ mfg_off = ((klhub_t *)k)->hub_mfg_nic ;
+ break ;
+
+ case KLSTRUCT_ROU:
+ mfg_off = ((klrou_t *)k)->rou_mfg_nic ;
+ break ;
+
+ case KLSTRUCT_GFX:
+ mfg_off = ((klgfx_t *)k)->gfx_mfg_nic ;
+ break ;
+
+ case KLSTRUCT_TPU:
+ mfg_off = ((kltpu_t *)k)->tpu_mfg_nic ;
+ break ;
+
+ case KLSTRUCT_GSN_A:
+ case KLSTRUCT_GSN_B:
+ mfg_off = ((klgsn_t *)k)->gsn_mfg_nic ;
+ break ;
+
+ case KLSTRUCT_XTHD:
+ mfg_off = ((klxthd_t *)k)->xthd_mfg_nic ;
+ break;
+
+ default:
+ mfg_off = 0 ;
+ break ;
+ }
+ if (mfg_off)
+ break ;
+ }
+
+ if ((mfg_off) && (k))
+ mfg_nic = (char *)NODE_OFFSET_TO_K0(k->nasid, mfg_off) ;
+
+ return mfg_nic ;
+}
+
+char *
+get_first_string(char **ptrs, int n)
+{
+ int i ;
+ char *tmpptr ;
+
+ if ((ptrs == NULL) || (n == 0))
+ return NULL ;
+
+ tmpptr = ptrs[0] ;
+
+ if (n == 1)
+ return tmpptr ;
+
+ for (i = 0 ; i < n ; i++) {
+ if (strcmp(tmpptr, ptrs[i]) > 0)
+ tmpptr = ptrs[i] ;
+ }
+
+ return tmpptr ;
+}
+
+int
+get_ptrs(char *idata, char **ptrs, int n, char *label)
+{
+ int i = 0 ;
+ char *tmp = idata ;
+
+ if ((ptrs == NULL) || (idata == NULL) || (label == NULL) || (n == 0))
+ return 0 ;
+
+ while ( (tmp = strstr(tmp, label)) ){
+ tmp += strlen(label) ;
+ /* check for empty name field, and last NULL ptr */
+ if ((i < (n-1)) && (*tmp != ';')) {
+ ptrs[i++] = tmp ;
+ }
+ }
+
+ ptrs[i] = NULL ;
+
+ return i ;
+}
+
+/*
+ * sort_nic_names
+ *
+ * Does not really do sorting. Find the alphabetically lowest
+ * name among all the nic names found in a nic string.
+ *
+ * Return:
+ * Nothing
+ *
+ * Side Effects:
+ *
+ * lb->brd_name gets the new name found
+ */
+
+static void
+sort_nic_names(lboard_t *lb)
+{
+ char *nic_str ;
+ char *ptrs[MAX_NICS_PER_STRING] ;
+ char name[MAX_NIC_NAME_LEN] ;
+ char *tmp, *tmp1 ;
+
+ *name = 0 ;
+
+ /* Get the nic pointer from the lb */
+
+ if ((nic_str = get_nic_string(lb)) == NULL)
+ return ;
+
+ tmp = get_first_string(ptrs,
+ get_ptrs(nic_str, ptrs, MAX_NICS_PER_STRING, "Name:")) ;
+
+ if (tmp == NULL)
+ return ;
+
+ if ( (tmp1 = strchr(tmp, ';')) ){
+ strncpy(name, tmp, tmp1-tmp) ;
+ name[tmp1-tmp] = 0 ;
+ } else {
+ strncpy(name, tmp, (sizeof(name) -1)) ;
+ name[sizeof(name)-1] = 0 ;
+ }
+
+ strcpy(lb->brd_name, name) ;
+}
+
+
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+
+char brick_types[MAX_BRICK_TYPES + 1] = "crikxdp789012345";
+
+/*
+ * Format a module id for printing.
+ */
+void
+format_module_id(char *buffer, moduleid_t m, int fmt)
+{
+ int rack, position;
+ char brickchar;
+
+ rack = MODULE_GET_RACK(m);
+ ASSERT(MODULE_GET_BTYPE(m) < MAX_BRICK_TYPES);
+ brickchar = MODULE_GET_BTCHAR(m);
+ position = MODULE_GET_BPOS(m);
+
+ if (fmt == MODULE_FORMAT_BRIEF) {
+ /* Brief module number format, eg. 002c15 */
+
+ /* Decompress the rack number */
+ *buffer++ = '0' + RACK_GET_CLASS(rack);
+ *buffer++ = '0' + RACK_GET_GROUP(rack);
+ *buffer++ = '0' + RACK_GET_NUM(rack);
+
+ /* Add the brick type */
+ *buffer++ = brickchar;
+ }
+ else if (fmt == MODULE_FORMAT_LONG) {
+ /* Fuller hwgraph format, eg. rack/002/bay/15 */
+
+ strcpy(buffer, EDGE_LBL_RACK "/"); buffer += strlen(buffer);
+
+ *buffer++ = '0' + RACK_GET_CLASS(rack);
+ *buffer++ = '0' + RACK_GET_GROUP(rack);
+ *buffer++ = '0' + RACK_GET_NUM(rack);
+
+ strcpy(buffer, "/" EDGE_LBL_RPOS "/"); buffer += strlen(buffer);
+ }
+
+ /* Add the bay position, using at least two digits */
+ if (position < 10)
+ *buffer++ = '0';
+ sprintf(buffer, "%d", position);
+
+}
+
+/*
+ * Parse a module id, in either brief or long form.
+ * Returns < 0 on error.
+ * The long form does not include a brick type, so it defaults to 0 (CBrick)
+ */
+int
+parse_module_id(char *buffer)
+{
+ unsigned int v, rack, bay, type, form;
+ moduleid_t m;
+ char c;
+
+ if (strstr(buffer, EDGE_LBL_RACK "/") == buffer) {
+ form = MODULE_FORMAT_LONG;
+ buffer += strlen(EDGE_LBL_RACK "/");
+
+ /* A long module ID must be exactly 5 non-template chars. */
+ if (strlen(buffer) != strlen("/" EDGE_LBL_RPOS "/") + 5)
+ return -1;
+ }
+ else {
+ form = MODULE_FORMAT_BRIEF;
+
+ /* A brief module id must be exactly 6 characters */
+ if (strlen(buffer) != 6)
+ return -2;
+ }
+
+ /* The rack number must be exactly 3 digits */
+ if (!(isdigit(buffer[0]) && isdigit(buffer[1]) && isdigit(buffer[2])))
+ return -3;
+
+ rack = 0;
+ v = *buffer++ - '0';
+ if (v > RACK_CLASS_MASK(rack) >> RACK_CLASS_SHFT(rack))
+ return -4;
+ RACK_ADD_CLASS(rack, v);
+
+ v = *buffer++ - '0';
+ if (v > RACK_GROUP_MASK(rack) >> RACK_GROUP_SHFT(rack))
+ return -5;
+ RACK_ADD_GROUP(rack, v);
+
+ v = *buffer++ - '0';
+ /* rack numbers are 1-based */
+ if (v-1 > RACK_NUM_MASK(rack) >> RACK_NUM_SHFT(rack))
+ return -6;
+ RACK_ADD_NUM(rack, v);
+
+ if (form == MODULE_FORMAT_BRIEF) {
+ /* Next should be a module type character. Accept ucase or lcase. */
+ c = *buffer++;
+ if (!isalpha(c))
+ return -7;
+
+ /* strchr() returns a pointer into brick_types[], or NULL */
+ type = (unsigned int)(strchr(brick_types, tolower(c)) - brick_types);
+ if (type > MODULE_BTYPE_MASK >> MODULE_BTYPE_SHFT)
+ return -8;
+ }
+ else {
+ /* Hardcode the module type, and skip over the boilerplate */
+ type = MODULE_CBRICK;
+
+ if (strstr(buffer, "/" EDGE_LBL_RPOS "/") != buffer)
+ return -9;
+
+ buffer += strlen("/" EDGE_LBL_RPOS "/");
+ }
+
+ /* The bay number is last. Make sure it's exactly two digits */
+
+ if (!(isdigit(buffer[0]) && isdigit(buffer[1]) && !buffer[2]))
+ return -10;
+
+ bay = 10 * (buffer[0] - '0') + (buffer[1] - '0');
+
+ if (bay > MODULE_BPOS_MASK >> MODULE_BPOS_SHFT)
+ return -11;
+
+ m = RBT_TO_MODULE(rack, bay, type);
+
+ /* avoid sign extending the moduleid_t */
+ return (int)(unsigned short)m;
+}
+
+#else /* CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 */
+
+/*
+ * Format a module id for printing.
+ */
+void
+format_module_id(char *buffer, moduleid_t m, int fmt)
+{
+ if (fmt == MODULE_FORMAT_BRIEF) {
+ sprintf(buffer, "%d", m);
+ }
+ else if (fmt == MODULE_FORMAT_LONG) {
+ sprintf(buffer, EDGE_LBL_MODULE "/%d", m);
+ }
+}
+
+/*
+ * Parse a module id, in either brief or long form.
+ * Returns < 0 on error.
+ */
+int
+parse_module_id(char *buffer)
+{
+ moduleid_t m;
+ char c;
+
+ if (strstr(buffer, EDGE_LBL_MODULE "/") == buffer)
+ buffer += strlen(EDGE_LBL_MODULE "/");
+
+ m = 0;
+ while(c = *buffer++) {
+ if (!isdigit(c))
+ return -1;
+ m = 10 * m + (c - '0');
+ }
+
+ /* avoid sign extending the moduleid_t */
+ return (int)(unsigned short)m;
+}
+
+#endif /* CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 */
+
+
diff --git a/arch/ia64/sn/io/klgraph.c b/arch/ia64/sn/io/klgraph.c
new file mode 100644
index 000000000..dcd7c2316
--- /dev/null
+++ b/arch/ia64/sn/io/klgraph.c
@@ -0,0 +1,971 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+/*
+ * klgraph.c-
+ * This file specifies the interface between the kernel and the PROM's
+ * configuration data structures.
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+
+#include <asm/sn/cmn_err.h>
+#include <asm/sn/agent.h>
+#ifdef CONFIG_IA64_SGI_IO
+#include <asm/sn/kldir.h>
+#endif
+#include <asm/sn/gda.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/router.h>
+#include <asm/sn/xtalk/xbow.h>
+#include <asm/sn/hcl_util.h>
+
+#define KLGRAPH_DEBUG 1
+#ifdef KLGRAPH_DEBUG
+#define GRPRINTF(x) printk x
+#define CE_GRPANIC CE_PANIC
+#else
+#define GRPRINTF(x)
+#define CE_GRPANIC CE_PANIC
+#endif
+
+#include <asm/sn/sn_private.h>
+
+extern char arg_maxnodes[];
+extern int maxnodes;
+
+#ifndef BRINGUP
+/*
+ * Gets reason for diagval using table lookup.
+ */
+static char*
+get_diag_string(uint diagcode)
+{
+ int num_entries;
+ int i;
+ num_entries = sizeof(diagval_map) / sizeof(diagval_t);
+ for (i = 0; i < num_entries; i++){
+ if ((unchar)diagval_map[i].dv_code == (unchar)diagcode)
+ return diagval_map[i].dv_msg;
+ }
+ return "Unknown";
+}
+
+#endif /* ndef BRINGUP */
+
+
+/*
+ * Support for verbose inventory via hardware graph.
+ * klhwg_invent_alloc allocates the necessary size of inventory information
+ * and fills in the generic information.
+ */
+invent_generic_t *
+klhwg_invent_alloc(cnodeid_t cnode, int class, int size)
+{
+ invent_generic_t *invent;
+
+ invent = kern_malloc(size);
+ if (!invent) return NULL;
+
+ invent->ig_module = NODE_MODULEID(cnode);
+ invent->ig_slot = SLOTNUM_GETSLOT(NODE_SLOTID(cnode));
+ invent->ig_invclass = class;
+
+ return invent;
+}
+
+/*
+ * Add information about the baseio prom version number
+ * as a part of detailed inventory info in the hwgraph.
+ */
+void
+klhwg_baseio_inventory_add(devfs_handle_t baseio_vhdl,cnodeid_t cnode)
+{
+ invent_miscinfo_t *baseio_inventory;
+ unsigned char version = 0,revision = 0;
+
+ /* Allocate memory for the "detailed inventory" info
+ * for the baseio
+ */
+ baseio_inventory = (invent_miscinfo_t *)
+ klhwg_invent_alloc(cnode, INV_PROM, sizeof(invent_miscinfo_t));
+ baseio_inventory->im_type = INV_IO6PROM;
+ /* Read the io6prom revision from the nvram */
+#ifndef CONFIG_IA64_SGI_IO
+ nvram_prom_version_get(&version,&revision);
+#endif
+ /* Store the revision info in the inventory */
+ baseio_inventory->im_version = version;
+ baseio_inventory->im_rev = revision;
+ /* Put the inventory info in the hardware graph */
+ hwgraph_info_add_LBL(baseio_vhdl, INFO_LBL_DETAIL_INVENT,
+ (arbitrary_info_t) baseio_inventory);
+ /* Make the information available to the user programs
+ * thru hwgfs.
+ */
+ hwgraph_info_export_LBL(baseio_vhdl, INFO_LBL_DETAIL_INVENT,
+ sizeof(invent_miscinfo_t));
+}
+
+char *hub_rev[] = {
+ "0.0",
+ "1.0",
+ "2.0",
+ "2.1",
+ "2.2",
+ "2.3"
+};
+
+/*
+ * Add detailed cpu inventory info to the hardware graph.
+ */
+void
+klhwg_hub_invent_info(devfs_handle_t hubv,
+ cnodeid_t cnode,
+ klhub_t *hub)
+{
+ invent_miscinfo_t *hub_invent;
+
+ hub_invent = (invent_miscinfo_t *)
+ klhwg_invent_alloc(cnode, INV_MISC, sizeof(invent_miscinfo_t));
+ if (!hub_invent)
+ return;
+
+ if (KLCONFIG_INFO_ENABLED((klinfo_t *)hub))
+ hub_invent->im_gen.ig_flag = INVENT_ENABLED;
+
+ hub_invent->im_type = INV_HUB;
+ hub_invent->im_rev = hub->hub_info.revision;
+ hub_invent->im_speed = hub->hub_speed;
+ hwgraph_info_add_LBL(hubv, INFO_LBL_DETAIL_INVENT,
+ (arbitrary_info_t) hub_invent);
+ hwgraph_info_export_LBL(hubv, INFO_LBL_DETAIL_INVENT,
+ sizeof(invent_miscinfo_t));
+}
+
+/* ARGSUSED */
+void
+klhwg_add_hub(devfs_handle_t node_vertex, klhub_t *hub, cnodeid_t cnode)
+{
+ devfs_handle_t myhubv;
+ int rc;
+
+ GRPRINTF(("klhwg_add_hub: adding %s\n", EDGE_LBL_HUB));
+
+ (void) hwgraph_path_add(node_vertex, EDGE_LBL_HUB, &myhubv);
+ rc = device_master_set(myhubv, node_vertex);
+
+#ifndef CONFIG_IA64_SGI_IO
+ /*
+ * Activate when we support hub stats.
+ */
+ rc = hwgraph_info_add_LBL(myhubv, INFO_LBL_HUB_INFO,
+ (arbitrary_info_t)(&NODEPDA(cnode)->hubstats));
+#endif
+
+ if (rc != GRAPH_SUCCESS) {
+ cmn_err(CE_WARN,
+ "klhwg_add_hub: Can't add hub info label 0x%p, code %d",
+ myhubv, rc);
+ }
+
+ klhwg_hub_invent_info(myhubv, cnode, hub);
+
+#ifndef BRINGUP
+ init_hub_stats(cnode, NODEPDA(cnode));
+#endif /* ndef BRINGUP */
+
+#ifndef CONFIG_IA64_SGI_IO
+ sndrv_attach(myhubv);
+#else
+ /*
+ * Need to call our driver to do the attach?
+ */
+ printk("klhwg_add_hub: Need to add code to do the attach.\n");
+#endif
+}
+
+#ifndef BRINGUP
+
+void
+klhwg_add_rps(devfs_handle_t node_vertex, cnodeid_t cnode, int flag)
+{
+ devfs_handle_t myrpsv;
+ invent_rpsinfo_t *rps_invent;
+ int rc;
+
+ if(cnode == CNODEID_NONE)
+ return;
+
+ GRPRINTF(("klhwg_add_rps: adding %s to vertex 0x%x\n", EDGE_LBL_RPS,
+ node_vertex));
+
+ rc = hwgraph_path_add(node_vertex, EDGE_LBL_RPS, &myrpsv);
+ if (rc != GRAPH_SUCCESS)
+ return;
+
+ device_master_set(myrpsv, node_vertex);
+
+ rps_invent = (invent_rpsinfo_t *)
+ klhwg_invent_alloc(cnode, INV_RPS, sizeof(invent_rpsinfo_t));
+
+ if (!rps_invent)
+ return;
+
+ rps_invent->ir_xbox = 0; /* not an xbox RPS */
+
+ if (flag)
+ rps_invent->ir_gen.ig_flag = INVENT_ENABLED;
+ else
+ rps_invent->ir_gen.ig_flag = 0x0;
+
+ hwgraph_info_add_LBL(myrpsv, INFO_LBL_DETAIL_INVENT,
+ (arbitrary_info_t) rps_invent);
+ hwgraph_info_export_LBL(myrpsv, INFO_LBL_DETAIL_INVENT,
+ sizeof(invent_rpsinfo_t));
+
+}
+
+/*
+ * klhwg_update_rps gets invoked when the system controller sends an
+ * interrupt indicating the power supply has lost/regained the redundancy.
+ * It's responsible for updating the Hardware graph information.
+ * rps_state = 0 -> if the rps lost the redundancy
+ * = 1 -> If it is redundant.
+ */
+void
+klhwg_update_rps(cnodeid_t cnode, int rps_state)
+{
+ devfs_handle_t node_vertex;
+ devfs_handle_t rpsv;
+ invent_rpsinfo_t *rps_invent;
+ int rc;
+ if(cnode == CNODEID_NONE)
+ return;
+
+ node_vertex = cnodeid_to_vertex(cnode);
+ rc = hwgraph_edge_get(node_vertex, EDGE_LBL_RPS, &rpsv);
+ if (rc != GRAPH_SUCCESS) {
+ return;
+ }
+
+ rc = hwgraph_info_get_LBL(rpsv, INFO_LBL_DETAIL_INVENT,
+ (arbitrary_info_t *)&rps_invent);
+ if (rc != GRAPH_SUCCESS) {
+ return;
+ }
+
+ if (rps_state == 0 )
+ rps_invent->ir_gen.ig_flag = 0;
+ else
+ rps_invent->ir_gen.ig_flag = INVENT_ENABLED;
+}
+
+void
+klhwg_add_xbox_rps(devfs_handle_t node_vertex, cnodeid_t cnode, int flag)
+{
+ devfs_handle_t myrpsv;
+ invent_rpsinfo_t *rps_invent;
+ int rc;
+
+ if(cnode == CNODEID_NONE)
+ return;
+
+ GRPRINTF(("klhwg_add_rps: adding %s to vertex 0x%x\n",
+ EDGE_LBL_XBOX_RPS, node_vertex));
+
+ rc = hwgraph_path_add(node_vertex, EDGE_LBL_XBOX_RPS, &myrpsv);
+ if (rc != GRAPH_SUCCESS)
+ return;
+
+ device_master_set(myrpsv, node_vertex);
+
+ rps_invent = (invent_rpsinfo_t *)
+ klhwg_invent_alloc(cnode, INV_RPS, sizeof(invent_rpsinfo_t));
+
+ if (!rps_invent)
+ return;
+
+ rps_invent->ir_xbox = 1; /* xbox RPS */
+
+ if (flag)
+ rps_invent->ir_gen.ig_flag = INVENT_ENABLED;
+ else
+ rps_invent->ir_gen.ig_flag = 0x0;
+
+ hwgraph_info_add_LBL(myrpsv, INFO_LBL_DETAIL_INVENT,
+ (arbitrary_info_t) rps_invent);
+ hwgraph_info_export_LBL(myrpsv, INFO_LBL_DETAIL_INVENT,
+ sizeof(invent_rpsinfo_t));
+
+}
+
+/*
+ * klhwg_update_xbox_rps gets invoked when the xbox system controller
+ * polls the status register and discovers that the power supply has
+ * lost/regained the redundancy.
+ * It's responsible for updating the Hardware graph information.
+ * rps_state = 0 -> if the rps lost the redundancy
+ * = 1 -> If it is redundant.
+ */
+void
+klhwg_update_xbox_rps(cnodeid_t cnode, int rps_state)
+{
+ devfs_handle_t node_vertex;
+ devfs_handle_t rpsv;
+ invent_rpsinfo_t *rps_invent;
+ int rc;
+ if(cnode == CNODEID_NONE)
+ return;
+
+ node_vertex = cnodeid_to_vertex(cnode);
+ rc = hwgraph_edge_get(node_vertex, EDGE_LBL_XBOX_RPS, &rpsv);
+ if (rc != GRAPH_SUCCESS) {
+ return;
+ }
+
+ rc = hwgraph_info_get_LBL(rpsv, INFO_LBL_DETAIL_INVENT,
+ (arbitrary_info_t *)&rps_invent);
+ if (rc != GRAPH_SUCCESS) {
+ return;
+ }
+
+ if (rps_state == 0 )
+ rps_invent->ir_gen.ig_flag = 0;
+ else
+ rps_invent->ir_gen.ig_flag = INVENT_ENABLED;
+}
+
+#endif /* ndef BRINGUP */
+
+void
+klhwg_add_xbow(cnodeid_t cnode, nasid_t nasid)
+{
+ lboard_t *brd;
+ klxbow_t *xbow_p;
+ nasid_t hub_nasid;
+ cnodeid_t hub_cnode;
+ int widgetnum;
+ devfs_handle_t xbow_v, hubv;
+ /*REFERENCED*/
+ graph_error_t err;
+
+#if CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 || defined(CONFIG_IA64_GENERIC)
+ if ((brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid),
+ KLTYPE_PBRICK_XBOW)) == NULL)
+ return;
+#endif
+
+ if (KL_CONFIG_DUPLICATE_BOARD(brd))
+ return;
+
+ GRPRINTF(("klhwg_add_xbow: adding cnode %d nasid %d xbow edges\n",
+ cnode, nasid));
+
+ if ((xbow_p = (klxbow_t *)find_component(brd, NULL, KLSTRUCT_XBOW))
+ == NULL)
+ return;
+
+#ifndef CONFIG_IA64_SGI_IO
+ /*
+ * We cannot support this function in devfs .. see below where
+ * we use hwgraph_path_add() to create this vertex with a known
+ * name.
+ */
+ err = hwgraph_vertex_create(&xbow_v);
+ ASSERT(err == GRAPH_SUCCESS);
+
+ xswitch_vertex_init(xbow_v);
+#endif /* !CONFIG_IA64_SGI_IO */
+
+ for (widgetnum = HUB_WIDGET_ID_MIN; widgetnum <= HUB_WIDGET_ID_MAX; widgetnum++) {
+ if (!XBOW_PORT_TYPE_HUB(xbow_p, widgetnum))
+ continue;
+
+ hub_nasid = XBOW_PORT_NASID(xbow_p, widgetnum);
+ printk("klhwg_add_xbow: Found xbow port type hub hub_nasid %d widgetnum %d\n", hub_nasid, widgetnum);
+ if (hub_nasid == INVALID_NASID) {
+ cmn_err(CE_WARN, "hub widget %d, skipping xbow graph\n", widgetnum);
+ continue;
+ }
+
+ hub_cnode = NASID_TO_COMPACT_NODEID(hub_nasid);
+ printk("klhwg_add_xbow: cnode %d cnode %d\n", nasid_to_compact_node[0], nasid_to_compact_node[1]);
+
+ if (is_specified(arg_maxnodes) && hub_cnode == INVALID_CNODEID) {
+ continue;
+ }
+
+ hubv = cnodeid_to_vertex(hub_cnode);
+
+#ifdef CONFIG_IA64_SGI_IO
+ printk("klhwg_add_xbow: Hub Vertex found = %p hub_cnode %d\n", hubv, hub_cnode);
+ err = hwgraph_path_add(hubv, EDGE_LBL_XTALK, &xbow_v);
+ if (err != GRAPH_SUCCESS) {
+ if (err == GRAPH_DUP)
+ cmn_err(CE_WARN, "klhwg_add_xbow: Check for "
+ "working routers and router links!");
+
+ cmn_err(CE_GRPANIC, "klhwg_add_xbow: Failed to add "
+ "edge: vertex 0x%p (0x%p) to vertex 0x%p (0x%p),"
+ "error %d\n",
+ hubv, hubv, xbow_v, xbow_v, err);
+ }
+ xswitch_vertex_init(xbow_v);
+#endif
+
+ NODEPDA(hub_cnode)->xbow_vhdl = xbow_v;
+
+ /*
+ * XXX - This won't work is we ever hook up two hubs
+ * by crosstown through a crossbow.
+ */
+ if (hub_nasid != nasid) {
+ NODEPDA(hub_cnode)->xbow_peer = nasid;
+ NODEPDA(NASID_TO_COMPACT_NODEID(nasid))->xbow_peer =
+ hub_nasid;
+ }
+
+ GRPRINTF(("klhwg_add_xbow: adding port nasid %d %s to vertex 0x%p\n",
+ hub_nasid, EDGE_LBL_XTALK, hubv));
+
+#ifndef CONFIG_IA64_SGI_IO
+ err = hwgraph_edge_add(hubv, xbow_v, EDGE_LBL_XTALK);
+ if (err != GRAPH_SUCCESS) {
+ if (err == GRAPH_DUP)
+ cmn_err(CE_WARN, "klhwg_add_xbow: Check for "
+ "working routers and router links!");
+
+ cmn_err(CE_GRPANIC, "klhwg_add_xbow: Failed to add "
+ "edge: vertex 0x%p (0x%p) to vertex 0x%p (0x%p), "
+ "error %d\n",
+ hubv, hubv, xbow_v, xbow_v, err);
+ }
+#endif
+ }
+}
+
+
+/* ARGSUSED */
+void
+klhwg_add_node(devfs_handle_t hwgraph_root, cnodeid_t cnode, gda_t *gdap)
+{
+ nasid_t nasid;
+ lboard_t *brd;
+ klhub_t *hub;
+ devfs_handle_t node_vertex = NULL;
+ char path_buffer[100];
+ int rv;
+ char *s;
+ int board_disabled = 0;
+
+ nasid = COMPACT_TO_NASID_NODEID(cnode);
+ brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_IP27);
+ GRPRINTF(("klhwg_add_node: Adding cnode %d, nasid %d, brd 0x%p\n",
+ cnode, nasid, brd));
+ ASSERT(brd);
+
+ do {
+
+ /* Generate a hardware graph path for this board. */
+ board_to_path(brd, path_buffer);
+
+ GRPRINTF(("klhwg_add_node: adding %s to vertex 0x%p\n",
+ path_buffer, hwgraph_root));
+ rv = hwgraph_path_add(hwgraph_root, path_buffer, &node_vertex);
+
+ printk("klhwg_add_node: rv = %d graph success %d node_vertex 0x%p\n", rv, GRAPH_SUCCESS, node_vertex);
+ if (rv != GRAPH_SUCCESS)
+ cmn_err(CE_PANIC, "Node vertex creation failed. "
+ "Path == %s",
+ path_buffer);
+
+ hub = (klhub_t *)find_first_component(brd, KLSTRUCT_HUB);
+ ASSERT(hub);
+ if(hub->hub_info.flags & KLINFO_ENABLE)
+ board_disabled = 0;
+ else
+ board_disabled = 1;
+
+ if(!board_disabled) {
+ mark_nodevertex_as_node(node_vertex,
+ cnode + board_disabled * numnodes);
+ printk("klhwg_add_node: node_vertex %p, cnode %d numnodes %d\n", node_vertex, cnode, numnodes);
+
+ s = dev_to_name(node_vertex, path_buffer, sizeof(path_buffer));
+ printk("klhwg_add_node: s %s\n", s);
+
+ NODEPDA(cnode)->hwg_node_name =
+ kmalloc(strlen(s) + 1,
+ GFP_KERNEL);
+ ASSERT_ALWAYS(NODEPDA(cnode)->hwg_node_name != NULL);
+ strcpy(NODEPDA(cnode)->hwg_node_name, s);
+
+ hubinfo_set(node_vertex, NODEPDA(cnode)->pdinfo);
+
+ /* Set up node board's slot */
+ NODEPDA(cnode)->slotdesc = brd->brd_slot;
+
+ /* Set up the module we're in */
+ NODEPDA(cnode)->module_id = brd->brd_module;
+ NODEPDA(cnode)->module = module_lookup(brd->brd_module);
+ }
+
+ if(!board_disabled)
+ klhwg_add_hub(node_vertex, hub, cnode);
+
+ brd = KLCF_NEXT(brd);
+ if (brd)
+ brd = find_lboard(brd, KLTYPE_IP27);
+ else
+ break;
+ } while(brd);
+}
+
+
+/* ARGSUSED */
+void
+klhwg_add_all_routers(devfs_handle_t hwgraph_root)
+{
+ nasid_t nasid;
+ cnodeid_t cnode;
+ lboard_t *brd;
+ devfs_handle_t node_vertex;
+ char path_buffer[100];
+ int rv;
+
+ for (cnode = 0; cnode < maxnodes; cnode++) {
+ nasid = COMPACT_TO_NASID_NODEID(cnode);
+
+ GRPRINTF(("klhwg_add_all_routers: adding router on cnode %d\n",
+ cnode));
+
+ brd = find_lboard_class((lboard_t *)KL_CONFIG_INFO(nasid),
+ KLTYPE_ROUTER);
+
+ if (!brd)
+ /* No routers stored in this node's memory */
+ continue;
+
+ do {
+ ASSERT(brd);
+ GRPRINTF(("Router board struct is %p\n", brd));
+
+ /* Don't add duplicate boards. */
+ if (brd->brd_flags & DUPLICATE_BOARD)
+ continue;
+
+ GRPRINTF(("Router 0x%p module number is %d\n", brd, brd->brd_module));
+ /* Generate a hardware graph path for this board. */
+ board_to_path(brd, path_buffer);
+
+ GRPRINTF(("Router path is %s\n", path_buffer));
+
+ /* Add the router */
+ GRPRINTF(("klhwg_add_all_routers: adding %s to vertex 0x%p\n",
+ path_buffer, hwgraph_root));
+ rv = hwgraph_path_add(hwgraph_root, path_buffer, &node_vertex);
+
+ if (rv != GRAPH_SUCCESS)
+ cmn_err(CE_PANIC, "Router vertex creation "
+ "failed. Path == %s",
+ path_buffer);
+
+ GRPRINTF(("klhwg_add_all_routers: get next board from 0x%p\n",
+ brd));
+ /* Find the rest of the routers stored on this node. */
+ } while ( (brd = find_lboard_class(KLCF_NEXT(brd),
+ KLTYPE_ROUTER)) );
+
+ GRPRINTF(("klhwg_add_all_routers: Done.\n"));
+ }
+
+}
+
+/* ARGSUSED */
+void
+klhwg_connect_one_router(devfs_handle_t hwgraph_root, lboard_t *brd,
+ cnodeid_t cnode, nasid_t nasid)
+{
+ klrou_t *router;
+ char path_buffer[50];
+ char dest_path[50];
+ devfs_handle_t router_hndl;
+ devfs_handle_t dest_hndl;
+ int rc;
+ int port;
+ lboard_t *dest_brd;
+
+ GRPRINTF(("klhwg_connect_one_router: Connecting router on cnode %d\n",
+ cnode));
+
+ /* Don't add duplicate boards. */
+ if (brd->brd_flags & DUPLICATE_BOARD) {
+ GRPRINTF(("klhwg_connect_one_router: Duplicate router 0x%p on cnode %d\n",
+ brd, cnode));
+ return;
+ }
+
+ /* Generate a hardware graph path for this board. */
+ board_to_path(brd, path_buffer);
+
+ rc = hwgraph_traverse(hwgraph_root, path_buffer, &router_hndl);
+
+ if (rc != GRAPH_SUCCESS && is_specified(arg_maxnodes))
+ return;
+
+ if (rc != GRAPH_SUCCESS)
+ cmn_err(CE_WARN, "Can't find router: %s", path_buffer);
+
+ /* We don't know what to do with multiple router components */
+ if (brd->brd_numcompts != 1) {
+ cmn_err(CE_PANIC,
+ "klhwg_connect_one_router: %d cmpts on router\n",
+ brd->brd_numcompts);
+ return;
+ }
+
+
+ /* Convert component 0 to klrou_t ptr */
+ router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd),
+ brd->brd_compts[0]);
+
+ for (port = 1; port <= MAX_ROUTER_PORTS; port++) {
+ /* See if the port's active */
+ if (router->rou_port[port].port_nasid == INVALID_NASID) {
+ GRPRINTF(("klhwg_connect_one_router: port %d inactive.\n",
+ port));
+ continue;
+ }
+ if (is_specified(arg_maxnodes) && NASID_TO_COMPACT_NODEID(router->rou_port[port].port_nasid)
+ == INVALID_CNODEID) {
+ continue;
+ }
+
+ dest_brd = (lboard_t *)NODE_OFFSET_TO_K0(
+ router->rou_port[port].port_nasid,
+ router->rou_port[port].port_offset);
+
+ /* Generate a hardware graph path for this board. */
+ board_to_path(dest_brd, dest_path);
+
+ rc = hwgraph_traverse(hwgraph_root, dest_path, &dest_hndl);
+
+ if (rc != GRAPH_SUCCESS) {
+ if (is_specified(arg_maxnodes) && KL_CONFIG_DUPLICATE_BOARD(dest_brd))
+ continue;
+ cmn_err(CE_PANIC, "Can't find router: %s", dest_path);
+ }
+ GRPRINTF(("klhwg_connect_one_router: Link from %s/%d to %s\n",
+ path_buffer, port, dest_path));
+
+ sprintf(dest_path, "%d", port);
+
+ rc = hwgraph_edge_add(router_hndl, dest_hndl, dest_path);
+
+ if (rc == GRAPH_DUP) {
+ GRPRINTF(("Skipping port %d. nasid %d %s/%s\n",
+ port, router->rou_port[port].port_nasid,
+ path_buffer, dest_path));
+ continue;
+ }
+
+ if (rc != GRAPH_SUCCESS && !is_specified(arg_maxnodes))
+ cmn_err(CE_GRPANIC, "Can't create edge: %s/%s to vertex 0x%p error 0x%x\n",
+ path_buffer, dest_path, dest_hndl, rc);
+
+ }
+}
+
+
+void
+klhwg_connect_routers(devfs_handle_t hwgraph_root)
+{
+ nasid_t nasid;
+ cnodeid_t cnode;
+ lboard_t *brd;
+
+ for (cnode = 0; cnode < maxnodes; cnode++) {
+ nasid = COMPACT_TO_NASID_NODEID(cnode);
+
+ GRPRINTF(("klhwg_connect_routers: Connecting routers on cnode %d\n",
+ cnode));
+
+ brd = find_lboard_class((lboard_t *)KL_CONFIG_INFO(nasid),
+ KLTYPE_ROUTER);
+
+ if (!brd)
+ continue;
+
+ do {
+
+ nasid = COMPACT_TO_NASID_NODEID(cnode);
+
+ klhwg_connect_one_router(hwgraph_root, brd,
+ cnode, nasid);
+
+ /* Find the rest of the routers stored on this node. */
+ } while ( (brd = find_lboard_class(KLCF_NEXT(brd), KLTYPE_ROUTER)) );
+ }
+}
+
+
+
+void
+klhwg_connect_hubs(devfs_handle_t hwgraph_root)
+{
+ nasid_t nasid;
+ cnodeid_t cnode;
+ lboard_t *brd;
+ klhub_t *hub;
+ lboard_t *dest_brd;
+ devfs_handle_t hub_hndl;
+ devfs_handle_t dest_hndl;
+ char path_buffer[50];
+ char dest_path[50];
+ graph_error_t rc;
+
+ for (cnode = 0; cnode < maxnodes; cnode++) {
+ nasid = COMPACT_TO_NASID_NODEID(cnode);
+
+ GRPRINTF(("klhwg_connect_hubs: Connecting hubs on cnode %d\n",
+ cnode));
+
+ brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid),
+ KLTYPE_IP27);
+ ASSERT(brd);
+
+ hub = (klhub_t *)find_first_component(brd, KLSTRUCT_HUB);
+ ASSERT(hub);
+
+ /* See if the port's active */
+ if (hub->hub_port.port_nasid == INVALID_NASID) {
+ GRPRINTF(("klhwg_connect_hubs: port inactive.\n"));
+ continue;
+ }
+
+ if (is_specified(arg_maxnodes) && NASID_TO_COMPACT_NODEID(hub->hub_port.port_nasid) == INVALID_CNODEID)
+ continue;
+
+ /* Generate a hardware graph path for this board. */
+ board_to_path(brd, path_buffer);
+
+ GRPRINTF(("klhwg_connect_hubs: Hub path is %s.\n", path_buffer));
+ rc = hwgraph_traverse(hwgraph_root, path_buffer, &hub_hndl);
+
+ if (rc != GRAPH_SUCCESS)
+ cmn_err(CE_WARN, "Can't find hub: %s", path_buffer);
+
+ dest_brd = (lboard_t *)NODE_OFFSET_TO_K0(
+ hub->hub_port.port_nasid,
+ hub->hub_port.port_offset);
+
+ /* Generate a hardware graph path for this board. */
+ board_to_path(dest_brd, dest_path);
+
+ rc = hwgraph_traverse(hwgraph_root, dest_path, &dest_hndl);
+
+ if (rc != GRAPH_SUCCESS) {
+ if (is_specified(arg_maxnodes) && KL_CONFIG_DUPLICATE_BOARD(dest_brd))
+ continue;
+ cmn_err(CE_PANIC, "Can't find board: %s", dest_path);
+ } else {
+
+
+ GRPRINTF(("klhwg_connect_hubs: Link from %s to %s.\n",
+ path_buffer, dest_path));
+
+ rc = hwgraph_edge_add(hub_hndl, dest_hndl, EDGE_LBL_INTERCONNECT);
+
+ if (rc != GRAPH_SUCCESS)
+ cmn_err(CE_GRPANIC, "Can't create edge: %s/%s to vertex 0x%p, error 0x%x\n",
+ path_buffer, dest_path, dest_hndl, rc);
+
+ }
+ }
+}
+
+/* Store the pci/vme disabled board information as extended administrative
+ * hints which can later be used by the drivers using the device/driver
+ * admin interface.
+ */
+void
+klhwg_device_disable_hints_add(void)
+{
+ cnodeid_t cnode; /* node we are looking at */
+ nasid_t nasid; /* nasid of the node */
+ lboard_t *board; /* board we are looking at */
+ int comp_index; /* component index */
+ klinfo_t *component; /* component in the board we are
+ * looking at
+ */
+ char device_name[MAXDEVNAME];
+
+#ifndef CONFIG_IA64_SGI_IO
+ device_admin_table_init();
+#endif
+ for(cnode = 0; cnode < numnodes; cnode++) {
+ nasid = COMPACT_TO_NASID_NODEID(cnode);
+ board = (lboard_t *)KL_CONFIG_INFO(nasid);
+ /* Check out all the board info stored on a node */
+ while(board) {
+ /* No need to look at duplicate boards or non-io
+ * boards
+ */
+ if (KL_CONFIG_DUPLICATE_BOARD(board) ||
+ KLCLASS(board->brd_type) != KLCLASS_IO) {
+ board = KLCF_NEXT(board);
+ continue;
+ }
+ /* Check out all the components of a board */
+ for (comp_index = 0;
+ comp_index < KLCF_NUM_COMPS(board);
+ comp_index++) {
+ component = KLCF_COMP(board,comp_index);
+ /* If the component is enabled move on to
+ * the next component
+ */
+ if (KLCONFIG_INFO_ENABLED(component))
+ continue;
+ /* NOTE : Since the prom only supports
+ * the disabling of pci devices the following
+ * piece of code makes sense.
+ * Make sure that this assumption is valid
+ */
+ /* This component is disabled. Store this
+ * hint in the extended device admin table
+ */
+ /* Get the canonical name of the pci device */
+ device_component_canonical_name_get(board,
+ component,
+ device_name);
+#ifndef CONFIG_IA64_SGI_IO
+ device_admin_table_update(device_name,
+ ADMIN_LBL_DISABLED,
+ "yes");
+#endif
+#ifdef DEBUG
+ printf("%s DISABLED\n",device_name);
+#endif
+ }
+ /* go to the next board info stored on this
+ * node
+ */
+ board = KLCF_NEXT(board);
+ }
+ }
+}
+
+void
+klhwg_add_all_modules(devfs_handle_t hwgraph_root)
+{
+ cmoduleid_t cm;
+ char name[128];
+ devfs_handle_t vhdl;
+ int rc;
+
+ /* Add devices under each module */
+
+ for (cm = 0; cm < nummodules; cm++) {
+ /* Use module as module vertex fastinfo */
+
+ sprintf(name, EDGE_LBL_MODULE "/%x", modules[cm]->id);
+
+ rc = hwgraph_path_add(hwgraph_root, name, &vhdl);
+ ASSERT(rc == GRAPH_SUCCESS);
+ rc = rc;
+
+ hwgraph_fastinfo_set(vhdl, (arbitrary_info_t) modules[cm]);
+
+ /* Add system controller */
+
+ sprintf(name,
+ EDGE_LBL_MODULE "/%x/" EDGE_LBL_L1,
+ modules[cm]->id);
+
+ rc = hwgraph_path_add(hwgraph_root, name, &vhdl);
+ ASSERT_ALWAYS(rc == GRAPH_SUCCESS);
+ rc = rc;
+
+ hwgraph_info_add_LBL(vhdl,
+ INFO_LBL_ELSC,
+ (arbitrary_info_t) (__psint_t) 1);
+
+#ifndef CONFIG_IA64_SGI_IO
+ sndrv_attach(vhdl);
+#else
+ /*
+ * We need to call the drivers attach routine ..
+ */
+ FIXME("klhwg_add_all_modules: Need code to call driver attach.\n");
+#endif
+ }
+}
+
+void
+klhwg_add_all_nodes(devfs_handle_t hwgraph_root)
+{
+ //gda_t *gdap = GDA;
+ gda_t *gdap;
+ cnodeid_t cnode;
+
+#ifdef SIMULATED_KLGRAPH
+ //gdap = 0xa800000000011000;
+ gdap = (gda_t *)0xe000000000011000;
+ printk("klhwg_add_all_nodes: SIMULATED_KLGRAPH FIXME: gdap= 0x%p\n", gdap);
+#else
+ gdap = GDA;
+#endif /* SIMULATED_KLGRAPH */
+ for (cnode = 0; cnode < numnodes; cnode++) {
+ ASSERT(gdap->g_nasidtable[cnode] != INVALID_NASID);
+ klhwg_add_node(hwgraph_root, cnode, gdap);
+ }
+
+ for (cnode = 0; cnode < numnodes; cnode++) {
+ ASSERT(gdap->g_nasidtable[cnode] != INVALID_NASID);
+
+#ifndef CONFIG_IA64_SGI_IO
+ klhwg_add_xbow(cnode, gdap->g_nasidtable[cnode]);
+#else
+ printk("klhwg_add_all_nodes: Fix me by getting real nasid\n");
+ klhwg_add_xbow(cnode, 0);
+#endif
+ }
+
+ /*
+ * As for router hardware inventory information, we set this
+ * up in router.c.
+ */
+
+ klhwg_add_all_routers(hwgraph_root);
+ klhwg_connect_routers(hwgraph_root);
+ klhwg_connect_hubs(hwgraph_root);
+
+ /* Assign guardian nodes to each of the
+ * routers in the system.
+ */
+
+#ifndef CONFIG_IA64_SGI_IO
+ router_guardians_set(hwgraph_root);
+#endif
+
+ /* Go through the entire system's klconfig
+ * to figure out which pci components have been disabled
+ */
+ klhwg_device_disable_hints_add();
+
+}
diff --git a/arch/ia64/sn/io/klgraph_hack.c b/arch/ia64/sn/io/klgraph_hack.c
new file mode 100644
index 000000000..cc9d77871
--- /dev/null
+++ b/arch/ia64/sn/io/klgraph_hack.c
@@ -0,0 +1,847 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+
+/*
+ * This is a temporary file that statically initializes the expected
+ * initial klgraph information that is normally provided by prom.
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/klconfig.h>
+
+void * real_port;
+void * real_io_base;
+void * real_addr;
+
+char *BW0 = NULL;
+
+kl_config_hdr_t *linux_klcfg;
+
+#ifdef BRINGUP
+/* forward declarations */
+extern void dump_ii(void), dump_lb(void), dump_crossbow(void);
+extern void clear_ii_error(void);
+#endif /* BRINGUP */
+
+void
+simulated_BW0_init(void)
+{
+
+ unsigned long *cnode0_hub;
+ unsigned long hub_widget = 0x1000000;
+ unsigned long hub_offset = 0x800000;
+ unsigned long hub_reg_base = 0;
+ extern void * vmalloc(unsigned long);
+
+ memset(&nasid_to_compact_node[0], 0, sizeof(cnodeid_t) * MAX_NASIDS);
+
+ BW0 = vmalloc(0x10000000);
+ if (BW0 == NULL) {
+ printk("Darn it .. cannot create space for Big Window 0\n");
+ }
+ printk("BW0: Start Address %p\n", BW0);
+
+ memset(BW0+(0x10000000 - 8), 0xf, 0x8);
+
+ printk("BW0: Last WORD address %p has value 0x%lx\n", (char *)(BW0 +(0x10000000 - 8)), *(long *)(BW0 +(0x10000000 - 8)));
+
+ printk("XWIDGET 8 Address = 0x%p\n", (unsigned long *)(NODE_SWIN_BASE(0, 8)) );
+
+ /*
+ * Do some HUB Register Hack ..
+ */
+ hub_reg_base = (unsigned long)BW0 + hub_widget + hub_offset;
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_WID); *cnode0_hub = 0x1c110049;
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_WSTAT); *cnode0_hub = 0x0;
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_WCR); *cnode0_hub = 0x401b;
+ printk("IIO_WCR address = 0x%p\n", cnode0_hub);
+
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_ILAPR); *cnode0_hub = 0xffffffffffffffff;
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_ILAPO); *cnode0_hub = 0x0;
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_IOWA); *cnode0_hub = 0xff01;
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_IIWA); *cnode0_hub = 0xff01;
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_IIDEM); *cnode0_hub = 0xffffffffffffffff;
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_ILCSR); *cnode0_hub = 0x3fc03ff640a;
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_ILLR); *cnode0_hub = 0x0;
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_IIDSR); *cnode0_hub = 0x1000040;
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_IGFX0); *cnode0_hub = 0x0;
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_IGFX1); *cnode0_hub = 0x0;
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_ISCR0); *cnode0_hub = 0x23d;
+ cnode0_hub = (unsigned long *)(hub_reg_base + IIO_ISCR1); *cnode0_hub = 0x0;
+#endif /* CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 */
+}
+
+#define SYNERGY_WIDGET ((char *)0xc0000e0000000000)
+#define SYNERGY_SWIZZLE ((char *)0xc0000e0000000400)
+#define HUBREG ((char *)0xc0000a0001e00000)
+#define WIDGET0 ((char *)0xc0000a0000000000)
+#define WIDGET4 ((char *)0xc0000a0000000004)
+
+#define SYNERGY_WIDGET ((char *)0xc0000e0000000000)
+#define SYNERGY_SWIZZLE ((char *)0xc0000e0000000400)
+#define HUBREG ((char *)0xc0000a0001e00000)
+#define WIDGET0 ((char *)0xc0000a0000000000)
+
+int test = 0;
+
+/*
+ * Hack to loop for test.
+ */
+void
+test_io_regs(void)
+{
+
+ uint32_t reg_32bits;
+ uint64_t reg_64bits;
+
+ while (test) {
+
+ reg_32bits = (uint32_t)(*(volatile uint32_t *) SYNERGY_WIDGET);
+ reg_64bits = (uint64_t) (*(volatile uint64_t *) SYNERGY_WIDGET);
+
+ }
+
+ printk("Synergy Widget Address = 0x%p, Value = 0x%lx\n", SYNERGY_WIDGET, (uint64_t)*(SYNERGY_WIDGET));
+
+ printk("Synergy swizzle Address = 0x%p, Value = 0x%lx\n", SYNERGY_SWIZZLE, (uint64_t)*(SYNERGY_SWIZZLE));
+ printk("HUBREG Address = 0x%p, Value = 0x%lx\n", HUBREG, (uint64_t)*(HUBREG));
+ printk("WIDGET0 Address = 0x%p, Value = 0x%lx\n", WIDGET0, (uint64_t)*(WIDGET0));
+ printk("WIDGET4 Address = 0x%p, Value = 0x%x\n", WIDGET4, (uint32_t)*(WIDGET4));
+
+}
+
+void
+klgraph_hack_init(void)
+{
+
+ kl_config_hdr_t *kl_hdr_ptr;
+ lboard_t *lb_ptr;
+ lboard_t *temp_ptr;
+ klhub_t *klhub_ptr;
+ klioc3_t *klioc3_ptr;
+ klbri_t *klbri_ptr;
+ klxbow_t *klxbow_ptr;
+ klinfo_t *klinfo_ptr;
+ klcomp_t *klcomp_ptr;
+ uint64_t *tmp;
+ volatile u32 *tmp32;
+
+#ifdef 0
+ /* Preset some values */
+ /* Write IOERR clear to clear the CRAZY bit in the status */
+ tmp = (uint64_t *)0xc0000a0001c001f8; *tmp = (uint64_t)0xffffffff;
+ /* set widget control register...setting bedrock widget id to b */
+ /* tmp = (uint64_t *)0xc0000a0001c00020; *tmp = (uint64_t)0x801b; */
+ /* set io outbound widget access...allow all */
+ tmp = (uint64_t *)0xc0000a0001c00110; *tmp = (uint64_t)0xff01;
+ /* set io inbound widget access...allow all */
+ tmp = (uint64_t *)0xc0000a0001c00118; *tmp = (uint64_t)0xff01;
+ /* set io crb timeout to max */
+ tmp = (uint64_t *)0xc0000a0001c003c0; *tmp = (uint64_t)0xffffff;
+ tmp = (uint64_t *)0xc0000a0001c003c0; *tmp = (uint64_t)0xffffff;
+
+ /* set local block io permission...allow all */
+ tmp = (uint64_t *)0xc0000a0001e04010; *tmp = (uint64_t)0xfffffffffffffff;
+
+ /* clear any errors */
+ clear_ii_error();
+
+ /* set default read response buffers in bridge */
+ tmp32 = (volatile u32 *)0xc0000a000f000280L;
+ *tmp32 = 0xba98;
+ tmp32 = (volatile u32 *)0xc0000a000f000288L;
+ *tmp32 = 0xba98;
+#endif
+
+printk("Widget ID Address 0x%p Value 0x%lx\n", (uint64_t *)0xc0000a0001e00000, *( (volatile uint64_t *)0xc0000a0001e00000) );
+
+printk("Widget ID Address 0x%p Value 0x%lx\n", (uint64_t *)0xc0000a0001c00000, *( (volatile uint64_t *)0xc0000a0001c00000) );
+
+printk("Widget ID Address 0x%p Value 0x%lx\n", (uint64_t *)0xc000020001e00000, *( (volatile uint64_t *)0xc000020001e00000) );
+
+
+printk("Widget ID Address 0x%p Value 0x%lx\n", (uint64_t *)0xc000020001c00000, *( (volatile uint64_t *)0xc000020001c00000) );
+
+printk("Widget ID Address 0x%p Value 0x%lx\n", (uint64_t *)0xc0000a0001e00000, *( (volatile uint64_t *)0xc0000a0001e00000) );
+
+printk("Xbow ID Address 0x%p Value 0x%x\n", (uint64_t *)0xc0000a0000000000, *( (volatile uint32_t *)0xc0000a0000000000) );
+
+printk("Xbow ID Address 0x%p Value 0x%x\n", (uint64_t *)0xc000020000000004, *( (volatile uint32_t *)0xc000020000000004) );
+
+
+ if ( test )
+ test_io_regs();
+ /*
+ * Klconfig header.
+ */
+ kl_hdr_ptr = kmalloc(sizeof(kl_config_hdr_t), GFP_KERNEL);
+ kl_hdr_ptr->ch_magic = 0xbeedbabe;
+ kl_hdr_ptr->ch_version = 0x0;
+ kl_hdr_ptr->ch_malloc_hdr_off = 0x48;
+ kl_hdr_ptr->ch_cons_off = 0x18;
+ kl_hdr_ptr->ch_board_info = 0x0;
+ kl_hdr_ptr->ch_cons_info.uart_base = 0x920000000f820178;
+ kl_hdr_ptr->ch_cons_info.config_base = 0x920000000f024000;
+ kl_hdr_ptr->ch_cons_info.memory_base = 0x920000000f800000;
+ kl_hdr_ptr->ch_cons_info.baud = 0x2580;
+ kl_hdr_ptr->ch_cons_info.flag = 0x1;
+ kl_hdr_ptr->ch_cons_info.type = 0x300fafa;
+ kl_hdr_ptr->ch_cons_info.nasid = 0x0;
+ kl_hdr_ptr->ch_cons_info.wid = 0xf;
+ kl_hdr_ptr->ch_cons_info.npci = 0x4;
+ kl_hdr_ptr->ch_cons_info.baseio_nic = 0x0;
+
+ /*
+ * We need to know whether we are booting from PROM or
+ * boot from disk.
+ */
+ linux_klcfg = (kl_config_hdr_t *)0xe000000000030000;
+ if (linux_klcfg->ch_magic == 0xbeedbabe) {
+ printk("Linux Kernel Booted from Disk\n");
+ } else {
+ printk("Linux Kernel Booted from PROM\n");
+ linux_klcfg = kl_hdr_ptr;
+ }
+
+ /*
+ * lboard KLTYPE_IP35
+ */
+ lb_ptr = kmalloc(sizeof(lboard_t), GFP_KERNEL);
+ kl_hdr_ptr->ch_board_info = (klconf_off_t) lb_ptr;
+ temp_ptr = lb_ptr;
+ printk("First Lboard = %p\n", temp_ptr);
+
+ lb_ptr->brd_next = 0;
+ lb_ptr->struct_type = 0x1;
+ lb_ptr->brd_type = 0x11;
+ lb_ptr->brd_sversion = 0x3;
+ lb_ptr->brd_brevision = 0x1;
+ lb_ptr->brd_promver = 0x1;
+ lb_ptr->brd_promver = 0x1;
+ lb_ptr->brd_slot = 0x0;
+ lb_ptr->brd_debugsw = 0x0;
+ lb_ptr->brd_module = 0x145;
+ lb_ptr->brd_partition = 0x0;
+ lb_ptr->brd_diagval = 0x0;
+ lb_ptr->brd_diagparm = 0x0;
+ lb_ptr->brd_inventory = 0x0;
+ lb_ptr->brd_numcompts = 0x5;
+ lb_ptr->brd_nic = 0x2a0aed35;
+ lb_ptr->brd_nasid = 0x0;
+ lb_ptr->brd_errinfo = 0x0;
+ lb_ptr->brd_parent = 0x0;
+ lb_ptr->brd_graph_link = (devfs_handle_t)0x26;
+ lb_ptr->brd_owner = 0x0;
+ lb_ptr->brd_nic_flags = 0x0;
+ memcpy(&lb_ptr->brd_name[0], "IP35", 4);
+
+ /*
+ * Hub Component
+ */
+ klcomp_ptr = kmalloc(sizeof(klcomp_t), GFP_KERNEL);
+ klhub_ptr = (klhub_t *)klcomp_ptr;
+ klinfo_ptr = (klinfo_t *)klcomp_ptr;
+ lb_ptr->brd_compts[0] = (klconf_off_t)klcomp_ptr;
+ printk("hub info = %p lboard = %p\n", klhub_ptr, lb_ptr);
+
+ klinfo_ptr = (klinfo_t *)klhub_ptr;
+ klinfo_ptr->struct_type = 0x2;
+ klinfo_ptr->struct_version = 0x1;
+ klinfo_ptr->flags = 0x1;
+ klinfo_ptr->revision = 0x1;
+ klinfo_ptr->diagval = 0x0;
+ klinfo_ptr->diagparm = 0x0;
+ klinfo_ptr->inventory = 0x0;
+ klinfo_ptr->partid = 0x0;
+ klinfo_ptr->nic = 0x2a0aed35;
+ klinfo_ptr->physid = 0x0;
+ klinfo_ptr->virtid = 0x0;
+ klinfo_ptr->widid = 0x0;
+ klinfo_ptr->nasid = 0x0;
+
+ klhub_ptr->hub_flags = 0x0;
+ klhub_ptr->hub_port.port_nasid = (nasid_t)0x0ffffffff;
+ klhub_ptr->hub_port.port_flag = 0x0;
+ klhub_ptr->hub_port.port_offset = 0x0;
+ klhub_ptr->hub_box_nic = 0x0;
+ klhub_ptr->hub_mfg_nic = 0x3f420;
+ klhub_ptr->hub_speed = 0xbebc200;
+
+ /*
+ * Memory Component
+ */
+ klcomp_ptr = kmalloc(sizeof(klcomp_t), GFP_KERNEL);
+ klinfo_ptr = (klinfo_t *)klcomp_ptr;
+ lb_ptr->brd_compts[1] = (klconf_off_t)klcomp_ptr;
+
+ klinfo_ptr->struct_type = 0x3;
+ klinfo_ptr->struct_version = 0x2;
+ klinfo_ptr->flags = 0x1;
+ klinfo_ptr->revision = 0xff;
+ klinfo_ptr->diagval = 0x0;
+ klinfo_ptr->diagparm = 0x0;
+ klinfo_ptr->inventory = 0x0;
+ klinfo_ptr->partid = 0x0;
+ klinfo_ptr->nic = 0xffffffffffffffff;
+ klinfo_ptr->physid = 0xff;
+ klinfo_ptr->virtid = 0xffffffff;
+ klinfo_ptr->widid = 0x0;
+ klinfo_ptr->nasid = 0x0;
+
+ /*
+ * KLSTRUCT_HUB_UART Component
+ */
+ klcomp_ptr = kmalloc(sizeof(klcomp_t), GFP_KERNEL);
+ klinfo_ptr = (klinfo_t *)klcomp_ptr;
+ lb_ptr->brd_compts[2] = (klconf_off_t)klcomp_ptr;
+
+ klinfo_ptr->struct_type = 0x11;
+ klinfo_ptr->struct_version = 0x1;
+ klinfo_ptr->flags = 0x31;
+ klinfo_ptr->revision = 0xff;
+ klinfo_ptr->diagval = 0x0;
+ klinfo_ptr->diagparm = 0x0;
+ klinfo_ptr->inventory = 0x0;
+ klinfo_ptr->partid = 0x0;
+ klinfo_ptr->nic = 0xffffffffffffffff;
+ klinfo_ptr->physid = 0x0;
+ klinfo_ptr->virtid = 0x0;
+ klinfo_ptr->widid = 0x0;
+ klinfo_ptr->nasid = 0x0;
+
+ /*
+ * KLSTRUCT_CPU Component
+ */
+ klcomp_ptr = kmalloc(sizeof(klcomp_t), GFP_KERNEL);
+ klinfo_ptr = (klinfo_t *)klcomp_ptr;
+ lb_ptr->brd_compts[3] = (klconf_off_t)klcomp_ptr;
+
+ klinfo_ptr->struct_type = 0x1;
+ klinfo_ptr->struct_version = 0x2;
+ klinfo_ptr->flags = 0x1;
+ klinfo_ptr->revision = 0xff;
+ klinfo_ptr->diagval = 0x0;
+ klinfo_ptr->diagparm = 0x0;
+ klinfo_ptr->inventory = 0x0;
+ klinfo_ptr->partid = 0x0;
+ klinfo_ptr->nic = 0xffffffffffffffff;
+ klinfo_ptr->physid = 0x0;
+ klinfo_ptr->virtid = 0x0;
+ klinfo_ptr->widid = 0x0;
+ klinfo_ptr->nasid = 0x0;
+
+ /*
+ * KLSTRUCT_CPU Component
+ */
+ klcomp_ptr = kmalloc(sizeof(klcomp_t), GFP_KERNEL);
+ klinfo_ptr = (klinfo_t *)klcomp_ptr;
+ lb_ptr->brd_compts[4] = (klconf_off_t)klcomp_ptr;
+
+ klinfo_ptr->struct_type = 0x1;
+ klinfo_ptr->struct_version = 0x2;
+ klinfo_ptr->flags = 0x1;
+ klinfo_ptr->revision = 0xff;
+ klinfo_ptr->diagval = 0x0;
+ klinfo_ptr->diagparm = 0x0;
+ klinfo_ptr->inventory = 0x0;
+ klinfo_ptr->partid = 0x0;
+ klinfo_ptr->nic = 0xffffffffffffffff;
+ klinfo_ptr->physid = 0x1;
+ klinfo_ptr->virtid = 0x1;
+ klinfo_ptr->widid = 0x0;
+ klinfo_ptr->nasid = 0x0;
+
+ lb_ptr->brd_compts[5] = 0; /* Set the next one to 0 .. end */
+ lb_ptr->brd_numcompts = 5; /* 0 to 4 */
+
+ /*
+ * lboard(0x42) KLTYPE_PBRICK_XBOW
+ */
+ lb_ptr = kmalloc(sizeof(lboard_t), GFP_KERNEL);
+ temp_ptr->brd_next = (klconf_off_t)lb_ptr; /* Let the previous point at the new .. */
+ temp_ptr = lb_ptr;
+ printk("Second Lboard = %p\n", temp_ptr);
+
+ lb_ptr->brd_next = 0;
+ lb_ptr->struct_type = 0x1;
+ lb_ptr->brd_type = 0x42;
+ lb_ptr->brd_sversion = 0x2;
+ lb_ptr->brd_brevision = 0x0;
+ lb_ptr->brd_promver = 0x1;
+ lb_ptr->brd_promver = 0x1;
+ lb_ptr->brd_slot = 0x0;
+ lb_ptr->brd_debugsw = 0x0;
+ lb_ptr->brd_module = 0x145;
+ lb_ptr->brd_partition = 0x1;
+ lb_ptr->brd_diagval = 0x0;
+ lb_ptr->brd_diagparm = 0x0;
+ lb_ptr->brd_inventory = 0x0;
+ lb_ptr->brd_numcompts = 0x1;
+ lb_ptr->brd_nic = 0xffffffffffffffff;
+ lb_ptr->brd_nasid = 0x0;
+ lb_ptr->brd_errinfo = 0x0;
+ lb_ptr->brd_parent = (struct lboard_s *)0x9600000000030070;
+ lb_ptr->brd_graph_link = (devfs_handle_t)0xffffffff;
+ lb_ptr->brd_owner = 0x0;
+ lb_ptr->brd_nic_flags = 0x0;
+ memcpy(&lb_ptr->brd_name[0], "IOBRICK", 7);
+
+ /*
+ * KLSTRUCT_XBOW Component
+ */
+ klcomp_ptr = kmalloc(sizeof(klcomp_t), GFP_KERNEL);
+ memset(klcomp_ptr, 0, sizeof(klcomp_t));
+ klxbow_ptr = (klxbow_t *)klcomp_ptr;
+ klinfo_ptr = (klinfo_t *)klcomp_ptr;
+ lb_ptr->brd_compts[0] = (klconf_off_t)klcomp_ptr;
+ printk("xbow_p 0x%p\n", klcomp_ptr);
+
+ klinfo_ptr->struct_type = 0x4;
+ klinfo_ptr->struct_version = 0x1;
+ klinfo_ptr->flags = 0x1;
+ klinfo_ptr->revision = 0x2;
+ klinfo_ptr->diagval = 0x0;
+ klinfo_ptr->diagparm = 0x0;
+ klinfo_ptr->inventory = 0x0;
+ klinfo_ptr->partid = 0x0;
+ klinfo_ptr->nic = 0xffffffffffffffff;
+ klinfo_ptr->physid = 0xff;
+ klinfo_ptr->virtid = 0x0;
+ klinfo_ptr->widid = 0x0;
+ klinfo_ptr->nasid = 0x0;
+
+ klxbow_ptr->xbow_master_hub_link = 0xb;
+ klxbow_ptr->xbow_port_info[0].port_nasid = 0x0;
+ klxbow_ptr->xbow_port_info[0].port_flag = 0x0;
+ klxbow_ptr->xbow_port_info[0].port_offset = 0x0;
+
+ klxbow_ptr->xbow_port_info[1].port_nasid = 0x401;
+ klxbow_ptr->xbow_port_info[1].port_flag = 0x0;
+ klxbow_ptr->xbow_port_info[1].port_offset = 0x0;
+
+ klxbow_ptr->xbow_port_info[2].port_nasid = 0x0;
+ klxbow_ptr->xbow_port_info[2].port_flag = 0x0;
+ klxbow_ptr->xbow_port_info[2].port_offset = 0x0;
+
+ klxbow_ptr->xbow_port_info[3].port_nasid = 0x0; /* ffffffff */
+ klxbow_ptr->xbow_port_info[3].port_flag = 0x6;
+ klxbow_ptr->xbow_port_info[3].port_offset = 0x30070;
+
+ klxbow_ptr->xbow_port_info[4].port_nasid = 0x0; /* ffffff00; */
+ klxbow_ptr->xbow_port_info[4].port_flag = 0x0;
+ klxbow_ptr->xbow_port_info[4].port_offset = 0x0;
+
+ klxbow_ptr->xbow_port_info[5].port_nasid = 0x0;
+ klxbow_ptr->xbow_port_info[5].port_flag = 0x0;
+ klxbow_ptr->xbow_port_info[5].port_offset = 0x0;
+ klxbow_ptr->xbow_port_info[6].port_nasid = 0x0;
+ klxbow_ptr->xbow_port_info[6].port_flag = 0x5;
+ klxbow_ptr->xbow_port_info[6].port_offset = 0x30210;
+ klxbow_ptr->xbow_port_info[7].port_nasid = 0x3;
+ klxbow_ptr->xbow_port_info[7].port_flag = 0x5;
+ klxbow_ptr->xbow_port_info[7].port_offset = 0x302e0;
+
+ lb_ptr->brd_compts[1] = 0;
+ lb_ptr->brd_numcompts = 1;
+
+
+ /*
+ * lboard KLTYPE_PBRICK
+ */
+ lb_ptr = kmalloc(sizeof(lboard_t), GFP_KERNEL);
+ temp_ptr->brd_next = (klconf_off_t)lb_ptr; /* Let the previous point at the new .. */
+ temp_ptr = lb_ptr;
+ printk("Third Lboard %p\n", lb_ptr);
+
+ lb_ptr->brd_next = 0;
+ lb_ptr->struct_type = 0x1;
+ lb_ptr->brd_type = 0x72;
+ lb_ptr->brd_sversion = 0x2;
+ lb_ptr->brd_brevision = 0x0;
+ lb_ptr->brd_promver = 0x1;
+ lb_ptr->brd_promver = 0x41;
+ lb_ptr->brd_slot = 0xe;
+ lb_ptr->brd_debugsw = 0x0;
+ lb_ptr->brd_module = 0x145;
+ lb_ptr->brd_partition = 0x1;
+ lb_ptr->brd_diagval = 0x0;
+ lb_ptr->brd_diagparm = 0x0;
+ lb_ptr->brd_inventory = 0x0;
+ lb_ptr->brd_numcompts = 0x1;
+ lb_ptr->brd_nic = 0x30e3fd;
+ lb_ptr->brd_nasid = 0x0;
+ lb_ptr->brd_errinfo = 0x0;
+ lb_ptr->brd_parent = (struct lboard_s *)0x9600000000030140;
+ lb_ptr->brd_graph_link = (devfs_handle_t)0xffffffff;
+ lb_ptr->brd_owner = 0x0;
+ lb_ptr->brd_nic_flags = 0x0;
+ memcpy(&lb_ptr->brd_name[0], "IP35", 4);
+
+ /*
+ * KLSTRUCT_BRI Component
+ */
+ klcomp_ptr = kmalloc(sizeof(klcomp_t), GFP_KERNEL);
+ klbri_ptr = (klbri_t *)klcomp_ptr;
+ klinfo_ptr = (klinfo_t *)klcomp_ptr;
+ lb_ptr->brd_compts[0] = (klconf_off_t)klcomp_ptr;
+
+ klinfo_ptr->struct_type = 0x5;
+ klinfo_ptr->struct_version = 0x2;
+ klinfo_ptr->flags = 0x1;
+ klinfo_ptr->revision = 0x2;
+ klinfo_ptr->diagval = 0x0;
+ klinfo_ptr->diagparm = 0x0;
+ klinfo_ptr->inventory = 0x0;
+ klinfo_ptr->partid = 0xd002;
+ klinfo_ptr->nic = 0x30e3fd;
+ klinfo_ptr->physid = 0xe;
+ klinfo_ptr->virtid = 0xe;
+ klinfo_ptr->widid = 0xe;
+ klinfo_ptr->nasid = 0x0;
+
+ klbri_ptr->bri_eprominfo = 0xff;
+ klbri_ptr->bri_bustype = 0x7;
+ klbri_ptr->bri_mfg_nic = 0x3f4a8;
+
+ lb_ptr->brd_compts[1] = 0;
+ lb_ptr->brd_numcompts = 1;
+
+ /*
+ * lboard KLTYPE_PBRICK
+ */
+ lb_ptr = kmalloc(sizeof(lboard_t), GFP_KERNEL);
+ temp_ptr->brd_next = (klconf_off_t)lb_ptr; /* Let the previous point at the new .. */
+ temp_ptr = lb_ptr;
+ printk("Fourth Lboard %p\n", lb_ptr);
+
+ lb_ptr->brd_next = 0x0;
+ lb_ptr->struct_type = 0x1;
+ lb_ptr->brd_type = 0x72;
+ lb_ptr->brd_sversion = 0x2;
+ lb_ptr->brd_brevision = 0x0;
+ lb_ptr->brd_promver = 0x1;
+ lb_ptr->brd_promver = 0x31;
+ lb_ptr->brd_slot = 0xf;
+ lb_ptr->brd_debugsw = 0x0;
+ lb_ptr->brd_module = 0x145;
+ lb_ptr->brd_partition = 0x1;
+ lb_ptr->brd_diagval = 0x0;
+ lb_ptr->brd_diagparm = 0x0;
+ lb_ptr->brd_inventory = 0x0;
+ lb_ptr->brd_numcompts = 0x6;
+ lb_ptr->brd_nic = 0x30e3fd;
+ lb_ptr->brd_nasid = 0x0;
+ lb_ptr->brd_errinfo = 0x0;
+ lb_ptr->brd_parent = (struct lboard_s *)0x9600000000030140;
+ lb_ptr->brd_graph_link = (devfs_handle_t)0xffffffff;
+ lb_ptr->brd_owner = 0x0;
+ lb_ptr->brd_nic_flags = 0x0;
+ memcpy(&lb_ptr->brd_name[0], "IP35", 4);
+
+
+ /*
+ * KLSTRUCT_BRI Component
+ */
+ klcomp_ptr = kmalloc(sizeof(klcomp_t), GFP_KERNEL);
+ klbri_ptr = (klbri_t *)klcomp_ptr;
+ klinfo_ptr = (klinfo_t *)klcomp_ptr;
+ lb_ptr->brd_compts[0] = (klconf_off_t)klcomp_ptr;
+
+ klinfo_ptr->struct_type = 0x5;
+ klinfo_ptr->struct_version = 0x2;
+ klinfo_ptr->flags = 0x1;
+ klinfo_ptr->revision = 0x2;
+ klinfo_ptr->diagval = 0x0;
+ klinfo_ptr->diagparm = 0x0;
+ klinfo_ptr->inventory = 0x0;
+ klinfo_ptr->partid = 0xd002;
+ klinfo_ptr->nic = 0x30e3fd;
+ klinfo_ptr->physid = 0xf;
+ klinfo_ptr->virtid = 0xf;
+ klinfo_ptr->widid = 0xf;
+ klinfo_ptr->nasid = 0x0;
+
+ klbri_ptr->bri_eprominfo = 0xff;
+ klbri_ptr->bri_bustype = 0x7;
+ klbri_ptr->bri_mfg_nic = 0x3f528;
+
+ /*
+ * KLSTRUCT_SCSI component
+ */
+ klcomp_ptr = kmalloc(sizeof(klcomp_t), GFP_KERNEL);
+ klinfo_ptr = (klinfo_t *)klcomp_ptr;
+ lb_ptr->brd_compts[1] = (klconf_off_t)klcomp_ptr;
+
+ klinfo_ptr->struct_type = 0xb;
+ klinfo_ptr->struct_version = 0x1;
+ klinfo_ptr->flags = 0x31;
+ klinfo_ptr->revision = 0x5;
+ klinfo_ptr->diagval = 0x0;
+ klinfo_ptr->diagparm = 0x0;
+ klinfo_ptr->inventory = 0x0;
+ klinfo_ptr->partid = 0x0;
+ klinfo_ptr->nic = 0xffffffffffffffff;
+ klinfo_ptr->physid = 0x1;
+ klinfo_ptr->virtid = 0x0;
+ klinfo_ptr->widid = 0xf;
+ klinfo_ptr->nasid = 0x0;
+
+ /*
+ * KLSTRUCT_IOC3 Component
+ */
+ klcomp_ptr = kmalloc(sizeof(klcomp_t), GFP_KERNEL);
+ klioc3_ptr = (klioc3_t *)klcomp_ptr;
+ klinfo_ptr = (klinfo_t *)klcomp_ptr;
+ lb_ptr->brd_compts[2] = (klconf_off_t)klcomp_ptr;
+
+ klinfo_ptr->struct_type = 0x6;
+ klinfo_ptr->struct_version = 0x1;
+ klinfo_ptr->flags = 0x31;
+ klinfo_ptr->revision = 0x1;
+ klinfo_ptr->diagval = 0x0;
+ klinfo_ptr->diagparm = 0x0;
+ klinfo_ptr->inventory = 0x0;
+ klinfo_ptr->partid = 0x0;
+ klinfo_ptr->nic = 0xffffffffffffffff;
+ klinfo_ptr->physid = 0x4;
+ klinfo_ptr->virtid = 0x0;
+ klinfo_ptr->widid = 0xf;
+ klinfo_ptr->nasid = 0x0;
+
+ klioc3_ptr->ioc3_ssram = 0x0;
+ klioc3_ptr->ioc3_nvram = 0x0;
+
+ /*
+ * KLSTRUCT_UNKNOWN Component
+ */
+ klcomp_ptr = kmalloc(sizeof(klcomp_t), GFP_KERNEL);
+ klinfo_ptr = (klinfo_t *)klcomp_ptr;
+ lb_ptr->brd_compts[3] = (klconf_off_t)klcomp_ptr;
+
+ klinfo_ptr->struct_type = 0x0;
+ klinfo_ptr->struct_version = 0x1;
+ klinfo_ptr->flags = 0x31;
+ klinfo_ptr->revision = 0xff;
+ klinfo_ptr->diagval = 0x0;
+ klinfo_ptr->diagparm = 0x0;
+ klinfo_ptr->inventory = 0x0;
+ klinfo_ptr->partid = 0x0;
+ klinfo_ptr->nic = 0xffffffffffffffff;
+ klinfo_ptr->physid = 0x5;
+ klinfo_ptr->virtid = 0x0;
+ klinfo_ptr->widid = 0xf;
+ klinfo_ptr->nasid = 0x0;
+
+ /*
+ * KLSTRUCT_SCSI Component
+ */
+ klcomp_ptr = kmalloc(sizeof(klcomp_t), GFP_KERNEL);
+ klinfo_ptr = (klinfo_t *)klcomp_ptr;
+ lb_ptr->brd_compts[4] = (klconf_off_t)klcomp_ptr;
+
+ klinfo_ptr->struct_type = 0xb;
+ klinfo_ptr->struct_version = 0x1;
+ klinfo_ptr->flags = 0x31;
+ klinfo_ptr->revision = 0x1;
+ klinfo_ptr->diagval = 0x0;
+ klinfo_ptr->diagparm = 0x0;
+ klinfo_ptr->inventory = 0x0;
+ klinfo_ptr->partid = 0x0;
+ klinfo_ptr->nic = 0xffffffffffffffff;
+ klinfo_ptr->physid = 0x6;
+ klinfo_ptr->virtid = 0x5;
+ klinfo_ptr->widid = 0xf;
+ klinfo_ptr->nasid = 0x0;
+
+ /*
+ * KLSTRUCT_UNKNOWN
+ */
+ klcomp_ptr = kmalloc(sizeof(klcomp_t), GFP_KERNEL);
+ klinfo_ptr = (klinfo_t *)klcomp_ptr;
+ lb_ptr->brd_compts[5] = (klconf_off_t)klcomp_ptr;
+
+ klinfo_ptr->struct_type = 0x0;
+ klinfo_ptr->struct_version = 0x1;
+ klinfo_ptr->flags = 0x31;
+ klinfo_ptr->revision = 0xff;
+ klinfo_ptr->diagval = 0x0;
+ klinfo_ptr->diagparm = 0x0;
+ klinfo_ptr->inventory = 0x0;
+ klinfo_ptr->partid = 0x0;
+ klinfo_ptr->nic = 0xffffffffffffffff;
+ klinfo_ptr->physid = 0x7;
+ klinfo_ptr->virtid = 0x0;
+ klinfo_ptr->widid = 0xf;
+ klinfo_ptr->nasid = 0x0;
+
+ lb_ptr->brd_compts[6] = 0;
+ lb_ptr->brd_numcompts = 6;
+
+}
+
+
+
+
+
+#ifdef BRINGUP
+/*
+ * these were useful for printing out registers etc
+ * during bringup
+ */
+
+void
+xdump(long long *addr, int count)
+{
+ int ii;
+ volatile long long *xx = addr;
+
+ for ( ii = 0; ii < count; ii++, xx++ ) {
+ printk("0x%p : 0x%p\n", xx, *xx);
+ }
+}
+
+void
+xdump32(unsigned int *addr, int count)
+{
+ int ii;
+ volatile unsigned int *xx = addr;
+
+ for ( ii = 0; ii < count; ii++, xx++ ) {
+ printk("0x%p : 0x%0x\n", xx, *xx);
+ }
+}
+
+
+
+void
+clear_ii_error(void)
+{
+ volatile long long *tmp;
+
+ printk("... WSTAT ");
+ xdump((long long *)0xc0000a0001c00008, 1);
+ printk("... WCTRL ");
+ xdump((long long *)0xc0000a0001c00020, 1);
+ printk("... WLCSR ");
+ xdump((long long *)0xc0000a0001c00128, 1);
+ printk("... IIDSR ");
+ xdump((long long *)0xc0000a0001c00138, 1);
+ printk("... IOPRBs ");
+ xdump((long long *)0xc0000a0001c00198, 9);
+ printk("... IXSS ");
+ xdump((long long *)0xc0000a0001c00210, 1);
+ printk("... IBLS0 ");
+ xdump((long long *)0xc0000a0001c10000, 1);
+ printk("... IBLS1 ");
+ xdump((long long *)0xc0000a0001c20000, 1);
+
+ /* Write IOERR clear to clear the CRAZY bit in the status */
+ tmp = (long long *)0xc0000a0001c001f8; *tmp = (long long)0xffffffff;
+
+ /* dump out local block error registers */
+ printk("... ");
+ xdump((long long *)0xc0000a0001e04040, 1); /* LB_ERROR_BITS */
+ printk("... ");
+ xdump((long long *)0xc0000a0001e04050, 1); /* LB_ERROR_HDR1 */
+ printk("... ");
+ xdump((long long *)0xc0000a0001e04058, 1); /* LB_ERROR_HDR2 */
+ /* and clear the LB_ERROR_BITS */
+ tmp = (long long *)0xc0000a0001e04040; *tmp = 0x0;
+ printk("clr: ");
+ xdump((long long *)0xc0000a0001e04040, 1); /* LB_ERROR_BITS */
+ tmp = (long long *)0xc0000a0001e04050; *tmp = 0x0;
+ tmp = (long long *)0xc0000a0001e04058; *tmp = 0x0;
+}
+
+
+void
+dump_ii()
+{
+ printk("===== Dump the II regs =====\n");
+ xdump((long long *)0xc0000a0001c00000, 2);
+ xdump((long long *)0xc0000a0001c00020, 1);
+ xdump((long long *)0xc0000a0001c00100, 37);
+ xdump((long long *)0xc0000a0001c00300, 98);
+ xdump((long long *)0xc0000a0001c10000, 6);
+ xdump((long long *)0xc0000a0001c20000, 6);
+ xdump((long long *)0xc0000a0001c30000, 2);
+
+ xdump((long long *)0xc0000a0000000000, 1);
+ xdump((long long *)0xc0000a0001000000, 1);
+ xdump((long long *)0xc0000a0002000000, 1);
+ xdump((long long *)0xc0000a0003000000, 1);
+ xdump((long long *)0xc0000a0004000000, 1);
+ xdump((long long *)0xc0000a0005000000, 1);
+ xdump((long long *)0xc0000a0006000000, 1);
+ xdump((long long *)0xc0000a0007000000, 1);
+ xdump((long long *)0xc0000a0008000000, 1);
+ xdump((long long *)0xc0000a0009000000, 1);
+ xdump((long long *)0xc0000a000a000000, 1);
+ xdump((long long *)0xc0000a000b000000, 1);
+ xdump((long long *)0xc0000a000c000000, 1);
+ xdump((long long *)0xc0000a000d000000, 1);
+ xdump((long long *)0xc0000a000e000000, 1);
+ xdump((long long *)0xc0000a000f000000, 1);
+}
+
+void
+dump_lb()
+{
+ printk("===== Dump the LB regs =====\n");
+ xdump((long long *)0xc0000a0001e00000, 1);
+ xdump((long long *)0xc0000a0001e04000, 13);
+ xdump((long long *)0xc0000a0001e04100, 2);
+ xdump((long long *)0xc0000a0001e04200, 2);
+ xdump((long long *)0xc0000a0001e08000, 5);
+ xdump((long long *)0xc0000a0001e08040, 2);
+ xdump((long long *)0xc0000a0001e08050, 3);
+ xdump((long long *)0xc0000a0001e0c000, 3);
+ xdump((long long *)0xc0000a0001e0c020, 4);
+}
+
+void
+dump_crossbow()
+{
+ printk("===== Dump the Crossbow regs =====\n");
+ clear_ii_error();
+ xdump32((unsigned int *)0xc0000a0000000004, 1);
+ clear_ii_error();
+ xdump32((unsigned int *)0xc0000a0000000000, 1);
+ printk("and again..\n");
+ xdump32((unsigned int *)0xc0000a0000000000, 1);
+ xdump32((unsigned int *)0xc0000a0000000000, 1);
+
+
+ clear_ii_error();
+
+ xdump32((unsigned int *)0xc000020000000004, 1);
+ clear_ii_error();
+ xdump32((unsigned int *)0xc000020000000000, 1);
+ clear_ii_error();
+
+ xdump32((unsigned int *)0xc0000a0000800004, 1);
+ clear_ii_error();
+ xdump32((unsigned int *)0xc0000a0000800000, 1);
+ clear_ii_error();
+
+ xdump32((unsigned int *)0xc000020000800004, 1);
+ clear_ii_error();
+ xdump32((unsigned int *)0xc000020000800000, 1);
+ clear_ii_error();
+
+
+}
+#endif /* BRINGUP */
diff --git a/arch/ia64/sn/io/l1.c b/arch/ia64/sn/io/l1.c
new file mode 100644
index 000000000..b8c5af674
--- /dev/null
+++ b/arch/ia64/sn/io/l1.c
@@ -0,0 +1,2974 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+/* In general, this file is organized in a hierarchy from lower-level
+ * to higher-level layers, as follows:
+ *
+ * UART routines
+ * Bedrock/L1 "PPP-like" protocol implementation
+ * System controller "message" interface (allows multiplexing
+ * of various kinds of requests and responses with
+ * console I/O)
+ * Console interfaces (there are two):
+ * (1) "elscuart", used in the IP35prom and (maybe) some
+ * debugging situations elsewhere, and
+ * (2) "l1_cons", the glue that allows the L1 to act
+ * as the system console for the stdio libraries
+ *
+ * Routines making use of the system controller "message"-style interface
+ * can be found in l1_command.c. Their names are leftover from early SN0,
+ * when the "module system controller" (msc) was known as the "entry level
+ * system controller" (elsc). The names and signatures of those functions
+ * remain unchanged in order to keep the SN0 -> SN1 system controller
+ * changes fairly localized.
+ */
+
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/hcl_util.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/eeprom.h>
+#include <asm/sn/ksys/i2c.h>
+#include <asm/sn/cmn_err.h>
+#include <asm/sn/router.h>
+#include <asm/sn/module.h>
+#include <asm/sn/ksys/l1.h>
+#include <asm/sn/nodepda.h>
+#include <asm/sn/clksupport.h>
+
+#include <asm/sn/sn1/uart16550.h>
+
+
+#if defined(EEPROM_DEBUG)
+#define db_printf(x) printk x
+#else
+#define db_printf(x)
+#endif
+
+// From irix/kern/sys/SN/SN1/bdrkhspecregs.h
+#define HSPEC_UART_0 0x00000080 /* UART Registers */
+
+/*********************************************************************
+ * Hardware-level (UART) driver routines.
+ */
+
+/* macros for reading/writing registers */
+
+#define LD(x) (*(volatile uint64_t *)(x))
+#define SD(x, v) (LD(x) = (uint64_t) (v))
+
+/* location of uart receive/xmit data register */
+#define L1_UART_BASE(n) ((ulong)REMOTE_HSPEC_ADDR((n), HSPEC_UART_0))
+#define LOCAL_HUB LOCAL_HUB_ADDR
+
+#define ADDR_L1_REG(n, r) \
+ (L1_UART_BASE(n) | ( (r) << 3 ))
+
+#define READ_L1_UART_REG(n, r) \
+ ( LD(ADDR_L1_REG((n), (r))) )
+
+#define WRITE_L1_UART_REG(n, r, v) \
+ ( SD(ADDR_L1_REG((n), (r)), (v)) )
+
+
+/* Avoid conflicts with symmon...*/
+#define CONS_HW_LOCK(x)
+#define CONS_HW_UNLOCK(x)
+
+#define L1_CONS_HW_LOCK(sc) CONS_HW_LOCK(sc->uart == BRL1_LOCALUART)
+#define L1_CONS_HW_UNLOCK(sc) CONS_HW_UNLOCK(sc->uart == BRL1_LOCALUART)
+
+#if DEBUG
+static int debuglock_ospl; /* For CONS_HW_LOCK macro */
+#endif
+
+/* UART-related #defines */
+
+#define UART_BAUD_RATE 57600
+#define UART_FIFO_DEPTH 16
+#define UART_DELAY_SPAN 10
+#define UART_PUTC_TIMEOUT 50000
+#define UART_INIT_TIMEOUT 100000
+
+/* error codes */
+#define UART_SUCCESS 0
+#define UART_TIMEOUT (-1)
+#define UART_LINK (-2)
+#define UART_NO_CHAR (-3)
+#define UART_VECTOR (-4)
+
+#ifdef BRINGUP
+#define UART_DELAY(x) { int i; i = x * 1000; while (--i); }
+#else
+#define UART_DELAY(x) us_delay(x)
+#endif
+
+/*
+ * Some macros for handling Endian-ness
+ */
+
+#ifdef LITTLE_ENDIAN
+#define COPY_INT_TO_BUFFER(_b, _i, _n) \
+ { \
+ _b[_i++] = (_n >> 24) & 0xff; \
+ _b[_i++] = (_n >> 16) & 0xff; \
+ _b[_i++] = (_n >> 8) & 0xff; \
+ _b[_i++] = _n & 0xff; \
+ }
+
+#define COPY_BUFFER_TO_INT(_b, _i, _n) \
+ { \
+ _n = (_b[_i++] << 24) & 0xff; \
+ _n |= (_b[_i++] << 16) & 0xff; \
+ _n |= (_b[_i++] << 8) & 0xff; \
+ _n |= _b[_i++] & 0xff; \
+ }
+
+#define COPY_BUFFER_TO_BUFFER(_b, _i, _bn) \
+ { \
+ char *_xyz = (char *)_bn; \
+ _xyz[3] = _b[_i++]; \
+ _xyz[2] = _b[_i++]; \
+ _xyz[1] = _b[_i++]; \
+ _xyz[0] = _b[_i++]; \
+ }
+#else /* BIG_ENDIAN */
+#define COPY_INT_TO_BUFFER(_b, _i, _n) \
+ { \
+ bcopy((char *)&_n, _b, sizeof(_n)); \
+ _i += sizeof(_n); \
+ }
+
+#define COPY_BUFFER_TO_INT(_b, _i, _n) \
+ { \
+ bcopy(&_b[_i], &_n, sizeof(_n)); \
+ _i += sizeof(_n); \
+ }
+
+#define COPY_BUFFER_TO_BUFFER(_b, _i, _bn) \
+ { \
+ bcopy(&(_b[_i]), _bn, sizeof(int)); \
+ _i += sizeof(int); \
+ }
+#endif /* LITTLE_ENDIAN */
+
+int atomicAddInt(int *int_ptr, int value);
+int atomicClearInt(int *int_ptr, int value);
+void kmem_free(void *where, int size);
+
+#define BCOPY(x,y,z) memcpy(y,x,z)
+
+extern char *bcopy(const char * src, char * dest, int count);
+
+
+int
+get_L1_baud(void)
+{
+ return UART_BAUD_RATE;
+}
+
+
+/* uart driver functions */
+
+static void
+uart_delay( rtc_time_t delay_span )
+{
+ UART_DELAY( delay_span );
+}
+
+#define UART_PUTC_READY(n) (READ_L1_UART_REG((n), REG_LSR) & LSR_XHRE)
+
+static int
+uart_putc( l1sc_t *sc )
+{
+#ifdef BRINGUP
+ /* need a delay to avoid dropping chars */
+ UART_DELAY(57);
+#endif
+ WRITE_L1_UART_REG( sc->nasid, REG_DAT,
+ sc->send[sc->sent] );
+ return UART_SUCCESS;
+}
+
+
+static int
+uart_getc( l1sc_t *sc )
+{
+ u_char lsr_reg = 0;
+ nasid_t nasid = sc->nasid;
+
+ if( (lsr_reg = READ_L1_UART_REG( nasid, REG_LSR )) &
+ (LSR_RCA | LSR_PARERR | LSR_FRMERR) )
+ {
+ if( lsr_reg & LSR_RCA )
+ return( (u_char)READ_L1_UART_REG( nasid, REG_DAT ) );
+ else if( lsr_reg & (LSR_PARERR | LSR_FRMERR) ) {
+ return UART_LINK;
+ }
+ }
+
+ return UART_NO_CHAR;
+}
+
+
+#define PROM_SER_CLK_SPEED 12000000
+#define PROM_SER_DIVISOR(x) (PROM_SER_CLK_SPEED / ((x) * 16))
+
+static void
+uart_init( l1sc_t *sc, int baud )
+{
+ rtc_time_t expire;
+ int clkdiv;
+ nasid_t nasid;
+
+ clkdiv = PROM_SER_DIVISOR(baud);
+ expire = rtc_time() + UART_INIT_TIMEOUT;
+ nasid = sc->nasid;
+
+ /* make sure the transmit FIFO is empty */
+ while( !(READ_L1_UART_REG( nasid, REG_LSR ) & LSR_XSRE) ) {
+ uart_delay( UART_DELAY_SPAN );
+ if( rtc_time() > expire ) {
+ break;
+ }
+ }
+
+ L1_CONS_HW_LOCK( sc );
+
+ WRITE_L1_UART_REG( nasid, REG_LCR, LCR_DLAB );
+ uart_delay( UART_DELAY_SPAN );
+ WRITE_L1_UART_REG( nasid, REG_DLH, (clkdiv >> 8) & 0xff );
+ uart_delay( UART_DELAY_SPAN );
+ WRITE_L1_UART_REG( nasid, REG_DLL, clkdiv & 0xff );
+ uart_delay( UART_DELAY_SPAN );
+
+ /* set operating parameters and set DLAB to 0 */
+ WRITE_L1_UART_REG( nasid, REG_LCR, LCR_BITS8 | LCR_STOP1 );
+ uart_delay( UART_DELAY_SPAN );
+ WRITE_L1_UART_REG( nasid, REG_MCR, MCR_RTS | MCR_AFE );
+ uart_delay( UART_DELAY_SPAN );
+
+ /* disable interrupts */
+ WRITE_L1_UART_REG( nasid, REG_ICR, 0x0 );
+ uart_delay( UART_DELAY_SPAN );
+
+ /* enable FIFO mode and reset both FIFOs */
+ WRITE_L1_UART_REG( nasid, REG_FCR, FCR_FIFOEN );
+ uart_delay( UART_DELAY_SPAN );
+ WRITE_L1_UART_REG( nasid, REG_FCR,
+ FCR_FIFOEN | FCR_RxFIFO | FCR_TxFIFO );
+
+ L1_CONS_HW_UNLOCK( sc );
+}
+
+static void
+uart_intr_enable( l1sc_t *sc, u_char mask )
+{
+ u_char lcr_reg, icr_reg;
+ nasid_t nasid = sc->nasid;
+
+ L1_CONS_HW_LOCK(sc);
+
+ /* make sure that the DLAB bit in the LCR register is 0
+ */
+ lcr_reg = READ_L1_UART_REG( nasid, REG_LCR );
+ lcr_reg &= ~(LCR_DLAB);
+ WRITE_L1_UART_REG( nasid, REG_LCR, lcr_reg );
+
+ /* enable indicated interrupts
+ */
+ icr_reg = READ_L1_UART_REG( nasid, REG_ICR );
+ icr_reg |= mask;
+ WRITE_L1_UART_REG( nasid, REG_ICR, icr_reg /*(ICR_RIEN | ICR_TIEN)*/ );
+
+ L1_CONS_HW_UNLOCK(sc);
+}
+
+static void
+uart_intr_disable( l1sc_t *sc, u_char mask )
+{
+ u_char lcr_reg, icr_reg;
+ nasid_t nasid = sc->nasid;
+
+ L1_CONS_HW_LOCK(sc);
+
+ /* make sure that the DLAB bit in the LCR register is 0
+ */
+ lcr_reg = READ_L1_UART_REG( nasid, REG_LCR );
+ lcr_reg &= ~(LCR_DLAB);
+ WRITE_L1_UART_REG( nasid, REG_LCR, lcr_reg );
+
+ /* enable indicated interrupts
+ */
+ icr_reg = READ_L1_UART_REG( nasid, REG_ICR );
+ icr_reg &= mask;
+ WRITE_L1_UART_REG( nasid, REG_ICR, icr_reg /*(ICR_RIEN | ICR_TIEN)*/ );
+
+ L1_CONS_HW_UNLOCK(sc);
+}
+
+#define uart_enable_xmit_intr(sc) \
+ uart_intr_enable((sc), ICR_TIEN)
+
+#define uart_disable_xmit_intr(sc) \
+ uart_intr_disable((sc), ~(ICR_TIEN))
+
+#define uart_enable_recv_intr(sc) \
+ uart_intr_enable((sc), ICR_RIEN)
+
+#define uart_disable_recv_intr(sc) \
+ uart_intr_disable((sc), ~(ICR_RIEN))
+
+
+/*********************************************************************
+ * Routines for accessing a remote (router) UART
+ */
+
+#define READ_RTR_L1_UART_REG(p, n, r, v) \
+ { \
+ if( vector_read_node( (p), (n), 0, \
+ RR_JBUS1(r), (v) ) ) { \
+ return UART_VECTOR; \
+ } \
+ }
+
+#define WRITE_RTR_L1_UART_REG(p, n, r, v) \
+ { \
+ if( vector_write_node( (p), (n), 0, \
+ RR_JBUS1(r), (v) ) ) { \
+ return UART_VECTOR; \
+ } \
+ }
+
+#ifdef SABLE
+#define RTR_UART_PUTC_TIMEOUT 0
+#define RTR_UART_DELAY_SPAN 0
+#define RTR_UART_INIT_TIMEOUT 0
+#else
+#define RTR_UART_PUTC_TIMEOUT UART_PUTC_TIMEOUT*10
+#define RTR_UART_DELAY_SPAN UART_DELAY_SPAN
+#define RTR_UART_INIT_TIMEOUT UART_INIT_TIMEOUT*10
+#endif
+
+static int
+rtr_uart_putc( l1sc_t *sc )
+{
+ uint64_t regval, c;
+ nasid_t nasid = sc->nasid;
+ net_vec_t path = sc->uart;
+ rtc_time_t expire = rtc_time() + RTR_UART_PUTC_TIMEOUT;
+
+ c = (sc->send[sc->sent] & 0xffULL);
+
+ while( 1 )
+ {
+ /* Check for "tx hold reg empty" bit. */
+ READ_RTR_L1_UART_REG( path, nasid, REG_LSR, &regval );
+ if( regval & LSR_XHRE )
+ {
+ WRITE_RTR_L1_UART_REG( path, nasid, REG_DAT, c );
+ return UART_SUCCESS;
+ }
+
+ if( rtc_time() >= expire )
+ {
+ return UART_TIMEOUT;
+ }
+ uart_delay( RTR_UART_DELAY_SPAN );
+ }
+}
+
+
+static int
+rtr_uart_getc( l1sc_t *sc )
+{
+ uint64_t regval;
+ nasid_t nasid = sc->nasid;
+ net_vec_t path = sc->uart;
+
+ READ_RTR_L1_UART_REG( path, nasid, REG_LSR, &regval );
+ if( regval & (LSR_RCA | LSR_PARERR | LSR_FRMERR) )
+ {
+ if( regval & LSR_RCA )
+ {
+ READ_RTR_L1_UART_REG( path, nasid, REG_DAT, &regval );
+ return( (int)regval );
+ }
+ else
+ {
+ return UART_LINK;
+ }
+ }
+
+ return UART_NO_CHAR;
+}
+
+
+static int
+rtr_uart_init( l1sc_t *sc, int baud )
+{
+ rtc_time_t expire;
+ int clkdiv;
+ nasid_t nasid;
+ net_vec_t path;
+ uint64_t regval;
+
+ clkdiv = PROM_SER_DIVISOR(baud);
+ expire = rtc_time() + RTR_UART_INIT_TIMEOUT;
+ nasid = sc->nasid;
+ path = sc->uart;
+
+ /* make sure the transmit FIFO is empty */
+ while(1) {
+ READ_RTR_L1_UART_REG( path, nasid, REG_LSR, &regval );
+ if( regval & LSR_XSRE ) {
+ break;
+ }
+ if( rtc_time() > expire ) {
+ break;
+ }
+ uart_delay( RTR_UART_DELAY_SPAN );
+ }
+
+ WRITE_RTR_L1_UART_REG( path, nasid, REG_LCR, LCR_DLAB );
+ uart_delay( UART_DELAY_SPAN );
+ WRITE_RTR_L1_UART_REG( path, nasid, REG_DLH, (clkdiv >> 8) & 0xff );
+ uart_delay( UART_DELAY_SPAN );
+ WRITE_RTR_L1_UART_REG( path, nasid, REG_DLL, clkdiv & 0xff );
+ uart_delay( UART_DELAY_SPAN );
+
+ /* set operating parameters and set DLAB to 0 */
+ WRITE_RTR_L1_UART_REG( path, nasid, REG_LCR, LCR_BITS8 | LCR_STOP1 );
+ uart_delay( UART_DELAY_SPAN );
+ WRITE_RTR_L1_UART_REG( path, nasid, REG_MCR, MCR_RTS | MCR_AFE );
+ uart_delay( UART_DELAY_SPAN );
+
+ /* disable interrupts */
+ WRITE_RTR_L1_UART_REG( path, nasid, REG_ICR, 0x0 );
+ uart_delay( UART_DELAY_SPAN );
+
+ /* enable FIFO mode and reset both FIFOs */
+ WRITE_RTR_L1_UART_REG( path, nasid, REG_FCR, FCR_FIFOEN );
+ uart_delay( UART_DELAY_SPAN );
+ WRITE_RTR_L1_UART_REG( path, nasid, REG_FCR,
+ FCR_FIFOEN | FCR_RxFIFO | FCR_TxFIFO );
+
+ return 0;
+}
+
+
+
+/*********************************************************************
+ * locking macros
+ */
+
+#define L1SC_SEND_LOCK(l,pl) \
+ { if( (l)->uart == BRL1_LOCALUART ) \
+ (pl) = mutex_spinlock_spl( &((l)->send_lock), spl7 ); }
+
+#define L1SC_SEND_UNLOCK(l,pl) \
+ { if( (l)->uart == BRL1_LOCALUART ) \
+ mutex_spinunlock( &((l)->send_lock), (pl)); }
+
+#define L1SC_RECV_LOCK(l,pl) \
+ { if( (l)->uart == BRL1_LOCALUART ) \
+ (pl) = mutex_spinlock_spl( &((l)->recv_lock), spl7 ); }
+
+#define L1SC_RECV_UNLOCK(l,pl) \
+ { if( (l)->uart == BRL1_LOCALUART ) \
+ mutex_spinunlock( &((l)->recv_lock), (pl)); }
+
+
+/*********************************************************************
+ * subchannel manipulation
+ *
+ * The SUBCH_[UN]LOCK macros are used to arbitrate subchannel
+ * allocation. SUBCH_DATA_[UN]LOCK control access to data structures
+ * associated with particular subchannels (e.g., receive queues).
+ *
+ */
+
+
+#ifdef SPINLOCKS_WORK
+#define SUBCH_LOCK(sc,pl) \
+ (pl) = mutex_spinlock_spl( &((sc)->subch_lock), spl7 )
+#define SUBCH_UNLOCK(sc,pl) \
+ mutex_spinunlock( &((sc)->subch_lock), (pl) )
+
+#define SUBCH_DATA_LOCK(sbch,pl) \
+ (pl) = mutex_spinlock_spl( &((sbch)->data_lock), spl7 )
+#define SUBCH_DATA_UNLOCK(sbch,pl) \
+ mutex_spinunlock( &((sbch)->data_lock), (pl) )
+#else
+#define SUBCH_LOCK(sc,pl)
+#define SUBCH_UNLOCK(sc,pl)
+#define SUBCH_DATA_LOCK(sbch,pl)
+#define SUBCH_DATA_UNLOCK(sbch,pl)
+#endif /* SPINLOCKS_WORK */
+
+/*
+ * set a function to be called for subchannel ch in the event of
+ * a transmission low-water interrupt from the uart
+ */
+void
+subch_set_tx_notify( l1sc_t *sc, int ch, brl1_notif_t func )
+{
+ int pl;
+ L1SC_SEND_LOCK( sc, pl );
+ sc->subch[ch].tx_notify = func;
+
+ /* some upper layer is asking to be notified of low-water, but if the
+ * send buffer isn't already in use, we're going to need to get the
+ * interrupts going on the uart...
+ */
+ if( func && !sc->send_in_use )
+ uart_enable_xmit_intr( sc );
+ L1SC_SEND_UNLOCK(sc, pl );
+}
+
+/*
+ * set a function to be called for subchannel ch when data is received
+ */
+void
+subch_set_rx_notify( l1sc_t *sc, int ch, brl1_notif_t func )
+{
+#ifdef SPINLOCKS_WORK
+ int pl;
+#endif
+ brl1_sch_t *subch = &(sc->subch[ch]);
+
+ SUBCH_DATA_LOCK( subch, pl );
+ sc->subch[ch].rx_notify = func;
+ SUBCH_DATA_UNLOCK( subch, pl );
+}
+
+
+
+/* get_myid is an internal function that reads the PI_CPU_NUM
+ * register of the local bedrock to determine which of the
+ * four possible CPU's "this" one is
+ */
+static int
+get_myid( void )
+{
+ return( LD(LOCAL_HUB(PI_CPU_NUM)) );
+}
+
+
+
+/*********************************************************************
+ * Queue manipulation macros
+ *
+ *
+ */
+#define NEXT(p) (((p) + 1) & (BRL1_QSIZE-1)) /* assume power of 2 */
+
+#define cq_init(q) bzero((q), sizeof (*(q)))
+#define cq_empty(q) ((q)->ipos == (q)->opos)
+#define cq_full(q) (NEXT((q)->ipos) == (q)->opos)
+#define cq_used(q) ((q)->opos <= (q)->ipos ? \
+ (q)->ipos - (q)->opos : \
+ BRL1_QSIZE + (q)->ipos - (q)->opos)
+#define cq_room(q) ((q)->opos <= (q)->ipos ? \
+ BRL1_QSIZE - 1 + (q)->opos - (q)->ipos : \
+ (q)->opos - (q)->ipos - 1)
+#define cq_add(q, c) ((q)->buf[(q)->ipos] = (u_char) (c), \
+ (q)->ipos = NEXT((q)->ipos))
+#define cq_rem(q, c) ((c) = (q)->buf[(q)->opos], \
+ (q)->opos = NEXT((q)->opos))
+#define cq_discard(q) ((q)->opos = NEXT((q)->opos))
+
+#define cq_tent_full(q) (NEXT((q)->tent_next) == (q)->opos)
+#define cq_tent_len(q) ((q)->ipos <= (q)->tent_next ? \
+ (q)->tent_next - (q)->ipos : \
+ BRL1_QSIZE + (q)->tent_next - (q)->ipos)
+#define cq_tent_add(q, c) \
+ ((q)->buf[(q)->tent_next] = (u_char) (c), \
+ (q)->tent_next = NEXT((q)->tent_next))
+#define cq_commit_tent(q) \
+ ((q)->ipos = (q)->tent_next)
+#define cq_discard_tent(q) \
+ ((q)->tent_next = (q)->ipos)
+
+
+
+
+/*********************************************************************
+ * CRC-16 (for checking bedrock/L1 packets).
+ *
+ * These are based on RFC 1662 ("PPP in HDLC-like framing").
+ */
+
+static unsigned short fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+#define INIT_CRC 0xFFFF /* initial CRC value */
+#define GOOD_CRC 0xF0B8 /* "good" final CRC value */
+
+static unsigned short crc16_calc( unsigned short crc, u_char c )
+{
+ return( (crc >> 8) ^ fcstab[(crc ^ c) & 0xff] );
+}
+
+
+/***********************************************************************
+ * The following functions implement the PPP-like bedrock/L1 protocol
+ * layer.
+ *
+ */
+
+#define BRL1_FLAG_CH 0x7e
+#define BRL1_ESC_CH 0x7d
+#define BRL1_XOR_CH 0x20
+
+/* L1<->Bedrock packet types */
+#define BRL1_REQUEST 0x00
+#define BRL1_RESPONSE 0x20
+#define BRL1_EVENT 0x40
+
+#define BRL1_PKT_TYPE_MASK 0xE0
+#define BRL1_SUBCH_MASK 0x1F
+
+#define PKT_TYPE(tsb) ((tsb) & BRL1_PKT_TYPE_MASK)
+#define SUBCH(tsb) ((tsb) & BRL1_SUBCH_MASK)
+
+/* timeouts */
+#define BRL1_INIT_TIMEOUT 500000
+
+extern l1sc_t * get_elsc( void );
+
+/*
+ * brl1_discard_packet is a dummy "receive callback" used to get rid
+ * of packets we don't want
+ */
+void brl1_discard_packet( l1sc_t *sc, int ch )
+{
+ int pl;
+ brl1_sch_t *subch = &sc->subch[ch];
+ sc_cq_t *q = subch->iqp;
+ SUBCH_DATA_LOCK( subch, pl );
+ q->opos = q->ipos;
+ atomicClearInt( &(subch->packet_arrived), ~((unsigned)0) );
+ SUBCH_DATA_UNLOCK( subch, pl );
+}
+
+
+/*
+ * brl1_send_chars sends the send buffer in the l1sc_t structure
+ * out through the uart. Assumes that the caller has locked the
+ * UART (or send buffer in the kernel).
+ *
+ * This routine doesn't block-- if you want it to, call it in
+ * a loop.
+ */
+static int
+brl1_send_chars( l1sc_t *sc )
+{
+ /* In the kernel, we track the depth of the C brick's UART's
+ * fifo in software, and only check if the UART is accepting
+ * characters when our count indicates that the fifo should
+ * be full.
+ *
+ * For remote (router) UARTs, and also for the local (C brick)
+ * UART in the prom, we check with the UART before sending every
+ * character.
+ */
+ if( sc->uart == BRL1_LOCALUART )
+ {
+ CONS_HW_LOCK(1);
+ if( !(sc->fifo_space) && UART_PUTC_READY( sc->nasid ) )
+// sc->fifo_space = UART_FIFO_DEPTH;
+ sc->fifo_space = 1000;
+
+ while( (sc->sent < sc->send_len) && (sc->fifo_space) ) {
+ uart_putc( sc );
+ sc->fifo_space--;
+ sc->sent++;
+ }
+
+ CONS_HW_UNLOCK(1);
+ }
+
+ else
+
+ /* The following applies to all UARTs in the prom, and to remote
+ * (router) UARTs in the kernel...
+ */
+
+#define TIMEOUT_RETRIES 30
+
+ {
+ int result;
+ int tries = 0;
+
+ while( sc->sent < sc->send_len ) {
+ result = sc->putc_f( sc );
+ if( result >= 0 ) {
+ (sc->sent)++;
+ continue;
+ }
+ if( result == UART_TIMEOUT ) {
+ tries++;
+ /* send this character in TIMEOUT_RETRIES... */
+ if( tries < TIMEOUT_RETRIES ) {
+ continue;
+ }
+ /* ...or else... */
+ else {
+ /* ...drop the packet. */
+ sc->sent = sc->send_len;
+ return sc->send_len;
+ }
+ }
+ if( result < 0 ) {
+ return result;
+ }
+ }
+ }
+
+ return sc->sent;
+}
+
+
+/* brl1_send formats up a packet and (at least begins to) send it
+ * to the uart. If the send buffer is in use when this routine obtains
+ * the lock, it will behave differently depending on the "wait" parameter.
+ * For wait == 0 (most I/O), it will return 0 (as in "zero bytes sent"),
+ * hopefully encouraging the caller to back off (unlock any high-level
+ * spinlocks) and allow the buffer some time to drain. For wait==1 (high-
+ * priority I/O along the lines of kernel error messages), we will flush
+ * the current contents of the send buffer and beat on the uart
+ * until our message has been completely transmitted.
+ */
+
+int
+brl1_send( l1sc_t *sc, char *msg, int len, u_char type_and_subch, int wait )
+{
+ int pl;
+ int index;
+ int pkt_len = 0;
+ unsigned short crc = INIT_CRC;
+ char *send_ptr = sc->send;
+
+ L1SC_SEND_LOCK(sc, pl);
+
+ if( sc->send_in_use ) {
+ if( !wait ) {
+ L1SC_SEND_UNLOCK(sc, pl);
+ return 0; /* couldn't send anything; wait for buffer to drain */
+ }
+ else {
+ /* buffer's in use, but we're synchronous I/O, so we're going
+ * to send whatever's in there right now and take the buffer
+ */
+ while( sc->sent < sc->send_len )
+ brl1_send_chars( sc );
+ }
+ }
+ else {
+ sc->send_in_use = 1;
+ }
+ *send_ptr++ = BRL1_FLAG_CH;
+ *send_ptr++ = type_and_subch;
+ pkt_len += 2;
+ crc = crc16_calc( crc, type_and_subch );
+
+ /* limit number of characters accepted to max payload size */
+ if( len > (BRL1_QSIZE - 1) )
+ len = (BRL1_QSIZE - 1);
+
+ /* copy in the message buffer (inserting PPP
+ * framing info where necessary)
+ */
+ for( index = 0; index < len; index++ ) {
+
+ switch( *msg ) {
+
+ case BRL1_FLAG_CH:
+ *send_ptr++ = BRL1_ESC_CH;
+ *send_ptr++ = (*msg) ^ BRL1_XOR_CH;
+ pkt_len += 2;
+ break;
+
+ case BRL1_ESC_CH:
+ *send_ptr++ = BRL1_ESC_CH;
+ *send_ptr++ = (*msg) ^ BRL1_XOR_CH;
+ pkt_len += 2;
+ break;
+
+ default:
+ *send_ptr++ = *msg;
+ pkt_len++;
+ }
+ crc = crc16_calc( crc, *msg );
+ msg++;
+ }
+ crc ^= 0xffff;
+
+ for( index = 0; index < sizeof(crc); index++ ) {
+ char crc_char = (char)(crc & 0x00FF);
+ if( (crc_char == BRL1_ESC_CH) || (crc_char == BRL1_FLAG_CH) ) {
+ *send_ptr++ = BRL1_ESC_CH;
+ pkt_len++;
+ crc_char ^= BRL1_XOR_CH;
+ }
+ *send_ptr++ = crc_char;
+ pkt_len++;
+ crc >>= 8;
+ }
+
+ *send_ptr++ = BRL1_FLAG_CH;
+ pkt_len++;
+
+ sc->send_len = pkt_len;
+ sc->sent = 0;
+
+ do {
+ brl1_send_chars( sc );
+ } while( (sc->sent < sc->send_len) && wait );
+
+ if( sc->sent == sc->send_len ) {
+ /* success! release the send buffer */
+ sc->send_in_use = 0;
+ }
+ else if( !wait ) {
+ /* enable low-water interrupts so buffer will be drained */
+ uart_enable_xmit_intr(sc);
+ }
+ L1SC_SEND_UNLOCK(sc, pl);
+ return len;
+}
+
+
+/* brl1_send_cont is intended to be called as an interrupt service
+ * routine. It sends until the UART won't accept any more characters,
+ * or until an error is encountered (in which case we surrender the
+ * send buffer and give up trying to send the packet). Once the
+ * last character in the packet has been sent, this routine releases
+ * the send buffer and calls any previously-registered "low-water"
+ * output routines.
+ */
+int
+brl1_send_cont( l1sc_t *sc )
+{
+ int pl;
+ int done = 0;
+ brl1_notif_t callups[BRL1_NUM_SUBCHANS];
+ brl1_notif_t *callup;
+ brl1_sch_t *subch;
+ int index;
+
+ L1SC_SEND_LOCK(sc, pl);
+ brl1_send_chars( sc );
+ done = (sc->sent == sc->send_len);
+ if( done ) {
+
+ sc->send_in_use = 0;
+ uart_disable_xmit_intr(sc);
+
+ /* collect pointers to callups *before* unlocking */
+ subch = sc->subch;
+ callup = callups;
+ for( index = 0; index < BRL1_NUM_SUBCHANS; index++ ) {
+ *callup = subch->tx_notify;
+ subch++;
+ callup++;
+ }
+ }
+ L1SC_SEND_UNLOCK(sc, pl);
+
+ if( done ) {
+ /* call any upper layer that's asked for low-water notification */
+ callup = callups;
+ for( index = 0; index < BRL1_NUM_SUBCHANS; index++ ) {
+ if( *callup )
+ (*(*callup))( sc, index );
+ callup++;
+ }
+ }
+ return 0;
+}
+
+
+/* internal function -- used by brl1_receive to read a character
+ * from the uart and check whether errors occurred in the process.
+ */
+static int
+read_uart( l1sc_t *sc, int *c, int *result )
+{
+ *c = sc->getc_f( sc );
+
+ /* no character is available */
+ if( *c == UART_NO_CHAR ) {
+ *result = BRL1_NO_MESSAGE;
+ return 0;
+ }
+
+ /* some error in UART */
+ if( *c < 0 ) {
+ *result = BRL1_LINK;
+ return 0;
+ }
+
+ /* everything's fine */
+ *result = BRL1_VALID;
+ return 1;
+}
+
+
+/*
+ * brl1_receive
+ *
+ * This function reads a Bedrock-L1 protocol packet into the l1sc_t
+ * response buffer.
+ *
+ * The operation of this function can be expressed as a finite state
+ * machine:
+ *
+
+START STATE INPUT TRANSITION
+==========================================================
+BRL1_IDLE (reset or error) flag BRL1_FLAG
+ other BRL1_IDLE@
+
+BRL1_FLAG (saw a flag (0x7e)) flag BRL1_FLAG
+ escape BRL1_IDLE@
+ header byte BRL1_HDR
+ other BRL1_IDLE@
+
+BRL1_HDR (saw a type/subch byte)(see below) BRL1_BODY
+ BRL1_HDR
+
+BRL1_BODY (reading packet body) flag BRL1_FLAG
+ escape BRL1_ESC
+ other BRL1_BODY
+
+BRL1_ESC (saw an escape (0x7d)) flag BRL1_FLAG@
+ escape BRL1_IDLE@
+ other BRL1_BODY
+==========================================================
+
+"@" denotes an error transition.
+
+ * The BRL1_HDR state is a transient state which doesn't read input,
+ * but just provides a way in to code which decides to whom an
+ * incoming packet should be directed.
+ *
+ * brl1_receive can be used to poll for input from the L1, or as
+ * an interrupt service routine. It reads as much data as is
+ * ready from the junk bus UART and places into the appropriate
+ * input queues according to subchannel. The header byte is
+ * stripped from console-type data, but is retained for message-
+ * type data (L1 responses). A length byte will also be
+ * prepended to message-type packets.
+ *
+ * This routine is non-blocking; if the caller needs to block
+ * for input, it must call brl1_receive in a loop.
+ *
+ * brl1_receive returns when there is no more input, the queue
+ * for the current incoming message is full, or there is an
+ * error (parity error, bad header, bad CRC, etc.).
+ */
+
+#define STATE_SET(l,s) ((l)->brl1_state = (s))
+#define STATE_GET(l) ((l)->brl1_state)
+
+#define LAST_HDR_SET(l,h) ((l)->brl1_last_hdr = (h))
+#define LAST_HDR_GET(l) ((l)->brl1_last_hdr)
+
+#define SEQSTAMP_INCR(l)
+#define SEQSTAMP_GET(l)
+
+#define VALID_HDR(c) \
+ ( SUBCH((c)) <= SC_CONS_SYSTEM \
+ ? PKT_TYPE((c)) == BRL1_REQUEST \
+ : ( PKT_TYPE((c)) == BRL1_RESPONSE || \
+ PKT_TYPE((c)) == BRL1_EVENT ) )
+
+#define IS_TTY_PKT(l) \
+ ( SUBCH(LAST_HDR_GET(l)) <= SC_CONS_SYSTEM ? 1 : 0 )
+
+
+int
+brl1_receive( l1sc_t *sc )
+{
+ int result; /* value to be returned by brl1_receive */
+ int c; /* most-recently-read character */
+ int pl; /* priority level for UART receive lock */
+ int done; /* set done to break out of recv loop */
+ sc_cq_t *q; /* pointer to queue we're working with */
+
+ result = BRL1_NO_MESSAGE;
+
+ L1SC_RECV_LOCK( sc, pl );
+ L1_CONS_HW_LOCK( sc );
+
+ done = 0;
+ while( !done )
+ {
+ switch( STATE_GET(sc) )
+ {
+
+ case BRL1_IDLE:
+ /* Initial or error state. Waiting for a flag character
+ * to resynchronize with the L1.
+ */
+
+ if( !read_uart( sc, &c, &result ) ) {
+
+ /* error reading uart */
+ done = 1;
+ continue;
+ }
+
+ if( c == BRL1_FLAG_CH ) {
+ /* saw a flag character */
+ STATE_SET( sc, BRL1_FLAG );
+ continue;
+ }
+ break;
+
+ case BRL1_FLAG:
+ /* One or more flag characters have been read; look for
+ * the beginning of a packet (header byte).
+ */
+
+ if( !read_uart( sc, &c, &result ) ) {
+
+ /* error reading uart */
+ if( c != UART_NO_CHAR )
+ STATE_SET( sc, BRL1_IDLE );
+
+ done = 1;
+ continue;
+ }
+
+ if( c == BRL1_FLAG_CH ) {
+ /* multiple flags are OK */
+ continue;
+ }
+
+ if( !VALID_HDR( c ) ) {
+ /* if c isn't a flag it should have been
+ * a valid header, so we have an error
+ */
+ result = BRL1_PROTOCOL;
+ STATE_SET( sc, BRL1_IDLE );
+ done = 1;
+ continue;
+ }
+
+ /* we have a valid header byte */
+ LAST_HDR_SET( sc, c );
+ STATE_SET( sc, BRL1_HDR );
+
+ break;
+
+ case BRL1_HDR:
+ /* A header byte has been read. Do some bookkeeping. */
+ q = sc->subch[ SUBCH( LAST_HDR_GET(sc) ) ].iqp;
+ ASSERT(q);
+
+ if( !IS_TTY_PKT(sc) ) {
+ /* if this is an event or command response rather
+ * than console I/O, we need to reserve a couple
+ * of extra spaces in the queue for the header
+ * byte and a length byte; if we can't, stay in
+ * the BRL1_HDR state.
+ */
+ if( cq_room( q ) < 2 ) {
+ result = BRL1_FULL_Q;
+ done = 1;
+ continue;
+ }
+ cq_tent_add( q, 0 ); /* reserve length byte */
+ cq_tent_add( q, LAST_HDR_GET( sc ) ); /* record header byte */
+ }
+ STATE_SET( sc, BRL1_BODY );
+
+ break;
+
+ case BRL1_BODY:
+ /* A header byte has been read. We are now attempting
+ * to receive the packet body.
+ */
+
+ q = sc->subch[ SUBCH( LAST_HDR_GET(sc) ) ].iqp;
+ ASSERT(q);
+
+ /* if the queue we want to write into is full, don't read from
+ * the uart (this provides backpressure to the L1 side)
+ */
+ if( cq_tent_full( q ) ) {
+ result = BRL1_FULL_Q;
+ done = 1;
+ continue;
+ }
+
+ if( !read_uart( sc, &c, &result ) ) {
+
+ /* error reading uart */
+ if( c != UART_NO_CHAR )
+ STATE_SET( sc, BRL1_IDLE );
+ done = 1;
+ continue;
+ }
+
+ if( c == BRL1_ESC_CH ) {
+ /* prepare to unescape the next character */
+ STATE_SET( sc, BRL1_ESC );
+ continue;
+ }
+
+ if( c == BRL1_FLAG_CH ) {
+ /* flag signifies the end of a packet */
+
+ unsigned short crc; /* holds the crc as we calculate it */
+ int i; /* index variable */
+ brl1_sch_t *subch; /* subchannel for received packet */
+ int sch_pl; /* cookie for subchannel lock */
+ brl1_notif_t callup; /* "data ready" callup */
+
+ /* whatever else may happen, we've seen a flag and we're
+ * starting a new packet
+ */
+ STATE_SET( sc, BRL1_FLAG );
+ SEQSTAMP_INCR(sc); /* bump the packet sequence counter */
+
+ /* if the packet body has less than 2 characters,
+ * it can't be a well-formed packet. Discard it.
+ */
+ if( cq_tent_len( q ) < /* 2 + possible length byte */
+ (2 + (IS_TTY_PKT(sc) ? 0 : 1)) )
+ {
+ result = BRL1_PROTOCOL;
+ cq_discard_tent( q );
+ STATE_SET( sc, BRL1_FLAG );
+ done = 1;
+ continue;
+ }
+
+ /* check CRC */
+
+ /* accumulate CRC, starting with the header byte and
+ * ending with the transmitted CRC. This should
+ * result in a known good value.
+ */
+ crc = crc16_calc( INIT_CRC, LAST_HDR_GET(sc) );
+ for( i = (q->ipos + (IS_TTY_PKT(sc) ? 0 : 2)) % BRL1_QSIZE;
+ i != q->tent_next;
+ i = (i + 1) % BRL1_QSIZE )
+ {
+ crc = crc16_calc( crc, q->buf[i] );
+ }
+
+ /* verify the caclulated crc against the "good" crc value;
+ * if we fail, discard the bad packet and return an error.
+ */
+ if( crc != (unsigned short)GOOD_CRC ) {
+ result = BRL1_CRC;
+ cq_discard_tent( q );
+ STATE_SET( sc, BRL1_FLAG );
+ done = 1;
+ continue;
+ }
+
+ /* so the crc check was ok. Now we discard the CRC
+ * from the end of the received bytes.
+ */
+ q->tent_next += (BRL1_QSIZE - 2);
+ q->tent_next %= BRL1_QSIZE;
+
+ /* get the subchannel and lock it */
+ subch = &(sc->subch[SUBCH( LAST_HDR_GET(sc) )]);
+ SUBCH_DATA_LOCK( subch, sch_pl );
+
+ /* if this isn't a console packet, we need to record
+ * a length byte
+ */
+ if( !IS_TTY_PKT(sc) ) {
+ q->buf[q->ipos] = cq_tent_len( q ) - 1;
+ }
+
+ /* record packet for posterity */
+ cq_commit_tent( q );
+ result = BRL1_VALID;
+
+ /* notify subchannel owner that there's something
+ * on the queue for them
+ */
+ atomicAddInt( &(subch->packet_arrived), 1);
+ callup = subch->rx_notify;
+ SUBCH_DATA_UNLOCK( subch, sch_pl );
+
+ if( callup ) {
+ L1_CONS_HW_UNLOCK( sc );
+ L1SC_RECV_UNLOCK( sc, pl );
+ (*callup)( sc, SUBCH(LAST_HDR_GET(sc)) );
+ L1SC_RECV_LOCK( sc, pl );
+ L1_CONS_HW_LOCK( sc );
+ }
+ continue; /* go back for more! */
+ }
+
+ /* none of the special cases applied; we've got a normal
+ * body character
+ */
+ cq_tent_add( q, c );
+
+ break;
+
+ case BRL1_ESC:
+ /* saw an escape character. The next character will need
+ * to be unescaped.
+ */
+
+ q = sc->subch[ SUBCH( LAST_HDR_GET(sc) ) ].iqp;
+ ASSERT(q);
+
+ /* if the queue we want to write into is full, don't read from
+ * the uart (this provides backpressure to the L1 side)
+ */
+ if( cq_tent_full( q ) ) {
+ result = BRL1_FULL_Q;
+ done = 1;
+ continue;
+ }
+
+ if( !read_uart( sc, &c, &result ) ) {
+
+ /* error reading uart */
+ if( c != UART_NO_CHAR ) {
+ cq_discard_tent( q );
+ STATE_SET( sc, BRL1_IDLE );
+ }
+ done = 1;
+ continue;
+ }
+
+ if( c == BRL1_FLAG_CH ) {
+ /* flag after escape is an error */
+ STATE_SET( sc, BRL1_FLAG );
+ cq_discard_tent( q );
+ result = BRL1_PROTOCOL;
+ done = 1;
+ continue;
+ }
+
+ if( c == BRL1_ESC_CH ) {
+ /* two consecutive escapes is an error */
+ STATE_SET( sc, BRL1_IDLE );
+ cq_discard_tent( q );
+ result = BRL1_PROTOCOL;
+ done = 1;
+ continue;
+ }
+
+ /* otherwise, we've got a character that needs
+ * to be unescaped
+ */
+ cq_tent_add( q, (c ^ BRL1_XOR_CH) );
+ STATE_SET( sc, BRL1_BODY );
+
+ break;
+
+ } /* end of switch( STATE_GET(sc) ) */
+ } /* end of while(!done) */
+
+ L1_CONS_HW_UNLOCK( sc );
+ L1SC_RECV_UNLOCK(sc, pl);
+
+ return result;
+}
+
+
+/* brl1_init initializes the Bedrock/L1 protocol layer. This includes
+ * zeroing out the send and receive state information.
+ */
+
+void
+brl1_init( l1sc_t *sc, nasid_t nasid, net_vec_t uart )
+{
+ int i;
+ brl1_sch_t *subch;
+
+ bzero( sc, sizeof( *sc ) );
+ sc->nasid = nasid;
+ sc->uart = uart;
+ sc->getc_f = (uart == BRL1_LOCALUART ? uart_getc : rtr_uart_getc);
+ sc->putc_f = (uart == BRL1_LOCALUART ? uart_putc : rtr_uart_putc);
+ sc->sol = 1;
+ subch = sc->subch;
+
+ /* initialize L1 subchannels
+ */
+
+ /* assign processor TTY channels */
+ for( i = 0; i < CPUS_PER_NODE; i++, subch++ ) {
+ subch->use = BRL1_SUBCH_RSVD;
+ subch->packet_arrived = 0;
+ spinlock_init( &(subch->data_lock), NULL );
+ sv_init( &(subch->arrive_sv), SV_FIFO, NULL );
+ subch->tx_notify = NULL;
+ /* (for now, drop elscuart packets in the kernel) */
+ subch->rx_notify = brl1_discard_packet;
+ subch->iqp = &sc->garbage_q;
+ }
+
+ /* assign system TTY channel (first free subchannel after each
+ * processor's individual TTY channel has been assigned)
+ */
+ subch->use = BRL1_SUBCH_RSVD;
+ subch->packet_arrived = 0;
+ spinlock_init( &(subch->data_lock), NULL );
+ sv_init( &(subch->arrive_sv), SV_FIFO, NULL );
+ subch->tx_notify = NULL;
+ if( sc->uart == BRL1_LOCALUART ) {
+ subch->iqp = kmem_zalloc_node( sizeof(sc_cq_t), KM_NOSLEEP,
+ NASID_TO_COMPACT_NODEID(nasid) );
+ ASSERT( subch->iqp );
+ cq_init( subch->iqp );
+ subch->rx_notify = NULL;
+ }
+ else {
+ /* we shouldn't be getting console input from remote UARTs */
+ subch->iqp = &sc->garbage_q;
+ subch->rx_notify = brl1_discard_packet;
+ }
+ subch++; i++;
+
+ /* "reserved" subchannels (0x05-0x0F); for now, throw away
+ * incoming packets
+ */
+ for( ; i < 0x10; i++, subch++ ) {
+ subch->use = BRL1_SUBCH_FREE;
+ subch->packet_arrived = 0;
+ subch->tx_notify = NULL;
+ subch->rx_notify = brl1_discard_packet;
+ subch->iqp = &sc->garbage_q;
+ }
+
+ /* remaining subchannels are free */
+ for( ; i < BRL1_NUM_SUBCHANS; i++, subch++ ) {
+ subch->use = BRL1_SUBCH_FREE;
+ subch->packet_arrived = 0;
+ subch->tx_notify = NULL;
+ subch->rx_notify = brl1_discard_packet;
+ subch->iqp = &sc->garbage_q;
+ }
+
+ /* initialize synchronization structures
+ */
+ spinlock_init( &(sc->send_lock), NULL );
+ spinlock_init( &(sc->recv_lock), NULL );
+ spinlock_init( &(sc->subch_lock), NULL );
+
+ if( sc->uart == BRL1_LOCALUART ) {
+ uart_init( sc, UART_BAUD_RATE );
+ }
+ else {
+ rtr_uart_init( sc, UART_BAUD_RATE );
+ }
+
+ /* Set up remaining fields using L1 command functions-- elsc_module_get
+ * to read the module id, elsc_debug_get to see whether or not we're
+ * in verbose mode.
+ */
+ {
+ extern int elsc_module_get(l1sc_t *);
+
+ sc->modid = elsc_module_get( sc );
+ sc->modid =
+ (sc->modid < 0 ? INVALID_MODULE : sc->modid);
+
+ sc->verbose = 1;
+ }
+}
+
+
+/*********************************************************************
+ * These are interrupt-related functions used in the kernel to service
+ * the L1.
+ */
+
+/*
+ * brl1_intrd is the function which is called in a loop by the
+ * xthread that services L1 interrupts.
+ */
+#ifdef IRIX
+void
+brl1_intrd( struct eframe_s *ep )
+{
+ u_char isr_reg;
+ l1sc_t *sc = get_elsc();
+
+ isr_reg = READ_L1_UART_REG(sc->nasid, REG_ISR);
+
+ while( isr_reg & (ISR_RxRDY | ISR_TxRDY) ) {
+
+ if( isr_reg & ISR_RxRDY ) {
+ brl1_receive(sc);
+ }
+ if( (isr_reg & ISR_TxRDY) ||
+ (sc->send_in_use && UART_PUTC_READY(sc->nasid)) )
+ {
+ brl1_send_cont(sc);
+ }
+ isr_reg = READ_L1_UART_REG(sc->nasid, REG_ISR);
+ }
+
+ /* uart interrupts were blocked at bedrock when the the interrupt
+ * was initially answered; reenable them now
+ */
+ intr_unblock_bit( sc->intr_cpu, UART_INTR );
+ ep = ep; /* placate the compiler */
+}
+#endif
+
+
+
+/* brl1_intr is called directly from the uart interrupt; after it runs, the
+ * interrupt "daemon" xthread is signalled to continue.
+ */
+#ifdef IRIX
+void
+brl1_intr( struct eframe_s *ep )
+{
+ /* Disable the UART interrupt, giving the xthread time to respond.
+ * When the daemon (xthread) finishes doing its thing, it will
+ * unblock the interrupt.
+ */
+ intr_block_bit( get_elsc()->intr_cpu, UART_INTR );
+ ep = ep; /* placate the compiler */
+}
+
+
+/* set up uart interrupt handling for this node's uart
+ */
+void
+brl1_connect_intr( l1sc_t *sc )
+{
+ cpuid_t last_cpu;
+
+ sc->intr_cpu = nodepda->node_first_cpu;
+
+ if( intr_connect_level(sc->intr_cpu, UART_INTR, INTPEND0_MAXMASK,
+ (intr_func_t)brl1_intrd, 0,
+ (intr_func_t)brl1_intr) )
+ cmn_err(CE_PANIC, "brl1_connect_intr: Can't connect UART interrupt.");
+
+ uart_enable_recv_intr( sc );
+}
+#endif /* IRIX */
+
+#ifdef SABLE
+/* this function is called periodically to generate fake interrupts
+ * and allow brl1_intrd to send/receive characters
+ */
+void
+hubuart_service( void )
+{
+ l1sc_t *sc = get_elsc();
+ /* note that we'll lose error state by reading the lsr_reg.
+ * This is probably ok in the frictionless domain of sable.
+ */
+ int lsr_reg;
+ nasid_t nasid = sc->nasid;
+ lsr_reg = READ_L1_UART_REG( nasid, REG_LSR );
+ if( lsr_reg & (LSR_RCA | LSR_XSRE) ) {
+ REMOTE_HUB_PI_SEND_INTR(0, 0, UART_INTR);
+ }
+}
+#endif /* SABLE */
+
+
+/*********************************************************************
+ * The following function allows the kernel to "go around" the
+ * uninitialized l1sc structure to allow console output during
+ * early system startup.
+ */
+
+/* These are functions to use from serial_in/out when in protocol
+ * mode to send and receive uart control regs.
+ */
+void
+brl1_send_control(int offset, int value)
+{
+ nasid_t nasid = get_nasid();
+ WRITE_L1_UART_REG(nasid, offset, value);
+}
+
+int
+brl1_get_control(int offset)
+{
+ nasid_t nasid = get_nasid();
+ return(READ_L1_UART_REG(nasid, offset));
+}
+
+#define PUTCHAR(ch) \
+ { \
+ while( !(READ_L1_UART_REG( nasid, REG_LSR ) & LSR_XHRE) ); \
+ WRITE_L1_UART_REG( nasid, REG_DAT, (ch) ); \
+ }
+
+int
+brl1_send_console_packet( char *str, int len )
+{
+ int sent = len;
+ char crc_char;
+ unsigned short crc = INIT_CRC;
+ nasid_t nasid = get_nasid();
+
+ PUTCHAR( BRL1_FLAG_CH );
+ PUTCHAR( BRL1_EVENT | SC_CONS_SYSTEM );
+ crc = crc16_calc( crc, (BRL1_EVENT | SC_CONS_SYSTEM) );
+
+ while( len ) {
+
+ if( (*str == BRL1_FLAG_CH) || (*str == BRL1_ESC_CH) ) {
+ PUTCHAR( BRL1_ESC_CH );
+ PUTCHAR( (*str) ^ BRL1_XOR_CH );
+ }
+ else {
+ PUTCHAR( *str );
+ }
+
+ crc = crc16_calc( crc, *str );
+
+ str++; len--;
+ }
+
+ crc ^= 0xffff;
+ crc_char = crc & 0xff;
+ if( (crc_char == BRL1_ESC_CH) || (crc_char == BRL1_FLAG_CH) ) {
+ crc_char ^= BRL1_XOR_CH;
+ PUTCHAR( BRL1_ESC_CH );
+ }
+ PUTCHAR( crc_char );
+ crc_char = (crc >> 8) & 0xff;
+ if( (crc_char == BRL1_ESC_CH) || (crc_char == BRL1_FLAG_CH) ) {
+ crc_char ^= BRL1_XOR_CH;
+ PUTCHAR( BRL1_ESC_CH );
+ }
+ PUTCHAR( crc_char );
+ PUTCHAR( BRL1_FLAG_CH );
+
+ return sent - len;
+}
+
+
+/*********************************************************************
+ * l1_cons functions
+ *
+ * These allow the L1 to act as the system console. They're intended
+ * to abstract away most of the br/l1 internal details from the
+ * _L1_cons_* functions (in the prom-- see "l1_console.c") and
+ * l1_* functions (in the kernel-- see "sio_l1.c") that they support.
+ *
+ */
+
+int
+l1_cons_poll( l1sc_t *sc )
+{
+ /* in case this gets called before the l1sc_t structure for the module_t
+ * struct for this node is initialized (i.e., if we're called with a
+ * zero l1sc_t pointer)...
+ */
+ if( !sc ) {
+ return 0;
+ }
+
+ if( sc->subch[SC_CONS_SYSTEM].packet_arrived ) {
+ return 1;
+ }
+
+ brl1_receive( sc );
+
+ if( sc->subch[SC_CONS_SYSTEM].packet_arrived ) {
+ return 1;
+ }
+ return 0;
+}
+
+
+/* pull a character off of the system console queue (if one is available)
+ */
+int
+l1_cons_getc( l1sc_t *sc )
+{
+ int c;
+#ifdef SPINLOCKS_WORK
+ int pl;
+#endif
+ brl1_sch_t *subch = &(sc->subch[SC_CONS_SYSTEM]);
+ sc_cq_t *q = subch->iqp;
+
+ if( !l1_cons_poll( sc ) ) {
+ return 0;
+ }
+
+ SUBCH_DATA_LOCK( subch, pl );
+ if( cq_empty( q ) ) {
+ subch->packet_arrived = 0;
+ SUBCH_DATA_UNLOCK( subch, pl );
+ return 0;
+ }
+ cq_rem( q, c );
+ if( cq_empty( q ) )
+ subch->packet_arrived = 0;
+ SUBCH_DATA_UNLOCK( subch, pl );
+
+ return c;
+}
+
+
+/* initialize the system console subchannel
+ */
+void
+l1_cons_init( l1sc_t *sc )
+{
+#ifdef SPINLOCKS_WORK
+ int pl;
+#endif
+ brl1_sch_t *subch = &(sc->subch[SC_CONS_SYSTEM]);
+
+ SUBCH_DATA_LOCK( subch, pl );
+ subch->packet_arrived = 0;
+ cq_init( subch->iqp );
+ SUBCH_DATA_UNLOCK( subch, pl );
+}
+
+
+/*
+ * Write a message to the L1 on the system console subchannel.
+ *
+ * Danger: don't use a non-zero value for the wait parameter unless you're
+ * someone important (like a kernel error message).
+ */
+int
+l1_cons_write( l1sc_t *sc, char *msg, int len, int wait )
+{
+ return( brl1_send( sc, msg, len, (SC_CONS_SYSTEM | BRL1_EVENT), wait ) );
+}
+
+
+/*
+ * Read as many characters from the system console receive queue as are
+ * available there (up to avail bytes).
+ */
+int
+l1_cons_read( l1sc_t *sc, char *buf, int avail )
+{
+ int pl;
+ int before_wrap, after_wrap;
+ brl1_sch_t *subch = &(sc->subch[SC_CONS_SYSTEM]);
+ sc_cq_t *q = subch->iqp;
+
+ if( !(subch->packet_arrived) )
+ return 0;
+
+ SUBCH_DATA_LOCK( subch, pl );
+ if( q->opos > q->ipos ) {
+ before_wrap = BRL1_QSIZE - q->opos;
+ if( before_wrap >= avail ) {
+ before_wrap = avail;
+ after_wrap = 0;
+ }
+ else {
+ avail -= before_wrap;
+ after_wrap = q->ipos;
+ if( after_wrap > avail )
+ after_wrap = avail;
+ }
+ }
+ else {
+ before_wrap = q->ipos - q->opos;
+ if( before_wrap > avail )
+ before_wrap = avail;
+ after_wrap = 0;
+ }
+
+
+ BCOPY( q->buf + q->opos, buf, before_wrap );
+ if( after_wrap )
+ BCOPY( q->buf, buf + before_wrap, after_wrap );
+ q->opos = ((q->opos + before_wrap + after_wrap) % BRL1_QSIZE);
+
+ subch->packet_arrived = 0;
+ SUBCH_DATA_UNLOCK( subch, pl );
+
+ return( before_wrap + after_wrap );
+}
+
+
+/*
+ * Install a callback function for the system console subchannel
+ * to allow an upper layer to be notified when the send buffer
+ * has been emptied.
+ */
+void
+l1_cons_tx_notif( l1sc_t *sc, brl1_notif_t func )
+{
+ subch_set_tx_notify( sc, SC_CONS_SYSTEM, func );
+}
+
+
+/*
+ * Install a callback function for the system console subchannel
+ * to allow an upper layer to be notified when a packet has been
+ * received.
+ */
+void
+l1_cons_rx_notif( l1sc_t *sc, brl1_notif_t func )
+{
+ subch_set_rx_notify( sc, SC_CONS_SYSTEM, func );
+}
+
+
+
+
+/*********************************************************************
+ * The following functions and definitions implement the "message"-
+ * style interface to the L1 system controller.
+ *
+ * Note that throughout this file, "sc" generally stands for "system
+ * controller", while "subchannels" tend to be represented by
+ * variables with names like subch or ch.
+ *
+ */
+
+#ifdef L1_DEBUG
+#define L1_DBG_PRF(x) printf x
+#else
+#define L1_DBG_PRF(x)
+#endif
+
+/* sc_data_ready is called to signal threads that are blocked on
+ * l1 input.
+ */
+void
+sc_data_ready( l1sc_t *sc, int ch )
+{
+ brl1_sch_t *subch = &(sc->subch[ch]);
+ sv_signal( &(subch->arrive_sv) );
+}
+
+/* sc_open reserves a subchannel to send a request to the L1 (the
+ * L1's response will arrive on the same channel). The number
+ * returned by sc_open is the system controller subchannel
+ * acquired.
+ */
+int
+sc_open( l1sc_t *sc, uint target )
+{
+ /* The kernel version implements a locking scheme to arbitrate
+ * subchannel assignment.
+ */
+ int ch;
+ int pl;
+ brl1_sch_t *subch;
+
+ SUBCH_LOCK( sc, pl );
+
+ /* Look for a free subchannel. Subchannels 0-15 are reserved
+ * for other purposes.
+ */
+ for( subch = &(sc->subch[BRL1_CMD_SUBCH]), ch = BRL1_CMD_SUBCH;
+ ch < BRL1_NUM_SUBCHANS; subch++, ch++ ) {
+ if( subch->use == BRL1_SUBCH_FREE )
+ break;
+ }
+
+ if( ch == BRL1_NUM_SUBCHANS ) {
+ /* there were no subchannels available! */
+ SUBCH_UNLOCK( sc, pl );
+ return SC_NSUBCH;
+ }
+
+ subch->use = BRL1_SUBCH_RSVD;
+ SUBCH_UNLOCK( sc, pl );
+
+ subch->packet_arrived = 0;
+ subch->target = target;
+ sv_init( &(subch->arrive_sv), SV_FIFO, NULL );
+ spinlock_init( &(subch->data_lock), NULL );
+ subch->tx_notify = NULL;
+ subch->rx_notify = sc_data_ready;
+ subch->iqp = kmem_zalloc_node( sizeof(sc_cq_t), KM_NOSLEEP,
+ NASID_TO_COMPACT_NODEID(sc->nasid) );
+ ASSERT( subch->iqp );
+ cq_init( subch->iqp );
+
+ return ch;
+}
+
+
+/* sc_close frees a Bedrock<->L1 subchannel.
+ */
+int
+sc_close( l1sc_t *sc, int ch )
+{
+ brl1_sch_t *subch;
+ int pl;
+
+ SUBCH_LOCK( sc, pl );
+ subch = &(sc->subch[ch]);
+ if( subch->use != BRL1_SUBCH_RSVD ) {
+ /* we're trying to close a subchannel that's not open */
+ return SC_NOPEN;
+ }
+
+ subch->packet_arrived = 0;
+ subch->use = BRL1_SUBCH_FREE;
+
+ sv_broadcast( &(subch->arrive_sv) );
+ sv_destroy( &(subch->arrive_sv) );
+ spinlock_destroy( &(subch->data_lock) );
+
+ ASSERT( subch->iqp && (subch->iqp != &sc->garbage_q) );
+ kmem_free( subch->iqp, sizeof(sc_cq_t) );
+ subch->iqp = &sc->garbage_q;
+
+ SUBCH_UNLOCK( sc, pl );
+
+ return SC_SUCCESS;
+}
+
+
+/* sc_construct_msg builds a bedrock-to-L1 request in the supplied
+ * buffer. Returns the length of the message. The
+ * safest course when passing a buffer to be filled in is to use
+ * BRL1_QSIZE as the buffer size.
+ *
+ * Command arguments are passed as type/argument pairs, i.e., to
+ * pass the number 5 as an argument to an L1 command, call
+ * sc_construct_msg as follows:
+ *
+ * char msg[BRL1_QSIZE];
+ * msg_len = sc_construct_msg( msg,
+ * BRL1_QSIZE,
+ * target_component,
+ * L1_ADDR_TASK_BOGUSTASK,
+ * L1_BOGUSTASK_REQ_BOGUSREQ,
+ * 2,
+ * L1_ARG_INT, 5 );
+ *
+ * To pass an additional ASCII argument, you'd do the following:
+ *
+ * char *str;
+ * ... str points to a null-terminated ascii string ...
+ * msg_len = sc_construct_msg( msg,
+ * BRL1_QSIZE,
+ * target_component,
+ * L1_ADDR_TASK_BOGUSTASK,
+ * L1_BOGUSTASK_REQ_BOGUSREQ,
+ * 4,
+ * L1_ARG_INT, 5,
+ * L1_ARG_ASCII, str );
+ *
+ * Finally, arbitrary data of unknown type is passed using the argtype
+ * code L1_ARG_UNKNOWN, a data length, and a buffer pointer, e.g.
+ *
+ * msg_len = sc_construct_msg( msg,
+ * BRL1_QSIZE,
+ * target_component,
+ * L1_ADDR_TASK_BOGUSTASK,
+ * L1_BOGUSTASK_REQ_BOGUSREQ,
+ * 3,
+ * L1_ARG_UNKNOWN, 32, bufptr );
+ *
+ * ...passes 32 bytes of data starting at bufptr. Note that no string or
+ * "unknown"-type argument should be long enough to overflow the message
+ * buffer.
+ *
+ * To construct a message for an L1 command that requires no arguments,
+ * you'd use the following:
+ *
+ * msg_len = sc_construct_msg( msg,
+ * BRL1_QSIZE,
+ * target_component,
+ * L1_ADDR_TASK_BOGUSTASK,
+ * L1_BOGUSTASK_REQ_BOGUSREQ,
+ * 0 );
+ *
+ * The final 0 means "no varargs". Notice that this parameter is used to hold
+ * the number of additional arguments to sc_construct_msg, _not_ the actual
+ * number of arguments used by the L1 command (so 2 per L1_ARG_[INT,ASCII]
+ * type argument, and 3 per L1_ARG_UNKOWN type argument). A call to construct
+ * an L1 command which required three integer arguments and two arguments of
+ * some arbitrary (unknown) type would pass 12 as the value for this parameter.
+ *
+ * ENDIANNESS WARNING: The following code does a lot of copying back-and-forth
+ * between byte arrays and four-byte big-endian integers. Depending on the
+ * system controller connection and endianness of future architectures, some
+ * rewriting might be necessary.
+ */
+int
+sc_construct_msg( l1sc_t *sc, /* system controller struct */
+ int ch, /* subchannel for this message */
+ char *msg, /* message buffer */
+ int msg_len, /* size of message buffer */
+ l1addr_t addr_task, /* target system controller task */
+ short req_code, /* 16-bit request code */
+ int req_nargs, /* # of arguments (varargs) passed */
+ ... ) /* any additional parameters */
+{
+ uint32_t buf32; /* 32-bit buffer used to bounce things around */
+ void *bufptr; /* used to hold command argument addresses */
+ va_list al; /* variable argument list */
+ int index; /* current index into msg buffer */
+ int argno; /* current position in varargs list */
+ int l1_argno; /* running total of arguments to l1 */
+ int l1_arg_t; /* argument type/length */
+ int l1_argno_byte; /* offset of argument count byte */
+
+ index = argno = 0;
+
+ /* set up destination address */
+ if( (msg_len -= sizeof( buf32 )) < 0 )
+ return -1;
+ L1_ADDRESS_TO_TASK( &buf32, sc->subch[ch].target, addr_task );
+ COPY_INT_TO_BUFFER(msg, index, buf32);
+
+ /* copy request code */
+ if( (msg_len -= 2) < 0 )
+ return( -1 );
+ msg[index++] = ((req_code >> 8) & 0xff);
+ msg[index++] = (req_code & 0xff);
+
+ if( !req_nargs ) {
+ return index;
+ }
+
+ /* reserve a byte for the argument count */
+ if( (msg_len -= 1) < 0 )
+ return( -1 );
+ l1_argno_byte = index++;
+ l1_argno = 0;
+
+ /* copy additional arguments */
+ va_start( al, req_nargs );
+ while( argno < req_nargs ) {
+ l1_argno++;
+ l1_arg_t = va_arg( al, int ); argno++;
+ switch( l1_arg_t )
+ {
+ case L1_ARG_INT:
+ if( (msg_len -= (sizeof( buf32 ) + 1)) < 0 )
+ return( -1 );
+ msg[index++] = L1_ARG_INT;
+ buf32 = (unsigned)va_arg( al, int ); argno++;
+ COPY_INT_TO_BUFFER(msg, index, buf32);
+ break;
+
+ case L1_ARG_ASCII:
+ bufptr = va_arg( al, char* ); argno++;
+ if( (msg_len -= (strlen( bufptr ) + 2)) < 0 )
+ return( -1 );
+ msg[index++] = L1_ARG_ASCII;
+ strcpy( (char *)&(msg[index]), (char *)bufptr );
+ index += (strlen( bufptr ) + 1); /* include terminating null */
+ break;
+
+ case L1_ARG_UNKNOWN:
+ {
+ int arglen;
+
+ arglen = va_arg( al, int ); argno++;
+ bufptr = va_arg( al, void* ); argno++;
+ if( (msg_len -= (arglen + 1)) < 0 )
+ return( -1 );
+ msg[index++] = L1_ARG_UNKNOWN | arglen;
+ BCOPY( bufptr, &(msg[index]), arglen );
+ index += arglen;
+ break;
+ }
+
+ default: /* unhandled argument type */
+ return -1;
+ }
+ }
+
+ va_end( al );
+ msg[l1_argno_byte] = l1_argno;
+
+ return index;
+}
+
+
+
+/* sc_interpret_resp verifies an L1 response to a bedrock request, and
+ * breaks the response data up into the constituent parts. If the
+ * response message indicates error, or if a mismatch is found in the
+ * expected number and type of arguments, an error is returned. The
+ * arguments to this function work very much like the arguments to
+ * sc_construct_msg, above, except that L1_ARG_INTs must be followed
+ * by a _pointer_ to an integer that can be filled in by this function.
+ */
+int
+sc_interpret_resp( char *resp, /* buffer received from L1 */
+ int resp_nargs, /* number of _varargs_ passed in */
+ ... )
+{
+ uint32_t buf32; /* 32-bit buffer used to bounce things around */
+ void *bufptr; /* used to hold response field addresses */
+ va_list al; /* variable argument list */
+ int index; /* current index into response buffer */
+ int argno; /* current position in varargs list */
+ int l1_fldno; /* number of resp fields received from l1 */
+ int l1_fld_t; /* field type/length */
+
+ index = argno = 0;
+
+#if defined(L1_DEBUG)
+#define DUMP_RESP \
+ { \
+ int ix; \
+ char outbuf[512]; \
+ sprintf( outbuf, "sc_interpret_resp error line %d: ", __LINE__ ); \
+ for( ix = 0; ix < 16; ix++ ) { \
+ sprintf( &outbuf[strlen(outbuf)], "%x ", resp[ix] ); \
+ } \
+ printk( "%s\n", outbuf ); \
+ }
+#else
+#define DUMP_RESP
+#endif /* L1_DEBUG */
+
+ /* check response code */
+ COPY_BUFFER_TO_INT(resp, index, buf32);
+ if( buf32 != L1_RESP_OK ) {
+ DUMP_RESP;
+ return buf32;
+ }
+
+ /* get number of response fields */
+ l1_fldno = resp[index++];
+
+ va_start( al, resp_nargs );
+
+ /* copy out response fields */
+ while( argno < resp_nargs ) {
+ l1_fldno--;
+ l1_fld_t = va_arg( al, int ); argno++;
+ switch( l1_fld_t )
+ {
+ case L1_ARG_INT:
+ if( resp[index++] != L1_ARG_INT ) {
+ /* type mismatch */
+ va_end( al );
+ DUMP_RESP;
+ return -1;
+ }
+ bufptr = va_arg( al, int* ); argno++;
+ COPY_BUFFER_TO_BUFFER(resp, index, bufptr);
+ break;
+
+ case L1_ARG_ASCII:
+ if( resp[index++] != L1_ARG_ASCII ) {
+ /* type mismatch */
+ va_end( al );
+ DUMP_RESP;
+ return -1;
+ }
+ bufptr = va_arg( al, char* ); argno++;
+ strcpy( (char *)bufptr, (char *)&(resp[index]) );
+ /* include terminating null */
+ index += (strlen( &(resp[index]) ) + 1);
+ break;
+
+ default:
+ if( (l1_fld_t & L1_ARG_UNKNOWN) == L1_ARG_UNKNOWN )
+ {
+ int *arglen;
+
+ arglen = va_arg( al, int* ); argno++;
+ bufptr = va_arg( al, void* ); argno++;
+ *arglen = ((resp[index++] & ~L1_ARG_UNKNOWN) & 0xff);
+ BCOPY( &(resp[index]), bufptr, *arglen );
+ index += (*arglen);
+ }
+
+ else {
+ /* unhandled type */
+ va_end( al );
+ DUMP_RESP;
+ return -1;
+ }
+ }
+ }
+ va_end( al );
+
+ if( (l1_fldno != 0) || (argno != resp_nargs) ) {
+ /* wrong number of arguments */
+ DUMP_RESP;
+ return -1;
+ }
+ return 0;
+}
+
+
+
+
+/* sc_send takes as arguments a system controller struct, a
+ * buffer which contains a Bedrock<->L1 "request" message,
+ * the message length, and the subchannel (presumably obtained
+ * from an earlier invocation of sc_open) over which the
+ * message is to be sent. The final argument ("wait") indicates
+ * whether the send is to be performed synchronously or not.
+ *
+ * sc_send returns either zero or an error value. Synchronous sends
+ * (wait != 0) will not return until the data has actually been sent
+ * to the UART. Synchronous sends generally receive privileged
+ * treatment. The intent is that they be used sparingly, for such
+ * purposes as kernel printf's (the "ducons" routines). Run-of-the-mill
+ * console output and L1 requests should NOT use a non-zero value
+ * for wait.
+ */
+int
+sc_send( l1sc_t *sc, int ch, char *msg, int len, int wait )
+{
+ char type_and_subch;
+ int result;
+
+ if( (ch < 0) || ( ch >= BRL1_NUM_SUBCHANS) ) {
+ return SC_BADSUBCH;
+ }
+
+ /* Verify that this is an open subchannel
+ */
+ if( sc->subch[ch].use == BRL1_SUBCH_FREE )
+ {
+ return SC_NOPEN;
+ }
+
+ type_and_subch = (BRL1_REQUEST | ((u_char)ch));
+ result = brl1_send( sc, msg, len, type_and_subch, wait );
+
+ /* If we sent as much as we asked to, return "ok". */
+ if( result == len )
+ return( SC_SUCCESS );
+
+ /* Or, if we sent less, than either the UART is busy or
+ * we're trying to send too large a packet anyway.
+ */
+ else if( result >= 0 && result < len )
+ return( SC_BUSY );
+
+ /* Or, if something else went wrong (result < 0), then
+ * return that error value.
+ */
+ else
+ return( result );
+}
+
+
+
+/* subch_pull_msg pulls a message off the receive queue for subch
+ * and places it the buffer pointed to by msg. This routine should only
+ * be called when the caller already knows a message is available on the
+ * receive queue (and, in the kernel, only when the subchannel data lock
+ * is held by the caller).
+ */
+static void
+subch_pull_msg( brl1_sch_t *subch, char *msg, int *len )
+{
+ sc_cq_t *q; /* receive queue */
+ int before_wrap, /* packet may be split into two different */
+ after_wrap; /* pieces to acommodate queue wraparound */
+
+ /* pull message off the receive queue */
+ q = subch->iqp;
+
+ cq_rem( q, *len ); /* remove length byte and store */
+ cq_discard( q ); /* remove type/subch byte and discard */
+
+ if ( *len > 0 )
+ (*len)--; /* don't count type/subch byte in length returned */
+
+ if( (q->opos + (*len)) > BRL1_QSIZE ) {
+ before_wrap = BRL1_QSIZE - q->opos;
+ after_wrap = (*len) - before_wrap;
+ }
+ else {
+ before_wrap = (*len);
+ after_wrap = 0;
+ }
+
+ BCOPY( q->buf + q->opos, msg, before_wrap );
+ if( after_wrap ) {
+ BCOPY( q->buf, msg + before_wrap, after_wrap );
+ q->opos = after_wrap;
+ }
+ else {
+ q->opos = ((q->opos + before_wrap) & (BRL1_QSIZE - 1));
+ }
+ atomicAddInt( &(subch->packet_arrived), -1 );
+}
+
+
+/* sc_recv_poll can be called as a blocking or non-blocking function;
+ * it attempts to pull a message off of the subchannel specified
+ * in the argument list (ch).
+ *
+ * The "block" argument, if non-zero, is interpreted as a timeout
+ * delay (to avoid permanent waiting).
+ */
+
+int
+sc_recv_poll( l1sc_t *sc, int ch, char *msg, int *len, uint64_t block )
+{
+ int pl; /* lock cookie */
+ int is_msg = 0;
+ brl1_sch_t *subch = &(sc->subch[ch]);
+
+ rtc_time_t exp_time = rtc_time() + block;
+
+ /* sanity check-- make sure this is an open subchannel */
+ if( subch->use == BRL1_SUBCH_FREE )
+ return( SC_NOPEN );
+
+ do {
+
+ /* kick the next lower layer and see if it pulls anything in
+ */
+ brl1_receive( sc );
+ is_msg = subch->packet_arrived;
+
+ } while( block && !is_msg && (rtc_time() < exp_time) );
+
+ if( !is_msg ) {
+ /* no message and we didn't care to wait for one */
+ return( SC_NMSG );
+ }
+
+ SUBCH_DATA_LOCK( subch, pl );
+ subch_pull_msg( subch, msg, len );
+ SUBCH_DATA_UNLOCK( subch, pl );
+
+ return( SC_SUCCESS );
+}
+
+
+/* Like sc_recv_poll, sc_recv_intr can be called in either a blocking
+ * or non-blocking mode. Rather than polling until an appointed timeout,
+ * however, sc_recv_intr sleeps on a syncrhonization variable until a
+ * signal from the lower layer tells us that a packet has arrived.
+ *
+ * sc_recv_intr can't be used with remote (router) L1s.
+ */
+int
+sc_recv_intr( l1sc_t *sc, int ch, char *msg, int *len, uint64_t block )
+{
+ int pl; /* lock cookie */
+ int is_msg = 0;
+ brl1_sch_t *subch = &(sc->subch[ch]);
+
+ do {
+ SUBCH_DATA_LOCK(subch, pl);
+ is_msg = subch->packet_arrived;
+ if( !is_msg && block ) {
+ /* wake me when you've got something */
+ subch->rx_notify = sc_data_ready;
+ sv_wait( &(subch->arrive_sv), 0, &(subch->data_lock), pl );
+ if( subch->use == BRL1_SUBCH_FREE ) {
+ /* oops-- somebody closed our subchannel while we were
+ * sleeping!
+ */
+
+ /* no need to unlock since the channel's closed anyhow */
+ return( SC_NOPEN );
+ }
+ }
+ } while( !is_msg && block );
+
+ if( !is_msg ) {
+ /* no message and we didn't care to wait for one */
+ SUBCH_DATA_UNLOCK( subch, pl );
+ return( SC_NMSG );
+ }
+
+ subch_pull_msg( subch, msg, len );
+ SUBCH_DATA_UNLOCK( subch, pl );
+
+ return( SC_SUCCESS );
+}
+
+/* sc_command implements a (blocking) combination of sc_send and sc_recv.
+ * It is intended to be the SN1 equivalent of SN0's "elsc_command", which
+ * issued a system controller command and then waited for a response from
+ * the system controller before returning.
+ *
+ * cmd points to the outgoing command; resp points to the buffer in
+ * which the response is to be stored. Both buffers are assumed to
+ * be the same length; if there is any doubt as to whether the
+ * response buffer is long enough to hold the L1's response, then
+ * make it BRL1_QSIZE bytes-- no Bedrock<->L1 message can be any
+ * bigger.
+ *
+ * Be careful using the same buffer for both cmd and resp; it could get
+ * hairy if there were ever an L1 command reqeuest that spanned multiple
+ * packets. (On the other hand, that would require some additional
+ * rewriting of the L1 command interface anyway.)
+ */
+#define __RETRIES 50
+#define __WAIT_SEND ( sc->uart != BRL1_LOCALUART )
+#define __WAIT_RECV 10000000
+
+
+int
+sc_command( l1sc_t *sc, int ch, char *cmd, char *resp, int *len )
+{
+#ifndef CONFIG_SERIAL_SGI_L1_PROTOCOL
+ return SC_NMSG;
+#else
+ int result;
+ int retries;
+
+ if ( IS_RUNNING_ON_SIMULATOR() )
+ return SC_NMSG;
+
+ retries = __RETRIES;
+
+ while( (result = sc_send( sc, ch, cmd, *len, __WAIT_SEND )) < 0 ) {
+ if( result == SC_BUSY ) {
+ retries--;
+ if( retries <= 0 )
+ return result;
+ uart_delay(500);
+ }
+ else {
+ return result;
+ }
+ }
+
+ /* block on sc_recv_* */
+#ifdef notyet
+ if( sc->uart == BRL1_LOCALUART ) {
+ return( sc_recv_intr( sc, ch, resp, len, __WAIT_RECV ) );
+ }
+ else
+#endif
+ {
+ return( sc_recv_poll( sc, ch, resp, len, __WAIT_RECV ) );
+ }
+#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
+}
+
+/* sc_command_kern is a knuckle-dragging, no-patience version of sc_command
+ * used in situations where the kernel has a command that shouldn't be
+ * delayed until the send buffer clears. sc_command should be used instead
+ * under most circumstances.
+ */
+int
+sc_command_kern( l1sc_t *sc, int ch, char *cmd, char *resp, int *len )
+{
+#ifndef CONFIG_SERIAL_SGI_L1_PROTOCOL
+ return SC_NMSG;
+#else
+ int result;
+
+ if ( IS_RUNNING_ON_SIMULATOR() )
+ return SC_NMSG;
+
+ if( (result = sc_send( sc, ch, cmd, *len, 1 )) < 0 ) {
+ return result;
+ }
+
+ return( sc_recv_poll( sc, ch, resp, len, __WAIT_RECV ) );
+#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
+}
+
+
+
+/* sc_poll checks the queue corresponding to the given
+ * subchannel to see if there's anything available. If
+ * not, it kicks the brl1 layer and then checks again.
+ *
+ * Returns 1 if input is available on the given queue,
+ * 0 otherwise.
+ */
+int
+sc_poll( l1sc_t *sc, int ch )
+{
+ brl1_sch_t *subch = &(sc->subch[ch]);
+
+ if( subch->packet_arrived )
+ return 1;
+
+ brl1_receive( sc );
+
+ if( subch->packet_arrived )
+ return 1;
+
+ return 0;
+}
+
+/* for now, sc_init just calls brl1_init
+ */
+void
+sc_init( l1sc_t *sc, nasid_t nasid, net_vec_t uart )
+{
+ if ( !IS_RUNNING_ON_SIMULATOR() )
+ brl1_init( sc, nasid, uart );
+}
+
+/* sc_dispatch_env_event handles events sent from the system control
+ * network's environmental monitor tasks.
+ */
+static void
+sc_dispatch_env_event( uint code, int argc, char *args, int maxlen )
+{
+ int j, i = 0;
+ uint32_t ESPcode;
+
+ switch( code ) {
+ /* for now, all codes do the same thing: grab two arguments
+ * and print a cmn_err_tag message */
+ default:
+ /* check number of arguments */
+ if( argc != 2 ) {
+ L1_DBG_PRF(( "sc_dispatch_env_event: "
+ "expected 2 arguments, got %d\n", argc ));
+ return;
+ }
+
+ /* get ESP code (integer argument) */
+ if( args[i++] != L1_ARG_INT ) {
+ L1_DBG_PRF(( "sc_dispatch_env_event: "
+ "expected integer argument\n" ));
+ return;
+ }
+ /* WARNING: highly endian */
+ COPY_BUFFER_TO_INT(args, i, ESPcode);
+
+ /* verify string argument */
+ if( args[i++] != L1_ARG_ASCII ) {
+ L1_DBG_PRF(( "sc_dispatch_env_event: "
+ "expected an ASCII string\n" ));
+ return;
+ }
+ for( j = i; j < maxlen; j++ ) {
+ if( args[j] == '\0' ) break; /* found string termination */
+ }
+ if( j == maxlen ) {
+ j--;
+ L1_DBG_PRF(( "sc_dispatch_env_event: "
+ "message too long-- truncating\n" ));
+ }
+
+ /* strip out trailing cr/lf */
+ for( ;
+ j > 1 && ((args[j-1] == 0xd) || (args[j-1] == 0xa));
+ j-- );
+ args[j] = '\0';
+
+ /* strip out leading cr/lf */
+ for( ;
+ i < j && ((args[i] == 0xd) || (args[i] == 0xa));
+ i++ );
+
+ /* write the event to syslog */
+#ifdef IRIX
+ cmn_err_tag( ESPcode, CE_WARN, &(args[i]) );
+#endif
+ }
+}
+
+
+/* sc_event waits for events to arrive from the system controller, and
+ * prints appropriate messages to the syslog.
+ */
+static void
+sc_event( l1sc_t *sc, int ch )
+{
+ char event[BRL1_QSIZE];
+ int i;
+ int result;
+ int event_len;
+ uint32_t ev_src;
+ uint32_t ev_code;
+ int ev_argc;
+
+ while(1) {
+
+ bzero( event, BRL1_QSIZE );
+
+ /*
+ * wait for an event
+ */
+ result = sc_recv_intr( sc, ch, event, &event_len, 1 );
+ if( result != SC_SUCCESS ) {
+ cmn_err( CE_WARN, "Error receiving sysctl event on nasid %d\n",
+ sc->nasid );
+ }
+ else {
+ /*
+ * an event arrived; break it down into useful pieces
+ */
+#if defined(L1_DEBUG) && 0
+ int ix;
+ printf( "Event packet received:\n" );
+ for (ix = 0; ix < 64; ix++) {
+ printf( "%x%x ", ((event[ix] >> 4) & ((uint64_t)0xf)),
+ (event[ix] & ((uint64_t)0xf)) );
+ if( (ix % 16) == 0xf ) printf( "\n" );
+ }
+#endif /* L1_DEBUG */
+
+ i = 0;
+
+ /* get event source */
+ COPY_BUFFER_TO_INT(event, i, ev_src);
+ COPY_BUFFER_TO_INT(event, i, ev_code);
+
+ /* get arg count */
+ ev_argc = (event[i++] & 0xffUL);
+
+ /* dispatch events by task */
+ switch( (ev_src & L1_ADDR_TASK_MASK) >> L1_ADDR_TASK_SHFT )
+ {
+ case L1_ADDR_TASK_ENV: /* environmental monitor event */
+ sc_dispatch_env_event( ev_code, ev_argc, &(event[i]),
+ BRL1_QSIZE - i );
+ break;
+
+ default: /* unhandled task type */
+ L1_DBG_PRF(( "Unhandled event type received from system "
+ "controllers: source task %x\n",
+ (ev_src & L1_ADDR_TASK_MASK) >> L1_ADDR_TASK_SHFT
+ ));
+ }
+ }
+
+ }
+}
+
+/* sc_listen sets up a service thread to listen for incoming events.
+ */
+void
+sc_listen( l1sc_t *sc )
+{
+ int pl;
+ int result;
+ brl1_sch_t *subch;
+
+ char msg[BRL1_QSIZE];
+ int len; /* length of message being sent */
+ int ch; /* system controller subchannel used */
+
+ extern int msc_shutdown_pri;
+
+ /* grab the designated "event subchannel" */
+ SUBCH_LOCK( sc, pl );
+ subch = &(sc->subch[BRL1_EVENT_SUBCH]);
+ if( subch->use != BRL1_SUBCH_FREE ) {
+ SUBCH_UNLOCK( sc, pl );
+ cmn_err( CE_WARN, "sysctl event subchannel in use! "
+ "Not monitoring sysctl events.\n" );
+ return;
+ }
+ subch->use = BRL1_SUBCH_RSVD;
+ SUBCH_UNLOCK( sc, pl );
+
+ subch->packet_arrived = 0;
+ subch->target = BRL1_LOCALUART;
+ sv_init( &(subch->arrive_sv), SV_FIFO, NULL );
+ spinlock_init( &(subch->data_lock), NULL );
+ subch->tx_notify = NULL;
+ subch->rx_notify = sc_data_ready;
+ subch->iqp = kmem_zalloc_node( sizeof(sc_cq_t), KM_NOSLEEP,
+ NASID_TO_COMPACT_NODEID(sc->nasid) );
+ ASSERT( subch->iqp );
+ cq_init( subch->iqp );
+
+#ifdef LINUX_KERNEL_THREADS
+ /* set up a thread to listen for events */
+ sthread_create( "sysctl event handler", 0, 0, 0, msc_shutdown_pri,
+ KT_PS, (st_func_t *) sc_event,
+ (void *)sc, (void *)(uint64_t)BRL1_EVENT_SUBCH, 0, 0 );
+#endif
+
+ /* signal the L1 to begin sending events */
+ bzero( msg, BRL1_QSIZE );
+ ch = sc_open( sc, L1_ADDR_LOCAL );
+
+ if( (len = sc_construct_msg( sc, ch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_EVENT_SUBCH, 2,
+ L1_ARG_INT, BRL1_EVENT_SUBCH )) < 0 )
+ {
+ sc_close( sc, ch );
+ L1_DBG_PRF(( "Failure in sc_construct_msg (%d)\n", len ));
+ goto err_return;
+ }
+
+ result = sc_command_kern( sc, ch, msg, msg, &len );
+ if( result < 0 )
+ {
+ sc_close( sc, ch );
+ L1_DBG_PRF(( "Failure in sc_command_kern (%d)\n", result ));
+ goto err_return;
+ }
+
+ sc_close( sc, ch );
+
+ result = sc_interpret_resp( msg, 0 );
+ if( result < 0 )
+ {
+ L1_DBG_PRF(( "Failure in sc_interpret_resp (%d)\n", result ));
+ goto err_return;
+ }
+
+ /* everything went fine; just return */
+ return;
+
+err_return:
+ /* there was a problem; complain */
+ cmn_err( CE_WARN, "failed to set sysctl event-monitoring subchannel. "
+ "Sysctl events will not be monitored.\n" );
+}
+
+
+/*********************************************************************
+ * elscuart functions. These provide a uart-like interface to the
+ * bedrock/l1 protocol console channels. They are similar in form
+ * and intent to the elscuart_* functions defined for SN0 in elsc.c.
+ *
+ */
+
+int _elscuart_flush( l1sc_t *sc );
+
+/* Leave room in queue for CR/LF */
+#define ELSCUART_LINE_MAX (BRL1_QSIZE - 2)
+
+
+/*
+ * _elscuart_putc provides an entry point to the L1 interface driver;
+ * writes a single character to the output queue. Flushes at the
+ * end of each line, and translates newlines into CR/LF.
+ *
+ * The kernel should generally use l1_cons_write instead, since it assumes
+ * buffering, translation, prefixing, etc. are done at a higher
+ * level.
+ *
+ */
+int
+_elscuart_putc( l1sc_t *sc, int c )
+{
+ sc_cq_t *q;
+
+ q = &(sc->oq[ MAP_OQ(L1_ELSCUART_SUBCH(get_myid())) ]);
+
+ if( c != '\n' && c != '\r' && cq_used(q) >= ELSCUART_LINE_MAX ) {
+ cq_add( q, '\r' );
+ cq_add( q, '\n' );
+ _elscuart_flush( sc );
+ sc->sol = 1;
+ }
+
+ if( sc->sol && c != '\r' ) {
+ char prefix[16], *s;
+
+ if( cq_room( q ) < 8 && _elscuart_flush(sc) < 0 )
+ {
+ return -1;
+ }
+
+ if( sc->verbose )
+ {
+#ifdef SUPPORT_PRINTING_M_FORMAT
+ sprintf( prefix,
+ "%c %d%d%d %M:",
+ 'A' + get_myid(),
+ sc->nasid / 100,
+ (sc->nasid / 10) % 10,
+ sc->nasid / 10,
+ sc->modid );
+#else
+ sprintf( prefix,
+ "%c %d%d%d 0x%x:",
+ 'A' + get_myid(),
+ sc->nasid / 100,
+ (sc->nasid / 10) % 10,
+ sc->nasid / 10,
+ sc->modid );
+#endif
+
+ for( s = prefix; *s; s++ )
+ cq_add( q, *s );
+ }
+ sc->sol = 0;
+
+ }
+
+ if( cq_room( q ) < 2 && _elscuart_flush(sc) < 0 )
+ {
+ return -1;
+ }
+
+ if( c == '\n' ) {
+ cq_add( q, '\r' );
+ sc->sol = 1;
+ }
+
+ cq_add( q, (u_char) c );
+
+ if( c == '\n' ) {
+ /* flush buffered line */
+ if( _elscuart_flush( sc ) < 0 )
+ {
+ return -1;
+ }
+ }
+
+ if( c== '\r' )
+ {
+ sc->sol = 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * _elscuart_getc reads a character from the input queue. This
+ * routine blocks.
+ */
+int
+_elscuart_getc( l1sc_t *sc )
+{
+ int r;
+
+ while( (r = _elscuart_poll( sc )) == 0 );
+
+ if( r < 0 ) {
+ /* some error occured */
+ return r;
+ }
+
+ return _elscuart_readc( sc );
+}
+
+
+
+/*
+ * _elscuart_poll returns 1 if characters are ready for the
+ * calling processor, 0 if they are not
+ */
+int
+_elscuart_poll( l1sc_t *sc )
+{
+ int result;
+
+ if( sc->cons_listen ) {
+ result = l1_cons_poll( sc );
+ if( result )
+ return result;
+ }
+
+ return sc_poll( sc, L1_ELSCUART_SUBCH(get_myid()) );
+}
+
+
+
+/* _elscuart_readc is to be used only when _elscuart_poll has
+ * indicated that a character is waiting. Pulls a character
+ * of this processor's console queue and returns it.
+ *
+ */
+int
+_elscuart_readc( l1sc_t *sc )
+{
+ int c, pl;
+ sc_cq_t *q;
+ brl1_sch_t *subch;
+
+ if( sc->cons_listen ) {
+ subch = &(sc->subch[ SC_CONS_SYSTEM ]);
+ q = subch->iqp;
+
+ SUBCH_DATA_LOCK( subch, pl );
+ if( !cq_empty( q ) ) {
+ cq_rem( q, c );
+ if( cq_empty( q ) ) {
+ subch->packet_arrived = 0;
+ }
+ SUBCH_DATA_UNLOCK( subch, pl );
+ return c;
+ }
+ SUBCH_DATA_UNLOCK( subch, pl );
+ }
+
+ subch = &(sc->subch[ L1_ELSCUART_SUBCH(get_myid()) ]);
+ q = subch->iqp;
+
+ SUBCH_DATA_LOCK( subch, pl );
+ if( cq_empty( q ) ) {
+ SUBCH_DATA_UNLOCK( subch, pl );
+ return -1;
+ }
+
+ cq_rem( q, c );
+ if( cq_empty ( q ) ) {
+ subch->packet_arrived = 0;
+ }
+ SUBCH_DATA_UNLOCK( subch, pl );
+
+ return c;
+}
+
+
+/*
+ * _elscuart_flush flushes queued output to the the L1.
+ * This routine blocks until the queue is flushed.
+ */
+int
+_elscuart_flush( l1sc_t *sc )
+{
+ int r, n;
+ char buf[BRL1_QSIZE];
+ sc_cq_t *q = &(sc->oq[ MAP_OQ(L1_ELSCUART_SUBCH(get_myid())) ]);
+
+ while( (n = cq_used(q)) ) {
+
+ /* buffer queue contents */
+ r = BRL1_QSIZE - q->opos;
+
+ if( n > r ) {
+ BCOPY( q->buf + q->opos, buf, r );
+ BCOPY( q->buf, buf + r, n - r );
+ } else {
+ BCOPY( q->buf + q->opos, buf, n );
+ }
+
+ /* attempt to send buffer contents */
+ r = brl1_send( sc, buf, cq_used( q ),
+ (BRL1_EVENT | L1_ELSCUART_SUBCH(get_myid())), 1 );
+
+ /* if no error, dequeue the sent characters; otherwise,
+ * return the error
+ */
+ if( r >= SC_SUCCESS ) {
+ q->opos = (q->opos + r) % BRL1_QSIZE;
+ }
+ else {
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+
+
+/* _elscuart_probe returns non-zero if the L1 (and
+ * consequently the elscuart) can be accessed
+ */
+int
+_elscuart_probe( l1sc_t *sc )
+{
+#ifndef CONFIG_SERIAL_SGI_L1_PROTOCOL
+ return 0;
+#else
+ char ver[BRL1_QSIZE];
+ extern int elsc_version( l1sc_t *, char * );
+ if ( IS_RUNNING_ON_SIMULATOR() )
+ return 0;
+ return( elsc_version(sc, ver) >= 0 );
+#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
+}
+
+
+
+/* _elscuart_init zeroes out the l1sc_t console
+ * queues for this processor's console subchannel.
+ */
+void
+_elscuart_init( l1sc_t *sc )
+{
+ int pl;
+ brl1_sch_t *subch = &sc->subch[L1_ELSCUART_SUBCH(get_myid())];
+
+ SUBCH_DATA_LOCK(subch, pl);
+
+ subch->packet_arrived = 0;
+ cq_init( subch->iqp );
+ cq_init( &sc->oq[MAP_OQ(L1_ELSCUART_SUBCH(get_myid()))] );
+
+ SUBCH_DATA_UNLOCK(subch, pl);
+}
+
+
+#ifdef IRIX
+
+/* elscuart_syscon_listen causes the processor on which it's
+ * invoked to "listen" to the system console subchannel (that
+ * is, subchannel 4) for console input.
+ */
+void
+elscuart_syscon_listen( l1sc_t *sc )
+{
+ int pl;
+ brl1_sch_t *subch = &(sc->subch[SC_CONS_SYSTEM]);
+
+ /* if we're already listening, don't bother */
+ if( sc->cons_listen )
+ return;
+
+ SUBCH_DATA_LOCK( subch, pl );
+
+ subch->use = BRL1_SUBCH_RSVD;
+ subch->packet_arrived = 0;
+
+ SUBCH_DATA_UNLOCK( subch, pl );
+
+
+ sc->cons_listen = 1;
+}
+#endif /* IRIX */
diff --git a/arch/ia64/sn/io/l1_command.c b/arch/ia64/sn/io/l1_command.c
new file mode 100644
index 000000000..1cf6a3c87
--- /dev/null
+++ b/arch/ia64/sn/io/l1_command.c
@@ -0,0 +1,1356 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/hcl_util.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/eeprom.h>
+#include <asm/sn/ksys/i2c.h>
+#include <asm/sn/cmn_err.h>
+#include <asm/sn/router.h>
+#include <asm/sn/module.h>
+#include <asm/sn/ksys/l1.h>
+#include <asm/sn/nodepda.h>
+#include <asm/sn/clksupport.h>
+
+#define ELSC_TIMEOUT 1000000 /* ELSC response timeout (usec) */
+#define LOCK_TIMEOUT 5000000 /* Hub lock timeout (usec) */
+
+#define LOCAL_HUB LOCAL_HUB_ADDR
+#define LD(x) (*(volatile uint64_t *)(x))
+#define SD(x, v) (LD(x) = (uint64_t) (v))
+
+#define hub_cpu_get() 0
+
+#define LBYTE(caddr) (*(char *) caddr)
+
+extern char *bcopy(const char * src, char * dest, int count);
+
+#define LDEBUG 0
+
+/*
+ * ELSC data is in NVRAM page 7 at the following offsets.
+ */
+
+#define NVRAM_MAGIC_AD 0x700 /* magic number used for init */
+#define NVRAM_PASS_WD 0x701 /* password (4 bytes in length) */
+#define NVRAM_DBG1 0x705 /* virtual XOR debug switches */
+#define NVRAM_DBG2 0x706 /* physical XOR debug switches */
+#define NVRAM_CFG 0x707 /* ELSC Configuration info */
+#define NVRAM_MODULE 0x708 /* system module number */
+#define NVRAM_BIST_FLG 0x709 /* BIST flags (2 bits per nodeboard) */
+#define NVRAM_PARTITION 0x70a /* module's partition id */
+#define NVRAM_DOMAIN 0x70b /* module's domain id */
+#define NVRAM_CLUSTER 0x70c /* module's cluster id */
+#define NVRAM_CELL 0x70d /* module's cellid */
+
+#define NVRAM_MAGIC_NO 0x37 /* value of magic number */
+#define NVRAM_SIZE 16 /* 16 bytes in nvram */
+
+/*
+ * Declare a static ELSC NVRAM buffer to hold all data read from
+ * and written to NVRAM. This nvram "cache" will be used only during the
+ * IP27prom execution.
+ */
+static char elsc_nvram_buffer[NVRAM_SIZE];
+
+#define SC_COMMAND sc_command
+
+
+/*
+ * elsc_init
+ *
+ * Initialize ELSC structure
+ */
+
+void elsc_init(elsc_t *e, nasid_t nasid)
+{
+ sc_init((l1sc_t *)e, nasid, BRL1_LOCALUART);
+}
+
+
+/*
+ * elsc_errmsg
+ *
+ * Given a negative error code,
+ * returns a corresponding static error string.
+ */
+
+char *elsc_errmsg(int code)
+{
+ switch (code) {
+ case ELSC_ERROR_CMD_SEND:
+ return "Command send error";
+ case ELSC_ERROR_CMD_CHECKSUM:
+ return "Command packet checksum error";
+ case ELSC_ERROR_CMD_UNKNOWN:
+ return "Unknown command";
+ case ELSC_ERROR_CMD_ARGS:
+ return "Invalid command argument(s)";
+ case ELSC_ERROR_CMD_PERM:
+ return "Permission denied";
+ case ELSC_ERROR_RESP_TIMEOUT:
+ return "System controller response timeout";
+ case ELSC_ERROR_RESP_CHECKSUM:
+ return "Response packet checksum error";
+ case ELSC_ERROR_RESP_FORMAT:
+ return "Response format error";
+ case ELSC_ERROR_RESP_DIR:
+ return "Response direction error";
+ case ELSC_ERROR_MSG_LOST:
+ return "Message lost because queue is full";
+ case ELSC_ERROR_LOCK_TIMEOUT:
+ return "Timed out getting ELSC lock";
+ case ELSC_ERROR_DATA_SEND:
+ return "Error sending data";
+ case ELSC_ERROR_NIC:
+ return "NIC protocol error";
+ case ELSC_ERROR_NVMAGIC:
+ return "Bad magic number in NVRAM";
+ case ELSC_ERROR_MODULE:
+ return "Module location protocol error";
+ default:
+ return "Unknown error";
+ }
+}
+
+/*
+ * elsc_nvram_init
+ *
+ * Initializes reads and writes to NVRAM. This will perform a single
+ * read to NVRAM, getting all data at once. When the PROM tries to
+ * read NVRAM, it returns the data from the buffer being read. If the
+ * PROM tries to write out to NVRAM, the write is done, and the internal
+ * buffer is updated.
+ */
+
+void elsc_nvram_init(nasid_t nasid, uchar_t *elsc_nvram_data)
+{
+ /* This might require implementation of multiple-packet request/responses
+ * if it's to provide the same behavior that was available in SN0.
+ */
+ nasid = nasid;
+ elsc_nvram_data = elsc_nvram_data;
+}
+
+/*
+ * elsc_nvram_copy
+ *
+ * Copies the content of a buffer into the static buffer in this library.
+ */
+
+void elsc_nvram_copy(uchar_t *elsc_nvram_data)
+{
+ memcpy(elsc_nvram_buffer, elsc_nvram_data, NVRAM_SIZE);
+}
+
+/*
+ * elsc_nvram_write
+ *
+ * Copies bytes from 'buf' into NVRAM, starting at NVRAM address
+ * 'addr' which must be between 0 and 2047.
+ *
+ * If 'len' is non-negative, the routine copies 'len' bytes.
+ *
+ * If 'len' is negative, the routine treats the data as a string and
+ * copies bytes up to and including a NUL-terminating zero, but not
+ * to exceed '-len' bytes.
+ */
+
+int elsc_nvram_write(elsc_t *e, int addr, char *buf, int len)
+{
+ /* Here again, we might need to work out the details of a
+ * multiple-packet protocol.
+ */
+
+ /* For now, pretend it worked. */
+ e = e;
+ addr = addr;
+ buf = buf;
+ return (len < 0 ? -len : len);
+}
+
+/*
+ * elsc_nvram_read
+ *
+ * Copies bytes from NVRAM into 'buf', starting at NVRAM address
+ * 'addr' which must be between 0 and 2047.
+ *
+ * If 'len' is non-negative, the routine copies 'len' bytes.
+ *
+ * If 'len' is negative, the routine treats the data as a string and
+ * copies bytes up to and including a NUL-terminating zero, but not
+ * to exceed '-len' bytes. NOTE: This method is no longer supported.
+ * It was never used in the first place.
+ */
+
+int elsc_nvram_read(elsc_t *e, int addr, char *buf, int len)
+{
+ /* multiple packets? */
+ e = e;
+ addr = addr;
+ buf = buf;
+ len = len;
+ return -1;
+}
+
+/*
+ * Command Set
+ */
+
+int elsc_version(elsc_t *e, char *result)
+{
+ char msg[BRL1_QSIZE];
+ int len; /* length of message being sent */
+ int subch; /* system controller subchannel used */
+ int major, /* major rev number */
+ minor, /* minor rev number */
+ bugfix; /* bugfix rev number */
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ subch = sc_open( (l1sc_t *)e, L1_ADDR_LOCAL );
+
+ if( (len = sc_construct_msg( (l1sc_t *)e, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_FW_REV, 0 )) < 0 )
+ {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( SC_COMMAND( (l1sc_t *)e, subch, msg, msg, &len ) < 0 )
+ {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( (l1sc_t *)e, subch );
+
+ /* check response */
+ if( sc_interpret_resp( msg, 6, L1_ARG_INT, &major,
+ L1_ARG_INT, &minor, L1_ARG_INT, &bugfix )
+ < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ sprintf( result, "%d.%d.%d", major, minor, bugfix );
+
+ return 0;
+}
+
+int elsc_debug_set(elsc_t *e, u_char byte1, u_char byte2)
+{
+ /* shush compiler */
+ e = e;
+ byte1 = byte1;
+ byte2 = byte2;
+
+ /* fill in a buffer with the opcode & params; call sc_command */
+
+ return 0;
+}
+
+int elsc_debug_get(elsc_t *e, u_char *byte1, u_char *byte2)
+{
+ char msg[BRL1_QSIZE];
+ int subch; /* system controller subchannel used */
+ int dbg_sw; /* holds debug switch settings */
+ int len; /* number of msg buffer bytes used */
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ if( (subch = sc_open( (l1sc_t *)e, L1_ADDR_LOCAL )) < 0 ) {
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ if( (len = sc_construct_msg( (l1sc_t *)e, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_RDBG, 0 ) ) < 0 )
+ {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( sc_command( (l1sc_t *)e, subch, msg, msg, &len ) < 0 )
+ {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( (l1sc_t *)e, subch );
+
+ /* check response */
+ if( sc_interpret_resp( msg, 2, L1_ARG_INT, &dbg_sw ) < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ /* copy out debug switch settings (last two bytes of the
+ * integer response)
+ */
+ *byte1 = ((dbg_sw >> 8) & 0xFF);
+ *byte2 = (dbg_sw & 0xFF);
+
+ return 0;
+}
+
+/*
+ * elsc_rack_bay_get fills in the two int * arguments with the
+ * rack number and bay number of the L1 being addressed
+ */
+int elsc_rack_bay_get(elsc_t *e, uint *rack, uint *bay)
+{
+ char msg[BRL1_QSIZE]; /* L1 request/response info */
+ int subch; /* system controller subchannel used */
+ int len; /* length of message */
+ uint32_t buf32; /* used to copy 32-bit rack/bay out of msg */
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ if( (subch = sc_open( (l1sc_t *)e, L1_ADDR_LOCAL )) < 0 ) {
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ if( (len = sc_construct_msg( (l1sc_t *)e, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_RRACK, 0 )) < 0 )
+ {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+
+ /* send the request to the L1 */
+ if( sc_command( (l1sc_t *)e, subch, msg, msg, &len ) ) {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close(e, subch);
+
+ /* check response */
+ if( sc_interpret_resp( msg, 2, L1_ARG_INT, &buf32 ) < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ /* extract rack/bay info
+ *
+ * note that the 32-bit value returned by the L1 actually
+ * only uses the low-order sixteen bits for rack and bay
+ * information. A "normal" L1 address puts rack and bay
+ * information in bit positions 12 through 28. So if
+ * we initially shift the value returned 12 bits to the left,
+ * we can use the L1 addressing #define's to extract the
+ * values we need (see ksys/l1.h for a complete list of the
+ * various fields of an L1 address).
+ */
+ buf32 <<= L1_ADDR_BAY_SHFT;
+
+ *rack = (buf32 & L1_ADDR_RACK_MASK) >> L1_ADDR_RACK_SHFT;
+ *bay = (buf32 & L1_ADDR_BAY_MASK) >> L1_ADDR_BAY_SHFT;
+
+ return 0;
+}
+
+
+/* elsc_rack_bay_type_get fills in the three int * arguments with the
+ * rack number, bay number and brick type of the L1 being addressed. Note
+ * that if the L1 operation fails and this function returns an error value,
+ * garbage may be written to brick_type.
+ */
+int elsc_rack_bay_type_get( l1sc_t *sc, uint *rack,
+ uint *bay, uint *brick_type )
+{
+ char msg[BRL1_QSIZE]; /* L1 request/response info */
+ int subch; /* system controller subchannel used */
+ int len; /* length of message */
+ uint32_t buf32; /* used to copy 32-bit rack & bay out of msg */
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ if( (subch = sc_open( sc, L1_ADDR_LOCAL )) < 0 ) {
+ return ELSC_ERROR_CMD_SEND;
+ }
+
+ if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_RRBT, 0 )) < 0 )
+ {
+ sc_close( sc, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( SC_COMMAND( sc, subch, msg, msg, &len ) ) {
+ sc_close( sc, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( sc, subch );
+
+ /* check response */
+ if( sc_interpret_resp( msg, 4, L1_ARG_INT, &buf32,
+ L1_ARG_INT, brick_type ) < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ /* extract rack/bay info
+ *
+ * note that the 32-bit value returned by the L1 actually
+ * only uses the low-order sixteen bits for rack and bay
+ * information. A "normal" L1 address puts rack and bay
+ * information in bit positions 12 through 28. So if
+ * we initially shift the value returned 12 bits to the left,
+ * we can use the L1 addressing #define's to extract the
+ * values we need (see ksys/l1.h for a complete list of the
+ * various fields of an L1 address).
+ */
+ buf32 <<= L1_ADDR_BAY_SHFT;
+
+ *rack = (buf32 & L1_ADDR_RACK_MASK) >> L1_ADDR_RACK_SHFT;
+ *bay = (buf32 & L1_ADDR_BAY_MASK) >> L1_ADDR_BAY_SHFT;
+
+ /* convert brick_type to lower case */
+ *brick_type = *brick_type - 'A' + 'a';
+
+ return 0;
+}
+
+
+int elsc_module_get(elsc_t *e)
+{
+ extern char brick_types[];
+ uint rnum, rack, bay, bricktype, t;
+ int ret;
+
+ /* construct module ID from rack and slot info */
+
+ if ((ret = elsc_rack_bay_type_get(e, &rnum, &bay, &bricktype)) < 0)
+ return ret;
+
+ /* report unset location info. with a special, otherwise invalid modid */
+ if (rnum == 0 && bay == 0)
+ return MODULE_NOT_SET;
+
+ if (bay > MODULE_BPOS_MASK >> MODULE_BPOS_SHFT)
+ return ELSC_ERROR_MODULE;
+
+ /* Build a moduleid_t-compatible rack number */
+
+ rack = 0;
+ t = rnum / 100; /* rack class (CPU/IO) */
+ if (t > RACK_CLASS_MASK(rack) >> RACK_CLASS_SHFT(rack))
+ return ELSC_ERROR_MODULE;
+ RACK_ADD_CLASS(rack, t);
+ rnum %= 100;
+
+ t = rnum / 10; /* rack group */
+ if (t > RACK_GROUP_MASK(rack) >> RACK_GROUP_SHFT(rack))
+ return ELSC_ERROR_MODULE;
+ RACK_ADD_GROUP(rack, t);
+
+ t = rnum % 10; /* rack number (one-based) */
+ if (t-1 > RACK_NUM_MASK(rack) >> RACK_NUM_SHFT(rack))
+ return ELSC_ERROR_MODULE;
+ RACK_ADD_NUM(rack, t);
+
+ for( t = 0; t < MAX_BRICK_TYPES; t++ ) {
+ if( brick_types[t] == bricktype )
+ return RBT_TO_MODULE(rack, bay, t);
+ }
+
+ return ELSC_ERROR_MODULE;
+}
+
+int elsc_partition_set(elsc_t *e, int partition)
+{
+ char msg[BRL1_QSIZE]; /* L1 request/response info */
+ int subch; /* system controller subchannel used */
+ int len; /* length of message */
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ if( (subch = sc_open( e, L1_ADDR_LOCAL )) < 0 ) {
+ return ELSC_ERROR_CMD_SEND;
+ }
+
+ if( (len = sc_construct_msg( e, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_PARTITION_SET, 2,
+ L1_ARG_INT, partition )) < 0 )
+ {
+
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( sc_command( e, subch, msg, msg, &len ) ) {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( e, subch );
+
+ /* check response */
+ if( sc_interpret_resp( msg, 0 ) < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ return( 0 );
+}
+
+int elsc_partition_get(elsc_t *e)
+{
+ char msg[BRL1_QSIZE]; /* L1 request/response info */
+ int subch; /* system controller subchannel used */
+ int len; /* length of message */
+ uint32_t partition_id; /* used to copy partition id out of msg */
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ if( (subch = sc_open( e, L1_ADDR_LOCAL )) < 0 ) {
+ return ELSC_ERROR_CMD_SEND;
+ }
+
+ if( (len = sc_construct_msg( e, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_PARTITION_GET, 0 )) < 0 )
+
+ {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( sc_command( e, subch, msg, msg, &len ) ) {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( e, subch );
+
+ /* check response */
+ if( sc_interpret_resp( msg, 2, L1_ARG_INT, &partition_id ) < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ return( partition_id );
+}
+
+
+/*
+ * elsc_cons_subch selects the "active" console subchannel for this node
+ * (i.e., the one that will currently receive input)
+ */
+int elsc_cons_subch(elsc_t *e, uint ch)
+{
+ char msg[BRL1_QSIZE]; /* L1 request/response info */
+ int subch; /* system controller subchannel used */
+ int len; /* length of message */
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ subch = sc_open( e, L1_ADDR_LOCAL );
+
+ if( (len = sc_construct_msg( e, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_CONS_SUBCH, 2,
+ L1_ARG_INT, ch)) < 0 )
+ {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( SC_COMMAND( e, subch, msg, msg, &len ) ) {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( e, subch );
+
+ /* check response */
+ if( sc_interpret_resp( msg, 0 ) < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ return 0;
+}
+
+
+/*
+ * elsc_cons_node should only be executed by one node. It declares to
+ * the system controller that the node from which it is called will be
+ * the owner of the system console.
+ */
+int elsc_cons_node(elsc_t *e)
+{
+ char msg[BRL1_QSIZE]; /* L1 request/response info */
+ int subch; /* system controller subchannel used */
+ int len; /* length of message */
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ subch = sc_open( e, L1_ADDR_LOCAL );
+
+ if( (len = sc_construct_msg( e, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_CONS_NODE, 0 )) < 0 )
+ {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( SC_COMMAND( e, subch, msg, msg, &len ) ) {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( e, subch );
+
+ /* check response */
+ if( sc_interpret_resp( msg, 0 ) < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ return 0;
+}
+
+
+/* elsc_display_line writes up to 12 characters to either the top or bottom
+ * line of the L1 display. line points to a buffer containing the message
+ * to be displayed. The zero-based line number is specified by lnum (so
+ * lnum == 0 specifies the top line and lnum == 1 specifies the bottom).
+ * Lines longer than 12 characters, or line numbers not less than
+ * L1_DISPLAY_LINES, cause elsc_display_line to return an error.
+ */
+int elsc_display_line(elsc_t *e, char *line, int lnum)
+{
+ char msg[BRL1_QSIZE];
+ int subch; /* system controller subchannel used */
+ int len; /* number of msg buffer bytes used */
+
+ /* argument sanity checking */
+ if( !(lnum < L1_DISPLAY_LINES) )
+ return( ELSC_ERROR_CMD_ARGS );
+ if( !(strlen( line ) <= L1_DISPLAY_LINE_LENGTH) )
+ return( ELSC_ERROR_CMD_ARGS );
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ subch = sc_open( (l1sc_t *)e, L1_ADDR_LOCAL );
+
+ if( (len = sc_construct_msg( (l1sc_t *)e, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ (L1_REQ_DISP1+lnum), 2,
+ L1_ARG_ASCII, line )) < 0 )
+ {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( SC_COMMAND( (l1sc_t *)e, subch, msg, msg, &len ) < 0 )
+ {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( (l1sc_t *)e, subch );
+
+ /* check response */
+ if( sc_interpret_resp( msg, 0 ) < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ return 0;
+}
+
+
+/* elsc_display_mesg silently drops message characters beyond the 12th.
+ */
+int elsc_display_mesg(elsc_t *e, char *chr)
+{
+
+ char line[L1_DISPLAY_LINE_LENGTH+1];
+ int numlines, i;
+ int result;
+
+ numlines = (strlen( chr ) + L1_DISPLAY_LINE_LENGTH - 1) /
+ L1_DISPLAY_LINE_LENGTH;
+
+ if( numlines > L1_DISPLAY_LINES )
+ numlines = L1_DISPLAY_LINES;
+
+ for( i = 0; i < numlines; i++ )
+ {
+ strncpy( line, chr, L1_DISPLAY_LINE_LENGTH );
+ line[L1_DISPLAY_LINE_LENGTH] = '\0';
+
+ /* generally we want to leave the first line of the L1 display
+ * alone (so the L1 can manipulate it). If you need to be able
+ * to display to both lines (for debugging purposes), define
+ * L1_DISP_2LINES in irix/kern/ksys/l1.h, or add -DL1_DISP_2LINES
+ * to your 'defs file.
+ */
+#if defined(L1_DISP_2LINES)
+ if( (result = elsc_display_line( e, line, i )) < 0 )
+#else
+ if( (result = elsc_display_line( e, line, i+1 )) < 0 )
+#endif
+
+ return result;
+
+ chr += L1_DISPLAY_LINE_LENGTH;
+ }
+
+ return 0;
+}
+
+
+int elsc_password_set(elsc_t *e, char *password)
+{
+ /* shush compiler */
+ e = e;
+ password = password;
+
+ /* fill in buffer with the opcode & params; call elsc_command */
+
+ return 0;
+}
+
+int elsc_password_get(elsc_t *e, char *password)
+{
+ /* shush compiler */
+ e = e;
+ password = password;
+
+ /* fill in buffer with the opcode & params; call elsc_command */
+
+ return 0;
+}
+
+
+/*
+ * sc_portspeed_get
+ *
+ * retrieve the current portspeed setting for the bedrock II
+ */
+int sc_portspeed_get(l1sc_t *sc)
+{
+ char msg[BRL1_QSIZE];
+ int len; /* length of message being sent */
+ int subch; /* system controller subchannel used */
+ int portspeed_a, portspeed_b;
+ /* ioport clock rates */
+
+ bzero( msg, BRL1_QSIZE );
+ subch = sc_open( sc, L1_ADDR_LOCAL );
+
+ if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_PORTSPEED,
+ 0 )) < 0 )
+ {
+ sc_close( sc, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( sc_command( sc, subch, msg, msg, &len ) < 0 )
+ {
+ sc_close( sc, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( sc, subch );
+
+ /* check response */
+ if( sc_interpret_resp( msg, 4,
+ L1_ARG_INT, &portspeed_a,
+ L1_ARG_INT, &portspeed_b ) < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ /* for the c-brick, we ignore the portspeed_b value */
+ return (portspeed_a ? 600 : 400);
+}
+
+/*
+ * elsc_power_query
+ *
+ * To be used after system reset, this command returns 1 if the reset
+ * was the result of a power-on, 0 otherwise.
+ *
+ * The power query status is cleared to 0 after it is read.
+ */
+
+int elsc_power_query(elsc_t *e)
+{
+ e = e; /* shush the compiler */
+
+ /* fill in buffer with the opcode & params; call elsc_command */
+
+ return 1;
+}
+
+int elsc_rpwr_query(elsc_t *e, int is_master)
+{
+ /* shush the compiler */
+ e = e;
+ is_master = is_master;
+
+ /* fill in buffer with the opcode & params; call elsc_command */
+
+ return 0;
+}
+
+/*
+ * elsc_power_down
+ *
+ * Sets up system to shut down in "sec" seconds (or modifies the
+ * shutdown time if one is already in effect). Use 0 to power
+ * down immediately.
+ */
+
+int elsc_power_down(elsc_t *e, int sec)
+{
+ /* shush compiler */
+ e = e;
+ sec = sec;
+
+ /* fill in buffer with the opcode & params; call elsc_command */
+
+ return 0;
+}
+
+
+int elsc_system_reset(elsc_t *e)
+{
+ char msg[BRL1_QSIZE];
+ int subch; /* system controller subchannel used */
+ int len; /* number of msg buffer bytes used */
+ int result;
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ if( (subch = sc_open( e, L1_ADDR_LOCAL )) < 0 ) {
+ return ELSC_ERROR_CMD_SEND;
+ }
+
+ if( (len = sc_construct_msg( e, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_RESET, 0 )) < 0 )
+ {
+ sc_close( e, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( (result = sc_command( e, subch, msg, msg, &len )) ) {
+ sc_close( e, subch );
+ if( result == SC_NMSG ) {
+ /* timeout is OK. We've sent the reset. Now it's just
+ * a matter of time...
+ */
+ return( 0 );
+ }
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( e, subch );
+
+ /* check response */
+ if( sc_interpret_resp( msg, 0 ) < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ return 0;
+}
+
+
+int elsc_power_cycle(elsc_t *e)
+{
+ /* shush compiler */
+ e = e;
+
+ /* fill in buffer with the opcode & params; call sc_command */
+
+ return 0;
+}
+
+
+/*
+ * L1 Support for reading
+ * cbrick uid.
+ */
+
+int elsc_nic_get(elsc_t *e, uint64_t *nic, int verbose)
+{
+ /* this parameter included only for SN0 compatibility */
+ verbose = verbose;
+
+ /* We don't go straight to the bedrock/L1 protocol on this one, but let
+ * the eeprom layer prepare the eeprom data as we would like it to
+ * appear to the caller
+ */
+ return cbrick_uid_get( e->nasid, nic );
+}
+
+int _elsc_hbt(elsc_t *e, int ival, int rdly)
+{
+ e = e;
+ ival = ival;
+ rdly = rdly;
+
+ /* fill in buffer with the opcode & params; call elsc_command */
+
+ return 0;
+}
+
+
+/* send a command string to an L1 */
+int sc_command_interp( l1sc_t *sc, l1addr_t compt, l1addr_t rack, l1addr_t bay,
+ char *cmd )
+{
+ char msg[BRL1_QSIZE];
+ int len; /* length of message being sent */
+ int subch; /* system controller subchannel used */
+ l1addr_t target; /* target system controller for command */
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ subch = sc_open( sc, L1_ADDR_LOCAL );
+
+ L1_BUILD_ADDR( &target, compt, rack, bay, L1_ADDR_TASK_CMD );
+ if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+ target, L1_REQ_EXEC_CMD, 2,
+ L1_ARG_ASCII, cmd )) < 0 )
+ {
+ sc_close( sc, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( sc_command( sc, subch, msg, msg, &len ) < 0 )
+ {
+ sc_close( sc, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( sc, subch );
+
+ /* check response */
+ if( sc_interpret_resp( msg, 0 ) < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ return 0;
+}
+
+
+/*
+ * Routines for reading the R-brick's L1
+ */
+
+int router_module_get( nasid_t nasid, net_vec_t path )
+{
+ uint rnum, rack, bay, t;
+ int ret;
+ l1sc_t sc;
+
+ /* prepare l1sc_t struct */
+ sc_init( &sc, nasid, path );
+
+ /* construct module ID from rack and slot info */
+
+ if ((ret = elsc_rack_bay_get(&sc, &rnum, &bay)) < 0)
+ return ret;
+
+ /* report unset location info. with a special, otherwise invalid modid */
+ if (rnum == 0 && bay == 0)
+ return MODULE_NOT_SET;
+
+ if (bay > MODULE_BPOS_MASK >> MODULE_BPOS_SHFT)
+ return ELSC_ERROR_MODULE;
+
+ /* Build a moduleid_t-compatible rack number */
+
+ rack = 0;
+ t = rnum / 100; /* rack class (CPU/IO) */
+ if (t > RACK_CLASS_MASK(rack) >> RACK_CLASS_SHFT(rack))
+ return ELSC_ERROR_MODULE;
+ RACK_ADD_CLASS(rack, t);
+ rnum %= 100;
+
+ t = rnum / 10; /* rack group */
+ if (t > RACK_GROUP_MASK(rack) >> RACK_GROUP_SHFT(rack))
+ return ELSC_ERROR_MODULE;
+ RACK_ADD_GROUP(rack, t);
+
+ t = rnum % 10; /* rack number (one-based) */
+ if (t-1 > RACK_NUM_MASK(rack) >> RACK_NUM_SHFT(rack))
+ return ELSC_ERROR_MODULE;
+ RACK_ADD_NUM(rack, t);
+
+ ret = RBT_TO_MODULE(rack, bay, MODULE_RBRICK);
+ return ret;
+}
+
+
+/*
+ * iobrick routines
+ */
+
+/* iobrick_rack_bay_type_get fills in the three int * arguments with the
+ * rack number, bay number and brick type of the L1 being addressed. Note
+ * that if the L1 operation fails and this function returns an error value,
+ * garbage may be written to brick_type.
+ */
+int iobrick_rack_bay_type_get( l1sc_t *sc, uint *rack,
+ uint *bay, uint *brick_type )
+{
+ char msg[BRL1_QSIZE]; /* L1 request/response info */
+ int subch; /* system controller subchannel used */
+ int len; /* length of message */
+ uint32_t buf32; /* used to copy 32-bit rack & bay out of msg */
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ if( (subch = sc_open( sc, L1_ADDR_LOCALIO )) < 0 ) {
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_RRBT, 0 )) < 0 )
+ {
+ sc_close( sc, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( sc_command( sc, subch, msg, msg, &len ) ) {
+ sc_close( sc, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( sc, subch );
+
+ /* check response */
+ if( sc_interpret_resp( msg, 4, L1_ARG_INT, &buf32,
+ L1_ARG_INT, brick_type ) < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ /* extract rack/bay info
+ *
+ * note that the 32-bit value returned by the L1 actually
+ * only uses the low-order sixteen bits for rack and bay
+ * information. A "normal" L1 address puts rack and bay
+ * information in bit positions 12 through 28. So if
+ * we initially shift the value returned 12 bits to the left,
+ * we can use the L1 addressing #define's to extract the
+ * values we need (see ksys/l1.h for a complete list of the
+ * various fields of an L1 address).
+ */
+ buf32 <<= L1_ADDR_BAY_SHFT;
+
+ *rack = (buf32 & L1_ADDR_RACK_MASK) >> L1_ADDR_RACK_SHFT;
+ *bay = (buf32 & L1_ADDR_BAY_MASK) >> L1_ADDR_BAY_SHFT;
+
+ return 0;
+}
+
+
+int iobrick_module_get(l1sc_t *sc)
+{
+ uint rnum, rack, bay, brick_type, t;
+ int ret;
+
+ /* construct module ID from rack and slot info */
+
+ if ((ret = iobrick_rack_bay_type_get(sc, &rnum, &bay, &brick_type)) < 0)
+ return ret;
+
+ /* report unset location info. with a special, otherwise invalid modid */
+ if (rnum == 0 && bay == 0)
+ return MODULE_NOT_SET;
+
+ if (bay > MODULE_BPOS_MASK >> MODULE_BPOS_SHFT)
+ return ELSC_ERROR_MODULE;
+
+ /* Build a moduleid_t-compatible rack number */
+
+ rack = 0;
+ t = rnum / 100; /* rack class (CPU/IO) */
+ if (t > RACK_CLASS_MASK(rack) >> RACK_CLASS_SHFT(rack))
+ return ELSC_ERROR_MODULE;
+ RACK_ADD_CLASS(rack, t);
+ rnum %= 100;
+
+ t = rnum / 10; /* rack group */
+ if (t > RACK_GROUP_MASK(rack) >> RACK_GROUP_SHFT(rack))
+ return ELSC_ERROR_MODULE;
+ RACK_ADD_GROUP(rack, t);
+
+ t = rnum % 10; /* rack number (one-based) */
+ if (t-1 > RACK_NUM_MASK(rack) >> RACK_NUM_SHFT(rack))
+ return ELSC_ERROR_MODULE;
+ RACK_ADD_NUM(rack, t);
+
+ switch( brick_type ) {
+ case 'I':
+ brick_type = MODULE_IBRICK; break;
+ case 'P':
+ brick_type = MODULE_PBRICK; break;
+ case 'X':
+ brick_type = MODULE_XBRICK; break;
+ }
+
+ ret = RBT_TO_MODULE(rack, bay, brick_type);
+
+ return ret;
+}
+
+/* iobrick_get_sys_snum asks the attached iobrick for the system
+ * serial number. This function will only be relevant to the master
+ * cbrick (the one attached to the bootmaster ibrick); other nodes
+ * may call the function, but the value returned to the master node
+ * will be the one used as the system serial number by the kernel.
+ */
+
+int
+iobrick_get_sys_snum( l1sc_t *sc, char *snum_str )
+{
+ char msg[BRL1_QSIZE]; /* L1 request/response info */
+ int subch; /* system controller subchannel used */
+ int len; /* length of message */
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ if( (subch = sc_open( sc, L1_ADDR_LOCALIO )) < 0 ) {
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_SYS_SERIAL, 0 )) < 0 )
+ {
+ sc_close( sc, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( sc_command( sc, subch, msg, msg, &len ) ) {
+ sc_close( sc, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( sc, subch );
+
+ /* check response */
+ return( sc_interpret_resp( msg, 2, L1_ARG_ASCII, snum_str ) );
+}
+
+
+/*
+ * The following functions apply (or cut off) power to the specified
+ * pci bus or slot.
+ */
+
+int
+iobrick_pci_slot_pwr( l1sc_t *sc, int bus, int slot, int up )
+{
+ char cmd[BRL1_QSIZE];
+ unsigned rack, bay, brick_type;
+ if( iobrick_rack_bay_type_get( sc, &rack, &bay, &brick_type ) < 0 )
+ return( ELSC_ERROR_CMD_SEND );
+ sprintf( cmd, "pci %d %d %s", bus, slot,
+ (up ? "u" : "d") );
+ return( sc_command_interp
+ ( sc, L1_ADDR_TYPE_L1, rack, bay, cmd ) );
+}
+
+int
+iobrick_pci_bus_pwr( l1sc_t *sc, int bus, int up )
+{
+ char cmd[BRL1_QSIZE];
+ unsigned rack, bay, brick_type;
+ if( iobrick_rack_bay_type_get( sc, &rack, &bay, &brick_type ) < 0 )
+ return( ELSC_ERROR_CMD_SEND );
+ sprintf( cmd, "pci %d %s", bus, (up ? "u" : "d") );
+ return( sc_command_interp
+ ( sc, L1_ADDR_TYPE_L1, rack, bay, cmd ) );
+}
+
+
+/* get the L1 firmware version for an iobrick */
+int
+iobrick_sc_version( l1sc_t *sc, char *result )
+{
+ char msg[BRL1_QSIZE];
+ int len; /* length of message being sent */
+ int subch; /* system controller subchannel used */
+ int major, /* major rev number */
+ minor, /* minor rev number */
+ bugfix; /* bugfix rev number */
+
+ /* fill in msg with the opcode & params */
+ bzero( msg, BRL1_QSIZE );
+ subch = sc_open( sc, L1_ADDR_LOCALIO );
+
+ if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
+ L1_ADDR_TASK_GENERAL,
+ L1_REQ_FW_REV, 0 )) < 0 )
+ {
+ sc_close( sc, subch );
+ return( ELSC_ERROR_CMD_ARGS );
+ }
+
+ /* send the request to the L1 */
+ if( SC_COMMAND(sc, subch, msg, msg, &len ) < 0 )
+ {
+ sc_close( sc, subch );
+ return( ELSC_ERROR_CMD_SEND );
+ }
+
+ /* free up subchannel */
+ sc_close( sc, subch );
+
+ /* check response */
+ if( sc_interpret_resp( msg, 6, L1_ARG_INT, &major,
+ L1_ARG_INT, &minor, L1_ARG_INT, &bugfix )
+ < 0 )
+ {
+ return( ELSC_ERROR_RESP_FORMAT );
+ }
+
+ sprintf( result, "%d.%d.%d", major, minor, bugfix );
+
+ return 0;
+}
+
+
+
+/* elscuart routines
+ *
+ * Most of the elscuart functionality is implemented in l1.c. The following
+ * is directly "recycled" from elsc.c.
+ */
+
+
+/*
+ * _elscuart_puts
+ */
+
+int _elscuart_puts(elsc_t *e, char *s)
+{
+ int c;
+
+ if (s == 0)
+ s = "<NULL>";
+
+ while ((c = LBYTE(s)) != 0) {
+ if (_elscuart_putc(e, c) < 0)
+ return -1;
+ s++;
+ }
+
+ return 0;
+}
+
+
+/*
+ * elscuart wrapper routines
+ *
+ * The following routines are similar to their counterparts in l1.c,
+ * except instead of taking an elsc_t pointer directly, they call
+ * a global routine "get_elsc" to obtain the pointer.
+ * This is useful when the elsc is employed for stdio.
+ */
+
+int elscuart_probe(void)
+{
+ return _elscuart_probe(get_elsc());
+}
+
+void elscuart_init(void *init_data)
+{
+ _elscuart_init(get_elsc());
+ /* dummy variable included for driver compatability */
+ init_data = init_data;
+}
+
+int elscuart_poll(void)
+{
+ return _elscuart_poll(get_elsc());
+}
+
+int elscuart_readc(void)
+{
+ return _elscuart_readc(get_elsc());
+}
+
+int elscuart_getc(void)
+{
+ return _elscuart_getc(get_elsc());
+}
+
+int elscuart_puts(char *s)
+{
+ return _elscuart_puts(get_elsc(), s);
+}
+
+int elscuart_putc(int c)
+{
+ return _elscuart_putc(get_elsc(), c);
+}
+
+int elscuart_flush(void)
+{
+ return _elscuart_flush(get_elsc());
+}
diff --git a/arch/ia64/sn/io/labelcl.c b/arch/ia64/sn/io/labelcl.c
new file mode 100644
index 000000000..c8b714508
--- /dev/null
+++ b/arch/ia64/sn/io/labelcl.c
@@ -0,0 +1,665 @@
+/* labelcl - SGI's Hwgraph Compatibility Layer.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Colin Ngam may be reached by email at cngam@sgi.com
+
+*/
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <linux/devfs_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+
+/*
+** Very simple and dumb string table that supports only find/insert.
+** In practice, if this table gets too large, we may need a more
+** efficient data structure. Also note that currently there is no
+** way to delete an item once it's added. Therefore, name collision
+** will return an error.
+*/
+
+struct string_table label_string_table;
+
+
+
+/*
+ * string_table_init - Initialize the given string table.
+ */
+void
+string_table_init(struct string_table *string_table)
+{
+ string_table->string_table_head = NULL;
+ string_table->string_table_generation = 0;
+
+ /*
+ * We nedd to initialize locks here!
+ */
+
+ return;
+}
+
+
+/*
+ * string_table_destroy - Destroy the given string table.
+ */
+void
+string_table_destroy(struct string_table *string_table)
+{
+ struct string_table_item *item, *next_item;
+
+ item = string_table->string_table_head;
+ while (item) {
+ next_item = item->next;
+
+ STRTBL_FREE(item);
+ item = next_item;
+ }
+
+ /*
+ * We need to destroy whatever lock we have here
+ */
+
+ return;
+}
+
+
+
+/*
+ * string_table_insert - Insert an entry in the string table .. duplicate
+ * names are not allowed.
+ */
+char *
+string_table_insert(struct string_table *string_table, char *name)
+{
+ struct string_table_item *item, *new_item = NULL, *last_item = NULL;
+
+again:
+ /*
+ * Need to lock the table ..
+ */
+ item = string_table->string_table_head;
+ last_item = NULL;
+
+ while (item) {
+ if (!strcmp(item->string, name)) {
+ /*
+ * If we allocated space for the string and the found that
+ * someone else already entered it into the string table,
+ * free the space we just allocated.
+ */
+ if (new_item)
+ STRTBL_FREE(new_item);
+
+
+ /*
+ * Search optimization: move the found item to the head
+ * of the list.
+ */
+ if (last_item != NULL) {
+ last_item->next = item->next;
+ item->next = string_table->string_table_head;
+ string_table->string_table_head = item;
+ }
+ goto out;
+ }
+ last_item = item;
+ item=item->next;
+ }
+
+ /*
+ * name was not found, so add it to the string table.
+ */
+ if (new_item == NULL) {
+ long old_generation = string_table->string_table_generation;
+
+ new_item = STRTBL_ALLOC(strlen(name));
+
+ strcpy(new_item->string, name);
+
+ /*
+ * While we allocated memory for the new string, someone else
+ * changed the string table.
+ */
+ if (old_generation != string_table->string_table_generation) {
+ goto again;
+ }
+ } else {
+ /* At this we only have the string table lock in access mode.
+ * Promote the access lock to an update lock for the string
+ * table insertion below.
+ */
+ long old_generation =
+ string_table->string_table_generation;
+
+ /*
+ * After we did the unlock and wer waiting for update
+ * lock someone could have potentially updated
+ * the string table. Check the generation number
+ * for this case. If it is the case we have to
+ * try all over again.
+ */
+ if (old_generation !=
+ string_table->string_table_generation) {
+ goto again;
+ }
+ }
+
+ /*
+ * At this point, we're committed to adding new_item to the string table.
+ */
+ new_item->next = string_table->string_table_head;
+ item = string_table->string_table_head = new_item;
+ string_table->string_table_generation++;
+
+out:
+ /*
+ * Need to unlock here.
+ */
+ return(item->string);
+}
+
+/*
+ * labelcl_info_create - Creates the data structure that will hold the
+ * device private information asscoiated with a devfs entry.
+ * The pointer to this structure is what gets stored in the devfs
+ * (void * info).
+ */
+labelcl_info_t *
+labelcl_info_create()
+{
+
+ labelcl_info_t *new = NULL;
+
+ /* Initial allocation does not include any area for labels */
+ if ( ( new = (labelcl_info_t *)kmalloc (sizeof(labelcl_info_t), GFP_KERNEL) ) == NULL )
+ return NULL;
+
+ memset (new, 0, sizeof(labelcl_info_t));
+ new->hwcl_magic = LABELCL_MAGIC;
+ return( new);
+
+}
+
+/*
+ * labelcl_info_destroy - Frees the data structure that holds the
+ * device private information asscoiated with a devfs entry. This
+ * data structure was created by device_info_create().
+ *
+ * The caller is responsible for nulling the (void *info) in the
+ * corresponding devfs entry.
+ */
+int
+labelcl_info_destroy(labelcl_info_t *labelcl_info)
+{
+
+ if (labelcl_info == NULL)
+ return(0);
+
+ /* Free the label list */
+ if (labelcl_info->label_list)
+ kfree(labelcl_info->label_list);
+
+ /* Now free the label info area */
+ labelcl_info->hwcl_magic = 0;
+ kfree(labelcl_info);
+
+ return(0);
+}
+
+/*
+ * labelcl_info_add_LBL - Adds a new label entry in the labelcl info
+ * structure.
+ *
+ * Error is returned if we find another label with the same name.
+ */
+int
+labelcl_info_add_LBL(devfs_handle_t de,
+ char *info_name,
+ arb_info_desc_t info_desc,
+ arbitrary_info_t info)
+{
+ labelcl_info_t *labelcl_info = NULL;
+ int num_labels;
+ int new_label_list_size;
+ label_info_t *old_label_list, *new_label_list = NULL;
+ char *name;
+ int i;
+
+ if (de == NULL)
+ return(-1);
+
+ labelcl_info = devfs_get_info(de);
+ if (labelcl_info == NULL)
+ return(-1);
+
+ if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
+ return(-1);
+
+ if (info_name == NULL)
+ return(-1);
+
+ if (strlen(info_name) >= LABEL_LENGTH_MAX)
+ return(-1);
+
+ name = string_table_insert(&label_string_table, info_name);
+
+ num_labels = labelcl_info->num_labels;
+ new_label_list_size = sizeof(label_info_t) * (num_labels+1);
+
+ /*
+ * Create a new label info area.
+ */
+ if (new_label_list_size != 0) {
+ new_label_list = (label_info_t *) kmalloc(new_label_list_size, GFP_KERNEL);
+
+ if (new_label_list == NULL)
+ return(-1);
+ }
+
+ /*
+ * At this point, we are committed to adding the labelled info,
+ * if there isn't already information there with the same name.
+ */
+ old_label_list = labelcl_info->label_list;
+
+ /*
+ * Look for matching info name.
+ */
+ for (i=0; i<num_labels; i++) {
+ if (!strcmp(info_name, old_label_list[i].name)) {
+ /* Not allowed to add duplicate labelled info names. */
+ kfree(new_label_list);
+ printk("labelcl_info_add_LBL: Duplicate label name %s for vertex 0x%p\n", info_name, de);
+ return(-1);
+ }
+ new_label_list[i] = old_label_list[i]; /* structure copy */
+ }
+
+ new_label_list[num_labels].name = name;
+ new_label_list[num_labels].desc = info_desc;
+ new_label_list[num_labels].info = info;
+
+ labelcl_info->num_labels = num_labels+1;
+ labelcl_info->label_list = new_label_list;
+
+ if (old_label_list != NULL)
+ kfree(old_label_list);
+
+ return(0);
+}
+
+/*
+ * labelcl_info_remove_LBL - Remove a label entry.
+ */
+int
+labelcl_info_remove_LBL(devfs_handle_t de,
+ char *info_name,
+ arb_info_desc_t *info_desc,
+ arbitrary_info_t *info)
+{
+ labelcl_info_t *labelcl_info = NULL;
+ int num_labels;
+ int new_label_list_size;
+ label_info_t *old_label_list, *new_label_list = NULL;
+ arb_info_desc_t label_desc_found;
+ arbitrary_info_t label_info_found;
+ int i;
+
+ if (de == NULL)
+ return(-1);
+
+ labelcl_info = devfs_get_info(de);
+ if (labelcl_info == NULL)
+ return(-1);
+
+ if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
+ return(-1);
+
+ num_labels = labelcl_info->num_labels;
+ if (num_labels == 0) {
+ return(-1);
+ }
+
+ /*
+ * Create a new info area.
+ */
+ new_label_list_size = sizeof(label_info_t) * (num_labels-1);
+ if (new_label_list_size) {
+ new_label_list = (label_info_t *) kmalloc(new_label_list_size, GFP_KERNEL);
+ if (new_label_list == NULL)
+ return(-1);
+ }
+
+ /*
+ * At this point, we are committed to removing the labelled info,
+ * if it still exists.
+ */
+ old_label_list = labelcl_info->label_list;
+
+ /*
+ * Find matching info name.
+ */
+ for (i=0; i<num_labels; i++) {
+ if (!strcmp(info_name, old_label_list[i].name)) {
+ label_desc_found = old_label_list[i].desc;
+ label_info_found = old_label_list[i].info;
+ goto found;
+ }
+ if (i < num_labels-1) /* avoid walking off the end of the new vertex */
+ new_label_list[i] = old_label_list[i]; /* structure copy */
+ }
+
+ /* The named info doesn't exist. */
+ if (new_label_list)
+ kfree(new_label_list);
+
+ return(-1);
+
+found:
+ /* Finish up rest of labelled info */
+ for (i=i+1; i<num_labels; i++)
+ new_label_list[i-1] = old_label_list[i]; /* structure copy */
+
+ labelcl_info->num_labels = num_labels+1;
+ labelcl_info->label_list = new_label_list;
+
+ kfree(old_label_list);
+
+ if (info != NULL)
+ *info = label_info_found;
+
+ if (info_desc != NULL)
+ *info_desc = label_desc_found;
+
+ return(0);
+}
+
+
+/*
+ * labelcl_info_replace_LBL - Replace an existing label entry with the
+ * given new information.
+ *
+ * Label entry must exist.
+ */
+int
+labelcl_info_replace_LBL(devfs_handle_t de,
+ char *info_name,
+ arb_info_desc_t info_desc,
+ arbitrary_info_t info,
+ arb_info_desc_t *old_info_desc,
+ arbitrary_info_t *old_info)
+{
+ labelcl_info_t *labelcl_info = NULL;
+ int num_labels;
+ label_info_t *label_list;
+ int i;
+
+ if (de == NULL)
+ return(-1);
+
+ labelcl_info = devfs_get_info(de);
+ if (labelcl_info == NULL)
+ return(-1);
+
+ if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
+ return(-1);
+
+ num_labels = labelcl_info->num_labels;
+ if (num_labels == 0) {
+ return(-1);
+ }
+
+ if (info_name == NULL)
+ return(-1);
+
+ label_list = labelcl_info->label_list;
+
+ /*
+ * Verify that information under info_name already exists.
+ */
+ for (i=0; i<num_labels; i++)
+ if (!strcmp(info_name, label_list[i].name)) {
+ if (old_info != NULL)
+ *old_info = label_list[i].info;
+
+ if (old_info_desc != NULL)
+ *old_info_desc = label_list[i].desc;
+
+ label_list[i].info = info;
+ label_list[i].desc = info_desc;
+
+ return(0);
+ }
+
+
+ return(-1);
+}
+
+/*
+ * labelcl_info_get_LBL - Retrieve and return the information for the
+ * given label entry.
+ */
+int
+labelcl_info_get_LBL(devfs_handle_t de,
+ char *info_name,
+ arb_info_desc_t *info_desc,
+ arbitrary_info_t *info)
+{
+ labelcl_info_t *labelcl_info = NULL;
+ int num_labels;
+ label_info_t *label_list;
+ int i;
+
+ if (de == NULL)
+ return(-1);
+
+ labelcl_info = devfs_get_info(de);
+ if (labelcl_info == NULL)
+ return(-1);
+
+ if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
+ return(-1);
+
+ num_labels = labelcl_info->num_labels;
+ if (num_labels == 0) {
+ return(-1);
+ }
+
+ label_list = labelcl_info->label_list;
+
+ /*
+ * Find information under info_name.
+ */
+ for (i=0; i<num_labels; i++)
+ if (!strcmp(info_name, label_list[i].name)) {
+ if (info != NULL)
+ *info = label_list[i].info;
+ if (info_desc != NULL)
+ *info_desc = label_list[i].desc;
+
+ return(0);
+ }
+
+ return(-1);
+}
+
+/*
+ * labelcl_info_get_next_LBL - returns the next label entry on the list.
+ */
+int
+labelcl_info_get_next_LBL(devfs_handle_t de,
+ char *buffer,
+ arb_info_desc_t *info_descp,
+ arbitrary_info_t *infop,
+ labelcl_info_place_t *placeptr)
+{
+ labelcl_info_t *labelcl_info = NULL;
+ uint which_info;
+ label_info_t *label_list;
+
+ if ((buffer == NULL) && (infop == NULL))
+ return(-1);
+
+ if (placeptr == NULL)
+ return(-1);
+
+ if (de == NULL)
+ return(-1);
+
+ labelcl_info = devfs_get_info(de);
+ if (labelcl_info == NULL)
+ return(-1);
+
+ if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
+ return(-1);
+
+ which_info = *placeptr;
+
+ if (which_info >= labelcl_info->num_labels) {
+ return(-1);
+ }
+
+ label_list = (label_info_t *) labelcl_info->label_list;
+
+ if (buffer != NULL)
+ strcpy(buffer, label_list[which_info].name);
+
+ if (infop)
+ *infop = label_list[which_info].info;
+
+ if (info_descp)
+ *info_descp = label_list[which_info].desc;
+
+ *placeptr = which_info + 1;
+
+ return(0);
+}
+
+
+int
+labelcl_info_replace_IDX(devfs_handle_t de,
+ int index,
+ arbitrary_info_t info,
+ arbitrary_info_t *old_info)
+{
+ arbitrary_info_t *info_list_IDX;
+ labelcl_info_t *labelcl_info = NULL;
+
+ if (de == NULL) {
+ printk(KERN_ALERT "labelcl: NULL devfs handle given.\n");
+ return(-1);
+ }
+
+ labelcl_info = devfs_get_info(de);
+ if (labelcl_info == NULL) {
+ printk(KERN_ALERT "labelcl: Entry does not have info pointer.\n");
+ return(-1);
+ }
+
+ if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
+ return(-1);
+
+ if ( (index < 0) || (index >= HWGRAPH_NUM_INDEX_INFO) )
+ return(-1);
+
+ /*
+ * Replace information at the appropriate index in this vertex with
+ * the new info.
+ */
+ info_list_IDX = labelcl_info->IDX_list;
+ if (old_info != NULL)
+ *old_info = info_list_IDX[index];
+ info_list_IDX[index] = info;
+
+ return(0);
+
+}
+
+/*
+ * labelcl_info_connectpt_set - Sets the connectpt.
+ */
+int
+labelcl_info_connectpt_set(struct devfs_entry *de,
+ struct devfs_entry *connect_de)
+{
+ arbitrary_info_t old_info;
+ int rv;
+
+ rv = labelcl_info_replace_IDX(de, HWGRAPH_CONNECTPT,
+ (arbitrary_info_t) connect_de, &old_info);
+
+ if (rv) {
+ return(rv);
+ }
+
+ return(0);
+}
+
+
+/*
+ * labelcl_info_get_IDX - Returns the information pointed at by index.
+ *
+ */
+int
+labelcl_info_get_IDX(devfs_handle_t de,
+ int index,
+ arbitrary_info_t *info)
+{
+ arbitrary_info_t *info_list_IDX;
+ labelcl_info_t *labelcl_info = NULL;
+
+ if (de == NULL)
+ return(-1);
+
+ labelcl_info = devfs_get_info(de);
+ if (labelcl_info == NULL)
+ return(-1);
+
+ if (labelcl_info->hwcl_magic != LABELCL_MAGIC)
+ return(-1);
+
+ if ( (index < 0) || (index >= HWGRAPH_NUM_INDEX_INFO) )
+ return(-1);
+
+ /*
+ * Return information at the appropriate index in this vertex.
+ */
+ info_list_IDX = labelcl_info->IDX_list;
+ if (info != NULL)
+ *info = info_list_IDX[index];
+
+ return(0);
+}
+
+/*
+ * labelcl_info_connectpt_get - Retrieve the connect point for a device entry.
+ */
+struct devfs_entry *
+labelcl_info_connectpt_get(struct devfs_entry *de)
+{
+ int rv;
+ arbitrary_info_t info;
+
+ rv = labelcl_info_get_IDX(de, HWGRAPH_CONNECTPT, &info);
+ if (rv)
+ return(NULL);
+
+ return((struct devfs_entry *)info);
+}
diff --git a/arch/ia64/sn/io/mem_refcnt.c b/arch/ia64/sn/io/mem_refcnt.c
new file mode 100644
index 000000000..b2df4279b
--- /dev/null
+++ b/arch/ia64/sn/io/mem_refcnt.c
@@ -0,0 +1,233 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/hcl_util.h>
+#include <asm/sn/nodepda.h>
+#include <asm/sn/hubspc.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/mem_refcnt.h>
+#include <asm/sn/hwcntrs.h>
+// From numa_hw.h
+
+#define MIGR_COUNTER_MAX_GET(nodeid) \
+ (NODEPDA_MCD((nodeid))->migr_system_kparms.migr_threshold_reference)
+/*
+ * Get the Absolute Theshold
+ */
+#define MIGR_THRESHOLD_ABS_GET(nodeid) ( \
+ MD_MIG_VALUE_THRESH_GET(COMPACT_TO_NASID_NODEID(nodeid)))
+/*
+ * Get the current Differential Threshold
+ */
+#define MIGR_THRESHOLD_DIFF_GET(nodeid) \
+ (NODEPDA_MCD(nodeid)->migr_as_kparms.migr_base_threshold)
+
+#define NUM_OF_HW_PAGES_PER_SW_PAGE() (NBPP / MD_PAGE_SIZE)
+
+// #include "migr_control.h"
+
+int
+mem_refcnt_attach(devfs_handle_t hub)
+{
+ devfs_handle_t refcnt_dev;
+
+ hwgraph_char_device_add(hub,
+ "refcnt",
+ "hubspc_",
+ &refcnt_dev);
+ device_info_set(refcnt_dev, (void*)(ulong)HUBSPC_REFCOUNTERS);
+
+ return (0);
+}
+
+
+/*ARGSUSED*/
+int
+mem_refcnt_open(devfs_handle_t *devp, mode_t oflag, int otyp, cred_t *crp)
+{
+ cnodeid_t node;
+#ifndef CONFIG_IA64_SGI_SN1
+ extern int numnodes;
+#endif
+
+ ASSERT( (hubspc_subdevice_t)(ulong)device_info_get(*devp) == HUBSPC_REFCOUNTERS );
+
+ if (!cap_able(CAP_MEMORY_MGT)) {
+ return (EPERM);
+ }
+
+ node = master_node_get(*devp);
+
+ ASSERT( (node >= 0) && (node < numnodes) );
+
+ if (NODEPDA(node)->migr_refcnt_counterbuffer == NULL) {
+ return (ENODEV);
+ }
+
+ ASSERT( NODEPDA(node)->migr_refcnt_counterbase != NULL );
+ ASSERT( NODEPDA(node)->migr_refcnt_cbsize != (size_t)0 );
+
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+mem_refcnt_close(devfs_handle_t dev, int oflag, int otyp, cred_t *crp)
+{
+ return 0;
+}
+
+/*ARGSUSED*/
+int
+mem_refcnt_mmap(devfs_handle_t dev, vhandl_t *vt, off_t off, size_t len, uint prot)
+{
+ cnodeid_t node;
+ int errcode;
+ char* buffer;
+ size_t blen;
+#ifndef CONFIG_IA64_SGI_SN1
+ extern int numnodes;
+#endif
+
+ ASSERT( (hubspc_subdevice_t)(ulong)device_info_get(dev) == HUBSPC_REFCOUNTERS );
+
+ node = master_node_get(dev);
+
+ ASSERT( (node >= 0) && (node < numnodes) );
+
+ ASSERT( NODEPDA(node)->migr_refcnt_counterbuffer != NULL);
+ ASSERT( NODEPDA(node)->migr_refcnt_counterbase != NULL );
+ ASSERT( NODEPDA(node)->migr_refcnt_cbsize != 0 );
+
+ /*
+ * XXXX deal with prot's somewhere around here....
+ */
+
+ buffer = NODEPDA(node)->migr_refcnt_counterbuffer;
+ blen = NODEPDA(node)->migr_refcnt_cbsize;
+
+ /*
+ * Force offset to be a multiple of sizeof(refcnt_t)
+ * We round up.
+ */
+
+ off = (((off - 1)/sizeof(refcnt_t)) + 1) * sizeof(refcnt_t);
+
+ if ( ((buffer + blen) - (buffer + off + len)) < 0 ) {
+ return (EPERM);
+ }
+
+ errcode = v_mapphys(vt,
+ buffer + off,
+ len);
+
+ return errcode;
+}
+
+/*ARGSUSED*/
+int
+mem_refcnt_unmap(devfs_handle_t dev, vhandl_t *vt)
+{
+ return 0;
+}
+
+/* ARGSUSED */
+int
+mem_refcnt_ioctl(devfs_handle_t dev,
+ int cmd,
+ void *arg,
+ int mode,
+ cred_t *cred_p,
+ int *rvalp)
+{
+ cnodeid_t node;
+ int errcode;
+ extern int numnodes;
+
+ ASSERT( (hubspc_subdevice_t)(ulong)device_info_get(dev) == HUBSPC_REFCOUNTERS );
+
+ node = master_node_get(dev);
+
+ ASSERT( (node >= 0) && (node < numnodes) );
+
+ ASSERT( NODEPDA(node)->migr_refcnt_counterbuffer != NULL);
+ ASSERT( NODEPDA(node)->migr_refcnt_counterbase != NULL );
+ ASSERT( NODEPDA(node)->migr_refcnt_cbsize != 0 );
+
+ errcode = 0;
+
+ switch (cmd) {
+ case RCB_INFO_GET:
+ {
+ rcb_info_t rcb;
+
+ rcb.rcb_len = NODEPDA(node)->migr_refcnt_cbsize;
+
+ rcb.rcb_sw_sets = NODEPDA(node)->migr_refcnt_numsets;
+ rcb.rcb_sw_counters_per_set = numnodes;
+ rcb.rcb_sw_counter_size = sizeof(refcnt_t);
+
+ rcb.rcb_base_pages = NODEPDA(node)->migr_refcnt_numsets /
+ NUM_OF_HW_PAGES_PER_SW_PAGE();
+ rcb.rcb_base_page_size = NBPP;
+ rcb.rcb_base_paddr = ctob(slot_getbasepfn(node, 0));
+
+ rcb.rcb_cnodeid = node;
+ rcb.rcb_granularity = MD_PAGE_SIZE;
+#ifdef notyet
+ rcb.rcb_hw_counter_max = MIGR_COUNTER_MAX_GET(node);
+ rcb.rcb_diff_threshold = MIGR_THRESHOLD_DIFF_GET(node);
+#endif
+ rcb.rcb_abs_threshold = MIGR_THRESHOLD_ABS_GET(node);
+ rcb.rcb_num_slots = node_getnumslots(node);
+
+ if (COPYOUT(&rcb, arg, sizeof(rcb_info_t))) {
+ errcode = EFAULT;
+ }
+
+ break;
+ }
+ case RCB_SLOT_GET:
+ {
+ rcb_slot_t slot[MAX_MEM_SLOTS];
+ int s;
+ int nslots;
+
+ nslots = node_getnumslots(node);
+ ASSERT(nslots <= MAX_MEM_SLOTS);
+ for (s = 0; s < nslots; s++) {
+ slot[s].base = (uint64_t)ctob(slot_getbasepfn(node, s));
+#ifdef notyet
+ slot[s].size = (uint64_t)ctob(slot_getsize(node, s));
+#else
+ slot[s].size = (uint64_t)1;
+#endif
+ }
+ if (COPYOUT(&slot[0], arg, nslots * sizeof(rcb_slot_t))) {
+ errcode = EFAULT;
+ }
+
+ *rvalp = nslots;
+ break;
+ }
+
+ default:
+ errcode = EINVAL;
+ break;
+
+ }
+
+ return errcode;
+}
diff --git a/arch/ia64/sn/io/ml_SN_init.c b/arch/ia64/sn/io/ml_SN_init.c
new file mode 100644
index 000000000..4dbce801d
--- /dev/null
+++ b/arch/ia64/sn/io/ml_SN_init.c
@@ -0,0 +1,661 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/nodemask.h>
+#include <asm/sn/sn_private.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/sn_cpuid.h>
+#include <asm/sn/synergy.h>
+
+
+#if defined (CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+#include <asm/sn/sn1/ip27config.h>
+#include <asm/sn/sn1/hubdev.h>
+#include <asm/sn/sn1/sn1.h>
+#endif /* CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 */
+
+
+extern int numcpus;
+extern char arg_maxnodes[];
+extern cpuid_t master_procid;
+extern void * kmem_alloc_node(register size_t, register int , cnodeid_t);
+extern synergy_da_t *Synergy_da_indr[];
+
+extern int hasmetarouter;
+
+int maxcpus;
+cpumask_t boot_cpumask;
+hubreg_t region_mask = 0;
+
+
+extern xwidgetnum_t hub_widget_id(nasid_t);
+
+#ifndef CONFIG_IA64_SGI_IO
+#if defined (IP27)
+short cputype = CPU_IP27;
+#elif defined (IP33)
+short cputype = CPU_IP33;
+#elif defined (IP35)
+short cputype = CPU_IP35;
+#else
+#error <BOMB! define new cputype here >
+#endif
+#endif /* CONFIG_IA64_SGI_IO */
+
+static int fine_mode = 0;
+
+#ifndef CONFIG_IA64_SGI_IO
+/* Global variables */
+pdaindr_t pdaindr[MAXCPUS];
+#endif
+
+static cnodemask_t hub_init_mask; /* Mask of cpu in a node doing init */
+static volatile cnodemask_t hub_init_done_mask;
+ /* Node mask where we wait for
+ * per hub initialization
+ */
+spinlock_t hub_mask_lock; /* Lock for hub_init_mask above. */
+
+extern int valid_icache_reasons; /* Reasons to flush the icache */
+extern int valid_dcache_reasons; /* Reasons to flush the dcache */
+extern int numnodes;
+extern u_char miniroot;
+extern volatile int need_utlbmiss_patch;
+extern void iograph_early_init(void);
+
+nasid_t master_nasid = INVALID_NASID;
+
+
+/*
+ * mlreset(int slave)
+ * very early machine reset - at this point NO interrupts have been
+ * enabled; nor is memory, tlb, p0, etc setup.
+ *
+ * slave is zero when mlreset is called for the master processor and
+ * is nonzero thereafter.
+ */
+
+
+void
+mlreset(int slave)
+{
+ if (!slave) {
+ /*
+ * We are the master cpu and node.
+ */
+ master_nasid = get_nasid();
+ set_master_bridge_base();
+ FIXME("mlreset: Enable when we support ioc3 ..");
+#ifndef CONFIG_IA64_SGI_IO
+ if (get_console_nasid() == master_nasid)
+ /* Set up the IOC3 */
+ ioc3_mlreset((ioc3_cfg_t *)KL_CONFIG_CH_CONS_INFO(master_nasid)->config_base,
+ (ioc3_mem_t *)KL_CONFIG_CH_CONS_INFO(master_nasid)->memory_base);
+
+ /*
+ * Initialize Master nvram base.
+ */
+ nvram_baseinit();
+
+ fine_mode = is_fine_dirmode();
+#endif /* CONFIG_IA64_SGI_IO */
+
+ /* We're the master processor */
+ master_procid = smp_processor_id();
+ master_nasid = cpuid_to_nasid(master_procid);
+
+ /*
+ * master_nasid we get back better be same as one from
+ * get_nasid()
+ */
+ ASSERT_ALWAYS(master_nasid == get_nasid());
+
+#ifndef CONFIG_IA64_SGI_IO
+
+ /*
+ * Activate when calias is implemented.
+ */
+ /* Set all nodes' calias sizes to 8k */
+ for (i = 0; i < maxnodes; i++) {
+ nasid_t nasid;
+ int sn;
+
+ nasid = COMPACT_TO_NASID_NODEID(i);
+
+ /*
+ * Always have node 0 in the region mask, otherwise CALIAS accesses
+ * get exceptions since the hub thinks it is a node 0 address.
+ */
+ for (sn=0; sn<NUM_SUBNODES; sn++) {
+ REMOTE_HUB_PI_S(nasid, sn, PI_REGION_PRESENT, (region_mask | 1));
+ REMOTE_HUB_PI_S(nasid, sn, PI_CALIAS_SIZE, PI_CALIAS_SIZE_8K);
+ }
+
+ /*
+ * Set up all hubs to havew a big window pointing at
+ * widget 0.
+ * Memory mode, widget 0, offset 0
+ */
+ REMOTE_HUB_S(nasid, IIO_ITTE(SWIN0_BIGWIN),
+ ((HUB_PIO_MAP_TO_MEM << IIO_ITTE_IOSP_SHIFT) |
+ (0 << IIO_ITTE_WIDGET_SHIFT)));
+ }
+#endif /* CONFIG_IA64_SGI_IO */
+
+ /* Set up the hub initialization mask and init the lock */
+ CNODEMASK_CLRALL(hub_init_mask);
+ CNODEMASK_CLRALL(hub_init_done_mask);
+
+ spin_lock_init(&hub_mask_lock);
+
+ /* early initialization of iograph */
+ iograph_early_init();
+
+ /* Initialize Hub Pseudodriver Management */
+ hubdev_init();
+
+#ifndef CONFIG_IA64_SGI_IO
+ /*
+ * Our IO system doesn't require cache writebacks. Set some
+ * variables appropriately.
+ */
+ cachewrback = 0;
+ valid_icache_reasons &= ~(CACH_AVOID_VCES | CACH_IO_COHERENCY);
+ valid_dcache_reasons &= ~(CACH_AVOID_VCES | CACH_IO_COHERENCY);
+
+ /*
+ * make sure we are running with the right rev of chips
+ */
+ verify_snchip_rev();
+
+ /*
+ * Since we've wiped out memory at this point, we
+ * need to reset the ARCS vector table so that it
+ * points to appropriate functions in the kernel
+ * itself. In this way, we can maintain the ARCS
+ * vector table conventions without having to actually
+ * keep redundant PROM code in memory.
+ */
+ he_arcs_set_vectors();
+#endif /* CONFIG_IA64_SGI_IO */
+
+ } else { /* slave != 0 */
+ /*
+ * This code is performed ONLY by slave processors.
+ */
+
+ }
+}
+
+
+/* XXX - Move the meat of this to intr.c ? */
+/*
+ * Set up the platform-dependent fields in the nodepda.
+ */
+void init_platform_nodepda(nodepda_t *npda, cnodeid_t node)
+{
+ hubinfo_t hubinfo;
+ int sn;
+ cnodeid_t i;
+ ushort *numcpus_p;
+
+ extern void router_map_init(nodepda_t *);
+ extern void router_queue_init(nodepda_t *,cnodeid_t);
+#if defined(DEBUG)
+ extern lock_t intr_dev_targ_map_lock;
+ extern uint64_t intr_dev_targ_map_size;
+
+ /* Initialize the lock to access the device - target cpu mapping
+ * table. This table is explicitly for debugging purposes only and
+ * to aid the "intrmap" idbg command
+ */
+ if (node == 0) {
+ /* Make sure we do this only once .
+ * There is always a cnode 0 present.
+ */
+ intr_dev_targ_map_size = 0;
+ init_spinlock(&intr_dev_targ_map_lock,"dtmap_lock",0);
+ }
+#endif /* DEBUG */
+ /* Allocate per-node platform-dependent data */
+ hubinfo = (hubinfo_t)kmem_alloc_node(sizeof(struct hubinfo_s), GFP_ATOMIC, node);
+
+ ASSERT_ALWAYS(hubinfo);
+ npda->pdinfo = (void *)hubinfo;
+ hubinfo->h_nodepda = npda;
+ hubinfo->h_cnodeid = node;
+ hubinfo->h_nasid = COMPACT_TO_NASID_NODEID(node);
+
+ printk("init_platform_nodepda: hubinfo 0x%p, &hubinfo->h_crblock 0x%p\n", hubinfo, &hubinfo->h_crblock);
+
+ spin_lock_init(&hubinfo->h_crblock);
+
+ hubinfo->h_widgetid = hub_widget_id(hubinfo->h_nasid);
+ npda->xbow_peer = INVALID_NASID;
+ /* Initialize the linked list of
+ * router info pointers to the dependent routers
+ */
+ npda->npda_rip_first = NULL;
+ /* npda_rip_last always points to the place
+ * where the next element is to be inserted
+ * into the list
+ */
+ npda->npda_rip_last = &npda->npda_rip_first;
+ npda->dependent_routers = 0;
+ npda->module_id = INVALID_MODULE;
+
+ /*
+ * Initialize the subnodePDA.
+ */
+ for (sn=0; sn<NUM_SUBNODES; sn++) {
+ SNPDA(npda,sn)->prof_count = 0;
+ SNPDA(npda,sn)->next_prof_timeout = 0;
+// ajm
+#ifndef CONFIG_IA64_SGI_IO
+ intr_init_vecblk(npda, node, sn);
+#endif
+ }
+
+ npda->vector_unit_busy = 0;
+
+ spin_lock_init(&npda->vector_lock);
+ init_MUTEX_LOCKED(&npda->xbow_sema); /* init it locked? */
+ spin_lock_init(&npda->fprom_lock);
+
+ spin_lock_init(&npda->node_utlbswitchlock);
+ npda->ni_error_print = 0;
+#ifndef CONFIG_IA64_SGI_IO
+ if (need_utlbmiss_patch) {
+ npda->node_need_utlbmiss_patch = 1;
+ npda->node_utlbmiss_patched = 1;
+ }
+#endif
+
+ /*
+ * Clear out the nasid mask.
+ */
+ for (i = 0; i < NASID_MASK_BYTES; i++)
+ npda->nasid_mask[i] = 0;
+
+ for (i = 0; i < numnodes; i++) {
+ nasid_t nasid = COMPACT_TO_NASID_NODEID(i);
+
+ /* Set my mask bit */
+ npda->nasid_mask[nasid / 8] |= (1 << nasid % 8);
+ }
+
+#ifndef CONFIG_IA64_SGI_IO
+ npda->node_first_cpu = get_cnode_cpu(node);
+#endif
+
+ if (npda->node_first_cpu != CPU_NONE) {
+ /*
+ * Count number of cpus only if first CPU is valid.
+ */
+ numcpus_p = &npda->node_num_cpus;
+ *numcpus_p = 0;
+ for (i = npda->node_first_cpu; i < MAXCPUS; i++) {
+ if (CPUID_TO_COMPACT_NODEID(i) != node)
+ break;
+ else
+ (*numcpus_p)++;
+ }
+ } else {
+ npda->node_num_cpus = 0;
+ }
+
+ /* Allocate memory for the dump stack on each node
+ * This is useful during nmi handling since we
+ * may not be guaranteed shared memory at that time
+ * which precludes depending on a global dump stack
+ */
+#ifndef CONFIG_IA64_SGI_IO
+ npda->dump_stack = (uint64_t *)kmem_zalloc_node(DUMP_STACK_SIZE,VM_NOSLEEP,
+ node);
+ ASSERT_ALWAYS(npda->dump_stack);
+ ASSERT(npda->dump_stack);
+#endif
+ /* Initialize the counter which prevents
+ * both the cpus on a node to proceed with nmi
+ * handling.
+ */
+#ifndef CONFIG_IA64_SGI_IO
+ npda->dump_count = 0;
+
+ /* Setup the (module,slot) --> nic mapping for all the routers
+ * in the system. This is useful during error handling when
+ * there is no shared memory.
+ */
+ router_map_init(npda);
+
+ /* Allocate memory for the per-node router traversal queue */
+ router_queue_init(npda,node);
+ npda->sbe_info = kmem_zalloc_node_hint(sizeof (sbe_info_t), 0, node);
+ ASSERT(npda->sbe_info);
+
+#ifdef CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 || CONFIG_IA64_GENERIC
+ /*
+ * Initialize bte info pointers to NULL
+ */
+ for (i = 0; i < BTES_PER_NODE; i++) {
+ npda->node_bte_info[i] = (bteinfo_t *)NULL;
+ }
+#endif
+#endif /* CONFIG_IA64_SGI_IO */
+}
+
+/* XXX - Move the interrupt stuff to intr.c ? */
+/*
+ * Set up the platform-dependent fields in the processor pda.
+ * Must be done _after_ init_platform_nodepda().
+ * If we need a lock here, something else is wrong!
+ */
+// void init_platform_pda(pda_t *ppda, cpuid_t cpu)
+void init_platform_pda(cpuid_t cpu)
+{
+ hub_intmasks_t *intmasks;
+ cpuinfo_t cpuinfo;
+ int i;
+ cnodeid_t cnode;
+ synergy_da_t *sda;
+ int which_synergy;
+
+#ifndef CONFIG_IA64_SGI_IO
+ /* Allocate per-cpu platform-dependent data */
+ cpuinfo = (cpuinfo_t)kmem_alloc_node(sizeof(struct cpuinfo_s), GFP_ATOMIC, cputocnode(cpu));
+ ASSERT_ALWAYS(cpuinfo);
+ ppda->pdinfo = (void *)cpuinfo;
+ cpuinfo->ci_cpupda = ppda;
+ cpuinfo->ci_cpuid = cpu;
+#endif
+
+ cnode = cpuid_to_cnodeid(cpu);
+ which_synergy = cpuid_to_synergy(cpu);
+ sda = Synergy_da_indr[(cnode * 2) + which_synergy];
+ // intmasks = &ppda->p_intmasks;
+ intmasks = &sda->s_intmasks;
+
+#ifndef CONFIG_IA64_SGI_IO
+ ASSERT_ALWAYS(&ppda->p_nodepda);
+#endif
+
+ /* Clear INT_PEND0 masks. */
+ for (i = 0; i < N_INTPEND0_MASKS; i++)
+ intmasks->intpend0_masks[i] = 0;
+
+ /* Set up pointer to the vector block in the nodepda. */
+ /* (Cant use SUBNODEPDA - not working yet) */
+ intmasks->dispatch0 = &Nodepdaindr[cnode]->snpda[cputosubnode(cpu)].intr_dispatch0;
+ intmasks->dispatch1 = &Nodepdaindr[cnode]->snpda[cputosubnode(cpu)].intr_dispatch1;
+
+ /* Clear INT_PEND1 masks. */
+ for (i = 0; i < N_INTPEND1_MASKS; i++)
+ intmasks->intpend1_masks[i] = 0;
+
+
+#ifndef CONFIG_IA64_SGI_IO
+ /* Don't read the routers unless we're the master. */
+ ppda->p_routertick = 0;
+#endif
+
+}
+
+#if (defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)) && !defined(BRINGUP) /* protect low mem for IP35/7 */
+#error "need protect_hub_calias, protect_nmi_handler_data"
+#endif
+
+#ifndef CONFIG_IA64_SGI_IO
+/*
+ * For now, just protect the first page (exception handlers). We
+ * may want to protect more stuff later.
+ */
+void
+protect_hub_calias(nasid_t nasid)
+{
+ paddr_t pa = NODE_OFFSET(nasid) + 0; /* page 0 on node nasid */
+ int i;
+
+ for (i = 0; i < MAX_REGIONS; i++) {
+ if (i == nasid_to_region(nasid))
+ continue;
+#ifndef BRINGUP
+ /* Protect the exception handlers. */
+ *(__psunsigned_t *)BDPRT_ENTRY(pa, i) = MD_PROT_NO;
+
+ /* Protect the ARCS SPB. */
+ *(__psunsigned_t *)BDPRT_ENTRY(pa + 4096, i) = MD_PROT_NO;
+#endif
+ }
+}
+
+/*
+ * Protect the page of low memory used to communicate with the NMI handler.
+ */
+void
+protect_nmi_handler_data(nasid_t nasid, int slice)
+{
+ paddr_t pa = NODE_OFFSET(nasid) + NMI_OFFSET(nasid, slice);
+ int i;
+
+ for (i = 0; i < MAX_REGIONS; i++) {
+ if (i == nasid_to_region(nasid))
+ continue;
+#ifndef BRINGUP
+ *(__psunsigned_t *)BDPRT_ENTRY(pa, i) = MD_PROT_NO;
+#endif
+ }
+}
+#endif /* CONFIG_IA64_SGI_IO */
+
+
+#ifdef IRIX
+/*
+ * Protect areas of memory that we access uncached by marking them as
+ * poisoned so the T5 can't read them speculatively and erroneously
+ * mark them dirty in its cache only to write them back with old data
+ * later.
+ */
+static void
+protect_low_memory(nasid_t nasid)
+{
+ /* Protect low memory directory */
+ poison_state_alter_range(KLDIR_ADDR(nasid), KLDIR_SIZE, 1);
+
+ /* Protect klconfig area */
+ poison_state_alter_range(KLCONFIG_ADDR(nasid), KLCONFIG_SIZE(nasid), 1);
+
+ /* Protect the PI error spool area. */
+ poison_state_alter_range(PI_ERROR_ADDR(nasid), PI_ERROR_SIZE(nasid), 1);
+
+ /* Protect CPU A's cache error eframe area. */
+ poison_state_alter_range(TO_NODE_UNCAC(nasid, CACHE_ERR_EFRAME),
+ CACHE_ERR_AREA_SIZE, 1);
+
+ /* Protect CPU B's area */
+ poison_state_alter_range(TO_NODE_UNCAC(nasid, CACHE_ERR_EFRAME)
+ ^ UALIAS_FLIP_BIT,
+ CACHE_ERR_AREA_SIZE, 1);
+#error "SN1 not handled correctly"
+}
+#endif /* IRIX */
+
+/*
+ * per_hub_init
+ *
+ * This code is executed once for each Hub chip.
+ */
+void
+per_hub_init(cnodeid_t cnode)
+{
+ uint64_t done;
+ nasid_t nasid;
+ nodepda_t *npdap;
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC) /* SN1 specific */
+ ii_icmr_u_t ii_icmr;
+ ii_ibcr_u_t ii_ibcr;
+#endif
+#ifndef CONFIG_IA64_SGI_IO
+ int i;
+#endif
+
+#ifdef SIMULATED_KLGRAPH
+ compact_to_nasid_node[0] = 0;
+ nasid_to_compact_node[0] = 0;
+ FIXME("per_hub_init: SIMULATED_KLCONFIG: compact_to_nasid_node[0] = 0\n");
+#endif /* SIMULATED_KLGRAPH */
+ nasid = COMPACT_TO_NASID_NODEID(cnode);
+
+ ASSERT(nasid != INVALID_NASID);
+ ASSERT(NASID_TO_COMPACT_NODEID(nasid) == cnode);
+
+ /* Grab the hub_mask lock. */
+ spin_lock(&hub_mask_lock);
+
+ /* Test our bit. */
+ if (!(done = CNODEMASK_TSTB(hub_init_mask, cnode))) {
+
+ /* Turn our bit on in the mask. */
+ CNODEMASK_SETB(hub_init_mask, cnode);
+ }
+
+#if defined(SN0_HWDEBUG)
+ hub_config_setup();
+#endif
+ /* Release the hub_mask lock. */
+ spin_unlock(&hub_mask_lock);
+
+ /*
+ * Do the actual initialization if it hasn't been done yet.
+ * We don't need to hold a lock for this work.
+ */
+ if (!done) {
+ npdap = NODEPDA(cnode);
+
+ npdap->hub_chip_rev = get_hub_chiprev(nasid);
+
+#ifndef CONFIG_IA64_SGI_IO
+ for (i = 0; i < CPUS_PER_NODE; i++) {
+ cpu = cnode_slice_to_cpuid(cnode, i);
+ if (!cpu_enabled(cpu))
+ SET_CPU_LEDS(nasid, i, 0xf);
+ }
+#endif /* CONFIG_IA64_SGI_IO */
+
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC) /* SN1 specific */
+
+ /*
+ * Set the total number of CRBs that can be used.
+ */
+ ii_icmr.ii_icmr_regval= 0x0;
+ ii_icmr.ii_icmr_fld_s.i_c_cnt = 0xF;
+ REMOTE_HUB_S(nasid, IIO_ICMR, ii_icmr.ii_icmr_regval);
+
+ /*
+ * Set the number of CRBs that both of the BTEs combined
+ * can use minus 1.
+ */
+ ii_ibcr.ii_ibcr_regval= 0x0;
+ ii_ibcr.ii_ibcr_fld_s.i_count = 0x8;
+ REMOTE_HUB_S(nasid, IIO_IBCR, ii_ibcr.ii_ibcr_regval);
+
+ /*
+ * Set CRB timeout to be 10ms.
+ */
+ REMOTE_HUB_S(nasid, IIO_ICTP, 0x1000 );
+ REMOTE_HUB_S(nasid, IIO_ICTO, 0xff);
+
+#endif /* SN0_HWDEBUG */
+
+
+#ifndef CONFIG_IA64_SGI_IO
+
+ /* Reserve all of the hardwired interrupt levels. */
+ intr_reserve_hardwired(cnode);
+
+ /* Initialize error interrupts for this hub. */
+ hub_error_init(cnode);
+
+ /* Set up correctable memory/directory ECC error interrupt. */
+ install_eccintr(cnode);
+
+ /* Protect our exception vectors from accidental corruption. */
+ protect_hub_calias(nasid);
+
+ /* Enable RT clock interrupts */
+ hub_rtc_init(cnode);
+ hub_migrintr_init(cnode); /* Enable migration interrupt */
+#endif
+
+ spin_lock(&hub_mask_lock);
+ CNODEMASK_SETB(hub_init_done_mask, cnode);
+ spin_unlock(&hub_mask_lock);
+
+ } else {
+ /*
+ * Wait for the other CPU to complete the initialization.
+ */
+ while (CNODEMASK_TSTB(hub_init_done_mask, cnode) == 0)
+ /* LOOP */
+ ;
+ }
+}
+
+extern void
+update_node_information(cnodeid_t cnodeid)
+{
+ nodepda_t *npda = NODEPDA(cnodeid);
+ nodepda_router_info_t *npda_rip;
+
+ /* Go through the list of router info
+ * structures and copy some frequently
+ * accessed info from the info hanging
+ * off the corresponding router vertices
+ */
+ npda_rip = npda->npda_rip_first;
+ while(npda_rip) {
+ if (npda_rip->router_infop) {
+ npda_rip->router_portmask =
+ npda_rip->router_infop->ri_portmask;
+ npda_rip->router_slot =
+ npda_rip->router_infop->ri_slotnum;
+ } else {
+ /* No router, no ports. */
+ npda_rip->router_portmask = 0;
+ }
+ npda_rip = npda_rip->router_next;
+ }
+}
+
+hubreg_t
+get_region(cnodeid_t cnode)
+{
+ if (fine_mode)
+ return COMPACT_TO_NASID_NODEID(cnode) >> NASID_TO_FINEREG_SHFT;
+ else
+ return COMPACT_TO_NASID_NODEID(cnode) >> NASID_TO_COARSEREG_SHFT;
+}
+
+hubreg_t
+nasid_to_region(nasid_t nasid)
+{
+ if (fine_mode)
+ return nasid >> NASID_TO_FINEREG_SHFT;
+ else
+ return nasid >> NASID_TO_COARSEREG_SHFT;
+}
+
diff --git a/arch/ia64/sn/io/ml_SN_intr.c b/arch/ia64/sn/io/ml_SN_intr.c
new file mode 100644
index 000000000..c643b6e8b
--- /dev/null
+++ b/arch/ia64/sn/io/ml_SN_intr.c
@@ -0,0 +1,1730 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Alan Mayer
+ */
+
+/*
+ * intr.c-
+ * This file contains all of the routines necessary to set up and
+ * handle interrupts on an IP27 board.
+ */
+
+#ident "$Revision: 1.167 $"
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <asm/smp.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/nodemask.h>
+#include <asm/sn/sn_private.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/synergy.h>
+#include <asm/sn/sn_cpuid.h>
+#include <asm/sn/pci/pciio.h>
+#include <asm/sn/pci/pcibr.h>
+#include <asm/sn/xtalk/xtalk.h>
+#include <asm/sn/pci/pcibr_private.h>
+
+#if DEBUG_INTR_TSTAMP_DEBUG
+#include <sys/debug.h>
+#include <sys/idbg.h>
+#include <sys/inst.h>
+void do_splx_log(int, int);
+void spldebug_log_event(int);
+#endif
+
+// FIXME - BRINGUP
+#ifdef CONFIG_SMP
+extern unsigned long cpu_online_map;
+#endif
+#define cpu_allows_intr(cpu) (1)
+// If I understand what's going on with this, 32 should work.
+// physmem_maxradius seems to be the maximum number of router
+// hops to get from one end of the system to the other. With
+// a maximally configured machine, with the dumbest possible
+// topology, we would make 32 router hops. For what we're using
+// it for, the dumbest possible should suffice.
+#define physmem_maxradius() 32
+
+#define SUBNODE_ANY -1
+
+extern int nmied;
+extern int hub_intr_wakeup_cnt;
+extern synergy_da_t *Synergy_da_indr[];
+extern cpuid_t master_procid;
+
+extern cnodeid_t master_node_get(devfs_handle_t vhdl);
+
+
+#define INTR_LOCK(vecblk) \
+ (s = mutex_spinlock(&(vecblk)->vector_lock))
+#define INTR_UNLOCK(vecblk) \
+ mutex_spinunlock(&(vecblk)->vector_lock, s)
+
+/*
+ * REACT/Pro
+ */
+
+
+
+/*
+ * Find first bit set
+ * Used outside this file also
+ */
+int ms1bit(unsigned long x)
+{
+ int b;
+
+ if (x >> 32) b = 32, x >>= 32;
+ else b = 0;
+ if (x >> 16) b += 16, x >>= 16;
+ if (x >> 8) b += 8, x >>= 8;
+ if (x >> 4) b += 4, x >>= 4;
+ if (x >> 2) b += 2, x >>= 2;
+
+ return b + (int) (x >> 1);
+}
+
+/* ARGSUSED */
+void
+intr_stray(void *lvl)
+{
+ printk("Stray Interrupt - level %ld to cpu %d", (long)lvl, cpuid());
+}
+
+#if defined(DEBUG)
+
+/* Infrastructure to gather the device - target cpu mapping info */
+#define MAX_DEVICES 1000 /* Reasonable large number . Need not be
+ * the exact maximum # devices possible.
+ */
+#define MAX_NAME 100
+typedef struct {
+ dev_t dev; /* device */
+ cpuid_t cpuid; /* target cpu */
+ cnodeid_t cnodeid;/* node on which the target cpu is present */
+ int bit; /* intr bit reserved */
+ char intr_name[MAX_NAME]; /* name of the interrupt */
+} intr_dev_targ_map_t;
+
+intr_dev_targ_map_t intr_dev_targ_map[MAX_DEVICES];
+uint64_t intr_dev_targ_map_size;
+lock_t intr_dev_targ_map_lock;
+
+/* Print out the device - target cpu mapping.
+ * This routine is used only in the idbg command
+ * "intrmap"
+ */
+void
+intr_dev_targ_map_print(cnodeid_t cnodeid)
+{
+ int i,j,size = 0;
+ int print_flag = 0,verbose = 0;
+ char node_name[10];
+
+ if (cnodeid != CNODEID_NONE) {
+ nodepda_t *npda;
+
+ npda = NODEPDA(cnodeid);
+ for (j=0; j<NUM_SUBNODES; j++) {
+ qprintf("\n SUBNODE %d\n INT_PEND0: ", j);
+ for(i = 0 ; i < N_INTPEND_BITS ; i++)
+ qprintf("%d",SNPDA(npda,j)->intr_dispatch0.info[i].ii_flags);
+ qprintf("\n INT_PEND1: ");
+ for(i = 0 ; i < N_INTPEND_BITS ; i++)
+ qprintf("%d",SNPDA(npda,j)->intr_dispatch1.info[i].ii_flags);
+ }
+ verbose = 1;
+ }
+ qprintf("\n Device - Target Map [Interrupts: %s Node%s]\n\n",
+ (verbose ? "All" : "Non-hardwired"),
+ (cnodeid == CNODEID_NONE) ? "s: All" : node_name);
+
+ qprintf("Device\tCpu\tCnode\tIntr_bit\tIntr_name\n");
+ for (i = 0 ; i < intr_dev_targ_map_size ; i++) {
+
+ print_flag = 0;
+ if (verbose) {
+ if (cnodeid != CNODEID_NONE) {
+ if (cnodeid == intr_dev_targ_map[i].cnodeid)
+ print_flag = 1;
+ } else {
+ print_flag = 1;
+ }
+ } else {
+ if (intr_dev_targ_map[i].dev != 0) {
+ if (cnodeid != CNODEID_NONE) {
+ if (cnodeid ==
+ intr_dev_targ_map[i].cnodeid)
+ print_flag = 1;
+ } else {
+ print_flag = 1;
+ }
+ }
+ }
+ if (print_flag) {
+ size++;
+ qprintf("%d\t%d\t%d\t%d\t%s\n",
+ intr_dev_targ_map[i].dev,
+ intr_dev_targ_map[i].cpuid,
+ intr_dev_targ_map[i].cnodeid,
+ intr_dev_targ_map[i].bit,
+ intr_dev_targ_map[i].intr_name);
+ }
+
+ }
+ qprintf("\nTotal : %d\n",size);
+}
+#endif /* DEBUG */
+
+/*
+ * The spinlocks have already been initialized. Now initialize the interrupt
+ * vectors. One processor on each hub does the work.
+ */
+void
+intr_init_vecblk(nodepda_t *npda, cnodeid_t node, int sn)
+{
+ int i, ip=0;
+ intr_vecblk_t *vecblk;
+ subnode_pda_t *snpda;
+
+
+ snpda = SNPDA(npda,sn);
+ do {
+ if (ip == 0) {
+ vecblk = &snpda->intr_dispatch0;
+ } else {
+ vecblk = &snpda->intr_dispatch1;
+ }
+
+ /* Initialize this vector. */
+ for (i = 0; i < N_INTPEND_BITS; i++) {
+ vecblk->vectors[i].iv_func = intr_stray;
+ vecblk->vectors[i].iv_prefunc = NULL;
+ vecblk->vectors[i].iv_arg = (void *)(__psint_t)(ip * N_INTPEND_BITS + i);
+
+ vecblk->info[i].ii_owner_dev = 0;
+ strcpy(vecblk->info[i].ii_name, "Unused");
+ vecblk->info[i].ii_flags = 0; /* No flags */
+ vecblk->vectors[i].iv_mustruncpu = -1; /* No CPU yet. */
+
+ }
+
+ spinlock_init(&vecblk->vector_lock, "ivecb");
+
+ vecblk->vector_count = 0;
+ for (i = 0; i < CPUS_PER_SUBNODE; i++)
+ vecblk->cpu_count[i] = 0;
+
+ vecblk->vector_state = VECTOR_UNINITED;
+
+ } while (++ip < 2);
+
+}
+
+
+/*
+ * do_intr_reserve_level(cpuid_t cpu, int bit, int resflags, int reserve,
+ * devfs_handle_t owner_dev, char *name)
+ * Internal work routine to reserve or unreserve an interrupt level.
+ * cpu is the CPU to which the interrupt will be sent.
+ * bit is the level bit to reserve. -1 means any level
+ * resflags should include II_ERRORINT if this is an
+ * error interrupt, II_THREADED if the interrupt handler
+ * will be threaded, or 0 otherwise.
+ * reserve should be set to II_RESERVE or II_UNRESERVE
+ * to get or clear a reservation.
+ * owner_dev is the device that "owns" this interrupt, if supplied
+ * name is a human-readable name for this interrupt, if supplied
+ * intr_reserve_level returns the bit reserved or -1 to indicate an error
+ */
+static int
+do_intr_reserve_level(cpuid_t cpu, int bit, int resflags, int reserve,
+ devfs_handle_t owner_dev, char *name)
+{
+ intr_vecblk_t *vecblk;
+ hub_intmasks_t *hub_intmasks;
+ int s;
+ int rv = 0;
+ int ip;
+ synergy_da_t *sda;
+ int which_synergy;
+ cnodeid_t cnode;
+
+ ASSERT(bit < N_INTPEND_BITS * 2);
+
+ cnode = cpuid_to_cnodeid(cpu);
+ which_synergy = cpuid_to_synergy(cpu);
+ sda = Synergy_da_indr[(cnode * 2) + which_synergy];
+ hub_intmasks = &sda->s_intmasks;
+ // hub_intmasks = &pdaindr[cpu].pda->p_intmasks;
+
+ // if (pdaindr[cpu].pda == NULL) return -1;
+ if ((bit < N_INTPEND_BITS) && !(resflags & II_ERRORINT)) {
+ vecblk = hub_intmasks->dispatch0;
+ ip = 0;
+ } else {
+ ASSERT((bit >= N_INTPEND_BITS) || (bit == -1));
+ bit -= N_INTPEND_BITS; /* Get position relative to INT_PEND1 reg. */
+ vecblk = hub_intmasks->dispatch1;
+ ip = 1;
+ }
+
+ INTR_LOCK(vecblk);
+
+ if (bit <= -1) {
+ // bit = 0;
+ bit = 7; /* First available on SNIA */
+ ASSERT(reserve == II_RESERVE);
+ /* Choose any available level */
+ for (; bit < N_INTPEND_BITS; bit++) {
+ if (!(vecblk->info[bit].ii_flags & II_RESERVE)) {
+ rv = bit;
+ break;
+ }
+ }
+
+ /* Return -1 if all interrupt levels int this register are taken. */
+ if (bit == N_INTPEND_BITS)
+ rv = -1;
+
+ } else {
+ /* Reserve a particular level if it's available. */
+ if ((vecblk->info[bit].ii_flags & II_RESERVE) == reserve) {
+ /* Can't (un)reserve a level that's already (un)reserved. */
+ rv = -1;
+ } else {
+ rv = bit;
+ }
+ }
+
+ /* Reserve the level and bump the count. */
+ if (rv != -1) {
+ if (reserve) {
+ int maxlen = sizeof(vecblk->info[bit].ii_name) - 1;
+ int namelen;
+ vecblk->info[bit].ii_flags |= (II_RESERVE | resflags);
+ vecblk->info[bit].ii_owner_dev = owner_dev;
+ /* Copy in the name. */
+ namelen = name ? strlen(name) : 0;
+ strncpy(vecblk->info[bit].ii_name, name, MIN(namelen, maxlen));
+ vecblk->info[bit].ii_name[maxlen] = '\0';
+ vecblk->vector_count++;
+ } else {
+ vecblk->info[bit].ii_flags = 0; /* Clear all the flags */
+ vecblk->info[bit].ii_owner_dev = 0;
+ /* Clear the name. */
+ vecblk->info[bit].ii_name[0] = '\0';
+ vecblk->vector_count--;
+ }
+ }
+
+ INTR_UNLOCK(vecblk);
+
+#if defined(DEBUG)
+ if (rv >= 0) {
+ int namelen = name ? strlen(name) : 0;
+ /* Gather this device - target cpu mapping information
+ * in a table which can be used later by the idbg "intrmap"
+ * command
+ */
+ s = mutex_spinlock(&intr_dev_targ_map_lock);
+ if (intr_dev_targ_map_size < MAX_DEVICES) {
+ intr_dev_targ_map_t *p;
+
+ p = &intr_dev_targ_map[intr_dev_targ_map_size];
+ p->dev = owner_dev;
+ p->cpuid = cpu;
+ p->cnodeid = cputocnode(cpu);
+ p->bit = ip * N_INTPEND_BITS + rv;
+ strncpy(p->intr_name,
+ name,
+ MIN(MAX_NAME,namelen));
+ intr_dev_targ_map_size++;
+ }
+ mutex_spinunlock(&intr_dev_targ_map_lock,s);
+ }
+#endif /* DEBUG */
+
+ return (((rv == -1) ? rv : (ip * N_INTPEND_BITS) + rv)) ;
+}
+
+
+/*
+ * WARNING: This routine should only be called from within ml/SN.
+ * Reserve an interrupt level.
+ */
+int
+intr_reserve_level(cpuid_t cpu, int bit, int resflags, devfs_handle_t owner_dev, char *name)
+{
+ return(do_intr_reserve_level(cpu, bit, resflags, II_RESERVE, owner_dev, name));
+}
+
+
+/*
+ * WARNING: This routine should only be called from within ml/SN.
+ * Unreserve an interrupt level.
+ */
+void
+intr_unreserve_level(cpuid_t cpu, int bit)
+{
+ (void)do_intr_reserve_level(cpu, bit, 0, II_UNRESERVE, 0, NULL);
+}
+
+/*
+ * Get values that vary depending on which CPU and bit we're operating on
+ */
+static hub_intmasks_t *
+intr_get_ptrs(cpuid_t cpu, int bit,
+ int *new_bit, /* Bit relative to the register */
+ hubreg_t **intpend_masks, /* Masks for this register */
+ intr_vecblk_t **vecblk, /* Vecblock for this interrupt */
+ int *ip) /* Which intpend register */
+{
+ hub_intmasks_t *hub_intmasks;
+ synergy_da_t *sda;
+ int which_synergy;
+ cnodeid_t cnode;
+
+ ASSERT(bit < N_INTPEND_BITS * 2);
+
+ cnode = cpuid_to_cnodeid(cpu);
+ which_synergy = cpuid_to_synergy(cpu);
+ sda = Synergy_da_indr[(cnode * 2) + which_synergy];
+ hub_intmasks = &sda->s_intmasks;
+
+ // hub_intmasks = &pdaindr[cpu].pda->p_intmasks;
+
+ if (bit < N_INTPEND_BITS) {
+ *intpend_masks = hub_intmasks->intpend0_masks;
+ *vecblk = hub_intmasks->dispatch0;
+ *ip = 0;
+ *new_bit = bit;
+ } else {
+ *intpend_masks = hub_intmasks->intpend1_masks;
+ *vecblk = hub_intmasks->dispatch1;
+ *ip = 1;
+ *new_bit = bit - N_INTPEND_BITS;
+ }
+
+ return hub_intmasks;
+}
+
+
+/*
+ * intr_connect_level(cpuid_t cpu, int bit, ilvl_t intr_swlevel,
+ * intr_func_t intr_func, void *intr_arg);
+ * This is the lowest-level interface to the interrupt code. It shouldn't
+ * be called from outside the ml/SN directory.
+ * intr_connect_level hooks up an interrupt to a particular bit in
+ * the INT_PEND0/1 masks. Returns 0 on success.
+ * cpu is the CPU to which the interrupt will be sent.
+ * bit is the level bit to connect to
+ * intr_swlevel tells which software level to use
+ * intr_func is the interrupt handler
+ * intr_arg is an arbitrary argument interpreted by the handler
+ * intr_prefunc is a prologue function, to be called
+ * with interrupts disabled, to disable
+ * the interrupt at source. It is called
+ * with the same argument. Should be NULL for
+ * typical interrupts, which can be masked
+ * by the infrastructure at the level bit.
+ * intr_connect_level returns 0 on success or nonzero on an error
+ */
+/* ARGSUSED */
+int
+intr_connect_level(cpuid_t cpu, int bit, ilvl_t intr_swlevel,
+ intr_func_t intr_func, void *intr_arg,
+ intr_func_t intr_prefunc)
+{
+ intr_vecblk_t *vecblk;
+ hubreg_t *intpend_masks;
+ int s;
+ int rv = 0;
+ int ip;
+
+ ASSERT(bit < N_INTPEND_BITS * 2);
+
+ (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks,
+ &vecblk, &ip);
+
+ INTR_LOCK(vecblk);
+
+ if ((vecblk->info[bit].ii_flags & II_INUSE) ||
+ (!(vecblk->info[bit].ii_flags & II_RESERVE))) {
+ /* Can't assign to a level that's in use or isn't reserved. */
+ rv = -1;
+ } else {
+ /* Stuff parameters into vector and info */
+ vecblk->vectors[bit].iv_func = intr_func;
+ vecblk->vectors[bit].iv_prefunc = intr_prefunc;
+ vecblk->vectors[bit].iv_arg = intr_arg;
+ vecblk->info[bit].ii_flags |= II_INUSE;
+ }
+
+ /* Now stuff the masks if everything's okay. */
+ if (!rv) {
+ int lslice;
+ volatile hubreg_t *mask_reg;
+ // nasid_t nasid = COMPACT_TO_NASID_NODEID(cputocnode(cpu));
+ nasid_t nasid = cpuid_to_nasid(cpu);
+ int subnode = cpuid_to_subnode(cpu);
+
+ /* Make sure it's not already pending when we connect it. */
+ REMOTE_HUB_PI_CLR_INTR(nasid, subnode, bit + ip * N_INTPEND_BITS);
+
+ intpend_masks[0] |= (1ULL << (uint64_t)bit);
+
+ lslice = cputolocalslice(cpu);
+ vecblk->cpu_count[lslice]++;
+#if SN1
+ /*
+ * On SN1, there are 8 interrupt mask registers per node:
+ * PI_0 MASK_0 A
+ * PI_0 MASK_1 A
+ * PI_0 MASK_0 B
+ * PI_0 MASK_1 B
+ * PI_1 MASK_0 A
+ * PI_1 MASK_1 A
+ * PI_1 MASK_0 B
+ * PI_1 MASK_1 B
+ */
+#endif
+ if (ip == 0) {
+ mask_reg = REMOTE_HUB_PI_ADDR(nasid, subnode,
+ PI_INT_MASK0_A + PI_INT_MASK_OFFSET * lslice);
+ } else {
+ mask_reg = REMOTE_HUB_PI_ADDR(nasid, subnode,
+ PI_INT_MASK1_A + PI_INT_MASK_OFFSET * lslice);
+ }
+
+ HUB_S(mask_reg, intpend_masks[0]);
+ }
+
+ INTR_UNLOCK(vecblk);
+
+ return rv;
+}
+
+
+/*
+ * intr_disconnect_level(cpuid_t cpu, int bit)
+ *
+ * This is the lowest-level interface to the interrupt code. It should
+ * not be called from outside the ml/SN directory.
+ * intr_disconnect_level removes a particular bit from an interrupt in
+ * the INT_PEND0/1 masks. Returns 0 on success or nonzero on failure.
+ */
+int
+intr_disconnect_level(cpuid_t cpu, int bit)
+{
+ intr_vecblk_t *vecblk;
+ hubreg_t *intpend_masks;
+ int s;
+ int rv = 0;
+ int ip;
+
+ (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks,
+ &vecblk, &ip);
+
+ INTR_LOCK(vecblk);
+
+ if ((vecblk->info[bit].ii_flags & (II_RESERVE | II_INUSE)) !=
+ ((II_RESERVE | II_INUSE))) {
+ /* Can't remove a level that's not in use or isn't reserved. */
+ rv = -1;
+ } else {
+ /* Stuff parameters into vector and info */
+ vecblk->vectors[bit].iv_func = (intr_func_t)NULL;
+ vecblk->vectors[bit].iv_prefunc = (intr_func_t)NULL;
+ vecblk->vectors[bit].iv_arg = 0;
+ vecblk->info[bit].ii_flags &= ~II_INUSE;
+#ifdef BASE_ITHRTEAD
+ vecblk->vectors[bit].iv_mustruncpu = -1; /* No mustrun CPU any more. */
+#endif
+ }
+
+ /* Now clear the masks if everything's okay. */
+ if (!rv) {
+ int lslice;
+ volatile hubreg_t *mask_reg;
+
+ intpend_masks[0] &= ~(1ULL << (uint64_t)bit);
+ lslice = cputolocalslice(cpu);
+ vecblk->cpu_count[lslice]--;
+ mask_reg = REMOTE_HUB_PI_ADDR(COMPACT_TO_NASID_NODEID(cputocnode(cpu)),
+ cpuid_to_subnode(cpu),
+ ip == 0 ? PI_INT_MASK0_A : PI_INT_MASK1_A);
+ mask_reg = (volatile hubreg_t *)((__psunsigned_t)mask_reg +
+ (PI_INT_MASK_OFFSET * lslice));
+ *mask_reg = intpend_masks[0];
+ }
+
+ INTR_UNLOCK(vecblk);
+
+ return rv;
+}
+
+/*
+ * Actually block or unblock an interrupt
+ */
+void
+do_intr_block_bit(cpuid_t cpu, int bit, int block)
+{
+ intr_vecblk_t *vecblk;
+ int s;
+ int ip;
+ hubreg_t *intpend_masks;
+ volatile hubreg_t mask_value;
+ volatile hubreg_t *mask_reg;
+
+ intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &vecblk, &ip);
+
+ INTR_LOCK(vecblk);
+
+ if (block)
+ /* Block */
+ intpend_masks[0] &= ~(1ULL << (uint64_t)bit);
+ else
+ /* Unblock */
+ intpend_masks[0] |= (1ULL << (uint64_t)bit);
+
+ if (ip == 0) {
+ mask_reg = REMOTE_HUB_PI_ADDR(COMPACT_TO_NASID_NODEID(cputocnode(cpu)),
+ cpuid_to_subnode(cpu), PI_INT_MASK0_A);
+ } else {
+ mask_reg = REMOTE_HUB_PI_ADDR(COMPACT_TO_NASID_NODEID(cputocnode(cpu)),
+ cpuid_to_subnode(cpu), PI_INT_MASK1_A);
+ }
+
+ HUB_S(mask_reg, intpend_masks[0]);
+
+ /*
+ * Wait for it to take effect. (One read should suffice.)
+ * This is only necessary when blocking an interrupt
+ */
+ if (block)
+ while ((mask_value = HUB_L(mask_reg)) != intpend_masks[0])
+ ;
+
+ INTR_UNLOCK(vecblk);
+}
+
+
+/*
+ * Block a particular interrupt (cpu/bit pair).
+ */
+/* ARGSUSED */
+void
+intr_block_bit(cpuid_t cpu, int bit)
+{
+ do_intr_block_bit(cpu, bit, 1);
+}
+
+
+/*
+ * Unblock a particular interrupt (cpu/bit pair).
+ */
+/* ARGSUSED */
+void
+intr_unblock_bit(cpuid_t cpu, int bit)
+{
+ do_intr_block_bit(cpu, bit, 0);
+}
+
+
+/* verifies that the specified CPUID is on the specified SUBNODE (if any) */
+#define cpu_on_subnode(cpuid, which_subnode) \
+ (((which_subnode) == SUBNODE_ANY) || (cpuid_to_subnode(cpuid) == (which_subnode)))
+
+
+/*
+ * Choose one of the CPUs on a specified node or subnode to receive
+ * interrupts. Don't pick a cpu which has been specified as a NOINTR cpu.
+ *
+ * Among all acceptable CPUs, the CPU that has the fewest total number
+ * of interrupts targetted towards it is chosen. Note that we never
+ * consider how frequent each of these interrupts might occur, so a rare
+ * hardware error interrupt is weighted equally with a disk interrupt.
+ */
+static cpuid_t
+do_intr_cpu_choose(cnodeid_t cnode, int which_subnode)
+{
+ cpuid_t cpu, best_cpu = CPU_NONE;
+ int slice, min_count=1000;
+
+ min_count = 1000;
+ for (slice=0; slice < CPUS_PER_NODE; slice++) {
+ intr_vecblk_t *vecblk0, *vecblk1;
+ int total_intrs_to_slice;
+ subnode_pda_t *snpda;
+ int local_cpu_num;
+
+ cpu = cnode_slice_to_cpuid(cnode, slice);
+ cpu = cpu_logical_id(cpu);
+ if (cpu == CPU_NONE)
+ continue;
+
+ /* If this cpu isn't enabled for interrupts, skip it */
+ if (!cpu_enabled(cpu) || !cpu_allows_intr(cpu))
+ continue;
+
+ /* If this isn't the right subnode, skip it */
+ if (!cpu_on_subnode(cpu, which_subnode))
+ continue;
+
+ /* OK, this one's a potential CPU for interrupts */
+ snpda = SUBNODEPDA(cnode,SUBNODE(slice));
+ vecblk0 = &snpda->intr_dispatch0;
+ vecblk1 = &snpda->intr_dispatch1;
+ local_cpu_num = LOCALCPU(slice);
+ total_intrs_to_slice = vecblk0->cpu_count[local_cpu_num] +
+ vecblk1->cpu_count[local_cpu_num];
+
+ if (min_count > total_intrs_to_slice) {
+ min_count = total_intrs_to_slice;
+ best_cpu = cpu;
+ }
+ }
+ return best_cpu;
+}
+
+/*
+ * Choose an appropriate interrupt target CPU on a specified node.
+ * If which_subnode is SUBNODE_ANY, then subnode is not considered.
+ * Otherwise, the chosen CPU must be on the specified subnode.
+ */
+static cpuid_t
+intr_cpu_choose_from_node(cnodeid_t cnode, int which_subnode)
+{
+ return(do_intr_cpu_choose(cnode, which_subnode));
+}
+
+
+#ifndef CONFIG_IA64_SGI_IO
+/*
+ * Convert a subnode vertex into a (cnodeid, which_subnode) pair.
+ * Return 0 on success, non-zero on failure.
+ */
+static int
+subnodevertex_to_subnode(devfs_handle_t vhdl, cnodeid_t *cnodeidp, int *which_subnodep)
+{
+ arbitrary_info_t which_subnode;
+ cnodeid_t cnodeid;
+
+ /* Try to grab subnode information */
+ if (hwgraph_info_get_LBL(vhdl, INFO_LBL_CPUBUS, &which_subnode) != GRAPH_SUCCESS)
+ return(-1);
+
+ /* On which node? */
+ cnodeid = master_node_get(vhdl);
+ if (cnodeid == CNODEID_NONE)
+ return(-1);
+
+ *which_subnodep = (int)which_subnode;
+ *cnodeidp = cnodeid;
+ return(0); /* success */
+}
+
+#endif /* CONFIG_IA64_SGI_IO */
+
+/* Make it easy to identify subnode vertices in the hwgraph */
+void
+mark_subnodevertex_as_subnode(devfs_handle_t vhdl, int which_subnode)
+{
+ graph_error_t rv;
+
+ ASSERT(0 <= which_subnode);
+ ASSERT(which_subnode < NUM_SUBNODES);
+
+ rv = hwgraph_info_add_LBL(vhdl, INFO_LBL_CPUBUS, (arbitrary_info_t)which_subnode);
+ ASSERT_ALWAYS(rv == GRAPH_SUCCESS);
+
+ rv = hwgraph_info_export_LBL(vhdl, INFO_LBL_CPUBUS, sizeof(arbitrary_info_t));
+ ASSERT_ALWAYS(rv == GRAPH_SUCCESS);
+}
+
+
+#ifndef CONFIG_IA64_SGI_IO
+/*
+ * Given a device descriptor, extract interrupt target information and
+ * choose an appropriate CPU. Return CPU_NONE if we can't make sense
+ * out of the target information.
+ * TBD: Should this be considered platform-independent code?
+ */
+static cpuid_t
+intr_target_from_desc(device_desc_t dev_desc, int favor_subnode)
+{
+ cpuid_t cpuid = CPU_NONE;
+ cnodeid_t cnodeid;
+ int which_subnode;
+ devfs_handle_t intr_target_dev;
+
+ if ((intr_target_dev = device_desc_intr_target_get(dev_desc)) != GRAPH_VERTEX_NONE) {
+ /*
+ * A valid device was specified. If it's a particular
+ * CPU, then use that CPU as target.
+ */
+ cpuid = cpuvertex_to_cpuid(intr_target_dev);
+ if (cpuid != CPU_NONE)
+ goto cpuchosen;
+
+ /* If a subnode vertex was specified, pick a CPU on that subnode. */
+ if (subnodevertex_to_subnode(intr_target_dev, &cnodeid, &which_subnode) == 0) {
+ cpuid = intr_cpu_choose_from_node(cnodeid, which_subnode);
+ goto cpuchosen;
+ }
+
+ /*
+ * Otherwise, pick a CPU on the node that owns the
+ * specified target. Favor "favor_subnode", if specified.
+ */
+ cnodeid = master_node_get(intr_target_dev);
+ if (cnodeid != CNODEID_NONE) {
+ cpuid = intr_cpu_choose_from_node(cnodeid, favor_subnode);
+ goto cpuchosen;
+ }
+ }
+
+cpuchosen:
+ return(cpuid);
+}
+#endif /* CONFIG_IA64_SGI_IO */
+
+
+#ifndef CONFIG_IA64_SGI_IO
+/*
+ * Check if we had already visited this candidate cnode
+ */
+static void *
+intr_cnode_seen(cnodeid_t candidate,
+ void *arg1,
+ void *arg2)
+{
+ int i;
+ cnodeid_t *visited_cnodes = (cnodeid_t *)arg1;
+ int *num_visited_cnodes = (int *)arg2;
+
+ ASSERT(visited_cnodes);
+ ASSERT(*num_visited_cnodes <= numnodes);
+ for(i = 0 ; i < *num_visited_cnodes; i++) {
+ if (candidate == visited_cnodes[i])
+ return(NULL);
+ }
+ return(visited_cnodes);
+}
+
+#endif /* CONFIG_IA64_SGI_IO */
+
+
+
+/*
+ * intr_bit_reserve_test(cpuid,which_subnode,cnode,req_bit,intr_resflags,
+ * owner_dev,intr_name,*resp_bit)
+ * Either cpuid is not CPU_NONE or cnodeid not CNODE_NONE but
+ * not both.
+ * 1. If cpuid is specified, this routine tests if this cpu can be a valid
+ * interrupt target candidate.
+ * 2. If cnodeid is specified, this routine tests if there is a cpu on
+ * this node which can be a valid interrupt target candidate.
+ * 3. If a valid interrupt target cpu candidate is found then an attempt at
+ * reserving an interrupt bit on the corresponding cnode is made.
+ *
+ * If steps 1 & 2 both fail or step 3 fails then we are not able to get a valid
+ * interrupt target cpu then routine returns CPU_NONE (failure)
+ * Otherwise routine returns cpuid of interrupt target (success)
+ */
+static cpuid_t
+intr_bit_reserve_test(cpuid_t cpuid,
+ int favor_subnode,
+ cnodeid_t cnodeid,
+ int req_bit,
+ int intr_resflags,
+ devfs_handle_t owner_dev,
+ char *intr_name,
+ int *resp_bit)
+{
+
+ ASSERT((cpuid==CPU_NONE) || (cnodeid==CNODEID_NONE));
+
+ if (cnodeid != CNODEID_NONE) {
+ /* Try to choose a interrupt cpu candidate */
+ cpuid = intr_cpu_choose_from_node(cnodeid, favor_subnode);
+ }
+
+ if (cpuid != CPU_NONE) {
+ /* Try to reserve an interrupt bit on the hub
+ * corresponding to the canidate cnode. If we
+ * are successful then we got a cpu which can
+ * act as an interrupt target for the io device.
+ * Otherwise we need to continue the search
+ * further.
+ */
+ *resp_bit = do_intr_reserve_level(cpuid,
+ req_bit,
+ intr_resflags,
+ II_RESERVE,
+ owner_dev,
+ intr_name);
+
+ if (*resp_bit >= 0)
+ /* The interrupt target specified was fine */
+ return(cpuid);
+ }
+ return(CPU_NONE);
+}
+/*
+ * intr_heuristic(dev_t dev,device_desc_t dev_desc,
+ * int req_bit,int intr_resflags,dev_t owner_dev,
+ * char *intr_name,int *resp_bit)
+ *
+ * Choose an interrupt destination for an interrupt.
+ * dev is the device for which the interrupt is being set up
+ * dev_desc is a description of hardware and policy that could
+ * help determine where this interrupt should go
+ * req_bit is the interrupt bit requested
+ * (can be INTRCONNECT_ANY_BIT in which the first available
+ * interrupt bit is used)
+ * intr_resflags indicates whether we want to (un)reserve bit
+ * owner_dev is the owner device
+ * intr_name is the readable interrupt name
+ * resp_bit indicates whether we succeeded in getting the required
+ * action { (un)reservation} done
+ * negative value indicates failure
+ *
+ */
+/* ARGSUSED */
+cpuid_t
+intr_heuristic(devfs_handle_t dev,
+ device_desc_t dev_desc,
+ int req_bit,
+ int intr_resflags,
+ devfs_handle_t owner_dev,
+ char *intr_name,
+ int *resp_bit)
+{
+ cpuid_t cpuid; /* possible intr targ*/
+ cnodeid_t candidate; /* possible canidate */
+#ifndef BRINGUP
+ cnodeid_t visited_cnodes[MAX_NASIDS], /* nodes seen so far */
+ center, /* node we are on */
+ candidate; /* possible canidate */
+ int num_visited_cnodes = 0; /* # nodes seen */
+
+ int radius = 1, /* start looking at the
+ * current node
+ */
+ maxradius = physmem_maxradius();
+ void *rv;
+#endif /* BRINGUP */
+ int which_subnode = SUBNODE_ANY;
+
+#if CONFIG_IA64_SGI_IO /* SN1 + pcibr Addressing Limitation */
+ {
+ devfs_handle_t pconn_vhdl;
+ pcibr_soft_t pcibr_soft;
+
+ /*
+ * This combination of SN1 and Bridge hardware has an odd "limitation".
+ * Due to the choice of addresses for PI0 and PI1 registers on SN1
+ * and historical limitations in Bridge, Bridge is unable to
+ * send interrupts to both PI0 CPUs and PI1 CPUs -- we have
+ * to choose one set or the other. That choice is implicitly
+ * made when Bridge first attaches its error interrupt. After
+ * that point, all subsequent interrupts are restricted to the
+ * same PI number (though it's possible to send interrupts to
+ * the same PI number on a different node).
+ *
+ * Since neither SN1 nor Bridge designers are willing to admit a
+ * bug, we can't really call this a "workaround". It's a permanent
+ * solution for an SN1-specific and Bridge-specific hardware
+ * limitation that won't ever be lifted.
+ */
+ if ((hwgraph_edge_get(dev, EDGE_LBL_PCI, &pconn_vhdl) == GRAPH_SUCCESS) &&
+ ((pcibr_soft = pcibr_soft_get(pconn_vhdl)) != NULL)) {
+ /*
+ * We "know" that the error interrupt is the first
+ * interrupt set up by pcibr_attach. Send all interrupts
+ * on this bridge to the same subnode number.
+ */
+ if (pcibr_soft->bsi_err_intr) {
+ which_subnode = cpuid_to_subnode(((hub_intr_t) pcibr_soft->bsi_err_intr)->i_cpuid);
+ }
+ }
+ }
+#endif /* CONFIG_IA64_SGI_IO */
+
+#ifndef CONFIG_IA64_SGI_IO
+ /*
+ * If an interrupt target was specified for this
+ * interrupt allocation, try to use it.
+ */
+ if (dev_desc) {
+
+ /* Try to see if the interrupt target specified in the
+ * device descriptor is a legal candidate.
+ */
+ cpuid = intr_bit_reserve_test(intr_target_from_desc(dev_desc, which_subnode),
+ which_subnode,
+ CNODEID_NONE,
+ req_bit,
+ intr_resflags,
+ owner_dev,
+ intr_name,
+ resp_bit);
+
+ if (cpuid != CPU_NONE) {
+ if (cpu_on_subnode(cpuid, which_subnode))
+ return(cpuid); /* got a valid interrupt target */
+
+ printk("Override explicit interrupt targetting: %v (0x%x)\n",
+ owner_dev, owner_dev);
+
+ intr_unreserve_level(cpuid, *resp_bit);
+ }
+
+ /* Fall through on to the next step in the search for
+ * the interrupt candidate.
+ */
+
+ }
+#endif /* CONFIG_IA64_SGI_IO */
+
+ /* Check if we can find a valid interrupt target candidate on
+ * the master node for the device.
+ */
+ cpuid = intr_bit_reserve_test(CPU_NONE,
+ which_subnode,
+ master_node_get(dev),
+ req_bit,
+ intr_resflags,
+ owner_dev,
+ intr_name,
+ resp_bit);
+
+ if (cpuid != CPU_NONE) {
+ if (cpu_on_subnode(cpuid, which_subnode))
+ return(cpuid); /* got a valid interrupt target */
+ else
+ intr_unreserve_level(cpuid, *resp_bit);
+ }
+
+ printk("Cannot target interrupts to closest node(%d): %ld (0x%lx)\n",
+ master_node_get(dev),(long) owner_dev, (unsigned long)owner_dev);
+
+ /* Fall through into the default algorithm
+ * (exhaustive-search-for-the-nearest-possible-interrupt-target)
+ * for finding the interrupt target
+ */
+
+#ifndef BRINGUP
+ // Use of this algorithm is deferred until the supporting
+ // code has been implemented.
+ /*
+ * No valid interrupt specification exists.
+ * Try to find a node which is closest to the current node
+ * which can process interrupts from a device
+ */
+
+ center = cpuid_to_cnodeid(smp_processor_id());
+ while (radius <= maxradius) {
+
+ /* Try to find a node at the given radius and which
+ * we haven't seen already.
+ */
+ rv = physmem_select_neighbor_node(center,radius,&candidate,
+ intr_cnode_seen,
+ (void *)visited_cnodes,
+ (void *)&num_visited_cnodes);
+ if (!rv) {
+ /* We have seen all the nodes at this particular radius
+ * Go on to the next radius level.
+ */
+ radius++;
+ continue;
+ }
+ /* We are seeing this candidate cnode for the first time
+ */
+ visited_cnodes[num_visited_cnodes++] = candidate;
+
+ cpuid = intr_bit_reserve_test(CPU_NONE,
+ which_subnode,
+ candidate,
+ req_bit,
+ intr_resflags,
+ owner_dev,
+ intr_name,
+ resp_bit);
+
+ if (cpuid != CPU_NONE) {
+ if (cpu_on_subnode(cpuid, which_subnode))
+ return(cpuid); /* got a valid interrupt target */
+ else
+ intr_unreserve_level(cpuid, *resp_bit);
+ }
+ }
+#else /* BRINGUP */
+ {
+ // Do a stupid round-robin assignment of the node.
+ static cnodeid_t last_node = 0;
+
+ if (last_node > numnodes) last_node = 0;
+ for (candidate = last_node; candidate <= numnodes; candidate++) {
+ cpuid = intr_bit_reserve_test(CPU_NONE,
+ which_subnode,
+ candidate,
+ req_bit,
+ intr_resflags,
+ owner_dev,
+ intr_name,
+ resp_bit);
+
+ if (cpuid != CPU_NONE) {
+ if (cpu_on_subnode(cpuid, which_subnode)) {
+ last_node++;
+ return(cpuid); /* got a valid interrupt target */
+ }
+ else
+ intr_unreserve_level(cpuid, *resp_bit);
+ }
+ last_node++;
+ }
+ }
+#endif
+
+ printk("Cannot target interrupts to any close node: %ld (0x%lx)\n",
+ (long)owner_dev, (unsigned long)owner_dev);
+
+ /* In the worst case try to allocate interrupt bits on the
+ * master processor's node. We may get here during error interrupt
+ * allocation phase when the topology matrix is not yet setup
+ * and hence cannot do an exhaustive search.
+ */
+ ASSERT(cpu_allows_intr(master_procid));
+ cpuid = intr_bit_reserve_test(master_procid,
+ which_subnode,
+ CNODEID_NONE,
+ req_bit,
+ intr_resflags,
+ owner_dev,
+ intr_name,
+ resp_bit);
+
+ if (cpuid != CPU_NONE) {
+ if (cpu_on_subnode(cpuid, which_subnode))
+ return(cpuid);
+ else
+ intr_unreserve_level(cpuid, *resp_bit);
+ }
+
+ printk("Cannot target interrupts: %ld (0x%lx)\n",
+ (long)owner_dev, (unsigned long)owner_dev);
+
+ return(CPU_NONE); /* Should never get here */
+}
+
+
+
+
+#ifndef BRINGUP
+/*
+ * Should never receive an exception while running on the idle
+ * stack. It IS possible to handle *interrupts* while on the
+ * idle stack, but a non-interrupt *exception* is a problem.
+ */
+void
+idle_err(inst_t *epc, uint cause, void *fep, void *sp)
+{
+ eframe_t *ep = (eframe_t *)fep;
+
+ if ((cause & CAUSE_EXCMASK) == EXC_IBE ||
+ (cause & CAUSE_EXCMASK) == EXC_DBE) {
+ (void)dobuserre((eframe_t *)ep, epc, 0);
+ }
+
+ /* XXX - This will have to change to deal with various SN errors. */
+ panic( "exception on IDLE stack "
+ "ep:0x%x epc:0x%x cause:0x%w32x sp:0x%x badvaddr:0x%x",
+ ep, epc, cause, sp, getbadvaddr());
+ /* NOTREACHED */
+}
+
+
+/*
+ * earlynofault - handle very early global faults - usually just while
+ * sizing memory
+ * Returns: 1 if should do nofault
+ * 0 if not
+ */
+/* ARGSUSED */
+int
+earlynofault(eframe_t *ep, uint code)
+{
+ switch(code) {
+ case EXC_DBE:
+ return(1);
+ default:
+ return(0);
+ }
+}
+
+
+
+/* ARGSUSED */
+static void
+cpuintr(void *arg1, void *arg2)
+{
+#if RTE
+ static int rte_intrdebug = 1;
+#endif
+ /*
+ * Frame Scheduler
+ */
+ LOG_TSTAMP_EVENT(RTMON_INTR, TSTAMP_EV_CPUINTR, NULL, NULL,
+ NULL, NULL);
+
+ /*
+ * Hardware clears the IO interrupts, but we need to clear software-
+ * generated interrupts.
+ */
+ LOCAL_HUB_CLR_INTR(CPU_ACTION_A + cputolocalslice(cpuid()));
+
+#if 0
+ /* XXX - Handle error interrupts. */
+ if (error_intr_reason)
+ error_intr();
+#endif /* 0 */
+
+ /*
+ * If we're headed for panicspin and it is due to a NMI, save the
+ * eframe in the NMI area
+ */
+ if (private.p_va_panicspin && nmied) {
+ caddr_t nmi_save_area;
+
+ nmi_save_area = (caddr_t) (TO_UNCAC(TO_NODE(
+ cputonasid(cpuid()), IP27_NMI_EFRAME_OFFSET)) +
+ cputoslice(cpuid()) * IP27_NMI_EFRAME_SIZE);
+ bcopy((caddr_t) arg2, nmi_save_area, sizeof(eframe_t));
+ }
+
+ doacvec();
+#if RTE
+ if (private.p_flags & PDAF_ISOLATED && !rte_intrdebug)
+ goto end_cpuintr;
+#endif
+ doactions();
+#if RTE
+end_cpuintr:
+#endif
+ LOG_TSTAMP_EVENT(RTMON_INTR, TSTAMP_EV_INTREXIT, TSTAMP_EV_CPUINTR, NULL, NULL, NULL);
+}
+
+void
+install_cpuintr(cpuid_t cpu)
+{
+ int intr_bit = CPU_ACTION_A + cputolocalslice(cpu);
+
+ if (intr_connect_level(cpu, intr_bit, INTPEND0_MAXMASK,
+ (intr_func_t) cpuintr, NULL, NULL))
+ panic("install_cpuintr: Can't connect interrupt.");
+}
+#endif /* BRINGUP */
+
+#ifdef DEBUG_INTR_TSTAMP
+/* We allocate an array, but only use element number 64. This guarantees that
+ * the entry is in a cacheline by itself.
+ */
+#define DINTR_CNTIDX 32
+#define DINTR_TSTAMP1 48
+#define DINTR_TSTAMP2 64
+volatile long long dintr_tstamp_cnt[128];
+int dintr_debug_output=0;
+extern void idbg_tstamp_debug(void);
+#ifdef SPLDEBUG
+extern void idbg_splx_log(int);
+#endif
+#if DEBUG_INTR_TSTAMP_DEBUG
+int dintr_enter_symmon=1000; /* 1000 microseconds is 1 millisecond */
+#endif
+
+#ifndef BRINGUP
+/* ARGSUSED */
+static void
+cpulatintr(void *arg)
+{
+ /*
+ * Hardware only clears IO interrupts so we have to clear our level
+ * here.
+ */
+ LOCAL_HUB_CLR_INTR(CPU_INTRLAT_A + cputolocalslice(cpuid()));
+
+#if DEBUG_INTR_TSTAMP_DEBUG
+ dintr_tstamp_cnt[DINTR_TSTAMP2] = GET_LOCAL_RTC;
+ if ((dintr_tstamp_cnt[DINTR_TSTAMP2] - dintr_tstamp_cnt[DINTR_TSTAMP1])
+ > dintr_enter_symmon) {
+#ifdef SPLDEBUG
+ extern int spldebug_log_off;
+
+ spldebug_log_off = 1;
+#endif /* SPLDEBUG */
+ debug("ring");
+#ifdef SPLDEBUG
+ spldebug_log_off = 0;
+#endif /* SPLDEBUG */
+ }
+#endif
+ dintr_tstamp_cnt[DINTR_CNTIDX]++;
+
+ return;
+}
+
+static int install_cpulat_first=0;
+
+void
+install_cpulatintr(cpuid_t cpu)
+{
+ int intr_bit;
+ devfs_handle_t cpuv = cpuid_to_vertex(cpu);
+
+ intr_bit = CPU_INTRLAT_A + cputolocalslice(cpu);
+ if (intr_bit != intr_reserve_level(cpu, intr_bit, II_THREADED,
+ cpuv, "intrlat"))
+ panic( "install_cpulatintr: Can't reserve interrupt.");
+
+ if (intr_connect_level(cpu, intr_bit, INTPEND0_MAXMASK,
+ cpulatintr, NULL, NULL))
+ panic( "install_cpulatintr: Can't connect interrupt.");
+
+ if (!install_cpulat_first) {
+ install_cpulat_first++;
+ idbg_addfunc("tstamp_debug", (void (*)())idbg_tstamp_debug);
+#if defined(SPLDEBUG) || defined(SPLDEBUG_CPU_EVENTS)
+ idbg_addfunc("splx_log", (void (*)())idbg_splx_log);
+#endif /* SPLDEBUG || SPLDEBUG_CPU_EVENTS */
+ }
+}
+#endif /* BRINGUP */
+
+#endif /* DEBUG_INTR_TSTAMP */
+
+#ifndef BRINGUP
+/* ARGSUSED */
+static void
+dbgintr(void *arg)
+{
+ /*
+ * Hardware only clears IO interrupts so we have to clear our level
+ * here.
+ */
+ LOCAL_HUB_CLR_INTR(N_INTPEND_BITS + DEBUG_INTR_A + cputolocalslice(cpuid()));
+
+ debug("zing");
+ return;
+}
+
+
+void
+install_dbgintr(cpuid_t cpu)
+{
+ int intr_bit;
+ devfs_handle_t cpuv = cpuid_to_vertex(cpu);
+
+ intr_bit = N_INTPEND_BITS + DEBUG_INTR_A + cputolocalslice(cpu);
+ if (intr_bit != intr_reserve_level(cpu, intr_bit, 1, cpuv, "DEBUG"))
+ panic("install_dbgintr: Can't reserve interrupt. "
+ " intr_bit %d" ,intr_bit);
+
+ if (intr_connect_level(cpu, intr_bit, INTPEND1_MAXMASK,
+ dbgintr, NULL, NULL))
+ panic("install_dbgintr: Can't connect interrupt.");
+
+#ifdef DEBUG_INTR_TSTAMP
+ /* Set up my interrupt latency test interrupt */
+ install_cpulatintr(cpu);
+#endif
+}
+
+/* ARGSUSED */
+static void
+tlbintr(void *arg)
+{
+ extern void tlbflush_rand(void);
+
+ /*
+ * Hardware only clears IO interrupts so we have to clear our level
+ * here.
+ */
+ LOCAL_HUB_CLR_INTR(N_INTPEND_BITS + TLB_INTR_A + cputolocalslice(cpuid()));
+
+ tlbflush_rand();
+ return;
+}
+
+
+void
+install_tlbintr(cpuid_t cpu)
+{
+ int intr_bit;
+ devfs_handle_t cpuv = cpuid_to_vertex(cpu);
+
+ intr_bit = N_INTPEND_BITS + TLB_INTR_A + cputolocalslice(cpu);
+ if (intr_bit != intr_reserve_level(cpu, intr_bit, 1, cpuv, "DEBUG"))
+ panic("install_tlbintr: Can't reserve interrupt. "
+ " intr_bit %d" ,intr_bit);
+
+ if (intr_connect_level(cpu, intr_bit, INTPEND1_MAXMASK,
+ tlbintr, NULL, NULL))
+ panic("install_tlbintr: Can't connect interrupt.");
+
+}
+
+
+/*
+ * Send an interrupt to all nodes. Don't panic if we get an error.
+ * Returns 1 if any exceptions occurred.
+ */
+int
+protected_broadcast(hubreg_t intrbit)
+{
+ nodepda_t *npdap = private.p_nodepda;
+ int byte, bit, sn;
+ int error = 0;
+
+ extern int _wbadaddr_val(volatile void *, int, volatile int *);
+
+ /* Send rather than clear an interrupt. */
+ intrbit |= 0x100;
+
+ for (byte = 0; byte < NASID_MASK_BYTES; byte++) {
+ for (bit = 0; bit < 8; bit++) {
+ if (npdap->nasid_mask[byte] & (1 << bit)) {
+ nasid_t nasid = byte * 8 + bit;
+ for (sn=0; sn<NUM_SUBNODES; sn++) {
+ error += _wbadaddr_val(REMOTE_HUB_PI_ADDR(nasid,
+ sn, PI_INT_PEND_MOD),
+ sizeof(hubreg_t),
+ (volatile int *)&intrbit);
+ }
+ }
+ }
+ }
+
+ return error;
+}
+
+
+/*
+ * Poll the interrupt register to see if another cpu has asked us
+ * to drop into the debugger (without lowering spl).
+ */
+void
+chkdebug(void)
+{
+ if (LOCAL_HUB_L(PI_INT_PEND1) & (1L << (DEBUG_INTR_A + cputolocalslice(cpuid()))))
+ dbgintr((void *)NULL);
+}
+
+
+/*
+ * Install special graphics interrupt.
+ */
+void
+install_gfxintr(cpuid_t cpu, ilvl_t swlevel, intr_func_t intr_func, void *intr_arg)
+{
+ int intr_bit = GFX_INTR_A + cputolocalslice(cpu);
+
+ if (intr_connect_level(cpu, intr_bit, swlevel,
+ intr_func, intr_arg, NULL))
+ panic("install_gfxintr: Can't connect interrupt.");
+}
+
+
+/*
+ * Install page migration interrupt handler.
+ */
+void
+hub_migrintr_init(cnodeid_t cnode)
+{
+ cpuid_t cpu = cnodetocpu(cnode);
+ int intr_bit = INT_PEND0_BASELVL + PG_MIG_INTR;
+
+ if (numnodes == 1){
+ /*
+ * No migration with just one node..
+ */
+ return;
+ }
+
+ if (cpu != -1) {
+ if (intr_connect_level(cpu, intr_bit, 0,
+ (intr_func_t) migr_intr_handler, 0, (intr_func_t) migr_intr_prologue_handler))
+ panic( "hub_migrintr_init: Can't connect interrupt.");
+ }
+}
+
+
+/*
+ * Cause all CPUs to stop by sending them each a DEBUG interrupt.
+ * Parameter is actually a (cpumask_t *).
+ */
+void
+debug_stop_all_cpus(void *stoplist)
+{
+ int cpu;
+ ulong level;
+
+ for (cpu=0; cpu<maxcpus; cpu++) {
+ if (cpu == cpuid())
+ continue;
+ if (!cpu_enabled(cpu))
+ continue;
+ /* "-1" is the old style parameter OR could be the new style
+ * if no-one is currently stopped. We only stop the
+ * requested cpus, the others are already stopped (probably
+ * at a breakpoint).
+ */
+
+ if (((cpumask_t *)stoplist != (cpumask_t *)-1LL) &&
+ (!CPUMASK_TSTB(*(cpumask_t*)stoplist, cpu)))
+ continue;
+
+ /*
+ * CPU lslice A gets level DEBUG_INTR_A
+ * CPU lslice B gets level DEBUG_INTR_B
+ */
+ level = DEBUG_INTR_A + LOCALCPU(get_cpu_slice(cpu));
+ /*
+ * Convert the compact hub number to the NASID to get the
+ * correct part of the address space. Then set the interrupt
+ * bit associated with the CPU we want to send the interrupt
+ * to.
+ */
+ REMOTE_CPU_SEND_INTR(cpu, N_INTPEND_BITS + level);
+
+ }
+}
+
+
+struct hardwired_intr_s {
+ signed char level;
+ int flags;
+ char *name;
+} const hardwired_intr[] = {
+ { INT_PEND0_BASELVL + RESERVED_INTR, 0, "Reserved" },
+ { INT_PEND0_BASELVL + GFX_INTR_A, 0, "Gfx A" },
+ { INT_PEND0_BASELVL + GFX_INTR_B, 0, "Gfx B" },
+ { INT_PEND0_BASELVL + PG_MIG_INTR, II_THREADED, "Migration" },
+#if defined(SN1) && !defined(DIRECT_L1_CONSOLE)
+ { INT_PEND0_BASELVL + UART_INTR, II_THREADED, "Bedrock/L1" },
+#else
+ { INT_PEND0_BASELVL + UART_INTR, 0, "Hub I2C" },
+#endif
+ { INT_PEND0_BASELVL + CC_PEND_A, 0, "Crosscall A" },
+ { INT_PEND0_BASELVL + CC_PEND_B, 0, "Crosscall B" },
+ { INT_PEND0_BASELVL + MSC_MESG_INTR, II_THREADED, "MSC Message" },
+ { INT_PEND0_BASELVL + CPU_ACTION_A, 0, "CPU Action A" },
+ { INT_PEND0_BASELVL + CPU_ACTION_B, 0, "CPU Action B" },
+ { INT_PEND1_BASELVL + IO_ERROR_INTR, II_ERRORINT, "IO Error" },
+ { INT_PEND1_BASELVL + CLK_ERR_INTR, II_ERRORINT, "Clock Error" },
+ { INT_PEND1_BASELVL + COR_ERR_INTR_A, II_ERRORINT, "Correctable Error A" },
+ { INT_PEND1_BASELVL + COR_ERR_INTR_B, II_ERRORINT, "Correctable Error B" },
+ { INT_PEND1_BASELVL + MD_COR_ERR_INTR, II_ERRORINT, "MD Correct. Error" },
+ { INT_PEND1_BASELVL + NI_ERROR_INTR, II_ERRORINT, "NI Error" },
+ { INT_PEND1_BASELVL + NI_BRDCAST_ERR_A, II_ERRORINT, "Remote NI Error"},
+ { INT_PEND1_BASELVL + NI_BRDCAST_ERR_B, II_ERRORINT, "Remote NI Error"},
+ { INT_PEND1_BASELVL + MSC_PANIC_INTR, II_ERRORINT, "MSC Panic" },
+ { INT_PEND1_BASELVL + LLP_PFAIL_INTR_A, II_ERRORINT, "LLP Pfail WAR" },
+ { INT_PEND1_BASELVL + LLP_PFAIL_INTR_B, II_ERRORINT, "LLP Pfail WAR" },
+#ifdef SN1
+ { INT_PEND1_BASELVL + NACK_INT_A, 0, "CPU A Nack count == NACK_CMP" },
+ { INT_PEND1_BASELVL + NACK_INT_B, 0, "CPU B Nack count == NACK_CMP" },
+ { INT_PEND1_BASELVL + LB_ERROR, 0, "Local Block Error" },
+ { INT_PEND1_BASELVL + XB_ERROR, 0, "Local XBar Error" },
+#endif /* SN1 */
+ { -1, 0, (char *)NULL}
+};
+
+/*
+ * Reserve all of the hardwired interrupt levels so they're not used as
+ * general purpose bits later.
+ */
+void
+intr_reserve_hardwired(cnodeid_t cnode)
+{
+ cpuid_t cpu;
+ int level;
+ int i;
+ char subnode_done[NUM_SUBNODES];
+
+ cpu = cnodetocpu(cnode);
+ if (cpu == CPU_NONE) {
+ printk("Node %d has no CPUs", cnode);
+ return;
+ }
+
+ for (i=0; i<NUM_SUBNODES; i++)
+ subnode_done[i] = 0;
+
+ for (; cpu<maxcpus && cpu_enabled(cpu) && cputocnode(cpu) == cnode; cpu++) {
+ int which_subnode = cpuid_to_subnode(cpu);
+ if (subnode_done[which_subnode])
+ continue;
+ subnode_done[which_subnode] = 1;
+
+ for (i = 0; hardwired_intr[i].level != -1; i++) {
+ level = hardwired_intr[i].level;
+
+ if (level != intr_reserve_level(cpu, level,
+ hardwired_intr[i].flags,
+ (devfs_handle_t) NULL,
+ hardwired_intr[i].name))
+ panic("intr_reserve_hardwired: Can't reserve level %d.", level);
+ }
+ }
+}
+
+#endif /* BRINGUP */
+
+/*
+ * Check and clear interrupts.
+ */
+/*ARGSUSED*/
+static void
+intr_clear_bits(nasid_t nasid, volatile hubreg_t *pend, int base_level,
+ char *name)
+{
+ volatile hubreg_t bits;
+ int i;
+
+ /* Check pending interrupts */
+ if ((bits = HUB_L(pend)) != 0) {
+ for (i = 0; i < N_INTPEND_BITS; i++) {
+ if (bits & (1 << i)) {
+#ifdef INTRDEBUG
+ printk( "Nasid %d interrupt bit %d set in %s",
+ nasid, i, name);
+#endif
+ LOCAL_HUB_CLR_INTR(base_level + i);
+ }
+ }
+ }
+}
+
+/*
+ * Clear out our interrupt registers.
+ */
+void
+intr_clear_all(nasid_t nasid)
+{
+ int sn;
+
+ for(sn=0; sn<NUM_SUBNODES; sn++) {
+ REMOTE_HUB_PI_S(nasid, sn, PI_INT_MASK0_A, 0);
+ REMOTE_HUB_PI_S(nasid, sn, PI_INT_MASK0_B, 0);
+ REMOTE_HUB_PI_S(nasid, sn, PI_INT_MASK1_A, 0);
+ REMOTE_HUB_PI_S(nasid, sn, PI_INT_MASK1_B, 0);
+
+ intr_clear_bits(nasid, REMOTE_HUB_PI_ADDR(nasid, sn, PI_INT_PEND0),
+ INT_PEND0_BASELVL, "INT_PEND0");
+ intr_clear_bits(nasid, REMOTE_HUB_PI_ADDR(nasid, sn, PI_INT_PEND1),
+ INT_PEND1_BASELVL, "INT_PEND1");
+ }
+}
+
+/*
+ * Dump information about a particular interrupt vector.
+ */
+static void
+dump_vector(intr_info_t *info, intr_vector_t *vector, int bit, hubreg_t ip,
+ hubreg_t ima, hubreg_t imb, void (*pf)(char *, ...))
+{
+ hubreg_t value = 1LL << bit;
+
+ pf(" Bit %02d: %s: func 0x%x arg 0x%x prefunc 0x%x\n",
+ bit, info->ii_name,
+ vector->iv_func, vector->iv_arg, vector->iv_prefunc);
+ pf(" vertex 0x%x %s%s",
+ info->ii_owner_dev,
+ ((info->ii_flags) & II_RESERVE) ? "R" : "U",
+ ((info->ii_flags) & II_INUSE) ? "C" : "-");
+ pf("%s%s%s%s",
+ ip & value ? "P" : "-",
+ ima & value ? "A" : "-",
+ imb & value ? "B" : "-",
+ ((info->ii_flags) & II_ERRORINT) ? "E" : "-");
+ pf("\n");
+}
+
+
+/*
+ * Dump information about interrupt vector assignment.
+ */
+void
+intr_dumpvec(cnodeid_t cnode, void (*pf)(char *, ...))
+{
+ nodepda_t *npda;
+ int ip, sn, bit;
+ intr_vecblk_t *dispatch;
+ hubreg_t ipr, ima, imb;
+ nasid_t nasid;
+
+ if ((cnode < 0) || (cnode >= numnodes)) {
+ pf("intr_dumpvec: cnodeid out of range: %d\n", cnode);
+ return ;
+ }
+
+ nasid = COMPACT_TO_NASID_NODEID(cnode);
+
+ if (nasid == INVALID_NASID) {
+ pf("intr_dumpvec: Bad cnodeid: %d\n", cnode);
+ return ;
+ }
+
+
+ npda = NODEPDA(cnode);
+
+ for (sn = 0; sn < NUM_SUBNODES; sn++) {
+ for (ip = 0; ip < 2; ip++) {
+ dispatch = ip ? &(SNPDA(npda,sn)->intr_dispatch1) : &(SNPDA(npda,sn)->intr_dispatch0);
+ ipr = REMOTE_HUB_PI_L(nasid, sn, ip ? PI_INT_PEND1 : PI_INT_PEND0);
+ ima = REMOTE_HUB_PI_L(nasid, sn, ip ? PI_INT_MASK1_A : PI_INT_MASK0_A);
+ imb = REMOTE_HUB_PI_L(nasid, sn, ip ? PI_INT_MASK1_B : PI_INT_MASK0_B);
+
+ pf("Node %d INT_PEND%d:\n", cnode, ip);
+
+ if (dispatch->ithreads_enabled)
+ pf(" Ithreads enabled\n");
+ else
+ pf(" Ithreads disabled\n");
+ pf(" vector_count = %d, vector_state = %d\n",
+ dispatch->vector_count,
+ dispatch->vector_state);
+ pf(" CPU A count %d, CPU B count %d\n",
+ dispatch->cpu_count[0],
+ dispatch->cpu_count[1]);
+ pf(" &vector_lock = 0x%x\n",
+ &(dispatch->vector_lock));
+ for (bit = 0; bit < N_INTPEND_BITS; bit++) {
+ if ((dispatch->info[bit].ii_flags & II_RESERVE) ||
+ (ipr & (1L << bit))) {
+ dump_vector(&(dispatch->info[bit]),
+ &(dispatch->vectors[bit]),
+ bit, ipr, ima, imb, pf);
+ }
+ }
+ pf("\n");
+ }
+ }
+}
+
diff --git a/arch/ia64/sn/io/ml_iograph.c b/arch/ia64/sn/io/ml_iograph.c
new file mode 100644
index 000000000..a43fe74cc
--- /dev/null
+++ b/arch/ia64/sn/io/ml_iograph.c
@@ -0,0 +1,1582 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/hcl_util.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/xtalk/xbow.h>
+#include <asm/sn/pci/bridge.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/eeprom.h>
+#include <asm/sn/sn_private.h>
+#include <asm/sn/pci/pcibr.h>
+#include <asm/sn/xtalk/xtalk.h>
+#include <asm/sn/xtalk/xswitch.h>
+#include <asm/sn/xtalk/xwidget.h>
+#include <asm/sn/xtalk/xtalk_private.h>
+#include <asm/sn/xtalk/xtalkaddrs.h>
+
+extern int maxnodes;
+
+/* #define PROBE_TEST */
+
+/* At most 2 hubs can be connected to an xswitch */
+#define NUM_XSWITCH_VOLUNTEER 2
+
+/*
+ * Track which hubs have volunteered to manage devices hanging off of
+ * a Crosstalk Switch (e.g. xbow). This structure is allocated,
+ * initialized, and hung off the xswitch vertex early on when the
+ * xswitch vertex is created.
+ */
+typedef struct xswitch_vol_s {
+ struct semaphore xswitch_volunteer_mutex;
+ int xswitch_volunteer_count;
+ devfs_handle_t xswitch_volunteer[NUM_XSWITCH_VOLUNTEER];
+} *xswitch_vol_t;
+
+void
+xswitch_vertex_init(devfs_handle_t xswitch)
+{
+ xswitch_vol_t xvolinfo;
+ int rc;
+
+ xvolinfo = kmalloc(sizeof(struct xswitch_vol_s), GFP_KERNEL);
+ init_MUTEX(&xvolinfo->xswitch_volunteer_mutex);
+ xvolinfo->xswitch_volunteer_count = 0;
+ rc = hwgraph_info_add_LBL(xswitch,
+ INFO_LBL_XSWITCH_VOL,
+ (arbitrary_info_t)xvolinfo);
+ ASSERT(rc == GRAPH_SUCCESS); rc = rc;
+}
+
+
+/*
+ * When assignment of hubs to widgets is complete, we no longer need the
+ * xswitch volunteer structure hanging around. Destroy it.
+ */
+static void
+xswitch_volunteer_delete(devfs_handle_t xswitch)
+{
+ xswitch_vol_t xvolinfo;
+ int rc;
+
+ rc = hwgraph_info_remove_LBL(xswitch,
+ INFO_LBL_XSWITCH_VOL,
+ (arbitrary_info_t *)&xvolinfo);
+#ifndef CONFIG_IA64_SGI_IO
+ ASSERT(rc == GRAPH_SUCCESS); rc = rc;
+#endif
+
+ kfree(xvolinfo);
+}
+/*
+ * A Crosstalk master volunteers to manage xwidgets on the specified xswitch.
+ */
+/* ARGSUSED */
+static void
+volunteer_for_widgets(devfs_handle_t xswitch, devfs_handle_t master)
+{
+ xswitch_vol_t xvolinfo = NULL;
+
+ (void)hwgraph_info_get_LBL(xswitch,
+ INFO_LBL_XSWITCH_VOL,
+ (arbitrary_info_t *)&xvolinfo);
+ if (xvolinfo == NULL) {
+#ifndef CONFIG_IA64_SGI_IO
+ if (!is_headless_node_vertex(master))
+ cmn_err(CE_WARN,
+ "volunteer for widgets: vertex %v has no info label",
+ xswitch);
+#endif
+ return;
+ }
+
+#ifndef CONFIG_IA64_SGI_IO
+ mutex_lock(&xvolinfo->xswitch_volunteer_mutex, PZERO);
+#endif
+ ASSERT(xvolinfo->xswitch_volunteer_count < NUM_XSWITCH_VOLUNTEER);
+ xvolinfo->xswitch_volunteer[xvolinfo->xswitch_volunteer_count] = master;
+ xvolinfo->xswitch_volunteer_count++;
+#ifndef CONFIG_IA64_SGI_IO
+ mutex_unlock(&xvolinfo->xswitch_volunteer_mutex);
+#endif
+}
+
+#ifndef BRINGUP
+/*
+ * The "ideal fixed assignment" of 12 IO slots to 4 node slots.
+ * At index N is the node slot number of the node board that should
+ * ideally control the widget in IO slot N. Note that if there is
+ * only one node board on a given xbow, it will control all of the
+ * devices on that xbow regardless of these defaults.
+ *
+ * N1 controls IO slots IO1, IO3, IO5 (upper left)
+ * N3 controls IO slots IO2, IO4, IO6 (upper right)
+ * N2 controls IO slots IO7, IO9, IO11 (lower left)
+ * N4 controls IO slots IO8, IO10, IO12 (lower right)
+ *
+ * This makes assignments predictable and easily controllable.
+ * TBD: Allow administrator to override these defaults.
+ */
+static slotid_t ideal_assignment[] = {
+ -1, /* IO0 -->non-existent */
+ 1, /* IO1 -->N1 */
+ 3, /* IO2 -->N3 */
+ 1, /* IO3 -->N1 */
+ 3, /* IO4 -->N3 */
+ 1, /* IO5 -->N1 */
+ 3, /* IO6 -->N3 */
+ 2, /* IO7 -->N2 */
+ 4, /* IO8 -->N4 */
+ 2, /* IO9 -->N2 */
+ 4, /* IO10-->N4 */
+ 2, /* IO11-->N2 */
+ 4 /* IO12-->N4 */
+};
+
+static int
+is_ideal_assignment(slotid_t hubslot, slotid_t ioslot)
+{
+ return(ideal_assignment[ioslot] == hubslot);
+}
+#endif /* ifndef BRINGUP */
+
+extern int xbow_port_io_enabled(nasid_t nasid, int widgetnum);
+
+/*
+ * Assign all the xwidgets hanging off the specified xswitch to the
+ * Crosstalk masters that have volunteered for xswitch duty.
+ */
+/* ARGSUSED */
+static void
+assign_widgets_to_volunteers(devfs_handle_t xswitch, devfs_handle_t hubv)
+{
+ xswitch_info_t xswitch_info;
+ xswitch_vol_t xvolinfo = NULL;
+ xwidgetnum_t widgetnum;
+ int curr_volunteer, num_volunteer;
+ nasid_t nasid;
+ hubinfo_t hubinfo;
+#ifndef BRINGUP
+ int xbownum;
+#endif
+
+ hubinfo_get(hubv, &hubinfo);
+ nasid = hubinfo->h_nasid;
+
+ xswitch_info = xswitch_info_get(xswitch);
+ ASSERT(xswitch_info != NULL);
+
+ (void)hwgraph_info_get_LBL(xswitch,
+ INFO_LBL_XSWITCH_VOL,
+ (arbitrary_info_t *)&xvolinfo);
+ if (xvolinfo == NULL) {
+#ifndef CONFIG_IA64_SGI_IO
+ if (!is_headless_node_vertex(hubv))
+ cmn_err(CE_WARN,
+ "assign_widgets_to_volunteers:vertex %v has "
+ " no info label",
+ xswitch);
+#endif
+ return;
+ }
+
+ num_volunteer = xvolinfo->xswitch_volunteer_count;
+ ASSERT(num_volunteer > 0);
+ curr_volunteer = 0;
+
+ /* Assign master hub for xswitch itself. */
+ if (HUB_WIDGET_ID_MIN > 0) {
+ hubv = xvolinfo->xswitch_volunteer[0];
+ xswitch_info_master_assignment_set(xswitch_info, (xwidgetnum_t)0, hubv);
+ }
+
+#ifndef BRINGUP
+ xbownum = get_node_crossbow(nasid);
+#endif /* ifndef BRINGUP */
+
+ /*
+ * TBD: Use administrative information to alter assignment of
+ * widgets to hubs.
+ */
+ for (widgetnum=HUB_WIDGET_ID_MIN; widgetnum <= HUB_WIDGET_ID_MAX; widgetnum++) {
+
+#ifndef BRINGUP
+ int i;
+#endif
+ /*
+ * Ignore disabled/empty ports.
+ */
+ if (!xbow_port_io_enabled(nasid, widgetnum))
+ continue;
+
+ /*
+ * If this is the master IO board, assign it to the same
+ * hub that owned it in the prom.
+ */
+ if (is_master_nasid_widget(nasid, widgetnum)) {
+ int i;
+
+ for (i=0; i<num_volunteer; i++) {
+ hubv = xvolinfo->xswitch_volunteer[i];
+ hubinfo_get(hubv, &hubinfo);
+ nasid = hubinfo->h_nasid;
+ if (nasid == get_console_nasid())
+ goto do_assignment;
+ }
+#ifndef CONFIG_IA64_SGI_IO
+ cmn_err(CE_PANIC,
+ "Nasid == %d, console nasid == %d",
+ nasid, get_console_nasid());
+#endif
+ }
+
+#ifndef BRINGUP
+ /*
+ * Try to do the "ideal" assignment if IO slots to nodes.
+ */
+ for (i=0; i<num_volunteer; i++) {
+ hubv = xvolinfo->xswitch_volunteer[i];
+ hubinfo_get(hubv, &hubinfo);
+ nasid = hubinfo->h_nasid;
+ if (is_ideal_assignment(SLOTNUM_GETSLOT(get_node_slotid(nasid)),
+ SLOTNUM_GETSLOT(get_widget_slotnum(xbownum, widgetnum)))) {
+
+ goto do_assignment;
+
+ }
+ }
+#endif /* ifndef BRINGUP */
+
+ /*
+ * Do a round-robin assignment among the volunteer nodes.
+ */
+ hubv = xvolinfo->xswitch_volunteer[curr_volunteer];
+ curr_volunteer = (curr_volunteer + 1) % num_volunteer;
+ /* fall through */
+
+do_assignment:
+ /*
+ * At this point, we want to make hubv the master of widgetnum.
+ */
+ xswitch_info_master_assignment_set(xswitch_info, widgetnum, hubv);
+ }
+
+ xswitch_volunteer_delete(xswitch);
+}
+
+/*
+ * Early iograph initialization. Called by master CPU in mlreset().
+ * Useful for including iograph.o in kernel.o.
+ */
+void
+iograph_early_init(void)
+{
+/*
+ * Need new way to get this information ..
+ */
+ cnodeid_t cnode;
+ nasid_t nasid;
+ lboard_t *board;
+
+ /*
+ * Init. the board-to-hwgraph link early, so FRU analyzer
+ * doesn't trip on leftover values if we panic early on.
+ */
+ for(cnode = 0; cnode < numnodes; cnode++) {
+ nasid = COMPACT_TO_NASID_NODEID(cnode);
+ board = (lboard_t *)KL_CONFIG_INFO(nasid);
+ printk("iograph_early_init: Found board 0x%p\n", board);
+
+ /* Check out all the board info stored on a node */
+ while(board) {
+ board->brd_graph_link = GRAPH_VERTEX_NONE;
+ board = KLCF_NEXT(board);
+ printk("iograph_early_init: Found board 0x%p\n", board);
+
+
+ }
+ }
+
+ hubio_init();
+}
+
+#ifndef CONFIG_IA64_SGI_IO
+/* There is an identical definition of this in os/scheduler/runq.c */
+#define INIT_COOKIE(cookie) cookie.must_run = 0; cookie.cpu = PDA_RUNANYWHERE
+/*
+ * These functions absolutely doesn't belong here. It's here, though,
+ * until the scheduler provides a platform-independent version
+ * that works the way it should. The interface will definitely change,
+ * too. Currently used only in this file and by io/cdl.c in order to
+ * bind various I/O threads to a CPU on the proper node.
+ */
+cpu_cookie_t
+setnoderun(cnodeid_t cnodeid)
+{
+ int i;
+ cpuid_t cpunum;
+ cpu_cookie_t cookie;
+
+ INIT_COOKIE(cookie);
+ if (cnodeid == CNODEID_NONE)
+ return(cookie);
+
+ /*
+ * Do a setmustrun to one of the CPUs on the specified
+ * node.
+ */
+ if ((cpunum = CNODE_TO_CPU_BASE(cnodeid)) == CPU_NONE) {
+ return(cookie);
+ }
+
+ cpunum += CNODE_NUM_CPUS(cnodeid) - 1;
+
+ for (i = 0; i < CNODE_NUM_CPUS(cnodeid); i++, cpunum--) {
+
+ if (cpu_enabled(cpunum)) {
+ cookie = setmustrun(cpunum);
+ break;
+ }
+ }
+
+ return(cookie);
+}
+
+void
+restorenoderun(cpu_cookie_t cookie)
+{
+ restoremustrun(cookie);
+}
+static sema_t io_init_sema;
+
+#endif /* !CONFIG_IA64_SGI_IO */
+
+struct semaphore io_init_sema;
+
+
+/*
+ * Let boot processor know that we're done initializing our node's IO
+ * and then exit.
+ */
+/* ARGSUSED */
+static void
+io_init_done(cnodeid_t cnodeid,cpu_cookie_t c)
+{
+#ifndef CONFIG_IA64_SGI_IO
+ /* Let boot processor know that we're done. */
+ up(&io_init_sema);
+ /* This is for the setnoderun done when the io_init thread
+ * started
+ */
+ restorenoderun(c);
+ sthread_exit();
+#endif
+}
+
+/*
+ * Probe to see if this hub's xtalk link is active. If so,
+ * return the Crosstalk Identification of the widget that we talk to.
+ * This is called before any of the Crosstalk infrastructure for
+ * this hub is set up. It's usually called on the node that we're
+ * probing, but not always.
+ *
+ * TBD: Prom code should actually do this work, and pass through
+ * hwid for our use.
+ */
+static void
+early_probe_for_widget(devfs_handle_t hubv, xwidget_hwid_t hwid)
+{
+ hubreg_t llp_csr_reg;
+ nasid_t nasid;
+ hubinfo_t hubinfo;
+
+ hubinfo_get(hubv, &hubinfo);
+ nasid = hubinfo->h_nasid;
+
+ llp_csr_reg = REMOTE_HUB_L(nasid, IIO_LLP_CSR);
+ /*
+ * If link is up, read the widget's part number.
+ * A direct connect widget must respond to widgetnum=0.
+ */
+ if (llp_csr_reg & IIO_LLP_CSR_IS_UP) {
+ /* TBD: Put hub into "indirect" mode */
+ /*
+ * We're able to read from a widget because our hub's
+ * WIDGET_ID was set up earlier.
+ */
+#ifdef BRINGUP
+ widgetreg_t widget_id = *(volatile widgetreg_t *)
+ (RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID);
+
+ printk("early_probe_for_widget: Hub Vertex 0x%p is UP widget_id = 0x%x Register 0x%p\n", hubv, widget_id,
+ (volatile widgetreg_t *)(RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID) );
+
+#else /* !BRINGUP */
+ widgetreg_t widget_id = XWIDGET_ID_READ(nasid, 0);
+#endif /* BRINGUP */
+
+ hwid->part_num = XWIDGET_PART_NUM(widget_id);
+ hwid->rev_num = XWIDGET_REV_NUM(widget_id);
+ hwid->mfg_num = XWIDGET_MFG_NUM(widget_id);
+
+ /* TBD: link reset */
+ } else {
+
+ panic("\n\n**** early_probe_for_widget: Hub Vertex 0x%p is DOWN llp_csr_reg 0x%x ****\n\n", hubv, llp_csr_reg);
+
+ hwid->part_num = XWIDGET_PART_NUM_NONE;
+ hwid->rev_num = XWIDGET_REV_NUM_NONE;
+ hwid->mfg_num = XWIDGET_MFG_NUM_NONE;
+ }
+
+}
+
+/* Add inventory information to the widget vertex
+ * Right now (module,slot,revision) is being
+ * added as inventory information.
+ */
+static void
+xwidget_inventory_add(devfs_handle_t widgetv,
+ lboard_t *board,
+ struct xwidget_hwid_s hwid)
+{
+ if (!board)
+ return;
+ /* Donot add inventory information for the baseio
+ * on a speedo with an xbox. It has already been
+ * taken care of in SN00_vmc.
+ * Speedo with xbox's baseio comes in at slot io1 (widget 9)
+ */
+ device_inventory_add(widgetv,INV_IOBD,board->brd_type,
+ board->brd_module,
+ SLOTNUM_GETSLOT(board->brd_slot),
+ hwid.rev_num);
+}
+
+/*
+ * io_xswitch_widget_init
+ *
+ */
+
+/* defined in include/linux/ctype.h */
+/* #define toupper(c) (islower(c) ? (c) - 'a' + 'A' : (c)) */
+
+void
+io_xswitch_widget_init(devfs_handle_t xswitchv,
+ devfs_handle_t hubv,
+ xwidgetnum_t widgetnum,
+ async_attach_t aa)
+{
+ xswitch_info_t xswitch_info;
+ xwidgetnum_t hub_widgetid;
+ devfs_handle_t widgetv;
+ cnodeid_t cnode;
+ widgetreg_t widget_id;
+ nasid_t nasid, peer_nasid;
+ struct xwidget_hwid_s hwid;
+ hubinfo_t hubinfo;
+ /*REFERENCED*/
+ int rc;
+ char slotname[SLOTNUM_MAXLENGTH];
+ char pathname[128];
+ char new_name[64];
+ moduleid_t module;
+ slotid_t slot;
+ lboard_t *board = NULL;
+
+ printk("\nio_xswitch_widget_init: hubv 0x%p, xswitchv 0x%p, widgetnum 0x%x\n", hubv, xswitchv, widgetnum);
+ /*
+ * Verify that xswitchv is indeed an attached xswitch.
+ */
+ xswitch_info = xswitch_info_get(xswitchv);
+ ASSERT(xswitch_info != NULL);
+
+ hubinfo_get(hubv, &hubinfo);
+ nasid = hubinfo->h_nasid;
+ cnode = NASID_TO_COMPACT_NODEID(nasid);
+ hub_widgetid = hubinfo->h_widgetid;
+
+
+ /* Who's the other guy on out crossbow (if anyone) */
+ peer_nasid = NODEPDA(cnode)->xbow_peer;
+ if (peer_nasid == INVALID_NASID)
+ /* If I don't have a peer, use myself. */
+ peer_nasid = nasid;
+
+
+ /* Check my xbow structure and my peer's */
+ if (!xbow_port_io_enabled(nasid, widgetnum) &&
+ !xbow_port_io_enabled(peer_nasid, widgetnum)) {
+ return;
+ }
+
+ if (xswitch_info_link_ok(xswitch_info, widgetnum)) {
+ char name[4];
+ /*
+ * If the current hub is not supposed to be the master
+ * for this widgetnum, then skip this widget.
+ */
+ if (xswitch_info_master_assignment_get(xswitch_info,
+ widgetnum) != hubv) {
+ return;
+ }
+
+ module = NODEPDA(cnode)->module_id;
+#ifdef XBRIDGE_REGS_SIM
+ /* hardwire for now...could do this with something like:
+ * xbow_soft_t soft = hwgraph_fastinfo_get(vhdl);
+ * xbow_t xbow = soft->base;
+ * xbowreg_t xwidget_id = xbow->xb_wid_id;
+ * but I don't feel like figuring out vhdl right now..
+ * and I know for a fact the answer is 0x2d000049
+ */
+ printk("io_xswitch_widget_init: XBRIDGE_REGS_SIM FIXME: reading xwidget id: hardwired to xbridge (0x2d000049).\n");
+ printk("XWIDGET_PART_NUM(0x2d000049)= 0x%x\n", XWIDGET_PART_NUM(0x2d000049));
+ if (XWIDGET_PART_NUM(0x2d000049)==XXBOW_WIDGET_PART_NUM) {
+#else
+ if (nasid_has_xbridge(nasid)) {
+#endif /* XBRIDGE_REGS_SIM */
+ board = find_lboard_module_class(
+ (lboard_t *)KL_CONFIG_INFO(nasid),
+ module,
+ KLTYPE_IOBRICK);
+
+ if (board)
+ printk("io_xswitch_widget_init: Found KLTYPE_IOBRICK Board 0x%p brd_type 0x%x\n", board, board->brd_type);
+
+ /*
+ * BRINGUP
+ * Make sure we really want to say xbrick, pbrick,
+ * etc. rather than XIO, graphics, etc.
+ */
+
+#ifdef SUPPORT_PRINTING_M_FORMAT
+ sprintf(pathname, EDGE_LBL_MODULE "/%M/"
+#else
+ sprintf(pathname, EDGE_LBL_MODULE "/%x/"
+#endif
+ "%cbrick" "/%s/%d",
+ NODEPDA(cnode)->module_id,
+#ifdef BRINGUP
+
+ (board->brd_type == KLTYPE_IBRICK) ? 'I' :
+ (board->brd_type == KLTYPE_PBRICK) ? 'P' :
+ (board->brd_type == KLTYPE_XBRICK) ? 'X' : '?',
+#else
+ toupper(MODULE_GET_BTCHAR(NODEPDA(cnode)->module_id)),
+#endif /* BRINGUP */
+ EDGE_LBL_XTALK, widgetnum);
+ }
+
+ printk("io_xswitch_widget_init: path= %s\n", pathname);
+ rc = hwgraph_path_add(hwgraph_root, pathname, &widgetv);
+
+ ASSERT(rc == GRAPH_SUCCESS);
+
+ /* This is needed to let the user programs to map the
+ * module,slot numbers to the corresponding widget numbers
+ * on the crossbow.
+ */
+ rc = device_master_set(hwgraph_connectpt_get(widgetv), hubv);
+
+ /* If we are looking at the global master io6
+ * then add information about the version of
+ * the io6prom as a part of "detailed inventory"
+ * information.
+ */
+ if (is_master_baseio(nasid,
+ NODEPDA(cnode)->module_id,
+#ifdef BRINGUP
+ get_widget_slotnum(0,widgetnum))) {
+#else
+ <<< BOMB! >>> Need a new way to get slot numbers on IP35/IP37
+#endif
+ extern void klhwg_baseio_inventory_add(devfs_handle_t,
+ cnodeid_t);
+ module = NODEPDA(cnode)->module_id;
+
+#ifdef XBRIDGE_REGS_SIM
+ printk("io_xswitch_widget_init: XBRIDGE_REGS_SIM FIXME: reading xwidget id: hardwired to xbridge (0x2d000049).\n");
+ if (XWIDGET_PART_NUM(0x2d000049)==XXBOW_WIDGET_PART_NUM) {
+#else
+ if (nasid_has_xbridge(nasid)) {
+#endif /* XBRIDGE_REGS_SIM */
+ board = find_lboard_module(
+ (lboard_t *)KL_CONFIG_INFO(nasid),
+ module);
+ /*
+ * BRINGUP
+ * Change iobrick to correct i/o brick
+ */
+#ifdef SUPPORT_PRINTING_M_FORMAT
+ sprintf(pathname, EDGE_LBL_MODULE "/%M/"
+#else
+ sprintf(pathname, EDGE_LBL_MODULE "/%x/"
+#endif
+ "iobrick" "/%s/%d",
+ NODEPDA(cnode)->module_id,
+ EDGE_LBL_XTALK, widgetnum);
+ } else {
+#ifdef BRINGUP
+ slot = get_widget_slotnum(0, widgetnum);
+#else
+ <<< BOMB! Need a new way to get slot numbers on IP35/IP37
+#endif
+ board = get_board_name(nasid, module, slot,
+ new_name);
+ /*
+ * Create the vertex for the widget,
+ * using the decimal
+ * widgetnum as the name of the primary edge.
+ */
+#ifdef SUPPORT_PRINTING_M_FORMAT
+ sprintf(pathname, EDGE_LBL_MODULE "/%M/"
+#else
+ sprintf(pathname, EDGE_LBL_MODULE "/%x/"
+#endif
+ EDGE_LBL_SLOT "/%s/%s",
+ NODEPDA(cnode)->module_id,
+ slotname, new_name);
+ }
+
+ rc = hwgraph_path_add(hwgraph_root, pathname, &widgetv);
+ printk("io_xswitch_widget_init: (2) path= %s\n", pathname);
+ /*
+ * This is a weird ass code needed for error injection
+ * purposes.
+ */
+ rc = device_master_set(hwgraph_connectpt_get(widgetv), hubv);
+
+ klhwg_baseio_inventory_add(widgetv,cnode);
+ }
+ sprintf(name, "%d", widgetnum);
+ printk("io_xswitch_widget_init: FIXME hwgraph_edge_add %s xswitchv 0x%p, widgetv 0x%p\n", name, xswitchv, widgetv);
+ rc = hwgraph_edge_add(xswitchv, widgetv, name);
+
+ /*
+ * crosstalk switch code tracks which
+ * widget is attached to each link.
+ */
+ xswitch_info_vhdl_set(xswitch_info, widgetnum, widgetv);
+
+ /*
+ * Peek at the widget to get its crosstalk part and
+ * mfgr numbers, then present it to the generic xtalk
+ * bus provider to have its driver attach routine
+ * called (or not).
+ */
+#ifdef XBRIDGE_REGS_SIM
+ widget_id = 0x2d000049;
+ printk("io_xswitch_widget_init: XBRIDGE_REGS_SIM FIXME: id hardwired to widget_id\n");
+#else
+ widget_id = XWIDGET_ID_READ(nasid, widgetnum);
+#endif /* XBRIDGE_REGS_SIM */
+ hwid.part_num = XWIDGET_PART_NUM(widget_id);
+ hwid.rev_num = XWIDGET_REV_NUM(widget_id);
+ hwid.mfg_num = XWIDGET_MFG_NUM(widget_id);
+ /* Store some inventory information about
+ * the xwidget in the hardware graph.
+ */
+ xwidget_inventory_add(widgetv,board,hwid);
+
+ (void)xwidget_register(&hwid, widgetv, widgetnum,
+ hubv, hub_widgetid,
+ aa);
+
+#ifdef SN0_USE_BTE
+ bte_bpush_war(cnode, (void *)board);
+#endif
+ }
+
+}
+
+
+static void
+io_init_xswitch_widgets(devfs_handle_t xswitchv, cnodeid_t cnode)
+{
+ xwidgetnum_t widgetnum;
+ async_attach_t aa;
+
+ aa = async_attach_new();
+
+ printk("io_init_xswitch_widgets: xswitchv 0x%p for cnode %d\n", xswitchv, cnode);
+
+ for (widgetnum = HUB_WIDGET_ID_MIN; widgetnum <= HUB_WIDGET_ID_MAX;
+ widgetnum++) {
+#ifdef BRINGUP
+ if (widgetnum != 0xe)
+ io_xswitch_widget_init(xswitchv,
+ cnodeid_to_vertex(cnode),
+ widgetnum, aa);
+
+#else
+ io_xswitch_widget_init(xswitchv,
+ cnodeid_to_vertex(cnode),
+ widgetnum, aa);
+#endif /* BRINGUP */
+ }
+ /*
+ * Wait for parallel attach threads, if any, to complete.
+ */
+ async_attach_waitall(aa);
+ async_attach_free(aa);
+}
+
+/*
+ * For each PCI bridge connected to the xswitch, add a link from the
+ * board's klconfig info to the bridge's hwgraph vertex. This lets
+ * the FRU analyzer find the bridge without traversing the hardware
+ * graph and risking hangs.
+ */
+static void
+io_link_xswitch_widgets(devfs_handle_t xswitchv, cnodeid_t cnodeid)
+{
+ xwidgetnum_t widgetnum;
+ char pathname[128];
+ devfs_handle_t vhdl;
+ nasid_t nasid, peer_nasid;
+ lboard_t *board;
+
+
+
+ /* And its connected hub's nasids */
+ nasid = COMPACT_TO_NASID_NODEID(cnodeid);
+ peer_nasid = NODEPDA(cnodeid)->xbow_peer;
+
+ /*
+ * Look for paths matching "<widgetnum>/pci" under xswitchv.
+ * For every widget, init. its lboard's hwgraph link. If the
+ * board has a PCI bridge, point the link to it.
+ */
+ for (widgetnum = HUB_WIDGET_ID_MIN; widgetnum <= HUB_WIDGET_ID_MAX;
+ widgetnum++) {
+ sprintf(pathname, "%d", widgetnum);
+ if (hwgraph_traverse(xswitchv, pathname, &vhdl) !=
+ GRAPH_SUCCESS)
+ continue;
+
+#if defined (CONFIG_SGI_IP35) || defined (CONFIG_IA64_SGI_SN1) || defined (CONFIG_IA64_GENERIC)
+ board = find_lboard_module((lboard_t *)KL_CONFIG_INFO(nasid),
+ NODEPDA(cnodeid)->module_id);
+#else
+ {
+ slotid_t slot;
+ slot = get_widget_slotnum(xbow_num, widgetnum);
+ board = find_lboard_modslot((lboard_t *)KL_CONFIG_INFO(nasid),
+ NODEPDA(cnodeid)->module_id, slot);
+ }
+#endif /* CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 */
+ if (board == NULL && peer_nasid != INVALID_NASID) {
+ /*
+ * Try to find the board on our peer
+ */
+#if defined (CONFIG_SGI_IP35) || defined (CONFIG_IA64_SGI_SN1) || defined (CONFIG_IA64_GENERIC)
+ board = find_lboard_module(
+ (lboard_t *)KL_CONFIG_INFO(peer_nasid),
+ NODEPDA(cnodeid)->module_id);
+
+#else
+ board = find_lboard_modslot((lboard_t *)KL_CONFIG_INFO(peer_nasid),
+ NODEPDA(cnodeid)->module_id, slot);
+
+#endif /* CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 */
+ }
+ if (board == NULL) {
+#ifndef CONFIG_IA64_SGI_IO
+ cmn_err(CE_WARN,
+ "Could not find PROM info for vertex %v, "
+ "FRU analyzer may fail",
+ vhdl);
+#endif
+ return;
+ }
+
+ sprintf(pathname, "%d/"EDGE_LBL_PCI, widgetnum);
+ if (hwgraph_traverse(xswitchv, pathname, &vhdl) ==
+ GRAPH_SUCCESS)
+ board->brd_graph_link = vhdl;
+ else
+ board->brd_graph_link = GRAPH_VERTEX_NONE;
+ }
+}
+
+/*
+ * Initialize all I/O on the specified node.
+ */
+static void
+io_init_node(cnodeid_t cnodeid)
+{
+ /*REFERENCED*/
+ devfs_handle_t hubv, switchv, widgetv;
+ struct xwidget_hwid_s hwid;
+ hubinfo_t hubinfo;
+ int is_xswitch;
+ nodepda_t *npdap;
+#ifndef CONFIG_IA64_SGI_IO
+ sema_t *peer_sema = 0;
+#else
+ struct semaphore *peer_sema = 0;
+#endif
+ uint32_t widget_partnum;
+ nodepda_router_info_t *npda_rip;
+ cpu_cookie_t c = 0;
+
+#ifndef CONFIG_IA64_SGI_IO
+ /* Try to execute on the node that we're initializing. */
+ c = setnoderun(cnodeid);
+#endif
+ npdap = NODEPDA(cnodeid);
+
+ /*
+ * Get the "top" vertex for this node's hardware
+ * graph; it will carry the per-hub hub-specific
+ * data, and act as the crosstalk provider master.
+ * It's canonical path is probably something of the
+ * form /hw/module/%M/slot/%d/node
+ */
+ hubv = cnodeid_to_vertex(cnodeid);
+ printk("io_init_node: Initialize IO for cnode %d hubv(node) 0x%p npdap 0x%p\n", cnodeid, hubv, npdap);
+
+ ASSERT(hubv != GRAPH_VERTEX_NONE);
+
+#if CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 || CONFIG_IA64_GENERIC
+ hubdev_docallouts(hubv);
+#endif
+
+ /*
+ * Set up the dependent routers if we have any.
+ */
+ npda_rip = npdap->npda_rip_first;
+
+ while(npda_rip) {
+ /* If the router info has not been initialized
+ * then we need to do the router initialization
+ */
+ if (!npda_rip->router_infop) {
+ router_init(cnodeid,0,npda_rip);
+ }
+ npda_rip = npda_rip->router_next;
+ }
+
+ /*
+ * Read mfg info on this hub
+ */
+#ifndef CONFIG_IA64_SGI_IO
+ printk("io_init_node: FIXME need to implement HUB_VERTEX_MFG_INFO\n");
+ HUB_VERTEX_MFG_INFO(hubv);
+#endif /* CONFIG_IA64_SGI_IO */
+
+ /*
+ * If nothing connected to this hub's xtalk port, we're done.
+ */
+ early_probe_for_widget(hubv, &hwid);
+ if (hwid.part_num == XWIDGET_PART_NUM_NONE) {
+#ifdef PROBE_TEST
+ if ((cnodeid == 1) || (cnodeid == 2)) {
+ int index;
+
+ for (index = 0; index < 600; index++)
+ printk("Interfering with device probing!!!\n");
+ }
+#endif
+ /* io_init_done takes cpu cookie as 2nd argument
+ * to do a restorenoderun for the setnoderun done
+ * at the start of this thread
+ */
+
+ printk("**** io_init_node: Node's 0x%p hub widget has XWIDGET_PART_NUM_NONE ****\n", hubv);
+ io_init_done(cnodeid,c);
+ /* NOTREACHED */
+ }
+
+ /*
+ * attach our hub_provider information to hubv,
+ * so we can use it as a crosstalk provider "master"
+ * vertex.
+ */
+ xtalk_provider_register(hubv, &hub_provider);
+ xtalk_provider_startup(hubv);
+
+ /*
+ * Create a vertex to represent the crosstalk bus
+ * attached to this hub, and a vertex to be used
+ * as the connect point for whatever is out there
+ * on the other side of our crosstalk connection.
+ *
+ * Crosstalk Switch drivers "climb up" from their
+ * connection point to try and take over the switch
+ * point.
+ *
+ * Of course, the edges and verticies may already
+ * exist, in which case our net effect is just to
+ * associate the "xtalk_" driver with the connection
+ * point for the device.
+ */
+
+ (void)hwgraph_path_add(hubv, EDGE_LBL_XTALK, &switchv);
+
+ printk("io_init_node: Created 'xtalk' entry to '../node/' xtalk vertex 0x%p\n", switchv);
+
+ ASSERT(switchv != GRAPH_VERTEX_NONE);
+
+ (void)hwgraph_edge_add(hubv, switchv, EDGE_LBL_IO);
+
+ printk("io_init_node: Created symlink 'io' from ../node/io to ../node/xtalk \n");
+
+ /*
+ * We need to find the widget id and update the basew_id field
+ * accordingly. In particular, SN00 has direct connected bridge,
+ * and hence widget id is Not 0.
+ */
+
+ widget_partnum = (((*(volatile int32_t *)(NODE_SWIN_BASE(COMPACT_TO_NASID_NODEID(cnodeid), 0) + WIDGET_ID))) & WIDGET_PART_NUM) >> WIDGET_PART_NUM_SHFT;
+
+ if (widget_partnum == BRIDGE_WIDGET_PART_NUM ||
+ widget_partnum == XBRIDGE_WIDGET_PART_NUM){
+ npdap->basew_id = (((*(volatile int32_t *)(NODE_SWIN_BASE(COMPACT_TO_NASID_NODEID(cnodeid), 0) + BRIDGE_WID_CONTROL))) & WIDGET_WIDGET_ID);
+
+ printk("io_init_node: Found XBRIDGE widget_partnum= 0x%x\n", widget_partnum);
+
+ } else if (widget_partnum == XBOW_WIDGET_PART_NUM ||
+ widget_partnum == XXBOW_WIDGET_PART_NUM) {
+ /*
+ * Xbow control register does not have the widget ID field.
+ * So, hard code the widget ID to be zero.
+ */
+ printk("io_init_node: Found XBOW widget_partnum= 0x%x\n", widget_partnum);
+ npdap->basew_id = 0;
+
+#if defined(BRINGUP)
+ } else if (widget_partnum == XG_WIDGET_PART_NUM) {
+ /*
+ * OK, WTF do we do here if we have an XG direct connected to a HUB/Bedrock???
+ * So, hard code the widget ID to be zero?
+ */
+ npdap->basew_id = 0;
+ npdap->basew_id = (((*(volatile int32_t *)(NODE_SWIN_BASE(COMPACT_TO_NASID_NODEID(cnodeid), 0) + BRIDGE_WID_CONTROL))) & WIDGET_WIDGET_ID);
+#endif
+ } else {
+ npdap->basew_id = (((*(volatile int32_t *)(NODE_SWIN_BASE(COMPACT_TO_NASID_NODEID(cnodeid), 0) + BRIDGE_WID_CONTROL))) & WIDGET_WIDGET_ID);
+
+ panic(" ****io_init_node: Unknown Widget Part Number 0x%x Widgt ID 0x%x attached to Hubv 0x%p ****\n", widget_partnum, npdap->basew_id, hubv);
+
+ /*NOTREACHED*/
+ }
+ {
+ char widname[10];
+ sprintf(widname, "%x", npdap->basew_id);
+ (void)hwgraph_path_add(switchv, widname, &widgetv);
+ printk("io_init_node: Created '%s' to '..node/xtalk/' vertex 0x%p\n", widname, widgetv);
+ ASSERT(widgetv != GRAPH_VERTEX_NONE);
+ }
+
+ nodepda->basew_xc = widgetv;
+
+ is_xswitch = xwidget_hwid_is_xswitch(&hwid);
+
+ /*
+ * Try to become the master of the widget. If this is an xswitch
+ * with multiple hubs connected, only one will succeed. Mastership
+ * of an xswitch is used only when touching registers on that xswitch.
+ * The slave xwidgets connected to the xswitch can be owned by various
+ * masters.
+ */
+ if (device_master_set(widgetv, hubv) == 0) {
+
+ /* Only one hub (thread) per Crosstalk device or switch makes
+ * it to here.
+ */
+
+ /*
+ * Initialize whatever xwidget is hanging off our hub.
+ * Whatever it is, it's accessible through widgetnum 0.
+ */
+ hubinfo_get(hubv, &hubinfo);
+
+ (void)xwidget_register(&hwid, widgetv, npdap->basew_id, hubv, hubinfo->h_widgetid, NULL);
+
+ if (!is_xswitch) {
+ /* io_init_done takes cpu cookie as 2nd argument
+ * to do a restorenoderun for the setnoderun done
+ * at the start of this thread
+ */
+ io_init_done(cnodeid,c);
+ /* NOTREACHED */
+ }
+
+ /*
+ * Special handling for Crosstalk Switches (e.g. xbow).
+ * We need to do things in roughly the following order:
+ * 1) Initialize xswitch hardware (done above)
+ * 2) Determine which hubs are available to be widget masters
+ * 3) Discover which links are active from the xswitch
+ * 4) Assign xwidgets hanging off the xswitch to hubs
+ * 5) Initialize all xwidgets on the xswitch
+ */
+
+ volunteer_for_widgets(switchv, hubv);
+
+ /* If there's someone else on this crossbow, recognize him */
+ if (npdap->xbow_peer != INVALID_NASID) {
+ nodepda_t *peer_npdap = NODEPDA(NASID_TO_COMPACT_NODEID(npdap->xbow_peer));
+ peer_sema = &peer_npdap->xbow_sema;
+ volunteer_for_widgets(switchv, peer_npdap->node_vertex);
+ }
+
+ assign_widgets_to_volunteers(switchv, hubv);
+
+ /* Signal that we're done */
+ if (peer_sema) {
+ up(peer_sema);
+ }
+
+ }
+ else {
+ /* Wait 'til master is done assigning widgets. */
+ down(&npdap->xbow_sema);
+ }
+
+#ifdef PROBE_TEST
+ if ((cnodeid == 1) || (cnodeid == 2)) {
+ int index;
+
+ for (index = 0; index < 500; index++)
+ printk("Interfering with device probing!!!\n");
+ }
+#endif
+ /* Now both nodes can safely inititialize widgets */
+ io_init_xswitch_widgets(switchv, cnodeid);
+ io_link_xswitch_widgets(switchv, cnodeid);
+
+ /* io_init_done takes cpu cookie as 2nd argument
+ * to do a restorenoderun for the setnoderun done
+ * at the start of this thread
+ */
+ io_init_done(cnodeid,c);
+
+ printk("\nio_init_node: DONE INITIALIZED ALL I/O FOR CNODEID %d\n\n", cnodeid);
+}
+
+
+#define IOINIT_STKSZ (16 * 1024)
+
+#ifndef CONFIG_IA64_SGI_IO
+#include <sys/sn/iograph.h>
+#endif
+#define __DEVSTR1 "/../.master/"
+#define __DEVSTR2 "/target/"
+#define __DEVSTR3 "/lun/0/disk/partition/"
+#define __DEVSTR4 "/../ef"
+
+#if CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 || CONFIG_IA64_GENERIC
+/*
+ * Currently, we need to allow for 5 IBrick slots with 1 FC each
+ * plus an internal 1394.
+ *
+ * ioconfig starts numbering SCSI's at NUM_BASE_IO_SCSI_CTLR.
+ */
+#define NUM_BASE_IO_SCSI_CTLR 6
+#endif
+/*
+ * This tells ioconfig where it can start numbering scsi controllers.
+ * Below this base number, platform-specific handles the numbering.
+ * XXX Irix legacy..controller numbering should be part of devfsd's job
+ */
+int num_base_io_scsi_ctlr = 2; /* used by syssgi */
+devfs_handle_t base_io_scsi_ctlr_vhdl[NUM_BASE_IO_SCSI_CTLR];
+static devfs_handle_t baseio_enet_vhdl,baseio_console_vhdl;
+
+/*
+ * Put the logical controller number information in the
+ * scsi controller vertices for each scsi controller that
+ * is in a "fixed position".
+ */
+static void
+scsi_ctlr_nums_add(devfs_handle_t pci_vhdl)
+{
+ {
+ int i;
+
+ num_base_io_scsi_ctlr = NUM_BASE_IO_SCSI_CTLR;
+
+ /* Initialize base_io_scsi_ctlr_vhdl array */
+ for (i=0; i<NUM_BASE_IO_SCSI_CTLR; i++)
+ base_io_scsi_ctlr_vhdl[i] = GRAPH_VERTEX_NONE;
+ }
+#if CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 || CONFIG_IA64_GENERIC
+ {
+ /*
+ * May want to consider changing the SN0 code, above, to work more like
+ * the way this works.
+ */
+ devfs_handle_t base_ibrick_xbridge_vhdl;
+ devfs_handle_t base_ibrick_xtalk_widget_vhdl;
+ devfs_handle_t scsi_ctlr_vhdl;
+ int i;
+ graph_error_t rv;
+
+ /*
+ * This is a table of "well-known" SCSI controllers and their well-known
+ * controller numbers. The names in the table start from the base IBrick's
+ * Xbridge vertex, so the first component is the xtalk widget number.
+ */
+ static struct {
+ char *base_ibrick_scsi_path;
+ int controller_number;
+ } hardwired_scsi_controllers[] = {
+ {"15/" EDGE_LBL_PCI "/1/" EDGE_LBL_SCSI_CTLR "/0", 0},
+ {"15/" EDGE_LBL_PCI "/2/" EDGE_LBL_SCSI_CTLR "/0", 1},
+ {"15/" EDGE_LBL_PCI "/3/" EDGE_LBL_SCSI_CTLR "/0", 2},
+ {"14/" EDGE_LBL_PCI "/1/" EDGE_LBL_SCSI_CTLR "/0", 3},
+ {"14/" EDGE_LBL_PCI "/2/" EDGE_LBL_SCSI_CTLR "/0", 4},
+ {NULL, -1} /* must be last */
+ };
+
+ base_ibrick_xtalk_widget_vhdl = hwgraph_connectpt_get(pci_vhdl);
+ ASSERT_ALWAYS(base_ibrick_xtalk_widget_vhdl != GRAPH_VERTEX_NONE);
+
+ base_ibrick_xbridge_vhdl = hwgraph_connectpt_get(base_ibrick_xtalk_widget_vhdl);
+ ASSERT_ALWAYS(base_ibrick_xbridge_vhdl != GRAPH_VERTEX_NONE);
+ hwgraph_vertex_unref(base_ibrick_xtalk_widget_vhdl);
+
+ /*
+ * Iterate through the list of well-known SCSI controllers.
+ * For each controller found, set it's controller number according
+ * to the table.
+ */
+ for (i=0; hardwired_scsi_controllers[i].base_ibrick_scsi_path != NULL; i++) {
+ rv = hwgraph_path_lookup(base_ibrick_xbridge_vhdl,
+ hardwired_scsi_controllers[i].base_ibrick_scsi_path, &scsi_ctlr_vhdl, NULL);
+
+ if (rv != GRAPH_SUCCESS) /* No SCSI at this path */
+ continue;
+
+ ASSERT(hardwired_scsi_controllers[i].controller_number < NUM_BASE_IO_SCSI_CTLR);
+ base_io_scsi_ctlr_vhdl[hardwired_scsi_controllers[i].controller_number] = scsi_ctlr_vhdl;
+ device_controller_num_set(scsi_ctlr_vhdl, hardwired_scsi_controllers[i].controller_number);
+ hwgraph_vertex_unref(scsi_ctlr_vhdl); /* (even though we're actually keeping a reference) */
+ }
+
+ hwgraph_vertex_unref(base_ibrick_xbridge_vhdl);
+ }
+#else
+#pragma error Bomb!
+#endif
+}
+
+
+#ifndef CONFIG_IA64_SGI_IO
+#include <sys/asm/sn/ioerror_handling.h>
+#else
+#include <asm/sn/ioerror_handling.h>
+#endif
+extern devfs_handle_t ioc3_console_vhdl_get(void);
+devfs_handle_t sys_critical_graph_root = GRAPH_VERTEX_NONE;
+
+/* Define the system critical vertices and connect them through
+ * a canonical parent-child relationships for easy traversal
+ * during io error handling.
+ */
+static void
+sys_critical_graph_init(void)
+{
+ devfs_handle_t bridge_vhdl,master_node_vhdl;
+ devfs_handle_t xbow_vhdl = GRAPH_VERTEX_NONE;
+ extern devfs_handle_t hwgraph_root;
+ devfs_handle_t pci_slot_conn;
+ int slot;
+ devfs_handle_t baseio_console_conn;
+
+ printk("sys_critical_graph_init: FIXME.\n");
+ baseio_console_conn = hwgraph_connectpt_get(baseio_console_vhdl);
+
+ if (baseio_console_conn == NULL) {
+ return;
+ }
+
+ /* Get the vertex handle for the baseio bridge */
+ bridge_vhdl = device_master_get(baseio_console_conn);
+
+ /* Get the master node of the baseio card */
+ master_node_vhdl = cnodeid_to_vertex(
+ master_node_get(baseio_console_vhdl));
+
+ /* Add the "root->node" part of the system critical graph */
+
+ sys_critical_graph_vertex_add(hwgraph_root,master_node_vhdl);
+
+ /* Check if we have a crossbow */
+ if (hwgraph_traverse(master_node_vhdl,
+ EDGE_LBL_XTALK"/0",
+ &xbow_vhdl) == GRAPH_SUCCESS) {
+ /* We have a crossbow.Add "node->xbow" part of the system
+ * critical graph.
+ */
+ sys_critical_graph_vertex_add(master_node_vhdl,xbow_vhdl);
+
+ /* Add "xbow->baseio bridge" of the system critical graph */
+ sys_critical_graph_vertex_add(xbow_vhdl,bridge_vhdl);
+
+ hwgraph_vertex_unref(xbow_vhdl);
+ } else
+ /* We donot have a crossbow. Add "node->baseio_bridge"
+ * part of the system critical graph.
+ */
+ sys_critical_graph_vertex_add(master_node_vhdl,bridge_vhdl);
+
+ /* Add all the populated PCI slot vertices to the system critical
+ * graph with the bridge vertex as the parent.
+ */
+ for (slot = 0 ; slot < 8; slot++) {
+ char slot_edge[10];
+
+ sprintf(slot_edge,"%d",slot);
+ if (hwgraph_traverse(bridge_vhdl,slot_edge, &pci_slot_conn)
+ != GRAPH_SUCCESS)
+ continue;
+ sys_critical_graph_vertex_add(bridge_vhdl,pci_slot_conn);
+ hwgraph_vertex_unref(pci_slot_conn);
+ }
+
+ hwgraph_vertex_unref(bridge_vhdl);
+
+ /* Add the "ioc3 pci connection point -> console ioc3" part
+ * of the system critical graph
+ */
+
+ if (hwgraph_traverse(baseio_console_vhdl,"..",&pci_slot_conn) ==
+ GRAPH_SUCCESS) {
+ sys_critical_graph_vertex_add(pci_slot_conn,
+ baseio_console_vhdl);
+ hwgraph_vertex_unref(pci_slot_conn);
+ }
+
+ /* Add the "ethernet pci connection point -> base ethernet" part of
+ * the system critical graph
+ */
+ if (hwgraph_traverse(baseio_enet_vhdl,"..",&pci_slot_conn) ==
+ GRAPH_SUCCESS) {
+ sys_critical_graph_vertex_add(pci_slot_conn,
+ baseio_enet_vhdl);
+ hwgraph_vertex_unref(pci_slot_conn);
+ }
+
+ /* Add the "scsi controller pci connection point -> base scsi
+ * controller" part of the system critical graph
+ */
+ if (hwgraph_traverse(base_io_scsi_ctlr_vhdl[0],
+ "../..",&pci_slot_conn) == GRAPH_SUCCESS) {
+ sys_critical_graph_vertex_add(pci_slot_conn,
+ base_io_scsi_ctlr_vhdl[0]);
+ hwgraph_vertex_unref(pci_slot_conn);
+ }
+ if (hwgraph_traverse(base_io_scsi_ctlr_vhdl[1],
+ "../..",&pci_slot_conn) == GRAPH_SUCCESS) {
+ sys_critical_graph_vertex_add(pci_slot_conn,
+ base_io_scsi_ctlr_vhdl[1]);
+ hwgraph_vertex_unref(pci_slot_conn);
+ }
+ hwgraph_vertex_unref(baseio_console_conn);
+
+}
+
+static void
+baseio_ctlr_num_set(void)
+{
+ char name[MAXDEVNAME];
+ devfs_handle_t console_vhdl, pci_vhdl, enet_vhdl;
+
+
+ printk("baseio_ctlr_num_set; FIXME\n");
+ console_vhdl = ioc3_console_vhdl_get();
+ if (console_vhdl == GRAPH_VERTEX_NONE)
+ return;
+ /* Useful for setting up the system critical graph */
+ baseio_console_vhdl = console_vhdl;
+
+ vertex_to_name(console_vhdl,name,MAXDEVNAME);
+
+ strcat(name,__DEVSTR1);
+ pci_vhdl = hwgraph_path_to_vertex(name);
+ scsi_ctlr_nums_add(pci_vhdl);
+ /* Unref the pci_vhdl due to the reference by hwgraph_path_to_vertex
+ */
+ hwgraph_vertex_unref(pci_vhdl);
+
+ vertex_to_name(console_vhdl, name, MAXDEVNAME);
+ strcat(name, __DEVSTR4);
+ enet_vhdl = hwgraph_path_to_vertex(name);
+
+ /* Useful for setting up the system critical graph */
+ baseio_enet_vhdl = enet_vhdl;
+
+ device_controller_num_set(enet_vhdl, 0);
+ /* Unref the enet_vhdl due to the reference by hwgraph_path_to_vertex
+ */
+ hwgraph_vertex_unref(enet_vhdl);
+}
+/* #endif */
+
+void
+sn00_rrb_alloc(devfs_handle_t vhdl, int *vendor_list)
+{
+ /* REFERENCED */
+ int rtn_val;
+
+ /*
+ ** sn00 population: errb orrb
+ ** 0- ql 3+?
+ ** 1- ql 2
+ ** 2- ioc3 ethernet 2+?
+ ** 3- ioc3 secondary 1
+ ** 4- 0
+ ** 5- PCI slot
+ ** 6- PCI slot
+ ** 7- PCI slot
+ */
+
+ /* The following code implements this heuristic for getting
+ * maximum usage out of the rrbs
+ *
+ * constraints:
+ * 8 bit ql1 needs 1+1
+ * ql0 or ql5,6,7 wants 1+2
+ * ethernet wants 2 or more
+ *
+ * rules for even rrbs:
+ * if nothing in slot 6
+ * 4 rrbs to 0 and 2 (0xc8889999)
+ * else
+ * 3 2 3 to slots 0 2 6 (0xc8899bbb)
+ *
+ * rules for odd rrbs
+ * if nothing in slot 5 or 7 (0xc8889999)
+ * 4 rrbs to 1 and 3
+ * else if 1 thing in 5 or 7 (0xc8899aaa) or (0xc8899bbb)
+ * 3 2 3 to slots 1 3 5|7
+ * else
+ * 2 1 3 2 to slots 1 3 5 7 (note: if there's a ql card in 7 this
+ * (0xc89aaabb) may short what it wants therefore the
+ * rule should be to plug pci slots in order)
+ */
+
+
+ if (vendor_list[6] != PCIIO_VENDOR_ID_NONE) {
+ /* something in slot 6 */
+ rtn_val = pcibr_alloc_all_rrbs(vhdl, 0, 3,1, 2,0, 0,0, 3,0);
+ }
+ else {
+ rtn_val = pcibr_alloc_all_rrbs(vhdl, 0, 4,1, 4,0, 0,0, 0,0);
+ }
+#ifndef CONFIG_IA64_SGI_IO
+ if (rtn_val)
+ cmn_err(CE_WARN, "sn00_rrb_alloc: pcibr_alloc_all_rrbs failed");
+#endif
+
+ if ((vendor_list[5] != PCIIO_VENDOR_ID_NONE) &&
+ (vendor_list[7] != PCIIO_VENDOR_ID_NONE)) {
+ /* soemthing in slot 5 and 7 */
+ rtn_val = pcibr_alloc_all_rrbs(vhdl, 1, 2,1, 1,0, 3,0, 2,0);
+ }
+ else if (vendor_list[5] != PCIIO_VENDOR_ID_NONE) {
+ /* soemthing in slot 5 but not 7 */
+ rtn_val = pcibr_alloc_all_rrbs(vhdl, 1, 3,1, 2,0, 3,0, 0,0);
+ }
+ else if (vendor_list[7] != PCIIO_VENDOR_ID_NONE) {
+ /* soemthing in slot 7 but not 5 */
+ rtn_val = pcibr_alloc_all_rrbs(vhdl, 1, 3,1, 2,0, 0,0, 3,0);
+ }
+ else {
+ /* nothing in slot 5 or 7 */
+ rtn_val = pcibr_alloc_all_rrbs(vhdl, 1, 4,1, 4,0, 0,0, 0,0);
+ }
+#ifndef CONFIG_IA64_SGI_IO
+ if (rtn_val)
+ cmn_err(CE_WARN, "sn00_rrb_alloc: pcibr_alloc_all_rrbs failed");
+#endif
+}
+
+
+/*
+ * Initialize all I/O devices. Starting closest to nodes, probe and
+ * initialize outward.
+ */
+void
+init_all_devices(void)
+{
+ /* Governor on init threads..bump up when safe
+ * (beware many devfs races)
+ */
+#ifndef CONFIG_IA64_SGI_IO
+ int io_init_node_threads = 2;
+#endif
+ cnodeid_t cnodeid, active;
+
+ init_MUTEX(&io_init_sema);
+
+
+ active = 0;
+ for (cnodeid = 0; cnodeid < maxnodes; cnodeid++) {
+#ifndef CONFIG_IA64_SGI_IO
+ char thread_name[16];
+ extern int io_init_pri;
+
+ /*
+ * Spawn a service thread for each node to initialize all
+ * I/O on that node. Each thread attempts to bind itself
+ * to the node whose I/O it's initializing.
+ */
+ sprintf(thread_name, "IO_init[%d]", cnodeid);
+
+ (void)sthread_create(thread_name, 0, IOINIT_STKSZ, 0,
+ io_init_pri, KT_PS, (st_func_t *)io_init_node,
+ (void *)(long)cnodeid, 0, 0, 0);
+#else
+ printk("init_all_devices: Calling io_init_node() for cnode %d\n", cnodeid);
+ io_init_node(cnodeid);
+
+ printk("init_all_devices: Done io_init_node() for cnode %d\n", cnodeid);
+
+#endif /* !CONFIG_IA64_SGI_IO */
+
+
+ /* Limit how many nodes go at once, to not overload hwgraph */
+ /* TBD: Should timeout */
+#ifdef AA_DEBUG
+ printk("started thread for cnode %d\n", cnodeid);
+#endif
+#ifdef LINUX_KERNEL_THREADS
+ active++;
+ if (io_init_node_threads &&
+ active >= io_init_node_threads) {
+ down(&io_init_sema);
+ active--;
+ }
+#endif /* LINUX_KERNEL_THREADS */
+ }
+
+#ifdef LINUX_KERNEL_THREADS
+ /* Wait until all IO_init threads are done */
+
+ while (active > 0) {
+#ifdef AA_DEBUG
+ printk("waiting, %d still active\n", active);
+#endif
+ sema(&io_init_sema);
+ active--;
+ }
+
+#endif /* LINUX_KERNEL_THREADS */
+
+ for (cnodeid = 0; cnodeid < maxnodes; cnodeid++)
+ /*
+ * Update information generated by IO init.
+ */
+ update_node_information(cnodeid);
+
+ baseio_ctlr_num_set();
+ /* Setup the system critical graph (which is a subgraph of the
+ * main hwgraph). This information is useful during io error
+ * handling.
+ */
+ sys_critical_graph_init();
+
+#if HWG_PRINT
+ hwgraph_print();
+#endif
+
+}
+
+#define toint(x) ((int)(x) - (int)('0'))
+
+void
+devnamefromarcs(char *devnm)
+{
+ int val;
+ char tmpnm[MAXDEVNAME];
+ char *tmp1, *tmp2;
+
+ val = strncmp(devnm, "dks", 3);
+ if (val != 0)
+ return;
+ tmp1 = devnm + 3;
+ if (!isdigit(*tmp1))
+ return;
+
+ val = 0;
+ while (isdigit(*tmp1)) {
+ val = 10*val+toint(*tmp1);
+ tmp1++;
+ }
+
+ if(*tmp1 != 'd')
+ return;
+ else
+ tmp1++;
+
+ if ((val < 0) || (val >= NUM_BASE_IO_SCSI_CTLR)) {
+ int i;
+ int viable_found = 0;
+
+ printk("Only controller numbers 0..%d are supported for\n", NUM_BASE_IO_SCSI_CTLR-1);
+ printk("prom \"root\" variables of the form dksXdXsX.\n");
+ printk("To use another disk you must use the full hardware graph path\n\n");
+ printk("Possible controller numbers for use in 'dksXdXsX' on this system: ");
+ for (i=0; i<NUM_BASE_IO_SCSI_CTLR; i++) {
+ if (base_io_scsi_ctlr_vhdl[i] != GRAPH_VERTEX_NONE) {
+ printk("%d ", i);
+ viable_found=1;
+ }
+ }
+ if (viable_found)
+ printk("\n");
+ else
+ printk("none found!\n");
+
+#ifndef CONFIG_IA64_SGI_IO
+ if (kdebug)
+ debug("ring");
+#endif
+ DELAY(15000000);
+ //prom_reboot();
+ panic("FIXME: devnamefromarcs: should call prom_reboot here.\n");
+ /* NOTREACHED */
+ }
+
+ ASSERT(base_io_scsi_ctlr_vhdl[val] != GRAPH_VERTEX_NONE);
+ vertex_to_name(base_io_scsi_ctlr_vhdl[val],
+ tmpnm,
+ MAXDEVNAME);
+ tmp2 = tmpnm + strlen(tmpnm);
+ strcpy(tmp2, __DEVSTR2);
+ tmp2 += strlen(__DEVSTR2);
+ while (*tmp1 != 's') {
+ if((*tmp2++ = *tmp1++) == '\0')
+ return;
+ }
+ tmp1++;
+ strcpy(tmp2, __DEVSTR3);
+ tmp2 += strlen(__DEVSTR3);
+ while ( (*tmp2++ = *tmp1++) )
+ ;
+ tmp2--;
+ *tmp2++ = '/';
+ strcpy(tmp2, EDGE_LBL_BLOCK);
+ strcpy(devnm,tmpnm);
+}
diff --git a/arch/ia64/sn/io/module.c b/arch/ia64/sn/io/module.c
new file mode 100644
index 000000000..7a2d5f0e2
--- /dev/null
+++ b/arch/ia64/sn/io/module.c
@@ -0,0 +1,311 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/cmn_err.h>
+#include <asm/sn/xtalk/xbow.h>
+#include <asm/sn/pci/bridge.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/sn1/hubdev.h>
+#include <asm/sn/module.h>
+#include <asm/sn/pci/pcibr.h>
+#include <asm/sn/xtalk/xswitch.h>
+#include <asm/sn/nodepda.h>
+
+
+#define LDEBUG 1
+
+#define DPRINTF if (LDEBUG) printk
+#define printf printk
+
+module_t *modules[MODULE_MAX];
+int nummodules;
+
+#define SN00_SERIAL_FUDGE 0x3b1af409d513c2
+#define SN0_SERIAL_FUDGE 0x6e
+
+void
+encode_int_serial(uint64_t src,uint64_t *dest)
+{
+ uint64_t val;
+ int i;
+
+ val = src + SN00_SERIAL_FUDGE;
+
+
+ for (i = 0; i < sizeof(long long); i++) {
+ ((char*)dest)[i] =
+ ((char*)&val)[sizeof(long long)/2 +
+ ((i%2) ? ((i/2 * -1) - 1) : (i/2))];
+ }
+}
+
+
+void
+decode_int_serial(uint64_t src, uint64_t *dest)
+{
+ uint64_t val;
+ int i;
+
+ for (i = 0; i < sizeof(long long); i++) {
+ ((char*)&val)[sizeof(long long)/2 +
+ ((i%2) ? ((i/2 * -1) - 1) : (i/2))] =
+ ((char*)&src)[i];
+ }
+
+ *dest = val - SN00_SERIAL_FUDGE;
+}
+
+
+void
+encode_str_serial(const char *src, char *dest)
+{
+ int i;
+
+ for (i = 0; i < MAX_SERIAL_NUM_SIZE; i++) {
+
+ dest[i] = src[MAX_SERIAL_NUM_SIZE/2 +
+ ((i%2) ? ((i/2 * -1) - 1) : (i/2))] +
+ SN0_SERIAL_FUDGE;
+ }
+}
+
+void
+decode_str_serial(const char *src, char *dest)
+{
+ int i;
+
+ for (i = 0; i < MAX_SERIAL_NUM_SIZE; i++) {
+ dest[MAX_SERIAL_NUM_SIZE/2 +
+ ((i%2) ? ((i/2 * -1) - 1) : (i/2))] = src[i] -
+ SN0_SERIAL_FUDGE;
+ }
+}
+
+
+module_t *module_lookup(moduleid_t id)
+{
+ int i;
+
+ DPRINTF("module_lookup: id=%d\n", id);
+
+ for (i = 0; i < nummodules; i++)
+ if (modules[i]->id == id) {
+ DPRINTF("module_lookup: found m=0x%p\n", modules[i]);
+ return modules[i];
+ }
+
+ return NULL;
+}
+
+/*
+ * module_add_node
+ *
+ * The first time a new module number is seen, a module structure is
+ * inserted into the module list in order sorted by module number
+ * and the structure is initialized.
+ *
+ * The node number is added to the list of nodes in the module.
+ */
+
+module_t *module_add_node(moduleid_t id, cnodeid_t n)
+{
+ module_t *m;
+ int i;
+
+ DPRINTF("module_add_node: id=%x node=%d\n", id, n);
+
+ if ((m = module_lookup(id)) == 0) {
+#ifndef CONFIG_IA64_SGI_IO
+ m = kmem_zalloc_node(sizeof (module_t), KM_NOSLEEP, n);
+#else
+ m = kmalloc(sizeof (module_t), GFP_KERNEL);
+ memset(m, 0 , sizeof(module_t));
+ printk("Module nodecnt = %d\n", m->nodecnt);
+#endif
+ ASSERT_ALWAYS(m);
+
+ DPRINTF("module_add_node: m=0x%p\n", m);
+
+ m->id = id;
+ spin_lock_init(&m->lock);
+
+ init_MUTEX_LOCKED(&m->thdcnt);
+
+printk("Set elsc to 0x%p on node %d\n", &m->elsc, get_nasid());
+
+set_elsc(&m->elsc);
+ elsc_init(&m->elsc, COMPACT_TO_NASID_NODEID(n));
+ spin_lock_init(&m->elsclock);
+
+ /* Insert in sorted order by module number */
+
+ for (i = nummodules; i > 0 && modules[i - 1]->id > id; i--)
+ modules[i] = modules[i - 1];
+
+ modules[i] = m;
+ nummodules++;
+ }
+
+ m->nodes[m->nodecnt++] = n;
+
+printk("module_add_node: module %x now has %d nodes\n", id, m->nodecnt);
+ DPRINTF("module_add_node: module %x now has %d nodes\n", id, m->nodecnt);
+
+ return m;
+}
+
+int module_probe_snum(module_t *m, nasid_t nasid)
+{
+ lboard_t *board;
+ klmod_serial_num_t *comp;
+
+ board = find_lboard((lboard_t *) KL_CONFIG_INFO(nasid),
+ KLTYPE_MIDPLANE8);
+
+ if (! board || KL_CONFIG_DUPLICATE_BOARD(board))
+ return 0;
+
+ comp = GET_SNUM_COMP(board);
+
+ if (comp) {
+#if LDEBUG
+ int i;
+
+ printf("********found module with id %x and string", m->id);
+
+ for (i = 0; i < MAX_SERIAL_NUM_SIZE; i++)
+ printf(" %x ", comp->snum.snum_str[i]);
+
+ printf("\n"); /* Fudged string is not ASCII */
+#endif
+
+ if (comp->snum.snum_str[0] != '\0') {
+ bcopy(comp->snum.snum_str,
+ m->snum.snum_str,
+ MAX_SERIAL_NUM_SIZE);
+ m->snum_valid = 1;
+ }
+ }
+
+ if (m->snum_valid)
+ return 1;
+ else {
+#ifndef CONFIG_IA64_SGI_IO
+ cmn_err(CE_WARN | CE_MAINTENANCE,
+ "Invalid serial number for module %d, "
+ "possible missing or invalid NIC.", m->id);
+#else
+ printk("Invalid serial number for module %d, "
+ "possible missing or invalid NIC.", m->id);
+#endif
+ return 0;
+ }
+}
+
+void
+io_module_init(void)
+{
+ cnodeid_t node;
+ lboard_t *board;
+ nasid_t nasid;
+ int nserial;
+ module_t *m;
+
+ DPRINTF("*******module_init\n");
+
+ nserial = 0;
+
+ for (node = 0; node < numnodes; node++) {
+ nasid = COMPACT_TO_NASID_NODEID(node);
+
+ board = find_lboard((lboard_t *) KL_CONFIG_INFO(nasid),
+ KLTYPE_IP27);
+ ASSERT(board);
+
+ m = module_add_node(board->brd_module, node);
+
+ if (! m->snum_valid && module_probe_snum(m, nasid))
+ nserial++;
+ }
+
+ DPRINTF("********found total of %d serial numbers in the system\n",
+ nserial);
+
+ if (nserial == 0)
+ cmn_err(CE_WARN, "No serial number found.");
+}
+
+#ifdef BRINGUP
+elsc_t *Elsc[100];
+
+void
+set_elsc(elsc_t *p)
+{
+ Elsc[get_nasid()] = p;
+}
+#endif
+
+elsc_t *get_elsc(void)
+{
+#ifdef BRINGUP
+return(Elsc[get_nasid()]);
+#else
+ if ( NODEPDA(get_nasid())->module == (module_t *)0 ) {
+ printf("get_elsc() for nasd %d fails\n", get_nasid());
+// return((elsc_t *)0);
+ }
+ return &NODEPDA(get_nasid())->module->elsc;
+
+// return &NODEPDA(NASID_TO_COMPACT_NODEID(0))->module->elsc;
+#endif
+}
+
+int
+get_kmod_info(cmoduleid_t cmod, module_info_t *mod_info)
+{
+ int i;
+
+ if (cmod < 0 || cmod >= nummodules)
+ return EINVAL;
+
+ if (! modules[cmod]->snum_valid)
+ return ENXIO;
+
+ mod_info->mod_num = modules[cmod]->id;
+ {
+ char temp[MAX_SERIAL_NUM_SIZE];
+
+ decode_str_serial(modules[cmod]->snum.snum_str, temp);
+
+ /* if this is an invalid serial number return an error */
+ if (temp[0] != 'K')
+ return ENXIO;
+
+ mod_info->serial_num = 0;
+
+ for (i = 0; i < MAX_SERIAL_NUM_SIZE && temp[i] != '\0'; i++) {
+ mod_info->serial_num <<= 4;
+ mod_info->serial_num |= (temp[i] & 0xf);
+
+ mod_info->serial_str[i] = temp[i];
+ }
+
+ mod_info->serial_str[i] = '\0';
+ }
+
+ return 0;
+}
diff --git a/arch/ia64/sn/io/pci.c b/arch/ia64/sn/io/pci.c
new file mode 100644
index 000000000..e0745c91f
--- /dev/null
+++ b/arch/ia64/sn/io/pci.c
@@ -0,0 +1,306 @@
+/*
+ *
+ * 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.
+ *
+ * SNI64 specific PCI support for SNI IO.
+ *
+ * Copyright (C) 1997, 1998, 2000 Colin Ngam
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <asm/sn/types.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/cmn_err.h>
+#include <asm/sn/iobus.h>
+#include <asm/sn/iograph.h>
+#include <asm/param.h>
+#include <asm/sn/pio.h>
+#include <asm/sn/xtalk/xwidget.h>
+#include <asm/sn/sn_private.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/hcl_util.h>
+#include <asm/sn/pci/pciio.h>
+#include <asm/sn/pci/pcibr.h>
+#include <asm/sn/pci/pcibr_private.h>
+#include <asm/sn/pci/bridge.h>
+
+#ifdef DEBUG_CONFIG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+
+
+#ifdef CONFIG_PCI
+
+extern devfs_handle_t pci_bus_to_vertex(unsigned char);
+extern devfs_handle_t devfn_to_vertex(unsigned char bus, unsigned char devfn);
+
+/*
+ * snia64_read_config_byte - Read a byte from the config area of the device.
+ */
+static int snia64_read_config_byte (struct pci_dev *dev,
+ int where, unsigned char *val)
+{
+ unsigned long res = 0;
+ unsigned size = 1;
+ devfs_handle_t device_vertex;
+
+ if ( (dev == (struct pci_dev *)0) || (val == (unsigned char *)0) ) {
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+ device_vertex = devfn_to_vertex(dev->bus->number, dev->devfn);
+ if (!device_vertex) {
+ DBG("%s : nonexistent device: bus= 0x%x slot= 0x%x func= 0x%x\n",
+ __FUNCTION__, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+ return(-1);
+ }
+ res = pciio_config_get(device_vertex, (unsigned) where, size);
+ *val = (unsigned char) res;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+/*
+ * snia64_read_config_word - Read 2 bytes from the config area of the device.
+ */
+static int snia64_read_config_word (struct pci_dev *dev,
+ int where, unsigned short *val)
+{
+ unsigned long res = 0;
+ unsigned size = 2; /* 2 bytes */
+ devfs_handle_t device_vertex;
+
+ if ( (dev == (struct pci_dev *)0) || (val == (unsigned short *)0) ) {
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+ device_vertex = devfn_to_vertex(dev->bus->number, dev->devfn);
+ if (!device_vertex) {
+ DBG("%s : nonexistent device: bus= 0x%x slot= 0x%x func= 0x%x\n",
+ __FUNCTION__, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+ return(-1);
+ }
+ res = pciio_config_get(device_vertex, (unsigned) where, size);
+ *val = (unsigned short) res;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+/*
+ * snia64_read_config_dword - Read 4 bytes from the config area of the device.
+ */
+static int snia64_read_config_dword (struct pci_dev *dev,
+ int where, unsigned int *val)
+{
+ unsigned long res = 0;
+ unsigned size = 4; /* 4 bytes */
+ devfs_handle_t device_vertex;
+
+ if (where & 3) {
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+ if ( (dev == (struct pci_dev *)0) || (val == (unsigned int *)0) ) {
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ device_vertex = devfn_to_vertex(dev->bus->number, dev->devfn);
+ if (!device_vertex) {
+ DBG("%s : nonexistent device: bus= 0x%x slot= 0x%x func= 0x%x\n",
+ __FUNCTION__, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+ return(-1);
+ }
+ res = pciio_config_get(device_vertex, (unsigned) where, size);
+ *val = (unsigned int) res;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+/*
+ * snia64_write_config_byte - Writes 1 byte to the config area of the device.
+ */
+static int snia64_write_config_byte (struct pci_dev *dev,
+ int where, unsigned char val)
+{
+ devfs_handle_t device_vertex;
+
+ if ( dev == (struct pci_dev *)0 ) {
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+ /*
+ * if it's an IOC3 then we bail out, we special
+ * case them with pci_fixup_ioc3
+ */
+ if (dev->vendor == PCI_VENDOR_ID_SGI &&
+ dev->device == PCI_DEVICE_ID_SGI_IOC3 )
+ return PCIBIOS_SUCCESSFUL;
+
+ device_vertex = devfn_to_vertex(dev->bus->number, dev->devfn);
+ if (!device_vertex) {
+ DBG("%s : nonexistent device: bus= 0x%x slot= 0x%x func= 0x%x\n",
+ __FUNCTION__, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+ return(-1);
+ }
+ pciio_config_set( device_vertex, (unsigned)where, 1, (uint64_t) val);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+/*
+ * snia64_write_config_word - Writes 2 bytes to the config area of the device.
+ */
+static int snia64_write_config_word (struct pci_dev *dev,
+ int where, unsigned short val)
+{
+ devfs_handle_t device_vertex = NULL;
+
+ if (where & 1) {
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+ if ( dev == (struct pci_dev *)0 ) {
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+ /*
+ * if it's an IOC3 then we bail out, we special
+ * case them with pci_fixup_ioc3
+ */
+ if (dev->vendor == PCI_VENDOR_ID_SGI &&
+ dev->device == PCI_DEVICE_ID_SGI_IOC3)
+ return PCIBIOS_SUCCESSFUL;
+
+ device_vertex = devfn_to_vertex(dev->bus->number, dev->devfn);
+ if (!device_vertex) {
+ DBG("%s : nonexistent device: bus= 0x%x slot= 0x%x func= 0x%x\n",
+ __FUNCTION__, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+ return(-1);
+ }
+ pciio_config_set( device_vertex, (unsigned)where, 2, (uint64_t) val);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+/*
+ * snia64_write_config_dword - Writes 4 bytes to the config area of the device.
+ */
+static int snia64_write_config_dword (struct pci_dev *dev,
+ int where, unsigned int val)
+{
+ devfs_handle_t device_vertex;
+
+ if (where & 3) {
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+ if ( dev == (struct pci_dev *)0 ) {
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+ /*
+ * if it's an IOC3 then we bail out, we special
+ * case them with pci_fixup_ioc3
+ */
+ if (dev->vendor == PCI_VENDOR_ID_SGI &&
+ dev->device == PCI_DEVICE_ID_SGI_IOC3)
+ return PCIBIOS_SUCCESSFUL;
+
+ device_vertex = devfn_to_vertex(dev->bus->number, dev->devfn);
+ if (!device_vertex) {
+ DBG("%s : nonexistent device: bus= 0x%x slot= 0x%x func= 0x%x\n",
+ __FUNCTION__, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+ return(-1);
+ }
+ pciio_config_set( device_vertex, (unsigned)where, 4, (uint64_t) val);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops snia64_pci_ops = {
+ snia64_read_config_byte,
+ snia64_read_config_word,
+ snia64_read_config_dword,
+ snia64_write_config_byte,
+ snia64_write_config_word,
+ snia64_write_config_dword
+};
+
+/*
+ * snia64_pci_find_bios - SNIA64 pci_find_bios() platform specific code.
+ */
+void __init
+sn1_pci_find_bios(void)
+{
+ extern struct pci_ops pci_conf;
+ /*
+ * Go initialize our IO Infrastructure ..
+ */
+ extern void sgi_master_io_infr_init(void);
+
+ sgi_master_io_infr_init();
+
+#ifdef BRINGUP
+ if ( IS_RUNNING_ON_SIMULATOR() )
+ return;
+#endif
+ /* sn1_io_infrastructure_init(); */
+ pci_conf = snia64_pci_ops;
+}
+
+void
+pci_fixup_ioc3(struct pci_dev *d)
+{
+ int i;
+ int slot;
+ unsigned long res = 0;
+ unsigned int val, size;
+ int ret;
+ u_short command;
+
+ devfs_handle_t device_vertex;
+ devfs_handle_t bridge_vhdl = pci_bus_to_vertex(d->bus->number);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) hwgraph_fastinfo_get(bridge_vhdl);
+ devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
+ bridge_t *bridge = pcibr_soft->bs_base;
+ bridgereg_t devreg;
+
+ /* IOC3 only decodes 0x20 bytes of the config space, reading
+ * beyond that is relatively benign but writing beyond that
+ * (especially the base address registers) will shut down the
+ * pci bus...so avoid doing so.
+ * NOTE: this means we can't program the intr_pin into the device,
+ * currently we hack this with special code in
+ * sgi_pci_intr_support()
+ */
+ printk("pci_fixup_ioc3: Fixing base addresses for ioc3 device %s\n", d->slot_name);
+
+ /* I happen to know from the spec that the ioc3 needs only 0xfffff
+ * The standard pci trick of writing ~0 to the baddr and seeing
+ * what comes back doesn't work with the ioc3
+ */
+ size = 0xfffff;
+ d->resource[0].end = (unsigned long) d->resource[0].start + (unsigned long) size;
+
+ /*
+ * Zero out the resource structure .. because we did not go through
+ * the normal PCI Infrastructure Init, garbbage are left in these
+ * fileds.
+ */
+ for (i = 1; i <= PCI_ROM_RESOURCE; i++) {
+ d->resource[i].start = 0UL;
+ d->resource[i].end = 0UL;
+ d->resource[i].flags = 0UL;
+ }
+
+ /*
+ * Hardcode Device 4 register(IOC3 is in Slot 4) to set the
+ * DEV_DIRECT bit. This will not work if IOC3 is not on Slot
+ * 4.
+ */
+ *(volatile u32 *)0xc0000a000f000220 |= 0x90000;
+
+ d->subsystem_vendor = 0;
+ d->subsystem_device = 0;
+
+}
+
+#endif /* CONFIG_PCI */
diff --git a/arch/ia64/sn/io/pci_bus_cvlink.c b/arch/ia64/sn/io/pci_bus_cvlink.c
new file mode 100644
index 000000000..a90b6456f
--- /dev/null
+++ b/arch/ia64/sn/io/pci_bus_cvlink.c
@@ -0,0 +1,591 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <asm/sn/types.h>
+#include <asm/sn/hack.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/cmn_err.h>
+#include <asm/sn/iobus.h>
+#include <asm/sn/iograph.h>
+#include <asm/param.h>
+#include <asm/sn/pio.h>
+#include <asm/sn/xtalk/xwidget.h>
+#include <asm/sn/sn_private.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/hcl_util.h>
+#include <asm/sn/agent.h>
+#include <asm/sn/intr.h>
+#include <asm/sn/xtalk/xtalkaddrs.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/io.h>
+#include <asm/sn/pci/pci_bus_cvlink.h>
+
+#include <asm/sn/pci/pciio.h>
+// #include <sys/ql.h>
+#include <asm/sn/pci/pcibr.h>
+#include <asm/sn/pci/pcibr_private.h>
+extern int bridge_rev_b_data_check_disable;
+
+#define MAX_PCI_XWIDGET 256
+devfs_handle_t busnum_to_xwidget[MAX_PCI_XWIDGET];
+nasid_t busnum_to_nid[MAX_PCI_XWIDGET];
+unsigned char num_bridges;
+static int done_probing = 0;
+
+static int pci_bus_map_create(devfs_handle_t xtalk);
+devfs_handle_t devfn_to_vertex(unsigned char busnum, unsigned int devfn);
+
+/*
+ * pci_bus_cvlink_init() - To be called once during initialization before
+ * SGI IO Infrastructure init is called.
+ */
+void
+pci_bus_cvlink_init(void)
+{
+
+ memset(busnum_to_xwidget, 0x0, sizeof(devfs_handle_t) * MAX_PCI_XWIDGET);
+ memset(busnum_to_nid, 0x0, sizeof(nasid_t) * MAX_PCI_XWIDGET);
+ num_bridges = 0;
+}
+
+/*
+ * pci_bus_to_vertex() - Given a logical Linux Bus Number returns the associated
+ * pci bus vertex from the SGI IO Infrastructure.
+ */
+devfs_handle_t
+pci_bus_to_vertex(unsigned char busnum)
+{
+
+ devfs_handle_t xwidget;
+ devfs_handle_t pci_bus = NULL;
+
+
+ /*
+ * First get the xwidget vertex.
+ */
+ xwidget = busnum_to_xwidget[busnum];
+ if (!xwidget)
+ return (NULL);
+
+ /*
+ * Use devfs to get the pci vertex from xwidget.
+ */
+ if (hwgraph_traverse(xwidget, EDGE_LBL_PCI, &pci_bus) != GRAPH_SUCCESS) {
+ if (!pci_bus) {
+ printk("pci_bus_to_vertex: Cannot find pci bus for given bus number %d\n", busnum);
+ return (NULL);
+ }
+ }
+
+ return(pci_bus);
+}
+
+/*
+ * devfn_to_vertex() - returns the vertex of the device given the bus, slot,
+ * and function numbers.
+ */
+devfs_handle_t
+devfn_to_vertex(unsigned char busnum, unsigned int devfn)
+{
+
+ int slot = 0;
+ int func = 0;
+ char name[16];
+ devfs_handle_t pci_bus = NULL;
+ devfs_handle_t device_vertex = NULL;
+
+ /*
+ * Go get the pci bus vertex.
+ */
+ pci_bus = pci_bus_to_vertex(busnum);
+ if (!pci_bus) {
+ /*
+ * During probing, the Linux pci code invents non existant
+ * bus numbers and pci_dev structures and tries to access
+ * them to determine existance. Don't crib during probing.
+ */
+ if (done_probing)
+ printk("devfn_to_vertex: Invalid bus number %d given.\n", busnum);
+ return(NULL);
+ }
+
+
+ /*
+ * Go get the slot&function vertex.
+ * Should call pciio_slot_func_to_name() when ready.
+ */
+ slot = PCI_SLOT(devfn);
+ func = PCI_FUNC(devfn);
+
+ if (func == 0)
+ sprintf(name, "%d", slot);
+ else
+ sprintf(name, "%d%c", slot, 'a'+func);
+
+ if (hwgraph_traverse(pci_bus, name, &device_vertex) != GRAPH_SUCCESS) {
+ if (!device_vertex) {
+ printk("devfn_to_vertex: Unable to get slot&func %s from pci vertex 0x%p\n", name, pci_bus);
+ return(NULL);
+ }
+ }
+
+ return(device_vertex);
+}
+
+/*
+ * Most drivers currently do not properly tell the arch specific pci dma
+ * interfaces whether they can handle A64. Here is where we privately
+ * keep track of this.
+ */
+static void __init
+set_sn1_pci64(struct pci_dev *dev)
+{
+ unsigned short vendor = dev->vendor;
+ unsigned short device = dev->device;
+
+ if (vendor == PCI_VENDOR_ID_QLOGIC) {
+ if ((device == PCI_DEVICE_ID_QLOGIC_ISP2100) ||
+ (device == PCI_DEVICE_ID_QLOGIC_ISP2200)) {
+ SET_PCIA64(dev);
+ return;
+ }
+ }
+
+ if (vendor == PCI_VENDOR_ID_SGI) {
+ if (device == PCI_DEVICE_ID_SGI_IOC3) {
+ SET_PCIA64(dev);
+ return;
+ }
+ }
+
+}
+
+/*
+ * sn1_pci_fixup() - This routine is called when platform_pci_fixup() is
+ * invoked at the end of pcibios_init() to link the Linux pci
+ * infrastructure to SGI IO Infrasturcture - ia64/kernel/pci.c
+ *
+ * Other platform specific fixup can also be done here.
+ */
+void
+sn1_pci_fixup(int arg)
+{
+ struct list_head *ln;
+ struct pci_bus *pci_bus = NULL;
+ struct pci_dev *device_dev = NULL;
+ struct sn1_widget_sysdata *widget_sysdata;
+ struct sn1_device_sysdata *device_sysdata;
+ extern void sn1_pci_find_bios(void);
+
+
+unsigned long res;
+
+ if (arg == 0) {
+ sn1_pci_find_bios();
+ return;
+ }
+
+#if 0
+{
+ devfs_handle_t bridge_vhdl = pci_bus_to_vertex(0);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) hwgraph_fastinfo_get(bridge_vhdl);
+ bridge_t *bridge = pcibr_soft->bs_base;
+printk("Before Changing PIO Map Address:\n");
+ printk("pci_fixup_ioc3: Before devreg fixup\n");
+ printk("pci_fixup_ioc3: Devreg 0 0x%x\n", bridge->b_device[0].reg);
+ printk("pci_fixup_ioc3: Devreg 1 0x%x\n", bridge->b_device[1].reg);
+ printk("pci_fixup_ioc3: Devreg 2 0x%x\n", bridge->b_device[2].reg);
+ printk("pci_fixup_ioc3: Devreg 3 0x%x\n", bridge->b_device[3].reg);
+ printk("pci_fixup_ioc3: Devreg 4 0x%x\n", bridge->b_device[4].reg);
+ printk("pci_fixup_ioc3: Devreg 5 0x%x\n", bridge->b_device[5].reg);
+ printk("pci_fixup_ioc3: Devreg 6 0x%x\n", bridge->b_device[6].reg);
+ printk("pci_fixup_ioc3: Devreg 7 0x%x\n", bridge->b_device[7].reg);
+}
+#endif
+ done_probing = 1;
+
+ if ( IS_RUNNING_ON_SIMULATOR() ) {
+ printk("sn1_pci_fixup not supported on simulator.\n");
+ return;
+ }
+
+#ifdef REAL_HARDWARE
+
+ /*
+ * Initialize the pci bus vertex in the pci_bus struct.
+ */
+ for( ln = pci_root_buses.next; ln != &pci_root_buses; ln = ln->next) {
+ pci_bus = pci_bus_b(ln);
+ widget_sysdata = kmalloc(sizeof(struct sn1_widget_sysdata),
+ GFP_KERNEL);
+ widget_sysdata->vhdl = pci_bus_to_vertex(pci_bus->number);
+ pci_bus->sysdata = (void *)widget_sysdata;
+ }
+
+ /*
+ * set the root start and end so that drivers calling check_region()
+ * won't see a conflict
+ */
+ ioport_resource.start |= IO_SWIZ_BASE;
+ ioport_resource.end |= (HSPEC_SWIZ_BASE-1);
+ /*
+ * Initialize the device vertex in the pci_dev struct.
+ */
+ pci_for_each_dev(device_dev) {
+ unsigned int irq;
+ int idx;
+ u16 cmd;
+ devfs_handle_t vhdl;
+ unsigned long size;
+
+ if (device_dev->vendor == PCI_VENDOR_ID_SGI &&
+ device_dev->device == PCI_DEVICE_ID_SGI_IOC3) {
+ extern void pci_fixup_ioc3(struct pci_dev *d);
+ pci_fixup_ioc3(device_dev);
+ }
+
+ /* Set the device vertex */
+
+ device_sysdata = kmalloc(sizeof(struct sn1_device_sysdata),
+ GFP_KERNEL);
+ device_sysdata->vhdl = devfn_to_vertex(device_dev->bus->number, device_dev->devfn);
+ device_sysdata->isa64 = 0;
+ device_dev->sysdata = (void *) device_sysdata;
+ set_sn1_pci64(device_dev);
+ pci_read_config_word(device_dev, PCI_COMMAND, &cmd);
+
+ /*
+ * Set the resources address correctly. The assumption here
+ * is that the addresses in the resource structure has been
+ * read from the card and it was set in the card by our
+ * Infrastructure ..
+ */
+ vhdl = device_sysdata->vhdl;
+ for (idx = 0; idx < PCI_ROM_RESOURCE; idx++) {
+ size = 0;
+ size = device_dev->resource[idx].end -
+ device_dev->resource[idx].start;
+ if (size) {
+res = 0;
+res = pciio_config_get(vhdl, (unsigned) PCI_BASE_ADDRESS_0 + idx, 4);
+printk("Before pciio_pio_addr Base address %d = 0x%lx\n", idx, res);
+
+ printk(" Changing device %d:%d resource start address from 0x%lx",
+ PCI_SLOT(device_dev->devfn),PCI_FUNC(device_dev->devfn),
+ device_dev->resource[idx].start);
+ device_dev->resource[idx].start =
+ (unsigned long)pciio_pio_addr(vhdl, 0,
+ PCIIO_SPACE_WIN(idx), 0, size, 0, PCIIO_BYTE_STREAM);
+ }
+ else
+ continue;
+
+ device_dev->resource[idx].end =
+ device_dev->resource[idx].start + size;
+
+ /*
+ * Adjust the addresses to go to the SWIZZLE ..
+ */
+ device_dev->resource[idx].start =
+ device_dev->resource[idx].start & 0xfffff7ffffffffff;
+ device_dev->resource[idx].end =
+ device_dev->resource[idx].end & 0xfffff7ffffffffff;
+ printk(" to 0x%lx\n", device_dev->resource[idx].start);
+res = 0;
+res = pciio_config_get(vhdl, (unsigned) PCI_BASE_ADDRESS_0 + idx, 4);
+printk("After pciio_pio_addr Base address %d = 0x%lx\n", idx, res);
+
+ if (device_dev->resource[idx].flags & IORESOURCE_IO)
+ cmd |= PCI_COMMAND_IO;
+ else if (device_dev->resource[idx].flags & IORESOURCE_MEM)
+ cmd |= PCI_COMMAND_MEMORY;
+ }
+ /*
+ * Now handle the ROM resource ..
+ */
+ size = device_dev->resource[PCI_ROM_RESOURCE].end -
+ device_dev->resource[PCI_ROM_RESOURCE].start;
+ printk(" Changing device %d:%d ROM resource start address from 0x%lx",
+ PCI_SLOT(device_dev->devfn),PCI_FUNC(device_dev->devfn),
+ device_dev->resource[PCI_ROM_RESOURCE].start);
+ device_dev->resource[PCI_ROM_RESOURCE].start =
+ (unsigned long) pciio_pio_addr(vhdl, 0, PCIIO_SPACE_ROM, 0,
+ size, 0, PCIIO_BYTE_STREAM);
+ device_dev->resource[PCI_ROM_RESOURCE].end =
+ device_dev->resource[PCI_ROM_RESOURCE].start + size;
+
+ /*
+ * go through synergy swizzled space
+ */
+ device_dev->resource[PCI_ROM_RESOURCE].start &= 0xfffff7ffffffffffUL;
+ device_dev->resource[PCI_ROM_RESOURCE].end &= 0xfffff7ffffffffffUL;
+
+ /*
+ * Update the Command Word on the Card.
+ */
+ cmd |= PCI_COMMAND_MASTER; /* If the device doesn't support */
+ /* bit gets dropped .. no harm */
+ pci_write_config_word(device_dev, PCI_COMMAND, cmd);
+
+ printk(" to 0x%lx\n", device_dev->resource[PCI_ROM_RESOURCE].start);
+
+ /*
+ * Set the irq correctly.
+ * Bits 7:3 = slot
+ * Bits 2:0 = function
+ *
+ * In the IRQ we will have:
+ * Bits 24:16 = bus number
+ * Bits 15:8 = slot|func number
+ */
+ irq = 0;
+ irq = (irq | (device_dev->devfn << 8));
+ irq = (irq | ( (device_dev->bus->number & 0xff) << 16) );
+ device_dev->irq = irq;
+printk("sn1_pci_fixup: slot= %d fn= %d vendor= 0x%x device= 0x%x irq= 0x%x\n",
+PCI_SLOT(device_dev->devfn),PCI_FUNC(device_dev->devfn),device_dev->vendor,
+device_dev->device, device_dev->irq);
+
+ }
+#endif /* REAL_HARDWARE */
+#if 0
+
+{
+ devfs_handle_t bridge_vhdl = pci_bus_to_vertex(0);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) hwgraph_fastinfo_get(bridge_vhdl);
+ bridge_t *bridge = pcibr_soft->bs_base;
+
+printk("After Changing PIO Map Address:\n");
+ printk("pci_fixup_ioc3: Before devreg fixup\n");
+ printk("pci_fixup_ioc3: Devreg 0 0x%x\n", bridge->b_device[0].reg);
+ printk("pci_fixup_ioc3: Devreg 1 0x%x\n", bridge->b_device[1].reg);
+ printk("pci_fixup_ioc3: Devreg 2 0x%x\n", bridge->b_device[2].reg);
+ printk("pci_fixup_ioc3: Devreg 3 0x%x\n", bridge->b_device[3].reg);
+ printk("pci_fixup_ioc3: Devreg 4 0x%x\n", bridge->b_device[4].reg);
+ printk("pci_fixup_ioc3: Devreg 5 0x%x\n", bridge->b_device[5].reg);
+ printk("pci_fixup_ioc3: Devreg 6 0x%x\n", bridge->b_device[6].reg);
+ printk("pci_fixup_ioc3: Devreg 7 0x%x\n", bridge->b_device[7].reg);
+}
+#endif
+
+}
+
+/*
+ * pci_bus_map_create() - Called by pci_bus_to_hcl_cvlink() to finish the job.
+ */
+static int
+pci_bus_map_create(devfs_handle_t xtalk)
+{
+
+ devfs_handle_t master_node_vertex = NULL;
+ devfs_handle_t xwidget = NULL;
+ devfs_handle_t pci_bus = NULL;
+ hubinfo_t hubinfo = NULL;
+ xwidgetnum_t widgetnum;
+ char pathname[128];
+ graph_error_t rv;
+
+ /*
+ * Loop throught this vertex and get the Xwidgets ..
+ */
+ for (widgetnum = HUB_WIDGET_ID_MIN; widgetnum <= HUB_WIDGET_ID_MAX; widgetnum++) {
+ sprintf(pathname, "%d", widgetnum);
+ xwidget = NULL;
+
+ rv = hwgraph_traverse(xtalk, pathname, &xwidget);
+ if ( (rv != GRAPH_SUCCESS) ) {
+ if (!xwidget)
+ continue;
+ }
+
+ sprintf(pathname, "%d/"EDGE_LBL_PCI, widgetnum);
+ pci_bus = NULL;
+ if (hwgraph_traverse(xtalk, pathname, &pci_bus) != GRAPH_SUCCESS)
+ if (!pci_bus)
+ continue;
+
+ /*
+ * Assign the correct bus number and also the nasid of this
+ * pci Xwidget.
+ *
+ * Should not be any race here ...
+ */
+ num_bridges++;
+ busnum_to_xwidget[num_bridges - 1] = xwidget;
+
+ /*
+ * Get the master node and from there get the NASID.
+ */
+ master_node_vertex = device_master_get(xwidget);
+ if (!master_node_vertex) {
+ printk(" **** pci_bus_map_create: Unable to get .master for vertex 0x%p **** \n", xwidget);
+ }
+
+ hubinfo_get(master_node_vertex, &hubinfo);
+ if (!hubinfo) {
+ printk(" **** pci_bus_map_create: Unable to get hubinfo for master node vertex 0x%p ****\n", master_node_vertex);
+ return(1);
+ } else {
+ busnum_to_nid[num_bridges - 1] = hubinfo->h_nasid;
+ }
+
+ printk("pci_bus_map_create: Found Hub nasid %d PCI Xwidget 0x%p widgetnum= %d\n", hubinfo->h_nasid, xwidget, widgetnum);
+ }
+
+ return(0);
+}
+
+/*
+ * pci_bus_to_hcl_cvlink() - This routine is called after SGI IO Infrastructure
+ * initialization has completed to set up the mappings between Xbridge
+ * and logical pci bus numbers. We also set up the NASID for each of these
+ * xbridges.
+ *
+ * Must be called before pci_init() is invoked.
+ */
+int
+pci_bus_to_hcl_cvlink(void)
+{
+
+ devfs_handle_t devfs_hdl = NULL;
+ devfs_handle_t module_comp = NULL;
+ devfs_handle_t node = NULL;
+ devfs_handle_t xtalk = NULL;
+ graph_vertex_place_t placeptr = EDGE_PLACE_WANT_REAL_EDGES;
+ int rv = 0;
+ char name[256];
+
+ /*
+ * Iterate throught each xtalk links in the system ..
+ * /hw/module/001c01/node/xtalk/ 8|9|10|11|12|13|14|15
+ *
+ * /hw/module/001c01/node/xtalk/15 -> /hw/module/001c01/Ibrick/xtalk/15
+ *
+ * What if it is not pci?
+ */
+ devfs_hdl = hwgraph_path_to_vertex("/dev/hw/module");
+
+ /*
+ * Loop throught this directory "/devfs/hw/module/" and get each
+ * of it's entry.
+ */
+ while (1) {
+
+ /* Get vertex of component /dev/hw/<module_number> */
+ memset((char *)name, '0', 256);
+ module_comp = NULL;
+ rv = hwgraph_edge_get_next(devfs_hdl, (char *)name, &module_comp, (uint *)&placeptr);
+ if ((rv == 0) && (module_comp)) {
+ /* Found a valid entry */
+ node = NULL;
+ rv = hwgraph_edge_get(module_comp, "node", &node);
+
+ } else {
+ printk("pci_bus_to_hcl_cvlink: No more Module Component.\n");
+ return(0);
+ }
+
+ if ( (rv != 0) || (!node) ){
+ printk("pci_bus_to_hcl_cvlink: Module Component does not have node vertex.\n");
+ continue;
+ } else {
+ xtalk = NULL;
+ rv = hwgraph_edge_get(node, "xtalk", &xtalk);
+ if ( (rv != 0) || (xtalk == NULL) ){
+ printk("pci_bus_to_hcl_cvlink: Node has no xtalk vertex.\n");
+ continue;
+ }
+ }
+
+ printk("pci_bus_to_hcl_cvlink: Found Module %s node vertex = 0x%p xtalk vertex = 0x%p\n", name, node, xtalk);
+ /*
+ * Call routine to get the existing PCI Xwidget and create
+ * the convenience link from "/devfs/hw/pci_bus/.."
+ */
+ pci_bus_map_create(xtalk);
+ }
+
+ return(0);
+}
+
+/*
+ * sgi_pci_intr_support -
+ */
+int
+sgi_pci_intr_support (unsigned int requested_irq, device_desc_t *dev_desc,
+ devfs_handle_t *bus_vertex, pciio_intr_line_t *lines,
+ devfs_handle_t *device_vertex)
+
+{
+
+ unsigned int bus;
+ unsigned int devfn;
+ struct pci_dev *pci_dev;
+ unsigned char intr_pin = 0;
+ struct sn1_widget_sysdata *widget_sysdata;
+ struct sn1_device_sysdata *device_sysdata;
+
+ printk("sgi_pci_intr_support: Called with requested_irq 0x%x\n", requested_irq);
+
+ if (!dev_desc || !bus_vertex || !device_vertex) {
+ printk("sgi_pci_intr_support: Invalid parameter dev_desc 0x%p, bus_vertex 0x%p, device_vertex 0x%p\n", dev_desc, bus_vertex, device_vertex);
+ return(-1);
+ }
+
+ devfn = (requested_irq >> 8) & 0xff;
+ bus = (requested_irq >> 16) & 0xffff;
+ pci_dev = pci_find_slot(bus, devfn);
+ widget_sysdata = (struct sn1_widget_sysdata *)pci_dev->bus->sysdata;
+ *bus_vertex = widget_sysdata->vhdl;
+ device_sysdata = (struct sn1_device_sysdata *)pci_dev->sysdata;
+ *device_vertex = device_sysdata->vhdl;
+#if 0
+ {
+ int pos;
+ char dname[256];
+ pos = devfs_generate_path(*device_vertex, dname, 256);
+ printk("%s : path= %s pos %d\n", __FUNCTION__, &dname[pos], pos);
+ }
+#endif /* BRINGUP */
+
+
+ /*
+ * Get the Interrupt PIN.
+ */
+ pci_read_config_byte(pci_dev, PCI_INTERRUPT_PIN, &intr_pin);
+ *lines = (pciio_intr_line_t)intr_pin;
+
+#ifdef BRINGUP
+ /*
+ * ioc3 can't decode the PCI_INTERRUPT_PIN field of its config
+ * space so we have to set it here
+ */
+ if (pci_dev->vendor == PCI_VENDOR_ID_SGI &&
+ pci_dev->device == PCI_DEVICE_ID_SGI_IOC3 ) {
+ *lines = 1;
+ printk("%s : IOC3 HACK: lines= %d\n", __FUNCTION__, *lines);
+ }
+#endif /* BRINGUP */
+
+ /* Not supported currently */
+ *dev_desc = NULL;
+
+ printk("sgi_pci_intr_support: Device Descriptor 0x%p, Bus Vertex 0x%p, Interrupt Pins 0x%x, Device Vertex 0x%p\n", *dev_desc, *bus_vertex, *lines, *device_vertex);
+
+ return(0);
+
+}
diff --git a/arch/ia64/sn/io/pci_dma.c b/arch/ia64/sn/io/pci_dma.c
new file mode 100644
index 000000000..cab036127
--- /dev/null
+++ b/arch/ia64/sn/io/pci_dma.c
@@ -0,0 +1,334 @@
+/*
+ * 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 Leo Dagum
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/devfs_fs_kernel.h>
+
+#ifndef LANGUAGE_C
+#define LANGUAGE_C 99
+#endif
+#ifndef _LANGUAGE_C
+#define _LANGUAGE_C 99
+#endif
+#ifndef CONFIG_IA64_SGI_IO
+#define CONFIG_IA64_SGI_IO 99
+#endif
+
+#include <asm/io.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/pci/pcibr.h>
+#include <asm/sn/pci/pcibr_private.h>
+#include <asm/sn/iobus.h>
+#include <asm/sn/pci/pci_bus_cvlink.h>
+#include <asm/sn/types.h>
+#include <asm/sn/alenlist.h>
+
+/*
+ * this is REALLY ugly, blame it on gcc's lame inlining that we
+ * have to put procedures in header files
+ */
+#if LANGUAGE_C == 99
+#undef LANGUAGE_C
+#endif
+#if _LANGUAGE_C == 99
+#undef _LANGUAGE_C
+#endif
+#if CONFIG_IA64_SGI_IO == 99
+#undef CONFIG_IA64_SGI_IO
+#endif
+
+/*
+ * sn1 platform specific pci_alloc_consistent()
+ *
+ * this interface is meant for "command" streams, i.e. called only
+ * once for initializing a device, so we don't want prefetching or
+ * write gathering turned on, hence the PCIIO_DMA_CMD flag
+ */
+void *
+sn1_pci_alloc_consistent (struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle)
+{
+ void *ret;
+ int gfp = GFP_ATOMIC;
+ devfs_handle_t vhdl;
+ struct sn1_device_sysdata *device_sysdata;
+ paddr_t temp_ptr;
+
+ *dma_handle = (dma_addr_t) NULL;
+
+ /*
+ * get vertex for the device
+ */
+ device_sysdata = (struct sn1_device_sysdata *) hwdev->sysdata;
+ vhdl = device_sysdata->vhdl;
+
+ if ( ret = (void *)__get_free_pages(gfp, get_order(size)) ) {
+ memset(ret, 0, size);
+ } else {
+ return(NULL);
+ }
+
+ temp_ptr = (paddr_t) __pa(ret);
+ if (IS_PCIA64(hwdev)) {
+
+ /*
+ * This device supports 64bits DMA addresses.
+ */
+ *dma_handle = pciio_dmatrans_addr(vhdl, NULL, temp_ptr, size,
+ PCIBR_BARRIER | PCIIO_BYTE_STREAM | PCIIO_DMA_CMD
+ | PCIIO_DMA_A64 );
+ return (ret);
+ }
+
+ /*
+ * Devices that supports 32 Bits upto 63 Bits DMA Address gets
+ * 32 Bits DMA addresses.
+ *
+ * First try to get 32 Bit Direct Map Support.
+ */
+ if (IS_PCI32G(hwdev)) {
+ *dma_handle = pciio_dmatrans_addr(vhdl, NULL, temp_ptr, size,
+ PCIBR_BARRIER | PCIIO_BYTE_STREAM | PCIIO_DMA_CMD);
+ if (dma_handle) {
+ return (ret);
+ } else {
+ /*
+ * We need to map this request by using ATEs.
+ */
+ printk("sn1_pci_alloc_consistent: 32Bits DMA Page Map support not available yet!");
+ BUG();
+ }
+ }
+
+ if (IS_PCI32L(hwdev)) {
+ /*
+ * SNIA64 cannot support DMA Addresses smaller than 32 bits.
+ */
+ return (NULL);
+ }
+
+ return NULL;
+}
+
+void
+sn1_pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle)
+{
+ free_pages((unsigned long) vaddr, get_order(size));
+}
+
+/*
+ * On sn1 we use the alt_address entry of the scatterlist to store
+ * the physical address corresponding to the given virtual address
+ */
+int
+sn1_pci_map_sg (struct pci_dev *hwdev,
+ struct scatterlist *sg, int nents, int direction)
+{
+
+ int i;
+ devfs_handle_t vhdl;
+ dma_addr_t dma_addr;
+ paddr_t temp_ptr;
+ struct sn1_device_sysdata *device_sysdata;
+
+
+ if (direction == PCI_DMA_NONE)
+ BUG();
+
+ /*
+ * Handle 64 bit cards.
+ */
+ device_sysdata = (struct sn1_device_sysdata *) hwdev->sysdata;
+ vhdl = device_sysdata->vhdl;
+ for (i = 0; i < nents; i++, sg++) {
+ sg->orig_address = sg->address;
+ dma_addr = 0;
+ temp_ptr = (paddr_t) __pa(sg->address);
+
+ /*
+ * Handle the most common case 64Bit cards.
+ */
+ if (IS_PCIA64(hwdev)) {
+ dma_addr = (dma_addr_t) pciio_dmatrans_addr(vhdl, NULL,
+ temp_ptr, sg->length,
+ PCIBR_BARRIER | PCIIO_BYTE_STREAM |
+ PCIIO_DMA_CMD | PCIIO_DMA_A64 );
+ sg->address = (char *)dma_addr;
+/* printk("pci_map_sg: 64Bits hwdev %p DMA Address 0x%p alt_address 0x%p orig_address 0x%p length 0x%x\n", hwdev, sg->address, sg->alt_address, sg->orig_address, sg->length); */
+ continue;
+ }
+
+ /*
+ * Handle 32Bits and greater cards.
+ */
+ if (IS_PCI32G(hwdev)) {
+ dma_addr = (dma_addr_t) pciio_dmatrans_addr(vhdl, NULL,
+ temp_ptr, sg->length,
+ PCIBR_BARRIER | PCIIO_BYTE_STREAM |
+ PCIIO_DMA_CMD);
+ if (dma_addr) {
+ sg->address = (char *)dma_addr;
+/* printk("pci_map_single: 32Bit direct pciio_dmatrans_addr pcidev %p returns dma_addr 0x%lx\n", hwdev, dma_addr); */
+ continue;
+ } else {
+ /*
+ * We need to map this request by using ATEs.
+ */
+ printk("pci_map_single: 32Bits DMA Page Map support not available yet!");
+ BUG();
+
+ }
+ }
+ }
+
+ return nents;
+
+}
+
+/*
+ * Unmap a set of streaming mode DMA translations.
+ * Again, cpu read rules concerning calls here are the same as for
+ * pci_unmap_single() above.
+ */
+void
+sn1_pci_unmap_sg (struct pci_dev *hwdev, struct scatterlist *sg, int nelems, int direction)
+{
+ int i;
+
+ if (direction == PCI_DMA_NONE)
+ BUG();
+ for (i = 0; i < nelems; i++, sg++)
+ if (sg->orig_address != sg->address) {
+ /* phys_to_virt((dma_addr_t)sg->address | ~0x80000000); */
+ sg->address = sg->orig_address;
+ sg->orig_address = 0;
+ }
+}
+
+/*
+ * We map this to the one step pciio_dmamap_trans interface rather than
+ * the two step pciio_dmamap_alloc/pciio_dmamap_addr because we have
+ * no way of saving the dmamap handle from the alloc to later free
+ * (which is pretty much unacceptable).
+ *
+ * TODO: simplify our interface;
+ * get rid of dev_desc and vhdl (seems redundant given a pci_dev);
+ * figure out how to save dmamap handle so can use two step.
+ */
+dma_addr_t sn1_pci_map_single (struct pci_dev *hwdev,
+ void *ptr, size_t size, int direction)
+{
+ devfs_handle_t vhdl;
+ dma_addr_t dma_addr;
+ paddr_t temp_ptr;
+ struct sn1_device_sysdata *device_sysdata;
+
+
+ if (direction == PCI_DMA_NONE)
+ BUG();
+
+ if (IS_PCI32L(hwdev)) {
+ /*
+ * SNIA64 cannot support DMA Addresses smaller than 32 bits.
+ */
+ return ((dma_addr_t) NULL);
+ }
+
+ /*
+ * find vertex for the device
+ */
+ device_sysdata = (struct sn1_device_sysdata *)hwdev->sysdata;
+ vhdl = device_sysdata->vhdl;
+/* printk("pci_map_single: Called vhdl = 0x%p ptr = 0x%p size = %d\n", vhdl, ptr, size); */
+ /*
+ * Call our dmamap interface
+ */
+ dma_addr = 0;
+ temp_ptr = (paddr_t) __pa(ptr);
+
+ if (IS_PCIA64(hwdev)) {
+ /*
+ * This device supports 64bits DMA addresses.
+ */
+ dma_addr = (dma_addr_t) pciio_dmatrans_addr(vhdl, NULL,
+ temp_ptr, size,
+ PCIBR_BARRIER | PCIIO_BYTE_STREAM | PCIIO_DMA_CMD
+ | PCIIO_DMA_A64 );
+/* printk("pci_map_single: 64Bit pciio_dmatrans_addr pcidev %p returns dma_addr 0x%lx\n", hwdev, dma_addr); */
+ return (dma_addr);
+ }
+
+ /*
+ * Devices that supports 32 Bits upto 63 Bits DMA Address gets
+ * 32 Bits DMA addresses.
+ *
+ * First try to get 32 Bit Direct Map Support.
+ */
+ if (IS_PCI32G(hwdev)) {
+ dma_addr = (dma_addr_t) pciio_dmatrans_addr(vhdl, NULL,
+ temp_ptr, size,
+ PCIBR_BARRIER | PCIIO_BYTE_STREAM | PCIIO_DMA_CMD);
+ if (dma_addr) {
+/* printk("pci_map_single: 32Bit direct pciio_dmatrans_addr pcidev %p returns dma_addr 0x%lx\n", hwdev, dma_addr); */
+ return (dma_addr);
+ } else {
+ /*
+ * We need to map this request by using ATEs.
+ */
+ printk("pci_map_single: 32Bits DMA Page Map support not available yet!");
+ BUG();
+ }
+ }
+
+ if (IS_PCI32L(hwdev)) {
+ /*
+ * SNIA64 cannot support DMA Addresses smaller than 32 bits.
+ */
+ return ((dma_addr_t) NULL);
+ }
+
+ return ((dma_addr_t) NULL);
+
+}
+
+void
+sn1_pci_unmap_single (struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size, int direction)
+{
+ if (direction == PCI_DMA_NONE)
+ BUG();
+ /* Nothing to do */
+}
+
+void
+sn1_pci_dma_sync_single (struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction)
+{
+ if (direction == PCI_DMA_NONE)
+ BUG();
+ /* Nothing to do */
+}
+
+void
+sn1_pci_dma_sync_sg (struct pci_dev *hwdev, struct scatterlist *sg, int nelems, int direction)
+{
+ if (direction == PCI_DMA_NONE)
+ BUG();
+ /* Nothing to do */
+}
+
+unsigned long
+sn1_dma_address (struct scatterlist *sg)
+{
+ return (sg->address);
+}
diff --git a/arch/ia64/sn/io/pcibr.c b/arch/ia64/sn/io/pcibr.c
new file mode 100644
index 000000000..e5279fefd
--- /dev/null
+++ b/arch/ia64/sn/io/pcibr.c
@@ -0,0 +1,9572 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#ifdef BRINGUP
+int NeedXbridgeSwap = 0;
+#endif
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/arch.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/cmn_err.h>
+#include <asm/sn/xtalk/xwidget.h>
+#include <asm/sn/pci/bridge.h>
+#include <asm/sn/pci/pciio.h>
+#include <asm/sn/pci/pcibr.h>
+#include <asm/sn/pci/pcibr_private.h>
+#include <asm/sn/pci/pci_defs.h>
+#include <asm/sn/prio.h>
+#include <asm/sn/ioerror_handling.h>
+#include <asm/sn/xtalk/xbow.h>
+#include <asm/sn/ioc3.h>
+#include <asm/sn/eeprom.h>
+#include <asm/sn/sn1/bedrock.h>
+#include <asm/sn/sn_private.h>
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+#include <asm/sn/sn1/hubio.h>
+#include <asm/sn/sn1/hubio_next.h>
+#endif
+
+#if defined(BRINGUP)
+#if 0
+#define DEBUG 1 /* To avoid lots of bad printk() formats leave off */
+#endif
+#define PCI_DEBUG 1
+#define ATTACH_DEBUG 1
+#define PCIBR_SOFT_LIST 1
+#endif
+
+#ifndef LOCAL
+#define LOCAL static
+#endif
+
+#define PCIBR_LLP_CONTROL_WAR
+#if defined (PCIBR_LLP_CONTROL_WAR)
+int pcibr_llp_control_war_cnt;
+#endif /* PCIBR_LLP_CONTROL_WAR */
+
+#define NEWAf(ptr,n,f) (ptr = kmem_zalloc((n)*sizeof (*(ptr)), (f&PCIIO_NOSLEEP)?KM_NOSLEEP:KM_SLEEP))
+#define NEWA(ptr,n) (ptr = kmem_zalloc((n)*sizeof (*(ptr)), KM_SLEEP))
+#define DELA(ptr,n) (kfree(ptr))
+
+#define NEWf(ptr,f) NEWAf(ptr,1,f)
+#define NEW(ptr) NEWA(ptr,1)
+#define DEL(ptr) DELA(ptr,1)
+
+int pcibr_devflag = D_MP;
+
+#define F(s,n) { 1l<<(s),-(s), n }
+
+struct reg_desc bridge_int_status_desc[] =
+{
+ F(31, "MULTI_ERR"),
+ F(30, "PMU_ESIZE_EFAULT"),
+ F(29, "UNEXPECTED_RESP"),
+ F(28, "BAD_XRESP_PACKET"),
+ F(27, "BAD_XREQ_PACKET"),
+ F(26, "RESP_XTALK_ERROR"),
+ F(25, "REQ_XTALK_ERROR"),
+ F(24, "INVALID_ADDRESS"),
+ F(23, "UNSUPPORTED_XOP"),
+ F(22, "XREQ_FIFO_OFLOW"),
+ F(21, "LLP_REC_SNERROR"),
+ F(20, "LLP_REC_CBERROR"),
+ F(19, "LLP_RCTY"),
+ F(18, "LLP_TX_RETRY"),
+ F(17, "LLP_TCTY"),
+ F(16, "SSRAM_PERR"),
+ F(15, "PCI_ABORT"),
+ F(14, "PCI_PARITY"),
+ F(13, "PCI_SERR"),
+ F(12, "PCI_PERR"),
+ F(11, "PCI_MASTER_TOUT"),
+ F(10, "PCI_RETRY_CNT"),
+ F(9, "XREAD_REQ_TOUT"),
+ F(8, "GIO_BENABLE_ERR"),
+ F(7, "INT7"),
+ F(6, "INT6"),
+ F(5, "INT5"),
+ F(4, "INT4"),
+ F(3, "INT3"),
+ F(2, "INT2"),
+ F(1, "INT1"),
+ F(0, "INT0"),
+ {0}
+};
+
+struct reg_values space_v[] =
+{
+ {PCIIO_SPACE_NONE, "none"},
+ {PCIIO_SPACE_ROM, "ROM"},
+ {PCIIO_SPACE_IO, "I/O"},
+ {PCIIO_SPACE_MEM, "MEM"},
+ {PCIIO_SPACE_MEM32, "MEM(32)"},
+ {PCIIO_SPACE_MEM64, "MEM(64)"},
+ {PCIIO_SPACE_CFG, "CFG"},
+ {PCIIO_SPACE_WIN(0), "WIN(0)"},
+ {PCIIO_SPACE_WIN(1), "WIN(1)"},
+ {PCIIO_SPACE_WIN(2), "WIN(2)"},
+ {PCIIO_SPACE_WIN(3), "WIN(3)"},
+ {PCIIO_SPACE_WIN(4), "WIN(4)"},
+ {PCIIO_SPACE_WIN(5), "WIN(5)"},
+ {PCIIO_SPACE_BAD, "BAD"},
+ {0}
+};
+
+struct reg_desc space_desc[] =
+{
+ {0xFF, 0, "space", 0, space_v},
+ {0}
+};
+
+#if DEBUG
+#define device_desc device_bits
+LOCAL struct reg_desc device_bits[] =
+{
+ {BRIDGE_DEV_ERR_LOCK_EN, 0, "ERR_LOCK_EN"},
+ {BRIDGE_DEV_PAGE_CHK_DIS, 0, "PAGE_CHK_DIS"},
+ {BRIDGE_DEV_FORCE_PCI_PAR, 0, "FORCE_PCI_PAR"},
+ {BRIDGE_DEV_VIRTUAL_EN, 0, "VIRTUAL_EN"},
+ {BRIDGE_DEV_PMU_WRGA_EN, 0, "PMU_WRGA_EN"},
+ {BRIDGE_DEV_DIR_WRGA_EN, 0, "DIR_WRGA_EN"},
+ {BRIDGE_DEV_DEV_SIZE, 0, "DEV_SIZE"},
+ {BRIDGE_DEV_RT, 0, "RT"},
+ {BRIDGE_DEV_SWAP_PMU, 0, "SWAP_PMU"},
+ {BRIDGE_DEV_SWAP_DIR, 0, "SWAP_DIR"},
+ {BRIDGE_DEV_PREF, 0, "PREF"},
+ {BRIDGE_DEV_PRECISE, 0, "PRECISE"},
+ {BRIDGE_DEV_COH, 0, "COH"},
+ {BRIDGE_DEV_BARRIER, 0, "BARRIER"},
+ {BRIDGE_DEV_GBR, 0, "GBR"},
+ {BRIDGE_DEV_DEV_SWAP, 0, "DEV_SWAP"},
+ {BRIDGE_DEV_DEV_IO_MEM, 0, "DEV_IO_MEM"},
+ {BRIDGE_DEV_OFF_MASK, BRIDGE_DEV_OFF_ADDR_SHFT, "DEV_OFF", "%x"},
+ {0}
+};
+#endif /* DEBUG */
+
+#ifdef SUPPORT_PRINTING_R_FORMAT
+LOCAL struct reg_values xio_cmd_pactyp[] =
+{
+ {0x0, "RdReq"},
+ {0x1, "RdResp"},
+ {0x2, "WrReqWithResp"},
+ {0x3, "WrResp"},
+ {0x4, "WrReqNoResp"},
+ {0x5, "Reserved(5)"},
+ {0x6, "FetchAndOp"},
+ {0x7, "Reserved(7)"},
+ {0x8, "StoreAndOp"},
+ {0x9, "Reserved(9)"},
+ {0xa, "Reserved(a)"},
+ {0xb, "Reserved(b)"},
+ {0xc, "Reserved(c)"},
+ {0xd, "Reserved(d)"},
+ {0xe, "SpecialReq"},
+ {0xf, "SpecialResp"},
+ {0}
+};
+
+LOCAL struct reg_desc xio_cmd_bits[] =
+{
+ {WIDGET_DIDN, -28, "DIDN", "%x"},
+ {WIDGET_SIDN, -24, "SIDN", "%x"},
+ {WIDGET_PACTYP, -20, "PACTYP", 0, xio_cmd_pactyp},
+ {WIDGET_TNUM, -15, "TNUM", "%x"},
+ {WIDGET_COHERENT, 0, "COHERENT"},
+ {WIDGET_DS, 0, "DS"},
+ {WIDGET_GBR, 0, "GBR"},
+ {WIDGET_VBPM, 0, "VBPM"},
+ {WIDGET_ERROR, 0, "ERROR"},
+ {WIDGET_BARRIER, 0, "BARRIER"},
+ {0}
+};
+#endif /* SUPPORT_PRINTING_R_FORMAT */
+
+#if PCIBR_FREEZE_TIME || PCIBR_ATE_DEBUG
+LOCAL struct reg_desc ate_bits[] =
+{
+ {0xFFFF000000000000ull, -48, "RMF", "%x"},
+ {~(IOPGSIZE - 1) & /* may trim off some low bits */
+ 0x0000FFFFFFFFF000ull, 0, "XIO", "%x"},
+ {0x0000000000000F00ull, -8, "port", "%x"},
+ {0x0000000000000010ull, 0, "Barrier"},
+ {0x0000000000000008ull, 0, "Prefetch"},
+ {0x0000000000000004ull, 0, "Precise"},
+ {0x0000000000000002ull, 0, "Coherent"},
+ {0x0000000000000001ull, 0, "Valid"},
+ {0}
+};
+#endif
+
+#if PCIBR_ATE_DEBUG
+LOCAL struct reg_values ssram_sizes[] =
+{
+ {BRIDGE_CTRL_SSRAM_512K, "512k"},
+ {BRIDGE_CTRL_SSRAM_128K, "128k"},
+ {BRIDGE_CTRL_SSRAM_64K, "64k"},
+ {BRIDGE_CTRL_SSRAM_1K, "1k"},
+ {0}
+};
+
+LOCAL struct reg_desc control_bits[] =
+{
+ {BRIDGE_CTRL_FLASH_WR_EN, 0, "FLASH_WR_EN"},
+ {BRIDGE_CTRL_EN_CLK50, 0, "EN_CLK50"},
+ {BRIDGE_CTRL_EN_CLK40, 0, "EN_CLK40"},
+ {BRIDGE_CTRL_EN_CLK33, 0, "EN_CLK33"},
+ {BRIDGE_CTRL_RST_MASK, -24, "RST", "%x"},
+ {BRIDGE_CTRL_IO_SWAP, 0, "IO_SWAP"},
+ {BRIDGE_CTRL_MEM_SWAP, 0, "MEM_SWAP"},
+ {BRIDGE_CTRL_PAGE_SIZE, 0, "PAGE_SIZE"},
+ {BRIDGE_CTRL_SS_PAR_BAD, 0, "SS_PAR_BAD"},
+ {BRIDGE_CTRL_SS_PAR_EN, 0, "SS_PAR_EN"},
+ {BRIDGE_CTRL_SSRAM_SIZE_MASK, 0, "SSRAM_SIZE", 0, ssram_sizes},
+ {BRIDGE_CTRL_F_BAD_PKT, 0, "F_BAD_PKT"},
+ {BRIDGE_CTRL_LLP_XBAR_CRD_MASK, -12, "LLP_XBAR_CRD", "%d"},
+ {BRIDGE_CTRL_CLR_RLLP_CNT, 0, "CLR_RLLP_CNT"},
+ {BRIDGE_CTRL_CLR_TLLP_CNT, 0, "CLR_TLLP_CNT"},
+ {BRIDGE_CTRL_SYS_END, 0, "SYS_END"},
+ {BRIDGE_CTRL_MAX_TRANS_MASK, -4, "MAX_TRANS", "%d"},
+ {BRIDGE_CTRL_WIDGET_ID_MASK, 0, "WIDGET_ID", "%x"},
+ {0}
+};
+#endif
+
+/* kbrick widgetnum-to-bus layout */
+int p_busnum[MAX_PORT_NUM] = { /* widget# */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x0 - 0x7 */
+ 2, /* 0x8 */
+ 1, /* 0x9 */
+ 0, 0, /* 0xa - 0xb */
+ 5, /* 0xc */
+ 6, /* 0xd */
+ 4, /* 0xe */
+ 3, /* 0xf */
+};
+
+/*
+ * Additional PIO spaces per slot are
+ * recorded in this structure.
+ */
+struct pciio_piospace_s {
+ pciio_piospace_t next; /* another space for this device */
+ char free; /* 1 if free, 0 if in use */
+ pciio_space_t space; /* Which space is in use */
+ iopaddr_t start; /* Starting address of the PIO space */
+ size_t count; /* size of PIO space */
+};
+
+/* Use io spin locks. This ensures that all the PIO writes from a particular
+ * CPU to a particular IO device are synched before the start of the next
+ * set of PIO operations to the same device.
+ */
+#define pcibr_lock(pcibr_soft) io_splock(pcibr_soft->bs_lock)
+#define pcibr_unlock(pcibr_soft, s) io_spunlock(pcibr_soft->bs_lock,s)
+
+#if PCIBR_SOFT_LIST
+typedef struct pcibr_list_s *pcibr_list_p;
+struct pcibr_list_s {
+ pcibr_list_p bl_next;
+ pcibr_soft_t bl_soft;
+ devfs_handle_t bl_vhdl;
+};
+pcibr_list_p pcibr_list = 0;
+#endif
+
+typedef volatile unsigned *cfg_p;
+typedef volatile bridgereg_t *reg_p;
+
+#define INFO_LBL_PCIBR_ASIC_REV "_pcibr_asic_rev"
+
+#define PCIBR_D64_BASE_UNSET (0xFFFFFFFFFFFFFFFF)
+#define PCIBR_D32_BASE_UNSET (0xFFFFFFFF)
+
+#define PCIBR_VALID_SLOT(s) (s < 8)
+
+#ifdef SN_XXX
+extern int hub_device_flags_set(devfs_handle_t widget_dev,
+ hub_widget_flags_t flags);
+#endif
+
+extern devfs_handle_t hwgraph_root;
+extern graph_error_t hwgraph_vertex_unref(devfs_handle_t vhdl);
+extern int cap_able(uint64_t x);
+extern uint64_t rmalloc(struct map *mp, size_t size);
+extern void rmfree(struct map *mp, size_t size, uint64_t a);
+extern int hwgraph_vertex_name_get(devfs_handle_t vhdl, char *buf, uint buflen);
+extern long atoi(register char *p);
+extern void *swap_ptr(void **loc, void *new);
+extern char *dev_to_name(devfs_handle_t dev, char *buf, uint buflen);
+extern cnodeid_t nodevertex_to_cnodeid(devfs_handle_t vhdl);
+extern graph_error_t hwgraph_edge_remove(devfs_handle_t from, char *name, devfs_handle_t *toptr);
+extern struct map *rmallocmap(uint64_t mapsiz);
+extern void rmfreemap(struct map *mp);
+extern int compare_and_swap_ptr(void **location, void *old_ptr, void *new_ptr);
+extern void cmn_err_tag(int seqnumber, register int level, char *fmt, ...);
+
+
+
+/* =====================================================================
+ * Function Table of Contents
+ *
+ * The order of functions in this file has stopped
+ * making much sense. We might want to take a look
+ * at it some time and bring back some sanity, or
+ * perhaps bust this file into smaller chunks.
+ */
+
+LOCAL void do_pcibr_rrb_clear(bridge_t *, int);
+LOCAL void do_pcibr_rrb_flush(bridge_t *, int);
+LOCAL int do_pcibr_rrb_count_valid(bridge_t *, pciio_slot_t);
+LOCAL int do_pcibr_rrb_count_avail(bridge_t *, pciio_slot_t);
+LOCAL int do_pcibr_rrb_alloc(bridge_t *, pciio_slot_t, int);
+LOCAL int do_pcibr_rrb_free(bridge_t *, pciio_slot_t, int);
+
+LOCAL void do_pcibr_rrb_autoalloc(pcibr_soft_t, int, int);
+
+int pcibr_wrb_flush(devfs_handle_t);
+int pcibr_rrb_alloc(devfs_handle_t, int *, int *);
+int pcibr_rrb_check(devfs_handle_t, int *, int *, int *, int *);
+int pcibr_alloc_all_rrbs(devfs_handle_t, int, int, int, int, int, int, int, int, int);
+void pcibr_rrb_flush(devfs_handle_t);
+
+LOCAL int pcibr_try_set_device(pcibr_soft_t, pciio_slot_t, unsigned, bridgereg_t);
+void pcibr_release_device(pcibr_soft_t, pciio_slot_t, bridgereg_t);
+
+LOCAL void pcibr_clearwidint(bridge_t *);
+LOCAL void pcibr_setwidint(xtalk_intr_t);
+LOCAL int pcibr_probe_slot(bridge_t *, cfg_p, unsigned *);
+
+void pcibr_init(void);
+int pcibr_attach(devfs_handle_t);
+int pcibr_detach(devfs_handle_t);
+int pcibr_open(devfs_handle_t *, int, int, cred_t *);
+int pcibr_close(devfs_handle_t, int, int, cred_t *);
+int pcibr_map(devfs_handle_t, vhandl_t *, off_t, size_t, uint);
+int pcibr_unmap(devfs_handle_t, vhandl_t *);
+int pcibr_ioctl(devfs_handle_t, int, void *, int, struct cred *, int *);
+
+void pcibr_freeblock_sub(iopaddr_t *, iopaddr_t *, iopaddr_t, size_t);
+
+#ifndef BRINGUP
+LOCAL int pcibr_init_ext_ate_ram(bridge_t *);
+#endif
+LOCAL int pcibr_ate_alloc(pcibr_soft_t, int);
+LOCAL void pcibr_ate_free(pcibr_soft_t, int, int);
+
+LOCAL pcibr_info_t pcibr_info_get(devfs_handle_t);
+LOCAL pcibr_info_t pcibr_device_info_new(pcibr_soft_t, pciio_slot_t, pciio_function_t, pciio_vendor_id_t, pciio_device_id_t);
+LOCAL void pcibr_device_info_free(devfs_handle_t, pciio_slot_t);
+LOCAL int pcibr_device_attach(devfs_handle_t,pciio_slot_t);
+LOCAL int pcibr_device_detach(devfs_handle_t,pciio_slot_t);
+LOCAL iopaddr_t pcibr_addr_pci_to_xio(devfs_handle_t, pciio_slot_t, pciio_space_t, iopaddr_t, size_t, unsigned);
+
+pcibr_piomap_t pcibr_piomap_alloc(devfs_handle_t, device_desc_t, pciio_space_t, iopaddr_t, size_t, size_t, unsigned);
+void pcibr_piomap_free(pcibr_piomap_t);
+caddr_t pcibr_piomap_addr(pcibr_piomap_t, iopaddr_t, size_t);
+void pcibr_piomap_done(pcibr_piomap_t);
+caddr_t pcibr_piotrans_addr(devfs_handle_t, device_desc_t, pciio_space_t, iopaddr_t, size_t, unsigned);
+iopaddr_t pcibr_piospace_alloc(devfs_handle_t, device_desc_t, pciio_space_t, size_t, size_t);
+void pcibr_piospace_free(devfs_handle_t, pciio_space_t, iopaddr_t, size_t);
+
+LOCAL iopaddr_t pcibr_flags_to_d64(unsigned, pcibr_soft_t);
+LOCAL bridge_ate_t pcibr_flags_to_ate(unsigned);
+
+pcibr_dmamap_t pcibr_dmamap_alloc(devfs_handle_t, device_desc_t, size_t, unsigned);
+void pcibr_dmamap_free(pcibr_dmamap_t);
+LOCAL bridge_ate_p pcibr_ate_addr(pcibr_soft_t, int);
+LOCAL iopaddr_t pcibr_addr_xio_to_pci(pcibr_soft_t, iopaddr_t, size_t);
+iopaddr_t pcibr_dmamap_addr(pcibr_dmamap_t, paddr_t, size_t);
+alenlist_t pcibr_dmamap_list(pcibr_dmamap_t, alenlist_t, unsigned);
+void pcibr_dmamap_done(pcibr_dmamap_t);
+cnodeid_t pcibr_get_dmatrans_node(devfs_handle_t);
+iopaddr_t pcibr_dmatrans_addr(devfs_handle_t, device_desc_t, paddr_t, size_t, unsigned);
+alenlist_t pcibr_dmatrans_list(devfs_handle_t, device_desc_t, alenlist_t, unsigned);
+void pcibr_dmamap_drain(pcibr_dmamap_t);
+void pcibr_dmaaddr_drain(devfs_handle_t, paddr_t, size_t);
+void pcibr_dmalist_drain(devfs_handle_t, alenlist_t);
+iopaddr_t pcibr_dmamap_pciaddr_get(pcibr_dmamap_t);
+
+static unsigned pcibr_intr_bits(pciio_info_t info, pciio_intr_line_t lines);
+pcibr_intr_t pcibr_intr_alloc(devfs_handle_t, device_desc_t, pciio_intr_line_t, devfs_handle_t);
+void pcibr_intr_free(pcibr_intr_t);
+LOCAL void pcibr_setpciint(xtalk_intr_t);
+int pcibr_intr_connect(pcibr_intr_t, intr_func_t, intr_arg_t, void *);
+void pcibr_intr_disconnect(pcibr_intr_t);
+
+devfs_handle_t pcibr_intr_cpu_get(pcibr_intr_t);
+void pcibr_xintr_preset(void *, int, xwidgetnum_t, iopaddr_t, xtalk_intr_vector_t);
+void pcibr_intr_list_func(intr_arg_t);
+
+LOCAL void print_bridge_errcmd(uint32_t, char *);
+
+void pcibr_error_dump(pcibr_soft_t);
+uint32_t pcibr_errintr_group(uint32_t);
+LOCAL void pcibr_pioerr_check(pcibr_soft_t);
+LOCAL void pcibr_error_intr_handler(intr_arg_t);
+
+LOCAL int pcibr_addr_toslot(pcibr_soft_t, iopaddr_t, pciio_space_t *, iopaddr_t *, pciio_function_t *);
+LOCAL void pcibr_error_cleanup(pcibr_soft_t, int);
+void pcibr_device_disable(pcibr_soft_t, int);
+LOCAL int pcibr_pioerror(pcibr_soft_t, int, ioerror_mode_t, ioerror_t *);
+int pcibr_dmard_error(pcibr_soft_t, int, ioerror_mode_t, ioerror_t *);
+int pcibr_dmawr_error(pcibr_soft_t, int, ioerror_mode_t, ioerror_t *);
+LOCAL int pcibr_error_handler(error_handler_arg_t, int, ioerror_mode_t, ioerror_t *);
+int pcibr_error_devenable(devfs_handle_t, int);
+
+void pcibr_provider_startup(devfs_handle_t);
+void pcibr_provider_shutdown(devfs_handle_t);
+
+int pcibr_reset(devfs_handle_t);
+pciio_endian_t pcibr_endian_set(devfs_handle_t, pciio_endian_t, pciio_endian_t);
+int pcibr_priority_bits_set(pcibr_soft_t, pciio_slot_t, pciio_priority_t);
+pciio_priority_t pcibr_priority_set(devfs_handle_t, pciio_priority_t);
+int pcibr_device_flags_set(devfs_handle_t, pcibr_device_flags_t);
+
+LOCAL cfg_p pcibr_config_addr(devfs_handle_t, unsigned);
+uint64_t pcibr_config_get(devfs_handle_t, unsigned, unsigned);
+LOCAL uint64_t do_pcibr_config_get(cfg_p, unsigned, unsigned);
+void pcibr_config_set(devfs_handle_t, unsigned, unsigned, uint64_t);
+LOCAL void do_pcibr_config_set(cfg_p, unsigned, unsigned, uint64_t);
+
+LOCAL pcibr_hints_t pcibr_hints_get(devfs_handle_t, int);
+void pcibr_hints_fix_rrbs(devfs_handle_t);
+void pcibr_hints_dualslot(devfs_handle_t, pciio_slot_t, pciio_slot_t);
+void pcibr_hints_intr_bits(devfs_handle_t, pcibr_intr_bits_f *);
+void pcibr_set_rrb_callback(devfs_handle_t, rrb_alloc_funct_t);
+void pcibr_hints_handsoff(devfs_handle_t);
+void pcibr_hints_subdevs(devfs_handle_t, pciio_slot_t, uint64_t);
+
+LOCAL int pcibr_slot_reset(devfs_handle_t,pciio_slot_t);
+LOCAL int pcibr_slot_info_init(devfs_handle_t,pciio_slot_t);
+LOCAL int pcibr_slot_info_free(devfs_handle_t,pciio_slot_t);
+LOCAL int pcibr_slot_addr_space_init(devfs_handle_t,pciio_slot_t);
+LOCAL int pcibr_slot_device_init(devfs_handle_t, pciio_slot_t);
+LOCAL int pcibr_slot_guest_info_init(devfs_handle_t,pciio_slot_t);
+LOCAL int pcibr_slot_initial_rrb_alloc(devfs_handle_t,pciio_slot_t);
+LOCAL int pcibr_slot_call_device_attach(devfs_handle_t,pciio_slot_t);
+LOCAL int pcibr_slot_call_device_detach(devfs_handle_t,pciio_slot_t);
+
+int pcibr_slot_powerup(devfs_handle_t,pciio_slot_t);
+int pcibr_slot_shutdown(devfs_handle_t,pciio_slot_t);
+int pcibr_slot_inquiry(devfs_handle_t,pciio_slot_t);
+
+/* =====================================================================
+ * RRB management
+ */
+
+#define LSBIT(word) ((word) &~ ((word)-1))
+
+#define PCIBR_RRB_SLOT_VIRTUAL 8
+
+LOCAL void
+do_pcibr_rrb_clear(bridge_t *bridge, int rrb)
+{
+ bridgereg_t status;
+
+ /* bridge_lock must be held;
+ * this RRB must be disabled.
+ */
+
+ /* wait until RRB has no outstanduing XIO packets. */
+ while ((status = bridge->b_resp_status) & BRIDGE_RRB_INUSE(rrb)) {
+ ; /* XXX- beats on bridge. bad idea? */
+ }
+
+ /* if the RRB has data, drain it. */
+ if (status & BRIDGE_RRB_VALID(rrb)) {
+ bridge->b_resp_clear = BRIDGE_RRB_CLEAR(rrb);
+
+ /* wait until RRB is no longer valid. */
+ while ((status = bridge->b_resp_status) & BRIDGE_RRB_VALID(rrb)) {
+ ; /* XXX- beats on bridge. bad idea? */
+ }
+ }
+}
+
+LOCAL void
+do_pcibr_rrb_flush(bridge_t *bridge, int rrbn)
+{
+ reg_p rrbp = &bridge->b_rrb_map[rrbn & 1].reg;
+ bridgereg_t rrbv;
+ int shft = 4 * (rrbn >> 1);
+ unsigned ebit = BRIDGE_RRB_EN << shft;
+
+ rrbv = *rrbp;
+ if (rrbv & ebit)
+ *rrbp = rrbv & ~ebit;
+
+ do_pcibr_rrb_clear(bridge, rrbn);
+
+ if (rrbv & ebit)
+ *rrbp = rrbv;
+}
+
+/*
+ * pcibr_rrb_count_valid: count how many RRBs are
+ * marked valid for the specified PCI slot on this
+ * bridge.
+ *
+ * NOTE: The "slot" parameter for all pcibr_rrb
+ * management routines must include the "virtual"
+ * bit; when manageing both the normal and the
+ * virtual channel, separate calls to these
+ * routines must be made. To denote the virtual
+ * channel, add PCIBR_RRB_SLOT_VIRTUAL to the slot
+ * number.
+ *
+ * IMPL NOTE: The obvious algorithm is to iterate
+ * through the RRB fields, incrementing a count if
+ * the RRB is valid and matches the slot. However,
+ * it is much simpler to use an algorithm derived
+ * from the "partitioned add" idea. First, XOR in a
+ * pattern such that the fields that match this
+ * slot come up "all ones" and all other fields
+ * have zeros in the mismatching bits. Then AND
+ * together the bits in the field, so we end up
+ * with one bit turned on for each field that
+ * matched. Now we need to count these bits. This
+ * can be done either with a series of shift/add
+ * instructions or by using "tmp % 15"; I expect
+ * that the cascaded shift/add will be faster.
+ */
+
+LOCAL int
+do_pcibr_rrb_count_valid(bridge_t *bridge,
+ pciio_slot_t slot)
+{
+ bridgereg_t tmp;
+
+ tmp = bridge->b_rrb_map[slot & 1].reg;
+ tmp ^= 0x11111111 * (7 - slot / 2);
+ tmp &= (0xCCCCCCCC & tmp) >> 2;
+ tmp &= (0x22222222 & tmp) >> 1;
+ tmp += tmp >> 4;
+ tmp += tmp >> 8;
+ tmp += tmp >> 16;
+ return tmp & 15;
+}
+
+/*
+ * do_pcibr_rrb_count_avail: count how many RRBs are
+ * available to be allocated for the specified slot.
+ *
+ * IMPL NOTE: similar to the above, except we are
+ * just counting how many fields have the valid bit
+ * turned off.
+ */
+LOCAL int
+do_pcibr_rrb_count_avail(bridge_t *bridge,
+ pciio_slot_t slot)
+{
+ bridgereg_t tmp;
+
+ tmp = bridge->b_rrb_map[slot & 1].reg;
+ tmp = (0x88888888 & ~tmp) >> 3;
+ tmp += tmp >> 4;
+ tmp += tmp >> 8;
+ tmp += tmp >> 16;
+ return tmp & 15;
+}
+
+/*
+ * do_pcibr_rrb_alloc: allocate some additional RRBs
+ * for the specified slot. Returns -1 if there were
+ * insufficient free RRBs to satisfy the request,
+ * or 0 if the request was fulfilled.
+ *
+ * Note that if a request can be partially filled,
+ * it will be, even if we return failure.
+ *
+ * IMPL NOTE: again we avoid iterating across all
+ * the RRBs; instead, we form up a word containing
+ * one bit for each free RRB, then peel the bits
+ * off from the low end.
+ */
+LOCAL int
+do_pcibr_rrb_alloc(bridge_t *bridge,
+ pciio_slot_t slot,
+ int more)
+{
+ int rv = 0;
+ bridgereg_t reg, tmp, bit;
+
+ reg = bridge->b_rrb_map[slot & 1].reg;
+ tmp = (0x88888888 & ~reg) >> 3;
+ while (more-- > 0) {
+ bit = LSBIT(tmp);
+ if (!bit) {
+ rv = -1;
+ break;
+ }
+ tmp &= ~bit;
+ reg = ((reg & ~(bit * 15)) | (bit * (8 + slot / 2)));
+ }
+ bridge->b_rrb_map[slot & 1].reg = reg;
+ return rv;
+}
+
+/*
+ * do_pcibr_rrb_free: release some of the RRBs that
+ * have been allocated for the specified
+ * slot. Returns zero for success, or negative if
+ * it was unable to free that many RRBs.
+ *
+ * IMPL NOTE: We form up a bit for each RRB
+ * allocated to the slot, aligned with the VALID
+ * bitfield this time; then we peel bits off one at
+ * a time, releasing the corresponding RRB.
+ */
+LOCAL int
+do_pcibr_rrb_free(bridge_t *bridge,
+ pciio_slot_t slot,
+ int less)
+{
+ int rv = 0;
+ bridgereg_t reg, tmp, clr, bit;
+ int i;
+
+ clr = 0;
+ reg = bridge->b_rrb_map[slot & 1].reg;
+
+ /* This needs to be done otherwise the rrb's on the virtual channel
+ * for this slot won't be freed !!
+ */
+ tmp = reg & 0xbbbbbbbb;
+
+ tmp ^= (0x11111111 * (7 - slot / 2));
+ tmp &= (0x33333333 & tmp) << 2;
+ tmp &= (0x44444444 & tmp) << 1;
+ while (less-- > 0) {
+ bit = LSBIT(tmp);
+ if (!bit) {
+ rv = -1;
+ break;
+ }
+ tmp &= ~bit;
+ reg &= ~bit;
+ clr |= bit;
+ }
+ bridge->b_rrb_map[slot & 1].reg = reg;
+
+ for (i = 0; i < 8; i++)
+ if (clr & (8 << (4 * i)))
+ do_pcibr_rrb_clear(bridge, (2 * i) + (slot & 1));
+
+ return rv;
+}
+
+LOCAL void
+do_pcibr_rrb_autoalloc(pcibr_soft_t pcibr_soft,
+ int slot,
+ int more_rrbs)
+{
+ bridge_t *bridge = pcibr_soft->bs_base;
+ int got;
+
+ for (got = 0; got < more_rrbs; ++got) {
+ if (pcibr_soft->bs_rrb_res[slot & 7] > 0)
+ pcibr_soft->bs_rrb_res[slot & 7]--;
+ else if (pcibr_soft->bs_rrb_avail[slot & 1] > 0)
+ pcibr_soft->bs_rrb_avail[slot & 1]--;
+ else
+ break;
+ if (do_pcibr_rrb_alloc(bridge, slot, 1) < 0)
+ break;
+#if PCIBR_RRB_DEBUG
+ printk( "do_pcibr_rrb_autoalloc: add one to slot %d%s\n",
+ slot & 7, slot & 8 ? "v" : "");
+#endif
+ pcibr_soft->bs_rrb_valid[slot]++;
+ }
+#if PCIBR_RRB_DEBUG
+ printk("%s: %d+%d free RRBs. Allocation list:\n", pcibr_soft->bs_name,
+ pcibr_soft->bs_rrb_avail[0],
+ pcibr_soft->bs_rrb_avail[1]);
+ for (slot = 0; slot < 8; ++slot)
+ printk("\t%d+%d+%d",
+ 0xFFF & pcibr_soft->bs_rrb_valid[slot],
+ 0xFFF & pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL],
+ pcibr_soft->bs_rrb_res[slot]);
+ printk("\n");
+#endif
+}
+
+/*
+ * Device driver interface to flush the write buffers for a specified
+ * device hanging off the bridge.
+ */
+int
+pcibr_wrb_flush(devfs_handle_t pconn_vhdl)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ bridge_t *bridge = pcibr_soft->bs_base;
+ volatile bridgereg_t *wrb_flush;
+
+ wrb_flush = &(bridge->b_wr_req_buf[pciio_slot].reg);
+ while (*wrb_flush);
+
+ return(0);
+}
+/*
+ * Device driver interface to request RRBs for a specified device
+ * hanging off a Bridge. The driver requests the total number of
+ * RRBs it would like for the normal channel (vchan0) and for the
+ * "virtual channel" (vchan1). The actual number allocated to each
+ * channel is returned.
+ *
+ * If we cannot allocate at least one RRB to a channel that needs
+ * at least one, return -1 (failure). Otherwise, satisfy the request
+ * as best we can and return 0.
+ */
+int
+pcibr_rrb_alloc(devfs_handle_t pconn_vhdl,
+ int *count_vchan0,
+ int *count_vchan1)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ bridge_t *bridge = pcibr_soft->bs_base;
+ int desired_vchan0;
+ int desired_vchan1;
+ int orig_vchan0;
+ int orig_vchan1;
+ int delta_vchan0;
+ int delta_vchan1;
+ int final_vchan0;
+ int final_vchan1;
+ int avail_rrbs;
+ unsigned s;
+ int error;
+
+ /*
+ * TBD: temper request with admin info about RRB allocation,
+ * and according to demand from other devices on this Bridge.
+ *
+ * One way of doing this would be to allocate two RRBs
+ * for each device on the bus, before any drivers start
+ * asking for extras. This has the weakness that one
+ * driver might not give back an "extra" RRB until after
+ * another driver has already failed to get one that
+ * it wanted.
+ */
+
+ s = pcibr_lock(pcibr_soft);
+
+ /* How many RRBs do we own? */
+ orig_vchan0 = pcibr_soft->bs_rrb_valid[pciio_slot];
+ orig_vchan1 = pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL];
+
+ /* How many RRBs do we want? */
+ desired_vchan0 = count_vchan0 ? *count_vchan0 : orig_vchan0;
+ desired_vchan1 = count_vchan1 ? *count_vchan1 : orig_vchan1;
+
+ /* How many RRBs are free? */
+ avail_rrbs = pcibr_soft->bs_rrb_avail[pciio_slot & 1]
+ + pcibr_soft->bs_rrb_res[pciio_slot];
+
+ /* Figure desired deltas */
+ delta_vchan0 = desired_vchan0 - orig_vchan0;
+ delta_vchan1 = desired_vchan1 - orig_vchan1;
+
+ /* Trim back deltas to something
+ * that we can actually meet, by
+ * decreasing the ending allocation
+ * for whichever channel wants
+ * more RRBs. If both want the same
+ * number, cut the second channel.
+ * NOTE: do not change the allocation for
+ * a channel that was passed as NULL.
+ */
+ while ((delta_vchan0 + delta_vchan1) > avail_rrbs) {
+ if (count_vchan0 &&
+ (!count_vchan1 ||
+ ((orig_vchan0 + delta_vchan0) >
+ (orig_vchan1 + delta_vchan1))))
+ delta_vchan0--;
+ else
+ delta_vchan1--;
+ }
+
+ /* Figure final RRB allocations
+ */
+ final_vchan0 = orig_vchan0 + delta_vchan0;
+ final_vchan1 = orig_vchan1 + delta_vchan1;
+
+ /* If either channel wants RRBs but our actions
+ * would leave it with none, declare an error,
+ * but DO NOT change any RRB allocations.
+ */
+ if ((desired_vchan0 && !final_vchan0) ||
+ (desired_vchan1 && !final_vchan1)) {
+
+ error = -1;
+
+ } else {
+
+ /* Commit the allocations: free, then alloc.
+ */
+ if (delta_vchan0 < 0)
+ (void) do_pcibr_rrb_free(bridge, pciio_slot, -delta_vchan0);
+ if (delta_vchan1 < 0)
+ (void) do_pcibr_rrb_free(bridge, PCIBR_RRB_SLOT_VIRTUAL + pciio_slot, -delta_vchan1);
+
+ if (delta_vchan0 > 0)
+ (void) do_pcibr_rrb_alloc(bridge, pciio_slot, delta_vchan0);
+ if (delta_vchan1 > 0)
+ (void) do_pcibr_rrb_alloc(bridge, PCIBR_RRB_SLOT_VIRTUAL + pciio_slot, delta_vchan1);
+
+ /* Return final values to caller.
+ */
+ if (count_vchan0)
+ *count_vchan0 = final_vchan0;
+ if (count_vchan1)
+ *count_vchan1 = final_vchan1;
+
+ /* prevent automatic changes to this slot's RRBs
+ */
+ pcibr_soft->bs_rrb_fixed |= 1 << pciio_slot;
+
+ /* Track the actual allocations, release
+ * any further reservations, and update the
+ * number of available RRBs.
+ */
+
+ pcibr_soft->bs_rrb_valid[pciio_slot] = final_vchan0;
+ pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL] = final_vchan1;
+ pcibr_soft->bs_rrb_avail[pciio_slot & 1] =
+ pcibr_soft->bs_rrb_avail[pciio_slot & 1]
+ + pcibr_soft->bs_rrb_res[pciio_slot]
+ - delta_vchan0
+ - delta_vchan1;
+ pcibr_soft->bs_rrb_res[pciio_slot] = 0;
+
+#if PCIBR_RRB_DEBUG
+ printk("pcibr_rrb_alloc: slot %d set to %d+%d; %d+%d free\n",
+ pciio_slot, final_vchan0, final_vchan1,
+ pcibr_soft->bs_rrb_avail[0],
+ pcibr_soft->bs_rrb_avail[1]);
+ for (pciio_slot = 0; pciio_slot < 8; ++pciio_slot)
+ printk("\t%d+%d+%d",
+ 0xFFF & pcibr_soft->bs_rrb_valid[pciio_slot],
+ 0xFFF & pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL],
+ pcibr_soft->bs_rrb_res[pciio_slot]);
+ printk("\n");
+#endif
+
+ error = 0;
+ }
+
+ pcibr_unlock(pcibr_soft, s);
+ return error;
+}
+
+/*
+ * Device driver interface to check the current state
+ * of the RRB allocations.
+ *
+ * pconn_vhdl is your PCI connection point (specifies which
+ * PCI bus and which slot).
+ *
+ * count_vchan0 points to where to return the number of RRBs
+ * assigned to the primary DMA channel, used by all DMA
+ * that does not explicitly ask for the alternate virtual
+ * channel.
+ *
+ * count_vchan1 points to where to return the number of RRBs
+ * assigned to the secondary DMA channel, used when
+ * PCIBR_VCHAN1 and PCIIO_DMA_A64 are specified.
+ *
+ * count_reserved points to where to return the number of RRBs
+ * that have been automatically reserved for your device at
+ * startup, but which have not been assigned to a
+ * channel. RRBs must be assigned to a channel to be used;
+ * this can be done either with an explicit pcibr_rrb_alloc
+ * call, or automatically by the infrastructure when a DMA
+ * translation is constructed. Any call to pcibr_rrb_alloc
+ * will release any unassigned reserved RRBs back to the
+ * free pool.
+ *
+ * count_pool points to where to return the number of RRBs
+ * that are currently unassigned and unreserved. This
+ * number can (and will) change as other drivers make calls
+ * to pcibr_rrb_alloc, or automatically allocate RRBs for
+ * DMA beyond their initial reservation.
+ *
+ * NULL may be passed for any of the return value pointers
+ * the caller is not interested in.
+ *
+ * The return value is "0" if all went well, or "-1" if
+ * there is a problem. Additionally, if the wrong vertex
+ * is passed in, one of the subsidiary support functions
+ * could panic with a "bad pciio fingerprint."
+ */
+
+int
+pcibr_rrb_check(devfs_handle_t pconn_vhdl,
+ int *count_vchan0,
+ int *count_vchan1,
+ int *count_reserved,
+ int *count_pool)
+{
+ pciio_info_t pciio_info;
+ pciio_slot_t pciio_slot;
+ pcibr_soft_t pcibr_soft;
+ unsigned s;
+ int error = -1;
+
+ if ((pciio_info = pciio_info_get(pconn_vhdl)) &&
+ (pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info)) &&
+ ((pciio_slot = pciio_info_slot_get(pciio_info)) < 8)) {
+
+ s = pcibr_lock(pcibr_soft);
+
+ if (count_vchan0)
+ *count_vchan0 =
+ pcibr_soft->bs_rrb_valid[pciio_slot];
+
+ if (count_vchan1)
+ *count_vchan1 =
+ pcibr_soft->bs_rrb_valid[pciio_slot + PCIBR_RRB_SLOT_VIRTUAL];
+
+ if (count_reserved)
+ *count_reserved =
+ pcibr_soft->bs_rrb_res[pciio_slot];
+
+ if (count_pool)
+ *count_pool =
+ pcibr_soft->bs_rrb_avail[pciio_slot & 1];
+
+ error = 0;
+
+ pcibr_unlock(pcibr_soft, s);
+ }
+ return error;
+}
+
+/* pcibr_alloc_all_rrbs allocates all the rrbs available in the quantities
+ * requested for each of the devies. The evn_odd argument indicates whether
+ * allcoation for the odd or even rrbs is requested and next group of four pairse
+ * are the amount to assign to each device (they should sum to <= 8) and
+ * whether to set the viritual bit for that device (1 indictaes yes, 0 indicates no)
+ * the devices in order are either 0, 2, 4, 6 or 1, 3, 5, 7
+ * if even_odd is even we alloc even rrbs else we allocate odd rrbs
+ * returns 0 if no errors else returns -1
+ */
+
+int
+pcibr_alloc_all_rrbs(devfs_handle_t vhdl, int even_odd,
+ int dev_1_rrbs, int virt1, int dev_2_rrbs, int virt2,
+ int dev_3_rrbs, int virt3, int dev_4_rrbs, int virt4)
+{
+ devfs_handle_t pcibr_vhdl;
+#ifdef colin
+ pcibr_soft_t pcibr_soft;
+#else
+ pcibr_soft_t pcibr_soft = NULL;
+#endif
+ bridge_t *bridge = NULL;
+
+ uint32_t rrb_setting = 0;
+ int rrb_shift = 7;
+ uint32_t cur_rrb;
+ int dev_rrbs[4];
+ int virt[4];
+ int i, j;
+ unsigned s;
+
+ if (GRAPH_SUCCESS ==
+ hwgraph_traverse(vhdl, EDGE_LBL_PCI, &pcibr_vhdl)) {
+ pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ if (pcibr_soft)
+ bridge = pcibr_soft->bs_base;
+ hwgraph_vertex_unref(pcibr_vhdl);
+ }
+ if (bridge == NULL)
+ bridge = (bridge_t *) xtalk_piotrans_addr
+ (vhdl, NULL, 0, sizeof(bridge_t), 0);
+
+ even_odd &= 1;
+
+ dev_rrbs[0] = dev_1_rrbs;
+ dev_rrbs[1] = dev_2_rrbs;
+ dev_rrbs[2] = dev_3_rrbs;
+ dev_rrbs[3] = dev_4_rrbs;
+
+ virt[0] = virt1;
+ virt[1] = virt2;
+ virt[2] = virt3;
+ virt[3] = virt4;
+
+ if ((dev_1_rrbs + dev_2_rrbs + dev_3_rrbs + dev_4_rrbs) > 8) {
+ return -1;
+ }
+ if ((dev_1_rrbs < 0) || (dev_2_rrbs < 0) || (dev_3_rrbs < 0) || (dev_4_rrbs < 0)) {
+ return -1;
+ }
+ /* walk through rrbs */
+ for (i = 0; i < 4; i++) {
+ if (virt[i]) {
+ cur_rrb = i | 0xc;
+ cur_rrb = cur_rrb << (rrb_shift * 4);
+ rrb_shift--;
+ rrb_setting = rrb_setting | cur_rrb;
+ dev_rrbs[i] = dev_rrbs[i] - 1;
+ }
+ for (j = 0; j < dev_rrbs[i]; j++) {
+ cur_rrb = i | 0x8;
+ cur_rrb = cur_rrb << (rrb_shift * 4);
+ rrb_shift--;
+ rrb_setting = rrb_setting | cur_rrb;
+ }
+ }
+
+ if (pcibr_soft)
+ s = pcibr_lock(pcibr_soft);
+
+ bridge->b_rrb_map[even_odd].reg = rrb_setting;
+
+ if (pcibr_soft) {
+
+ pcibr_soft->bs_rrb_fixed |= 0x55 << even_odd;
+
+ /* since we've "FIXED" the allocations
+ * for these slots, we probably can dispense
+ * with tracking avail/res/valid data, but
+ * keeping it up to date helps debugging.
+ */
+
+ pcibr_soft->bs_rrb_avail[even_odd] =
+ 8 - (dev_1_rrbs + dev_2_rrbs + dev_3_rrbs + dev_4_rrbs);
+
+ pcibr_soft->bs_rrb_res[even_odd + 0] = 0;
+ pcibr_soft->bs_rrb_res[even_odd + 2] = 0;
+ pcibr_soft->bs_rrb_res[even_odd + 4] = 0;
+ pcibr_soft->bs_rrb_res[even_odd + 6] = 0;
+
+ pcibr_soft->bs_rrb_valid[even_odd + 0] = dev_1_rrbs - virt1;
+ pcibr_soft->bs_rrb_valid[even_odd + 2] = dev_2_rrbs - virt2;
+ pcibr_soft->bs_rrb_valid[even_odd + 4] = dev_3_rrbs - virt3;
+ pcibr_soft->bs_rrb_valid[even_odd + 6] = dev_4_rrbs - virt4;
+
+ pcibr_soft->bs_rrb_valid[even_odd + 0 + PCIBR_RRB_SLOT_VIRTUAL] = virt1;
+ pcibr_soft->bs_rrb_valid[even_odd + 2 + PCIBR_RRB_SLOT_VIRTUAL] = virt2;
+ pcibr_soft->bs_rrb_valid[even_odd + 4 + PCIBR_RRB_SLOT_VIRTUAL] = virt3;
+ pcibr_soft->bs_rrb_valid[even_odd + 6 + PCIBR_RRB_SLOT_VIRTUAL] = virt4;
+
+ pcibr_unlock(pcibr_soft, s);
+ }
+ return 0;
+}
+
+/*
+ * pcibr_rrb_flush: chase down all the RRBs assigned
+ * to the specified connection point, and flush
+ * them.
+ */
+void
+pcibr_rrb_flush(devfs_handle_t pconn_vhdl)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
+ bridge_t *bridge = pcibr_soft->bs_base;
+ unsigned s;
+ reg_p rrbp;
+ unsigned rrbm;
+ int i;
+ int rrbn;
+ unsigned sval;
+ unsigned mask;
+
+ sval = BRIDGE_RRB_EN | (pciio_slot >> 1);
+ mask = BRIDGE_RRB_EN | BRIDGE_RRB_PDEV;
+ rrbn = pciio_slot & 1;
+ rrbp = &bridge->b_rrb_map[rrbn].reg;
+
+ s = pcibr_lock(pcibr_soft);
+ rrbm = *rrbp;
+ for (i = 0; i < 8; ++i) {
+ if ((rrbm & mask) == sval)
+ do_pcibr_rrb_flush(bridge, rrbn);
+ rrbm >>= 4;
+ rrbn += 2;
+ }
+ pcibr_unlock(pcibr_soft, s);
+}
+
+/* =====================================================================
+ * Device(x) register management
+ */
+
+/* pcibr_try_set_device: attempt to modify Device(x)
+ * for the specified slot on the specified bridge
+ * as requested in flags, limited to the specified
+ * bits. Returns which BRIDGE bits were in conflict,
+ * or ZERO if everything went OK.
+ *
+ * Caller MUST hold pcibr_lock when calling this function.
+ */
+LOCAL int
+pcibr_try_set_device(pcibr_soft_t pcibr_soft,
+ pciio_slot_t slot,
+ unsigned flags,
+ bridgereg_t mask)
+{
+ bridge_t *bridge;
+ pcibr_soft_slot_t slotp;
+ bridgereg_t old;
+ bridgereg_t new;
+ bridgereg_t chg;
+ bridgereg_t bad;
+ bridgereg_t badpmu;
+ bridgereg_t badd32;
+ bridgereg_t badd64;
+ bridgereg_t fix;
+ unsigned s;
+ bridgereg_t xmask;
+
+ xmask = mask;
+ if (pcibr_soft->bs_xbridge) {
+ if (mask == BRIDGE_DEV_PMU_BITS)
+ xmask = XBRIDGE_DEV_PMU_BITS;
+ if (mask == BRIDGE_DEV_D64_BITS)
+ xmask = XBRIDGE_DEV_D64_BITS;
+ }
+
+ slotp = &pcibr_soft->bs_slot[slot];
+
+ s = pcibr_lock(pcibr_soft);
+
+ bridge = pcibr_soft->bs_base;
+
+ old = slotp->bss_device;
+
+ /* figure out what the desired
+ * Device(x) bits are based on
+ * the flags specified.
+ */
+
+ new = old;
+
+ /* Currently, we inherit anything that
+ * the new caller has not specified in
+ * one way or another, unless we take
+ * action here to not inherit.
+ *
+ * This is needed for the "swap" stuff,
+ * since it could have been set via
+ * pcibr_endian_set -- altho note that
+ * any explicit PCIBR_BYTE_STREAM or
+ * PCIBR_WORD_VALUES will freely override
+ * the effect of that call (and vice
+ * versa, no protection either way).
+ *
+ * I want to get rid of pcibr_endian_set
+ * in favor of tracking DMA endianness
+ * using the flags specified when DMA
+ * channels are created.
+ */
+
+#define BRIDGE_DEV_WRGA_BITS (BRIDGE_DEV_PMU_WRGA_EN | BRIDGE_DEV_DIR_WRGA_EN)
+#define BRIDGE_DEV_SWAP_BITS (BRIDGE_DEV_SWAP_PMU | BRIDGE_DEV_SWAP_DIR)
+
+ /* Do not use Barrier, Write Gather,
+ * or Prefetch unless asked.
+ * Leave everything else as it
+ * was from the last time.
+ */
+ new = new
+ & ~BRIDGE_DEV_BARRIER
+ & ~BRIDGE_DEV_WRGA_BITS
+ & ~BRIDGE_DEV_PREF
+ ;
+
+ /* Generic macro flags
+ */
+ if (flags & PCIIO_DMA_DATA) {
+#ifdef colin
+ new = new
+ & ~BRIDGE_DEV_BARRIER /* barrier off */
+ | BRIDGE_DEV_PREF; /* prefetch on */
+#else
+ new = (new
+ & ~BRIDGE_DEV_BARRIER) /* barrier off */
+ | BRIDGE_DEV_PREF; /* prefetch on */
+#endif
+
+ }
+ if (flags & PCIIO_DMA_CMD) {
+#ifdef colin
+ new = new
+ & ~BRIDGE_DEV_PREF /* prefetch off */
+ & ~BRIDGE_DEV_WRGA_BITS /* write gather off */
+ | BRIDGE_DEV_BARRIER; /* barrier on */
+#else
+ new = ((new
+ & ~BRIDGE_DEV_PREF) /* prefetch off */
+ & ~BRIDGE_DEV_WRGA_BITS) /* write gather off */
+ | BRIDGE_DEV_BARRIER; /* barrier on */
+#endif
+ }
+ /* Generic detail flags
+ */
+ if (flags & PCIIO_WRITE_GATHER)
+ new |= BRIDGE_DEV_WRGA_BITS;
+ if (flags & PCIIO_NOWRITE_GATHER)
+ new &= ~BRIDGE_DEV_WRGA_BITS;
+
+ if (flags & PCIIO_PREFETCH)
+ new |= BRIDGE_DEV_PREF;
+ if (flags & PCIIO_NOPREFETCH)
+ new &= ~BRIDGE_DEV_PREF;
+
+ if (flags & PCIBR_WRITE_GATHER)
+ new |= BRIDGE_DEV_WRGA_BITS;
+ if (flags & PCIBR_NOWRITE_GATHER)
+ new &= ~BRIDGE_DEV_WRGA_BITS;
+
+ if (flags & PCIIO_BYTE_STREAM)
+ new |= (pcibr_soft->bs_xbridge) ?
+ BRIDGE_DEV_SWAP_DIR : BRIDGE_DEV_SWAP_BITS;
+ if (flags & PCIIO_WORD_VALUES)
+ new &= (pcibr_soft->bs_xbridge) ?
+ ~BRIDGE_DEV_SWAP_DIR : ~BRIDGE_DEV_SWAP_BITS;
+
+ /* Provider-specific flags
+ */
+ if (flags & PCIBR_PREFETCH)
+ new |= BRIDGE_DEV_PREF;
+ if (flags & PCIBR_NOPREFETCH)
+ new &= ~BRIDGE_DEV_PREF;
+
+ if (flags & PCIBR_PRECISE)
+ new |= BRIDGE_DEV_PRECISE;
+ if (flags & PCIBR_NOPRECISE)
+ new &= ~BRIDGE_DEV_PRECISE;
+
+ if (flags & PCIBR_BARRIER)
+ new |= BRIDGE_DEV_BARRIER;
+ if (flags & PCIBR_NOBARRIER)
+ new &= ~BRIDGE_DEV_BARRIER;
+
+ if (flags & PCIBR_64BIT)
+ new |= BRIDGE_DEV_DEV_SIZE;
+ if (flags & PCIBR_NO64BIT)
+ new &= ~BRIDGE_DEV_DEV_SIZE;
+
+ chg = old ^ new; /* what are we changing, */
+ chg &= xmask; /* of the interesting bits */
+
+ if (chg) {
+
+ badd32 = slotp->bss_d32_uctr ? (BRIDGE_DEV_D32_BITS & chg) : 0;
+ if (pcibr_soft->bs_xbridge) {
+ badpmu = slotp->bss_pmu_uctr ? (XBRIDGE_DEV_PMU_BITS & chg) : 0;
+ badd64 = slotp->bss_d64_uctr ? (XBRIDGE_DEV_D64_BITS & chg) : 0;
+ } else {
+ badpmu = slotp->bss_pmu_uctr ? (BRIDGE_DEV_PMU_BITS & chg) : 0;
+ badd64 = slotp->bss_d64_uctr ? (BRIDGE_DEV_D64_BITS & chg) : 0;
+ }
+ bad = badpmu | badd32 | badd64;
+
+ if (bad) {
+
+ /* some conflicts can be resolved by
+ * forcing the bit on. this may cause
+ * some performance degredation in
+ * the stream(s) that want the bit off,
+ * but the alternative is not allowing
+ * the new stream at all.
+ */
+#ifdef colin
+ if (fix = bad & (BRIDGE_DEV_PRECISE |
+ BRIDGE_DEV_BARRIER)) {
+#else
+ if ( (fix = bad & (BRIDGE_DEV_PRECISE |
+ BRIDGE_DEV_BARRIER)) ){
+#endif
+ bad &= ~fix;
+ /* don't change these bits if
+ * they are already set in "old"
+ */
+ chg &= ~(fix & old);
+ }
+ /* some conflicts can be resolved by
+ * forcing the bit off. this may cause
+ * some performance degredation in
+ * the stream(s) that want the bit on,
+ * but the alternative is not allowing
+ * the new stream at all.
+ */
+#ifdef colin
+ if (fix = bad & (BRIDGE_DEV_WRGA_BITS |
+ BRIDGE_DEV_PREF)) {
+#else
+ if ( (fix = bad & (BRIDGE_DEV_WRGA_BITS |
+ BRIDGE_DEV_PREF)) ){
+#endif
+ bad &= ~fix;
+ /* don't change these bits if
+ * we wanted to turn them on.
+ */
+ chg &= ~(fix & new);
+ }
+ /* conflicts in other bits mean
+ * we can not establish this DMA
+ * channel while the other(s) are
+ * still present.
+ */
+ if (bad) {
+ pcibr_unlock(pcibr_soft, s);
+#if (DEBUG && PCIBR_DEV_DEBUG)
+ printk("pcibr_try_set_device: mod blocked by %R\n", bad, device_bits);
+#endif
+ return bad;
+ }
+ }
+ }
+ if (mask == BRIDGE_DEV_PMU_BITS)
+ slotp->bss_pmu_uctr++;
+ if (mask == BRIDGE_DEV_D32_BITS)
+ slotp->bss_d32_uctr++;
+ if (mask == BRIDGE_DEV_D64_BITS)
+ slotp->bss_d64_uctr++;
+
+ /* the value we want to write is the
+ * original value, with the bits for
+ * our selected changes flipped, and
+ * with any disabled features turned off.
+ */
+ new = old ^ chg; /* only change what we want to change */
+
+ if (slotp->bss_device == new) {
+ pcibr_unlock(pcibr_soft, s);
+ return 0;
+ }
+ bridge->b_device[slot].reg = new;
+ slotp->bss_device = new;
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+ pcibr_unlock(pcibr_soft, s);
+#if DEBUG && PCIBR_DEV_DEBUG
+ printk("pcibr Device(%d): 0x%p\n", slot, bridge->b_device[slot].reg);
+#endif
+
+ return 0;
+}
+
+void
+pcibr_release_device(pcibr_soft_t pcibr_soft,
+ pciio_slot_t slot,
+ bridgereg_t mask)
+{
+ pcibr_soft_slot_t slotp;
+ unsigned s;
+
+ slotp = &pcibr_soft->bs_slot[slot];
+
+ s = pcibr_lock(pcibr_soft);
+
+ if (mask == BRIDGE_DEV_PMU_BITS)
+ slotp->bss_pmu_uctr--;
+ if (mask == BRIDGE_DEV_D32_BITS)
+ slotp->bss_d32_uctr--;
+ if (mask == BRIDGE_DEV_D64_BITS)
+ slotp->bss_d64_uctr--;
+
+ pcibr_unlock(pcibr_soft, s);
+}
+
+/*
+ * flush write gather buffer for slot
+ */
+LOCAL void
+pcibr_device_write_gather_flush(pcibr_soft_t pcibr_soft,
+ pciio_slot_t slot)
+{
+ bridge_t *bridge;
+ unsigned s;
+ volatile uint32_t wrf;
+ s = pcibr_lock(pcibr_soft);
+ bridge = pcibr_soft->bs_base;
+ wrf = bridge->b_wr_req_buf[slot].reg;
+ pcibr_unlock(pcibr_soft, s);
+}
+
+/* =====================================================================
+ * Bridge (pcibr) "Device Driver" entry points
+ */
+
+/*
+ * pcibr_probe_slot: read a config space word
+ * while trapping any errors; reutrn zero if
+ * all went OK, or nonzero if there was an error.
+ * The value read, if any, is passed back
+ * through the valp parameter.
+ */
+LOCAL int
+pcibr_probe_slot(bridge_t *bridge,
+ cfg_p cfg,
+ unsigned *valp)
+{
+ int rv;
+ bridgereg_t old_enable, new_enable;
+
+ old_enable = bridge->b_int_enable;
+ new_enable = old_enable & ~BRIDGE_IMR_PCI_MST_TIMEOUT;
+
+ bridge->b_int_enable = new_enable;
+
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+#if defined(BRINGUP)
+ /*
+ * The xbridge doesn't clear b_err_int_view unless
+ * multi-err is cleared...
+ */
+ if (is_xbridge(bridge))
+ if (bridge->b_err_int_view & BRIDGE_ISR_PCI_MST_TIMEOUT) {
+ bridge->b_int_rst_stat = BRIDGE_IRR_MULTI_CLR;
+ }
+#endif /* BRINGUP */
+#endif /* CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 */
+
+ if (bridge->b_int_status & BRIDGE_IRR_PCI_GRP) {
+ bridge->b_int_rst_stat = BRIDGE_IRR_PCI_GRP_CLR;
+ (void) bridge->b_wid_tflush; /* flushbus */
+ }
+ rv = badaddr_val((void *) cfg, 4, valp);
+
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+#if defined(BRINGUP)
+ /*
+ * The xbridge doesn't set master timeout in b_int_status
+ * here. Fortunately it's in error_interrupt_view.
+ */
+ if (is_xbridge(bridge))
+ if (bridge->b_err_int_view & BRIDGE_ISR_PCI_MST_TIMEOUT) {
+ bridge->b_int_rst_stat = BRIDGE_IRR_MULTI_CLR;
+ rv = 1; /* unoccupied slot */
+ }
+#endif /* BRINGUP */
+#endif /* CONFIG_SGI_IP35 */
+
+ bridge->b_int_enable = old_enable;
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+
+ return rv;
+}
+
+/*
+ * pcibr_init: called once during system startup or
+ * when a loadable driver is loaded.
+ *
+ * The driver_register function should normally
+ * be in _reg, not _init. But the pcibr driver is
+ * required by devinit before the _reg routines
+ * are called, so this is an exception.
+ */
+void
+pcibr_init(void)
+{
+#if DEBUG && ATTACH_DEBUG
+ printk("pcibr_init\n");
+#endif
+
+ xwidget_driver_register(XBRIDGE_WIDGET_PART_NUM,
+ XBRIDGE_WIDGET_MFGR_NUM,
+ "pcibr_",
+ 0);
+ xwidget_driver_register(BRIDGE_WIDGET_PART_NUM,
+ BRIDGE_WIDGET_MFGR_NUM,
+ "pcibr_",
+ 0);
+}
+
+/*
+ * open/close mmap/munmap interface would be used by processes
+ * that plan to map the PCI bridge, and muck around with the
+ * registers. This is dangerous to do, and will be allowed
+ * to a select brand of programs. Typically these are
+ * diagnostics programs, or some user level commands we may
+ * write to do some weird things.
+ * To start with expect them to have root priveleges.
+ * We will ask for more later.
+ */
+/* ARGSUSED */
+int
+pcibr_open(devfs_handle_t *devp, int oflag, int otyp, cred_t *credp)
+{
+#ifndef CONFIG_IA64_SGI_IO
+ if (!_CAP_CRABLE((uint64_t)credp, (uint64_t)CAP_DEVICE_MGT))
+ return EPERM;
+#endif
+ return 0;
+}
+
+/*ARGSUSED */
+int
+pcibr_close(devfs_handle_t dev, int oflag, int otyp, cred_t *crp)
+{
+ return 0;
+}
+
+/*ARGSUSED */
+int
+pcibr_map(devfs_handle_t dev, vhandl_t *vt, off_t off, size_t len, uint prot)
+{
+ int error;
+ devfs_handle_t vhdl = dev_to_vhdl(dev);
+ devfs_handle_t pcibr_vhdl = hwgraph_connectpt_get(vhdl);
+ pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ bridge_t *bridge = pcibr_soft->bs_base;
+
+ hwgraph_vertex_unref(pcibr_vhdl);
+
+ ASSERT(pcibr_soft);
+ len = ctob(btoc(len)); /* Make len page aligned */
+ error = v_mapphys(vt, (void *) ((__psunsigned_t) bridge + off), len);
+
+ /*
+ * If the offset being mapped corresponds to the flash prom
+ * base, and if the mapping succeeds, and if the user
+ * has requested the protections to be WRITE, enable the
+ * flash prom to be written.
+ *
+ * XXX- deprecate this in favor of using the
+ * real flash driver ...
+ */
+ if (!error &&
+ ((off == BRIDGE_EXTERNAL_FLASH) ||
+ (len > BRIDGE_EXTERNAL_FLASH))) {
+ int s;
+
+ /*
+ * ensure that we write and read without any interruption.
+ * The read following the write is required for the Bridge war
+ */
+ s = splhi();
+ bridge->b_wid_control |= BRIDGE_CTRL_FLASH_WR_EN;
+ bridge->b_wid_control; /* inval addr bug war */
+ splx(s);
+ }
+ return error;
+}
+
+/*ARGSUSED */
+int
+pcibr_unmap(devfs_handle_t dev, vhandl_t *vt)
+{
+ devfs_handle_t pcibr_vhdl = hwgraph_connectpt_get((devfs_handle_t) dev);
+ pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ bridge_t *bridge = pcibr_soft->bs_base;
+
+ hwgraph_vertex_unref(pcibr_vhdl);
+
+ /*
+ * If flashprom write was enabled, disable it, as
+ * this is the last unmap.
+ */
+ if (bridge->b_wid_control & BRIDGE_CTRL_FLASH_WR_EN) {
+ int s;
+
+ /*
+ * ensure that we write and read without any interruption.
+ * The read following the write is required for the Bridge war
+ */
+ s = splhi();
+ bridge->b_wid_control &= ~BRIDGE_CTRL_FLASH_WR_EN;
+ bridge->b_wid_control; /* inval addr bug war */
+ splx(s);
+ }
+ return 0;
+}
+
+/* This is special case code used by grio. There are plans to make
+ * this a bit more general in the future, but till then this should
+ * be sufficient.
+ */
+pciio_slot_t
+pcibr_device_slot_get(devfs_handle_t dev_vhdl)
+{
+ char devname[MAXDEVNAME];
+ devfs_handle_t tdev;
+ pciio_info_t pciio_info;
+ pciio_slot_t slot = PCIIO_SLOT_NONE;
+
+ vertex_to_name(dev_vhdl, devname, MAXDEVNAME);
+
+ /* run back along the canonical path
+ * until we find a PCI connection point.
+ */
+ tdev = hwgraph_connectpt_get(dev_vhdl);
+ while (tdev != GRAPH_VERTEX_NONE) {
+ pciio_info = pciio_info_chk(tdev);
+ if (pciio_info) {
+ slot = pciio_info_slot_get(pciio_info);
+ break;
+ }
+ hwgraph_vertex_unref(tdev);
+ tdev = hwgraph_connectpt_get(tdev);
+ }
+ hwgraph_vertex_unref(tdev);
+
+ return slot;
+}
+/*==========================================================================
+ * BRIDGE PCI SLOT RELATED IOCTLs
+ */
+/*
+ * pcibr_slot_powerup
+ * Software initialize the pci slot.
+ */
+int
+pcibr_slot_powerup(devfs_handle_t pcibr_vhdl,pciio_slot_t slot)
+{
+ /* Check for the valid slot */
+ if (!PCIBR_VALID_SLOT(slot))
+ return(EINVAL);
+
+ if (pcibr_device_attach(pcibr_vhdl,slot))
+ return(EINVAL);
+
+ return(0);
+}
+/*
+ * pcibr_slot_shutdown
+ * Software shutdown the pci slot
+ */
+int
+pcibr_slot_shutdown(devfs_handle_t pcibr_vhdl,pciio_slot_t slot)
+{
+ /* Check for valid slot */
+ if (!PCIBR_VALID_SLOT(slot))
+ return(EINVAL);
+
+ if (pcibr_device_detach(pcibr_vhdl,slot))
+ return(EINVAL);
+
+ return(0);
+}
+
+char *pci_space_name[] = {"NONE",
+ "ROM",
+ "IO",
+ "",
+ "MEM",
+ "MEM32",
+ "MEM64",
+ "CFG",
+ "WIN0",
+ "WIN1",
+ "WIN2",
+ "WIN3",
+ "WIN4",
+ "WIN5",
+ "",
+ "BAD"};
+
+void
+pcibr_slot_func_info_print(pcibr_info_h pcibr_infoh, int func, int verbose)
+{
+ pcibr_info_t pcibr_info = pcibr_infoh[func];
+ char name[MAXDEVNAME];
+ int win;
+
+ if (!pcibr_info)
+ return;
+
+#ifdef SUPPORT_PRINTING_V_FORMAT
+ sprintf(name, "%v", pcibr_info->f_vertex);
+#endif
+ if (!verbose) {
+ printk("\tSlot Name : %s\n",name);
+ } else {
+ printk("\tPER-SLOT FUNCTION INFO\n");
+#ifdef SUPPORT_PRINTING_V_FORMAT
+ sprintf(name, "%v", pcibr_info->f_vertex);
+#endif
+ printk("\tSlot Name : %s\n",name);
+ printk("\tPCI Bus : %d ",pcibr_info->f_bus);
+ printk("Slot : %d ", pcibr_info->f_slot);
+ printk("Function : %d\n", pcibr_info->f_func);
+#ifdef SUPPORT_PRINTING_V_FORMAT
+ sprintf(name, "%v", pcibr_info->f_master);
+#endif
+ printk("\tBus provider : %s\n",name);
+ printk("\tProvider Fns : 0x%p ", pcibr_info->f_pops);
+ printk("Error Handler : 0x%p Arg 0x%p\n",
+ pcibr_info->f_efunc,pcibr_info->f_einfo);
+ }
+ printk("\tVendorId : 0x%x " , pcibr_info->f_vendor);
+ printk("DeviceId : 0x%x\n", pcibr_info->f_device);
+
+ printk("\n\tBase Register Info\n");
+ printk("\t\tReg#\tBase\t\tSize\t\tSpace\n");
+ for(win = 0 ; win < 6 ; win++)
+ printk("\t\t%d\t0x%lx\t%s0x%lx\t%s%s\n",
+ win,
+ pcibr_info->f_window[win].w_base,
+ pcibr_info->f_window[win].w_base >= 0x100000 ? "": "\t",
+ pcibr_info->f_window[win].w_size,
+ pcibr_info->f_window[win].w_size >= 0x100000 ? "": "\t",
+ pci_space_name[pcibr_info->f_window[win].w_space]);
+
+ printk("\t\t7\t0x%x\t%s0x%x\t%sROM\n",
+ pcibr_info->f_rbase,
+ pcibr_info->f_rbase > 0x100000 ? "" : "\t",
+ pcibr_info->f_rsize,
+ pcibr_info->f_rsize > 0x100000 ? "" : "\t");
+
+ printk("\n\tInterrupt Bit Map\n");
+ printk("\t\tPCI Int#\tBridge Pin#\n");
+ for (win = 0 ; win < 4; win++)
+ printk("\t\tINT%c\t\t%d\n",win+'A',pcibr_info->f_ibit[win]);
+ printk("\n");
+}
+
+
+void
+pcibr_slot_info_print(pcibr_soft_t pcibr_soft,
+ pciio_slot_t slot,
+ int verbose)
+{
+ pcibr_soft_slot_t pss;
+ char slot_conn_name[MAXDEVNAME];
+ int func;
+ bridge_t *bridge = pcibr_soft->bs_base;
+ bridgereg_t b_resp;
+ reg_p b_respp;
+ int dev;
+ bridgereg_t b_int_device;
+ bridgereg_t b_int_host;
+ bridgereg_t b_int_enable;
+ int pin = 0;
+ int int_bits = 0;
+
+ pss = &pcibr_soft->bs_slot[slot];
+
+ printk("\nPCI INFRASTRUCTURAL INFO FOR SLOT %d\n\n", slot);
+
+ if (verbose) {
+ printk("\tHost Present ? %s ", pss->has_host ? "yes" : "no");
+ printk("\tHost Slot : %d\n",pss->host_slot);
+#ifdef SUPPORT_PRINTING_V_FORMAT
+ sprintf(slot_conn_name, "%v", pss->slot_conn);
+#endif
+ printk("\tSlot Conn : %s\n",slot_conn_name);
+ printk("\t#Functions : %d\n",pss->bss_ninfo);
+ }
+ for (func = 0; func < pss->bss_ninfo; func++)
+ pcibr_slot_func_info_print(pss->bss_infos,func, verbose);
+ printk("\tDevio[Space:%s,Base:0x%lx,Shadow:0x%x]\n",
+ pci_space_name[pss->bss_devio.bssd_space],
+ pss->bss_devio.bssd_base,
+ pss->bss_device);
+
+ if (verbose) {
+ printk("\tUsage counts : pmu %d d32 %d d64 %d\n",
+ pss->bss_pmu_uctr,pss->bss_d32_uctr,pss->bss_d64_uctr);
+
+ printk("\tDirect Trans Info : d64_base 0x%x d64_flags 0x%x"
+ "d32_base 0x%x d32_flags 0x%x\n",
+ (unsigned int)pss->bss_d64_base, pss->bss_d64_flags,
+ (unsigned int)pss->bss_d32_base, pss->bss_d32_flags);
+
+ printk("\tExt ATEs active ? %s",
+ pss->bss_ext_ates_active ? "yes" : "no");
+ printk(" Command register : 0x%p ", pss->bss_cmd_pointer);
+ printk(" Shadow command val : 0x%x\n", pss->bss_cmd_shadow);
+ }
+
+ printk("\tSoft RRB Info[Valid %d+%d, Reserved %d]\n",
+ pcibr_soft->bs_rrb_valid[slot],
+ pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL],
+ pcibr_soft->bs_rrb_res[slot]);
+
+
+ if (slot & 1)
+ b_respp = &bridge->b_odd_resp;
+ else
+ b_respp = &bridge->b_even_resp;
+
+ b_resp = *b_respp;
+
+ printk("\n\tBridge RRB Info\n");
+ printk("\t\tRRB#\tVirtual\n");
+ for (dev = 0; dev < 8; dev++) {
+ if ((b_resp & BRIDGE_RRB_EN) &&
+ (b_resp & BRIDGE_RRB_PDEV) == (slot >> 1))
+ printk( "\t\t%d\t%s\n",
+ dev,
+ (b_resp & BRIDGE_RRB_VDEV) ? "yes" : "no");
+ b_resp >>= 4;
+
+ }
+ b_int_device = bridge->b_int_device;
+ b_int_enable = bridge->b_int_enable;
+
+ printk("\n\tBridge Interrupt Info\n"
+ "\t\tInt_device 0x%x\n\t\tInt_enable 0x%x "
+ "\n\t\tEnabled pin#s for this slot: ",
+ b_int_device,
+ b_int_enable);
+
+ while (b_int_device) {
+ if (((b_int_device & 7) == slot) &&
+ (b_int_enable & (1 << pin))) {
+ int_bits |= (1 << pin);
+ printk("%d ", pin);
+ }
+ pin++;
+ b_int_device >>= 3;
+ }
+
+ if (!int_bits)
+ printk("NONE ");
+
+ b_int_host = bridge->b_int_addr[slot].addr;
+
+ printk("\n\t\tInt_host_addr 0x%x\n",
+ b_int_host);
+
+}
+
+int verbose = 0;
+/*
+ * pcibr_slot_inquiry
+ * Print information about the pci slot maintained by the infrastructure.
+ * Current information displayed
+ * Slot hwgraph name
+ * Vendor/Device info
+ * Base register info
+ * Interrupt mapping from device pins to the bridge pins
+ * Devio register
+ * Software RRB info
+ * RRB register info
+ * In verbose mode following additional info is displayed
+ * Host/Gues info
+ * PCI Bus #,slot #, function #
+ * Slot provider hwgraph name
+ * Provider Functions
+ * Error handler
+ * DMA mapping usage counters
+ * DMA direct translation info
+ * External SSRAM workaround info
+ */
+int
+pcibr_slot_inquiry(devfs_handle_t pcibr_vhdl, pciio_slot_t slot)
+{
+ pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+
+ /* Make sure that we are dealing with a bridge device vertex */
+ if (!pcibr_soft)
+ return(EINVAL);
+
+ /* Make sure that we have a valid pci slot number or PCIIO_SLOT_NONE */
+ if ((!PCIBR_VALID_SLOT(slot)) && (slot != PCIIO_SLOT_NONE))
+ return(EINVAL);
+
+ /* Print information for the requested pci slot */
+ if (slot != PCIIO_SLOT_NONE) {
+ pcibr_slot_info_print(pcibr_soft,slot,verbose);
+ return(0);
+ }
+ /* Print information for all the slots */
+ for (slot = 0; slot < 8; slot++)
+ pcibr_slot_info_print(pcibr_soft, slot,verbose);
+ return(0);
+}
+
+/*ARGSUSED */
+int
+pcibr_ioctl(devfs_handle_t dev,
+ int cmd,
+ void *arg,
+ int flag,
+ struct cred *cr,
+ int *rvalp)
+{
+ devfs_handle_t pcibr_vhdl = hwgraph_connectpt_get((devfs_handle_t)dev);
+#ifdef colin
+ pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+#endif
+ int error = 0;
+
+ hwgraph_vertex_unref(pcibr_vhdl);
+
+ switch (cmd) {
+#ifdef colin
+ case GIOCSETBW:
+ {
+ grio_ioctl_info_t info;
+ pciio_slot_t slot = 0;
+
+ if (!cap_able((uint64_t)CAP_DEVICE_MGT)) {
+ error = EPERM;
+ break;
+ }
+ if (COPYIN(arg, &info, sizeof(grio_ioctl_info_t))) {
+ error = EFAULT;
+ break;
+ }
+#ifdef GRIO_DEBUG
+ printk("pcibr:: prev_vhdl: %d reqbw: %lld\n",
+ info.prev_vhdl, info.reqbw);
+#endif /* GRIO_DEBUG */
+
+ if ((slot = pcibr_device_slot_get(info.prev_vhdl)) ==
+ PCIIO_SLOT_NONE) {
+ error = EIO;
+ break;
+ }
+ if (info.reqbw)
+ pcibr_priority_bits_set(pcibr_soft, slot, PCI_PRIO_HIGH);
+ break;
+ }
+
+ case GIOCRELEASEBW:
+ {
+ grio_ioctl_info_t info;
+ pciio_slot_t slot = 0;
+
+ if (!cap_able(CAP_DEVICE_MGT)) {
+ error = EPERM;
+ break;
+ }
+ if (COPYIN(arg, &info, sizeof(grio_ioctl_info_t))) {
+ error = EFAULT;
+ break;
+ }
+#ifdef GRIO_DEBUG
+ printk("pcibr:: prev_vhdl: %d reqbw: %lld\n",
+ info.prev_vhdl, info.reqbw);
+#endif /* GRIO_DEBUG */
+
+ if ((slot = pcibr_device_slot_get(info.prev_vhdl)) ==
+ PCIIO_SLOT_NONE) {
+ error = EIO;
+ break;
+ }
+ if (info.reqbw)
+ pcibr_priority_bits_set(pcibr_soft, slot, PCI_PRIO_LOW);
+ break;
+ }
+#endif /* colin */
+
+ case PCIBR_SLOT_POWERUP:
+ {
+ pciio_slot_t slot;
+
+ if (!cap_able(CAP_DEVICE_MGT)) {
+ error = EPERM;
+ break;
+ }
+
+ slot = (pciio_slot_t)(uint64_t)arg;
+ error = pcibr_slot_powerup(pcibr_vhdl,slot);
+ break;
+ }
+ case PCIBR_SLOT_SHUTDOWN:
+ {
+ pciio_slot_t slot;
+
+ if (!cap_able(CAP_DEVICE_MGT)) {
+ error = EPERM;
+ break;
+ }
+
+ slot = (pciio_slot_t)(uint64_t)arg;
+ error = pcibr_slot_shutdown(pcibr_vhdl,slot);
+ break;
+ }
+ case PCIBR_SLOT_INQUIRY:
+ {
+ pciio_slot_t slot;
+
+ if (!cap_able(CAP_DEVICE_MGT)) {
+ error = EPERM;
+ break;
+ }
+
+ slot = (pciio_slot_t)(uint64_t)arg;
+ error = pcibr_slot_inquiry(pcibr_vhdl,slot);
+ break;
+ }
+ default:
+ break;
+
+ }
+
+ return error;
+}
+
+void
+pcibr_freeblock_sub(iopaddr_t *free_basep,
+ iopaddr_t *free_lastp,
+ iopaddr_t base,
+ size_t size)
+{
+ iopaddr_t free_base = *free_basep;
+ iopaddr_t free_last = *free_lastp;
+ iopaddr_t last = base + size - 1;
+
+ if ((last < free_base) || (base > free_last)); /* free block outside arena */
+
+ else if ((base <= free_base) && (last >= free_last))
+ /* free block contains entire arena */
+ *free_basep = *free_lastp = 0;
+
+ else if (base <= free_base)
+ /* free block is head of arena */
+ *free_basep = last + 1;
+
+ else if (last >= free_last)
+ /* free block is tail of arena */
+ *free_lastp = base - 1;
+
+ /*
+ * We are left with two regions: the free area
+ * in the arena "below" the block, and the free
+ * area in the arena "above" the block. Keep
+ * the one that is bigger.
+ */
+
+ else if ((base - free_base) > (free_last - last))
+ *free_lastp = base - 1; /* keep lower chunk */
+ else
+ *free_basep = last + 1; /* keep upper chunk */
+}
+
+#ifdef IRIX
+/* Convert from ssram_bits in control register to number of SSRAM entries */
+#define ATE_NUM_ENTRIES(n) _ate_info[n]
+
+/* Possible choices for number of ATE entries in Bridge's SSRAM */
+LOCAL int _ate_info[] =
+{
+ 0, /* 0 entries */
+ 8 * 1024, /* 8K entries */
+ 16 * 1024, /* 16K entries */
+ 64 * 1024 /* 64K entries */
+};
+
+#define ATE_NUM_SIZES (sizeof(_ate_info) / sizeof(int))
+#define ATE_PROBE_VALUE 0x0123456789abcdefULL
+#endif /* IRIX */
+
+/*
+ * Determine the size of this bridge's external mapping SSRAM, and set
+ * the control register appropriately to reflect this size, and initialize
+ * the external SSRAM.
+ */
+#ifndef BRINGUP
+LOCAL int
+pcibr_init_ext_ate_ram(bridge_t *bridge)
+{
+ int largest_working_size = 0;
+ int num_entries, entry;
+ int i, j;
+ bridgereg_t old_enable, new_enable;
+ int s;
+
+ if (is_xbridge(bridge))
+ return 0;
+
+ /* Probe SSRAM to determine its size. */
+ old_enable = bridge->b_int_enable;
+ new_enable = old_enable & ~BRIDGE_IMR_PCI_MST_TIMEOUT;
+ bridge->b_int_enable = new_enable;
+
+ for (i = 1; i < ATE_NUM_SIZES; i++) {
+ /* Try writing a value */
+ bridge->b_ext_ate_ram[ATE_NUM_ENTRIES(i) - 1] = ATE_PROBE_VALUE;
+
+ /* Guard against wrap */
+ for (j = 1; j < i; j++)
+ bridge->b_ext_ate_ram[ATE_NUM_ENTRIES(j) - 1] = 0;
+
+ /* See if value was written */
+ if (bridge->b_ext_ate_ram[ATE_NUM_ENTRIES(i) - 1] == ATE_PROBE_VALUE)
+ largest_working_size = i;
+ }
+ bridge->b_int_enable = old_enable;
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+
+ /*
+ * ensure that we write and read without any interruption.
+ * The read following the write is required for the Bridge war
+ */
+
+ s = splhi();
+#ifdef colin
+ bridge->b_wid_control = (bridge->b_wid_control
+ & ~BRIDGE_CTRL_SSRAM_SIZE_MASK)
+ | BRIDGE_CTRL_SSRAM_SIZE(largest_working_size);
+#endif
+ bridge->b_wid_control; /* inval addr bug war */
+ splx(s);
+
+ num_entries = ATE_NUM_ENTRIES(largest_working_size);
+
+#if PCIBR_ATE_DEBUG
+ if (num_entries)
+ printk("bridge at 0x%x: clearing %d external ATEs\n", bridge, num_entries);
+ else
+ printk("bridge at 0x%x: no externa9422l ATE RAM found\n", bridge);
+#endif
+
+ /* Initialize external mapping entries */
+ for (entry = 0; entry < num_entries; entry++)
+ bridge->b_ext_ate_ram[entry] = 0;
+
+ return (num_entries);
+}
+#endif /* !BRINGUP */
+
+/*
+ * Allocate "count" contiguous Bridge Address Translation Entries
+ * on the specified bridge to be used for PCI to XTALK mappings.
+ * Indices in rm map range from 1..num_entries. Indicies returned
+ * to caller range from 0..num_entries-1.
+ *
+ * Return the start index on success, -1 on failure.
+ */
+LOCAL int
+pcibr_ate_alloc(pcibr_soft_t pcibr_soft, int count)
+{
+ int index = 0;
+
+ index = (int) rmalloc(pcibr_soft->bs_int_ate_map, (size_t) count);
+
+ if (!index && pcibr_soft->bs_ext_ate_map)
+ index = (int) rmalloc(pcibr_soft->bs_ext_ate_map, (size_t) count);
+
+ /* rmalloc manages resources in the 1..n
+ * range, with 0 being failure.
+ * pcibr_ate_alloc manages resources
+ * in the 0..n-1 range, with -1 being failure.
+ */
+ return index - 1;
+}
+
+LOCAL void
+pcibr_ate_free(pcibr_soft_t pcibr_soft, int index, int count)
+/* Who says there's no such thing as a free meal? :-) */
+{
+ /* note the "+1" since rmalloc handles 1..n but
+ * we start counting ATEs at zero.
+ */
+ rmfree((index < pcibr_soft->bs_int_ate_size)
+ ? pcibr_soft->bs_int_ate_map
+ : pcibr_soft->bs_ext_ate_map,
+ count, index + 1);
+}
+
+LOCAL pcibr_info_t
+pcibr_info_get(devfs_handle_t vhdl)
+{
+ return (pcibr_info_t) pciio_info_get(vhdl);
+}
+
+pcibr_info_t
+pcibr_device_info_new(
+ pcibr_soft_t pcibr_soft,
+ pciio_slot_t slot,
+ pciio_function_t rfunc,
+ pciio_vendor_id_t vendor,
+ pciio_device_id_t device)
+{
+ pcibr_info_t pcibr_info;
+ pciio_function_t func;
+ int ibit;
+
+ func = (rfunc == PCIIO_FUNC_NONE) ? 0 : rfunc;
+
+ NEW(pcibr_info);
+ pciio_device_info_new(&pcibr_info->f_c,
+ pcibr_soft->bs_vhdl,
+ slot, rfunc,
+ vendor, device);
+
+ if (slot != PCIIO_SLOT_NONE) {
+
+ /*
+ * Currently favored mapping from PCI
+ * slot number and INTA/B/C/D to Bridge
+ * PCI Interrupt Bit Number:
+ *
+ * SLOT A B C D
+ * 0 0 4 0 4
+ * 1 1 5 1 5
+ * 2 2 6 2 6
+ * 3 3 7 3 7
+ * 4 4 0 4 0
+ * 5 5 1 5 1
+ * 6 6 2 6 2
+ * 7 7 3 7 3
+ *
+ * XXX- allow pcibr_hints to override default
+ * XXX- allow ADMIN to override pcibr_hints
+ */
+ for (ibit = 0; ibit < 4; ++ibit)
+ pcibr_info->f_ibit[ibit] =
+ (slot + 4 * ibit) & 7;
+
+ /*
+ * Record the info in the sparse func info space.
+ */
+printk("pcibr_device_info_new: slot= %d func= %d bss_ninfo= %d pcibr_info= 0x%p\n", slot, func, pcibr_soft->bs_slot[slot].bss_ninfo, pcibr_info);
+
+ if (func < pcibr_soft->bs_slot[slot].bss_ninfo)
+ pcibr_soft->bs_slot[slot].bss_infos[func] = pcibr_info;
+ }
+ return pcibr_info;
+}
+
+void
+pcibr_device_info_free(devfs_handle_t pcibr_vhdl, pciio_slot_t slot)
+{
+ pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ pcibr_info_t pcibr_info;
+ pciio_function_t func;
+ pcibr_soft_slot_t slotp = &pcibr_soft->bs_slot[slot];
+ int nfunc = slotp->bss_ninfo;
+
+
+ for (func = 0; func < nfunc; func++) {
+ pcibr_info = slotp->bss_infos[func];
+
+ if (!pcibr_info)
+ continue;
+
+ slotp->bss_infos[func] = 0;
+ pciio_device_info_unregister(pcibr_vhdl, &pcibr_info->f_c);
+ pciio_device_info_free(&pcibr_info->f_c);
+ DEL(pcibr_info);
+ }
+
+ /* Clear the DEVIO(x) for this slot */
+ slotp->bss_devio.bssd_space = PCIIO_SPACE_NONE;
+ slotp->bss_devio.bssd_base = PCIBR_D32_BASE_UNSET;
+ slotp->bss_device = 0;
+
+
+ /* Reset the mapping usage counters */
+ slotp->bss_pmu_uctr = 0;
+ slotp->bss_d32_uctr = 0;
+ slotp->bss_d64_uctr = 0;
+
+ /* Clear the Direct translation info */
+ slotp->bss_d64_base = PCIBR_D64_BASE_UNSET;
+ slotp->bss_d64_flags = 0;
+ slotp->bss_d32_base = PCIBR_D32_BASE_UNSET;
+ slotp->bss_d32_flags = 0;
+
+ /* Clear out shadow info necessary for the external SSRAM workaround */
+ slotp->bss_ext_ates_active = 0;
+ slotp->bss_cmd_pointer = 0;
+ slotp->bss_cmd_shadow = 0;
+
+}
+
+/*
+ * PCI_ADDR_SPACE_LIMITS_LOAD
+ * Gets the current values of
+ * pci io base,
+ * pci io last,
+ * pci low memory base,
+ * pci low memory last,
+ * pci high memory base,
+ * pci high memory last
+ */
+#define PCI_ADDR_SPACE_LIMITS_LOAD() \
+ pci_io_fb = pcibr_soft->bs_spinfo.pci_io_base; \
+ pci_io_fl = pcibr_soft->bs_spinfo.pci_io_last; \
+ pci_lo_fb = pcibr_soft->bs_spinfo.pci_swin_base; \
+ pci_lo_fl = pcibr_soft->bs_spinfo.pci_swin_last; \
+ pci_hi_fb = pcibr_soft->bs_spinfo.pci_mem_base; \
+ pci_hi_fl = pcibr_soft->bs_spinfo.pci_mem_last;
+/*
+ * PCI_ADDR_SPACE_LIMITS_STORE
+ * Sets the current values of
+ * pci io base,
+ * pci io last,
+ * pci low memory base,
+ * pci low memory last,
+ * pci high memory base,
+ * pci high memory last
+ */
+#define PCI_ADDR_SPACE_LIMITS_STORE() \
+ pcibr_soft->bs_spinfo.pci_io_base = pci_io_fb; \
+ pcibr_soft->bs_spinfo.pci_io_last = pci_io_fl; \
+ pcibr_soft->bs_spinfo.pci_swin_base = pci_lo_fb; \
+ pcibr_soft->bs_spinfo.pci_swin_last = pci_lo_fl; \
+ pcibr_soft->bs_spinfo.pci_mem_base = pci_hi_fb; \
+ pcibr_soft->bs_spinfo.pci_mem_last = pci_hi_fl;
+
+#define PCI_ADDR_SPACE_LIMITS_PRINT() \
+ printf("+++++++++++++++++++++++\n" \
+ "IO base 0x%x last 0x%x\n" \
+ "SWIN base 0x%x last 0x%x\n" \
+ "MEM base 0x%x last 0x%x\n" \
+ "+++++++++++++++++++++++\n", \
+ pcibr_soft->bs_spinfo.pci_io_base, \
+ pcibr_soft->bs_spinfo.pci_io_last, \
+ pcibr_soft->bs_spinfo.pci_swin_base, \
+ pcibr_soft->bs_spinfo.pci_swin_last, \
+ pcibr_soft->bs_spinfo.pci_mem_base, \
+ pcibr_soft->bs_spinfo.pci_mem_last);
+
+/*
+ * pcibr_slot_reset
+ * Reset the pci device in the particular slot .
+ */
+int
+pcibr_slot_reset(devfs_handle_t pcibr_vhdl,pciio_slot_t slot)
+{
+ pcibr_soft_t pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ bridge_t *bridge;
+ bridgereg_t ctrlreg,tmp;
+ volatile bridgereg_t *wrb_flush;
+
+ if (!PCIBR_VALID_SLOT(slot))
+ return(1);
+
+ if (!pcibr_soft)
+ return(1);
+
+ /* Enable the DMA operations from this device of the xtalk widget
+ * (PCI host bridge in this case).
+ */
+ xtalk_widgetdev_enable(pcibr_soft->bs_conn, slot);
+ /* Set the reset slot bit in the bridge's wid control register
+ * to reset the pci slot
+ */
+ bridge = pcibr_soft->bs_base;
+ /* Read the bridge widget control and clear out the reset pin
+ * bit for the corresponding slot.
+ */
+ tmp = ctrlreg = bridge->b_wid_control;
+ tmp &= ~BRIDGE_CTRL_RST_PIN(slot);
+ bridge->b_wid_control = tmp;
+ tmp = bridge->b_wid_control;
+ /* Restore the old control register back.
+ * NOTE : pci card gets reset when the reset pin bit
+ * changes from 0 (set above) to 1 (going to be set now).
+ */
+ bridge->b_wid_control = ctrlreg;
+
+ /* Flush the write buffers if any !! */
+ wrb_flush = &(bridge->b_wr_req_buf[slot].reg);
+ while (*wrb_flush);
+
+ return(0);
+}
+/*
+ * pcibr_slot_info_init
+ * Probe for this slot and see if it is populated.
+ * If it is populated initialize the generic pci infrastructural
+ * information associated with this particular pci device.
+ */
+int
+pcibr_slot_info_init(devfs_handle_t pcibr_vhdl,
+ pciio_slot_t slot)
+{
+ pcibr_soft_t pcibr_soft;
+ pcibr_info_h pcibr_infoh;
+ pcibr_info_t pcibr_info;
+ bridge_t *bridge;
+ cfg_p cfgw;
+ unsigned idword;
+ unsigned pfail;
+ unsigned idwords[8];
+ pciio_vendor_id_t vendor;
+ pciio_device_id_t device;
+ unsigned htype;
+ cfg_p wptr;
+ int win;
+ pciio_space_t space;
+ iopaddr_t pci_io_fb, pci_io_fl;
+ iopaddr_t pci_lo_fb, pci_lo_fl;
+ iopaddr_t pci_hi_fb, pci_hi_fl;
+ int nfunc;
+ pciio_function_t rfunc;
+ int func;
+ devfs_handle_t conn_vhdl;
+ pcibr_soft_slot_t slotp;
+
+ /* Get the basic software information required to proceed */
+ pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ if (!pcibr_soft)
+ return(1);
+
+ bridge = pcibr_soft->bs_base;
+ if (!PCIBR_VALID_SLOT(slot))
+ return(1);
+
+ slotp = &pcibr_soft->bs_slot[slot];
+
+ /* Load the current values of allocated pci address spaces */
+ PCI_ADDR_SPACE_LIMITS_LOAD();
+
+ /* If we have a host slot (eg:- IOC3 has 2 pci slots and the initialization
+ * is done by the host slot then we are done.
+ */
+ if (pcibr_soft->bs_slot[slot].has_host)
+ return(0);
+
+ /* Try to read the device-id/vendor-id from the config space */
+ cfgw = bridge->b_type0_cfg_dev[slot].l;
+
+#ifdef BRINGUP
+ if (slot < 3 || slot == 7)
+ return (0);
+ else
+#endif /* BRINGUP */
+ if (pcibr_probe_slot(bridge, cfgw, &idword))
+ return(0);
+
+ vendor = 0xFFFF & idword;
+ /* If the vendor id is not valid then the slot is not populated
+ * and we are done.
+ */
+ if (vendor == 0xFFFF)
+ return(0); /* next slot */
+
+ device = 0xFFFF & (idword >> 16);
+ htype = do_pcibr_config_get(cfgw, PCI_CFG_HEADER_TYPE, 1);
+
+ nfunc = 1;
+ rfunc = PCIIO_FUNC_NONE;
+ pfail = 0;
+
+ /* NOTE: if a card claims to be multifunction
+ * but only responds to config space 0, treat
+ * it as a unifunction card.
+ */
+
+ if (htype & 0x80) { /* MULTIFUNCTION */
+ for (func = 1; func < 8; ++func) {
+ cfgw = bridge->b_type0_cfg_dev[slot].f[func].l;
+ if (pcibr_probe_slot(bridge, cfgw, &idwords[func])) {
+ pfail |= 1 << func;
+ continue;
+ }
+ vendor = 0xFFFF & idwords[func];
+ if (vendor == 0xFFFF) {
+ pfail |= 1 << func;
+ continue;
+ }
+ nfunc = func + 1;
+ rfunc = 0;
+ }
+ cfgw = bridge->b_type0_cfg_dev[slot].l;
+ }
+ NEWA(pcibr_infoh, nfunc);
+
+ pcibr_soft->bs_slot[slot].bss_ninfo = nfunc;
+ pcibr_soft->bs_slot[slot].bss_infos = pcibr_infoh;
+
+ for (func = 0; func < nfunc; ++func) {
+ unsigned cmd_reg;
+
+ if (func) {
+ if (pfail & (1 << func))
+ continue;
+
+ idword = idwords[func];
+ cfgw = bridge->b_type0_cfg_dev[slot].f[func].l;
+
+ device = 0xFFFF & (idword >> 16);
+ htype = do_pcibr_config_get(cfgw, PCI_CFG_HEADER_TYPE, 1);
+ rfunc = func;
+ }
+ htype &= 0x7f;
+ if (htype != 0x00) {
+ PRINT_WARNING("%s pcibr: pci slot %d func %d has strange header type 0x%x\n",
+ pcibr_soft->bs_name, slot, func, htype);
+ continue;
+ }
+#if DEBUG && ATTACH_DEBUG
+ PRINT_NOTICE(
+ "%s pcibr: pci slot %d func %d: vendor 0x%x device 0x%x",
+ pcibr_soft->bs_name, slot, func, vendor, device);
+#endif
+
+ pcibr_info = pcibr_device_info_new
+ (pcibr_soft, slot, rfunc, vendor, device);
+ conn_vhdl = pciio_device_info_register(pcibr_vhdl, &pcibr_info->f_c);
+ if (func == 0)
+ slotp->slot_conn = conn_vhdl;
+
+ cmd_reg = cfgw[PCI_CFG_COMMAND / 4];
+
+ wptr = cfgw + PCI_CFG_BASE_ADDR_0 / 4;
+
+
+ for (win = 0; win < PCI_CFG_BASE_ADDRS; ++win) {
+ iopaddr_t base, mask, code;
+ size_t size;
+
+ /*
+ * GET THE BASE & SIZE OF THIS WINDOW:
+ *
+ * The low two or four bits of the BASE register
+ * determines which address space we are in; the
+ * rest is a base address. BASE registers
+ * determine windows that are power-of-two sized
+ * and naturally aligned, so we can get the size
+ * of a window by writing all-ones to the
+ * register, reading it back, and seeing which
+ * bits are used for decode; the least
+ * significant nonzero bit is also the size of
+ * the window.
+ *
+ * WARNING: someone may already have allocated
+ * some PCI space to this window, and in fact
+ * PIO may be in process at this very moment
+ * from another processor (or even from this
+ * one, if we get interrupted)! So, if the BASE
+ * already has a nonzero address, be generous
+ * and use the LSBit of that address as the
+ * size; this could overstate the window size.
+ * Usually, when one card is set up, all are set
+ * up; so, since we don't bitch about
+ * overlapping windows, we are ok.
+ *
+ * UNFORTUNATELY, some cards do not clear their
+ * BASE registers on reset. I have two heuristics
+ * that can detect such cards: first, if the
+ * decode enable is turned off for the space
+ * that the window uses, we can disregard the
+ * initial value. second, if the address is
+ * outside the range that we use, we can disregard
+ * it as well.
+ *
+ * This is looking very PCI generic. Except for
+ * knowing how many slots and where their config
+ * spaces are, this window loop and the next one
+ * could probably be shared with other PCI host
+ * adapters. It would be interesting to see if
+ * this could be pushed up into pciio, when we
+ * start supporting more PCI providers.
+ */
+#ifdef LITTLE_ENDIAN
+ base = wptr[((win*4)^4)/4];
+#else
+ base = wptr[win];
+#endif /* LITTLE_ENDIAN */
+
+ if (base & 1) {
+ /* BASE is in I/O space. */
+ space = PCIIO_SPACE_IO;
+ mask = -4;
+ code = base & 3;
+ base = base & mask;
+ if (base == 0) {
+ ; /* not assigned */
+ } else if (!(cmd_reg & PCI_CMD_IO_SPACE)) {
+ base = 0; /* decode not enabled */
+ }
+ } else {
+ /* BASE is in MEM space. */
+ space = PCIIO_SPACE_MEM;
+ mask = -16;
+ code = base & 15;
+ base = base & mask;
+ if (base == 0) {
+ ; /* not assigned */
+ } else if (!(cmd_reg & PCI_CMD_MEM_SPACE)) {
+ base = 0; /* decode not enabled */
+ } else if (base & 0xC0000000) {
+ base = 0; /* outside permissable range */
+ } else if ((code == PCI_BA_MEM_64BIT) &&
+#ifdef LITTLE_ENDIAN
+ (wptr[(((win + 1)*4)^4)/4] != 0)) {
+#else
+ (wptr[win + 1] != 0)) {
+#endif /* LITTLE_ENDIAN */
+ base = 0; /* outside permissable range */
+ }
+ }
+
+ if (base != 0) { /* estimate size */
+ size = base & -base;
+ } else { /* calculate size */
+#ifdef LITTLE_ENDIAN
+ wptr[((win*4)^4)/4] = ~0; /* turn on all bits */
+ size = wptr[((win*4)^4)/4]; /* get stored bits */
+#else
+ wptr[win] = ~0; /* turn on all bits */
+ size = wptr[win]; /* get stored bits */
+#endif /* LITTLE_ENDIAN */
+ size &= mask; /* keep addr */
+ size &= -size; /* keep lsbit */
+ if (size == 0)
+ continue;
+ }
+
+ pcibr_info->f_window[win].w_space = space;
+ pcibr_info->f_window[win].w_base = base;
+ pcibr_info->f_window[win].w_size = size;
+
+ /*
+ * If this window already has PCI space
+ * allocated for it, "subtract" that space from
+ * our running freeblocks. Don't worry about
+ * overlaps in existing allocated windows; we
+ * may be overstating their sizes anyway.
+ */
+
+ if (base && size) {
+ if (space == PCIIO_SPACE_IO) {
+ pcibr_freeblock_sub(&pci_io_fb,
+ &pci_io_fl,
+ base, size);
+ } else {
+ pcibr_freeblock_sub(&pci_lo_fb,
+ &pci_lo_fl,
+ base, size);
+ pcibr_freeblock_sub(&pci_hi_fb,
+ &pci_hi_fl,
+ base, size);
+ }
+ }
+#if defined(IOC3_VENDOR_ID_NUM) && defined(IOC3_DEVICE_ID_NUM)
+ /*
+ * IOC3 BASE_ADDR* BUG WORKAROUND
+ *
+
+ * If we write to BASE1 on the IOC3, the
+ * data in BASE0 is replaced. The
+ * original workaround was to remember
+ * the value of BASE0 and restore it
+ * when we ran off the end of the BASE
+ * registers; however, a later
+ * workaround was added (I think it was
+ * rev 1.44) to avoid setting up
+ * anything but BASE0, with the comment
+ * that writing all ones to BASE1 set
+ * the enable-parity-error test feature
+ * in IOC3's SCR bit 14.
+ *
+ * So, unless we defer doing any PCI
+ * space allocation until drivers
+ * attach, and set up a way for drivers
+ * (the IOC3 in paricular) to tell us
+ * generically to keep our hands off
+ * BASE registers, we gotta "know" about
+ * the IOC3 here.
+ *
+ * Too bad the PCI folks didn't reserve the
+ * all-zero value for 'no BASE here' (it is a
+ * valid code for an uninitialized BASE in
+ * 32-bit PCI memory space).
+ */
+
+ if ((vendor == IOC3_VENDOR_ID_NUM) &&
+ (device == IOC3_DEVICE_ID_NUM))
+ break;
+#endif
+ if (code == PCI_BA_MEM_64BIT) {
+ win++; /* skip upper half */
+#ifdef LITTLE_ENDIAN
+ wptr[((win*4)^4)/4] = 0; /* which must be zero */
+#else
+ wptr[win] = 0; /* which must be zero */
+#endif /* LITTLE_ENDIAN */
+ }
+ } /* next win */
+ } /* next func */
+
+ /* Store back the values for allocated pci address spaces */
+ PCI_ADDR_SPACE_LIMITS_STORE();
+ return(0);
+}
+
+/*
+ * pcibr_slot_info_free
+ * Remove all the pci infrastructural information associated
+ * with a particular pci device.
+ */
+int
+pcibr_slot_info_free(devfs_handle_t pcibr_vhdl,
+ pciio_slot_t slot)
+{
+ pcibr_soft_t pcibr_soft;
+ pcibr_info_h pcibr_infoh;
+ int nfunc;
+#if defined(PCI_HOTSWAP_DEBUG)
+ cfg_p cfgw;
+ bridge_t *bridge;
+ int win;
+ cfg_p wptr;
+#endif /* PCI_HOTSWAP_DEBUG */
+
+
+
+ pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
+ return(1);
+
+#if defined(PCI_HOTSWAP_DEBUG)
+ /* Clean out all the base registers */
+ bridge = pcibr_soft->bs_base;
+ cfgw = bridge->b_type0_cfg_dev[slot].l;
+ wptr = cfgw + PCI_CFG_BASE_ADDR_0 / 4;
+
+ for (win = 0; win < PCI_CFG_BASE_ADDRS; ++win)
+#ifdef LITTLE_ENDIAN
+ wptr[((win*4)^4)/4] = 0;
+#else
+ wptr[win] = 0;
+#endif /* LITTLE_ENDIAN */
+#endif /* PCI_HOTSWAP_DEBUG */
+
+ nfunc = pcibr_soft->bs_slot[slot].bss_ninfo;
+
+ pcibr_device_info_free(pcibr_vhdl, slot);
+
+ pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos;
+ DELA(pcibr_infoh,nfunc);
+ pcibr_soft->bs_slot[slot].bss_ninfo = 0;
+
+ return(0);
+
+
+}
+int as_debug = 0;
+/*
+ * pcibr_slot_addr_space_init
+ * Reserve chunks of pci address space as required by
+ * the base registers in the card.
+ */
+int
+pcibr_slot_addr_space_init(devfs_handle_t pcibr_vhdl,
+ pciio_slot_t slot)
+{
+ pcibr_soft_t pcibr_soft;
+ pcibr_info_h pcibr_infoh;
+ pcibr_info_t pcibr_info;
+ bridge_t *bridge;
+ iopaddr_t pci_io_fb, pci_io_fl;
+ iopaddr_t pci_lo_fb, pci_lo_fl;
+ iopaddr_t pci_hi_fb, pci_hi_fl;
+ size_t align;
+ iopaddr_t mask;
+ int nfunc;
+ int func;
+ int win;
+
+ pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
+ return(1);
+
+ bridge = pcibr_soft->bs_base;
+
+ /* Get the current values for the allocated pci address spaces */
+ PCI_ADDR_SPACE_LIMITS_LOAD();
+
+ if (as_debug)
+#ifdef colin
+ PCI_ADDR_SPACE_LIMITS_PRINT();
+#endif
+ /* allocate address space,
+ * for windows that have not been
+ * previously assigned.
+ */
+
+ if (pcibr_soft->bs_slot[slot].has_host)
+ return(0);
+
+ nfunc = pcibr_soft->bs_slot[slot].bss_ninfo;
+ if (nfunc < 1)
+ return(0);
+
+ pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos;
+ if (!pcibr_infoh)
+ return(0);
+
+ /*
+ * Try to make the DevIO windows not
+ * overlap by pushing the "io" and "hi"
+ * allocation areas up to the next one
+ * or two megabyte bound. This also
+ * keeps them from being zero.
+ *
+ * DO NOT do this with "pci_lo" since
+ * the entire "lo" area is only a
+ * megabyte, total ...
+ */
+ align = (slot < 2) ? 0x200000 : 0x100000;
+ mask = -align;
+ pci_io_fb = (pci_io_fb + align - 1) & mask;
+ pci_hi_fb = (pci_hi_fb + align - 1) & mask;
+
+ for (func = 0; func < nfunc; ++func) {
+ cfg_p cfgw;
+ cfg_p wptr;
+ pciio_space_t space;
+ iopaddr_t base;
+ size_t size;
+ cfg_p pci_cfg_cmd_reg_p;
+ unsigned pci_cfg_cmd_reg;
+ unsigned pci_cfg_cmd_reg_add = 0;
+
+ pcibr_info = pcibr_infoh[func];
+
+ if (!pcibr_info)
+ continue;
+
+ if (pcibr_info->f_vendor == PCIIO_VENDOR_ID_NONE)
+ continue;
+
+ cfgw = bridge->b_type0_cfg_dev[slot].f[func].l;
+ wptr = cfgw + PCI_CFG_BASE_ADDR_0 / 4;
+
+ for (win = 0; win < PCI_CFG_BASE_ADDRS; ++win) {
+
+ space = pcibr_info->f_window[win].w_space;
+ base = pcibr_info->f_window[win].w_base;
+ size = pcibr_info->f_window[win].w_size;
+
+ if (size < 1)
+ continue;
+
+ if (base >= size) {
+#if DEBUG && PCI_DEBUG
+ printk("pcibr: slot %d func %d window %d is in %d[0x%x..0x%x], alloc by prom\n",
+ slot, func, win, space, base, base + size - 1);
+#endif
+ continue; /* already allocated */
+ }
+ align = size; /* ie. 0x00001000 */
+ if (align < _PAGESZ)
+ align = _PAGESZ; /* ie. 0x00004000 */
+ mask = -align; /* ie. 0xFFFFC000 */
+
+ switch (space) {
+ case PCIIO_SPACE_IO:
+ base = (pci_io_fb + align - 1) & mask;
+ if ((base + size) > pci_io_fl) {
+ base = 0;
+ break;
+ }
+ pci_io_fb = base + size;
+ break;
+
+ case PCIIO_SPACE_MEM:
+#ifdef LITTLE_ENDIAN
+ if ((wptr[((win*4)^4)/4] & PCI_BA_MEM_LOCATION) ==
+#else
+ if ((wptr[win] & PCI_BA_MEM_LOCATION) ==
+#endif /* LITTLE_ENDIAN */
+ PCI_BA_MEM_1MEG) {
+ /* allocate from 20-bit PCI space */
+ base = (pci_lo_fb + align - 1) & mask;
+ if ((base + size) > pci_lo_fl) {
+ base = 0;
+ break;
+ }
+ pci_lo_fb = base + size;
+ } else {
+ /* allocate from 32-bit or 64-bit PCI space */
+ base = (pci_hi_fb + align - 1) & mask;
+ if ((base + size) > pci_hi_fl) {
+ base = 0;
+ break;
+ }
+ pci_hi_fb = base + size;
+ }
+ break;
+
+ default:
+ base = 0;
+#if DEBUG && PCI_DEBUG
+ printk("pcibr: slot %d window %d had bad space code %d\n",
+ slot, win, space);
+#endif
+ }
+ pcibr_info->f_window[win].w_base = base;
+#ifdef LITTLE_ENDIAN
+ wptr[((win*4)^4)/4] = base;
+ printk("Setting base address 0x%p base 0x%x\n", &(wptr[((win*4)^4)/4]), base);
+#else
+ wptr[win] = base;
+#endif /* LITTLE_ENDIAN */
+
+#if DEBUG && PCI_DEBUG
+ if (base >= size)
+ printk("pcibr: slot %d func %d window %d is in %d [0x%x..0x%x], alloc by pcibr\n",
+ slot, func, win, space, base, base + size - 1);
+ else
+ printk("pcibr: slot %d func %d window %d, unable to alloc 0x%x in 0x%p\n",
+ slot, func, win, size, space);
+#endif
+ } /* next base */
+
+ /*
+ * Allocate space for the EXPANSION ROM
+ * NOTE: DO NOT DO THIS ON AN IOC3,
+ * as it blows the system away.
+ */
+ base = size = 0;
+ if ((pcibr_soft->bs_slot[slot].bss_vendor_id != IOC3_VENDOR_ID_NUM) ||
+ (pcibr_soft->bs_slot[slot].bss_device_id != IOC3_DEVICE_ID_NUM)) {
+
+ wptr = cfgw + PCI_EXPANSION_ROM / 4;
+#ifdef LITTLE_ENDIAN
+ wptr[1] = 0xFFFFF000;
+ mask = wptr[1];
+#else
+ *wptr = 0xFFFFF000;
+ mask = *wptr;
+#endif /* LITTLE_ENDIAN */
+ if (mask & 0xFFFFF000) {
+ size = mask & -mask;
+ align = size;
+ if (align < _PAGESZ)
+ align = _PAGESZ;
+ mask = -align;
+ base = (pci_hi_fb + align - 1) & mask;
+ if ((base + size) > pci_hi_fl)
+ base = size = 0;
+ else {
+ pci_hi_fb = base + size;
+#ifdef LITTLE_ENDIAN
+ wptr[1] = base;
+#else
+ *wptr = base;
+#endif /* LITTLE_ENDIAN */
+#if DEBUG && PCI_DEBUG
+ printk("%s/%d ROM in 0x%lx..0x%lx (alloc by pcibr)\n",
+ pcibr_soft->bs_name, slot,
+ base, base + size - 1);
+#endif
+ }
+ }
+ }
+ pcibr_info->f_rbase = base;
+ pcibr_info->f_rsize = size;
+
+ /*
+ * if necessary, update the board's
+ * command register to enable decoding
+ * in the windows we added.
+ *
+ * There are some bits we always want to
+ * be sure are set.
+ */
+ pci_cfg_cmd_reg_add |= PCI_CMD_IO_SPACE;
+ pci_cfg_cmd_reg_add |= PCI_CMD_MEM_SPACE;
+ pci_cfg_cmd_reg_add |= PCI_CMD_BUS_MASTER;
+
+ pci_cfg_cmd_reg_p = cfgw + PCI_CFG_COMMAND / 4;
+ pci_cfg_cmd_reg = *pci_cfg_cmd_reg_p;
+#if PCI_FBBE /* XXX- check here to see if dev can do fast-back-to-back */
+ if (!((pci_cfg_cmd_reg >> 16) & PCI_STAT_F_BK_BK_CAP))
+ fast_back_to_back_enable = 0;
+#endif
+ pci_cfg_cmd_reg &= 0xFFFF;
+ if (pci_cfg_cmd_reg_add & ~pci_cfg_cmd_reg)
+ *pci_cfg_cmd_reg_p = pci_cfg_cmd_reg | pci_cfg_cmd_reg_add;
+
+ } /* next func */
+
+ /* Now that we have allocated new chunks of pci address spaces to this
+ * card we need to update the bookkeeping values which indicate
+ * the current pci address space allocations.
+ */
+ PCI_ADDR_SPACE_LIMITS_STORE();
+ return(0);
+}
+/*
+ * pcibr_slot_device_init
+ * Setup the device register in the bridge for this pci slot.
+ */
+int
+pcibr_slot_device_init(devfs_handle_t pcibr_vhdl,
+ pciio_slot_t slot)
+{
+ pcibr_soft_t pcibr_soft;
+ bridge_t *bridge;
+ bridgereg_t devreg;
+
+ pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
+ return(1);
+
+ bridge = pcibr_soft->bs_base;
+
+ /*
+ * Adjustments to Device(x)
+ * and init of bss_device shadow
+ */
+ devreg = bridge->b_device[slot].reg;
+ devreg &= ~BRIDGE_DEV_PAGE_CHK_DIS;
+ devreg |= BRIDGE_DEV_COH | BRIDGE_DEV_VIRTUAL_EN;
+#ifdef LITTLE_ENDIAN
+ devreg |= BRIDGE_DEV_DEV_SWAP;
+#endif
+ pcibr_soft->bs_slot[slot].bss_device = devreg;
+ bridge->b_device[slot].reg = devreg;
+
+#if DEBUG && PCI_DEBUG
+ printk("pcibr Device(%d): 0x%lx\n", slot, bridge->b_device[slot].reg);
+#endif
+
+#if DEBUG && PCI_DEBUG
+ printk("pcibr: PCI space allocation done.\n");
+#endif
+
+ return(0);
+}
+
+/*
+ * pcibr_slot_guest_info_init
+ * Setup the host/guest relations for a pci slot.
+ */
+int
+pcibr_slot_guest_info_init(devfs_handle_t pcibr_vhdl,
+ pciio_slot_t slot)
+{
+ pcibr_soft_t pcibr_soft;
+ pcibr_info_h pcibr_infoh;
+ pcibr_info_t pcibr_info;
+ pcibr_soft_slot_t slotp;
+
+ pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+
+ if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
+ return(1);
+
+ slotp = &pcibr_soft->bs_slot[slot];
+
+ /* create info and verticies for guest slots;
+ * for compatibilitiy macros, create info
+ * for even unpopulated slots (but do not
+ * build verticies for them).
+ */
+ if (pcibr_soft->bs_slot[slot].bss_ninfo < 1) {
+ NEWA(pcibr_infoh, 1);
+ pcibr_soft->bs_slot[slot].bss_ninfo = 1;
+ pcibr_soft->bs_slot[slot].bss_infos = pcibr_infoh;
+
+ pcibr_info = pcibr_device_info_new
+ (pcibr_soft, slot, PCIIO_FUNC_NONE,
+ PCIIO_VENDOR_ID_NONE, PCIIO_DEVICE_ID_NONE);
+
+ if (pcibr_soft->bs_slot[slot].has_host) {
+ slotp->slot_conn = pciio_device_info_register
+ (pcibr_vhdl, &pcibr_info->f_c);
+ }
+ }
+
+ /* generate host/guest relations
+ */
+ if (pcibr_soft->bs_slot[slot].has_host) {
+ int host = pcibr_soft->bs_slot[slot].host_slot;
+ pcibr_soft_slot_t host_slotp = &pcibr_soft->bs_slot[host];
+
+ hwgraph_edge_add(slotp->slot_conn,
+ host_slotp->slot_conn,
+ EDGE_LBL_HOST);
+
+ /* XXX- only gives us one guest edge per
+ * host. If/when we have a host with more than
+ * one guest, we will need to figure out how
+ * the host finds all its guests, and sorts
+ * out which one is which.
+ */
+ hwgraph_edge_add(host_slotp->slot_conn,
+ slotp->slot_conn,
+ EDGE_LBL_GUEST);
+ }
+
+ return(0);
+}
+/*
+ * pcibr_slot_initial_rrb_alloc
+ * Allocate a default number of rrbs for this slot on
+ * the two channels. This is dictated by the rrb allocation
+ * strategy routine defined per platform.
+ */
+
+int
+pcibr_slot_initial_rrb_alloc(devfs_handle_t pcibr_vhdl,
+ pciio_slot_t slot)
+
+{
+ pcibr_soft_t pcibr_soft;
+ pcibr_info_h pcibr_infoh;
+ pcibr_info_t pcibr_info;
+ bridge_t *bridge;
+ int c0, c1;
+ int r;
+
+ pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
+ return(1);
+
+ bridge = pcibr_soft->bs_base;
+
+
+ /* How may RRBs are on this slot?
+ */
+ c0 = do_pcibr_rrb_count_valid(bridge, slot);
+ c1 = do_pcibr_rrb_count_valid(bridge, slot + PCIBR_RRB_SLOT_VIRTUAL);
+#if PCIBR_RRB_DEBUG
+ printk("pcibr_attach: slot %d started with %d+%d\n", slot, c0, c1);
+#endif
+
+ /* Do we really need any?
+ */
+ pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos;
+ pcibr_info = pcibr_infoh[0];
+ if ((pcibr_info->f_vendor == PCIIO_VENDOR_ID_NONE) &&
+ !pcibr_soft->bs_slot[slot].has_host) {
+ if (c0 > 0)
+ do_pcibr_rrb_free(bridge, slot, c0);
+ if (c1 > 0)
+ do_pcibr_rrb_free(bridge, slot + PCIBR_RRB_SLOT_VIRTUAL, c1);
+ pcibr_soft->bs_rrb_valid[slot] = 0x1000;
+ pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL] = 0x1000;
+ return(0);
+ }
+
+ pcibr_soft->bs_rrb_avail[slot & 1] -= c0 + c1;
+ pcibr_soft->bs_rrb_valid[slot] = c0;
+ pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL] = c1;
+
+ pcibr_soft->bs_rrb_avail[0] = do_pcibr_rrb_count_avail(bridge, 0);
+ pcibr_soft->bs_rrb_avail[1] = do_pcibr_rrb_count_avail(bridge, 1);
+
+ r = 3 - (c0 + c1);
+
+ if (r > 0) {
+ pcibr_soft->bs_rrb_res[slot] = r;
+ pcibr_soft->bs_rrb_avail[slot & 1] -= r;
+ }
+
+#if PCIBR_RRB_DEBUG
+ printk("\t%d+%d+%d",
+ 0xFFF & pcibr_soft->bs_rrb_valid[slot],
+ 0xFFF & pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL],
+ pcibr_soft->bs_rrb_res[slot]);
+ printk("\n");
+#endif
+ return(0);
+}
+
+/*
+ * pcibr_slot_call_device_attach
+ * This calls the associated driver attach routine for the pci
+ * card in this slot.
+ */
+int
+pcibr_slot_call_device_attach(devfs_handle_t pcibr_vhdl,
+ pciio_slot_t slot)
+{
+ pcibr_soft_t pcibr_soft;
+ pcibr_info_h pcibr_infoh;
+ pcibr_info_t pcibr_info;
+ async_attach_t aa = NULL;
+ int func;
+ devfs_handle_t xconn_vhdl,conn_vhdl;
+ int nfunc;
+
+ pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
+ return(1);
+
+
+ if (pcibr_soft->bs_slot[slot].has_host)
+ return(0);
+
+ xconn_vhdl = pcibr_soft->bs_conn;
+ aa = async_attach_get_info(xconn_vhdl);
+
+ nfunc = pcibr_soft->bs_slot[slot].bss_ninfo;
+ pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos;
+
+ printk("\npcibr_slot_call_device_attach: link 0x%p pci bus 0x%p slot %d\n", xconn_vhdl, pcibr_vhdl, slot);
+
+ for (func = 0; func < nfunc; ++func) {
+
+ pcibr_info = pcibr_infoh[func];
+
+ if (!pcibr_info)
+ continue;
+
+ if (pcibr_info->f_vendor == PCIIO_VENDOR_ID_NONE)
+ continue;
+
+ conn_vhdl = pcibr_info->f_vertex;
+
+ /* If the pci device has been disabled in the prom,
+ * do not set it up for driver attach. NOTE: usrpci
+ * and pciba will not "see" this connection point!
+ */
+ if (device_admin_info_get(conn_vhdl, ADMIN_LBL_DISABLED)) {
+#ifdef SUPPORT_PRINTING_V_FORMAT
+ PRINT_WARNING( "pcibr_slot_call_device_attach: %v disabled\n",
+ conn_vhdl);
+#endif
+ continue;
+ }
+ if (aa)
+ async_attach_add_info(conn_vhdl, aa);
+ pciio_device_attach(conn_vhdl);
+ } /* next func */
+
+ printk("\npcibr_slot_call_device_attach: DONE\n");
+
+ return(0);
+}
+/*
+ * pcibr_slot_call_device_detach
+ * This calls the associated driver detach routine for the pci
+ * card in this slot.
+ */
+int
+pcibr_slot_call_device_detach(devfs_handle_t pcibr_vhdl,
+ pciio_slot_t slot)
+{
+ pcibr_soft_t pcibr_soft;
+ pcibr_info_h pcibr_infoh;
+ pcibr_info_t pcibr_info;
+ int func;
+ devfs_handle_t conn_vhdl;
+ int nfunc;
+ int ndetach = 1;
+
+ pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ if (!pcibr_soft || !PCIBR_VALID_SLOT(slot))
+ return(1);
+
+
+ if (pcibr_soft->bs_slot[slot].has_host)
+ return(0);
+
+
+ nfunc = pcibr_soft->bs_slot[slot].bss_ninfo;
+ pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos;
+
+ for (func = 0; func < nfunc; ++func) {
+
+ pcibr_info = pcibr_infoh[func];
+
+ if (!pcibr_info)
+ continue;
+
+ if (pcibr_info->f_vendor == PCIIO_VENDOR_ID_NONE)
+ continue;
+
+ conn_vhdl = pcibr_info->f_vertex;
+
+ /* Make sure that we do not detach a system critical device
+ * vertex.
+ */
+ if (is_sys_critical_vertex(conn_vhdl)) {
+#ifdef SUPPORT_PRINTING_V_FORMAT
+ PRINT_WARNING( "%v is a system critical device vertex\n",
+ conn_vhdl);
+#endif
+ continue;
+ }
+
+ ndetach = 0;
+ pciio_device_detach(conn_vhdl);
+ } /* next func */
+
+
+ return(ndetach);
+}
+
+/*
+ * pcibr_device_attach
+ * This is a place holder routine to keep track of all the
+ * slot-specific initialization that needs to be done.
+ * This is usually called when we want to initialize a new
+ * pci card on the bus.
+ */
+int
+pcibr_device_attach(devfs_handle_t pcibr_vhdl,
+ pciio_slot_t slot)
+{
+ return (
+ /* Reset the slot */
+ pcibr_slot_reset(pcibr_vhdl,slot) ||
+ /* FInd out what is out there */
+ pcibr_slot_info_init(pcibr_vhdl,slot) ||
+
+ /* Set up the address space for this slot in the pci land */
+ pcibr_slot_addr_space_init(pcibr_vhdl,slot) ||
+
+ /* Setup the device register */
+ pcibr_slot_device_init(pcibr_vhdl, slot) ||
+
+ /* Setup host/guest relations */
+ pcibr_slot_guest_info_init(pcibr_vhdl,slot) ||
+
+ /* Initial RRB management */
+ pcibr_slot_initial_rrb_alloc(pcibr_vhdl,slot) ||
+
+ /* Call the device attach */
+ pcibr_slot_call_device_attach(pcibr_vhdl,slot)
+ );
+
+}
+/*
+ * pcibr_device_detach
+ * This is a place holder routine to keep track of all the
+ * slot-specific freeing that needs to be done.
+ */
+int
+pcibr_device_detach(devfs_handle_t pcibr_vhdl,
+ pciio_slot_t slot)
+{
+
+ /* Call the device detach */
+ return (pcibr_slot_call_device_detach(pcibr_vhdl,slot));
+
+}
+/*
+ * pcibr_device_unregister
+ * This frees up any hardware resources reserved for this pci device
+ * and removes any pci infrastructural information setup for it.
+ * This is usually used at the time of shutting down of the pci card.
+ */
+void
+pcibr_device_unregister(devfs_handle_t pconn_vhdl)
+{
+ pciio_info_t pciio_info;
+ devfs_handle_t pcibr_vhdl;
+ pciio_slot_t slot;
+ pcibr_soft_t pcibr_soft;
+ bridge_t *bridge;
+
+ pciio_info = pciio_info_get(pconn_vhdl);
+
+ /* Detach the pciba name space */
+ pciio_device_detach(pconn_vhdl);
+
+ pcibr_vhdl = pciio_info_master_get(pciio_info);
+ slot = pciio_info_slot_get(pciio_info);
+
+ pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ bridge = pcibr_soft->bs_base;
+
+ /* Clear all the hardware xtalk resources for this device */
+ xtalk_widgetdev_shutdown(pcibr_soft->bs_conn, slot);
+
+ /* Flush all the rrbs */
+ pcibr_rrb_flush(pconn_vhdl);
+
+ /* Free the rrbs allocated to this slot */
+ do_pcibr_rrb_free(bridge, slot,
+ pcibr_soft->bs_rrb_valid[slot] +
+ pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL]);
+
+
+ pcibr_soft->bs_rrb_valid[slot] = 0;
+ pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL] = 0;
+ pcibr_soft->bs_rrb_res[slot] = 0;
+
+ /* Flush the write buffers !! */
+ (void)pcibr_wrb_flush(pconn_vhdl);
+ /* Clear the information specific to the slot */
+ (void)pcibr_slot_info_free(pcibr_vhdl, slot);
+
+}
+
+/*
+ * build a convenience link path in the
+ * form of ".../<iobrick>/bus/<busnum>"
+ *
+ * returns 1 on success, 0 otherwise
+ *
+ * depends on hwgraph separator == '/'
+ */
+int
+pcibr_bus_cnvlink(devfs_handle_t f_c, int slot)
+{
+ char dst[MAXDEVNAME];
+ char *dp = dst;
+ char *cp, *xp;
+ int widgetnum;
+ char pcibus[8];
+ devfs_handle_t nvtx, svtx;
+ int rv;
+
+#if DEBUG
+ printk("pcibr_bus_cnvlink: slot= %d f_c= %p\n",
+ slot, f_c);
+ {
+ int pos;
+ char dname[256];
+ pos = devfs_generate_path(f_c, dname, 256);
+ printk("%s : path= %s\n", __FUNCTION__, &dname[pos]);
+ }
+#endif
+
+ if (GRAPH_SUCCESS != hwgraph_vertex_name_get(f_c, dst, MAXDEVNAME))
+ return 0;
+
+ /* dst example == /hw/module/001c02/Pbrick/xtalk/8/pci/direct */
+
+ /* find the widget number */
+ xp = strstr(dst, "/"EDGE_LBL_XTALK"/");
+ if (xp == NULL)
+ return 0;
+ widgetnum = atoi(xp+7);
+ if (widgetnum < XBOW_PORT_8 || widgetnum > XBOW_PORT_F)
+ return 0;
+
+ /* remove "/pci/direct" from path */
+ cp = strstr(dst, "/" EDGE_LBL_PCI "/" "direct");
+ if (cp == NULL)
+ return 0;
+ *cp = (char)NULL;
+
+ /* get the vertex for the widget */
+ if (GRAPH_SUCCESS != hwgraph_traverse(NULL, dp, &svtx))
+ return 0;
+
+ *xp = (char)NULL; /* remove "/xtalk/..." from path */
+
+ /* dst example now == /hw/module/001c02/Pbrick */
+
+ /* get the bus number */
+ strcat(dst, "/bus");
+ sprintf(pcibus, "%d", p_busnum[widgetnum]);
+
+ /* link to bus to widget */
+ rv = hwgraph_path_add(NULL, dp, &nvtx);
+ if (GRAPH_SUCCESS == rv)
+ rv = hwgraph_edge_add(nvtx, svtx, pcibus);
+
+ return (rv == GRAPH_SUCCESS);
+}
+
+/*
+ * pcibr_attach: called every time the crosstalk
+ * infrastructure is asked to initialize a widget
+ * that matches the part number we handed to the
+ * registration routine above.
+ */
+/*ARGSUSED */
+int
+pcibr_attach(devfs_handle_t xconn_vhdl)
+{
+ /* REFERENCED */
+ graph_error_t rc;
+ devfs_handle_t pcibr_vhdl;
+ devfs_handle_t ctlr_vhdl;
+ bridge_t *bridge = NULL;
+ bridgereg_t id;
+ int rev;
+ pcibr_soft_t pcibr_soft;
+ pcibr_info_t pcibr_info;
+ xwidget_info_t info;
+ xtalk_intr_t xtalk_intr;
+ device_desc_t dev_desc;
+ int slot;
+ int ibit;
+ devfs_handle_t noslot_conn;
+ char devnm[MAXDEVNAME], *s;
+ pcibr_hints_t pcibr_hints;
+ bridgereg_t b_int_enable;
+ unsigned rrb_fixed = 0;
+
+ iopaddr_t pci_io_fb, pci_io_fl;
+ iopaddr_t pci_lo_fb, pci_lo_fl;
+ iopaddr_t pci_hi_fb, pci_hi_fl;
+
+ int spl_level;
+ char *nicinfo = (char *)0;
+
+#if PCI_FBBE
+ int fast_back_to_back_enable;
+#endif
+
+ async_attach_t aa = NULL;
+
+ aa = async_attach_get_info(xconn_vhdl);
+
+#if DEBUG && ATTACH_DEBUG
+ printk("pcibr_attach: xconn_vhdl= %p\n", xconn_vhdl);
+ {
+ int pos;
+ char dname[256];
+ pos = devfs_generate_path(xconn_vhdl, dname, 256);
+ printk("%s : path= %s \n", __FUNCTION__, &dname[pos]);
+ }
+#endif
+
+ /* Setup the PRB for the bridge in CONVEYOR BELT
+ * mode. PRBs are setup in default FIRE-AND-FORGET
+ * mode during the initialization.
+ */
+ hub_device_flags_set(xconn_vhdl, HUB_PIO_CONVEYOR);
+
+ bridge = (bridge_t *)
+ xtalk_piotrans_addr(xconn_vhdl, NULL,
+ 0, sizeof(bridge_t), 0);
+
+#ifndef MEDUSA_HACK
+ if ((bridge->b_wid_stat & BRIDGE_STAT_PCI_GIO_N) == 0)
+ return -1; /* someone else handles GIO bridges. */
+#endif
+
+#ifdef BRINGUP
+ if (XWIDGET_PART_REV_NUM(bridge->b_wid_id) == XBRIDGE_PART_REV_A)
+ NeedXbridgeSwap = 1;
+#endif
+
+ printk("pcibr_attach: Called with vertex 0x%p, b_wid_stat 0x%x, gio 0x%x\n",xconn_vhdl, bridge->b_wid_stat, BRIDGE_STAT_PCI_GIO_N);
+
+ /*
+ * Create the vertex for the PCI bus, which we
+ * will also use to hold the pcibr_soft and
+ * which will be the "master" vertex for all the
+ * pciio connection points we will hang off it.
+ * This needs to happen before we call nic_bridge_vertex_info
+ * as we are some of the *_vmc functions need access to the edges.
+ *
+ * Opening this vertex will provide access to
+ * the Bridge registers themselves.
+ */
+ rc = hwgraph_path_add(xconn_vhdl, EDGE_LBL_PCI, &pcibr_vhdl);
+ ASSERT(rc == GRAPH_SUCCESS);
+
+ rc = hwgraph_char_device_add(pcibr_vhdl, EDGE_LBL_CONTROLLER, "pcibr_", &ctlr_vhdl);
+ ASSERT(rc == GRAPH_SUCCESS);
+
+ /*
+ * decode the nic, and hang its stuff off our
+ * connection point where other drivers can get
+ * at it.
+ */
+#ifdef LATER
+ nicinfo = BRIDGE_VERTEX_MFG_INFO(xconn_vhdl, (nic_data_t) & bridge->b_nic);
+#endif
+
+ /*
+ * Get the hint structure; if some NIC callback
+ * marked this vertex as "hands-off" then we
+ * just return here, before doing anything else.
+ */
+ pcibr_hints = pcibr_hints_get(xconn_vhdl, 0);
+
+ if (pcibr_hints && pcibr_hints->ph_hands_off)
+ return -1; /* generic operations disabled */
+
+ id = bridge->b_wid_id;
+ rev = XWIDGET_PART_REV_NUM(id);
+
+ hwgraph_info_add_LBL(pcibr_vhdl, INFO_LBL_PCIBR_ASIC_REV, (arbitrary_info_t) rev);
+
+ /*
+ * allocate soft state structure, fill in some
+ * fields, and hook it up to our vertex.
+ */
+ NEW(pcibr_soft);
+ BZERO(pcibr_soft, sizeof *pcibr_soft);
+ pcibr_soft_set(pcibr_vhdl, pcibr_soft);
+
+ pcibr_soft->bs_conn = xconn_vhdl;
+ pcibr_soft->bs_vhdl = pcibr_vhdl;
+ pcibr_soft->bs_base = bridge;
+ pcibr_soft->bs_rev_num = rev;
+ pcibr_soft->bs_intr_bits = pcibr_intr_bits;
+ if (is_xbridge(bridge)) {
+ pcibr_soft->bs_int_ate_size = XBRIDGE_INTERNAL_ATES;
+ pcibr_soft->bs_xbridge = 1;
+ } else {
+ pcibr_soft->bs_int_ate_size = BRIDGE_INTERNAL_ATES;
+ pcibr_soft->bs_xbridge = 0;
+ }
+
+ pcibr_soft->bsi_err_intr = 0;
+
+ /* Bridges up through REV C
+ * are unable to set the direct
+ * byteswappers to BYTE_STREAM.
+ */
+ if (pcibr_soft->bs_rev_num <= BRIDGE_PART_REV_C) {
+ pcibr_soft->bs_pio_end_io = PCIIO_WORD_VALUES;
+ pcibr_soft->bs_pio_end_mem = PCIIO_WORD_VALUES;
+ }
+#if PCIBR_SOFT_LIST
+ {
+ pcibr_list_p self;
+
+ NEW(self);
+ self->bl_soft = pcibr_soft;
+ self->bl_vhdl = pcibr_vhdl;
+ self->bl_next = pcibr_list;
+ self->bl_next = swap_ptr((void **) &pcibr_list, (void *)self);
+ }
+#endif
+
+ /*
+ * get the name of this bridge vertex and keep the info. Use this
+ * only where it is really needed now: like error interrupts.
+ */
+ s = dev_to_name(pcibr_vhdl, devnm, MAXDEVNAME);
+ pcibr_soft->bs_name = kmalloc(strlen(s) + 1, GFP_KERNEL);
+ strcpy(pcibr_soft->bs_name, s);
+
+#if SHOW_REVS || DEBUG
+#if !DEBUG
+ if (kdebug)
+#endif
+ printk("%sBridge ASIC: rev %s (code=0x%x) at %s\n",
+ is_xbridge(bridge) ? "X" : "",
+ (rev == BRIDGE_PART_REV_A) ? "A" :
+ (rev == BRIDGE_PART_REV_B) ? "B" :
+ (rev == BRIDGE_PART_REV_C) ? "C" :
+ (rev == BRIDGE_PART_REV_D) ? "D" :
+ (rev == XBRIDGE_PART_REV_A) ? "A" :
+ (rev == XBRIDGE_PART_REV_B) ? "B" :
+ "unknown",
+ rev, pcibr_soft->bs_name);
+#endif
+
+ info = xwidget_info_get(xconn_vhdl);
+ pcibr_soft->bs_xid = xwidget_info_id_get(info);
+ pcibr_soft->bs_master = xwidget_info_master_get(info);
+ pcibr_soft->bs_mxid = xwidget_info_masterid_get(info);
+
+ /*
+ * Init bridge lock.
+ */
+ spinlock_init(&pcibr_soft->bs_lock, "pcibr_loc");
+
+ /*
+ * If we have one, process the hints structure.
+ */
+ if (pcibr_hints) {
+ rrb_fixed = pcibr_hints->ph_rrb_fixed;
+
+ pcibr_soft->bs_rrb_fixed = rrb_fixed;
+
+ if (pcibr_hints->ph_intr_bits)
+ pcibr_soft->bs_intr_bits = pcibr_hints->ph_intr_bits;
+
+ for (slot = 0; slot < 8; ++slot) {
+ int hslot = pcibr_hints->ph_host_slot[slot] - 1;
+
+ if (hslot < 0) {
+ pcibr_soft->bs_slot[slot].host_slot = slot;
+ } else {
+ pcibr_soft->bs_slot[slot].has_host = 1;
+ pcibr_soft->bs_slot[slot].host_slot = hslot;
+ }
+ }
+ }
+ /*
+ * set up initial values for state fields
+ */
+ for (slot = 0; slot < 8; ++slot) {
+ pcibr_soft->bs_slot[slot].bss_devio.bssd_space = PCIIO_SPACE_NONE;
+ pcibr_soft->bs_slot[slot].bss_d64_base = PCIBR_D64_BASE_UNSET;
+ pcibr_soft->bs_slot[slot].bss_d32_base = PCIBR_D32_BASE_UNSET;
+ pcibr_soft->bs_slot[slot].bss_ext_ates_active = 0;
+ }
+
+ for (ibit = 0; ibit < 8; ++ibit) {
+ pcibr_soft->bs_intr[ibit].bsi_xtalk_intr = 0;
+ pcibr_soft->bs_intr[ibit].bsi_pcibr_intr_list = 0;
+ }
+
+ /*
+ * connect up our error handler
+ */
+ xwidget_error_register(xconn_vhdl, pcibr_error_handler, pcibr_soft);
+
+ /*
+ * Initialize various Bridge registers.
+ */
+
+ /*
+ * On pre-Rev.D bridges, set the PCI_RETRY_CNT
+ * to zero to avoid dropping stores. (#475347)
+ */
+ if (rev < BRIDGE_PART_REV_D)
+ bridge->b_bus_timeout &= ~BRIDGE_BUS_PCI_RETRY_MASK;
+
+ /*
+ * Clear all pending interrupts.
+ */
+ bridge->b_int_rst_stat = (BRIDGE_IRR_ALL_CLR);
+
+ /*
+ * Until otherwise set up,
+ * assume all interrupts are
+ * from slot 7.
+ */
+ bridge->b_int_device = (uint32_t) 0xffffffff;
+
+ {
+ bridgereg_t dirmap;
+ paddr_t paddr;
+ iopaddr_t xbase;
+ xwidgetnum_t xport;
+ iopaddr_t offset;
+ int num_entries;
+ int entry;
+ cnodeid_t cnodeid;
+ nasid_t nasid;
+ char *node_val;
+ devfs_handle_t node_vhdl;
+ char vname[MAXDEVNAME];
+
+ /* Set the Bridge's 32-bit PCI to XTalk
+ * Direct Map register to the most useful
+ * value we can determine. Note that we
+ * must use a single xid for all of:
+ * direct-mapped 32-bit DMA accesses
+ * direct-mapped 64-bit DMA accesses
+ * DMA accesses through the PMU
+ * interrupts
+ * This is the only way to guarantee that
+ * completion interrupts will reach a CPU
+ * after all DMA data has reached memory.
+ * (Of course, there may be a few special
+ * drivers/controlers that explicitly manage
+ * this ordering problem.)
+ */
+
+ cnodeid = 0; /* default node id */
+ /*
+ * Determine the base address node id to be used for all 32-bit
+ * Direct Mapping I/O. The default is node 0, but this can be changed
+ * via a DEVICE_ADMIN directive and the PCIBUS_DMATRANS_NODE
+ * attribute in the irix.sm config file. A device driver can obtain
+ * this node value via a call to pcibr_get_dmatrans_node().
+ */
+ node_val = device_admin_info_get(pcibr_vhdl, ADMIN_LBL_DMATRANS_NODE);
+ if (node_val != NULL) {
+ node_vhdl = hwgraph_path_to_vertex(node_val);
+ if (node_vhdl != GRAPH_VERTEX_NONE) {
+ cnodeid = nodevertex_to_cnodeid(node_vhdl);
+ }
+ if ((node_vhdl == GRAPH_VERTEX_NONE) || (cnodeid == CNODEID_NONE)) {
+ cnodeid = 0;
+ vertex_to_name(pcibr_vhdl, vname, sizeof(vname));
+ PRINT_WARNING( "Invalid hwgraph node path specified:\n DEVICE_ADMIN: %s %s=%s\n",
+ vname, ADMIN_LBL_DMATRANS_NODE, node_val);
+ }
+ }
+ nasid = COMPACT_TO_NASID_NODEID(cnodeid);
+ paddr = NODE_OFFSET(nasid) + 0;
+
+ /* currently, we just assume that if we ask
+ * for a DMA mapping to "zero" the XIO
+ * host will transmute this into a request
+ * for the lowest hunk of memory.
+ */
+ xbase = xtalk_dmatrans_addr(xconn_vhdl, 0,
+ paddr, _PAGESZ, 0);
+
+ if (xbase != XIO_NOWHERE) {
+ if (XIO_PACKED(xbase)) {
+ xport = XIO_PORT(xbase);
+ xbase = XIO_ADDR(xbase);
+ } else
+ xport = pcibr_soft->bs_mxid;
+
+ offset = xbase & ((1ull << BRIDGE_DIRMAP_OFF_ADDRSHFT) - 1ull);
+ xbase >>= BRIDGE_DIRMAP_OFF_ADDRSHFT;
+
+ dirmap = xport << BRIDGE_DIRMAP_W_ID_SHFT;
+
+#ifdef IRIX
+ dirmap |= BRIDGE_DIRMAP_RMF_64;
+#endif
+
+ if (xbase)
+ dirmap |= BRIDGE_DIRMAP_OFF & xbase;
+ else if (offset >= (512 << 20))
+ dirmap |= BRIDGE_DIRMAP_ADD512;
+
+ bridge->b_dir_map = dirmap;
+ }
+ /*
+ * Set bridge's idea of page size according to the system's
+ * idea of "IO page size". TBD: The idea of IO page size
+ * should really go away.
+ */
+ /*
+ * ensure that we write and read without any interruption.
+ * The read following the write is required for the Bridge war
+ */
+ spl_level = splhi();
+#if IOPGSIZE == 4096
+ bridge->b_wid_control &= ~BRIDGE_CTRL_PAGE_SIZE;
+#elif IOPGSIZE == 16384
+ bridge->b_wid_control |= BRIDGE_CTRL_PAGE_SIZE;
+#else
+ <<<Unable to deal with IOPGSIZE >>>;
+#endif
+ bridge->b_wid_control; /* inval addr bug war */
+ splx(spl_level);
+
+ /* Initialize internal mapping entries */
+ for (entry = 0; entry < pcibr_soft->bs_int_ate_size; entry++)
+ bridge->b_int_ate_ram[entry].wr = 0;
+
+ /*
+ * Determine if there's external mapping SSRAM on this
+ * bridge. Set up Bridge control register appropriately,
+ * inititlize SSRAM, and set software up to manage RAM
+ * entries as an allocatable resource.
+ *
+ * Currently, we just use the rm* routines to manage ATE
+ * allocation. We should probably replace this with a
+ * Best Fit allocator.
+ *
+ * For now, if we have external SSRAM, avoid using
+ * the internal ssram: we can't turn PREFETCH on
+ * when we use the internal SSRAM; and besides,
+ * this also guarantees that no allocation will
+ * straddle the internal/external line, so we
+ * can increment ATE write addresses rather than
+ * recomparing against BRIDGE_INTERNAL_ATES every
+ * time.
+ */
+#ifdef BRINGUP
+ /*
+ * 082799: for some reason pcibr_init_ext_ate_ram is causing
+ * a Data Bus Error. It should be zero anyway so just force it.
+ */
+ num_entries = 0;
+#else
+ num_entries = pcibr_init_ext_ate_ram(bridge);
+#endif
+
+ /* we always have 128 ATEs (512 for Xbridge) inside the chip
+ * even if disabled for debugging.
+ */
+ pcibr_soft->bs_int_ate_map = rmallocmap(pcibr_soft->bs_int_ate_size);
+ pcibr_ate_free(pcibr_soft, 0, pcibr_soft->bs_int_ate_size);
+#if PCIBR_ATE_DEBUG
+ printk("pcibr_attach: %d INTERNAL ATEs\n", pcibr_soft->bs_int_ate_size);
+#endif
+
+ if (num_entries > pcibr_soft->bs_int_ate_size) {
+#if PCIBR_ATE_NOTBOTH /* for debug -- forces us to use external ates */
+ printk("pcibr_attach: disabling internal ATEs.\n");
+ pcibr_ate_alloc(pcibr_soft, pcibr_soft->bs_int_ate_size);
+#endif
+ pcibr_soft->bs_ext_ate_map = rmallocmap(num_entries);
+ pcibr_ate_free(pcibr_soft, pcibr_soft->bs_int_ate_size,
+ num_entries - pcibr_soft->bs_int_ate_size);
+#if PCIBR_ATE_DEBUG
+ printk("pcibr_attach: %d EXTERNAL ATEs\n",
+ num_entries - pcibr_soft->bs_int_ate_size);
+#endif
+ }
+ }
+
+ {
+ bridgereg_t dirmap;
+ iopaddr_t xbase;
+
+ /*
+ * now figure the *real* xtalk base address
+ * that dirmap sends us to.
+ */
+ dirmap = bridge->b_dir_map;
+ if (dirmap & BRIDGE_DIRMAP_OFF)
+ xbase = (iopaddr_t)(dirmap & BRIDGE_DIRMAP_OFF)
+ << BRIDGE_DIRMAP_OFF_ADDRSHFT;
+ else if (dirmap & BRIDGE_DIRMAP_ADD512)
+ xbase = 512 << 20;
+ else
+ xbase = 0;
+
+ pcibr_soft->bs_dir_xbase = xbase;
+
+ /* it is entirely possible that we may, at this
+ * point, have our dirmap pointing somewhere
+ * other than our "master" port.
+ */
+ pcibr_soft->bs_dir_xport =
+ (dirmap & BRIDGE_DIRMAP_W_ID) >> BRIDGE_DIRMAP_W_ID_SHFT;
+ }
+
+ /* pcibr sources an error interrupt;
+ * figure out where to send it.
+ *
+ * If any interrupts are enabled in bridge,
+ * then the prom set us up and our interrupt
+ * has already been reconnected in mlreset
+ * above.
+ *
+ * Need to set the D_INTR_ISERR flag
+ * in the dev_desc used for alocating the
+ * error interrupt, so our interrupt will
+ * be properly routed and prioritized.
+ *
+ * If our crosstalk provider wants to
+ * fix widget error interrupts to specific
+ * destinations, D_INTR_ISERR is how it
+ * knows to do this.
+ */
+
+ dev_desc = device_desc_dup(pcibr_vhdl);
+ device_desc_flags_set(dev_desc,
+ device_desc_flags_get(dev_desc) | D_INTR_ISERR);
+ device_desc_intr_name_set(dev_desc, "Bridge error");
+
+ xtalk_intr = xtalk_intr_alloc(xconn_vhdl, dev_desc, pcibr_vhdl);
+ ASSERT(xtalk_intr != NULL);
+
+ device_desc_free(dev_desc);
+
+ pcibr_soft->bsi_err_intr = xtalk_intr;
+
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+ /*
+ * On IP35 with XBridge, we do some extra checks in pcibr_setwidint
+ * in order to work around some addressing limitations. In order
+ * for that fire wall to work properly, we need to make sure we
+ * start from a known clean state.
+ */
+ pcibr_clearwidint(bridge);
+#endif
+
+ printk("pribr_attach: FIXME Error Interrupt not registered\n");
+
+ xtalk_intr_connect(xtalk_intr,
+ (intr_func_t) pcibr_error_intr_handler,
+ (intr_arg_t) pcibr_soft,
+ (xtalk_intr_setfunc_t) pcibr_setwidint,
+ (void *) bridge,
+ (void *) 0);
+
+ /*
+ * now we can start handling error interrupts;
+ * enable all of them.
+ * NOTE: some PCI ints may already be enabled.
+ */
+ b_int_enable = bridge->b_int_enable | BRIDGE_ISR_ERRORS;
+
+
+ bridge->b_int_enable = b_int_enable;
+ bridge->b_int_mode = 0; /* do not send "clear interrupt" packets */
+
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+
+ /*
+ * Depending on the rev of bridge, disable certain features.
+ * Easiest way seems to be to force the PCIBR_NOwhatever
+ * flag to be on for all DMA calls, which overrides any
+ * PCIBR_whatever flag or even the setting of whatever
+ * from the PCIIO_DMA_class flags (or even from the other
+ * PCIBR flags, since NO overrides YES).
+ */
+ pcibr_soft->bs_dma_flags = 0;
+
+ /* PREFETCH:
+ * Always completely disabled for REV.A;
+ * at "pcibr_prefetch_enable_rev", anyone
+ * asking for PCIIO_PREFETCH gets it.
+ * Between these two points, you have to ask
+ * for PCIBR_PREFETCH, which promises that
+ * your driver knows about known Bridge WARs.
+ */
+ if (pcibr_soft->bs_rev_num < BRIDGE_PART_REV_B)
+ pcibr_soft->bs_dma_flags |= PCIBR_NOPREFETCH;
+ else if (pcibr_soft->bs_rev_num <
+ (BRIDGE_WIDGET_PART_NUM << 4 | pcibr_prefetch_enable_rev))
+ pcibr_soft->bs_dma_flags |= PCIIO_NOPREFETCH;
+
+ /* WRITE_GATHER:
+ * Disabled up to but not including the
+ * rev number in pcibr_wg_enable_rev. There
+ * is no "WAR range" as with prefetch.
+ */
+ if (pcibr_soft->bs_rev_num <
+ (BRIDGE_WIDGET_PART_NUM << 4 | pcibr_wg_enable_rev))
+ pcibr_soft->bs_dma_flags |= PCIBR_NOWRITE_GATHER;
+
+ pciio_provider_register(pcibr_vhdl, &pcibr_provider);
+ pciio_provider_startup(pcibr_vhdl);
+
+ pci_io_fb = 0x00000004; /* I/O FreeBlock Base */
+ pci_io_fl = 0xFFFFFFFF; /* I/O FreeBlock Last */
+
+ pci_lo_fb = 0x00000010; /* Low Memory FreeBlock Base */
+ pci_lo_fl = 0x001FFFFF; /* Low Memory FreeBlock Last */
+
+ pci_hi_fb = 0x00200000; /* High Memory FreeBlock Base */
+ pci_hi_fl = 0x3FFFFFFF; /* High Memory FreeBlock Last */
+
+
+ PCI_ADDR_SPACE_LIMITS_STORE();
+
+ /* build "no-slot" connection point
+ */
+ pcibr_info = pcibr_device_info_new
+ (pcibr_soft, PCIIO_SLOT_NONE, PCIIO_FUNC_NONE,
+ PCIIO_VENDOR_ID_NONE, PCIIO_DEVICE_ID_NONE);
+ noslot_conn = pciio_device_info_register
+ (pcibr_vhdl, &pcibr_info->f_c);
+
+ /* Remember the no slot connection point info for tearing it
+ * down during detach.
+ */
+ pcibr_soft->bs_noslot_conn = noslot_conn;
+ pcibr_soft->bs_noslot_info = pcibr_info;
+#if PCI_FBBE
+ fast_back_to_back_enable = 1;
+#endif
+
+#if PCI_FBBE
+ if (fast_back_to_back_enable) {
+ /*
+ * All devices on the bus are capable of fast back to back, so
+ * we need to set the fast back to back bit in all devices on
+ * the bus that are capable of doing such accesses.
+ */
+ }
+#endif
+
+#ifdef IRIX
+ /* If the bridge has been reset then there is no need to reset
+ * the individual PCI slots.
+ */
+ for (slot = 0; slot < 8; ++slot)
+ /* Reset all the slots */
+ (void)pcibr_slot_reset(pcibr_vhdl,slot);
+#endif
+
+ for (slot = 0; slot < 8; ++slot)
+ /* Find out what is out there */
+ (void)pcibr_slot_info_init(pcibr_vhdl,slot);
+
+ for (slot = 0; slot < 8; ++slot)
+ /* Set up the address space for this slot in the pci land */
+ (void)pcibr_slot_addr_space_init(pcibr_vhdl,slot);
+
+ for (slot = 0; slot < 8; ++slot)
+ /* Setup the device register */
+ (void)pcibr_slot_device_init(pcibr_vhdl, slot);
+
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+ for (slot = 0; slot < 8; ++slot)
+ /* Set up convenience links */
+ if (is_xbridge(bridge))
+ if (pcibr_soft->bs_slot[slot].bss_ninfo > 0) /* if occupied */
+ pcibr_bus_cnvlink(pcibr_info->f_vertex, slot);
+#endif
+
+ for (slot = 0; slot < 8; ++slot)
+ /* Setup host/guest relations */
+ (void)pcibr_slot_guest_info_init(pcibr_vhdl,slot);
+
+ for (slot = 0; slot < 8; ++slot)
+ /* Initial RRB management */
+ (void)pcibr_slot_initial_rrb_alloc(pcibr_vhdl,slot);
+
+#ifdef dagum
+ /* driver attach routines should be called out from generic linux code */
+ for (slot = 0; slot < 8; ++slot)
+ /* Call the device attach */
+ (void)pcibr_slot_call_device_attach(pcibr_vhdl,slot);
+#endif /* dagum */
+
+#ifdef LATER
+ if (strstr(nicinfo, XTALK_PCI_PART_NUM)) {
+ do_pcibr_rrb_autoalloc(pcibr_soft, 1, 8);
+#if PCIBR_RRB_DEBUG
+ printf("\n\nFound XTALK_PCI (030-1275) at %v\n", xconn_vhdl);
+
+ printf("pcibr_attach: %v Shoebox RRB MANAGEMENT: %d+%d free\n",
+ pcibr_vhdl,
+ pcibr_soft->bs_rrb_avail[0],
+ pcibr_soft->bs_rrb_avail[1]);
+
+ for (slot = 0; slot < 8; ++slot)
+ printf("\t%d+%d+%d",
+ 0xFFF & pcibr_soft->bs_rrb_valid[slot],
+ 0xFFF & pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL],
+ pcibr_soft->bs_rrb_res[slot]);
+
+ printf("\n");
+#endif
+ }
+#else
+ printk("pcibr_attach: FIXME to call do_pcibr_rrb_autoalloc nicinfo 0x%p\n", nicinfo);
+#endif
+
+ if (aa)
+ async_attach_add_info(noslot_conn, aa);
+
+ pciio_device_attach(noslot_conn);
+
+
+ /*
+ * Tear down pointer to async attach info -- async threads for
+ * bridge's descendants may be running but the bridge's work is done.
+ */
+ if (aa)
+ async_attach_del_info(xconn_vhdl);
+
+ return 0;
+}
+/*
+ * pcibr_detach:
+ * Detach the bridge device from the hwgraph after cleaning out all the
+ * underlying vertices.
+ */
+int
+pcibr_detach(devfs_handle_t xconn)
+{
+ pciio_slot_t slot;
+ devfs_handle_t pcibr_vhdl;
+ pcibr_soft_t pcibr_soft;
+ bridge_t *bridge;
+
+ /* Get the bridge vertex from its xtalk connection point */
+ if (hwgraph_traverse(xconn, EDGE_LBL_PCI, &pcibr_vhdl) != GRAPH_SUCCESS)
+ return(1);
+
+ pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ bridge = pcibr_soft->bs_base;
+
+ /* Disable the interrupts from the bridge */
+ bridge->b_int_enable = 0;
+
+ /* Detach all the PCI devices talking to this bridge */
+ for(slot = 0; slot < 8; slot++) {
+#ifdef DEBUG
+ printk("pcibr_device_detach called for %p/%d\n",
+ pcibr_vhdl,slot);
+#endif
+ pcibr_device_detach(pcibr_vhdl, slot);
+ }
+
+ /* Unregister the no-slot connection point */
+ pciio_device_info_unregister(pcibr_vhdl,
+ &(pcibr_soft->bs_noslot_info->f_c));
+
+ spinlock_destroy(&pcibr_soft->bs_lock);
+ kfree(pcibr_soft->bs_name);
+
+ /* Error handler gets unregistered when the widget info is
+ * cleaned
+ */
+ /* Free the soft ATE maps */
+ if (pcibr_soft->bs_int_ate_map)
+ rmfreemap(pcibr_soft->bs_int_ate_map);
+ if (pcibr_soft->bs_ext_ate_map)
+ rmfreemap(pcibr_soft->bs_ext_ate_map);
+
+ /* Disconnect the error interrupt and free the xtalk resources
+ * associated with it.
+ */
+ xtalk_intr_disconnect(pcibr_soft->bsi_err_intr);
+ xtalk_intr_free(pcibr_soft->bsi_err_intr);
+
+ /* Clear the software state maintained by the bridge driver for this
+ * bridge.
+ */
+ DEL(pcibr_soft);
+ /* Remove the Bridge revision labelled info */
+ (void)hwgraph_info_remove_LBL(pcibr_vhdl, INFO_LBL_PCIBR_ASIC_REV, NULL);
+ /* Remove the character device associated with this bridge */
+ (void)hwgraph_edge_remove(pcibr_vhdl, EDGE_LBL_CONTROLLER, NULL);
+ /* Remove the PCI bridge vertex */
+ (void)hwgraph_edge_remove(xconn, EDGE_LBL_PCI, NULL);
+
+ return(0);
+}
+
+int
+pcibr_asic_rev(devfs_handle_t pconn_vhdl)
+{
+ devfs_handle_t pcibr_vhdl;
+ arbitrary_info_t ainfo;
+
+ if (GRAPH_SUCCESS !=
+ hwgraph_traverse(pconn_vhdl, EDGE_LBL_MASTER, &pcibr_vhdl))
+ return -1;
+
+ if (GRAPH_SUCCESS !=
+ hwgraph_info_get_LBL(pcibr_vhdl, INFO_LBL_PCIBR_ASIC_REV, &ainfo))
+ return -1;
+
+ return (int) ainfo;
+}
+
+int
+pcibr_write_gather_flush(devfs_handle_t pconn_vhdl)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ pciio_slot_t slot;
+ slot = pciio_info_slot_get(pciio_info);
+ pcibr_device_write_gather_flush(pcibr_soft, slot);
+ return 0;
+}
+
+/* =====================================================================
+ * PIO MANAGEMENT
+ */
+
+LOCAL iopaddr_t
+pcibr_addr_pci_to_xio(devfs_handle_t pconn_vhdl,
+ pciio_slot_t slot,
+ pciio_space_t space,
+ iopaddr_t pci_addr,
+ size_t req_size,
+ unsigned flags)
+{
+ pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
+ pciio_info_t pciio_info = &pcibr_info->f_c;
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ bridge_t *bridge = pcibr_soft->bs_base;
+
+ unsigned bar; /* which BASE reg on device is decoding */
+ iopaddr_t xio_addr = XIO_NOWHERE;
+
+ pciio_space_t wspace; /* which space device is decoding */
+ iopaddr_t wbase; /* base of device decode on PCI */
+ size_t wsize; /* size of device decode on PCI */
+
+ int try; /* DevIO(x) window scanning order control */
+ int win; /* which DevIO(x) window is being used */
+ pciio_space_t mspace; /* target space for devio(x) register */
+ iopaddr_t mbase; /* base of devio(x) mapped area on PCI */
+ size_t msize; /* size of devio(x) mapped area on PCI */
+ size_t mmask; /* addr bits stored in Device(x) */
+
+ unsigned s;
+
+ s = pcibr_lock(pcibr_soft);
+
+ if (pcibr_soft->bs_slot[slot].has_host) {
+ slot = pcibr_soft->bs_slot[slot].host_slot;
+ pcibr_info = pcibr_soft->bs_slot[slot].bss_infos[0];
+ }
+ if (space == PCIIO_SPACE_NONE)
+ goto done;
+
+ if (space == PCIIO_SPACE_CFG) {
+ /*
+ * Usually, the first mapping
+ * established to a PCI device
+ * is to its config space.
+ *
+ * In any case, we definitely
+ * do NOT need to worry about
+ * PCI BASE registers, and
+ * MUST NOT attempt to point
+ * the DevIO(x) window at
+ * this access ...
+ */
+ if (((flags & PCIIO_BYTE_STREAM) == 0) &&
+ ((pci_addr + req_size) <= BRIDGE_TYPE0_CFG_FUNC_OFF))
+ xio_addr = pci_addr + BRIDGE_TYPE0_CFG_DEV(slot);
+
+ goto done;
+ }
+ if (space == PCIIO_SPACE_ROM) {
+ /* PIO to the Expansion Rom.
+ * Driver is responsible for
+ * enabling and disabling
+ * decodes properly.
+ */
+ wbase = pcibr_info->f_rbase;
+ wsize = pcibr_info->f_rsize;
+
+ /*
+ * While the driver should know better
+ * than to attempt to map more space
+ * than the device is decoding, he might
+ * do it; better to bail out here.
+ */
+ if ((pci_addr + req_size) > wsize)
+ goto done;
+
+ pci_addr += wbase;
+ space = PCIIO_SPACE_MEM;
+ }
+ /*
+ * reduce window mappings to raw
+ * space mappings (maybe allocating
+ * windows), and try for DevIO(x)
+ * usage (setting it if it is available).
+ */
+ bar = space - PCIIO_SPACE_WIN0;
+ if (bar < 6) {
+ wspace = pcibr_info->f_window[bar].w_space;
+ if (wspace == PCIIO_SPACE_NONE)
+ goto done;
+
+ /* get pci base and size */
+ wbase = pcibr_info->f_window[bar].w_base;
+ wsize = pcibr_info->f_window[bar].w_size;
+
+ /*
+ * While the driver should know better
+ * than to attempt to map more space
+ * than the device is decoding, he might
+ * do it; better to bail out here.
+ */
+ if ((pci_addr + req_size) > wsize)
+ goto done;
+
+ /* shift from window relative to
+ * decoded space relative.
+ */
+ pci_addr += wbase;
+ space = wspace;
+ } else
+ bar = -1;
+
+ /* Scan all the DevIO(x) windows twice looking for one
+ * that can satisfy our request. The first time through,
+ * only look at assigned windows; the second time, also
+ * look at PCIIO_SPACE_NONE windows. Arrange the order
+ * so we always look at our own window first.
+ *
+ * We will not attempt to satisfy a single request
+ * by concatinating multiple windows.
+ */
+ for (try = 0; try < 16; ++try) {
+ bridgereg_t devreg;
+ unsigned offset;
+
+ win = (try + slot) % 8;
+
+ /* If this DevIO(x) mapping area can provide
+ * a mapping to this address, use it.
+ */
+ msize = (win < 2) ? 0x200000 : 0x100000;
+ mmask = -msize;
+ if (space != PCIIO_SPACE_IO)
+ mmask &= 0x3FFFFFFF;
+
+ offset = pci_addr & (msize - 1);
+
+ /* If this window can't possibly handle that request,
+ * go on to the next window.
+ */
+ if (((pci_addr & (msize - 1)) + req_size) > msize)
+ continue;
+
+ devreg = pcibr_soft->bs_slot[win].bss_device;
+
+ /* Is this window "nailed down"?
+ * If not, maybe we can use it.
+ * (only check this the second time through)
+ */
+ mspace = pcibr_soft->bs_slot[win].bss_devio.bssd_space;
+ if ((try > 7) && (mspace == PCIIO_SPACE_NONE)) {
+
+ /* If this is the primary DevIO(x) window
+ * for some other device, skip it.
+ */
+ if ((win != slot) &&
+ (PCIIO_VENDOR_ID_NONE !=
+ pcibr_soft->bs_slot[win].bss_vendor_id))
+ continue;
+
+ /* It's a free window, and we fit in it.
+ * Set up Device(win) to our taste.
+ */
+ mbase = pci_addr & mmask;
+
+ /* check that we would really get from
+ * here to there.
+ */
+ if ((mbase | offset) != pci_addr)
+ continue;
+
+ devreg &= ~BRIDGE_DEV_OFF_MASK;
+ if (space != PCIIO_SPACE_IO)
+ devreg |= BRIDGE_DEV_DEV_IO_MEM;
+ else
+ devreg &= ~BRIDGE_DEV_DEV_IO_MEM;
+ devreg |= (mbase >> 20) & BRIDGE_DEV_OFF_MASK;
+
+ /* default is WORD_VALUES.
+ * if you specify both,
+ * operation is undefined.
+ */
+ if (flags & PCIIO_BYTE_STREAM)
+ devreg |= BRIDGE_DEV_DEV_SWAP;
+ else
+ devreg &= ~BRIDGE_DEV_DEV_SWAP;
+
+ if (pcibr_soft->bs_slot[win].bss_device != devreg) {
+ bridge->b_device[win].reg = devreg;
+ pcibr_soft->bs_slot[win].bss_device = devreg;
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+
+#if DEBUG && PCI_DEBUG
+ printk("pcibr Device(%d): 0x%lx\n", win, bridge->b_device[win].reg);
+#endif
+ }
+ pcibr_soft->bs_slot[win].bss_devio.bssd_space = space;
+ pcibr_soft->bs_slot[win].bss_devio.bssd_base = mbase;
+ xio_addr = BRIDGE_DEVIO(win) + (pci_addr - mbase);
+
+#if DEBUG && PCI_DEBUG
+ printk("%s LINE %d map to space %d space desc 0x%x[%lx..%lx] for slot %d allocates DevIO(%d) devreg 0x%x\n",
+ __FUNCTION__, __LINE__, space, space_desc,
+ pci_addr, pci_addr + req_size - 1,
+ slot, win, devreg);
+#endif
+
+ goto done;
+ } /* endif DevIO(x) not pointed */
+ mbase = pcibr_soft->bs_slot[win].bss_devio.bssd_base;
+
+ /* Now check for request incompat with DevIO(x)
+ */
+ if ((mspace != space) ||
+ (pci_addr < mbase) ||
+ ((pci_addr + req_size) > (mbase + msize)) ||
+ ((flags & PCIIO_BYTE_STREAM) && !(devreg & BRIDGE_DEV_DEV_SWAP)) ||
+ (!(flags & PCIIO_BYTE_STREAM) && (devreg & BRIDGE_DEV_DEV_SWAP)))
+ continue;
+
+ /* DevIO(x) window is pointed at PCI space
+ * that includes our target. Calculate the
+ * final XIO address, release the lock and
+ * return.
+ */
+ xio_addr = BRIDGE_DEVIO(win) + (pci_addr - mbase);
+
+#if DEBUG && PCI_DEBUG
+ printk("%s LINE %d map to space %d [0x%p..0x%p] for slot %d uses DevIO(%d)\n",
+ __FUNCTION__, __LINE__, space, pci_addr, pci_addr + req_size - 1, slot, win);
+#endif
+ goto done;
+ }
+
+ switch (space) {
+ /*
+ * Accesses to device decode
+ * areas that do a not fit
+ * within the DevIO(x) space are
+ * modified to be accesses via
+ * the direct mapping areas.
+ *
+ * If necessary, drivers can
+ * explicitly ask for mappings
+ * into these address spaces,
+ * but this should never be needed.
+ */
+ case PCIIO_SPACE_MEM: /* "mem space" */
+ case PCIIO_SPACE_MEM32: /* "mem, use 32-bit-wide bus" */
+ if ((pci_addr + BRIDGE_PCI_MEM32_BASE + req_size - 1) <=
+ BRIDGE_PCI_MEM32_LIMIT)
+ xio_addr = pci_addr + BRIDGE_PCI_MEM32_BASE;
+ break;
+
+ case PCIIO_SPACE_MEM64: /* "mem, use 64-bit-wide bus" */
+ if ((pci_addr + BRIDGE_PCI_MEM64_BASE + req_size - 1) <=
+ BRIDGE_PCI_MEM64_LIMIT)
+ xio_addr = pci_addr + BRIDGE_PCI_MEM64_BASE;
+ break;
+
+ case PCIIO_SPACE_IO: /* "i/o space" */
+ /* Bridge Hardware Bug WAR #482741:
+ * The 4G area that maps directly from
+ * XIO space to PCI I/O space is busted
+ * until Bridge Rev D.
+ */
+ if ((pcibr_soft->bs_rev_num > BRIDGE_PART_REV_C) &&
+ ((pci_addr + BRIDGE_PCI_IO_BASE + req_size - 1) <=
+ BRIDGE_PCI_IO_LIMIT))
+ xio_addr = pci_addr + BRIDGE_PCI_IO_BASE;
+ break;
+ }
+
+ /* Check that "Direct PIO" byteswapping matches,
+ * try to change it if it does not.
+ */
+ if (xio_addr != XIO_NOWHERE) {
+ unsigned bst; /* nonzero to set bytestream */
+ unsigned *bfp; /* addr of record of how swapper is set */
+ unsigned swb; /* which control bit to mung */
+ unsigned bfo; /* current swapper setting */
+ unsigned bfn; /* desired swapper setting */
+
+ bfp = ((space == PCIIO_SPACE_IO)
+ ? (&pcibr_soft->bs_pio_end_io)
+ : (&pcibr_soft->bs_pio_end_mem));
+
+ bfo = *bfp;
+
+ bst = flags & PCIIO_BYTE_STREAM;
+
+ bfn = bst ? PCIIO_BYTE_STREAM : PCIIO_WORD_VALUES;
+
+ if (bfn == bfo) { /* we already match. */
+ ;
+ } else if (bfo != 0) { /* we have a conflict. */
+#if DEBUG && PCI_DEBUG
+ printk("pcibr_addr_pci_to_xio: swap conflict in space %d , was%s%s, want%s%s\n",
+ space,
+ bfo & PCIIO_BYTE_STREAM ? " BYTE_STREAM" : "",
+ bfo & PCIIO_WORD_VALUES ? " WORD_VALUES" : "",
+ bfn & PCIIO_BYTE_STREAM ? " BYTE_STREAM" : "",
+ bfn & PCIIO_WORD_VALUES ? " WORD_VALUES" : "");
+#endif
+ xio_addr = XIO_NOWHERE;
+ } else { /* OK to make the change. */
+ bridgereg_t octl, nctl;
+
+ swb = (space == PCIIO_SPACE_IO) ? BRIDGE_CTRL_IO_SWAP : BRIDGE_CTRL_MEM_SWAP;
+ octl = bridge->b_wid_control;
+ nctl = bst ? octl | swb : octl & ~swb;
+
+ if (octl != nctl) /* make the change if any */
+ bridge->b_wid_control = nctl;
+
+ *bfp = bfn; /* record the assignment */
+
+#if DEBUG && PCI_DEBUG
+ printk("pcibr_addr_pci_to_xio: swap for space %d set to%s%s\n",
+ space,
+ bfn & PCIIO_BYTE_STREAM ? " BYTE_STREAM" : "",
+ bfn & PCIIO_WORD_VALUES ? " WORD_VALUES" : "");
+#endif
+ }
+ }
+ done:
+ pcibr_unlock(pcibr_soft, s);
+ return xio_addr;
+}
+
+/*ARGSUSED6 */
+pcibr_piomap_t
+pcibr_piomap_alloc(devfs_handle_t pconn_vhdl,
+ device_desc_t dev_desc,
+ pciio_space_t space,
+ iopaddr_t pci_addr,
+ size_t req_size,
+ size_t req_size_max,
+ unsigned flags)
+{
+ pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
+ pciio_info_t pciio_info = &pcibr_info->f_c;
+ pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
+
+ pcibr_piomap_t *mapptr;
+ pcibr_piomap_t maplist;
+ pcibr_piomap_t pcibr_piomap;
+ iopaddr_t xio_addr;
+ xtalk_piomap_t xtalk_piomap;
+ unsigned s;
+
+ /* Make sure that the req sizes are non-zero */
+ if ((req_size < 1) || (req_size_max < 1))
+ return NULL;
+
+ /*
+ * Code to translate slot/space/addr
+ * into xio_addr is common between
+ * this routine and pcibr_piotrans_addr.
+ */
+ xio_addr = pcibr_addr_pci_to_xio(pconn_vhdl, pciio_slot, space, pci_addr, req_size, flags);
+
+ if (xio_addr == XIO_NOWHERE)
+ return NULL;
+
+ /* Check the piomap list to see if there is already an allocated
+ * piomap entry but not in use. If so use that one. Otherwise
+ * allocate a new piomap entry and add it to the piomap list
+ */
+ mapptr = &(pcibr_info->f_piomap);
+
+ s = pcibr_lock(pcibr_soft);
+ for (pcibr_piomap = *mapptr;
+ pcibr_piomap != NULL;
+ pcibr_piomap = pcibr_piomap->bp_next) {
+ if (pcibr_piomap->bp_mapsz == 0)
+ break;
+ }
+
+ if (pcibr_piomap)
+ mapptr = NULL;
+ else {
+ pcibr_unlock(pcibr_soft, s);
+ NEW(pcibr_piomap);
+ }
+
+ pcibr_piomap->bp_dev = pconn_vhdl;
+ pcibr_piomap->bp_slot = pciio_slot;
+ pcibr_piomap->bp_flags = flags;
+ pcibr_piomap->bp_space = space;
+ pcibr_piomap->bp_pciaddr = pci_addr;
+ pcibr_piomap->bp_mapsz = req_size;
+ pcibr_piomap->bp_soft = pcibr_soft;
+ pcibr_piomap->bp_toc[0] = 0;
+
+ if (mapptr) {
+ s = pcibr_lock(pcibr_soft);
+ maplist = *mapptr;
+ pcibr_piomap->bp_next = maplist;
+ *mapptr = pcibr_piomap;
+ }
+ pcibr_unlock(pcibr_soft, s);
+
+
+ if (pcibr_piomap) {
+ xtalk_piomap =
+ xtalk_piomap_alloc(xconn_vhdl, 0,
+ xio_addr,
+ req_size, req_size_max,
+ flags & PIOMAP_FLAGS);
+ if (xtalk_piomap) {
+ pcibr_piomap->bp_xtalk_addr = xio_addr;
+ pcibr_piomap->bp_xtalk_pio = xtalk_piomap;
+ } else {
+ pcibr_piomap->bp_mapsz = 0;
+ pcibr_piomap = 0;
+ }
+ }
+ return pcibr_piomap;
+}
+
+/*ARGSUSED */
+void
+pcibr_piomap_free(pcibr_piomap_t pcibr_piomap)
+{
+ xtalk_piomap_free(pcibr_piomap->bp_xtalk_pio);
+ pcibr_piomap->bp_xtalk_pio = 0;
+ pcibr_piomap->bp_mapsz = 0;
+}
+
+/*ARGSUSED */
+caddr_t
+pcibr_piomap_addr(pcibr_piomap_t pcibr_piomap,
+ iopaddr_t pci_addr,
+ size_t req_size)
+{
+ return xtalk_piomap_addr(pcibr_piomap->bp_xtalk_pio,
+ pcibr_piomap->bp_xtalk_addr +
+ pci_addr - pcibr_piomap->bp_pciaddr,
+ req_size);
+}
+
+/*ARGSUSED */
+void
+pcibr_piomap_done(pcibr_piomap_t pcibr_piomap)
+{
+ xtalk_piomap_done(pcibr_piomap->bp_xtalk_pio);
+}
+
+/*ARGSUSED */
+caddr_t
+pcibr_piotrans_addr(devfs_handle_t pconn_vhdl,
+ device_desc_t dev_desc,
+ pciio_space_t space,
+ iopaddr_t pci_addr,
+ size_t req_size,
+ unsigned flags)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
+
+ iopaddr_t xio_addr;
+
+ xio_addr = pcibr_addr_pci_to_xio(pconn_vhdl, pciio_slot, space, pci_addr, req_size, flags);
+
+ if (xio_addr == XIO_NOWHERE)
+ return NULL;
+
+ return xtalk_piotrans_addr(xconn_vhdl, 0, xio_addr, req_size, flags & PIOMAP_FLAGS);
+}
+
+/*
+ * PIO Space allocation and management.
+ * Allocate and Manage the PCI PIO space (mem and io space)
+ * This routine is pretty simplistic at this time, and
+ * does pretty trivial management of allocation and freeing..
+ * The current scheme is prone for fragmentation..
+ * Change the scheme to use bitmaps.
+ */
+
+/*ARGSUSED */
+iopaddr_t
+pcibr_piospace_alloc(devfs_handle_t pconn_vhdl,
+ device_desc_t dev_desc,
+ pciio_space_t space,
+ size_t req_size,
+ size_t alignment)
+{
+ pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
+ pciio_info_t pciio_info = &pcibr_info->f_c;
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+
+ pciio_piospace_t piosp;
+ int s;
+
+ iopaddr_t *pciaddr, *pcilast;
+ iopaddr_t start_addr;
+ size_t align_mask;
+
+ /*
+ * Check for proper alignment
+ */
+ ASSERT(alignment >= NBPP);
+ ASSERT((alignment & (alignment - 1)) == 0);
+
+ align_mask = alignment - 1;
+ s = pcibr_lock(pcibr_soft);
+
+ /*
+ * First look if a previously allocated chunk exists.
+ */
+ if ((piosp = pcibr_info->f_piospace) != (pciio_piospace_t)0) {
+ /*
+ * Look through the list for a right sized free chunk.
+ */
+ do {
+ if (piosp->free &&
+ (piosp->space == space) &&
+ (piosp->count >= req_size) &&
+ !(piosp->start & align_mask)) {
+ piosp->free = 0;
+ pcibr_unlock(pcibr_soft, s);
+ return piosp->start;
+ }
+ piosp = piosp->next;
+ } while (piosp);
+ }
+ ASSERT(!piosp);
+
+ switch (space) {
+ case PCIIO_SPACE_IO:
+ pciaddr = &pcibr_soft->bs_spinfo.pci_io_base;
+ pcilast = &pcibr_soft->bs_spinfo.pci_io_last;
+ break;
+ case PCIIO_SPACE_MEM:
+ case PCIIO_SPACE_MEM32:
+ pciaddr = &pcibr_soft->bs_spinfo.pci_mem_base;
+ pcilast = &pcibr_soft->bs_spinfo.pci_mem_last;
+ break;
+ default:
+ ASSERT(0);
+ pcibr_unlock(pcibr_soft, s);
+ return 0;
+ }
+
+ start_addr = *pciaddr;
+
+ /*
+ * Align start_addr.
+ */
+ if (start_addr & align_mask)
+ start_addr = (start_addr + align_mask) & ~align_mask;
+
+ if ((start_addr + req_size) > *pcilast) {
+ /*
+ * If too big a request, reject it.
+ */
+ pcibr_unlock(pcibr_soft, s);
+ return 0;
+ }
+ *pciaddr = (start_addr + req_size);
+
+ NEW(piosp);
+ piosp->free = 0;
+ piosp->space = space;
+ piosp->start = start_addr;
+ piosp->count = req_size;
+ piosp->next = pcibr_info->f_piospace;
+ pcibr_info->f_piospace = piosp;
+
+ pcibr_unlock(pcibr_soft, s);
+ return start_addr;
+}
+
+/*ARGSUSED */
+void
+pcibr_piospace_free(devfs_handle_t pconn_vhdl,
+ pciio_space_t space,
+ iopaddr_t pciaddr,
+ size_t req_size)
+{
+ pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pcibr_info->f_mfast;
+
+ pciio_piospace_t piosp;
+ int s;
+ char name[1024];
+
+ /*
+ * Look through the bridge data structures for the pciio_piospace_t
+ * structure corresponding to 'pciaddr'
+ */
+ s = pcibr_lock(pcibr_soft);
+ piosp = pcibr_info->f_piospace;
+ while (piosp) {
+ /*
+ * Piospace free can only be for the complete
+ * chunk and not parts of it..
+ */
+ if (piosp->start == pciaddr) {
+ if (piosp->count == req_size)
+ break;
+ /*
+ * Improper size passed for freeing..
+ * Print a message and break;
+ */
+ hwgraph_vertex_name_get(pconn_vhdl, name, 1024);
+ PRINT_WARNING("pcibr_piospace_free: error");
+ PRINT_WARNING("Device %s freeing size (0x%lx) different than allocated (0x%lx)",
+ name, req_size, piosp->count);
+ PRINT_WARNING("Freeing 0x%lx instead", piosp->count);
+ break;
+ }
+ piosp = piosp->next;
+ }
+
+ if (!piosp) {
+ PRINT_WARNING(
+ "pcibr_piospace_free: Address 0x%lx size 0x%lx - No match\n",
+ pciaddr, req_size);
+ pcibr_unlock(pcibr_soft, s);
+ return;
+ }
+ piosp->free = 1;
+ pcibr_unlock(pcibr_soft, s);
+ return;
+}
+
+/* =====================================================================
+ * DMA MANAGEMENT
+ *
+ * The Bridge ASIC provides three methods of doing
+ * DMA: via a "direct map" register available in
+ * 32-bit PCI space (which selects a contiguous 2G
+ * address space on some other widget), via
+ * "direct" addressing via 64-bit PCI space (all
+ * destination information comes from the PCI
+ * address, including transfer attributes), and via
+ * a "mapped" region that allows a bunch of
+ * different small mappings to be established with
+ * the PMU.
+ *
+ * For efficiency, we most prefer to use the 32-bit
+ * direct mapping facility, since it requires no
+ * resource allocations. The advantage of using the
+ * PMU over the 64-bit direct is that single-cycle
+ * PCI addressing can be used; the advantage of
+ * using 64-bit direct over PMU addressing is that
+ * we do not have to allocate entries in the PMU.
+ */
+
+/*
+ * Convert PCI-generic software flags and Bridge-specific software flags
+ * into Bridge-specific Direct Map attribute bits.
+ */
+LOCAL iopaddr_t
+pcibr_flags_to_d64(unsigned flags, pcibr_soft_t pcibr_soft)
+{
+ iopaddr_t attributes = 0;
+
+ /* Sanity check: Bridge only allows use of VCHAN1 via 64-bit addrs */
+#ifdef IRIX
+ ASSERT_ALWAYS(!(flags & PCIBR_VCHAN1) || (flags & PCIIO_DMA_A64));
+#endif
+
+ /* Generic macro flags
+ */
+ if (flags & PCIIO_DMA_DATA) { /* standard data channel */
+ attributes &= ~PCI64_ATTR_BAR; /* no barrier bit */
+ attributes |= PCI64_ATTR_PREF; /* prefetch on */
+ }
+ if (flags & PCIIO_DMA_CMD) { /* standard command channel */
+ attributes |= PCI64_ATTR_BAR; /* barrier bit on */
+ attributes &= ~PCI64_ATTR_PREF; /* disable prefetch */
+ }
+ /* Generic detail flags
+ */
+ if (flags & PCIIO_PREFETCH)
+ attributes |= PCI64_ATTR_PREF;
+ if (flags & PCIIO_NOPREFETCH)
+ attributes &= ~PCI64_ATTR_PREF;
+
+ /* the swap bit is in the address attributes for xbridge */
+ if (pcibr_soft->bs_xbridge) {
+ if (flags & PCIIO_BYTE_STREAM)
+ attributes |= PCI64_ATTR_SWAP;
+ if (flags & PCIIO_WORD_VALUES)
+ attributes &= ~PCI64_ATTR_SWAP;
+ }
+
+ /* Provider-specific flags
+ */
+ if (flags & PCIBR_BARRIER)
+ attributes |= PCI64_ATTR_BAR;
+ if (flags & PCIBR_NOBARRIER)
+ attributes &= ~PCI64_ATTR_BAR;
+
+ if (flags & PCIBR_PREFETCH)
+ attributes |= PCI64_ATTR_PREF;
+ if (flags & PCIBR_NOPREFETCH)
+ attributes &= ~PCI64_ATTR_PREF;
+
+ if (flags & PCIBR_PRECISE)
+ attributes |= PCI64_ATTR_PREC;
+ if (flags & PCIBR_NOPRECISE)
+ attributes &= ~PCI64_ATTR_PREC;
+
+ if (flags & PCIBR_VCHAN1)
+ attributes |= PCI64_ATTR_VIRTUAL;
+ if (flags & PCIBR_VCHAN0)
+ attributes &= ~PCI64_ATTR_VIRTUAL;
+
+ return (attributes);
+}
+
+/*
+ * Convert PCI-generic software flags and Bridge-specific software flags
+ * into Bridge-specific Address Translation Entry attribute bits.
+ */
+LOCAL bridge_ate_t
+pcibr_flags_to_ate(unsigned flags)
+{
+ bridge_ate_t attributes;
+
+ /* default if nothing specified:
+ * NOBARRIER
+ * NOPREFETCH
+ * NOPRECISE
+ * COHERENT
+ * Plus the valid bit
+ */
+ attributes = ATE_CO | ATE_V;
+
+ /* Generic macro flags
+ */
+ if (flags & PCIIO_DMA_DATA) { /* standard data channel */
+ attributes &= ~ATE_BAR; /* no barrier */
+ attributes |= ATE_PREF; /* prefetch on */
+ }
+ if (flags & PCIIO_DMA_CMD) { /* standard command channel */
+ attributes |= ATE_BAR; /* barrier bit on */
+ attributes &= ~ATE_PREF; /* disable prefetch */
+ }
+ /* Generic detail flags
+ */
+ if (flags & PCIIO_PREFETCH)
+ attributes |= ATE_PREF;
+ if (flags & PCIIO_NOPREFETCH)
+ attributes &= ~ATE_PREF;
+
+ /* Provider-specific flags
+ */
+ if (flags & PCIBR_BARRIER)
+ attributes |= ATE_BAR;
+ if (flags & PCIBR_NOBARRIER)
+ attributes &= ~ATE_BAR;
+
+ if (flags & PCIBR_PREFETCH)
+ attributes |= ATE_PREF;
+ if (flags & PCIBR_NOPREFETCH)
+ attributes &= ~ATE_PREF;
+
+ if (flags & PCIBR_PRECISE)
+ attributes |= ATE_PREC;
+ if (flags & PCIBR_NOPRECISE)
+ attributes &= ~ATE_PREC;
+
+ return (attributes);
+}
+
+/*ARGSUSED */
+pcibr_dmamap_t
+pcibr_dmamap_alloc(devfs_handle_t pconn_vhdl,
+ device_desc_t dev_desc,
+ size_t req_size_max,
+ unsigned flags)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
+ pciio_slot_t slot;
+ xwidgetnum_t xio_port;
+
+ xtalk_dmamap_t xtalk_dmamap;
+ pcibr_dmamap_t pcibr_dmamap;
+ int ate_count;
+ int ate_index;
+
+ /* merge in forced flags */
+ flags |= pcibr_soft->bs_dma_flags;
+
+ NEWf(pcibr_dmamap, flags);
+ if (!pcibr_dmamap)
+ return 0;
+
+ xtalk_dmamap = xtalk_dmamap_alloc(xconn_vhdl, dev_desc, req_size_max,
+ flags & DMAMAP_FLAGS);
+ if (!xtalk_dmamap) {
+#if PCIBR_ATE_DEBUG
+ printk("pcibr_attach: xtalk_dmamap_alloc failed\n");
+#endif
+ DEL(pcibr_dmamap);
+ return 0;
+ }
+ xio_port = pcibr_soft->bs_mxid;
+ slot = pciio_info_slot_get(pciio_info);
+
+ pcibr_dmamap->bd_dev = pconn_vhdl;
+ pcibr_dmamap->bd_slot = slot;
+ pcibr_dmamap->bd_soft = pcibr_soft;
+ pcibr_dmamap->bd_xtalk = xtalk_dmamap;
+ pcibr_dmamap->bd_max_size = req_size_max;
+ pcibr_dmamap->bd_xio_port = xio_port;
+
+ if (flags & PCIIO_DMA_A64) {
+ if (!pcibr_try_set_device(pcibr_soft, slot, flags, BRIDGE_DEV_D64_BITS)) {
+ iopaddr_t pci_addr;
+ int have_rrbs;
+ int min_rrbs;
+
+ /* Device is capable of A64 operations,
+ * and the attributes of the DMA are
+ * consistant with any previous DMA
+ * mappings using shared resources.
+ */
+
+ pci_addr = pcibr_flags_to_d64(flags, pcibr_soft);
+
+ pcibr_dmamap->bd_flags = flags;
+ pcibr_dmamap->bd_xio_addr = 0;
+ pcibr_dmamap->bd_pci_addr = pci_addr;
+
+ /* Make sure we have an RRB (or two).
+ */
+ if (!(pcibr_soft->bs_rrb_fixed & (1 << slot))) {
+ if (flags & PCIBR_VCHAN1)
+ slot += PCIBR_RRB_SLOT_VIRTUAL;
+ have_rrbs = pcibr_soft->bs_rrb_valid[slot];
+ if (have_rrbs < 2) {
+ if (pci_addr & PCI64_ATTR_PREF)
+ min_rrbs = 2;
+ else
+ min_rrbs = 1;
+ if (have_rrbs < min_rrbs)
+ do_pcibr_rrb_autoalloc(pcibr_soft, slot, min_rrbs - have_rrbs);
+ }
+ }
+#if PCIBR_ATE_DEBUG
+ printk("pcibr_dmamap_alloc: using direct64\n");
+#endif
+ return pcibr_dmamap;
+ }
+#if PCIBR_ATE_DEBUG
+ printk("pcibr_dmamap_alloc: unable to use direct64\n");
+#endif
+ flags &= ~PCIIO_DMA_A64;
+ }
+ if (flags & PCIIO_FIXED) {
+ /* warning: mappings may fail later,
+ * if direct32 can't get to the address.
+ */
+ if (!pcibr_try_set_device(pcibr_soft, slot, flags, BRIDGE_DEV_D32_BITS)) {
+ /* User desires DIRECT A32 operations,
+ * and the attributes of the DMA are
+ * consistant with any previous DMA
+ * mappings using shared resources.
+ * Mapping calls may fail if target
+ * is outside the direct32 range.
+ */
+#if PCIBR_ATE_DEBUG
+ printk("pcibr_dmamap_alloc: using direct32\n");
+#endif
+ pcibr_dmamap->bd_flags = flags;
+ pcibr_dmamap->bd_xio_addr = pcibr_soft->bs_dir_xbase;
+ pcibr_dmamap->bd_pci_addr = PCI32_DIRECT_BASE;
+ return pcibr_dmamap;
+ }
+#if PCIBR_ATE_DEBUG
+ printk("pcibr_dmamap_alloc: unable to use direct32\n");
+#endif
+ /* If the user demands FIXED and we can't
+ * give it to him, fail.
+ */
+ xtalk_dmamap_free(xtalk_dmamap);
+ DEL(pcibr_dmamap);
+ return 0;
+ }
+ /*
+ * Allocate Address Translation Entries from the mapping RAM.
+ * Unless the PCIBR_NO_ATE_ROUNDUP flag is specified,
+ * the maximum number of ATEs is based on the worst-case
+ * scenario, where the requested target is in the
+ * last byte of an ATE; thus, mapping IOPGSIZE+2
+ * does end up requiring three ATEs.
+ */
+ if (!(flags & PCIBR_NO_ATE_ROUNDUP)) {
+ ate_count = IOPG((IOPGSIZE - 1) /* worst case start offset */
+ +req_size_max /* max mapping bytes */
+ - 1) + 1; /* round UP */
+ } else { /* assume requested target is page aligned */
+ ate_count = IOPG(req_size_max /* max mapping bytes */
+ - 1) + 1; /* round UP */
+ }
+
+ ate_index = pcibr_ate_alloc(pcibr_soft, ate_count);
+
+ if (ate_index != -1) {
+ if (!pcibr_try_set_device(pcibr_soft, slot, flags, BRIDGE_DEV_PMU_BITS)) {
+ bridge_ate_t ate_proto;
+ int have_rrbs;
+ int min_rrbs;
+
+#if PCIBR_ATE_DEBUG
+ printk("pcibr_dmamap_alloc: using PMU\n");
+#endif
+
+ ate_proto = pcibr_flags_to_ate(flags);
+
+ pcibr_dmamap->bd_flags = flags;
+ pcibr_dmamap->bd_pci_addr =
+ PCI32_MAPPED_BASE + IOPGSIZE * ate_index;
+ /*
+ * for xbridge the byte-swap bit == bit 29 of pci address
+ */
+ if (pcibr_soft->bs_xbridge) {
+ if (flags & PCIIO_BYTE_STREAM)
+ ATE_SWAP_ON(pcibr_dmamap->bd_pci_addr);
+ /*
+ * If swap was set in bss_device in pcibr_endian_set()
+ * we need to change the address bit.
+ */
+ if (pcibr_soft->bs_slot[slot].bss_device &
+ BRIDGE_DEV_SWAP_PMU)
+ ATE_SWAP_ON(pcibr_dmamap->bd_pci_addr);
+ if (flags & PCIIO_WORD_VALUES)
+ ATE_SWAP_OFF(pcibr_dmamap->bd_pci_addr);
+ }
+ pcibr_dmamap->bd_xio_addr = 0;
+ pcibr_dmamap->bd_ate_ptr = pcibr_ate_addr(pcibr_soft, ate_index);
+ pcibr_dmamap->bd_ate_index = ate_index;
+ pcibr_dmamap->bd_ate_count = ate_count;
+ pcibr_dmamap->bd_ate_proto = ate_proto;
+
+ /* Make sure we have an RRB (or two).
+ */
+ if (!(pcibr_soft->bs_rrb_fixed & (1 << slot))) {
+ have_rrbs = pcibr_soft->bs_rrb_valid[slot];
+ if (have_rrbs < 2) {
+ if (ate_proto & ATE_PREF)
+ min_rrbs = 2;
+ else
+ min_rrbs = 1;
+ if (have_rrbs < min_rrbs)
+ do_pcibr_rrb_autoalloc(pcibr_soft, slot, min_rrbs - have_rrbs);
+ }
+ }
+ if (ate_index >= pcibr_soft->bs_int_ate_size &&
+ !pcibr_soft->bs_xbridge) {
+ bridge_t *bridge = pcibr_soft->bs_base;
+ volatile unsigned *cmd_regp;
+ unsigned cmd_reg;
+ unsigned s;
+
+ pcibr_dmamap->bd_flags |= PCIBR_DMAMAP_SSRAM;
+
+ s = pcibr_lock(pcibr_soft);
+ cmd_regp = &(bridge->
+ b_type0_cfg_dev[slot].
+ l[PCI_CFG_COMMAND / 4]);
+ cmd_reg = *cmd_regp;
+ pcibr_soft->bs_slot[slot].bss_cmd_pointer = cmd_regp;
+ pcibr_soft->bs_slot[slot].bss_cmd_shadow = cmd_reg;
+ pcibr_unlock(pcibr_soft, s);
+ }
+ return pcibr_dmamap;
+ }
+#if PCIBR_ATE_DEBUG
+ printk("pcibr_dmamap_alloc: unable to use PMU\n");
+#endif
+ pcibr_ate_free(pcibr_soft, ate_index, ate_count);
+ }
+ /* total failure: sorry, you just can't
+ * get from here to there that way.
+ */
+#if PCIBR_ATE_DEBUG
+ printk("pcibr_dmamap_alloc: complete failure.\n");
+#endif
+ xtalk_dmamap_free(xtalk_dmamap);
+ DEL(pcibr_dmamap);
+ return 0;
+}
+
+/*ARGSUSED */
+void
+pcibr_dmamap_free(pcibr_dmamap_t pcibr_dmamap)
+{
+ pcibr_soft_t pcibr_soft = pcibr_dmamap->bd_soft;
+ pciio_slot_t slot = pcibr_dmamap->bd_slot;
+
+#ifdef IRIX
+ unsigned flags = pcibr_dmamap->bd_flags;
+#endif
+
+ /* Make sure that bss_ext_ates_active
+ * is properly kept up to date.
+ */
+#ifdef IRIX
+ if (PCIBR_DMAMAP_BUSY & flags)
+ if (PCIBR_DMAMAP_SSRAM & flags)
+ atomicAddInt(&(pcibr_soft->
+ bs_slot[slot].
+ bss_ext_ates_active), -1);
+#endif
+
+ xtalk_dmamap_free(pcibr_dmamap->bd_xtalk);
+
+ if (pcibr_dmamap->bd_flags & PCIIO_DMA_A64) {
+ pcibr_release_device(pcibr_soft, slot, BRIDGE_DEV_D64_BITS);
+ }
+ if (pcibr_dmamap->bd_ate_count) {
+ pcibr_ate_free(pcibr_dmamap->bd_soft,
+ pcibr_dmamap->bd_ate_index,
+ pcibr_dmamap->bd_ate_count);
+ pcibr_release_device(pcibr_soft, slot, BRIDGE_DEV_PMU_BITS);
+ }
+ DEL(pcibr_dmamap);
+}
+
+/*
+ * Setup an Address Translation Entry as specified. Use either the Bridge
+ * internal maps or the external map RAM, as appropriate.
+ */
+LOCAL bridge_ate_p
+pcibr_ate_addr(pcibr_soft_t pcibr_soft,
+ int ate_index)
+{
+ bridge_t *bridge = pcibr_soft->bs_base;
+
+ return (ate_index < pcibr_soft->bs_int_ate_size)
+ ? &(bridge->b_int_ate_ram[ate_index].wr)
+ : &(bridge->b_ext_ate_ram[ate_index]);
+}
+
+/*
+ * pcibr_addr_xio_to_pci: given a PIO range, hand
+ * back the corresponding base PCI MEM address;
+ * this is used to short-circuit DMA requests that
+ * loop back onto this PCI bus.
+ */
+LOCAL iopaddr_t
+pcibr_addr_xio_to_pci(pcibr_soft_t soft,
+ iopaddr_t xio_addr,
+ size_t req_size)
+{
+ iopaddr_t xio_lim = xio_addr + req_size - 1;
+ iopaddr_t pci_addr;
+ pciio_slot_t slot;
+
+ if ((xio_addr >= BRIDGE_PCI_MEM32_BASE) &&
+ (xio_lim <= BRIDGE_PCI_MEM32_LIMIT)) {
+ pci_addr = xio_addr - BRIDGE_PCI_MEM32_BASE;
+ return pci_addr;
+ }
+ if ((xio_addr >= BRIDGE_PCI_MEM64_BASE) &&
+ (xio_lim <= BRIDGE_PCI_MEM64_LIMIT)) {
+ pci_addr = xio_addr - BRIDGE_PCI_MEM64_BASE;
+ return pci_addr;
+ }
+ for (slot = 0; slot < 8; ++slot)
+ if ((xio_addr >= BRIDGE_DEVIO(slot)) &&
+ (xio_lim < BRIDGE_DEVIO(slot + 1))) {
+ bridgereg_t dev;
+
+ dev = soft->bs_slot[slot].bss_device;
+ pci_addr = dev & BRIDGE_DEV_OFF_MASK;
+ pci_addr <<= BRIDGE_DEV_OFF_ADDR_SHFT;
+ pci_addr += xio_addr - BRIDGE_DEVIO(slot);
+ return (dev & BRIDGE_DEV_DEV_IO_MEM) ? pci_addr : PCI_NOWHERE;
+ }
+ return 0;
+}
+
+/* We are starting to get more complexity
+ * surrounding writing ATEs, so pull
+ * the writing code into this new function.
+ * XXX mail ranga@engr for IP27 prom!
+ */
+
+#if PCIBR_FREEZE_TIME
+#define ATE_FREEZE() s = ate_freeze(pcibr_dmamap, &freeze_time, cmd_regs)
+#else
+#define ATE_FREEZE() s = ate_freeze(pcibr_dmamap, cmd_regs)
+#endif
+
+LOCAL unsigned
+ate_freeze(pcibr_dmamap_t pcibr_dmamap,
+#if PCIBR_FREEZE_TIME
+ unsigned *freeze_time_ptr,
+#endif
+ unsigned *cmd_regs)
+{
+ pcibr_soft_t pcibr_soft = pcibr_dmamap->bd_soft;
+#ifdef IRIX
+ int dma_slot = pcibr_dmamap->bd_slot;
+#endif
+ int ext_ates = pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM;
+ int slot;
+
+ unsigned s;
+ unsigned cmd_reg;
+ volatile unsigned *cmd_lwa;
+ unsigned cmd_lwd;
+
+ if (!ext_ates)
+ return 0;
+
+ /* Bridge Hardware Bug WAR #484930:
+ * Bridge can't handle updating External ATEs
+ * while DMA is occuring that uses External ATEs,
+ * even if the particular ATEs involved are disjoint.
+ */
+
+ /* need to prevent anyone else from
+ * unfreezing the grant while we
+ * are working; also need to prevent
+ * this thread from being interrupted
+ * to keep PCI grant freeze time
+ * at an absolute minimum.
+ */
+ s = pcibr_lock(pcibr_soft);
+
+#ifdef IRIX
+ /* just in case pcibr_dmamap_done was not called */
+ if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_BUSY) {
+ pcibr_dmamap->bd_flags &= ~PCIBR_DMAMAP_BUSY;
+ if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM)
+ atomicAddInt(&(pcibr_soft->
+ bs_slot[dma_slot].
+ bss_ext_ates_active), -1);
+ xtalk_dmamap_done(pcibr_dmamap->bd_xtalk);
+ }
+#endif
+#if PCIBR_FREEZE_TIME
+ *freeze_time_ptr = get_timestamp();
+#endif
+
+ cmd_lwa = 0;
+ for (slot = 0; slot < 8; ++slot)
+ if (pcibr_soft->
+ bs_slot[slot].
+ bss_ext_ates_active) {
+
+ cmd_reg = pcibr_soft->
+ bs_slot[slot].
+ bss_cmd_shadow;
+ if (cmd_reg & PCI_CMD_BUS_MASTER) {
+ cmd_lwa = pcibr_soft->
+ bs_slot[slot].
+ bss_cmd_pointer;
+ cmd_lwd = cmd_reg ^ PCI_CMD_BUS_MASTER;
+ cmd_lwa[0] = cmd_lwd;
+ }
+ cmd_regs[slot] = cmd_reg;
+ } else
+ cmd_regs[slot] = 0;
+
+ if (cmd_lwa) {
+ bridge_t *bridge = pcibr_soft->bs_base;
+
+ /* Read the last master bit that has been cleared. This PIO read
+ * on the PCI bus is to ensure the completion of any DMAs that
+ * are due to bus requests issued by PCI devices before the
+ * clearing of master bits.
+ */
+ cmd_lwa[0];
+
+ /* Flush all the write buffers in the bridge */
+ for (slot = 0; slot < 8; ++slot)
+ if (pcibr_soft->
+ bs_slot[slot].
+ bss_ext_ates_active) {
+ /* Flush the write buffer associated with this
+ * PCI device which might be using dma map RAM.
+ */
+ bridge->b_wr_req_buf[slot].reg;
+ }
+ }
+ return s;
+}
+
+#define ATE_WRITE() ate_write(ate_ptr, ate_count, ate)
+
+LOCAL void
+ate_write(bridge_ate_p ate_ptr,
+ int ate_count,
+ bridge_ate_t ate)
+{
+ while (ate_count-- > 0) {
+ *ate_ptr++ = ate;
+ ate += IOPGSIZE;
+ }
+}
+
+
+#if PCIBR_FREEZE_TIME
+#define ATE_THAW() ate_thaw(pcibr_dmamap, ate_index, ate, ate_total, freeze_time, cmd_regs, s)
+#else
+#define ATE_THAW() ate_thaw(pcibr_dmamap, ate_index, cmd_regs, s)
+#endif
+
+LOCAL void
+ate_thaw(pcibr_dmamap_t pcibr_dmamap,
+ int ate_index,
+#if PCIBR_FREEZE_TIME
+ bridge_ate_t ate,
+ int ate_total,
+ unsigned freeze_time_start,
+#endif
+ unsigned *cmd_regs,
+ unsigned s)
+{
+ pcibr_soft_t pcibr_soft = pcibr_dmamap->bd_soft;
+#ifdef IRIX
+ int dma_slot = pcibr_dmamap->bd_slot;
+#endif
+ int slot;
+ bridge_t *bridge = pcibr_soft->bs_base;
+ int ext_ates = pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM;
+
+ unsigned cmd_reg;
+
+#if PCIBR_FREEZE_TIME
+ unsigned freeze_time;
+ static unsigned max_freeze_time = 0;
+ static unsigned max_ate_total;
+#endif
+
+ if (!ext_ates)
+ return;
+
+ /* restore cmd regs */
+ for (slot = 0; slot < 8; ++slot)
+ if ((cmd_reg = cmd_regs[slot]) & PCI_CMD_BUS_MASTER)
+ bridge->b_type0_cfg_dev[slot].l[PCI_CFG_COMMAND / 4] = cmd_reg;
+
+ pcibr_dmamap->bd_flags |= PCIBR_DMAMAP_BUSY;
+#ifdef IRIX
+ atomicAddInt(&(pcibr_soft->
+ bs_slot[dma_slot].
+ bss_ext_ates_active), 1);
+#endif
+
+#if PCIBR_FREEZE_TIME
+ freeze_time = get_timestamp() - freeze_time_start;
+
+ if ((max_freeze_time < freeze_time) ||
+ (max_ate_total < ate_total)) {
+ if (max_freeze_time < freeze_time)
+ max_freeze_time = freeze_time;
+ if (max_ate_total < ate_total)
+ max_ate_total = ate_total;
+ pcibr_unlock(pcibr_soft, s);
+ printk("%s: pci freeze time %d usec for %d ATEs\n"
+ "\tfirst ate: %R\n",
+ pcibr_soft->bs_name,
+ freeze_time * 1000 / 1250,
+ ate_total,
+ ate, ate_bits);
+ } else
+#endif
+ pcibr_unlock(pcibr_soft, s);
+}
+
+/*ARGSUSED */
+iopaddr_t
+pcibr_dmamap_addr(pcibr_dmamap_t pcibr_dmamap,
+ paddr_t paddr,
+ size_t req_size)
+{
+ pcibr_soft_t pcibr_soft;
+ iopaddr_t xio_addr;
+ xwidgetnum_t xio_port;
+ iopaddr_t pci_addr;
+ unsigned flags;
+
+ ASSERT(pcibr_dmamap != NULL);
+ ASSERT(req_size > 0);
+ ASSERT(req_size <= pcibr_dmamap->bd_max_size);
+
+ pcibr_soft = pcibr_dmamap->bd_soft;
+
+ flags = pcibr_dmamap->bd_flags;
+
+ xio_addr = xtalk_dmamap_addr(pcibr_dmamap->bd_xtalk, paddr, req_size);
+ if (XIO_PACKED(xio_addr)) {
+ xio_port = XIO_PORT(xio_addr);
+ xio_addr = XIO_ADDR(xio_addr);
+ } else
+ xio_port = pcibr_dmamap->bd_xio_port;
+
+ /* If this DMA is to an addres that
+ * refers back to this Bridge chip,
+ * reduce it back to the correct
+ * PCI MEM address.
+ */
+ if (xio_port == pcibr_soft->bs_xid) {
+ pci_addr = pcibr_addr_xio_to_pci(pcibr_soft, xio_addr, req_size);
+ } else if (flags & PCIIO_DMA_A64) {
+ /* A64 DMA:
+ * always use 64-bit direct mapping,
+ * which always works.
+ * Device(x) was set up during
+ * dmamap allocation.
+ */
+
+ /* attributes are already bundled up into bd_pci_addr.
+ */
+ pci_addr = pcibr_dmamap->bd_pci_addr
+ | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT)
+ | xio_addr;
+
+ /* Bridge Hardware WAR #482836:
+ * If the transfer is not cache aligned
+ * and the Bridge Rev is <= B, force
+ * prefetch to be off.
+ */
+ if (flags & PCIBR_NOPREFETCH)
+ pci_addr &= ~PCI64_ATTR_PREF;
+
+#if DEBUG && PCIBR_DMA_DEBUG
+ printk("pcibr_dmamap_addr (direct64):\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\tXIO port 0x%x offset 0x%x\n"
+ "\treturning PCI 0x%x\n",
+ paddr, paddr + req_size - 1,
+ xio_port, xio_addr, pci_addr);
+#endif
+ } else if (flags & PCIIO_FIXED) {
+ /* A32 direct DMA:
+ * always use 32-bit direct mapping,
+ * which may fail.
+ * Device(x) was set up during
+ * dmamap allocation.
+ */
+
+ if (xio_port != pcibr_soft->bs_dir_xport)
+ pci_addr = 0; /* wrong DIDN */
+ else if (xio_addr < pcibr_dmamap->bd_xio_addr)
+ pci_addr = 0; /* out of range */
+ else if ((xio_addr + req_size) >
+ (pcibr_dmamap->bd_xio_addr + BRIDGE_DMA_DIRECT_SIZE))
+ pci_addr = 0; /* out of range */
+ else
+ pci_addr = pcibr_dmamap->bd_pci_addr +
+ xio_addr - pcibr_dmamap->bd_xio_addr;
+
+#if DEBUG && PCIBR_DMA_DEBUG
+ printk("pcibr_dmamap_addr (direct32):\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\tXIO port 0x%x offset 0x%x\n"
+ "\treturning PCI 0x%x\n",
+ paddr, paddr + req_size - 1,
+ xio_port, xio_addr, pci_addr);
+#endif
+ } else {
+ bridge_t *bridge = pcibr_soft->bs_base;
+ iopaddr_t offset = IOPGOFF(xio_addr);
+ bridge_ate_t ate_proto = pcibr_dmamap->bd_ate_proto;
+ int ate_count = IOPG(offset + req_size - 1) + 1;
+
+ int ate_index = pcibr_dmamap->bd_ate_index;
+ unsigned cmd_regs[8];
+ unsigned s;
+
+#if PCIBR_FREEZE_TIME
+ int ate_total = ate_count;
+ unsigned freeze_time;
+#endif
+
+#if PCIBR_ATE_DEBUG
+ bridge_ate_t ate_cmp;
+ bridge_ate_p ate_cptr;
+ unsigned ate_lo, ate_hi;
+ int ate_bad = 0;
+ int ate_rbc = 0;
+#endif
+ bridge_ate_p ate_ptr = pcibr_dmamap->bd_ate_ptr;
+ bridge_ate_t ate;
+
+ /* Bridge Hardware WAR #482836:
+ * If the transfer is not cache aligned
+ * and the Bridge Rev is <= B, force
+ * prefetch to be off.
+ */
+ if (flags & PCIBR_NOPREFETCH)
+ ate_proto &= ~ATE_PREF;
+
+ ate = ate_proto
+ | (xio_port << ATE_TIDSHIFT)
+ | (xio_addr - offset);
+
+ pci_addr = pcibr_dmamap->bd_pci_addr + offset;
+
+ /* Fill in our mapping registers
+ * with the appropriate xtalk data,
+ * and hand back the PCI address.
+ */
+
+ ASSERT(ate_count > 0);
+ if (ate_count <= pcibr_dmamap->bd_ate_count) {
+ ATE_FREEZE();
+ ATE_WRITE();
+ ATE_THAW();
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+ } else {
+ /* The number of ATE's required is greater than the number
+ * allocated for this map. One way this can happen is if
+ * pcibr_dmamap_alloc() was called with the PCIBR_NO_ATE_ROUNDUP
+ * flag, and then when that map is used (right now), the
+ * target address tells us we really did need to roundup.
+ * The other possibility is that the map is just plain too
+ * small to handle the requested target area.
+ */
+#if PCIBR_ATE_DEBUG
+ PRINT_WARNING( "pcibr_dmamap_addr :\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\tate_count 0x%x bd_ate_count 0x%x\n"
+ "\tATE's required > number allocated\n",
+ paddr, paddr + req_size - 1,
+ ate_count, pcibr_dmamap->bd_ate_count);
+#endif
+ pci_addr = 0;
+ }
+
+ }
+ return pci_addr;
+}
+
+/*ARGSUSED */
+alenlist_t
+pcibr_dmamap_list(pcibr_dmamap_t pcibr_dmamap,
+ alenlist_t palenlist,
+ unsigned flags)
+{
+ pcibr_soft_t pcibr_soft;
+#ifdef IRIX
+ bridge_t *bridge;
+#else
+ bridge_t *bridge=NULL;
+#endif
+
+ unsigned al_flags = (flags & PCIIO_NOSLEEP) ? AL_NOSLEEP : 0;
+ int inplace = flags & PCIIO_INPLACE;
+
+ alenlist_t pciio_alenlist = 0;
+ alenlist_t xtalk_alenlist;
+ size_t length;
+ iopaddr_t offset;
+ unsigned direct64;
+#ifdef IRIX
+ int ate_index;
+ int ate_count;
+ int ate_total = 0;
+ bridge_ate_p ate_ptr;
+ bridge_ate_t ate_proto;
+#else
+ int ate_index = 0;
+ int ate_count = 0;
+ int ate_total = 0;
+ bridge_ate_p ate_ptr = (bridge_ate_p)0;
+ bridge_ate_t ate_proto = (bridge_ate_t)0;
+#endif
+ bridge_ate_t ate_prev;
+ bridge_ate_t ate;
+ alenaddr_t xio_addr;
+ xwidgetnum_t xio_port;
+ iopaddr_t pci_addr;
+ alenaddr_t new_addr;
+
+ unsigned cmd_regs[8];
+ unsigned s = 0;
+
+#if PCIBR_FREEZE_TIME
+ unsigned freeze_time;
+#endif
+ int ate_freeze_done = 0; /* To pair ATE_THAW
+ * with an ATE_FREEZE
+ */
+
+ pcibr_soft = pcibr_dmamap->bd_soft;
+
+ xtalk_alenlist = xtalk_dmamap_list(pcibr_dmamap->bd_xtalk, palenlist,
+ flags & DMAMAP_FLAGS);
+ if (!xtalk_alenlist)
+ goto fail;
+
+ alenlist_cursor_init(xtalk_alenlist, 0, NULL);
+
+ if (inplace) {
+ pciio_alenlist = xtalk_alenlist;
+ } else {
+ pciio_alenlist = alenlist_create(al_flags);
+ if (!pciio_alenlist)
+ goto fail;
+ }
+
+ direct64 = pcibr_dmamap->bd_flags & PCIIO_DMA_A64;
+ if (!direct64) {
+ bridge = pcibr_soft->bs_base;
+ ate_ptr = pcibr_dmamap->bd_ate_ptr;
+ ate_index = pcibr_dmamap->bd_ate_index;
+ ate_proto = pcibr_dmamap->bd_ate_proto;
+ ATE_FREEZE();
+ ate_freeze_done = 1; /* Remember that we need to do an ATE_THAW */
+ }
+ pci_addr = pcibr_dmamap->bd_pci_addr;
+
+ ate_prev = 0; /* matches no valid ATEs */
+ while (ALENLIST_SUCCESS ==
+ alenlist_get(xtalk_alenlist, NULL, 0,
+ &xio_addr, &length, al_flags)) {
+ if (XIO_PACKED(xio_addr)) {
+ xio_port = XIO_PORT(xio_addr);
+ xio_addr = XIO_ADDR(xio_addr);
+ } else
+ xio_port = pcibr_dmamap->bd_xio_port;
+
+ if (xio_port == pcibr_soft->bs_xid) {
+ new_addr = pcibr_addr_xio_to_pci(pcibr_soft, xio_addr, length);
+ if (new_addr == PCI_NOWHERE)
+ goto fail;
+ } else if (direct64) {
+ new_addr = pci_addr | xio_addr
+ | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT);
+
+ /* Bridge Hardware WAR #482836:
+ * If the transfer is not cache aligned
+ * and the Bridge Rev is <= B, force
+ * prefetch to be off.
+ */
+ if (flags & PCIBR_NOPREFETCH)
+ new_addr &= ~PCI64_ATTR_PREF;
+
+ } else {
+ /* calculate the ate value for
+ * the first address. If it
+ * matches the previous
+ * ATE written (ie. we had
+ * multiple blocks in the
+ * same IOPG), then back up
+ * and reuse that ATE.
+ *
+ * We are NOT going to
+ * aggressively try to
+ * reuse any other ATEs.
+ */
+ offset = IOPGOFF(xio_addr);
+ ate = ate_proto
+ | (xio_port << ATE_TIDSHIFT)
+ | (xio_addr - offset);
+ if (ate == ate_prev) {
+#if PCIBR_ATE_DEBUG
+ printk("pcibr_dmamap_list: ATE share\n");
+#endif
+ ate_ptr--;
+ ate_index--;
+ pci_addr -= IOPGSIZE;
+ }
+ new_addr = pci_addr + offset;
+
+ /* Fill in the hardware ATEs
+ * that contain this block.
+ */
+ ate_count = IOPG(offset + length - 1) + 1;
+ ate_total += ate_count;
+
+ /* Ensure that this map contains enough ATE's */
+ if (ate_total > pcibr_dmamap->bd_ate_count) {
+#if PCIBR_ATE_DEBUG
+ PRINT_WARNING( "pcibr_dmamap_list :\n"
+ "\twanted xio_addr [0x%x..0x%x]\n"
+ "\tate_total 0x%x bd_ate_count 0x%x\n"
+ "\tATE's required > number allocated\n",
+ xio_addr, xio_addr + length - 1,
+ ate_total, pcibr_dmamap->bd_ate_count);
+#endif
+ goto fail;
+ }
+
+ ATE_WRITE();
+
+ ate_index += ate_count;
+ ate_ptr += ate_count;
+
+ ate_count <<= IOPFNSHIFT;
+ ate += ate_count;
+ pci_addr += ate_count;
+ }
+
+ /* write the PCI DMA address
+ * out to the scatter-gather list.
+ */
+ if (inplace) {
+ if (ALENLIST_SUCCESS !=
+ alenlist_replace(pciio_alenlist, NULL,
+ &new_addr, &length, al_flags))
+ goto fail;
+ } else {
+ if (ALENLIST_SUCCESS !=
+ alenlist_append(pciio_alenlist,
+ new_addr, length, al_flags))
+ goto fail;
+ }
+ }
+ if (!inplace)
+ alenlist_done(xtalk_alenlist);
+
+ /* Reset the internal cursor of the alenlist to be returned back
+ * to the caller.
+ */
+ alenlist_cursor_init(pciio_alenlist, 0, NULL);
+
+
+ /* In case an ATE_FREEZE was done do the ATE_THAW to unroll all the
+ * changes that ATE_FREEZE has done to implement the external SSRAM
+ * bug workaround.
+ */
+ if (ate_freeze_done) {
+ ATE_THAW();
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+ }
+ return pciio_alenlist;
+
+ fail:
+ /* There are various points of failure after doing an ATE_FREEZE
+ * We need to do an ATE_THAW. Otherwise the ATEs are locked forever.
+ * The decision to do an ATE_THAW needs to be based on whether a
+ * an ATE_FREEZE was done before.
+ */
+ if (ate_freeze_done) {
+ ATE_THAW();
+ bridge->b_wid_tflush;
+ }
+ if (pciio_alenlist && !inplace)
+ alenlist_destroy(pciio_alenlist);
+ return 0;
+}
+
+/*ARGSUSED */
+void
+pcibr_dmamap_done(pcibr_dmamap_t pcibr_dmamap)
+{
+ /*
+ * We could go through and invalidate ATEs here;
+ * for performance reasons, we don't.
+ * We also don't enforce the strict alternation
+ * between _addr/_list and _done, but Hub does.
+ */
+
+#ifdef IRIX
+ if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_BUSY) {
+ pcibr_dmamap->bd_flags &= ~PCIBR_DMAMAP_BUSY;
+
+ if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM)
+ atomicAddInt(&(pcibr_dmamap->bd_soft->
+ bs_slot[pcibr_dmamap->bd_slot].
+ bss_ext_ates_active), -1);
+ }
+#endif
+
+ xtalk_dmamap_done(pcibr_dmamap->bd_xtalk);
+}
+
+
+/*
+ * For each bridge, the DIR_OFF value in the Direct Mapping Register
+ * determines the PCI to Crosstalk memory mapping to be used for all
+ * 32-bit Direct Mapping memory accesses. This mapping can be to any
+ * node in the system. This function will return that compact node id.
+ */
+
+/*ARGSUSED */
+cnodeid_t
+pcibr_get_dmatrans_node(devfs_handle_t pconn_vhdl)
+{
+
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+
+ return(NASID_TO_COMPACT_NODEID(NASID_GET(pcibr_soft->bs_dir_xbase)));
+}
+
+/*ARGSUSED */
+iopaddr_t
+pcibr_dmatrans_addr(devfs_handle_t pconn_vhdl,
+ device_desc_t dev_desc,
+ paddr_t paddr,
+ size_t req_size,
+ unsigned flags)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
+ pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
+ pcibr_soft_slot_t slotp = &pcibr_soft->bs_slot[pciio_slot];
+
+ xwidgetnum_t xio_port;
+ iopaddr_t xio_addr;
+ iopaddr_t pci_addr;
+
+ int have_rrbs;
+ int min_rrbs;
+
+ /* merge in forced flags */
+ flags |= pcibr_soft->bs_dma_flags;
+
+ xio_addr = xtalk_dmatrans_addr(xconn_vhdl, 0, paddr, req_size,
+ flags & DMAMAP_FLAGS);
+
+ if (!xio_addr) {
+#if PCIBR_DMA_DEBUG
+ printk("pcibr_dmatrans_addr:\n"
+ "\tpciio connection point %v\n"
+ "\txtalk connection point %v\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\txtalk_dmatrans_addr returned 0x%x\n",
+ pconn_vhdl, xconn_vhdl,
+ paddr, paddr + req_size - 1,
+ xio_addr);
+#endif
+ return 0;
+ }
+ /*
+ * find which XIO port this goes to.
+ */
+ if (XIO_PACKED(xio_addr)) {
+ if (xio_addr == XIO_NOWHERE) {
+#if PCIBR_DMA_DEBUG
+ printk("pcibr_dmatrans_addr:\n"
+ "\tpciio connection point %v\n"
+ "\txtalk connection point %v\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\txtalk_dmatrans_addr returned 0x%x\n",
+ pconn_vhdl, xconn_vhdl,
+ paddr, paddr + req_size - 1,
+ xio_addr);
+#endif
+ return 0;
+ }
+ xio_port = XIO_PORT(xio_addr);
+ xio_addr = XIO_ADDR(xio_addr);
+
+ } else
+ xio_port = pcibr_soft->bs_mxid;
+
+ /*
+ * If this DMA comes back to us,
+ * return the PCI MEM address on
+ * which it would land, or NULL
+ * if the target is something
+ * on bridge other than PCI MEM.
+ */
+ if (xio_port == pcibr_soft->bs_xid) {
+ pci_addr = pcibr_addr_xio_to_pci(pcibr_soft, xio_addr, req_size);
+ return pci_addr;
+ }
+ /* If the caller can use A64, try to
+ * satisfy the request with the 64-bit
+ * direct map. This can fail if the
+ * configuration bits in Device(x)
+ * conflict with our flags.
+ */
+
+ if (flags & PCIIO_DMA_A64) {
+ pci_addr = slotp->bss_d64_base;
+ if (!(flags & PCIBR_VCHAN1))
+ flags |= PCIBR_VCHAN0;
+ if ((pci_addr != PCIBR_D64_BASE_UNSET) &&
+ (flags == slotp->bss_d64_flags)) {
+
+ pci_addr |= xio_addr
+ | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT);
+
+#if DEBUG && PCIBR_DMA_DEBUG
+#if HWG_PERF_CHECK
+ if (xio_addr != 0x20000000)
+#endif
+ printk("pcibr_dmatrans_addr: [reuse]\n"
+ "\tpciio connection point %v\n"
+ "\txtalk connection point %v\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\txtalk_dmatrans_addr returned 0x%x\n"
+ "\tdirect 64bit address is 0x%x\n",
+ pconn_vhdl, xconn_vhdl,
+ paddr, paddr + req_size - 1,
+ xio_addr, pci_addr);
+#endif
+ return (pci_addr);
+ }
+ if (!pcibr_try_set_device(pcibr_soft, pciio_slot, flags, BRIDGE_DEV_D64_BITS)) {
+ pci_addr = pcibr_flags_to_d64(flags, pcibr_soft);
+ slotp->bss_d64_flags = flags;
+ slotp->bss_d64_base = pci_addr;
+ pci_addr |= xio_addr
+ | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT);
+
+ /* Make sure we have an RRB (or two).
+ */
+ if (!(pcibr_soft->bs_rrb_fixed & (1 << pciio_slot))) {
+ if (flags & PCIBR_VCHAN1)
+ pciio_slot += PCIBR_RRB_SLOT_VIRTUAL;
+ have_rrbs = pcibr_soft->bs_rrb_valid[pciio_slot];
+ if (have_rrbs < 2) {
+ if (pci_addr & PCI64_ATTR_PREF)
+ min_rrbs = 2;
+ else
+ min_rrbs = 1;
+ if (have_rrbs < min_rrbs)
+ do_pcibr_rrb_autoalloc(pcibr_soft, pciio_slot, min_rrbs - have_rrbs);
+ }
+ }
+#if PCIBR_DMA_DEBUG
+#if HWG_PERF_CHECK
+ if (xio_addr != 0x20000000)
+#endif
+ printk("pcibr_dmatrans_addr:\n"
+ "\tpciio connection point %v\n"
+ "\txtalk connection point %v\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\txtalk_dmatrans_addr returned 0x%x\n"
+ "\tdirect 64bit address is 0x%x\n"
+ "\tnew flags: 0x%x\n",
+ pconn_vhdl, xconn_vhdl,
+ paddr, paddr + req_size - 1,
+ xio_addr, pci_addr, (uint64_t) flags);
+#endif
+ return (pci_addr);
+ }
+ /* our flags conflict with Device(x).
+ */
+ flags = flags
+ & ~PCIIO_DMA_A64
+ & ~PCIBR_VCHAN0
+ ;
+
+#if PCIBR_DMA_DEBUG
+ printk("pcibr_dmatrans_addr:\n"
+ "\tpciio connection point %v\n"
+ "\txtalk connection point %v\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\txtalk_dmatrans_addr returned 0x%x\n"
+ "\tUnable to set Device(x) bits for Direct-64\n",
+ pconn_vhdl, xconn_vhdl,
+ paddr, paddr + req_size - 1,
+ xio_addr);
+#endif
+ }
+ /* Try to satisfy the request with the 32-bit direct
+ * map. This can fail if the configuration bits in
+ * Device(x) conflict with our flags, or if the
+ * target address is outside where DIR_OFF points.
+ */
+ {
+ size_t map_size = 1ULL << 31;
+ iopaddr_t xio_base = pcibr_soft->bs_dir_xbase;
+ iopaddr_t offset = xio_addr - xio_base;
+ iopaddr_t endoff = req_size + offset;
+
+ if ((req_size > map_size) ||
+ (xio_addr < xio_base) ||
+ (xio_port != pcibr_soft->bs_dir_xport) ||
+ (endoff > map_size)) {
+#if PCIBR_DMA_DEBUG
+ printk("pcibr_dmatrans_addr:\n"
+ "\tpciio connection point %v\n"
+ "\txtalk connection point %v\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\txtalk_dmatrans_addr returned 0x%x\n"
+ "\txio region outside direct32 target\n",
+ pconn_vhdl, xconn_vhdl,
+ paddr, paddr + req_size - 1,
+ xio_addr);
+#endif
+ } else {
+ pci_addr = slotp->bss_d32_base;
+ if ((pci_addr != PCIBR_D32_BASE_UNSET) &&
+ (flags == slotp->bss_d32_flags)) {
+
+ pci_addr |= offset;
+
+#if DEBUG && PCIBR_DMA_DEBUG
+ printk("pcibr_dmatrans_addr: [reuse]\n"
+ "\tpciio connection point %v\n"
+ "\txtalk connection point %v\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\txtalk_dmatrans_addr returned 0x%x\n"
+ "\tmapped via direct32 offset 0x%x\n"
+ "\twill DMA via pci addr 0x%x\n",
+ pconn_vhdl, xconn_vhdl,
+ paddr, paddr + req_size - 1,
+ xio_addr, offset, pci_addr);
+#endif
+ return (pci_addr);
+ }
+ if (!pcibr_try_set_device(pcibr_soft, pciio_slot, flags, BRIDGE_DEV_D32_BITS)) {
+
+ pci_addr = PCI32_DIRECT_BASE;
+ slotp->bss_d32_flags = flags;
+ slotp->bss_d32_base = pci_addr;
+ pci_addr |= offset;
+
+ /* Make sure we have an RRB (or two).
+ */
+ if (!(pcibr_soft->bs_rrb_fixed & (1 << pciio_slot))) {
+ have_rrbs = pcibr_soft->bs_rrb_valid[pciio_slot];
+ if (have_rrbs < 2) {
+ if (slotp->bss_device & BRIDGE_DEV_PREF)
+ min_rrbs = 2;
+ else
+ min_rrbs = 1;
+ if (have_rrbs < min_rrbs)
+ do_pcibr_rrb_autoalloc(pcibr_soft, pciio_slot, min_rrbs - have_rrbs);
+ }
+ }
+#if PCIBR_DMA_DEBUG
+#if HWG_PERF_CHECK
+ if (xio_addr != 0x20000000)
+#endif
+ printk("pcibr_dmatrans_addr:\n"
+ "\tpciio connection point %v\n"
+ "\txtalk connection point %v\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\txtalk_dmatrans_addr returned 0x%x\n"
+ "\tmapped via direct32 offset 0x%x\n"
+ "\twill DMA via pci addr 0x%x\n"
+ "\tnew flags: 0x%x\n",
+ pconn_vhdl, xconn_vhdl,
+ paddr, paddr + req_size - 1,
+ xio_addr, offset, pci_addr, (uint64_t) flags);
+#endif
+ return (pci_addr);
+ }
+ /* our flags conflict with Device(x).
+ */
+#if PCIBR_DMA_DEBUG
+ printk("pcibr_dmatrans_addr:\n"
+ "\tpciio connection point %v\n"
+ "\txtalk connection point %v\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\txtalk_dmatrans_addr returned 0x%x\n"
+ "\tUnable to set Device(x) bits for Direct-32\n",
+ pconn_vhdl, xconn_vhdl,
+ paddr, paddr + req_size - 1,
+ xio_addr);
+#endif
+ }
+ }
+
+#if PCIBR_DMA_DEBUG
+ printk("pcibr_dmatrans_addr:\n"
+ "\tpciio connection point %v\n"
+ "\txtalk connection point %v\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\txtalk_dmatrans_addr returned 0x%x\n"
+ "\tno acceptable PCI address found or constructable\n",
+ pconn_vhdl, xconn_vhdl,
+ paddr, paddr + req_size - 1,
+ xio_addr);
+#endif
+
+ return 0;
+}
+
+/*ARGSUSED */
+alenlist_t
+pcibr_dmatrans_list(devfs_handle_t pconn_vhdl,
+ device_desc_t dev_desc,
+ alenlist_t palenlist,
+ unsigned flags)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
+ pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
+ pcibr_soft_slot_t slotp = &pcibr_soft->bs_slot[pciio_slot];
+ xwidgetnum_t xio_port;
+
+ alenlist_t pciio_alenlist = 0;
+ alenlist_t xtalk_alenlist = 0;
+
+ int inplace;
+ unsigned direct64;
+ unsigned al_flags;
+
+ iopaddr_t xio_base;
+ alenaddr_t xio_addr;
+ size_t xio_size;
+
+ size_t map_size;
+ iopaddr_t pci_base;
+ alenaddr_t pci_addr;
+
+ unsigned relbits = 0;
+
+ /* merge in forced flags */
+ flags |= pcibr_soft->bs_dma_flags;
+
+ inplace = flags & PCIIO_INPLACE;
+ direct64 = flags & PCIIO_DMA_A64;
+ al_flags = (flags & PCIIO_NOSLEEP) ? AL_NOSLEEP : 0;
+
+ if (direct64) {
+ map_size = 1ull << 48;
+ xio_base = 0;
+ pci_base = slotp->bss_d64_base;
+ if ((pci_base != PCIBR_D64_BASE_UNSET) &&
+ (flags == slotp->bss_d64_flags)) {
+ /* reuse previous base info */
+ } else if (pcibr_try_set_device(pcibr_soft, pciio_slot, flags, BRIDGE_DEV_D64_BITS) < 0) {
+ /* DMA configuration conflict */
+ goto fail;
+ } else {
+ relbits = BRIDGE_DEV_D64_BITS;
+ pci_base =
+ pcibr_flags_to_d64(flags, pcibr_soft);
+ }
+ } else {
+ xio_base = pcibr_soft->bs_dir_xbase;
+ map_size = 1ull << 31;
+ pci_base = slotp->bss_d32_base;
+ if ((pci_base != PCIBR_D32_BASE_UNSET) &&
+ (flags == slotp->bss_d32_flags)) {
+ /* reuse previous base info */
+ } else if (pcibr_try_set_device(pcibr_soft, pciio_slot, flags, BRIDGE_DEV_D32_BITS) < 0) {
+ /* DMA configuration conflict */
+ goto fail;
+ } else {
+ relbits = BRIDGE_DEV_D32_BITS;
+ pci_base = PCI32_DIRECT_BASE;
+ }
+ }
+
+ xtalk_alenlist = xtalk_dmatrans_list(xconn_vhdl, 0, palenlist,
+ flags & DMAMAP_FLAGS);
+ if (!xtalk_alenlist)
+ goto fail;
+
+ alenlist_cursor_init(xtalk_alenlist, 0, NULL);
+
+ if (inplace) {
+ pciio_alenlist = xtalk_alenlist;
+ } else {
+ pciio_alenlist = alenlist_create(al_flags);
+ if (!pciio_alenlist)
+ goto fail;
+ }
+
+ while (ALENLIST_SUCCESS ==
+ alenlist_get(xtalk_alenlist, NULL, 0,
+ &xio_addr, &xio_size, al_flags)) {
+
+ /*
+ * find which XIO port this goes to.
+ */
+ if (XIO_PACKED(xio_addr)) {
+ if (xio_addr == XIO_NOWHERE) {
+#if PCIBR_DMA_DEBUG
+ printk("pcibr_dmatrans_addr:\n"
+ "\tpciio connection point %v\n"
+ "\txtalk connection point %v\n"
+ "\twanted paddr [0x%x..0x%x]\n"
+ "\txtalk_dmatrans_addr returned 0x%x\n",
+ pconn_vhdl, xconn_vhdl,
+ paddr, paddr + req_size - 1,
+ xio_addr);
+#endif
+ return 0;
+ }
+ xio_port = XIO_PORT(xio_addr);
+ xio_addr = XIO_ADDR(xio_addr);
+ } else
+ xio_port = pcibr_soft->bs_mxid;
+
+ /*
+ * If this DMA comes back to us,
+ * return the PCI MEM address on
+ * which it would land, or NULL
+ * if the target is something
+ * on bridge other than PCI MEM.
+ */
+ if (xio_port == pcibr_soft->bs_xid) {
+ pci_addr = pcibr_addr_xio_to_pci(pcibr_soft, xio_addr, xio_size);
+#ifdef IRIX
+ if (pci_addr == NULL)
+#else
+ if ( (pci_addr == (alenaddr_t)NULL) )
+#endif
+ goto fail;
+ } else if (direct64) {
+ ASSERT(xio_port != 0);
+ pci_addr = pci_base | xio_addr
+ | ((uint64_t) xio_port << PCI64_ATTR_TARG_SHFT);
+ } else {
+ iopaddr_t offset = xio_addr - xio_base;
+ iopaddr_t endoff = xio_size + offset;
+
+ if ((xio_size > map_size) ||
+ (xio_addr < xio_base) ||
+ (xio_port != pcibr_soft->bs_dir_xport) ||
+ (endoff > map_size))
+ goto fail;
+
+ pci_addr = pci_base + (xio_addr - xio_base);
+ }
+
+ /* write the PCI DMA address
+ * out to the scatter-gather list.
+ */
+ if (inplace) {
+ if (ALENLIST_SUCCESS !=
+ alenlist_replace(pciio_alenlist, NULL,
+ &pci_addr, &xio_size, al_flags))
+ goto fail;
+ } else {
+ if (ALENLIST_SUCCESS !=
+ alenlist_append(pciio_alenlist,
+ pci_addr, xio_size, al_flags))
+ goto fail;
+ }
+ }
+
+#ifdef IRIX
+ if (relbits)
+#else
+ if (relbits) {
+#endif
+ if (direct64) {
+ slotp->bss_d64_flags = flags;
+ slotp->bss_d64_base = pci_base;
+ } else {
+ slotp->bss_d32_flags = flags;
+ slotp->bss_d32_base = pci_base;
+ }
+#ifndef IRIX
+ }
+#endif
+ if (!inplace)
+ alenlist_done(xtalk_alenlist);
+
+ /* Reset the internal cursor of the alenlist to be returned back
+ * to the caller.
+ */
+ alenlist_cursor_init(pciio_alenlist, 0, NULL);
+ return pciio_alenlist;
+
+ fail:
+ if (relbits)
+ pcibr_release_device(pcibr_soft, pciio_slot, relbits);
+ if (pciio_alenlist && !inplace)
+ alenlist_destroy(pciio_alenlist);
+ return 0;
+}
+
+void
+pcibr_dmamap_drain(pcibr_dmamap_t map)
+{
+ xtalk_dmamap_drain(map->bd_xtalk);
+}
+
+void
+pcibr_dmaaddr_drain(devfs_handle_t pconn_vhdl,
+ paddr_t paddr,
+ size_t bytes)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
+
+ xtalk_dmaaddr_drain(xconn_vhdl, paddr, bytes);
+}
+
+void
+pcibr_dmalist_drain(devfs_handle_t pconn_vhdl,
+ alenlist_t list)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
+
+ xtalk_dmalist_drain(xconn_vhdl, list);
+}
+
+/*
+ * Get the starting PCIbus address out of the given DMA map.
+ * This function is supposed to be used by a close friend of PCI bridge
+ * since it relies on the fact that the starting address of the map is fixed at
+ * the allocation time in the current implementation of PCI bridge.
+ */
+iopaddr_t
+pcibr_dmamap_pciaddr_get(pcibr_dmamap_t pcibr_dmamap)
+{
+ return (pcibr_dmamap->bd_pci_addr);
+}
+
+/* =====================================================================
+ * INTERRUPT MANAGEMENT
+ */
+
+static unsigned
+pcibr_intr_bits(pciio_info_t info,
+ pciio_intr_line_t lines)
+{
+ pciio_slot_t slot = pciio_info_slot_get(info);
+ unsigned bbits = 0;
+
+ /*
+ * Currently favored mapping from PCI
+ * slot number and INTA/B/C/D to Bridge
+ * PCI Interrupt Bit Number:
+ *
+ * SLOT A B C D
+ * 0 0 4 0 4
+ * 1 1 5 1 5
+ * 2 2 6 2 6
+ * 3 3 7 3 7
+ * 4 4 0 4 0
+ * 5 5 1 5 1
+ * 6 6 2 6 2
+ * 7 7 3 7 3
+ */
+
+ if (slot < 8) {
+ if (lines & (PCIIO_INTR_LINE_A| PCIIO_INTR_LINE_C))
+ bbits |= 1 << slot;
+ if (lines & (PCIIO_INTR_LINE_B| PCIIO_INTR_LINE_D))
+ bbits |= 1 << (slot ^ 4);
+ }
+ return bbits;
+}
+
+#ifdef IRIX
+/* Wrapper for pcibr interrupt threads. */
+static void
+pcibr_intrd(pcibr_intr_t intr)
+{
+ /* Called on each restart */
+ ASSERT(cpuid() == intr->bi_mustruncpu);
+
+#ifdef ITHREAD_LATENCY
+ xthread_update_latstats(intr->bi_tinfo->thd_latstats);
+#endif /* ITHREAD_LATENCY */
+
+ ASSERT(intr->bi_func != NULL);
+ intr->bi_func(intr->bi_arg); /* Invoke the interrupt handler */
+
+ ipsema(&intr->bi_tinfo.thd_isync); /* Sleep 'till next interrupt */
+ /* NOTREACHED */
+}
+
+
+static void
+pcibr_intrd_start(pcibr_intr_t intr)
+{
+ ASSERT(intr->bi_mustruncpu >= 0);
+ setmustrun(intr->bi_mustruncpu);
+
+ xthread_set_func(KT_TO_XT(curthreadp), (xt_func_t *)pcibr_intrd, (void *)intr);
+ atomicSetInt(&intr->bi_tinfo.thd_flags, THD_INIT);
+ ipsema(&intr->bi_tinfo.thd_isync); /* Comes out in pcibr_intrd */
+ /* NOTREACHED */
+}
+
+
+static void
+pcibr_thread_setup(pcibr_intr_t intr, int bridge_levels, ilvl_t intr_swlevel)
+{
+ char thread_name[32];
+
+ sprintf(thread_name, "pcibr_intrd[0x%x]", bridge_levels);
+
+ /* XXX need to adjust priority whenever an interrupt is connected */
+ atomicSetInt(&intr->bi_tinfo.thd_flags, THD_ISTHREAD | THD_REG);
+ xthread_setup(thread_name, intr_swlevel, &intr->bi_tinfo,
+ (xt_func_t *)pcibr_intrd_start,
+ (void *)intr);
+}
+#endif /* IRIX */
+
+
+
+/*ARGSUSED */
+pcibr_intr_t
+pcibr_intr_alloc(devfs_handle_t pconn_vhdl,
+ device_desc_t dev_desc,
+ pciio_intr_line_t lines,
+ devfs_handle_t owner_dev)
+{
+ pcibr_info_t pcibr_info = pcibr_info_get(pconn_vhdl);
+ pciio_slot_t pciio_slot = pcibr_info->f_slot;
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pcibr_info->f_mfast;
+ devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
+ bridge_t *bridge = pcibr_soft->bs_base;
+ int is_threaded;
+ int thread_swlevel;
+
+ xtalk_intr_t *xtalk_intr_p;
+ pcibr_intr_t *pcibr_intr_p;
+ pcibr_intr_list_t *intr_list_p;
+ pcibr_intr_wrap_t *intr_wrap_p;
+
+ unsigned pcibr_int_bits;
+ unsigned pcibr_int_bit;
+ xtalk_intr_t xtalk_intr = (xtalk_intr_t)0;
+ hub_intr_t hub_intr;
+ pcibr_intr_t pcibr_intr;
+ pcibr_intr_list_t intr_entry;
+ pcibr_intr_list_t intr_list;
+ pcibr_intr_wrap_t intr_wrap;
+ bridgereg_t int_dev;
+
+#if DEBUG && INTR_DEBUG
+ printk("%v: pcibr_intr_alloc\n"
+ "%v:%s%s%s%s%s\n",
+ owner_dev, pconn_vhdl,
+ !(lines & 15) ? " No INTs?" : "",
+ lines & 1 ? " INTA" : "",
+ lines & 2 ? " INTB" : "",
+ lines & 4 ? " INTC" : "",
+ lines & 8 ? " INTD" : "");
+#endif
+
+ NEW(pcibr_intr);
+ if (!pcibr_intr)
+ return NULL;
+
+ if (dev_desc) {
+ is_threaded = !(device_desc_flags_get(dev_desc) & D_INTR_NOTHREAD);
+ if (is_threaded)
+ thread_swlevel = device_desc_intr_swlevel_get(dev_desc);
+ } else {
+ extern int default_intr_pri;
+
+ is_threaded = 1; /* PCI interrupts are threaded, by default */
+ thread_swlevel = default_intr_pri;
+ }
+
+ pcibr_intr->bi_dev = pconn_vhdl;
+ pcibr_intr->bi_lines = lines;
+ pcibr_intr->bi_soft = pcibr_soft;
+ pcibr_intr->bi_ibits = 0; /* bits will be added below */
+ pcibr_intr->bi_func = 0; /* unset until connect */
+ pcibr_intr->bi_arg = 0; /* unset until connect */
+ pcibr_intr->bi_flags = is_threaded ? 0 : PCIIO_INTR_NOTHREAD;
+ pcibr_intr->bi_mustruncpu = CPU_NONE;
+
+ pcibr_int_bits = pcibr_soft->bs_intr_bits((pciio_info_t)pcibr_info, lines);
+
+
+ /*
+ * For each PCI interrupt line requested, figure
+ * out which Bridge PCI Interrupt Line it maps
+ * to, and make sure there are xtalk resources
+ * allocated for it.
+ */
+#if DEBUG && INTR_DEBUG
+ printk("pcibr_int_bits: 0x%X\n", pcibr_int_bits);
+#endif
+ for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit ++) {
+ if (pcibr_int_bits & (1 << pcibr_int_bit)) {
+ xtalk_intr_p = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr;
+
+ xtalk_intr = *xtalk_intr_p;
+
+ if (xtalk_intr == NULL) {
+ /*
+ * This xtalk_intr_alloc is constrained for two reasons:
+ * 1) Normal interrupts and error interrupts need to be delivered
+ * through a single xtalk target widget so that there aren't any
+ * ordering problems with DMA, completion interrupts, and error
+ * interrupts. (Use of xconn_vhdl forces this.)
+ *
+ * 2) On IP35, addressing constraints on IP35 and Bridge force
+ * us to use a single PI number for all interrupts from a
+ * single Bridge. (IP35-specific code forces this, and we
+ * verify in pcibr_setwidint.)
+ */
+ xtalk_intr = xtalk_intr_alloc(xconn_vhdl, dev_desc, owner_dev);
+#if DEBUG && INTR_DEBUG
+ printk("%v: xtalk_intr=0x%X\n", xconn_vhdl, xtalk_intr);
+#endif
+
+ /* both an assert and a runtime check on this:
+ * we need to check in non-DEBUG kernels, and
+ * the ASSERT gets us more information when
+ * we use DEBUG kernels.
+ */
+ ASSERT(xtalk_intr != NULL);
+ if (xtalk_intr == NULL) {
+ /* it is quite possible that our
+ * xtalk_intr_alloc failed because
+ * someone else got there first,
+ * and we can find their results
+ * in xtalk_intr_p.
+ */
+ if (!*xtalk_intr_p) {
+#ifdef SUPPORT_PRINTING_V_FORMAT
+ PRINT_ALERT(
+ "pcibr_intr_alloc %v: unable to get xtalk interrupt resources",
+ xconn_vhdl);
+#endif
+ /* yes, we leak resources here. */
+ return 0;
+ }
+ } else if (compare_and_swap_ptr((void **) xtalk_intr_p, NULL, xtalk_intr)) {
+ /*
+ * now tell the bridge which slot is
+ * using this interrupt line.
+ */
+ int_dev = bridge->b_int_device;
+ int_dev &= ~BRIDGE_INT_DEV_MASK(pcibr_int_bit);
+ int_dev |= pciio_slot << BRIDGE_INT_DEV_SHFT(pcibr_int_bit);
+ bridge->b_int_device = int_dev; /* XXXMP */
+
+#if DEBUG && INTR_DEBUG
+ printk("%v: bridge intr bit %d clears my wrb\n",
+ pconn_vhdl, pcibr_int_bit);
+#endif
+ } else {
+ /* someone else got one allocated first;
+ * free the one we just created, and
+ * retrieve the one they allocated.
+ */
+ xtalk_intr_free(xtalk_intr);
+ xtalk_intr = *xtalk_intr_p;
+#if PARANOID
+ /* once xtalk_intr is set, we never clear it,
+ * so if the CAS fails above, this condition
+ * can "never happen" ...
+ */
+ if (!xtalk_intr) {
+ PRINT_ALERT(
+ "pcibr_intr_alloc %v: unable to set xtalk interrupt resources",
+ xconn_vhdl);
+ /* yes, we leak resources here. */
+ return 0;
+ }
+#endif
+ }
+ }
+
+ /*
+ * For threaded drivers, set the interrupt thread to run wherever
+ * the interrupt is targeted.
+ */
+#ifdef notyet
+ if (is_threaded) {
+ cpuid_t old_mustrun = pcibr_intr->bi_mustruncpu;
+ pcibr_intr->bi_mustruncpu = cpuvertex_to_cpuid(xtalk_intr_cpu_get(xtalk_intr));
+ ASSERT(pcibr_intr->bi_mustruncpu >= 0);
+
+ /*
+ * This is possible, but very unlikely: It means that 2 (or more) interrupts
+ * originating on a single Bridge and used by a single device were unable to
+ * find sufficient xtalk interrupt resources that would allow them all to be
+ * handled by the same CPU. If someone tries to target lots of interrupts to
+ * a single CPU, we might hit this case. Things should still operate correctly,
+ * but it's a sub-optimal configuration.
+ */
+ if ((old_mustrun != CPU_NONE) && (old_mustrun != pcibr_intr->bi_mustruncpu)) {
+#ifdef SUPPORT_PRINTING_V_FORMAT
+ PRINT_WARNING( "Conflict on where to schedule interrupts for %v\n", pconn_vhdl);
+#endif
+ PRINT_WARNING( "(on cpu %d or on cpu %d)\n", old_mustrun, pcibr_intr->bi_mustruncpu);
+ }
+ }
+#endif
+
+ pcibr_intr->bi_ibits |= 1 << pcibr_int_bit;
+
+ NEW(intr_entry);
+ intr_entry->il_next = NULL;
+ intr_entry->il_intr = pcibr_intr;
+ intr_entry->il_wrbf = &(bridge->b_wr_req_buf[pciio_slot].reg);
+
+ intr_list_p = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_list;
+ if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {
+ /* we are the first interrupt on this bridge bit.
+ */
+#if DEBUG && INTR_DEBUG
+ printk("%v INT 0x%x (bridge bit %d) allocated [FIRST]\n",
+ pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
+#endif
+ continue;
+ }
+ intr_list = *intr_list_p;
+ pcibr_intr_p = &intr_list->il_intr;
+ if (compare_and_swap_ptr((void **) pcibr_intr_p, NULL, pcibr_intr)) {
+ /* first entry on list was erased,
+ * and we replaced it, so we
+ * don't need our intr_entry.
+ */
+ DEL(intr_entry);
+#if DEBUG && INTR_DEBUG
+ printk("%v INT 0x%x (bridge bit %d) replaces erased first\n",
+ pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
+#endif
+ continue;
+ }
+ intr_list_p = &intr_list->il_next;
+ if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {
+ /* we are the new second interrupt on this bit.
+ * switch to local wrapper.
+ */
+#if DEBUG && INTR_DEBUG
+ printk("%v INT 0x%x (bridge bit %d) is new SECOND\n",
+ pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
+#endif
+ NEW(intr_wrap);
+ intr_wrap->iw_soft = pcibr_soft;
+ intr_wrap->iw_stat = &(bridge->b_int_status);
+ intr_wrap->iw_intr = 1 << pcibr_int_bit;
+ intr_wrap->iw_list = intr_list;
+ intr_wrap_p = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap;
+ if (!compare_and_swap_ptr((void **) intr_wrap_p, NULL, intr_wrap)) {
+ /* someone else set up the wrapper.
+ */
+ DEL(intr_wrap);
+ continue;
+#if DEBUG && INTR_DEBUG
+ } else {
+ printk("%v bridge bit %d wrapper state created\n",
+ pconn_vhdl, pcibr_int_bit);
+#endif
+ }
+ continue;
+ }
+ while (1) {
+ pcibr_intr_p = &intr_list->il_intr;
+ if (compare_and_swap_ptr((void **) pcibr_intr_p, NULL, pcibr_intr)) {
+ /* an entry on list was erased,
+ * and we replaced it, so we
+ * don't need our intr_entry.
+ */
+ DEL(intr_entry);
+#if DEBUG && INTR_DEBUG
+ printk("%v INT 0x%x (bridge bit %d) replaces erased Nth\n",
+ pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
+#endif
+ break;
+ }
+ intr_list_p = &intr_list->il_next;
+ if (compare_and_swap_ptr((void **) intr_list_p, NULL, intr_entry)) {
+ /* entry appended to share list
+ */
+#if DEBUG && INTR_DEBUG
+ printk("%v INT 0x%x (bridge bit %d) is new Nth\n",
+ pconn_vhdl, pcibr_int_bits, pcibr_int_bit);
+#endif
+ break;
+ }
+ /* step to next record in chain
+ */
+ intr_list = *intr_list_p;
+ }
+ }
+ }
+
+#ifdef IRIX
+ if (is_threaded) {
+ /* Set pcibr_intr->bi_tinfo */
+ pcibr_thread_setup(pcibr_intr, pcibr_int_bits, thread_swlevel);
+ ASSERT(!(pcibr_intr->bi_flags & PCIIO_INTR_CONNECTED));
+ }
+#endif
+
+#if DEBUG && INTR_DEBUG
+ printk("%v pcibr_intr_alloc complete\n", pconn_vhdl);
+#endif
+ hub_intr = (hub_intr_t)xtalk_intr;
+ pcibr_intr->bi_irq = hub_intr->i_bit;
+ pcibr_intr->bi_cpu = hub_intr->i_cpuid;
+ return pcibr_intr;
+}
+
+/*ARGSUSED */
+void
+pcibr_intr_free(pcibr_intr_t pcibr_intr)
+{
+ unsigned pcibr_int_bits = pcibr_intr->bi_ibits;
+ pcibr_soft_t pcibr_soft = pcibr_intr->bi_soft;
+ unsigned pcibr_int_bit;
+ pcibr_intr_list_t intr_list;
+ pcibr_intr_wrap_t intr_wrap;
+ xtalk_intr_t *xtalk_intrp;
+
+ for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++) {
+ if (pcibr_int_bits & (1 << pcibr_int_bit)) {
+ for (intr_list =
+ pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_list;
+ intr_list != NULL;
+ intr_list = intr_list->il_next)
+ if (compare_and_swap_ptr((void **) &intr_list->il_intr,
+ pcibr_intr,
+ NULL)) {
+#if DEBUG && INTR_DEBUG
+ printk("%s: cleared a handler from bit %d\n",
+ pcibr_soft->bs_name, pcibr_int_bit);
+#endif
+ }
+ /* If this interrupt line is not being shared between multiple
+ * devices release the xtalk interrupt resources.
+ */
+ intr_wrap =
+ pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap;
+ xtalk_intrp = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr;
+ if ((intr_wrap == NULL) && (*xtalk_intrp)) {
+
+ bridge_t *bridge = pcibr_soft->bs_base;
+ bridgereg_t int_dev;
+
+ xtalk_intr_free(*xtalk_intrp);
+ *xtalk_intrp = 0;
+
+ /* Clear the PCI device interrupt to bridge interrupt pin
+ * mapping.
+ */
+ int_dev = bridge->b_int_device;
+ int_dev &= ~BRIDGE_INT_DEV_MASK(pcibr_int_bit);
+ bridge->b_int_device = int_dev;
+
+ }
+ }
+ }
+ DEL(pcibr_intr);
+}
+
+LOCAL void
+pcibr_setpciint(xtalk_intr_t xtalk_intr)
+{
+ iopaddr_t addr = xtalk_intr_addr_get(xtalk_intr);
+ xtalk_intr_vector_t vect = xtalk_intr_vector_get(xtalk_intr);
+ bridgereg_t *int_addr = (bridgereg_t *)
+ xtalk_intr_sfarg_get(xtalk_intr);
+
+ *int_addr = ((BRIDGE_INT_ADDR_HOST & (addr >> 30)) |
+ (BRIDGE_INT_ADDR_FLD & vect));
+}
+
+/*ARGSUSED */
+int
+pcibr_intr_connect(pcibr_intr_t pcibr_intr,
+ intr_func_t intr_func,
+ intr_arg_t intr_arg,
+ void *thread)
+{
+ pcibr_soft_t pcibr_soft = pcibr_intr->bi_soft;
+ bridge_t *bridge = pcibr_soft->bs_base;
+ unsigned pcibr_int_bits = pcibr_intr->bi_ibits;
+ unsigned pcibr_int_bit;
+ bridgereg_t b_int_enable;
+ unsigned s;
+
+ if (pcibr_intr == NULL)
+ return -1;
+
+#if DEBUG && INTR_DEBUG
+ printk("%v: pcibr_intr_connect 0x%X(0x%X)\n",
+ pcibr_intr->bi_dev, intr_func, intr_arg);
+#endif
+
+ pcibr_intr->bi_func = intr_func;
+ pcibr_intr->bi_arg = intr_arg;
+ *((volatile unsigned *)&pcibr_intr->bi_flags) |= PCIIO_INTR_CONNECTED;
+
+ /*
+ * For each PCI interrupt line requested, figure
+ * out which Bridge PCI Interrupt Line it maps
+ * to, and make sure there are xtalk resources
+ * allocated for it.
+ */
+ for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
+ if (pcibr_int_bits & (1 << pcibr_int_bit)) {
+ pcibr_intr_wrap_t intr_wrap;
+ xtalk_intr_t xtalk_intr;
+ int *setptr;
+
+ xtalk_intr = pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr;
+
+ /* if we have no wrap structure,
+ * tell xtalk to deliver the interrupt
+ * directly to the client.
+ */
+ intr_wrap = pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap;
+ if (intr_wrap == NULL) {
+ xtalk_intr_connect(xtalk_intr,
+ (intr_func_t) intr_func,
+ (intr_arg_t) intr_arg,
+ (xtalk_intr_setfunc_t) pcibr_setpciint,
+ (void *) &(bridge->b_int_addr[pcibr_int_bit].addr),
+ thread);
+#if DEBUG && INTR_DEBUG
+ printk("%v bridge bit %d routed by xtalk\n",
+ pcibr_intr->bi_dev, pcibr_int_bit);
+#endif
+ continue;
+ }
+
+ setptr = &pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_wrap_set;
+ if (*setptr)
+ continue;
+
+
+ /* We have a wrap structure, so we're sharing a Bridge interrupt level */
+
+ xtalk_intr_disconnect(xtalk_intr); /* Disconnect old interrupt */
+
+ /*
+ If the existing xtalk_intr was allocated without the NOTHREAD flag,
+ we need to allocate a new one that's NOTHREAD, and connect to the
+ new one. pcibr_intr_list_func expects to run at interrupt level
+ rather than in a thread. With today's devices, this can't happen,
+ so let's punt on writing the code till we need it (probably never).
+ Instead, just ASSERT that we're a NOTHREAD xtalk_intr.
+ */
+#ifdef IRIX
+ ASSERT_ALWAYS(!(pcibr_intr->bi_flags & PCIIO_INTR_NOTHREAD) ||
+ xtalk_intr_flags_get(xtalk_intr) & XTALK_INTR_NOTHREAD);
+#endif
+
+ /* Use the wrapper dispatch function to handle shared Bridge interrupts */
+ xtalk_intr_connect(xtalk_intr,
+ pcibr_intr_list_func,
+ (intr_arg_t) intr_wrap,
+ (xtalk_intr_setfunc_t) pcibr_setpciint,
+ (void *) &(bridge->b_int_addr[pcibr_int_bit].addr),
+ 0);
+ *setptr = 1;
+
+#if DEBUG && INTR_DEBUG
+ printk("%v bridge bit %d wrapper connected\n",
+ pcibr_intr->bi_dev, pcibr_int_bit);
+#endif
+ }
+ s = pcibr_lock(pcibr_soft);
+ b_int_enable = bridge->b_int_enable;
+ b_int_enable |= pcibr_int_bits;
+ bridge->b_int_enable = b_int_enable;
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+ pcibr_unlock(pcibr_soft, s);
+
+ return 0;
+}
+
+/*ARGSUSED */
+void
+pcibr_intr_disconnect(pcibr_intr_t pcibr_intr)
+{
+ pcibr_soft_t pcibr_soft = pcibr_intr->bi_soft;
+ bridge_t *bridge = pcibr_soft->bs_base;
+ unsigned pcibr_int_bits = pcibr_intr->bi_ibits;
+ unsigned pcibr_int_bit;
+ pcibr_intr_wrap_t intr_wrap;
+ bridgereg_t b_int_enable;
+ unsigned s;
+
+ /* Stop calling the function. Now.
+ */
+ *((volatile unsigned *)&pcibr_intr->bi_flags) &= ~PCIIO_INTR_CONNECTED;
+ pcibr_intr->bi_func = 0;
+ pcibr_intr->bi_arg = 0;
+ /*
+ * For each PCI interrupt line requested, figure
+ * out which Bridge PCI Interrupt Line it maps
+ * to, and disconnect the interrupt.
+ */
+
+ /* don't disable interrupts for lines that
+ * are shared between devices.
+ */
+ for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
+ if ((pcibr_int_bits & (1 << pcibr_int_bit)) &&
+ (pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_wrap_set))
+ pcibr_int_bits &= ~(1 << pcibr_int_bit);
+ if (!pcibr_int_bits)
+ return;
+
+ s = pcibr_lock(pcibr_soft);
+ b_int_enable = bridge->b_int_enable;
+ b_int_enable &= ~pcibr_int_bits;
+ bridge->b_int_enable = b_int_enable;
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+ pcibr_unlock(pcibr_soft, s);
+
+ for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
+ if (pcibr_int_bits & (1 << pcibr_int_bit)) {
+ /* if we have set up the share wrapper,
+ * do not disconnect it.
+ */
+ if (pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_wrap_set)
+ continue;
+
+ xtalk_intr_disconnect(pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr);
+
+ /* if we have a share wrapper state,
+ * connect us up; this closes the hole
+ * where the connection of the wrapper
+ * was in progress as we disconnected.
+ */
+ intr_wrap = pcibr_soft->bs_intr[pcibr_int_bit].bsi_pcibr_intr_wrap;
+ if (intr_wrap == NULL)
+ continue;
+
+
+ xtalk_intr_connect(pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr,
+ pcibr_intr_list_func,
+ (intr_arg_t) intr_wrap,
+ (xtalk_intr_setfunc_t) pcibr_setpciint,
+ (void *) &(bridge->b_int_addr[pcibr_int_bit].addr),
+ 0);
+ }
+}
+
+/*ARGSUSED */
+devfs_handle_t
+pcibr_intr_cpu_get(pcibr_intr_t pcibr_intr)
+{
+ pcibr_soft_t pcibr_soft = pcibr_intr->bi_soft;
+ unsigned pcibr_int_bits = pcibr_intr->bi_ibits;
+ unsigned pcibr_int_bit;
+
+ for (pcibr_int_bit = 0; pcibr_int_bit < 8; pcibr_int_bit++)
+ if (pcibr_int_bits & (1 << pcibr_int_bit))
+ return xtalk_intr_cpu_get(pcibr_soft->bs_intr[pcibr_int_bit].bsi_xtalk_intr);
+ return 0;
+}
+
+/* =====================================================================
+ * INTERRUPT HANDLING
+ */
+LOCAL void
+pcibr_clearwidint(bridge_t *bridge)
+{
+ bridge->b_wid_int_upper = 0;
+ bridge->b_wid_int_lower = 0;
+}
+
+
+LOCAL void
+pcibr_setwidint(xtalk_intr_t intr)
+{
+ xwidgetnum_t targ = xtalk_intr_target_get(intr);
+ iopaddr_t addr = xtalk_intr_addr_get(intr);
+ xtalk_intr_vector_t vect = xtalk_intr_vector_get(intr);
+ widgetreg_t NEW_b_wid_int_upper, NEW_b_wid_int_lower;
+ widgetreg_t OLD_b_wid_int_upper, OLD_b_wid_int_lower;
+
+ bridge_t *bridge = (bridge_t *)xtalk_intr_sfarg_get(intr);
+
+ NEW_b_wid_int_upper = ( (0x000F0000 & (targ << 16)) |
+ XTALK_ADDR_TO_UPPER(addr));
+ NEW_b_wid_int_lower = XTALK_ADDR_TO_LOWER(addr);
+
+ OLD_b_wid_int_upper = bridge->b_wid_int_upper;
+ OLD_b_wid_int_lower = bridge->b_wid_int_lower;
+
+#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
+ /* Verify that all interrupts from this Bridge are using a single PI */
+ if ((OLD_b_wid_int_upper != 0) && (OLD_b_wid_int_lower != 0)) {
+ /*
+ * Once set, these registers shouldn't change; they should
+ * be set multiple times with the same values.
+ *
+ * If we're attempting to change these registers, it means
+ * that our heuristics for allocating interrupts in a way
+ * appropriate for IP35 have failed, and the admin needs to
+ * explicitly direct some interrupts (or we need to make the
+ * heuristics more clever).
+ *
+ * In practice, we hope this doesn't happen very often, if
+ * at all.
+ */
+ if ((OLD_b_wid_int_upper != NEW_b_wid_int_upper) ||
+ (OLD_b_wid_int_lower != NEW_b_wid_int_lower)) {
+ PRINT_WARNING("Interrupt allocation is too complex.\n");
+ PRINT_WARNING("Use explicit administrative interrupt targetting.\n");
+ PRINT_WARNING("bridge=0x%lx targ=0x%x\n", (unsigned long)bridge, targ);
+ PRINT_WARNING("NEW=0x%x/0x%x OLD=0x%x/0x%x\n",
+ NEW_b_wid_int_upper, NEW_b_wid_int_lower,
+ OLD_b_wid_int_upper, OLD_b_wid_int_lower);
+ PRINT_PANIC("PCI Bridge interrupt targetting error\n");
+ }
+ }
+#endif /* CONFIG_SGI_IP35 */
+
+ bridge->b_wid_int_upper = NEW_b_wid_int_upper;
+ bridge->b_wid_int_lower = NEW_b_wid_int_lower;
+ bridge->b_int_host_err = vect;
+}
+
+/*
+ * pcibr_intr_preset: called during mlreset time
+ * if the platform specific code needs to route
+ * one of the Bridge's xtalk interrupts before the
+ * xtalk infrastructure is available.
+ */
+void
+pcibr_xintr_preset(void *which_widget,
+ int which_widget_intr,
+ xwidgetnum_t targ,
+ iopaddr_t addr,
+ xtalk_intr_vector_t vect)
+{
+ bridge_t *bridge = (bridge_t *) which_widget;
+
+ if (which_widget_intr == -1) {
+ /* bridge widget error interrupt */
+ bridge->b_wid_int_upper = ( (0x000F0000 & (targ << 16)) |
+ XTALK_ADDR_TO_UPPER(addr));
+ bridge->b_wid_int_lower = XTALK_ADDR_TO_LOWER(addr);
+ bridge->b_int_host_err = vect;
+
+ /* turn on all interrupts except
+ * the PCI interrupt requests,
+ * at least at heart.
+ */
+ bridge->b_int_enable |= ~BRIDGE_IMR_INT_MSK;
+
+ } else {
+ /* routing a pci device interrupt.
+ * targ and low 38 bits of addr must
+ * be the same as the already set
+ * value for the widget error interrupt.
+ */
+ bridge->b_int_addr[which_widget_intr].addr =
+ ((BRIDGE_INT_ADDR_HOST & (addr >> 30)) |
+ (BRIDGE_INT_ADDR_FLD & vect));
+ /*
+ * now bridge can let it through;
+ * NB: still should be blocked at
+ * xtalk provider end, until the service
+ * function is set.
+ */
+ bridge->b_int_enable |= 1 << vect;
+ }
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+}
+
+void
+pcibr_intr_list_func(intr_arg_t arg)
+{
+ pcibr_intr_wrap_t wrap = (pcibr_intr_wrap_t) arg;
+ reg_p statp = wrap->iw_stat;
+ bridgereg_t mask = wrap->iw_intr;
+ reg_p wrbf;
+ pcibr_intr_list_t list;
+ pcibr_intr_t intr;
+ intr_func_t func;
+ int clearit;
+ int thread_count = 0;
+
+ /*
+ * Loop until either
+ * 1) All interrupts have been removed by direct-called interrupt handlers OR
+ * 2) We've woken up at least one interrupt thread that will presumably clear
+ * Bridge interrupt bits
+ */
+
+ while ((!thread_count) && (mask & *statp)) {
+ clearit = 1;
+ for (list = wrap->iw_list;
+ list != NULL;
+ list = list->il_next) {
+ if ((intr = list->il_intr) &&
+ (intr->bi_flags & PCIIO_INTR_CONNECTED)) {
+ int is_threaded;
+
+ ASSERT(intr->bi_func);
+
+ /*
+ * This device may have initiated write
+ * requests since the bridge last saw
+ * an edge on this interrupt input; flushing
+ * the buffer here should help but may not
+ * be sufficient if we get more requests after
+ * the flush, followed by the card deciding
+ * it wants service, before the interrupt
+ * handler checks to see if things need
+ * to be done.
+ *
+ * There is a similar race condition if
+ * an interrupt handler loops around and
+ * notices further service is requred.
+ * Perhaps we need to have an explicit
+ * call that interrupt handlers need to
+ * do between noticing that DMA to memory
+ * has completed, but before observing the
+ * contents of memory?
+ */
+#ifdef IRIX
+ if (wrbf = list->il_wrbf)
+#else
+ if ((wrbf = list->il_wrbf))
+#endif
+ (void) *wrbf; /* write request buffer flush */
+
+ is_threaded = !(intr->bi_flags & PCIIO_INTR_NOTHREAD);
+
+ if (is_threaded) {
+ thread_count++;
+#ifdef IRIX
+ icvsema(&intr->bi_tinfo.thd_isync, intr->bi_tinfo.thd_pri,
+ NULL, NULL, NULL);
+#endif
+ } else {
+ /* Non-threaded. Call the interrupt handler at interrupt level */
+ func = intr->bi_func;
+ func(intr->bi_arg);
+ }
+
+ clearit = 0;
+ }
+ }
+
+ /* If there were no handlers,
+ * disable the interrupt and return.
+ * It will get enabled again after
+ * a handler is connected.
+ * If we don't do this, we would
+ * sit here and spin through the
+ * list forever.
+ */
+ if (clearit) {
+ pcibr_soft_t pcibr_soft = wrap->iw_soft;
+ bridge_t *bridge = pcibr_soft->bs_base;
+ bridgereg_t b_int_enable;
+ unsigned s;
+
+ s = pcibr_lock(pcibr_soft);
+ b_int_enable = bridge->b_int_enable;
+ b_int_enable &= ~mask;
+ bridge->b_int_enable = b_int_enable;
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+ pcibr_unlock(pcibr_soft, s);
+ return;
+ }
+ }
+}
+
+/* =====================================================================
+ * ERROR HANDLING
+ */
+
+#ifdef DEBUG
+#ifdef ERROR_DEBUG
+#define BRIDGE_PIOERR_TIMEOUT 100 /* Timeout with ERROR_DEBUG defined */
+#else
+#define BRIDGE_PIOERR_TIMEOUT 40 /* Timeout in debug mode */
+#endif
+#else
+#define BRIDGE_PIOERR_TIMEOUT 1 /* Timeout in non-debug mode */
+#endif
+
+LOCAL void
+print_bridge_errcmd(uint32_t cmdword, char *errtype)
+{
+#ifdef SUPPORT_PRINTING_R_FORMAT
+ PRINT_WARNING(
+ " Bridge %s error command word register %R",
+ errtype, cmdword, xio_cmd_bits);
+#else
+ PRINT_WARNING(
+ " Bridge %s error command word register 0x%x",
+ errtype, cmdword);
+#endif
+}
+
+LOCAL char *pcibr_isr_errs[] =
+{
+ "", "", "", "", "", "", "", "",
+ "08: GIO non-contiguous byte enable in crosstalk packet",
+ "09: PCI to Crosstalk read request timeout",
+ "10: PCI retry operation count exhausted.",
+ "11: PCI bus device select timeout",
+ "12: PCI device reported parity error",
+ "13: PCI Address/Cmd parity error ",
+ "14: PCI Bridge detected parity error",
+ "15: PCI abort condition",
+ "16: SSRAM parity error",
+ "17: LLP Transmitter Retry count wrapped",
+ "18: LLP Transmitter side required Retry",
+ "19: LLP Receiver retry count wrapped",
+ "20: LLP Receiver check bit error",
+ "21: LLP Receiver sequence number error",
+ "22: Request packet overflow",
+ "23: Request operation not supported by bridge",
+ "24: Request packet has invalid address for bridge widget",
+ "25: Incoming request xtalk command word error bit set or invalid sideband",
+ "26: Incoming response xtalk command word error bit set or invalid sideband",
+ "27: Framing error, request cmd data size does not match actual",
+ "28: Framing error, response cmd data size does not match actual",
+ "29: Unexpected response arrived",
+ "30: Access to SSRAM beyond device limits",
+ "31: Multiple errors occurred",
+};
+
+/*
+ * PCI Bridge Error interrupt handling.
+ * This routine gets invoked from system interrupt dispatcher
+ * and is responsible for invoking appropriate error handler,
+ * depending on the type of error.
+ * This IS a duplicate of bridge_errintr defined specfic to IP30.
+ * There are some minor differences in terms of the return value and
+ * parameters passed. One of these two should be removed at some point
+ * of time.
+ */
+/*ARGSUSED */
+void
+pcibr_error_dump(pcibr_soft_t pcibr_soft)
+{
+ bridge_t *bridge = pcibr_soft->bs_base;
+ bridgereg_t int_status;
+ int i;
+
+ int_status = (bridge->b_int_status & ~BRIDGE_ISR_INT_MSK);
+
+ PRINT_ALERT( "%s PCI BRIDGE ERROR: int_status is 0x%X",
+ pcibr_soft->bs_name, int_status);
+
+ for (i = PCIBR_ISR_ERR_START; i < PCIBR_ISR_MAX_ERRS; i++) {
+ if (int_status & (1 << i)) {
+ PRINT_WARNING( "%s", pcibr_isr_errs[i]);
+ }
+ }
+
+ if (int_status & BRIDGE_ISR_XTALK_ERROR) {
+ print_bridge_errcmd(bridge->b_wid_err_cmdword, "");
+
+ PRINT_WARNING(" Bridge error address 0x%lx",
+ (((uint64_t) bridge->b_wid_err_upper << 32) |
+ bridge->b_wid_err_lower));
+
+ print_bridge_errcmd(bridge->b_wid_aux_err, "Aux");
+
+ if (int_status & (BRIDGE_ISR_BAD_XRESP_PKT | BRIDGE_ISR_RESP_XTLK_ERR)) {
+ PRINT_WARNING(" Bridge response buffer: dev-num %d buff-num %d addr 0x%lx\n",
+ ((bridge->b_wid_resp_upper >> 20) & 0x3),
+ ((bridge->b_wid_resp_upper >> 16) & 0xF),
+ (((uint64_t) (bridge->b_wid_resp_upper & 0xFFFF) << 32) |
+ bridge->b_wid_resp_lower));
+ }
+ }
+ if (int_status & BRIDGE_ISR_SSRAM_PERR)
+ PRINT_WARNING(" Bridge SSRAM parity error register 0x%x",
+ bridge->b_ram_perr);
+
+ if (int_status & BRIDGE_ISR_PCIBUS_ERROR) {
+ PRINT_WARNING(" PCI/GIO error upper address register 0x%x",
+ bridge->b_pci_err_upper);
+
+ PRINT_WARNING(" PCI/GIO error lower address register 0x%x",
+ bridge->b_pci_err_lower);
+ }
+ if (int_status & BRIDGE_ISR_ERROR_FATAL) {
+ cmn_err_tag(14, (int)CE_PANIC, "PCI Bridge Error interrupt killed the system");
+ /*NOTREACHED */
+ } else {
+ PRINT_ALERT( "Non-fatal Error in Bridge..");
+ }
+}
+
+#define PCIBR_ERRINTR_GROUP(error) \
+ (( error & (BRIDGE_IRR_PCI_GRP|BRIDGE_IRR_GIO_GRP)
+
+uint32_t
+pcibr_errintr_group(uint32_t error)
+{
+ uint32_t group = BRIDGE_IRR_MULTI_CLR;
+
+ if (error & BRIDGE_IRR_PCI_GRP)
+ group |= BRIDGE_IRR_PCI_GRP_CLR;
+ if (error & BRIDGE_IRR_SSRAM_GRP)
+ group |= BRIDGE_IRR_SSRAM_GRP_CLR;
+ if (error & BRIDGE_IRR_LLP_GRP)
+ group |= BRIDGE_IRR_LLP_GRP_CLR;
+ if (error & BRIDGE_IRR_REQ_DSP_GRP)
+ group |= BRIDGE_IRR_REQ_DSP_GRP_CLR;
+ if (error & BRIDGE_IRR_RESP_BUF_GRP)
+ group |= BRIDGE_IRR_RESP_BUF_GRP_CLR;
+ if (error & BRIDGE_IRR_CRP_GRP)
+ group |= BRIDGE_IRR_CRP_GRP_CLR;
+
+ return group;
+
+}
+
+
+/* pcibr_pioerr_check():
+ * Check to see if this pcibr has a PCI PIO
+ * TIMEOUT error; if so, clear it and bump
+ * the timeout-count on any piomaps that
+ * could cover the address.
+ */
+static void
+pcibr_pioerr_check(pcibr_soft_t soft)
+{
+ bridge_t *bridge;
+ bridgereg_t b_int_status;
+ bridgereg_t b_pci_err_lower;
+ bridgereg_t b_pci_err_upper;
+ iopaddr_t pci_addr;
+ pciio_slot_t slot;
+ pcibr_piomap_t map;
+ iopaddr_t base;
+ size_t size;
+ unsigned win;
+ int func;
+
+ bridge = soft->bs_base;
+ b_int_status = bridge->b_int_status;
+ if (b_int_status & BRIDGE_ISR_PCIBUS_PIOERR) {
+ b_pci_err_lower = bridge->b_pci_err_lower;
+ b_pci_err_upper = bridge->b_pci_err_upper;
+ b_int_status = bridge->b_int_status;
+ if (b_int_status & BRIDGE_ISR_PCIBUS_PIOERR) {
+ bridge->b_int_rst_stat = (BRIDGE_IRR_PCI_GRP_CLR|
+ BRIDGE_IRR_MULTI_CLR);
+
+ pci_addr = b_pci_err_upper & BRIDGE_ERRUPPR_ADDRMASK;
+ pci_addr = (pci_addr << 32) | b_pci_err_lower;
+
+ slot = 8;
+ while (slot-- > 0) {
+ int nfunc = soft->bs_slot[slot].bss_ninfo;
+ pcibr_info_h pcibr_infoh = soft->bs_slot[slot].bss_infos;
+
+ for (func = 0; func < nfunc; func++) {
+ pcibr_info_t pcibr_info = pcibr_infoh[func];
+
+ if (!pcibr_info)
+ continue;
+
+ for (map = pcibr_info->f_piomap;
+ map != NULL; map = map->bp_next) {
+ base = map->bp_pciaddr;
+ size = map->bp_mapsz;
+ win = map->bp_space - PCIIO_SPACE_WIN(0);
+ if (win < 6)
+ base +=
+ soft->bs_slot[slot].bss_window[win].bssw_base;
+ else if (map->bp_space == PCIIO_SPACE_ROM)
+ base += pcibr_info->f_rbase;
+#ifdef IRIX
+ if ((pci_addr >= base) && (pci_addr < (base + size)))
+ atomicAddInt(map->bp_toc, 1);
+#endif
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+ * PCI Bridge Error interrupt handler.
+ * This gets invoked, whenever a PCI bridge sends an error interrupt.
+ * Primarily this servers two purposes.
+ * - If an error can be handled (typically a PIO read/write
+ * error, we try to do it silently.
+ * - If an error cannot be handled, we die violently.
+ * Interrupt due to PIO errors:
+ * - Bridge sends an interrupt, whenever a PCI operation
+ * done by the bridge as the master fails. Operations could
+ * be either a PIO read or a PIO write.
+ * PIO Read operation also triggers a bus error, and it's
+ * We primarily ignore this interrupt in that context..
+ * For PIO write errors, this is the only indication.
+ * and we have to handle with the info from here.
+ *
+ * So, there is no way to distinguish if an interrupt is
+ * due to read or write error!.
+ */
+
+
+LOCAL void
+pcibr_error_intr_handler(intr_arg_t arg)
+{
+ pcibr_soft_t pcibr_soft;
+ bridge_t *bridge;
+ bridgereg_t int_status;
+ bridgereg_t err_status;
+ int i;
+
+#if defined(SN0_HWDEBUG)
+ extern int la_trigger_nasid1;
+ extern int la_trigger_nasid2;
+ extern long la_trigger_val;
+#endif
+
+ /* REFERENCED */
+ bridgereg_t disable_errintr_mask = 0;
+#ifdef IRIX
+ int rv;
+#else
+ int rv = 0;
+#endif
+ int error_code = IOECODE_DMA | IOECODE_READ;
+ ioerror_mode_t mode = MODE_DEVERROR;
+ ioerror_t ioe;
+
+#if defined(SN0_HWDEBUG)
+ /*
+ * trigger points for logic analyzer. Used to debug the DMA timeout
+ * note that 0xcafe is added to the trigger values to avoid false
+ * triggers when la_trigger_val shows up in a cacheline as data
+ */
+ if (la_trigger_nasid1 != -1)
+ REMOTE_HUB_PI_S(la_trigger_nasid1, 0, PI_CPU_NUM, la_trigger_val + 0xcafe);
+ if (la_trigger_nasid2 != -1)
+ REMOTE_HUB_PI_S(la_trigger_nasid2, 0, PI_CPU_NUM, la_trigger_val + 0xcafe);
+#endif
+
+#if PCIBR_SOFT_LIST
+ /* IP27 seems to be handing us junk.
+ */
+ {
+ pcibr_list_p entry;
+
+ entry = pcibr_list;
+ while (1) {
+ if (entry == NULL) {
+ printk("pcibr_error_intr_handler:\n"
+ "\tparameter (0x%p) is not a pcibr_soft!",
+ arg);
+ PRINT_PANIC("Invalid parameter to pcibr_error_intr_handler");
+ }
+ if ((intr_arg_t) entry->bl_soft == arg)
+ break;
+ entry = entry->bl_next;
+ }
+ }
+#endif
+ pcibr_soft = (pcibr_soft_t) arg;
+ bridge = pcibr_soft->bs_base;
+
+ /*
+ * pcibr_error_intr_handler gets invoked whenever bridge encounters
+ * an error situation, and the interrupt for that error is enabled.
+ * This routine decides if the error is fatal or not, and takes
+ * action accordingly.
+ *
+ * In one case there is a need for special action.
+ * In case of PIO read/write timeouts due to user level, we do
+ * get an error interrupt. In this case, way to handle would
+ * be to start a timeout. If the error was due to "read", bus
+ * error handling code takes care of it. If error is due to write,
+ * it's handled at timeout
+ */
+
+ /* int_status is which bits we have to clear;
+ * err_status is the bits we haven't handled yet.
+ */
+
+ int_status = bridge->b_int_status & ~BRIDGE_ISR_INT_MSK;
+ err_status = int_status & ~BRIDGE_ISR_MULTI_ERR;
+
+ if (!(int_status & ~BRIDGE_ISR_INT_MSK)) {
+ /*
+ * No error bit set!!.
+ */
+ return;
+ }
+ /* If we have a PCIBUS_PIOERR,
+ * hand it to the logger but otherwise
+ * ignore the event.
+ */
+ if (int_status & BRIDGE_ISR_PCIBUS_PIOERR) {
+ pcibr_pioerr_check(pcibr_soft);
+ err_status &= ~BRIDGE_ISR_PCIBUS_PIOERR;
+ int_status &= ~BRIDGE_ISR_PCIBUS_PIOERR;
+ }
+
+
+ if (err_status) {
+ struct bs_errintr_stat_s *bs_estat = pcibr_soft->bs_errintr_stat;
+
+ for (i = PCIBR_ISR_ERR_START; i < PCIBR_ISR_MAX_ERRS; i++, bs_estat++) {
+ if (err_status & (1 << i)) {
+ uint32_t errrate = 0;
+ uint32_t errcount = 0;
+ uint32_t errinterval = 0, current_tick = 0;
+ int panic_on_llp_tx_retry = 0;
+ int is_llp_tx_retry_intr = 0;
+
+ bs_estat->bs_errcount_total++;
+
+#ifdef IRIX
+ current_tick = lbolt;
+#else
+ current_tick = 0;
+#endif
+ errinterval = (current_tick - bs_estat->bs_lasterr_timestamp);
+ errcount = (bs_estat->bs_errcount_total -
+ bs_estat->bs_lasterr_snapshot);
+
+ is_llp_tx_retry_intr = (BRIDGE_ISR_LLP_TX_RETRY == (1 << i));
+
+ /* On a non-zero error rate (which is equivalent to
+ * to 100 errors /sec at least) for the LLP transmitter
+ * retry interrupt we need to panic the system
+ * to prevent potential data corruption .
+ * NOTE : errcount is being compared to PCIBR_ERRTIME_THRESHOLD
+ * to make sure that we are not seing cases like x error
+ * interrupts per y ticks for very low x ,y (x > y ) which
+ * makes error rate be > 100 /sec.
+ */
+
+ /* Check for the divide by zero condition while
+ * calculating the error rates.
+ */
+
+ if (errinterval) {
+ errrate = errcount / errinterval;
+ /* If able to calculate error rate
+ * on a LLP transmitter retry interrupt check
+ * if the error rate is nonzero and we have seen
+ * a certain minimum number of errors.
+ */
+ if (is_llp_tx_retry_intr &&
+ errrate &&
+ (errcount >= PCIBR_ERRTIME_THRESHOLD)) {
+ panic_on_llp_tx_retry = 1;
+ }
+ } else {
+ errrate = 0;
+ /* Since we are not able to calculate the
+ * error rate check if we exceeded a certain
+ * minimum number of errors for LLP transmitter
+ * retries. Note that this can only happen
+ * within the first tick after the last snapshot.
+ */
+ if (is_llp_tx_retry_intr &&
+ (errcount >= PCIBR_ERRINTR_DISABLE_LEVEL)) {
+ panic_on_llp_tx_retry = 1;
+ }
+ }
+ if (panic_on_llp_tx_retry) {
+ static uint32_t last_printed_rate;
+
+ if (errrate > last_printed_rate) {
+ last_printed_rate = errrate;
+ /* Print the warning only if the error rate
+ * for the transmitter retry interrupt
+ * exceeded the previously printed rate.
+ */
+ PRINT_WARNING(
+ "%s: %s, Excessive error interrupts : %d/tick\n",
+ pcibr_soft->bs_name,
+ pcibr_isr_errs[i],
+ errrate);
+
+ }
+ /*
+ * Update snapshot, and time
+ */
+ bs_estat->bs_lasterr_timestamp = current_tick;
+ bs_estat->bs_lasterr_snapshot =
+ bs_estat->bs_errcount_total;
+
+ }
+ /*
+ * If the error rate is high enough, print the error rate.
+ */
+ if (errinterval > PCIBR_ERRTIME_THRESHOLD) {
+
+ if (errrate > PCIBR_ERRRATE_THRESHOLD) {
+ PRINT_NOTICE( "%s: %s, Error rate %d/tick",
+ pcibr_soft->bs_name,
+ pcibr_isr_errs[i],
+ errrate);
+ /*
+ * Update snapshot, and time
+ */
+ bs_estat->bs_lasterr_timestamp = current_tick;
+ bs_estat->bs_lasterr_snapshot =
+ bs_estat->bs_errcount_total;
+ }
+ }
+ if (bs_estat->bs_errcount_total > PCIBR_ERRINTR_DISABLE_LEVEL) {
+ /*
+ * We have seen a fairly large number of errors of
+ * this type. Let's disable the interrupt. But flash
+ * a message about the interrupt being disabled.
+ */
+ PRINT_NOTICE(
+ "%s Disabling error interrupt type %s. Error count %d",
+ pcibr_soft->bs_name,
+ pcibr_isr_errs[i],
+ bs_estat->bs_errcount_total);
+ disable_errintr_mask |= (1 << i);
+ }
+ }
+ }
+ }
+
+ if (disable_errintr_mask) {
+ /*
+ * Disable some high frequency errors as they
+ * could eat up too much cpu time.
+ */
+ bridge->b_int_enable &= ~disable_errintr_mask;
+ }
+ /*
+ * If we leave the PROM cacheable, T5 might
+ * try to do a cache line sized writeback to it,
+ * which will cause a BRIDGE_ISR_INVLD_ADDR.
+ */
+ if ((err_status & BRIDGE_ISR_INVLD_ADDR) &&
+ (0x00000000 == bridge->b_wid_err_upper) &&
+ (0x00C00000 == (0xFFC00000 & bridge->b_wid_err_lower)) &&
+ (0x00402000 == (0x00F07F00 & bridge->b_wid_err_cmdword))) {
+ err_status &= ~BRIDGE_ISR_INVLD_ADDR;
+ }
+#if defined (PCIBR_LLP_CONTROL_WAR)
+ /*
+ * The bridge bug, where the llp_config or control registers
+ * need to be read back after being written, affects an MP
+ * system since there could be small windows between writing
+ * the register and reading it back on one cpu while another
+ * cpu is fielding an interrupt. If we run into this scenario,
+ * workaround the problem by ignoring the error. (bug 454474)
+ * pcibr_llp_control_war_cnt keeps an approximate number of
+ * times we saw this problem on a system.
+ */
+
+ if ((err_status & BRIDGE_ISR_INVLD_ADDR) &&
+ ((((uint64_t) bridge->b_wid_err_upper << 32) | (bridge->b_wid_err_lower))
+ == (BRIDGE_INT_RST_STAT & 0xff0))) {
+#ifdef IRIX
+ if (kdebug)
+ PRINT_NOTICE( "%s bridge: ignoring llp/control address interrupt",
+ pcibr_soft->bs_name);
+#endif
+ pcibr_llp_control_war_cnt++;
+ err_status &= ~BRIDGE_ISR_INVLD_ADDR;
+ }
+#endif /* PCIBR_LLP_CONTROL_WAR */
+
+ /* Check if this is the RESP_XTALK_ERROR interrupt.
+ * This can happen due to a failed DMA READ operation.
+ */
+ if (err_status & BRIDGE_ISR_RESP_XTLK_ERR) {
+ /* Phase 1 : Look at the error state in the bridge and further
+ * down in the device layers.
+ */
+#if defined(CONFIG_SGI_IO_ERROR_HANDLING)
+ (void)error_state_set(pcibr_soft->bs_conn, ERROR_STATE_LOOKUP);
+#endif
+ IOERROR_SETVALUE(&ioe, widgetnum, pcibr_soft->bs_xid);
+ (void)pcibr_error_handler((error_handler_arg_t)pcibr_soft,
+ error_code,
+ mode,
+ &ioe);
+ /* Phase 2 : Perform the action agreed upon in phase 1.
+ */
+#if defined(CONFIG_SGI_IO_ERROR_HANDLING)
+ (void)error_state_set(pcibr_soft->bs_conn, ERROR_STATE_ACTION);
+#endif
+ rv = pcibr_error_handler((error_handler_arg_t)pcibr_soft,
+ error_code,
+ mode,
+ &ioe);
+ }
+ if (rv != IOERROR_HANDLED) {
+#ifdef DEBUG
+ if (err_status & BRIDGE_ISR_ERROR_DUMP)
+ pcibr_error_dump(pcibr_soft);
+#else
+ if (err_status & BRIDGE_ISR_ERROR_FATAL) {
+ printk("BRIDGE ERR STATUS 0x%x\n", err_status);
+ pcibr_error_dump(pcibr_soft);
+ }
+#endif
+ }
+ /*
+ * We can't return without re-enabling the interrupt, since
+ * it would cause problems for devices like IOC3 (Lost
+ * interrupts ?.). So, just cleanup the interrupt, and
+ * use saved values later..
+ */
+ bridge->b_int_rst_stat = pcibr_errintr_group(int_status);
+}
+
+/*
+ * pcibr_addr_toslot
+ * Given the 'pciaddr' find out which slot this address is
+ * allocated to, and return the slot number.
+ * While we have the info handy, construct the
+ * function number, space code and offset as well.
+ *
+ * NOTE: if this routine is called, we don't know whether
+ * the address is in CFG, MEM, or I/O space. We have to guess.
+ * This will be the case on PIO stores, where the only way
+ * we have of getting the address is to check the Bridge, which
+ * stores the PCI address but not the space and not the xtalk
+ * address (from which we could get it).
+ */
+LOCAL int
+pcibr_addr_toslot(pcibr_soft_t pcibr_soft,
+ iopaddr_t pciaddr,
+ pciio_space_t *spacep,
+ iopaddr_t *offsetp,
+ pciio_function_t *funcp)
+{
+#ifdef IRIX
+ int s, f, w;
+#else
+ int s, f=0, w;
+#endif
+ iopaddr_t base;
+ size_t size;
+ pciio_piospace_t piosp;
+
+ /*
+ * Check if the address is in config space
+ */
+
+ if ((pciaddr >= BRIDGE_CONFIG_BASE) && (pciaddr < BRIDGE_CONFIG_END)) {
+
+ if (pciaddr >= BRIDGE_CONFIG1_BASE)
+ pciaddr -= BRIDGE_CONFIG1_BASE;
+ else
+ pciaddr -= BRIDGE_CONFIG_BASE;
+
+ s = pciaddr / BRIDGE_CONFIG_SLOT_SIZE;
+ pciaddr %= BRIDGE_CONFIG_SLOT_SIZE;
+
+ if (funcp) {
+ f = pciaddr / 0x100;
+ pciaddr %= 0x100;
+ }
+ if (spacep)
+ *spacep = PCIIO_SPACE_CFG;
+ if (offsetp)
+ *offsetp = pciaddr;
+ if (funcp)
+ *funcp = f;
+
+ return s;
+ }
+ for (s = 0; s < 8; s++) {
+ int nf = pcibr_soft->bs_slot[s].bss_ninfo;
+ pcibr_info_h pcibr_infoh = pcibr_soft->bs_slot[s].bss_infos;
+
+ for (f = 0; f < nf; f++) {
+ pcibr_info_t pcibr_info = pcibr_infoh[f];
+
+ if (!pcibr_info)
+ continue;
+ for (w = 0; w < 6; w++) {
+ if (pcibr_info->f_window[w].w_space
+ == PCIIO_SPACE_NONE) {
+ continue;
+ }
+ base = pcibr_info->f_window[w].w_base;
+ size = pcibr_info->f_window[w].w_size;
+
+ if ((pciaddr >= base) && (pciaddr < (base + size))) {
+ if (spacep)
+ *spacep = PCIIO_SPACE_WIN(w);
+ if (offsetp)
+ *offsetp = pciaddr - base;
+ if (funcp)
+ *funcp = f;
+ return s;
+ } /* endif match */
+ } /* next window */
+ } /* next func */
+ } /* next slot */
+
+ /*
+ * Check if the address was allocated as part of the
+ * pcibr_piospace_alloc calls.
+ */
+ for (s = 0; s < 8; s++) {
+ int nf = pcibr_soft->bs_slot[s].bss_ninfo;
+ pcibr_info_h pcibr_infoh = pcibr_soft->bs_slot[s].bss_infos;
+
+ for (f = 0; f < nf; f++) {
+ pcibr_info_t pcibr_info = pcibr_infoh[f];
+
+ if (!pcibr_info)
+ continue;
+ piosp = pcibr_info->f_piospace;
+ while (piosp) {
+ if ((piosp->start <= pciaddr) &&
+ ((piosp->count + piosp->start) > pciaddr)) {
+ if (spacep)
+ *spacep = piosp->space;
+ if (offsetp)
+ *offsetp = pciaddr - piosp->start;
+ return s;
+ } /* endif match */
+ piosp = piosp->next;
+ } /* next piosp */
+ } /* next func */
+ } /* next slot */
+
+ /*
+ * Some other random address on the PCI bus ...
+ * we have no way of knowing whether this was
+ * a MEM or I/O access; so, for now, we just
+ * assume that the low 1G is MEM, the next
+ * 3G is I/O, and anything above the 4G limit
+ * is obviously MEM.
+ */
+
+ if (spacep)
+ *spacep = ((pciaddr < (1ul << 30)) ? PCIIO_SPACE_MEM :
+ (pciaddr < (4ul << 30)) ? PCIIO_SPACE_IO :
+ PCIIO_SPACE_MEM);
+ if (offsetp)
+ *offsetp = pciaddr;
+
+ return PCIIO_SLOT_NONE;
+
+}
+
+LOCAL void
+pcibr_error_cleanup(pcibr_soft_t pcibr_soft, int error_code)
+{
+ bridge_t *bridge = pcibr_soft->bs_base;
+
+ ASSERT(error_code & IOECODE_PIO);
+ error_code = error_code;
+
+ bridge->b_int_rst_stat =
+ (BRIDGE_IRR_PCI_GRP_CLR | BRIDGE_IRR_MULTI_CLR);
+ (void) bridge->b_wid_tflush; /* flushbus */
+}
+
+/*
+ * pcibr_error_extract
+ * Given the 'pcibr vertex handle' find out which slot
+ * the bridge status error address (from pcibr_soft info
+ * hanging off the vertex)
+ * allocated to, and return the slot number.
+ * While we have the info handy, construct the
+ * space code and offset as well.
+ *
+ * NOTE: if this routine is called, we don't know whether
+ * the address is in CFG, MEM, or I/O space. We have to guess.
+ * This will be the case on PIO stores, where the only way
+ * we have of getting the address is to check the Bridge, which
+ * stores the PCI address but not the space and not the xtalk
+ * address (from which we could get it).
+ *
+ * XXX- this interface has no way to return the function
+ * number on a multifunction card, even though that data
+ * is available.
+ */
+
+pciio_slot_t
+pcibr_error_extract(devfs_handle_t pcibr_vhdl,
+ pciio_space_t *spacep,
+ iopaddr_t *offsetp)
+{
+ pcibr_soft_t pcibr_soft = 0;
+ iopaddr_t bserr_addr;
+ bridge_t *bridge;
+ pciio_slot_t slot = PCIIO_SLOT_NONE;
+ arbitrary_info_t rev;
+
+ /* Do a sanity check as to whether we really got a
+ * bridge vertex handle.
+ */
+ if (hwgraph_info_get_LBL(pcibr_vhdl, INFO_LBL_PCIBR_ASIC_REV, &rev) !=
+ GRAPH_SUCCESS)
+ return(slot);
+
+ pcibr_soft = pcibr_soft_get(pcibr_vhdl);
+ if (pcibr_soft) {
+ bridge = pcibr_soft->bs_base;
+ bserr_addr =
+ bridge->b_pci_err_lower |
+ ((uint64_t) (bridge->b_pci_err_upper &
+ BRIDGE_ERRUPPR_ADDRMASK) << 32);
+
+ slot = pcibr_addr_toslot(pcibr_soft, bserr_addr,
+ spacep, offsetp, NULL);
+ }
+ return slot;
+}
+
+/*ARGSUSED */
+void
+pcibr_device_disable(pcibr_soft_t pcibr_soft, int devnum)
+{
+ /*
+ * XXX
+ * Device failed to handle error. Take steps to
+ * disable this device ? HOW TO DO IT ?
+ *
+ * If there are any Read response buffers associated
+ * with this device, it's time to get them back!!
+ *
+ * We can disassociate any interrupt level associated
+ * with this device, and disable that interrupt level
+ *
+ * For now it's just a place holder
+ */
+}
+
+/*
+ * pcibr_pioerror
+ * Handle PIO error that happened at the bridge pointed by pcibr_soft.
+ *
+ * Queries the Bus interface attached to see if the device driver
+ * mapping the device-number that caused error can handle the
+ * situation. If so, it will clean up any error, and return
+ * indicating the error was handled. If the device driver is unable
+ * to handle the error, it expects the bus-interface to disable that
+ * device, and takes any steps needed here to take away any resources
+ * associated with this device.
+ */
+
+#define BEM_ADD_STR(s) printk("%s", (s))
+#ifdef SUPPORT_SGI_CMN_ERR_STUFF
+#define BEM_ADD_VAR(v) printk("\t%20s: 0x%x\n", #v, (v))
+#define BEM_ADD_REG(r) printk("\t%20s: %R\n", #r, (r), r ## _desc)
+
+#define BEM_ADD_NSPC(n,s) printk("\t%20s: %R\n", n, s, space_desc)
+#else
+#define BEM_ADD_VAR(v)
+#define BEM_ADD_REG(r)
+#define BEM_ADD_NSPC(n,s)
+#endif
+#define BEM_ADD_SPC(s) BEM_ADD_NSPC(#s, s)
+
+/* BEM_ADD_IOE doesn't dump the whole ioerror, it just
+ * decodes the PCI specific portions -- we count on our
+ * callers to dump the raw IOE data.
+ */
+#ifdef colin
+#define BEM_ADD_IOE(ioe) \
+ do { \
+ if (IOERROR_FIELDVALID(ioe, busspace)) { \
+ unsigned spc; \
+ unsigned win; \
+ \
+ spc = IOERROR_GETVALUE(ioe, busspace); \
+ win = spc - PCIIO_SPACE_WIN(0); \
+ \
+ switch (spc) { \
+ case PCIIO_SPACE_CFG: \
+ printk("\tPCI Slot %d Func %d CFG space Offset 0x%x\n", \
+ pciio_widgetdev_slot_get(IOERROR_GETVALUE(ioe, widgetdev)), \
+ pciio_widgetdev_func_get(IOERROR_GETVALUE(ioe, widgetdev)), \
+ IOERROR_GETVALUE(ioe, busaddr)); \
+ break; \
+ case PCIIO_SPACE_IO: \
+ printk("\tPCI I/O space Offset 0x%x\n", \
+ IOERROR_GETVALUE(ioe, busaddr)); \
+ break; \
+ case PCIIO_SPACE_MEM: \
+ case PCIIO_SPACE_MEM32: \
+ case PCIIO_SPACE_MEM64: \
+ printk("\tPCI MEM space Offset 0x%x\n", \
+ IOERROR_GETVALUE(ioe, busaddr)); \
+ break; \
+ default: \
+ if (win < 6) { \
+ printk("\tPCI Slot %d Func %d Window %d Offset 0x%x\n",\
+ pciio_widgetdev_slot_get(IOERROR_GETVALUE(ioe, widgetdev)), \
+ pciio_widgetdev_func_get(IOERROR_GETVALUE(ioe, widgetdev)), \
+ win, \
+ IOERROR_GETVALUE(ioe, busaddr)); \
+ } \
+ break; \
+ } \
+ } \
+ } while (0)
+#else
+#define BEM_ADD_IOE(ioe)
+#endif
+
+/*ARGSUSED */
+LOCAL int
+pcibr_pioerror(
+ pcibr_soft_t pcibr_soft,
+ int error_code,
+ ioerror_mode_t mode,
+ ioerror_t *ioe)
+{
+ int retval = IOERROR_HANDLED;
+
+ devfs_handle_t pcibr_vhdl = pcibr_soft->bs_vhdl;
+ bridge_t *bridge = pcibr_soft->bs_base;
+
+ bridgereg_t bridge_int_status;
+ bridgereg_t bridge_pci_err_lower;
+ bridgereg_t bridge_pci_err_upper;
+ bridgereg_t bridge_pci_err_addr;
+
+ iopaddr_t bad_xaddr;
+
+ pciio_space_t raw_space; /* raw PCI space */
+ iopaddr_t raw_paddr; /* raw PCI address */
+
+ pciio_space_t space; /* final PCI space */
+ pciio_slot_t slot; /* final PCI slot, if appropriate */
+ pciio_function_t func; /* final PCI func, if appropriate */
+ iopaddr_t offset; /* final PCI offset */
+
+ int cs, cw, cf;
+ pciio_space_t wx;
+ iopaddr_t wb;
+ size_t ws;
+ iopaddr_t wl;
+
+
+ /*
+ * We expect to have an "xtalkaddr" coming in,
+ * and need to construct the slot/space/offset.
+ */
+
+#ifdef colin
+ bad_xaddr = IOERROR_GETVALUE(ioe, xtalkaddr);
+#else
+ bad_xaddr = -1;
+#endif
+
+ slot = PCIIO_SLOT_NONE;
+ func = PCIIO_FUNC_NONE;
+ raw_space = PCIIO_SPACE_NONE;
+ raw_paddr = 0;
+
+ if ((bad_xaddr >= BRIDGE_TYPE0_CFG_DEV0) &&
+ (bad_xaddr < BRIDGE_TYPE1_CFG)) {
+ raw_paddr = bad_xaddr - BRIDGE_TYPE0_CFG_DEV0;
+ slot = raw_paddr / BRIDGE_TYPE0_CFG_SLOT_OFF;
+ raw_paddr = raw_paddr % BRIDGE_TYPE0_CFG_SLOT_OFF;
+ raw_space = PCIIO_SPACE_CFG;
+ }
+ if ((bad_xaddr >= BRIDGE_TYPE1_CFG) &&
+ (bad_xaddr < (BRIDGE_TYPE1_CFG + 0x1000))) {
+ /* Type 1 config space:
+ * slot and function numbers not known.
+ * Perhaps we can read them back?
+ */
+ raw_paddr = bad_xaddr - BRIDGE_TYPE1_CFG;
+ raw_space = PCIIO_SPACE_CFG;
+ }
+ if ((bad_xaddr >= BRIDGE_DEVIO0) &&
+ (bad_xaddr < BRIDGE_DEVIO(BRIDGE_DEV_CNT))) {
+ int x;
+
+ raw_paddr = bad_xaddr - BRIDGE_DEVIO0;
+ x = raw_paddr / BRIDGE_DEVIO_OFF;
+ raw_paddr %= BRIDGE_DEVIO_OFF;
+ /* first two devio windows are double-sized */
+ if ((x == 1) || (x == 3))
+ raw_paddr += BRIDGE_DEVIO_OFF;
+ if (x > 0)
+ x--;
+ if (x > 1)
+ x--;
+ /* x is which devio reg; no guarantee
+ * pci slot x will be responding.
+ * still need to figure out who decodes
+ * space/offset on the bus.
+ */
+ raw_space = pcibr_soft->bs_slot[x].bss_devio.bssd_space;
+ if (raw_space == PCIIO_SPACE_NONE) {
+ /* Someone got an error because they
+ * accessed the PCI bus via a DevIO(x)
+ * window that pcibr has not yet assigned
+ * to any specific PCI address. It is
+ * quite possible that the Device(x)
+ * register has been changed since they
+ * made their access, but we will give it
+ * our best decode shot.
+ */
+ raw_space = pcibr_soft->bs_slot[x].bss_device
+ & BRIDGE_DEV_DEV_IO_MEM
+ ? PCIIO_SPACE_MEM
+ : PCIIO_SPACE_IO;
+ raw_paddr +=
+ (pcibr_soft->bs_slot[x].bss_device &
+ BRIDGE_DEV_OFF_MASK) <<
+ BRIDGE_DEV_OFF_ADDR_SHFT;
+ } else
+ raw_paddr += pcibr_soft->bs_slot[x].bss_devio.bssd_base;
+ }
+ if ((bad_xaddr >= BRIDGE_PCI_MEM32_BASE) &&
+ (bad_xaddr <= BRIDGE_PCI_MEM32_LIMIT)) {
+ raw_space = PCIIO_SPACE_MEM32;
+ raw_paddr = bad_xaddr - BRIDGE_PCI_MEM32_BASE;
+ }
+ if ((bad_xaddr >= BRIDGE_PCI_MEM64_BASE) &&
+ (bad_xaddr <= BRIDGE_PCI_MEM64_LIMIT)) {
+ raw_space = PCIIO_SPACE_MEM64;
+ raw_paddr = bad_xaddr - BRIDGE_PCI_MEM64_BASE;
+ }
+ if ((bad_xaddr >= BRIDGE_PCI_IO_BASE) &&
+ (bad_xaddr <= BRIDGE_PCI_IO_LIMIT)) {
+ raw_space = PCIIO_SPACE_IO;
+ raw_paddr = bad_xaddr - BRIDGE_PCI_IO_BASE;
+ }
+ space = raw_space;
+ offset = raw_paddr;
+
+ if ((slot == PCIIO_SLOT_NONE) && (space != PCIIO_SPACE_NONE)) {
+ /* we've got a space/offset but not which
+ * pci slot decodes it. Check through our
+ * notions of which devices decode where.
+ *
+ * Yes, this "duplicates" some logic in
+ * pcibr_addr_toslot; the difference is,
+ * this code knows which space we are in,
+ * and can really really tell what is
+ * going on (no guessing).
+ */
+
+ for (cs = 0; (cs < 8) && (slot == PCIIO_SLOT_NONE); cs++) {
+ int nf = pcibr_soft->bs_slot[cs].bss_ninfo;
+ pcibr_info_h pcibr_infoh = pcibr_soft->bs_slot[cs].bss_infos;
+
+ for (cf = 0; (cf < nf) && (slot == PCIIO_SLOT_NONE); cf++) {
+ pcibr_info_t pcibr_info = pcibr_infoh[cf];
+
+ if (!pcibr_info)
+ continue;
+ for (cw = 0; (cw < 6) && (slot == PCIIO_SLOT_NONE); ++cw) {
+ if (((wx = pcibr_info->f_window[cw].w_space) != PCIIO_SPACE_NONE) &&
+ ((wb = pcibr_info->f_window[cw].w_base) != 0) &&
+ ((ws = pcibr_info->f_window[cw].w_size) != 0) &&
+ ((wl = wb + ws) > wb) &&
+ ((wb <= offset) && (wl > offset))) {
+ /* MEM, MEM32 and MEM64 need to
+ * compare as equal ...
+ */
+ if ((wx == space) ||
+ (((wx == PCIIO_SPACE_MEM) ||
+ (wx == PCIIO_SPACE_MEM32) ||
+ (wx == PCIIO_SPACE_MEM64)) &&
+ ((space == PCIIO_SPACE_MEM) ||
+ (space == PCIIO_SPACE_MEM32) ||
+ (space == PCIIO_SPACE_MEM64)))) {
+ slot = cs;
+ func = cf;
+ space = PCIIO_SPACE_WIN(cw);
+ offset -= wb;
+ } /* endif window space match */
+ } /* endif window valid and addr match */
+ } /* next window unless slot set */
+ } /* next func unless slot set */
+ } /* next slot unless slot set */
+ /* XXX- if slot is still -1, no PCI devices are
+ * decoding here using their standard PCI BASE
+ * registers. This would be a really good place
+ * to cross-coordinate with the pciio PCI
+ * address space allocation routines, to find
+ * out if this address is "allocated" by any of
+ * our subsidiary devices.
+ */
+ }
+ /* Scan all piomap records on this PCI bus to update
+ * the TimeOut Counters on all matching maps. If we
+ * don't already know the slot number, take it from
+ * the first matching piomap. Note that we have to
+ * compare maps against raw_space and raw_paddr
+ * since space and offset could already be
+ * window-relative.
+ *
+ * There is a chance that one CPU could update
+ * through this path, and another CPU could also
+ * update due to an interrupt. Closing this hole
+ * would only result in the possibility of some
+ * errors never getting logged at all, and since the
+ * use for bp_toc is as a logical test rather than a
+ * strict count, the excess counts are not a
+ * problem.
+ */
+ for (cs = 0; cs < 8; ++cs) {
+ int nf = pcibr_soft->bs_slot[cs].bss_ninfo;
+ pcibr_info_h pcibr_infoh = pcibr_soft->bs_slot[cs].bss_infos;
+
+ for (cf = 0; cf < nf; cf++) {
+ pcibr_info_t pcibr_info = pcibr_infoh[cf];
+ pcibr_piomap_t map;
+
+ if (!pcibr_info)
+ continue;
+
+ for (map = pcibr_info->f_piomap;
+ map != NULL; map = map->bp_next) {
+ wx = map->bp_space;
+ wb = map->bp_pciaddr;
+ ws = map->bp_mapsz;
+ cw = wx - PCIIO_SPACE_WIN(0);
+ if (cw < 6) {
+ wb += pcibr_soft->bs_slot[cs].bss_window[cw].bssw_base;
+ wx = pcibr_soft->bs_slot[cs].bss_window[cw].bssw_space;
+ }
+ if (wx == PCIIO_SPACE_ROM) {
+ wb += pcibr_info->f_rbase;
+ wx = PCIIO_SPACE_MEM;
+ }
+ if ((wx == PCIIO_SPACE_MEM32) ||
+ (wx == PCIIO_SPACE_MEM64))
+ wx = PCIIO_SPACE_MEM;
+ wl = wb + ws;
+ if ((wx == raw_space) && (raw_paddr >= wb) && (raw_paddr < wl)) {
+#ifdef IRIX
+ atomicAddInt(map->bp_toc, 1);
+#endif
+ if (slot == PCIIO_SLOT_NONE) {
+ slot = cs;
+ space = map->bp_space;
+ if (cw < 6)
+ offset -= pcibr_soft->bs_slot[cs].bss_window[cw].bssw_base;
+ }
+ }
+ }
+ }
+ }
+
+ if (space != PCIIO_SPACE_NONE) {
+ if (slot != PCIIO_SLOT_NONE) {
+#ifdef IRIX
+ if (func != PCIIO_FUNC_NONE)
+ IOERROR_SETVALUE(ioe, widgetdev,
+ pciio_widgetdev_create(slot,func));
+ else
+ IOERROR_SETVALUE(ioe, widgetdev,
+ pciio_widgetdev_create(slot,0));
+#else
+ if (func != PCIIO_FUNC_NONE) {
+ IOERROR_SETVALUE(ioe, widgetdev,
+ pciio_widgetdev_create(slot,func));
+ } else {
+ IOERROR_SETVALUE(ioe, widgetdev,
+ pciio_widgetdev_create(slot,0));
+ }
+#endif
+ }
+
+ IOERROR_SETVALUE(ioe, busspace, space);
+ IOERROR_SETVALUE(ioe, busaddr, offset);
+ }
+ if (mode == MODE_DEVPROBE) {
+ /*
+ * During probing, we don't really care what the
+ * error is. Clean up the error in Bridge, notify
+ * subsidiary devices, and return success.
+ */
+ pcibr_error_cleanup(pcibr_soft, error_code);
+
+ /* if appropriate, give the error handler for this slot
+ * a shot at this probe access as well.
+ */
+ return (slot == PCIIO_SLOT_NONE) ? IOERROR_HANDLED :
+ pciio_error_handler(pcibr_vhdl, error_code, mode, ioe);
+ }
+ /*
+ * If we don't know what "PCI SPACE" the access
+ * was targeting, we may have problems at the
+ * Bridge itself. Don't touch any bridge registers,
+ * and do complain loudly.
+ */
+
+ if (space == PCIIO_SPACE_NONE) {
+ printk("XIO Bus Error at %s\n"
+ "\taccess to XIO bus offset 0x%lx\n"
+ "\tdoes not correspond to any PCI address\n",
+ pcibr_soft->bs_name, bad_xaddr);
+
+ /* caller will dump contents of ioe struct */
+ return IOERROR_XTALKLEVEL;
+ }
+ /*
+ * Read the PCI Bridge error log registers.
+ */
+ bridge_int_status = bridge->b_int_status;
+ bridge_pci_err_upper = bridge->b_pci_err_upper;
+ bridge_pci_err_lower = bridge->b_pci_err_lower;
+
+ bridge_pci_err_addr =
+ bridge_pci_err_lower
+ | (((iopaddr_t) bridge_pci_err_upper
+ & BRIDGE_ERRUPPR_ADDRMASK) << 32);
+
+ /*
+ * Actual PCI Error handling situation.
+ * Typically happens when a user level process accesses
+ * PCI space, and it causes some error.
+ *
+ * Due to PCI Bridge implementation, we get two indication
+ * for a read error: an interrupt and a Bus error.
+ * We like to handle read error in the bus error context.
+ * But the interrupt comes and goes before bus error
+ * could make much progress. (NOTE: interrupd does
+ * come in _after_ bus error processing starts. But it's
+ * completed by the time bus error code reaches PCI PIO
+ * error handling.
+ * Similarly write error results in just an interrupt,
+ * and error handling has to be done at interrupt level.
+ * There is no way to distinguish at interrupt time, if an
+ * error interrupt is due to read/write error..
+ */
+
+ /* We know the xtalk addr, the raw pci bus space,
+ * the raw pci bus address, the decoded pci bus
+ * space, the offset within that space, and the
+ * decoded pci slot (which may be "PCIIO_SLOT_NONE" if no slot
+ * is known to be involved).
+ */
+
+ /*
+ * Hand the error off to the handler registered
+ * for the slot that should have decoded the error,
+ * or to generic PCI handling (if pciio decides that
+ * such is appropriate).
+ */
+ retval = pciio_error_handler(pcibr_vhdl, error_code, mode, ioe);
+
+ if (retval != IOERROR_HANDLED) {
+
+ /* Generate a generic message for IOERROR_UNHANDLED
+ * since the subsidiary handlers were silent, and
+ * did no recovery.
+ */
+ if (retval == IOERROR_UNHANDLED) {
+ retval = IOERROR_PANIC;
+
+ /* we may or may not want to print some of this,
+ * depending on debug level and which error code.
+ */
+
+ PRINT_ALERT(
+ "PIO Error on PCI Bus %s",
+ pcibr_soft->bs_name);
+ /* this decodes part of the ioe; our caller
+ * will dump the raw details in DEBUG and
+ * kdebug kernels.
+ */
+ BEM_ADD_IOE(ioe);
+ }
+#if defined(FORCE_ERRORS)
+ if (0) {
+#elif !DEBUG
+ if (kdebug) {
+#endif
+ /*
+ * dump raw data from bridge
+ */
+
+ BEM_ADD_STR("DEBUG DATA -- raw info from Bridge ASIC:\n");
+ BEM_ADD_REG(bridge_int_status);
+ BEM_ADD_VAR(bridge_pci_err_upper);
+ BEM_ADD_VAR(bridge_pci_err_lower);
+ BEM_ADD_VAR(bridge_pci_err_addr);
+ BEM_ADD_SPC(raw_space);
+ BEM_ADD_VAR(raw_paddr);
+ if (IOERROR_FIELDVALID(ioe, widgetdev)) {
+
+#ifdef colin
+ slot = pciio_widgetdev_slot_get(IOERROR_GETVALUE(ioe,
+ widgetdev));
+ func = pciio_widgetdev_func_get(IOERROR_GETVALUE(ioe,
+ widgetdev));
+#else
+ slot = -1;
+ func = -1;
+#endif
+ if (slot < 8) {
+#ifdef SUPPORT_SGI_CMN_ERR_STUFF
+ bridgereg_t device = bridge->b_device[slot].reg;
+#endif
+
+ BEM_ADD_VAR(slot);
+ BEM_ADD_VAR(func);
+ BEM_ADD_REG(device);
+ }
+ }
+#if !DEBUG || defined(FORCE_ERRORS)
+ }
+#endif
+
+ /*
+ * Since error could not be handled at lower level,
+ * error data logged has not been cleared.
+ * Clean up errors, and
+ * re-enable bridge to interrupt on error conditions.
+ * NOTE: Wheather we get the interrupt on PCI_ABORT or not is
+ * dependent on INT_ENABLE register. This write just makes sure
+ * that if the interrupt was enabled, we do get the interrupt.
+ *
+ * CAUTION: Resetting bit BRIDGE_IRR_PCI_GRP_CLR, acknowledges
+ * a group of interrupts. If while handling this error,
+ * some other error has occured, that would be
+ * implicitly cleared by this write.
+ * Need a way to ensure we don't inadvertently clear some
+ * other errors.
+ */
+#ifdef IRIX
+ if (IOERROR_FIELDVALID(ioe, widgetdev))
+ pcibr_device_disable(pcibr_soft,
+ pciio_widgetdev_slot_get(
+ IOERROR_GETVALUE(ioe, widgetdev)));
+#endif
+
+ if (mode == MODE_DEVUSERERROR)
+ pcibr_error_cleanup(pcibr_soft, error_code);
+ }
+ return retval;
+}
+
+/*
+ * bridge_dmaerror
+ * Some error was identified in a DMA transaction.
+ * This routine will identify the <device, address> that caused the error,
+ * and try to invoke the appropriate bus service to handle this.
+ */
+
+#define BRIDGE_DMA_READ_ERROR (BRIDGE_ISR_RESP_XTLK_ERR|BRIDGE_ISR_XREAD_REQ_TIMEOUT)
+
+int
+pcibr_dmard_error(
+ pcibr_soft_t pcibr_soft,
+ int error_code,
+ ioerror_mode_t mode,
+ ioerror_t *ioe)
+{
+ devfs_handle_t pcibr_vhdl = pcibr_soft->bs_vhdl;
+ bridge_t *bridge = pcibr_soft->bs_base;
+ bridgereg_t bus_lowaddr, bus_uppraddr;
+ int retval = 0;
+ int bufnum;
+
+ /*
+ * In case of DMA errors, bridge should have logged the
+ * address that caused the error.
+ * Look up the address, in the bridge error registers, and
+ * take appropriate action
+ */
+#ifdef colin
+ ASSERT(IOERROR_GETVALUE(ioe, widgetnum) == pcibr_soft->bs_xid);
+ ASSERT(bridge);
+#endif
+
+ /*
+ * read error log registers
+ */
+ bus_lowaddr = bridge->b_wid_resp_lower;
+ bus_uppraddr = bridge->b_wid_resp_upper;
+
+ bufnum = BRIDGE_RESP_ERRUPPR_BUFNUM(bus_uppraddr);
+ IOERROR_SETVALUE(ioe, widgetdev,
+ pciio_widgetdev_create(
+ BRIDGE_RESP_ERRUPPR_DEVICE(bus_uppraddr),
+ 0));
+ IOERROR_SETVALUE(ioe, busaddr,
+ (bus_lowaddr |
+ ((iopaddr_t)
+ (bus_uppraddr &
+ BRIDGE_ERRUPPR_ADDRMASK) << 32)));
+
+ /*
+ * need to ensure that the xtalk adress in ioe
+ * maps to PCI error address read from bridge.
+ * How to convert PCI address back to Xtalk address ?
+ * (better idea: convert XTalk address to PCI address
+ * and then do the compare!)
+ */
+
+ retval = pciio_error_handler(pcibr_vhdl, error_code, mode, ioe);
+ if (retval != IOERROR_HANDLED)
+#ifdef colin
+ pcibr_device_disable(pcibr_soft,
+ pciio_widgetdev_slot_get(
+ IOERROR_GETVALUE(ioe,widgetdev)));
+#else
+ pcibr_device_disable(pcibr_soft,
+ pciio_widgetdev_slot_get(-1));
+#endif
+
+ /*
+ * Re-enable bridge to interrupt on BRIDGE_IRR_RESP_BUF_GRP_CLR
+ * NOTE: Wheather we get the interrupt on BRIDGE_IRR_RESP_BUF_GRP_CLR or
+ * not is dependent on INT_ENABLE register. This write just makes sure
+ * that if the interrupt was enabled, we do get the interrupt.
+ */
+ bridge->b_int_rst_stat = BRIDGE_IRR_RESP_BUF_GRP_CLR;
+
+ /*
+ * Also, release the "bufnum" back to buffer pool that could be re-used.
+ * This is done by "disabling" the buffer for a moment, then restoring
+ * the original assignment.
+ */
+
+ {
+ reg_p regp;
+ bridgereg_t regv;
+ bridgereg_t mask;
+
+ regp = (bufnum & 1)
+ ? &bridge->b_odd_resp
+ : &bridge->b_even_resp;
+
+ mask = 0xF << ((bufnum >> 1) * 4);
+
+ regv = *regp;
+ *regp = regv & ~mask;
+ *regp = regv;
+ }
+
+ return retval;
+}
+
+/*
+ * pcibr_dmawr_error:
+ * Handle a dma write error caused by a device attached to this bridge.
+ *
+ * ioe has the widgetnum, widgetdev, and memaddr fields updated
+ * But we don't know the PCI address that corresponds to "memaddr"
+ * nor do we know which device driver is generating this address.
+ *
+ * There is no easy way to find out the PCI address(es) that map
+ * to a specific system memory address. Bus handling code is also
+ * of not much help, since they don't keep track of the DMA mapping
+ * that have been handed out.
+ * So it's a dead-end at this time.
+ *
+ * If translation is available, we could invoke the error handling
+ * interface of the device driver.
+ */
+/*ARGSUSED */
+int
+pcibr_dmawr_error(
+ pcibr_soft_t pcibr_soft,
+ int error_code,
+ ioerror_mode_t mode,
+ ioerror_t *ioe)
+{
+ devfs_handle_t pcibr_vhdl = pcibr_soft->bs_vhdl;
+ int retval;
+
+ retval = pciio_error_handler(pcibr_vhdl, error_code, mode, ioe);
+
+#ifdef IRIX
+ if (retval != IOERROR_HANDLED) {
+ pcibr_device_disable(pcibr_soft,
+ pciio_widgetdev_slot_get(
+ IOERROR_GETVALUE(ioe, widgetdev)));
+
+ }
+#endif
+ return retval;
+}
+
+/*
+ * Bridge error handler.
+ * Interface to handle all errors that involve bridge in some way.
+ *
+ * This normally gets called from xtalk error handler.
+ * ioe has different set of fields set depending on the error that
+ * was encountered. So, we have a bit field indicating which of the
+ * fields are valid.
+ *
+ * NOTE: This routine could be operating in interrupt context. So,
+ * don't try to sleep here (till interrupt threads work!!)
+ */
+LOCAL int
+pcibr_error_handler(
+ error_handler_arg_t einfo,
+ int error_code,
+ ioerror_mode_t mode,
+ ioerror_t *ioe)
+{
+ pcibr_soft_t pcibr_soft;
+ int retval = IOERROR_BADERRORCODE;
+ devfs_handle_t xconn_vhdl,pcibr_vhdl;
+#if defined(CONFIG_SGI_IO_ERROR_HANDLING)
+ error_state_t e_state;
+#endif
+ pcibr_soft = (pcibr_soft_t) einfo;
+
+ xconn_vhdl = pcibr_soft->bs_conn;
+ pcibr_vhdl = pcibr_soft->bs_vhdl;
+
+#if defined(CONFIG_SGI_IO_ERROR_HANDLING)
+ e_state = error_state_get(xconn_vhdl);
+
+ if (error_state_set(pcibr_vhdl, e_state) ==
+ ERROR_RETURN_CODE_CANNOT_SET_STATE)
+ return(IOERROR_UNHANDLED);
+#endif
+
+ /* If we are in the action handling phase clean out the error state
+ * on the xswitch.
+ */
+#if defined(CONFIG_SGI_IO_ERROR_HANDLING)
+ if (e_state == ERROR_STATE_ACTION)
+ (void)error_state_set(xconn_vhdl, ERROR_STATE_NONE);
+#endif
+
+#if DEBUG && ERROR_DEBUG
+ printk("%s: pcibr_error_handler\n", pcibr_soft->bs_name);
+#endif
+
+ ASSERT(pcibr_soft != NULL);
+
+ if (error_code & IOECODE_PIO)
+ retval = pcibr_pioerror(pcibr_soft, error_code, mode, ioe);
+
+ if (error_code & IOECODE_DMA) {
+ if (error_code & IOECODE_READ) {
+ /*
+ * DMA read error occurs when a device attached to the bridge
+ * tries to read some data from system memory, and this
+ * either results in a timeout or access error.
+ * First case is indicated by the bit "XREAD_REQ_TOUT"
+ * and second case by "RESP_XTALK_ERROR" bit in bridge error
+ * interrupt status register.
+ *
+ * pcibr_error_intr_handler would get invoked first, and it has
+ * the responsibility of calling pcibr_error_handler with
+ * suitable parameters.
+ */
+
+ retval = pcibr_dmard_error(pcibr_soft, error_code, MODE_DEVERROR, ioe);
+ }
+ if (error_code & IOECODE_WRITE) {
+ /*
+ * A device attached to this bridge has been generating
+ * bad DMA writes. Find out the device attached, and
+ * slap on it's wrist.
+ */
+
+ retval = pcibr_dmawr_error(pcibr_soft, error_code, MODE_DEVERROR, ioe);
+ }
+ }
+ return retval;
+
+}
+
+/*
+ * Reenable a device after handling the error.
+ * This is called by the lower layers when they wish to be reenabled
+ * after an error.
+ * Note that each layer would be calling the previous layer to reenable
+ * first, before going ahead with their own re-enabling.
+ */
+
+int
+pcibr_error_devenable(devfs_handle_t pconn_vhdl, int error_code)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+
+ ASSERT(error_code & IOECODE_PIO);
+
+ /* If the error is not known to be a write,
+ * we have to call devenable.
+ * write errors are isolated to the bridge.
+ */
+ if (!(error_code & IOECODE_WRITE)) {
+ devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn;
+ int rc;
+
+ rc = xtalk_error_devenable(xconn_vhdl, pciio_slot, error_code);
+ if (rc != IOERROR_HANDLED)
+ return rc;
+ }
+ pcibr_error_cleanup(pcibr_soft, error_code);
+ return IOERROR_HANDLED;
+}
+
+/* =====================================================================
+ * CONFIGURATION MANAGEMENT
+ */
+/*ARGSUSED */
+void
+pcibr_provider_startup(devfs_handle_t pcibr)
+{
+}
+
+/*ARGSUSED */
+void
+pcibr_provider_shutdown(devfs_handle_t pcibr)
+{
+}
+
+int
+pcibr_reset(devfs_handle_t conn)
+{
+ pciio_info_t pciio_info = pciio_info_get(conn);
+ pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ bridge_t *bridge = pcibr_soft->bs_base;
+ bridgereg_t ctlreg;
+ unsigned cfgctl[8];
+ unsigned s;
+ int f, nf;
+ pcibr_info_h pcibr_infoh;
+ pcibr_info_t pcibr_info;
+ int win;
+
+ if (pcibr_soft->bs_slot[pciio_slot].has_host) {
+ pciio_slot = pcibr_soft->bs_slot[pciio_slot].host_slot;
+ pcibr_info = pcibr_soft->bs_slot[pciio_slot].bss_infos[0];
+ }
+ if (pciio_slot < 4) {
+ s = pcibr_lock(pcibr_soft);
+ nf = pcibr_soft->bs_slot[pciio_slot].bss_ninfo;
+ pcibr_infoh = pcibr_soft->bs_slot[pciio_slot].bss_infos;
+ for (f = 0; f < nf; ++f)
+ if (pcibr_infoh[f])
+ cfgctl[f] = bridge->b_type0_cfg_dev[pciio_slot].f[f].l[PCI_CFG_COMMAND / 4];
+
+ ctlreg = bridge->b_wid_control;
+ bridge->b_wid_control = ctlreg | BRIDGE_CTRL_RST(pciio_slot);
+ /* XXX delay? */
+ bridge->b_wid_control = ctlreg;
+ /* XXX delay? */
+
+ for (f = 0; f < nf; ++f)
+#ifdef IRIX
+ if (pcibr_info = pcibr_infoh[f])
+#else
+ if ((pcibr_info = pcibr_infoh[f]))
+#endif
+ for (win = 0; win < 6; ++win)
+ if (pcibr_info->f_window[win].w_base != 0)
+ bridge->b_type0_cfg_dev[pciio_slot].f[f].l[PCI_CFG_BASE_ADDR(win) / 4] =
+ pcibr_info->f_window[win].w_base;
+ for (f = 0; f < nf; ++f)
+ if (pcibr_infoh[f])
+ bridge->b_type0_cfg_dev[pciio_slot].f[f].l[PCI_CFG_COMMAND / 4] = cfgctl[f];
+ pcibr_unlock(pcibr_soft, s);
+
+ return 0;
+ }
+#ifdef SUPPORT_PRINTING_V_FORMAT
+ PRINT_WARNING( "%v: pcibr_reset unimplemented for slot %d\n",
+ conn, pciio_slot);
+#endif
+ return -1;
+}
+
+pciio_endian_t
+pcibr_endian_set(devfs_handle_t pconn_vhdl,
+ pciio_endian_t device_end,
+ pciio_endian_t desired_end)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ bridgereg_t devreg;
+ unsigned s;
+
+ /*
+ * Bridge supports hardware swapping; so we can always
+ * arrange for the caller's desired endianness.
+ */
+
+ s = pcibr_lock(pcibr_soft);
+ devreg = pcibr_soft->bs_slot[pciio_slot].bss_device;
+ if (device_end != desired_end)
+ devreg |= BRIDGE_DEV_SWAP_BITS;
+ else
+ devreg &= ~BRIDGE_DEV_SWAP_BITS;
+
+ /* NOTE- if we ever put SWAP bits
+ * onto the disabled list, we will
+ * have to change the logic here.
+ */
+ if (pcibr_soft->bs_slot[pciio_slot].bss_device != devreg) {
+ bridge_t *bridge = pcibr_soft->bs_base;
+
+ bridge->b_device[pciio_slot].reg = devreg;
+ pcibr_soft->bs_slot[pciio_slot].bss_device = devreg;
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+ }
+ pcibr_unlock(pcibr_soft, s);
+
+#if DEBUG && PCIBR_DEV_DEBUG
+ printk("pcibr Device(%d): 0x%p\n", pciio_slot, bridge->b_device[pciio_slot].reg);
+#endif
+
+ return desired_end;
+}
+
+/* This (re)sets the GBR and REALTIME bits and also keeps track of how
+ * many sets are outstanding. Reset succeeds only if the number of outstanding
+ * sets == 1.
+ */
+int
+pcibr_priority_bits_set(pcibr_soft_t pcibr_soft,
+ pciio_slot_t pciio_slot,
+ pciio_priority_t device_prio)
+{
+ int s;
+ int *counter;
+ bridgereg_t rtbits = 0;
+ bridgereg_t devreg;
+ int rc = PRIO_SUCCESS;
+
+ /* in dual-slot configurations, the host and the
+ * guest have separate DMA resources, so they
+ * have separate requirements for priority bits.
+ */
+
+ counter = &(pcibr_soft->bs_slot[pciio_slot].bss_pri_uctr);
+
+ /*
+ * Bridge supports PCI notions of LOW and HIGH priority
+ * arbitration rings via a "REAL_TIME" bit in the per-device
+ * Bridge register. The "GBR" bit controls access to the GBR
+ * ring on the xbow. These two bits are (re)set together.
+ *
+ * XXX- Bug in Rev B Bridge Si:
+ * Symptom: Prefetcher starts operating incorrectly. This happens
+ * due to corruption of the address storage ram in the prefetcher
+ * when a non-real time pci request is pulled and a real-time one is
+ * put in it's place. Workaround: Use only a single arbitration ring
+ * on pci bus. GBR and RR can still be uniquely used per
+ * device. NETLIST MERGE DONE, WILL BE FIXED IN REV C.
+ */
+
+ if (pcibr_soft->bs_rev_num != BRIDGE_PART_REV_B)
+ rtbits |= BRIDGE_DEV_RT;
+
+ /* NOTE- if we ever put DEV_RT or DEV_GBR on
+ * the disabled list, we will have to take
+ * it into account here.
+ */
+
+ s = pcibr_lock(pcibr_soft);
+ devreg = pcibr_soft->bs_slot[pciio_slot].bss_device;
+ if (device_prio == PCI_PRIO_HIGH) {
+#ifdef IRIX
+ if (++*counter == 1)
+#else
+ if ((++*counter == 1)) {
+#endif
+ if (rtbits)
+ devreg |= rtbits;
+ else
+ rc = PRIO_FAIL;
+#ifndef IRIX
+ }
+#endif
+ } else if (device_prio == PCI_PRIO_LOW) {
+ if (*counter <= 0)
+ rc = PRIO_FAIL;
+ else if (--*counter == 0)
+ if (rtbits)
+ devreg &= ~rtbits;
+ }
+ if (pcibr_soft->bs_slot[pciio_slot].bss_device != devreg) {
+ bridge_t *bridge = pcibr_soft->bs_base;
+
+ bridge->b_device[pciio_slot].reg = devreg;
+ pcibr_soft->bs_slot[pciio_slot].bss_device = devreg;
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+ }
+ pcibr_unlock(pcibr_soft, s);
+
+ return rc;
+}
+
+pciio_priority_t
+pcibr_priority_set(devfs_handle_t pconn_vhdl,
+ pciio_priority_t device_prio)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+
+ (void) pcibr_priority_bits_set(pcibr_soft, pciio_slot, device_prio);
+
+ return device_prio;
+}
+
+/*
+ * Interfaces to allow special (e.g. SGI) drivers to set/clear
+ * Bridge-specific device flags. Many flags are modified through
+ * PCI-generic interfaces; we don't allow them to be directly
+ * manipulated here. Only flags that at this point seem pretty
+ * Bridge-specific can be set through these special interfaces.
+ * We may add more flags as the need arises, or remove flags and
+ * create PCI-generic interfaces as the need arises.
+ *
+ * Returns 0 on failure, 1 on success
+ */
+int
+pcibr_device_flags_set(devfs_handle_t pconn_vhdl,
+ pcibr_device_flags_t flags)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+ bridgereg_t set = 0;
+ bridgereg_t clr = 0;
+
+ ASSERT((flags & PCIBR_DEVICE_FLAGS) == flags);
+
+ if (flags & PCIBR_WRITE_GATHER)
+ set |= BRIDGE_DEV_PMU_WRGA_EN;
+ if (flags & PCIBR_NOWRITE_GATHER)
+ clr |= BRIDGE_DEV_PMU_WRGA_EN;
+
+ if (flags & PCIBR_WRITE_GATHER)
+ set |= BRIDGE_DEV_DIR_WRGA_EN;
+ if (flags & PCIBR_NOWRITE_GATHER)
+ clr |= BRIDGE_DEV_DIR_WRGA_EN;
+
+ if (flags & PCIBR_PREFETCH)
+ set |= BRIDGE_DEV_PREF;
+ if (flags & PCIBR_NOPREFETCH)
+ clr |= BRIDGE_DEV_PREF;
+
+ if (flags & PCIBR_PRECISE)
+ set |= BRIDGE_DEV_PRECISE;
+ if (flags & PCIBR_NOPRECISE)
+ clr |= BRIDGE_DEV_PRECISE;
+
+ if (flags & PCIBR_BARRIER)
+ set |= BRIDGE_DEV_BARRIER;
+ if (flags & PCIBR_NOBARRIER)
+ clr |= BRIDGE_DEV_BARRIER;
+
+ if (flags & PCIBR_64BIT)
+ set |= BRIDGE_DEV_DEV_SIZE;
+ if (flags & PCIBR_NO64BIT)
+ clr |= BRIDGE_DEV_DEV_SIZE;
+
+ if (set || clr) {
+ bridgereg_t devreg;
+ unsigned s;
+
+ s = pcibr_lock(pcibr_soft);
+ devreg = pcibr_soft->bs_slot[pciio_slot].bss_device;
+#ifdef IRIX
+ devreg = devreg & ~clr | set;
+#else
+ devreg = (devreg & ~clr) | set;
+#endif
+ if (pcibr_soft->bs_slot[pciio_slot].bss_device != devreg) {
+ bridge_t *bridge = pcibr_soft->bs_base;
+
+ bridge->b_device[pciio_slot].reg = devreg;
+ pcibr_soft->bs_slot[pciio_slot].bss_device = devreg;
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+ }
+ pcibr_unlock(pcibr_soft, s);
+#if DEBUG && PCIBR_DEV_DEBUG
+ printk("pcibr Device(%d): %R\n", pciio_slot, bridge->b_device[pciio_slot].regbridge->b_device[pciio_slot].reg, device_bits);
+#endif
+ }
+ return (1);
+}
+
+#ifdef LITTLE_ENDIAN
+/*
+ * on sn-ia we need to twiddle the the addresses going out
+ * the pci bus because we use the unswizzled synergy space
+ * (the alternative is to use the swizzled synergy space
+ * and byte swap the data)
+ */
+#define CB(b,r) (((volatile uint8_t *) b)[((r)^4)])
+#define CS(b,r) (((volatile uint16_t *) b)[((r^4)/2)])
+#define CW(b,r) (((volatile uint32_t *) b)[((r^4)/4)])
+#else
+#define CB(b,r) (((volatile uint8_t *) cfgbase)[(r)^3])
+#define CS(b,r) (((volatile uint16_t *) cfgbase)[((r)/2)^1])
+#define CW(b,r) (((volatile uint32_t *) cfgbase)[(r)/4])
+#endif /* LITTLE_ENDIAN */
+
+
+LOCAL cfg_p
+pcibr_config_addr(devfs_handle_t conn,
+ unsigned reg)
+{
+ pcibr_info_t pcibr_info;
+ pciio_slot_t pciio_slot;
+ pciio_function_t pciio_func;
+ pcibr_soft_t pcibr_soft;
+ bridge_t *bridge;
+ cfg_p cfgbase = (cfg_p)0;
+
+ pcibr_info = pcibr_info_get(conn);
+
+ pciio_slot = pcibr_info->f_slot;
+ if (pciio_slot == PCIIO_SLOT_NONE)
+ pciio_slot = PCI_TYPE1_SLOT(reg);
+
+ pciio_func = pcibr_info->f_func;
+ if (pciio_func == PCIIO_FUNC_NONE)
+ pciio_func = PCI_TYPE1_FUNC(reg);
+
+ pcibr_soft = (pcibr_soft_t) pcibr_info->f_mfast;
+
+ if ( (pcibr_soft_t)0 != pcibr_soft ) {
+ bridge = pcibr_soft->bs_base;
+ if ( (bridge_t *)0 != bridge ) {
+ cfgbase = bridge->b_type0_cfg_dev[pciio_slot].f[pciio_func].l;
+ }
+ }
+
+
+ return cfgbase;
+}
+
+uint64_t
+pcibr_config_get(devfs_handle_t conn,
+ unsigned reg,
+ unsigned size)
+{
+ return do_pcibr_config_get(pcibr_config_addr(conn, reg),
+ PCI_TYPE1_REG(reg), size);
+}
+
+LOCAL uint64_t
+do_pcibr_config_get(
+ cfg_p cfgbase,
+ unsigned reg,
+ unsigned size)
+{
+ unsigned value;
+
+
+ value = CW(cfgbase, reg);
+
+ if (reg & 3)
+ value >>= 8 * (reg & 3);
+ if (size < 4)
+ value &= (1 << (8 * size)) - 1;
+
+ return value;
+}
+
+void
+pcibr_config_set(devfs_handle_t conn,
+ unsigned reg,
+ unsigned size,
+ uint64_t value)
+{
+ do_pcibr_config_set(pcibr_config_addr(conn, reg),
+ PCI_TYPE1_REG(reg), size, value);
+}
+
+LOCAL void
+do_pcibr_config_set(cfg_p cfgbase,
+ unsigned reg,
+ unsigned size,
+ uint64_t value)
+{
+ switch (size) {
+ case 1:
+ CB(cfgbase, reg) = value;
+ break;
+ case 2:
+ if (reg & 1) {
+ CB(cfgbase, reg) = value;
+ CB(cfgbase, reg + 1) = value >> 8;
+ } else
+ CS(cfgbase, reg) = value;
+ break;
+ case 3:
+ if (reg & 1) {
+ CB(cfgbase, reg) = value;
+ CS(cfgbase, reg + 1) = value >> 8;
+ } else {
+ CS(cfgbase, reg) = value;
+ CB(cfgbase, reg + 2) = value >> 16;
+ }
+ break;
+
+ case 4:
+ CW(cfgbase, reg) = value;
+ break;
+ }
+}
+
+pciio_provider_t pcibr_provider =
+{
+ (pciio_piomap_alloc_f *) pcibr_piomap_alloc,
+ (pciio_piomap_free_f *) pcibr_piomap_free,
+ (pciio_piomap_addr_f *) pcibr_piomap_addr,
+ (pciio_piomap_done_f *) pcibr_piomap_done,
+ (pciio_piotrans_addr_f *) pcibr_piotrans_addr,
+ (pciio_piospace_alloc_f *) pcibr_piospace_alloc,
+ (pciio_piospace_free_f *) pcibr_piospace_free,
+
+ (pciio_dmamap_alloc_f *) pcibr_dmamap_alloc,
+ (pciio_dmamap_free_f *) pcibr_dmamap_free,
+ (pciio_dmamap_addr_f *) pcibr_dmamap_addr,
+ (pciio_dmamap_list_f *) pcibr_dmamap_list,
+ (pciio_dmamap_done_f *) pcibr_dmamap_done,
+ (pciio_dmatrans_addr_f *) pcibr_dmatrans_addr,
+ (pciio_dmatrans_list_f *) pcibr_dmatrans_list,
+ (pciio_dmamap_drain_f *) pcibr_dmamap_drain,
+ (pciio_dmaaddr_drain_f *) pcibr_dmaaddr_drain,
+ (pciio_dmalist_drain_f *) pcibr_dmalist_drain,
+
+ (pciio_intr_alloc_f *) pcibr_intr_alloc,
+ (pciio_intr_free_f *) pcibr_intr_free,
+ (pciio_intr_connect_f *) pcibr_intr_connect,
+ (pciio_intr_disconnect_f *) pcibr_intr_disconnect,
+ (pciio_intr_cpu_get_f *) pcibr_intr_cpu_get,
+
+ (pciio_provider_startup_f *) pcibr_provider_startup,
+ (pciio_provider_shutdown_f *) pcibr_provider_shutdown,
+ (pciio_reset_f *) pcibr_reset,
+ (pciio_write_gather_flush_f *) pcibr_write_gather_flush,
+ (pciio_endian_set_f *) pcibr_endian_set,
+ (pciio_priority_set_f *) pcibr_priority_set,
+ (pciio_config_get_f *) pcibr_config_get,
+ (pciio_config_set_f *) pcibr_config_set,
+
+ (pciio_error_devenable_f *) pcibr_error_devenable,
+ (pciio_error_extract_f *) pcibr_error_extract,
+};
+
+LOCAL pcibr_hints_t
+pcibr_hints_get(devfs_handle_t xconn_vhdl, int alloc)
+{
+ arbitrary_info_t ainfo = 0;
+ graph_error_t rv;
+ pcibr_hints_t hint;
+
+ rv = hwgraph_info_get_LBL(xconn_vhdl, INFO_LBL_PCIBR_HINTS, &ainfo);
+
+ if (alloc && (rv != GRAPH_SUCCESS)) {
+
+ NEW(hint);
+ hint->rrb_alloc_funct = NULL;
+ hint->ph_intr_bits = NULL;
+ rv = hwgraph_info_add_LBL(xconn_vhdl,
+ INFO_LBL_PCIBR_HINTS,
+ (arbitrary_info_t) hint);
+ if (rv != GRAPH_SUCCESS)
+ goto abnormal_exit;
+
+ rv = hwgraph_info_get_LBL(xconn_vhdl, INFO_LBL_PCIBR_HINTS, &ainfo);
+
+ if (rv != GRAPH_SUCCESS)
+ goto abnormal_exit;
+
+ if (ainfo != (arbitrary_info_t) hint)
+ goto abnormal_exit;
+ }
+ return (pcibr_hints_t) ainfo;
+
+abnormal_exit:
+#ifdef IRIX
+ printf("SHOULD NOT BE HERE\n");
+#endif
+ DEL(hint);
+ return(NULL);
+
+}
+
+void
+pcibr_hints_fix_some_rrbs(devfs_handle_t xconn_vhdl, unsigned mask)
+{
+ pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
+
+ if (hint)
+ hint->ph_rrb_fixed = mask;
+#if DEBUG
+ else
+ printk("pcibr_hints_fix_rrbs: pcibr_hints_get failed at\n"
+ "\t%p\n", xconn_vhdl);
+#endif
+}
+
+void
+pcibr_hints_fix_rrbs(devfs_handle_t xconn_vhdl)
+{
+ pcibr_hints_fix_some_rrbs(xconn_vhdl, 0xFF);
+}
+
+void
+pcibr_hints_dualslot(devfs_handle_t xconn_vhdl,
+ pciio_slot_t host,
+ pciio_slot_t guest)
+{
+ pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
+
+ if (hint)
+ hint->ph_host_slot[guest] = host + 1;
+#if DEBUG
+ else
+ printk("pcibr_hints_dualslot: pcibr_hints_get failed at\n"
+ "\t%p\n", xconn_vhdl);
+#endif
+}
+
+void
+pcibr_hints_intr_bits(devfs_handle_t xconn_vhdl,
+ pcibr_intr_bits_f *xxx_intr_bits)
+{
+ pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
+
+ if (hint)
+ hint->ph_intr_bits = xxx_intr_bits;
+#if DEBUG
+ else
+ printk("pcibr_hints_intr_bits: pcibr_hints_get failed at\n"
+ "\t%p\n", xconn_vhdl);
+#endif
+}
+
+void
+pcibr_set_rrb_callback(devfs_handle_t xconn_vhdl, rrb_alloc_funct_t rrb_alloc_funct)
+{
+ pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
+
+ if (hint)
+ hint->rrb_alloc_funct = rrb_alloc_funct;
+}
+
+void
+pcibr_hints_handsoff(devfs_handle_t xconn_vhdl)
+{
+ pcibr_hints_t hint = pcibr_hints_get(xconn_vhdl, 1);
+
+ if (hint)
+ hint->ph_hands_off = 1;
+#if DEBUG
+ else
+ printk("pcibr_hints_handsoff: pcibr_hints_get failed at\n"
+ "\t%p\n", xconn_vhdl);
+#endif
+}
+
+void
+pcibr_hints_subdevs(devfs_handle_t xconn_vhdl,
+ pciio_slot_t slot,
+ uint64_t subdevs)
+{
+ arbitrary_info_t ainfo = 0;
+ char sdname[16];
+ devfs_handle_t pconn_vhdl = GRAPH_VERTEX_NONE;
+
+ sprintf(sdname, "pci/%d", slot);
+ (void) hwgraph_path_add(xconn_vhdl, sdname, &pconn_vhdl);
+ if (pconn_vhdl == GRAPH_VERTEX_NONE) {
+#if DEBUG
+ printk("pcibr_hints_subdevs: hwgraph_path_create failed at\n"
+ "\t%p (seeking %s)\n", xconn_vhdl, sdname);
+#endif
+ return;
+ }
+ hwgraph_info_get_LBL(pconn_vhdl, INFO_LBL_SUBDEVS, &ainfo);
+ if (ainfo == 0) {
+ uint64_t *subdevp;
+
+ NEW(subdevp);
+ if (!subdevp) {
+#if DEBUG
+ printk("pcibr_hints_subdevs: subdev ptr alloc failed at\n"
+ "\t%p\n", pconn_vhdl);
+#endif
+ return;
+ }
+ *subdevp = subdevs;
+ hwgraph_info_add_LBL(pconn_vhdl, INFO_LBL_SUBDEVS, (arbitrary_info_t) subdevp);
+ hwgraph_info_get_LBL(pconn_vhdl, INFO_LBL_SUBDEVS, &ainfo);
+ if (ainfo == (arbitrary_info_t) subdevp)
+ return;
+ DEL(subdevp);
+#ifdef IRIX
+ if (ainfo == NULL)
+#else
+ if (ainfo == (arbitrary_info_t) NULL)
+#endif
+ {
+#if DEBUG
+ printk("pcibr_hints_subdevs: null subdevs ptr at\n"
+ "\t%p\n", pconn_vhdl);
+#endif
+ return;
+ }
+#if DEBUG
+ printk("pcibr_subdevs_get: dup subdev add_LBL at\n"
+ "\t%p\n", pconn_vhdl);
+#endif
+ }
+ *(uint64_t *) ainfo = subdevs;
+}
+
+
+#ifdef colin
+
+#include <sys/idbg.h>
+#include <sys/idbgentry.h>
+
+char *pci_space[] = {"NONE",
+ "ROM",
+ "IO",
+ "",
+ "MEM",
+ "MEM32",
+ "MEM64",
+ "CFG",
+ "WIN0",
+ "WIN1",
+ "WIN2",
+ "WIN3",
+ "WIN4",
+ "WIN5",
+ "",
+ "BAD"};
+
+void
+idbg_pss_func(pcibr_info_h pcibr_infoh, int func)
+{
+ pcibr_info_t pcibr_info = pcibr_infoh[func];
+ char name[MAXDEVNAME];
+ int win;
+
+ if (!pcibr_info)
+ return;
+ qprintf("Per-slot Function Info\n");
+#ifdef SUPPORT_PRINTING_V_FORMAT
+ sprintf(name, "%v", pcibr_info->f_vertex);
+#endif
+ qprintf("\tSlot Name : %s\n",name);
+ qprintf("\tPCI Bus : %d ",pcibr_info->f_bus);
+ qprintf("Slot : %d ", pcibr_info->f_slot);
+ qprintf("Function : %d ", pcibr_info->f_func);
+ qprintf("VendorId : 0x%x " , pcibr_info->f_vendor);
+ qprintf("DeviceId : 0x%x\n", pcibr_info->f_device);
+#ifdef SUPPORT_PRINTING_V_FORMAT
+ sprintf(name, "%v", pcibr_info->f_master);
+#endif
+ qprintf("\tBus provider : %s\n",name);
+ qprintf("\tProvider Fns : 0x%x ", pcibr_info->f_pops);
+ qprintf("Error Handler : 0x%x Arg 0x%x\n",
+ pcibr_info->f_efunc,pcibr_info->f_einfo);
+ for(win = 0 ; win < 6 ; win++)
+ qprintf("\tBase Reg #%d space %s base 0x%x size 0x%x\n",
+ win,pci_space[pcibr_info->f_window[win].w_space],
+ pcibr_info->f_window[win].w_base,
+ pcibr_info->f_window[win].w_size);
+
+ qprintf("\tRom base 0x%x size 0x%x\n",
+ pcibr_info->f_rbase,pcibr_info->f_rsize);
+
+ qprintf("\tInterrupt Bit Map\n");
+ qprintf("\t\tPCI Int#\tBridge Pin#\n");
+ for (win = 0 ; win < 4; win++)
+ qprintf("\t\tINT%c\t\t%d\n",win+'A',pcibr_info->f_ibit[win]);
+ qprintf("\n");
+}
+
+
+void
+idbg_pss_info(pcibr_soft_t pcibr_soft, pciio_slot_t slot)
+{
+ pcibr_soft_slot_t pss;
+ char slot_conn_name[MAXDEVNAME];
+ int func;
+
+ pss = &pcibr_soft->bs_slot[slot];
+ qprintf("PCI INFRASTRUCTURAL INFO FOR SLOT %d\n", slot);
+ qprintf("\tHost Present ? %s ", pss->has_host ? "yes" : "no");
+ qprintf("\tHost Slot : %d\n",pss->host_slot);
+ sprintf(slot_conn_name, "%v", pss->slot_conn);
+ qprintf("\tSlot Conn : %s\n",slot_conn_name);
+ qprintf("\t#Functions : %d\n",pss->bss_ninfo);
+ for (func = 0; func < pss->bss_ninfo; func++)
+ idbg_pss_func(pss->bss_infos,func);
+ qprintf("\tSpace : %s ",pci_space[pss->bss_devio.bssd_space]);
+ qprintf("\tBase : 0x%x ", pss->bss_devio.bssd_base);
+ qprintf("\tShadow Devreg : 0x%x\n", pss->bss_device);
+ qprintf("\tUsage counts : pmu %d d32 %d d64 %d\n",
+ pss->bss_pmu_uctr,pss->bss_d32_uctr,pss->bss_d64_uctr);
+
+ qprintf("\tDirect Trans Info : d64_base 0x%x d64_flags 0x%x"
+ "d32_base 0x%x d32_flags 0x%x\n",
+ pss->bss_d64_base, pss->bss_d64_flags,
+ pss->bss_d32_base, pss->bss_d32_flags);
+
+ qprintf("\tExt ATEs active ? %s",
+ pss->bss_ext_ates_active ? "yes" : "no");
+ qprintf(" Command register : 0x%x ", pss->bss_cmd_pointer);
+ qprintf(" Shadow command val : 0x%x\n", pss->bss_cmd_shadow);
+
+ qprintf("\tRRB Info : Valid %d+%d Reserved %d\n",
+ pcibr_soft->bs_rrb_valid[slot],
+ pcibr_soft->bs_rrb_valid[slot + PCIBR_RRB_SLOT_VIRTUAL],
+ pcibr_soft->bs_rrb_res[slot]);
+
+}
+
+int ips = 0;
+
+void
+idbg_pss(pcibr_soft_t pcibr_soft)
+{
+ pciio_slot_t slot;
+
+
+ if (ips >= 0 && ips < 8)
+ idbg_pss_info(pcibr_soft,ips);
+ else if (ips < 0)
+ for (slot = 0; slot < 8; slot++)
+ idbg_pss_info(pcibr_soft,slot);
+ else
+ qprintf("Invalid ips %d\n",ips);
+}
+
+#endif /* colin */
+
+int
+pcibr_dma_enabled(devfs_handle_t pconn_vhdl)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+ pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info);
+
+
+ return xtalk_dma_enabled(pcibr_soft->bs_conn);
+}
diff --git a/arch/ia64/sn/io/pciio.c b/arch/ia64/sn/io/pciio.c
new file mode 100644
index 000000000..618fcf51c
--- /dev/null
+++ b/arch/ia64/sn/io/pciio.c
@@ -0,0 +1,1562 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#define USRPCI 0
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/hcl_util.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/pci/bridge.h>
+#include <asm/sn/ioerror_handling.h>
+#include <asm/sn/pci/pciio.h>
+#include <asm/sn/pci/pciio_private.h>
+
+#define DEBUG_PCIIO
+#undef DEBUG_PCIIO /* turn this on for yet more console output */
+
+
+#define NEW(ptr) (ptr = kmalloc(sizeof (*(ptr)), GFP_KERNEL))
+#define DEL(ptr) (kfree(ptr))
+
+char pciio_info_fingerprint[] = "pciio_info";
+
+cdl_p pciio_registry = NULL;
+
+int
+badaddr_val(volatile void *addr, int len, volatile void *ptr)
+{
+ switch (len) {
+ case 4: *(volatile u32*)ptr = *(((volatile u32*)(((u64) addr)^4)));
+ default: printk("FIXME: argh fix badaddr_val\n");
+ }
+ /* no such thing as a bad addr .... */
+ return(0);
+}
+
+
+void
+cmn_err_tag(int seqnumber, register int level, char *fmt, ...)
+{
+}
+
+nasid_t
+get_console_nasid(void)
+{
+#ifdef IRIX
+ return console_nasid;
+#else
+ return 0;
+#endif
+}
+
+int
+hub_dma_enabled(devfs_handle_t xconn_vhdl)
+{
+ return(0);
+}
+
+int
+hub_error_devenable(devfs_handle_t xconn_vhdl, int devnum, int error_code)
+{
+ return(0);
+}
+
+void
+ioerror_dump(char *name, int error_code, int error_mode, ioerror_t *ioerror)
+{
+}
+
+/******
+ ****** end hack defines ......
+ ******/
+
+
+
+
+/* =====================================================================
+ * PCI Generic Bus Provider
+ * Implement PCI provider operations. The pciio* layer provides a
+ * platform-independent interface for PCI devices. This layer
+ * switches among the possible implementations of a PCI adapter.
+ */
+
+/* =====================================================================
+ * Provider Function Location SHORTCUT
+ *
+ * On platforms with only one possible PCI provider, macros can be
+ * set up at the top that cause the table lookups and indirections to
+ * completely disappear.
+ */
+
+#if CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 || CONFIG_IA64_GENERIC
+/*
+ * For the moment, we will assume that IP27
+ * only use Bridge ASICs to provide PCI support.
+ */
+#include <asm/sn/pci/pcibr.h>
+#define DEV_FUNC(dev,func) pcibr_##func
+#define CAST_PIOMAP(x) ((pcibr_piomap_t)(x))
+#define CAST_DMAMAP(x) ((pcibr_dmamap_t)(x))
+#define CAST_INTR(x) ((pcibr_intr_t)(x))
+#endif /* CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 */
+
+/* =====================================================================
+ * Function Table of Contents
+ */
+
+#if !defined(DEV_FUNC)
+static pciio_provider_t *pciio_to_provider_fns(devfs_handle_t dev);
+#endif
+
+pciio_piomap_t pciio_piomap_alloc(devfs_handle_t, device_desc_t, pciio_space_t, iopaddr_t, size_t, size_t, unsigned);
+void pciio_piomap_free(pciio_piomap_t);
+caddr_t pciio_piomap_addr(pciio_piomap_t, iopaddr_t, size_t);
+
+void pciio_piomap_done(pciio_piomap_t);
+caddr_t pciio_piotrans_addr(devfs_handle_t, device_desc_t, pciio_space_t, iopaddr_t, size_t, unsigned);
+caddr_t pciio_pio_addr(devfs_handle_t, device_desc_t, pciio_space_t, iopaddr_t, size_t, pciio_piomap_t *, unsigned);
+
+iopaddr_t pciio_piospace_alloc(devfs_handle_t, device_desc_t, pciio_space_t, size_t, size_t);
+void pciio_piospace_free(devfs_handle_t, pciio_space_t, iopaddr_t, size_t);
+
+pciio_dmamap_t pciio_dmamap_alloc(devfs_handle_t, device_desc_t, size_t, unsigned);
+void pciio_dmamap_free(pciio_dmamap_t);
+iopaddr_t pciio_dmamap_addr(pciio_dmamap_t, paddr_t, size_t);
+alenlist_t pciio_dmamap_list(pciio_dmamap_t, alenlist_t, unsigned);
+void pciio_dmamap_done(pciio_dmamap_t);
+iopaddr_t pciio_dmatrans_addr(devfs_handle_t, device_desc_t, paddr_t, size_t, unsigned);
+alenlist_t pciio_dmatrans_list(devfs_handle_t, device_desc_t, alenlist_t, unsigned);
+void pciio_dmamap_drain(pciio_dmamap_t);
+void pciio_dmaaddr_drain(devfs_handle_t, paddr_t, size_t);
+void pciio_dmalist_drain(devfs_handle_t, alenlist_t);
+iopaddr_t pciio_dma_addr(devfs_handle_t, device_desc_t, paddr_t, size_t, pciio_dmamap_t *, unsigned);
+
+pciio_intr_t pciio_intr_alloc(devfs_handle_t, device_desc_t, pciio_intr_line_t, devfs_handle_t);
+void pciio_intr_free(pciio_intr_t);
+int pciio_intr_connect(pciio_intr_t, intr_func_t, intr_arg_t, void *thread);
+void pciio_intr_disconnect(pciio_intr_t);
+devfs_handle_t pciio_intr_cpu_get(pciio_intr_t);
+
+void pciio_slot_func_to_name(char *, pciio_slot_t, pciio_function_t);
+static pciio_info_t pciio_cardinfo_get(devfs_handle_t, pciio_slot_t);
+int pciio_error_handler(devfs_handle_t, int, ioerror_mode_t, ioerror_t *);
+int pciio_error_devenable(devfs_handle_t, int);
+
+void pciio_provider_startup(devfs_handle_t);
+void pciio_provider_shutdown(devfs_handle_t);
+
+pciio_endian_t pciio_endian_set(devfs_handle_t, pciio_endian_t, pciio_endian_t);
+pciio_priority_t pciio_priority_set(devfs_handle_t, pciio_priority_t);
+devfs_handle_t pciio_intr_dev_get(pciio_intr_t);
+
+devfs_handle_t pciio_pio_dev_get(pciio_piomap_t);
+pciio_slot_t pciio_pio_slot_get(pciio_piomap_t);
+pciio_space_t pciio_pio_space_get(pciio_piomap_t);
+iopaddr_t pciio_pio_pciaddr_get(pciio_piomap_t);
+ulong pciio_pio_mapsz_get(pciio_piomap_t);
+caddr_t pciio_pio_kvaddr_get(pciio_piomap_t);
+
+devfs_handle_t pciio_dma_dev_get(pciio_dmamap_t);
+pciio_slot_t pciio_dma_slot_get(pciio_dmamap_t);
+
+pciio_info_t pciio_info_chk(devfs_handle_t);
+pciio_info_t pciio_info_get(devfs_handle_t);
+void pciio_info_set(devfs_handle_t, pciio_info_t);
+devfs_handle_t pciio_info_dev_get(pciio_info_t);
+pciio_slot_t pciio_info_slot_get(pciio_info_t);
+pciio_function_t pciio_info_function_get(pciio_info_t);
+pciio_vendor_id_t pciio_info_vendor_id_get(pciio_info_t);
+pciio_device_id_t pciio_info_device_id_get(pciio_info_t);
+devfs_handle_t pciio_info_master_get(pciio_info_t);
+arbitrary_info_t pciio_info_mfast_get(pciio_info_t);
+pciio_provider_t *pciio_info_pops_get(pciio_info_t);
+error_handler_f *pciio_info_efunc_get(pciio_info_t);
+error_handler_arg_t *pciio_info_einfo_get(pciio_info_t);
+pciio_space_t pciio_info_bar_space_get(pciio_info_t, int);
+iopaddr_t pciio_info_bar_base_get(pciio_info_t, int);
+size_t pciio_info_bar_size_get(pciio_info_t, int);
+iopaddr_t pciio_info_rom_base_get(pciio_info_t);
+size_t pciio_info_rom_size_get(pciio_info_t);
+
+void pciio_init(void);
+int pciio_attach(devfs_handle_t);
+
+void pciio_provider_register(devfs_handle_t, pciio_provider_t *pciio_fns);
+void pciio_provider_unregister(devfs_handle_t);
+pciio_provider_t *pciio_provider_fns_get(devfs_handle_t);
+
+int pciio_driver_register(pciio_vendor_id_t, pciio_device_id_t, char *driver_prefix, unsigned);
+void pciio_driver_unregister(char *driver_prefix);
+
+devfs_handle_t pciio_device_register(devfs_handle_t, devfs_handle_t, pciio_slot_t, pciio_function_t, pciio_vendor_id_t, pciio_device_id_t);
+
+void pciio_device_unregister(devfs_handle_t);
+pciio_info_t pciio_device_info_new(pciio_info_t, devfs_handle_t, pciio_slot_t, pciio_function_t, pciio_vendor_id_t, pciio_device_id_t);
+void pciio_device_info_free(pciio_info_t);
+devfs_handle_t pciio_device_info_register(devfs_handle_t, pciio_info_t);
+void pciio_device_info_unregister(devfs_handle_t, pciio_info_t);
+int pciio_device_attach(devfs_handle_t);
+int pciio_device_detach(devfs_handle_t);
+void pciio_error_register(devfs_handle_t, error_handler_f *, error_handler_arg_t);
+
+int pciio_reset(devfs_handle_t);
+int pciio_write_gather_flush(devfs_handle_t);
+int pciio_slot_inuse(devfs_handle_t);
+
+/* =====================================================================
+ * Provider Function Location
+ *
+ * If there is more than one possible provider for
+ * this platform, we need to examine the master
+ * vertex of the current vertex for a provider
+ * function structure, and indirect through the
+ * appropriately named member.
+ */
+
+#if !defined(DEV_FUNC)
+
+static pciio_provider_t *
+pciio_to_provider_fns(devfs_handle_t dev)
+{
+ pciio_info_t card_info;
+ pciio_provider_t *provider_fns;
+
+ card_info = pciio_info_get(dev);
+ ASSERT(card_info != NULL);
+
+ provider_fns = pciio_info_pops_get(card_info);
+ ASSERT(provider_fns != NULL);
+
+ return (provider_fns);
+}
+
+#define DEV_FUNC(dev,func) pciio_to_provider_fns(dev)->func
+#define CAST_PIOMAP(x) ((pciio_piomap_t)(x))
+#define CAST_DMAMAP(x) ((pciio_dmamap_t)(x))
+#define CAST_INTR(x) ((pciio_intr_t)(x))
+#endif
+
+/*
+ * Many functions are not passed their vertex
+ * information directly; rather, they must
+ * dive through a resource map. These macros
+ * are available to coordinate this detail.
+ */
+#define PIOMAP_FUNC(map,func) DEV_FUNC((map)->pp_dev,func)
+#define DMAMAP_FUNC(map,func) DEV_FUNC((map)->pd_dev,func)
+#define INTR_FUNC(intr_hdl,func) DEV_FUNC((intr_hdl)->pi_dev,func)
+
+/* =====================================================================
+ * PIO MANAGEMENT
+ *
+ * For mapping system virtual address space to
+ * pciio space on a specified card
+ */
+
+pciio_piomap_t
+pciio_piomap_alloc(devfs_handle_t dev, /* set up mapping for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ pciio_space_t space, /* CFG, MEM, IO, or a device-decoded window */
+ iopaddr_t addr, /* lowest address (or offset in window) */
+ size_t byte_count, /* size of region containing our mappings */
+ size_t byte_count_max, /* maximum size of a mapping */
+ unsigned flags)
+{ /* defined in sys/pio.h */
+ return (pciio_piomap_t) DEV_FUNC(dev, piomap_alloc)
+ (dev, dev_desc, space, addr, byte_count, byte_count_max, flags);
+}
+
+void
+pciio_piomap_free(pciio_piomap_t pciio_piomap)
+{
+ PIOMAP_FUNC(pciio_piomap, piomap_free)
+ (CAST_PIOMAP(pciio_piomap));
+}
+
+caddr_t
+pciio_piomap_addr(pciio_piomap_t pciio_piomap, /* mapping resources */
+ iopaddr_t pciio_addr, /* map for this pciio address */
+ size_t byte_count)
+{ /* map this many bytes */
+ pciio_piomap->pp_kvaddr = PIOMAP_FUNC(pciio_piomap, piomap_addr)
+ (CAST_PIOMAP(pciio_piomap), pciio_addr, byte_count);
+
+ return pciio_piomap->pp_kvaddr;
+}
+
+void
+pciio_piomap_done(pciio_piomap_t pciio_piomap)
+{
+ PIOMAP_FUNC(pciio_piomap, piomap_done)
+ (CAST_PIOMAP(pciio_piomap));
+}
+
+caddr_t
+pciio_piotrans_addr(devfs_handle_t dev, /* translate for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ pciio_space_t space, /* CFG, MEM, IO, or a device-decoded window */
+ iopaddr_t addr, /* starting address (or offset in window) */
+ size_t byte_count, /* map this many bytes */
+ unsigned flags)
+{ /* (currently unused) */
+ return DEV_FUNC(dev, piotrans_addr)
+ (dev, dev_desc, space, addr, byte_count, flags);
+}
+
+caddr_t
+pciio_pio_addr(devfs_handle_t dev, /* translate for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ pciio_space_t space, /* CFG, MEM, IO, or a device-decoded window */
+ iopaddr_t addr, /* starting address (or offset in window) */
+ size_t byte_count, /* map this many bytes */
+ pciio_piomap_t *mapp, /* where to return the map pointer */
+ unsigned flags)
+{ /* PIO flags */
+ pciio_piomap_t map = 0;
+ int errfree = 0;
+ caddr_t res;
+
+ if (mapp) {
+ map = *mapp; /* possible pre-allocated map */
+ *mapp = 0; /* record "no map used" */
+ }
+
+ res = pciio_piotrans_addr
+ (dev, dev_desc, space, addr, byte_count, flags);
+ if (res)
+ return res; /* pciio_piotrans worked */
+
+ if (!map) {
+ map = pciio_piomap_alloc
+ (dev, dev_desc, space, addr, byte_count, byte_count, flags);
+ if (!map)
+ return res; /* pciio_piomap_alloc failed */
+ errfree = 1;
+ }
+
+ res = pciio_piomap_addr
+ (map, addr, byte_count);
+ if (!res) {
+ if (errfree)
+ pciio_piomap_free(map);
+ return res; /* pciio_piomap_addr failed */
+ }
+ if (mapp)
+ *mapp = map; /* pass back map used */
+
+ return res; /* pciio_piomap_addr succeeded */
+}
+
+iopaddr_t
+pciio_piospace_alloc(devfs_handle_t dev, /* Device requiring space */
+ device_desc_t dev_desc, /* Device descriptor */
+ pciio_space_t space, /* MEM32/MEM64/IO */
+ size_t byte_count, /* Size of mapping */
+ size_t align)
+{ /* Alignment needed */
+ if (align < NBPP)
+ align = NBPP;
+ return DEV_FUNC(dev, piospace_alloc)
+ (dev, dev_desc, space, byte_count, align);
+}
+
+void
+pciio_piospace_free(devfs_handle_t dev, /* Device freeing space */
+ pciio_space_t space, /* Type of space */
+ iopaddr_t pciaddr, /* starting address */
+ size_t byte_count)
+{ /* Range of address */
+ DEV_FUNC(dev, piospace_free)
+ (dev, space, pciaddr, byte_count);
+}
+
+/* =====================================================================
+ * DMA MANAGEMENT
+ *
+ * For mapping from pci space to system
+ * physical space.
+ */
+
+pciio_dmamap_t
+pciio_dmamap_alloc(devfs_handle_t dev, /* set up mappings for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ size_t byte_count_max, /* max size of a mapping */
+ unsigned flags)
+{ /* defined in dma.h */
+ return (pciio_dmamap_t) DEV_FUNC(dev, dmamap_alloc)
+ (dev, dev_desc, byte_count_max, flags);
+}
+
+void
+pciio_dmamap_free(pciio_dmamap_t pciio_dmamap)
+{
+ DMAMAP_FUNC(pciio_dmamap, dmamap_free)
+ (CAST_DMAMAP(pciio_dmamap));
+}
+
+iopaddr_t
+pciio_dmamap_addr(pciio_dmamap_t pciio_dmamap, /* use these mapping resources */
+ paddr_t paddr, /* map for this address */
+ size_t byte_count)
+{ /* map this many bytes */
+ return DMAMAP_FUNC(pciio_dmamap, dmamap_addr)
+ (CAST_DMAMAP(pciio_dmamap), paddr, byte_count);
+}
+
+alenlist_t
+pciio_dmamap_list(pciio_dmamap_t pciio_dmamap, /* use these mapping resources */
+ alenlist_t alenlist, /* map this Address/Length List */
+ unsigned flags)
+{
+ return DMAMAP_FUNC(pciio_dmamap, dmamap_list)
+ (CAST_DMAMAP(pciio_dmamap), alenlist, flags);
+}
+
+void
+pciio_dmamap_done(pciio_dmamap_t pciio_dmamap)
+{
+ DMAMAP_FUNC(pciio_dmamap, dmamap_done)
+ (CAST_DMAMAP(pciio_dmamap));
+}
+
+iopaddr_t
+pciio_dmatrans_addr(devfs_handle_t dev, /* translate for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ paddr_t paddr, /* system physical address */
+ size_t byte_count, /* length */
+ unsigned flags)
+{ /* defined in dma.h */
+ return DEV_FUNC(dev, dmatrans_addr)
+ (dev, dev_desc, paddr, byte_count, flags);
+}
+
+alenlist_t
+pciio_dmatrans_list(devfs_handle_t dev, /* translate for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ alenlist_t palenlist, /* system address/length list */
+ unsigned flags)
+{ /* defined in dma.h */
+ return DEV_FUNC(dev, dmatrans_list)
+ (dev, dev_desc, palenlist, flags);
+}
+
+iopaddr_t
+pciio_dma_addr(devfs_handle_t dev, /* translate for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ paddr_t paddr, /* system physical address */
+ size_t byte_count, /* length */
+ pciio_dmamap_t *mapp, /* map to use, then map we used */
+ unsigned flags)
+{ /* PIO flags */
+ pciio_dmamap_t map = 0;
+ int errfree = 0;
+ iopaddr_t res;
+
+ if (mapp) {
+ map = *mapp; /* possible pre-allocated map */
+ *mapp = 0; /* record "no map used" */
+ }
+
+ res = pciio_dmatrans_addr
+ (dev, dev_desc, paddr, byte_count, flags);
+ if (res)
+ return res; /* pciio_dmatrans worked */
+
+ if (!map) {
+ map = pciio_dmamap_alloc
+ (dev, dev_desc, byte_count, flags);
+ if (!map)
+ return res; /* pciio_dmamap_alloc failed */
+ errfree = 1;
+ }
+
+ res = pciio_dmamap_addr
+ (map, paddr, byte_count);
+ if (!res) {
+ if (errfree)
+ pciio_dmamap_free(map);
+ return res; /* pciio_dmamap_addr failed */
+ }
+ if (mapp)
+ *mapp = map; /* pass back map used */
+
+ return res; /* pciio_dmamap_addr succeeded */
+}
+
+void
+pciio_dmamap_drain(pciio_dmamap_t map)
+{
+ DMAMAP_FUNC(map, dmamap_drain)
+ (CAST_DMAMAP(map));
+}
+
+void
+pciio_dmaaddr_drain(devfs_handle_t dev, paddr_t addr, size_t size)
+{
+ DEV_FUNC(dev, dmaaddr_drain)
+ (dev, addr, size);
+}
+
+void
+pciio_dmalist_drain(devfs_handle_t dev, alenlist_t list)
+{
+ DEV_FUNC(dev, dmalist_drain)
+ (dev, list);
+}
+
+/* =====================================================================
+ * INTERRUPT MANAGEMENT
+ *
+ * Allow crosstalk devices to establish interrupts
+ */
+
+/*
+ * Allocate resources required for an interrupt as specified in intr_desc.
+ * Return resource handle in intr_hdl.
+ */
+pciio_intr_t
+pciio_intr_alloc(devfs_handle_t dev, /* which Crosstalk device */
+ device_desc_t dev_desc, /* device descriptor */
+ pciio_intr_line_t lines, /* INTR line(s) to attach */
+ devfs_handle_t owner_dev)
+{ /* owner of this interrupt */
+ return (pciio_intr_t) DEV_FUNC(dev, intr_alloc)
+ (dev, dev_desc, lines, owner_dev);
+}
+
+/*
+ * Free resources consumed by intr_alloc.
+ */
+void
+pciio_intr_free(pciio_intr_t intr_hdl)
+{
+ INTR_FUNC(intr_hdl, intr_free)
+ (CAST_INTR(intr_hdl));
+}
+
+/*
+ * Associate resources allocated with a previous pciio_intr_alloc call with the
+ * described handler, arg, name, etc.
+ *
+ * Returns 0 on success, returns <0 on failure.
+ */
+int
+pciio_intr_connect(pciio_intr_t intr_hdl, /* pciio intr resource handle */
+ intr_func_t intr_func, /* pciio intr handler */
+ intr_arg_t intr_arg, /* arg to intr handler */
+ void *thread)
+{ /* intr thread to use */
+ return INTR_FUNC(intr_hdl, intr_connect)
+ (CAST_INTR(intr_hdl), intr_func, intr_arg, thread);
+}
+
+/*
+ * Disassociate handler with the specified interrupt.
+ */
+void
+pciio_intr_disconnect(pciio_intr_t intr_hdl)
+{
+ INTR_FUNC(intr_hdl, intr_disconnect)
+ (CAST_INTR(intr_hdl));
+}
+
+/*
+ * Return a hwgraph vertex that represents the CPU currently
+ * targeted by an interrupt.
+ */
+devfs_handle_t
+pciio_intr_cpu_get(pciio_intr_t intr_hdl)
+{
+ return INTR_FUNC(intr_hdl, intr_cpu_get)
+ (CAST_INTR(intr_hdl));
+}
+
+/* =====================================================================
+ * ERROR MANAGEMENT
+ */
+
+void
+pciio_slot_func_to_name(char *name,
+ pciio_slot_t slot,
+ pciio_function_t func)
+{
+ /*
+ * standard connection points:
+ *
+ * PCIIO_SLOT_NONE: .../pci/direct
+ * PCIIO_FUNC_NONE: .../pci/<SLOT> ie. .../pci/3
+ * multifunction: .../pci/<SLOT><FUNC> ie. .../pci/3c
+ */
+
+ if (slot == PCIIO_SLOT_NONE)
+ sprintf(name, "direct");
+ else if (func == PCIIO_FUNC_NONE)
+ sprintf(name, "%d", slot);
+ else
+ sprintf(name, "%d%c", slot, 'a'+func);
+}
+
+/*
+ * pciio_cardinfo_get
+ *
+ * Get the pciio info structure corresponding to the
+ * specified PCI "slot" (we like it when the same index
+ * number is used for the PCI IDSEL, the REQ/GNT pair,
+ * and the interrupt line being used for INTA. We like
+ * it so much we call it the slot number).
+ */
+static pciio_info_t
+pciio_cardinfo_get(
+ devfs_handle_t pciio_vhdl,
+ pciio_slot_t pci_slot)
+{
+ char namebuf[16];
+ pciio_info_t info = 0;
+ devfs_handle_t conn;
+
+ pciio_slot_func_to_name(namebuf, pci_slot, PCIIO_FUNC_NONE);
+ if (GRAPH_SUCCESS ==
+ hwgraph_traverse(pciio_vhdl, namebuf, &conn)) {
+ info = pciio_info_chk(conn);
+ hwgraph_vertex_unref(conn);
+ }
+
+ return info;
+}
+
+/*
+ * pciio_error_handler:
+ * dispatch an error to the appropriate
+ * pciio connection point, or process
+ * it as a generic pci error.
+ * Yes, the first parameter is the
+ * provider vertex at the middle of
+ * the bus; we get to the pciio connect
+ * point using the ioerror widgetdev field.
+ *
+ * This function is called by the
+ * specific PCI provider, after it has figured
+ * out where on the PCI bus (including which slot,
+ * if it can tell) the error came from.
+ */
+/*ARGSUSED */
+int
+pciio_error_handler(
+ devfs_handle_t pciio_vhdl,
+ int error_code,
+ ioerror_mode_t mode,
+ ioerror_t *ioerror)
+{
+ pciio_info_t pciio_info;
+ devfs_handle_t pconn_vhdl;
+#if USRPCI
+ devfs_handle_t usrpci_v;
+#endif
+ pciio_slot_t slot;
+
+ int retval;
+#if defined(CONFIG_SGI_IO_ERROR_HANDLING)
+ error_state_t e_state;
+#endif
+
+#ifdef IRIX
+#if DEBUG && ERROR_DEBUG
+ cmn_err(CE_CONT, "%v: pciio_error_handler\n", pciio_vhdl);
+#endif
+#endif
+
+ IOERR_PRINTF(cmn_err(CE_NOTE,
+ "%v: PCI Bus Error: Error code: %d Error mode: %d\n",
+ pciio_vhdl, error_code, mode));
+
+ /* If there is an error handler sitting on
+ * the "no-slot" connection point, give it
+ * first crack at the error. NOTE: it is
+ * quite possible that this function may
+ * do further refining of the ioerror.
+ */
+ pciio_info = pciio_cardinfo_get(pciio_vhdl, PCIIO_SLOT_NONE);
+ if (pciio_info && pciio_info->c_efunc) {
+ pconn_vhdl = pciio_info_dev_get(pciio_info);
+#if defined(CONFIG_SGI_IO_ERROR_HANDLING)
+ e_state = error_state_get(pciio_vhdl);
+
+ if (e_state == ERROR_STATE_ACTION)
+ (void)error_state_set(pciio_vhdl, ERROR_STATE_NONE);
+
+ if (error_state_set(pconn_vhdl,e_state) ==
+ ERROR_RETURN_CODE_CANNOT_SET_STATE)
+ return(IOERROR_UNHANDLED);
+#endif
+ retval = pciio_info->c_efunc
+ (pciio_info->c_einfo, error_code, mode, ioerror);
+ if (retval != IOERROR_UNHANDLED)
+ return retval;
+ }
+
+ /* Is the error associated with a particular slot?
+ */
+ if (IOERROR_FIELDVALID(ioerror, widgetdev)) {
+ /*
+ * NOTE :
+ * widgetdev is a 4byte value encoded as slot in the higher order
+ * 2 bytes and function in the lower order 2 bytes.
+ */
+#ifdef IRIX
+ slot = pciio_widgetdev_slot_get(IOERROR_GETVALUE(ioerror, widgetdev));
+#else
+ slot = 0;
+#endif
+
+ /* If this slot has an error handler,
+ * deliver the error to it.
+ */
+ pciio_info = pciio_cardinfo_get(pciio_vhdl, slot);
+ if (pciio_info != NULL) {
+ if (pciio_info->c_efunc != NULL) {
+
+ pconn_vhdl = pciio_info_dev_get(pciio_info);
+#if defined(CONFIG_SGI_IO_ERROR_HANDLING)
+ e_state = error_state_get(pciio_vhdl);
+
+ if (e_state == ERROR_STATE_ACTION)
+ (void)error_state_set(pciio_vhdl, ERROR_STATE_NONE);
+
+ if (error_state_set(pconn_vhdl,e_state) ==
+ ERROR_RETURN_CODE_CANNOT_SET_STATE)
+ return(IOERROR_UNHANDLED);
+#endif
+ retval = pciio_info->c_efunc
+ (pciio_info->c_einfo, error_code, mode, ioerror);
+ if (retval != IOERROR_UNHANDLED)
+ return retval;
+ }
+
+#if USRPCI
+ /* If the USRPCI driver is available and
+ * knows about this connection point,
+ * deliver the error to it.
+ *
+ * OK to use pconn_vhdl here, even though we
+ * have already UNREF'd it, since we know that
+ * it is not going away.
+ */
+ pconn_vhdl = pciio_info_dev_get(pciio_info);
+ if (GRAPH_SUCCESS ==
+ hwgraph_traverse(pconn_vhdl, EDGE_LBL_USRPCI, &usrpci_v)) {
+ retval = usrpci_error_handler
+ (usrpci_v, error_code, IOERROR_GETVALUE(ioerror, busaddr));
+ hwgraph_vertex_unref(usrpci_v);
+ if (retval != IOERROR_UNHANDLED) {
+ /*
+ * This unref is not needed. If this code is called often enough,
+ * the system will crash, due to vertex reference count reaching 0,
+ * causing vertex to be unallocated. -jeremy
+ * hwgraph_vertex_unref(pconn_vhdl);
+ */
+ return retval;
+ }
+ }
+#endif
+ }
+ }
+
+ return (mode == MODE_DEVPROBE)
+ ? IOERROR_HANDLED /* probes are OK */
+ : IOERROR_UNHANDLED; /* otherwise, foo! */
+}
+
+int
+pciio_error_devenable(devfs_handle_t pconn_vhdl, int error_code)
+{
+ return DEV_FUNC(pconn_vhdl, error_devenable)
+ (pconn_vhdl, error_code);
+ /* no cleanup specific to this layer. */
+}
+
+/* =====================================================================
+ * CONFIGURATION MANAGEMENT
+ */
+
+/*
+ * Startup a crosstalk provider
+ */
+void
+pciio_provider_startup(devfs_handle_t pciio_provider)
+{
+ DEV_FUNC(pciio_provider, provider_startup)
+ (pciio_provider);
+}
+
+/*
+ * Shutdown a crosstalk provider
+ */
+void
+pciio_provider_shutdown(devfs_handle_t pciio_provider)
+{
+ DEV_FUNC(pciio_provider, provider_shutdown)
+ (pciio_provider);
+}
+
+/*
+ * Specify endianness constraints. The driver tells us what the device
+ * does and how it would like to see things in memory. We reply with
+ * how things will actually appear in memory.
+ */
+pciio_endian_t
+pciio_endian_set(devfs_handle_t dev,
+ pciio_endian_t device_end,
+ pciio_endian_t desired_end)
+{
+ ASSERT((device_end == PCIDMA_ENDIAN_BIG) || (device_end == PCIDMA_ENDIAN_LITTLE));
+ ASSERT((desired_end == PCIDMA_ENDIAN_BIG) || (desired_end == PCIDMA_ENDIAN_LITTLE));
+
+#if DEBUG
+ cmn_err(CE_ALERT,
+ "%v: pciio_endian_set is going away.\n"
+ "\tplease use PCIIO_BYTE_STREAM or PCIIO_WORD_VALUES in your\n"
+ "\tpciio_dmamap_alloc and pciio_dmatrans calls instead.\n",
+ dev);
+#endif
+
+ return DEV_FUNC(dev, endian_set)
+ (dev, device_end, desired_end);
+}
+
+/*
+ * Specify PCI arbitration priority.
+ */
+pciio_priority_t
+pciio_priority_set(devfs_handle_t dev,
+ pciio_priority_t device_prio)
+{
+ ASSERT((device_prio == PCI_PRIO_HIGH) || (device_prio == PCI_PRIO_LOW));
+
+ return DEV_FUNC(dev, priority_set)
+ (dev, device_prio);
+}
+
+/*
+ * Read value of configuration register
+ */
+uint64_t
+pciio_config_get(devfs_handle_t dev,
+ unsigned reg,
+ unsigned size)
+{
+ uint64_t value = 0;
+ unsigned shift = 0;
+
+ /* handle accesses that cross words here,
+ * since that's common code between all
+ * possible providers.
+ */
+ while (size > 0) {
+ unsigned biw = 4 - (reg&3);
+ if (biw > size)
+ biw = size;
+
+ value |= DEV_FUNC(dev, config_get)
+ (dev, reg, biw) << shift;
+
+ shift += 8*biw;
+ reg += biw;
+ size -= biw;
+ }
+ return value;
+}
+
+/*
+ * Change value of configuration register
+ */
+void
+pciio_config_set(devfs_handle_t dev,
+ unsigned reg,
+ unsigned size,
+ uint64_t value)
+{
+ /* handle accesses that cross words here,
+ * since that's common code between all
+ * possible providers.
+ */
+ while (size > 0) {
+ unsigned biw = 4 - (reg&3);
+ if (biw > size)
+ biw = size;
+
+ DEV_FUNC(dev, config_set)
+ (dev, reg, biw, value);
+ reg += biw;
+ size -= biw;
+ value >>= biw * 8;
+ }
+}
+
+/* =====================================================================
+ * GENERIC PCI SUPPORT FUNCTIONS
+ */
+pciio_slot_t
+pciio_error_extract(devfs_handle_t dev,
+ pciio_space_t *space,
+ iopaddr_t *offset)
+{
+ ASSERT(dev != NODEV);
+ return DEV_FUNC(dev,error_extract)(dev,space,offset);
+}
+
+/*
+ * Issue a hardware reset to a card.
+ */
+int
+pciio_reset(devfs_handle_t dev)
+{
+ return DEV_FUNC(dev, reset) (dev);
+}
+
+/*
+ * flush write gather buffers
+ */
+int
+pciio_write_gather_flush(devfs_handle_t dev)
+{
+ return DEV_FUNC(dev, write_gather_flush) (dev);
+}
+
+devfs_handle_t
+pciio_intr_dev_get(pciio_intr_t pciio_intr)
+{
+ return (pciio_intr->pi_dev);
+}
+
+/****** Generic crosstalk pio interfaces ******/
+devfs_handle_t
+pciio_pio_dev_get(pciio_piomap_t pciio_piomap)
+{
+ return (pciio_piomap->pp_dev);
+}
+
+pciio_slot_t
+pciio_pio_slot_get(pciio_piomap_t pciio_piomap)
+{
+ return (pciio_piomap->pp_slot);
+}
+
+pciio_space_t
+pciio_pio_space_get(pciio_piomap_t pciio_piomap)
+{
+ return (pciio_piomap->pp_space);
+}
+
+iopaddr_t
+pciio_pio_pciaddr_get(pciio_piomap_t pciio_piomap)
+{
+ return (pciio_piomap->pp_pciaddr);
+}
+
+ulong
+pciio_pio_mapsz_get(pciio_piomap_t pciio_piomap)
+{
+ return (pciio_piomap->pp_mapsz);
+}
+
+caddr_t
+pciio_pio_kvaddr_get(pciio_piomap_t pciio_piomap)
+{
+ return (pciio_piomap->pp_kvaddr);
+}
+
+/****** Generic crosstalk dma interfaces ******/
+devfs_handle_t
+pciio_dma_dev_get(pciio_dmamap_t pciio_dmamap)
+{
+ return (pciio_dmamap->pd_dev);
+}
+
+pciio_slot_t
+pciio_dma_slot_get(pciio_dmamap_t pciio_dmamap)
+{
+ return (pciio_dmamap->pd_slot);
+}
+
+/****** Generic pci slot information interfaces ******/
+
+pciio_info_t
+pciio_info_chk(devfs_handle_t pciio)
+{
+ arbitrary_info_t ainfo = 0;
+
+ hwgraph_info_get_LBL(pciio, INFO_LBL_PCIIO, &ainfo);
+ return (pciio_info_t) ainfo;
+}
+
+pciio_info_t
+pciio_info_get(devfs_handle_t pciio)
+{
+ pciio_info_t pciio_info;
+
+ pciio_info = (pciio_info_t) hwgraph_fastinfo_get(pciio);
+
+#ifdef DEBUG_PCIIO
+ {
+ int pos;
+ char dname[256];
+ pos = devfs_generate_path(pciio, dname, 256);
+ printk("%s : path= %s\n", __FUNCTION__, &dname[pos]);
+ }
+#endif /* DEBUG_PCIIO */
+
+#ifdef BRINGUP
+ if ((pciio_info != NULL) &&
+ (pciio_info->c_fingerprint != pciio_info_fingerprint)
+ && (pciio_info->c_fingerprint != NULL)) {
+#else
+ if ((pciio_info != NULL) &&
+ (pciio_info->c_fingerprint != pciio_info_fingerprint)) {
+#endif /* BRINGUP */
+
+ printk("pciio_info_get: Found fastinfo 0x%p but wrong fingerprint %s\n", pciio_info,
+ pciio_info->c_fingerprint);
+ return((pciio_info_t)-1); /* Should panic .. */
+ }
+
+
+ return pciio_info;
+}
+
+void
+pciio_info_set(devfs_handle_t pciio, pciio_info_t pciio_info)
+{
+ if (pciio_info != NULL)
+ pciio_info->c_fingerprint = pciio_info_fingerprint;
+ hwgraph_fastinfo_set(pciio, (arbitrary_info_t) pciio_info);
+
+ /* Also, mark this vertex as a PCI slot
+ * and use the pciio_info, so pciio_info_chk
+ * can work (and be fairly efficient).
+ */
+ hwgraph_info_add_LBL(pciio, INFO_LBL_PCIIO,
+ (arbitrary_info_t) pciio_info);
+}
+
+devfs_handle_t
+pciio_info_dev_get(pciio_info_t pciio_info)
+{
+ return (pciio_info->c_vertex);
+}
+
+/*ARGSUSED*/
+pciio_bus_t
+pciio_info_bus_get(pciio_info_t pciio_info)
+{
+ /* XXX for now O2 always gets back bus 0 */
+ return (pciio_bus_t)0;
+}
+
+pciio_slot_t
+pciio_info_slot_get(pciio_info_t pciio_info)
+{
+ return (pciio_info->c_slot);
+}
+
+pciio_function_t
+pciio_info_function_get(pciio_info_t pciio_info)
+{
+ return (pciio_info->c_func);
+}
+
+pciio_vendor_id_t
+pciio_info_vendor_id_get(pciio_info_t pciio_info)
+{
+ return (pciio_info->c_vendor);
+}
+
+pciio_device_id_t
+pciio_info_device_id_get(pciio_info_t pciio_info)
+{
+ return (pciio_info->c_device);
+}
+
+devfs_handle_t
+pciio_info_master_get(pciio_info_t pciio_info)
+{
+ return (pciio_info->c_master);
+}
+
+arbitrary_info_t
+pciio_info_mfast_get(pciio_info_t pciio_info)
+{
+ return (pciio_info->c_mfast);
+}
+
+pciio_provider_t *
+pciio_info_pops_get(pciio_info_t pciio_info)
+{
+ return (pciio_info->c_pops);
+}
+
+error_handler_f *
+pciio_info_efunc_get(pciio_info_t pciio_info)
+{
+ return (pciio_info->c_efunc);
+}
+
+error_handler_arg_t *
+pciio_info_einfo_get(pciio_info_t pciio_info)
+{
+ return (pciio_info->c_einfo);
+}
+
+pciio_space_t
+pciio_info_bar_space_get(pciio_info_t info, int win)
+{
+ return info->c_window[win].w_space;
+}
+
+iopaddr_t
+pciio_info_bar_base_get(pciio_info_t info, int win)
+{
+ return info->c_window[win].w_base;
+}
+
+size_t
+pciio_info_bar_size_get(pciio_info_t info, int win)
+{
+ return info->c_window[win].w_size;
+}
+
+iopaddr_t
+pciio_info_rom_base_get(pciio_info_t info)
+{
+ return info->c_rbase;
+}
+
+size_t
+pciio_info_rom_size_get(pciio_info_t info)
+{
+ return info->c_rsize;
+}
+
+
+/* =====================================================================
+ * GENERIC PCI INITIALIZATION FUNCTIONS
+ */
+
+/*
+ * pciioinit: called once during device driver
+ * initializtion if this driver is configured into
+ * the system.
+ */
+void
+pciio_init(void)
+{
+ cdl_p cp;
+
+#if DEBUG && ATTACH_DEBUG
+ printf("pciio_init\n");
+#endif
+ /* Allocate the registry.
+ * We might already have one.
+ * If we don't, go get one.
+ * MPness: someone might have
+ * set one up for us while we
+ * were not looking; use an atomic
+ * compare-and-swap to commit to
+ * using the new registry if and
+ * only if nobody else did first.
+ * If someone did get there first,
+ * toss the one we allocated back
+ * into the pool.
+ */
+ if (pciio_registry == NULL) {
+ cp = cdl_new(EDGE_LBL_PCI, "vendor", "device");
+ if (!compare_and_swap_ptr((void **) &pciio_registry, NULL, (void *) cp)) {
+ cdl_del(cp);
+ }
+ }
+ ASSERT(pciio_registry != NULL);
+}
+
+/*
+ * pciioattach: called for each vertex in the graph
+ * that is a PCI provider.
+ */
+/*ARGSUSED */
+int
+pciio_attach(devfs_handle_t pciio)
+{
+#if DEBUG && ATTACH_DEBUG
+ cmn_err(CE_CONT, "%v: pciio_attach\n", pciio);
+#endif
+ return 0;
+}
+
+/*
+ * Associate a set of pciio_provider functions with a vertex.
+ */
+void
+pciio_provider_register(devfs_handle_t provider, pciio_provider_t *pciio_fns)
+{
+ hwgraph_info_add_LBL(provider, INFO_LBL_PFUNCS, (arbitrary_info_t) pciio_fns);
+}
+
+/*
+ * Disassociate a set of pciio_provider functions with a vertex.
+ */
+void
+pciio_provider_unregister(devfs_handle_t provider)
+{
+ arbitrary_info_t ainfo;
+
+#ifdef IRIX
+ hwgraph_info_remove_LBL(provider, INFO_LBL_PFUNCS, &ainfo);
+#else
+ hwgraph_info_remove_LBL(provider, INFO_LBL_PFUNCS, (long *) &ainfo);
+#endif
+}
+
+/*
+ * Obtain a pointer to the pciio_provider functions for a specified Crosstalk
+ * provider.
+ */
+pciio_provider_t *
+pciio_provider_fns_get(devfs_handle_t provider)
+{
+ arbitrary_info_t ainfo = 0;
+
+ (void) hwgraph_info_get_LBL(provider, INFO_LBL_PFUNCS, &ainfo);
+ return (pciio_provider_t *) ainfo;
+}
+
+/*ARGSUSED4 */
+int
+pciio_driver_register(
+ pciio_vendor_id_t vendor_id,
+ pciio_device_id_t device_id,
+ char *driver_prefix,
+ unsigned flags)
+{
+ /* a driver's init routine might call
+ * pciio_driver_register before the
+ * system calls pciio_init; so we
+ * make the init call ourselves here.
+ */
+ if (pciio_registry == NULL)
+ pciio_init();
+
+ return cdl_add_driver(pciio_registry,
+ vendor_id, device_id,
+ driver_prefix, flags);
+}
+
+/*
+ * Remove an initialization function.
+ */
+void
+pciio_driver_unregister(
+ char *driver_prefix)
+{
+ /* before a driver calls unregister,
+ * it must have called register; so
+ * we can assume we have a registry here.
+ */
+ ASSERT(pciio_registry != NULL);
+
+ cdl_del_driver(pciio_registry, driver_prefix);
+}
+
+/*
+ * Call some function with each vertex that
+ * might be one of this driver's attach points.
+ */
+void
+pciio_iterate(char *driver_prefix,
+ pciio_iter_f * func)
+{
+ /* a driver's init routine might call
+ * pciio_iterate before the
+ * system calls pciio_init; so we
+ * make the init call ourselves here.
+ */
+ if (pciio_registry == NULL)
+ pciio_init();
+
+ ASSERT(pciio_registry != NULL);
+
+ cdl_iterate(pciio_registry, driver_prefix, (cdl_iter_f *) func);
+}
+
+devfs_handle_t
+pciio_device_register(
+ devfs_handle_t connectpt, /* vertex for /hw/.../pciio/%d */
+ devfs_handle_t master, /* card's master ASIC (PCI provider) */
+ pciio_slot_t slot, /* card's slot */
+ pciio_function_t func, /* card's func */
+ pciio_vendor_id_t vendor_id,
+ pciio_device_id_t device_id)
+{
+
+ return pciio_device_info_register
+ (connectpt, pciio_device_info_new (NULL, master, slot, func,
+ vendor_id, device_id));
+}
+
+void
+pciio_device_unregister(devfs_handle_t pconn)
+{
+ DEV_FUNC(pconn,device_unregister)(pconn);
+}
+
+pciio_info_t
+pciio_device_info_new(
+ pciio_info_t pciio_info,
+ devfs_handle_t master,
+ pciio_slot_t slot,
+ pciio_function_t func,
+ pciio_vendor_id_t vendor_id,
+ pciio_device_id_t device_id)
+{
+ if (!pciio_info)
+ NEW(pciio_info);
+ ASSERT(pciio_info != NULL);
+
+ pciio_info->c_slot = slot;
+ pciio_info->c_func = func;
+ pciio_info->c_vendor = vendor_id;
+ pciio_info->c_device = device_id;
+ pciio_info->c_master = master;
+ pciio_info->c_mfast = hwgraph_fastinfo_get(master);
+ pciio_info->c_pops = pciio_provider_fns_get(master);
+ pciio_info->c_efunc = 0;
+ pciio_info->c_einfo = 0;
+
+ return pciio_info;
+}
+
+void
+pciio_device_info_free(pciio_info_t pciio_info)
+{
+ /* NOTE : pciio_info is a structure within the pcibr_info
+ * and not a pointer to memory allocated on the heap !!
+ */
+ BZERO((char *)pciio_info,sizeof(pciio_info));
+}
+
+devfs_handle_t
+pciio_device_info_register(
+ devfs_handle_t connectpt, /* vertex at center of bus */
+ pciio_info_t pciio_info) /* details about the connectpt */
+{
+ char name[32];
+ devfs_handle_t pconn;
+
+ pciio_slot_func_to_name(name,
+ pciio_info->c_slot,
+ pciio_info->c_func);
+
+ printk("pciio_device_info_register: connectpt 0x%p, pciio_info 0x%p\n", connectpt, pciio_info);
+
+ if (GRAPH_SUCCESS !=
+ hwgraph_path_add(connectpt, name, &pconn))
+ return pconn;
+
+ pciio_info->c_vertex = pconn;
+ pciio_info_set(pconn, pciio_info);
+#ifdef BRINGUP
+ {
+ int pos;
+ char dname[256];
+ pos = devfs_generate_path(pconn, dname, 256);
+ printk("%s : pconn path= %s \n", __FUNCTION__, &dname[pos]);
+ }
+#endif /* BRINGUP */
+
+ /*
+ * create link to our pci provider
+ */
+
+ device_master_set(pconn, pciio_info->c_master);
+
+#if USRPCI
+ /*
+ * Call into usrpci provider to let it initialize for
+ * the given slot.
+ */
+ if (pciio_info->c_slot != PCIIO_SLOT_NONE)
+ usrpci_device_register(pconn, pciio_info->c_master, pciio_info->c_slot);
+#endif
+
+ return pconn;
+}
+
+void
+pciio_device_info_unregister(devfs_handle_t connectpt,
+ pciio_info_t pciio_info)
+{
+ char name[32];
+ devfs_handle_t pconn;
+
+ if (!pciio_info)
+ return;
+
+ pciio_slot_func_to_name(name,
+ pciio_info->c_slot,
+ pciio_info->c_func);
+
+ hwgraph_edge_remove(connectpt,name,&pconn);
+ pciio_info_set(pconn,0);
+
+ /* Remove the link to our pci provider */
+ hwgraph_edge_remove(pconn, EDGE_LBL_MASTER, NULL);
+
+ hwgraph_vertex_unref(pconn);
+ hwgraph_vertex_destroy(pconn);
+
+}
+/* Add the pci card inventory information to the hwgraph
+ */
+static void
+pciio_device_inventory_add(devfs_handle_t pconn_vhdl)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+
+ ASSERT(pciio_info);
+ ASSERT(pciio_info->c_vertex == pconn_vhdl);
+
+ /* Donot add inventory for non-existent devices */
+ if ((pciio_info->c_vendor == PCIIO_VENDOR_ID_NONE) ||
+ (pciio_info->c_device == PCIIO_DEVICE_ID_NONE))
+ return;
+ device_inventory_add(pconn_vhdl,INV_IOBD,INV_PCIADAP,
+ pciio_info->c_vendor,pciio_info->c_device,
+ pciio_info->c_slot);
+}
+
+static void
+pciio_device_inventory_remove(devfs_handle_t pconn_vhdl)
+{
+#ifdef IRIX
+ hwgraph_inventory_remove(pconn_vhdl,-1,-1,-1,-1,-1);
+#endif
+}
+
+/*ARGSUSED */
+int
+pciio_device_attach(devfs_handle_t pconn)
+{
+ pciio_info_t pciio_info;
+ pciio_vendor_id_t vendor_id;
+ pciio_device_id_t device_id;
+
+ pciio_device_inventory_add(pconn);
+ pciio_info = pciio_info_get(pconn);
+
+ vendor_id = pciio_info->c_vendor;
+ device_id = pciio_info->c_device;
+
+ printk("pciio_device_attach: Function 0x%p, vendor 0x%x, device_id %x\n", pconn, vendor_id, device_id);
+
+ /* we don't start attaching things until
+ * all the driver init routines (including
+ * pciio_init) have been called; so we
+ * can assume here that we have a registry.
+ */
+ ASSERT(pciio_registry != NULL);
+
+ return(cdl_add_connpt(pciio_registry, vendor_id, device_id, pconn));
+
+}
+
+int
+pciio_device_detach(devfs_handle_t pconn)
+{
+ pciio_info_t pciio_info;
+ pciio_vendor_id_t vendor_id;
+ pciio_device_id_t device_id;
+
+ pciio_device_inventory_remove(pconn);
+ pciio_info = pciio_info_get(pconn);
+
+ vendor_id = pciio_info->c_vendor;
+ device_id = pciio_info->c_device;
+
+ /* we don't start attaching things until
+ * all the driver init routines (including
+ * pciio_init) have been called; so we
+ * can assume here that we have a registry.
+ */
+ ASSERT(pciio_registry != NULL);
+
+ cdl_del_connpt(pciio_registry, vendor_id, device_id, pconn);
+
+ return(0);
+
+}
+
+/*
+ * pciio_error_register:
+ * arrange for a function to be called with
+ * a specified first parameter plus other
+ * information when an error is encountered
+ * and traced to the pci slot corresponding
+ * to the connection point pconn.
+ *
+ * may also be called with a null function
+ * pointer to "unregister" the error handler.
+ *
+ * NOTE: subsequent calls silently overwrite
+ * previous data for this vertex. We assume that
+ * cooperating drivers, well, cooperate ...
+ */
+void
+pciio_error_register(devfs_handle_t pconn,
+ error_handler_f *efunc,
+ error_handler_arg_t einfo)
+{
+ pciio_info_t pciio_info;
+
+ pciio_info = pciio_info_get(pconn);
+ ASSERT(pciio_info != NULL);
+ pciio_info->c_efunc = efunc;
+ pciio_info->c_einfo = einfo;
+}
+
+/*
+ * Check if any device has been found in this slot, and return
+ * true or false
+ * vhdl is the vertex for the slot
+ */
+int
+pciio_slot_inuse(devfs_handle_t pconn_vhdl)
+{
+ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl);
+
+ ASSERT(pciio_info);
+ ASSERT(pciio_info->c_vertex == pconn_vhdl);
+ if (pciio_info->c_vendor) {
+ /*
+ * Non-zero value for vendor indicate
+ * a board being found in this slot.
+ */
+ return 1;
+ }
+ return 0;
+}
+
+int
+pciio_dma_enabled(devfs_handle_t pconn_vhdl)
+{
+ return DEV_FUNC(pconn_vhdl, dma_enabled)(pconn_vhdl);
+}
diff --git a/arch/ia64/sn/io/sgi_if.c b/arch/ia64/sn/io/sgi_if.c
new file mode 100644
index 000000000..6ab200fb7
--- /dev/null
+++ b/arch/ia64/sn/io/sgi_if.c
@@ -0,0 +1,72 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/pci/bridge.h>
+#include <asm/sn/ioerror_handling.h>
+#include <asm/sn/pci/pciio.h>
+#include <asm/sn/slotnum.h>
+
+#define spinlock_init(x,name) mutex_init(x, MUTEX_DEFAULT, name);
+
+void *
+kmem_zalloc(size_t size, int flag)
+{
+ void *ptr = kmalloc(size, GFP_KERNEL);
+ BZERO(ptr, size);
+ return ptr;
+}
+
+#define xtod(c) ((c) <= '9' ? '0' - (c) : 'a' - (c) - 10)
+long
+atoi(register char *p)
+{
+ register long n;
+ register int c, neg = 0;
+
+ if (p == NULL)
+ return 0;
+
+ if (!isdigit(c = *p)) {
+ while (isspace(c))
+ c = *++p;
+ switch (c) {
+ case '-':
+ neg++;
+ case '+': /* fall-through */
+ c = *++p;
+ }
+ if (!isdigit(c))
+ return (0);
+ }
+ if (c == '0' && *(p + 1) == 'x') {
+ p += 2;
+ c = *p;
+ n = xtod(c);
+ while ((c = *++p) && isxdigit(c)) {
+ n *= 16; /* two steps to avoid unnecessary overflow */
+ n += xtod(c); /* accum neg to avoid surprises at MAX */
+ }
+ } else {
+ n = '0' - c;
+ while ((c = *++p) && isdigit(c)) {
+ n *= 10; /* two steps to avoid unnecessary overflow */
+ n += '0' - c; /* accum neg to avoid surprises at MAX */
+ }
+ }
+ return (neg ? n : -n);
+}
diff --git a/arch/ia64/sn/io/sgi_io_init.c b/arch/ia64/sn/io/sgi_io_init.c
new file mode 100644
index 000000000..87ab61b62
--- /dev/null
+++ b/arch/ia64/sn/io/sgi_io_init.c
@@ -0,0 +1,312 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/agent.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/sn_private.h>
+#include <asm/sn/synergy.h>
+#include <linux/smp.h>
+
+extern void mlreset(int );
+extern int init_hcl(void);
+extern void klgraph_hack_init(void);
+extern void per_hub_init(cnodeid_t);
+extern void hubspc_init(void);
+extern void pciba_init(void);
+extern void pciio_init(void);
+extern void pcibr_init(void);
+extern void xtalk_init(void);
+extern void xbow_init(void);
+extern void xbmon_init(void);
+extern void pciiox_init(void);
+extern void usrpci_init(void);
+extern void ioc3_init(void);
+extern void initialize_io(void);
+extern void init_platform_nodepda(nodepda_t *, cnodeid_t );
+extern void intr_clear_all(nasid_t);
+extern void klhwg_add_all_modules(devfs_handle_t);
+extern void klhwg_add_all_nodes(devfs_handle_t);
+
+void sn_mp_setup(void);
+extern devfs_handle_t hwgraph_root;
+extern void io_module_init(void);
+extern cnodeid_t nasid_to_compact_node[];
+extern void pci_bus_cvlink_init(void);
+extern void temp_hack(void);
+extern void init_platform_pda(cpuid_t cpu);
+
+extern int pci_bus_to_hcl_cvlink(void);
+extern synergy_da_t *Synergy_da_indr[];
+
+#define DEBUG_IO_INIT
+#ifdef DEBUG_IO_INIT
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif /* DEBUG_IO_INIT */
+
+/*
+ * kern/ml/csu.s calls mlsetup
+ * mlsetup calls mlreset(master) - kern/os/startup.c
+ * j main
+ *
+
+ * SN/slave.s start_slave_loop calls slave_entry
+ * SN/slave.s slave_entry calls slave_loop
+ * SN/slave.s slave_loop calls bootstrap
+ * bootstrap in SN1/SN1asm.s calls cboot
+ * cboot calls mlreset(slave) - ml/SN/mp.c
+ *
+ * sgi_io_infrastructure_init() gets called right before pci_init()
+ * in Linux mainline. This routine actually mirrors the IO Infrastructure
+ * call sequence in IRIX, ofcourse, nicely modified for Linux.
+ *
+ * It is very IMPORTANT that this call is only made by the Master CPU!
+ *
+ */
+
+void
+sgi_master_io_infr_init(void)
+{
+#ifdef Colin
+ /*
+ * Simulate Big Window 0.
+ * Only when we build for lutsen etc. ..
+ */
+ simulated_BW0_init();
+#endif
+
+ /*
+ * Do any early init stuff .. einit_tbl[] etc.
+ */
+ DBG("--> sgi_master_io_infr_init: calling init_hcl().\n");
+ init_hcl(); /* Sets up the hwgraph compatibility layer with devfs */
+
+ /*
+ * initialize the Linux PCI to xwidget vertexes ..
+ */
+ DBG("--> sgi_master_io_infr_init: calling pci_bus_cvlink_init().\n");
+ pci_bus_cvlink_init();
+
+ /*
+ * Hack to provide statically initialzed klgraph entries.
+ */
+ DBG("--> sgi_master_io_infr_init: calling klgraph_hack_init()\n");
+ klgraph_hack_init();
+
+ /*
+ * This is the Master CPU. Emulate mlsetup and main.c in Irix.
+ */
+ DBG("--> sgi_master_io_infr_init: calling mlreset(0).\n");
+ mlreset(0); /* Master .. */
+
+ /*
+ * allowboot() is called by kern/os/main.c in main()
+ * Emulate allowboot() ...
+ * per_cpu_init() - only need per_hub_init()
+ * cpu_io_setup() - Nothing to do.
+ *
+ */
+ DBG("--> sgi_master_io_infr_init: calling sn_mp_setup().\n");
+ sn_mp_setup();
+
+ DBG("--> sgi_master_io_infr_init: calling per_hub_init(0).\n");
+ per_hub_init(0); /* Need to get and send in actual cnode number */
+
+ /* We can do headless hub cnodes here .. */
+
+ /*
+ * io_init[] stuff.
+ *
+ * Get SGI IO Infrastructure drivers to init and register with
+ * each other etc.
+ */
+
+ DBG("--> sgi_master_io_infr_init: calling hubspc_init()\n");
+ hubspc_init();
+
+ DBG("--> sgi_master_io_infr_init: calling pciba_init()\n");
+ pciba_init();
+
+ DBG("--> sgi_master_io_infr_init: calling pciio_init()\n");
+ pciio_init();
+
+ DBG("--> sgi_master_io_infr_init: calling pcibr_init()\n");
+ pcibr_init();
+
+ DBG("--> sgi_master_io_infr_init: calling xtalk_init()\n");
+ xtalk_init();
+
+ DBG("--> sgi_master_io_infr_init: calling xbow_init()\n");
+ xbow_init();
+
+ DBG("--> sgi_master_io_infr_init: calling xbmon_init()\n");
+ xbmon_init();
+
+ DBG("--> sgi_master_io_infr_init: calling pciiox_init()\n");
+ pciiox_init();
+
+ DBG("--> sgi_master_io_infr_init: calling usrpci_init()\n");
+ usrpci_init();
+
+ DBG("--> sgi_master_io_infr_init: calling ioc3_init()\n");
+ ioc3_init();
+
+ /*
+ *
+ * Our IO Infrastructure drivers are in place ..
+ * Initialize the whole IO Infrastructure .. xwidget/device probes.
+ *
+ */
+ DBG("--> sgi_master_io_infr_init: Start Probe and IO Initialization\n");
+ initialize_io();
+
+ DBG("--> sgi_master_io_infr_init: Setting up SGI IO Links for Linux PCI\n");
+ pci_bus_to_hcl_cvlink();
+
+ DBG("--> Leave sgi_master_io_infr_init: DONE setting up SGI Links for PCI\n");
+}
+
+/*
+ * sgi_slave_io_infr_init - This routine must be called on all cpus except
+ * the Master CPU.
+ */
+void
+sgi_slave_io_infr_init(void)
+{
+ /* Emulate cboot() .. */
+ mlreset(1); /* This is a slave cpu */
+
+ per_hub_init(0); /* Need to get and send in actual cnode number */
+
+ /* Done */
+}
+
+/*
+ * One-time setup for MP SN.
+ * Allocate per-node data, slurp prom klconfig information and
+ * convert it to hwgraph information.
+ */
+void
+sn_mp_setup(void)
+{
+ cnodeid_t cnode;
+ extern int maxnodes;
+ cpuid_t cpu;
+
+ DBG("sn_mp_setup: Entered.\n");
+ /*
+ * NODEPDA(x) Macro depends on nodepda
+ * subnodepda is also statically set to calias space which we
+ * do not currently support yet .. just a hack for now.
+ */
+#ifdef NUMA_BASE
+ DBG("sn_mp_setup(): maxnodes= %d numnodes= %d\n", maxnodes,numnodes);
+ maxnodes = numnodes;
+#ifdef SIMULATED_KLGRAPH
+ maxnodes = 1;
+ numnodes = 1;
+#endif /* SIMULATED_KLGRAPH */
+ printk("sn_mp_setup(): Allocating backing store for *Nodepdaindr[%2d] \n",
+ maxnodes);
+
+ /*
+ * Initialize Nodpdaindr and per-node nodepdaindr array
+ */
+ *Nodepdaindr = (nodepda_t *) kmalloc(sizeof(nodepda_t *)*numnodes, GFP_KERNEL);
+ for (cnode=0; cnode<maxnodes; cnode++) {
+ Nodepdaindr[cnode] = (nodepda_t *) kmalloc(sizeof(struct nodepda_s),
+ GFP_KERNEL);
+ Synergy_da_indr[cnode * 2] = (synergy_da_t *) kmalloc(
+ sizeof(synergy_da_t), GFP_KERNEL);
+ Synergy_da_indr[(cnode * 2) + 1] = (synergy_da_t *) kmalloc(
+ sizeof(synergy_da_t), GFP_KERNEL);
+ Nodepdaindr[cnode]->pernode_pdaindr = Nodepdaindr;
+ subnodepda = &Nodepdaindr[cnode]->snpda[cnode];
+ }
+ nodepda = Nodepdaindr[0];
+#else
+ Nodepdaindr = (nodepda_t *) kmalloc(sizeof(struct nodepda_s), GFP_KERNEL);
+ nodepda = Nodepdaindr[0];
+ subnodepda = &Nodepdaindr[0]->snpda[0];
+
+#endif /* NUMA_BASE */
+
+ /*
+ * Before we let the other processors run, set up the platform specific
+ * stuff in the nodepda.
+ *
+ * ???? maxnodes set in mlreset .. who sets it now ????
+ * ???? cpu_node_probe() called in mlreset to set up the following:
+ * compact_to_nasid_node[] - cnode id gives nasid
+ * nasid_to_compact_node[] - nasid gives cnode id
+ *
+ * do_cpumask() sets the following:
+ * cpuid_to_compact_node[] - cpuid gives cnode id
+ *
+ * nasid comes from gdap->g_nasidtable[]
+ * ml/SN/promif.c
+ */
+
+ for (cnode = 0; cnode < maxnodes; cnode++) {
+ /*
+ * Set up platform-dependent nodepda fields.
+ * The following routine actually sets up the hubinfo struct
+ * in nodepda.
+ */
+ DBG("sn_mp_io_setup: calling init_platform_nodepda(%2d)\n",cnode);
+ init_platform_nodepda(Nodepdaindr[cnode], cnode);
+
+ /*
+ * This routine clears the Hub's Interrupt registers.
+ */
+#ifndef CONFIG_IA64_SGI_IO
+ /*
+ * We need to move this intr_clear_all() routine
+ * from SN/intr.c to a more appropriate file.
+ * Talk to Al Mayer.
+ */
+ intr_clear_all(COMPACT_TO_NASID_NODEID(cnode));
+#endif
+ }
+
+#ifdef CONFIG_IA64_SGI_IO
+ for (cpu = 0; cpu < smp_num_cpus; cpu++) {
+ /* Skip holes in CPU space */
+ if (cpu_enabled(cpu)) {
+ init_platform_pda(cpu);
+ }
+ }
+#endif
+
+ /*
+ * Initialize platform-dependent vertices in the hwgraph:
+ * module
+ * node
+ * cpu
+ * memory
+ * slot
+ * hub
+ * router
+ * xbow
+ */
+
+ DBG("sn_mp_io_setup: calling io_module_init()\n");
+ io_module_init(); /* Use to be called module_init() .. */
+
+ DBG("sn_mp_setup: calling klhwg_add_all_modules()\n");
+ klhwg_add_all_modules(hwgraph_root);
+ DBG("sn_mp_setup: calling klhwg_add_all_nodes()\n");
+ klhwg_add_all_nodes(hwgraph_root);
+}
diff --git a/arch/ia64/sn/io/sgi_io_sim.c b/arch/ia64/sn/io/sgi_io_sim.c
new file mode 100644
index 000000000..41ce62d51
--- /dev/null
+++ b/arch/ia64/sn/io/sgi_io_sim.c
@@ -0,0 +1,161 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/agent.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/module.h>
+#include <asm/sn/nic.h>
+#include <asm/sn/sn_private.h>
+#include <asm/sn/synergy.h>
+
+cnodeid_t nasid_to_compact_node[MAX_NASIDS];
+nasid_t compact_to_nasid_node[MAX_COMPACT_NODES];
+cnodeid_t cpuid_to_compact_node[MAXCPUS];
+cpuid_t master_procid = 0;
+int maxnodes;
+char arg_maxnodes[4];
+
+nodepda_t *Nodepdaindr[MAX_COMPACT_NODES];
+nodepda_t *nodepda;
+subnode_pda_t *subnodepda;
+
+synergy_da_t *Synergy_da_indr[MAX_COMPACT_NODES * 2];
+
+extern void init_all_devices(void);
+
+
+/*
+ * Return non-zero if the given variable was specified
+ */
+int
+is_specified(char *s)
+{
+ return (strlen(s) != 0);
+}
+
+
+void pciba_init(void)
+{
+ FIXME("pciba_init : no-op\n");
+}
+
+void xbmon_init(void)
+{
+ FIXME("xbmon_init : no-op\n");
+
+}
+
+void pciiox_init(void)
+{
+ FIXME("pciiox_init : no-op\n");
+
+}
+
+void usrpci_init(void)
+{
+ FIXME("usrpci_init : no-op\n");
+
+}
+
+void ioc3_init(void)
+{
+ FIXME("ioc3_init : no-op\n");
+
+}
+
+void initialize_io(void)
+{
+
+ init_all_devices();
+}
+
+/*
+ * Routines provided by ml/SN/promif.c.
+ */
+static __psunsigned_t master_bridge_base = (__psunsigned_t)NULL;
+static nasid_t console_nasid;
+static char console_wid;
+static char console_pcislot;
+
+void
+set_master_bridge_base(void)
+{
+
+#ifdef SIMULATED_KLGRAPH
+ printk("set_master_bridge_base: SIMULATED_KLGRAPH FIXME hardwired master.\n");
+ console_nasid = 0;
+ console_wid = 0x8;
+ console_pcislot = 0x2;
+#else
+ console_nasid = KL_CONFIG_CH_CONS_INFO(master_nasid)->nasid;
+ console_wid = WIDGETID_GET(KL_CONFIG_CH_CONS_INFO(master_nasid)->memory_base);
+ console_pcislot = KL_CONFIG_CH_CONS_INFO(master_nasid)->npci;
+#endif /* SIMULATED_KLGRAPH */
+
+ master_bridge_base = (__psunsigned_t)NODE_SWIN_BASE(console_nasid,
+ console_wid);
+}
+
+int
+check_nasid_equiv(nasid_t nasida, nasid_t nasidb)
+{
+ if ((nasida == nasidb) ||
+ (nasida == NODEPDA(NASID_TO_COMPACT_NODEID(nasidb))->xbow_peer))
+ return 1;
+ else
+ return 0;
+}
+
+int
+is_master_nasid_widget(nasid_t test_nasid, xwidgetnum_t test_wid)
+{
+
+ /*
+ * If the widget numbers are different, we're not the master.
+ */
+ if (test_wid != (xwidgetnum_t)console_wid)
+ return 0;
+
+ /*
+ * If the NASIDs are the same or equivalent, we're the master.
+ */
+ if (check_nasid_equiv(test_nasid, console_nasid)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+cnodeid_t
+nasid_to_compact_nodeid(nasid_t nasid)
+{
+ ASSERT(nasid >= 0 && nasid < MAX_NASIDS);
+ return nasid_to_compact_node[nasid];
+}
+
+nasid_t
+compact_to_nasid_nodeid(cnodeid_t cnode)
+{
+ ASSERT(cnode >= 0 && cnode <= MAX_COMPACT_NODES);
+ ASSERT(compact_to_nasid_node[cnode] >= 0);
+ return compact_to_nasid_node[cnode];
+}
+
+/*
+ * Routines provided by ml/SN/nvram.c
+ */
+void
+nvram_baseinit(void)
+{
+ FIXME("nvram_baseinit : no-op\n");
+
+}
diff --git a/arch/ia64/sn/io/stubs.c b/arch/ia64/sn/io/stubs.c
new file mode 100644
index 000000000..3dacf2fe5
--- /dev/null
+++ b/arch/ia64/sn/io/stubs.c
@@ -0,0 +1,256 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/pci/bridge.h>
+#include <asm/sn/ioerror_handling.h>
+#include <asm/sn/pci/pciio.h>
+#include <asm/sn/slotnum.h>
+#include <asm/sn/vector.h>
+
+/******
+ ****** hack defines ......
+ ******/
+
+int pcibr_prefetch_enable_rev, pcibr_wg_enable_rev;
+int default_intr_pri;
+int force_fire_and_forget;
+int ignore_conveyor_override;
+
+#define spinlock_init(x,name) mutex_init(x, MUTEX_DEFAULT, name);
+
+devfs_handle_t dummy_vrtx; /* Needed for cpuid_to_vertex() in hack.h */
+
+
+/* ARGSUSED */
+void hub_widgetdev_enable(devfs_handle_t xconn_vhdl, int devnum)
+ {FIXME("hub_widgetdev_enable");}
+
+/* ARGSUSED */
+void hub_widgetdev_shutdown(devfs_handle_t xconn_vhdl, int devnum)
+ {FIXME("hub_widgetdev_shutdown");}
+
+/* ARGSUSED */
+void hub_widget_reset(devfs_handle_t hubv, xwidgetnum_t widget)
+ {FIXME("hub_widget_reset");}
+
+boolean_t
+is_sys_critical_vertex(devfs_handle_t x)
+{
+ FIXME("is_sys_critical_vertex : returns 0");
+ return(0);
+}
+
+char *
+nic_bridge_vertex_info(devfs_handle_t v, nic_data_t mcr)
+{
+ FIXME("nic_bridge_vertex_info : returns NULL");
+ return((char *)0);
+}
+
+void *
+kmem_alloc_node(register size_t size, register int flags, cnodeid_t node)
+{
+ /* Allocates on node 'node' */
+ FIXME("kmem_alloc_node : use kmalloc");
+ return(kmalloc(size, GFP_KERNEL));
+}
+
+void *
+kmem_zalloc_node(register size_t size, register int flags, cnodeid_t node)
+{
+ FIXME("kmem_zalloc_node : use kmalloc");
+ return(kmalloc(size, GFP_KERNEL));
+}
+
+void
+kmem_free(void *where, int size)
+{
+ FIXME("kmem_free : use kfree");
+ return(kfree(where));
+}
+
+
+void *
+kmem_zone_alloc(register zone_t *zone, int flags)
+{
+ FIXME("kmem_zone_alloc : return null");
+ return((void *)0);
+}
+
+void
+kmem_zone_free(register zone_t *zone, void *ptr)
+{
+ FIXME("kmem_zone_free : no-op");
+}
+
+zone_t *
+kmem_zone_init(register int size, char *zone_name)
+{
+ FIXME("kmem_zone_free : returns NULL");
+ return((zone_t *)0);
+}
+
+uint64_t
+rmalloc(struct map *mp, size_t size)
+{
+ FIXME("rmalloc : returns NULL");
+ return((uint64_t)0);
+}
+
+void
+rmfree(struct map *mp, size_t size, uint64_t a)
+{
+ FIXME("rmfree : no-op");
+}
+
+struct map *
+rmallocmap(uint64_t mapsiz)
+{
+ FIXME("rmallocmap : returns NULL");
+ return((struct map *)0);
+}
+
+void
+rmfreemap(struct map *mp)
+{
+ FIXME("rmfreemap : no-op");
+}
+
+int
+compare_and_swap_ptr(void **location, void *old_ptr, void *new_ptr)
+{
+ FIXME("compare_and_swap_ptr : NOT ATOMIC");
+ if (*location == old_ptr) {
+ *location = new_ptr;
+ return(1);
+ }
+ else
+ return(0);
+}
+
+void *
+swap_ptr(void **loc, void *new)
+{
+ FIXME("swap_ptr : returns null");
+ return((void *)0);
+}
+
+/* For ml/SN/SN1/slots.c */
+/* ARGSUSED */
+slotid_t get_widget_slotnum(int xbow, int widget)
+ {FIXME("get_widget_slotnum"); return (unsigned char)NULL;}
+
+/* For router */
+int
+router_init(cnodeid_t cnode,int writeid, void *npda_rip)
+ {FIXME("router_init"); return(0);}
+
+/* From io/ioerror_handling.c */
+error_return_code_t
+sys_critical_graph_vertex_add(devfs_handle_t parent, devfs_handle_t child)
+ {FIXME("sys_critical_graph_vertex_add"); return(0);}
+
+/* From io/ioc3.c */
+devfs_handle_t
+ioc3_console_vhdl_get(void)
+ {FIXME("ioc3_console_vhdl_get"); return( (devfs_handle_t)-1);}
+
+
+#if 0
+#define io_splock(l) 1
+#define io_spunlock(l,s)
+
+#define spinlock_destroy(a) /* needed by pcibr_detach() */
+#define mutex_spinlock(a) 0
+#define mutex_spinunlock(a,b)
+#define mutex_init(a,b,c) ;
+#define mutex_lock(a,b) ;
+#define mutex_unlock(a) ;
+#define dev_to_vhdl(dev) 0
+#define get_timestamp() 0
+#define us_delay(a)
+#define v_mapphys(a,b,c) 0
+#define splhi() 0
+#define splx(s)
+#define spinlock_init(x,name) mutex_init(x, MUTEX_DEFAULT, name);
+#endif /* 0 */
+
+int
+cap_able(uint64_t x)
+{
+ FIXME("cap_able : returns 1");
+ return(1);
+}
+
+int
+cap_able_cred(uint64_t a, uint64_t b)
+{
+ FIXME("cap_able_cred : returns 1");
+ return(1);
+}
+
+void
+nic_vmc_check(devfs_handle_t vhdl, char *nicinfo)
+{
+
+ FIXME("nic_vmc_check\n");
+
+}
+
+char *
+nic_vertex_info_get(devfs_handle_t v)
+{
+
+ FIXME("nic_vertex_info_get\n");
+ return(NULL);
+
+}
+
+int
+vector_read_node(net_vec_t dest, nasid_t nasid,
+ int write_id, int address,
+ uint64_t *value)
+{
+ FIXME("vector_read_node\n");
+ return(0);
+}
+
+int
+vector_write_node(net_vec_t dest, nasid_t nasid,
+ int write_id, int address,
+ uint64_t value)
+{
+ FIXME("vector_write_node\n");
+ return(0);
+}
+
+int
+atomicAddInt(int *int_ptr, int value)
+{
+// FIXME("atomicAddInt : simple add\n");
+ *int_ptr += value;
+ return(0);
+}
+
+int
+atomicClearInt(int *int_ptr, int value)
+{
+ FIXME("atomicClearInt : simple clear\n");
+ *int_ptr &= ~value;
+ return(0);
+}
diff --git a/arch/ia64/sn/io/xbow.c b/arch/ia64/sn/io/xbow.c
new file mode 100644
index 000000000..904cf732c
--- /dev/null
+++ b/arch/ia64/sn/io/xbow.c
@@ -0,0 +1,1866 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/pci/bridge.h>
+#include <asm/sn/xtalk/xtalk_private.h>
+
+#define DEBUG 1
+#define XBOW_DEBUG 1
+
+
+/*
+ * Files needed to get the device driver entry points
+ */
+
+/* #include <asm/cred.h> */
+
+#include <asm/sn/xtalk/xbow.h>
+#include <asm/sn/xtalk/xtalk.h>
+#include <asm/sn/xtalk/xswitch.h>
+#include <asm/sn/xtalk/xwidget.h>
+
+#include <asm/sn/prio.h>
+#include <asm/sn/hcl_util.h>
+
+
+#define NEW(ptr) (ptr = kmalloc(sizeof (*(ptr)), GFP_KERNEL))
+#define DEL(ptr) (kfree(ptr))
+
+int xbow_devflag = D_MP;
+
+/*
+ * This file supports the Xbow chip. Main functions: initializtion,
+ * error handling, and GBR.
+ */
+
+/*
+ * each vertex corresponding to an xbow chip
+ * has a "fastinfo" pointer pointing at one
+ * of these things.
+ */
+typedef struct xbow_soft_s *xbow_soft_t;
+
+struct xbow_soft_s {
+ devfs_handle_t conn; /* our connection point */
+ devfs_handle_t vhdl; /* xbow's private vertex */
+ devfs_handle_t busv; /* the xswitch vertex */
+ xbow_t *base; /* PIO pointer to crossbow chip */
+ char *name; /* hwgraph name */
+
+ xbow_perf_t xbow_perfcnt[XBOW_PERF_COUNTERS];
+ xbow_perf_link_t xbow_perflink[MAX_XBOW_PORTS];
+ xbow_link_status_t xbow_link_status[MAX_XBOW_PORTS];
+ lock_t xbow_perf_lock;
+ int link_monitor;
+ widget_cfg_t *wpio[MAX_XBOW_PORTS]; /* cached PIO pointer */
+
+ /* Bandwidth allocation state. Bandwidth values are for the
+ * destination port since contention happens there.
+ * Implicit mapping from xbow ports (8..f) -> (0..7) array indices.
+ */
+ lock_t xbow_bw_alloc_lock; /* bw allocation lock */
+ unsigned long long bw_hiwm[MAX_XBOW_PORTS]; /* hiwater mark values */
+ unsigned long long bw_cur_used[MAX_XBOW_PORTS]; /* bw used currently */
+};
+
+#define xbow_soft_set(v,i) hwgraph_fastinfo_set((v), (arbitrary_info_t)(i))
+#define xbow_soft_get(v) ((xbow_soft_t)hwgraph_fastinfo_get((v)))
+
+/*
+ * Function Table of Contents
+ */
+
+void xbow_mlreset(xbow_t *);
+void xbow_init(void);
+int xbow_attach(devfs_handle_t);
+
+int xbow_open(devfs_handle_t *, int, int, cred_t *);
+int xbow_close(devfs_handle_t, int, int, cred_t *);
+
+int xbow_map(devfs_handle_t, vhandl_t *, off_t, size_t, uint);
+int xbow_unmap(devfs_handle_t, vhandl_t *);
+int xbow_ioctl(devfs_handle_t, int, void *, int, struct cred *, int *);
+
+int xbow_widget_present(xbow_t *, int);
+static int xbow_link_alive(xbow_t *, int);
+devfs_handle_t xbow_widget_lookup(devfs_handle_t, int);
+
+#ifdef LATER
+static void xbow_setwidint(xtalk_intr_t);
+static void xbow_errintr_handler(intr_arg_t);
+static error_handler_f xbow_error_handler;
+#endif
+void xbow_intr_preset(void *, int, xwidgetnum_t, iopaddr_t, xtalk_intr_vector_t);
+
+
+
+void xbow_update_perf_counters(devfs_handle_t);
+xbow_perf_link_t *xbow_get_perf_counters(devfs_handle_t);
+int xbow_enable_perf_counter(devfs_handle_t, int, int, int);
+xbow_link_status_t *xbow_get_llp_status(devfs_handle_t);
+void xbow_update_llp_status(devfs_handle_t);
+
+int xbow_disable_llp_monitor(devfs_handle_t);
+int xbow_enable_llp_monitor(devfs_handle_t);
+
+#ifdef IRIX
+int xbow_prio_bw_alloc(devfs_handle_t, xwidgetnum_t, xwidgetnum_t,
+ unsigned long long, unsigned long long);
+#else
+int xbow_prio_bw_alloc(devfs_handle_t, xwidgetnum_t, xwidgetnum_t,
+ unsigned long long, unsigned long long);
+#endif
+
+
+xswitch_reset_link_f xbow_reset_link;
+
+void idbg_xbowregs(int64_t);
+
+xswitch_provider_t xbow_provider =
+{
+ xbow_reset_link,
+};
+
+/*
+ * xbow_mlreset: called at mlreset time if the
+ * platform specific code determines that there is
+ * a crossbow in a critical path that must be
+ * functional before the driver would normally get
+ * the device properly set up.
+ *
+ * what do we need to do, that the boot prom can
+ * not be counted on to have already done, that is
+ * generic across all platforms using crossbows?
+ */
+/*ARGSUSED */
+void
+xbow_mlreset(xbow_t * xbow)
+{
+}
+
+/*
+ * xbow_init: called with the rest of the device
+ * driver XXX_init routines. This platform *might*
+ * have a Crossbow chip, or even several, but it
+ * might have none. Register with the crosstalk
+ * generic provider so when we encounter the chip
+ * the right magic happens.
+ */
+void
+xbow_init(void)
+{
+
+#if DEBUG && ATTACH_DEBUG
+ printf("xbow_init\n");
+#endif
+
+ xwidget_driver_register(XXBOW_WIDGET_PART_NUM,
+ 0, /* XXBOW_WIDGET_MFGR_NUM, */
+ "xbow_",
+ CDL_PRI_HI); /* attach before friends */
+
+ xwidget_driver_register(XBOW_WIDGET_PART_NUM,
+ XBOW_WIDGET_MFGR_NUM,
+ "xbow_",
+ CDL_PRI_HI); /* attach before friends */
+}
+
+#ifdef XBRIDGE_REGS_SIM
+/* xbow_set_simulated_regs: sets xbow regs as needed
+ * for powering through the boot
+ */
+void
+xbow_set_simulated_regs(xbow_t *xbow, int port)
+{
+ /*
+ * turn on link
+ */
+ xbow->xb_link(port).link_status = (1<<31);
+ /*
+ * and give it a live widget too
+ */
+ xbow->xb_link(port).link_aux_status = XB_AUX_STAT_PRESENT;
+ /*
+ * zero the link control reg
+ */
+ xbow->xb_link(port).link_control = 0x0;
+}
+#endif /* XBRIDGE_REGS_SIM */
+
+/*
+ * xbow_attach: the crosstalk provider has
+ * determined that there is a crossbow widget
+ * present, and has handed us the connection
+ * point for that vertex.
+ *
+ * We not only add our own vertex, but add
+ * some "xtalk switch" data to the switch
+ * vertex (at the connect point's parent) if
+ * it does not have any.
+ */
+
+/*ARGSUSED */
+int
+xbow_attach(devfs_handle_t conn)
+{
+ /*REFERENCED */
+ devfs_handle_t vhdl;
+ devfs_handle_t busv;
+ xbow_t *xbow;
+ xbow_soft_t soft;
+ int port;
+ xswitch_info_t info;
+#ifdef LATER
+ xtalk_intr_t intr_hdl;
+ device_desc_t dev_desc;
+#endif
+ char devnm[MAXDEVNAME], *s;
+ xbowreg_t id;
+ int rev;
+ int i;
+ int xbow_num;
+
+#if DEBUG && ATTACH_DEBUG
+ cmn_err(CE_CONT, "%v: xbow_attach\n", conn);
+#endif
+
+ /*
+ * Get a PIO pointer to the base of the crossbow
+ * chip.
+ */
+#ifdef XBRIDGE_REGS_SIM
+ printk("xbow_attach: XBRIDGE_REGS_SIM FIXME: allocating %ld bytes for xbow_s\n", sizeof(xbow_t));
+ xbow = (xbow_t *) kmalloc(sizeof(xbow_t), GFP_KERNEL);
+ /*
+ * turn on ports e and f like in a real live ibrick
+ */
+ xbow_set_simulated_regs(xbow, 0xe);
+ xbow_set_simulated_regs(xbow, 0xf);
+#else
+ xbow = (xbow_t *) xtalk_piotrans_addr(conn, 0, 0, sizeof(xbow_t), 0);
+#endif /* XBRIDGE_REGS_SIM */
+
+ /*
+ * Locate the "switch" vertex: it is the parent
+ * of our connection point.
+ */
+ busv = hwgraph_connectpt_get(conn);
+ printk("xbow_attach: Bus Vertex 0x%p, conn 0x%p, xbow register 0x%p wid= 0x%x\n", busv, conn, xbow, *(volatile u32 *)xbow);
+
+ ASSERT(busv != GRAPH_VERTEX_NONE);
+
+ /*
+ * Create our private vertex, and connect our
+ * driver information to it. This makes it possible
+ * for diagnostic drivers to open the crossbow
+ * vertex for access to registers.
+ */
+
+ /*
+ * We need to teach xbow drivers to provide the right set of
+ * file ops.
+ */
+ vhdl = NULL;
+ vhdl = hwgraph_register(conn, EDGE_LBL_XBOW,
+ 0, DEVFS_FL_AUTO_DEVNUM,
+ 0, 0,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0,
+ /* &hcl_fops */ (void *)&vhdl, NULL);
+ if (!vhdl) {
+ printk("xbow_attach: Unable to create char device for xbow conn
+0x%p\n",
+ conn);
+ }
+
+ /*
+ * Allocate the soft state structure and attach
+ * it to the xbow's vertex
+ */
+ NEW(soft);
+ soft->conn = conn;
+ soft->vhdl = vhdl;
+ soft->busv = busv;
+ soft->base = xbow;
+ /* does the universe really need another macro? */
+ /* xbow_soft_set(vhdl, (arbitrary_info_t) soft); */
+ hwgraph_fastinfo_set(vhdl, (arbitrary_info_t) soft);
+
+#define XBOW_NUM_SUFFIX_FORMAT "[xbow# %d]"
+
+ /* Add xbow number as a suffix to the hwgraph name of the xbow.
+ * This is helpful while looking at the error/warning messages.
+ */
+#if CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 || CONFIG_IA64_GENERIC
+ xbow_num = 0;
+#else
+ xbow_num = xswitch_id_get(busv);
+#endif
+
+ /*
+ * get the name of this xbow vertex and keep the info.
+ * This is needed during errors and interupts, but as
+ * long as we have it, we can use it elsewhere.
+ */
+ s = dev_to_name(vhdl, devnm, MAXDEVNAME);
+ soft->name = kmalloc(strlen(s) + strlen(XBOW_NUM_SUFFIX_FORMAT) + 1,
+ GFP_KERNEL);
+ sprintf(soft->name,"%s"XBOW_NUM_SUFFIX_FORMAT, s,xbow_num);
+
+#ifdef XBRIDGE_REGS_SIM
+ /* my o200/ibrick has id=0x2d002049, but XXBOW_WIDGET_PART_NUM is defined
+ * as 0xd000, so I'm using that for the partnum bitfield.
+ */
+ printk("xbow_attach: XBRIDGE_REGS_SIM FIXME: need xb_wid_id value!!\n");
+ id = 0x2d000049;
+#else
+ id = xbow->xb_wid_id;
+#endif /* XBRIDGE_REGS_SIM */
+ rev = XWIDGET_PART_REV_NUM(id);
+
+ /*
+ * Print the revision if DEBUG, or SHOW_REVS and kdebug,
+ * or the xbow is downrev.
+ *
+ * If xbow is downrev, make it a WARNING that the
+ * Crossbow is DOWNREV: these chips are not good
+ * to have around, and the operator should be told.
+ */
+#ifdef IRIX
+#if !DEBUG
+ if (
+#if SHOW_REVS
+ (kdebug) ||
+#endif /* SHOW_REVS */
+ (rev < XBOW_REV_1_1))
+#endif /* !DEBUG */
+ cmn_err((rev < XBOW_REV_1_1) ? CE_WARN : CE_CONT,
+ "%sCrossbow ASIC: rev %s (code=%d) at %s%s",
+ (rev < XBOW_REV_1_1) ? "DOWNREV " : "",
+ (rev == XBOW_REV_1_0) ? "1.0" :
+ (rev == XBOW_REV_1_1) ? "1.1" :
+ (rev == XBOW_REV_1_2) ? "1.2" :
+ (rev == XBOW_REV_1_3) ? "1.3" :
+ (rev == XBOW_REV_2_0) ? "2.0" :
+ (rev == XXBOW_PART_REV_1_0) ? "Xbridge 1.0" :
+ (rev == XXBOW_PART_REV_2_0) ? "Xbridge 2.0" :
+ "unknown",
+ rev, soft->name,
+ (rev < XBOW_REV_1_1) ? "" : "\n");
+#endif /* IRIX */
+
+ spinlock_init(&soft->xbow_perf_lock, "xbow_perf_lock");
+ soft->xbow_perfcnt[0].xp_perf_reg = &xbow->xb_perf_ctr_a;
+ soft->xbow_perfcnt[1].xp_perf_reg = &xbow->xb_perf_ctr_b;
+
+ /* Initialization for GBR bw allocation */
+ spinlock_init(&soft->xbow_bw_alloc_lock, "xbow_bw_alloc_lock");
+
+#define XBOW_8_BIT_PORT_BW_MAX (400 * 1000 * 1000) /* 400 MB/s */
+#define XBOW_16_BIT_PORT_BW_MAX (800 * 1000 * 1000) /* 800 MB/s */
+
+ /* Set bandwidth hiwatermark and current values */
+ for (i = 0; i < MAX_XBOW_PORTS; i++) {
+ soft->bw_hiwm[i] = XBOW_16_BIT_PORT_BW_MAX; /* for now */
+ soft->bw_cur_used[i] = 0;
+ }
+
+ /*
+ * attach the crossbow error interrupt.
+ */
+#ifdef LATER
+ dev_desc = device_desc_dup(vhdl);
+ device_desc_flags_set(dev_desc,
+ device_desc_flags_get(dev_desc) | D_INTR_ISERR);
+ device_desc_intr_name_set(dev_desc, "Crossbow error");
+
+ intr_hdl = xtalk_intr_alloc(conn, dev_desc, vhdl);
+ ASSERT(intr_hdl != NULL);
+
+ xtalk_intr_connect(intr_hdl,
+ (intr_func_t) xbow_errintr_handler,
+ (intr_arg_t) soft,
+ (xtalk_intr_setfunc_t) xbow_setwidint,
+ (void *) xbow,
+ (void *) 0);
+ device_desc_free(dev_desc);
+
+ xwidget_error_register(conn, xbow_error_handler, soft);
+
+#else
+ printk("xbow_attach: Fixme: we bypassed attaching xbow error interrupt.\n");
+#endif /* LATER */
+
+ /*
+ * Enable xbow error interrupts
+ */
+ xbow->xb_wid_control = (XB_WID_CTRL_REG_ACC_IE |
+ XB_WID_CTRL_XTALK_IE);
+
+ /*
+ * take a census of the widgets present,
+ * leaving notes at the switch vertex.
+ */
+ info = xswitch_info_new(busv);
+
+ for (port = MAX_PORT_NUM - MAX_XBOW_PORTS;
+ port < MAX_PORT_NUM; ++port) {
+ if (!xbow_link_alive(xbow, port)) {
+#if DEBUG && XBOW_DEBUG
+ printk(KERN_INFO "0x%p link %d is not alive\n",
+ busv, port);
+#endif
+ continue;
+ }
+ if (!xbow_widget_present(xbow, port)) {
+#if DEBUG && XBOW_DEBUG
+ printk(KERN_INFO "0x%p link %d is alive but no widget is present\n", busv, port);
+#endif
+ continue;
+ }
+#if DEBUG && XBOW_DEBUG
+ printk(KERN_INFO "0x%p link %d has a widget\n",
+ busv, port);
+#endif
+
+ xswitch_info_link_is_ok(info, port);
+ /*
+ * Turn some error interrupts on
+ * and turn others off. The PROM has
+ * some things turned on we don't
+ * want to see (bandwidth allocation
+ * errors for instance); so if it
+ * is not listed here, it is not on.
+ */
+ xbow->xb_link(port).link_control =
+ ( (xbow->xb_link(port).link_control
+ /*
+ * Turn off these bits; they are non-fatal,
+ * but we might want to save some statistics
+ * on the frequency of these errors.
+ * XXX FIXME XXX
+ */
+ & ~XB_CTRL_RCV_CNT_OFLOW_IE
+ & ~XB_CTRL_XMT_CNT_OFLOW_IE
+ & ~XB_CTRL_BNDWDTH_ALLOC_IE
+ & ~XB_CTRL_RCV_IE)
+ /*
+ * These are the ones we want to turn on.
+ */
+ | (XB_CTRL_ILLEGAL_DST_IE
+ | XB_CTRL_OALLOC_IBUF_IE
+ | XB_CTRL_XMT_MAX_RTRY_IE
+ | XB_CTRL_MAXREQ_TOUT_IE
+ | XB_CTRL_XMT_RTRY_IE
+ | XB_CTRL_SRC_TOUT_IE) );
+ }
+
+ xswitch_provider_register(busv, &xbow_provider);
+
+ return 0; /* attach successful */
+}
+
+/*ARGSUSED */
+int
+xbow_open(devfs_handle_t *devp, int oflag, int otyp, cred_t *credp)
+{
+ if (!_CAP_CRABLE((uint64_t)credp, CAP_DEVICE_MGT))
+ return EPERM;
+ return 0;
+
+}
+
+/*ARGSUSED */
+int
+xbow_close(devfs_handle_t dev, int oflag, int otyp, cred_t *crp)
+{
+ return 0;
+}
+
+/*ARGSUSED */
+int
+xbow_map(devfs_handle_t dev, vhandl_t *vt, off_t off, size_t len, uint prot)
+{
+ devfs_handle_t vhdl = dev_to_vhdl(dev);
+ xbow_soft_t soft = xbow_soft_get(vhdl);
+ int error;
+
+ ASSERT(soft);
+ len = ctob(btoc(len));
+ /* XXX- this ignores the offset!!! */
+ error = v_mapphys(vt, (void *) soft->base, len);
+ return error;
+}
+
+/*ARGSUSED */
+int
+xbow_unmap(devfs_handle_t dev, vhandl_t *vt)
+{
+ return 0;
+}
+
+/* This contains special-case code for grio. There are plans to make
+ * this general sometime in the future, but till then this should
+ * be good enough.
+ */
+xwidgetnum_t
+xbow_widget_num_get(devfs_handle_t dev)
+{
+ devfs_handle_t tdev;
+ char devname[MAXDEVNAME];
+ xwidget_info_t xwidget_info;
+ int i;
+#if IP27
+ cnodeid_t cnodeid = CNODEID_NONE;
+#endif
+
+ vertex_to_name(dev, devname, MAXDEVNAME);
+
+#if IP30
+ /* If there is a ".connection" edge from this vertex,
+ * then it must be "/hw/node" vertex. Return the widget
+ * number for heart: 8.
+ */
+ if (hwgraph_edge_get(dev, EDGE_LBL_CONN, &tdev) ==
+ GRAPH_SUCCESS) {
+ return ((xwidgetnum_t) 8);
+ }
+#elif IP27
+ if ((cnodeid = nodevertex_to_cnodeid(dev)) != CNODEID_NONE) {
+ ASSERT(cnodeid < maxnodes);
+ return(hub_widget_id(COMPACT_TO_NASID_NODEID(cnodeid)));
+ }
+#endif
+
+ /* If this is a pci controller vertex, traverse up using
+ * the ".." links to get to the widget.
+ */
+ if (strstr(devname, EDGE_LBL_PCI) &&
+ strstr(devname, EDGE_LBL_CONTROLLER)) {
+ tdev = dev;
+ for (i=0; i< 2; i++) {
+ if (hwgraph_edge_get(tdev,
+ HWGRAPH_EDGELBL_DOTDOT, &tdev) !=
+ GRAPH_SUCCESS)
+ return XWIDGET_NONE;
+ }
+
+ if ((xwidget_info = xwidget_info_chk(tdev)) != NULL) {
+ return (xwidget_info_id_get(xwidget_info));
+ } else {
+ return XWIDGET_NONE;
+ }
+ }
+
+ return XWIDGET_NONE;
+}
+
+int
+xbow_ioctl(devfs_handle_t dev,
+ int cmd,
+ void *arg,
+ int flag,
+ struct cred *cr,
+ int *rvalp)
+{
+ devfs_handle_t vhdl;
+ int error = 0;
+
+#if defined (DEBUG)
+ int rc;
+ devfs_handle_t conn;
+ struct xwidget_info_s *xwidget_info;
+ xbow_soft_t xbow_soft;
+#endif
+ *rvalp = 0;
+
+ vhdl = dev_to_vhdl(dev);
+#if defined (DEBUG)
+ xbow_soft = xbow_soft_get(vhdl);
+ conn = xbow_soft->conn;
+
+ xwidget_info = xwidget_info_get(conn);
+ ASSERT_ALWAYS(xwidget_info != NULL);
+
+ rc = xwidget_hwid_is_xswitch(&xwidget_info->w_hwid);
+ ASSERT_ALWAYS(rc != 0);
+#endif
+ switch (cmd) {
+#ifdef IRIX
+ case XBOWIOC_PERF_ENABLE:
+ case XBOWIOC_PERF_DISABLE:
+ {
+ struct xbow_perfarg_t xbow_perf_en;
+
+ if (!_CAP_CRABLE(cr, CAP_DEVICE_MGT)) {
+ error = EPERM;
+ break;
+ }
+ if ((flag & FWRITE) == 0) {
+ error = EBADF;
+ break;
+ }
+ if (COPYIN(arg, &xbow_perf_en, sizeof(xbow_perf_en))) {
+ error = EFAULT;
+ break;
+ }
+ if (error = xbow_enable_perf_counter(vhdl,
+ xbow_perf_en.link,
+ (cmd == XBOWIOC_PERF_DISABLE) ? 0 : xbow_perf_en.mode,
+ xbow_perf_en.counter)) {
+ error = EINVAL;
+ break;
+ }
+ break;
+ }
+#endif
+
+#ifdef IRIX
+ case XBOWIOC_PERF_GET:
+ {
+ xbow_perf_link_t *xbow_perf_cnt;
+
+ if ((flag & FREAD) == 0) {
+ error = EBADF;
+ break;
+ }
+ xbow_perf_cnt = xbow_get_perf_counters(vhdl);
+ ASSERT_ALWAYS(xbow_perf_cnt != NULL);
+
+ if (COPYOUT((void *) xbow_perf_cnt, (void *) arg,
+ MAX_XBOW_PORTS * sizeof(xbow_perf_link_t))) {
+ error = EFAULT;
+ break;
+ }
+ break;
+ }
+#endif
+
+ case XBOWIOC_LLP_ERROR_ENABLE:
+ if (!_CAP_CRABLE((uint64_t)cr, CAP_DEVICE_MGT)) {
+ error = EPERM;
+ break;
+ }
+ if ((error = xbow_enable_llp_monitor(vhdl)) != 0)
+ error = EINVAL;
+
+ break;
+
+ case XBOWIOC_LLP_ERROR_DISABLE:
+
+ if (!_CAP_CRABLE((uint64_t)cr, CAP_DEVICE_MGT)) {
+ error = EPERM;
+ break;
+ }
+ if ((error = xbow_disable_llp_monitor(vhdl)) != 0)
+ error = EINVAL;
+
+ break;
+
+#ifdef IRIX
+ case XBOWIOC_LLP_ERROR_GET:
+ {
+ xbow_link_status_t *xbow_llp_status;
+
+ if ((flag & FREAD) == 0) {
+ error = EBADF;
+ break;
+ }
+ xbow_llp_status = xbow_get_llp_status(vhdl);
+ ASSERT_ALWAYS(xbow_llp_status != NULL);
+
+ if (COPYOUT((void *) xbow_llp_status, (void *) arg,
+ MAX_XBOW_PORTS * sizeof(xbow_link_status_t))) {
+ error = EFAULT;
+ break;
+ }
+ break;
+ }
+#endif
+
+#ifdef IRIX
+ case GIOCSETBW:
+ {
+ grio_ioctl_info_t info;
+ xwidgetnum_t src_widgetnum, dest_widgetnum;
+
+ if (!cap_able(CAP_DEVICE_MGT)) {
+ error = EPERM;
+ break;
+ }
+
+ if (COPYIN(arg, &info, sizeof(grio_ioctl_info_t))) {
+ error = EFAULT;
+ break;
+ }
+#ifdef GRIO_DEBUG
+ printf("xbow:: prev_vhdl: %d next_vhdl: %d reqbw: %lld\n",
+ info.prev_vhdl, info.next_vhdl, info.reqbw);
+#endif /* GRIO_DEBUG */
+
+ src_widgetnum = xbow_widget_num_get(info.prev_vhdl);
+ dest_widgetnum = xbow_widget_num_get(info.next_vhdl);
+
+ /* Bandwidth allocation is bi-directional. Since bandwidth
+ * reservations have already been done at an earlier stage,
+ * we cannot fail here for lack of bandwidth.
+ */
+ xbow_prio_bw_alloc(dev, src_widgetnum, dest_widgetnum,
+ 0, info.reqbw);
+ xbow_prio_bw_alloc(dev, dest_widgetnum, src_widgetnum,
+ 0, info.reqbw);
+
+ break;
+ }
+
+ case GIOCRELEASEBW:
+ {
+ grio_ioctl_info_t info;
+ xwidgetnum_t src_widgetnum, dest_widgetnum;
+
+ if (!cap_able(CAP_DEVICE_MGT)) {
+ error = EPERM;
+ break;
+ }
+
+ if (COPYIN(arg, &info, sizeof(grio_ioctl_info_t))) {
+ error = EFAULT;
+ break;
+ }
+#ifdef GRIO_DEBUG
+ printf("xbow:: prev_vhdl: %d next_vhdl: %d reqbw: %lld\n",
+ info.prev_vhdl, info.next_vhdl, info.reqbw);
+#endif /* GRIO_DEBUG */
+
+ src_widgetnum = xbow_widget_num_get(info.prev_vhdl);
+ dest_widgetnum = xbow_widget_num_get(info.next_vhdl);
+
+ /* Bandwidth reservation is bi-directional. Hence, remove
+ * bandwidth reservations for both directions.
+ */
+ xbow_prio_bw_alloc(dev, src_widgetnum, dest_widgetnum,
+ info.reqbw, (-1 * info.reqbw));
+ xbow_prio_bw_alloc(dev, dest_widgetnum, src_widgetnum,
+ info.reqbw, (-1 * info.reqbw));
+
+ break;
+ }
+#endif
+
+ default:
+ break;
+
+ }
+ return error;
+}
+
+/*
+ * xbow_widget_present: See if a device is present
+ * on the specified port of this crossbow.
+ */
+int
+xbow_widget_present(xbow_t * xbow, int port)
+{
+ if ( IS_RUNNING_ON_SIMULATOR() ) {
+ if ( (port == 14) || (port == 15) ) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ return xbow->xb_link(port).link_aux_status & XB_AUX_STAT_PRESENT;
+ }
+}
+
+static int
+xbow_link_alive(xbow_t * xbow, int port)
+{
+ xbwX_stat_t xbow_linkstat;
+
+ xbow_linkstat.linkstatus = xbow->xb_link(port).link_status;
+ return (xbow_linkstat.link_alive);
+}
+
+/*
+ * xbow_widget_lookup
+ * Lookup the edges connected to the xbow specified, and
+ * retrieve the handle corresponding to the widgetnum
+ * specified.
+ * If not found, return 0.
+ */
+devfs_handle_t
+xbow_widget_lookup(devfs_handle_t vhdl,
+ int widgetnum)
+{
+ xswitch_info_t xswitch_info;
+ devfs_handle_t conn;
+
+ xswitch_info = xswitch_info_get(vhdl);
+ conn = xswitch_info_vhdl_get(xswitch_info, widgetnum);
+ return conn;
+}
+
+/*
+ * xbow_setwidint: called when xtalk
+ * is establishing or migrating our
+ * interrupt service.
+ */
+#ifdef LATER
+static void
+xbow_setwidint(xtalk_intr_t intr)
+{
+ xwidgetnum_t targ = xtalk_intr_target_get(intr);
+ iopaddr_t addr = xtalk_intr_addr_get(intr);
+ xtalk_intr_vector_t vect = xtalk_intr_vector_get(intr);
+ xbow_t *xbow = (xbow_t *) xtalk_intr_sfarg_get(intr);
+
+ xbow_intr_preset((void *) xbow, 0, targ, addr, vect);
+}
+#endif /* LATER */
+
+/*
+ * xbow_intr_preset: called during mlreset time
+ * if the platform specific code needs to route
+ * an xbow interrupt before the xtalk infrastructure
+ * is available for use.
+ *
+ * Also called from xbow_setwidint, so we don't
+ * replicate the guts of the routine.
+ *
+ * XXX- probably should be renamed xbow_wid_intr_set or
+ * something to reduce confusion.
+ */
+/*ARGSUSED3 */
+void
+xbow_intr_preset(void *which_widget,
+ int which_widget_intr,
+ xwidgetnum_t targ,
+ iopaddr_t addr,
+ xtalk_intr_vector_t vect)
+{
+ xbow_t *xbow = (xbow_t *) which_widget;
+
+ xbow->xb_wid_int_upper = ((0xFF000000 & (vect << 24)) |
+ (0x000F0000 & (targ << 16)) |
+ XTALK_ADDR_TO_UPPER(addr));
+ xbow->xb_wid_int_lower = XTALK_ADDR_TO_LOWER(addr);
+}
+
+#define XEM_ADD_STR(s) cmn_err(CE_CONT, "%s", (s))
+#define XEM_ADD_NVAR(n,v) cmn_err(CE_CONT, "\t%20s: 0x%x\n", (n), (v))
+#define XEM_ADD_VAR(v) XEM_ADD_NVAR(#v,(v))
+#define XEM_ADD_IOEF(n) if (IOERROR_FIELDVALID(ioe,n)) \
+ XEM_ADD_NVAR("ioe." #n, \
+ IOERROR_GETVALUE(ioe,n))
+
+#ifdef IRIX
+static void
+xem_add_ioe(ioerror_t *ioe)
+{
+ XEM_ADD_IOEF(errortype);
+ XEM_ADD_IOEF(widgetnum);
+ XEM_ADD_IOEF(widgetdev);
+ XEM_ADD_IOEF(srccpu);
+ XEM_ADD_IOEF(srcnode);
+ XEM_ADD_IOEF(errnode);
+ XEM_ADD_IOEF(sysioaddr);
+ XEM_ADD_IOEF(xtalkaddr);
+ XEM_ADD_IOEF(busspace);
+ XEM_ADD_IOEF(busaddr);
+ XEM_ADD_IOEF(vaddr);
+ XEM_ADD_IOEF(memaddr);
+ XEM_ADD_IOEF(epc);
+ XEM_ADD_IOEF(ef);
+}
+
+#define XEM_ADD_IOE() (xem_add_ioe(ioe))
+#endif /* IRIX */
+
+int xbow_xmit_retry_errors = 0;
+
+int
+xbow_xmit_retry_error(xbow_soft_t soft,
+ int port)
+{
+ xswitch_info_t info;
+ devfs_handle_t vhdl;
+ widget_cfg_t *wid;
+ widgetreg_t id;
+ int part;
+ int mfgr;
+
+ wid = soft->wpio[port - BASE_XBOW_PORT];
+ if (wid == NULL) {
+ /* If we can't track down a PIO
+ * pointer to our widget yet,
+ * leave our caller knowing that
+ * we are interested in this
+ * interrupt if it occurs in
+ * the future.
+ */
+ info = xswitch_info_get(soft->busv);
+ if (!info)
+ return 1;
+ vhdl = xswitch_info_vhdl_get(info, port);
+ if (vhdl == GRAPH_VERTEX_NONE)
+ return 1;
+ wid = (widget_cfg_t *) xtalk_piotrans_addr
+ (vhdl, 0, 0, sizeof *wid, 0);
+ if (!wid)
+ return 1;
+ soft->wpio[port - BASE_XBOW_PORT] = wid;
+ }
+ id = wid->w_id;
+ part = XWIDGET_PART_NUM(id);
+ mfgr = XWIDGET_MFG_NUM(id);
+
+ /* If this thing is not a Bridge,
+ * do not activate the WAR, and
+ * tell our caller we do not need
+ * to be called again.
+ */
+ if ((part != BRIDGE_WIDGET_PART_NUM) ||
+ (mfgr != BRIDGE_WIDGET_MFGR_NUM)) {
+ /* FIXME: add Xbridge to the WAR.
+ * Shouldn't hurt anything. Later need to
+ * check if we can remove this.
+ */
+ if ((part != XBRIDGE_WIDGET_PART_NUM) ||
+ (mfgr != XBRIDGE_WIDGET_MFGR_NUM))
+ return 0;
+ }
+
+ /* count how many times we
+ * have picked up after
+ * LLP Transmit problems.
+ */
+ xbow_xmit_retry_errors++;
+
+ /* rewrite the control register
+ * to fix things up.
+ */
+ wid->w_control = wid->w_control;
+ wid->w_control;
+
+ return 1;
+}
+
+/*
+ * xbow_errintr_handler will be called if the xbow
+ * sends an interrupt request to report an error.
+ */
+
+#ifdef LATER
+static void
+xbow_errintr_handler(intr_arg_t arg)
+{
+#ifdef IRIX
+ ioerror_t ioe[1];
+ xbow_soft_t soft = (xbow_soft_t) arg;
+ xbow_t *xbow = soft->base;
+ xbowreg_t wid_control;
+ xbowreg_t wid_stat;
+ xbowreg_t wid_err_cmdword;
+ xbowreg_t wid_err_upper;
+ xbowreg_t wid_err_lower;
+ w_err_cmd_word_u wid_err;
+ uint64_t wid_err_addr;
+
+ int fatal = 0;
+ int dump_ioe = 0;
+
+ wid_control = xbow->xb_wid_control;
+ wid_stat = xbow->xb_wid_stat_clr;
+ wid_err_cmdword = xbow->xb_wid_err_cmdword;
+ wid_err_upper = xbow->xb_wid_err_upper;
+ wid_err_lower = xbow->xb_wid_err_lower;
+ xbow->xb_wid_err_cmdword = 0;
+
+ wid_err_addr =
+ wid_err_lower
+ | (((iopaddr_t) wid_err_upper
+ & WIDGET_ERR_UPPER_ADDR_ONLY)
+ << 32);
+
+ if (wid_stat & XB_WID_STAT_LINK_INTR_MASK) {
+ int port;
+
+ wid_err.r = wid_err_cmdword;
+
+ for (port = MAX_PORT_NUM - MAX_XBOW_PORTS;
+ port < MAX_PORT_NUM; port++) {
+ if (wid_stat & XB_WID_STAT_LINK_INTR(port)) {
+ xb_linkregs_t *link = &(xbow->xb_link(port));
+ xbowreg_t link_control = link->link_control;
+ xbowreg_t link_status = link->link_status_clr;
+ xbowreg_t link_aux_status = link->link_aux_status;
+ xbowreg_t link_pend;
+
+ link_pend = link_status & link_control &
+ (XB_STAT_ILLEGAL_DST_ERR
+ | XB_STAT_OALLOC_IBUF_ERR
+ | XB_STAT_RCV_CNT_OFLOW_ERR
+ | XB_STAT_XMT_CNT_OFLOW_ERR
+ | XB_STAT_XMT_MAX_RTRY_ERR
+ | XB_STAT_RCV_ERR
+ | XB_STAT_XMT_RTRY_ERR
+ | XB_STAT_MAXREQ_TOUT_ERR
+ | XB_STAT_SRC_TOUT_ERR
+ );
+
+ if (link_pend & XB_STAT_ILLEGAL_DST_ERR) {
+ if (wid_err.f.sidn == port) {
+ IOERROR_INIT(ioe);
+ IOERROR_SETVALUE(ioe, widgetnum, port);
+ IOERROR_SETVALUE(ioe, xtalkaddr, wid_err_addr);
+ if (IOERROR_HANDLED ==
+ xbow_error_handler(soft,
+ IOECODE_DMA,
+ MODE_DEVERROR,
+ ioe)) {
+ link_pend &= ~XB_STAT_ILLEGAL_DST_ERR;
+ } else {
+ dump_ioe++;
+ }
+ }
+ }
+ /* Xbow/Bridge WAR:
+ * if the bridge signals an LLP Transmitter Retry,
+ * rewrite its control register.
+ * If someone else triggers this interrupt,
+ * ignore (and disable) the interrupt.
+ */
+ if (link_pend & XB_STAT_XMT_RTRY_ERR) {
+ if (!xbow_xmit_retry_error(soft, port)) {
+ link_control &= ~XB_CTRL_XMT_RTRY_IE;
+ link->link_control = link_control;
+ link->link_control; /* stall until written */
+ }
+ link_pend &= ~XB_STAT_XMT_RTRY_ERR;
+ }
+ if (link_pend) {
+ devfs_handle_t xwidget_vhdl;
+ char *xwidget_name;
+
+ /* Get the widget name corresponding to the current
+ * xbow link.
+ */
+ xwidget_vhdl = xbow_widget_lookup(soft->busv,port);
+ xwidget_name = xwidget_name_get(xwidget_vhdl);
+
+#ifdef IRIX
+ cmn_err(CE_CONT,
+ "%s port %X[%s] XIO Bus Error",
+ soft->name, port, xwidget_name);
+ if (link_status & XB_STAT_MULTI_ERR)
+ XEM_ADD_STR("\tMultiple Errors\n");
+ if (link_status & XB_STAT_ILLEGAL_DST_ERR)
+ XEM_ADD_STR("\tInvalid Packet Destination\n");
+ if (link_status & XB_STAT_OALLOC_IBUF_ERR)
+ XEM_ADD_STR("\tInput Overallocation Error\n");
+ if (link_status & XB_STAT_RCV_CNT_OFLOW_ERR)
+ XEM_ADD_STR("\tLLP receive error counter overflow\n");
+ if (link_status & XB_STAT_XMT_CNT_OFLOW_ERR)
+ XEM_ADD_STR("\tLLP transmit retry counter overflow\n");
+ if (link_status & XB_STAT_XMT_MAX_RTRY_ERR)
+ XEM_ADD_STR("\tLLP Max Transmitter Retry\n");
+ if (link_status & XB_STAT_RCV_ERR)
+ XEM_ADD_STR("\tLLP Receiver error\n");
+ if (link_status & XB_STAT_XMT_RTRY_ERR)
+ XEM_ADD_STR("\tLLP Transmitter Retry\n");
+ if (link_status & XB_STAT_MAXREQ_TOUT_ERR)
+ XEM_ADD_STR("\tMaximum Request Timeout\n");
+ if (link_status & XB_STAT_SRC_TOUT_ERR)
+ XEM_ADD_STR("\tSource Timeout Error\n");
+#endif
+
+ {
+ int other_port;
+
+ for (other_port = 8; other_port < 16; ++other_port) {
+ if (link_aux_status & (1 << other_port)) {
+ /* XXX- need to go to "other_port"
+ * and clean up after the timeout?
+ */
+ XEM_ADD_VAR(other_port);
+ }
+ }
+ }
+
+#if !DEBUG
+ if (kdebug) {
+#endif
+ XEM_ADD_VAR(link_control);
+ XEM_ADD_VAR(link_status);
+ XEM_ADD_VAR(link_aux_status);
+
+ if (dump_ioe) {
+ XEM_ADD_IOE();
+ dump_ioe = 0;
+ }
+#if !DEBUG
+ }
+#endif
+ fatal++;
+ }
+ }
+ }
+ }
+ if (wid_stat & wid_control & XB_WID_STAT_WIDGET0_INTR) {
+ /* we have a "widget zero" problem */
+
+ if (wid_stat & (XB_WID_STAT_MULTI_ERR
+ | XB_WID_STAT_XTALK_ERR
+ | XB_WID_STAT_REG_ACC_ERR)) {
+
+ cmn_err(CE_CONT,
+ "%s Port 0 XIO Bus Error",
+ soft->name);
+ if (wid_stat & XB_WID_STAT_MULTI_ERR)
+ XEM_ADD_STR("\tMultiple Error\n");
+ if (wid_stat & XB_WID_STAT_XTALK_ERR)
+ XEM_ADD_STR("\tXIO Error\n");
+ if (wid_stat & XB_WID_STAT_REG_ACC_ERR)
+ XEM_ADD_STR("\tRegister Access Error\n");
+
+ fatal++;
+ }
+ }
+ if (fatal) {
+ XEM_ADD_VAR(wid_stat);
+ XEM_ADD_VAR(wid_control);
+ XEM_ADD_VAR(wid_err_cmdword);
+ XEM_ADD_VAR(wid_err_upper);
+ XEM_ADD_VAR(wid_err_lower);
+ XEM_ADD_VAR(wid_err_addr);
+ cmn_err_tag(8, CE_PANIC, "XIO Bus Error");
+ }
+#endif
+}
+#endif /* LATER */
+
+/*
+ * XBOW ERROR Handling routines.
+ * These get invoked as part of walking down the error handling path
+ * from hub/heart towards the I/O device that caused the error.
+ */
+
+/*
+ * xbow_error_handler
+ * XBow error handling dispatch routine.
+ * This is the primary interface used by external world to invoke
+ * in case of an error related to a xbow.
+ * Only functionality in this layer is to identify the widget handle
+ * given the widgetnum. Otherwise, xbow does not gathers any error
+ * data.
+ */
+
+#ifdef LATER
+static int
+xbow_error_handler(
+ void *einfo,
+ int error_code,
+ ioerror_mode_t mode,
+ ioerror_t *ioerror)
+{
+#ifdef IRIX
+ int retval = IOERROR_WIDGETLEVEL;
+
+ xbow_soft_t soft = (xbow_soft_t) einfo;
+ int port;
+ devfs_handle_t conn;
+ devfs_handle_t busv;
+
+ xbow_t *xbow = soft->base;
+ xbowreg_t wid_stat;
+ xbowreg_t wid_err_cmdword;
+ xbowreg_t wid_err_upper;
+ xbowreg_t wid_err_lower;
+ uint64_t wid_err_addr;
+
+ xb_linkregs_t *link;
+ xbowreg_t link_control;
+ xbowreg_t link_status;
+ xbowreg_t link_aux_status;
+
+ ASSERT(soft != 0);
+ busv = soft->busv;
+
+#if DEBUG && ERROR_DEBUG
+ cmn_err(CE_CONT, "%s: xbow_error_handler\n", soft->name, busv);
+#endif
+
+ port = IOERROR_GETVALUE(ioerror, widgetnum);
+
+ if (port == 0) {
+ /* error during access to xbow:
+ * do NOT attempt to access xbow regs.
+ */
+ if (mode == MODE_DEVPROBE)
+ return IOERROR_HANDLED;
+
+ if (error_code & IOECODE_DMA) {
+ cmn_err(CE_ALERT,
+ "DMA error blamed on Crossbow at %s\n"
+ "\tbut Crosbow never initiates DMA!",
+ soft->name);
+ }
+ if (error_code & IOECODE_PIO) {
+ cmn_err(CE_ALERT,
+ "PIO Error on XIO Bus %s\n"
+ "\tattempting to access XIO controller\n"
+ "\twith offset 0x%X",
+ soft->name,
+ IOERROR_GETVALUE(ioerror, xtalkaddr));
+ }
+ /* caller will dump contents of ioerror
+ * in DEBUG and kdebug kernels.
+ */
+
+ return retval;
+ }
+ /*
+ * error not on port zero:
+ * safe to read xbow registers.
+ */
+ wid_stat = xbow->xb_wid_stat;
+ wid_err_cmdword = xbow->xb_wid_err_cmdword;
+ wid_err_upper = xbow->xb_wid_err_upper;
+ wid_err_lower = xbow->xb_wid_err_lower;
+
+ wid_err_addr =
+ wid_err_lower
+ | (((iopaddr_t) wid_err_upper
+ & WIDGET_ERR_UPPER_ADDR_ONLY)
+ << 32);
+
+ if ((port < BASE_XBOW_PORT) ||
+ (port >= MAX_PORT_NUM)) {
+
+ if (mode == MODE_DEVPROBE)
+ return IOERROR_HANDLED;
+
+ if (error_code & IOECODE_DMA) {
+ cmn_err(CE_ALERT,
+ "DMA error blamed on XIO port at %s/%d\n"
+ "\tbut Crossbow does not support that port",
+ soft->name, port);
+ }
+ if (error_code & IOECODE_PIO) {
+ cmn_err(CE_ALERT,
+ "PIO Error on XIO Bus %s\n"
+ "\tattempting to access XIO port %d\n"
+ "\t(which Crossbow does not support)"
+ "\twith offset 0x%X",
+ soft->name, port,
+ IOERROR_GETVALUE(ioerror, xtalkaddr));
+ }
+#if !DEBUG
+ if (kdebug) {
+#endif
+ XEM_ADD_STR("Raw status values for Crossbow:\n");
+ XEM_ADD_VAR(wid_stat);
+ XEM_ADD_VAR(wid_err_cmdword);
+ XEM_ADD_VAR(wid_err_upper);
+ XEM_ADD_VAR(wid_err_lower);
+ XEM_ADD_VAR(wid_err_addr);
+#if !DEBUG
+ }
+#endif
+
+ /* caller will dump contents of ioerror
+ * in DEBUG and kdebug kernels.
+ */
+
+ return retval;
+ }
+ /* access to valid port:
+ * ok to check port status.
+ */
+
+ link = &(xbow->xb_link(port));
+ link_control = link->link_control;
+ link_status = link->link_status;
+ link_aux_status = link->link_aux_status;
+
+ /* Check that there is something present
+ * in that XIO port.
+ */
+ if (!(link_aux_status & XB_AUX_STAT_PRESENT)) {
+ /* nobody connected. */
+ if (mode == MODE_DEVPROBE)
+ return IOERROR_HANDLED;
+
+ if (error_code & IOECODE_DMA) {
+ cmn_err(CE_ALERT,
+ "DMA error blamed on XIO port at %s/%d\n"
+ "\tbut there is no device connected there.",
+ soft->name, port);
+ }
+ if (error_code & IOECODE_PIO) {
+ cmn_err(CE_ALERT,
+ "PIO Error on XIO Bus %s\n"
+ "\tattempting to access XIO port %d\n"
+ "\t(which has no device connected)"
+ "\twith offset 0x%X",
+ soft->name, port,
+ IOERROR_GETVALUE(ioerror, xtalkaddr));
+ }
+#if !DEBUG
+ if (kdebug) {
+#endif
+ XEM_ADD_STR("Raw status values for Crossbow:\n");
+ XEM_ADD_VAR(wid_stat);
+ XEM_ADD_VAR(wid_err_cmdword);
+ XEM_ADD_VAR(wid_err_upper);
+ XEM_ADD_VAR(wid_err_lower);
+ XEM_ADD_VAR(wid_err_addr);
+ XEM_ADD_VAR(port);
+ XEM_ADD_VAR(link_control);
+ XEM_ADD_VAR(link_status);
+ XEM_ADD_VAR(link_aux_status);
+#if !DEBUG
+ }
+#endif
+ return retval;
+
+ }
+ /* Check that the link is alive.
+ */
+ if (!(link_status & XB_STAT_LINKALIVE)) {
+ /* nobody connected. */
+ if (mode == MODE_DEVPROBE)
+ return IOERROR_HANDLED;
+
+ cmn_err(CE_ALERT,
+ "%s%sError on XIO Bus %s port %d",
+ (error_code & IOECODE_DMA) ? "DMA " : "",
+ (error_code & IOECODE_PIO) ? "PIO " : "",
+ soft->name, port);
+
+ if ((error_code & IOECODE_PIO) &&
+ (IOERROR_FIELDVALID(ioerror, xtalkaddr))) {
+ cmn_err(CE_CONT,
+ "\tAccess attempted to offset 0x%X\n",
+ IOERROR_GETVALUE(ioerror, xtalkaddr));
+ }
+ if (link_aux_status & XB_AUX_LINKFAIL_RST_BAD)
+ XEM_ADD_STR("\tLink never came out of reset\n");
+ else
+ XEM_ADD_STR("\tLink failed while transferring data\n");
+
+ }
+ /* get the connection point for the widget
+ * involved in this error; if it exists and
+ * is not our connectpoint, cycle back through
+ * xtalk_error_handler to deliver control to
+ * the proper handler (or to report a generic
+ * crosstalk error).
+ *
+ * If the downstream handler won't handle
+ * the problem, we let our upstream caller
+ * deal with it, after (in DEBUG and kdebug
+ * kernels) dumping the xbow state for this
+ * port.
+ */
+ conn = xbow_widget_lookup(busv, port);
+ if ((conn != GRAPH_VERTEX_NONE) &&
+ (conn != soft->conn)) {
+ retval = xtalk_error_handler(conn, error_code, mode, ioerror);
+ if (retval == IOERROR_HANDLED)
+ return IOERROR_HANDLED;
+ }
+ if (mode == MODE_DEVPROBE)
+ return IOERROR_HANDLED;
+
+ if (retval == IOERROR_UNHANDLED) {
+ retval = IOERROR_PANIC;
+
+ cmn_err(CE_ALERT,
+ "%s%sError on XIO Bus %s port %d",
+ (error_code & IOECODE_DMA) ? "DMA " : "",
+ (error_code & IOECODE_PIO) ? "PIO " : "",
+ soft->name, port);
+
+ if ((error_code & IOECODE_PIO) &&
+ (IOERROR_FIELDVALID(ioerror, xtalkaddr))) {
+ cmn_err(CE_CONT,
+ "\tAccess attempted to offset 0x%X\n",
+ IOERROR_GETVALUE(ioerror, xtalkaddr));
+ }
+ }
+
+#if !DEBUG
+ if (kdebug) {
+#endif
+ XEM_ADD_STR("Raw status values for Crossbow:\n");
+ XEM_ADD_VAR(wid_stat);
+ XEM_ADD_VAR(wid_err_cmdword);
+ XEM_ADD_VAR(wid_err_upper);
+ XEM_ADD_VAR(wid_err_lower);
+ XEM_ADD_VAR(wid_err_addr);
+ XEM_ADD_VAR(port);
+ XEM_ADD_VAR(link_control);
+ XEM_ADD_VAR(link_status);
+ XEM_ADD_VAR(link_aux_status);
+#if !DEBUG
+ }
+#endif
+ /* caller will dump raw ioerror data
+ * in DEBUG and kdebug kernels.
+ */
+
+ return retval;
+#endif /* IRIX */
+}
+
+#endif /* LATER */
+
+void
+xbow_update_perf_counters(devfs_handle_t vhdl)
+{
+ xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
+ xbow_perf_t *xbow_perf = xbow_soft->xbow_perfcnt;
+ xbow_perf_link_t *xbow_plink = xbow_soft->xbow_perflink;
+ xbow_perfcount_t perf_reg;
+ int link, s, i;
+
+ for (i = 0; i < XBOW_PERF_COUNTERS; i++, xbow_perf++) {
+ if (xbow_perf->xp_mode == XBOW_MONITOR_NONE)
+ continue;
+
+ s = mutex_spinlock(&xbow_soft->xbow_perf_lock);
+
+ perf_reg.xb_counter_val = *(xbowreg_t *) xbow_perf->xp_perf_reg;
+
+ link = perf_reg.xb_perf.link_select;
+
+ (xbow_plink + link)->xlp_cumulative[xbow_perf->xp_curmode] +=
+ ((perf_reg.xb_perf.count - xbow_perf->xp_current) & XBOW_COUNTER_MASK);
+ xbow_perf->xp_current = perf_reg.xb_perf.count;
+
+ mutex_spinunlock(&xbow_soft->xbow_perf_lock, s);
+ }
+ /* Do port /mode multiplexing here */
+
+#ifdef IRIX
+ (void) timeout(xbow_update_perf_counters,
+ (void *) (__psunsigned_t) vhdl, XBOW_PERF_TIMEOUT);
+#endif
+
+}
+
+xbow_perf_link_t *
+xbow_get_perf_counters(devfs_handle_t vhdl)
+{
+ xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
+ xbow_perf_link_t *xbow_perf_link = xbow_soft->xbow_perflink;
+
+ return xbow_perf_link;
+}
+
+int
+xbow_enable_perf_counter(devfs_handle_t vhdl, int link, int mode, int counter)
+{
+ xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
+ xbow_perf_t *xbow_perf = xbow_soft->xbow_perfcnt;
+ xbow_linkctrl_t xbow_link_ctrl;
+ xbow_t *xbow = xbow_soft->base;
+ xbow_perfcount_t perf_reg;
+ int s, i;
+
+ link -= BASE_XBOW_PORT;
+ if ((link < 0) || (link >= MAX_XBOW_PORTS))
+ return -1;
+
+ if ((mode < XBOW_MONITOR_NONE) || (mode > XBOW_MONITOR_DEST_LINK))
+ return -1;
+
+ if ((counter < 0) || (counter >= XBOW_PERF_COUNTERS))
+ return -1;
+
+ s = mutex_spinlock(&xbow_soft->xbow_perf_lock);
+
+ if ((xbow_perf + counter)->xp_mode && mode) {
+ mutex_spinunlock(&xbow_soft->xbow_perf_lock, s);
+ return -1;
+ }
+ for (i = 0; i < XBOW_PERF_COUNTERS; i++) {
+ if (i == counter)
+ continue;
+ if (((xbow_perf + i)->xp_link == link) &&
+ ((xbow_perf + i)->xp_mode)) {
+ mutex_spinunlock(&xbow_soft->xbow_perf_lock, s);
+ return -1;
+ }
+ }
+ xbow_perf += counter;
+
+ xbow_perf->xp_curlink = xbow_perf->xp_link = link;
+ xbow_perf->xp_curmode = xbow_perf->xp_mode = mode;
+
+ xbow_link_ctrl.xbl_ctrlword = xbow->xb_link_raw[link].link_control;
+ xbow_link_ctrl.xb_linkcontrol.perf_mode = mode;
+ xbow->xb_link_raw[link].link_control = xbow_link_ctrl.xbl_ctrlword;
+
+ perf_reg.xb_counter_val = *(xbowreg_t *) xbow_perf->xp_perf_reg;
+ perf_reg.xb_perf.link_select = link;
+ *(xbowreg_t *) xbow_perf->xp_perf_reg = perf_reg.xb_counter_val;
+ xbow_perf->xp_current = perf_reg.xb_perf.count;
+
+#ifdef IRIX
+ (void) timeout(xbow_update_perf_counters,
+ (void *) (__psunsigned_t) vhdl, XBOW_PERF_TIMEOUT);
+#endif
+
+ mutex_spinunlock(&xbow_soft->xbow_perf_lock, s);
+
+ return 0;
+}
+
+xbow_link_status_t *
+xbow_get_llp_status(devfs_handle_t vhdl)
+{
+ xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
+ xbow_link_status_t *xbow_llp_status = xbow_soft->xbow_link_status;
+
+ return xbow_llp_status;
+}
+
+void
+xbow_update_llp_status(devfs_handle_t vhdl)
+{
+ xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
+ xbow_link_status_t *xbow_llp_status = xbow_soft->xbow_link_status;
+ xbow_t *xbow;
+ xbwX_stat_t lnk_sts;
+ xbow_aux_link_status_t aux_sts;
+ int link;
+ devfs_handle_t xwidget_vhdl;
+ char *xwidget_name;
+
+ xbow = (xbow_t *) xbow_soft->base;
+ for (link = 0; link < MAX_XBOW_PORTS; link++, xbow_llp_status++) {
+ /* Get the widget name corresponding the current link.
+ * Note : 0 <= link < MAX_XBOW_PORTS(8).
+ * BASE_XBOW_PORT(0x8) <= xwidget number < MAX_PORT_NUM (0x10)
+ */
+ xwidget_vhdl = xbow_widget_lookup(xbow_soft->busv,link+BASE_XBOW_PORT);
+ xwidget_name = xwidget_name_get(xwidget_vhdl);
+ aux_sts.aux_linkstatus
+ = xbow->xb_link_raw[link].link_aux_status;
+ lnk_sts.linkstatus = xbow->xb_link_raw[link].link_status_clr;
+
+ if (lnk_sts.link_alive == 0)
+ continue;
+
+ xbow_llp_status->rx_err_count +=
+ aux_sts.xb_aux_linkstatus.rx_err_cnt;
+
+ xbow_llp_status->tx_retry_count +=
+ aux_sts.xb_aux_linkstatus.tx_retry_cnt;
+
+ if (lnk_sts.linkstatus & ~(XB_STAT_RCV_ERR | XB_STAT_XMT_RTRY_ERR | XB_STAT_LINKALIVE)) {
+#ifdef IRIX
+ cmn_err(CE_WARN, "link %d[%s]: bad status 0x%x\n",
+ link, xwidget_name, lnk_sts.linkstatus);
+#endif
+ }
+ }
+#ifdef IRIX
+ if (xbow_soft->link_monitor)
+ (void) timeout(xbow_update_llp_status,
+ (void *) (__psunsigned_t) vhdl, XBOW_STATS_TIMEOUT);
+#endif
+}
+
+int
+xbow_disable_llp_monitor(devfs_handle_t vhdl)
+{
+ xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
+ int port;
+
+ for (port = 0; port < MAX_XBOW_PORTS; port++) {
+ xbow_soft->xbow_link_status[port].rx_err_count = 0;
+ xbow_soft->xbow_link_status[port].tx_retry_count = 0;
+ }
+
+ xbow_soft->link_monitor = 0;
+ return 0;
+}
+
+int
+xbow_enable_llp_monitor(devfs_handle_t vhdl)
+{
+ xbow_soft_t xbow_soft = xbow_soft_get(vhdl);
+
+#ifdef IRIX
+ (void) timeout(xbow_update_llp_status,
+ (void *) (__psunsigned_t) vhdl, XBOW_STATS_TIMEOUT);
+#endif
+ xbow_soft->link_monitor = 1;
+ return 0;
+}
+
+
+int
+xbow_reset_link(devfs_handle_t xconn_vhdl)
+{
+ xwidget_info_t widget_info;
+ xwidgetnum_t port;
+ xbow_t *xbow;
+ xbowreg_t ctrl;
+ xbwX_stat_t stat;
+ unsigned itick;
+ unsigned dtick;
+ static int ticks_per_ms = 0;
+
+ if (!ticks_per_ms) {
+ itick = get_timestamp();
+ us_delay(1000);
+ ticks_per_ms = get_timestamp() - itick;
+ }
+ widget_info = xwidget_info_get(xconn_vhdl);
+ port = xwidget_info_id_get(widget_info);
+
+#ifdef XBOW_K1PTR /* defined if we only have one xbow ... */
+ xbow = XBOW_K1PTR;
+#else
+ {
+ devfs_handle_t xbow_vhdl;
+ xbow_soft_t xbow_soft;
+
+ hwgraph_traverse(xconn_vhdl, ".master/xtalk/0/xbow", &xbow_vhdl);
+ xbow_soft = xbow_soft_get(xbow_vhdl);
+ xbow = xbow_soft->base;
+ }
+#endif
+
+ /*
+ * This requires three PIOs (reset the link, check for the
+ * reset, restore the control register for the link) plus
+ * 10us to wait for the reset. We allow up to 1ms for the
+ * widget to come out of reset before giving up and
+ * returning a failure.
+ */
+ ctrl = xbow->xb_link(port).link_control;
+ xbow->xb_link(port).link_reset = 0;
+ itick = get_timestamp();
+ while (1) {
+ stat.linkstatus = xbow->xb_link(port).link_status;
+ if (stat.link_alive)
+ break;
+ dtick = get_timestamp() - itick;
+ if (dtick > ticks_per_ms) {
+ return -1; /* never came out of reset */
+ }
+ DELAY(2); /* don't beat on link_status */
+ }
+ xbow->xb_link(port).link_control = ctrl;
+ return 0;
+}
+
+/*
+ * Dump xbow registers.
+ * input parameter is either a pointer to
+ * the xbow chip or the vertex handle for
+ * an xbow vertex.
+ */
+void
+idbg_xbowregs(int64_t regs)
+{
+ xbow_t *xbow;
+ int i;
+ xb_linkregs_t *link;
+
+#ifdef IRIX
+ if (dev_is_vertex((devfs_handle_t) regs)) {
+ devfs_handle_t vhdl = (devfs_handle_t) regs;
+ xbow_soft_t soft = xbow_soft_get(vhdl);
+
+ xbow = soft->base;
+ } else
+#endif
+ {
+ xbow = (xbow_t *) regs;
+ }
+
+#ifdef IRIX
+ qprintf("Printing xbow registers starting at 0x%x\n", xbow);
+ qprintf("wid %x status %x erruppr %x errlower %x control %x timeout %x\n",
+ xbow->xb_wid_id, xbow->xb_wid_stat, xbow->xb_wid_err_upper,
+ xbow->xb_wid_err_lower, xbow->xb_wid_control,
+ xbow->xb_wid_req_timeout);
+ qprintf("intr uppr %x lower %x errcmd %x llp ctrl %x arb_reload %x\n",
+ xbow->xb_wid_int_upper, xbow->xb_wid_int_lower,
+ xbow->xb_wid_err_cmdword, xbow->xb_wid_llp,
+ xbow->xb_wid_arb_reload);
+#endif
+
+ for (i = 8; i <= 0xf; i++) {
+ link = &xbow->xb_link(i);
+#ifdef IRIX
+ qprintf("Link %d registers\n", i);
+ qprintf("\tctrl %x stat %x arbuppr %x arblowr %x auxstat %x\n",
+ link->link_control, link->link_status,
+ link->link_arb_upper, link->link_arb_lower,
+ link->link_aux_status);
+#endif
+ }
+}
+
+
+#define XBOW_ARB_RELOAD_TICKS 25
+ /* granularity: 4 MB/s, max: 124 MB/s */
+#define GRANULARITY ((100 * 1000000) / XBOW_ARB_RELOAD_TICKS)
+
+#define XBOW_BYTES_TO_GBR(BYTES_per_s) (int) (BYTES_per_s / GRANULARITY)
+
+#define XBOW_GBR_TO_BYTES(cnt) (bandwidth_t) ((cnt) * GRANULARITY)
+
+#define CEILING_BYTES_TO_GBR(gbr, bytes_per_sec) \
+ ((XBOW_GBR_TO_BYTES(gbr) < bytes_per_sec) ? gbr+1 : gbr)
+
+#define XBOW_ARB_GBR_MAX 31
+
+#define ABS(x) ((x > 0) ? (x) : (-1 * x))
+ /* absolute value */
+
+int
+xbow_bytes_to_gbr(bandwidth_t old_bytes_per_sec, bandwidth_t bytes_per_sec)
+{
+ int gbr_granted;
+ int new_total_gbr;
+ int change_gbr;
+ bandwidth_t new_total_bw;
+
+#ifdef GRIO_DEBUG
+ printf("xbow_bytes_to_gbr: old_bytes_per_sec %lld bytes_per_sec %lld\n",
+ old_bytes_per_sec, bytes_per_sec);
+#endif /* GRIO_DEBUG */
+
+ gbr_granted = CEILING_BYTES_TO_GBR((XBOW_BYTES_TO_GBR(old_bytes_per_sec)),
+ old_bytes_per_sec);
+ new_total_bw = old_bytes_per_sec + bytes_per_sec;
+ new_total_gbr = CEILING_BYTES_TO_GBR((XBOW_BYTES_TO_GBR(new_total_bw)),
+ new_total_bw);
+
+ change_gbr = new_total_gbr - gbr_granted;
+
+#ifdef GRIO_DEBUG
+ printf("xbow_bytes_to_gbr: gbr_granted %d new_total_gbr %d change_gbr %d\n",
+ gbr_granted, new_total_gbr, change_gbr);
+#endif /* GRIO_DEBUG */
+
+ return (change_gbr);
+}
+
+/* Conversion from GBR to bytes */
+bandwidth_t
+xbow_gbr_to_bytes(int gbr)
+{
+ return (XBOW_GBR_TO_BYTES(gbr));
+}
+
+/* Given the vhdl for the desired xbow, the src and dest. widget ids
+ * and the req_bw value, this xbow driver entry point accesses the
+ * xbow registers and allocates the desired bandwidth if available.
+ *
+ * If bandwidth allocation is successful, return success else return failure.
+ */
+int
+xbow_prio_bw_alloc(devfs_handle_t vhdl,
+ xwidgetnum_t src_wid,
+ xwidgetnum_t dest_wid,
+ unsigned long long old_alloc_bw,
+ unsigned long long req_bw)
+{
+ xbow_soft_t soft = xbow_soft_get(vhdl);
+ volatile xbowreg_t *xreg;
+ xbowreg_t mask;
+ int s;
+ int error = 0;
+ bandwidth_t old_bw_BYTES, req_bw_BYTES;
+ xbowreg_t old_xreg;
+ int old_bw_GBR, req_bw_GBR, new_bw_GBR;
+
+#ifdef GRIO_DEBUG
+ printf("xbow_prio_bw_alloc: vhdl %d src_wid %d dest_wid %d req_bw %lld\n",
+ (int) vhdl, (int) src_wid, (int) dest_wid, req_bw);
+#endif
+
+ ASSERT(XBOW_WIDGET_IS_VALID(src_wid));
+ ASSERT(XBOW_WIDGET_IS_VALID(dest_wid));
+
+ s = mutex_spinlock(&soft->xbow_bw_alloc_lock);
+
+ /* Get pointer to the correct register */
+ xreg = XBOW_PRIO_ARBREG_PTR(soft->base, dest_wid, src_wid);
+
+ /* Get mask for GBR count value */
+ mask = XB_ARB_GBR_MSK << XB_ARB_GBR_SHFT(src_wid);
+
+ req_bw_GBR = xbow_bytes_to_gbr(old_alloc_bw, req_bw);
+ req_bw_BYTES = (req_bw_GBR < 0) ? (-1 * xbow_gbr_to_bytes(ABS(req_bw_GBR)))
+ : xbow_gbr_to_bytes(req_bw_GBR);
+
+#ifdef GRIO_DEBUG
+ printf("req_bw %lld req_bw_BYTES %lld req_bw_GBR %d\n",
+ req_bw, req_bw_BYTES, req_bw_GBR);
+#endif /* GRIO_DEBUG */
+
+ old_bw_BYTES = soft->bw_cur_used[(int) dest_wid - MAX_XBOW_PORTS];
+ old_xreg = *xreg;
+ old_bw_GBR = (((*xreg) & mask) >> XB_ARB_GBR_SHFT(src_wid));
+
+#ifdef GRIO_DEBUG
+ ASSERT(XBOW_BYTES_TO_GBR(old_bw_BYTES) == old_bw_GBR);
+
+ printf("old_bw_BYTES %lld old_bw_GBR %d\n", old_bw_BYTES, old_bw_GBR);
+
+ printf("req_bw_BYTES %lld old_bw_BYTES %lld soft->bw_hiwm %lld\n",
+ req_bw_BYTES, old_bw_BYTES,
+ soft->bw_hiwm[(int) dest_wid - MAX_XBOW_PORTS]);
+
+#endif /* GRIO_DEBUG */
+
+ /* Accept the request only if we don't exceed the destination
+ * port HIWATER_MARK *AND* the max. link GBR arbitration count
+ */
+ if (((old_bw_BYTES + req_bw_BYTES) <=
+ soft->bw_hiwm[(int) dest_wid - MAX_XBOW_PORTS]) &&
+ (req_bw_GBR + old_bw_GBR <= XBOW_ARB_GBR_MAX)) {
+
+ new_bw_GBR = (old_bw_GBR + req_bw_GBR);
+
+ /* Set this in the xbow link register */
+ *xreg = (old_xreg & ~mask) | \
+ (new_bw_GBR << XB_ARB_GBR_SHFT(src_wid) & mask);
+
+ soft->bw_cur_used[(int) dest_wid - MAX_XBOW_PORTS] =
+ xbow_gbr_to_bytes(new_bw_GBR);
+ } else {
+ error = 1;
+ }
+
+ mutex_spinunlock(&soft->xbow_bw_alloc_lock, s);
+
+ return (error);
+}
diff --git a/arch/ia64/sn/io/xswitch.c b/arch/ia64/sn/io/xswitch.c
new file mode 100644
index 000000000..7255650da
--- /dev/null
+++ b/arch/ia64/sn/io/xswitch.c
@@ -0,0 +1,267 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iobus.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/xtalk/xtalk.h>
+#include <asm/sn/xtalk/xswitch.h>
+#include <asm/sn/xtalk/xwidget.h>
+#include <asm/sn/xtalk/xtalk_private.h>
+
+#define NEW(ptr) (ptr = kmalloc(sizeof (*(ptr)), GFP_KERNEL))
+#define DEL(ptr) (kfree(ptr))
+
+int xswitch_devflag = D_MP;
+
+/*
+ * This file provides generic support for Crosstalk
+ * Switches, in a way that insulates crosstalk providers
+ * from specifics about the switch chips being used.
+ */
+
+#include <asm/sn/xtalk/xbow.h>
+#define DEV_FUNC(dev,func) xbow_##func
+
+#if !defined(DEV_FUNC)
+/*
+ * There is more than one possible provider
+ * for this platform. We need to examine the
+ * master vertex of the current vertex for
+ * a provider function structure, and indirect
+ * through the appropriately named member.
+ */
+#define DEV_FUNC(dev,func) xwidget_to_provider_fns(dev)->func
+
+static xswitch_provider_t *
+xwidget_to_provider_fns(devfs_handle_t xconn)
+{
+ devfs_handle_t busv;
+ xswitch_info_t xswitch_info;
+ xswitch_provider_t provider_fns;
+
+ busv = hwgraph_connectpt_get(xconn_vhdl);
+ ASSERT(busv != GRAPH_VERTEX_NONE);
+
+ xswitch_info = xswitch_info_get(busv);
+ ASSERT(xswitch_info != NULL);
+
+ provider_fns = xswitch_info->xswitch_fns;
+ ASSERT(provider_fns != NULL);
+
+ return provider_fns;
+}
+#endif
+
+#define XSWITCH_CENSUS_BIT(port) (1<<(port))
+#define XSWITCH_CENSUS_PORT_MIN (0x0)
+#define XSWITCH_CENSUS_PORT_MAX (0xF)
+#define XSWITCH_CENSUS_PORTS (0x10)
+#define XSWITCH_WIDGET_PRESENT(infop,port) ((infop)->census & XSWITCH_CENSUS_BIT(port))
+
+static char xswitch_info_fingerprint[] = "xswitch_info";
+
+struct xswitch_info_s {
+ char *fingerprint;
+ unsigned census;
+ devfs_handle_t vhdl[XSWITCH_CENSUS_PORTS];
+ devfs_handle_t master_vhdl[XSWITCH_CENSUS_PORTS];
+ xswitch_provider_t *xswitch_fns;
+};
+
+xswitch_info_t
+xswitch_info_get(devfs_handle_t xwidget)
+{
+ xswitch_info_t xswitch_info;
+
+ xswitch_info = (xswitch_info_t)
+ hwgraph_fastinfo_get(xwidget);
+#ifdef IRIX
+ if ((xswitch_info != NULL) &&
+ (xswitch_info->fingerprint != xswitch_info_fingerprint))
+ cmn_err(CE_PANIC, "%v xswitch_info_get bad fingerprint", xwidget);
+#endif
+
+ printk("xswitch_info_get: xwidget 0x%p xswitch_info 0x%p\n", xwidget, xswitch_info);
+
+ return (xswitch_info);
+}
+
+void
+xswitch_info_vhdl_set(xswitch_info_t xswitch_info,
+ xwidgetnum_t port,
+ devfs_handle_t xwidget)
+{
+#if XSWITCH_CENSUS_PORT_MIN
+ if (port < XSWITCH_CENSUS_PORT_MIN)
+ return;
+#endif
+ if (port > XSWITCH_CENSUS_PORT_MAX)
+ return;
+
+ xswitch_info->vhdl[port - XSWITCH_CENSUS_PORT_MIN] = xwidget;
+}
+
+devfs_handle_t
+xswitch_info_vhdl_get(xswitch_info_t xswitch_info,
+ xwidgetnum_t port)
+{
+#ifdef IRIX
+ if (xswitch_info == NULL)
+ cmn_err(CE_PANIC, "xswitch_info_vhdl_get: null xswitch_info");
+#endif
+
+#if XSWITCH_CENSUS_PORT_MIN
+ if (port < XSWITCH_CENSUS_PORT_MIN)
+ return GRAPH_VERTEX_NONE;
+#endif
+ if (port > XSWITCH_CENSUS_PORT_MAX)
+ return GRAPH_VERTEX_NONE;
+
+ return xswitch_info->vhdl[port - XSWITCH_CENSUS_PORT_MIN];
+}
+
+/*
+ * Some systems may allow for multiple switch masters. On such systems,
+ * we assign a master for each port on the switch. These interfaces
+ * establish and retrieve that assignment.
+ */
+void
+xswitch_info_master_assignment_set(xswitch_info_t xswitch_info,
+ xwidgetnum_t port,
+ devfs_handle_t master_vhdl)
+{
+#if XSWITCH_CENSUS_PORT_MIN
+ if (port < XSWITCH_CENSUS_PORT_MIN)
+ return;
+#endif
+ if (port > XSWITCH_CENSUS_PORT_MAX)
+ return;
+
+ xswitch_info->master_vhdl[port - XSWITCH_CENSUS_PORT_MIN] = master_vhdl;
+}
+
+devfs_handle_t
+xswitch_info_master_assignment_get(xswitch_info_t xswitch_info,
+ xwidgetnum_t port)
+{
+#if XSWITCH_CENSUS_PORT_MIN
+ if (port < XSWITCH_CENSUS_PORT_MIN)
+ return GRAPH_VERTEX_NONE;
+#endif
+ if (port > XSWITCH_CENSUS_PORT_MAX)
+ return GRAPH_VERTEX_NONE;
+
+ return xswitch_info->master_vhdl[port - XSWITCH_CENSUS_PORT_MIN];
+}
+
+void
+xswitch_info_set(devfs_handle_t xwidget, xswitch_info_t xswitch_info)
+{
+ xswitch_info->fingerprint = xswitch_info_fingerprint;
+ hwgraph_fastinfo_set(xwidget, (arbitrary_info_t) xswitch_info);
+}
+
+xswitch_info_t
+xswitch_info_new(devfs_handle_t xwidget)
+{
+ xswitch_info_t xswitch_info;
+
+ xswitch_info = xswitch_info_get(xwidget);
+ if (xswitch_info == NULL) {
+ int port;
+
+ NEW(xswitch_info);
+ xswitch_info->census = 0;
+ for (port = XSWITCH_CENSUS_PORT_MIN;
+ port <= XSWITCH_CENSUS_PORT_MAX;
+ port++) {
+ xswitch_info_vhdl_set(xswitch_info, port,
+ GRAPH_VERTEX_NONE);
+
+ xswitch_info_master_assignment_set(xswitch_info,
+ port,
+ GRAPH_VERTEX_NONE);
+ }
+ xswitch_info_set(xwidget, xswitch_info);
+ printk("xswitch_info_new: xswitch_info_set xwidget 0x%p, xswitch_info 0x%p\n",
+ xwidget, xswitch_info);
+ }
+ return xswitch_info;
+}
+
+void
+xswitch_provider_register(devfs_handle_t busv,
+ xswitch_provider_t * xswitch_fns)
+{
+ xswitch_info_t xswitch_info = xswitch_info_get(busv);
+
+ ASSERT(xswitch_info);
+ xswitch_info->xswitch_fns = xswitch_fns;
+}
+
+void
+xswitch_info_link_is_ok(xswitch_info_t xswitch_info, xwidgetnum_t port)
+{
+ xswitch_info->census |= XSWITCH_CENSUS_BIT(port);
+}
+
+int
+xswitch_info_link_ok(xswitch_info_t xswitch_info, xwidgetnum_t port)
+{
+#if XSWITCH_CENSUS_PORT_MIN
+ if (port < XSWITCH_CENSUS_PORT_MIN)
+ return 0;
+#endif
+
+ if (port > XSWITCH_CENSUS_PORT_MAX)
+ return 0;
+
+ return (xswitch_info->census & XSWITCH_CENSUS_BIT(port));
+}
+
+int
+xswitch_reset_link(devfs_handle_t xconn_vhdl)
+{
+ return DEV_FUNC(xconn_vhdl, reset_link)
+ (xconn_vhdl);
+}
+
+/* Given a vertex handle to the xswitch get its logical
+ * id.
+ */
+int
+xswitch_id_get(devfs_handle_t xconn_vhdl)
+{
+ arbitrary_info_t xbow_num;
+ graph_error_t rv;
+
+ rv = hwgraph_info_get_LBL(xconn_vhdl,INFO_LBL_XSWITCH_ID,&xbow_num);
+ ASSERT(rv == GRAPH_SUCCESS);
+ return(xbow_num);
+}
+
+/* Given a vertex handle to the xswitch set its logical
+ * id.
+ */
+void
+xswitch_id_set(devfs_handle_t xconn_vhdl,int xbow_num)
+{
+ graph_error_t rv;
+
+ rv = hwgraph_info_add_LBL(xconn_vhdl,INFO_LBL_XSWITCH_ID,
+ (arbitrary_info_t)xbow_num);
+ ASSERT(rv == GRAPH_SUCCESS);
+}
diff --git a/arch/ia64/sn/io/xtalk.c b/arch/ia64/sn/io/xtalk.c
new file mode 100644
index 000000000..22810d54c
--- /dev/null
+++ b/arch/ia64/sn/io/xtalk.c
@@ -0,0 +1,1137 @@
+/* $Id$
+ *
+ * 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) 1992 - 1997, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2000 by Colin Ngam
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/iobus.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/hcl_util.h>
+
+#include <asm/sn/xtalk/xtalk.h>
+#include <asm/sn/xtalk/xswitch.h>
+#include <asm/sn/xtalk/xwidget.h>
+
+#include <asm/sn/xtalk/xtalk_private.h>
+
+/*
+ * Implement crosstalk provider operations. The xtalk* layer provides a
+ * platform-independent interface for crosstalk devices. This layer
+ * switches among the possible implementations of a crosstalk adapter.
+ *
+ * On platforms with only one possible xtalk provider, macros can be
+ * set up at the top that cause the table lookups and indirections to
+ * completely disappear.
+ */
+
+#define NEW(ptr) (ptr = kmalloc(sizeof (*(ptr)), GFP_KERNEL))
+#define DEL(ptr) (kfree(ptr))
+
+char widget_info_fingerprint[] = "widget_info";
+
+cdl_p xtalk_registry = NULL;
+
+#include <asm/sn/agent.h>
+#define DEV_FUNC(dev,func) hub_##func
+#define CAST_PIOMAP(x) ((hub_piomap_t)(x))
+#define CAST_DMAMAP(x) ((hub_dmamap_t)(x))
+#define CAST_INTR(x) ((hub_intr_t)(x))
+
+/* =====================================================================
+ * Function Table of Contents
+ */
+xtalk_piomap_t xtalk_piomap_alloc(devfs_handle_t, device_desc_t, iopaddr_t, size_t, size_t, unsigned);
+void xtalk_piomap_free(xtalk_piomap_t);
+caddr_t xtalk_piomap_addr(xtalk_piomap_t, iopaddr_t, size_t);
+void xtalk_piomap_done(xtalk_piomap_t);
+caddr_t xtalk_piotrans_addr(devfs_handle_t, device_desc_t, iopaddr_t, size_t, unsigned);
+caddr_t xtalk_pio_addr(devfs_handle_t, device_desc_t, iopaddr_t, size_t, xtalk_piomap_t *, unsigned);
+void xtalk_set_early_piotrans_addr(xtalk_early_piotrans_addr_f *);
+caddr_t xtalk_early_piotrans_addr(xwidget_part_num_t, xwidget_mfg_num_t, int, iopaddr_t, size_t, unsigned);
+static caddr_t null_xtalk_early_piotrans_addr(xwidget_part_num_t, xwidget_mfg_num_t, int, iopaddr_t, size_t, unsigned);
+xtalk_dmamap_t xtalk_dmamap_alloc(devfs_handle_t, device_desc_t, size_t, unsigned);
+void xtalk_dmamap_free(xtalk_dmamap_t);
+iopaddr_t xtalk_dmamap_addr(xtalk_dmamap_t, paddr_t, size_t);
+alenlist_t xtalk_dmamap_list(xtalk_dmamap_t, alenlist_t, unsigned);
+void xtalk_dmamap_done(xtalk_dmamap_t);
+iopaddr_t xtalk_dmatrans_addr(devfs_handle_t, device_desc_t, paddr_t, size_t, unsigned);
+alenlist_t xtalk_dmatrans_list(devfs_handle_t, device_desc_t, alenlist_t, unsigned);
+void xtalk_dmamap_drain(xtalk_dmamap_t);
+void xtalk_dmaaddr_drain(devfs_handle_t, iopaddr_t, size_t);
+void xtalk_dmalist_drain(devfs_handle_t, alenlist_t);
+xtalk_intr_t xtalk_intr_alloc(devfs_handle_t, device_desc_t, devfs_handle_t);
+void xtalk_intr_free(xtalk_intr_t);
+int xtalk_intr_connect(xtalk_intr_t, intr_func_t, intr_arg_t, xtalk_intr_setfunc_t, void *, void *);
+void xtalk_intr_disconnect(xtalk_intr_t);
+devfs_handle_t xtalk_intr_cpu_get(xtalk_intr_t);
+int xtalk_error_handler(devfs_handle_t, int, ioerror_mode_t, ioerror_t *);
+int xtalk_error_devenable(devfs_handle_t, int, int);
+void xtalk_provider_startup(devfs_handle_t);
+void xtalk_provider_shutdown(devfs_handle_t);
+devfs_handle_t xtalk_intr_dev_get(xtalk_intr_t);
+xwidgetnum_t xtalk_intr_target_get(xtalk_intr_t);
+xtalk_intr_vector_t xtalk_intr_vector_get(xtalk_intr_t);
+iopaddr_t xtalk_intr_addr_get(struct xtalk_intr_s *);
+void *xtalk_intr_sfarg_get(xtalk_intr_t);
+devfs_handle_t xtalk_pio_dev_get(xtalk_piomap_t);
+xwidgetnum_t xtalk_pio_target_get(xtalk_piomap_t);
+iopaddr_t xtalk_pio_xtalk_addr_get(xtalk_piomap_t);
+ulong xtalk_pio_mapsz_get(xtalk_piomap_t);
+caddr_t xtalk_pio_kvaddr_get(xtalk_piomap_t);
+devfs_handle_t xtalk_dma_dev_get(xtalk_dmamap_t);
+xwidgetnum_t xtalk_dma_target_get(xtalk_dmamap_t);
+xwidget_info_t xwidget_info_chk(devfs_handle_t);
+xwidget_info_t xwidget_info_get(devfs_handle_t);
+void xwidget_info_set(devfs_handle_t, xwidget_info_t);
+devfs_handle_t xwidget_info_dev_get(xwidget_info_t);
+xwidgetnum_t xwidget_info_id_get(xwidget_info_t);
+devfs_handle_t xwidget_info_master_get(xwidget_info_t);
+xwidgetnum_t xwidget_info_masterid_get(xwidget_info_t);
+xwidget_part_num_t xwidget_info_part_num_get(xwidget_info_t);
+xwidget_mfg_num_t xwidget_info_mfg_num_get(xwidget_info_t);
+char *xwidget_info_name_get(xwidget_info_t);
+void xtalk_init(void);
+void xtalk_provider_register(devfs_handle_t, xtalk_provider_t *);
+void xtalk_provider_unregister(devfs_handle_t);
+xtalk_provider_t *xtalk_provider_fns_get(devfs_handle_t);
+int xwidget_driver_register(xwidget_part_num_t,
+ xwidget_mfg_num_t,
+ char *, unsigned);
+void xwidget_driver_unregister(char *);
+int xwidget_register(xwidget_hwid_t, devfs_handle_t,
+ xwidgetnum_t, devfs_handle_t,
+ xwidgetnum_t, async_attach_t);
+int xwidget_unregister(devfs_handle_t);
+void xwidget_error_register(devfs_handle_t, error_handler_f *,
+ error_handler_arg_t);
+void xwidget_reset(devfs_handle_t);
+char *xwidget_name_get(devfs_handle_t);
+#if !defined(DEV_FUNC)
+/*
+ * There is more than one possible provider
+ * for this platform. We need to examine the
+ * master vertex of the current vertex for
+ * a provider function structure, and indirect
+ * through the appropriately named member.
+ */
+#define DEV_FUNC(dev,func) xwidget_to_provider_fns(dev)->func
+#define CAST_PIOMAP(x) ((xtalk_piomap_t)(x))
+#define CAST_DMAMAP(x) ((xtalk_dmamap_t)(x))
+#define CAST_INTR(x) ((xtalk_intr_t)(x))
+
+static xtalk_provider_t *
+xwidget_to_provider_fns(devfs_handle_t xconn)
+{
+ xwidget_info_t widget_info;
+ xtalk_provider_t *provider_fns;
+
+ widget_info = xwidget_info_get(xconn);
+ ASSERT(widget_info != NULL);
+
+ provider_fns = xwidget_info_pops_get(widget_info);
+ ASSERT(provider_fns != NULL);
+
+ return (provider_fns);
+}
+#endif
+
+/*
+ * Many functions are not passed their vertex
+ * information directly; rather, they must
+ * dive through a resource map. These macros
+ * are available to coordinate this detail.
+ */
+#define PIOMAP_FUNC(map,func) DEV_FUNC(map->xp_dev,func)
+#define DMAMAP_FUNC(map,func) DEV_FUNC(map->xd_dev,func)
+#define INTR_FUNC(intr,func) DEV_FUNC(intr_hdl->xi_dev,func)
+
+/* =====================================================================
+ * PIO MANAGEMENT
+ *
+ * For mapping system virtual address space to
+ * xtalk space on a specified widget
+ */
+
+xtalk_piomap_t
+xtalk_piomap_alloc(devfs_handle_t dev, /* set up mapping for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ iopaddr_t xtalk_addr, /* map for this xtalk_addr range */
+ size_t byte_count,
+ size_t byte_count_max, /* maximum size of a mapping */
+ unsigned flags)
+{ /* defined in sys/pio.h */
+ return (xtalk_piomap_t) DEV_FUNC(dev, piomap_alloc)
+ (dev, dev_desc, xtalk_addr, byte_count, byte_count_max, flags);
+}
+
+
+void
+xtalk_piomap_free(xtalk_piomap_t xtalk_piomap)
+{
+ PIOMAP_FUNC(xtalk_piomap, piomap_free)
+ (CAST_PIOMAP(xtalk_piomap));
+}
+
+
+caddr_t
+xtalk_piomap_addr(xtalk_piomap_t xtalk_piomap, /* mapping resources */
+ iopaddr_t xtalk_addr, /* map for this xtalk address */
+ size_t byte_count)
+{ /* map this many bytes */
+ return PIOMAP_FUNC(xtalk_piomap, piomap_addr)
+ (CAST_PIOMAP(xtalk_piomap), xtalk_addr, byte_count);
+}
+
+
+void
+xtalk_piomap_done(xtalk_piomap_t xtalk_piomap)
+{
+ PIOMAP_FUNC(xtalk_piomap, piomap_done)
+ (CAST_PIOMAP(xtalk_piomap));
+}
+
+
+caddr_t
+xtalk_piotrans_addr(devfs_handle_t dev, /* translate for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ iopaddr_t xtalk_addr, /* Crosstalk address */
+ size_t byte_count, /* map this many bytes */
+ unsigned flags)
+{ /* (currently unused) */
+ return DEV_FUNC(dev, piotrans_addr)
+ (dev, dev_desc, xtalk_addr, byte_count, flags);
+}
+
+caddr_t
+xtalk_pio_addr(devfs_handle_t dev, /* translate for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ iopaddr_t addr, /* starting address (or offset in window) */
+ size_t byte_count, /* map this many bytes */
+ xtalk_piomap_t *mapp, /* where to return the map pointer */
+ unsigned flags)
+{ /* PIO flags */
+ xtalk_piomap_t map = 0;
+ caddr_t res;
+
+ if (mapp)
+ *mapp = 0; /* record "no map used" */
+
+ res = xtalk_piotrans_addr
+ (dev, dev_desc, addr, byte_count, flags);
+ if (res)
+ return res; /* xtalk_piotrans worked */
+
+ map = xtalk_piomap_alloc
+ (dev, dev_desc, addr, byte_count, byte_count, flags);
+ if (!map)
+ return res; /* xtalk_piomap_alloc failed */
+
+ res = xtalk_piomap_addr
+ (map, addr, byte_count);
+ if (!res) {
+ xtalk_piomap_free(map);
+ return res; /* xtalk_piomap_addr failed */
+ }
+ if (mapp)
+ *mapp = map; /* pass back map used */
+
+ return res; /* xtalk_piomap_addr succeeded */
+}
+
+/* =====================================================================
+ * EARLY PIOTRANS SUPPORT
+ *
+ * There are places where drivers (mgras, for instance)
+ * need to get PIO translations before the infrastructure
+ * is extended to them (setting up textports, for
+ * instance). These drivers should call
+ * xtalk_early_piotrans_addr with their xtalk ID
+ * information, a sequence number (so we can use the second
+ * mgras for instance), and the usual piotrans parameters.
+ *
+ * Machine specific code should provide an implementation
+ * of early_piotrans_addr, and present a pointer to this
+ * function to xtalk_set_early_piotrans_addr so it can be
+ * used by clients without the clients having to know what
+ * platform or what xtalk provider is in use.
+ */
+
+static xtalk_early_piotrans_addr_f null_xtalk_early_piotrans_addr;
+
+xtalk_early_piotrans_addr_f *impl_early_piotrans_addr = null_xtalk_early_piotrans_addr;
+
+/* xtalk_set_early_piotrans_addr:
+ * specify the early_piotrans_addr implementation function.
+ */
+void
+xtalk_set_early_piotrans_addr(xtalk_early_piotrans_addr_f *impl)
+{
+ impl_early_piotrans_addr = impl;
+}
+
+/* xtalk_early_piotrans_addr:
+ * figure out a PIO address for the "nth" crosstalk widget that
+ * matches the specified part and mfgr number. Returns NULL if
+ * there is no such widget, or if the requested mapping can not
+ * be constructed.
+ * Limitations on which crosstalk slots (and busses) are
+ * checked, and definitions of the ordering of the search across
+ * the crosstalk slots, are defined by the platform.
+ */
+caddr_t
+xtalk_early_piotrans_addr(xwidget_part_num_t part_num,
+ xwidget_mfg_num_t mfg_num,
+ int which,
+ iopaddr_t xtalk_addr,
+ size_t byte_count,
+ unsigned flags)
+{
+ return impl_early_piotrans_addr
+ (part_num, mfg_num, which, xtalk_addr, byte_count, flags);
+}
+
+/* null_xtalk_early_piotrans_addr:
+ * used as the early_piotrans_addr implementation until and
+ * unless a real implementation is provided. In DEBUG kernels,
+ * we want to know who is calling before the implementation is
+ * registered; in non-DEBUG kernels, return NULL representing
+ * lack of mapping support.
+ */
+/*ARGSUSED */
+static caddr_t
+null_xtalk_early_piotrans_addr(xwidget_part_num_t part_num,
+ xwidget_mfg_num_t mfg_num,
+ int which,
+ iopaddr_t xtalk_addr,
+ size_t byte_count,
+ unsigned flags)
+{
+#if DEBUG
+ cmn_err(CE_PANIC, "null_xtalk_early_piotrans_addr");
+#endif
+ return NULL;
+}
+
+/* =====================================================================
+ * DMA MANAGEMENT
+ *
+ * For mapping from crosstalk space to system
+ * physical space.
+ */
+
+xtalk_dmamap_t
+xtalk_dmamap_alloc(devfs_handle_t dev, /* set up mappings for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ size_t byte_count_max, /* max size of a mapping */
+ unsigned flags)
+{ /* defined in dma.h */
+ return (xtalk_dmamap_t) DEV_FUNC(dev, dmamap_alloc)
+ (dev, dev_desc, byte_count_max, flags);
+}
+
+
+void
+xtalk_dmamap_free(xtalk_dmamap_t xtalk_dmamap)
+{
+ DMAMAP_FUNC(xtalk_dmamap, dmamap_free)
+ (CAST_DMAMAP(xtalk_dmamap));
+}
+
+
+iopaddr_t
+xtalk_dmamap_addr(xtalk_dmamap_t xtalk_dmamap, /* use these mapping resources */
+ paddr_t paddr, /* map for this address */
+ size_t byte_count)
+{ /* map this many bytes */
+ return DMAMAP_FUNC(xtalk_dmamap, dmamap_addr)
+ (CAST_DMAMAP(xtalk_dmamap), paddr, byte_count);
+}
+
+
+alenlist_t
+xtalk_dmamap_list(xtalk_dmamap_t xtalk_dmamap, /* use these mapping resources */
+ alenlist_t alenlist, /* map this Address/Length List */
+ unsigned flags)
+{
+ return DMAMAP_FUNC(xtalk_dmamap, dmamap_list)
+ (CAST_DMAMAP(xtalk_dmamap), alenlist, flags);
+}
+
+
+void
+xtalk_dmamap_done(xtalk_dmamap_t xtalk_dmamap)
+{
+ DMAMAP_FUNC(xtalk_dmamap, dmamap_done)
+ (CAST_DMAMAP(xtalk_dmamap));
+}
+
+
+iopaddr_t
+xtalk_dmatrans_addr(devfs_handle_t dev, /* translate for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ paddr_t paddr, /* system physical address */
+ size_t byte_count, /* length */
+ unsigned flags)
+{ /* defined in dma.h */
+ return DEV_FUNC(dev, dmatrans_addr)
+ (dev, dev_desc, paddr, byte_count, flags);
+}
+
+
+alenlist_t
+xtalk_dmatrans_list(devfs_handle_t dev, /* translate for this device */
+ device_desc_t dev_desc, /* device descriptor */
+ alenlist_t palenlist, /* system address/length list */
+ unsigned flags)
+{ /* defined in dma.h */
+ return DEV_FUNC(dev, dmatrans_list)
+ (dev, dev_desc, palenlist, flags);
+}
+
+void
+xtalk_dmamap_drain(xtalk_dmamap_t map)
+{
+ DMAMAP_FUNC(map, dmamap_drain)
+ (CAST_DMAMAP(map));
+}
+
+void
+xtalk_dmaaddr_drain(devfs_handle_t dev, paddr_t addr, size_t size)
+{
+ DEV_FUNC(dev, dmaaddr_drain)
+ (dev, addr, size);
+}
+
+void
+xtalk_dmalist_drain(devfs_handle_t dev, alenlist_t list)
+{
+ DEV_FUNC(dev, dmalist_drain)
+ (dev, list);
+}
+
+/* =====================================================================
+ * INTERRUPT MANAGEMENT
+ *
+ * Allow crosstalk devices to establish interrupts
+ */
+
+/*
+ * Allocate resources required for an interrupt as specified in intr_desc.
+ * Return resource handle in intr_hdl.
+ */
+xtalk_intr_t
+xtalk_intr_alloc(devfs_handle_t dev, /* which Crosstalk device */
+ device_desc_t dev_desc, /* device descriptor */
+ devfs_handle_t owner_dev)
+{ /* owner of this interrupt */
+ return (xtalk_intr_t) DEV_FUNC(dev, intr_alloc)
+ (dev, dev_desc, owner_dev);
+}
+
+
+/*
+ * Free resources consumed by intr_alloc.
+ */
+void
+xtalk_intr_free(xtalk_intr_t intr_hdl)
+{
+ INTR_FUNC(intr_hdl, intr_free)
+ (CAST_INTR(intr_hdl));
+}
+
+
+/*
+ * Associate resources allocated with a previous xtalk_intr_alloc call with the
+ * described handler, arg, name, etc.
+ *
+ * Returns 0 on success, returns <0 on failure.
+ */
+int
+xtalk_intr_connect(xtalk_intr_t intr_hdl, /* xtalk intr resource handle */
+ intr_func_t intr_func, /* xtalk intr handler */
+ intr_arg_t intr_arg, /* arg to intr handler */
+ xtalk_intr_setfunc_t setfunc, /* func to set intr hw */
+ void *setfunc_arg, /* arg to setfunc */
+ void *thread)
+{ /* intr thread to use */
+ return INTR_FUNC(intr_hdl, intr_connect)
+ (CAST_INTR(intr_hdl), intr_func, intr_arg, setfunc, setfunc_arg, thread);
+}
+
+
+/*
+ * Disassociate handler with the specified interrupt.
+ */
+void
+xtalk_intr_disconnect(xtalk_intr_t intr_hdl)
+{
+ INTR_FUNC(intr_hdl, intr_disconnect)
+ (CAST_INTR(intr_hdl));
+}
+
+
+/*
+ * Return a hwgraph vertex that represents the CPU currently
+ * targeted by an interrupt.
+ */
+devfs_handle_t
+xtalk_intr_cpu_get(xtalk_intr_t intr_hdl)
+{
+ return INTR_FUNC(intr_hdl, intr_cpu_get)
+ (CAST_INTR(intr_hdl));
+}
+
+
+/*
+ * =====================================================================
+ * ERROR MANAGEMENT
+ */
+
+/*
+ * xtalk_error_handler:
+ * pass this error on to the handler registered
+ * at the specified xtalk connecdtion point,
+ * or complain about it here if there is no handler.
+ *
+ * This routine plays two roles during error delivery
+ * to most widgets: first, the external agent (heart,
+ * hub, or whatever) calls in with the error and the
+ * connect point representing the crosstalk switch,
+ * or whatever crosstalk device is directly connected
+ * to the agent.
+ *
+ * If there is a switch, it will generally look at the
+ * widget number stashed in the ioerror structure; and,
+ * if the error came from some widget other than the
+ * switch, it will call back into xtalk_error_handler
+ * with the connection point of the offending port.
+ */
+int
+xtalk_error_handler(
+ devfs_handle_t xconn,
+ int error_code,
+ ioerror_mode_t mode,
+ ioerror_t *ioerror)
+{
+ xwidget_info_t xwidget_info;
+
+#if DEBUG && ERROR_DEBUG
+ cmn_err(CE_CONT, "%v: xtalk_error_handler\n", xconn);
+#endif
+
+ xwidget_info = xwidget_info_get(xconn);
+ /* Make sure that xwidget_info is a valid pointer before derefencing it.
+ * We could come in here during very early initialization.
+ */
+ if (xwidget_info && xwidget_info->w_efunc)
+ return xwidget_info->w_efunc
+ (xwidget_info->w_einfo,
+ error_code, mode, ioerror);
+ /*
+ * no error handler registered for
+ * the offending port. it's not clear
+ * what needs to be done, but reporting
+ * it would be a good thing, unless it
+ * is a mode that requires nothing.
+ */
+ if ((mode == MODE_DEVPROBE) || (mode == MODE_DEVUSERERROR) ||
+ (mode == MODE_DEVREENABLE))
+ return IOERROR_HANDLED;
+
+#ifdef IRIX
+ cmn_err(CE_WARN, "Xbow at %v encountered Fatal error", xconn);
+#endif
+ ioerror_dump("xtalk", error_code, mode, ioerror);
+
+ return IOERROR_UNHANDLED;
+}
+
+int
+xtalk_error_devenable(devfs_handle_t xconn_vhdl, int devnum, int error_code)
+{
+ return DEV_FUNC(xconn_vhdl, error_devenable) (xconn_vhdl, devnum, error_code);
+}
+
+
+/* =====================================================================
+ * CONFIGURATION MANAGEMENT
+ */
+
+/*
+ * Startup a crosstalk provider
+ */
+void
+xtalk_provider_startup(devfs_handle_t xtalk_provider)
+{
+ DEV_FUNC(xtalk_provider, provider_startup)
+ (xtalk_provider);
+}
+
+
+/*
+ * Shutdown a crosstalk provider
+ */
+void
+xtalk_provider_shutdown(devfs_handle_t xtalk_provider)
+{
+ DEV_FUNC(xtalk_provider, provider_shutdown)
+ (xtalk_provider);
+}
+
+/*
+ * Enable a device on a xtalk widget
+ */
+void
+xtalk_widgetdev_enable(devfs_handle_t xconn_vhdl, int devnum)
+{
+ DEV_FUNC(xconn_vhdl, widgetdev_enable) (xconn_vhdl, devnum);
+}
+
+/*
+ * Shutdown a device on a xtalk widget
+ */
+void
+xtalk_widgetdev_shutdown(devfs_handle_t xconn_vhdl, int devnum)
+{
+ DEV_FUNC(xconn_vhdl, widgetdev_shutdown) (xconn_vhdl, devnum);
+}
+
+int
+xtalk_dma_enabled(devfs_handle_t xconn_vhdl)
+{
+ return DEV_FUNC(xconn_vhdl, dma_enabled) (xconn_vhdl);
+}
+/*
+ * Generic crosstalk functions, for use with all crosstalk providers
+ * and all crosstalk devices.
+ */
+
+/****** Generic crosstalk interrupt interfaces ******/
+devfs_handle_t
+xtalk_intr_dev_get(xtalk_intr_t xtalk_intr)
+{
+ return (xtalk_intr->xi_dev);
+}
+
+xwidgetnum_t
+xtalk_intr_target_get(xtalk_intr_t xtalk_intr)
+{
+ return (xtalk_intr->xi_target);
+}
+
+xtalk_intr_vector_t
+xtalk_intr_vector_get(xtalk_intr_t xtalk_intr)
+{
+ return (xtalk_intr->xi_vector);
+}
+
+iopaddr_t
+xtalk_intr_addr_get(struct xtalk_intr_s *xtalk_intr)
+{
+ return (xtalk_intr->xi_addr);
+}
+
+void *
+xtalk_intr_sfarg_get(xtalk_intr_t xtalk_intr)
+{
+ return (xtalk_intr->xi_sfarg);
+}
+
+
+int
+xtalk_intr_flags_get(xtalk_intr_t xtalk_intr)
+{
+ return(xtalk_intr->xi_flags);
+}
+
+/****** Generic crosstalk pio interfaces ******/
+devfs_handle_t
+xtalk_pio_dev_get(xtalk_piomap_t xtalk_piomap)
+{
+ return (xtalk_piomap->xp_dev);
+}
+
+xwidgetnum_t
+xtalk_pio_target_get(xtalk_piomap_t xtalk_piomap)
+{
+ return (xtalk_piomap->xp_target);
+}
+
+iopaddr_t
+xtalk_pio_xtalk_addr_get(xtalk_piomap_t xtalk_piomap)
+{
+ return (xtalk_piomap->xp_xtalk_addr);
+}
+
+ulong
+xtalk_pio_mapsz_get(xtalk_piomap_t xtalk_piomap)
+{
+ return (xtalk_piomap->xp_mapsz);
+}
+
+caddr_t
+xtalk_pio_kvaddr_get(xtalk_piomap_t xtalk_piomap)
+{
+ return (xtalk_piomap->xp_kvaddr);
+}
+
+
+/****** Generic crosstalk dma interfaces ******/
+devfs_handle_t
+xtalk_dma_dev_get(xtalk_dmamap_t xtalk_dmamap)
+{
+ return (xtalk_dmamap->xd_dev);
+}
+
+xwidgetnum_t
+xtalk_dma_target_get(xtalk_dmamap_t xtalk_dmamap)
+{
+ return (xtalk_dmamap->xd_target);
+}
+
+
+/****** Generic crosstalk widget information interfaces ******/
+
+/* xwidget_info_chk:
+ * check to see if this vertex is a widget;
+ * if so, return its widget_info (if any).
+ * if not, return NULL.
+ */
+xwidget_info_t
+xwidget_info_chk(devfs_handle_t xwidget)
+{
+ arbitrary_info_t ainfo = 0;
+
+ hwgraph_info_get_LBL(xwidget, INFO_LBL_XWIDGET, &ainfo);
+ return (xwidget_info_t) ainfo;
+}
+
+
+xwidget_info_t
+xwidget_info_get(devfs_handle_t xwidget)
+{
+ xwidget_info_t widget_info;
+
+ widget_info = (xwidget_info_t)
+ hwgraph_fastinfo_get(xwidget);
+
+#ifdef IRIX
+ if ((widget_info != NULL) &&
+ (widget_info->w_fingerprint != widget_info_fingerprint))
+ cmn_err(CE_PANIC, "%v bad xwidget_info", xwidget);
+#endif
+
+ return (widget_info);
+}
+
+void
+xwidget_info_set(devfs_handle_t xwidget, xwidget_info_t widget_info)
+{
+ if (widget_info != NULL)
+ widget_info->w_fingerprint = widget_info_fingerprint;
+
+ hwgraph_fastinfo_set(xwidget, (arbitrary_info_t) widget_info);
+
+ /* Also, mark this vertex as an xwidget,
+ * and use the widget_info, so xwidget_info_chk
+ * can work (and be fairly efficient).
+ */
+ hwgraph_info_add_LBL(xwidget, INFO_LBL_XWIDGET,
+ (arbitrary_info_t) widget_info);
+}
+
+devfs_handle_t
+xwidget_info_dev_get(xwidget_info_t xwidget_info)
+{
+ if (xwidget_info == NULL)
+ panic("null xwidget_info");
+ return (xwidget_info->w_vertex);
+}
+
+xwidgetnum_t
+xwidget_info_id_get(xwidget_info_t xwidget_info)
+{
+ if (xwidget_info == NULL)
+ panic("null xwidget_info");
+ return (xwidget_info->w_id);
+}
+
+
+devfs_handle_t
+xwidget_info_master_get(xwidget_info_t xwidget_info)
+{
+ if (xwidget_info == NULL)
+ panic("null xwidget_info");
+ return (xwidget_info->w_master);
+}
+
+xwidgetnum_t
+xwidget_info_masterid_get(xwidget_info_t xwidget_info)
+{
+ if (xwidget_info == NULL)
+ panic("null xwidget_info");
+ return (xwidget_info->w_masterid);
+}
+
+xwidget_part_num_t
+xwidget_info_part_num_get(xwidget_info_t xwidget_info)
+{
+ if (xwidget_info == NULL)
+ panic("null xwidget_info");
+ return (xwidget_info->w_hwid.part_num);
+}
+
+xwidget_mfg_num_t
+xwidget_info_mfg_num_get(xwidget_info_t xwidget_info)
+{
+ if (xwidget_info == NULL)
+ panic("null xwidget_info");
+ return (xwidget_info->w_hwid.mfg_num);
+}
+/* Extract the widget name from the widget information
+ * for the xtalk widget.
+ */
+char *
+xwidget_info_name_get(xwidget_info_t xwidget_info)
+{
+ if (xwidget_info == NULL)
+ panic("null xwidget info");
+ return(xwidget_info->w_name);
+}
+/****** Generic crosstalk initialization interfaces ******/
+
+/*
+ * One-time initialization needed for systems that support crosstalk.
+ */
+void
+xtalk_init(void)
+{
+ cdl_p cp;
+
+#if DEBUG && ATTACH_DEBUG
+ printf("xtalk_init\n");
+#endif
+ /* Allocate the registry.
+ * We might already have one.
+ * If we don't, go get one.
+ * MPness: someone might have
+ * set one up for us while we
+ * were not looking; use an atomic
+ * compare-and-swap to commit to
+ * using the new registry if and
+ * only if nobody else did first.
+ * If someone did get there first,
+ * toss the one we allocated back
+ * into the pool.
+ */
+ if (xtalk_registry == NULL) {
+ cp = cdl_new(EDGE_LBL_XIO, "part", "mfgr");
+ if (!compare_and_swap_ptr((void **) &xtalk_registry, NULL, (void *) cp)) {
+ cdl_del(cp);
+ }
+ }
+ ASSERT(xtalk_registry != NULL);
+}
+
+/*
+ * Associate a set of xtalk_provider functions with a vertex.
+ */
+void
+xtalk_provider_register(devfs_handle_t provider, xtalk_provider_t *xtalk_fns)
+{
+ hwgraph_fastinfo_set(provider, (arbitrary_info_t) xtalk_fns);
+}
+
+/*
+ * Disassociate a set of xtalk_provider functions with a vertex.
+ */
+void
+xtalk_provider_unregister(devfs_handle_t provider)
+{
+ hwgraph_fastinfo_set(provider, (arbitrary_info_t)NULL);
+}
+
+/*
+ * Obtain a pointer to the xtalk_provider functions for a specified Crosstalk
+ * provider.
+ */
+xtalk_provider_t *
+xtalk_provider_fns_get(devfs_handle_t provider)
+{
+ return ((xtalk_provider_t *) hwgraph_fastinfo_get(provider));
+}
+
+/*
+ * Announce a driver for a particular crosstalk part.
+ * Returns 0 on success or -1 on failure. Failure occurs if the
+ * specified hardware already has a driver.
+ */
+/*ARGSUSED4 */
+int
+xwidget_driver_register(xwidget_part_num_t part_num,
+ xwidget_mfg_num_t mfg_num,
+ char *driver_prefix,
+ unsigned flags)
+{
+ /* a driver's init routine could call
+ * xwidget_driver_register before the
+ * system calls xtalk_init; so, we
+ * make the call here.
+ */
+ if (xtalk_registry == NULL)
+ xtalk_init();
+
+ return cdl_add_driver(xtalk_registry,
+ part_num, mfg_num,
+ driver_prefix, flags);
+}
+
+/*
+ * Inform xtalk infrastructure that a driver is no longer available for
+ * handling any widgets.
+ */
+void
+xwidget_driver_unregister(char *driver_prefix)
+{
+ /* before a driver calls unregister,
+ * it must have called registger; so we
+ * can assume we have a registry here.
+ */
+ ASSERT(xtalk_registry != NULL);
+
+ cdl_del_driver(xtalk_registry, driver_prefix);
+}
+
+/*
+ * Call some function with each vertex that
+ * might be one of this driver's attach points.
+ */
+void
+xtalk_iterate(char *driver_prefix,
+ xtalk_iter_f *func)
+{
+ ASSERT(xtalk_registry != NULL);
+
+ cdl_iterate(xtalk_registry, driver_prefix, (cdl_iter_f *)func);
+}
+
+/*
+ * xwidget_register:
+ * Register a xtalk device (xwidget) by doing the following.
+ * -allocate and initialize xwidget_info data
+ * -allocate a hwgraph vertex with name based on widget number (id)
+ * -look up the widget's initialization function and call it,
+ * or remember the vertex for later initialization.
+ *
+ */
+int
+xwidget_register(xwidget_hwid_t hwid, /* widget's hardware ID */
+ devfs_handle_t widget, /* widget to initialize */
+ xwidgetnum_t id, /* widget's target id (0..f) */
+ devfs_handle_t master, /* widget's master vertex */
+ xwidgetnum_t targetid, /* master's target id (9/a) */
+ async_attach_t aa)
+{
+ xwidget_info_t widget_info;
+ char *s,devnm[MAXDEVNAME];
+
+ /* Allocate widget_info and associate it with widget vertex */
+ NEW(widget_info);
+
+ /* Initialize widget_info */
+ widget_info->w_vertex = widget;
+ widget_info->w_id = id;
+ widget_info->w_master = master;
+ widget_info->w_masterid = targetid;
+ widget_info->w_hwid = *hwid; /* structure copy */
+ widget_info->w_efunc = 0;
+ widget_info->w_einfo = 0;
+ /*
+ * get the name of this xwidget vertex and keep the info.
+ * This is needed during errors and interupts, but as
+ * long as we have it, we can use it elsewhere.
+ */
+ s = dev_to_name(widget,devnm,MAXDEVNAME);
+ printk("xwidget_register: dev_to_name widget id 0x%p, s = %s\n", widget, s);
+ widget_info->w_name = kmalloc(strlen(s) + 1, GFP_KERNEL);
+ strcpy(widget_info->w_name,s);
+
+ xwidget_info_set(widget, widget_info);
+
+ device_master_set(widget, master);
+
+ /* All the driver init routines (including
+ * xtalk_init) are called before we get into
+ * attaching devices, so we can assume we
+ * have a registry here.
+ */
+ ASSERT(xtalk_registry != NULL);
+
+ /*
+ * Add pointer to async attach info -- tear down will be done when
+ * the particular descendant is done with the info.
+ */
+ if (aa)
+ async_attach_add_info(widget, aa);
+
+ return cdl_add_connpt(xtalk_registry, hwid->part_num, hwid->mfg_num, widget);
+}
+
+/*
+ * xwidget_unregister :
+ * Unregister the xtalk device and detach all its hwgraph namespace.
+ */
+int
+xwidget_unregister(devfs_handle_t widget)
+{
+ xwidget_info_t widget_info;
+ xwidget_hwid_t hwid;
+
+ /* Make sure that we have valid widget information initialized */
+ if (!(widget_info = xwidget_info_get(widget)))
+ return(1);
+
+ /* Remove the inventory information associated
+ * with the widget.
+ */
+ hwgraph_inventory_remove(widget, -1, -1, -1, -1, -1);
+
+ hwid = &(widget_info->w_hwid);
+
+ cdl_del_connpt(xtalk_registry, hwid->part_num,
+ hwid->mfg_num, widget);
+
+ /* Clean out the xwidget information */
+ (void)kfree(widget_info->w_name);
+ BZERO((void *)widget_info, sizeof(widget_info));
+ DEL(widget_info);
+
+ return(0);
+}
+
+void
+xwidget_error_register(devfs_handle_t xwidget,
+ error_handler_f *efunc,
+ error_handler_arg_t einfo)
+{
+ xwidget_info_t xwidget_info;
+
+ xwidget_info = xwidget_info_get(xwidget);
+ ASSERT(xwidget_info != NULL);
+ xwidget_info->w_efunc = efunc;
+ xwidget_info->w_einfo = einfo;
+}
+
+/*
+ * Issue a link reset to a widget.
+ */
+void
+xwidget_reset(devfs_handle_t xwidget)
+{
+ xswitch_reset_link(xwidget);
+
+}
+
+
+void
+xwidget_gfx_reset(devfs_handle_t xwidget)
+{
+ xwidget_info_t info;
+
+ xswitch_reset_link(xwidget);
+ info = xwidget_info_get(xwidget);
+#ifdef IRIX
+ ASSERT_ALWAYS(info != NULL);
+#endif
+
+ /*
+ * Enable this for other architectures once we add widget_reset to the
+ * xtalk provider interface.
+ */
+ DEV_FUNC(xtalk_provider, widget_reset)
+ (xwidget_info_master_get(info), xwidget_info_id_get(info));
+}
+
+#define ANON_XWIDGET_NAME "No Name" /* Default Widget Name */
+
+/* Get the canonical hwgraph name of xtalk widget */
+char *
+xwidget_name_get(devfs_handle_t xwidget_vhdl)
+{
+ xwidget_info_t info;
+
+ /* If we have a bogus widget handle then return
+ * a default anonymous widget name.
+ */
+ if (xwidget_vhdl == GRAPH_VERTEX_NONE)
+ return(ANON_XWIDGET_NAME);
+ /* Read the widget name stored in the widget info
+ * for the widget setup during widget initialization.
+ */
+ info = xwidget_info_get(xwidget_vhdl);
+ ASSERT(info != NULL);
+ return(xwidget_info_name_get(info));
+}
+/*
+ * xtalk_device_powerup
+ * Reset and initialize the specified xtalk widget
+ */
+int
+xtalk_device_powerup(devfs_handle_t xbus_vhdl, xwidgetnum_t widget)
+{
+#ifndef CONFIG_IA64_SGI_IO
+ extern void io_xswitch_widget_init(devfs_handle_t,
+ devfs_handle_t,
+ xwidgetnum_t,
+ async_attach_t);
+ io_xswitch_widget_init(xbus_vhdl,
+ hwgraph_connectpt_get(xbus_vhdl),
+ widget,
+ NULL);
+#endif /* CONFIG_IA64_SGI_IO */
+
+ return(0);
+}
+/*
+ * xtalk_device_shutdown
+ * Disable the specified xtalk widget and clean out all the software
+ * state associated with it.
+ */
+int
+xtalk_device_shutdown(devfs_handle_t xbus_vhdl, xwidgetnum_t widget)
+{
+ devfs_handle_t widget_vhdl;
+ char edge_name[8];
+
+ sprintf(edge_name, "%d", widget);
+ if (hwgraph_traverse(xbus_vhdl, edge_name, &widget_vhdl)
+ != GRAPH_SUCCESS)
+ return(1);
+
+ xwidget_unregister(widget_vhdl);
+
+ return(0);
+}
+/*
+ * xtalk_device_inquiry
+ * Find out hardware information about the xtalk widget.
+ */
+int
+xtalk_device_inquiry(devfs_handle_t xbus_vhdl, xwidgetnum_t widget)
+{
+
+ extern void hub_device_inquiry(devfs_handle_t, xwidgetnum_t);
+ hub_device_inquiry(xbus_vhdl, widget);
+ return(0);
+}
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);
+ }
+}
diff --git a/arch/ia64/sn/tools/make_textsym b/arch/ia64/sn/tools/make_textsym
new file mode 100644
index 000000000..0a0787f08
--- /dev/null
+++ b/arch/ia64/sn/tools/make_textsym
@@ -0,0 +1,138 @@
+#!/bin/sh
+# Build a textsym file for use in the Arium ITP probe.
+
+help() {
+cat <<END
+Build a WinDD "symtxt" file for use with the Arium ECM-30 probe.
+
+ Usage: $0 [<vmlinux file> [<output file>]]
+ If no input file is specified, it defaults to vmlinux.
+ If no output file name is specified, it defaults to "textsym".
+END
+exit 1
+}
+
+err () {
+ echo "ERROR - $*" >&2
+ exit 1
+}
+
+
+OPTS="H"
+while getopts "$OPTS" c ; do
+ case $c in
+ H) help;;
+ \?) help;;
+ esac
+
+done
+shift `expr $OPTIND - 1`
+
+LINUX=${1:-vmlinux}
+TEXTSYM=${2:-${LINUX}.sym}
+
+[ -f $VMLINUX ] || help
+
+
+# pipe everything thru sort
+echo "TEXTSYM V1.0"
+(cat <<END
+GLOBAL | e000000000500000 | CODE | VEC_VHPT_Translation_0000
+GLOBAL | e000000000500400 | CODE | VEC_ITLB_0400
+GLOBAL | e000000000500800 | CODE | VEC_DTLB_0800
+GLOBAL | e000000000500c00 | CODE | VEC_Alt_ITLB_0c00
+GLOBAL | e000000000501000 | CODE | VEC_Alt_DTLB_1000
+GLOBAL | e000000000501400 | CODE | VEC_Data_nested_TLB_1400
+GLOBAL | e000000000501800 | CODE | VEC_Instruction_Key_Miss_1800
+GLOBAL | e000000000501c00 | CODE | VEC_Data_Key_Miss_1c00
+GLOBAL | e000000000502000 | CODE | VEC_Dirty-bit_2000
+GLOBAL | e000000000502400 | CODE | VEC_Instruction_Access-bit_2400
+GLOBAL | e000000000502800 | CODE | VEC_Data_Access-bit_2800
+GLOBAL | e000000000502c00 | CODE | VEC_Break_instruction_2c00
+GLOBAL | e000000000503000 | CODE | VEC_External_Interrupt_3000
+GLOBAL | e000000000503400 | CODE | VEC_Reserved_3400
+GLOBAL | e000000000503800 | CODE | VEC_Reserved_3800
+GLOBAL | e000000000503c00 | CODE | VEC_Reserved_3c00
+GLOBAL | e000000000504000 | CODE | VEC_Reserved_4000
+GLOBAL | e000000000504400 | CODE | VEC_Reserved_4400
+GLOBAL | e000000000504800 | CODE | VEC_Reserved_4800
+GLOBAL | e000000000504c00 | CODE | VEC_Reserved_4c00
+GLOBAL | e000000000505000 | CODE | VEC_Page_Not_Present_5000
+GLOBAL | e000000000505100 | CODE | VEC_Key_Permission_5100
+GLOBAL | e000000000505200 | CODE | VEC_Instruction_Access_Rights_5200
+GLOBAL | e000000000505300 | CODE | VEC_Data_Access_Rights_5300
+GLOBAL | e000000000505400 | CODE | VEC_General_Exception_5400
+GLOBAL | e000000000505500 | CODE | VEC_Disabled_FP-Register_5500
+GLOBAL | e000000000505600 | CODE | VEC_Nat_Consumption_5600
+GLOBAL | e000000000505700 | CODE | VEC_Speculation_5700
+GLOBAL | e000000000505800 | CODE | VEC_Reserved_5800
+GLOBAL | e000000000505900 | CODE | VEC_Debug_5900
+GLOBAL | e000000000505a00 | CODE | VEC_Unaligned_Reference_5a00
+GLOBAL | e000000000505b00 | CODE | VEC_Unsupported_Data_Reference_5b00
+GLOBAL | e000000000505c00 | CODE | VEC_Floating-Point_Fault_5c00
+GLOBAL | e000000000505d00 | CODE | VEC_Floating_Point_Trap_5d00
+GLOBAL | e000000000505e00 | CODE | VEC_Lower_Privilege_Tranfer_Trap_5e00
+GLOBAL | e000000000505f00 | CODE | VEC_Taken_Branch_Trap_5f00
+GLOBAL | e000000000506000 | CODE | VEC_Single_Step_Trap_6000
+GLOBAL | e000000000506100 | CODE | VEC_Reserved_6100
+GLOBAL | e000000000506200 | CODE | VEC_Reserved_6200
+GLOBAL | e000000000506300 | CODE | VEC_Reserved_6300
+GLOBAL | e000000000506400 | CODE | VEC_Reserved_6400
+GLOBAL | e000000000506500 | CODE | VEC_Reserved_6500
+GLOBAL | e000000000506600 | CODE | VEC_Reserved_6600
+GLOBAL | e000000000506700 | CODE | VEC_Reserved_6700
+GLOBAL | e000000000506800 | CODE | VEC_Reserved_6800
+GLOBAL | e000000000506900 | CODE | VEC_IA-32_Exeception_6900
+GLOBAL | e000000000506a00 | CODE | VEC_IA-32_Intercept_6a00
+GLOBAL | e000000000506b00 | CODE | VEC_IA-32_Interrupt_6b00
+GLOBAL | e000000000506c00 | CODE | VEC_Reserved_6c00
+GLOBAL | e000000000506d00 | CODE | VEC_Reserved_6d00
+GLOBAL | e000000000506e00 | CODE | VEC_Reserved_6e00
+GLOBAL | e000000000506f00 | CODE | VEC_Reserved_6f00
+GLOBAL | e000000000507000 | CODE | VEC_Reserved_7000
+GLOBAL | e000000000507100 | CODE | VEC_Reserved_7100
+GLOBAL | e000000000507200 | CODE | VEC_Reserved_7200
+GLOBAL | e000000000507300 | CODE | VEC_Reserved_7300
+GLOBAL | e000000000507400 | CODE | VEC_Reserved_7400
+GLOBAL | e000000000507500 | CODE | VEC_Reserved_7500
+GLOBAL | e000000000507600 | CODE | VEC_Reserved_7600
+GLOBAL | e000000000507700 | CODE | VEC_Reserved_7700
+GLOBAL | e000000000507800 | CODE | VEC_Reserved_7800
+GLOBAL | e000000000507900 | CODE | VEC_Reserved_7900
+GLOBAL | e000000000507a00 | CODE | VEC_Reserved_7a00
+GLOBAL | e000000000507b00 | CODE | VEC_Reserved_7b00
+GLOBAL | e000000000507c00 | CODE | VEC_Reserved_7c00
+GLOBAL | e000000000507d00 | CODE | VEC_Reserved_7d00
+GLOBAL | e000000000507e00 | CODE | VEC_Reserved_7e00
+GLOBAL | e000000000507f00 | CODE | VEC_Reserved_7f00
+END
+
+$OBJDUMP -t $LINUX | sort | awk '
+/empty_zero_page/ {start=1}
+/e0000000/ {
+ if ($4 == ".kdb")
+ next
+ if (start && substr($NF,1,1) != "0") {
+ type = substr($0,26,5)
+ if (type == ".text")
+ printf "GLOBAL | %s | CODE | %s\n", $1, $NF
+ else
+ printf "GLOBAL | %s | DATA | %s | %d\n", $1, $NF, $(NF-1)
+ }
+ if($NF == "_end")
+ exit
+
+}
+' ) | egrep -v " __device| __vendor" | awk '
+/GLOBAL/ {
+ print $0
+ print substr($0,1,9) substr($0,18,18) "Phy_" substr($0,36)
+
+} ' | sort -k3
+
+
+
+N=`wc -l $TEXTSYM|awk '{print $1}'`
+echo "Generated TEXTSYM file" >&2
+echo " $LINUX --> $TEXTSYM" >&2
+echo " Found $N symbols" >&2