]> git.hungrycats.org Git - linux/commitdiff
Added new IBM PCI Hotplug controller driver.
authorGreg Kroah-Hartman <greg@kroah.com>
Wed, 27 Feb 2002 06:15:44 +0000 (22:15 -0800)
committerGreg Kroah-Hartman <greg@kroah.com>
Wed, 27 Feb 2002 06:15:44 +0000 (22:15 -0800)
Written by Irene Zubarev, Tong Yu, Jyoti Shah, Chuck Cole, and me.

drivers/hotplug/Config.help
drivers/hotplug/Config.in
drivers/hotplug/Makefile
drivers/hotplug/ibmphp.h [new file with mode: 0644]
drivers/hotplug/ibmphp_core.c [new file with mode: 0644]
drivers/hotplug/ibmphp_ebda.c [new file with mode: 0644]
drivers/hotplug/ibmphp_hpc.c [new file with mode: 0644]
drivers/hotplug/ibmphp_pci.c [new file with mode: 0644]
drivers/hotplug/ibmphp_res.c [new file with mode: 0644]

index b1eb31232123b6f9b011481ccc878537967ae568..81ba7879aa1cc225c796aaf96b10b840294be2ca 100644 (file)
@@ -29,3 +29,14 @@ CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM
 
   When in doubt, say N.
 
+CONFIG_HOTPLUG_PCI_IBM
+  Say Y here if you have a motherboard with a IBM PCI Hotplug
+  controller.
+
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called cpqphp.o. If you want to compile it
+  as a module, say M here and read <file:Documentation/modules.txt>.
+
+  When in doubt, say N.
+
index c47588c2932ac85ca94d0d9988ae559351d43d55..7dda8c299aaa88418942bab393b937fb8f2dd211 100644 (file)
@@ -4,9 +4,10 @@
 mainmenu_option next_comment
 comment 'PCI Hotplug Support'
 
-dep_tristate 'Support for PCI Hotplug (EXPERIMENTAL)' CONFIG_HOTPLUG_PCI $CONFIG_DDFS $CONFIG_EXPERIMENTAL
+dep_tristate 'Support for PCI Hotplug (EXPERIMENTAL)' CONFIG_HOTPLUG_PCI $CONFIG_PCI $CONFIG_EXPERIMENTAL 
 
-dep_tristate '  Compaq PCI Hotplug driver' CONFIG_HOTPLUG_PCI_COMPAQ $CONFIG_HOTPLUG_PCI
+dep_tristate '  Compaq PCI Hotplug driver' CONFIG_HOTPLUG_PCI_COMPAQ $CONFIG_HOTPLUG_PCI $CONFIG_X86
 dep_mbool '    Save configuration into NVRAM on Compaq servers' CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM $CONFIG_HOTPLUG_PCI_COMPAQ
+dep_tristate '  IBM PCI Hotplug driver' CONFIG_HOTPLUG_PCI_IBM $CONFIG_HOTPLUG_PCI $CONFIG_X86_IO_APIC $CONFIG_X86
 
 endmenu
index 63cd2165c076f9e537faf3c4bc8d7756bd5739a3..d3f1c0f5b345095e547213e419c90c2973330071 100644 (file)
@@ -4,12 +4,13 @@
 
 O_TARGET       := vmlinux-obj.o
 
-list-multi     := cpqphp.o pci_hotplug.o
+list-multi     := cpqphp.o pci_hotplug.o ibmphp.o
 
 export-objs    := pci_hotplug_core.o pci_hotplug_util.o
 
 obj-$(CONFIG_HOTPLUG_PCI)              += pci_hotplug.o
 obj-$(CONFIG_HOTPLUG_PCI_COMPAQ)       += cpqphp.o
+obj-$(CONFIG_HOTPLUG_PCI_IBM)          += ibmphp.o
 
 pci_hotplug-objs       :=      pci_hotplug_core.o      \
                                pci_hotplug_util.o
@@ -19,6 +20,12 @@ cpqphp-objs          :=      cpqphp_core.o   \
                                cpqphp_proc.o   \
                                cpqphp_pci.o
 
+ibmphp-objs            :=      ibmphp_core.o   \
+                               ibmphp_ebda.o   \
+                               ibmphp_pci.o    \
+                               ibmphp_res.o    \
+                               ibmphp_hpc.o
+
 ifeq ($(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM),y)
        cpqphp-objs += cpqphp_nvram.o
 endif
@@ -32,3 +39,6 @@ pci_hotplug.o: $(pci_hotplug-objs)
 cpqphp.o: $(cpqphp-objs)
        $(LD) -r -o $@ $(cpqphp-objs)
 
+ibmphp.o: $(ibmphp-objs)
+       $(LD) -r -o $@ $(ibmphp-objs)
+
diff --git a/drivers/hotplug/ibmphp.h b/drivers/hotplug/ibmphp.h
new file mode 100644 (file)
index 0000000..eb9d25e
--- /dev/null
@@ -0,0 +1,745 @@
+#ifndef __IBMPHP_H
+#define __IBMPHP_H
+
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation
+ *
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (c) 2001,2002 IBM Corp.
+ *
+ * All rights reserved.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <gregkh@us.ibm.com>
+ *
+ */
+
+#include "pci_hotplug.h"
+
+extern int ibmphp_debug;
+
+#if !defined(CONFIG_HOTPLUG_PCI_IBM_MODULE)
+       #define MY_NAME "ibmphpd"
+#else
+       #define MY_NAME THIS_MODULE->name
+#endif
+#define debug(fmt, arg...) do { if (ibmphp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
+
+
+/* EBDA stuff */
+
+/***********************************************************
+* SLOT CAPABILITY                                          *
+***********************************************************/
+
+#define EBDA_SLOT_133_MAX              0x20
+#define EBDA_SLOT_100_MAX              0x10
+#define EBDA_SLOT_66_MAX               0x02
+#define EBDA_SLOT_PCIX_CAP             0x08
+
+
+/************************************************************
+*  RESOURE TYPE                                             *
+************************************************************/
+
+#define EBDA_RSRC_TYPE_MASK            0x03
+#define EBDA_IO_RSRC_TYPE              0x00
+#define EBDA_MEM_RSRC_TYPE             0x01
+#define EBDA_PFM_RSRC_TYPE             0x03
+#define EBDA_RES_RSRC_TYPE             0x02
+
+
+/*************************************************************
+*  IO RESTRICTION TYPE                                       *
+*************************************************************/
+
+#define EBDA_IO_RESTRI_MASK            0x0c
+#define EBDA_NO_RESTRI                 0x00
+#define EBDA_AVO_VGA_ADDR              0x04
+#define EBDA_AVO_VGA_ADDR_AND_ALIA     0x08
+#define EBDA_AVO_ISA_ADDR              0x0c
+
+
+/**************************************************************
+*  DEVICE TYPE DEF                                            *
+**************************************************************/
+
+#define EBDA_DEV_TYPE_MASK             0x10
+#define EBDA_PCI_DEV                   0x10
+#define EBDA_NON_PCI_DEV               0x00
+
+
+/***************************************************************
+*  PRIMARY DEF DEFINITION                                      *
+***************************************************************/
+
+#define EBDA_PRI_DEF_MASK              0x20
+#define EBDA_PRI_PCI_BUS_INFO          0x20
+#define EBDA_NORM_DEV_RSRC_INFO                0x00
+
+
+//--------------------------------------------------------------
+// RIO TABLE DATA STRUCTURE
+//--------------------------------------------------------------
+
+struct rio_table_hdr {
+       u8 ver_num; 
+       u8 scal_count;
+       u8 riodev_count;
+       u16 offset;
+};
+
+//-------------------------------------------------------------
+// SCALABILITY DETAIL
+//-------------------------------------------------------------
+
+struct scal_detail {
+       u8 node_id;
+       u32 cbar;
+       u8 port0_node_connect;
+       u8 port0_port_connect;
+       u8 port1_node_connect;
+       u8 port1_port_connect;
+       u8 port2_node_connect;
+       u8 port2_port_connect;
+//     struct list_head scal_detail_list;
+};
+
+//--------------------------------------------------------------
+// RIO DETAIL 
+//--------------------------------------------------------------
+
+struct rio_detail {
+       u8 rio_node_id;
+       u32 bbar;
+       u8 rio_type;
+       u8 owner_id;
+       u8 port0_node_connect;
+       u8 port0_port_connect;
+       u8 port1_node_connect;
+       u8 port1_port_connect;
+       u8 first_slot_num;
+       u8 status;
+//     struct list_head rio_detail_list;
+};
+
+
+/****************************************************************
+*  HPC DESCRIPTOR NODE                                          *
+****************************************************************/
+
+struct ebda_hpc_list {
+       u8 format;
+       u16 num_ctlrs;
+       short phys_addr;
+//      struct list_head ebda_hpc_list;
+};
+
+/*****************************************************************
+*   IN HPC DATA STRUCTURE, THE ASSOCIATED SLOT AND BUS           *
+*   STRUCTURE                                                    *
+*****************************************************************/
+
+struct ebda_hpc_slot {
+       u8 slot_num;
+       u32 slot_bus_num;
+       u8 ctl_index;
+       u8 slot_cap;
+};
+
+struct ebda_hpc_bus {
+       u32 bus_num;
+/*
+       u8 slots_at_33_conv;
+       u8 slots_at_66_conv;
+       u8 slots_at_66_pcix;
+       u8 slots_at_100_pcix;
+       u8 slots_at_133_pcix;
+*/
+};
+
+
+/********************************************************************
+*   THREE TYPE OF HOT PLUG CONTROLER                                *
+********************************************************************/
+
+struct isa_ctlr_access {
+       u16 io_start;
+       u16 io_end;
+};
+
+struct pci_ctlr_access {
+       u8 bus;
+       u8 dev_fun;
+};
+
+struct wpeg_i2c_ctlr_access {
+       ulong wpegbbar;
+       u8 i2c_addr;
+};
+
+/*************************************************************************
+*   RSTC DESCRIPTOR NODE                                                 *
+*************************************************************************/
+
+struct ebda_rsrc_list {
+       u8 format;
+       u16 num_entries;
+       u16 phys_addr;
+       struct ebda_rsrc_list *next;
+};
+
+
+/***************************************************************************
+*   PCI RSRC NODE                                                          *
+***************************************************************************/
+
+struct ebda_pci_rsrc {
+       u8 rsrc_type;
+       u8 bus_num;
+       u8 dev_fun;
+       ulong start_addr;
+       ulong end_addr;
+       struct list_head ebda_pci_rsrc_list;
+};
+
+
+/***********************************************************
+* BUS_INFO DATE STRUCTURE                                  *
+***********************************************************/
+
+struct bus_info {
+       u8 slot_min;
+       u8 slot_max;
+       u8 slot_count;
+       u8 busno;
+       u8 current_speed;
+       u8 supported_speed;
+       u8 controller_id;
+       u8 supported_bus_mode;
+       u8 current_bus_mode;
+       u8 index;
+       struct list_head bus_info_list;
+};
+
+
+/***********************************************************
+* GLOBAL VARIABLES                                         *
+***********************************************************/
+extern struct list_head ibmphp_ebda_pci_rsrc_head;
+extern struct list_head ibmphp_slot_head;
+
+/***********************************************************
+* FUNCTION PROTOTYPES                                      *
+***********************************************************/
+
+extern void ibmphp_free_ebda_hpc_queue (void);
+extern int ibmphp_access_ebda (void);
+extern struct slot *ibmphp_get_slot_from_physical_num (u8);
+extern int ibmphp_get_total_hp_slots (void);
+extern void ibmphp_free_ibm_slot (struct slot *);
+extern void ibmphp_free_bus_info_queue (void);
+extern void ibmphp_free_ebda_pci_rsrc_queue (void);
+extern struct bus_info *ibmphp_find_same_bus_num (u32);
+extern int ibmphp_get_bus_index (u8);
+extern u16 ibmphp_get_total_controllers (void);
+
+/* passed parameters */
+#define MEM            0
+#define IO             1
+#define PFMEM          2
+
+/* bit masks */
+#define RESTYPE                0x03
+#define IOMASK         0x00    /* will need to take its complement */
+#define MMASK          0x01
+#define PFMASK         0x03
+#define PCIDEVMASK     0x10    /* we should always have PCI devices */
+#define PRIMARYBUSMASK 0x20
+
+/* pci specific defines */
+#define PCI_VENDOR_ID_NOTVALID         0xFFFF
+#define PCI_HEADER_TYPE_MULTIDEVICE    0x80
+#define PCI_HEADER_TYPE_MULTIBRIDGE    0x81
+
+#define LATENCY                0x64
+#define CACHE          64
+#define DEVICEENABLE   0x015F          /* CPQ has 0x0157 */
+
+#define IOBRIDGE       0x1000          /* 4k */
+#define MEMBRIDGE      0x100000        /* 1M */
+
+/* irqs */
+#define SCSI_IRQ       0x09
+#define LAN_IRQ                0x0A
+#define OTHER_IRQ      0x0B
+
+/* Data Structures */
+
+/* type is of the form x x xx xx
+ *                     | |  |  |_ 00 - I/O, 01 - Memory, 11 - PFMemory
+ *                     | |  - 00 - No Restrictions, 01 - Avoid VGA, 10 - Avoid
+ *                     | |    VGA and their aliases, 11 - Avoid ISA
+ *                     | - 1 - PCI device, 0 - non pci device
+ *                     - 1 - Primary PCI Bus Information (0 if Normal device)
+ * the IO restrictions [2:3] are only for primary buses
+ */
+
+
+/* we need this struct because there could be several resource blocks
+ * allocated per primary bus in the EBDA
+ */
+struct range_node {
+       int rangeno;
+       u32 start;
+       u32 end;
+       struct range_node *next;
+};
+
+struct bus_node {
+       u8 busno;
+       int noIORanges;
+       struct range_node *rangeIO;
+       int noMemRanges;
+       struct range_node *rangeMem;
+       int noPFMemRanges;
+       struct range_node *rangePFMem;
+       int needIOUpdate;
+       int needMemUpdate;
+       int needPFMemUpdate;
+       struct resource_node *firstIO;  /* first IO resource on the Bus */
+       struct resource_node *firstMem; /* first memory resource on the Bus */
+       struct resource_node *firstPFMem;       /* first prefetchable memory resource on the Bus */
+       struct resource_node *firstPFMemFromMem;        /* when run out of pfmem available, taking from Mem */
+       struct list_head bus_list;
+};
+
+struct resource_node {
+       int rangeno;
+       u8 busno;
+       u8 devfunc;
+       u32 start;
+       u32 end;
+       u32 len;
+       int type;               /* MEM, IO, PFMEM */
+       u8 fromMem;             /* this is to indicate that the range is from
+                                * from the Memory bucket rather than from PFMem */
+       struct resource_node *next;
+       struct resource_node *nextRange;        /* for the other mem range on bus */
+};
+
+struct res_needed {
+       u32 mem;
+       u32 pfmem;
+       u32 io;
+       u8 not_correct;         /* needed for return */
+       int devices[32];        /* for device numbers behind this bridge */
+};
+
+/* functions */
+
+extern int ibmphp_rsrc_init (void);
+extern int ibmphp_add_resource (struct resource_node *);
+extern int ibmphp_remove_resource (struct resource_node *);
+extern int ibmphp_find_resource (struct bus_node *, u32, struct resource_node **, int);
+extern int ibmphp_check_resource (struct resource_node *, u8);
+extern int ibmphp_remove_bus (struct bus_node *, u8);
+extern void ibmphp_free_resources (void);
+extern int ibmphp_add_pfmem_from_mem (struct resource_node *);
+extern struct bus_node *ibmphp_find_res_bus (u8);
+extern void ibmphp_print_test (void);  /* for debugging purposes */
+
+extern void ibmphp_hpc_initvars (void);
+extern int ibmphp_hpc_readslot (struct slot *, u8, u8 *);
+extern int ibmphp_hpc_writeslot (struct slot *, u8);
+extern void ibmphp_lock_operations (void);
+extern void ibmphp_unlock_operations (void);
+extern int ibmphp_hpc_fillhpslotinfo (struct hotplug_slot *);
+extern int ibmphp_hpc_start_poll_thread (void);
+extern void ibmphp_hpc_stop_poll_thread (void);
+
+//----------------------------------------------------------------------------
+
+
+//----------------------------------------------------------------------------
+// HPC return codes
+//----------------------------------------------------------------------------
+#define FALSE                          0x00
+#define TRUE                           0x01
+#define HPC_ERROR                      0xFF
+
+//-----------------------------------------------------------------------------
+// BUS INFO
+//-----------------------------------------------------------------------------
+#define BUS_SPEED                      0x30
+#define BUS_MODE                       0x40
+#define BUS_MODE_PCIX                  0x01
+#define BUS_MODE_PCI                   0x00
+#define BUS_SPEED_2                    0x20
+#define BUS_SPEED_1                    0x10
+#define BUS_SPEED_33                   0x00
+#define BUS_SPEED_66                   0x01
+#define BUS_SPEED_100                  0x02
+#define BUS_SPEED_133                  0x03
+#define BUS_SPEED_66PCIX               0x04
+#define BUS_SPEED_66UNKNOWN            0x05
+#define BUS_STATUS_AVAILABLE           0x01
+#define BUS_CONTROL_AVAILABLE          0x02
+#define SLOT_LATCH_REGS_SUPPORTED      0x10
+
+#define PRGM_MODEL_REV_LEVEL           0xF0
+#define MAX_ADAPTER_NONE               0x09
+
+//----------------------------------------------------------------------------
+// HPC 'write' operations/commands
+//----------------------------------------------------------------------------
+//     Command                 Code    State   Write to reg
+//                                     Machine at index
+//-------------------------    ----    ------- ------------
+#define HPC_CTLR_ENABLEIRQ     0x00    // N    15
+#define HPC_CTLR_DISABLEIRQ    0x01    // N    15
+#define HPC_SLOT_OFF           0x02    // Y    0-14
+#define HPC_SLOT_ON            0x03    // Y    0-14
+#define HPC_SLOT_ATTNOFF       0x04    // N    0-14
+#define HPC_SLOT_ATTNON                0x05    // N    0-14
+#define HPC_CTLR_CLEARIRQ      0x06    // N    15
+#define HPC_CTLR_RESET         0x07    // Y    15
+#define HPC_CTLR_IRQSTEER      0x08    // N    15
+#define HPC_BUS_33CONVMODE     0x09    // Y    31-34
+#define HPC_BUS_66CONVMODE     0x0A    // Y    31-34
+#define HPC_BUS_66PCIXMODE     0x0B    // Y    31-34
+#define HPC_BUS_100PCIXMODE    0x0C    // Y    31-34
+#define HPC_BUS_133PCIXMODE    0x0D    // Y    31-34
+#define HPC_ALLSLOT_OFF                0x11    // Y    15
+#define HPC_ALLSLOT_ON         0x12    // Y    15
+#define HPC_SLOT_BLINKLED      0x13    // N    0-14
+
+//----------------------------------------------------------------------------
+// read commands
+//----------------------------------------------------------------------------
+#define READ_SLOTSTATUS                0x01
+#define READ_EXTSLOTSTATUS     0x02
+#define READ_BUSSTATUS         0x03
+#define READ_CTLRSTATUS                0x04
+#define READ_ALLSTAT           0x05
+#define READ_ALLSLOT           0x06
+#define READ_SLOTLATCHLOWREG   0x07
+#define READ_REVLEVEL          0x08
+#define READ_HPCOPTIONS                0x09
+//----------------------------------------------------------------------------
+// slot status
+//----------------------------------------------------------------------------
+#define HPC_SLOT_POWER         0x01
+#define HPC_SLOT_CONNECT       0x02
+#define HPC_SLOT_ATTN          0x04
+#define HPC_SLOT_PRSNT2                0x08
+#define HPC_SLOT_PRSNT1                0x10
+#define HPC_SLOT_PWRGD         0x20
+#define HPC_SLOT_BUS_SPEED     0x40
+#define HPC_SLOT_LATCH         0x80
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_POWER status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_POWER_OFF     0x00
+#define HPC_SLOT_POWER_ON      0x01
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_CONNECT status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_CONNECTED     0x00
+#define HPC_SLOT_DISCONNECTED  0x01
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_ATTN status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_ATTN_OFF      0x00
+#define HPC_SLOT_ATTN_ON       0x01
+#define HPC_SLOT_ATTN_BLINK    0x02
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_PRSNT status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_EMPTY         0x00
+#define HPC_SLOT_PRSNT_7       0x01
+#define HPC_SLOT_PRSNT_15      0x02
+#define HPC_SLOT_PRSNT_25      0x03
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_PWRGD status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_PWRGD_FAULT_NONE      0x00
+#define HPC_SLOT_PWRGD_GOOD            0x01
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_BUS_SPEED status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_BUS_SPEED_OK  0x00
+#define HPC_SLOT_BUS_SPEED_MISM        0x01
+
+//----------------------------------------------------------------------------
+// HPC_SLOT_LATCH status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_LATCH_OPEN    0x01    // NOTE : in PCI spec bit off = open
+#define HPC_SLOT_LATCH_CLOSED  0x00    // NOTE : in PCI spec bit on  = closed
+
+
+//----------------------------------------------------------------------------
+// extended slot status
+//----------------------------------------------------------------------------
+#define HPC_SLOT_PCIX          0x01
+#define HPC_SLOT_SPEED1                0x02
+#define HPC_SLOT_SPEED2                0x04
+#define HPC_SLOT_BLINK_ATTN    0x08
+#define HPC_SLOT_RSRVD1                0x10
+#define HPC_SLOT_RSRVD2                0x20
+#define HPC_SLOT_BUS_MODE      0x40
+#define HPC_SLOT_RSRVD3                0x80
+
+//----------------------------------------------------------------------------
+// HPC_XSLOT_PCIX_CAP status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_PCIX_NO       0x00
+#define HPC_SLOT_PCIX_YES      0x01
+
+//----------------------------------------------------------------------------
+// HPC_XSLOT_SPEED status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_SPEED_33      0x00
+#define HPC_SLOT_SPEED_66      0x01
+#define HPC_SLOT_SPEED_133     0x02
+
+//----------------------------------------------------------------------------
+// HPC_XSLOT_ATTN_BLINK status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_ATTN_BLINK_OFF        0x00
+#define HPC_SLOT_ATTN_BLINK_ON 0x01
+
+//----------------------------------------------------------------------------
+// HPC_XSLOT_BUS_MODE status return codes
+//----------------------------------------------------------------------------
+#define HPC_SLOT_BUS_MODE_OK   0x00
+#define HPC_SLOT_BUS_MODE_MISM 0x01
+
+//----------------------------------------------------------------------------
+// Controller status
+//----------------------------------------------------------------------------
+#define HPC_CTLR_WORKING       0x01
+#define HPC_CTLR_FINISHED      0x02
+#define HPC_CTLR_RESULT0       0x04
+#define HPC_CTLR_RESULT1       0x08
+#define HPC_CTLR_RESULE2       0x10
+#define HPC_CTLR_RESULT3       0x20
+#define HPC_CTLR_IRQ_ROUTG     0x40
+#define HPC_CTLR_IRQ_PENDG     0x80
+
+//----------------------------------------------------------------------------
+// HPC_CTLR_WROKING status return codes
+//----------------------------------------------------------------------------
+#define HPC_CTLR_WORKING_NO    0x00
+#define HPC_CTLR_WORKING_YES   0x01
+
+//----------------------------------------------------------------------------
+// HPC_CTLR_FINISHED status return codes
+//----------------------------------------------------------------------------
+#define HPC_CTLR_FINISHED_NO   0x00
+#define HPC_CTLR_FINISHED_YES  0x01
+
+//----------------------------------------------------------------------------
+// HPC_CTLR_RESULT status return codes
+//----------------------------------------------------------------------------
+#define HPC_CTLR_RESULT_SUCCESS        0x00
+#define HPC_CTLR_RESULT_FAILED 0x01
+#define HPC_CTLR_RESULT_RSVD   0x02
+#define HPC_CTLR_RESULT_NORESP 0x03
+
+
+//----------------------------------------------------------------------------
+// macro for slot info
+//----------------------------------------------------------------------------
+#define SLOT_POWER(s)  ((u8) ((s & HPC_SLOT_POWER) \
+       ? HPC_SLOT_POWER_ON : HPC_SLOT_POWER_OFF))
+
+#define SLOT_CONNECT(s)        ((u8) ((s & HPC_SLOT_CONNECT) \
+       ? HPC_SLOT_DISCONNECTED : HPC_SLOT_CONNECTED))
+
+#define SLOT_ATTN(s,es)        ((u8) ((es & HPC_SLOT_BLINK_ATTN) \
+       ? HPC_SLOT_ATTN_BLINK \
+       : ((s & HPC_SLOT_ATTN) ? HPC_SLOT_ATTN_ON : HPC_SLOT_ATTN_OFF)))
+
+#define SLOT_PRESENT(s)        ((u8) ((s & HPC_SLOT_PRSNT1) \
+       ? ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_EMPTY : HPC_SLOT_PRSNT_15) \
+       : ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_PRSNT_25 : HPC_SLOT_PRSNT_7)))
+
+#define SLOT_PWRGD(s)  ((u8) ((s & HPC_SLOT_PWRGD) \
+       ? HPC_SLOT_PWRGD_GOOD : HPC_SLOT_PWRGD_FAULT_NONE))
+
+#define SLOT_BUS_SPEED(s)      ((u8) ((s & HPC_SLOT_BUS_SPEED) \
+       ? HPC_SLOT_BUS_SPEED_MISM : HPC_SLOT_BUS_SPEED_OK))
+
+#define SLOT_LATCH(s)  ((u8) ((s & HPC_SLOT_LATCH) \
+       ? HPC_SLOT_LATCH_CLOSED : HPC_SLOT_LATCH_OPEN))
+
+#define SLOT_PCIX(es)  ((u8) ((es & HPC_SLOT_PCIX) \
+       ? HPC_SLOT_PCIX_YES : HPC_SLOT_PCIX_NO))
+
+#define SLOT_SPEED(es) ((u8) ((es & HPC_SLOT_SPEED2) \
+       ? ((es & HPC_SLOT_SPEED1) ? HPC_SLOT_SPEED_133   \
+                               : HPC_SLOT_SPEED_66)   \
+       : HPC_SLOT_SPEED_33))
+
+#define SLOT_BUS_MODE(es)      ((u8) ((es & HPC_SLOT_BUS_MODE) \
+       ? HPC_SLOT_BUS_MODE_MISM : HPC_SLOT_BUS_MODE_OK))
+
+//--------------------------------------------------------------------------
+// macro for bus info
+//---------------------------------------------------------------------------
+#define CURRENT_BUS_SPEED(s)   ((u8) (s & BUS_SPEED_2) \
+       ? ((s & BUS_SPEED_1) ? BUS_SPEED_133 : BUS_SPEED_100) \
+       : ((s & BUS_SPEED_1) ? BUS_SPEED_66 : BUS_SPEED_33))
+
+#define CURRENT_BUS_MODE(s)    ((u8) (s & BUS_MODE) ? BUS_MODE_PCIX : BUS_MODE_PCI)
+
+#define READ_BUS_STATUS(s)     ((u8) (s->options & BUS_STATUS_AVAILABLE))
+
+#define READ_BUS_MODE(s)       ((s->revision & PRGM_MODEL_REV_LEVEL) >= 0x20)
+
+#define SET_BUS_STATUS(s)      ((u8) (s->options & BUS_CONTROL_AVAILABLE))
+
+#define READ_SLOT_LATCH(s)     ((u8) (s->options & SLOT_LATCH_REGS_SUPPORTED))
+
+//----------------------------------------------------------------------------
+// macro for controller info
+//----------------------------------------------------------------------------
+#define CTLR_WORKING(c) ((u8) ((c & HPC_CTLR_WORKING) \
+       ? HPC_CTLR_WORKING_YES : HPC_CTLR_WORKING_NO))
+#define CTLR_FINISHED(c) ((u8) ((c & HPC_CTLR_FINISHED) \
+       ? HPC_CTLR_FINISHED_YES : HPC_CTLR_FINISHED_NO))
+#define CTLR_RESULT(c) ((u8) ((c & HPC_CTLR_RESULT1)  \
+       ? ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_NORESP \
+                               : HPC_CTLR_RESULT_RSVD)  \
+       : ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_FAILED \
+                               : HPC_CTLR_RESULT_SUCCESS)))
+
+// command that affect the state machine of HPC
+#define NEEDTOCHECK_CMDSTATUS(c) ((c == HPC_SLOT_OFF)        || \
+                                 (c == HPC_SLOT_ON)         || \
+                                 (c == HPC_CTLR_RESET)      || \
+                                 (c == HPC_BUS_33CONVMODE)  || \
+                                 (c == HPC_BUS_66CONVMODE)  || \
+                                 (c == HPC_BUS_66PCIXMODE)  || \
+                                 (c == HPC_BUS_100PCIXMODE) || \
+                                 (c == HPC_BUS_133PCIXMODE) || \
+                                 (c == HPC_ALLSLOT_OFF)     || \
+                                 (c == HPC_ALLSLOT_ON))
+
+
+/* Core part of the driver */
+
+#define ENABLE         1
+#define DISABLE                0
+
+#define ADD            0
+#define REMOVE         1
+#define DETAIL         2
+
+#define MAX_OPS                3
+#define CARD_INFO      0x07
+#define PCIX133                0x07
+#define PCIX66         0x05
+#define PCI66          0x04
+
+extern struct pci_ops *ibmphp_pci_root_ops;
+
+/* Variables */
+
+struct pci_func {
+       struct pci_dev *dev;    /* from the OS */
+       u8 busno;
+       u8 device;
+       u8 function;
+       struct resource_node *io[6];
+       struct resource_node *mem[6];
+       struct resource_node *pfmem[6];
+       struct pci_func *next;
+       int devices[32];        /* for bridge config */
+       u8 irq[4];              /* for interrupt config */
+       u8 bus;                 /* flag for unconfiguring, to say if PPB */
+};
+
+struct slot {
+       u8 bus;
+       u8 device;
+       u8 number;
+       char name[100];
+       u32 capabilities;
+       struct hotplug_slot *hotplug_slot;
+       struct controller *ctrl;
+       struct pci_func *func;
+       u8 irq[4];
+       u8 flag;                /* this is for disable slot and polling */
+       int bit_mode;           /* 0 = 32, 1 = 64 */
+       u8 ctlr_index;
+       struct bus_info *bus_on;
+       struct list_head ibm_slot_list;
+       u8 status;
+       u8 ext_status;
+       u8 busstatus;
+};
+
+struct controller {
+       struct ebda_hpc_slot *slots;
+       struct ebda_hpc_bus *buses;
+       u8 revision;
+       u8 options;             /* which options HPC supports */
+       u8 status;
+       u8 ctlr_id;             /* TONI */
+       u8 slot_count;
+       u8 bus_count;
+       u8 ctlr_relative_id;
+       u32 irq;
+       union {
+               struct isa_ctlr_access isa_ctlr;
+               struct pci_ctlr_access pci_ctlr;
+               struct wpeg_i2c_ctlr_access wpeg_ctlr;
+       } u;
+       u8 ctlr_type;
+       struct list_head ebda_hpc_list;
+};
+
+/* Functions */
+
+extern int ibmphp_init_devno (struct slot **); /* This function is called from EBDA, so we need it not be static */
+extern int ibmphp_disable_slot (struct hotplug_slot *);        /* This function is called from HPC, so we need it to not be static */
+extern int ibmphp_update_slot_info (struct slot *);    /* This function is called from HPC, so we need it to not be be static */
+extern int ibmphp_configure_card (struct pci_func *, u8);
+extern int ibmphp_unconfigure_card (struct slot **, int);
+extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops;
+
+static inline void long_delay (int delay)
+{
+       set_current_state (TASK_INTERRUPTIBLE);
+       schedule_timeout (delay);
+}
+
+#endif                         //__IBMPHP_H
+
diff --git a/drivers/hotplug/ibmphp_core.c b/drivers/hotplug/ibmphp_core.c
new file mode 100644 (file)
index 0000000..dfc0be4
--- /dev/null
@@ -0,0 +1,1480 @@
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Chuck Cole, Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation
+ *
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (c) 2001,2002 IBM Corp.
+ *
+ * All rights reserved.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <gregkh@us.ibm.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/smp_lock.h>
+#include "../../arch/i386/kernel/pci-i386.h"   /* for struct irq_routing_table */
+#include "ibmphp.h"
+
+#define attn_on(sl)  ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNON)
+#define attn_off(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNOFF)
+#define attn_LED_blink(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_BLINKLED)
+#define get_ctrl_revision(sl, rev) ibmphp_hpc_readslot (sl, READ_REVLEVEL, rev)
+#define get_hpc_options(sl, opt) ibmphp_hpc_readslot (sl, READ_HPCOPTIONS, opt)
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_DESC    "IBM Hot Plug PCI Controller Driver"
+
+int ibmphp_debug;
+
+static int debug;
+MODULE_PARM (debug, "i");
+MODULE_PARM_DESC (debug, "Debugging mode enabled or not");
+MODULE_LICENSE ("GPL");
+MODULE_DESCRIPTION (DRIVER_DESC);
+
+static int *ops[MAX_OPS + 1];
+static struct pci_ops *ibmphp_pci_root_ops;
+static int max_slots;
+
+static int irqs[16];    /* PIC mode IRQ's we're using so far (in case MPS tables don't provide default info for empty slots */
+
+static int init_flag;
+
+/*
+static int get_max_adapter_speed_1 (struct hotplug_slot *, u8 *, u8);
+
+static inline int get_max_adapter_speed (struct hotplug_slot *hs, u8 *value)
+{
+       return get_max_adapter_speed_1 (hs, value, 1);
+}
+*/
+static inline int get_cur_bus_info (struct slot **sl) 
+{
+       int rc = 1;
+       struct slot * slot_cur = *sl;
+
+       debug ("options = %x\n", slot_cur->ctrl->options);
+       debug ("revision = %x\n", slot_cur->ctrl->revision);    
+
+       if (READ_BUS_STATUS (slot_cur->ctrl)) 
+               rc = ibmphp_hpc_readslot (slot_cur, READ_BUSSTATUS, NULL);
+       
+       if (rc) 
+               return rc;
+         
+       slot_cur->bus_on->current_speed = CURRENT_BUS_SPEED (slot_cur->busstatus);
+       if (READ_BUS_MODE (slot_cur->ctrl))
+               slot_cur->bus_on->current_bus_mode = CURRENT_BUS_MODE (slot_cur->busstatus);
+
+       debug ("busstatus = %x, bus_speed = %x, bus_mode = %x\n", slot_cur->busstatus, slot_cur->bus_on->current_speed, slot_cur->bus_on->current_bus_mode);
+       
+       *sl = slot_cur;
+       return 0;
+}
+
+static inline int slot_update (struct slot **sl)
+{
+       int rc;
+       rc = ibmphp_hpc_readslot (*sl, READ_ALLSTAT, NULL);
+       if (rc) 
+               return rc;
+       if (!init_flag)
+               return get_cur_bus_info (sl);
+       return rc;
+}
+
+static int get_max_slots (void)
+{
+       struct list_head * tmp;
+       int slot_count = 0;
+
+       list_for_each (tmp, &ibmphp_slot_head) 
+               ++slot_count;
+       return slot_count;
+}
+
+/* This routine will put the correct slot->device information per slot.  It's
+ * called from initialization of the slot structures. It will also assign
+ * interrupt numbers per each slot.
+ * Parameters: struct slot
+ * Returns 0 or errors
+ */
+int ibmphp_init_devno (struct slot **cur_slot)
+{
+       struct irq_routing_table *rtable;
+       int len;
+       int loop;
+       int i;
+
+       rtable = pcibios_get_irq_routing_table ();
+       if (!rtable) {
+               err ("no BIOS routing table...\n");
+               return -ENOMEM;
+       }
+
+       len = (rtable->size - sizeof (struct irq_routing_table)) / sizeof (struct irq_info);
+
+       if (!len)
+               return -1;
+       for (loop = 0; loop < len; loop++) {
+               if ((*cur_slot)->number == rtable->slots[loop].slot) {
+               if ((*cur_slot)->bus == rtable->slots[loop].bus) {
+                       (*cur_slot)->device = PCI_SLOT (rtable->slots[loop].devfn);
+                       for (i = 0; i < 4; i++)
+                               (*cur_slot)->irq[i] = IO_APIC_get_PCI_irq_vector ((int) (*cur_slot)->bus, (int) (*cur_slot)->device, i);
+
+                               debug ("(*cur_slot)->irq[0] = %x\n", (*cur_slot)->irq[0]);
+                               debug ("(*cur_slot)->irq[1] = %x\n", (*cur_slot)->irq[1]);
+                               debug ("(*cur_slot)->irq[2] = %x\n", (*cur_slot)->irq[2]);
+                               debug ("(*cur_slot)->irq[3] = %x\n", (*cur_slot)->irq[3]);
+
+                               debug ("rtable->exlusive_irqs = %x\n", rtable->exclusive_irqs);
+                               debug ("rtable->slots[loop].irq[0].bitmap = %x\n", rtable->slots[loop].irq[0].bitmap);
+                               debug ("rtable->slots[loop].irq[1].bitmap = %x\n", rtable->slots[loop].irq[1].bitmap);
+                               debug ("rtable->slots[loop].irq[2].bitmap = %x\n", rtable->slots[loop].irq[2].bitmap);
+                               debug ("rtable->slots[loop].irq[3].bitmap = %x\n", rtable->slots[loop].irq[3].bitmap);
+
+                               debug ("rtable->slots[loop].irq[0].link= %x\n", rtable->slots[loop].irq[0].link);
+                               debug ("rtable->slots[loop].irq[1].link = %x\n", rtable->slots[loop].irq[1].link);
+                               debug ("rtable->slots[loop].irq[2].link = %x\n", rtable->slots[loop].irq[2].link);
+                               debug ("rtable->slots[loop].irq[3].link = %x\n", rtable->slots[loop].irq[3].link);
+                               debug ("end of init_devno\n");
+                               return 0;
+                       }
+               }
+       }
+
+       return -1;
+}
+
+static inline int power_on (struct slot *slot_cur)
+{
+       u8 cmd = HPC_SLOT_ON;
+       int retval;
+
+       retval = ibmphp_hpc_writeslot (slot_cur, cmd);
+       if (retval) {
+               err ("power on failed\n");
+               return retval;
+       }
+       if (CTLR_RESULT (slot_cur->ctrl->status)) {
+               err ("command not completed successfully in power_on \n");
+               return -EIO;
+       }
+       long_delay (3 * HZ); /* For ServeRAID cards, and some 66 PCI */
+       return 0;
+}
+
+static inline int power_off (struct slot *slot_cur)
+{
+       u8 cmd = HPC_SLOT_OFF;
+       int retval;
+
+       retval = ibmphp_hpc_writeslot (slot_cur, cmd);
+       if (retval) {
+               err ("power off failed \n");
+               return retval;
+       }
+       if (CTLR_RESULT (slot_cur->ctrl->status)) {
+               err ("command not completed successfully in power_off \n");
+               return -EIO;
+       }
+       return 0;
+}
+
+static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 value)
+{
+       int rc = 0;
+       struct slot *pslot;
+       u8 cmd;
+       int hpcrc = 0;
+
+       debug ("set_attention_status - Entry hotplug_slot[%lx] value[%x]\n", (ulong) hotplug_slot, value);
+       ibmphp_lock_operations ();
+       cmd = 0x00;     // avoid compiler warning
+
+       if (hotplug_slot) {
+               switch (value) {
+               case HPC_SLOT_ATTN_OFF:
+                       cmd = HPC_SLOT_ATTNOFF;
+                       break;
+               case HPC_SLOT_ATTN_ON:
+                       cmd = HPC_SLOT_ATTNON;
+                       break;
+               case HPC_SLOT_ATTN_BLINK:
+                       cmd = HPC_SLOT_BLINKLED;
+                       break;
+               default:
+                       rc = -ENODEV;
+                       err ("set_attention_status - Error : invalid input [%x]\n", value);
+                       break;
+               }
+               if (rc == 0) {
+                       pslot = (struct slot *) hotplug_slot->private;
+                       if (pslot)
+                               hpcrc = ibmphp_hpc_writeslot (pslot, cmd);
+                       else
+                               rc = -ENODEV;
+               }
+       } else  
+               rc = -ENODEV;
+
+       if (hpcrc)
+               rc = hpcrc;
+
+       ibmphp_unlock_operations ();
+
+       debug ("set_attention_status - Exit rc[%d]\n", rc);
+       return rc;
+}
+
+static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 * value)
+{
+       int rc = -ENODEV;
+       struct slot *pslot;
+       int hpcrc = 0;
+       struct slot myslot;
+
+       debug ("get_attention_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value);
+        
+       ibmphp_lock_operations ();
+       if (hotplug_slot && value) {
+               pslot = (struct slot *) hotplug_slot->private;
+               if (pslot) {
+                       memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
+                       hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
+                       if (!hpcrc)
+                               hpcrc = ibmphp_hpc_readslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status));
+                       if (!hpcrc) {
+                               *value = SLOT_ATTN (myslot.status, myslot.ext_status);
+                               rc = 0;
+                       }
+               }
+       } else
+               rc = -ENODEV;
+
+       if (hpcrc)
+               rc = hpcrc;
+
+       ibmphp_unlock_operations ();
+       debug ("get_attention_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
+       return rc;
+}
+
+static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 * value)
+{
+       int rc = -ENODEV;
+       struct slot *pslot;
+       int hpcrc = 0;
+       struct slot myslot;
+
+       debug ("get_latch_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value);
+       ibmphp_lock_operations ();
+       if (hotplug_slot && value) {
+               pslot = (struct slot *) hotplug_slot->private;
+               if (pslot) {
+                       memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
+                       hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
+                       if (!hpcrc) {
+                               *value = SLOT_LATCH (myslot.status);
+                               rc = 0;
+                       }
+               }
+       } else
+               rc = -ENODEV;
+
+       if (hpcrc)
+               rc = hpcrc;
+
+       ibmphp_unlock_operations ();
+       debug ("get_latch_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
+       return rc;
+}
+
+
+static int get_power_status (struct hotplug_slot *hotplug_slot, u8 * value)
+{
+       int rc = -ENODEV;
+       struct slot *pslot;
+       int hpcrc = 0;
+       struct slot myslot;
+
+       debug ("get_power_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value);
+       ibmphp_lock_operations ();
+       if (hotplug_slot && value) {
+               pslot = (struct slot *) hotplug_slot->private;
+               if (pslot) {
+                       memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
+                       hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
+                       if (!hpcrc) {
+                               *value = SLOT_POWER (myslot.status);
+                               rc = 0;
+                       }
+               }
+       } else
+               rc = -ENODEV;
+
+       if (hpcrc)
+               rc = hpcrc;
+
+       ibmphp_unlock_operations ();
+       debug ("get_power_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
+       return rc;
+}
+
+static int get_adapter_present (struct hotplug_slot *hotplug_slot, u8 * value)
+{
+       int rc = -ENODEV;
+       struct slot *pslot;
+       u8 present;
+       int hpcrc = 0;
+       struct slot myslot;
+
+       debug ("get_adapter_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value);
+       ibmphp_lock_operations ();
+       if (hotplug_slot && value) {
+               pslot = (struct slot *) hotplug_slot->private;
+               if (pslot) {
+                       memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
+                       hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
+                       if (!hpcrc) {
+                               present = SLOT_PRESENT (myslot.status);
+                               if (present == HPC_SLOT_EMPTY)
+                                       *value = 0;
+                               else
+                                       *value = 1;
+                               rc = 0;
+                       }
+               }
+       } else
+               rc = -ENODEV;
+       if (hpcrc)
+               rc = hpcrc;
+
+       ibmphp_unlock_operations ();
+       debug ("get_adapter_present - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
+       return rc;
+}
+/*
+static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, u8 * value)
+{
+       int rc = -ENODEV;
+       struct slot *pslot;
+       u8 mode = 0;
+
+       debug ("get_max_bus_speed - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong)hotplug_slot, (ulong) value);
+
+       ibmphp_lock_operations ();
+
+       if (hotplug_slot && value) {
+               pslot = (struct slot *) hotplug_slot->private;
+               if (pslot) {
+                       rc = 0;
+                       mode = pslot->bus_on->supported_bus_mode;
+                       *value = pslot->bus_on->supported_speed;
+                       *value &= 0x0f;
+
+                       if (mode == BUS_MODE_PCIX)
+                               *value |= 0x80;
+                       else if (mode == BUS_MODE_PCI)
+                               *value |= 0x40;
+                       else
+                               *value |= 0x20;
+               }
+       } else
+               rc = -ENODEV;
+
+       ibmphp_unlock_operations ();
+       debug ("get_max_bus_speed - Exit rc[%d] value[%x]\n", rc, *value);
+       return rc;
+}
+
+static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, u8 * value)
+{
+       int rc = -ENODEV;
+       struct slot *pslot;
+       u8 mode = 0;
+
+       debug ("get_cur_bus_speed - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong)hotplug_slot, (ulong) value);
+
+       ibmphp_lock_operations ();
+
+       if (hotplug_slot && value) {
+               pslot = (struct slot *) hotplug_slot->private;
+               if (pslot) {
+                       rc = get_cur_bus_info (&pslot);
+                       if (!rc) {
+                               mode = pslot->bus_on->current_bus_mode;
+                               *value = pslot->bus_on->current_speed;
+                               *value &= 0x0f;
+                               
+                               if (mode == BUS_MODE_PCIX)
+                                       *value |= 0x80;
+                               else if (mode == BUS_MODE_PCI)
+                                       *value |= 0x40;
+                               else
+                                       *value |= 0x20; 
+                       }
+               }
+       } else
+               rc = -ENODEV;
+
+       ibmphp_unlock_operations ();
+       debug ("get_cur_bus_speed - Exit rc[%d] value[%x]\n", rc, *value);
+       return rc;
+}
+
+static int get_max_adapter_speed_1 (struct hotplug_slot *hotplug_slot, u8 * value, u8 flag)
+{
+       int rc = -ENODEV;
+       struct slot *pslot;
+       int hpcrc = 0;
+       struct slot myslot;
+
+       debug ("get_max_adapter_speed - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong)hotplug_slot, (ulong) value);
+
+       if (flag)
+               ibmphp_lock_operations ();
+
+       if (hotplug_slot && value) {
+               pslot = (struct slot *) hotplug_slot->private;
+               if (pslot) {
+                       memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
+                       hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
+
+                       if (!(SLOT_LATCH (myslot.status)) && (SLOT_PRESENT (myslot.status))) {
+                               hpcrc = ibmphp_hpc_readslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status));
+                               if (!hpcrc) {
+                                       *value = SLOT_SPEED (myslot.ext_status);
+                                       rc = 0;
+                               }
+                       } else {
+                               *value = MAX_ADAPTER_NONE;
+                               rc = 0;
+                       }
+                }
+        } else
+               rc = -ENODEV;
+
+       if (hpcrc)
+               rc = hpcrc;
+
+       if (flag)
+               ibmphp_unlock_operations ();
+
+       debug ("get_adapter_present - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
+       return rc;
+}
+
+static int get_card_bus_names (struct hotplug_slot *hotplug_slot, char * value)
+{
+       int rc = -ENODEV;
+       struct slot *pslot = NULL;
+       struct pci_dev * dev = NULL;
+
+       debug ("get_card_bus_names - Entry hotplug_slot[%lx] \n", (ulong)hotplug_slot);
+
+       ibmphp_lock_operations ();
+
+       if (hotplug_slot) {
+               pslot = (struct slot *) hotplug_slot->private;
+               if (pslot) {
+                       rc = 0;
+                       if (pslot->func)
+                               dev = pslot->func->dev;
+                       else
+                               dev = pci_find_slot (pslot->bus, (pslot->device << 3) | (0x00 & 0x7));
+                       if (dev) 
+                               snprintf (value, 100, "Bus %d : %s", pslot->bus,dev->name);
+                       else
+                               snprintf (value, 100, "Bus %d", pslot->bus);
+                       
+                               
+               }
+       } else
+               rc = -ENODEV;
+
+       ibmphp_unlock_operations ();
+       debug ("get_card_bus_names - Exit rc[%d] value[%x]\n", rc, *value);
+       return rc;
+}
+
+*/
+/*******************************************************************************
+ * This routine will initialize the ops data structure used in the validate
+ * function. It will also power off empty slots that are powered on since BIOS
+ * leaves those on, albeit disconnected
+ ******************************************************************************/
+static int init_ops (void)
+{
+       struct slot *slot_cur;
+       int retval;
+       int j;
+       int rc;
+
+       for (j = 0; j < MAX_OPS; j++) {
+               ops[j] = (int *) kmalloc ((max_slots + 1) * sizeof (int), GFP_KERNEL);
+               if (!ops[j]) {
+                       err ("out of system memory \n");
+                       return -ENOMEM;
+               }
+       }
+
+       ops[ADD][0] = 0;
+       ops[REMOVE][0] = 0;
+       ops[DETAIL][0] = 0;
+
+       for (j = 1; j <= max_slots; j++) {
+
+               slot_cur = ibmphp_get_slot_from_physical_num (j);
+
+               debug ("BEFORE GETTING SLOT STATUS, slot # %x\n", slot_cur->number);
+
+               if (slot_cur->ctrl->revision == 0xFF) 
+                       if (get_ctrl_revision (slot_cur, &slot_cur->ctrl->revision))
+                               return -1;
+
+               if (slot_cur->bus_on->current_speed == 0xFF) 
+                       if (get_cur_bus_info (&slot_cur)) 
+                               return -1;
+
+               if (slot_cur->ctrl->options == 0xFF)
+                       if (get_hpc_options (slot_cur, &slot_cur->ctrl->options))
+                               return -1;
+
+               retval = slot_update (&slot_cur);
+               if (retval)
+                       return retval;
+
+               debug ("status = %x, ext_status = %x\n", slot_cur->status, slot_cur->ext_status);
+               debug ("SLOT_POWER = %x, SLOT_PRESENT = %x, SLOT_LATCH = %x\n", SLOT_POWER (slot_cur->status), SLOT_PRESENT (slot_cur->status), SLOT_LATCH (slot_cur->status));
+
+               if (!(SLOT_POWER (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status)) && !(SLOT_LATCH (slot_cur->status)))
+                       /* No power, adapter, and latch closed */
+                       ops[ADD][j] = 1;
+               else
+                       ops[ADD][j] = 0;
+
+               ops[DETAIL][j] = 1;
+
+               if ((SLOT_POWER (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status)) && !(SLOT_LATCH (slot_cur->status)))
+                       /*Power,adapter,latch closed */
+                       ops[REMOVE][j] = 1;
+               else
+                       ops[REMOVE][j] = 0;
+
+               if ((SLOT_POWER (slot_cur->status)) && !(SLOT_PRESENT (slot_cur->status)) && !(SLOT_LATCH (slot_cur->status))) {
+                       debug ("BEFORE POWER OFF COMMAND\n");
+                               rc = power_off (slot_cur);
+                               if (rc)
+                                       return rc;
+
+       /*              retval = slot_update (&slot_cur);
+        *              if (retval)
+        *                      return retval;
+        *              ibmphp_update_slot_info (slot_cur);
+        */
+               }
+       }
+       init_flag = 0;
+       return 0;
+}
+
+/* This operation will check whether the slot is within the bounds and
+ * the operation is valid to perform on that slot
+ * Parameters: slot, operation
+ * Returns: 0 or error codes
+ */
+static int validate (struct slot *slot_cur, int opn)
+{
+       int number;
+       int retval;
+
+       if (!slot_cur)
+               return -ENODEV;
+       number = slot_cur->number;
+       if ((number > max_slots) || (number < 0))
+               return -EBADSLT;
+       debug ("slot_number in validate is %d\n", slot_cur->number);
+
+       retval = slot_update (&slot_cur);
+       if (retval)
+               return retval;
+
+       if (!(SLOT_POWER (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status))
+           && !(SLOT_LATCH (slot_cur->status)))
+               ops[ADD][number] = 1;
+       else
+               ops[ADD][number] = 0;
+
+       ops[DETAIL][number] = 1;
+
+       if ((SLOT_POWER (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status))
+           && !(SLOT_LATCH (slot_cur->status)))
+               ops[REMOVE][number] = 1;
+       else
+               ops[REMOVE][number] = 0;
+
+       switch (opn) {
+               case ENABLE:
+                       if (ops[ADD][number])
+                               return 0;
+                       break;
+               case DISABLE:
+                       if (ops[REMOVE][number])
+                               return 0;
+                       break;
+               case DETAIL:
+                       if (ops[DETAIL][number])
+                               return 0;
+                       break;
+               default:
+                       return -EINVAL;
+                       break;
+       }
+       err ("validate failed....\n");
+       return -EINVAL;
+}
+
+/********************************************************************************
+ * This routine is for updating the data structures in the hotplug core
+ * Parameters: struct slot
+ * Returns: 0 or error
+ *******************************************************************************/
+int ibmphp_update_slot_info (struct slot *slot_cur)
+{
+       struct hotplug_slot_info *info;
+       char buffer[10];
+       int rc;
+//     u8 bus_speed;
+
+       info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
+       if (!info) {
+               err ("out of system memory \n");
+               return -ENOMEM;
+       }
+        
+       snprintf (buffer, 10, "%d", slot_cur->number);
+       info->power_status = SLOT_POWER (slot_cur->status);
+       info->attention_status = SLOT_ATTN (slot_cur->status, slot_cur->ext_status);
+       info->latch_status = SLOT_LATCH (slot_cur->status);
+        if (!SLOT_PRESENT (slot_cur->status)) {
+                info->adapter_status = 0;
+//             info->max_adapter_speed_status = MAX_ADAPTER_NONE;
+       } else {
+                info->adapter_status = 1;
+//             get_max_adapter_speed_1 (slot_cur->hotplug_slot, &info->max_adapter_speed_status, 0);
+       }
+/*
+       bus_speed = slot_cur->bus_on->current_speed;
+       bus_speed &= 0x0f;
+                        
+       if (slot_cur->bus_on->current_bus_mode == BUS_MODE_PCIX)
+               bus_speed |= 0x80;
+       else if (slot_cur->bus_on->current_bus_mode == BUS_MODE_PCI)
+               bus_speed |= 0x40;
+       else
+               bus_speed |= 0x20;
+
+       info->cur_bus_speed_status = bus_speed;
+       info->max_bus_speed_status = slot_cur->hotplug_slot->info->max_bus_speed_status;
+       // To do: card_bus_names 
+*/     
+       rc = pci_hp_change_slot_info (buffer, info);
+       kfree (info);
+       return rc;
+}
+
+
+/******************************************************************************
+ * This function will return the pci_func, given bus and devfunc, or NULL.  It
+ * is called from visit routines
+ ******************************************************************************/
+
+static struct pci_func *ibm_slot_find (u8 busno, u8 device, u8 function)
+{
+       struct pci_func *func_cur;
+       struct slot *slot_cur;
+       struct list_head * tmp;
+       list_for_each (tmp, &ibmphp_slot_head) {
+               slot_cur = list_entry (tmp, struct slot, ibm_slot_list);
+               if (slot_cur->func) {
+                       func_cur = slot_cur->func;
+                       while (func_cur) {
+                               if ((func_cur->busno == busno) && (func_cur->device == device) && (func_cur->function == function))
+                                       return func_cur;
+                               func_cur = func_cur->next;
+                       }
+               }
+       }
+       return NULL;
+}
+
+/* This routine is to find the pci_bus from kernel structures.
+ * Parameters: bus number
+ * Returns : pci_bus *  or NULL if not found
+ */
+static struct pci_bus *find_bus (u8 busno)
+{
+       const struct list_head *tmp;
+       struct pci_bus *bus;
+       debug ("inside find_bus, busno = %x \n", busno);
+
+       list_for_each (tmp, &pci_root_buses) {
+               bus = (struct pci_bus *) pci_bus_b (tmp);
+               if (bus)
+                       if (bus->number == busno)
+                               return bus;
+       }
+       return NULL;
+}
+
+/******************************************************************
+ * This function is here because we can no longer use pci_root_ops
+ ******************************************************************/
+static struct pci_ops *get_root_pci_ops (void)
+{
+       struct pci_bus * bus;
+
+       if ((bus = find_bus (0)))
+               return bus->ops;
+       return NULL;
+}
+
+/*************************************************************
+ * This routine frees up memory used by struct slot, including
+ * the pointers to pci_func, bus, hotplug_slot, controller,
+ * and deregistering from the hotplug core
+ *************************************************************/
+static void free_slots (void)
+{
+       struct slot *slot_cur;
+       struct list_head * tmp;
+       struct list_head * next;
+
+       list_for_each_safe (tmp, next, &ibmphp_slot_head) {
+
+               slot_cur = list_entry (tmp, struct slot, ibm_slot_list);
+
+               pci_hp_deregister (slot_cur->hotplug_slot);
+
+               if (slot_cur->hotplug_slot) {
+                       kfree (slot_cur->hotplug_slot);
+                       slot_cur->hotplug_slot = NULL;
+               }
+
+               if (slot_cur->ctrl) 
+                       slot_cur->ctrl = NULL;
+               
+               if (slot_cur->bus_on) 
+                       slot_cur->bus_on = NULL;
+
+               ibmphp_unconfigure_card (&slot_cur, -1);  /* we don't want to actually remove the resources, since free_resources will do just that */
+
+               kfree (slot_cur);
+       }
+}
+
+static int ibm_is_pci_dev_in_use (struct pci_dev *dev)
+{
+       int i = 0;
+       int inuse = 0;
+
+       if (dev->driver)
+               return 1;
+
+       for (i = 0; !dev->driver && !inuse && (i < 6); i++) {
+
+               if (!pci_resource_start (dev, i))
+                       continue;
+
+               if (pci_resource_flags (dev, i) & IORESOURCE_IO)
+                       inuse = check_region (pci_resource_start (dev, i), pci_resource_len (dev, i));
+
+               else if (pci_resource_flags (dev, i) & IORESOURCE_MEM)
+                       inuse = check_mem_region (pci_resource_start (dev, i), pci_resource_len (dev, i));
+       }
+
+       return inuse;
+}
+
+static int ibm_pci_hp_remove_device (struct pci_dev *dev)
+{
+       if (ibm_is_pci_dev_in_use (dev)) {
+               err ("***Cannot safely power down device -- it appears to be in use***\n");
+               return -EBUSY;
+       }
+       pci_remove_device (dev);
+       return 0;
+}
+
+static int ibm_unconfigure_visit_pci_dev_phase2 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus)
+{
+       struct pci_dev *dev = wrapped_dev->dev;
+       struct pci_func *temp_func;
+       int i = 0;
+
+       do {
+               temp_func = ibm_slot_find (dev->bus->number, dev->devfn >> 3, i++);
+       } while (temp_func && (temp_func->function != (dev->devfn & 0x07)));
+
+       if (dev) {
+               if (ibm_pci_hp_remove_device (dev) == 0)
+                       kfree (dev);    /* Now, remove */
+               else
+                       return -1;
+       }
+
+       if (temp_func)
+               temp_func->dev = NULL;
+       else
+               err ("No pci_func representation for bus, devfn = %d, %x\n", dev->bus->number, dev->devfn);
+
+       return 0;
+}
+
+static int ibm_unconfigure_visit_pci_bus_phase2 (struct pci_bus_wrapped *wrapped_bus, struct pci_dev_wrapped *wrapped_dev)
+{
+       struct pci_bus *bus = wrapped_bus->bus;
+
+       pci_proc_detach_bus (bus);
+       /* The cleanup code should live in the kernel... */
+       bus->self->subordinate = NULL;
+       /* unlink from parent bus */
+       list_del (&bus->node);
+
+       /* Now, remove */
+       if (bus)
+               kfree (bus);
+
+       return 0;
+}
+
+static int ibm_unconfigure_visit_pci_dev_phase1 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus)
+{
+       struct pci_dev *dev = wrapped_dev->dev;
+
+       debug ("attempting removal of driver for device (%x, %x, %x)\n", dev->bus->number, PCI_SLOT (dev->devfn), PCI_FUNC (dev->devfn));
+
+       /* Now, remove the Linux Driver Representation */
+       if (dev->driver) {
+               debug ("is there a driver?\n");
+               if (dev->driver->remove) {
+                       dev->driver->remove (dev);
+                       debug ("driver was properly removed\n");
+               }
+               dev->driver = NULL;
+       }
+
+       return ibm_is_pci_dev_in_use (dev);
+}
+
+static struct pci_visit ibm_unconfigure_functions_phase1 = {
+       post_visit_pci_dev:     ibm_unconfigure_visit_pci_dev_phase1,
+};
+
+static struct pci_visit ibm_unconfigure_functions_phase2 = {
+       post_visit_pci_bus:     ibm_unconfigure_visit_pci_bus_phase2,
+       post_visit_pci_dev:     ibm_unconfigure_visit_pci_dev_phase2,
+};
+
+static int ibm_unconfigure_device (struct pci_func *func)
+{
+       int rc = 0;
+       struct pci_dev_wrapped wrapped_dev;
+       struct pci_bus_wrapped wrapped_bus;
+       struct pci_dev *temp;
+       u8 j;
+
+       memset (&wrapped_dev, 0, sizeof (struct pci_dev_wrapped));
+       memset (&wrapped_bus, 0, sizeof (struct pci_bus_wrapped));
+
+       debug ("inside ibm_unconfigure_device\n");
+       debug ("func->device = %x, func->function = %x\n", func->device, func->function);
+       debug ("func->device << 3 | 0x0  = %x\n", func->device << 3 | 0x0);
+
+       for (j = 0; j < 0x08; j++) {
+               temp = pci_find_slot (func->busno, (func->device << 3) | j);
+               if (temp) {
+                       wrapped_dev.dev = temp;
+                       wrapped_bus.bus = temp->bus;
+                       rc = pci_visit_dev (&ibm_unconfigure_functions_phase1, &wrapped_dev, &wrapped_bus);
+                       if (rc)
+                               break;
+
+                       rc = pci_visit_dev (&ibm_unconfigure_functions_phase2, &wrapped_dev, &wrapped_bus);
+                       if (rc)
+                               break;
+               }
+       }
+       debug ("rc in ibm_unconfigure_device b4 returning is %d \n", rc);
+       return rc;
+}
+
+static int configure_visit_pci_dev (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus)
+{
+       //      struct pci_bus *bus = wrapped_bus->bus; /* We don't need this, since we don't create in the else statement */
+       struct pci_dev *dev = wrapped_dev->dev;
+       struct pci_func *temp_func;
+       int i = 0;
+
+       do {
+               temp_func = ibm_slot_find (dev->bus->number, dev->devfn >> 3, i++);
+       } while (temp_func && (temp_func->function != (dev->devfn & 0x07)));
+
+       if (temp_func)
+               temp_func->dev = dev;
+       else {
+               /* This should not really happen, since we create functions
+                  first and then call to configure */
+               debug (" We shouldn't come here \n");
+       }
+
+       if (temp_func->dev) {
+               pci_proc_attach_device (temp_func->dev);
+               pci_announce_device_to_drivers (temp_func->dev);
+       }
+
+       return 0;
+}
+
+static struct pci_visit configure_functions = {
+       visit_pci_dev:  configure_visit_pci_dev,
+};
+
+static int ibm_configure_device (struct pci_func *func)
+{
+       unsigned char bus;
+       struct pci_dev dev0;
+       struct pci_bus *child;
+       struct pci_dev *temp;
+       int rc = 0;
+
+       struct pci_dev_wrapped wrapped_dev;
+       struct pci_bus_wrapped wrapped_bus;
+
+       memset (&wrapped_dev, 0, sizeof (struct pci_dev_wrapped));
+       memset (&wrapped_bus, 0, sizeof (struct pci_bus_wrapped));
+       memset (&dev0, 0, sizeof (struct pci_dev));
+
+       if (func->dev == NULL)
+               func->dev = pci_find_slot (func->busno, (func->device << 3) | (func->function & 0x7));
+
+       if (func->dev == NULL) {
+               dev0.bus = find_bus (func->busno);
+               dev0.devfn = ((func->device << 3) + (func->function & 0x7));
+               dev0.sysdata = dev0.bus->sysdata;
+
+               func->dev = pci_scan_slot (&dev0);
+
+               if (func->dev == NULL) {
+                       err ("ERROR... : pci_dev still NULL \n");
+                       return 0;
+               }
+       }
+       if (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+               pci_read_config_byte (func->dev, PCI_SECONDARY_BUS, &bus);
+               child = (struct pci_bus *) pci_add_new_bus (func->dev->bus, (func->dev), bus);
+               pci_do_scan_bus (child);
+       }
+
+       temp = func->dev;
+       if (temp) {
+               wrapped_dev.dev = temp;
+               wrapped_bus.bus = temp->bus;
+               rc = pci_visit_dev (&configure_functions, &wrapped_dev, &wrapped_bus);
+       }
+       return rc;
+}
+/*******************************************************
+ * Returns whether the bus is empty or not 
+ *******************************************************/
+static int is_bus_empty (struct slot * slot_cur)
+{
+       int rc;
+       struct slot * tmp_slot;
+       u8 i = slot_cur->bus_on->slot_min;
+
+       while (i <= slot_cur->bus_on->slot_max) {
+               if (i == slot_cur->number) {
+                       i++;
+                       continue;
+               }
+               tmp_slot = ibmphp_get_slot_from_physical_num (i);
+               rc = slot_update (&tmp_slot);
+               if (rc)
+                       return 0;
+               if (SLOT_PRESENT (tmp_slot->status) && SLOT_POWER (tmp_slot->status))
+                       return 0;
+               i++;
+       }
+       return 1;
+}
+
+/***********************************************************
+ * If the HPC permits and the bus currently empty, tries to set the 
+ * bus speed and mode at the maximum card capability
+ ***********************************************************/
+static int set_bus (struct slot * slot_cur)
+{
+       int rc;
+       u8 speed;
+       u8 cmd = 0x0;
+
+       debug ("%s - entry slot # %d \n", __FUNCTION__, slot_cur->number);
+       if (SET_BUS_STATUS (slot_cur->ctrl) && is_bus_empty (slot_cur)) {
+               rc = slot_update (&slot_cur);
+               if (rc)
+                       return rc;
+               speed = SLOT_SPEED (slot_cur->ext_status);
+               debug ("ext_status = %x, speed = %x\n", slot_cur->ext_status, speed);
+               switch (speed) {
+               case HPC_SLOT_SPEED_33:
+                       cmd = HPC_BUS_33CONVMODE;
+                       break;
+               case HPC_SLOT_SPEED_66:
+                       if (SLOT_PCIX (slot_cur->ext_status))
+                               cmd = HPC_BUS_66PCIXMODE;
+                       else
+                               cmd = HPC_BUS_66CONVMODE;
+                       break;
+               case HPC_SLOT_SPEED_133:
+                       if (slot_cur->bus_on->slot_count > 1)
+                               cmd = HPC_BUS_100PCIXMODE;
+                       else
+                               cmd = HPC_BUS_133PCIXMODE;
+                       break;
+               default:
+                       err ("wrong slot speed \n");
+                       return -ENODEV;
+               }
+               debug ("setting bus speed for slot %d, cmd %x\n", slot_cur->number, cmd);
+               rc = ibmphp_hpc_writeslot (slot_cur, cmd);
+               if (rc)
+                       return rc;
+       }
+       debug ("%s -Exit \n", __FUNCTION__);
+       return 0;
+}
+
+static inline void print_card_capability (struct slot *slot_cur)
+{
+       info ("capability of the card is ");
+       if ((slot_cur->ext_status & CARD_INFO) == PCIX133) 
+               info ("   133 MHz PCI-X \n");
+       else if ((slot_cur->ext_status & CARD_INFO) == PCIX66)
+               info ("    66 MHz PCI-X \n");
+       else if ((slot_cur->ext_status & CARD_INFO) == PCI66)
+               info ("    66 MHz PCI \n");
+       else
+               info ("    33 MHz PCI \n");
+
+}
+
+/* This routine will power on the slot, configure the device(s) and find the
+ * drivers for them.
+ * Parameters: hotplug_slot
+ * Returns: 0 or failure codes
+ */
+static int enable_slot (struct hotplug_slot *hs)
+{
+       int rc, i, rcpr;
+       struct slot *slot_cur;
+       u8 function;
+       u8 faulted = 0;
+       struct pci_func *tmp_func;
+
+       ibmphp_lock_operations ();
+
+       debug ("ENABLING SLOT........ \n");
+       slot_cur = (struct slot *) hs->private;
+
+       if ((rc = validate (slot_cur, ENABLE))) {
+               err ("validate function failed \n");
+               attn_off (slot_cur);    /* need to turn off if was blinking b4 */
+               attn_on (slot_cur);
+               rc = slot_update (&slot_cur);
+               if (rc) {
+                       ibmphp_unlock_operations();
+                       return rc;
+               }
+               ibmphp_update_slot_info (slot_cur);
+                ibmphp_unlock_operations ();
+               return rc;
+       }
+
+       attn_LED_blink (slot_cur);
+       
+       rc = set_bus (slot_cur);
+       if (rc) {
+               err ("was not able to set the bus \n");
+               attn_off (slot_cur);
+               attn_on (slot_cur);
+               ibmphp_unlock_operations ();
+               return -ENODEV;
+       }
+               
+       rc = power_on (slot_cur);
+
+       if (rc) {
+               err ("something wrong when powering up... please see below for details\n");
+               /* need to turn off before on, otherwise, blinking overwrites */
+               attn_off(slot_cur);
+               attn_on (slot_cur);
+               if (slot_update (&slot_cur)) {
+                       attn_off (slot_cur);
+                       attn_on (slot_cur);
+                       ibmphp_unlock_operations ();
+                       return -ENODEV;
+               }
+               /* Check to see the error of why it failed */
+               if (!(SLOT_PWRGD (slot_cur->status)))
+                       err ("power fault occured trying to power up \n");
+               else if (SLOT_BUS_SPEED (slot_cur->status)) {
+                       err ("bus speed mismatch occured.  please check current bus speed and card capability \n");
+                       print_card_capability (slot_cur);
+               } else if (SLOT_BUS_MODE (slot_cur->ext_status)) 
+                       err ("bus mode mismatch occured.  please check current bus mode and card capability \n");
+
+               ibmphp_update_slot_info (slot_cur);
+               ibmphp_unlock_operations (); 
+               return rc;
+       }
+       debug ("after power_on\n");
+
+       rc = slot_update (&slot_cur);
+       if (rc) {
+               attn_off (slot_cur);
+               attn_on (slot_cur);
+               rcpr = power_off (slot_cur);
+               if (rcpr) {
+                       ibmphp_unlock_operations ();
+                       return rcpr;
+               }
+               ibmphp_unlock_operations ();
+               return rc;
+       }
+       
+       if (SLOT_POWER (slot_cur->status) && !(SLOT_PWRGD (slot_cur->status))) {
+               faulted = 1;
+               err ("power fault occured trying to power up...\n");
+       } else if (SLOT_POWER (slot_cur->status) && (SLOT_BUS_SPEED (slot_cur->status))) {
+               faulted = 1;
+               err ("bus speed mismatch occured.  please check current bus speed and card capability \n");
+               print_card_capability (slot_cur);
+       } 
+       /* Don't think this case will happen after above checks... but just in case, for paranoia sake */
+       else if (!(SLOT_POWER (slot_cur->status))) {
+               err ("power on failed... \n");
+               faulted = 1;
+       }
+       if (faulted) {
+               attn_off (slot_cur);    /* need to turn off b4 on */
+               attn_on (slot_cur);
+               rcpr = power_off (slot_cur);
+               if (rcpr) {
+                       ibmphp_unlock_operations ();
+                       return rcpr;
+               }
+                       
+               if (slot_update (&slot_cur)) {
+                       ibmphp_unlock_operations ();
+                       return -ENODEV;
+               }
+               ibmphp_update_slot_info (slot_cur);
+               ibmphp_unlock_operations ();
+               return -EINVAL;
+       }
+
+       slot_cur->func = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
+       if (!slot_cur->func) { /* We cannot do update_slot_info here, since no memory for kmalloc n.e.ways, and update_slot_info allocates some */
+               err ("out of system memory \n");
+               attn_off (slot_cur);
+               attn_on (slot_cur);
+               rcpr = power_off (slot_cur);
+               if (rcpr) {
+                       ibmphp_unlock_operations ();
+                       return rcpr;
+               }
+               ibmphp_unlock_operations ();
+               return -ENOMEM;
+       }
+       memset (slot_cur->func, 0, sizeof (struct pci_func));
+       slot_cur->func->busno = slot_cur->bus;
+       slot_cur->func->device = slot_cur->device;
+       for (i = 0; i < 4; i++)
+               slot_cur->func->irq[i] = slot_cur->irq[i];
+
+       debug ("b4 configure_card, slot_cur->bus = %x, slot_cur->device = %x\n", slot_cur->bus, slot_cur->device);
+
+       if (ibmphp_configure_card (slot_cur->func, slot_cur->number)) {
+               err ("configure_card was unsuccessful... \n");
+               ibmphp_unconfigure_card (&slot_cur, 1); /* true because don't need to actually deallocate resources, just remove references */
+               debug ("after unconfigure_card\n");
+               slot_cur->func = NULL;
+               attn_off (slot_cur);    /* need to turn off in case was blinking */
+               attn_on (slot_cur);
+               rcpr = power_off (slot_cur);
+               if (rcpr) {
+                       ibmphp_unlock_operations ();
+                       return rcpr;
+               }
+               if (slot_update (&slot_cur)) {
+                       ibmphp_unlock_operations();
+                       return -ENODEV;
+               }
+               ibmphp_update_slot_info (slot_cur);
+               ibmphp_unlock_operations ();
+               return -ENOMEM;
+       }
+       function = 0x00;
+       do {
+               tmp_func = ibm_slot_find (slot_cur->bus, slot_cur->func->device, function++);
+               if (tmp_func && !(tmp_func->dev))
+                       ibm_configure_device (tmp_func);
+       } while (tmp_func);
+
+       attn_off (slot_cur);
+       if (slot_update (&slot_cur)) {
+               ibmphp_unlock_operations ();
+               return -EFAULT;
+       }
+       ibmphp_print_test ();
+       rc = ibmphp_update_slot_info (slot_cur);
+       ibmphp_unlock_operations(); 
+       return rc;
+}
+
+/**************************************************************
+* HOT REMOVING ADAPTER CARD                                   *
+* INPUT: POINTER TO THE HOTPLUG SLOT STRUCTURE                *
+* OUTPUT: SUCCESS 0 ; FAILURE: UNCONFIGURE , VALIDATE         *
+          DISABLE POWER ,                                    *
+**************************************************************/
+int ibmphp_disable_slot (struct hotplug_slot *hotplug_slot)
+{
+       int rc;
+       struct slot *slot_cur = (struct slot *) hotplug_slot->private;
+       u8 flag = slot_cur->flag;
+
+       slot_cur->flag = TRUE;
+       debug ("DISABLING SLOT... \n"); 
+               
+       ibmphp_lock_operations (); 
+       if (slot_cur == NULL) {
+               ibmphp_unlock_operations ();
+               return -ENODEV;
+       }
+       if (slot_cur->ctrl == NULL) {
+               ibmphp_unlock_operations ();
+               return -ENODEV;
+       }
+       if (flag == TRUE) {
+               rc = validate (slot_cur, DISABLE);      /* checking if powered off already & valid slot # */
+               if (rc) {
+                       /*  Need to turn off if was blinking b4 */
+                       attn_off (slot_cur);
+                       attn_on (slot_cur);
+                       if (slot_update (&slot_cur)) {
+                               ibmphp_unlock_operations ();
+                               return -EFAULT;
+                       }
+               
+                       ibmphp_update_slot_info (slot_cur);
+                       ibmphp_unlock_operations ();
+                       return rc;
+               }
+       }
+       attn_LED_blink (slot_cur);
+
+       if (slot_cur->func == NULL) {
+               /* We need this for fncs's that were there on bootup */
+               slot_cur->func = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
+               if (!slot_cur->func) {
+                       err ("out of system memory \n");
+                       attn_off (slot_cur);
+                       attn_on (slot_cur);
+                       ibmphp_unlock_operations ();
+                       return -ENOMEM;
+               }
+               memset (slot_cur->func, 0, sizeof (struct pci_func));
+               slot_cur->func->busno = slot_cur->bus;
+               slot_cur->func->device = slot_cur->device;
+       }
+
+       if ((rc = ibm_unconfigure_device (slot_cur->func))) {
+               err ("removing from kernel failed... \n");
+               err ("Please check to see if it was statically linked or is in use otherwise. (perhaps the driver is not 'hot-removable')\n");
+               attn_off (slot_cur);
+               attn_on (slot_cur);
+               ibmphp_unlock_operations ();
+               return rc;
+       }
+
+       rc = ibmphp_unconfigure_card (&slot_cur, 0);
+       slot_cur->func = NULL;
+       debug ("in disable_slot. after unconfigure_card \n");
+       if (rc) {
+               err ("could not unconfigure card.\n");
+               attn_off (slot_cur);    /* need to turn off if was blinking b4 */
+               attn_on (slot_cur);
+
+               if (slot_update (&slot_cur)) {
+                       ibmphp_unlock_operations ();
+                       return -EFAULT;
+               }
+
+               if (flag) 
+                       ibmphp_update_slot_info (slot_cur);
+
+               ibmphp_unlock_operations ();
+               return -EFAULT;
+       }
+
+       rc = ibmphp_hpc_writeslot (hotplug_slot->private, HPC_SLOT_OFF);
+       if (rc) {
+               attn_off (slot_cur);
+               attn_on (slot_cur);
+               if (slot_update (&slot_cur)) {
+                       ibmphp_unlock_operations ();
+                       return -EFAULT;
+               }
+
+               if (flag)
+                       ibmphp_update_slot_info (slot_cur);
+
+               ibmphp_unlock_operations ();
+               return rc;
+       }
+
+       attn_off (slot_cur);
+       if (slot_update (&slot_cur)) {
+               ibmphp_unlock_operations ();
+               return -EFAULT;
+       }
+       if (flag) 
+               rc = ibmphp_update_slot_info (slot_cur);
+       else
+               rc = 0;
+
+       ibmphp_print_test ();
+       ibmphp_unlock_operations();
+       return rc;
+}
+
+struct hotplug_slot_ops ibmphp_hotplug_slot_ops = {
+       owner:                          THIS_MODULE,
+       set_attention_status:           set_attention_status,
+       enable_slot:                    enable_slot,
+       disable_slot:                   ibmphp_disable_slot,
+       hardware_test:                  NULL,
+       get_power_status:               get_power_status,
+       get_attention_status:           get_attention_status,
+       get_latch_status:               get_latch_status,
+       get_adapter_status:             get_adapter_present,
+/*     get_max_bus_speed_status:       get_max_bus_speed,
+       get_max_adapter_speed_status:   get_max_adapter_speed,
+       get_cur_bus_speed_status:       get_cur_bus_speed,
+       get_card_bus_names_status:      get_card_bus_names,
+*/
+};
+
+static void ibmphp_unload (void)
+{
+       free_slots ();
+       debug ("after slots \n");
+       ibmphp_free_resources ();
+       debug ("after resources \n");
+       ibmphp_free_bus_info_queue ();
+       debug ("after bus info \n");
+       ibmphp_free_ebda_hpc_queue ();
+       debug ("after ebda hpc \n");
+       ibmphp_free_ebda_pci_rsrc_queue ();
+       debug ("after ebda pci rsrc \n");
+}
+
+static int __init ibmphp_init (void)
+{
+       int i = 0;
+       int rc = 0;
+
+       init_flag = 1;
+       ibmphp_pci_root_ops = get_root_pci_ops ();
+       if (ibmphp_pci_root_ops == NULL) {
+               err ("cannot read bus operations... will not be able to read the cards.  Please check your system \n");
+               return -ENODEV; 
+       }
+
+       ibmphp_debug = debug;
+
+       ibmphp_hpc_initvars ();
+
+       for (i = 0; i < 16; i++)
+               irqs[i] = 0;
+
+       if ((rc = ibmphp_access_ebda ())) {
+               ibmphp_unload ();
+               return rc;
+       }
+       debug ("after ibmphp_access_ebda () \n");
+
+       if ((rc = ibmphp_rsrc_init ())) {
+               ibmphp_unload ();
+               return rc;
+       }
+       debug ("AFTER Resource & EBDA INITIALIZATIONS \n");
+
+       max_slots = get_max_slots ();
+
+       if (init_ops ()) {
+               ibmphp_unload ();
+               return -ENODEV;
+       }
+       ibmphp_print_test ();
+       if ((rc = ibmphp_hpc_start_poll_thread ())) {
+               ibmphp_unload ();
+               return -ENODEV;
+       }
+
+       /* lock ourselves into memory with a module count of -1 
+        * so that no one can unload us. */
+       MOD_DEC_USE_COUNT;
+
+       info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+       return 0;
+}
+
+static void __exit ibmphp_exit (void)
+{
+       ibmphp_hpc_stop_poll_thread ();
+       debug ("after polling \n");
+       ibmphp_unload ();
+       debug ("done \n");
+}
+
+module_init (ibmphp_init);
+module_exit (ibmphp_exit);
diff --git a/drivers/hotplug/ibmphp_ebda.c b/drivers/hotplug/ibmphp_ebda.c
new file mode 100644 (file)
index 0000000..75a69b6
--- /dev/null
@@ -0,0 +1,851 @@
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Tong Yu, IBM Corporation
+ *
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (c) 2001,2002 IBM Corp.
+ *
+ * All rights reserved.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <gregkh@us.ibm.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include "ibmphp.h"
+
+/*
+ * POST builds data blocks(in this data block definition, a char-1
+ * byte, short(or word)-2 byte, long(dword)-4 byte) in the Extended
+ * BIOS Data Area which describe the configuration of the hot-plug
+ * controllers and resources used by the PCI Hot-Plug devices.
+ *
+ * This file walks EBDA, maps data block from physical addr,
+ * reconstruct linked lists about all system resource(MEM, PFM, IO)
+ * already assigned by POST, as well as linked lists about hot plug
+ * controllers (ctlr#, slot#, bus&slot features...)
+ */
+
+/* Global lists */
+LIST_HEAD (ibmphp_ebda_pci_rsrc_head);
+LIST_HEAD (ibmphp_slot_head);
+
+/* Local variables */
+static struct ebda_hpc_list *hpc_list_ptr;
+static struct ebda_rsrc_list *rsrc_list_ptr;
+static struct rio_table_hdr *rio_table_ptr;
+static LIST_HEAD (ebda_hpc_head);
+static LIST_HEAD (bus_info_head);
+static void *io_mem;
+
+/* Local functions */
+static int ebda_rsrc_controller (void);
+static int ebda_rsrc_rsrc (void);
+static int ebda_rio_table (void);
+
+static struct slot *alloc_ibm_slot (void)
+{
+       struct slot *slot;
+
+       slot = kmalloc (sizeof (struct slot), GFP_KERNEL);
+       if (!slot)
+               return NULL;
+       memset (slot, 0, sizeof (*slot));
+       return slot;
+}
+
+static struct ebda_hpc_list *alloc_ebda_hpc_list (void)
+{
+       struct ebda_hpc_list *list;
+
+       list = kmalloc (sizeof (struct ebda_hpc_list), GFP_KERNEL);
+       if (!list)
+               return NULL;
+       memset (list, 0, sizeof (*list));
+       return list;
+}
+
+static struct controller *alloc_ebda_hpc (u32 slot_count, u32 bus_count)
+{
+       struct controller *controller;
+       struct ebda_hpc_slot *slots;
+       struct ebda_hpc_bus *buses;
+
+       controller = kmalloc (sizeof (struct controller), GFP_KERNEL);
+       if (!controller)
+               return NULL;
+       memset (controller, 0, sizeof (*controller));
+
+       slots = kmalloc (sizeof (struct ebda_hpc_slot) * slot_count, GFP_KERNEL);
+       if (!slots) {
+               kfree (controller);
+               return NULL;
+       }
+       memset (slots, 0, sizeof (*slots) * slot_count);
+       controller->slots = slots;
+
+       buses = kmalloc (sizeof (struct ebda_hpc_bus) * bus_count, GFP_KERNEL);
+       if (!buses) {
+               kfree (controller->slots);
+               kfree (controller);
+               return NULL;
+       }
+       memset (buses, 0, sizeof (*buses) * bus_count);
+       controller->buses = buses;
+
+       return controller;
+}
+
+static void free_ebda_hpc (struct controller *controller)
+{
+       kfree (controller->slots);
+       controller->slots = NULL;
+       kfree (controller->buses);
+       controller->buses = NULL;
+       kfree (controller);
+}
+
+static struct ebda_rsrc_list *alloc_ebda_rsrc_list (void)
+{
+       struct ebda_rsrc_list *list;
+
+       list = kmalloc (sizeof (struct ebda_rsrc_list), GFP_KERNEL);
+       if (!list)
+               return NULL;
+       memset (list, 0, sizeof (*list));
+       return list;
+}
+
+static struct ebda_pci_rsrc *alloc_ebda_pci_rsrc (void)
+{
+       struct ebda_pci_rsrc *resource;
+
+       resource = kmalloc (sizeof (struct ebda_pci_rsrc), GFP_KERNEL);
+       if (!resource)
+               return NULL;
+       memset (resource, 0, sizeof (*resource));
+       return resource;
+}
+
+static void print_bus_info (void)
+{
+       struct bus_info *ptr;
+       struct list_head *ptr1;
+       
+       list_for_each (ptr1, &bus_info_head) {
+               ptr = list_entry (ptr1, struct bus_info, bus_info_list);
+               debug ("%s - slot_min = %x\n", __FUNCTION__, ptr->slot_min);
+               debug ("%s - slot_max = %x\n", __FUNCTION__, ptr->slot_max);
+               debug ("%s - slot_count = %x\n", __FUNCTION__, ptr->slot_count);
+               debug ("%s - bus# = %x\n", __FUNCTION__, ptr->busno);
+               debug ("%s - current_speed = %x\n", __FUNCTION__, ptr->current_speed);
+               debug ("%s - supported_speed = %x\n", __FUNCTION__, ptr->supported_speed);
+               debug ("%s - controller_id = %x\n", __FUNCTION__, ptr->controller_id);
+               debug ("%s - bus_mode = %x\n", __FUNCTION__, ptr->supported_bus_mode);
+       }
+}
+
+static void print_ebda_pci_rsrc (void)
+{
+       struct ebda_pci_rsrc *ptr;
+       struct list_head *ptr1;
+
+       list_for_each (ptr1, &ibmphp_ebda_pci_rsrc_head) {
+               ptr = list_entry (ptr1, struct ebda_pci_rsrc, ebda_pci_rsrc_list);
+               debug ("%s - rsrc type: %x bus#: %x dev_func: %x start addr: %lx end addr: %lx\n", 
+                       __FUNCTION__, ptr->rsrc_type ,ptr->bus_num, ptr->dev_fun,ptr->start_addr, ptr->end_addr);
+       }
+}
+
+static void print_ebda_hpc (void)
+{
+       struct controller *hpc_ptr;
+       struct list_head *ptr1;
+       u16 index;
+
+       list_for_each (ptr1, &ebda_hpc_head) {
+
+               hpc_ptr = list_entry (ptr1, struct controller, ebda_hpc_list); 
+
+               for (index = 0; index < hpc_ptr->slot_count; index++) {
+                       debug ("%s - physical slot#: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_num);
+                       debug ("%s - pci bus# of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_bus_num);
+                       debug ("%s - index into ctlr addr: %x\n", __FUNCTION__, hpc_ptr->slots[index].ctl_index);
+                       debug ("%s - cap of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_cap);
+               }
+
+               for (index = 0; index < hpc_ptr->bus_count; index++) {
+                       debug ("%s - bus# of each bus controlled by this ctlr: %x\n", __FUNCTION__, hpc_ptr->buses[index].bus_num);
+               }
+
+               debug ("%s - type of hpc: %x\n", __FUNCTION__, hpc_ptr->ctlr_type);
+               switch (hpc_ptr->ctlr_type) {
+               case 1:
+                       debug ("%s - bus: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.bus);
+                       debug ("%s - dev_fun: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.dev_fun);
+                       debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq);
+                       break;
+
+               case 0:
+                       debug ("%s - io_start: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_start);
+                       debug ("%s - io_end: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_end);
+                       debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq);
+                       break;
+
+               case 2:
+                       debug ("%s - wpegbbar: %lx\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.wpegbbar);
+                       debug ("%s - i2c_addr: %x\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.i2c_addr);
+                       debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq);
+                       break;
+               }
+       }
+}
+
+int ibmphp_access_ebda (void)
+{
+       u8 format, num_ctlrs, rio_complete, hs_complete;
+       u16 ebda_seg, num_entries, next_offset, offset, blk_id, sub_addr, rc, re, rc_id, re_id, base;
+
+
+       rio_complete = 0;
+       hs_complete = 0;
+
+       io_mem = ioremap ((0x40 << 4) + 0x0e, 2);
+       if (!io_mem )
+               return -ENOMEM;
+       ebda_seg = readw (io_mem);
+       iounmap (io_mem);
+       debug ("returned ebda segment: %x\n", ebda_seg);
+       
+       io_mem = ioremap (ebda_seg<<4, 65000);
+       if (!io_mem )
+               return -ENOMEM;
+       next_offset = 0x180;
+
+       for (;;) {
+               offset = next_offset;
+               next_offset = readw (io_mem + offset);  /* offset of next blk */
+
+               offset += 2;
+               if (next_offset == 0)   /* 0 indicate it's last blk */
+                       break;
+               blk_id = readw (io_mem + offset);       /* this blk id */
+
+               offset += 2;
+               /* check if it is hot swap block or rio block */
+               if (blk_id != 0x4853 && blk_id != 0x4752)
+                       continue;
+               /* found hs table */
+               if (blk_id == 0x4853) {
+                       debug ("now enter hot swap block---\n");
+                       debug ("hot blk id: %x\n", blk_id);
+                       format = readb (io_mem + offset);
+
+                       offset += 1;
+                       if (format != 4) {
+                               iounmap (io_mem);
+                               return -ENODEV;
+                       }
+                       debug ("hot blk format: %x\n", format);
+                       /* hot swap sub blk */
+                       base = offset;
+
+                       sub_addr = base;
+                       re = readw (io_mem + sub_addr); /* next sub blk */
+
+                       sub_addr += 2;
+                       rc_id = readw (io_mem + sub_addr);      /* sub blk id */
+
+                       sub_addr += 2;
+                       if (rc_id != 0x5243) {
+                               iounmap (io_mem);
+                               return -ENODEV;
+                       }
+                       /* rc sub blk signature  */
+                       num_ctlrs = readb (io_mem + sub_addr);
+
+                       sub_addr += 1;
+                       hpc_list_ptr = alloc_ebda_hpc_list ();
+                       if (!hpc_list_ptr) {
+                               iounmap (io_mem);
+                               return -ENOMEM;
+                       }
+                       hpc_list_ptr->format = format;
+                       hpc_list_ptr->num_ctlrs = num_ctlrs;
+                       hpc_list_ptr->phys_addr = sub_addr;     /*  offset of RSRC_CONTROLLER blk */
+                       debug ("info about hpc descriptor---\n");
+                       debug ("hot blk format: %x\n", format);
+                       debug ("num of controller: %x\n", num_ctlrs);
+                       debug ("offset of hpc data structure enteries: %x\n ", sub_addr);
+
+                       sub_addr = base + re;   /* re sub blk */
+                       rc = readw (io_mem + sub_addr); /* next sub blk */
+
+                       sub_addr += 2;
+                       re_id = readw (io_mem + sub_addr);      /* sub blk id */
+
+                       sub_addr += 2;
+                       if (re_id != 0x5245) {
+                               iounmap (io_mem);
+                               return -ENODEV;
+                       }
+
+                       /* signature of re */
+                       num_entries = readw (io_mem + sub_addr);
+
+                       sub_addr += 2;  /* offset of RSRC_ENTRIES blk */
+                       rsrc_list_ptr = alloc_ebda_rsrc_list ();
+                       if (!rsrc_list_ptr ) {
+                               iounmap (io_mem);
+                               return -ENOMEM;
+                       }
+                       rsrc_list_ptr->format = format;
+                       rsrc_list_ptr->num_entries = num_entries;
+                       rsrc_list_ptr->phys_addr = sub_addr;
+
+                       debug ("info about rsrc descriptor---\n");
+                       debug ("format: %x\n", format);
+                       debug ("num of rsrc: %x\n", num_entries);
+                       debug ("offset of rsrc data structure enteries: %x\n ", sub_addr);
+
+                       hs_complete = 1;
+               }
+               /* found rio table */
+               else if (blk_id == 0x4752) {
+                       debug ("now enter io table ---\n");
+                       debug ("rio blk id: %x\n", blk_id);
+
+                       rio_table_ptr = kmalloc (sizeof (struct rio_table_hdr), GFP_KERNEL);
+                       if (!rio_table_ptr)
+                               return -ENOMEM; 
+                       memset (rio_table_ptr, 0, sizeof (struct rio_table_hdr) );
+                       rio_table_ptr->ver_num = readb (io_mem + offset);
+                       rio_table_ptr->scal_count = readb (io_mem + offset + 1);
+                       rio_table_ptr->riodev_count = readb (io_mem + offset + 2);
+                       rio_table_ptr->offset = offset +3 ;
+                       
+                       debug ("info about rio table hdr ---\n");
+                       debug ("ver_num: %x\nscal_count: %x\nriodev_count: %x\noffset of rio table: %x\n ", rio_table_ptr->ver_num, rio_table_ptr->scal_count, rio_table_ptr->riodev_count, rio_table_ptr->offset);
+
+                       rio_complete = 1;
+               }
+
+               if (hs_complete && rio_complete) {
+                       rc = ebda_rsrc_controller ();
+                       if (rc) {
+                               iounmap(io_mem);
+                               return rc;
+                       }
+                       rc = ebda_rsrc_rsrc ();
+                       if (rc) {
+                               iounmap(io_mem);
+                               return rc;
+                       }
+                       rc = ebda_rio_table ();
+                       if (rc) {
+                               iounmap(io_mem);
+                               return rc;
+                       }       
+                       iounmap (io_mem);
+                       return 0;
+               }
+       }
+       iounmap (io_mem);
+       return -ENODEV;
+}
+
+
+/*
+ * map info (ctlr-id, slot count, slot#.. bus count, bus#, ctlr type...) of
+ * each hpc from physical address to a list of hot plug controllers based on
+ * hpc descriptors.
+ */
+static int ebda_rsrc_controller (void)
+{
+       u16 addr, addr_slot, addr_bus;
+       u8 ctlr_id, temp, bus_index;
+       u16 ctlr, slot, bus;
+       u16 slot_num, bus_num, index;
+       struct hotplug_slot *hp_slot_ptr;
+       struct controller *hpc_ptr;
+       struct ebda_hpc_bus *bus_ptr;
+       struct ebda_hpc_slot *slot_ptr;
+       struct bus_info *bus_info_ptr1, *bus_info_ptr2;
+       int rc;
+
+       addr = hpc_list_ptr->phys_addr;
+       for (ctlr = 0; ctlr < hpc_list_ptr->num_ctlrs; ctlr++) {
+               bus_index = 1;
+               ctlr_id = readb (io_mem + addr);
+               addr += 1;
+               slot_num = readb (io_mem + addr);
+
+               addr += 1;
+               addr_slot = addr;       /* offset of slot structure */
+               addr += (slot_num * 4);
+
+               bus_num = readb (io_mem + addr);
+
+               addr += 1;
+               addr_bus = addr;        /* offset of bus */
+               addr += (bus_num * 9);  /* offset of ctlr_type */
+               temp = readb (io_mem + addr);
+
+               addr += 1;
+               /* init hpc structure */
+               hpc_ptr = alloc_ebda_hpc (slot_num, bus_num);
+               if (!hpc_ptr ) {
+                       iounmap (io_mem);
+                       return -ENOMEM;
+               }
+               hpc_ptr->ctlr_id = ctlr_id;
+               hpc_ptr->ctlr_relative_id = ctlr;
+               hpc_ptr->slot_count = slot_num;
+               hpc_ptr->bus_count = bus_num;
+               debug ("now enter ctlr data struture ---\n");
+               debug ("ctlr id: %x\n", ctlr_id);
+               debug ("ctlr_relative_id: %x\n", hpc_ptr->ctlr_relative_id);
+               debug ("count of slots controlled by this ctlr: %x\n", slot_num);
+               debug ("count of buses controlled by this ctlr: %x\n", bus_num);
+
+               /* init slot structure, fetch slot, bus, cap... */
+               slot_ptr = hpc_ptr->slots;
+               for (slot = 0; slot < slot_num; slot++) {
+                       slot_ptr->slot_num = readb (io_mem + addr_slot);
+                       slot_ptr->slot_bus_num = readb (io_mem + addr_slot + slot_num);
+                       slot_ptr->ctl_index = readb (io_mem + addr_slot + 2*slot_num);
+                       slot_ptr->slot_cap = readb (io_mem + addr_slot + 3*slot_num);
+
+                       // create bus_info lined list --- if only one slot per bus: slot_min = slot_max 
+
+                       bus_info_ptr2 = ibmphp_find_same_bus_num (slot_ptr->slot_bus_num);
+                       if (!bus_info_ptr2) {
+                               bus_info_ptr1 = (struct bus_info *) kmalloc (sizeof (struct bus_info), GFP_KERNEL);
+                               if (!bus_info_ptr1) {
+                                       iounmap (io_mem);
+                                       return -ENOMEM;
+                               }
+                               memset (bus_info_ptr1, 0, sizeof (struct bus_info));
+                               bus_info_ptr1->slot_min = slot_ptr->slot_num;
+                               bus_info_ptr1->slot_max = slot_ptr->slot_num;
+                               bus_info_ptr1->slot_count += 1;
+                               bus_info_ptr1->busno = slot_ptr->slot_bus_num;
+                               bus_info_ptr1->index = bus_index++;
+                               bus_info_ptr1->current_speed = 0xff;
+                               bus_info_ptr1->current_bus_mode = 0xff;
+                               if ( ((slot_ptr->slot_cap) & EBDA_SLOT_133_MAX) == EBDA_SLOT_133_MAX )
+                                       bus_info_ptr1->supported_speed =  3;
+                               else if ( ((slot_ptr->slot_cap) & EBDA_SLOT_100_MAX) == EBDA_SLOT_100_MAX )
+                                       bus_info_ptr1->supported_speed =  2;
+                               else if ( ((slot_ptr->slot_cap) & EBDA_SLOT_66_MAX) == EBDA_SLOT_66_MAX )
+                                       bus_info_ptr1->supported_speed =  1;
+                               bus_info_ptr1->controller_id = hpc_ptr->ctlr_id;
+                               if ( ((slot_ptr->slot_cap) & EBDA_SLOT_PCIX_CAP) == EBDA_SLOT_PCIX_CAP )
+                                       bus_info_ptr1->supported_bus_mode = 1;
+                               else
+                                       bus_info_ptr1->supported_bus_mode =0;
+
+
+                               list_add_tail (&bus_info_ptr1->bus_info_list, &bus_info_head);
+
+                       } else {
+                               bus_info_ptr2->slot_min = min (bus_info_ptr2->slot_min, slot_ptr->slot_num);
+                               bus_info_ptr2->slot_max = max (bus_info_ptr2->slot_max, slot_ptr->slot_num);
+                               bus_info_ptr2->slot_count += 1;
+
+                       }
+
+                       // end of creating the bus_info linked list
+
+                       slot_ptr++;
+                       addr_slot += 1;
+               }
+
+               /* init bus structure */
+               bus_ptr = hpc_ptr->buses;
+               for (bus = 0; bus < bus_num; bus++) {
+                       bus_ptr->bus_num = readb (io_mem + addr_bus);
+                       bus_ptr++;
+                       addr_bus += 1;
+               }
+
+               hpc_ptr->ctlr_type = temp;
+
+               switch (hpc_ptr->ctlr_type) {
+                       case 1:
+                               hpc_ptr->u.pci_ctlr.bus = readb (io_mem + addr);
+                               hpc_ptr->u.pci_ctlr.dev_fun = readb (io_mem + addr + 1);
+                               hpc_ptr->irq = readb (io_mem + addr + 2);
+                               addr += 3; 
+                               break;
+
+                       case 0:
+                               hpc_ptr->u.isa_ctlr.io_start = readw (io_mem + addr);
+                               hpc_ptr->u.isa_ctlr.io_end = readw (io_mem + addr + 2);
+                               hpc_ptr->irq = readb (io_mem + addr + 4);
+                               addr += 5;
+                               break;
+
+                       case 2:
+                               hpc_ptr->u.wpeg_ctlr.wpegbbar = readl (io_mem + addr);
+                               hpc_ptr->u.wpeg_ctlr.i2c_addr = readb (io_mem + addr + 4);
+                               /* following 2 lines for testing purpose */
+                               if (hpc_ptr->u.wpeg_ctlr.i2c_addr == 0) 
+                                       hpc_ptr->ctlr_type = 4; 
+                               
+
+                               hpc_ptr->irq = readb (io_mem + addr + 5);
+                               addr += 6;
+                               break;
+                       case 4:
+                               hpc_ptr->u.wpeg_ctlr.wpegbbar = readl (io_mem + addr);
+                               hpc_ptr->u.wpeg_ctlr.i2c_addr = readb (io_mem + addr + 4);
+                               hpc_ptr->irq = readb (io_mem + addr + 5);
+                               addr += 6;
+                               break;
+                       default:
+                               iounmap (io_mem);
+                               return -ENODEV;
+               }
+               /* following 3 line: Now our driver only supports I2c ctlrType */
+               if ((hpc_ptr->ctlr_type != 2) && (hpc_ptr->ctlr_type != 4)) {
+                       err ("Please run this driver on ibm xseries440\n ");
+                       return -ENODEV;
+               }
+
+               hpc_ptr->revision = 0xff;
+               hpc_ptr->options = 0xff;
+
+               // register slots with hpc core as well as create linked list of ibm slot
+               for (index = 0; index < hpc_ptr->slot_count; index++) {
+
+                       hp_slot_ptr = (struct hotplug_slot *) kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL);
+                       if (!hp_slot_ptr) {
+                               iounmap (io_mem);
+                               return -ENOMEM;
+                       }
+                       memset (hp_slot_ptr, 0, sizeof (struct hotplug_slot));
+
+                       hp_slot_ptr->info = (struct hotplug_slot_info *) kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
+                       if (!hp_slot_ptr->info) {
+                               iounmap (io_mem);
+                               kfree (hp_slot_ptr);
+                               return -ENOMEM;
+                       }
+                       memset (hp_slot_ptr->info, 0, sizeof (struct hotplug_slot_info));
+
+                       hp_slot_ptr->name = (char *) kmalloc (10, GFP_KERNEL);
+                       if (!hp_slot_ptr->name) {
+                               iounmap (io_mem);
+                               kfree (hp_slot_ptr->info);
+                               kfree (hp_slot_ptr);
+                               return -ENOMEM;
+                       }
+
+                       hp_slot_ptr->private = alloc_ibm_slot ();
+                       if (!hp_slot_ptr->private) {
+                               iounmap (io_mem);
+                               kfree (hp_slot_ptr->name);
+                               kfree (hp_slot_ptr->info);
+                               kfree (hp_slot_ptr);
+                               return -ENOMEM;
+                       }
+
+                       ((struct slot *)hp_slot_ptr->private)->flag = TRUE;
+                       snprintf (hp_slot_ptr->name, 10, "%d", hpc_ptr->slots[index].slot_num);
+
+                       ((struct slot *) hp_slot_ptr->private)->capabilities = hpc_ptr->slots[index].slot_cap;
+                       ((struct slot *) hp_slot_ptr->private)->bus = hpc_ptr->slots[index].slot_bus_num;
+
+                       bus_info_ptr1 = ibmphp_find_same_bus_num (hpc_ptr->slots[index].slot_bus_num);
+                       if (!bus_info_ptr1) {
+                               iounmap (io_mem);
+                               return -ENODEV;
+                       }
+                       ((struct slot *) hp_slot_ptr->private)->bus_on = bus_info_ptr1;
+                       bus_info_ptr1 = NULL;
+                       ((struct slot *) hp_slot_ptr->private)->ctrl = hpc_ptr;
+
+
+                       ((struct slot *) hp_slot_ptr->private)->ctlr_index = hpc_ptr->slots[index].ctl_index;
+                       ((struct slot *) hp_slot_ptr->private)->number = hpc_ptr->slots[index].slot_num;
+
+                       ((struct slot *) hp_slot_ptr->private)->hotplug_slot = hp_slot_ptr;
+
+                       rc = ibmphp_hpc_fillhpslotinfo (hp_slot_ptr);
+                       if (rc) {
+                               iounmap (io_mem);
+                               return rc;
+                       }
+
+                       rc = ibmphp_init_devno ((struct slot **) &hp_slot_ptr->private);
+                       if (rc) {
+                               iounmap (io_mem);
+                               return rc;
+                       }
+                       hp_slot_ptr->ops = &ibmphp_hotplug_slot_ops;
+
+                       pci_hp_register (hp_slot_ptr);
+
+                       // end of registering ibm slot with hotplug core
+
+                       list_add (& ((struct slot *)(hp_slot_ptr->private))->ibm_slot_list, &ibmphp_slot_head);
+               }
+
+               print_bus_info ();
+               list_add (&hpc_ptr->ebda_hpc_list, &ebda_hpc_head );
+
+       }                       /* each hpc  */
+       print_ebda_hpc ();
+       return 0;
+}
+
+/* 
+ * map info (bus, devfun, start addr, end addr..) of i/o, memory,
+ * pfm from the physical addr to a list of resource.
+ */
+static int ebda_rsrc_rsrc (void)
+{
+       u16 addr;
+       short rsrc;
+       u8 type, rsrc_type;
+       struct ebda_pci_rsrc *rsrc_ptr;
+
+       addr = rsrc_list_ptr->phys_addr;
+       debug ("now entering rsrc land\n");
+       debug ("offset of rsrc: %x\n", rsrc_list_ptr->phys_addr);
+
+       for (rsrc = 0; rsrc < rsrc_list_ptr->num_entries; rsrc++) {
+               type = readb (io_mem + addr);
+
+               addr += 1;
+               rsrc_type = type & EBDA_RSRC_TYPE_MASK;
+
+               if (rsrc_type == EBDA_IO_RSRC_TYPE) {
+                       rsrc_ptr = alloc_ebda_pci_rsrc ();
+                       if (!rsrc_ptr) {
+                               iounmap (io_mem);
+                               return -ENOMEM;
+                       }
+                       rsrc_ptr->rsrc_type = type;
+
+                       rsrc_ptr->bus_num = readb (io_mem + addr);
+                       rsrc_ptr->dev_fun = readb (io_mem + addr + 1);
+                       rsrc_ptr->start_addr = readw (io_mem + addr + 2);
+                       rsrc_ptr->end_addr = readw (io_mem + addr + 4);
+                       addr += 6;
+
+                       debug ("rsrc from io type ----\n");
+                       debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %lx end addr: %lx\n",
+                               rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr);
+
+                       list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head);
+               }
+
+               if (rsrc_type == EBDA_MEM_RSRC_TYPE || rsrc_type == EBDA_PFM_RSRC_TYPE) {
+                       rsrc_ptr = alloc_ebda_pci_rsrc ();
+                       if (!rsrc_ptr ) {
+                               iounmap (io_mem);
+                               return -ENOMEM;
+                       }
+                       rsrc_ptr->rsrc_type = type;
+
+                       rsrc_ptr->bus_num = readb (io_mem + addr);
+                       rsrc_ptr->dev_fun = readb (io_mem + addr + 1);
+                       rsrc_ptr->start_addr = readl (io_mem + addr + 2);
+                       rsrc_ptr->end_addr = readl (io_mem + addr + 6);
+                       addr += 10;
+
+                       debug ("rsrc from mem or pfm ---\n");
+                       debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %lx end addr: %lx\n", 
+                               rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr);
+
+                       list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head);
+               }
+       }
+       kfree (rsrc_list_ptr);
+       rsrc_list_ptr = NULL;
+       print_ebda_pci_rsrc ();
+       return 0;
+}
+
+/*
+ * map info of scalability details and rio details from physical address
+ */
+static int ebda_rio_table(void)
+{
+       u16 offset;
+       u8 i;
+       struct scal_detail *scal_detail_ptr;
+       struct rio_detail *rio_detail_ptr;
+
+       offset = rio_table_ptr->offset;
+       for (i = 0; i < rio_table_ptr->scal_count; i++) {
+               
+               scal_detail_ptr = kmalloc (sizeof (struct scal_detail), GFP_KERNEL );
+               if (!scal_detail_ptr )
+                       return -ENOMEM;
+               memset (scal_detail_ptr, 0, sizeof (struct scal_detail) );
+               scal_detail_ptr->node_id = readb (io_mem + offset);
+               scal_detail_ptr->cbar = readl (io_mem+ offset + 1);
+               scal_detail_ptr->port0_node_connect = readb (io_mem + 5);
+               scal_detail_ptr->port0_port_connect = readb (io_mem + 6);
+               scal_detail_ptr->port1_node_connect = readb (io_mem + 7);
+               scal_detail_ptr->port1_port_connect = readb (io_mem + 8);
+               scal_detail_ptr->port2_node_connect = readb (io_mem + 9);
+               scal_detail_ptr->port2_port_connect = readb (io_mem + 10);
+               debug ("node_id: %x\ncbar: %x\nport0_node: %x\nport0_port: %x\nport1_node: %x\nport1_port: %x\nport2_node: %x\nport2_port: %x\n", scal_detail_ptr->node_id, scal_detail_ptr->cbar, scal_detail_ptr->port0_node_connect, scal_detail_ptr->port0_port_connect, scal_detail_ptr->port1_node_connect, scal_detail_ptr->port1_port_connect, scal_detail_ptr->port2_node_connect, scal_detail_ptr->port2_port_connect);
+//             list_add (&scal_detail_ptr->scal_detail_list, &scal_detail_head);
+               offset += 11;
+       }
+       for (i=0; i < rio_table_ptr->riodev_count; i++) {
+               rio_detail_ptr = kmalloc (sizeof (struct rio_detail), GFP_KERNEL );
+               if (!rio_detail_ptr )
+                       return -ENOMEM;
+               memset (rio_detail_ptr, 0, sizeof (struct rio_detail) );
+               rio_detail_ptr->rio_node_id = readb (io_mem + offset );
+               rio_detail_ptr->bbar = readl (io_mem + offset + 1);
+               rio_detail_ptr->rio_type = readb (io_mem + offset + 5);
+               rio_detail_ptr->owner_id = readb (io_mem + offset + 6);
+               rio_detail_ptr->port0_node_connect = readb (io_mem + offset + 7);
+               rio_detail_ptr->port0_port_connect = readb (io_mem + offset + 8);
+               rio_detail_ptr->port1_node_connect = readb (io_mem + offset + 9);
+               rio_detail_ptr->port1_port_connect = readb (io_mem + offset + 10);
+               rio_detail_ptr->first_slot_num = readb (io_mem + offset + 11);
+               rio_detail_ptr->status = readb (io_mem + offset + 12);
+               debug ("rio_node_id: %x\nbbar: %x\nrio_type: %x\nowner_id: %x\nport0_node: %x\nport0_port: %x\nport1_node: %x\nport1_port: %x\nfirst_slot_num: %x\nstatus: %x\n", rio_detail_ptr->rio_node_id, rio_detail_ptr->bbar, rio_detail_ptr->rio_type, rio_detail_ptr->owner_id, rio_detail_ptr->port0_node_connect, rio_detail_ptr->port0_port_connect, rio_detail_ptr->port1_node_connect, rio_detail_ptr->port1_port_connect, rio_detail_ptr->first_slot_num, rio_detail_ptr->status);
+               offset += 13;
+       }
+       return 0;
+}
+
+u16 ibmphp_get_total_controllers (void)
+{
+       return hpc_list_ptr->num_ctlrs;
+}
+
+struct slot *ibmphp_get_slot_from_physical_num (u8 physical_num)
+{
+       struct slot *slot;
+       struct list_head *list;
+
+       list_for_each (list, &ibmphp_slot_head) {
+               slot = list_entry (list, struct slot, ibm_slot_list);
+               if (slot->number == physical_num)
+                       return slot;
+       }
+       return NULL;
+}
+
+/* To find:
+ *     - the smallest slot number
+ *     - the largest slot number
+ *     - the total number of the slots based on each bus
+ *       (if only one slot per bus slot_min = slot_max )
+ */
+struct bus_info *ibmphp_find_same_bus_num (u32 num)
+{
+       struct bus_info *ptr;
+       struct list_head  *ptr1;
+
+       list_for_each (ptr1, &bus_info_head) {
+               ptr = list_entry (ptr1, struct bus_info, bus_info_list); 
+               if (ptr->busno == num) 
+                        return ptr;
+       }
+       return NULL;
+}
+
+/*  Finding relative bus number, in order to map corresponding
+ *  bus register
+ */
+int ibmphp_get_bus_index (u8 num)
+{
+       struct bus_info *ptr;
+       struct list_head  *ptr1;
+
+       list_for_each (ptr1, &bus_info_head) {
+               ptr = list_entry (ptr1, struct bus_info, bus_info_list);
+               if (ptr->busno == num)  
+                       return ptr->index;
+       }
+       return -ENODEV;
+}
+
+void ibmphp_free_bus_info_queue (void)
+{
+       struct bus_info *bus_info;
+       struct list_head *list;
+       struct list_head *next;
+
+       list_for_each_safe (list, next, &bus_info_head ) {
+               bus_info = list_entry (list, struct bus_info, bus_info_list);
+               kfree (bus_info);
+       }
+}
+
+/*
+ * Calculate the total hot pluggable slots controlled by total hpcs
+ */
+/*
+int ibmphp_get_total_hp_slots (void)
+{
+       struct ebda_hpc *ptr;
+       int slot_num = 0;
+
+       ptr = ebda_hpc_head;
+       while (ptr != NULL) {
+               slot_num += ptr->slot_count;
+               ptr = ptr->next;
+       }
+       return slot_num;
+}
+*/
+
+void ibmphp_free_ebda_hpc_queue (void)
+{
+       struct controller *controller;
+       struct list_head *list;
+       struct list_head *next;
+
+       list_for_each_safe (list, next, &ebda_hpc_head) {
+               controller = list_entry (list, struct controller, ebda_hpc_list);
+               free_ebda_hpc (controller);
+       }
+}
+
+void ibmphp_free_ebda_pci_rsrc_queue (void)
+{
+       struct ebda_pci_rsrc *resource;
+       struct list_head *list;
+       struct list_head *next;
+
+       list_for_each_safe (list, next, &ibmphp_ebda_pci_rsrc_head) {
+               resource = list_entry (list, struct ebda_pci_rsrc, ebda_pci_rsrc_list);
+               kfree (resource);
+               resource = NULL;
+       }
+}
+
diff --git a/drivers/hotplug/ibmphp_hpc.c b/drivers/hotplug/ibmphp_hpc.c
new file mode 100644 (file)
index 0000000..87eac06
--- /dev/null
@@ -0,0 +1,1135 @@
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Jyoti Shah, IBM Corporation
+ *
+ * Copyright (c) 2001,2001 IBM Corp.
+ *
+ * All rights reserved.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <gregkh@us.ibm.com>
+ *                  <jshah@us.ibm.com>
+ *
+ */
+
+//#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/smp_lock.h>
+#include "ibmphp.h"
+
+#define POLL_NO                0x01
+#define POLL_YES       0x00
+
+static int to_debug = FALSE;
+#define debug_polling(fmt, arg...)     do { if (to_debug) debug (fmt, arg); } while (0)
+
+//----------------------------------------------------------------------------
+// timeout values
+//----------------------------------------------------------------------------
+#define CMD_COMPLETE_TOUT_SEC  60      // give HPC 60 sec to finish cmd
+#define HPC_CTLR_WORKING_TOUT  60      // give HPC 60 sec to finish cmd
+#define HPC_GETACCESS_TIMEOUT  60      // seconds
+#define POLL_INTERVAL_SEC      2       // poll HPC every 2 seconds
+#define POLL_LATCH_CNT         5       // poll latch 5 times, then poll slots
+
+//----------------------------------------------------------------------------
+// Winnipeg Architected Register Offsets
+//----------------------------------------------------------------------------
+#define WPG_I2CMBUFL_OFFSET    0x08    // I2C Message Buffer Low
+#define WPG_I2CMOSUP_OFFSET    0x10    // I2C Master Operation Setup Reg
+#define WPG_I2CMCNTL_OFFSET    0x20    // I2C Master Control Register
+#define WPG_I2CPARM_OFFSET     0x40    // I2C Parameter Register
+#define WPG_I2CSTAT_OFFSET     0x70    // I2C Status Register
+
+//----------------------------------------------------------------------------
+// Winnipeg Store Type commands (Add this commands to the register offset)
+//----------------------------------------------------------------------------
+#define WPG_I2C_AND            0x1000  // I2C AND operation
+#define WPG_I2C_OR             0x2000  // I2C OR operation
+
+//----------------------------------------------------------------------------
+// Command set for I2C Master Operation Setup Regisetr
+//----------------------------------------------------------------------------
+#define WPG_READATADDR_MASK    0x00010000      // read,bytes,I2C shifted,index
+#define WPG_WRITEATADDR_MASK   0x40010000      // write,bytes,I2C shifted,index
+#define WPG_READDIRECT_MASK    0x10010000
+#define WPG_WRITEDIRECT_MASK   0x60010000
+
+
+//----------------------------------------------------------------------------
+// bit masks for I2C Master Control Register
+//----------------------------------------------------------------------------
+#define WPG_I2CMCNTL_STARTOP_MASK      0x00000002      // Start the Operation
+
+//----------------------------------------------------------------------------
+//
+//----------------------------------------------------------------------------
+#define WPG_I2C_IOREMAP_SIZE   0x2044  // size of linear address interval
+#define WPG_CTLR_MAX           0x01    // max controllers
+#define WPG_SLOT_MAX           0x06    // max slots
+#define WPG_CTLR_SLOT_MAX      0x06    // max slots per controller
+#define WPG_FIRST_CTLR         0x00    // index of the controller
+
+//----------------------------------------------------------------------------
+// command index
+//----------------------------------------------------------------------------
+#define WPG_1ST_SLOT_INDEX     0x01    // index - 1st slot for ctlr
+#define WPG_CTLR_INDEX         0x0F    // index - ctlr
+#define WPG_1ST_EXTSLOT_INDEX  0x10    // index - 1st ext slot for ctlr
+#define WPG_1ST_BUS_INDEX      0x1F    // index - 1st bus for ctlr
+
+//----------------------------------------------------------------------------
+// macro utilities
+//----------------------------------------------------------------------------
+// if bits 20,22,25,26,27,29,30 are OFF return TRUE
+#define HPC_I2CSTATUS_CHECK(s) ((u8)((s & 0x00000A76) ? FALSE : TRUE))
+
+// return code 0:poll slots, 1-POLL_LATCH_CNT:poll latch register
+#define INCREMENT_POLLCNT(i)   ((i < POLL_LATCH_CNT) ? i++ : (i=0))
+//----------------------------------------------------------------------------
+// global variables
+//----------------------------------------------------------------------------
+static int ibmphp_shutdown;
+static int tid_poll;
+static int stop_polling;               // 2 values: poll, don't poll
+static struct semaphore sem_hpcaccess; // lock access to HPC
+static struct semaphore semOperations; // lock all operations and
+                                       // access to data structures
+static struct semaphore sem_exit;      // make sure polling thread goes away
+static struct semaphore sem_poll;      // make sure poll is idle 
+//----------------------------------------------------------------------------
+// local function prototypes
+//----------------------------------------------------------------------------
+static u8 ctrl_read (struct controller *, void *, u8);
+static u8 ctrl_write (struct controller *, void *, u8, u8);
+static u8 hpc_writecmdtoindex (u8, u8);
+static u8 hpc_readcmdtoindex (u8, u8);
+static void get_hpc_access (void);
+static void free_hpc_access (void);
+static void poll_hpc (void);
+static int update_slot (struct slot *, u8);
+static int process_changeinstatus (struct slot *, struct slot *);
+static int process_changeinlatch (u8, u8);
+static int hpc_poll_thread (void *);
+static int hpc_wait_ctlr_notworking (int, struct controller *, void *, u8 *);
+//----------------------------------------------------------------------------
+
+
+/*----------------------------------------------------------------------
+* Name:    ibmphp_hpc_initvars
+*
+* Action:  initialize semaphores and variables
+*---------------------------------------------------------------------*/
+void ibmphp_hpc_initvars (void)
+{
+       debug ("%s - Entry\n", __FUNCTION__);
+
+       init_MUTEX (&sem_hpcaccess);
+       init_MUTEX (&semOperations);
+       init_MUTEX_LOCKED (&sem_exit);
+       init_MUTEX_LOCKED (&sem_poll);
+       stop_polling = POLL_YES;
+       to_debug = FALSE;
+       ibmphp_shutdown = FALSE;
+       tid_poll = 0;
+
+       debug ("%s - Exit\n", __FUNCTION__);
+}
+
+/*----------------------------------------------------------------------
+* Name:    ctrl_read
+*
+* Action:  read from HPC over I2C
+*
+*---------------------------------------------------------------------*/
+static u8 ctrl_read (struct controller *ctlr_ptr, void *WPGBbar, u8 index)
+{
+       u8 status;
+       int i;
+       void *wpg_addr;         // base addr + offset
+       ulong wpg_data,         // data to/from WPG LOHI format
+       ultemp, data;           // actual data HILO format
+
+
+       debug_polling ("%s - Entry WPGBbar[%lx] index[%x] \n", __FUNCTION__, (ulong) WPGBbar, index);
+
+       //--------------------------------------------------------------------
+       // READ - step 1
+       // read at address, byte length, I2C address (shifted), index
+       // or read direct, byte length, index
+       if (ctlr_ptr->ctlr_type == 0x02) {
+               data = WPG_READATADDR_MASK;
+               // fill in I2C address
+               ultemp = (ulong) ctlr_ptr->u.wpeg_ctlr.i2c_addr;
+               ultemp = ultemp >> 1;
+               data |= (ultemp << 8);
+
+               // fill in index
+               data |= (ulong) index;
+       } else if (ctlr_ptr->ctlr_type == 0x04) {
+               data = WPG_READDIRECT_MASK;
+
+               // fill in index
+               ultemp = (ulong) index;
+               ultemp = ultemp << 8;
+               data |= ultemp;
+       } else {
+               err ("this controller type is not supported \n");
+               return HPC_ERROR;
+       }
+
+       wpg_data = swab32 (data);       // swap data before writing
+       (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMOSUP_OFFSET;
+       writel (wpg_data, wpg_addr);
+
+       //--------------------------------------------------------------------
+       // READ - step 2 : clear the message buffer
+       data = 0x00000000;
+       wpg_data = swab32 (data);
+       (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET;
+       writel (wpg_data, wpg_addr);
+
+       //--------------------------------------------------------------------
+       // READ - step 3 : issue start operation, I2C master control bit 30:ON
+       //                 2020 : [20] OR operation at [20] offset 0x20
+       data = WPG_I2CMCNTL_STARTOP_MASK;
+       wpg_data = swab32 (data);
+       (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET + (ulong) WPG_I2C_OR;
+       writel (wpg_data, wpg_addr);
+
+       //--------------------------------------------------------------------
+       // READ - step 4 : wait until start operation bit clears
+       i = CMD_COMPLETE_TOUT_SEC;
+       while (i) {
+               long_delay (1 * HZ / 100);
+               (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET;
+               wpg_data = readl (wpg_addr);
+               data = swab32 (wpg_data);
+               if (!(data & WPG_I2CMCNTL_STARTOP_MASK))
+                       break;
+               i--;
+       }
+       if (i == 0) {
+               debug ("%s - Error : WPG timeout\n", __FUNCTION__);
+               return HPC_ERROR;
+       }
+       //--------------------------------------------------------------------
+       // READ - step 5 : read I2C status register
+       i = CMD_COMPLETE_TOUT_SEC;
+       while (i) {
+               long_delay (1 * HZ / 100);
+               (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CSTAT_OFFSET;
+               wpg_data = readl (wpg_addr);
+               data = swab32 (wpg_data);
+               if (HPC_I2CSTATUS_CHECK (data))
+                       break;
+               i--;
+       }
+       if (i == 0) {
+               debug ("ctrl_read - Exit Error:I2C timeout\n");
+               return HPC_ERROR;
+       }
+
+       //--------------------------------------------------------------------
+       // READ - step 6 : get DATA
+       (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET;
+       wpg_data = readl (wpg_addr);
+       data = swab32 (wpg_data);
+
+       status = (u8) data;
+
+       debug_polling ("%s - Exit index[%x] status[%x]\n", __FUNCTION__, index, status);
+
+       return (status);
+}
+
+/*----------------------------------------------------------------------
+* Name:    ctrl_write
+*
+* Action:  write to HPC over I2C
+*
+* Return   0 or error codes
+*---------------------------------------------------------------------*/
+static u8 ctrl_write (struct controller *ctlr_ptr, void *WPGBbar, u8 index, u8 cmd)
+{
+       u8 rc;
+       void *wpg_addr;         // base addr + offset
+       ulong wpg_data,         // data to/from WPG LOHI format 
+       ultemp, data;           // actual data HILO format
+       int i;
+
+
+       debug_polling ("%s - Entry WPGBbar[%lx] index[%x] cmd[%x]\n",
+                      __FUNCTION__, (ulong) WPGBbar, index, cmd);
+
+       rc = 0;
+       //--------------------------------------------------------------------
+       // WRITE - step 1
+       // write at address, byte length, I2C address (shifted), index
+       // or write direct, byte length, index
+       data = 0x00000000;
+
+       if (ctlr_ptr->ctlr_type == 0x02) {
+               data = WPG_WRITEATADDR_MASK;
+               // fill in I2C address
+               ultemp = (ulong) ctlr_ptr->u.wpeg_ctlr.i2c_addr;
+               ultemp = ultemp >> 1;
+               data |= (ultemp << 8);
+
+               // fill in index
+               data |= (ulong) index;
+       } else if (ctlr_ptr->ctlr_type == 0x04) {
+               data = WPG_WRITEDIRECT_MASK;
+
+               // fill in index
+               ultemp = (ulong) index;
+               ultemp = ultemp << 8;
+               data |= ultemp;
+       } else {
+               err ("this controller type is not supported \n");
+               return HPC_ERROR;
+       }
+
+       wpg_data = swab32 (data);       // swap data before writing
+       (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMOSUP_OFFSET;
+       writel (wpg_data, wpg_addr);
+
+       //--------------------------------------------------------------------
+       // WRITE - step 2 : clear the message buffer
+       data = 0x00000000 | (ulong) cmd;
+       wpg_data = swab32 (data);
+       (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET;
+       writel (wpg_data, wpg_addr);
+
+       //--------------------------------------------------------------------
+       // WRITE - step 3 : issue start operation,I2C master control bit 30:ON
+       //                 2020 : [20] OR operation at [20] offset 0x20
+       data = WPG_I2CMCNTL_STARTOP_MASK;
+       wpg_data = swab32 (data);
+       (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET + (ulong) WPG_I2C_OR;
+       writel (wpg_data, wpg_addr);
+
+       //--------------------------------------------------------------------
+       // WRITE - step 4 : wait until start operation bit clears
+       i = CMD_COMPLETE_TOUT_SEC;
+       while (i) {
+               long_delay (1 * HZ / 100);
+               (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET;
+               wpg_data = readl (wpg_addr);
+               data = swab32 (wpg_data);
+               if (!(data & WPG_I2CMCNTL_STARTOP_MASK))
+                       break;
+               i--;
+       }
+       if (i == 0) {
+               debug ("%s - Exit Error:WPG timeout\n", __FUNCTION__);
+               rc = HPC_ERROR;
+       }
+
+       //--------------------------------------------------------------------
+       // WRITE - step 5 : read I2C status register
+       i = CMD_COMPLETE_TOUT_SEC;
+       while (i) {
+               long_delay (1 * HZ / 100);
+               (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CSTAT_OFFSET;
+               wpg_data = readl (wpg_addr);
+               data = swab32 (wpg_data);
+               if (HPC_I2CSTATUS_CHECK (data))
+                       break;
+               i--;
+       }
+       if (i == 0) {
+               debug ("ctrl_read - Error : I2C timeout\n");
+               rc = HPC_ERROR;
+       }
+
+       debug_polling ("%s Exit rc[%x]\n", __FUNCTION__, rc);
+       return (rc);
+}
+
+/*----------------------------------------------------------------------
+* Name:    hpc_writecmdtoindex()
+*
+* Action:  convert a write command to proper index within a controller
+*
+* Return   index, HPC_ERROR
+*---------------------------------------------------------------------*/
+static u8 hpc_writecmdtoindex (u8 cmd, u8 index)
+{
+       u8 rc;
+
+       switch (cmd) {
+       case HPC_CTLR_ENABLEIRQ:        // 0x00.N.15
+       case HPC_CTLR_CLEARIRQ: // 0x06.N.15
+       case HPC_CTLR_RESET:    // 0x07.N.15
+       case HPC_CTLR_IRQSTEER: // 0x08.N.15
+       case HPC_CTLR_DISABLEIRQ:       // 0x01.N.15
+       case HPC_ALLSLOT_ON:    // 0x11.N.15
+       case HPC_ALLSLOT_OFF:   // 0x12.N.15
+               rc = 0x0F;
+               break;
+
+       case HPC_SLOT_OFF:      // 0x02.Y.0-14
+       case HPC_SLOT_ON:       // 0x03.Y.0-14
+       case HPC_SLOT_ATTNOFF:  // 0x04.N.0-14
+       case HPC_SLOT_ATTNON:   // 0x05.N.0-14
+       case HPC_SLOT_BLINKLED: // 0x13.N.0-14
+               rc = index;
+               break;
+
+       case HPC_BUS_33CONVMODE:
+       case HPC_BUS_66CONVMODE:
+       case HPC_BUS_66PCIXMODE:
+       case HPC_BUS_100PCIXMODE:
+       case HPC_BUS_133PCIXMODE:
+               rc = index + WPG_1ST_BUS_INDEX - 1;
+               break;
+
+       default:
+               err ("hpc_writecmdtoindex - Error invalid cmd[%x]\n", cmd);
+               rc = HPC_ERROR;
+       }
+
+       return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    hpc_readcmdtoindex()
+*
+* Action:  convert a read command to proper index within a controller
+*
+* Return   index, HPC_ERROR
+*---------------------------------------------------------------------*/
+static u8 hpc_readcmdtoindex (u8 cmd, u8 index)
+{
+       u8 rc;
+
+       switch (cmd) {
+       case READ_CTLRSTATUS:
+               rc = 0x0F;
+               break;
+       case READ_SLOTSTATUS:
+       case READ_ALLSTAT:
+               rc = index;
+               break;
+       case READ_EXTSLOTSTATUS:
+               rc = index + WPG_1ST_EXTSLOT_INDEX;
+               break;
+       case READ_BUSSTATUS:
+               rc = index + WPG_1ST_BUS_INDEX - 1;
+               break;
+       case READ_SLOTLATCHLOWREG:
+               rc = 0x28;
+               break;
+       case READ_REVLEVEL:
+               rc = 0x25;
+               break;
+       case READ_HPCOPTIONS:
+               rc = 0x27;
+               break;
+       default:
+               rc = HPC_ERROR;
+       }
+       return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    HPCreadslot()
+*
+* Action:  issue a READ command to HPC
+*
+* Input:   pslot   - can not be NULL for READ_ALLSTAT
+*          pstatus - can be NULL for READ_ALLSTAT
+*
+* Return   0 or error codes
+*---------------------------------------------------------------------*/
+int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus)
+{
+       void *wpg_bbar;
+       struct controller *ctlr_ptr;
+       struct list_head *pslotlist;
+       u8 index, status;
+       int rc = 0;
+       int busindex;
+
+       debug_polling ("%s - Entry pslot[%lx] cmd[%x] pstatus[%lx]\n",
+                      __FUNCTION__, (ulong) pslot, cmd, (ulong) pstatus);
+
+       if ((pslot == NULL)
+           || ((pstatus == NULL) && (cmd != READ_ALLSTAT) && (cmd != READ_BUSSTATUS))) {
+               rc = -EINVAL;
+               err ("%s - Error invalid pointer, rc[%d]\n", __FUNCTION__, rc);
+               return rc;
+       }
+
+       if (cmd == READ_BUSSTATUS) {
+               busindex = ibmphp_get_bus_index (pslot->bus);
+               if (busindex < 0) {
+                       rc = -EINVAL;
+                       err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc);
+                       return rc;
+               } else
+                       index = (u8) busindex;
+       } else
+               index = pslot->ctlr_index;
+
+       index = hpc_readcmdtoindex (cmd, index);
+
+       if (index == HPC_ERROR) {
+               rc = -EINVAL;
+               err ("%s - Exit Error:invalid index, rc[%d]\n", __FUNCTION__, rc);
+               return rc;
+       }
+
+       ctlr_ptr = pslot->ctrl;
+
+       get_hpc_access ();
+
+       //--------------------------------------------------------------------
+       // map physical address to logical address
+       //--------------------------------------------------------------------
+       wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE);
+
+       //--------------------------------------------------------------------
+       // check controller status before reading
+       //--------------------------------------------------------------------
+       rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status);
+       if (!rc) {
+               switch (cmd) {
+               case READ_ALLSTAT:
+                       // update the slot structure
+                       pslot->ctrl->status = status;
+                       pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index);
+                       rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar,
+                                                      &status);
+                       if (!rc)
+                               pslot->ext_status = ctrl_read (ctlr_ptr, wpg_bbar, index + WPG_1ST_EXTSLOT_INDEX);
+
+                       break;
+
+               case READ_SLOTSTATUS:
+                       // DO NOT update the slot structure
+                       *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
+                       break;
+
+               case READ_EXTSLOTSTATUS:
+                       // DO NOT update the slot structure
+                       *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
+                       break;
+
+               case READ_CTLRSTATUS:
+                       // DO NOT update the slot structure
+                       *pstatus = status;
+                       break;
+
+               case READ_BUSSTATUS:
+                       pslot->busstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
+                       break;
+               case READ_REVLEVEL:
+                       *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
+                       break;
+               case READ_HPCOPTIONS:
+                       *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
+                       break;
+               case READ_SLOTLATCHLOWREG:
+                       // DO NOT update the slot structure
+                       *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
+                       break;
+
+                       // Not used
+               case READ_ALLSLOT:
+                       list_for_each (pslotlist, &ibmphp_slot_head) {
+                               pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
+                               index = pslot->ctlr_index;
+                               rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr,
+                                                               wpg_bbar, &status);
+                               if (!rc) {
+                                       pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index);
+                                       rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT,
+                                                                       ctlr_ptr, wpg_bbar, &status);
+                                       if (!rc)
+                                               pslot->ext_status =
+                                                   ctrl_read (ctlr_ptr, wpg_bbar,
+                                                               index + WPG_1ST_EXTSLOT_INDEX);
+                               } else {
+                                       err ("%s - Error ctrl_read failed\n", __FUNCTION__);
+                                       rc = -EINVAL;
+                                       break;
+                               }
+                       }
+                       break;
+               default:
+                       rc = -EINVAL;
+                       break;
+               }
+       }
+       //--------------------------------------------------------------------
+       // cleanup
+       //--------------------------------------------------------------------
+       iounmap (wpg_bbar);     // remove physical to logical address mapping
+       free_hpc_access ();
+
+       debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc);
+       return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    ibmphp_hpc_writeslot()
+*
+* Action: issue a WRITE command to HPC
+*---------------------------------------------------------------------*/
+int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd)
+{
+       void *wpg_bbar;
+       struct controller *ctlr_ptr;
+       u8 index, status;
+       int busindex;
+       u8 done;
+       int rc = 0;
+       int timeout;
+
+       debug_polling ("%s - Entry pslot[%lx] cmd[%x]\n", __FUNCTION__, (ulong) pslot, cmd);
+       if (pslot == NULL) {
+               rc = -EINVAL;
+               err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc);
+               return rc;
+       }
+
+       if ((cmd == HPC_BUS_33CONVMODE) || (cmd == HPC_BUS_66CONVMODE) ||
+               (cmd == HPC_BUS_66PCIXMODE) || (cmd == HPC_BUS_100PCIXMODE) ||
+               (cmd == HPC_BUS_133PCIXMODE)) {
+               busindex = ibmphp_get_bus_index (pslot->bus);
+               if (busindex < 0) {
+                       rc = -EINVAL;
+                       err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc);
+                       return rc;
+               } else
+                       index = (u8) busindex;
+       } else
+               index = pslot->ctlr_index;
+
+       index = hpc_writecmdtoindex (cmd, index);
+
+       if (index == HPC_ERROR) {
+               rc = -EINVAL;
+               err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc);
+               return rc;
+       }
+
+       ctlr_ptr = pslot->ctrl;
+
+       get_hpc_access ();
+
+       //--------------------------------------------------------------------
+       // map physical address to logical address
+       //--------------------------------------------------------------------
+       wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE);
+
+       debug ("%s - ctlr id[%x] physical[%lx] logical[%lx] i2c[%x]\n", __FUNCTION__,
+               ctlr_ptr->ctlr_id, (ulong) (ctlr_ptr->u.wpeg_ctlr.wpegbbar), (ulong) wpg_bbar,
+               ctlr_ptr->u.wpeg_ctlr.i2c_addr);
+
+       //--------------------------------------------------------------------
+       // check controller status before writing
+       //--------------------------------------------------------------------
+       rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status);
+       if (!rc) {
+
+               ctrl_write (ctlr_ptr, wpg_bbar, index, cmd);
+
+               //--------------------------------------------------------------------
+               // check controller is still not working on the command
+               //--------------------------------------------------------------------
+               timeout = CMD_COMPLETE_TOUT_SEC;
+               done = FALSE;
+               while (!done) {
+                       rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar,
+                                                       &status);
+                       if (!rc) {
+                               if (NEEDTOCHECK_CMDSTATUS (cmd)) {
+                                       if (CTLR_FINISHED (status) == HPC_CTLR_FINISHED_YES)
+                                               done = TRUE;
+                               } else
+                                       done = TRUE;
+                       }
+                       if (!done) {
+                               long_delay (1 * HZ);
+                               if (timeout < 1) {
+                                       done = TRUE;
+                                       err ("%s - Error command complete timeout\n", __FUNCTION__);
+                                       rc = -EFAULT;
+                               } else
+                                       timeout--;
+                       }
+               }
+               ctlr_ptr->status = status;
+       }
+       // cleanup
+       iounmap (wpg_bbar);     // remove physical to logical address mapping
+       free_hpc_access ();
+
+       debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc);
+       return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    get_hpc_access()
+*
+* Action: make sure only one process can access HPC at one time
+*---------------------------------------------------------------------*/
+static void get_hpc_access (void)
+{
+       down (&sem_hpcaccess);
+}
+
+/*----------------------------------------------------------------------
+* Name:    free_hpc_access()
+*---------------------------------------------------------------------*/
+void free_hpc_access (void)
+{
+       up (&sem_hpcaccess);
+}
+
+/*----------------------------------------------------------------------
+* Name:    ibmphp_lock_operations()
+*
+* Action: make sure only one process can change the data structure
+*---------------------------------------------------------------------*/
+void ibmphp_lock_operations (void)
+{
+       down (&semOperations);
+       stop_polling = POLL_NO;
+       to_debug = TRUE;
+
+       /* waiting for polling to actually stop */
+       down (&sem_poll);
+}
+
+/*----------------------------------------------------------------------
+* Name:    ibmphp_unlock_operations()
+*---------------------------------------------------------------------*/
+void ibmphp_unlock_operations (void)
+{
+       debug ("%s - Entry\n", __FUNCTION__);
+       stop_polling = POLL_YES;
+       to_debug = FALSE;
+       up (&semOperations);
+       debug ("%s - Exit\n", __FUNCTION__);
+}
+
+/*----------------------------------------------------------------------
+* Name:    poll_hpc()
+*---------------------------------------------------------------------*/
+static void poll_hpc (void)
+{
+       struct slot myslot, *pslot = NULL;
+       struct list_head *pslotlist;
+       int rc;
+       u8 oldlatchlow = 0x00;
+       u8 curlatchlow = 0x00;
+       int pollcnt = 0;
+       u8 ctrl_count = 0x00;
+
+       debug ("poll_hpc - Entry\n");
+
+       while (!ibmphp_shutdown) {
+               if (stop_polling) {
+                       debug ("poll_hpc - stop_polling\n");
+                       up (&sem_poll); 
+                       /* to prevent deadlock */
+                       if (ibmphp_shutdown)
+                               break;
+                       /* to make the thread sleep */
+                       down (&semOperations);
+                       up (&semOperations);
+                       debug ("poll_hpc - after stop_polling sleep\n");
+               } else {
+                       if (pollcnt) {
+                               // only poll the latch register
+                               oldlatchlow = curlatchlow;
+
+                               ctrl_count = 0x00;
+                               list_for_each (pslotlist, &ibmphp_slot_head) {
+                                       if (ctrl_count >= ibmphp_get_total_controllers())
+                                               break;
+                                       pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
+                                       if (pslot->ctrl->ctlr_relative_id == ctrl_count) {
+                                               ctrl_count++;
+                                               if (READ_SLOT_LATCH (pslot->ctrl)) {
+                                                       rc = ibmphp_hpc_readslot (pslot,
+                                                                                 READ_SLOTLATCHLOWREG,
+                                                                                 &curlatchlow);
+                                                       if (oldlatchlow != curlatchlow)
+                                                               process_changeinlatch (oldlatchlow,
+                                                                                       curlatchlow);
+                                               }
+                                       }
+                               }
+                       } else {
+                               list_for_each (pslotlist, &ibmphp_slot_head) {
+                                       if (stop_polling)
+                                               break;
+                                       pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
+                                       // make a copy of the old status
+                                       memcpy ((void *) &myslot, (void *) pslot,
+                                               sizeof (struct slot));
+                                       rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL);
+                                       if ((myslot.status != pslot->status)
+                                           || (myslot.ext_status != pslot->ext_status))
+                                               process_changeinstatus (pslot, &myslot);
+                               }
+
+                               if (!stop_polling) {
+                                       ctrl_count = 0x00;
+                                       list_for_each (pslotlist, &ibmphp_slot_head) {
+                                               if (ctrl_count >= ibmphp_get_total_controllers())
+                                                       break;
+                                               pslot =
+                                                   list_entry (pslotlist, struct slot,
+                                                               ibm_slot_list);
+                                               if (pslot->ctrl->ctlr_relative_id == ctrl_count) {
+                                                       ctrl_count++;
+                                                       if (READ_SLOT_LATCH (pslot->ctrl))
+                                                               rc = ibmphp_hpc_readslot (pslot,
+                                                                                         READ_SLOTLATCHLOWREG,
+                                                                                         &curlatchlow);
+                                               }
+                                       }
+                               }
+                       }
+                       INCREMENT_POLLCNT (pollcnt);
+                       long_delay (POLL_INTERVAL_SEC * HZ);    // snooze
+               }
+       }
+       up (&sem_poll);
+       up (&sem_exit);
+       debug ("poll_hpc - Exit\n");
+}
+
+
+/* ----------------------------------------------------------------------
+ *  Name:    ibmphp_hpc_fillhpslotinfo(hotplug_slot * phpslot)
+ *
+ *  Action:  fill out the hotplug_slot info
+ *
+ *  Input:   pointer to hotplug_slot
+ *
+ *  Return
+ *  Value:   0 or error codes
+ *-----------------------------------------------------------------------*/
+int ibmphp_hpc_fillhpslotinfo (struct hotplug_slot *phpslot)
+{
+       int rc = 0;
+       struct slot *pslot;
+
+       if (phpslot && phpslot->private) {
+               pslot = (struct slot *) phpslot->private;
+               rc = update_slot (pslot, (u8) TRUE);
+               if (!rc) {
+
+                       // power - enabled:1  not:0
+                       phpslot->info->power_status = SLOT_POWER (pslot->status);
+
+                       // attention - off:0, on:1, blinking:2
+                       phpslot->info->attention_status = SLOT_ATTN (pslot->status, pslot->ext_status);
+
+                       // latch - open:1 closed:0
+                       phpslot->info->latch_status = SLOT_LATCH (pslot->status);
+
+                       // pci board - present:1 not:0
+                       if (SLOT_PRESENT (pslot->status))
+                               phpslot->info->adapter_status = 1;
+                       else
+                               phpslot->info->adapter_status = 0;
+/*
+                       if (pslot->bus_on->supported_bus_mode
+                               && (pslot->bus_on->supported_speed == BUS_SPEED_66))
+                               phpslot->info->max_bus_speed_status = BUS_SPEED_66PCIX;
+                       else
+                               phpslot->info->max_bus_speed_status = pslot->bus_on->supported_speed;
+*/             } else
+                       rc = -EINVAL;
+       } else
+               rc = -EINVAL;
+
+       return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    update_slot
+*
+* Action:  fill out slot status and extended status, controller status
+*
+* Input:   pointer to slot struct
+*---------------------------------------------------------------------*/
+static int update_slot (struct slot *pslot, u8 update)
+{
+       int rc = 0;
+
+       debug ("%s - Entry pslot[%lx]\n", __FUNCTION__, (ulong) pslot);
+       rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL);
+       debug ("%s - Exit rc[%d]\n", __FUNCTION__, rc);
+       return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    process_changeinstatus
+*
+* Action:  compare old and new slot status, process the change in status
+*
+* Input:   pointer to slot struct, old slot struct
+*
+* Return   0 or error codes
+* Value:
+*
+* Side
+* Effects: None.
+*
+* Notes:
+*---------------------------------------------------------------------*/
+static int process_changeinstatus (struct slot *pslot, struct slot *poldslot)
+{
+       u8 status;
+       int rc = 0;
+       u8 disable = FALSE;
+       u8 update = FALSE;
+
+       debug ("process_changeinstatus - Entry pslot[%lx], poldslot[%lx]\n", (ulong) pslot,
+              (ulong) poldslot);
+
+       // bit 0 - HPC_SLOT_POWER
+       if ((pslot->status & 0x01) != (poldslot->status & 0x01))
+               /* ????????? DO WE NEED TO UPDATE BUS SPEED INFO HERE ??? */
+               update = TRUE;
+
+       // bit 1 - HPC_SLOT_CONNECT
+       // ignore
+
+       // bit 2 - HPC_SLOT_ATTN
+       if ((pslot->status & 0x04) != (poldslot->status & 0x04))
+               update = TRUE;
+
+       // bit 3 - HPC_SLOT_PRSNT2
+       // bit 4 - HPC_SLOT_PRSNT1
+       if (((pslot->status & 0x08) != (poldslot->status & 0x08))
+               || ((pslot->status & 0x10) != (poldslot->status & 0x10)))
+               update = TRUE;
+
+       // bit 5 - HPC_SLOT_PWRGD
+       if ((pslot->status & 0x20) != (poldslot->status & 0x20))
+               // OFF -> ON: ignore, ON -> OFF: disable slot
+               if (poldslot->status & 0x20)
+                       disable = TRUE;
+
+       // bit 6 - HPC_SLOT_BUS_SPEED
+       // ignore
+
+       // bit 7 - HPC_SLOT_LATCH
+       if ((pslot->status & 0x80) != (poldslot->status & 0x80)) {
+               update = TRUE;
+               // OPEN -> CLOSE
+               if (pslot->status & 0x80) {
+                       if (SLOT_POWER (pslot->status)) {
+                               // power goes on and off after closing latch
+                               // check again to make sure power is still ON
+                               long_delay (1 * HZ);
+                               rc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &status);
+                               if (SLOT_POWER (status))
+                                       update = TRUE;
+                               else    // overwrite power in pslot to OFF
+                                       pslot->status &= ~HPC_SLOT_POWER;
+                       }
+               }
+               // CLOSE -> OPEN 
+               else if ((SLOT_POWER (poldslot->status) == HPC_SLOT_POWER_ON)
+                       || (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED)) {
+                       disable = TRUE;
+               }
+               // else - ignore
+       }
+       // bit 4 - HPC_SLOT_BLINK_ATTN
+       if ((pslot->ext_status & 0x08) != (poldslot->ext_status & 0x08))
+               update = TRUE;
+
+       if (disable) {
+               debug ("process_changeinstatus - disable slot\n");
+               pslot->flag = FALSE;
+               rc = ibmphp_disable_slot (pslot->hotplug_slot);
+       }
+
+       if (update || disable) {
+               ibmphp_update_slot_info (pslot);
+       }
+
+       debug ("%s - Exit rc[%d] disable[%x] update[%x]\n", __FUNCTION__, rc, disable, update);
+
+       return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    process_changeinlatch
+*
+* Action:  compare old and new latch reg status, process the change
+*
+* Input:   old and current latch register status
+*
+* Return   0 or error codes
+* Value:
+*---------------------------------------------------------------------*/
+static int process_changeinlatch (u8 old, u8 new)
+{
+       struct slot myslot, *pslot;
+       u8 i;
+       u8 mask;
+       int rc = 0;
+
+       debug ("%s - Entry old[%x], new[%x]\n", __FUNCTION__, old, new);
+       // bit 0 reserved, 0 is LSB, check bit 1-6 for 6 slots
+
+       for (i = 1; i <= 6; i++) {
+               mask = 0x01 << i;
+               if ((mask & old) != (mask & new)) {
+                       pslot = ibmphp_get_slot_from_physical_num (i);
+                       if (pslot) {
+                               memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
+                               rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL);
+                               debug ("%s - call process_changeinstatus for slot[%d]\n", __FUNCTION__, i);
+                               process_changeinstatus (pslot, &myslot);
+                       } else {
+                               rc = -EINVAL;
+                               err ("%s - Error bad pointer for slot[%d]\n", __FUNCTION__, i);
+                       }
+               }
+       }
+       debug ("%s - Exit rc[%d]\n", __FUNCTION__, rc);
+       return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    hpc_poll_thread
+*
+* Action:  polling
+*
+* Return   0
+* Value:
+*---------------------------------------------------------------------*/
+static int hpc_poll_thread (void *data)
+{
+       debug ("%s - Entry\n", __FUNCTION__);
+       lock_kernel ();
+       daemonize ();
+       reparent_to_init ();
+
+       //  New name
+       strcpy (current->comm, "hpc_poll");
+
+       unlock_kernel ();
+
+       poll_hpc ();
+
+       tid_poll = 0;
+       debug ("%s - Exit\n", __FUNCTION__);
+       return 0;
+}
+
+
+/*----------------------------------------------------------------------
+* Name:    ibmphp_hpc_start_poll_thread
+*
+* Action:  start polling thread
+*---------------------------------------------------------------------*/
+int ibmphp_hpc_start_poll_thread (void)
+{
+       int rc = 0;
+
+       debug ("ibmphp_hpc_start_poll_thread - Entry\n");
+
+       tid_poll = kernel_thread (hpc_poll_thread, 0, 0);
+       if (tid_poll < 0) {
+               err ("ibmphp_hpc_start_poll_thread - Error, thread not started\n");
+               rc = -1;
+       }
+
+       debug ("ibmphp_hpc_start_poll_thread - Exit tid_poll[%d] rc[%d]\n", tid_poll, rc);
+       return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name:    ibmphp_hpc_stop_poll_thread
+*
+* Action:  stop polling thread and cleanup
+*---------------------------------------------------------------------*/
+void ibmphp_hpc_stop_poll_thread (void)
+{
+       debug ("ibmphp_hpc_stop_poll_thread - Entry\n");
+
+       ibmphp_shutdown = TRUE;
+       ibmphp_lock_operations ();
+
+       // wait for poll thread to exit
+       down (&sem_exit);
+
+       // cleanup
+       free_hpc_access ();
+       ibmphp_unlock_operations ();
+       up (&sem_poll);
+       up (&sem_exit);
+
+       debug ("ibmphp_hpc_stop_poll_thread - Exit\n");
+}
+
+/*----------------------------------------------------------------------
+* Name:    hpc_wait_ctlr_notworking
+*
+* Action:  wait until the controller is in a not working state
+*
+* Return   0, HPC_ERROR
+* Value:
+*---------------------------------------------------------------------*/
+static int hpc_wait_ctlr_notworking (int timeout, struct controller *ctlr_ptr, void *wpg_bbar,
+                                   u8 * pstatus)
+{
+       int rc = 0;
+       u8 done = FALSE;
+
+       debug_polling ("hpc_wait_ctlr_notworking - Entry timeout[%d]\n", timeout);
+
+       while (!done) {
+               *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, WPG_CTLR_INDEX);
+               if (*pstatus == HPC_ERROR) {
+                       rc = HPC_ERROR;
+                       done = TRUE;
+               }
+               if (CTLR_WORKING (*pstatus) == HPC_CTLR_WORKING_NO)
+                       done = TRUE;
+               if (!done) {
+                       long_delay (1 * HZ);
+                       if (timeout < 1) {
+                               done = TRUE;
+                               err ("HPCreadslot - Error ctlr timeout\n");
+                               rc = HPC_ERROR;
+                       } else
+                               timeout--;
+               }
+       }
+       debug_polling ("hpc_wait_ctlr_notworking - Exit rc[%x] status[%x]\n", rc, *pstatus);
+       return rc;
+}
diff --git a/drivers/hotplug/ibmphp_pci.c b/drivers/hotplug/ibmphp_pci.c
new file mode 100644 (file)
index 0000000..6273c44
--- /dev/null
@@ -0,0 +1,1719 @@
+/*
+ * IBM Hot Plug Controller Driver
+ * 
+ * Written By: Irene Zubarev, IBM Corporation
+ * 
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (c) 2001,2002 IBM Corp.
+ *
+ * All rights reserved.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <gregkh@us.ibm.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include "ibmphp.h"
+
+
+static int configure_device(struct pci_func *);
+static int configure_bridge(struct pci_func **, u8);
+static struct res_needed *scan_behind_bridge(struct pci_func *, u8);
+static int add_new_bus (struct bus_node *, struct resource_node *, struct resource_node *, struct resource_node *, u8);
+static u8 find_sec_number (u8 primary_busno, u8 slotno);
+
+/*
+ * NOTE..... If BIOS doesn't provide default routing, we assign:
+ * 9 for SCSI, 10 for LAN adapters, and 11 for everything else. 
+ * If adapter is bridged, then we assign 11 to it and devices behind it.
+ * We also assign the same irq numbers for multi function devices.
+ * These are PIC mode, so shouldn't matter n.e.ways (hopefully)
+ */
+static void assign_alt_irq (struct pci_func * cur_func, u8 class_code)
+{
+       int j = 0;
+       for (j = 0; j < 4; j++) {
+               if (cur_func->irq[j] == 0xff) {
+                       switch (class_code) {
+                               case PCI_BASE_CLASS_STORAGE:
+                                       cur_func->irq[j] = SCSI_IRQ;
+                                       break;
+                               case PCI_BASE_CLASS_NETWORK:
+                                       cur_func->irq[j] = LAN_IRQ;
+                                       break;
+                               default:
+                                       cur_func->irq[j] = OTHER_IRQ;
+                                       break;
+                       }
+               }
+       }
+}
+
+/*
+ * Configures the device to be added (will allocate needed resources if it
+ * can), the device can be a bridge or a regular pci device, can also be
+ * multi-functional
+ * 
+ * Input: function to be added
+ * 
+ * TO DO:  The error case with Multifunction device or multi function bridge,
+ * if there is an error, will need to go through all previous functions and 
+ * unconfigure....or can add some code into unconfigure_card....
+ */
+int ibmphp_configure_card (struct pci_func *func, u8 slotno)
+{
+       u16 vendor_id;
+       u32 class;
+       u8 class_code;
+       u8 hdr_type, device, sec_number;
+       u8 function;
+       struct pci_func *newfunc;       /* for multi devices */
+       struct pci_func *cur_func, *prev_func;
+       int rc, i, j;
+       int cleanup_count;
+       u8 flag;
+       u8 valid_device = 0x00; /* to see if we are able to read from card any device info at all */
+
+       debug ("inside configure_card, func->busno = %x \n", func->busno);
+
+       device = func->device;
+       cur_func = func;
+
+       /* We only get bus and device from IRQ routing table.  So at this point,
+        * func->busno is correct, and func->device contains only device (at the 5 
+        * highest bits)
+        */
+
+       /* For every function on the card */
+       for (function = 0x00; function < 0x08; function++) {
+               cur_func->function = function;
+
+               debug ("inside the loop, cur_func->busno = %x, cur_func->device = %x, cur_func->funcion = %x\n", cur_func->busno, device, function);
+
+               pci_read_config_word_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_VENDOR_ID, &vendor_id);
+
+               debug ("vendor_id is %x\n", vendor_id);
+               if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
+                       /* found correct device!!! */
+                       debug ("found valid device, vendor_id = %x\n", vendor_id);
+
+                       ++valid_device;
+
+                       /* header: x x x x x x x x
+                        *         | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge
+                        *         |_=> 0 = single function device, 1 = multi-function device
+                        */
+
+                       pci_read_config_byte_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_HEADER_TYPE, &hdr_type);
+                       pci_read_config_dword_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_CLASS_REVISION, &class);
+
+                       class_code = class >> 24;
+                       debug ("hrd_type = %x, class = %x, class_code %x \n", hdr_type, class, class_code);
+                       class >>= 8;    /* to take revision out, class = class.subclass.prog i/f */
+                       if (class == PCI_CLASS_NOT_DEFINED_VGA) {
+                               err ("The device %x is VGA compatible and as is not supported for hot plugging. "
+                                    "Please choose another device.\n", cur_func->device);
+                               return -ENODEV;
+                       } else if (class == PCI_CLASS_DISPLAY_VGA) {
+                               err ("The device %x is not supported for hot plugging. "
+                                    "Please choose another device.\n", cur_func->device);
+                               return -ENODEV;
+                       }
+                       switch (hdr_type) {
+                               case PCI_HEADER_TYPE_NORMAL:
+                                       debug ("single device case.... vendor id = %x, hdr_type = %x, class = %x\n", vendor_id, hdr_type, class);
+                                       assign_alt_irq (cur_func, class_code);
+                                       if ((rc = configure_device (cur_func)) < 0) {
+                                               /* We need to do this in case some other BARs were properly inserted */
+                                               err ("was not able to configure devfunc %x on bus %x. \n",
+                                                    cur_func->device, cur_func->busno);
+                                               cleanup_count = 6;
+                                               goto error;
+                                       }       
+                                       cur_func->next = NULL;
+                                       function = 0x8;
+                                       break;
+                               case PCI_HEADER_TYPE_MULTIDEVICE:
+                                       assign_alt_irq (cur_func, class_code);
+                                       if ((rc = configure_device (cur_func)) < 0) {
+                                               /* We need to do this in case some other BARs were properly inserted */
+                                               err ("was not able to configure devfunc %x on bus %x...bailing out\n",
+                                                    cur_func->device, cur_func->busno);
+                                               cleanup_count = 6;
+                                               goto error;
+                                       }
+                                       newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
+                                       if (!newfunc) {
+                                               err ("out of system memory \n");
+                                               return -ENOMEM;
+                                       }
+                                       memset (newfunc, 0, sizeof (struct pci_func));
+                                       newfunc->busno = cur_func->busno;
+                                       newfunc->device = device;
+                                       cur_func->next = newfunc;
+                                       cur_func = newfunc;
+                                       for (j = 0; j < 4; j++)
+                                               newfunc->irq[j] = cur_func->irq[j];
+                                       break;
+                               case PCI_HEADER_TYPE_MULTIBRIDGE:
+                                       class >>= 8;
+                                       if (class != PCI_CLASS_BRIDGE_PCI) {
+                                               err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. "
+                                                    "Please insert another card.\n", cur_func->device);
+                                               return -ENODEV;
+                                       }
+                                       assign_alt_irq (cur_func, class_code);
+                                       rc = configure_bridge (&cur_func, slotno);
+                                       if (rc == -ENODEV) {
+                                               err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n");
+                                               err ("Bus %x, devfunc %x \n", cur_func->busno, cur_func->device);
+                                               return rc;
+                                       }
+                                       if (rc) {
+                                               /* We need to do this in case some other BARs were properly inserted */
+                                               err ("was not able to hot-add PPB properly.\n");
+                                               func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */
+                                               cleanup_count = 2;
+                                               goto error;
+                                       }
+
+                                       pci_read_config_byte_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_SECONDARY_BUS, &sec_number);
+                                       flag = FALSE;
+                                       for (i = 0; i < 32; i++) {
+                                               if (func->devices[i]) {
+                                                       newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
+                                                       if (!newfunc) {
+                                                               err ("out of system memory \n");
+                                                               return -ENOMEM;
+                                                       }
+                                                       memset (newfunc, 0, sizeof (struct pci_func));
+                                                       newfunc->busno = sec_number;
+                                                       newfunc->device = (u8) i;
+                                                       for (j = 0; j < 4; j++)
+                                                               newfunc->irq[j] = cur_func->irq[j];
+
+                                                       if (flag) {
+                                                               for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ;
+                                                               prev_func->next = newfunc;
+                                                       } else
+                                                               cur_func->next = newfunc;
+
+                                                       rc = ibmphp_configure_card (newfunc, slotno);
+                                                       /* This could only happen if kmalloc failed */
+                                                       if (rc) {
+                                                               /* We need to do this in case bridge itself got configured properly, but devices behind it failed */
+                                                               func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */
+                                                               cleanup_count = 2;
+                                                               goto error;
+                                                       }
+                                                       flag = TRUE;
+                                               }
+                                       }
+
+                                       newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
+                                       if (!newfunc) {
+                                               err ("out of system memory \n");
+                                               return -ENOMEM;
+                                       }
+                                       memset (newfunc, 0, sizeof (struct pci_func));
+                                       newfunc->busno = cur_func->busno;
+                                       newfunc->device = device;
+                                       for (j = 0; j < 4; j++)
+                                               newfunc->irq[j] = cur_func->irq[j];
+                                       for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ;
+                                       prev_func->next = newfunc;
+                                       cur_func = newfunc;
+                                       break;
+                               case PCI_HEADER_TYPE_BRIDGE:
+                                       class >>= 8;
+                                       debug ("class now is %x\n", class);
+                                       if (class != PCI_CLASS_BRIDGE_PCI) {
+                                               err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. "
+                                                    "Please insert another card.\n", cur_func->device);
+                                               return -ENODEV;
+                                       }
+
+                                       assign_alt_irq (cur_func, class_code);
+
+                                       debug ("cur_func->busno b4 configure_bridge is %x\n", cur_func->busno);
+                                       rc = configure_bridge (&cur_func, slotno);
+                                       if (rc == -ENODEV) {
+                                               err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n");
+                                               err ("Bus %x, devfunc %x \n", cur_func->busno, cur_func->device);
+                                               return rc;
+                                       }
+                                       if (rc) {
+                                               /* We need to do this in case some other BARs were properly inserted */
+                                               func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */
+                                               err ("was not able to hot-add PPB properly.\n");
+                                               cleanup_count = 2;
+                                               goto error;
+                                       }
+                                       debug ("cur_func->busno = %x, device = %x, function = %x\n", cur_func->busno, device, function);
+                                       pci_read_config_byte_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_SECONDARY_BUS, &sec_number);
+                                       debug ("after configuring bridge..., sec_number = %x\n", sec_number);
+                                       flag = FALSE;
+                                       for (i = 0; i < 32; i++) {
+                                               if (func->devices[i]) {
+                                                       debug ("inside for loop, device is %x\n", i);
+                                                       newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
+                                                       if (!newfunc) {
+                                                               err (" out of system memory \n");
+                                                               return -ENOMEM;
+                                                       }
+                                                       memset (newfunc, 0, sizeof (struct pci_func));
+                                                       newfunc->busno = sec_number;
+                                                       newfunc->device = (u8) i;
+                                                       for (j = 0; j < 4; j++)
+                                                               newfunc->irq[j] = cur_func->irq[j];
+
+                                                       if (flag) {
+                                                               for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ;
+                                                               prev_func->next = newfunc;
+                                                       } else
+                                                               cur_func->next = newfunc;
+
+                                                       rc = ibmphp_configure_card (newfunc, slotno);
+
+                                                       /* Again, this case should not happen... For complete paranoia, will need to call remove_bus */
+                                                       if (rc) {
+                                                               /* We need to do this in case some other BARs were properly inserted */
+                                                               func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */
+                                                               cleanup_count = 2;
+                                                               goto error;
+                                                       }
+                                                       flag = TRUE;
+                                               }
+                                       }
+
+                                       function = 0x8;
+                                       break;
+                               default:
+                                       err ("MAJOR PROBLEM!!!!, header type not supported? %x\n", hdr_type);
+                                       return -ENXIO;
+                                       break;
+                       }       /* end of switch */
+               }       /* end of valid device */
+       }       /* end of for */
+
+       if (!valid_device) {
+               err ("Cannot find any valid devices on the card.  Or unable to read from card.\n");
+               return -ENODEV;
+       }
+
+       return 0;
+
+error:
+       for (i = 0; i < cleanup_count; i++) {
+               if (cur_func->io[i]) {
+                       ibmphp_remove_resource (cur_func->io[i]);
+                       cur_func->io[i] = NULL;
+               } else if (cur_func->pfmem[i]) {
+                       ibmphp_remove_resource (cur_func->pfmem[i]);
+                       cur_func->pfmem[i] = NULL;
+               } else if (cur_func->mem[i]) {
+                       ibmphp_remove_resource (cur_func->mem[i]);
+                       cur_func->mem[i] = NULL;
+               }
+       }
+       return rc;
+}
+
+/*
+ * This function configures the pci BARs of a single device.  
+ * Input: pointer to the pci_func
+ * Output: configured PCI, 0, or error
+ */
+static int configure_device (struct pci_func *func)
+{
+       u32 bar[6];
+       u32 address[] = {
+               PCI_BASE_ADDRESS_0,
+               PCI_BASE_ADDRESS_1,
+               PCI_BASE_ADDRESS_2,
+               PCI_BASE_ADDRESS_3,
+               PCI_BASE_ADDRESS_4,
+               PCI_BASE_ADDRESS_5,
+               0
+       };
+       u8 irq;
+       int count;
+       int len[6];
+       struct resource_node *io[6];
+       struct resource_node *mem[6];
+       struct resource_node *mem_tmp;
+       struct resource_node *pfmem[6];
+       u8 device;
+       u8 function;
+
+       debug ("%s - inside\n", __FUNCTION__);
+
+       device = func->device;
+       function = func->function;
+
+       for (count = 0; address[count]; count++) {      /* for 6 BARs */
+
+               /* not sure if i need this.  per scott, said maybe need smth like this
+                  if devices don't adhere 100% to the spec, so don't want to write
+                  to the reserved bits
+
+               pcibios_read_config_byte(cur_func->busno, cur_func->device, 
+               PCI_BASE_ADDRESS_0 + 4 * count, &tmp);
+               if (tmp & 0x01) // IO
+                       pcibios_write_config_dword(cur_func->busno, cur_func->device, 
+                       PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFD);
+               else  // Memory
+                       pcibios_write_config_dword(cur_func->busno, cur_func->device, 
+                       PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFF);
+                */
+               pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0xFFFFFFFF);
+               pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]);
+
+               if (!bar[count])        /* This BAR is not implemented */
+                       continue;
+
+               debug ("Device %x BAR %d wants %x\n", func->device, count, bar[count]);
+
+               if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) {
+                       /* This is IO */
+                       debug ("inside IO SPACE\n");
+
+                       len[count] = bar[count] & 0xFFFFFFFC;
+                       len[count] = ~len[count] + 1;
+
+                       debug ("len[count] in IO %x, count %d\n", len[count], count);
+
+                       io[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+
+                       if (!io[count]) {
+                               err ("out of system memory \n");
+                               return -ENOMEM;
+                       }
+                       memset (io[count], 0, sizeof (struct resource_node));
+                       io[count]->type = IO;
+                       io[count]->busno = func->busno;
+                       io[count]->devfunc = ((func->device << 3) | (func->function & 0x7));
+                       io[count]->len = len[count];
+                       if (ibmphp_check_resource(io[count], 0) == 0) {
+                               ibmphp_add_resource (io[count]);
+                               func->io[count] = io[count];
+                       } else {
+                               err ("cannot allocate requested io for bus %x device %x function %x len %x\n",
+                                    func->busno, func->device, func->function, len[count]);
+                               kfree (io[count]);
+                               return -EIO;
+                       }
+                       pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->io[count]->start);
+       
+                       /* _______________This is for debugging purposes only_____________________ */ 
+                       debug ("b4 writing, the IO address is %x\n", func->io[count]->start);
+                       pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]);
+                       debug ("after writing.... the start address is %x\n", bar[count]);
+                       /* _________________________________________________________________________*/
+
+               } else {
+                       /* This is Memory */
+                       if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+                               /* pfmem */
+                               debug ("PFMEM SPACE\n");
+
+                               len[count] = bar[count] & 0xFFFFFFF0;
+                               len[count] = ~len[count] + 1;
+
+                               debug ("len[count] in PFMEM %x, count %d\n", len[count], count);
+
+                               pfmem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+                               if (!pfmem[count]) {
+                                       err ("out of system memory \n");
+                                       return -ENOMEM;
+                               }
+                               memset (pfmem[count], 0, sizeof (struct resource_node));
+                               pfmem[count]->type = PFMEM;
+                               pfmem[count]->busno = func->busno;
+                               pfmem[count]->devfunc = ((func->device << 3) | (func->function & 0x7));
+                               pfmem[count]->len = len[count];
+                               pfmem[count]->fromMem = FALSE;
+                               if (ibmphp_check_resource (pfmem[count], 0) == 0) {
+                                       ibmphp_add_resource (pfmem[count]);
+                                       func->pfmem[count] = pfmem[count];
+                               } else {
+                                       mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+                                       if (!mem_tmp) {
+                                               err ("out of system memory \n");
+                                               kfree (pfmem[count]);
+                                               return -ENOMEM;
+                                       }
+                                       memset (mem_tmp, 0, sizeof (struct resource_node));
+                                       mem_tmp->type = MEM;
+                                       mem_tmp->busno = pfmem[count]->busno;
+                                       mem_tmp->devfunc = pfmem[count]->devfunc;
+                                       mem_tmp->len = pfmem[count]->len;
+                                       debug ("there's no pfmem... going into mem.\n");
+                                       if (ibmphp_check_resource (mem_tmp, 0) == 0) {
+                                               ibmphp_add_resource (mem_tmp);
+                                               pfmem[count]->fromMem = TRUE;
+                                               pfmem[count]->rangeno = mem_tmp->rangeno;
+                                               pfmem[count]->start = mem_tmp->start;
+                                               pfmem[count]->end = mem_tmp->end;
+                                               ibmphp_add_pfmem_from_mem (pfmem[count]);
+                                               func->pfmem[count] = pfmem[count];
+                                       } else {
+                                               err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n",
+                                                    func->busno, func->device, len[count]);
+                                               kfree (mem_tmp);
+                                               kfree (pfmem[count]);
+                                               return -EIO;
+                                       }
+                               }
+
+                               pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->pfmem[count]->start);
+
+                               /*_______________This if for debugging purposes only______________________________*/                            
+                               debug ("b4 writing, start addres is %x\n", func->pfmem[count]->start);
+                               pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]);
+                               debug ("after writing, start address is %x\n", bar[count]);
+                               /*_________________________________________________________________________________*/
+
+                               if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {        /* takes up another dword */
+                                       debug ("inside the mem 64 case, count %d\n", count);
+                                       count += 1;
+                                       /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
+                                       pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0x00000000);
+                               }
+                       } else {
+                               /* regular memory */
+                               debug ("REGULAR MEM SPACE\n");
+
+                               len[count] = bar[count] & 0xFFFFFFF0;
+                               len[count] = ~len[count] + 1;
+
+                               debug ("len[count] in Mem %x, count %d\n", len[count], count);
+
+                               mem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+                               if (!mem[count]) {
+                                       err ("out of system memory \n");
+                                       return -ENOMEM;
+                               }
+                               memset (mem[count], 0, sizeof (struct resource_node));
+                               mem[count]->type = MEM;
+                               mem[count]->busno = func->busno;
+                               mem[count]->devfunc = ((func->device << 3) | (func->function & 0x7));
+                               mem[count]->len = len[count];
+                               if (ibmphp_check_resource (mem[count], 0) == 0) {
+                                       ibmphp_add_resource (mem[count]);
+                                       func->mem[count] = mem[count];
+                               } else {
+                                       err ("cannot allocate requested mem for bus %x, device %x, len %x\n",
+                                            func->busno, func->device, len[count]);
+                                       kfree (mem[count]);
+                                       return -EIO;
+                               }
+                               pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->mem[count]->start);
+                               /* _______________________This is for debugging purposes only _______________________*/
+                               debug ("b4 writing, start address is %x\n", func->mem[count]->start);
+                               pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]);
+                               debug ("after writing, the address is %x\n", bar[count]);
+                               /* __________________________________________________________________________________*/
+
+                               if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+                                       /* takes up another dword */
+                                       debug ("inside mem 64 case, reg. mem, count %d\n", count);
+                                       count += 1;
+                                       /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
+                                       pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0x00000000);
+                               }
+                       }
+               }               /* end of mem */
+       }                       /* end of for */
+
+       func->bus = 0;          /* To indicate that this is not a PPB */
+       pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_INTERRUPT_PIN, &irq);
+       if ((irq > 0x00) && (irq < 0x05))
+               pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_INTERRUPT_LINE, func->irq[irq - 1]);
+
+       pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_CACHE_LINE_SIZE, CACHE);
+       pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_LATENCY_TIMER, LATENCY);
+
+       pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_ROM_ADDRESS, 0x00L);
+       pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_COMMAND, DEVICEENABLE);
+
+       return 0;
+}
+
+/******************************************************************************
+ * This routine configures a PCI-2-PCI bridge and the functions behind it
+ * Parameters: pci_func
+ * Returns: 
+ ******************************************************************************/
+static int configure_bridge (struct pci_func **func_passed, u8 slotno)
+{
+       int count;
+       int i;
+       int rc;
+       u8 sec_number;
+       u8 io_base;
+       u16 pfmem_base;
+       u32 bar[2];
+       u32 len[2];
+       u8 flag_io = FALSE;
+       u8 flag_mem = FALSE;
+       u8 flag_pfmem = FALSE;
+       u8 need_io_upper = FALSE;
+       u8 need_pfmem_upper = FALSE;
+       struct res_needed *amount_needed = NULL;
+       struct resource_node *io = NULL;
+       struct resource_node *bus_io[2] = {NULL, NULL};
+       struct resource_node *mem = NULL;
+       struct resource_node *bus_mem[2] = {NULL, NULL};
+       struct resource_node *mem_tmp = NULL;
+       struct resource_node *pfmem = NULL;
+       struct resource_node *bus_pfmem[2] = {NULL, NULL};
+       struct bus_node *bus;
+       u32 address[] = {
+               PCI_BASE_ADDRESS_0,
+               PCI_BASE_ADDRESS_1,
+               0
+       };
+       struct pci_func *func = *func_passed;
+       u8 function;
+       u8 device;
+       u8 irq;
+       int retval;
+
+       debug ("%s - enter\n", __FUNCTION__);
+
+       function = func->function;
+       device = func->device;
+
+       /* Configuring necessary info for the bridge so that we could see the devices
+        * behind it
+        */
+
+       pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PRIMARY_BUS, func->busno);
+
+       /* _____________________For debugging purposes only __________________________
+       pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PRIMARY_BUS, &pri_number);
+       debug ("primary # written into the bridge is %x\n", pri_number);
+        ___________________________________________________________________________*/
+
+       /* in EBDA, only get allocated 1 additional bus # per slot */
+       sec_number = find_sec_number (func->busno, slotno);
+       if (sec_number == 0xff) {
+               err ("cannot allocate secondary bus number for the bridged device \n");
+               return -EINVAL;
+       }
+
+       debug ("after find_sec_number, the number we got is %x\n", sec_number);
+       debug ("AFTER FIND_SEC_NUMBER, func->busno IS %x\n", func->busno);
+
+       pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SECONDARY_BUS, sec_number);
+       
+       /* __________________For debugging purposes only __________________________________
+       pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SECONDARY_BUS, &sec_number);
+       debug ("sec_number after write/read is %x\n", sec_number);
+        ________________________________________________________________________________*/
+
+       pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SUBORDINATE_BUS, sec_number);
+
+       /* __________________For debugging purposes only ____________________________________
+       pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SUBORDINATE_BUS, &sec_number);
+       debug ("subordinate number after write/read is %x\n", sec_number);
+        __________________________________________________________________________________*/
+
+       pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_CACHE_LINE_SIZE, CACHE);
+       pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_LATENCY_TIMER, LATENCY);
+       pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SEC_LATENCY_TIMER, LATENCY);
+
+       debug ("func->busno is %x\n", func->busno);
+       debug ("sec_number after writing is %x\n", sec_number);
+
+
+       /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+          !!!!!!!!!!!!!!!NEED TO ADD!!!  FAST BACK-TO-BACK ENABLE!!!!!!!!!!!!!!!!!!!! 
+          !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
+
+
+       /* First we need to allocate mem/io for the bridge itself in case it needs it */
+       for (count = 0; address[count]; count++) {      /* for 2 BARs */
+               pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0xFFFFFFFF);
+               pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]);
+
+               if (!bar[count]) {
+                       /* This BAR is not implemented */
+                       debug ("so we come here then, eh?, count = %d\n", count);
+                       continue;
+               }
+               //  tmp_bar = bar[count];
+
+               debug ("Bar %d wants %x\n", count, bar[count]);
+
+               if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) {
+                       /* This is IO */
+                       len[count] = bar[count] & 0xFFFFFFFC;
+                       len[count] = ~len[count] + 1;
+
+                       debug ("len[count] in IO = %x\n", len[count]);
+
+                       bus_io[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+               
+                       if (!bus_io[count]) {
+                               err ("out of system memory \n");
+                               retval = -ENOMEM;
+                               goto error;
+                       }
+                       memset (bus_io[count], 0, sizeof (struct resource_node));
+                       bus_io[count]->type = IO;
+                       bus_io[count]->busno = func->busno;
+                       bus_io[count]->devfunc = ((func->device << 3) | (func->function & 0x7));
+                       bus_io[count]->len = len[count];
+                       if (ibmphp_check_resource (bus_io[count], 0) == 0) {
+                               ibmphp_add_resource (bus_io[count]);
+                               func->io[count] = bus_io[count];
+                       } else {
+                               err ("cannot allocate requested io for bus %x, device %x, len %x\n",
+                                    func->busno, func->device, len[count]);
+                               kfree (bus_io[count]);
+                               return -EIO;
+                       }
+
+                       pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->io[count]->start);
+
+               } else {
+                       /* This is Memory */
+                       if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+                               /* pfmem */
+                               len[count] = bar[count] & 0xFFFFFFF0;
+                               len[count] = ~len[count] + 1;
+
+                               debug ("len[count] in PFMEM = %x\n", len[count]);
+
+                               bus_pfmem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+                               if (!bus_pfmem[count]) {
+                                       err ("out of system memory \n");
+                                       retval = -ENOMEM;
+                                       goto error;
+                               }
+                               memset (bus_pfmem[count], 0, sizeof (struct resource_node));
+                               bus_pfmem[count]->type = PFMEM;
+                               bus_pfmem[count]->busno = func->busno;
+                               bus_pfmem[count]->devfunc = ((func->device << 3) | (func->function & 0x7));
+                               bus_pfmem[count]->len = len[count];
+                               bus_pfmem[count]->fromMem = FALSE;
+                               if (ibmphp_check_resource (bus_pfmem[count], 0) == 0) {
+                                       ibmphp_add_resource (bus_pfmem[count]);
+                                       func->pfmem[count] = bus_pfmem[count];
+                               } else {
+                                       mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+                                       if (!mem_tmp) {
+                                               err ("out of system memory \n");
+                                               retval = -ENOMEM;
+                                               goto error;
+                                       }
+                                       memset (mem_tmp, 0, sizeof (struct resource_node));
+                                       mem_tmp->type = MEM;
+                                       mem_tmp->busno = bus_pfmem[count]->busno;
+                                       mem_tmp->devfunc = bus_pfmem[count]->devfunc;
+                                       mem_tmp->len = bus_pfmem[count]->len;
+                                       if (ibmphp_check_resource (mem_tmp, 0) == 0) {
+                                               ibmphp_add_resource (mem_tmp);
+                                               bus_pfmem[count]->fromMem = TRUE;
+                                               bus_pfmem[count]->rangeno = mem_tmp->rangeno;
+                                               ibmphp_add_pfmem_from_mem (bus_pfmem[count]);
+                                               func->pfmem[count] = bus_pfmem[count];
+                                       } else {
+                                               err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n", 
+                                                    func->busno, func->device, len[count]);
+                                               kfree (mem_tmp);
+                                               kfree (bus_pfmem[count]);
+                                               return -EIO;
+                                       }
+                               }
+
+                               pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->pfmem[count]->start);
+
+                               if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+                                       /* takes up another dword */
+                                       count += 1;
+                                       /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
+                                       pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0x00000000);
+
+                               }
+                       } else {
+                               /* regular memory */
+                               len[count] = bar[count] & 0xFFFFFFF0;
+                               len[count] = ~len[count] + 1;
+
+                               debug ("len[count] in Memory is %x\n", len[count]);
+
+                               bus_mem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+                               if (!bus_mem[count]) {
+                                       err ("out of system memory \n");
+                                       retval = -ENOMEM;
+                                       goto error;
+                               }
+                               memset (bus_mem[count], 0, sizeof (struct resource_node));
+                               bus_mem[count]->type = MEM;
+                               bus_mem[count]->busno = func->busno;
+                               bus_mem[count]->devfunc = ((func->device << 3) | (func->function & 0x7));
+                               bus_mem[count]->len = len[count];
+                               if (ibmphp_check_resource (bus_mem[count], 0) == 0) {
+                                       ibmphp_add_resource (bus_mem[count]);
+                                       func->mem[count] = bus_mem[count];
+                               } else {
+                                       err ("cannot allocate requested mem for bus %x, device %x, len %x\n",
+                                            func->busno, func->device, len[count]);
+                                       kfree (bus_mem[count]);
+                                       return -EIO;
+                               }
+
+                               pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->mem[count]->start);
+
+                               if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+                                       /* takes up another dword */
+                                       count += 1;
+                                       /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
+                                       pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0x00000000);
+
+                               }
+                       }
+               }               /* end of mem */
+       }                       /* end of for  */
+
+       /* Now need to see how much space the devices behind the bridge needed */
+       amount_needed = scan_behind_bridge (func, sec_number);
+       if (amount_needed == NULL)
+               return -ENOMEM;
+
+       debug ("after coming back from scan_behind_bridge\n");
+       debug ("amount_needed->not_correct = %x\n", amount_needed->not_correct);
+       debug ("amount_needed->io = %x\n", amount_needed->io);
+       debug ("amount_needed->mem = %x\n", amount_needed->mem);
+       debug ("amount_needed->pfmem =  %x\n", amount_needed->pfmem);
+
+       if (amount_needed->not_correct) {               
+               debug ("amount_needed is not correct \n");
+               for (count = 0; address[count]; count++) {
+                       /* for 2 BARs */
+                       if (bus_io[count]) {
+                               ibmphp_remove_resource (bus_io[count]);
+                               func->io[count] = NULL;
+                       } else if (bus_pfmem[count]) {
+                               ibmphp_remove_resource (bus_pfmem[count]);
+                               func->pfmem[count] = NULL;
+                       } else if (bus_mem[count]) {
+                               ibmphp_remove_resource (bus_mem[count]);
+                               func->mem[count] = NULL;
+                       }
+               }
+               kfree (amount_needed);
+               return -ENODEV;
+       }
+
+       if (!amount_needed->io) {
+               debug ("it doesn't want IO?\n");
+               flag_io = TRUE;
+       } else {
+               debug ("it wants %x IO behind the bridge \n", amount_needed->io);
+               io = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+               
+               if (!io) {
+                       err ("out of system memory \n");
+                       retval = -ENOMEM;
+                       goto error;
+               }
+               memset (io, 0, sizeof (struct resource_node));
+               io->type = IO;
+               io->busno = func->busno;
+               io->devfunc = ((func->device << 3) | (func->function & 0x7));
+               io->len = amount_needed->io;
+               if (ibmphp_check_resource (io, 1) == 0) {
+                       debug ("were we able to add io\n");
+                       ibmphp_add_resource (io);
+                       flag_io = TRUE;
+               }
+       }
+
+       if (!amount_needed->mem) {
+               debug ("it doesn't want n.e.memory?\n");
+               flag_mem = TRUE;
+       } else {
+               debug ("it wants %x memory behind the bridge\n", amount_needed->mem);
+               mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+               if (!mem) {
+                       err ("out of system memory \n");
+                       retval = -ENOMEM;
+                       goto error;
+               }
+               memset (mem, 0, sizeof (struct resource_node));
+               mem->type = MEM;
+               mem->busno = func->busno;
+               mem->devfunc = ((func->device << 3) | (func->function & 0x7));
+               mem->len = amount_needed->mem;
+               if (ibmphp_check_resource (mem, 1) == 0) {
+                       ibmphp_add_resource (mem);
+                       flag_mem = TRUE;
+                       debug ("were we able to add mem\n");
+               }
+       }
+
+       if (!amount_needed->pfmem) {
+               debug ("it doesn't want n.e.pfmem mem?\n");
+               flag_pfmem = TRUE;
+       } else {
+               debug ("it wants %x pfmemory behind the bridge\n", amount_needed->pfmem);
+               pfmem = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+               if (!pfmem) {
+                       err ("out of system memory \n");
+                       retval = -ENOMEM;
+                       goto error;
+               }
+               memset (pfmem, 0, sizeof (struct resource_node));
+               pfmem->type = PFMEM;
+               pfmem->busno = func->busno;
+               pfmem->devfunc = ((func->device << 3) | (func->function & 0x7));
+               pfmem->len = amount_needed->pfmem;
+               pfmem->fromMem = FALSE;
+               if (ibmphp_check_resource (pfmem, 1) == 0) {
+                       ibmphp_add_resource (pfmem);
+                       flag_pfmem = TRUE;
+               } else {
+                       mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+                       if (!mem_tmp) {
+                               err ("out of system memory \n");
+                               retval = -ENOMEM;
+                               goto error;
+                       }
+                       memset (mem_tmp, 0, sizeof (struct resource_node));
+                       mem_tmp->type = MEM;
+                       mem_tmp->busno = pfmem->busno;
+                       mem_tmp->devfunc = pfmem->devfunc;
+                       mem_tmp->len = pfmem->len;
+                       if (ibmphp_check_resource (mem_tmp, 1) == 0) {
+                               ibmphp_add_resource (mem_tmp);
+                               pfmem->fromMem = TRUE;
+                               pfmem->rangeno = mem_tmp->rangeno;
+                               ibmphp_add_pfmem_from_mem (pfmem);
+                               flag_pfmem = TRUE;
+                       }
+               }
+       }
+
+       debug ("b4 if (flag_io && flag_mem && flag_pfmem)\n");
+       debug ("flag_io = %x, flag_mem = %x, flag_pfmem = %x\n", flag_io, flag_mem, flag_pfmem);
+
+       if (flag_io && flag_mem && flag_pfmem) {
+               bus = kmalloc (sizeof (struct bus_node), GFP_KERNEL);
+               if (!bus) {
+                       err ("out of system memory \n");
+                       retval = -ENOMEM;
+                       goto error;
+               }
+               memset (bus, 0, sizeof (struct bus_node));
+               bus->busno = sec_number;
+               debug ("b4 adding new bus\n");
+               rc = add_new_bus (bus, io, mem, pfmem, func->busno);
+               if (rc) {
+                       if (rc == -ENOMEM) {
+                               ibmphp_remove_bus (bus, func->busno);
+                               return rc;
+                       }
+                       retval = rc;
+                       goto error;
+               }
+               pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE, &io_base);
+               pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_BASE, &pfmem_base);
+
+               if ((io_base & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {
+                       debug ("io 32\n");
+                       need_io_upper = TRUE;
+               }
+               if ((io_base & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
+                       debug ("pfmem 64\n");
+                       need_pfmem_upper = TRUE;
+               }
+
+               if (bus->noIORanges) {
+                       pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE, 0x00 | bus->rangeIO->start >> 8);
+                       pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_LIMIT, 0x00 | bus->rangeIO->end >> 8);  
+
+                       /* _______________This is for debugging purposes only ____________________
+                       pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE, &temp);
+                       debug ("io_base = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8);
+                       pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_LIMIT, &temp);
+                       debug ("io_limit = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8);
+                        ________________________________________________________________________*/
+
+                       if (need_io_upper) {    /* since can't support n.e.ways */
+                               pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE_UPPER16, 0x0000);
+                               pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_LIMIT_UPPER16, 0x0000);
+                       }
+               } else {
+                       pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE, 0x00);
+                       pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_LIMIT, 0x00);
+               }
+
+               if (bus->noMemRanges) {
+                       pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_BASE, 0x0000 | bus->rangeMem->start >> 16);
+                       pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_LIMIT, 0x0000 | bus->rangeMem->end >> 16);
+                       
+                       /* ____________________This is for debugging purposes only ________________________
+                       pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_BASE, &temp);
+                       debug ("mem_base = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
+                       pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_LIMIT, &temp);
+                       debug ("mem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
+                        __________________________________________________________________________________*/
+
+               } else {
+                       pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_BASE, 0xffff);
+                       pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_LIMIT, 0x0000);
+               }
+               if (bus->noPFMemRanges) {
+                       pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_BASE, 0x0000 | bus->rangePFMem->start >> 16);
+                       pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_LIMIT, 0x0000 | bus->rangePFMem->end >> 16);
+
+                       /* __________________________This is for debugging purposes only _______________________
+                       pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_BASE, &temp);
+                       debug ("pfmem_base = %x", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
+                       pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_LIMIT, &temp);
+                       debug ("pfmem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
+                        ______________________________________________________________________________________*/
+
+                       if (need_pfmem_upper) { /* since can't support n.e.ways */
+                               pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_BASE_UPPER32, 0x00000000);
+                               pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_LIMIT_UPPER32, 0x00000000);
+                       }
+               } else {
+                       pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_BASE, 0xffff);
+                       pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_LIMIT, 0x0000);
+               }
+
+               debug ("b4 writing control information\n");
+
+               pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_INTERRUPT_PIN, &irq);
+               if ((irq > 0x00) && (irq < 0x05))
+                       pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_INTERRUPT_LINE, func->irq[irq - 1]);
+               /*    
+               pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_BRIDGE_CONTROL, ctrl);
+               pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_PARITY);
+               pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_SERR);
+                */
+
+               pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_COMMAND, DEVICEENABLE);
+               pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_BRIDGE_CONTROL, 0x07);
+               for (i = 0; i < 32; i++) {
+                       if (amount_needed->devices[i]) {
+                               debug ("device where devices[i] is 1 = %x\n", i);
+                               func->devices[i] = 1;
+                       }
+               }
+               func->bus = 1;  /* For unconfiguring, to indicate it's PPB */
+               func_passed = &func;
+               debug ("func->busno b4 returning is %x\n", func->busno);
+               debug ("func->busno b4 returning in the other structure is %x\n", (*func_passed)->busno);
+               kfree (amount_needed);
+               return 0;
+       } else {
+               err ("Configuring bridge was unsuccessful... \n");
+               mem_tmp = NULL;
+               retval = -EIO;
+               goto error;
+       }
+
+error:
+       if (amount_needed)
+               kfree (amount_needed);
+       if (pfmem)
+               ibmphp_remove_resource (pfmem);
+       if (io)
+               ibmphp_remove_resource (io);
+       if (mem)
+               ibmphp_remove_resource (mem);
+       for (i = 0; i < 2; i++) {       /* for 2 BARs */
+               if (bus_io[i]) {
+                       ibmphp_remove_resource (bus_io[i]);
+                       func->io[i] = NULL;
+               } else if (bus_pfmem[i]) {
+                       ibmphp_remove_resource (bus_pfmem[i]);
+                       func->pfmem[i] = NULL;
+               } else if (bus_mem[i]) {
+                       ibmphp_remove_resource (bus_mem[i]);
+                       func->mem[i] = NULL;
+               }
+       }
+       return retval;
+}
+
+/*****************************************************************************
+ * This function adds up the amount of resources needed behind the PPB bridge
+ * and passes it to the configure_bridge function
+ * Input: bridge function
+ * Ouput: amount of resources needed
+ *****************************************************************************/
+static struct res_needed *scan_behind_bridge (struct pci_func * func, u8 busno)
+{
+       int count, len[6];
+       u16 vendor_id;
+       u8 hdr_type;
+       u8 device, function;
+       int howmany = 0;        /*this is to see if there are any devices behind the bridge */
+
+       u32 bar[6], class;
+       u32 address[] = {
+               PCI_BASE_ADDRESS_0,
+               PCI_BASE_ADDRESS_1,
+               PCI_BASE_ADDRESS_2,
+               PCI_BASE_ADDRESS_3,
+               PCI_BASE_ADDRESS_4,
+               PCI_BASE_ADDRESS_5,
+               0
+       };
+       struct res_needed *amount;
+
+       amount = kmalloc (sizeof (struct res_needed), GFP_KERNEL);
+       if (amount == NULL)
+               return NULL;
+       memset (amount, 0, sizeof (struct res_needed));
+
+       debug ("the bus_no behind the bridge is %x\n", busno);
+       debug ("scanning devices behind the bridge...\n");
+       for (device = 0; device < 32; device++) {
+               amount->devices[device] = 0;
+               for (function = 0; function < 8; function++) {
+
+                       pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_VENDOR_ID, &vendor_id);
+
+                       if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
+                               /* found correct device!!! */
+                               howmany++;
+
+                               pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_HEADER_TYPE, &hdr_type);
+                               pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_CLASS_REVISION, &class);
+
+                               debug ("hdr_type behind the bridge is %x\n", hdr_type);
+                               if (hdr_type & PCI_HEADER_TYPE_BRIDGE) {
+                                       err ("embedded bridges not supported for hot-plugging.\n");
+                                       amount->not_correct = TRUE;
+                                       return amount;
+                               }
+
+                               class >>= 8;    /* to take revision out, class = class.subclass.prog i/f */
+                               if (class == PCI_CLASS_NOT_DEFINED_VGA) {
+                                       err ("The device %x is VGA compatible and as is not supported for hot plugging. "
+                                            "Please choose another device.\n", device);
+                                       amount->not_correct = TRUE;
+                                       return amount;
+                               } else if (class == PCI_CLASS_DISPLAY_VGA) {
+                                       err ("The device %x is not supported for hot plugging. "
+                                            "Please choose another device.\n", device);
+                                       amount->not_correct = TRUE;
+                                       return amount;
+                               }
+
+                               amount->devices[device] = 1;
+
+                               for (count = 0; address[count]; count++) {
+                                       /* for 6 BARs */
+                                       /*
+                                       pci_read_config_byte_nodev(ibmphp_pci_root_ops, busno, device, function, address[count], &tmp);
+                                       if (tmp & 0x01) // IO
+                                               pci_write_config_dword_nodev(ibmphp_pci_root_ops, busno, device, function, address[count], 0xFFFFFFFD);
+                                       else // MEMORY
+                                               pci_write_config_dword_nodev(ibmphp_pci_root_ops, busno, device, function, address[count], 0xFFFFFFFF);
+                                       */
+                                       pci_write_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], 0xFFFFFFFF);
+                                       pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], &bar[count]);
+
+                                       debug ("what is bar[count]? %x, count = %d\n", bar[count], count);
+
+                                       if (!bar[count])        /* This BAR is not implemented */
+                                               continue;
+
+                                       //tmp_bar = bar[count];
+
+                                       debug ("count %d device %x function %x wants %x resources \n", count, device, function, bar[count]);
+
+                                       if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) {
+                                               /* This is IO */
+                                               len[count] = bar[count] & 0xFFFFFFFC;
+                                               len[count] = ~len[count] + 1;
+                                               amount->io += len[count];
+                                       } else {
+                                               /* This is Memory */
+                                               if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+                                                       /* pfmem */
+                                                       len[count] = bar[count] & 0xFFFFFFF0;
+                                                       len[count] = ~len[count] + 1;
+                                                       amount->pfmem += len[count];
+                                                       if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64)
+                                                               /* takes up another dword */
+                                                               count += 1;
+
+                                               } else {
+                                                       /* regular memory */
+                                                       len[count] = bar[count] & 0xFFFFFFF0;
+                                                       len[count] = ~len[count] + 1;
+                                                       amount->mem += len[count];
+                                                       if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+                                                               /* takes up another dword */
+                                                               count += 1;
+                                                       }
+                                               }
+                                       }
+                               }       /* end for */
+                       }       /* end if (valid) */
+               }       /* end for */
+       }       /* end for */
+
+       if (!howmany)
+               amount->not_correct = TRUE;
+       else
+               amount->not_correct = FALSE;
+       if ((amount->io) && (amount->io < IOBRIDGE))
+               amount->io = IOBRIDGE;
+       if ((amount->mem) && (amount->mem < MEMBRIDGE))
+               amount->mem = MEMBRIDGE;
+       if ((amount->pfmem) && (amount->pfmem < MEMBRIDGE))
+               amount->pfmem = MEMBRIDGE;
+       return amount;
+}
+
+/* The following 3 unconfigure_boot_ routines deal with the case when we had the card 
+ * upon bootup in the system, since we don't allocate func to such case, we need to read 
+ * the start addresses from pci config space and then find the corresponding entries in 
+ * our resource lists.  The functions return either 0, -ENODEV, or -1 (general failure)
+ * Change: we also call these functions even if we configured the card ourselves (i.e., not
+ * the bootup case), since it should work same way
+ */
+static int unconfigure_boot_device (u8 busno, u8 device, u8 function)
+{
+       u32 start_address;
+       u32 address[] = {
+               PCI_BASE_ADDRESS_0,
+               PCI_BASE_ADDRESS_1,
+               PCI_BASE_ADDRESS_2,
+               PCI_BASE_ADDRESS_3,
+               PCI_BASE_ADDRESS_4,
+               PCI_BASE_ADDRESS_5,
+               0
+       };
+       int count;
+       struct resource_node *io;
+       struct resource_node *mem;
+       struct resource_node *pfmem;
+       struct bus_node *bus;
+       u32 end_address;
+       u32 temp_end;
+       u32 size;
+       u32 tmp_address;
+
+       debug ("%s - enter\n", __FUNCTION__);
+
+       bus = ibmphp_find_res_bus (busno);
+       if (!bus) {
+               debug ("cannot find corresponding bus.\n");
+               return -EINVAL;
+       }
+
+       for (count = 0; address[count]; count++) {      /* for 6 BARs */
+               pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], &start_address);
+
+               /* We can do this here, b/c by that time the device driver of the card has been stopped */
+
+               pci_write_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], 0xFFFFFFFF);
+               pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], &size);
+               pci_write_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], start_address);
+
+               debug ("start_address is %x\n", start_address);
+               debug ("busno, device, function %x %x %x\n", busno, device, function);
+               if (!size) {
+                       /* This BAR is not implemented */
+                       debug ("is this bar no implemented?, count = %d\n", count);
+                       continue;
+               }
+               tmp_address = start_address;
+               if (start_address & PCI_BASE_ADDRESS_SPACE_IO) {
+                       /* This is IO */
+                       start_address &= PCI_BASE_ADDRESS_IO_MASK;
+                       size = size & 0xFFFFFFFC;
+                       size = ~size + 1;
+                       end_address = start_address + size - 1;
+                       if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) {
+                               err ("cannot find corresponding IO resource to remove\n");
+                               return -EIO;
+                       }
+                       debug ("io->start = %x\n", io->start);
+                       temp_end = io->end;
+                       start_address = io->end + 1;
+                       ibmphp_remove_resource (io);
+                       /* This is needed b/c of the old I/O restrictions in the BIOS */
+                       while (temp_end < end_address) {
+                               if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) {
+                                       err ("cannot find corresponding IO resource to remove\n");
+                                       return -EIO;
+                               }
+                               debug ("io->start = %x\n", io->start);
+                               temp_end = io->end;
+                               start_address = io->end + 1;
+                               ibmphp_remove_resource (io);
+                       }
+
+                       /* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */
+               } else {
+                       /* This is Memory */
+                       if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+                               /* pfmem */
+                               start_address &= PCI_BASE_ADDRESS_MEM_MASK;
+                               debug ("start address of pfmem is %x\n", start_address);
+
+                               if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) {
+                                       err ("cannot find corresponding PFMEM resource to remove\n");
+                                       return -EIO;
+                               }
+                               if (pfmem)
+                                       debug ("pfmem->start = %x\n", pfmem->start);
+
+                               ibmphp_remove_resource (pfmem);
+
+                               if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+                                       /* takes up another dword */
+                                       count += 1;
+                               }
+
+                       } else {
+                               /* regular memory */
+                               start_address &= PCI_BASE_ADDRESS_MEM_MASK;
+                               debug ("start address of mem is %x\n", start_address);
+                               if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) {
+                                       err ("cannot find corresponding MEM resource to remove\n");
+                                       return -EIO;
+                               }
+                               if (mem)
+                                       debug ("mem->start = %x\n", mem->start);
+
+                               ibmphp_remove_resource (mem);
+
+                               if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+                                       /* takes up another dword */
+                                       count += 1;
+                               }
+                       }
+               }       /* end of mem */
+       }       /* end of for */
+
+       return 0;
+}
+
+static int unconfigure_boot_bridge (u8 busno, u8 device, u8 function)
+{
+       int count;
+       int bus_no, pri_no, sub_no, sec_no = 0;
+       u32 start_address, tmp_address;
+       u8 sec_number, sub_number, pri_number;
+       struct resource_node *io = NULL;
+       struct resource_node *mem = NULL;
+       struct resource_node *pfmem = NULL;
+       struct bus_node *bus;
+       u32 address[] = {
+               PCI_BASE_ADDRESS_0,
+               PCI_BASE_ADDRESS_1,
+               0
+       };
+
+       bus_no = (int) busno;
+       debug ("busno is %x\n", busno);
+       pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PRIMARY_BUS, &pri_number);
+       debug ("%s - busno = %x, primary_number = %x\n", __FUNCTION__, busno, pri_number);
+
+       pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_SECONDARY_BUS, &sec_number);
+       debug ("sec_number is %x\n", sec_number);
+       sec_no = (int) sec_number;
+       pri_no = (int) pri_number;
+       if (pri_no != bus_no) {
+               err ("primary numbers in our structures and pci config space don't match.\n");
+               return -EINVAL;
+       }
+
+       pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_SECONDARY_BUS, &sec_number);
+       sec_no = (int) sec_no;
+
+       pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_SUBORDINATE_BUS, &sub_number);
+       sub_no = (int) sub_number;
+       debug ("sub_no is %d, sec_no is %d\n", sub_no, sec_no);
+       if (sec_no != sub_number) {
+               err ("there're more buses behind this bridge.  Hot removal is not supported.  Please choose another card\n");
+               return -ENODEV;
+       }
+
+       bus = ibmphp_find_res_bus (sec_number);
+       debug ("bus->busno is %x\n", bus->busno);
+       debug ("sec_number is %x\n", sec_number);
+       if (!bus) {
+               err ("cannot find Bus structure for the bridged device\n");
+               return -EINVAL;
+       }
+
+       ibmphp_remove_bus (bus, busno);
+
+       for (count = 0; address[count]; count++) {
+               /* for 2 BARs */
+               pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], &start_address);
+
+               if (!start_address) {
+                       /* This BAR is not implemented */
+                       continue;
+               }
+
+               tmp_address = start_address;
+
+               if (start_address & PCI_BASE_ADDRESS_SPACE_IO) {
+                       /* This is IO */
+                       start_address &= PCI_BASE_ADDRESS_IO_MASK;
+                       if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) {
+                               err ("cannot find corresponding IO resource to remove\n");
+                               return -EIO;
+                       }
+                       if (io)
+                               debug ("io->start = %x\n", io->start);
+
+                       ibmphp_remove_resource (io);
+
+                       /* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */
+               } else {
+                       /* This is Memory */
+                       if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+                               /* pfmem */
+                               start_address &= PCI_BASE_ADDRESS_MEM_MASK;
+                               if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) {
+                                       err ("cannot find corresponding PFMEM resource to remove\n");
+                                       return -EINVAL;
+                               }
+                               if (pfmem)
+                                       debug ("pfmem->start = %x\n", pfmem->start);
+
+                               ibmphp_remove_resource (pfmem);
+
+                               if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+                                       /* takes up another dword */
+                                       count += 1;
+                               }
+
+                       } else {
+                               /* regular memory */
+                               start_address &= PCI_BASE_ADDRESS_MEM_MASK;
+                               if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) {
+                                       err ("cannot find corresponding MEM resource to remove\n");
+                                       return -EINVAL;
+                               }
+                               if (mem)
+                                       debug ("mem->start = %x\n", mem->start);
+
+                               ibmphp_remove_resource (mem);
+
+                               if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+                                       /* takes up another dword */
+                                       count += 1;
+                               }
+                       }
+               }       /* end of mem */
+       }       /* end of for */
+       debug ("%s - exiting, returning success\n", __FUNCTION__);
+       return 0;
+}
+
+static int unconfigure_boot_card (struct slot *slot_cur)
+{
+       u16 vendor_id;
+       u32 class;
+       u8 hdr_type;
+       u8 device;
+       u8 busno;
+       u8 function;
+       int rc;
+       u8 valid_device = 0x00; /* To see if we are ever able to find valid device and read it */
+
+       debug ("%s - enter\n", __FUNCTION__);
+
+       device = slot_cur->device;
+       busno = slot_cur->bus;
+
+       debug ("b4 for loop, device is %x\n", device);
+       /* For every function on the card */
+       for (function = 0x0; function < 0x08; function++) {
+
+               pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_VENDOR_ID, &vendor_id);
+
+               if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
+                       /* found correct device!!! */
+                       ++valid_device;
+
+                       debug ("%s - found correct device\n", __FUNCTION__);
+
+                       /* header: x x x x x x x x
+                        *         | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge
+                        *         |_=> 0 = single function device, 1 = multi-function device
+                        */
+
+                       pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_HEADER_TYPE, &hdr_type);
+                       pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_CLASS_REVISION, &class);
+
+                       debug ("hdr_type %x, class %x\n", hdr_type, class);
+                       class >>= 8;    /* to take revision out, class = class.subclass.prog i/f */
+                       if (class == PCI_CLASS_NOT_DEFINED_VGA) {
+                               err ("The device %x function %x is VGA compatible and is not supported for hot removing. "
+                                    "Please choose another device.\n", device, function);
+                               return -ENODEV;
+                       } else if (class == PCI_CLASS_DISPLAY_VGA) {
+                               err ("The device %x function %x is not supported for hot removing. "
+                                    "Please choose another device.\n", device, function);
+                               return -ENODEV;
+                       }
+
+                       switch (hdr_type) {
+                               case PCI_HEADER_TYPE_NORMAL:
+                                       rc = unconfigure_boot_device (busno, device, function);
+                                       if (rc) {
+                                               err ("was not able to unconfigure device %x func %x on bus %x. bailing out... \n",
+                                                    device, function, busno);
+                                               return rc;
+                                       }
+                                       function = 0x8;
+                                       break;
+                               case PCI_HEADER_TYPE_MULTIDEVICE:
+                                       rc = unconfigure_boot_device (busno, device, function);
+                                       if (rc) {
+                                               err ("was not able to unconfigure device %x func %x on bus %x. bailing out... \n",
+                                                    device, function, busno);
+                                               return rc;
+                                       }
+                                       break;
+                               case PCI_HEADER_TYPE_BRIDGE:
+                                       class >>= 8;
+                                       if (class != PCI_CLASS_BRIDGE_PCI) {
+                                               err ("This device %x function %x is not PCI-to-PCI bridge, "
+                                                    "and is not supported for hot-removing. "
+                                                    "Please try another card.\n", device, function);
+                                               return -ENODEV;
+                                       }
+                                       rc = unconfigure_boot_bridge (busno, device, function);
+                                       if (rc != 0) {
+                                               err ("was not able to hot-remove PPB properly.\n");
+                                               return rc;
+                                       }
+
+                                       function = 0x8;
+                                       break;
+                               case PCI_HEADER_TYPE_MULTIBRIDGE:
+                                       class >>= 8;
+                                       if (class != PCI_CLASS_BRIDGE_PCI) {
+                                               err ("This device %x function %x is not PCI-to-PCI bridge, "
+                                                    "and is not supported for hot-removing. "
+                                                    "Please try another card.\n", device, function);
+                                               return -ENODEV;
+                                       }
+                                       rc = unconfigure_boot_bridge (busno, device, function);
+                                       if (rc != 0) {
+                                               err ("was not able to hot-remove PPB properly.\n");
+                                               return rc;
+                                       }
+                                       break;
+                               default:
+                                       err ("MAJOR PROBLEM!!!! Cannot read device's header \n");
+                                       return -1;
+                                       break;
+                       }       /* end of switch */
+               }       /* end of valid device */
+       }       /* end of for */
+
+       if (!valid_device) {
+               err ("Could not find device to unconfigure.  Or could not read the card. \n");
+               return -1;
+       }
+       return 0;
+}
+
+/*
+ * free the resources of the card (multi, single, or bridged)
+ * Parameters: slot, flag to say if this is for removing entire module or just
+ * unconfiguring the device
+ * TO DO:  will probably need to add some code in case there was some resource,
+ * to remove it... this is from when we have errors in the configure_card...
+ *                     !!!!!!!!!!!!!!!!!!!!!!!!!FOR BUSES!!!!!!!!!!!!
+ * Returns: 0, -1, -ENODEV 
+ */
+int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end)
+{
+       int i;
+       int count;
+       int rc;
+       struct slot *sl = *slot_cur;
+       struct pci_func *cur_func = NULL;
+       struct pci_func *temp_func;
+
+       debug ("%s - enter\n", __FUNCTION__);
+
+       if (!the_end) {
+               /* Need to unconfigure the card */
+               rc = unconfigure_boot_card (sl);
+               if ((rc == -ENODEV) || (rc == -EIO) || (rc == -EINVAL)) {
+                       /* In all other cases, will still need to get rid of func structure if it exists */
+                       return rc;
+               }
+       }
+
+       if (sl->func) {
+               debug ("do we come in here? \n");
+               cur_func = sl->func;
+               while (cur_func) {
+                       /* TO DO: WILL MOST LIKELY NEED TO GET RID OF THE BUS STRUCTURE FROM RESOURCES AS WELL */
+                       if (cur_func->bus) {
+                               /* in other words, it's a PPB */
+                               count = 2;
+                       } else {
+                               count = 6;
+                       }
+
+                       for (i = 0; i < count; i++) {
+                               if (cur_func->io[count]) {
+                                       debug ("io[%d] exists \n", count);
+                                       if (the_end > 0)
+                                               ibmphp_remove_resource (cur_func->io[count]);
+                                       cur_func->io[count] = NULL;
+                               }
+                               if (cur_func->mem[count]) {
+                                       debug ("mem[%d] exists \n", count);
+                                       if (the_end > 0)
+                                               ibmphp_remove_resource (cur_func->mem[count]);
+                                       cur_func->mem[count] = NULL;
+                               }
+                               if (cur_func->pfmem[count]) {
+                                       debug ("pfmem[%d] exists \n", count);
+                                       if (the_end > 0)
+                                               ibmphp_remove_resource (cur_func->pfmem[count]);
+                                       cur_func->pfmem[count] = NULL;
+                               }
+                       }
+
+                       temp_func = cur_func->next;
+                       kfree (cur_func);
+                       cur_func = temp_func;
+               }
+       }
+
+       sl->func = NULL;
+       *slot_cur = sl;
+       return 0;
+}
+
+/*
+ * add a new bus resulting from hot-plugging a PPB bridge with devices
+ *
+ * Input: bus and the amount of resources needed (we know we can assign those,
+ *        since they've been checked already
+ * Output: bus added to the correct spot
+ *         0, -1, error 
+ */
+static int add_new_bus (struct bus_node *bus, struct resource_node *io, struct resource_node *mem, struct resource_node *pfmem, u8 parent_busno)
+{
+       struct range_node *io_range = NULL;
+       struct range_node *mem_range = NULL;
+       struct range_node *pfmem_range = NULL;
+       struct bus_node *cur_bus = NULL;
+
+       /* Trying to find the parent bus number */
+       cur_bus = ibmphp_find_res_bus (parent_busno);
+       if (!cur_bus) {
+               err ("strange, cannot find bus which is supposed to be at the system... something is terribly wrong...\n");
+               return -ENODEV;
+       }
+
+       list_add (&bus->bus_list, &cur_bus->bus_list);
+
+       if (io) {
+               io_range = kmalloc (sizeof (struct range_node), GFP_KERNEL);
+               if (!io_range) {
+                       err ("out of system memory \n");
+                       return -ENOMEM;
+               }
+               memset (io_range, 0, sizeof (struct range_node));
+               io_range->start = io->start;
+               io_range->end = io->end;
+               io_range->rangeno = 1;
+               bus->noIORanges = 1;
+               bus->rangeIO = io_range;
+       }
+       if (mem) {
+               mem_range = kmalloc (sizeof (struct range_node), GFP_KERNEL);
+               if (!mem_range) {
+                       err ("out of system memory \n");
+                       return -ENOMEM;
+               }
+               memset (mem_range, 0, sizeof (struct range_node));
+               mem_range->start = mem->start;
+               mem_range->end = mem->end;
+               mem_range->rangeno = 1;
+               bus->noMemRanges = 1;
+               bus->rangeMem = mem_range;
+       }
+       if (pfmem) {
+               pfmem_range = kmalloc (sizeof (struct range_node), GFP_KERNEL);
+               if (!pfmem_range) {     
+                       err ("out of system memory \n");
+                       return -ENOMEM;
+               }
+               memset (pfmem_range, 0, sizeof (struct range_node));
+               pfmem_range->start = pfmem->start;
+               pfmem_range->end = pfmem->end;
+               pfmem_range->rangeno = 1;
+               bus->noPFMemRanges = 1;
+               bus->rangePFMem = pfmem_range;
+       }
+       return 0;
+}
+
+/*
+ * find the 1st available bus number for PPB to set as its secondary bus
+ * Parameters: bus_number of the primary bus
+ * Returns: bus_number of the secondary bus or 0xff in case of failure
+ */
+static u8 find_sec_number (u8 primary_busno, u8 slotno)
+{
+       int min, max;
+       u8 busno;
+       struct bus_info *bus;
+
+       bus = ibmphp_find_same_bus_num (primary_busno);
+       if (!bus) {
+               err ("cannot get slot range of the bus from the BIOS\n");
+               return 0xff;
+       }
+       max = bus->slot_max;
+       min = bus->slot_min;
+       if ((slotno > max) || (slotno < min)) {
+               err ("got the wrong range\n");
+               return 0xff;
+       }
+       busno = (u8) (slotno - (u8) min);
+       busno += primary_busno + 0x01;
+       if (!ibmphp_find_res_bus (busno))
+               return busno;
+       return 0xff;
+}
+
diff --git a/drivers/hotplug/ibmphp_res.c b/drivers/hotplug/ibmphp_res.c
new file mode 100644 (file)
index 0000000..cfd4ad4
--- /dev/null
@@ -0,0 +1,2067 @@
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Irene Zubarev, IBM Corporation
+ *
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (c) 2001,2002 IBM Corp.
+ *
+ * All rights reserved.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <gregkh@us.ibm.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include "ibmphp.h"
+
+static int flags = 0;          /* for testing */
+
+static void update_resources (struct bus_node *bus_cur, int type, int rangeno);
+static int once_over (void);
+static int remove_ranges (struct bus_node *, struct bus_node *);
+static int update_bridge_ranges (struct bus_node **);
+static int add_range (int type, struct range_node *, struct bus_node *);
+static void fix_resources (struct bus_node *);
+static inline struct bus_node *find_bus_wprev (u8, struct bus_node **, u8);
+
+static LIST_HEAD(gbuses);
+
+static struct bus_node * alloc_error_bus (struct ebda_pci_rsrc * curr)
+{
+       struct bus_node * newbus;
+
+       newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL);
+       if (!newbus) {
+               err ("out of system memory \n");
+               return NULL;
+       }
+
+       memset (newbus, 0, sizeof (struct bus_node));
+       newbus->busno = curr->bus_num;
+       list_add_tail (&newbus->bus_list, &gbuses);
+       return newbus;
+}
+
+static struct resource_node * alloc_resources (struct ebda_pci_rsrc * curr)
+{
+       struct resource_node *rs = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+       if (!rs) {
+               err ("out of system memory \n");
+               return NULL;
+       }
+       memset (rs, 0, sizeof (struct resource_node));
+       rs->busno = curr->bus_num;
+       rs->devfunc = curr->dev_fun;
+       rs->start = curr->start_addr;
+       rs->end = curr->end_addr;
+       rs->len = curr->end_addr - curr->start_addr + 1;
+       return rs;
+}
+
+static int alloc_bus_range (struct bus_node **new_bus, struct range_node **new_range, struct ebda_pci_rsrc *curr, int flag, u8 first_bus)
+{
+       struct bus_node * newbus;
+       struct range_node *newrange;
+       u8 num_ranges = 0;
+
+       if (first_bus) {
+               newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL);
+               if (!newbus) {
+                       err ("out of system memory. \n");
+                       return -ENOMEM;
+               }
+               memset (newbus, 0, sizeof (struct bus_node));
+               newbus->busno = curr->bus_num;
+       } else {
+               newbus = *new_bus;
+               switch (flag) {
+                       case MEM:
+                               num_ranges = newbus->noMemRanges;
+                               break;
+                       case PFMEM:
+                               num_ranges = newbus->noPFMemRanges;
+                               break;
+                       case IO:
+                               num_ranges = newbus->noIORanges;
+                               break;
+               }
+       }
+
+       newrange = kmalloc (sizeof (struct range_node), GFP_KERNEL);
+       if (!newrange) {
+               if (first_bus)
+                       kfree (newbus);
+               err ("out of system memory \n");
+               return -ENOMEM;
+       }
+       memset (newrange, 0, sizeof (struct range_node));
+       newrange->start = curr->start_addr;
+       newrange->end = curr->end_addr;
+               
+       if (first_bus || (!num_ranges))
+               newrange->rangeno = 1;
+       else {
+               /* need to insert our range */
+               add_range (flag, newrange, newbus);
+               debug ("%d resource Primary Bus inserted on bus %x [%x - %x]\n", flag, newbus->busno, newrange->start, newrange->end);
+       }
+
+       switch (flag) {
+               case MEM:
+                       newbus->rangeMem = newrange;
+                       if (first_bus)
+                               newbus->noMemRanges = 1;
+                       else {
+                               debug ("First Memory Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+                               ++newbus->noMemRanges;
+                               fix_resources (newbus);
+                       }
+                       break;
+               case IO:
+                       newbus->rangeIO = newrange;
+                       if (first_bus)
+                               newbus->noIORanges = 1;
+                       else {
+                               debug ("First IO Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+                               ++newbus->noIORanges;
+                               fix_resources (newbus);
+                       }
+                       break;
+               case PFMEM:
+                       newbus->rangePFMem = newrange;
+                       if (first_bus)
+                               newbus->noPFMemRanges = 1;
+                       else {  
+                               debug ("1st PFMemory Primary on Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+                               ++newbus->noPFMemRanges;
+                               fix_resources (newbus);
+                       }
+
+                       break;
+       }
+
+       *new_bus = newbus;
+       *new_range = newrange;
+       return 0;
+}
+
+
+/* Notes:
+ * 1. The ranges are ordered.  The buses are not ordered.  (First come)
+ *
+ * 2. If cannot allocate out of PFMem range, allocate from Mem ranges.  PFmemFromMem
+ * are not sorted. (no need since use mem node). To not change the entire code, we
+ * also add mem node whenever this case happens so as not to change
+ * ibmphp_check_mem_resource etc (and since it really is taking Mem resource)
+ */
+
+/*****************************************************************************
+ * This is the Resource Management initialization function.  It will go through
+ * the Resource list taken from EBDA and fill in this module's data structures
+ *
+ * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES, 
+ * SINCE WE'RE GOING TO ASSUME FOR NOW WE DON'T HAVE THOSE ON OUR BUSES FOR NOW
+ *
+ * Input: ptr to the head of the resource list from EBDA
+ * Output: 0, -1 or error codes
+ ***************************************************************************/
+int ibmphp_rsrc_init (void)
+{
+       struct ebda_pci_rsrc *curr;
+       struct range_node *newrange = NULL;
+       struct bus_node *newbus = NULL;
+       struct bus_node *bus_cur;
+       struct bus_node *bus_prev;
+       struct list_head *tmp;
+       struct resource_node *new_io = NULL;
+       struct resource_node *new_mem = NULL;
+       struct resource_node *new_pfmem = NULL;
+       int rc;
+       struct list_head *tmp_ebda;
+
+       list_for_each (tmp_ebda, &ibmphp_ebda_pci_rsrc_head) {
+               curr = list_entry (tmp_ebda, struct ebda_pci_rsrc, ebda_pci_rsrc_list);
+               if (!(curr->rsrc_type & PCIDEVMASK)) {
+                       /* EBDA still lists non PCI devices, so ignore... */
+                       debug ("this is not a PCI DEVICE in rsrc_init, please take care\n");
+                       // continue;
+               }
+
+               /* this is a primary bus resource */
+               if (curr->rsrc_type & PRIMARYBUSMASK) {
+                       /* memory */
+                       if ((curr->rsrc_type & RESTYPE) == MMASK) {
+                               /* no bus structure exists in place yet */
+                               if (list_empty (&gbuses)) {
+                                       if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1)))
+                                               return rc;
+                                       list_add_tail (&newbus->bus_list, &gbuses);
+                                       debug ("gbuses = NULL, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+                               } else {
+                                       bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1);
+                                       /* found our bus */
+                                       if (bus_cur) {
+                                               rc = alloc_bus_range (&bus_cur, &newrange, curr, MEM, 0);
+                                               if (rc)
+                                                       return rc;
+                                       } else {
+                                               /* went through all the buses and didn't find ours, need to create a new bus node */
+                                               if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1)))
+                                                       return rc;
+
+                                               list_add_tail (&newbus->bus_list, &gbuses);
+                                               debug ("New Bus, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+                                       }
+                               }
+                       } else if ((curr->rsrc_type & RESTYPE) == PFMASK) {
+                               /* prefetchable memory */
+                               if (list_empty (&gbuses)) {
+                                       /* no bus structure exists in place yet */
+                                       if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1)))
+                                               return rc;
+                                       list_add_tail (&newbus->bus_list, &gbuses);
+                                       debug ("gbuses = NULL, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+                               } else {
+                                       bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1);
+                                       if (bus_cur) {
+                                               /* found our bus */
+                                               rc = alloc_bus_range (&bus_cur, &newrange, curr, PFMEM, 0);
+                                               if (rc)
+                                                       return rc;
+                                       } else {
+                                               /* went through all the buses and didn't find ours, need to create a new bus node */
+                                               if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1)))
+                                                       return rc;
+                                               list_add_tail (&newbus->bus_list, &gbuses);
+                                               debug ("1st Bus, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+                                       }
+                               }
+                       } else if ((curr->rsrc_type & RESTYPE) == IOMASK) {
+                               /* IO */
+                               if (list_empty (&gbuses)) {
+                                       /* no bus structure exists in place yet */
+                                       if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1)))
+                                               return rc;
+                                       list_add_tail (&newbus->bus_list, &gbuses);
+                                       debug ("gbuses = NULL, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+                               } else {
+                                       bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1);
+                                       if (bus_cur) {
+                                               rc = alloc_bus_range (&bus_cur, &newrange, curr, IO, 0);
+                                               if (rc)
+                                                       return rc;
+                                       } else {
+                                               /* went through all the buses and didn't find ours, need to create a new bus node */
+                                               if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1)))
+                                                       return rc;
+                                               list_add_tail (&newbus->bus_list, &gbuses);
+                                               debug ("1st Bus, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
+                                       }
+                               }
+
+                       } else {
+                               ;       /* type is reserved  WHAT TO DO IN THIS CASE???
+                                          NOTHING TO DO??? */
+                       }
+               } else {
+                       /* regular pci device resource */
+                       if ((curr->rsrc_type & RESTYPE) == MMASK) {
+                               /* Memory resource */
+                               new_mem = alloc_resources (curr);
+                               if (!new_mem)
+                                       return -ENOMEM;
+                               new_mem->type = MEM;
+                               /*
+                                * if it didn't find the bus, means PCI dev
+                                * came b4 the Primary Bus info, so need to
+                                * create a bus rangeno becomes a problem...
+                                * assign a -1 and then update once the range
+                                * actually appears...
+                                */
+                               if (ibmphp_add_resource (new_mem) < 0) {
+                                       newbus = alloc_error_bus (curr);
+                                       if (!newbus)
+                                               return -ENOMEM;
+                                       newbus->firstMem = new_mem;
+                                       ++newbus->needMemUpdate;
+                                       new_mem->rangeno = -1;
+                               }
+                               debug ("Memory resource for device %x, bus %x, [%x - %x]\n", new_mem->devfunc, new_mem->busno, new_mem->start, new_mem->end);
+
+                       } else if ((curr->rsrc_type & RESTYPE) == PFMASK) {
+                               /* PFMemory resource */
+                               new_pfmem = alloc_resources (curr);
+                               if (!new_pfmem)
+                                       return -ENOMEM;
+                               new_pfmem->type = PFMEM;
+                               new_pfmem->fromMem = FALSE;
+                               if (ibmphp_add_resource (new_pfmem) < 0) {
+                                       newbus = alloc_error_bus (curr);
+                                       if (!newbus)
+                                               return -ENOMEM;
+                                       newbus->firstPFMem = new_pfmem;
+                                       ++newbus->needPFMemUpdate;
+                                       new_pfmem->rangeno = -1;
+                               }
+
+                               debug ("PFMemory resource for device %x, bus %x, [%x - %x]\n", new_pfmem->devfunc, new_pfmem->busno, new_pfmem->start, new_pfmem->end);
+                       } else if ((curr->rsrc_type & RESTYPE) == IOMASK) {
+                               /* IO resource */
+                               new_io = alloc_resources (curr);
+                               if (!new_io)
+                                       return -ENOMEM;
+                               new_io->type = IO;
+
+                               /*
+                                * if it didn't find the bus, means PCI dev
+                                * came b4 the Primary Bus info, so need to
+                                * create a bus rangeno becomes a problem...
+                                * Can assign a -1 and then update once the
+                                * range actually appears...
+                                */
+                               if (ibmphp_add_resource (new_io) < 0) {
+                                       newbus = alloc_error_bus (curr);
+                                       if (!newbus)
+                                               return -ENOMEM;
+                                       newbus->firstIO = new_io;
+                                       ++newbus->needIOUpdate;
+                                       new_io->rangeno = -1;
+                               }
+                               debug ("IO resource for device %x, bus %x, [%x - %x]\n", new_io->devfunc, new_io->busno, new_io->start, new_io->end);
+                       }
+               }
+       }
+
+       debug ("after the while loop in rsrc_init \n");
+
+       list_for_each (tmp, &gbuses) {
+               bus_cur = list_entry (tmp, struct bus_node, bus_list);
+               /* This is to get info about PPB resources, since EBDA doesn't put this info into the primary bus info */
+               rc = update_bridge_ranges (&bus_cur);
+               if (rc)
+                       return rc;
+       }
+       debug ("b4 once_over in rsrc_init \n");
+       rc = once_over ();  /* This is to align ranges (so no -1) */
+       if (rc)
+               return rc;
+       debug ("after once_over in rsrc_init \n");
+       return 0;
+}
+
+/********************************************************************************
+ * This function adds a range into a sorted list of ranges per bus for a particular
+ * range type, it then calls another routine to update the range numbers on the
+ * pci devices' resources for the appropriate resource
+ *
+ * Input: type of the resource, range to add, current bus
+ * Output: 0 or -1, bus and range ptrs 
+ ********************************************************************************/
+static int add_range (int type, struct range_node *range, struct bus_node *bus_cur)
+{
+       struct range_node *range_cur = NULL;
+       struct range_node *range_prev;
+       int count = 0, i_init;
+       int noRanges = 0;
+
+       switch (type) {
+               case MEM:
+                       range_cur = bus_cur->rangeMem;
+                       noRanges = bus_cur->noMemRanges;
+                       break;
+               case PFMEM:
+                       range_cur = bus_cur->rangePFMem;
+                       noRanges = bus_cur->noPFMemRanges;
+                       break;
+               case IO:
+                       range_cur = bus_cur->rangeIO;
+                       noRanges = bus_cur->noIORanges;
+                       break;
+       }
+
+       range_prev = NULL;
+       while (range_cur) {
+               if (range->start < range_cur->start)
+                       break;
+               range_prev = range_cur;
+               range_cur = range_cur->next;
+               count = count + 1;
+       }
+       if (!count) {
+               /* our range will go at the beginning of the list */
+               switch (type) {
+                       case MEM:
+                               bus_cur->rangeMem = range;
+                               break;
+                       case PFMEM:
+                               bus_cur->rangePFMem = range;
+                               break;
+                       case IO:
+                               bus_cur->rangeIO = range;
+                               break;
+               }
+               range->next = range_cur;
+               range->rangeno = 1;
+               i_init = 0;
+       } else if (!range_cur) {
+               /* our range will go at the end of the list */
+               range->next = NULL;
+               range_prev->next = range;
+               range->rangeno = range_prev->rangeno + 1;
+               return 0;
+       } else {
+               /* the range is in the middle */
+               range_prev->next = range;
+               range->next = range_cur;
+               range->rangeno = range_cur->rangeno;
+               i_init = range_prev->rangeno;
+       }
+
+       for (count = i_init; count < noRanges; ++count) {
+               ++range_cur->rangeno;
+               range_cur = range_cur->next;
+       }
+
+       update_resources (bus_cur, type, i_init + 1);
+       return 0;
+}
+
+/*******************************************************************************
+ * This routine goes through the list of resources of type 'type' and updates
+ * the range numbers that they correspond to.  It was called from add_range fnc
+ *
+ * Input: bus, type of the resource, the rangeno starting from which to update
+ ******************************************************************************/
+static void update_resources (struct bus_node *bus_cur, int type, int rangeno)
+{
+       struct resource_node *res = NULL;
+       u8 eol = FALSE; /* end of list indicator */
+
+       switch (type) {
+               case MEM:
+                       if (bus_cur->firstMem) 
+                               res = bus_cur->firstMem;
+                       break;
+               case PFMEM:
+                       if (bus_cur->firstPFMem)
+                               res = bus_cur->firstPFMem;
+                       break;
+               case IO:
+                       if (bus_cur->firstIO)
+                               res = bus_cur->firstIO;
+                       break;
+       }
+
+       if (res) {
+               while (res) {
+                       if (res->rangeno == rangeno)
+                               break;
+                       if (res->next)
+                               res = res->next;
+                       else if (res->nextRange)
+                               res = res->nextRange;
+                       else {
+                               eol = TRUE;
+                               break;
+                       }
+               }
+
+               if (!eol) {
+                       /* found the range */
+                       while (res) {
+                               ++res->rangeno;
+                               res = res->next;
+                       }
+               }
+       }
+}
+
+static void fix_me (struct resource_node *res, struct bus_node *bus_cur, struct range_node *range)
+{
+       char * str = "";
+       switch (res->type) {
+               case IO:
+                       str = "io";
+                       break;
+               case MEM:
+                       str = "mem";
+                       break;
+               case PFMEM:
+                       str = "pfmem";
+                       break;
+       }
+
+       while (res) {
+               if (res->rangeno == -1) {
+                       while (range) {
+                               if ((res->start >= range->start) && (res->end <= range->end)) {
+                                       res->rangeno = range->rangeno;
+                                       debug ("%s->rangeno in fix_resources is %d\n", str, res->rangeno);
+                                       switch (res->type) {
+                                               case IO:
+                                                       --bus_cur->needIOUpdate;
+                                                       break;
+                                               case MEM:
+                                                       --bus_cur->needMemUpdate;
+                                                       break;
+                                               case PFMEM:
+                                                       --bus_cur->needPFMemUpdate;
+                                                       break;
+                                       }
+                                       break;
+                               }
+                               range = range->next;
+                       }
+               }
+               if (res->next)
+                       res = res->next;
+               else
+                       res = res->nextRange;
+       }
+
+}
+
+/*****************************************************************************
+ * This routine reassigns the range numbers to the resources that had a -1
+ * This case can happen only if upon initialization, resources taken by pci dev
+ * appear in EBDA before the resources allocated for that bus, since we don't
+ * know the range, we assign -1, and this routine is called after a new range
+ * is assigned to see the resources with unknown range belong to the added range
+ *
+ * Input: current bus
+ * Output: none, list of resources for that bus are fixed if can be
+ *******************************************************************************/
+static void fix_resources (struct bus_node *bus_cur)
+{
+       struct range_node *range;
+       struct resource_node *res;
+
+       debug ("%s - bus_cur->busno = %d\n", __FUNCTION__, bus_cur->busno);
+
+       if (bus_cur->needIOUpdate) {
+               res = bus_cur->firstIO;
+               range = bus_cur->rangeIO;
+               fix_me (res, bus_cur, range);
+       }
+       if (bus_cur->needMemUpdate) {
+               res = bus_cur->firstMem;
+               range = bus_cur->rangeMem;
+               fix_me (res, bus_cur, range);
+       }
+       if (bus_cur->needPFMemUpdate) {
+               res = bus_cur->firstPFMem;
+               range = bus_cur->rangePFMem;
+               fix_me (res, bus_cur, range);
+       }
+}
+
+/*******************************************************************************
+ * This routine adds a resource to the list of resources to the appropriate bus 
+ * based on their resource type and sorted by their starting addresses.  It assigns
+ * the ptrs to next and nextRange if needed.
+ *
+ * Input: 3 diff. resources (nulled out if not needed)
+ * Output: ptrs assigned (to the node)
+ * 0 or -1
+ *******************************************************************************/
+int ibmphp_add_resource (struct resource_node *res)
+{
+       struct resource_node *res_cur;
+       struct resource_node *res_prev;
+       struct bus_node *bus_cur;
+       struct range_node *range_cur = NULL;
+       struct resource_node *res_start = NULL;
+
+       debug ("%s - enter\n", __FUNCTION__);
+       
+       bus_cur = find_bus_wprev (res->busno, NULL, 0);
+       
+       if (!bus_cur) {
+               /* didn't find a bus, smth's wrong!!! */
+               err ("no bus in the system, either pci_dev's wrong or allocation failed\n");
+               return -ENODEV;
+       }
+
+       /* Normal case */
+       switch (res->type) {
+               case IO:
+                       range_cur = bus_cur->rangeIO;
+                       res_start = bus_cur->firstIO;
+                       break;
+               case MEM:
+                       range_cur = bus_cur->rangeMem;
+                       res_start = bus_cur->firstMem;
+                       break;
+               case PFMEM:
+                       range_cur = bus_cur->rangePFMem;
+                       res_start = bus_cur->firstPFMem;
+                       break;
+               default:
+                       err ("cannot read the type of the resource to add... problem \n");
+                       return -EINVAL;
+       }
+       while (range_cur) {
+               if ((res->start >= range_cur->start) && (res->end <= range_cur->end)) {
+                       res->rangeno = range_cur->rangeno;
+                       break;
+               }
+               range_cur = range_cur->next;
+       }
+
+       /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+        * this is again the case of rangeno = -1
+        * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+        */
+
+       if (!range_cur) {
+               switch (res->type) {
+                       case IO:
+                               ++bus_cur->needIOUpdate;                                        
+                               break;
+                       case MEM:
+                               ++bus_cur->needMemUpdate;
+                               break;
+                       case PFMEM:
+                               ++bus_cur->needPFMemUpdate;
+                               break;
+               }
+               res->rangeno = -1;
+       }
+       
+       debug ("The range is %d\n", res->rangeno);
+       if (!res_start) {
+               /* no first{IO,Mem,Pfmem} on the bus, 1st IO/Mem/Pfmem resource ever */
+               switch (res->type) {
+                       case IO:
+                               bus_cur->firstIO = res;                                 
+                               break;
+                       case MEM:
+                               bus_cur->firstMem = res;
+                               break;
+                       case PFMEM:
+                               bus_cur->firstPFMem = res;
+                               break;
+               }       
+               res->next = NULL;
+               res->nextRange = NULL;
+       } else {
+               res_cur = res_start;
+               res_prev = NULL;
+
+               debug ("res_cur->rangeno is %d\n", res_cur->rangeno);
+
+               while (res_cur) {
+                       if (res_cur->rangeno >= res->rangeno)
+                               break;
+                       res_prev = res_cur;
+                       if (res_cur->next)
+                               res_cur = res_cur->next;
+                       else
+                               res_cur = res_cur->nextRange;
+               }
+
+               if (!res_cur) {
+                       /* at the end of the resource list */
+                       debug ("i should be here, [%x - %x]\n", res->start, res->end);
+                       res_prev->nextRange = res;
+                       res->next = NULL;
+                       res->nextRange = NULL;
+               } else if (res_cur->rangeno == res->rangeno) {
+                       /* in the same range */
+                       while (res_cur) {
+                               if (res->start < res_cur->start)
+                                       break;
+                               res_prev = res_cur;
+                               res_cur = res_cur->next;
+                       }
+                       if (!res_cur) {
+                               /* the last resource in this range */
+                               res_prev->next = res;
+                               res->next = NULL;
+                               res->nextRange = res_prev->nextRange;
+                               res_prev->nextRange = NULL;
+                       } else if (res->start < res_cur->start) {
+                               /* at the beginning or middle of the range */
+                               if (!res_prev)  {
+                                       switch (res->type) {
+                                               case IO:
+                                                       bus_cur->firstIO = res;
+                                                       break;
+                                               case MEM:
+                                                       bus_cur->firstMem = res;
+                                                       break;
+                                               case PFMEM:
+                                                       bus_cur->firstPFMem = res;
+                                                       break;
+                                       }
+                               } else if (res_prev->rangeno == res_cur->rangeno)
+                                       res_prev->next = res;
+                               else
+                                       res_prev->nextRange = res;
+
+                               res->next = res_cur;
+                               res->nextRange = NULL;
+                       }
+               } else {
+                       /* this is the case where it is 1st occurence of the range */
+                       if (!res_prev) {
+                               /* at the beginning of the resource list */
+                               res->next = NULL;
+                               switch (res->type) {
+                                       case IO:
+                                               res->nextRange = bus_cur->firstIO;
+                                               bus_cur->firstIO = res;
+                                               break;
+                                       case MEM:
+                                               res->nextRange = bus_cur->firstMem;
+                                               bus_cur->firstMem = res;
+                                               break;
+                                       case PFMEM:
+                                               res->nextRange = bus_cur->firstPFMem;
+                                               bus_cur->firstPFMem = res;
+                                               break;
+                               }
+                       } else if (res_cur->rangeno > res->rangeno) {
+                               /* in the middle of the resource list */
+                               res_prev->nextRange = res;
+                               res->next = NULL;
+                               res->nextRange = res_cur;
+                       }
+               }
+       }
+
+       debug ("%s - exit\n", __FUNCTION__);
+       return 0;
+}
+
+/****************************************************************************
+ * This routine will remove the resource from the list of resources
+ *
+ * Input: io, mem, and/or pfmem resource to be deleted
+ * Ouput: modified resource list
+ *        0 or error code
+ ****************************************************************************/
+int ibmphp_remove_resource (struct resource_node *res)
+{
+       struct bus_node *bus_cur;
+       struct resource_node *res_cur = NULL;
+       struct resource_node *res_prev;
+       struct resource_node *mem_cur;
+       char * type = "";
+
+       bus_cur = find_bus_wprev (res->busno, NULL, 0);
+
+       if (!bus_cur) {
+               err ("cannot find corresponding bus of the io resource to remove  "
+                       "bailing out...\n");
+               return -ENODEV;
+       }
+
+       switch (res->type) {
+               case IO:
+                       res_cur = bus_cur->firstIO;
+                       type = "io";
+                       break;
+               case MEM:
+                       res_cur = bus_cur->firstMem;
+                       type = "mem";
+                       break;
+               case PFMEM:
+                       res_cur = bus_cur->firstPFMem;
+                       type = "pfmem";
+                       break;
+               default:
+                       err ("unknown type for resource to remove \n");
+                       return -EINVAL;
+       }
+       res_prev = NULL;
+
+       while (res_cur) {
+               /* ???????????DO WE _NEED_ TO BE CHECKING FOR END AS WELL?????????? */
+               if ((res_cur->start == res->start) && (res_cur->end == res->end))
+                       break;
+               res_prev = res_cur;
+               if (res_cur->next)
+                       res_cur = res_cur->next;
+               else
+                       res_cur = res_cur->nextRange;
+       }
+
+       if (!res_cur) {
+               if (res->type == PFMEM) {
+                       /* 
+                        * case where pfmem might be in the PFMemFromMem list
+                        * so will also need to remove the corresponding mem
+                        * entry
+                        */
+                       res_cur = bus_cur->firstPFMemFromMem;
+                       res_prev = NULL;
+
+                       while (res_cur) {
+                               if ((res_cur->start == res->start) && (res_cur->end == res->end)) {
+                                       mem_cur = bus_cur->firstMem;
+                                       while (mem_cur) {
+                                               if ((mem_cur->start == res_cur->start)
+                                                   && (mem_cur->end == res_cur->end))
+                                                       break;
+                                               if (mem_cur->next)
+                                                       mem_cur = mem_cur->next;
+                                               else
+                                                       mem_cur = mem_cur->nextRange;
+                                       }
+                                       if (!mem_cur) {
+                                               err ("cannot find corresponding mem node for pfmem...\n");
+                                               return -EINVAL;
+                                       }
+
+                                       ibmphp_remove_resource (mem_cur);
+                                       if (!res_prev)
+                                               bus_cur->firstPFMemFromMem = res_cur->next;
+                                       else
+                                               res_prev->next = res_cur->next;
+                                       kfree (res_cur);
+                                       return 0;
+                               }
+                               res_prev = res_cur;
+                               if (res_cur->next)
+                                       res_cur = res_cur->next;
+                               else
+                                       res_cur = res_cur->nextRange;
+                       }
+                       if (!res_cur) {
+                               err ("cannot find pfmem to delete...\n");
+                               return -EINVAL;
+                       }
+               } else {
+                       err ("the %s resource is not in the list to be deleted...\n", type);
+                       return -EINVAL;
+               }
+       }
+       if (!res_prev) {
+               /* first device to be deleted */
+               if (res_cur->next) {
+                       switch (res->type) {
+                               case IO:
+                                       bus_cur->firstIO = res_cur->next;
+                                       break;
+                               case MEM:
+                                       bus_cur->firstMem = res_cur->next;
+                                       break;
+                               case PFMEM:
+                                       bus_cur->firstPFMem = res_cur->next;
+                                       break;
+                       }
+               } else if (res_cur->nextRange) {
+                       switch (res->type) {
+                               case IO:
+                                       bus_cur->firstIO = res_cur->nextRange;
+                                       break;
+                               case MEM:
+                                       bus_cur->firstMem = res_cur->nextRange;
+                                       break;
+                               case PFMEM:
+                                       bus_cur->firstPFMem = res_cur->nextRange;
+                                       break;
+                       }
+               } else {
+                       switch (res->type) {
+                               case IO:
+                                       bus_cur->firstIO = NULL;
+                                       break;
+                               case MEM:
+                                       bus_cur->firstMem = NULL;
+                                       break;
+                               case PFMEM:
+                                       bus_cur->firstPFMem = NULL;
+                                       break;
+                       }
+               }
+               kfree (res_cur);
+               return 0;
+       } else {
+               if (res_cur->next) {
+                       if (res_prev->rangeno == res_cur->rangeno)
+                               res_prev->next = res_cur->next;
+                       else
+                               res_prev->nextRange = res_cur->next;
+               } else if (res_cur->nextRange) {
+                       res_prev->next = NULL;
+                       res_prev->nextRange = res_cur->nextRange;
+               } else {
+                       res_prev->next = NULL;
+                       res_prev->nextRange = NULL;
+               }
+               kfree (res_cur);
+               return 0;
+       }
+
+       return 0;
+}
+
+static struct range_node * find_range (struct bus_node *bus_cur, struct resource_node * res)
+{
+       struct range_node * range = NULL;
+
+       switch (res->type) {
+               case IO:
+                       range = bus_cur->rangeIO;
+                       break;
+               case MEM:
+                       range = bus_cur->rangeMem;
+                       break;
+               case PFMEM:
+                       range = bus_cur->rangePFMem;
+                       break;
+               default:
+                       err ("cannot read resource type in find_range \n");
+       }
+
+       while (range) {
+               if (res->rangeno == range->rangeno)
+                       break;
+               range = range->next;
+       }
+       return range;
+}
+
+/*****************************************************************************
+ * This routine will check to make sure the io/mem/pfmem->len that the device asked for 
+ * can fit w/i our list of available IO/MEM/PFMEM resources.  If cannot, returns -EINVAL,
+ * otherwise, returns 0
+ *
+ * Input: resource
+ * Ouput: the correct start and end address are inputted into the resource node,
+ *        0 or -EINVAL
+ *****************************************************************************/
+int ibmphp_check_resource (struct resource_node *res, u8 bridge)
+{
+       struct bus_node *bus_cur;
+       struct range_node *range = NULL;
+       struct resource_node *res_prev;
+       struct resource_node *res_cur = NULL;
+       u32 len_cur = 0, start_cur = 0, len_tmp = 0;
+       int noranges = 0;
+       u32 tmp_start;          /* this is to make sure start address is divisible by the length needed */
+       u32 tmp_divide;
+       u8 flag = FALSE;
+
+       if (!res)
+               return -EINVAL;
+
+       if (bridge) {
+               /* The rules for bridges are different, 4K divisible for IO, 1M for (pf)mem*/
+               if (res->type == IO)
+                       tmp_divide = IOBRIDGE;
+               else
+                       tmp_divide = MEMBRIDGE;
+       } else
+               tmp_divide = res->len;
+
+       bus_cur = find_bus_wprev (res->busno, NULL, 0);
+
+       if (!bus_cur) {
+               /* didn't find a bus, smth's wrong!!! */
+               err ("no bus in the system, either pci_dev's wrong or allocation failed \n");
+               return -EINVAL;
+       }
+
+       debug ("%s - enter\n", __FUNCTION__);
+       debug ("bus_cur->busno is %d\n", bus_cur->busno);
+
+       /* This is a quick fix to not mess up with the code very much.  i.e.,
+        * 2000-2fff, len = 1000, but when we compare, we need it to be fff */
+       res->len -= 1;
+
+       switch (res->type) {
+               case IO:
+                       res_cur = bus_cur->firstIO;
+                       noranges = bus_cur->noIORanges;
+                       break;
+               case MEM:
+                       res_cur = bus_cur->firstMem;
+                       noranges = bus_cur->noMemRanges;
+                       break;
+               case PFMEM:
+                       res_cur = bus_cur->firstPFMem;
+                       noranges = bus_cur->noPFMemRanges;
+                       break;
+               default:
+                       err ("wrong type of resource to check \n");
+                       return -EINVAL;
+       }
+       res_prev = NULL;
+
+       while (res_cur) {
+               range = find_range (bus_cur, res_cur);
+               debug ("%s - rangeno = %d\n", __FUNCTION__, res_cur->rangeno);
+
+               if (!range) {
+                       err ("no range for the device exists... bailing out...\n");
+                       return -EINVAL;
+               }
+
+               /* found our range */
+               if (!res_prev) {
+                       /* first time in the loop */
+                       if ((res_cur->start != range->start) && ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) {
+                               debug ("len_tmp = %x\n", len_tmp);
+
+                               if ((len_tmp < len_cur) || (len_cur == 0)) {
+
+                                       if ((range->start % tmp_divide) == 0) {
+                                               /* just perfect, starting address is divisible by length */
+                                               flag = TRUE;
+                                               len_cur = len_tmp;
+                                               start_cur = range->start;
+                                       } else {
+                                               /* Needs adjusting */
+                                               tmp_start = range->start;
+                                               flag = FALSE;
+
+                                               while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
+                                                       if ((tmp_start % tmp_divide) == 0) {
+                                                               flag = TRUE;
+                                                               len_cur = len_tmp;
+                                                               start_cur = tmp_start;
+                                                               break;
+                                                       }
+                                                       tmp_start += tmp_divide - tmp_start % tmp_divide;
+                                                       if (tmp_start >= res_cur->start - 1)
+                                                               break;
+                                               }
+                                       }
+                       
+                                       if (flag && len_cur == res->len) {
+                                               debug ("but we are not here, right?\n");
+                                               res->start = start_cur;
+                                               res->len += 1; /* To restore the balance */
+                                               res->end = res->start + res->len - 1;
+                                               return 0;
+                                       }
+                               }
+                       }
+               }
+               if (!res_cur->next) {
+                       /* last device on the range */
+                       if ((range->end != res_cur->end) && ((len_tmp = range->end - (res_cur->end + 1)) >= res->len)) {
+                               debug ("len_tmp = %x\n", len_tmp);
+                               if ((len_tmp < len_cur) || (len_cur == 0)) {
+
+                                       if (((res_cur->end + 1) % tmp_divide) == 0) {
+                                               /* just perfect, starting address is divisible by length */
+                                               flag = TRUE;
+                                               len_cur = len_tmp;
+                                               start_cur = res_cur->end + 1;
+                                       } else {
+                                               /* Needs adjusting */
+                                               tmp_start = res_cur->end + 1;
+                                               flag = FALSE;
+
+                                               while ((len_tmp = range->end - tmp_start) >= res->len) {
+                                                       if ((tmp_start % tmp_divide) == 0) {
+                                                               flag = TRUE;
+                                                               len_cur = len_tmp;
+                                                               start_cur = tmp_start;
+                                                               break;
+                                                       }
+                                                       tmp_start += tmp_divide - tmp_start % tmp_divide;
+                                                       if (tmp_start >= range->end)
+                                                               break;
+                                               }
+                                       }
+                                       if (flag && len_cur == res->len) {
+                                               res->start = start_cur;
+                                               res->len += 1; /* To restore the balance */
+                                               res->end = res->start + res->len - 1;
+                                               return 0;
+                                       }
+                               }
+                       }
+               }
+
+               if (res_prev) {
+                       if (res_prev->rangeno != res_cur->rangeno) {
+                               /* 1st device on this range */
+                               if ((res_cur->start != range->start) && 
+                                       ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) {
+                                       if ((len_tmp < len_cur) || (len_cur == 0)) {
+                                               if ((range->start % tmp_divide) == 0) { 
+                                                       /* just perfect, starting address is divisible by length */
+                                                       flag = TRUE;
+                                                       len_cur = len_tmp;
+                                                       start_cur = range->start;
+                                               } else {
+                                                       /* Needs adjusting */
+                                                       tmp_start = range->start;
+                                                       flag = FALSE;
+
+                                                       while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
+                                                               if ((tmp_start % tmp_divide) == 0) {
+                                                                       flag = TRUE;
+                                                                       len_cur = len_tmp;
+                                                                       start_cur = tmp_start;
+                                                                       break;
+                                                               }
+                                                               tmp_start += tmp_divide - tmp_start % tmp_divide;
+                                                               if (tmp_start >= res_cur->start - 1)
+                                                                       break;
+                                                       }
+                                               }
+
+                                               if (flag && len_cur == res->len) {
+                                                       res->start = start_cur;
+                                                       res->len += 1; /* To restore the balance */
+                                                       res->end = res->start + res->len - 1;
+                                                       return 0;
+                                               }
+                                       }
+                               }
+                       } else {
+                               /* in the same range */
+                               if ((len_tmp = res_cur->start - 1 - res_prev->end - 1) >= res->len) {
+                                       if ((len_tmp < len_cur) || (len_cur == 0)) {
+                                               if (((res_prev->end + 1) % tmp_divide) == 0) {
+                                                       /* just perfect, starting address's divisible by length */
+                                                       flag = TRUE;
+                                                       len_cur = len_tmp;
+                                                       start_cur = res_prev->end + 1;
+                                               } else {
+                                                       /* Needs adjusting */
+                                                       tmp_start = res_prev->end + 1;
+                                                       flag = FALSE;
+
+                                                       while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
+                                                               if ((tmp_start % tmp_divide) == 0) {
+                                                                       flag = TRUE;
+                                                                       len_cur = len_tmp;
+                                                                       start_cur = tmp_start;
+                                                                       break;
+                                                               }
+                                                               tmp_start += tmp_divide - tmp_start % tmp_divide;
+                                                               if (tmp_start >= res_cur->start - 1)
+                                                                       break;
+                                                       }
+                                               }
+
+                                               if (flag && len_cur == res->len) {
+                                                       res->start = start_cur;
+                                                       res->len += 1; /* To restore the balance */
+                                                       res->end = res->start + res->len - 1;
+                                                       return 0;
+                                               }
+                                       }
+                               }
+                       }
+               }
+               /* end if (res_prev) */
+               res_prev = res_cur;
+               if (res_cur->next)
+                       res_cur = res_cur->next;
+               else
+                       res_cur = res_cur->nextRange;
+       }       /* end of while */
+
+
+       if (!res_prev) {
+               /* 1st device ever */
+               /* need to find appropriate range */
+               switch (res->type) {
+                       case IO:
+                               range = bus_cur->rangeIO;
+                               break;
+                       case MEM:
+                               range = bus_cur->rangeMem;
+                               break;
+                       case PFMEM:
+                               range = bus_cur->rangePFMem;
+                               break;
+               }
+               while (range) {
+                       if ((len_tmp = range->end - range->start) >= res->len) {
+                               if ((len_tmp < len_cur) || (len_cur == 0)) {
+                                       if ((range->start % tmp_divide) == 0) {
+                                               /* just perfect, starting address's divisible by length */
+                                               flag = TRUE;
+                                               len_cur = len_tmp;
+                                               start_cur = range->start;
+                                       } else {
+                                               /* Needs adjusting */
+                                               tmp_start = range->start;
+                                               flag = FALSE;
+
+                                               while ((len_tmp = range->end - tmp_start) >= res->len) {
+                                                       if ((tmp_start % tmp_divide) == 0) {
+                                                               flag = TRUE;
+                                                               len_cur = len_tmp;
+                                                               start_cur = tmp_start;
+                                                               break;
+                                                       }
+                                                       tmp_start += tmp_divide - tmp_start % tmp_divide;
+                                                       if (tmp_start >= range->end)
+                                                               break;
+                                               }
+                                       }
+
+                                       if (flag && len_cur == res->len) {
+                                               res->start = start_cur;
+                                               res->len += 1; /* To restore the balance */
+                                               res->end = res->start + res->len - 1;
+                                               return 0;
+                                       }
+                               }
+                       }
+                       range = range->next;
+               }               /* end of while */
+
+               if ((!range) && (len_cur == 0)) {
+                       /* have gone through the list of devices and ranges and haven't found n.e.thing */
+                       err ("no appropriate range.. bailing out...\n");
+                       return -EINVAL;
+               } else if (len_cur) {
+                       res->start = start_cur;
+                       res->len += 1; /* To restore the balance */
+                       res->end = res->start + res->len - 1;
+                       return 0;
+               }
+       }
+
+       if (!res_cur) {
+               debug ("prev->rangeno = %d, noranges = %d\n", res_prev->rangeno, noranges);
+               if (res_prev->rangeno < noranges) {
+                       /* if there're more ranges out there to check */
+                       switch (res->type) {
+                               case IO:
+                                       range = bus_cur->rangeIO;
+                                       break;
+                               case MEM:
+                                       range = bus_cur->rangeMem;
+                                       break;
+                               case PFMEM:
+                                       range = bus_cur->rangePFMem;
+                                       break;
+                       }
+                       while (range) {
+                               if ((len_tmp = range->end - range->start) >= res->len) {
+                                       if ((len_tmp < len_cur) || (len_cur == 0)) {
+                                               if ((range->start % tmp_divide) == 0) {
+                                                       /* just perfect, starting address's divisible by length */
+                                                       flag = TRUE;
+                                                       len_cur = len_tmp;
+                                                       start_cur = range->start;
+                                               } else {
+                                                       /* Needs adjusting */
+                                                       tmp_start = range->start;
+                                                       flag = FALSE;
+
+                                                       while ((len_tmp = range->end - tmp_start) >= res->len) {
+                                                               if ((tmp_start % tmp_divide) == 0) {
+                                                                       flag = TRUE;
+                                                                       len_cur = len_tmp;
+                                                                       start_cur = tmp_start;
+                                                                       break;
+                                                               }
+                                                               tmp_start += tmp_divide - tmp_start % tmp_divide;
+                                                               if (tmp_start >= range->end)
+                                                                       break;
+                                                       }
+                                               }
+
+                                               if (flag && len_cur == res->len) {
+                                                       res->start = start_cur;
+                                                       res->len += 1; /* To restore the balance */
+                                                       res->end = res->start + res->len - 1;
+                                                       return 0;
+                                               }
+                                       }
+                               }
+                               range = range->next;
+                       }       /* end of while */
+
+                       if ((!range) && (len_cur == 0)) {
+                               /* have gone through the list of devices and ranges and haven't found n.e.thing */
+                               err ("no appropriate range.. bailing out...\n");
+                               return -EINVAL;
+                       } else if (len_cur) {
+                               res->start = start_cur;
+                               res->len += 1; /* To restore the balance */
+                               res->end = res->start + res->len - 1;
+                               return 0;
+                       }
+               } else {
+                       /* no more ranges to check on */
+                       if (len_cur) {
+                               res->start = start_cur;
+                               res->len += 1; /* To restore the balance */
+                               res->end = res->start + res->len - 1;
+                               return 0;
+                       } else {
+                               /* have gone through the list of devices and haven't found n.e.thing */
+                               err ("no appropriate range.. bailing out...\n");
+                               return -EINVAL;
+                       }
+               }
+       }       /* end if(!res_cur) */
+       return -EINVAL;
+}
+
+/********************************************************************************
+ * This routine is called from remove_card if the card contained PPB.
+ * It will remove all the resources on the bus as well as the bus itself
+ * Input: Bus
+ * Ouput: 0, -ENODEV
+ ********************************************************************************/
+int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno)
+{
+       struct resource_node *res_cur;
+       struct resource_node *res_tmp;
+       struct bus_node *prev_bus;
+       int rc;
+
+       prev_bus = find_bus_wprev (parent_busno, NULL, 0);      
+
+       if (!prev_bus) {
+               err ("something terribly wrong. Cannot find parent bus to the one to remove\n");
+               return -ENODEV;
+       }
+
+       debug ("In ibmphp_remove_bus... prev_bus->busno is %x\n", prev_bus->busno);
+
+       rc = remove_ranges (bus, prev_bus);
+       if (rc)
+               return rc;
+
+       if (bus->firstIO) {
+               res_cur = bus->firstIO;
+               while (res_cur) {
+                       res_tmp = res_cur;
+                       if (res_cur->next)
+                               res_cur = res_cur->next;
+                       else
+                               res_cur = res_cur->nextRange;
+                       kfree (res_tmp);
+                       res_tmp = NULL;
+               }
+               bus->firstIO = NULL;
+       }
+       if (bus->firstMem) {
+               res_cur = bus->firstMem;
+               while (res_cur) {
+                       res_tmp = res_cur;
+                       if (res_cur->next)
+                               res_cur = res_cur->next;
+                       else
+                               res_cur = res_cur->nextRange;
+                       kfree (res_tmp);
+                       res_tmp = NULL;
+               }
+               bus->firstMem = NULL;
+       }
+       if (bus->firstPFMem) {
+               res_cur = bus->firstPFMem;
+               while (res_cur) {
+                       res_tmp = res_cur;
+                       if (res_cur->next)
+                               res_cur = res_cur->next;
+                       else
+                               res_cur = res_cur->nextRange;
+                       kfree (res_tmp);
+                       res_tmp = NULL;
+               }
+               bus->firstPFMem = NULL;
+       }
+
+       if (bus->firstPFMemFromMem) {
+               res_cur = bus->firstPFMemFromMem;
+               while (res_cur) {
+                       res_tmp = res_cur;
+                       res_cur = res_cur->next;
+
+                       kfree (res_tmp);
+                       res_tmp = NULL;
+               }
+               bus->firstPFMemFromMem = NULL;
+       }
+
+       list_del (&bus->bus_list);
+       kfree (bus);
+       return 0;
+}
+
+/******************************************************************************
+ * This routine deletes the ranges from a given bus, and the entries from the 
+ * parent's bus in the resources
+ * Input: current bus, previous bus
+ * Output: 0, -EINVAL
+ ******************************************************************************/
+static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev)
+{
+       struct range_node *range_cur;
+       struct range_node *range_tmp;
+       int i;
+       struct resource_node *res = NULL;
+
+       if (bus_cur->noIORanges) {
+               range_cur = bus_cur->rangeIO;
+               for (i = 0; i < bus_cur->noIORanges; i++) {
+                       if (ibmphp_find_resource (bus_prev, range_cur->start, &res, IO) < 0)
+                               return -EINVAL;
+                       ibmphp_remove_resource (res);
+
+                       range_tmp = range_cur;
+                       range_cur = range_cur->next;
+                       kfree (range_tmp);
+                       range_tmp = NULL;
+               }
+               bus_cur->rangeIO = NULL;
+       }
+       if (bus_cur->noMemRanges) {
+               range_cur = bus_cur->rangeMem;
+               for (i = 0; i < bus_cur->noMemRanges; i++) {
+                       if (ibmphp_find_resource (bus_prev, range_cur->start, &res, MEM) < 0) 
+                               return -EINVAL;
+
+                       ibmphp_remove_resource (res);
+                       range_tmp = range_cur;
+                       range_cur = range_cur->next;
+                       kfree (range_tmp);
+                       range_tmp = NULL;
+               }
+               bus_cur->rangeMem = NULL;
+       }
+       if (bus_cur->noPFMemRanges) {
+               range_cur = bus_cur->rangePFMem;
+               for (i = 0; i < bus_cur->noPFMemRanges; i++) {
+                       if (ibmphp_find_resource (bus_prev, range_cur->start, &res, PFMEM) < 0) 
+                               return -EINVAL;
+
+                       ibmphp_remove_resource (res);
+                       range_tmp = range_cur;
+                       range_cur = range_cur->next;
+                       kfree (range_tmp);
+                       range_tmp = NULL;
+               }
+               bus_cur->rangePFMem = NULL;
+       }
+       return 0;
+}
+
+/*
+ * find the resource node in the bus 
+ * Input: Resource needed, start address of the resource, type or resource
+ */
+int ibmphp_find_resource (struct bus_node *bus, u32 start_address, struct resource_node **res, int flag)
+{
+       struct resource_node *res_cur = NULL;
+       char * type = "";
+
+       switch (flag) {
+               case IO:
+                       res_cur = bus->firstIO;
+                       type = "io";
+                       break;
+               case MEM:
+                       res_cur = bus->firstMem;
+                       type = "mem";
+                       break;
+               case PFMEM:
+                       res_cur = bus->firstPFMem;
+                       type = "pfmem";
+                       break;
+               default:
+                       err ("wrong type of flag \n");
+                       return -EINVAL;
+       }
+       
+       while (res_cur) {
+               if (res_cur->start == start_address) {
+                       *res = res_cur;
+                       break;
+               }
+               if (res_cur->next)
+                       res_cur = res_cur->next;
+               else
+                       res_cur = res_cur->nextRange;
+       }
+
+       if (!res_cur) {
+               if (flag == PFMEM) {
+                       res_cur = bus->firstPFMemFromMem;
+                       while (res_cur) {
+                               if (res_cur->start == start_address) {
+                                       *res = res_cur;
+                                       break;
+                               }
+                               res_cur = res_cur->next;
+                       }
+                       if (!res_cur) {
+                               err ("SOS...cannot find %s resource in the bus. \n", type);
+                               return -EINVAL;
+                       }
+               } else {
+                       err ("SOS... cannot find %s resource in the bus. \n", type);
+                       return -EINVAL;
+               }
+       }
+
+       if (*res)
+               debug ("*res->start = %x \n", (*res)->start);
+
+       return 0;
+}
+
+/***********************************************************************
+ * This routine will free the resource structures used by the
+ * system.  It is called from cleanup routine for the module
+ * Parameters: none
+ * Returns: none
+ ***********************************************************************/
+void ibmphp_free_resources (void)
+{
+       struct bus_node *bus_cur = NULL;
+       struct bus_node *bus_tmp;
+       struct range_node *range_cur;
+       struct range_node *range_tmp;
+       struct resource_node *res_cur;
+       struct resource_node *res_tmp;
+       struct list_head *tmp;
+       struct list_head *next;
+       int i = 0;
+       flags = 1;
+
+       list_for_each_safe (tmp, next, &gbuses) {
+               bus_cur = list_entry (tmp, struct bus_node, bus_list);
+               if (bus_cur->noIORanges) {
+                       range_cur = bus_cur->rangeIO;
+                       for (i = 0; i < bus_cur->noIORanges; i++) {
+                               if (!range_cur)
+                                       break;
+                               range_tmp = range_cur;
+                               range_cur = range_cur->next;
+                               kfree (range_tmp);
+                               range_tmp = NULL;
+                       }
+               }
+               if (bus_cur->noMemRanges) {
+                       range_cur = bus_cur->rangeMem;
+                       for (i = 0; i < bus_cur->noMemRanges; i++) {
+                               if (!range_cur)
+                                       break;
+                               range_tmp = range_cur;
+                               range_cur = range_cur->next;
+                               kfree (range_tmp);
+                               range_tmp = NULL;
+                       }
+               }
+               if (bus_cur->noPFMemRanges) {
+                       range_cur = bus_cur->rangePFMem;
+                       for (i = 0; i < bus_cur->noPFMemRanges; i++) {
+                               if (!range_cur)
+                                       break;
+                               range_tmp = range_cur;
+                               range_cur = range_cur->next;
+                               kfree (range_tmp);
+                               range_tmp = NULL;
+                       }
+               }
+
+               if (bus_cur->firstIO) {
+                       res_cur = bus_cur->firstIO;
+                       while (res_cur) {
+                               res_tmp = res_cur;
+                               if (res_cur->next)
+                                       res_cur = res_cur->next;
+                               else
+                                       res_cur = res_cur->nextRange;
+                               kfree (res_tmp);
+                               res_tmp = NULL;
+                       }
+                       bus_cur->firstIO = NULL;
+               }
+               if (bus_cur->firstMem) {
+                       res_cur = bus_cur->firstMem;
+                       while (res_cur) {
+                               res_tmp = res_cur;
+                               if (res_cur->next)
+                                       res_cur = res_cur->next;
+                               else
+                                       res_cur = res_cur->nextRange;
+                               kfree (res_tmp);
+                               res_tmp = NULL;
+                       }
+                       bus_cur->firstMem = NULL;
+               }
+               if (bus_cur->firstPFMem) {
+                       res_cur = bus_cur->firstPFMem;
+                       while (res_cur) {
+                               res_tmp = res_cur;
+                               if (res_cur->next)
+                                       res_cur = res_cur->next;
+                               else
+                                       res_cur = res_cur->nextRange;
+                               kfree (res_tmp);
+                               res_tmp = NULL;
+                       }
+                       bus_cur->firstPFMem = NULL;
+               }
+
+               if (bus_cur->firstPFMemFromMem) {
+                       res_cur = bus_cur->firstPFMemFromMem;
+                       while (res_cur) {
+                               res_tmp = res_cur;
+                               res_cur = res_cur->next;
+
+                               kfree (res_tmp);
+                               res_tmp = NULL;
+                       }
+                       bus_cur->firstPFMemFromMem = NULL;
+               }
+
+               bus_tmp = bus_cur;
+               list_del (&bus_cur->bus_list);
+               kfree (bus_tmp);
+               bus_tmp = NULL;
+       }
+}
+
+/*********************************************************************************
+ * This function will go over the PFmem resources to check if the EBDA allocated
+ * pfmem out of memory buckets of the bus.  If so, it will change the range numbers
+ * and a flag to indicate that this resource is out of memory. It will also move the
+ * Pfmem out of the pfmem resource list to the PFMemFromMem list, and will create
+ * a new Mem node
+ * This routine is called right after initialization
+ *******************************************************************************/
+static int once_over (void)
+{
+       struct resource_node *pfmem_cur;
+       struct resource_node *pfmem_prev;
+       struct resource_node *mem;
+       struct bus_node *bus_cur;
+       struct list_head *tmp;
+
+       list_for_each (tmp, &gbuses) {
+               bus_cur = list_entry (tmp, struct bus_node, bus_list);
+               if ((!bus_cur->rangePFMem) && (bus_cur->firstPFMem)) {
+                       for (pfmem_cur = bus_cur->firstPFMem, pfmem_prev = NULL; pfmem_cur; pfmem_prev = pfmem_cur, pfmem_cur = pfmem_cur->next) {
+                               pfmem_cur->fromMem = TRUE;
+                               if (pfmem_prev)
+                                       pfmem_prev->next = pfmem_cur->next;
+                               else
+                                       bus_cur->firstPFMem = pfmem_cur->next;
+
+                               if (!bus_cur->firstPFMemFromMem)
+                                       pfmem_cur->next = NULL;
+                               else
+                                       /* we don't need to sort PFMemFromMem since we're using mem node for
+                                          all the real work anyways, so just insert at the beginning of the
+                                          list
+                                        */
+                                       pfmem_cur->next = bus_cur->firstPFMemFromMem;
+
+                               bus_cur->firstPFMemFromMem = pfmem_cur;
+
+                               mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL);              
+                               if (!mem) {
+                                       err ("out of system memory \n");
+                                       return -ENOMEM;
+                               }
+                               memset (mem, 0, sizeof (struct resource_node));
+                               mem->type = MEM;
+                               mem->busno = pfmem_cur->busno;
+                               mem->devfunc = pfmem_cur->devfunc;
+                               mem->start = pfmem_cur->start;
+                               mem->end = pfmem_cur->end;
+                               mem->len = pfmem_cur->len;
+                               if (ibmphp_add_resource (mem) < 0)
+                                       err ("Trouble...trouble... EBDA allocated pfmem from mem, but system doesn't display it has this space... unless not PCI device...\n");
+                               pfmem_cur->rangeno = mem->rangeno;
+                       }       /* end for pfmem */
+               }       /* end if */
+       }       /* end list_for_each bus */
+       return 0; 
+}
+
+int ibmphp_add_pfmem_from_mem (struct resource_node *pfmem)
+{
+       struct bus_node *bus_cur = find_bus_wprev (pfmem->busno, NULL, 0);
+
+       if (!bus_cur) {
+               err ("cannot find bus of pfmem to add...\n");
+               return -ENODEV;
+       }
+
+       if (bus_cur->firstPFMemFromMem)
+               pfmem->next = bus_cur->firstPFMemFromMem;
+       else
+               pfmem->next = NULL;
+
+       bus_cur->firstPFMemFromMem = pfmem;
+
+       return 0;
+}
+
+/* This routine just goes through the buses to see if the bus already exists.
+ * It is called from ibmphp_find_sec_number, to find out a secondary bus number for
+ * bridged cards
+ * Parameters: bus_number
+ * Returns: Bus pointer or NULL
+ */
+struct bus_node *ibmphp_find_res_bus (u8 bus_number)
+{
+       return find_bus_wprev (bus_number, NULL, 0);
+}
+
+static inline struct bus_node *find_bus_wprev (u8 bus_number, struct bus_node **prev, u8 flag)
+{
+       struct bus_node *bus_cur;
+       struct list_head *tmp;
+       struct list_head *tmp_prev;
+
+       list_for_each (tmp, &gbuses) {
+               tmp_prev = tmp->prev;
+               bus_cur = list_entry (tmp, struct bus_node, bus_list);
+               if (flag) 
+                       *prev = list_entry (tmp_prev, struct bus_node, bus_list);
+               if (bus_cur->busno == bus_number) 
+                       return bus_cur;
+       }
+
+       return NULL;
+}
+
+void ibmphp_print_test (void)
+{
+       int i = 0;
+       struct bus_node *bus_cur = NULL;
+       struct range_node *range;
+       struct resource_node *res;
+       struct list_head *tmp;
+
+       if ((!list_empty(&gbuses)) && flags) {
+               err ("The GBUSES is not NULL?!?!?!?!?\n");
+               return;
+       }
+
+       list_for_each (tmp, &gbuses) {
+               bus_cur = list_entry (tmp, struct bus_node, bus_list);
+               debug ("This is bus # %d.  There are \n", bus_cur->busno);
+               debug ("IORanges = %d\t", bus_cur->noIORanges);
+               debug ("MemRanges = %d\t", bus_cur->noMemRanges);
+               debug ("PFMemRanges = %d\n", bus_cur->noPFMemRanges);
+               debug ("The IO Ranges are as follows:\n");
+               if (bus_cur->rangeIO) {
+                       range = bus_cur->rangeIO;
+                       for (i = 0; i < bus_cur->noIORanges; i++) {
+                               debug ("rangeno is %d\n", range->rangeno);
+                               debug ("[%x - %x]\n", range->start, range->end);
+                               range = range->next;
+                       }
+               }
+
+               debug ("The Mem Ranges are as follows:\n");
+               if (bus_cur->rangeMem) {
+                       range = bus_cur->rangeMem;
+                       for (i = 0; i < bus_cur->noMemRanges; i++) {
+                               debug ("rangeno is %d\n", range->rangeno);
+                               debug ("[%x - %x]\n", range->start, range->end);
+                               range = range->next;
+                       }
+               }
+
+               debug ("The PFMem Ranges are as follows:\n");
+
+               if (bus_cur->rangePFMem) {
+                       range = bus_cur->rangePFMem;
+                       for (i = 0; i < bus_cur->noPFMemRanges; i++) {
+                               debug ("rangeno is %d\n", range->rangeno);
+                               debug ("[%x - %x]\n", range->start, range->end);
+                               range = range->next;
+                       }
+               }
+
+               debug ("The resources on this bus are as follows\n");
+
+               debug ("IO...\n");
+               if (bus_cur->firstIO) {
+                       res = bus_cur->firstIO;
+                       while (res) {
+                               debug ("The range # is %d\n", res->rangeno);
+                               debug ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
+                               debug ("[%x - %x], len=%x\n", res->start, res->end, res->len);
+                               if (res->next)
+                                       res = res->next;
+                               else if (res->nextRange)
+                                       res = res->nextRange;
+                               else
+                                       break;
+                       }
+               }
+               debug ("Mem...\n");
+               if (bus_cur->firstMem) {
+                       res = bus_cur->firstMem;
+                       while (res) {
+                               debug ("The range # is %d\n", res->rangeno);
+                               debug ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
+                               debug ("[%x - %x], len=%x\n", res->start, res->end, res->len);
+                               if (res->next)
+                                       res = res->next;
+                               else if (res->nextRange)
+                                       res = res->nextRange;
+                               else
+                                       break;
+                       }
+               }
+               debug ("PFMem...\n");
+               if (bus_cur->firstPFMem) {
+                       res = bus_cur->firstPFMem;
+                       while (res) {
+                               debug ("The range # is %d\n", res->rangeno);
+                               debug ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
+                               debug ("[%x - %x], len=%x\n", res->start, res->end, res->len);
+                               if (res->next)
+                                       res = res->next;
+                               else if (res->nextRange)
+                                       res = res->nextRange;
+                               else
+                                       break;
+                       }
+               }
+
+               debug ("PFMemFromMem...\n");
+               if (bus_cur->firstPFMemFromMem) {
+                       res = bus_cur->firstPFMemFromMem;
+                       while (res) {
+                               debug ("The range # is %d\n", res->rangeno);
+                               debug ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
+                               debug ("[%x - %x], len=%x\n", res->start, res->end, res->len);
+                               res = res->next;
+                       }
+               }
+       }
+}
+
+/* This routine will read the windows for any PPB we have and update the
+ * range info for the secondary bus, and will also input this info into
+ * primary bus, since BIOS doesn't. This is for PPB that are in the system
+ * on bootup
+ * Input: primary busno
+ * Returns: none
+ * Note: this function doesn't take into account IO restrictions etc,
+ *      so will only work for bridges with no video/ISA devices behind them It
+ *      also will not work for onboard PPB's that can have more than 1 *bus
+ *      behind them All these are TO DO.
+ *      Also need to add more error checkings... (from fnc returns etc)
+ */
+static int update_bridge_ranges (struct bus_node **bus)
+{
+       u8 sec_busno, device, function, busno, hdr_type, start_io_address, end_io_address;
+       u16 vendor_id, upper_io_start, upper_io_end, start_mem_address, end_mem_address;
+       u32 start_address, end_address, upper_start, upper_end;
+       struct bus_node *bus_sec;
+       struct bus_node *bus_cur;
+       struct resource_node *io;
+       struct resource_node *mem;
+       struct resource_node *pfmem;
+       struct range_node *range;
+       bus_cur = *bus;
+       busno = bus_cur->busno;
+
+       debug ("inside update_bridge_ranges \n");
+       debug ("bus_cur->busno = %x\n", bus_cur->busno);
+
+       for (device = 0; device < 32; device++) {
+               for (function = 0x00; function < 0x08; function++) {
+                       pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_VENDOR_ID, &vendor_id);
+
+                       if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
+                               /* found correct device!!! */
+                               pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_HEADER_TYPE, &hdr_type);
+
+                               switch (hdr_type) {
+                                       case PCI_HEADER_TYPE_NORMAL:
+                                               function = 0x8;
+                                               break;
+                                       case PCI_HEADER_TYPE_MULTIDEVICE:
+                                               break;
+                                       case PCI_HEADER_TYPE_BRIDGE:
+                                               function = 0x8;
+                                       case PCI_HEADER_TYPE_MULTIBRIDGE:
+                                               /* We assume here that only 1 bus behind the bridge 
+                                                  TO DO: add functionality for several:
+                                                  temp = secondary;
+                                                  while (temp < subordinate) {
+                                                  ...
+                                                  temp++;
+                                                  }
+                                                */
+                                               pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_SECONDARY_BUS, &sec_busno);
+                                               bus_sec = find_bus_wprev (sec_busno, NULL, 0); 
+                                               pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_IO_BASE, &start_io_address);
+                                               pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_IO_LIMIT, &end_io_address);
+                                               pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_IO_BASE_UPPER16, &upper_io_start);
+                                               pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_IO_LIMIT_UPPER16, &upper_io_end);
+                                               start_address = (start_io_address & PCI_IO_RANGE_MASK) << 8;
+                                               start_address |= (upper_io_start << 16);
+                                               end_address = (end_io_address & PCI_IO_RANGE_MASK) << 8;
+                                               end_address |= (upper_io_end << 16);
+
+                                               if ((start_address) && (start_address <= end_address)) {
+
+                                                       range = kmalloc (sizeof (struct range_node), GFP_KERNEL);
+
+                                                       if (!range) {
+                                                               err ("out of system memory \n");
+                                                               return -ENOMEM;
+                                                       }
+                                                       memset (range, 0, sizeof (struct range_node));
+                                                       range->start = start_address;
+                                                       range->end = end_address + 0xfff;
+
+                                                       if (bus_sec->noIORanges > 0) 
+                                                               add_range (IO, range, bus_sec);
+                                                       else {
+                                                               /* 1st IO Range on the bus */
+                                                               range->rangeno = 1;
+                                                               bus_sec->rangeIO = range;
+                                                       }
+
+                                                       ++bus_sec->noIORanges;
+                                                       fix_resources (bus_sec);
+                                               
+                                                       io = kmalloc (sizeof (struct resource_node), GFP_KERNEL);                                                       
+                                                       if (!io) {
+                                                               kfree (range);
+                                                               err ("out of system memory \n");
+                                                               return -ENOMEM;
+                                                       }
+                                                       memset (io, 0, sizeof (struct resource_node));
+                                                       io->type = IO;
+                                                       io->busno = bus_cur->busno;
+                                                       io->devfunc = ((device << 3) | (function & 0x7));
+                                                       io->start = start_address;
+                                                       io->end = end_address + 0xfff;
+                                                       io->len = io->end - io->start + 1;
+
+                                                       ibmphp_add_resource (io);
+                                               }
+
+                                               pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_MEMORY_BASE, &start_mem_address);
+                                               pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_MEMORY_LIMIT, &end_mem_address);
+
+                                               start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
+                                               end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
+
+                                               if ((start_address) && (start_address <= end_address)) {
+
+                                                       range = kmalloc (sizeof (struct range_node), GFP_KERNEL);
+                                                       if (!range) {
+                                                               err ("out of system memory \n");
+                                                               return -ENOMEM;
+                                                       }
+                                                       memset (range, 0, sizeof (struct range_node));
+                                                       range->start = start_address;
+                                                       range->end = end_address + 0xfffff;
+
+                                                       if (bus_sec->noMemRanges > 0) 
+                                                               add_range (MEM, range, bus_sec);
+                                                       else {
+                                                               /* 1st Mem Range on the bus */
+                                                               range->rangeno = 1;
+                                                               bus_sec->rangeMem = range;
+                                                       }
+
+                                                       ++bus_sec->noMemRanges;
+                                                       fix_resources (bus_sec);
+
+                                                       mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+                                                       if (!mem) {
+                                                               kfree (range);
+                                                               err ("out of system memory \n");
+                                                               return -ENOMEM;
+                                                       }
+                                                       memset (mem, 0, sizeof (struct resource_node));
+                                                       mem->type = MEM;
+                                                       mem->busno = bus_cur->busno;
+                                                       mem->devfunc = ((device << 3) | (function & 0x7));
+                                                       mem->start = start_address;
+                                                       mem->end = end_address + 0xfffff;
+                                                       mem->len = mem->end - mem->start + 1;
+                                                       ibmphp_add_resource (mem);
+                                               }
+                                               pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PREF_MEMORY_BASE, &start_mem_address);
+                                               pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PREF_MEMORY_LIMIT, &end_mem_address);
+                                               pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PREF_BASE_UPPER32, &upper_start);
+                                               pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PREF_LIMIT_UPPER32, &upper_end);
+                                               start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
+                                               end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
+#if BITS_PER_LONG == 64
+                                               start_address |= ((long) upper_start) << 32;
+                                               end_address |= ((long) upper_end) << 32;
+#endif
+
+                                               if ((start_address) && (start_address <= end_address)) {
+
+                                                       range = kmalloc (sizeof (struct range_node), GFP_KERNEL);
+                                                       if (!range) {
+                                                               err ("out of system memory \n");
+                                                               return -ENOMEM;
+                                                       }
+                                                       memset (range, 0, sizeof (struct range_node));
+                                                       range->start = start_address;
+                                                       range->end = end_address + 0xfffff;
+
+                                                       if (bus_sec->noPFMemRanges > 0)
+                                                               add_range (PFMEM, range, bus_sec);
+                                                       else {
+                                                               /* 1st PFMem Range on the bus */
+                                                               range->rangeno = 1;
+                                                               bus_sec->rangePFMem = range;
+                                                       }
+
+                                                       ++bus_sec->noPFMemRanges;
+                                                       fix_resources (bus_sec);
+
+                                                       pfmem = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
+                                                       if (!pfmem) {
+                                                               kfree (range);
+                                                               err ("out of system memory \n");
+                                                               return -ENOMEM;
+                                                       }
+                                                       memset (pfmem, 0, sizeof (struct resource_node));
+                                                       pfmem->type = PFMEM;
+                                                       pfmem->busno = bus_cur->busno;
+                                                       pfmem->devfunc = ((device << 3) | (function & 0x7));
+                                                       pfmem->start = start_address;
+                                                       pfmem->end = end_address + 0xfffff;
+                                                       pfmem->len = pfmem->end - pfmem->start + 1;
+                                                       pfmem->fromMem = FALSE;
+                                                       ibmphp_add_resource (pfmem);
+                                               }
+                                               break;
+                               }       /* end of switch */
+                       }       /* end if vendor */
+               }       /* end for function */
+       }       /* end for device */
+
+       bus = &bus_cur;
+       return 0;
+}