Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/parisc/Kconfig b/drivers/parisc/Kconfig
new file mode 100644
index 0000000..3f5de86
--- /dev/null
+++ b/drivers/parisc/Kconfig
@@ -0,0 +1,169 @@
+menu "Bus options (PCI, PCMCIA, EISA, GSC, ISA)"
+
+config GSC
+	bool "VSC/GSC/HSC bus support"
+	default y
+	help
+	  The VSC, GSC and HSC busses were used from the earliest 700-series
+	  workstations up to and including the C360/J2240 workstations.  They
+	  were also used in servers from the E-class to the K-class.  They
+	  are not found in B1000, C3000, J5000, A500, L1000, N4000 and upwards.
+	  If in doubt, say "Y".
+
+config HPPB
+	bool "HP-PB bus support"
+	depends on GSC
+	help
+	  The HP-PB bus was used in the Nova class and K-class servers.
+	  If in doubt, say "Y"
+
+config IOMMU_CCIO
+	bool "U2/Uturn I/O MMU"
+	depends on GSC
+	help
+	  Say Y here to enable DMA management routines for the first
+	  generation of PA-RISC cache-coherent machines.  Programs the
+	  U2/Uturn chip in "Virtual Mode" and use the I/O MMU.
+
+config GSC_LASI
+	bool "Lasi I/O support"
+	depends on GSC
+	help
+	  Say Y here to support the Lasi multifunction chip found in
+	  many PA-RISC workstations & servers.	It includes interfaces
+	  for a parallel port, serial port, NCR 53c710 SCSI, Apricot
+	  Ethernet, Harmony audio, PS/2 keyboard & mouse, ISDN, telephony
+	  and floppy.  Note that you must still enable all the individual
+	  drivers for these chips.
+
+config GSC_WAX
+	bool "Wax I/O support"
+	depends on GSC
+	help
+	  Say Y here to support the Wax multifunction chip found in some
+	  older systems, including B/C/D/R class and 715/64, 715/80 and
+	  715/100.  Wax includes an EISA adapter, a serial port (not always
+	  used), a HIL interface chip and is also known to be used as the
+	  GSC bridge for an X.25 GSC card.
+
+config EISA
+	bool "EISA support"
+	depends on GSC
+	help
+	  Say Y here if you have an EISA bus in your machine.  This code
+	  supports both the Mongoose & Wax EISA adapters.  It is sadly
+	  incomplete and lacks support for card-to-host DMA.
+
+source "drivers/eisa/Kconfig"
+
+config ISA
+	bool "ISA support"
+	depends on EISA
+	help
+	  If you want to plug an ISA card into your EISA bus, say Y here.
+	  Most people should say N.
+
+config PCI
+	bool "PCI support"
+	help
+	  All recent HP machines have PCI slots, and you should say Y here
+	  if you have a recent machine.  If you are convinced you do not have
+	  PCI slots in your machine (eg a 712), then you may say "N" here.
+	  Beware that some GSC cards have a Dino onboard and PCI inside them,
+	  so it may be safest to say "Y" anyway.
+
+source "drivers/pci/Kconfig"
+
+config GSC_DINO
+	bool "GSCtoPCI/Dino PCI support"
+	depends on PCI && GSC
+	help
+	  Say Y here to support the Dino & Cujo GSC to PCI bridges found in
+	  machines from the B132 to the C360, the J2240 and the A180.  Some
+	  GSC/HSC cards (eg gigabit & dual 100 Mbit Ethernet) have a Dino on
+	  the card, and you also need to say Y here if you have such a card.
+	  Note that Dino also supplies one of the serial ports on certain
+	  machines.  If in doubt, say Y.
+
+config PCI_LBA
+	bool "LBA/Elroy PCI support"
+	depends on PCI
+	help
+	  Say Y here to support the Elroy PCI Lower Bus Adapter.  This is
+	  present on B, C, J, L and N-class machines with 4-digit model
+	  numbers and the A400/A500.
+
+config IOSAPIC
+	bool
+	depends on PCI_LBA
+	default PCI_LBA
+
+config IOMMU_SBA
+	bool
+	depends on PCI_LBA
+	default PCI_LBA
+
+#config PCI_EPIC
+#	bool "EPIC/SAGA PCI support"
+#	depends on PCI
+#	default y
+#	help
+#	  Say Y here for V-class PCI, DMA/IOMMU, IRQ subsystem support.
+
+source "drivers/pcmcia/Kconfig"
+
+source "drivers/pci/hotplug/Kconfig"
+
+endmenu
+
+menu "PA-RISC specific drivers"
+
+config SUPERIO
+	bool "SuperIO (SuckyIO) support"
+	depends on PCI_LBA
+	default y
+	help
+	  Say Y here to support the SuperIO chip found in Bxxxx, C3xxx and
+	  J5xxx+ machines. This enables IDE, Floppy, Parallel Port, and
+	  Serial port on those machines.
+
+config CHASSIS_LCD_LED
+	bool "Chassis LCD and LED support"
+	default y
+	help
+	  Say Y here if you want to enable support for the Heartbeat,
+	  Disk/Network activities LEDs on some PA-RISC machines,
+	  or support for the LCD that can be found on recent material.
+	
+	  This has nothing to do with LED State support for A and E class.
+	
+	  If unsure, say Y.
+
+config PDC_CHASSIS
+	bool "PDC chassis State Panel support"
+	default y
+	help
+	  Say Y here if you want to enable support for the LED State front
+	  panel as found on E class, and support for the GSP Virtual Front
+	  Panel (LED State and message logging)  as found on high end
+	  servers such as A, L and N-class.
+	  
+	  This has nothing to do with Chassis LCD and LED support.
+	  
+	  If unsure, say Y.
+
+config PDC_STABLE
+	tristate "PDC Stable Storage support"
+	depends on SYSFS
+	default y
+	help
+	  Say Y here if you want to enable support for accessing Stable Storage
+	  variables (PDC non volatile variables such as Primary Boot Path,
+	  Console Path, Autoboot, Autosearch, etc) through SysFS.
+	
+	  If unsure, say Y.
+	
+	  To compile this driver as a module, choose M here.
+	  The module will be called pdc_stable.
+
+endmenu
diff --git a/drivers/parisc/Makefile b/drivers/parisc/Makefile
new file mode 100644
index 0000000..f95cab5
--- /dev/null
+++ b/drivers/parisc/Makefile
@@ -0,0 +1,27 @@
+#
+# Makefile for most of the non-PCI devices in PA-RISC machines
+#
+
+# I/O SAPIC is also on IA64 platforms.
+# The two could be merged into a common source some day.
+obj-$(CONFIG_IOSAPIC)		+= iosapic.o
+obj-$(CONFIG_IOMMU_SBA)		+= sba_iommu.o
+obj-$(CONFIG_PCI_LBA)		+= lba_pci.o
+
+# Only use one of them: ccio-rm-dma is for PCX-W systems *only*
+# obj-$(CONFIG_IOMMU_CCIO)	+= ccio-rm-dma.o
+obj-$(CONFIG_IOMMU_CCIO)	+= ccio-dma.o
+
+obj-$(CONFIG_GSC)		+= gsc.o
+
+obj-$(CONFIG_HPPB)		+= hppb.o
+obj-$(CONFIG_GSC_DINO)		+= dino.o
+obj-$(CONFIG_GSC_LASI)		+= lasi.o asp.o
+obj-$(CONFIG_GSC_WAX)		+= wax.o
+obj-$(CONFIG_EISA)		+= eisa.o eisa_enumerator.o eisa_eeprom.o
+
+obj-$(CONFIG_SUPERIO)		+= superio.o
+obj-$(CONFIG_CHASSIS_LCD_LED)	+= led.o
+obj-$(CONFIG_PDC_STABLE)	+= pdc_stable.o
+obj-y				+= power.o
+
diff --git a/drivers/parisc/README.dino b/drivers/parisc/README.dino
new file mode 100644
index 0000000..097324f
--- /dev/null
+++ b/drivers/parisc/README.dino
@@ -0,0 +1,28 @@
+/*
+** HP VISUALIZE Workstation PCI Bus Defect
+**
+** "HP has discovered a potential system defect that can affect
+** the behavior of five models of HP VISUALIZE workstations when
+** equipped with third-party or customer-installed PCI I/O expansion
+** cards. The defect is limited to the HP C180, C160, C160L, B160L,
+** and B132L VISUALIZE workstations, and will only be encountered
+** when data is transmitted through PCI I/O expansion cards on the
+** PCI bus. HP-supplied graphics cards that utilize the PCI bus are
+** not affected."
+**
+** REVISIT: "go/pci_defect" link below is stale.
+**	HP Internal can use <http://hpfcdma.fc.hp.com:80/Dino/>
+**
+**	Product		First Good Serial Number
+**  C200/C240 (US)	US67350000
+**B132L+/B180 (US)	US67390000
+**   C200 (Europe)	3713G01000
+**  B180L (Europe)	3720G01000
+**
+** Note that many boards were fixed/replaced under a free replacement
+** program. Assume a machine is only "suspect" until proven otherwise.
+**
+** "The pci_check program will also be available as application
+**  patch PHSS_12295"
+*/
+
diff --git a/drivers/parisc/asp.c b/drivers/parisc/asp.c
new file mode 100644
index 0000000..38860996
--- /dev/null
+++ b/drivers/parisc/asp.c
@@ -0,0 +1,132 @@
+/*
+ *	ASP Device Driver
+ *
+ *	(c) Copyright 2000 The Puffin Group Inc.
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *	by Helge Deller <deller@gmx.de>
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/led.h>
+
+#include "gsc.h"
+
+#define ASP_GSC_IRQ	3		/* hardcoded interrupt for GSC */
+
+#define ASP_VER_OFFSET 	0x20		/* offset of ASP version */
+
+#define ASP_LED_ADDR	0xf0800020
+
+#define VIPER_INT_WORD  0xFFFBF088      /* addr of viper interrupt word */
+
+static struct gsc_asic asp;
+
+static void asp_choose_irq(struct parisc_device *dev, void *ctrl)
+{
+	int irq;
+
+	switch (dev->id.sversion) {
+	case 0x71:	irq =  9; break; /* SCSI */
+	case 0x72:	irq =  8; break; /* LAN */
+	case 0x73:	irq =  1; break; /* HIL */
+	case 0x74:	irq =  7; break; /* Centronics */
+	case 0x75:	irq = (dev->hw_path == 4) ? 5 : 6; break; /* RS232 */
+	case 0x76:	irq = 10; break; /* EISA BA */
+	case 0x77:	irq = 11; break; /* Graphics1 */
+	case 0x7a:	irq = 13; break; /* Audio (Bushmaster) */
+	case 0x7b:	irq = 13; break; /* Audio (Scorpio) */
+	case 0x7c:	irq =  3; break; /* FW SCSI */
+	case 0x7d:	irq =  4; break; /* FDDI */
+	case 0x7f:	irq = 13; break; /* Audio (Outfield) */
+	default:	return;		 /* Unknown */
+	}
+
+	gsc_asic_assign_irq(ctrl, irq, &dev->irq);
+
+	switch (dev->id.sversion) {
+	case 0x73:	irq =  2; break; /* i8042 High-priority */
+	case 0x76:	irq =  0; break; /* EISA BA */
+	default:	return;		 /* Other */
+	}
+
+	gsc_asic_assign_irq(ctrl, irq, &dev->aux_irq);
+}
+
+/* There are two register ranges we're interested in.  Interrupt /
+ * Status / LED are at 0xf080xxxx and Asp special registers are at
+ * 0xf082fxxx.  PDC only tells us that Asp is at 0xf082f000, so for
+ * the purposes of interrupt handling, we have to tell other bits of
+ * the kernel to look at the other registers.
+ */
+#define ASP_INTERRUPT_ADDR 0xf0800000
+
+int __init
+asp_init_chip(struct parisc_device *dev)
+{
+	struct gsc_irq gsc_irq;
+	int ret;
+
+	asp.version = gsc_readb(dev->hpa + ASP_VER_OFFSET) & 0xf;
+	asp.name = (asp.version == 1) ? "Asp" : "Cutoff";
+	asp.hpa = ASP_INTERRUPT_ADDR;
+
+	printk(KERN_INFO "%s version %d at 0x%lx found.\n", 
+		asp.name, asp.version, dev->hpa);
+
+	/* the IRQ ASP should use */
+	ret = -EBUSY;
+	dev->irq = gsc_claim_irq(&gsc_irq, ASP_GSC_IRQ);
+	if (dev->irq < 0) {
+		printk(KERN_ERR "%s(): cannot get GSC irq\n", __FUNCTION__);
+		goto out;
+	}
+
+	asp.eim = ((u32) gsc_irq.txn_addr) | gsc_irq.txn_data;
+
+	ret = request_irq(gsc_irq.irq, gsc_asic_intr, 0, "asp", &asp);
+	if (ret < 0)
+		goto out;
+
+	/* Program VIPER to interrupt on the ASP irq */
+	gsc_writel((1 << (31 - ASP_GSC_IRQ)),VIPER_INT_WORD);
+
+	/* Done init'ing, register this driver */
+	ret = gsc_common_setup(dev, &asp);
+	if (ret)
+		goto out;
+
+	gsc_fixup_irqs(dev, &asp, asp_choose_irq);
+	/* Mongoose is a sibling of Asp, not a child... */
+	gsc_fixup_irqs(parisc_parent(dev), &asp, asp_choose_irq);
+
+	/* initialize the chassis LEDs */ 
+#ifdef CONFIG_CHASSIS_LCD_LED	
+	register_led_driver(DISPLAY_MODEL_OLD_ASP, LED_CMD_REG_NONE, 
+		    ASP_LED_ADDR);
+#endif
+
+ out:
+	return ret;
+}
+
+static struct parisc_device_id asp_tbl[] = {
+	{ HPHW_BA, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00070 },
+	{ 0, }
+};
+
+struct parisc_driver asp_driver = {
+	.name =		"Asp",
+	.id_table =	asp_tbl,
+	.probe =	asp_init_chip,
+};
diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c
new file mode 100644
index 0000000..0e98a9d
--- /dev/null
+++ b/drivers/parisc/ccio-dma.c
@@ -0,0 +1,1593 @@
+/*
+** ccio-dma.c:
+**	DMA management routines for first generation cache-coherent machines.
+**	Program U2/Uturn in "Virtual Mode" and use the I/O MMU.
+**
+**	(c) Copyright 2000 Grant Grundler
+**	(c) Copyright 2000 Ryan Bradetich
+**	(c) Copyright 2000 Hewlett-Packard Company
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+**
+**  "Real Mode" operation refers to U2/Uturn chip operation.
+**  U2/Uturn were designed to perform coherency checks w/o using
+**  the I/O MMU - basically what x86 does.
+**
+**  Philipp Rumpf has a "Real Mode" driver for PCX-W machines at:
+**      CVSROOT=:pserver:anonymous@198.186.203.37:/cvsroot/linux-parisc
+**      cvs -z3 co linux/arch/parisc/kernel/dma-rm.c
+**
+**  I've rewritten his code to work under TPG's tree. See ccio-rm-dma.c.
+**
+**  Drawbacks of using Real Mode are:
+**	o outbound DMA is slower - U2 won't prefetch data (GSC+ XQL signal).
+**      o Inbound DMA less efficient - U2 can't use DMA_FAST attribute.
+**	o Ability to do scatter/gather in HW is lost.
+**	o Doesn't work under PCX-U/U+ machines since they didn't follow
+**        the coherency design originally worked out. Only PCX-W does.
+*/
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/reboot.h>
+
+#include <asm/byteorder.h>
+#include <asm/cache.h>		/* for L1_CACHE_BYTES */
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/hardware.h>       /* for register_module() */
+#include <asm/parisc-device.h>
+
+/* 
+** Choose "ccio" since that's what HP-UX calls it.
+** Make it easier for folks to migrate from one to the other :^)
+*/
+#define MODULE_NAME "ccio"
+
+#undef DEBUG_CCIO_RES
+#undef DEBUG_CCIO_RUN
+#undef DEBUG_CCIO_INIT
+#undef DEBUG_CCIO_RUN_SG
+
+#ifdef CONFIG_PROC_FS
+/*
+ * CCIO_SEARCH_TIME can help measure how fast the bitmap search is.
+ * impacts performance though - ditch it if you don't use it.
+ */
+#define CCIO_SEARCH_TIME
+#undef CCIO_MAP_STATS
+#else
+#undef CCIO_SEARCH_TIME
+#undef CCIO_MAP_STATS
+#endif
+
+#include <linux/proc_fs.h>
+#include <asm/runway.h>		/* for proc_runway_root */
+
+#ifdef DEBUG_CCIO_INIT
+#define DBG_INIT(x...)  printk(x)
+#else
+#define DBG_INIT(x...)
+#endif
+
+#ifdef DEBUG_CCIO_RUN
+#define DBG_RUN(x...)   printk(x)
+#else
+#define DBG_RUN(x...)
+#endif
+
+#ifdef DEBUG_CCIO_RES
+#define DBG_RES(x...)   printk(x)
+#else
+#define DBG_RES(x...)
+#endif
+
+#ifdef DEBUG_CCIO_RUN_SG
+#define DBG_RUN_SG(x...) printk(x)
+#else
+#define DBG_RUN_SG(x...)
+#endif
+
+#define CCIO_INLINE	/* inline */
+#define WRITE_U32(value, addr) gsc_writel(value, (u32 *)(addr))
+#define READ_U32(addr) gsc_readl((u32 *)(addr))
+
+#define U2_IOA_RUNWAY 0x580
+#define U2_BC_GSC     0x501
+#define UTURN_IOA_RUNWAY 0x581
+#define UTURN_BC_GSC     0x502
+
+#define IOA_NORMAL_MODE      0x00020080 /* IO_CONTROL to turn on CCIO        */
+#define CMD_TLB_DIRECT_WRITE 35         /* IO_COMMAND for I/O TLB Writes     */
+#define CMD_TLB_PURGE        33         /* IO_COMMAND to Purge I/O TLB entry */
+
+struct ioa_registers {
+        /* Runway Supervisory Set */
+        volatile int32_t    unused1[12];
+        volatile uint32_t   io_command;             /* Offset 12 */
+        volatile uint32_t   io_status;              /* Offset 13 */
+        volatile uint32_t   io_control;             /* Offset 14 */
+        volatile int32_t    unused2[1];
+
+        /* Runway Auxiliary Register Set */
+        volatile uint32_t   io_err_resp;            /* Offset  0 */
+        volatile uint32_t   io_err_info;            /* Offset  1 */
+        volatile uint32_t   io_err_req;             /* Offset  2 */
+        volatile uint32_t   io_err_resp_hi;         /* Offset  3 */
+        volatile uint32_t   io_tlb_entry_m;         /* Offset  4 */
+        volatile uint32_t   io_tlb_entry_l;         /* Offset  5 */
+        volatile uint32_t   unused3[1];
+        volatile uint32_t   io_pdir_base;           /* Offset  7 */
+        volatile uint32_t   io_io_low_hv;           /* Offset  8 */
+        volatile uint32_t   io_io_high_hv;          /* Offset  9 */
+        volatile uint32_t   unused4[1];
+        volatile uint32_t   io_chain_id_mask;       /* Offset 11 */
+        volatile uint32_t   unused5[2];
+        volatile uint32_t   io_io_low;              /* Offset 14 */
+        volatile uint32_t   io_io_high;             /* Offset 15 */
+};
+
+/*
+** IOA Registers
+** -------------
+**
+** Runway IO_CONTROL Register (+0x38)
+** 
+** The Runway IO_CONTROL register controls the forwarding of transactions.
+**
+** | 0  ...  13  |  14 15 | 16 ... 21 | 22 | 23 24 |  25 ... 31 |
+** |    HV       |   TLB  |  reserved | HV | mode  |  reserved  |
+**
+** o mode field indicates the address translation of transactions
+**   forwarded from Runway to GSC+:
+**       Mode Name     Value        Definition
+**       Off (default)   0          Opaque to matching addresses.
+**       Include         1          Transparent for matching addresses.
+**       Peek            3          Map matching addresses.
+**
+**       + "Off" mode: Runway transactions which match the I/O range
+**         specified by the IO_IO_LOW/IO_IO_HIGH registers will be ignored.
+**       + "Include" mode: all addresses within the I/O range specified
+**         by the IO_IO_LOW and IO_IO_HIGH registers are transparently
+**         forwarded. This is the I/O Adapter's normal operating mode.
+**       + "Peek" mode: used during system configuration to initialize the
+**         GSC+ bus. Runway Write_Shorts in the address range specified by
+**         IO_IO_LOW and IO_IO_HIGH are forwarded through the I/O Adapter
+**         *AND* the GSC+ address is remapped to the Broadcast Physical
+**         Address space by setting the 14 high order address bits of the
+**         32 bit GSC+ address to ones.
+**
+** o TLB field affects transactions which are forwarded from GSC+ to Runway.
+**   "Real" mode is the poweron default.
+** 
+**   TLB Mode  Value  Description
+**   Real        0    No TLB translation. Address is directly mapped and the
+**                    virtual address is composed of selected physical bits.
+**   Error       1    Software fills the TLB manually.
+**   Normal      2    IOA fetches IO TLB misses from IO PDIR (in host memory).
+**
+**
+** IO_IO_LOW_HV	  +0x60 (HV dependent)
+** IO_IO_HIGH_HV  +0x64 (HV dependent)
+** IO_IO_LOW      +0x78	(Architected register)
+** IO_IO_HIGH     +0x7c	(Architected register)
+**
+** IO_IO_LOW and IO_IO_HIGH set the lower and upper bounds of the
+** I/O Adapter address space, respectively.
+**
+** 0  ... 7 | 8 ... 15 |  16   ...   31 |
+** 11111111 | 11111111 |      address   |
+**
+** Each LOW/HIGH pair describes a disjoint address space region.
+** (2 per GSC+ port). Each incoming Runway transaction address is compared
+** with both sets of LOW/HIGH registers. If the address is in the range
+** greater than or equal to IO_IO_LOW and less than IO_IO_HIGH the transaction
+** for forwarded to the respective GSC+ bus.
+** Specify IO_IO_LOW equal to or greater than IO_IO_HIGH to avoid specifying
+** an address space region.
+**
+** In order for a Runway address to reside within GSC+ extended address space:
+**	Runway Address [0:7]    must identically compare to 8'b11111111
+**	Runway Address [8:11]   must be equal to IO_IO_LOW(_HV)[16:19]
+** 	Runway Address [12:23]  must be greater than or equal to
+**	           IO_IO_LOW(_HV)[20:31] and less than IO_IO_HIGH(_HV)[20:31].
+**	Runway Address [24:39]  is not used in the comparison.
+**
+** When the Runway transaction is forwarded to GSC+, the GSC+ address is
+** as follows:
+**	GSC+ Address[0:3]	4'b1111
+**	GSC+ Address[4:29]	Runway Address[12:37]
+**	GSC+ Address[30:31]	2'b00
+**
+** All 4 Low/High registers must be initialized (by PDC) once the lower bus
+** is interrogated and address space is defined. The operating system will
+** modify the architectural IO_IO_LOW and IO_IO_HIGH registers following
+** the PDC initialization.  However, the hardware version dependent IO_IO_LOW
+** and IO_IO_HIGH registers should not be subsequently altered by the OS.
+** 
+** Writes to both sets of registers will take effect immediately, bypassing
+** the queues, which ensures that subsequent Runway transactions are checked
+** against the updated bounds values. However reads are queued, introducing
+** the possibility of a read being bypassed by a subsequent write to the same
+** register. This sequence can be avoided by having software wait for read
+** returns before issuing subsequent writes.
+*/
+
+struct ioc {
+	struct ioa_registers *ioc_hpa;  /* I/O MMU base address */
+	u8  *res_map;	                /* resource map, bit == pdir entry */
+	u64 *pdir_base;	                /* physical base address */
+	u32 pdir_size; 			/* bytes, function of IOV Space size */
+	u32 res_hint;	                /* next available IOVP - 
+					   circular search */
+	u32 res_size;		    	/* size of resource map in bytes */
+	spinlock_t res_lock;
+
+#ifdef CCIO_SEARCH_TIME
+#define CCIO_SEARCH_SAMPLE 0x100
+	unsigned long avg_search[CCIO_SEARCH_SAMPLE];
+	unsigned long avg_idx;		  /* current index into avg_search */
+#endif
+#ifdef CCIO_MAP_STATS
+	unsigned long used_pages;
+	unsigned long msingle_calls;
+	unsigned long msingle_pages;
+	unsigned long msg_calls;
+	unsigned long msg_pages;
+	unsigned long usingle_calls;
+	unsigned long usingle_pages;
+	unsigned long usg_calls;
+	unsigned long usg_pages;
+#endif
+	unsigned short cujo20_bug;
+
+	/* STUFF We don't need in performance path */
+	u32 chainid_shift; 		/* specify bit location of chain_id */
+	struct ioc *next;		/* Linked list of discovered iocs */
+	const char *name;		/* device name from firmware */
+	unsigned int hw_path;           /* the hardware path this ioc is associatd with */
+	struct pci_dev *fake_pci_dev;   /* the fake pci_dev for non-pci devs */
+	struct resource mmio_region[2]; /* The "routed" MMIO regions */
+};
+
+static struct ioc *ioc_list;
+static int ioc_count;
+
+/**************************************************************
+*
+*   I/O Pdir Resource Management
+*
+*   Bits set in the resource map are in use.
+*   Each bit can represent a number of pages.
+*   LSbs represent lower addresses (IOVA's).
+*
+*   This was was copied from sba_iommu.c. Don't try to unify
+*   the two resource managers unless a way to have different
+*   allocation policies is also adjusted. We'd like to avoid
+*   I/O TLB thrashing by having resource allocation policy
+*   match the I/O TLB replacement policy.
+*
+***************************************************************/
+#define IOVP_SIZE PAGE_SIZE
+#define IOVP_SHIFT PAGE_SHIFT
+#define IOVP_MASK PAGE_MASK
+
+/* Convert from IOVP to IOVA and vice versa. */
+#define CCIO_IOVA(iovp,offset) ((iovp) | (offset))
+#define CCIO_IOVP(iova) ((iova) & IOVP_MASK)
+
+#define PDIR_INDEX(iovp)    ((iovp)>>IOVP_SHIFT)
+#define MKIOVP(pdir_idx)    ((long)(pdir_idx) << IOVP_SHIFT)
+#define MKIOVA(iovp,offset) (dma_addr_t)((long)iovp | (long)offset)
+#define ROUNDUP(x,y) ((x + ((y)-1)) & ~((y)-1))
+
+/*
+** Don't worry about the 150% average search length on a miss.
+** If the search wraps around, and passes the res_hint, it will
+** cause the kernel to panic anyhow.
+*/
+#define CCIO_SEARCH_LOOP(ioc, res_idx, mask, size)  \
+       for(; res_ptr < res_end; ++res_ptr) { \
+               if(0 == (*res_ptr & mask)) { \
+                       *res_ptr |= mask; \
+                       res_idx = (unsigned int)((unsigned long)res_ptr - (unsigned long)ioc->res_map); \
+                       ioc->res_hint = res_idx + (size >> 3); \
+                       goto resource_found; \
+               } \
+       }
+
+#define CCIO_FIND_FREE_MAPPING(ioa, res_idx, mask, size) \
+       u##size *res_ptr = (u##size *)&((ioc)->res_map[ioa->res_hint & ~((size >> 3) - 1)]); \
+       u##size *res_end = (u##size *)&(ioc)->res_map[ioa->res_size]; \
+       CCIO_SEARCH_LOOP(ioc, res_idx, mask, size); \
+       res_ptr = (u##size *)&(ioc)->res_map[0]; \
+       CCIO_SEARCH_LOOP(ioa, res_idx, mask, size);
+
+/*
+** Find available bit in this ioa's resource map.
+** Use a "circular" search:
+**   o Most IOVA's are "temporary" - avg search time should be small.
+** o keep a history of what happened for debugging
+** o KISS.
+**
+** Perf optimizations:
+** o search for log2(size) bits at a time.
+** o search for available resource bits using byte/word/whatever.
+** o use different search for "large" (eg > 4 pages) or "very large"
+**   (eg > 16 pages) mappings.
+*/
+
+/**
+ * ccio_alloc_range - Allocate pages in the ioc's resource map.
+ * @ioc: The I/O Controller.
+ * @pages_needed: The requested number of pages to be mapped into the
+ * I/O Pdir...
+ *
+ * This function searches the resource map of the ioc to locate a range
+ * of available pages for the requested size.
+ */
+static int
+ccio_alloc_range(struct ioc *ioc, size_t size)
+{
+	unsigned int pages_needed = size >> IOVP_SHIFT;
+	unsigned int res_idx;
+#ifdef CCIO_SEARCH_TIME
+	unsigned long cr_start = mfctl(16);
+#endif
+	
+	BUG_ON(pages_needed == 0);
+	BUG_ON((pages_needed * IOVP_SIZE) > DMA_CHUNK_SIZE);
+     
+	DBG_RES("%s() size: %d pages_needed %d\n", 
+		__FUNCTION__, size, pages_needed);
+
+	/*
+	** "seek and ye shall find"...praying never hurts either...
+	** ggg sacrifices another 710 to the computer gods.
+	*/
+
+	if (pages_needed <= 8) {
+		/*
+		 * LAN traffic will not thrash the TLB IFF the same NIC
+		 * uses 8 adjacent pages to map seperate payload data.
+		 * ie the same byte in the resource bit map.
+		 */
+#if 0
+		/* FIXME: bit search should shift it's way through
+		 * an unsigned long - not byte at a time. As it is now,
+		 * we effectively allocate this byte to this mapping.
+		 */
+		unsigned long mask = ~(~0UL >> pages_needed);
+		CCIO_FIND_FREE_MAPPING(ioc, res_idx, mask, 8);
+#else
+		CCIO_FIND_FREE_MAPPING(ioc, res_idx, 0xff, 8);
+#endif
+	} else if (pages_needed <= 16) {
+		CCIO_FIND_FREE_MAPPING(ioc, res_idx, 0xffff, 16);
+	} else if (pages_needed <= 32) {
+		CCIO_FIND_FREE_MAPPING(ioc, res_idx, ~(unsigned int)0, 32);
+#ifdef __LP64__
+	} else if (pages_needed <= 64) {
+		CCIO_FIND_FREE_MAPPING(ioc, res_idx, ~0UL, 64);
+#endif
+	} else {
+		panic("%s: %s() Too many pages to map. pages_needed: %u\n",
+		       __FILE__,  __FUNCTION__, pages_needed);
+	}
+
+	panic("%s: %s() I/O MMU is out of mapping resources.\n", __FILE__,
+	      __FUNCTION__);
+	
+resource_found:
+	
+	DBG_RES("%s() res_idx %d res_hint: %d\n",
+		__FUNCTION__, res_idx, ioc->res_hint);
+
+#ifdef CCIO_SEARCH_TIME
+	{
+		unsigned long cr_end = mfctl(16);
+		unsigned long tmp = cr_end - cr_start;
+		/* check for roll over */
+		cr_start = (cr_end < cr_start) ?  -(tmp) : (tmp);
+	}
+	ioc->avg_search[ioc->avg_idx++] = cr_start;
+	ioc->avg_idx &= CCIO_SEARCH_SAMPLE - 1;
+#endif
+#ifdef CCIO_MAP_STATS
+	ioc->used_pages += pages_needed;
+#endif
+	/* 
+	** return the bit address.
+	*/
+	return res_idx << 3;
+}
+
+#define CCIO_FREE_MAPPINGS(ioc, res_idx, mask, size) \
+        u##size *res_ptr = (u##size *)&((ioc)->res_map[res_idx]); \
+        BUG_ON((*res_ptr & mask) != mask); \
+        *res_ptr &= ~(mask);
+
+/**
+ * ccio_free_range - Free pages from the ioc's resource map.
+ * @ioc: The I/O Controller.
+ * @iova: The I/O Virtual Address.
+ * @pages_mapped: The requested number of pages to be freed from the
+ * I/O Pdir.
+ *
+ * This function frees the resouces allocated for the iova.
+ */
+static void
+ccio_free_range(struct ioc *ioc, dma_addr_t iova, unsigned long pages_mapped)
+{
+	unsigned long iovp = CCIO_IOVP(iova);
+	unsigned int res_idx = PDIR_INDEX(iovp) >> 3;
+
+	BUG_ON(pages_mapped == 0);
+	BUG_ON((pages_mapped * IOVP_SIZE) > DMA_CHUNK_SIZE);
+	BUG_ON(pages_mapped > BITS_PER_LONG);
+
+	DBG_RES("%s():  res_idx: %d pages_mapped %d\n", 
+		__FUNCTION__, res_idx, pages_mapped);
+
+#ifdef CCIO_MAP_STATS
+	ioc->used_pages -= pages_mapped;
+#endif
+
+	if(pages_mapped <= 8) {
+#if 0
+		/* see matching comments in alloc_range */
+		unsigned long mask = ~(~0UL >> pages_mapped);
+		CCIO_FREE_MAPPINGS(ioc, res_idx, mask, 8);
+#else
+		CCIO_FREE_MAPPINGS(ioc, res_idx, 0xff, 8);
+#endif
+	} else if(pages_mapped <= 16) {
+		CCIO_FREE_MAPPINGS(ioc, res_idx, 0xffff, 16);
+	} else if(pages_mapped <= 32) {
+		CCIO_FREE_MAPPINGS(ioc, res_idx, ~(unsigned int)0, 32);
+#ifdef __LP64__
+	} else if(pages_mapped <= 64) {
+		CCIO_FREE_MAPPINGS(ioc, res_idx, ~0UL, 64);
+#endif
+	} else {
+		panic("%s:%s() Too many pages to unmap.\n", __FILE__,
+		      __FUNCTION__);
+	}
+}
+
+/****************************************************************
+**
+**          CCIO dma_ops support routines
+**
+*****************************************************************/
+
+typedef unsigned long space_t;
+#define KERNEL_SPACE 0
+
+/*
+** DMA "Page Type" and Hints 
+** o if SAFE_DMA isn't set, mapping is for FAST_DMA. SAFE_DMA should be
+**   set for subcacheline DMA transfers since we don't want to damage the
+**   other part of a cacheline.
+** o SAFE_DMA must be set for "memory" allocated via pci_alloc_consistent().
+**   This bit tells U2 to do R/M/W for partial cachelines. "Streaming"
+**   data can avoid this if the mapping covers full cache lines.
+** o STOP_MOST is needed for atomicity across cachelines.
+**   Apperently only "some EISA devices" need this.
+**   Using CONFIG_ISA is hack. Only the IOA with EISA under it needs
+**   to use this hint iff the EISA devices needs this feature.
+**   According to the U2 ERS, STOP_MOST enabled pages hurt performance.
+** o PREFETCH should *not* be set for cases like Multiple PCI devices
+**   behind GSCtoPCI (dino) bus converter. Only one cacheline per GSC
+**   device can be fetched and multiply DMA streams will thrash the
+**   prefetch buffer and burn memory bandwidth. See 6.7.3 "Prefetch Rules
+**   and Invalidation of Prefetch Entries".
+**
+** FIXME: the default hints need to be per GSC device - not global.
+** 
+** HP-UX dorks: linux device driver programming model is totally different
+**    than HP-UX's. HP-UX always sets HINT_PREFETCH since it's drivers
+**    do special things to work on non-coherent platforms...linux has to
+**    be much more careful with this.
+*/
+#define IOPDIR_VALID    0x01UL
+#define HINT_SAFE_DMA   0x02UL	/* used for pci_alloc_consistent() pages */
+#ifdef CONFIG_EISA
+#define HINT_STOP_MOST  0x04UL	/* LSL support */
+#else
+#define HINT_STOP_MOST  0x00UL	/* only needed for "some EISA devices" */
+#endif
+#define HINT_UDPATE_ENB 0x08UL  /* not used/supported by U2 */
+#define HINT_PREFETCH   0x10UL	/* for outbound pages which are not SAFE */
+
+
+/*
+** Use direction (ie PCI_DMA_TODEVICE) to pick hint.
+** ccio_alloc_consistent() depends on this to get SAFE_DMA
+** when it passes in BIDIRECTIONAL flag.
+*/
+static u32 hint_lookup[] = {
+	[PCI_DMA_BIDIRECTIONAL]	= HINT_STOP_MOST | HINT_SAFE_DMA | IOPDIR_VALID,
+	[PCI_DMA_TODEVICE]	= HINT_STOP_MOST | HINT_PREFETCH | IOPDIR_VALID,
+	[PCI_DMA_FROMDEVICE]	= HINT_STOP_MOST | IOPDIR_VALID,
+};
+
+/**
+ * ccio_io_pdir_entry - Initialize an I/O Pdir.
+ * @pdir_ptr: A pointer into I/O Pdir.
+ * @sid: The Space Identifier.
+ * @vba: The virtual address.
+ * @hints: The DMA Hint.
+ *
+ * Given a virtual address (vba, arg2) and space id, (sid, arg1),
+ * load the I/O PDIR entry pointed to by pdir_ptr (arg0). Each IO Pdir
+ * entry consists of 8 bytes as shown below (MSB == bit 0):
+ *
+ *
+ * WORD 0:
+ * +------+----------------+-----------------------------------------------+
+ * | Phys | Virtual Index  |               Phys                            |
+ * | 0:3  |     0:11       |               4:19                            |
+ * |4 bits|   12 bits      |              16 bits                          |
+ * +------+----------------+-----------------------------------------------+
+ * WORD 1:
+ * +-----------------------+-----------------------------------------------+
+ * |      Phys    |  Rsvd  | Prefetch |Update |Rsvd  |Lock  |Safe  |Valid  |
+ * |     20:39    |        | Enable   |Enable |      |Enable|DMA   |       |
+ * |    20 bits   | 5 bits | 1 bit    |1 bit  |2 bits|1 bit |1 bit |1 bit  |
+ * +-----------------------+-----------------------------------------------+
+ *
+ * The virtual index field is filled with the results of the LCI
+ * (Load Coherence Index) instruction.  The 8 bits used for the virtual
+ * index are bits 12:19 of the value returned by LCI.
+ */ 
+void CCIO_INLINE
+ccio_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba,
+		   unsigned long hints)
+{
+	register unsigned long pa;
+	register unsigned long ci; /* coherent index */
+
+	/* We currently only support kernel addresses */
+	BUG_ON(sid != KERNEL_SPACE);
+
+	mtsp(sid,1);
+
+	/*
+	** WORD 1 - low order word
+	** "hints" parm includes the VALID bit!
+	** "dep" clobbers the physical address offset bits as well.
+	*/
+	pa = virt_to_phys(vba);
+	asm volatile("depw  %1,31,12,%0" : "+r" (pa) : "r" (hints));
+	((u32 *)pdir_ptr)[1] = (u32) pa;
+
+	/*
+	** WORD 0 - high order word
+	*/
+
+#ifdef __LP64__
+	/*
+	** get bits 12:15 of physical address
+	** shift bits 16:31 of physical address
+	** and deposit them
+	*/
+	asm volatile ("extrd,u %1,15,4,%0" : "=r" (ci) : "r" (pa));
+	asm volatile ("extrd,u %1,31,16,%0" : "+r" (pa) : "r" (pa));
+	asm volatile ("depd  %1,35,4,%0" : "+r" (pa) : "r" (ci));
+#else
+	pa = 0;
+#endif
+	/*
+	** get CPU coherency index bits
+	** Grab virtual index [0:11]
+	** Deposit virt_idx bits into I/O PDIR word
+	*/
+	asm volatile ("lci 0(%%sr1, %1), %0" : "=r" (ci) : "r" (vba));
+	asm volatile ("extru %1,19,12,%0" : "+r" (ci) : "r" (ci));
+	asm volatile ("depw  %1,15,12,%0" : "+r" (pa) : "r" (ci));
+
+	((u32 *)pdir_ptr)[0] = (u32) pa;
+
+
+	/* FIXME: PCX_W platforms don't need FDC/SYNC. (eg C360)
+	**        PCX-U/U+ do. (eg C200/C240)
+	**        PCX-T'? Don't know. (eg C110 or similar K-class)
+	**
+	** See PDC_MODEL/option 0/SW_CAP word for "Non-coherent IO-PDIR bit".
+	** Hopefully we can patch (NOP) these out at boot time somehow.
+	**
+	** "Since PCX-U employs an offset hash that is incompatible with
+	** the real mode coherence index generation of U2, the PDIR entry
+	** must be flushed to memory to retain coherence."
+	*/
+	asm volatile("fdc 0(%0)" : : "r" (pdir_ptr));
+	asm volatile("sync");
+}
+
+/**
+ * ccio_clear_io_tlb - Remove stale entries from the I/O TLB.
+ * @ioc: The I/O Controller.
+ * @iovp: The I/O Virtual Page.
+ * @byte_cnt: The requested number of bytes to be freed from the I/O Pdir.
+ *
+ * Purge invalid I/O PDIR entries from the I/O TLB.
+ *
+ * FIXME: Can we change the byte_cnt to pages_mapped?
+ */
+static CCIO_INLINE void
+ccio_clear_io_tlb(struct ioc *ioc, dma_addr_t iovp, size_t byte_cnt)
+{
+	u32 chain_size = 1 << ioc->chainid_shift;
+
+	iovp &= IOVP_MASK;	/* clear offset bits, just want pagenum */
+	byte_cnt += chain_size;
+
+	while(byte_cnt > chain_size) {
+		WRITE_U32(CMD_TLB_PURGE | iovp, &ioc->ioc_hpa->io_command);
+		iovp += chain_size;
+		byte_cnt -= chain_size;
+	}
+}
+
+/**
+ * ccio_mark_invalid - Mark the I/O Pdir entries invalid.
+ * @ioc: The I/O Controller.
+ * @iova: The I/O Virtual Address.
+ * @byte_cnt: The requested number of bytes to be freed from the I/O Pdir.
+ *
+ * Mark the I/O Pdir entries invalid and blow away the corresponding I/O
+ * TLB entries.
+ *
+ * FIXME: at some threshhold it might be "cheaper" to just blow
+ *        away the entire I/O TLB instead of individual entries.
+ *
+ * FIXME: Uturn has 256 TLB entries. We don't need to purge every
+ *        PDIR entry - just once for each possible TLB entry.
+ *        (We do need to maker I/O PDIR entries invalid regardless).
+ *
+ * FIXME: Can we change byte_cnt to pages_mapped?
+ */ 
+static CCIO_INLINE void
+ccio_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt)
+{
+	u32 iovp = (u32)CCIO_IOVP(iova);
+	size_t saved_byte_cnt;
+
+	/* round up to nearest page size */
+	saved_byte_cnt = byte_cnt = ROUNDUP(byte_cnt, IOVP_SIZE);
+
+	while(byte_cnt > 0) {
+		/* invalidate one page at a time */
+		unsigned int idx = PDIR_INDEX(iovp);
+		char *pdir_ptr = (char *) &(ioc->pdir_base[idx]);
+
+		BUG_ON(idx >= (ioc->pdir_size / sizeof(u64)));
+		pdir_ptr[7] = 0;	/* clear only VALID bit */ 
+		/*
+		** FIXME: PCX_W platforms don't need FDC/SYNC. (eg C360)
+		**   PCX-U/U+ do. (eg C200/C240)
+		** See PDC_MODEL/option 0/SW_CAP for "Non-coherent IO-PDIR bit".
+		**
+		** Hopefully someone figures out how to patch (NOP) the
+		** FDC/SYNC out at boot time.
+		*/
+		asm volatile("fdc 0(%0)" : : "r" (pdir_ptr[7]));
+
+		iovp     += IOVP_SIZE;
+		byte_cnt -= IOVP_SIZE;
+	}
+
+	asm volatile("sync");
+	ccio_clear_io_tlb(ioc, CCIO_IOVP(iova), saved_byte_cnt);
+}
+
+/****************************************************************
+**
+**          CCIO dma_ops
+**
+*****************************************************************/
+
+/**
+ * ccio_dma_supported - Verify the IOMMU supports the DMA address range.
+ * @dev: The PCI device.
+ * @mask: A bit mask describing the DMA address range of the device.
+ *
+ * This function implements the pci_dma_supported function.
+ */
+static int 
+ccio_dma_supported(struct device *dev, u64 mask)
+{
+	if(dev == NULL) {
+		printk(KERN_ERR MODULE_NAME ": EISA/ISA/et al not supported\n");
+		BUG();
+		return 0;
+	}
+
+	/* only support 32-bit devices (ie PCI/GSC) */
+	return (int)(mask == 0xffffffffUL);
+}
+
+/**
+ * ccio_map_single - Map an address range into the IOMMU.
+ * @dev: The PCI device.
+ * @addr: The start address of the DMA region.
+ * @size: The length of the DMA region.
+ * @direction: The direction of the DMA transaction (to/from device).
+ *
+ * This function implements the pci_map_single function.
+ */
+static dma_addr_t 
+ccio_map_single(struct device *dev, void *addr, size_t size,
+		enum dma_data_direction direction)
+{
+	int idx;
+	struct ioc *ioc;
+	unsigned long flags;
+	dma_addr_t iovp;
+	dma_addr_t offset;
+	u64 *pdir_start;
+	unsigned long hint = hint_lookup[(int)direction];
+
+	BUG_ON(!dev);
+	ioc = GET_IOC(dev);
+
+	BUG_ON(size <= 0);
+
+	/* save offset bits */
+	offset = ((unsigned long) addr) & ~IOVP_MASK;
+
+	/* round up to nearest IOVP_SIZE */
+	size = ROUNDUP(size + offset, IOVP_SIZE);
+	spin_lock_irqsave(&ioc->res_lock, flags);
+
+#ifdef CCIO_MAP_STATS
+	ioc->msingle_calls++;
+	ioc->msingle_pages += size >> IOVP_SHIFT;
+#endif
+
+	idx = ccio_alloc_range(ioc, size);
+	iovp = (dma_addr_t)MKIOVP(idx);
+
+	pdir_start = &(ioc->pdir_base[idx]);
+
+	DBG_RUN("%s() 0x%p -> 0x%lx size: %0x%x\n",
+		__FUNCTION__, addr, (long)iovp | offset, size);
+
+	/* If not cacheline aligned, force SAFE_DMA on the whole mess */
+	if((size % L1_CACHE_BYTES) || ((unsigned long)addr % L1_CACHE_BYTES))
+		hint |= HINT_SAFE_DMA;
+
+	while(size > 0) {
+		ccio_io_pdir_entry(pdir_start, KERNEL_SPACE, (unsigned long)addr, hint);
+
+		DBG_RUN(" pdir %p %08x%08x\n",
+			pdir_start,
+			(u32) (((u32 *) pdir_start)[0]),
+			(u32) (((u32 *) pdir_start)[1]));
+		++pdir_start;
+		addr += IOVP_SIZE;
+		size -= IOVP_SIZE;
+	}
+
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+
+	/* form complete address */
+	return CCIO_IOVA(iovp, offset);
+}
+
+/**
+ * ccio_unmap_single - Unmap an address range from the IOMMU.
+ * @dev: The PCI device.
+ * @addr: The start address of the DMA region.
+ * @size: The length of the DMA region.
+ * @direction: The direction of the DMA transaction (to/from device).
+ *
+ * This function implements the pci_unmap_single function.
+ */
+static void 
+ccio_unmap_single(struct device *dev, dma_addr_t iova, size_t size, 
+		  enum dma_data_direction direction)
+{
+	struct ioc *ioc;
+	unsigned long flags; 
+	dma_addr_t offset = iova & ~IOVP_MASK;
+	
+	BUG_ON(!dev);
+	ioc = GET_IOC(dev);
+
+	DBG_RUN("%s() iovp 0x%lx/%x\n",
+		__FUNCTION__, (long)iova, size);
+
+	iova ^= offset;        /* clear offset bits */
+	size += offset;
+	size = ROUNDUP(size, IOVP_SIZE);
+
+	spin_lock_irqsave(&ioc->res_lock, flags);
+
+#ifdef CCIO_MAP_STATS
+	ioc->usingle_calls++;
+	ioc->usingle_pages += size >> IOVP_SHIFT;
+#endif
+
+	ccio_mark_invalid(ioc, iova, size);
+	ccio_free_range(ioc, iova, (size >> IOVP_SHIFT));
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+}
+
+/**
+ * ccio_alloc_consistent - Allocate a consistent DMA mapping.
+ * @dev: The PCI device.
+ * @size: The length of the DMA region.
+ * @dma_handle: The DMA address handed back to the device (not the cpu).
+ *
+ * This function implements the pci_alloc_consistent function.
+ */
+static void * 
+ccio_alloc_consistent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag)
+{
+      void *ret;
+#if 0
+/* GRANT Need to establish hierarchy for non-PCI devs as well
+** and then provide matching gsc_map_xxx() functions for them as well.
+*/
+	if(!hwdev) {
+		/* only support PCI */
+		*dma_handle = 0;
+		return 0;
+	}
+#endif
+        ret = (void *) __get_free_pages(flag, get_order(size));
+
+	if (ret) {
+		memset(ret, 0, size);
+		*dma_handle = ccio_map_single(dev, ret, size, PCI_DMA_BIDIRECTIONAL);
+	}
+
+	return ret;
+}
+
+/**
+ * ccio_free_consistent - Free a consistent DMA mapping.
+ * @dev: The PCI device.
+ * @size: The length of the DMA region.
+ * @cpu_addr: The cpu address returned from the ccio_alloc_consistent.
+ * @dma_handle: The device address returned from the ccio_alloc_consistent.
+ *
+ * This function implements the pci_free_consistent function.
+ */
+static void 
+ccio_free_consistent(struct device *dev, size_t size, void *cpu_addr, 
+		     dma_addr_t dma_handle)
+{
+	ccio_unmap_single(dev, dma_handle, size, 0);
+	free_pages((unsigned long)cpu_addr, get_order(size));
+}
+
+/*
+** Since 0 is a valid pdir_base index value, can't use that
+** to determine if a value is valid or not. Use a flag to indicate
+** the SG list entry contains a valid pdir index.
+*/
+#define PIDE_FLAG 0x80000000UL
+
+#ifdef CCIO_MAP_STATS
+#define IOMMU_MAP_STATS
+#endif
+#include "iommu-helpers.h"
+
+/**
+ * ccio_map_sg - Map the scatter/gather list into the IOMMU.
+ * @dev: The PCI device.
+ * @sglist: The scatter/gather list to be mapped in the IOMMU.
+ * @nents: The number of entries in the scatter/gather list.
+ * @direction: The direction of the DMA transaction (to/from device).
+ *
+ * This function implements the pci_map_sg function.
+ */
+static int
+ccio_map_sg(struct device *dev, struct scatterlist *sglist, int nents, 
+	    enum dma_data_direction direction)
+{
+	struct ioc *ioc;
+	int coalesced, filled = 0;
+	unsigned long flags;
+	unsigned long hint = hint_lookup[(int)direction];
+	unsigned long prev_len = 0, current_len = 0;
+	int i;
+	
+	BUG_ON(!dev);
+	ioc = GET_IOC(dev);
+	
+	DBG_RUN_SG("%s() START %d entries\n", __FUNCTION__, nents);
+
+	/* Fast path single entry scatterlists. */
+	if (nents == 1) {
+		sg_dma_address(sglist) = ccio_map_single(dev,
+				(void *)sg_virt_addr(sglist), sglist->length,
+				direction);
+		sg_dma_len(sglist) = sglist->length;
+		return 1;
+	}
+
+	for(i = 0; i < nents; i++)
+		prev_len += sglist[i].length;
+	
+	spin_lock_irqsave(&ioc->res_lock, flags);
+
+#ifdef CCIO_MAP_STATS
+	ioc->msg_calls++;
+#endif
+
+	/*
+	** First coalesce the chunks and allocate I/O pdir space
+	**
+	** If this is one DMA stream, we can properly map using the
+	** correct virtual address associated with each DMA page.
+	** w/o this association, we wouldn't have coherent DMA!
+	** Access to the virtual address is what forces a two pass algorithm.
+	*/
+	coalesced = iommu_coalesce_chunks(ioc, sglist, nents, ccio_alloc_range);
+
+	/*
+	** Program the I/O Pdir
+	**
+	** map the virtual addresses to the I/O Pdir
+	** o dma_address will contain the pdir index
+	** o dma_len will contain the number of bytes to map 
+	** o page/offset contain the virtual address.
+	*/
+	filled = iommu_fill_pdir(ioc, sglist, nents, hint, ccio_io_pdir_entry);
+
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+
+	BUG_ON(coalesced != filled);
+
+	DBG_RUN_SG("%s() DONE %d mappings\n", __FUNCTION__, filled);
+
+	for (i = 0; i < filled; i++)
+		current_len += sg_dma_len(sglist + i);
+
+	BUG_ON(current_len != prev_len);
+
+	return filled;
+}
+
+/**
+ * ccio_unmap_sg - Unmap the scatter/gather list from the IOMMU.
+ * @dev: The PCI device.
+ * @sglist: The scatter/gather list to be unmapped from the IOMMU.
+ * @nents: The number of entries in the scatter/gather list.
+ * @direction: The direction of the DMA transaction (to/from device).
+ *
+ * This function implements the pci_unmap_sg function.
+ */
+static void 
+ccio_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents, 
+	      enum dma_data_direction direction)
+{
+	struct ioc *ioc;
+
+	BUG_ON(!dev);
+	ioc = GET_IOC(dev);
+
+	DBG_RUN_SG("%s() START %d entries,  %08lx,%x\n",
+		__FUNCTION__, nents, sg_virt_addr(sglist), sglist->length);
+
+#ifdef CCIO_MAP_STATS
+	ioc->usg_calls++;
+#endif
+
+	while(sg_dma_len(sglist) && nents--) {
+
+#ifdef CCIO_MAP_STATS
+		ioc->usg_pages += sg_dma_len(sglist) >> PAGE_SHIFT;
+#endif
+		ccio_unmap_single(dev, sg_dma_address(sglist),
+				  sg_dma_len(sglist), direction);
+		++sglist;
+	}
+
+	DBG_RUN_SG("%s() DONE (nents %d)\n", __FUNCTION__, nents);
+}
+
+static struct hppa_dma_ops ccio_ops = {
+	.dma_supported =	ccio_dma_supported,
+	.alloc_consistent =	ccio_alloc_consistent,
+	.alloc_noncoherent =	ccio_alloc_consistent,
+	.free_consistent =	ccio_free_consistent,
+	.map_single =		ccio_map_single,
+	.unmap_single =		ccio_unmap_single,
+	.map_sg = 		ccio_map_sg,
+	.unmap_sg = 		ccio_unmap_sg,
+	.dma_sync_single_for_cpu =	NULL,	/* NOP for U2/Uturn */
+	.dma_sync_single_for_device =	NULL,	/* NOP for U2/Uturn */
+	.dma_sync_sg_for_cpu =		NULL,	/* ditto */
+	.dma_sync_sg_for_device =		NULL,	/* ditto */
+};
+
+#ifdef CONFIG_PROC_FS
+static int proc_append(char *src, int len, char **dst, off_t *offset, int *max)
+{
+	if (len < *offset) {
+		*offset -= len;
+		return 0;
+	}
+	if (*offset > 0) {
+		src += *offset;
+		len -= *offset;
+		*offset = 0;
+	}
+	if (len > *max) {
+		len = *max;
+	}
+	memcpy(*dst, src, len);
+	*dst += len;
+	*max -= len;
+	return (*max == 0);
+}
+
+static int ccio_proc_info(char *buf, char **start, off_t offset, int count,
+			  int *eof, void *data)
+{
+	int max = count;
+	char tmp[80]; /* width of an ANSI-standard terminal */
+	struct ioc *ioc = ioc_list;
+
+	while (ioc != NULL) {
+		unsigned int total_pages = ioc->res_size << 3;
+		unsigned long avg = 0, min, max;
+		int j, len;
+
+		len = sprintf(tmp, "%s\n", ioc->name);
+		if (proc_append(tmp, len, &buf, &offset, &count))
+			break;
+		
+		len = sprintf(tmp, "Cujo 2.0 bug    : %s\n",
+			      (ioc->cujo20_bug ? "yes" : "no"));
+		if (proc_append(tmp, len, &buf, &offset, &count))
+			break;
+		
+		len = sprintf(tmp, "IO PDIR size    : %d bytes (%d entries)\n",
+			      total_pages * 8, total_pages);
+		if (proc_append(tmp, len, &buf, &offset, &count))
+			break;
+#ifdef CCIO_MAP_STATS
+		len = sprintf(tmp, "IO PDIR entries : %ld free  %ld used (%d%%)\n",
+			      total_pages - ioc->used_pages, ioc->used_pages,
+			      (int)(ioc->used_pages * 100 / total_pages));
+		if (proc_append(tmp, len, &buf, &offset, &count))
+			break;
+#endif
+		len = sprintf(tmp, "Resource bitmap : %d bytes (%d pages)\n", 
+			ioc->res_size, total_pages);
+		if (proc_append(tmp, len, &buf, &offset, &count))
+			break;
+#ifdef CCIO_SEARCH_TIME
+		min = max = ioc->avg_search[0];
+		for(j = 0; j < CCIO_SEARCH_SAMPLE; ++j) {
+			avg += ioc->avg_search[j];
+			if(ioc->avg_search[j] > max) 
+				max = ioc->avg_search[j];
+			if(ioc->avg_search[j] < min) 
+				min = ioc->avg_search[j];
+		}
+		avg /= CCIO_SEARCH_SAMPLE;
+		len = sprintf(tmp, "  Bitmap search : %ld/%ld/%ld (min/avg/max CPU Cycles)\n",
+			      min, avg, max);
+		if (proc_append(tmp, len, &buf, &offset, &count))
+			break;
+#endif
+#ifdef CCIO_MAP_STATS
+		len = sprintf(tmp, "pci_map_single(): %8ld calls  %8ld pages (avg %d/1000)\n",
+			      ioc->msingle_calls, ioc->msingle_pages,
+			      (int)((ioc->msingle_pages * 1000)/ioc->msingle_calls));
+		if (proc_append(tmp, len, &buf, &offset, &count))
+			break;
+		
+
+		/* KLUGE - unmap_sg calls unmap_single for each mapped page */
+		min = ioc->usingle_calls - ioc->usg_calls;
+		max = ioc->usingle_pages - ioc->usg_pages;
+		len = sprintf(tmp, "pci_unmap_single: %8ld calls  %8ld pages (avg %d/1000)\n",
+			      min, max, (int)((max * 1000)/min));
+		if (proc_append(tmp, len, &buf, &offset, &count))
+			break;
+ 
+		len = sprintf(tmp, "pci_map_sg()    : %8ld calls  %8ld pages (avg %d/1000)\n",
+			      ioc->msg_calls, ioc->msg_pages,
+			      (int)((ioc->msg_pages * 1000)/ioc->msg_calls));
+		if (proc_append(tmp, len, &buf, &offset, &count))
+			break;
+		len = sprintf(tmp, "pci_unmap_sg()  : %8ld calls  %8ld pages (avg %d/1000)\n\n\n",
+			      ioc->usg_calls, ioc->usg_pages,
+			      (int)((ioc->usg_pages * 1000)/ioc->usg_calls));
+		if (proc_append(tmp, len, &buf, &offset, &count))
+			break;
+#endif	/* CCIO_MAP_STATS */
+		ioc = ioc->next;
+	}
+
+	if (count == 0) {
+		*eof = 1;
+	}
+	return (max - count);
+}
+
+static int ccio_resource_map(char *buf, char **start, off_t offset, int len,
+			     int *eof, void *data)
+{
+	struct ioc *ioc = ioc_list;
+
+	buf[0] = '\0';
+	while (ioc != NULL) {
+		u32 *res_ptr = (u32 *)ioc->res_map;
+		int j;
+
+		for (j = 0; j < (ioc->res_size / sizeof(u32)); j++) {
+			if ((j & 7) == 0)
+				strcat(buf,"\n   ");
+			sprintf(buf, "%s %08x", buf, *res_ptr);
+			res_ptr++;
+		}
+		strcat(buf, "\n\n");
+		ioc = ioc->next;
+		break; /* XXX - remove me */
+	}
+
+	return strlen(buf);
+}
+#endif
+
+/**
+ * ccio_find_ioc - Find the ioc in the ioc_list
+ * @hw_path: The hardware path of the ioc.
+ *
+ * This function searches the ioc_list for an ioc that matches
+ * the provide hardware path.
+ */
+static struct ioc * ccio_find_ioc(int hw_path)
+{
+	int i;
+	struct ioc *ioc;
+
+	ioc = ioc_list;
+	for (i = 0; i < ioc_count; i++) {
+		if (ioc->hw_path == hw_path)
+			return ioc;
+
+		ioc = ioc->next;
+	}
+
+	return NULL;
+}
+
+/**
+ * ccio_get_iommu - Find the iommu which controls this device
+ * @dev: The parisc device.
+ *
+ * This function searches through the registered IOMMU's and returns
+ * the appropriate IOMMU for the device based on its hardware path.
+ */
+void * ccio_get_iommu(const struct parisc_device *dev)
+{
+	dev = find_pa_parent_type(dev, HPHW_IOA);
+	if (!dev)
+		return NULL;
+
+	return ccio_find_ioc(dev->hw_path);
+}
+
+#define CUJO_20_STEP       0x10000000	/* inc upper nibble */
+
+/* Cujo 2.0 has a bug which will silently corrupt data being transferred
+ * to/from certain pages.  To avoid this happening, we mark these pages
+ * as `used', and ensure that nothing will try to allocate from them.
+ */
+void ccio_cujo20_fixup(struct parisc_device *cujo, u32 iovp)
+{
+	unsigned int idx;
+	struct parisc_device *dev = parisc_parent(cujo);
+	struct ioc *ioc = ccio_get_iommu(dev);
+	u8 *res_ptr;
+
+	ioc->cujo20_bug = 1;
+	res_ptr = ioc->res_map;
+	idx = PDIR_INDEX(iovp) >> 3;
+
+	while (idx < ioc->res_size) {
+ 		res_ptr[idx] |= 0xff;
+		idx += PDIR_INDEX(CUJO_20_STEP) >> 3;
+	}
+}
+
+#if 0
+/* GRANT -  is this needed for U2 or not? */
+
+/*
+** Get the size of the I/O TLB for this I/O MMU.
+**
+** If spa_shift is non-zero (ie probably U2),
+** then calculate the I/O TLB size using spa_shift.
+**
+** Otherwise we are supposed to get the IODC entry point ENTRY TLB
+** and execute it. However, both U2 and Uturn firmware supplies spa_shift.
+** I think only Java (K/D/R-class too?) systems don't do this.
+*/
+static int
+ccio_get_iotlb_size(struct parisc_device *dev)
+{
+	if (dev->spa_shift == 0) {
+		panic("%s() : Can't determine I/O TLB size.\n", __FUNCTION__);
+	}
+	return (1 << dev->spa_shift);
+}
+#else
+
+/* Uturn supports 256 TLB entries */
+#define CCIO_CHAINID_SHIFT	8
+#define CCIO_CHAINID_MASK	0xff
+#endif /* 0 */
+
+/* We *can't* support JAVA (T600). Venture there at your own risk. */
+static struct parisc_device_id ccio_tbl[] = {
+	{ HPHW_IOA, HVERSION_REV_ANY_ID, U2_IOA_RUNWAY, 0xb }, /* U2 */
+	{ HPHW_IOA, HVERSION_REV_ANY_ID, UTURN_IOA_RUNWAY, 0xb }, /* UTurn */
+	{ 0, }
+};
+
+static int ccio_probe(struct parisc_device *dev);
+
+static struct parisc_driver ccio_driver = {
+	.name =		"U2:Uturn",
+	.id_table =	ccio_tbl,
+	.probe =	ccio_probe,
+};
+
+/**
+ * ccio_ioc_init - Initalize the I/O Controller
+ * @ioc: The I/O Controller.
+ *
+ * Initalize the I/O Controller which includes setting up the
+ * I/O Page Directory, the resource map, and initalizing the
+ * U2/Uturn chip into virtual mode.
+ */
+static void
+ccio_ioc_init(struct ioc *ioc)
+{
+	int i;
+	unsigned int iov_order;
+	u32 iova_space_size;
+
+	/*
+	** Determine IOVA Space size from memory size.
+	**
+	** Ideally, PCI drivers would register the maximum number
+	** of DMA they can have outstanding for each device they
+	** own.  Next best thing would be to guess how much DMA
+	** can be outstanding based on PCI Class/sub-class. Both
+	** methods still require some "extra" to support PCI
+	** Hot-Plug/Removal of PCI cards. (aka PCI OLARD).
+	*/
+
+	iova_space_size = (u32) (num_physpages / count_parisc_driver(&ccio_driver));
+
+	/* limit IOVA space size to 1MB-1GB */
+
+	if (iova_space_size < (1 << (20 - PAGE_SHIFT))) {
+		iova_space_size =  1 << (20 - PAGE_SHIFT);
+#ifdef __LP64__
+	} else if (iova_space_size > (1 << (30 - PAGE_SHIFT))) {
+		iova_space_size =  1 << (30 - PAGE_SHIFT);
+#endif
+	}
+
+	/*
+	** iova space must be log2() in size.
+	** thus, pdir/res_map will also be log2().
+	*/
+
+	/* We could use larger page sizes in order to *decrease* the number
+	** of mappings needed.  (ie 8k pages means 1/2 the mappings).
+	**
+	** Note: Grant Grunder says "Using 8k I/O pages isn't trivial either
+	**   since the pages must also be physically contiguous - typically
+	**   this is the case under linux."
+	*/
+
+	iov_order = get_order(iova_space_size << PAGE_SHIFT);
+
+	/* iova_space_size is now bytes, not pages */
+	iova_space_size = 1 << (iov_order + PAGE_SHIFT);
+
+	ioc->pdir_size = (iova_space_size / IOVP_SIZE) * sizeof(u64);
+
+	BUG_ON(ioc->pdir_size >= 4 * 1024 * 1024);   /* max pdir size < 4MB */
+
+	/* Verify it's a power of two */
+	BUG_ON((1 << get_order(ioc->pdir_size)) != (ioc->pdir_size >> PAGE_SHIFT));
+
+	DBG_INIT("%s() hpa 0x%lx mem %luMB IOV %dMB (%d bits)\n",
+			__FUNCTION__,
+			ioc->ioc_hpa,
+			(unsigned long) num_physpages >> (20 - PAGE_SHIFT),
+			iova_space_size>>20,
+			iov_order + PAGE_SHIFT);
+
+	ioc->pdir_base = (u64 *)__get_free_pages(GFP_KERNEL, 
+						 get_order(ioc->pdir_size));
+	if(NULL == ioc->pdir_base) {
+		panic("%s:%s() could not allocate I/O Page Table\n", __FILE__,
+		      __FUNCTION__);
+	}
+	memset(ioc->pdir_base, 0, ioc->pdir_size);
+
+	BUG_ON((((unsigned long)ioc->pdir_base) & PAGE_MASK) != (unsigned long)ioc->pdir_base);
+	DBG_INIT(" base %p", ioc->pdir_base);
+
+	/* resource map size dictated by pdir_size */
+ 	ioc->res_size = (ioc->pdir_size / sizeof(u64)) >> 3;
+	DBG_INIT("%s() res_size 0x%x\n", __FUNCTION__, ioc->res_size);
+	
+	ioc->res_map = (u8 *)__get_free_pages(GFP_KERNEL, 
+					      get_order(ioc->res_size));
+	if(NULL == ioc->res_map) {
+		panic("%s:%s() could not allocate resource map\n", __FILE__,
+		      __FUNCTION__);
+	}
+	memset(ioc->res_map, 0, ioc->res_size);
+
+	/* Initialize the res_hint to 16 */
+	ioc->res_hint = 16;
+
+	/* Initialize the spinlock */
+	spin_lock_init(&ioc->res_lock);
+
+	/*
+	** Chainid is the upper most bits of an IOVP used to determine
+	** which TLB entry an IOVP will use.
+	*/
+	ioc->chainid_shift = get_order(iova_space_size) + PAGE_SHIFT - CCIO_CHAINID_SHIFT;
+	DBG_INIT(" chainid_shift 0x%x\n", ioc->chainid_shift);
+
+	/*
+	** Initialize IOA hardware
+	*/
+	WRITE_U32(CCIO_CHAINID_MASK << ioc->chainid_shift, 
+		  &ioc->ioc_hpa->io_chain_id_mask);
+
+	WRITE_U32(virt_to_phys(ioc->pdir_base), 
+		  &ioc->ioc_hpa->io_pdir_base);
+
+	/*
+	** Go to "Virtual Mode"
+	*/
+	WRITE_U32(IOA_NORMAL_MODE, &ioc->ioc_hpa->io_control);
+
+	/*
+	** Initialize all I/O TLB entries to 0 (Valid bit off).
+	*/
+	WRITE_U32(0, &ioc->ioc_hpa->io_tlb_entry_m);
+	WRITE_U32(0, &ioc->ioc_hpa->io_tlb_entry_l);
+
+	for(i = 1 << CCIO_CHAINID_SHIFT; i ; i--) {
+		WRITE_U32((CMD_TLB_DIRECT_WRITE | (i << ioc->chainid_shift)),
+			  &ioc->ioc_hpa->io_command);
+	}
+}
+
+static void
+ccio_init_resource(struct resource *res, char *name, unsigned long ioaddr)
+{
+	int result;
+
+	res->parent = NULL;
+	res->flags = IORESOURCE_MEM;
+	res->start = (unsigned long)(signed) __raw_readl(ioaddr) << 16;
+	res->end = (unsigned long)(signed) (__raw_readl(ioaddr + 4) << 16) - 1;
+	res->name = name;
+	if (res->end + 1 == res->start)
+		return;
+	result = request_resource(&iomem_resource, res);
+	if (result < 0) {
+		printk(KERN_ERR "%s: failed to claim CCIO bus address space (%08lx,%08lx)\n", 
+		       __FILE__, res->start, res->end);
+	}
+}
+
+static void __init ccio_init_resources(struct ioc *ioc)
+{
+	struct resource *res = ioc->mmio_region;
+	char *name = kmalloc(14, GFP_KERNEL);
+
+	sprintf(name, "GSC Bus [%d/]", ioc->hw_path);
+
+	ccio_init_resource(res, name, (unsigned long)&ioc->ioc_hpa->io_io_low);
+	ccio_init_resource(res + 1, name,
+			(unsigned long)&ioc->ioc_hpa->io_io_low_hv);
+}
+
+static int new_ioc_area(struct resource *res, unsigned long size,
+		unsigned long min, unsigned long max, unsigned long align)
+{
+	if (max <= min)
+		return -EBUSY;
+
+	res->start = (max - size + 1) &~ (align - 1);
+	res->end = res->start + size;
+	if (!request_resource(&iomem_resource, res))
+		return 0;
+
+	return new_ioc_area(res, size, min, max - size, align);
+}
+
+static int expand_ioc_area(struct resource *res, unsigned long size,
+		unsigned long min, unsigned long max, unsigned long align)
+{
+	unsigned long start, len;
+
+	if (!res->parent)
+		return new_ioc_area(res, size, min, max, align);
+
+	start = (res->start - size) &~ (align - 1);
+	len = res->end - start + 1;
+	if (start >= min) {
+		if (!adjust_resource(res, start, len))
+			return 0;
+	}
+
+	start = res->start;
+	len = ((size + res->end + align) &~ (align - 1)) - start;
+	if (start + len <= max) {
+		if (!adjust_resource(res, start, len))
+			return 0;
+	}
+
+	return -EBUSY;
+}
+
+/*
+ * Dino calls this function.  Beware that we may get called on systems
+ * which have no IOC (725, B180, C160L, etc) but do have a Dino.
+ * So it's legal to find no parent IOC.
+ *
+ * Some other issues: one of the resources in the ioc may be unassigned.
+ */
+int ccio_allocate_resource(const struct parisc_device *dev,
+		struct resource *res, unsigned long size,
+		unsigned long min, unsigned long max, unsigned long align)
+{
+	struct resource *parent = &iomem_resource;
+	struct ioc *ioc = ccio_get_iommu(dev);
+	if (!ioc)
+		goto out;
+
+	parent = ioc->mmio_region;
+	if (parent->parent &&
+	    !allocate_resource(parent, res, size, min, max, align, NULL, NULL))
+		return 0;
+
+	if ((parent + 1)->parent &&
+	    !allocate_resource(parent + 1, res, size, min, max, align,
+				NULL, NULL))
+		return 0;
+
+	if (!expand_ioc_area(parent, size, min, max, align)) {
+		__raw_writel(((parent->start)>>16) | 0xffff0000,
+			     (unsigned long)&(ioc->ioc_hpa->io_io_low));
+		__raw_writel(((parent->end)>>16) | 0xffff0000,
+			     (unsigned long)&(ioc->ioc_hpa->io_io_high));
+	} else if (!expand_ioc_area(parent + 1, size, min, max, align)) {
+		parent++;
+		__raw_writel(((parent->start)>>16) | 0xffff0000,
+			     (unsigned long)&(ioc->ioc_hpa->io_io_low_hv));
+		__raw_writel(((parent->end)>>16) | 0xffff0000,
+			     (unsigned long)&(ioc->ioc_hpa->io_io_high_hv));
+	} else {
+		return -EBUSY;
+	}
+
+ out:
+	return allocate_resource(parent, res, size, min, max, align, NULL,NULL);
+}
+
+int ccio_request_resource(const struct parisc_device *dev,
+		struct resource *res)
+{
+	struct resource *parent;
+	struct ioc *ioc = ccio_get_iommu(dev);
+
+	if (!ioc) {
+		parent = &iomem_resource;
+	} else if ((ioc->mmio_region->start <= res->start) &&
+			(res->end <= ioc->mmio_region->end)) {
+		parent = ioc->mmio_region;
+	} else if (((ioc->mmio_region + 1)->start <= res->start) &&
+			(res->end <= (ioc->mmio_region + 1)->end)) {
+		parent = ioc->mmio_region + 1;
+	} else {
+		return -EBUSY;
+	}
+
+	return request_resource(parent, res);
+}
+
+/**
+ * ccio_probe - Determine if ccio should claim this device.
+ * @dev: The device which has been found
+ *
+ * Determine if ccio should claim this chip (return 0) or not (return 1).
+ * If so, initialize the chip and tell other partners in crime they
+ * have work to do.
+ */
+static int ccio_probe(struct parisc_device *dev)
+{
+	int i;
+	struct ioc *ioc, **ioc_p = &ioc_list;
+	
+	ioc = kmalloc(sizeof(struct ioc), GFP_KERNEL);
+	if (ioc == NULL) {
+		printk(KERN_ERR MODULE_NAME ": memory allocation failure\n");
+		return 1;
+	}
+	memset(ioc, 0, sizeof(struct ioc));
+
+	ioc->name = dev->id.hversion == U2_IOA_RUNWAY ? "U2" : "UTurn";
+
+	printk(KERN_INFO "Found %s at 0x%lx\n", ioc->name, dev->hpa);
+
+	for (i = 0; i < ioc_count; i++) {
+		ioc_p = &(*ioc_p)->next;
+	}
+	*ioc_p = ioc;
+
+	ioc->hw_path = dev->hw_path;
+	ioc->ioc_hpa = (struct ioa_registers *)dev->hpa;
+	ccio_ioc_init(ioc);
+	ccio_init_resources(ioc);
+	hppa_dma_ops = &ccio_ops;
+	dev->dev.platform_data = kmalloc(sizeof(struct pci_hba_data), GFP_KERNEL);
+
+	/* if this fails, no I/O cards will work, so may as well bug */
+	BUG_ON(dev->dev.platform_data == NULL);
+	HBA_DATA(dev->dev.platform_data)->iommu = ioc;
+	
+
+	if (ioc_count == 0) {
+		/* FIXME: Create separate entries for each ioc */
+		create_proc_read_entry(MODULE_NAME, S_IRWXU, proc_runway_root,
+				       ccio_proc_info, NULL);
+		create_proc_read_entry(MODULE_NAME"-bitmap", S_IRWXU,
+				       proc_runway_root, ccio_resource_map, NULL);
+	}
+
+	ioc_count++;
+
+	parisc_vmerge_boundary = IOVP_SIZE;
+	parisc_vmerge_max_size = BITS_PER_LONG * IOVP_SIZE;
+	parisc_has_iommu();
+	return 0;
+}
+
+/**
+ * ccio_init - ccio initalization procedure.
+ *
+ * Register this driver.
+ */
+void __init ccio_init(void)
+{
+	register_parisc_driver(&ccio_driver);
+}
+
diff --git a/drivers/parisc/ccio-rm-dma.c b/drivers/parisc/ccio-rm-dma.c
new file mode 100644
index 0000000..57e6385
--- /dev/null
+++ b/drivers/parisc/ccio-rm-dma.c
@@ -0,0 +1,201 @@
+/*
+ * ccio-rm-dma.c:
+ *	DMA management routines for first generation cache-coherent machines.
+ *	"Real Mode" operation refers to U2/Uturn chip operation. The chip
+ *      can perform coherency checks w/o using the I/O MMU. That's all we
+ *      need until support for more than 4GB phys mem is needed.
+ * 
+ *	This is the trivial case - basically what x86 does.
+ *
+ *	Drawbacks of using Real Mode are:
+ *	o outbound DMA is slower since one isn't using the prefetching
+ *	  U2 can do for outbound DMA.
+ *	o Ability to do scatter/gather in HW is also lost.
+ *      o only known to work with PCX-W processor. (eg C360)
+ *        (PCX-U/U+ are not coherent with U2 in real mode.)
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ * Original version/author:
+ *      CVSROOT=:pserver:anonymous@198.186.203.37:/cvsroot/linux-parisc
+ *      cvs -z3 co linux/arch/parisc/kernel/dma-rm.c
+ *
+ *	(C) Copyright 2000 Philipp Rumpf <prumpf@tux.org>
+ *
+ *
+ * Adopted for The Puffin Group's parisc-linux port by Grant Grundler.
+ *	(C) Copyright 2000 Grant Grundler <grundler@puffin.external.hp.com>
+ *	
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+
+#include <asm/uaccess.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/page.h>
+
+/* Only chose "ccio" since that's what HP-UX calls it....
+** Make it easier for folks to migrate from one to the other :^)
+*/
+#define MODULE_NAME "ccio"
+
+#define U2_IOA_RUNWAY 0x580
+#define U2_BC_GSC     0x501
+#define UTURN_IOA_RUNWAY 0x581
+#define UTURN_BC_GSC     0x502
+
+#define IS_U2(id) ( \
+    (((id)->hw_type == HPHW_IOA) && ((id)->hversion == U2_IOA_RUNWAY)) || \
+    (((id)->hw_type == HPHW_BCPORT) && ((id)->hversion == U2_BC_GSC))  \
+)
+
+#define IS_UTURN(id) ( \
+    (((id)->hw_type == HPHW_IOA) && ((id)->hversion == UTURN_IOA_RUNWAY)) || \
+    (((id)->hw_type == HPHW_BCPORT) && ((id)->hversion == UTURN_BC_GSC))  \
+)
+
+static int ccio_dma_supported( struct pci_dev *dev, u64 mask)
+{
+	if (dev == NULL) {
+		printk(KERN_ERR MODULE_NAME ": EISA/ISA/et al not supported\n");
+		BUG();
+		return(0);
+	}
+
+	/* only support 32-bit devices (ie PCI/GSC) */
+	return((int) (mask >= 0xffffffffUL));
+}
+
+
+static void *ccio_alloc_consistent(struct pci_dev *dev, size_t size,
+				 dma_addr_t *handle)
+{
+	void *ret;
+	
+	ret = (void *)__get_free_pages(GFP_ATOMIC, get_order(size));
+
+	if (ret != NULL) {
+		memset(ret, 0, size);
+		*handle = virt_to_phys(ret);
+	}
+	return ret;
+}
+	
+static void ccio_free_consistent(struct pci_dev *dev, size_t size,
+			       void *vaddr, dma_addr_t handle)
+{
+	free_pages((unsigned long)vaddr, get_order(size));
+}
+
+static dma_addr_t ccio_map_single(struct pci_dev *dev, void *ptr, size_t size,
+			  int direction)
+{
+	return virt_to_phys(ptr);
+}
+
+static void ccio_unmap_single(struct pci_dev *dev, dma_addr_t dma_addr,
+			    size_t size, int direction)
+{
+	/* Nothing to do */
+}
+
+
+static int ccio_map_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, int direction)
+{
+	int tmp = nents;
+
+        /* KISS: map each buffer separately. */
+	while (nents) {
+		sg_dma_address(sglist) = ccio_map_single(dev, sglist->address, sglist->length, direction);
+		sg_dma_len(sglist) = sglist->length;
+		nents--;
+		sglist++;
+	}
+
+	return tmp;
+}
+
+
+static void ccio_unmap_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, int direction)
+{
+#if 0
+	while (nents) {
+		ccio_unmap_single(dev, sg_dma_address(sglist), sg_dma_len(sglist), direction);
+		nents--;
+		sglist++;
+	}
+	return;
+#else
+	/* Do nothing (copied from current ccio_unmap_single()  :^) */
+#endif
+}
+
+
+static struct pci_dma_ops ccio_ops = {
+	ccio_dma_supported,
+	ccio_alloc_consistent,
+	ccio_free_consistent,
+	ccio_map_single,
+	ccio_unmap_single,
+	ccio_map_sg,
+	ccio_unmap_sg,
+	NULL,                   /* dma_sync_single_for_cpu : NOP for U2 */
+	NULL,                   /* dma_sync_single_for_device : NOP for U2 */
+	NULL,                   /* dma_sync_sg_for_cpu     : ditto */
+	NULL,                   /* dma_sync_sg_for_device     : ditto */
+};
+
+
+/*
+** Determine if u2 should claim this chip (return 0) or not (return 1).
+** If so, initialize the chip and tell other partners in crime they
+** have work to do.
+*/
+static int
+ccio_probe(struct parisc_device *dev)
+{
+	printk(KERN_INFO "%s found %s at 0x%lx\n", MODULE_NAME,
+			dev->id.hversion == U2_BC_GSC ? "U2" : "UTurn",
+			dev->hpa);
+
+/*
+** FIXME - should check U2 registers to verify it's really running
+** in "Real Mode".
+*/
+
+#if 0
+/* will need this for "Virtual Mode" operation */
+	ccio_hw_init(ccio_dev);
+	ccio_common_init(ccio_dev);
+#endif
+	hppa_dma_ops = &ccio_ops;
+	return 0;
+}
+
+static struct parisc_device_id ccio_tbl[] = {
+	{ HPHW_BCPORT, HVERSION_REV_ANY_ID, U2_BC_GSC, 0xc },
+	{ HPHW_BCPORT, HVERSION_REV_ANY_ID, UTURN_BC_GSC, 0xc },
+	{ 0, }
+};
+
+static struct parisc_driver ccio_driver = {
+	.name =		"U2/Uturn",
+	.id_table =	ccio_tbl,
+	.probe =	ccio_probe,
+};
+
+void __init ccio_init(void)
+{
+	register_parisc_driver(&ccio_driver);
+}
diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c
new file mode 100644
index 0000000..b0d2a73
--- /dev/null
+++ b/drivers/parisc/dino.c
@@ -0,0 +1,1044 @@
+/*
+**	DINO manager
+**
+**	(c) Copyright 1999 Red Hat Software
+**	(c) Copyright 1999 SuSE GmbH
+**	(c) Copyright 1999,2000 Hewlett-Packard Company
+**	(c) Copyright 2000 Grant Grundler
+**
+**	This program is free software; you can redistribute it and/or modify
+**	it under the terms of the GNU General Public License as published by
+**      the Free Software Foundation; either version 2 of the License, or
+**      (at your option) any later version.
+**
+**	This module provides access to Dino PCI bus (config/IOport spaces)
+**	and helps manage Dino IRQ lines.
+**
+**	Dino interrupt handling is a bit complicated.
+**	Dino always writes to the broadcast EIR via irr0 for now.
+**	(BIG WARNING: using broadcast EIR is a really bad thing for SMP!)
+**	Only one processor interrupt is used for the 11 IRQ line 
+**	inputs to dino.
+**
+**	The different between Built-in Dino and Card-Mode
+**	dino is in chip initialization and pci device initialization.
+**
+**	Linux drivers can only use Card-Mode Dino if pci devices I/O port
+**	BARs are configured and used by the driver. Programming MMIO address 
+**	requires substantial knowledge of available Host I/O address ranges
+**	is currently not supported.  Port/Config accessor functions are the
+**	same. "BIOS" differences are handled within the existing routines.
+*/
+
+/*	Changes :
+**	2001-06-14 : Clement Moyroud (moyroudc@esiee.fr)
+**		- added support for the integrated RS232. 	
+*/
+
+/*
+** TODO: create a virtual address for each Dino HPA.
+**       GSC code might be able to do this since IODC data tells us
+**       how many pages are used. PCI subsystem could (must?) do this
+**       for PCI drivers devices which implement/use MMIO registers.
+*/
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>	/* for struct irqaction */
+#include <linux/spinlock.h>	/* for spinlock_t and prototypes */
+
+#include <asm/pdc.h>
+#include <asm/page.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+
+#include "gsc.h"
+
+#undef DINO_DEBUG
+
+#ifdef DINO_DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+/*
+** Config accessor functions only pass in the 8-bit bus number
+** and not the 8-bit "PCI Segment" number. Each Dino will be
+** assigned a PCI bus number based on "when" it's discovered.
+**
+** The "secondary" bus number is set to this before calling
+** pci_scan_bus(). If any PPB's are present, the scan will
+** discover them and update the "secondary" and "subordinate"
+** fields in Dino's pci_bus structure.
+**
+** Changes in the configuration *will* result in a different
+** bus number for each dino.
+*/
+
+#define is_card_dino(id) ((id)->hw_type == HPHW_A_DMA)
+
+#define DINO_IAR0		0x004
+#define DINO_IODC_ADDR		0x008
+#define DINO_IODC_DATA_0	0x008
+#define DINO_IODC_DATA_1	0x008
+#define DINO_IRR0		0x00C
+#define DINO_IAR1		0x010
+#define DINO_IRR1		0x014
+#define DINO_IMR		0x018
+#define DINO_IPR		0x01C
+#define DINO_TOC_ADDR		0x020
+#define DINO_ICR		0x024
+#define DINO_ILR		0x028
+#define DINO_IO_COMMAND		0x030
+#define DINO_IO_STATUS		0x034
+#define DINO_IO_CONTROL		0x038
+#define DINO_IO_GSC_ERR_RESP	0x040
+#define DINO_IO_ERR_INFO	0x044
+#define DINO_IO_PCI_ERR_RESP	0x048
+#define DINO_IO_FBB_EN		0x05c
+#define DINO_IO_ADDR_EN		0x060
+#define DINO_PCI_ADDR		0x064
+#define DINO_CONFIG_DATA	0x068
+#define DINO_IO_DATA		0x06c
+#define DINO_MEM_DATA		0x070	/* Dino 3.x only */
+#define DINO_GSC2X_CONFIG	0x7b4
+#define DINO_GMASK		0x800
+#define DINO_PAMR		0x804
+#define DINO_PAPR		0x808
+#define DINO_DAMODE		0x80c
+#define DINO_PCICMD		0x810
+#define DINO_PCISTS		0x814
+#define DINO_MLTIM		0x81c
+#define DINO_BRDG_FEAT		0x820
+#define DINO_PCIROR		0x824
+#define DINO_PCIWOR		0x828
+#define DINO_TLTIM		0x830
+
+#define DINO_IRQS 11		/* bits 0-10 are architected */
+#define DINO_IRR_MASK	0x5ff	/* only 10 bits are implemented */
+
+#define DINO_MASK_IRQ(x)	(1<<(x))
+
+#define PCIINTA   0x001
+#define PCIINTB   0x002
+#define PCIINTC   0x004
+#define PCIINTD   0x008
+#define PCIINTE   0x010
+#define PCIINTF   0x020
+#define GSCEXTINT 0x040
+/* #define xxx       0x080 - bit 7 is "default" */
+/* #define xxx    0x100 - bit 8 not used */
+/* #define xxx    0x200 - bit 9 not used */
+#define RS232INT  0x400
+
+struct dino_device
+{
+	struct pci_hba_data	hba;	/* 'C' inheritance - must be first */
+	spinlock_t		dinosaur_pen;
+	unsigned long		txn_addr; /* EIR addr to generate interrupt */ 
+	u32			txn_data; /* EIR data assign to each dino */ 
+	u32 			imr;	  /* IRQ's which are enabled */ 
+	int			global_irq[12]; /* map IMR bit to global irq */
+#ifdef DINO_DEBUG
+	unsigned int		dino_irr0; /* save most recent IRQ line stat */
+#endif
+};
+
+/* Looks nice and keeps the compiler happy */
+#define DINO_DEV(d) ((struct dino_device *) d)
+
+
+/*
+ * Dino Configuration Space Accessor Functions
+ */
+
+#define DINO_CFG_TOK(bus,dfn,pos) ((u32) ((bus)<<16 | (dfn)<<8 | (pos)))
+
+/*
+ * keep the current highest bus count to assist in allocating busses.  This
+ * tries to keep a global bus count total so that when we discover an 
+ * entirely new bus, it can be given a unique bus number.
+ */
+static int dino_current_bus = 0;
+
+static int dino_cfg_read(struct pci_bus *bus, unsigned int devfn, int where,
+		int size, u32 *val)
+{
+	struct dino_device *d = DINO_DEV(parisc_walk_tree(bus->bridge));
+	u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary;
+	u32 v = DINO_CFG_TOK(local_bus, devfn, where & ~3);
+	void __iomem *base_addr = d->hba.base_addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&d->dinosaur_pen, flags);
+
+	/* tell HW which CFG address */
+	__raw_writel(v, base_addr + DINO_PCI_ADDR);
+
+	/* generate cfg read cycle */
+	if (size == 1) {
+		*val = readb(base_addr + DINO_CONFIG_DATA + (where & 3));
+	} else if (size == 2) {
+		*val = readw(base_addr + DINO_CONFIG_DATA + (where & 2));
+	} else if (size == 4) {
+		*val = readl(base_addr + DINO_CONFIG_DATA);
+	}
+
+	spin_unlock_irqrestore(&d->dinosaur_pen, flags);
+	return 0;
+}
+
+/*
+ * Dino address stepping "feature":
+ * When address stepping, Dino attempts to drive the bus one cycle too soon
+ * even though the type of cycle (config vs. MMIO) might be different. 
+ * The read of Ven/Prod ID is harmless and avoids Dino's address stepping.
+ */
+static int dino_cfg_write(struct pci_bus *bus, unsigned int devfn, int where,
+	int size, u32 val)
+{
+	struct dino_device *d = DINO_DEV(parisc_walk_tree(bus->bridge));
+	u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary;
+	u32 v = DINO_CFG_TOK(local_bus, devfn, where & ~3);
+	void __iomem *base_addr = d->hba.base_addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&d->dinosaur_pen, flags);
+
+	/* avoid address stepping feature */
+	__raw_writel(v & 0xffffff00, base_addr + DINO_PCI_ADDR);
+	__raw_readl(base_addr + DINO_CONFIG_DATA);
+
+	/* tell HW which CFG address */
+	__raw_writel(v, base_addr + DINO_PCI_ADDR);
+	/* generate cfg read cycle */
+	if (size == 1) {
+		writeb(val, base_addr + DINO_CONFIG_DATA + (where & 3));
+	} else if (size == 2) {
+		writew(val, base_addr + DINO_CONFIG_DATA + (where & 2));
+	} else if (size == 4) {
+		writel(val, base_addr + DINO_CONFIG_DATA);
+	}
+
+	spin_unlock_irqrestore(&d->dinosaur_pen, flags);
+	return 0;
+}
+
+static struct pci_ops dino_cfg_ops = {
+	.read =		dino_cfg_read,
+	.write =	dino_cfg_write,
+};
+
+
+/*
+ * Dino "I/O Port" Space Accessor Functions
+ *
+ * Many PCI devices don't require use of I/O port space (eg Tulip,
+ * NCR720) since they export the same registers to both MMIO and
+ * I/O port space.  Performance is going to stink if drivers use
+ * I/O port instead of MMIO.
+ */
+
+#define DINO_PORT_IN(type, size, mask) \
+static u##size dino_in##size (struct pci_hba_data *d, u16 addr) \
+{ \
+	u##size v; \
+	unsigned long flags; \
+	spin_lock_irqsave(&(DINO_DEV(d)->dinosaur_pen), flags); \
+	/* tell HW which IO Port address */ \
+	__raw_writel((u32) addr, d->base_addr + DINO_PCI_ADDR); \
+	/* generate I/O PORT read cycle */ \
+	v = read##type(d->base_addr+DINO_IO_DATA+(addr&mask)); \
+	spin_unlock_irqrestore(&(DINO_DEV(d)->dinosaur_pen), flags); \
+	return v; \
+}
+
+DINO_PORT_IN(b,  8, 3)
+DINO_PORT_IN(w, 16, 2)
+DINO_PORT_IN(l, 32, 0)
+
+#define DINO_PORT_OUT(type, size, mask) \
+static void dino_out##size (struct pci_hba_data *d, u16 addr, u##size val) \
+{ \
+	unsigned long flags; \
+	spin_lock_irqsave(&(DINO_DEV(d)->dinosaur_pen), flags); \
+	/* tell HW which IO port address */ \
+	__raw_writel((u32) addr, d->base_addr + DINO_PCI_ADDR); \
+	/* generate cfg write cycle */ \
+	write##type(val, d->base_addr+DINO_IO_DATA+(addr&mask)); \
+	spin_unlock_irqrestore(&(DINO_DEV(d)->dinosaur_pen), flags); \
+}
+
+DINO_PORT_OUT(b,  8, 3)
+DINO_PORT_OUT(w, 16, 2)
+DINO_PORT_OUT(l, 32, 0)
+
+struct pci_port_ops dino_port_ops = {
+	.inb	= dino_in8,
+	.inw	= dino_in16,
+	.inl	= dino_in32,
+	.outb	= dino_out8,
+	.outw	= dino_out16,
+	.outl	= dino_out32
+};
+
+static void dino_disable_irq(unsigned int irq)
+{
+	struct dino_device *dino_dev = irq_desc[irq].handler_data;
+	int local_irq = gsc_find_local_irq(irq, dino_dev->global_irq, irq);
+
+	DBG(KERN_WARNING "%s(0x%p, %d)\n", __FUNCTION__, irq_dev, irq);
+
+	/* Clear the matching bit in the IMR register */
+	dino_dev->imr &= ~(DINO_MASK_IRQ(local_irq));
+	__raw_writel(dino_dev->imr, dino_dev->hba.base_addr+DINO_IMR);
+}
+
+static void dino_enable_irq(unsigned int irq)
+{
+	struct dino_device *dino_dev = irq_desc[irq].handler_data;
+	int local_irq = gsc_find_local_irq(irq, dino_dev->global_irq, irq);
+	u32 tmp;
+
+	DBG(KERN_WARNING "%s(0x%p, %d)\n", __FUNCTION__, irq_dev, irq);
+
+	/*
+	** clear pending IRQ bits
+	**
+	** This does NOT change ILR state!
+	** See comment below for ILR usage.
+	*/
+	__raw_readl(dino_dev->hba.base_addr+DINO_IPR);
+
+	/* set the matching bit in the IMR register */
+	dino_dev->imr |= DINO_MASK_IRQ(local_irq);	/* used in dino_isr() */
+	__raw_writel( dino_dev->imr, dino_dev->hba.base_addr+DINO_IMR);
+
+	/* Emulate "Level Triggered" Interrupt
+	** Basically, a driver is blowing it if the IRQ line is asserted
+	** while the IRQ is disabled.  But tulip.c seems to do that....
+	** Give 'em a kluge award and a nice round of applause!
+	**
+	** The gsc_write will generate an interrupt which invokes dino_isr().
+	** dino_isr() will read IPR and find nothing. But then catch this
+	** when it also checks ILR.
+	*/
+	tmp = __raw_readl(dino_dev->hba.base_addr+DINO_ILR);
+	if (tmp & DINO_MASK_IRQ(local_irq)) {
+		DBG(KERN_WARNING "%s(): IRQ asserted! (ILR 0x%x)\n",
+				__FUNCTION__, tmp);
+		gsc_writel(dino_dev->txn_data, dino_dev->txn_addr);
+	}
+}
+
+static unsigned int dino_startup_irq(unsigned int irq)
+{
+	dino_enable_irq(irq);
+	return 0;
+}
+
+static struct hw_interrupt_type dino_interrupt_type = {
+	.typename	= "GSC-PCI",
+	.startup	= dino_startup_irq,
+	.shutdown	= dino_disable_irq,
+	.enable		= dino_enable_irq, 
+	.disable	= dino_disable_irq,
+	.ack		= no_ack_irq,
+	.end		= no_end_irq,
+};
+
+
+/*
+ * Handle a Processor interrupt generated by Dino.
+ *
+ * ilr_loop counter is a kluge to prevent a "stuck" IRQ line from
+ * wedging the CPU. Could be removed or made optional at some point.
+ */
+static irqreturn_t
+dino_isr(int irq, void *intr_dev, struct pt_regs *regs)
+{
+	struct dino_device *dino_dev = intr_dev;
+	u32 mask;
+	int ilr_loop = 100;
+
+	/* read and acknowledge pending interrupts */
+#ifdef DINO_DEBUG
+	dino_dev->dino_irr0 =
+#endif
+	mask = __raw_readl(dino_dev->hba.base_addr+DINO_IRR0) & DINO_IRR_MASK;
+
+	if (mask == 0)
+		return IRQ_NONE;
+
+ilr_again:
+	do {
+		int local_irq = __ffs(mask);
+		int irq = dino_dev->global_irq[local_irq];
+		DBG(KERN_DEBUG "%s(%d, %p) mask 0x%x\n",
+			__FUNCTION__, irq, intr_dev, mask);
+		__do_IRQ(irq, regs);
+		mask &= ~(1 << local_irq);
+	} while (mask);
+
+	/* Support for level triggered IRQ lines.
+	** 
+	** Dropping this support would make this routine *much* faster.
+	** But since PCI requires level triggered IRQ line to share lines...
+	** device drivers may assume lines are level triggered (and not
+	** edge triggered like EISA/ISA can be).
+	*/
+	mask = __raw_readl(dino_dev->hba.base_addr+DINO_ILR) & dino_dev->imr;
+	if (mask) {
+		if (--ilr_loop > 0)
+			goto ilr_again;
+		printk(KERN_ERR "Dino 0x%p: stuck interrupt %d\n", 
+		       dino_dev->hba.base_addr, mask);
+		return IRQ_NONE;
+	}
+	return IRQ_HANDLED;
+}
+
+static void dino_assign_irq(struct dino_device *dino, int local_irq, int *irqp)
+{
+	int irq = gsc_assign_irq(&dino_interrupt_type, dino);
+	if (irq == NO_IRQ)
+		return;
+
+	*irqp = irq;
+	dino->global_irq[local_irq] = irq;
+}
+
+static void dino_choose_irq(struct parisc_device *dev, void *ctrl)
+{
+	int irq;
+	struct dino_device *dino = ctrl;
+
+	switch (dev->id.sversion) {
+		case 0x00084:	irq =  8; break; /* PS/2 */
+		case 0x0008c:	irq = 10; break; /* RS232 */
+		case 0x00096:	irq =  8; break; /* PS/2 */
+		default:	return;		 /* Unknown */
+	}
+
+	dino_assign_irq(dino, irq, &dev->irq);
+}
+
+static void __init
+dino_bios_init(void)
+{
+	DBG("dino_bios_init\n");
+}
+
+/*
+ * dino_card_setup - Set up the memory space for a Dino in card mode.
+ * @bus: the bus under this dino
+ *
+ * Claim an 8MB chunk of unused IO space and call the generic PCI routines
+ * to set up the addresses of the devices on this bus.
+ */
+#define _8MB 0x00800000UL
+static void __init
+dino_card_setup(struct pci_bus *bus, void __iomem *base_addr)
+{
+	int i;
+	struct dino_device *dino_dev = DINO_DEV(parisc_walk_tree(bus->bridge));
+	struct resource *res;
+	char name[128];
+	int size;
+
+	res = &dino_dev->hba.lmmio_space;
+	res->flags = IORESOURCE_MEM;
+	size = scnprintf(name, sizeof(name), "Dino LMMIO (%s)", 
+			 bus->bridge->bus_id);
+	res->name = kmalloc(size+1, GFP_KERNEL);
+	if(res->name)
+		strcpy((char *)res->name, name);
+	else
+		res->name = dino_dev->hba.lmmio_space.name;
+	
+
+	if (ccio_allocate_resource(dino_dev->hba.dev, res, _8MB,
+				F_EXTEND(0xf0000000UL) | _8MB,
+				F_EXTEND(0xffffffffUL) &~ _8MB, _8MB) < 0) {
+		struct list_head *ln, *tmp_ln;
+
+		printk(KERN_ERR "Dino: cannot attach bus %s\n",
+		       bus->bridge->bus_id);
+		/* kill the bus, we can't do anything with it */
+		list_for_each_safe(ln, tmp_ln, &bus->devices) {
+			struct pci_dev *dev = pci_dev_b(ln);
+
+			list_del(&dev->global_list);
+			list_del(&dev->bus_list);
+		}
+			
+		return;
+	}
+	bus->resource[1] = res;
+	bus->resource[0] = &(dino_dev->hba.io_space);
+
+	/* Now tell dino what range it has */
+	for (i = 1; i < 31; i++) {
+		if (res->start == F_EXTEND(0xf0000000UL | (i * _8MB)))
+			break;
+	}
+	DBG("DINO GSC WRITE i=%d, start=%lx, dino addr = %lx\n",
+	    i, res->start, base_addr + DINO_IO_ADDR_EN);
+	__raw_writel(1 << i, base_addr + DINO_IO_ADDR_EN);
+}
+
+static void __init
+dino_card_fixup(struct pci_dev *dev)
+{
+	u32 irq_pin;
+
+	/*
+	** REVISIT: card-mode PCI-PCI expansion chassis do exist.
+	**         Not sure they were ever productized.
+	**         Die here since we'll die later in dino_inb() anyway.
+	*/
+	if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+		panic("Card-Mode Dino: PCI-PCI Bridge not supported\n");
+	}
+
+	/*
+	** Set Latency Timer to 0xff (not a shared bus)
+	** Set CACHELINE_SIZE.
+	*/
+	dino_cfg_write(dev->bus, dev->devfn, 
+		       PCI_CACHE_LINE_SIZE, 2, 0xff00 | L1_CACHE_BYTES/4); 
+
+	/*
+	** Program INT_LINE for card-mode devices.
+	** The cards are hardwired according to this algorithm.
+	** And it doesn't matter if PPB's are present or not since
+	** the IRQ lines bypass the PPB.
+	**
+	** "-1" converts INTA-D (1-4) to PCIINTA-D (0-3) range.
+	** The additional "-1" adjusts for skewing the IRQ<->slot.
+	*/
+	dino_cfg_read(dev->bus, dev->devfn, PCI_INTERRUPT_PIN, 1, &irq_pin); 
+	dev->irq = (irq_pin + PCI_SLOT(dev->devfn) - 1) % 4 ;
+
+	/* Shouldn't really need to do this but it's in case someone tries
+	** to bypass PCI services and look at the card themselves.
+	*/
+	dino_cfg_write(dev->bus, dev->devfn, PCI_INTERRUPT_LINE, 1, dev->irq); 
+}
+
+/* The alignment contraints for PCI bridges under dino */
+#define DINO_BRIDGE_ALIGN 0x100000
+
+
+static void __init
+dino_fixup_bus(struct pci_bus *bus)
+{
+	struct list_head *ln;
+        struct pci_dev *dev;
+        struct dino_device *dino_dev = DINO_DEV(parisc_walk_tree(bus->bridge));
+	int port_base = HBA_PORT_BASE(dino_dev->hba.hba_num);
+
+	DBG(KERN_WARNING "%s(0x%p) bus %d platform_data 0x%p\n",
+	    __FUNCTION__, bus, bus->secondary, 
+	    bus->bridge->platform_data);
+
+	/* Firmware doesn't set up card-mode dino, so we have to */
+	if (is_card_dino(&dino_dev->hba.dev->id)) {
+		dino_card_setup(bus, dino_dev->hba.base_addr);
+	} else if(bus->parent == NULL) {
+		/* must have a dino above it, reparent the resources
+		 * into the dino window */
+		int i;
+		struct resource *res = &dino_dev->hba.lmmio_space;
+
+		bus->resource[0] = &(dino_dev->hba.io_space);
+		for(i = 0; i < DINO_MAX_LMMIO_RESOURCES; i++) {
+			if(res[i].flags == 0)
+				break;
+			bus->resource[i+1] = &res[i];
+		}
+
+	} else if(bus->self) {
+		int i;
+
+		pci_read_bridge_bases(bus);
+
+
+		for(i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) {
+			if((bus->self->resource[i].flags & 
+			    (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
+				continue;
+			
+			if(bus->self->resource[i].flags & IORESOURCE_MEM) {
+				/* There's a quirk to alignment of
+				 * bridge memory resources: the start
+				 * is the alignment and start-end is
+				 * the size.  However, firmware will
+				 * have assigned start and end, so we
+				 * need to take this into account */
+				bus->self->resource[i].end = bus->self->resource[i].end - bus->self->resource[i].start + DINO_BRIDGE_ALIGN;
+				bus->self->resource[i].start = DINO_BRIDGE_ALIGN;
+				
+			}
+					
+			DBG("DEBUG %s assigning %d [0x%lx,0x%lx]\n",
+			    bus->self->dev.bus_id, i,
+			    bus->self->resource[i].start,
+			    bus->self->resource[i].end);
+			pci_assign_resource(bus->self, i);
+			DBG("DEBUG %s after assign %d [0x%lx,0x%lx]\n",
+			    bus->self->dev.bus_id, i,
+			    bus->self->resource[i].start,
+			    bus->self->resource[i].end);
+		}
+	}
+
+
+	list_for_each(ln, &bus->devices) {
+		int i;
+
+		dev = pci_dev_b(ln);
+		if (is_card_dino(&dino_dev->hba.dev->id))
+			dino_card_fixup(dev);
+
+		/*
+		** P2PB's only have 2 BARs, no IRQs.
+		** I'd like to just ignore them for now.
+		*/
+		if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)
+			continue;
+
+		/* Adjust the I/O Port space addresses */
+		for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+			struct resource *res = &dev->resource[i];
+			if (res->flags & IORESOURCE_IO) {
+				res->start |= port_base;
+				res->end |= port_base;
+			}
+#ifdef __LP64__
+			/* Sign Extend MMIO addresses */
+			else if (res->flags & IORESOURCE_MEM) {
+				res->start |= F_EXTEND(0UL);
+				res->end   |= F_EXTEND(0UL);
+			}
+#endif
+		}
+		/* null out the ROM resource if there is one (we don't
+		 * care about an expansion rom on parisc, since it
+		 * usually contains (x86) bios code) */
+		dev->resource[PCI_ROM_RESOURCE].flags = 0;
+				
+		if(dev->irq == 255) {
+
+#define DINO_FIX_UNASSIGNED_INTERRUPTS
+#ifdef DINO_FIX_UNASSIGNED_INTERRUPTS
+
+			/* This code tries to assign an unassigned
+			 * interrupt.  Leave it disabled unless you
+			 * *really* know what you're doing since the
+			 * pin<->interrupt line mapping varies by bus
+			 * and machine */
+
+			u32 irq_pin;
+			
+			dino_cfg_read(dev->bus, dev->devfn, 
+				      PCI_INTERRUPT_PIN, 1, &irq_pin);
+			irq_pin = (irq_pin + PCI_SLOT(dev->devfn) - 1) % 4 ;
+			printk(KERN_WARNING "Device %s has undefined IRQ, "
+					"setting to %d\n", pci_name(dev), irq_pin);
+			dino_cfg_write(dev->bus, dev->devfn, 
+				       PCI_INTERRUPT_LINE, 1, irq_pin);
+			dino_assign_irq(dino_dev, irq_pin, &dev->irq);
+#else
+			dev->irq = 65535;
+			printk(KERN_WARNING "Device %s has unassigned IRQ\n", pci_name(dev));
+#endif
+		} else {
+
+			/* Adjust INT_LINE for that busses region */
+			dino_assign_irq(dino_dev, dev->irq, &dev->irq);
+		}
+	}
+}
+
+
+struct pci_bios_ops dino_bios_ops = {
+	.init		= dino_bios_init,
+	.fixup_bus	= dino_fixup_bus
+};
+
+
+/*
+ *	Initialise a DINO controller chip
+ */
+static void __init
+dino_card_init(struct dino_device *dino_dev)
+{
+	u32 brdg_feat = 0x00784e05;
+
+	__raw_writel(0x00000000, dino_dev->hba.base_addr+DINO_GMASK);
+	__raw_writel(0x00000001, dino_dev->hba.base_addr+DINO_IO_FBB_EN);
+	__raw_writel(0x00000000, dino_dev->hba.base_addr+DINO_ICR);
+
+#if 1
+/* REVISIT - should be a runtime check (eg if (CPU_IS_PCX_L) ...) */
+	/*
+	** PCX-L processors don't support XQL like Dino wants it.
+	** PCX-L2 ignore XQL signal and it doesn't matter.
+	*/
+	brdg_feat &= ~0x4;	/* UXQL */
+#endif
+	__raw_writel( brdg_feat, dino_dev->hba.base_addr+DINO_BRDG_FEAT);
+
+	/*
+	** Don't enable address decoding until we know which I/O range
+	** currently is available from the host. Only affects MMIO
+	** and not I/O port space.
+	*/
+	__raw_writel(0x00000000, dino_dev->hba.base_addr+DINO_IO_ADDR_EN);
+
+	__raw_writel(0x00000000, dino_dev->hba.base_addr+DINO_DAMODE);
+	__raw_writel(0x00222222, dino_dev->hba.base_addr+DINO_PCIROR);
+	__raw_writel(0x00222222, dino_dev->hba.base_addr+DINO_PCIWOR);
+
+	__raw_writel(0x00000040, dino_dev->hba.base_addr+DINO_MLTIM);
+	__raw_writel(0x00000080, dino_dev->hba.base_addr+DINO_IO_CONTROL);
+	__raw_writel(0x0000008c, dino_dev->hba.base_addr+DINO_TLTIM);
+
+	/* Disable PAMR before writing PAPR */
+	__raw_writel(0x0000007e, dino_dev->hba.base_addr+DINO_PAMR);
+	__raw_writel(0x0000007f, dino_dev->hba.base_addr+DINO_PAPR);
+	__raw_writel(0x00000000, dino_dev->hba.base_addr+DINO_PAMR);
+
+	/*
+	** Dino ERS encourages enabling FBB (0x6f).
+	** We can't until we know *all* devices below us can support it.
+	** (Something in device configuration header tells us).
+	*/
+	__raw_writel(0x0000004f, dino_dev->hba.base_addr+DINO_PCICMD);
+
+	/* Somewhere, the PCI spec says give devices 1 second
+	** to recover from the #RESET being de-asserted.
+	** Experience shows most devices only need 10ms.
+	** This short-cut speeds up booting significantly.
+	*/
+	mdelay(pci_post_reset_delay);
+}
+
+static int __init
+dino_bridge_init(struct dino_device *dino_dev, const char *name)
+{
+	unsigned long io_addr;
+	int result, i, count=0;
+	struct resource *res, *prevres = NULL;
+	/*
+	 * Decoding IO_ADDR_EN only works for Built-in Dino
+	 * since PDC has already initialized this.
+	 */
+
+	io_addr = __raw_readl(dino_dev->hba.base_addr + DINO_IO_ADDR_EN);
+	if (io_addr == 0) {
+		printk(KERN_WARNING "%s: No PCI devices enabled.\n", name);
+		return -ENODEV;
+	}
+
+	res = &dino_dev->hba.lmmio_space;
+	for (i = 0; i < 32; i++) {
+		unsigned long start, end;
+
+		if((io_addr & (1 << i)) == 0)
+			continue;
+
+		start = (unsigned long)(signed int)(0xf0000000 | (i << 23));
+		end = start + 8 * 1024 * 1024 - 1;
+
+		DBG("DINO RANGE %d is at 0x%lx-0x%lx\n", count,
+		    start, end);
+
+		if(prevres && prevres->end + 1 == start) {
+			prevres->end = end;
+		} else {
+			if(count >= DINO_MAX_LMMIO_RESOURCES) {
+				printk(KERN_ERR "%s is out of resource windows for range %d (0x%lx-0x%lx)\n", name, count, start, end);
+				break;
+			}
+			prevres = res;
+			res->start = start;
+			res->end = end;
+			res->flags = IORESOURCE_MEM;
+			res->name = kmalloc(64, GFP_KERNEL);
+			if(res->name)
+				snprintf((char *)res->name, 64, "%s LMMIO %d",
+					 name, count);
+			res++;
+			count++;
+		}
+	}
+
+	res = &dino_dev->hba.lmmio_space;
+
+	for(i = 0; i < DINO_MAX_LMMIO_RESOURCES; i++) {
+		if(res[i].flags == 0)
+			break;
+
+		result = ccio_request_resource(dino_dev->hba.dev, &res[i]);
+		if (result < 0) {
+			printk(KERN_ERR "%s: failed to claim PCI Bus address space %d (0x%lx-0x%lx)!\n", name, i, res[i].start, res[i].end);
+			return result;
+		}
+	}
+	return 0;
+}
+
+static int __init dino_common_init(struct parisc_device *dev,
+		struct dino_device *dino_dev, const char *name)
+{
+	int status;
+	u32 eim;
+	struct gsc_irq gsc_irq;
+	struct resource *res;
+
+	pcibios_register_hba(&dino_dev->hba);
+
+	pci_bios = &dino_bios_ops;   /* used by pci_scan_bus() */
+	pci_port = &dino_port_ops;
+
+	/*
+	** Note: SMP systems can make use of IRR1/IAR1 registers
+	**   But it won't buy much performance except in very
+	**   specific applications/configurations. Note Dino
+	**   still only has 11 IRQ input lines - just map some of them
+	**   to a different processor.
+	*/
+	dev->irq = gsc_alloc_irq(&gsc_irq);
+	dino_dev->txn_addr = gsc_irq.txn_addr;
+	dino_dev->txn_data = gsc_irq.txn_data;
+	eim = ((u32) gsc_irq.txn_addr) | gsc_irq.txn_data;
+
+	/* 
+	** Dino needs a PA "IRQ" to get a processor's attention.
+	** arch/parisc/kernel/irq.c returns an EIRR bit.
+	*/
+	if (dev->irq < 0) {
+		printk(KERN_WARNING "%s: gsc_alloc_irq() failed\n", name);
+		return 1;
+	}
+
+	status = request_irq(dev->irq, dino_isr, 0, name, dino_dev);
+	if (status) {
+		printk(KERN_WARNING "%s: request_irq() failed with %d\n", 
+			name, status);
+		return 1;
+	}
+
+	/* Support the serial port which is sometimes attached on built-in
+	 * Dino / Cujo chips.
+	 */
+
+	gsc_fixup_irqs(dev, dino_dev, dino_choose_irq);
+
+	/*
+	** This enables DINO to generate interrupts when it sees
+	** any of its inputs *change*. Just asserting an IRQ
+	** before it's enabled (ie unmasked) isn't good enough.
+	*/
+	__raw_writel(eim, dino_dev->hba.base_addr+DINO_IAR0);
+
+	/*
+	** Some platforms don't clear Dino's IRR0 register at boot time.
+	** Reading will clear it now.
+	*/
+	__raw_readl(dino_dev->hba.base_addr+DINO_IRR0);
+
+	/* allocate I/O Port resource region */
+	res = &dino_dev->hba.io_space;
+	if (dev->id.hversion == 0x680 || is_card_dino(&dev->id)) {
+		res->name = "Dino I/O Port";
+	} else {
+		res->name = "Cujo I/O Port";
+	}
+	res->start = HBA_PORT_BASE(dino_dev->hba.hba_num);
+	res->end = res->start + (HBA_PORT_SPACE_SIZE - 1);
+	res->flags = IORESOURCE_IO; /* do not mark it busy ! */
+	if (request_resource(&ioport_resource, res) < 0) {
+		printk(KERN_ERR "%s: request I/O Port region failed "
+		       "0x%lx/%lx (hpa 0x%p)\n",
+		       name, res->start, res->end, dino_dev->hba.base_addr);
+		return 1;
+	}
+
+	return 0;
+}
+
+#define CUJO_RAVEN_ADDR		F_EXTEND(0xf1000000UL)
+#define CUJO_FIREHAWK_ADDR	F_EXTEND(0xf1604000UL)
+#define CUJO_RAVEN_BADPAGE	0x01003000UL
+#define CUJO_FIREHAWK_BADPAGE	0x01607000UL
+
+static const char *dino_vers[] = {
+	"2.0",
+	"2.1",
+	"3.0",
+	"3.1"
+};
+
+static const char *cujo_vers[] = {
+	"1.0",
+	"2.0"
+};
+
+void ccio_cujo20_fixup(struct parisc_device *dev, u32 iovp);
+
+/*
+** Determine if dino should claim this chip (return 0) or not (return 1).
+** If so, initialize the chip appropriately (card-mode vs bridge mode).
+** Much of the initialization is common though.
+*/
+static int __init
+dino_driver_callback(struct parisc_device *dev)
+{
+	struct dino_device *dino_dev;	// Dino specific control struct
+	const char *version = "unknown";
+	char *name;
+	int is_cujo = 0;
+	struct pci_bus *bus;
+	
+	name = "Dino";
+	if (is_card_dino(&dev->id)) {
+		version = "3.x (card mode)";
+	} else {
+		if(dev->id.hversion == 0x680) {
+			if (dev->id.hversion_rev < 4) {
+				version = dino_vers[dev->id.hversion_rev];
+			}
+		} else {
+			name = "Cujo";
+			is_cujo = 1;
+			if (dev->id.hversion_rev < 2) {
+				version = cujo_vers[dev->id.hversion_rev];
+			}
+		}
+	}
+
+	printk("%s version %s found at 0x%lx\n", name, version, dev->hpa);
+
+	if (!request_mem_region(dev->hpa, PAGE_SIZE, name)) {
+		printk(KERN_ERR "DINO: Hey! Someone took my MMIO space (0x%ld)!\n",
+			dev->hpa);
+		return 1;
+	}
+
+	/* Check for bugs */
+	if (is_cujo && dev->id.hversion_rev == 1) {
+#ifdef CONFIG_IOMMU_CCIO
+		printk(KERN_WARNING "Enabling Cujo 2.0 bug workaround\n");
+		if (dev->hpa == (unsigned long)CUJO_RAVEN_ADDR) {
+			ccio_cujo20_fixup(dev, CUJO_RAVEN_BADPAGE);
+		} else if (dev->hpa == (unsigned long)CUJO_FIREHAWK_ADDR) {
+			ccio_cujo20_fixup(dev, CUJO_FIREHAWK_BADPAGE);
+		} else {
+			printk("Don't recognise Cujo at address 0x%lx, not enabling workaround\n", dev->hpa);
+		}
+#endif
+	} else if (!is_cujo && !is_card_dino(&dev->id) &&
+			dev->id.hversion_rev < 3) {
+		printk(KERN_WARNING
+"The GSCtoPCI (Dino hrev %d) bus converter found may exhibit\n"
+"data corruption.  See Service Note Numbers: A4190A-01, A4191A-01.\n"
+"Systems shipped after Aug 20, 1997 will not exhibit this problem.\n"
+"Models affected: C180, C160, C160L, B160L, and B132L workstations.\n\n",
+			dev->id.hversion_rev);
+/* REVISIT: why are C200/C240 listed in the README table but not
+**   "Models affected"? Could be an omission in the original literature.
+*/
+	}
+
+	dino_dev = kmalloc(sizeof(struct dino_device), GFP_KERNEL);
+	if (!dino_dev) {
+		printk("dino_init_chip - couldn't alloc dino_device\n");
+		return 1;
+	}
+
+	memset(dino_dev, 0, sizeof(struct dino_device));
+
+	dino_dev->hba.dev = dev;
+	dino_dev->hba.base_addr = ioremap(dev->hpa, 4096); /* faster access */
+	dino_dev->hba.lmmio_space_offset = 0;	/* CPU addrs == bus addrs */
+	spin_lock_init(&dino_dev->dinosaur_pen);
+	dino_dev->hba.iommu = ccio_get_iommu(dev);
+
+	if (is_card_dino(&dev->id)) {
+		dino_card_init(dino_dev);
+	} else {
+		dino_bridge_init(dino_dev, name);
+	}
+
+	if (dino_common_init(dev, dino_dev, name))
+		return 1;
+
+	dev->dev.platform_data = dino_dev;
+
+	/*
+	** It's not used to avoid chicken/egg problems
+	** with configuration accessor functions.
+	*/
+	bus = pci_scan_bus_parented(&dev->dev, dino_current_bus,
+				    &dino_cfg_ops, NULL);
+	if(bus) {
+		/* This code *depends* on scanning being single threaded
+		 * if it isn't, this global bus number count will fail
+		 */
+		dino_current_bus = bus->subordinate + 1;
+		pci_bus_assign_resources(bus);
+	} else {
+		printk(KERN_ERR "ERROR: failed to scan PCI bus on %s (probably duplicate bus number %d)\n", dev->dev.bus_id, dino_current_bus);
+		/* increment the bus number in case of duplicates */
+		dino_current_bus++;
+	}
+	dino_dev->hba.hba_bus = bus;
+	return 0;
+}
+
+/*
+ * Normally, we would just test sversion.  But the Elroy PCI adapter has
+ * the same sversion as Dino, so we have to check hversion as well.
+ * Unfortunately, the J2240 PDC reports the wrong hversion for the first
+ * Dino, so we have to test for Dino, Cujo and Dino-in-a-J2240.
+ * For card-mode Dino, most machines report an sversion of 9D.  But 715
+ * and 725 firmware misreport it as 0x08080 for no adequately explained
+ * reason.
+ */
+static struct parisc_device_id dino_tbl[] = {
+	{ HPHW_A_DMA, HVERSION_REV_ANY_ID, 0x004, 0x0009D },/* Card-mode Dino */
+	{ HPHW_A_DMA, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x08080 }, /* XXX */
+	{ HPHW_BRIDGE, HVERSION_REV_ANY_ID, 0x680, 0xa }, /* Bridge-mode Dino */
+	{ HPHW_BRIDGE, HVERSION_REV_ANY_ID, 0x682, 0xa }, /* Bridge-mode Cujo */
+	{ HPHW_BRIDGE, HVERSION_REV_ANY_ID, 0x05d, 0xa }, /* Dino in a J2240 */
+	{ 0, }
+};
+
+static struct parisc_driver dino_driver = {
+	.name =		"Dino",
+	.id_table =	dino_tbl,
+	.probe =	dino_driver_callback,
+};
+
+/*
+ * One time initialization to let the world know Dino is here.
+ * This is the only routine which is NOT static.
+ * Must be called exactly once before pci_init().
+ */
+int __init dino_init(void)
+{
+	register_parisc_driver(&dino_driver);
+	return 0;
+}
+
diff --git a/drivers/parisc/eisa.c b/drivers/parisc/eisa.c
new file mode 100644
index 0000000..043d47a
--- /dev/null
+++ b/drivers/parisc/eisa.c
@@ -0,0 +1,464 @@
+/*
+ * eisa.c - provide support for EISA adapters in PA-RISC machines
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Copyright (c) 2001 Matthew Wilcox for Hewlett Packard
+ * Copyright (c) 2001 Daniel Engstrom <5116@telia.com>
+ *
+ * There are two distinct EISA adapters.  Mongoose is found in machines
+ * before the 712; then the Wax ASIC is used.  To complicate matters, the
+ * Wax ASIC also includes a PS/2 and RS-232 controller, but those are
+ * dealt with elsewhere; this file is concerned only with the EISA portions
+ * of Wax.
+ * 
+ * 
+ * HINT:
+ * -----
+ * To allow an ISA card to work properly in the EISA slot you need to
+ * set an edge trigger level. This may be done on the palo command line 
+ * by adding the kernel parameter "eisa_irq_edge=n,n2,[...]]", with 
+ * n and n2 as the irq levels you want to use.
+ * 
+ * Example: "eisa_irq_edge=10,11" allows ISA cards to operate at 
+ * irq levels 10 and 11.
+ */
+
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/eisa.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/processor.h>
+#include <asm/parisc-device.h>
+#include <asm/delay.h>
+#include <asm/eisa_bus.h>
+#include <asm/eisa_eeprom.h>
+
+#if 0
+#define EISA_DBG(msg, arg... ) printk(KERN_DEBUG "eisa: " msg , ## arg )
+#else
+#define EISA_DBG(msg, arg... )  
+#endif
+
+#define SNAKES_EEPROM_BASE_ADDR 0xF0810400
+#define MIRAGE_EEPROM_BASE_ADDR 0xF00C0400
+
+static DEFINE_SPINLOCK(eisa_irq_lock);
+
+void __iomem *eisa_eeprom_addr;
+
+/* We can only have one EISA adapter in the system because neither
+ * implementation can be flexed.
+ */
+static struct eisa_ba {
+	struct pci_hba_data	hba;
+	unsigned long eeprom_addr;
+	struct eisa_root_device root;
+} eisa_dev;
+
+/* Port ops */
+
+static inline unsigned long eisa_permute(unsigned short port)
+{
+	if (port & 0x300) {
+		return 0xfc000000 | ((port & 0xfc00) >> 6)
+			| ((port & 0x3f8) << 9) | (port & 7);
+	} else {
+		return 0xfc000000 | port;
+	}
+}
+
+unsigned char eisa_in8(unsigned short port)
+{
+	if (EISA_bus)
+		return gsc_readb(eisa_permute(port));
+	return 0xff;
+}
+
+unsigned short eisa_in16(unsigned short port)
+{
+	if (EISA_bus)
+		return le16_to_cpu(gsc_readw(eisa_permute(port)));
+	return 0xffff;
+}
+
+unsigned int eisa_in32(unsigned short port)
+{
+	if (EISA_bus)
+		return le32_to_cpu(gsc_readl(eisa_permute(port)));
+	return 0xffffffff;
+}
+
+void eisa_out8(unsigned char data, unsigned short port)
+{
+	if (EISA_bus)
+		gsc_writeb(data, eisa_permute(port));
+}
+
+void eisa_out16(unsigned short data, unsigned short port)
+{
+	if (EISA_bus)	
+		gsc_writew(cpu_to_le16(data), eisa_permute(port));
+}
+
+void eisa_out32(unsigned int data, unsigned short port)
+{
+	if (EISA_bus)
+		gsc_writel(cpu_to_le32(data), eisa_permute(port));
+}
+
+#ifndef CONFIG_PCI
+/* We call these directly without PCI.  See asm/io.h. */
+EXPORT_SYMBOL(eisa_in8);
+EXPORT_SYMBOL(eisa_in16);
+EXPORT_SYMBOL(eisa_in32);
+EXPORT_SYMBOL(eisa_out8);
+EXPORT_SYMBOL(eisa_out16);
+EXPORT_SYMBOL(eisa_out32);
+#endif
+
+/* Interrupt handling */
+
+/* cached interrupt mask registers */
+static int master_mask;
+static int slave_mask;
+
+/* the trig level can be set with the
+ * eisa_irq_edge=n,n,n commandline parameter 
+ * We should really read this from the EEPROM 
+ * in the furure. 
+ */
+/* irq 13,8,2,1,0 must be edge */
+static unsigned int eisa_irq_level; /* default to edge triggered */
+
+
+/* called by free irq */
+static void eisa_disable_irq(unsigned int irq)
+{
+	unsigned long flags;
+
+	EISA_DBG("disable irq %d\n", irq);
+	/* just mask for now */
+	spin_lock_irqsave(&eisa_irq_lock, flags);
+        if (irq & 8) {
+		slave_mask |= (1 << (irq&7));
+		eisa_out8(slave_mask, 0xa1);
+	} else {
+		master_mask |= (1 << (irq&7));
+		eisa_out8(master_mask, 0x21);
+	}
+	spin_unlock_irqrestore(&eisa_irq_lock, flags);
+	EISA_DBG("pic0 mask %02x\n", eisa_in8(0x21));
+	EISA_DBG("pic1 mask %02x\n", eisa_in8(0xa1));
+}
+
+/* called by request irq */
+static void eisa_enable_irq(unsigned int irq)
+{
+	unsigned long flags;
+	EISA_DBG("enable irq %d\n", irq);
+		
+	spin_lock_irqsave(&eisa_irq_lock, flags);
+        if (irq & 8) {
+		slave_mask &= ~(1 << (irq&7));
+		eisa_out8(slave_mask, 0xa1);
+	} else {
+		master_mask &= ~(1 << (irq&7));
+		eisa_out8(master_mask, 0x21);
+	}
+	spin_unlock_irqrestore(&eisa_irq_lock, flags);
+	EISA_DBG("pic0 mask %02x\n", eisa_in8(0x21));
+	EISA_DBG("pic1 mask %02x\n", eisa_in8(0xa1));
+}
+
+static unsigned int eisa_startup_irq(unsigned int irq)
+{
+	eisa_enable_irq(irq);
+	return 0;
+}
+
+static struct hw_interrupt_type eisa_interrupt_type = {
+	.typename =	"EISA",
+	.startup =	eisa_startup_irq,
+	.shutdown =	eisa_disable_irq,
+	.enable =	eisa_enable_irq,
+	.disable =	eisa_disable_irq,
+	.ack =		no_ack_irq,
+	.end =		no_end_irq,
+};
+
+static irqreturn_t eisa_irq(int wax_irq, void *intr_dev, struct pt_regs *regs)
+{
+	int irq = gsc_readb(0xfc01f000); /* EISA supports 16 irqs */
+	unsigned long flags;
+        
+	spin_lock_irqsave(&eisa_irq_lock, flags);
+	/* read IRR command */
+	eisa_out8(0x0a, 0x20);
+	eisa_out8(0x0a, 0xa0);
+
+	EISA_DBG("irq IAR %02x 8259-1 irr %02x 8259-2 irr %02x\n",
+		   irq, eisa_in8(0x20), eisa_in8(0xa0));
+   
+	/* read ISR command */
+	eisa_out8(0x0a, 0x20);
+	eisa_out8(0x0a, 0xa0);
+	EISA_DBG("irq 8259-1 isr %02x imr %02x 8259-2 isr %02x imr %02x\n",
+		 eisa_in8(0x20), eisa_in8(0x21), eisa_in8(0xa0), eisa_in8(0xa1));
+	
+	irq &= 0xf;
+	
+	/* mask irq and write eoi */
+	if (irq & 8) {
+		slave_mask |= (1 << (irq&7));
+		eisa_out8(slave_mask, 0xa1);
+		eisa_out8(0x60 | (irq&7),0xa0);/* 'Specific EOI' to slave */
+		eisa_out8(0x62,0x20);	/* 'Specific EOI' to master-IRQ2 */
+		
+	} else {
+		master_mask |= (1 << (irq&7));
+		eisa_out8(master_mask, 0x21);
+		eisa_out8(0x60|irq,0x20);	/* 'Specific EOI' to master */
+	}
+	spin_unlock_irqrestore(&eisa_irq_lock, flags);
+
+	__do_IRQ(irq, regs);
+   
+	spin_lock_irqsave(&eisa_irq_lock, flags);
+	/* unmask */
+        if (irq & 8) {
+		slave_mask &= ~(1 << (irq&7));
+		eisa_out8(slave_mask, 0xa1);
+	} else {
+		master_mask &= ~(1 << (irq&7));
+		eisa_out8(master_mask, 0x21);
+	}
+	spin_unlock_irqrestore(&eisa_irq_lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t dummy_irq2_handler(int _, void *dev, struct pt_regs *regs)
+{
+	printk(KERN_ALERT "eisa: uhh, irq2?\n");
+	return IRQ_HANDLED;
+}
+
+static struct irqaction irq2_action = {
+	.handler = dummy_irq2_handler,
+	.name = "cascade",
+};
+
+static void init_eisa_pic(void)
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&eisa_irq_lock, flags);
+
+	eisa_out8(0xff, 0x21); /* mask during init */
+	eisa_out8(0xff, 0xa1); /* mask during init */
+	
+	/* master pic */
+	eisa_out8(0x11,0x20); /* ICW1 */   
+	eisa_out8(0x00,0x21); /* ICW2 */   
+	eisa_out8(0x04,0x21); /* ICW3 */   
+	eisa_out8(0x01,0x21); /* ICW4 */   
+	eisa_out8(0x40,0x20); /* OCW2 */   
+	
+	/* slave pic */
+	eisa_out8(0x11,0xa0); /* ICW1 */   
+	eisa_out8(0x08,0xa1); /* ICW2 */   
+        eisa_out8(0x02,0xa1); /* ICW3 */   
+	eisa_out8(0x01,0xa1); /* ICW4 */   
+	eisa_out8(0x40,0xa0); /* OCW2 */   
+        
+	udelay(100);
+	
+	slave_mask = 0xff; 
+	master_mask = 0xfb; 
+	eisa_out8(slave_mask, 0xa1); /* OCW1 */
+	eisa_out8(master_mask, 0x21); /* OCW1 */
+	
+	/* setup trig level */
+	EISA_DBG("EISA edge/level %04x\n", eisa_irq_level);
+	
+	eisa_out8(eisa_irq_level&0xff, 0x4d0); /* Set all irq's to edge  */
+	eisa_out8((eisa_irq_level >> 8) & 0xff, 0x4d1); 
+	
+	EISA_DBG("pic0 mask %02x\n", eisa_in8(0x21));
+	EISA_DBG("pic1 mask %02x\n", eisa_in8(0xa1));
+	EISA_DBG("pic0 edge/level %02x\n", eisa_in8(0x4d0));
+	EISA_DBG("pic1 edge/level %02x\n", eisa_in8(0x4d1));
+	
+	spin_unlock_irqrestore(&eisa_irq_lock, flags);
+}
+
+/* Device initialisation */
+
+#define is_mongoose(dev) (dev->id.sversion == 0x00076)
+
+static int __devinit eisa_probe(struct parisc_device *dev)
+{
+	int i, result;
+
+	char *name = is_mongoose(dev) ? "Mongoose" : "Wax";
+
+	printk(KERN_INFO "%s EISA Adapter found at 0x%08lx\n", 
+		name, dev->hpa);
+
+	eisa_dev.hba.dev = dev;
+	eisa_dev.hba.iommu = ccio_get_iommu(dev);
+
+	eisa_dev.hba.lmmio_space.name = "EISA";
+	eisa_dev.hba.lmmio_space.start = F_EXTEND(0xfc000000);
+	eisa_dev.hba.lmmio_space.end = F_EXTEND(0xffbfffff);
+	eisa_dev.hba.lmmio_space.flags = IORESOURCE_MEM;
+	result = ccio_request_resource(dev, &eisa_dev.hba.lmmio_space);
+	if (result < 0) {
+		printk(KERN_ERR "EISA: failed to claim EISA Bus address space!\n");
+		return result;
+	}
+	eisa_dev.hba.io_space.name = "EISA";
+	eisa_dev.hba.io_space.start = 0;
+	eisa_dev.hba.io_space.end = 0xffff;
+	eisa_dev.hba.lmmio_space.flags = IORESOURCE_IO;
+	result = request_resource(&ioport_resource, &eisa_dev.hba.io_space);
+	if (result < 0) {
+		printk(KERN_ERR "EISA: failed to claim EISA Bus port space!\n");
+		return result;
+	}
+	pcibios_register_hba(&eisa_dev.hba);
+
+	result = request_irq(dev->irq, eisa_irq, SA_SHIRQ, "EISA", &eisa_dev);
+	if (result) {
+		printk(KERN_ERR "EISA: request_irq failed!\n");
+		return result;
+	}
+	
+	/* Reserve IRQ2 */
+	irq_desc[2].action = &irq2_action;
+	
+	for (i = 0; i < 16; i++) {
+		irq_desc[i].handler = &eisa_interrupt_type;
+	}
+	
+	EISA_bus = 1;
+
+	if (dev->num_addrs) {
+		/* newer firmware hand out the eeprom address */
+		eisa_dev.eeprom_addr = dev->addr[0];
+	} else {
+		/* old firmware, need to figure out the box */
+		if (is_mongoose(dev)) {
+			eisa_dev.eeprom_addr = SNAKES_EEPROM_BASE_ADDR;
+		} else {
+			eisa_dev.eeprom_addr = MIRAGE_EEPROM_BASE_ADDR;
+		}
+	}
+	eisa_eeprom_addr = ioremap(eisa_dev.eeprom_addr, HPEE_MAX_LENGTH);
+	result = eisa_enumerator(eisa_dev.eeprom_addr, &eisa_dev.hba.io_space,
+			&eisa_dev.hba.lmmio_space);
+	init_eisa_pic();
+
+	if (result >= 0) {
+		/* FIXME : Don't enumerate the bus twice. */
+		eisa_dev.root.dev = &dev->dev;
+		dev->dev.driver_data = &eisa_dev.root;
+		eisa_dev.root.bus_base_addr = 0;
+		eisa_dev.root.res = &eisa_dev.hba.io_space;
+		eisa_dev.root.slots = result;
+		eisa_dev.root.dma_mask = 0xffffffff; /* wild guess */
+		if (eisa_root_register (&eisa_dev.root)) {
+			printk(KERN_ERR "EISA: Failed to register EISA root\n");
+			return -1;
+		}
+	}
+	
+	return 0;
+}
+
+static struct parisc_device_id eisa_tbl[] = {
+	{ HPHW_BA, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00076 }, /* Mongoose */
+	{ HPHW_BA, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00090 }, /* Wax EISA */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, eisa_tbl);
+
+static struct parisc_driver eisa_driver = {
+	.name =		"EISA Bus Adapter",
+	.id_table =	eisa_tbl,
+	.probe =	eisa_probe,
+};
+
+void __init eisa_init(void)
+{
+	register_parisc_driver(&eisa_driver);
+}
+
+
+static unsigned int eisa_irq_configured;
+void eisa_make_irq_level(int num)
+{
+	if (eisa_irq_configured& (1<<num)) {
+		printk(KERN_WARNING
+		       "IRQ %d polarity configured twice (last to level)\n", 
+		       num);
+	}
+	eisa_irq_level |= (1<<num); /* set the corresponding bit */
+	eisa_irq_configured |= (1<<num); /* set the corresponding bit */
+}
+
+void eisa_make_irq_edge(int num)
+{
+	if (eisa_irq_configured& (1<<num)) {
+		printk(KERN_WARNING 
+		       "IRQ %d polarity configured twice (last to edge)\n",
+		       num);
+	}
+	eisa_irq_level &= ~(1<<num); /* clear the corresponding bit */
+	eisa_irq_configured |= (1<<num); /* set the corresponding bit */
+}
+
+static int __init eisa_irq_setup(char *str)
+{
+	char *cur = str;
+	int val;
+
+	EISA_DBG("IRQ setup\n");
+	while (cur != NULL) {
+		char *pe;
+		
+		val = (int) simple_strtoul(cur, &pe, 0);
+		if (val > 15 || val < 0) {
+			printk(KERN_ERR "eisa: EISA irq value are 0-15\n");
+			continue;
+		}
+		if (val == 2) { 
+			val = 9;
+		}
+		eisa_make_irq_edge(val); /* clear the corresponding bit */
+		EISA_DBG("setting IRQ %d to edge-triggered mode\n", val);
+		
+		if ((cur = strchr(cur, ','))) {
+			cur++;
+		} else {
+			break;
+		}
+	}
+	return 1;
+}
+
+__setup("eisa_irq_edge=", eisa_irq_setup);
+
diff --git a/drivers/parisc/eisa_eeprom.c b/drivers/parisc/eisa_eeprom.c
new file mode 100644
index 0000000..3a1b482
--- /dev/null
+++ b/drivers/parisc/eisa_eeprom.c
@@ -0,0 +1,134 @@
+/* 
+ *    EISA "eeprom" support routines
+ *
+ *    Copyright (C) 2001 Thomas Bogendoerfer <tsbogend at parisc-linux.org>
+ *
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program 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 General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/eisa_eeprom.h>
+
+#define 	EISA_EEPROM_MINOR 241
+
+static loff_t eisa_eeprom_llseek(struct file *file, loff_t offset, int origin )
+{
+	switch (origin) {
+	  case 0:
+		/* nothing to do */
+		break;
+	  case 1:
+		offset += file->f_pos;
+		break;
+	  case 2:
+		offset += HPEE_MAX_LENGTH;
+		break;
+	}
+	return (offset >= 0 && offset < HPEE_MAX_LENGTH) ? (file->f_pos = offset) : -EINVAL;
+}
+
+static ssize_t eisa_eeprom_read(struct file * file,
+			      char *buf, size_t count, loff_t *ppos )
+{
+	unsigned char *tmp;
+	ssize_t ret;
+	int i;
+	
+	if (*ppos >= HPEE_MAX_LENGTH)
+		return 0;
+	
+	count = *ppos + count < HPEE_MAX_LENGTH ? count : HPEE_MAX_LENGTH - *ppos;
+	tmp = kmalloc(count, GFP_KERNEL);
+	if (tmp) {
+		for (i = 0; i < count; i++)
+			tmp[i] = readb(eisa_eeprom_addr+(*ppos)++);
+
+		if (copy_to_user (buf, tmp, count))
+			ret = -EFAULT;
+		else
+			ret = count;
+		kfree (tmp);
+	} else
+		ret = -ENOMEM;
+	
+	return ret;
+}
+
+static int eisa_eeprom_ioctl(struct inode *inode, struct file *file, 
+			   unsigned int cmd,
+			   unsigned long arg)
+{
+	return -ENOTTY;
+}
+
+static int eisa_eeprom_open(struct inode *inode, struct file *file)
+{
+	if (file->f_mode & 2)
+		return -EINVAL;
+   
+	return 0;
+}
+
+static int eisa_eeprom_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+/*
+ *	The various file operations we support.
+ */
+static struct file_operations eisa_eeprom_fops = {
+	.owner =	THIS_MODULE,
+	.llseek =	eisa_eeprom_llseek,
+	.read =		eisa_eeprom_read,
+	.ioctl =	eisa_eeprom_ioctl,
+	.open =		eisa_eeprom_open,
+	.release =	eisa_eeprom_release,
+};
+
+static struct miscdevice eisa_eeprom_dev = {
+	EISA_EEPROM_MINOR,
+	"eisa_eeprom",
+	&eisa_eeprom_fops
+};
+
+static int __init eisa_eeprom_init(void)
+{
+	int retval;
+
+	if (!eisa_eeprom_addr)
+		return -ENODEV;
+
+	retval = misc_register(&eisa_eeprom_dev);
+	if (retval < 0) {
+		printk(KERN_ERR "EISA EEPROM: cannot register misc device.\n");
+		return retval;
+	}
+
+	printk(KERN_INFO "EISA EEPROM at 0x%p\n", eisa_eeprom_addr);
+	return 0;
+}
+
+MODULE_LICENSE("GPL");
+
+module_init(eisa_eeprom_init);
diff --git a/drivers/parisc/eisa_enumerator.c b/drivers/parisc/eisa_enumerator.c
new file mode 100644
index 0000000..6d8aae0
--- /dev/null
+++ b/drivers/parisc/eisa_enumerator.c
@@ -0,0 +1,521 @@
+/*
+ * eisa_enumerator.c - provide support for EISA adapters in PA-RISC machines
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Copyright (c) 2002 Daniel Engstrom <5116@telia.com>
+ *
+ */
+
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+#include <asm/eisa_bus.h>
+#include <asm/eisa_eeprom.h>
+
+
+/*
+ * Todo:
+ * 
+ * PORT init with MASK attr and other size than byte
+ * MEMORY with other decode than 20 bit
+ * CRC stuff
+ * FREEFORM stuff
+ */
+
+#define EPI 0xc80
+#define NUM_SLOT 16
+#define SLOT2PORT(x) (x<<12)
+
+
+/* macros to handle unaligned accesses and 
+ * byte swapping. The data in the EEPROM is
+ * little-endian on the big-endian PAROSC */
+#define get_8(x) (*(u_int8_t*)(x))
+
+static inline u_int16_t get_16(const unsigned char *x)
+{ 
+	return (x[1] << 8) | x[0];
+}
+
+static inline u_int32_t get_32(const unsigned char *x)
+{
+	return (x[3] << 24) | (x[2] << 16) | (x[1] << 8) | x[0];
+}
+
+static inline u_int32_t get_24(const unsigned char *x)
+{
+	return (x[2] << 24) | (x[1] << 16) | (x[0] << 8);
+}
+
+static void print_eisa_id(char *s, u_int32_t id)
+{
+	char vendor[4];
+	int rev;
+	int device;
+	
+	rev = id & 0xff;
+	id >>= 8;
+	device = id & 0xff;
+	id >>= 8;
+	vendor[3] = '\0';
+	vendor[2] = '@' + (id & 0x1f);
+	id >>= 5;	
+	vendor[1] = '@' + (id & 0x1f);
+	id >>= 5;	
+	vendor[0] = '@' + (id & 0x1f);
+	id >>= 5;	
+	
+	sprintf(s, "%s%02X%02X", vendor, device, rev);
+}
+       
+static int configure_memory(const unsigned char *buf, 
+		       struct resource *mem_parent,
+		       char *name)
+{
+	int len;
+	u_int8_t c;
+	int i;
+	struct resource *res;
+	
+	len=0;
+	
+	for (i=0;i<HPEE_MEMORY_MAX_ENT;i++) {
+		c = get_8(buf+len);
+		
+		if (NULL != (res = kmalloc(sizeof(struct resource), GFP_KERNEL))) {
+			int result;
+			
+			res->name = name;
+			res->start = mem_parent->start + get_24(buf+len+2);
+			res->end = res->start + get_16(buf+len+5)*1024;
+			res->flags = IORESOURCE_MEM;
+			printk("memory %lx-%lx ", res->start, res->end);
+			result = request_resource(mem_parent, res);
+			if (result < 0) {
+				printk("\n" KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n");
+				return result;
+			}
+		}
+		 	
+		len+=7;      
+	
+		if (!(c & HPEE_MEMORY_MORE)) {
+			break;
+		}
+	}
+	
+	return len;
+}
+
+
+static int configure_irq(const unsigned char *buf)
+{
+	int len;
+	u_int8_t c;
+	int i;
+	
+	len=0;
+	
+	for (i=0;i<HPEE_IRQ_MAX_ENT;i++) {
+		c = get_8(buf+len);
+		
+		printk("IRQ %d ", c & HPEE_IRQ_CHANNEL_MASK);
+		if (c & HPEE_IRQ_TRIG_LEVEL) {
+			eisa_make_irq_level(c & HPEE_IRQ_CHANNEL_MASK);
+		} else {
+			eisa_make_irq_edge(c & HPEE_IRQ_CHANNEL_MASK);
+		}
+		
+		len+=2; 
+		/* hpux seems to allow for
+		 * two bytes of irq data but only defines one of
+		 * them, I think */
+		if  (!(c & HPEE_IRQ_MORE)) {
+			break;
+		}
+	}
+	
+	return len;
+}
+
+
+static int configure_dma(const unsigned char *buf)
+{
+	int len;
+	u_int8_t c;
+	int i;
+	
+	len=0;
+	
+	for (i=0;i<HPEE_DMA_MAX_ENT;i++) {
+		c = get_8(buf+len);
+		printk("DMA %d ", c&HPEE_DMA_CHANNEL_MASK);
+		/* fixme: maybe initialize the dma channel withthe timing ? */
+		len+=2;      
+		if (!(c & HPEE_DMA_MORE)) {
+			break;
+		}
+	}
+	
+	return len;
+}
+
+static int configure_port(const unsigned char *buf, struct resource *io_parent,
+		     char *board)
+{
+	int len;
+	u_int8_t c;
+	int i;
+	struct resource *res;
+	int result;
+	
+	len=0;
+	
+	for (i=0;i<HPEE_PORT_MAX_ENT;i++) {
+		c = get_8(buf+len);
+		
+		if (NULL != (res = kmalloc(sizeof(struct resource), GFP_KERNEL))) {
+			res->name = board;
+			res->start = get_16(buf+len+1);
+			res->end = get_16(buf+len+1)+(c&HPEE_PORT_SIZE_MASK)+1;
+			res->flags = IORESOURCE_IO;
+			printk("ioports %lx-%lx ", res->start, res->end);
+			result = request_resource(io_parent, res);
+			if (result < 0) {
+				printk("\n" KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n");
+				return result;
+			}
+		}
+
+		len+=3;      
+		if (!(c & HPEE_PORT_MORE)) {
+			break;
+		}
+	}
+	
+	return len;
+}
+
+
+/* byte 1 and 2 is the port number to write
+ * and at byte 3 the value to write starts.
+ * I assume that there are and- and or- masks
+ * here when HPEE_PORT_INIT_MASK is set but I have 
+ * not yet encountered this. */
+static int configure_port_init(const unsigned char *buf)
+{
+	int len=0;
+	u_int8_t c;
+	
+	while (len<HPEE_PORT_INIT_MAX_LEN) {
+		int s=0;
+		c = get_8(buf+len);
+		
+		switch (c & HPEE_PORT_INIT_WIDTH_MASK)  {
+		 case HPEE_PORT_INIT_WIDTH_BYTE:
+			s=1;
+			if (c & HPEE_PORT_INIT_MASK) {
+				printk("\n" KERN_WARNING "port_init: unverified mask attribute\n");
+				outb((inb(get_16(buf+len+1) & 
+					  get_8(buf+len+3)) | 
+				      get_8(buf+len+4)), get_16(buf+len+1));
+				      
+			} else {
+				outb(get_8(buf+len+3), get_16(buf+len+1));
+				      
+			}
+			break;
+		 case HPEE_PORT_INIT_WIDTH_WORD:
+			s=2;
+			if (c & HPEE_PORT_INIT_MASK) {
+ 				printk(KERN_WARNING "port_init: unverified mask attribute\n");
+				       outw((inw(get_16(buf+len+1)) &
+					     get_16(buf+len+3)) |
+					    get_16(buf+len+5), 
+					    get_16(buf+len+1));
+			} else {
+				outw(cpu_to_le16(get_16(buf+len+3)), get_16(buf+len+1));
+			}
+			break;
+		 case HPEE_PORT_INIT_WIDTH_DWORD:
+			s=4;
+			if (c & HPEE_PORT_INIT_MASK) {
+ 				printk("\n" KERN_WARNING "port_init: unverified mask attribute\n");
+				outl((inl(get_16(buf+len+1) &
+					  get_32(buf+len+3)) |
+				      get_32(buf+len+7)), get_16(buf+len+1));
+			} else {
+				outl(cpu_to_le32(get_32(buf+len+3)), get_16(buf+len+1));
+			}
+
+			break;
+		 default:
+			printk("\n" KERN_ERR "Invalid port init word %02x\n", c);
+			return 0;
+		}
+		
+		if (c & HPEE_PORT_INIT_MASK) {   
+			s*=2;
+		}
+		
+		len+=s+3;
+		if (!(c & HPEE_PORT_INIT_MORE)) {
+			break;
+		}
+	}
+	
+	return len;
+}
+
+static int configure_choise(const unsigned char *buf, u_int8_t *info)
+{
+	int len;
+	
+	/* theis record contain the value of the functions
+	 * configuration choises and an info byte which 
+	 * describes which other records to expect in this 
+	 * function */
+	len = get_8(buf);
+	*info=get_8(buf+len+1);
+	 
+	return len+2;
+}
+
+static int configure_type_string(const unsigned char *buf) 
+{
+	int len;
+	
+	/* just skip past the type field */
+	len = get_8(buf);
+	if (len > 80) {
+		printk("\n" KERN_ERR "eisa_enumerator: type info field too long (%d, max is 80)\n", len);
+	}
+	
+	return 1+len;
+}
+
+static int configure_function(const unsigned char *buf, int *more) 
+{
+	/* the init field seems to be a two-byte field
+	 * which is non-zero if there are an other function following
+	 * I think it is the length of the function def 
+	 */
+	*more = get_16(buf);
+	
+	return 2;
+}
+
+static int parse_slot_config(int slot,
+			     const unsigned char *buf,
+			     struct eeprom_eisa_slot_info *es, 
+			     struct resource *io_parent,
+			     struct resource *mem_parent)
+{
+	int res=0;
+	int function_len;
+	unsigned int pos=0;
+	unsigned int maxlen;
+	int num_func=0;
+	u_int8_t flags;
+	int p0;
+	
+	char *board;
+	int id_string_used=0;
+	
+	if (NULL == (board = kmalloc(8, GFP_KERNEL))) {
+		return -1;
+	}
+	print_eisa_id(board, es->eisa_slot_id);
+	printk(KERN_INFO "EISA slot %d: %s %s ", 
+	       slot, board, es->flags&HPEE_FLAG_BOARD_IS_ISA ? "ISA" : "EISA");
+	
+	maxlen = es->config_data_length < HPEE_MAX_LENGTH ?
+			 es->config_data_length : HPEE_MAX_LENGTH;
+	while ((pos < maxlen) && (num_func <= es->num_functions)) {
+		pos+=configure_function(buf+pos, &function_len); 
+		
+		if (!function_len) {
+			break;
+		}
+		num_func++;
+		p0 = pos;
+		pos += configure_choise(buf+pos, &flags);
+
+		if (flags & HPEE_FUNCTION_INFO_F_DISABLED) {
+			/* function disabled, skip silently */
+			pos = p0 + function_len;
+			continue;
+		}
+		if (flags & HPEE_FUNCTION_INFO_CFG_FREE_FORM) {
+			/* I have no idea how to handle this */
+			printk("function %d have free-form confgiuration, skipping ",
+				num_func);
+			pos = p0 + function_len;
+			continue;
+		}
+
+		/* the ordering of the sections need
+		 * more investigation.
+		 * Currently I think that memory comaed before IRQ
+		 * I assume the order is LSB to MSB in the 
+		 * info flags 
+		 * eg type, memory, irq, dma, port, HPEE_PORT_init 
+		 */
+
+		if (flags & HPEE_FUNCTION_INFO_HAVE_TYPE) {
+			pos += configure_type_string(buf+pos);
+		}
+		
+		if (flags & HPEE_FUNCTION_INFO_HAVE_MEMORY) {
+			id_string_used=1;
+			pos += configure_memory(buf+pos, mem_parent, board);
+		} 
+		
+		if (flags & HPEE_FUNCTION_INFO_HAVE_IRQ) {
+			pos += configure_irq(buf+pos);
+		} 
+		
+		if (flags & HPEE_FUNCTION_INFO_HAVE_DMA) {
+			pos += configure_dma(buf+pos);
+		} 
+		
+		if (flags & HPEE_FUNCTION_INFO_HAVE_PORT) {
+			id_string_used=1;
+			pos += configure_port(buf+pos, io_parent, board);
+		} 
+		
+		if (flags &  HPEE_FUNCTION_INFO_HAVE_PORT_INIT) {
+			pos += configure_port_init(buf+pos);
+		}
+		
+		if (p0 + function_len < pos) {
+			printk("\n" KERN_ERR "eisa_enumerator: function %d length mis-match "
+			       "got %d, expected %d\n",
+			       num_func, pos-p0, function_len);
+			res=-1;
+			break;
+		}
+		pos = p0 + function_len;
+	}
+	printk("\n");
+	if (!id_string_used) {
+		kfree(board);
+	}
+	
+	if (pos != es->config_data_length) {
+		printk(KERN_ERR "eisa_enumerator: config data length mis-match got %d, expected %d\n",
+			pos, es->config_data_length);
+		res=-1;
+	}
+	
+	if (num_func != es->num_functions) {
+		printk(KERN_ERR "eisa_enumerator: number of functions mis-match got %d, expected %d\n",
+			num_func, es->num_functions);
+		res=-2;
+	}
+	
+	return res;
+	
+}
+
+static int init_slot(int slot, struct eeprom_eisa_slot_info *es)
+{
+	unsigned int id;
+	
+	char id_string[8];
+	
+	if (!(es->slot_info&HPEE_SLOT_INFO_NO_READID)) {
+		/* try to read the id of the board in the slot */
+		id = le32_to_cpu(inl(SLOT2PORT(slot)+EPI));
+		
+		if (0xffffffff == id) {
+			/* Maybe we didn't expect a card to be here... */
+			if (es->eisa_slot_id == 0xffffffff)
+				return -1;
+			
+			/* this board is not here or it does not 
+			 * support readid 
+			 */
+			printk(KERN_ERR "EISA slot %d a configured board was not detected (", 
+			       slot);
+			
+			print_eisa_id(id_string, es->eisa_slot_id);
+			printk(" expected %s)\n", id_string);
+		
+			return -1;	
+
+		}
+		if (es->eisa_slot_id != id) {
+			print_eisa_id(id_string, id);
+			printk(KERN_ERR "EISA slot %d id mis-match: got %s", 
+			       slot, id_string);
+			
+			print_eisa_id(id_string, es->eisa_slot_id);
+			printk(" expected %s \n", id_string);
+		
+			return -1;	
+			
+		}
+	}
+	
+	/* now: we need to enable the board if 
+	 * it supports enabling and run through
+	 * the port init sction if present
+	 * and finally record any interrupt polarity
+	 */
+	if (es->slot_features & HPEE_SLOT_FEATURES_ENABLE) {
+		/* enable board */
+		outb(0x01| inb(SLOT2PORT(slot)+EPI+4),
+		     SLOT2PORT(slot)+EPI+4);
+	}
+	
+	return 0;
+}
+
+
+int eisa_enumerator(unsigned long eeprom_addr,
+		    struct resource *io_parent, struct resource *mem_parent) 
+{
+	int i;
+	struct eeprom_header *eh;
+	static char eeprom_buf[HPEE_MAX_LENGTH];
+	
+	for (i=0; i < HPEE_MAX_LENGTH; i++) {
+		eeprom_buf[i] = gsc_readb(eeprom_addr+i);
+	}
+	
+	printk(KERN_INFO "Enumerating EISA bus\n");
+		    	
+	eh = (struct eeprom_header*)(eeprom_buf);
+	for (i=0;i<eh->num_slots;i++) {
+		struct eeprom_eisa_slot_info *es;
+		
+		es = (struct eeprom_eisa_slot_info*)
+			(&eeprom_buf[HPEE_SLOT_INFO(i)]);
+	        
+		if (-1==init_slot(i+1, es)) {
+			continue;
+		}
+		
+		if (es->config_data_offset < HPEE_MAX_LENGTH) {
+			if (parse_slot_config(i+1, &eeprom_buf[es->config_data_offset],
+					      es, io_parent, mem_parent)) {
+				return -1;
+			}
+		} else {
+			printk (KERN_WARNING "EISA EEPROM offset 0x%x out of range\n",es->config_data_offset);
+			return -1;
+		}
+	}
+	return eh->num_slots;
+}
+
diff --git a/drivers/parisc/gsc.c b/drivers/parisc/gsc.c
new file mode 100644
index 0000000..af5e025
--- /dev/null
+++ b/drivers/parisc/gsc.c
@@ -0,0 +1,245 @@
+/*
+ * Interrupt management for most GSC and related devices.
+ *
+ * (c) Copyright 1999 Alex deVries for The Puffin Group
+ * (c) Copyright 1999 Grant Grundler for Hewlett-Packard
+ * (c) Copyright 1999 Matthew Wilcox
+ * (c) Copyright 2000 Helge Deller
+ * (c) Copyright 2001 Matthew Wilcox for Hewlett-Packard
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ */
+
+#include <linux/bitops.h>
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+
+#include "gsc.h"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DEBPRINTK printk
+#else
+#define DEBPRINTK(x,...)
+#endif
+
+int gsc_alloc_irq(struct gsc_irq *i)
+{
+	int irq = txn_alloc_irq(GSC_EIM_WIDTH);
+	if (irq < 0) {
+		printk("cannot get irq\n");
+		return irq;
+	}
+
+	i->txn_addr = txn_alloc_addr(irq);
+	i->txn_data = txn_alloc_data(irq);
+	i->irq = irq;
+
+	return irq;
+}
+
+int gsc_claim_irq(struct gsc_irq *i, int irq)
+{
+	int c = irq;
+
+	irq += CPU_IRQ_BASE; /* virtualize the IRQ first */
+
+	irq = txn_claim_irq(irq);
+	if (irq < 0) {
+		printk("cannot claim irq %d\n", c);
+		return irq;
+	}
+
+	i->txn_addr = txn_alloc_addr(irq);
+	i->txn_data = txn_alloc_data(irq);
+	i->irq = irq;
+
+	return irq;
+}
+
+EXPORT_SYMBOL(gsc_alloc_irq);
+EXPORT_SYMBOL(gsc_claim_irq);
+
+/* Common interrupt demultiplexer used by Asp, Lasi & Wax.  */
+irqreturn_t gsc_asic_intr(int gsc_asic_irq, void *dev, struct pt_regs *regs)
+{
+	unsigned long irr;
+	struct gsc_asic *gsc_asic = dev;
+
+	irr = gsc_readl(gsc_asic->hpa + OFFSET_IRR);
+	if (irr == 0)
+		return IRQ_NONE;
+
+	DEBPRINTK("%s intr, mask=0x%x\n", gsc_asic->name, irr);
+
+	do {
+		int local_irq = __ffs(irr);
+		unsigned int irq = gsc_asic->global_irq[local_irq];
+		__do_IRQ(irq, regs);
+		irr &= ~(1 << local_irq);
+	} while (irr);
+
+	return IRQ_HANDLED;
+}
+
+int gsc_find_local_irq(unsigned int irq, int *global_irqs, int limit)
+{
+	int local_irq;
+
+	for (local_irq = 0; local_irq < limit; local_irq++) {
+		if (global_irqs[local_irq] == irq)
+			return local_irq;
+	}
+
+	return NO_IRQ;
+}
+
+static void gsc_asic_disable_irq(unsigned int irq)
+{
+	struct gsc_asic *irq_dev = irq_desc[irq].handler_data;
+	int local_irq = gsc_find_local_irq(irq, irq_dev->global_irq, 32);
+	u32 imr;
+
+	DEBPRINTK(KERN_DEBUG "%s(%d) %s: IMR 0x%x\n", __FUNCTION__, irq,
+			irq_dev->name, imr);
+
+	/* Disable the IRQ line by clearing the bit in the IMR */
+	imr = gsc_readl(irq_dev->hpa + OFFSET_IMR);
+	imr &= ~(1 << local_irq);
+	gsc_writel(imr, irq_dev->hpa + OFFSET_IMR);
+}
+
+static void gsc_asic_enable_irq(unsigned int irq)
+{
+	struct gsc_asic *irq_dev = irq_desc[irq].handler_data;
+	int local_irq = gsc_find_local_irq(irq, irq_dev->global_irq, 32);
+	u32 imr;
+
+	DEBPRINTK(KERN_DEBUG "%s(%d) %s: IMR 0x%x\n", __FUNCTION__, irq,
+			irq_dev->name, imr);
+
+	/* Enable the IRQ line by setting the bit in the IMR */
+	imr = gsc_readl(irq_dev->hpa + OFFSET_IMR);
+	imr |= 1 << local_irq;
+	gsc_writel(imr, irq_dev->hpa + OFFSET_IMR);
+	/*
+	 * FIXME: read IPR to make sure the IRQ isn't already pending.
+	 *   If so, we need to read IRR and manually call do_irq().
+	 */
+}
+
+static unsigned int gsc_asic_startup_irq(unsigned int irq)
+{
+	gsc_asic_enable_irq(irq);
+	return 0;
+}
+
+static struct hw_interrupt_type gsc_asic_interrupt_type = {
+	.typename =	"GSC-ASIC",
+	.startup =	gsc_asic_startup_irq,
+	.shutdown =	gsc_asic_disable_irq,
+	.enable =	gsc_asic_enable_irq,
+	.disable =	gsc_asic_disable_irq,
+	.ack =		no_ack_irq,
+	.end =		no_end_irq,
+};
+
+int gsc_assign_irq(struct hw_interrupt_type *type, void *data)
+{
+	static int irq = GSC_IRQ_BASE;
+
+	if (irq > GSC_IRQ_MAX)
+		return NO_IRQ;
+
+	irq_desc[irq].handler = type;
+	irq_desc[irq].handler_data = data;
+	return irq++;
+}
+
+void gsc_asic_assign_irq(struct gsc_asic *asic, int local_irq, int *irqp)
+{
+	int irq = asic->global_irq[local_irq];
+	
+	if (irq <= 0) {
+		irq = gsc_assign_irq(&gsc_asic_interrupt_type, asic);
+		if (irq == NO_IRQ)
+			return;
+
+		asic->global_irq[local_irq] = irq;
+	}
+	*irqp = irq;
+}
+
+void gsc_fixup_irqs(struct parisc_device *parent, void *ctrl,
+			void (*choose_irq)(struct parisc_device *, void *))
+{
+	struct device *dev;
+
+	list_for_each_entry(dev, &parent->dev.children, node) {
+		struct parisc_device *padev = to_parisc_device(dev);
+
+		/* work-around for 715/64 and others which have parent 
+		   at path [5] and children at path [5/0/x] */
+		if (padev->id.hw_type == HPHW_FAULTY)
+			return gsc_fixup_irqs(padev, ctrl, choose_irq);
+		choose_irq(padev, ctrl);
+	}
+}
+
+int gsc_common_setup(struct parisc_device *parent, struct gsc_asic *gsc_asic)
+{
+	struct resource *res;
+	int i;
+
+	gsc_asic->gsc = parent;
+
+	/* Initialise local irq -> global irq mapping */
+	for (i = 0; i < 32; i++) {
+		gsc_asic->global_irq[i] = NO_IRQ;
+	}
+
+	/* allocate resource region */
+	res = request_mem_region(gsc_asic->hpa, 0x100000, gsc_asic->name);
+	if (res) {
+		res->flags = IORESOURCE_MEM; 	/* do not mark it busy ! */
+	}
+
+#if 0
+	printk(KERN_WARNING "%s IRQ %d EIM 0x%x", gsc_asic->name,
+			parent->irq, gsc_asic->eim);
+	if (gsc_readl(gsc_asic->hpa + OFFSET_IMR))
+		printk("  IMR is non-zero! (0x%x)",
+				gsc_readl(gsc_asic->hpa + OFFSET_IMR));
+	printk("\n");
+#endif
+
+	return 0;
+}
+
+extern struct parisc_driver lasi_driver;
+extern struct parisc_driver asp_driver;
+extern struct parisc_driver wax_driver;
+
+void __init gsc_init(void)
+{
+#ifdef CONFIG_GSC_LASI
+	register_parisc_driver(&lasi_driver);
+	register_parisc_driver(&asp_driver);
+#endif
+#ifdef CONFIG_GSC_WAX
+	register_parisc_driver(&wax_driver);
+#endif
+}
diff --git a/drivers/parisc/gsc.h b/drivers/parisc/gsc.h
new file mode 100644
index 0000000..a3dc456
--- /dev/null
+++ b/drivers/parisc/gsc.h
@@ -0,0 +1,47 @@
+/*
+ * drivers/parisc/gsc.h
+ * Declarations for functions in gsc.c
+ * Copyright (c) 2000-2002 Helge Deller, Matthew Wilcox
+ *
+ * Distributed under the terms of the GPL, version 2
+ */
+
+#include <linux/interrupt.h>
+#include <asm/hardware.h>
+#include <asm/parisc-device.h>
+
+#define OFFSET_IRR 0x0000   /* Interrupt request register */
+#define OFFSET_IMR 0x0004   /* Interrupt mask register */
+#define OFFSET_IPR 0x0008   /* Interrupt pending register */
+#define OFFSET_ICR 0x000C   /* Interrupt control register */
+#define OFFSET_IAR 0x0010   /* Interrupt address register */
+
+/* PA I/O Architected devices support at least 5 bits in the EIM register. */
+#define GSC_EIM_WIDTH 5
+
+struct gsc_irq {
+	unsigned long txn_addr;	/* IRQ "target" */
+	int txn_data;		/* HW "IRQ" */
+	int irq;		/* virtual IRQ */
+};
+
+struct gsc_asic {
+	struct parisc_device *gsc;
+	unsigned long hpa;
+	char *name;
+	int version;
+	int type;
+	int eim;
+	int global_irq[32];
+};
+
+int gsc_common_setup(struct parisc_device *parent, struct gsc_asic *gsc_asic);
+int gsc_alloc_irq(struct gsc_irq *dev);			/* dev needs an irq */
+int gsc_claim_irq(struct gsc_irq *dev, int irq);	/* dev needs this irq */
+int gsc_assign_irq(struct hw_interrupt_type *type, void *data);
+int gsc_find_local_irq(unsigned int irq, int *global_irq, int limit);
+void gsc_fixup_irqs(struct parisc_device *parent, void *ctrl,
+		void (*choose)(struct parisc_device *child, void *ctrl));
+void gsc_asic_assign_irq(struct gsc_asic *asic, int local_irq, int *irqp);
+
+irqreturn_t gsc_asic_intr(int irq, void *dev, struct pt_regs *regs);
diff --git a/drivers/parisc/hppb.c b/drivers/parisc/hppb.c
new file mode 100644
index 0000000..e869c60
--- /dev/null
+++ b/drivers/parisc/hppb.c
@@ -0,0 +1,109 @@
+/*
+** hppb.c:
+**      HP-PB bus driver for the NOVA and K-Class systems.
+**
+**      (c) Copyright 2002 Ryan Bradetich
+**      (c) Copyright 2002 Hewlett-Packard Company
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This Driver currently only supports the console (port 0) on the MUX.
+** Additional work will be needed on this driver to enable the full
+** functionality of the MUX.
+**
+*/
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/parisc-device.h>
+
+#include <linux/pci.h>
+
+struct hppb_card {
+	unsigned long hpa;
+	struct resource mmio_region;
+	struct hppb_card *next;
+};
+
+struct hppb_card hppb_card_head = {
+	.hpa = 0,
+	.next = NULL,
+};
+
+#define IO_IO_LOW  offsetof(struct bc_module, io_io_low)
+#define IO_IO_HIGH offsetof(struct bc_module, io_io_high)
+
+/**
+ * hppb_probe - Determine if the hppb driver should claim this device.
+ * @dev: The device which has been found
+ *
+ * Determine if hppb driver should claim this chip (return 0) or not 
+ * (return 1). If so, initialize the chip and tell other partners in crime 
+ * they have work to do.
+ */
+static int hppb_probe(struct parisc_device *dev)
+{
+	int status;
+	struct hppb_card *card = &hppb_card_head;
+
+	while(card->next) {
+		card = card->next;
+	}
+
+	if(card->hpa) {
+		card->next = kmalloc(sizeof(struct hppb_card), GFP_KERNEL);
+		if(!card->next) {
+			printk(KERN_ERR "HP-PB: Unable to allocate memory.\n");
+			return 1;
+		}
+		memset(card->next, '\0', sizeof(struct hppb_card));
+		card = card->next;
+	}
+        printk(KERN_INFO "Found GeckoBoa at 0x%lx\n", dev->hpa);
+
+	card->hpa = dev->hpa;
+	card->mmio_region.name = "HP-PB Bus";
+	card->mmio_region.flags = IORESOURCE_MEM;
+
+	card->mmio_region.start = __raw_readl(dev->hpa + IO_IO_LOW);
+	card->mmio_region.end = __raw_readl(dev->hpa + IO_IO_HIGH) - 1;
+
+	status = ccio_request_resource(dev, &card->mmio_region);
+	if(status < 0) {
+		printk(KERN_ERR "%s: failed to claim HP-PB bus space (%08lx, %08lx)\n",
+			__FILE__, card->mmio_region.start, card->mmio_region.end);
+	}
+
+        return 0;
+}
+
+
+static struct parisc_device_id hppb_tbl[] = {
+        { HPHW_BCPORT, HVERSION_REV_ANY_ID, 0x500, 0xc }, 
+        { 0, }
+};
+
+static struct parisc_driver hppb_driver = {
+        .name =         "Gecko Boa",
+        .id_table =     hppb_tbl,
+	.probe =        hppb_probe,
+};
+
+/**
+ * hppb_init - HP-PB bus initalization procedure.
+ *
+ * Register this driver.   
+ */
+void __init hppb_init(void)
+{
+        register_parisc_driver(&hppb_driver);
+}
diff --git a/drivers/parisc/iommu-helpers.h b/drivers/parisc/iommu-helpers.h
new file mode 100644
index 0000000..38d9e1a
--- /dev/null
+++ b/drivers/parisc/iommu-helpers.h
@@ -0,0 +1,171 @@
+/**
+ * iommu_fill_pdir - Insert coalesced scatter/gather chunks into the I/O Pdir.
+ * @ioc: The I/O Controller.
+ * @startsg: The scatter/gather list of coalesced chunks.
+ * @nents: The number of entries in the scatter/gather list.
+ * @hint: The DMA Hint.
+ *
+ * This function inserts the coalesced scatter/gather list chunks into the
+ * I/O Controller's I/O Pdir.
+ */ 
+static inline unsigned int
+iommu_fill_pdir(struct ioc *ioc, struct scatterlist *startsg, int nents, 
+		unsigned long hint,
+		void (*iommu_io_pdir_entry)(u64 *, space_t, unsigned long,
+					    unsigned long))
+{
+	struct scatterlist *dma_sg = startsg;	/* pointer to current DMA */
+	unsigned int n_mappings = 0;
+	unsigned long dma_offset = 0, dma_len = 0;
+	u64 *pdirp = NULL;
+
+	/* Horrible hack.  For efficiency's sake, dma_sg starts one 
+	 * entry below the true start (it is immediately incremented
+	 * in the loop) */
+	 dma_sg--;
+
+	while (nents-- > 0) {
+		unsigned long vaddr;
+		long size;
+
+		DBG_RUN_SG(" %d : %08lx/%05x %08lx/%05x\n", nents,
+			   (unsigned long)sg_dma_address(startsg), cnt,
+			   sg_virt_addr(startsg), startsg->length
+		);
+
+
+		/*
+		** Look for the start of a new DMA stream
+		*/
+		
+		if (sg_dma_address(startsg) & PIDE_FLAG) {
+			u32 pide = sg_dma_address(startsg) & ~PIDE_FLAG;
+
+			BUG_ON(pdirp && (dma_len != sg_dma_len(dma_sg)));
+
+			dma_sg++;
+
+			dma_len = sg_dma_len(startsg);
+			sg_dma_len(startsg) = 0;
+			dma_offset = (unsigned long) pide & ~IOVP_MASK;
+			n_mappings++;
+#if defined(ZX1_SUPPORT)
+			/* Pluto IOMMU IO Virt Address is not zero based */
+			sg_dma_address(dma_sg) = pide | ioc->ibase;
+#else
+			/* SBA, ccio, and dino are zero based.
+			 * Trying to save a few CPU cycles for most users.
+			 */
+			sg_dma_address(dma_sg) = pide;
+#endif
+			pdirp = &(ioc->pdir_base[pide >> IOVP_SHIFT]);
+			prefetchw(pdirp);
+		}
+		
+		BUG_ON(pdirp == NULL);
+		
+		vaddr = sg_virt_addr(startsg);
+		sg_dma_len(dma_sg) += startsg->length;
+		size = startsg->length + dma_offset;
+		dma_offset = 0;
+#ifdef IOMMU_MAP_STATS
+		ioc->msg_pages += startsg->length >> IOVP_SHIFT;
+#endif
+		do {
+			iommu_io_pdir_entry(pdirp, KERNEL_SPACE, 
+					    vaddr, hint);
+			vaddr += IOVP_SIZE;
+			size -= IOVP_SIZE;
+			pdirp++;
+		} while(unlikely(size > 0));
+		startsg++;
+	}
+	return(n_mappings);
+}
+
+
+/*
+** First pass is to walk the SG list and determine where the breaks are
+** in the DMA stream. Allocates PDIR entries but does not fill them.
+** Returns the number of DMA chunks.
+**
+** Doing the fill separate from the coalescing/allocation keeps the
+** code simpler. Future enhancement could make one pass through
+** the sglist do both.
+*/
+
+static inline unsigned int
+iommu_coalesce_chunks(struct ioc *ioc, struct scatterlist *startsg, int nents,
+		      int (*iommu_alloc_range)(struct ioc *, size_t))
+{
+	struct scatterlist *contig_sg;	   /* contig chunk head */
+	unsigned long dma_offset, dma_len; /* start/len of DMA stream */
+	unsigned int n_mappings = 0;
+
+	while (nents > 0) {
+
+		/*
+		** Prepare for first/next DMA stream
+		*/
+		contig_sg = startsg;
+		dma_len = startsg->length;
+		dma_offset = sg_virt_addr(startsg) & ~IOVP_MASK;
+
+		/* PARANOID: clear entries */
+		sg_dma_address(startsg) = 0;
+		sg_dma_len(startsg) = 0;
+
+		/*
+		** This loop terminates one iteration "early" since
+		** it's always looking one "ahead".
+		*/
+		while(--nents > 0) {
+			unsigned long prevstartsg_end, startsg_end;
+
+			prevstartsg_end = sg_virt_addr(startsg) +
+				startsg->length;
+
+			startsg++;
+			startsg_end = sg_virt_addr(startsg) + 
+				startsg->length;
+
+			/* PARANOID: clear entries */
+			sg_dma_address(startsg) = 0;
+			sg_dma_len(startsg) = 0;
+
+			/*
+			** First make sure current dma stream won't
+			** exceed DMA_CHUNK_SIZE if we coalesce the
+			** next entry.
+			*/   
+			if(unlikely(ROUNDUP(dma_len + dma_offset + startsg->length,
+					    IOVP_SIZE) > DMA_CHUNK_SIZE))
+				break;
+
+			/*
+			** Next see if we can append the next chunk (i.e.
+			** it must end on one page and begin on another
+			*/
+			if (unlikely(((prevstartsg_end | sg_virt_addr(startsg)) & ~PAGE_MASK) != 0))
+				break;
+			
+			dma_len += startsg->length;
+		}
+
+		/*
+		** End of DMA Stream
+		** Terminate last VCONTIG block.
+		** Allocate space for DMA stream.
+		*/
+		sg_dma_len(contig_sg) = dma_len;
+		dma_len = ROUNDUP(dma_len + dma_offset, IOVP_SIZE);
+		sg_dma_address(contig_sg) =
+			PIDE_FLAG 
+			| (iommu_alloc_range(ioc, dma_len) << IOVP_SHIFT)
+			| dma_offset;
+		n_mappings++;
+	}
+
+	return n_mappings;
+}
+
diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c
new file mode 100644
index 0000000..91df0bf
--- /dev/null
+++ b/drivers/parisc/iosapic.c
@@ -0,0 +1,921 @@
+/*
+** I/O Sapic Driver - PCI interrupt line support
+**
+**      (c) Copyright 1999 Grant Grundler
+**      (c) Copyright 1999 Hewlett-Packard Company
+**
+**      This program is free software; you can redistribute it and/or modify
+**      it under the terms of the GNU General Public License as published by
+**      the Free Software Foundation; either version 2 of the License, or
+**      (at your option) any later version.
+**
+** The I/O sapic driver manages the Interrupt Redirection Table which is
+** the control logic to convert PCI line based interrupts into a Message
+** Signaled Interrupt (aka Transaction Based Interrupt, TBI).
+**
+** Acronyms
+** --------
+** HPA  Hard Physical Address (aka MMIO address)
+** IRQ  Interrupt ReQuest. Implies Line based interrupt.
+** IRT	Interrupt Routing Table (provided by PAT firmware)
+** IRdT Interrupt Redirection Table. IRQ line to TXN ADDR/DATA
+**      table which is implemented in I/O SAPIC.
+** ISR  Interrupt Service Routine. aka Interrupt handler.
+** MSI	Message Signaled Interrupt. PCI 2.2 functionality.
+**      aka Transaction Based Interrupt (or TBI).
+** PA   Precision Architecture. HP's RISC architecture.
+** RISC Reduced Instruction Set Computer.
+**
+**
+** What's a Message Signalled Interrupt?
+** -------------------------------------
+** MSI is a write transaction which targets a processor and is similar
+** to a processor write to memory or MMIO. MSIs can be generated by I/O
+** devices as well as processors and require *architecture* to work.
+**
+** PA only supports MSI. So I/O subsystems must either natively generate
+** MSIs (e.g. GSC or HP-PB) or convert line based interrupts into MSIs
+** (e.g. PCI and EISA).  IA64 supports MSIs via a "local SAPIC" which
+** acts on behalf of a processor.
+**
+** MSI allows any I/O device to interrupt any processor. This makes
+** load balancing of the interrupt processing possible on an SMP platform.
+** Interrupts are also ordered WRT to DMA data.  It's possible on I/O
+** coherent systems to completely eliminate PIO reads from the interrupt
+** path. The device and driver must be designed and implemented to
+** guarantee all DMA has been issued (issues about atomicity here)
+** before the MSI is issued. I/O status can then safely be read from
+** DMA'd data by the ISR.
+**
+**
+** PA Firmware
+** -----------
+** PA-RISC platforms have two fundementally different types of firmware.
+** For PCI devices, "Legacy" PDC initializes the "INTERRUPT_LINE" register
+** and BARs similar to a traditional PC BIOS.
+** The newer "PAT" firmware supports PDC calls which return tables.
+** PAT firmware only initializes PCI Console and Boot interface.
+** With these tables, the OS can progam all other PCI devices.
+**
+** One such PAT PDC call returns the "Interrupt Routing Table" (IRT).
+** The IRT maps each PCI slot's INTA-D "output" line to an I/O SAPIC
+** input line.  If the IRT is not available, this driver assumes
+** INTERRUPT_LINE register has been programmed by firmware. The latter
+** case also means online addition of PCI cards can NOT be supported
+** even if HW support is present.
+**
+** All platforms with PAT firmware to date (Oct 1999) use one Interrupt
+** Routing Table for the entire platform.
+**
+** Where's the iosapic?
+** --------------------
+** I/O sapic is part of the "Core Electronics Complex". And on HP platforms
+** it's integrated as part of the PCI bus adapter, "lba".  So no bus walk
+** will discover I/O Sapic. I/O Sapic driver learns about each device
+** when lba driver advertises the presence of the I/O sapic by calling
+** iosapic_register().
+**
+**
+** IRQ handling notes
+** ------------------
+** The IO-SAPIC can indicate to the CPU which interrupt was asserted.
+** So, unlike the GSC-ASIC and Dino, we allocate one CPU interrupt per
+** IO-SAPIC interrupt and call the device driver's handler directly.
+** The IO-SAPIC driver hijacks the CPU interrupt handler so it can
+** issue the End Of Interrupt command to the IO-SAPIC.
+**
+** Overview of exported iosapic functions
+** --------------------------------------
+** (caveat: code isn't finished yet - this is just the plan)
+**
+** iosapic_init:
+**   o initialize globals (lock, etc)
+**   o try to read IRT. Presence of IRT determines if this is
+**     a PAT platform or not.
+**
+** iosapic_register():
+**   o create iosapic_info instance data structure
+**   o allocate vector_info array for this iosapic
+**   o initialize vector_info - read corresponding IRdT?
+**
+** iosapic_xlate_pin: (only called by fixup_irq for PAT platform)
+**   o intr_pin = read cfg (INTERRUPT_PIN);
+**   o if (device under PCI-PCI bridge)
+**               translate slot/pin
+**
+** iosapic_fixup_irq:
+**   o if PAT platform (IRT present)
+**	   intr_pin = iosapic_xlate_pin(isi,pcidev):
+**         intr_line = find IRT entry(isi, PCI_SLOT(pcidev), intr_pin)
+**         save IRT entry into vector_info later
+**         write cfg INTERRUPT_LINE (with intr_line)?
+**     else
+**         intr_line = pcidev->irq
+**         IRT pointer = NULL
+**     endif
+**   o locate vector_info (needs: isi, intr_line)
+**   o allocate processor "irq" and get txn_addr/data
+**   o request_irq(processor_irq,  iosapic_interrupt, vector_info,...)
+**
+** iosapic_enable_irq:
+**   o clear any pending IRQ on that line
+**   o enable IRdT - call enable_irq(vector[line]->processor_irq)
+**   o write EOI in case line is already asserted.
+**
+** iosapic_disable_irq:
+**   o disable IRdT - call disable_irq(vector[line]->processor_irq)
+*/
+
+
+/* FIXME: determine which include files are really needed */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <asm/byteorder.h>	/* get in-line asm for swab */
+#include <asm/pdc.h>
+#include <asm/pdcpat.h>
+#include <asm/page.h>
+#include <asm/system.h>
+#include <asm/io.h>		/* read/write functions */
+#ifdef CONFIG_SUPERIO
+#include <asm/superio.h>
+#endif
+
+#include <asm/iosapic.h>
+#include "./iosapic_private.h"
+
+#define MODULE_NAME "iosapic"
+
+/* "local" compile flags */
+#undef PCI_BRIDGE_FUNCS
+#undef DEBUG_IOSAPIC
+#undef DEBUG_IOSAPIC_IRT
+
+
+#ifdef DEBUG_IOSAPIC
+#define DBG(x...) printk(x)
+#else /* DEBUG_IOSAPIC */
+#define DBG(x...)
+#endif /* DEBUG_IOSAPIC */
+
+#ifdef DEBUG_IOSAPIC_IRT
+#define DBG_IRT(x...) printk(x)
+#else
+#define DBG_IRT(x...)
+#endif
+
+#ifdef CONFIG_64BIT
+#define COMPARE_IRTE_ADDR(irte, hpa)	((irte)->dest_iosapic_addr == (hpa))
+#else
+#define COMPARE_IRTE_ADDR(irte, hpa)	\
+		((irte)->dest_iosapic_addr == ((hpa) | 0xffffffff00000000ULL))
+#endif
+
+#define IOSAPIC_REG_SELECT              0x00
+#define IOSAPIC_REG_WINDOW              0x10
+#define IOSAPIC_REG_EOI                 0x40
+
+#define IOSAPIC_REG_VERSION		0x1
+
+#define IOSAPIC_IRDT_ENTRY(idx)		(0x10+(idx)*2)
+#define IOSAPIC_IRDT_ENTRY_HI(idx)	(0x11+(idx)*2)
+
+static inline unsigned int iosapic_read(void __iomem *iosapic, unsigned int reg)
+{
+	writel(reg, iosapic + IOSAPIC_REG_SELECT);
+	return readl(iosapic + IOSAPIC_REG_WINDOW);
+}
+
+static inline void iosapic_write(void __iomem *iosapic, unsigned int reg, u32 val)
+{
+	writel(reg, iosapic + IOSAPIC_REG_SELECT);
+	writel(val, iosapic + IOSAPIC_REG_WINDOW);
+}
+
+#define IOSAPIC_VERSION_MASK	0x000000ff
+#define	IOSAPIC_VERSION(ver)	((int) (ver & IOSAPIC_VERSION_MASK))
+
+#define IOSAPIC_MAX_ENTRY_MASK          0x00ff0000
+#define IOSAPIC_MAX_ENTRY_SHIFT         0x10
+#define	IOSAPIC_IRDT_MAX_ENTRY(ver)	\
+	(int) (((ver) & IOSAPIC_MAX_ENTRY_MASK) >> IOSAPIC_MAX_ENTRY_SHIFT)
+
+/* bits in the "low" I/O Sapic IRdT entry */
+#define IOSAPIC_IRDT_ENABLE       0x10000
+#define IOSAPIC_IRDT_PO_LOW       0x02000
+#define IOSAPIC_IRDT_LEVEL_TRIG   0x08000
+#define IOSAPIC_IRDT_MODE_LPRI    0x00100
+
+/* bits in the "high" I/O Sapic IRdT entry */
+#define IOSAPIC_IRDT_ID_EID_SHIFT              0x10
+
+
+static spinlock_t iosapic_lock = SPIN_LOCK_UNLOCKED;
+
+static inline void iosapic_eoi(void __iomem *addr, unsigned int data)
+{
+	__raw_writel(data, addr);
+}
+
+/*
+** REVISIT: future platforms may have more than one IRT.
+** If so, the following three fields form a structure which
+** then be linked into a list. Names are chosen to make searching
+** for them easy - not necessarily accurate (eg "cell").
+**
+** Alternative: iosapic_info could point to the IRT it's in.
+** iosapic_register() could search a list of IRT's.
+*/
+static struct irt_entry *irt_cell;
+static size_t irt_num_entry;
+
+static struct irt_entry *iosapic_alloc_irt(int num_entries)
+{
+	unsigned long a;
+
+	/* The IRT needs to be 8-byte aligned for the PDC call. 
+	 * Normally kmalloc would guarantee larger alignment, but
+	 * if CONFIG_DEBUG_SLAB is enabled, then we can get only
+	 * 4-byte alignment on 32-bit kernels
+	 */
+	a = (unsigned long)kmalloc(sizeof(struct irt_entry) * num_entries + 8, GFP_KERNEL);
+	a = (a + 7) & ~7;
+	return (struct irt_entry *)a;
+}
+
+/**
+ * iosapic_load_irt - Fill in the interrupt routing table
+ * @cell_num: The cell number of the CPU we're currently executing on
+ * @irt: The address to place the new IRT at
+ * @return The number of entries found
+ *
+ * The "Get PCI INT Routing Table Size" option returns the number of 
+ * entries in the PCI interrupt routing table for the cell specified 
+ * in the cell_number argument.  The cell number must be for a cell 
+ * within the caller's protection domain.
+ *
+ * The "Get PCI INT Routing Table" option returns, for the cell 
+ * specified in the cell_number argument, the PCI interrupt routing 
+ * table in the caller allocated memory pointed to by mem_addr.
+ * We assume the IRT only contains entries for I/O SAPIC and
+ * calculate the size based on the size of I/O sapic entries.
+ *
+ * The PCI interrupt routing table entry format is derived from the
+ * IA64 SAL Specification 2.4.   The PCI interrupt routing table defines
+ * the routing of PCI interrupt signals between the PCI device output
+ * "pins" and the IO SAPICs' input "lines" (including core I/O PCI
+ * devices).  This table does NOT include information for devices/slots
+ * behind PCI to PCI bridges. See PCI to PCI Bridge Architecture Spec.
+ * for the architected method of routing of IRQ's behind PPB's.
+ */
+
+
+static int __init
+iosapic_load_irt(unsigned long cell_num, struct irt_entry **irt)
+{
+	long status;              /* PDC return value status */
+	struct irt_entry *table;  /* start of interrupt routing tbl */
+	unsigned long num_entries = 0UL;
+
+	BUG_ON(!irt);
+
+	if (is_pdc_pat()) {
+		/* Use pat pdc routine to get interrupt routing table size */
+		DBG("calling get_irt_size (cell %ld)\n", cell_num);
+		status = pdc_pat_get_irt_size(&num_entries, cell_num);
+		DBG("get_irt_size: %ld\n", status);
+
+		BUG_ON(status != PDC_OK);
+		BUG_ON(num_entries == 0);
+
+		/*
+		** allocate memory for interrupt routing table
+		** This interface isn't really right. We are assuming
+		** the contents of the table are exclusively
+		** for I/O sapic devices.
+		*/
+		table = iosapic_alloc_irt(num_entries);
+		if (table == NULL) {
+			printk(KERN_WARNING MODULE_NAME ": read_irt : can "
+					"not alloc mem for IRT\n");
+			return 0;
+		}
+
+		/* get PCI INT routing table */
+		status = pdc_pat_get_irt(table, cell_num);
+		DBG("pdc_pat_get_irt: %ld\n", status);
+		WARN_ON(status != PDC_OK);
+	} else {
+		/*
+		** C3000/J5000 (and similar) platforms with Sprockets PDC
+		** will return exactly one IRT for all iosapics.
+		** So if we have one, don't need to get it again.
+		*/
+		if (irt_cell)
+			return 0;
+
+		/* Should be using the Elroy's HPA, but it's ignored anyway */
+		status = pdc_pci_irt_size(&num_entries, 0);
+		DBG("pdc_pci_irt_size: %ld\n", status);
+
+		if (status != PDC_OK) {
+			/* Not a "legacy" system with I/O SAPIC either */
+			return 0;
+		}
+
+		BUG_ON(num_entries == 0);
+
+		table = iosapic_alloc_irt(num_entries);
+		if (!table) {
+			printk(KERN_WARNING MODULE_NAME ": read_irt : can "
+					"not alloc mem for IRT\n");
+			return 0;
+		}
+
+		/* HPA ignored by this call too. */
+		status = pdc_pci_irt(num_entries, 0, table);
+		BUG_ON(status != PDC_OK);
+	}
+
+	/* return interrupt table address */
+	*irt = table;
+
+#ifdef DEBUG_IOSAPIC_IRT
+{
+	struct irt_entry *p = table;
+	int i;
+
+	printk(MODULE_NAME " Interrupt Routing Table (cell %ld)\n", cell_num);
+	printk(MODULE_NAME " start = 0x%p num_entries %ld entry_size %d\n",
+		table,
+		num_entries,
+		(int) sizeof(struct irt_entry));
+
+	for (i = 0 ; i < num_entries ; i++, p++) {
+		printk(MODULE_NAME " %02x %02x %02x %02x %02x %02x %02x %02x %08x%08x\n",
+		p->entry_type, p->entry_length, p->interrupt_type,
+		p->polarity_trigger, p->src_bus_irq_devno, p->src_bus_id,
+		p->src_seg_id, p->dest_iosapic_intin,
+		((u32 *) p)[2],
+		((u32 *) p)[3]
+		);
+	}
+}
+#endif /* DEBUG_IOSAPIC_IRT */
+
+	return num_entries;
+}
+
+
+
+void __init iosapic_init(void)
+{
+	unsigned long cell = 0;
+
+	DBG("iosapic_init()\n");
+
+#ifdef __LP64__
+	if (is_pdc_pat()) {
+		int status;
+		struct pdc_pat_cell_num cell_info;
+
+		status = pdc_pat_cell_get_number(&cell_info);
+		if (status == PDC_OK) {
+			cell = cell_info.cell_num;
+		}
+	}
+#endif
+
+	/* get interrupt routing table for this cell */
+	irt_num_entry = iosapic_load_irt(cell, &irt_cell);
+	if (irt_num_entry == 0)
+		irt_cell = NULL;	/* old PDC w/o iosapic */
+}
+
+
+/*
+** Return the IRT entry in case we need to look something else up.
+*/
+static struct irt_entry *
+irt_find_irqline(struct iosapic_info *isi, u8 slot, u8 intr_pin)
+{
+	struct irt_entry *i = irt_cell;
+	int cnt;	/* track how many entries we've looked at */
+	u8 irq_devno = (slot << IRT_DEV_SHIFT) | (intr_pin-1);
+
+	DBG_IRT("irt_find_irqline() SLOT %d pin %d\n", slot, intr_pin);
+
+	for (cnt=0; cnt < irt_num_entry; cnt++, i++) {
+
+		/*
+		** Validate: entry_type, entry_length, interrupt_type
+		**
+		** Difference between validate vs compare is the former
+		** should print debug info and is not expected to "fail"
+		** on current platforms.
+		*/
+		if (i->entry_type != IRT_IOSAPIC_TYPE) {
+			DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d type %d\n", i, cnt, i->entry_type);
+			continue;
+		}
+		
+		if (i->entry_length != IRT_IOSAPIC_LENGTH) {
+			DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d  length %d\n", i, cnt, i->entry_length);
+			continue;
+		}
+
+		if (i->interrupt_type != IRT_VECTORED_INTR) {
+			DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry  %d interrupt_type %d\n", i, cnt, i->interrupt_type);
+			continue;
+		}
+
+		if (!COMPARE_IRTE_ADDR(i, isi->isi_hpa))
+			continue;
+
+		if ((i->src_bus_irq_devno & IRT_IRQ_DEVNO_MASK) != irq_devno)
+			continue;
+
+		/*
+		** Ignore: src_bus_id and rc_seg_id correlate with
+		**         iosapic_info->isi_hpa on HP platforms.
+		**         If needed, pass in "PFA" (aka config space addr)
+		**         instead of slot.
+		*/
+
+		/* Found it! */
+		return i;
+	}
+
+	printk(KERN_WARNING MODULE_NAME ": 0x%lx : no IRT entry for slot %d, pin %d\n",
+			isi->isi_hpa, slot, intr_pin);
+	return NULL;
+}
+
+
+/*
+** xlate_pin() supports the skewing of IRQ lines done by subsidiary bridges.
+** Legacy PDC already does this translation for us and stores it in INTR_LINE.
+**
+** PAT PDC needs to basically do what legacy PDC does:
+** o read PIN
+** o adjust PIN in case device is "behind" a PPB
+**     (eg 4-port 100BT and SCSI/LAN "Combo Card")
+** o convert slot/pin to I/O SAPIC input line.
+**
+** HP platforms only support:
+** o one level of skewing for any number of PPBs
+** o only support PCI-PCI Bridges.
+*/
+static struct irt_entry *
+iosapic_xlate_pin(struct iosapic_info *isi, struct pci_dev *pcidev)
+{
+	u8 intr_pin, intr_slot;
+
+	pci_read_config_byte(pcidev, PCI_INTERRUPT_PIN, &intr_pin);
+
+	DBG_IRT("iosapic_xlate_pin(%s) SLOT %d pin %d\n",
+		pcidev->slot_name, PCI_SLOT(pcidev->devfn), intr_pin);
+
+	if (intr_pin == 0) {
+		/* The device does NOT support/use IRQ lines.  */
+		return NULL;
+	}
+
+	/* Check if pcidev behind a PPB */
+	if (NULL != pcidev->bus->self) {
+		/* Convert pcidev INTR_PIN into something we
+		** can lookup in the IRT.
+		*/
+#ifdef PCI_BRIDGE_FUNCS
+		/*
+		** Proposal #1:
+		**
+		** call implementation specific translation function
+		** This is architecturally "cleaner". HP-UX doesn't
+		** support other secondary bus types (eg. E/ISA) directly.
+		** May be needed for other processor (eg IA64) architectures
+		** or by some ambitous soul who wants to watch TV.
+		*/
+		if (pci_bridge_funcs->xlate_intr_line) {
+			intr_pin = pci_bridge_funcs->xlate_intr_line(pcidev);
+		}
+#else	/* PCI_BRIDGE_FUNCS */
+		struct pci_bus *p = pcidev->bus;
+		/*
+		** Proposal #2:
+		** The "pin" is skewed ((pin + dev - 1) % 4).
+		**
+		** This isn't very clean since I/O SAPIC must assume:
+		**   - all platforms only have PCI busses.
+		**   - only PCI-PCI bridge (eg not PCI-EISA, PCI-PCMCIA)
+		**   - IRQ routing is only skewed once regardless of
+		**     the number of PPB's between iosapic and device.
+		**     (Bit3 expansion chassis follows this rule)
+		**
+		** Advantage is it's really easy to implement.
+		*/
+		intr_pin = ((intr_pin-1)+PCI_SLOT(pcidev->devfn)) % 4;
+		intr_pin++;	/* convert back to INTA-D (1-4) */
+#endif /* PCI_BRIDGE_FUNCS */
+
+		/*
+		** Locate the host slot the PPB nearest the Host bus
+		** adapter.
+		*/
+		while (NULL != p->parent->self)
+			p = p->parent;
+
+		intr_slot = PCI_SLOT(p->self->devfn);
+	} else {
+		intr_slot = PCI_SLOT(pcidev->devfn);
+	}
+	DBG_IRT("iosapic_xlate_pin:  bus %d slot %d pin %d\n",
+				pcidev->bus->secondary, intr_slot, intr_pin);
+
+	return irt_find_irqline(isi, intr_slot, intr_pin);
+}
+
+static void iosapic_rd_irt_entry(struct vector_info *vi , u32 *dp0, u32 *dp1)
+{
+	struct iosapic_info *isp = vi->iosapic;
+	u8 idx = vi->irqline;
+
+	*dp0 = iosapic_read(isp->addr, IOSAPIC_IRDT_ENTRY(idx));
+	*dp1 = iosapic_read(isp->addr, IOSAPIC_IRDT_ENTRY_HI(idx));
+}
+
+
+static void iosapic_wr_irt_entry(struct vector_info *vi, u32 dp0, u32 dp1)
+{
+	struct iosapic_info *isp = vi->iosapic;
+
+	DBG_IRT("iosapic_wr_irt_entry(): irq %d hpa %lx 0x%x 0x%x\n",
+		vi->irqline, isp->isi_hpa, dp0, dp1);
+
+	iosapic_write(isp->addr, IOSAPIC_IRDT_ENTRY(vi->irqline), dp0);
+
+	/* Read the window register to flush the writes down to HW  */
+	dp0 = readl(isp->addr+IOSAPIC_REG_WINDOW);
+
+	iosapic_write(isp->addr, IOSAPIC_IRDT_ENTRY_HI(vi->irqline), dp1);
+
+	/* Read the window register to flush the writes down to HW  */
+	dp1 = readl(isp->addr+IOSAPIC_REG_WINDOW);
+}
+
+/*
+** set_irt prepares the data (dp0, dp1) according to the vector_info
+** and target cpu (id_eid).  dp0/dp1 are then used to program I/O SAPIC
+** IRdT for the given "vector" (aka IRQ line).
+*/
+static void
+iosapic_set_irt_data( struct vector_info *vi, u32 *dp0, u32 *dp1)
+{
+	u32 mode = 0;
+	struct irt_entry *p = vi->irte;
+
+	if ((p->polarity_trigger & IRT_PO_MASK) == IRT_ACTIVE_LO)
+		mode |= IOSAPIC_IRDT_PO_LOW;
+
+	if (((p->polarity_trigger >> IRT_EL_SHIFT) & IRT_EL_MASK) == IRT_LEVEL_TRIG)
+		mode |= IOSAPIC_IRDT_LEVEL_TRIG;
+
+	/*
+	** IA64 REVISIT
+	** PA doesn't support EXTINT or LPRIO bits.
+	*/
+
+	*dp0 = mode | (u32) vi->txn_data;
+
+	/*
+	** Extracting id_eid isn't a real clean way of getting it.
+	** But the encoding is the same for both PA and IA64 platforms.
+	*/
+	if (is_pdc_pat()) {
+		/*
+		** PAT PDC just hands it to us "right".
+		** txn_addr comes from cpu_data[x].txn_addr.
+		*/
+		*dp1 = (u32) (vi->txn_addr);
+	} else {
+		/* 
+		** eg if base_addr == 0xfffa0000),
+		**    we want to get 0xa0ff0000.
+		**
+		** eid	0x0ff00000 -> 0x00ff0000
+		** id	0x000ff000 -> 0xff000000
+		*/
+		*dp1 = (((u32)vi->txn_addr & 0x0ff00000) >> 4) |
+			(((u32)vi->txn_addr & 0x000ff000) << 12);
+	}
+	DBG_IRT("iosapic_set_irt_data(): 0x%x 0x%x\n", *dp0, *dp1);
+}
+
+
+static struct vector_info *iosapic_get_vector(unsigned int irq)
+{
+	return irq_desc[irq].handler_data;
+}
+
+static void iosapic_disable_irq(unsigned int irq)
+{
+	unsigned long flags;
+	struct vector_info *vi = iosapic_get_vector(irq);
+	u32 d0, d1;
+
+	spin_lock_irqsave(&iosapic_lock, flags);
+	iosapic_rd_irt_entry(vi, &d0, &d1);
+	d0 |= IOSAPIC_IRDT_ENABLE;
+	iosapic_wr_irt_entry(vi, d0, d1);
+	spin_unlock_irqrestore(&iosapic_lock, flags);
+}
+
+static void iosapic_enable_irq(unsigned int irq)
+{
+	struct vector_info *vi = iosapic_get_vector(irq);
+	u32 d0, d1;
+
+	/* data is initialized by fixup_irq */
+	WARN_ON(vi->txn_irq  == 0);
+
+	iosapic_set_irt_data(vi, &d0, &d1);
+	iosapic_wr_irt_entry(vi, d0, d1);
+
+#ifdef DEBUG_IOSAPIC_IRT
+{
+	u32 *t = (u32 *) ((ulong) vi->eoi_addr & ~0xffUL);
+	printk("iosapic_enable_irq(): regs %p", vi->eoi_addr);
+	for ( ; t < vi->eoi_addr; t++)
+		printk(" %x", readl(t));
+	printk("\n");
+}
+
+printk("iosapic_enable_irq(): sel ");
+{
+	struct iosapic_info *isp = vi->iosapic;
+
+	for (d0=0x10; d0<0x1e; d0++) {
+		d1 = iosapic_read(isp->addr, d0);
+		printk(" %x", d1);
+	}
+}
+printk("\n");
+#endif
+
+	/*
+	 * Issuing I/O SAPIC an EOI causes an interrupt IFF IRQ line is
+	 * asserted.  IRQ generally should not be asserted when a driver
+	 * enables their IRQ. It can lead to "interesting" race conditions
+	 * in the driver initialization sequence.
+	 */
+	DBG(KERN_DEBUG "enable_irq(%d): eoi(%p, 0x%x)\n", irq,
+			vi->eoi_addr, vi->eoi_data);
+	iosapic_eoi(vi->eoi_addr, vi->eoi_data);
+}
+
+/*
+ * PARISC only supports PCI devices below I/O SAPIC.
+ * PCI only supports level triggered in order to share IRQ lines.
+ * ergo I/O SAPIC must always issue EOI on parisc.
+ *
+ * i386/ia64 support ISA devices and have to deal with
+ * edge-triggered interrupts too.
+ */
+static void iosapic_end_irq(unsigned int irq)
+{
+	struct vector_info *vi = iosapic_get_vector(irq);
+	DBG(KERN_DEBUG "end_irq(%d): eoi(%p, 0x%x)\n", irq,
+			vi->eoi_addr, vi->eoi_data);
+	iosapic_eoi(vi->eoi_addr, vi->eoi_data);
+}
+
+static unsigned int iosapic_startup_irq(unsigned int irq)
+{
+	iosapic_enable_irq(irq);
+	return 0;
+}
+
+static struct hw_interrupt_type iosapic_interrupt_type = {
+	.typename =	"IO-SAPIC-level",
+	.startup =	iosapic_startup_irq,
+	.shutdown =	iosapic_disable_irq,
+	.enable =	iosapic_enable_irq,
+	.disable =	iosapic_disable_irq,
+	.ack =		no_ack_irq,
+	.end =		iosapic_end_irq,
+//	.set_affinity =	iosapic_set_affinity_irq,
+};
+
+int iosapic_fixup_irq(void *isi_obj, struct pci_dev *pcidev)
+{
+	struct iosapic_info *isi = isi_obj;
+	struct irt_entry *irte = NULL;  /* only used if PAT PDC */
+	struct vector_info *vi;
+	int isi_line;	/* line used by device */
+
+	if (!isi) {
+		printk(KERN_WARNING MODULE_NAME ": hpa not registered for %s\n",
+			pci_name(pcidev));
+		return -1;
+	}
+
+#ifdef CONFIG_SUPERIO
+	/*
+	 * HACK ALERT! (non-compliant PCI device support)
+	 *
+	 * All SuckyIO interrupts are routed through the PIC's on function 1.
+	 * But SuckyIO OHCI USB controller gets an IRT entry anyway because
+	 * it advertises INT D for INT_PIN.  Use that IRT entry to get the
+	 * SuckyIO interrupt routing for PICs on function 1 (*BLEECCHH*).
+	 */
+	if (is_superio_device(pcidev)) {
+		/* We must call superio_fixup_irq() to register the pdev */
+		pcidev->irq = superio_fixup_irq(pcidev);
+
+		/* Don't return if need to program the IOSAPIC's IRT... */
+		if (PCI_FUNC(pcidev->devfn) != SUPERIO_USB_FN)
+			return pcidev->irq;
+	}
+#endif /* CONFIG_SUPERIO */
+
+	/* lookup IRT entry for isi/slot/pin set */
+	irte = iosapic_xlate_pin(isi, pcidev);
+	if (!irte) {
+		printk("iosapic: no IRTE for %s (IRQ not connected?)\n",
+				pci_name(pcidev));
+		return -1;
+	}
+	DBG_IRT("iosapic_fixup_irq(): irte %p %x %x %x %x %x %x %x %x\n",
+		irte,
+		irte->entry_type,
+		irte->entry_length,
+		irte->polarity_trigger,
+		irte->src_bus_irq_devno,
+		irte->src_bus_id,
+		irte->src_seg_id,
+		irte->dest_iosapic_intin,
+		(u32) irte->dest_iosapic_addr);
+	isi_line = irte->dest_iosapic_intin;
+
+	/* get vector info for this input line */
+	vi = isi->isi_vector + isi_line;
+	DBG_IRT("iosapic_fixup_irq:  line %d vi 0x%p\n", isi_line, vi);
+
+	/* If this IRQ line has already been setup, skip it */
+	if (vi->irte)
+		goto out;
+
+	vi->irte = irte;
+
+	/*
+	 * Allocate processor IRQ
+	 *
+	 * XXX/FIXME The txn_alloc_irq() code and related code should be
+	 * moved to enable_irq(). That way we only allocate processor IRQ
+	 * bits for devices that actually have drivers claiming them.
+	 * Right now we assign an IRQ to every PCI device present,
+	 * regardless of whether it's used or not.
+	 */
+	vi->txn_irq = txn_alloc_irq(8);
+
+	if (vi->txn_irq < 0)
+		panic("I/O sapic: couldn't get TXN IRQ\n");
+
+	/* enable_irq() will use txn_* to program IRdT */
+	vi->txn_addr = txn_alloc_addr(vi->txn_irq);
+	vi->txn_data = txn_alloc_data(vi->txn_irq);
+
+	vi->eoi_addr = isi->addr + IOSAPIC_REG_EOI;
+	vi->eoi_data = cpu_to_le32(vi->txn_data);
+
+	cpu_claim_irq(vi->txn_irq, &iosapic_interrupt_type, vi);
+
+ out:
+	pcidev->irq = vi->txn_irq;
+
+	DBG_IRT("iosapic_fixup_irq() %d:%d %x %x line %d irq %d\n",
+		PCI_SLOT(pcidev->devfn), PCI_FUNC(pcidev->devfn),
+		pcidev->vendor, pcidev->device, isi_line, pcidev->irq);
+
+	return pcidev->irq;
+}
+
+
+/*
+** squirrel away the I/O Sapic Version
+*/
+static unsigned int
+iosapic_rd_version(struct iosapic_info *isi)
+{
+	return iosapic_read(isi->addr, IOSAPIC_REG_VERSION);
+}
+
+
+/*
+** iosapic_register() is called by "drivers" with an integrated I/O SAPIC.
+** Caller must be certain they have an I/O SAPIC and know its MMIO address.
+**
+**	o allocate iosapic_info and add it to the list
+**	o read iosapic version and squirrel that away
+**	o read size of IRdT.
+**	o allocate and initialize isi_vector[]
+**	o allocate irq region
+*/
+void *iosapic_register(unsigned long hpa)
+{
+	struct iosapic_info *isi = NULL;
+	struct irt_entry *irte = irt_cell;
+	struct vector_info *vip;
+	int cnt;	/* track how many entries we've looked at */
+
+	/*
+	 * Astro based platforms can only support PCI OLARD if they implement
+	 * PAT PDC.  Legacy PDC omits LBAs with no PCI devices from the IRT.
+	 * Search the IRT and ignore iosapic's which aren't in the IRT.
+	 */
+	for (cnt=0; cnt < irt_num_entry; cnt++, irte++) {
+		WARN_ON(IRT_IOSAPIC_TYPE != irte->entry_type);
+		if (COMPARE_IRTE_ADDR(irte, hpa))
+			break;
+	}
+
+	if (cnt >= irt_num_entry) {
+		DBG("iosapic_register() ignoring 0x%lx (NOT FOUND)\n", hpa);
+		return NULL;
+	}
+
+	isi = (struct iosapic_info *)kmalloc(sizeof(struct iosapic_info), GFP_KERNEL);
+	if (!isi) {
+		BUG();
+		return NULL;
+	}
+
+	memset(isi, 0, sizeof(struct iosapic_info));
+
+	isi->addr = ioremap(hpa, 4096);
+	isi->isi_hpa = hpa;
+	isi->isi_version = iosapic_rd_version(isi);
+	isi->isi_num_vectors = IOSAPIC_IRDT_MAX_ENTRY(isi->isi_version) + 1;
+
+	vip = isi->isi_vector = (struct vector_info *)
+		kmalloc(sizeof(struct vector_info) * isi->isi_num_vectors, GFP_KERNEL);
+	if (vip == NULL) {
+		kfree(isi);
+		return NULL;
+	}
+
+	memset(vip, 0, sizeof(struct vector_info) * isi->isi_num_vectors);
+
+	for (cnt=0; cnt < isi->isi_num_vectors; cnt++, vip++) {
+		vip->irqline = (unsigned char) cnt;
+		vip->iosapic = isi;
+	}
+	return isi;
+}
+
+
+#ifdef DEBUG_IOSAPIC
+
+static void
+iosapic_prt_irt(void *irt, long num_entry)
+{
+	unsigned int i, *irp = (unsigned int *) irt;
+
+
+	printk(KERN_DEBUG MODULE_NAME ": Interrupt Routing Table (%lx entries)\n", num_entry);
+
+	for (i=0; i<num_entry; i++, irp += 4) {
+		printk(KERN_DEBUG "%p : %2d %.8x %.8x %.8x %.8x\n",
+					irp, i, irp[0], irp[1], irp[2], irp[3]);
+	}
+}
+
+
+static void
+iosapic_prt_vi(struct vector_info *vi)
+{
+	printk(KERN_DEBUG MODULE_NAME ": vector_info[%d] is at %p\n", vi->irqline, vi);
+	printk(KERN_DEBUG "\t\tstatus:	 %.4x\n", vi->status);
+	printk(KERN_DEBUG "\t\ttxn_irq:  %d\n",  vi->txn_irq);
+	printk(KERN_DEBUG "\t\ttxn_addr: %lx\n", vi->txn_addr);
+	printk(KERN_DEBUG "\t\ttxn_data: %lx\n", vi->txn_data);
+	printk(KERN_DEBUG "\t\teoi_addr: %p\n",  vi->eoi_addr);
+	printk(KERN_DEBUG "\t\teoi_data: %x\n",  vi->eoi_data);
+}
+
+
+static void
+iosapic_prt_isi(struct iosapic_info *isi)
+{
+	printk(KERN_DEBUG MODULE_NAME ": io_sapic_info at %p\n", isi);
+	printk(KERN_DEBUG "\t\tisi_hpa:       %lx\n", isi->isi_hpa);
+	printk(KERN_DEBUG "\t\tisi_status:    %x\n", isi->isi_status);
+	printk(KERN_DEBUG "\t\tisi_version:   %x\n", isi->isi_version);
+	printk(KERN_DEBUG "\t\tisi_vector:    %p\n", isi->isi_vector);
+}
+#endif /* DEBUG_IOSAPIC */
diff --git a/drivers/parisc/iosapic_private.h b/drivers/parisc/iosapic_private.h
new file mode 100644
index 0000000..41e7ec2
--- /dev/null
+++ b/drivers/parisc/iosapic_private.h
@@ -0,0 +1,188 @@
+/* 
+ *    Private structs/constants for PARISC IOSAPIC support
+ *
+ *    Copyright (C) 2000 Hewlett Packard (Grant Grundler)
+ *    Copyright (C) 2000,2003 Grant Grundler (grundler at parisc-linux.org)
+ *    Copyright (C) 2002 Matthew Wilcox (willy at parisc-linux.org)
+ *
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program 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 General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+** This file is private to iosapic driver.
+** If stuff needs to be used by another driver, move it to a common file.
+**
+** WARNING: fields most data structures here are ordered to make sure
+**          they pack nicely for 64-bit compilation. (ie sizeof(long) == 8)
+*/
+
+
+/*
+** Interrupt Routing Stuff
+** -----------------------
+** The interrupt routing table consists of entries derived from
+** MP Specification Draft 1.5. There is one interrupt routing 
+** table per cell.  N- and L-class consist of a single cell.
+*/
+struct irt_entry {
+
+	/* Entry Type 139 identifies an I/O SAPIC interrupt entry */
+	u8 entry_type;
+
+	/* Entry Length 16 indicates entry is 16 bytes long */
+	u8 entry_length;
+
+	/* 
+	** Interrupt Type of 0 indicates a vectored interrupt, 
+	** all other values are reserved 
+	*/
+	u8 interrupt_type;
+
+	/* 
+	** PO and EL
+	** Polarity of SAPIC I/O input signals: 
+	**    00 = Reserved 
+	**    01 = Active high 
+	**    10 = Reserved 
+	**    11 = Active low 
+	** Trigger mode of SAPIC I/O input signals: 
+	**    00 = Reserved 
+	**    01 = Edge-triggered 
+	**    10 = Reserved 
+	**    11 = Level-triggered
+	*/
+	u8 polarity_trigger;
+
+	/* 
+	** IRQ and DEVNO
+	** irq identifies PCI interrupt signal where
+	**    0x0 corresponds to INT_A#, 
+	**    0x1 corresponds to INT_B#, 
+	**    0x2 corresponds to INT_C# 
+	**    0x3 corresponds to INT_D# 
+	** PCI device number where interrupt originates 
+	*/
+	u8 src_bus_irq_devno;
+
+	/* Source Bus ID identifies the bus where interrupt signal comes from */
+	u8 src_bus_id;
+
+	/* 
+	** Segment ID is unique across a protection domain and
+	** identifies a segment of PCI buses (reserved in 
+	** MP Specification Draft 1.5) 
+	*/
+	u8 src_seg_id;
+
+	/* 
+	** Destination I/O SAPIC INTIN# identifies the INTIN n pin 
+	** to which the signal is connected 
+	*/
+	u8 dest_iosapic_intin;
+
+	/* 
+	** Destination I/O SAPIC Address identifies the I/O SAPIC 
+	** to which the signal is connected 
+	*/
+	u64 dest_iosapic_addr;
+};
+
+#define IRT_IOSAPIC_TYPE   139
+#define IRT_IOSAPIC_LENGTH 16
+
+#define IRT_VECTORED_INTR  0
+
+#define IRT_PO_MASK        0x3
+#define IRT_ACTIVE_HI      1
+#define IRT_ACTIVE_LO      3
+
+#define IRT_EL_MASK        0x3
+#define IRT_EL_SHIFT       2
+#define IRT_EDGE_TRIG      1
+#define IRT_LEVEL_TRIG     3
+
+#define IRT_IRQ_MASK       0x3
+#define IRT_DEV_MASK       0x1f
+#define IRT_DEV_SHIFT      2
+
+#define IRT_IRQ_DEVNO_MASK	((IRT_DEV_MASK << IRT_DEV_SHIFT) | IRT_IRQ_MASK)
+
+#ifdef SUPPORT_MULTI_CELL
+struct iosapic_irt {
+        struct iosapic_irt *irt_next;  /* next routing table */
+        struct irt_entry *irt_base;             /* intr routing table address */
+        size_t  irte_count;            /* number of entries in the table */
+        size_t  irte_size;             /* size (bytes) of each entry */
+};
+#endif
+
+struct vector_info {
+	struct iosapic_info *iosapic;	/* I/O SAPIC this vector is on */
+	struct irt_entry *irte;		/* IRT entry */
+	u32	*eoi_addr;		/* precalculate EOI reg address */
+	u32	eoi_data;		/* IA64: ?       PA: swapped txn_data */
+	int	txn_irq;		/* virtual IRQ number for processor */
+	ulong	txn_addr;		/* IA64: id_eid  PA: partial HPA */
+	u32	txn_data;		/* CPU interrupt bit */
+	u8	status;			/* status/flags */
+	u8	irqline;		/* INTINn(IRQ) */
+};
+
+
+struct iosapic_info {
+	struct iosapic_info *	isi_next;	/* list of I/O SAPIC */
+	void __iomem *		addr;		/* remapped address */
+	unsigned long		isi_hpa;	/* physical base address */
+	struct vector_info *	isi_vector;	/* IRdT (IRQ line) array */
+	int			isi_num_vectors; /* size of IRdT array */
+	int			isi_status;	/* status/flags */
+	unsigned int		isi_version;	/* DEBUG: data fr version reg */
+};
+
+
+
+#ifdef __IA64__
+/*
+** PA risc does NOT have any local sapics. IA64 does.
+** PIB (Processor Interrupt Block) is handled by Astro or Dew (Stretch CEC).
+**
+** PA: Get id_eid from IRT and hardcode PIB to 0xfeeNNNN0
+**     Emulate the data on PAT platforms.
+*/
+struct local_sapic_info {
+	struct local_sapic_info *lsi_next;      /* point to next CPU info */
+	int                     *lsi_cpu_id;    /* point to logical CPU id */
+	unsigned long           *lsi_id_eid;    /* point to IA-64 CPU id */
+	int                     *lsi_status;    /* point to CPU status   */
+	void                    *lsi_private;   /* point to special info */
+};
+
+/*
+** "root" data structure which ties everything together.
+** Should always be able to start with sapic_root and locate
+** the desired information.
+*/
+struct sapic_info {
+	struct sapic_info	*si_next;	/* info is per cell */
+	int                     si_cellid;      /* cell id */
+	unsigned int            si_status;       /* status  */
+	char                    *si_pib_base;   /* intr blk base address */
+	local_sapic_info_t      *si_local_info;
+	io_sapic_info_t         *si_io_info;
+	extint_info_t           *si_extint_info;/* External Intr info      */
+};
+#endif
+
diff --git a/drivers/parisc/lasi.c b/drivers/parisc/lasi.c
new file mode 100644
index 0000000..7318550
--- /dev/null
+++ b/drivers/parisc/lasi.c
@@ -0,0 +1,240 @@
+/*
+ *	LASI Device Driver
+ *
+ *	(c) Copyright 1999 Red Hat Software
+ *	Portions (c) Copyright 1999 The Puffin Group Inc.
+ *	Portions (c) Copyright 1999 Hewlett-Packard
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *	by Alan Cox <alan@redhat.com> and 
+ * 	   Alex deVries <alex@onefishtwo.ca>
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/led.h>
+
+#include "gsc.h"
+
+
+#define LASI_VER	0xC008	/* LASI Version */
+
+#define LASI_IO_CONF	0x7FFFE	/* LASI primary configuration register */
+#define LASI_IO_CONF2	0x7FFFF	/* LASI secondary configuration register */
+
+static void lasi_choose_irq(struct parisc_device *dev, void *ctrl)
+{
+	int irq;
+
+	switch (dev->id.sversion) {
+		case 0x74:	irq =  7; break; /* Centronics */
+		case 0x7B:	irq = 13; break; /* Audio */
+		case 0x81:	irq = 14; break; /* Lasi itself */
+		case 0x82:	irq =  9; break; /* SCSI */
+		case 0x83:	irq = 20; break; /* Floppy */
+		case 0x84:	irq = 26; break; /* PS/2 Keyboard */
+		case 0x87:	irq = 18; break; /* ISDN */
+		case 0x8A:	irq =  8; break; /* LAN */
+		case 0x8C:	irq =  5; break; /* RS232 */
+		case 0x8D:	irq = (dev->hw_path == 13) ? 16 : 17; break;
+						 /* Telephone */
+		default: 	return;		 /* unknown */
+	}
+
+	gsc_asic_assign_irq(ctrl, irq, &dev->irq);
+}
+
+static void __init
+lasi_init_irq(struct gsc_asic *this_lasi)
+{
+	unsigned long lasi_base = this_lasi->hpa;
+
+	/* Stop LASI barking for a bit */
+	gsc_writel(0x00000000, lasi_base+OFFSET_IMR);
+
+	/* clear pending interrupts */
+	gsc_readl(lasi_base+OFFSET_IRR);
+
+	/* We're not really convinced we want to reset the onboard
+         * devices. Firmware does it for us...
+	 */
+
+	/* Resets */
+	/* gsc_writel(0xFFFFFFFF, lasi_base+0x2000);*/	/* Parallel */
+	if(pdc_add_valid(lasi_base+0x4004) == PDC_OK)
+		gsc_writel(0xFFFFFFFF, lasi_base+0x4004);	/* Audio */
+	/* gsc_writel(0xFFFFFFFF, lasi_base+0x5000);*/	/* Serial */ 
+	/* gsc_writel(0xFFFFFFFF, lasi_base+0x6000);*/	/* SCSI */
+	gsc_writel(0xFFFFFFFF, lasi_base+0x7000);	/* LAN */
+	gsc_writel(0xFFFFFFFF, lasi_base+0x8000);	/* Keyboard */
+	gsc_writel(0xFFFFFFFF, lasi_base+0xA000);	/* FDC */
+	
+	/* Ok we hit it on the head with a hammer, our Dog is now
+	** comatose and muzzled.  Devices will now unmask LASI
+	** interrupts as they are registered as irq's in the LASI range.
+	*/
+	/* XXX: I thought it was `awks that got `it on the `ead with an
+	 * `ammer.  -- willy
+	 */
+}
+
+
+/*
+   ** lasi_led_init()
+   ** 
+   ** lasi_led_init() initializes the LED controller on the LASI.
+   **
+   ** Since Mirage and Electra machines use a different LED
+   ** address register, we need to check for these machines 
+   ** explicitly.
+ */
+
+#ifndef CONFIG_CHASSIS_LCD_LED
+
+#define lasi_led_init(x)	/* nothing */
+
+#else
+
+void __init lasi_led_init(unsigned long lasi_hpa)
+{
+	unsigned long datareg;
+
+	switch (CPU_HVERSION) {
+	/* Gecko machines have only one single LED, which can be permanently 
+	   turned on by writing a zero into the power control register. */ 
+	case 0x600:		/* Gecko (712/60) */
+	case 0x601:		/* Gecko (712/80) */
+	case 0x602:		/* Gecko (712/100) */
+	case 0x603:		/* Anole 64 (743/64) */
+	case 0x604:		/* Anole 100 (743/100) */
+	case 0x605:		/* Gecko (712/120) */
+		datareg = lasi_hpa + 0x0000C000;
+		gsc_writeb(0, datareg);
+		return; /* no need to register the LED interrupt-function */  
+
+	/* Mirage and Electra machines need special offsets */
+	case 0x60A:		/* Mirage Jr (715/64) */
+	case 0x60B:		/* Mirage 100 */
+	case 0x60C:		/* Mirage 100+ */
+	case 0x60D:		/* Electra 100 */
+	case 0x60E:		/* Electra 120 */
+		datareg = lasi_hpa - 0x00020000;
+		break;
+
+	default:
+		datareg = lasi_hpa + 0x0000C000;
+		break;
+	}
+
+	register_led_driver(DISPLAY_MODEL_LASI, LED_CMD_REG_NONE, datareg);
+}
+#endif
+
+/*
+ * lasi_power_off
+ *
+ * Function for lasi to turn off the power.  This is accomplished by setting a
+ * 1 to PWR_ON_L in the Power Control Register
+ * 
+ */
+
+static unsigned long lasi_power_off_hpa;
+
+static void lasi_power_off(void)
+{
+	unsigned long datareg;
+
+	/* calculate addr of the Power Control Register */
+	datareg = lasi_power_off_hpa + 0x0000C000;
+
+	/* Power down the machine */
+	gsc_writel(0x02, datareg);
+}
+
+int __init
+lasi_init_chip(struct parisc_device *dev)
+{
+	struct gsc_asic *lasi;
+	struct gsc_irq gsc_irq;
+	int ret;
+
+	lasi = kmalloc(sizeof(*lasi), GFP_KERNEL);
+	if (!lasi)
+		return -ENOMEM;
+
+	lasi->name = "Lasi";
+	lasi->hpa = dev->hpa;
+
+	/* Check the 4-bit (yes, only 4) version register */
+	lasi->version = gsc_readl(lasi->hpa + LASI_VER) & 0xf;
+	printk(KERN_INFO "%s version %d at 0x%lx found.\n",
+		lasi->name, lasi->version, lasi->hpa);
+
+	/* initialize the chassis LEDs really early */ 
+	lasi_led_init(lasi->hpa);
+
+	/* Stop LASI barking for a bit */
+	lasi_init_irq(lasi);
+
+	/* the IRQ lasi should use */
+	dev->irq = gsc_alloc_irq(&gsc_irq);
+	if (dev->irq < 0) {
+		printk(KERN_ERR "%s(): cannot get GSC irq\n",
+				__FUNCTION__);
+		kfree(lasi);
+		return -EBUSY;
+	}
+
+	lasi->eim = ((u32) gsc_irq.txn_addr) | gsc_irq.txn_data;
+
+	ret = request_irq(gsc_irq.irq, gsc_asic_intr, 0, "lasi", lasi);
+	if (ret < 0) {
+		kfree(lasi);
+		return ret;
+	}
+
+	/* enable IRQ's for devices below LASI */
+	gsc_writel(lasi->eim, lasi->hpa + OFFSET_IAR);
+
+	/* Done init'ing, register this driver */
+	ret = gsc_common_setup(dev, lasi);
+	if (ret) {
+		kfree(lasi);
+		return ret;
+	}    
+
+	gsc_fixup_irqs(dev, lasi, lasi_choose_irq);
+
+	/* initialize the power off function */
+	/* FIXME: Record the LASI HPA for the power off function.  This should
+	 * ensure that only the first LASI (the one controlling the power off)
+	 * should set the HPA here */
+	lasi_power_off_hpa = lasi->hpa;
+	pm_power_off = lasi_power_off;
+	
+	return ret;
+}
+
+static struct parisc_device_id lasi_tbl[] = {
+	{ HPHW_BA, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00081 },
+	{ 0, }
+};
+
+struct parisc_driver lasi_driver = {
+	.name =		"Lasi",
+	.id_table =	lasi_tbl,
+	.probe =	lasi_init_chip,
+};
diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c
new file mode 100644
index 0000000..dc83880
--- /dev/null
+++ b/drivers/parisc/lba_pci.c
@@ -0,0 +1,1649 @@
+/*
+**
+**  PCI Lower Bus Adapter (LBA) manager
+**
+**	(c) Copyright 1999,2000 Grant Grundler
+**	(c) Copyright 1999,2000 Hewlett-Packard Company
+**
+**	This program is free software; you can redistribute it and/or modify
+**	it under the terms of the GNU General Public License as published by
+**      the Free Software Foundation; either version 2 of the License, or
+**      (at your option) any later version.
+**
+**
+** This module primarily provides access to PCI bus (config/IOport
+** spaces) on platforms with an SBA/LBA chipset. A/B/C/J/L/N-class
+** with 4 digit model numbers - eg C3000 (and A400...sigh).
+**
+** LBA driver isn't as simple as the Dino driver because:
+**   (a) this chip has substantial bug fixes between revisions
+**       (Only one Dino bug has a software workaround :^(  )
+**   (b) has more options which we don't (yet) support (DMA hints, OLARD)
+**   (c) IRQ support lives in the I/O SAPIC driver (not with PCI driver)
+**   (d) play nicely with both PAT and "Legacy" PA-RISC firmware (PDC).
+**       (dino only deals with "Legacy" PDC)
+**
+** LBA driver passes the I/O SAPIC HPA to the I/O SAPIC driver.
+** (I/O SAPIC is integratd in the LBA chip).
+**
+** FIXME: Add support to SBA and LBA drivers for DMA hint sets
+** FIXME: Add support for PCI card hot-plug (OLARD).
+*/
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>		/* for __init and __devinit */
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+
+#include <asm/byteorder.h>
+#include <asm/pdc.h>
+#include <asm/pdcpat.h>
+#include <asm/page.h>
+#include <asm/system.h>
+
+#include <asm/hardware.h>	/* for register_parisc_driver() stuff */
+#include <asm/parisc-device.h>
+#include <asm/iosapic.h>	/* for iosapic_register() */
+#include <asm/io.h>		/* read/write stuff */
+
+#undef DEBUG_LBA	/* general stuff */
+#undef DEBUG_LBA_PORT	/* debug I/O Port access */
+#undef DEBUG_LBA_CFG	/* debug Config Space Access (ie PCI Bus walk) */
+#undef DEBUG_LBA_PAT	/* debug PCI Resource Mgt code - PDC PAT only */
+
+#undef FBB_SUPPORT	/* Fast Back-Back xfers - NOT READY YET */
+
+
+#ifdef DEBUG_LBA
+#define DBG(x...)	printk(x)
+#else
+#define DBG(x...)
+#endif
+
+#ifdef DEBUG_LBA_PORT
+#define DBG_PORT(x...)	printk(x)
+#else
+#define DBG_PORT(x...)
+#endif
+
+#ifdef DEBUG_LBA_CFG
+#define DBG_CFG(x...)	printk(x)
+#else
+#define DBG_CFG(x...)
+#endif
+
+#ifdef DEBUG_LBA_PAT
+#define DBG_PAT(x...)	printk(x)
+#else
+#define DBG_PAT(x...)
+#endif
+
+
+/*
+** Config accessor functions only pass in the 8-bit bus number and not
+** the 8-bit "PCI Segment" number. Each LBA will be assigned a PCI bus
+** number based on what firmware wrote into the scratch register.
+**
+** The "secondary" bus number is set to this before calling
+** pci_register_ops(). If any PPB's are present, the scan will
+** discover them and update the "secondary" and "subordinate"
+** fields in the pci_bus structure.
+**
+** Changes in the configuration *may* result in a different
+** bus number for each LBA depending on what firmware does.
+*/
+
+#define MODULE_NAME "LBA"
+
+#define LBA_FUNC_ID	0x0000	/* function id */
+#define LBA_FCLASS	0x0008	/* function class, bist, header, rev... */
+#define LBA_CAPABLE	0x0030	/* capabilities register */
+
+#define LBA_PCI_CFG_ADDR	0x0040	/* poke CFG address here */
+#define LBA_PCI_CFG_DATA	0x0048	/* read or write data here */
+
+#define LBA_PMC_MTLT	0x0050	/* Firmware sets this - read only. */
+#define LBA_FW_SCRATCH	0x0058	/* Firmware writes the PCI bus number here. */
+#define LBA_ERROR_ADDR	0x0070	/* On error, address gets logged here */
+
+#define LBA_ARB_MASK	0x0080	/* bit 0 enable arbitration. PAT/PDC enables */
+#define LBA_ARB_PRI	0x0088	/* firmware sets this. */
+#define LBA_ARB_MODE	0x0090	/* firmware sets this. */
+#define LBA_ARB_MTLT	0x0098	/* firmware sets this. */
+
+#define LBA_MOD_ID	0x0100	/* Module ID. PDC_PAT_CELL reports 4 */
+
+#define LBA_STAT_CTL	0x0108	/* Status & Control */
+#define   LBA_BUS_RESET		0x01	/*  Deassert PCI Bus Reset Signal */
+#define   CLEAR_ERRLOG		0x10	/*  "Clear Error Log" cmd */
+#define   CLEAR_ERRLOG_ENABLE	0x20	/*  "Clear Error Log" Enable */
+#define   HF_ENABLE	0x40	/*    enable HF mode (default is -1 mode) */
+
+#define LBA_LMMIO_BASE	0x0200	/* < 4GB I/O address range */
+#define LBA_LMMIO_MASK	0x0208
+
+#define LBA_GMMIO_BASE	0x0210	/* > 4GB I/O address range */
+#define LBA_GMMIO_MASK	0x0218
+
+#define LBA_WLMMIO_BASE	0x0220	/* All < 4GB ranges under the same *SBA* */
+#define LBA_WLMMIO_MASK	0x0228
+
+#define LBA_WGMMIO_BASE	0x0230	/* All > 4GB ranges under the same *SBA* */
+#define LBA_WGMMIO_MASK	0x0238
+
+#define LBA_IOS_BASE	0x0240	/* I/O port space for this LBA */
+#define LBA_IOS_MASK	0x0248
+
+#define LBA_ELMMIO_BASE	0x0250	/* Extra LMMIO range */
+#define LBA_ELMMIO_MASK	0x0258
+
+#define LBA_EIOS_BASE	0x0260	/* Extra I/O port space */
+#define LBA_EIOS_MASK	0x0268
+
+#define LBA_GLOBAL_MASK	0x0270	/* Mercury only: Global Address Mask */
+#define LBA_DMA_CTL	0x0278	/* firmware sets this */
+
+#define LBA_IBASE	0x0300	/* SBA DMA support */
+#define LBA_IMASK	0x0308
+
+/* FIXME: ignore DMA Hint stuff until we can measure performance */
+#define LBA_HINT_CFG	0x0310
+#define LBA_HINT_BASE	0x0380	/* 14 registers at every 8 bytes. */
+
+#define LBA_BUS_MODE	0x0620
+
+/* ERROR regs are needed for config cycle kluges */
+#define LBA_ERROR_CONFIG 0x0680
+#define     LBA_SMART_MODE 0x20
+#define LBA_ERROR_STATUS 0x0688
+#define LBA_ROPE_CTL     0x06A0
+
+#define LBA_IOSAPIC_BASE	0x800 /* Offset of IRQ logic */
+
+/* non-postable I/O port space, densely packed */
+#define LBA_PORT_BASE	(PCI_F_EXTEND | 0xfee00000UL)
+static void __iomem *astro_iop_base;
+
+#define ELROY_HVERS	0x782
+#define MERCURY_HVERS	0x783
+#define QUICKSILVER_HVERS	0x784
+
+static inline int IS_ELROY(struct parisc_device *d)
+{
+	return (d->id.hversion == ELROY_HVERS);
+}
+
+static inline int IS_MERCURY(struct parisc_device *d)
+{
+	return (d->id.hversion == MERCURY_HVERS);
+}
+
+static inline int IS_QUICKSILVER(struct parisc_device *d)
+{
+	return (d->id.hversion == QUICKSILVER_HVERS);
+}
+
+
+/*
+** lba_device: Per instance Elroy data structure
+*/
+struct lba_device {
+	struct pci_hba_data hba;
+
+	spinlock_t	lba_lock;
+	void		*iosapic_obj;
+
+#ifdef CONFIG_64BIT
+	void __iomem *	iop_base;    /* PA_VIEW - for IO port accessor funcs */
+#endif
+
+	int		flags;       /* state/functionality enabled */
+	int		hw_rev;      /* HW revision of chip */
+};
+
+
+static u32 lba_t32;
+
+/* lba flags */
+#define LBA_FLAG_SKIP_PROBE	0x10
+
+#define LBA_SKIP_PROBE(d) ((d)->flags & LBA_FLAG_SKIP_PROBE)
+
+
+/* Looks nice and keeps the compiler happy */
+#define LBA_DEV(d) ((struct lba_device *) (d))
+
+
+/*
+** Only allow 8 subsidiary busses per LBA
+** Problem is the PCI bus numbering is globally shared.
+*/
+#define LBA_MAX_NUM_BUSES 8
+
+/************************************
+ * LBA register read and write support
+ *
+ * BE WARNED: register writes are posted.
+ *  (ie follow writes which must reach HW with a read)
+ */
+#define READ_U8(addr)  __raw_readb(addr)
+#define READ_U16(addr) __raw_readw(addr)
+#define READ_U32(addr) __raw_readl(addr)
+#define WRITE_U8(value, addr)  __raw_writeb(value, addr)
+#define WRITE_U16(value, addr) __raw_writew(value, addr)
+#define WRITE_U32(value, addr) __raw_writel(value, addr)
+
+#define READ_REG8(addr)  readb(addr)
+#define READ_REG16(addr) readw(addr)
+#define READ_REG32(addr) readl(addr)
+#define READ_REG64(addr) readq(addr)
+#define WRITE_REG8(value, addr)  writeb(value, addr)
+#define WRITE_REG16(value, addr) writew(value, addr)
+#define WRITE_REG32(value, addr) writel(value, addr)
+
+
+#define LBA_CFG_TOK(bus,dfn) ((u32) ((bus)<<16 | (dfn)<<8))
+#define LBA_CFG_BUS(tok)  ((u8) ((tok)>>16))
+#define LBA_CFG_DEV(tok)  ((u8) ((tok)>>11) & 0x1f)
+#define LBA_CFG_FUNC(tok) ((u8) ((tok)>>8 ) & 0x7)
+
+
+/*
+** Extract LBA (Rope) number from HPA
+** REVISIT: 16 ropes for Stretch/Ike?
+*/
+#define ROPES_PER_IOC	8
+#define LBA_NUM(x)    ((((unsigned long) x) >> 13) & (ROPES_PER_IOC-1))
+
+
+static void
+lba_dump_res(struct resource *r, int d)
+{
+	int i;
+
+	if (NULL == r)
+		return;
+
+	printk(KERN_DEBUG "(%p)", r->parent);
+	for (i = d; i ; --i) printk(" ");
+	printk(KERN_DEBUG "%p [%lx,%lx]/%lx\n", r, r->start, r->end, r->flags);
+	lba_dump_res(r->child, d+2);
+	lba_dump_res(r->sibling, d);
+}
+
+
+/*
+** LBA rev 2.0, 2.1, 2.2, and 3.0 bus walks require a complex
+** workaround for cfg cycles:
+**	-- preserve  LBA state
+**	-- prevent any DMA from occurring
+**	-- turn on smart mode
+**	-- probe with config writes before doing config reads
+**	-- check ERROR_STATUS
+**	-- clear ERROR_STATUS
+**	-- restore LBA state
+**
+** The workaround is only used for device discovery.
+*/
+
+static int lba_device_present(u8 bus, u8 dfn, struct lba_device *d)
+{
+	u8 first_bus = d->hba.hba_bus->secondary;
+	u8 last_sub_bus = d->hba.hba_bus->subordinate;
+
+	if ((bus < first_bus) ||
+	    (bus > last_sub_bus) ||
+	    ((bus - first_bus) >= LBA_MAX_NUM_BUSES)) {
+		return 0;
+	}
+
+	return 1;
+}
+
+
+
+#define LBA_CFG_SETUP(d, tok) {				\
+    /* Save contents of error config register.  */			\
+    error_config = READ_REG32(d->hba.base_addr + LBA_ERROR_CONFIG);		\
+\
+    /* Save contents of status control register.  */			\
+    status_control = READ_REG32(d->hba.base_addr + LBA_STAT_CTL);		\
+\
+    /* For LBA rev 2.0, 2.1, 2.2, and 3.0, we must disable DMA		\
+    ** arbitration for full bus walks.					\
+    */									\
+	/* Save contents of arb mask register. */			\
+	arb_mask = READ_REG32(d->hba.base_addr + LBA_ARB_MASK);		\
+\
+	/*								\
+	 * Turn off all device arbitration bits (i.e. everything	\
+	 * except arbitration enable bit).				\
+	 */								\
+	WRITE_REG32(0x1, d->hba.base_addr + LBA_ARB_MASK);		\
+\
+    /*									\
+     * Set the smart mode bit so that master aborts don't cause		\
+     * LBA to go into PCI fatal mode (required).			\
+     */									\
+    WRITE_REG32(error_config | LBA_SMART_MODE, d->hba.base_addr + LBA_ERROR_CONFIG);	\
+}
+
+
+#define LBA_CFG_PROBE(d, tok) {				\
+    /*									\
+     * Setup Vendor ID write and read back the address register		\
+     * to make sure that LBA is the bus master.				\
+     */									\
+    WRITE_REG32(tok | PCI_VENDOR_ID, (d)->hba.base_addr + LBA_PCI_CFG_ADDR);\
+    /*									\
+     * Read address register to ensure that LBA is the bus master,	\
+     * which implies that DMA traffic has stopped when DMA arb is off.	\
+     */									\
+    lba_t32 = READ_REG32((d)->hba.base_addr + LBA_PCI_CFG_ADDR);	\
+    /*									\
+     * Generate a cfg write cycle (will have no affect on		\
+     * Vendor ID register since read-only).				\
+     */									\
+    WRITE_REG32(~0, (d)->hba.base_addr + LBA_PCI_CFG_DATA);		\
+    /*									\
+     * Make sure write has completed before proceeding further,		\
+     * i.e. before setting clear enable.				\
+     */									\
+    lba_t32 = READ_REG32((d)->hba.base_addr + LBA_PCI_CFG_ADDR);	\
+}
+
+
+/*
+ * HPREVISIT:
+ *   -- Can't tell if config cycle got the error.
+ *
+ *		OV bit is broken until rev 4.0, so can't use OV bit and
+ *		LBA_ERROR_LOG_ADDR to tell if error belongs to config cycle.
+ *
+ *		As of rev 4.0, no longer need the error check.
+ *
+ *   -- Even if we could tell, we still want to return -1
+ *	for **ANY** error (not just master abort).
+ *
+ *   -- Only clear non-fatal errors (we don't want to bring
+ *	LBA out of pci-fatal mode).
+ *
+ *		Actually, there is still a race in which
+ *		we could be clearing a fatal error.  We will
+ *		live with this during our initial bus walk
+ *		until rev 4.0 (no driver activity during
+ *		initial bus walk).  The initial bus walk
+ *		has race conditions concerning the use of
+ *		smart mode as well.
+ */
+
+#define LBA_MASTER_ABORT_ERROR 0xc
+#define LBA_FATAL_ERROR 0x10
+
+#define LBA_CFG_MASTER_ABORT_CHECK(d, base, tok, error) {		\
+    u32 error_status = 0;						\
+    /*									\
+     * Set clear enable (CE) bit. Unset by HW when new			\
+     * errors are logged -- LBA HW ERS section 14.3.3).		\
+     */									\
+    WRITE_REG32(status_control | CLEAR_ERRLOG_ENABLE, base + LBA_STAT_CTL); \
+    error_status = READ_REG32(base + LBA_ERROR_STATUS);		\
+    if ((error_status & 0x1f) != 0) {					\
+	/*								\
+	 * Fail the config read request.				\
+	 */								\
+	error = 1;							\
+	if ((error_status & LBA_FATAL_ERROR) == 0) {			\
+	    /*								\
+	     * Clear error status (if fatal bit not set) by setting	\
+	     * clear error log bit (CL).				\
+	     */								\
+	    WRITE_REG32(status_control | CLEAR_ERRLOG, base + LBA_STAT_CTL); \
+	}								\
+    }									\
+}
+
+#define LBA_CFG_TR4_ADDR_SETUP(d, addr)					\
+	WRITE_REG32(((addr) & ~3), (d)->hba.base_addr + LBA_PCI_CFG_ADDR);
+
+#define LBA_CFG_ADDR_SETUP(d, addr) {					\
+    WRITE_REG32(((addr) & ~3), (d)->hba.base_addr + LBA_PCI_CFG_ADDR);	\
+    /*									\
+     * Read address register to ensure that LBA is the bus master,	\
+     * which implies that DMA traffic has stopped when DMA arb is off.	\
+     */									\
+    lba_t32 = READ_REG32((d)->hba.base_addr + LBA_PCI_CFG_ADDR);	\
+}
+
+
+#define LBA_CFG_RESTORE(d, base) {					\
+    /*									\
+     * Restore status control register (turn off clear enable).		\
+     */									\
+    WRITE_REG32(status_control, base + LBA_STAT_CTL);			\
+    /*									\
+     * Restore error config register (turn off smart mode).		\
+     */									\
+    WRITE_REG32(error_config, base + LBA_ERROR_CONFIG);			\
+	/*								\
+	 * Restore arb mask register (reenables DMA arbitration).	\
+	 */								\
+	WRITE_REG32(arb_mask, base + LBA_ARB_MASK);			\
+}
+
+
+
+static unsigned int
+lba_rd_cfg(struct lba_device *d, u32 tok, u8 reg, u32 size)
+{
+	u32 data = ~0U;
+	int error = 0;
+	u32 arb_mask = 0;	/* used by LBA_CFG_SETUP/RESTORE */
+	u32 error_config = 0;	/* used by LBA_CFG_SETUP/RESTORE */
+	u32 status_control = 0;	/* used by LBA_CFG_SETUP/RESTORE */
+
+	LBA_CFG_SETUP(d, tok);
+	LBA_CFG_PROBE(d, tok);
+	LBA_CFG_MASTER_ABORT_CHECK(d, d->hba.base_addr, tok, error);
+	if (!error) {
+		void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA;
+
+		LBA_CFG_ADDR_SETUP(d, tok | reg);
+		switch (size) {
+		case 1: data = (u32) READ_REG8(data_reg + (reg & 3)); break;
+		case 2: data = (u32) READ_REG16(data_reg+ (reg & 2)); break;
+		case 4: data = READ_REG32(data_reg); break;
+		}
+	}
+	LBA_CFG_RESTORE(d, d->hba.base_addr);
+	return(data);
+}
+
+
+static int elroy_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 *data)
+{
+	struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge));
+	u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary;
+	u32 tok = LBA_CFG_TOK(local_bus, devfn);
+	void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA;
+
+	if ((pos > 255) || (devfn > 255))
+		return -EINVAL;
+
+/* FIXME: B2K/C3600 workaround is always use old method... */
+	/* if (!LBA_SKIP_PROBE(d)) */ {
+		/* original - Generate config cycle on broken elroy
+		  with risk we will miss PCI bus errors. */
+		*data = lba_rd_cfg(d, tok, pos, size);
+		DBG_CFG("%s(%x+%2x) -> 0x%x (a)\n", __FUNCTION__, tok, pos, *data);
+		return 0;
+	}
+
+	if (LBA_SKIP_PROBE(d) && !lba_device_present(bus->secondary, devfn, d)) {
+		DBG_CFG("%s(%x+%2x) -> -1 (b)\n", __FUNCTION__, tok, pos);
+		/* either don't want to look or know device isn't present. */
+		*data = ~0U;
+		return(0);
+	}
+
+	/* Basic Algorithm
+	** Should only get here on fully working LBA rev.
+	** This is how simple the code should have been.
+	*/
+	LBA_CFG_ADDR_SETUP(d, tok | pos);
+	switch(size) {
+	case 1: *data = READ_REG8 (data_reg + (pos & 3)); break;
+	case 2: *data = READ_REG16(data_reg + (pos & 2)); break;
+	case 4: *data = READ_REG32(data_reg); break;
+	}
+	DBG_CFG("%s(%x+%2x) -> 0x%x (c)\n", __FUNCTION__, tok, pos, *data);
+	return 0;
+}
+
+
+static void
+lba_wr_cfg(struct lba_device *d, u32 tok, u8 reg, u32 data, u32 size)
+{
+	int error = 0;
+	u32 arb_mask = 0;
+	u32 error_config = 0;
+	u32 status_control = 0;
+	void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA;
+
+	LBA_CFG_SETUP(d, tok);
+	LBA_CFG_ADDR_SETUP(d, tok | reg);
+	switch (size) {
+	case 1: WRITE_REG8 (data, data_reg + (reg & 3)); break;
+	case 2: WRITE_REG16(data, data_reg + (reg & 2)); break;
+	case 4: WRITE_REG32(data, data_reg);             break;
+	}
+	LBA_CFG_MASTER_ABORT_CHECK(d, d->hba.base_addr, tok, error);
+	LBA_CFG_RESTORE(d, d->hba.base_addr);
+}
+
+
+/*
+ * LBA 4.0 config write code implements non-postable semantics
+ * by doing a read of CONFIG ADDR after the write.
+ */
+
+static int elroy_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 data)
+{
+	struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge));
+	u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary;
+	u32 tok = LBA_CFG_TOK(local_bus,devfn);
+
+	if ((pos > 255) || (devfn > 255))
+		return -EINVAL;
+
+	if (!LBA_SKIP_PROBE(d)) {
+		/* Original Workaround */
+		lba_wr_cfg(d, tok, pos, (u32) data, size);
+		DBG_CFG("%s(%x+%2x) = 0x%x (a)\n", __FUNCTION__, tok, pos,data);
+		return 0;
+	}
+
+	if (LBA_SKIP_PROBE(d) && (!lba_device_present(bus->secondary, devfn, d))) {
+		DBG_CFG("%s(%x+%2x) = 0x%x (b)\n", __FUNCTION__, tok, pos,data);
+		return 1; /* New Workaround */
+	}
+
+	DBG_CFG("%s(%x+%2x) = 0x%x (c)\n", __FUNCTION__, tok, pos, data);
+
+	/* Basic Algorithm */
+	LBA_CFG_ADDR_SETUP(d, tok | pos);
+	switch(size) {
+	case 1: WRITE_REG8 (data, d->hba.base_addr + LBA_PCI_CFG_DATA + (pos & 3));
+		   break;
+	case 2: WRITE_REG16(data, d->hba.base_addr + LBA_PCI_CFG_DATA + (pos & 2));
+		   break;
+	case 4: WRITE_REG32(data, d->hba.base_addr + LBA_PCI_CFG_DATA);
+		   break;
+	}
+	/* flush posted write */
+	lba_t32 = READ_REG32(d->hba.base_addr + LBA_PCI_CFG_ADDR);
+	return 0;
+}
+
+
+static struct pci_ops elroy_cfg_ops = {
+	.read =		elroy_cfg_read,
+	.write =	elroy_cfg_write,
+};
+
+/*
+ * The mercury_cfg_ops are slightly misnamed; they're also used for Elroy
+ * TR4.0 as no additional bugs were found in this areea between Elroy and
+ * Mercury
+ */
+
+static int mercury_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 *data)
+{
+	struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge));
+	u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary;
+	u32 tok = LBA_CFG_TOK(local_bus, devfn);
+	void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA;
+
+	if ((pos > 255) || (devfn > 255))
+		return -EINVAL;
+
+	LBA_CFG_TR4_ADDR_SETUP(d, tok | pos);
+	switch(size) {
+	case 1:
+		*data = READ_REG8(data_reg + (pos & 3));
+		break;
+	case 2:
+		*data = READ_REG16(data_reg + (pos & 2));
+		break;
+	case 4:
+		*data = READ_REG32(data_reg);             break;
+		break;
+	}
+
+	DBG_CFG("mercury_cfg_read(%x+%2x) -> 0x%x\n", tok, pos, *data);
+	return 0;
+}
+
+/*
+ * LBA 4.0 config write code implements non-postable semantics
+ * by doing a read of CONFIG ADDR after the write.
+ */
+
+static int mercury_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 data)
+{
+	struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge));
+	void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA;
+	u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary;
+	u32 tok = LBA_CFG_TOK(local_bus,devfn);
+
+	if ((pos > 255) || (devfn > 255))
+		return -EINVAL;
+
+	DBG_CFG("%s(%x+%2x) <- 0x%x (c)\n", __FUNCTION__, tok, pos, data);
+
+	LBA_CFG_TR4_ADDR_SETUP(d, tok | pos);
+	switch(size) {
+	case 1:
+		WRITE_REG8 (data, data_reg + (pos & 3));
+		break;
+	case 2:
+		WRITE_REG16(data, data_reg + (pos & 2));
+		break;
+	case 4:
+		WRITE_REG32(data, data_reg);
+		break;
+	}
+
+	/* flush posted write */
+	lba_t32 = READ_U32(d->hba.base_addr + LBA_PCI_CFG_ADDR);
+	return 0;
+}
+
+static struct pci_ops mercury_cfg_ops = {
+	.read =		mercury_cfg_read,
+	.write =	mercury_cfg_write,
+};
+
+
+static void
+lba_bios_init(void)
+{
+	DBG(MODULE_NAME ": lba_bios_init\n");
+}
+
+
+#ifdef CONFIG_64BIT
+
+/*
+** Determine if a device is already configured.
+** If so, reserve it resources.
+**
+** Read PCI cfg command register and see if I/O or MMIO is enabled.
+** PAT has to enable the devices it's using.
+**
+** Note: resources are fixed up before we try to claim them.
+*/
+static void
+lba_claim_dev_resources(struct pci_dev *dev)
+{
+	u16 cmd;
+	int i, srch_flags;
+
+	(void) pci_read_config_word(dev, PCI_COMMAND, &cmd);
+
+	srch_flags  = (cmd & PCI_COMMAND_IO) ? IORESOURCE_IO : 0;
+	if (cmd & PCI_COMMAND_MEMORY)
+		srch_flags |= IORESOURCE_MEM;
+
+	if (!srch_flags)
+		return;
+
+	for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
+		if (dev->resource[i].flags & srch_flags) {
+			pci_claim_resource(dev, i);
+			DBG("   claimed %s %d [%lx,%lx]/%lx\n",
+				pci_name(dev), i,
+				dev->resource[i].start,
+				dev->resource[i].end,
+				dev->resource[i].flags
+				);
+		}
+	}
+}
+#else
+#define lba_claim_dev_resources(dev)
+#endif
+
+
+/*
+** The algorithm is generic code.
+** But it needs to access local data structures to get the IRQ base.
+** Could make this a "pci_fixup_irq(bus, region)" but not sure
+** it's worth it.
+**
+** Called by do_pci_scan_bus() immediately after each PCI bus is walked.
+** Resources aren't allocated until recursive buswalk below HBA is completed.
+*/
+static void
+lba_fixup_bus(struct pci_bus *bus)
+{
+	struct list_head *ln;
+#ifdef FBB_SUPPORT
+	u16 status;
+#endif
+	struct lba_device *ldev = LBA_DEV(parisc_walk_tree(bus->bridge));
+	int lba_portbase = HBA_PORT_BASE(ldev->hba.hba_num);
+
+	DBG("lba_fixup_bus(0x%p) bus %d platform_data 0x%p\n",
+		bus, bus->secondary, bus->bridge->platform_data);
+
+	/*
+	** Properly Setup MMIO resources for this bus.
+	** pci_alloc_primary_bus() mangles this.
+	*/
+	if (bus->self) {
+		/* PCI-PCI Bridge */
+		pci_read_bridge_bases(bus);
+	} else {
+		/* Host-PCI Bridge */
+		int err, i;
+
+		DBG("lba_fixup_bus() %s [%lx/%lx]/%lx\n",
+			ldev->hba.io_space.name,
+			ldev->hba.io_space.start, ldev->hba.io_space.end,
+			ldev->hba.io_space.flags);
+		DBG("lba_fixup_bus() %s [%lx/%lx]/%lx\n",
+			ldev->hba.lmmio_space.name,
+			ldev->hba.lmmio_space.start, ldev->hba.lmmio_space.end,
+			ldev->hba.lmmio_space.flags);
+
+		err = request_resource(&ioport_resource, &(ldev->hba.io_space));
+		if (err < 0) {
+			lba_dump_res(&ioport_resource, 2);
+			BUG();
+		}
+
+		if (ldev->hba.elmmio_space.start) {
+			err = request_resource(&iomem_resource,
+					&(ldev->hba.elmmio_space));
+			if (err < 0) {
+
+				printk("FAILED: lba_fixup_bus() request for "
+						"elmmio_space [%lx/%lx]\n",
+						ldev->hba.elmmio_space.start,
+						ldev->hba.elmmio_space.end);
+
+				/* lba_dump_res(&iomem_resource, 2); */
+				/* BUG(); */
+			}
+		}
+
+		err = request_resource(&iomem_resource, &(ldev->hba.lmmio_space));
+		if (err < 0) {
+			/*   FIXME  overlaps with elmmio will fail here.
+			 *   Need to prune (or disable) the distributed range.
+			 *
+			 *   BEWARE: conflicts with this lmmio range may be
+			 *   elmmio range which is pointing down another rope.
+			 */
+
+			printk("FAILED: lba_fixup_bus() request for "
+					"lmmio_space [%lx/%lx]\n",
+					ldev->hba.lmmio_space.start,
+					ldev->hba.lmmio_space.end);
+			/* lba_dump_res(&iomem_resource, 2); */
+		}
+
+#ifdef CONFIG_64BIT
+		/* GMMIO is  distributed range. Every LBA/Rope gets part it. */
+		if (ldev->hba.gmmio_space.flags) {
+			err = request_resource(&iomem_resource, &(ldev->hba.gmmio_space));
+			if (err < 0) {
+				printk("FAILED: lba_fixup_bus() request for "
+					"gmmio_space [%lx/%lx]\n",
+					ldev->hba.gmmio_space.start,
+					ldev->hba.gmmio_space.end);
+				lba_dump_res(&iomem_resource, 2);
+				BUG();
+			}
+		}
+#endif
+
+		/* advertize Host bridge resources to PCI bus */
+		bus->resource[0] = &(ldev->hba.io_space);
+		bus->resource[1] = &(ldev->hba.lmmio_space);
+		i=2;
+		if (ldev->hba.elmmio_space.start)
+			bus->resource[i++] = &(ldev->hba.elmmio_space);
+		if (ldev->hba.gmmio_space.start)
+			bus->resource[i++] = &(ldev->hba.gmmio_space);
+			
+	}
+
+	list_for_each(ln, &bus->devices) {
+		int i;
+		struct pci_dev *dev = pci_dev_b(ln);
+
+		DBG("lba_fixup_bus() %s\n", pci_name(dev));
+
+		/* Virtualize Device/Bridge Resources. */
+		for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) {
+			struct resource *res = &dev->resource[i];
+
+			/* If resource not allocated - skip it */
+			if (!res->start)
+				continue;
+
+			if (res->flags & IORESOURCE_IO) {
+				DBG("lba_fixup_bus() I/O Ports [%lx/%lx] -> ",
+					res->start, res->end);
+				res->start |= lba_portbase;
+				res->end   |= lba_portbase;
+				DBG("[%lx/%lx]\n", res->start, res->end);
+			} else if (res->flags & IORESOURCE_MEM) {
+				/*
+				** Convert PCI (IO_VIEW) addresses to
+				** processor (PA_VIEW) addresses
+				 */
+				DBG("lba_fixup_bus() MMIO [%lx/%lx] -> ",
+					res->start, res->end);
+				res->start = PCI_HOST_ADDR(HBA_DATA(ldev), res->start);
+				res->end   = PCI_HOST_ADDR(HBA_DATA(ldev), res->end);
+				DBG("[%lx/%lx]\n", res->start, res->end);
+			} else {
+				DBG("lba_fixup_bus() WTF? 0x%lx [%lx/%lx] XXX",
+					res->flags, res->start, res->end);
+			}
+		}
+
+#ifdef FBB_SUPPORT
+		/*
+		** If one device does not support FBB transfers,
+		** No one on the bus can be allowed to use them.
+		*/
+		(void) pci_read_config_word(dev, PCI_STATUS, &status);
+		bus->bridge_ctl &= ~(status & PCI_STATUS_FAST_BACK);
+#endif
+
+		if (is_pdc_pat()) {
+			/* Claim resources for PDC's devices */
+			lba_claim_dev_resources(dev);
+		}
+
+                /*
+		** P2PB's have no IRQs. ignore them.
+		*/
+		if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)
+			continue;
+
+		/* Adjust INTERRUPT_LINE for this dev */
+		iosapic_fixup_irq(ldev->iosapic_obj, dev);
+	}
+
+#ifdef FBB_SUPPORT
+/* FIXME/REVISIT - finish figuring out to set FBB on both
+** pci_setup_bridge() clobbers PCI_BRIDGE_CONTROL.
+** Can't fixup here anyway....garr...
+*/
+	if (fbb_enable) {
+		if (bus->self) {
+			u8 control;
+			/* enable on PPB */
+			(void) pci_read_config_byte(bus->self, PCI_BRIDGE_CONTROL, &control);
+			(void) pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, control | PCI_STATUS_FAST_BACK);
+
+		} else {
+			/* enable on LBA */
+		}
+		fbb_enable = PCI_COMMAND_FAST_BACK;
+	}
+
+	/* Lastly enable FBB/PERR/SERR on all devices too */
+	list_for_each(ln, &bus->devices) {
+		(void) pci_read_config_word(dev, PCI_COMMAND, &status);
+		status |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR | fbb_enable;
+		(void) pci_write_config_word(dev, PCI_COMMAND, status);
+	}
+#endif
+}
+
+
+struct pci_bios_ops lba_bios_ops = {
+	.init =		lba_bios_init,
+	.fixup_bus =	lba_fixup_bus,
+};
+
+
+
+
+/*******************************************************
+**
+** LBA Sprockets "I/O Port" Space Accessor Functions
+**
+** This set of accessor functions is intended for use with
+** "legacy firmware" (ie Sprockets on Allegro/Forte boxes).
+**
+** Many PCI devices don't require use of I/O port space (eg Tulip,
+** NCR720) since they export the same registers to both MMIO and
+** I/O port space. In general I/O port space is slower than
+** MMIO since drivers are designed so PIO writes can be posted.
+**
+********************************************************/
+
+#define LBA_PORT_IN(size, mask) \
+static u##size lba_astro_in##size (struct pci_hba_data *d, u16 addr) \
+{ \
+	u##size t; \
+	t = READ_REG##size(astro_iop_base + addr); \
+	DBG_PORT(" 0x%x\n", t); \
+	return (t); \
+}
+
+LBA_PORT_IN( 8, 3)
+LBA_PORT_IN(16, 2)
+LBA_PORT_IN(32, 0)
+
+
+
+/*
+** BUG X4107:  Ordering broken - DMA RD return can bypass PIO WR
+**
+** Fixed in Elroy 2.2. The READ_U32(..., LBA_FUNC_ID) below is
+** guarantee non-postable completion semantics - not avoid X4107.
+** The READ_U32 only guarantees the write data gets to elroy but
+** out to the PCI bus. We can't read stuff from I/O port space
+** since we don't know what has side-effects. Attempting to read
+** from configuration space would be suicidal given the number of
+** bugs in that elroy functionality.
+**
+**      Description:
+**          DMA read results can improperly pass PIO writes (X4107).  The
+**          result of this bug is that if a processor modifies a location in
+**          memory after having issued PIO writes, the PIO writes are not
+**          guaranteed to be completed before a PCI device is allowed to see
+**          the modified data in a DMA read.
+**
+**          Note that IKE bug X3719 in TR1 IKEs will result in the same
+**          symptom.
+**
+**      Workaround:
+**          The workaround for this bug is to always follow a PIO write with
+**          a PIO read to the same bus before starting DMA on that PCI bus.
+**
+*/
+#define LBA_PORT_OUT(size, mask) \
+static void lba_astro_out##size (struct pci_hba_data *d, u16 addr, u##size val) \
+{ \
+	DBG_PORT("%s(0x%p, 0x%x, 0x%x)\n", __FUNCTION__, d, addr, val); \
+	WRITE_REG##size(val, astro_iop_base + addr); \
+	if (LBA_DEV(d)->hw_rev < 3) \
+		lba_t32 = READ_U32(d->base_addr + LBA_FUNC_ID); \
+}
+
+LBA_PORT_OUT( 8, 3)
+LBA_PORT_OUT(16, 2)
+LBA_PORT_OUT(32, 0)
+
+
+static struct pci_port_ops lba_astro_port_ops = {
+	.inb =	lba_astro_in8,
+	.inw =	lba_astro_in16,
+	.inl =	lba_astro_in32,
+	.outb =	lba_astro_out8,
+	.outw =	lba_astro_out16,
+	.outl =	lba_astro_out32
+};
+
+
+#ifdef CONFIG_64BIT
+#define PIOP_TO_GMMIO(lba, addr) \
+	((lba)->iop_base + (((addr)&0xFFFC)<<10) + ((addr)&3))
+
+/*******************************************************
+**
+** LBA PAT "I/O Port" Space Accessor Functions
+**
+** This set of accessor functions is intended for use with
+** "PAT PDC" firmware (ie Prelude/Rhapsody/Piranha boxes).
+**
+** This uses the PIOP space located in the first 64MB of GMMIO.
+** Each rope gets a full 64*KB* (ie 4 bytes per page) this way.
+** bits 1:0 stay the same.  bits 15:2 become 25:12.
+** Then add the base and we can generate an I/O Port cycle.
+********************************************************/
+#undef LBA_PORT_IN
+#define LBA_PORT_IN(size, mask) \
+static u##size lba_pat_in##size (struct pci_hba_data *l, u16 addr) \
+{ \
+	u##size t; \
+	DBG_PORT("%s(0x%p, 0x%x) ->", __FUNCTION__, l, addr); \
+	t = READ_REG##size(PIOP_TO_GMMIO(LBA_DEV(l), addr)); \
+	DBG_PORT(" 0x%x\n", t); \
+	return (t); \
+}
+
+LBA_PORT_IN( 8, 3)
+LBA_PORT_IN(16, 2)
+LBA_PORT_IN(32, 0)
+
+
+#undef LBA_PORT_OUT
+#define LBA_PORT_OUT(size, mask) \
+static void lba_pat_out##size (struct pci_hba_data *l, u16 addr, u##size val) \
+{ \
+	void *where = (void *) PIOP_TO_GMMIO(LBA_DEV(l), addr); \
+	DBG_PORT("%s(0x%p, 0x%x, 0x%x)\n", __FUNCTION__, l, addr, val); \
+	WRITE_REG##size(val, where); \
+	/* flush the I/O down to the elroy at least */ \
+	lba_t32 = READ_U32(l->base_addr + LBA_FUNC_ID); \
+}
+
+LBA_PORT_OUT( 8, 3)
+LBA_PORT_OUT(16, 2)
+LBA_PORT_OUT(32, 0)
+
+
+static struct pci_port_ops lba_pat_port_ops = {
+	.inb =	lba_pat_in8,
+	.inw =	lba_pat_in16,
+	.inl =	lba_pat_in32,
+	.outb =	lba_pat_out8,
+	.outw =	lba_pat_out16,
+	.outl =	lba_pat_out32
+};
+
+
+
+/*
+** make range information from PDC available to PCI subsystem.
+** We make the PDC call here in order to get the PCI bus range
+** numbers. The rest will get forwarded in pcibios_fixup_bus().
+** We don't have a struct pci_bus assigned to us yet.
+*/
+static void
+lba_pat_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev)
+{
+	unsigned long bytecnt;
+	pdc_pat_cell_mod_maddr_block_t pa_pdc_cell;	/* PA_VIEW */
+	pdc_pat_cell_mod_maddr_block_t io_pdc_cell;	/* IO_VIEW */
+	long io_count;
+	long status;	/* PDC return status */
+	long pa_count;
+	int i;
+
+	/* return cell module (IO view) */
+	status = pdc_pat_cell_module(&bytecnt, pa_dev->pcell_loc, pa_dev->mod_index,
+				PA_VIEW, & pa_pdc_cell);
+	pa_count = pa_pdc_cell.mod[1];
+
+	status |= pdc_pat_cell_module(&bytecnt, pa_dev->pcell_loc, pa_dev->mod_index,
+				IO_VIEW, &io_pdc_cell);
+	io_count = io_pdc_cell.mod[1];
+
+	/* We've already done this once for device discovery...*/
+	if (status != PDC_OK) {
+		panic("pdc_pat_cell_module() call failed for LBA!\n");
+	}
+
+	if (PAT_GET_ENTITY(pa_pdc_cell.mod_info) != PAT_ENTITY_LBA) {
+		panic("pdc_pat_cell_module() entity returned != PAT_ENTITY_LBA!\n");
+	}
+
+	/*
+	** Inspect the resources PAT tells us about
+	*/
+	for (i = 0; i < pa_count; i++) {
+		struct {
+			unsigned long type;
+			unsigned long start;
+			unsigned long end;	/* aka finish */
+		} *p, *io;
+		struct resource *r;
+
+		p = (void *) &(pa_pdc_cell.mod[2+i*3]);
+		io = (void *) &(io_pdc_cell.mod[2+i*3]);
+
+		/* Convert the PAT range data to PCI "struct resource" */
+		switch(p->type & 0xff) {
+		case PAT_PBNUM:
+			lba_dev->hba.bus_num.start = p->start;
+			lba_dev->hba.bus_num.end   = p->end;
+			break;
+
+		case PAT_LMMIO:
+			/* used to fix up pre-initialized MEM BARs */
+			if (!lba_dev->hba.lmmio_space.start) {
+				sprintf(lba_dev->hba.lmmio_name,
+						"PCI%02lx LMMIO",
+						lba_dev->hba.bus_num.start);
+				lba_dev->hba.lmmio_space_offset = p->start -
+					io->start;
+				r = &lba_dev->hba.lmmio_space;
+				r->name = lba_dev->hba.lmmio_name;
+			} else if (!lba_dev->hba.elmmio_space.start) {
+				sprintf(lba_dev->hba.elmmio_name,
+						"PCI%02lx ELMMIO",
+						lba_dev->hba.bus_num.start);
+				r = &lba_dev->hba.elmmio_space;
+				r->name = lba_dev->hba.elmmio_name;
+			} else {
+				printk(KERN_WARNING MODULE_NAME
+					" only supports 2 LMMIO resources!\n");
+				break;
+			}
+
+			r->start  = p->start;
+			r->end    = p->end;
+			r->flags  = IORESOURCE_MEM;
+			r->parent = r->sibling = r->child = NULL;
+			break;
+
+		case PAT_GMMIO:
+			/* MMIO space > 4GB phys addr; for 64-bit BAR */
+			sprintf(lba_dev->hba.gmmio_name, "PCI%02lx GMMIO",
+					lba_dev->hba.bus_num.start);
+			r = &lba_dev->hba.gmmio_space;
+			r->name  = lba_dev->hba.gmmio_name;
+			r->start  = p->start;
+			r->end    = p->end;
+			r->flags  = IORESOURCE_MEM;
+			r->parent = r->sibling = r->child = NULL;
+			break;
+
+		case PAT_NPIOP:
+			printk(KERN_WARNING MODULE_NAME
+				" range[%d] : ignoring NPIOP (0x%lx)\n",
+				i, p->start);
+			break;
+
+		case PAT_PIOP:
+			/*
+			** Postable I/O port space is per PCI host adapter.
+			** base of 64MB PIOP region
+			*/
+			lba_dev->iop_base = ioremap(p->start, 64 * 1024 * 1024);
+
+			sprintf(lba_dev->hba.io_name, "PCI%02lx Ports",
+					lba_dev->hba.bus_num.start);
+			r = &lba_dev->hba.io_space;
+			r->name  = lba_dev->hba.io_name;
+			r->start  = HBA_PORT_BASE(lba_dev->hba.hba_num);
+			r->end    = r->start + HBA_PORT_SPACE_SIZE - 1;
+			r->flags  = IORESOURCE_IO;
+			r->parent = r->sibling = r->child = NULL;
+			break;
+
+		default:
+			printk(KERN_WARNING MODULE_NAME
+				" range[%d] : unknown pat range type (0x%lx)\n",
+				i, p->type & 0xff);
+			break;
+		}
+	}
+}
+#else
+/* keep compiler from complaining about missing declarations */
+#define lba_pat_port_ops lba_astro_port_ops
+#define lba_pat_resources(pa_dev, lba_dev)
+#endif	/* CONFIG_64BIT */
+
+
+extern void sba_distributed_lmmio(struct parisc_device *, struct resource *);
+extern void sba_directed_lmmio(struct parisc_device *, struct resource *);
+
+
+static void
+lba_legacy_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev)
+{
+	struct resource *r;
+	int lba_num;
+
+	lba_dev->hba.lmmio_space_offset = PCI_F_EXTEND;
+
+	/*
+	** With "legacy" firmware, the lowest byte of FW_SCRATCH
+	** represents bus->secondary and the second byte represents
+	** bus->subsidiary (i.e. highest PPB programmed by firmware).
+	** PCI bus walk *should* end up with the same result.
+	** FIXME: But we don't have sanity checks in PCI or LBA.
+	*/
+	lba_num = READ_REG32(lba_dev->hba.base_addr + LBA_FW_SCRATCH);
+	r = &(lba_dev->hba.bus_num);
+	r->name = "LBA PCI Busses";
+	r->start = lba_num & 0xff;
+	r->end = (lba_num>>8) & 0xff;
+
+	/* Set up local PCI Bus resources - we don't need them for
+	** Legacy boxes but it's nice to see in /proc/iomem.
+	*/
+	r = &(lba_dev->hba.lmmio_space);
+	sprintf(lba_dev->hba.lmmio_name, "PCI%02lx LMMIO",
+					lba_dev->hba.bus_num.start);
+	r->name  = lba_dev->hba.lmmio_name;
+
+#if 1
+	/* We want the CPU -> IO routing of addresses.
+	 * The SBA BASE/MASK registers control CPU -> IO routing.
+	 * Ask SBA what is routed to this rope/LBA.
+	 */
+	sba_distributed_lmmio(pa_dev, r);
+#else
+	/*
+	 * The LBA BASE/MASK registers control IO -> System routing.
+	 *
+	 * The following code works but doesn't get us what we want.
+	 * Well, only because firmware (v5.0) on C3000 doesn't program
+	 * the LBA BASE/MASE registers to be the exact inverse of 
+	 * the corresponding SBA registers. Other Astro/Pluto
+	 * based platform firmware may do it right.
+	 *
+	 * Should someone want to mess with MSI, they may need to
+	 * reprogram LBA BASE/MASK registers. Thus preserve the code
+	 * below until MSI is known to work on C3000/A500/N4000/RP3440.
+	 *
+	 * Using the code below, /proc/iomem shows:
+	 * ...
+	 * f0000000-f0ffffff : PCI00 LMMIO
+	 *   f05d0000-f05d0000 : lcd_data
+	 *   f05d0008-f05d0008 : lcd_cmd
+	 * f1000000-f1ffffff : PCI01 LMMIO
+	 * f4000000-f4ffffff : PCI02 LMMIO
+	 *   f4000000-f4001fff : sym53c8xx
+	 *   f4002000-f4003fff : sym53c8xx
+	 *   f4004000-f40043ff : sym53c8xx
+	 *   f4005000-f40053ff : sym53c8xx
+	 *   f4007000-f4007fff : ohci_hcd
+	 *   f4008000-f40083ff : tulip
+	 * f6000000-f6ffffff : PCI03 LMMIO
+	 * f8000000-fbffffff : PCI00 ELMMIO
+	 *   fa100000-fa4fffff : stifb mmio
+	 *   fb000000-fb1fffff : stifb fb
+	 *
+	 * But everything listed under PCI02 actually lives under PCI00.
+	 * This is clearly wrong.
+	 *
+	 * Asking SBA how things are routed tells the correct story:
+	 * LMMIO_BASE/MASK/ROUTE f4000001 fc000000 00000000
+	 * DIR0_BASE/MASK/ROUTE fa000001 fe000000 00000006
+	 * DIR1_BASE/MASK/ROUTE f9000001 ff000000 00000004
+	 * DIR2_BASE/MASK/ROUTE f0000000 fc000000 00000000
+	 * DIR3_BASE/MASK/ROUTE f0000000 fc000000 00000000
+	 *
+	 * Which looks like this in /proc/iomem:
+	 * f4000000-f47fffff : PCI00 LMMIO
+	 *   f4000000-f4001fff : sym53c8xx
+	 *   ...[deteled core devices - same as above]...
+	 *   f4008000-f40083ff : tulip
+	 * f4800000-f4ffffff : PCI01 LMMIO
+	 * f6000000-f67fffff : PCI02 LMMIO
+	 * f7000000-f77fffff : PCI03 LMMIO
+	 * f9000000-f9ffffff : PCI02 ELMMIO
+	 * fa000000-fbffffff : PCI03 ELMMIO
+	 *   fa100000-fa4fffff : stifb mmio
+	 *   fb000000-fb1fffff : stifb fb
+	 *
+	 * ie all Built-in core are under now correctly under PCI00.
+	 * The "PCI02 ELMMIO" directed range is for:
+	 *  +-[02]---03.0  3Dfx Interactive, Inc. Voodoo 2
+	 *
+	 * All is well now.
+	 */
+	r->start = READ_REG32(lba_dev->hba.base_addr + LBA_LMMIO_BASE);
+	if (r->start & 1) {
+		unsigned long rsize;
+
+		r->flags = IORESOURCE_MEM;
+		/* mmio_mask also clears Enable bit */
+		r->start &= mmio_mask;
+		r->start = PCI_HOST_ADDR(HBA_DATA(lba_dev), r->start);
+		rsize = ~ READ_REG32(lba_dev->hba.base_addr + LBA_LMMIO_MASK);
+
+		/*
+		** Each rope only gets part of the distributed range.
+		** Adjust "window" for this rope.
+		*/
+		rsize /= ROPES_PER_IOC;
+		r->start += (rsize + 1) * LBA_NUM(pa_dev->hpa);
+		r->end = r->start + rsize;
+	} else {
+		r->end = r->start = 0;	/* Not enabled. */
+	}
+#endif
+
+	/*
+	** "Directed" ranges are used when the "distributed range" isn't
+	** sufficient for all devices below a given LBA.  Typically devices
+	** like graphics cards or X25 may need a directed range when the
+	** bus has multiple slots (ie multiple devices) or the device
+	** needs more than the typical 4 or 8MB a distributed range offers.
+	**
+	** The main reason for ignoring it now frigging complications.
+	** Directed ranges may overlap (and have precedence) over
+	** distributed ranges. Or a distributed range assigned to a unused
+	** rope may be used by a directed range on a different rope.
+	** Support for graphics devices may require fixing this
+	** since they may be assigned a directed range which overlaps
+	** an existing (but unused portion of) distributed range.
+	*/
+	r = &(lba_dev->hba.elmmio_space);
+	sprintf(lba_dev->hba.elmmio_name, "PCI%02lx ELMMIO",
+					lba_dev->hba.bus_num.start);
+	r->name  = lba_dev->hba.elmmio_name;
+
+#if 1
+	/* See comment which precedes call to sba_directed_lmmio() */
+	sba_directed_lmmio(pa_dev, r);
+#else
+	r->start = READ_REG32(lba_dev->hba.base_addr + LBA_ELMMIO_BASE);
+
+	if (r->start & 1) {
+		unsigned long rsize;
+		r->flags = IORESOURCE_MEM;
+		/* mmio_mask also clears Enable bit */
+		r->start &= mmio_mask;
+		r->start = PCI_HOST_ADDR(HBA_DATA(lba_dev), r->start);
+		rsize = READ_REG32(lba_dev->hba.base_addr + LBA_ELMMIO_MASK);
+		r->end = r->start + ~rsize;
+	}
+#endif
+
+	r = &(lba_dev->hba.io_space);
+	sprintf(lba_dev->hba.io_name, "PCI%02lx Ports",
+					lba_dev->hba.bus_num.start);
+	r->name  = lba_dev->hba.io_name;
+	r->flags = IORESOURCE_IO;
+	r->start = READ_REG32(lba_dev->hba.base_addr + LBA_IOS_BASE) & ~1L;
+	r->end   = r->start + (READ_REG32(lba_dev->hba.base_addr + LBA_IOS_MASK) ^ (HBA_PORT_SPACE_SIZE - 1));
+
+	/* Virtualize the I/O Port space ranges */
+	lba_num = HBA_PORT_BASE(lba_dev->hba.hba_num);
+	r->start |= lba_num;
+	r->end   |= lba_num;
+}
+
+
+/**************************************************************************
+**
+**   LBA initialization code (HW and SW)
+**
+**   o identify LBA chip itself
+**   o initialize LBA chip modes (HardFail)
+**   o FIXME: initialize DMA hints for reasonable defaults
+**   o enable configuration functions
+**   o call pci_register_ops() to discover devs (fixup/fixup_bus get invoked)
+**
+**************************************************************************/
+
+static int __init
+lba_hw_init(struct lba_device *d)
+{
+	u32 stat;
+	u32 bus_reset;	/* PDC_PAT_BUG */
+
+#if 0
+	printk(KERN_DEBUG "LBA %lx  STAT_CTL %Lx  ERROR_CFG %Lx  STATUS %Lx DMA_CTL %Lx\n",
+		d->hba.base_addr,
+		READ_REG64(d->hba.base_addr + LBA_STAT_CTL),
+		READ_REG64(d->hba.base_addr + LBA_ERROR_CONFIG),
+		READ_REG64(d->hba.base_addr + LBA_ERROR_STATUS),
+		READ_REG64(d->hba.base_addr + LBA_DMA_CTL) );
+	printk(KERN_DEBUG "	ARB mask %Lx  pri %Lx  mode %Lx  mtlt %Lx\n",
+		READ_REG64(d->hba.base_addr + LBA_ARB_MASK),
+		READ_REG64(d->hba.base_addr + LBA_ARB_PRI),
+		READ_REG64(d->hba.base_addr + LBA_ARB_MODE),
+		READ_REG64(d->hba.base_addr + LBA_ARB_MTLT) );
+	printk(KERN_DEBUG "	HINT cfg 0x%Lx\n",
+		READ_REG64(d->hba.base_addr + LBA_HINT_CFG));
+	printk(KERN_DEBUG "	HINT reg ");
+	{ int i;
+	for (i=LBA_HINT_BASE; i< (14*8 + LBA_HINT_BASE); i+=8)
+		printk(" %Lx", READ_REG64(d->hba.base_addr + i));
+	}
+	printk("\n");
+#endif	/* DEBUG_LBA_PAT */
+
+#ifdef CONFIG_64BIT
+/*
+ * FIXME add support for PDC_PAT_IO "Get slot status" - OLAR support
+ * Only N-Class and up can really make use of Get slot status.
+ * maybe L-class too but I've never played with it there.
+ */
+#endif
+
+	/* PDC_PAT_BUG: exhibited in rev 40.48  on L2000 */
+	bus_reset = READ_REG32(d->hba.base_addr + LBA_STAT_CTL + 4) & 1;
+	if (bus_reset) {
+		printk(KERN_DEBUG "NOTICE: PCI bus reset still asserted! (clearing)\n");
+	}
+
+	stat = READ_REG32(d->hba.base_addr + LBA_ERROR_CONFIG);
+	if (stat & LBA_SMART_MODE) {
+		printk(KERN_DEBUG "NOTICE: LBA in SMART mode! (cleared)\n");
+		stat &= ~LBA_SMART_MODE;
+		WRITE_REG32(stat, d->hba.base_addr + LBA_ERROR_CONFIG);
+	}
+
+	/* Set HF mode as the default (vs. -1 mode). */
+        stat = READ_REG32(d->hba.base_addr + LBA_STAT_CTL);
+	WRITE_REG32(stat | HF_ENABLE, d->hba.base_addr + LBA_STAT_CTL);
+
+	/*
+	** Writing a zero to STAT_CTL.rf (bit 0) will clear reset signal
+	** if it's not already set. If we just cleared the PCI Bus Reset
+	** signal, wait a bit for the PCI devices to recover and setup.
+	*/
+	if (bus_reset)
+		mdelay(pci_post_reset_delay);
+
+	if (0 == READ_REG32(d->hba.base_addr + LBA_ARB_MASK)) {
+		/*
+		** PDC_PAT_BUG: PDC rev 40.48 on L2000.
+		** B2000/C3600/J6000 also have this problem?
+		** 
+		** Elroys with hot pluggable slots don't get configured
+		** correctly if the slot is empty.  ARB_MASK is set to 0
+		** and we can't master transactions on the bus if it's
+		** not at least one. 0x3 enables elroy and first slot.
+		*/
+		printk(KERN_DEBUG "NOTICE: Enabling PCI Arbitration\n");
+		WRITE_REG32(0x3, d->hba.base_addr + LBA_ARB_MASK);
+	}
+
+	/*
+	** FIXME: Hint registers are programmed with default hint
+	** values by firmware. Hints should be sane even if we
+	** can't reprogram them the way drivers want.
+	*/
+	return 0;
+}
+
+
+
+/*
+** Determine if lba should claim this chip (return 0) or not (return 1).
+** If so, initialize the chip and tell other partners in crime they
+** have work to do.
+*/
+static int __init
+lba_driver_probe(struct parisc_device *dev)
+{
+	struct lba_device *lba_dev;
+	struct pci_bus *lba_bus;
+	struct pci_ops *cfg_ops;
+	u32 func_class;
+	void *tmp_obj;
+	char *version;
+	void __iomem *addr = ioremap(dev->hpa, 4096);
+
+	/* Read HW Rev First */
+	func_class = READ_REG32(addr + LBA_FCLASS);
+
+	if (IS_ELROY(dev)) {	
+		func_class &= 0xf;
+		switch (func_class) {
+		case 0:	version = "TR1.0"; break;
+		case 1:	version = "TR2.0"; break;
+		case 2:	version = "TR2.1"; break;
+		case 3:	version = "TR2.2"; break;
+		case 4:	version = "TR3.0"; break;
+		case 5:	version = "TR4.0"; break;
+		default: version = "TR4+";
+		}
+
+		printk(KERN_INFO "%s version %s (0x%x) found at 0x%lx\n",
+			MODULE_NAME, version, func_class & 0xf, dev->hpa);
+
+		if (func_class < 2) {
+			printk(KERN_WARNING "Can't support LBA older than "
+				"TR2.1 - continuing under adversity.\n");
+		}
+
+#if 0
+/* Elroy TR4.0 should work with simple algorithm.
+   But it doesn't.  Still missing something. *sigh*
+*/
+		if (func_class > 4) {
+			cfg_ops = &mercury_cfg_ops;
+		} else
+#endif
+		{
+			cfg_ops = &elroy_cfg_ops;
+		}
+
+	} else if (IS_MERCURY(dev) || IS_QUICKSILVER(dev)) {
+		func_class &= 0xff;
+		version = kmalloc(6, GFP_KERNEL);
+		sprintf(version,"TR%d.%d",(func_class >> 4),(func_class & 0xf));
+		/* We could use one printk for both Elroy and Mercury,
+                 * but for the mask for func_class.
+                 */ 
+		printk(KERN_INFO "%s version %s (0x%x) found at 0x%lx\n",
+			MODULE_NAME, version, func_class & 0xff, dev->hpa);
+		cfg_ops = &mercury_cfg_ops;
+	} else {
+		printk(KERN_ERR "Unknown LBA found at 0x%lx\n", dev->hpa);
+		return -ENODEV;
+	}
+
+	/*
+	** Tell I/O SAPIC driver we have a IRQ handler/region.
+	*/
+	tmp_obj = iosapic_register(dev->hpa + LBA_IOSAPIC_BASE);
+
+	/* NOTE: PCI devices (e.g. 103c:1005 graphics card) which don't
+	**	have an IRT entry will get NULL back from iosapic code.
+	*/
+	
+	lba_dev = kmalloc(sizeof(struct lba_device), GFP_KERNEL);
+	if (!lba_dev) {
+		printk(KERN_ERR "lba_init_chip - couldn't alloc lba_device\n");
+		return(1);
+	}
+
+	memset(lba_dev, 0, sizeof(struct lba_device));
+
+
+	/* ---------- First : initialize data we already have --------- */
+
+	lba_dev->hw_rev = func_class;
+	lba_dev->hba.base_addr = addr;
+	lba_dev->hba.dev = dev;
+	lba_dev->iosapic_obj = tmp_obj;  /* save interrupt handle */
+	lba_dev->hba.iommu = sba_get_iommu(dev);  /* get iommu data */
+
+	/* ------------ Second : initialize common stuff ---------- */
+	pci_bios = &lba_bios_ops;
+	pcibios_register_hba(HBA_DATA(lba_dev));
+	spin_lock_init(&lba_dev->lba_lock);
+
+	if (lba_hw_init(lba_dev))
+		return(1);
+
+	/* ---------- Third : setup I/O Port and MMIO resources  --------- */
+
+	if (is_pdc_pat()) {
+		/* PDC PAT firmware uses PIOP region of GMMIO space. */
+		pci_port = &lba_pat_port_ops;
+		/* Go ask PDC PAT what resources this LBA has */
+		lba_pat_resources(dev, lba_dev);
+	} else {
+		if (!astro_iop_base) {
+			/* Sprockets PDC uses NPIOP region */
+			astro_iop_base = ioremap(LBA_PORT_BASE, 64 * 1024);
+			pci_port = &lba_astro_port_ops;
+		}
+
+		/* Poke the chip a bit for /proc output */
+		lba_legacy_resources(dev, lba_dev);
+	}
+
+	/* 
+	** Tell PCI support another PCI bus was found.
+	** Walks PCI bus for us too.
+	*/
+	dev->dev.platform_data = lba_dev;
+	lba_bus = lba_dev->hba.hba_bus =
+		pci_scan_bus_parented(&dev->dev, lba_dev->hba.bus_num.start,
+				cfg_ops, NULL);
+
+	/* This is in lieu of calling pci_assign_unassigned_resources() */
+	if (is_pdc_pat()) {
+		/* assign resources to un-initialized devices */
+
+		DBG_PAT("LBA pci_bus_size_bridges()\n");
+		pci_bus_size_bridges(lba_bus);
+
+		DBG_PAT("LBA pci_bus_assign_resources()\n");
+		pci_bus_assign_resources(lba_bus);
+
+#ifdef DEBUG_LBA_PAT
+		DBG_PAT("\nLBA PIOP resource tree\n");
+		lba_dump_res(&lba_dev->hba.io_space, 2);
+		DBG_PAT("\nLBA LMMIO resource tree\n");
+		lba_dump_res(&lba_dev->hba.lmmio_space, 2);
+#endif
+	}
+	pci_enable_bridges(lba_bus);
+
+
+	/*
+	** Once PCI register ops has walked the bus, access to config
+	** space is restricted. Avoids master aborts on config cycles.
+	** Early LBA revs go fatal on *any* master abort.
+	*/
+	if (cfg_ops == &elroy_cfg_ops) {
+		lba_dev->flags |= LBA_FLAG_SKIP_PROBE;
+	}
+
+	/* Whew! Finally done! Tell services we got this one covered. */
+	return 0;
+}
+
+static struct parisc_device_id lba_tbl[] = {
+	{ HPHW_BRIDGE, HVERSION_REV_ANY_ID, ELROY_HVERS, 0xa },
+	{ HPHW_BRIDGE, HVERSION_REV_ANY_ID, MERCURY_HVERS, 0xa },
+	{ HPHW_BRIDGE, HVERSION_REV_ANY_ID, QUICKSILVER_HVERS, 0xa },
+	{ 0, }
+};
+
+static struct parisc_driver lba_driver = {
+	.name =		MODULE_NAME,
+	.id_table =	lba_tbl,
+	.probe =	lba_driver_probe,
+};
+
+/*
+** One time initialization to let the world know the LBA was found.
+** Must be called exactly once before pci_init().
+*/
+void __init lba_init(void)
+{
+	register_parisc_driver(&lba_driver);
+}
+
+/*
+** Initialize the IBASE/IMASK registers for LBA (Elroy).
+** Only called from sba_iommu.c in order to route ranges (MMIO vs DMA).
+** sba_iommu is responsible for locking (none needed at init time).
+*/
+void lba_set_iregs(struct parisc_device *lba, u32 ibase, u32 imask)
+{
+	void __iomem * base_addr = ioremap(lba->hpa, 4096);
+
+	imask <<= 2;	/* adjust for hints - 2 more bits */
+
+	/* Make sure we aren't trying to set bits that aren't writeable. */
+	WARN_ON((ibase & 0x001fffff) != 0);
+	WARN_ON((imask & 0x001fffff) != 0);
+	
+	DBG("%s() ibase 0x%x imask 0x%x\n", __FUNCTION__, ibase, imask);
+	WRITE_REG32( imask, base_addr + LBA_IMASK);
+	WRITE_REG32( ibase, base_addr + LBA_IBASE);
+	iounmap(base_addr);
+}
+
diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c
new file mode 100644
index 0000000..e90fb72
--- /dev/null
+++ b/drivers/parisc/led.c
@@ -0,0 +1,760 @@
+/*
+ *    Chassis LCD/LED driver for HP-PARISC workstations
+ *
+ *      (c) Copyright 2000 Red Hat Software
+ *      (c) Copyright 2000 Helge Deller <hdeller@redhat.com>
+ *      (c) Copyright 2001-2004 Helge Deller <deller@gmx.de>
+ *      (c) Copyright 2001 Randolph Chung <tausq@debian.org>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ * TODO:
+ *	- speed-up calculations with inlined assembler
+ *	- interface to write to second row of LCD from /proc (if technically possible)
+ *
+ * Changes:
+ *      - Audit copy_from_user in led_proc_write.
+ *                                Daniele Bellucci <bellucda@tiscali.it>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/stddef.h>	/* for offsetof() */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/utsname.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/in.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/reboot.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/blkdev.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/hardware.h>
+#include <asm/param.h>		/* HZ */
+#include <asm/led.h>
+#include <asm/pdc.h>
+#include <asm/uaccess.h>
+
+/* The control of the LEDs and LCDs on PARISC-machines have to be done 
+   completely in software. The necessary calculations are done in a tasklet
+   which is scheduled at every timer interrupt and since the calculations 
+   may consume relatively much CPU-time some of the calculations can be 
+   turned off with the following variables (controlled via procfs) */
+
+static int led_type = -1;
+static int led_heartbeat = 1;
+static int led_diskio = 1;
+static int led_lanrxtx = 1;
+static char lcd_text[32];
+static char lcd_text_default[32];
+
+#if 0
+#define DPRINTK(x)	printk x
+#else
+#define DPRINTK(x)
+#endif
+
+
+struct lcd_block {
+	unsigned char command;	/* stores the command byte      */
+	unsigned char on;	/* value for turning LED on     */
+	unsigned char off;	/* value for turning LED off    */
+};
+
+/* Structure returned by PDC_RETURN_CHASSIS_INFO */
+/* NOTE: we use unsigned long:16 two times, since the following member 
+   lcd_cmd_reg_addr needs to be 64bit aligned on 64bit PA2.0-machines */
+struct pdc_chassis_lcd_info_ret_block {
+	unsigned long model:16;		/* DISPLAY_MODEL_XXXX */
+	unsigned long lcd_width:16;	/* width of the LCD in chars (DISPLAY_MODEL_LCD only) */
+	unsigned long lcd_cmd_reg_addr;	/* ptr to LCD cmd-register & data ptr for LED */
+	unsigned long lcd_data_reg_addr; /* ptr to LCD data-register (LCD only) */
+	unsigned int min_cmd_delay;	/* delay in uS after cmd-write (LCD only) */
+	unsigned char reset_cmd1;	/* command #1 for writing LCD string (LCD only) */
+	unsigned char reset_cmd2;	/* command #2 for writing LCD string (LCD only) */
+	unsigned char act_enable;	/* 0 = no activity (LCD only) */
+	struct lcd_block heartbeat;
+	struct lcd_block disk_io;
+	struct lcd_block lan_rcv;
+	struct lcd_block lan_tx;
+	char _pad;
+};
+
+
+/* LCD_CMD and LCD_DATA for KittyHawk machines */
+#define KITTYHAWK_LCD_CMD  F_EXTEND(0xf0190000UL) /* 64bit-ready */
+#define KITTYHAWK_LCD_DATA (KITTYHAWK_LCD_CMD+1)
+
+/* lcd_info is pre-initialized to the values needed to program KittyHawk LCD's 
+ * HP seems to have used Sharp/Hitachi HD44780 LCDs most of the time. */
+static struct pdc_chassis_lcd_info_ret_block
+lcd_info __attribute__((aligned(8))) =
+{
+	.model =		DISPLAY_MODEL_LCD,
+	.lcd_width =		16,
+	.lcd_cmd_reg_addr =	KITTYHAWK_LCD_CMD,
+	.lcd_data_reg_addr =	KITTYHAWK_LCD_DATA,
+	.min_cmd_delay =	40,
+	.reset_cmd1 =		0x80,
+	.reset_cmd2 =		0xc0,
+};
+
+
+/* direct access to some of the lcd_info variables */
+#define LCD_CMD_REG	lcd_info.lcd_cmd_reg_addr	 
+#define LCD_DATA_REG	lcd_info.lcd_data_reg_addr	 
+#define LED_DATA_REG	lcd_info.lcd_cmd_reg_addr	/* LASI & ASP only */
+
+
+/* ptr to LCD/LED-specific function */
+static void (*led_func_ptr) (unsigned char);
+
+#define LED_HASLCD 1
+#define LED_NOLCD  0
+#ifdef CONFIG_PROC_FS
+static int led_proc_read(char *page, char **start, off_t off, int count, 
+	int *eof, void *data)
+{
+	char *out = page;
+	int len;
+
+	switch ((long)data)
+	{
+	case LED_NOLCD:
+		out += sprintf(out, "Heartbeat: %d\n", led_heartbeat);
+		out += sprintf(out, "Disk IO: %d\n", led_diskio);
+		out += sprintf(out, "LAN Rx/Tx: %d\n", led_lanrxtx);
+		break;
+	case LED_HASLCD:
+		out += sprintf(out, "%s\n", lcd_text);
+		break;
+	default:
+		*eof = 1;
+		return 0;
+	}
+
+	len = out - page - off;
+	if (len < count) {
+		*eof = 1;
+		if (len <= 0) return 0;
+	} else {
+		len = count;
+	}
+	*start = page + off;
+	return len;
+}
+
+static int led_proc_write(struct file *file, const char *buf, 
+	unsigned long count, void *data)
+{
+	char *cur, lbuf[count + 1];
+	int d;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	memset(lbuf, 0, count + 1);
+
+	if (copy_from_user(lbuf, buf, count))
+		return -EFAULT;
+
+	cur = lbuf;
+
+	/* skip initial spaces */
+	while (*cur && isspace(*cur))
+	{
+		cur++;
+	}
+
+	switch ((long)data)
+	{
+	case LED_NOLCD:
+		d = *cur++ - '0';
+		if (d != 0 && d != 1) goto parse_error;
+		led_heartbeat = d;
+
+		if (*cur++ != ' ') goto parse_error;
+
+		d = *cur++ - '0';
+		if (d != 0 && d != 1) goto parse_error;
+		led_diskio = d;
+
+		if (*cur++ != ' ') goto parse_error;
+
+		d = *cur++ - '0';
+		if (d != 0 && d != 1) goto parse_error;
+		led_lanrxtx = d;
+
+		break;
+	case LED_HASLCD:
+		if (*cur && cur[strlen(cur)-1] == '\n')
+			cur[strlen(cur)-1] = 0;
+		if (*cur == 0) 
+			cur = lcd_text_default;
+		lcd_print(cur);
+		break;
+	default:
+		return 0;
+	}
+	
+	return count;
+
+parse_error:
+	if ((long)data == LED_NOLCD)
+		printk(KERN_CRIT "Parse error: expect \"n n n\" (n == 0 or 1) for heartbeat,\ndisk io and lan tx/rx indicators\n");
+	return -EINVAL;
+}
+
+static int __init led_create_procfs(void)
+{
+	struct proc_dir_entry *proc_pdc_root = NULL;
+	struct proc_dir_entry *ent;
+
+	if (led_type == -1) return -1;
+
+	proc_pdc_root = proc_mkdir("pdc", 0);
+	if (!proc_pdc_root) return -1;
+	proc_pdc_root->owner = THIS_MODULE;
+	ent = create_proc_entry("led", S_IFREG|S_IRUGO|S_IWUSR, proc_pdc_root);
+	if (!ent) return -1;
+	ent->nlink = 1;
+	ent->data = (void *)LED_NOLCD; /* LED */
+	ent->read_proc = led_proc_read;
+	ent->write_proc = led_proc_write;
+	ent->owner = THIS_MODULE;
+
+	if (led_type == LED_HASLCD)
+	{
+		ent = create_proc_entry("lcd", S_IFREG|S_IRUGO|S_IWUSR, proc_pdc_root);
+		if (!ent) return -1;
+		ent->nlink = 1;
+		ent->data = (void *)LED_HASLCD; /* LCD */
+		ent->read_proc = led_proc_read;
+		ent->write_proc = led_proc_write;
+		ent->owner = THIS_MODULE;
+	}
+
+	return 0;
+}
+#endif
+
+/*
+   ** 
+   ** led_ASP_driver()
+   ** 
+ */
+#define	LED_DATA	0x01	/* data to shift (0:on 1:off) */
+#define	LED_STROBE	0x02	/* strobe to clock data */
+static void led_ASP_driver(unsigned char leds)
+{
+	int i;
+
+	leds = ~leds;
+	for (i = 0; i < 8; i++) {
+		unsigned char value;
+		value = (leds & 0x80) >> 7;
+		gsc_writeb( value,		 LED_DATA_REG );
+		gsc_writeb( value | LED_STROBE,	 LED_DATA_REG );
+		leds <<= 1;
+	}
+}
+
+
+/*
+   ** 
+   ** led_LASI_driver()
+   ** 
+ */
+static void led_LASI_driver(unsigned char leds)
+{
+	leds = ~leds;
+	gsc_writeb( leds, LED_DATA_REG );
+}
+
+
+/*
+   ** 
+   ** led_LCD_driver()
+   ** 
+   ** The logic of the LCD driver is, that we write at every scheduled call
+   ** only to one of LCD_CMD_REG _or_ LCD_DATA_REG - registers.
+   ** That way we don't need to let this tasklet busywait for min_cmd_delay
+   ** milliseconds.
+   **
+   ** TODO: check the value of "min_cmd_delay" against the value of HZ.
+   **   
+ */
+static void led_LCD_driver(unsigned char leds)
+{
+	static int last_index;	/* 0:heartbeat, 1:disk, 2:lan_in, 3:lan_out */
+	static int last_was_cmd;/* 0: CMD was written last, 1: DATA was last */
+	struct lcd_block *block_ptr;
+	int value;
+
+	switch (last_index) {
+	    case 0:	block_ptr = &lcd_info.heartbeat;
+			value = leds & LED_HEARTBEAT;
+			break;
+	    case 1:	block_ptr = &lcd_info.disk_io;
+			value = leds & LED_DISK_IO;
+			break;					
+	    case 2:	block_ptr = &lcd_info.lan_rcv;
+			value = leds & LED_LAN_RCV;
+			break;					
+	    case 3:	block_ptr = &lcd_info.lan_tx;
+			value = leds & LED_LAN_TX;
+			break;
+	    default:	/* should never happen: */
+			return;
+	}
+
+	if (last_was_cmd) {
+	    /* write the value to the LCD data port */
+    	    gsc_writeb( value ? block_ptr->on : block_ptr->off, LCD_DATA_REG );
+	} else {
+	    /* write the command-byte to the LCD command register */
+    	    gsc_writeb( block_ptr->command, LCD_CMD_REG );
+	}    
+	
+	/* now update the vars for the next interrupt iteration */ 
+	if (++last_was_cmd == 2) { /* switch between cmd & data */
+	    last_was_cmd = 0;
+	    if (++last_index == 4) 
+		last_index = 0;	 /* switch back to heartbeat index */
+	}
+}
+
+
+/*
+   ** 
+   ** led_get_net_activity()
+   ** 
+   ** calculate if there was TX- or RX-troughput on the network interfaces
+   ** (analog to dev_get_info() from net/core/dev.c)
+   **   
+ */
+static __inline__ int led_get_net_activity(void)
+{ 
+#ifndef CONFIG_NET
+	return 0;
+#else
+	static unsigned long rx_total_last, tx_total_last;
+	unsigned long rx_total, tx_total;
+	struct net_device *dev;
+	int retval;
+
+	rx_total = tx_total = 0;
+	
+	/* we are running as tasklet, so locking dev_base 
+	 * for reading should be OK */
+	read_lock(&dev_base_lock);
+	for (dev = dev_base; dev; dev = dev->next) {
+	    struct net_device_stats *stats;
+	    struct in_device *in_dev = __in_dev_get(dev);
+	    if (!in_dev || !in_dev->ifa_list)
+		continue;
+	    if (LOOPBACK(in_dev->ifa_list->ifa_local))
+		continue;
+	    if (!dev->get_stats) 
+		continue;
+	    stats = dev->get_stats(dev);
+	    rx_total += stats->rx_packets;
+	    tx_total += stats->tx_packets;
+	}
+	read_unlock(&dev_base_lock);
+
+	retval = 0;
+
+	if (rx_total != rx_total_last) {
+		rx_total_last = rx_total;
+		retval |= LED_LAN_RCV;
+	}
+
+	if (tx_total != tx_total_last) {
+		tx_total_last = tx_total;
+		retval |= LED_LAN_TX;
+	}
+
+	return retval;
+#endif
+}
+
+
+/*
+   ** 
+   ** led_get_diskio_activity()
+   ** 
+   ** calculate if there was disk-io in the system
+   **   
+ */
+static __inline__ int led_get_diskio_activity(void)
+{	
+	static unsigned long last_pgpgin, last_pgpgout;
+	struct page_state pgstat;
+	int changed;
+	
+	get_full_page_state(&pgstat); /* get no of sectors in & out */
+
+	/* Just use a very simple calculation here. Do not care about overflow,
+	   since we only want to know if there was activity or not. */
+	changed = (pgstat.pgpgin != last_pgpgin) || (pgstat.pgpgout != last_pgpgout);
+	last_pgpgin  = pgstat.pgpgin;
+	last_pgpgout = pgstat.pgpgout;
+	
+	return (changed ? LED_DISK_IO : 0);
+}
+
+
+
+/*
+   ** led_tasklet_func()
+   ** 
+   ** is scheduled at every timer interrupt from time.c and
+   ** updates the chassis LCD/LED 
+
+    TODO:
+    - display load average (older machines like 715/64 have 4 "free" LED's for that)
+    - optimizations
+ */
+
+#define HEARTBEAT_LEN (HZ*6/100)
+#define HEARTBEAT_2ND_RANGE_START (HZ*22/100)
+#define HEARTBEAT_2ND_RANGE_END   (HEARTBEAT_2ND_RANGE_START + HEARTBEAT_LEN)
+
+#define NORMALIZED_COUNT(count) (count/(HZ/100))
+
+static void led_tasklet_func(unsigned long unused)
+{
+	static unsigned char lastleds;
+	unsigned char currentleds; /* stores current value of the LEDs */
+	static unsigned long count; /* static incremented value, not wrapped */
+	static unsigned long count_HZ; /* counter in range 0..HZ */
+
+	/* exit if not initialized */
+	if (!led_func_ptr)
+	    return;
+
+	/* increment the local counters */
+	++count;
+	if (++count_HZ == HZ)
+	    count_HZ = 0;
+
+	currentleds = lastleds;
+
+	if (led_heartbeat)
+	{
+		/* flash heartbeat-LED like a real heart (2 x short then a long delay) */
+		if (count_HZ<HEARTBEAT_LEN || 
+		    (count_HZ>=HEARTBEAT_2ND_RANGE_START && count_HZ<HEARTBEAT_2ND_RANGE_END)) 
+		    currentleds |= LED_HEARTBEAT;
+		else
+		    currentleds &= ~LED_HEARTBEAT;
+	}
+
+	/* look for network activity and flash LEDs respectively */
+	if (led_lanrxtx && ((NORMALIZED_COUNT(count)+(8/2)) & 7) == 0)
+	{
+		currentleds &= ~(LED_LAN_RCV | LED_LAN_TX);
+		currentleds |= led_get_net_activity();
+	}
+
+	/* avoid to calculate diskio-stats at same irq  as netio-stats */
+	if (led_diskio && (NORMALIZED_COUNT(count) & 7) == 0)
+	{
+		currentleds &= ~LED_DISK_IO;
+		currentleds |= led_get_diskio_activity();
+	}
+
+	/* blink all LEDs twice a second if we got an Oops (HPMC) */
+	if (oops_in_progress) {
+		currentleds = (count_HZ<=(HZ/2)) ? 0 : 0xff;
+	}
+	
+	/* update the LCD/LEDs */
+	if (currentleds != lastleds) {
+	    led_func_ptr(currentleds);
+	    lastleds = currentleds;
+	}
+}
+
+/* main led tasklet struct (scheduled from time.c) */
+DECLARE_TASKLET_DISABLED(led_tasklet, led_tasklet_func, 0);
+
+
+/*
+   ** led_halt()
+   ** 
+   ** called by the reboot notifier chain at shutdown and stops all
+   ** LED/LCD activities.
+   ** 
+ */
+
+static int led_halt(struct notifier_block *, unsigned long, void *);
+
+static struct notifier_block led_notifier = {
+	.notifier_call = led_halt,
+};
+
+static int led_halt(struct notifier_block *nb, unsigned long event, void *buf) 
+{
+	char *txt;
+	
+	switch (event) {
+	case SYS_RESTART:	txt = "SYSTEM RESTART";
+				break;
+	case SYS_HALT:		txt = "SYSTEM HALT";
+				break;
+	case SYS_POWER_OFF:	txt = "SYSTEM POWER OFF";
+				break;
+	default:		return NOTIFY_DONE;
+	}
+	
+	/* completely stop the LED/LCD tasklet */
+	tasklet_disable(&led_tasklet);
+
+	if (lcd_info.model == DISPLAY_MODEL_LCD)
+		lcd_print(txt);
+	else
+		if (led_func_ptr)
+			led_func_ptr(0xff); /* turn all LEDs ON */
+	
+	unregister_reboot_notifier(&led_notifier);
+	return NOTIFY_OK;
+}
+
+/*
+   ** register_led_driver()
+   ** 
+   ** registers an external LED or LCD for usage by this driver.
+   ** currently only LCD-, LASI- and ASP-style LCD/LED's are supported.
+   ** 
+ */
+
+int __init register_led_driver(int model, unsigned long cmd_reg, unsigned long data_reg)
+{
+	static int initialized;
+	
+	if (initialized || !data_reg)
+		return 1;
+	
+	lcd_info.model = model;		/* store the values */
+	LCD_CMD_REG = (cmd_reg == LED_CMD_REG_NONE) ? 0 : cmd_reg;
+
+	switch (lcd_info.model) {
+	case DISPLAY_MODEL_LCD:
+		LCD_DATA_REG = data_reg;
+		printk(KERN_INFO "LCD display at %lx,%lx registered\n", 
+			LCD_CMD_REG , LCD_DATA_REG);
+		led_func_ptr = led_LCD_driver;
+		lcd_print( lcd_text_default );
+		led_type = LED_HASLCD;
+		break;
+
+	case DISPLAY_MODEL_LASI:
+		LED_DATA_REG = data_reg;
+		led_func_ptr = led_LASI_driver;
+		printk(KERN_INFO "LED display at %lx registered\n", LED_DATA_REG);
+		led_type = LED_NOLCD;
+		break;
+
+	case DISPLAY_MODEL_OLD_ASP:
+		LED_DATA_REG = data_reg;
+		led_func_ptr = led_ASP_driver;
+		printk(KERN_INFO "LED (ASP-style) display at %lx registered\n", 
+		    LED_DATA_REG);
+		led_type = LED_NOLCD;
+		break;
+
+	default:
+		printk(KERN_ERR "%s: Wrong LCD/LED model %d !\n",
+		       __FUNCTION__, lcd_info.model);
+		return 1;
+	}
+	
+	/* mark the LCD/LED driver now as initialized and 
+	 * register to the reboot notifier chain */
+	initialized++;
+	register_reboot_notifier(&led_notifier);
+
+	/* start the led tasklet for the first time */
+	tasklet_enable(&led_tasklet);
+	
+	return 0;
+}
+
+/*
+   ** register_led_regions()
+   ** 
+   ** register_led_regions() registers the LCD/LED regions for /procfs.
+   ** At bootup - where the initialisation of the LCD/LED normally happens - 
+   ** not all internal structures of request_region() are properly set up,
+   ** so that we delay the led-registration until after busdevices_init() 
+   ** has been executed.
+   **
+ */
+
+void __init register_led_regions(void)
+{
+	switch (lcd_info.model) {
+	case DISPLAY_MODEL_LCD:
+		request_mem_region((unsigned long)LCD_CMD_REG,  1, "lcd_cmd");
+		request_mem_region((unsigned long)LCD_DATA_REG, 1, "lcd_data");
+		break;
+	case DISPLAY_MODEL_LASI:
+	case DISPLAY_MODEL_OLD_ASP:
+		request_mem_region((unsigned long)LED_DATA_REG, 1, "led_data");
+		break;
+	}
+}
+
+
+/*
+   ** 
+   ** lcd_print()
+   ** 
+   ** Displays the given string on the LCD-Display of newer machines.
+   ** lcd_print() disables the timer-based led tasklet during its 
+   ** execution and enables it afterwards again.
+   **
+ */
+int lcd_print( char *str )
+{
+	int i;
+
+	if (!led_func_ptr || lcd_info.model != DISPLAY_MODEL_LCD)
+	    return 0;
+	
+	/* temporarily disable the led tasklet */
+	tasklet_disable(&led_tasklet);
+
+	/* copy display string to buffer for procfs */
+	strlcpy(lcd_text, str, sizeof(lcd_text));
+	
+	/* Set LCD Cursor to 1st character */
+	gsc_writeb(lcd_info.reset_cmd1, LCD_CMD_REG);
+	udelay(lcd_info.min_cmd_delay);
+
+	/* Print the string */
+	for (i=0; i < lcd_info.lcd_width; i++) {
+	    if (str && *str)
+		gsc_writeb(*str++, LCD_DATA_REG);
+	    else
+		gsc_writeb(' ', LCD_DATA_REG);
+	    udelay(lcd_info.min_cmd_delay);
+	}
+	
+	/* re-enable the led tasklet */
+	tasklet_enable(&led_tasklet);
+
+	return lcd_info.lcd_width;
+}
+
+/*
+   ** led_init()
+   ** 
+   ** led_init() is called very early in the bootup-process from setup.c 
+   ** and asks the PDC for an usable chassis LCD or LED.
+   ** If the PDC doesn't return any info, then the LED
+   ** is detected by lasi.c or asp.c and registered with the
+   ** above functions lasi_led_init() or asp_led_init().
+   ** KittyHawk machines have often a buggy PDC, so that
+   ** we explicitly check for those machines here.
+ */
+
+int __init led_init(void)
+{
+	struct pdc_chassis_info chassis_info;
+	int ret;
+
+	snprintf(lcd_text_default, sizeof(lcd_text_default),
+		"Linux %s", system_utsname.release);
+
+	/* Work around the buggy PDC of KittyHawk-machines */
+	switch (CPU_HVERSION) {
+	case 0x580:		/* KittyHawk DC2-100 (K100) */
+	case 0x581:		/* KittyHawk DC3-120 (K210) */
+	case 0x582:		/* KittyHawk DC3 100 (K400) */
+	case 0x583:		/* KittyHawk DC3 120 (K410) */
+	case 0x58B:		/* KittyHawk DC2 100 (K200) */
+		printk(KERN_INFO "%s: KittyHawk-Machine (hversion 0x%x) found, "
+				"LED detection skipped.\n", __FILE__, CPU_HVERSION);
+		goto found;	/* use the preinitialized values of lcd_info */
+	}
+
+	/* initialize the struct, so that we can check for valid return values */
+	lcd_info.model = DISPLAY_MODEL_NONE;
+	chassis_info.actcnt = chassis_info.maxcnt = 0;
+
+	ret = pdc_chassis_info(&chassis_info, &lcd_info, sizeof(lcd_info));
+	if (ret == PDC_OK) {
+		DPRINTK((KERN_INFO "%s: chassis info: model=%d (%s), "
+			 "lcd_width=%d, cmd_delay=%u,\n"
+			 "%s: sizecnt=%d, actcnt=%ld, maxcnt=%ld\n",
+		         __FILE__, lcd_info.model,
+			 (lcd_info.model==DISPLAY_MODEL_LCD) ? "LCD" :
+			  (lcd_info.model==DISPLAY_MODEL_LASI) ? "LED" : "unknown",
+			 lcd_info.lcd_width, lcd_info.min_cmd_delay,
+			 __FILE__, sizeof(lcd_info), 
+			 chassis_info.actcnt, chassis_info.maxcnt));
+		DPRINTK((KERN_INFO "%s: cmd=%p, data=%p, reset1=%x, reset2=%x, act_enable=%d\n",
+			__FILE__, lcd_info.lcd_cmd_reg_addr, 
+			lcd_info.lcd_data_reg_addr, lcd_info.reset_cmd1,  
+			lcd_info.reset_cmd2, lcd_info.act_enable ));
+	
+		/* check the results. Some machines have a buggy PDC */
+		if (chassis_info.actcnt <= 0 || chassis_info.actcnt != chassis_info.maxcnt)
+			goto not_found;
+
+		switch (lcd_info.model) {
+		case DISPLAY_MODEL_LCD:		/* LCD display */
+			if (chassis_info.actcnt < 
+				offsetof(struct pdc_chassis_lcd_info_ret_block, _pad)-1)
+				goto not_found;
+			if (!lcd_info.act_enable) {
+				DPRINTK((KERN_INFO "PDC prohibited usage of the LCD.\n"));
+				goto not_found;
+			}
+			break;
+
+		case DISPLAY_MODEL_NONE:	/* no LED or LCD available */
+			printk(KERN_INFO "PDC reported no LCD or LED.\n");
+			goto not_found;
+
+		case DISPLAY_MODEL_LASI:	/* Lasi style 8 bit LED display */
+			if (chassis_info.actcnt != 8 && chassis_info.actcnt != 32)
+				goto not_found;
+			break;
+
+		default:
+			printk(KERN_WARNING "PDC reported unknown LCD/LED model %d\n",
+			       lcd_info.model);
+			goto not_found;
+		} /* switch() */
+
+found:
+		/* register the LCD/LED driver */
+		register_led_driver(lcd_info.model, LCD_CMD_REG, LCD_DATA_REG);
+		return 0;
+
+	} else { /* if() */
+		DPRINTK((KERN_INFO "pdc_chassis_info call failed with retval = %d\n", ret));
+	}
+
+not_found:
+	lcd_info.model = DISPLAY_MODEL_NONE;
+	return 1;
+}
+
+#ifdef CONFIG_PROC_FS
+module_init(led_create_procfs)
+#endif
diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c
new file mode 100644
index 0000000..67c8f3b
--- /dev/null
+++ b/drivers/parisc/pdc_stable.c
@@ -0,0 +1,735 @@
+/* 
+ *    Interfaces to retrieve and set PDC Stable options (firmware)
+ *
+ *    Copyright (C) 2005 Thibaut VARENE <varenet@parisc-linux.org>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program 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 General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ *    DEV NOTE: the PDC Procedures reference states that:
+ *    "A minimum of 96 bytes of Stable Storage is required. Providing more than
+ *    96 bytes of Stable Storage is optional [...]. Failure to provide the
+ *    optional locations from 96 to 192 results in the loss of certain
+ *    functionality during boot."
+ *
+ *    Since locations between 96 and 192 are the various paths, most (if not
+ *    all) PA-RISC machines should have them. Anyway, for safety reasons, the
+ *    following code can deal with only 96 bytes of Stable Storage, and all
+ *    sizes between 96 and 192 bytes (provided they are multiple of struct
+ *    device_path size, eg: 128, 160 and 192) to provide full information.
+ *    The code makes no use of data above 192 bytes. One last word: there's one
+ *    path we can always count on: the primary path.
+ */
+
+#undef PDCS_DEBUG
+#ifdef PDCS_DEBUG
+#define DPRINTK(fmt, args...)	printk(KERN_DEBUG fmt, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>		/* for capable() */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+
+#include <asm/pdc.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+
+#define PDCS_VERSION	"0.09"
+
+#define PDCS_ADDR_PPRI	0x00
+#define PDCS_ADDR_OSID	0x40
+#define PDCS_ADDR_FSIZ	0x5C
+#define PDCS_ADDR_PCON	0x60
+#define PDCS_ADDR_PALT	0x80
+#define PDCS_ADDR_PKBD	0xA0
+
+MODULE_AUTHOR("Thibaut VARENE <varenet@parisc-linux.org>");
+MODULE_DESCRIPTION("sysfs interface to HP PDC Stable Storage data");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PDCS_VERSION);
+
+static unsigned long pdcs_size = 0;
+
+/* This struct defines what we need to deal with a parisc pdc path entry */
+struct pdcspath_entry {
+	short ready;			/* entry record is valid if != 0 */
+	unsigned long addr;		/* entry address in stable storage */
+	char *name;			/* entry name */
+	struct device_path devpath;	/* device path in parisc representation */
+	struct device *dev;		/* corresponding device */
+	struct kobject kobj;
+};
+
+struct pdcspath_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct pdcspath_entry *entry, char *buf);
+	ssize_t (*store)(struct pdcspath_entry *entry, const char *buf, size_t count);
+};
+
+#define PDCSPATH_ENTRY(_addr, _name) \
+struct pdcspath_entry pdcspath_entry_##_name = { \
+	.ready = 0, \
+	.addr = _addr, \
+	.name = __stringify(_name), \
+};
+
+#define PDCS_ATTR(_name, _mode, _show, _store) \
+struct subsys_attribute pdcs_attr_##_name = { \
+	.attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \
+	.show = _show, \
+	.store = _store, \
+};
+
+#define PATHS_ATTR(_name, _mode, _show, _store) \
+struct pdcspath_attribute paths_attr_##_name = { \
+	.attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \
+	.show = _show, \
+	.store = _store, \
+};
+
+#define to_pdcspath_attribute(_attr) container_of(_attr, struct pdcspath_attribute, attr)
+#define to_pdcspath_entry(obj)  container_of(obj, struct pdcspath_entry, kobj)
+
+/**
+ * pdcspath_fetch - This function populates the path entry structs.
+ * @entry: A pointer to an allocated pdcspath_entry.
+ * 
+ * The general idea is that you don't read from the Stable Storage every time
+ * you access the files provided by the facilites. We store a copy of the
+ * content of the stable storage WRT various paths in these structs. We read
+ * these structs when reading the files, and we will write to these structs when
+ * writing to the files, and only then write them back to the Stable Storage.
+ */
+static int
+pdcspath_fetch(struct pdcspath_entry *entry)
+{
+	struct device_path *devpath;
+
+	if (!entry)
+		return -EINVAL;
+
+	devpath = &entry->devpath;
+	
+	DPRINTK("%s: fetch: 0x%p, 0x%p, addr: 0x%lx\n", __func__,
+			entry, devpath, entry->addr);
+
+	/* addr, devpath and count must be word aligned */
+	if (pdc_stable_read(entry->addr, devpath, sizeof(*devpath)) != PDC_OK)
+		return -EIO;
+		
+	/* Find the matching device.
+	   NOTE: hardware_path overlays with device_path, so the nice cast can
+	   be used */
+	entry->dev = hwpath_to_device((struct hardware_path *)devpath);
+
+	entry->ready = 1;
+	
+	DPRINTK("%s: device: 0x%p\n", __func__, entry->dev);
+	
+	return 0;
+}
+
+/**
+ * pdcspath_store - This function writes a path to stable storage.
+ * @entry: A pointer to an allocated pdcspath_entry.
+ * 
+ * It can be used in two ways: either by passing it a preset devpath struct
+ * containing an already computed hardware path, or by passing it a device
+ * pointer, from which it'll find out the corresponding hardware path.
+ * For now we do not handle the case where there's an error in writing to the
+ * Stable Storage area, so you'd better not mess up the data :P
+ */
+static int
+pdcspath_store(struct pdcspath_entry *entry)
+{
+	struct device_path *devpath;
+
+	if (!entry)
+		return -EINVAL;
+
+	devpath = &entry->devpath;
+	
+	/* We expect the caller to set the ready flag to 0 if the hardware
+	   path struct provided is invalid, so that we know we have to fill it.
+	   First case, we don't have a preset hwpath... */
+	if (!entry->ready) {
+		/* ...but we have a device, map it */
+		if (entry->dev)
+			device_to_hwpath(entry->dev, (struct hardware_path *)devpath);
+		else
+			return -EINVAL;
+	}
+	/* else, we expect the provided hwpath to be valid. */
+	
+	DPRINTK("%s: store: 0x%p, 0x%p, addr: 0x%lx\n", __func__,
+			entry, devpath, entry->addr);
+
+	/* addr, devpath and count must be word aligned */
+	if (pdc_stable_write(entry->addr, devpath, sizeof(*devpath)) != PDC_OK) {
+		printk(KERN_ERR "%s: an error occured when writing to PDC.\n"
+				"It is likely that the Stable Storage data has been corrupted.\n"
+				"Please check it carefully upon next reboot.\n", __func__);
+		return -EIO;
+	}
+		
+	entry->ready = 1;
+	
+	DPRINTK("%s: device: 0x%p\n", __func__, entry->dev);
+	
+	return 0;
+}
+
+/**
+ * pdcspath_hwpath_read - This function handles hardware path pretty printing.
+ * @entry: An allocated and populated pdscpath_entry struct.
+ * @buf: The output buffer to write to.
+ * 
+ * We will call this function to format the output of the hwpath attribute file.
+ */
+static ssize_t
+pdcspath_hwpath_read(struct pdcspath_entry *entry, char *buf)
+{
+	char *out = buf;
+	struct device_path *devpath;
+	unsigned short i;
+
+	if (!entry || !buf)
+		return -EINVAL;
+
+	devpath = &entry->devpath;
+
+	if (!entry->ready)
+		return -ENODATA;
+	
+	for (i = 0; i < 6; i++) {
+		if (devpath->bc[i] >= 128)
+			continue;
+		out += sprintf(out, "%u/", (unsigned char)devpath->bc[i]);
+	}
+	out += sprintf(out, "%u\n", (unsigned char)devpath->mod);
+	
+	return out - buf;
+}
+
+/**
+ * pdcspath_hwpath_write - This function handles hardware path modifying.
+ * @entry: An allocated and populated pdscpath_entry struct.
+ * @buf: The input buffer to read from.
+ * @count: The number of bytes to be read.
+ * 
+ * We will call this function to change the current hardware path.
+ * Hardware paths are to be given '/'-delimited, without brackets.
+ * We take care to make sure that the provided path actually maps to an existing
+ * device, BUT nothing would prevent some foolish user to set the path to some
+ * PCI bridge or even a CPU...
+ * A better work around would be to make sure we are at the end of a device tree
+ * for instance, but it would be IMHO beyond the simple scope of that driver.
+ * The aim is to provide a facility. Data correctness is left to userland.
+ */
+static ssize_t
+pdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t count)
+{
+	struct hardware_path hwpath;
+	unsigned short i;
+	char in[count+1], *temp;
+	struct device *dev;
+
+	if (!entry || !buf || !count)
+		return -EINVAL;
+
+	/* We'll use a local copy of buf */
+	memset(in, 0, count+1);
+	strncpy(in, buf, count);
+	
+	/* Let's clean up the target. 0xff is a blank pattern */
+	memset(&hwpath, 0xff, sizeof(hwpath));
+	
+	/* First, pick the mod field (the last one of the input string) */
+	if (!(temp = strrchr(in, '/')))
+		return -EINVAL;
+			
+	hwpath.mod = simple_strtoul(temp+1, NULL, 10);
+	in[temp-in] = '\0';	/* truncate the remaining string. just precaution */
+	DPRINTK("%s: mod: %d\n", __func__, hwpath.mod);
+	
+	/* Then, loop for each delimiter, making sure we don't have too many.
+	   we write the bc fields in a down-top way. No matter what, we stop
+	   before writing the last field. If there are too many fields anyway,
+	   then the user is a moron and it'll be caught up later when we'll
+	   check the consistency of the given hwpath. */
+	for (i=5; ((temp = strrchr(in, '/'))) && (temp-in > 0) && (likely(i)); i--) {
+		hwpath.bc[i] = simple_strtoul(temp+1, NULL, 10);
+		in[temp-in] = '\0';
+		DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]);
+	}
+	
+	/* Store the final field */		
+	hwpath.bc[i] = simple_strtoul(in, NULL, 10);
+	DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]);
+	
+	/* Now we check that the user isn't trying to lure us */
+	if (!(dev = hwpath_to_device((struct hardware_path *)&hwpath))) {
+		printk(KERN_WARNING "%s: attempt to set invalid \"%s\" "
+			"hardware path: %s\n", __func__, entry->name, buf);
+		return -EINVAL;
+	}
+	
+	/* So far so good, let's get in deep */
+	entry->ready = 0;
+	entry->dev = dev;
+	
+	/* Now, dive in. Write back to the hardware */
+	WARN_ON(pdcspath_store(entry));	/* this warn should *NEVER* happen */
+	
+	/* Update the symlink to the real device */
+	sysfs_remove_link(&entry->kobj, "device");
+	sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");
+	
+	printk(KERN_INFO "PDC Stable Storage: changed \"%s\" path to \"%s\"\n",
+		entry->name, buf);
+	
+	return count;
+}
+
+/**
+ * pdcspath_layer_read - Extended layer (eg. SCSI ids) pretty printing.
+ * @entry: An allocated and populated pdscpath_entry struct.
+ * @buf: The output buffer to write to.
+ * 
+ * We will call this function to format the output of the layer attribute file.
+ */
+static ssize_t
+pdcspath_layer_read(struct pdcspath_entry *entry, char *buf)
+{
+	char *out = buf;
+	struct device_path *devpath;
+	unsigned short i;
+
+	if (!entry || !buf)
+		return -EINVAL;
+	
+	devpath = &entry->devpath;
+
+	if (!entry->ready)
+		return -ENODATA;
+	
+	for (i = 0; devpath->layers[i] && (likely(i < 6)); i++)
+		out += sprintf(out, "%u ", devpath->layers[i]);
+
+	out += sprintf(out, "\n");
+	
+	return out - buf;
+}
+
+/**
+ * pdcspath_layer_write - This function handles extended layer modifying.
+ * @entry: An allocated and populated pdscpath_entry struct.
+ * @buf: The input buffer to read from.
+ * @count: The number of bytes to be read.
+ * 
+ * We will call this function to change the current layer value.
+ * Layers are to be given '.'-delimited, without brackets.
+ * XXX beware we are far less checky WRT input data provided than for hwpath.
+ * Potential harm can be done, since there's no way to check the validity of
+ * the layer fields.
+ */
+static ssize_t
+pdcspath_layer_write(struct pdcspath_entry *entry, const char *buf, size_t count)
+{
+	unsigned int layers[6]; /* device-specific info (ctlr#, unit#, ...) */
+	unsigned short i;
+	char in[count+1], *temp;
+
+	if (!entry || !buf || !count)
+		return -EINVAL;
+
+	/* We'll use a local copy of buf */
+	memset(in, 0, count+1);
+	strncpy(in, buf, count);
+	
+	/* Let's clean up the target. 0 is a blank pattern */
+	memset(&layers, 0, sizeof(layers));
+	
+	/* First, pick the first layer */
+	if (unlikely(!isdigit(*in)))
+		return -EINVAL;
+	layers[0] = simple_strtoul(in, NULL, 10);
+	DPRINTK("%s: layer[0]: %d\n", __func__, layers[0]);
+	
+	temp = in;
+	for (i=1; ((temp = strchr(temp, '.'))) && (likely(i<6)); i++) {
+		if (unlikely(!isdigit(*(++temp))))
+			return -EINVAL;
+		layers[i] = simple_strtoul(temp, NULL, 10);
+		DPRINTK("%s: layer[%d]: %d\n", __func__, i, layers[i]);
+	}
+		
+	/* So far so good, let's get in deep */
+	
+	/* First, overwrite the current layers with the new ones, not touching
+	   the hardware path. */
+	memcpy(&entry->devpath.layers, &layers, sizeof(layers));
+	
+	/* Now, dive in. Write back to the hardware */
+	WARN_ON(pdcspath_store(entry));	/* this warn should *NEVER* happen */
+	
+	printk(KERN_INFO "PDC Stable Storage: changed \"%s\" layers to \"%s\"\n",
+		entry->name, buf);
+	
+	return count;
+}
+
+/**
+ * pdcspath_attr_show - Generic read function call wrapper.
+ * @kobj: The kobject to get info from.
+ * @attr: The attribute looked upon.
+ * @buf: The output buffer.
+ */
+static ssize_t
+pdcspath_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+	struct pdcspath_entry *entry = to_pdcspath_entry(kobj);
+	struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr);
+	ssize_t ret = 0;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	if (pdcs_attr->show)
+		ret = pdcs_attr->show(entry, buf);
+
+	return ret;
+}
+
+/**
+ * pdcspath_attr_store - Generic write function call wrapper.
+ * @kobj: The kobject to write info to.
+ * @attr: The attribute to be modified.
+ * @buf: The input buffer.
+ * @count: The size of the buffer.
+ */
+static ssize_t
+pdcspath_attr_store(struct kobject *kobj, struct attribute *attr,
+			const char *buf, size_t count)
+{
+	struct pdcspath_entry *entry = to_pdcspath_entry(kobj);
+	struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr);
+	ssize_t ret = 0;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	if (pdcs_attr->store)
+		ret = pdcs_attr->store(entry, buf, count);
+
+	return ret;
+}
+
+static struct sysfs_ops pdcspath_attr_ops = {
+	.show = pdcspath_attr_show,
+	.store = pdcspath_attr_store,
+};
+
+/* These are the two attributes of any PDC path. */
+static PATHS_ATTR(hwpath, 0600, pdcspath_hwpath_read, pdcspath_hwpath_write);
+static PATHS_ATTR(layer, 0600, pdcspath_layer_read, pdcspath_layer_write);
+
+static struct attribute *paths_subsys_attrs[] = {
+	&paths_attr_hwpath.attr,
+	&paths_attr_layer.attr,
+	NULL,
+};
+
+/* Specific kobject type for our PDC paths */
+static struct kobj_type ktype_pdcspath = {
+	.sysfs_ops = &pdcspath_attr_ops,
+	.default_attrs = paths_subsys_attrs,
+};
+
+/* We hard define the 4 types of path we expect to find */
+static PDCSPATH_ENTRY(PDCS_ADDR_PPRI, primary);
+static PDCSPATH_ENTRY(PDCS_ADDR_PCON, console);
+static PDCSPATH_ENTRY(PDCS_ADDR_PALT, alternative);
+static PDCSPATH_ENTRY(PDCS_ADDR_PKBD, keyboard);
+
+/* An array containing all PDC paths we will deal with */
+static struct pdcspath_entry *pdcspath_entries[] = {
+	&pdcspath_entry_primary,
+	&pdcspath_entry_alternative,
+	&pdcspath_entry_console,
+	&pdcspath_entry_keyboard,
+	NULL,
+};
+
+/**
+ * pdcs_info_read - Pretty printing of the remaining useful data.
+ * @entry: An allocated and populated subsytem struct. We don't use it tho.
+ * @buf: The output buffer to write to.
+ * 
+ * We will call this function to format the output of the 'info' attribute file.
+ * Please refer to PDC Procedures documentation, section PDC_STABLE to get a
+ * better insight of what we're doing here.
+ */
+static ssize_t
+pdcs_info_read(struct subsystem *entry, char *buf)
+{
+	char *out = buf;
+	__u32 result;
+	struct device_path devpath;
+	char *tmpstr = NULL;
+	
+	if (!entry || !buf)
+		return -EINVAL;
+		
+	/* show the size of the stable storage */
+	out += sprintf(out, "Stable Storage size: %ld bytes\n", pdcs_size);
+
+	/* deal with flags */
+	if (pdc_stable_read(PDCS_ADDR_PPRI, &devpath, sizeof(devpath)) != PDC_OK)
+		return -EIO;
+	
+	out += sprintf(out, "Autoboot: %s\n", (devpath.flags & PF_AUTOBOOT) ? "On" : "Off");
+	out += sprintf(out, "Autosearch: %s\n", (devpath.flags & PF_AUTOSEARCH) ? "On" : "Off");
+	out += sprintf(out, "Timer: %u s\n", (devpath.flags & PF_TIMER) ? (1 << (devpath.flags & PF_TIMER)) : 0);
+
+	/* get OSID */
+	if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK)
+		return -EIO;
+
+	/* the actual result is 16 bits away */
+	switch (result >> 16) {
+		case 0x0000:	tmpstr = "No OS-dependent data"; break;
+		case 0x0001:	tmpstr = "HP-UX dependent data"; break;
+		case 0x0002:	tmpstr = "MPE-iX dependent data"; break;
+		case 0x0003:	tmpstr = "OSF dependent data"; break;
+		case 0x0004:	tmpstr = "HP-RT dependent data"; break;
+		case 0x0005:	tmpstr = "Novell Netware dependent data"; break;
+		default:	tmpstr = "Unknown"; break;
+	}
+	out += sprintf(out, "OS ID: %s (0x%.4x)\n", tmpstr, (result >> 16));
+
+	/* get fast-size */
+	if (pdc_stable_read(PDCS_ADDR_FSIZ, &result, sizeof(result)) != PDC_OK)
+		return -EIO;
+
+	out += sprintf(out, "Memory tested: ");
+	if ((result & 0x0F) < 0x0E)
+		out += sprintf(out, "%.3f MB", 0.256*(1<<(result & 0x0F)));
+	else
+		out += sprintf(out, "All");
+	out += sprintf(out, "\n");
+	
+	return out - buf;
+}
+
+/**
+ * pdcs_info_write - This function handles boot flag modifying.
+ * @entry: An allocated and populated subsytem struct. We don't use it tho.
+ * @buf: The input buffer to read from.
+ * @count: The number of bytes to be read.
+ * 
+ * We will call this function to change the current boot flags.
+ * We expect a precise syntax:
+ *	\"n n\" (n == 0 or 1) to toggle respectively AutoBoot and AutoSearch
+ *
+ * As of now there is no incentive on my side to provide more "knobs" to that
+ * interface, since modifying the rest of the data is pretty meaningless when
+ * the machine is running and for the expected use of that facility, such as
+ * PALO setting up the boot disk when installing a Linux distribution...
+ */
+static ssize_t
+pdcs_info_write(struct subsystem *entry, const char *buf, size_t count)
+{
+	struct pdcspath_entry *pathentry;
+	unsigned char flags;
+	char in[count+1], *temp;
+	char c;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	if (!entry || !buf || !count)
+		return -EINVAL;
+
+	/* We'll use a local copy of buf */
+	memset(in, 0, count+1);
+	strncpy(in, buf, count);
+
+	/* Current flags are stored in primary boot path entry */
+	pathentry = &pdcspath_entry_primary;
+	
+	/* Be nice to the existing flag record */
+	flags = pathentry->devpath.flags;
+	
+	DPRINTK("%s: flags before: 0x%X\n", __func__, flags);
+			
+	temp = in;
+	
+	while (*temp && isspace(*temp))
+		temp++;
+	
+	c = *temp++ - '0';
+	if ((c != 0) && (c != 1))
+		goto parse_error;
+	if (c == 0)
+		flags &= ~PF_AUTOBOOT;
+	else
+		flags |= PF_AUTOBOOT;
+	
+	if (*temp++ != ' ')
+		goto parse_error;
+	
+	c = *temp++ - '0';
+	if ((c != 0) && (c != 1))
+		goto parse_error;
+	if (c == 0)
+		flags &= ~PF_AUTOSEARCH;
+	else
+		flags |= PF_AUTOSEARCH;
+	
+	DPRINTK("%s: flags after: 0x%X\n", __func__, flags);
+		
+	/* So far so good, let's get in deep */
+	
+	/* Change the path entry flags first */
+	pathentry->devpath.flags = flags;
+		
+	/* Now, dive in. Write back to the hardware */
+	WARN_ON(pdcspath_store(pathentry));	/* this warn should *NEVER* happen */
+	
+	printk(KERN_INFO "PDC Stable Storage: changed flags to \"%s\"\n", buf);
+	
+	return count;
+
+parse_error:
+	printk(KERN_WARNING "%s: Parse error: expect \"n n\" (n == 0 or 1) for AB and AS\n", __func__);
+	return -EINVAL;
+}
+
+/* The last attribute (the 'root' one actually) with all remaining data. */
+static PDCS_ATTR(info, 0600, pdcs_info_read, pdcs_info_write);
+
+static struct subsys_attribute *pdcs_subsys_attrs[] = {
+	&pdcs_attr_info,
+	NULL,	/* maybe more in the future? */
+};
+
+static decl_subsys(paths, &ktype_pdcspath, NULL);
+static decl_subsys(pdc, NULL, NULL);
+
+/**
+ * pdcs_register_pathentries - Prepares path entries kobjects for sysfs usage.
+ * 
+ * It creates kobjects corresponding to each path entry with nice sysfs
+ * links to the real device. This is where the magic takes place: when
+ * registering the subsystem attributes during module init, each kobject hereby
+ * created will show in the sysfs tree as a folder containing files as defined
+ * by path_subsys_attr[].
+ */
+static inline int __init
+pdcs_register_pathentries(void)
+{
+	unsigned short i;
+	struct pdcspath_entry *entry;
+	
+	for (i = 0; (entry = pdcspath_entries[i]); i++) {
+		if (pdcspath_fetch(entry) < 0)
+			continue;
+
+		kobject_set_name(&entry->kobj, "%s", entry->name);
+		kobj_set_kset_s(entry, paths_subsys);
+		kobject_register(&entry->kobj);
+
+		if (!entry->dev)
+			continue;
+
+		/* Add a nice symlink to the real device */
+		sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");
+	}
+	
+	return 0;
+}
+
+/**
+ * pdcs_unregister_pathentries - Routine called when unregistering the module.
+ */
+static inline void __exit
+pdcs_unregister_pathentries(void)
+{
+	unsigned short i;
+	struct pdcspath_entry *entry;
+	
+	for (i = 0; (entry = pdcspath_entries[i]); i++)
+		if (entry->ready)
+			kobject_unregister(&entry->kobj);	
+}
+
+/*
+ * For now we register the pdc subsystem with the firmware subsystem
+ * and the paths subsystem with the pdc subsystem
+ */
+static int __init
+pdc_stable_init(void)
+{
+	struct subsys_attribute *attr;
+	int i, rc = 0, error = 0;
+
+	/* find the size of the stable storage */
+	if (pdc_stable_get_size(&pdcs_size) != PDC_OK) 
+		return -ENODEV;
+
+	printk(KERN_INFO "PDC Stable Storage facility v%s\n", PDCS_VERSION);
+
+	/* For now we'll register the pdc subsys within this driver */
+	if ((rc = firmware_register(&pdc_subsys)))
+		return rc;
+
+	/* Don't forget the info entry */
+	for (i = 0; (attr = pdcs_subsys_attrs[i]) && !error; i++)
+		if (attr->show)
+			error = subsys_create_file(&pdc_subsys, attr);
+	
+	/* register the paths subsys as a subsystem of pdc subsys */
+	kset_set_kset_s(&paths_subsys, pdc_subsys);
+	subsystem_register(&paths_subsys);
+
+	/* now we create all "files" for the paths subsys */
+	pdcs_register_pathentries();
+	
+	return 0;
+}
+
+static void __exit
+pdc_stable_exit(void)
+{
+	pdcs_unregister_pathentries();
+	subsystem_unregister(&paths_subsys);
+
+	firmware_unregister(&pdc_subsys);
+}
+
+
+module_init(pdc_stable_init);
+module_exit(pdc_stable_exit);
diff --git a/drivers/parisc/power.c b/drivers/parisc/power.c
new file mode 100644
index 0000000..ff75e92
--- /dev/null
+++ b/drivers/parisc/power.c
@@ -0,0 +1,278 @@
+/*
+ * linux/arch/parisc/kernel/power.c
+ * HP PARISC soft power switch support driver
+ *
+ * Copyright (c) 2001-2002 Helge Deller <deller@gmx.de>
+ * All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *
+ *
+ * 
+ *  HINT:
+ *  Support of the soft power switch button may be enabled or disabled at
+ *  runtime through the "/proc/sys/kernel/power" procfs entry.
+ */ 
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+
+#include <asm/pdc.h>
+#include <asm/io.h>
+#include <asm/led.h>
+#include <asm/uaccess.h>
+
+
+#ifdef DEBUG
+# define DPRINTK(x...) printk(x)
+#else
+# define DPRINTK(x...)
+#endif
+
+
+/* filename in /proc which can be used to enable/disable the power switch */
+#define SYSCTL_FILENAME		"sys/kernel/power"
+
+
+#define DIAG_CODE(code)		(0x14000000 + ((code)<<5))
+
+/* this will go to processor.h or any other place... */
+/* taken from PCXL ERS page 82 */
+#define MFCPU_X(rDiagReg, t_ch, t_th, code) \
+	(DIAG_CODE(code) + ((rDiagReg)<<21) + ((t_ch)<<16) + ((t_th)<<0) )
+	
+#define MTCPU(dr, gr)		MFCPU_X(dr, gr,  0, 0x12)       /* move value of gr to dr[dr] */
+#define MFCPU_C(dr, gr)		MFCPU_X(dr, gr,  0, 0x30)	/* for dr0 and dr8 only ! */
+#define MFCPU_T(dr, gr)		MFCPU_X(dr,  0, gr, 0xa0)	/* all dr except dr0 and dr8 */
+	
+#define __getDIAG(dr) ( { 			\
+        register unsigned long __res asm("r28");\
+	 __asm__ __volatile__ (			\
+		".word %1\n nop\n" : "=&r" (__res) : "i" (MFCPU_T(dr,28)) \
+	);					\
+	__res;					\
+} )
+
+
+static void deferred_poweroff(void *dummy)
+{
+	extern int cad_pid;	/* from kernel/sys.c */
+	if (kill_proc(cad_pid, SIGINT, 1)) {
+		/* just in case killing init process failed */
+		machine_power_off();
+	}
+}
+
+/*
+ * This function gets called from interrupt context.
+ * As it's called within an interrupt, it wouldn't sync if we don't
+ * use schedule_work().
+ */
+
+static DECLARE_WORK(poweroff_work, deferred_poweroff, NULL);
+
+static void poweroff(void)
+{
+	static int powering_off;
+
+	if (powering_off)
+		return;
+
+	powering_off++;
+	schedule_work(&poweroff_work);
+}
+
+
+/* local time-counter for shutdown */
+static int shutdown_timer;
+
+/* check, give feedback and start shutdown after one second */
+static void process_shutdown(void)
+{
+	if (shutdown_timer == 0)
+		DPRINTK(KERN_INFO "Shutdown requested...\n");
+
+	shutdown_timer++;
+	
+	/* wait until the button was pressed for 1 second */
+	if (shutdown_timer == HZ) {
+#if defined (DEBUG) || defined(CONFIG_CHASSIS_LCD_LED)
+		static char msg[] = "Shutting down...";
+#endif
+		DPRINTK(KERN_INFO "%s\n", msg);
+		lcd_print(msg);
+		poweroff();
+	}
+}
+
+
+/* main power switch tasklet struct (scheduled from time.c) */
+DECLARE_TASKLET_DISABLED(power_tasklet, NULL, 0);
+
+/* soft power switch enabled/disabled */
+int pwrsw_enabled = 1;
+
+/*
+ * On gecko style machines (e.g. 712/xx and 715/xx) 
+ * the power switch status is stored in Bit 0 ("the highest bit")
+ * of CPU diagnose register 25.
+ * 
+ */
+static void gecko_tasklet_func(unsigned long unused)
+{
+	if (!pwrsw_enabled)
+		return;
+
+	if (__getDIAG(25) & 0x80000000) {
+		/* power switch button not pressed or released again */
+		/* Warning: Some machines do never reset this DIAG flag! */
+		shutdown_timer = 0;
+	} else {
+		process_shutdown();
+	}
+}
+
+
+
+/*
+ * Check the power switch status which is read from the
+ * real I/O location at soft_power_reg.
+ * Bit 31 ("the lowest bit) is the status of the power switch.
+ */
+
+static void polling_tasklet_func(unsigned long soft_power_reg)
+{
+        unsigned long current_status;
+	
+	if (!pwrsw_enabled)
+		return;
+
+	current_status = gsc_readl(soft_power_reg);
+	if (current_status & 0x1) {
+		/* power switch button not pressed */
+		shutdown_timer = 0;
+	} else {
+		process_shutdown();
+	}
+}
+
+
+/*
+ * powerfail interruption handler (irq IRQ_FROM_REGION(CPU_IRQ_REGION)+2) 
+ */
+#if 0
+static void powerfail_interrupt(int code, void *x, struct pt_regs *regs)
+{
+	printk(KERN_CRIT "POWERFAIL INTERRUPTION !\n");
+	poweroff();
+}
+#endif
+
+
+
+
+/* parisc_panic_event() is called by the panic handler.
+ * As soon as a panic occurs, our tasklets above will not be
+ * executed any longer. This function then re-enables the 
+ * soft-power switch and allows the user to switch off the system
+ */
+static int parisc_panic_event(struct notifier_block *this,
+		unsigned long event, void *ptr)
+{
+	/* re-enable the soft-power switch */
+	pdc_soft_power_button(0);
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block parisc_panic_block = {
+	.notifier_call	= parisc_panic_event,
+	.priority	= INT_MAX,
+};
+
+
+static int __init power_init(void)
+{
+	unsigned long ret;
+	unsigned long soft_power_reg = 0;
+
+#if 0
+	request_irq( IRQ_FROM_REGION(CPU_IRQ_REGION)+2, &powerfail_interrupt,
+		0, "powerfail", NULL);
+#endif
+
+	/* enable the soft power switch if possible */
+	ret = pdc_soft_power_info(&soft_power_reg);
+	if (ret == PDC_OK)
+		ret = pdc_soft_power_button(1);
+	if (ret != PDC_OK)
+		soft_power_reg = -1UL;
+	
+	switch (soft_power_reg) {
+	case 0:		printk(KERN_INFO "Gecko-style soft power switch enabled.\n");
+			power_tasklet.func = gecko_tasklet_func;
+			break;
+			
+	case -1UL:	printk(KERN_INFO "Soft power switch support not available.\n");
+			return -ENODEV;
+	
+	default:	printk(KERN_INFO "Soft power switch enabled, polling @ 0x%08lx.\n",
+				soft_power_reg);
+			power_tasklet.data = soft_power_reg;
+			power_tasklet.func = polling_tasklet_func;
+	}
+
+	/* Register a call for panic conditions. */
+	notifier_chain_register(&panic_notifier_list, &parisc_panic_block);
+
+	tasklet_enable(&power_tasklet);
+
+	return 0;
+}
+
+static void __exit power_exit(void)
+{
+	if (!power_tasklet.func)
+		return;
+
+	tasklet_disable(&power_tasklet);
+	notifier_chain_unregister(&panic_notifier_list, &parisc_panic_block);
+	power_tasklet.func = NULL;
+	pdc_soft_power_button(0);
+}
+
+module_init(power_init);
+module_exit(power_exit);
+
+
+MODULE_AUTHOR("Helge Deller");
+MODULE_DESCRIPTION("Soft power switch driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c
new file mode 100644
index 0000000..82ea68b5
--- /dev/null
+++ b/drivers/parisc/sba_iommu.c
@@ -0,0 +1,2165 @@
+/*
+**  System Bus Adapter (SBA) I/O MMU manager
+**
+**	(c) Copyright 2000-2004 Grant Grundler <grundler @ parisc-linux x org>
+**	(c) Copyright 2004 Naresh Kumar Inna <knaresh at india x hp x com>
+**	(c) Copyright 2000-2004 Hewlett-Packard Company
+**
+**	Portions (c) 1999 Dave S. Miller (from sparc64 I/O MMU code)
+**
+**	This program is free software; you can redistribute it and/or modify
+**	it under the terms of the GNU General Public License as published by
+**      the Free Software Foundation; either version 2 of the License, or
+**      (at your option) any later version.
+**
+**
+** This module initializes the IOC (I/O Controller) found on B1000/C3000/
+** J5000/J7000/N-class/L-class machines and their successors.
+**
+** FIXME: add DMA hint support programming in both sba and lba modules.
+*/
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/dma.h>		/* for DMA_CHUNK_SIZE */
+
+#include <asm/hardware.h>	/* for register_parisc_driver() stuff */
+
+#include <linux/proc_fs.h>
+#include <asm/runway.h>		/* for proc_runway_root */
+#include <asm/pdc.h>		/* for PDC_MODEL_* */
+#include <asm/pdcpat.h>		/* for is_pdc_pat() */
+#include <asm/parisc-device.h>
+
+
+/* declared in arch/parisc/kernel/setup.c */
+extern struct proc_dir_entry * proc_mckinley_root;
+
+#define MODULE_NAME "SBA"
+
+#ifdef CONFIG_PROC_FS
+/* depends on proc fs support. But costs CPU performance */
+#undef SBA_COLLECT_STATS
+#endif
+
+/*
+** The number of debug flags is a clue - this code is fragile.
+** Don't even think about messing with it unless you have
+** plenty of 710's to sacrifice to the computer gods. :^)
+*/
+#undef DEBUG_SBA_INIT
+#undef DEBUG_SBA_RUN
+#undef DEBUG_SBA_RUN_SG
+#undef DEBUG_SBA_RESOURCE
+#undef ASSERT_PDIR_SANITY
+#undef DEBUG_LARGE_SG_ENTRIES
+#undef DEBUG_DMB_TRAP
+
+#ifdef DEBUG_SBA_INIT
+#define DBG_INIT(x...)	printk(x)
+#else
+#define DBG_INIT(x...)
+#endif
+
+#ifdef DEBUG_SBA_RUN
+#define DBG_RUN(x...)	printk(x)
+#else
+#define DBG_RUN(x...)
+#endif
+
+#ifdef DEBUG_SBA_RUN_SG
+#define DBG_RUN_SG(x...)	printk(x)
+#else
+#define DBG_RUN_SG(x...)
+#endif
+
+
+#ifdef DEBUG_SBA_RESOURCE
+#define DBG_RES(x...)	printk(x)
+#else
+#define DBG_RES(x...)
+#endif
+
+#if defined(__LP64__) && !defined(CONFIG_PDC_NARROW)
+/* "low end" PA8800 machines use ZX1 chipset */
+#define ZX1_SUPPORT
+#endif
+
+#define SBA_INLINE	__inline__
+
+
+/*
+** The number of pdir entries to "free" before issueing
+** a read to PCOM register to flush out PCOM writes.
+** Interacts with allocation granularity (ie 4 or 8 entries
+** allocated and free'd/purged at a time might make this
+** less interesting).
+*/
+#define DELAYED_RESOURCE_CNT	16
+
+#define DEFAULT_DMA_HINT_REG	0
+
+#define ASTRO_RUNWAY_PORT	0x582
+#define IKE_MERCED_PORT		0x803
+#define REO_MERCED_PORT		0x804
+#define REOG_MERCED_PORT	0x805
+#define PLUTO_MCKINLEY_PORT	0x880
+
+#define SBA_FUNC_ID	0x0000	/* function id */
+#define SBA_FCLASS	0x0008	/* function class, bist, header, rev... */
+
+#define IS_ASTRO(id)		((id)->hversion == ASTRO_RUNWAY_PORT)
+#define IS_IKE(id)		((id)->hversion == IKE_MERCED_PORT)
+#define IS_PLUTO(id)		((id)->hversion == PLUTO_MCKINLEY_PORT)
+
+#define SBA_FUNC_SIZE 4096   /* SBA configuration function reg set */
+
+#define ASTRO_IOC_OFFSET	(32 * SBA_FUNC_SIZE)
+#define PLUTO_IOC_OFFSET	(1 * SBA_FUNC_SIZE)
+/* Ike's IOC's occupy functions 2 and 3 */
+#define IKE_IOC_OFFSET(p)	((p+2) * SBA_FUNC_SIZE)
+
+#define IOC_CTRL          0x8	/* IOC_CTRL offset */
+#define IOC_CTRL_TC       (1 << 0) /* TOC Enable */
+#define IOC_CTRL_CE       (1 << 1) /* Coalesce Enable */
+#define IOC_CTRL_DE       (1 << 2) /* Dillon Enable */
+#define IOC_CTRL_RM       (1 << 8) /* Real Mode */
+#define IOC_CTRL_NC       (1 << 9) /* Non Coherent Mode */
+#define IOC_CTRL_D4       (1 << 11) /* Disable 4-byte coalescing */
+#define IOC_CTRL_DD       (1 << 13) /* Disable distr. LMMIO range coalescing */
+
+#define MAX_IOC		2	/* per Ike. Pluto/Astro only have 1. */
+
+#define ROPES_PER_IOC	8	/* per Ike half or Pluto/Astro */
+
+
+/*
+** Offsets into MBIB (Function 0 on Ike and hopefully Astro)
+** Firmware programs this stuff. Don't touch it.
+*/
+#define LMMIO_DIRECT0_BASE  0x300
+#define LMMIO_DIRECT0_MASK  0x308
+#define LMMIO_DIRECT0_ROUTE 0x310
+
+#define LMMIO_DIST_BASE  0x360
+#define LMMIO_DIST_MASK  0x368
+#define LMMIO_DIST_ROUTE 0x370
+
+#define IOS_DIST_BASE	0x390
+#define IOS_DIST_MASK	0x398
+#define IOS_DIST_ROUTE	0x3A0
+
+#define IOS_DIRECT_BASE	0x3C0
+#define IOS_DIRECT_MASK	0x3C8
+#define IOS_DIRECT_ROUTE 0x3D0
+
+/*
+** Offsets into I/O TLB (Function 2 and 3 on Ike)
+*/
+#define ROPE0_CTL	0x200  /* "regbus pci0" */
+#define ROPE1_CTL	0x208
+#define ROPE2_CTL	0x210
+#define ROPE3_CTL	0x218
+#define ROPE4_CTL	0x220
+#define ROPE5_CTL	0x228
+#define ROPE6_CTL	0x230
+#define ROPE7_CTL	0x238
+
+#define HF_ENABLE	0x40
+
+
+#define IOC_IBASE	0x300	/* IO TLB */
+#define IOC_IMASK	0x308
+#define IOC_PCOM	0x310
+#define IOC_TCNFG	0x318
+#define IOC_PDIR_BASE	0x320
+
+/* AGP GART driver looks for this */
+#define SBA_IOMMU_COOKIE    0x0000badbadc0ffeeUL
+
+
+/*
+** IOC supports 4/8/16/64KB page sizes (see TCNFG register)
+** It's safer (avoid memory corruption) to keep DMA page mappings
+** equivalently sized to VM PAGE_SIZE.
+**
+** We really can't avoid generating a new mapping for each
+** page since the Virtual Coherence Index has to be generated
+** and updated for each page.
+**
+** PAGE_SIZE could be greater than IOVP_SIZE. But not the inverse.
+*/
+#define IOVP_SIZE	PAGE_SIZE
+#define IOVP_SHIFT	PAGE_SHIFT
+#define IOVP_MASK	PAGE_MASK
+
+#define SBA_PERF_CFG	0x708	/* Performance Counter stuff */
+#define SBA_PERF_MASK1	0x718
+#define SBA_PERF_MASK2	0x730
+
+
+/*
+** Offsets into PCI Performance Counters (functions 12 and 13)
+** Controlled by PERF registers in function 2 & 3 respectively.
+*/
+#define SBA_PERF_CNT1	0x200
+#define SBA_PERF_CNT2	0x208
+#define SBA_PERF_CNT3	0x210
+
+
+struct ioc {
+	void __iomem	*ioc_hpa;	/* I/O MMU base address */
+	char		*res_map;	/* resource map, bit == pdir entry */
+	u64		*pdir_base;	/* physical base address */
+	unsigned long	ibase;	/* pdir IOV Space base - shared w/lba_pci */
+	unsigned long	imask;	/* pdir IOV Space mask - shared w/lba_pci */
+#ifdef ZX1_SUPPORT
+	unsigned long	iovp_mask;	/* help convert IOVA to IOVP */
+#endif
+	unsigned long	*res_hint;	/* next avail IOVP - circular search */
+	spinlock_t	res_lock;
+	unsigned int	res_bitshift;	/* from the LEFT! */
+	unsigned int	res_size;	/* size of resource map in bytes */
+#if SBA_HINT_SUPPORT
+/* FIXME : DMA HINTs not used */
+	unsigned long	hint_mask_pdir;	/* bits used for DMA hints */
+	unsigned int	hint_shift_pdir;
+#endif
+#if DELAYED_RESOURCE_CNT > 0
+	int saved_cnt;
+	struct sba_dma_pair {
+		dma_addr_t	iova;
+		size_t		size;
+	} saved[DELAYED_RESOURCE_CNT];
+#endif
+
+#ifdef SBA_COLLECT_STATS
+#define SBA_SEARCH_SAMPLE	0x100
+	unsigned long avg_search[SBA_SEARCH_SAMPLE];
+	unsigned long avg_idx;	/* current index into avg_search */
+	unsigned long used_pages;
+	unsigned long msingle_calls;
+	unsigned long msingle_pages;
+	unsigned long msg_calls;
+	unsigned long msg_pages;
+	unsigned long usingle_calls;
+	unsigned long usingle_pages;
+	unsigned long usg_calls;
+	unsigned long usg_pages;
+#endif
+
+	/* STUFF We don't need in performance path */
+	unsigned int	pdir_size;	/* in bytes, determined by IOV Space size */
+};
+
+struct sba_device {
+	struct sba_device	*next;	/* list of SBA's in system */
+	struct parisc_device	*dev;	/* dev found in bus walk */
+	struct parisc_device_id	*iodc;	/* data about dev from firmware */
+	const char 		*name;
+	void __iomem		*sba_hpa; /* base address */
+	spinlock_t		sba_lock;
+	unsigned int		flags;  /* state/functionality enabled */
+	unsigned int		hw_rev;  /* HW revision of chip */
+
+	struct resource		chip_resv; /* MMIO reserved for chip */
+	struct resource		iommu_resv; /* MMIO reserved for iommu */
+
+	unsigned int		num_ioc;  /* number of on-board IOC's */
+	struct ioc		ioc[MAX_IOC];
+};
+
+
+static struct sba_device *sba_list;
+
+static unsigned long ioc_needs_fdc = 0;
+
+/* global count of IOMMUs in the system */
+static unsigned int global_ioc_cnt = 0;
+
+/* PA8700 (Piranha 2.2) bug workaround */
+static unsigned long piranha_bad_128k = 0;
+
+/* Looks nice and keeps the compiler happy */
+#define SBA_DEV(d) ((struct sba_device *) (d))
+
+#if SBA_AGP_SUPPORT
+static int reserve_sba_gart = 1;
+#endif
+
+#define ROUNDUP(x,y) ((x + ((y)-1)) & ~((y)-1))
+
+
+/************************************
+** SBA register read and write support
+**
+** BE WARNED: register writes are posted.
+**  (ie follow writes which must reach HW with a read)
+**
+** Superdome (in particular, REO) allows only 64-bit CSR accesses.
+*/
+#define READ_REG32(addr)	 le32_to_cpu(__raw_readl(addr))
+#define READ_REG64(addr)	 le64_to_cpu(__raw_readq(addr))
+#define WRITE_REG32(val, addr) __raw_writel(cpu_to_le32(val), addr)
+#define WRITE_REG64(val, addr) __raw_writeq(cpu_to_le64(val), addr)
+
+#ifdef __LP64__
+#define READ_REG(addr)		READ_REG64(addr)
+#define WRITE_REG(value, addr)	WRITE_REG64(value, addr)
+#else
+#define READ_REG(addr)		READ_REG32(addr)
+#define WRITE_REG(value, addr)	WRITE_REG32(value, addr)
+#endif
+
+#ifdef DEBUG_SBA_INIT
+
+/* NOTE: When __LP64__ isn't defined, READ_REG64() is two 32-bit reads */
+
+/**
+ * sba_dump_ranges - debugging only - print ranges assigned to this IOA
+ * @hpa: base address of the sba
+ *
+ * Print the MMIO and IO Port address ranges forwarded by an Astro/Ike/RIO
+ * IO Adapter (aka Bus Converter).
+ */
+static void
+sba_dump_ranges(void __iomem *hpa)
+{
+	DBG_INIT("SBA at 0x%p\n", hpa);
+	DBG_INIT("IOS_DIST_BASE   : %Lx\n", READ_REG64(hpa+IOS_DIST_BASE));
+	DBG_INIT("IOS_DIST_MASK   : %Lx\n", READ_REG64(hpa+IOS_DIST_MASK));
+	DBG_INIT("IOS_DIST_ROUTE  : %Lx\n", READ_REG64(hpa+IOS_DIST_ROUTE));
+	DBG_INIT("\n");
+	DBG_INIT("IOS_DIRECT_BASE : %Lx\n", READ_REG64(hpa+IOS_DIRECT_BASE));
+	DBG_INIT("IOS_DIRECT_MASK : %Lx\n", READ_REG64(hpa+IOS_DIRECT_MASK));
+	DBG_INIT("IOS_DIRECT_ROUTE: %Lx\n", READ_REG64(hpa+IOS_DIRECT_ROUTE));
+}
+
+/**
+ * sba_dump_tlb - debugging only - print IOMMU operating parameters
+ * @hpa: base address of the IOMMU
+ *
+ * Print the size/location of the IO MMU PDIR.
+ */
+static void sba_dump_tlb(void __iomem *hpa)
+{
+	DBG_INIT("IO TLB at 0x%p\n", hpa);
+	DBG_INIT("IOC_IBASE    : 0x%Lx\n", READ_REG64(hpa+IOC_IBASE));
+	DBG_INIT("IOC_IMASK    : 0x%Lx\n", READ_REG64(hpa+IOC_IMASK));
+	DBG_INIT("IOC_TCNFG    : 0x%Lx\n", READ_REG64(hpa+IOC_TCNFG));
+	DBG_INIT("IOC_PDIR_BASE: 0x%Lx\n", READ_REG64(hpa+IOC_PDIR_BASE));
+	DBG_INIT("\n");
+}
+#else
+#define sba_dump_ranges(x)
+#define sba_dump_tlb(x)
+#endif
+
+
+#ifdef ASSERT_PDIR_SANITY
+
+/**
+ * sba_dump_pdir_entry - debugging only - print one IOMMU PDIR entry
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @msg: text to print ont the output line.
+ * @pide: pdir index.
+ *
+ * Print one entry of the IO MMU PDIR in human readable form.
+ */
+static void
+sba_dump_pdir_entry(struct ioc *ioc, char *msg, uint pide)
+{
+	/* start printing from lowest pde in rval */
+	u64 *ptr = &(ioc->pdir_base[pide & (~0U * BITS_PER_LONG)]);
+	unsigned long *rptr = (unsigned long *) &(ioc->res_map[(pide >>3) & ~(sizeof(unsigned long) - 1)]);
+	uint rcnt;
+
+	printk(KERN_DEBUG "SBA: %s rp %p bit %d rval 0x%lx\n",
+		 msg,
+		 rptr, pide & (BITS_PER_LONG - 1), *rptr);
+
+	rcnt = 0;
+	while (rcnt < BITS_PER_LONG) {
+		printk(KERN_DEBUG "%s %2d %p %016Lx\n",
+			(rcnt == (pide & (BITS_PER_LONG - 1)))
+				? "    -->" : "       ",
+			rcnt, ptr, *ptr );
+		rcnt++;
+		ptr++;
+	}
+	printk(KERN_DEBUG "%s", msg);
+}
+
+
+/**
+ * sba_check_pdir - debugging only - consistency checker
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @msg: text to print ont the output line.
+ *
+ * Verify the resource map and pdir state is consistent
+ */
+static int
+sba_check_pdir(struct ioc *ioc, char *msg)
+{
+	u32 *rptr_end = (u32 *) &(ioc->res_map[ioc->res_size]);
+	u32 *rptr = (u32 *) ioc->res_map;	/* resource map ptr */
+	u64 *pptr = ioc->pdir_base;	/* pdir ptr */
+	uint pide = 0;
+
+	while (rptr < rptr_end) {
+		u32 rval = *rptr;
+		int rcnt = 32;	/* number of bits we might check */
+
+		while (rcnt) {
+			/* Get last byte and highest bit from that */
+			u32 pde = ((u32) (((char *)pptr)[7])) << 24;
+			if ((rval ^ pde) & 0x80000000)
+			{
+				/*
+				** BUMMER!  -- res_map != pdir --
+				** Dump rval and matching pdir entries
+				*/
+				sba_dump_pdir_entry(ioc, msg, pide);
+				return(1);
+			}
+			rcnt--;
+			rval <<= 1;	/* try the next bit */
+			pptr++;
+			pide++;
+		}
+		rptr++;	/* look at next word of res_map */
+	}
+	/* It'd be nice if we always got here :^) */
+	return 0;
+}
+
+
+/**
+ * sba_dump_sg - debugging only - print Scatter-Gather list
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @startsg: head of the SG list
+ * @nents: number of entries in SG list
+ *
+ * print the SG list so we can verify it's correct by hand.
+ */
+static void
+sba_dump_sg( struct ioc *ioc, struct scatterlist *startsg, int nents)
+{
+	while (nents-- > 0) {
+		printk(KERN_DEBUG " %d : %08lx/%05x %p/%05x\n",
+				nents,
+				(unsigned long) sg_dma_address(startsg),
+				sg_dma_len(startsg),
+				sg_virt_addr(startsg), startsg->length);
+		startsg++;
+	}
+}
+
+#endif /* ASSERT_PDIR_SANITY */
+
+
+
+
+/**************************************************************
+*
+*   I/O Pdir Resource Management
+*
+*   Bits set in the resource map are in use.
+*   Each bit can represent a number of pages.
+*   LSbs represent lower addresses (IOVA's).
+*
+***************************************************************/
+#define PAGES_PER_RANGE 1	/* could increase this to 4 or 8 if needed */
+
+/* Convert from IOVP to IOVA and vice versa. */
+
+#ifdef ZX1_SUPPORT
+/* Pluto (aka ZX1) boxes need to set or clear the ibase bits appropriately */
+#define SBA_IOVA(ioc,iovp,offset,hint_reg) ((ioc->ibase) | (iovp) | (offset))
+#define SBA_IOVP(ioc,iova) ((iova) & (ioc)->iovp_mask)
+#else
+/* only support Astro and ancestors. Saves a few cycles in key places */
+#define SBA_IOVA(ioc,iovp,offset,hint_reg) ((iovp) | (offset))
+#define SBA_IOVP(ioc,iova) (iova)
+#endif
+
+#define PDIR_INDEX(iovp)   ((iovp)>>IOVP_SHIFT)
+
+#define RESMAP_MASK(n)    (~0UL << (BITS_PER_LONG - (n)))
+#define RESMAP_IDX_MASK   (sizeof(unsigned long) - 1)
+
+
+/**
+ * sba_search_bitmap - find free space in IO PDIR resource bitmap
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @bits_wanted: number of entries we need.
+ *
+ * Find consecutive free bits in resource bitmap.
+ * Each bit represents one entry in the IO Pdir.
+ * Cool perf optimization: search for log2(size) bits at a time.
+ */
+static SBA_INLINE unsigned long
+sba_search_bitmap(struct ioc *ioc, unsigned long bits_wanted)
+{
+	unsigned long *res_ptr = ioc->res_hint;
+	unsigned long *res_end = (unsigned long *) &(ioc->res_map[ioc->res_size]);
+	unsigned long pide = ~0UL;
+
+	if (bits_wanted > (BITS_PER_LONG/2)) {
+		/* Search word at a time - no mask needed */
+		for(; res_ptr < res_end; ++res_ptr) {
+			if (*res_ptr == 0) {
+				*res_ptr = RESMAP_MASK(bits_wanted);
+				pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map);
+				pide <<= 3;	/* convert to bit address */
+				break;
+			}
+		}
+		/* point to the next word on next pass */
+		res_ptr++;
+		ioc->res_bitshift = 0;
+	} else {
+		/*
+		** Search the resource bit map on well-aligned values.
+		** "o" is the alignment.
+		** We need the alignment to invalidate I/O TLB using
+		** SBA HW features in the unmap path.
+		*/
+		unsigned long o = 1 << get_order(bits_wanted << PAGE_SHIFT);
+		uint bitshiftcnt = ROUNDUP(ioc->res_bitshift, o);
+		unsigned long mask;
+
+		if (bitshiftcnt >= BITS_PER_LONG) {
+			bitshiftcnt = 0;
+			res_ptr++;
+		}
+		mask = RESMAP_MASK(bits_wanted) >> bitshiftcnt;
+
+		DBG_RES("%s() o %ld %p", __FUNCTION__, o, res_ptr);
+		while(res_ptr < res_end)
+		{ 
+			DBG_RES("    %p %lx %lx\n", res_ptr, mask, *res_ptr);
+			WARN_ON(mask == 0);
+			if(((*res_ptr) & mask) == 0) {
+				*res_ptr |= mask;     /* mark resources busy! */
+				pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map);
+				pide <<= 3;	/* convert to bit address */
+				pide += bitshiftcnt;
+				break;
+			}
+			mask >>= o;
+			bitshiftcnt += o;
+			if (mask == 0) {
+				mask = RESMAP_MASK(bits_wanted);
+				bitshiftcnt=0;
+				res_ptr++;
+			}
+		}
+		/* look in the same word on the next pass */
+		ioc->res_bitshift = bitshiftcnt + bits_wanted;
+	}
+
+	/* wrapped ? */
+	if (res_end <= res_ptr) {
+		ioc->res_hint = (unsigned long *) ioc->res_map;
+		ioc->res_bitshift = 0;
+	} else {
+		ioc->res_hint = res_ptr;
+	}
+	return (pide);
+}
+
+
+/**
+ * sba_alloc_range - find free bits and mark them in IO PDIR resource bitmap
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @size: number of bytes to create a mapping for
+ *
+ * Given a size, find consecutive unmarked and then mark those bits in the
+ * resource bit map.
+ */
+static int
+sba_alloc_range(struct ioc *ioc, size_t size)
+{
+	unsigned int pages_needed = size >> IOVP_SHIFT;
+#ifdef SBA_COLLECT_STATS
+	unsigned long cr_start = mfctl(16);
+#endif
+	unsigned long pide;
+
+	pide = sba_search_bitmap(ioc, pages_needed);
+	if (pide >= (ioc->res_size << 3)) {
+		pide = sba_search_bitmap(ioc, pages_needed);
+		if (pide >= (ioc->res_size << 3))
+			panic("%s: I/O MMU @ %p is out of mapping resources\n",
+			      __FILE__, ioc->ioc_hpa);
+	}
+
+#ifdef ASSERT_PDIR_SANITY
+	/* verify the first enable bit is clear */
+	if(0x00 != ((u8 *) ioc->pdir_base)[pide*sizeof(u64) + 7]) {
+		sba_dump_pdir_entry(ioc, "sba_search_bitmap() botched it?", pide);
+	}
+#endif
+
+	DBG_RES("%s(%x) %d -> %lx hint %x/%x\n",
+		__FUNCTION__, size, pages_needed, pide,
+		(uint) ((unsigned long) ioc->res_hint - (unsigned long) ioc->res_map),
+		ioc->res_bitshift );
+
+#ifdef SBA_COLLECT_STATS
+	{
+		unsigned long cr_end = mfctl(16);
+		unsigned long tmp = cr_end - cr_start;
+		/* check for roll over */
+		cr_start = (cr_end < cr_start) ?  -(tmp) : (tmp);
+	}
+	ioc->avg_search[ioc->avg_idx++] = cr_start;
+	ioc->avg_idx &= SBA_SEARCH_SAMPLE - 1;
+
+	ioc->used_pages += pages_needed;
+#endif
+
+	return (pide);
+}
+
+
+/**
+ * sba_free_range - unmark bits in IO PDIR resource bitmap
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @iova: IO virtual address which was previously allocated.
+ * @size: number of bytes to create a mapping for
+ *
+ * clear bits in the ioc's resource map
+ */
+static SBA_INLINE void
+sba_free_range(struct ioc *ioc, dma_addr_t iova, size_t size)
+{
+	unsigned long iovp = SBA_IOVP(ioc, iova);
+	unsigned int pide = PDIR_INDEX(iovp);
+	unsigned int ridx = pide >> 3;	/* convert bit to byte address */
+	unsigned long *res_ptr = (unsigned long *) &((ioc)->res_map[ridx & ~RESMAP_IDX_MASK]);
+
+	int bits_not_wanted = size >> IOVP_SHIFT;
+
+	/* 3-bits "bit" address plus 2 (or 3) bits for "byte" == bit in word */
+	unsigned long m = RESMAP_MASK(bits_not_wanted) >> (pide & (BITS_PER_LONG - 1));
+
+	DBG_RES("%s( ,%x,%x) %x/%lx %x %p %lx\n",
+		__FUNCTION__, (uint) iova, size,
+		bits_not_wanted, m, pide, res_ptr, *res_ptr);
+
+#ifdef SBA_COLLECT_STATS
+	ioc->used_pages -= bits_not_wanted;
+#endif
+
+	*res_ptr &= ~m;
+}
+
+
+/**************************************************************
+*
+*   "Dynamic DMA Mapping" support (aka "Coherent I/O")
+*
+***************************************************************/
+
+#if SBA_HINT_SUPPORT
+#define SBA_DMA_HINT(ioc, val) ((val) << (ioc)->hint_shift_pdir)
+#endif
+
+typedef unsigned long space_t;
+#define KERNEL_SPACE 0
+
+/**
+ * sba_io_pdir_entry - fill in one IO PDIR entry
+ * @pdir_ptr:  pointer to IO PDIR entry
+ * @sid: process Space ID - currently only support KERNEL_SPACE
+ * @vba: Virtual CPU address of buffer to map
+ * @hint: DMA hint set to use for this mapping
+ *
+ * SBA Mapping Routine
+ *
+ * Given a virtual address (vba, arg2) and space id, (sid, arg1)
+ * sba_io_pdir_entry() loads the I/O PDIR entry pointed to by
+ * pdir_ptr (arg0). 
+ * Using the bass-ackwards HP bit numbering, Each IO Pdir entry
+ * for Astro/Ike looks like:
+ *
+ *
+ *  0                    19                                 51   55       63
+ * +-+---------------------+----------------------------------+----+--------+
+ * |V|        U            |            PPN[43:12]            | U  |   VI   |
+ * +-+---------------------+----------------------------------+----+--------+
+ *
+ * Pluto is basically identical, supports fewer physical address bits:
+ *
+ *  0                       23                              51   55       63
+ * +-+------------------------+-------------------------------+----+--------+
+ * |V|        U               |         PPN[39:12]            | U  |   VI   |
+ * +-+------------------------+-------------------------------+----+--------+
+ *
+ *  V  == Valid Bit  (Most Significant Bit is bit 0)
+ *  U  == Unused
+ * PPN == Physical Page Number
+ * VI  == Virtual Index (aka Coherent Index)
+ *
+ * LPA instruction output is put into PPN field.
+ * LCI (Load Coherence Index) instruction provides the "VI" bits.
+ *
+ * We pre-swap the bytes since PCX-W is Big Endian and the
+ * IOMMU uses little endian for the pdir.
+ */
+
+void SBA_INLINE
+sba_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba,
+		  unsigned long hint)
+{
+	u64 pa; /* physical address */
+	register unsigned ci; /* coherent index */
+
+	pa = virt_to_phys(vba);
+	pa &= IOVP_MASK;
+
+	mtsp(sid,1);
+	asm("lci 0(%%sr1, %1), %0" : "=r" (ci) : "r" (vba));
+	pa |= (ci >> 12) & 0xff;  /* move CI (8 bits) into lowest byte */
+
+	pa |= 0x8000000000000000ULL;	/* set "valid" bit */
+	*pdir_ptr = cpu_to_le64(pa);	/* swap and store into I/O Pdir */
+
+	/*
+	 * If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit set
+	 * (bit #61, big endian), we have to flush and sync every time
+	 * IO-PDIR is changed in Ike/Astro.
+	 */
+	if (ioc_needs_fdc) {
+		asm volatile("fdc 0(%%sr1,%0)\n\tsync" : : "r" (pdir_ptr));
+	}
+}
+
+
+/**
+ * sba_mark_invalid - invalidate one or more IO PDIR entries
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @iova:  IO Virtual Address mapped earlier
+ * @byte_cnt:  number of bytes this mapping covers.
+ *
+ * Marking the IO PDIR entry(ies) as Invalid and invalidate
+ * corresponding IO TLB entry. The Ike PCOM (Purge Command Register)
+ * is to purge stale entries in the IO TLB when unmapping entries.
+ *
+ * The PCOM register supports purging of multiple pages, with a minium
+ * of 1 page and a maximum of 2GB. Hardware requires the address be
+ * aligned to the size of the range being purged. The size of the range
+ * must be a power of 2. The "Cool perf optimization" in the
+ * allocation routine helps keep that true.
+ */
+static SBA_INLINE void
+sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt)
+{
+	u32 iovp = (u32) SBA_IOVP(ioc,iova);
+
+	/* Even though this is a big-endian machine, the entries
+	** in the iopdir are little endian. That's why we clear the byte
+	** at +7 instead of at +0.
+	*/
+	int off = PDIR_INDEX(iovp)*sizeof(u64)+7;
+
+#ifdef ASSERT_PDIR_SANITY
+	/* Assert first pdir entry is set */
+	if (0x80 != (((u8 *) ioc->pdir_base)[off])) {
+		sba_dump_pdir_entry(ioc,"sba_mark_invalid()", PDIR_INDEX(iovp));
+	}
+#endif
+
+	if (byte_cnt <= IOVP_SIZE)
+	{
+		iovp |= IOVP_SHIFT;     /* set "size" field for PCOM */
+
+		/*
+		** clear I/O PDIR entry "valid" bit
+		** Do NOT clear the rest - save it for debugging.
+		** We should only clear bits that have previously
+		** been enabled.
+		*/
+		((u8 *)(ioc->pdir_base))[off] = 0;
+	} else {
+		u32 t = get_order(byte_cnt) + PAGE_SHIFT;
+
+		iovp |= t;
+		do {
+			/* clear I/O Pdir entry "valid" bit first */
+			((u8 *)(ioc->pdir_base))[off] = 0;
+			off += sizeof(u64);
+			byte_cnt -= IOVP_SIZE;
+		} while (byte_cnt > 0);
+	}
+
+	WRITE_REG( SBA_IOVA(ioc, iovp, 0, 0), ioc->ioc_hpa+IOC_PCOM);
+}
+
+/**
+ * sba_dma_supported - PCI driver can query DMA support
+ * @dev: instance of PCI owned by the driver that's asking
+ * @mask:  number of address bits this PCI device can handle
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+static int sba_dma_supported( struct device *dev, u64 mask)
+{
+	struct ioc *ioc;
+	if (dev == NULL) {
+		printk(KERN_ERR MODULE_NAME ": EISA/ISA/et al not supported\n");
+		BUG();
+		return(0);
+	}
+
+	ioc = GET_IOC(dev);
+
+	/* check if mask is > than the largest IO Virt Address */
+
+	return((int) (mask >= (ioc->ibase +
+				(ioc->pdir_size / sizeof(u64) * IOVP_SIZE) )));
+}
+
+
+/**
+ * sba_map_single - map one buffer and return IOVA for DMA
+ * @dev: instance of PCI owned by the driver that's asking.
+ * @addr:  driver buffer to map.
+ * @size:  number of bytes to map in driver buffer.
+ * @direction:  R/W or both.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+static dma_addr_t
+sba_map_single(struct device *dev, void *addr, size_t size,
+	       enum dma_data_direction direction)
+{
+	struct ioc *ioc;
+	unsigned long flags; 
+	dma_addr_t iovp;
+	dma_addr_t offset;
+	u64 *pdir_start;
+	int pide;
+
+	ioc = GET_IOC(dev);
+
+	/* save offset bits */
+	offset = ((dma_addr_t) (long) addr) & ~IOVP_MASK;
+
+	/* round up to nearest IOVP_SIZE */
+	size = (size + offset + ~IOVP_MASK) & IOVP_MASK;
+
+	spin_lock_irqsave(&ioc->res_lock, flags);
+#ifdef ASSERT_PDIR_SANITY
+	sba_check_pdir(ioc,"Check before sba_map_single()");
+#endif
+
+#ifdef SBA_COLLECT_STATS
+	ioc->msingle_calls++;
+	ioc->msingle_pages += size >> IOVP_SHIFT;
+#endif
+	pide = sba_alloc_range(ioc, size);
+	iovp = (dma_addr_t) pide << IOVP_SHIFT;
+
+	DBG_RUN("%s() 0x%p -> 0x%lx\n",
+		__FUNCTION__, addr, (long) iovp | offset);
+
+	pdir_start = &(ioc->pdir_base[pide]);
+
+	while (size > 0) {
+		sba_io_pdir_entry(pdir_start, KERNEL_SPACE, (unsigned long) addr, 0);
+
+		DBG_RUN("	pdir 0x%p %02x%02x%02x%02x%02x%02x%02x%02x\n",
+			pdir_start,
+			(u8) (((u8 *) pdir_start)[7]),
+			(u8) (((u8 *) pdir_start)[6]),
+			(u8) (((u8 *) pdir_start)[5]),
+			(u8) (((u8 *) pdir_start)[4]),
+			(u8) (((u8 *) pdir_start)[3]),
+			(u8) (((u8 *) pdir_start)[2]),
+			(u8) (((u8 *) pdir_start)[1]),
+			(u8) (((u8 *) pdir_start)[0])
+			);
+
+		addr += IOVP_SIZE;
+		size -= IOVP_SIZE;
+		pdir_start++;
+	}
+	/* form complete address */
+#ifdef ASSERT_PDIR_SANITY
+	sba_check_pdir(ioc,"Check after sba_map_single()");
+#endif
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+	return SBA_IOVA(ioc, iovp, offset, DEFAULT_DMA_HINT_REG);
+}
+
+
+/**
+ * sba_unmap_single - unmap one IOVA and free resources
+ * @dev: instance of PCI owned by the driver that's asking.
+ * @iova:  IOVA of driver buffer previously mapped.
+ * @size:  number of bytes mapped in driver buffer.
+ * @direction:  R/W or both.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+static void
+sba_unmap_single(struct device *dev, dma_addr_t iova, size_t size,
+		 enum dma_data_direction direction)
+{
+	struct ioc *ioc;
+#if DELAYED_RESOURCE_CNT > 0
+	struct sba_dma_pair *d;
+#endif
+	unsigned long flags; 
+	dma_addr_t offset;
+
+	DBG_RUN("%s() iovp 0x%lx/%x\n", __FUNCTION__, (long) iova, size);
+
+	ioc = GET_IOC(dev);
+	offset = iova & ~IOVP_MASK;
+	iova ^= offset;        /* clear offset bits */
+	size += offset;
+	size = ROUNDUP(size, IOVP_SIZE);
+
+	spin_lock_irqsave(&ioc->res_lock, flags);
+
+#ifdef SBA_COLLECT_STATS
+	ioc->usingle_calls++;
+	ioc->usingle_pages += size >> IOVP_SHIFT;
+#endif
+
+	sba_mark_invalid(ioc, iova, size);
+
+#if DELAYED_RESOURCE_CNT > 0
+	/* Delaying when we re-use a IO Pdir entry reduces the number
+	 * of MMIO reads needed to flush writes to the PCOM register.
+	 */
+	d = &(ioc->saved[ioc->saved_cnt]);
+	d->iova = iova;
+	d->size = size;
+	if (++(ioc->saved_cnt) >= DELAYED_RESOURCE_CNT) {
+		int cnt = ioc->saved_cnt;
+		while (cnt--) {
+			sba_free_range(ioc, d->iova, d->size);
+			d--;
+		}
+		ioc->saved_cnt = 0;
+		READ_REG(ioc->ioc_hpa+IOC_PCOM);	/* flush purges */
+	}
+#else /* DELAYED_RESOURCE_CNT == 0 */
+	sba_free_range(ioc, iova, size);
+	READ_REG(ioc->ioc_hpa+IOC_PCOM);	/* flush purges */
+#endif /* DELAYED_RESOURCE_CNT == 0 */
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+
+	/* XXX REVISIT for 2.5 Linux - need syncdma for zero-copy support.
+	** For Astro based systems this isn't a big deal WRT performance.
+	** As long as 2.4 kernels copyin/copyout data from/to userspace,
+	** we don't need the syncdma. The issue here is I/O MMU cachelines
+	** are *not* coherent in all cases.  May be hwrev dependent.
+	** Need to investigate more.
+	asm volatile("syncdma");	
+	*/
+}
+
+
+/**
+ * sba_alloc_consistent - allocate/map shared mem for DMA
+ * @hwdev: instance of PCI owned by the driver that's asking.
+ * @size:  number of bytes mapped in driver buffer.
+ * @dma_handle:  IOVA of new buffer.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+static void *sba_alloc_consistent(struct device *hwdev, size_t size,
+					dma_addr_t *dma_handle, int gfp)
+{
+	void *ret;
+
+	if (!hwdev) {
+		/* only support PCI */
+		*dma_handle = 0;
+		return 0;
+	}
+
+        ret = (void *) __get_free_pages(gfp, get_order(size));
+
+	if (ret) {
+		memset(ret, 0, size);
+		*dma_handle = sba_map_single(hwdev, ret, size, 0);
+	}
+
+	return ret;
+}
+
+
+/**
+ * sba_free_consistent - free/unmap shared mem for DMA
+ * @hwdev: instance of PCI owned by the driver that's asking.
+ * @size:  number of bytes mapped in driver buffer.
+ * @vaddr:  virtual address IOVA of "consistent" buffer.
+ * @dma_handler:  IO virtual address of "consistent" buffer.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+static void
+sba_free_consistent(struct device *hwdev, size_t size, void *vaddr,
+		    dma_addr_t dma_handle)
+{
+	sba_unmap_single(hwdev, dma_handle, size, 0);
+	free_pages((unsigned long) vaddr, get_order(size));
+}
+
+
+/*
+** Since 0 is a valid pdir_base index value, can't use that
+** to determine if a value is valid or not. Use a flag to indicate
+** the SG list entry contains a valid pdir index.
+*/
+#define PIDE_FLAG 0x80000000UL
+
+#ifdef SBA_COLLECT_STATS
+#define IOMMU_MAP_STATS
+#endif
+#include "iommu-helpers.h"
+
+#ifdef DEBUG_LARGE_SG_ENTRIES
+int dump_run_sg = 0;
+#endif
+
+
+/**
+ * sba_map_sg - map Scatter/Gather list
+ * @dev: instance of PCI owned by the driver that's asking.
+ * @sglist:  array of buffer/length pairs
+ * @nents:  number of entries in list
+ * @direction:  R/W or both.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+static int
+sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
+	   enum dma_data_direction direction)
+{
+	struct ioc *ioc;
+	int coalesced, filled = 0;
+	unsigned long flags;
+
+	DBG_RUN_SG("%s() START %d entries\n", __FUNCTION__, nents);
+
+	ioc = GET_IOC(dev);
+
+	/* Fast path single entry scatterlists. */
+	if (nents == 1) {
+		sg_dma_address(sglist) = sba_map_single(dev,
+						(void *)sg_virt_addr(sglist),
+						sglist->length, direction);
+		sg_dma_len(sglist)     = sglist->length;
+		return 1;
+	}
+
+	spin_lock_irqsave(&ioc->res_lock, flags);
+
+#ifdef ASSERT_PDIR_SANITY
+	if (sba_check_pdir(ioc,"Check before sba_map_sg()"))
+	{
+		sba_dump_sg(ioc, sglist, nents);
+		panic("Check before sba_map_sg()");
+	}
+#endif
+
+#ifdef SBA_COLLECT_STATS
+	ioc->msg_calls++;
+#endif
+
+	/*
+	** First coalesce the chunks and allocate I/O pdir space
+	**
+	** If this is one DMA stream, we can properly map using the
+	** correct virtual address associated with each DMA page.
+	** w/o this association, we wouldn't have coherent DMA!
+	** Access to the virtual address is what forces a two pass algorithm.
+	*/
+	coalesced = iommu_coalesce_chunks(ioc, sglist, nents, sba_alloc_range);
+
+	/*
+	** Program the I/O Pdir
+	**
+	** map the virtual addresses to the I/O Pdir
+	** o dma_address will contain the pdir index
+	** o dma_len will contain the number of bytes to map 
+	** o address contains the virtual address.
+	*/
+	filled = iommu_fill_pdir(ioc, sglist, nents, 0, sba_io_pdir_entry);
+
+#ifdef ASSERT_PDIR_SANITY
+	if (sba_check_pdir(ioc,"Check after sba_map_sg()"))
+	{
+		sba_dump_sg(ioc, sglist, nents);
+		panic("Check after sba_map_sg()\n");
+	}
+#endif
+
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+
+	DBG_RUN_SG("%s() DONE %d mappings\n", __FUNCTION__, filled);
+
+	return filled;
+}
+
+
+/**
+ * sba_unmap_sg - unmap Scatter/Gather list
+ * @dev: instance of PCI owned by the driver that's asking.
+ * @sglist:  array of buffer/length pairs
+ * @nents:  number of entries in list
+ * @direction:  R/W or both.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+static void 
+sba_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents,
+	     enum dma_data_direction direction)
+{
+	struct ioc *ioc;
+#ifdef ASSERT_PDIR_SANITY
+	unsigned long flags;
+#endif
+
+	DBG_RUN_SG("%s() START %d entries,  %p,%x\n",
+		__FUNCTION__, nents, sg_virt_addr(sglist), sglist->length);
+
+	ioc = GET_IOC(dev);
+
+#ifdef SBA_COLLECT_STATS
+	ioc->usg_calls++;
+#endif
+
+#ifdef ASSERT_PDIR_SANITY
+	spin_lock_irqsave(&ioc->res_lock, flags);
+	sba_check_pdir(ioc,"Check before sba_unmap_sg()");
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+#endif
+
+	while (sg_dma_len(sglist) && nents--) {
+
+		sba_unmap_single(dev, sg_dma_address(sglist), sg_dma_len(sglist), direction);
+#ifdef SBA_COLLECT_STATS
+		ioc->usg_pages += ((sg_dma_address(sglist) & ~IOVP_MASK) + sg_dma_len(sglist) + IOVP_SIZE - 1) >> PAGE_SHIFT;
+		ioc->usingle_calls--;	/* kluge since call is unmap_sg() */
+#endif
+		++sglist;
+	}
+
+	DBG_RUN_SG("%s() DONE (nents %d)\n", __FUNCTION__,  nents);
+
+#ifdef ASSERT_PDIR_SANITY
+	spin_lock_irqsave(&ioc->res_lock, flags);
+	sba_check_pdir(ioc,"Check after sba_unmap_sg()");
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+#endif
+
+}
+
+static struct hppa_dma_ops sba_ops = {
+	.dma_supported =	sba_dma_supported,
+	.alloc_consistent =	sba_alloc_consistent,
+	.alloc_noncoherent =	sba_alloc_consistent,
+	.free_consistent =	sba_free_consistent,
+	.map_single =		sba_map_single,
+	.unmap_single =		sba_unmap_single,
+	.map_sg =		sba_map_sg,
+	.unmap_sg =		sba_unmap_sg,
+	.dma_sync_single_for_cpu =	NULL,
+	.dma_sync_single_for_device =	NULL,
+	.dma_sync_sg_for_cpu =		NULL,
+	.dma_sync_sg_for_device =	NULL,
+};
+
+
+/**************************************************************************
+**
+**   SBA PAT PDC support
+**
+**   o call pdc_pat_cell_module()
+**   o store ranges in PCI "resource" structures
+**
+**************************************************************************/
+
+static void
+sba_get_pat_resources(struct sba_device *sba_dev)
+{
+#if 0
+/*
+** TODO/REVISIT/FIXME: support for directed ranges requires calls to
+**      PAT PDC to program the SBA/LBA directed range registers...this
+**      burden may fall on the LBA code since it directly supports the
+**      PCI subsystem. It's not clear yet. - ggg
+*/
+PAT_MOD(mod)->mod_info.mod_pages   = PAT_GET_MOD_PAGES(temp);
+	FIXME : ???
+PAT_MOD(mod)->mod_info.dvi         = PAT_GET_DVI(temp);
+	Tells where the dvi bits are located in the address.
+PAT_MOD(mod)->mod_info.ioc         = PAT_GET_IOC(temp);
+	FIXME : ???
+#endif
+}
+
+
+/**************************************************************
+*
+*   Initialization and claim
+*
+***************************************************************/
+#define PIRANHA_ADDR_MASK	0x00160000UL /* bit 17,18,20 */
+#define PIRANHA_ADDR_VAL	0x00060000UL /* bit 17,18 on */
+static void *
+sba_alloc_pdir(unsigned int pdir_size)
+{
+        unsigned long pdir_base;
+	unsigned long pdir_order = get_order(pdir_size);
+
+	pdir_base = __get_free_pages(GFP_KERNEL, pdir_order);
+	if (NULL == (void *) pdir_base)
+		panic("sba_ioc_init() could not allocate I/O Page Table\n");
+
+	/* If this is not PA8700 (PCX-W2)
+	**	OR newer than ver 2.2
+	**	OR in a system that doesn't need VINDEX bits from SBA,
+	**
+	** then we aren't exposed to the HW bug.
+	*/
+	if ( ((boot_cpu_data.pdc.cpuid >> 5) & 0x7f) != 0x13
+			|| (boot_cpu_data.pdc.versions > 0x202)
+			|| (boot_cpu_data.pdc.capabilities & 0x08L) )
+		return (void *) pdir_base;
+
+	/*
+	 * PA8700 (PCX-W2, aka piranha) silent data corruption fix
+	 *
+	 * An interaction between PA8700 CPU (Ver 2.2 or older) and
+	 * Ike/Astro can cause silent data corruption. This is only
+	 * a problem if the I/O PDIR is located in memory such that
+	 * (little-endian)  bits 17 and 18 are on and bit 20 is off.
+	 *
+	 * Since the max IO Pdir size is 2MB, by cleverly allocating the
+	 * right physical address, we can either avoid (IOPDIR <= 1MB)
+	 * or minimize (2MB IO Pdir) the problem if we restrict the
+	 * IO Pdir to a maximum size of 2MB-128K (1902K).
+	 *
+	 * Because we always allocate 2^N sized IO pdirs, either of the
+	 * "bad" regions will be the last 128K if at all. That's easy
+	 * to test for.
+	 * 
+	 */
+	if (pdir_order <= (19-12)) {
+		if (((virt_to_phys(pdir_base)+pdir_size-1) & PIRANHA_ADDR_MASK) == PIRANHA_ADDR_VAL) {
+			/* allocate a new one on 512k alignment */
+			unsigned long new_pdir = __get_free_pages(GFP_KERNEL, (19-12));
+			/* release original */
+			free_pages(pdir_base, pdir_order);
+
+			pdir_base = new_pdir;
+
+			/* release excess */
+			while (pdir_order < (19-12)) {
+				new_pdir += pdir_size;
+				free_pages(new_pdir, pdir_order);
+				pdir_order +=1;
+				pdir_size <<=1;
+			}
+		}
+	} else {
+		/*
+		** 1MB or 2MB Pdir
+		** Needs to be aligned on an "odd" 1MB boundary.
+		*/
+		unsigned long new_pdir = __get_free_pages(GFP_KERNEL, pdir_order+1); /* 2 or 4MB */
+
+		/* release original */
+		free_pages( pdir_base, pdir_order);
+
+		/* release first 1MB */
+		free_pages(new_pdir, 20-12);
+
+		pdir_base = new_pdir + 1024*1024;
+
+		if (pdir_order > (20-12)) {
+			/*
+			** 2MB Pdir.
+			**
+			** Flag tells init_bitmap() to mark bad 128k as used
+			** and to reduce the size by 128k.
+			*/
+			piranha_bad_128k = 1;
+
+			new_pdir += 3*1024*1024;
+			/* release last 1MB */
+			free_pages(new_pdir, 20-12);
+
+			/* release unusable 128KB */
+			free_pages(new_pdir - 128*1024 , 17-12);
+
+			pdir_size -= 128*1024;
+		}
+	}
+
+	memset((void *) pdir_base, 0, pdir_size);
+	return (void *) pdir_base;
+}
+
+/* setup Mercury or Elroy IBASE/IMASK registers. */
+static void setup_ibase_imask(struct parisc_device *sba, struct ioc *ioc, int ioc_num)
+{
+        /* lba_set_iregs() is in drivers/parisc/lba_pci.c */
+        extern void lba_set_iregs(struct parisc_device *, u32, u32);
+	struct device *dev;
+
+	list_for_each_entry(dev, &sba->dev.children, node) {
+		struct parisc_device *lba = to_parisc_device(dev);
+		int rope_num = (lba->hpa >> 13) & 0xf;
+		if (rope_num >> 3 == ioc_num)
+			lba_set_iregs(lba, ioc->ibase, ioc->imask);
+	}
+}
+
+static void
+sba_ioc_init_pluto(struct parisc_device *sba, struct ioc *ioc, int ioc_num)
+{
+	u32 iova_space_mask;
+	u32 iova_space_size;
+	int iov_order, tcnfg;
+#if SBA_AGP_SUPPORT
+	int agp_found = 0;
+#endif
+	/*
+	** Firmware programs the base and size of a "safe IOVA space"
+	** (one that doesn't overlap memory or LMMIO space) in the
+	** IBASE and IMASK registers.
+	*/
+	ioc->ibase = READ_REG(ioc->ioc_hpa + IOC_IBASE);
+	iova_space_size = ~(READ_REG(ioc->ioc_hpa + IOC_IMASK) & 0xFFFFFFFFUL) + 1;
+
+	if ((ioc->ibase < 0xfed00000UL) && ((ioc->ibase + iova_space_size) > 0xfee00000UL)) {
+		printk("WARNING: IOV space overlaps local config and interrupt message, truncating\n");
+		iova_space_size /= 2;
+	}
+
+	/*
+	** iov_order is always based on a 1GB IOVA space since we want to
+	** turn on the other half for AGP GART.
+	*/
+	iov_order = get_order(iova_space_size >> (IOVP_SHIFT - PAGE_SHIFT));
+	ioc->pdir_size = (iova_space_size / IOVP_SIZE) * sizeof(u64);
+
+	DBG_INIT("%s() hpa 0x%lx IOV %dMB (%d bits)\n",
+		__FUNCTION__, ioc->ioc_hpa, iova_space_size >> 20,
+		iov_order + PAGE_SHIFT);
+
+	ioc->pdir_base = (void *) __get_free_pages(GFP_KERNEL,
+						   get_order(ioc->pdir_size));
+	if (!ioc->pdir_base)
+		panic("Couldn't allocate I/O Page Table\n");
+
+	memset(ioc->pdir_base, 0, ioc->pdir_size);
+
+	DBG_INIT("%s() pdir %p size %x\n",
+			__FUNCTION__, ioc->pdir_base, ioc->pdir_size);
+
+#if SBA_HINT_SUPPORT
+	ioc->hint_shift_pdir = iov_order + PAGE_SHIFT;
+	ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT));
+
+	DBG_INIT("	hint_shift_pdir %x hint_mask_pdir %lx\n",
+		ioc->hint_shift_pdir, ioc->hint_mask_pdir);
+#endif
+
+	WARN_ON((((unsigned long) ioc->pdir_base) & PAGE_MASK) != (unsigned long) ioc->pdir_base);
+	WRITE_REG(virt_to_phys(ioc->pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE);
+
+	/* build IMASK for IOC and Elroy */
+	iova_space_mask =  0xffffffff;
+	iova_space_mask <<= (iov_order + PAGE_SHIFT);
+	ioc->imask = iova_space_mask;
+#ifdef ZX1_SUPPORT
+	ioc->iovp_mask = ~(iova_space_mask + PAGE_SIZE - 1);
+#endif
+	sba_dump_tlb(ioc->ioc_hpa);
+
+	setup_ibase_imask(sba, ioc, ioc_num);
+
+	WRITE_REG(ioc->imask, ioc->ioc_hpa + IOC_IMASK);
+
+#ifdef __LP64__
+	/*
+	** Setting the upper bits makes checking for bypass addresses
+	** a little faster later on.
+	*/
+	ioc->imask |= 0xFFFFFFFF00000000UL;
+#endif
+
+	/* Set I/O PDIR Page size to system page size */
+	switch (PAGE_SHIFT) {
+		case 12: tcnfg = 0; break;	/*  4K */
+		case 13: tcnfg = 1; break;	/*  8K */
+		case 14: tcnfg = 2; break;	/* 16K */
+		case 16: tcnfg = 3; break;	/* 64K */
+		default:
+			panic(__FILE__ "Unsupported system page size %d",
+				1 << PAGE_SHIFT);
+			break;
+	}
+	WRITE_REG(tcnfg, ioc->ioc_hpa + IOC_TCNFG);
+
+	/*
+	** Program the IOC's ibase and enable IOVA translation
+	** Bit zero == enable bit.
+	*/
+	WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa + IOC_IBASE);
+
+	/*
+	** Clear I/O TLB of any possible entries.
+	** (Yes. This is a bit paranoid...but so what)
+	*/
+	WRITE_REG(ioc->ibase | 31, ioc->ioc_hpa + IOC_PCOM);
+
+#if SBA_AGP_SUPPORT
+	/*
+	** If an AGP device is present, only use half of the IOV space
+	** for PCI DMA.  Unfortunately we can't know ahead of time
+	** whether GART support will actually be used, for now we
+	** can just key on any AGP device found in the system.
+	** We program the next pdir index after we stop w/ a key for
+	** the GART code to handshake on.
+	*/
+	device=NULL;
+	for (lba = sba->child; lba; lba = lba->sibling) {
+		if (IS_QUICKSILVER(lba))
+			break;
+	}
+
+	if (lba) {
+		DBG_INIT("%s: Reserving half of IOVA space for AGP GART support\n", __FUNCTION__);
+		ioc->pdir_size /= 2;
+		((u64 *)ioc->pdir_base)[PDIR_INDEX(iova_space_size/2)] = SBA_IOMMU_COOKIE;
+	} else {
+		DBG_INIT("%s: No GART needed - no AGP controller found\n", __FUNCTION__);
+	}
+#endif /* 0 */
+
+}
+
+static void
+sba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num)
+{
+	u32 iova_space_size, iova_space_mask;
+	unsigned int pdir_size, iov_order;
+
+	/*
+	** Determine IOVA Space size from memory size.
+	**
+	** Ideally, PCI drivers would register the maximum number
+	** of DMA they can have outstanding for each device they
+	** own.  Next best thing would be to guess how much DMA
+	** can be outstanding based on PCI Class/sub-class. Both
+	** methods still require some "extra" to support PCI
+	** Hot-Plug/Removal of PCI cards. (aka PCI OLARD).
+	**
+	** While we have 32-bits "IOVA" space, top two 2 bits are used
+	** for DMA hints - ergo only 30 bits max.
+	*/
+
+	iova_space_size = (u32) (num_physpages/global_ioc_cnt);
+
+	/* limit IOVA space size to 1MB-1GB */
+	if (iova_space_size < (1 << (20 - PAGE_SHIFT))) {
+		iova_space_size = 1 << (20 - PAGE_SHIFT);
+	}
+#ifdef __LP64__
+	else if (iova_space_size > (1 << (30 - PAGE_SHIFT))) {
+		iova_space_size = 1 << (30 - PAGE_SHIFT);
+	}
+#endif
+
+	/*
+	** iova space must be log2() in size.
+	** thus, pdir/res_map will also be log2().
+	** PIRANHA BUG: Exception is when IO Pdir is 2MB (gets reduced)
+	*/
+	iov_order = get_order(iova_space_size << PAGE_SHIFT);
+
+	/* iova_space_size is now bytes, not pages */
+	iova_space_size = 1 << (iov_order + PAGE_SHIFT);
+
+	ioc->pdir_size = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64);
+
+	DBG_INIT("%s() hpa 0x%lx mem %ldMB IOV %dMB (%d bits)\n",
+			__FUNCTION__,
+			ioc->ioc_hpa,
+			(unsigned long) num_physpages >> (20 - PAGE_SHIFT),
+			iova_space_size>>20,
+			iov_order + PAGE_SHIFT);
+
+	ioc->pdir_base = sba_alloc_pdir(pdir_size);
+
+	DBG_INIT("%s() pdir %p size %x\n",
+			__FUNCTION__, ioc->pdir_base, pdir_size);
+
+#if SBA_HINT_SUPPORT
+	/* FIXME : DMA HINTs not used */
+	ioc->hint_shift_pdir = iov_order + PAGE_SHIFT;
+	ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT));
+
+	DBG_INIT("	hint_shift_pdir %x hint_mask_pdir %lx\n",
+			ioc->hint_shift_pdir, ioc->hint_mask_pdir);
+#endif
+
+	WRITE_REG64(virt_to_phys(ioc->pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE);
+
+	/* build IMASK for IOC and Elroy */
+	iova_space_mask =  0xffffffff;
+	iova_space_mask <<= (iov_order + PAGE_SHIFT);
+
+	/*
+	** On C3000 w/512MB mem, HP-UX 10.20 reports:
+	**     ibase=0, imask=0xFE000000, size=0x2000000.
+	*/
+	ioc->ibase = 0;
+	ioc->imask = iova_space_mask;	/* save it */
+#ifdef ZX1_SUPPORT
+	ioc->iovp_mask = ~(iova_space_mask + PAGE_SIZE - 1);
+#endif
+
+	DBG_INIT("%s() IOV base 0x%lx mask 0x%0lx\n",
+		__FUNCTION__, ioc->ibase, ioc->imask);
+
+	/*
+	** FIXME: Hint registers are programmed with default hint
+	** values during boot, so hints should be sane even if we
+	** can't reprogram them the way drivers want.
+	*/
+
+	setup_ibase_imask(sba, ioc, ioc_num);
+
+	/*
+	** Program the IOC's ibase and enable IOVA translation
+	*/
+	WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa+IOC_IBASE);
+	WRITE_REG(ioc->imask, ioc->ioc_hpa+IOC_IMASK);
+
+	/* Set I/O PDIR Page size to 4K */
+	WRITE_REG(0, ioc->ioc_hpa+IOC_TCNFG);
+
+	/*
+	** Clear I/O TLB of any possible entries.
+	** (Yes. This is a bit paranoid...but so what)
+	*/
+	WRITE_REG(0 | 31, ioc->ioc_hpa+IOC_PCOM);
+
+	ioc->ibase = 0; /* used by SBA_IOVA and related macros */	
+
+	DBG_INIT("%s() DONE\n", __FUNCTION__);
+}
+
+
+
+/**************************************************************************
+**
+**   SBA initialization code (HW and SW)
+**
+**   o identify SBA chip itself
+**   o initialize SBA chip modes (HardFail)
+**   o initialize SBA chip modes (HardFail)
+**   o FIXME: initialize DMA hints for reasonable defaults
+**
+**************************************************************************/
+
+static void __iomem *ioc_remap(struct sba_device *sba_dev, int offset)
+{
+	return ioremap(sba_dev->dev->hpa + offset, SBA_FUNC_SIZE);
+}
+
+static void sba_hw_init(struct sba_device *sba_dev)
+{ 
+	int i;
+	int num_ioc;
+	u64 ioc_ctl;
+
+	if (!is_pdc_pat()) {
+		/* Shutdown the USB controller on Astro-based workstations.
+		** Once we reprogram the IOMMU, the next DMA performed by
+		** USB will HPMC the box. USB is only enabled if a
+		** keyboard is present and found.
+		**
+		** With serial console, j6k v5.0 firmware says:
+		**   mem_kbd hpa 0xfee003f8 sba 0x0 pad 0x0 cl_class 0x7
+		**
+		** FIXME: Using GFX+USB console at power up but direct
+		**	linux to serial console is still broken.
+		**	USB could generate DMA so we must reset USB.
+		**	The proper sequence would be:
+		**	o block console output
+		**	o reset USB device
+		**	o reprogram serial port
+		**	o unblock console output
+		*/
+		if (PAGE0->mem_kbd.cl_class == CL_KEYBD) {
+			pdc_io_reset_devices();
+		}
+
+	}
+
+
+#if 0
+printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa,
+	PAGE0->mem_boot.spa, PAGE0->mem_boot.pad, PAGE0->mem_boot.cl_class);
+
+	/*
+	** Need to deal with DMA from LAN.
+	**	Maybe use page zero boot device as a handle to talk
+	**	to PDC about which device to shutdown.
+	**
+	** Netbooting, j6k v5.0 firmware says:
+	** 	mem_boot hpa 0xf4008000 sba 0x0 pad 0x0 cl_class 0x1002
+	** ARGH! invalid class.
+	*/
+	if ((PAGE0->mem_boot.cl_class != CL_RANDOM)
+		&& (PAGE0->mem_boot.cl_class != CL_SEQU)) {
+			pdc_io_reset();
+	}
+#endif
+
+	if (!IS_PLUTO(sba_dev->iodc)) {
+		ioc_ctl = READ_REG(sba_dev->sba_hpa+IOC_CTRL);
+		DBG_INIT("%s() hpa 0x%lx ioc_ctl 0x%Lx ->",
+			__FUNCTION__, sba_dev->sba_hpa, ioc_ctl);
+		ioc_ctl &= ~(IOC_CTRL_RM | IOC_CTRL_NC | IOC_CTRL_CE);
+		ioc_ctl |= IOC_CTRL_DD | IOC_CTRL_D4 | IOC_CTRL_TC;
+			/* j6700 v1.6 firmware sets 0x294f */
+			/* A500 firmware sets 0x4d */
+
+		WRITE_REG(ioc_ctl, sba_dev->sba_hpa+IOC_CTRL);
+
+#ifdef DEBUG_SBA_INIT
+		ioc_ctl = READ_REG64(sba_dev->sba_hpa+IOC_CTRL);
+		DBG_INIT(" 0x%Lx\n", ioc_ctl);
+#endif
+	} /* if !PLUTO */
+
+	if (IS_ASTRO(sba_dev->iodc)) {
+		int err;
+		/* PAT_PDC (L-class) also reports the same goofy base */
+		sba_dev->ioc[0].ioc_hpa = ioc_remap(sba_dev, ASTRO_IOC_OFFSET);
+		num_ioc = 1;
+
+		sba_dev->chip_resv.name = "Astro Intr Ack";
+		sba_dev->chip_resv.start = PCI_F_EXTEND | 0xfef00000UL;
+		sba_dev->chip_resv.end   = PCI_F_EXTEND | (0xff000000UL - 1) ;
+		err = request_resource(&iomem_resource, &(sba_dev->chip_resv));
+		if (err < 0) {
+			BUG();
+		}
+
+	} else if (IS_PLUTO(sba_dev->iodc)) {
+		int err;
+
+		/* We use a negative value for IOC HPA so it gets 
+                 * corrected when we add it with IKE's IOC offset.
+		 * Doesnt look clean, but fewer code. 
+                 */
+		sba_dev->ioc[0].ioc_hpa = ioc_remap(sba_dev, PLUTO_IOC_OFFSET);
+		num_ioc = 1;
+
+		sba_dev->chip_resv.name = "Pluto Intr/PIOP/VGA";
+		sba_dev->chip_resv.start = PCI_F_EXTEND | 0xfee00000UL;
+		sba_dev->chip_resv.end   = PCI_F_EXTEND | (0xff200000UL - 1);
+		err = request_resource(&iomem_resource, &(sba_dev->chip_resv));
+		WARN_ON(err < 0);
+
+		sba_dev->iommu_resv.name = "IOVA Space";
+		sba_dev->iommu_resv.start = 0x40000000UL;
+		sba_dev->iommu_resv.end   = 0x50000000UL - 1;
+		err = request_resource(&iomem_resource, &(sba_dev->iommu_resv));
+		WARN_ON(err < 0);
+	} else {
+		/* IS_IKE (ie N-class, L3000, L1500) */
+		sba_dev->ioc[0].ioc_hpa = ioc_remap(sba_dev, IKE_IOC_OFFSET(0));
+		sba_dev->ioc[1].ioc_hpa = ioc_remap(sba_dev, IKE_IOC_OFFSET(1));
+		num_ioc = 2;
+
+		/* TODO - LOOKUP Ike/Stretch chipset mem map */
+	}
+	/* XXX: What about Reo? */
+
+	sba_dev->num_ioc = num_ioc;
+	for (i = 0; i < num_ioc; i++) {
+		/*
+		** Make sure the box crashes if we get any errors on a rope.
+		*/
+		WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE0_CTL);
+		WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE1_CTL);
+		WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE2_CTL);
+		WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE3_CTL);
+		WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE4_CTL);
+		WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE5_CTL);
+		WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE6_CTL);
+		WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE7_CTL);
+
+		/* flush out the writes */
+		READ_REG(sba_dev->ioc[i].ioc_hpa + ROPE7_CTL);
+
+		DBG_INIT("	ioc[%d] ROPE_CFG 0x%Lx  ROPE_DBG 0x%Lx\n",
+				i,
+				READ_REG(sba_dev->ioc[i].ioc_hpa + 0x40),
+				READ_REG(sba_dev->ioc[i].ioc_hpa + 0x50)
+			);
+		DBG_INIT("	STATUS_CONTROL 0x%Lx  FLUSH_CTRL 0x%Lx\n",
+				READ_REG(sba_dev->ioc[i].ioc_hpa + 0x108),
+				READ_REG(sba_dev->ioc[i].ioc_hpa + 0x400)
+			);
+
+		if (IS_PLUTO(sba_dev->iodc)) {
+			sba_ioc_init_pluto(sba_dev->dev, &(sba_dev->ioc[i]), i);
+		} else {
+			sba_ioc_init(sba_dev->dev, &(sba_dev->ioc[i]), i);
+		}
+	}
+}
+
+static void
+sba_common_init(struct sba_device *sba_dev)
+{
+	int i;
+
+	/* add this one to the head of the list (order doesn't matter)
+	** This will be useful for debugging - especially if we get coredumps
+	*/
+	sba_dev->next = sba_list;
+	sba_list = sba_dev;
+
+	for(i=0; i< sba_dev->num_ioc; i++) {
+		int res_size;
+#ifdef DEBUG_DMB_TRAP
+		extern void iterate_pages(unsigned long , unsigned long ,
+					  void (*)(pte_t * , unsigned long),
+					  unsigned long );
+		void set_data_memory_break(pte_t * , unsigned long);
+#endif
+		/* resource map size dictated by pdir_size */
+		res_size = sba_dev->ioc[i].pdir_size/sizeof(u64); /* entries */
+
+		/* Second part of PIRANHA BUG */
+		if (piranha_bad_128k) {
+			res_size -= (128*1024)/sizeof(u64);
+		}
+
+		res_size >>= 3;  /* convert bit count to byte count */
+		DBG_INIT("%s() res_size 0x%x\n",
+			__FUNCTION__, res_size);
+
+		sba_dev->ioc[i].res_size = res_size;
+		sba_dev->ioc[i].res_map = (char *) __get_free_pages(GFP_KERNEL, get_order(res_size));
+
+#ifdef DEBUG_DMB_TRAP
+		iterate_pages( sba_dev->ioc[i].res_map, res_size,
+				set_data_memory_break, 0);
+#endif
+
+		if (NULL == sba_dev->ioc[i].res_map)
+		{
+			panic("%s:%s() could not allocate resource map\n",
+			      __FILE__, __FUNCTION__ );
+		}
+
+		memset(sba_dev->ioc[i].res_map, 0, res_size);
+		/* next available IOVP - circular search */
+		sba_dev->ioc[i].res_hint = (unsigned long *)
+				&(sba_dev->ioc[i].res_map[L1_CACHE_BYTES]);
+
+#ifdef ASSERT_PDIR_SANITY
+		/* Mark first bit busy - ie no IOVA 0 */
+		sba_dev->ioc[i].res_map[0] = 0x80;
+		sba_dev->ioc[i].pdir_base[0] = 0xeeffc0addbba0080ULL;
+#endif
+
+		/* Third (and last) part of PIRANHA BUG */
+		if (piranha_bad_128k) {
+			/* region from +1408K to +1536 is un-usable. */
+
+			int idx_start = (1408*1024/sizeof(u64)) >> 3;
+			int idx_end   = (1536*1024/sizeof(u64)) >> 3;
+			long *p_start = (long *) &(sba_dev->ioc[i].res_map[idx_start]);
+			long *p_end   = (long *) &(sba_dev->ioc[i].res_map[idx_end]);
+
+			/* mark that part of the io pdir busy */
+			while (p_start < p_end)
+				*p_start++ = -1;
+				
+		}
+
+#ifdef DEBUG_DMB_TRAP
+		iterate_pages( sba_dev->ioc[i].res_map, res_size,
+				set_data_memory_break, 0);
+		iterate_pages( sba_dev->ioc[i].pdir_base, sba_dev->ioc[i].pdir_size,
+				set_data_memory_break, 0);
+#endif
+
+		DBG_INIT("%s() %d res_map %x %p\n",
+			__FUNCTION__, i, res_size, sba_dev->ioc[i].res_map);
+	}
+
+	spin_lock_init(&sba_dev->sba_lock);
+	ioc_needs_fdc = boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC;
+
+#ifdef DEBUG_SBA_INIT
+	/*
+	 * If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit set
+	 * (bit #61, big endian), we have to flush and sync every time
+	 * IO-PDIR is changed in Ike/Astro.
+	 */
+	if (boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC) {
+		printk(KERN_INFO MODULE_NAME " FDC/SYNC required.\n");
+	} else {
+		printk(KERN_INFO MODULE_NAME " IOC has cache coherent PDIR.\n");
+	}
+#endif
+}
+
+#ifdef CONFIG_PROC_FS
+static int sba_proc_info(char *buf, char **start, off_t offset, int len)
+{
+	struct sba_device *sba_dev = sba_list;
+	struct ioc *ioc = &sba_dev->ioc[0];	/* FIXME: Multi-IOC support! */
+	int total_pages = (int) (ioc->res_size << 3); /* 8 bits per byte */
+	unsigned long i;
+#ifdef SBA_COLLECT_STATS
+	unsigned long avg = 0, min, max;
+#endif
+
+	sprintf(buf, "%s rev %d.%d\n",
+		sba_dev->name,
+		(sba_dev->hw_rev & 0x7) + 1,
+		(sba_dev->hw_rev & 0x18) >> 3
+		);
+	sprintf(buf, "%sIO PDIR size    : %d bytes (%d entries)\n",
+		buf,
+		(int) ((ioc->res_size << 3) * sizeof(u64)), /* 8 bits/byte */
+		total_pages);
+
+	sprintf(buf, "%sResource bitmap : %d bytes (%d pages)\n", 
+		buf, ioc->res_size, ioc->res_size << 3);   /* 8 bits per byte */
+
+	sprintf(buf, "%sLMMIO_BASE/MASK/ROUTE %08x %08x %08x\n",
+		buf,
+		READ_REG32(sba_dev->sba_hpa + LMMIO_DIST_BASE),
+		READ_REG32(sba_dev->sba_hpa + LMMIO_DIST_MASK),
+		READ_REG32(sba_dev->sba_hpa + LMMIO_DIST_ROUTE)
+		);
+
+	for (i=0; i<4; i++)
+		sprintf(buf, "%sDIR%ld_BASE/MASK/ROUTE %08x %08x %08x\n",
+			buf, i,
+			READ_REG32(sba_dev->sba_hpa + LMMIO_DIRECT0_BASE  + i*0x18),
+			READ_REG32(sba_dev->sba_hpa + LMMIO_DIRECT0_MASK  + i*0x18),
+			READ_REG32(sba_dev->sba_hpa + LMMIO_DIRECT0_ROUTE + i*0x18)
+		);
+
+#ifdef SBA_COLLECT_STATS
+	sprintf(buf, "%sIO PDIR entries : %ld free  %ld used (%d%%)\n", buf,
+		total_pages - ioc->used_pages, ioc->used_pages,
+		(int) (ioc->used_pages * 100 / total_pages));
+
+	min = max = ioc->avg_search[0];
+	for (i = 0; i < SBA_SEARCH_SAMPLE; i++) {
+		avg += ioc->avg_search[i];
+		if (ioc->avg_search[i] > max) max = ioc->avg_search[i];
+		if (ioc->avg_search[i] < min) min = ioc->avg_search[i];
+	}
+	avg /= SBA_SEARCH_SAMPLE;
+	sprintf(buf, "%s  Bitmap search : %ld/%ld/%ld (min/avg/max CPU Cycles)\n",
+		buf, min, avg, max);
+
+	sprintf(buf, "%spci_map_single(): %12ld calls  %12ld pages (avg %d/1000)\n",
+		buf, ioc->msingle_calls, ioc->msingle_pages,
+		(int) ((ioc->msingle_pages * 1000)/ioc->msingle_calls));
+
+	/* KLUGE - unmap_sg calls unmap_single for each mapped page */
+	min = ioc->usingle_calls;
+	max = ioc->usingle_pages - ioc->usg_pages;
+	sprintf(buf, "%spci_unmap_single: %12ld calls  %12ld pages (avg %d/1000)\n",
+		buf, min, max,
+		(int) ((max * 1000)/min));
+
+	sprintf(buf, "%spci_map_sg()    : %12ld calls  %12ld pages (avg %d/1000)\n",
+		buf, ioc->msg_calls, ioc->msg_pages,
+		(int) ((ioc->msg_pages * 1000)/ioc->msg_calls));
+
+	sprintf(buf, "%spci_unmap_sg()  : %12ld calls  %12ld pages (avg %d/1000)\n",
+		buf, ioc->usg_calls, ioc->usg_pages,
+		(int) ((ioc->usg_pages * 1000)/ioc->usg_calls));
+#endif
+
+	return strlen(buf);
+}
+
+#if 0
+/* XXX too much output - exceeds 4k limit and needs to be re-written */
+static int
+sba_resource_map(char *buf, char **start, off_t offset, int len)
+{
+	struct sba_device *sba_dev = sba_list;
+	struct ioc *ioc = &sba_dev->ioc[0];	/* FIXME: Mutli-IOC suppoer! */
+	unsigned int *res_ptr = (unsigned int *)ioc->res_map;
+	int i;
+
+	buf[0] = '\0';
+	for(i = 0; i < (ioc->res_size / sizeof(unsigned int)); ++i, ++res_ptr) {
+		if ((i & 7) == 0)
+		    strcat(buf,"\n   ");
+		sprintf(buf, "%s %08x", buf, *res_ptr);
+	}
+	strcat(buf, "\n");
+
+	return strlen(buf);
+}
+#endif /* 0 */
+#endif /* CONFIG_PROC_FS */
+
+static struct parisc_device_id sba_tbl[] = {
+	{ HPHW_IOA, HVERSION_REV_ANY_ID, ASTRO_RUNWAY_PORT, 0xb },
+	{ HPHW_BCPORT, HVERSION_REV_ANY_ID, IKE_MERCED_PORT, 0xc },
+	{ HPHW_BCPORT, HVERSION_REV_ANY_ID, REO_MERCED_PORT, 0xc },
+	{ HPHW_BCPORT, HVERSION_REV_ANY_ID, REOG_MERCED_PORT, 0xc },
+	{ HPHW_IOA, HVERSION_REV_ANY_ID, PLUTO_MCKINLEY_PORT, 0xc },
+	{ 0, }
+};
+
+int sba_driver_callback(struct parisc_device *);
+
+static struct parisc_driver sba_driver = {
+	.name =		MODULE_NAME,
+	.id_table =	sba_tbl,
+	.probe =	sba_driver_callback,
+};
+
+/*
+** Determine if sba should claim this chip (return 0) or not (return 1).
+** If so, initialize the chip and tell other partners in crime they
+** have work to do.
+*/
+int
+sba_driver_callback(struct parisc_device *dev)
+{
+	struct sba_device *sba_dev;
+	u32 func_class;
+	int i;
+	char *version;
+	void __iomem *sba_addr = ioremap(dev->hpa, SBA_FUNC_SIZE);
+
+	sba_dump_ranges(sba_addr);
+
+	/* Read HW Rev First */
+	func_class = READ_REG(sba_addr + SBA_FCLASS);
+
+	if (IS_ASTRO(&dev->id)) {
+		unsigned long fclass;
+		static char astro_rev[]="Astro ?.?";
+
+		/* Astro is broken...Read HW Rev First */
+		fclass = READ_REG(sba_addr);
+
+		astro_rev[6] = '1' + (char) (fclass & 0x7);
+		astro_rev[8] = '0' + (char) ((fclass & 0x18) >> 3);
+		version = astro_rev;
+
+	} else if (IS_IKE(&dev->id)) {
+		static char ike_rev[] = "Ike rev ?";
+		ike_rev[8] = '0' + (char) (func_class & 0xff);
+		version = ike_rev;
+	} else if (IS_PLUTO(&dev->id)) {
+		static char pluto_rev[]="Pluto ?.?";
+		pluto_rev[6] = '0' + (char) ((func_class & 0xf0) >> 4); 
+		pluto_rev[8] = '0' + (char) (func_class & 0x0f); 
+		version = pluto_rev;
+	} else {
+		static char reo_rev[] = "REO rev ?";
+		reo_rev[8] = '0' + (char) (func_class & 0xff);
+		version = reo_rev;
+	}
+
+	if (!global_ioc_cnt) {
+		global_ioc_cnt = count_parisc_driver(&sba_driver);
+
+		/* Astro and Pluto have one IOC per SBA */
+		if ((!IS_ASTRO(&dev->id)) || (!IS_PLUTO(&dev->id)))
+			global_ioc_cnt *= 2;
+	}
+
+	printk(KERN_INFO "%s found %s at 0x%lx\n",
+		MODULE_NAME, version, dev->hpa);
+
+	sba_dev = kmalloc(sizeof(struct sba_device), GFP_KERNEL);
+	if (!sba_dev) {
+		printk(KERN_ERR MODULE_NAME " - couldn't alloc sba_device\n");
+		return -ENOMEM;
+	}
+
+	parisc_set_drvdata(dev, sba_dev);
+	memset(sba_dev, 0, sizeof(struct sba_device));
+
+	for(i=0; i<MAX_IOC; i++)
+		spin_lock_init(&(sba_dev->ioc[i].res_lock));
+
+	sba_dev->dev = dev;
+	sba_dev->hw_rev = func_class;
+	sba_dev->iodc = &dev->id;
+	sba_dev->name = dev->name;
+	sba_dev->sba_hpa = sba_addr;
+
+	sba_get_pat_resources(sba_dev);
+	sba_hw_init(sba_dev);
+	sba_common_init(sba_dev);
+
+	hppa_dma_ops = &sba_ops;
+
+#ifdef CONFIG_PROC_FS
+	if (IS_ASTRO(&dev->id)) {
+		create_proc_info_entry("Astro", 0, proc_runway_root, sba_proc_info);
+	} else if (IS_IKE(&dev->id)) {
+		create_proc_info_entry("Ike", 0, proc_runway_root, sba_proc_info);
+	} else if (IS_PLUTO(&dev->id)) {
+		create_proc_info_entry("Pluto", 0, proc_mckinley_root, sba_proc_info);
+	} else {
+		create_proc_info_entry("Reo", 0, proc_runway_root, sba_proc_info);
+	}
+#if 0
+	create_proc_info_entry("bitmap", 0, proc_runway_root, sba_resource_map);
+#endif
+#endif
+	parisc_vmerge_boundary = IOVP_SIZE;
+	parisc_vmerge_max_size = IOVP_SIZE * BITS_PER_LONG;
+	parisc_has_iommu();
+	return 0;
+}
+
+/*
+** One time initialization to let the world know the SBA was found.
+** This is the only routine which is NOT static.
+** Must be called exactly once before pci_init().
+*/
+void __init sba_init(void)
+{
+	register_parisc_driver(&sba_driver);
+}
+
+
+/**
+ * sba_get_iommu - Assign the iommu pointer for the pci bus controller.
+ * @dev: The parisc device.
+ *
+ * Returns the appropriate IOMMU data for the given parisc PCI controller.
+ * This is cached and used later for PCI DMA Mapping.
+ */
+void * sba_get_iommu(struct parisc_device *pci_hba)
+{
+	struct parisc_device *sba_dev = parisc_parent(pci_hba);
+	struct sba_device *sba = sba_dev->dev.driver_data;
+	char t = sba_dev->id.hw_type;
+	int iocnum = (pci_hba->hw_path >> 3);	/* rope # */
+
+	WARN_ON((t != HPHW_IOA) && (t != HPHW_BCPORT));
+
+	return &(sba->ioc[iocnum]);
+}
+
+
+/**
+ * sba_directed_lmmio - return first directed LMMIO range routed to rope
+ * @pa_dev: The parisc device.
+ * @r: resource PCI host controller wants start/end fields assigned.
+ *
+ * For the given parisc PCI controller, determine if any direct ranges
+ * are routed down the corresponding rope.
+ */
+void sba_directed_lmmio(struct parisc_device *pci_hba, struct resource *r)
+{
+	struct parisc_device *sba_dev = parisc_parent(pci_hba);
+	struct sba_device *sba = sba_dev->dev.driver_data;
+	char t = sba_dev->id.hw_type;
+	int i;
+	int rope = (pci_hba->hw_path & (ROPES_PER_IOC-1));  /* rope # */
+
+	if ((t!=HPHW_IOA) && (t!=HPHW_BCPORT))
+		BUG();
+
+	r->start = r->end = 0;
+
+	/* Astro has 4 directed ranges. Not sure about Ike/Pluto/et al */
+	for (i=0; i<4; i++) {
+		int base, size;
+		void __iomem *reg = sba->sba_hpa + i*0x18;
+
+		base = READ_REG32(reg + LMMIO_DIRECT0_BASE);
+		if ((base & 1) == 0)
+			continue;	/* not enabled */
+
+		size = READ_REG32(reg + LMMIO_DIRECT0_ROUTE);
+
+		if ((size & (ROPES_PER_IOC-1)) != rope)
+			continue;	/* directed down different rope */
+		
+		r->start = (base & ~1UL) | PCI_F_EXTEND;
+		size = ~ READ_REG32(reg + LMMIO_DIRECT0_MASK);
+		r->end = r->start + size;
+	}
+}
+
+
+/**
+ * sba_distributed_lmmio - return portion of distributed LMMIO range
+ * @pa_dev: The parisc device.
+ * @r: resource PCI host controller wants start/end fields assigned.
+ *
+ * For the given parisc PCI controller, return portion of distributed LMMIO
+ * range. The distributed LMMIO is always present and it's just a question
+ * of the base address and size of the range.
+ */
+void sba_distributed_lmmio(struct parisc_device *pci_hba, struct resource *r )
+{
+	struct parisc_device *sba_dev = parisc_parent(pci_hba);
+	struct sba_device *sba = sba_dev->dev.driver_data;
+	char t = sba_dev->id.hw_type;
+	int base, size;
+	int rope = (pci_hba->hw_path & (ROPES_PER_IOC-1));  /* rope # */
+
+	if ((t!=HPHW_IOA) && (t!=HPHW_BCPORT))
+		BUG();
+
+	r->start = r->end = 0;
+
+	base = READ_REG32(sba->sba_hpa + LMMIO_DIST_BASE);
+	if ((base & 1) == 0) {
+		BUG();	/* Gah! Distr Range wasn't enabled! */
+		return;
+	}
+
+	r->start = (base & ~1UL) | PCI_F_EXTEND;
+
+	size = (~READ_REG32(sba->sba_hpa + LMMIO_DIST_MASK)) / ROPES_PER_IOC;
+	r->start += rope * (size + 1);	/* adjust base for this rope */
+	r->end = r->start + size;
+}
diff --git a/drivers/parisc/superio.c b/drivers/parisc/superio.c
new file mode 100644
index 0000000..e0efed7
--- /dev/null
+++ b/drivers/parisc/superio.c
@@ -0,0 +1,508 @@
+/*      National Semiconductor NS87560UBD Super I/O controller used in
+ *      HP [BCJ]x000 workstations.
+ *
+ *      This chip is a horrid piece of engineering, and National
+ *      denies any knowledge of its existence. Thus no datasheet is
+ *      available off www.national.com. 
+ *
+ *	(C) Copyright 2000 Linuxcare, Inc.
+ * 	(C) Copyright 2000 Linuxcare Canada, Inc.
+ *	(C) Copyright 2000 Martin K. Petersen <mkp@linuxcare.com>
+ * 	(C) Copyright 2000 Alex deVries <alex@onefishtwo.ca>
+ *      (C) Copyright 2001 John Marvin <jsm fc hp com>
+ *      (C) Copyright 2003 Grant Grundler <grundler parisc-linux org>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation; either version 2 of
+ *	the License, or (at your option) any later version.  
+ *
+ *	The initial version of this is by Martin Peterson.  Alex deVries
+ *	has spent a bit of time trying to coax it into working.
+ *
+ *      Major changes to get basic interrupt infrastructure working to
+ *      hopefully be able to support all SuperIO devices. Currently
+ *      works with serial. -- John Marvin <jsm@fc.hp.com>
+ */
+
+
+/* NOTES:
+ * 
+ * Function 0 is an IDE controller. It is identical to a PC87415 IDE
+ * controller (and identifies itself as such).
+ *
+ * Function 1 is a "Legacy I/O" controller. Under this function is a
+ * whole mess of legacy I/O peripherals. Of course, HP hasn't enabled
+ * all the functionality in hardware, but the following is available:
+ *
+ *      Two 16550A compatible serial controllers
+ *      An IEEE 1284 compatible parallel port
+ *      A floppy disk controller
+ *
+ * Function 2 is a USB controller.
+ *
+ * We must be incredibly careful during initialization.  Since all
+ * interrupts are routed through function 1 (which is not allowed by
+ * the PCI spec), we need to program the PICs on the legacy I/O port
+ * *before* we attempt to set up IDE and USB.  @#$!&
+ *
+ * According to HP, devices are only enabled by firmware if they have
+ * a physical device connected.
+ *
+ * Configuration register bits:
+ *     0x5A: FDC, SP1, IDE1, SP2, IDE2, PAR, Reserved, P92
+ *     0x5B: RTC, 8259, 8254, DMA1, DMA2, KBC, P61, APM
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/serial.h>
+#include <linux/pci.h>
+#include <linux/parport.h>
+#include <linux/parport_pc.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/serial_core.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/superio.h>
+
+static struct superio_device sio_dev;
+
+
+#undef DEBUG_SUPERIO_INIT
+
+#ifdef DEBUG_SUPERIO_INIT
+#define DBG_INIT(x...)  printk(x)
+#else
+#define DBG_INIT(x...)
+#endif
+
+static irqreturn_t
+superio_interrupt(int parent_irq, void *devp, struct pt_regs *regs)
+{
+	u8 results;
+	u8 local_irq;
+
+	/* Poll the 8259 to see if there's an interrupt. */
+	outb (OCW3_POLL,IC_PIC1+0);
+
+	results = inb(IC_PIC1+0);
+
+	/*
+	 * Bit    7:	1 = active Interrupt; 0 = no Interrupt pending
+	 * Bits 6-3:	zero
+	 * Bits 2-0:	highest priority, active requesting interrupt ID (0-7)
+	 */
+	if ((results & 0x80) == 0) {
+		/* I suspect "spurious" interrupts are from unmasking an IRQ.
+		 * We don't know if an interrupt was/is pending and thus
+		 * just call the handler for that IRQ as if it were pending.
+		 */
+		return IRQ_NONE;
+	}
+
+	/* Check to see which device is interrupting */
+	local_irq = results & 0x0f;
+
+	if (local_irq == 2 || local_irq > 7) {
+		printk(KERN_ERR "SuperIO: slave interrupted!\n");
+		return IRQ_HANDLED;
+	}
+
+	if (local_irq == 7) {
+
+		/* Could be spurious. Check in service bits */
+
+		outb(OCW3_ISR,IC_PIC1+0);
+		results = inb(IC_PIC1+0);
+		if ((results & 0x80) == 0) { /* if ISR7 not set: spurious */
+			printk(KERN_WARNING "SuperIO: spurious interrupt!\n");
+			return IRQ_HANDLED;
+		}
+	}
+
+	/* Call the appropriate device's interrupt */
+	__do_IRQ(local_irq, regs);
+
+	/* set EOI - forces a new interrupt if a lower priority device
+	 * still needs service.
+	 */
+	outb((OCW2_SEOI|local_irq),IC_PIC1 + 0);
+	return IRQ_HANDLED;
+}
+
+/* Initialize Super I/O device */
+
+static void __devinit
+superio_init(struct superio_device *sio)
+{
+	struct pci_dev *pdev = sio->lio_pdev;
+	u16 word;
+
+        if (sio->suckyio_irq_enabled)                                       
+		return;
+
+	if (!pdev) BUG();
+	if (!sio->usb_pdev) BUG();
+
+	/* use the IRQ iosapic found for USB INT D... */
+	pdev->irq = sio->usb_pdev->irq;
+
+	/* ...then properly fixup the USB to point at suckyio PIC */
+	sio->usb_pdev->irq = superio_fixup_irq(sio->usb_pdev);
+
+	printk (KERN_INFO "SuperIO: Found NS87560 Legacy I/O device at %s (IRQ %i) \n",
+		pci_name(pdev),pdev->irq);
+
+	pci_read_config_dword (pdev, SIO_SP1BAR, &sio->sp1_base);
+	sio->sp1_base &= ~1;
+	printk (KERN_INFO "SuperIO: Serial port 1 at 0x%x\n", sio->sp1_base);
+
+	pci_read_config_dword (pdev, SIO_SP2BAR, &sio->sp2_base);
+	sio->sp2_base &= ~1;
+	printk (KERN_INFO "SuperIO: Serial port 2 at 0x%x\n", sio->sp2_base);
+
+	pci_read_config_dword (pdev, SIO_PPBAR, &sio->pp_base);
+	sio->pp_base &= ~1;
+	printk (KERN_INFO "SuperIO: Parallel port at 0x%x\n", sio->pp_base);
+
+	pci_read_config_dword (pdev, SIO_FDCBAR, &sio->fdc_base);
+	sio->fdc_base &= ~1;
+	printk (KERN_INFO "SuperIO: Floppy controller at 0x%x\n", sio->fdc_base);
+	pci_read_config_dword (pdev, SIO_ACPIBAR, &sio->acpi_base);
+	sio->acpi_base &= ~1;
+	printk (KERN_INFO "SuperIO: ACPI at 0x%x\n", sio->acpi_base);
+
+	request_region (IC_PIC1, 0x1f, "pic1");
+	request_region (IC_PIC2, 0x1f, "pic2");
+	request_region (sio->acpi_base, 0x1f, "acpi");
+
+	/* Enable the legacy I/O function */
+        pci_read_config_word (pdev, PCI_COMMAND, &word);
+	word |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_IO;
+	pci_write_config_word (pdev, PCI_COMMAND, word);
+
+	pci_set_master (pdev);
+	pci_enable_device(pdev);
+
+	/*
+	 * Next project is programming the onboard interrupt controllers.
+	 * PDC hasn't done this for us, since it's using polled I/O.
+	 *
+	 * XXX Use dword writes to avoid bugs in Elroy or Suckyio Config
+	 *     space access.  PCI is by nature a 32-bit bus and config
+	 *     space can be sensitive to that.
+	 */
+
+	/* 0x64 - 0x67 :
+		DMA Rtg 2
+		DMA Rtg 3
+		DMA Chan Ctl
+		TRIGGER_1    == 0x82   USB & IDE level triggered, rest to edge
+	*/
+	pci_write_config_dword (pdev, 0x64,         0x82000000U);
+
+	/* 0x68 - 0x6b :
+		TRIGGER_2    == 0x00   all edge triggered (not used)
+		CFG_IR_SER   == 0x43   SerPort1 = IRQ3, SerPort2 = IRQ4
+		CFG_IR_PF    == 0x65   ParPort  = IRQ5, FloppyCtlr = IRQ6
+		CFG_IR_IDE   == 0x07   IDE1 = IRQ7, reserved
+	*/
+	pci_write_config_dword (pdev, TRIGGER_2,    0x07654300U);
+
+	/* 0x6c - 0x6f :
+		CFG_IR_INTAB == 0x00
+		CFG_IR_INTCD == 0x10   USB = IRQ1
+		CFG_IR_PS2   == 0x00
+		CFG_IR_FXBUS == 0x00
+	*/
+	pci_write_config_dword (pdev, CFG_IR_INTAB, 0x00001000U);
+
+	/* 0x70 - 0x73 :
+		CFG_IR_USB   == 0x00  not used. USB is connected to INTD.
+		CFG_IR_ACPI  == 0x00  not used.
+		DMA Priority == 0x4c88  Power on default value. NFC.
+	*/
+	pci_write_config_dword (pdev, CFG_IR_USB, 0x4c880000U);
+
+	/* PIC1 Initialization Command Word register programming */
+	outb (0x11,IC_PIC1+0);	/* ICW1: ICW4 write req | ICW1 */
+	outb (0x00,IC_PIC1+1);	/* ICW2: interrupt vector table - not used */
+	outb (0x04,IC_PIC1+1);	/* ICW3: Cascade */
+	outb (0x01,IC_PIC1+1);	/* ICW4: x86 mode */
+
+	/* PIC1 Program Operational Control Words */
+	outb (0xff,IC_PIC1+1);	/* OCW1: Mask all interrupts */
+	outb (0xc2,IC_PIC1+0);  /* OCW2: priority (3-7,0-2) */
+
+	/* PIC2 Initialization Command Word register programming */
+	outb (0x11,IC_PIC2+0);	/* ICW1: ICW4 write req | ICW1 */
+	outb (0x00,IC_PIC2+1);	/* ICW2: N/A */
+	outb (0x02,IC_PIC2+1);	/* ICW3: Slave ID code */
+	outb (0x01,IC_PIC2+1);	/* ICW4: x86 mode */
+		
+	/* Program Operational Control Words */
+	outb (0xff,IC_PIC1+1);	/* OCW1: Mask all interrupts */
+	outb (0x68,IC_PIC1+0);	/* OCW3: OCW3 select | ESMM | SMM */
+
+	/* Write master mask reg */
+	outb (0xff,IC_PIC1+1);
+
+	/* Setup USB power regulation */
+	outb(1, sio->acpi_base + USB_REG_CR);
+	if (inb(sio->acpi_base + USB_REG_CR) & 1)
+		printk(KERN_INFO "SuperIO: USB regulator enabled\n");
+	else
+		printk(KERN_ERR "USB regulator not initialized!\n");
+
+	if (request_irq(pdev->irq, superio_interrupt, SA_INTERRUPT,
+			"SuperIO", (void *)sio)) {
+
+		printk(KERN_ERR "SuperIO: could not get irq\n");
+		BUG();
+		return;
+	}
+
+	sio->suckyio_irq_enabled = 1;
+}
+
+
+static void superio_disable_irq(unsigned int irq)
+{
+	u8 r8;
+
+	if ((irq < 1) || (irq == 2) || (irq > 7)) {
+		printk(KERN_ERR "SuperIO: Illegal irq number.\n");
+		BUG();
+		return;
+	}
+
+	/* Mask interrupt */
+
+	r8 = inb(IC_PIC1+1);
+	r8 |= (1 << irq);
+	outb (r8,IC_PIC1+1);
+}
+
+static void superio_enable_irq(unsigned int irq)
+{
+	u8 r8;
+
+	if ((irq < 1) || (irq == 2) || (irq > 7)) {
+		printk(KERN_ERR "SuperIO: Illegal irq number (%d).\n", irq);
+		BUG();
+		return;
+	}
+
+	/* Unmask interrupt */
+	r8 = inb(IC_PIC1+1);
+	r8 &= ~(1 << irq);
+	outb (r8,IC_PIC1+1);
+}
+
+static unsigned int superio_startup_irq(unsigned int irq)
+{
+	superio_enable_irq(irq);
+	return 0;
+}
+
+static struct hw_interrupt_type superio_interrupt_type = {
+	.typename =	"SuperIO",
+	.startup =	superio_startup_irq,
+	.shutdown =	superio_disable_irq,
+	.enable =	superio_enable_irq,
+	.disable =	superio_disable_irq,
+	.ack =		no_ack_irq,
+	.end =		no_end_irq,
+};
+
+#ifdef DEBUG_SUPERIO_INIT
+static unsigned short expected_device[3] = {
+	PCI_DEVICE_ID_NS_87415,
+	PCI_DEVICE_ID_NS_87560_LIO,
+	PCI_DEVICE_ID_NS_87560_USB
+};
+#endif
+
+int superio_fixup_irq(struct pci_dev *pcidev)
+{
+	int local_irq, i;
+
+#ifdef DEBUG_SUPERIO_INIT
+	int fn;
+	fn = PCI_FUNC(pcidev->devfn);
+
+	/* Verify the function number matches the expected device id. */
+	if (expected_device[fn] != pcidev->device) {
+		BUG();
+		return -1;
+	}
+	printk("superio_fixup_irq(%s) ven 0x%x dev 0x%x from %p\n",
+		pci_name(pcidev),
+		pcidev->vendor, pcidev->device,
+		__builtin_return_address(0));
+#endif
+
+	for (i = 0; i < 16; i++) {
+		irq_desc[i].handler = &superio_interrupt_type;
+	}
+
+	/*
+	 * We don't allocate a SuperIO irq for the legacy IO function,
+	 * since it is a "bridge". Instead, we will allocate irq's for
+	 * each legacy device as they are initialized.
+	 */
+
+	switch(pcidev->device) {
+	case PCI_DEVICE_ID_NS_87415:		/* Function 0 */
+		local_irq = IDE_IRQ;
+		break;
+	case PCI_DEVICE_ID_NS_87560_LIO:	/* Function 1 */
+		sio_dev.lio_pdev = pcidev;	/* save for superio_init() */
+		return -1;
+	case PCI_DEVICE_ID_NS_87560_USB:	/* Function 2 */
+		sio_dev.usb_pdev = pcidev;	/* save for superio_init() */
+		local_irq = USB_IRQ;
+		break;
+	default:
+		local_irq = -1;
+		BUG();
+		break;
+	}
+
+	return local_irq;
+}
+
+static struct uart_port serial[] = {
+	{
+		.iotype		= UPIO_PORT,
+		.line		= 0,
+		.type		= PORT_16550A,
+		.uartclk	= 115200*16,
+		.fifosize	= 16,
+	},
+	{
+		.iotype		= UPIO_PORT,
+		.line		= 1,
+		.type		= PORT_16550A,
+		.uartclk	= 115200*16,
+		.fifosize	= 16,
+	}
+};
+
+static void __devinit superio_serial_init(void)
+{
+#ifdef CONFIG_SERIAL_8250
+	int retval;
+        
+	serial[0].iobase = sio_dev.sp1_base;
+	serial[0].irq = SP1_IRQ;
+
+	retval = early_serial_setup(&serial[0]);
+	if (retval < 0) {
+		printk(KERN_WARNING "SuperIO: Register Serial #0 failed.\n");
+		return;
+	}
+
+	serial[1].iobase = sio_dev.sp2_base;
+	serial[1].irq = SP2_IRQ;
+	retval = early_serial_setup(&serial[1]);
+
+	if (retval < 0)
+		printk(KERN_WARNING "SuperIO: Register Serial #1 failed.\n");
+#endif /* CONFIG_SERIAL_8250 */
+}
+
+
+static void __devinit superio_parport_init(void)
+{
+#ifdef CONFIG_PARPORT_PC
+	if (!parport_pc_probe_port(sio_dev.pp_base,
+			0 /*base_hi*/,
+			PAR_IRQ, 
+			PARPORT_DMA_NONE /* dma */,
+			NULL /*struct pci_dev* */) )
+
+		printk(KERN_WARNING "SuperIO: Probing parallel port failed.\n");
+#endif	/* CONFIG_PARPORT_PC */
+}
+
+
+static void superio_fixup_pci(struct pci_dev *pdev)
+{
+	u8 prog;
+
+	pdev->class |= 0x5;
+	pci_write_config_byte(pdev, PCI_CLASS_PROG, pdev->class);
+
+	pci_read_config_byte(pdev, PCI_CLASS_PROG, &prog);
+	printk("PCI: Enabled native mode for NS87415 (pif=0x%x)\n", prog);
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415, superio_fixup_pci);
+
+
+static int __devinit superio_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+
+	/*
+	** superio_probe(00:0e.0) ven 0x100b dev 0x2 sv 0x0 sd 0x0 class 0x1018a
+	** superio_probe(00:0e.1) ven 0x100b dev 0xe sv 0x0 sd 0x0 class 0x68000
+	** superio_probe(00:0e.2) ven 0x100b dev 0x12 sv 0x0 sd 0x0 class 0xc0310
+	*/
+	DBG_INIT("superio_probe(%s) ven 0x%x dev 0x%x sv 0x%x sd 0x%x class 0x%x\n",
+		pci_name(dev),
+		dev->vendor, dev->device,
+		dev->subsystem_vendor, dev->subsystem_device,
+		dev->class);
+
+	superio_init(&sio_dev);
+
+	if (dev->device == PCI_DEVICE_ID_NS_87560_LIO) {	/* Function 1 */
+		superio_parport_init();
+		superio_serial_init();
+		/* REVISIT XXX : superio_fdc_init() ? */
+		return 0;
+	} else if (dev->device == PCI_DEVICE_ID_NS_87415) {	/* Function 0 */
+		DBG_INIT("superio_probe: ignoring IDE 87415\n");
+	} else if (dev->device == PCI_DEVICE_ID_NS_87560_USB) {	/* Function 2 */
+		DBG_INIT("superio_probe: ignoring USB OHCI controller\n");
+	} else {
+		DBG_INIT("superio_probe: WTF? Fire Extinguisher?\n");
+	}
+
+	/* Let appropriate other driver claim this device. */ 
+	return -ENODEV;
+}
+
+static struct pci_device_id superio_tbl[] = {
+	{ PCI_VENDOR_ID_NS, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ 0, }
+};
+
+static struct pci_driver superio_driver = {
+	.name =		"SuperIO",
+	.id_table =	superio_tbl,
+	.probe =	superio_probe,
+};
+
+static int __init superio_modinit(void)
+{
+	return pci_register_driver(&superio_driver);
+}
+
+static void __exit superio_exit(void)
+{
+	pci_unregister_driver(&superio_driver);
+}
+
+
+module_init(superio_modinit);
+module_exit(superio_exit);
diff --git a/drivers/parisc/wax.c b/drivers/parisc/wax.c
new file mode 100644
index 0000000..e547d7d
--- /dev/null
+++ b/drivers/parisc/wax.c
@@ -0,0 +1,140 @@
+/*
+ *	WAX Device Driver
+ *
+ *	(c) Copyright 2000 The Puffin Group Inc.
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *	by Helge Deller <deller@gmx.de>
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+
+#include "gsc.h"
+
+#define WAX_GSC_IRQ	7	/* Hardcoded Interrupt for GSC */
+
+static void wax_choose_irq(struct parisc_device *dev, void *ctrl)
+{
+	int irq;
+
+	switch (dev->id.sversion) {
+		case 0x73:	irq =  1; break; /* i8042 General */
+		case 0x8c:	irq =  6; break; /* Serial */
+		case 0x90:	irq = 10; break; /* EISA */
+		default:	return;		 /* Unknown */
+	}
+
+	gsc_asic_assign_irq(ctrl, irq, &dev->irq);
+
+	switch (dev->id.sversion) {
+		case 0x73:	irq =  2; break; /* i8042 High-priority */
+		case 0x90:	irq =  0; break; /* EISA NMI */
+		default:	return;		 /* No secondary IRQ */
+	}
+
+	gsc_asic_assign_irq(ctrl, irq, &dev->aux_irq);
+}
+
+static void __init
+wax_init_irq(struct gsc_asic *wax)
+{
+	unsigned long base = wax->hpa;
+
+	/* Wax-off */
+	gsc_writel(0x00000000, base+OFFSET_IMR);
+
+	/* clear pending interrupts */
+	gsc_readl(base+OFFSET_IRR);
+
+	/* We're not really convinced we want to reset the onboard
+         * devices. Firmware does it for us...
+	 */
+
+	/* Resets */
+//	gsc_writel(0xFFFFFFFF, base+0x1000); /* HIL */
+//	gsc_writel(0xFFFFFFFF, base+0x2000); /* RS232-B on Wax */
+}
+
+int __init
+wax_init_chip(struct parisc_device *dev)
+{
+	struct gsc_asic *wax;
+	struct parisc_device *parent;
+	struct gsc_irq gsc_irq;
+	int ret;
+
+	wax = kmalloc(sizeof(*wax), GFP_KERNEL);
+	if (!wax)
+		return -ENOMEM;
+
+	wax->name = "wax";
+	wax->hpa = dev->hpa;
+
+	wax->version = 0;   /* gsc_readb(wax->hpa+WAX_VER); */
+	printk(KERN_INFO "%s at 0x%lx found.\n", wax->name, wax->hpa);
+
+	/* Stop wax hissing for a bit */
+	wax_init_irq(wax);
+
+	/* the IRQ wax should use */
+	dev->irq = gsc_claim_irq(&gsc_irq, WAX_GSC_IRQ);
+	if (dev->irq < 0) {
+		printk(KERN_ERR "%s(): cannot get GSC irq\n",
+				__FUNCTION__);
+		kfree(wax);
+		return -EBUSY;
+	}
+
+	wax->eim = ((u32) gsc_irq.txn_addr) | gsc_irq.txn_data;
+
+	ret = request_irq(gsc_irq.irq, gsc_asic_intr, 0, "wax", wax);
+	if (ret < 0) {
+		kfree(wax);
+		return ret;
+	}
+
+	/* enable IRQ's for devices below WAX */
+	gsc_writel(wax->eim, wax->hpa + OFFSET_IAR);
+
+	/* Done init'ing, register this driver */
+	ret = gsc_common_setup(dev, wax);
+	if (ret) {
+		kfree(wax);
+		return ret;
+	}
+
+	gsc_fixup_irqs(dev, wax, wax_choose_irq);
+	/* On 715-class machines, Wax EISA is a sibling of Wax, not a child. */
+	parent = parisc_parent(dev);
+	if (parent->id.hw_type != HPHW_IOA) {
+		gsc_fixup_irqs(parent, wax, wax_choose_irq);
+	}
+
+	return ret;
+}
+
+static struct parisc_device_id wax_tbl[] = {
+  	{ HPHW_BA, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008e },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, wax_tbl);
+
+struct parisc_driver wax_driver = {
+	.name =		"wax",
+	.id_table =	wax_tbl,
+	.probe =	wax_init_chip,
+};