aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/pci/hotplug
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/pci/hotplug
downloadwireguard-linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz
wireguard-linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/pci/hotplug')
-rw-r--r--drivers/pci/hotplug/Kconfig197
-rw-r--r--drivers/pci/hotplug/Makefile74
-rw-r--r--drivers/pci/hotplug/acpiphp.h268
-rw-r--r--drivers/pci/hotplug/acpiphp_core.c453
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c1344
-rw-r--r--drivers/pci/hotplug/acpiphp_ibm.c499
-rw-r--r--drivers/pci/hotplug/acpiphp_pci.c449
-rw-r--r--drivers/pci/hotplug/acpiphp_res.c700
-rw-r--r--drivers/pci/hotplug/cpci_hotplug.h96
-rw-r--r--drivers/pci/hotplug/cpci_hotplug_core.c792
-rw-r--r--drivers/pci/hotplug/cpci_hotplug_pci.c661
-rw-r--r--drivers/pci/hotplug/cpcihp_generic.c223
-rw-r--r--drivers/pci/hotplug/cpcihp_zt5550.c305
-rw-r--r--drivers/pci/hotplug/cpcihp_zt5550.h79
-rw-r--r--drivers/pci/hotplug/cpqphp.h721
-rw-r--r--drivers/pci/hotplug/cpqphp_core.c1509
-rw-r--r--drivers/pci/hotplug/cpqphp_ctrl.c3096
-rw-r--r--drivers/pci/hotplug/cpqphp_nvram.c666
-rw-r--r--drivers/pci/hotplug/cpqphp_nvram.h57
-rw-r--r--drivers/pci/hotplug/cpqphp_pci.c1569
-rw-r--r--drivers/pci/hotplug/cpqphp_sysfs.c143
-rw-r--r--drivers/pci/hotplug/fakephp.c358
-rw-r--r--drivers/pci/hotplug/ibmphp.h763
-rw-r--r--drivers/pci/hotplug/ibmphp_core.c1422
-rw-r--r--drivers/pci/hotplug/ibmphp_ebda.c1275
-rw-r--r--drivers/pci/hotplug/ibmphp_hpc.c1161
-rw-r--r--drivers/pci/hotplug/ibmphp_pci.c1747
-rw-r--r--drivers/pci/hotplug/ibmphp_res.c2156
-rw-r--r--drivers/pci/hotplug/pci_hotplug.h180
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c715
-rw-r--r--drivers/pci/hotplug/pciehp.h352
-rw-r--r--drivers/pci/hotplug/pciehp_core.c662
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c2706
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c1501
-rw-r--r--drivers/pci/hotplug/pciehp_pci.c827
-rw-r--r--drivers/pci/hotplug/pciehprm.h52
-rw-r--r--drivers/pci/hotplug/pciehprm_acpi.c1737
-rw-r--r--drivers/pci/hotplug/pciehprm_nonacpi.c501
-rw-r--r--drivers/pci/hotplug/pciehprm_nonacpi.h56
-rw-r--r--drivers/pci/hotplug/pcihp_skeleton.c375
-rw-r--r--drivers/pci/hotplug/rpadlpar.h24
-rw-r--r--drivers/pci/hotplug/rpadlpar_core.c503
-rw-r--r--drivers/pci/hotplug/rpadlpar_sysfs.c151
-rw-r--r--drivers/pci/hotplug/rpaphp.h138
-rw-r--r--drivers/pci/hotplug/rpaphp_core.c536
-rw-r--r--drivers/pci/hotplug/rpaphp_pci.c538
-rw-r--r--drivers/pci/hotplug/rpaphp_slot.c267
-rw-r--r--drivers/pci/hotplug/rpaphp_vio.c129
-rw-r--r--drivers/pci/hotplug/shpchp.h463
-rw-r--r--drivers/pci/hotplug/shpchp_core.c630
-rw-r--r--drivers/pci/hotplug/shpchp_ctrl.c2848
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c1620
-rw-r--r--drivers/pci/hotplug/shpchp_pci.c810
-rw-r--r--drivers/pci/hotplug/shpchp_sysfs.c143
-rw-r--r--drivers/pci/hotplug/shpchprm.h55
-rw-r--r--drivers/pci/hotplug/shpchprm_acpi.c1713
-rw-r--r--drivers/pci/hotplug/shpchprm_legacy.c439
-rw-r--r--drivers/pci/hotplug/shpchprm_legacy.h113
-rw-r--r--drivers/pci/hotplug/shpchprm_nonacpi.c434
-rw-r--r--drivers/pci/hotplug/shpchprm_nonacpi.h56
60 files changed, 44057 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
new file mode 100644
index 000000000000..1a4d4ca2a4dc
--- /dev/null
+++ b/drivers/pci/hotplug/Kconfig
@@ -0,0 +1,197 @@
+#
+# PCI Hotplug support
+#
+
+menu "PCI Hotplug Support"
+
+config HOTPLUG_PCI
+ tristate "Support for PCI Hotplug (EXPERIMENTAL)"
+ depends on PCI && EXPERIMENTAL
+ select HOTPLUG
+ ---help---
+ Say Y here if you have a motherboard with a PCI Hotplug controller.
+ This allows you to add and remove PCI cards while the machine is
+ powered up and running. The file system pcihpfs must be mounted
+ in order to interact with any PCI Hotplug controllers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pci_hotplug.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_FAKE
+ tristate "Fake PCI Hotplug driver"
+ depends on HOTPLUG_PCI
+ help
+ Say Y here if you want to use the fake PCI hotplug driver. It can
+ be used to simulate PCI hotplug events if even if your system is
+ not PCI hotplug capable.
+
+ This driver will "emulate" removing PCI devices from the system.
+ If the "power" file is written to with "0" then the specified PCI
+ device will be completely removed from the kernel.
+
+ WARNING, this does NOT turn off the power to the PCI device.
+ This is a "logical" removal, not a physical or electrical
+ removal.
+
+ Use this module at your own risk. You have been warned!
+
+ To compile this driver as a module, choose M here: the
+ module will be called fakephp.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_COMPAQ
+ tristate "Compaq PCI Hotplug driver"
+ depends on HOTPLUG_PCI && X86 && PCI_BIOS
+ help
+ Say Y here if you have a motherboard with a Compaq PCI Hotplug
+ controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cpqphp.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_COMPAQ_NVRAM
+ bool "Save configuration into NVRAM on Compaq servers"
+ depends on HOTPLUG_PCI_COMPAQ
+ help
+ Say Y here if you have a Compaq server that has a PCI Hotplug
+ controller. This will allow the PCI Hotplug driver to store the PCI
+ system configuration options in NVRAM.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_IBM
+ tristate "IBM PCI Hotplug driver"
+ depends on HOTPLUG_PCI && X86_IO_APIC && X86 && PCI_BIOS
+ help
+ Say Y here if you have a motherboard with a IBM PCI Hotplug
+ controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ibmphp.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_ACPI
+ tristate "ACPI PCI Hotplug driver"
+ depends on ACPI_BUS && HOTPLUG_PCI
+ help
+ Say Y here if you have a system that supports PCI Hotplug using
+ ACPI.
+
+ To compile this driver as a module, choose M here: the
+ module will be called acpiphp.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_ACPI_IBM
+ tristate "ACPI PCI Hotplug driver IBM extensions"
+ depends on HOTPLUG_PCI_ACPI
+ help
+ Say Y here if you have an IBM system that supports PCI Hotplug using
+ ACPI.
+
+ To compile this driver as a module, choose M here: the
+ module will be called acpiphp_ibm.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_CPCI
+ bool "CompactPCI Hotplug driver"
+ depends on HOTPLUG_PCI
+ help
+ Say Y here if you have a CompactPCI system card with CompactPCI
+ hotswap support per the PICMG 2.1 specification.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_CPCI_ZT5550
+ tristate "Ziatech ZT5550 CompactPCI Hotplug driver"
+ depends on HOTPLUG_PCI && HOTPLUG_PCI_CPCI && X86
+ help
+ Say Y here if you have an Performance Technologies (formerly Intel,
+ formerly just Ziatech) Ziatech ZT5550 CompactPCI system card.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cpcihp_zt5550.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_CPCI_GENERIC
+ tristate "Generic port I/O CompactPCI Hotplug driver"
+ depends on HOTPLUG_PCI && HOTPLUG_PCI_CPCI && X86
+ help
+ Say Y here if you have a CompactPCI system card that exposes the #ENUM
+ hotswap signal as a bit in a system register that can be read through
+ standard port I/O.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cpcihp_generic.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_SHPC
+ tristate "SHPC PCI Hotplug driver"
+ depends on HOTPLUG_PCI
+ help
+ Say Y here if you have a motherboard with a SHPC PCI Hotplug
+ controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called shpchp.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_SHPC_POLL_EVENT_MODE
+ bool "Use polling mechanism for hot-plug events (for testing purpose)"
+ depends on HOTPLUG_PCI_SHPC
+ help
+ Say Y here if you want to use the polling mechanism for hot-plug
+ events for early platform testing.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_SHPC_PHPRM_LEGACY
+ bool "For AMD SHPC only: Use $HRT for resource/configuration"
+ depends on HOTPLUG_PCI_SHPC && !ACPI_BUS
+ help
+ Say Y here for AMD SHPC. You have to select this option if you are
+ using this driver on platform with AMD SHPC.
+
+config HOTPLUG_PCI_RPA
+ tristate "RPA PCI Hotplug driver"
+ depends on HOTPLUG_PCI && PPC_PSERIES && PPC64 && !HOTPLUG_PCI_FAKE
+ help
+ Say Y here if you have a a RPA system that supports PCI Hotplug.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rpaphp.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_RPA_DLPAR
+ tristate "RPA Dynamic Logical Partitioning for I/O slots"
+ depends on HOTPLUG_PCI_RPA
+ help
+ Say Y here if your system supports Dynamic Logical Partitioning
+ for I/O slots.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rpadlpar_io.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_SGI
+ tristate "SGI PCI Hotplug Support"
+ depends on HOTPLUG_PCI && IA64_SGI_SN2
+ help
+ Say Y here if you have an SGI IA64 Altix system.
+
+ When in doubt, say N.
+
+endmenu
+
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
new file mode 100644
index 000000000000..93c120ddbd39
--- /dev/null
+++ b/drivers/pci/hotplug/Makefile
@@ -0,0 +1,74 @@
+#
+# Makefile for the Linux kernel pci hotplug controller drivers.
+#
+
+obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o
+obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o
+obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o
+obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o
+obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o
+obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o
+obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o
+obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o
+obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o
+obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o
+obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o
+obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o
+
+pci_hotplug-objs := pci_hotplug_core.o
+
+ifdef CONFIG_HOTPLUG_PCI_CPCI
+pci_hotplug-objs += cpci_hotplug_core.o \
+ cpci_hotplug_pci.o
+endif
+
+cpqphp-objs := cpqphp_core.o \
+ cpqphp_ctrl.o \
+ cpqphp_sysfs.o \
+ cpqphp_pci.o
+cpqphp-$(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM) += cpqphp_nvram.o
+cpqphp-objs += $(cpqphp-y)
+
+ibmphp-objs := ibmphp_core.o \
+ ibmphp_ebda.o \
+ ibmphp_pci.o \
+ ibmphp_res.o \
+ ibmphp_hpc.o
+
+acpiphp-objs := acpiphp_core.o \
+ acpiphp_glue.o \
+ acpiphp_pci.o \
+ acpiphp_res.o
+
+rpaphp-objs := rpaphp_core.o \
+ rpaphp_pci.o \
+ rpaphp_slot.o \
+ rpaphp_vio.o
+
+rpadlpar_io-objs := rpadlpar_core.o \
+ rpadlpar_sysfs.o
+
+pciehp-objs := pciehp_core.o \
+ pciehp_ctrl.o \
+ pciehp_pci.o \
+ pciehp_hpc.o
+ifdef CONFIG_ACPI_BUS
+ pciehp-objs += pciehprm_acpi.o
+else
+ pciehp-objs += pciehprm_nonacpi.o
+endif
+
+shpchp-objs := shpchp_core.o \
+ shpchp_ctrl.o \
+ shpchp_pci.o \
+ shpchp_sysfs.o \
+ shpchp_hpc.o
+ifdef CONFIG_ACPI_BUS
+ shpchp-objs += shpchprm_acpi.o
+else
+ ifdef CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY
+ shpchp-objs += shpchprm_legacy.o
+ else
+ shpchp-objs += shpchprm_nonacpi.o
+ endif
+endif
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
new file mode 100644
index 000000000000..d9499874c8a9
--- /dev/null
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -0,0 +1,268 @@
+/*
+ * ACPI PCI Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
+ * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
+ * Copyright (C) 2002,2003 NEC Corporation
+ *
+ * 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>,
+ * <t-kochi@bq.jp.nec.com>
+ *
+ */
+
+#ifndef _ACPIPHP_H
+#define _ACPIPHP_H
+
+#include <linux/acpi.h>
+#include <linux/kobject.h> /* for KOBJ_NAME_LEN */
+#include "pci_hotplug.h"
+
+#define dbg(format, arg...) \
+ do { \
+ if (acpiphp_debug) \
+ printk(KERN_DEBUG "%s: " format, \
+ 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)
+
+/* name size which is used for entries in pcihpfs */
+#define SLOT_NAME_SIZE KOBJ_NAME_LEN /* {_SUN} */
+
+struct acpiphp_bridge;
+struct acpiphp_slot;
+struct pci_resource;
+
+/*
+ * struct slot - slot information for each *physical* slot
+ */
+struct slot {
+ u8 number;
+ struct hotplug_slot *hotplug_slot;
+ struct list_head slot_list;
+
+ struct acpiphp_slot *acpi_slot;
+};
+
+/*
+ * struct pci_resource - describes pci resource (mem, pfmem, io, bus)
+ */
+struct pci_resource {
+ struct pci_resource * next;
+ u64 base;
+ u32 length;
+};
+
+/**
+ * struct hpp_param - ACPI 2.0 _HPP Hot Plug Parameters
+ * @cache_line_size in DWORD
+ * @latency_timer in PCI clock
+ * @enable_SERR 0 or 1
+ * @enable_PERR 0 or 1
+ */
+struct hpp_param {
+ u8 cache_line_size;
+ u8 latency_timer;
+ u8 enable_SERR;
+ u8 enable_PERR;
+};
+
+
+/**
+ * struct acpiphp_bridge - PCI bridge information
+ *
+ * for each bridge device in ACPI namespace
+ */
+struct acpiphp_bridge {
+ struct list_head list;
+ acpi_handle handle;
+ struct acpiphp_slot *slots;
+ int type;
+ int nr_slots;
+
+ u8 seg;
+ u8 bus;
+ u8 sub;
+
+ u32 flags;
+
+ /* This bus (host bridge) or Secondary bus (PCI-to-PCI bridge) */
+ struct pci_bus *pci_bus;
+
+ /* PCI-to-PCI bridge device */
+ struct pci_dev *pci_dev;
+
+ /* ACPI 2.0 _HPP parameters */
+ struct hpp_param hpp;
+
+ spinlock_t res_lock;
+
+ /* available resources on this bus */
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+};
+
+
+/**
+ * struct acpiphp_slot - PCI slot information
+ *
+ * PCI slot information for each *physical* PCI slot
+ */
+struct acpiphp_slot {
+ struct acpiphp_slot *next;
+ struct acpiphp_bridge *bridge; /* parent */
+ struct list_head funcs; /* one slot may have different
+ objects (i.e. for each function) */
+ struct semaphore crit_sect;
+
+ u32 id; /* slot id (serial #) for hotplug core */
+ u8 device; /* pci device# */
+
+ u32 sun; /* ACPI _SUN (slot unique number) */
+ u32 slotno; /* slot number relative to bridge */
+ u32 flags; /* see below */
+};
+
+
+/**
+ * struct acpiphp_func - PCI function information
+ *
+ * PCI function information for each object in ACPI namespace
+ * typically 8 objects per slot (i.e. for each PCI function)
+ */
+struct acpiphp_func {
+ struct acpiphp_slot *slot; /* parent */
+
+ struct list_head sibling;
+ struct pci_dev *pci_dev;
+
+ acpi_handle handle;
+
+ u8 function; /* pci function# */
+ u32 flags; /* see below */
+
+ /* resources used for this function */
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+};
+
+/**
+ * struct acpiphp_attention_info - device specific attention registration
+ *
+ * ACPI has no generic method of setting/getting attention status
+ * this allows for device specific driver registration
+ */
+struct acpiphp_attention_info
+{
+ int (*set_attn)(struct hotplug_slot *slot, u8 status);
+ int (*get_attn)(struct hotplug_slot *slot, u8 *status);
+ struct module *owner;
+};
+
+/* PCI bus bridge HID */
+#define ACPI_PCI_HOST_HID "PNP0A03"
+
+/* PCI BRIDGE type */
+#define BRIDGE_TYPE_HOST 0
+#define BRIDGE_TYPE_P2P 1
+
+/* ACPI _STA method value (ignore bit 4; battery present) */
+#define ACPI_STA_PRESENT (0x00000001)
+#define ACPI_STA_ENABLED (0x00000002)
+#define ACPI_STA_SHOW_IN_UI (0x00000004)
+#define ACPI_STA_FUNCTIONING (0x00000008)
+#define ACPI_STA_ALL (0x0000000f)
+
+/* bridge flags */
+#define BRIDGE_HAS_STA (0x00000001)
+#define BRIDGE_HAS_EJ0 (0x00000002)
+#define BRIDGE_HAS_HPP (0x00000004)
+#define BRIDGE_HAS_PS0 (0x00000010)
+#define BRIDGE_HAS_PS1 (0x00000020)
+#define BRIDGE_HAS_PS2 (0x00000040)
+#define BRIDGE_HAS_PS3 (0x00000080)
+
+/* slot flags */
+
+#define SLOT_POWEREDON (0x00000001)
+#define SLOT_ENABLED (0x00000002)
+#define SLOT_MULTIFUNCTION (0x00000004)
+
+/* function flags */
+
+#define FUNC_HAS_STA (0x00000001)
+#define FUNC_HAS_EJ0 (0x00000002)
+#define FUNC_HAS_PS0 (0x00000010)
+#define FUNC_HAS_PS1 (0x00000020)
+#define FUNC_HAS_PS2 (0x00000040)
+#define FUNC_HAS_PS3 (0x00000080)
+
+/* function prototypes */
+
+/* acpiphp_core.c */
+extern int acpiphp_register_attention(struct acpiphp_attention_info*info);
+extern int acpiphp_unregister_attention(struct acpiphp_attention_info *info);
+
+/* acpiphp_glue.c */
+extern int acpiphp_glue_init (void);
+extern void acpiphp_glue_exit (void);
+extern int acpiphp_get_num_slots (void);
+extern struct acpiphp_slot *get_slot_from_id (int id);
+typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data);
+
+extern int acpiphp_enable_slot (struct acpiphp_slot *slot);
+extern int acpiphp_disable_slot (struct acpiphp_slot *slot);
+extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot);
+extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot);
+extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot);
+extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot);
+extern u32 acpiphp_get_address (struct acpiphp_slot *slot);
+
+/* acpiphp_pci.c */
+extern struct pci_dev *acpiphp_allocate_pcidev (struct pci_bus *pbus, int dev, int fn);
+extern int acpiphp_configure_slot (struct acpiphp_slot *slot);
+extern int acpiphp_configure_function (struct acpiphp_func *func);
+extern void acpiphp_unconfigure_function (struct acpiphp_func *func);
+extern int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge);
+extern int acpiphp_init_func_resource (struct acpiphp_func *func);
+
+/* acpiphp_res.c */
+extern struct pci_resource *acpiphp_get_io_resource (struct pci_resource **head, u32 size);
+extern struct pci_resource *acpiphp_get_resource (struct pci_resource **head, u32 size);
+extern struct pci_resource *acpiphp_get_resource_with_base (struct pci_resource **head, u64 base, u32 size);
+extern int acpiphp_resource_sort_and_combine (struct pci_resource **head);
+extern struct pci_resource *acpiphp_make_resource (u64 base, u32 length);
+extern void acpiphp_move_resource (struct pci_resource **from, struct pci_resource **to);
+extern void acpiphp_free_resource (struct pci_resource **res);
+extern void acpiphp_dump_resource (struct acpiphp_bridge *bridge); /* debug */
+extern void acpiphp_dump_func_resource (struct acpiphp_func *func); /* debug */
+
+/* variables */
+extern int acpiphp_debug;
+
+#endif /* _ACPIPHP_H */
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c
new file mode 100644
index 000000000000..4539e61a3dc1
--- /dev/null
+++ b/drivers/pci/hotplug/acpiphp_core.c
@@ -0,0 +1,453 @@
+/*
+ * ACPI PCI Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
+ * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
+ * Copyright (C) 2002,2003 NEC Corporation
+ *
+ * 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>,
+ * <t-kochi@bq.jp.nec.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include "pci_hotplug.h"
+#include "acpiphp.h"
+
+static LIST_HEAD(slot_list);
+
+#define MY_NAME "acpiphp"
+
+static int debug;
+int acpiphp_debug;
+
+/* local variables */
+static int num_slots;
+static struct acpiphp_attention_info *attention_info;
+
+#define DRIVER_VERSION "0.4"
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <gregkh@us.ibm.com>, Takayoshi Kochi <t-kochi@bq.jp.nec.com>"
+#define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+module_param(debug, bool, 0644);
+
+/* export the attention callback registration methods */
+EXPORT_SYMBOL_GPL(acpiphp_register_attention);
+EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
+
+static int enable_slot (struct hotplug_slot *slot);
+static int disable_slot (struct hotplug_slot *slot);
+static int set_attention_status (struct hotplug_slot *slot, u8 value);
+static int get_power_status (struct hotplug_slot *slot, u8 *value);
+static int get_attention_status (struct hotplug_slot *slot, u8 *value);
+static int get_address (struct hotplug_slot *slot, u32 *value);
+static int get_latch_status (struct hotplug_slot *slot, u8 *value);
+static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
+
+static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .enable_slot = enable_slot,
+ .disable_slot = disable_slot,
+ .set_attention_status = set_attention_status,
+ .get_power_status = get_power_status,
+ .get_attention_status = get_attention_status,
+ .get_latch_status = get_latch_status,
+ .get_adapter_status = get_adapter_status,
+ .get_address = get_address,
+};
+
+
+/**
+ * acpiphp_register_attention - set attention LED callback
+ * @info: must be completely filled with LED callbacks
+ *
+ * Description: this is used to register a hardware specific ACPI
+ * driver that manipulates the attention LED. All the fields in
+ * info must be set.
+ **/
+int acpiphp_register_attention(struct acpiphp_attention_info *info)
+{
+ int retval = -EINVAL;
+
+ if (info && info->owner && info->set_attn &&
+ info->get_attn && !attention_info) {
+ retval = 0;
+ attention_info = info;
+ }
+ return retval;
+}
+
+
+/**
+ * acpiphp_unregister_attention - unset attention LED callback
+ * @info: must match the pointer used to register
+ *
+ * Description: this is used to un-register a hardware specific acpi
+ * driver that manipulates the attention LED. The pointer to the
+ * info struct must be the same as the one used to set it.
+ **/
+int acpiphp_unregister_attention(struct acpiphp_attention_info *info)
+{
+ int retval = -EINVAL;
+
+ if (info && attention_info == info) {
+ attention_info = NULL;
+ retval = 0;
+ }
+ return retval;
+}
+
+
+/**
+ * enable_slot - power on and enable a slot
+ * @hotplug_slot: slot to enable
+ *
+ * Actual tasks are done in acpiphp_enable_slot()
+ *
+ */
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ /* enable the specified slot */
+ return acpiphp_enable_slot(slot->acpi_slot);
+}
+
+
+/**
+ * disable_slot - disable and power off a slot
+ * @hotplug_slot: slot to disable
+ *
+ * Actual tasks are done in acpiphp_disable_slot()
+ *
+ */
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ /* disable the specified slot */
+ return acpiphp_disable_slot(slot->acpi_slot);
+}
+
+
+ /**
+ * set_attention_status - set attention LED
+ * @hotplug_slot: slot to set attention LED on
+ * @status: value to set attention LED to (0 or 1)
+ *
+ * attention status LED, so we use a callback that
+ * was registered with us. This allows hardware specific
+ * ACPI implementations to blink the light for us.
+ **/
+ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
+ {
+ int retval = -ENODEV;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ if (attention_info && try_module_get(attention_info->owner)) {
+ retval = attention_info->set_attn(hotplug_slot, status);
+ module_put(attention_info->owner);
+ } else
+ attention_info = NULL;
+ return retval;
+ }
+
+
+/**
+ * get_power_status - get power status of a slot
+ * @hotplug_slot: slot to get status
+ * @value: pointer to store status
+ *
+ * Some platforms may not implement _STA method properly.
+ * In that case, the value returned may not be reliable.
+ *
+ */
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ *value = acpiphp_get_power_status(slot->acpi_slot);
+
+ return 0;
+}
+
+
+ /**
+ * get_attention_status - get attention LED status
+ * @hotplug_slot: slot to get status from
+ * @value: returns with value of attention LED
+ *
+ * ACPI doesn't have known method to determine the state
+ * of the attention status LED, so we use a callback that
+ * was registered with us. This allows hardware specific
+ * ACPI implementations to determine its state
+ **/
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ int retval = -EINVAL;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ if (attention_info && try_module_get(attention_info->owner)) {
+ retval = attention_info->get_attn(hotplug_slot, value);
+ module_put(attention_info->owner);
+ } else
+ attention_info = NULL;
+ return retval;
+}
+
+
+/**
+ * get_latch_status - get latch status of a slot
+ * @hotplug_slot: slot to get status
+ * @value: pointer to store status
+ *
+ * ACPI doesn't provide any formal means to access latch status.
+ * Instead, we fake latch status from _STA
+ *
+ */
+static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ *value = acpiphp_get_latch_status(slot->acpi_slot);
+
+ return 0;
+}
+
+
+/**
+ * get_adapter_status - get adapter status of a slot
+ * @hotplug_slot: slot to get status
+ * @value: pointer to store status
+ *
+ * ACPI doesn't provide any formal means to access adapter status.
+ * Instead, we fake adapter status from _STA
+ *
+ */
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ *value = acpiphp_get_adapter_status(slot->acpi_slot);
+
+ return 0;
+}
+
+
+/**
+ * get_address - get pci address of a slot
+ * @hotplug_slot: slot to get status
+ * @busdev: pointer to struct pci_busdev (seg, bus, dev)
+ *
+ */
+static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ *value = acpiphp_get_address(slot->acpi_slot);
+
+ return 0;
+}
+
+static int __init init_acpi(void)
+{
+ int retval;
+
+ /* initialize internal data structure etc. */
+ retval = acpiphp_glue_init();
+
+ /* read initial number of slots */
+ if (!retval) {
+ num_slots = acpiphp_get_num_slots();
+ if (num_slots == 0)
+ retval = -ENODEV;
+ }
+
+ return retval;
+}
+
+
+/**
+ * make_slot_name - make a slot name that appears in pcihpfs
+ * @slot: slot to name
+ *
+ */
+static void make_slot_name(struct slot *slot)
+{
+ snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%u",
+ slot->acpi_slot->sun);
+}
+
+/**
+ * release_slot - free up the memory used by a slot
+ * @hotplug_slot: slot to free
+ */
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ kfree(slot->hotplug_slot->info);
+ kfree(slot->hotplug_slot->name);
+ kfree(slot->hotplug_slot);
+ kfree(slot);
+}
+
+/**
+ * init_slots - initialize 'struct slot' structures for each slot
+ *
+ */
+static int __init init_slots(void)
+{
+ struct slot *slot;
+ int retval = -ENOMEM;
+ int i;
+
+ for (i = 0; i < num_slots; ++i) {
+ slot = kmalloc(sizeof(struct slot), GFP_KERNEL);
+ if (!slot)
+ goto error;
+ memset(slot, 0, sizeof(struct slot));
+
+ slot->hotplug_slot = kmalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
+ if (!slot->hotplug_slot)
+ goto error_slot;
+ memset(slot->hotplug_slot, 0, sizeof(struct hotplug_slot));
+
+ slot->hotplug_slot->info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
+ if (!slot->hotplug_slot->info)
+ goto error_hpslot;
+ memset(slot->hotplug_slot->info, 0, sizeof(struct hotplug_slot_info));
+
+ slot->hotplug_slot->name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
+ if (!slot->hotplug_slot->name)
+ goto error_info;
+
+ slot->number = i;
+
+ slot->hotplug_slot->private = slot;
+ slot->hotplug_slot->release = &release_slot;
+ slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
+
+ slot->acpi_slot = get_slot_from_id(i);
+ slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot);
+ slot->hotplug_slot->info->attention_status = 0;
+ slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot);
+ slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
+ slot->hotplug_slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
+ slot->hotplug_slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
+
+ make_slot_name(slot);
+
+ retval = pci_hp_register(slot->hotplug_slot);
+ if (retval) {
+ err("pci_hp_register failed with error %d\n", retval);
+ goto error_name;
+ }
+
+ /* add slot to our internal list */
+ list_add(&slot->slot_list, &slot_list);
+ info("Slot [%s] registered\n", slot->hotplug_slot->name);
+ }
+
+ return 0;
+error_name:
+ kfree(slot->hotplug_slot->name);
+error_info:
+ kfree(slot->hotplug_slot->info);
+error_hpslot:
+ kfree(slot->hotplug_slot);
+error_slot:
+ kfree(slot);
+error:
+ return retval;
+}
+
+
+static void __exit cleanup_slots (void)
+{
+ struct list_head *tmp, *n;
+ struct slot *slot;
+
+ list_for_each_safe (tmp, n, &slot_list) {
+ /* memory will be freed in release_slot callback */
+ slot = list_entry(tmp, struct slot, slot_list);
+ list_del(&slot->slot_list);
+ pci_hp_deregister(slot->hotplug_slot);
+ }
+}
+
+
+static int __init acpiphp_init(void)
+{
+ int retval;
+
+ info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+ acpiphp_debug = debug;
+
+ /* read all the ACPI info from the system */
+ retval = init_acpi();
+ if (retval)
+ return retval;
+
+ return init_slots();
+}
+
+
+static void __exit acpiphp_exit(void)
+{
+ cleanup_slots();
+ /* deallocate internal data structures etc. */
+ acpiphp_glue_exit();
+}
+
+module_init(acpiphp_init);
+module_exit(acpiphp_exit);
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
new file mode 100644
index 000000000000..e7f41294f811
--- /dev/null
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -0,0 +1,1344 @@
+/*
+ * ACPI PCI HotPlug glue functions to ACPI CA subsystem
+ *
+ * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
+ * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
+ * Copyright (C) 2002,2003 NEC Corporation
+ *
+ * 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 <t-kochi@bq.jp.nec.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/smp_lock.h>
+#include <asm/semaphore.h>
+
+#include "../pci.h"
+#include "pci_hotplug.h"
+#include "acpiphp.h"
+
+static LIST_HEAD(bridge_list);
+
+#define MY_NAME "acpiphp_glue"
+
+static void handle_hotplug_event_bridge (acpi_handle, u32, void *);
+static void handle_hotplug_event_func (acpi_handle, u32, void *);
+
+/*
+ * initialization & terminatation routines
+ */
+
+/**
+ * is_ejectable - determine if a slot is ejectable
+ * @handle: handle to acpi namespace
+ *
+ * Ejectable slot should satisfy at least these conditions:
+ *
+ * 1. has _ADR method
+ * 2. has _EJ0 method
+ *
+ * optionally
+ *
+ * 1. has _STA method
+ * 2. has _PS0 method
+ * 3. has _PS3 method
+ * 4. ..
+ *
+ */
+static int is_ejectable(acpi_handle handle)
+{
+ acpi_status status;
+ acpi_handle tmp;
+
+ status = acpi_get_handle(handle, "_ADR", &tmp);
+ if (ACPI_FAILURE(status)) {
+ return 0;
+ }
+
+ status = acpi_get_handle(handle, "_EJ0", &tmp);
+ if (ACPI_FAILURE(status)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* callback routine to check the existence of ejectable slots */
+static acpi_status
+is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ int *count = (int *)context;
+
+ if (is_ejectable(handle)) {
+ (*count)++;
+ /* only one ejectable slot is enough */
+ return AE_CTRL_TERMINATE;
+ } else {
+ return AE_OK;
+ }
+}
+
+
+/* callback routine to register each ACPI PCI slot object */
+static acpi_status
+register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ struct acpiphp_bridge *bridge = (struct acpiphp_bridge *)context;
+ struct acpiphp_slot *slot;
+ struct acpiphp_func *newfunc;
+ acpi_handle tmp;
+ acpi_status status = AE_OK;
+ unsigned long adr, sun;
+ int device, function;
+ static int num_slots = 0; /* XXX if we support I/O node hotplug... */
+
+ status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ status = acpi_get_handle(handle, "_EJ0", &tmp);
+
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ device = (adr >> 16) & 0xffff;
+ function = adr & 0xffff;
+
+ newfunc = kmalloc(sizeof(struct acpiphp_func), GFP_KERNEL);
+ if (!newfunc)
+ return AE_NO_MEMORY;
+ memset(newfunc, 0, sizeof(struct acpiphp_func));
+
+ INIT_LIST_HEAD(&newfunc->sibling);
+ newfunc->handle = handle;
+ newfunc->function = function;
+ newfunc->flags = FUNC_HAS_EJ0;
+
+ if (ACPI_SUCCESS(acpi_get_handle(handle, "_STA", &tmp)))
+ newfunc->flags |= FUNC_HAS_STA;
+
+ if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS0", &tmp)))
+ newfunc->flags |= FUNC_HAS_PS0;
+
+ if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp)))
+ newfunc->flags |= FUNC_HAS_PS3;
+
+ status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
+ if (ACPI_FAILURE(status))
+ sun = -1;
+
+ /* search for objects that share the same slot */
+ for (slot = bridge->slots; slot; slot = slot->next)
+ if (slot->device == device) {
+ if (slot->sun != sun)
+ warn("sibling found, but _SUN doesn't match!\n");
+ break;
+ }
+
+ if (!slot) {
+ slot = kmalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
+ if (!slot) {
+ kfree(newfunc);
+ return AE_NO_MEMORY;
+ }
+
+ memset(slot, 0, sizeof(struct acpiphp_slot));
+ slot->bridge = bridge;
+ slot->id = num_slots++;
+ slot->device = device;
+ slot->sun = sun;
+ INIT_LIST_HEAD(&slot->funcs);
+ init_MUTEX(&slot->crit_sect);
+
+ slot->next = bridge->slots;
+ bridge->slots = slot;
+
+ bridge->nr_slots++;
+
+ dbg("found ACPI PCI Hotplug slot at PCI %02x:%02x Slot:%d\n",
+ slot->bridge->bus, slot->device, slot->sun);
+ }
+
+ newfunc->slot = slot;
+ list_add_tail(&newfunc->sibling, &slot->funcs);
+
+ /* associate corresponding pci_dev */
+ newfunc->pci_dev = pci_find_slot(bridge->bus,
+ PCI_DEVFN(device, function));
+ if (newfunc->pci_dev) {
+ if (acpiphp_init_func_resource(newfunc) < 0) {
+ kfree(newfunc);
+ return AE_ERROR;
+ }
+ slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON);
+ }
+
+ /* install notify handler */
+ status = acpi_install_notify_handler(handle,
+ ACPI_SYSTEM_NOTIFY,
+ handle_hotplug_event_func,
+ newfunc);
+
+ if (ACPI_FAILURE(status)) {
+ err("failed to register interrupt notify handler\n");
+ return status;
+ }
+
+ return AE_OK;
+}
+
+
+/* see if it's worth looking at this bridge */
+static int detect_ejectable_slots(acpi_handle *bridge_handle)
+{
+ acpi_status status;
+ int count;
+
+ count = 0;
+
+ /* only check slots defined directly below bridge object */
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1,
+ is_ejectable_slot, (void *)&count, NULL);
+
+ return count;
+}
+
+
+/* decode ACPI _CRS data and convert into our internal resource list
+ * TBD: _TRA, etc.
+ */
+static acpi_status
+decode_acpi_resource(struct acpi_resource *resource, void *context)
+{
+ struct acpiphp_bridge *bridge = (struct acpiphp_bridge *) context;
+ struct acpi_resource_address64 address;
+ struct pci_resource *res;
+
+ if (resource->id != ACPI_RSTYPE_ADDRESS16 &&
+ resource->id != ACPI_RSTYPE_ADDRESS32 &&
+ resource->id != ACPI_RSTYPE_ADDRESS64)
+ return AE_OK;
+
+ acpi_resource_to_address64(resource, &address);
+
+ if (address.producer_consumer == ACPI_PRODUCER && address.address_length > 0) {
+ dbg("resource type: %d: 0x%llx - 0x%llx\n", address.resource_type,
+ (unsigned long long)address.min_address_range,
+ (unsigned long long)address.max_address_range);
+ res = acpiphp_make_resource(address.min_address_range,
+ address.address_length);
+ if (!res) {
+ err("out of memory\n");
+ return AE_OK;
+ }
+
+ switch (address.resource_type) {
+ case ACPI_MEMORY_RANGE:
+ if (address.attribute.memory.cache_attribute == ACPI_PREFETCHABLE_MEMORY) {
+ res->next = bridge->p_mem_head;
+ bridge->p_mem_head = res;
+ } else {
+ res->next = bridge->mem_head;
+ bridge->mem_head = res;
+ }
+ break;
+ case ACPI_IO_RANGE:
+ res->next = bridge->io_head;
+ bridge->io_head = res;
+ break;
+ case ACPI_BUS_NUMBER_RANGE:
+ res->next = bridge->bus_head;
+ bridge->bus_head = res;
+ break;
+ default:
+ /* invalid type */
+ kfree(res);
+ break;
+ }
+ }
+
+ return AE_OK;
+}
+
+/* decode ACPI 2.0 _HPP hot plug parameters */
+static void decode_hpp(struct acpiphp_bridge *bridge)
+{
+ acpi_status status;
+ struct acpi_buffer buffer = { .length = ACPI_ALLOCATE_BUFFER,
+ .pointer = NULL};
+ union acpi_object *package;
+ int i;
+
+ /* default numbers */
+ bridge->hpp.cache_line_size = 0x10;
+ bridge->hpp.latency_timer = 0x40;
+ bridge->hpp.enable_SERR = 0;
+ bridge->hpp.enable_PERR = 0;
+
+ status = acpi_evaluate_object(bridge->handle, "_HPP", NULL, &buffer);
+
+ if (ACPI_FAILURE(status)) {
+ dbg("_HPP evaluation failed\n");
+ return;
+ }
+
+ package = (union acpi_object *) buffer.pointer;
+
+ if (!package || package->type != ACPI_TYPE_PACKAGE ||
+ package->package.count != 4 || !package->package.elements) {
+ err("invalid _HPP object; ignoring\n");
+ goto err_exit;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (package->package.elements[i].type != ACPI_TYPE_INTEGER) {
+ err("invalid _HPP parameter type; ignoring\n");
+ goto err_exit;
+ }
+ }
+
+ bridge->hpp.cache_line_size = package->package.elements[0].integer.value;
+ bridge->hpp.latency_timer = package->package.elements[1].integer.value;
+ bridge->hpp.enable_SERR = package->package.elements[2].integer.value;
+ bridge->hpp.enable_PERR = package->package.elements[3].integer.value;
+
+ dbg("_HPP parameter = (%02x, %02x, %02x, %02x)\n",
+ bridge->hpp.cache_line_size,
+ bridge->hpp.latency_timer,
+ bridge->hpp.enable_SERR,
+ bridge->hpp.enable_PERR);
+
+ bridge->flags |= BRIDGE_HAS_HPP;
+
+ err_exit:
+ kfree(buffer.pointer);
+}
+
+
+/* initialize miscellaneous stuff for both root and PCI-to-PCI bridge */
+static void init_bridge_misc(struct acpiphp_bridge *bridge)
+{
+ acpi_status status;
+
+ /* decode ACPI 2.0 _HPP (hot plug parameters) */
+ decode_hpp(bridge);
+
+ /* subtract all resources already allocated */
+ acpiphp_detect_pci_resource(bridge);
+
+ /* register all slot objects under this bridge */
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, (u32)1,
+ register_slot, bridge, NULL);
+
+ /* install notify handler */
+ status = acpi_install_notify_handler(bridge->handle,
+ ACPI_SYSTEM_NOTIFY,
+ handle_hotplug_event_bridge,
+ bridge);
+
+ if (ACPI_FAILURE(status)) {
+ err("failed to register interrupt notify handler\n");
+ }
+
+ list_add(&bridge->list, &bridge_list);
+
+ dbg("Bridge resource:\n");
+ acpiphp_dump_resource(bridge);
+}
+
+
+/* allocate and initialize host bridge data structure */
+static void add_host_bridge(acpi_handle *handle, int seg, int bus)
+{
+ acpi_status status;
+ struct acpiphp_bridge *bridge;
+
+ bridge = kmalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
+ if (bridge == NULL)
+ return;
+
+ memset(bridge, 0, sizeof(struct acpiphp_bridge));
+
+ bridge->type = BRIDGE_TYPE_HOST;
+ bridge->handle = handle;
+ bridge->seg = seg;
+ bridge->bus = bus;
+
+ bridge->pci_bus = pci_find_bus(seg, bus);
+
+ spin_lock_init(&bridge->res_lock);
+
+ /* to be overridden when we decode _CRS */
+ bridge->sub = bridge->bus;
+
+ /* decode resources */
+
+ status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+ decode_acpi_resource, bridge);
+
+ if (ACPI_FAILURE(status)) {
+ err("failed to decode bridge resources\n");
+ kfree(bridge);
+ return;
+ }
+
+ acpiphp_resource_sort_and_combine(&bridge->io_head);
+ acpiphp_resource_sort_and_combine(&bridge->mem_head);
+ acpiphp_resource_sort_and_combine(&bridge->p_mem_head);
+ acpiphp_resource_sort_and_combine(&bridge->bus_head);
+
+ dbg("ACPI _CRS resource:\n");
+ acpiphp_dump_resource(bridge);
+
+ if (bridge->bus_head) {
+ bridge->bus = bridge->bus_head->base;
+ bridge->sub = bridge->bus_head->base + bridge->bus_head->length - 1;
+ }
+
+ init_bridge_misc(bridge);
+}
+
+
+/* allocate and initialize PCI-to-PCI bridge data structure */
+static void add_p2p_bridge(acpi_handle *handle, int seg, int bus, int dev, int fn)
+{
+ struct acpiphp_bridge *bridge;
+ u8 tmp8;
+ u16 tmp16;
+ u64 base64, limit64;
+ u32 base, limit, base32u, limit32u;
+
+ bridge = kmalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
+ if (bridge == NULL) {
+ err("out of memory\n");
+ return;
+ }
+
+ memset(bridge, 0, sizeof(struct acpiphp_bridge));
+
+ bridge->type = BRIDGE_TYPE_P2P;
+ bridge->handle = handle;
+ bridge->seg = seg;
+
+ bridge->pci_dev = pci_find_slot(bus, PCI_DEVFN(dev, fn));
+ if (!bridge->pci_dev) {
+ err("Can't get pci_dev\n");
+ kfree(bridge);
+ return;
+ }
+
+ bridge->pci_bus = bridge->pci_dev->subordinate;
+ if (!bridge->pci_bus) {
+ err("This is not a PCI-to-PCI bridge!\n");
+ kfree(bridge);
+ return;
+ }
+
+ spin_lock_init(&bridge->res_lock);
+
+ bridge->bus = bridge->pci_bus->number;
+ bridge->sub = bridge->pci_bus->subordinate;
+
+ /*
+ * decode resources under this P2P bridge
+ */
+
+ /* I/O resources */
+ pci_read_config_byte(bridge->pci_dev, PCI_IO_BASE, &tmp8);
+ base = tmp8;
+ pci_read_config_byte(bridge->pci_dev, PCI_IO_LIMIT, &tmp8);
+ limit = tmp8;
+
+ switch (base & PCI_IO_RANGE_TYPE_MASK) {
+ case PCI_IO_RANGE_TYPE_16:
+ base = (base << 8) & 0xf000;
+ limit = ((limit << 8) & 0xf000) + 0xfff;
+ bridge->io_head = acpiphp_make_resource((u64)base, limit - base + 1);
+ if (!bridge->io_head) {
+ err("out of memory\n");
+ kfree(bridge);
+ return;
+ }
+ dbg("16bit I/O range: %04x-%04x\n",
+ (u32)bridge->io_head->base,
+ (u32)(bridge->io_head->base + bridge->io_head->length - 1));
+ break;
+ case PCI_IO_RANGE_TYPE_32:
+ pci_read_config_word(bridge->pci_dev, PCI_IO_BASE_UPPER16, &tmp16);
+ base = ((u32)tmp16 << 16) | ((base << 8) & 0xf000);
+ pci_read_config_word(bridge->pci_dev, PCI_IO_LIMIT_UPPER16, &tmp16);
+ limit = (((u32)tmp16 << 16) | ((limit << 8) & 0xf000)) + 0xfff;
+ bridge->io_head = acpiphp_make_resource((u64)base, limit - base + 1);
+ if (!bridge->io_head) {
+ err("out of memory\n");
+ kfree(bridge);
+ return;
+ }
+ dbg("32bit I/O range: %08x-%08x\n",
+ (u32)bridge->io_head->base,
+ (u32)(bridge->io_head->base + bridge->io_head->length - 1));
+ break;
+ case 0x0f:
+ dbg("I/O space unsupported\n");
+ break;
+ default:
+ warn("Unknown I/O range type\n");
+ }
+
+ /* Memory resources (mandatory for P2P bridge) */
+ pci_read_config_word(bridge->pci_dev, PCI_MEMORY_BASE, &tmp16);
+ base = (tmp16 & 0xfff0) << 16;
+ pci_read_config_word(bridge->pci_dev, PCI_MEMORY_LIMIT, &tmp16);
+ limit = ((tmp16 & 0xfff0) << 16) | 0xfffff;
+ bridge->mem_head = acpiphp_make_resource((u64)base, limit - base + 1);
+ if (!bridge->mem_head) {
+ err("out of memory\n");
+ kfree(bridge);
+ return;
+ }
+ dbg("32bit Memory range: %08x-%08x\n",
+ (u32)bridge->mem_head->base,
+ (u32)(bridge->mem_head->base + bridge->mem_head->length-1));
+
+ /* Prefetchable Memory resources (optional) */
+ pci_read_config_word(bridge->pci_dev, PCI_PREF_MEMORY_BASE, &tmp16);
+ base = tmp16;
+ pci_read_config_word(bridge->pci_dev, PCI_PREF_MEMORY_LIMIT, &tmp16);
+ limit = tmp16;
+
+ switch (base & PCI_MEMORY_RANGE_TYPE_MASK) {
+ case PCI_PREF_RANGE_TYPE_32:
+ base = (base & 0xfff0) << 16;
+ limit = ((limit & 0xfff0) << 16) | 0xfffff;
+ bridge->p_mem_head = acpiphp_make_resource((u64)base, limit - base + 1);
+ if (!bridge->p_mem_head) {
+ err("out of memory\n");
+ kfree(bridge);
+ return;
+ }
+ dbg("32bit Prefetchable memory range: %08x-%08x\n",
+ (u32)bridge->p_mem_head->base,
+ (u32)(bridge->p_mem_head->base + bridge->p_mem_head->length - 1));
+ break;
+ case PCI_PREF_RANGE_TYPE_64:
+ pci_read_config_dword(bridge->pci_dev, PCI_PREF_BASE_UPPER32, &base32u);
+ pci_read_config_dword(bridge->pci_dev, PCI_PREF_LIMIT_UPPER32, &limit32u);
+ base64 = ((u64)base32u << 32) | ((base & 0xfff0) << 16);
+ limit64 = (((u64)limit32u << 32) | ((limit & 0xfff0) << 16)) + 0xfffff;
+
+ bridge->p_mem_head = acpiphp_make_resource(base64, limit64 - base64 + 1);
+ if (!bridge->p_mem_head) {
+ err("out of memory\n");
+ kfree(bridge);
+ return;
+ }
+ dbg("64bit Prefetchable memory range: %08x%08x-%08x%08x\n",
+ (u32)(bridge->p_mem_head->base >> 32),
+ (u32)(bridge->p_mem_head->base & 0xffffffff),
+ (u32)((bridge->p_mem_head->base + bridge->p_mem_head->length - 1) >> 32),
+ (u32)((bridge->p_mem_head->base + bridge->p_mem_head->length - 1) & 0xffffffff));
+ break;
+ case 0x0f:
+ break;
+ default:
+ warn("Unknown prefetchale memory type\n");
+ }
+
+ init_bridge_misc(bridge);
+}
+
+
+/* callback routine to find P2P bridges */
+static acpi_status
+find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ acpi_status status;
+ acpi_handle dummy_handle;
+ unsigned long *segbus = context;
+ unsigned long tmp;
+ int seg, bus, device, function;
+ struct pci_dev *dev;
+
+ /* get PCI address */
+ seg = (*segbus >> 8) & 0xff;
+ bus = *segbus & 0xff;
+
+ status = acpi_get_handle(handle, "_ADR", &dummy_handle);
+ if (ACPI_FAILURE(status))
+ return AE_OK; /* continue */
+
+ status = acpi_evaluate_integer(handle, "_ADR", NULL, &tmp);
+ if (ACPI_FAILURE(status)) {
+ dbg("%s: _ADR evaluation failure\n", __FUNCTION__);
+ return AE_OK;
+ }
+
+ device = (tmp >> 16) & 0xffff;
+ function = tmp & 0xffff;
+
+ dev = pci_find_slot(bus, PCI_DEVFN(device, function));
+
+ if (!dev)
+ return AE_OK;
+
+ if (!dev->subordinate)
+ return AE_OK;
+
+ /* check if this bridge has ejectable slots */
+ if (detect_ejectable_slots(handle) > 0) {
+ dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev));
+ add_p2p_bridge(handle, seg, bus, device, function);
+ }
+
+ return AE_OK;
+}
+
+
+/* find hot-pluggable slots, and then find P2P bridge */
+static int add_bridge(acpi_handle handle)
+{
+ acpi_status status;
+ unsigned long tmp;
+ int seg, bus;
+ acpi_handle dummy_handle;
+
+ /* if the bridge doesn't have _STA, we assume it is always there */
+ status = acpi_get_handle(handle, "_STA", &dummy_handle);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp);
+ if (ACPI_FAILURE(status)) {
+ dbg("%s: _STA evaluation failure\n", __FUNCTION__);
+ return 0;
+ }
+ if ((tmp & ACPI_STA_FUNCTIONING) == 0)
+ /* don't register this object */
+ return 0;
+ }
+
+ /* get PCI segment number */
+ status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp);
+
+ seg = ACPI_SUCCESS(status) ? tmp : 0;
+
+ /* get PCI bus number */
+ status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp);
+
+ if (ACPI_SUCCESS(status)) {
+ bus = tmp;
+ } else {
+ warn("can't get bus number, assuming 0\n");
+ bus = 0;
+ }
+
+ /* check if this bridge has ejectable slots */
+ if (detect_ejectable_slots(handle) > 0) {
+ dbg("found PCI host-bus bridge with hot-pluggable slots\n");
+ add_host_bridge(handle, seg, bus);
+ return 0;
+ }
+
+ tmp = seg << 8 | bus;
+
+ /* search P2P bridges under this host bridge */
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+ find_p2p_bridge, &tmp, NULL);
+
+ if (ACPI_FAILURE(status))
+ warn("find_p2p_bridge faied (error code = 0x%x)\n",status);
+
+ return 0;
+}
+
+
+static void remove_bridge(acpi_handle handle)
+{
+ /* No-op for now .. */
+}
+
+
+static int power_on_slot(struct acpiphp_slot *slot)
+{
+ acpi_status status;
+ struct acpiphp_func *func;
+ struct list_head *l;
+ int retval = 0;
+
+ /* if already enabled, just skip */
+ if (slot->flags & SLOT_POWEREDON)
+ goto err_exit;
+
+ list_for_each (l, &slot->funcs) {
+ func = list_entry(l, struct acpiphp_func, sibling);
+
+ if (func->flags & FUNC_HAS_PS0) {
+ dbg("%s: executing _PS0\n", __FUNCTION__);
+ status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ warn("%s: _PS0 failed\n", __FUNCTION__);
+ retval = -1;
+ goto err_exit;
+ } else
+ break;
+ }
+ }
+
+ /* TBD: evaluate _STA to check if the slot is enabled */
+
+ slot->flags |= SLOT_POWEREDON;
+
+ err_exit:
+ return retval;
+}
+
+
+static int power_off_slot(struct acpiphp_slot *slot)
+{
+ acpi_status status;
+ struct acpiphp_func *func;
+ struct list_head *l;
+ struct acpi_object_list arg_list;
+ union acpi_object arg;
+
+ int retval = 0;
+
+ /* if already disabled, just skip */
+ if ((slot->flags & SLOT_POWEREDON) == 0)
+ goto err_exit;
+
+ list_for_each (l, &slot->funcs) {
+ func = list_entry(l, struct acpiphp_func, sibling);
+
+ if (func->pci_dev && (func->flags & FUNC_HAS_PS3)) {
+ status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ warn("%s: _PS3 failed\n", __FUNCTION__);
+ retval = -1;
+ goto err_exit;
+ } else
+ break;
+ }
+ }
+
+ list_for_each (l, &slot->funcs) {
+ func = list_entry(l, struct acpiphp_func, sibling);
+
+ /* We don't want to call _EJ0 on non-existing functions. */
+ if (func->pci_dev && (func->flags & FUNC_HAS_EJ0)) {
+ /* _EJ0 method take one argument */
+ arg_list.count = 1;
+ arg_list.pointer = &arg;
+ arg.type = ACPI_TYPE_INTEGER;
+ arg.integer.value = 1;
+
+ status = acpi_evaluate_object(func->handle, "_EJ0", &arg_list, NULL);
+ if (ACPI_FAILURE(status)) {
+ warn("%s: _EJ0 failed\n", __FUNCTION__);
+ retval = -1;
+ goto err_exit;
+ } else
+ break;
+ }
+ }
+
+ /* TBD: evaluate _STA to check if the slot is disabled */
+
+ slot->flags &= (~SLOT_POWEREDON);
+
+ err_exit:
+ return retval;
+}
+
+
+/**
+ * enable_device - enable, configure a slot
+ * @slot: slot to be enabled
+ *
+ * This function should be called per *physical slot*,
+ * not per each slot object in ACPI namespace.
+ *
+ */
+static int enable_device(struct acpiphp_slot *slot)
+{
+ u8 bus;
+ struct pci_dev *dev;
+ struct pci_bus *child;
+ struct list_head *l;
+ struct acpiphp_func *func;
+ int retval = 0;
+ int num;
+
+ if (slot->flags & SLOT_ENABLED)
+ goto err_exit;
+
+ /* sanity check: dev should be NULL when hot-plugged in */
+ dev = pci_find_slot(slot->bridge->bus, PCI_DEVFN(slot->device, 0));
+ if (dev) {
+ /* This case shouldn't happen */
+ err("pci_dev structure already exists.\n");
+ retval = -1;
+ goto err_exit;
+ }
+
+ /* allocate resources to device */
+ retval = acpiphp_configure_slot(slot);
+ if (retval)
+ goto err_exit;
+
+ /* returned `dev' is the *first function* only! */
+ num = pci_scan_slot(slot->bridge->pci_bus, PCI_DEVFN(slot->device, 0));
+ if (num)
+ pci_bus_add_devices(slot->bridge->pci_bus);
+ dev = pci_find_slot(slot->bridge->bus, PCI_DEVFN(slot->device, 0));
+
+ if (!dev) {
+ err("No new device found\n");
+ retval = -1;
+ goto err_exit;
+ }
+
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ pci_read_config_byte(dev, PCI_SECONDARY_BUS, &bus);
+ child = (struct pci_bus*) pci_add_new_bus(dev->bus, dev, bus);
+ pci_do_scan_bus(child);
+ }
+
+ /* associate pci_dev to our representation */
+ list_for_each (l, &slot->funcs) {
+ func = list_entry(l, struct acpiphp_func, sibling);
+
+ func->pci_dev = pci_find_slot(slot->bridge->bus,
+ PCI_DEVFN(slot->device,
+ func->function));
+ if (!func->pci_dev)
+ continue;
+
+ /* configure device */
+ retval = acpiphp_configure_function(func);
+ if (retval)
+ goto err_exit;
+ }
+
+ slot->flags |= SLOT_ENABLED;
+
+ dbg("Available resources:\n");
+ acpiphp_dump_resource(slot->bridge);
+
+ err_exit:
+ return retval;
+}
+
+
+/**
+ * disable_device - disable a slot
+ */
+static int disable_device(struct acpiphp_slot *slot)
+{
+ int retval = 0;
+ struct acpiphp_func *func;
+ struct list_head *l;
+
+ /* is this slot already disabled? */
+ if (!(slot->flags & SLOT_ENABLED))
+ goto err_exit;
+
+ list_for_each (l, &slot->funcs) {
+ func = list_entry(l, struct acpiphp_func, sibling);
+
+ if (func->pci_dev)
+ acpiphp_unconfigure_function(func);
+ }
+
+ slot->flags &= (~SLOT_ENABLED);
+
+ err_exit:
+ return retval;
+}
+
+
+/**
+ * get_slot_status - get ACPI slot status
+ *
+ * if a slot has _STA for each function and if any one of them
+ * returned non-zero status, return it
+ *
+ * if a slot doesn't have _STA and if any one of its functions'
+ * configuration space is configured, return 0x0f as a _STA
+ *
+ * otherwise return 0
+ */
+static unsigned int get_slot_status(struct acpiphp_slot *slot)
+{
+ acpi_status status;
+ unsigned long sta = 0;
+ u32 dvid;
+ struct list_head *l;
+ struct acpiphp_func *func;
+
+ list_for_each (l, &slot->funcs) {
+ func = list_entry(l, struct acpiphp_func, sibling);
+
+ if (func->flags & FUNC_HAS_STA) {
+ status = acpi_evaluate_integer(func->handle, "_STA", NULL, &sta);
+ if (ACPI_SUCCESS(status) && sta)
+ break;
+ } else {
+ pci_bus_read_config_dword(slot->bridge->pci_bus,
+ PCI_DEVFN(slot->device,
+ func->function),
+ PCI_VENDOR_ID, &dvid);
+ if (dvid != 0xffffffff) {
+ sta = ACPI_STA_ALL;
+ break;
+ }
+ }
+ }
+
+ return (unsigned int)sta;
+}
+
+/**
+ * acpiphp_check_bridge - re-enumerate devices
+ *
+ * Iterate over all slots under this bridge and make sure that if a
+ * card is present they are enabled, and if not they are disabled.
+ */
+static int acpiphp_check_bridge(struct acpiphp_bridge *bridge)
+{
+ struct acpiphp_slot *slot;
+ int retval = 0;
+ int enabled, disabled;
+
+ enabled = disabled = 0;
+
+ for (slot = bridge->slots; slot; slot = slot->next) {
+ unsigned int status = get_slot_status(slot);
+ if (slot->flags & SLOT_ENABLED) {
+ if (status == ACPI_STA_ALL)
+ continue;
+ retval = acpiphp_disable_slot(slot);
+ if (retval) {
+ err("Error occurred in disabling\n");
+ goto err_exit;
+ }
+ disabled++;
+ } else {
+ if (status != ACPI_STA_ALL)
+ continue;
+ retval = acpiphp_enable_slot(slot);
+ if (retval) {
+ err("Error occurred in enabling\n");
+ goto err_exit;
+ }
+ enabled++;
+ }
+ }
+
+ dbg("%s: %d enabled, %d disabled\n", __FUNCTION__, enabled, disabled);
+
+ err_exit:
+ return retval;
+}
+
+/*
+ * ACPI event handlers
+ */
+
+/**
+ * handle_hotplug_event_bridge - handle ACPI event on bridges
+ *
+ * @handle: Notify()'ed acpi_handle
+ * @type: Notify code
+ * @context: pointer to acpiphp_bridge structure
+ *
+ * handles ACPI event notification on {host,p2p} bridges
+ *
+ */
+static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *context)
+{
+ struct acpiphp_bridge *bridge;
+ char objname[64];
+ struct acpi_buffer buffer = { .length = sizeof(objname),
+ .pointer = objname };
+
+ bridge = (struct acpiphp_bridge *)context;
+
+ acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+
+ switch (type) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ /* bus re-enumerate */
+ dbg("%s: Bus check notify on %s\n", __FUNCTION__, objname);
+ acpiphp_check_bridge(bridge);
+ break;
+
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ /* device check */
+ dbg("%s: Device check notify on %s\n", __FUNCTION__, objname);
+ acpiphp_check_bridge(bridge);
+ break;
+
+ case ACPI_NOTIFY_DEVICE_WAKE:
+ /* wake event */
+ dbg("%s: Device wake notify on %s\n", __FUNCTION__, objname);
+ break;
+
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ /* request device eject */
+ dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname);
+ break;
+
+ case ACPI_NOTIFY_FREQUENCY_MISMATCH:
+ printk(KERN_ERR "Device %s cannot be configured due"
+ " to a frequency mismatch\n", objname);
+ break;
+
+ case ACPI_NOTIFY_BUS_MODE_MISMATCH:
+ printk(KERN_ERR "Device %s cannot be configured due"
+ " to a bus mode mismatch\n", objname);
+ break;
+
+ case ACPI_NOTIFY_POWER_FAULT:
+ printk(KERN_ERR "Device %s has suffered a power fault\n",
+ objname);
+ break;
+
+ default:
+ warn("notify_handler: unknown event type 0x%x for %s\n", type, objname);
+ break;
+ }
+}
+
+
+/**
+ * handle_hotplug_event_func - handle ACPI event on functions (i.e. slots)
+ *
+ * @handle: Notify()'ed acpi_handle
+ * @type: Notify code
+ * @context: pointer to acpiphp_func structure
+ *
+ * handles ACPI event notification on slots
+ *
+ */
+static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context)
+{
+ struct acpiphp_func *func;
+ char objname[64];
+ struct acpi_buffer buffer = { .length = sizeof(objname),
+ .pointer = objname };
+
+ acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+
+ func = (struct acpiphp_func *)context;
+
+ switch (type) {
+ case ACPI_NOTIFY_BUS_CHECK:
+ /* bus re-enumerate */
+ dbg("%s: Bus check notify on %s\n", __FUNCTION__, objname);
+ acpiphp_enable_slot(func->slot);
+ break;
+
+ case ACPI_NOTIFY_DEVICE_CHECK:
+ /* device check : re-enumerate from parent bus */
+ dbg("%s: Device check notify on %s\n", __FUNCTION__, objname);
+ acpiphp_check_bridge(func->slot->bridge);
+ break;
+
+ case ACPI_NOTIFY_DEVICE_WAKE:
+ /* wake event */
+ dbg("%s: Device wake notify on %s\n", __FUNCTION__, objname);
+ break;
+
+ case ACPI_NOTIFY_EJECT_REQUEST:
+ /* request device eject */
+ dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname);
+ acpiphp_disable_slot(func->slot);
+ break;
+
+ default:
+ warn("notify_handler: unknown event type 0x%x for %s\n", type, objname);
+ break;
+ }
+}
+
+
+static struct acpi_pci_driver acpi_pci_hp_driver = {
+ .add = add_bridge,
+ .remove = remove_bridge,
+};
+
+/**
+ * acpiphp_glue_init - initializes all PCI hotplug - ACPI glue data structures
+ *
+ */
+int __init acpiphp_glue_init(void)
+{
+ int num;
+
+ if (list_empty(&pci_root_buses))
+ return -1;
+
+ num = acpi_pci_register_driver(&acpi_pci_hp_driver);
+
+ if (num <= 0)
+ return -1;
+
+ return 0;
+}
+
+
+/**
+ * acpiphp_glue_exit - terminates all PCI hotplug - ACPI glue data structures
+ *
+ * This function frees all data allocated in acpiphp_glue_init()
+ */
+void __exit acpiphp_glue_exit(void)
+{
+ struct list_head *l1, *l2, *n1, *n2;
+ struct acpiphp_bridge *bridge;
+ struct acpiphp_slot *slot, *next;
+ struct acpiphp_func *func;
+ acpi_status status;
+
+ list_for_each_safe (l1, n1, &bridge_list) {
+ bridge = (struct acpiphp_bridge *)l1;
+ slot = bridge->slots;
+ while (slot) {
+ next = slot->next;
+ list_for_each_safe (l2, n2, &slot->funcs) {
+ func = list_entry(l2, struct acpiphp_func, sibling);
+ acpiphp_free_resource(&func->io_head);
+ acpiphp_free_resource(&func->mem_head);
+ acpiphp_free_resource(&func->p_mem_head);
+ acpiphp_free_resource(&func->bus_head);
+ status = acpi_remove_notify_handler(func->handle,
+ ACPI_SYSTEM_NOTIFY,
+ handle_hotplug_event_func);
+ if (ACPI_FAILURE(status))
+ err("failed to remove notify handler\n");
+ kfree(func);
+ }
+ kfree(slot);
+ slot = next;
+ }
+ status = acpi_remove_notify_handler(bridge->handle, ACPI_SYSTEM_NOTIFY,
+ handle_hotplug_event_bridge);
+ if (ACPI_FAILURE(status))
+ err("failed to remove notify handler\n");
+
+ acpiphp_free_resource(&bridge->io_head);
+ acpiphp_free_resource(&bridge->mem_head);
+ acpiphp_free_resource(&bridge->p_mem_head);
+ acpiphp_free_resource(&bridge->bus_head);
+
+ kfree(bridge);
+ }
+
+ acpi_pci_unregister_driver(&acpi_pci_hp_driver);
+}
+
+
+/**
+ * acpiphp_get_num_slots - count number of slots in a system
+ */
+int __init acpiphp_get_num_slots(void)
+{
+ struct list_head *node;
+ struct acpiphp_bridge *bridge;
+ int num_slots;
+
+ num_slots = 0;
+
+ list_for_each (node, &bridge_list) {
+ bridge = (struct acpiphp_bridge *)node;
+ dbg("Bus%d %dslot(s)\n", bridge->bus, bridge->nr_slots);
+ num_slots += bridge->nr_slots;
+ }
+
+ dbg("Total %dslots\n", num_slots);
+ return num_slots;
+}
+
+
+#if 0
+/**
+ * acpiphp_for_each_slot - call function for each slot
+ * @fn: callback function
+ * @data: context to be passed to callback function
+ *
+ */
+static int acpiphp_for_each_slot(acpiphp_callback fn, void *data)
+{
+ struct list_head *node;
+ struct acpiphp_bridge *bridge;
+ struct acpiphp_slot *slot;
+ int retval = 0;
+
+ list_for_each (node, &bridge_list) {
+ bridge = (struct acpiphp_bridge *)node;
+ for (slot = bridge->slots; slot; slot = slot->next) {
+ retval = fn(slot, data);
+ if (!retval)
+ goto err_exit;
+ }
+ }
+
+ err_exit:
+ return retval;
+}
+#endif
+
+/* search matching slot from id */
+struct acpiphp_slot *get_slot_from_id(int id)
+{
+ struct list_head *node;
+ struct acpiphp_bridge *bridge;
+ struct acpiphp_slot *slot;
+
+ list_for_each (node, &bridge_list) {
+ bridge = (struct acpiphp_bridge *)node;
+ for (slot = bridge->slots; slot; slot = slot->next)
+ if (slot->id == id)
+ return slot;
+ }
+
+ /* should never happen! */
+ err("%s: no object for id %d\n", __FUNCTION__, id);
+ WARN_ON(1);
+ return NULL;
+}
+
+
+/**
+ * acpiphp_enable_slot - power on slot
+ */
+int acpiphp_enable_slot(struct acpiphp_slot *slot)
+{
+ int retval;
+
+ down(&slot->crit_sect);
+
+ /* wake up all functions */
+ retval = power_on_slot(slot);
+ if (retval)
+ goto err_exit;
+
+ if (get_slot_status(slot) == ACPI_STA_ALL)
+ /* configure all functions */
+ retval = enable_device(slot);
+
+ err_exit:
+ up(&slot->crit_sect);
+ return retval;
+}
+
+
+/**
+ * acpiphp_disable_slot - power off slot
+ */
+int acpiphp_disable_slot(struct acpiphp_slot *slot)
+{
+ int retval = 0;
+
+ down(&slot->crit_sect);
+
+ /* unconfigure all functions */
+ retval = disable_device(slot);
+ if (retval)
+ goto err_exit;
+
+ /* power off all functions */
+ retval = power_off_slot(slot);
+ if (retval)
+ goto err_exit;
+
+ acpiphp_resource_sort_and_combine(&slot->bridge->io_head);
+ acpiphp_resource_sort_and_combine(&slot->bridge->mem_head);
+ acpiphp_resource_sort_and_combine(&slot->bridge->p_mem_head);
+ acpiphp_resource_sort_and_combine(&slot->bridge->bus_head);
+ dbg("Available resources:\n");
+ acpiphp_dump_resource(slot->bridge);
+
+ err_exit:
+ up(&slot->crit_sect);
+ return retval;
+}
+
+
+/*
+ * slot enabled: 1
+ * slot disabled: 0
+ */
+u8 acpiphp_get_power_status(struct acpiphp_slot *slot)
+{
+ unsigned int sta;
+
+ sta = get_slot_status(slot);
+
+ return (sta & ACPI_STA_ENABLED) ? 1 : 0;
+}
+
+
+/*
+ * latch closed: 1
+ * latch open: 0
+ */
+u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)
+{
+ unsigned int sta;
+
+ sta = get_slot_status(slot);
+
+ return (sta & ACPI_STA_SHOW_IN_UI) ? 1 : 0;
+}
+
+
+/*
+ * adapter presence : 1
+ * absence : 0
+ */
+u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot)
+{
+ unsigned int sta;
+
+ sta = get_slot_status(slot);
+
+ return (sta == 0) ? 0 : 1;
+}
+
+
+/*
+ * pci address (seg/bus/dev)
+ */
+u32 acpiphp_get_address(struct acpiphp_slot *slot)
+{
+ u32 address;
+
+ address = ((slot->bridge->seg) << 16) |
+ ((slot->bridge->bus) << 8) |
+ slot->device;
+
+ return address;
+}
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
new file mode 100644
index 000000000000..7e7f913ba7b9
--- /dev/null
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -0,0 +1,499 @@
+/*
+ * ACPI PCI Hot Plug IBM Extension
+ *
+ * Copyright (C) 2004 Vernon Mauery <vernux@us.ibm.com>
+ * Copyright (C) 2004 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 <vernux@us.ibm.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <acpi/acpi_bus.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <asm/uaccess.h>
+#include <linux/moduleparam.h>
+
+#include "acpiphp.h"
+#include "pci_hotplug.h"
+
+#define DRIVER_VERSION "1.0.1"
+#define DRIVER_AUTHOR "Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
+#define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver IBM extension"
+
+static int debug;
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, " Debugging mode enabled or not");
+#define MY_NAME "acpiphp_ibm"
+
+#undef dbg
+#define dbg(format, arg...) \
+do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: " format, \
+ MY_NAME , ## arg); \
+} while (0)
+
+#define FOUND_APCI 0x61504349
+/* these are the names for the IBM ACPI pseudo-device */
+#define IBM_HARDWARE_ID1 "IBM37D0"
+#define IBM_HARDWARE_ID2 "IBM37D4"
+
+#define hpslot_to_sun(A) (((struct slot *)((A)->private))->acpi_slot->sun)
+
+/* union apci_descriptor - allows access to the
+ * various device descriptors that are embedded in the
+ * aPCI table
+ */
+union apci_descriptor {
+ struct {
+ char sig[4];
+ u8 len;
+ } header;
+ struct {
+ u8 type;
+ u8 len;
+ u16 slot_id;
+ u8 bus_id;
+ u8 dev_num;
+ u8 slot_num;
+ u8 slot_attr[2];
+ u8 attn;
+ u8 status[2];
+ u8 sun;
+ u8 res[3];
+ } slot;
+ struct {
+ u8 type;
+ u8 len;
+ } generic;
+};
+
+/* struct notification - keeps info about the device
+ * that cause the ACPI notification event
+ */
+struct notification {
+ struct acpi_device *device;
+ u8 event;
+};
+
+static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status);
+static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status);
+static void ibm_handle_events(acpi_handle handle, u32 event, void *context);
+static int ibm_get_table_from_acpi(char **bufp);
+static ssize_t ibm_read_apci_table(struct kobject *kobj,
+ char *buffer, loff_t pos, size_t size);
+static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
+ u32 lvl, void *context, void **rv);
+static int __init ibm_acpiphp_init(void);
+static void __exit ibm_acpiphp_exit(void);
+
+static acpi_handle ibm_acpi_handle;
+static struct notification ibm_note;
+static struct bin_attribute ibm_apci_table_attr = {
+ .attr = {
+ .name = "apci_table",
+ .owner = THIS_MODULE,
+ .mode = S_IRUGO,
+ },
+ .read = ibm_read_apci_table,
+ .write = NULL,
+};
+static struct acpiphp_attention_info ibm_attention_info =
+{
+ .set_attn = ibm_set_attention_status,
+ .get_attn = ibm_get_attention_status,
+ .owner = THIS_MODULE,
+};
+
+/**
+ * ibm_slot_from_id - workaround for bad ibm hardware
+ * @id: the slot number that linux refers to the slot by
+ *
+ * Description: this method returns the aCPI slot descriptor
+ * corresponding to the Linux slot number. This descriptor
+ * has info about the aPCI slot id and attention status.
+ * This descriptor must be freed using kfree when done.
+ **/
+static union apci_descriptor *ibm_slot_from_id(int id)
+{
+ int ind = 0, size;
+ union apci_descriptor *ret = NULL, *des;
+ char *table;
+
+ size = ibm_get_table_from_acpi(&table);
+ des = (union apci_descriptor *)table;
+ if (memcmp(des->header.sig, "aPCI", 4) != 0)
+ goto ibm_slot_done;
+
+ des = (union apci_descriptor *)&table[ind += des->header.len];
+ while (ind < size && (des->generic.type != 0x82 ||
+ des->slot.slot_num != id)) {
+ des = (union apci_descriptor *)&table[ind += des->generic.len];
+ }
+
+ if (ind < size && des->slot.slot_num == id)
+ ret = des;
+
+ibm_slot_done:
+ if (ret) {
+ ret = kmalloc(sizeof(union apci_descriptor), GFP_KERNEL);
+ memcpy(ret, des, sizeof(union apci_descriptor));
+ }
+ kfree(table);
+ return ret;
+}
+
+/**
+ * ibm_set_attention_status - callback method to set the attention LED
+ * @slot: the hotplug_slot to work with
+ * @status: what to set the LED to (0 or 1)
+ *
+ * Description: this method is registered with the acpiphp module as a
+ * callback to do the device specific task of setting the LED status
+ **/
+static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
+{
+ union acpi_object args[2];
+ struct acpi_object_list params = { .pointer = args, .count = 2 };
+ acpi_status stat;
+ unsigned long rc;
+ union apci_descriptor *ibm_slot;
+
+ ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
+
+ dbg("%s: set slot %d (%d) attention status to %d\n", __FUNCTION__,
+ ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
+ (status ? 1 : 0));
+
+ args[0].type = ACPI_TYPE_INTEGER;
+ args[0].integer.value = ibm_slot->slot.slot_id;
+ args[1].type = ACPI_TYPE_INTEGER;
+ args[1].integer.value = (status) ? 1 : 0;
+
+ kfree(ibm_slot);
+
+ stat = acpi_evaluate_integer(ibm_acpi_handle, "APLS", &params, &rc);
+ if (ACPI_FAILURE(stat)) {
+ err("APLS evaluation failed: 0x%08x\n", stat);
+ return -ENODEV;
+ } else if (!rc) {
+ err("APLS method failed: 0x%08lx\n", rc);
+ return -ERANGE;
+ }
+ return 0;
+}
+
+/**
+ * ibm_get_attention_status - callback method to get attention LED status
+ * @slot: the hotplug_slot to work with
+ * @status: returns what the LED is set to (0 or 1)
+ *
+ * Description: this method is registered with the acpiphp module as a
+ * callback to do the device specific task of getting the LED status
+ *
+ * Because there is no direct method of getting the LED status directly
+ * from an ACPI call, we read the aPCI table and parse out our
+ * slot descriptor to read the status from that.
+ **/
+static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status)
+{
+ union apci_descriptor *ibm_slot;
+
+ ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
+
+ if (ibm_slot->slot.attn & 0xa0 || ibm_slot->slot.status[1] & 0x08)
+ *status = 1;
+ else
+ *status = 0;
+
+ dbg("%s: get slot %d (%d) attention status is %d\n", __FUNCTION__,
+ ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
+ *status);
+
+ kfree(ibm_slot);
+ return 0;
+}
+
+/**
+ * ibm_handle_events - listens for ACPI events for the IBM37D0 device
+ * @handle: an ACPI handle to the device that caused the event
+ * @event: the event info (device specific)
+ * @context: passed context (our notification struct)
+ *
+ * Description: this method is registered as a callback with the ACPI
+ * subsystem it is called when this device has an event to notify the OS of
+ *
+ * The events actually come from the device as two events that get
+ * synthesized into one event with data by this function. The event
+ * ID comes first and then the slot number that caused it. We report
+ * this as one event to the OS.
+ *
+ * From section 5.6.2.2 of the ACPI 2.0 spec, I understand that the OSPM will
+ * only re-enable the interrupt that causes this event AFTER this method
+ * has returned, thereby enforcing serial access for the notification struct.
+ **/
+static void ibm_handle_events(acpi_handle handle, u32 event, void *context)
+{
+ u8 detail = event & 0x0f;
+ u8 subevent = event & 0xf0;
+ struct notification *note = context;
+
+ dbg("%s: Received notification %02x\n", __FUNCTION__, event);
+
+ if (subevent == 0x80) {
+ dbg("%s: generationg bus event\n", __FUNCTION__);
+ acpi_bus_generate_event(note->device, note->event, detail);
+ } else
+ note->event = event;
+}
+
+/**
+ * ibm_get_table_from_acpi - reads the APLS buffer from ACPI
+ * @bufp: address to pointer to allocate for the table
+ *
+ * Description: this method reads the APLS buffer in from ACPI and
+ * stores the "stripped" table into a single buffer
+ * it allocates and passes the address back in bufp
+ *
+ * If NULL is passed in as buffer, this method only calculates
+ * the size of the table and returns that without filling
+ * in the buffer
+ *
+ * returns < 0 on error or the size of the table on success
+ **/
+static int ibm_get_table_from_acpi(char **bufp)
+{
+ union acpi_object *package;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+ char *lbuf = NULL;
+ int i, size = -EIO;
+
+ status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ err("%s: APCI evaluation failed\n", __FUNCTION__);
+ return -ENODEV;
+ }
+
+ package = (union acpi_object *) buffer.pointer;
+ if(!(package) ||
+ (package->type != ACPI_TYPE_PACKAGE) ||
+ !(package->package.elements)) {
+ err("%s: Invalid APCI object\n", __FUNCTION__);
+ goto read_table_done;
+ }
+
+ for(size = 0, i = 0; i < package->package.count; i++) {
+ if (package->package.elements[i].type != ACPI_TYPE_BUFFER) {
+ err("%s: Invalid APCI element %d\n", __FUNCTION__, i);
+ goto read_table_done;
+ }
+ size += package->package.elements[i].buffer.length;
+ }
+
+ if (bufp == NULL)
+ goto read_table_done;
+
+ lbuf = kmalloc(size, GFP_KERNEL);
+ dbg("%s: element count: %i, ASL table size: %i, &table = 0x%p\n",
+ __FUNCTION__, package->package.count, size, lbuf);
+
+ if (lbuf) {
+ *bufp = lbuf;
+ memset(lbuf, 0, size);
+ } else {
+ size = -ENOMEM;
+ goto read_table_done;
+ }
+
+ size = 0;
+ for (i=0; i<package->package.count; i++) {
+ memcpy(&lbuf[size],
+ package->package.elements[i].buffer.pointer,
+ package->package.elements[i].buffer.length);
+ size += package->package.elements[i].buffer.length;
+ }
+
+read_table_done:
+ kfree(buffer.pointer);
+ return size;
+}
+
+/**
+ * ibm_read_apci_table - callback for the sysfs apci_table file
+ * @kobj: the kobject this binary attribute is a part of
+ * @buffer: the kernel space buffer to fill
+ * @pos: the offset into the file
+ * @size: the number of bytes requested
+ *
+ * Description: gets registered with sysfs as the reader callback
+ * to be executed when /sys/bus/pci/slots/apci_table gets read
+ *
+ * Since we don't get notified on open and close for this file,
+ * things get really tricky here...
+ * our solution is to only allow reading the table in all at once
+ **/
+static ssize_t ibm_read_apci_table(struct kobject *kobj,
+ char *buffer, loff_t pos, size_t size)
+{
+ int bytes_read = -EINVAL;
+ char *table = NULL;
+
+ dbg("%s: pos = %d, size = %zd\n", __FUNCTION__, (int)pos, size);
+
+ if (pos == 0) {
+ bytes_read = ibm_get_table_from_acpi(&table);
+ if (bytes_read > 0 && bytes_read <= size)
+ memcpy(buffer, table, bytes_read);
+ kfree(table);
+ }
+ return bytes_read;
+}
+
+/**
+ * ibm_find_acpi_device - callback to find our ACPI device
+ * @handle: the ACPI handle of the device we are inspecting
+ * @lvl: depth into the namespace tree
+ * @context: a pointer to our handle to fill when we find the device
+ * @rv: a return value to fill if desired
+ *
+ * Description: used as a callback when calling acpi_walk_namespace
+ * to find our device. When this method returns non-zero
+ * acpi_walk_namespace quits its search and returns our value
+ **/
+static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
+ u32 lvl, void *context, void **rv)
+{
+ acpi_handle *phandle = (acpi_handle *)context;
+ acpi_status status;
+ struct acpi_device_info info;
+ struct acpi_buffer info_buffer = {
+ .length = sizeof(struct acpi_device_info),
+ .pointer = &info,
+ };
+
+ status = acpi_get_object_info(handle, &info_buffer);
+ if (ACPI_FAILURE(status)) {
+ err("%s: Failed to get device information", __FUNCTION__);
+ return 0;
+ }
+ info.hardware_id.value[sizeof(info.hardware_id.value) - 1] = '\0';
+
+ if(info.current_status && (info.valid & ACPI_VALID_HID) &&
+ (!strcmp(info.hardware_id.value, IBM_HARDWARE_ID1) ||
+ !strcmp(info.hardware_id.value, IBM_HARDWARE_ID2))) {
+ dbg("found hardware: %s, handle: %p\n", info.hardware_id.value,
+ handle);
+ *phandle = handle;
+ /* returning non-zero causes the search to stop
+ * and returns this value to the caller of
+ * acpi_walk_namespace, but it also causes some warnings
+ * in the acpi debug code to print...
+ */
+ return FOUND_APCI;
+ }
+ return 0;
+}
+
+static int __init ibm_acpiphp_init(void)
+{
+ int retval = 0;
+ acpi_status status;
+ struct acpi_device *device;
+ struct kobject *sysdir = &pci_hotplug_slots_subsys.kset.kobj;
+
+ dbg("%s\n", __FUNCTION__);
+
+ if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, ibm_find_acpi_device,
+ &ibm_acpi_handle, NULL) != FOUND_APCI) {
+ err("%s: acpi_walk_namespace failed\n", __FUNCTION__);
+ retval = -ENODEV;
+ goto init_return;
+ }
+ dbg("%s: found IBM aPCI device\n", __FUNCTION__);
+ if (acpi_bus_get_device(ibm_acpi_handle, &device)) {
+ err("%s: acpi_bus_get_device failed\n", __FUNCTION__);
+ retval = -ENODEV;
+ goto init_return;
+ }
+ if (acpiphp_register_attention(&ibm_attention_info)) {
+ retval = -ENODEV;
+ goto init_return;
+ }
+
+ ibm_note.device = device;
+ status = acpi_install_notify_handler(
+ ibm_acpi_handle,
+ ACPI_DEVICE_NOTIFY,
+ ibm_handle_events,
+ &ibm_note);
+ if (ACPI_FAILURE(status)) {
+ err("%s: Failed to register notification handler\n",
+ __FUNCTION__);
+ retval = -EBUSY;
+ goto init_cleanup;
+ }
+
+ ibm_apci_table_attr.size = ibm_get_table_from_acpi(NULL);
+ retval = sysfs_create_bin_file(sysdir, &ibm_apci_table_attr);
+
+ return retval;
+
+init_cleanup:
+ acpiphp_unregister_attention(&ibm_attention_info);
+init_return:
+ return retval;
+}
+
+static void __exit ibm_acpiphp_exit(void)
+{
+ acpi_status status;
+ struct kobject *sysdir = &pci_hotplug_slots_subsys.kset.kobj;
+
+ dbg("%s\n", __FUNCTION__);
+
+ if (acpiphp_unregister_attention(&ibm_attention_info))
+ err("%s: attention info deregistration failed", __FUNCTION__);
+
+ status = acpi_remove_notify_handler(
+ ibm_acpi_handle,
+ ACPI_DEVICE_NOTIFY,
+ ibm_handle_events);
+ if (ACPI_FAILURE(status))
+ err("%s: Notification handler removal failed\n",
+ __FUNCTION__);
+ // remove the /sys entries
+ if (sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr))
+ err("%s: removal of sysfs file apci_table failed\n",
+ __FUNCTION__);
+}
+
+module_init(ibm_acpiphp_init);
+module_exit(ibm_acpiphp_exit);
diff --git a/drivers/pci/hotplug/acpiphp_pci.c b/drivers/pci/hotplug/acpiphp_pci.c
new file mode 100644
index 000000000000..54d97c9d1dff
--- /dev/null
+++ b/drivers/pci/hotplug/acpiphp_pci.c
@@ -0,0 +1,449 @@
+/*
+ * ACPI PCI HotPlug PCI configuration space management
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2002 IBM Corp.
+ * Copyright (C) 2002 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
+ * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
+ * Copyright (C) 2002 NEC Corporation
+ *
+ * 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 <t-kochi@bq.jp.nec.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include "../pci.h"
+#include "pci_hotplug.h"
+#include "acpiphp.h"
+
+#define MY_NAME "acpiphp_pci"
+
+
+/* allocate mem/pmem/io resource to a new function */
+static int init_config_space (struct acpiphp_func *func)
+{
+ u32 bar, len;
+ 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 acpiphp_bridge *bridge;
+ struct pci_resource *res;
+ struct pci_bus *pbus;
+ int bus, device, function;
+ unsigned int devfn;
+ u16 tmp;
+
+ bridge = func->slot->bridge;
+ pbus = bridge->pci_bus;
+ bus = bridge->bus;
+ device = func->slot->device;
+ function = func->function;
+ devfn = PCI_DEVFN(device, function);
+
+ for (count = 0; address[count]; count++) { /* for 6 BARs */
+ pci_bus_write_config_dword(pbus, devfn,
+ address[count], 0xFFFFFFFF);
+ pci_bus_read_config_dword(pbus, devfn, address[count], &bar);
+
+ if (!bar) /* This BAR is not implemented */
+ continue;
+
+ dbg("Device %02x.%02x BAR %d wants %x\n", device, function, count, bar);
+
+ if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
+ /* This is IO */
+
+ len = bar & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF);
+ len = len & ~(len - 1);
+
+ dbg("len in IO %x, BAR %d\n", len, count);
+
+ spin_lock(&bridge->res_lock);
+ res = acpiphp_get_io_resource(&bridge->io_head, len);
+ spin_unlock(&bridge->res_lock);
+
+ if (!res) {
+ err("cannot allocate requested io for %02x:%02x.%d len %x\n",
+ bus, device, function, len);
+ return -1;
+ }
+ pci_bus_write_config_dword(pbus, devfn,
+ address[count],
+ (u32)res->base);
+ res->next = func->io_head;
+ func->io_head = res;
+
+ } else {
+ /* This is Memory */
+ if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+ /* pfmem */
+
+ len = bar & 0xFFFFFFF0;
+ len = ~len + 1;
+
+ dbg("len in PFMEM %x, BAR %d\n", len, count);
+
+ spin_lock(&bridge->res_lock);
+ res = acpiphp_get_resource(&bridge->p_mem_head, len);
+ spin_unlock(&bridge->res_lock);
+
+ if (!res) {
+ err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n",
+ bus, device, function, len);
+ return -1;
+ }
+
+ pci_bus_write_config_dword(pbus, devfn,
+ address[count],
+ (u32)res->base);
+
+ if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */
+ dbg("inside the pfmem 64 case, count %d\n", count);
+ count += 1;
+ pci_bus_write_config_dword(pbus, devfn,
+ address[count],
+ (u32)(res->base >> 32));
+ }
+
+ res->next = func->p_mem_head;
+ func->p_mem_head = res;
+
+ } else {
+ /* regular memory */
+
+ len = bar & 0xFFFFFFF0;
+ len = ~len + 1;
+
+ dbg("len in MEM %x, BAR %d\n", len, count);
+
+ spin_lock(&bridge->res_lock);
+ res = acpiphp_get_resource(&bridge->mem_head, len);
+ spin_unlock(&bridge->res_lock);
+
+ if (!res) {
+ err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n",
+ bus, device, function, len);
+ return -1;
+ }
+
+ pci_bus_write_config_dword(pbus, devfn,
+ address[count],
+ (u32)res->base);
+
+ if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ /* takes up another dword */
+ dbg("inside mem 64 case, reg. mem, count %d\n", count);
+ count += 1;
+ pci_bus_write_config_dword(pbus, devfn,
+ address[count],
+ (u32)(res->base >> 32));
+ }
+
+ res->next = func->mem_head;
+ func->mem_head = res;
+
+ }
+ }
+ }
+
+ /* disable expansion rom */
+ pci_bus_write_config_dword(pbus, devfn, PCI_ROM_ADDRESS, 0x00000000);
+
+ /* set PCI parameters from _HPP */
+ pci_bus_write_config_byte(pbus, devfn, PCI_CACHE_LINE_SIZE,
+ bridge->hpp.cache_line_size);
+ pci_bus_write_config_byte(pbus, devfn, PCI_LATENCY_TIMER,
+ bridge->hpp.latency_timer);
+
+ pci_bus_read_config_word(pbus, devfn, PCI_COMMAND, &tmp);
+ if (bridge->hpp.enable_SERR)
+ tmp |= PCI_COMMAND_SERR;
+ if (bridge->hpp.enable_PERR)
+ tmp |= PCI_COMMAND_PARITY;
+ pci_bus_write_config_word(pbus, devfn, PCI_COMMAND, tmp);
+
+ return 0;
+}
+
+/* detect_used_resource - subtract resource under dev from bridge */
+static int detect_used_resource (struct acpiphp_bridge *bridge, struct pci_dev *dev)
+{
+ int count;
+
+ dbg("Device %s\n", pci_name(dev));
+
+ for (count = 0; count < DEVICE_COUNT_RESOURCE; count++) {
+ struct pci_resource *res;
+ struct pci_resource **head;
+ unsigned long base = dev->resource[count].start;
+ unsigned long len = dev->resource[count].end - base + 1;
+ unsigned long flags = dev->resource[count].flags;
+
+ if (!flags)
+ continue;
+
+ dbg("BAR[%d] 0x%lx - 0x%lx (0x%lx)\n", count, base,
+ base + len - 1, flags);
+
+ if (flags & IORESOURCE_IO) {
+ head = &bridge->io_head;
+ } else if (flags & IORESOURCE_PREFETCH) {
+ head = &bridge->p_mem_head;
+ } else {
+ head = &bridge->mem_head;
+ }
+
+ spin_lock(&bridge->res_lock);
+ res = acpiphp_get_resource_with_base(head, base, len);
+ spin_unlock(&bridge->res_lock);
+ if (res)
+ kfree(res);
+ }
+
+ return 0;
+}
+
+
+/**
+ * acpiphp_detect_pci_resource - detect resources under bridge
+ * @bridge: detect all resources already used under this bridge
+ *
+ * collect all resources already allocated for all devices under a bridge.
+ */
+int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge)
+{
+ struct list_head *l;
+ struct pci_dev *dev;
+
+ list_for_each (l, &bridge->pci_bus->devices) {
+ dev = pci_dev_b(l);
+ detect_used_resource(bridge, dev);
+ }
+
+ return 0;
+}
+
+
+/**
+ * acpiphp_init_slot_resource - gather resource usage information of a slot
+ * @slot: ACPI slot object to be checked, should have valid pci_dev member
+ *
+ * TBD: PCI-to-PCI bridge case
+ * use pci_dev->resource[]
+ */
+int acpiphp_init_func_resource (struct acpiphp_func *func)
+{
+ u64 base;
+ u32 bar, len;
+ 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 pci_resource *res;
+ struct pci_dev *dev;
+
+ dev = func->pci_dev;
+ dbg("Hot-pluggable device %s\n", pci_name(dev));
+
+ for (count = 0; address[count]; count++) { /* for 6 BARs */
+ pci_read_config_dword(dev, address[count], &bar);
+
+ if (!bar) /* This BAR is not implemented */
+ continue;
+
+ pci_write_config_dword(dev, address[count], 0xFFFFFFFF);
+ pci_read_config_dword(dev, address[count], &len);
+
+ if (len & PCI_BASE_ADDRESS_SPACE_IO) {
+ /* This is IO */
+ base = bar & 0xFFFFFFFC;
+ len = len & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF);
+ len = len & ~(len - 1);
+
+ dbg("BAR[%d] %08x - %08x (IO)\n", count, (u32)base, (u32)base + len - 1);
+
+ res = acpiphp_make_resource(base, len);
+ if (!res)
+ goto no_memory;
+
+ res->next = func->io_head;
+ func->io_head = res;
+
+ } else {
+ /* This is Memory */
+ base = bar & 0xFFFFFFF0;
+ if (len & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+ /* pfmem */
+
+ len &= 0xFFFFFFF0;
+ len = ~len + 1;
+
+ if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */
+ dbg("prefetch mem 64\n");
+ count += 1;
+ }
+ dbg("BAR[%d] %08x - %08x (PMEM)\n", count, (u32)base, (u32)base + len - 1);
+ res = acpiphp_make_resource(base, len);
+ if (!res)
+ goto no_memory;
+
+ res->next = func->p_mem_head;
+ func->p_mem_head = res;
+
+ } else {
+ /* regular memory */
+
+ len &= 0xFFFFFFF0;
+ len = ~len + 1;
+
+ if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ /* takes up another dword */
+ dbg("mem 64\n");
+ count += 1;
+ }
+ dbg("BAR[%d] %08x - %08x (MEM)\n", count, (u32)base, (u32)base + len - 1);
+ res = acpiphp_make_resource(base, len);
+ if (!res)
+ goto no_memory;
+
+ res->next = func->mem_head;
+ func->mem_head = res;
+
+ }
+ }
+
+ pci_write_config_dword(dev, address[count], bar);
+ }
+#if 1
+ acpiphp_dump_func_resource(func);
+#endif
+
+ return 0;
+
+ no_memory:
+ err("out of memory\n");
+ acpiphp_free_resource(&func->io_head);
+ acpiphp_free_resource(&func->mem_head);
+ acpiphp_free_resource(&func->p_mem_head);
+
+ return -1;
+}
+
+
+/**
+ * acpiphp_configure_slot - allocate PCI resources
+ * @slot: slot to be configured
+ *
+ * initializes a PCI functions on a device inserted
+ * into the slot
+ *
+ */
+int acpiphp_configure_slot (struct acpiphp_slot *slot)
+{
+ struct acpiphp_func *func;
+ struct list_head *l;
+ u8 hdr;
+ u32 dvid;
+ int retval = 0;
+ int is_multi = 0;
+
+ pci_bus_read_config_byte(slot->bridge->pci_bus,
+ PCI_DEVFN(slot->device, 0),
+ PCI_HEADER_TYPE, &hdr);
+
+ if (hdr & 0x80)
+ is_multi = 1;
+
+ list_for_each (l, &slot->funcs) {
+ func = list_entry(l, struct acpiphp_func, sibling);
+ if (is_multi || func->function == 0) {
+ pci_bus_read_config_dword(slot->bridge->pci_bus,
+ PCI_DEVFN(slot->device,
+ func->function),
+ PCI_VENDOR_ID, &dvid);
+ if (dvid != 0xffffffff) {
+ retval = init_config_space(func);
+ if (retval)
+ break;
+ }
+ }
+ }
+
+ return retval;
+}
+
+/**
+ * acpiphp_configure_function - configure PCI function
+ * @func: function to be configured
+ *
+ * initializes a PCI functions on a device inserted
+ * into the slot
+ *
+ */
+int acpiphp_configure_function (struct acpiphp_func *func)
+{
+ /* all handled by the pci core now */
+ return 0;
+}
+
+/**
+ * acpiphp_unconfigure_function - unconfigure PCI function
+ * @func: function to be unconfigured
+ *
+ */
+void acpiphp_unconfigure_function (struct acpiphp_func *func)
+{
+ struct acpiphp_bridge *bridge;
+
+ /* if pci_dev is NULL, ignore it */
+ if (!func->pci_dev)
+ return;
+
+ pci_remove_bus_device(func->pci_dev);
+
+ /* free all resources */
+ bridge = func->slot->bridge;
+
+ spin_lock(&bridge->res_lock);
+ acpiphp_move_resource(&func->io_head, &bridge->io_head);
+ acpiphp_move_resource(&func->mem_head, &bridge->mem_head);
+ acpiphp_move_resource(&func->p_mem_head, &bridge->p_mem_head);
+ acpiphp_move_resource(&func->bus_head, &bridge->bus_head);
+ spin_unlock(&bridge->res_lock);
+}
diff --git a/drivers/pci/hotplug/acpiphp_res.c b/drivers/pci/hotplug/acpiphp_res.c
new file mode 100644
index 000000000000..f54b1fa7b75a
--- /dev/null
+++ b/drivers/pci/hotplug/acpiphp_res.c
@@ -0,0 +1,700 @@
+/*
+ * ACPI PCI HotPlug Utility functions
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
+ * Copyright (C) 2002 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
+ * Copyright (C) 2002 NEC Corporation
+ *
+ * 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>, <t-kochi@bq.jp.nec.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/sysctl.h>
+#include <linux/pci.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+
+#include <linux/ioctl.h>
+#include <linux/fcntl.h>
+
+#include <linux/list.h>
+
+#include "pci_hotplug.h"
+#include "acpiphp.h"
+
+#define MY_NAME "acpiphp_res"
+
+
+/*
+ * sort_by_size - sort nodes by their length, smallest first
+ */
+static int sort_by_size(struct pci_resource **head)
+{
+ struct pci_resource *current_res;
+ struct pci_resource *next_res;
+ int out_of_order = 1;
+
+ if (!(*head))
+ return 1;
+
+ if (!((*head)->next))
+ return 0;
+
+ while (out_of_order) {
+ out_of_order = 0;
+
+ /* Special case for swapping list head */
+ if (((*head)->next) &&
+ ((*head)->length > (*head)->next->length)) {
+ out_of_order++;
+ current_res = *head;
+ *head = (*head)->next;
+ current_res->next = (*head)->next;
+ (*head)->next = current_res;
+ }
+
+ current_res = *head;
+
+ while (current_res->next && current_res->next->next) {
+ if (current_res->next->length > current_res->next->next->length) {
+ out_of_order++;
+ next_res = current_res->next;
+ current_res->next = current_res->next->next;
+ current_res = current_res->next;
+ next_res->next = current_res->next;
+ current_res->next = next_res;
+ } else
+ current_res = current_res->next;
+ }
+ } /* End of out_of_order loop */
+
+ return 0;
+}
+
+#if 0
+/*
+ * sort_by_max_size - sort nodes by their length, largest first
+ */
+static int sort_by_max_size(struct pci_resource **head)
+{
+ struct pci_resource *current_res;
+ struct pci_resource *next_res;
+ int out_of_order = 1;
+
+ if (!(*head))
+ return 1;
+
+ if (!((*head)->next))
+ return 0;
+
+ while (out_of_order) {
+ out_of_order = 0;
+
+ /* Special case for swapping list head */
+ if (((*head)->next) &&
+ ((*head)->length < (*head)->next->length)) {
+ out_of_order++;
+ current_res = *head;
+ *head = (*head)->next;
+ current_res->next = (*head)->next;
+ (*head)->next = current_res;
+ }
+
+ current_res = *head;
+
+ while (current_res->next && current_res->next->next) {
+ if (current_res->next->length < current_res->next->next->length) {
+ out_of_order++;
+ next_res = current_res->next;
+ current_res->next = current_res->next->next;
+ current_res = current_res->next;
+ next_res->next = current_res->next;
+ current_res->next = next_res;
+ } else
+ current_res = current_res->next;
+ }
+ } /* End of out_of_order loop */
+
+ return 0;
+}
+#endif
+
+/**
+ * get_io_resource - get resource for I/O ports
+ *
+ * this function sorts the resource list by size and then
+ * returns the first node of "size" length that is not in the
+ * ISA aliasing window. If it finds a node larger than "size"
+ * it will split it up.
+ *
+ * size must be a power of two.
+ *
+ * difference from get_resource is handling of ISA aliasing space.
+ *
+ */
+struct pci_resource *acpiphp_get_io_resource (struct pci_resource **head, u32 size)
+{
+ struct pci_resource *prevnode;
+ struct pci_resource *node;
+ struct pci_resource *split_node;
+ u64 temp_qword;
+
+ if (!(*head))
+ return NULL;
+
+ if (acpiphp_resource_sort_and_combine(head))
+ return NULL;
+
+ if (sort_by_size(head))
+ return NULL;
+
+ for (node = *head; node; node = node->next) {
+ if (node->length < size)
+ continue;
+
+ if (node->base & (size - 1)) {
+ /* this one isn't base aligned properly
+ so we'll make a new entry and split it up */
+ temp_qword = (node->base | (size-1)) + 1;
+
+ /* Short circuit if adjusted size is too small */
+ if ((node->length - (temp_qword - node->base)) < size)
+ continue;
+
+ split_node = acpiphp_make_resource(node->base, temp_qword - node->base);
+
+ if (!split_node)
+ return NULL;
+
+ node->base = temp_qword;
+ node->length -= split_node->length;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of non-aligned base */
+
+ /* Don't need to check if too small since we already did */
+ if (node->length > size) {
+ /* this one is longer than we need
+ so we'll make a new entry and split it up */
+ split_node = acpiphp_make_resource(node->base + size, node->length - size);
+
+ if (!split_node)
+ return NULL;
+
+ node->length = size;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of too big on top end */
+
+ /* For IO make sure it's not in the ISA aliasing space */
+ if ((node->base & 0x300L) && !(node->base & 0xfffff000))
+ continue;
+
+ /* If we got here, then it is the right size
+ Now take it out of the list */
+ if (*head == node) {
+ *head = node->next;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ }
+ node->next = NULL;
+ /* Stop looping */
+ break;
+ }
+
+ return node;
+}
+
+
+#if 0
+/**
+ * get_max_resource - get the largest resource
+ *
+ * Gets the largest node that is at least "size" big from the
+ * list pointed to by head. It aligns the node on top and bottom
+ * to "size" alignment before returning it.
+ */
+static struct pci_resource *acpiphp_get_max_resource (struct pci_resource **head, u32 size)
+{
+ struct pci_resource *max;
+ struct pci_resource *temp;
+ struct pci_resource *split_node;
+ u64 temp_qword;
+
+ if (!(*head))
+ return NULL;
+
+ if (acpiphp_resource_sort_and_combine(head))
+ return NULL;
+
+ if (sort_by_max_size(head))
+ return NULL;
+
+ for (max = *head;max; max = max->next) {
+
+ /* If not big enough we could probably just bail,
+ instead we'll continue to the next. */
+ if (max->length < size)
+ continue;
+
+ if (max->base & (size - 1)) {
+ /* this one isn't base aligned properly
+ so we'll make a new entry and split it up */
+ temp_qword = (max->base | (size-1)) + 1;
+
+ /* Short circuit if adjusted size is too small */
+ if ((max->length - (temp_qword - max->base)) < size)
+ continue;
+
+ split_node = acpiphp_make_resource(max->base, temp_qword - max->base);
+
+ if (!split_node)
+ return NULL;
+
+ max->base = temp_qword;
+ max->length -= split_node->length;
+
+ /* Put it next in the list */
+ split_node->next = max->next;
+ max->next = split_node;
+ }
+
+ if ((max->base + max->length) & (size - 1)) {
+ /* this one isn't end aligned properly at the top
+ so we'll make a new entry and split it up */
+ temp_qword = ((max->base + max->length) & ~(size - 1));
+
+ split_node = acpiphp_make_resource(temp_qword,
+ max->length + max->base - temp_qword);
+
+ if (!split_node)
+ return NULL;
+
+ max->length -= split_node->length;
+
+ /* Put it in the list */
+ split_node->next = max->next;
+ max->next = split_node;
+ }
+
+ /* Make sure it didn't shrink too much when we aligned it */
+ if (max->length < size)
+ continue;
+
+ /* Now take it out of the list */
+ temp = (struct pci_resource*) *head;
+ if (temp == max) {
+ *head = max->next;
+ } else {
+ while (temp && temp->next != max) {
+ temp = temp->next;
+ }
+
+ temp->next = max->next;
+ }
+
+ max->next = NULL;
+ return max;
+ }
+
+ /* If we get here, we couldn't find one */
+ return NULL;
+}
+#endif
+
+/**
+ * get_resource - get resource (mem, pfmem)
+ *
+ * this function sorts the resource list by size and then
+ * returns the first node of "size" length. If it finds a node
+ * larger than "size" it will split it up.
+ *
+ * size must be a power of two.
+ *
+ */
+struct pci_resource *acpiphp_get_resource (struct pci_resource **head, u32 size)
+{
+ struct pci_resource *prevnode;
+ struct pci_resource *node;
+ struct pci_resource *split_node;
+ u64 temp_qword;
+
+ if (!(*head))
+ return NULL;
+
+ if (acpiphp_resource_sort_and_combine(head))
+ return NULL;
+
+ if (sort_by_size(head))
+ return NULL;
+
+ for (node = *head; node; node = node->next) {
+ dbg("%s: req_size =%x node=%p, base=%x, length=%x\n",
+ __FUNCTION__, size, node, (u32)node->base, node->length);
+ if (node->length < size)
+ continue;
+
+ if (node->base & (size - 1)) {
+ dbg("%s: not aligned\n", __FUNCTION__);
+ /* this one isn't base aligned properly
+ so we'll make a new entry and split it up */
+ temp_qword = (node->base | (size-1)) + 1;
+
+ /* Short circuit if adjusted size is too small */
+ if ((node->length - (temp_qword - node->base)) < size)
+ continue;
+
+ split_node = acpiphp_make_resource(node->base, temp_qword - node->base);
+
+ if (!split_node)
+ return NULL;
+
+ node->base = temp_qword;
+ node->length -= split_node->length;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of non-aligned base */
+
+ /* Don't need to check if too small since we already did */
+ if (node->length > size) {
+ dbg("%s: too big\n", __FUNCTION__);
+ /* this one is longer than we need
+ so we'll make a new entry and split it up */
+ split_node = acpiphp_make_resource(node->base + size, node->length - size);
+
+ if (!split_node)
+ return NULL;
+
+ node->length = size;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of too big on top end */
+
+ dbg("%s: got one!!!\n", __FUNCTION__);
+ /* If we got here, then it is the right size
+ Now take it out of the list */
+ if (*head == node) {
+ *head = node->next;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ }
+ node->next = NULL;
+ /* Stop looping */
+ break;
+ }
+ return node;
+}
+
+/**
+ * get_resource_with_base - get resource with specific base address
+ *
+ * this function
+ * returns the first node of "size" length located at specified base address.
+ * If it finds a node larger than "size" it will split it up.
+ *
+ * size must be a power of two.
+ *
+ */
+struct pci_resource *acpiphp_get_resource_with_base (struct pci_resource **head, u64 base, u32 size)
+{
+ struct pci_resource *prevnode;
+ struct pci_resource *node;
+ struct pci_resource *split_node;
+ u64 temp_qword;
+
+ if (!(*head))
+ return NULL;
+
+ if (acpiphp_resource_sort_and_combine(head))
+ return NULL;
+
+ for (node = *head; node; node = node->next) {
+ dbg(": 1st req_base=%x req_size =%x node=%p, base=%x, length=%x\n",
+ (u32)base, size, node, (u32)node->base, node->length);
+ if (node->base > base)
+ continue;
+
+ if ((node->base + node->length) < (base + size))
+ continue;
+
+ if (node->base < base) {
+ dbg(": split 1\n");
+ /* this one isn't base aligned properly
+ so we'll make a new entry and split it up */
+ temp_qword = base;
+
+ /* Short circuit if adjusted size is too small */
+ if ((node->length - (temp_qword - node->base)) < size)
+ continue;
+
+ split_node = acpiphp_make_resource(node->base, temp_qword - node->base);
+
+ if (!split_node)
+ return NULL;
+
+ node->base = temp_qword;
+ node->length -= split_node->length;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ }
+
+ dbg(": 2nd req_base=%x req_size =%x node=%p, base=%x, length=%x\n",
+ (u32)base, size, node, (u32)node->base, node->length);
+
+ /* Don't need to check if too small since we already did */
+ if (node->length > size) {
+ dbg(": split 2\n");
+ /* this one is longer than we need
+ so we'll make a new entry and split it up */
+ split_node = acpiphp_make_resource(node->base + size, node->length - size);
+
+ if (!split_node)
+ return NULL;
+
+ node->length = size;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of too big on top end */
+
+ dbg(": got one!!!\n");
+ /* If we got here, then it is the right size
+ Now take it out of the list */
+ if (*head == node) {
+ *head = node->next;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ }
+ node->next = NULL;
+ /* Stop looping */
+ break;
+ }
+ return node;
+}
+
+
+/**
+ * acpiphp_resource_sort_and_combine
+ *
+ * Sorts all of the nodes in the list in ascending order by
+ * their base addresses. Also does garbage collection by
+ * combining adjacent nodes.
+ *
+ * returns 0 if success
+ */
+int acpiphp_resource_sort_and_combine (struct pci_resource **head)
+{
+ struct pci_resource *node1;
+ struct pci_resource *node2;
+ int out_of_order = 1;
+
+ if (!(*head))
+ return 1;
+
+ dbg("*head->next = %p\n",(*head)->next);
+
+ if (!(*head)->next)
+ return 0; /* only one item on the list, already sorted! */
+
+ dbg("*head->base = 0x%x\n",(u32)(*head)->base);
+ dbg("*head->next->base = 0x%x\n", (u32)(*head)->next->base);
+ while (out_of_order) {
+ out_of_order = 0;
+
+ /* Special case for swapping list head */
+ if (((*head)->next) &&
+ ((*head)->base > (*head)->next->base)) {
+ node1 = *head;
+ (*head) = (*head)->next;
+ node1->next = (*head)->next;
+ (*head)->next = node1;
+ out_of_order++;
+ }
+
+ node1 = (*head);
+
+ while (node1->next && node1->next->next) {
+ if (node1->next->base > node1->next->next->base) {
+ out_of_order++;
+ node2 = node1->next;
+ node1->next = node1->next->next;
+ node1 = node1->next;
+ node2->next = node1->next;
+ node1->next = node2;
+ } else
+ node1 = node1->next;
+ }
+ } /* End of out_of_order loop */
+
+ node1 = *head;
+
+ while (node1 && node1->next) {
+ if ((node1->base + node1->length) == node1->next->base) {
+ /* Combine */
+ dbg("8..\n");
+ node1->length += node1->next->length;
+ node2 = node1->next;
+ node1->next = node1->next->next;
+ kfree(node2);
+ } else
+ node1 = node1->next;
+ }
+
+ return 0;
+}
+
+
+/**
+ * acpiphp_make_resource - make resource structure
+ * @base: base address of a resource
+ * @length: length of a resource
+ */
+struct pci_resource *acpiphp_make_resource (u64 base, u32 length)
+{
+ struct pci_resource *res;
+
+ res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (res) {
+ memset(res, 0, sizeof(struct pci_resource));
+ res->base = base;
+ res->length = length;
+ }
+
+ return res;
+}
+
+
+/**
+ * acpiphp_move_resource - move linked resources from one to another
+ * @from: head of linked resource list
+ * @to: head of linked resource list
+ */
+void acpiphp_move_resource (struct pci_resource **from, struct pci_resource **to)
+{
+ struct pci_resource *tmp;
+
+ while (*from) {
+ tmp = (*from)->next;
+ (*from)->next = *to;
+ *to = *from;
+ *from = tmp;
+ }
+
+ /* *from = NULL is guaranteed */
+}
+
+
+/**
+ * acpiphp_free_resource - free all linked resources
+ * @res: head of linked resource list
+ */
+void acpiphp_free_resource (struct pci_resource **res)
+{
+ struct pci_resource *tmp;
+
+ while (*res) {
+ tmp = (*res)->next;
+ kfree(*res);
+ *res = tmp;
+ }
+
+ /* *res = NULL is guaranteed */
+}
+
+
+/* debug support functions; will go away sometime :) */
+static void dump_resource(struct pci_resource *head)
+{
+ struct pci_resource *p;
+ int cnt;
+
+ p = head;
+ cnt = 0;
+
+ while (p) {
+ dbg("[%02d] %08x - %08x\n",
+ cnt++, (u32)p->base, (u32)p->base + p->length - 1);
+ p = p->next;
+ }
+}
+
+void acpiphp_dump_resource(struct acpiphp_bridge *bridge)
+{
+ dbg("I/O resource:\n");
+ dump_resource(bridge->io_head);
+ dbg("MEM resource:\n");
+ dump_resource(bridge->mem_head);
+ dbg("PMEM resource:\n");
+ dump_resource(bridge->p_mem_head);
+ dbg("BUS resource:\n");
+ dump_resource(bridge->bus_head);
+}
+
+void acpiphp_dump_func_resource(struct acpiphp_func *func)
+{
+ dbg("I/O resource:\n");
+ dump_resource(func->io_head);
+ dbg("MEM resource:\n");
+ dump_resource(func->mem_head);
+ dbg("PMEM resource:\n");
+ dump_resource(func->p_mem_head);
+ dbg("BUS resource:\n");
+ dump_resource(func->bus_head);
+}
diff --git a/drivers/pci/hotplug/cpci_hotplug.h b/drivers/pci/hotplug/cpci_hotplug.h
new file mode 100644
index 000000000000..3ddd75937a40
--- /dev/null
+++ b/drivers/pci/hotplug/cpci_hotplug.h
@@ -0,0 +1,96 @@
+/*
+ * CompactPCI Hot Plug Core Functions
+ *
+ * Copyright (C) 2002 SOMA Networks, Inc.
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 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 <scottm@somanetworks.com>
+ */
+
+#ifndef _CPCI_HOTPLUG_H
+#define _CPCI_HOTPLUG_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+
+/* PICMG 2.12 R2.0 HS CSR bits: */
+#define HS_CSR_INS 0x0080
+#define HS_CSR_EXT 0x0040
+#define HS_CSR_PI 0x0030
+#define HS_CSR_LOO 0x0008
+#define HS_CSR_PIE 0x0004
+#define HS_CSR_EIM 0x0002
+#define HS_CSR_DHA 0x0001
+
+struct slot {
+ u8 number;
+ unsigned int devfn;
+ struct pci_bus *bus;
+ struct pci_dev *dev;
+ unsigned int extracting;
+ struct hotplug_slot *hotplug_slot;
+ struct list_head slot_list;
+};
+
+struct cpci_hp_controller_ops {
+ int (*query_enum) (void);
+ int (*enable_irq) (void);
+ int (*disable_irq) (void);
+ int (*check_irq) (void *dev_id);
+ int (*hardware_test) (struct slot* slot, u32 value);
+ u8 (*get_power) (struct slot* slot);
+ int (*set_power) (struct slot* slot, int value);
+};
+
+struct cpci_hp_controller {
+ unsigned int irq;
+ unsigned long irq_flags;
+ char *devname;
+ void *dev_id;
+ char *name;
+ struct cpci_hp_controller_ops *ops;
+};
+
+extern int cpci_hp_register_controller(struct cpci_hp_controller *controller);
+extern int cpci_hp_unregister_controller(struct cpci_hp_controller *controller);
+extern int cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last);
+extern int cpci_hp_unregister_bus(struct pci_bus *bus);
+extern int cpci_hp_start(void);
+extern int cpci_hp_stop(void);
+
+/*
+ * Internal function prototypes, these functions should not be used by
+ * board/chassis drivers.
+ */
+extern u8 cpci_get_attention_status(struct slot *slot);
+extern u8 cpci_get_latch_status(struct slot *slot);
+extern u8 cpci_get_adapter_status(struct slot *slot);
+extern u16 cpci_get_hs_csr(struct slot * slot);
+extern int cpci_set_attention_status(struct slot *slot, int status);
+extern int cpci_check_and_clear_ins(struct slot * slot);
+extern int cpci_check_ext(struct slot * slot);
+extern int cpci_clear_ext(struct slot * slot);
+extern int cpci_led_on(struct slot * slot);
+extern int cpci_led_off(struct slot * slot);
+extern int cpci_configure_slot(struct slot *slot);
+extern int cpci_unconfigure_slot(struct slot *slot);
+
+#endif /* _CPCI_HOTPLUG_H */
diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c
new file mode 100644
index 000000000000..ed243605dc7b
--- /dev/null
+++ b/drivers/pci/hotplug/cpci_hotplug_core.c
@@ -0,0 +1,792 @@
+/*
+ * CompactPCI Hot Plug Driver
+ *
+ * Copyright (C) 2002 SOMA Networks, Inc.
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 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 <scottm@somanetworks.com>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+#include <linux/delay.h>
+#include "pci_hotplug.h"
+#include "cpci_hotplug.h"
+
+#define DRIVER_VERSION "0.2"
+#define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>"
+#define DRIVER_DESC "CompactPCI Hot Plug Core"
+
+#define MY_NAME "cpci_hotplug"
+
+#define dbg(format, arg...) \
+ do { \
+ if(cpci_debug) \
+ printk (KERN_DEBUG "%s: " format "\n", \
+ MY_NAME , ## arg); \
+ } while(0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
+
+/* local variables */
+static spinlock_t list_lock;
+static LIST_HEAD(slot_list);
+static int slots;
+int cpci_debug;
+static struct cpci_hp_controller *controller;
+static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */
+static struct semaphore thread_exit; /* guard ensure thread has exited before calling it quits */
+static int thread_finished = 1;
+
+static int enable_slot(struct hotplug_slot *slot);
+static int disable_slot(struct hotplug_slot *slot);
+static int set_attention_status(struct hotplug_slot *slot, u8 value);
+static int get_power_status(struct hotplug_slot *slot, u8 * value);
+static int get_attention_status(struct hotplug_slot *slot, u8 * value);
+
+static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .enable_slot = enable_slot,
+ .disable_slot = disable_slot,
+ .set_attention_status = set_attention_status,
+ .get_power_status = get_power_status,
+ .get_attention_status = get_attention_status,
+};
+
+static int
+update_latch_status(struct hotplug_slot *hotplug_slot, u8 value)
+{
+ struct hotplug_slot_info info;
+
+ memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
+ info.latch_status = value;
+ return pci_hp_change_slot_info(hotplug_slot, &info);
+}
+
+static int
+update_adapter_status(struct hotplug_slot *hotplug_slot, u8 value)
+{
+ struct hotplug_slot_info info;
+
+ memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
+ info.adapter_status = value;
+ return pci_hp_change_slot_info(hotplug_slot, &info);
+}
+
+static int
+enable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval = 0;
+
+ dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
+
+ if(controller->ops->set_power) {
+ retval = controller->ops->set_power(slot, 1);
+ }
+
+ return retval;
+}
+
+static int
+disable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval = 0;
+
+ dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
+
+ /* Unconfigure device */
+ dbg("%s - unconfiguring slot %s",
+ __FUNCTION__, slot->hotplug_slot->name);
+ if((retval = cpci_unconfigure_slot(slot))) {
+ err("%s - could not unconfigure slot %s",
+ __FUNCTION__, slot->hotplug_slot->name);
+ return retval;
+ }
+ dbg("%s - finished unconfiguring slot %s",
+ __FUNCTION__, slot->hotplug_slot->name);
+
+ /* Clear EXT (by setting it) */
+ if(cpci_clear_ext(slot)) {
+ err("%s - could not clear EXT for slot %s",
+ __FUNCTION__, slot->hotplug_slot->name);
+ retval = -ENODEV;
+ }
+ cpci_led_on(slot);
+
+ if(controller->ops->set_power) {
+ retval = controller->ops->set_power(slot, 0);
+ }
+
+ if(update_adapter_status(slot->hotplug_slot, 0)) {
+ warn("failure to update adapter file");
+ }
+
+ slot->extracting = 0;
+
+ return retval;
+}
+
+static u8
+cpci_get_power_status(struct slot *slot)
+{
+ u8 power = 1;
+
+ if(controller->ops->get_power) {
+ power = controller->ops->get_power(slot);
+ }
+ return power;
+}
+
+static int
+get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ *value = cpci_get_power_status(slot);
+ return 0;
+}
+
+static int
+get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ *value = cpci_get_attention_status(slot);
+ return 0;
+}
+
+static int
+set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
+{
+ return cpci_set_attention_status(hotplug_slot->private, status);
+}
+
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ kfree(slot->hotplug_slot->info);
+ kfree(slot->hotplug_slot->name);
+ kfree(slot->hotplug_slot);
+ kfree(slot);
+}
+
+#define SLOT_NAME_SIZE 6
+static void
+make_slot_name(struct slot *slot)
+{
+ snprintf(slot->hotplug_slot->name,
+ SLOT_NAME_SIZE, "%02x:%02x", slot->bus->number, slot->number);
+}
+
+int
+cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
+{
+ struct slot *slot;
+ struct hotplug_slot *hotplug_slot;
+ struct hotplug_slot_info *info;
+ char *name;
+ int status = -ENOMEM;
+ int i;
+
+ if(!(controller && bus)) {
+ return -ENODEV;
+ }
+
+ /*
+ * Create a structure for each slot, and register that slot
+ * with the pci_hotplug subsystem.
+ */
+ for (i = first; i <= last; ++i) {
+ slot = kmalloc(sizeof (struct slot), GFP_KERNEL);
+ if (!slot)
+ goto error;
+ memset(slot, 0, sizeof (struct slot));
+
+ hotplug_slot =
+ kmalloc(sizeof (struct hotplug_slot), GFP_KERNEL);
+ if (!hotplug_slot)
+ goto error_slot;
+ memset(hotplug_slot, 0, sizeof (struct hotplug_slot));
+ slot->hotplug_slot = hotplug_slot;
+
+ info = kmalloc(sizeof (struct hotplug_slot_info), GFP_KERNEL);
+ if (!info)
+ goto error_hpslot;
+ memset(info, 0, sizeof (struct hotplug_slot_info));
+ hotplug_slot->info = info;
+
+ name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
+ if (!name)
+ goto error_info;
+ hotplug_slot->name = name;
+
+ slot->bus = bus;
+ slot->number = i;
+ slot->devfn = PCI_DEVFN(i, 0);
+
+ hotplug_slot->private = slot;
+ hotplug_slot->release = &release_slot;
+ make_slot_name(slot);
+ hotplug_slot->ops = &cpci_hotplug_slot_ops;
+
+ /*
+ * Initialize the slot info structure with some known
+ * good values.
+ */
+ dbg("initializing slot %s", slot->hotplug_slot->name);
+ info->power_status = cpci_get_power_status(slot);
+ info->attention_status = cpci_get_attention_status(slot);
+
+ dbg("registering slot %s", slot->hotplug_slot->name);
+ status = pci_hp_register(slot->hotplug_slot);
+ if (status) {
+ err("pci_hp_register failed with error %d", status);
+ goto error_name;
+ }
+
+ /* Add slot to our internal list */
+ spin_lock(&list_lock);
+ list_add(&slot->slot_list, &slot_list);
+ slots++;
+ spin_unlock(&list_lock);
+ }
+ return 0;
+error_name:
+ kfree(name);
+error_info:
+ kfree(info);
+error_hpslot:
+ kfree(hotplug_slot);
+error_slot:
+ kfree(slot);
+error:
+ return status;
+}
+
+int
+cpci_hp_unregister_bus(struct pci_bus *bus)
+{
+ struct slot *slot;
+ struct list_head *tmp;
+ struct list_head *next;
+ int status;
+
+ spin_lock(&list_lock);
+ if(!slots) {
+ spin_unlock(&list_lock);
+ return -1;
+ }
+ list_for_each_safe(tmp, next, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ if(slot->bus == bus) {
+ dbg("deregistering slot %s", slot->hotplug_slot->name);
+ status = pci_hp_deregister(slot->hotplug_slot);
+ if(status) {
+ err("pci_hp_deregister failed with error %d",
+ status);
+ return status;
+ }
+
+ list_del(&slot->slot_list);
+ slots--;
+ }
+ }
+ spin_unlock(&list_lock);
+ return 0;
+}
+
+/* This is the interrupt mode interrupt handler */
+static irqreturn_t
+cpci_hp_intr(int irq, void *data, struct pt_regs *regs)
+{
+ dbg("entered cpci_hp_intr");
+
+ /* Check to see if it was our interrupt */
+ if((controller->irq_flags & SA_SHIRQ) &&
+ !controller->ops->check_irq(controller->dev_id)) {
+ dbg("exited cpci_hp_intr, not our interrupt");
+ return IRQ_NONE;
+ }
+
+ /* Disable ENUM interrupt */
+ controller->ops->disable_irq();
+
+ /* Trigger processing by the event thread */
+ dbg("Signal event_semaphore");
+ up(&event_semaphore);
+ dbg("exited cpci_hp_intr");
+ return IRQ_HANDLED;
+}
+
+/*
+ * According to PICMG 2.12 R2.0, section 6.3.2, upon
+ * initialization, the system driver shall clear the
+ * INS bits of the cold-inserted devices.
+ */
+static int
+init_slots(void)
+{
+ struct slot *slot;
+ struct list_head *tmp;
+ struct pci_dev* dev;
+
+ dbg("%s - enter", __FUNCTION__);
+ spin_lock(&list_lock);
+ if(!slots) {
+ spin_unlock(&list_lock);
+ return -1;
+ }
+ list_for_each(tmp, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ dbg("%s - looking at slot %s",
+ __FUNCTION__, slot->hotplug_slot->name);
+ if(cpci_check_and_clear_ins(slot)) {
+ dbg("%s - cleared INS for slot %s",
+ __FUNCTION__, slot->hotplug_slot->name);
+ dev = pci_find_slot(slot->bus->number, PCI_DEVFN(slot->number, 0));
+ if(dev) {
+ if(update_adapter_status(slot->hotplug_slot, 1)) {
+ warn("failure to update adapter file");
+ }
+ if(update_latch_status(slot->hotplug_slot, 1)) {
+ warn("failure to update latch file");
+ }
+ slot->dev = dev;
+ } else {
+ err("%s - no driver attached to device in slot %s",
+ __FUNCTION__, slot->hotplug_slot->name);
+ }
+ }
+ }
+ spin_unlock(&list_lock);
+ dbg("%s - exit", __FUNCTION__);
+ return 0;
+}
+
+static int
+check_slots(void)
+{
+ struct slot *slot;
+ struct list_head *tmp;
+ int extracted;
+ int inserted;
+
+ spin_lock(&list_lock);
+ if(!slots) {
+ spin_unlock(&list_lock);
+ err("no slots registered, shutting down");
+ return -1;
+ }
+ extracted = inserted = 0;
+ list_for_each(tmp, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ dbg("%s - looking at slot %s",
+ __FUNCTION__, slot->hotplug_slot->name);
+ if(cpci_check_and_clear_ins(slot)) {
+ u16 hs_csr;
+
+ /* Some broken hardware (e.g. PLX 9054AB) asserts ENUM# twice... */
+ if(slot->dev) {
+ warn("slot %s already inserted", slot->hotplug_slot->name);
+ inserted++;
+ continue;
+ }
+
+ /* Process insertion */
+ dbg("%s - slot %s inserted",
+ __FUNCTION__, slot->hotplug_slot->name);
+
+ /* GSM, debug */
+ hs_csr = cpci_get_hs_csr(slot);
+ dbg("%s - slot %s HS_CSR (1) = %04x",
+ __FUNCTION__, slot->hotplug_slot->name, hs_csr);
+
+ /* Configure device */
+ dbg("%s - configuring slot %s",
+ __FUNCTION__, slot->hotplug_slot->name);
+ if(cpci_configure_slot(slot)) {
+ err("%s - could not configure slot %s",
+ __FUNCTION__, slot->hotplug_slot->name);
+ continue;
+ }
+ dbg("%s - finished configuring slot %s",
+ __FUNCTION__, slot->hotplug_slot->name);
+
+ /* GSM, debug */
+ hs_csr = cpci_get_hs_csr(slot);
+ dbg("%s - slot %s HS_CSR (2) = %04x",
+ __FUNCTION__, slot->hotplug_slot->name, hs_csr);
+
+ if(update_latch_status(slot->hotplug_slot, 1)) {
+ warn("failure to update latch file");
+ }
+
+ if(update_adapter_status(slot->hotplug_slot, 1)) {
+ warn("failure to update adapter file");
+ }
+
+ cpci_led_off(slot);
+
+ /* GSM, debug */
+ hs_csr = cpci_get_hs_csr(slot);
+ dbg("%s - slot %s HS_CSR (3) = %04x",
+ __FUNCTION__, slot->hotplug_slot->name, hs_csr);
+
+ inserted++;
+ } else if(cpci_check_ext(slot)) {
+ u16 hs_csr;
+
+ /* Process extraction request */
+ dbg("%s - slot %s extracted",
+ __FUNCTION__, slot->hotplug_slot->name);
+
+ /* GSM, debug */
+ hs_csr = cpci_get_hs_csr(slot);
+ dbg("%s - slot %s HS_CSR = %04x",
+ __FUNCTION__, slot->hotplug_slot->name, hs_csr);
+
+ if(!slot->extracting) {
+ if(update_latch_status(slot->hotplug_slot, 0)) {
+ warn("failure to update latch file");
+ }
+ slot->extracting = 1;
+ }
+ extracted++;
+ }
+ }
+ spin_unlock(&list_lock);
+ if(inserted || extracted) {
+ return extracted;
+ }
+ else {
+ err("cannot find ENUM# source, shutting down");
+ return -1;
+ }
+}
+
+/* This is the interrupt mode worker thread body */
+static int
+event_thread(void *data)
+{
+ int rc;
+ struct slot *slot;
+ struct list_head *tmp;
+
+ lock_kernel();
+ daemonize("cpci_hp_eventd");
+ unlock_kernel();
+
+ dbg("%s - event thread started", __FUNCTION__);
+ while(1) {
+ dbg("event thread sleeping");
+ down_interruptible(&event_semaphore);
+ dbg("event thread woken, thread_finished = %d",
+ thread_finished);
+ if(thread_finished || signal_pending(current))
+ break;
+ while(controller->ops->query_enum()) {
+ rc = check_slots();
+ if (rc > 0)
+ /* Give userspace a chance to handle extraction */
+ msleep(500);
+ else if (rc < 0) {
+ dbg("%s - error checking slots", __FUNCTION__);
+ thread_finished = 1;
+ break;
+ }
+ }
+ /* Check for someone yanking out a board */
+ list_for_each(tmp, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ if(slot->extracting) {
+ /*
+ * Hmmm, we're likely hosed at this point, should we
+ * bother trying to tell the driver or not?
+ */
+ err("card in slot %s was improperly removed",
+ slot->hotplug_slot->name);
+ if(update_adapter_status(slot->hotplug_slot, 0)) {
+ warn("failure to update adapter file");
+ }
+ slot->extracting = 0;
+ }
+ }
+
+ /* Re-enable ENUM# interrupt */
+ dbg("%s - re-enabling irq", __FUNCTION__);
+ controller->ops->enable_irq();
+ }
+
+ dbg("%s - event thread signals exit", __FUNCTION__);
+ up(&thread_exit);
+ return 0;
+}
+
+/* This is the polling mode worker thread body */
+static int
+poll_thread(void *data)
+{
+ int rc;
+ struct slot *slot;
+ struct list_head *tmp;
+
+ lock_kernel();
+ daemonize("cpci_hp_polld");
+ unlock_kernel();
+
+ while(1) {
+ if(thread_finished || signal_pending(current))
+ break;
+
+ while(controller->ops->query_enum()) {
+ rc = check_slots();
+ if(rc > 0)
+ /* Give userspace a chance to handle extraction */
+ msleep(500);
+ else if (rc < 0) {
+ dbg("%s - error checking slots", __FUNCTION__);
+ thread_finished = 1;
+ break;
+ }
+ }
+ /* Check for someone yanking out a board */
+ list_for_each(tmp, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ if(slot->extracting) {
+ /*
+ * Hmmm, we're likely hosed at this point, should we
+ * bother trying to tell the driver or not?
+ */
+ err("card in slot %s was improperly removed",
+ slot->hotplug_slot->name);
+ if(update_adapter_status(slot->hotplug_slot, 0)) {
+ warn("failure to update adapter file");
+ }
+ slot->extracting = 0;
+ }
+ }
+
+ msleep(100);
+ }
+ dbg("poll thread signals exit");
+ up(&thread_exit);
+ return 0;
+}
+
+static int
+cpci_start_thread(void)
+{
+ int pid;
+
+ /* initialize our semaphores */
+ init_MUTEX_LOCKED(&event_semaphore);
+ init_MUTEX_LOCKED(&thread_exit);
+ thread_finished = 0;
+
+ if(controller->irq) {
+ pid = kernel_thread(event_thread, NULL, 0);
+ } else {
+ pid = kernel_thread(poll_thread, NULL, 0);
+ }
+ if(pid < 0) {
+ err("Can't start up our thread");
+ return -1;
+ }
+ dbg("Our thread pid = %d", pid);
+ return 0;
+}
+
+static void
+cpci_stop_thread(void)
+{
+ thread_finished = 1;
+ dbg("thread finish command given");
+ if(controller->irq) {
+ up(&event_semaphore);
+ }
+ dbg("wait for thread to exit");
+ down(&thread_exit);
+}
+
+int
+cpci_hp_register_controller(struct cpci_hp_controller *new_controller)
+{
+ int status = 0;
+
+ if(!controller) {
+ controller = new_controller;
+ if(controller->irq) {
+ if(request_irq(controller->irq,
+ cpci_hp_intr,
+ controller->irq_flags,
+ MY_NAME, controller->dev_id)) {
+ err("Can't get irq %d for the hotplug cPCI controller", controller->irq);
+ status = -ENODEV;
+ }
+ dbg("%s - acquired controller irq %d", __FUNCTION__,
+ controller->irq);
+ }
+ } else {
+ err("cPCI hotplug controller already registered");
+ status = -1;
+ }
+ return status;
+}
+
+int
+cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller)
+{
+ int status = 0;
+
+ if(controller) {
+ if(!thread_finished) {
+ cpci_stop_thread();
+ }
+ if(controller->irq) {
+ free_irq(controller->irq, controller->dev_id);
+ }
+ controller = NULL;
+ } else {
+ status = -ENODEV;
+ }
+ return status;
+}
+
+int
+cpci_hp_start(void)
+{
+ static int first = 1;
+ int status;
+
+ dbg("%s - enter", __FUNCTION__);
+ if(!controller) {
+ return -ENODEV;
+ }
+
+ spin_lock(&list_lock);
+ if(!slots) {
+ spin_unlock(&list_lock);
+ return -ENODEV;
+ }
+ spin_unlock(&list_lock);
+
+ if(first) {
+ status = init_slots();
+ if(status) {
+ return status;
+ }
+ first = 0;
+ }
+
+ status = cpci_start_thread();
+ if(status) {
+ return status;
+ }
+ dbg("%s - thread started", __FUNCTION__);
+
+ if(controller->irq) {
+ /* Start enum interrupt processing */
+ dbg("%s - enabling irq", __FUNCTION__);
+ controller->ops->enable_irq();
+ }
+ dbg("%s - exit", __FUNCTION__);
+ return 0;
+}
+
+int
+cpci_hp_stop(void)
+{
+ if(!controller) {
+ return -ENODEV;
+ }
+
+ if(controller->irq) {
+ /* Stop enum interrupt processing */
+ dbg("%s - disabling irq", __FUNCTION__);
+ controller->ops->disable_irq();
+ }
+ cpci_stop_thread();
+ return 0;
+}
+
+static void __exit
+cleanup_slots(void)
+{
+ struct list_head *tmp;
+ struct slot *slot;
+
+ /*
+ * Unregister all of our slots with the pci_hotplug subsystem,
+ * and free up all memory that we had allocated.
+ */
+ spin_lock(&list_lock);
+ if(!slots) {
+ goto null_cleanup;
+ }
+ list_for_each(tmp, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ list_del(&slot->slot_list);
+ pci_hp_deregister(slot->hotplug_slot);
+ kfree(slot->hotplug_slot->info);
+ kfree(slot->hotplug_slot->name);
+ kfree(slot->hotplug_slot);
+ kfree(slot);
+ }
+ null_cleanup:
+ spin_unlock(&list_lock);
+ return;
+}
+
+int __init
+cpci_hotplug_init(int debug)
+{
+ spin_lock_init(&list_lock);
+ cpci_debug = debug;
+
+ info(DRIVER_DESC " version: " DRIVER_VERSION);
+ return 0;
+}
+
+void __exit
+cpci_hotplug_exit(void)
+{
+ /*
+ * Clean everything up.
+ */
+ cleanup_slots();
+}
+
+EXPORT_SYMBOL_GPL(cpci_hp_register_controller);
+EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller);
+EXPORT_SYMBOL_GPL(cpci_hp_register_bus);
+EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus);
+EXPORT_SYMBOL_GPL(cpci_hp_start);
+EXPORT_SYMBOL_GPL(cpci_hp_stop);
diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c
new file mode 100644
index 000000000000..2e969616f298
--- /dev/null
+++ b/drivers/pci/hotplug/cpci_hotplug_pci.c
@@ -0,0 +1,661 @@
+/*
+ * CompactPCI Hot Plug Driver PCI functions
+ *
+ * Copyright (C) 2002 by SOMA Networks, Inc.
+ *
+ * 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 <scottm@somanetworks.com>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include "../pci.h"
+#include "pci_hotplug.h"
+#include "cpci_hotplug.h"
+
+#if !defined(MODULE)
+#define MY_NAME "cpci_hotplug"
+#else
+#define MY_NAME THIS_MODULE->name
+#endif
+
+extern int cpci_debug;
+
+#define dbg(format, arg...) \
+ do { \
+ if(cpci_debug) \
+ printk (KERN_DEBUG "%s: " format "\n", \
+ MY_NAME , ## arg); \
+ } while(0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
+
+#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
+
+
+u8 cpci_get_attention_status(struct slot* slot)
+{
+ int hs_cap;
+ u16 hs_csr;
+
+ hs_cap = pci_bus_find_capability(slot->bus,
+ slot->devfn,
+ PCI_CAP_ID_CHSWP);
+ if(!hs_cap) {
+ return 0;
+ }
+
+ if(pci_bus_read_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ &hs_csr)) {
+ return 0;
+ }
+ return hs_csr & 0x0008 ? 1 : 0;
+}
+
+int cpci_set_attention_status(struct slot* slot, int status)
+{
+ int hs_cap;
+ u16 hs_csr;
+
+ hs_cap = pci_bus_find_capability(slot->bus,
+ slot->devfn,
+ PCI_CAP_ID_CHSWP);
+ if(!hs_cap) {
+ return 0;
+ }
+
+ if(pci_bus_read_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ &hs_csr)) {
+ return 0;
+ }
+ if(status) {
+ hs_csr |= HS_CSR_LOO;
+ } else {
+ hs_csr &= ~HS_CSR_LOO;
+ }
+ if(pci_bus_write_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ hs_csr)) {
+ return 0;
+ }
+ return 1;
+}
+
+u16 cpci_get_hs_csr(struct slot* slot)
+{
+ int hs_cap;
+ u16 hs_csr;
+
+ hs_cap = pci_bus_find_capability(slot->bus,
+ slot->devfn,
+ PCI_CAP_ID_CHSWP);
+ if(!hs_cap) {
+ return 0xFFFF;
+ }
+
+ if(pci_bus_read_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ &hs_csr)) {
+ return 0xFFFF;
+ }
+ return hs_csr;
+}
+
+#if 0
+u16 cpci_set_hs_csr(struct slot* slot, u16 hs_csr)
+{
+ int hs_cap;
+ u16 new_hs_csr;
+
+ hs_cap = pci_bus_find_capability(slot->bus,
+ slot->devfn,
+ PCI_CAP_ID_CHSWP);
+ if(!hs_cap) {
+ return 0xFFFF;
+ }
+
+ /* Write out the new value */
+ if(pci_bus_write_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ hs_csr)) {
+ return 0xFFFF;
+ }
+
+ /* Read back what we just wrote out */
+ if(pci_bus_read_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ &new_hs_csr)) {
+ return 0xFFFF;
+ }
+ return new_hs_csr;
+}
+#endif
+
+int cpci_check_and_clear_ins(struct slot* slot)
+{
+ int hs_cap;
+ u16 hs_csr;
+ int ins = 0;
+
+ hs_cap = pci_bus_find_capability(slot->bus,
+ slot->devfn,
+ PCI_CAP_ID_CHSWP);
+ if(!hs_cap) {
+ return 0;
+ }
+ if(pci_bus_read_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ &hs_csr)) {
+ return 0;
+ }
+ if(hs_csr & HS_CSR_INS) {
+ /* Clear INS (by setting it) */
+ if(pci_bus_write_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ hs_csr)) {
+ ins = 0;
+ }
+ ins = 1;
+ }
+ return ins;
+}
+
+int cpci_check_ext(struct slot* slot)
+{
+ int hs_cap;
+ u16 hs_csr;
+ int ext = 0;
+
+ hs_cap = pci_bus_find_capability(slot->bus,
+ slot->devfn,
+ PCI_CAP_ID_CHSWP);
+ if(!hs_cap) {
+ return 0;
+ }
+ if(pci_bus_read_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ &hs_csr)) {
+ return 0;
+ }
+ if(hs_csr & HS_CSR_EXT) {
+ ext = 1;
+ }
+ return ext;
+}
+
+int cpci_clear_ext(struct slot* slot)
+{
+ int hs_cap;
+ u16 hs_csr;
+
+ hs_cap = pci_bus_find_capability(slot->bus,
+ slot->devfn,
+ PCI_CAP_ID_CHSWP);
+ if(!hs_cap) {
+ return -ENODEV;
+ }
+ if(pci_bus_read_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ &hs_csr)) {
+ return -ENODEV;
+ }
+ if(hs_csr & HS_CSR_EXT) {
+ /* Clear EXT (by setting it) */
+ if(pci_bus_write_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ hs_csr)) {
+ return -ENODEV;
+ }
+ }
+ return 0;
+}
+
+int cpci_led_on(struct slot* slot)
+{
+ int hs_cap;
+ u16 hs_csr;
+
+ hs_cap = pci_bus_find_capability(slot->bus,
+ slot->devfn,
+ PCI_CAP_ID_CHSWP);
+ if(!hs_cap) {
+ return -ENODEV;
+ }
+ if(pci_bus_read_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ &hs_csr)) {
+ return -ENODEV;
+ }
+ if((hs_csr & HS_CSR_LOO) != HS_CSR_LOO) {
+ /* Set LOO */
+ hs_csr |= HS_CSR_LOO;
+ if(pci_bus_write_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ hs_csr)) {
+ err("Could not set LOO for slot %s",
+ slot->hotplug_slot->name);
+ return -ENODEV;
+ }
+ }
+ return 0;
+}
+
+int cpci_led_off(struct slot* slot)
+{
+ int hs_cap;
+ u16 hs_csr;
+
+ hs_cap = pci_bus_find_capability(slot->bus,
+ slot->devfn,
+ PCI_CAP_ID_CHSWP);
+ if(!hs_cap) {
+ return -ENODEV;
+ }
+ if(pci_bus_read_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ &hs_csr)) {
+ return -ENODEV;
+ }
+ if(hs_csr & HS_CSR_LOO) {
+ /* Clear LOO */
+ hs_csr &= ~HS_CSR_LOO;
+ if(pci_bus_write_config_word(slot->bus,
+ slot->devfn,
+ hs_cap + 2,
+ hs_csr)) {
+ err("Could not clear LOO for slot %s",
+ slot->hotplug_slot->name);
+ return -ENODEV;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Device configuration functions
+ */
+
+static int cpci_configure_dev(struct pci_bus *bus, struct pci_dev *dev)
+{
+ u8 irq_pin;
+ int r;
+
+ dbg("%s - enter", __FUNCTION__);
+
+ /* NOTE: device already setup from prior scan */
+
+ /* FIXME: How would we know if we need to enable the expansion ROM? */
+ pci_write_config_word(dev, PCI_ROM_ADDRESS, 0x00L);
+
+ /* Assign resources */
+ dbg("assigning resources for %02x:%02x.%x",
+ dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+ for (r = 0; r < 6; r++) {
+ struct resource *res = dev->resource + r;
+ if(res->flags)
+ pci_assign_resource(dev, r);
+ }
+ dbg("finished assigning resources for %02x:%02x.%x",
+ dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+
+ /* Does this function have an interrupt at all? */
+ dbg("checking for function interrupt");
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq_pin);
+ if(irq_pin) {
+ dbg("function uses interrupt pin %d", irq_pin);
+ }
+
+ /*
+ * Need to explicitly set irq field to 0 so that it'll get assigned
+ * by the pcibios platform dependent code called by pci_enable_device.
+ */
+ dev->irq = 0;
+
+ dbg("enabling device");
+ pci_enable_device(dev); /* XXX check return */
+ dbg("now dev->irq = %d", dev->irq);
+ if(irq_pin && dev->irq) {
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+ }
+
+ /* Can't use pci_insert_device at the moment, do it manually for now */
+ pci_proc_attach_device(dev);
+ dbg("notifying drivers");
+ //pci_announce_device_to_drivers(dev);
+ dbg("%s - exit", __FUNCTION__);
+ return 0;
+}
+
+static int cpci_configure_bridge(struct pci_bus* bus, struct pci_dev* dev)
+{
+ int rc;
+ struct pci_bus* child;
+ struct resource* r;
+ u8 max, n;
+ u16 command;
+
+ dbg("%s - enter", __FUNCTION__);
+
+ /* Do basic bridge initialization */
+ rc = pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x40);
+ if(rc) {
+ printk(KERN_ERR "%s - write of PCI_LATENCY_TIMER failed\n", __FUNCTION__);
+ }
+ rc = pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 0x40);
+ if(rc) {
+ printk(KERN_ERR "%s - write of PCI_SEC_LATENCY_TIMER failed\n", __FUNCTION__);
+ }
+ rc = pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES / 4);
+ if(rc) {
+ printk(KERN_ERR "%s - write of PCI_CACHE_LINE_SIZE failed\n", __FUNCTION__);
+ }
+
+ /*
+ * Set parent bridge's subordinate field so that configuration space
+ * access will work in pci_scan_bridge and friends.
+ */
+ max = pci_max_busnr();
+ bus->subordinate = max + 1;
+ pci_write_config_byte(bus->self, PCI_SUBORDINATE_BUS, max + 1);
+
+ /* Scan behind bridge */
+ n = pci_scan_bridge(bus, dev, max, 2);
+ child = pci_find_bus(0, max + 1);
+ if (!child)
+ return -ENODEV;
+ pci_proc_attach_bus(child);
+
+ /*
+ * Update parent bridge's subordinate field if there were more bridges
+ * behind the bridge that was scanned.
+ */
+ if(n > max) {
+ bus->subordinate = n;
+ pci_write_config_byte(bus->self, PCI_SUBORDINATE_BUS, n);
+ }
+
+ /*
+ * Update the bridge resources of the bridge to accommodate devices
+ * behind it.
+ */
+ pci_bus_size_bridges(child);
+ pci_bus_assign_resources(child);
+
+ /* Enable resource mapping via command register */
+ command = PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
+ r = child->resource[0];
+ if(r && r->start) {
+ command |= PCI_COMMAND_IO;
+ }
+ r = child->resource[1];
+ if(r && r->start) {
+ command |= PCI_COMMAND_MEMORY;
+ }
+ r = child->resource[2];
+ if(r && r->start) {
+ command |= PCI_COMMAND_MEMORY;
+ }
+ rc = pci_write_config_word(dev, PCI_COMMAND, command);
+ if(rc) {
+ err("Error setting command register");
+ return rc;
+ }
+
+ /* Set bridge control register */
+ command = PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_NO_ISA;
+ rc = pci_write_config_word(dev, PCI_BRIDGE_CONTROL, command);
+ if(rc) {
+ err("Error setting bridge control register");
+ return rc;
+ }
+ dbg("%s - exit", __FUNCTION__);
+ return 0;
+}
+
+static int configure_visit_pci_dev(struct pci_dev_wrapped *wrapped_dev,
+ struct pci_bus_wrapped *wrapped_bus)
+{
+ int rc;
+ struct pci_dev *dev = wrapped_dev->dev;
+ struct pci_bus *bus = wrapped_bus->bus;
+ struct slot* slot;
+
+ dbg("%s - enter", __FUNCTION__);
+
+ /*
+ * We need to fix up the hotplug representation with the Linux
+ * representation.
+ */
+ if(wrapped_dev->data) {
+ slot = (struct slot*) wrapped_dev->data;
+ slot->dev = dev;
+ }
+
+ /* If it's a bridge, scan behind it for devices */
+ if(dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ rc = cpci_configure_bridge(bus, dev);
+ if(rc)
+ return rc;
+ }
+
+ /* Actually configure device */
+ if(dev) {
+ rc = cpci_configure_dev(bus, dev);
+ if(rc)
+ return rc;
+ }
+ dbg("%s - exit", __FUNCTION__);
+ return 0;
+}
+
+static int 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 slot* slot;
+
+ dbg("%s - enter", __FUNCTION__);
+ if(!dev)
+ return -ENODEV;
+
+ /* Remove the Linux representation */
+ if(pci_remove_device_safe(dev)) {
+ err("Could not remove device\n");
+ return -1;
+ }
+
+ /*
+ * Now remove the hotplug representation.
+ */
+ if(wrapped_dev->data) {
+ slot = (struct slot*) wrapped_dev->data;
+ slot->dev = NULL;
+ } else {
+ dbg("No hotplug representation for %02x:%02x.%x",
+ dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+ }
+ dbg("%s - exit", __FUNCTION__);
+ return 0;
+}
+
+static int unconfigure_visit_pci_bus_phase2(struct pci_bus_wrapped *wrapped_bus,
+ struct pci_dev_wrapped *wrapped_dev)
+{
+ struct pci_bus *bus = wrapped_bus->bus;
+ struct pci_bus *parent = bus->self->bus;
+
+ dbg("%s - enter", __FUNCTION__);
+
+ /* The cleanup code for proc entries regarding buses should be in the kernel... */
+ if(bus->procdir)
+ dbg("detach_pci_bus %s", bus->procdir->name);
+ 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);
+
+ /* Update parent's subordinate field */
+ if(parent) {
+ u8 n = pci_bus_max_busnr(parent);
+ if(n < parent->subordinate) {
+ parent->subordinate = n;
+ pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, n);
+ }
+ }
+ dbg("%s - exit", __FUNCTION__);
+ return 0;
+}
+
+static struct pci_visit configure_functions = {
+ .visit_pci_dev = configure_visit_pci_dev,
+};
+
+static struct pci_visit unconfigure_functions_phase2 = {
+ .post_visit_pci_bus = unconfigure_visit_pci_bus_phase2,
+ .post_visit_pci_dev = unconfigure_visit_pci_dev_phase2
+};
+
+
+int cpci_configure_slot(struct slot* slot)
+{
+ int rc = 0;
+
+ dbg("%s - enter", __FUNCTION__);
+
+ if(slot->dev == NULL) {
+ dbg("pci_dev null, finding %02x:%02x:%x",
+ slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn));
+ slot->dev = pci_find_slot(slot->bus->number, slot->devfn);
+ }
+
+ /* Still NULL? Well then scan for it! */
+ if(slot->dev == NULL) {
+ int n;
+ dbg("pci_dev still null");
+
+ /*
+ * This will generate pci_dev structures for all functions, but
+ * we will only call this case when lookup fails.
+ */
+ n = pci_scan_slot(slot->bus, slot->devfn);
+ dbg("%s: pci_scan_slot returned %d", __FUNCTION__, n);
+ if(n > 0)
+ pci_bus_add_devices(slot->bus);
+ slot->dev = pci_find_slot(slot->bus->number, slot->devfn);
+ if(slot->dev == NULL) {
+ err("Could not find PCI device for slot %02x", slot->number);
+ return 0;
+ }
+ }
+ dbg("slot->dev = %p", slot->dev);
+ if(slot->dev) {
+ struct pci_dev *dev;
+ struct pci_dev_wrapped wrapped_dev;
+ struct pci_bus_wrapped wrapped_bus;
+ int i;
+
+ memset(&wrapped_dev, 0, sizeof (struct pci_dev_wrapped));
+ memset(&wrapped_bus, 0, sizeof (struct pci_bus_wrapped));
+
+ for (i = 0; i < 8; i++) {
+ dev = pci_find_slot(slot->bus->number,
+ PCI_DEVFN(PCI_SLOT(slot->dev->devfn), i));
+ if(!dev)
+ continue;
+ wrapped_dev.dev = dev;
+ wrapped_bus.bus = slot->dev->bus;
+ if(i)
+ wrapped_dev.data = NULL;
+ else
+ wrapped_dev.data = (void*) slot;
+ rc = pci_visit_dev(&configure_functions, &wrapped_dev, &wrapped_bus);
+ }
+ }
+
+ dbg("%s - exit, rc = %d", __FUNCTION__, rc);
+ return rc;
+}
+
+int cpci_unconfigure_slot(struct slot* slot)
+{
+ int rc = 0;
+ int i;
+ struct pci_dev_wrapped wrapped_dev;
+ struct pci_bus_wrapped wrapped_bus;
+ struct pci_dev *dev;
+
+ dbg("%s - enter", __FUNCTION__);
+
+ if(!slot->dev) {
+ err("No device for slot %02x\n", slot->number);
+ return -ENODEV;
+ }
+
+ memset(&wrapped_dev, 0, sizeof (struct pci_dev_wrapped));
+ memset(&wrapped_bus, 0, sizeof (struct pci_bus_wrapped));
+
+ for (i = 0; i < 8; i++) {
+ dev = pci_find_slot(slot->bus->number,
+ PCI_DEVFN(PCI_SLOT(slot->devfn), i));
+ if(dev) {
+ wrapped_dev.dev = dev;
+ wrapped_bus.bus = dev->bus;
+ if(i)
+ wrapped_dev.data = NULL;
+ else
+ wrapped_dev.data = (void*) slot;
+ dbg("%s - unconfigure phase 2", __FUNCTION__);
+ rc = pci_visit_dev(&unconfigure_functions_phase2,
+ &wrapped_dev,
+ &wrapped_bus);
+ if(rc)
+ break;
+ }
+ }
+ dbg("%s - exit, rc = %d", __FUNCTION__, rc);
+ return rc;
+}
diff --git a/drivers/pci/hotplug/cpcihp_generic.c b/drivers/pci/hotplug/cpcihp_generic.c
new file mode 100644
index 000000000000..a62a4345b466
--- /dev/null
+++ b/drivers/pci/hotplug/cpcihp_generic.c
@@ -0,0 +1,223 @@
+/*
+ * cpcihp_generic.c
+ *
+ * Generic port I/O CompactPCI driver
+ *
+ * Copyright 2002 SOMA Networks, Inc.
+ * Copyright 2001 Intel San Luis Obispo
+ * Copyright 2000,2001 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.
+ *
+ * This generic CompactPCI hotplug driver should allow using the PCI hotplug
+ * mechanism on any CompactPCI board that exposes the #ENUM signal as a bit
+ * in a system register that can be read through standard port I/O.
+ *
+ * Send feedback to <scottm@somanetworks.com>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include "cpci_hotplug.h"
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>"
+#define DRIVER_DESC "Generic port I/O CompactPCI Hot Plug Driver"
+
+#if !defined(MODULE)
+#define MY_NAME "cpcihp_generic"
+#else
+#define MY_NAME THIS_MODULE->name
+#endif
+
+#define dbg(format, arg...) \
+ do { \
+ if(debug) \
+ printk (KERN_DEBUG "%s: " format "\n", \
+ MY_NAME , ## arg); \
+ } while(0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
+
+/* local variables */
+static int debug;
+static char *bridge;
+static u8 bridge_busnr;
+static u8 bridge_slot;
+static struct pci_bus *bus;
+static u8 first_slot;
+static u8 last_slot;
+static u16 port;
+static unsigned int enum_bit;
+static u8 enum_mask;
+
+static struct cpci_hp_controller_ops generic_hpc_ops;
+static struct cpci_hp_controller generic_hpc;
+
+static int __init validate_parameters(void)
+{
+ char* str;
+ char* p;
+ unsigned long tmp;
+
+ if(!bridge) {
+ info("not configured, disabling.");
+ return 1;
+ }
+ str = bridge;
+ if(!*str)
+ return -EINVAL;
+
+ tmp = simple_strtoul(str, &p, 16);
+ if(p == str || tmp > 0xff) {
+ err("Invalid hotplug bus bridge device bus number");
+ return -EINVAL;
+ }
+ bridge_busnr = (u8) tmp;
+ dbg("bridge_busnr = 0x%02x", bridge_busnr);
+ if(*p != ':') {
+ err("Invalid hotplug bus bridge device");
+ return -EINVAL;
+ }
+ str = p + 1;
+ tmp = simple_strtoul(str, &p, 16);
+ if(p == str || tmp > 0x1f) {
+ err("Invalid hotplug bus bridge device slot number");
+ return -EINVAL;
+ }
+ bridge_slot = (u8) tmp;
+ dbg("bridge_slot = 0x%02x", bridge_slot);
+
+ dbg("first_slot = 0x%02x", first_slot);
+ dbg("last_slot = 0x%02x", last_slot);
+ if(!(first_slot && last_slot)) {
+ err("Need to specify first_slot and last_slot");
+ return -EINVAL;
+ }
+ if(last_slot < first_slot) {
+ err("first_slot must be less than last_slot");
+ return -EINVAL;
+ }
+
+ dbg("port = 0x%04x", port);
+ dbg("enum_bit = 0x%02x", enum_bit);
+ if(enum_bit > 7) {
+ err("Invalid #ENUM bit");
+ return -EINVAL;
+ }
+ enum_mask = 1 << enum_bit;
+ return 0;
+}
+
+static int query_enum(void)
+{
+ u8 value;
+
+ value = inb_p(port);
+ return ((value & enum_mask) == enum_mask);
+}
+
+static int __init cpcihp_generic_init(void)
+{
+ int status;
+ struct resource* r;
+ struct pci_dev* dev;
+
+ info(DRIVER_DESC " version: " DRIVER_VERSION);
+ status = validate_parameters();
+ if(status != 0)
+ return status;
+
+ r = request_region(port, 1, "#ENUM hotswap signal register");
+ if(!r)
+ return -EBUSY;
+
+ dev = pci_find_slot(bridge_busnr, PCI_DEVFN(bridge_slot, 0));
+ if(!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
+ err("Invalid bridge device %s", bridge);
+ return -EINVAL;
+ }
+ bus = dev->subordinate;
+
+ memset(&generic_hpc, 0, sizeof (struct cpci_hp_controller));
+ generic_hpc_ops.query_enum = query_enum;
+ generic_hpc.ops = &generic_hpc_ops;
+
+ status = cpci_hp_register_controller(&generic_hpc);
+ if(status != 0) {
+ err("Could not register cPCI hotplug controller");
+ return -ENODEV;
+ }
+ dbg("registered controller");
+
+ status = cpci_hp_register_bus(bus, first_slot, last_slot);
+ if(status != 0) {
+ err("Could not register cPCI hotplug bus");
+ goto init_bus_register_error;
+ }
+ dbg("registered bus");
+
+ status = cpci_hp_start();
+ if(status != 0) {
+ err("Could not started cPCI hotplug system");
+ goto init_start_error;
+ }
+ dbg("started cpci hp system");
+ return 0;
+init_start_error:
+ cpci_hp_unregister_bus(bus);
+init_bus_register_error:
+ cpci_hp_unregister_controller(&generic_hpc);
+ err("status = %d", status);
+ return status;
+
+}
+
+static void __exit cpcihp_generic_exit(void)
+{
+ cpci_hp_stop();
+ cpci_hp_unregister_bus(bus);
+ cpci_hp_unregister_controller(&generic_hpc);
+ release_region(port, 1);
+}
+
+module_init(cpcihp_generic_init);
+module_exit(cpcihp_generic_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+module_param(bridge, charp, 0);
+MODULE_PARM_DESC(bridge, "Hotswap bus bridge device, <bus>:<slot> (bus and slot are in hexadecimal)");
+module_param(first_slot, byte, 0);
+MODULE_PARM_DESC(first_slot, "Hotswap bus first slot number");
+module_param(last_slot, byte, 0);
+MODULE_PARM_DESC(last_slot, "Hotswap bus last slot number");
+module_param(port, ushort, 0);
+MODULE_PARM_DESC(port, "#ENUM signal I/O port");
+module_param(enum_bit, uint, 0);
+MODULE_PARM_DESC(enum_bit, "#ENUM signal bit (0-7)");
diff --git a/drivers/pci/hotplug/cpcihp_zt5550.c b/drivers/pci/hotplug/cpcihp_zt5550.c
new file mode 100644
index 000000000000..e9928024be78
--- /dev/null
+++ b/drivers/pci/hotplug/cpcihp_zt5550.c
@@ -0,0 +1,305 @@
+/*
+ * cpcihp_zt5550.c
+ *
+ * Intel/Ziatech ZT5550 CompactPCI Host Controller driver
+ *
+ * Copyright 2002 SOMA Networks, Inc.
+ * Copyright 2001 Intel San Luis Obispo
+ * Copyright 2000,2001 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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 <scottm@somanetworks.com>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include "cpci_hotplug.h"
+#include "cpcihp_zt5550.h"
+
+#define DRIVER_VERSION "0.2"
+#define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>"
+#define DRIVER_DESC "ZT5550 CompactPCI Hot Plug Driver"
+
+#define MY_NAME "cpcihp_zt5550"
+
+#define dbg(format, arg...) \
+ do { \
+ if(debug) \
+ printk (KERN_DEBUG "%s: " format "\n", \
+ MY_NAME , ## arg); \
+ } while(0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
+
+/* local variables */
+static int debug;
+static int poll;
+static struct cpci_hp_controller_ops zt5550_hpc_ops;
+static struct cpci_hp_controller zt5550_hpc;
+
+/* Primary cPCI bus bridge device */
+static struct pci_dev *bus0_dev;
+static struct pci_bus *bus0;
+
+/* Host controller device */
+static struct pci_dev *hc_dev;
+
+/* Host controller register addresses */
+static void __iomem *hc_registers;
+static void __iomem *csr_hc_index;
+static void __iomem *csr_hc_data;
+static void __iomem *csr_int_status;
+static void __iomem *csr_int_mask;
+
+
+static int zt5550_hc_config(struct pci_dev *pdev)
+{
+ /* Since we know that no boards exist with two HC chips, treat it as an error */
+ if(hc_dev) {
+ err("too many host controller devices?");
+ return -EBUSY;
+ }
+ hc_dev = pdev;
+ dbg("hc_dev = %p", hc_dev);
+ dbg("pci resource start %lx", pci_resource_start(hc_dev, 1));
+ dbg("pci resource len %lx", pci_resource_len(hc_dev, 1));
+
+ if(!request_mem_region(pci_resource_start(hc_dev, 1),
+ pci_resource_len(hc_dev, 1), MY_NAME)) {
+ err("cannot reserve MMIO region");
+ return -ENOMEM;
+ }
+
+ hc_registers =
+ ioremap(pci_resource_start(hc_dev, 1), pci_resource_len(hc_dev, 1));
+ if(!hc_registers) {
+ err("cannot remap MMIO region %lx @ %lx",
+ pci_resource_len(hc_dev, 1), pci_resource_start(hc_dev, 1));
+ release_mem_region(pci_resource_start(hc_dev, 1),
+ pci_resource_len(hc_dev, 1));
+ return -ENODEV;
+ }
+
+ csr_hc_index = hc_registers + CSR_HCINDEX;
+ csr_hc_data = hc_registers + CSR_HCDATA;
+ csr_int_status = hc_registers + CSR_INTSTAT;
+ csr_int_mask = hc_registers + CSR_INTMASK;
+
+ /*
+ * Disable host control, fault and serial interrupts
+ */
+ dbg("disabling host control, fault and serial interrupts");
+ writeb((u8) HC_INT_MASK_REG, csr_hc_index);
+ writeb((u8) ALL_INDEXED_INTS_MASK, csr_hc_data);
+ dbg("disabled host control, fault and serial interrupts");
+
+ /*
+ * Disable timer0, timer1 and ENUM interrupts
+ */
+ dbg("disabling timer0, timer1 and ENUM interrupts");
+ writeb((u8) ALL_DIRECT_INTS_MASK, csr_int_mask);
+ dbg("disabled timer0, timer1 and ENUM interrupts");
+ return 0;
+}
+
+static int zt5550_hc_cleanup(void)
+{
+ if(!hc_dev)
+ return -ENODEV;
+
+ iounmap(hc_registers);
+ release_mem_region(pci_resource_start(hc_dev, 1),
+ pci_resource_len(hc_dev, 1));
+ return 0;
+}
+
+static int zt5550_hc_query_enum(void)
+{
+ u8 value;
+
+ value = inb_p(ENUM_PORT);
+ return ((value & ENUM_MASK) == ENUM_MASK);
+}
+
+static int zt5550_hc_check_irq(void *dev_id)
+{
+ int ret;
+ u8 reg;
+
+ ret = 0;
+ if(dev_id == zt5550_hpc.dev_id) {
+ reg = readb(csr_int_status);
+ if(reg)
+ ret = 1;
+ }
+ return ret;
+}
+
+static int zt5550_hc_enable_irq(void)
+{
+ u8 reg;
+
+ if(hc_dev == NULL) {
+ return -ENODEV;
+ }
+ reg = readb(csr_int_mask);
+ reg = reg & ~ENUM_INT_MASK;
+ writeb(reg, csr_int_mask);
+ return 0;
+}
+
+static int zt5550_hc_disable_irq(void)
+{
+ u8 reg;
+
+ if(hc_dev == NULL) {
+ return -ENODEV;
+ }
+
+ reg = readb(csr_int_mask);
+ reg = reg | ENUM_INT_MASK;
+ writeb(reg, csr_int_mask);
+ return 0;
+}
+
+static int zt5550_hc_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int status;
+
+ status = zt5550_hc_config(pdev);
+ if(status != 0) {
+ return status;
+ }
+ dbg("returned from zt5550_hc_config");
+
+ memset(&zt5550_hpc, 0, sizeof (struct cpci_hp_controller));
+ zt5550_hpc_ops.query_enum = zt5550_hc_query_enum;
+ zt5550_hpc.ops = &zt5550_hpc_ops;
+ if(!poll) {
+ zt5550_hpc.irq = hc_dev->irq;
+ zt5550_hpc.irq_flags = SA_SHIRQ;
+ zt5550_hpc.dev_id = hc_dev;
+
+ zt5550_hpc_ops.enable_irq = zt5550_hc_enable_irq;
+ zt5550_hpc_ops.disable_irq = zt5550_hc_disable_irq;
+ zt5550_hpc_ops.check_irq = zt5550_hc_check_irq;
+ } else {
+ info("using ENUM# polling mode");
+ }
+
+ status = cpci_hp_register_controller(&zt5550_hpc);
+ if(status != 0) {
+ err("could not register cPCI hotplug controller");
+ goto init_hc_error;
+ }
+ dbg("registered controller");
+
+ /* Look for first device matching cPCI bus's bridge vendor and device IDs */
+ if(!(bus0_dev = pci_get_device(PCI_VENDOR_ID_DEC,
+ PCI_DEVICE_ID_DEC_21154, NULL))) {
+ status = -ENODEV;
+ goto init_register_error;
+ }
+ bus0 = bus0_dev->subordinate;
+ pci_dev_put(bus0_dev);
+
+ status = cpci_hp_register_bus(bus0, 0x0a, 0x0f);
+ if(status != 0) {
+ err("could not register cPCI hotplug bus");
+ goto init_register_error;
+ }
+ dbg("registered bus");
+
+ status = cpci_hp_start();
+ if(status != 0) {
+ err("could not started cPCI hotplug system");
+ cpci_hp_unregister_bus(bus0);
+ goto init_register_error;
+ }
+ dbg("started cpci hp system");
+
+ return 0;
+init_register_error:
+ cpci_hp_unregister_controller(&zt5550_hpc);
+init_hc_error:
+ err("status = %d", status);
+ zt5550_hc_cleanup();
+ return status;
+
+}
+
+static void __devexit zt5550_hc_remove_one(struct pci_dev *pdev)
+{
+ cpci_hp_stop();
+ cpci_hp_unregister_bus(bus0);
+ cpci_hp_unregister_controller(&zt5550_hpc);
+ zt5550_hc_cleanup();
+}
+
+
+static struct pci_device_id zt5550_hc_pci_tbl[] = {
+ { PCI_VENDOR_ID_ZIATECH, PCI_DEVICE_ID_ZIATECH_5550_HC, PCI_ANY_ID, PCI_ANY_ID, },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, zt5550_hc_pci_tbl);
+
+static struct pci_driver zt5550_hc_driver = {
+ .name = "zt5550_hc",
+ .id_table = zt5550_hc_pci_tbl,
+ .probe = zt5550_hc_init_one,
+ .remove = __devexit_p(zt5550_hc_remove_one),
+};
+
+static int __init zt5550_init(void)
+{
+ struct resource* r;
+
+ info(DRIVER_DESC " version: " DRIVER_VERSION);
+ r = request_region(ENUM_PORT, 1, "#ENUM hotswap signal register");
+ if(!r)
+ return -EBUSY;
+
+ return pci_register_driver(&zt5550_hc_driver);
+}
+
+static void __exit
+zt5550_exit(void)
+{
+ pci_unregister_driver(&zt5550_hc_driver);
+ release_region(ENUM_PORT, 1);
+}
+
+module_init(zt5550_init);
+module_exit(zt5550_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+module_param(poll, bool, 0644);
+MODULE_PARM_DESC(poll, "#ENUM polling mode enabled or not");
diff --git a/drivers/pci/hotplug/cpcihp_zt5550.h b/drivers/pci/hotplug/cpcihp_zt5550.h
new file mode 100644
index 000000000000..bebc6060a558
--- /dev/null
+++ b/drivers/pci/hotplug/cpcihp_zt5550.h
@@ -0,0 +1,79 @@
+/*
+ * cpcihp_zt5550.h
+ *
+ * Intel/Ziatech ZT5550 CompactPCI Host Controller driver definitions
+ *
+ * Copyright 2002 SOMA Networks, Inc.
+ * Copyright 2001 Intel San Luis Obispo
+ * Copyright 2000,2001 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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 <scottm@somanetworks.com>
+ */
+
+#ifndef _CPCIHP_ZT5550_H
+#define _CPCIHP_ZT5550_H
+
+/* Direct registers */
+#define CSR_HCINDEX 0x00
+#define CSR_HCDATA 0x04
+#define CSR_INTSTAT 0x08
+#define CSR_INTMASK 0x09
+#define CSR_CNT0CMD 0x0C
+#define CSR_CNT1CMD 0x0E
+#define CSR_CNT0 0x10
+#define CSR_CNT1 0x14
+
+/* Masks for interrupt bits in CSR_INTMASK direct register */
+#define CNT0_INT_MASK 0x01
+#define CNT1_INT_MASK 0x02
+#define ENUM_INT_MASK 0x04
+#define ALL_DIRECT_INTS_MASK 0x07
+
+/* Indexed registers (through CSR_INDEX, CSR_DATA) */
+#define HC_INT_MASK_REG 0x04
+#define HC_STATUS_REG 0x08
+#define HC_CMD_REG 0x0C
+#define ARB_CONFIG_GNT_REG 0x10
+#define ARB_CONFIG_CFG_REG 0x12
+#define ARB_CONFIG_REG 0x10
+#define ISOL_CONFIG_REG 0x18
+#define FAULT_STATUS_REG 0x20
+#define FAULT_CONFIG_REG 0x24
+#define WD_CONFIG_REG 0x2C
+#define HC_DIAG_REG 0x30
+#define SERIAL_COMM_REG 0x34
+#define SERIAL_OUT_REG 0x38
+#define SERIAL_IN_REG 0x3C
+
+/* Masks for interrupt bits in HC_INT_MASK_REG indexed register */
+#define SERIAL_INT_MASK 0x01
+#define FAULT_INT_MASK 0x02
+#define HCF_INT_MASK 0x04
+#define ALL_INDEXED_INTS_MASK 0x07
+
+/* Digital I/O port storing ENUM# */
+#define ENUM_PORT 0xE1
+/* Mask to get to the ENUM# bit on the bus */
+#define ENUM_MASK 0x40
+
+#endif /* _CPCIHP_ZT5550_H */
diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h
new file mode 100644
index 000000000000..092491e25ef2
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp.h
@@ -0,0 +1,721 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM
+ *
+ * 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 <greg@kroah.com>
+ *
+ */
+#ifndef _CPQPHP_H
+#define _CPQPHP_H
+
+#include "pci_hotplug.h"
+#include <linux/interrupt.h>
+#include <asm/io.h> /* for read? and write? functions */
+#include <linux/delay.h> /* for delays */
+
+#define MY_NAME "cpqphp"
+
+#define dbg(fmt, arg...) do { if (cpqhp_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)
+
+
+
+struct smbios_system_slot {
+ u8 type;
+ u8 length;
+ u16 handle;
+ u8 name_string_num;
+ u8 slot_type;
+ u8 slot_width;
+ u8 slot_current_usage;
+ u8 slot_length;
+ u16 slot_number;
+ u8 properties1;
+ u8 properties2;
+} __attribute__ ((packed));
+
+/* offsets to the smbios generic type based on the above structure layout */
+enum smbios_system_slot_offsets {
+ SMBIOS_SLOT_GENERIC_TYPE = offsetof(struct smbios_system_slot, type),
+ SMBIOS_SLOT_GENERIC_LENGTH = offsetof(struct smbios_system_slot, length),
+ SMBIOS_SLOT_GENERIC_HANDLE = offsetof(struct smbios_system_slot, handle),
+ SMBIOS_SLOT_NAME_STRING_NUM = offsetof(struct smbios_system_slot, name_string_num),
+ SMBIOS_SLOT_TYPE = offsetof(struct smbios_system_slot, slot_type),
+ SMBIOS_SLOT_WIDTH = offsetof(struct smbios_system_slot, slot_width),
+ SMBIOS_SLOT_CURRENT_USAGE = offsetof(struct smbios_system_slot, slot_current_usage),
+ SMBIOS_SLOT_LENGTH = offsetof(struct smbios_system_slot, slot_length),
+ SMBIOS_SLOT_NUMBER = offsetof(struct smbios_system_slot, slot_number),
+ SMBIOS_SLOT_PROPERTIES1 = offsetof(struct smbios_system_slot, properties1),
+ SMBIOS_SLOT_PROPERTIES2 = offsetof(struct smbios_system_slot, properties2),
+};
+
+struct smbios_generic {
+ u8 type;
+ u8 length;
+ u16 handle;
+} __attribute__ ((packed));
+
+/* offsets to the smbios generic type based on the above structure layout */
+enum smbios_generic_offsets {
+ SMBIOS_GENERIC_TYPE = offsetof(struct smbios_generic, type),
+ SMBIOS_GENERIC_LENGTH = offsetof(struct smbios_generic, length),
+ SMBIOS_GENERIC_HANDLE = offsetof(struct smbios_generic, handle),
+};
+
+struct smbios_entry_point {
+ char anchor[4];
+ u8 ep_checksum;
+ u8 ep_length;
+ u8 major_version;
+ u8 minor_version;
+ u16 max_size_entry;
+ u8 ep_rev;
+ u8 reserved[5];
+ char int_anchor[5];
+ u8 int_checksum;
+ u16 st_length;
+ u32 st_address;
+ u16 number_of_entrys;
+ u8 bcd_rev;
+} __attribute__ ((packed));
+
+/* offsets to the smbios entry point based on the above structure layout */
+enum smbios_entry_point_offsets {
+ ANCHOR = offsetof(struct smbios_entry_point, anchor[0]),
+ EP_CHECKSUM = offsetof(struct smbios_entry_point, ep_checksum),
+ EP_LENGTH = offsetof(struct smbios_entry_point, ep_length),
+ MAJOR_VERSION = offsetof(struct smbios_entry_point, major_version),
+ MINOR_VERSION = offsetof(struct smbios_entry_point, minor_version),
+ MAX_SIZE_ENTRY = offsetof(struct smbios_entry_point, max_size_entry),
+ EP_REV = offsetof(struct smbios_entry_point, ep_rev),
+ INT_ANCHOR = offsetof(struct smbios_entry_point, int_anchor[0]),
+ INT_CHECKSUM = offsetof(struct smbios_entry_point, int_checksum),
+ ST_LENGTH = offsetof(struct smbios_entry_point, st_length),
+ ST_ADDRESS = offsetof(struct smbios_entry_point, st_address),
+ NUMBER_OF_ENTRYS = offsetof(struct smbios_entry_point, number_of_entrys),
+ BCD_REV = offsetof(struct smbios_entry_point, bcd_rev),
+};
+
+struct ctrl_reg { /* offset */
+ u8 slot_RST; /* 0x00 */
+ u8 slot_enable; /* 0x01 */
+ u16 misc; /* 0x02 */
+ u32 led_control; /* 0x04 */
+ u32 int_input_clear; /* 0x08 */
+ u32 int_mask; /* 0x0a */
+ u8 reserved0; /* 0x10 */
+ u8 reserved1; /* 0x11 */
+ u8 reserved2; /* 0x12 */
+ u8 gen_output_AB; /* 0x13 */
+ u32 non_int_input; /* 0x14 */
+ u32 reserved3; /* 0x18 */
+ u32 reserved4; /* 0x1a */
+ u32 reserved5; /* 0x20 */
+ u8 reserved6; /* 0x24 */
+ u8 reserved7; /* 0x25 */
+ u16 reserved8; /* 0x26 */
+ u8 slot_mask; /* 0x28 */
+ u8 reserved9; /* 0x29 */
+ u8 reserved10; /* 0x2a */
+ u8 reserved11; /* 0x2b */
+ u8 slot_SERR; /* 0x2c */
+ u8 slot_power; /* 0x2d */
+ u8 reserved12; /* 0x2e */
+ u8 reserved13; /* 0x2f */
+ u8 next_curr_freq; /* 0x30 */
+ u8 reset_freq_mode; /* 0x31 */
+} __attribute__ ((packed));
+
+/* offsets to the controller registers based on the above structure layout */
+enum ctrl_offsets {
+ SLOT_RST = offsetof(struct ctrl_reg, slot_RST),
+ SLOT_ENABLE = offsetof(struct ctrl_reg, slot_enable),
+ MISC = offsetof(struct ctrl_reg, misc),
+ LED_CONTROL = offsetof(struct ctrl_reg, led_control),
+ INT_INPUT_CLEAR = offsetof(struct ctrl_reg, int_input_clear),
+ INT_MASK = offsetof(struct ctrl_reg, int_mask),
+ CTRL_RESERVED0 = offsetof(struct ctrl_reg, reserved0),
+ CTRL_RESERVED1 = offsetof(struct ctrl_reg, reserved1),
+ CTRL_RESERVED2 = offsetof(struct ctrl_reg, reserved1),
+ GEN_OUTPUT_AB = offsetof(struct ctrl_reg, gen_output_AB),
+ NON_INT_INPUT = offsetof(struct ctrl_reg, non_int_input),
+ CTRL_RESERVED3 = offsetof(struct ctrl_reg, reserved3),
+ CTRL_RESERVED4 = offsetof(struct ctrl_reg, reserved4),
+ CTRL_RESERVED5 = offsetof(struct ctrl_reg, reserved5),
+ CTRL_RESERVED6 = offsetof(struct ctrl_reg, reserved6),
+ CTRL_RESERVED7 = offsetof(struct ctrl_reg, reserved7),
+ CTRL_RESERVED8 = offsetof(struct ctrl_reg, reserved8),
+ SLOT_MASK = offsetof(struct ctrl_reg, slot_mask),
+ CTRL_RESERVED9 = offsetof(struct ctrl_reg, reserved9),
+ CTRL_RESERVED10 = offsetof(struct ctrl_reg, reserved10),
+ CTRL_RESERVED11 = offsetof(struct ctrl_reg, reserved11),
+ SLOT_SERR = offsetof(struct ctrl_reg, slot_SERR),
+ SLOT_POWER = offsetof(struct ctrl_reg, slot_power),
+ NEXT_CURR_FREQ = offsetof(struct ctrl_reg, next_curr_freq),
+ RESET_FREQ_MODE = offsetof(struct ctrl_reg, reset_freq_mode),
+};
+
+struct hrt {
+ char sig0;
+ char sig1;
+ char sig2;
+ char sig3;
+ u16 unused_IRQ;
+ u16 PCIIRQ;
+ u8 number_of_entries;
+ u8 revision;
+ u16 reserved1;
+ u32 reserved2;
+} __attribute__ ((packed));
+
+/* offsets to the hotplug resource table registers based on the above structure layout */
+enum hrt_offsets {
+ SIG0 = offsetof(struct hrt, sig0),
+ SIG1 = offsetof(struct hrt, sig1),
+ SIG2 = offsetof(struct hrt, sig2),
+ SIG3 = offsetof(struct hrt, sig3),
+ UNUSED_IRQ = offsetof(struct hrt, unused_IRQ),
+ PCIIRQ = offsetof(struct hrt, PCIIRQ),
+ NUMBER_OF_ENTRIES = offsetof(struct hrt, number_of_entries),
+ REVISION = offsetof(struct hrt, revision),
+ HRT_RESERVED1 = offsetof(struct hrt, reserved1),
+ HRT_RESERVED2 = offsetof(struct hrt, reserved2),
+};
+
+struct slot_rt {
+ u8 dev_func;
+ u8 primary_bus;
+ u8 secondary_bus;
+ u8 max_bus;
+ u16 io_base;
+ u16 io_length;
+ u16 mem_base;
+ u16 mem_length;
+ u16 pre_mem_base;
+ u16 pre_mem_length;
+} __attribute__ ((packed));
+
+/* offsets to the hotplug slot resource table registers based on the above structure layout */
+enum slot_rt_offsets {
+ DEV_FUNC = offsetof(struct slot_rt, dev_func),
+ PRIMARY_BUS = offsetof(struct slot_rt, primary_bus),
+ SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus),
+ MAX_BUS = offsetof(struct slot_rt, max_bus),
+ IO_BASE = offsetof(struct slot_rt, io_base),
+ IO_LENGTH = offsetof(struct slot_rt, io_length),
+ MEM_BASE = offsetof(struct slot_rt, mem_base),
+ MEM_LENGTH = offsetof(struct slot_rt, mem_length),
+ PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base),
+ PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length),
+};
+
+struct pci_func {
+ struct pci_func *next;
+ u8 bus;
+ u8 device;
+ u8 function;
+ u8 is_a_board;
+ u16 status;
+ u8 configured;
+ u8 switch_save;
+ u8 presence_save;
+ u32 base_length[0x06];
+ u8 base_type[0x06];
+ u16 reserved2;
+ u32 config_space[0x20];
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ struct timer_list *p_task_event;
+ struct pci_dev* pci_dev;
+};
+
+struct slot {
+ struct slot *next;
+ u8 bus;
+ u8 device;
+ u8 number;
+ u8 is_a_board;
+ u8 configured;
+ u8 state;
+ u8 switch_save;
+ u8 presence_save;
+ u32 capabilities;
+ u16 reserved2;
+ struct timer_list task_event;
+ u8 hp_slot;
+ struct controller *ctrl;
+ void __iomem *p_sm_slot;
+ struct hotplug_slot *hotplug_slot;
+};
+
+struct pci_resource {
+ struct pci_resource * next;
+ u32 base;
+ u32 length;
+};
+
+struct event_info {
+ u32 event_type;
+ u8 hp_slot;
+};
+
+struct controller {
+ struct controller *next;
+ u32 ctrl_int_comp;
+ struct semaphore crit_sect; /* critical section semaphore */
+ void __iomem *hpc_reg; /* cookie for our pci controller location */
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ struct pci_dev *pci_dev;
+ struct pci_bus *pci_bus;
+ struct event_info event_queue[10];
+ struct slot *slot;
+ u8 next_event;
+ u8 interrupt;
+ u8 cfgspc_irq;
+ u8 bus; /* bus number for the pci hotplug controller */
+ u8 rev;
+ u8 slot_device_offset;
+ u8 first_slot;
+ u8 add_support;
+ u8 push_flag;
+ enum pci_bus_speed speed;
+ enum pci_bus_speed speed_capability;
+ u8 push_button; /* 0 = no pushbutton, 1 = pushbutton present */
+ u8 slot_switch_type; /* 0 = no switch, 1 = switch present */
+ u8 defeature_PHP; /* 0 = PHP not supported, 1 = PHP supported */
+ u8 alternate_base_address; /* 0 = not supported, 1 = supported */
+ u8 pci_config_space; /* Index/data access to working registers 0 = not supported, 1 = supported */
+ u8 pcix_speed_capability; /* PCI-X */
+ u8 pcix_support; /* PCI-X */
+ u16 vendor_id;
+ struct work_struct int_task_event;
+ wait_queue_head_t queue; /* sleep & wake process */
+};
+
+struct irq_mapping {
+ u8 barber_pole;
+ u8 valid_INT;
+ u8 interrupt[4];
+};
+
+struct resource_lists {
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ struct irq_mapping *irqs;
+};
+
+#define ROM_PHY_ADDR 0x0F0000
+#define ROM_PHY_LEN 0x00ffff
+
+#define PCI_HPC_ID 0xA0F7
+#define PCI_SUB_HPC_ID 0xA2F7
+#define PCI_SUB_HPC_ID2 0xA2F8
+#define PCI_SUB_HPC_ID3 0xA2F9
+#define PCI_SUB_HPC_ID_INTC 0xA2FA
+#define PCI_SUB_HPC_ID4 0xA2FD
+
+#define INT_BUTTON_IGNORE 0
+#define INT_PRESENCE_ON 1
+#define INT_PRESENCE_OFF 2
+#define INT_SWITCH_CLOSE 3
+#define INT_SWITCH_OPEN 4
+#define INT_POWER_FAULT 5
+#define INT_POWER_FAULT_CLEAR 6
+#define INT_BUTTON_PRESS 7
+#define INT_BUTTON_RELEASE 8
+#define INT_BUTTON_CANCEL 9
+
+#define STATIC_STATE 0
+#define BLINKINGON_STATE 1
+#define BLINKINGOFF_STATE 2
+#define POWERON_STATE 3
+#define POWEROFF_STATE 4
+
+#define PCISLOT_INTERLOCK_CLOSED 0x00000001
+#define PCISLOT_ADAPTER_PRESENT 0x00000002
+#define PCISLOT_POWERED 0x00000004
+#define PCISLOT_66_MHZ_OPERATION 0x00000008
+#define PCISLOT_64_BIT_OPERATION 0x00000010
+#define PCISLOT_REPLACE_SUPPORTED 0x00000020
+#define PCISLOT_ADD_SUPPORTED 0x00000040
+#define PCISLOT_INTERLOCK_SUPPORTED 0x00000080
+#define PCISLOT_66_MHZ_SUPPORTED 0x00000100
+#define PCISLOT_64_BIT_SUPPORTED 0x00000200
+
+#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400
+
+#define INTERLOCK_OPEN 0x00000002
+#define ADD_NOT_SUPPORTED 0x00000003
+#define CARD_FUNCTIONING 0x00000005
+#define ADAPTER_NOT_SAME 0x00000006
+#define NO_ADAPTER_PRESENT 0x00000009
+#define NOT_ENOUGH_RESOURCES 0x0000000B
+#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C
+#define POWER_FAILURE 0x0000000E
+
+#define REMOVE_NOT_SUPPORTED 0x00000003
+
+
+/*
+ * error Messages
+ */
+#define msg_initialization_err "Initialization failure, error=%d\n"
+#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n"
+#define msg_HPC_non_compaq_or_intel "The PCI hot plug controller is not supported by this driver.\n"
+#define msg_HPC_not_supported "this system is not supported by this version of cpqphpd. Upgrade to a newer version of cpqphpd\n"
+#define msg_unable_to_save "unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n"
+#define msg_button_on "PCI slot #%d - powering on due to button press.\n"
+#define msg_button_off "PCI slot #%d - powering off due to button press.\n"
+#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n"
+#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n"
+
+
+/* sysfs functions for the hotplug controller info */
+extern void cpqhp_create_ctrl_files (struct controller *ctrl);
+
+/* controller functions */
+extern void cpqhp_pushbutton_thread (unsigned long event_pointer);
+extern irqreturn_t cpqhp_ctrl_intr (int IRQ, void *data, struct pt_regs *regs);
+extern int cpqhp_find_available_resources (struct controller *ctrl, void __iomem *rom_start);
+extern int cpqhp_event_start_thread (void);
+extern void cpqhp_event_stop_thread (void);
+extern struct pci_func *cpqhp_slot_create (unsigned char busnumber);
+extern struct pci_func *cpqhp_slot_find (unsigned char bus, unsigned char device, unsigned char index);
+extern int cpqhp_process_SI (struct controller *ctrl, struct pci_func *func);
+extern int cpqhp_process_SS (struct controller *ctrl, struct pci_func *func);
+extern int cpqhp_hardware_test (struct controller *ctrl, int test_num);
+
+/* resource functions */
+extern int cpqhp_resource_sort_and_combine (struct pci_resource **head);
+
+/* pci functions */
+extern int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
+extern int cpqhp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot);
+extern int cpqhp_save_config (struct controller *ctrl, int busnumber, int is_hot_plug);
+extern int cpqhp_save_base_addr_length (struct controller *ctrl, struct pci_func * func);
+extern int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func);
+extern int cpqhp_configure_board (struct controller *ctrl, struct pci_func * func);
+extern int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot);
+extern int cpqhp_valid_replace (struct controller *ctrl, struct pci_func * func);
+extern void cpqhp_destroy_board_resources (struct pci_func * func);
+extern int cpqhp_return_board_resources (struct pci_func * func, struct resource_lists * resources);
+extern void cpqhp_destroy_resource_list (struct resource_lists * resources);
+extern int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func);
+extern int cpqhp_unconfigure_device (struct pci_func* func);
+
+/* Global variables */
+extern int cpqhp_debug;
+extern int cpqhp_legacy_mode;
+extern struct controller *cpqhp_ctrl_list;
+extern struct pci_func *cpqhp_slot_list[256];
+
+/* these can be gotten rid of, but for debugging they are purty */
+extern u8 cpqhp_nic_irq;
+extern u8 cpqhp_disk_irq;
+
+
+/* inline functions */
+
+/*
+ * return_resource
+ *
+ * Puts node back in the resource list pointed to by head
+ *
+ */
+static inline void return_resource(struct pci_resource **head, struct pci_resource *node)
+{
+ if (!node || !head)
+ return;
+ node->next = *head;
+ *head = node;
+}
+
+static inline void set_SOGO(struct controller *ctrl)
+{
+ u16 misc;
+
+ misc = readw(ctrl->hpc_reg + MISC);
+ misc = (misc | 0x0001) & 0xFFFB;
+ writew(misc, ctrl->hpc_reg + MISC);
+}
+
+
+static inline void amber_LED_on(struct controller *ctrl, u8 slot)
+{
+ u32 led_control;
+
+ led_control = readl(ctrl->hpc_reg + LED_CONTROL);
+ led_control |= (0x01010000L << slot);
+ writel(led_control, ctrl->hpc_reg + LED_CONTROL);
+}
+
+
+static inline void amber_LED_off(struct controller *ctrl, u8 slot)
+{
+ u32 led_control;
+
+ led_control = readl(ctrl->hpc_reg + LED_CONTROL);
+ led_control &= ~(0x01010000L << slot);
+ writel(led_control, ctrl->hpc_reg + LED_CONTROL);
+}
+
+
+static inline int read_amber_LED(struct controller *ctrl, u8 slot)
+{
+ u32 led_control;
+
+ led_control = readl(ctrl->hpc_reg + LED_CONTROL);
+ led_control &= (0x01010000L << slot);
+
+ return led_control ? 1 : 0;
+}
+
+
+static inline void green_LED_on(struct controller *ctrl, u8 slot)
+{
+ u32 led_control;
+
+ led_control = readl(ctrl->hpc_reg + LED_CONTROL);
+ led_control |= 0x0101L << slot;
+ writel(led_control, ctrl->hpc_reg + LED_CONTROL);
+}
+
+static inline void green_LED_off(struct controller *ctrl, u8 slot)
+{
+ u32 led_control;
+
+ led_control = readl(ctrl->hpc_reg + LED_CONTROL);
+ led_control &= ~(0x0101L << slot);
+ writel(led_control, ctrl->hpc_reg + LED_CONTROL);
+}
+
+
+static inline void green_LED_blink(struct controller *ctrl, u8 slot)
+{
+ u32 led_control;
+
+ led_control = readl(ctrl->hpc_reg + LED_CONTROL);
+ led_control &= ~(0x0101L << slot);
+ led_control |= (0x0001L << slot);
+ writel(led_control, ctrl->hpc_reg + LED_CONTROL);
+}
+
+
+static inline void slot_disable(struct controller *ctrl, u8 slot)
+{
+ u8 slot_enable;
+
+ slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE);
+ slot_enable &= ~(0x01 << slot);
+ writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE);
+}
+
+
+static inline void slot_enable(struct controller *ctrl, u8 slot)
+{
+ u8 slot_enable;
+
+ slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE);
+ slot_enable |= (0x01 << slot);
+ writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE);
+}
+
+
+static inline u8 is_slot_enabled(struct controller *ctrl, u8 slot)
+{
+ u8 slot_enable;
+
+ slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE);
+ slot_enable &= (0x01 << slot);
+ return slot_enable ? 1 : 0;
+}
+
+
+static inline u8 read_slot_enable(struct controller *ctrl)
+{
+ return readb(ctrl->hpc_reg + SLOT_ENABLE);
+}
+
+
+/*
+ * get_controller_speed - find the current frequency/mode of controller.
+ *
+ * @ctrl: controller to get frequency/mode for.
+ *
+ * Returns controller speed.
+ *
+ */
+static inline u8 get_controller_speed(struct controller *ctrl)
+{
+ u8 curr_freq;
+ u16 misc;
+
+ if (ctrl->pcix_support) {
+ curr_freq = readb(ctrl->hpc_reg + NEXT_CURR_FREQ);
+ if ((curr_freq & 0xB0) == 0xB0)
+ return PCI_SPEED_133MHz_PCIX;
+ if ((curr_freq & 0xA0) == 0xA0)
+ return PCI_SPEED_100MHz_PCIX;
+ if ((curr_freq & 0x90) == 0x90)
+ return PCI_SPEED_66MHz_PCIX;
+ if (curr_freq & 0x10)
+ return PCI_SPEED_66MHz;
+
+ return PCI_SPEED_33MHz;
+ }
+
+ misc = readw(ctrl->hpc_reg + MISC);
+ return (misc & 0x0800) ? PCI_SPEED_66MHz : PCI_SPEED_33MHz;
+}
+
+
+/*
+ * get_adapter_speed - find the max supported frequency/mode of adapter.
+ *
+ * @ctrl: hotplug controller.
+ * @hp_slot: hotplug slot where adapter is installed.
+ *
+ * Returns adapter speed.
+ *
+ */
+static inline u8 get_adapter_speed(struct controller *ctrl, u8 hp_slot)
+{
+ u32 temp_dword = readl(ctrl->hpc_reg + NON_INT_INPUT);
+ dbg("slot: %d, PCIXCAP: %8x\n", hp_slot, temp_dword);
+ if (ctrl->pcix_support) {
+ if (temp_dword & (0x10000 << hp_slot))
+ return PCI_SPEED_133MHz_PCIX;
+ if (temp_dword & (0x100 << hp_slot))
+ return PCI_SPEED_66MHz_PCIX;
+ }
+
+ if (temp_dword & (0x01 << hp_slot))
+ return PCI_SPEED_66MHz;
+
+ return PCI_SPEED_33MHz;
+}
+
+static inline void enable_slot_power(struct controller *ctrl, u8 slot)
+{
+ u8 slot_power;
+
+ slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
+ slot_power |= (0x01 << slot);
+ writeb(slot_power, ctrl->hpc_reg + SLOT_POWER);
+}
+
+static inline void disable_slot_power(struct controller *ctrl, u8 slot)
+{
+ u8 slot_power;
+
+ slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
+ slot_power &= ~(0x01 << slot);
+ writeb(slot_power, ctrl->hpc_reg + SLOT_POWER);
+}
+
+
+static inline int cpq_get_attention_status(struct controller *ctrl, struct slot *slot)
+{
+ u8 hp_slot;
+
+ hp_slot = slot->device - ctrl->slot_device_offset;
+
+ return read_amber_LED(ctrl, hp_slot);
+}
+
+
+static inline int get_slot_enabled(struct controller *ctrl, struct slot *slot)
+{
+ u8 hp_slot;
+
+ hp_slot = slot->device - ctrl->slot_device_offset;
+
+ return is_slot_enabled(ctrl, hp_slot);
+}
+
+
+static inline int cpq_get_latch_status(struct controller *ctrl, struct slot *slot)
+{
+ u32 status;
+ u8 hp_slot;
+
+ hp_slot = slot->device - ctrl->slot_device_offset;
+ dbg("%s: slot->device = %d, ctrl->slot_device_offset = %d \n",
+ __FUNCTION__, slot->device, ctrl->slot_device_offset);
+
+ status = (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot));
+
+ return(status == 0) ? 1 : 0;
+}
+
+
+static inline int get_presence_status(struct controller *ctrl, struct slot *slot)
+{
+ int presence_save = 0;
+ u8 hp_slot;
+ u32 tempdword;
+
+ hp_slot = slot->device - ctrl->slot_device_offset;
+
+ tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
+ presence_save = (int) ((((~tempdword) >> 23) | ((~tempdword) >> 15)) >> hp_slot) & 0x02;
+
+ return presence_save;
+}
+
+#define SLOT_NAME_SIZE 10
+
+static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot)
+{
+ snprintf(buffer, buffer_size, "%d", slot->number);
+}
+
+
+static inline int wait_for_ctrl_irq(struct controller *ctrl)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int retval = 0;
+
+ dbg("%s - start\n", __FUNCTION__);
+ add_wait_queue(&ctrl->queue, &wait);
+ /* Sleep for up to 1 second to wait for the LED to change. */
+ msleep_interruptible(1000);
+ remove_wait_queue(&ctrl->queue, &wait);
+ if (signal_pending(current))
+ retval = -EINTR;
+
+ dbg("%s - end\n", __FUNCTION__);
+ return retval;
+}
+
+#endif
+
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c
new file mode 100644
index 000000000000..afbccfa5217d
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp_core.c
@@ -0,0 +1,1509 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 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 <greg@kroah.com>
+ *
+ * Jan 12, 2003 - Added 66/100/133MHz PCI-X support,
+ * Torben Mathiasen <torben.mathiasen@hp.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/uaccess.h>
+
+#include "cpqphp.h"
+#include "cpqphp_nvram.h"
+#include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependent we are... */
+
+
+/* Global variables */
+int cpqhp_debug;
+int cpqhp_legacy_mode;
+struct controller *cpqhp_ctrl_list; /* = NULL */
+struct pci_func *cpqhp_slot_list[256];
+
+/* local variables */
+static void __iomem *smbios_table;
+static void __iomem *smbios_start;
+static void __iomem *cpqhp_rom_start;
+static int power_mode;
+static int debug;
+
+#define DRIVER_VERSION "0.9.8"
+#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>"
+#define DRIVER_DESC "Compaq Hot Plug PCI Controller Driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(power_mode, bool, 0644);
+MODULE_PARM_DESC(power_mode, "Power mode enabled or not");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+
+#define CPQHPC_MODULE_MINOR 208
+
+static int one_time_init (void);
+static int set_attention_status (struct hotplug_slot *slot, u8 value);
+static int process_SI (struct hotplug_slot *slot);
+static int process_SS (struct hotplug_slot *slot);
+static int hardware_test (struct hotplug_slot *slot, u32 value);
+static int get_power_status (struct hotplug_slot *slot, u8 *value);
+static int get_attention_status (struct hotplug_slot *slot, u8 *value);
+static int get_latch_status (struct hotplug_slot *slot, u8 *value);
+static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
+static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
+static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
+
+static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .set_attention_status = set_attention_status,
+ .enable_slot = process_SI,
+ .disable_slot = process_SS,
+ .hardware_test = hardware_test,
+ .get_power_status = get_power_status,
+ .get_attention_status = get_attention_status,
+ .get_latch_status = get_latch_status,
+ .get_adapter_status = get_adapter_status,
+ .get_max_bus_speed = get_max_bus_speed,
+ .get_cur_bus_speed = get_cur_bus_speed,
+};
+
+
+static inline int is_slot64bit(struct slot *slot)
+{
+ return (readb(slot->p_sm_slot + SMBIOS_SLOT_WIDTH) == 0x06) ? 1 : 0;
+}
+
+static inline int is_slot66mhz(struct slot *slot)
+{
+ return (readb(slot->p_sm_slot + SMBIOS_SLOT_TYPE) == 0x0E) ? 1 : 0;
+}
+
+/**
+ * detect_SMBIOS_pointer - find the System Management BIOS Table in mem region.
+ *
+ * @begin: begin pointer for region to be scanned.
+ * @end: end pointer for region to be scanned.
+ *
+ * Returns pointer to the head of the SMBIOS tables (or NULL)
+ *
+ */
+static void __iomem * detect_SMBIOS_pointer(void __iomem *begin, void __iomem *end)
+{
+ void __iomem *fp;
+ void __iomem *endp;
+ u8 temp1, temp2, temp3, temp4;
+ int status = 0;
+
+ endp = (end - sizeof(u32) + 1);
+
+ for (fp = begin; fp <= endp; fp += 16) {
+ temp1 = readb(fp);
+ temp2 = readb(fp+1);
+ temp3 = readb(fp+2);
+ temp4 = readb(fp+3);
+ if (temp1 == '_' &&
+ temp2 == 'S' &&
+ temp3 == 'M' &&
+ temp4 == '_') {
+ status = 1;
+ break;
+ }
+ }
+
+ if (!status)
+ fp = NULL;
+
+ dbg("Discovered SMBIOS Entry point at %p\n", fp);
+
+ return fp;
+}
+
+/**
+ * init_SERR - Initializes the per slot SERR generation.
+ *
+ * For unexpected switch opens
+ *
+ */
+static int init_SERR(struct controller * ctrl)
+{
+ u32 tempdword;
+ u32 number_of_slots;
+ u8 physical_slot;
+
+ if (!ctrl)
+ return 1;
+
+ tempdword = ctrl->first_slot;
+
+ number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
+ // Loop through slots
+ while (number_of_slots) {
+ physical_slot = tempdword;
+ writeb(0, ctrl->hpc_reg + SLOT_SERR);
+ tempdword++;
+ number_of_slots--;
+ }
+
+ return 0;
+}
+
+
+/* nice debugging output */
+static int pci_print_IRQ_route (void)
+{
+ struct irq_routing_table *routing_table;
+ int len;
+ int loop;
+
+ u8 tbus, tdevice, tslot;
+
+ routing_table = pcibios_get_irq_routing_table();
+ if (routing_table == NULL) {
+ err("No BIOS Routing Table??? Not good\n");
+ return -ENOMEM;
+ }
+
+ len = (routing_table->size - sizeof(struct irq_routing_table)) /
+ sizeof(struct irq_info);
+ // Make sure I got at least one entry
+ if (len == 0) {
+ kfree(routing_table);
+ return -1;
+ }
+
+ dbg("bus dev func slot\n");
+
+ for (loop = 0; loop < len; ++loop) {
+ tbus = routing_table->slots[loop].bus;
+ tdevice = routing_table->slots[loop].devfn;
+ tslot = routing_table->slots[loop].slot;
+ dbg("%d %d %d %d\n", tbus, tdevice >> 3, tdevice & 0x7, tslot);
+
+ }
+ kfree(routing_table);
+ return 0;
+}
+
+
+/**
+ * get_subsequent_smbios_entry: get the next entry from bios table.
+ *
+ * Gets the first entry if previous == NULL
+ * Otherwise, returns the next entry
+ * Uses global SMBIOS Table pointer
+ *
+ * @curr: %NULL or pointer to previously returned structure
+ *
+ * returns a pointer to an SMBIOS structure or NULL if none found
+ */
+static void __iomem *get_subsequent_smbios_entry(void __iomem *smbios_start,
+ void __iomem *smbios_table,
+ void __iomem *curr)
+{
+ u8 bail = 0;
+ u8 previous_byte = 1;
+ void __iomem *p_temp;
+ void __iomem *p_max;
+
+ if (!smbios_table || !curr)
+ return(NULL);
+
+ // set p_max to the end of the table
+ p_max = smbios_start + readw(smbios_table + ST_LENGTH);
+
+ p_temp = curr;
+ p_temp += readb(curr + SMBIOS_GENERIC_LENGTH);
+
+ while ((p_temp < p_max) && !bail) {
+ /* Look for the double NULL terminator
+ * The first condition is the previous byte
+ * and the second is the curr */
+ if (!previous_byte && !(readb(p_temp))) {
+ bail = 1;
+ }
+
+ previous_byte = readb(p_temp);
+ p_temp++;
+ }
+
+ if (p_temp < p_max) {
+ return p_temp;
+ } else {
+ return NULL;
+ }
+}
+
+
+/**
+ * get_SMBIOS_entry
+ *
+ * @type:SMBIOS structure type to be returned
+ * @previous: %NULL or pointer to previously returned structure
+ *
+ * Gets the first entry of the specified type if previous == NULL
+ * Otherwise, returns the next entry of the given type.
+ * Uses global SMBIOS Table pointer
+ * Uses get_subsequent_smbios_entry
+ *
+ * returns a pointer to an SMBIOS structure or %NULL if none found
+ */
+static void __iomem *get_SMBIOS_entry(void __iomem *smbios_start,
+ void __iomem *smbios_table,
+ u8 type,
+ void __iomem *previous)
+{
+ if (!smbios_table)
+ return NULL;
+
+ if (!previous) {
+ previous = smbios_start;
+ } else {
+ previous = get_subsequent_smbios_entry(smbios_start,
+ smbios_table, previous);
+ }
+
+ while (previous) {
+ if (readb(previous + SMBIOS_GENERIC_TYPE) != type) {
+ previous = get_subsequent_smbios_entry(smbios_start,
+ smbios_table, previous);
+ } else {
+ break;
+ }
+ }
+
+ return previous;
+}
+
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ kfree(slot->hotplug_slot->info);
+ kfree(slot->hotplug_slot->name);
+ kfree(slot->hotplug_slot);
+ kfree(slot);
+}
+
+static int ctrl_slot_setup(struct controller *ctrl,
+ void __iomem *smbios_start,
+ void __iomem *smbios_table)
+{
+ struct slot *new_slot;
+ u8 number_of_slots;
+ u8 slot_device;
+ u8 slot_number;
+ u8 ctrl_slot;
+ u32 tempdword;
+ void __iomem *slot_entry= NULL;
+ int result = -ENOMEM;
+
+ dbg("%s\n", __FUNCTION__);
+
+ tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+ number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
+ slot_device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
+ slot_number = ctrl->first_slot;
+
+ while (number_of_slots) {
+ new_slot = kmalloc(sizeof(*new_slot), GFP_KERNEL);
+ if (!new_slot)
+ goto error;
+
+ memset(new_slot, 0, sizeof(struct slot));
+ new_slot->hotplug_slot = kmalloc(sizeof(*(new_slot->hotplug_slot)),
+ GFP_KERNEL);
+ if (!new_slot->hotplug_slot)
+ goto error_slot;
+ memset(new_slot->hotplug_slot, 0, sizeof(struct hotplug_slot));
+
+ new_slot->hotplug_slot->info =
+ kmalloc(sizeof(*(new_slot->hotplug_slot->info)),
+ GFP_KERNEL);
+ if (!new_slot->hotplug_slot->info)
+ goto error_hpslot;
+ memset(new_slot->hotplug_slot->info, 0,
+ sizeof(struct hotplug_slot_info));
+ new_slot->hotplug_slot->name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
+ if (!new_slot->hotplug_slot->name)
+ goto error_info;
+
+ new_slot->ctrl = ctrl;
+ new_slot->bus = ctrl->bus;
+ new_slot->device = slot_device;
+ new_slot->number = slot_number;
+ dbg("slot->number = %d\n",new_slot->number);
+
+ slot_entry = get_SMBIOS_entry(smbios_start, smbios_table, 9,
+ slot_entry);
+
+ while (slot_entry && (readw(slot_entry + SMBIOS_SLOT_NUMBER) != new_slot->number)) {
+ slot_entry = get_SMBIOS_entry(smbios_start,
+ smbios_table, 9, slot_entry);
+ }
+
+ new_slot->p_sm_slot = slot_entry;
+
+ init_timer(&new_slot->task_event);
+ new_slot->task_event.expires = jiffies + 5 * HZ;
+ new_slot->task_event.function = cpqhp_pushbutton_thread;
+
+ //FIXME: these capabilities aren't used but if they are
+ // they need to be correctly implemented
+ new_slot->capabilities |= PCISLOT_REPLACE_SUPPORTED;
+ new_slot->capabilities |= PCISLOT_INTERLOCK_SUPPORTED;
+
+ if (is_slot64bit(new_slot))
+ new_slot->capabilities |= PCISLOT_64_BIT_SUPPORTED;
+ if (is_slot66mhz(new_slot))
+ new_slot->capabilities |= PCISLOT_66_MHZ_SUPPORTED;
+ if (ctrl->speed == PCI_SPEED_66MHz)
+ new_slot->capabilities |= PCISLOT_66_MHZ_OPERATION;
+
+ ctrl_slot = slot_device - (readb(ctrl->hpc_reg + SLOT_MASK) >> 4);
+
+ // Check presence
+ new_slot->capabilities |= ((((~tempdword) >> 23) | ((~tempdword) >> 15)) >> ctrl_slot) & 0x02;
+ // Check the switch state
+ new_slot->capabilities |= ((~tempdword & 0xFF) >> ctrl_slot) & 0x01;
+ // Check the slot enable
+ new_slot->capabilities |= ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04;
+
+ /* register this slot with the hotplug pci core */
+ new_slot->hotplug_slot->release = &release_slot;
+ new_slot->hotplug_slot->private = new_slot;
+ make_slot_name(new_slot->hotplug_slot->name, SLOT_NAME_SIZE, new_slot);
+ new_slot->hotplug_slot->ops = &cpqphp_hotplug_slot_ops;
+
+ new_slot->hotplug_slot->info->power_status = get_slot_enabled(ctrl, new_slot);
+ new_slot->hotplug_slot->info->attention_status = cpq_get_attention_status(ctrl, new_slot);
+ new_slot->hotplug_slot->info->latch_status = cpq_get_latch_status(ctrl, new_slot);
+ new_slot->hotplug_slot->info->adapter_status = get_presence_status(ctrl, new_slot);
+
+ dbg ("registering bus %d, dev %d, number %d, "
+ "ctrl->slot_device_offset %d, slot %d\n",
+ new_slot->bus, new_slot->device,
+ new_slot->number, ctrl->slot_device_offset,
+ slot_number);
+ result = pci_hp_register (new_slot->hotplug_slot);
+ if (result) {
+ err ("pci_hp_register failed with error %d\n", result);
+ goto error_name;
+ }
+
+ new_slot->next = ctrl->slot;
+ ctrl->slot = new_slot;
+
+ number_of_slots--;
+ slot_device++;
+ slot_number++;
+ }
+
+ return 0;
+
+error_name:
+ kfree(new_slot->hotplug_slot->name);
+error_info:
+ kfree(new_slot->hotplug_slot->info);
+error_hpslot:
+ kfree(new_slot->hotplug_slot);
+error_slot:
+ kfree(new_slot);
+error:
+ return result;
+}
+
+static int ctrl_slot_cleanup (struct controller * ctrl)
+{
+ struct slot *old_slot, *next_slot;
+
+ old_slot = ctrl->slot;
+ ctrl->slot = NULL;
+
+ while (old_slot) {
+ /* memory will be freed by the release_slot callback */
+ next_slot = old_slot->next;
+ pci_hp_deregister (old_slot->hotplug_slot);
+ old_slot = next_slot;
+ }
+
+ //Free IRQ associated with hot plug device
+ free_irq(ctrl->interrupt, ctrl);
+ //Unmap the memory
+ iounmap(ctrl->hpc_reg);
+ //Finally reclaim PCI mem
+ release_mem_region(pci_resource_start(ctrl->pci_dev, 0),
+ pci_resource_len(ctrl->pci_dev, 0));
+
+ return(0);
+}
+
+
+//============================================================================
+// function: get_slot_mapping
+//
+// Description: Attempts to determine a logical slot mapping for a PCI
+// device. Won't work for more than one PCI-PCI bridge
+// in a slot.
+//
+// Input: u8 bus_num - bus number of PCI device
+// u8 dev_num - device number of PCI device
+// u8 *slot - Pointer to u8 where slot number will
+// be returned
+//
+// Output: SUCCESS or FAILURE
+//=============================================================================
+static int
+get_slot_mapping(struct pci_bus *bus, u8 bus_num, u8 dev_num, u8 *slot)
+{
+ struct irq_routing_table *PCIIRQRoutingInfoLength;
+ u32 work;
+ long len;
+ long loop;
+
+ u8 tbus, tdevice, tslot, bridgeSlot;
+
+ dbg("%s: %p, %d, %d, %p\n", __FUNCTION__, bus, bus_num, dev_num, slot);
+
+ bridgeSlot = 0xFF;
+
+ PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table();
+ if (!PCIIRQRoutingInfoLength)
+ return -1;
+
+ len = (PCIIRQRoutingInfoLength->size -
+ sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
+ // Make sure I got at least one entry
+ if (len == 0) {
+ kfree(PCIIRQRoutingInfoLength);
+ return -1;
+ }
+
+ for (loop = 0; loop < len; ++loop) {
+ tbus = PCIIRQRoutingInfoLength->slots[loop].bus;
+ tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn >> 3;
+ tslot = PCIIRQRoutingInfoLength->slots[loop].slot;
+
+ if ((tbus == bus_num) && (tdevice == dev_num)) {
+ *slot = tslot;
+ kfree(PCIIRQRoutingInfoLength);
+ return 0;
+ } else {
+ /* Did not get a match on the target PCI device. Check
+ * if the current IRQ table entry is a PCI-to-PCI bridge
+ * device. If so, and it's secondary bus matches the
+ * bus number for the target device, I need to save the
+ * bridge's slot number. If I can not find an entry for
+ * the target device, I will have to assume it's on the
+ * other side of the bridge, and assign it the bridge's
+ * slot. */
+ bus->number = tbus;
+ pci_bus_read_config_dword(bus, PCI_DEVFN(tdevice, 0),
+ PCI_REVISION_ID, &work);
+
+ if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
+ pci_bus_read_config_dword(bus,
+ PCI_DEVFN(tdevice, 0),
+ PCI_PRIMARY_BUS, &work);
+ // See if bridge's secondary bus matches target bus.
+ if (((work >> 8) & 0x000000FF) == (long) bus_num) {
+ bridgeSlot = tslot;
+ }
+ }
+ }
+
+ }
+
+ // If we got here, we didn't find an entry in the IRQ mapping table
+ // for the target PCI device. If we did determine that the target
+ // device is on the other side of a PCI-to-PCI bridge, return the
+ // slot number for the bridge.
+ if (bridgeSlot != 0xFF) {
+ *slot = bridgeSlot;
+ kfree(PCIIRQRoutingInfoLength);
+ return 0;
+ }
+ kfree(PCIIRQRoutingInfoLength);
+ // Couldn't find an entry in the routing table for this PCI device
+ return -1;
+}
+
+
+/**
+ * cpqhp_set_attention_status - Turns the Amber LED for a slot on or off
+ *
+ */
+static int
+cpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func,
+ u32 status)
+{
+ u8 hp_slot;
+
+ if (func == NULL)
+ return(1);
+
+ hp_slot = func->device - ctrl->slot_device_offset;
+
+ // Wait for exclusive access to hardware
+ down(&ctrl->crit_sect);
+
+ if (status == 1) {
+ amber_LED_on (ctrl, hp_slot);
+ } else if (status == 0) {
+ amber_LED_off (ctrl, hp_slot);
+ } else {
+ // Done with exclusive hardware access
+ up(&ctrl->crit_sect);
+ return(1);
+ }
+
+ set_SOGO(ctrl);
+
+ // Wait for SOBS to be unset
+ wait_for_ctrl_irq (ctrl);
+
+ // Done with exclusive hardware access
+ up(&ctrl->crit_sect);
+
+ return(0);
+}
+
+
+/**
+ * set_attention_status - Turns the Amber LED for a slot on or off
+ *
+ */
+static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
+{
+ struct pci_func *slot_func;
+ struct slot *slot = hotplug_slot->private;
+ struct controller *ctrl = slot->ctrl;
+ u8 bus;
+ u8 devfn;
+ u8 device;
+ u8 function;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)
+ return -ENODEV;
+
+ device = devfn >> 3;
+ function = devfn & 0x7;
+ dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function);
+
+ slot_func = cpqhp_slot_find(bus, device, function);
+ if (!slot_func)
+ return -ENODEV;
+
+ return cpqhp_set_attention_status(ctrl, slot_func, status);
+}
+
+
+static int process_SI(struct hotplug_slot *hotplug_slot)
+{
+ struct pci_func *slot_func;
+ struct slot *slot = hotplug_slot->private;
+ struct controller *ctrl = slot->ctrl;
+ u8 bus;
+ u8 devfn;
+ u8 device;
+ u8 function;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)
+ return -ENODEV;
+
+ device = devfn >> 3;
+ function = devfn & 0x7;
+ dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function);
+
+ slot_func = cpqhp_slot_find(bus, device, function);
+ if (!slot_func)
+ return -ENODEV;
+
+ slot_func->bus = bus;
+ slot_func->device = device;
+ slot_func->function = function;
+ slot_func->configured = 0;
+ dbg("board_added(%p, %p)\n", slot_func, ctrl);
+ return cpqhp_process_SI(ctrl, slot_func);
+}
+
+
+static int process_SS(struct hotplug_slot *hotplug_slot)
+{
+ struct pci_func *slot_func;
+ struct slot *slot = hotplug_slot->private;
+ struct controller *ctrl = slot->ctrl;
+ u8 bus;
+ u8 devfn;
+ u8 device;
+ u8 function;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ if (cpqhp_get_bus_dev(ctrl, &bus, &devfn, slot->number) == -1)
+ return -ENODEV;
+
+ device = devfn >> 3;
+ function = devfn & 0x7;
+ dbg("bus, dev, fn = %d, %d, %d\n", bus, device, function);
+
+ slot_func = cpqhp_slot_find(bus, device, function);
+ if (!slot_func)
+ return -ENODEV;
+
+ dbg("In %s, slot_func = %p, ctrl = %p\n", __FUNCTION__, slot_func, ctrl);
+ return cpqhp_process_SS(ctrl, slot_func);
+}
+
+
+static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
+{
+ struct slot *slot = hotplug_slot->private;
+ struct controller *ctrl = slot->ctrl;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ return cpqhp_hardware_test(ctrl, value);
+}
+
+
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ struct controller *ctrl = slot->ctrl;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ *value = get_slot_enabled(ctrl, slot);
+ return 0;
+}
+
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ struct controller *ctrl = slot->ctrl;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ *value = cpq_get_attention_status(ctrl, slot);
+ return 0;
+}
+
+static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ struct controller *ctrl = slot->ctrl;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ *value = cpq_get_latch_status(ctrl, slot);
+
+ return 0;
+}
+
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ struct controller *ctrl = slot->ctrl;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ *value = get_presence_status(ctrl, slot);
+
+ return 0;
+}
+
+static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ struct controller *ctrl = slot->ctrl;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ *value = ctrl->speed_capability;
+
+ return 0;
+}
+
+static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ struct controller *ctrl = slot->ctrl;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ *value = ctrl->speed;
+
+ return 0;
+}
+
+static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ u8 num_of_slots = 0;
+ u8 hp_slot = 0;
+ u8 device;
+ u8 rev;
+ u8 bus_cap;
+ u16 temp_word;
+ u16 vendor_id;
+ u16 subsystem_vid;
+ u16 subsystem_deviceid;
+ u32 rc;
+ struct controller *ctrl;
+ struct pci_func *func;
+
+ // Need to read VID early b/c it's used to differentiate CPQ and INTC discovery
+ rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id);
+ if (rc || ((vendor_id != PCI_VENDOR_ID_COMPAQ) && (vendor_id != PCI_VENDOR_ID_INTEL))) {
+ err(msg_HPC_non_compaq_or_intel);
+ return -ENODEV;
+ }
+ dbg("Vendor ID: %x\n", vendor_id);
+
+ rc = pci_read_config_byte(pdev, PCI_REVISION_ID, &rev);
+ dbg("revision: %d\n", rev);
+ if (rc || ((vendor_id == PCI_VENDOR_ID_COMPAQ) && (!rev))) {
+ err(msg_HPC_rev_error);
+ return -ENODEV;
+ }
+
+ /* Check for the proper subsytem ID's
+ * Intel uses a different SSID programming model than Compaq.
+ * For Intel, each SSID bit identifies a PHP capability.
+ * Also Intel HPC's may have RID=0.
+ */
+ if ((rev > 2) || (vendor_id == PCI_VENDOR_ID_INTEL)) {
+ // TODO: This code can be made to support non-Compaq or Intel subsystem IDs
+ rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vid);
+ if (rc) {
+ err("%s : pci_read_config_word failed\n", __FUNCTION__);
+ return rc;
+ }
+ dbg("Subsystem Vendor ID: %x\n", subsystem_vid);
+ if ((subsystem_vid != PCI_VENDOR_ID_COMPAQ) && (subsystem_vid != PCI_VENDOR_ID_INTEL)) {
+ err(msg_HPC_non_compaq_or_intel);
+ return -ENODEV;
+ }
+
+ ctrl = (struct controller *) kmalloc(sizeof(struct controller), GFP_KERNEL);
+ if (!ctrl) {
+ err("%s : out of memory\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+ memset(ctrl, 0, sizeof(struct controller));
+
+ rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsystem_deviceid);
+ if (rc) {
+ err("%s : pci_read_config_word failed\n", __FUNCTION__);
+ goto err_free_ctrl;
+ }
+
+ info("Hot Plug Subsystem Device ID: %x\n", subsystem_deviceid);
+
+ /* Set Vendor ID, so it can be accessed later from other functions */
+ ctrl->vendor_id = vendor_id;
+
+ switch (subsystem_vid) {
+ case PCI_VENDOR_ID_COMPAQ:
+ if (rev >= 0x13) { /* CIOBX */
+ ctrl->push_flag = 1;
+ ctrl->slot_switch_type = 1;
+ ctrl->push_button = 1;
+ ctrl->pci_config_space = 1;
+ ctrl->defeature_PHP = 1;
+ ctrl->pcix_support = 1;
+ ctrl->pcix_speed_capability = 1;
+ pci_read_config_byte(pdev, 0x41, &bus_cap);
+ if (bus_cap & 0x80) {
+ dbg("bus max supports 133MHz PCI-X\n");
+ ctrl->speed_capability = PCI_SPEED_133MHz_PCIX;
+ break;
+ }
+ if (bus_cap & 0x40) {
+ dbg("bus max supports 100MHz PCI-X\n");
+ ctrl->speed_capability = PCI_SPEED_100MHz_PCIX;
+ break;
+ }
+ if (bus_cap & 20) {
+ dbg("bus max supports 66MHz PCI-X\n");
+ ctrl->speed_capability = PCI_SPEED_66MHz_PCIX;
+ break;
+ }
+ if (bus_cap & 10) {
+ dbg("bus max supports 66MHz PCI\n");
+ ctrl->speed_capability = PCI_SPEED_66MHz;
+ break;
+ }
+
+ break;
+ }
+
+ switch (subsystem_deviceid) {
+ case PCI_SUB_HPC_ID:
+ /* Original 6500/7000 implementation */
+ ctrl->slot_switch_type = 1;
+ ctrl->speed_capability = PCI_SPEED_33MHz;
+ ctrl->push_button = 0;
+ ctrl->pci_config_space = 1;
+ ctrl->defeature_PHP = 1;
+ ctrl->pcix_support = 0;
+ ctrl->pcix_speed_capability = 0;
+ break;
+ case PCI_SUB_HPC_ID2:
+ /* First Pushbutton implementation */
+ ctrl->push_flag = 1;
+ ctrl->slot_switch_type = 1;
+ ctrl->speed_capability = PCI_SPEED_33MHz;
+ ctrl->push_button = 1;
+ ctrl->pci_config_space = 1;
+ ctrl->defeature_PHP = 1;
+ ctrl->pcix_support = 0;
+ ctrl->pcix_speed_capability = 0;
+ break;
+ case PCI_SUB_HPC_ID_INTC:
+ /* Third party (6500/7000) */
+ ctrl->slot_switch_type = 1;
+ ctrl->speed_capability = PCI_SPEED_33MHz;
+ ctrl->push_button = 0;
+ ctrl->pci_config_space = 1;
+ ctrl->defeature_PHP = 1;
+ ctrl->pcix_support = 0;
+ ctrl->pcix_speed_capability = 0;
+ break;
+ case PCI_SUB_HPC_ID3:
+ /* First 66 Mhz implementation */
+ ctrl->push_flag = 1;
+ ctrl->slot_switch_type = 1;
+ ctrl->speed_capability = PCI_SPEED_66MHz;
+ ctrl->push_button = 1;
+ ctrl->pci_config_space = 1;
+ ctrl->defeature_PHP = 1;
+ ctrl->pcix_support = 0;
+ ctrl->pcix_speed_capability = 0;
+ break;
+ case PCI_SUB_HPC_ID4:
+ /* First PCI-X implementation, 100MHz */
+ ctrl->push_flag = 1;
+ ctrl->slot_switch_type = 1;
+ ctrl->speed_capability = PCI_SPEED_100MHz_PCIX;
+ ctrl->push_button = 1;
+ ctrl->pci_config_space = 1;
+ ctrl->defeature_PHP = 1;
+ ctrl->pcix_support = 1;
+ ctrl->pcix_speed_capability = 0;
+ break;
+ default:
+ err(msg_HPC_not_supported);
+ rc = -ENODEV;
+ goto err_free_ctrl;
+ }
+ break;
+
+ case PCI_VENDOR_ID_INTEL:
+ /* Check for speed capability (0=33, 1=66) */
+ if (subsystem_deviceid & 0x0001) {
+ ctrl->speed_capability = PCI_SPEED_66MHz;
+ } else {
+ ctrl->speed_capability = PCI_SPEED_33MHz;
+ }
+
+ /* Check for push button */
+ if (subsystem_deviceid & 0x0002) {
+ /* no push button */
+ ctrl->push_button = 0;
+ } else {
+ /* push button supported */
+ ctrl->push_button = 1;
+ }
+
+ /* Check for slot switch type (0=mechanical, 1=not mechanical) */
+ if (subsystem_deviceid & 0x0004) {
+ /* no switch */
+ ctrl->slot_switch_type = 0;
+ } else {
+ /* switch */
+ ctrl->slot_switch_type = 1;
+ }
+
+ /* PHP Status (0=De-feature PHP, 1=Normal operation) */
+ if (subsystem_deviceid & 0x0008) {
+ ctrl->defeature_PHP = 1; // PHP supported
+ } else {
+ ctrl->defeature_PHP = 0; // PHP not supported
+ }
+
+ /* Alternate Base Address Register Interface (0=not supported, 1=supported) */
+ if (subsystem_deviceid & 0x0010) {
+ ctrl->alternate_base_address = 1; // supported
+ } else {
+ ctrl->alternate_base_address = 0; // not supported
+ }
+
+ /* PCI Config Space Index (0=not supported, 1=supported) */
+ if (subsystem_deviceid & 0x0020) {
+ ctrl->pci_config_space = 1; // supported
+ } else {
+ ctrl->pci_config_space = 0; // not supported
+ }
+
+ /* PCI-X support */
+ if (subsystem_deviceid & 0x0080) {
+ /* PCI-X capable */
+ ctrl->pcix_support = 1;
+ /* Frequency of operation in PCI-X mode */
+ if (subsystem_deviceid & 0x0040) {
+ /* 133MHz PCI-X if bit 7 is 1 */
+ ctrl->pcix_speed_capability = 1;
+ } else {
+ /* 100MHz PCI-X if bit 7 is 1 and bit 0 is 0, */
+ /* 66MHz PCI-X if bit 7 is 1 and bit 0 is 1 */
+ ctrl->pcix_speed_capability = 0;
+ }
+ } else {
+ /* Conventional PCI */
+ ctrl->pcix_support = 0;
+ ctrl->pcix_speed_capability = 0;
+ }
+ break;
+
+ default:
+ err(msg_HPC_not_supported);
+ rc = -ENODEV;
+ goto err_free_ctrl;
+ }
+
+ } else {
+ err(msg_HPC_not_supported);
+ return -ENODEV;
+ }
+
+ // Tell the user that we found one.
+ info("Initializing the PCI hot plug controller residing on PCI bus %d\n",
+ pdev->bus->number);
+
+ dbg("Hotplug controller capabilities:\n");
+ dbg(" speed_capability %d\n", ctrl->speed_capability);
+ dbg(" slot_switch_type %s\n", ctrl->slot_switch_type ?
+ "switch present" : "no switch");
+ dbg(" defeature_PHP %s\n", ctrl->defeature_PHP ?
+ "PHP supported" : "PHP not supported");
+ dbg(" alternate_base_address %s\n", ctrl->alternate_base_address ?
+ "supported" : "not supported");
+ dbg(" pci_config_space %s\n", ctrl->pci_config_space ?
+ "supported" : "not supported");
+ dbg(" pcix_speed_capability %s\n", ctrl->pcix_speed_capability ?
+ "supported" : "not supported");
+ dbg(" pcix_support %s\n", ctrl->pcix_support ?
+ "supported" : "not supported");
+
+ ctrl->pci_dev = pdev;
+ pci_set_drvdata(pdev, ctrl);
+
+ /* make our own copy of the pci bus structure,
+ * as we like tweaking it a lot */
+ ctrl->pci_bus = kmalloc(sizeof(*ctrl->pci_bus), GFP_KERNEL);
+ if (!ctrl->pci_bus) {
+ err("out of memory\n");
+ rc = -ENOMEM;
+ goto err_free_ctrl;
+ }
+ memcpy(ctrl->pci_bus, pdev->bus, sizeof(*ctrl->pci_bus));
+
+ ctrl->bus = pdev->bus->number;
+ ctrl->rev = rev;
+ dbg("bus device function rev: %d %d %d %d\n", ctrl->bus,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), ctrl->rev);
+
+ init_MUTEX(&ctrl->crit_sect);
+ init_waitqueue_head(&ctrl->queue);
+
+ /* initialize our threads if they haven't already been started up */
+ rc = one_time_init();
+ if (rc) {
+ goto err_free_bus;
+ }
+
+ dbg("pdev = %p\n", pdev);
+ dbg("pci resource start %lx\n", pci_resource_start(pdev, 0));
+ dbg("pci resource len %lx\n", pci_resource_len(pdev, 0));
+
+ if (!request_mem_region(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0), MY_NAME)) {
+ err("cannot reserve MMIO region\n");
+ rc = -ENOMEM;
+ goto err_free_bus;
+ }
+
+ ctrl->hpc_reg = ioremap(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (!ctrl->hpc_reg) {
+ err("cannot remap MMIO region %lx @ %lx\n",
+ pci_resource_len(pdev, 0),
+ pci_resource_start(pdev, 0));
+ rc = -ENODEV;
+ goto err_free_mem_region;
+ }
+
+ // Check for 66Mhz operation
+ ctrl->speed = get_controller_speed(ctrl);
+
+
+ /********************************************************
+ *
+ * Save configuration headers for this and
+ * subordinate PCI buses
+ *
+ ********************************************************/
+
+ // find the physical slot number of the first hot plug slot
+
+ /* Get slot won't work for devices behind bridges, but
+ * in this case it will always be called for the "base"
+ * bus/dev/func of a slot.
+ * CS: this is leveraging the PCIIRQ routing code from the kernel
+ * (pci-pc.c: get_irq_routing_table) */
+ rc = get_slot_mapping(ctrl->pci_bus, pdev->bus->number,
+ (readb(ctrl->hpc_reg + SLOT_MASK) >> 4),
+ &(ctrl->first_slot));
+ dbg("get_slot_mapping: first_slot = %d, returned = %d\n",
+ ctrl->first_slot, rc);
+ if (rc) {
+ err(msg_initialization_err, rc);
+ goto err_iounmap;
+ }
+
+ // Store PCI Config Space for all devices on this bus
+ rc = cpqhp_save_config(ctrl, ctrl->bus, readb(ctrl->hpc_reg + SLOT_MASK));
+ if (rc) {
+ err("%s: unable to save PCI configuration data, error %d\n",
+ __FUNCTION__, rc);
+ goto err_iounmap;
+ }
+
+ /*
+ * Get IO, memory, and IRQ resources for new devices
+ */
+ // The next line is required for cpqhp_find_available_resources
+ ctrl->interrupt = pdev->irq;
+ if (ctrl->interrupt < 0x10) {
+ cpqhp_legacy_mode = 1;
+ dbg("System seems to be configured for Full Table Mapped MPS mode\n");
+ }
+
+ ctrl->cfgspc_irq = 0;
+ pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &ctrl->cfgspc_irq);
+
+ rc = cpqhp_find_available_resources(ctrl, cpqhp_rom_start);
+ ctrl->add_support = !rc;
+ if (rc) {
+ dbg("cpqhp_find_available_resources = 0x%x\n", rc);
+ err("unable to locate PCI configuration resources for hot plug add.\n");
+ goto err_iounmap;
+ }
+
+ /*
+ * Finish setting up the hot plug ctrl device
+ */
+ ctrl->slot_device_offset = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
+ dbg("NumSlots %d \n", ctrl->slot_device_offset);
+
+ ctrl->next_event = 0;
+
+ /* Setup the slot information structures */
+ rc = ctrl_slot_setup(ctrl, smbios_start, smbios_table);
+ if (rc) {
+ err(msg_initialization_err, 6);
+ err("%s: unable to save PCI configuration data, error %d\n",
+ __FUNCTION__, rc);
+ goto err_iounmap;
+ }
+
+ /* Mask all general input interrupts */
+ writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_MASK);
+
+ /* set up the interrupt */
+ dbg("HPC interrupt = %d \n", ctrl->interrupt);
+ if (request_irq(ctrl->interrupt, cpqhp_ctrl_intr,
+ SA_SHIRQ, MY_NAME, ctrl)) {
+ err("Can't get irq %d for the hotplug pci controller\n",
+ ctrl->interrupt);
+ rc = -ENODEV;
+ goto err_iounmap;
+ }
+
+ /* Enable Shift Out interrupt and clear it, also enable SERR on power fault */
+ temp_word = readw(ctrl->hpc_reg + MISC);
+ temp_word |= 0x4006;
+ writew(temp_word, ctrl->hpc_reg + MISC);
+
+ // Changed 05/05/97 to clear all interrupts at start
+ writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+ ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+ writel(0x0L, ctrl->hpc_reg + INT_MASK);
+
+ if (!cpqhp_ctrl_list) {
+ cpqhp_ctrl_list = ctrl;
+ ctrl->next = NULL;
+ } else {
+ ctrl->next = cpqhp_ctrl_list;
+ cpqhp_ctrl_list = ctrl;
+ }
+
+ // turn off empty slots here unless command line option "ON" set
+ // Wait for exclusive access to hardware
+ down(&ctrl->crit_sect);
+
+ num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
+
+ // find first device number for the ctrl
+ device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
+
+ while (num_of_slots) {
+ dbg("num_of_slots: %d\n", num_of_slots);
+ func = cpqhp_slot_find(ctrl->bus, device, 0);
+ if (!func)
+ break;
+
+ hp_slot = func->device - ctrl->slot_device_offset;
+ dbg("hp_slot: %d\n", hp_slot);
+
+ // We have to save the presence info for these slots
+ temp_word = ctrl->ctrl_int_comp >> 16;
+ func->presence_save = (temp_word >> hp_slot) & 0x01;
+ func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
+
+ if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
+ func->switch_save = 0;
+ } else {
+ func->switch_save = 0x10;
+ }
+
+ if (!power_mode) {
+ if (!func->is_a_board) {
+ green_LED_off(ctrl, hp_slot);
+ slot_disable(ctrl, hp_slot);
+ }
+ }
+
+ device++;
+ num_of_slots--;
+ }
+
+ if (!power_mode) {
+ set_SOGO(ctrl);
+ // Wait for SOBS to be unset
+ wait_for_ctrl_irq(ctrl);
+ }
+
+ rc = init_SERR(ctrl);
+ if (rc) {
+ err("init_SERR failed\n");
+ up(&ctrl->crit_sect);
+ goto err_free_irq;
+ }
+
+ // Done with exclusive hardware access
+ up(&ctrl->crit_sect);
+
+ cpqhp_create_ctrl_files(ctrl);
+
+ return 0;
+
+err_free_irq:
+ free_irq(ctrl->interrupt, ctrl);
+err_iounmap:
+ iounmap(ctrl->hpc_reg);
+err_free_mem_region:
+ release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+err_free_bus:
+ kfree(ctrl->pci_bus);
+err_free_ctrl:
+ kfree(ctrl);
+ return rc;
+}
+
+
+static int one_time_init(void)
+{
+ int loop;
+ int retval = 0;
+ static int initialized = 0;
+
+ if (initialized)
+ return 0;
+
+ power_mode = 0;
+
+ retval = pci_print_IRQ_route();
+ if (retval)
+ goto error;
+
+ dbg("Initialize + Start the notification mechanism \n");
+
+ retval = cpqhp_event_start_thread();
+ if (retval)
+ goto error;
+
+ dbg("Initialize slot lists\n");
+ for (loop = 0; loop < 256; loop++) {
+ cpqhp_slot_list[loop] = NULL;
+ }
+
+ // FIXME: We also need to hook the NMI handler eventually.
+ // this also needs to be worked with Christoph
+ // register_NMI_handler();
+
+ // Map rom address
+ cpqhp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN);
+ if (!cpqhp_rom_start) {
+ err ("Could not ioremap memory region for ROM\n");
+ retval = -EIO;
+ goto error;
+ }
+
+ /* Now, map the int15 entry point if we are on compaq specific hardware */
+ compaq_nvram_init(cpqhp_rom_start);
+
+ /* Map smbios table entry point structure */
+ smbios_table = detect_SMBIOS_pointer(cpqhp_rom_start,
+ cpqhp_rom_start + ROM_PHY_LEN);
+ if (!smbios_table) {
+ err ("Could not find the SMBIOS pointer in memory\n");
+ retval = -EIO;
+ goto error_rom_start;
+ }
+
+ smbios_start = ioremap(readl(smbios_table + ST_ADDRESS),
+ readw(smbios_table + ST_LENGTH));
+ if (!smbios_start) {
+ err ("Could not ioremap memory region taken from SMBIOS values\n");
+ retval = -EIO;
+ goto error_smbios_start;
+ }
+
+ initialized = 1;
+
+ return retval;
+
+error_smbios_start:
+ iounmap(smbios_start);
+error_rom_start:
+ iounmap(cpqhp_rom_start);
+error:
+ return retval;
+}
+
+
+static void __exit unload_cpqphpd(void)
+{
+ struct pci_func *next;
+ struct pci_func *TempSlot;
+ int loop;
+ u32 rc;
+ struct controller *ctrl;
+ struct controller *tctrl;
+ struct pci_resource *res;
+ struct pci_resource *tres;
+
+ rc = compaq_nvram_store(cpqhp_rom_start);
+
+ ctrl = cpqhp_ctrl_list;
+
+ while (ctrl) {
+ if (ctrl->hpc_reg) {
+ u16 misc;
+ rc = read_slot_enable (ctrl);
+
+ writeb(0, ctrl->hpc_reg + SLOT_SERR);
+ writel(0xFFFFFFC0L | ~rc, ctrl->hpc_reg + INT_MASK);
+
+ misc = readw(ctrl->hpc_reg + MISC);
+ misc &= 0xFFFD;
+ writew(misc, ctrl->hpc_reg + MISC);
+ }
+
+ ctrl_slot_cleanup(ctrl);
+
+ res = ctrl->io_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = ctrl->mem_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = ctrl->p_mem_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = ctrl->bus_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ kfree (ctrl->pci_bus);
+
+ tctrl = ctrl;
+ ctrl = ctrl->next;
+ kfree(tctrl);
+ }
+
+ for (loop = 0; loop < 256; loop++) {
+ next = cpqhp_slot_list[loop];
+ while (next != NULL) {
+ res = next->io_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = next->mem_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = next->p_mem_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = next->bus_head;
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ TempSlot = next;
+ next = next->next;
+ kfree(TempSlot);
+ }
+ }
+
+ // Stop the notification mechanism
+ cpqhp_event_stop_thread();
+
+ //unmap the rom address
+ if (cpqhp_rom_start)
+ iounmap(cpqhp_rom_start);
+ if (smbios_start)
+ iounmap(smbios_start);
+}
+
+
+
+static struct pci_device_id hpcd_pci_tbl[] = {
+ {
+ /* handle any PCI Hotplug controller */
+ .class = ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00),
+ .class_mask = ~0,
+
+ /* no matter who makes it */
+ .vendor = PCI_ANY_ID,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+
+ }, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, hpcd_pci_tbl);
+
+
+
+static struct pci_driver cpqhpc_driver = {
+ .name = "compaq_pci_hotplug",
+ .id_table = hpcd_pci_tbl,
+ .probe = cpqhpc_probe,
+ /* remove: cpqhpc_remove_one, */
+};
+
+
+
+static int __init cpqhpc_init(void)
+{
+ int result;
+
+ cpqhp_debug = debug;
+
+ info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
+ result = pci_register_driver(&cpqhpc_driver);
+ dbg("pci_register_driver = %d\n", result);
+ return result;
+}
+
+
+static void __exit cpqhpc_cleanup(void)
+{
+ dbg("unload_cpqphpd()\n");
+ unload_cpqphpd();
+
+ dbg("pci_unregister_driver\n");
+ pci_unregister_driver(&cpqhpc_driver);
+}
+
+
+module_init(cpqhpc_init);
+module_exit(cpqhpc_cleanup);
+
+
diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c
new file mode 100644
index 000000000000..10a5a7674a8a
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp_ctrl.c
@@ -0,0 +1,3096 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 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 <greg@kroah.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/smp_lock.h>
+#include <linux/pci.h>
+#include "cpqphp.h"
+
+static u32 configure_new_device(struct controller* ctrl, struct pci_func *func,
+ u8 behind_bridge, struct resource_lists *resources);
+static int configure_new_function(struct controller* ctrl, struct pci_func *func,
+ u8 behind_bridge, struct resource_lists *resources);
+static void interrupt_event_handler(struct controller *ctrl);
+
+static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */
+static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */
+static int event_finished;
+static unsigned long pushbutton_pending; /* = 0 */
+
+/* things needed for the long_delay function */
+static struct semaphore delay_sem;
+static wait_queue_head_t delay_wait;
+
+/* delay is in jiffies to wait for */
+static void long_delay(int delay)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ /* only allow 1 customer into the delay queue at once
+ * yes this makes some people wait even longer, but who really cares?
+ * this is for _huge_ delays to make the hardware happy as the
+ * signals bounce around
+ */
+ down (&delay_sem);
+
+ init_waitqueue_head(&delay_wait);
+
+ add_wait_queue(&delay_wait, &wait);
+ msleep_interruptible(jiffies_to_msecs(delay));
+ remove_wait_queue(&delay_wait, &wait);
+
+ up(&delay_sem);
+}
+
+
+/* FIXME: The following line needs to be somewhere else... */
+#define WRONG_BUS_FREQUENCY 0x07
+static u8 handle_switch_change(u8 change, struct controller * ctrl)
+{
+ int hp_slot;
+ u8 rc = 0;
+ u16 temp_word;
+ struct pci_func *func;
+ struct event_info *taskInfo;
+
+ if (!change)
+ return 0;
+
+ /* Switch Change */
+ dbg("cpqsbd: Switch interrupt received.\n");
+
+ for (hp_slot = 0; hp_slot < 6; hp_slot++) {
+ if (change & (0x1L << hp_slot)) {
+ /**********************************
+ * this one changed.
+ **********************************/
+ func = cpqhp_slot_find(ctrl->bus,
+ (hp_slot + ctrl->slot_device_offset), 0);
+
+ /* this is the structure that tells the worker thread
+ *what to do */
+ taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+ ctrl->next_event = (ctrl->next_event + 1) % 10;
+ taskInfo->hp_slot = hp_slot;
+
+ rc++;
+
+ temp_word = ctrl->ctrl_int_comp >> 16;
+ func->presence_save = (temp_word >> hp_slot) & 0x01;
+ func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
+
+ if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
+ /**********************************
+ * Switch opened
+ **********************************/
+
+ func->switch_save = 0;
+
+ taskInfo->event_type = INT_SWITCH_OPEN;
+ } else {
+ /**********************************
+ * Switch closed
+ **********************************/
+
+ func->switch_save = 0x10;
+
+ taskInfo->event_type = INT_SWITCH_CLOSE;
+ }
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * cpqhp_find_slot: find the struct slot of given device
+ * @ctrl: scan lots of this controller
+ * @device: the device id to find
+ */
+static struct slot *cpqhp_find_slot(struct controller *ctrl, u8 device)
+{
+ struct slot *slot = ctrl->slot;
+
+ while (slot && (slot->device != device)) {
+ slot = slot->next;
+ }
+
+ return slot;
+}
+
+
+static u8 handle_presence_change(u16 change, struct controller * ctrl)
+{
+ int hp_slot;
+ u8 rc = 0;
+ u8 temp_byte;
+ u16 temp_word;
+ struct pci_func *func;
+ struct event_info *taskInfo;
+ struct slot *p_slot;
+
+ if (!change)
+ return 0;
+
+ /**********************************
+ * Presence Change
+ **********************************/
+ dbg("cpqsbd: Presence/Notify input change.\n");
+ dbg(" Changed bits are 0x%4.4x\n", change );
+
+ for (hp_slot = 0; hp_slot < 6; hp_slot++) {
+ if (change & (0x0101 << hp_slot)) {
+ /**********************************
+ * this one changed.
+ **********************************/
+ func = cpqhp_slot_find(ctrl->bus,
+ (hp_slot + ctrl->slot_device_offset), 0);
+
+ taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+ ctrl->next_event = (ctrl->next_event + 1) % 10;
+ taskInfo->hp_slot = hp_slot;
+
+ rc++;
+
+ p_slot = cpqhp_find_slot(ctrl, hp_slot + (readb(ctrl->hpc_reg + SLOT_MASK) >> 4));
+ if (!p_slot)
+ return 0;
+
+ /* If the switch closed, must be a button
+ * If not in button mode, nevermind */
+ if (func->switch_save && (ctrl->push_button == 1)) {
+ temp_word = ctrl->ctrl_int_comp >> 16;
+ temp_byte = (temp_word >> hp_slot) & 0x01;
+ temp_byte |= (temp_word >> (hp_slot + 7)) & 0x02;
+
+ if (temp_byte != func->presence_save) {
+ /**************************************
+ * button Pressed (doesn't do anything)
+ **************************************/
+ dbg("hp_slot %d button pressed\n", hp_slot);
+ taskInfo->event_type = INT_BUTTON_PRESS;
+ } else {
+ /**********************************
+ * button Released - TAKE ACTION!!!!
+ **********************************/
+ dbg("hp_slot %d button released\n", hp_slot);
+ taskInfo->event_type = INT_BUTTON_RELEASE;
+
+ /* Cancel if we are still blinking */
+ if ((p_slot->state == BLINKINGON_STATE)
+ || (p_slot->state == BLINKINGOFF_STATE)) {
+ taskInfo->event_type = INT_BUTTON_CANCEL;
+ dbg("hp_slot %d button cancel\n", hp_slot);
+ } else if ((p_slot->state == POWERON_STATE)
+ || (p_slot->state == POWEROFF_STATE)) {
+ /* info(msg_button_ignore, p_slot->number); */
+ taskInfo->event_type = INT_BUTTON_IGNORE;
+ dbg("hp_slot %d button ignore\n", hp_slot);
+ }
+ }
+ } else {
+ /* Switch is open, assume a presence change
+ * Save the presence state */
+ temp_word = ctrl->ctrl_int_comp >> 16;
+ func->presence_save = (temp_word >> hp_slot) & 0x01;
+ func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
+
+ if ((!(ctrl->ctrl_int_comp & (0x010000 << hp_slot))) ||
+ (!(ctrl->ctrl_int_comp & (0x01000000 << hp_slot)))) {
+ /* Present */
+ taskInfo->event_type = INT_PRESENCE_ON;
+ } else {
+ /* Not Present */
+ taskInfo->event_type = INT_PRESENCE_OFF;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+static u8 handle_power_fault(u8 change, struct controller * ctrl)
+{
+ int hp_slot;
+ u8 rc = 0;
+ struct pci_func *func;
+ struct event_info *taskInfo;
+
+ if (!change)
+ return 0;
+
+ /**********************************
+ * power fault
+ **********************************/
+
+ info("power fault interrupt\n");
+
+ for (hp_slot = 0; hp_slot < 6; hp_slot++) {
+ if (change & (0x01 << hp_slot)) {
+ /**********************************
+ * this one changed.
+ **********************************/
+ func = cpqhp_slot_find(ctrl->bus,
+ (hp_slot + ctrl->slot_device_offset), 0);
+
+ taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+ ctrl->next_event = (ctrl->next_event + 1) % 10;
+ taskInfo->hp_slot = hp_slot;
+
+ rc++;
+
+ if (ctrl->ctrl_int_comp & (0x00000100 << hp_slot)) {
+ /**********************************
+ * power fault Cleared
+ **********************************/
+ func->status = 0x00;
+
+ taskInfo->event_type = INT_POWER_FAULT_CLEAR;
+ } else {
+ /**********************************
+ * power fault
+ **********************************/
+ taskInfo->event_type = INT_POWER_FAULT;
+
+ if (ctrl->rev < 4) {
+ amber_LED_on (ctrl, hp_slot);
+ green_LED_off (ctrl, hp_slot);
+ set_SOGO (ctrl);
+
+ /* this is a fatal condition, we want
+ * to crash the machine to protect from
+ * data corruption. simulated_NMI
+ * shouldn't ever return */
+ /* FIXME
+ simulated_NMI(hp_slot, ctrl); */
+
+ /* The following code causes a software
+ * crash just in case simulated_NMI did
+ * return */
+ /*FIXME
+ panic(msg_power_fault); */
+ } else {
+ /* set power fault status for this board */
+ func->status = 0xFF;
+ info("power fault bit %x set\n", hp_slot);
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * sort_by_size: sort nodes on the list by their length, smallest first.
+ * @head: list to sort
+ *
+ */
+static int sort_by_size(struct pci_resource **head)
+{
+ struct pci_resource *current_res;
+ struct pci_resource *next_res;
+ int out_of_order = 1;
+
+ if (!(*head))
+ return 1;
+
+ if (!((*head)->next))
+ return 0;
+
+ while (out_of_order) {
+ out_of_order = 0;
+
+ /* Special case for swapping list head */
+ if (((*head)->next) &&
+ ((*head)->length > (*head)->next->length)) {
+ out_of_order++;
+ current_res = *head;
+ *head = (*head)->next;
+ current_res->next = (*head)->next;
+ (*head)->next = current_res;
+ }
+
+ current_res = *head;
+
+ while (current_res->next && current_res->next->next) {
+ if (current_res->next->length > current_res->next->next->length) {
+ out_of_order++;
+ next_res = current_res->next;
+ current_res->next = current_res->next->next;
+ current_res = current_res->next;
+ next_res->next = current_res->next;
+ current_res->next = next_res;
+ } else
+ current_res = current_res->next;
+ }
+ } /* End of out_of_order loop */
+
+ return 0;
+}
+
+
+/**
+ * sort_by_max_size: sort nodes on the list by their length, largest first.
+ * @head: list to sort
+ *
+ */
+static int sort_by_max_size(struct pci_resource **head)
+{
+ struct pci_resource *current_res;
+ struct pci_resource *next_res;
+ int out_of_order = 1;
+
+ if (!(*head))
+ return 1;
+
+ if (!((*head)->next))
+ return 0;
+
+ while (out_of_order) {
+ out_of_order = 0;
+
+ /* Special case for swapping list head */
+ if (((*head)->next) &&
+ ((*head)->length < (*head)->next->length)) {
+ out_of_order++;
+ current_res = *head;
+ *head = (*head)->next;
+ current_res->next = (*head)->next;
+ (*head)->next = current_res;
+ }
+
+ current_res = *head;
+
+ while (current_res->next && current_res->next->next) {
+ if (current_res->next->length < current_res->next->next->length) {
+ out_of_order++;
+ next_res = current_res->next;
+ current_res->next = current_res->next->next;
+ current_res = current_res->next;
+ next_res->next = current_res->next;
+ current_res->next = next_res;
+ } else
+ current_res = current_res->next;
+ }
+ } /* End of out_of_order loop */
+
+ return 0;
+}
+
+
+/**
+ * do_pre_bridge_resource_split: find node of resources that are unused
+ *
+ */
+static struct pci_resource *do_pre_bridge_resource_split(struct pci_resource **head,
+ struct pci_resource **orig_head, u32 alignment)
+{
+ struct pci_resource *prevnode = NULL;
+ struct pci_resource *node;
+ struct pci_resource *split_node;
+ u32 rc;
+ u32 temp_dword;
+ dbg("do_pre_bridge_resource_split\n");
+
+ if (!(*head) || !(*orig_head))
+ return NULL;
+
+ rc = cpqhp_resource_sort_and_combine(head);
+
+ if (rc)
+ return NULL;
+
+ if ((*head)->base != (*orig_head)->base)
+ return NULL;
+
+ if ((*head)->length == (*orig_head)->length)
+ return NULL;
+
+
+ /* If we got here, there the bridge requires some of the resource, but
+ * we may be able to split some off of the front */
+
+ node = *head;
+
+ if (node->length & (alignment -1)) {
+ /* this one isn't an aligned length, so we'll make a new entry
+ * and split it up. */
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+
+ temp_dword = (node->length | (alignment-1)) + 1 - alignment;
+
+ split_node->base = node->base;
+ split_node->length = temp_dword;
+
+ node->length -= temp_dword;
+ node->base += split_node->length;
+
+ /* Put it in the list */
+ *head = split_node;
+ split_node->next = node;
+ }
+
+ if (node->length < alignment)
+ return NULL;
+
+ /* Now unlink it */
+ if (*head == node) {
+ *head = node->next;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ }
+ node->next = NULL;
+
+ return node;
+}
+
+
+/**
+ * do_bridge_resource_split: find one node of resources that aren't in use
+ *
+ */
+static struct pci_resource *do_bridge_resource_split(struct pci_resource **head, u32 alignment)
+{
+ struct pci_resource *prevnode = NULL;
+ struct pci_resource *node;
+ u32 rc;
+ u32 temp_dword;
+
+ rc = cpqhp_resource_sort_and_combine(head);
+
+ if (rc)
+ return NULL;
+
+ node = *head;
+
+ while (node->next) {
+ prevnode = node;
+ node = node->next;
+ kfree(prevnode);
+ }
+
+ if (node->length < alignment)
+ goto error;
+
+ if (node->base & (alignment - 1)) {
+ /* Short circuit if adjusted size is too small */
+ temp_dword = (node->base | (alignment-1)) + 1;
+ if ((node->length - (temp_dword - node->base)) < alignment)
+ goto error;
+
+ node->length -= (temp_dword - node->base);
+ node->base = temp_dword;
+ }
+
+ if (node->length & (alignment - 1))
+ /* There's stuff in use after this node */
+ goto error;
+
+ return node;
+error:
+ kfree(node);
+ return NULL;
+}
+
+
+/**
+ * get_io_resource: find first node of given size not in ISA aliasing window.
+ * @head: list to search
+ * @size: size of node to find, must be a power of two.
+ *
+ * Description: this function sorts the resource list by size and then returns
+ * returns the first node of "size" length that is not in the ISA aliasing
+ * window. If it finds a node larger than "size" it will split it up.
+ *
+ */
+static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size)
+{
+ struct pci_resource *prevnode;
+ struct pci_resource *node;
+ struct pci_resource *split_node;
+ u32 temp_dword;
+
+ if (!(*head))
+ return NULL;
+
+ if ( cpqhp_resource_sort_and_combine(head) )
+ return NULL;
+
+ if ( sort_by_size(head) )
+ return NULL;
+
+ for (node = *head; node; node = node->next) {
+ if (node->length < size)
+ continue;
+
+ if (node->base & (size - 1)) {
+ /* this one isn't base aligned properly
+ * so we'll make a new entry and split it up */
+ temp_dword = (node->base | (size-1)) + 1;
+
+ /* Short circuit if adjusted size is too small */
+ if ((node->length - (temp_dword - node->base)) < size)
+ continue;
+
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+
+ split_node->base = node->base;
+ split_node->length = temp_dword - node->base;
+ node->base = temp_dword;
+ node->length -= split_node->length;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of non-aligned base */
+
+ /* Don't need to check if too small since we already did */
+ if (node->length > size) {
+ /* this one is longer than we need
+ * so we'll make a new entry and split it up */
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+
+ split_node->base = node->base + size;
+ split_node->length = node->length - size;
+ node->length = size;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of too big on top end */
+
+ /* For IO make sure it's not in the ISA aliasing space */
+ if (node->base & 0x300L)
+ continue;
+
+ /* If we got here, then it is the right size
+ * Now take it out of the list and break */
+ if (*head == node) {
+ *head = node->next;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ }
+ node->next = NULL;
+ break;
+ }
+
+ return node;
+}
+
+
+/**
+ * get_max_resource: get largest node which has at least the given size.
+ * @head: the list to search the node in
+ * @size: the minimum size of the node to find
+ *
+ * Description: Gets the largest node that is at least "size" big from the
+ * list pointed to by head. It aligns the node on top and bottom
+ * to "size" alignment before returning it.
+ */
+static struct pci_resource *get_max_resource(struct pci_resource **head, u32 size)
+{
+ struct pci_resource *max;
+ struct pci_resource *temp;
+ struct pci_resource *split_node;
+ u32 temp_dword;
+
+ if (cpqhp_resource_sort_and_combine(head))
+ return NULL;
+
+ if (sort_by_max_size(head))
+ return NULL;
+
+ for (max = *head; max; max = max->next) {
+ /* If not big enough we could probably just bail,
+ * instead we'll continue to the next. */
+ if (max->length < size)
+ continue;
+
+ if (max->base & (size - 1)) {
+ /* this one isn't base aligned properly
+ * so we'll make a new entry and split it up */
+ temp_dword = (max->base | (size-1)) + 1;
+
+ /* Short circuit if adjusted size is too small */
+ if ((max->length - (temp_dword - max->base)) < size)
+ continue;
+
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+
+ split_node->base = max->base;
+ split_node->length = temp_dword - max->base;
+ max->base = temp_dword;
+ max->length -= split_node->length;
+
+ split_node->next = max->next;
+ max->next = split_node;
+ }
+
+ if ((max->base + max->length) & (size - 1)) {
+ /* this one isn't end aligned properly at the top
+ * so we'll make a new entry and split it up */
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+ temp_dword = ((max->base + max->length) & ~(size - 1));
+ split_node->base = temp_dword;
+ split_node->length = max->length + max->base
+ - split_node->base;
+ max->length -= split_node->length;
+
+ split_node->next = max->next;
+ max->next = split_node;
+ }
+
+ /* Make sure it didn't shrink too much when we aligned it */
+ if (max->length < size)
+ continue;
+
+ /* Now take it out of the list */
+ temp = *head;
+ if (temp == max) {
+ *head = max->next;
+ } else {
+ while (temp && temp->next != max) {
+ temp = temp->next;
+ }
+
+ temp->next = max->next;
+ }
+
+ max->next = NULL;
+ break;
+ }
+
+ return max;
+}
+
+
+/**
+ * get_resource: find resource of given size and split up larger ones.
+ * @head: the list to search for resources
+ * @size: the size limit to use
+ *
+ * Description: This function sorts the resource list by size and then
+ * returns the first node of "size" length. If it finds a node
+ * larger than "size" it will split it up.
+ *
+ * size must be a power of two.
+ */
+static struct pci_resource *get_resource(struct pci_resource **head, u32 size)
+{
+ struct pci_resource *prevnode;
+ struct pci_resource *node;
+ struct pci_resource *split_node;
+ u32 temp_dword;
+
+ if (cpqhp_resource_sort_and_combine(head))
+ return NULL;
+
+ if (sort_by_size(head))
+ return NULL;
+
+ for (node = *head; node; node = node->next) {
+ dbg("%s: req_size =%x node=%p, base=%x, length=%x\n",
+ __FUNCTION__, size, node, node->base, node->length);
+ if (node->length < size)
+ continue;
+
+ if (node->base & (size - 1)) {
+ dbg("%s: not aligned\n", __FUNCTION__);
+ /* this one isn't base aligned properly
+ * so we'll make a new entry and split it up */
+ temp_dword = (node->base | (size-1)) + 1;
+
+ /* Short circuit if adjusted size is too small */
+ if ((node->length - (temp_dword - node->base)) < size)
+ continue;
+
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+
+ split_node->base = node->base;
+ split_node->length = temp_dword - node->base;
+ node->base = temp_dword;
+ node->length -= split_node->length;
+
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of non-aligned base */
+
+ /* Don't need to check if too small since we already did */
+ if (node->length > size) {
+ dbg("%s: too big\n", __FUNCTION__);
+ /* this one is longer than we need
+ * so we'll make a new entry and split it up */
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+
+ split_node->base = node->base + size;
+ split_node->length = node->length - size;
+ node->length = size;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of too big on top end */
+
+ dbg("%s: got one!!!\n", __FUNCTION__);
+ /* If we got here, then it is the right size
+ * Now take it out of the list */
+ if (*head == node) {
+ *head = node->next;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ }
+ node->next = NULL;
+ break;
+ }
+ return node;
+}
+
+
+/**
+ * cpqhp_resource_sort_and_combine: sort nodes by base addresses and clean up.
+ * @head: the list to sort and clean up
+ *
+ * Description: Sorts all of the nodes in the list in ascending order by
+ * their base addresses. Also does garbage collection by
+ * combining adjacent nodes.
+ *
+ * returns 0 if success
+ */
+int cpqhp_resource_sort_and_combine(struct pci_resource **head)
+{
+ struct pci_resource *node1;
+ struct pci_resource *node2;
+ int out_of_order = 1;
+
+ dbg("%s: head = %p, *head = %p\n", __FUNCTION__, head, *head);
+
+ if (!(*head))
+ return 1;
+
+ dbg("*head->next = %p\n",(*head)->next);
+
+ if (!(*head)->next)
+ return 0; /* only one item on the list, already sorted! */
+
+ dbg("*head->base = 0x%x\n",(*head)->base);
+ dbg("*head->next->base = 0x%x\n",(*head)->next->base);
+ while (out_of_order) {
+ out_of_order = 0;
+
+ /* Special case for swapping list head */
+ if (((*head)->next) &&
+ ((*head)->base > (*head)->next->base)) {
+ node1 = *head;
+ (*head) = (*head)->next;
+ node1->next = (*head)->next;
+ (*head)->next = node1;
+ out_of_order++;
+ }
+
+ node1 = (*head);
+
+ while (node1->next && node1->next->next) {
+ if (node1->next->base > node1->next->next->base) {
+ out_of_order++;
+ node2 = node1->next;
+ node1->next = node1->next->next;
+ node1 = node1->next;
+ node2->next = node1->next;
+ node1->next = node2;
+ } else
+ node1 = node1->next;
+ }
+ } /* End of out_of_order loop */
+
+ node1 = *head;
+
+ while (node1 && node1->next) {
+ if ((node1->base + node1->length) == node1->next->base) {
+ /* Combine */
+ dbg("8..\n");
+ node1->length += node1->next->length;
+ node2 = node1->next;
+ node1->next = node1->next->next;
+ kfree(node2);
+ } else
+ node1 = node1->next;
+ }
+
+ return 0;
+}
+
+
+irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data, struct pt_regs *regs)
+{
+ struct controller *ctrl = data;
+ u8 schedule_flag = 0;
+ u8 reset;
+ u16 misc;
+ u32 Diff;
+ u32 temp_dword;
+
+
+ misc = readw(ctrl->hpc_reg + MISC);
+ /***************************************
+ * Check to see if it was our interrupt
+ ***************************************/
+ if (!(misc & 0x000C)) {
+ return IRQ_NONE;
+ }
+
+ if (misc & 0x0004) {
+ /**********************************
+ * Serial Output interrupt Pending
+ **********************************/
+
+ /* Clear the interrupt */
+ misc |= 0x0004;
+ writew(misc, ctrl->hpc_reg + MISC);
+
+ /* Read to clear posted writes */
+ misc = readw(ctrl->hpc_reg + MISC);
+
+ dbg ("%s - waking up\n", __FUNCTION__);
+ wake_up_interruptible(&ctrl->queue);
+ }
+
+ if (misc & 0x0008) {
+ /* General-interrupt-input interrupt Pending */
+ Diff = readl(ctrl->hpc_reg + INT_INPUT_CLEAR) ^ ctrl->ctrl_int_comp;
+
+ ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+ /* Clear the interrupt */
+ writel(Diff, ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+ /* Read it back to clear any posted writes */
+ temp_dword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+ if (!Diff)
+ /* Clear all interrupts */
+ writel(0xFFFFFFFF, ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+ schedule_flag += handle_switch_change((u8)(Diff & 0xFFL), ctrl);
+ schedule_flag += handle_presence_change((u16)((Diff & 0xFFFF0000L) >> 16), ctrl);
+ schedule_flag += handle_power_fault((u8)((Diff & 0xFF00L) >> 8), ctrl);
+ }
+
+ reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE);
+ if (reset & 0x40) {
+ /* Bus reset has completed */
+ reset &= 0xCF;
+ writeb(reset, ctrl->hpc_reg + RESET_FREQ_MODE);
+ reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE);
+ wake_up_interruptible(&ctrl->queue);
+ }
+
+ if (schedule_flag) {
+ up(&event_semaphore);
+ dbg("Signal event_semaphore\n");
+ }
+ return IRQ_HANDLED;
+}
+
+
+/**
+ * cpqhp_slot_create - Creates a node and adds it to the proper bus.
+ * @busnumber - bus where new node is to be located
+ *
+ * Returns pointer to the new node or NULL if unsuccessful
+ */
+struct pci_func *cpqhp_slot_create(u8 busnumber)
+{
+ struct pci_func *new_slot;
+ struct pci_func *next;
+
+ new_slot = kmalloc(sizeof(*new_slot), GFP_KERNEL);
+
+ if (new_slot == NULL) {
+ /* I'm not dead yet!
+ * You will be. */
+ return new_slot;
+ }
+
+ memset(new_slot, 0, sizeof(struct pci_func));
+
+ new_slot->next = NULL;
+ new_slot->configured = 1;
+
+ if (cpqhp_slot_list[busnumber] == NULL) {
+ cpqhp_slot_list[busnumber] = new_slot;
+ } else {
+ next = cpqhp_slot_list[busnumber];
+ while (next->next != NULL)
+ next = next->next;
+ next->next = new_slot;
+ }
+ return new_slot;
+}
+
+
+/**
+ * slot_remove - Removes a node from the linked list of slots.
+ * @old_slot: slot to remove
+ *
+ * Returns 0 if successful, !0 otherwise.
+ */
+static int slot_remove(struct pci_func * old_slot)
+{
+ struct pci_func *next;
+
+ if (old_slot == NULL)
+ return 1;
+
+ next = cpqhp_slot_list[old_slot->bus];
+
+ if (next == NULL) {
+ return 1;
+ }
+
+ if (next == old_slot) {
+ cpqhp_slot_list[old_slot->bus] = old_slot->next;
+ cpqhp_destroy_board_resources(old_slot);
+ kfree(old_slot);
+ return 0;
+ }
+
+ while ((next->next != old_slot) && (next->next != NULL)) {
+ next = next->next;
+ }
+
+ if (next->next == old_slot) {
+ next->next = old_slot->next;
+ cpqhp_destroy_board_resources(old_slot);
+ kfree(old_slot);
+ return 0;
+ } else
+ return 2;
+}
+
+
+/**
+ * bridge_slot_remove - Removes a node from the linked list of slots.
+ * @bridge: bridge to remove
+ *
+ * Returns 0 if successful, !0 otherwise.
+ */
+static int bridge_slot_remove(struct pci_func *bridge)
+{
+ u8 subordinateBus, secondaryBus;
+ u8 tempBus;
+ struct pci_func *next;
+
+ secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF;
+ subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF;
+
+ for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) {
+ next = cpqhp_slot_list[tempBus];
+
+ while (!slot_remove(next)) {
+ next = cpqhp_slot_list[tempBus];
+ }
+ }
+
+ next = cpqhp_slot_list[bridge->bus];
+
+ if (next == NULL)
+ return 1;
+
+ if (next == bridge) {
+ cpqhp_slot_list[bridge->bus] = bridge->next;
+ goto out;
+ }
+
+ while ((next->next != bridge) && (next->next != NULL))
+ next = next->next;
+
+ if (next->next != bridge)
+ return 2;
+ next->next = bridge->next;
+out:
+ kfree(bridge);
+ return 0;
+}
+
+
+/**
+ * cpqhp_slot_find - Looks for a node by bus, and device, multiple functions accessed
+ * @bus: bus to find
+ * @device: device to find
+ * @index: is 0 for first function found, 1 for the second...
+ *
+ * Returns pointer to the node if successful, %NULL otherwise.
+ */
+struct pci_func *cpqhp_slot_find(u8 bus, u8 device, u8 index)
+{
+ int found = -1;
+ struct pci_func *func;
+
+ func = cpqhp_slot_list[bus];
+
+ if ((func == NULL) || ((func->device == device) && (index == 0)))
+ return func;
+
+ if (func->device == device)
+ found++;
+
+ while (func->next != NULL) {
+ func = func->next;
+
+ if (func->device == device)
+ found++;
+
+ if (found == index)
+ return func;
+ }
+
+ return NULL;
+}
+
+
+/* DJZ: I don't think is_bridge will work as is.
+ * FIXME */
+static int is_bridge(struct pci_func * func)
+{
+ /* Check the header type */
+ if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01)
+ return 1;
+ else
+ return 0;
+}
+
+
+/**
+ * set_controller_speed - set the frequency and/or mode of a specific
+ * controller segment.
+ *
+ * @ctrl: controller to change frequency/mode for.
+ * @adapter_speed: the speed of the adapter we want to match.
+ * @hp_slot: the slot number where the adapter is installed.
+ *
+ * Returns 0 if we successfully change frequency and/or mode to match the
+ * adapter speed.
+ *
+ */
+static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_slot)
+{
+ struct slot *slot;
+ u8 reg;
+ u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
+ u16 reg16;
+ u32 leds = readl(ctrl->hpc_reg + LED_CONTROL);
+
+ if (ctrl->speed == adapter_speed)
+ return 0;
+
+ /* We don't allow freq/mode changes if we find another adapter running
+ * in another slot on this controller */
+ for(slot = ctrl->slot; slot; slot = slot->next) {
+ if (slot->device == (hp_slot + ctrl->slot_device_offset))
+ continue;
+ if (!slot->hotplug_slot && !slot->hotplug_slot->info)
+ continue;
+ if (slot->hotplug_slot->info->adapter_status == 0)
+ continue;
+ /* If another adapter is running on the same segment but at a
+ * lower speed/mode, we allow the new adapter to function at
+ * this rate if supported */
+ if (ctrl->speed < adapter_speed)
+ return 0;
+
+ return 1;
+ }
+
+ /* If the controller doesn't support freq/mode changes and the
+ * controller is running at a higher mode, we bail */
+ if ((ctrl->speed > adapter_speed) && (!ctrl->pcix_speed_capability))
+ return 1;
+
+ /* But we allow the adapter to run at a lower rate if possible */
+ if ((ctrl->speed < adapter_speed) && (!ctrl->pcix_speed_capability))
+ return 0;
+
+ /* We try to set the max speed supported by both the adapter and
+ * controller */
+ if (ctrl->speed_capability < adapter_speed) {
+ if (ctrl->speed == ctrl->speed_capability)
+ return 0;
+ adapter_speed = ctrl->speed_capability;
+ }
+
+ writel(0x0L, ctrl->hpc_reg + LED_CONTROL);
+ writeb(0x00, ctrl->hpc_reg + SLOT_ENABLE);
+
+ set_SOGO(ctrl);
+ wait_for_ctrl_irq(ctrl);
+
+ if (adapter_speed != PCI_SPEED_133MHz_PCIX)
+ reg = 0xF5;
+ else
+ reg = 0xF4;
+ pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
+
+ reg16 = readw(ctrl->hpc_reg + NEXT_CURR_FREQ);
+ reg16 &= ~0x000F;
+ switch(adapter_speed) {
+ case(PCI_SPEED_133MHz_PCIX):
+ reg = 0x75;
+ reg16 |= 0xB;
+ break;
+ case(PCI_SPEED_100MHz_PCIX):
+ reg = 0x74;
+ reg16 |= 0xA;
+ break;
+ case(PCI_SPEED_66MHz_PCIX):
+ reg = 0x73;
+ reg16 |= 0x9;
+ break;
+ case(PCI_SPEED_66MHz):
+ reg = 0x73;
+ reg16 |= 0x1;
+ break;
+ default: /* 33MHz PCI 2.2 */
+ reg = 0x71;
+ break;
+
+ }
+ reg16 |= 0xB << 12;
+ writew(reg16, ctrl->hpc_reg + NEXT_CURR_FREQ);
+
+ mdelay(5);
+
+ /* Reenable interrupts */
+ writel(0, ctrl->hpc_reg + INT_MASK);
+
+ pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
+
+ /* Restart state machine */
+ reg = ~0xF;
+ pci_read_config_byte(ctrl->pci_dev, 0x43, &reg);
+ pci_write_config_byte(ctrl->pci_dev, 0x43, reg);
+
+ /* Only if mode change...*/
+ if (((ctrl->speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) ||
+ ((ctrl->speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz)))
+ set_SOGO(ctrl);
+
+ wait_for_ctrl_irq(ctrl);
+ mdelay(1100);
+
+ /* Restore LED/Slot state */
+ writel(leds, ctrl->hpc_reg + LED_CONTROL);
+ writeb(slot_power, ctrl->hpc_reg + SLOT_ENABLE);
+
+ set_SOGO(ctrl);
+ wait_for_ctrl_irq(ctrl);
+
+ ctrl->speed = adapter_speed;
+ slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ info("Successfully changed frequency/mode for adapter in slot %d\n",
+ slot->number);
+ return 0;
+}
+
+/* the following routines constitute the bulk of the
+ hotplug controller logic
+ */
+
+
+/**
+ * board_replaced - Called after a board has been replaced in the system.
+ *
+ * This is only used if we don't have resources for hot add
+ * Turns power on for the board
+ * Checks to see if board is the same
+ * If board is same, reconfigures it
+ * If board isn't same, turns it back off.
+ *
+ */
+static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
+{
+ u8 hp_slot;
+ u8 temp_byte;
+ u8 adapter_speed;
+ u32 index;
+ u32 rc = 0;
+ u32 src = 8;
+
+ hp_slot = func->device - ctrl->slot_device_offset;
+
+ if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot)) {
+ /**********************************
+ * The switch is open.
+ **********************************/
+ rc = INTERLOCK_OPEN;
+ } else if (is_slot_enabled (ctrl, hp_slot)) {
+ /**********************************
+ * The board is already on
+ **********************************/
+ rc = CARD_FUNCTIONING;
+ } else {
+ down(&ctrl->crit_sect);
+
+ /* turn on board without attaching to the bus */
+ enable_slot_power (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ /* Change bits in slot power register to force another shift out
+ * NOTE: this is to work around the timer bug */
+ temp_byte = readb(ctrl->hpc_reg + SLOT_POWER);
+ writeb(0x00, ctrl->hpc_reg + SLOT_POWER);
+ writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ adapter_speed = get_adapter_speed(ctrl, hp_slot);
+ if (ctrl->speed != adapter_speed)
+ if (set_controller_speed(ctrl, adapter_speed, hp_slot))
+ rc = WRONG_BUS_FREQUENCY;
+
+ /* turn off board without attaching to the bus */
+ disable_slot_power (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ up(&ctrl->crit_sect);
+
+ if (rc)
+ return rc;
+
+ down(&ctrl->crit_sect);
+
+ slot_enable (ctrl, hp_slot);
+ green_LED_blink (ctrl, hp_slot);
+
+ amber_LED_off (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ up(&ctrl->crit_sect);
+
+ /* Wait for ~1 second because of hot plug spec */
+ long_delay(1*HZ);
+
+ /* Check for a power fault */
+ if (func->status == 0xFF) {
+ /* power fault occurred, but it was benign */
+ rc = POWER_FAILURE;
+ func->status = 0;
+ } else
+ rc = cpqhp_valid_replace(ctrl, func);
+
+ if (!rc) {
+ /* It must be the same board */
+
+ rc = cpqhp_configure_board(ctrl, func);
+
+ if (rc || src) {
+ /* If configuration fails, turn it off
+ * Get slot won't work for devices behind
+ * bridges, but in this case it will always be
+ * called for the "base" bus/dev/func of an
+ * adapter. */
+
+ down(&ctrl->crit_sect);
+
+ amber_LED_on (ctrl, hp_slot);
+ green_LED_off (ctrl, hp_slot);
+ slot_disable (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ up(&ctrl->crit_sect);
+
+ if (rc)
+ return rc;
+ else
+ return 1;
+ }
+
+ func->status = 0;
+ func->switch_save = 0x10;
+
+ index = 1;
+ while (((func = cpqhp_slot_find(func->bus, func->device, index)) != NULL) && !rc) {
+ rc |= cpqhp_configure_board(ctrl, func);
+ index++;
+ }
+
+ if (rc) {
+ /* If configuration fails, turn it off
+ * Get slot won't work for devices behind
+ * bridges, but in this case it will always be
+ * called for the "base" bus/dev/func of an
+ * adapter. */
+
+ down(&ctrl->crit_sect);
+
+ amber_LED_on (ctrl, hp_slot);
+ green_LED_off (ctrl, hp_slot);
+ slot_disable (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ up(&ctrl->crit_sect);
+
+ return rc;
+ }
+ /* Done configuring so turn LED on full time */
+
+ down(&ctrl->crit_sect);
+
+ green_LED_on (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ up(&ctrl->crit_sect);
+ rc = 0;
+ } else {
+ /* Something is wrong
+
+ * Get slot won't work for devices behind bridges, but
+ * in this case it will always be called for the "base"
+ * bus/dev/func of an adapter. */
+
+ down(&ctrl->crit_sect);
+
+ amber_LED_on (ctrl, hp_slot);
+ green_LED_off (ctrl, hp_slot);
+ slot_disable (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ up(&ctrl->crit_sect);
+ }
+
+ }
+ return rc;
+
+}
+
+
+/**
+ * board_added - Called after a board has been added to the system.
+ *
+ * Turns power on for the board
+ * Configures board
+ *
+ */
+static u32 board_added(struct pci_func *func, struct controller *ctrl)
+{
+ u8 hp_slot;
+ u8 temp_byte;
+ u8 adapter_speed;
+ int index;
+ u32 temp_register = 0xFFFFFFFF;
+ u32 rc = 0;
+ struct pci_func *new_slot = NULL;
+ struct slot *p_slot;
+ struct resource_lists res_lists;
+
+ hp_slot = func->device - ctrl->slot_device_offset;
+ dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n",
+ __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot);
+
+ down(&ctrl->crit_sect);
+
+ /* turn on board without attaching to the bus */
+ enable_slot_power(ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ /* Change bits in slot power register to force another shift out
+ * NOTE: this is to work around the timer bug */
+ temp_byte = readb(ctrl->hpc_reg + SLOT_POWER);
+ writeb(0x00, ctrl->hpc_reg + SLOT_POWER);
+ writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ adapter_speed = get_adapter_speed(ctrl, hp_slot);
+ if (ctrl->speed != adapter_speed)
+ if (set_controller_speed(ctrl, adapter_speed, hp_slot))
+ rc = WRONG_BUS_FREQUENCY;
+
+ /* turn off board without attaching to the bus */
+ disable_slot_power (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq(ctrl);
+
+ up(&ctrl->crit_sect);
+
+ if (rc)
+ return rc;
+
+ p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ /* turn on board and blink green LED */
+
+ dbg("%s: before down\n", __FUNCTION__);
+ down(&ctrl->crit_sect);
+ dbg("%s: after down\n", __FUNCTION__);
+
+ dbg("%s: before slot_enable\n", __FUNCTION__);
+ slot_enable (ctrl, hp_slot);
+
+ dbg("%s: before green_LED_blink\n", __FUNCTION__);
+ green_LED_blink (ctrl, hp_slot);
+
+ dbg("%s: before amber_LED_blink\n", __FUNCTION__);
+ amber_LED_off (ctrl, hp_slot);
+
+ dbg("%s: before set_SOGO\n", __FUNCTION__);
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ dbg("%s: before wait_for_ctrl_irq\n", __FUNCTION__);
+ wait_for_ctrl_irq (ctrl);
+ dbg("%s: after wait_for_ctrl_irq\n", __FUNCTION__);
+
+ dbg("%s: before up\n", __FUNCTION__);
+ up(&ctrl->crit_sect);
+ dbg("%s: after up\n", __FUNCTION__);
+
+ /* Wait for ~1 second because of hot plug spec */
+ dbg("%s: before long_delay\n", __FUNCTION__);
+ long_delay(1*HZ);
+ dbg("%s: after long_delay\n", __FUNCTION__);
+
+ dbg("%s: func status = %x\n", __FUNCTION__, func->status);
+ /* Check for a power fault */
+ if (func->status == 0xFF) {
+ /* power fault occurred, but it was benign */
+ temp_register = 0xFFFFFFFF;
+ dbg("%s: temp register set to %x by power fault\n", __FUNCTION__, temp_register);
+ rc = POWER_FAILURE;
+ func->status = 0;
+ } else {
+ /* Get vendor/device ID u32 */
+ ctrl->pci_bus->number = func->bus;
+ rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register);
+ dbg("%s: pci_read_config_dword returns %d\n", __FUNCTION__, rc);
+ dbg("%s: temp_register is %x\n", __FUNCTION__, temp_register);
+
+ if (rc != 0) {
+ /* Something's wrong here */
+ temp_register = 0xFFFFFFFF;
+ dbg("%s: temp register set to %x by error\n", __FUNCTION__, temp_register);
+ }
+ /* Preset return code. It will be changed later if things go okay. */
+ rc = NO_ADAPTER_PRESENT;
+ }
+
+ /* All F's is an empty slot or an invalid board */
+ if (temp_register != 0xFFFFFFFF) { /* Check for a board in the slot */
+ res_lists.io_head = ctrl->io_head;
+ res_lists.mem_head = ctrl->mem_head;
+ res_lists.p_mem_head = ctrl->p_mem_head;
+ res_lists.bus_head = ctrl->bus_head;
+ res_lists.irqs = NULL;
+
+ rc = configure_new_device(ctrl, func, 0, &res_lists);
+
+ dbg("%s: back from configure_new_device\n", __FUNCTION__);
+ ctrl->io_head = res_lists.io_head;
+ ctrl->mem_head = res_lists.mem_head;
+ ctrl->p_mem_head = res_lists.p_mem_head;
+ ctrl->bus_head = res_lists.bus_head;
+
+ cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
+ cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
+ cpqhp_resource_sort_and_combine(&(ctrl->io_head));
+ cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
+
+ if (rc) {
+ down(&ctrl->crit_sect);
+
+ amber_LED_on (ctrl, hp_slot);
+ green_LED_off (ctrl, hp_slot);
+ slot_disable (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ up(&ctrl->crit_sect);
+ return rc;
+ } else {
+ cpqhp_save_slot_config(ctrl, func);
+ }
+
+
+ func->status = 0;
+ func->switch_save = 0x10;
+ func->is_a_board = 0x01;
+
+ /* next, we will instantiate the linux pci_dev structures (with
+ * appropriate driver notification, if already present) */
+ dbg("%s: configure linux pci_dev structure\n", __FUNCTION__);
+ index = 0;
+ do {
+ new_slot = cpqhp_slot_find(ctrl->bus, func->device, index++);
+ if (new_slot && !new_slot->pci_dev) {
+ cpqhp_configure_device(ctrl, new_slot);
+ }
+ } while (new_slot);
+
+ down(&ctrl->crit_sect);
+
+ green_LED_on (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ up(&ctrl->crit_sect);
+ } else {
+ down(&ctrl->crit_sect);
+
+ amber_LED_on (ctrl, hp_slot);
+ green_LED_off (ctrl, hp_slot);
+ slot_disable (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ up(&ctrl->crit_sect);
+
+ return rc;
+ }
+ return 0;
+}
+
+
+/**
+ * remove_board - Turns off slot and LED's
+ *
+ */
+static u32 remove_board(struct pci_func * func, u32 replace_flag, struct controller * ctrl)
+{
+ int index;
+ u8 skip = 0;
+ u8 device;
+ u8 hp_slot;
+ u8 temp_byte;
+ u32 rc;
+ struct resource_lists res_lists;
+ struct pci_func *temp_func;
+
+ if (cpqhp_unconfigure_device(func))
+ return 1;
+
+ device = func->device;
+
+ hp_slot = func->device - ctrl->slot_device_offset;
+ dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
+
+ /* When we get here, it is safe to change base address registers.
+ * We will attempt to save the base address register lengths */
+ if (replace_flag || !ctrl->add_support)
+ rc = cpqhp_save_base_addr_length(ctrl, func);
+ else if (!func->bus_head && !func->mem_head &&
+ !func->p_mem_head && !func->io_head) {
+ /* Here we check to see if we've saved any of the board's
+ * resources already. If so, we'll skip the attempt to
+ * determine what's being used. */
+ index = 0;
+ temp_func = cpqhp_slot_find(func->bus, func->device, index++);
+ while (temp_func) {
+ if (temp_func->bus_head || temp_func->mem_head
+ || temp_func->p_mem_head || temp_func->io_head) {
+ skip = 1;
+ break;
+ }
+ temp_func = cpqhp_slot_find(temp_func->bus, temp_func->device, index++);
+ }
+
+ if (!skip)
+ rc = cpqhp_save_used_resources(ctrl, func);
+ }
+ /* Change status to shutdown */
+ if (func->is_a_board)
+ func->status = 0x01;
+ func->configured = 0;
+
+ down(&ctrl->crit_sect);
+
+ green_LED_off (ctrl, hp_slot);
+ slot_disable (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* turn off SERR for slot */
+ temp_byte = readb(ctrl->hpc_reg + SLOT_SERR);
+ temp_byte &= ~(0x01 << hp_slot);
+ writeb(temp_byte, ctrl->hpc_reg + SLOT_SERR);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ up(&ctrl->crit_sect);
+
+ if (!replace_flag && ctrl->add_support) {
+ while (func) {
+ res_lists.io_head = ctrl->io_head;
+ res_lists.mem_head = ctrl->mem_head;
+ res_lists.p_mem_head = ctrl->p_mem_head;
+ res_lists.bus_head = ctrl->bus_head;
+
+ cpqhp_return_board_resources(func, &res_lists);
+
+ ctrl->io_head = res_lists.io_head;
+ ctrl->mem_head = res_lists.mem_head;
+ ctrl->p_mem_head = res_lists.p_mem_head;
+ ctrl->bus_head = res_lists.bus_head;
+
+ cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
+ cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
+ cpqhp_resource_sort_and_combine(&(ctrl->io_head));
+ cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
+
+ if (is_bridge(func)) {
+ bridge_slot_remove(func);
+ } else
+ slot_remove(func);
+
+ func = cpqhp_slot_find(ctrl->bus, device, 0);
+ }
+
+ /* Setup slot structure with entry for empty slot */
+ func = cpqhp_slot_create(ctrl->bus);
+
+ if (func == NULL)
+ return 1;
+
+ func->bus = ctrl->bus;
+ func->device = device;
+ func->function = 0;
+ func->configured = 0;
+ func->switch_save = 0x10;
+ func->is_a_board = 0;
+ func->p_task_event = NULL;
+ }
+
+ return 0;
+}
+
+static void pushbutton_helper_thread(unsigned long data)
+{
+ pushbutton_pending = data;
+ up(&event_semaphore);
+}
+
+
+/* this is the main worker thread */
+static int event_thread(void* data)
+{
+ struct controller *ctrl;
+ lock_kernel();
+ daemonize("phpd_event");
+
+ unlock_kernel();
+
+ while (1) {
+ dbg("!!!!event_thread sleeping\n");
+ down_interruptible (&event_semaphore);
+ dbg("event_thread woken finished = %d\n", event_finished);
+ if (event_finished) break;
+ /* Do stuff here */
+ if (pushbutton_pending)
+ cpqhp_pushbutton_thread(pushbutton_pending);
+ else
+ for (ctrl = cpqhp_ctrl_list; ctrl; ctrl=ctrl->next)
+ interrupt_event_handler(ctrl);
+ }
+ dbg("event_thread signals exit\n");
+ up(&event_exit);
+ return 0;
+}
+
+
+int cpqhp_event_start_thread(void)
+{
+ int pid;
+
+ /* initialize our semaphores */
+ init_MUTEX(&delay_sem);
+ init_MUTEX_LOCKED(&event_semaphore);
+ init_MUTEX_LOCKED(&event_exit);
+ event_finished=0;
+
+ pid = kernel_thread(event_thread, NULL, 0);
+ if (pid < 0) {
+ err ("Can't start up our event thread\n");
+ return -1;
+ }
+ dbg("Our event thread pid = %d\n", pid);
+ return 0;
+}
+
+
+void cpqhp_event_stop_thread(void)
+{
+ event_finished = 1;
+ dbg("event_thread finish command given\n");
+ up(&event_semaphore);
+ dbg("wait for event_thread to exit\n");
+ down(&event_exit);
+}
+
+
+static int update_slot_info(struct controller *ctrl, struct slot *slot)
+{
+ struct hotplug_slot_info *info;
+ int result;
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->power_status = get_slot_enabled(ctrl, slot);
+ info->attention_status = cpq_get_attention_status(ctrl, slot);
+ info->latch_status = cpq_get_latch_status(ctrl, slot);
+ info->adapter_status = get_presence_status(ctrl, slot);
+ result = pci_hp_change_slot_info(slot->hotplug_slot, info);
+ kfree (info);
+ return result;
+}
+
+static void interrupt_event_handler(struct controller *ctrl)
+{
+ int loop = 0;
+ int change = 1;
+ struct pci_func *func;
+ u8 hp_slot;
+ struct slot *p_slot;
+
+ while (change) {
+ change = 0;
+
+ for (loop = 0; loop < 10; loop++) {
+ /* dbg("loop %d\n", loop); */
+ if (ctrl->event_queue[loop].event_type != 0) {
+ hp_slot = ctrl->event_queue[loop].hp_slot;
+
+ func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0);
+ if (!func)
+ return;
+
+ p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+ if (!p_slot)
+ return;
+
+ dbg("hp_slot %d, func %p, p_slot %p\n",
+ hp_slot, func, p_slot);
+
+ if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) {
+ dbg("button pressed\n");
+ } else if (ctrl->event_queue[loop].event_type ==
+ INT_BUTTON_CANCEL) {
+ dbg("button cancel\n");
+ del_timer(&p_slot->task_event);
+
+ down(&ctrl->crit_sect);
+
+ if (p_slot->state == BLINKINGOFF_STATE) {
+ /* slot is on */
+ dbg("turn on green LED\n");
+ green_LED_on (ctrl, hp_slot);
+ } else if (p_slot->state == BLINKINGON_STATE) {
+ /* slot is off */
+ dbg("turn off green LED\n");
+ green_LED_off (ctrl, hp_slot);
+ }
+
+ info(msg_button_cancel, p_slot->number);
+
+ p_slot->state = STATIC_STATE;
+
+ amber_LED_off (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ up(&ctrl->crit_sect);
+ }
+ /*** button Released (No action on press...) */
+ else if (ctrl->event_queue[loop].event_type == INT_BUTTON_RELEASE) {
+ dbg("button release\n");
+
+ if (is_slot_enabled (ctrl, hp_slot)) {
+ dbg("slot is on\n");
+ p_slot->state = BLINKINGOFF_STATE;
+ info(msg_button_off, p_slot->number);
+ } else {
+ dbg("slot is off\n");
+ p_slot->state = BLINKINGON_STATE;
+ info(msg_button_on, p_slot->number);
+ }
+ down(&ctrl->crit_sect);
+
+ dbg("blink green LED and turn off amber\n");
+
+ amber_LED_off (ctrl, hp_slot);
+ green_LED_blink (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+
+ up(&ctrl->crit_sect);
+ init_timer(&p_slot->task_event);
+ p_slot->hp_slot = hp_slot;
+ p_slot->ctrl = ctrl;
+/* p_slot->physical_slot = physical_slot; */
+ p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */
+ p_slot->task_event.function = pushbutton_helper_thread;
+ p_slot->task_event.data = (u32) p_slot;
+
+ dbg("add_timer p_slot = %p\n", p_slot);
+ add_timer(&p_slot->task_event);
+ }
+ /***********POWER FAULT */
+ else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
+ dbg("power fault\n");
+ } else {
+ /* refresh notification */
+ if (p_slot)
+ update_slot_info(ctrl, p_slot);
+ }
+
+ ctrl->event_queue[loop].event_type = 0;
+
+ change = 1;
+ }
+ } /* End of FOR loop */
+ }
+
+ return;
+}
+
+
+/**
+ * cpqhp_pushbutton_thread
+ *
+ * Scheduled procedure to handle blocking stuff for the pushbuttons
+ * Handles all pending events and exits.
+ *
+ */
+void cpqhp_pushbutton_thread(unsigned long slot)
+{
+ u8 hp_slot;
+ u8 device;
+ struct pci_func *func;
+ struct slot *p_slot = (struct slot *) slot;
+ struct controller *ctrl = (struct controller *) p_slot->ctrl;
+
+ pushbutton_pending = 0;
+ hp_slot = p_slot->hp_slot;
+
+ device = p_slot->device;
+
+ if (is_slot_enabled(ctrl, hp_slot)) {
+ p_slot->state = POWEROFF_STATE;
+ /* power Down board */
+ func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0);
+ dbg("In power_down_board, func = %p, ctrl = %p\n", func, ctrl);
+ if (!func) {
+ dbg("Error! func NULL in %s\n", __FUNCTION__);
+ return ;
+ }
+
+ if (func != NULL && ctrl != NULL) {
+ if (cpqhp_process_SS(ctrl, func) != 0) {
+ amber_LED_on (ctrl, hp_slot);
+ green_LED_on (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+ }
+ }
+
+ p_slot->state = STATIC_STATE;
+ } else {
+ p_slot->state = POWERON_STATE;
+ /* slot is off */
+
+ func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0);
+ dbg("In add_board, func = %p, ctrl = %p\n", func, ctrl);
+ if (!func) {
+ dbg("Error! func NULL in %s\n", __FUNCTION__);
+ return ;
+ }
+
+ if (func != NULL && ctrl != NULL) {
+ if (cpqhp_process_SI(ctrl, func) != 0) {
+ amber_LED_on(ctrl, hp_slot);
+ green_LED_off(ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+ }
+ }
+
+ p_slot->state = STATIC_STATE;
+ }
+
+ return;
+}
+
+
+int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func)
+{
+ u8 device, hp_slot;
+ u16 temp_word;
+ u32 tempdword;
+ int rc;
+ struct slot* p_slot;
+ int physical_slot = 0;
+
+ tempdword = 0;
+
+ device = func->device;
+ hp_slot = device - ctrl->slot_device_offset;
+ p_slot = cpqhp_find_slot(ctrl, device);
+ if (p_slot)
+ physical_slot = p_slot->number;
+
+ /* Check to see if the interlock is closed */
+ tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
+
+ if (tempdword & (0x01 << hp_slot)) {
+ return 1;
+ }
+
+ if (func->is_a_board) {
+ rc = board_replaced(func, ctrl);
+ } else {
+ /* add board */
+ slot_remove(func);
+
+ func = cpqhp_slot_create(ctrl->bus);
+ if (func == NULL)
+ return 1;
+
+ func->bus = ctrl->bus;
+ func->device = device;
+ func->function = 0;
+ func->configured = 0;
+ func->is_a_board = 1;
+
+ /* We have to save the presence info for these slots */
+ temp_word = ctrl->ctrl_int_comp >> 16;
+ func->presence_save = (temp_word >> hp_slot) & 0x01;
+ func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
+
+ if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
+ func->switch_save = 0;
+ } else {
+ func->switch_save = 0x10;
+ }
+
+ rc = board_added(func, ctrl);
+ if (rc) {
+ if (is_bridge(func)) {
+ bridge_slot_remove(func);
+ } else
+ slot_remove(func);
+
+ /* Setup slot structure with entry for empty slot */
+ func = cpqhp_slot_create(ctrl->bus);
+
+ if (func == NULL)
+ return 1;
+
+ func->bus = ctrl->bus;
+ func->device = device;
+ func->function = 0;
+ func->configured = 0;
+ func->is_a_board = 0;
+
+ /* We have to save the presence info for these slots */
+ temp_word = ctrl->ctrl_int_comp >> 16;
+ func->presence_save = (temp_word >> hp_slot) & 0x01;
+ func->presence_save |=
+ (temp_word >> (hp_slot + 7)) & 0x02;
+
+ if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
+ func->switch_save = 0;
+ } else {
+ func->switch_save = 0x10;
+ }
+ }
+ }
+
+ if (rc) {
+ dbg("%s: rc = %d\n", __FUNCTION__, rc);
+ }
+
+ if (p_slot)
+ update_slot_info(ctrl, p_slot);
+
+ return rc;
+}
+
+
+int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
+{
+ u8 device, class_code, header_type, BCR;
+ u8 index = 0;
+ u8 replace_flag;
+ u32 rc = 0;
+ unsigned int devfn;
+ struct slot* p_slot;
+ struct pci_bus *pci_bus = ctrl->pci_bus;
+ int physical_slot=0;
+
+ device = func->device;
+ func = cpqhp_slot_find(ctrl->bus, device, index++);
+ p_slot = cpqhp_find_slot(ctrl, device);
+ if (p_slot) {
+ physical_slot = p_slot->number;
+ }
+
+ /* Make sure there are no video controllers here */
+ while (func && !rc) {
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ /* Check the Class Code */
+ rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
+ if (rc)
+ return rc;
+
+ if (class_code == PCI_BASE_CLASS_DISPLAY) {
+ /* Display/Video adapter (not supported) */
+ rc = REMOVE_NOT_SUPPORTED;
+ } else {
+ /* See if it's a bridge */
+ rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+ if (rc)
+ return rc;
+
+ /* If it's a bridge, check the VGA Enable bit */
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+ rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR);
+ if (rc)
+ return rc;
+
+ /* If the VGA Enable bit is set, remove isn't
+ * supported */
+ if (BCR & PCI_BRIDGE_CTL_VGA) {
+ rc = REMOVE_NOT_SUPPORTED;
+ }
+ }
+ }
+
+ func = cpqhp_slot_find(ctrl->bus, device, index++);
+ }
+
+ func = cpqhp_slot_find(ctrl->bus, device, 0);
+ if ((func != NULL) && !rc) {
+ /* FIXME: Replace flag should be passed into process_SS */
+ replace_flag = !(ctrl->add_support);
+ rc = remove_board(func, replace_flag, ctrl);
+ } else if (!rc) {
+ rc = 1;
+ }
+
+ if (p_slot)
+ update_slot_info(ctrl, p_slot);
+
+ return rc;
+}
+
+/**
+ * switch_leds: switch the leds, go from one site to the other.
+ * @ctrl: controller to use
+ * @num_of_slots: number of slots to use
+ * @direction: 1 to start from the left side, 0 to start right.
+ */
+static void switch_leds(struct controller *ctrl, const int num_of_slots,
+ u32 *work_LED, const int direction)
+{
+ int loop;
+
+ for (loop = 0; loop < num_of_slots; loop++) {
+ if (direction)
+ *work_LED = *work_LED >> 1;
+ else
+ *work_LED = *work_LED << 1;
+ writel(*work_LED, ctrl->hpc_reg + LED_CONTROL);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOGO interrupt */
+ wait_for_ctrl_irq(ctrl);
+
+ /* Get ready for next iteration */
+ long_delay((2*HZ)/10);
+ }
+}
+
+/**
+ * hardware_test - runs hardware tests
+ *
+ * For hot plug ctrl folks to play with.
+ * test_num is the number written to the "test" file in sysfs
+ *
+ */
+int cpqhp_hardware_test(struct controller *ctrl, int test_num)
+{
+ u32 save_LED;
+ u32 work_LED;
+ int loop;
+ int num_of_slots;
+
+ num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0f;
+
+ switch (test_num) {
+ case 1:
+ /* Do stuff here! */
+
+ /* Do that funky LED thing */
+ /* so we can restore them later */
+ save_LED = readl(ctrl->hpc_reg + LED_CONTROL);
+ work_LED = 0x01010101;
+ switch_leds(ctrl, num_of_slots, &work_LED, 0);
+ switch_leds(ctrl, num_of_slots, &work_LED, 1);
+ switch_leds(ctrl, num_of_slots, &work_LED, 0);
+ switch_leds(ctrl, num_of_slots, &work_LED, 1);
+
+ work_LED = 0x01010000;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+ switch_leds(ctrl, num_of_slots, &work_LED, 0);
+ switch_leds(ctrl, num_of_slots, &work_LED, 1);
+ work_LED = 0x00000101;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+ switch_leds(ctrl, num_of_slots, &work_LED, 0);
+ switch_leds(ctrl, num_of_slots, &work_LED, 1);
+
+ work_LED = 0x01010000;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+ for (loop = 0; loop < num_of_slots; loop++) {
+ set_SOGO(ctrl);
+
+ /* Wait for SOGO interrupt */
+ wait_for_ctrl_irq (ctrl);
+
+ /* Get ready for next iteration */
+ long_delay((3*HZ)/10);
+ work_LED = work_LED >> 16;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOGO interrupt */
+ wait_for_ctrl_irq (ctrl);
+
+ /* Get ready for next iteration */
+ long_delay((3*HZ)/10);
+ work_LED = work_LED << 16;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+ work_LED = work_LED << 1;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+ }
+
+ /* put it back the way it was */
+ writel(save_LED, ctrl->hpc_reg + LED_CONTROL);
+
+ set_SOGO(ctrl);
+
+ /* Wait for SOBS to be unset */
+ wait_for_ctrl_irq (ctrl);
+ break;
+ case 2:
+ /* Do other stuff here! */
+ break;
+ case 3:
+ /* and more... */
+ break;
+ }
+ return 0;
+}
+
+
+/**
+ * configure_new_device - Configures the PCI header information of one board.
+ *
+ * @ctrl: pointer to controller structure
+ * @func: pointer to function structure
+ * @behind_bridge: 1 if this is a recursive call, 0 if not
+ * @resources: pointer to set of resource lists
+ *
+ * Returns 0 if success
+ *
+ */
+static u32 configure_new_device(struct controller * ctrl, struct pci_func * func,
+ u8 behind_bridge, struct resource_lists * resources)
+{
+ u8 temp_byte, function, max_functions, stop_it;
+ int rc;
+ u32 ID;
+ struct pci_func *new_slot;
+ int index;
+
+ new_slot = func;
+
+ dbg("%s\n", __FUNCTION__);
+ /* Check for Multi-function device */
+ ctrl->pci_bus->number = func->bus;
+ rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte);
+ if (rc) {
+ dbg("%s: rc = %d\n", __FUNCTION__, rc);
+ return rc;
+ }
+
+ if (temp_byte & 0x80) /* Multi-function device */
+ max_functions = 8;
+ else
+ max_functions = 1;
+
+ function = 0;
+
+ do {
+ rc = configure_new_function(ctrl, new_slot, behind_bridge, resources);
+
+ if (rc) {
+ dbg("configure_new_function failed %d\n",rc);
+ index = 0;
+
+ while (new_slot) {
+ new_slot = cpqhp_slot_find(new_slot->bus, new_slot->device, index++);
+
+ if (new_slot)
+ cpqhp_return_board_resources(new_slot, resources);
+ }
+
+ return rc;
+ }
+
+ function++;
+
+ stop_it = 0;
+
+ /* The following loop skips to the next present function
+ * and creates a board structure */
+
+ while ((function < max_functions) && (!stop_it)) {
+ pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID);
+
+ if (ID == 0xFFFFFFFF) { /* There's nothing there. */
+ function++;
+ } else { /* There's something there */
+ /* Setup slot structure. */
+ new_slot = cpqhp_slot_create(func->bus);
+
+ if (new_slot == NULL)
+ return 1;
+
+ new_slot->bus = func->bus;
+ new_slot->device = func->device;
+ new_slot->function = function;
+ new_slot->is_a_board = 1;
+ new_slot->status = 0;
+
+ stop_it++;
+ }
+ }
+
+ } while (function < max_functions);
+ dbg("returning from configure_new_device\n");
+
+ return 0;
+}
+
+
+/*
+ Configuration logic that involves the hotplug data structures and
+ their bookkeeping
+ */
+
+
+/**
+ * configure_new_function - Configures the PCI header information of one device
+ *
+ * @ctrl: pointer to controller structure
+ * @func: pointer to function structure
+ * @behind_bridge: 1 if this is a recursive call, 0 if not
+ * @resources: pointer to set of resource lists
+ *
+ * Calls itself recursively for bridged devices.
+ * Returns 0 if success
+ *
+ */
+static int configure_new_function(struct controller *ctrl, struct pci_func *func,
+ u8 behind_bridge,
+ struct resource_lists *resources)
+{
+ int cloop;
+ u8 IRQ = 0;
+ u8 temp_byte;
+ u8 device;
+ u8 class_code;
+ u16 command;
+ u16 temp_word;
+ u32 temp_dword;
+ u32 rc;
+ u32 temp_register;
+ u32 base;
+ u32 ID;
+ unsigned int devfn;
+ struct pci_resource *mem_node;
+ struct pci_resource *p_mem_node;
+ struct pci_resource *io_node;
+ struct pci_resource *bus_node;
+ struct pci_resource *hold_mem_node;
+ struct pci_resource *hold_p_mem_node;
+ struct pci_resource *hold_IO_node;
+ struct pci_resource *hold_bus_node;
+ struct irq_mapping irqs;
+ struct pci_func *new_slot;
+ struct pci_bus *pci_bus;
+ struct resource_lists temp_resources;
+
+ pci_bus = ctrl->pci_bus;
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ /* Check for Bridge */
+ rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte);
+ if (rc)
+ return rc;
+
+ if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
+ /* set Primary bus */
+ dbg("set Primary bus = %d\n", func->bus);
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus);
+ if (rc)
+ return rc;
+
+ /* find range of busses to use */
+ dbg("find ranges of buses to use\n");
+ bus_node = get_max_resource(&(resources->bus_head), 1);
+
+ /* If we don't have any busses to allocate, we can't continue */
+ if (!bus_node)
+ return -ENOMEM;
+
+ /* set Secondary bus */
+ temp_byte = bus_node->base;
+ dbg("set Secondary bus = %d\n", bus_node->base);
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte);
+ if (rc)
+ return rc;
+
+ /* set subordinate bus */
+ temp_byte = bus_node->base + bus_node->length - 1;
+ dbg("set subordinate bus = %d\n", bus_node->base + bus_node->length - 1);
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
+ if (rc)
+ return rc;
+
+ /* set subordinate Latency Timer and base Latency Timer */
+ temp_byte = 0x40;
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
+ if (rc)
+ return rc;
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
+ if (rc)
+ return rc;
+
+ /* set Cache Line size */
+ temp_byte = 0x08;
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
+ if (rc)
+ return rc;
+
+ /* Setup the IO, memory, and prefetchable windows */
+ io_node = get_max_resource(&(resources->io_head), 0x1000);
+ if (!io_node)
+ return -ENOMEM;
+ mem_node = get_max_resource(&(resources->mem_head), 0x100000);
+ if (!mem_node)
+ return -ENOMEM;
+ p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000);
+ if (!p_mem_node)
+ return -ENOMEM;
+ dbg("Setup the IO, memory, and prefetchable windows\n");
+ dbg("io_node\n");
+ dbg("(base, len, next) (%x, %x, %p)\n", io_node->base,
+ io_node->length, io_node->next);
+ dbg("mem_node\n");
+ dbg("(base, len, next) (%x, %x, %p)\n", mem_node->base,
+ mem_node->length, mem_node->next);
+ dbg("p_mem_node\n");
+ dbg("(base, len, next) (%x, %x, %p)\n", p_mem_node->base,
+ p_mem_node->length, p_mem_node->next);
+
+ /* set up the IRQ info */
+ if (!resources->irqs) {
+ irqs.barber_pole = 0;
+ irqs.interrupt[0] = 0;
+ irqs.interrupt[1] = 0;
+ irqs.interrupt[2] = 0;
+ irqs.interrupt[3] = 0;
+ irqs.valid_INT = 0;
+ } else {
+ irqs.barber_pole = resources->irqs->barber_pole;
+ irqs.interrupt[0] = resources->irqs->interrupt[0];
+ irqs.interrupt[1] = resources->irqs->interrupt[1];
+ irqs.interrupt[2] = resources->irqs->interrupt[2];
+ irqs.interrupt[3] = resources->irqs->interrupt[3];
+ irqs.valid_INT = resources->irqs->valid_INT;
+ }
+
+ /* set up resource lists that are now aligned on top and bottom
+ * for anything behind the bridge. */
+ temp_resources.bus_head = bus_node;
+ temp_resources.io_head = io_node;
+ temp_resources.mem_head = mem_node;
+ temp_resources.p_mem_head = p_mem_node;
+ temp_resources.irqs = &irqs;
+
+ /* Make copies of the nodes we are going to pass down so that
+ * if there is a problem,we can just use these to free resources */
+ hold_bus_node = kmalloc(sizeof(*hold_bus_node), GFP_KERNEL);
+ hold_IO_node = kmalloc(sizeof(*hold_IO_node), GFP_KERNEL);
+ hold_mem_node = kmalloc(sizeof(*hold_mem_node), GFP_KERNEL);
+ hold_p_mem_node = kmalloc(sizeof(*hold_p_mem_node), GFP_KERNEL);
+
+ if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) {
+ kfree(hold_bus_node);
+ kfree(hold_IO_node);
+ kfree(hold_mem_node);
+ kfree(hold_p_mem_node);
+
+ return 1;
+ }
+
+ memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource));
+
+ bus_node->base += 1;
+ bus_node->length -= 1;
+ bus_node->next = NULL;
+
+ /* If we have IO resources copy them and fill in the bridge's
+ * IO range registers */
+ if (io_node) {
+ memcpy(hold_IO_node, io_node, sizeof(struct pci_resource));
+ io_node->next = NULL;
+
+ /* set IO base and Limit registers */
+ temp_byte = io_node->base >> 8;
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte);
+
+ temp_byte = (io_node->base + io_node->length - 1) >> 8;
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+ } else {
+ kfree(hold_IO_node);
+ hold_IO_node = NULL;
+ }
+
+ /* If we have memory resources copy them and fill in the
+ * bridge's memory range registers. Otherwise, fill in the
+ * range registers with values that disable them. */
+ if (mem_node) {
+ memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource));
+ mem_node->next = NULL;
+
+ /* set Mem base and Limit registers */
+ temp_word = mem_node->base >> 16;
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+ temp_word = (mem_node->base + mem_node->length - 1) >> 16;
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+ } else {
+ temp_word = 0xFFFF;
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+ temp_word = 0x0000;
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+ kfree(hold_mem_node);
+ hold_mem_node = NULL;
+ }
+
+ /* If we have prefetchable memory resources copy them and
+ * fill in the bridge's memory range registers. Otherwise,
+ * fill in the range registers with values that disable them. */
+ if (p_mem_node) {
+ memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource));
+ p_mem_node->next = NULL;
+
+ /* set Pre Mem base and Limit registers */
+ temp_word = p_mem_node->base >> 16;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+ temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+ } else {
+ temp_word = 0xFFFF;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+ temp_word = 0x0000;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+ kfree(hold_p_mem_node);
+ hold_p_mem_node = NULL;
+ }
+
+ /* Adjust this to compensate for extra adjustment in first loop */
+ irqs.barber_pole--;
+
+ rc = 0;
+
+ /* Here we actually find the devices and configure them */
+ for (device = 0; (device <= 0x1F) && !rc; device++) {
+ irqs.barber_pole = (irqs.barber_pole + 1) & 0x03;
+
+ ID = 0xFFFFFFFF;
+ pci_bus->number = hold_bus_node->base;
+ pci_bus_read_config_dword (pci_bus, PCI_DEVFN(device, 0), 0x00, &ID);
+ pci_bus->number = func->bus;
+
+ if (ID != 0xFFFFFFFF) { /* device present */
+ /* Setup slot structure. */
+ new_slot = cpqhp_slot_create(hold_bus_node->base);
+
+ if (new_slot == NULL) {
+ rc = -ENOMEM;
+ continue;
+ }
+
+ new_slot->bus = hold_bus_node->base;
+ new_slot->device = device;
+ new_slot->function = 0;
+ new_slot->is_a_board = 1;
+ new_slot->status = 0;
+
+ rc = configure_new_device(ctrl, new_slot, 1, &temp_resources);
+ dbg("configure_new_device rc=0x%x\n",rc);
+ } /* End of IF (device in slot?) */
+ } /* End of FOR loop */
+
+ if (rc)
+ goto free_and_out;
+ /* save the interrupt routing information */
+ if (resources->irqs) {
+ resources->irqs->interrupt[0] = irqs.interrupt[0];
+ resources->irqs->interrupt[1] = irqs.interrupt[1];
+ resources->irqs->interrupt[2] = irqs.interrupt[2];
+ resources->irqs->interrupt[3] = irqs.interrupt[3];
+ resources->irqs->valid_INT = irqs.valid_INT;
+ } else if (!behind_bridge) {
+ /* We need to hook up the interrupts here */
+ for (cloop = 0; cloop < 4; cloop++) {
+ if (irqs.valid_INT & (0x01 << cloop)) {
+ rc = cpqhp_set_irq(func->bus, func->device,
+ 0x0A + cloop, irqs.interrupt[cloop]);
+ if (rc)
+ goto free_and_out;
+ }
+ } /* end of for loop */
+ }
+ /* Return unused bus resources
+ * First use the temporary node to store information for
+ * the board */
+ if (hold_bus_node && bus_node && temp_resources.bus_head) {
+ hold_bus_node->length = bus_node->base - hold_bus_node->base;
+
+ hold_bus_node->next = func->bus_head;
+ func->bus_head = hold_bus_node;
+
+ temp_byte = temp_resources.bus_head->base - 1;
+
+ /* set subordinate bus */
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
+
+ if (temp_resources.bus_head->length == 0) {
+ kfree(temp_resources.bus_head);
+ temp_resources.bus_head = NULL;
+ } else {
+ return_resource(&(resources->bus_head), temp_resources.bus_head);
+ }
+ }
+
+ /* If we have IO space available and there is some left,
+ * return the unused portion */
+ if (hold_IO_node && temp_resources.io_head) {
+ io_node = do_pre_bridge_resource_split(&(temp_resources.io_head),
+ &hold_IO_node, 0x1000);
+
+ /* Check if we were able to split something off */
+ if (io_node) {
+ hold_IO_node->base = io_node->base + io_node->length;
+
+ temp_byte = (hold_IO_node->base) >> 8;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_IO_BASE, temp_byte);
+
+ return_resource(&(resources->io_head), io_node);
+ }
+
+ io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000);
+
+ /* Check if we were able to split something off */
+ if (io_node) {
+ /* First use the temporary node to store
+ * information for the board */
+ hold_IO_node->length = io_node->base - hold_IO_node->base;
+
+ /* If we used any, add it to the board's list */
+ if (hold_IO_node->length) {
+ hold_IO_node->next = func->io_head;
+ func->io_head = hold_IO_node;
+
+ temp_byte = (io_node->base - 1) >> 8;
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+
+ return_resource(&(resources->io_head), io_node);
+ } else {
+ /* it doesn't need any IO */
+ temp_word = 0x0000;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_IO_LIMIT, temp_word);
+
+ return_resource(&(resources->io_head), io_node);
+ kfree(hold_IO_node);
+ }
+ } else {
+ /* it used most of the range */
+ hold_IO_node->next = func->io_head;
+ func->io_head = hold_IO_node;
+ }
+ } else if (hold_IO_node) {
+ /* it used the whole range */
+ hold_IO_node->next = func->io_head;
+ func->io_head = hold_IO_node;
+ }
+ /* If we have memory space available and there is some left,
+ * return the unused portion */
+ if (hold_mem_node && temp_resources.mem_head) {
+ mem_node = do_pre_bridge_resource_split(&(temp_resources. mem_head),
+ &hold_mem_node, 0x100000);
+
+ /* Check if we were able to split something off */
+ if (mem_node) {
+ hold_mem_node->base = mem_node->base + mem_node->length;
+
+ temp_word = (hold_mem_node->base) >> 16;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+ return_resource(&(resources->mem_head), mem_node);
+ }
+
+ mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000);
+
+ /* Check if we were able to split something off */
+ if (mem_node) {
+ /* First use the temporary node to store
+ * information for the board */
+ hold_mem_node->length = mem_node->base - hold_mem_node->base;
+
+ if (hold_mem_node->length) {
+ hold_mem_node->next = func->mem_head;
+ func->mem_head = hold_mem_node;
+
+ /* configure end address */
+ temp_word = (mem_node->base - 1) >> 16;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+ /* Return unused resources to the pool */
+ return_resource(&(resources->mem_head), mem_node);
+ } else {
+ /* it doesn't need any Mem */
+ temp_word = 0x0000;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+ return_resource(&(resources->mem_head), mem_node);
+ kfree(hold_mem_node);
+ }
+ } else {
+ /* it used most of the range */
+ hold_mem_node->next = func->mem_head;
+ func->mem_head = hold_mem_node;
+ }
+ } else if (hold_mem_node) {
+ /* it used the whole range */
+ hold_mem_node->next = func->mem_head;
+ func->mem_head = hold_mem_node;
+ }
+ /* If we have prefetchable memory space available and there
+ * is some left at the end, return the unused portion */
+ if (hold_p_mem_node && temp_resources.p_mem_head) {
+ p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head),
+ &hold_p_mem_node, 0x100000);
+
+ /* Check if we were able to split something off */
+ if (p_mem_node) {
+ hold_p_mem_node->base = p_mem_node->base + p_mem_node->length;
+
+ temp_word = (hold_p_mem_node->base) >> 16;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+ return_resource(&(resources->p_mem_head), p_mem_node);
+ }
+
+ p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000);
+
+ /* Check if we were able to split something off */
+ if (p_mem_node) {
+ /* First use the temporary node to store
+ * information for the board */
+ hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base;
+
+ /* If we used any, add it to the board's list */
+ if (hold_p_mem_node->length) {
+ hold_p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = hold_p_mem_node;
+
+ temp_word = (p_mem_node->base - 1) >> 16;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+ return_resource(&(resources->p_mem_head), p_mem_node);
+ } else {
+ /* it doesn't need any PMem */
+ temp_word = 0x0000;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+ return_resource(&(resources->p_mem_head), p_mem_node);
+ kfree(hold_p_mem_node);
+ }
+ } else {
+ /* it used the most of the range */
+ hold_p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = hold_p_mem_node;
+ }
+ } else if (hold_p_mem_node) {
+ /* it used the whole range */
+ hold_p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = hold_p_mem_node;
+ }
+ /* We should be configuring an IRQ and the bridge's base address
+ * registers if it needs them. Although we have never seen such
+ * a device */
+
+ /* enable card */
+ command = 0x0157; /* = PCI_COMMAND_IO |
+ * PCI_COMMAND_MEMORY |
+ * PCI_COMMAND_MASTER |
+ * PCI_COMMAND_INVALIDATE |
+ * PCI_COMMAND_PARITY |
+ * PCI_COMMAND_SERR */
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_COMMAND, command);
+
+ /* set Bridge Control Register */
+ command = 0x07; /* = PCI_BRIDGE_CTL_PARITY |
+ * PCI_BRIDGE_CTL_SERR |
+ * PCI_BRIDGE_CTL_NO_ISA */
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
+ } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
+ /* Standard device */
+ rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
+
+ if (class_code == PCI_BASE_CLASS_DISPLAY) {
+ /* Display (video) adapter (not supported) */
+ return DEVICE_TYPE_NOT_SUPPORTED;
+ }
+ /* Figure out IO and memory needs */
+ for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+ temp_register = 0xFFFFFFFF;
+
+ dbg("CND: bus=%d, devfn=%d, offset=%d\n", pci_bus->number, devfn, cloop);
+ rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
+
+ rc = pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp_register);
+ dbg("CND: base = 0x%x\n", temp_register);
+
+ if (temp_register) { /* If this register is implemented */
+ if ((temp_register & 0x03L) == 0x01) {
+ /* Map IO */
+
+ /* set base = amount of IO space */
+ base = temp_register & 0xFFFFFFFC;
+ base = ~base + 1;
+
+ dbg("CND: length = 0x%x\n", base);
+ io_node = get_io_resource(&(resources->io_head), base);
+ dbg("Got io_node start = %8.8x, length = %8.8x next (%p)\n",
+ io_node->base, io_node->length, io_node->next);
+ dbg("func (%p) io_head (%p)\n", func, func->io_head);
+
+ /* allocate the resource to the board */
+ if (io_node) {
+ base = io_node->base;
+
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ } else
+ return -ENOMEM;
+ } else if ((temp_register & 0x0BL) == 0x08) {
+ /* Map prefetchable memory */
+ base = temp_register & 0xFFFFFFF0;
+ base = ~base + 1;
+
+ dbg("CND: length = 0x%x\n", base);
+ p_mem_node = get_resource(&(resources->p_mem_head), base);
+
+ /* allocate the resource to the board */
+ if (p_mem_node) {
+ base = p_mem_node->base;
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ } else
+ return -ENOMEM;
+ } else if ((temp_register & 0x0BL) == 0x00) {
+ /* Map memory */
+ base = temp_register & 0xFFFFFFF0;
+ base = ~base + 1;
+
+ dbg("CND: length = 0x%x\n", base);
+ mem_node = get_resource(&(resources->mem_head), base);
+
+ /* allocate the resource to the board */
+ if (mem_node) {
+ base = mem_node->base;
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ } else
+ return -ENOMEM;
+ } else if ((temp_register & 0x0BL) == 0x04) {
+ /* Map memory */
+ base = temp_register & 0xFFFFFFF0;
+ base = ~base + 1;
+
+ dbg("CND: length = 0x%x\n", base);
+ mem_node = get_resource(&(resources->mem_head), base);
+
+ /* allocate the resource to the board */
+ if (mem_node) {
+ base = mem_node->base;
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ } else
+ return -ENOMEM;
+ } else if ((temp_register & 0x0BL) == 0x06) {
+ /* Those bits are reserved, we can't handle this */
+ return 1;
+ } else {
+ /* Requesting space below 1M */
+ return NOT_ENOUGH_RESOURCES;
+ }
+
+ rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base);
+
+ /* Check for 64-bit base */
+ if ((temp_register & 0x07L) == 0x04) {
+ cloop += 4;
+
+ /* Upper 32 bits of address always zero
+ * on today's systems */
+ /* FIXME this is probably not true on
+ * Alpha and ia64??? */
+ base = 0;
+ rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base);
+ }
+ }
+ } /* End of base register loop */
+ if (cpqhp_legacy_mode) {
+ /* Figure out which interrupt pin this function uses */
+ rc = pci_bus_read_config_byte (pci_bus, devfn,
+ PCI_INTERRUPT_PIN, &temp_byte);
+
+ /* If this function needs an interrupt and we are behind
+ * a bridge and the pin is tied to something that's
+ * alread mapped, set this one the same */
+ if (temp_byte && resources->irqs &&
+ (resources->irqs->valid_INT &
+ (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) {
+ /* We have to share with something already set up */
+ IRQ = resources->irqs->interrupt[(temp_byte +
+ resources->irqs->barber_pole - 1) & 0x03];
+ } else {
+ /* Program IRQ based on card type */
+ rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
+
+ if (class_code == PCI_BASE_CLASS_STORAGE) {
+ IRQ = cpqhp_disk_irq;
+ } else {
+ IRQ = cpqhp_nic_irq;
+ }
+ }
+
+ /* IRQ Line */
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ);
+ }
+
+ if (!behind_bridge) {
+ rc = cpqhp_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ);
+ if (rc)
+ return 1;
+ } else {
+ /* TBD - this code may also belong in the other clause
+ * of this If statement */
+ resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ;
+ resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03;
+ }
+
+ /* Latency Timer */
+ temp_byte = 0x40;
+ rc = pci_bus_write_config_byte(pci_bus, devfn,
+ PCI_LATENCY_TIMER, temp_byte);
+
+ /* Cache Line size */
+ temp_byte = 0x08;
+ rc = pci_bus_write_config_byte(pci_bus, devfn,
+ PCI_CACHE_LINE_SIZE, temp_byte);
+
+ /* disable ROM base Address */
+ temp_dword = 0x00L;
+ rc = pci_bus_write_config_word(pci_bus, devfn,
+ PCI_ROM_ADDRESS, temp_dword);
+
+ /* enable card */
+ temp_word = 0x0157; /* = PCI_COMMAND_IO |
+ * PCI_COMMAND_MEMORY |
+ * PCI_COMMAND_MASTER |
+ * PCI_COMMAND_INVALIDATE |
+ * PCI_COMMAND_PARITY |
+ * PCI_COMMAND_SERR */
+ rc = pci_bus_write_config_word (pci_bus, devfn,
+ PCI_COMMAND, temp_word);
+ } else { /* End of Not-A-Bridge else */
+ /* It's some strange type of PCI adapter (Cardbus?) */
+ return DEVICE_TYPE_NOT_SUPPORTED;
+ }
+
+ func->configured = 1;
+
+ return 0;
+free_and_out:
+ cpqhp_destroy_resource_list (&temp_resources);
+
+ return_resource(&(resources-> bus_head), hold_bus_node);
+ return_resource(&(resources-> io_head), hold_IO_node);
+ return_resource(&(resources-> mem_head), hold_mem_node);
+ return_resource(&(resources-> p_mem_head), hold_p_mem_node);
+ return rc;
+}
diff --git a/drivers/pci/hotplug/cpqphp_nvram.c b/drivers/pci/hotplug/cpqphp_nvram.c
new file mode 100644
index 000000000000..ac98a11bd1eb
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp_nvram.c
@@ -0,0 +1,666 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 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 <greg@kroah.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include "cpqphp.h"
+#include "cpqphp_nvram.h"
+
+
+#define ROM_INT15_PHY_ADDR 0x0FF859
+#define READ_EV 0xD8A4
+#define WRITE_EV 0xD8A5
+
+struct register_foo {
+ union {
+ unsigned long lword; /* eax */
+ unsigned short word; /* ax */
+
+ struct {
+ unsigned char low; /* al */
+ unsigned char high; /* ah */
+ } byte;
+ } data;
+
+ unsigned char opcode; /* see below */
+ unsigned long length; /* if the reg. is a pointer, how much data */
+} __attribute__ ((packed));
+
+struct all_reg {
+ struct register_foo eax_reg;
+ struct register_foo ebx_reg;
+ struct register_foo ecx_reg;
+ struct register_foo edx_reg;
+ struct register_foo edi_reg;
+ struct register_foo esi_reg;
+ struct register_foo eflags_reg;
+} __attribute__ ((packed));
+
+
+struct ev_hrt_header {
+ u8 Version;
+ u8 num_of_ctrl;
+ u8 next;
+};
+
+struct ev_hrt_ctrl {
+ u8 bus;
+ u8 device;
+ u8 function;
+ u8 mem_avail;
+ u8 p_mem_avail;
+ u8 io_avail;
+ u8 bus_avail;
+ u8 next;
+};
+
+
+static u8 evbuffer_init;
+static u8 evbuffer_length;
+static u8 evbuffer[1024];
+
+static void __iomem *compaq_int15_entry_point;
+
+static spinlock_t int15_lock; /* lock for ordering int15_bios_call() */
+
+
+/* This is a series of function that deals with
+ setting & getting the hotplug resource table in some environment variable.
+*/
+
+/*
+ * We really shouldn't be doing this unless there is a _very_ good reason to!!!
+ * greg k-h
+ */
+
+
+static u32 add_byte( u32 **p_buffer, u8 value, u32 *used, u32 *avail)
+{
+ u8 **tByte;
+
+ if ((*used + 1) > *avail)
+ return(1);
+
+ *((u8*)*p_buffer) = value;
+ tByte = (u8**)p_buffer;
+ (*tByte)++;
+ *used+=1;
+ return(0);
+}
+
+
+static u32 add_dword( u32 **p_buffer, u32 value, u32 *used, u32 *avail)
+{
+ if ((*used + 4) > *avail)
+ return(1);
+
+ **p_buffer = value;
+ (*p_buffer)++;
+ *used+=4;
+ return(0);
+}
+
+
+/*
+ * check_for_compaq_ROM
+ *
+ * this routine verifies that the ROM OEM string is 'COMPAQ'
+ *
+ * returns 0 for non-Compaq ROM, 1 for Compaq ROM
+ */
+static int check_for_compaq_ROM (void __iomem *rom_start)
+{
+ u8 temp1, temp2, temp3, temp4, temp5, temp6;
+ int result = 0;
+
+ temp1 = readb(rom_start + 0xffea + 0);
+ temp2 = readb(rom_start + 0xffea + 1);
+ temp3 = readb(rom_start + 0xffea + 2);
+ temp4 = readb(rom_start + 0xffea + 3);
+ temp5 = readb(rom_start + 0xffea + 4);
+ temp6 = readb(rom_start + 0xffea + 5);
+ if ((temp1 == 'C') &&
+ (temp2 == 'O') &&
+ (temp3 == 'M') &&
+ (temp4 == 'P') &&
+ (temp5 == 'A') &&
+ (temp6 == 'Q')) {
+ result = 1;
+ }
+ dbg ("%s - returned %d\n", __FUNCTION__, result);
+ return result;
+}
+
+
+static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size)
+{
+ unsigned long flags;
+ int op = operation;
+ int ret_val;
+
+ if (!compaq_int15_entry_point)
+ return -ENODEV;
+
+ spin_lock_irqsave(&int15_lock, flags);
+ __asm__ (
+ "xorl %%ebx,%%ebx\n" \
+ "xorl %%edx,%%edx\n" \
+ "pushf\n" \
+ "push %%cs\n" \
+ "cli\n" \
+ "call *%6\n"
+ : "=c" (*buf_size), "=a" (ret_val)
+ : "a" (op), "c" (*buf_size), "S" (ev_name),
+ "D" (buffer), "m" (compaq_int15_entry_point)
+ : "%ebx", "%edx");
+ spin_unlock_irqrestore(&int15_lock, flags);
+
+ return((ret_val & 0xFF00) >> 8);
+}
+
+
+/*
+ * load_HRT
+ *
+ * Read the hot plug Resource Table from NVRAM
+ */
+static int load_HRT (void __iomem *rom_start)
+{
+ u32 available;
+ u32 temp_dword;
+ u8 temp_byte = 0xFF;
+ u32 rc;
+
+ if (!check_for_compaq_ROM(rom_start)) {
+ return -ENODEV;
+ }
+
+ available = 1024;
+
+ // Now load the EV
+ temp_dword = available;
+
+ rc = access_EV(READ_EV, "CQTHPS", evbuffer, &temp_dword);
+
+ evbuffer_length = temp_dword;
+
+ // We're maintaining the resource lists so write FF to invalidate old info
+ temp_dword = 1;
+
+ rc = access_EV(WRITE_EV, "CQTHPS", &temp_byte, &temp_dword);
+
+ return rc;
+}
+
+
+/*
+ * store_HRT
+ *
+ * Save the hot plug Resource Table in NVRAM
+ */
+static u32 store_HRT (void __iomem *rom_start)
+{
+ u32 *buffer;
+ u32 *pFill;
+ u32 usedbytes;
+ u32 available;
+ u32 temp_dword;
+ u32 rc;
+ u8 loop;
+ u8 numCtrl = 0;
+ struct controller *ctrl;
+ struct pci_resource *resNode;
+ struct ev_hrt_header *p_EV_header;
+ struct ev_hrt_ctrl *p_ev_ctrl;
+
+ available = 1024;
+
+ if (!check_for_compaq_ROM(rom_start)) {
+ return(1);
+ }
+
+ buffer = (u32*) evbuffer;
+
+ if (!buffer)
+ return(1);
+
+ pFill = buffer;
+ usedbytes = 0;
+
+ p_EV_header = (struct ev_hrt_header *) pFill;
+
+ ctrl = cpqhp_ctrl_list;
+
+ // The revision of this structure
+ rc = add_byte( &pFill, 1 + ctrl->push_flag, &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ // The number of controllers
+ rc = add_byte( &pFill, 1, &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ while (ctrl) {
+ p_ev_ctrl = (struct ev_hrt_ctrl *) pFill;
+
+ numCtrl++;
+
+ // The bus number
+ rc = add_byte( &pFill, ctrl->bus, &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ // The device Number
+ rc = add_byte( &pFill, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ // The function Number
+ rc = add_byte( &pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ // Skip the number of available entries
+ rc = add_dword( &pFill, 0, &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ // Figure out memory Available
+
+ resNode = ctrl->mem_head;
+
+ loop = 0;
+
+ while (resNode) {
+ loop ++;
+
+ // base
+ rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ // length
+ rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ resNode = resNode->next;
+ }
+
+ // Fill in the number of entries
+ p_ev_ctrl->mem_avail = loop;
+
+ // Figure out prefetchable memory Available
+
+ resNode = ctrl->p_mem_head;
+
+ loop = 0;
+
+ while (resNode) {
+ loop ++;
+
+ // base
+ rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ // length
+ rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ resNode = resNode->next;
+ }
+
+ // Fill in the number of entries
+ p_ev_ctrl->p_mem_avail = loop;
+
+ // Figure out IO Available
+
+ resNode = ctrl->io_head;
+
+ loop = 0;
+
+ while (resNode) {
+ loop ++;
+
+ // base
+ rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ // length
+ rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ resNode = resNode->next;
+ }
+
+ // Fill in the number of entries
+ p_ev_ctrl->io_avail = loop;
+
+ // Figure out bus Available
+
+ resNode = ctrl->bus_head;
+
+ loop = 0;
+
+ while (resNode) {
+ loop ++;
+
+ // base
+ rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ // length
+ rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ resNode = resNode->next;
+ }
+
+ // Fill in the number of entries
+ p_ev_ctrl->bus_avail = loop;
+
+ ctrl = ctrl->next;
+ }
+
+ p_EV_header->num_of_ctrl = numCtrl;
+
+ // Now store the EV
+
+ temp_dword = usedbytes;
+
+ rc = access_EV(WRITE_EV, "CQTHPS", (u8*) buffer, &temp_dword);
+
+ dbg("usedbytes = 0x%x, length = 0x%x\n", usedbytes, temp_dword);
+
+ evbuffer_length = temp_dword;
+
+ if (rc) {
+ err(msg_unable_to_save);
+ return(1);
+ }
+
+ return(0);
+}
+
+
+void compaq_nvram_init (void __iomem *rom_start)
+{
+ if (rom_start) {
+ compaq_int15_entry_point = (rom_start + ROM_INT15_PHY_ADDR - ROM_PHY_ADDR);
+ }
+ dbg("int15 entry = %p\n", compaq_int15_entry_point);
+
+ /* initialize our int15 lock */
+ spin_lock_init(&int15_lock);
+}
+
+
+int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
+{
+ u8 bus, device, function;
+ u8 nummem, numpmem, numio, numbus;
+ u32 rc;
+ u8 *p_byte;
+ struct pci_resource *mem_node;
+ struct pci_resource *p_mem_node;
+ struct pci_resource *io_node;
+ struct pci_resource *bus_node;
+ struct ev_hrt_ctrl *p_ev_ctrl;
+ struct ev_hrt_header *p_EV_header;
+
+ if (!evbuffer_init) {
+ // Read the resource list information in from NVRAM
+ if (load_HRT(rom_start))
+ memset (evbuffer, 0, 1024);
+
+ evbuffer_init = 1;
+ }
+
+ // If we saved information in NVRAM, use it now
+ p_EV_header = (struct ev_hrt_header *) evbuffer;
+
+ // The following code is for systems where version 1.0 of this
+ // driver has been loaded, but doesn't support the hardware.
+ // In that case, the driver would incorrectly store something
+ // in NVRAM.
+ if ((p_EV_header->Version == 2) ||
+ ((p_EV_header->Version == 1) && !ctrl->push_flag)) {
+ p_byte = &(p_EV_header->next);
+
+ p_ev_ctrl = (struct ev_hrt_ctrl *) &(p_EV_header->next);
+
+ p_byte += 3;
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length))
+ return 2;
+
+ bus = p_ev_ctrl->bus;
+ device = p_ev_ctrl->device;
+ function = p_ev_ctrl->function;
+
+ while ((bus != ctrl->bus) ||
+ (device != PCI_SLOT(ctrl->pci_dev->devfn)) ||
+ (function != PCI_FUNC(ctrl->pci_dev->devfn))) {
+ nummem = p_ev_ctrl->mem_avail;
+ numpmem = p_ev_ctrl->p_mem_avail;
+ numio = p_ev_ctrl->io_avail;
+ numbus = p_ev_ctrl->bus_avail;
+
+ p_byte += 4;
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length))
+ return 2;
+
+ // Skip forward to the next entry
+ p_byte += (nummem + numpmem + numio + numbus) * 8;
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length))
+ return 2;
+
+ p_ev_ctrl = (struct ev_hrt_ctrl *) p_byte;
+
+ p_byte += 3;
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length))
+ return 2;
+
+ bus = p_ev_ctrl->bus;
+ device = p_ev_ctrl->device;
+ function = p_ev_ctrl->function;
+ }
+
+ nummem = p_ev_ctrl->mem_avail;
+ numpmem = p_ev_ctrl->p_mem_avail;
+ numio = p_ev_ctrl->io_avail;
+ numbus = p_ev_ctrl->bus_avail;
+
+ p_byte += 4;
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length))
+ return 2;
+
+ while (nummem--) {
+ mem_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!mem_node)
+ break;
+
+ mem_node->base = *(u32*)p_byte;
+ dbg("mem base = %8.8x\n",mem_node->base);
+ p_byte += 4;
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+ kfree(mem_node);
+ return 2;
+ }
+
+ mem_node->length = *(u32*)p_byte;
+ dbg("mem length = %8.8x\n",mem_node->length);
+ p_byte += 4;
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+ kfree(mem_node);
+ return 2;
+ }
+
+ mem_node->next = ctrl->mem_head;
+ ctrl->mem_head = mem_node;
+ }
+
+ while (numpmem--) {
+ p_mem_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!p_mem_node)
+ break;
+
+ p_mem_node->base = *(u32*)p_byte;
+ dbg("pre-mem base = %8.8x\n",p_mem_node->base);
+ p_byte += 4;
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+ kfree(p_mem_node);
+ return 2;
+ }
+
+ p_mem_node->length = *(u32*)p_byte;
+ dbg("pre-mem length = %8.8x\n",p_mem_node->length);
+ p_byte += 4;
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+ kfree(p_mem_node);
+ return 2;
+ }
+
+ p_mem_node->next = ctrl->p_mem_head;
+ ctrl->p_mem_head = p_mem_node;
+ }
+
+ while (numio--) {
+ io_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!io_node)
+ break;
+
+ io_node->base = *(u32*)p_byte;
+ dbg("io base = %8.8x\n",io_node->base);
+ p_byte += 4;
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+ kfree(io_node);
+ return 2;
+ }
+
+ io_node->length = *(u32*)p_byte;
+ dbg("io length = %8.8x\n",io_node->length);
+ p_byte += 4;
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+ kfree(io_node);
+ return 2;
+ }
+
+ io_node->next = ctrl->io_head;
+ ctrl->io_head = io_node;
+ }
+
+ while (numbus--) {
+ bus_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!bus_node)
+ break;
+
+ bus_node->base = *(u32*)p_byte;
+ p_byte += 4;
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+ kfree(bus_node);
+ return 2;
+ }
+
+ bus_node->length = *(u32*)p_byte;
+ p_byte += 4;
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
+ kfree(bus_node);
+ return 2;
+ }
+
+ bus_node->next = ctrl->bus_head;
+ ctrl->bus_head = bus_node;
+ }
+
+ // If all of the following fail, we don't have any resources for
+ // hot plug add
+ rc = 1;
+ rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
+ rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
+ rc &= cpqhp_resource_sort_and_combine(&(ctrl->io_head));
+ rc &= cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
+
+ if (rc)
+ return(rc);
+ } else {
+ if ((evbuffer[0] != 0) && (!ctrl->push_flag))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int compaq_nvram_store (void __iomem *rom_start)
+{
+ int rc = 1;
+
+ if (rom_start == NULL)
+ return -ENODEV;
+
+ if (evbuffer_init) {
+ rc = store_HRT(rom_start);
+ if (rc) {
+ err(msg_unable_to_save);
+ }
+ }
+ return rc;
+}
+
diff --git a/drivers/pci/hotplug/cpqphp_nvram.h b/drivers/pci/hotplug/cpqphp_nvram.h
new file mode 100644
index 000000000000..e89c0702119d
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp_nvram.h
@@ -0,0 +1,57 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * 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 <greg@kroah.com>
+ *
+ */
+
+#ifndef _CPQPHP_NVRAM_H
+#define _CPQPHP_NVRAM_H
+
+#ifndef CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM
+
+static inline void compaq_nvram_init (void __iomem *rom_start)
+{
+ return;
+}
+
+static inline int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
+{
+ return 0;
+}
+
+static inline int compaq_nvram_store (void __iomem *rom_start)
+{
+ return 0;
+}
+
+#else
+
+extern void compaq_nvram_init (void __iomem *rom_start);
+extern int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl);
+extern int compaq_nvram_store (void __iomem *rom_start);
+
+#endif
+
+#endif
+
diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c
new file mode 100644
index 000000000000..93e39c4096a9
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp_pci.c
@@ -0,0 +1,1569 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 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 <greg@kroah.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include "../pci.h"
+#include "cpqphp.h"
+#include "cpqphp_nvram.h"
+#include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependent we are... */
+
+
+u8 cpqhp_nic_irq;
+u8 cpqhp_disk_irq;
+
+static u16 unused_IRQ;
+
+/*
+ * detect_HRT_floating_pointer
+ *
+ * find the Hot Plug Resource Table in the specified region of memory.
+ *
+ */
+static void __iomem *detect_HRT_floating_pointer(void __iomem *begin, void __iomem *end)
+{
+ void __iomem *fp;
+ void __iomem *endp;
+ u8 temp1, temp2, temp3, temp4;
+ int status = 0;
+
+ endp = (end - sizeof(struct hrt) + 1);
+
+ for (fp = begin; fp <= endp; fp += 16) {
+ temp1 = readb(fp + SIG0);
+ temp2 = readb(fp + SIG1);
+ temp3 = readb(fp + SIG2);
+ temp4 = readb(fp + SIG3);
+ if (temp1 == '$' &&
+ temp2 == 'H' &&
+ temp3 == 'R' &&
+ temp4 == 'T') {
+ status = 1;
+ break;
+ }
+ }
+
+ if (!status)
+ fp = NULL;
+
+ dbg("Discovered Hotplug Resource Table at %p\n", fp);
+ return fp;
+}
+
+
+int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)
+{
+ unsigned char bus;
+ struct pci_bus *child;
+ int num;
+
+ if (func->pci_dev == NULL)
+ func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
+
+ /* No pci device, we need to create it then */
+ if (func->pci_dev == NULL) {
+ dbg("INFO: pci_dev still null\n");
+
+ num = pci_scan_slot(ctrl->pci_dev->bus, PCI_DEVFN(func->device, func->function));
+ if (num)
+ pci_bus_add_devices(ctrl->pci_dev->bus);
+
+ func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
+ if (func->pci_dev == NULL) {
+ dbg("ERROR: pci_dev still null\n");
+ return 0;
+ }
+ }
+
+ if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus);
+ child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus);
+ pci_do_scan_bus(child);
+ }
+
+ return 0;
+}
+
+
+int cpqhp_unconfigure_device(struct pci_func* func)
+{
+ int j;
+
+ dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus, func->device, func->function);
+
+ for (j=0; j<8 ; j++) {
+ struct pci_dev* temp = pci_find_slot(func->bus, PCI_DEVFN(func->device, j));
+ if (temp)
+ pci_remove_bus_device(temp);
+ }
+ return 0;
+}
+
+static int PCI_RefinedAccessConfig(struct pci_bus *bus, unsigned int devfn, u8 offset, u32 *value)
+{
+ u32 vendID = 0;
+
+ if (pci_bus_read_config_dword (bus, devfn, PCI_VENDOR_ID, &vendID) == -1)
+ return -1;
+ if (vendID == 0xffffffff)
+ return -1;
+ return pci_bus_read_config_dword (bus, devfn, offset, value);
+}
+
+
+/*
+ * cpqhp_set_irq
+ *
+ * @bus_num: bus number of PCI device
+ * @dev_num: device number of PCI device
+ * @slot: pointer to u8 where slot number will be returned
+ */
+int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num)
+{
+ int rc = 0;
+
+ if (cpqhp_legacy_mode) {
+ struct pci_dev *fakedev;
+ struct pci_bus *fakebus;
+ u16 temp_word;
+
+ fakedev = kmalloc(sizeof(*fakedev), GFP_KERNEL);
+ fakebus = kmalloc(sizeof(*fakebus), GFP_KERNEL);
+ if (!fakedev || !fakebus) {
+ kfree(fakedev);
+ kfree(fakebus);
+ return -ENOMEM;
+ }
+
+ fakedev->devfn = dev_num << 3;
+ fakedev->bus = fakebus;
+ fakebus->number = bus_num;
+ dbg("%s: dev %d, bus %d, pin %d, num %d\n",
+ __FUNCTION__, dev_num, bus_num, int_pin, irq_num);
+ rc = pcibios_set_irq_routing(fakedev, int_pin - 0x0a, irq_num);
+ kfree(fakedev);
+ kfree(fakebus);
+ dbg("%s: rc %d\n", __FUNCTION__, rc);
+ if (!rc)
+ return !rc;
+
+ // set the Edge Level Control Register (ELCR)
+ temp_word = inb(0x4d0);
+ temp_word |= inb(0x4d1) << 8;
+
+ temp_word |= 0x01 << irq_num;
+
+ // This should only be for x86 as it sets the Edge Level Control Register
+ outb((u8) (temp_word & 0xFF), 0x4d0);
+ outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+
+/*
+ * WTF??? This function isn't in the code, yet a function calls it, but the
+ * compiler optimizes it away? strange. Here as a placeholder to keep the
+ * compiler happy.
+ */
+static int PCI_ScanBusNonBridge (u8 bus, u8 device)
+{
+ return 0;
+}
+
+static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev_num)
+{
+ u16 tdevice;
+ u32 work;
+ u8 tbus;
+
+ ctrl->pci_bus->number = bus_num;
+
+ for (tdevice = 0; tdevice < 0xFF; tdevice++) {
+ //Scan for access first
+ if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1)
+ continue;
+ dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice);
+ //Yep we got one. Not a bridge ?
+ if ((work >> 8) != PCI_TO_PCI_BRIDGE_CLASS) {
+ *dev_num = tdevice;
+ dbg("found it !\n");
+ return 0;
+ }
+ }
+ for (tdevice = 0; tdevice < 0xFF; tdevice++) {
+ //Scan for access first
+ if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1)
+ continue;
+ dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice);
+ //Yep we got one. bridge ?
+ if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
+ pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(tdevice, 0), PCI_SECONDARY_BUS, &tbus);
+ dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice);
+ if (PCI_ScanBusNonBridge(tbus, tdevice) == 0)
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge)
+{
+ struct irq_routing_table *PCIIRQRoutingInfoLength;
+ long len;
+ long loop;
+ u32 work;
+
+ u8 tbus, tdevice, tslot;
+
+ PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table();
+ if (!PCIIRQRoutingInfoLength)
+ return -1;
+
+ len = (PCIIRQRoutingInfoLength->size -
+ sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
+ // Make sure I got at least one entry
+ if (len == 0) {
+ if (PCIIRQRoutingInfoLength != NULL)
+ kfree(PCIIRQRoutingInfoLength );
+ return -1;
+ }
+
+ for (loop = 0; loop < len; ++loop) {
+ tbus = PCIIRQRoutingInfoLength->slots[loop].bus;
+ tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn;
+ tslot = PCIIRQRoutingInfoLength->slots[loop].slot;
+
+ if (tslot == slot) {
+ *bus_num = tbus;
+ *dev_num = tdevice;
+ ctrl->pci_bus->number = tbus;
+ pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_VENDOR_ID, &work);
+ if (!nobridge || (work == 0xffffffff)) {
+ if (PCIIRQRoutingInfoLength != NULL)
+ kfree(PCIIRQRoutingInfoLength );
+ return 0;
+ }
+
+ dbg("bus_num %d devfn %d\n", *bus_num, *dev_num);
+ pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_CLASS_REVISION, &work);
+ dbg("work >> 8 (%x) = BRIDGE (%x)\n", work >> 8, PCI_TO_PCI_BRIDGE_CLASS);
+
+ if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
+ pci_bus_read_config_byte (ctrl->pci_bus, *dev_num, PCI_SECONDARY_BUS, &tbus);
+ dbg("Scan bus for Non Bridge: bus %d\n", tbus);
+ if (PCI_ScanBusForNonBridge(ctrl, tbus, dev_num) == 0) {
+ *bus_num = tbus;
+ if (PCIIRQRoutingInfoLength != NULL)
+ kfree(PCIIRQRoutingInfoLength );
+ return 0;
+ }
+ } else {
+ if (PCIIRQRoutingInfoLength != NULL)
+ kfree(PCIIRQRoutingInfoLength );
+ return 0;
+ }
+
+ }
+ }
+ if (PCIIRQRoutingInfoLength != NULL)
+ kfree(PCIIRQRoutingInfoLength );
+ return -1;
+}
+
+
+int cpqhp_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 slot)
+{
+ return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0); //plain (bridges allowed)
+}
+
+
+/* More PCI configuration routines; this time centered around hotplug controller */
+
+
+/*
+ * cpqhp_save_config
+ *
+ * Reads configuration for all slots in a PCI bus and saves info.
+ *
+ * Note: For non-hot plug busses, the slot # saved is the device #
+ *
+ * returns 0 if success
+ */
+int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug)
+{
+ long rc;
+ u8 class_code;
+ u8 header_type;
+ u32 ID;
+ u8 secondary_bus;
+ struct pci_func *new_slot;
+ int sub_bus;
+ int FirstSupported;
+ int LastSupported;
+ int max_functions;
+ int function;
+ u8 DevError;
+ int device = 0;
+ int cloop = 0;
+ int stop_it;
+ int index;
+
+ // Decide which slots are supported
+
+ if (is_hot_plug) {
+ //*********************************
+ // is_hot_plug is the slot mask
+ //*********************************
+ FirstSupported = is_hot_plug >> 4;
+ LastSupported = FirstSupported + (is_hot_plug & 0x0F) - 1;
+ } else {
+ FirstSupported = 0;
+ LastSupported = 0x1F;
+ }
+
+ // Save PCI configuration space for all devices in supported slots
+ ctrl->pci_bus->number = busnumber;
+ for (device = FirstSupported; device <= LastSupported; device++) {
+ ID = 0xFFFFFFFF;
+ rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID);
+
+ if (ID != 0xFFFFFFFF) { // device in slot
+ rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code);
+ if (rc)
+ return rc;
+
+ rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type);
+ if (rc)
+ return rc;
+
+ // If multi-function device, set max_functions to 8
+ if (header_type & 0x80)
+ max_functions = 8;
+ else
+ max_functions = 1;
+
+ function = 0;
+
+ do {
+ DevError = 0;
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // P-P Bridge
+ // Recurse the subordinate bus
+ // get the subordinate bus number
+ rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus);
+ if (rc) {
+ return rc;
+ } else {
+ sub_bus = (int) secondary_bus;
+
+ // Save secondary bus cfg spc
+ // with this recursive call.
+ rc = cpqhp_save_config(ctrl, sub_bus, 0);
+ if (rc)
+ return rc;
+ ctrl->pci_bus->number = busnumber;
+ }
+ }
+
+ index = 0;
+ new_slot = cpqhp_slot_find(busnumber, device, index++);
+ while (new_slot &&
+ (new_slot->function != (u8) function))
+ new_slot = cpqhp_slot_find(busnumber, device, index++);
+
+ if (!new_slot) {
+ // Setup slot structure.
+ new_slot = cpqhp_slot_create(busnumber);
+
+ if (new_slot == NULL)
+ return(1);
+ }
+
+ new_slot->bus = (u8) busnumber;
+ new_slot->device = (u8) device;
+ new_slot->function = (u8) function;
+ new_slot->is_a_board = 1;
+ new_slot->switch_save = 0x10;
+ // In case of unsupported board
+ new_slot->status = DevError;
+ new_slot->pci_dev = pci_find_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function);
+
+ for (cloop = 0; cloop < 0x20; cloop++) {
+ rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
+ if (rc)
+ return rc;
+ }
+
+ function++;
+
+ stop_it = 0;
+
+ // this loop skips to the next present function
+ // reading in Class Code and Header type.
+
+ while ((function < max_functions)&&(!stop_it)) {
+ rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID);
+ if (ID == 0xFFFFFFFF) { // nothing there.
+ function++;
+ } else { // Something there
+ rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code);
+ if (rc)
+ return rc;
+
+ rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type);
+ if (rc)
+ return rc;
+
+ stop_it++;
+ }
+ }
+
+ } while (function < max_functions);
+ } // End of IF (device in slot?)
+ else if (is_hot_plug) {
+ // Setup slot structure with entry for empty slot
+ new_slot = cpqhp_slot_create(busnumber);
+
+ if (new_slot == NULL) {
+ return(1);
+ }
+
+ new_slot->bus = (u8) busnumber;
+ new_slot->device = (u8) device;
+ new_slot->function = 0;
+ new_slot->is_a_board = 0;
+ new_slot->presence_save = 0;
+ new_slot->switch_save = 0;
+ }
+ } // End of FOR loop
+
+ return(0);
+}
+
+
+/*
+ * cpqhp_save_slot_config
+ *
+ * Saves configuration info for all PCI devices in a given slot
+ * including subordinate busses.
+ *
+ * returns 0 if success
+ */
+int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot)
+{
+ long rc;
+ u8 class_code;
+ u8 header_type;
+ u32 ID;
+ u8 secondary_bus;
+ int sub_bus;
+ int max_functions;
+ int function;
+ int cloop = 0;
+ int stop_it;
+
+ ID = 0xFFFFFFFF;
+
+ ctrl->pci_bus->number = new_slot->bus;
+ pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID);
+
+ if (ID != 0xFFFFFFFF) { // device in slot
+ pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code);
+ pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type);
+
+ if (header_type & 0x80) // Multi-function device
+ max_functions = 8;
+ else
+ max_functions = 1;
+
+ function = 0;
+
+ do {
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
+ // Recurse the subordinate bus
+ pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus);
+
+ sub_bus = (int) secondary_bus;
+
+ // Save the config headers for the secondary bus.
+ rc = cpqhp_save_config(ctrl, sub_bus, 0);
+ if (rc)
+ return(rc);
+ ctrl->pci_bus->number = new_slot->bus;
+
+ } // End of IF
+
+ new_slot->status = 0;
+
+ for (cloop = 0; cloop < 0x20; cloop++) {
+ pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
+ }
+
+ function++;
+
+ stop_it = 0;
+
+ // this loop skips to the next present function
+ // reading in the Class Code and the Header type.
+
+ while ((function < max_functions) && (!stop_it)) {
+ pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID);
+
+ if (ID == 0xFFFFFFFF) { // nothing there.
+ function++;
+ } else { // Something there
+ pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code);
+
+ pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type);
+
+ stop_it++;
+ }
+ }
+
+ } while (function < max_functions);
+ } // End of IF (device in slot?)
+ else {
+ return 2;
+ }
+
+ return 0;
+}
+
+
+/*
+ * cpqhp_save_base_addr_length
+ *
+ * Saves the length of all base address registers for the
+ * specified slot. this is for hot plug REPLACE
+ *
+ * returns 0 if success
+ */
+int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func)
+{
+ u8 cloop;
+ u8 header_type;
+ u8 secondary_bus;
+ u8 type;
+ int sub_bus;
+ u32 temp_register;
+ u32 base;
+ u32 rc;
+ struct pci_func *next;
+ int index = 0;
+ struct pci_bus *pci_bus = ctrl->pci_bus;
+ unsigned int devfn;
+
+ func = cpqhp_slot_find(func->bus, func->device, index++);
+
+ while (func != NULL) {
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ // Check for Bridge
+ pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+ // PCI-PCI Bridge
+ pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+
+ sub_bus = (int) secondary_bus;
+
+ next = cpqhp_slot_list[sub_bus];
+
+ while (next != NULL) {
+ rc = cpqhp_save_base_addr_length(ctrl, next);
+ if (rc)
+ return rc;
+
+ next = next->next;
+ }
+ pci_bus->number = func->bus;
+
+ //FIXME: this loop is duplicated in the non-bridge case. The two could be rolled together
+ // Figure out IO and memory base lengths
+ for (cloop = 0x10; cloop <= 0x14; cloop += 4) {
+ temp_register = 0xFFFFFFFF;
+ pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
+ pci_bus_read_config_dword (pci_bus, devfn, cloop, &base);
+
+ if (base) { // If this register is implemented
+ if (base & 0x01L) {
+ // IO base
+ // set base = amount of IO space requested
+ base = base & 0xFFFFFFFE;
+ base = (~base) + 1;
+
+ type = 1;
+ } else {
+ // memory base
+ base = base & 0xFFFFFFF0;
+ base = (~base) + 1;
+
+ type = 0;
+ }
+ } else {
+ base = 0x0L;
+ type = 0;
+ }
+
+ // Save information in slot structure
+ func->base_length[(cloop - 0x10) >> 2] =
+ base;
+ func->base_type[(cloop - 0x10) >> 2] = type;
+
+ } // End of base register loop
+
+
+ } else if ((header_type & 0x7F) == 0x00) { // PCI-PCI Bridge
+ // Figure out IO and memory base lengths
+ for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+ temp_register = 0xFFFFFFFF;
+ pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
+ pci_bus_read_config_dword (pci_bus, devfn, cloop, &base);
+
+ if (base) { // If this register is implemented
+ if (base & 0x01L) {
+ // IO base
+ // base = amount of IO space requested
+ base = base & 0xFFFFFFFE;
+ base = (~base) + 1;
+
+ type = 1;
+ } else {
+ // memory base
+ // base = amount of memory space requested
+ base = base & 0xFFFFFFF0;
+ base = (~base) + 1;
+
+ type = 0;
+ }
+ } else {
+ base = 0x0L;
+ type = 0;
+ }
+
+ // Save information in slot structure
+ func->base_length[(cloop - 0x10) >> 2] = base;
+ func->base_type[(cloop - 0x10) >> 2] = type;
+
+ } // End of base register loop
+
+ } else { // Some other unknown header type
+ }
+
+ // find the next device in this slot
+ func = cpqhp_slot_find(func->bus, func->device, index++);
+ }
+
+ return(0);
+}
+
+
+/*
+ * cpqhp_save_used_resources
+ *
+ * Stores used resource information for existing boards. this is
+ * for boards that were in the system when this driver was loaded.
+ * this function is for hot plug ADD
+ *
+ * returns 0 if success
+ */
+int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
+{
+ u8 cloop;
+ u8 header_type;
+ u8 secondary_bus;
+ u8 temp_byte;
+ u8 b_base;
+ u8 b_length;
+ u16 command;
+ u16 save_command;
+ u16 w_base;
+ u16 w_length;
+ u32 temp_register;
+ u32 save_base;
+ u32 base;
+ int index = 0;
+ struct pci_resource *mem_node;
+ struct pci_resource *p_mem_node;
+ struct pci_resource *io_node;
+ struct pci_resource *bus_node;
+ struct pci_bus *pci_bus = ctrl->pci_bus;
+ unsigned int devfn;
+
+ func = cpqhp_slot_find(func->bus, func->device, index++);
+
+ while ((func != NULL) && func->is_a_board) {
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ // Save the command register
+ pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command);
+
+ // disable card
+ command = 0x00;
+ pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
+
+ // Check for Bridge
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
+ // Clear Bridge Control Register
+ command = 0x00;
+ pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte);
+
+ bus_node = kmalloc(sizeof(*bus_node), GFP_KERNEL);
+ if (!bus_node)
+ return -ENOMEM;
+
+ bus_node->base = secondary_bus;
+ bus_node->length = temp_byte - secondary_bus + 1;
+
+ bus_node->next = func->bus_head;
+ func->bus_head = bus_node;
+
+ // Save IO base and Limit registers
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &b_base);
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &b_length);
+
+ if ((b_base <= b_length) && (save_command & 0x01)) {
+ io_node = kmalloc(sizeof(*io_node), GFP_KERNEL);
+ if (!io_node)
+ return -ENOMEM;
+
+ io_node->base = (b_base & 0xF0) << 8;
+ io_node->length = (b_length - b_base + 0x10) << 8;
+
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ }
+
+ // Save memory base and Limit registers
+ pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base);
+ pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length);
+
+ if ((w_base <= w_length) && (save_command & 0x02)) {
+ mem_node = kmalloc(sizeof(*mem_node), GFP_KERNEL);
+ if (!mem_node)
+ return -ENOMEM;
+
+ mem_node->base = w_base << 16;
+ mem_node->length = (w_length - w_base + 0x10) << 16;
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ }
+
+ // Save prefetchable memory base and Limit registers
+ pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base);
+ pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length);
+
+ if ((w_base <= w_length) && (save_command & 0x02)) {
+ p_mem_node = kmalloc(sizeof(*p_mem_node), GFP_KERNEL);
+ if (!p_mem_node)
+ return -ENOMEM;
+
+ p_mem_node->base = w_base << 16;
+ p_mem_node->length = (w_length - w_base + 0x10) << 16;
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ }
+ // Figure out IO and memory base lengths
+ for (cloop = 0x10; cloop <= 0x14; cloop += 4) {
+ pci_bus_read_config_dword (pci_bus, devfn, cloop, &save_base);
+
+ temp_register = 0xFFFFFFFF;
+ pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register);
+ pci_bus_read_config_dword(pci_bus, devfn, cloop, &base);
+
+ temp_register = base;
+
+ if (base) { // If this register is implemented
+ if (((base & 0x03L) == 0x01)
+ && (save_command & 0x01)) {
+ // IO base
+ // set temp_register = amount of IO space requested
+ temp_register = base & 0xFFFFFFFE;
+ temp_register = (~temp_register) + 1;
+
+ io_node = kmalloc(sizeof(*io_node),
+ GFP_KERNEL);
+ if (!io_node)
+ return -ENOMEM;
+
+ io_node->base =
+ save_base & (~0x03L);
+ io_node->length = temp_register;
+
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ } else
+ if (((base & 0x0BL) == 0x08)
+ && (save_command & 0x02)) {
+ // prefetchable memory base
+ temp_register = base & 0xFFFFFFF0;
+ temp_register = (~temp_register) + 1;
+
+ p_mem_node = kmalloc(sizeof(*p_mem_node),
+ GFP_KERNEL);
+ if (!p_mem_node)
+ return -ENOMEM;
+
+ p_mem_node->base = save_base & (~0x0FL);
+ p_mem_node->length = temp_register;
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ } else
+ if (((base & 0x0BL) == 0x00)
+ && (save_command & 0x02)) {
+ // prefetchable memory base
+ temp_register = base & 0xFFFFFFF0;
+ temp_register = (~temp_register) + 1;
+
+ mem_node = kmalloc(sizeof(*mem_node),
+ GFP_KERNEL);
+ if (!mem_node)
+ return -ENOMEM;
+
+ mem_node->base = save_base & (~0x0FL);
+ mem_node->length = temp_register;
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ } else
+ return(1);
+ }
+ } // End of base register loop
+ } else if ((header_type & 0x7F) == 0x00) { // Standard header
+ // Figure out IO and memory base lengths
+ for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+ pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base);
+
+ temp_register = 0xFFFFFFFF;
+ pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register);
+ pci_bus_read_config_dword(pci_bus, devfn, cloop, &base);
+
+ temp_register = base;
+
+ if (base) { // If this register is implemented
+ if (((base & 0x03L) == 0x01)
+ && (save_command & 0x01)) {
+ // IO base
+ // set temp_register = amount of IO space requested
+ temp_register = base & 0xFFFFFFFE;
+ temp_register = (~temp_register) + 1;
+
+ io_node = kmalloc(sizeof(*io_node),
+ GFP_KERNEL);
+ if (!io_node)
+ return -ENOMEM;
+
+ io_node->base = save_base & (~0x01L);
+ io_node->length = temp_register;
+
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ } else
+ if (((base & 0x0BL) == 0x08)
+ && (save_command & 0x02)) {
+ // prefetchable memory base
+ temp_register = base & 0xFFFFFFF0;
+ temp_register = (~temp_register) + 1;
+
+ p_mem_node = kmalloc(sizeof(*p_mem_node),
+ GFP_KERNEL);
+ if (!p_mem_node)
+ return -ENOMEM;
+
+ p_mem_node->base = save_base & (~0x0FL);
+ p_mem_node->length = temp_register;
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ } else
+ if (((base & 0x0BL) == 0x00)
+ && (save_command & 0x02)) {
+ // prefetchable memory base
+ temp_register = base & 0xFFFFFFF0;
+ temp_register = (~temp_register) + 1;
+
+ mem_node = kmalloc(sizeof(*mem_node),
+ GFP_KERNEL);
+ if (!mem_node)
+ return -ENOMEM;
+
+ mem_node->base = save_base & (~0x0FL);
+ mem_node->length = temp_register;
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ } else
+ return(1);
+ }
+ } // End of base register loop
+ } else { // Some other unknown header type
+ }
+
+ // find the next device in this slot
+ func = cpqhp_slot_find(func->bus, func->device, index++);
+ }
+
+ return(0);
+}
+
+
+/*
+ * cpqhp_configure_board
+ *
+ * Copies saved configuration information to one slot.
+ * this is called recursively for bridge devices.
+ * this is for hot plug REPLACE!
+ *
+ * returns 0 if success
+ */
+int cpqhp_configure_board(struct controller *ctrl, struct pci_func * func)
+{
+ int cloop;
+ u8 header_type;
+ u8 secondary_bus;
+ int sub_bus;
+ struct pci_func *next;
+ u32 temp;
+ u32 rc;
+ int index = 0;
+ struct pci_bus *pci_bus = ctrl->pci_bus;
+ unsigned int devfn;
+
+ func = cpqhp_slot_find(func->bus, func->device, index++);
+
+ while (func != NULL) {
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ // Start at the top of config space so that the control
+ // registers are programmed last
+ for (cloop = 0x3C; cloop > 0; cloop -= 4) {
+ pci_bus_write_config_dword (pci_bus, devfn, cloop, func->config_space[cloop >> 2]);
+ }
+
+ pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+ // If this is a bridge device, restore subordinate devices
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
+ pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+
+ sub_bus = (int) secondary_bus;
+
+ next = cpqhp_slot_list[sub_bus];
+
+ while (next != NULL) {
+ rc = cpqhp_configure_board(ctrl, next);
+ if (rc)
+ return rc;
+
+ next = next->next;
+ }
+ } else {
+
+ // Check all the base Address Registers to make sure
+ // they are the same. If not, the board is different.
+
+ for (cloop = 16; cloop < 40; cloop += 4) {
+ pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp);
+
+ if (temp != func->config_space[cloop >> 2]) {
+ dbg("Config space compare failure!!! offset = %x\n", cloop);
+ dbg("bus = %x, device = %x, function = %x\n", func->bus, func->device, func->function);
+ dbg("temp = %x, config space = %x\n\n", temp, func->config_space[cloop >> 2]);
+ return 1;
+ }
+ }
+ }
+
+ func->configured = 1;
+
+ func = cpqhp_slot_find(func->bus, func->device, index++);
+ }
+
+ return 0;
+}
+
+
+/*
+ * cpqhp_valid_replace
+ *
+ * this function checks to see if a board is the same as the
+ * one it is replacing. this check will detect if the device's
+ * vendor or device id's are the same
+ *
+ * returns 0 if the board is the same nonzero otherwise
+ */
+int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func)
+{
+ u8 cloop;
+ u8 header_type;
+ u8 secondary_bus;
+ u8 type;
+ u32 temp_register = 0;
+ u32 base;
+ u32 rc;
+ struct pci_func *next;
+ int index = 0;
+ struct pci_bus *pci_bus = ctrl->pci_bus;
+ unsigned int devfn;
+
+ if (!func->is_a_board)
+ return(ADD_NOT_SUPPORTED);
+
+ func = cpqhp_slot_find(func->bus, func->device, index++);
+
+ while (func != NULL) {
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ pci_bus_read_config_dword (pci_bus, devfn, PCI_VENDOR_ID, &temp_register);
+
+ // No adapter present
+ if (temp_register == 0xFFFFFFFF)
+ return(NO_ADAPTER_PRESENT);
+
+ if (temp_register != func->config_space[0])
+ return(ADAPTER_NOT_SAME);
+
+ // Check for same revision number and class code
+ pci_bus_read_config_dword (pci_bus, devfn, PCI_CLASS_REVISION, &temp_register);
+
+ // Adapter not the same
+ if (temp_register != func->config_space[0x08 >> 2])
+ return(ADAPTER_NOT_SAME);
+
+ // Check for Bridge
+ pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
+ // In order to continue checking, we must program the
+ // bus registers in the bridge to respond to accesses
+ // for it's subordinate bus(es)
+
+ temp_register = func->config_space[0x18 >> 2];
+ pci_bus_write_config_dword (pci_bus, devfn, PCI_PRIMARY_BUS, temp_register);
+
+ secondary_bus = (temp_register >> 8) & 0xFF;
+
+ next = cpqhp_slot_list[secondary_bus];
+
+ while (next != NULL) {
+ rc = cpqhp_valid_replace(ctrl, next);
+ if (rc)
+ return rc;
+
+ next = next->next;
+ }
+
+ }
+ // Check to see if it is a standard config header
+ else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
+ // Check subsystem vendor and ID
+ pci_bus_read_config_dword (pci_bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &temp_register);
+
+ if (temp_register != func->config_space[0x2C >> 2]) {
+ // If it's a SMART-2 and the register isn't filled
+ // in, ignore the difference because
+ // they just have an old rev of the firmware
+
+ if (!((func->config_space[0] == 0xAE100E11)
+ && (temp_register == 0x00L)))
+ return(ADAPTER_NOT_SAME);
+ }
+ // Figure out IO and memory base lengths
+ for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+ temp_register = 0xFFFFFFFF;
+ pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
+ pci_bus_read_config_dword (pci_bus, devfn, cloop, &base);
+ if (base) { // If this register is implemented
+ if (base & 0x01L) {
+ // IO base
+ // set base = amount of IO space requested
+ base = base & 0xFFFFFFFE;
+ base = (~base) + 1;
+
+ type = 1;
+ } else {
+ // memory base
+ base = base & 0xFFFFFFF0;
+ base = (~base) + 1;
+
+ type = 0;
+ }
+ } else {
+ base = 0x0L;
+ type = 0;
+ }
+
+ // Check information in slot structure
+ if (func->base_length[(cloop - 0x10) >> 2] != base)
+ return(ADAPTER_NOT_SAME);
+
+ if (func->base_type[(cloop - 0x10) >> 2] != type)
+ return(ADAPTER_NOT_SAME);
+
+ } // End of base register loop
+
+ } // End of (type 0 config space) else
+ else {
+ // this is not a type 0 or 1 config space header so
+ // we don't know how to do it
+ return(DEVICE_TYPE_NOT_SUPPORTED);
+ }
+
+ // Get the next function
+ func = cpqhp_slot_find(func->bus, func->device, index++);
+ }
+
+
+ return 0;
+}
+
+
+/*
+ * cpqhp_find_available_resources
+ *
+ * Finds available memory, IO, and IRQ resources for programming
+ * devices which may be added to the system
+ * this function is for hot plug ADD!
+ *
+ * returns 0 if success
+ */
+int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_start)
+{
+ u8 temp;
+ u8 populated_slot;
+ u8 bridged_slot;
+ void __iomem *one_slot;
+ void __iomem *rom_resource_table;
+ struct pci_func *func = NULL;
+ int i = 10, index;
+ u32 temp_dword, rc;
+ struct pci_resource *mem_node;
+ struct pci_resource *p_mem_node;
+ struct pci_resource *io_node;
+ struct pci_resource *bus_node;
+
+ rom_resource_table = detect_HRT_floating_pointer(rom_start, rom_start+0xffff);
+ dbg("rom_resource_table = %p\n", rom_resource_table);
+
+ if (rom_resource_table == NULL) {
+ return -ENODEV;
+ }
+ // Sum all resources and setup resource maps
+ unused_IRQ = readl(rom_resource_table + UNUSED_IRQ);
+ dbg("unused_IRQ = %x\n", unused_IRQ);
+
+ temp = 0;
+ while (unused_IRQ) {
+ if (unused_IRQ & 1) {
+ cpqhp_disk_irq = temp;
+ break;
+ }
+ unused_IRQ = unused_IRQ >> 1;
+ temp++;
+ }
+
+ dbg("cpqhp_disk_irq= %d\n", cpqhp_disk_irq);
+ unused_IRQ = unused_IRQ >> 1;
+ temp++;
+
+ while (unused_IRQ) {
+ if (unused_IRQ & 1) {
+ cpqhp_nic_irq = temp;
+ break;
+ }
+ unused_IRQ = unused_IRQ >> 1;
+ temp++;
+ }
+
+ dbg("cpqhp_nic_irq= %d\n", cpqhp_nic_irq);
+ unused_IRQ = readl(rom_resource_table + PCIIRQ);
+
+ temp = 0;
+
+ if (!cpqhp_nic_irq) {
+ cpqhp_nic_irq = ctrl->cfgspc_irq;
+ }
+
+ if (!cpqhp_disk_irq) {
+ cpqhp_disk_irq = ctrl->cfgspc_irq;
+ }
+
+ dbg("cpqhp_disk_irq, cpqhp_nic_irq= %d, %d\n", cpqhp_disk_irq, cpqhp_nic_irq);
+
+ rc = compaq_nvram_load(rom_start, ctrl);
+ if (rc)
+ return rc;
+
+ one_slot = rom_resource_table + sizeof (struct hrt);
+
+ i = readb(rom_resource_table + NUMBER_OF_ENTRIES);
+ dbg("number_of_entries = %d\n", i);
+
+ if (!readb(one_slot + SECONDARY_BUS))
+ return 1;
+
+ dbg("dev|IO base|length|Mem base|length|Pre base|length|PB SB MB\n");
+
+ while (i && readb(one_slot + SECONDARY_BUS)) {
+ u8 dev_func = readb(one_slot + DEV_FUNC);
+ u8 primary_bus = readb(one_slot + PRIMARY_BUS);
+ u8 secondary_bus = readb(one_slot + SECONDARY_BUS);
+ u8 max_bus = readb(one_slot + MAX_BUS);
+ u16 io_base = readw(one_slot + IO_BASE);
+ u16 io_length = readw(one_slot + IO_LENGTH);
+ u16 mem_base = readw(one_slot + MEM_BASE);
+ u16 mem_length = readw(one_slot + MEM_LENGTH);
+ u16 pre_mem_base = readw(one_slot + PRE_MEM_BASE);
+ u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH);
+
+ dbg("%2.2x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x |%2.2x %2.2x %2.2x\n",
+ dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length,
+ primary_bus, secondary_bus, max_bus);
+
+ // If this entry isn't for our controller's bus, ignore it
+ if (primary_bus != ctrl->bus) {
+ i--;
+ one_slot += sizeof (struct slot_rt);
+ continue;
+ }
+ // find out if this entry is for an occupied slot
+ ctrl->pci_bus->number = primary_bus;
+ pci_bus_read_config_dword (ctrl->pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword);
+ dbg("temp_D_word = %x\n", temp_dword);
+
+ if (temp_dword != 0xFFFFFFFF) {
+ index = 0;
+ func = cpqhp_slot_find(primary_bus, dev_func >> 3, 0);
+
+ while (func && (func->function != (dev_func & 0x07))) {
+ dbg("func = %p (bus, dev, fun) = (%d, %d, %d)\n", func, primary_bus, dev_func >> 3, index);
+ func = cpqhp_slot_find(primary_bus, dev_func >> 3, index++);
+ }
+
+ // If we can't find a match, skip this table entry
+ if (!func) {
+ i--;
+ one_slot += sizeof (struct slot_rt);
+ continue;
+ }
+ // this may not work and shouldn't be used
+ if (secondary_bus != primary_bus)
+ bridged_slot = 1;
+ else
+ bridged_slot = 0;
+
+ populated_slot = 1;
+ } else {
+ populated_slot = 0;
+ bridged_slot = 0;
+ }
+
+
+ // If we've got a valid IO base, use it
+
+ temp_dword = io_base + io_length;
+
+ if ((io_base) && (temp_dword < 0x10000)) {
+ io_node = kmalloc(sizeof(*io_node), GFP_KERNEL);
+ if (!io_node)
+ return -ENOMEM;
+
+ io_node->base = io_base;
+ io_node->length = io_length;
+
+ dbg("found io_node(base, length) = %x, %x\n",
+ io_node->base, io_node->length);
+ dbg("populated slot =%d \n", populated_slot);
+ if (!populated_slot) {
+ io_node->next = ctrl->io_head;
+ ctrl->io_head = io_node;
+ } else {
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ }
+ }
+
+ // If we've got a valid memory base, use it
+ temp_dword = mem_base + mem_length;
+ if ((mem_base) && (temp_dword < 0x10000)) {
+ mem_node = kmalloc(sizeof(*mem_node), GFP_KERNEL);
+ if (!mem_node)
+ return -ENOMEM;
+
+ mem_node->base = mem_base << 16;
+
+ mem_node->length = mem_length << 16;
+
+ dbg("found mem_node(base, length) = %x, %x\n",
+ mem_node->base, mem_node->length);
+ dbg("populated slot =%d \n", populated_slot);
+ if (!populated_slot) {
+ mem_node->next = ctrl->mem_head;
+ ctrl->mem_head = mem_node;
+ } else {
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ }
+ }
+
+ // If we've got a valid prefetchable memory base, and
+ // the base + length isn't greater than 0xFFFF
+ temp_dword = pre_mem_base + pre_mem_length;
+ if ((pre_mem_base) && (temp_dword < 0x10000)) {
+ p_mem_node = kmalloc(sizeof(*p_mem_node), GFP_KERNEL);
+ if (!p_mem_node)
+ return -ENOMEM;
+
+ p_mem_node->base = pre_mem_base << 16;
+
+ p_mem_node->length = pre_mem_length << 16;
+ dbg("found p_mem_node(base, length) = %x, %x\n",
+ p_mem_node->base, p_mem_node->length);
+ dbg("populated slot =%d \n", populated_slot);
+
+ if (!populated_slot) {
+ p_mem_node->next = ctrl->p_mem_head;
+ ctrl->p_mem_head = p_mem_node;
+ } else {
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ }
+ }
+
+ // If we've got a valid bus number, use it
+ // The second condition is to ignore bus numbers on
+ // populated slots that don't have PCI-PCI bridges
+ if (secondary_bus && (secondary_bus != primary_bus)) {
+ bus_node = kmalloc(sizeof(*bus_node), GFP_KERNEL);
+ if (!bus_node)
+ return -ENOMEM;
+
+ bus_node->base = secondary_bus;
+ bus_node->length = max_bus - secondary_bus + 1;
+ dbg("found bus_node(base, length) = %x, %x\n",
+ bus_node->base, bus_node->length);
+ dbg("populated slot =%d \n", populated_slot);
+ if (!populated_slot) {
+ bus_node->next = ctrl->bus_head;
+ ctrl->bus_head = bus_node;
+ } else {
+ bus_node->next = func->bus_head;
+ func->bus_head = bus_node;
+ }
+ }
+
+ i--;
+ one_slot += sizeof (struct slot_rt);
+ }
+
+ // If all of the following fail, we don't have any resources for
+ // hot plug add
+ rc = 1;
+ rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
+ rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
+ rc &= cpqhp_resource_sort_and_combine(&(ctrl->io_head));
+ rc &= cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
+
+ return rc;
+}
+
+
+/*
+ * cpqhp_return_board_resources
+ *
+ * this routine returns all resources allocated to a board to
+ * the available pool.
+ *
+ * returns 0 if success
+ */
+int cpqhp_return_board_resources(struct pci_func * func, struct resource_lists * resources)
+{
+ int rc = 0;
+ struct pci_resource *node;
+ struct pci_resource *t_node;
+ dbg("%s\n", __FUNCTION__);
+
+ if (!func)
+ return 1;
+
+ node = func->io_head;
+ func->io_head = NULL;
+ while (node) {
+ t_node = node->next;
+ return_resource(&(resources->io_head), node);
+ node = t_node;
+ }
+
+ node = func->mem_head;
+ func->mem_head = NULL;
+ while (node) {
+ t_node = node->next;
+ return_resource(&(resources->mem_head), node);
+ node = t_node;
+ }
+
+ node = func->p_mem_head;
+ func->p_mem_head = NULL;
+ while (node) {
+ t_node = node->next;
+ return_resource(&(resources->p_mem_head), node);
+ node = t_node;
+ }
+
+ node = func->bus_head;
+ func->bus_head = NULL;
+ while (node) {
+ t_node = node->next;
+ return_resource(&(resources->bus_head), node);
+ node = t_node;
+ }
+
+ rc |= cpqhp_resource_sort_and_combine(&(resources->mem_head));
+ rc |= cpqhp_resource_sort_and_combine(&(resources->p_mem_head));
+ rc |= cpqhp_resource_sort_and_combine(&(resources->io_head));
+ rc |= cpqhp_resource_sort_and_combine(&(resources->bus_head));
+
+ return rc;
+}
+
+
+/*
+ * cpqhp_destroy_resource_list
+ *
+ * Puts node back in the resource list pointed to by head
+ */
+void cpqhp_destroy_resource_list (struct resource_lists * resources)
+{
+ struct pci_resource *res, *tres;
+
+ res = resources->io_head;
+ resources->io_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = resources->mem_head;
+ resources->mem_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = resources->p_mem_head;
+ resources->p_mem_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = resources->bus_head;
+ resources->bus_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+}
+
+
+/*
+ * cpqhp_destroy_board_resources
+ *
+ * Puts node back in the resource list pointed to by head
+ */
+void cpqhp_destroy_board_resources (struct pci_func * func)
+{
+ struct pci_resource *res, *tres;
+
+ res = func->io_head;
+ func->io_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = func->mem_head;
+ func->mem_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = func->p_mem_head;
+ func->p_mem_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+
+ res = func->bus_head;
+ func->bus_head = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+}
+
diff --git a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c
new file mode 100644
index 000000000000..41c7971d06c5
--- /dev/null
+++ b/drivers/pci/hotplug/cpqphp_sysfs.c
@@ -0,0 +1,143 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 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 <greg@kroah.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include "cpqphp.h"
+
+
+/* A few routines that create sysfs entries for the hot plug controller */
+
+static ssize_t show_ctrl (struct device *dev, char *buf)
+{
+ struct pci_dev *pci_dev;
+ struct controller *ctrl;
+ char * out = buf;
+ int index;
+ struct pci_resource *res;
+
+ pci_dev = container_of (dev, struct pci_dev, dev);
+ ctrl = pci_get_drvdata(pci_dev);
+
+ out += sprintf(buf, "Free resources: memory\n");
+ index = 11;
+ res = ctrl->mem_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ out += sprintf(out, "Free resources: prefetchable memory\n");
+ index = 11;
+ res = ctrl->p_mem_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ out += sprintf(out, "Free resources: IO\n");
+ index = 11;
+ res = ctrl->io_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ out += sprintf(out, "Free resources: bus numbers\n");
+ index = 11;
+ res = ctrl->bus_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+
+ return out - buf;
+}
+static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL);
+
+static ssize_t show_dev (struct device *dev, char *buf)
+{
+ struct pci_dev *pci_dev;
+ struct controller *ctrl;
+ char * out = buf;
+ int index;
+ struct pci_resource *res;
+ struct pci_func *new_slot;
+ struct slot *slot;
+
+ pci_dev = container_of (dev, struct pci_dev, dev);
+ ctrl = pci_get_drvdata(pci_dev);
+
+ slot=ctrl->slot;
+
+ while (slot) {
+ new_slot = cpqhp_slot_find(slot->bus, slot->device, 0);
+ if (!new_slot)
+ break;
+ out += sprintf(out, "assigned resources: memory\n");
+ index = 11;
+ res = new_slot->mem_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ out += sprintf(out, "assigned resources: prefetchable memory\n");
+ index = 11;
+ res = new_slot->p_mem_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ out += sprintf(out, "assigned resources: IO\n");
+ index = 11;
+ res = new_slot->io_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ out += sprintf(out, "assigned resources: bus numbers\n");
+ index = 11;
+ res = new_slot->bus_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ slot=slot->next;
+ }
+
+ return out - buf;
+}
+static DEVICE_ATTR (dev, S_IRUGO, show_dev, NULL);
+
+void cpqhp_create_ctrl_files (struct controller *ctrl)
+{
+ device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
+ device_create_file (&ctrl->pci_dev->dev, &dev_attr_dev);
+}
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
new file mode 100644
index 000000000000..8e47fa66e25e
--- /dev/null
+++ b/drivers/pci/hotplug/fakephp.c
@@ -0,0 +1,358 @@
+/*
+ * Fake PCI Hot Plug Controller Driver
+ *
+ * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2003 IBM Corp.
+ * Copyright (C) 2003 Rolf Eike Beer <eike-kernel@sf-tec.de>
+ *
+ * Based on ideas and code from:
+ * Vladimir Kondratiev <vladimir.kondratiev@intel.com>
+ * Rolf Eike Beer <eike-kernel@sf-tec.de>
+ *
+ * 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, version 2 of the License.
+ *
+ * Send feedback to <greg@kroah.com>
+ */
+
+/*
+ *
+ * This driver will "emulate" removing PCI devices from the system. If
+ * the "power" file is written to with "0" then the specified PCI device
+ * will be completely removed from the kernel.
+ *
+ * WARNING, this does NOT turn off the power to the PCI device. This is
+ * a "logical" removal, not a physical or electrical removal.
+ *
+ * Use this module at your own risk, you have been warned!
+ *
+ * Enabling PCI devices is left as an exercise for the reader...
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include "pci_hotplug.h"
+#include "../pci.h"
+
+#if !defined(MODULE)
+ #define MY_NAME "fakephp"
+#else
+ #define MY_NAME THIS_MODULE->name
+#endif
+
+#define dbg(format, arg...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: " format, \
+ 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 DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
+#define DRIVER_DESC "Fake PCI Hot Plug Controller Driver"
+
+struct dummy_slot {
+ struct list_head node;
+ struct hotplug_slot *slot;
+ struct pci_dev *dev;
+};
+
+static int debug;
+static LIST_HEAD(slot_list);
+
+static int enable_slot (struct hotplug_slot *slot);
+static int disable_slot (struct hotplug_slot *slot);
+
+static struct hotplug_slot_ops dummy_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .enable_slot = enable_slot,
+ .disable_slot = disable_slot,
+};
+
+static void dummy_release(struct hotplug_slot *slot)
+{
+ struct dummy_slot *dslot = slot->private;
+
+ list_del(&dslot->node);
+ kfree(dslot->slot->info);
+ kfree(dslot->slot);
+ pci_dev_put(dslot->dev);
+ kfree(dslot);
+}
+
+static int add_slot(struct pci_dev *dev)
+{
+ struct dummy_slot *dslot;
+ struct hotplug_slot *slot;
+ int retval = -ENOMEM;
+
+ slot = kmalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
+ if (!slot)
+ goto error;
+ memset(slot, 0, sizeof(*slot));
+
+ slot->info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
+ if (!slot->info)
+ goto error_slot;
+ memset(slot->info, 0, sizeof(struct hotplug_slot_info));
+
+ slot->info->power_status = 1;
+ slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
+ slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
+
+ slot->name = &dev->dev.bus_id[0];
+ dbg("slot->name = %s\n", slot->name);
+
+ dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL);
+ if (!dslot)
+ goto error_info;
+
+ slot->ops = &dummy_hotplug_slot_ops;
+ slot->release = &dummy_release;
+ slot->private = dslot;
+
+ retval = pci_hp_register(slot);
+ if (retval) {
+ err("pci_hp_register failed with error %d\n", retval);
+ goto error_dslot;
+ }
+
+ dslot->slot = slot;
+ dslot->dev = pci_dev_get(dev);
+ list_add (&dslot->node, &slot_list);
+ return retval;
+
+error_dslot:
+ kfree(dslot);
+error_info:
+ kfree(slot->info);
+error_slot:
+ kfree(slot);
+error:
+ return retval;
+}
+
+static int __init pci_scan_buses(void)
+{
+ struct pci_dev *dev = NULL;
+ int retval = 0;
+
+ while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+ retval = add_slot(dev);
+ if (retval) {
+ pci_dev_put(dev);
+ break;
+ }
+ }
+
+ return retval;
+}
+
+static void remove_slot(struct dummy_slot *dslot)
+{
+ int retval;
+
+ dbg("removing slot %s\n", dslot->slot->name);
+ retval = pci_hp_deregister(dslot->slot);
+ if (retval)
+ err("Problem unregistering a slot %s\n", dslot->slot->name);
+}
+
+/**
+ * Rescan slot.
+ * Tries hard not to re-enable already existing devices
+ * also handles scanning of subfunctions
+ *
+ * @param temp Device template. Should be set: bus and devfn.
+ */
+static void pci_rescan_slot(struct pci_dev *temp)
+{
+ struct pci_bus *bus = temp->bus;
+ struct pci_dev *dev;
+ int func;
+ u8 hdr_type;
+ if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) {
+ temp->hdr_type = hdr_type & 0x7f;
+ if (!pci_find_slot(bus->number, temp->devfn)) {
+ dev = pci_scan_single_device(bus, temp->devfn);
+ if (dev) {
+ dbg("New device on %s function %x:%x\n",
+ bus->name, temp->devfn >> 3,
+ temp->devfn & 7);
+ pci_bus_add_device(dev);
+ add_slot(dev);
+ }
+ }
+ /* multifunction device? */
+ if (!(hdr_type & 0x80))
+ return;
+
+ /* continue scanning for other functions */
+ for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) {
+ if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type))
+ continue;
+ temp->hdr_type = hdr_type & 0x7f;
+
+ if (!pci_find_slot(bus->number, temp->devfn)) {
+ dev = pci_scan_single_device(bus, temp->devfn);
+ if (dev) {
+ dbg("New device on %s function %x:%x\n",
+ bus->name, temp->devfn >> 3,
+ temp->devfn & 7);
+ pci_bus_add_device(dev);
+ add_slot(dev);
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Rescan PCI bus.
+ * call pci_rescan_slot for each possible function of the bus
+ *
+ * @param bus
+ */
+static void pci_rescan_bus(const struct pci_bus *bus)
+{
+ unsigned int devfn;
+ struct pci_dev *dev;
+ dev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL);
+ if (!dev)
+ return;
+
+ memset(dev, 0, sizeof(dev));
+ dev->bus = (struct pci_bus*)bus;
+ dev->sysdata = bus->sysdata;
+ for (devfn = 0; devfn < 0x100; devfn += 8) {
+ dev->devfn = devfn;
+ pci_rescan_slot(dev);
+ }
+ kfree(dev);
+}
+
+/* recursively scan all buses */
+static void pci_rescan_buses(const struct list_head *list)
+{
+ const struct list_head *l;
+ list_for_each(l,list) {
+ const struct pci_bus *b = pci_bus_b(l);
+ pci_rescan_bus(b);
+ pci_rescan_buses(&b->children);
+ }
+}
+
+/* initiate rescan of all pci buses */
+static inline void pci_rescan(void) {
+ pci_rescan_buses(&pci_root_buses);
+}
+
+
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+ /* mis-use enable_slot for rescanning of the pci bus */
+ pci_rescan();
+ return -ENODEV;
+}
+
+/* find the hotplug_slot for the pci_dev */
+static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev)
+{
+ struct dummy_slot *dslot;
+
+ list_for_each_entry(dslot, &slot_list, node) {
+ if (dslot->dev == dev)
+ return dslot->slot;
+ }
+ return NULL;
+}
+
+
+static int disable_slot(struct hotplug_slot *slot)
+{
+ struct dummy_slot *dslot;
+ struct hotplug_slot *hslot;
+ struct pci_dev *dev;
+ int func;
+
+ if (!slot)
+ return -ENODEV;
+ dslot = slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, slot->name);
+
+ /* don't disable bridged devices just yet, we can't handle them easily... */
+ if (dslot->dev->subordinate) {
+ err("Can't remove PCI devices with other PCI devices behind it yet.\n");
+ return -ENODEV;
+ }
+ /* search for subfunctions and disable them first */
+ if (!(dslot->dev->devfn & 7)) {
+ for (func = 1; func < 8; func++) {
+ dev = pci_find_slot(dslot->dev->bus->number,
+ dslot->dev->devfn + func);
+ if (dev) {
+ hslot = get_slot_from_dev(dev);
+ if (hslot)
+ disable_slot(hslot);
+ else {
+ err("Hotplug slot not found for subfunction of PCI device\n");
+ return -ENODEV;
+ }
+ } else
+ dbg("No device in slot found\n");
+ }
+ }
+
+ /* remove the device from the pci core */
+ pci_remove_bus_device(dslot->dev);
+
+ /* blow away this sysfs entry and other parts. */
+ remove_slot(dslot);
+
+ return 0;
+}
+
+static void cleanup_slots (void)
+{
+ struct list_head *tmp;
+ struct list_head *next;
+ struct dummy_slot *dslot;
+
+ list_for_each_safe (tmp, next, &slot_list) {
+ dslot = list_entry (tmp, struct dummy_slot, node);
+ remove_slot(dslot);
+ }
+
+}
+
+static int __init dummyphp_init(void)
+{
+ info(DRIVER_DESC "\n");
+
+ return pci_scan_buses();
+}
+
+
+static void __exit dummyphp_exit(void)
+{
+ cleanup_slots();
+}
+
+module_init(dummyphp_init);
+module_exit(dummyphp_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+
diff --git a/drivers/pci/hotplug/ibmphp.h b/drivers/pci/hotplug/ibmphp.h
new file mode 100644
index 000000000000..5bc039da647f
--- /dev/null
+++ b/drivers/pci/hotplug/ibmphp.h
@@ -0,0 +1,763 @@
+#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-2003 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(MODULE)
+ #define MY_NAME "ibmphpd"
+#else
+ #define MY_NAME THIS_MODULE->name
+#endif
+#define debug(fmt, arg...) do { if (ibmphp_debug == 1) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
+#define debug_pci(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;
+ u8 chassis_num;
+// 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;
+ u8 wpindex;
+ u8 chassis_num;
+ struct list_head rio_detail_list;
+};
+
+struct opt_rio {
+ u8 rio_type;
+ u8 chassis_num;
+ u8 first_slot_num;
+ u8 middle_num;
+ struct list_head opt_rio_list;
+};
+
+struct opt_rio_lo {
+ u8 rio_type;
+ u8 chassis_num;
+ u8 first_slot_num;
+ u8 middle_num;
+ u8 pack_count;
+ struct list_head opt_rio_lo_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;
+};
+
+#define HPC_DEVICE_ID 0x0246
+#define HPC_SUBSYSTEM_ID 0x0247
+#define HPC_PCI_OFFSET 0x40
+/*************************************************************************
+* 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;
+ u32 start_addr;
+ u32 end_addr;
+ u8 marked; /* for NVRAM */
+ 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 controller_id;
+ u8 current_speed;
+ u8 current_bus_mode;
+ u8 index;
+ 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;
+ 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);
+extern int ibmphp_register_pci (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_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 CARD_INFO 0x07
+#define PCIX133 0x07
+#define PCIX66 0x05
+#define PCI66 0x04
+
+extern struct pci_bus *ibmphp_pci_bus;
+
+/* 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;
+ u8 real_physical_slot_num;
+ char name[100];
+ u32 capabilities;
+ u8 supported_speed;
+ u8 supported_bus_mode;
+ 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;
+ struct pci_dev *ctrl_dev; /* in case where controller is PCI */
+ u8 starting_slot_num; /* starting and ending slot #'s this ctrl controls*/
+ u8 ending_slot_num;
+ u8 revision;
+ u8 options; /* which options HPC supports */
+ u8 status;
+ u8 ctlr_id;
+ 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_do_disable_slot (struct slot *slot_cur);
+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;
+
+#endif //__IBMPHP_H
+
diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c
new file mode 100644
index 000000000000..0392e004258f
--- /dev/null
+++ b/drivers/pci/hotplug/ibmphp_core.c
@@ -0,0 +1,1422 @@
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Chuck Cole, Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation
+ *
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001-2003 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 "../pci.h"
+#include "../../../arch/i386/pci/pci.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.6"
+#define DRIVER_DESC "IBM Hot Plug PCI Controller Driver"
+
+int ibmphp_debug;
+
+static int debug;
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC (debug, "Debugging mode enabled or not");
+MODULE_LICENSE ("GPL");
+MODULE_DESCRIPTION (DRIVER_DESC);
+
+struct pci_bus *ibmphp_pci_bus;
+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);
+ else
+ slot_cur->bus_on->current_bus_mode = 0xFF;
+
+ 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)
+ rc = get_cur_bus_info(sl);
+ return rc;
+}
+
+static int __init get_max_slots (void)
+{
+ struct slot * slot_cur;
+ struct list_head * tmp;
+ u8 slot_count = 0;
+
+ list_for_each(tmp, &ibmphp_slot_head) {
+ slot_cur = list_entry(tmp, struct slot, ibm_slot_list);
+ /* sometimes the hot-pluggable slots start with 4 (not always from 1) */
+ slot_count = max(slot_count, slot_cur->number);
+ }
+ 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;
+ }
+ msleep(3000); /* 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");
+ retval = -EIO;
+ }
+ return retval;
+}
+
+static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
+{
+ int rc = 0;
+ struct slot *pslot;
+ u8 cmd;
+
+ 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 = hotplug_slot->private;
+ if (pslot)
+ rc = ibmphp_hpc_writeslot(pslot, cmd);
+ else
+ rc = -ENODEV;
+ }
+ } else
+ rc = -ENODEV;
+
+ 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;
+ 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 = hotplug_slot->private;
+ if (pslot) {
+ memcpy(&myslot, pslot, sizeof(struct slot));
+ rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+ &(myslot.status));
+ if (!rc)
+ rc = ibmphp_hpc_readslot(pslot,
+ READ_EXTSLOTSTATUS,
+ &(myslot.ext_status));
+ if (!rc)
+ *value = SLOT_ATTN(myslot.status,
+ myslot.ext_status);
+ }
+ }
+
+ ibmphp_unlock_operations();
+ debug("get_attention_status - Exit rc[%d] value[%x]\n", rc, *value);
+ return rc;
+}
+
+static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value)
+{
+ int rc = -ENODEV;
+ struct slot *pslot;
+ 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 = hotplug_slot->private;
+ if (pslot) {
+ memcpy(&myslot, pslot, sizeof(struct slot));
+ rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+ &(myslot.status));
+ if (!rc)
+ *value = SLOT_LATCH(myslot.status);
+ }
+ }
+
+ ibmphp_unlock_operations();
+ debug("get_latch_status - Exit rc[%d] rc[%x] value[%x]\n",
+ rc, rc, *value);
+ return rc;
+}
+
+
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
+{
+ int rc = -ENODEV;
+ struct slot *pslot;
+ 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 = hotplug_slot->private;
+ if (pslot) {
+ memcpy(&myslot, pslot, sizeof(struct slot));
+ rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+ &(myslot.status));
+ if (!rc)
+ *value = SLOT_PWRGD(myslot.status);
+ }
+ }
+
+ ibmphp_unlock_operations();
+ debug("get_power_status - Exit rc[%d] rc[%x] value[%x]\n",
+ rc, rc, *value);
+ return rc;
+}
+
+static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 * value)
+{
+ int rc = -ENODEV;
+ struct slot *pslot;
+ u8 present;
+ 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 = hotplug_slot->private;
+ if (pslot) {
+ memcpy(&myslot, pslot, sizeof(struct slot));
+ rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+ &(myslot.status));
+ if (!rc) {
+ present = SLOT_PRESENT(myslot.status);
+ if (present == HPC_SLOT_EMPTY)
+ *value = 0;
+ else
+ *value = 1;
+ }
+ }
+ }
+
+ ibmphp_unlock_operations();
+ debug("get_adapter_present - Exit rc[%d] value[%x]\n", rc, *value);
+ return rc;
+}
+
+static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+{
+ int rc = -ENODEV;
+ struct slot *pslot;
+ u8 mode = 0;
+
+ debug("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __FUNCTION__,
+ hotplug_slot, value);
+
+ ibmphp_lock_operations();
+
+ if (hotplug_slot && value) {
+ pslot = hotplug_slot->private;
+ if (pslot) {
+ rc = 0;
+ mode = pslot->supported_bus_mode;
+ *value = pslot->supported_speed;
+ switch (*value) {
+ case BUS_SPEED_33:
+ break;
+ case BUS_SPEED_66:
+ if (mode == BUS_MODE_PCIX)
+ *value += 0x01;
+ break;
+ case BUS_SPEED_100:
+ case BUS_SPEED_133:
+ *value = pslot->supported_speed + 0x01;
+ break;
+ default:
+ /* Note (will need to change): there would be soon 256, 512 also */
+ rc = -ENODEV;
+ }
+ }
+ }
+
+ ibmphp_unlock_operations();
+ debug("%s - Exit rc[%d] value[%x]\n", __FUNCTION__, rc, *value);
+ return rc;
+}
+
+static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+{
+ int rc = -ENODEV;
+ struct slot *pslot;
+ u8 mode = 0;
+
+ debug("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __FUNCTION__,
+ hotplug_slot, value);
+
+ ibmphp_lock_operations();
+
+ if (hotplug_slot && value) {
+ pslot = 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;
+ switch (*value) {
+ case BUS_SPEED_33:
+ break;
+ case BUS_SPEED_66:
+ if (mode == BUS_MODE_PCIX)
+ *value += 0x01;
+ else if (mode == BUS_MODE_PCI)
+ ;
+ else
+ *value = PCI_SPEED_UNKNOWN;
+ break;
+ case BUS_SPEED_100:
+ case BUS_SPEED_133:
+ *value += 0x01;
+ break;
+ default:
+ /* Note of change: there would also be 256, 512 soon */
+ rc = -ENODEV;
+ }
+ }
+ }
+ }
+
+ ibmphp_unlock_operations();
+ debug("%s - Exit rc[%d] value[%x]\n", __FUNCTION__, 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;
+ struct slot myslot;
+
+ debug("get_max_adapter_speed_1 - Entry hotplug_slot[%lx] pvalue[%lx]\n",
+ (ulong)hotplug_slot, (ulong) value);
+
+ if (flag)
+ ibmphp_lock_operations();
+
+ if (hotplug_slot && value) {
+ pslot = hotplug_slot->private;
+ if (pslot) {
+ memcpy(&myslot, pslot, sizeof(struct slot));
+ rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+ &(myslot.status));
+
+ if (!(SLOT_LATCH (myslot.status)) &&
+ (SLOT_PRESENT (myslot.status))) {
+ rc = ibmphp_hpc_readslot(pslot,
+ READ_EXTSLOTSTATUS,
+ &(myslot.ext_status));
+ if (!rc)
+ *value = SLOT_SPEED(myslot.ext_status);
+ } else
+ *value = MAX_ADAPTER_NONE;
+ }
+ }
+
+ if (flag)
+ ibmphp_unlock_operations();
+
+ debug("get_max_adapter_speed_1 - Exit rc[%d] value[%x]\n", rc, *value);
+ return rc;
+}
+
+static int get_bus_name(struct hotplug_slot *hotplug_slot, char * value)
+{
+ int rc = -ENODEV;
+ struct slot *pslot = NULL;
+
+ debug("get_bus_name - Entry hotplug_slot[%lx]\n", (ulong)hotplug_slot);
+
+ ibmphp_lock_operations();
+
+ if (hotplug_slot) {
+ pslot = hotplug_slot->private;
+ if (pslot) {
+ rc = 0;
+ snprintf(value, 100, "Bus %x", pslot->bus);
+ }
+ } else
+ rc = -ENODEV;
+
+ ibmphp_unlock_operations();
+ debug("get_bus_name - 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 init_ops(void)
+{
+ struct slot *slot_cur;
+ struct list_head *tmp;
+ int retval;
+ int rc;
+
+ list_for_each(tmp, &ibmphp_slot_head) {
+ slot_cur = list_entry(tmp, struct slot, ibm_slot_list);
+
+ if (!slot_cur)
+ return -ENODEV;
+
+ 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\n", slot_cur->status);
+ debug("ext_status = %x\n", slot_cur->ext_status);
+ debug("SLOT_POWER = %x\n", SLOT_POWER(slot_cur->status));
+ debug("SLOT_PRESENT = %x\n", SLOT_PRESENT(slot_cur->status));
+ debug("SLOT_LATCH = %x\n", SLOT_LATCH(slot_cur->status));
+
+ if ((SLOT_PWRGD(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;
+
+ switch (opn) {
+ case ENABLE:
+ if (!(SLOT_PWRGD(slot_cur->status)) &&
+ (SLOT_PRESENT(slot_cur->status)) &&
+ !(SLOT_LATCH(slot_cur->status)))
+ return 0;
+ break;
+ case DISABLE:
+ if ((SLOT_PWRGD(slot_cur->status)) &&
+ (SLOT_PRESENT(slot_cur->status)) &&
+ !(SLOT_LATCH(slot_cur->status)))
+ return 0;
+ break;
+ default:
+ 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;
+ int rc;
+ u8 bus_speed;
+ u8 mode;
+
+ info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
+ if (!info) {
+ err("out of system memory\n");
+ return -ENOMEM;
+ }
+
+ info->power_status = SLOT_PWRGD(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;
+ mode = slot_cur->bus_on->current_bus_mode;
+
+ switch (bus_speed) {
+ case BUS_SPEED_33:
+ break;
+ case BUS_SPEED_66:
+ if (mode == BUS_MODE_PCIX)
+ bus_speed += 0x01;
+ else if (mode == BUS_MODE_PCI)
+ ;
+ else
+ bus_speed = PCI_SPEED_UNKNOWN;
+ break;
+ case BUS_SPEED_100:
+ case BUS_SPEED_133:
+ bus_speed += 0x01;
+ break;
+ default:
+ bus_speed = PCI_SPEED_UNKNOWN;
+ }
+
+ info->cur_bus_speed = bus_speed;
+ info->max_bus_speed = slot_cur->hotplug_slot->info->max_bus_speed;
+ // To do: bus_names
+
+ rc = pci_hp_change_slot_info(slot_cur->hotplug_slot, 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 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;
+
+ debug("%s -- enter\n", __FUNCTION__);
+
+ 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);
+ }
+ debug("%s -- exit\n", __FUNCTION__);
+}
+
+static void ibm_unconfigure_device(struct pci_func *func)
+{
+ struct pci_dev *temp;
+ u8 j;
+
+ debug("inside %s\n", __FUNCTION__);
+ 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)
+ pci_remove_bus_device(temp);
+ }
+}
+
+/*
+ * The following function is to fix kernel bug regarding
+ * getting bus entries, here we manually add those primary
+ * bus entries to kernel bus structure whenever apply
+ */
+static u8 bus_structure_fixup(u8 busno)
+{
+ struct pci_bus *bus;
+ struct pci_dev *dev;
+ u16 l;
+
+ if (pci_find_bus(0, busno) || !(ibmphp_find_same_bus_num(busno)))
+ return 1;
+
+ bus = kmalloc(sizeof(*bus), GFP_KERNEL);
+ if (!bus) {
+ err("%s - out of memory\n", __FUNCTION__);
+ return 1;
+ }
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ kfree(bus);
+ err("%s - out of memory\n", __FUNCTION__);
+ return 1;
+ }
+
+ bus->number = busno;
+ bus->ops = ibmphp_pci_bus->ops;
+ dev->bus = bus;
+ for (dev->devfn = 0; dev->devfn < 256; dev->devfn += 8) {
+ if (!pci_read_config_word(dev, PCI_VENDOR_ID, &l) &&
+ (l != 0x0000) && (l != 0xffff)) {
+ debug("%s - Inside bus_struture_fixup()\n",
+ __FUNCTION__);
+ pci_scan_bus(busno, ibmphp_pci_bus->ops, NULL);
+ break;
+ }
+ }
+
+ kfree(dev);
+ kfree(bus);
+
+ return 0;
+}
+
+static int ibm_configure_device(struct pci_func *func)
+{
+ unsigned char bus;
+ struct pci_bus *child;
+ int num;
+ int flag = 0; /* this is to make sure we don't double scan the bus,
+ for bridged devices primarily */
+
+ if (!(bus_structure_fixup(func->busno)))
+ flag = 1;
+ if (func->dev == NULL)
+ func->dev = pci_find_slot(func->busno,
+ PCI_DEVFN(func->device, func->function));
+
+ if (func->dev == NULL) {
+ struct pci_bus *bus = pci_find_bus(0, func->busno);
+ if (!bus)
+ return 0;
+
+ num = pci_scan_slot(bus,
+ PCI_DEVFN(func->device, func->function));
+ if (num)
+ pci_bus_add_devices(bus);
+
+ func->dev = pci_find_slot(func->busno,
+ PCI_DEVFN(func->device, func->function));
+ if (func->dev == NULL) {
+ err("ERROR... : pci_dev still NULL\n");
+ return 0;
+ }
+ }
+ if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) {
+ pci_read_config_byte(func->dev, PCI_SECONDARY_BUS, &bus);
+ child = pci_add_new_bus(func->dev->bus, func->dev, bus);
+ pci_do_scan_bus(child);
+ }
+
+ return 0;
+}
+
+/*******************************************************
+ * 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);
+ if (!tmp_slot)
+ return 0;
+ rc = slot_update(&tmp_slot);
+ if (rc)
+ return 0;
+ if (SLOT_PRESENT(tmp_slot->status) &&
+ SLOT_PWRGD(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 and bus capability
+ * Parameters: slot
+ * Returns: bus is set (0) or error code
+ ***********************************************************/
+static int set_bus(struct slot * slot_cur)
+{
+ int rc;
+ u8 speed;
+ u8 cmd = 0x0;
+ int retval;
+ static struct pci_device_id ciobx[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 0x0101) },
+ { },
+ };
+
+ 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)) {
+ if ((slot_cur->supported_speed >= BUS_SPEED_66) &&
+ (slot_cur->supported_bus_mode == BUS_MODE_PCIX))
+ cmd = HPC_BUS_66PCIXMODE;
+ else if (!SLOT_BUS_MODE(slot_cur->ext_status))
+ /* if max slot/bus capability is 66 pci
+ and there's no bus mode mismatch, then
+ the adapter supports 66 pci */
+ cmd = HPC_BUS_66CONVMODE;
+ else
+ cmd = HPC_BUS_33CONVMODE;
+ } else {
+ if (slot_cur->supported_speed >= BUS_SPEED_66)
+ cmd = HPC_BUS_66CONVMODE;
+ else
+ cmd = HPC_BUS_33CONVMODE;
+ }
+ break;
+ case HPC_SLOT_SPEED_133:
+ switch (slot_cur->supported_speed) {
+ case BUS_SPEED_33:
+ cmd = HPC_BUS_33CONVMODE;
+ break;
+ case BUS_SPEED_66:
+ if (slot_cur->supported_bus_mode == BUS_MODE_PCIX)
+ cmd = HPC_BUS_66PCIXMODE;
+ else
+ cmd = HPC_BUS_66CONVMODE;
+ break;
+ case BUS_SPEED_100:
+ cmd = HPC_BUS_100PCIXMODE;
+ break;
+ case BUS_SPEED_133:
+ /* This is to take care of the bug in CIOBX chip */
+ if (pci_dev_present(ciobx))
+ ibmphp_hpc_writeslot(slot_cur,
+ HPC_BUS_100PCIXMODE);
+ cmd = HPC_BUS_133PCIXMODE;
+ break;
+ default:
+ err("Wrong bus speed\n");
+ return -ENODEV;
+ }
+ break;
+ default:
+ err("wrong slot speed\n");
+ return -ENODEV;
+ }
+ debug("setting bus speed for slot %d, cmd %x\n",
+ slot_cur->number, cmd);
+ retval = ibmphp_hpc_writeslot(slot_cur, cmd);
+ if (retval) {
+ err("setting bus speed failed\n");
+ return retval;
+ }
+ if (CTLR_RESULT(slot_cur->ctrl->status)) {
+ err("command not completed successfully in set_bus\n");
+ return -EIO;
+ }
+ }
+ /* This is for x440, once Brandon fixes the firmware,
+ will not need this delay */
+ msleep(1000);
+ debug("%s -Exit\n", __FUNCTION__);
+ return 0;
+}
+
+/* This routine checks the bus limitations that the slot is on from the BIOS.
+ * This is used in deciding whether or not to power up the slot.
+ * (electrical/spec limitations. For example, >1 133 MHz or >2 66 PCI cards on
+ * same bus)
+ * Parameters: slot
+ * Returns: 0 = no limitations, -EINVAL = exceeded limitations on the bus
+ */
+static int check_limitations(struct slot *slot_cur)
+{
+ u8 i;
+ struct slot * tmp_slot;
+ u8 count = 0;
+ u8 limitation = 0;
+
+ for (i = slot_cur->bus_on->slot_min; i <= slot_cur->bus_on->slot_max; i++) {
+ tmp_slot = ibmphp_get_slot_from_physical_num(i);
+ if (!tmp_slot)
+ return -ENODEV;
+ if ((SLOT_PWRGD(tmp_slot->status)) &&
+ !(SLOT_CONNECT(tmp_slot->status)))
+ count++;
+ }
+ get_cur_bus_info(&slot_cur);
+ switch (slot_cur->bus_on->current_speed) {
+ case BUS_SPEED_33:
+ limitation = slot_cur->bus_on->slots_at_33_conv;
+ break;
+ case BUS_SPEED_66:
+ if (slot_cur->bus_on->current_bus_mode == BUS_MODE_PCIX)
+ limitation = slot_cur->bus_on->slots_at_66_pcix;
+ else
+ limitation = slot_cur->bus_on->slots_at_66_conv;
+ break;
+ case BUS_SPEED_100:
+ limitation = slot_cur->bus_on->slots_at_100_pcix;
+ break;
+ case BUS_SPEED_133:
+ limitation = slot_cur->bus_on->slots_at_133_pcix;
+ break;
+ }
+
+ if ((count + 1) > limitation)
+ return -EINVAL;
+ 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;
+ struct pci_func *tmp_func;
+
+ ibmphp_lock_operations();
+
+ debug("ENABLING SLOT........\n");
+ slot_cur = hs->private;
+
+ if ((rc = validate(slot_cur, ENABLE))) {
+ err("validate function failed\n");
+ goto error_nopower;
+ }
+
+ attn_LED_blink(slot_cur);
+
+ rc = set_bus(slot_cur);
+ if (rc) {
+ err("was not able to set the bus\n");
+ goto error_nopower;
+ }
+
+ /*-----------------debugging------------------------------*/
+ get_cur_bus_info(&slot_cur);
+ debug("the current bus speed right after set_bus = %x\n",
+ slot_cur->bus_on->current_speed);
+ /*----------------------------------------------------------*/
+
+ rc = check_limitations(slot_cur);
+ if (rc) {
+ err("Adding this card exceeds the limitations of this bus.\n");
+ err("(i.e., >1 133MHz cards running on same bus, or "
+ ">2 66 PCI cards running on same bus\n.");
+ err("Try hot-adding into another bus\n");
+ rc = -EINVAL;
+ goto error_nopower;
+ }
+
+ 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);
+ rc = -ENODEV;
+ goto exit;
+ }
+ /* Check to see the error of why it failed */
+ if ((SLOT_POWER(slot_cur->status)) &&
+ !(SLOT_PWRGD(slot_cur->status)))
+ err("power fault occurred trying to power up\n");
+ else if (SLOT_BUS_SPEED(slot_cur->status)) {
+ err("bus speed mismatch occurred. 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 occurred. please check "
+ "current bus mode and card capability\n");
+ print_card_capability(slot_cur);
+ }
+ ibmphp_update_slot_info(slot_cur);
+ goto exit;
+ }
+ debug("after power_on\n");
+ /*-----------------------debugging---------------------------*/
+ get_cur_bus_info(&slot_cur);
+ debug("the current bus speed right after power_on = %x\n",
+ slot_cur->bus_on->current_speed);
+ /*----------------------------------------------------------*/
+
+ rc = slot_update(&slot_cur);
+ if (rc)
+ goto error_power;
+
+ rc = -EINVAL;
+ if (SLOT_POWER(slot_cur->status) && !(SLOT_PWRGD(slot_cur->status))) {
+ err("power fault occurred trying to power up...\n");
+ goto error_power;
+ }
+ if (SLOT_POWER(slot_cur->status) && (SLOT_BUS_SPEED(slot_cur->status))) {
+ err("bus speed mismatch occurred. please check current bus "
+ "speed and card capability\n");
+ print_card_capability(slot_cur);
+ goto error_power;
+ }
+ /* Don't think this case will happen after above checks...
+ * but just in case, for paranoia sake */
+ if (!(SLOT_POWER(slot_cur->status))) {
+ err("power on failed...\n");
+ goto error_power;
+ }
+
+ slot_cur->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");
+ rc = -ENOMEM;
+ goto error_power;
+ }
+ 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");
+ /* true because don't need to actually deallocate resources,
+ * just remove references */
+ ibmphp_unconfigure_card(&slot_cur, 1);
+ debug("after unconfigure_card\n");
+ slot_cur->func = NULL;
+ rc = -ENOMEM;
+ goto error_power;
+ }
+
+ 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)) {
+ rc = -EFAULT;
+ goto exit;
+ }
+ ibmphp_print_test();
+ rc = ibmphp_update_slot_info(slot_cur);
+exit:
+ ibmphp_unlock_operations();
+ return rc;
+
+error_nopower:
+ attn_off(slot_cur); /* need to turn off if was blinking b4 */
+ attn_on(slot_cur);
+error_cont:
+ rcpr = slot_update(&slot_cur);
+ if (rcpr) {
+ rc = rcpr;
+ goto exit;
+ }
+ ibmphp_update_slot_info(slot_cur);
+ goto exit;
+
+error_power:
+ attn_off(slot_cur); /* need to turn off if was blinking b4 */
+ attn_on(slot_cur);
+ rcpr = power_off(slot_cur);
+ if (rcpr) {
+ rc = rcpr;
+ goto exit;
+ }
+ goto error_cont;
+}
+
+/**************************************************************
+* HOT REMOVING ADAPTER CARD *
+* INPUT: POINTER TO THE HOTPLUG SLOT STRUCTURE *
+* OUTPUT: SUCCESS 0 ; FAILURE: UNCONFIGURE , VALIDATE *
+ DISABLE POWER , *
+**************************************************************/
+static int ibmphp_disable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+ int rc;
+
+ ibmphp_lock_operations();
+ rc = ibmphp_do_disable_slot(slot);
+ ibmphp_unlock_operations();
+ return rc;
+}
+
+int ibmphp_do_disable_slot(struct slot *slot_cur)
+{
+ int rc;
+ u8 flag;
+
+ debug("DISABLING SLOT...\n");
+
+ if ((slot_cur == NULL) || (slot_cur->ctrl == NULL)) {
+ return -ENODEV;
+ }
+
+ flag = slot_cur->flag;
+ slot_cur->flag = TRUE;
+
+ if (flag == TRUE) {
+ rc = validate(slot_cur, DISABLE);
+ /* checking if powered off already & valid slot # */
+ if (rc)
+ goto error;
+ }
+ attn_LED_blink(slot_cur);
+
+ if (slot_cur->func == NULL) {
+ /* We need this for fncs's that were there on bootup */
+ slot_cur->func = kmalloc(sizeof(struct pci_func), GFP_KERNEL);
+ if (!slot_cur->func) {
+ err("out of system memory\n");
+ rc = -ENOMEM;
+ goto error;
+ }
+ memset(slot_cur->func, 0, sizeof(struct pci_func));
+ slot_cur->func->busno = slot_cur->bus;
+ slot_cur->func->device = slot_cur->device;
+ }
+
+ ibm_unconfigure_device(slot_cur->func);
+
+ /* If we got here from latch suddenly opening on operating card or
+ a power fault, there's no power to the card, so cannot
+ read from it to determine what resources it occupied. This operation
+ is forbidden anyhow. The best we can do is remove it from kernel
+ lists at least */
+
+ if (!flag) {
+ attn_off(slot_cur);
+ return 0;
+ }
+
+ 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");
+ goto error;
+ }
+
+ rc = ibmphp_hpc_writeslot(slot_cur, HPC_SLOT_OFF);
+ if (rc)
+ goto error;
+
+ attn_off(slot_cur);
+ rc = slot_update(&slot_cur);
+ if (rc)
+ goto exit;
+
+ rc = ibmphp_update_slot_info(slot_cur);
+ ibmphp_print_test();
+exit:
+ return rc;
+
+error:
+ /* Need to turn off if was blinking b4 */
+ attn_off(slot_cur);
+ attn_on(slot_cur);
+ if (slot_update(&slot_cur)) {
+ rc = -EFAULT;
+ goto exit;
+ }
+ if (flag)
+ ibmphp_update_slot_info(slot_cur);
+ goto exit;
+}
+
+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 = get_max_bus_speed,
+ .get_cur_bus_speed = get_cur_bus_speed,
+/* .get_max_adapter_speed = get_max_adapter_speed,
+ .get_bus_name_status = get_bus_name,
+*/
+};
+
+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");
+ kfree(ibmphp_pci_bus);
+}
+
+static int __init ibmphp_init(void)
+{
+ struct pci_bus *bus;
+ int i = 0;
+ int rc = 0;
+
+ init_flag = 1;
+
+ info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+ ibmphp_pci_bus = kmalloc(sizeof(*ibmphp_pci_bus), GFP_KERNEL);
+ if (!ibmphp_pci_bus) {
+ err("out of memory\n");
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ bus = pci_find_bus(0, 0);
+ if (!bus) {
+ err("Can't find the root pci bus, can not continue\n");
+ rc = -ENODEV;
+ goto error;
+ }
+ memcpy(ibmphp_pci_bus, bus, sizeof(*ibmphp_pci_bus));
+
+ ibmphp_debug = debug;
+
+ ibmphp_hpc_initvars();
+
+ for (i = 0; i < 16; i++)
+ irqs[i] = 0;
+
+ if ((rc = ibmphp_access_ebda()))
+ goto error;
+ debug("after ibmphp_access_ebda()\n");
+
+ if ((rc = ibmphp_rsrc_init()))
+ goto error;
+ debug("AFTER Resource & EBDA INITIALIZATIONS\n");
+
+ max_slots = get_max_slots();
+
+ if ((rc = ibmphp_register_pci()))
+ goto error;
+
+ if (init_ops()) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ ibmphp_print_test();
+ if ((rc = ibmphp_hpc_start_poll_thread())) {
+ goto error;
+ }
+
+ /* lock ourselves into memory with a module
+ * count of -1 so that no one can unload us. */
+ module_put(THIS_MODULE);
+
+exit:
+ return rc;
+
+error:
+ ibmphp_unload();
+ goto exit;
+}
+
+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/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c
new file mode 100644
index 000000000000..aea1187c73ad
--- /dev/null
+++ b/drivers/pci/hotplug/ibmphp_ebda.c
@@ -0,0 +1,1275 @@
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Tong Yu, IBM Corporation
+ *
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001-2003 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 <linux/init.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 = NULL;
+static LIST_HEAD (ebda_hpc_head);
+static LIST_HEAD (bus_info_head);
+static LIST_HEAD (rio_vg_head);
+static LIST_HEAD (rio_lo_head);
+static LIST_HEAD (opt_vg_head);
+static LIST_HEAD (opt_lo_head);
+static void __iomem *io_mem;
+
+/* Local functions */
+static int ebda_rsrc_controller (void);
+static int ebda_rsrc_rsrc (void);
+static int ebda_rio_table (void);
+
+static struct ebda_hpc_list * __init 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)
+ goto error;
+ memset (controller, 0, sizeof (*controller));
+
+ slots = kmalloc (sizeof (struct ebda_hpc_slot) * slot_count, GFP_KERNEL);
+ if (!slots)
+ goto error_contr;
+ memset (slots, 0, sizeof (*slots) * slot_count);
+ controller->slots = slots;
+
+ buses = kmalloc (sizeof (struct ebda_hpc_bus) * bus_count, GFP_KERNEL);
+ if (!buses)
+ goto error_slots;
+ memset (buses, 0, sizeof (*buses) * bus_count);
+ controller->buses = buses;
+
+ return controller;
+error_slots:
+ kfree(controller->slots);
+error_contr:
+ kfree(controller);
+error:
+ return NULL;
+}
+
+static void free_ebda_hpc (struct controller *controller)
+{
+ kfree (controller->slots);
+ kfree (controller->buses);
+ kfree (controller);
+}
+
+static struct ebda_rsrc_list * __init 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 __init 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 - controller_id = %x\n", __FUNCTION__, ptr->controller_id);
+
+ debug ("%s - slots_at_33_conv = %x\n", __FUNCTION__, ptr->slots_at_33_conv);
+ debug ("%s - slots_at_66_conv = %x\n", __FUNCTION__, ptr->slots_at_66_conv);
+ debug ("%s - slots_at_66_pcix = %x\n", __FUNCTION__, ptr->slots_at_66_pcix);
+ debug ("%s - slots_at_100_pcix = %x\n", __FUNCTION__, ptr->slots_at_100_pcix);
+ debug ("%s - slots_at_133_pcix = %x\n", __FUNCTION__, ptr->slots_at_133_pcix);
+
+ }
+}
+
+static void print_lo_info (void)
+{
+ struct rio_detail *ptr;
+ struct list_head *ptr1;
+ debug ("print_lo_info ----\n");
+ list_for_each (ptr1, &rio_lo_head) {
+ ptr = list_entry (ptr1, struct rio_detail, rio_detail_list);
+ debug ("%s - rio_node_id = %x\n", __FUNCTION__, ptr->rio_node_id);
+ debug ("%s - rio_type = %x\n", __FUNCTION__, ptr->rio_type);
+ debug ("%s - owner_id = %x\n", __FUNCTION__, ptr->owner_id);
+ debug ("%s - first_slot_num = %x\n", __FUNCTION__, ptr->first_slot_num);
+ debug ("%s - wpindex = %x\n", __FUNCTION__, ptr->wpindex);
+ debug ("%s - chassis_num = %x\n", __FUNCTION__, ptr->chassis_num);
+
+ }
+}
+
+static void print_vg_info (void)
+{
+ struct rio_detail *ptr;
+ struct list_head *ptr1;
+ debug ("%s ---\n", __FUNCTION__);
+ list_for_each (ptr1, &rio_vg_head) {
+ ptr = list_entry (ptr1, struct rio_detail, rio_detail_list);
+ debug ("%s - rio_node_id = %x\n", __FUNCTION__, ptr->rio_node_id);
+ debug ("%s - rio_type = %x\n", __FUNCTION__, ptr->rio_type);
+ debug ("%s - owner_id = %x\n", __FUNCTION__, ptr->owner_id);
+ debug ("%s - first_slot_num = %x\n", __FUNCTION__, ptr->first_slot_num);
+ debug ("%s - wpindex = %x\n", __FUNCTION__, ptr->wpindex);
+ debug ("%s - chassis_num = %x\n", __FUNCTION__, ptr->chassis_num);
+
+ }
+}
+
+static void __init 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: %x end addr: %x\n",
+ __FUNCTION__, ptr->rsrc_type ,ptr->bus_num, ptr->dev_fun,ptr->start_addr, ptr->end_addr);
+ }
+}
+
+static void __init print_ibm_slot (void)
+{
+ struct slot *ptr;
+ struct list_head *ptr1;
+
+ list_for_each (ptr1, &ibmphp_slot_head) {
+ ptr = list_entry (ptr1, struct slot, ibm_slot_list);
+ debug ("%s - slot_number: %x\n", __FUNCTION__, ptr->number);
+ }
+}
+
+static void __init print_opt_vg (void)
+{
+ struct opt_rio *ptr;
+ struct list_head *ptr1;
+ debug ("%s ---\n", __FUNCTION__);
+ list_for_each (ptr1, &opt_vg_head) {
+ ptr = list_entry (ptr1, struct opt_rio, opt_rio_list);
+ debug ("%s - rio_type %x\n", __FUNCTION__, ptr->rio_type);
+ debug ("%s - chassis_num: %x\n", __FUNCTION__, ptr->chassis_num);
+ debug ("%s - first_slot_num: %x\n", __FUNCTION__, ptr->first_slot_num);
+ debug ("%s - middle_num: %x\n", __FUNCTION__, ptr->middle_num);
+ }
+}
+
+static void __init 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:
+ case 4:
+ 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 __init ibmphp_access_ebda (void)
+{
+ u8 format, num_ctlrs, rio_complete, hs_complete;
+ u16 ebda_seg, num_entries, next_offset, offset, blk_id, sub_addr, re, rc_id, re_id, base;
+ int rc = 0;
+
+
+ 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)
+ goto error_nodev;
+ 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)
+ goto error_nodev;
+ /* 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) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ 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 */
+ /* FIXME: rc is never used/checked */
+ 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)
+ goto error_nodev;
+
+ /* 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 ) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ 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;
+ } else {
+ /* found rio table, 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)
+ goto error_nodev;
+
+ if (rio_table_ptr) {
+ if (rio_complete && rio_table_ptr->ver_num == 3) {
+ rc = ebda_rio_table ();
+ if (rc)
+ goto out;
+ }
+ }
+ rc = ebda_rsrc_controller ();
+ if (rc)
+ goto out;
+
+ rc = ebda_rsrc_rsrc ();
+ goto out;
+error_nodev:
+ rc = -ENODEV;
+out:
+ iounmap (io_mem);
+ return rc;
+}
+
+/*
+ * map info of scalability details and rio details from physical address
+ */
+static int __init ebda_rio_table (void)
+{
+ u16 offset;
+ u8 i;
+ struct rio_detail *rio_detail_ptr;
+
+ offset = rio_table_ptr->offset;
+ offset += 12 * rio_table_ptr->scal_count;
+
+ // we do concern about rio details
+ 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);
+ rio_detail_ptr->wpindex = readb (io_mem + offset + 13);
+ rio_detail_ptr->chassis_num = readb (io_mem + offset + 14);
+// 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);
+ //create linked list of chassis
+ if (rio_detail_ptr->rio_type == 4 || rio_detail_ptr->rio_type == 5)
+ list_add (&rio_detail_ptr->rio_detail_list, &rio_vg_head);
+ //create linked list of expansion box
+ else if (rio_detail_ptr->rio_type == 6 || rio_detail_ptr->rio_type == 7)
+ list_add (&rio_detail_ptr->rio_detail_list, &rio_lo_head);
+ else
+ // not in my concern
+ kfree (rio_detail_ptr);
+ offset += 15;
+ }
+ print_lo_info ();
+ print_vg_info ();
+ return 0;
+}
+
+/*
+ * reorganizing linked list of chassis
+ */
+static struct opt_rio *search_opt_vg (u8 chassis_num)
+{
+ struct opt_rio *ptr;
+ struct list_head *ptr1;
+ list_for_each (ptr1, &opt_vg_head) {
+ ptr = list_entry (ptr1, struct opt_rio, opt_rio_list);
+ if (ptr->chassis_num == chassis_num)
+ return ptr;
+ }
+ return NULL;
+}
+
+static int __init combine_wpg_for_chassis (void)
+{
+ struct opt_rio *opt_rio_ptr = NULL;
+ struct rio_detail *rio_detail_ptr = NULL;
+ struct list_head *list_head_ptr = NULL;
+
+ list_for_each (list_head_ptr, &rio_vg_head) {
+ rio_detail_ptr = list_entry (list_head_ptr, struct rio_detail, rio_detail_list);
+ opt_rio_ptr = search_opt_vg (rio_detail_ptr->chassis_num);
+ if (!opt_rio_ptr) {
+ opt_rio_ptr = (struct opt_rio *) kmalloc (sizeof (struct opt_rio), GFP_KERNEL);
+ if (!opt_rio_ptr)
+ return -ENOMEM;
+ memset (opt_rio_ptr, 0, sizeof (struct opt_rio));
+ opt_rio_ptr->rio_type = rio_detail_ptr->rio_type;
+ opt_rio_ptr->chassis_num = rio_detail_ptr->chassis_num;
+ opt_rio_ptr->first_slot_num = rio_detail_ptr->first_slot_num;
+ opt_rio_ptr->middle_num = rio_detail_ptr->first_slot_num;
+ list_add (&opt_rio_ptr->opt_rio_list, &opt_vg_head);
+ } else {
+ opt_rio_ptr->first_slot_num = min (opt_rio_ptr->first_slot_num, rio_detail_ptr->first_slot_num);
+ opt_rio_ptr->middle_num = max (opt_rio_ptr->middle_num, rio_detail_ptr->first_slot_num);
+ }
+ }
+ print_opt_vg ();
+ return 0;
+}
+
+/*
+ * reorgnizing linked list of expansion box
+ */
+static struct opt_rio_lo *search_opt_lo (u8 chassis_num)
+{
+ struct opt_rio_lo *ptr;
+ struct list_head *ptr1;
+ list_for_each (ptr1, &opt_lo_head) {
+ ptr = list_entry (ptr1, struct opt_rio_lo, opt_rio_lo_list);
+ if (ptr->chassis_num == chassis_num)
+ return ptr;
+ }
+ return NULL;
+}
+
+static int combine_wpg_for_expansion (void)
+{
+ struct opt_rio_lo *opt_rio_lo_ptr = NULL;
+ struct rio_detail *rio_detail_ptr = NULL;
+ struct list_head *list_head_ptr = NULL;
+
+ list_for_each (list_head_ptr, &rio_lo_head) {
+ rio_detail_ptr = list_entry (list_head_ptr, struct rio_detail, rio_detail_list);
+ opt_rio_lo_ptr = search_opt_lo (rio_detail_ptr->chassis_num);
+ if (!opt_rio_lo_ptr) {
+ opt_rio_lo_ptr = (struct opt_rio_lo *) kmalloc (sizeof (struct opt_rio_lo), GFP_KERNEL);
+ if (!opt_rio_lo_ptr)
+ return -ENOMEM;
+ memset (opt_rio_lo_ptr, 0, sizeof (struct opt_rio_lo));
+ opt_rio_lo_ptr->rio_type = rio_detail_ptr->rio_type;
+ opt_rio_lo_ptr->chassis_num = rio_detail_ptr->chassis_num;
+ opt_rio_lo_ptr->first_slot_num = rio_detail_ptr->first_slot_num;
+ opt_rio_lo_ptr->middle_num = rio_detail_ptr->first_slot_num;
+ opt_rio_lo_ptr->pack_count = 1;
+
+ list_add (&opt_rio_lo_ptr->opt_rio_lo_list, &opt_lo_head);
+ } else {
+ opt_rio_lo_ptr->first_slot_num = min (opt_rio_lo_ptr->first_slot_num, rio_detail_ptr->first_slot_num);
+ opt_rio_lo_ptr->middle_num = max (opt_rio_lo_ptr->middle_num, rio_detail_ptr->first_slot_num);
+ opt_rio_lo_ptr->pack_count = 2;
+ }
+ }
+ return 0;
+}
+
+
+/* Since we don't know the max slot number per each chassis, hence go
+ * through the list of all chassis to find out the range
+ * Arguments: slot_num, 1st slot number of the chassis we think we are on,
+ * var (0 = chassis, 1 = expansion box)
+ */
+static int first_slot_num (u8 slot_num, u8 first_slot, u8 var)
+{
+ struct opt_rio *opt_vg_ptr = NULL;
+ struct opt_rio_lo *opt_lo_ptr = NULL;
+ struct list_head *ptr = NULL;
+ int rc = 0;
+
+ if (!var) {
+ list_for_each (ptr, &opt_vg_head) {
+ opt_vg_ptr = list_entry (ptr, struct opt_rio, opt_rio_list);
+ if ((first_slot < opt_vg_ptr->first_slot_num) && (slot_num >= opt_vg_ptr->first_slot_num)) {
+ rc = -ENODEV;
+ break;
+ }
+ }
+ } else {
+ list_for_each (ptr, &opt_lo_head) {
+ opt_lo_ptr = list_entry (ptr, struct opt_rio_lo, opt_rio_lo_list);
+ if ((first_slot < opt_lo_ptr->first_slot_num) && (slot_num >= opt_lo_ptr->first_slot_num)) {
+ rc = -ENODEV;
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static struct opt_rio_lo * find_rxe_num (u8 slot_num)
+{
+ struct opt_rio_lo *opt_lo_ptr;
+ struct list_head *ptr;
+
+ list_for_each (ptr, &opt_lo_head) {
+ opt_lo_ptr = list_entry (ptr, struct opt_rio_lo, opt_rio_lo_list);
+ //check to see if this slot_num belongs to expansion box
+ if ((slot_num >= opt_lo_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_lo_ptr->first_slot_num, 1)))
+ return opt_lo_ptr;
+ }
+ return NULL;
+}
+
+static struct opt_rio * find_chassis_num (u8 slot_num)
+{
+ struct opt_rio *opt_vg_ptr;
+ struct list_head *ptr;
+
+ list_for_each (ptr, &opt_vg_head) {
+ opt_vg_ptr = list_entry (ptr, struct opt_rio, opt_rio_list);
+ //check to see if this slot_num belongs to chassis
+ if ((slot_num >= opt_vg_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_vg_ptr->first_slot_num, 0)))
+ return opt_vg_ptr;
+ }
+ return NULL;
+}
+
+/* This routine will find out how many slots are in the chassis, so that
+ * the slot numbers for rxe100 would start from 1, and not from 7, or 6 etc
+ */
+static u8 calculate_first_slot (u8 slot_num)
+{
+ u8 first_slot = 1;
+ struct list_head * list;
+ struct slot * slot_cur;
+
+ list_for_each (list, &ibmphp_slot_head) {
+ slot_cur = list_entry (list, struct slot, ibm_slot_list);
+ if (slot_cur->ctrl) {
+ if ((slot_cur->ctrl->ctlr_type != 4) && (slot_cur->ctrl->ending_slot_num > first_slot) && (slot_num > slot_cur->ctrl->ending_slot_num))
+ first_slot = slot_cur->ctrl->ending_slot_num;
+ }
+ }
+ return first_slot + 1;
+
+}
+static char *create_file_name (struct slot * slot_cur)
+{
+ struct opt_rio *opt_vg_ptr = NULL;
+ struct opt_rio_lo *opt_lo_ptr = NULL;
+ static char str[30];
+ int which = 0; /* rxe = 1, chassis = 0 */
+ u8 number = 1; /* either chassis or rxe # */
+ u8 first_slot = 1;
+ u8 slot_num;
+ u8 flag = 0;
+
+ if (!slot_cur) {
+ err ("Structure passed is empty\n");
+ return NULL;
+ }
+
+ slot_num = slot_cur->number;
+
+ memset (str, 0, sizeof(str));
+
+ if (rio_table_ptr) {
+ if (rio_table_ptr->ver_num == 3) {
+ opt_vg_ptr = find_chassis_num (slot_num);
+ opt_lo_ptr = find_rxe_num (slot_num);
+ }
+ }
+ if (opt_vg_ptr) {
+ if (opt_lo_ptr) {
+ if ((slot_num - opt_vg_ptr->first_slot_num) > (slot_num - opt_lo_ptr->first_slot_num)) {
+ number = opt_lo_ptr->chassis_num;
+ first_slot = opt_lo_ptr->first_slot_num;
+ which = 1; /* it is RXE */
+ } else {
+ first_slot = opt_vg_ptr->first_slot_num;
+ number = opt_vg_ptr->chassis_num;
+ which = 0;
+ }
+ } else {
+ first_slot = opt_vg_ptr->first_slot_num;
+ number = opt_vg_ptr->chassis_num;
+ which = 0;
+ }
+ ++flag;
+ } else if (opt_lo_ptr) {
+ number = opt_lo_ptr->chassis_num;
+ first_slot = opt_lo_ptr->first_slot_num;
+ which = 1;
+ ++flag;
+ } else if (rio_table_ptr) {
+ if (rio_table_ptr->ver_num == 3) {
+ /* if both NULL and we DO have correct RIO table in BIOS */
+ return NULL;
+ }
+ }
+ if (!flag) {
+ if (slot_cur->ctrl->ctlr_type == 4) {
+ first_slot = calculate_first_slot (slot_num);
+ which = 1;
+ } else {
+ which = 0;
+ }
+ }
+
+ sprintf(str, "%s%dslot%d",
+ which == 0 ? "chassis" : "rxe",
+ number, slot_num - first_slot + 1);
+ return str;
+}
+
+static int fillslotinfo(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot;
+ int rc = 0;
+
+ if (!hotplug_slot || !hotplug_slot->private)
+ return -EINVAL;
+
+ slot = hotplug_slot->private;
+ rc = ibmphp_hpc_readslot(slot, READ_ALLSTAT, NULL);
+ if (rc)
+ return rc;
+
+ // power - enabled:1 not:0
+ hotplug_slot->info->power_status = SLOT_POWER(slot->status);
+
+ // attention - off:0, on:1, blinking:2
+ hotplug_slot->info->attention_status = SLOT_ATTN(slot->status, slot->ext_status);
+
+ // latch - open:1 closed:0
+ hotplug_slot->info->latch_status = SLOT_LATCH(slot->status);
+
+ // pci board - present:1 not:0
+ if (SLOT_PRESENT (slot->status))
+ hotplug_slot->info->adapter_status = 1;
+ else
+ hotplug_slot->info->adapter_status = 0;
+/*
+ if (slot->bus_on->supported_bus_mode
+ && (slot->bus_on->supported_speed == BUS_SPEED_66))
+ hotplug_slot->info->max_bus_speed_status = BUS_SPEED_66PCIX;
+ else
+ hotplug_slot->info->max_bus_speed_status = slot->bus_on->supported_speed;
+*/
+
+ return rc;
+}
+
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot;
+
+ if (!hotplug_slot || !hotplug_slot->private)
+ return;
+
+ slot = hotplug_slot->private;
+ kfree(slot->hotplug_slot->info);
+ kfree(slot->hotplug_slot->name);
+ kfree(slot->hotplug_slot);
+ slot->ctrl = NULL;
+ slot->bus_on = NULL;
+
+ /* we don't want to actually remove the resources, since free_resources will do just that */
+ ibmphp_unconfigure_card(&slot, -1);
+
+ kfree (slot);
+}
+
+static struct pci_driver ibmphp_driver;
+
+/*
+ * 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 __init 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;
+ struct slot *tmp_slot;
+ struct list_head *list;
+
+ 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 ) {
+ rc = -ENOMEM;
+ goto error_no_hpc;
+ }
+ 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) {
+ rc = -ENOMEM;
+ goto error_no_hp_slot;
+ }
+ 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;
+
+ bus_info_ptr1->controller_id = hpc_ptr->ctlr_id;
+
+ 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);
+ bus_ptr->slots_at_33_conv = readb (io_mem + addr_bus + bus_num + 8 * bus);
+ bus_ptr->slots_at_66_conv = readb (io_mem + addr_bus + bus_num + 8 * bus + 1);
+
+ bus_ptr->slots_at_66_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 2);
+
+ bus_ptr->slots_at_100_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 3);
+
+ bus_ptr->slots_at_133_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 4);
+
+ bus_info_ptr2 = ibmphp_find_same_bus_num (bus_ptr->bus_num);
+ if (bus_info_ptr2) {
+ bus_info_ptr2->slots_at_33_conv = bus_ptr->slots_at_33_conv;
+ bus_info_ptr2->slots_at_66_conv = bus_ptr->slots_at_66_conv;
+ bus_info_ptr2->slots_at_66_pcix = bus_ptr->slots_at_66_pcix;
+ bus_info_ptr2->slots_at_100_pcix = bus_ptr->slots_at_100_pcix;
+ bus_info_ptr2->slots_at_133_pcix = bus_ptr->slots_at_133_pcix;
+ }
+ bus_ptr++;
+ }
+
+ 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;
+ debug ("ctrl bus = %x, ctlr devfun = %x, irq = %x\n",
+ hpc_ptr->u.pci_ctlr.bus,
+ hpc_ptr->u.pci_ctlr.dev_fun, hpc_ptr->irq);
+ 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);
+ if (!request_region (hpc_ptr->u.isa_ctlr.io_start,
+ (hpc_ptr->u.isa_ctlr.io_end - hpc_ptr->u.isa_ctlr.io_start + 1),
+ "ibmphp")) {
+ rc = -ENODEV;
+ goto error_no_hp_slot;
+ }
+ hpc_ptr->irq = readb (io_mem + addr + 4);
+ addr += 5;
+ break;
+
+ case 2:
+ 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:
+ rc = -ENODEV;
+ goto error_no_hp_slot;
+ }
+
+ //reorganize chassis' linked list
+ combine_wpg_for_chassis ();
+ combine_wpg_for_expansion ();
+ hpc_ptr->revision = 0xff;
+ hpc_ptr->options = 0xff;
+ hpc_ptr->starting_slot_num = hpc_ptr->slots[0].slot_num;
+ hpc_ptr->ending_slot_num = hpc_ptr->slots[slot_num-1].slot_num;
+
+ // 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 = kmalloc(sizeof(*hp_slot_ptr), GFP_KERNEL);
+ if (!hp_slot_ptr) {
+ rc = -ENOMEM;
+ goto error_no_hp_slot;
+ }
+ memset(hp_slot_ptr, 0, sizeof(*hp_slot_ptr));
+
+ hp_slot_ptr->info = kmalloc (sizeof(struct hotplug_slot_info), GFP_KERNEL);
+ if (!hp_slot_ptr->info) {
+ rc = -ENOMEM;
+ goto error_no_hp_info;
+ }
+ memset(hp_slot_ptr->info, 0, sizeof(struct hotplug_slot_info));
+
+ hp_slot_ptr->name = kmalloc(30, GFP_KERNEL);
+ if (!hp_slot_ptr->name) {
+ rc = -ENOMEM;
+ goto error_no_hp_name;
+ }
+
+ tmp_slot = kmalloc(sizeof(*tmp_slot), GFP_KERNEL);
+ if (!tmp_slot) {
+ rc = -ENOMEM;
+ goto error_no_slot;
+ }
+ memset(tmp_slot, 0, sizeof(*tmp_slot));
+
+ tmp_slot->flag = TRUE;
+
+ tmp_slot->capabilities = hpc_ptr->slots[index].slot_cap;
+ if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_133_MAX) == EBDA_SLOT_133_MAX)
+ tmp_slot->supported_speed = 3;
+ else if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_100_MAX) == EBDA_SLOT_100_MAX)
+ tmp_slot->supported_speed = 2;
+ else if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_66_MAX) == EBDA_SLOT_66_MAX)
+ tmp_slot->supported_speed = 1;
+
+ if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_PCIX_CAP) == EBDA_SLOT_PCIX_CAP)
+ tmp_slot->supported_bus_mode = 1;
+ else
+ tmp_slot->supported_bus_mode = 0;
+
+
+ tmp_slot->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) {
+ rc = -ENODEV;
+ goto error;
+ }
+ tmp_slot->bus_on = bus_info_ptr1;
+ bus_info_ptr1 = NULL;
+ tmp_slot->ctrl = hpc_ptr;
+
+ tmp_slot->ctlr_index = hpc_ptr->slots[index].ctl_index;
+ tmp_slot->number = hpc_ptr->slots[index].slot_num;
+ tmp_slot->hotplug_slot = hp_slot_ptr;
+
+ hp_slot_ptr->private = tmp_slot;
+ hp_slot_ptr->release = release_slot;
+
+ rc = fillslotinfo(hp_slot_ptr);
+ if (rc)
+ goto error;
+
+ rc = ibmphp_init_devno ((struct slot **) &hp_slot_ptr->private);
+ if (rc)
+ goto error;
+ hp_slot_ptr->ops = &ibmphp_hotplug_slot_ops;
+
+ // 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 */
+
+ list_for_each (list, &ibmphp_slot_head) {
+ tmp_slot = list_entry (list, struct slot, ibm_slot_list);
+
+ snprintf (tmp_slot->hotplug_slot->name, 30, "%s", create_file_name (tmp_slot));
+ pci_hp_register (tmp_slot->hotplug_slot);
+ }
+
+ print_ebda_hpc ();
+ print_ibm_slot ();
+ return 0;
+
+error:
+ kfree (hp_slot_ptr->private);
+error_no_slot:
+ kfree (hp_slot_ptr->name);
+error_no_hp_name:
+ kfree (hp_slot_ptr->info);
+error_no_hp_info:
+ kfree (hp_slot_ptr);
+error_no_hp_slot:
+ free_ebda_hpc (hpc_ptr);
+error_no_hpc:
+ iounmap (io_mem);
+ return rc;
+}
+
+/*
+ * map info (bus, devfun, start addr, end addr..) of i/o, memory,
+ * pfm from the physical addr to a list of resource.
+ */
+static int __init 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: %x end addr: %x\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: %x end addr: %x\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;
+}
+
+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);
+ }
+}
+
+void ibmphp_free_ebda_hpc_queue (void)
+{
+ struct controller *controller = NULL;
+ struct list_head *list;
+ struct list_head *next;
+ int pci_flag = 0;
+
+ list_for_each_safe (list, next, &ebda_hpc_head) {
+ controller = list_entry (list, struct controller, ebda_hpc_list);
+ if (controller->ctlr_type == 0)
+ release_region (controller->u.isa_ctlr.io_start, (controller->u.isa_ctlr.io_end - controller->u.isa_ctlr.io_start + 1));
+ else if ((controller->ctlr_type == 1) && (!pci_flag)) {
+ ++pci_flag;
+ pci_unregister_driver (&ibmphp_driver);
+ }
+ 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;
+ }
+}
+
+static struct pci_device_id id_table[] = {
+ {
+ .vendor = PCI_VENDOR_ID_IBM,
+ .device = HPC_DEVICE_ID,
+ .subvendor = PCI_VENDOR_ID_IBM,
+ .subdevice = HPC_SUBSYSTEM_ID,
+ .class = ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00),
+ }, {}
+};
+
+MODULE_DEVICE_TABLE(pci, id_table);
+
+static int ibmphp_probe (struct pci_dev *, const struct pci_device_id *);
+static struct pci_driver ibmphp_driver = {
+ .name = "ibmphp",
+ .id_table = id_table,
+ .probe = ibmphp_probe,
+};
+
+int ibmphp_register_pci (void)
+{
+ struct controller *ctrl;
+ struct list_head *tmp;
+ int rc = 0;
+
+ list_for_each (tmp, &ebda_hpc_head) {
+ ctrl = list_entry (tmp, struct controller, ebda_hpc_list);
+ if (ctrl->ctlr_type == 1) {
+ rc = pci_register_driver(&ibmphp_driver);
+ break;
+ }
+ }
+ return rc;
+}
+static int ibmphp_probe (struct pci_dev * dev, const struct pci_device_id *ids)
+{
+ struct controller *ctrl;
+ struct list_head *tmp;
+
+ debug ("inside ibmphp_probe\n");
+
+ list_for_each (tmp, &ebda_hpc_head) {
+ ctrl = list_entry (tmp, struct controller, ebda_hpc_list);
+ if (ctrl->ctlr_type == 1) {
+ if ((dev->devfn == ctrl->u.pci_ctlr.dev_fun) && (dev->bus->number == ctrl->u.pci_ctlr.bus)) {
+ ctrl->ctrl_dev = dev;
+ debug ("found device!!!\n");
+ debug ("dev->device = %x, dev->subsystem_device = %x\n", dev->device, dev->subsystem_device);
+ return 0;
+ }
+ }
+ }
+ return -ENODEV;
+}
+
diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c
new file mode 100644
index 000000000000..6894b548c8ca
--- /dev/null
+++ b/drivers/pci/hotplug/ibmphp_hpc.c
@@ -0,0 +1,1161 @@
+/*
+ * IBM Hot Plug Controller Driver
+ *
+ * Written By: Jyoti Shah, IBM Corporation
+ *
+ * Copyright (C) 2001-2003 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/wait.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include "ibmphp.h"
+
+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
+
+//----------------------------------------------------------------------------
+// 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))
+
+//----------------------------------------------------------------------------
+// global variables
+//----------------------------------------------------------------------------
+static int ibmphp_shutdown;
+static int tid_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
+//----------------------------------------------------------------------------
+// local function prototypes
+//----------------------------------------------------------------------------
+static u8 i2c_ctrl_read (struct controller *, void __iomem *, u8);
+static u8 i2c_ctrl_write (struct controller *, void __iomem *, 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 process_changeinstatus (struct slot *, struct slot *);
+static int process_changeinlatch (u8, u8, struct controller *);
+static int hpc_poll_thread (void *);
+static int hpc_wait_ctlr_notworking (int, struct controller *, void __iomem *, u8 *);
+//----------------------------------------------------------------------------
+
+
+/*----------------------------------------------------------------------
+* Name: ibmphp_hpc_initvars
+*
+* Action: initialize semaphores and variables
+*---------------------------------------------------------------------*/
+void __init ibmphp_hpc_initvars (void)
+{
+ debug ("%s - Entry\n", __FUNCTION__);
+
+ init_MUTEX (&sem_hpcaccess);
+ init_MUTEX (&semOperations);
+ init_MUTEX_LOCKED (&sem_exit);
+ to_debug = FALSE;
+ ibmphp_shutdown = FALSE;
+ tid_poll = 0;
+
+ debug ("%s - Exit\n", __FUNCTION__);
+}
+
+/*----------------------------------------------------------------------
+* Name: i2c_ctrl_read
+*
+* Action: read from HPC over I2C
+*
+*---------------------------------------------------------------------*/
+static u8 i2c_ctrl_read (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8 index)
+{
+ u8 status;
+ int i;
+ void __iomem *wpg_addr; // base addr + offset
+ unsigned long wpg_data; // data to/from WPG LOHI format
+ unsigned long ultemp;
+ unsigned long data; // actual data HILO format
+
+ debug_polling ("%s - Entry WPGBbar[%p] index[%x] \n", __FUNCTION__, 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 = (unsigned long)ctlr_ptr->u.wpeg_ctlr.i2c_addr;
+ ultemp = ultemp >> 1;
+ data |= (ultemp << 8);
+
+ // fill in index
+ data |= (unsigned long)index;
+ } else if (ctlr_ptr->ctlr_type == 0x04) {
+ data = WPG_READDIRECT_MASK;
+
+ // fill in index
+ ultemp = (unsigned long)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
+ wpg_addr = WPGBbar + WPG_I2CMOSUP_OFFSET;
+ writel (wpg_data, wpg_addr);
+
+ //--------------------------------------------------------------------
+ // READ - step 2 : clear the message buffer
+ data = 0x00000000;
+ wpg_data = swab32 (data);
+ wpg_addr = WPGBbar + 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);
+ wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET + WPG_I2C_OR;
+ writel (wpg_data, wpg_addr);
+
+ //--------------------------------------------------------------------
+ // READ - step 4 : wait until start operation bit clears
+ i = CMD_COMPLETE_TOUT_SEC;
+ while (i) {
+ msleep(10);
+ wpg_addr = WPGBbar + 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) {
+ msleep(10);
+ wpg_addr = WPGBbar + 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
+ wpg_addr = WPGBbar + 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: i2c_ctrl_write
+*
+* Action: write to HPC over I2C
+*
+* Return 0 or error codes
+*---------------------------------------------------------------------*/
+static u8 i2c_ctrl_write (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8 index, u8 cmd)
+{
+ u8 rc;
+ void __iomem *wpg_addr; // base addr + offset
+ unsigned long wpg_data; // data to/from WPG LOHI format
+ unsigned long ultemp;
+ unsigned long data; // actual data HILO format
+ int i;
+
+ debug_polling ("%s - Entry WPGBbar[%p] index[%x] cmd[%x]\n", __FUNCTION__, 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 = (unsigned long)ctlr_ptr->u.wpeg_ctlr.i2c_addr;
+ ultemp = ultemp >> 1;
+ data |= (ultemp << 8);
+
+ // fill in index
+ data |= (unsigned long)index;
+ } else if (ctlr_ptr->ctlr_type == 0x04) {
+ data = WPG_WRITEDIRECT_MASK;
+
+ // fill in index
+ ultemp = (unsigned long)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
+ wpg_addr = WPGBbar + WPG_I2CMOSUP_OFFSET;
+ writel (wpg_data, wpg_addr);
+
+ //--------------------------------------------------------------------
+ // WRITE - step 2 : clear the message buffer
+ data = 0x00000000 | (unsigned long)cmd;
+ wpg_data = swab32 (data);
+ wpg_addr = WPGBbar + 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);
+ wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET + WPG_I2C_OR;
+ writel (wpg_data, wpg_addr);
+
+ //--------------------------------------------------------------------
+ // WRITE - step 4 : wait until start operation bit clears
+ i = CMD_COMPLETE_TOUT_SEC;
+ while (i) {
+ msleep(10);
+ wpg_addr = WPGBbar + 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) {
+ msleep(10);
+ wpg_addr = WPGBbar + 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);
+}
+
+//------------------------------------------------------------
+// Read from ISA type HPC
+//------------------------------------------------------------
+static u8 isa_ctrl_read (struct controller *ctlr_ptr, u8 offset)
+{
+ u16 start_address;
+ u16 end_address;
+ u8 data;
+
+ start_address = ctlr_ptr->u.isa_ctlr.io_start;
+ end_address = ctlr_ptr->u.isa_ctlr.io_end;
+ data = inb (start_address + offset);
+ return data;
+}
+
+//--------------------------------------------------------------
+// Write to ISA type HPC
+//--------------------------------------------------------------
+static void isa_ctrl_write (struct controller *ctlr_ptr, u8 offset, u8 data)
+{
+ u16 start_address;
+ u16 port_address;
+
+ start_address = ctlr_ptr->u.isa_ctlr.io_start;
+ port_address = start_address + (u16) offset;
+ outb (data, port_address);
+}
+
+static u8 pci_ctrl_read (struct controller *ctrl, u8 offset)
+{
+ u8 data = 0x00;
+ debug ("inside pci_ctrl_read\n");
+ if (ctrl->ctrl_dev)
+ pci_read_config_byte (ctrl->ctrl_dev, HPC_PCI_OFFSET + offset, &data);
+ return data;
+}
+
+static u8 pci_ctrl_write (struct controller *ctrl, u8 offset, u8 data)
+{
+ u8 rc = -ENODEV;
+ debug ("inside pci_ctrl_write\n");
+ if (ctrl->ctrl_dev) {
+ pci_write_config_byte (ctrl->ctrl_dev, HPC_PCI_OFFSET + offset, data);
+ rc = 0;
+ }
+ return rc;
+}
+
+static u8 ctrl_read (struct controller *ctlr, void __iomem *base, u8 offset)
+{
+ u8 rc;
+ switch (ctlr->ctlr_type) {
+ case 0:
+ rc = isa_ctrl_read (ctlr, offset);
+ break;
+ case 1:
+ rc = pci_ctrl_read (ctlr, offset);
+ break;
+ case 2:
+ case 4:
+ rc = i2c_ctrl_read (ctlr, base, offset);
+ break;
+ default:
+ return -ENODEV;
+ }
+ return rc;
+}
+
+static u8 ctrl_write (struct controller *ctlr, void __iomem *base, u8 offset, u8 data)
+{
+ u8 rc = 0;
+ switch (ctlr->ctlr_type) {
+ case 0:
+ isa_ctrl_write(ctlr, offset, data);
+ break;
+ case 1:
+ rc = pci_ctrl_write (ctlr, offset, data);
+ break;
+ case 2:
+ case 4:
+ rc = i2c_ctrl_write(ctlr, base, offset, data);
+ break;
+ default:
+ return -ENODEV;
+ }
+ 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 __iomem *wpg_bbar = NULL;
+ struct controller *ctlr_ptr;
+ struct list_head *pslotlist;
+ u8 index, status;
+ int rc = 0;
+ int busindex;
+
+ debug_polling ("%s - Entry pslot[%p] cmd[%x] pstatus[%p]\n", __FUNCTION__, pslot, cmd, 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
+ //--------------------------------------------------------------------
+ if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4))
+ 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
+ //--------------------------------------------------------------------
+
+ // remove physical to logical address mapping
+ if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4))
+ iounmap (wpg_bbar);
+
+ 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 __iomem *wpg_bbar = NULL;
+ struct controller *ctlr_ptr;
+ u8 index, status;
+ int busindex;
+ u8 done;
+ int rc = 0;
+ int timeout;
+
+ debug_polling ("%s - Entry pslot[%p] cmd[%x]\n", __FUNCTION__, 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
+ //--------------------------------------------------------------------
+ if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) {
+ 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) {
+ msleep(1000);
+ if (timeout < 1) {
+ done = TRUE;
+ err ("%s - Error command complete timeout\n", __FUNCTION__);
+ rc = -EFAULT;
+ } else
+ timeout--;
+ }
+ }
+ ctlr_ptr->status = status;
+ }
+ // cleanup
+
+ // remove physical to logical address mapping
+ if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4))
+ iounmap (wpg_bbar);
+ 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);
+ to_debug = TRUE;
+}
+
+/*----------------------------------------------------------------------
+* Name: ibmphp_unlock_operations()
+*---------------------------------------------------------------------*/
+void ibmphp_unlock_operations (void)
+{
+ debug ("%s - Entry\n", __FUNCTION__);
+ up (&semOperations);
+ to_debug = FALSE;
+ debug ("%s - Exit\n", __FUNCTION__);
+}
+
+/*----------------------------------------------------------------------
+* Name: poll_hpc()
+*---------------------------------------------------------------------*/
+#define POLL_LATCH_REGISTER 0
+#define POLL_SLOTS 1
+#define POLL_SLEEP 2
+static void poll_hpc (void)
+{
+ struct slot myslot;
+ struct slot *pslot = NULL;
+ struct list_head *pslotlist;
+ int rc;
+ int poll_state = POLL_LATCH_REGISTER;
+ u8 oldlatchlow = 0x00;
+ u8 curlatchlow = 0x00;
+ int poll_count = 0;
+ u8 ctrl_count = 0x00;
+
+ debug ("%s - Entry\n", __FUNCTION__);
+
+ while (!ibmphp_shutdown) {
+ if (ibmphp_shutdown)
+ break;
+
+ /* try to get the lock to do some kind of harware access */
+ down (&semOperations);
+
+ switch (poll_state) {
+ case POLL_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,
+ pslot->ctrl);
+ }
+ }
+ }
+ ++poll_count;
+ poll_state = POLL_SLEEP;
+ break;
+ case POLL_SLOTS:
+ list_for_each (pslotlist, &ibmphp_slot_head) {
+ 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);
+ }
+ 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);
+ }
+ }
+ ++poll_count;
+ poll_state = POLL_SLEEP;
+ break;
+ case POLL_SLEEP:
+ /* don't sleep with a lock on the hardware */
+ up (&semOperations);
+ msleep(POLL_INTERVAL_SEC * 1000);
+
+ if (ibmphp_shutdown)
+ break;
+
+ down (&semOperations);
+
+ if (poll_count >= POLL_LATCH_CNT) {
+ poll_count = 0;
+ poll_state = POLL_SLOTS;
+ } else
+ poll_state = POLL_LATCH_REGISTER;
+ break;
+ }
+ /* give up the harware semaphore */
+ up (&semOperations);
+ /* sleep for a short time just for good measure */
+ msleep(100);
+ }
+ up (&sem_exit);
+ debug ("%s - Exit\n", __FUNCTION__);
+}
+
+
+/*----------------------------------------------------------------------
+* 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[%p], poldslot[%p]\n", pslot, poldslot);
+
+ // bit 0 - HPC_SLOT_POWER
+ if ((pslot->status & 0x01) != (poldslot->status & 0x01))
+ 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) && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status)))
+ 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_PWRGD (pslot->status)) {
+ // power goes on and off after closing latch
+ // check again to make sure power is still ON
+ msleep(1000);
+ rc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &status);
+ if (SLOT_PWRGD (status))
+ update = TRUE;
+ else // overwrite power in pslot to OFF
+ pslot->status &= ~HPC_SLOT_POWER;
+ }
+ }
+ // CLOSE -> OPEN
+ else if ((SLOT_PWRGD (poldslot->status) == HPC_SLOT_PWRGD_GOOD)
+ && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status))) {
+ 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_do_disable_slot (pslot);
+ }
+
+ 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 controller *ctrl)
+{
+ 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 = ctrl->starting_slot_num; i <= ctrl->ending_slot_num; 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__);
+
+ daemonize("hpc_poll");
+ allow_signal(SIGKILL);
+
+ poll_hpc ();
+
+ tid_poll = 0;
+ debug ("%s - Exit\n", __FUNCTION__);
+ return 0;
+}
+
+
+/*----------------------------------------------------------------------
+* Name: ibmphp_hpc_start_poll_thread
+*
+* Action: start polling thread
+*---------------------------------------------------------------------*/
+int __init ibmphp_hpc_start_poll_thread (void)
+{
+ int rc = 0;
+
+ debug ("%s - Entry\n", __FUNCTION__);
+
+ tid_poll = kernel_thread (hpc_poll_thread, NULL, 0);
+ if (tid_poll < 0) {
+ err ("%s - Error, thread not started\n", __FUNCTION__);
+ rc = -1;
+ }
+
+ debug ("%s - Exit tid_poll[%d] rc[%d]\n", __FUNCTION__, tid_poll, rc);
+ return rc;
+}
+
+/*----------------------------------------------------------------------
+* Name: ibmphp_hpc_stop_poll_thread
+*
+* Action: stop polling thread and cleanup
+*---------------------------------------------------------------------*/
+void __exit ibmphp_hpc_stop_poll_thread (void)
+{
+ debug ("%s - Entry\n", __FUNCTION__);
+
+ ibmphp_shutdown = TRUE;
+ debug ("before locking operations \n");
+ ibmphp_lock_operations ();
+ debug ("after locking operations \n");
+
+ // wait for poll thread to exit
+ debug ("before sem_exit down \n");
+ down (&sem_exit);
+ debug ("after sem_exit down \n");
+
+ // cleanup
+ debug ("before free_hpc_access \n");
+ free_hpc_access ();
+ debug ("after free_hpc_access \n");
+ ibmphp_unlock_operations ();
+ debug ("after unlock operations \n");
+ up (&sem_exit);
+ debug ("after sem exit up\n");
+
+ debug ("%s - Exit\n", __FUNCTION__);
+}
+
+/*----------------------------------------------------------------------
+* 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 __iomem *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) {
+ msleep(1000);
+ 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/pci/hotplug/ibmphp_pci.c b/drivers/pci/hotplug/ibmphp_pci.c
new file mode 100644
index 000000000000..2335fac65fb4
--- /dev/null
+++ b/drivers/pci/hotplug/ibmphp_pci.c
@@ -0,0 +1,1747 @@
+/*
+ * 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;
+ 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++) {
+ unsigned int devfn = PCI_DEVFN(device, function);
+ ibmphp_pci_bus->number = cur_func->busno;
+
+ cur_func->function = function;
+
+ debug ("inside the loop, cur_func->busno = %x, cur_func->device = %x, cur_func->funcion = %x\n",
+ cur_func->busno, cur_func->device, cur_func->function);
+
+ pci_bus_read_config_word (ibmphp_pci_bus, devfn, 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_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type);
+ pci_bus_read_config_dword (ibmphp_pci_bus, devfn, 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 = kmalloc(sizeof(*newfunc), 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_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number);
+ flag = FALSE;
+ for (i = 0; i < 32; i++) {
+ if (func->devices[i]) {
+ newfunc = kmalloc(sizeof(*newfunc), 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 = kmalloc(sizeof(*newfunc), 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_bus_read_config_byte (ibmphp_pci_bus, devfn, 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 = kmalloc(sizeof(*newfunc), 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];
+ unsigned int devfn;
+
+ debug ("%s - inside\n", __FUNCTION__);
+
+ devfn = PCI_DEVFN(func->device, func->function);
+ ibmphp_pci_bus->number = func->busno;
+
+ 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_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF);
+ pci_bus_read_config_dword (ibmphp_pci_bus, devfn, 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 = PCI_DEVFN(func->device, func->function);
+ 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_bus_write_config_dword (ibmphp_pci_bus, devfn, 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_bus_read_config_dword (ibmphp_pci_bus, devfn, 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 = PCI_DEVFN(func->device,
+ func->function);
+ 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(*mem_tmp), 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_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->pfmem[count]->start);
+
+ /*_______________This is for debugging purposes only______________________________*/
+ debug ("b4 writing, start address is %x\n", func->pfmem[count]->start);
+ pci_bus_read_config_dword (ibmphp_pci_bus, devfn, 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_bus_write_config_dword (ibmphp_pci_bus, devfn, 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 = PCI_DEVFN(func->device,
+ func->function);
+ 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_bus_write_config_dword (ibmphp_pci_bus, devfn, 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_bus_read_config_dword (ibmphp_pci_bus, devfn, 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_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000);
+ }
+ }
+ } /* end of mem */
+ } /* end of for */
+
+ func->bus = 0; /* To indicate that this is not a PPB */
+ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_PIN, &irq);
+ if ((irq > 0x00) && (irq < 0x05))
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_LINE, func->irq[irq - 1]);
+
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_CACHE_LINE_SIZE, CACHE);
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_LATENCY_TIMER, LATENCY);
+
+ pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_ROM_ADDRESS, 0x00L);
+ pci_bus_write_config_word (ibmphp_pci_bus, devfn, 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;
+ unsigned int devfn;
+ u8 irq;
+ int retval;
+
+ debug ("%s - enter\n", __FUNCTION__);
+
+ devfn = PCI_DEVFN(func->function, func->device);
+ ibmphp_pci_bus->number = func->busno;
+
+ /* Configuring necessary info for the bridge so that we could see the devices
+ * behind it
+ */
+
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, func->busno);
+
+ /* _____________________For debugging purposes only __________________________
+ pci_bus_config_byte (ibmphp_pci_bus, devfn, 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_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, sec_number);
+
+ /* __________________For debugging purposes only __________________________________
+ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number);
+ debug ("sec_number after write/read is %x\n", sec_number);
+ ________________________________________________________________________________*/
+
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, sec_number);
+
+ /* __________________For debugging purposes only ____________________________________
+ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, &sec_number);
+ debug ("subordinate number after write/read is %x\n", sec_number);
+ __________________________________________________________________________________*/
+
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_CACHE_LINE_SIZE, CACHE);
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_LATENCY_TIMER, LATENCY);
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, 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_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF);
+ pci_bus_read_config_dword (ibmphp_pci_bus, devfn, 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 = PCI_DEVFN(func->device,
+ func->function);
+ 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_bus_write_config_dword (ibmphp_pci_bus, devfn, 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 = PCI_DEVFN(func->device,
+ func->function);
+ 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(*mem_tmp), 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_bus_write_config_dword (ibmphp_pci_bus, devfn, 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_bus_write_config_dword (ibmphp_pci_bus, devfn, 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 = PCI_DEVFN(func->device,
+ func->function);
+ 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_bus_write_config_dword (ibmphp_pci_bus, devfn, 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_bus_write_config_dword (ibmphp_pci_bus, devfn, 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;
+
+ ibmphp_pci_bus->number = func->busno;
+ 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(*io), 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 = PCI_DEVFN(func->device, func->function);
+ 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(*mem), 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 = PCI_DEVFN(func->device, func->function);
+ 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(*pfmem), 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 = PCI_DEVFN(func->device, func->function);
+ 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(*mem_tmp), 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) {
+ /* If on bootup, there was a bridged card in this slot,
+ * then card was removed and ibmphp got unloaded and loaded
+ * back again, there's no way for us to remove the bus
+ * struct, so no need to kmalloc, can use existing node
+ */
+ bus = ibmphp_find_res_bus (sec_number);
+ if (!bus) {
+ bus = kmalloc(sizeof(*bus), 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);
+ } else if (!(bus->rangeIO) && !(bus->rangeMem) && !(bus->rangePFMem))
+ rc = add_new_bus (bus, io, mem, pfmem, 0xFF);
+ else {
+ err ("expected bus structure not empty?\n");
+ retval = -EIO;
+ goto error;
+ }
+ if (rc) {
+ if (rc == -ENOMEM) {
+ ibmphp_remove_bus (bus, func->busno);
+ kfree (amount_needed);
+ return rc;
+ }
+ retval = rc;
+ goto error;
+ }
+ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &io_base);
+ pci_bus_read_config_word (ibmphp_pci_bus, devfn, 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_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00 | bus->rangeIO->start >> 8);
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00 | bus->rangeIO->end >> 8);
+
+ /* _______________This is for debugging purposes only ____________________
+ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &temp);
+ debug ("io_base = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8);
+ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, 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_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, 0x0000);
+ pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, 0x0000);
+ }
+ } else {
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00);
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00);
+ }
+
+ if (bus->noMemRanges) {
+ pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0x0000 | bus->rangeMem->start >> 16);
+ pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000 | bus->rangeMem->end >> 16);
+
+ /* ____________________This is for debugging purposes only ________________________
+ pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &temp);
+ debug ("mem_base = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
+ pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &temp);
+ debug ("mem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
+ __________________________________________________________________________________*/
+
+ } else {
+ pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0xffff);
+ pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000);
+ }
+ if (bus->noPFMemRanges) {
+ pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, 0x0000 | bus->rangePFMem->start >> 16);
+ pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, 0x0000 | bus->rangePFMem->end >> 16);
+
+ /* __________________________This is for debugging purposes only _______________________
+ pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &temp);
+ debug ("pfmem_base = %x", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
+ pci_bus_read_config_word (ibmphp_pci_bus, devfn, 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_bus_write_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, 0x00000000);
+ pci_bus_write_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, 0x00000000);
+ }
+ } else {
+ pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, 0xffff);
+ pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, 0x0000);
+ }
+
+ debug ("b4 writing control information\n");
+
+ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_PIN, &irq);
+ if ((irq > 0x00) && (irq < 0x05))
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_LINE, func->irq[irq - 1]);
+ /*
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, ctrl);
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_PARITY);
+ pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_SERR);
+ */
+
+ pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_COMMAND, DEVICEENABLE);
+ pci_bus_write_config_word (ibmphp_pci_bus, devfn, 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:
+ 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;
+ unsigned int devfn;
+ 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(*amount), GFP_KERNEL);
+ if (amount == NULL)
+ return NULL;
+ memset (amount, 0, sizeof (struct res_needed));
+
+ ibmphp_pci_bus->number = busno;
+
+ 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++) {
+ devfn = PCI_DEVFN(device, function);
+
+ pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id);
+
+ if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
+ /* found correct device!!! */
+ howmany++;
+
+ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type);
+ pci_bus_read_config_dword (ibmphp_pci_bus, devfn, 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_bus_read_config_byte (ibmphp_pci_bus, devfn, address[count], &tmp);
+ if (tmp & 0x01) // IO
+ pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFD);
+ else // MEMORY
+ pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF);
+ */
+ pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF);
+ pci_bus_read_config_dword (ibmphp_pci_bus, devfn, 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;
+ unsigned int devfn;
+
+ debug ("%s - enter\n", __FUNCTION__);
+
+ bus = ibmphp_find_res_bus (busno);
+ if (!bus) {
+ debug ("cannot find corresponding bus.\n");
+ return -EINVAL;
+ }
+
+ devfn = PCI_DEVFN(device, function);
+ ibmphp_pci_bus->number = busno;
+ for (count = 0; address[count]; count++) { /* for 6 BARs */
+ pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &start_address);
+
+ /* We can do this here, b/c by that time the device driver of the card has been stopped */
+
+ pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF);
+ pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &size);
+ pci_bus_write_config_dword (ibmphp_pci_bus, devfn, 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 */
+ start_address &= PCI_BASE_ADDRESS_MEM_MASK;
+ if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+ /* pfmem */
+ 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);
+ }
+ } else {
+ /* regular memory */
+ 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
+ };
+ unsigned int devfn;
+
+ devfn = PCI_DEVFN(device, function);
+ ibmphp_pci_bus->number = busno;
+ bus_no = (int) busno;
+ debug ("busno is %x\n", busno);
+ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, &pri_number);
+ debug ("%s - busno = %x, primary_number = %x\n", __FUNCTION__, busno, pri_number);
+
+ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, 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_bus_read_config_byte (ibmphp_pci_bus, devfn, 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_bus_read_config_dword (ibmphp_pci_bus, devfn, 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 */
+ start_address &= PCI_BASE_ADDRESS_MEM_MASK;
+ if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+ /* pfmem */
+ 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);
+ }
+ } else {
+ /* regular memory */
+ 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;
+ unsigned int devfn;
+ 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++) {
+ devfn = PCI_DEVFN(device, function);
+ ibmphp_pci_bus->number = busno;
+
+ pci_bus_read_config_word (ibmphp_pci_bus, devfn, 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_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type);
+ pci_bus_read_config_dword (ibmphp_pci_bus, devfn, 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) {
+ 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[i]) {
+ debug ("io[%d] exists\n", i);
+ if (the_end > 0)
+ ibmphp_remove_resource (cur_func->io[i]);
+ cur_func->io[i] = NULL;
+ }
+ if (cur_func->mem[i]) {
+ debug ("mem[%d] exists\n", i);
+ if (the_end > 0)
+ ibmphp_remove_resource (cur_func->mem[i]);
+ cur_func->mem[i] = NULL;
+ }
+ if (cur_func->pfmem[i]) {
+ debug ("pfmem[%d] exists\n", i);
+ if (the_end > 0)
+ ibmphp_remove_resource (cur_func->pfmem[i]);
+ cur_func->pfmem[i] = NULL;
+ }
+ }
+
+ temp_func = cur_func->next;
+ kfree (cur_func);
+ cur_func = temp_func;
+ }
+ }
+
+ sl->func = NULL;
+ *slot_cur = sl;
+ debug ("%s - exit\n", __FUNCTION__);
+ 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 */
+ if (parent_busno != 0xFF) {
+ 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(*io_range), 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(*mem_range), 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(*pfmem_range), 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;
+ struct bus_node *bus_cur;
+
+ 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;
+ bus_cur = ibmphp_find_res_bus (busno);
+ /* either there is no such bus number, or there are no ranges, which
+ * can only happen if we removed the bridged device in previous load
+ * of the driver, and now only have the skeleton bus struct
+ */
+ if ((!bus_cur) || (!(bus_cur->rangeIO) && !(bus_cur->rangeMem) && !(bus_cur->rangePFMem)))
+ return busno;
+ return 0xff;
+}
+
diff --git a/drivers/pci/hotplug/ibmphp_res.c b/drivers/pci/hotplug/ibmphp_res.c
new file mode 100644
index 000000000000..9c224c94d698
--- /dev/null
+++ b/drivers/pci/hotplug/ibmphp_res.c
@@ -0,0 +1,2156 @@
+/*
+ * 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 <linux/init.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 struct bus_node *find_bus_wprev (u8, struct bus_node **, u8);
+
+static LIST_HEAD(gbuses);
+
+static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc * curr, u8 busno, int flag)
+{
+ struct bus_node * newbus;
+
+ if (!(curr) && !(flag)) {
+ err ("NULL pointer passed\n");
+ return NULL;
+ }
+
+ 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));
+ if (flag)
+ newbus->busno = busno;
+ else
+ newbus->busno = curr->bus_num;
+ list_add_tail (&newbus->bus_list, &gbuses);
+ return newbus;
+}
+
+static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc * curr)
+{
+ struct resource_node *rs;
+
+ if (!curr) {
+ err ("NULL passed to allocate\n");
+ return NULL;
+ }
+
+ 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 __init 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 __init 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, 0, 0);
+ 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, 0, 0);
+ 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, 0, 0);
+ 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);
+ }
+ }
+ }
+
+ 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;
+ }
+ rc = once_over (); /* This is to align ranges (so no -1) */
+ if (rc)
+ return rc;
+ 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: resource ptr
+ * 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__);
+
+ if (!res) {
+ err ("NULL passed to add\n");
+ return -ENODEV;
+ }
+
+ bus_cur = find_bus_wprev (res->busno, NULL, 0);
+
+ if (!bus_cur) {
+ /* didn't find a bus, smth's wrong!!! */
+ debug ("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 occurrence 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 = "";
+
+ if (!res) {
+ err ("resource to remove is NULL\n");
+ return -ENODEV;
+ }
+
+ 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) {
+ 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!!! */
+ debug ("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) {
+ debug ("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 of 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 = "";
+
+ if (!bus) {
+ err ("The bus passed in NULL to find resource\n");
+ return -ENODEV;
+ }
+
+ 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) {
+ debug ("SOS...cannot find %s resource in the bus.\n", type);
+ return -EINVAL;
+ }
+ } else {
+ debug ("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 __init 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 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;
+
+ debug_pci ("*****************START**********************\n");
+
+ 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_pci ("This is bus # %d. There are\n", bus_cur->busno);
+ debug_pci ("IORanges = %d\t", bus_cur->noIORanges);
+ debug_pci ("MemRanges = %d\t", bus_cur->noMemRanges);
+ debug_pci ("PFMemRanges = %d\n", bus_cur->noPFMemRanges);
+ debug_pci ("The IO Ranges are as follows:\n");
+ if (bus_cur->rangeIO) {
+ range = bus_cur->rangeIO;
+ for (i = 0; i < bus_cur->noIORanges; i++) {
+ debug_pci ("rangeno is %d\n", range->rangeno);
+ debug_pci ("[%x - %x]\n", range->start, range->end);
+ range = range->next;
+ }
+ }
+
+ debug_pci ("The Mem Ranges are as follows:\n");
+ if (bus_cur->rangeMem) {
+ range = bus_cur->rangeMem;
+ for (i = 0; i < bus_cur->noMemRanges; i++) {
+ debug_pci ("rangeno is %d\n", range->rangeno);
+ debug_pci ("[%x - %x]\n", range->start, range->end);
+ range = range->next;
+ }
+ }
+
+ debug_pci ("The PFMem Ranges are as follows:\n");
+
+ if (bus_cur->rangePFMem) {
+ range = bus_cur->rangePFMem;
+ for (i = 0; i < bus_cur->noPFMemRanges; i++) {
+ debug_pci ("rangeno is %d\n", range->rangeno);
+ debug_pci ("[%x - %x]\n", range->start, range->end);
+ range = range->next;
+ }
+ }
+
+ debug_pci ("The resources on this bus are as follows\n");
+
+ debug_pci ("IO...\n");
+ if (bus_cur->firstIO) {
+ res = bus_cur->firstIO;
+ while (res) {
+ debug_pci ("The range # is %d\n", res->rangeno);
+ debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
+ debug_pci ("[%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_pci ("Mem...\n");
+ if (bus_cur->firstMem) {
+ res = bus_cur->firstMem;
+ while (res) {
+ debug_pci ("The range # is %d\n", res->rangeno);
+ debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
+ debug_pci ("[%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_pci ("PFMem...\n");
+ if (bus_cur->firstPFMem) {
+ res = bus_cur->firstPFMem;
+ while (res) {
+ debug_pci ("The range # is %d\n", res->rangeno);
+ debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
+ debug_pci ("[%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_pci ("PFMemFromMem...\n");
+ if (bus_cur->firstPFMemFromMem) {
+ res = bus_cur->firstPFMemFromMem;
+ while (res) {
+ debug_pci ("The range # is %d\n", res->rangeno);
+ debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
+ debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len);
+ res = res->next;
+ }
+ }
+ }
+ debug_pci ("***********************END***********************\n");
+}
+
+static int range_exists_already (struct range_node * range, struct bus_node * bus_cur, u8 type)
+{
+ struct range_node * range_cur = NULL;
+ switch (type) {
+ case IO:
+ range_cur = bus_cur->rangeIO;
+ break;
+ case MEM:
+ range_cur = bus_cur->rangeMem;
+ break;
+ case PFMEM:
+ range_cur = bus_cur->rangePFMem;
+ break;
+ default:
+ err ("wrong type passed to find out if range already exists\n");
+ return -ENODEV;
+ }
+
+ while (range_cur) {
+ if ((range_cur->start == range->start) && (range_cur->end == range->end))
+ return 1;
+ range_cur = range_cur->next;
+ }
+
+ return 0;
+}
+
+/* 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. For bridged cards that were added during previous load of the
+ * driver, only the ranges and the bus structure are added, the devices are
+ * added from NVRAM
+ * 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 __init update_bridge_ranges (struct bus_node **bus)
+{
+ u8 sec_busno, device, function, 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;
+ unsigned int devfn;
+
+ bus_cur = *bus;
+ if (!bus_cur)
+ return -ENODEV;
+ ibmphp_pci_bus->number = bus_cur->busno;
+
+ debug ("inside %s\n", __FUNCTION__);
+ debug ("bus_cur->busno = %x\n", bus_cur->busno);
+
+ for (device = 0; device < 32; device++) {
+ for (function = 0x00; function < 0x08; function++) {
+ devfn = PCI_DEVFN(device, function);
+ pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id);
+
+ if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
+ /* found correct device!!! */
+ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, 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_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_busno);
+ bus_sec = find_bus_wprev (sec_busno, NULL, 0);
+ /* this bus structure doesn't exist yet, PPB was configured during previous loading of ibmphp */
+ if (!bus_sec) {
+ bus_sec = alloc_error_bus (NULL, sec_busno, 1);
+ /* the rest will be populated during NVRAM call */
+ return 0;
+ }
+ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &start_io_address);
+ pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &end_io_address);
+ pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, &upper_io_start);
+ pci_bus_read_config_word (ibmphp_pci_bus, devfn, 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) {
+ if (!range_exists_already (range, bus_sec, IO)) {
+ add_range (IO, range, bus_sec);
+ ++bus_sec->noIORanges;
+ } else {
+ kfree (range);
+ range = NULL;
+ }
+ } else {
+ /* 1st IO Range on the bus */
+ range->rangeno = 1;
+ bus_sec->rangeIO = range;
+ ++bus_sec->noIORanges;
+ }
+ fix_resources (bus_sec);
+
+ if (ibmphp_find_resource (bus_cur, start_address, &io, IO)) {
+ 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_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &start_mem_address);
+ pci_bus_read_config_word (ibmphp_pci_bus, devfn, 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) {
+ if (!range_exists_already (range, bus_sec, MEM)) {
+ add_range (MEM, range, bus_sec);
+ ++bus_sec->noMemRanges;
+ } else {
+ kfree (range);
+ range = NULL;
+ }
+ } else {
+ /* 1st Mem Range on the bus */
+ range->rangeno = 1;
+ bus_sec->rangeMem = range;
+ ++bus_sec->noMemRanges;
+ }
+
+ fix_resources (bus_sec);
+
+ if (ibmphp_find_resource (bus_cur, start_address, &mem, MEM)) {
+ 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_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &start_mem_address);
+ pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &end_mem_address);
+ pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, &upper_start);
+ pci_bus_read_config_dword (ibmphp_pci_bus, devfn, 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) {
+ if (!range_exists_already (range, bus_sec, PFMEM)) {
+ add_range (PFMEM, range, bus_sec);
+ ++bus_sec->noPFMemRanges;
+ } else {
+ kfree (range);
+ range = NULL;
+ }
+ } else {
+ /* 1st PFMem Range on the bus */
+ range->rangeno = 1;
+ bus_sec->rangePFMem = range;
+ ++bus_sec->noPFMemRanges;
+ }
+
+ fix_resources (bus_sec);
+ if (ibmphp_find_resource (bus_cur, start_address, &pfmem, PFMEM)) {
+ 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;
+}
diff --git a/drivers/pci/hotplug/pci_hotplug.h b/drivers/pci/hotplug/pci_hotplug.h
new file mode 100644
index 000000000000..57ace325168d
--- /dev/null
+++ b/drivers/pci/hotplug/pci_hotplug.h
@@ -0,0 +1,180 @@
+/*
+ * PCI HotPlug Core Functions
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 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 <greg@kroah.com>
+ *
+ */
+#ifndef _PCI_HOTPLUG_H
+#define _PCI_HOTPLUG_H
+
+
+/* These values come from the PCI Hotplug Spec */
+enum pci_bus_speed {
+ PCI_SPEED_33MHz = 0x00,
+ PCI_SPEED_66MHz = 0x01,
+ PCI_SPEED_66MHz_PCIX = 0x02,
+ PCI_SPEED_100MHz_PCIX = 0x03,
+ PCI_SPEED_133MHz_PCIX = 0x04,
+ PCI_SPEED_66MHz_PCIX_ECC = 0x05,
+ PCI_SPEED_100MHz_PCIX_ECC = 0x06,
+ PCI_SPEED_133MHz_PCIX_ECC = 0x07,
+ PCI_SPEED_66MHz_PCIX_266 = 0x09,
+ PCI_SPEED_100MHz_PCIX_266 = 0x0a,
+ PCI_SPEED_133MHz_PCIX_266 = 0x0b,
+ PCI_SPEED_66MHz_PCIX_533 = 0x11,
+ PCI_SPEED_100MHz_PCIX_533 = 0x12,
+ PCI_SPEED_133MHz_PCIX_533 = 0x13,
+ PCI_SPEED_UNKNOWN = 0xff,
+};
+
+/* These values come from the PCI Express Spec */
+enum pcie_link_width {
+ PCIE_LNK_WIDTH_RESRV = 0x00,
+ PCIE_LNK_X1 = 0x01,
+ PCIE_LNK_X2 = 0x02,
+ PCIE_LNK_X4 = 0x04,
+ PCIE_LNK_X8 = 0x08,
+ PCIE_LNK_X12 = 0x0C,
+ PCIE_LNK_X16 = 0x10,
+ PCIE_LNK_X32 = 0x20,
+ PCIE_LNK_WIDTH_UNKNOWN = 0xFF,
+};
+
+enum pcie_link_speed {
+ PCIE_2PT5GB = 0x14,
+ PCIE_LNK_SPEED_UNKNOWN = 0xFF,
+};
+
+struct hotplug_slot;
+struct hotplug_slot_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct hotplug_slot *, char *);
+ ssize_t (*store)(struct hotplug_slot *, const char *, size_t);
+};
+#define to_hotplug_attr(n) container_of(n, struct hotplug_slot_attribute, attr);
+
+/**
+ * struct hotplug_slot_ops -the callbacks that the hotplug pci core can use
+ * @owner: The module owner of this structure
+ * @enable_slot: Called when the user wants to enable a specific pci slot
+ * @disable_slot: Called when the user wants to disable a specific pci slot
+ * @set_attention_status: Called to set the specific slot's attention LED to
+ * the specified value
+ * @hardware_test: Called to run a specified hardware test on the specified
+ * slot.
+ * @get_power_status: Called to get the current power status of a slot.
+ * If this field is NULL, the value passed in the struct hotplug_slot_info
+ * will be used when this value is requested by a user.
+ * @get_attention_status: Called to get the current attention status of a slot.
+ * If this field is NULL, the value passed in the struct hotplug_slot_info
+ * will be used when this value is requested by a user.
+ * @get_latch_status: Called to get the current latch status of a slot.
+ * If this field is NULL, the value passed in the struct hotplug_slot_info
+ * will be used when this value is requested by a user.
+ * @get_adapter_status: Called to get see if an adapter is present in the slot or not.
+ * If this field is NULL, the value passed in the struct hotplug_slot_info
+ * will be used when this value is requested by a user.
+ * @get_address: Called to get pci address of a slot.
+ * If this field is NULL, the value passed in the struct hotplug_slot_info
+ * will be used when this value is requested by a user.
+ * @get_max_bus_speed: Called to get the max bus speed for a slot.
+ * If this field is NULL, the value passed in the struct hotplug_slot_info
+ * will be used when this value is requested by a user.
+ * @get_cur_bus_speed: Called to get the current bus speed for a slot.
+ * If this field is NULL, the value passed in the struct hotplug_slot_info
+ * will be used when this value is requested by a user.
+ *
+ * The table of function pointers that is passed to the hotplug pci core by a
+ * hotplug pci driver. These functions are called by the hotplug pci core when
+ * the user wants to do something to a specific slot (query it for information,
+ * set an LED, enable / disable power, etc.)
+ */
+struct hotplug_slot_ops {
+ struct module *owner;
+ int (*enable_slot) (struct hotplug_slot *slot);
+ int (*disable_slot) (struct hotplug_slot *slot);
+ int (*set_attention_status) (struct hotplug_slot *slot, u8 value);
+ int (*hardware_test) (struct hotplug_slot *slot, u32 value);
+ int (*get_power_status) (struct hotplug_slot *slot, u8 *value);
+ int (*get_attention_status) (struct hotplug_slot *slot, u8 *value);
+ int (*get_latch_status) (struct hotplug_slot *slot, u8 *value);
+ int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value);
+ int (*get_address) (struct hotplug_slot *slot, u32 *value);
+ int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
+ int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
+};
+
+/**
+ * struct hotplug_slot_info - used to notify the hotplug pci core of the state of the slot
+ * @power: if power is enabled or not (1/0)
+ * @attention_status: if the attention light is enabled or not (1/0)
+ * @latch_status: if the latch (if any) is open or closed (1/0)
+ * @adapter_present: if there is a pci board present in the slot or not (1/0)
+ * @address: (domain << 16 | bus << 8 | dev)
+ *
+ * Used to notify the hotplug pci core of the status of a specific slot.
+ */
+struct hotplug_slot_info {
+ u8 power_status;
+ u8 attention_status;
+ u8 latch_status;
+ u8 adapter_status;
+ u32 address;
+ enum pci_bus_speed max_bus_speed;
+ enum pci_bus_speed cur_bus_speed;
+};
+
+/**
+ * struct hotplug_slot - used to register a physical slot with the hotplug pci core
+ * @name: the name of the slot being registered. This string must
+ * be unique amoung slots registered on this system.
+ * @ops: pointer to the &struct hotplug_slot_ops to be used for this slot
+ * @info: pointer to the &struct hotplug_slot_info for the inital values for
+ * this slot.
+ * @release: called during pci_hp_deregister to free memory allocated in a
+ * hotplug_slot structure.
+ * @private: used by the hotplug pci controller driver to store whatever it
+ * needs.
+ */
+struct hotplug_slot {
+ char *name;
+ struct hotplug_slot_ops *ops;
+ struct hotplug_slot_info *info;
+ void (*release) (struct hotplug_slot *slot);
+ void *private;
+
+ /* Variables below this are for use only by the hotplug pci core. */
+ struct list_head slot_list;
+ struct kobject kobj;
+};
+#define to_hotplug_slot(n) container_of(n, struct hotplug_slot, kobj)
+
+extern int pci_hp_register (struct hotplug_slot *slot);
+extern int pci_hp_deregister (struct hotplug_slot *slot);
+extern int pci_hp_change_slot_info (struct hotplug_slot *slot,
+ struct hotplug_slot_info *info);
+extern struct subsystem pci_hotplug_slots_subsys;
+
+#endif
+
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
new file mode 100644
index 000000000000..c802f6270b89
--- /dev/null
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -0,0 +1,715 @@
+/*
+ * PCI HotPlug Controller Core
+ *
+ * Copyright (C) 2001-2002 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 <greg@kroah.com>
+ *
+ * Filesystem portion based on work done by Pat Mochel on ddfs/driverfs
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/pci.h>
+#include <asm/uaccess.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include "pci_hotplug.h"
+
+
+#define MY_NAME "pci_hotplug"
+
+#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __FUNCTION__ , ## 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)
+
+
+/* local variables */
+static int debug;
+
+#define DRIVER_VERSION "0.5"
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>"
+#define DRIVER_DESC "PCI Hot Plug PCI Core"
+
+
+//////////////////////////////////////////////////////////////////
+
+static LIST_HEAD(pci_hotplug_slot_list);
+
+struct subsystem pci_hotplug_slots_subsys;
+
+static ssize_t hotplug_slot_attr_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct hotplug_slot *slot = to_hotplug_slot(kobj);
+ struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
+ return attribute->show ? attribute->show(slot, buf) : 0;
+}
+
+static ssize_t hotplug_slot_attr_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t len)
+{
+ struct hotplug_slot *slot = to_hotplug_slot(kobj);
+ struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
+ return attribute->store ? attribute->store(slot, buf, len) : 0;
+}
+
+static struct sysfs_ops hotplug_slot_sysfs_ops = {
+ .show = hotplug_slot_attr_show,
+ .store = hotplug_slot_attr_store,
+};
+
+static void hotplug_slot_release(struct kobject *kobj)
+{
+ struct hotplug_slot *slot = to_hotplug_slot(kobj);
+ if (slot->release)
+ slot->release(slot);
+}
+
+static struct kobj_type hotplug_slot_ktype = {
+ .sysfs_ops = &hotplug_slot_sysfs_ops,
+ .release = &hotplug_slot_release,
+};
+
+decl_subsys_name(pci_hotplug_slots, slots, &hotplug_slot_ktype, NULL);
+
+/* these strings match up with the values in pci_bus_speed */
+static char *pci_bus_speed_strings[] = {
+ "33 MHz PCI", /* 0x00 */
+ "66 MHz PCI", /* 0x01 */
+ "66 MHz PCIX", /* 0x02 */
+ "100 MHz PCIX", /* 0x03 */
+ "133 MHz PCIX", /* 0x04 */
+ NULL, /* 0x05 */
+ NULL, /* 0x06 */
+ NULL, /* 0x07 */
+ NULL, /* 0x08 */
+ "66 MHz PCIX 266", /* 0x09 */
+ "100 MHz PCIX 266", /* 0x0a */
+ "133 MHz PCIX 266", /* 0x0b */
+ NULL, /* 0x0c */
+ NULL, /* 0x0d */
+ NULL, /* 0x0e */
+ NULL, /* 0x0f */
+ NULL, /* 0x10 */
+ "66 MHz PCIX 533", /* 0x11 */
+ "100 MHz PCIX 533", /* 0x12 */
+ "133 MHz PCIX 533", /* 0x13 */
+ "25 GBps PCI-E", /* 0x14 */
+};
+
+#ifdef CONFIG_HOTPLUG_PCI_CPCI
+extern int cpci_hotplug_init(int debug);
+extern void cpci_hotplug_exit(void);
+#else
+static inline int cpci_hotplug_init(int debug) { return 0; }
+static inline void cpci_hotplug_exit(void) { }
+#endif
+
+/* Weee, fun with macros... */
+#define GET_STATUS(name,type) \
+static int get_##name (struct hotplug_slot *slot, type *value) \
+{ \
+ struct hotplug_slot_ops *ops = slot->ops; \
+ int retval = 0; \
+ if (try_module_get(ops->owner)) { \
+ if (ops->get_##name) \
+ retval = ops->get_##name (slot, value); \
+ else \
+ *value = slot->info->name; \
+ module_put(ops->owner); \
+ } \
+ return retval; \
+}
+
+GET_STATUS(power_status, u8)
+GET_STATUS(attention_status, u8)
+GET_STATUS(latch_status, u8)
+GET_STATUS(adapter_status, u8)
+GET_STATUS(address, u32)
+GET_STATUS(max_bus_speed, enum pci_bus_speed)
+GET_STATUS(cur_bus_speed, enum pci_bus_speed)
+
+static ssize_t power_read_file (struct hotplug_slot *slot, char *buf)
+{
+ int retval;
+ u8 value;
+
+ retval = get_power_status (slot, &value);
+ if (retval)
+ goto exit;
+ retval = sprintf (buf, "%d\n", value);
+exit:
+ return retval;
+}
+
+static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf,
+ size_t count)
+{
+ unsigned long lpower;
+ u8 power;
+ int retval = 0;
+
+ lpower = simple_strtoul (buf, NULL, 10);
+ power = (u8)(lpower & 0xff);
+ dbg ("power = %d\n", power);
+
+ if (!try_module_get(slot->ops->owner)) {
+ retval = -ENODEV;
+ goto exit;
+ }
+ switch (power) {
+ case 0:
+ if (slot->ops->disable_slot)
+ retval = slot->ops->disable_slot(slot);
+ break;
+
+ case 1:
+ if (slot->ops->enable_slot)
+ retval = slot->ops->enable_slot(slot);
+ break;
+
+ default:
+ err ("Illegal value specified for power\n");
+ retval = -EINVAL;
+ }
+ module_put(slot->ops->owner);
+
+exit:
+ if (retval)
+ return retval;
+ return count;
+}
+
+static struct hotplug_slot_attribute hotplug_slot_attr_power = {
+ .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
+ .show = power_read_file,
+ .store = power_write_file
+};
+
+static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf)
+{
+ int retval;
+ u8 value;
+
+ retval = get_attention_status (slot, &value);
+ if (retval)
+ goto exit;
+ retval = sprintf (buf, "%d\n", value);
+
+exit:
+ return retval;
+}
+
+static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
+ size_t count)
+{
+ unsigned long lattention;
+ u8 attention;
+ int retval = 0;
+
+ lattention = simple_strtoul (buf, NULL, 10);
+ attention = (u8)(lattention & 0xff);
+ dbg (" - attention = %d\n", attention);
+
+ if (!try_module_get(slot->ops->owner)) {
+ retval = -ENODEV;
+ goto exit;
+ }
+ if (slot->ops->set_attention_status)
+ retval = slot->ops->set_attention_status(slot, attention);
+ module_put(slot->ops->owner);
+
+exit:
+ if (retval)
+ return retval;
+ return count;
+}
+
+static struct hotplug_slot_attribute hotplug_slot_attr_attention = {
+ .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
+ .show = attention_read_file,
+ .store = attention_write_file
+};
+
+static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf)
+{
+ int retval;
+ u8 value;
+
+ retval = get_latch_status (slot, &value);
+ if (retval)
+ goto exit;
+ retval = sprintf (buf, "%d\n", value);
+
+exit:
+ return retval;
+}
+
+static struct hotplug_slot_attribute hotplug_slot_attr_latch = {
+ .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
+ .show = latch_read_file,
+};
+
+static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf)
+{
+ int retval;
+ u8 value;
+
+ retval = get_adapter_status (slot, &value);
+ if (retval)
+ goto exit;
+ retval = sprintf (buf, "%d\n", value);
+
+exit:
+ return retval;
+}
+
+static struct hotplug_slot_attribute hotplug_slot_attr_presence = {
+ .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
+ .show = presence_read_file,
+};
+
+static ssize_t address_read_file (struct hotplug_slot *slot, char *buf)
+{
+ int retval;
+ u32 address;
+
+ retval = get_address (slot, &address);
+ if (retval)
+ goto exit;
+ retval = sprintf (buf, "%04x:%02x:%02x\n",
+ (address >> 16) & 0xffff,
+ (address >> 8) & 0xff,
+ address & 0xff);
+
+exit:
+ return retval;
+}
+
+static struct hotplug_slot_attribute hotplug_slot_attr_address = {
+ .attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
+ .show = address_read_file,
+};
+
+static char *unknown_speed = "Unknown bus speed";
+
+static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+{
+ char *speed_string;
+ int retval;
+ enum pci_bus_speed value;
+
+ retval = get_max_bus_speed (slot, &value);
+ if (retval)
+ goto exit;
+
+ if (value == PCI_SPEED_UNKNOWN)
+ speed_string = unknown_speed;
+ else
+ speed_string = pci_bus_speed_strings[value];
+
+ retval = sprintf (buf, "%s\n", speed_string);
+
+exit:
+ return retval;
+}
+
+static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = {
+ .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
+ .show = max_bus_speed_read_file,
+};
+
+static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+{
+ char *speed_string;
+ int retval;
+ enum pci_bus_speed value;
+
+ retval = get_cur_bus_speed (slot, &value);
+ if (retval)
+ goto exit;
+
+ if (value == PCI_SPEED_UNKNOWN)
+ speed_string = unknown_speed;
+ else
+ speed_string = pci_bus_speed_strings[value];
+
+ retval = sprintf (buf, "%s\n", speed_string);
+
+exit:
+ return retval;
+}
+
+static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = {
+ .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
+ .show = cur_bus_speed_read_file,
+};
+
+static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf,
+ size_t count)
+{
+ unsigned long ltest;
+ u32 test;
+ int retval = 0;
+
+ ltest = simple_strtoul (buf, NULL, 10);
+ test = (u32)(ltest & 0xffffffff);
+ dbg ("test = %d\n", test);
+
+ if (!try_module_get(slot->ops->owner)) {
+ retval = -ENODEV;
+ goto exit;
+ }
+ if (slot->ops->hardware_test)
+ retval = slot->ops->hardware_test(slot, test);
+ module_put(slot->ops->owner);
+
+exit:
+ if (retval)
+ return retval;
+ return count;
+}
+
+static struct hotplug_slot_attribute hotplug_slot_attr_test = {
+ .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
+ .store = test_write_file
+};
+
+static int has_power_file (struct hotplug_slot *slot)
+{
+ if ((!slot) || (!slot->ops))
+ return -ENODEV;
+ if ((slot->ops->enable_slot) ||
+ (slot->ops->disable_slot) ||
+ (slot->ops->get_power_status))
+ return 0;
+ return -ENOENT;
+}
+
+static int has_attention_file (struct hotplug_slot *slot)
+{
+ if ((!slot) || (!slot->ops))
+ return -ENODEV;
+ if ((slot->ops->set_attention_status) ||
+ (slot->ops->get_attention_status))
+ return 0;
+ return -ENOENT;
+}
+
+static int has_latch_file (struct hotplug_slot *slot)
+{
+ if ((!slot) || (!slot->ops))
+ return -ENODEV;
+ if (slot->ops->get_latch_status)
+ return 0;
+ return -ENOENT;
+}
+
+static int has_adapter_file (struct hotplug_slot *slot)
+{
+ if ((!slot) || (!slot->ops))
+ return -ENODEV;
+ if (slot->ops->get_adapter_status)
+ return 0;
+ return -ENOENT;
+}
+
+static int has_address_file (struct hotplug_slot *slot)
+{
+ if ((!slot) || (!slot->ops))
+ return -ENODEV;
+ if (slot->ops->get_address)
+ return 0;
+ return -ENOENT;
+}
+
+static int has_max_bus_speed_file (struct hotplug_slot *slot)
+{
+ if ((!slot) || (!slot->ops))
+ return -ENODEV;
+ if (slot->ops->get_max_bus_speed)
+ return 0;
+ return -ENOENT;
+}
+
+static int has_cur_bus_speed_file (struct hotplug_slot *slot)
+{
+ if ((!slot) || (!slot->ops))
+ return -ENODEV;
+ if (slot->ops->get_cur_bus_speed)
+ return 0;
+ return -ENOENT;
+}
+
+static int has_test_file (struct hotplug_slot *slot)
+{
+ if ((!slot) || (!slot->ops))
+ return -ENODEV;
+ if (slot->ops->hardware_test)
+ return 0;
+ return -ENOENT;
+}
+
+static int fs_add_slot (struct hotplug_slot *slot)
+{
+ if (has_power_file(slot) == 0)
+ sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+
+ if (has_attention_file(slot) == 0)
+ sysfs_create_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
+
+ if (has_latch_file(slot) == 0)
+ sysfs_create_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
+
+ if (has_adapter_file(slot) == 0)
+ sysfs_create_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
+
+ if (has_address_file(slot) == 0)
+ sysfs_create_file(&slot->kobj, &hotplug_slot_attr_address.attr);
+
+ if (has_max_bus_speed_file(slot) == 0)
+ sysfs_create_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
+
+ if (has_cur_bus_speed_file(slot) == 0)
+ sysfs_create_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
+
+ if (has_test_file(slot) == 0)
+ sysfs_create_file(&slot->kobj, &hotplug_slot_attr_test.attr);
+
+ return 0;
+}
+
+static void fs_remove_slot (struct hotplug_slot *slot)
+{
+ if (has_power_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+
+ if (has_attention_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
+
+ if (has_latch_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
+
+ if (has_adapter_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
+
+ if (has_address_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
+
+ if (has_max_bus_speed_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
+
+ if (has_cur_bus_speed_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
+
+ if (has_test_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr);
+}
+
+static struct hotplug_slot *get_slot_from_name (const char *name)
+{
+ struct hotplug_slot *slot;
+ struct list_head *tmp;
+
+ list_for_each (tmp, &pci_hotplug_slot_list) {
+ slot = list_entry (tmp, struct hotplug_slot, slot_list);
+ if (strcmp(slot->name, name) == 0)
+ return slot;
+ }
+ return NULL;
+}
+
+/**
+ * pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
+ * @slot: pointer to the &struct hotplug_slot to register
+ *
+ * Registers a hotplug slot with the pci hotplug subsystem, which will allow
+ * userspace interaction to the slot.
+ *
+ * Returns 0 if successful, anything else for an error.
+ */
+int pci_hp_register (struct hotplug_slot *slot)
+{
+ int result;
+
+ if (slot == NULL)
+ return -ENODEV;
+ if ((slot->info == NULL) || (slot->ops == NULL))
+ return -EINVAL;
+ if (slot->release == NULL) {
+ dbg("Why are you trying to register a hotplug slot"
+ "without a proper release function?\n");
+ return -EINVAL;
+ }
+
+ kobject_set_name(&slot->kobj, "%s", slot->name);
+ kobj_set_kset_s(slot, pci_hotplug_slots_subsys);
+
+ /* this can fail if we have already registered a slot with the same name */
+ if (kobject_register(&slot->kobj)) {
+ err("Unable to register kobject");
+ return -EINVAL;
+ }
+
+ list_add (&slot->slot_list, &pci_hotplug_slot_list);
+
+ result = fs_add_slot (slot);
+ dbg ("Added slot %s to the list\n", slot->name);
+ return result;
+}
+
+/**
+ * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
+ * @slot: pointer to the &struct hotplug_slot to deregister
+ *
+ * The @slot must have been registered with the pci hotplug subsystem
+ * previously with a call to pci_hp_register().
+ *
+ * Returns 0 if successful, anything else for an error.
+ */
+int pci_hp_deregister (struct hotplug_slot *slot)
+{
+ struct hotplug_slot *temp;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ temp = get_slot_from_name (slot->name);
+ if (temp != slot) {
+ return -ENODEV;
+ }
+ list_del (&slot->slot_list);
+
+ fs_remove_slot (slot);
+ dbg ("Removed slot %s from the list\n", slot->name);
+ kobject_unregister(&slot->kobj);
+ return 0;
+}
+
+/**
+ * pci_hp_change_slot_info - changes the slot's information structure in the core
+ * @slot: pointer to the slot whose info has changed
+ * @info: pointer to the info copy into the slot's info structure
+ *
+ * @slot must have been registered with the pci
+ * hotplug subsystem previously with a call to pci_hp_register().
+ *
+ * Returns 0 if successful, anything else for an error.
+ */
+int pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info *info)
+{
+ if ((slot == NULL) || (info == NULL))
+ return -ENODEV;
+
+ /*
+ * check all fields in the info structure, and update timestamps
+ * for the files referring to the fields that have now changed.
+ */
+ if ((has_power_file(slot) == 0) &&
+ (slot->info->power_status != info->power_status))
+ sysfs_update_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+
+ if ((has_attention_file(slot) == 0) &&
+ (slot->info->attention_status != info->attention_status))
+ sysfs_update_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
+
+ if ((has_latch_file(slot) == 0) &&
+ (slot->info->latch_status != info->latch_status))
+ sysfs_update_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
+
+ if ((has_adapter_file(slot) == 0) &&
+ (slot->info->adapter_status != info->adapter_status))
+ sysfs_update_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
+
+ if ((has_address_file(slot) == 0) &&
+ (slot->info->address != info->address))
+ sysfs_update_file(&slot->kobj, &hotplug_slot_attr_address.attr);
+
+ if ((has_max_bus_speed_file(slot) == 0) &&
+ (slot->info->max_bus_speed != info->max_bus_speed))
+ sysfs_update_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
+
+ if ((has_cur_bus_speed_file(slot) == 0) &&
+ (slot->info->cur_bus_speed != info->cur_bus_speed))
+ sysfs_update_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
+
+ memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
+
+ return 0;
+}
+
+static int __init pci_hotplug_init (void)
+{
+ int result;
+
+ kset_set_kset_s(&pci_hotplug_slots_subsys, pci_bus_type.subsys);
+ result = subsystem_register(&pci_hotplug_slots_subsys);
+ if (result) {
+ err("Register subsys with error %d\n", result);
+ goto exit;
+ }
+ result = cpci_hotplug_init(debug);
+ if (result) {
+ err ("cpci_hotplug_init with error %d\n", result);
+ goto err_subsys;
+ }
+
+ info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
+ goto exit;
+
+err_subsys:
+ subsystem_unregister(&pci_hotplug_slots_subsys);
+exit:
+ return result;
+}
+
+static void __exit pci_hotplug_exit (void)
+{
+ cpci_hotplug_exit();
+ subsystem_unregister(&pci_hotplug_slots_subsys);
+}
+
+module_init(pci_hotplug_init);
+module_exit(pci_hotplug_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+
+EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys);
+EXPORT_SYMBOL_GPL(pci_hp_register);
+EXPORT_SYMBOL_GPL(pci_hp_deregister);
+EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
new file mode 100644
index 000000000000..f313121d5141
--- /dev/null
+++ b/drivers/pci/hotplug/pciehp.h
@@ -0,0 +1,352 @@
+/*
+ * PCI Express Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+#ifndef _PCIEHP_H
+#define _PCIEHP_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <asm/semaphore.h>
+#include <asm/io.h>
+#include <linux/pcieport_if.h>
+#include "pci_hotplug.h"
+
+#define MY_NAME "pciehp"
+
+extern int pciehp_poll_mode;
+extern int pciehp_poll_time;
+extern int pciehp_debug;
+
+/*#define dbg(format, arg...) do { if (pciehp_debug) printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); } while (0)*/
+#define dbg(format, arg...) do { if (pciehp_debug) printk("%s: " format, 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)
+
+struct pci_func {
+ struct pci_func *next;
+ u8 bus;
+ u8 device;
+ u8 function;
+ u8 is_a_board;
+ u16 status;
+ u8 configured;
+ u8 switch_save;
+ u8 presence_save;
+ u32 base_length[0x06];
+ u8 base_type[0x06];
+ u16 reserved2;
+ u32 config_space[0x20];
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ struct pci_dev* pci_dev;
+};
+
+struct slot {
+ struct slot *next;
+ u8 bus;
+ u8 device;
+ u32 number;
+ u8 is_a_board;
+ u8 configured;
+ u8 state;
+ u8 switch_save;
+ u8 presence_save;
+ u32 capabilities;
+ u16 reserved2;
+ struct timer_list task_event;
+ u8 hp_slot;
+ struct controller *ctrl;
+ struct hpc_ops *hpc_ops;
+ struct hotplug_slot *hotplug_slot;
+ struct list_head slot_list;
+};
+
+struct pci_resource {
+ struct pci_resource * next;
+ u32 base;
+ u32 length;
+};
+
+struct event_info {
+ u32 event_type;
+ u8 hp_slot;
+};
+
+struct controller {
+ struct controller *next;
+ struct semaphore crit_sect; /* critical section semaphore */
+ void *hpc_ctlr_handle; /* HPC controller handle */
+ int num_slots; /* Number of slots on ctlr */
+ int slot_num_inc; /* 1 or -1 */
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ struct pci_dev *pci_dev;
+ struct pci_bus *pci_bus;
+ struct event_info event_queue[10];
+ struct slot *slot;
+ struct hpc_ops *hpc_ops;
+ wait_queue_head_t queue; /* sleep & wake process */
+ u8 next_event;
+ u8 seg;
+ u8 bus;
+ u8 device;
+ u8 function;
+ u8 rev;
+ u8 slot_device_offset;
+ u8 add_support;
+ enum pci_bus_speed speed;
+ u32 first_slot; /* First physical slot number */ /* PCIE only has 1 slot */
+ u8 slot_bus; /* Bus where the slots handled by this controller sit */
+ u8 ctrlcap;
+ u16 vendor_id;
+};
+
+struct irq_mapping {
+ u8 barber_pole;
+ u8 valid_INT;
+ u8 interrupt[4];
+};
+
+struct resource_lists {
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ struct irq_mapping *irqs;
+};
+
+#define INT_BUTTON_IGNORE 0
+#define INT_PRESENCE_ON 1
+#define INT_PRESENCE_OFF 2
+#define INT_SWITCH_CLOSE 3
+#define INT_SWITCH_OPEN 4
+#define INT_POWER_FAULT 5
+#define INT_POWER_FAULT_CLEAR 6
+#define INT_BUTTON_PRESS 7
+#define INT_BUTTON_RELEASE 8
+#define INT_BUTTON_CANCEL 9
+
+#define STATIC_STATE 0
+#define BLINKINGON_STATE 1
+#define BLINKINGOFF_STATE 2
+#define POWERON_STATE 3
+#define POWEROFF_STATE 4
+
+#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400
+
+/* Error messages */
+#define INTERLOCK_OPEN 0x00000002
+#define ADD_NOT_SUPPORTED 0x00000003
+#define CARD_FUNCTIONING 0x00000005
+#define ADAPTER_NOT_SAME 0x00000006
+#define NO_ADAPTER_PRESENT 0x00000009
+#define NOT_ENOUGH_RESOURCES 0x0000000B
+#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C
+#define WRONG_BUS_FREQUENCY 0x0000000D
+#define POWER_FAILURE 0x0000000E
+
+#define REMOVE_NOT_SUPPORTED 0x00000003
+
+#define DISABLE_CARD 1
+
+/* Field definitions in Slot Capabilities Register */
+#define ATTN_BUTTN_PRSN 0x00000001
+#define PWR_CTRL_PRSN 0x00000002
+#define MRL_SENS_PRSN 0x00000004
+#define ATTN_LED_PRSN 0x00000008
+#define PWR_LED_PRSN 0x00000010
+#define HP_SUPR_RM_SUP 0x00000020
+
+#define ATTN_BUTTN(cap) (cap & ATTN_BUTTN_PRSN)
+#define POWER_CTRL(cap) (cap & PWR_CTRL_PRSN)
+#define MRL_SENS(cap) (cap & MRL_SENS_PRSN)
+#define ATTN_LED(cap) (cap & ATTN_LED_PRSN)
+#define PWR_LED(cap) (cap & PWR_LED_PRSN)
+#define HP_SUPR_RM(cap) (cap & HP_SUPR_RM_SUP)
+
+/*
+ * error Messages
+ */
+#define msg_initialization_err "Initialization failure, error=%d\n"
+#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n"
+#define msg_HPC_non_pcie "The PCI hot plug controller is not supported by this driver.\n"
+#define msg_HPC_not_supported "This system is not supported by this version of pciephd module. Upgrade to a newer version of pciehpd\n"
+#define msg_unable_to_save "Unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n"
+#define msg_button_on "PCI slot #%d - powering on due to button press.\n"
+#define msg_button_off "PCI slot #%d - powering off due to button press.\n"
+#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n"
+#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n"
+
+/* controller functions */
+extern int pciehprm_find_available_resources (struct controller *ctrl);
+extern int pciehp_event_start_thread (void);
+extern void pciehp_event_stop_thread (void);
+extern struct pci_func *pciehp_slot_create (unsigned char busnumber);
+extern struct pci_func *pciehp_slot_find (unsigned char bus, unsigned char device, unsigned char index);
+extern int pciehp_enable_slot (struct slot *slot);
+extern int pciehp_disable_slot (struct slot *slot);
+
+extern u8 pciehp_handle_attention_button (u8 hp_slot, void *inst_id);
+extern u8 pciehp_handle_switch_change (u8 hp_slot, void *inst_id);
+extern u8 pciehp_handle_presence_change (u8 hp_slot, void *inst_id);
+extern u8 pciehp_handle_power_fault (u8 hp_slot, void *inst_id);
+/* extern void long_delay (int delay); */
+
+/* resource functions */
+extern int pciehp_resource_sort_and_combine (struct pci_resource **head);
+
+/* pci functions */
+extern int pciehp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
+/*extern int pciehp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, struct slot *slot);*/
+extern int pciehp_save_config (struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num);
+extern int pciehp_save_used_resources (struct controller *ctrl, struct pci_func * func, int flag);
+extern int pciehp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot);
+extern void pciehp_destroy_board_resources (struct pci_func * func);
+extern int pciehp_return_board_resources (struct pci_func * func, struct resource_lists * resources);
+extern void pciehp_destroy_resource_list (struct resource_lists * resources);
+extern int pciehp_configure_device (struct controller* ctrl, struct pci_func* func);
+extern int pciehp_unconfigure_device (struct pci_func* func);
+
+
+/* Global variables */
+extern struct controller *pciehp_ctrl_list;
+extern struct pci_func *pciehp_slot_list[256];
+
+/* Inline functions */
+
+static inline struct slot *pciehp_find_slot(struct controller *ctrl, u8 device)
+{
+ struct slot *p_slot, *tmp_slot = NULL;
+
+ p_slot = ctrl->slot;
+
+ dbg("p_slot = %p\n", p_slot);
+
+ while (p_slot && (p_slot->device != device)) {
+ tmp_slot = p_slot;
+ p_slot = p_slot->next;
+ dbg("In while loop, p_slot = %p\n", p_slot);
+ }
+ if (p_slot == NULL) {
+ err("ERROR: pciehp_find_slot device=0x%x\n", device);
+ p_slot = tmp_slot;
+ }
+
+ return p_slot;
+}
+
+static inline int wait_for_ctrl_irq(struct controller *ctrl)
+{
+ int retval = 0;
+
+ DECLARE_WAITQUEUE(wait, current);
+
+ dbg("%s : start\n", __FUNCTION__);
+ add_wait_queue(&ctrl->queue, &wait);
+ if (!pciehp_poll_mode)
+ /* Sleep for up to 1 second */
+ msleep_interruptible(1000);
+ else
+ msleep_interruptible(2500);
+
+ remove_wait_queue(&ctrl->queue, &wait);
+ if (signal_pending(current))
+ retval = -EINTR;
+
+ dbg("%s : end\n", __FUNCTION__);
+ return retval;
+}
+
+/* Puts node back in the resource list pointed to by head */
+static inline void return_resource(struct pci_resource **head, struct pci_resource *node)
+{
+ if (!node || !head)
+ return;
+ node->next = *head;
+ *head = node;
+}
+
+#define SLOT_NAME_SIZE 10
+
+static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot)
+{
+ snprintf(buffer, buffer_size, "%d", slot->number);
+}
+
+enum php_ctlr_type {
+ PCI,
+ ISA,
+ ACPI
+};
+
+typedef u8(*php_intr_callback_t) (unsigned int change_id, void *instance_id);
+
+int pcie_init(struct controller *ctrl, struct pcie_device *dev,
+ php_intr_callback_t attention_button_callback,
+ php_intr_callback_t switch_change_callback,
+ php_intr_callback_t presence_change_callback,
+ php_intr_callback_t power_fault_callback);
+
+
+/* This has no meaning for PCI Express, as there is only 1 slot per port */
+int pcie_get_ctlr_slot_config(struct controller *ctrl,
+ int *num_ctlr_slots,
+ int *first_device_num,
+ int *physical_slot_num,
+ u8 *ctrlcap);
+
+struct hpc_ops {
+ int (*power_on_slot) (struct slot *slot);
+ int (*power_off_slot) (struct slot *slot);
+ int (*get_power_status) (struct slot *slot, u8 *status);
+ int (*get_attention_status) (struct slot *slot, u8 *status);
+ int (*set_attention_status) (struct slot *slot, u8 status);
+ int (*get_latch_status) (struct slot *slot, u8 *status);
+ int (*get_adapter_status) (struct slot *slot, u8 *status);
+
+ int (*get_max_bus_speed) (struct slot *slot, enum pci_bus_speed *speed);
+ int (*get_cur_bus_speed) (struct slot *slot, enum pci_bus_speed *speed);
+
+ int (*get_max_lnk_width) (struct slot *slot, enum pcie_link_width *value);
+ int (*get_cur_lnk_width) (struct slot *slot, enum pcie_link_width *value);
+
+ int (*query_power_fault) (struct slot *slot);
+ void (*green_led_on) (struct slot *slot);
+ void (*green_led_off) (struct slot *slot);
+ void (*green_led_blink) (struct slot *slot);
+ void (*release_ctlr) (struct controller *ctrl);
+ int (*check_lnk_status) (struct controller *ctrl);
+};
+
+#endif /* _PCIEHP_H */
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
new file mode 100644
index 000000000000..8a5b2b51bde4
--- /dev/null
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -0,0 +1,662 @@
+/*
+ * PCI Express Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include "pciehp.h"
+#include "pciehprm.h"
+#include <linux/interrupt.h>
+
+/* Global variables */
+int pciehp_debug;
+int pciehp_poll_mode;
+int pciehp_poll_time;
+struct controller *pciehp_ctrl_list;
+struct pci_func *pciehp_slot_list[256];
+
+#define DRIVER_VERSION "0.4"
+#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
+#define DRIVER_DESC "PCI Express Hot Plug Controller Driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(pciehp_debug, bool, 0644);
+module_param(pciehp_poll_mode, bool, 0644);
+module_param(pciehp_poll_time, int, 0644);
+MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
+MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
+MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
+
+#define PCIE_MODULE_NAME "pciehp"
+
+static int pcie_start_thread (void);
+static int set_attention_status (struct hotplug_slot *slot, u8 value);
+static int enable_slot (struct hotplug_slot *slot);
+static int disable_slot (struct hotplug_slot *slot);
+static int get_power_status (struct hotplug_slot *slot, u8 *value);
+static int get_attention_status (struct hotplug_slot *slot, u8 *value);
+static int get_latch_status (struct hotplug_slot *slot, u8 *value);
+static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
+static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
+static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
+
+static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .set_attention_status = set_attention_status,
+ .enable_slot = enable_slot,
+ .disable_slot = disable_slot,
+ .get_power_status = get_power_status,
+ .get_attention_status = get_attention_status,
+ .get_latch_status = get_latch_status,
+ .get_adapter_status = get_adapter_status,
+ .get_max_bus_speed = get_max_bus_speed,
+ .get_cur_bus_speed = get_cur_bus_speed,
+};
+
+static int init_slots(struct controller *ctrl)
+{
+ struct slot *new_slot;
+ u8 number_of_slots;
+ u8 slot_device;
+ u32 slot_number;
+ int result = -ENOMEM;
+
+ dbg("%s\n",__FUNCTION__);
+
+ number_of_slots = ctrl->num_slots;
+ slot_device = ctrl->slot_device_offset;
+ slot_number = ctrl->first_slot;
+
+ while (number_of_slots) {
+ new_slot = kmalloc(sizeof(*new_slot), GFP_KERNEL);
+ if (!new_slot)
+ goto error;
+
+ memset(new_slot, 0, sizeof(struct slot));
+ new_slot->hotplug_slot =
+ kmalloc(sizeof(*(new_slot->hotplug_slot)),
+ GFP_KERNEL);
+ if (!new_slot->hotplug_slot)
+ goto error_slot;
+ memset(new_slot->hotplug_slot, 0, sizeof(struct hotplug_slot));
+
+ new_slot->hotplug_slot->info =
+ kmalloc(sizeof(*(new_slot->hotplug_slot->info)),
+ GFP_KERNEL);
+ if (!new_slot->hotplug_slot->info)
+ goto error_hpslot;
+ memset(new_slot->hotplug_slot->info, 0,
+ sizeof(struct hotplug_slot_info));
+ new_slot->hotplug_slot->name = kmalloc(SLOT_NAME_SIZE,
+ GFP_KERNEL);
+ if (!new_slot->hotplug_slot->name)
+ goto error_info;
+
+ new_slot->ctrl = ctrl;
+ new_slot->bus = ctrl->slot_bus;
+ new_slot->device = slot_device;
+ new_slot->hpc_ops = ctrl->hpc_ops;
+
+ new_slot->number = ctrl->first_slot;
+ new_slot->hp_slot = slot_device - ctrl->slot_device_offset;
+
+ /* register this slot with the hotplug pci core */
+ new_slot->hotplug_slot->private = new_slot;
+ make_slot_name (new_slot->hotplug_slot->name, SLOT_NAME_SIZE, new_slot);
+ new_slot->hotplug_slot->ops = &pciehp_hotplug_slot_ops;
+
+ new_slot->hpc_ops->get_power_status(new_slot, &(new_slot->hotplug_slot->info->power_status));
+ new_slot->hpc_ops->get_attention_status(new_slot, &(new_slot->hotplug_slot->info->attention_status));
+ new_slot->hpc_ops->get_latch_status(new_slot, &(new_slot->hotplug_slot->info->latch_status));
+ new_slot->hpc_ops->get_adapter_status(new_slot, &(new_slot->hotplug_slot->info->adapter_status));
+
+ dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x slot_device_offset=%x\n",
+ new_slot->bus, new_slot->device, new_slot->hp_slot, new_slot->number, ctrl->slot_device_offset);
+ result = pci_hp_register (new_slot->hotplug_slot);
+ if (result) {
+ err ("pci_hp_register failed with error %d\n", result);
+ goto error_name;
+ }
+
+ new_slot->next = ctrl->slot;
+ ctrl->slot = new_slot;
+
+ number_of_slots--;
+ slot_device++;
+ slot_number += ctrl->slot_num_inc;
+ }
+
+ return 0;
+
+error_name:
+ kfree(new_slot->hotplug_slot->name);
+error_info:
+ kfree(new_slot->hotplug_slot->info);
+error_hpslot:
+ kfree(new_slot->hotplug_slot);
+error_slot:
+ kfree(new_slot);
+error:
+ return result;
+}
+
+
+static int cleanup_slots (struct controller * ctrl)
+{
+ struct slot *old_slot, *next_slot;
+
+ old_slot = ctrl->slot;
+ ctrl->slot = NULL;
+
+ while (old_slot) {
+ next_slot = old_slot->next;
+ pci_hp_deregister (old_slot->hotplug_slot);
+ kfree(old_slot->hotplug_slot->info);
+ kfree(old_slot->hotplug_slot->name);
+ kfree(old_slot->hotplug_slot);
+ kfree(old_slot);
+ old_slot = next_slot;
+ }
+
+
+ return(0);
+}
+
+static int get_ctlr_slot_config(struct controller *ctrl)
+{
+ int num_ctlr_slots; /* Not needed; PCI Express has 1 slot per port*/
+ int first_device_num; /* Not needed */
+ int physical_slot_num;
+ u8 ctrlcap;
+ int rc;
+
+ rc = pcie_get_ctlr_slot_config(ctrl, &num_ctlr_slots, &first_device_num, &physical_slot_num, &ctrlcap);
+ if (rc) {
+ err("%s: get_ctlr_slot_config fail for b:d (%x:%x)\n", __FUNCTION__, ctrl->bus, ctrl->device);
+ return (-1);
+ }
+
+ ctrl->num_slots = num_ctlr_slots; /* PCI Express has 1 slot per port */
+ ctrl->slot_device_offset = first_device_num;
+ ctrl->first_slot = physical_slot_num;
+ ctrl->ctrlcap = ctrlcap;
+
+ dbg("%s: bus(0x%x) num_slot(0x%x) 1st_dev(0x%x) psn(0x%x) ctrlcap(%x) for b:d (%x:%x)\n",
+ __FUNCTION__, ctrl->slot_bus, num_ctlr_slots, first_device_num, physical_slot_num, ctrlcap,
+ ctrl->bus, ctrl->device);
+
+ return (0);
+}
+
+
+/*
+ * set_attention_status - Turns the Amber LED for a slot on, off or blink
+ */
+static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ hotplug_slot->info->attention_status = status;
+
+ if (ATTN_LED(slot->ctrl->ctrlcap))
+ slot->hpc_ops->set_attention_status(slot, status);
+
+ return 0;
+}
+
+
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ return pciehp_enable_slot(slot);
+}
+
+
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ return pciehp_disable_slot(slot);
+}
+
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ retval = slot->hpc_ops->get_power_status(slot, value);
+ if (retval < 0)
+ *value = hotplug_slot->info->power_status;
+
+ return 0;
+}
+
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ retval = slot->hpc_ops->get_attention_status(slot, value);
+ if (retval < 0)
+ *value = hotplug_slot->info->attention_status;
+
+ return 0;
+}
+
+static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ retval = slot->hpc_ops->get_latch_status(slot, value);
+ if (retval < 0)
+ *value = hotplug_slot->info->latch_status;
+
+ return 0;
+}
+
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ retval = slot->hpc_ops->get_adapter_status(slot, value);
+ if (retval < 0)
+ *value = hotplug_slot->info->adapter_status;
+
+ return 0;
+}
+
+static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ retval = slot->hpc_ops->get_max_bus_speed(slot, value);
+ if (retval < 0)
+ *value = PCI_SPEED_UNKNOWN;
+
+ return 0;
+}
+
+static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ retval = slot->hpc_ops->get_cur_bus_speed(slot, value);
+ if (retval < 0)
+ *value = PCI_SPEED_UNKNOWN;
+
+ return 0;
+}
+
+static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_id *id)
+{
+ int rc;
+ struct controller *ctrl;
+ struct slot *t_slot;
+ int first_device_num = 0 ; /* first PCI device number supported by this PCIE */
+ int num_ctlr_slots; /* number of slots supported by this HPC */
+ u8 value;
+ struct pci_dev *pdev;
+
+ dbg("%s: Called by hp_drv\n", __FUNCTION__);
+ ctrl = kmalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl) {
+ err("%s : out of memory\n", __FUNCTION__);
+ goto err_out_none;
+ }
+ memset(ctrl, 0, sizeof(struct controller));
+
+ dbg("%s: DRV_thread pid = %d\n", __FUNCTION__, current->pid);
+
+ pdev = dev->port;
+
+ rc = pcie_init(ctrl, dev,
+ (php_intr_callback_t) pciehp_handle_attention_button,
+ (php_intr_callback_t) pciehp_handle_switch_change,
+ (php_intr_callback_t) pciehp_handle_presence_change,
+ (php_intr_callback_t) pciehp_handle_power_fault);
+ if (rc) {
+ dbg("%s: controller initialization failed\n", PCIE_MODULE_NAME);
+ goto err_out_free_ctrl;
+ }
+
+ ctrl->pci_dev = pdev;
+
+ pci_set_drvdata(pdev, ctrl);
+
+ ctrl->pci_bus = kmalloc(sizeof(*ctrl->pci_bus), GFP_KERNEL);
+ if (!ctrl->pci_bus) {
+ err("%s: out of memory\n", __FUNCTION__);
+ rc = -ENOMEM;
+ goto err_out_unmap_mmio_region;
+ }
+ dbg("%s: ctrl->pci_bus %p\n", __FUNCTION__, ctrl->pci_bus);
+ memcpy (ctrl->pci_bus, pdev->bus, sizeof (*ctrl->pci_bus));
+ ctrl->bus = pdev->bus->number; /* ctrl bus */
+ ctrl->slot_bus = pdev->subordinate->number; /* bus controlled by this HPC */
+
+ ctrl->device = PCI_SLOT(pdev->devfn);
+ ctrl->function = PCI_FUNC(pdev->devfn);
+ dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", __FUNCTION__,
+ ctrl->bus, ctrl->device, ctrl->function, pdev->irq);
+
+ /*
+ * Save configuration headers for this and subordinate PCI buses
+ */
+
+ rc = get_ctlr_slot_config(ctrl);
+ if (rc) {
+ err(msg_initialization_err, rc);
+ goto err_out_free_ctrl_bus;
+ }
+ first_device_num = ctrl->slot_device_offset;
+ num_ctlr_slots = ctrl->num_slots;
+
+ /* Store PCI Config Space for all devices on this bus */
+ dbg("%s: Before calling pciehp_save_config, ctrl->bus %x,ctrl->slot_bus %x\n",
+ __FUNCTION__,ctrl->bus, ctrl->slot_bus);
+ rc = pciehp_save_config(ctrl, ctrl->slot_bus, num_ctlr_slots, first_device_num);
+ if (rc) {
+ err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc);
+ goto err_out_free_ctrl_bus;
+ }
+
+ /* Get IO, memory, and IRQ resources for new devices */
+ rc = pciehprm_find_available_resources(ctrl);
+ ctrl->add_support = !rc;
+
+ if (rc) {
+ dbg("pciehprm_find_available_resources = %#x\n", rc);
+ err("unable to locate PCI configuration resources for hot plug add.\n");
+ goto err_out_free_ctrl_bus;
+ }
+
+ /* Setup the slot information structures */
+ rc = init_slots(ctrl);
+ if (rc) {
+ err(msg_initialization_err, 6);
+ goto err_out_free_ctrl_slot;
+ }
+
+ t_slot = pciehp_find_slot(ctrl, first_device_num);
+ dbg("%s: t_slot %p\n", __FUNCTION__, t_slot);
+
+ /* Finish setting up the hot plug ctrl device */
+ ctrl->next_event = 0;
+
+ if (!pciehp_ctrl_list) {
+ pciehp_ctrl_list = ctrl;
+ ctrl->next = NULL;
+ } else {
+ ctrl->next = pciehp_ctrl_list;
+ pciehp_ctrl_list = ctrl;
+ }
+
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ t_slot->hpc_ops->get_adapter_status(t_slot, &value); /* Check if slot is occupied */
+ dbg("%s: adpater value %x\n", __FUNCTION__, value);
+
+ if ((POWER_CTRL(ctrl->ctrlcap)) && !value) {
+ rc = t_slot->hpc_ops->power_off_slot(t_slot); /* Power off slot if not occupied*/
+ if (rc) {
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ goto err_out_free_ctrl_slot;
+ } else
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+ }
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+
+ return 0;
+
+err_out_free_ctrl_slot:
+ cleanup_slots(ctrl);
+err_out_free_ctrl_bus:
+ kfree(ctrl->pci_bus);
+err_out_unmap_mmio_region:
+ ctrl->hpc_ops->release_ctlr(ctrl);
+err_out_free_ctrl:
+ kfree(ctrl);
+err_out_none:
+ return -ENODEV;
+}
+
+
+static int pcie_start_thread(void)
+{
+ int loop;
+ int retval = 0;
+
+ dbg("Initialize + Start the notification/polling mechanism \n");
+
+ retval = pciehp_event_start_thread();
+ if (retval) {
+ dbg("pciehp_event_start_thread() failed\n");
+ return retval;
+ }
+
+ dbg("Initialize slot lists\n");
+ /* One slot list for each bus in the system */
+ for (loop = 0; loop < 256; loop++) {
+ pciehp_slot_list[loop] = NULL;
+ }
+
+ return retval;
+}
+
+static inline void __exit
+free_pciehp_res(struct pci_resource *res)
+{
+ struct pci_resource *tres;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+}
+
+static void __exit unload_pciehpd(void)
+{
+ struct pci_func *next;
+ struct pci_func *TempSlot;
+ int loop;
+ struct controller *ctrl;
+ struct controller *tctrl;
+
+ ctrl = pciehp_ctrl_list;
+
+ while (ctrl) {
+ cleanup_slots(ctrl);
+
+ free_pciehp_res(ctrl->io_head);
+ free_pciehp_res(ctrl->mem_head);
+ free_pciehp_res(ctrl->p_mem_head);
+ free_pciehp_res(ctrl->bus_head);
+
+ kfree (ctrl->pci_bus);
+
+ ctrl->hpc_ops->release_ctlr(ctrl);
+
+ tctrl = ctrl;
+ ctrl = ctrl->next;
+
+ kfree(tctrl);
+ }
+
+ for (loop = 0; loop < 256; loop++) {
+ next = pciehp_slot_list[loop];
+ while (next != NULL) {
+ free_pciehp_res(next->io_head);
+ free_pciehp_res(next->mem_head);
+ free_pciehp_res(next->p_mem_head);
+ free_pciehp_res(next->bus_head);
+
+ TempSlot = next;
+ next = next->next;
+ kfree(TempSlot);
+ }
+ }
+
+ /* Stop the notification mechanism */
+ pciehp_event_stop_thread();
+
+}
+
+int hpdriver_context = 0;
+
+static void pciehp_remove (struct pcie_device *device)
+{
+ printk("%s ENTRY\n", __FUNCTION__);
+ printk("%s -> Call free_irq for irq = %d\n",
+ __FUNCTION__, device->irq);
+ free_irq(device->irq, &hpdriver_context);
+}
+
+#ifdef CONFIG_PM
+static int pciehp_suspend (struct pcie_device *dev, u32 state)
+{
+ printk("%s ENTRY\n", __FUNCTION__);
+ return 0;
+}
+
+static int pciehp_resume (struct pcie_device *dev)
+{
+ printk("%s ENTRY\n", __FUNCTION__);
+ return 0;
+}
+#endif
+
+static struct pcie_port_service_id port_pci_ids[] = { {
+ .vendor = PCI_ANY_ID,
+ .device = PCI_ANY_ID,
+ .port_type = PCIE_RC_PORT,
+ .service_type = PCIE_PORT_SERVICE_HP,
+ .driver_data = 0,
+ }, { /* end: all zeroes */ }
+};
+static const char device_name[] = "hpdriver";
+
+static struct pcie_port_service_driver hpdriver_portdrv = {
+ .name = (char *)device_name,
+ .id_table = &port_pci_ids[0],
+
+ .probe = pciehp_probe,
+ .remove = pciehp_remove,
+
+#ifdef CONFIG_PM
+ .suspend = pciehp_suspend,
+ .resume = pciehp_resume,
+#endif /* PM */
+};
+
+static int __init pcied_init(void)
+{
+ int retval = 0;
+
+#ifdef CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE
+ pciehp_poll_mode = 1;
+#endif
+
+ retval = pcie_start_thread();
+ if (retval)
+ goto error_hpc_init;
+
+ retval = pciehprm_init(PCI);
+ if (!retval) {
+ retval = pcie_port_service_register(&hpdriver_portdrv);
+ dbg("pcie_port_service_register = %d\n", retval);
+ info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+ if (retval)
+ dbg("%s: Failure to register service\n", __FUNCTION__);
+ }
+
+error_hpc_init:
+ if (retval) {
+ pciehprm_cleanup();
+ pciehp_event_stop_thread();
+ } else
+ pciehprm_print_pirt();
+
+ return retval;
+}
+
+static void __exit pcied_cleanup(void)
+{
+ dbg("unload_pciehpd()\n");
+ unload_pciehpd();
+
+ pciehprm_cleanup();
+
+ dbg("pcie_port_service_unregister\n");
+ pcie_port_service_unregister(&hpdriver_portdrv);
+
+ info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
+}
+
+module_init(pcied_init);
+module_exit(pcied_cleanup);
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
new file mode 100644
index 000000000000..0dbcf04aa35e
--- /dev/null
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -0,0 +1,2706 @@
+/*
+ * PCI Express Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/smp_lock.h>
+#include <linux/pci.h>
+#include "../pci.h"
+#include "pciehp.h"
+#include "pciehprm.h"
+
+static u32 configure_new_device(struct controller *ctrl, struct pci_func *func,
+ u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev);
+static int configure_new_function( struct controller *ctrl, struct pci_func *func,
+ u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev);
+static void interrupt_event_handler(struct controller *ctrl);
+
+static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */
+static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */
+static int event_finished;
+static unsigned long pushbutton_pending; /* = 0 */
+static unsigned long surprise_rm_pending; /* = 0 */
+
+u8 pciehp_handle_attention_button(u8 hp_slot, void *inst_id)
+{
+ struct controller *ctrl = (struct controller *) inst_id;
+ struct slot *p_slot;
+ u8 rc = 0;
+ u8 getstatus;
+ struct pci_func *func;
+ struct event_info *taskInfo;
+
+ /* Attention Button Change */
+ dbg("pciehp: Attention button interrupt received.\n");
+
+ func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
+
+ /* This is the structure that tells the worker thread what to do */
+ taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+ p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
+ p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+
+ ctrl->next_event = (ctrl->next_event + 1) % 10;
+ taskInfo->hp_slot = hp_slot;
+
+ rc++;
+
+ /*
+ * Button pressed - See if need to TAKE ACTION!!!
+ */
+ info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ taskInfo->event_type = INT_BUTTON_PRESS;
+
+ if ((p_slot->state == BLINKINGON_STATE)
+ || (p_slot->state == BLINKINGOFF_STATE)) {
+ /* Cancel if we are still blinking; this means that we press the
+ * attention again before the 5 sec. limit expires to cancel hot-add
+ * or hot-remove
+ */
+ taskInfo->event_type = INT_BUTTON_CANCEL;
+ info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ } else if ((p_slot->state == POWERON_STATE)
+ || (p_slot->state == POWEROFF_STATE)) {
+ /* Ignore if the slot is on power-on or power-off state; this
+ * means that the previous attention button action to hot-add or
+ * hot-remove is undergoing
+ */
+ taskInfo->event_type = INT_BUTTON_IGNORE;
+ info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ }
+
+ if (rc)
+ up(&event_semaphore); /* signal event thread that new event is posted */
+
+ return 0;
+
+}
+
+u8 pciehp_handle_switch_change(u8 hp_slot, void *inst_id)
+{
+ struct controller *ctrl = (struct controller *) inst_id;
+ struct slot *p_slot;
+ u8 rc = 0;
+ u8 getstatus;
+ struct pci_func *func;
+ struct event_info *taskInfo;
+
+ /* Switch Change */
+ dbg("pciehp: Switch interrupt received.\n");
+
+ func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
+
+ /* This is the structure that tells the worker thread
+ * what to do
+ */
+ taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+ ctrl->next_event = (ctrl->next_event + 1) % 10;
+ taskInfo->hp_slot = hp_slot;
+
+ rc++;
+ p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+ p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
+ p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+
+ if (getstatus) {
+ /*
+ * Switch opened
+ */
+ info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ func->switch_save = 0;
+ taskInfo->event_type = INT_SWITCH_OPEN;
+ } else {
+ /*
+ * Switch closed
+ */
+ info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ func->switch_save = 0x10;
+ taskInfo->event_type = INT_SWITCH_CLOSE;
+ }
+
+ if (rc)
+ up(&event_semaphore); /* signal event thread that new event is posted */
+
+ return rc;
+}
+
+u8 pciehp_handle_presence_change(u8 hp_slot, void *inst_id)
+{
+ struct controller *ctrl = (struct controller *) inst_id;
+ struct slot *p_slot;
+ u8 rc = 0;
+ struct pci_func *func;
+ struct event_info *taskInfo;
+
+ /* Presence Change */
+ dbg("pciehp: Presence/Notify input change.\n");
+
+ func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
+
+ /* This is the structure that tells the worker thread
+ * what to do
+ */
+ taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+ ctrl->next_event = (ctrl->next_event + 1) % 10;
+ taskInfo->hp_slot = hp_slot;
+
+ rc++;
+ p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ /* Switch is open, assume a presence change
+ * Save the presence state
+ */
+ p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
+ if (func->presence_save) {
+ /*
+ * Card Present
+ */
+ info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ taskInfo->event_type = INT_PRESENCE_ON;
+ } else {
+ /*
+ * Not Present
+ */
+ info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ taskInfo->event_type = INT_PRESENCE_OFF;
+ }
+
+ if (rc)
+ up(&event_semaphore); /* signal event thread that new event is posted */
+
+ return rc;
+}
+
+u8 pciehp_handle_power_fault(u8 hp_slot, void *inst_id)
+{
+ struct controller *ctrl = (struct controller *) inst_id;
+ struct slot *p_slot;
+ u8 rc = 0;
+ struct pci_func *func;
+ struct event_info *taskInfo;
+
+ /* power fault */
+ dbg("pciehp: Power fault interrupt received.\n");
+
+ func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
+
+ /* this is the structure that tells the worker thread
+ * what to do
+ */
+ taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+ ctrl->next_event = (ctrl->next_event + 1) % 10;
+ taskInfo->hp_slot = hp_slot;
+
+ rc++;
+ p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
+ /*
+ * power fault Cleared
+ */
+ info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ func->status = 0x00;
+ taskInfo->event_type = INT_POWER_FAULT_CLEAR;
+ } else {
+ /*
+ * power fault
+ */
+ info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ taskInfo->event_type = INT_POWER_FAULT;
+ /* set power fault status for this board */
+ func->status = 0xFF;
+ info("power fault bit %x set\n", hp_slot);
+ }
+ if (rc)
+ up(&event_semaphore); /* signal event thread that new event is posted */
+
+ return rc;
+}
+
+
+/**
+ * sort_by_size: sort nodes by their length, smallest first.
+ *
+ * @head: list to sort
+ */
+static int sort_by_size(struct pci_resource **head)
+{
+ struct pci_resource *current_res;
+ struct pci_resource *next_res;
+ int out_of_order = 1;
+
+ if (!(*head))
+ return 1;
+
+ if (!((*head)->next))
+ return 0;
+
+ while (out_of_order) {
+ out_of_order = 0;
+
+ /* Special case for swapping list head */
+ if (((*head)->next) &&
+ ((*head)->length > (*head)->next->length)) {
+ out_of_order++;
+ current_res = *head;
+ *head = (*head)->next;
+ current_res->next = (*head)->next;
+ (*head)->next = current_res;
+ }
+
+ current_res = *head;
+
+ while (current_res->next && current_res->next->next) {
+ if (current_res->next->length > current_res->next->next->length) {
+ out_of_order++;
+ next_res = current_res->next;
+ current_res->next = current_res->next->next;
+ current_res = current_res->next;
+ next_res->next = current_res->next;
+ current_res->next = next_res;
+ } else
+ current_res = current_res->next;
+ }
+ } /* End of out_of_order loop */
+
+ return 0;
+}
+
+
+/*
+ * sort_by_max_size
+ *
+ * Sorts nodes on the list by their length.
+ * Largest first.
+ *
+ */
+static int sort_by_max_size(struct pci_resource **head)
+{
+ struct pci_resource *current_res;
+ struct pci_resource *next_res;
+ int out_of_order = 1;
+
+ if (!(*head))
+ return 1;
+
+ if (!((*head)->next))
+ return 0;
+
+ while (out_of_order) {
+ out_of_order = 0;
+
+ /* Special case for swapping list head */
+ if (((*head)->next) &&
+ ((*head)->length < (*head)->next->length)) {
+ out_of_order++;
+ current_res = *head;
+ *head = (*head)->next;
+ current_res->next = (*head)->next;
+ (*head)->next = current_res;
+ }
+
+ current_res = *head;
+
+ while (current_res->next && current_res->next->next) {
+ if (current_res->next->length < current_res->next->next->length) {
+ out_of_order++;
+ next_res = current_res->next;
+ current_res->next = current_res->next->next;
+ current_res = current_res->next;
+ next_res->next = current_res->next;
+ current_res->next = next_res;
+ } else
+ current_res = current_res->next;
+ }
+ } /* End of out_of_order loop */
+
+ return 0;
+}
+
+
+/**
+ * do_pre_bridge_resource_split: return one unused resource node
+ * @head: list to scan
+ *
+ */
+static struct pci_resource *
+do_pre_bridge_resource_split(struct pci_resource **head,
+ struct pci_resource **orig_head, u32 alignment)
+{
+ struct pci_resource *prevnode = NULL;
+ struct pci_resource *node;
+ struct pci_resource *split_node;
+ u32 rc;
+ u32 temp_dword;
+ dbg("do_pre_bridge_resource_split\n");
+
+ if (!(*head) || !(*orig_head))
+ return NULL;
+
+ rc = pciehp_resource_sort_and_combine(head);
+
+ if (rc)
+ return NULL;
+
+ if ((*head)->base != (*orig_head)->base)
+ return NULL;
+
+ if ((*head)->length == (*orig_head)->length)
+ return NULL;
+
+
+ /* If we got here, there the bridge requires some of the resource, but
+ * we may be able to split some off of the front
+ */
+ node = *head;
+
+ if (node->length & (alignment -1)) {
+ /* this one isn't an aligned length, so we'll make a new entry
+ * and split it up.
+ */
+ split_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+
+ temp_dword = (node->length | (alignment-1)) + 1 - alignment;
+
+ split_node->base = node->base;
+ split_node->length = temp_dword;
+
+ node->length -= temp_dword;
+ node->base += split_node->length;
+
+ /* Put it in the list */
+ *head = split_node;
+ split_node->next = node;
+ }
+
+ if (node->length < alignment)
+ return NULL;
+
+ /* Now unlink it */
+ if (*head == node) {
+ *head = node->next;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ }
+ node->next = NULL;
+
+ return node;
+}
+
+
+/**
+ * do_bridge_resource_split: return one unused resource node
+ * @head: list to scan
+ *
+ */
+static struct pci_resource *
+do_bridge_resource_split(struct pci_resource **head, u32 alignment)
+{
+ struct pci_resource *prevnode = NULL;
+ struct pci_resource *node;
+ u32 rc;
+ u32 temp_dword;
+
+ if (!(*head))
+ return NULL;
+
+ rc = pciehp_resource_sort_and_combine(head);
+
+ if (rc)
+ return NULL;
+
+ node = *head;
+
+ while (node->next) {
+ prevnode = node;
+ node = node->next;
+ kfree(prevnode);
+ }
+
+ if (node->length < alignment) {
+ kfree(node);
+ return NULL;
+ }
+
+ if (node->base & (alignment - 1)) {
+ /* Short circuit if adjusted size is too small */
+ temp_dword = (node->base | (alignment-1)) + 1;
+ if ((node->length - (temp_dword - node->base)) < alignment) {
+ kfree(node);
+ return NULL;
+ }
+
+ node->length -= (temp_dword - node->base);
+ node->base = temp_dword;
+ }
+
+ if (node->length & (alignment - 1)) {
+ /* There's stuff in use after this node */
+ kfree(node);
+ return NULL;
+ }
+
+ return node;
+}
+
+
+/*
+ * get_io_resource
+ *
+ * this function sorts the resource list by size and then
+ * returns the first node of "size" length that is not in the
+ * ISA aliasing window. If it finds a node larger than "size"
+ * it will split it up.
+ *
+ * size must be a power of two.
+ */
+static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size)
+{
+ struct pci_resource *prevnode;
+ struct pci_resource *node;
+ struct pci_resource *split_node = NULL;
+ u32 temp_dword;
+
+ if (!(*head))
+ return NULL;
+
+ if ( pciehp_resource_sort_and_combine(head) )
+ return NULL;
+
+ if ( sort_by_size(head) )
+ return NULL;
+
+ for (node = *head; node; node = node->next) {
+ if (node->length < size)
+ continue;
+
+ if (node->base & (size - 1)) {
+ /* this one isn't base aligned properly
+ so we'll make a new entry and split it up */
+ temp_dword = (node->base | (size-1)) + 1;
+
+ /*/ Short circuit if adjusted size is too small */
+ if ((node->length - (temp_dword - node->base)) < size)
+ continue;
+
+ split_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+
+ split_node->base = node->base;
+ split_node->length = temp_dword - node->base;
+ node->base = temp_dword;
+ node->length -= split_node->length;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of non-aligned base */
+
+ /* Don't need to check if too small since we already did */
+ if (node->length > size) {
+ /* this one is longer than we need
+ so we'll make a new entry and split it up */
+ split_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+
+ split_node->base = node->base + size;
+ split_node->length = node->length - size;
+ node->length = size;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of too big on top end */
+
+ /* For IO make sure it's not in the ISA aliasing space */
+ if (node->base & 0x300L)
+ continue;
+
+ /* If we got here, then it is the right size
+ Now take it out of the list */
+ if (*head == node) {
+ *head = node->next;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ }
+ node->next = NULL;
+ /* Stop looping */
+ break;
+ }
+
+ return node;
+}
+
+
+/*
+ * get_max_resource
+ *
+ * Gets the largest node that is at least "size" big from the
+ * list pointed to by head. It aligns the node on top and bottom
+ * to "size" alignment before returning it.
+ * J.I. modified to put max size limits of; 64M->32M->16M->8M->4M->1M
+ * This is needed to avoid allocating entire ACPI _CRS res to one child bridge/slot.
+ */
+static struct pci_resource *get_max_resource(struct pci_resource **head, u32 size)
+{
+ struct pci_resource *max;
+ struct pci_resource *temp;
+ struct pci_resource *split_node;
+ u32 temp_dword;
+ u32 max_size[] = { 0x4000000, 0x2000000, 0x1000000, 0x0800000, 0x0400000, 0x0200000, 0x0100000, 0x00 };
+ int i;
+
+ if (!(*head))
+ return NULL;
+
+ if (pciehp_resource_sort_and_combine(head))
+ return NULL;
+
+ if (sort_by_max_size(head))
+ return NULL;
+
+ for (max = *head;max; max = max->next) {
+
+ /* If not big enough we could probably just bail,
+ instead we'll continue to the next. */
+ if (max->length < size)
+ continue;
+
+ if (max->base & (size - 1)) {
+ /* this one isn't base aligned properly
+ so we'll make a new entry and split it up */
+ temp_dword = (max->base | (size-1)) + 1;
+
+ /* Short circuit if adjusted size is too small */
+ if ((max->length - (temp_dword - max->base)) < size)
+ continue;
+
+ split_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+
+ split_node->base = max->base;
+ split_node->length = temp_dword - max->base;
+ max->base = temp_dword;
+ max->length -= split_node->length;
+
+ /* Put it next in the list */
+ split_node->next = max->next;
+ max->next = split_node;
+ }
+
+ if ((max->base + max->length) & (size - 1)) {
+ /* this one isn't end aligned properly at the top
+ so we'll make a new entry and split it up */
+ split_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+ temp_dword = ((max->base + max->length) & ~(size - 1));
+ split_node->base = temp_dword;
+ split_node->length = max->length + max->base
+ - split_node->base;
+ max->length -= split_node->length;
+
+ /* Put it in the list */
+ split_node->next = max->next;
+ max->next = split_node;
+ }
+
+ /* Make sure it didn't shrink too much when we aligned it */
+ if (max->length < size)
+ continue;
+
+ for ( i = 0; max_size[i] > size; i++) {
+ if (max->length > max_size[i]) {
+ split_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+ if (!split_node)
+ break; /* return NULL; */
+ split_node->base = max->base + max_size[i];
+ split_node->length = max->length - max_size[i];
+ max->length = max_size[i];
+ /* Put it next in the list */
+ split_node->next = max->next;
+ max->next = split_node;
+ break;
+ }
+ }
+
+ /* Now take it out of the list */
+ temp = (struct pci_resource*) *head;
+ if (temp == max) {
+ *head = max->next;
+ } else {
+ while (temp && temp->next != max) {
+ temp = temp->next;
+ }
+
+ temp->next = max->next;
+ }
+
+ max->next = NULL;
+ return max;
+ }
+
+ /* If we get here, we couldn't find one */
+ return NULL;
+}
+
+
+/*
+ * get_resource
+ *
+ * this function sorts the resource list by size and then
+ * returns the first node of "size" length. If it finds a node
+ * larger than "size" it will split it up.
+ *
+ * size must be a power of two.
+ */
+static struct pci_resource *get_resource(struct pci_resource **head, u32 size)
+{
+ struct pci_resource *prevnode;
+ struct pci_resource *node;
+ struct pci_resource *split_node;
+ u32 temp_dword;
+
+ if (!(*head))
+ return NULL;
+
+ if ( pciehp_resource_sort_and_combine(head) )
+ return NULL;
+
+ if ( sort_by_size(head) )
+ return NULL;
+
+ for (node = *head; node; node = node->next) {
+ dbg("%s: req_size =0x%x node=%p, base=0x%x, length=0x%x\n",
+ __FUNCTION__, size, node, node->base, node->length);
+ if (node->length < size)
+ continue;
+
+ if (node->base & (size - 1)) {
+ dbg("%s: not aligned\n", __FUNCTION__);
+ /* this one isn't base aligned properly
+ so we'll make a new entry and split it up */
+ temp_dword = (node->base | (size-1)) + 1;
+
+ /* Short circuit if adjusted size is too small */
+ if ((node->length - (temp_dword - node->base)) < size)
+ continue;
+
+ split_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+
+ split_node->base = node->base;
+ split_node->length = temp_dword - node->base;
+ node->base = temp_dword;
+ node->length -= split_node->length;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of non-aligned base */
+
+ /* Don't need to check if too small since we already did */
+ if (node->length > size) {
+ dbg("%s: too big\n", __FUNCTION__);
+ /* this one is longer than we need
+ so we'll make a new entry and split it up */
+ split_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+
+ if (!split_node)
+ return NULL;
+
+ split_node->base = node->base + size;
+ split_node->length = node->length - size;
+ node->length = size;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of too big on top end */
+
+ dbg("%s: got one!!!\n", __FUNCTION__);
+ /* If we got here, then it is the right size
+ Now take it out of the list */
+ if (*head == node) {
+ *head = node->next;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ }
+ node->next = NULL;
+ /* Stop looping */
+ break;
+ }
+ return node;
+}
+
+
+/*
+ * pciehp_resource_sort_and_combine
+ *
+ * Sorts all of the nodes in the list in ascending order by
+ * their base addresses. Also does garbage collection by
+ * combining adjacent nodes.
+ *
+ * returns 0 if success
+ */
+int pciehp_resource_sort_and_combine(struct pci_resource **head)
+{
+ struct pci_resource *node1;
+ struct pci_resource *node2;
+ int out_of_order = 1;
+
+ dbg("%s: head = %p, *head = %p\n", __FUNCTION__, head, *head);
+
+ if (!(*head))
+ return 1;
+
+ dbg("*head->next = %p\n",(*head)->next);
+
+ if (!(*head)->next)
+ return 0; /* only one item on the list, already sorted! */
+
+ dbg("*head->base = 0x%x\n",(*head)->base);
+ dbg("*head->next->base = 0x%x\n",(*head)->next->base);
+ while (out_of_order) {
+ out_of_order = 0;
+
+ /* Special case for swapping list head */
+ if (((*head)->next) &&
+ ((*head)->base > (*head)->next->base)) {
+ node1 = *head;
+ (*head) = (*head)->next;
+ node1->next = (*head)->next;
+ (*head)->next = node1;
+ out_of_order++;
+ }
+
+ node1 = (*head);
+
+ while (node1->next && node1->next->next) {
+ if (node1->next->base > node1->next->next->base) {
+ out_of_order++;
+ node2 = node1->next;
+ node1->next = node1->next->next;
+ node1 = node1->next;
+ node2->next = node1->next;
+ node1->next = node2;
+ } else
+ node1 = node1->next;
+ }
+ } /* End of out_of_order loop */
+
+ node1 = *head;
+
+ while (node1 && node1->next) {
+ if ((node1->base + node1->length) == node1->next->base) {
+ /* Combine */
+ dbg("8..\n");
+ node1->length += node1->next->length;
+ node2 = node1->next;
+ node1->next = node1->next->next;
+ kfree(node2);
+ } else
+ node1 = node1->next;
+ }
+
+ return 0;
+}
+
+
+/**
+ * pciehp_slot_create - Creates a node and adds it to the proper bus.
+ * @busnumber - bus where new node is to be located
+ *
+ * Returns pointer to the new node or NULL if unsuccessful
+ */
+struct pci_func *pciehp_slot_create(u8 busnumber)
+{
+ struct pci_func *new_slot;
+ struct pci_func *next;
+ dbg("%s: busnumber %x\n", __FUNCTION__, busnumber);
+ new_slot = kmalloc(sizeof(struct pci_func), GFP_KERNEL);
+
+ if (new_slot == NULL)
+ return new_slot;
+
+ memset(new_slot, 0, sizeof(struct pci_func));
+
+ new_slot->next = NULL;
+ new_slot->configured = 1;
+
+ if (pciehp_slot_list[busnumber] == NULL) {
+ pciehp_slot_list[busnumber] = new_slot;
+ } else {
+ next = pciehp_slot_list[busnumber];
+ while (next->next != NULL)
+ next = next->next;
+ next->next = new_slot;
+ }
+ return new_slot;
+}
+
+
+/**
+ * slot_remove - Removes a node from the linked list of slots.
+ * @old_slot: slot to remove
+ *
+ * Returns 0 if successful, !0 otherwise.
+ */
+static int slot_remove(struct pci_func * old_slot)
+{
+ struct pci_func *next;
+
+ if (old_slot == NULL)
+ return 1;
+
+ next = pciehp_slot_list[old_slot->bus];
+
+ if (next == NULL)
+ return 1;
+
+ if (next == old_slot) {
+ pciehp_slot_list[old_slot->bus] = old_slot->next;
+ pciehp_destroy_board_resources(old_slot);
+ kfree(old_slot);
+ return 0;
+ }
+
+ while ((next->next != old_slot) && (next->next != NULL)) {
+ next = next->next;
+ }
+
+ if (next->next == old_slot) {
+ next->next = old_slot->next;
+ pciehp_destroy_board_resources(old_slot);
+ kfree(old_slot);
+ return 0;
+ } else
+ return 2;
+}
+
+
+/**
+ * bridge_slot_remove - Removes a node from the linked list of slots.
+ * @bridge: bridge to remove
+ *
+ * Returns 0 if successful, !0 otherwise.
+ */
+static int bridge_slot_remove(struct pci_func *bridge)
+{
+ u8 subordinateBus, secondaryBus;
+ u8 tempBus;
+ struct pci_func *next;
+
+ if (bridge == NULL)
+ return 1;
+
+ secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF;
+ subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF;
+
+ for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) {
+ next = pciehp_slot_list[tempBus];
+
+ while (!slot_remove(next)) {
+ next = pciehp_slot_list[tempBus];
+ }
+ }
+
+ next = pciehp_slot_list[bridge->bus];
+
+ if (next == NULL) {
+ return 1;
+ }
+
+ if (next == bridge) {
+ pciehp_slot_list[bridge->bus] = bridge->next;
+ kfree(bridge);
+ return 0;
+ }
+
+ while ((next->next != bridge) && (next->next != NULL)) {
+ next = next->next;
+ }
+
+ if (next->next == bridge) {
+ next->next = bridge->next;
+ kfree(bridge);
+ return 0;
+ } else
+ return 2;
+}
+
+
+/**
+ * pciehp_slot_find - Looks for a node by bus, and device, multiple functions accessed
+ * @bus: bus to find
+ * @device: device to find
+ * @index: is 0 for first function found, 1 for the second...
+ *
+ * Returns pointer to the node if successful, %NULL otherwise.
+ */
+struct pci_func *pciehp_slot_find(u8 bus, u8 device, u8 index)
+{
+ int found = -1;
+ struct pci_func *func;
+
+ func = pciehp_slot_list[bus];
+ dbg("%s: bus %x device %x index %x\n",
+ __FUNCTION__, bus, device, index);
+ if (func != NULL) {
+ dbg("%s: func-> bus %x device %x function %x pci_dev %p\n",
+ __FUNCTION__, func->bus, func->device, func->function,
+ func->pci_dev);
+ } else
+ dbg("%s: func == NULL\n", __FUNCTION__);
+
+ if ((func == NULL) || ((func->device == device) && (index == 0)))
+ return func;
+
+ if (func->device == device)
+ found++;
+
+ while (func->next != NULL) {
+ func = func->next;
+
+ dbg("%s: In while loop, func-> bus %x device %x function %x pci_dev %p\n",
+ __FUNCTION__, func->bus, func->device, func->function,
+ func->pci_dev);
+ if (func->device == device)
+ found++;
+ dbg("%s: while loop, found %d, index %d\n", __FUNCTION__,
+ found, index);
+
+ if ((found == index) || (func->function == index)) {
+ dbg("%s: Found bus %x dev %x func %x\n", __FUNCTION__,
+ func->bus, func->device, func->function);
+ return func;
+ }
+ }
+
+ return NULL;
+}
+
+static int is_bridge(struct pci_func * func)
+{
+ /* Check the header type */
+ if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01)
+ return 1;
+ else
+ return 0;
+}
+
+
+/* The following routines constitute the bulk of the
+ hotplug controller logic
+ */
+
+static void set_slot_off(struct controller *ctrl, struct slot * pslot)
+{
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
+ if (POWER_CTRL(ctrl->ctrlcap)) {
+ if (pslot->hpc_ops->power_off_slot(pslot)) {
+ err("%s: Issue of Slot Power Off command failed\n", __FUNCTION__);
+ up(&ctrl->crit_sect);
+ return;
+ }
+ wait_for_ctrl_irq (ctrl);
+ }
+
+ if (PWR_LED(ctrl->ctrlcap)) {
+ pslot->hpc_ops->green_led_off(pslot);
+ wait_for_ctrl_irq (ctrl);
+ }
+
+ if (ATTN_LED(ctrl->ctrlcap)) {
+ if (pslot->hpc_ops->set_attention_status(pslot, 1)) {
+ err("%s: Issue of Set Attention Led command failed\n", __FUNCTION__);
+ up(&ctrl->crit_sect);
+ return;
+ }
+ wait_for_ctrl_irq (ctrl);
+ }
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+}
+
+/**
+ * board_added - Called after a board has been added to the system.
+ *
+ * Turns power on for the board
+ * Configures board
+ *
+ */
+static u32 board_added(struct pci_func * func, struct controller * ctrl)
+{
+ u8 hp_slot;
+ int index;
+ u32 temp_register = 0xFFFFFFFF;
+ u32 rc = 0;
+ struct pci_func *new_func = NULL;
+ struct slot *p_slot;
+ struct resource_lists res_lists;
+
+ p_slot = pciehp_find_slot(ctrl, func->device);
+ hp_slot = func->device - ctrl->slot_device_offset;
+
+ dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot);
+
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ if (POWER_CTRL(ctrl->ctrlcap)) {
+ /* Power on slot */
+ rc = p_slot->hpc_ops->power_on_slot(p_slot);
+ if (rc) {
+ up(&ctrl->crit_sect);
+ return -1;
+ }
+
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+ }
+
+ if (PWR_LED(ctrl->ctrlcap)) {
+ p_slot->hpc_ops->green_led_blink(p_slot);
+
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+ }
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+
+ /* Wait for ~1 second */
+ dbg("%s: before long_delay\n", __FUNCTION__);
+ wait_for_ctrl_irq (ctrl);
+ dbg("%s: afterlong_delay\n", __FUNCTION__);
+
+ /* Check link training status */
+ rc = p_slot->hpc_ops->check_lnk_status(ctrl);
+ if (rc) {
+ err("%s: Failed to check link status\n", __FUNCTION__);
+ set_slot_off(ctrl, p_slot);
+ return rc;
+ }
+
+ dbg("%s: func status = %x\n", __FUNCTION__, func->status);
+
+ /* Check for a power fault */
+ if (func->status == 0xFF) {
+ /* power fault occurred, but it was benign */
+ temp_register = 0xFFFFFFFF;
+ dbg("%s: temp register set to %x by power fault\n", __FUNCTION__, temp_register);
+ rc = POWER_FAILURE;
+ func->status = 0;
+ } else {
+ /* Get vendor/device ID u32 */
+ rc = pci_bus_read_config_dword (ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function),
+ PCI_VENDOR_ID, &temp_register);
+ dbg("%s: pci_bus_read_config_dword returns %d\n", __FUNCTION__, rc);
+ dbg("%s: temp_register is %x\n", __FUNCTION__, temp_register);
+
+ if (rc != 0) {
+ /* Something's wrong here */
+ temp_register = 0xFFFFFFFF;
+ dbg("%s: temp register set to %x by error\n", __FUNCTION__, temp_register);
+ }
+ /* Preset return code. It will be changed later if things go okay. */
+ rc = NO_ADAPTER_PRESENT;
+ }
+
+ /* All F's is an empty slot or an invalid board */
+ if (temp_register != 0xFFFFFFFF) { /* Check for a board in the slot */
+ res_lists.io_head = ctrl->io_head;
+ res_lists.mem_head = ctrl->mem_head;
+ res_lists.p_mem_head = ctrl->p_mem_head;
+ res_lists.bus_head = ctrl->bus_head;
+ res_lists.irqs = NULL;
+
+ rc = configure_new_device(ctrl, func, 0, &res_lists, 0, 0);
+ dbg("%s: back from configure_new_device\n", __FUNCTION__);
+
+ ctrl->io_head = res_lists.io_head;
+ ctrl->mem_head = res_lists.mem_head;
+ ctrl->p_mem_head = res_lists.p_mem_head;
+ ctrl->bus_head = res_lists.bus_head;
+
+ pciehp_resource_sort_and_combine(&(ctrl->mem_head));
+ pciehp_resource_sort_and_combine(&(ctrl->p_mem_head));
+ pciehp_resource_sort_and_combine(&(ctrl->io_head));
+ pciehp_resource_sort_and_combine(&(ctrl->bus_head));
+
+ if (rc) {
+ set_slot_off(ctrl, p_slot);
+ return rc;
+ }
+ pciehp_save_slot_config(ctrl, func);
+
+ func->status = 0;
+ func->switch_save = 0x10;
+ func->is_a_board = 0x01;
+
+ /* next, we will instantiate the linux pci_dev structures
+ * (with appropriate driver notification, if already present)
+ */
+ index = 0;
+ do {
+ new_func = pciehp_slot_find(ctrl->slot_bus, func->device, index++);
+ if (new_func && !new_func->pci_dev) {
+ dbg("%s:call pci_hp_configure_dev, func %x\n",
+ __FUNCTION__, index);
+ pciehp_configure_device(ctrl, new_func);
+ }
+ } while (new_func);
+
+ /*
+ * Some PCI Express root ports require fixup after hot-plug operation.
+ */
+ if (pcie_mch_quirk)
+ pci_fixup_device(pci_fixup_final, ctrl->pci_dev);
+
+ if (PWR_LED(ctrl->ctrlcap)) {
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ p_slot->hpc_ops->green_led_on(p_slot);
+
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ }
+ } else {
+ set_slot_off(ctrl, p_slot);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * remove_board - Turns off slot and LED's
+ *
+ */
+static u32 remove_board(struct pci_func *func, struct controller *ctrl)
+{
+ int index;
+ u8 skip = 0;
+ u8 device;
+ u8 hp_slot;
+ u32 rc;
+ struct resource_lists res_lists;
+ struct pci_func *temp_func;
+ struct slot *p_slot;
+
+ if (func == NULL)
+ return 1;
+
+ if (pciehp_unconfigure_device(func))
+ return 1;
+
+ device = func->device;
+
+ hp_slot = func->device - ctrl->slot_device_offset;
+ p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
+
+ if ((ctrl->add_support) &&
+ !(func->bus_head || func->mem_head || func->p_mem_head || func->io_head)) {
+ /* Here we check to see if we've saved any of the board's
+ * resources already. If so, we'll skip the attempt to
+ * determine what's being used.
+ */
+ index = 0;
+
+ temp_func = func;
+
+ while ((temp_func = pciehp_slot_find(temp_func->bus, temp_func->device, index++))) {
+ if (temp_func->bus_head || temp_func->mem_head
+ || temp_func->p_mem_head || temp_func->io_head) {
+ skip = 1;
+ break;
+ }
+ }
+
+ if (!skip)
+ rc = pciehp_save_used_resources(ctrl, func, DISABLE_CARD);
+ }
+ /* Change status to shutdown */
+ if (func->is_a_board)
+ func->status = 0x01;
+ func->configured = 0;
+
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ if (POWER_CTRL(ctrl->ctrlcap)) {
+ /* power off slot */
+ rc = p_slot->hpc_ops->power_off_slot(p_slot);
+ if (rc) {
+ err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
+ up(&ctrl->crit_sect);
+ return rc;
+ }
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+ }
+
+ if (PWR_LED(ctrl->ctrlcap)) {
+ /* turn off Green LED */
+ p_slot->hpc_ops->green_led_off(p_slot);
+
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+ }
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+
+ if (ctrl->add_support) {
+ while (func) {
+ res_lists.io_head = ctrl->io_head;
+ res_lists.mem_head = ctrl->mem_head;
+ res_lists.p_mem_head = ctrl->p_mem_head;
+ res_lists.bus_head = ctrl->bus_head;
+
+ dbg("Returning resources to ctlr lists for (B/D/F) = (%#x/%#x/%#x)\n",
+ func->bus, func->device, func->function);
+
+ pciehp_return_board_resources(func, &res_lists);
+
+ ctrl->io_head = res_lists.io_head;
+ ctrl->mem_head = res_lists.mem_head;
+ ctrl->p_mem_head = res_lists.p_mem_head;
+ ctrl->bus_head = res_lists.bus_head;
+
+ pciehp_resource_sort_and_combine(&(ctrl->mem_head));
+ pciehp_resource_sort_and_combine(&(ctrl->p_mem_head));
+ pciehp_resource_sort_and_combine(&(ctrl->io_head));
+ pciehp_resource_sort_and_combine(&(ctrl->bus_head));
+
+ if (is_bridge(func)) {
+ dbg("PCI Bridge Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n",
+ ctrl->seg, func->bus, func->device, func->function);
+ bridge_slot_remove(func);
+ } else {
+ dbg("PCI Function Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n",
+ ctrl->seg, func->bus, func->device, func->function);
+ slot_remove(func);
+ }
+
+ func = pciehp_slot_find(ctrl->slot_bus, device, 0);
+ }
+
+ /* Setup slot structure with entry for empty slot */
+ func = pciehp_slot_create(ctrl->slot_bus);
+
+ if (func == NULL) {
+ return 1;
+ }
+
+ func->bus = ctrl->slot_bus;
+ func->device = device;
+ func->function = 0;
+ func->configured = 0;
+ func->switch_save = 0x10;
+ func->is_a_board = 0;
+ }
+
+ return 0;
+}
+
+
+static void pushbutton_helper_thread(unsigned long data)
+{
+ pushbutton_pending = data;
+
+ up(&event_semaphore);
+}
+
+/**
+ * pciehp_pushbutton_thread
+ *
+ * Scheduled procedure to handle blocking stuff for the pushbuttons
+ * Handles all pending events and exits.
+ *
+ */
+static void pciehp_pushbutton_thread(unsigned long slot)
+{
+ struct slot *p_slot = (struct slot *) slot;
+ u8 getstatus;
+
+ pushbutton_pending = 0;
+
+ if (!p_slot) {
+ dbg("%s: Error! slot NULL\n", __FUNCTION__);
+ return;
+ }
+
+ p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
+ if (getstatus) {
+ p_slot->state = POWEROFF_STATE;
+ dbg("In power_down_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device);
+
+ pciehp_disable_slot(p_slot);
+ p_slot->state = STATIC_STATE;
+ } else {
+ p_slot->state = POWERON_STATE;
+ dbg("In add_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device);
+
+ if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl->ctrlcap)) {
+ /* Wait for exclusive access to hardware */
+ down(&p_slot->ctrl->crit_sect);
+
+ p_slot->hpc_ops->green_led_off(p_slot);
+
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (p_slot->ctrl);
+
+ /* Done with exclusive hardware access */
+ up(&p_slot->ctrl->crit_sect);
+ }
+ p_slot->state = STATIC_STATE;
+ }
+
+ return;
+}
+
+/**
+ * pciehp_surprise_rm_thread
+ *
+ * Scheduled procedure to handle blocking stuff for the surprise removal
+ * Handles all pending events and exits.
+ *
+ */
+static void pciehp_surprise_rm_thread(unsigned long slot)
+{
+ struct slot *p_slot = (struct slot *) slot;
+ u8 getstatus;
+
+ surprise_rm_pending = 0;
+
+ if (!p_slot) {
+ dbg("%s: Error! slot NULL\n", __FUNCTION__);
+ return;
+ }
+
+ p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
+ if (!getstatus) {
+ p_slot->state = POWEROFF_STATE;
+ dbg("In removing board, b:d(%x:%x)\n", p_slot->bus, p_slot->device);
+
+ pciehp_disable_slot(p_slot);
+ p_slot->state = STATIC_STATE;
+ } else {
+ p_slot->state = POWERON_STATE;
+ dbg("In add_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device);
+
+ if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl->ctrlcap)) {
+ /* Wait for exclusive access to hardware */
+ down(&p_slot->ctrl->crit_sect);
+
+ p_slot->hpc_ops->green_led_off(p_slot);
+
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (p_slot->ctrl);
+
+ /* Done with exclusive hardware access */
+ up(&p_slot->ctrl->crit_sect);
+ }
+ p_slot->state = STATIC_STATE;
+ }
+
+ return;
+}
+
+
+
+/* this is the main worker thread */
+static int event_thread(void* data)
+{
+ struct controller *ctrl;
+ lock_kernel();
+ daemonize("pciehpd_event");
+
+ unlock_kernel();
+
+ while (1) {
+ dbg("!!!!event_thread sleeping\n");
+ down_interruptible (&event_semaphore);
+ dbg("event_thread woken finished = %d\n", event_finished);
+ if (event_finished || signal_pending(current))
+ break;
+ /* Do stuff here */
+ if (pushbutton_pending)
+ pciehp_pushbutton_thread(pushbutton_pending);
+ else if (surprise_rm_pending)
+ pciehp_surprise_rm_thread(surprise_rm_pending);
+ else
+ for (ctrl = pciehp_ctrl_list; ctrl; ctrl=ctrl->next)
+ interrupt_event_handler(ctrl);
+ }
+ dbg("event_thread signals exit\n");
+ up(&event_exit);
+ return 0;
+}
+
+int pciehp_event_start_thread(void)
+{
+ int pid;
+
+ /* initialize our semaphores */
+ init_MUTEX_LOCKED(&event_exit);
+ event_finished=0;
+
+ init_MUTEX_LOCKED(&event_semaphore);
+ pid = kernel_thread(event_thread, NULL, 0);
+
+ if (pid < 0) {
+ err ("Can't start up our event thread\n");
+ return -1;
+ }
+ dbg("Our event thread pid = %d\n", pid);
+ return 0;
+}
+
+
+void pciehp_event_stop_thread(void)
+{
+ event_finished = 1;
+ dbg("event_thread finish command given\n");
+ up(&event_semaphore);
+ dbg("wait for event_thread to exit\n");
+ down(&event_exit);
+}
+
+
+static int update_slot_info(struct slot *slot)
+{
+ struct hotplug_slot_info *info;
+ /* char buffer[SLOT_NAME_SIZE]; */
+ int result;
+
+ info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ /* make_slot_name (&buffer[0], SLOT_NAME_SIZE, slot); */
+
+ slot->hpc_ops->get_power_status(slot, &(info->power_status));
+ slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
+ slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
+ slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
+
+ /* result = pci_hp_change_slot_info(buffer, info); */
+ result = pci_hp_change_slot_info(slot->hotplug_slot, info);
+ kfree (info);
+ return result;
+}
+
+static void interrupt_event_handler(struct controller *ctrl)
+{
+ int loop = 0;
+ int change = 1;
+ struct pci_func *func;
+ u8 hp_slot;
+ u8 getstatus;
+ struct slot *p_slot;
+
+ while (change) {
+ change = 0;
+
+ for (loop = 0; loop < 10; loop++) {
+ if (ctrl->event_queue[loop].event_type != 0) {
+ hp_slot = ctrl->event_queue[loop].hp_slot;
+
+ func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
+
+ p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ dbg("hp_slot %d, func %p, p_slot %p\n", hp_slot, func, p_slot);
+
+ if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) {
+ dbg("button cancel\n");
+ del_timer(&p_slot->task_event);
+
+ switch (p_slot->state) {
+ case BLINKINGOFF_STATE:
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ if (PWR_LED(ctrl->ctrlcap)) {
+ p_slot->hpc_ops->green_led_on(p_slot);
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+ }
+ if (ATTN_LED(ctrl->ctrlcap)) {
+ p_slot->hpc_ops->set_attention_status(p_slot, 0);
+
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+ }
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ break;
+ case BLINKINGON_STATE:
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ if (PWR_LED(ctrl->ctrlcap)) {
+ p_slot->hpc_ops->green_led_off(p_slot);
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+ }
+ if (ATTN_LED(ctrl->ctrlcap)){
+ p_slot->hpc_ops->set_attention_status(p_slot, 0);
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+ }
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+
+ break;
+ default:
+ warn("Not a valid state\n");
+ return;
+ }
+ info(msg_button_cancel, p_slot->number);
+ p_slot->state = STATIC_STATE;
+ }
+ /* ***********Button Pressed (No action on 1st press...) */
+ else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) {
+
+ if (ATTN_BUTTN(ctrl->ctrlcap)) {
+ dbg("Button pressed\n");
+ p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
+ if (getstatus) {
+ /* slot is on */
+ dbg("slot is on\n");
+ p_slot->state = BLINKINGOFF_STATE;
+ info(msg_button_off, p_slot->number);
+ } else {
+ /* slot is off */
+ dbg("slot is off\n");
+ p_slot->state = BLINKINGON_STATE;
+ info(msg_button_on, p_slot->number);
+ }
+
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ /* blink green LED and turn off amber */
+ if (PWR_LED(ctrl->ctrlcap)) {
+ p_slot->hpc_ops->green_led_blink(p_slot);
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+ }
+
+ if (ATTN_LED(ctrl->ctrlcap)) {
+ p_slot->hpc_ops->set_attention_status(p_slot, 0);
+
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+ }
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+
+ init_timer(&p_slot->task_event);
+ p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */
+ p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread;
+ p_slot->task_event.data = (unsigned long) p_slot;
+
+ dbg("add_timer p_slot = %p\n", (void *) p_slot);
+ add_timer(&p_slot->task_event);
+ }
+ }
+ /***********POWER FAULT********************/
+ else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
+ if (POWER_CTRL(ctrl->ctrlcap)) {
+ dbg("power fault\n");
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ if (ATTN_LED(ctrl->ctrlcap)) {
+ p_slot->hpc_ops->set_attention_status(p_slot, 1);
+ wait_for_ctrl_irq (ctrl);
+ }
+
+ if (PWR_LED(ctrl->ctrlcap)) {
+ p_slot->hpc_ops->green_led_off(p_slot);
+ wait_for_ctrl_irq (ctrl);
+ }
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ }
+ }
+ /***********SURPRISE REMOVAL********************/
+ else if ((ctrl->event_queue[loop].event_type == INT_PRESENCE_ON) ||
+ (ctrl->event_queue[loop].event_type == INT_PRESENCE_OFF)) {
+ if (HP_SUPR_RM(ctrl->ctrlcap)) {
+ dbg("Surprise Removal\n");
+ if (p_slot) {
+ surprise_rm_pending = (unsigned long) p_slot;
+ up(&event_semaphore);
+ update_slot_info(p_slot);
+ }
+ }
+ } else {
+ /* refresh notification */
+ if (p_slot)
+ update_slot_info(p_slot);
+ }
+
+ ctrl->event_queue[loop].event_type = 0;
+
+ change = 1;
+ }
+ } /* End of FOR loop */
+ }
+}
+
+
+int pciehp_enable_slot(struct slot *p_slot)
+{
+ u8 getstatus = 0;
+ int rc;
+ struct pci_func *func;
+
+ func = pciehp_slot_find(p_slot->bus, p_slot->device, 0);
+ if (!func) {
+ dbg("%s: Error! slot NULL\n", __FUNCTION__);
+ return 1;
+ }
+
+ /* Check to see if (latch closed, card present, power off) */
+ down(&p_slot->ctrl->crit_sect);
+
+ rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
+ if (rc || !getstatus) {
+ info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
+ up(&p_slot->ctrl->crit_sect);
+ return 1;
+ }
+ if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
+ rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+ if (rc || getstatus) {
+ info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
+ up(&p_slot->ctrl->crit_sect);
+ return 1;
+ }
+ }
+
+ if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
+ rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
+ if (rc || getstatus) {
+ info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number);
+ up(&p_slot->ctrl->crit_sect);
+ return 1;
+ }
+ }
+ up(&p_slot->ctrl->crit_sect);
+
+ slot_remove(func);
+
+ func = pciehp_slot_create(p_slot->bus);
+ if (func == NULL)
+ return 1;
+
+ func->bus = p_slot->bus;
+ func->device = p_slot->device;
+ func->function = 0;
+ func->configured = 0;
+ func->is_a_board = 1;
+
+ /* We have to save the presence info for these slots */
+ p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
+ p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+ func->switch_save = !getstatus? 0x10:0;
+
+ rc = board_added(func, p_slot->ctrl);
+ if (rc) {
+ if (is_bridge(func))
+ bridge_slot_remove(func);
+ else
+ slot_remove(func);
+
+ /* Setup slot structure with entry for empty slot */
+ func = pciehp_slot_create(p_slot->bus);
+ if (func == NULL)
+ return 1; /* Out of memory */
+
+ func->bus = p_slot->bus;
+ func->device = p_slot->device;
+ func->function = 0;
+ func->configured = 0;
+ func->is_a_board = 1;
+
+ /* We have to save the presence info for these slots */
+ p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
+ p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+ func->switch_save = !getstatus? 0x10:0;
+ }
+
+ if (p_slot)
+ update_slot_info(p_slot);
+
+ return rc;
+}
+
+
+int pciehp_disable_slot(struct slot *p_slot)
+{
+ u8 class_code, header_type, BCR;
+ u8 index = 0;
+ u8 getstatus = 0;
+ u32 rc = 0;
+ int ret = 0;
+ unsigned int devfn;
+ struct pci_bus *pci_bus = p_slot->ctrl->pci_dev->subordinate;
+ struct pci_func *func;
+
+ if (!p_slot->ctrl)
+ return 1;
+
+ /* Check to see if (latch closed, card present, power on) */
+ down(&p_slot->ctrl->crit_sect);
+
+ if (!HP_SUPR_RM(p_slot->ctrl->ctrlcap)) {
+ ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
+ if (ret || !getstatus) {
+ info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
+ up(&p_slot->ctrl->crit_sect);
+ return 1;
+ }
+ }
+
+ if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
+ ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+ if (ret || getstatus) {
+ info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
+ up(&p_slot->ctrl->crit_sect);
+ return 1;
+ }
+ }
+
+ if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
+ ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
+ if (ret || !getstatus) {
+ info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number);
+ up(&p_slot->ctrl->crit_sect);
+ return 1;
+ }
+ }
+
+ up(&p_slot->ctrl->crit_sect);
+
+ func = pciehp_slot_find(p_slot->bus, p_slot->device, index++);
+
+ /* Make sure there are no video controllers here
+ * for all func of p_slot
+ */
+ while (func && !rc) {
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ /* Check the Class Code */
+ rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
+ if (rc)
+ return rc;
+
+ if (class_code == PCI_BASE_CLASS_DISPLAY) {
+ /* Display/Video adapter (not supported) */
+ rc = REMOVE_NOT_SUPPORTED;
+ } else {
+ /* See if it's a bridge */
+ rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+ if (rc)
+ return rc;
+
+ /* If it's a bridge, check the VGA Enable bit */
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+ rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR);
+ if (rc)
+ return rc;
+
+ /* If the VGA Enable bit is set, remove isn't supported */
+ if (BCR & PCI_BRIDGE_CTL_VGA) {
+ rc = REMOVE_NOT_SUPPORTED;
+ }
+ }
+ }
+
+ func = pciehp_slot_find(p_slot->bus, p_slot->device, index++);
+ }
+
+ func = pciehp_slot_find(p_slot->bus, p_slot->device, 0);
+ if ((func != NULL) && !rc) {
+ rc = remove_board(func, p_slot->ctrl);
+ } else if (!rc)
+ rc = 1;
+
+ if (p_slot)
+ update_slot_info(p_slot);
+
+ return rc;
+}
+
+
+/**
+ * configure_new_device - Configures the PCI header information of one board.
+ *
+ * @ctrl: pointer to controller structure
+ * @func: pointer to function structure
+ * @behind_bridge: 1 if this is a recursive call, 0 if not
+ * @resources: pointer to set of resource lists
+ *
+ * Returns 0 if success
+ *
+ */
+static u32 configure_new_device(struct controller * ctrl, struct pci_func * func,
+ u8 behind_bridge, struct resource_lists * resources, u8 bridge_bus, u8 bridge_dev)
+{
+ u8 temp_byte, function, max_functions, stop_it;
+ int rc;
+ u32 ID;
+ struct pci_func *new_slot;
+ struct pci_bus lpci_bus, *pci_bus;
+ int index;
+
+ new_slot = func;
+
+ dbg("%s\n", __FUNCTION__);
+ memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+
+ /* Check for Multi-function device */
+ rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte);
+ if (rc) {
+ dbg("%s: rc = %d\n", __FUNCTION__, rc);
+ return rc;
+ }
+
+ if (temp_byte & 0x80) /* Multi-function device */
+ max_functions = 8;
+ else
+ max_functions = 1;
+
+ function = 0;
+
+ do {
+ rc = configure_new_function(ctrl, new_slot, behind_bridge,
+ resources, bridge_bus, bridge_dev);
+
+ if (rc) {
+ dbg("configure_new_function failed: %d\n", rc);
+ index = 0;
+
+ while (new_slot) {
+ new_slot = pciehp_slot_find(new_slot->bus,
+ new_slot->device, index++);
+
+ if (new_slot)
+ pciehp_return_board_resources(new_slot,
+ resources);
+ }
+
+ return rc;
+ }
+
+ function++;
+
+ stop_it = 0;
+
+ /* The following loop skips to the next present function
+ * and creates a board structure
+ */
+
+ while ((function < max_functions) && (!stop_it)) {
+ pci_bus_read_config_dword(pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID);
+
+ if (ID == 0xFFFFFFFF) { /* There's nothing there. */
+ function++;
+ } else { /* There's something there */
+ /* Setup slot structure. */
+ new_slot = pciehp_slot_create(func->bus);
+
+ if (new_slot == NULL) {
+ /* Out of memory */
+ return 1;
+ }
+
+ new_slot->bus = func->bus;
+ new_slot->device = func->device;
+ new_slot->function = function;
+ new_slot->is_a_board = 1;
+ new_slot->status = 0;
+
+ stop_it++;
+ }
+ }
+
+ } while (function < max_functions);
+ dbg("returning from %s\n", __FUNCTION__);
+
+ return 0;
+}
+
+/*
+ * Configuration logic that involves the hotplug data structures and
+ * their bookkeeping
+ */
+
+/**
+ * configure_bridge: fill bridge's registers, either configure or disable it.
+ */
+static int
+configure_bridge(struct pci_bus *pci_bus, unsigned int devfn,
+ struct pci_resource *mem_node,
+ struct pci_resource **hold_mem_node,
+ int base_addr, int limit_addr)
+{
+ u16 temp_word;
+ u32 rc;
+
+ if (mem_node) {
+ memcpy(*hold_mem_node, mem_node, sizeof(struct pci_resource));
+ mem_node->next = NULL;
+
+ /* set Mem base and Limit registers */
+ RES_CHECK(mem_node->base, 16);
+ temp_word = (u16)(mem_node->base >> 16);
+ rc = pci_bus_write_config_word(pci_bus, devfn, base_addr, temp_word);
+
+ RES_CHECK(mem_node->base + mem_node->length - 1, 16);
+ temp_word = (u16)((mem_node->base + mem_node->length - 1) >> 16);
+ rc = pci_bus_write_config_word(pci_bus, devfn, limit_addr, temp_word);
+ } else {
+ temp_word = 0xFFFF;
+ rc = pci_bus_write_config_word(pci_bus, devfn, base_addr, temp_word);
+
+ temp_word = 0x0000;
+ rc = pci_bus_write_config_word(pci_bus, devfn, limit_addr, temp_word);
+
+ kfree(*hold_mem_node);
+ *hold_mem_node = NULL;
+ }
+ return rc;
+}
+
+static int
+configure_new_bridge(struct controller *ctrl, struct pci_func *func,
+ u8 behind_bridge, struct resource_lists *resources,
+ struct pci_bus *pci_bus)
+{
+ int cloop;
+ u8 temp_byte;
+ u8 device;
+ u16 temp_word;
+ u32 rc;
+ u32 ID;
+ unsigned int devfn;
+ struct pci_resource *mem_node;
+ struct pci_resource *p_mem_node;
+ struct pci_resource *io_node;
+ struct pci_resource *bus_node;
+ struct pci_resource *hold_mem_node;
+ struct pci_resource *hold_p_mem_node;
+ struct pci_resource *hold_IO_node;
+ struct pci_resource *hold_bus_node;
+ struct irq_mapping irqs;
+ struct pci_func *new_slot;
+ struct resource_lists temp_resources;
+
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ /* set Primary bus */
+ dbg("set Primary bus = 0x%x\n", func->bus);
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus);
+ if (rc)
+ return rc;
+
+ /* find range of busses to use */
+ bus_node = get_max_resource(&resources->bus_head, 1L);
+
+ /* If we don't have any busses to allocate, we can't continue */
+ if (!bus_node) {
+ err("Got NO bus resource to use\n");
+ return -ENOMEM;
+ }
+ dbg("Got ranges of buses to use: base:len=0x%x:%x\n", bus_node->base, bus_node->length);
+
+ /* set Secondary bus */
+ temp_byte = (u8)bus_node->base;
+ dbg("set Secondary bus = 0x%x\n", temp_byte);
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte);
+ if (rc)
+ return rc;
+
+ /* set subordinate bus */
+ temp_byte = (u8)(bus_node->base + bus_node->length - 1);
+ dbg("set subordinate bus = 0x%x\n", temp_byte);
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
+ if (rc)
+ return rc;
+
+ /* Set HP parameters (Cache Line Size, Latency Timer) */
+ rc = pciehprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_BRIDGE);
+ if (rc)
+ return rc;
+
+ /* Setup the IO, memory, and prefetchable windows */
+
+ io_node = get_max_resource(&(resources->io_head), 0x1000L);
+ if (io_node) {
+ dbg("io_node(base, len, next) (%x, %x, %p)\n", io_node->base,
+ io_node->length, io_node->next);
+ }
+
+ mem_node = get_max_resource(&(resources->mem_head), 0x100000L);
+ if (mem_node) {
+ dbg("mem_node(base, len, next) (%x, %x, %p)\n", mem_node->base,
+ mem_node->length, mem_node->next);
+ }
+
+ if (resources->p_mem_head)
+ p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000L);
+ else {
+ /*
+ * In some platform implementation, MEM and PMEM are not
+ * distinguished, and hence ACPI _CRS has only MEM entries
+ * for both MEM and PMEM.
+ */
+ dbg("using MEM for PMEM\n");
+ p_mem_node = get_max_resource(&(resources->mem_head), 0x100000L);
+ }
+ if (p_mem_node) {
+ dbg("p_mem_node(base, len, next) (%x, %x, %p)\n", p_mem_node->base,
+ p_mem_node->length, p_mem_node->next);
+ }
+
+ /* set up the IRQ info */
+ if (!resources->irqs) {
+ irqs.barber_pole = 0;
+ irqs.interrupt[0] = 0;
+ irqs.interrupt[1] = 0;
+ irqs.interrupt[2] = 0;
+ irqs.interrupt[3] = 0;
+ irqs.valid_INT = 0;
+ } else {
+ irqs.barber_pole = resources->irqs->barber_pole;
+ irqs.interrupt[0] = resources->irqs->interrupt[0];
+ irqs.interrupt[1] = resources->irqs->interrupt[1];
+ irqs.interrupt[2] = resources->irqs->interrupt[2];
+ irqs.interrupt[3] = resources->irqs->interrupt[3];
+ irqs.valid_INT = resources->irqs->valid_INT;
+ }
+
+ /* set up resource lists that are now aligned on top and bottom
+ * for anything behind the bridge.
+ */
+ temp_resources.bus_head = bus_node;
+ temp_resources.io_head = io_node;
+ temp_resources.mem_head = mem_node;
+ temp_resources.p_mem_head = p_mem_node;
+ temp_resources.irqs = &irqs;
+
+ /* Make copies of the nodes we are going to pass down so that
+ * if there is a problem,we can just use these to free resources
+ */
+ hold_bus_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ hold_IO_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ hold_mem_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ hold_p_mem_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) {
+ kfree(hold_bus_node);
+ kfree(hold_IO_node);
+ kfree(hold_mem_node);
+ kfree(hold_p_mem_node);
+
+ return 1;
+ }
+
+ memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource));
+
+ bus_node->base += 1;
+ bus_node->length -= 1;
+ bus_node->next = NULL;
+
+ /* If we have IO resources copy them and fill in the bridge's
+ * IO range registers
+ */
+ if (io_node) {
+ memcpy(hold_IO_node, io_node, sizeof(struct pci_resource));
+ io_node->next = NULL;
+
+ /* set IO base and Limit registers */
+ RES_CHECK(io_node->base, 8);
+ temp_byte = (u8)(io_node->base >> 8);
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte);
+
+ RES_CHECK(io_node->base + io_node->length - 1, 8);
+ temp_byte = (u8)((io_node->base + io_node->length - 1) >> 8);
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+ } else {
+ kfree(hold_IO_node);
+ hold_IO_node = NULL;
+ }
+
+ /* If we have memory resources copy them and fill in the bridge's
+ * memory range registers. Otherwise, fill in the range
+ * registers with values that disable them.
+ */
+ rc = configure_bridge(pci_bus, devfn, mem_node, &hold_mem_node,
+ PCI_MEMORY_BASE, PCI_MEMORY_LIMIT);
+
+ /* If we have prefetchable memory resources copy them and
+ * fill in the bridge's memory range registers. Otherwise,
+ * fill in the range registers with values that disable them.
+ */
+ rc = configure_bridge(pci_bus, devfn, p_mem_node, &hold_p_mem_node,
+ PCI_PREF_MEMORY_BASE, PCI_PREF_MEMORY_LIMIT);
+
+ /* Adjust this to compensate for extra adjustment in first loop */
+ irqs.barber_pole--;
+
+ rc = 0;
+
+ /* Here we actually find the devices and configure them */
+ for (device = 0; (device <= 0x1F) && !rc; device++) {
+ irqs.barber_pole = (irqs.barber_pole + 1) & 0x03;
+
+ ID = 0xFFFFFFFF;
+ pci_bus->number = hold_bus_node->base;
+ pci_bus_read_config_dword (pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID);
+ pci_bus->number = func->bus;
+
+ if (ID != 0xFFFFFFFF) { /* device Present */
+ /* Setup slot structure. */
+ new_slot = pciehp_slot_create(hold_bus_node->base);
+
+ if (new_slot == NULL) {
+ /* Out of memory */
+ rc = -ENOMEM;
+ continue;
+ }
+
+ new_slot->bus = hold_bus_node->base;
+ new_slot->device = device;
+ new_slot->function = 0;
+ new_slot->is_a_board = 1;
+ new_slot->status = 0;
+
+ rc = configure_new_device(ctrl, new_slot, 1,
+ &temp_resources, func->bus,
+ func->device);
+ dbg("configure_new_device rc=0x%x\n",rc);
+ } /* End of IF (device in slot?) */
+ } /* End of FOR loop */
+
+ if (rc) {
+ pciehp_destroy_resource_list(&temp_resources);
+
+ return_resource(&(resources->bus_head), hold_bus_node);
+ return_resource(&(resources->io_head), hold_IO_node);
+ return_resource(&(resources->mem_head), hold_mem_node);
+ return_resource(&(resources->p_mem_head), hold_p_mem_node);
+ return(rc);
+ }
+
+ /* save the interrupt routing information */
+ if (resources->irqs) {
+ resources->irqs->interrupt[0] = irqs.interrupt[0];
+ resources->irqs->interrupt[1] = irqs.interrupt[1];
+ resources->irqs->interrupt[2] = irqs.interrupt[2];
+ resources->irqs->interrupt[3] = irqs.interrupt[3];
+ resources->irqs->valid_INT = irqs.valid_INT;
+ } else if (!behind_bridge) {
+ /* We need to hook up the interrupts here */
+ for (cloop = 0; cloop < 4; cloop++) {
+ if (irqs.valid_INT & (0x01 << cloop)) {
+ rc = pciehp_set_irq(func->bus, func->device,
+ 0x0A + cloop, irqs.interrupt[cloop]);
+ if (rc) {
+ pciehp_destroy_resource_list (&temp_resources);
+ return_resource(&(resources->bus_head), hold_bus_node);
+ return_resource(&(resources->io_head), hold_IO_node);
+ return_resource(&(resources->mem_head), hold_mem_node);
+ return_resource(&(resources->p_mem_head), hold_p_mem_node);
+ return rc;
+ }
+ }
+ } /* end of for loop */
+ }
+
+ /* Return unused bus resources
+ * First use the temporary node to store information for the board
+ */
+ if (hold_bus_node && bus_node && temp_resources.bus_head) {
+ hold_bus_node->length = bus_node->base - hold_bus_node->base;
+
+ hold_bus_node->next = func->bus_head;
+ func->bus_head = hold_bus_node;
+
+ temp_byte = (u8)(temp_resources.bus_head->base - 1);
+
+ /* set subordinate bus */
+ dbg("re-set subordinate bus = 0x%x\n", temp_byte);
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
+
+ if (temp_resources.bus_head->length == 0) {
+ kfree(temp_resources.bus_head);
+ temp_resources.bus_head = NULL;
+ } else {
+ dbg("return bus res of b:d(0x%x:%x) base:len(0x%x:%x)\n",
+ func->bus, func->device, temp_resources.bus_head->base, temp_resources.bus_head->length);
+ return_resource(&(resources->bus_head), temp_resources.bus_head);
+ }
+ }
+
+ /* If we have IO space available and there is some left,
+ * return the unused portion
+ */
+ if (hold_IO_node && temp_resources.io_head) {
+ io_node = do_pre_bridge_resource_split(&(temp_resources.io_head),
+ &hold_IO_node, 0x1000);
+
+ /* Check if we were able to split something off */
+ if (io_node) {
+ hold_IO_node->base = io_node->base + io_node->length;
+
+ RES_CHECK(hold_IO_node->base, 8);
+ temp_byte = (u8)((hold_IO_node->base) >> 8);
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte);
+
+ return_resource(&(resources->io_head), io_node);
+ }
+
+ io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000);
+
+ /* Check if we were able to split something off */
+ if (io_node) {
+ /* First use the temporary node to store information for the board */
+ hold_IO_node->length = io_node->base - hold_IO_node->base;
+
+ /* If we used any, add it to the board's list */
+ if (hold_IO_node->length) {
+ hold_IO_node->next = func->io_head;
+ func->io_head = hold_IO_node;
+
+ RES_CHECK(io_node->base - 1, 8);
+ temp_byte = (u8)((io_node->base - 1) >> 8);
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+
+ return_resource(&(resources->io_head), io_node);
+ } else {
+ /* it doesn't need any IO */
+ temp_byte = 0x00;
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+
+ return_resource(&(resources->io_head), io_node);
+ kfree(hold_IO_node);
+ }
+ } else {
+ /* it used most of the range */
+ hold_IO_node->next = func->io_head;
+ func->io_head = hold_IO_node;
+ }
+ } else if (hold_IO_node) {
+ /* it used the whole range */
+ hold_IO_node->next = func->io_head;
+ func->io_head = hold_IO_node;
+ }
+
+ /* If we have memory space available and there is some left,
+ * return the unused portion
+ */
+ if (hold_mem_node && temp_resources.mem_head) {
+ mem_node = do_pre_bridge_resource_split(&(temp_resources.mem_head), &hold_mem_node, 0x100000L);
+
+ /* Check if we were able to split something off */
+ if (mem_node) {
+ hold_mem_node->base = mem_node->base + mem_node->length;
+
+ RES_CHECK(hold_mem_node->base, 16);
+ temp_word = (u16)((hold_mem_node->base) >> 16);
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+ return_resource(&(resources->mem_head), mem_node);
+ }
+
+ mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000L);
+
+ /* Check if we were able to split something off */
+ if (mem_node) {
+ /* First use the temporary node to store information for the board */
+ hold_mem_node->length = mem_node->base - hold_mem_node->base;
+
+ if (hold_mem_node->length) {
+ hold_mem_node->next = func->mem_head;
+ func->mem_head = hold_mem_node;
+
+ /* configure end address */
+ RES_CHECK(mem_node->base - 1, 16);
+ temp_word = (u16)((mem_node->base - 1) >> 16);
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+ /* Return unused resources to the pool */
+ return_resource(&(resources->mem_head), mem_node);
+ } else {
+ /* it doesn't need any Mem */
+ temp_word = 0x0000;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+ return_resource(&(resources->mem_head), mem_node);
+ kfree(hold_mem_node);
+ }
+ } else {
+ /* it used most of the range */
+ hold_mem_node->next = func->mem_head;
+ func->mem_head = hold_mem_node;
+ }
+ } else if (hold_mem_node) {
+ /* it used the whole range */
+ hold_mem_node->next = func->mem_head;
+ func->mem_head = hold_mem_node;
+ }
+
+ /* If we have prefetchable memory space available and there is some
+ * left at the end, return the unused portion
+ */
+ if (hold_p_mem_node && temp_resources.p_mem_head) {
+ p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head),
+ &hold_p_mem_node, 0x100000L);
+
+ /* Check if we were able to split something off */
+ if (p_mem_node) {
+ hold_p_mem_node->base = p_mem_node->base + p_mem_node->length;
+
+ RES_CHECK(hold_p_mem_node->base, 16);
+ temp_word = (u16)((hold_p_mem_node->base) >> 16);
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+ return_resource(&(resources->p_mem_head), p_mem_node);
+ }
+
+ p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000L);
+
+ /* Check if we were able to split something off */
+ if (p_mem_node) {
+ /* First use the temporary node to store information for the board */
+ hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base;
+
+ /* If we used any, add it to the board's list */
+ if (hold_p_mem_node->length) {
+ hold_p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = hold_p_mem_node;
+
+ RES_CHECK(p_mem_node->base - 1, 16);
+ temp_word = (u16)((p_mem_node->base - 1) >> 16);
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+ return_resource(&(resources->p_mem_head), p_mem_node);
+ } else {
+ /* it doesn't need any PMem */
+ temp_word = 0x0000;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+ return_resource(&(resources->p_mem_head), p_mem_node);
+ kfree(hold_p_mem_node);
+ }
+ } else {
+ /* it used the most of the range */
+ hold_p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = hold_p_mem_node;
+ }
+ } else if (hold_p_mem_node) {
+ /* it used the whole range */
+ hold_p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = hold_p_mem_node;
+ }
+
+ /* We should be configuring an IRQ and the bridge's base address
+ * registers if it needs them. Although we have never seen such
+ * a device
+ */
+
+ pciehprm_enable_card(ctrl, func, PCI_HEADER_TYPE_BRIDGE);
+
+ dbg("PCI Bridge Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device, func->function);
+
+ return rc;
+}
+
+/**
+ * configure_new_function - Configures the PCI header information of one device
+ *
+ * @ctrl: pointer to controller structure
+ * @func: pointer to function structure
+ * @behind_bridge: 1 if this is a recursive call, 0 if not
+ * @resources: pointer to set of resource lists
+ *
+ * Calls itself recursively for bridged devices.
+ * Returns 0 if success
+ *
+ */
+static int
+configure_new_function(struct controller *ctrl, struct pci_func *func,
+ u8 behind_bridge, struct resource_lists *resources,
+ u8 bridge_bus, u8 bridge_dev)
+{
+ int cloop;
+ u8 temp_byte;
+ u8 class_code;
+ u16 temp_word;
+ u32 rc;
+ u32 temp_register;
+ u32 base;
+ unsigned int devfn;
+ struct pci_resource *mem_node;
+ struct pci_resource *io_node;
+ struct pci_bus lpci_bus, *pci_bus;
+
+ memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ /* Check for Bridge */
+ rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte);
+ if (rc)
+ return rc;
+ dbg("%s: bus %x dev %x func %x temp_byte = %x\n", __FUNCTION__,
+ func->bus, func->device, func->function, temp_byte);
+
+ if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
+ rc = configure_new_bridge(ctrl, func, behind_bridge, resources,
+ pci_bus);
+
+ if (rc)
+ return rc;
+ } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
+ /* Standard device */
+ u64 base64;
+ rc = pci_bus_read_config_byte(pci_bus, devfn, 0x0B, &class_code);
+
+ if (class_code == PCI_BASE_CLASS_DISPLAY)
+ return DEVICE_TYPE_NOT_SUPPORTED;
+
+ /* Figure out IO and memory needs */
+ for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) {
+ temp_register = 0xFFFFFFFF;
+
+ rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
+ rc = pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register);
+ dbg("Bar[%x]=0x%x on bus:dev:func(0x%x:%x:%x)\n", cloop, temp_register,
+ func->bus, func->device, func->function);
+
+ if (!temp_register)
+ continue;
+
+ base64 = 0L;
+ if (temp_register & PCI_BASE_ADDRESS_SPACE_IO) {
+ /* Map IO */
+
+ /* set base = amount of IO space */
+ base = temp_register & 0xFFFFFFFC;
+ base = ~base + 1;
+
+ dbg("NEED IO length(0x%x)\n", base);
+ io_node = get_io_resource(&(resources->io_head),(ulong)base);
+
+ /* allocate the resource to the board */
+ if (io_node) {
+ dbg("Got IO base=0x%x(length=0x%x)\n", io_node->base, io_node->length);
+ base = (u32)io_node->base;
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ } else {
+ err("Got NO IO resource(length=0x%x)\n", base);
+ return -ENOMEM;
+ }
+ } else { /* map MEM */
+ int prefetchable = 1;
+ struct pci_resource **res_node = &func->p_mem_head;
+ char *res_type_str = "PMEM";
+ u32 temp_register2;
+
+ if (!(temp_register & PCI_BASE_ADDRESS_MEM_PREFETCH)) {
+ prefetchable = 0;
+ res_node = &func->mem_head;
+ res_type_str++;
+ }
+
+ base = temp_register & 0xFFFFFFF0;
+ base = ~base + 1;
+
+ switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
+ case PCI_BASE_ADDRESS_MEM_TYPE_32:
+ dbg("NEED 32 %s bar=0x%x(length=0x%x)\n", res_type_str, temp_register, base);
+
+ if (prefetchable && resources->p_mem_head)
+ mem_node=get_resource(&(resources->p_mem_head), (ulong)base);
+ else {
+ if (prefetchable)
+ dbg("using MEM for PMEM\n");
+ mem_node = get_resource(&(resources->mem_head), (ulong)base);
+ }
+
+ /* allocate the resource to the board */
+ if (mem_node) {
+ base = (u32)mem_node->base;
+ mem_node->next = *res_node;
+ *res_node = mem_node;
+ dbg("Got 32 %s base=0x%x(length=0x%x)\n", res_type_str, mem_node->base,
+ mem_node->length);
+ } else {
+ err("Got NO 32 %s resource(length=0x%x)\n", res_type_str, base);
+ return -ENOMEM;
+ }
+ break;
+ case PCI_BASE_ADDRESS_MEM_TYPE_64:
+ rc = pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2);
+ dbg("NEED 64 %s bar=0x%x:%x(length=0x%x)\n", res_type_str, temp_register2,
+ temp_register, base);
+
+ if (prefetchable && resources->p_mem_head)
+ mem_node = get_resource(&(resources->p_mem_head), (ulong)base);
+ else {
+ if (prefetchable)
+ dbg("using MEM for PMEM\n");
+ mem_node = get_resource(&(resources->mem_head), (ulong)base);
+ }
+
+ /* allocate the resource to the board */
+ if (mem_node) {
+ base64 = mem_node->base;
+ mem_node->next = *res_node;
+ *res_node = mem_node;
+ dbg("Got 64 %s base=0x%x:%x(length=%x)\n", res_type_str, (u32)(base64 >> 32),
+ (u32)base64, mem_node->length);
+ } else {
+ err("Got NO 64 %s resource(length=0x%x)\n", res_type_str, base);
+ return -ENOMEM;
+ }
+ break;
+ default:
+ dbg("reserved BAR type=0x%x\n", temp_register);
+ break;
+ }
+
+ }
+
+ if (base64) {
+ rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64);
+ cloop += 4;
+ base64 >>= 32;
+
+ if (base64) {
+ dbg("%s: high dword of base64(0x%x) set to 0\n", __FUNCTION__, (u32)base64);
+ base64 = 0x0L;
+ }
+
+ rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64);
+ } else {
+ rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base);
+ }
+ } /* End of base register loop */
+
+ /* disable ROM base Address */
+ temp_word = 0x00L;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_ROM_ADDRESS, temp_word);
+
+ /* Set HP parameters (Cache Line Size, Latency Timer) */
+ rc = pciehprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_NORMAL);
+ if (rc)
+ return rc;
+
+ pciehprm_enable_card(ctrl, func, PCI_HEADER_TYPE_NORMAL);
+
+ dbg("PCI function Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device,
+ func->function);
+ } /* End of Not-A-Bridge else */
+ else {
+ /* It's some strange type of PCI adapter (Cardbus?) */
+ return DEVICE_TYPE_NOT_SUPPORTED;
+ }
+
+ func->configured = 1;
+
+ return 0;
+}
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
new file mode 100644
index 000000000000..9e70c4681f77
--- /dev/null
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -0,0 +1,1501 @@
+/*
+ * PCI Express PCI Hot Plug Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>,<dely.l.sy@intel.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+#include <asm/system.h>
+#include "../pci.h"
+#include "pciehp.h"
+
+#ifdef DEBUG
+#define DBG_K_TRACE_ENTRY ((unsigned int)0x00000001) /* On function entry */
+#define DBG_K_TRACE_EXIT ((unsigned int)0x00000002) /* On function exit */
+#define DBG_K_INFO ((unsigned int)0x00000004) /* Info messages */
+#define DBG_K_ERROR ((unsigned int)0x00000008) /* Error messages */
+#define DBG_K_TRACE (DBG_K_TRACE_ENTRY|DBG_K_TRACE_EXIT)
+#define DBG_K_STANDARD (DBG_K_INFO|DBG_K_ERROR|DBG_K_TRACE)
+/* Redefine this flagword to set debug level */
+#define DEBUG_LEVEL DBG_K_STANDARD
+
+#define DEFINE_DBG_BUFFER char __dbg_str_buf[256];
+
+#define DBG_PRINT( dbg_flags, args... ) \
+ do { \
+ if ( DEBUG_LEVEL & ( dbg_flags ) ) \
+ { \
+ int len; \
+ len = sprintf( __dbg_str_buf, "%s:%d: %s: ", \
+ __FILE__, __LINE__, __FUNCTION__ ); \
+ sprintf( __dbg_str_buf + len, args ); \
+ printk( KERN_NOTICE "%s\n", __dbg_str_buf ); \
+ } \
+ } while (0)
+
+#define DBG_ENTER_ROUTINE DBG_PRINT (DBG_K_TRACE_ENTRY, "%s", "[Entry]");
+#define DBG_LEAVE_ROUTINE DBG_PRINT (DBG_K_TRACE_EXIT, "%s", "[Exit]");
+#else
+#define DEFINE_DBG_BUFFER
+#define DBG_ENTER_ROUTINE
+#define DBG_LEAVE_ROUTINE
+#endif /* DEBUG */
+
+struct ctrl_reg {
+ u8 cap_id;
+ u8 nxt_ptr;
+ u16 cap_reg;
+ u32 dev_cap;
+ u16 dev_ctrl;
+ u16 dev_status;
+ u32 lnk_cap;
+ u16 lnk_ctrl;
+ u16 lnk_status;
+ u32 slot_cap;
+ u16 slot_ctrl;
+ u16 slot_status;
+ u16 root_ctrl;
+ u16 rsvp;
+ u32 root_status;
+} __attribute__ ((packed));
+
+/* offsets to the controller registers based on the above structure layout */
+enum ctrl_offsets {
+ PCIECAPID = offsetof(struct ctrl_reg, cap_id),
+ NXTCAPPTR = offsetof(struct ctrl_reg, nxt_ptr),
+ CAPREG = offsetof(struct ctrl_reg, cap_reg),
+ DEVCAP = offsetof(struct ctrl_reg, dev_cap),
+ DEVCTRL = offsetof(struct ctrl_reg, dev_ctrl),
+ DEVSTATUS = offsetof(struct ctrl_reg, dev_status),
+ LNKCAP = offsetof(struct ctrl_reg, lnk_cap),
+ LNKCTRL = offsetof(struct ctrl_reg, lnk_ctrl),
+ LNKSTATUS = offsetof(struct ctrl_reg, lnk_status),
+ SLOTCAP = offsetof(struct ctrl_reg, slot_cap),
+ SLOTCTRL = offsetof(struct ctrl_reg, slot_ctrl),
+ SLOTSTATUS = offsetof(struct ctrl_reg, slot_status),
+ ROOTCTRL = offsetof(struct ctrl_reg, root_ctrl),
+ ROOTSTATUS = offsetof(struct ctrl_reg, root_status),
+};
+static int pcie_cap_base = 0; /* Base of the PCI Express capability item structure */
+
+#define PCIE_CAP_ID ( pcie_cap_base + PCIECAPID )
+#define NXT_CAP_PTR ( pcie_cap_base + NXTCAPPTR )
+#define CAP_REG ( pcie_cap_base + CAPREG )
+#define DEV_CAP ( pcie_cap_base + DEVCAP )
+#define DEV_CTRL ( pcie_cap_base + DEVCTRL )
+#define DEV_STATUS ( pcie_cap_base + DEVSTATUS )
+#define LNK_CAP ( pcie_cap_base + LNKCAP )
+#define LNK_CTRL ( pcie_cap_base + LNKCTRL )
+#define LNK_STATUS ( pcie_cap_base + LNKSTATUS )
+#define SLOT_CAP ( pcie_cap_base + SLOTCAP )
+#define SLOT_CTRL ( pcie_cap_base + SLOTCTRL )
+#define SLOT_STATUS ( pcie_cap_base + SLOTSTATUS )
+#define ROOT_CTRL ( pcie_cap_base + ROOTCTRL )
+#define ROOT_STATUS ( pcie_cap_base + ROOTSTATUS )
+
+#define hp_register_read_word(pdev, reg , value) \
+ pci_read_config_word(pdev, reg, &value)
+
+#define hp_register_read_dword(pdev, reg , value) \
+ pci_read_config_dword(pdev, reg, &value)
+
+#define hp_register_write_word(pdev, reg , value) \
+ pci_write_config_word(pdev, reg, value)
+
+#define hp_register_dwrite_word(pdev, reg , value) \
+ pci_write_config_dword(pdev, reg, value)
+
+/* Field definitions in PCI Express Capabilities Register */
+#define CAP_VER 0x000F
+#define DEV_PORT_TYPE 0x00F0
+#define SLOT_IMPL 0x0100
+#define MSG_NUM 0x3E00
+
+/* Device or Port Type */
+#define NAT_ENDPT 0x00
+#define LEG_ENDPT 0x01
+#define ROOT_PORT 0x04
+#define UP_STREAM 0x05
+#define DN_STREAM 0x06
+#define PCIE_PCI_BRDG 0x07
+#define PCI_PCIE_BRDG 0x10
+
+/* Field definitions in Device Capabilities Register */
+#define DATTN_BUTTN_PRSN 0x1000
+#define DATTN_LED_PRSN 0x2000
+#define DPWR_LED_PRSN 0x4000
+
+/* Field definitions in Link Capabilities Register */
+#define MAX_LNK_SPEED 0x000F
+#define MAX_LNK_WIDTH 0x03F0
+
+/* Link Width Encoding */
+#define LNK_X1 0x01
+#define LNK_X2 0x02
+#define LNK_X4 0x04
+#define LNK_X8 0x08
+#define LNK_X12 0x0C
+#define LNK_X16 0x10
+#define LNK_X32 0x20
+
+/*Field definitions of Link Status Register */
+#define LNK_SPEED 0x000F
+#define NEG_LINK_WD 0x03F0
+#define LNK_TRN_ERR 0x0400
+#define LNK_TRN 0x0800
+#define SLOT_CLK_CONF 0x1000
+
+/* Field definitions in Slot Capabilities Register */
+#define ATTN_BUTTN_PRSN 0x00000001
+#define PWR_CTRL_PRSN 0x00000002
+#define MRL_SENS_PRSN 0x00000004
+#define ATTN_LED_PRSN 0x00000008
+#define PWR_LED_PRSN 0x00000010
+#define HP_SUPR_RM_SUP 0x00000020
+#define HP_CAP 0x00000040
+#define SLOT_PWR_VALUE 0x000003F8
+#define SLOT_PWR_LIMIT 0x00000C00
+#define PSN 0xFFF80000 /* PSN: Physical Slot Number */
+
+/* Field definitions in Slot Control Register */
+#define ATTN_BUTTN_ENABLE 0x0001
+#define PWR_FAULT_DETECT_ENABLE 0x0002
+#define MRL_DETECT_ENABLE 0x0004
+#define PRSN_DETECT_ENABLE 0x0008
+#define CMD_CMPL_INTR_ENABLE 0x0010
+#define HP_INTR_ENABLE 0x0020
+#define ATTN_LED_CTRL 0x00C0
+#define PWR_LED_CTRL 0x0300
+#define PWR_CTRL 0x0400
+
+/* Attention indicator and Power indicator states */
+#define LED_ON 0x01
+#define LED_BLINK 0x10
+#define LED_OFF 0x11
+
+/* Power Control Command */
+#define POWER_ON 0
+#define POWER_OFF 0x0400
+
+/* Field definitions in Slot Status Register */
+#define ATTN_BUTTN_PRESSED 0x0001
+#define PWR_FAULT_DETECTED 0x0002
+#define MRL_SENS_CHANGED 0x0004
+#define PRSN_DETECT_CHANGED 0x0008
+#define CMD_COMPLETED 0x0010
+#define MRL_STATE 0x0020
+#define PRSN_STATE 0x0040
+
+struct php_ctlr_state_s {
+ struct php_ctlr_state_s *pnext;
+ struct pci_dev *pci_dev;
+ unsigned int irq;
+ unsigned long flags; /* spinlock's */
+ u32 slot_device_offset;
+ u32 num_slots;
+ struct timer_list int_poll_timer; /* Added for poll event */
+ php_intr_callback_t attention_button_callback;
+ php_intr_callback_t switch_change_callback;
+ php_intr_callback_t presence_change_callback;
+ php_intr_callback_t power_fault_callback;
+ void *callback_instance_id;
+ struct ctrl_reg *creg; /* Ptr to controller register space */
+};
+
+
+static spinlock_t hpc_event_lock;
+
+DEFINE_DBG_BUFFER /* Debug string buffer for entire HPC defined here */
+static struct php_ctlr_state_s *php_ctlr_list_head; /* HPC state linked list */
+static int ctlr_seq_num = 0; /* Controller sequence # */
+static spinlock_t list_lock;
+
+static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs);
+
+static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds);
+
+/* This is the interrupt polling timeout function. */
+static void int_poll_timeout(unsigned long lphp_ctlr)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *)lphp_ctlr;
+
+ DBG_ENTER_ROUTINE
+
+ if ( !php_ctlr ) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return;
+ }
+
+ /* Poll for interrupt events. regs == NULL => polling */
+ pcie_isr( 0, (void *)php_ctlr, NULL );
+
+ init_timer(&php_ctlr->int_poll_timer);
+
+ if (!pciehp_poll_time)
+ pciehp_poll_time = 2; /* reset timer to poll in 2 secs if user doesn't specify at module installation*/
+
+ start_int_poll_timer(php_ctlr, pciehp_poll_time);
+
+ return;
+}
+
+/* This function starts the interrupt polling timer. */
+static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds)
+{
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return;
+ }
+
+ if ( ( seconds <= 0 ) || ( seconds > 60 ) )
+ seconds = 2; /* Clamp to sane value */
+
+ php_ctlr->int_poll_timer.function = &int_poll_timeout;
+ php_ctlr->int_poll_timer.data = (unsigned long)php_ctlr; /* Instance data */
+ php_ctlr->int_poll_timer.expires = jiffies + seconds * HZ;
+ add_timer(&php_ctlr->int_poll_timer);
+
+ return;
+}
+
+static int pcie_write_cmd(struct slot *slot, u16 cmd)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ int retval = 0;
+ u16 slot_status;
+
+ DBG_ENTER_ROUTINE
+
+ dbg("%s : Enter\n", __FUNCTION__);
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
+ if (retval) {
+ err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
+ return retval;
+ }
+ dbg("%s : hp_register_read_word SLOT_STATUS %x\n", __FUNCTION__, slot_status);
+
+ if ((slot_status & CMD_COMPLETED) == CMD_COMPLETED ) {
+ /* After 1 sec and CMD_COMPLETED still not set, just proceed forward to issue
+ the next command according to spec. Just print out the error message */
+ dbg("%s : CMD_COMPLETED not clear after 1 sec.\n", __FUNCTION__);
+ }
+
+ dbg("%s: Before hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, cmd);
+ retval = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL, cmd | CMD_CMPL_INTR_ENABLE);
+ if (retval) {
+ err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
+ return retval;
+ }
+ dbg("%s : hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, cmd | CMD_CMPL_INTR_ENABLE);
+ dbg("%s : Exit\n", __FUNCTION__);
+
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static int hpc_check_lnk_status(struct controller *ctrl)
+{
+ struct php_ctlr_state_s *php_ctlr = ctrl->hpc_ctlr_handle;
+ u16 lnk_status;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS, lnk_status);
+
+ if (retval) {
+ err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__);
+ return retval;
+ }
+
+ dbg("%s: lnk_status = %x\n", __FUNCTION__, lnk_status);
+ if ( (lnk_status & LNK_TRN) || (lnk_status & LNK_TRN_ERR) ||
+ !(lnk_status & NEG_LINK_WD)) {
+ err("%s : Link Training Error occurs \n", __FUNCTION__);
+ retval = -1;
+ return retval;
+ }
+
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+
+static int hpc_get_attention_status(struct slot *slot, u8 *status)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ u16 slot_ctrl;
+ u8 atten_led_state;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
+
+ if (retval) {
+ err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
+ return retval;
+ }
+
+ dbg("%s: SLOT_CTRL %x, value read %x\n", __FUNCTION__,SLOT_CTRL, slot_ctrl);
+
+ atten_led_state = (slot_ctrl & ATTN_LED_CTRL) >> 6;
+
+ switch (atten_led_state) {
+ case 0:
+ *status = 0xFF; /* Reserved */
+ break;
+ case 1:
+ *status = 1; /* On */
+ break;
+ case 2:
+ *status = 2; /* Blink */
+ break;
+ case 3:
+ *status = 0; /* Off */
+ break;
+ default:
+ *status = 0xFF;
+ break;
+ }
+
+ DBG_LEAVE_ROUTINE
+ return 0;
+}
+
+static int hpc_get_power_status(struct slot * slot, u8 *status)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ u16 slot_ctrl;
+ u8 pwr_state;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
+
+ if (retval) {
+ err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
+ return retval;
+ }
+ dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL, slot_ctrl);
+
+ pwr_state = (slot_ctrl & PWR_CTRL) >> 10;
+
+ switch (pwr_state) {
+ case 0:
+ *status = 1;
+ break;
+ case 1:
+ *status = 0;
+ break;
+ default:
+ *status = 0xFF;
+ break;
+ }
+
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+
+static int hpc_get_latch_status(struct slot *slot, u8 *status)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ u16 slot_status;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
+
+ if (retval) {
+ err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
+ return retval;
+ }
+
+ *status = (((slot_status & MRL_STATE) >> 5) == 0) ? 0 : 1;
+
+ DBG_LEAVE_ROUTINE
+ return 0;
+}
+
+static int hpc_get_adapter_status(struct slot *slot, u8 *status)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ u16 slot_status;
+ u8 card_state;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
+
+ if (retval) {
+ err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
+ return retval;
+ }
+ card_state = (u8)((slot_status & PRSN_STATE) >> 6);
+ *status = (card_state == 1) ? 1 : 0;
+
+ DBG_LEAVE_ROUTINE
+ return 0;
+}
+
+static int hpc_query_power_fault(struct slot * slot)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ u16 slot_status;
+ u8 pwr_fault;
+ int retval = 0;
+ u8 status;
+
+ DBG_ENTER_ROUTINE
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
+
+ if (retval) {
+ err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
+ return retval;
+ }
+ pwr_fault = (u8)((slot_status & PWR_FAULT_DETECTED) >> 1);
+ status = (pwr_fault != 1) ? 1 : 0;
+
+ DBG_LEAVE_ROUTINE
+ /* Note: Logic 0 => fault */
+ return status;
+}
+
+static int hpc_set_attention_status(struct slot *slot, u8 value)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ u16 slot_cmd = 0;
+ u16 slot_ctrl;
+ int rc = 0;
+
+ dbg("%s: \n", __FUNCTION__);
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+ rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
+
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
+ return rc;
+ }
+ dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl);
+
+ switch (value) {
+ case 0 : /* turn off */
+ slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x00C0;
+ break;
+ case 1: /* turn on */
+ slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0040;
+ break;
+ case 2: /* turn blink */
+ slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0080;
+ break;
+ default:
+ return -1;
+ }
+ if (!pciehp_poll_mode)
+ slot_cmd = slot_cmd | HP_INTR_ENABLE;
+
+ pcie_write_cmd(slot, slot_cmd);
+ dbg("%s: SLOT_CTRL %x write cmd %x\n", __FUNCTION__, SLOT_CTRL, slot_cmd);
+
+ return rc;
+}
+
+
+static void hpc_set_green_led_on(struct slot *slot)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ u16 slot_cmd;
+ u16 slot_ctrl;
+ int rc = 0;
+
+ dbg("%s: \n", __FUNCTION__);
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return ;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return ;
+ }
+
+ rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
+
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
+ return;
+ }
+ dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl);
+ slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0100;
+ if (!pciehp_poll_mode)
+ slot_cmd = slot_cmd | HP_INTR_ENABLE;
+
+ pcie_write_cmd(slot, slot_cmd);
+
+ dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL, slot_cmd);
+ return;
+}
+
+static void hpc_set_green_led_off(struct slot *slot)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ u16 slot_cmd;
+ u16 slot_ctrl;
+ int rc = 0;
+
+ dbg("%s: \n", __FUNCTION__);
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return ;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return ;
+ }
+
+ rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
+
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
+ return;
+ }
+ dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl);
+
+ slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0300;
+
+ if (!pciehp_poll_mode)
+ slot_cmd = slot_cmd | HP_INTR_ENABLE;
+ pcie_write_cmd(slot, slot_cmd);
+ dbg("%s: SLOT_CTRL %x write cmd %x\n", __FUNCTION__, SLOT_CTRL, slot_cmd);
+
+ return;
+}
+
+static void hpc_set_green_led_blink(struct slot *slot)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ u16 slot_cmd;
+ u16 slot_ctrl;
+ int rc = 0;
+
+ dbg("%s: \n", __FUNCTION__);
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return ;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return ;
+ }
+
+ rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
+
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
+ return;
+ }
+ dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl);
+
+ slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0200;
+
+ if (!pciehp_poll_mode)
+ slot_cmd = slot_cmd | HP_INTR_ENABLE;
+ pcie_write_cmd(slot, slot_cmd);
+
+ dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL, slot_cmd);
+ return;
+}
+
+int pcie_get_ctlr_slot_config(struct controller *ctrl,
+ int *num_ctlr_slots, /* number of slots in this HPC; only 1 in PCIE */
+ int *first_device_num, /* PCI dev num of the first slot in this PCIE */
+ int *physical_slot_num, /* phy slot num of the first slot in this PCIE */
+ u8 *ctrlcap)
+{
+ struct php_ctlr_state_s *php_ctlr = ctrl->hpc_ctlr_handle;
+ u32 slot_cap;
+ int rc = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ *first_device_num = 0;
+ *num_ctlr_slots = 1;
+
+ rc = hp_register_read_dword(php_ctlr->pci_dev, SLOT_CAP, slot_cap);
+
+ if (rc) {
+ err("%s : hp_register_read_dword SLOT_CAP failed\n", __FUNCTION__);
+ return -1;
+ }
+
+ *physical_slot_num = slot_cap >> 19;
+ dbg("%s: PSN %d \n", __FUNCTION__, *physical_slot_num);
+
+ *ctrlcap = slot_cap & 0x0000007f;
+
+ DBG_LEAVE_ROUTINE
+ return 0;
+}
+
+static void hpc_release_ctlr(struct controller *ctrl)
+{
+ struct php_ctlr_state_s *php_ctlr = ctrl->hpc_ctlr_handle;
+ struct php_ctlr_state_s *p, *p_prev;
+
+ DBG_ENTER_ROUTINE
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return ;
+ }
+
+ if (pciehp_poll_mode) {
+ del_timer(&php_ctlr->int_poll_timer);
+ } else {
+ if (php_ctlr->irq) {
+ free_irq(php_ctlr->irq, ctrl);
+ php_ctlr->irq = 0;
+ if (!pcie_mch_quirk)
+ pci_disable_msi(php_ctlr->pci_dev);
+ }
+ }
+ if (php_ctlr->pci_dev)
+ php_ctlr->pci_dev = NULL;
+
+ spin_lock(&list_lock);
+ p = php_ctlr_list_head;
+ p_prev = NULL;
+ while (p) {
+ if (p == php_ctlr) {
+ if (p_prev)
+ p_prev->pnext = p->pnext;
+ else
+ php_ctlr_list_head = p->pnext;
+ break;
+ } else {
+ p_prev = p;
+ p = p->pnext;
+ }
+ }
+ spin_unlock(&list_lock);
+
+ kfree(php_ctlr);
+
+ DBG_LEAVE_ROUTINE
+
+}
+
+static int hpc_power_on_slot(struct slot * slot)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ u16 slot_cmd;
+ u16 slot_ctrl;
+
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+ dbg("%s: \n", __FUNCTION__);
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot);
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+
+ retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
+
+ if (retval) {
+ err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
+ return retval;
+ }
+ dbg("%s: SLOT_CTRL %x, value read %xn", __FUNCTION__, SLOT_CTRL,
+ slot_ctrl);
+
+ slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_ON;
+
+ if (!pciehp_poll_mode)
+ slot_cmd = slot_cmd | HP_INTR_ENABLE;
+
+ retval = pcie_write_cmd(slot, slot_cmd);
+
+ if (retval) {
+ err("%s: Write %x command failed!\n", __FUNCTION__, slot_cmd);
+ return -1;
+ }
+ dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL, slot_cmd);
+
+ DBG_LEAVE_ROUTINE
+
+ return retval;
+}
+
+static int hpc_power_off_slot(struct slot * slot)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ u16 slot_cmd;
+ u16 slot_ctrl;
+
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+ dbg("%s: \n", __FUNCTION__);
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot);
+ slot->hp_slot = 0;
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+ retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
+
+ if (retval) {
+ err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
+ return retval;
+ }
+ dbg("%s: SLOT_CTRL %x, value read %x\n", __FUNCTION__, SLOT_CTRL,
+ slot_ctrl);
+
+ slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_OFF;
+
+ if (!pciehp_poll_mode)
+ slot_cmd = slot_cmd | HP_INTR_ENABLE;
+
+ retval = pcie_write_cmd(slot, slot_cmd);
+
+ if (retval) {
+ err("%s: Write command failed!\n", __FUNCTION__);
+ return -1;
+ }
+ dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL, slot_cmd);
+
+ DBG_LEAVE_ROUTINE
+
+ return retval;
+}
+
+static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs)
+{
+ struct controller *ctrl = NULL;
+ struct php_ctlr_state_s *php_ctlr;
+ u8 schedule_flag = 0;
+ u16 slot_status, intr_detect, intr_loc;
+ u16 temp_word;
+ int hp_slot = 0; /* only 1 slot per PCI Express port */
+ int rc = 0;
+
+ if (!dev_id)
+ return IRQ_NONE;
+
+ if (!pciehp_poll_mode) {
+ ctrl = dev_id;
+ php_ctlr = ctrl->hpc_ctlr_handle;
+ } else {
+ php_ctlr = dev_id;
+ ctrl = (struct controller *)php_ctlr->callback_instance_id;
+ }
+
+ if (!ctrl) {
+ dbg("%s: dev_id %p ctlr == NULL\n", __FUNCTION__, (void*) dev_id);
+ return IRQ_NONE;
+ }
+
+ if (!php_ctlr) {
+ dbg("%s: php_ctlr == NULL\n", __FUNCTION__);
+ return IRQ_NONE;
+ }
+
+ rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
+ return IRQ_NONE;
+ }
+
+ intr_detect = ( ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED | MRL_SENS_CHANGED |
+ PRSN_DETECT_CHANGED | CMD_COMPLETED );
+
+ intr_loc = slot_status & intr_detect;
+
+ /* Check to see if it was our interrupt */
+ if ( !intr_loc )
+ return IRQ_NONE;
+
+ dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc);
+ /* Mask Hot-plug Interrupt Enable */
+ if (!pciehp_poll_mode) {
+ rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word);
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
+ return IRQ_NONE;
+ }
+
+ dbg("%s: Set Mask Hot-plug Interrupt Enable\n", __FUNCTION__);
+ dbg("%s: hp_register_read_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word);
+ temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) | 0x00;
+
+ rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word);
+ if (rc) {
+ err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
+ return IRQ_NONE;
+ }
+ dbg("%s: hp_register_write_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word);
+
+ rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
+ return IRQ_NONE;
+ }
+ dbg("%s: hp_register_read_word SLOT_STATUS with value %x\n", __FUNCTION__, slot_status);
+
+ /* Clear command complete interrupt caused by this write */
+ temp_word = 0x1f;
+ rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word);
+ if (rc) {
+ err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__);
+ return IRQ_NONE;
+ }
+ dbg("%s: hp_register_write_word SLOT_STATUS with value %x\n", __FUNCTION__, temp_word);
+ }
+
+ if (intr_loc & CMD_COMPLETED) {
+ /*
+ * Command Complete Interrupt Pending
+ */
+ dbg("%s: In Command Complete Interrupt Pending\n", __FUNCTION__);
+ wake_up_interruptible(&ctrl->queue);
+ }
+
+ if ((php_ctlr->switch_change_callback) && (intr_loc & MRL_SENS_CHANGED))
+ schedule_flag += php_ctlr->switch_change_callback(
+ hp_slot, php_ctlr->callback_instance_id);
+ if ((php_ctlr->attention_button_callback) && (intr_loc & ATTN_BUTTN_PRESSED))
+ schedule_flag += php_ctlr->attention_button_callback(
+ hp_slot, php_ctlr->callback_instance_id);
+ if ((php_ctlr->presence_change_callback) && (intr_loc & PRSN_DETECT_CHANGED))
+ schedule_flag += php_ctlr->presence_change_callback(
+ hp_slot , php_ctlr->callback_instance_id);
+ if ((php_ctlr->power_fault_callback) && (intr_loc & PWR_FAULT_DETECTED))
+ schedule_flag += php_ctlr->power_fault_callback(
+ hp_slot, php_ctlr->callback_instance_id);
+
+ /* Clear all events after serving them */
+ temp_word = 0x1F;
+ rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word);
+ if (rc) {
+ err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__);
+ return IRQ_NONE;
+ }
+ /* Unmask Hot-plug Interrupt Enable */
+ if (!pciehp_poll_mode) {
+ rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word);
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
+ return IRQ_NONE;
+ }
+
+ dbg("%s: Unmask Hot-plug Interrupt Enable\n", __FUNCTION__);
+ dbg("%s: hp_register_read_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word);
+ temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE;
+
+ rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word);
+ if (rc) {
+ err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
+ return IRQ_NONE;
+ }
+ dbg("%s: hp_register_write_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word);
+
+ rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
+ return IRQ_NONE;
+ }
+ dbg("%s: hp_register_read_word SLOT_STATUS with value %x\n", __FUNCTION__, slot_status);
+
+ /* Clear command complete interrupt caused by this write */
+ temp_word = 0x1F;
+ rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word);
+ if (rc) {
+ err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__);
+ return IRQ_NONE;
+ }
+ dbg("%s: hp_register_write_word SLOT_STATUS with value %x\n", __FUNCTION__, temp_word);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int hpc_get_max_lnk_speed (struct slot *slot, enum pci_bus_speed *value)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ enum pcie_link_speed lnk_speed;
+ u32 lnk_cap;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+
+ retval = hp_register_read_dword(php_ctlr->pci_dev, LNK_CAP, lnk_cap);
+
+ if (retval) {
+ err("%s : hp_register_read_dword LNK_CAP failed\n", __FUNCTION__);
+ return retval;
+ }
+
+ switch (lnk_cap & 0x000F) {
+ case 1:
+ lnk_speed = PCIE_2PT5GB;
+ break;
+ default:
+ lnk_speed = PCIE_LNK_SPEED_UNKNOWN;
+ break;
+ }
+
+ *value = lnk_speed;
+ dbg("Max link speed = %d\n", lnk_speed);
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static int hpc_get_max_lnk_width (struct slot *slot, enum pcie_link_width *value)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ enum pcie_link_width lnk_wdth;
+ u32 lnk_cap;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+
+ retval = hp_register_read_dword(php_ctlr->pci_dev, LNK_CAP, lnk_cap);
+
+ if (retval) {
+ err("%s : hp_register_read_dword LNK_CAP failed\n", __FUNCTION__);
+ return retval;
+ }
+
+ switch ((lnk_cap & 0x03F0) >> 4){
+ case 0:
+ lnk_wdth = PCIE_LNK_WIDTH_RESRV;
+ break;
+ case 1:
+ lnk_wdth = PCIE_LNK_X1;
+ break;
+ case 2:
+ lnk_wdth = PCIE_LNK_X2;
+ break;
+ case 4:
+ lnk_wdth = PCIE_LNK_X4;
+ break;
+ case 8:
+ lnk_wdth = PCIE_LNK_X8;
+ break;
+ case 12:
+ lnk_wdth = PCIE_LNK_X12;
+ break;
+ case 16:
+ lnk_wdth = PCIE_LNK_X16;
+ break;
+ case 32:
+ lnk_wdth = PCIE_LNK_X32;
+ break;
+ default:
+ lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN;
+ break;
+ }
+
+ *value = lnk_wdth;
+ dbg("Max link width = %d\n", lnk_wdth);
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static int hpc_get_cur_lnk_speed (struct slot *slot, enum pci_bus_speed *value)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ enum pcie_link_speed lnk_speed = PCI_SPEED_UNKNOWN;
+ int retval = 0;
+ u16 lnk_status;
+
+ DBG_ENTER_ROUTINE
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+
+ retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS, lnk_status);
+
+ if (retval) {
+ err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__);
+ return retval;
+ }
+
+ switch (lnk_status & 0x0F) {
+ case 1:
+ lnk_speed = PCIE_2PT5GB;
+ break;
+ default:
+ lnk_speed = PCIE_LNK_SPEED_UNKNOWN;
+ break;
+ }
+
+ *value = lnk_speed;
+ dbg("Current link speed = %d\n", lnk_speed);
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static int hpc_get_cur_lnk_width (struct slot *slot, enum pcie_link_width *value)
+{
+ struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
+ enum pcie_link_width lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN;
+ int retval = 0;
+ u16 lnk_status;
+
+ DBG_ENTER_ROUTINE
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+
+ retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS, lnk_status);
+
+ if (retval) {
+ err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__);
+ return retval;
+ }
+
+ switch ((lnk_status & 0x03F0) >> 4){
+ case 0:
+ lnk_wdth = PCIE_LNK_WIDTH_RESRV;
+ break;
+ case 1:
+ lnk_wdth = PCIE_LNK_X1;
+ break;
+ case 2:
+ lnk_wdth = PCIE_LNK_X2;
+ break;
+ case 4:
+ lnk_wdth = PCIE_LNK_X4;
+ break;
+ case 8:
+ lnk_wdth = PCIE_LNK_X8;
+ break;
+ case 12:
+ lnk_wdth = PCIE_LNK_X12;
+ break;
+ case 16:
+ lnk_wdth = PCIE_LNK_X16;
+ break;
+ case 32:
+ lnk_wdth = PCIE_LNK_X32;
+ break;
+ default:
+ lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN;
+ break;
+ }
+
+ *value = lnk_wdth;
+ dbg("Current link width = %d\n", lnk_wdth);
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static struct hpc_ops pciehp_hpc_ops = {
+ .power_on_slot = hpc_power_on_slot,
+ .power_off_slot = hpc_power_off_slot,
+ .set_attention_status = hpc_set_attention_status,
+ .get_power_status = hpc_get_power_status,
+ .get_attention_status = hpc_get_attention_status,
+ .get_latch_status = hpc_get_latch_status,
+ .get_adapter_status = hpc_get_adapter_status,
+
+ .get_max_bus_speed = hpc_get_max_lnk_speed,
+ .get_cur_bus_speed = hpc_get_cur_lnk_speed,
+ .get_max_lnk_width = hpc_get_max_lnk_width,
+ .get_cur_lnk_width = hpc_get_cur_lnk_width,
+
+ .query_power_fault = hpc_query_power_fault,
+ .green_led_on = hpc_set_green_led_on,
+ .green_led_off = hpc_set_green_led_off,
+ .green_led_blink = hpc_set_green_led_blink,
+
+ .release_ctlr = hpc_release_ctlr,
+ .check_lnk_status = hpc_check_lnk_status,
+};
+
+int pcie_init(struct controller * ctrl,
+ struct pcie_device *dev,
+ php_intr_callback_t attention_button_callback,
+ php_intr_callback_t switch_change_callback,
+ php_intr_callback_t presence_change_callback,
+ php_intr_callback_t power_fault_callback)
+{
+ struct php_ctlr_state_s *php_ctlr, *p;
+ void *instance_id = ctrl;
+ int rc;
+ static int first = 1;
+ u16 temp_word;
+ u16 cap_reg;
+ u16 intr_enable = 0;
+ u32 slot_cap;
+ int cap_base, saved_cap_base;
+ u16 slot_status, slot_ctrl;
+ struct pci_dev *pdev;
+
+ DBG_ENTER_ROUTINE
+
+ spin_lock_init(&list_lock);
+ php_ctlr = (struct php_ctlr_state_s *) kmalloc(sizeof(struct php_ctlr_state_s), GFP_KERNEL);
+
+ if (!php_ctlr) { /* allocate controller state data */
+ err("%s: HPC controller memory allocation error!\n", __FUNCTION__);
+ goto abort;
+ }
+
+ memset(php_ctlr, 0, sizeof(struct php_ctlr_state_s));
+
+ pdev = dev->port;
+ php_ctlr->pci_dev = pdev; /* save pci_dev in context */
+
+ dbg("%s: pdev->vendor %x pdev->device %x\n", __FUNCTION__,
+ pdev->vendor, pdev->device);
+
+ saved_cap_base = pcie_cap_base;
+
+ if ((cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP)) == 0) {
+ dbg("%s: Can't find PCI_CAP_ID_EXP (0x10)\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+
+ pcie_cap_base = cap_base;
+
+ dbg("%s: pcie_cap_base %x\n", __FUNCTION__, pcie_cap_base);
+
+ rc = hp_register_read_word(pdev, CAP_REG, cap_reg);
+ if (rc) {
+ err("%s : hp_register_read_word CAP_REG failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ dbg("%s: CAP_REG offset %x cap_reg %x\n", __FUNCTION__, CAP_REG, cap_reg);
+
+ if (((cap_reg & SLOT_IMPL) == 0) || ((cap_reg & DEV_PORT_TYPE) != 0x0040)){
+ dbg("%s : This is not a root port or the port is not connected to a slot\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+
+ rc = hp_register_read_dword(php_ctlr->pci_dev, SLOT_CAP, slot_cap);
+ if (rc) {
+ err("%s : hp_register_read_word CAP_REG failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ dbg("%s: SLOT_CAP offset %x slot_cap %x\n", __FUNCTION__, SLOT_CAP, slot_cap);
+
+ if (!(slot_cap & HP_CAP)) {
+ dbg("%s : This slot is not hot-plug capable\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ /* For debugging purpose */
+ rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ dbg("%s: SLOT_STATUS offset %x slot_status %x\n", __FUNCTION__, SLOT_STATUS, slot_status);
+
+ rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ dbg("%s: SLOT_CTRL offset %x slot_ctrl %x\n", __FUNCTION__, SLOT_CTRL, slot_ctrl);
+
+ if (first) {
+ spin_lock_init(&hpc_event_lock);
+ first = 0;
+ }
+
+ dbg("pdev = %p: b:d:f:irq=0x%x:%x:%x:%x\n", pdev, pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), dev->irq);
+ for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++)
+ if (pci_resource_len(pdev, rc) > 0)
+ dbg("pci resource[%d] start=0x%lx(len=0x%lx)\n", rc,
+ pci_resource_start(pdev, rc), pci_resource_len(pdev, rc));
+
+ info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", pdev->vendor, pdev->device,
+ pdev->subsystem_vendor, pdev->subsystem_device);
+
+ if (pci_enable_device(pdev))
+ goto abort_free_ctlr;
+
+ init_MUTEX(&ctrl->crit_sect);
+ /* setup wait queue */
+ init_waitqueue_head(&ctrl->queue);
+
+ /* find the IRQ */
+ php_ctlr->irq = dev->irq;
+ dbg("HPC interrupt = %d\n", php_ctlr->irq);
+
+ /* Save interrupt callback info */
+ php_ctlr->attention_button_callback = attention_button_callback;
+ php_ctlr->switch_change_callback = switch_change_callback;
+ php_ctlr->presence_change_callback = presence_change_callback;
+ php_ctlr->power_fault_callback = power_fault_callback;
+ php_ctlr->callback_instance_id = instance_id;
+
+ /* return PCI Controller Info */
+ php_ctlr->slot_device_offset = 0;
+ php_ctlr->num_slots = 1;
+
+ /* Mask Hot-plug Interrupt Enable */
+ rc = hp_register_read_word(pdev, SLOT_CTRL, temp_word);
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+
+ dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL, temp_word);
+ temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) | 0x00;
+
+ rc = hp_register_write_word(pdev, SLOT_CTRL, temp_word);
+ if (rc) {
+ err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ dbg("%s : Mask HPIE hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, temp_word);
+
+ rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ dbg("%s: Mask HPIE SLOT_STATUS offset %x reads slot_status %x\n", __FUNCTION__, SLOT_STATUS, slot_status);
+
+ temp_word = 0x1F; /* Clear all events */
+ rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word);
+ if (rc) {
+ err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ dbg("%s: SLOT_STATUS offset %x writes slot_status %x\n", __FUNCTION__, SLOT_STATUS, temp_word);
+
+ if (pciehp_poll_mode) {/* Install interrupt polling code */
+ /* Install and start the interrupt polling timer */
+ init_timer(&php_ctlr->int_poll_timer);
+ start_int_poll_timer( php_ctlr, 10 ); /* start with 10 second delay */
+ } else {
+ /* Installs the interrupt handler */
+ rc = request_irq(php_ctlr->irq, pcie_isr, SA_SHIRQ, MY_NAME, (void *) ctrl);
+ dbg("%s: request_irq %d for hpc%d (returns %d)\n", __FUNCTION__, php_ctlr->irq, ctlr_seq_num, rc);
+ if (rc) {
+ err("Can't get irq %d for the hotplug controller\n", php_ctlr->irq);
+ goto abort_free_ctlr;
+ }
+ }
+
+ rc = hp_register_read_word(pdev, SLOT_CTRL, temp_word);
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL, temp_word);
+ dbg("%s: slot_cap %x\n", __FUNCTION__, slot_cap);
+
+ intr_enable = intr_enable | PRSN_DETECT_ENABLE;
+
+ if (ATTN_BUTTN(slot_cap))
+ intr_enable = intr_enable | ATTN_BUTTN_ENABLE;
+
+ if (POWER_CTRL(slot_cap))
+ intr_enable = intr_enable | PWR_FAULT_DETECT_ENABLE;
+
+ if (MRL_SENS(slot_cap))
+ intr_enable = intr_enable | MRL_DETECT_ENABLE;
+
+ temp_word = (temp_word & ~intr_enable) | intr_enable;
+
+ if (pciehp_poll_mode) {
+ temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x0;
+ } else {
+ temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE;
+ }
+ dbg("%s: temp_word %x\n", __FUNCTION__, temp_word);
+
+ /* Unmask Hot-plug Interrupt Enable for the interrupt notification mechanism case */
+ rc = hp_register_write_word(pdev, SLOT_CTRL, temp_word);
+ if (rc) {
+ err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ dbg("%s : Unmask HPIE hp_register_write_word SLOT_CTRL with %x\n", __FUNCTION__, temp_word);
+ rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
+ if (rc) {
+ err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ dbg("%s: Unmask HPIE SLOT_STATUS offset %x reads slot_status %x\n", __FUNCTION__,
+ SLOT_STATUS, slot_status);
+
+ temp_word = 0x1F; /* Clear all events */
+ rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word);
+ if (rc) {
+ err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ dbg("%s: SLOT_STATUS offset %x writes slot_status %x\n", __FUNCTION__, SLOT_STATUS, temp_word);
+
+ /* Add this HPC instance into the HPC list */
+ spin_lock(&list_lock);
+ if (php_ctlr_list_head == 0) {
+ php_ctlr_list_head = php_ctlr;
+ p = php_ctlr_list_head;
+ p->pnext = NULL;
+ } else {
+ p = php_ctlr_list_head;
+
+ while (p->pnext)
+ p = p->pnext;
+
+ p->pnext = php_ctlr;
+ }
+ spin_unlock(&list_lock);
+
+ ctlr_seq_num++;
+ ctrl->hpc_ctlr_handle = php_ctlr;
+ ctrl->hpc_ops = &pciehp_hpc_ops;
+
+ DBG_LEAVE_ROUTINE
+ return 0;
+
+ /* We end up here for the many possible ways to fail this API. */
+abort_free_ctlr:
+ pcie_cap_base = saved_cap_base;
+ kfree(php_ctlr);
+abort:
+ DBG_LEAVE_ROUTINE
+ return -1;
+}
diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c
new file mode 100644
index 000000000000..723b12c0bb7c
--- /dev/null
+++ b/drivers/pci/hotplug/pciehp_pci.c
@@ -0,0 +1,827 @@
+/*
+ * PCI Express Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include "../pci.h"
+#include "pciehp.h"
+#ifndef CONFIG_IA64
+#include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependant we are... */
+#endif
+
+
+int pciehp_configure_device (struct controller* ctrl, struct pci_func* func)
+{
+ unsigned char bus;
+ struct pci_bus *child;
+ int num;
+
+ if (func->pci_dev == NULL)
+ func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
+
+ /* Still NULL ? Well then scan for it ! */
+ if (func->pci_dev == NULL) {
+ dbg("%s: pci_dev still null. do pci_scan_slot\n", __FUNCTION__);
+
+ num = pci_scan_slot(ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function));
+
+ if (num)
+ pci_bus_add_devices(ctrl->pci_dev->subordinate);
+
+ func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
+ if (func->pci_dev == NULL) {
+ dbg("ERROR: pci_dev still null\n");
+ return 0;
+ }
+ }
+
+ if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus);
+ child = pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus);
+ pci_do_scan_bus(child);
+
+ }
+
+ return 0;
+}
+
+
+int pciehp_unconfigure_device(struct pci_func* func)
+{
+ int rc = 0;
+ int j;
+ struct pci_bus *pbus;
+
+ dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus,
+ func->device, func->function);
+ pbus = func->pci_dev->bus;
+
+ for (j=0; j<8 ; j++) {
+ struct pci_dev* temp = pci_find_slot(func->bus,
+ (func->device << 3) | j);
+ if (temp) {
+ pci_remove_bus_device(temp);
+ }
+ }
+ /*
+ * Some PCI Express root ports require fixup after hot-plug operation.
+ */
+ if (pcie_mch_quirk)
+ pci_fixup_device(pci_fixup_final, pbus->self);
+
+ return rc;
+}
+
+/*
+ * pciehp_set_irq
+ *
+ * @bus_num: bus number of PCI device
+ * @dev_num: device number of PCI device
+ * @slot: pointer to u8 where slot number will be returned
+ */
+int pciehp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num)
+{
+#if defined(CONFIG_X86) && !defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_64)
+ int rc;
+ u16 temp_word;
+ struct pci_dev fakedev;
+ struct pci_bus fakebus;
+
+ fakedev.devfn = dev_num << 3;
+ fakedev.bus = &fakebus;
+ fakebus.number = bus_num;
+ dbg("%s: dev %d, bus %d, pin %d, num %d\n",
+ __FUNCTION__, dev_num, bus_num, int_pin, irq_num);
+ rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num);
+ dbg("%s: rc %d\n", __FUNCTION__, rc);
+ if (!rc)
+ return !rc;
+
+ /* set the Edge Level Control Register (ELCR) */
+ temp_word = inb(0x4d0);
+ temp_word |= inb(0x4d1) << 8;
+
+ temp_word |= 0x01 << irq_num;
+
+ /* This should only be for x86 as it sets the Edge Level Control Register */
+ outb((u8) (temp_word & 0xFF), 0x4d0);
+ outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1);
+#endif
+ return 0;
+}
+
+/* More PCI configuration routines; this time centered around hotplug controller */
+
+
+/*
+ * pciehp_save_config
+ *
+ * Reads configuration for all slots in a PCI bus and saves info.
+ *
+ * Note: For non-hot plug busses, the slot # saved is the device #
+ *
+ * returns 0 if success
+ */
+int pciehp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num)
+{
+ int rc;
+ u8 class_code;
+ u8 header_type;
+ u32 ID;
+ u8 secondary_bus;
+ struct pci_func *new_slot;
+ int sub_bus;
+ int max_functions;
+ int function;
+ u8 DevError;
+ int device = 0;
+ int cloop = 0;
+ int stop_it;
+ int index;
+ int is_hot_plug = num_ctlr_slots || first_device_num;
+ struct pci_bus lpci_bus, *pci_bus;
+ int FirstSupported, LastSupported;
+
+ dbg("%s: Enter\n", __FUNCTION__);
+
+ memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+
+ dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__,
+ num_ctlr_slots, first_device_num);
+
+ /* Decide which slots are supported */
+ if (is_hot_plug) {
+ /*********************************
+ * is_hot_plug is the slot mask
+ *********************************/
+ FirstSupported = first_device_num;
+ LastSupported = FirstSupported + num_ctlr_slots - 1;
+ } else {
+ FirstSupported = 0;
+ LastSupported = 0x1F;
+ }
+
+ dbg("FirstSupported = %d, LastSupported = %d\n", FirstSupported,
+ LastSupported);
+
+ /* Save PCI configuration space for all devices in supported slots */
+ dbg("%s: pci_bus->number = %x\n", __FUNCTION__, pci_bus->number);
+ pci_bus->number = busnumber;
+ dbg("%s: bus = %x, dev = %x\n", __FUNCTION__, busnumber, device);
+ for (device = FirstSupported; device <= LastSupported; device++) {
+ ID = 0xFFFFFFFF;
+ rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0),
+ PCI_VENDOR_ID, &ID);
+
+ if (ID != 0xFFFFFFFF) { /* device in slot */
+ dbg("%s: ID = %x\n", __FUNCTION__, ID);
+ rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0),
+ 0x0B, &class_code);
+ if (rc)
+ return rc;
+
+ rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0),
+ PCI_HEADER_TYPE, &header_type);
+ if (rc)
+ return rc;
+
+ dbg("class_code = %x, header_type = %x\n", class_code, header_type);
+
+ /* If multi-function device, set max_functions to 8 */
+ if (header_type & 0x80)
+ max_functions = 8;
+ else
+ max_functions = 1;
+
+ function = 0;
+
+ do {
+ DevError = 0;
+ dbg("%s: In do loop\n", __FUNCTION__);
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* P-P Bridge */
+ /* Recurse the subordinate bus
+ * get the subordinate bus number
+ */
+ rc = pci_bus_read_config_byte(pci_bus,
+ PCI_DEVFN(device, function),
+ PCI_SECONDARY_BUS, &secondary_bus);
+ if (rc) {
+ return rc;
+ } else {
+ sub_bus = (int) secondary_bus;
+
+ /* Save secondary bus cfg spc with this recursive call. */
+ rc = pciehp_save_config(ctrl, sub_bus, 0, 0);
+ if (rc)
+ return rc;
+ }
+ }
+
+ index = 0;
+ new_slot = pciehp_slot_find(busnumber, device, index++);
+
+ dbg("%s: new_slot = %p bus %x dev %x fun %x\n",
+ __FUNCTION__, new_slot, busnumber, device, index-1);
+
+ while (new_slot && (new_slot->function != (u8) function)) {
+ new_slot = pciehp_slot_find(busnumber, device, index++);
+ dbg("%s: while loop, new_slot = %p bus %x dev %x fun %x\n",
+ __FUNCTION__, new_slot, busnumber, device, index-1);
+ }
+ if (!new_slot) {
+ /* Setup slot structure. */
+ new_slot = pciehp_slot_create(busnumber);
+ dbg("%s: if, new_slot = %p bus %x dev %x fun %x\n",
+ __FUNCTION__, new_slot, busnumber, device, function);
+
+ if (new_slot == NULL)
+ return(1);
+ }
+
+ new_slot->bus = (u8) busnumber;
+ new_slot->device = (u8) device;
+ new_slot->function = (u8) function;
+ new_slot->is_a_board = 1;
+ new_slot->switch_save = 0x10;
+ /* In case of unsupported board */
+ new_slot->status = DevError;
+ new_slot->pci_dev = pci_find_slot(new_slot->bus,
+ (new_slot->device << 3) | new_slot->function);
+ dbg("new_slot->pci_dev = %p\n", new_slot->pci_dev);
+
+ for (cloop = 0; cloop < 0x20; cloop++) {
+ rc = pci_bus_read_config_dword(pci_bus,
+ PCI_DEVFN(device, function),
+ cloop << 2,
+ (u32 *) &(new_slot->config_space [cloop]));
+ /* dbg("new_slot->config_space[%x] = %x\n",
+ cloop, new_slot->config_space[cloop]); */
+ if (rc)
+ return rc;
+ }
+
+ function++;
+
+ stop_it = 0;
+
+ /* this loop skips to the next present function
+ * reading in Class Code and Header type.
+ */
+
+ while ((function < max_functions)&&(!stop_it)) {
+ dbg("%s: In while loop \n", __FUNCTION__);
+ rc = pci_bus_read_config_dword(pci_bus,
+ PCI_DEVFN(device, function),
+ PCI_VENDOR_ID, &ID);
+
+ if (ID == 0xFFFFFFFF) { /* nothing there. */
+ function++;
+ dbg("Nothing there\n");
+ } else { /* Something there */
+ rc = pci_bus_read_config_byte(pci_bus,
+ PCI_DEVFN(device, function),
+ 0x0B, &class_code);
+ if (rc)
+ return rc;
+
+ rc = pci_bus_read_config_byte(pci_bus,
+ PCI_DEVFN(device, function),
+ PCI_HEADER_TYPE, &header_type);
+ if (rc)
+ return rc;
+
+ dbg("class_code = %x, header_type = %x\n", class_code, header_type);
+ stop_it++;
+ }
+ }
+
+ } while (function < max_functions);
+ /* End of IF (device in slot?) */
+ } else if (is_hot_plug) {
+ /* Setup slot structure with entry for empty slot */
+ new_slot = pciehp_slot_create(busnumber);
+
+ if (new_slot == NULL) {
+ return(1);
+ }
+ dbg("new_slot = %p, bus = %x, dev = %x, fun = %x\n", new_slot,
+ new_slot->bus, new_slot->device, new_slot->function);
+
+ new_slot->bus = (u8) busnumber;
+ new_slot->device = (u8) device;
+ new_slot->function = 0;
+ new_slot->is_a_board = 0;
+ new_slot->presence_save = 0;
+ new_slot->switch_save = 0;
+ }
+ } /* End of FOR loop */
+
+ dbg("%s: Exit\n", __FUNCTION__);
+ return(0);
+}
+
+
+/*
+ * pciehp_save_slot_config
+ *
+ * Saves configuration info for all PCI devices in a given slot
+ * including subordinate busses.
+ *
+ * returns 0 if success
+ */
+int pciehp_save_slot_config(struct controller *ctrl, struct pci_func * new_slot)
+{
+ int rc;
+ u8 class_code;
+ u8 header_type;
+ u32 ID;
+ u8 secondary_bus;
+ int sub_bus;
+ int max_functions;
+ int function;
+ int cloop = 0;
+ int stop_it;
+ struct pci_bus lpci_bus, *pci_bus;
+ memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = new_slot->bus;
+
+ ID = 0xFFFFFFFF;
+
+ pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, 0),
+ PCI_VENDOR_ID, &ID);
+
+ if (ID != 0xFFFFFFFF) { /* device in slot */
+ pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0),
+ 0x0B, &class_code);
+
+ pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0),
+ PCI_HEADER_TYPE, &header_type);
+
+ if (header_type & 0x80) /* Multi-function device */
+ max_functions = 8;
+ else
+ max_functions = 1;
+
+ function = 0;
+
+ do {
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
+ /* Recurse the subordinate bus */
+ pci_bus_read_config_byte(pci_bus,
+ PCI_DEVFN(new_slot->device, function),
+ PCI_SECONDARY_BUS, &secondary_bus);
+
+ sub_bus = (int) secondary_bus;
+
+ /* Save the config headers for the secondary bus. */
+ rc = pciehp_save_config(ctrl, sub_bus, 0, 0);
+
+ if (rc)
+ return rc;
+
+ } /* End of IF */
+
+ new_slot->status = 0;
+
+ for (cloop = 0; cloop < 0x20; cloop++) {
+ pci_bus_read_config_dword(pci_bus,
+ PCI_DEVFN(new_slot->device, function),
+ cloop << 2,
+ (u32 *) &(new_slot->config_space [cloop]));
+ }
+
+ function++;
+
+ stop_it = 0;
+
+ /* this loop skips to the next present function
+ * reading in the Class Code and the Header type.
+ */
+
+ while ((function < max_functions) && (!stop_it)) {
+ pci_bus_read_config_dword(pci_bus,
+ PCI_DEVFN(new_slot->device, function),
+ PCI_VENDOR_ID, &ID);
+
+ if (ID == 0xFFFFFFFF) { /* nothing there. */
+ function++;
+ } else { /* Something there */
+ pci_bus_read_config_byte(pci_bus,
+ PCI_DEVFN(new_slot->device, function),
+ 0x0B, &class_code);
+
+ pci_bus_read_config_byte(pci_bus,
+ PCI_DEVFN(new_slot->device, function),
+ PCI_HEADER_TYPE, &header_type);
+
+ stop_it++;
+ }
+ }
+
+ } while (function < max_functions);
+ } /* End of IF (device in slot?) */
+ else {
+ return 2;
+ }
+
+ return 0;
+}
+
+
+/*
+ * pciehp_save_used_resources
+ *
+ * Stores used resource information for existing boards. this is
+ * for boards that were in the system when this driver was loaded.
+ * this function is for hot plug ADD
+ *
+ * returns 0 if success
+ * if disable == 1(DISABLE_CARD),
+ * it loops for all functions of the slot and disables them.
+ * else, it just get resources of the function and return.
+ */
+int pciehp_save_used_resources(struct controller *ctrl, struct pci_func *func, int disable)
+{
+ u8 cloop;
+ u8 header_type;
+ u8 secondary_bus;
+ u8 temp_byte;
+ u16 command;
+ u16 save_command;
+ u16 w_base, w_length;
+ u32 temp_register;
+ u32 save_base;
+ u32 base, length;
+ u64 base64 = 0;
+ int index = 0;
+ unsigned int devfn;
+ struct pci_resource *mem_node = NULL;
+ struct pci_resource *p_mem_node = NULL;
+ struct pci_resource *t_mem_node;
+ struct pci_resource *io_node;
+ struct pci_resource *bus_node;
+ struct pci_bus lpci_bus, *pci_bus;
+ memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+
+ if (disable)
+ func = pciehp_slot_find(func->bus, func->device, index++);
+
+ while ((func != NULL) && func->is_a_board) {
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ /* Save the command register */
+ pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command);
+
+ if (disable) {
+ /* disable card */
+ command = 0x00;
+ pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
+ }
+
+ /* Check for Bridge */
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
+ dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n",
+ func->bus, func->device, save_command);
+ if (disable) {
+ /* Clear Bridge Control Register */
+ command = 0x00;
+ pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
+ }
+
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte);
+
+ bus_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+ if (!bus_node)
+ return -ENOMEM;
+
+ bus_node->base = (ulong)secondary_bus;
+ bus_node->length = (ulong)(temp_byte - secondary_bus + 1);
+
+ bus_node->next = func->bus_head;
+ func->bus_head = bus_node;
+
+ /* Save IO base and Limit registers */
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &temp_byte);
+ base = temp_byte;
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &temp_byte);
+ length = temp_byte;
+
+ if ((base <= length) && (!disable || (save_command & PCI_COMMAND_IO))) {
+ io_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+ if (!io_node)
+ return -ENOMEM;
+
+ io_node->base = (ulong)(base & PCI_IO_RANGE_MASK) << 8;
+ io_node->length = (ulong)(length - base + 0x10) << 8;
+
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ }
+
+ /* Save memory base and Limit registers */
+ pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base);
+ pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length);
+
+ if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
+ mem_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+ if (!mem_node)
+ return -ENOMEM;
+
+ mem_node->base = (ulong)w_base << 16;
+ mem_node->length = (ulong)(w_length - w_base + 0x10) << 16;
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ }
+ /* Save prefetchable memory base and Limit registers */
+ pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base);
+ pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length);
+
+ if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
+ p_mem_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+ if (!p_mem_node)
+ return -ENOMEM;
+
+ p_mem_node->base = (ulong)w_base << 16;
+ p_mem_node->length = (ulong)(w_length - w_base + 0x10) << 16;
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ }
+ } else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
+ dbg("Save_used_res of PCI adapter b:d=0x%x:%x, sc=0x%x\n",
+ func->bus, func->device, save_command);
+
+ /* Figure out IO and memory base lengths */
+ for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) {
+ pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base);
+
+ temp_register = 0xFFFFFFFF;
+ pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register);
+ pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register);
+
+ if (!disable)
+ pci_bus_write_config_dword(pci_bus, devfn, cloop, save_base);
+
+ if (!temp_register)
+ continue;
+
+ base = temp_register;
+
+ if ((base & PCI_BASE_ADDRESS_SPACE_IO) &&
+ (!disable || (save_command & PCI_COMMAND_IO))) {
+ /* IO base */
+ /* set temp_register = amount of IO space requested */
+ base = base & 0xFFFFFFFCL;
+ base = (~base) + 1;
+
+ io_node = kmalloc(sizeof (struct pci_resource),
+ GFP_KERNEL);
+ if (!io_node)
+ return -ENOMEM;
+
+ io_node->base = (ulong)save_base & PCI_BASE_ADDRESS_IO_MASK;
+ io_node->length = (ulong)base;
+ dbg("sur adapter: IO bar=0x%x(length=0x%x)\n",
+ io_node->base, io_node->length);
+
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ } else { /* map Memory */
+ int prefetchable = 1;
+ /* struct pci_resources **res_node; */
+ char *res_type_str = "PMEM";
+ u32 temp_register2;
+
+ t_mem_node = kmalloc(sizeof (struct pci_resource),
+ GFP_KERNEL);
+ if (!t_mem_node)
+ return -ENOMEM;
+
+ if (!(base & PCI_BASE_ADDRESS_MEM_PREFETCH) &&
+ (!disable || (save_command & PCI_COMMAND_MEMORY))) {
+ prefetchable = 0;
+ mem_node = t_mem_node;
+ res_type_str++;
+ } else
+ p_mem_node = t_mem_node;
+
+ base = base & 0xFFFFFFF0L;
+ base = (~base) + 1;
+
+ switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
+ case PCI_BASE_ADDRESS_MEM_TYPE_32:
+ if (prefetchable) {
+ p_mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK;
+ p_mem_node->length = (ulong)base;
+ dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n",
+ res_type_str,
+ p_mem_node->base,
+ p_mem_node->length);
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ } else {
+ mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK;
+ mem_node->length = (ulong)base;
+ dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n",
+ res_type_str,
+ mem_node->base,
+ mem_node->length);
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ }
+ break;
+ case PCI_BASE_ADDRESS_MEM_TYPE_64:
+ pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2);
+ base64 = temp_register2;
+ base64 = (base64 << 32) | save_base;
+
+ if (temp_register2) {
+ dbg("sur adapter: 64 %s high dword of base64(0x%x:%x) masked to 0\n",
+ res_type_str, temp_register2, (u32)base64);
+ base64 &= 0x00000000FFFFFFFFL;
+ }
+
+ if (prefetchable) {
+ p_mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK;
+ p_mem_node->length = base;
+ dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n",
+ res_type_str,
+ p_mem_node->base,
+ p_mem_node->length);
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ } else {
+ mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK;
+ mem_node->length = base;
+ dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n",
+ res_type_str,
+ mem_node->base,
+ mem_node->length);
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ }
+ cloop += 4;
+ break;
+ default:
+ dbg("asur: reserved BAR type=0x%x\n",
+ temp_register);
+ break;
+ }
+ }
+ } /* End of base register loop */
+ } else { /* Some other unknown header type */
+ dbg("Save_used_res of PCI unknown type b:d=0x%x:%x. skip.\n",
+ func->bus, func->device);
+ }
+
+ /* find the next device in this slot */
+ if (!disable)
+ break;
+ func = pciehp_slot_find(func->bus, func->device, index++);
+ }
+
+ return 0;
+}
+
+
+/**
+ * kfree_resource_list: release memory of all list members
+ * @res: resource list to free
+ */
+static inline void
+return_resource_list(struct pci_resource **func, struct pci_resource **res)
+{
+ struct pci_resource *node;
+ struct pci_resource *t_node;
+
+ node = *func;
+ *func = NULL;
+ while (node) {
+ t_node = node->next;
+ return_resource(res, node);
+ node = t_node;
+ }
+}
+
+/*
+ * pciehp_return_board_resources
+ *
+ * this routine returns all resources allocated to a board to
+ * the available pool.
+ *
+ * returns 0 if success
+ */
+int pciehp_return_board_resources(struct pci_func * func,
+ struct resource_lists * resources)
+{
+ int rc;
+
+ dbg("%s\n", __FUNCTION__);
+
+ if (!func)
+ return 1;
+
+ return_resource_list(&(func->io_head),&(resources->io_head));
+ return_resource_list(&(func->mem_head),&(resources->mem_head));
+ return_resource_list(&(func->p_mem_head),&(resources->p_mem_head));
+ return_resource_list(&(func->bus_head),&(resources->bus_head));
+
+ rc = pciehp_resource_sort_and_combine(&(resources->mem_head));
+ rc |= pciehp_resource_sort_and_combine(&(resources->p_mem_head));
+ rc |= pciehp_resource_sort_and_combine(&(resources->io_head));
+ rc |= pciehp_resource_sort_and_combine(&(resources->bus_head));
+
+ return rc;
+}
+
+/**
+ * kfree_resource_list: release memory of all list members
+ * @res: resource list to free
+ */
+static inline void
+kfree_resource_list(struct pci_resource **r)
+{
+ struct pci_resource *res, *tres;
+
+ res = *r;
+ *r = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+}
+
+/**
+ * pciehp_destroy_resource_list: put node back in the resource list
+ * @resources: list to put nodes back
+ */
+void pciehp_destroy_resource_list(struct resource_lists * resources)
+{
+ kfree_resource_list(&(resources->io_head));
+ kfree_resource_list(&(resources->mem_head));
+ kfree_resource_list(&(resources->p_mem_head));
+ kfree_resource_list(&(resources->bus_head));
+}
+
+/**
+ * pciehp_destroy_board_resources: put node back in the resource list
+ * @resources: list to put nodes back
+ */
+void pciehp_destroy_board_resources(struct pci_func * func)
+{
+ kfree_resource_list(&(func->io_head));
+ kfree_resource_list(&(func->mem_head));
+ kfree_resource_list(&(func->p_mem_head));
+ kfree_resource_list(&(func->bus_head));
+}
diff --git a/drivers/pci/hotplug/pciehprm.h b/drivers/pci/hotplug/pciehprm.h
new file mode 100644
index 000000000000..966775ffb0ff
--- /dev/null
+++ b/drivers/pci/hotplug/pciehprm.h
@@ -0,0 +1,52 @@
+/*
+ * PCIEHPRM : PCIEHP Resource Manager for ACPI/non-ACPI platform
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+
+#ifndef _PCIEHPRM_H_
+#define _PCIEHPRM_H_
+
+#ifdef CONFIG_HOTPLUG_PCI_PCIE_PHPRM_NONACPI
+#include "pciehprm_nonacpi.h"
+#endif
+
+int pciehprm_init(enum php_ctlr_type ct);
+void pciehprm_cleanup(void);
+int pciehprm_print_pirt(void);
+int pciehprm_find_available_resources(struct controller *ctrl);
+int pciehprm_set_hpp(struct controller *ctrl, struct pci_func *func, u8 card_type);
+void pciehprm_enable_card(struct controller *ctrl, struct pci_func *func, u8 card_type);
+
+#ifdef DEBUG
+#define RES_CHECK(this, bits) \
+ { if (((this) & (bits - 1))) \
+ printk("%s:%d ERR: potential res loss!\n", __FUNCTION__, __LINE__); }
+#else
+#define RES_CHECK(this, bits)
+#endif
+
+#endif /* _PCIEHPRM_H_ */
diff --git a/drivers/pci/hotplug/pciehprm_acpi.c b/drivers/pci/hotplug/pciehprm_acpi.c
new file mode 100644
index 000000000000..57f4e6d1b27c
--- /dev/null
+++ b/drivers/pci/hotplug/pciehprm_acpi.c
@@ -0,0 +1,1737 @@
+/*
+ * PCIEHPRM ACPI: PHP Resource Manager for ACPI platform
+ *
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <dely.l.sy@intel.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/efi.h>
+#include <linux/pci-acpi.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#ifdef CONFIG_IA64
+#include <asm/iosapic.h>
+#endif
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/actypes.h>
+#include "pciehp.h"
+#include "pciehprm.h"
+
+#define PCI_MAX_BUS 0x100
+#define ACPI_STA_DEVICE_PRESENT 0x01
+
+#define METHOD_NAME__SUN "_SUN"
+#define METHOD_NAME__HPP "_HPP"
+#define METHOD_NAME_OSHP "OSHP"
+
+/* Status code for running acpi method to gain native control */
+#define NC_NOT_RUN 0
+#define OSC_NOT_EXIST 1
+#define OSC_RUN_FAILED 2
+#define OSHP_NOT_EXIST 3
+#define OSHP_RUN_FAILED 4
+#define NC_RUN_SUCCESS 5
+
+#define PHP_RES_BUS 0xA0
+#define PHP_RES_IO 0xA1
+#define PHP_RES_MEM 0xA2
+#define PHP_RES_PMEM 0xA3
+
+#define BRIDGE_TYPE_P2P 0x00
+#define BRIDGE_TYPE_HOST 0x01
+
+/* this should go to drivers/acpi/include/ */
+struct acpi__hpp {
+ u8 cache_line_size;
+ u8 latency_timer;
+ u8 enable_serr;
+ u8 enable_perr;
+};
+
+struct acpi_php_slot {
+ struct acpi_php_slot *next;
+ struct acpi_bridge *bridge;
+ acpi_handle handle;
+ int seg;
+ int bus;
+ int dev;
+ int fun;
+ u32 sun;
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ void *slot_ops; /* _STA, _EJx, etc */
+ struct slot *slot;
+}; /* per func */
+
+struct acpi_bridge {
+ struct acpi_bridge *parent;
+ struct acpi_bridge *next;
+ struct acpi_bridge *child;
+ acpi_handle handle;
+ int seg;
+ int pbus; /* pdev->bus->number */
+ int pdevice; /* PCI_SLOT(pdev->devfn) */
+ int pfunction; /* PCI_DEVFN(pdev->devfn) */
+ int bus; /* pdev->subordinate->number */
+ struct acpi__hpp *_hpp;
+ struct acpi_php_slot *slots;
+ struct pci_resource *tmem_head; /* total from crs */
+ struct pci_resource *tp_mem_head; /* total from crs */
+ struct pci_resource *tio_head; /* total from crs */
+ struct pci_resource *tbus_head; /* total from crs */
+ struct pci_resource *mem_head; /* available */
+ struct pci_resource *p_mem_head; /* available */
+ struct pci_resource *io_head; /* available */
+ struct pci_resource *bus_head; /* available */
+ int scanned;
+ int type;
+};
+
+static struct acpi_bridge *acpi_bridges_head;
+
+static u8 * acpi_path_name( acpi_handle handle)
+{
+ acpi_status status;
+ static u8 path_name[ACPI_PATHNAME_MAX];
+ struct acpi_buffer ret_buf = { ACPI_PATHNAME_MAX, path_name };
+
+ memset(path_name, 0, sizeof (path_name));
+ status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &ret_buf);
+
+ if (ACPI_FAILURE(status))
+ return NULL;
+ else
+ return path_name;
+}
+
+static void acpi_get__hpp ( struct acpi_bridge *ab);
+static int acpi_run_oshp ( struct acpi_bridge *ab);
+static int osc_run_status = NC_NOT_RUN;
+static int oshp_run_status = NC_NOT_RUN;
+
+static int acpi_add_slot_to_php_slots(
+ struct acpi_bridge *ab,
+ int bus_num,
+ acpi_handle handle,
+ u32 adr,
+ u32 sun
+ )
+{
+ struct acpi_php_slot *aps;
+ static long samesun = -1;
+
+ aps = (struct acpi_php_slot *) kmalloc (sizeof(struct acpi_php_slot), GFP_KERNEL);
+ if (!aps) {
+ err ("acpi_pciehprm: alloc for aps fail\n");
+ return -1;
+ }
+ memset(aps, 0, sizeof(struct acpi_php_slot));
+
+ aps->handle = handle;
+ aps->bus = bus_num;
+ aps->dev = (adr >> 16) & 0xffff;
+ aps->fun = adr & 0xffff;
+ aps->sun = sun;
+
+ aps->next = ab->slots; /* cling to the bridge */
+ aps->bridge = ab;
+ ab->slots = aps;
+
+ ab->scanned += 1;
+ if (!ab->_hpp)
+ acpi_get__hpp(ab);
+
+ if (osc_run_status == OSC_NOT_EXIST)
+ oshp_run_status = acpi_run_oshp(ab);
+
+ if (sun != samesun) {
+ info("acpi_pciehprm: Slot sun(%x) at s:b:d:f=0x%02x:%02x:%02x:%02x\n",
+ aps->sun, ab->seg, aps->bus, aps->dev, aps->fun);
+ samesun = sun;
+ }
+ return 0;
+}
+
+static void acpi_get__hpp ( struct acpi_bridge *ab)
+{
+ acpi_status status;
+ u8 nui[4];
+ struct acpi_buffer ret_buf = { 0, NULL};
+ union acpi_object *ext_obj, *package;
+ u8 *path_name = acpi_path_name(ab->handle);
+ int i, len = 0;
+
+ /* get _hpp */
+ status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf);
+ switch (status) {
+ case AE_BUFFER_OVERFLOW:
+ ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL);
+ if (!ret_buf.pointer) {
+ err ("acpi_pciehprm:%s alloc for _HPP fail\n", path_name);
+ return;
+ }
+ status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf);
+ if (ACPI_SUCCESS(status))
+ break;
+ default:
+ if (ACPI_FAILURE(status)) {
+ err("acpi_pciehprm:%s _HPP fail=0x%x\n", path_name, status);
+ return;
+ }
+ }
+
+ ext_obj = (union acpi_object *) ret_buf.pointer;
+ if (ext_obj->type != ACPI_TYPE_PACKAGE) {
+ err ("acpi_pciehprm:%s _HPP obj not a package\n", path_name);
+ goto free_and_return;
+ }
+
+ len = ext_obj->package.count;
+ package = (union acpi_object *) ret_buf.pointer;
+ for ( i = 0; (i < len) || (i < 4); i++) {
+ ext_obj = (union acpi_object *) &package->package.elements[i];
+ switch (ext_obj->type) {
+ case ACPI_TYPE_INTEGER:
+ nui[i] = (u8)ext_obj->integer.value;
+ break;
+ default:
+ err ("acpi_pciehprm:%s _HPP obj type incorrect\n", path_name);
+ goto free_and_return;
+ }
+ }
+
+ ab->_hpp = kmalloc (sizeof (struct acpi__hpp), GFP_KERNEL);
+ if (!ab->_hpp) {
+ err ("acpi_pciehprm:%s alloc for _HPP failed\n", path_name);
+ goto free_and_return;
+ }
+ memset(ab->_hpp, 0, sizeof(struct acpi__hpp));
+
+ ab->_hpp->cache_line_size = nui[0];
+ ab->_hpp->latency_timer = nui[1];
+ ab->_hpp->enable_serr = nui[2];
+ ab->_hpp->enable_perr = nui[3];
+
+ dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size);
+ dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer);
+ dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr);
+ dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr);
+
+free_and_return:
+ kfree(ret_buf.pointer);
+}
+
+static int acpi_run_oshp ( struct acpi_bridge *ab)
+{
+ acpi_status status;
+ u8 *path_name = acpi_path_name(ab->handle);
+
+ /* run OSHP */
+ status = acpi_evaluate_object(ab->handle, METHOD_NAME_OSHP, NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_pciehprm:%s OSHP fails=0x%x\n", path_name, status);
+ oshp_run_status = (status == AE_NOT_FOUND) ? OSHP_NOT_EXIST : OSHP_RUN_FAILED;
+ } else {
+ oshp_run_status = NC_RUN_SUCCESS;
+ dbg("acpi_pciehprm:%s OSHP passes =0x%x\n", path_name, status);
+ dbg("acpi_pciehprm:%s oshp_run_status =0x%x\n", path_name, oshp_run_status);
+ }
+ return oshp_run_status;
+}
+
+static acpi_status acpi_evaluate_crs(
+ acpi_handle handle,
+ struct acpi_resource **retbuf
+ )
+{
+ acpi_status status;
+ struct acpi_buffer crsbuf;
+ u8 *path_name = acpi_path_name(handle);
+
+ crsbuf.length = 0;
+ crsbuf.pointer = NULL;
+
+ status = acpi_get_current_resources (handle, &crsbuf);
+
+ switch (status) {
+ case AE_BUFFER_OVERFLOW:
+ break; /* found */
+ case AE_NOT_FOUND:
+ dbg("acpi_pciehprm:%s _CRS not found\n", path_name);
+ return status;
+ default:
+ err ("acpi_pciehprm:%s _CRS fail=0x%x\n", path_name, status);
+ return status;
+ }
+
+ crsbuf.pointer = kmalloc (crsbuf.length, GFP_KERNEL);
+ if (!crsbuf.pointer) {
+ err ("acpi_pciehprm: alloc %ld bytes for %s _CRS fail\n", (ulong)crsbuf.length, path_name);
+ return AE_NO_MEMORY;
+ }
+
+ status = acpi_get_current_resources (handle, &crsbuf);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_pciehprm: %s _CRS fail=0x%x.\n", path_name, status);
+ kfree(crsbuf.pointer);
+ return status;
+ }
+
+ *retbuf = crsbuf.pointer;
+
+ return status;
+}
+
+static void free_pci_resource ( struct pci_resource *aprh)
+{
+ struct pci_resource *res, *next;
+
+ for (res = aprh; res; res = next) {
+ next = res->next;
+ kfree(res);
+ }
+}
+
+static void print_pci_resource ( struct pci_resource *aprh)
+{
+ struct pci_resource *res;
+
+ for (res = aprh; res; res = res->next)
+ dbg(" base= 0x%x length= 0x%x\n", res->base, res->length);
+}
+
+static void print_slot_resources( struct acpi_php_slot *aps)
+{
+ if (aps->bus_head) {
+ dbg(" BUS Resources:\n");
+ print_pci_resource (aps->bus_head);
+ }
+
+ if (aps->io_head) {
+ dbg(" IO Resources:\n");
+ print_pci_resource (aps->io_head);
+ }
+
+ if (aps->mem_head) {
+ dbg(" MEM Resources:\n");
+ print_pci_resource (aps->mem_head);
+ }
+
+ if (aps->p_mem_head) {
+ dbg(" PMEM Resources:\n");
+ print_pci_resource (aps->p_mem_head);
+ }
+}
+
+static void print_pci_resources( struct acpi_bridge *ab)
+{
+ if (ab->tbus_head) {
+ dbg(" Total BUS Resources:\n");
+ print_pci_resource (ab->tbus_head);
+ }
+ if (ab->bus_head) {
+ dbg(" BUS Resources:\n");
+ print_pci_resource (ab->bus_head);
+ }
+
+ if (ab->tio_head) {
+ dbg(" Total IO Resources:\n");
+ print_pci_resource (ab->tio_head);
+ }
+ if (ab->io_head) {
+ dbg(" IO Resources:\n");
+ print_pci_resource (ab->io_head);
+ }
+
+ if (ab->tmem_head) {
+ dbg(" Total MEM Resources:\n");
+ print_pci_resource (ab->tmem_head);
+ }
+ if (ab->mem_head) {
+ dbg(" MEM Resources:\n");
+ print_pci_resource (ab->mem_head);
+ }
+
+ if (ab->tp_mem_head) {
+ dbg(" Total PMEM Resources:\n");
+ print_pci_resource (ab->tp_mem_head);
+ }
+ if (ab->p_mem_head) {
+ dbg(" PMEM Resources:\n");
+ print_pci_resource (ab->p_mem_head);
+ }
+ if (ab->_hpp) {
+ dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size);
+ dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer);
+ dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr);
+ dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr);
+ }
+}
+
+static int pciehprm_delete_resource(
+ struct pci_resource **aprh,
+ ulong base,
+ ulong size)
+{
+ struct pci_resource *res;
+ struct pci_resource *prevnode;
+ struct pci_resource *split_node;
+ ulong tbase;
+
+ pciehp_resource_sort_and_combine(aprh);
+
+ for (res = *aprh; res; res = res->next) {
+ if (res->base > base)
+ continue;
+
+ if ((res->base + res->length) < (base + size))
+ continue;
+
+ if (res->base < base) {
+ tbase = base;
+
+ if ((res->length - (tbase - res->base)) < size)
+ continue;
+
+ split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!split_node)
+ return -ENOMEM;
+
+ split_node->base = res->base;
+ split_node->length = tbase - res->base;
+ res->base = tbase;
+ res->length -= split_node->length;
+
+ split_node->next = res->next;
+ res->next = split_node;
+ }
+
+ if (res->length >= size) {
+ split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!split_node)
+ return -ENOMEM;
+
+ split_node->base = res->base + size;
+ split_node->length = res->length - size;
+ res->length = size;
+
+ split_node->next = res->next;
+ res->next = split_node;
+ }
+
+ if (*aprh == res) {
+ *aprh = res->next;
+ } else {
+ prevnode = *aprh;
+ while (prevnode->next != res)
+ prevnode = prevnode->next;
+
+ prevnode->next = res->next;
+ }
+ res->next = NULL;
+ kfree(res);
+ break;
+ }
+
+ return 0;
+}
+
+static int pciehprm_delete_resources(
+ struct pci_resource **aprh,
+ struct pci_resource *this
+ )
+{
+ struct pci_resource *res;
+
+ for (res = this; res; res = res->next)
+ pciehprm_delete_resource(aprh, res->base, res->length);
+
+ return 0;
+}
+
+static int pciehprm_add_resource(
+ struct pci_resource **aprh,
+ ulong base,
+ ulong size)
+{
+ struct pci_resource *res;
+
+ for (res = *aprh; res; res = res->next) {
+ if ((res->base + res->length) == base) {
+ res->length += size;
+ size = 0L;
+ break;
+ }
+ if (res->next == *aprh)
+ break;
+ }
+
+ if (size) {
+ res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!res) {
+ err ("acpi_pciehprm: alloc for res fail\n");
+ return -ENOMEM;
+ }
+ memset(res, 0, sizeof (struct pci_resource));
+
+ res->base = base;
+ res->length = size;
+ res->next = *aprh;
+ *aprh = res;
+ }
+
+ return 0;
+}
+
+static int pciehprm_add_resources(
+ struct pci_resource **aprh,
+ struct pci_resource *this
+ )
+{
+ struct pci_resource *res;
+ int rc = 0;
+
+ for (res = this; res && !rc; res = res->next)
+ rc = pciehprm_add_resource(aprh, res->base, res->length);
+
+ return rc;
+}
+
+static void acpi_parse_io (
+ struct acpi_bridge *ab,
+ union acpi_resource_data *data
+ )
+{
+ struct acpi_resource_io *dataio;
+ dataio = (struct acpi_resource_io *) data;
+
+ dbg("Io Resource\n");
+ dbg(" %d bit decode\n", ACPI_DECODE_16 == dataio->io_decode ? 16:10);
+ dbg(" Range minimum base: %08X\n", dataio->min_base_address);
+ dbg(" Range maximum base: %08X\n", dataio->max_base_address);
+ dbg(" Alignment: %08X\n", dataio->alignment);
+ dbg(" Range Length: %08X\n", dataio->range_length);
+}
+
+static void acpi_parse_fixed_io (
+ struct acpi_bridge *ab,
+ union acpi_resource_data *data
+ )
+{
+ struct acpi_resource_fixed_io *datafio;
+ datafio = (struct acpi_resource_fixed_io *) data;
+
+ dbg("Fixed Io Resource\n");
+ dbg(" Range base address: %08X", datafio->base_address);
+ dbg(" Range length: %08X", datafio->range_length);
+}
+
+static void acpi_parse_address16_32 (
+ struct acpi_bridge *ab,
+ union acpi_resource_data *data,
+ acpi_resource_type id
+ )
+{
+ /*
+ * acpi_resource_address16 == acpi_resource_address32
+ * acpi_resource_address16 *data16 = (acpi_resource_address16 *) data;
+ */
+ struct acpi_resource_address32 *data32 = (struct acpi_resource_address32 *) data;
+ struct pci_resource **aprh, **tprh;
+
+ if (id == ACPI_RSTYPE_ADDRESS16)
+ dbg("acpi_pciehprm:16-Bit Address Space Resource\n");
+ else
+ dbg("acpi_pciehprm:32-Bit Address Space Resource\n");
+
+ switch (data32->resource_type) {
+ case ACPI_MEMORY_RANGE:
+ dbg(" Resource Type: Memory Range\n");
+ aprh = &ab->mem_head;
+ tprh = &ab->tmem_head;
+
+ switch (data32->attribute.memory.cache_attribute) {
+ case ACPI_NON_CACHEABLE_MEMORY:
+ dbg(" Type Specific: Noncacheable memory\n");
+ break;
+ case ACPI_CACHABLE_MEMORY:
+ dbg(" Type Specific: Cacheable memory\n");
+ break;
+ case ACPI_WRITE_COMBINING_MEMORY:
+ dbg(" Type Specific: Write-combining memory\n");
+ break;
+ case ACPI_PREFETCHABLE_MEMORY:
+ aprh = &ab->p_mem_head;
+ dbg(" Type Specific: Prefetchable memory\n");
+ break;
+ default:
+ dbg(" Type Specific: Invalid cache attribute\n");
+ break;
+ }
+
+ dbg(" Type Specific: Read%s\n", ACPI_READ_WRITE_MEMORY == data32->attribute.memory.read_write_attribute ? "/Write":" Only");
+ break;
+
+ case ACPI_IO_RANGE:
+ dbg(" Resource Type: I/O Range\n");
+ aprh = &ab->io_head;
+ tprh = &ab->tio_head;
+
+ switch (data32->attribute.io.range_attribute) {
+ case ACPI_NON_ISA_ONLY_RANGES:
+ dbg(" Type Specific: Non-ISA Io Addresses\n");
+ break;
+ case ACPI_ISA_ONLY_RANGES:
+ dbg(" Type Specific: ISA Io Addresses\n");
+ break;
+ case ACPI_ENTIRE_RANGE:
+ dbg(" Type Specific: ISA and non-ISA Io Addresses\n");
+ break;
+ default:
+ dbg(" Type Specific: Invalid range attribute\n");
+ break;
+ }
+ break;
+
+ case ACPI_BUS_NUMBER_RANGE:
+ dbg(" Resource Type: Bus Number Range(fixed)\n");
+ /* fixup to be compatible with the rest of php driver */
+ data32->min_address_range++;
+ data32->address_length--;
+ aprh = &ab->bus_head;
+ tprh = &ab->tbus_head;
+ break;
+ default:
+ dbg(" Resource Type: Invalid resource type. Exiting.\n");
+ return;
+ }
+
+ dbg(" Resource %s\n", ACPI_CONSUMER == data32->producer_consumer ? "Consumer":"Producer");
+ dbg(" %s decode\n", ACPI_SUB_DECODE == data32->decode ? "Subtractive":"Positive");
+ dbg(" Min address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->min_address_fixed ? "":"not");
+ dbg(" Max address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->max_address_fixed ? "":"not");
+ dbg(" Granularity: %08X\n", data32->granularity);
+ dbg(" Address range min: %08X\n", data32->min_address_range);
+ dbg(" Address range max: %08X\n", data32->max_address_range);
+ dbg(" Address translation offset: %08X\n", data32->address_translation_offset);
+ dbg(" Address Length: %08X\n", data32->address_length);
+
+ if (0xFF != data32->resource_source.index) {
+ dbg(" Resource Source Index: %X\n", data32->resource_source.index);
+ /* dbg(" Resource Source: %s\n", data32->resource_source.string_ptr); */
+ }
+
+ pciehprm_add_resource(aprh, data32->min_address_range, data32->address_length);
+}
+
+static acpi_status acpi_parse_crs(
+ struct acpi_bridge *ab,
+ struct acpi_resource *crsbuf
+ )
+{
+ acpi_status status = AE_OK;
+ struct acpi_resource *resource = crsbuf;
+ u8 count = 0;
+ u8 done = 0;
+
+ while (!done) {
+ dbg("acpi_pciehprm: PCI bus 0x%x Resource structure %x.\n", ab->bus, count++);
+ switch (resource->id) {
+ case ACPI_RSTYPE_IRQ:
+ dbg("Irq -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_DMA:
+ dbg("DMA -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_START_DPF:
+ dbg("Start DPF -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_END_DPF:
+ dbg("End DPF -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_IO:
+ acpi_parse_io (ab, &resource->data);
+ break;
+ case ACPI_RSTYPE_FIXED_IO:
+ acpi_parse_fixed_io (ab, &resource->data);
+ break;
+ case ACPI_RSTYPE_VENDOR:
+ dbg("Vendor -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_END_TAG:
+ dbg("End_tag -------- Resource\n");
+ done = 1;
+ break;
+ case ACPI_RSTYPE_MEM24:
+ dbg("Mem24 -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_MEM32:
+ dbg("Mem32 -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_FIXED_MEM32:
+ dbg("Fixed Mem32 -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_ADDRESS16:
+ acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS16);
+ break;
+ case ACPI_RSTYPE_ADDRESS32:
+ acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS32);
+ break;
+ case ACPI_RSTYPE_ADDRESS64:
+ info("Address64 -------- Resource unparsed\n");
+ break;
+ case ACPI_RSTYPE_EXT_IRQ:
+ dbg("Ext Irq -------- Resource\n");
+ break;
+ default:
+ dbg("Invalid -------- resource type 0x%x\n", resource->id);
+ break;
+ }
+
+ resource = (struct acpi_resource *) ((char *)resource + resource->length);
+ }
+
+ return status;
+}
+
+static acpi_status acpi_get_crs( struct acpi_bridge *ab)
+{
+ acpi_status status;
+ struct acpi_resource *crsbuf;
+
+ status = acpi_evaluate_crs(ab->handle, &crsbuf);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_parse_crs(ab, crsbuf);
+ kfree(crsbuf);
+
+ pciehp_resource_sort_and_combine(&ab->bus_head);
+ pciehp_resource_sort_and_combine(&ab->io_head);
+ pciehp_resource_sort_and_combine(&ab->mem_head);
+ pciehp_resource_sort_and_combine(&ab->p_mem_head);
+
+ pciehprm_add_resources (&ab->tbus_head, ab->bus_head);
+ pciehprm_add_resources (&ab->tio_head, ab->io_head);
+ pciehprm_add_resources (&ab->tmem_head, ab->mem_head);
+ pciehprm_add_resources (&ab->tp_mem_head, ab->p_mem_head);
+ }
+
+ return status;
+}
+
+/* find acpi_bridge downword from ab. */
+static struct acpi_bridge *
+find_acpi_bridge_by_bus(
+ struct acpi_bridge *ab,
+ int seg,
+ int bus /* pdev->subordinate->number */
+ )
+{
+ struct acpi_bridge *lab = NULL;
+
+ if (!ab)
+ return NULL;
+
+ if ((ab->bus == bus) && (ab->seg == seg))
+ return ab;
+
+ if (ab->child)
+ lab = find_acpi_bridge_by_bus(ab->child, seg, bus);
+
+ if (!lab)
+ if (ab->next)
+ lab = find_acpi_bridge_by_bus(ab->next, seg, bus);
+
+ return lab;
+}
+
+/*
+ * Build a device tree of ACPI PCI Bridges
+ */
+static void pciehprm_acpi_register_a_bridge (
+ struct acpi_bridge **head,
+ struct acpi_bridge *pab, /* parent bridge to which child bridge is added */
+ struct acpi_bridge *cab /* child bridge to add */
+ )
+{
+ struct acpi_bridge *lpab;
+ struct acpi_bridge *lcab;
+
+ lpab = find_acpi_bridge_by_bus(*head, pab->seg, pab->bus);
+ if (!lpab) {
+ if (!(pab->type & BRIDGE_TYPE_HOST))
+ warn("PCI parent bridge s:b(%x:%x) not in list.\n", pab->seg, pab->bus);
+ pab->next = *head;
+ *head = pab;
+ lpab = pab;
+ }
+
+ if ((cab->type & BRIDGE_TYPE_HOST) && (pab == cab))
+ return;
+
+ lcab = find_acpi_bridge_by_bus(*head, cab->seg, cab->bus);
+ if (lcab) {
+ if ((pab->bus != lcab->parent->bus) || (lcab->bus != cab->bus))
+ err("PCI child bridge s:b(%x:%x) in list with diff parent.\n", cab->seg, cab->bus);
+ return;
+ } else
+ lcab = cab;
+
+ lcab->parent = lpab;
+ lcab->next = lpab->child;
+ lpab->child = lcab;
+}
+
+static acpi_status pciehprm_acpi_build_php_slots_callback(
+ acpi_handle handle,
+ u32 Level,
+ void *context,
+ void **retval
+ )
+{
+ ulong bus_num;
+ ulong seg_num;
+ ulong sun, adr;
+ ulong padr = 0;
+ acpi_handle phandle = NULL;
+ struct acpi_bridge *pab = (struct acpi_bridge *)context;
+ struct acpi_bridge *lab;
+ acpi_status status;
+ u8 *path_name = acpi_path_name(handle);
+
+ /* get _SUN */
+ status = acpi_evaluate_integer(handle, METHOD_NAME__SUN, NULL, &sun);
+ switch(status) {
+ case AE_NOT_FOUND:
+ return AE_OK;
+ default:
+ if (ACPI_FAILURE(status)) {
+ err("acpi_pciehprm:%s _SUN fail=0x%x\n", path_name, status);
+ return status;
+ }
+ }
+
+ /* get _ADR. _ADR must exist if _SUN exists */
+ status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_pciehprm:%s _ADR fail=0x%x\n", path_name, status);
+ return status;
+ }
+
+ dbg("acpi_pciehprm:%s sun=0x%08x adr=0x%08x\n", path_name, (u32)sun, (u32)adr);
+
+ status = acpi_get_parent(handle, &phandle);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_pciehprm:%s get_parent fail=0x%x\n", path_name, status);
+ return (status);
+ }
+
+ bus_num = pab->bus;
+ seg_num = pab->seg;
+
+ if (pab->bus == bus_num) {
+ lab = pab;
+ } else {
+ dbg("WARN: pab is not parent\n");
+ lab = find_acpi_bridge_by_bus(pab, seg_num, bus_num);
+ if (!lab) {
+ dbg("acpi_pciehprm: alloc new P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun);
+ lab = (struct acpi_bridge *)kmalloc(sizeof(struct acpi_bridge), GFP_KERNEL);
+ if (!lab) {
+ err("acpi_pciehprm: alloc for ab fail\n");
+ return AE_NO_MEMORY;
+ }
+ memset(lab, 0, sizeof(struct acpi_bridge));
+
+ lab->handle = phandle;
+ lab->pbus = pab->bus;
+ lab->pdevice = (int)(padr >> 16) & 0xffff;
+ lab->pfunction = (int)(padr & 0xffff);
+ lab->bus = (int)bus_num;
+ lab->scanned = 0;
+ lab->type = BRIDGE_TYPE_P2P;
+
+ pciehprm_acpi_register_a_bridge (&acpi_bridges_head, pab, lab);
+ } else
+ dbg("acpi_pciehprm: found P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun);
+ }
+
+ acpi_add_slot_to_php_slots(lab, (int)bus_num, handle, (u32)adr, (u32)sun);
+
+ return (status);
+}
+
+static int pciehprm_acpi_build_php_slots(
+ struct acpi_bridge *ab,
+ u32 depth
+ )
+{
+ acpi_status status;
+ u8 *path_name = acpi_path_name(ab->handle);
+
+ /* Walk down this pci bridge to get _SUNs if any behind P2P */
+ status = acpi_walk_namespace ( ACPI_TYPE_DEVICE,
+ ab->handle,
+ depth,
+ pciehprm_acpi_build_php_slots_callback,
+ ab,
+ NULL );
+ if (ACPI_FAILURE(status)) {
+ dbg("acpi_pciehprm:%s walk for _SUN on pci bridge seg:bus(%x:%x) fail=0x%x\n", path_name, ab->seg, ab->bus, status);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void build_a_bridge(
+ struct acpi_bridge *pab,
+ struct acpi_bridge *ab
+ )
+{
+ u8 *path_name = acpi_path_name(ab->handle);
+
+ pciehprm_acpi_register_a_bridge (&acpi_bridges_head, pab, ab);
+
+ switch (ab->type) {
+ case BRIDGE_TYPE_HOST:
+ dbg("acpi_pciehprm: Registered PCI HOST Bridge(%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n",
+ ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name);
+ break;
+ case BRIDGE_TYPE_P2P:
+ dbg("acpi_pciehprm: Registered PCI P2P Bridge(%02x-%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n",
+ ab->pbus, ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name);
+ break;
+ };
+
+ /* build any immediate PHP slots under this pci bridge */
+ pciehprm_acpi_build_php_slots(ab, 1);
+}
+
+static struct acpi_bridge * add_p2p_bridge(
+ acpi_handle handle,
+ struct acpi_bridge *pab, /* parent */
+ ulong adr
+ )
+{
+ struct acpi_bridge *ab;
+ struct pci_dev *pdev;
+ ulong devnum, funcnum;
+ u8 *path_name = acpi_path_name(handle);
+
+ ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL);
+ if (!ab) {
+ err("acpi_pciehprm: alloc for ab fail\n");
+ return NULL;
+ }
+ memset(ab, 0, sizeof(struct acpi_bridge));
+
+ devnum = (adr >> 16) & 0xffff;
+ funcnum = adr & 0xffff;
+
+ pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum));
+ if (!pdev || !pdev->subordinate) {
+ err("acpi_pciehprm:%s is not a P2P Bridge\n", path_name);
+ kfree(ab);
+ return NULL;
+ }
+
+ ab->handle = handle;
+ ab->seg = pab->seg;
+ ab->pbus = pab->bus; /* or pdev->bus->number */
+ ab->pdevice = devnum; /* or PCI_SLOT(pdev->devfn) */
+ ab->pfunction = funcnum; /* or PCI_FUNC(pdev->devfn) */
+ ab->bus = pdev->subordinate->number;
+ ab->scanned = 0;
+ ab->type = BRIDGE_TYPE_P2P;
+
+ dbg("acpi_pciehprm: P2P(%x-%x) on pci=b:d:f(%x:%x:%x) acpi=b:d:f(%x:%x:%x) [%s]\n",
+ pab->bus, ab->bus, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ pab->bus, (u32)devnum, (u32)funcnum, path_name);
+
+ build_a_bridge(pab, ab);
+
+ return ab;
+}
+
+static acpi_status scan_p2p_bridge(
+ acpi_handle handle,
+ u32 Level,
+ void *context,
+ void **retval
+ )
+{
+ struct acpi_bridge *pab = (struct acpi_bridge *)context;
+ struct acpi_bridge *ab;
+ acpi_status status;
+ ulong adr = 0;
+ u8 *path_name = acpi_path_name(handle);
+ ulong devnum, funcnum;
+ struct pci_dev *pdev;
+
+ /* get device, function */
+ status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND)
+ err("acpi_pciehprm:%s _ADR fail=0x%x\n", path_name, status);
+ return AE_OK;
+ }
+
+ devnum = (adr >> 16) & 0xffff;
+ funcnum = adr & 0xffff;
+
+ pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum));
+ if (!pdev)
+ return AE_OK;
+ if (!pdev->subordinate)
+ return AE_OK;
+
+ ab = add_p2p_bridge(handle, pab, adr);
+ if (ab) {
+ status = acpi_walk_namespace ( ACPI_TYPE_DEVICE,
+ handle,
+ (u32)1,
+ scan_p2p_bridge,
+ ab,
+ NULL);
+ if (ACPI_FAILURE(status))
+ dbg("acpi_pciehprm:%s find_p2p fail=0x%x\n", path_name, status);
+ }
+
+ return AE_OK;
+}
+
+static struct acpi_bridge * add_host_bridge(
+ acpi_handle handle,
+ ulong segnum,
+ ulong busnum
+ )
+{
+ ulong adr = 0;
+ acpi_status status;
+ struct acpi_bridge *ab;
+ u8 *path_name = acpi_path_name(handle);
+
+ /* get device, function: host br adr is always 0000 though. */
+ status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_pciehprm:%s _ADR fail=0x%x\n", path_name, status);
+ return NULL;
+ }
+ dbg("acpi_pciehprm: ROOT PCI seg(0x%x)bus(0x%x)dev(0x%x)func(0x%x) [%s]\n", (u32)segnum,
+ (u32)busnum, (u32)(adr >> 16) & 0xffff, (u32)adr & 0xffff, path_name);
+
+ ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL);
+ if (!ab) {
+ err("acpi_pciehprm: alloc for ab fail\n");
+ return NULL;
+ }
+ memset(ab, 0, sizeof(struct acpi_bridge));
+
+ ab->handle = handle;
+ ab->seg = (int)segnum;
+ ab->bus = ab->pbus = (int)busnum;
+ ab->pdevice = (int)(adr >> 16) & 0xffff;
+ ab->pfunction = (int)(adr & 0xffff);
+ ab->scanned = 0;
+ ab->type = BRIDGE_TYPE_HOST;
+
+ /* get root pci bridge's current resources */
+ status = acpi_get_crs(ab);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_pciehprm:%s evaluate _CRS fail=0x%x\n", path_name, status);
+ kfree(ab);
+ return NULL;
+ }
+
+ status = pci_osc_control_set (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL);
+ if (ACPI_FAILURE(status)) {
+ err("%s: status %x\n", __FUNCTION__, status);
+ osc_run_status = (status == AE_NOT_FOUND) ? OSC_NOT_EXIST : OSC_RUN_FAILED;
+ } else {
+ osc_run_status = NC_RUN_SUCCESS;
+ }
+ dbg("%s: osc_run_status %x\n", __FUNCTION__, osc_run_status);
+
+ build_a_bridge(ab, ab);
+
+ return ab;
+}
+
+static acpi_status acpi_scan_from_root_pci_callback (
+ acpi_handle handle,
+ u32 Level,
+ void *context,
+ void **retval
+ )
+{
+ ulong segnum = 0;
+ ulong busnum = 0;
+ acpi_status status;
+ struct acpi_bridge *ab;
+ u8 *path_name = acpi_path_name(handle);
+
+ /* get bus number of this pci root bridge */
+ status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, &segnum);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND) {
+ err("acpi_pciehprm:%s evaluate _SEG fail=0x%x\n", path_name, status);
+ return status;
+ }
+ segnum = 0;
+ }
+
+ /* get bus number of this pci root bridge */
+ status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL, &busnum);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_pciehprm:%s evaluate _BBN fail=0x%x\n", path_name, status);
+ return (status);
+ }
+
+ ab = add_host_bridge(handle, segnum, busnum);
+ if (ab) {
+ status = acpi_walk_namespace ( ACPI_TYPE_DEVICE,
+ handle,
+ 1,
+ scan_p2p_bridge,
+ ab,
+ NULL);
+ if (ACPI_FAILURE(status))
+ dbg("acpi_pciehprm:%s find_p2p fail=0x%x\n", path_name, status);
+ }
+
+ return AE_OK;
+}
+
+static int pciehprm_acpi_scan_pci (void)
+{
+ acpi_status status;
+
+ /*
+ * TBD: traverse LDM device tree with the help of
+ * unified ACPI augmented for php device population.
+ */
+ status = acpi_get_devices ( PCI_ROOT_HID_STRING,
+ acpi_scan_from_root_pci_callback,
+ NULL,
+ NULL );
+ if (ACPI_FAILURE(status)) {
+ err("acpi_pciehprm:get_device PCI ROOT HID fail=0x%x\n", status);
+ return -1;
+ }
+
+ return 0;
+}
+
+int pciehprm_init(enum php_ctlr_type ctlr_type)
+{
+ int rc;
+
+ if (ctlr_type != PCI)
+ return -ENODEV;
+
+ dbg("pciehprm ACPI init <enter>\n");
+ acpi_bridges_head = NULL;
+
+ /* construct PCI bus:device tree of acpi_handles */
+ rc = pciehprm_acpi_scan_pci();
+ if (rc)
+ return rc;
+
+ if ((oshp_run_status != NC_RUN_SUCCESS) && (osc_run_status != NC_RUN_SUCCESS)) {
+ err("Fails to gain control of native hot-plug\n");
+ rc = -ENODEV;
+ }
+
+ dbg("pciehprm ACPI init %s\n", (rc)?"fail":"success");
+ return rc;
+}
+
+static void free_a_slot(struct acpi_php_slot *aps)
+{
+ dbg(" free a php func of slot(0x%02x) on PCI b:d:f=0x%02x:%02x:%02x\n", aps->sun, aps->bus, aps->dev, aps->fun);
+
+ free_pci_resource (aps->io_head);
+ free_pci_resource (aps->bus_head);
+ free_pci_resource (aps->mem_head);
+ free_pci_resource (aps->p_mem_head);
+
+ kfree(aps);
+}
+
+static void free_a_bridge( struct acpi_bridge *ab)
+{
+ struct acpi_php_slot *aps, *next;
+
+ switch (ab->type) {
+ case BRIDGE_TYPE_HOST:
+ dbg("Free ACPI PCI HOST Bridge(%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n",
+ ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction);
+ break;
+ case BRIDGE_TYPE_P2P:
+ dbg("Free ACPI PCI P2P Bridge(%x-%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n",
+ ab->pbus, ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction);
+ break;
+ };
+
+ /* free slots first */
+ for (aps = ab->slots; aps; aps = next) {
+ next = aps->next;
+ free_a_slot(aps);
+ }
+
+ free_pci_resource (ab->io_head);
+ free_pci_resource (ab->tio_head);
+ free_pci_resource (ab->bus_head);
+ free_pci_resource (ab->tbus_head);
+ free_pci_resource (ab->mem_head);
+ free_pci_resource (ab->tmem_head);
+ free_pci_resource (ab->p_mem_head);
+ free_pci_resource (ab->tp_mem_head);
+
+ kfree(ab);
+}
+
+static void pciehprm_free_bridges ( struct acpi_bridge *ab)
+{
+ if (!ab)
+ return;
+
+ if (ab->child)
+ pciehprm_free_bridges (ab->child);
+
+ if (ab->next)
+ pciehprm_free_bridges (ab->next);
+
+ free_a_bridge(ab);
+}
+
+void pciehprm_cleanup(void)
+{
+ pciehprm_free_bridges (acpi_bridges_head);
+}
+
+static int get_number_of_slots (
+ struct acpi_bridge *ab,
+ int selfonly
+ )
+{
+ struct acpi_php_slot *aps;
+ int prev_slot = -1;
+ int slot_num = 0;
+
+ for ( aps = ab->slots; aps; aps = aps->next)
+ if (aps->dev != prev_slot) {
+ prev_slot = aps->dev;
+ slot_num++;
+ }
+
+ if (ab->child)
+ slot_num += get_number_of_slots (ab->child, 0);
+
+ if (selfonly)
+ return slot_num;
+
+ if (ab->next)
+ slot_num += get_number_of_slots (ab->next, 0);
+
+ return slot_num;
+}
+
+static int print_acpi_resources (struct acpi_bridge *ab)
+{
+ struct acpi_php_slot *aps;
+ int i;
+
+ switch (ab->type) {
+ case BRIDGE_TYPE_HOST:
+ dbg("PCI HOST Bridge (%x) [%s]\n", ab->bus, acpi_path_name(ab->handle));
+ break;
+ case BRIDGE_TYPE_P2P:
+ dbg("PCI P2P Bridge (%x-%x) [%s]\n", ab->pbus, ab->bus, acpi_path_name(ab->handle));
+ break;
+ };
+
+ print_pci_resources (ab);
+
+ for ( i = -1, aps = ab->slots; aps; aps = aps->next) {
+ if (aps->dev == i)
+ continue;
+ dbg(" Slot sun(%x) s:b:d:f(%02x:%02x:%02x:%02x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun);
+ print_slot_resources(aps);
+ i = aps->dev;
+ }
+
+ if (ab->child)
+ print_acpi_resources (ab->child);
+
+ if (ab->next)
+ print_acpi_resources (ab->next);
+
+ return 0;
+}
+
+int pciehprm_print_pirt(void)
+{
+ dbg("PCIEHPRM ACPI Slots\n");
+ if (acpi_bridges_head)
+ print_acpi_resources (acpi_bridges_head);
+
+ return 0;
+}
+
+static struct acpi_php_slot * get_acpi_slot (
+ struct acpi_bridge *ab,
+ u32 sun
+ )
+{
+ struct acpi_php_slot *aps = NULL;
+
+ for ( aps = ab->slots; aps; aps = aps->next)
+ if (aps->sun == sun)
+ return aps;
+
+ if (!aps && ab->child) {
+ aps = (struct acpi_php_slot *)get_acpi_slot (ab->child, sun);
+ if (aps)
+ return aps;
+ }
+
+ if (!aps && ab->next) {
+ aps = (struct acpi_php_slot *)get_acpi_slot (ab->next, sun);
+ if (aps)
+ return aps;
+ }
+
+ return aps;
+
+}
+
+#if 0
+void * pciehprm_get_slot(struct slot *slot)
+{
+ struct acpi_bridge *ab = acpi_bridges_head;
+ struct acpi_php_slot *aps = get_acpi_slot (ab, slot->number);
+
+ aps->slot = slot;
+
+ dbg("Got acpi slot sun(%x): s:b:d:f(%x:%x:%x:%x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun);
+
+ return (void *)aps;
+}
+#endif
+
+static void pciehprm_dump_func_res( struct pci_func *fun)
+{
+ struct pci_func *func = fun;
+
+ if (func->bus_head) {
+ dbg(": BUS Resources:\n");
+ print_pci_resource (func->bus_head);
+ }
+ if (func->io_head) {
+ dbg(": IO Resources:\n");
+ print_pci_resource (func->io_head);
+ }
+ if (func->mem_head) {
+ dbg(": MEM Resources:\n");
+ print_pci_resource (func->mem_head);
+ }
+ if (func->p_mem_head) {
+ dbg(": PMEM Resources:\n");
+ print_pci_resource (func->p_mem_head);
+ }
+}
+
+static void pciehprm_dump_ctrl_res( struct controller *ctlr)
+{
+ struct controller *ctrl = ctlr;
+
+ if (ctrl->bus_head) {
+ dbg(": BUS Resources:\n");
+ print_pci_resource (ctrl->bus_head);
+ }
+ if (ctrl->io_head) {
+ dbg(": IO Resources:\n");
+ print_pci_resource (ctrl->io_head);
+ }
+ if (ctrl->mem_head) {
+ dbg(": MEM Resources:\n");
+ print_pci_resource (ctrl->mem_head);
+ }
+ if (ctrl->p_mem_head) {
+ dbg(": PMEM Resources:\n");
+ print_pci_resource (ctrl->p_mem_head);
+ }
+}
+
+static int pciehprm_get_used_resources (
+ struct controller *ctrl,
+ struct pci_func *func
+ )
+{
+ return pciehp_save_used_resources (ctrl, func, !DISABLE_CARD);
+}
+
+static int configure_existing_function(
+ struct controller *ctrl,
+ struct pci_func *func
+ )
+{
+ int rc;
+
+ /* see how much resources the func has used. */
+ rc = pciehprm_get_used_resources (ctrl, func);
+
+ if (!rc) {
+ /* subtract the resources used by the func from ctrl resources */
+ rc = pciehprm_delete_resources (&ctrl->bus_head, func->bus_head);
+ rc |= pciehprm_delete_resources (&ctrl->io_head, func->io_head);
+ rc |= pciehprm_delete_resources (&ctrl->mem_head, func->mem_head);
+ rc |= pciehprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head);
+ if (rc)
+ warn("aCEF: cannot del used resources\n");
+ } else
+ err("aCEF: cannot get used resources\n");
+
+ return rc;
+}
+
+static int bind_pci_resources_to_slots ( struct controller *ctrl)
+{
+ struct pci_func *func, new_func;
+ int busn = ctrl->slot_bus;
+ int devn, funn;
+ u32 vid;
+
+ for (devn = 0; devn < 32; devn++) {
+ for (funn = 0; funn < 8; funn++) {
+ /*
+ if (devn == ctrl->device && funn == ctrl->function)
+ continue;
+ */
+ /* find out if this entry is for an occupied slot */
+ vid = 0xFFFFFFFF;
+ pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid);
+
+ if (vid != 0xFFFFFFFF) {
+ dbg("%s: vid = %x\n", __FUNCTION__, vid);
+ func = pciehp_slot_find(busn, devn, funn);
+ if (!func) {
+ memset(&new_func, 0, sizeof(struct pci_func));
+ new_func.bus = busn;
+ new_func.device = devn;
+ new_func.function = funn;
+ new_func.is_a_board = 1;
+ configure_existing_function(ctrl, &new_func);
+ pciehprm_dump_func_res(&new_func);
+ } else {
+ configure_existing_function(ctrl, func);
+ pciehprm_dump_func_res(func);
+ }
+ dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int bind_pci_resources(
+ struct controller *ctrl,
+ struct acpi_bridge *ab
+ )
+{
+ int status = 0;
+
+ if (ab->bus_head) {
+ dbg("bapr: BUS Resources add on PCI 0x%x\n", ab->bus);
+ status = pciehprm_add_resources (&ctrl->bus_head, ab->bus_head);
+ if (pciehprm_delete_resources (&ab->bus_head, ctrl->bus_head))
+ warn("bapr: cannot sub BUS Resource on PCI 0x%x\n", ab->bus);
+ if (status) {
+ err("bapr: BUS Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
+ return status;
+ }
+ } else
+ info("bapr: No BUS Resource on PCI 0x%x.\n", ab->bus);
+
+ if (ab->io_head) {
+ dbg("bapr: IO Resources add on PCI 0x%x\n", ab->bus);
+ status = pciehprm_add_resources (&ctrl->io_head, ab->io_head);
+ if (pciehprm_delete_resources (&ab->io_head, ctrl->io_head))
+ warn("bapr: cannot sub IO Resource on PCI 0x%x\n", ab->bus);
+ if (status) {
+ err("bapr: IO Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
+ return status;
+ }
+ } else
+ info("bapr: No IO Resource on PCI 0x%x.\n", ab->bus);
+
+ if (ab->mem_head) {
+ dbg("bapr: MEM Resources add on PCI 0x%x\n", ab->bus);
+ status = pciehprm_add_resources (&ctrl->mem_head, ab->mem_head);
+ if (pciehprm_delete_resources (&ab->mem_head, ctrl->mem_head))
+ warn("bapr: cannot sub MEM Resource on PCI 0x%x\n", ab->bus);
+ if (status) {
+ err("bapr: MEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
+ return status;
+ }
+ } else
+ info("bapr: No MEM Resource on PCI 0x%x.\n", ab->bus);
+
+ if (ab->p_mem_head) {
+ dbg("bapr: PMEM Resources add on PCI 0x%x\n", ab->bus);
+ status = pciehprm_add_resources (&ctrl->p_mem_head, ab->p_mem_head);
+ if (pciehprm_delete_resources (&ab->p_mem_head, ctrl->p_mem_head))
+ warn("bapr: cannot sub PMEM Resource on PCI 0x%x\n", ab->bus);
+ if (status) {
+ err("bapr: PMEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
+ return status;
+ }
+ } else
+ info("bapr: No PMEM Resource on PCI 0x%x.\n", ab->bus);
+
+ return status;
+}
+
+static int no_pci_resources( struct acpi_bridge *ab)
+{
+ return !(ab->p_mem_head || ab->mem_head || ab->io_head || ab->bus_head);
+}
+
+static int find_pci_bridge_resources (
+ struct controller *ctrl,
+ struct acpi_bridge *ab
+ )
+{
+ int rc = 0;
+ struct pci_func func;
+
+ memset(&func, 0, sizeof(struct pci_func));
+
+ func.bus = ab->pbus;
+ func.device = ab->pdevice;
+ func.function = ab->pfunction;
+ func.is_a_board = 1;
+
+ /* Get used resources for this PCI bridge */
+ rc = pciehp_save_used_resources (ctrl, &func, !DISABLE_CARD);
+
+ ab->io_head = func.io_head;
+ ab->mem_head = func.mem_head;
+ ab->p_mem_head = func.p_mem_head;
+ ab->bus_head = func.bus_head;
+ if (ab->bus_head)
+ pciehprm_delete_resource(&ab->bus_head, ctrl->pci_dev->subordinate->number, 1);
+
+ return rc;
+}
+
+static int get_pci_resources_from_bridge(
+ struct controller *ctrl,
+ struct acpi_bridge *ab
+ )
+{
+ int rc = 0;
+
+ dbg("grfb: Get Resources for PCI 0x%x from actual PCI bridge 0x%x.\n", ctrl->bus, ab->bus);
+
+ rc = find_pci_bridge_resources (ctrl, ab);
+
+ pciehp_resource_sort_and_combine(&ab->bus_head);
+ pciehp_resource_sort_and_combine(&ab->io_head);
+ pciehp_resource_sort_and_combine(&ab->mem_head);
+ pciehp_resource_sort_and_combine(&ab->p_mem_head);
+
+ pciehprm_add_resources (&ab->tbus_head, ab->bus_head);
+ pciehprm_add_resources (&ab->tio_head, ab->io_head);
+ pciehprm_add_resources (&ab->tmem_head, ab->mem_head);
+ pciehprm_add_resources (&ab->tp_mem_head, ab->p_mem_head);
+
+ return rc;
+}
+
+static int get_pci_resources(
+ struct controller *ctrl,
+ struct acpi_bridge *ab
+ )
+{
+ int rc = 0;
+
+ if (no_pci_resources(ab)) {
+ dbg("spbr:PCI 0x%x has no resources. Get parent resources.\n", ab->bus);
+ rc = get_pci_resources_from_bridge(ctrl, ab);
+ }
+
+ return rc;
+}
+
+/*
+ * Get resources for this ctrl.
+ * 1. get total resources from ACPI _CRS or bridge (this ctrl)
+ * 2. find used resources of existing adapters
+ * 3. subtract used resources from total resources
+ */
+int pciehprm_find_available_resources( struct controller *ctrl)
+{
+ int rc = 0;
+ struct acpi_bridge *ab;
+
+ ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->pci_dev->subordinate->number);
+ if (!ab) {
+ err("pfar:cannot locate acpi bridge of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number);
+ return -1;
+ }
+ if (no_pci_resources(ab)) {
+ rc = get_pci_resources(ctrl, ab);
+ if (rc) {
+ err("pfar:cannot get pci resources of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number);
+ return -1;
+ }
+ }
+
+ rc = bind_pci_resources(ctrl, ab);
+ dbg("pfar:pre-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number);
+ pciehprm_dump_ctrl_res(ctrl);
+
+ bind_pci_resources_to_slots (ctrl);
+
+ dbg("pfar:post-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number);
+ pciehprm_dump_ctrl_res(ctrl);
+
+ return rc;
+}
+
+int pciehprm_set_hpp(
+ struct controller *ctrl,
+ struct pci_func *func,
+ u8 card_type
+ )
+{
+ struct acpi_bridge *ab;
+ struct pci_bus lpci_bus, *pci_bus;
+ int rc = 0;
+ unsigned int devfn;
+ u8 cls= 0x08; /* default cache line size */
+ u8 lt = 0x40; /* default latency timer */
+ u8 ep = 0;
+ u8 es = 0;
+
+ memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus);
+
+ if (ab) {
+ if (ab->_hpp) {
+ lt = (u8)ab->_hpp->latency_timer;
+ cls = (u8)ab->_hpp->cache_line_size;
+ ep = (u8)ab->_hpp->enable_perr;
+ es = (u8)ab->_hpp->enable_serr;
+ } else
+ dbg("_hpp: no _hpp for B/D/F=%#x/%#x/%#x. use default value\n", func->bus, func->device, func->function);
+ } else
+ dbg("_hpp: no acpi bridge for B/D/F = %#x/%#x/%#x. use default value\n", func->bus, func->device, func->function);
+
+
+ if (card_type == PCI_HEADER_TYPE_BRIDGE) {
+ /* set subordinate Latency Timer */
+ rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, lt);
+ }
+
+ /* set base Latency Timer */
+ rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, lt);
+ dbg(" set latency timer =0x%02x: %x\n", lt, rc);
+
+ rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, cls);
+ dbg(" set cache_line_size=0x%02x: %x\n", cls, rc);
+
+ return rc;
+}
+
+void pciehprm_enable_card(
+ struct controller *ctrl,
+ struct pci_func *func,
+ u8 card_type)
+{
+ u16 command, cmd, bcommand, bcmd;
+ struct pci_bus lpci_bus, *pci_bus;
+ struct acpi_bridge *ab;
+ unsigned int devfn;
+ int rc;
+
+ memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
+
+ if (card_type == PCI_HEADER_TYPE_BRIDGE) {
+ rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
+ }
+
+ cmd = command = command | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
+ | PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+ bcmd = bcommand = bcommand | PCI_BRIDGE_CTL_NO_ISA;
+
+ ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus);
+ if (ab) {
+ if (ab->_hpp) {
+ if (ab->_hpp->enable_perr) {
+ command |= PCI_COMMAND_PARITY;
+ bcommand |= PCI_BRIDGE_CTL_PARITY;
+ } else {
+ command &= ~PCI_COMMAND_PARITY;
+ bcommand &= ~PCI_BRIDGE_CTL_PARITY;
+ }
+ if (ab->_hpp->enable_serr) {
+ command |= PCI_COMMAND_SERR;
+ bcommand |= PCI_BRIDGE_CTL_SERR;
+ } else {
+ command &= ~PCI_COMMAND_SERR;
+ bcommand &= ~PCI_BRIDGE_CTL_SERR;
+ }
+ } else
+ dbg("no _hpp for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function);
+ } else
+ dbg("no acpi bridge for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function);
+
+ if (command != cmd) {
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
+ }
+ if ((card_type == PCI_HEADER_TYPE_BRIDGE) && (bcommand != bcmd)) {
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
+ }
+}
diff --git a/drivers/pci/hotplug/pciehprm_nonacpi.c b/drivers/pci/hotplug/pciehprm_nonacpi.c
new file mode 100644
index 000000000000..79a0aa6238ef
--- /dev/null
+++ b/drivers/pci/hotplug/pciehprm_nonacpi.c
@@ -0,0 +1,501 @@
+/*
+ * PCIEHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#ifdef CONFIG_IA64
+#include <asm/iosapic.h>
+#endif
+#include "pciehp.h"
+#include "pciehprm.h"
+#include "pciehprm_nonacpi.h"
+
+
+void pciehprm_cleanup(void)
+{
+ return;
+}
+
+int pciehprm_print_pirt(void)
+{
+ return 0;
+}
+
+int pciehprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum)
+{
+
+ *sun = (u8) (ctrl->first_slot);
+ return 0;
+}
+
+
+static void print_pci_resource ( struct pci_resource *aprh)
+{
+ struct pci_resource *res;
+
+ for (res = aprh; res; res = res->next)
+ dbg(" base= 0x%x length= 0x%x\n", res->base, res->length);
+}
+
+
+static void phprm_dump_func_res( struct pci_func *fun)
+{
+ struct pci_func *func = fun;
+
+ if (func->bus_head) {
+ dbg(": BUS Resources:\n");
+ print_pci_resource (func->bus_head);
+ }
+ if (func->io_head) {
+ dbg(": IO Resources:\n");
+ print_pci_resource (func->io_head);
+ }
+ if (func->mem_head) {
+ dbg(": MEM Resources:\n");
+ print_pci_resource (func->mem_head);
+ }
+ if (func->p_mem_head) {
+ dbg(": PMEM Resources:\n");
+ print_pci_resource (func->p_mem_head);
+ }
+}
+
+static int phprm_get_used_resources (
+ struct controller *ctrl,
+ struct pci_func *func
+ )
+{
+ return pciehp_save_used_resources (ctrl, func, !DISABLE_CARD);
+}
+
+static int phprm_delete_resource(
+ struct pci_resource **aprh,
+ ulong base,
+ ulong size)
+{
+ struct pci_resource *res;
+ struct pci_resource *prevnode;
+ struct pci_resource *split_node;
+ ulong tbase;
+
+ pciehp_resource_sort_and_combine(aprh);
+
+ for (res = *aprh; res; res = res->next) {
+ if (res->base > base)
+ continue;
+
+ if ((res->base + res->length) < (base + size))
+ continue;
+
+ if (res->base < base) {
+ tbase = base;
+
+ if ((res->length - (tbase - res->base)) < size)
+ continue;
+
+ split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!split_node)
+ return -ENOMEM;
+
+ split_node->base = res->base;
+ split_node->length = tbase - res->base;
+ res->base = tbase;
+ res->length -= split_node->length;
+
+ split_node->next = res->next;
+ res->next = split_node;
+ }
+
+ if (res->length >= size) {
+ split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!split_node)
+ return -ENOMEM;
+
+ split_node->base = res->base + size;
+ split_node->length = res->length - size;
+ res->length = size;
+
+ split_node->next = res->next;
+ res->next = split_node;
+ }
+
+ if (*aprh == res) {
+ *aprh = res->next;
+ } else {
+ prevnode = *aprh;
+ while (prevnode->next != res)
+ prevnode = prevnode->next;
+
+ prevnode->next = res->next;
+ }
+ res->next = NULL;
+ kfree(res);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int phprm_delete_resources(
+ struct pci_resource **aprh,
+ struct pci_resource *this
+ )
+{
+ struct pci_resource *res;
+
+ for (res = this; res; res = res->next)
+ phprm_delete_resource(aprh, res->base, res->length);
+
+ return 0;
+}
+
+
+static int configure_existing_function(
+ struct controller *ctrl,
+ struct pci_func *func
+ )
+{
+ int rc;
+
+ /* see how much resources the func has used. */
+ rc = phprm_get_used_resources (ctrl, func);
+
+ if (!rc) {
+ /* subtract the resources used by the func from ctrl resources */
+ rc = phprm_delete_resources (&ctrl->bus_head, func->bus_head);
+ rc |= phprm_delete_resources (&ctrl->io_head, func->io_head);
+ rc |= phprm_delete_resources (&ctrl->mem_head, func->mem_head);
+ rc |= phprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head);
+ if (rc)
+ warn("aCEF: cannot del used resources\n");
+ } else
+ err("aCEF: cannot get used resources\n");
+
+ return rc;
+}
+
+static int pciehprm_delete_resource(
+ struct pci_resource **aprh,
+ ulong base,
+ ulong size)
+{
+ struct pci_resource *res;
+ struct pci_resource *prevnode;
+ struct pci_resource *split_node;
+ ulong tbase;
+
+ pciehp_resource_sort_and_combine(aprh);
+
+ for (res = *aprh; res; res = res->next) {
+ if (res->base > base)
+ continue;
+
+ if ((res->base + res->length) < (base + size))
+ continue;
+
+ if (res->base < base) {
+ tbase = base;
+
+ if ((res->length - (tbase - res->base)) < size)
+ continue;
+
+ split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!split_node)
+ return -ENOMEM;
+
+ split_node->base = res->base;
+ split_node->length = tbase - res->base;
+ res->base = tbase;
+ res->length -= split_node->length;
+
+ split_node->next = res->next;
+ res->next = split_node;
+ }
+
+ if (res->length >= size) {
+ split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!split_node)
+ return -ENOMEM;
+
+ split_node->base = res->base + size;
+ split_node->length = res->length - size;
+ res->length = size;
+
+ split_node->next = res->next;
+ res->next = split_node;
+ }
+
+ if (*aprh == res) {
+ *aprh = res->next;
+ } else {
+ prevnode = *aprh;
+ while (prevnode->next != res)
+ prevnode = prevnode->next;
+
+ prevnode->next = res->next;
+ }
+ res->next = NULL;
+ kfree(res);
+ break;
+ }
+
+ return 0;
+}
+
+static int bind_pci_resources_to_slots ( struct controller *ctrl)
+{
+ struct pci_func *func, new_func;
+ int busn = ctrl->slot_bus;
+ int devn, funn;
+ u32 vid;
+
+ for (devn = 0; devn < 32; devn++) {
+ for (funn = 0; funn < 8; funn++) {
+ /*
+ if (devn == ctrl->device && funn == ctrl->function)
+ continue;
+ */
+ /* find out if this entry is for an occupied slot */
+ vid = 0xFFFFFFFF;
+
+ pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid);
+
+ if (vid != 0xFFFFFFFF) {
+ dbg("%s: vid = %x bus %x dev %x fun %x\n", __FUNCTION__,
+ vid, busn, devn, funn);
+ func = pciehp_slot_find(busn, devn, funn);
+ dbg("%s: func = %p\n", __FUNCTION__,func);
+ if (!func) {
+ memset(&new_func, 0, sizeof(struct pci_func));
+ new_func.bus = busn;
+ new_func.device = devn;
+ new_func.function = funn;
+ new_func.is_a_board = 1;
+ configure_existing_function(ctrl, &new_func);
+ phprm_dump_func_res(&new_func);
+ } else {
+ configure_existing_function(ctrl, func);
+ phprm_dump_func_res(func);
+ }
+ dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void phprm_dump_ctrl_res( struct controller *ctlr)
+{
+ struct controller *ctrl = ctlr;
+
+ if (ctrl->bus_head) {
+ dbg(": BUS Resources:\n");
+ print_pci_resource (ctrl->bus_head);
+ }
+ if (ctrl->io_head) {
+ dbg(": IO Resources:\n");
+ print_pci_resource (ctrl->io_head);
+ }
+ if (ctrl->mem_head) {
+ dbg(": MEM Resources:\n");
+ print_pci_resource (ctrl->mem_head);
+ }
+ if (ctrl->p_mem_head) {
+ dbg(": PMEM Resources:\n");
+ print_pci_resource (ctrl->p_mem_head);
+ }
+}
+
+/*
+ * phprm_find_available_resources
+ *
+ * Finds available memory, IO, and IRQ resources for programming
+ * devices which may be added to the system
+ * this function is for hot plug ADD!
+ *
+ * returns 0 if success
+ */
+int pciehprm_find_available_resources(struct controller *ctrl)
+{
+ struct pci_func func;
+ u32 rc;
+
+ memset(&func, 0, sizeof(struct pci_func));
+
+ func.bus = ctrl->bus;
+ func.device = ctrl->device;
+ func.function = ctrl->function;
+ func.is_a_board = 1;
+
+ /* Get resources for this PCI bridge */
+ rc = pciehp_save_used_resources (ctrl, &func, !DISABLE_CARD);
+ dbg("%s: pciehp_save_used_resources rc = %d\n", __FUNCTION__, rc);
+
+ if (func.mem_head)
+ func.mem_head->next = ctrl->mem_head;
+ ctrl->mem_head = func.mem_head;
+
+ if (func.p_mem_head)
+ func.p_mem_head->next = ctrl->p_mem_head;
+ ctrl->p_mem_head = func.p_mem_head;
+
+ if (func.io_head)
+ func.io_head->next = ctrl->io_head;
+ ctrl->io_head = func.io_head;
+
+ if(func.bus_head)
+ func.bus_head->next = ctrl->bus_head;
+ ctrl->bus_head = func.bus_head;
+
+ if (ctrl->bus_head)
+ pciehprm_delete_resource(&ctrl->bus_head, ctrl->pci_dev->subordinate->number, 1);
+
+ dbg("%s:pre-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
+ phprm_dump_ctrl_res(ctrl);
+
+ dbg("%s: before bind_pci_resources_to slots\n", __FUNCTION__);
+
+ bind_pci_resources_to_slots (ctrl);
+
+ dbg("%s:post-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
+ phprm_dump_ctrl_res(ctrl);
+
+ return (rc);
+}
+
+int pciehprm_set_hpp(
+ struct controller *ctrl,
+ struct pci_func *func,
+ u8 card_type)
+{
+ u32 rc;
+ u8 temp_byte;
+ struct pci_bus lpci_bus, *pci_bus;
+ unsigned int devfn;
+ memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ temp_byte = 0x40; /* hard coded value for LT */
+ if (card_type == PCI_HEADER_TYPE_BRIDGE) {
+ /* set subordinate Latency Timer */
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
+
+ if (rc) {
+ dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__,
+ func->bus, func->device, func->function);
+ return rc;
+ }
+ }
+
+ /* set base Latency Timer */
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
+
+ if (rc) {
+ dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
+ return rc;
+ }
+
+ /* set Cache Line size */
+ temp_byte = 0x08; /* hard coded value for CLS */
+
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
+
+ if (rc) {
+ dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
+ }
+
+ /* set enable_perr */
+ /* set enable_serr */
+
+ return rc;
+}
+
+void pciehprm_enable_card(
+ struct controller *ctrl,
+ struct pci_func *func,
+ u8 card_type)
+{
+ u16 command, bcommand;
+ struct pci_bus lpci_bus, *pci_bus;
+ unsigned int devfn;
+ int rc;
+
+ memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
+
+ command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR
+ | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
+ | PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
+
+ if (card_type == PCI_HEADER_TYPE_BRIDGE) {
+
+ rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
+
+ bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR
+ | PCI_BRIDGE_CTL_NO_ISA;
+
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
+ }
+}
+
+static int legacy_pciehprm_init_pci(void)
+{
+ return 0;
+}
+
+int pciehprm_init(enum php_ctlr_type ctrl_type)
+{
+ int retval;
+
+ switch (ctrl_type) {
+ case PCI:
+ retval = legacy_pciehprm_init_pci();
+ break;
+ default:
+ retval = -ENODEV;
+ break;
+ }
+
+ return retval;
+}
diff --git a/drivers/pci/hotplug/pciehprm_nonacpi.h b/drivers/pci/hotplug/pciehprm_nonacpi.h
new file mode 100644
index 000000000000..87c90e85ede9
--- /dev/null
+++ b/drivers/pci/hotplug/pciehprm_nonacpi.h
@@ -0,0 +1,56 @@
+/*
+ * PCIEHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+
+#ifndef _PCIEHPRM_NONACPI_H_
+#define _PCIEHPRM_NONACPI_H_
+
+struct irq_info {
+ u8 bus, devfn; /* bus, device and function */
+ struct {
+ u8 link; /* IRQ line ID, chipset dependent, 0=not routed */
+ u16 bitmap; /* Available IRQs */
+ } __attribute__ ((packed)) irq[4];
+ u8 slot; /* slot number, 0=onboard */
+ u8 rfu;
+} __attribute__ ((packed));
+
+struct irq_routing_table {
+ u32 signature; /* PIRQ_SIGNATURE should be here */
+ u16 version; /* PIRQ_VERSION */
+ u16 size; /* Table size in bytes */
+ u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
+ u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
+ u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
+ u32 miniport_data; /* Crap */
+ u8 rfu[11];
+ u8 checksum; /* Modulo 256 checksum must give zero */
+ struct irq_info slots[0];
+} __attribute__ ((packed));
+
+#endif /* _PCIEHPRM_NONACPI_H_ */
diff --git a/drivers/pci/hotplug/pcihp_skeleton.c b/drivers/pci/hotplug/pcihp_skeleton.c
new file mode 100644
index 000000000000..6605d6bda529
--- /dev/null
+++ b/drivers/pci/hotplug/pcihp_skeleton.c
@@ -0,0 +1,375 @@
+/*
+ * PCI Hot Plug Controller Skeleton Driver - 0.2
+ *
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 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.
+ *
+ * This driver is to be used as a skeleton driver to be show how to interface
+ * with the pci hotplug core easily.
+ *
+ * Send feedback to <greg@kroah.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include "pci_hotplug.h"
+
+struct slot {
+ u8 number;
+ struct hotplug_slot *hotplug_slot;
+ struct list_head slot_list;
+};
+
+static LIST_HEAD(slot_list);
+
+#define MY_NAME "pcihp_skeleton"
+
+#define dbg(format, arg...) \
+ do { \
+ if (debug) \
+ printk (KERN_DEBUG "%s: " format "\n", \
+ MY_NAME , ## arg); \
+ } while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
+
+
+
+/* local variables */
+static int debug;
+static int num_slots;
+
+#define DRIVER_VERSION "0.3"
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
+#define DRIVER_DESC "Hot Plug PCI Controller Skeleton Driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+
+static int enable_slot (struct hotplug_slot *slot);
+static int disable_slot (struct hotplug_slot *slot);
+static int set_attention_status (struct hotplug_slot *slot, u8 value);
+static int hardware_test (struct hotplug_slot *slot, u32 value);
+static int get_power_status (struct hotplug_slot *slot, u8 *value);
+static int get_attention_status (struct hotplug_slot *slot, u8 *value);
+static int get_latch_status (struct hotplug_slot *slot, u8 *value);
+static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
+
+static struct hotplug_slot_ops skel_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .enable_slot = enable_slot,
+ .disable_slot = disable_slot,
+ .set_attention_status = set_attention_status,
+ .hardware_test = hardware_test,
+ .get_power_status = get_power_status,
+ .get_attention_status = get_attention_status,
+ .get_latch_status = get_latch_status,
+ .get_adapter_status = get_adapter_status,
+};
+
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval = 0;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ /*
+ * Fill in code here to enable the specified slot
+ */
+
+ return retval;
+}
+
+
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval = 0;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ /*
+ * Fill in code here to disable the specified slot
+ */
+
+ return retval;
+}
+
+static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval = 0;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ switch (status) {
+ case 0:
+ /*
+ * Fill in code here to turn light off
+ */
+ break;
+
+ case 1:
+ default:
+ /*
+ * Fill in code here to turn light on
+ */
+ break;
+ }
+
+ return retval;
+}
+
+static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval = 0;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ switch (value) {
+ case 0:
+ /* Specify a test here */
+ break;
+ case 1:
+ /* Specify another test here */
+ break;
+ }
+
+ return retval;
+}
+
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval = 0;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ /*
+ * Fill in logic to get the current power status of the specific
+ * slot and store it in the *value location.
+ */
+
+ return retval;
+}
+
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval = 0;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ /*
+ * Fill in logic to get the current attention status of the specific
+ * slot and store it in the *value location.
+ */
+
+ return retval;
+}
+
+static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval = 0;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ /*
+ * Fill in logic to get the current latch status of the specific
+ * slot and store it in the *value location.
+ */
+
+ return retval;
+}
+
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+ int retval = 0;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ /*
+ * Fill in logic to get the current adapter status of the specific
+ * slot and store it in the *value location.
+ */
+
+ return retval;
+}
+
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+ kfree(slot->hotplug_slot->info);
+ kfree(slot->hotplug_slot->name);
+ kfree(slot->hotplug_slot);
+ kfree(slot);
+}
+
+#define SLOT_NAME_SIZE 10
+static void make_slot_name(struct slot *slot)
+{
+ /*
+ * Stupid way to make a filename out of the slot name.
+ * replace this if your hardware provides a better way to name slots.
+ */
+ snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d", slot->number);
+}
+
+/**
+ * init_slots - initialize 'struct slot' structures for each slot
+ *
+ */
+static int __init init_slots(void)
+{
+ struct slot *slot;
+ struct hotplug_slot *hotplug_slot;
+ struct hotplug_slot_info *info;
+ char *name;
+ int retval = -ENOMEM;
+ int i;
+
+ /*
+ * Create a structure for each slot, and register that slot
+ * with the pci_hotplug subsystem.
+ */
+ for (i = 0; i < num_slots; ++i) {
+ slot = kmalloc(sizeof(struct slot), GFP_KERNEL);
+ if (!slot)
+ goto error;
+ memset(slot, 0, sizeof(struct slot));
+
+ hotplug_slot = kmalloc(sizeof(struct hotplug_slot),
+ GFP_KERNEL);
+ if (!hotplug_slot)
+ goto error_slot;
+ memset(hotplug_slot, 0, sizeof (struct hotplug_slot));
+ slot->hotplug_slot = hotplug_slot;
+
+ info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
+ if (!info)
+ goto error_hpslot;
+ memset(info, 0, sizeof (struct hotplug_slot_info));
+ hotplug_slot->info = info;
+
+ name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
+ if (!name)
+ goto error_info;
+ hotplug_slot->name = name;
+
+ slot->number = i;
+
+ hotplug_slot->private = slot;
+ hotplug_slot->release = &release_slot;
+ make_slot_name(slot);
+ hotplug_slot->ops = &skel_hotplug_slot_ops;
+
+ /*
+ * Initilize the slot info structure with some known
+ * good values.
+ */
+ info->power_status = get_power_status(slot);
+ info->attention_status = get_attention_status(slot);
+ info->latch_status = get_latch_status(slot);
+ info->adapter_status = get_adapter_status(slot);
+
+ dbg("registering slot %d\n", i);
+ retval = pci_hp_register(slot->hotplug_slot);
+ if (retval) {
+ err("pci_hp_register failed with error %d\n", retval);
+ goto error_name;
+ }
+
+ /* add slot to our internal list */
+ list_add(&slot->slot_list, &slot_list);
+ }
+
+ return 0;
+error_name:
+ kfree(name);
+error_info:
+ kfree(info);
+error_hpslot:
+ kfree(hotplug_slot);
+error_slot:
+ kfree(slot);
+error:
+ return retval;
+}
+
+static void __exit cleanup_slots(void)
+{
+ struct list_head *tmp;
+ struct list_head *next;
+ struct slot *slot;
+
+ /*
+ * Unregister all of our slots with the pci_hotplug subsystem.
+ * Memory will be freed in release_slot() callback after slot's
+ * lifespan is finished.
+ */
+ list_for_each_safe(tmp, next, &slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ list_del(&slot->slot_list);
+ pci_hp_deregister(slot->hotplug_slot);
+ }
+}
+
+static int __init pcihp_skel_init(void)
+{
+ int retval;
+
+ info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+ /*
+ * Do specific initialization stuff for your driver here
+ * Like initializing your controller hardware (if any) and
+ * determining the number of slots you have in the system
+ * right now.
+ */
+ num_slots = 5;
+
+ return init_slots();
+}
+
+static void __exit pcihp_skel_exit(void)
+{
+ /*
+ * Clean everything up.
+ */
+ cleanup_slots();
+}
+
+module_init(pcihp_skel_init);
+module_exit(pcihp_skel_exit);
diff --git a/drivers/pci/hotplug/rpadlpar.h b/drivers/pci/hotplug/rpadlpar.h
new file mode 100644
index 000000000000..4a0a59b82eae
--- /dev/null
+++ b/drivers/pci/hotplug/rpadlpar.h
@@ -0,0 +1,24 @@
+/*
+ * Interface for Dynamic Logical Partitioning of I/O Slots on
+ * RPA-compliant PPC64 platform.
+ *
+ * John Rose <johnrose@austin.ibm.com>
+ * October 2003
+ *
+ * Copyright (C) 2003 IBM.
+ *
+ * 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.
+ */
+#ifndef _RPADLPAR_IO_H_
+#define _RPADLPAR_IO_H_
+
+extern int dlpar_sysfs_init(void);
+extern void dlpar_sysfs_exit(void);
+
+extern int dlpar_add_slot(char *drc_name);
+extern int dlpar_remove_slot(char *drc_name);
+
+#endif
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
new file mode 100644
index 000000000000..86b384e42717
--- /dev/null
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -0,0 +1,503 @@
+/*
+ * Interface for Dynamic Logical Partitioning of I/O Slots on
+ * RPA-compliant PPC64 platform.
+ *
+ * John Rose <johnrose@austin.ibm.com>
+ * Linda Xie <lxie@us.ibm.com>
+ *
+ * October 2003
+ *
+ * Copyright (C) 2003 IBM.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/pci-bridge.h>
+#include <asm/semaphore.h>
+#include <asm/rtas.h>
+#include "../pci.h"
+#include "rpaphp.h"
+#include "rpadlpar.h"
+
+static DECLARE_MUTEX(rpadlpar_sem);
+
+#define NODE_TYPE_VIO 1
+#define NODE_TYPE_SLOT 2
+#define NODE_TYPE_PHB 3
+
+static struct device_node *find_php_slot_vio_node(char *drc_name)
+{
+ struct device_node *child;
+ struct device_node *parent = of_find_node_by_name(NULL, "vdevice");
+ char *loc_code;
+
+ if (!parent)
+ return NULL;
+
+ for (child = of_get_next_child(parent, NULL);
+ child; child = of_get_next_child(parent, child)) {
+ loc_code = get_property(child, "ibm,loc-code", NULL);
+ if (loc_code && !strncmp(loc_code, drc_name, strlen(drc_name)))
+ return child;
+ }
+
+ return NULL;
+}
+
+/* Find dlpar-capable pci node that contains the specified name and type */
+static struct device_node *find_php_slot_pci_node(char *drc_name,
+ char *drc_type)
+{
+ struct device_node *np = NULL;
+ char *name;
+ char *type;
+ int rc;
+
+ while ((np = of_find_node_by_type(np, "pci"))) {
+ rc = rpaphp_get_drc_props(np, NULL, &name, &type, NULL);
+ if (rc == 0)
+ if (!strcmp(drc_name, name) && !strcmp(drc_type, type))
+ break;
+ }
+
+ return np;
+}
+
+static struct device_node *find_newly_added_node(char *drc_name, int *node_type)
+{
+ struct device_node *dn;
+
+ dn = find_php_slot_pci_node(drc_name, "SLOT");
+ if (dn) {
+ *node_type = NODE_TYPE_SLOT;
+ return dn;
+ }
+
+ dn = find_php_slot_pci_node(drc_name, "PHB");
+ if (dn) {
+ *node_type = NODE_TYPE_PHB;
+ return dn;
+ }
+
+ dn = find_php_slot_vio_node(drc_name);
+ if (dn) {
+ *node_type = NODE_TYPE_VIO;
+ return dn;
+ }
+
+ return NULL;
+}
+
+static struct slot *find_slot(char *drc_name)
+{
+ struct list_head *tmp, *n;
+ struct slot *slot;
+
+ list_for_each_safe(tmp, n, &rpaphp_slot_head) {
+ slot = list_entry(tmp, struct slot, rpaphp_slot_list);
+ if (strcmp(slot->location, drc_name) == 0)
+ return slot;
+ }
+
+ return NULL;
+}
+
+static void rpadlpar_claim_one_bus(struct pci_bus *b)
+{
+ struct list_head *ld;
+ struct pci_bus *child_bus;
+
+ for (ld = b->devices.next; ld != &b->devices; ld = ld->next) {
+ struct pci_dev *dev = pci_dev_b(ld);
+ int i;
+
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ struct resource *r = &dev->resource[i];
+
+ if (r->parent || !r->start || !r->flags)
+ continue;
+ rpaphp_claim_resource(dev, i);
+ }
+ }
+
+ list_for_each_entry(child_bus, &b->children, node)
+ rpadlpar_claim_one_bus(child_bus);
+}
+
+static int pci_add_secondary_bus(struct device_node *dn,
+ struct pci_dev *bridge_dev)
+{
+ struct pci_controller *hose = dn->phb;
+ struct pci_bus *child;
+ u8 sec_busno;
+
+ /* Get busno of downstream bus */
+ pci_read_config_byte(bridge_dev, PCI_SECONDARY_BUS, &sec_busno);
+
+ /* Allocate and add to children of bridge_dev->bus */
+ child = pci_add_new_bus(bridge_dev->bus, bridge_dev, sec_busno);
+ if (!child) {
+ printk(KERN_ERR "%s: could not add secondary bus\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ sprintf(child->name, "PCI Bus #%02x", child->number);
+
+ /* Fixup subordinate bridge bases and resources */
+ pcibios_fixup_bus(child);
+
+ /* Claim new bus resources */
+ rpadlpar_claim_one_bus(bridge_dev->bus);
+
+ if (hose->last_busno < child->number)
+ hose->last_busno = child->number;
+
+ dn->bussubno = child->number;
+
+ /* ioremap() for child bus, which may or may not succeed */
+ remap_bus_range(child);
+
+ return 0;
+}
+
+static struct pci_dev *dlpar_pci_add_bus(struct device_node *dn)
+{
+ struct pci_controller *hose = dn->phb;
+ struct pci_dev *dev = NULL;
+
+ /* Scan phb bus for EADS device, adding new one to bus->devices */
+ if (!pci_scan_single_device(hose->bus, dn->devfn)) {
+ printk(KERN_ERR "%s: found no device on bus\n", __FUNCTION__);
+ return NULL;
+ }
+
+ /* Add new devices to global lists. Register in proc, sysfs. */
+ pci_bus_add_devices(hose->bus);
+
+ /* Confirm new bridge dev was created */
+ dev = rpaphp_find_pci_dev(dn);
+ if (!dev) {
+ printk(KERN_ERR "%s: failed to add pci device\n", __FUNCTION__);
+ return NULL;
+ }
+
+ if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
+ printk(KERN_ERR "%s: unexpected header type %d\n",
+ __FUNCTION__, dev->hdr_type);
+ return NULL;
+ }
+
+ if (pci_add_secondary_bus(dn, dev))
+ return NULL;
+
+ return dev;
+}
+
+static int dlpar_pci_remove_bus(struct pci_dev *bridge_dev)
+{
+ struct pci_bus *secondary_bus;
+
+ if (!bridge_dev) {
+ printk(KERN_ERR "%s: unexpected null device\n",
+ __FUNCTION__);
+ return -EINVAL;
+ }
+
+ secondary_bus = bridge_dev->subordinate;
+
+ if (unmap_bus_range(secondary_bus)) {
+ printk(KERN_ERR "%s: failed to unmap bus range\n",
+ __FUNCTION__);
+ return -ERANGE;
+ }
+
+ pci_remove_bus_device(bridge_dev);
+ return 0;
+}
+
+static inline int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
+{
+ struct pci_dev *dev;
+
+ /* Add pci bus */
+ dev = dlpar_pci_add_bus(dn);
+ if (!dev) {
+ printk(KERN_ERR "%s: unable to add bus %s\n", __FUNCTION__,
+ drc_name);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int dlpar_remove_root_bus(struct pci_controller *phb)
+{
+ struct pci_bus *phb_bus;
+ int rc;
+
+ phb_bus = phb->bus;
+ if (!(list_empty(&phb_bus->children) &&
+ list_empty(&phb_bus->devices))) {
+ return -EBUSY;
+ }
+
+ rc = pcibios_remove_root_bus(phb);
+ if (rc)
+ return -EIO;
+
+ device_unregister(phb_bus->bridge);
+ pci_remove_bus(phb_bus);
+
+ return 0;
+}
+
+static int dlpar_remove_phb(struct slot *slot)
+{
+ struct pci_controller *phb;
+ struct device_node *dn;
+ int rc = 0;
+
+ dn = slot->dn;
+ if (!dn) {
+ printk(KERN_ERR "%s: unexpected NULL slot device node\n",
+ __FUNCTION__);
+ return -EIO;
+ }
+
+ phb = dn->phb;
+ if (!phb) {
+ printk(KERN_ERR "%s: unexpected NULL phb pointer\n",
+ __FUNCTION__);
+ return -EIO;
+ }
+
+ if (rpaphp_remove_slot(slot)) {
+ printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
+ __FUNCTION__, slot->location);
+ return -EIO;
+ }
+
+ rc = dlpar_remove_root_bus(phb);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static int dlpar_add_phb(struct device_node *dn)
+{
+ struct pci_controller *phb;
+
+ phb = init_phb_dynamic(dn);
+ if (!phb)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * dlpar_add_slot - DLPAR add an I/O Slot
+ * @drc_name: drc-name of newly added slot
+ *
+ * Make the hotplug module and the kernel aware
+ * of a newly added I/O Slot.
+ * Return Codes -
+ * 0 Success
+ * -ENODEV Not a valid drc_name
+ * -EINVAL Slot already added
+ * -ERESTARTSYS Signalled before obtaining lock
+ * -EIO Internal PCI Error
+ */
+int dlpar_add_slot(char *drc_name)
+{
+ struct device_node *dn = NULL;
+ int node_type;
+ int rc = 0;
+
+ if (down_interruptible(&rpadlpar_sem))
+ return -ERESTARTSYS;
+
+ /* Check for existing hotplug slot */
+ if (find_slot(drc_name)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ dn = find_newly_added_node(drc_name, &node_type);
+ if (!dn) {
+ rc = -ENODEV;
+ goto exit;
+ }
+
+ switch (node_type) {
+ case NODE_TYPE_VIO:
+ /* Just add hotplug slot */
+ break;
+ case NODE_TYPE_SLOT:
+ rc = dlpar_add_pci_slot(drc_name, dn);
+ break;
+ case NODE_TYPE_PHB:
+ rc = dlpar_add_phb(dn);
+ break;
+ default:
+ printk("%s: unexpected node type\n", __FUNCTION__);
+ return -EIO;
+ }
+
+ if (!rc && rpaphp_add_slot(dn)) {
+ printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
+ __FUNCTION__, drc_name);
+ rc = -EIO;
+ }
+exit:
+ up(&rpadlpar_sem);
+ return rc;
+}
+
+/**
+ * dlpar_remove_vio_slot - DLPAR remove a virtual I/O Slot
+ * @drc_name: drc-name of newly added slot
+ *
+ * Remove the kernel and hotplug representations
+ * of an I/O Slot.
+ * Return Codes:
+ * 0 Success
+ * -EIO Internal Error
+ */
+int dlpar_remove_vio_slot(struct slot *slot, char *drc_name)
+{
+ /* Remove hotplug slot */
+
+ if (rpaphp_remove_slot(slot)) {
+ printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
+ __FUNCTION__, drc_name);
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
+ * dlpar_remove_slot - DLPAR remove a PCI I/O Slot
+ * @drc_name: drc-name of newly added slot
+ *
+ * Remove the kernel and hotplug representations
+ * of a PCI I/O Slot.
+ * Return Codes:
+ * 0 Success
+ * -ENODEV Not a valid drc_name
+ * -EIO Internal PCI Error
+ */
+int dlpar_remove_pci_slot(struct slot *slot, char *drc_name)
+{
+ struct pci_dev *bridge_dev;
+
+ bridge_dev = slot->bridge;
+ if (!bridge_dev) {
+ printk(KERN_ERR "%s: unexpected null bridge device\n",
+ __FUNCTION__);
+ return -EIO;
+ }
+
+ /* Remove hotplug slot */
+ if (rpaphp_remove_slot(slot)) {
+ printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
+ __FUNCTION__, drc_name);
+ return -EIO;
+ }
+
+ /* Remove pci bus */
+
+ if (dlpar_pci_remove_bus(bridge_dev)) {
+ printk(KERN_ERR "%s: unable to remove pci bus %s\n",
+ __FUNCTION__, drc_name);
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
+ * dlpar_remove_slot - DLPAR remove an I/O Slot
+ * @drc_name: drc-name of newly added slot
+ *
+ * Remove the kernel and hotplug representations
+ * of an I/O Slot.
+ * Return Codes:
+ * 0 Success
+ * -ENODEV Not a valid drc_name
+ * -EINVAL Slot already removed
+ * -ERESTARTSYS Signalled before obtaining lock
+ * -EIO Internal Error
+ */
+int dlpar_remove_slot(char *drc_name)
+{
+ struct slot *slot;
+ int rc = 0;
+
+ if (down_interruptible(&rpadlpar_sem))
+ return -ERESTARTSYS;
+
+ if (!find_php_slot_vio_node(drc_name) &&
+ !find_php_slot_pci_node(drc_name, "SLOT") &&
+ !find_php_slot_pci_node(drc_name, "PHB")) {
+ rc = -ENODEV;
+ goto exit;
+ }
+
+ slot = find_slot(drc_name);
+ if (!slot) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (slot->type == PHB) {
+ rc = dlpar_remove_phb(slot);
+ } else {
+ switch (slot->dev_type) {
+ case PCI_DEV:
+ rc = dlpar_remove_pci_slot(slot, drc_name);
+ break;
+
+ case VIO_DEV:
+ rc = dlpar_remove_vio_slot(slot, drc_name);
+ break;
+ }
+ }
+exit:
+ up(&rpadlpar_sem);
+ return rc;
+}
+
+static inline int is_dlpar_capable(void)
+{
+ int rc = rtas_token("ibm,configure-connector");
+
+ return (int) (rc != RTAS_UNKNOWN_SERVICE);
+}
+
+int __init rpadlpar_io_init(void)
+{
+ int rc = 0;
+
+ if (!is_dlpar_capable()) {
+ printk(KERN_WARNING "%s: partition not DLPAR capable\n",
+ __FUNCTION__);
+ return -EPERM;
+ }
+
+ rc = dlpar_sysfs_init();
+ return rc;
+}
+
+void rpadlpar_io_exit(void)
+{
+ dlpar_sysfs_exit();
+ return;
+}
+
+module_init(rpadlpar_io_init);
+module_exit(rpadlpar_io_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c
new file mode 100644
index 000000000000..3285b822478d
--- /dev/null
+++ b/drivers/pci/hotplug/rpadlpar_sysfs.c
@@ -0,0 +1,151 @@
+/*
+ * Interface for Dynamic Logical Partitioning of I/O Slots on
+ * RPA-compliant PPC64 platform.
+ *
+ * John Rose <johnrose@austin.ibm.com>
+ * October 2003
+ *
+ * Copyright (C) 2003 IBM.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include "pci_hotplug.h"
+#include "rpadlpar.h"
+
+#define DLPAR_KOBJ_NAME "control"
+#define ADD_SLOT_ATTR_NAME "add_slot"
+#define REMOVE_SLOT_ATTR_NAME "remove_slot"
+
+#define MAX_DRC_NAME_LEN 64
+
+/* Store return code of dlpar operation in attribute struct */
+struct dlpar_io_attr {
+ int rc;
+ struct attribute attr;
+ ssize_t (*store)(struct dlpar_io_attr *dlpar_attr, const char *buf,
+ size_t nbytes);
+};
+
+/* Common show callback for all attrs, display the return code
+ * of the dlpar op */
+static ssize_t
+dlpar_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
+{
+ struct dlpar_io_attr *dlpar_attr = container_of(attr,
+ struct dlpar_io_attr, attr);
+ return sprintf(buf, "%d\n", dlpar_attr->rc);
+}
+
+static ssize_t
+dlpar_attr_store(struct kobject * kobj, struct attribute * attr,
+ const char *buf, size_t nbytes)
+{
+ struct dlpar_io_attr *dlpar_attr = container_of(attr,
+ struct dlpar_io_attr, attr);
+ return dlpar_attr->store ?
+ dlpar_attr->store(dlpar_attr, buf, nbytes) : 0;
+}
+
+static struct sysfs_ops dlpar_attr_sysfs_ops = {
+ .show = dlpar_attr_show,
+ .store = dlpar_attr_store,
+};
+
+static ssize_t add_slot_store(struct dlpar_io_attr *dlpar_attr,
+ const char *buf, size_t nbytes)
+{
+ char drc_name[MAX_DRC_NAME_LEN];
+ char *end;
+
+ if (nbytes > MAX_DRC_NAME_LEN)
+ return 0;
+
+ memcpy(drc_name, buf, nbytes);
+
+ end = strchr(drc_name, '\n');
+ if (!end)
+ end = &drc_name[nbytes];
+ *end = '\0';
+
+ dlpar_attr->rc = dlpar_add_slot(drc_name);
+
+ return nbytes;
+}
+
+static ssize_t remove_slot_store(struct dlpar_io_attr *dlpar_attr,
+ const char *buf, size_t nbytes)
+{
+ char drc_name[MAX_DRC_NAME_LEN];
+ char *end;
+
+ if (nbytes > MAX_DRC_NAME_LEN)
+ return 0;
+
+ memcpy(drc_name, buf, nbytes);
+
+ end = strchr(drc_name, '\n');
+ if (!end)
+ end = &drc_name[nbytes];
+ *end = '\0';
+
+ dlpar_attr->rc = dlpar_remove_slot(drc_name);
+
+ return nbytes;
+}
+
+static struct dlpar_io_attr add_slot_attr = {
+ .rc = 0,
+ .attr = { .name = ADD_SLOT_ATTR_NAME, .mode = 0644, },
+ .store = add_slot_store,
+};
+
+static struct dlpar_io_attr remove_slot_attr = {
+ .rc = 0,
+ .attr = { .name = REMOVE_SLOT_ATTR_NAME, .mode = 0644},
+ .store = remove_slot_store,
+};
+
+static struct attribute *default_attrs[] = {
+ &add_slot_attr.attr,
+ &remove_slot_attr.attr,
+ NULL,
+};
+
+static void dlpar_io_release(struct kobject *kobj)
+{
+ /* noop */
+ return;
+}
+
+struct kobj_type ktype_dlpar_io = {
+ .release = dlpar_io_release,
+ .sysfs_ops = &dlpar_attr_sysfs_ops,
+ .default_attrs = default_attrs,
+};
+
+struct kset dlpar_io_kset = {
+ .subsys = &pci_hotplug_slots_subsys,
+ .kobj = {.name = DLPAR_KOBJ_NAME, .ktype=&ktype_dlpar_io,},
+ .ktype = &ktype_dlpar_io,
+};
+
+int dlpar_sysfs_init(void)
+{
+ if (kset_register(&dlpar_io_kset)) {
+ printk(KERN_ERR "rpadlpar_io: cannot register kset for %s\n",
+ dlpar_io_kset.kobj.name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void dlpar_sysfs_exit(void)
+{
+ kset_unregister(&dlpar_io_kset);
+}
diff --git a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h
new file mode 100644
index 000000000000..81746e6e0e0f
--- /dev/null
+++ b/drivers/pci/hotplug/rpaphp.h
@@ -0,0 +1,138 @@
+/*
+ * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
+ *
+ * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
+ *
+ * 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 <lxie@us.ibm.com>,
+ *
+ */
+
+#ifndef _PPC64PHP_H
+#define _PPC64PHP_H
+
+#include <linux/pci.h>
+#include "pci_hotplug.h"
+
+#define PHB 2
+#define HOTPLUG 1
+#define EMBEDDED 0
+
+#define DR_INDICATOR 9002
+#define DR_ENTITY_SENSE 9003
+
+#define POWER_ON 100
+#define POWER_OFF 0
+
+#define LED_OFF 0
+#define LED_ON 1 /* continuous on */
+#define LED_ID 2 /* slow blinking */
+#define LED_ACTION 3 /* fast blinking */
+
+/* Sensor values from rtas_get-sensor */
+#define EMPTY 0 /* No card in slot */
+#define PRESENT 1 /* Card in slot */
+
+#define MY_NAME "rpaphp"
+extern int debug;
+#define dbg(format, arg...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: " format, \
+ 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)
+
+/* slot types */
+#define VIO_DEV 1
+#define PCI_DEV 2
+
+/* slot states */
+
+#define NOT_VALID 3
+#define NOT_CONFIGURED 2
+#define CONFIGURED 1
+#define EMPTY 0
+
+struct rpaphp_pci_func {
+ struct pci_dev *pci_dev;
+ struct list_head sibling;
+};
+
+/*
+ * struct slot - slot information for each *physical* slot
+ */
+struct slot {
+ struct list_head rpaphp_slot_list;
+ int state;
+ u32 index;
+ u32 type;
+ u32 power_domain;
+ char *name;
+ char *location;
+ u8 removable;
+ u8 dev_type; /* VIO or PCI */
+ struct device_node *dn; /* slot's device_node in OFDT */
+ /* dn has phb info */
+ struct pci_dev *bridge; /* slot's pci_dev in pci_devices */
+ union {
+ struct list_head *pci_devs; /* pci_devs in PCI slot */
+ struct vio_dev *vio_dev; /* vio_dev in VIO slot */
+ } dev;
+ struct hotplug_slot *hotplug_slot;
+};
+
+extern struct hotplug_slot_ops rpaphp_hotplug_slot_ops;
+extern struct list_head rpaphp_slot_head;
+extern int num_slots;
+
+/* function prototypes */
+
+/* rpaphp_pci.c */
+extern struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn);
+extern int rpaphp_claim_resource(struct pci_dev *dev, int resource);
+extern int rpaphp_enable_pci_slot(struct slot *slot);
+extern int register_pci_slot(struct slot *slot);
+extern int rpaphp_unconfig_pci_adapter(struct slot *slot);
+extern int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value);
+extern struct hotplug_slot *rpaphp_find_hotplug_slot(struct pci_dev *dev);
+
+/* rpaphp_core.c */
+extern int rpaphp_add_slot(struct device_node *dn);
+extern int rpaphp_remove_slot(struct slot *slot);
+extern int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
+ char **drc_name, char **drc_type, int *drc_power_domain);
+
+/* rpaphp_vio.c */
+extern int rpaphp_get_vio_adapter_status(struct slot *slot, int is_init, u8 * value);
+extern int rpaphp_unconfig_vio_adapter(struct slot *slot);
+extern int register_vio_slot(struct device_node *dn);
+extern int rpaphp_enable_vio_slot(struct slot *slot);
+
+/* rpaphp_slot.c */
+extern void dealloc_slot_struct(struct slot *slot);
+extern struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name, int power_domain);
+extern int register_slot(struct slot *slot);
+extern int deregister_slot(struct slot *slot);
+extern int rpaphp_get_power_status(struct slot *slot, u8 * value);
+extern int rpaphp_set_attention_status(struct slot *slot, u8 status);
+
+#endif /* _PPC64PHP_H */
diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c
new file mode 100644
index 000000000000..29117a3a3287
--- /dev/null
+++ b/drivers/pci/hotplug/rpaphp_core.c
@@ -0,0 +1,536 @@
+/*
+ * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
+ * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
+ *
+ * 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 <lxie@us.ibm.com>
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <asm/eeh.h> /* for eeh_add_device() */
+#include <asm/rtas.h> /* rtas_call */
+#include <asm/pci-bridge.h> /* for pci_controller */
+#include "../pci.h" /* for pci_add_new_bus */
+ /* and pci_do_scan_bus */
+#include "rpaphp.h"
+#include "pci_hotplug.h"
+
+int debug;
+static struct semaphore rpaphp_sem;
+LIST_HEAD(rpaphp_slot_head);
+int num_slots;
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_AUTHOR "Linda Xie <lxie@us.ibm.com>"
+#define DRIVER_DESC "RPA HOT Plug PCI Controller Driver"
+
+#define MAX_LOC_CODE 128
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, 0644);
+
+static int enable_slot(struct hotplug_slot *slot);
+static int disable_slot(struct hotplug_slot *slot);
+static int set_attention_status(struct hotplug_slot *slot, u8 value);
+static int get_power_status(struct hotplug_slot *slot, u8 * value);
+static int get_attention_status(struct hotplug_slot *slot, u8 * value);
+static int get_adapter_status(struct hotplug_slot *slot, u8 * value);
+static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value);
+
+struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .enable_slot = enable_slot,
+ .disable_slot = disable_slot,
+ .set_attention_status = set_attention_status,
+ .get_power_status = get_power_status,
+ .get_attention_status = get_attention_status,
+ .get_adapter_status = get_adapter_status,
+ .get_max_bus_speed = get_max_bus_speed,
+};
+
+static int rpaphp_get_attention_status(struct slot *slot)
+{
+ return slot->hotplug_slot->info->attention_status;
+}
+
+/**
+ * set_attention_status - set attention LED
+ * echo 0 > attention -- set LED OFF
+ * echo 1 > attention -- set LED ON
+ * echo 2 > attention -- set LED ID(identify, light is blinking)
+ *
+ */
+static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
+{
+ int retval = 0;
+ struct slot *slot = (struct slot *)hotplug_slot->private;
+
+ down(&rpaphp_sem);
+ switch (value) {
+ case 0:
+ retval = rpaphp_set_attention_status(slot, LED_OFF);
+ hotplug_slot->info->attention_status = 0;
+ break;
+ case 1:
+ default:
+ retval = rpaphp_set_attention_status(slot, LED_ON);
+ hotplug_slot->info->attention_status = 1;
+ break;
+ case 2:
+ retval = rpaphp_set_attention_status(slot, LED_ID);
+ hotplug_slot->info->attention_status = 2;
+ break;
+ }
+ up(&rpaphp_sem);
+ return retval;
+}
+
+/**
+ * get_power_status - get power status of a slot
+ * @hotplug_slot: slot to get status
+ * @value: pointer to store status
+ *
+ *
+ */
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
+{
+ int retval;
+ struct slot *slot = (struct slot *)hotplug_slot->private;
+
+ down(&rpaphp_sem);
+ retval = rpaphp_get_power_status(slot, value);
+ up(&rpaphp_sem);
+ return retval;
+}
+
+/**
+ * get_attention_status - get attention LED status
+ *
+ *
+ */
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
+{
+ int retval = 0;
+ struct slot *slot = (struct slot *)hotplug_slot->private;
+
+ down(&rpaphp_sem);
+ *value = rpaphp_get_attention_status(slot);
+ up(&rpaphp_sem);
+ return retval;
+}
+
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
+{
+ struct slot *slot = (struct slot *)hotplug_slot->private;
+ int retval = 0;
+
+ down(&rpaphp_sem);
+ /* have to go through this */
+ switch (slot->dev_type) {
+ case PCI_DEV:
+ retval = rpaphp_get_pci_adapter_status(slot, 0, value);
+ break;
+ case VIO_DEV:
+ retval = rpaphp_get_vio_adapter_status(slot, 0, value);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ up(&rpaphp_sem);
+ return retval;
+}
+
+static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+{
+ struct slot *slot = (struct slot *)hotplug_slot->private;
+
+ down(&rpaphp_sem);
+ switch (slot->type) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ *value = PCI_SPEED_33MHz; /* speed for case 1-6 */
+ break;
+ case 7:
+ case 8:
+ *value = PCI_SPEED_66MHz;
+ break;
+ case 11:
+ case 14:
+ *value = PCI_SPEED_66MHz_PCIX;
+ break;
+ case 12:
+ case 15:
+ *value = PCI_SPEED_100MHz_PCIX;
+ break;
+ case 13:
+ case 16:
+ *value = PCI_SPEED_133MHz_PCIX;
+ break;
+ default:
+ *value = PCI_SPEED_UNKNOWN;
+ break;
+
+ }
+ up(&rpaphp_sem);
+ return 0;
+}
+
+int rpaphp_remove_slot(struct slot *slot)
+{
+ return deregister_slot(slot);
+}
+
+static int get_children_props(struct device_node *dn, int **drc_indexes,
+ int **drc_names, int **drc_types, int **drc_power_domains)
+{
+ int *indexes, *names;
+ int *types, *domains;
+
+ indexes = (int *) get_property(dn, "ibm,drc-indexes", NULL);
+ names = (int *) get_property(dn, "ibm,drc-names", NULL);
+ types = (int *) get_property(dn, "ibm,drc-types", NULL);
+ domains = (int *) get_property(dn, "ibm,drc-power-domains", NULL);
+
+ if (!indexes || !names || !types || !domains) {
+ /* Slot does not have dynamically-removable children */
+ return -EINVAL;
+ }
+ if (drc_indexes)
+ *drc_indexes = indexes;
+ if (drc_names)
+ /* &drc_names[1] contains NULL terminated slot names */
+ *drc_names = names;
+ if (drc_types)
+ /* &drc_types[1] contains NULL terminated slot types */
+ *drc_types = types;
+ if (drc_power_domains)
+ *drc_power_domains = domains;
+
+ return 0;
+}
+
+/* To get the DRC props describing the current node, first obtain it's
+ * my-drc-index property. Next obtain the DRC list from it's parent. Use
+ * the my-drc-index for correlation, and obtain the requested properties.
+ */
+int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
+ char **drc_name, char **drc_type, int *drc_power_domain)
+{
+ int *indexes, *names;
+ int *types, *domains;
+ unsigned int *my_index;
+ char *name_tmp, *type_tmp;
+ int i, rc;
+
+ my_index = (int *) get_property(dn, "ibm,my-drc-index", NULL);
+ if (!my_index) {
+ /* Node isn't DLPAR/hotplug capable */
+ return -EINVAL;
+ }
+
+ rc = get_children_props(dn->parent, &indexes, &names, &types, &domains);
+ if (rc < 0) {
+ return -EINVAL;
+ }
+
+ name_tmp = (char *) &names[1];
+ type_tmp = (char *) &types[1];
+
+ /* Iterate through parent properties, looking for my-drc-index */
+ for (i = 0; i < indexes[0]; i++) {
+ if ((unsigned int) indexes[i + 1] == *my_index) {
+ if (drc_name)
+ *drc_name = name_tmp;
+ if (drc_type)
+ *drc_type = type_tmp;
+ if (drc_index)
+ *drc_index = *my_index;
+ if (drc_power_domain)
+ *drc_power_domain = domains[i+1];
+ return 0;
+ }
+ name_tmp += (strlen(name_tmp) + 1);
+ type_tmp += (strlen(type_tmp) + 1);
+ }
+
+ return -EINVAL;
+}
+
+static int is_php_type(char *drc_type)
+{
+ unsigned long value;
+ char *endptr;
+
+ /* PCI Hotplug nodes have an integer for drc_type */
+ value = simple_strtoul(drc_type, &endptr, 10);
+ if (endptr == drc_type)
+ return 0;
+
+ return 1;
+}
+
+static int is_php_dn(struct device_node *dn, int **indexes, int **names,
+ int **types, int **power_domains)
+{
+ int *drc_types;
+ int rc;
+
+ rc = get_children_props(dn, indexes, names, &drc_types, power_domains);
+ if (rc >= 0) {
+ if (is_php_type((char *) &drc_types[1])) {
+ *types = drc_types;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int is_dr_dn(struct device_node *dn, int **indexes, int **names,
+ int **types, int **power_domains, int **my_drc_index)
+{
+ int rc;
+
+ *my_drc_index = (int *) get_property(dn, "ibm,my-drc-index", NULL);
+ if(!*my_drc_index)
+ return (0);
+
+ if (!dn->parent)
+ return (0);
+
+ rc = get_children_props(dn->parent, indexes, names, types,
+ power_domains);
+ return (rc >= 0);
+}
+
+static inline int is_vdevice_root(struct device_node *dn)
+{
+ return !strcmp(dn->name, "vdevice");
+}
+
+int is_dlpar_type(const char *type_str)
+{
+ /* Only register DLPAR-capable nodes of drc-type PHB or SLOT */
+ return (!strcmp(type_str, "PHB") || !strcmp(type_str, "SLOT"));
+}
+
+/****************************************************************
+ * rpaphp not only registers PCI hotplug slots(HOTPLUG),
+ * but also logical DR slots(EMBEDDED).
+ * HOTPLUG slot: An adapter can be physically added/removed.
+ * EMBEDDED slot: An adapter can be logically removed/added
+ * from/to a partition with the slot.
+ ***************************************************************/
+int rpaphp_add_slot(struct device_node *dn)
+{
+ struct slot *slot;
+ int retval = 0;
+ int i, *my_drc_index, slot_type;
+ int *indexes, *names, *types, *power_domains;
+ char *name, *type;
+
+ dbg("Entry %s: dn->full_name=%s\n", __FUNCTION__, dn->full_name);
+
+ if (dn->parent && is_vdevice_root(dn->parent)) {
+ /* register a VIO device */
+ retval = register_vio_slot(dn);
+ goto exit;
+ }
+
+ /* register PCI devices */
+ if (dn->name != 0 && strcmp(dn->name, "pci") == 0) {
+ if (is_php_dn(dn, &indexes, &names, &types, &power_domains))
+ slot_type = HOTPLUG;
+ else if (is_dr_dn(dn, &indexes, &names, &types, &power_domains, &my_drc_index))
+ slot_type = EMBEDDED;
+ else goto exit;
+
+ name = (char *) &names[1];
+ type = (char *) &types[1];
+ for (i = 0; i < indexes[0]; i++,
+ name += (strlen(name) + 1), type += (strlen(type) + 1)) {
+
+ if (slot_type == HOTPLUG ||
+ (slot_type == EMBEDDED &&
+ indexes[i + 1] == my_drc_index[0] &&
+ is_dlpar_type(type))) {
+ if (!(slot = alloc_slot_struct(dn, indexes[i + 1], name,
+ power_domains[i + 1]))) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+ if (!strcmp(type, "PHB"))
+ slot->type = PHB;
+ else if (slot_type == EMBEDDED)
+ slot->type = EMBEDDED;
+ else
+ slot->type = simple_strtoul(type, NULL, 10);
+
+ dbg(" Found drc-index:0x%x drc-name:%s drc-type:%s\n",
+ indexes[i + 1], name, type);
+
+ retval = register_pci_slot(slot);
+ if (slot_type == EMBEDDED)
+ goto exit;
+ }
+ }
+ }
+exit:
+ dbg("%s - Exit: num_slots=%d rc[%d]\n",
+ __FUNCTION__, num_slots, retval);
+ return retval;
+}
+
+/*
+ * init_slots - initialize 'struct slot' structures for each slot
+ *
+ */
+static void init_slots(void)
+{
+ struct device_node *dn;
+
+ for (dn = find_all_nodes(); dn; dn = dn->next)
+ rpaphp_add_slot(dn);
+}
+
+static int __init init_rpa(void)
+{
+
+ init_MUTEX(&rpaphp_sem);
+
+ /* initialize internal data structure etc. */
+ init_slots();
+ if (!num_slots)
+ return -ENODEV;
+
+ return 0;
+}
+
+static void __exit cleanup_slots(void)
+{
+ struct list_head *tmp, *n;
+ struct slot *slot;
+
+ /*
+ * Unregister all of our slots with the pci_hotplug subsystem,
+ * and free up all memory that we had allocated.
+ * memory will be freed in release_slot callback.
+ */
+
+ list_for_each_safe(tmp, n, &rpaphp_slot_head) {
+ slot = list_entry(tmp, struct slot, rpaphp_slot_list);
+ list_del(&slot->rpaphp_slot_list);
+ pci_hp_deregister(slot->hotplug_slot);
+ }
+ return;
+}
+
+static int __init rpaphp_init(void)
+{
+ info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+ /* read all the PRA info from the system */
+ return init_rpa();
+}
+
+static void __exit rpaphp_exit(void)
+{
+ cleanup_slots();
+}
+
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+ int retval = 0;
+ struct slot *slot = (struct slot *)hotplug_slot->private;
+
+ if (slot->state == CONFIGURED) {
+ dbg("%s: %s is already enabled\n", __FUNCTION__, slot->name);
+ goto exit;
+ }
+
+ dbg("ENABLING SLOT %s\n", slot->name);
+ down(&rpaphp_sem);
+ switch (slot->dev_type) {
+ case PCI_DEV:
+ retval = rpaphp_enable_pci_slot(slot);
+ break;
+ case VIO_DEV:
+ retval = rpaphp_enable_vio_slot(slot);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ up(&rpaphp_sem);
+exit:
+ dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
+ return retval;
+}
+
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+ int retval = -EINVAL;
+ struct slot *slot = (struct slot *)hotplug_slot->private;
+
+ dbg("%s - Entry: slot[%s]\n", __FUNCTION__, slot->name);
+
+ if (slot->state == NOT_CONFIGURED) {
+ dbg("%s: %s is already disabled\n", __FUNCTION__, slot->name);
+ goto exit;
+ }
+
+ dbg("DISABLING SLOT %s\n", slot->name);
+ down(&rpaphp_sem);
+ switch (slot->dev_type) {
+ case PCI_DEV:
+ retval = rpaphp_unconfig_pci_adapter(slot);
+ break;
+ case VIO_DEV:
+ retval = rpaphp_unconfig_vio_adapter(slot);
+ break;
+ default:
+ retval = -ENODEV;
+ }
+ up(&rpaphp_sem);
+exit:
+ dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
+ return retval;
+}
+
+module_init(rpaphp_init);
+module_exit(rpaphp_exit);
+
+EXPORT_SYMBOL_GPL(rpaphp_add_slot);
+EXPORT_SYMBOL_GPL(rpaphp_remove_slot);
+EXPORT_SYMBOL_GPL(rpaphp_slot_head);
+EXPORT_SYMBOL_GPL(rpaphp_get_drc_props);
diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c
new file mode 100644
index 000000000000..d8305a935aab
--- /dev/null
+++ b/drivers/pci/hotplug/rpaphp_pci.c
@@ -0,0 +1,538 @@
+/*
+ * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
+ * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
+ *
+ * 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 <lxie@us.ibm.com>
+ *
+ */
+#include <linux/pci.h>
+#include <asm/pci-bridge.h>
+#include <asm/rtas.h>
+#include <asm/machdep.h>
+#include "../pci.h" /* for pci_add_new_bus */
+
+#include "rpaphp.h"
+
+struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn)
+{
+ struct pci_dev *dev = NULL;
+ char bus_id[BUS_ID_SIZE];
+
+ sprintf(bus_id, "%04x:%02x:%02x.%d", dn->phb->global_number,
+ dn->busno, PCI_SLOT(dn->devfn), PCI_FUNC(dn->devfn));
+ for_each_pci_dev(dev) {
+ if (!strcmp(pci_name(dev), bus_id)) {
+ break;
+ }
+ }
+ return dev;
+}
+
+EXPORT_SYMBOL_GPL(rpaphp_find_pci_dev);
+
+int rpaphp_claim_resource(struct pci_dev *dev, int resource)
+{
+ struct resource *res = &dev->resource[resource];
+ struct resource *root = pci_find_parent_resource(dev, res);
+ char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge";
+ int err = -EINVAL;
+
+ if (root != NULL) {
+ err = request_resource(root, res);
+ }
+
+ if (err) {
+ err("PCI: %s region %d of %s %s [%lx:%lx]\n",
+ root ? "Address space collision on" :
+ "No parent found for",
+ resource, dtype, pci_name(dev), res->start, res->end);
+ }
+ return err;
+}
+
+EXPORT_SYMBOL_GPL(rpaphp_claim_resource);
+
+static struct pci_dev *rpaphp_find_bridge_pdev(struct slot *slot)
+{
+ return rpaphp_find_pci_dev(slot->dn);
+}
+
+static int rpaphp_get_sensor_state(struct slot *slot, int *state)
+{
+ int rc;
+ int setlevel;
+
+ rc = rtas_get_sensor(DR_ENTITY_SENSE, slot->index, state);
+
+ if (rc < 0) {
+ if (rc == -EFAULT || rc == -EEXIST) {
+ dbg("%s: slot must be power up to get sensor-state\n",
+ __FUNCTION__);
+
+ /* some slots have to be powered up
+ * before get-sensor will succeed.
+ */
+ rc = rtas_set_power_level(slot->power_domain, POWER_ON,
+ &setlevel);
+ if (rc < 0) {
+ dbg("%s: power on slot[%s] failed rc=%d.\n",
+ __FUNCTION__, slot->name, rc);
+ } else {
+ rc = rtas_get_sensor(DR_ENTITY_SENSE,
+ slot->index, state);
+ }
+ } else if (rc == -ENODEV)
+ info("%s: slot is unusable\n", __FUNCTION__);
+ else
+ err("%s failed to get sensor state\n", __FUNCTION__);
+ }
+ return rc;
+}
+
+/**
+ * get_pci_adapter_status - get the status of a slot
+ *
+ * 0-- slot is empty
+ * 1-- adapter is configured
+ * 2-- adapter is not configured
+ * 3-- not valid
+ */
+int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value)
+{
+ int state, rc;
+ struct device_node *child_dn;
+ struct pci_dev *child_dev = NULL;
+
+ *value = NOT_VALID;
+ rc = rpaphp_get_sensor_state(slot, &state);
+ if (rc)
+ goto exit;
+
+ if ((state == EMPTY) || (slot->type == PHB)) {
+ dbg("slot is empty\n");
+ *value = EMPTY;
+ }
+ else if (state == PRESENT) {
+ if (!is_init) {
+ /* at run-time slot->state can be changed by */
+ /* config/unconfig adapter */
+ *value = slot->state;
+ } else {
+ child_dn = slot->dn->child;
+ if (child_dn)
+ child_dev = rpaphp_find_pci_dev(child_dn);
+
+ if (child_dev)
+ *value = CONFIGURED;
+ else if (!child_dn)
+ dbg("%s: %s is not valid OFDT node\n",
+ __FUNCTION__, slot->dn->full_name);
+ else {
+ err("%s: can't find pdev of adapter in slot[%s]\n",
+ __FUNCTION__, slot->dn->full_name);
+ *value = NOT_CONFIGURED;
+ }
+ }
+ }
+exit:
+ return rc;
+}
+
+/* Must be called before pci_bus_add_devices */
+static void
+rpaphp_fixup_new_pci_devices(struct pci_bus *bus, int fix_bus)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ /*
+ * Skip already-present devices (which are on the
+ * global device list.)
+ */
+ if (list_empty(&dev->global_list)) {
+ int i;
+
+ /* Need to setup IOMMU tables */
+ ppc_md.iommu_dev_setup(dev);
+
+ if(fix_bus)
+ pcibios_fixup_device_resources(dev, bus);
+ pci_read_irq_line(dev);
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ struct resource *r = &dev->resource[i];
+
+ if (r->parent || !r->start || !r->flags)
+ continue;
+ rpaphp_claim_resource(dev, i);
+ }
+ }
+ }
+}
+
+static int rpaphp_pci_config_bridge(struct pci_dev *dev);
+
+/*****************************************************************************
+ rpaphp_pci_config_slot() will configure all devices under the
+ given slot->dn and return the the first pci_dev.
+ *****************************************************************************/
+static struct pci_dev *
+rpaphp_pci_config_slot(struct device_node *dn, struct pci_bus *bus)
+{
+ struct device_node *eads_first_child = dn->child;
+ struct pci_dev *dev = NULL;
+ int num;
+
+ dbg("Enter %s: dn=%s bus=%s\n", __FUNCTION__, dn->full_name, bus->name);
+
+ if (eads_first_child) {
+ /* pci_scan_slot should find all children of EADs */
+ num = pci_scan_slot(bus, PCI_DEVFN(PCI_SLOT(eads_first_child->devfn), 0));
+ if (num) {
+ rpaphp_fixup_new_pci_devices(bus, 1);
+ pci_bus_add_devices(bus);
+ }
+ dev = rpaphp_find_pci_dev(eads_first_child);
+ if (!dev) {
+ err("No new device found\n");
+ return NULL;
+ }
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
+ rpaphp_pci_config_bridge(dev);
+ }
+ return dev;
+}
+
+static int rpaphp_pci_config_bridge(struct pci_dev *dev)
+{
+ u8 sec_busno;
+ struct pci_bus *child_bus;
+ struct pci_dev *child_dev;
+
+ dbg("Enter %s: BRIDGE dev=%s\n", __FUNCTION__, pci_name(dev));
+
+ /* get busno of downstream bus */
+ pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno);
+
+ /* add to children of PCI bridge dev->bus */
+ child_bus = pci_add_new_bus(dev->bus, dev, sec_busno);
+ if (!child_bus) {
+ err("%s: could not add second bus\n", __FUNCTION__);
+ return -EIO;
+ }
+ sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number);
+ /* do pci_scan_child_bus */
+ pci_scan_child_bus(child_bus);
+
+ list_for_each_entry(child_dev, &child_bus->devices, bus_list) {
+ eeh_add_device_late(child_dev);
+ }
+
+ /* fixup new pci devices without touching bus struct */
+ rpaphp_fixup_new_pci_devices(child_bus, 0);
+
+ /* Make the discovered devices available */
+ pci_bus_add_devices(child_bus);
+ return 0;
+}
+
+static void enable_eeh(struct device_node *dn)
+{
+ struct device_node *sib;
+
+ for (sib = dn->child; sib; sib = sib->sibling)
+ enable_eeh(sib);
+ eeh_add_device_early(dn);
+ return;
+
+}
+
+static void print_slot_pci_funcs(struct slot *slot)
+{
+ struct pci_dev *dev;
+
+ if (slot->dev_type == PCI_DEV) {
+ dbg("%s: pci_devs of slot[%s]\n", __FUNCTION__, slot->name);
+ list_for_each_entry (dev, slot->dev.pci_devs, bus_list)
+ dbg("\t%s\n", pci_name(dev));
+ }
+ return;
+}
+
+static int rpaphp_config_pci_adapter(struct slot *slot)
+{
+ struct pci_bus *pci_bus;
+ struct pci_dev *dev;
+ int rc = -ENODEV;
+
+ dbg("Entry %s: slot[%s]\n", __FUNCTION__, slot->name);
+
+ if (slot->bridge) {
+
+ pci_bus = slot->bridge->subordinate;
+ if (!pci_bus) {
+ err("%s: can't find bus structure\n", __FUNCTION__);
+ goto exit;
+ }
+ enable_eeh(slot->dn);
+ dev = rpaphp_pci_config_slot(slot->dn, pci_bus);
+ if (!dev) {
+ err("%s: can't find any devices.\n", __FUNCTION__);
+ goto exit;
+ }
+ print_slot_pci_funcs(slot);
+ rc = 0;
+ } else {
+ /* slot is not enabled */
+ err("slot doesn't have pci_dev structure\n");
+ }
+exit:
+ dbg("Exit %s: rc=%d\n", __FUNCTION__, rc);
+ return rc;
+}
+
+static void rpaphp_eeh_remove_bus_device(struct pci_dev *dev)
+{
+ eeh_remove_device(dev);
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ struct pci_bus *bus = dev->subordinate;
+ struct list_head *ln;
+ if (!bus)
+ return;
+ for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) {
+ struct pci_dev *pdev = pci_dev_b(ln);
+ if (pdev)
+ rpaphp_eeh_remove_bus_device(pdev);
+ }
+
+ }
+ return;
+}
+
+int rpaphp_unconfig_pci_adapter(struct slot *slot)
+{
+ struct pci_dev *dev;
+ int retval = 0;
+
+ list_for_each_entry(dev, slot->dev.pci_devs, bus_list)
+ rpaphp_eeh_remove_bus_device(dev);
+
+ pci_remove_behind_bridge(slot->bridge);
+ slot->state = NOT_CONFIGURED;
+ info("%s: devices in slot[%s] unconfigured.\n", __FUNCTION__,
+ slot->name);
+ return retval;
+}
+
+static int setup_pci_hotplug_slot_info(struct slot *slot)
+{
+ dbg("%s Initilize the PCI slot's hotplug->info structure ...\n",
+ __FUNCTION__);
+ rpaphp_get_power_status(slot, &slot->hotplug_slot->info->power_status);
+ rpaphp_get_pci_adapter_status(slot, 1,
+ &slot->hotplug_slot->info->
+ adapter_status);
+ if (slot->hotplug_slot->info->adapter_status == NOT_VALID) {
+ err("%s: NOT_VALID: skip dn->full_name=%s\n",
+ __FUNCTION__, slot->dn->full_name);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int set_phb_slot_name(struct slot *slot)
+{
+ struct device_node *dn;
+ struct pci_controller *phb;
+ struct pci_bus *bus;
+
+ dn = slot->dn;
+ if (!dn) {
+ return -EINVAL;
+ }
+ phb = dn->phb;
+ if (!phb) {
+ return -EINVAL;
+ }
+ bus = phb->bus;
+ if (!bus) {
+ return -EINVAL;
+ }
+
+ sprintf(slot->name, "%04x:%02x:%02x.%x", pci_domain_nr(bus),
+ bus->number, 0, 0);
+ return 0;
+}
+
+static int setup_pci_slot(struct slot *slot)
+{
+ struct pci_bus *bus;
+ int rc;
+
+ if (slot->type == PHB) {
+ rc = set_phb_slot_name(slot);
+ if (rc < 0) {
+ err("%s: failed to set phb slot name\n", __FUNCTION__);
+ goto exit_rc;
+ }
+ } else {
+ slot->bridge = rpaphp_find_bridge_pdev(slot);
+ if (!slot->bridge) {
+ /* slot being added doesn't have pci_dev yet */
+ err("%s: no pci_dev for bridge dn %s\n",
+ __FUNCTION__, slot->name);
+ goto exit_rc;
+ }
+
+ bus = slot->bridge->subordinate;
+ if (!bus)
+ goto exit_rc;
+ slot->dev.pci_devs = &bus->devices;
+
+ dbg("%s set slot->name to %s\n", __FUNCTION__,
+ pci_name(slot->bridge));
+ strcpy(slot->name, pci_name(slot->bridge));
+ }
+
+ /* find slot's pci_dev if it's not empty */
+ if (slot->hotplug_slot->info->adapter_status == EMPTY) {
+ slot->state = EMPTY; /* slot is empty */
+ } else {
+ /* slot is occupied */
+ if (!(slot->dn->child)) {
+ /* non-empty slot has to have child */
+ err("%s: slot[%s]'s device_node doesn't have child for adapter\n",
+ __FUNCTION__, slot->name);
+ goto exit_rc;
+ }
+
+ if (slot->hotplug_slot->info->adapter_status == NOT_CONFIGURED) {
+ dbg("%s CONFIGURING pci adapter in slot[%s]\n",
+ __FUNCTION__, slot->name);
+ if (rpaphp_config_pci_adapter(slot)) {
+ err("%s: CONFIG pci adapter failed\n", __FUNCTION__);
+ goto exit_rc;
+ }
+
+ } else if (slot->hotplug_slot->info->adapter_status != CONFIGURED) {
+ err("%s: slot[%s]'s adapter_status is NOT_VALID.\n",
+ __FUNCTION__, slot->name);
+ goto exit_rc;
+ }
+ print_slot_pci_funcs(slot);
+ if (!list_empty(slot->dev.pci_devs)) {
+ slot->state = CONFIGURED;
+ } else {
+ /* DLPAR add as opposed to
+ * boot time */
+ slot->state = NOT_CONFIGURED;
+ }
+ }
+ return 0;
+exit_rc:
+ dealloc_slot_struct(slot);
+ return -EINVAL;
+}
+
+int register_pci_slot(struct slot *slot)
+{
+ int rc = -EINVAL;
+
+ slot->dev_type = PCI_DEV;
+ if ((slot->type == EMBEDDED) || (slot->type == PHB))
+ slot->removable = 0;
+ else
+ slot->removable = 1;
+ if (setup_pci_hotplug_slot_info(slot))
+ goto exit_rc;
+ if (setup_pci_slot(slot))
+ goto exit_rc;
+ rc = register_slot(slot);
+exit_rc:
+ return rc;
+}
+
+int rpaphp_enable_pci_slot(struct slot *slot)
+{
+ int retval = 0, state;
+
+ retval = rpaphp_get_sensor_state(slot, &state);
+ if (retval)
+ goto exit;
+ dbg("%s: sensor state[%d]\n", __FUNCTION__, state);
+ /* if slot is not empty, enable the adapter */
+ if (state == PRESENT) {
+ dbg("%s : slot[%s] is occupied.\n", __FUNCTION__, slot->name);
+ retval = rpaphp_config_pci_adapter(slot);
+ if (!retval) {
+ slot->state = CONFIGURED;
+ dbg("%s: PCI devices in slot[%s] has been configured\n",
+ __FUNCTION__, slot->name);
+ } else {
+ slot->state = NOT_CONFIGURED;
+ dbg("%s: no pci_dev struct for adapter in slot[%s]\n",
+ __FUNCTION__, slot->name);
+ }
+ } else if (state == EMPTY) {
+ dbg("%s : slot[%s] is empty\n", __FUNCTION__, slot->name);
+ slot->state = EMPTY;
+ } else {
+ err("%s: slot[%s] is in invalid state\n", __FUNCTION__,
+ slot->name);
+ slot->state = NOT_VALID;
+ retval = -EINVAL;
+ }
+exit:
+ dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
+ return retval;
+}
+
+struct hotplug_slot *rpaphp_find_hotplug_slot(struct pci_dev *dev)
+{
+ struct list_head *tmp, *n;
+ struct slot *slot;
+
+ list_for_each_safe(tmp, n, &rpaphp_slot_head) {
+ struct pci_bus *bus;
+ struct list_head *ln;
+
+ slot = list_entry(tmp, struct slot, rpaphp_slot_list);
+ if (slot->bridge == NULL) {
+ if (slot->dev_type == PCI_DEV) {
+ printk(KERN_WARNING "PCI slot missing bridge %s %s \n",
+ slot->name, slot->location);
+ }
+ continue;
+ }
+
+ bus = slot->bridge->subordinate;
+ if (!bus) {
+ continue; /* should never happen? */
+ }
+ for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) {
+ struct pci_dev *pdev = pci_dev_b(ln);
+ if (pdev == dev)
+ return slot->hotplug_slot;
+ }
+ }
+
+ return NULL;
+}
+
+EXPORT_SYMBOL_GPL(rpaphp_find_hotplug_slot);
diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c
new file mode 100644
index 000000000000..ff2cbf0652d8
--- /dev/null
+++ b/drivers/pci/hotplug/rpaphp_slot.c
@@ -0,0 +1,267 @@
+/*
+ * RPA Virtual I/O device functions
+ * Copyright (C) 2004 Linda Xie <lxie@us.ibm.com>
+ *
+ * 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 <lxie@us.ibm.com>
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/pci.h>
+#include <asm/rtas.h>
+#include "rpaphp.h"
+
+static ssize_t removable_read_file (struct hotplug_slot *php_slot, char *buf)
+{
+ u8 value;
+ int retval = -ENOENT;
+ struct slot *slot = (struct slot *)php_slot->private;
+
+ if (!slot)
+ return retval;
+
+ value = slot->removable;
+ retval = sprintf (buf, "%d\n", value);
+ return retval;
+}
+
+static struct hotplug_slot_attribute hotplug_slot_attr_removable = {
+ .attr = {.name = "phy_removable", .mode = S_IFREG | S_IRUGO},
+ .show = removable_read_file,
+};
+
+static void rpaphp_sysfs_add_attr_removable (struct hotplug_slot *slot)
+{
+ sysfs_create_file(&slot->kobj, &hotplug_slot_attr_removable.attr);
+}
+
+static void rpaphp_sysfs_remove_attr_removable (struct hotplug_slot *slot)
+{
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_removable.attr);
+}
+
+static ssize_t location_read_file (struct hotplug_slot *php_slot, char *buf)
+{
+ char *value;
+ int retval = -ENOENT;
+ struct slot *slot = (struct slot *)php_slot->private;
+
+ if (!slot)
+ return retval;
+
+ value = slot->location;
+ retval = sprintf (buf, "%s\n", value);
+ return retval;
+}
+
+static struct hotplug_slot_attribute hotplug_slot_attr_location = {
+ .attr = {.name = "phy_location", .mode = S_IFREG | S_IRUGO},
+ .show = location_read_file,
+};
+
+static void rpaphp_sysfs_add_attr_location (struct hotplug_slot *slot)
+{
+ sysfs_create_file(&slot->kobj, &hotplug_slot_attr_location.attr);
+}
+
+static void rpaphp_sysfs_remove_attr_location (struct hotplug_slot *slot)
+{
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_location.attr);
+}
+
+/* free up the memory used by a slot */
+static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = (struct slot *) hotplug_slot->private;
+
+ dealloc_slot_struct(slot);
+}
+
+void dealloc_slot_struct(struct slot *slot)
+{
+ kfree(slot->hotplug_slot->info);
+ kfree(slot->hotplug_slot->name);
+ kfree(slot->hotplug_slot);
+ kfree(slot);
+ return;
+}
+
+struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name,
+ int power_domain)
+{
+ struct slot *slot;
+
+ slot = kmalloc(sizeof (struct slot), GFP_KERNEL);
+ if (!slot)
+ goto error_nomem;
+ memset(slot, 0, sizeof (struct slot));
+ slot->hotplug_slot = kmalloc(sizeof (struct hotplug_slot), GFP_KERNEL);
+ if (!slot->hotplug_slot)
+ goto error_slot;
+ memset(slot->hotplug_slot, 0, sizeof (struct hotplug_slot));
+ slot->hotplug_slot->info = kmalloc(sizeof (struct hotplug_slot_info),
+ GFP_KERNEL);
+ if (!slot->hotplug_slot->info)
+ goto error_hpslot;
+ memset(slot->hotplug_slot->info, 0, sizeof (struct hotplug_slot_info));
+ slot->hotplug_slot->name = kmalloc(BUS_ID_SIZE + 1, GFP_KERNEL);
+ if (!slot->hotplug_slot->name)
+ goto error_info;
+ slot->location = kmalloc(strlen(drc_name) + 1, GFP_KERNEL);
+ if (!slot->location)
+ goto error_name;
+ slot->name = slot->hotplug_slot->name;
+ slot->dn = dn;
+ slot->index = drc_index;
+ strcpy(slot->location, drc_name);
+ slot->power_domain = power_domain;
+ slot->hotplug_slot->private = slot;
+ slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
+ slot->hotplug_slot->release = &rpaphp_release_slot;
+
+ return (slot);
+
+error_name:
+ kfree(slot->hotplug_slot->name);
+error_info:
+ kfree(slot->hotplug_slot->info);
+error_hpslot:
+ kfree(slot->hotplug_slot);
+error_slot:
+ kfree(slot);
+error_nomem:
+ return NULL;
+}
+
+static int is_registered(struct slot *slot)
+{
+ struct slot *tmp_slot;
+
+ list_for_each_entry(tmp_slot, &rpaphp_slot_head, rpaphp_slot_list) {
+ if (!strcmp(tmp_slot->name, slot->name))
+ return 1;
+ }
+ return 0;
+}
+
+int deregister_slot(struct slot *slot)
+{
+ int retval = 0;
+ struct hotplug_slot *php_slot = slot->hotplug_slot;
+
+ dbg("%s - Entry: deregistering slot=%s\n",
+ __FUNCTION__, slot->name);
+
+ list_del(&slot->rpaphp_slot_list);
+
+ /* remove "phy_location" file */
+ rpaphp_sysfs_remove_attr_location(php_slot);
+
+ /* remove "phy_removable" file */
+ rpaphp_sysfs_remove_attr_removable(php_slot);
+
+ retval = pci_hp_deregister(php_slot);
+ if (retval)
+ err("Problem unregistering a slot %s\n", slot->name);
+ else
+ num_slots--;
+
+ dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
+ return retval;
+}
+
+int register_slot(struct slot *slot)
+{
+ int retval;
+
+ dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
+ __FUNCTION__, slot->dn->full_name, slot->index, slot->name,
+ slot->power_domain, slot->type);
+ /* should not try to register the same slot twice */
+ if (is_registered(slot)) { /* should't be here */
+ err("register_slot: slot[%s] is already registered\n", slot->name);
+ rpaphp_release_slot(slot->hotplug_slot);
+ return -EAGAIN;
+ }
+ retval = pci_hp_register(slot->hotplug_slot);
+ if (retval) {
+ err("pci_hp_register failed with error %d\n", retval);
+ rpaphp_release_slot(slot->hotplug_slot);
+ return retval;
+ }
+
+ /* create "phy_locatoin" file */
+ rpaphp_sysfs_add_attr_location(slot->hotplug_slot);
+
+ /* create "phy_removable" file */
+ rpaphp_sysfs_add_attr_removable(slot->hotplug_slot);
+
+ /* add slot to our internal list */
+ dbg("%s adding slot[%s] to rpaphp_slot_list\n",
+ __FUNCTION__, slot->name);
+
+ list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
+
+ if (slot->dev_type == VIO_DEV)
+ info("Slot [%s](VIO location=%s) registered\n",
+ slot->name, slot->location);
+ else
+ info("Slot [%s](PCI location=%s) registered\n",
+ slot->name, slot->location);
+ num_slots++;
+ return 0;
+}
+
+int rpaphp_get_power_status(struct slot *slot, u8 * value)
+{
+ int rc = 0, level;
+
+ if (slot->type == HOTPLUG) {
+ rc = rtas_get_power_level(slot->power_domain, &level);
+ if (!rc) {
+ dbg("%s the power level of slot %s(pwd-domain:0x%x) is %d\n",
+ __FUNCTION__, slot->name, slot->power_domain, level);
+ *value = level;
+ } else
+ err("failed to get power-level for slot(%s), rc=0x%x\n",
+ slot->location, rc);
+ } else {
+ dbg("%s report POWER_ON for EMBEDDED or PHB slot %s\n",
+ __FUNCTION__, slot->location);
+ *value = (u8) POWER_ON;
+ }
+
+ return rc;
+}
+
+int rpaphp_set_attention_status(struct slot *slot, u8 status)
+{
+ int rc;
+
+ /* status: LED_OFF or LED_ON */
+ rc = rtas_set_indicator(DR_INDICATOR, slot->index, status);
+ if (rc < 0)
+ err("slot(name=%s location=%s index=0x%x) set attention-status(%d) failed! rc=0x%x\n",
+ slot->name, slot->location, slot->index, status, rc);
+
+ return rc;
+}
diff --git a/drivers/pci/hotplug/rpaphp_vio.c b/drivers/pci/hotplug/rpaphp_vio.c
new file mode 100644
index 000000000000..74df6a305e64
--- /dev/null
+++ b/drivers/pci/hotplug/rpaphp_vio.c
@@ -0,0 +1,129 @@
+/*
+ * RPA Hot Plug Virtual I/O device functions
+ * Copyright (C) 2004 Linda Xie <lxie@us.ibm.com>
+ *
+ * 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 <lxie@us.ibm.com>
+ *
+ */
+#include <asm/vio.h>
+#include "rpaphp.h"
+
+/*
+ * get_vio_adapter_status - get the status of a slot
+ *
+ * status:
+ *
+ * 1-- adapter is configured
+ * 2-- adapter is not configured
+ * 3-- not valid
+ */
+inline int rpaphp_get_vio_adapter_status(struct slot *slot, int is_init, u8 *value)
+{
+ *value = slot->state;
+ return 0;
+}
+
+int rpaphp_unconfig_vio_adapter(struct slot *slot)
+{
+ int retval = 0;
+
+ dbg("Entry %s: slot[%s]\n", __FUNCTION__, slot->name);
+ if (!slot->dev.vio_dev) {
+ info("%s: no VIOA in slot[%s]\n", __FUNCTION__, slot->name);
+ retval = -EINVAL;
+ goto exit;
+ }
+ /* remove the device from the vio core */
+ vio_unregister_device(slot->dev.vio_dev);
+ slot->state = NOT_CONFIGURED;
+ info("%s: adapter in slot[%s] unconfigured.\n", __FUNCTION__, slot->name);
+exit:
+ dbg("Exit %s, rc=0x%x\n", __FUNCTION__, retval);
+ return retval;
+}
+
+static int setup_vio_hotplug_slot_info(struct slot *slot)
+{
+ slot->hotplug_slot->info->power_status = 1;
+ rpaphp_get_vio_adapter_status(slot, 1,
+ &slot->hotplug_slot->info->adapter_status);
+ return 0;
+}
+
+int register_vio_slot(struct device_node *dn)
+{
+ u32 *index;
+ char *name;
+ int rc = -EINVAL;
+ struct slot *slot = NULL;
+
+ rc = rpaphp_get_drc_props(dn, NULL, &name, NULL, NULL);
+ if (rc < 0)
+ goto exit_rc;
+ index = (u32 *) get_property(dn, "ibm,my-drc-index", NULL);
+ if (!index)
+ goto exit_rc;
+ if (!(slot = alloc_slot_struct(dn, *index, name, 0))) {
+ rc = -ENOMEM;
+ goto exit_rc;
+ }
+ slot->dev_type = VIO_DEV;
+ slot->dev.vio_dev = vio_find_node(dn);
+ if (slot->dev.vio_dev) {
+ /*
+ * rpaphp is the only owner of vio devices and
+ * does not need extra reference taken by
+ * vio_find_node
+ */
+ put_device(&slot->dev.vio_dev->dev);
+ } else
+ slot->dev.vio_dev = vio_register_device_node(dn);
+ if (slot->dev.vio_dev)
+ slot->state = CONFIGURED;
+ else
+ slot->state = NOT_CONFIGURED;
+ if (setup_vio_hotplug_slot_info(slot))
+ goto exit_rc;
+ strcpy(slot->name, slot->dev.vio_dev->dev.bus_id);
+ info("%s: registered VIO device[name=%s vio_dev=%p]\n",
+ __FUNCTION__, slot->name, slot->dev.vio_dev);
+ rc = register_slot(slot);
+exit_rc:
+ if (rc && slot)
+ dealloc_slot_struct(slot);
+ return (rc);
+}
+
+int rpaphp_enable_vio_slot(struct slot *slot)
+{
+ int retval = 0;
+
+ if ((slot->dev.vio_dev = vio_register_device_node(slot->dn))) {
+ info("%s: VIO adapter %s in slot[%s] has been configured\n",
+ __FUNCTION__, slot->dn->name, slot->name);
+ slot->state = CONFIGURED;
+ } else {
+ info("%s: no vio_dev struct for adapter in slot[%s]\n",
+ __FUNCTION__, slot->name);
+ slot->state = NOT_CONFIGURED;
+ }
+
+ return retval;
+}
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h
new file mode 100644
index 000000000000..67b6a3370ceb
--- /dev/null
+++ b/drivers/pci/hotplug/shpchp.h
@@ -0,0 +1,463 @@
+/*
+ * Standard Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>,<dely.l.sy@intel.com>
+ *
+ */
+#ifndef _SHPCHP_H
+#define _SHPCHP_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <asm/semaphore.h>
+#include <asm/io.h>
+#include "pci_hotplug.h"
+
+#if !defined(MODULE)
+ #define MY_NAME "shpchp"
+#else
+ #define MY_NAME THIS_MODULE->name
+#endif
+
+extern int shpchp_poll_mode;
+extern int shpchp_poll_time;
+extern int shpchp_debug;
+
+/*#define dbg(format, arg...) do { if (shpchp_debug) printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); } while (0)*/
+#define dbg(format, arg...) do { if (shpchp_debug) printk("%s: " format, 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)
+
+struct pci_func {
+ struct pci_func *next;
+ u8 bus;
+ u8 device;
+ u8 function;
+ u8 is_a_board;
+ u16 status;
+ u8 configured;
+ u8 switch_save;
+ u8 presence_save;
+ u8 pwr_save;
+ u32 base_length[0x06];
+ u8 base_type[0x06];
+ u16 reserved2;
+ u32 config_space[0x20];
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ struct pci_dev* pci_dev;
+};
+
+#define SLOT_MAGIC 0x67267321
+struct slot {
+ u32 magic;
+ struct slot *next;
+ u8 bus;
+ u8 device;
+ u32 number;
+ u8 is_a_board;
+ u8 configured;
+ u8 state;
+ u8 switch_save;
+ u8 presence_save;
+ u32 capabilities;
+ u16 reserved2;
+ struct timer_list task_event;
+ u8 hp_slot;
+ struct controller *ctrl;
+ struct hpc_ops *hpc_ops;
+ struct hotplug_slot *hotplug_slot;
+ struct list_head slot_list;
+};
+
+struct pci_resource {
+ struct pci_resource * next;
+ u32 base;
+ u32 length;
+};
+
+struct event_info {
+ u32 event_type;
+ u8 hp_slot;
+};
+
+struct controller {
+ struct controller *next;
+ struct semaphore crit_sect; /* critical section semaphore */
+ void * hpc_ctlr_handle; /* HPC controller handle */
+ int num_slots; /* Number of slots on ctlr */
+ int slot_num_inc; /* 1 or -1 */
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ struct pci_dev *pci_dev;
+ struct pci_bus *pci_bus;
+ struct event_info event_queue[10];
+ struct slot *slot;
+ struct hpc_ops *hpc_ops;
+ wait_queue_head_t queue; /* sleep & wake process */
+ u8 next_event;
+ u8 seg;
+ u8 bus;
+ u8 device;
+ u8 function;
+ u8 rev;
+ u8 slot_device_offset;
+ u8 add_support;
+ enum pci_bus_speed speed;
+ u32 first_slot; /* First physical slot number */
+ u8 slot_bus; /* Bus where the slots handled by this controller sit */
+ u8 push_flag;
+ u16 ctlrcap;
+ u16 vendor_id;
+};
+
+struct irq_mapping {
+ u8 barber_pole;
+ u8 valid_INT;
+ u8 interrupt[4];
+};
+
+struct resource_lists {
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ struct irq_mapping *irqs;
+};
+
+/* Define AMD SHPC ID */
+#define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450
+
+#define INT_BUTTON_IGNORE 0
+#define INT_PRESENCE_ON 1
+#define INT_PRESENCE_OFF 2
+#define INT_SWITCH_CLOSE 3
+#define INT_SWITCH_OPEN 4
+#define INT_POWER_FAULT 5
+#define INT_POWER_FAULT_CLEAR 6
+#define INT_BUTTON_PRESS 7
+#define INT_BUTTON_RELEASE 8
+#define INT_BUTTON_CANCEL 9
+
+#define STATIC_STATE 0
+#define BLINKINGON_STATE 1
+#define BLINKINGOFF_STATE 2
+#define POWERON_STATE 3
+#define POWEROFF_STATE 4
+
+#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400
+
+/* Error messages */
+#define INTERLOCK_OPEN 0x00000002
+#define ADD_NOT_SUPPORTED 0x00000003
+#define CARD_FUNCTIONING 0x00000005
+#define ADAPTER_NOT_SAME 0x00000006
+#define NO_ADAPTER_PRESENT 0x00000009
+#define NOT_ENOUGH_RESOURCES 0x0000000B
+#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C
+#define WRONG_BUS_FREQUENCY 0x0000000D
+#define POWER_FAILURE 0x0000000E
+
+#define REMOVE_NOT_SUPPORTED 0x00000003
+
+#define DISABLE_CARD 1
+
+/*
+ * error Messages
+ */
+#define msg_initialization_err "Initialization failure, error=%d\n"
+#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n"
+#define msg_HPC_non_shpc "The PCI hot plug controller is not supported by this driver.\n"
+#define msg_HPC_not_supported "This system is not supported by this version of shpcphd mdoule. Upgrade to a newer version of shpchpd\n"
+#define msg_unable_to_save "Unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n"
+#define msg_button_on "PCI slot #%d - powering on due to button press.\n"
+#define msg_button_off "PCI slot #%d - powering off due to button press.\n"
+#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n"
+#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n"
+
+/* sysfs functions for the hotplug controller info */
+extern void shpchp_create_ctrl_files (struct controller *ctrl);
+
+/* controller functions */
+extern int shpchprm_find_available_resources(struct controller *ctrl);
+extern int shpchp_event_start_thread(void);
+extern void shpchp_event_stop_thread(void);
+extern struct pci_func *shpchp_slot_create(unsigned char busnumber);
+extern struct pci_func *shpchp_slot_find(unsigned char bus, unsigned char device, unsigned char index);
+extern int shpchp_enable_slot(struct slot *slot);
+extern int shpchp_disable_slot(struct slot *slot);
+
+extern u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id);
+extern u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id);
+extern u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id);
+extern u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id);
+
+/* resource functions */
+extern int shpchp_resource_sort_and_combine(struct pci_resource **head);
+
+/* pci functions */
+extern int shpchp_set_irq(u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
+/*extern int shpchp_get_bus_dev(struct controller *ctrl, u8 *bus_num, u8 *dev_num, struct slot *slot);*/
+extern int shpchp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num);
+extern int shpchp_save_used_resources(struct controller *ctrl, struct pci_func * func, int flag);
+extern int shpchp_save_slot_config(struct controller *ctrl, struct pci_func * new_slot);
+extern void shpchp_destroy_board_resources(struct pci_func * func);
+extern int shpchp_return_board_resources(struct pci_func * func, struct resource_lists * resources);
+extern void shpchp_destroy_resource_list(struct resource_lists * resources);
+extern int shpchp_configure_device(struct controller* ctrl, struct pci_func* func);
+extern int shpchp_unconfigure_device(struct pci_func* func);
+
+
+/* Global variables */
+extern struct controller *shpchp_ctrl_list;
+extern struct pci_func *shpchp_slot_list[256];
+
+/* These are added to support AMD shpc */
+extern u8 shpchp_nic_irq;
+extern u8 shpchp_disk_irq;
+
+struct ctrl_reg {
+ volatile u32 base_offset;
+ volatile u32 slot_avail1;
+ volatile u32 slot_avail2;
+ volatile u32 slot_config;
+ volatile u16 sec_bus_config;
+ volatile u8 msi_ctrl;
+ volatile u8 prog_interface;
+ volatile u16 cmd;
+ volatile u16 cmd_status;
+ volatile u32 intr_loc;
+ volatile u32 serr_loc;
+ volatile u32 serr_intr_enable;
+ volatile u32 slot1;
+ volatile u32 slot2;
+ volatile u32 slot3;
+ volatile u32 slot4;
+ volatile u32 slot5;
+ volatile u32 slot6;
+ volatile u32 slot7;
+ volatile u32 slot8;
+ volatile u32 slot9;
+ volatile u32 slot10;
+ volatile u32 slot11;
+ volatile u32 slot12;
+} __attribute__ ((packed));
+
+/* offsets to the controller registers based on the above structure layout */
+enum ctrl_offsets {
+ BASE_OFFSET = offsetof(struct ctrl_reg, base_offset),
+ SLOT_AVAIL1 = offsetof(struct ctrl_reg, slot_avail1),
+ SLOT_AVAIL2 = offsetof(struct ctrl_reg, slot_avail2),
+ SLOT_CONFIG = offsetof(struct ctrl_reg, slot_config),
+ SEC_BUS_CONFIG = offsetof(struct ctrl_reg, sec_bus_config),
+ MSI_CTRL = offsetof(struct ctrl_reg, msi_ctrl),
+ PROG_INTERFACE = offsetof(struct ctrl_reg, prog_interface),
+ CMD = offsetof(struct ctrl_reg, cmd),
+ CMD_STATUS = offsetof(struct ctrl_reg, cmd_status),
+ INTR_LOC = offsetof(struct ctrl_reg, intr_loc),
+ SERR_LOC = offsetof(struct ctrl_reg, serr_loc),
+ SERR_INTR_ENABLE = offsetof(struct ctrl_reg, serr_intr_enable),
+ SLOT1 = offsetof(struct ctrl_reg, slot1),
+ SLOT2 = offsetof(struct ctrl_reg, slot2),
+ SLOT3 = offsetof(struct ctrl_reg, slot3),
+ SLOT4 = offsetof(struct ctrl_reg, slot4),
+ SLOT5 = offsetof(struct ctrl_reg, slot5),
+ SLOT6 = offsetof(struct ctrl_reg, slot6),
+ SLOT7 = offsetof(struct ctrl_reg, slot7),
+ SLOT8 = offsetof(struct ctrl_reg, slot8),
+ SLOT9 = offsetof(struct ctrl_reg, slot9),
+ SLOT10 = offsetof(struct ctrl_reg, slot10),
+ SLOT11 = offsetof(struct ctrl_reg, slot11),
+ SLOT12 = offsetof(struct ctrl_reg, slot12),
+};
+typedef u8(*php_intr_callback_t) (unsigned int change_id, void *instance_id);
+struct php_ctlr_state_s {
+ struct php_ctlr_state_s *pnext;
+ struct pci_dev *pci_dev;
+ unsigned int irq;
+ unsigned long flags; /* spinlock's */
+ u32 slot_device_offset;
+ u32 num_slots;
+ struct timer_list int_poll_timer; /* Added for poll event */
+ php_intr_callback_t attention_button_callback;
+ php_intr_callback_t switch_change_callback;
+ php_intr_callback_t presence_change_callback;
+ php_intr_callback_t power_fault_callback;
+ void *callback_instance_id;
+ void __iomem *creg; /* Ptr to controller register space */
+};
+/* Inline functions */
+
+
+/* Inline functions to check the sanity of a pointer that is passed to us */
+static inline int slot_paranoia_check (struct slot *slot, const char *function)
+{
+ if (!slot) {
+ dbg("%s - slot == NULL", function);
+ return -1;
+ }
+ if (slot->magic != SLOT_MAGIC) {
+ dbg("%s - bad magic number for slot", function);
+ return -1;
+ }
+ if (!slot->hotplug_slot) {
+ dbg("%s - slot->hotplug_slot == NULL!", function);
+ return -1;
+ }
+ return 0;
+}
+
+static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function)
+{
+ struct slot *slot;
+
+ if (!hotplug_slot) {
+ dbg("%s - hotplug_slot == NULL\n", function);
+ return NULL;
+ }
+
+ slot = (struct slot *)hotplug_slot->private;
+ if (slot_paranoia_check (slot, function))
+ return NULL;
+ return slot;
+}
+
+static inline struct slot *shpchp_find_slot (struct controller *ctrl, u8 device)
+{
+ struct slot *p_slot, *tmp_slot = NULL;
+
+ if (!ctrl)
+ return NULL;
+
+ p_slot = ctrl->slot;
+
+ dbg("p_slot = %p\n", p_slot);
+
+ while (p_slot && (p_slot->device != device)) {
+ tmp_slot = p_slot;
+ p_slot = p_slot->next;
+ dbg("In while loop, p_slot = %p\n", p_slot);
+ }
+ if (p_slot == NULL) {
+ err("ERROR: shpchp_find_slot device=0x%x\n", device);
+ p_slot = tmp_slot;
+ }
+
+ return (p_slot);
+}
+
+static inline int wait_for_ctrl_irq (struct controller *ctrl)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int retval = 0;
+
+ dbg("%s : start\n",__FUNCTION__);
+
+ add_wait_queue(&ctrl->queue, &wait);
+
+ if (!shpchp_poll_mode) {
+ /* Sleep for up to 1 second */
+ msleep_interruptible(1000);
+ } else {
+ /* Sleep for up to 2 seconds */
+ msleep_interruptible(2000);
+ }
+ remove_wait_queue(&ctrl->queue, &wait);
+ if (signal_pending(current))
+ retval = -EINTR;
+
+ dbg("%s : end\n", __FUNCTION__);
+ return retval;
+}
+
+/* Puts node back in the resource list pointed to by head */
+static inline void return_resource(struct pci_resource **head, struct pci_resource *node)
+{
+ if (!node || !head)
+ return;
+ node->next = *head;
+ *head = node;
+}
+
+#define SLOT_NAME_SIZE 10
+
+static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot)
+{
+ snprintf(buffer, buffer_size, "%d", slot->number);
+}
+
+enum php_ctlr_type {
+ PCI,
+ ISA,
+ ACPI
+};
+
+int shpc_init( struct controller *ctrl, struct pci_dev *pdev,
+ php_intr_callback_t attention_button_callback,
+ php_intr_callback_t switch_change_callback,
+ php_intr_callback_t presence_change_callback,
+ php_intr_callback_t power_fault_callback);
+
+int shpc_get_ctlr_slot_config( struct controller *ctrl,
+ int *num_ctlr_slots,
+ int *first_device_num,
+ int *physical_slot_num,
+ int *updown,
+ int *flags);
+
+struct hpc_ops {
+ int (*power_on_slot ) (struct slot *slot);
+ int (*slot_enable ) (struct slot *slot);
+ int (*slot_disable ) (struct slot *slot);
+ int (*enable_all_slots) (struct slot *slot);
+ int (*pwr_on_all_slots) (struct slot *slot);
+ int (*set_bus_speed_mode) (struct slot *slot, enum pci_bus_speed speed);
+ int (*get_power_status) (struct slot *slot, u8 *status);
+ int (*get_attention_status) (struct slot *slot, u8 *status);
+ int (*set_attention_status) (struct slot *slot, u8 status);
+ int (*get_latch_status) (struct slot *slot, u8 *status);
+ int (*get_adapter_status) (struct slot *slot, u8 *status);
+
+ int (*get_max_bus_speed) (struct slot *slot, enum pci_bus_speed *speed);
+ int (*get_cur_bus_speed) (struct slot *slot, enum pci_bus_speed *speed);
+ int (*get_adapter_speed) (struct slot *slot, enum pci_bus_speed *speed);
+ int (*get_mode1_ECC_cap) (struct slot *slot, u8 *mode);
+ int (*get_prog_int) (struct slot *slot, u8 *prog_int);
+
+ int (*query_power_fault) (struct slot *slot);
+ void (*green_led_on) (struct slot *slot);
+ void (*green_led_off) (struct slot *slot);
+ void (*green_led_blink) (struct slot *slot);
+ void (*release_ctlr) (struct controller *ctrl);
+ int (*check_cmd_status) (struct controller *ctrl);
+};
+
+#endif /* _SHPCHP_H */
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
new file mode 100644
index 000000000000..f0c53f850aed
--- /dev/null
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -0,0 +1,630 @@
+/*
+ * Standard Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include "shpchp.h"
+#include "shpchprm.h"
+
+/* Global variables */
+int shpchp_debug;
+int shpchp_poll_mode;
+int shpchp_poll_time;
+struct controller *shpchp_ctrl_list; /* = NULL */
+struct pci_func *shpchp_slot_list[256];
+
+#define DRIVER_VERSION "0.4"
+#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
+#define DRIVER_DESC "Standard Hot Plug PCI Controller Driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(shpchp_debug, bool, 0644);
+module_param(shpchp_poll_mode, bool, 0644);
+module_param(shpchp_poll_time, int, 0644);
+MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not");
+MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not");
+MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds");
+
+#define SHPC_MODULE_NAME "shpchp"
+
+static int shpc_start_thread (void);
+static int set_attention_status (struct hotplug_slot *slot, u8 value);
+static int enable_slot (struct hotplug_slot *slot);
+static int disable_slot (struct hotplug_slot *slot);
+static int get_power_status (struct hotplug_slot *slot, u8 *value);
+static int get_attention_status (struct hotplug_slot *slot, u8 *value);
+static int get_latch_status (struct hotplug_slot *slot, u8 *value);
+static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
+static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
+static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
+
+static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .set_attention_status = set_attention_status,
+ .enable_slot = enable_slot,
+ .disable_slot = disable_slot,
+ .get_power_status = get_power_status,
+ .get_attention_status = get_attention_status,
+ .get_latch_status = get_latch_status,
+ .get_adapter_status = get_adapter_status,
+ .get_max_bus_speed = get_max_bus_speed,
+ .get_cur_bus_speed = get_cur_bus_speed,
+};
+
+/**
+ * release_slot - free up the memory used by a slot
+ * @hotplug_slot: slot to free
+ */
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = (struct slot *)hotplug_slot->private;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ kfree(slot->hotplug_slot->info);
+ kfree(slot->hotplug_slot->name);
+ kfree(slot->hotplug_slot);
+ kfree(slot);
+}
+
+static int init_slots(struct controller *ctrl)
+{
+ struct slot *new_slot;
+ u8 number_of_slots;
+ u8 slot_device;
+ u32 slot_number, sun;
+ int result = -ENOMEM;
+
+ dbg("%s\n",__FUNCTION__);
+
+ number_of_slots = ctrl->num_slots;
+ slot_device = ctrl->slot_device_offset;
+ slot_number = ctrl->first_slot;
+
+ while (number_of_slots) {
+ new_slot = (struct slot *) kmalloc(sizeof(struct slot), GFP_KERNEL);
+ if (!new_slot)
+ goto error;
+
+ memset(new_slot, 0, sizeof(struct slot));
+ new_slot->hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL);
+ if (!new_slot->hotplug_slot)
+ goto error_slot;
+ memset(new_slot->hotplug_slot, 0, sizeof (struct hotplug_slot));
+
+ new_slot->hotplug_slot->info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
+ if (!new_slot->hotplug_slot->info)
+ goto error_hpslot;
+ memset(new_slot->hotplug_slot->info, 0, sizeof (struct hotplug_slot_info));
+ new_slot->hotplug_slot->name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL);
+ if (!new_slot->hotplug_slot->name)
+ goto error_info;
+
+ new_slot->magic = SLOT_MAGIC;
+ new_slot->ctrl = ctrl;
+ new_slot->bus = ctrl->slot_bus;
+ new_slot->device = slot_device;
+ new_slot->hpc_ops = ctrl->hpc_ops;
+
+ if (shpchprm_get_physical_slot_number(ctrl, &sun,
+ new_slot->bus, new_slot->device))
+ goto error_name;
+
+ new_slot->number = sun;
+ new_slot->hp_slot = slot_device - ctrl->slot_device_offset;
+
+ /* register this slot with the hotplug pci core */
+ new_slot->hotplug_slot->private = new_slot;
+ new_slot->hotplug_slot->release = &release_slot;
+ make_slot_name(new_slot->hotplug_slot->name, SLOT_NAME_SIZE, new_slot);
+ new_slot->hotplug_slot->ops = &shpchp_hotplug_slot_ops;
+
+ new_slot->hpc_ops->get_power_status(new_slot, &(new_slot->hotplug_slot->info->power_status));
+ new_slot->hpc_ops->get_attention_status(new_slot, &(new_slot->hotplug_slot->info->attention_status));
+ new_slot->hpc_ops->get_latch_status(new_slot, &(new_slot->hotplug_slot->info->latch_status));
+ new_slot->hpc_ops->get_adapter_status(new_slot, &(new_slot->hotplug_slot->info->adapter_status));
+
+ dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x slot_device_offset=%x\n", new_slot->bus,
+ new_slot->device, new_slot->hp_slot, new_slot->number, ctrl->slot_device_offset);
+ result = pci_hp_register (new_slot->hotplug_slot);
+ if (result) {
+ err ("pci_hp_register failed with error %d\n", result);
+ goto error_name;
+ }
+
+ new_slot->next = ctrl->slot;
+ ctrl->slot = new_slot;
+
+ number_of_slots--;
+ slot_device++;
+ slot_number += ctrl->slot_num_inc;
+ }
+
+ return 0;
+
+error_name:
+ kfree(new_slot->hotplug_slot->name);
+error_info:
+ kfree(new_slot->hotplug_slot->info);
+error_hpslot:
+ kfree(new_slot->hotplug_slot);
+error_slot:
+ kfree(new_slot);
+error:
+ return result;
+}
+
+static void cleanup_slots(struct controller *ctrl)
+{
+ struct slot *old_slot, *next_slot;
+
+ old_slot = ctrl->slot;
+ ctrl->slot = NULL;
+
+ while (old_slot) {
+ next_slot = old_slot->next;
+ pci_hp_deregister(old_slot->hotplug_slot);
+ old_slot = next_slot;
+ }
+}
+
+static int get_ctlr_slot_config(struct controller *ctrl)
+{
+ int num_ctlr_slots;
+ int first_device_num;
+ int physical_slot_num;
+ int updown;
+ int rc;
+ int flags;
+
+ rc = shpc_get_ctlr_slot_config(ctrl, &num_ctlr_slots, &first_device_num, &physical_slot_num, &updown, &flags);
+ if (rc) {
+ err("%s: get_ctlr_slot_config fail for b:d (%x:%x)\n", __FUNCTION__, ctrl->bus, ctrl->device);
+ return -1;
+ }
+
+ ctrl->num_slots = num_ctlr_slots;
+ ctrl->slot_device_offset = first_device_num;
+ ctrl->first_slot = physical_slot_num;
+ ctrl->slot_num_inc = updown; /* either -1 or 1 */
+
+ dbg("%s: num_slot(0x%x) 1st_dev(0x%x) psn(0x%x) updown(%d) for b:d (%x:%x)\n",
+ __FUNCTION__, num_ctlr_slots, first_device_num, physical_slot_num, updown, ctrl->bus, ctrl->device);
+
+ return 0;
+}
+
+
+/*
+ * set_attention_status - Turns the Amber LED for a slot on, off or blink
+ */
+static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ hotplug_slot->info->attention_status = status;
+ slot->hpc_ops->set_attention_status(slot, status);
+
+ return 0;
+}
+
+
+static int enable_slot (struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ return shpchp_enable_slot(slot);
+}
+
+
+static int disable_slot (struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ return shpchp_disable_slot(slot);
+}
+
+static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ int retval;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ retval = slot->hpc_ops->get_power_status(slot, value);
+ if (retval < 0)
+ *value = hotplug_slot->info->power_status;
+
+ return 0;
+}
+
+static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ int retval;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ retval = slot->hpc_ops->get_attention_status(slot, value);
+ if (retval < 0)
+ *value = hotplug_slot->info->attention_status;
+
+ return 0;
+}
+
+static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ int retval;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ retval = slot->hpc_ops->get_latch_status(slot, value);
+ if (retval < 0)
+ *value = hotplug_slot->info->latch_status;
+
+ return 0;
+}
+
+static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ int retval;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ retval = slot->hpc_ops->get_adapter_status(slot, value);
+ if (retval < 0)
+ *value = hotplug_slot->info->adapter_status;
+
+ return 0;
+}
+
+static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ int retval;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ retval = slot->hpc_ops->get_max_bus_speed(slot, value);
+ if (retval < 0)
+ *value = PCI_SPEED_UNKNOWN;
+
+ return 0;
+}
+
+static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ int retval;
+
+ dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+ retval = slot->hpc_ops->get_cur_bus_speed(slot, value);
+ if (retval < 0)
+ *value = PCI_SPEED_UNKNOWN;
+
+ return 0;
+}
+
+static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int rc;
+ struct controller *ctrl;
+ struct slot *t_slot;
+ int first_device_num; /* first PCI device number supported by this SHPC */
+ int num_ctlr_slots; /* number of slots supported by this SHPC */
+
+ ctrl = (struct controller *) kmalloc(sizeof(struct controller), GFP_KERNEL);
+ if (!ctrl) {
+ err("%s : out of memory\n", __FUNCTION__);
+ goto err_out_none;
+ }
+ memset(ctrl, 0, sizeof(struct controller));
+
+ dbg("DRV_thread pid = %d\n", current->pid);
+
+ rc = shpc_init(ctrl, pdev,
+ (php_intr_callback_t) shpchp_handle_attention_button,
+ (php_intr_callback_t) shpchp_handle_switch_change,
+ (php_intr_callback_t) shpchp_handle_presence_change,
+ (php_intr_callback_t) shpchp_handle_power_fault);
+ if (rc) {
+ dbg("%s: controller initialization failed\n", SHPC_MODULE_NAME);
+ goto err_out_free_ctrl;
+ }
+
+ dbg("%s: controller initialization success\n", __FUNCTION__);
+ ctrl->pci_dev = pdev; /* pci_dev of the P2P bridge */
+
+ pci_set_drvdata(pdev, ctrl);
+
+ ctrl->pci_bus = kmalloc (sizeof (*ctrl->pci_bus), GFP_KERNEL);
+ if (!ctrl->pci_bus) {
+ err("out of memory\n");
+ rc = -ENOMEM;
+ goto err_out_unmap_mmio_region;
+ }
+
+ memcpy (ctrl->pci_bus, pdev->bus, sizeof (*ctrl->pci_bus));
+ ctrl->bus = pdev->bus->number;
+ ctrl->slot_bus = pdev->subordinate->number;
+
+ ctrl->device = PCI_SLOT(pdev->devfn);
+ ctrl->function = PCI_FUNC(pdev->devfn);
+ dbg("ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", ctrl->bus, ctrl->device, ctrl->function, pdev->irq);
+
+ /*
+ * Save configuration headers for this and subordinate PCI buses
+ */
+
+ rc = get_ctlr_slot_config(ctrl);
+ if (rc) {
+ err(msg_initialization_err, rc);
+ goto err_out_free_ctrl_bus;
+ }
+ first_device_num = ctrl->slot_device_offset;
+ num_ctlr_slots = ctrl->num_slots;
+
+ /* Store PCI Config Space for all devices on this bus */
+ rc = shpchp_save_config(ctrl, ctrl->slot_bus, num_ctlr_slots, first_device_num);
+ if (rc) {
+ err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc);
+ goto err_out_free_ctrl_bus;
+ }
+
+ /* Get IO, memory, and IRQ resources for new devices */
+ rc = shpchprm_find_available_resources(ctrl);
+ ctrl->add_support = !rc;
+
+ if (rc) {
+ dbg("shpchprm_find_available_resources = %#x\n", rc);
+ err("unable to locate PCI configuration resources for hot plug add.\n");
+ goto err_out_free_ctrl_bus;
+ }
+
+ /* Setup the slot information structures */
+ rc = init_slots(ctrl);
+ if (rc) {
+ err(msg_initialization_err, 6);
+ goto err_out_free_ctrl_slot;
+ }
+
+ /* Now hpc_functions (slot->hpc_ops->functions) are ready */
+ t_slot = shpchp_find_slot(ctrl, first_device_num);
+
+ /* Check for operation bus speed */
+ rc = t_slot->hpc_ops->get_cur_bus_speed(t_slot, &ctrl->speed);
+ dbg("%s: t_slot->hp_slot %x\n", __FUNCTION__,t_slot->hp_slot);
+
+ if (rc || ctrl->speed == PCI_SPEED_UNKNOWN) {
+ err(SHPC_MODULE_NAME ": Can't get current bus speed. Set to 33MHz PCI.\n");
+ ctrl->speed = PCI_SPEED_33MHz;
+ }
+
+ /* Finish setting up the hot plug ctrl device */
+ ctrl->next_event = 0;
+
+ if (!shpchp_ctrl_list) {
+ shpchp_ctrl_list = ctrl;
+ ctrl->next = NULL;
+ } else {
+ ctrl->next = shpchp_ctrl_list;
+ shpchp_ctrl_list = ctrl;
+ }
+
+ shpchp_create_ctrl_files(ctrl);
+
+ return 0;
+
+err_out_free_ctrl_slot:
+ cleanup_slots(ctrl);
+err_out_free_ctrl_bus:
+ kfree(ctrl->pci_bus);
+err_out_unmap_mmio_region:
+ ctrl->hpc_ops->release_ctlr(ctrl);
+err_out_free_ctrl:
+ kfree(ctrl);
+err_out_none:
+ return -ENODEV;
+}
+
+
+static int shpc_start_thread(void)
+{
+ int loop;
+ int retval = 0;
+
+ dbg("Initialize + Start the notification/polling mechanism \n");
+
+ retval = shpchp_event_start_thread();
+ if (retval) {
+ dbg("shpchp_event_start_thread() failed\n");
+ return retval;
+ }
+
+ dbg("Initialize slot lists\n");
+ /* One slot list for each bus in the system */
+ for (loop = 0; loop < 256; loop++) {
+ shpchp_slot_list[loop] = NULL;
+ }
+
+ return retval;
+}
+
+static inline void __exit
+free_shpchp_res(struct pci_resource *res)
+{
+ struct pci_resource *tres;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+}
+
+static void __exit unload_shpchpd(void)
+{
+ struct pci_func *next;
+ struct pci_func *TempSlot;
+ int loop;
+ struct controller *ctrl;
+ struct controller *tctrl;
+
+ ctrl = shpchp_ctrl_list;
+
+ while (ctrl) {
+ cleanup_slots(ctrl);
+
+ free_shpchp_res(ctrl->io_head);
+ free_shpchp_res(ctrl->mem_head);
+ free_shpchp_res(ctrl->p_mem_head);
+ free_shpchp_res(ctrl->bus_head);
+
+ kfree (ctrl->pci_bus);
+
+ dbg("%s: calling release_ctlr\n", __FUNCTION__);
+ ctrl->hpc_ops->release_ctlr(ctrl);
+
+ tctrl = ctrl;
+ ctrl = ctrl->next;
+
+ kfree(tctrl);
+ }
+
+ for (loop = 0; loop < 256; loop++) {
+ next = shpchp_slot_list[loop];
+ while (next != NULL) {
+ free_shpchp_res(next->io_head);
+ free_shpchp_res(next->mem_head);
+ free_shpchp_res(next->p_mem_head);
+ free_shpchp_res(next->bus_head);
+
+ TempSlot = next;
+ next = next->next;
+ kfree(TempSlot);
+ }
+ }
+
+ /* Stop the notification mechanism */
+ shpchp_event_stop_thread();
+
+}
+
+
+static struct pci_device_id shpcd_pci_tbl[] = {
+ {
+ .class = ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00),
+ .class_mask = ~0,
+ .vendor = PCI_ANY_ID,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+
+ { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl);
+
+
+
+static struct pci_driver shpc_driver = {
+ .name = SHPC_MODULE_NAME,
+ .id_table = shpcd_pci_tbl,
+ .probe = shpc_probe,
+ /* remove: shpc_remove_one, */
+};
+
+
+
+static int __init shpcd_init(void)
+{
+ int retval = 0;
+
+#ifdef CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE
+ shpchp_poll_mode = 1;
+#endif
+
+ retval = shpc_start_thread();
+ if (retval)
+ goto error_hpc_init;
+
+ retval = shpchprm_init(PCI);
+ if (!retval) {
+ retval = pci_register_driver(&shpc_driver);
+ dbg("%s: pci_register_driver = %d\n", __FUNCTION__, retval);
+ info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+ }
+
+error_hpc_init:
+ if (retval) {
+ shpchprm_cleanup();
+ shpchp_event_stop_thread();
+ } else
+ shpchprm_print_pirt();
+
+ return retval;
+}
+
+static void __exit shpcd_cleanup(void)
+{
+ dbg("unload_shpchpd()\n");
+ unload_shpchpd();
+
+ shpchprm_cleanup();
+
+ dbg("pci_unregister_driver\n");
+ pci_unregister_driver(&shpc_driver);
+
+ info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
+}
+
+module_init(shpcd_init);
+module_exit(shpcd_cleanup);
diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c
new file mode 100644
index 000000000000..9f90eb8e6ecd
--- /dev/null
+++ b/drivers/pci/hotplug/shpchp_ctrl.c
@@ -0,0 +1,2848 @@
+/*
+ * Standard Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/smp_lock.h>
+#include <linux/pci.h>
+#include "shpchp.h"
+#include "shpchprm.h"
+
+static u32 configure_new_device(struct controller *ctrl, struct pci_func *func,
+ u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev);
+static int configure_new_function( struct controller *ctrl, struct pci_func *func,
+ u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev);
+static void interrupt_event_handler(struct controller *ctrl);
+
+static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */
+static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */
+static int event_finished;
+static unsigned long pushbutton_pending; /* = 0 */
+
+u8 shpchp_disk_irq;
+u8 shpchp_nic_irq;
+
+u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id)
+{
+ struct controller *ctrl = (struct controller *) inst_id;
+ struct slot *p_slot;
+ u8 rc = 0;
+ u8 getstatus;
+ struct pci_func *func;
+ struct event_info *taskInfo;
+
+ /* Attention Button Change */
+ dbg("shpchp: Attention button interrupt received.\n");
+
+ func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
+
+ /* This is the structure that tells the worker thread what to do */
+ taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+ p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
+ p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+
+ ctrl->next_event = (ctrl->next_event + 1) % 10;
+ taskInfo->hp_slot = hp_slot;
+
+ rc++;
+
+ /*
+ * Button pressed - See if need to TAKE ACTION!!!
+ */
+ info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ taskInfo->event_type = INT_BUTTON_PRESS;
+
+ if ((p_slot->state == BLINKINGON_STATE)
+ || (p_slot->state == BLINKINGOFF_STATE)) {
+ /* Cancel if we are still blinking; this means that we press the
+ * attention again before the 5 sec. limit expires to cancel hot-add
+ * or hot-remove
+ */
+ taskInfo->event_type = INT_BUTTON_CANCEL;
+ info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ } else if ((p_slot->state == POWERON_STATE)
+ || (p_slot->state == POWEROFF_STATE)) {
+ /* Ignore if the slot is on power-on or power-off state; this
+ * means that the previous attention button action to hot-add or
+ * hot-remove is undergoing
+ */
+ taskInfo->event_type = INT_BUTTON_IGNORE;
+ info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ }
+
+ if (rc)
+ up(&event_semaphore); /* signal event thread that new event is posted */
+
+ return 0;
+
+}
+
+u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id)
+{
+ struct controller *ctrl = (struct controller *) inst_id;
+ struct slot *p_slot;
+ u8 rc = 0;
+ u8 getstatus;
+ struct pci_func *func;
+ struct event_info *taskInfo;
+
+ /* Switch Change */
+ dbg("shpchp: Switch interrupt received.\n");
+
+ func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
+
+ /* This is the structure that tells the worker thread
+ * what to do
+ */
+ taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+ ctrl->next_event = (ctrl->next_event + 1) % 10;
+ taskInfo->hp_slot = hp_slot;
+
+ rc++;
+ p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+ p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
+ p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+ dbg("%s: Card present %x Power status %x\n", __FUNCTION__,
+ func->presence_save, func->pwr_save);
+
+ if (getstatus) {
+ /*
+ * Switch opened
+ */
+ info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ func->switch_save = 0;
+ taskInfo->event_type = INT_SWITCH_OPEN;
+ if (func->pwr_save && func->presence_save) {
+ taskInfo->event_type = INT_POWER_FAULT;
+ err("Surprise Removal of card\n");
+ }
+ } else {
+ /*
+ * Switch closed
+ */
+ info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ func->switch_save = 0x10;
+ taskInfo->event_type = INT_SWITCH_CLOSE;
+ }
+
+ if (rc)
+ up(&event_semaphore); /* signal event thread that new event is posted */
+
+ return rc;
+}
+
+u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id)
+{
+ struct controller *ctrl = (struct controller *) inst_id;
+ struct slot *p_slot;
+ u8 rc = 0;
+ /*u8 temp_byte;*/
+ struct pci_func *func;
+ struct event_info *taskInfo;
+
+ /* Presence Change */
+ dbg("shpchp: Presence/Notify input change.\n");
+
+ func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
+
+ /* This is the structure that tells the worker thread
+ * what to do
+ */
+ taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+ ctrl->next_event = (ctrl->next_event + 1) % 10;
+ taskInfo->hp_slot = hp_slot;
+
+ rc++;
+ p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ /*
+ * Save the presence state
+ */
+ p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
+ if (func->presence_save) {
+ /*
+ * Card Present
+ */
+ info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ taskInfo->event_type = INT_PRESENCE_ON;
+ } else {
+ /*
+ * Not Present
+ */
+ info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ taskInfo->event_type = INT_PRESENCE_OFF;
+ }
+
+ if (rc)
+ up(&event_semaphore); /* signal event thread that new event is posted */
+
+ return rc;
+}
+
+u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id)
+{
+ struct controller *ctrl = (struct controller *) inst_id;
+ struct slot *p_slot;
+ u8 rc = 0;
+ struct pci_func *func;
+ struct event_info *taskInfo;
+
+ /* Power fault */
+ dbg("shpchp: Power fault interrupt received.\n");
+
+ func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
+
+ /* This is the structure that tells the worker thread
+ * what to do
+ */
+ taskInfo = &(ctrl->event_queue[ctrl->next_event]);
+ ctrl->next_event = (ctrl->next_event + 1) % 10;
+ taskInfo->hp_slot = hp_slot;
+
+ rc++;
+ p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
+ /*
+ * Power fault Cleared
+ */
+ info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ func->status = 0x00;
+ taskInfo->event_type = INT_POWER_FAULT_CLEAR;
+ } else {
+ /*
+ * Power fault
+ */
+ info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot);
+ taskInfo->event_type = INT_POWER_FAULT;
+ /* set power fault status for this board */
+ func->status = 0xFF;
+ info("power fault bit %x set\n", hp_slot);
+ }
+ if (rc)
+ up(&event_semaphore); /* signal event thread that new event is posted */
+
+ return rc;
+}
+
+
+/*
+ * sort_by_size
+ *
+ * Sorts nodes on the list by their length.
+ * Smallest first.
+ *
+ */
+static int sort_by_size(struct pci_resource **head)
+{
+ struct pci_resource *current_res;
+ struct pci_resource *next_res;
+ int out_of_order = 1;
+
+ if (!(*head))
+ return(1);
+
+ if (!((*head)->next))
+ return(0);
+
+ while (out_of_order) {
+ out_of_order = 0;
+
+ /* Special case for swapping list head */
+ if (((*head)->next) &&
+ ((*head)->length > (*head)->next->length)) {
+ out_of_order++;
+ current_res = *head;
+ *head = (*head)->next;
+ current_res->next = (*head)->next;
+ (*head)->next = current_res;
+ }
+
+ current_res = *head;
+
+ while (current_res->next && current_res->next->next) {
+ if (current_res->next->length > current_res->next->next->length) {
+ out_of_order++;
+ next_res = current_res->next;
+ current_res->next = current_res->next->next;
+ current_res = current_res->next;
+ next_res->next = current_res->next;
+ current_res->next = next_res;
+ } else
+ current_res = current_res->next;
+ }
+ } /* End of out_of_order loop */
+
+ return(0);
+}
+
+
+/*
+ * sort_by_max_size
+ *
+ * Sorts nodes on the list by their length.
+ * Largest first.
+ *
+ */
+static int sort_by_max_size(struct pci_resource **head)
+{
+ struct pci_resource *current_res;
+ struct pci_resource *next_res;
+ int out_of_order = 1;
+
+ if (!(*head))
+ return(1);
+
+ if (!((*head)->next))
+ return(0);
+
+ while (out_of_order) {
+ out_of_order = 0;
+
+ /* Special case for swapping list head */
+ if (((*head)->next) &&
+ ((*head)->length < (*head)->next->length)) {
+ out_of_order++;
+ current_res = *head;
+ *head = (*head)->next;
+ current_res->next = (*head)->next;
+ (*head)->next = current_res;
+ }
+
+ current_res = *head;
+
+ while (current_res->next && current_res->next->next) {
+ if (current_res->next->length < current_res->next->next->length) {
+ out_of_order++;
+ next_res = current_res->next;
+ current_res->next = current_res->next->next;
+ current_res = current_res->next;
+ next_res->next = current_res->next;
+ current_res->next = next_res;
+ } else
+ current_res = current_res->next;
+ }
+ } /* End of out_of_order loop */
+
+ return(0);
+}
+
+
+/*
+ * do_pre_bridge_resource_split
+ *
+ * Returns zero or one node of resources that aren't in use
+ *
+ */
+static struct pci_resource *do_pre_bridge_resource_split (struct pci_resource **head, struct pci_resource **orig_head, u32 alignment)
+{
+ struct pci_resource *prevnode = NULL;
+ struct pci_resource *node;
+ struct pci_resource *split_node;
+ u32 rc;
+ u32 temp_dword;
+ dbg("do_pre_bridge_resource_split\n");
+
+ if (!(*head) || !(*orig_head))
+ return(NULL);
+
+ rc = shpchp_resource_sort_and_combine(head);
+
+ if (rc)
+ return(NULL);
+
+ if ((*head)->base != (*orig_head)->base)
+ return(NULL);
+
+ if ((*head)->length == (*orig_head)->length)
+ return(NULL);
+
+
+ /* If we got here, there the bridge requires some of the resource, but
+ * we may be able to split some off of the front
+ */
+ node = *head;
+
+ if (node->length & (alignment -1)) {
+ /* This one isn't an aligned length, so we'll make a new entry
+ * and split it up.
+ */
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+
+ temp_dword = (node->length | (alignment-1)) + 1 - alignment;
+
+ split_node->base = node->base;
+ split_node->length = temp_dword;
+
+ node->length -= temp_dword;
+ node->base += split_node->length;
+
+ /* Put it in the list */
+ *head = split_node;
+ split_node->next = node;
+ }
+
+ if (node->length < alignment) {
+ return(NULL);
+ }
+
+ /* Now unlink it */
+ if (*head == node) {
+ *head = node->next;
+ node->next = NULL;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ node->next = NULL;
+ }
+
+ return(node);
+}
+
+
+/*
+ * do_bridge_resource_split
+ *
+ * Returns zero or one node of resources that aren't in use
+ *
+ */
+static struct pci_resource *do_bridge_resource_split (struct pci_resource **head, u32 alignment)
+{
+ struct pci_resource *prevnode = NULL;
+ struct pci_resource *node;
+ u32 rc;
+ u32 temp_dword;
+
+ if (!(*head))
+ return(NULL);
+
+ rc = shpchp_resource_sort_and_combine(head);
+
+ if (rc)
+ return(NULL);
+
+ node = *head;
+
+ while (node->next) {
+ prevnode = node;
+ node = node->next;
+ kfree(prevnode);
+ }
+
+ if (node->length < alignment) {
+ kfree(node);
+ return(NULL);
+ }
+
+ if (node->base & (alignment - 1)) {
+ /* Short circuit if adjusted size is too small */
+ temp_dword = (node->base | (alignment-1)) + 1;
+ if ((node->length - (temp_dword - node->base)) < alignment) {
+ kfree(node);
+ return(NULL);
+ }
+
+ node->length -= (temp_dword - node->base);
+ node->base = temp_dword;
+ }
+
+ if (node->length & (alignment - 1)) {
+ /* There's stuff in use after this node */
+ kfree(node);
+ return(NULL);
+ }
+
+ return(node);
+}
+
+
+/*
+ * get_io_resource
+ *
+ * this function sorts the resource list by size and then
+ * returns the first node of "size" length that is not in the
+ * ISA aliasing window. If it finds a node larger than "size"
+ * it will split it up.
+ *
+ * size must be a power of two.
+ */
+static struct pci_resource *get_io_resource (struct pci_resource **head, u32 size)
+{
+ struct pci_resource *prevnode;
+ struct pci_resource *node;
+ struct pci_resource *split_node = NULL;
+ u32 temp_dword;
+
+ if (!(*head))
+ return(NULL);
+
+ if ( shpchp_resource_sort_and_combine(head) )
+ return(NULL);
+
+ if ( sort_by_size(head) )
+ return(NULL);
+
+ for (node = *head; node; node = node->next) {
+ if (node->length < size)
+ continue;
+
+ if (node->base & (size - 1)) {
+ /* This one isn't base aligned properly
+ so we'll make a new entry and split it up */
+ temp_dword = (node->base | (size-1)) + 1;
+
+ /*/ Short circuit if adjusted size is too small */
+ if ((node->length - (temp_dword - node->base)) < size)
+ continue;
+
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+
+ split_node->base = node->base;
+ split_node->length = temp_dword - node->base;
+ node->base = temp_dword;
+ node->length -= split_node->length;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of non-aligned base */
+
+ /* Don't need to check if too small since we already did */
+ if (node->length > size) {
+ /* This one is longer than we need
+ so we'll make a new entry and split it up */
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+
+ split_node->base = node->base + size;
+ split_node->length = node->length - size;
+ node->length = size;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of too big on top end */
+
+ /* For IO make sure it's not in the ISA aliasing space */
+ if (node->base & 0x300L)
+ continue;
+
+ /* If we got here, then it is the right size
+ Now take it out of the list */
+ if (*head == node) {
+ *head = node->next;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ }
+ node->next = NULL;
+ /* Stop looping */
+ break;
+ }
+
+ return(node);
+}
+
+
+/*
+ * get_max_resource
+ *
+ * Gets the largest node that is at least "size" big from the
+ * list pointed to by head. It aligns the node on top and bottom
+ * to "size" alignment before returning it.
+ * J.I. modified to put max size limits of; 64M->32M->16M->8M->4M->1M
+ * This is needed to avoid allocating entire ACPI _CRS res to one child bridge/slot.
+ */
+static struct pci_resource *get_max_resource (struct pci_resource **head, u32 size)
+{
+ struct pci_resource *max;
+ struct pci_resource *temp;
+ struct pci_resource *split_node;
+ u32 temp_dword;
+ u32 max_size[] = { 0x4000000, 0x2000000, 0x1000000, 0x0800000, 0x0400000, 0x0200000, 0x0100000, 0x00 };
+ int i;
+
+ if (!(*head))
+ return(NULL);
+
+ if (shpchp_resource_sort_and_combine(head))
+ return(NULL);
+
+ if (sort_by_max_size(head))
+ return(NULL);
+
+ for (max = *head;max; max = max->next) {
+
+ /* If not big enough we could probably just bail,
+ instead we'll continue to the next. */
+ if (max->length < size)
+ continue;
+
+ if (max->base & (size - 1)) {
+ /* This one isn't base aligned properly
+ so we'll make a new entry and split it up */
+ temp_dword = (max->base | (size-1)) + 1;
+
+ /* Short circuit if adjusted size is too small */
+ if ((max->length - (temp_dword - max->base)) < size)
+ continue;
+
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+
+ split_node->base = max->base;
+ split_node->length = temp_dword - max->base;
+ max->base = temp_dword;
+ max->length -= split_node->length;
+
+ /* Put it next in the list */
+ split_node->next = max->next;
+ max->next = split_node;
+ }
+
+ if ((max->base + max->length) & (size - 1)) {
+ /* This one isn't end aligned properly at the top
+ so we'll make a new entry and split it up */
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+ temp_dword = ((max->base + max->length) & ~(size - 1));
+ split_node->base = temp_dword;
+ split_node->length = max->length + max->base
+ - split_node->base;
+ max->length -= split_node->length;
+
+ /* Put it in the list */
+ split_node->next = max->next;
+ max->next = split_node;
+ }
+
+ /* Make sure it didn't shrink too much when we aligned it */
+ if (max->length < size)
+ continue;
+
+ for ( i = 0; max_size[i] > size; i++) {
+ if (max->length > max_size[i]) {
+ split_node = kmalloc(sizeof(*split_node),
+ GFP_KERNEL);
+ if (!split_node)
+ break; /* return (NULL); */
+ split_node->base = max->base + max_size[i];
+ split_node->length = max->length - max_size[i];
+ max->length = max_size[i];
+ /* Put it next in the list */
+ split_node->next = max->next;
+ max->next = split_node;
+ break;
+ }
+ }
+
+ /* Now take it out of the list */
+ temp = (struct pci_resource*) *head;
+ if (temp == max) {
+ *head = max->next;
+ } else {
+ while (temp && temp->next != max) {
+ temp = temp->next;
+ }
+
+ temp->next = max->next;
+ }
+
+ max->next = NULL;
+ return(max);
+ }
+
+ /* If we get here, we couldn't find one */
+ return(NULL);
+}
+
+
+/*
+ * get_resource
+ *
+ * this function sorts the resource list by size and then
+ * returns the first node of "size" length. If it finds a node
+ * larger than "size" it will split it up.
+ *
+ * size must be a power of two.
+ */
+static struct pci_resource *get_resource (struct pci_resource **head, u32 size)
+{
+ struct pci_resource *prevnode;
+ struct pci_resource *node;
+ struct pci_resource *split_node;
+ u32 temp_dword;
+
+ if (!(*head))
+ return(NULL);
+
+ if ( shpchp_resource_sort_and_combine(head) )
+ return(NULL);
+
+ if ( sort_by_size(head) )
+ return(NULL);
+
+ for (node = *head; node; node = node->next) {
+ dbg("%s: req_size =0x%x node=%p, base=0x%x, length=0x%x\n",
+ __FUNCTION__, size, node, node->base, node->length);
+ if (node->length < size)
+ continue;
+
+ if (node->base & (size - 1)) {
+ dbg("%s: not aligned\n", __FUNCTION__);
+ /* this one isn't base aligned properly
+ so we'll make a new entry and split it up */
+ temp_dword = (node->base | (size-1)) + 1;
+
+ /* Short circuit if adjusted size is too small */
+ if ((node->length - (temp_dword - node->base)) < size)
+ continue;
+
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+
+ split_node->base = node->base;
+ split_node->length = temp_dword - node->base;
+ node->base = temp_dword;
+ node->length -= split_node->length;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of non-aligned base */
+
+ /* Don't need to check if too small since we already did */
+ if (node->length > size) {
+ dbg("%s: too big\n", __FUNCTION__);
+ /* this one is longer than we need
+ so we'll make a new entry and split it up */
+ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
+
+ if (!split_node)
+ return(NULL);
+
+ split_node->base = node->base + size;
+ split_node->length = node->length - size;
+ node->length = size;
+
+ /* Put it in the list */
+ split_node->next = node->next;
+ node->next = split_node;
+ } /* End of too big on top end */
+
+ dbg("%s: got one!!!\n", __FUNCTION__);
+ /* If we got here, then it is the right size
+ Now take it out of the list */
+ if (*head == node) {
+ *head = node->next;
+ } else {
+ prevnode = *head;
+ while (prevnode->next != node)
+ prevnode = prevnode->next;
+
+ prevnode->next = node->next;
+ }
+ node->next = NULL;
+ /* Stop looping */
+ break;
+ }
+ return(node);
+}
+
+
+/*
+ * shpchp_resource_sort_and_combine
+ *
+ * Sorts all of the nodes in the list in ascending order by
+ * their base addresses. Also does garbage collection by
+ * combining adjacent nodes.
+ *
+ * returns 0 if success
+ */
+int shpchp_resource_sort_and_combine(struct pci_resource **head)
+{
+ struct pci_resource *node1;
+ struct pci_resource *node2;
+ int out_of_order = 1;
+
+ dbg("%s: head = %p, *head = %p\n", __FUNCTION__, head, *head);
+
+ if (!(*head))
+ return(1);
+
+ dbg("*head->next = %p\n",(*head)->next);
+
+ if (!(*head)->next)
+ return(0); /* only one item on the list, already sorted! */
+
+ dbg("*head->base = 0x%x\n",(*head)->base);
+ dbg("*head->next->base = 0x%x\n",(*head)->next->base);
+ while (out_of_order) {
+ out_of_order = 0;
+
+ /* Special case for swapping list head */
+ if (((*head)->next) &&
+ ((*head)->base > (*head)->next->base)) {
+ node1 = *head;
+ (*head) = (*head)->next;
+ node1->next = (*head)->next;
+ (*head)->next = node1;
+ out_of_order++;
+ }
+
+ node1 = (*head);
+
+ while (node1->next && node1->next->next) {
+ if (node1->next->base > node1->next->next->base) {
+ out_of_order++;
+ node2 = node1->next;
+ node1->next = node1->next->next;
+ node1 = node1->next;
+ node2->next = node1->next;
+ node1->next = node2;
+ } else
+ node1 = node1->next;
+ }
+ } /* End of out_of_order loop */
+
+ node1 = *head;
+
+ while (node1 && node1->next) {
+ if ((node1->base + node1->length) == node1->next->base) {
+ /* Combine */
+ dbg("8..\n");
+ node1->length += node1->next->length;
+ node2 = node1->next;
+ node1->next = node1->next->next;
+ kfree(node2);
+ } else
+ node1 = node1->next;
+ }
+
+ return(0);
+}
+
+
+/**
+ * shpchp_slot_create - Creates a node and adds it to the proper bus.
+ * @busnumber - bus where new node is to be located
+ *
+ * Returns pointer to the new node or NULL if unsuccessful
+ */
+struct pci_func *shpchp_slot_create(u8 busnumber)
+{
+ struct pci_func *new_slot;
+ struct pci_func *next;
+
+ new_slot = kmalloc(sizeof(*new_slot), GFP_KERNEL);
+
+ if (new_slot == NULL) {
+ return(new_slot);
+ }
+
+ memset(new_slot, 0, sizeof(struct pci_func));
+
+ new_slot->next = NULL;
+ new_slot->configured = 1;
+
+ if (shpchp_slot_list[busnumber] == NULL) {
+ shpchp_slot_list[busnumber] = new_slot;
+ } else {
+ next = shpchp_slot_list[busnumber];
+ while (next->next != NULL)
+ next = next->next;
+ next->next = new_slot;
+ }
+ return(new_slot);
+}
+
+
+/*
+ * slot_remove - Removes a node from the linked list of slots.
+ * @old_slot: slot to remove
+ *
+ * Returns 0 if successful, !0 otherwise.
+ */
+static int slot_remove(struct pci_func * old_slot)
+{
+ struct pci_func *next;
+
+ if (old_slot == NULL)
+ return(1);
+
+ next = shpchp_slot_list[old_slot->bus];
+
+ if (next == NULL) {
+ return(1);
+ }
+
+ if (next == old_slot) {
+ shpchp_slot_list[old_slot->bus] = old_slot->next;
+ shpchp_destroy_board_resources(old_slot);
+ kfree(old_slot);
+ return(0);
+ }
+
+ while ((next->next != old_slot) && (next->next != NULL)) {
+ next = next->next;
+ }
+
+ if (next->next == old_slot) {
+ next->next = old_slot->next;
+ shpchp_destroy_board_resources(old_slot);
+ kfree(old_slot);
+ return(0);
+ } else
+ return(2);
+}
+
+
+/**
+ * bridge_slot_remove - Removes a node from the linked list of slots.
+ * @bridge: bridge to remove
+ *
+ * Returns 0 if successful, !0 otherwise.
+ */
+static int bridge_slot_remove(struct pci_func *bridge)
+{
+ u8 subordinateBus, secondaryBus;
+ u8 tempBus;
+ struct pci_func *next;
+
+ if (bridge == NULL)
+ return(1);
+
+ secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF;
+ subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF;
+
+ for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) {
+ next = shpchp_slot_list[tempBus];
+
+ while (!slot_remove(next)) {
+ next = shpchp_slot_list[tempBus];
+ }
+ }
+
+ next = shpchp_slot_list[bridge->bus];
+
+ if (next == NULL) {
+ return(1);
+ }
+
+ if (next == bridge) {
+ shpchp_slot_list[bridge->bus] = bridge->next;
+ kfree(bridge);
+ return(0);
+ }
+
+ while ((next->next != bridge) && (next->next != NULL)) {
+ next = next->next;
+ }
+
+ if (next->next == bridge) {
+ next->next = bridge->next;
+ kfree(bridge);
+ return(0);
+ } else
+ return(2);
+}
+
+
+/**
+ * shpchp_slot_find - Looks for a node by bus, and device, multiple functions accessed
+ * @bus: bus to find
+ * @device: device to find
+ * @index: is 0 for first function found, 1 for the second...
+ *
+ * Returns pointer to the node if successful, %NULL otherwise.
+ */
+struct pci_func *shpchp_slot_find(u8 bus, u8 device, u8 index)
+{
+ int found = -1;
+ struct pci_func *func;
+
+ func = shpchp_slot_list[bus];
+
+ if ((func == NULL) || ((func->device == device) && (index == 0)))
+ return(func);
+
+ if (func->device == device)
+ found++;
+
+ while (func->next != NULL) {
+ func = func->next;
+
+ if (func->device == device)
+ found++;
+
+ if (found == index)
+ return(func);
+ }
+
+ return(NULL);
+}
+
+static int is_bridge(struct pci_func * func)
+{
+ /* Check the header type */
+ if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01)
+ return 1;
+ else
+ return 0;
+}
+
+
+/* The following routines constitute the bulk of the
+ hotplug controller logic
+ */
+static u32 change_bus_speed(struct controller *ctrl, struct slot *p_slot, enum pci_bus_speed speed)
+{
+ u32 rc = 0;
+
+ dbg("%s: change to speed %d\n", __FUNCTION__, speed);
+ down(&ctrl->crit_sect);
+ if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) {
+ err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
+ up(&ctrl->crit_sect);
+ return WRONG_BUS_FREQUENCY;
+ }
+ wait_for_ctrl_irq (ctrl);
+
+ if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) {
+ err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n",
+ __FUNCTION__);
+ err("%s: Error code (%d)\n", __FUNCTION__, rc);
+ up(&ctrl->crit_sect);
+ return WRONG_BUS_FREQUENCY;
+ }
+ up(&ctrl->crit_sect);
+ return rc;
+}
+
+static u32 fix_bus_speed(struct controller *ctrl, struct slot *pslot, u8 flag,
+enum pci_bus_speed asp, enum pci_bus_speed bsp, enum pci_bus_speed msp)
+{
+ u32 rc = 0;
+
+ if (flag != 0) { /* Other slots on the same bus are occupied */
+ if ( asp < bsp ) {
+ err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bsp, asp);
+ return WRONG_BUS_FREQUENCY;
+ }
+ } else {
+ /* Other slots on the same bus are empty */
+ if (msp == bsp) {
+ /* if adapter_speed >= bus_speed, do nothing */
+ if (asp < bsp) {
+ /*
+ * Try to lower bus speed to accommodate the adapter if other slots
+ * on the same controller are empty
+ */
+ if ((rc = change_bus_speed(ctrl, pslot, asp)))
+ return rc;
+ }
+ } else {
+ if (asp < msp) {
+ if ((rc = change_bus_speed(ctrl, pslot, asp)))
+ return rc;
+ } else {
+ if ((rc = change_bus_speed(ctrl, pslot, msp)))
+ return rc;
+ }
+ }
+ }
+ return rc;
+}
+
+/**
+ * board_added - Called after a board has been added to the system.
+ *
+ * Turns power on for the board
+ * Configures board
+ *
+ */
+static u32 board_added(struct pci_func * func, struct controller * ctrl)
+{
+ u8 hp_slot;
+ u8 slots_not_empty = 0;
+ int index;
+ u32 temp_register = 0xFFFFFFFF;
+ u32 retval, rc = 0;
+ struct pci_func *new_func = NULL;
+ struct slot *p_slot;
+ struct resource_lists res_lists;
+ enum pci_bus_speed adapter_speed, bus_speed, max_bus_speed;
+ u8 pi, mode;
+
+ p_slot = shpchp_find_slot(ctrl, func->device);
+ hp_slot = func->device - ctrl->slot_device_offset;
+
+ dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot);
+
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ /* Power on slot without connecting to bus */
+ rc = p_slot->hpc_ops->power_on_slot(p_slot);
+ if (rc) {
+ err("%s: Failed to power on slot\n", __FUNCTION__);
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ return -1;
+ }
+
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ rc = p_slot->hpc_ops->check_cmd_status(ctrl);
+ if (rc) {
+ err("%s: Failed to power on slot, error code(%d)\n", __FUNCTION__, rc);
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ return -1;
+ }
+
+
+ if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) {
+ if (slots_not_empty)
+ return WRONG_BUS_FREQUENCY;
+
+ if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) {
+ err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
+ up(&ctrl->crit_sect);
+ return WRONG_BUS_FREQUENCY;
+ }
+ wait_for_ctrl_irq (ctrl);
+
+ if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) {
+ err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n",
+ __FUNCTION__);
+ err("%s: Error code (%d)\n", __FUNCTION__, rc);
+ up(&ctrl->crit_sect);
+ return WRONG_BUS_FREQUENCY;
+ }
+ /* turn on board, blink green LED, turn off Amber LED */
+ if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
+ err("%s: Issue of Slot Enable command failed\n", __FUNCTION__);
+ up(&ctrl->crit_sect);
+ return rc;
+ }
+ wait_for_ctrl_irq (ctrl);
+
+ if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) {
+ err("%s: Failed to enable slot, error code(%d)\n", __FUNCTION__, rc);
+ up(&ctrl->crit_sect);
+ return rc;
+ }
+ }
+
+ rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &adapter_speed);
+ /* 0 = PCI 33Mhz, 1 = PCI 66 Mhz, 2 = PCI-X 66 PA, 4 = PCI-X 66 ECC, */
+ /* 5 = PCI-X 133 PA, 7 = PCI-X 133 ECC, 0xa = PCI-X 133 Mhz 266, */
+ /* 0xd = PCI-X 133 Mhz 533 */
+ /* This encoding is different from the one used in cur_bus_speed & */
+ /* max_bus_speed */
+
+ if (rc || adapter_speed == PCI_SPEED_UNKNOWN) {
+ err("%s: Can't get adapter speed or bus mode mismatch\n", __FUNCTION__);
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ return WRONG_BUS_FREQUENCY;
+ }
+
+ rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bus_speed);
+ if (rc || bus_speed == PCI_SPEED_UNKNOWN) {
+ err("%s: Can't get bus operation speed\n", __FUNCTION__);
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ return WRONG_BUS_FREQUENCY;
+ }
+
+ rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &max_bus_speed);
+ if (rc || max_bus_speed == PCI_SPEED_UNKNOWN) {
+ err("%s: Can't get max bus operation speed\n", __FUNCTION__);
+ max_bus_speed = bus_speed;
+ }
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+
+ if ((rc = p_slot->hpc_ops->get_prog_int(p_slot, &pi))) {
+ err("%s: Can't get controller programming interface, set it to 1\n", __FUNCTION__);
+ pi = 1;
+ }
+
+ /* Check if there are other slots or devices on the same bus */
+ if (!list_empty(&ctrl->pci_dev->subordinate->devices))
+ slots_not_empty = 1;
+
+ dbg("%s: slots_not_empty %d, pi %d\n", __FUNCTION__,
+ slots_not_empty, pi);
+ dbg("adapter_speed %d, bus_speed %d, max_bus_speed %d\n",
+ adapter_speed, bus_speed, max_bus_speed);
+
+ if (pi == 2) {
+ dbg("%s: In PI = %d\n", __FUNCTION__, pi);
+ if ((rc = p_slot->hpc_ops->get_mode1_ECC_cap(p_slot, &mode))) {
+ err("%s: Can't get Mode1_ECC, set mode to 0\n", __FUNCTION__);
+ mode = 0;
+ }
+
+ switch (adapter_speed) {
+ case PCI_SPEED_133MHz_PCIX_533:
+ case PCI_SPEED_133MHz_PCIX_266:
+ if ((bus_speed != adapter_speed) &&
+ ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
+ return rc;
+ break;
+ case PCI_SPEED_133MHz_PCIX_ECC:
+ case PCI_SPEED_133MHz_PCIX:
+ if (mode) { /* Bus - Mode 1 ECC */
+ if ((bus_speed != 0x7) &&
+ ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
+ return rc;
+ } else {
+ if ((bus_speed != 0x4) &&
+ ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
+ return rc;
+ }
+ break;
+ case PCI_SPEED_66MHz_PCIX_ECC:
+ case PCI_SPEED_66MHz_PCIX:
+ if (mode) { /* Bus - Mode 1 ECC */
+ if ((bus_speed != 0x5) &&
+ ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
+ return rc;
+ } else {
+ if ((bus_speed != 0x2) &&
+ ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
+ return rc;
+ }
+ break;
+ case PCI_SPEED_66MHz:
+ if ((bus_speed != 0x1) &&
+ ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
+ return rc;
+ break;
+ case PCI_SPEED_33MHz:
+ if (bus_speed > 0x0) {
+ if (slots_not_empty == 0) {
+ if ((rc = change_bus_speed(ctrl, p_slot, adapter_speed)))
+ return rc;
+ } else {
+ err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
+ return WRONG_BUS_FREQUENCY;
+ }
+ }
+ break;
+ default:
+ err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
+ return WRONG_BUS_FREQUENCY;
+ }
+ } else {
+ /* If adpater_speed == bus_speed, nothing to do here */
+ dbg("%s: In PI = %d\n", __FUNCTION__, pi);
+ if ((adapter_speed != bus_speed) &&
+ ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
+ return rc;
+ }
+
+ down(&ctrl->crit_sect);
+ /* turn on board, blink green LED, turn off Amber LED */
+ if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
+ err("%s: Issue of Slot Enable command failed\n", __FUNCTION__);
+ up(&ctrl->crit_sect);
+ return rc;
+ }
+ wait_for_ctrl_irq (ctrl);
+
+ if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) {
+ err("%s: Failed to enable slot, error code(%d)\n", __FUNCTION__, rc);
+ up(&ctrl->crit_sect);
+ return rc;
+ }
+
+ up(&ctrl->crit_sect);
+
+ /* Wait for ~1 second */
+ dbg("%s: before long_delay\n", __FUNCTION__);
+ wait_for_ctrl_irq (ctrl);
+ dbg("%s: after long_delay\n", __FUNCTION__);
+
+ dbg("%s: func status = %x\n", __FUNCTION__, func->status);
+ /* Check for a power fault */
+ if (func->status == 0xFF) {
+ /* power fault occurred, but it was benign */
+ temp_register = 0xFFFFFFFF;
+ dbg("%s: temp register set to %x by power fault\n", __FUNCTION__, temp_register);
+ rc = POWER_FAILURE;
+ func->status = 0;
+ } else {
+ /* Get vendor/device ID u32 */
+ rc = pci_bus_read_config_dword (ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function),
+ PCI_VENDOR_ID, &temp_register);
+ dbg("%s: pci_bus_read_config_dword returns %d\n", __FUNCTION__, rc);
+ dbg("%s: temp_register is %x\n", __FUNCTION__, temp_register);
+
+ if (rc != 0) {
+ /* Something's wrong here */
+ temp_register = 0xFFFFFFFF;
+ dbg("%s: temp register set to %x by error\n", __FUNCTION__, temp_register);
+ }
+ /* Preset return code. It will be changed later if things go okay. */
+ rc = NO_ADAPTER_PRESENT;
+ }
+
+ /* All F's is an empty slot or an invalid board */
+ if (temp_register != 0xFFFFFFFF) { /* Check for a board in the slot */
+ res_lists.io_head = ctrl->io_head;
+ res_lists.mem_head = ctrl->mem_head;
+ res_lists.p_mem_head = ctrl->p_mem_head;
+ res_lists.bus_head = ctrl->bus_head;
+ res_lists.irqs = NULL;
+
+ rc = configure_new_device(ctrl, func, 0, &res_lists, 0, 0);
+ dbg("%s: back from configure_new_device\n", __FUNCTION__);
+
+ ctrl->io_head = res_lists.io_head;
+ ctrl->mem_head = res_lists.mem_head;
+ ctrl->p_mem_head = res_lists.p_mem_head;
+ ctrl->bus_head = res_lists.bus_head;
+
+ shpchp_resource_sort_and_combine(&(ctrl->mem_head));
+ shpchp_resource_sort_and_combine(&(ctrl->p_mem_head));
+ shpchp_resource_sort_and_combine(&(ctrl->io_head));
+ shpchp_resource_sort_and_combine(&(ctrl->bus_head));
+
+ if (rc) {
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ /* turn off slot, turn on Amber LED, turn off Green LED */
+ retval = p_slot->hpc_ops->slot_disable(p_slot);
+ if (retval) {
+ err("%s: Issue of Slot Enable command failed\n", __FUNCTION__);
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ return retval;
+ }
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ retval = p_slot->hpc_ops->check_cmd_status(ctrl);
+ if (retval) {
+ err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, retval);
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ return retval;
+ }
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+
+ return(rc);
+ }
+ shpchp_save_slot_config(ctrl, func);
+
+ func->status = 0;
+ func->switch_save = 0x10;
+ func->is_a_board = 0x01;
+ func->pwr_save = 1;
+
+ /* Next, we will instantiate the linux pci_dev structures
+ * (with appropriate driver notification, if already present)
+ */
+ index = 0;
+ do {
+ new_func = shpchp_slot_find(ctrl->slot_bus, func->device, index++);
+ if (new_func && !new_func->pci_dev) {
+ dbg("%s:call pci_hp_configure_dev\n", __FUNCTION__);
+ shpchp_configure_device(ctrl, new_func);
+ }
+ } while (new_func);
+
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ p_slot->hpc_ops->green_led_on(p_slot);
+
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+
+ } else {
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ /* turn off slot, turn on Amber LED, turn off Green LED */
+ rc = p_slot->hpc_ops->slot_disable(p_slot);
+ if (rc) {
+ err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ return rc;
+ }
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ rc = p_slot->hpc_ops->check_cmd_status(ctrl);
+ if (rc) {
+ err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc);
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ return rc;
+ }
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+
+ return(rc);
+ }
+ return 0;
+}
+
+
+/**
+ * remove_board - Turns off slot and LED's
+ *
+ */
+static u32 remove_board(struct pci_func *func, struct controller *ctrl)
+{
+ int index;
+ u8 skip = 0;
+ u8 device;
+ u8 hp_slot;
+ u32 rc;
+ struct resource_lists res_lists;
+ struct pci_func *temp_func;
+ struct slot *p_slot;
+
+ if (func == NULL)
+ return(1);
+
+ if (shpchp_unconfigure_device(func))
+ return(1);
+
+ device = func->device;
+
+ hp_slot = func->device - ctrl->slot_device_offset;
+ p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
+
+ if ((ctrl->add_support) &&
+ !(func->bus_head || func->mem_head || func->p_mem_head || func->io_head)) {
+ /* Here we check to see if we've saved any of the board's
+ * resources already. If so, we'll skip the attempt to
+ * determine what's being used.
+ */
+ index = 0;
+
+ temp_func = func;
+
+ while ((temp_func = shpchp_slot_find(temp_func->bus, temp_func->device, index++))) {
+ if (temp_func->bus_head || temp_func->mem_head
+ || temp_func->p_mem_head || temp_func->io_head) {
+ skip = 1;
+ break;
+ }
+ }
+
+ if (!skip)
+ rc = shpchp_save_used_resources(ctrl, func, DISABLE_CARD);
+ }
+ /* Change status to shutdown */
+ if (func->is_a_board)
+ func->status = 0x01;
+ func->configured = 0;
+
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ /* turn off slot, turn on Amber LED, turn off Green LED */
+ rc = p_slot->hpc_ops->slot_disable(p_slot);
+ if (rc) {
+ err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ return rc;
+ }
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ rc = p_slot->hpc_ops->check_cmd_status(ctrl);
+ if (rc) {
+ err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc);
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ return rc;
+ }
+
+ rc = p_slot->hpc_ops->set_attention_status(p_slot, 0);
+ if (rc) {
+ err("%s: Issue of Set Attention command failed\n", __FUNCTION__);
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ return rc;
+ }
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+
+ if (ctrl->add_support) {
+ while (func) {
+ res_lists.io_head = ctrl->io_head;
+ res_lists.mem_head = ctrl->mem_head;
+ res_lists.p_mem_head = ctrl->p_mem_head;
+ res_lists.bus_head = ctrl->bus_head;
+
+ dbg("Returning resources to ctlr lists for (B/D/F) = (%#x/%#x/%#x)\n", func->bus,
+ func->device, func->function);
+
+ shpchp_return_board_resources(func, &res_lists);
+
+ ctrl->io_head = res_lists.io_head;
+ ctrl->mem_head = res_lists.mem_head;
+ ctrl->p_mem_head = res_lists.p_mem_head;
+ ctrl->bus_head = res_lists.bus_head;
+
+ shpchp_resource_sort_and_combine(&(ctrl->mem_head));
+ shpchp_resource_sort_and_combine(&(ctrl->p_mem_head));
+ shpchp_resource_sort_and_combine(&(ctrl->io_head));
+ shpchp_resource_sort_and_combine(&(ctrl->bus_head));
+
+ if (is_bridge(func)) {
+ dbg("PCI Bridge Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus,
+ func->device, func->function);
+ bridge_slot_remove(func);
+ } else
+ dbg("PCI Function Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus,
+ func->device, func->function);
+ slot_remove(func);
+
+ func = shpchp_slot_find(ctrl->slot_bus, device, 0);
+ }
+
+ /* Setup slot structure with entry for empty slot */
+ func = shpchp_slot_create(ctrl->slot_bus);
+
+ if (func == NULL) {
+ return(1);
+ }
+
+ func->bus = ctrl->slot_bus;
+ func->device = device;
+ func->function = 0;
+ func->configured = 0;
+ func->switch_save = 0x10;
+ func->pwr_save = 0;
+ func->is_a_board = 0;
+ }
+
+ return 0;
+}
+
+
+static void pushbutton_helper_thread (unsigned long data)
+{
+ pushbutton_pending = data;
+
+ up(&event_semaphore);
+}
+
+
+/**
+ * shpchp_pushbutton_thread
+ *
+ * Scheduled procedure to handle blocking stuff for the pushbuttons
+ * Handles all pending events and exits.
+ *
+ */
+static void shpchp_pushbutton_thread (unsigned long slot)
+{
+ struct slot *p_slot = (struct slot *) slot;
+ u8 getstatus;
+
+ pushbutton_pending = 0;
+
+ if (!p_slot) {
+ dbg("%s: Error! slot NULL\n", __FUNCTION__);
+ return;
+ }
+
+ p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
+ if (getstatus) {
+ p_slot->state = POWEROFF_STATE;
+ dbg("In power_down_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device);
+
+ shpchp_disable_slot(p_slot);
+ p_slot->state = STATIC_STATE;
+ } else {
+ p_slot->state = POWERON_STATE;
+ dbg("In add_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device);
+
+ if (shpchp_enable_slot(p_slot)) {
+ /* Wait for exclusive access to hardware */
+ down(&p_slot->ctrl->crit_sect);
+
+ p_slot->hpc_ops->green_led_off(p_slot);
+
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (p_slot->ctrl);
+
+ /* Done with exclusive hardware access */
+ up(&p_slot->ctrl->crit_sect);
+ }
+ p_slot->state = STATIC_STATE;
+ }
+
+ return;
+}
+
+
+/* this is the main worker thread */
+static int event_thread(void* data)
+{
+ struct controller *ctrl;
+ lock_kernel();
+ daemonize("shpchpd_event");
+ unlock_kernel();
+
+ while (1) {
+ dbg("!!!!event_thread sleeping\n");
+ down_interruptible (&event_semaphore);
+ dbg("event_thread woken finished = %d\n", event_finished);
+ if (event_finished || signal_pending(current))
+ break;
+ /* Do stuff here */
+ if (pushbutton_pending)
+ shpchp_pushbutton_thread(pushbutton_pending);
+ else
+ for (ctrl = shpchp_ctrl_list; ctrl; ctrl=ctrl->next)
+ interrupt_event_handler(ctrl);
+ }
+ dbg("event_thread signals exit\n");
+ up(&event_exit);
+ return 0;
+}
+
+int shpchp_event_start_thread (void)
+{
+ int pid;
+
+ /* initialize our semaphores */
+ init_MUTEX_LOCKED(&event_exit);
+ event_finished=0;
+
+ init_MUTEX_LOCKED(&event_semaphore);
+ pid = kernel_thread(event_thread, NULL, 0);
+
+ if (pid < 0) {
+ err ("Can't start up our event thread\n");
+ return -1;
+ }
+ dbg("Our event thread pid = %d\n", pid);
+ return 0;
+}
+
+
+void shpchp_event_stop_thread (void)
+{
+ event_finished = 1;
+ dbg("event_thread finish command given\n");
+ up(&event_semaphore);
+ dbg("wait for event_thread to exit\n");
+ down(&event_exit);
+}
+
+
+static int update_slot_info (struct slot *slot)
+{
+ struct hotplug_slot_info *info;
+ int result;
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ slot->hpc_ops->get_power_status(slot, &(info->power_status));
+ slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
+ slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
+ slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
+
+ result = pci_hp_change_slot_info(slot->hotplug_slot, info);
+ kfree (info);
+ return result;
+}
+
+static void interrupt_event_handler(struct controller *ctrl)
+{
+ int loop = 0;
+ int change = 1;
+ struct pci_func *func;
+ u8 hp_slot;
+ u8 getstatus;
+ struct slot *p_slot;
+
+ dbg("%s:\n", __FUNCTION__);
+ while (change) {
+ change = 0;
+
+ for (loop = 0; loop < 10; loop++) {
+ if (ctrl->event_queue[loop].event_type != 0) {
+ dbg("%s:loop %x event_type %x\n", __FUNCTION__, loop,
+ ctrl->event_queue[loop].event_type);
+ hp_slot = ctrl->event_queue[loop].hp_slot;
+
+ func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
+
+ p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ dbg("%s: hp_slot %d, func %p, p_slot %p\n", __FUNCTION__, hp_slot, func, p_slot);
+
+ if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) {
+ dbg("%s: button cancel\n", __FUNCTION__);
+ del_timer(&p_slot->task_event);
+
+ switch (p_slot->state) {
+ case BLINKINGOFF_STATE:
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ p_slot->hpc_ops->green_led_on(p_slot);
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ p_slot->hpc_ops->set_attention_status(p_slot, 0);
+
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ break;
+ case BLINKINGON_STATE:
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ p_slot->hpc_ops->green_led_off(p_slot);
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ p_slot->hpc_ops->set_attention_status(p_slot, 0);
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+
+ break;
+ default:
+ warn("Not a valid state\n");
+ return;
+ }
+ info(msg_button_cancel, p_slot->number);
+ p_slot->state = STATIC_STATE;
+ } else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) {
+ /* Button Pressed (No action on 1st press...) */
+ dbg("%s: Button pressed\n", __FUNCTION__);
+
+ p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
+ if (getstatus) {
+ /* slot is on */
+ dbg("%s: slot is on\n", __FUNCTION__);
+ p_slot->state = BLINKINGOFF_STATE;
+ info(msg_button_off, p_slot->number);
+ } else {
+ /* slot is off */
+ dbg("%s: slot is off\n", __FUNCTION__);
+ p_slot->state = BLINKINGON_STATE;
+ info(msg_button_on, p_slot->number);
+ }
+
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ /* blink green LED and turn off amber */
+ p_slot->hpc_ops->green_led_blink(p_slot);
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ p_slot->hpc_ops->set_attention_status(p_slot, 0);
+
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+
+ init_timer(&p_slot->task_event);
+ p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */
+ p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread;
+ p_slot->task_event.data = (unsigned long) p_slot;
+
+ dbg("%s: add_timer p_slot = %p\n", __FUNCTION__,(void *) p_slot);
+ add_timer(&p_slot->task_event);
+ } else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
+ /***********POWER FAULT********************/
+ dbg("%s: power fault\n", __FUNCTION__);
+ /* Wait for exclusive access to hardware */
+ down(&ctrl->crit_sect);
+
+ p_slot->hpc_ops->set_attention_status(p_slot, 1);
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ p_slot->hpc_ops->green_led_off(p_slot);
+ /* Wait for the command to complete */
+ wait_for_ctrl_irq (ctrl);
+
+ /* Done with exclusive hardware access */
+ up(&ctrl->crit_sect);
+ } else {
+ /* refresh notification */
+ if (p_slot)
+ update_slot_info(p_slot);
+ }
+
+ ctrl->event_queue[loop].event_type = 0;
+
+ change = 1;
+ }
+ } /* End of FOR loop */
+ }
+
+ return;
+}
+
+
+int shpchp_enable_slot (struct slot *p_slot)
+{
+ u8 getstatus = 0;
+ int rc;
+ struct pci_func *func;
+
+ func = shpchp_slot_find(p_slot->bus, p_slot->device, 0);
+ if (!func) {
+ dbg("%s: Error! slot NULL\n", __FUNCTION__);
+ return 1;
+ }
+
+ /* Check to see if (latch closed, card present, power off) */
+ down(&p_slot->ctrl->crit_sect);
+ rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
+ if (rc || !getstatus) {
+ info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
+ up(&p_slot->ctrl->crit_sect);
+ return 1;
+ }
+ rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+ if (rc || getstatus) {
+ info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
+ up(&p_slot->ctrl->crit_sect);
+ return 1;
+ }
+ rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
+ if (rc || getstatus) {
+ info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number);
+ up(&p_slot->ctrl->crit_sect);
+ return 1;
+ }
+ up(&p_slot->ctrl->crit_sect);
+
+ slot_remove(func);
+
+ func = shpchp_slot_create(p_slot->bus);
+ if (func == NULL)
+ return 1;
+
+ func->bus = p_slot->bus;
+ func->device = p_slot->device;
+ func->function = 0;
+ func->configured = 0;
+ func->is_a_board = 1;
+
+ /* We have to save the presence info for these slots */
+ p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
+ p_slot->hpc_ops->get_power_status(p_slot, &(func->pwr_save));
+ dbg("%s: func->pwr_save %x\n", __FUNCTION__, func->pwr_save);
+ p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+ func->switch_save = !getstatus? 0x10:0;
+
+ rc = board_added(func, p_slot->ctrl);
+ if (rc) {
+ if (is_bridge(func))
+ bridge_slot_remove(func);
+ else
+ slot_remove(func);
+
+ /* Setup slot structure with entry for empty slot */
+ func = shpchp_slot_create(p_slot->bus);
+ if (func == NULL)
+ return (1); /* Out of memory */
+
+ func->bus = p_slot->bus;
+ func->device = p_slot->device;
+ func->function = 0;
+ func->configured = 0;
+ func->is_a_board = 1;
+
+ /* We have to save the presence info for these slots */
+ p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
+ p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+ func->switch_save = !getstatus? 0x10:0;
+ }
+
+ if (p_slot)
+ update_slot_info(p_slot);
+
+ return rc;
+}
+
+
+int shpchp_disable_slot (struct slot *p_slot)
+{
+ u8 class_code, header_type, BCR;
+ u8 index = 0;
+ u8 getstatus = 0;
+ u32 rc = 0;
+ int ret = 0;
+ unsigned int devfn;
+ struct pci_bus *pci_bus;
+ struct pci_func *func;
+
+ if (!p_slot->ctrl)
+ return 1;
+
+ pci_bus = p_slot->ctrl->pci_dev->subordinate;
+
+ /* Check to see if (latch closed, card present, power on) */
+ down(&p_slot->ctrl->crit_sect);
+
+ ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
+ if (ret || !getstatus) {
+ info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
+ up(&p_slot->ctrl->crit_sect);
+ return 1;
+ }
+ ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
+ if (ret || getstatus) {
+ info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
+ up(&p_slot->ctrl->crit_sect);
+ return 1;
+ }
+ ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
+ if (ret || !getstatus) {
+ info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number);
+ up(&p_slot->ctrl->crit_sect);
+ return 1;
+ }
+ up(&p_slot->ctrl->crit_sect);
+
+ func = shpchp_slot_find(p_slot->bus, p_slot->device, index++);
+
+ /* Make sure there are no video controllers here
+ * for all func of p_slot
+ */
+ while (func && !rc) {
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ /* Check the Class Code */
+ rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
+ if (rc)
+ return rc;
+
+ if (class_code == PCI_BASE_CLASS_DISPLAY) {
+ /* Display/Video adapter (not supported) */
+ rc = REMOVE_NOT_SUPPORTED;
+ } else {
+ /* See if it's a bridge */
+ rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+ if (rc)
+ return rc;
+
+ /* If it's a bridge, check the VGA Enable bit */
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+ rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR);
+ if (rc)
+ return rc;
+
+ /* If the VGA Enable bit is set, remove isn't supported */
+ if (BCR & PCI_BRIDGE_CTL_VGA) {
+ rc = REMOVE_NOT_SUPPORTED;
+ }
+ }
+ }
+
+ func = shpchp_slot_find(p_slot->bus, p_slot->device, index++);
+ }
+
+ func = shpchp_slot_find(p_slot->bus, p_slot->device, 0);
+ if ((func != NULL) && !rc) {
+ rc = remove_board(func, p_slot->ctrl);
+ } else if (!rc)
+ rc = 1;
+
+ if (p_slot)
+ update_slot_info(p_slot);
+
+ return(rc);
+}
+
+
+/**
+ * configure_new_device - Configures the PCI header information of one board.
+ *
+ * @ctrl: pointer to controller structure
+ * @func: pointer to function structure
+ * @behind_bridge: 1 if this is a recursive call, 0 if not
+ * @resources: pointer to set of resource lists
+ *
+ * Returns 0 if success
+ *
+ */
+static u32 configure_new_device (struct controller * ctrl, struct pci_func * func,
+ u8 behind_bridge, struct resource_lists * resources, u8 bridge_bus, u8 bridge_dev)
+{
+ u8 temp_byte, function, max_functions, stop_it;
+ int rc;
+ u32 ID;
+ struct pci_func *new_slot;
+ struct pci_bus lpci_bus, *pci_bus;
+ int index;
+
+ new_slot = func;
+
+ dbg("%s\n", __FUNCTION__);
+ memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+
+ /* Check for Multi-function device */
+ rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte);
+ if (rc) {
+ dbg("%s: rc = %d\n", __FUNCTION__, rc);
+ return rc;
+ }
+
+ if (temp_byte & 0x80) /* Multi-function device */
+ max_functions = 8;
+ else
+ max_functions = 1;
+
+ function = 0;
+
+ do {
+ rc = configure_new_function(ctrl, new_slot, behind_bridge, resources, bridge_bus, bridge_dev);
+
+ if (rc) {
+ dbg("configure_new_function failed %d\n",rc);
+ index = 0;
+
+ while (new_slot) {
+ new_slot = shpchp_slot_find(new_slot->bus, new_slot->device, index++);
+
+ if (new_slot)
+ shpchp_return_board_resources(new_slot, resources);
+ }
+
+ return(rc);
+ }
+
+ function++;
+
+ stop_it = 0;
+
+ /* The following loop skips to the next present function
+ * and creates a board structure
+ */
+
+ while ((function < max_functions) && (!stop_it)) {
+ pci_bus_read_config_dword(pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID);
+
+ if (ID == 0xFFFFFFFF) { /* There's nothing there. */
+ function++;
+ } else { /* There's something there */
+ /* Setup slot structure. */
+ new_slot = shpchp_slot_create(func->bus);
+
+ if (new_slot == NULL) {
+ /* Out of memory */
+ return(1);
+ }
+
+ new_slot->bus = func->bus;
+ new_slot->device = func->device;
+ new_slot->function = function;
+ new_slot->is_a_board = 1;
+ new_slot->status = 0;
+
+ stop_it++;
+ }
+ }
+
+ } while (function < max_functions);
+ dbg("returning from configure_new_device\n");
+
+ return 0;
+}
+
+
+/*
+ * Configuration logic that involves the hotplug data structures and
+ * their bookkeeping
+ */
+
+
+/**
+ * configure_new_function - Configures the PCI header information of one device
+ *
+ * @ctrl: pointer to controller structure
+ * @func: pointer to function structure
+ * @behind_bridge: 1 if this is a recursive call, 0 if not
+ * @resources: pointer to set of resource lists
+ *
+ * Calls itself recursively for bridged devices.
+ * Returns 0 if success
+ *
+ */
+static int configure_new_function (struct controller * ctrl, struct pci_func * func,
+ u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev)
+{
+ int cloop;
+ u8 temp_byte;
+ u8 device;
+ u8 class_code;
+ u16 temp_word;
+ u32 rc;
+ u32 temp_register;
+ u32 base;
+ u32 ID;
+ unsigned int devfn;
+ struct pci_resource *mem_node;
+ struct pci_resource *p_mem_node;
+ struct pci_resource *io_node;
+ struct pci_resource *bus_node;
+ struct pci_resource *hold_mem_node;
+ struct pci_resource *hold_p_mem_node;
+ struct pci_resource *hold_IO_node;
+ struct pci_resource *hold_bus_node;
+ struct irq_mapping irqs;
+ struct pci_func *new_slot;
+ struct pci_bus lpci_bus, *pci_bus;
+ struct resource_lists temp_resources;
+#if defined(CONFIG_X86_64)
+ u8 IRQ=0;
+#endif
+
+ memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ /* Check for Bridge */
+ rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte);
+ if (rc)
+ return rc;
+
+ if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
+ /* set Primary bus */
+ dbg("set Primary bus = 0x%x\n", func->bus);
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus);
+ if (rc)
+ return rc;
+
+ /* find range of busses to use */
+ bus_node = get_max_resource(&resources->bus_head, 1L);
+
+ /* If we don't have any busses to allocate, we can't continue */
+ if (!bus_node) {
+ err("Got NO bus resource to use\n");
+ return -ENOMEM;
+ }
+ dbg("Got ranges of buses to use: base:len=0x%x:%x\n", bus_node->base, bus_node->length);
+
+ /* set Secondary bus */
+ temp_byte = (u8)bus_node->base;
+ dbg("set Secondary bus = 0x%x\n", temp_byte);
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte);
+ if (rc)
+ return rc;
+
+ /* set subordinate bus */
+ temp_byte = (u8)(bus_node->base + bus_node->length - 1);
+ dbg("set subordinate bus = 0x%x\n", temp_byte);
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
+ if (rc)
+ return rc;
+
+ /* Set HP parameters (Cache Line Size, Latency Timer) */
+ rc = shpchprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_BRIDGE);
+ if (rc)
+ return rc;
+
+ /* Setup the IO, memory, and prefetchable windows */
+
+ io_node = get_max_resource(&(resources->io_head), 0x1000L);
+ if (io_node) {
+ dbg("io_node(base, len, next) (%x, %x, %p)\n", io_node->base, io_node->length, io_node->next);
+ }
+
+ mem_node = get_max_resource(&(resources->mem_head), 0x100000L);
+ if (mem_node) {
+ dbg("mem_node(base, len, next) (%x, %x, %p)\n", mem_node->base, mem_node->length, mem_node->next);
+ }
+
+ if (resources->p_mem_head)
+ p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000L);
+ else {
+ /*
+ * In some platform implementation, MEM and PMEM are not
+ * distinguished, and hence ACPI _CRS has only MEM entries
+ * for both MEM and PMEM.
+ */
+ dbg("using MEM for PMEM\n");
+ p_mem_node = get_max_resource(&(resources->mem_head), 0x100000L);
+ }
+ if (p_mem_node) {
+ dbg("p_mem_node(base, len, next) (%x, %x, %p)\n", p_mem_node->base, p_mem_node->length, p_mem_node->next);
+ }
+
+ /* set up the IRQ info */
+ if (!resources->irqs) {
+ irqs.barber_pole = 0;
+ irqs.interrupt[0] = 0;
+ irqs.interrupt[1] = 0;
+ irqs.interrupt[2] = 0;
+ irqs.interrupt[3] = 0;
+ irqs.valid_INT = 0;
+ } else {
+ irqs.barber_pole = resources->irqs->barber_pole;
+ irqs.interrupt[0] = resources->irqs->interrupt[0];
+ irqs.interrupt[1] = resources->irqs->interrupt[1];
+ irqs.interrupt[2] = resources->irqs->interrupt[2];
+ irqs.interrupt[3] = resources->irqs->interrupt[3];
+ irqs.valid_INT = resources->irqs->valid_INT;
+ }
+
+ /* set up resource lists that are now aligned on top and bottom
+ * for anything behind the bridge.
+ */
+ temp_resources.bus_head = bus_node;
+ temp_resources.io_head = io_node;
+ temp_resources.mem_head = mem_node;
+ temp_resources.p_mem_head = p_mem_node;
+ temp_resources.irqs = &irqs;
+
+ /* Make copies of the nodes we are going to pass down so that
+ * if there is a problem,we can just use these to free resources
+ */
+ hold_bus_node = kmalloc(sizeof(*hold_bus_node), GFP_KERNEL);
+ hold_IO_node = kmalloc(sizeof(*hold_IO_node), GFP_KERNEL);
+ hold_mem_node = kmalloc(sizeof(*hold_mem_node), GFP_KERNEL);
+ hold_p_mem_node = kmalloc(sizeof(*hold_p_mem_node), GFP_KERNEL);
+
+ if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) {
+ kfree(hold_bus_node);
+ kfree(hold_IO_node);
+ kfree(hold_mem_node);
+ kfree(hold_p_mem_node);
+
+ return 1;
+ }
+
+ memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource));
+
+ bus_node->base += 1;
+ bus_node->length -= 1;
+ bus_node->next = NULL;
+
+ /* If we have IO resources copy them and fill in the bridge's
+ * IO range registers
+ */
+ if (io_node) {
+ memcpy(hold_IO_node, io_node, sizeof(struct pci_resource));
+ io_node->next = NULL;
+
+ /* set IO base and Limit registers */
+ RES_CHECK(io_node->base, 8);
+ temp_byte = (u8)(io_node->base >> 8);
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte);
+
+ RES_CHECK(io_node->base + io_node->length - 1, 8);
+ temp_byte = (u8)((io_node->base + io_node->length - 1) >> 8);
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+ } else {
+ kfree(hold_IO_node);
+ hold_IO_node = NULL;
+ }
+
+ /* If we have memory resources copy them and fill in the bridge's
+ * memory range registers. Otherwise, fill in the range
+ * registers with values that disable them.
+ */
+ if (mem_node) {
+ memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource));
+ mem_node->next = NULL;
+
+ /* set Mem base and Limit registers */
+ RES_CHECK(mem_node->base, 16);
+ temp_word = (u32)(mem_node->base >> 16);
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+ RES_CHECK(mem_node->base + mem_node->length - 1, 16);
+ temp_word = (u32)((mem_node->base + mem_node->length - 1) >> 16);
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+ } else {
+ temp_word = 0xFFFF;
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+ temp_word = 0x0000;
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+ kfree(hold_mem_node);
+ hold_mem_node = NULL;
+ }
+
+ /* If we have prefetchable memory resources copy them and
+ * fill in the bridge's memory range registers. Otherwise,
+ * fill in the range registers with values that disable them.
+ */
+ if (p_mem_node) {
+ memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource));
+ p_mem_node->next = NULL;
+
+ /* set Pre Mem base and Limit registers */
+ RES_CHECK(p_mem_node->base, 16);
+ temp_word = (u32)(p_mem_node->base >> 16);
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+ RES_CHECK(p_mem_node->base + p_mem_node->length - 1, 16);
+ temp_word = (u32)((p_mem_node->base + p_mem_node->length - 1) >> 16);
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+ } else {
+ temp_word = 0xFFFF;
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+ temp_word = 0x0000;
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+ kfree(hold_p_mem_node);
+ hold_p_mem_node = NULL;
+ }
+
+ /* Adjust this to compensate for extra adjustment in first loop */
+ irqs.barber_pole--;
+
+ rc = 0;
+
+ /* Here we actually find the devices and configure them */
+ for (device = 0; (device <= 0x1F) && !rc; device++) {
+ irqs.barber_pole = (irqs.barber_pole + 1) & 0x03;
+
+ ID = 0xFFFFFFFF;
+ pci_bus->number = hold_bus_node->base;
+ pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0),
+ PCI_VENDOR_ID, &ID);
+ pci_bus->number = func->bus;
+
+ if (ID != 0xFFFFFFFF) { /* device Present */
+ /* Setup slot structure. */
+ new_slot = shpchp_slot_create(hold_bus_node->base);
+
+ if (new_slot == NULL) {
+ /* Out of memory */
+ rc = -ENOMEM;
+ continue;
+ }
+
+ new_slot->bus = hold_bus_node->base;
+ new_slot->device = device;
+ new_slot->function = 0;
+ new_slot->is_a_board = 1;
+ new_slot->status = 0;
+
+ rc = configure_new_device(ctrl, new_slot, 1, &temp_resources, func->bus, func->device);
+ dbg("configure_new_device rc=0x%x\n",rc);
+ } /* End of IF (device in slot?) */
+ } /* End of FOR loop */
+
+ if (rc) {
+ shpchp_destroy_resource_list(&temp_resources);
+
+ return_resource(&(resources->bus_head), hold_bus_node);
+ return_resource(&(resources->io_head), hold_IO_node);
+ return_resource(&(resources->mem_head), hold_mem_node);
+ return_resource(&(resources->p_mem_head), hold_p_mem_node);
+ return(rc);
+ }
+
+ /* save the interrupt routing information */
+ if (resources->irqs) {
+ resources->irqs->interrupt[0] = irqs.interrupt[0];
+ resources->irqs->interrupt[1] = irqs.interrupt[1];
+ resources->irqs->interrupt[2] = irqs.interrupt[2];
+ resources->irqs->interrupt[3] = irqs.interrupt[3];
+ resources->irqs->valid_INT = irqs.valid_INT;
+ } else if (!behind_bridge) {
+ /* We need to hook up the interrupts here */
+ for (cloop = 0; cloop < 4; cloop++) {
+ if (irqs.valid_INT & (0x01 << cloop)) {
+ rc = shpchp_set_irq(func->bus, func->device,
+ 0x0A + cloop, irqs.interrupt[cloop]);
+ if (rc) {
+ shpchp_destroy_resource_list (&temp_resources);
+ return_resource(&(resources->bus_head), hold_bus_node);
+ return_resource(&(resources->io_head), hold_IO_node);
+ return_resource(&(resources->mem_head), hold_mem_node);
+ return_resource(&(resources->p_mem_head), hold_p_mem_node);
+ return rc;
+ }
+ }
+ } /* end of for loop */
+ }
+
+ /* Return unused bus resources
+ * First use the temporary node to store information for the board
+ */
+ if (hold_bus_node && bus_node && temp_resources.bus_head) {
+ hold_bus_node->length = bus_node->base - hold_bus_node->base;
+
+ hold_bus_node->next = func->bus_head;
+ func->bus_head = hold_bus_node;
+
+ temp_byte = (u8)(temp_resources.bus_head->base - 1);
+
+ /* set subordinate bus */
+ dbg("re-set subordinate bus = 0x%x\n", temp_byte);
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
+
+ if (temp_resources.bus_head->length == 0) {
+ kfree(temp_resources.bus_head);
+ temp_resources.bus_head = NULL;
+ } else {
+ dbg("return bus res of b:d(0x%x:%x) base:len(0x%x:%x)\n",
+ func->bus, func->device, temp_resources.bus_head->base, temp_resources.bus_head->length);
+ return_resource(&(resources->bus_head), temp_resources.bus_head);
+ }
+ }
+
+ /* If we have IO space available and there is some left,
+ * return the unused portion
+ */
+ if (hold_IO_node && temp_resources.io_head) {
+ io_node = do_pre_bridge_resource_split(&(temp_resources.io_head),
+ &hold_IO_node, 0x1000);
+
+ /* Check if we were able to split something off */
+ if (io_node) {
+ hold_IO_node->base = io_node->base + io_node->length;
+
+ RES_CHECK(hold_IO_node->base, 8);
+ temp_byte = (u8)((hold_IO_node->base) >> 8);
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte);
+
+ return_resource(&(resources->io_head), io_node);
+ }
+
+ io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000);
+
+ /* Check if we were able to split something off */
+ if (io_node) {
+ /* First use the temporary node to store information for the board */
+ hold_IO_node->length = io_node->base - hold_IO_node->base;
+
+ /* If we used any, add it to the board's list */
+ if (hold_IO_node->length) {
+ hold_IO_node->next = func->io_head;
+ func->io_head = hold_IO_node;
+
+ RES_CHECK(io_node->base - 1, 8);
+ temp_byte = (u8)((io_node->base - 1) >> 8);
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+
+ return_resource(&(resources->io_head), io_node);
+ } else {
+ /* it doesn't need any IO */
+ temp_byte = 0x00;
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+
+ return_resource(&(resources->io_head), io_node);
+ kfree(hold_IO_node);
+ }
+ } else {
+ /* it used most of the range */
+ hold_IO_node->next = func->io_head;
+ func->io_head = hold_IO_node;
+ }
+ } else if (hold_IO_node) {
+ /* it used the whole range */
+ hold_IO_node->next = func->io_head;
+ func->io_head = hold_IO_node;
+ }
+
+ /* If we have memory space available and there is some left,
+ * return the unused portion
+ */
+ if (hold_mem_node && temp_resources.mem_head) {
+ mem_node = do_pre_bridge_resource_split(&(temp_resources.mem_head), &hold_mem_node, 0x100000L);
+
+ /* Check if we were able to split something off */
+ if (mem_node) {
+ hold_mem_node->base = mem_node->base + mem_node->length;
+
+ RES_CHECK(hold_mem_node->base, 16);
+ temp_word = (u32)((hold_mem_node->base) >> 16);
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+ return_resource(&(resources->mem_head), mem_node);
+ }
+
+ mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000L);
+
+ /* Check if we were able to split something off */
+ if (mem_node) {
+ /* First use the temporary node to store information for the board */
+ hold_mem_node->length = mem_node->base - hold_mem_node->base;
+
+ if (hold_mem_node->length) {
+ hold_mem_node->next = func->mem_head;
+ func->mem_head = hold_mem_node;
+
+ /* configure end address */
+ RES_CHECK(mem_node->base - 1, 16);
+ temp_word = (u32)((mem_node->base - 1) >> 16);
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+ /* Return unused resources to the pool */
+ return_resource(&(resources->mem_head), mem_node);
+ } else {
+ /* it doesn't need any Mem */
+ temp_word = 0x0000;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+ return_resource(&(resources->mem_head), mem_node);
+ kfree(hold_mem_node);
+ }
+ } else {
+ /* it used most of the range */
+ hold_mem_node->next = func->mem_head;
+ func->mem_head = hold_mem_node;
+ }
+ } else if (hold_mem_node) {
+ /* it used the whole range */
+ hold_mem_node->next = func->mem_head;
+ func->mem_head = hold_mem_node;
+ }
+
+ /* If we have prefetchable memory space available and there is some
+ * left at the end, return the unused portion
+ */
+ if (hold_p_mem_node && temp_resources.p_mem_head) {
+ p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head),
+ &hold_p_mem_node, 0x100000L);
+
+ /* Check if we were able to split something off */
+ if (p_mem_node) {
+ hold_p_mem_node->base = p_mem_node->base + p_mem_node->length;
+
+ RES_CHECK(hold_p_mem_node->base, 16);
+ temp_word = (u32)((hold_p_mem_node->base) >> 16);
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+ return_resource(&(resources->p_mem_head), p_mem_node);
+ }
+
+ p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000L);
+
+ /* Check if we were able to split something off */
+ if (p_mem_node) {
+ /* First use the temporary node to store information for the board */
+ hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base;
+
+ /* If we used any, add it to the board's list */
+ if (hold_p_mem_node->length) {
+ hold_p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = hold_p_mem_node;
+
+ RES_CHECK(p_mem_node->base - 1, 16);
+ temp_word = (u32)((p_mem_node->base - 1) >> 16);
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+ return_resource(&(resources->p_mem_head), p_mem_node);
+ } else {
+ /* it doesn't need any PMem */
+ temp_word = 0x0000;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+ return_resource(&(resources->p_mem_head), p_mem_node);
+ kfree(hold_p_mem_node);
+ }
+ } else {
+ /* it used the most of the range */
+ hold_p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = hold_p_mem_node;
+ }
+ } else if (hold_p_mem_node) {
+ /* it used the whole range */
+ hold_p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = hold_p_mem_node;
+ }
+
+ /* We should be configuring an IRQ and the bridge's base address
+ * registers if it needs them. Although we have never seen such
+ * a device
+ */
+
+ shpchprm_enable_card(ctrl, func, PCI_HEADER_TYPE_BRIDGE);
+
+ dbg("PCI Bridge Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device, func->function);
+ } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
+ /* Standard device */
+ u64 base64;
+ rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
+
+ if (class_code == PCI_BASE_CLASS_DISPLAY)
+ return (DEVICE_TYPE_NOT_SUPPORTED);
+
+ /* Figure out IO and memory needs */
+ for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) {
+ temp_register = 0xFFFFFFFF;
+
+ rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
+ rc = pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register);
+ dbg("Bar[%x]=0x%x on bus:dev:func(0x%x:%x:%x)\n", cloop, temp_register, func->bus, func->device,
+ func->function);
+
+ if (!temp_register)
+ continue;
+
+ base64 = 0L;
+ if (temp_register & PCI_BASE_ADDRESS_SPACE_IO) {
+ /* Map IO */
+
+ /* set base = amount of IO space */
+ base = temp_register & 0xFFFFFFFC;
+ base = ~base + 1;
+
+ dbg("NEED IO length(0x%x)\n", base);
+ io_node = get_io_resource(&(resources->io_head),(ulong)base);
+
+ /* allocate the resource to the board */
+ if (io_node) {
+ dbg("Got IO base=0x%x(length=0x%x)\n", io_node->base, io_node->length);
+ base = (u32)io_node->base;
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ } else {
+ err("Got NO IO resource(length=0x%x)\n", base);
+ return -ENOMEM;
+ }
+ } else { /* map MEM */
+ int prefetchable = 1;
+ struct pci_resource **res_node = &func->p_mem_head;
+ char *res_type_str = "PMEM";
+ u32 temp_register2;
+
+ if (!(temp_register & PCI_BASE_ADDRESS_MEM_PREFETCH)) {
+ prefetchable = 0;
+ res_node = &func->mem_head;
+ res_type_str++;
+ }
+
+ base = temp_register & 0xFFFFFFF0;
+ base = ~base + 1;
+
+ switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
+ case PCI_BASE_ADDRESS_MEM_TYPE_32:
+ dbg("NEED 32 %s bar=0x%x(length=0x%x)\n", res_type_str, temp_register, base);
+
+ if (prefetchable && resources->p_mem_head)
+ mem_node=get_resource(&(resources->p_mem_head), (ulong)base);
+ else {
+ if (prefetchable)
+ dbg("using MEM for PMEM\n");
+ mem_node=get_resource(&(resources->mem_head), (ulong)base);
+ }
+
+ /* allocate the resource to the board */
+ if (mem_node) {
+ base = (u32)mem_node->base;
+ mem_node->next = *res_node;
+ *res_node = mem_node;
+ dbg("Got 32 %s base=0x%x(length=0x%x)\n", res_type_str, mem_node->base,
+ mem_node->length);
+ } else {
+ err("Got NO 32 %s resource(length=0x%x)\n", res_type_str, base);
+ return -ENOMEM;
+ }
+ break;
+ case PCI_BASE_ADDRESS_MEM_TYPE_64:
+ rc = pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2);
+ dbg("NEED 64 %s bar=0x%x:%x(length=0x%x)\n", res_type_str, temp_register2,
+ temp_register, base);
+
+ if (prefetchable && resources->p_mem_head)
+ mem_node = get_resource(&(resources->p_mem_head), (ulong)base);
+ else {
+ if (prefetchable)
+ dbg("using MEM for PMEM\n");
+ mem_node = get_resource(&(resources->mem_head), (ulong)base);
+ }
+
+ /* allocate the resource to the board */
+ if (mem_node) {
+ base64 = mem_node->base;
+ mem_node->next = *res_node;
+ *res_node = mem_node;
+ dbg("Got 64 %s base=0x%x:%x(length=%x)\n", res_type_str, (u32)(base64 >> 32),
+ (u32)base64, mem_node->length);
+ } else {
+ err("Got NO 64 %s resource(length=0x%x)\n", res_type_str, base);
+ return -ENOMEM;
+ }
+ break;
+ default:
+ dbg("reserved BAR type=0x%x\n", temp_register);
+ break;
+ }
+
+ }
+
+ if (base64) {
+ rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64);
+ cloop += 4;
+ base64 >>= 32;
+
+ if (base64) {
+ dbg("%s: high dword of base64(0x%x) set to 0\n", __FUNCTION__, (u32)base64);
+ base64 = 0x0L;
+ }
+
+ rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64);
+ } else {
+ rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base);
+ }
+ } /* End of base register loop */
+
+#if defined(CONFIG_X86_64)
+ /* Figure out which interrupt pin this function uses */
+ rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_INTERRUPT_PIN, &temp_byte);
+
+ /* If this function needs an interrupt and we are behind a bridge
+ and the pin is tied to something that's alread mapped,
+ set this one the same
+ */
+ if (temp_byte && resources->irqs &&
+ (resources->irqs->valid_INT &
+ (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) {
+ /* We have to share with something already set up */
+ IRQ = resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03];
+ } else {
+ /* Program IRQ based on card type */
+ rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
+
+ if (class_code == PCI_BASE_CLASS_STORAGE) {
+ IRQ = shpchp_disk_irq;
+ } else {
+ IRQ = shpchp_nic_irq;
+ }
+ }
+
+ /* IRQ Line */
+ rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ);
+
+ if (!behind_bridge) {
+ rc = shpchp_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ);
+ if (rc)
+ return(1);
+ } else {
+ /* TBD - this code may also belong in the other clause of this If statement */
+ resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ;
+ resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03;
+ }
+#endif
+ /* Disable ROM base Address */
+ temp_word = 0x00L;
+ rc = pci_bus_write_config_word (pci_bus, devfn, PCI_ROM_ADDRESS, temp_word);
+
+ /* Set HP parameters (Cache Line Size, Latency Timer) */
+ rc = shpchprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_NORMAL);
+ if (rc)
+ return rc;
+
+ shpchprm_enable_card(ctrl, func, PCI_HEADER_TYPE_NORMAL);
+
+ dbg("PCI function Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device, func->function);
+ } /* End of Not-A-Bridge else */
+ else {
+ /* It's some strange type of PCI adapter (Cardbus?) */
+ return(DEVICE_TYPE_NOT_SUPPORTED);
+ }
+
+ func->configured = 1;
+
+ return 0;
+}
+
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
new file mode 100644
index 000000000000..38c5d9066697
--- /dev/null
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -0,0 +1,1620 @@
+/*
+ * Standard PCI Hot Plug Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>,<dely.l.sy@intel.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <asm/system.h>
+#include "shpchp.h"
+
+#ifdef DEBUG
+#define DBG_K_TRACE_ENTRY ((unsigned int)0x00000001) /* On function entry */
+#define DBG_K_TRACE_EXIT ((unsigned int)0x00000002) /* On function exit */
+#define DBG_K_INFO ((unsigned int)0x00000004) /* Info messages */
+#define DBG_K_ERROR ((unsigned int)0x00000008) /* Error messages */
+#define DBG_K_TRACE (DBG_K_TRACE_ENTRY|DBG_K_TRACE_EXIT)
+#define DBG_K_STANDARD (DBG_K_INFO|DBG_K_ERROR|DBG_K_TRACE)
+/* Redefine this flagword to set debug level */
+#define DEBUG_LEVEL DBG_K_STANDARD
+
+#define DEFINE_DBG_BUFFER char __dbg_str_buf[256];
+
+#define DBG_PRINT( dbg_flags, args... ) \
+ do { \
+ if ( DEBUG_LEVEL & ( dbg_flags ) ) \
+ { \
+ int len; \
+ len = sprintf( __dbg_str_buf, "%s:%d: %s: ", \
+ __FILE__, __LINE__, __FUNCTION__ ); \
+ sprintf( __dbg_str_buf + len, args ); \
+ printk( KERN_NOTICE "%s\n", __dbg_str_buf ); \
+ } \
+ } while (0)
+
+#define DBG_ENTER_ROUTINE DBG_PRINT (DBG_K_TRACE_ENTRY, "%s", "[Entry]");
+#define DBG_LEAVE_ROUTINE DBG_PRINT (DBG_K_TRACE_EXIT, "%s", "[Exit]");
+#else
+#define DEFINE_DBG_BUFFER
+#define DBG_ENTER_ROUTINE
+#define DBG_LEAVE_ROUTINE
+#endif /* DEBUG */
+
+/* Slot Available Register I field definition */
+#define SLOT_33MHZ 0x0000001f
+#define SLOT_66MHZ_PCIX 0x00001f00
+#define SLOT_100MHZ_PCIX 0x001f0000
+#define SLOT_133MHZ_PCIX 0x1f000000
+
+/* Slot Available Register II field definition */
+#define SLOT_66MHZ 0x0000001f
+#define SLOT_66MHZ_PCIX_266 0x00000f00
+#define SLOT_100MHZ_PCIX_266 0x0000f000
+#define SLOT_133MHZ_PCIX_266 0x000f0000
+#define SLOT_66MHZ_PCIX_533 0x00f00000
+#define SLOT_100MHZ_PCIX_533 0x0f000000
+#define SLOT_133MHZ_PCIX_533 0xf0000000
+
+
+/* Secondary Bus Configuration Register */
+/* For PI = 1, Bits 0 to 2 have been encoded as follows to show current bus speed/mode */
+#define PCI_33MHZ 0x0
+#define PCI_66MHZ 0x1
+#define PCIX_66MHZ 0x2
+#define PCIX_100MHZ 0x3
+#define PCIX_133MHZ 0x4
+
+/* For PI = 2, Bits 0 to 3 have been encoded as follows to show current bus speed/mode */
+#define PCI_33MHZ 0x0
+#define PCI_66MHZ 0x1
+#define PCIX_66MHZ 0x2
+#define PCIX_100MHZ 0x3
+#define PCIX_133MHZ 0x4
+#define PCIX_66MHZ_ECC 0x5
+#define PCIX_100MHZ_ECC 0x6
+#define PCIX_133MHZ_ECC 0x7
+#define PCIX_66MHZ_266 0x9
+#define PCIX_100MHZ_266 0xa
+#define PCIX_133MHZ_266 0xb
+#define PCIX_66MHZ_533 0x11
+#define PCIX_100MHZ_533 0x12
+#define PCIX_133MHZ_533 0x13
+
+/* Slot Configuration */
+#define SLOT_NUM 0x0000001F
+#define FIRST_DEV_NUM 0x00001F00
+#define PSN 0x07FF0000
+#define UPDOWN 0x20000000
+#define MRLSENSOR 0x40000000
+#define ATTN_BUTTON 0x80000000
+
+/* Slot Status Field Definitions */
+/* Slot State */
+#define PWR_ONLY 0x0001
+#define ENABLED 0x0002
+#define DISABLED 0x0003
+
+/* Power Indicator State */
+#define PWR_LED_ON 0x0004
+#define PWR_LED_BLINK 0x0008
+#define PWR_LED_OFF 0x000c
+
+/* Attention Indicator State */
+#define ATTEN_LED_ON 0x0010
+#define ATTEN_LED_BLINK 0x0020
+#define ATTEN_LED_OFF 0x0030
+
+/* Power Fault */
+#define pwr_fault 0x0040
+
+/* Attention Button */
+#define ATTEN_BUTTON 0x0080
+
+/* MRL Sensor */
+#define MRL_SENSOR 0x0100
+
+/* 66 MHz Capable */
+#define IS_66MHZ_CAP 0x0200
+
+/* PRSNT1#/PRSNT2# */
+#define SLOT_EMP 0x0c00
+
+/* PCI-X Capability */
+#define NON_PCIX 0x0000
+#define PCIX_66 0x1000
+#define PCIX_133 0x3000
+#define PCIX_266 0x4000 /* For PI = 2 only */
+#define PCIX_533 0x5000 /* For PI = 2 only */
+
+/* SHPC 'write' operations/commands */
+
+/* Slot operation - 0x00h to 0x3Fh */
+
+#define NO_CHANGE 0x00
+
+/* Slot state - Bits 0 & 1 of controller command register */
+#define SET_SLOT_PWR 0x01
+#define SET_SLOT_ENABLE 0x02
+#define SET_SLOT_DISABLE 0x03
+
+/* Power indicator state - Bits 2 & 3 of controller command register*/
+#define SET_PWR_ON 0x04
+#define SET_PWR_BLINK 0x08
+#define SET_PWR_OFF 0x0C
+
+/* Attention indicator state - Bits 4 & 5 of controller command register*/
+#define SET_ATTN_ON 0x010
+#define SET_ATTN_BLINK 0x020
+#define SET_ATTN_OFF 0x030
+
+/* Set bus speed/mode A - 0x40h to 0x47h */
+#define SETA_PCI_33MHZ 0x40
+#define SETA_PCI_66MHZ 0x41
+#define SETA_PCIX_66MHZ 0x42
+#define SETA_PCIX_100MHZ 0x43
+#define SETA_PCIX_133MHZ 0x44
+#define RESERV_1 0x45
+#define RESERV_2 0x46
+#define RESERV_3 0x47
+
+/* Set bus speed/mode B - 0x50h to 0x5fh */
+#define SETB_PCI_33MHZ 0x50
+#define SETB_PCI_66MHZ 0x51
+#define SETB_PCIX_66MHZ_PM 0x52
+#define SETB_PCIX_100MHZ_PM 0x53
+#define SETB_PCIX_133MHZ_PM 0x54
+#define SETB_PCIX_66MHZ_EM 0x55
+#define SETB_PCIX_100MHZ_EM 0x56
+#define SETB_PCIX_133MHZ_EM 0x57
+#define SETB_PCIX_66MHZ_266 0x58
+#define SETB_PCIX_100MHZ_266 0x59
+#define SETB_PCIX_133MHZ_266 0x5a
+#define SETB_PCIX_66MHZ_533 0x5b
+#define SETB_PCIX_100MHZ_533 0x5c
+#define SETB_PCIX_133MHZ_533 0x5d
+
+
+/* Power-on all slots - 0x48h */
+#define SET_PWR_ON_ALL 0x48
+
+/* Enable all slots - 0x49h */
+#define SET_ENABLE_ALL 0x49
+
+/* SHPC controller command error code */
+#define SWITCH_OPEN 0x1
+#define INVALID_CMD 0x2
+#define INVALID_SPEED_MODE 0x4
+
+/* For accessing SHPC Working Register Set */
+#define DWORD_SELECT 0x2
+#define DWORD_DATA 0x4
+#define BASE_OFFSET 0x0
+
+/* Field Offset in Logical Slot Register - byte boundary */
+#define SLOT_EVENT_LATCH 0x2
+#define SLOT_SERR_INT_MASK 0x3
+
+static spinlock_t hpc_event_lock;
+
+DEFINE_DBG_BUFFER /* Debug string buffer for entire HPC defined here */
+static struct php_ctlr_state_s *php_ctlr_list_head; /* HPC state linked list */
+static int ctlr_seq_num = 0; /* Controller sequenc # */
+static spinlock_t list_lock;
+
+static irqreturn_t shpc_isr(int IRQ, void *dev_id, struct pt_regs *regs);
+
+static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds);
+
+/* This is the interrupt polling timeout function. */
+static void int_poll_timeout(unsigned long lphp_ctlr)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *)lphp_ctlr;
+
+ DBG_ENTER_ROUTINE
+
+ if ( !php_ctlr ) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return;
+ }
+
+ /* Poll for interrupt events. regs == NULL => polling */
+ shpc_isr( 0, (void *)php_ctlr, NULL );
+
+ init_timer(&php_ctlr->int_poll_timer);
+ if (!shpchp_poll_time)
+ shpchp_poll_time = 2; /* reset timer to poll in 2 secs if user doesn't specify at module installation*/
+
+ start_int_poll_timer(php_ctlr, shpchp_poll_time);
+
+ return;
+}
+
+/* This function starts the interrupt polling timer. */
+static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds)
+{
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return;
+ }
+
+ if ( ( seconds <= 0 ) || ( seconds > 60 ) )
+ seconds = 2; /* Clamp to sane value */
+
+ php_ctlr->int_poll_timer.function = &int_poll_timeout;
+ php_ctlr->int_poll_timer.data = (unsigned long)php_ctlr; /* Instance data */
+ php_ctlr->int_poll_timer.expires = jiffies + seconds * HZ;
+ add_timer(&php_ctlr->int_poll_timer);
+
+ return;
+}
+
+static int shpc_write_cmd(struct slot *slot, u8 t_slot, u8 cmd)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u16 cmd_status;
+ int retval = 0;
+ u16 temp_word;
+ int i;
+
+ DBG_ENTER_ROUTINE
+
+ if (!php_ctlr) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ for (i = 0; i < 10; i++) {
+ cmd_status = readw(php_ctlr->creg + CMD_STATUS);
+
+ if (!(cmd_status & 0x1))
+ break;
+ /* Check every 0.1 sec for a total of 1 sec*/
+ msleep(100);
+ }
+
+ cmd_status = readw(php_ctlr->creg + CMD_STATUS);
+
+ if (cmd_status & 0x1) {
+ /* After 1 sec and and the controller is still busy */
+ err("%s : Controller is still busy after 1 sec.\n", __FUNCTION__);
+ return -1;
+ }
+
+ ++t_slot;
+ temp_word = (t_slot << 8) | (cmd & 0xFF);
+ dbg("%s: t_slot %x cmd %x\n", __FUNCTION__, t_slot, cmd);
+
+ /* To make sure the Controller Busy bit is 0 before we send out the
+ * command.
+ */
+ writew(temp_word, php_ctlr->creg + CMD);
+ dbg("%s: temp_word written %x\n", __FUNCTION__, temp_word);
+
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static int hpc_check_cmd_status(struct controller *ctrl)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle;
+ u16 cmd_status;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ cmd_status = readw(php_ctlr->creg + CMD_STATUS) & 0x000F;
+
+ switch (cmd_status >> 1) {
+ case 0:
+ retval = 0;
+ break;
+ case 1:
+ retval = SWITCH_OPEN;
+ err("%s: Switch opened!\n", __FUNCTION__);
+ break;
+ case 2:
+ retval = INVALID_CMD;
+ err("%s: Invalid HPC command!\n", __FUNCTION__);
+ break;
+ case 4:
+ retval = INVALID_SPEED_MODE;
+ err("%s: Invalid bus speed/mode!\n", __FUNCTION__);
+ break;
+ default:
+ retval = cmd_status;
+ }
+
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+
+static int hpc_get_attention_status(struct slot *slot, u8 *status)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u32 slot_reg;
+ u16 slot_status;
+ u8 atten_led_state;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot));
+ slot_status = (u16) slot_reg;
+ atten_led_state = (slot_status & 0x0030) >> 4;
+
+ switch (atten_led_state) {
+ case 0:
+ *status = 0xFF; /* Reserved */
+ break;
+ case 1:
+ *status = 1; /* On */
+ break;
+ case 2:
+ *status = 2; /* Blink */
+ break;
+ case 3:
+ *status = 0; /* Off */
+ break;
+ default:
+ *status = 0xFF;
+ break;
+ }
+
+ DBG_LEAVE_ROUTINE
+ return 0;
+}
+
+static int hpc_get_power_status(struct slot * slot, u8 *status)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u32 slot_reg;
+ u16 slot_status;
+ u8 slot_state;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot));
+ slot_status = (u16) slot_reg;
+ slot_state = (slot_status & 0x0003);
+
+ switch (slot_state) {
+ case 0:
+ *status = 0xFF;
+ break;
+ case 1:
+ *status = 2; /* Powered only */
+ break;
+ case 2:
+ *status = 1; /* Enabled */
+ break;
+ case 3:
+ *status = 0; /* Disabled */
+ break;
+ default:
+ *status = 0xFF;
+ break;
+ }
+
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+
+static int hpc_get_latch_status(struct slot *slot, u8 *status)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u32 slot_reg;
+ u16 slot_status;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot));
+ slot_status = (u16)slot_reg;
+
+ *status = ((slot_status & 0x0100) == 0) ? 0 : 1; /* 0 -> close; 1 -> open */
+
+
+ DBG_LEAVE_ROUTINE
+ return 0;
+}
+
+static int hpc_get_adapter_status(struct slot *slot, u8 *status)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u32 slot_reg;
+ u16 slot_status;
+ u8 card_state;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot));
+ slot_status = (u16)slot_reg;
+ card_state = (u8)((slot_status & 0x0C00) >> 10);
+ *status = (card_state != 0x3) ? 1 : 0;
+
+ DBG_LEAVE_ROUTINE
+ return 0;
+}
+
+static int hpc_get_prog_int(struct slot *slot, u8 *prog_int)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ *prog_int = readb(php_ctlr->creg + PROG_INTERFACE);
+
+ DBG_LEAVE_ROUTINE
+ return 0;
+}
+
+static int hpc_get_adapter_speed(struct slot *slot, enum pci_bus_speed *value)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u32 slot_reg;
+ u16 slot_status, sec_bus_status;
+ u8 m66_cap, pcix_cap, pi;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+
+ pi = readb(php_ctlr->creg + PROG_INTERFACE);
+ slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot));
+ dbg("%s: pi = %d, slot_reg = %x\n", __FUNCTION__, pi, slot_reg);
+ slot_status = (u16) slot_reg;
+ dbg("%s: slot_status = %x\n", __FUNCTION__, slot_status);
+ sec_bus_status = readw(php_ctlr->creg + SEC_BUS_CONFIG);
+
+ pcix_cap = (u8) ((slot_status & 0x3000) >> 12);
+ dbg("%s: pcix_cap = %x\n", __FUNCTION__, pcix_cap);
+ m66_cap = (u8) ((slot_status & 0x0200) >> 9);
+ dbg("%s: m66_cap = %x\n", __FUNCTION__, m66_cap);
+
+
+ if (pi == 2) {
+ switch (pcix_cap) {
+ case 0:
+ *value = m66_cap ? PCI_SPEED_66MHz : PCI_SPEED_33MHz;
+ break;
+ case 1:
+ *value = PCI_SPEED_66MHz_PCIX;
+ break;
+ case 3:
+ *value = PCI_SPEED_133MHz_PCIX;
+ break;
+ case 4:
+ *value = PCI_SPEED_133MHz_PCIX_266;
+ break;
+ case 5:
+ *value = PCI_SPEED_133MHz_PCIX_533;
+ break;
+ case 2: /* Reserved */
+ default:
+ *value = PCI_SPEED_UNKNOWN;
+ retval = -ENODEV;
+ break;
+ }
+ } else {
+ switch (pcix_cap) {
+ case 0:
+ *value = m66_cap ? PCI_SPEED_66MHz : PCI_SPEED_33MHz;
+ break;
+ case 1:
+ *value = PCI_SPEED_66MHz_PCIX;
+ break;
+ case 3:
+ *value = PCI_SPEED_133MHz_PCIX;
+ break;
+ case 2: /* Reserved */
+ default:
+ *value = PCI_SPEED_UNKNOWN;
+ retval = -ENODEV;
+ break;
+ }
+ }
+
+ dbg("Adapter speed = %d\n", *value);
+
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static int hpc_get_mode1_ECC_cap(struct slot *slot, u8 *mode)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u16 sec_bus_status;
+ u8 pi;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ pi = readb(php_ctlr->creg + PROG_INTERFACE);
+ sec_bus_status = readw(php_ctlr->creg + SEC_BUS_CONFIG);
+
+ if (pi == 2) {
+ *mode = (sec_bus_status & 0x0100) >> 7;
+ } else {
+ retval = -1;
+ }
+
+ dbg("Mode 1 ECC cap = %d\n", *mode);
+
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static int hpc_query_power_fault(struct slot * slot)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u32 slot_reg;
+ u16 slot_status;
+ u8 pwr_fault_state, status;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot));
+ slot_status = (u16) slot_reg;
+ pwr_fault_state = (slot_status & 0x0040) >> 7;
+ status = (pwr_fault_state == 1) ? 0 : 1;
+
+ DBG_LEAVE_ROUTINE
+ /* Note: Logic 0 => fault */
+ return status;
+}
+
+static int hpc_set_attention_status(struct slot *slot, u8 value)
+{
+ struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u8 slot_cmd = 0;
+ int rc = 0;
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+
+ switch (value) {
+ case 0 :
+ slot_cmd = 0x30; /* OFF */
+ break;
+ case 1:
+ slot_cmd = 0x10; /* ON */
+ break;
+ case 2:
+ slot_cmd = 0x20; /* BLINK */
+ break;
+ default:
+ return -1;
+ }
+
+ shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
+
+ return rc;
+}
+
+
+static void hpc_set_green_led_on(struct slot *slot)
+{
+ struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u8 slot_cmd;
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return ;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return ;
+ }
+
+ slot_cmd = 0x04;
+
+ shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
+
+ return;
+}
+
+static void hpc_set_green_led_off(struct slot *slot)
+{
+ struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u8 slot_cmd;
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return ;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return ;
+ }
+
+ slot_cmd = 0x0C;
+
+ shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
+
+ return;
+}
+
+static void hpc_set_green_led_blink(struct slot *slot)
+{
+ struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u8 slot_cmd;
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return ;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return ;
+ }
+
+ slot_cmd = 0x08;
+
+ shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
+
+ return;
+}
+
+int shpc_get_ctlr_slot_config(struct controller *ctrl,
+ int *num_ctlr_slots, /* number of slots in this HPC */
+ int *first_device_num, /* PCI dev num of the first slot in this SHPC */
+ int *physical_slot_num, /* phy slot num of the first slot in this SHPC */
+ int *updown, /* physical_slot_num increament: 1 or -1 */
+ int *flags)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle;
+
+ DBG_ENTER_ROUTINE
+
+ if (!ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ *first_device_num = php_ctlr->slot_device_offset; /* Obtained in shpc_init() */
+ *num_ctlr_slots = php_ctlr->num_slots; /* Obtained in shpc_init() */
+
+ *physical_slot_num = (readl(php_ctlr->creg + SLOT_CONFIG) & PSN) >> 16;
+ dbg("%s: physical_slot_num = %x\n", __FUNCTION__, *physical_slot_num);
+ *updown = ((readl(php_ctlr->creg + SLOT_CONFIG) & UPDOWN ) >> 29) ? 1 : -1;
+
+ DBG_LEAVE_ROUTINE
+ return 0;
+}
+
+static void hpc_release_ctlr(struct controller *ctrl)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle;
+ struct php_ctlr_state_s *p, *p_prev;
+
+ DBG_ENTER_ROUTINE
+
+ if (!ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return ;
+ }
+
+ if (shpchp_poll_mode) {
+ del_timer(&php_ctlr->int_poll_timer);
+ } else {
+ if (php_ctlr->irq) {
+ free_irq(php_ctlr->irq, ctrl);
+ php_ctlr->irq = 0;
+ pci_disable_msi(php_ctlr->pci_dev);
+ }
+ }
+ if (php_ctlr->pci_dev) {
+ dbg("%s: before calling iounmap & release_mem_region\n", __FUNCTION__);
+ iounmap(php_ctlr->creg);
+ release_mem_region(pci_resource_start(php_ctlr->pci_dev, 0), pci_resource_len(php_ctlr->pci_dev, 0));
+ dbg("%s: before calling iounmap & release_mem_region\n", __FUNCTION__);
+ php_ctlr->pci_dev = NULL;
+ }
+
+ spin_lock(&list_lock);
+ p = php_ctlr_list_head;
+ p_prev = NULL;
+ while (p) {
+ if (p == php_ctlr) {
+ if (p_prev)
+ p_prev->pnext = p->pnext;
+ else
+ php_ctlr_list_head = p->pnext;
+ break;
+ } else {
+ p_prev = p;
+ p = p->pnext;
+ }
+ }
+ spin_unlock(&list_lock);
+
+ kfree(php_ctlr);
+
+DBG_LEAVE_ROUTINE
+
+}
+
+static int hpc_power_on_slot(struct slot * slot)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u8 slot_cmd;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+ slot_cmd = 0x01;
+
+ retval = shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
+
+ if (retval) {
+ err("%s: Write command failed!\n", __FUNCTION__);
+ return -1;
+ }
+
+ DBG_LEAVE_ROUTINE
+
+ return retval;
+}
+
+static int hpc_slot_enable(struct slot * slot)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u8 slot_cmd;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+ /* 3A => Slot - Enable, Power Indicator - Blink, Attention Indicator - Off */
+ slot_cmd = 0x3A;
+
+ retval = shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
+
+ if (retval) {
+ err("%s: Write command failed!\n", __FUNCTION__);
+ return -1;
+ }
+
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static int hpc_slot_disable(struct slot * slot)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ u8 slot_cmd;
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+
+ /* 1F => Slot - Disable, Power Indicator - Off, Attention Indicator - On */
+ slot_cmd = 0x1F;
+
+ retval = shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
+
+ if (retval) {
+ err("%s: Write command failed!\n", __FUNCTION__);
+ return -1;
+ }
+
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static int hpc_enable_all_slots( struct slot *slot )
+{
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ retval = shpc_write_cmd(slot, 0, SET_ENABLE_ALL);
+ if (retval) {
+ err("%s: Write command failed!\n", __FUNCTION__);
+ return -1;
+ }
+
+ DBG_LEAVE_ROUTINE
+
+ return retval;
+}
+
+static int hpc_pwr_on_all_slots(struct slot *slot)
+{
+ int retval = 0;
+
+ DBG_ENTER_ROUTINE
+
+ retval = shpc_write_cmd(slot, 0, SET_PWR_ON_ALL);
+
+ if (retval) {
+ err("%s: Write command failed!\n", __FUNCTION__);
+ return -1;
+ }
+
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static int hpc_set_bus_speed_mode(struct slot * slot, enum pci_bus_speed value)
+{
+ u8 slot_cmd;
+ u8 pi;
+ int retval = 0;
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ pi = readb(php_ctlr->creg + PROG_INTERFACE);
+
+ if (pi == 1) {
+ switch (value) {
+ case 0:
+ slot_cmd = SETA_PCI_33MHZ;
+ break;
+ case 1:
+ slot_cmd = SETA_PCI_66MHZ;
+ break;
+ case 2:
+ slot_cmd = SETA_PCIX_66MHZ;
+ break;
+ case 3:
+ slot_cmd = SETA_PCIX_100MHZ;
+ break;
+ case 4:
+ slot_cmd = SETA_PCIX_133MHZ;
+ break;
+ default:
+ slot_cmd = PCI_SPEED_UNKNOWN;
+ retval = -ENODEV;
+ return retval;
+ }
+ } else {
+ switch (value) {
+ case 0:
+ slot_cmd = SETB_PCI_33MHZ;
+ break;
+ case 1:
+ slot_cmd = SETB_PCI_66MHZ;
+ break;
+ case 2:
+ slot_cmd = SETB_PCIX_66MHZ_PM;
+ break;
+ case 3:
+ slot_cmd = SETB_PCIX_100MHZ_PM;
+ break;
+ case 4:
+ slot_cmd = SETB_PCIX_133MHZ_PM;
+ break;
+ case 5:
+ slot_cmd = SETB_PCIX_66MHZ_EM;
+ break;
+ case 6:
+ slot_cmd = SETB_PCIX_100MHZ_EM;
+ break;
+ case 7:
+ slot_cmd = SETB_PCIX_133MHZ_EM;
+ break;
+ case 8:
+ slot_cmd = SETB_PCIX_66MHZ_266;
+ break;
+ case 0x9:
+ slot_cmd = SETB_PCIX_100MHZ_266;
+ break;
+ case 0xa:
+ slot_cmd = SETB_PCIX_133MHZ_266;
+ break;
+ case 0xb:
+ slot_cmd = SETB_PCIX_66MHZ_533;
+ break;
+ case 0xc:
+ slot_cmd = SETB_PCIX_100MHZ_533;
+ break;
+ case 0xd:
+ slot_cmd = SETB_PCIX_133MHZ_533;
+ break;
+ default:
+ slot_cmd = PCI_SPEED_UNKNOWN;
+ retval = -ENODEV;
+ return retval;
+ }
+
+ }
+ retval = shpc_write_cmd(slot, 0, slot_cmd);
+ if (retval) {
+ err("%s: Write command failed!\n", __FUNCTION__);
+ return -1;
+ }
+
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static irqreturn_t shpc_isr(int IRQ, void *dev_id, struct pt_regs *regs)
+{
+ struct controller *ctrl = NULL;
+ struct php_ctlr_state_s *php_ctlr;
+ u8 schedule_flag = 0;
+ u8 temp_byte;
+ u32 temp_dword, intr_loc, intr_loc2;
+ int hp_slot;
+
+ if (!dev_id)
+ return IRQ_NONE;
+
+ if (!shpchp_poll_mode) {
+ ctrl = (struct controller *)dev_id;
+ php_ctlr = ctrl->hpc_ctlr_handle;
+ } else {
+ php_ctlr = (struct php_ctlr_state_s *) dev_id;
+ ctrl = (struct controller *)php_ctlr->callback_instance_id;
+ }
+
+ if (!ctrl)
+ return IRQ_NONE;
+
+ if (!php_ctlr || !php_ctlr->creg)
+ return IRQ_NONE;
+
+ /* Check to see if it was our interrupt */
+ intr_loc = readl(php_ctlr->creg + INTR_LOC);
+
+ if (!intr_loc)
+ return IRQ_NONE;
+ dbg("%s: shpc_isr proceeds\n", __FUNCTION__);
+ dbg("%s: intr_loc = %x\n",__FUNCTION__, intr_loc);
+
+ if(!shpchp_poll_mode) {
+ /* Mask Global Interrupt Mask - see implementation note on p. 139 */
+ /* of SHPC spec rev 1.0*/
+ temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
+ dbg("%s: Before masking global interrupt, temp_dword = %x\n",
+ __FUNCTION__, temp_dword);
+ temp_dword |= 0x00000001;
+ dbg("%s: After masking global interrupt, temp_dword = %x\n",
+ __FUNCTION__, temp_dword);
+ writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE);
+
+ intr_loc2 = readl(php_ctlr->creg + INTR_LOC);
+ dbg("%s: intr_loc2 = %x\n",__FUNCTION__, intr_loc2);
+ }
+
+ if (intr_loc & 0x0001) {
+ /*
+ * Command Complete Interrupt Pending
+ * RO only - clear by writing 0 to the Command Completion
+ * Detect bit in Controller SERR-INT register
+ */
+ temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
+ dbg("%s: Before clearing CCIP, temp_dword = %x\n",
+ __FUNCTION__, temp_dword);
+ temp_dword &= 0xfffeffff;
+ dbg("%s: After clearing CCIP, temp_dword = %x\n",
+ __FUNCTION__, temp_dword);
+ writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE);
+ wake_up_interruptible(&ctrl->queue);
+ }
+
+ if ((intr_loc = (intr_loc >> 1)) == 0) {
+ /* Unmask Global Interrupt Mask */
+ temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
+ dbg("%s: 1-Before unmasking global interrupt, temp_dword = %x\n",
+ __FUNCTION__, temp_dword);
+ temp_dword &= 0xfffffffe;
+ dbg("%s: 1-After unmasking global interrupt, temp_dword = %x\n",
+ __FUNCTION__, temp_dword);
+ writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE);
+
+ return IRQ_NONE;
+ }
+
+ for (hp_slot = 0; hp_slot < ctrl->num_slots; hp_slot++) {
+ /* To find out which slot has interrupt pending */
+ if ((intr_loc >> hp_slot) & 0x01) {
+ temp_dword = readl(php_ctlr->creg + SLOT1 + (4*hp_slot));
+ dbg("%s: Slot %x with intr, temp_dword = %x\n",
+ __FUNCTION__, hp_slot, temp_dword);
+ temp_byte = (temp_dword >> 16) & 0xFF;
+ dbg("%s: Slot with intr, temp_byte = %x\n",
+ __FUNCTION__, temp_byte);
+ if ((php_ctlr->switch_change_callback) && (temp_byte & 0x08))
+ schedule_flag += php_ctlr->switch_change_callback(
+ hp_slot, php_ctlr->callback_instance_id);
+ if ((php_ctlr->attention_button_callback) && (temp_byte & 0x04))
+ schedule_flag += php_ctlr->attention_button_callback(
+ hp_slot, php_ctlr->callback_instance_id);
+ if ((php_ctlr->presence_change_callback) && (temp_byte & 0x01))
+ schedule_flag += php_ctlr->presence_change_callback(
+ hp_slot , php_ctlr->callback_instance_id);
+ if ((php_ctlr->power_fault_callback) && (temp_byte & 0x12))
+ schedule_flag += php_ctlr->power_fault_callback(
+ hp_slot, php_ctlr->callback_instance_id);
+
+ /* Clear all slot events */
+ temp_dword = 0xe01f3fff;
+ dbg("%s: Clearing slot events, temp_dword = %x\n",
+ __FUNCTION__, temp_dword);
+ writel(temp_dword, php_ctlr->creg + SLOT1 + (4*hp_slot));
+
+ intr_loc2 = readl(php_ctlr->creg + INTR_LOC);
+ dbg("%s: intr_loc2 = %x\n",__FUNCTION__, intr_loc2);
+ }
+ }
+ if (!shpchp_poll_mode) {
+ /* Unmask Global Interrupt Mask */
+ temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
+ dbg("%s: 2-Before unmasking global interrupt, temp_dword = %x\n",
+ __FUNCTION__, temp_dword);
+ temp_dword &= 0xfffffffe;
+ dbg("%s: 2-After unmasking global interrupt, temp_dword = %x\n",
+ __FUNCTION__, temp_dword);
+ writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int hpc_get_max_bus_speed (struct slot *slot, enum pci_bus_speed *value)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
+ int retval = 0;
+ u8 pi;
+ u32 slot_avail1, slot_avail2;
+ int slot_num;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+
+ pi = readb(php_ctlr->creg + PROG_INTERFACE);
+ slot_avail1 = readl(php_ctlr->creg + SLOT_AVAIL1);
+ slot_avail2 = readl(php_ctlr->creg + SLOT_AVAIL2);
+
+ if (pi == 2) {
+ if ((slot_num = ((slot_avail2 & SLOT_133MHZ_PCIX_533) >> 27) ) != 0 )
+ bus_speed = PCIX_133MHZ_533;
+ else if ((slot_num = ((slot_avail2 & SLOT_100MHZ_PCIX_533) >> 23) ) != 0 )
+ bus_speed = PCIX_100MHZ_533;
+ else if ((slot_num = ((slot_avail2 & SLOT_66MHZ_PCIX_533) >> 19) ) != 0 )
+ bus_speed = PCIX_66MHZ_533;
+ else if ((slot_num = ((slot_avail2 & SLOT_133MHZ_PCIX_266) >> 15) ) != 0 )
+ bus_speed = PCIX_133MHZ_266;
+ else if ((slot_num = ((slot_avail2 & SLOT_100MHZ_PCIX_266) >> 11) ) != 0 )
+ bus_speed = PCIX_100MHZ_266;
+ else if ((slot_num = ((slot_avail2 & SLOT_66MHZ_PCIX_266) >> 7) ) != 0 )
+ bus_speed = PCIX_66MHZ_266;
+ else if ((slot_num = ((slot_avail1 & SLOT_133MHZ_PCIX) >> 23) ) != 0 )
+ bus_speed = PCIX_133MHZ;
+ else if ((slot_num = ((slot_avail1 & SLOT_100MHZ_PCIX) >> 15) ) != 0 )
+ bus_speed = PCIX_100MHZ;
+ else if ((slot_num = ((slot_avail1 & SLOT_66MHZ_PCIX) >> 7) ) != 0 )
+ bus_speed = PCIX_66MHZ;
+ else if ((slot_num = (slot_avail2 & SLOT_66MHZ)) != 0 )
+ bus_speed = PCI_66MHZ;
+ else if ((slot_num = (slot_avail1 & SLOT_33MHZ)) != 0 )
+ bus_speed = PCI_33MHZ;
+ else bus_speed = PCI_SPEED_UNKNOWN;
+ } else {
+ if ((slot_num = ((slot_avail1 & SLOT_133MHZ_PCIX) >> 23) ) != 0 )
+ bus_speed = PCIX_133MHZ;
+ else if ((slot_num = ((slot_avail1 & SLOT_100MHZ_PCIX) >> 15) ) != 0 )
+ bus_speed = PCIX_100MHZ;
+ else if ((slot_num = ((slot_avail1 & SLOT_66MHZ_PCIX) >> 7) ) != 0 )
+ bus_speed = PCIX_66MHZ;
+ else if ((slot_num = (slot_avail2 & SLOT_66MHZ)) != 0 )
+ bus_speed = PCI_66MHZ;
+ else if ((slot_num = (slot_avail1 & SLOT_33MHZ)) != 0 )
+ bus_speed = PCI_33MHZ;
+ else bus_speed = PCI_SPEED_UNKNOWN;
+ }
+
+ *value = bus_speed;
+ dbg("Max bus speed = %d\n", bus_speed);
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static int hpc_get_cur_bus_speed (struct slot *slot, enum pci_bus_speed *value)
+{
+ struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
+ enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
+ u16 sec_bus_status;
+ int retval = 0;
+ u8 pi;
+
+ DBG_ENTER_ROUTINE
+
+ if (!slot->ctrl->hpc_ctlr_handle) {
+ err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (slot->hp_slot >= php_ctlr->num_slots) {
+ err("%s: Invalid HPC slot number!\n", __FUNCTION__);
+ return -1;
+ }
+
+ pi = readb(php_ctlr->creg + PROG_INTERFACE);
+ sec_bus_status = readw(php_ctlr->creg + SEC_BUS_CONFIG);
+
+ if (pi == 2) {
+ switch (sec_bus_status & 0x000f) {
+ case 0:
+ bus_speed = PCI_SPEED_33MHz;
+ break;
+ case 1:
+ bus_speed = PCI_SPEED_66MHz;
+ break;
+ case 2:
+ bus_speed = PCI_SPEED_66MHz_PCIX;
+ break;
+ case 3:
+ bus_speed = PCI_SPEED_100MHz_PCIX;
+ break;
+ case 4:
+ bus_speed = PCI_SPEED_133MHz_PCIX;
+ break;
+ case 5:
+ bus_speed = PCI_SPEED_66MHz_PCIX_ECC;
+ break;
+ case 6:
+ bus_speed = PCI_SPEED_100MHz_PCIX_ECC;
+ break;
+ case 7:
+ bus_speed = PCI_SPEED_133MHz_PCIX_ECC;
+ break;
+ case 8:
+ bus_speed = PCI_SPEED_66MHz_PCIX_266;
+ break;
+ case 9:
+ bus_speed = PCI_SPEED_100MHz_PCIX_266;
+ break;
+ case 0xa:
+ bus_speed = PCI_SPEED_133MHz_PCIX_266;
+ break;
+ case 0xb:
+ bus_speed = PCI_SPEED_66MHz_PCIX_533;
+ break;
+ case 0xc:
+ bus_speed = PCI_SPEED_100MHz_PCIX_533;
+ break;
+ case 0xd:
+ bus_speed = PCI_SPEED_133MHz_PCIX_533;
+ break;
+ case 0xe:
+ case 0xf:
+ default:
+ bus_speed = PCI_SPEED_UNKNOWN;
+ break;
+ }
+ } else {
+ /* In the case where pi is undefined, default it to 1 */
+ switch (sec_bus_status & 0x0007) {
+ case 0:
+ bus_speed = PCI_SPEED_33MHz;
+ break;
+ case 1:
+ bus_speed = PCI_SPEED_66MHz;
+ break;
+ case 2:
+ bus_speed = PCI_SPEED_66MHz_PCIX;
+ break;
+ case 3:
+ bus_speed = PCI_SPEED_100MHz_PCIX;
+ break;
+ case 4:
+ bus_speed = PCI_SPEED_133MHz_PCIX;
+ break;
+ case 5:
+ bus_speed = PCI_SPEED_UNKNOWN; /* Reserved */
+ break;
+ case 6:
+ bus_speed = PCI_SPEED_UNKNOWN; /* Reserved */
+ break;
+ case 7:
+ bus_speed = PCI_SPEED_UNKNOWN; /* Reserved */
+ break;
+ default:
+ bus_speed = PCI_SPEED_UNKNOWN;
+ break;
+ }
+ }
+
+ *value = bus_speed;
+ dbg("Current bus speed = %d\n", bus_speed);
+ DBG_LEAVE_ROUTINE
+ return retval;
+}
+
+static struct hpc_ops shpchp_hpc_ops = {
+ .power_on_slot = hpc_power_on_slot,
+ .slot_enable = hpc_slot_enable,
+ .slot_disable = hpc_slot_disable,
+ .enable_all_slots = hpc_enable_all_slots,
+ .pwr_on_all_slots = hpc_pwr_on_all_slots,
+ .set_bus_speed_mode = hpc_set_bus_speed_mode,
+ .set_attention_status = hpc_set_attention_status,
+ .get_power_status = hpc_get_power_status,
+ .get_attention_status = hpc_get_attention_status,
+ .get_latch_status = hpc_get_latch_status,
+ .get_adapter_status = hpc_get_adapter_status,
+
+ .get_max_bus_speed = hpc_get_max_bus_speed,
+ .get_cur_bus_speed = hpc_get_cur_bus_speed,
+ .get_adapter_speed = hpc_get_adapter_speed,
+ .get_mode1_ECC_cap = hpc_get_mode1_ECC_cap,
+ .get_prog_int = hpc_get_prog_int,
+
+ .query_power_fault = hpc_query_power_fault,
+ .green_led_on = hpc_set_green_led_on,
+ .green_led_off = hpc_set_green_led_off,
+ .green_led_blink = hpc_set_green_led_blink,
+
+ .release_ctlr = hpc_release_ctlr,
+ .check_cmd_status = hpc_check_cmd_status,
+};
+
+int shpc_init(struct controller * ctrl,
+ struct pci_dev * pdev,
+ php_intr_callback_t attention_button_callback,
+ php_intr_callback_t switch_change_callback,
+ php_intr_callback_t presence_change_callback,
+ php_intr_callback_t power_fault_callback)
+{
+ struct php_ctlr_state_s *php_ctlr, *p;
+ void *instance_id = ctrl;
+ int rc;
+ u8 hp_slot;
+ static int first = 1;
+ u32 shpc_cap_offset, shpc_base_offset;
+ u32 tempdword, slot_reg;
+ u16 vendor_id, device_id;
+ u8 i;
+
+ DBG_ENTER_ROUTINE
+
+ spin_lock_init(&list_lock);
+ php_ctlr = (struct php_ctlr_state_s *) kmalloc(sizeof(struct php_ctlr_state_s), GFP_KERNEL);
+
+ if (!php_ctlr) { /* allocate controller state data */
+ err("%s: HPC controller memory allocation error!\n", __FUNCTION__);
+ goto abort;
+ }
+
+ memset(php_ctlr, 0, sizeof(struct php_ctlr_state_s));
+
+ php_ctlr->pci_dev = pdev; /* save pci_dev in context */
+
+ rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id);
+ dbg("%s: Vendor ID: %x\n",__FUNCTION__, vendor_id);
+ if (rc) {
+ err("%s: unable to read PCI configuration data\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+
+ rc = pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id);
+ dbg("%s: Device ID: %x\n",__FUNCTION__, device_id);
+ if (rc) {
+ err("%s: unable to read PCI configuration data\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+
+ if ((vendor_id == PCI_VENDOR_ID_AMD) || (device_id == PCI_DEVICE_ID_AMD_GOLAM_7450)) {
+ shpc_base_offset = 0; /* amd shpc driver doesn't use this; assume 0 */
+ } else {
+ if ((shpc_cap_offset = pci_find_capability(pdev, PCI_CAP_ID_SHPC)) == 0) {
+ err("%s : shpc_cap_offset == 0\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ dbg("%s: shpc_cap_offset = %x\n", __FUNCTION__, shpc_cap_offset);
+
+ rc = pci_write_config_byte(pdev, (u8)shpc_cap_offset + DWORD_SELECT , BASE_OFFSET);
+ if (rc) {
+ err("%s : pci_word_config_byte failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+
+ rc = pci_read_config_dword(pdev, (u8)shpc_cap_offset + DWORD_DATA, &shpc_base_offset);
+ if (rc) {
+ err("%s : pci_read_config_dword failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+
+ for (i = 0; i <= 14; i++) {
+ rc = pci_write_config_byte(pdev, (u8)shpc_cap_offset + DWORD_SELECT , i);
+ if (rc) {
+ err("%s : pci_word_config_byte failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+
+ rc = pci_read_config_dword(pdev, (u8)shpc_cap_offset + DWORD_DATA, &tempdword);
+ if (rc) {
+ err("%s : pci_read_config_dword failed\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+ dbg("%s: offset %d: tempdword %x\n", __FUNCTION__,i, tempdword);
+ }
+ }
+
+ if (first) {
+ spin_lock_init(&hpc_event_lock);
+ first = 0;
+ }
+
+ dbg("pdev = %p: b:d:f:irq=0x%x:%x:%x:%x\n", pdev, pdev->bus->number, PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn), pdev->irq);
+ for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++)
+ if (pci_resource_len(pdev, rc) > 0)
+ dbg("pci resource[%d] start=0x%lx(len=0x%lx), shpc_base_offset %x\n", rc,
+ pci_resource_start(pdev, rc), pci_resource_len(pdev, rc), shpc_base_offset);
+
+ info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", pdev->vendor, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
+
+ if (pci_enable_device(pdev))
+ goto abort_free_ctlr;
+
+ if (!request_mem_region(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0), MY_NAME)) {
+ err("%s: cannot reserve MMIO region\n", __FUNCTION__);
+ goto abort_free_ctlr;
+ }
+
+ php_ctlr->creg = ioremap(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0));
+ if (!php_ctlr->creg) {
+ err("%s: cannot remap MMIO region %lx @ %lx\n", __FUNCTION__, pci_resource_len(pdev, 0),
+ pci_resource_start(pdev, 0) + shpc_base_offset);
+ release_mem_region(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0));
+ goto abort_free_ctlr;
+ }
+ dbg("%s: php_ctlr->creg %p\n", __FUNCTION__, php_ctlr->creg);
+ dbg("%s: physical addr %p\n", __FUNCTION__, (void*)pci_resource_start(pdev, 0));
+
+ init_MUTEX(&ctrl->crit_sect);
+ /* Setup wait queue */
+ init_waitqueue_head(&ctrl->queue);
+
+ /* Find the IRQ */
+ php_ctlr->irq = pdev->irq;
+ dbg("HPC interrupt = %d\n", php_ctlr->irq);
+
+ /* Save interrupt callback info */
+ php_ctlr->attention_button_callback = attention_button_callback;
+ php_ctlr->switch_change_callback = switch_change_callback;
+ php_ctlr->presence_change_callback = presence_change_callback;
+ php_ctlr->power_fault_callback = power_fault_callback;
+ php_ctlr->callback_instance_id = instance_id;
+
+ /* Return PCI Controller Info */
+ php_ctlr->slot_device_offset = (readl(php_ctlr->creg + SLOT_CONFIG) & FIRST_DEV_NUM ) >> 8;
+ php_ctlr->num_slots = readl(php_ctlr->creg + SLOT_CONFIG) & SLOT_NUM;
+ dbg("%s: slot_device_offset %x\n", __FUNCTION__, php_ctlr->slot_device_offset);
+ dbg("%s: num_slots %x\n", __FUNCTION__, php_ctlr->num_slots);
+
+ /* Mask Global Interrupt Mask & Command Complete Interrupt Mask */
+ tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
+ dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword);
+ tempdword = 0x0003000f;
+ writel(tempdword, php_ctlr->creg + SERR_INTR_ENABLE);
+ tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
+ dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword);
+
+ /* Mask the MRL sensor SERR Mask of individual slot in
+ * Slot SERR-INT Mask & clear all the existing event if any
+ */
+ for (hp_slot = 0; hp_slot < php_ctlr->num_slots; hp_slot++) {
+ slot_reg = readl(php_ctlr->creg + SLOT1 + 4*hp_slot );
+ dbg("%s: Default Logical Slot Register %d value %x\n", __FUNCTION__,
+ hp_slot, slot_reg);
+ tempdword = 0xffff3fff;
+ writel(tempdword, php_ctlr->creg + SLOT1 + (4*hp_slot));
+ }
+
+ if (shpchp_poll_mode) {/* Install interrupt polling code */
+ /* Install and start the interrupt polling timer */
+ init_timer(&php_ctlr->int_poll_timer);
+ start_int_poll_timer( php_ctlr, 10 ); /* start with 10 second delay */
+ } else {
+ /* Installs the interrupt handler */
+ rc = pci_enable_msi(pdev);
+ if (rc) {
+ info("Can't get msi for the hotplug controller\n");
+ info("Use INTx for the hotplug controller\n");
+ dbg("%s: rc = %x\n", __FUNCTION__, rc);
+ } else
+ php_ctlr->irq = pdev->irq;
+
+ rc = request_irq(php_ctlr->irq, shpc_isr, SA_SHIRQ, MY_NAME, (void *) ctrl);
+ dbg("%s: request_irq %d for hpc%d (returns %d)\n", __FUNCTION__, php_ctlr->irq, ctlr_seq_num, rc);
+ if (rc) {
+ err("Can't get irq %d for the hotplug controller\n", php_ctlr->irq);
+ goto abort_free_ctlr;
+ }
+ /* Execute OSHP method here */
+ }
+ dbg("%s: Before adding HPC to HPC list\n", __FUNCTION__);
+
+ /* Add this HPC instance into the HPC list */
+ spin_lock(&list_lock);
+ if (php_ctlr_list_head == 0) {
+ php_ctlr_list_head = php_ctlr;
+ p = php_ctlr_list_head;
+ p->pnext = NULL;
+ } else {
+ p = php_ctlr_list_head;
+
+ while (p->pnext)
+ p = p->pnext;
+
+ p->pnext = php_ctlr;
+ }
+ spin_unlock(&list_lock);
+
+
+ ctlr_seq_num++;
+ ctrl->hpc_ctlr_handle = php_ctlr;
+ ctrl->hpc_ops = &shpchp_hpc_ops;
+
+ for (hp_slot = 0; hp_slot < php_ctlr->num_slots; hp_slot++) {
+ slot_reg = readl(php_ctlr->creg + SLOT1 + 4*hp_slot );
+ dbg("%s: Default Logical Slot Register %d value %x\n", __FUNCTION__,
+ hp_slot, slot_reg);
+ tempdword = 0xe01f3fff;
+ writel(tempdword, php_ctlr->creg + SLOT1 + (4*hp_slot));
+ }
+ if (!shpchp_poll_mode) {
+ /* Unmask all general input interrupts and SERR */
+ tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
+ tempdword = 0x0000000a;
+ writel(tempdword, php_ctlr->creg + SERR_INTR_ENABLE);
+ tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
+ dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword);
+ }
+
+ dbg("%s: Leaving shpc_init\n", __FUNCTION__);
+ DBG_LEAVE_ROUTINE
+ return 0;
+
+ /* We end up here for the many possible ways to fail this API. */
+abort_free_ctlr:
+ kfree(php_ctlr);
+abort:
+ DBG_LEAVE_ROUTINE
+ return -1;
+}
diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c
new file mode 100644
index 000000000000..90113e9cd69b
--- /dev/null
+++ b/drivers/pci/hotplug/shpchp_pci.c
@@ -0,0 +1,810 @@
+/*
+ * Standard Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include "../pci.h"
+#include "shpchp.h"
+#ifndef CONFIG_IA64
+#include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependant we are... */
+#endif
+
+int shpchp_configure_device (struct controller* ctrl, struct pci_func* func)
+{
+ unsigned char bus;
+ struct pci_bus *child;
+ int num;
+
+ if (func->pci_dev == NULL)
+ func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
+
+ /* Still NULL ? Well then scan for it ! */
+ if (func->pci_dev == NULL) {
+ num = pci_scan_slot(ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function));
+ if (num) {
+ dbg("%s: subordiante %p number %x\n", __FUNCTION__, ctrl->pci_dev->subordinate,
+ ctrl->pci_dev->subordinate->number);
+ pci_bus_add_devices(ctrl->pci_dev->subordinate);
+ }
+
+ func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
+ if (func->pci_dev == NULL) {
+ dbg("ERROR: pci_dev still null\n");
+ return 0;
+ }
+ }
+
+ if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus);
+ child = pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus);
+ pci_do_scan_bus(child);
+
+ }
+
+ return 0;
+}
+
+
+int shpchp_unconfigure_device(struct pci_func* func)
+{
+ int rc = 0;
+ int j;
+
+ dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus,
+ func->device, func->function);
+
+ for (j=0; j<8 ; j++) {
+ struct pci_dev* temp = pci_find_slot(func->bus,
+ (func->device << 3) | j);
+ if (temp) {
+ pci_remove_bus_device(temp);
+ }
+ }
+ return rc;
+}
+
+/*
+ * shpchp_set_irq
+ *
+ * @bus_num: bus number of PCI device
+ * @dev_num: device number of PCI device
+ * @slot: pointer to u8 where slot number will be returned
+ */
+int shpchp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num)
+{
+#if defined(CONFIG_X86) && !defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_64)
+ int rc;
+ u16 temp_word;
+ struct pci_dev fakedev;
+ struct pci_bus fakebus;
+
+ fakedev.devfn = dev_num << 3;
+ fakedev.bus = &fakebus;
+ fakebus.number = bus_num;
+ dbg("%s: dev %d, bus %d, pin %d, num %d\n",
+ __FUNCTION__, dev_num, bus_num, int_pin, irq_num);
+ rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num);
+ dbg("%s: rc %d\n", __FUNCTION__, rc);
+ if (!rc)
+ return !rc;
+
+ /* set the Edge Level Control Register (ELCR) */
+ temp_word = inb(0x4d0);
+ temp_word |= inb(0x4d1) << 8;
+
+ temp_word |= 0x01 << irq_num;
+
+ /* This should only be for x86 as it sets the Edge Level Control Register */
+ outb((u8) (temp_word & 0xFF), 0x4d0);
+ outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1);
+#endif
+ return 0;
+}
+
+/* More PCI configuration routines; this time centered around hotplug controller */
+
+
+/*
+ * shpchp_save_config
+ *
+ * Reads configuration for all slots in a PCI bus and saves info.
+ *
+ * Note: For non-hot plug busses, the slot # saved is the device #
+ *
+ * returns 0 if success
+ */
+int shpchp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num)
+{
+ int rc;
+ u8 class_code;
+ u8 header_type;
+ u32 ID;
+ u8 secondary_bus;
+ struct pci_func *new_slot;
+ int sub_bus;
+ int FirstSupported;
+ int LastSupported;
+ int max_functions;
+ int function;
+ u8 DevError;
+ int device = 0;
+ int cloop = 0;
+ int stop_it;
+ int index;
+ int is_hot_plug = num_ctlr_slots || first_device_num;
+ struct pci_bus lpci_bus, *pci_bus;
+
+ dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__,
+ num_ctlr_slots, first_device_num);
+
+ memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+
+ dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__,
+ num_ctlr_slots, first_device_num);
+
+ /* Decide which slots are supported */
+ if (is_hot_plug) {
+ /*********************************
+ * is_hot_plug is the slot mask
+ *********************************/
+ FirstSupported = first_device_num;
+ LastSupported = FirstSupported + num_ctlr_slots - 1;
+ } else {
+ FirstSupported = 0;
+ LastSupported = 0x1F;
+ }
+
+ dbg("FirstSupported = %d, LastSupported = %d\n", FirstSupported,
+ LastSupported);
+
+ /* Save PCI configuration space for all devices in supported slots */
+ pci_bus->number = busnumber;
+ for (device = FirstSupported; device <= LastSupported; device++) {
+ ID = 0xFFFFFFFF;
+ rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0),
+ PCI_VENDOR_ID, &ID);
+
+ if (ID != 0xFFFFFFFF) { /* device in slot */
+ rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0),
+ 0x0B, &class_code);
+ if (rc)
+ return rc;
+
+ rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0),
+ PCI_HEADER_TYPE, &header_type);
+ if (rc)
+ return rc;
+
+ dbg("class_code = %x, header_type = %x\n", class_code, header_type);
+
+ /* If multi-function device, set max_functions to 8 */
+ if (header_type & 0x80)
+ max_functions = 8;
+ else
+ max_functions = 1;
+
+ function = 0;
+
+ do {
+ DevError = 0;
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* P-P Bridge */
+ /* Recurse the subordinate bus
+ * get the subordinate bus number
+ */
+ rc = pci_bus_read_config_byte(pci_bus,
+ PCI_DEVFN(device, function),
+ PCI_SECONDARY_BUS, &secondary_bus);
+ if (rc) {
+ return rc;
+ } else {
+ sub_bus = (int) secondary_bus;
+
+ /* Save secondary bus cfg spc with this recursive call. */
+ rc = shpchp_save_config(ctrl, sub_bus, 0, 0);
+ if (rc)
+ return rc;
+ }
+ }
+
+ index = 0;
+ new_slot = shpchp_slot_find(busnumber, device, index++);
+
+ dbg("new_slot = %p\n", new_slot);
+
+ while (new_slot && (new_slot->function != (u8) function)) {
+ new_slot = shpchp_slot_find(busnumber, device, index++);
+ dbg("new_slot = %p\n", new_slot);
+ }
+ if (!new_slot) {
+ /* Setup slot structure. */
+ new_slot = shpchp_slot_create(busnumber);
+ dbg("new_slot = %p\n", new_slot);
+
+ if (new_slot == NULL)
+ return(1);
+ }
+
+ new_slot->bus = (u8) busnumber;
+ new_slot->device = (u8) device;
+ new_slot->function = (u8) function;
+ new_slot->is_a_board = 1;
+ new_slot->switch_save = 0x10;
+ new_slot->pwr_save = 1;
+ /* In case of unsupported board */
+ new_slot->status = DevError;
+ new_slot->pci_dev = pci_find_slot(new_slot->bus,
+ (new_slot->device << 3) | new_slot->function);
+ dbg("new_slot->pci_dev = %p\n", new_slot->pci_dev);
+
+ for (cloop = 0; cloop < 0x20; cloop++) {
+ rc = pci_bus_read_config_dword(pci_bus,
+ PCI_DEVFN(device, function),
+ cloop << 2,
+ (u32 *) &(new_slot->config_space [cloop]));
+ /* dbg("new_slot->config_space[%x] = %x\n",
+ cloop, new_slot->config_space[cloop]); */
+ if (rc)
+ return rc;
+ }
+
+ function++;
+
+ stop_it = 0;
+
+ /* this loop skips to the next present function
+ * reading in Class Code and Header type.
+ */
+
+ while ((function < max_functions)&&(!stop_it)) {
+ rc = pci_bus_read_config_dword(pci_bus,
+ PCI_DEVFN(device, function),
+ PCI_VENDOR_ID, &ID);
+
+ if (ID == 0xFFFFFFFF) { /* nothing there. */
+ function++;
+ dbg("Nothing there\n");
+ } else { /* Something there */
+ rc = pci_bus_read_config_byte(pci_bus,
+ PCI_DEVFN(device, function),
+ 0x0B, &class_code);
+ if (rc)
+ return rc;
+
+ rc = pci_bus_read_config_byte(pci_bus,
+ PCI_DEVFN(device, function),
+ PCI_HEADER_TYPE, &header_type);
+ if (rc)
+ return rc;
+
+ dbg("class_code = %x, header_type = %x\n",
+ class_code, header_type);
+ stop_it++;
+ }
+ }
+
+ } while (function < max_functions);
+ /* End of IF (device in slot?) */
+ } else if (is_hot_plug) {
+ /* Setup slot structure with entry for empty slot */
+ new_slot = shpchp_slot_create(busnumber);
+
+ if (new_slot == NULL) {
+ return(1);
+ }
+ dbg("new_slot = %p\n", new_slot);
+
+ new_slot->bus = (u8) busnumber;
+ new_slot->device = (u8) device;
+ new_slot->function = 0;
+ new_slot->is_a_board = 0;
+ new_slot->presence_save = 0;
+ new_slot->switch_save = 0;
+ }
+ } /* End of FOR loop */
+
+ return(0);
+}
+
+
+/*
+ * shpchp_save_slot_config
+ *
+ * Saves configuration info for all PCI devices in a given slot
+ * including subordinate busses.
+ *
+ * returns 0 if success
+ */
+int shpchp_save_slot_config(struct controller *ctrl, struct pci_func * new_slot)
+{
+ int rc;
+ u8 class_code;
+ u8 header_type;
+ u32 ID;
+ u8 secondary_bus;
+ int sub_bus;
+ int max_functions;
+ int function;
+ int cloop = 0;
+ int stop_it;
+ struct pci_bus lpci_bus, *pci_bus;
+ memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = new_slot->bus;
+
+ ID = 0xFFFFFFFF;
+
+ pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, 0),
+ PCI_VENDOR_ID, &ID);
+
+ if (ID != 0xFFFFFFFF) { /* device in slot */
+ pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0),
+ 0x0B, &class_code);
+
+ pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0),
+ PCI_HEADER_TYPE, &header_type);
+
+ if (header_type & 0x80) /* Multi-function device */
+ max_functions = 8;
+ else
+ max_functions = 1;
+
+ function = 0;
+
+ do {
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
+ /* Recurse the subordinate bus */
+ pci_bus_read_config_byte(pci_bus,
+ PCI_DEVFN(new_slot->device, function),
+ PCI_SECONDARY_BUS, &secondary_bus);
+
+ sub_bus = (int) secondary_bus;
+
+ /* Save the config headers for the secondary bus. */
+ rc = shpchp_save_config(ctrl, sub_bus, 0, 0);
+
+ if (rc)
+ return rc;
+
+ } /* End of IF */
+
+ new_slot->status = 0;
+
+ for (cloop = 0; cloop < 0x20; cloop++) {
+ pci_bus_read_config_dword(pci_bus,
+ PCI_DEVFN(new_slot->device, function),
+ cloop << 2,
+ (u32 *) &(new_slot->config_space [cloop]));
+ }
+
+ function++;
+
+ stop_it = 0;
+
+ /* this loop skips to the next present function
+ * reading in the Class Code and the Header type.
+ */
+
+ while ((function < max_functions) && (!stop_it)) {
+ pci_bus_read_config_dword(pci_bus,
+ PCI_DEVFN(new_slot->device, function),
+ PCI_VENDOR_ID, &ID);
+
+ if (ID == 0xFFFFFFFF) { /* nothing there. */
+ function++;
+ } else { /* Something there */
+ pci_bus_read_config_byte(pci_bus,
+ PCI_DEVFN(new_slot->device, function),
+ 0x0B, &class_code);
+
+ pci_bus_read_config_byte(pci_bus,
+ PCI_DEVFN(new_slot->device, function),
+ PCI_HEADER_TYPE, &header_type);
+
+ stop_it++;
+ }
+ }
+
+ } while (function < max_functions);
+ } /* End of IF (device in slot?) */
+ else {
+ return 2;
+ }
+
+ return 0;
+}
+
+
+/*
+ * shpchp_save_used_resources
+ *
+ * Stores used resource information for existing boards. this is
+ * for boards that were in the system when this driver was loaded.
+ * this function is for hot plug ADD
+ *
+ * returns 0 if success
+ * if disable == 1(DISABLE_CARD),
+ * it loops for all functions of the slot and disables them.
+ * else, it just get resources of the function and return.
+ */
+int shpchp_save_used_resources(struct controller *ctrl, struct pci_func *func, int disable)
+{
+ u8 cloop;
+ u8 header_type;
+ u8 secondary_bus;
+ u8 temp_byte;
+ u16 command;
+ u16 save_command;
+ u16 w_base, w_length;
+ u32 temp_register;
+ u32 save_base;
+ u32 base, length;
+ u64 base64 = 0;
+ int index = 0;
+ unsigned int devfn;
+ struct pci_resource *mem_node = NULL;
+ struct pci_resource *p_mem_node = NULL;
+ struct pci_resource *t_mem_node;
+ struct pci_resource *io_node;
+ struct pci_resource *bus_node;
+ struct pci_bus lpci_bus, *pci_bus;
+ memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+
+ if (disable)
+ func = shpchp_slot_find(func->bus, func->device, index++);
+
+ while ((func != NULL) && func->is_a_board) {
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ /* Save the command register */
+ pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command);
+
+ if (disable) {
+ /* disable card */
+ command = 0x00;
+ pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
+ }
+
+ /* Check for Bridge */
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
+ dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n",
+ func->bus, func->device, save_command);
+ if (disable) {
+ /* Clear Bridge Control Register */
+ command = 0x00;
+ pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
+ }
+
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte);
+
+ bus_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+ if (!bus_node)
+ return -ENOMEM;
+
+ bus_node->base = (ulong)secondary_bus;
+ bus_node->length = (ulong)(temp_byte - secondary_bus + 1);
+
+ bus_node->next = func->bus_head;
+ func->bus_head = bus_node;
+
+ /* Save IO base and Limit registers */
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &temp_byte);
+ base = temp_byte;
+ pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &temp_byte);
+ length = temp_byte;
+
+ if ((base <= length) && (!disable || (save_command & PCI_COMMAND_IO))) {
+ io_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+ if (!io_node)
+ return -ENOMEM;
+
+ io_node->base = (ulong)(base & PCI_IO_RANGE_MASK) << 8;
+ io_node->length = (ulong)(length - base + 0x10) << 8;
+
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ }
+
+ /* Save memory base and Limit registers */
+ pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base);
+ pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length);
+
+ if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
+ mem_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+ if (!mem_node)
+ return -ENOMEM;
+
+ mem_node->base = (ulong)w_base << 16;
+ mem_node->length = (ulong)(w_length - w_base + 0x10) << 16;
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ }
+ /* Save prefetchable memory base and Limit registers */
+ pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base);
+ pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length);
+
+ if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
+ p_mem_node = kmalloc(sizeof(struct pci_resource),
+ GFP_KERNEL);
+ if (!p_mem_node)
+ return -ENOMEM;
+
+ p_mem_node->base = (ulong)w_base << 16;
+ p_mem_node->length = (ulong)(w_length - w_base + 0x10) << 16;
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ }
+ } else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
+ dbg("Save_used_res of PCI adapter b:d=0x%x:%x, sc=0x%x\n",
+ func->bus, func->device, save_command);
+
+ /* Figure out IO and memory base lengths */
+ for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) {
+ pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base);
+
+ temp_register = 0xFFFFFFFF;
+ pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register);
+ pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register);
+
+ if (!disable)
+ pci_bus_write_config_dword(pci_bus, devfn, cloop, save_base);
+
+ if (!temp_register)
+ continue;
+
+ base = temp_register;
+
+ if ((base & PCI_BASE_ADDRESS_SPACE_IO) &&
+ (!disable || (save_command & PCI_COMMAND_IO))) {
+ /* IO base */
+ /* set temp_register = amount of IO space requested */
+ base = base & 0xFFFFFFFCL;
+ base = (~base) + 1;
+
+ io_node = kmalloc(sizeof (struct pci_resource),
+ GFP_KERNEL);
+ if (!io_node)
+ return -ENOMEM;
+
+ io_node->base = (ulong)save_base & PCI_BASE_ADDRESS_IO_MASK;
+ io_node->length = (ulong)base;
+ dbg("sur adapter: IO bar=0x%x(length=0x%x)\n",
+ io_node->base, io_node->length);
+
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ } else { /* map Memory */
+ int prefetchable = 1;
+ /* struct pci_resources **res_node; */
+ char *res_type_str = "PMEM";
+ u32 temp_register2;
+
+ t_mem_node = kmalloc(sizeof (struct pci_resource),
+ GFP_KERNEL);
+ if (!t_mem_node)
+ return -ENOMEM;
+
+ if (!(base & PCI_BASE_ADDRESS_MEM_PREFETCH) &&
+ (!disable || (save_command & PCI_COMMAND_MEMORY))) {
+ prefetchable = 0;
+ mem_node = t_mem_node;
+ res_type_str++;
+ } else
+ p_mem_node = t_mem_node;
+
+ base = base & 0xFFFFFFF0L;
+ base = (~base) + 1;
+
+ switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
+ case PCI_BASE_ADDRESS_MEM_TYPE_32:
+ if (prefetchable) {
+ p_mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK;
+ p_mem_node->length = (ulong)base;
+ dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n",
+ res_type_str,
+ p_mem_node->base,
+ p_mem_node->length);
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ } else {
+ mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK;
+ mem_node->length = (ulong)base;
+ dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n",
+ res_type_str,
+ mem_node->base,
+ mem_node->length);
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ }
+ break;
+ case PCI_BASE_ADDRESS_MEM_TYPE_64:
+ pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2);
+ base64 = temp_register2;
+ base64 = (base64 << 32) | save_base;
+
+ if (temp_register2) {
+ dbg("sur adapter: 64 %s high dword of base64(0x%x:%x) masked to 0\n",
+ res_type_str, temp_register2, (u32)base64);
+ base64 &= 0x00000000FFFFFFFFL;
+ }
+
+ if (prefetchable) {
+ p_mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK;
+ p_mem_node->length = base;
+ dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n",
+ res_type_str,
+ p_mem_node->base,
+ p_mem_node->length);
+
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ } else {
+ mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK;
+ mem_node->length = base;
+ dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n",
+ res_type_str,
+ mem_node->base,
+ mem_node->length);
+
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ }
+ cloop += 4;
+ break;
+ default:
+ dbg("asur: reserved BAR type=0x%x\n",
+ temp_register);
+ break;
+ }
+ }
+ } /* End of base register loop */
+ } else { /* Some other unknown header type */
+ dbg("Save_used_res of PCI unknown type b:d=0x%x:%x. skip.\n",
+ func->bus, func->device);
+ }
+
+ /* find the next device in this slot */
+ if (!disable)
+ break;
+ func = shpchp_slot_find(func->bus, func->device, index++);
+ }
+
+ return 0;
+}
+
+/**
+ * kfree_resource_list: release memory of all list members
+ * @res: resource list to free
+ */
+static inline void
+return_resource_list(struct pci_resource **func, struct pci_resource **res)
+{
+ struct pci_resource *node;
+ struct pci_resource *t_node;
+
+ node = *func;
+ *func = NULL;
+ while (node) {
+ t_node = node->next;
+ return_resource(res, node);
+ node = t_node;
+ }
+}
+
+/*
+ * shpchp_return_board_resources
+ *
+ * this routine returns all resources allocated to a board to
+ * the available pool.
+ *
+ * returns 0 if success
+ */
+int shpchp_return_board_resources(struct pci_func * func,
+ struct resource_lists * resources)
+{
+ int rc;
+ dbg("%s\n", __FUNCTION__);
+
+ if (!func)
+ return 1;
+
+ return_resource_list(&(func->io_head),&(resources->io_head));
+ return_resource_list(&(func->mem_head),&(resources->mem_head));
+ return_resource_list(&(func->p_mem_head),&(resources->p_mem_head));
+ return_resource_list(&(func->bus_head),&(resources->bus_head));
+
+ rc = shpchp_resource_sort_and_combine(&(resources->mem_head));
+ rc |= shpchp_resource_sort_and_combine(&(resources->p_mem_head));
+ rc |= shpchp_resource_sort_and_combine(&(resources->io_head));
+ rc |= shpchp_resource_sort_and_combine(&(resources->bus_head));
+
+ return rc;
+}
+
+/**
+ * kfree_resource_list: release memory of all list members
+ * @res: resource list to free
+ */
+static inline void
+kfree_resource_list(struct pci_resource **r)
+{
+ struct pci_resource *res, *tres;
+
+ res = *r;
+ *r = NULL;
+
+ while (res) {
+ tres = res;
+ res = res->next;
+ kfree(tres);
+ }
+}
+
+/**
+ * shpchp_destroy_resource_list: put node back in the resource list
+ * @resources: list to put nodes back
+ */
+void shpchp_destroy_resource_list(struct resource_lists *resources)
+{
+ kfree_resource_list(&(resources->io_head));
+ kfree_resource_list(&(resources->mem_head));
+ kfree_resource_list(&(resources->p_mem_head));
+ kfree_resource_list(&(resources->bus_head));
+}
+
+/**
+ * shpchp_destroy_board_resources: put node back in the resource list
+ * @resources: list to put nodes back
+ */
+void shpchp_destroy_board_resources(struct pci_func * func)
+{
+ kfree_resource_list(&(func->io_head));
+ kfree_resource_list(&(func->mem_head));
+ kfree_resource_list(&(func->p_mem_head));
+ kfree_resource_list(&(func->bus_head));
+}
diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c
new file mode 100644
index 000000000000..9a1ee132d12c
--- /dev/null
+++ b/drivers/pci/hotplug/shpchp_sysfs.c
@@ -0,0 +1,143 @@
+/*
+ * Compaq Hot Plug Controller Driver
+ *
+ * Copyright (c) 1995,2001 Compaq Computer Corporation
+ * Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (c) 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 <greg@kroah.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include "shpchp.h"
+
+
+/* A few routines that create sysfs entries for the hot plug controller */
+
+static ssize_t show_ctrl (struct device *dev, char *buf)
+{
+ struct pci_dev *pci_dev;
+ struct controller *ctrl;
+ char * out = buf;
+ int index;
+ struct pci_resource *res;
+
+ pci_dev = container_of (dev, struct pci_dev, dev);
+ ctrl = pci_get_drvdata(pci_dev);
+
+ out += sprintf(buf, "Free resources: memory\n");
+ index = 11;
+ res = ctrl->mem_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ out += sprintf(out, "Free resources: prefetchable memory\n");
+ index = 11;
+ res = ctrl->p_mem_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ out += sprintf(out, "Free resources: IO\n");
+ index = 11;
+ res = ctrl->io_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ out += sprintf(out, "Free resources: bus numbers\n");
+ index = 11;
+ res = ctrl->bus_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+
+ return out - buf;
+}
+static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL);
+
+static ssize_t show_dev (struct device *dev, char *buf)
+{
+ struct pci_dev *pci_dev;
+ struct controller *ctrl;
+ char * out = buf;
+ int index;
+ struct pci_resource *res;
+ struct pci_func *new_slot;
+ struct slot *slot;
+
+ pci_dev = container_of (dev, struct pci_dev, dev);
+ ctrl = pci_get_drvdata(pci_dev);
+
+ slot=ctrl->slot;
+
+ while (slot) {
+ new_slot = shpchp_slot_find(slot->bus, slot->device, 0);
+ if (!new_slot)
+ break;
+ out += sprintf(out, "assigned resources: memory\n");
+ index = 11;
+ res = new_slot->mem_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ out += sprintf(out, "assigned resources: prefetchable memory\n");
+ index = 11;
+ res = new_slot->p_mem_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ out += sprintf(out, "assigned resources: IO\n");
+ index = 11;
+ res = new_slot->io_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ out += sprintf(out, "assigned resources: bus numbers\n");
+ index = 11;
+ res = new_slot->bus_head;
+ while (res && index--) {
+ out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
+ res = res->next;
+ }
+ slot=slot->next;
+ }
+
+ return out - buf;
+}
+static DEVICE_ATTR (dev, S_IRUGO, show_dev, NULL);
+
+void shpchp_create_ctrl_files (struct controller *ctrl)
+{
+ device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
+ device_create_file (&ctrl->pci_dev->dev, &dev_attr_dev);
+}
diff --git a/drivers/pci/hotplug/shpchprm.h b/drivers/pci/hotplug/shpchprm.h
new file mode 100644
index 000000000000..88aeb978c911
--- /dev/null
+++ b/drivers/pci/hotplug/shpchprm.h
@@ -0,0 +1,55 @@
+/*
+ * SHPCHPRM : SHPCHP Resource Manager for ACPI/non-ACPI platform
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+
+#ifndef _SHPCHPRM_H_
+#define _SHPCHPRM_H_
+
+#ifdef CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY
+#include "shpchprm_legacy.h"
+#else
+#include "shpchprm_nonacpi.h"
+#endif
+
+int shpchprm_init(enum php_ctlr_type ct);
+void shpchprm_cleanup(void);
+int shpchprm_print_pirt(void);
+int shpchprm_find_available_resources(struct controller *ctrl);
+int shpchprm_set_hpp(struct controller *ctrl, struct pci_func *func, u8 card_type);
+void shpchprm_enable_card(struct controller *ctrl, struct pci_func *func, u8 card_type);
+int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum);
+
+#ifdef DEBUG
+#define RES_CHECK(this, bits) \
+ { if (((this) & (bits - 1))) \
+ printk("%s:%d ERR: potential res loss!\n", __FUNCTION__, __LINE__); }
+#else
+#define RES_CHECK(this, bits)
+#endif
+
+#endif /* _SHPCHPRM_H_ */
diff --git a/drivers/pci/hotplug/shpchprm_acpi.c b/drivers/pci/hotplug/shpchprm_acpi.c
new file mode 100644
index 000000000000..243a51d88b86
--- /dev/null
+++ b/drivers/pci/hotplug/shpchprm_acpi.c
@@ -0,0 +1,1713 @@
+/*
+ * SHPCHPRM ACPI: PHP Resource Manager for ACPI platform
+ *
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <dely.l.sy@intel.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/efi.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#ifdef CONFIG_IA64
+#include <asm/iosapic.h>
+#endif
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/actypes.h>
+#include "shpchp.h"
+#include "shpchprm.h"
+
+#define PCI_MAX_BUS 0x100
+#define ACPI_STA_DEVICE_PRESENT 0x01
+
+#define METHOD_NAME__SUN "_SUN"
+#define METHOD_NAME__HPP "_HPP"
+#define METHOD_NAME_OSHP "OSHP"
+
+#define PHP_RES_BUS 0xA0
+#define PHP_RES_IO 0xA1
+#define PHP_RES_MEM 0xA2
+#define PHP_RES_PMEM 0xA3
+
+#define BRIDGE_TYPE_P2P 0x00
+#define BRIDGE_TYPE_HOST 0x01
+
+/* this should go to drivers/acpi/include/ */
+struct acpi__hpp {
+ u8 cache_line_size;
+ u8 latency_timer;
+ u8 enable_serr;
+ u8 enable_perr;
+};
+
+struct acpi_php_slot {
+ struct acpi_php_slot *next;
+ struct acpi_bridge *bridge;
+ acpi_handle handle;
+ int seg;
+ int bus;
+ int dev;
+ int fun;
+ u32 sun;
+ struct pci_resource *mem_head;
+ struct pci_resource *p_mem_head;
+ struct pci_resource *io_head;
+ struct pci_resource *bus_head;
+ void *slot_ops; /* _STA, _EJx, etc */
+ struct slot *slot;
+}; /* per func */
+
+struct acpi_bridge {
+ struct acpi_bridge *parent;
+ struct acpi_bridge *next;
+ struct acpi_bridge *child;
+ acpi_handle handle;
+ int seg;
+ int pbus; /* pdev->bus->number */
+ int pdevice; /* PCI_SLOT(pdev->devfn) */
+ int pfunction; /* PCI_DEVFN(pdev->devfn) */
+ int bus; /* pdev->subordinate->number */
+ struct acpi__hpp *_hpp;
+ struct acpi_php_slot *slots;
+ struct pci_resource *tmem_head; /* total from crs */
+ struct pci_resource *tp_mem_head; /* total from crs */
+ struct pci_resource *tio_head; /* total from crs */
+ struct pci_resource *tbus_head; /* total from crs */
+ struct pci_resource *mem_head; /* available */
+ struct pci_resource *p_mem_head; /* available */
+ struct pci_resource *io_head; /* available */
+ struct pci_resource *bus_head; /* available */
+ int scanned;
+ int type;
+};
+
+static struct acpi_bridge *acpi_bridges_head;
+
+static u8 * acpi_path_name( acpi_handle handle)
+{
+ acpi_status status;
+ static u8 path_name[ACPI_PATHNAME_MAX];
+ struct acpi_buffer ret_buf = { ACPI_PATHNAME_MAX, path_name };
+
+ memset(path_name, 0, sizeof (path_name));
+ status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &ret_buf);
+
+ if (ACPI_FAILURE(status))
+ return NULL;
+ else
+ return path_name;
+}
+
+static void acpi_get__hpp ( struct acpi_bridge *ab);
+static void acpi_run_oshp ( struct acpi_bridge *ab);
+
+static int acpi_add_slot_to_php_slots(
+ struct acpi_bridge *ab,
+ int bus_num,
+ acpi_handle handle,
+ u32 adr,
+ u32 sun
+ )
+{
+ struct acpi_php_slot *aps;
+ static long samesun = -1;
+
+ aps = (struct acpi_php_slot *) kmalloc (sizeof(struct acpi_php_slot), GFP_KERNEL);
+ if (!aps) {
+ err ("acpi_shpchprm: alloc for aps fail\n");
+ return -1;
+ }
+ memset(aps, 0, sizeof(struct acpi_php_slot));
+
+ aps->handle = handle;
+ aps->bus = bus_num;
+ aps->dev = (adr >> 16) & 0xffff;
+ aps->fun = adr & 0xffff;
+ aps->sun = sun;
+
+ aps->next = ab->slots; /* cling to the bridge */
+ aps->bridge = ab;
+ ab->slots = aps;
+
+ ab->scanned += 1;
+ if (!ab->_hpp)
+ acpi_get__hpp(ab);
+
+ acpi_run_oshp(ab);
+
+ if (sun != samesun) {
+ info("acpi_shpchprm: Slot sun(%x) at s:b:d:f=0x%02x:%02x:%02x:%02x\n", aps->sun, ab->seg,
+ aps->bus, aps->dev, aps->fun);
+ samesun = sun;
+ }
+ return 0;
+}
+
+static void acpi_get__hpp ( struct acpi_bridge *ab)
+{
+ acpi_status status;
+ u8 nui[4];
+ struct acpi_buffer ret_buf = { 0, NULL};
+ union acpi_object *ext_obj, *package;
+ u8 *path_name = acpi_path_name(ab->handle);
+ int i, len = 0;
+
+ /* get _hpp */
+ status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf);
+ switch (status) {
+ case AE_BUFFER_OVERFLOW:
+ ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL);
+ if (!ret_buf.pointer) {
+ err ("acpi_shpchprm:%s alloc for _HPP fail\n", path_name);
+ return;
+ }
+ status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf);
+ if (ACPI_SUCCESS(status))
+ break;
+ default:
+ if (ACPI_FAILURE(status)) {
+ err("acpi_shpchprm:%s _HPP fail=0x%x\n", path_name, status);
+ return;
+ }
+ }
+
+ ext_obj = (union acpi_object *) ret_buf.pointer;
+ if (ext_obj->type != ACPI_TYPE_PACKAGE) {
+ err ("acpi_shpchprm:%s _HPP obj not a package\n", path_name);
+ goto free_and_return;
+ }
+
+ len = ext_obj->package.count;
+ package = (union acpi_object *) ret_buf.pointer;
+ for ( i = 0; (i < len) || (i < 4); i++) {
+ ext_obj = (union acpi_object *) &package->package.elements[i];
+ switch (ext_obj->type) {
+ case ACPI_TYPE_INTEGER:
+ nui[i] = (u8)ext_obj->integer.value;
+ break;
+ default:
+ err ("acpi_shpchprm:%s _HPP obj type incorrect\n", path_name);
+ goto free_and_return;
+ }
+ }
+
+ ab->_hpp = kmalloc (sizeof (struct acpi__hpp), GFP_KERNEL);
+ if (!ab->_hpp) {
+ err ("acpi_shpchprm:%s alloc for _HPP failed\n", path_name);
+ goto free_and_return;
+ }
+ memset(ab->_hpp, 0, sizeof(struct acpi__hpp));
+
+ ab->_hpp->cache_line_size = nui[0];
+ ab->_hpp->latency_timer = nui[1];
+ ab->_hpp->enable_serr = nui[2];
+ ab->_hpp->enable_perr = nui[3];
+
+ dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size);
+ dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer);
+ dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr);
+ dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr);
+
+free_and_return:
+ kfree(ret_buf.pointer);
+}
+
+static void acpi_run_oshp ( struct acpi_bridge *ab)
+{
+ acpi_status status;
+ u8 *path_name = acpi_path_name(ab->handle);
+
+ /* run OSHP */
+ status = acpi_evaluate_object(ab->handle, METHOD_NAME_OSHP, NULL, NULL);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_pciehprm:%s OSHP fails=0x%x\n", path_name, status);
+ } else
+ dbg("acpi_pciehprm:%s OSHP passes =0x%x\n", path_name, status);
+ return;
+}
+
+static acpi_status acpi_evaluate_crs(
+ acpi_handle handle,
+ struct acpi_resource **retbuf
+ )
+{
+ acpi_status status;
+ struct acpi_buffer crsbuf;
+ u8 *path_name = acpi_path_name(handle);
+
+ crsbuf.length = 0;
+ crsbuf.pointer = NULL;
+
+ status = acpi_get_current_resources (handle, &crsbuf);
+
+ switch (status) {
+ case AE_BUFFER_OVERFLOW:
+ break; /* found */
+ case AE_NOT_FOUND:
+ dbg("acpi_shpchprm:%s _CRS not found\n", path_name);
+ return status;
+ default:
+ err ("acpi_shpchprm:%s _CRS fail=0x%x\n", path_name, status);
+ return status;
+ }
+
+ crsbuf.pointer = kmalloc (crsbuf.length, GFP_KERNEL);
+ if (!crsbuf.pointer) {
+ err ("acpi_shpchprm: alloc %ld bytes for %s _CRS fail\n", (ulong)crsbuf.length, path_name);
+ return AE_NO_MEMORY;
+ }
+
+ status = acpi_get_current_resources (handle, &crsbuf);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_shpchprm: %s _CRS fail=0x%x.\n", path_name, status);
+ kfree(crsbuf.pointer);
+ return status;
+ }
+
+ *retbuf = crsbuf.pointer;
+
+ return status;
+}
+
+static void free_pci_resource ( struct pci_resource *aprh)
+{
+ struct pci_resource *res, *next;
+
+ for (res = aprh; res; res = next) {
+ next = res->next;
+ kfree(res);
+ }
+}
+
+static void print_pci_resource ( struct pci_resource *aprh)
+{
+ struct pci_resource *res;
+
+ for (res = aprh; res; res = res->next)
+ dbg(" base= 0x%x length= 0x%x\n", res->base, res->length);
+}
+
+static void print_slot_resources( struct acpi_php_slot *aps)
+{
+ if (aps->bus_head) {
+ dbg(" BUS Resources:\n");
+ print_pci_resource (aps->bus_head);
+ }
+
+ if (aps->io_head) {
+ dbg(" IO Resources:\n");
+ print_pci_resource (aps->io_head);
+ }
+
+ if (aps->mem_head) {
+ dbg(" MEM Resources:\n");
+ print_pci_resource (aps->mem_head);
+ }
+
+ if (aps->p_mem_head) {
+ dbg(" PMEM Resources:\n");
+ print_pci_resource (aps->p_mem_head);
+ }
+}
+
+static void print_pci_resources( struct acpi_bridge *ab)
+{
+ if (ab->tbus_head) {
+ dbg(" Total BUS Resources:\n");
+ print_pci_resource (ab->tbus_head);
+ }
+ if (ab->bus_head) {
+ dbg(" BUS Resources:\n");
+ print_pci_resource (ab->bus_head);
+ }
+
+ if (ab->tio_head) {
+ dbg(" Total IO Resources:\n");
+ print_pci_resource (ab->tio_head);
+ }
+ if (ab->io_head) {
+ dbg(" IO Resources:\n");
+ print_pci_resource (ab->io_head);
+ }
+
+ if (ab->tmem_head) {
+ dbg(" Total MEM Resources:\n");
+ print_pci_resource (ab->tmem_head);
+ }
+ if (ab->mem_head) {
+ dbg(" MEM Resources:\n");
+ print_pci_resource (ab->mem_head);
+ }
+
+ if (ab->tp_mem_head) {
+ dbg(" Total PMEM Resources:\n");
+ print_pci_resource (ab->tp_mem_head);
+ }
+ if (ab->p_mem_head) {
+ dbg(" PMEM Resources:\n");
+ print_pci_resource (ab->p_mem_head);
+ }
+ if (ab->_hpp) {
+ dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size);
+ dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer);
+ dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr);
+ dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr);
+ }
+}
+
+static int shpchprm_delete_resource(
+ struct pci_resource **aprh,
+ ulong base,
+ ulong size)
+{
+ struct pci_resource *res;
+ struct pci_resource *prevnode;
+ struct pci_resource *split_node;
+ ulong tbase;
+
+ shpchp_resource_sort_and_combine(aprh);
+
+ for (res = *aprh; res; res = res->next) {
+ if (res->base > base)
+ continue;
+
+ if ((res->base + res->length) < (base + size))
+ continue;
+
+ if (res->base < base) {
+ tbase = base;
+
+ if ((res->length - (tbase - res->base)) < size)
+ continue;
+
+ split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!split_node)
+ return -ENOMEM;
+
+ split_node->base = res->base;
+ split_node->length = tbase - res->base;
+ res->base = tbase;
+ res->length -= split_node->length;
+
+ split_node->next = res->next;
+ res->next = split_node;
+ }
+
+ if (res->length >= size) {
+ split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!split_node)
+ return -ENOMEM;
+
+ split_node->base = res->base + size;
+ split_node->length = res->length - size;
+ res->length = size;
+
+ split_node->next = res->next;
+ res->next = split_node;
+ }
+
+ if (*aprh == res) {
+ *aprh = res->next;
+ } else {
+ prevnode = *aprh;
+ while (prevnode->next != res)
+ prevnode = prevnode->next;
+
+ prevnode->next = res->next;
+ }
+ res->next = NULL;
+ kfree(res);
+ break;
+ }
+
+ return 0;
+}
+
+static int shpchprm_delete_resources(
+ struct pci_resource **aprh,
+ struct pci_resource *this
+ )
+{
+ struct pci_resource *res;
+
+ for (res = this; res; res = res->next)
+ shpchprm_delete_resource(aprh, res->base, res->length);
+
+ return 0;
+}
+
+static int shpchprm_add_resource(
+ struct pci_resource **aprh,
+ ulong base,
+ ulong size)
+{
+ struct pci_resource *res;
+
+ for (res = *aprh; res; res = res->next) {
+ if ((res->base + res->length) == base) {
+ res->length += size;
+ size = 0L;
+ break;
+ }
+ if (res->next == *aprh)
+ break;
+ }
+
+ if (size) {
+ res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!res) {
+ err ("acpi_shpchprm: alloc for res fail\n");
+ return -ENOMEM;
+ }
+ memset(res, 0, sizeof (struct pci_resource));
+
+ res->base = base;
+ res->length = size;
+ res->next = *aprh;
+ *aprh = res;
+ }
+
+ return 0;
+}
+
+static int shpchprm_add_resources(
+ struct pci_resource **aprh,
+ struct pci_resource *this
+ )
+{
+ struct pci_resource *res;
+ int rc = 0;
+
+ for (res = this; res && !rc; res = res->next)
+ rc = shpchprm_add_resource(aprh, res->base, res->length);
+
+ return rc;
+}
+
+static void acpi_parse_io (
+ struct acpi_bridge *ab,
+ union acpi_resource_data *data
+ )
+{
+ struct acpi_resource_io *dataio;
+ dataio = (struct acpi_resource_io *) data;
+
+ dbg("Io Resource\n");
+ dbg(" %d bit decode\n", ACPI_DECODE_16 == dataio->io_decode ? 16:10);
+ dbg(" Range minimum base: %08X\n", dataio->min_base_address);
+ dbg(" Range maximum base: %08X\n", dataio->max_base_address);
+ dbg(" Alignment: %08X\n", dataio->alignment);
+ dbg(" Range Length: %08X\n", dataio->range_length);
+}
+
+static void acpi_parse_fixed_io (
+ struct acpi_bridge *ab,
+ union acpi_resource_data *data
+ )
+{
+ struct acpi_resource_fixed_io *datafio;
+ datafio = (struct acpi_resource_fixed_io *) data;
+
+ dbg("Fixed Io Resource\n");
+ dbg(" Range base address: %08X", datafio->base_address);
+ dbg(" Range length: %08X", datafio->range_length);
+}
+
+static void acpi_parse_address16_32 (
+ struct acpi_bridge *ab,
+ union acpi_resource_data *data,
+ acpi_resource_type id
+ )
+{
+ /*
+ * acpi_resource_address16 == acpi_resource_address32
+ * acpi_resource_address16 *data16 = (acpi_resource_address16 *) data;
+ */
+ struct acpi_resource_address32 *data32 = (struct acpi_resource_address32 *) data;
+ struct pci_resource **aprh, **tprh;
+
+ if (id == ACPI_RSTYPE_ADDRESS16)
+ dbg("acpi_shpchprm:16-Bit Address Space Resource\n");
+ else
+ dbg("acpi_shpchprm:32-Bit Address Space Resource\n");
+
+ switch (data32->resource_type) {
+ case ACPI_MEMORY_RANGE:
+ dbg(" Resource Type: Memory Range\n");
+ aprh = &ab->mem_head;
+ tprh = &ab->tmem_head;
+
+ switch (data32->attribute.memory.cache_attribute) {
+ case ACPI_NON_CACHEABLE_MEMORY:
+ dbg(" Type Specific: Noncacheable memory\n");
+ break;
+ case ACPI_CACHABLE_MEMORY:
+ dbg(" Type Specific: Cacheable memory\n");
+ break;
+ case ACPI_WRITE_COMBINING_MEMORY:
+ dbg(" Type Specific: Write-combining memory\n");
+ break;
+ case ACPI_PREFETCHABLE_MEMORY:
+ aprh = &ab->p_mem_head;
+ dbg(" Type Specific: Prefetchable memory\n");
+ break;
+ default:
+ dbg(" Type Specific: Invalid cache attribute\n");
+ break;
+ }
+
+ dbg(" Type Specific: Read%s\n", ACPI_READ_WRITE_MEMORY == data32->attribute.memory.read_write_attribute ? "/Write":" Only");
+ break;
+
+ case ACPI_IO_RANGE:
+ dbg(" Resource Type: I/O Range\n");
+ aprh = &ab->io_head;
+ tprh = &ab->tio_head;
+
+ switch (data32->attribute.io.range_attribute) {
+ case ACPI_NON_ISA_ONLY_RANGES:
+ dbg(" Type Specific: Non-ISA Io Addresses\n");
+ break;
+ case ACPI_ISA_ONLY_RANGES:
+ dbg(" Type Specific: ISA Io Addresses\n");
+ break;
+ case ACPI_ENTIRE_RANGE:
+ dbg(" Type Specific: ISA and non-ISA Io Addresses\n");
+ break;
+ default:
+ dbg(" Type Specific: Invalid range attribute\n");
+ break;
+ }
+ break;
+
+ case ACPI_BUS_NUMBER_RANGE:
+ dbg(" Resource Type: Bus Number Range(fixed)\n");
+ /* fixup to be compatible with the rest of php driver */
+ data32->min_address_range++;
+ data32->address_length--;
+ aprh = &ab->bus_head;
+ tprh = &ab->tbus_head;
+ break;
+ default:
+ dbg(" Resource Type: Invalid resource type. Exiting.\n");
+ return;
+ }
+
+ dbg(" Resource %s\n", ACPI_CONSUMER == data32->producer_consumer ? "Consumer":"Producer");
+ dbg(" %s decode\n", ACPI_SUB_DECODE == data32->decode ? "Subtractive":"Positive");
+ dbg(" Min address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->min_address_fixed ? "":"not");
+ dbg(" Max address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->max_address_fixed ? "":"not");
+ dbg(" Granularity: %08X\n", data32->granularity);
+ dbg(" Address range min: %08X\n", data32->min_address_range);
+ dbg(" Address range max: %08X\n", data32->max_address_range);
+ dbg(" Address translation offset: %08X\n", data32->address_translation_offset);
+ dbg(" Address Length: %08X\n", data32->address_length);
+
+ if (0xFF != data32->resource_source.index) {
+ dbg(" Resource Source Index: %X\n", data32->resource_source.index);
+ /* dbg(" Resource Source: %s\n", data32->resource_source.string_ptr); */
+ }
+
+ shpchprm_add_resource(aprh, data32->min_address_range, data32->address_length);
+}
+
+static acpi_status acpi_parse_crs(
+ struct acpi_bridge *ab,
+ struct acpi_resource *crsbuf
+ )
+{
+ acpi_status status = AE_OK;
+ struct acpi_resource *resource = crsbuf;
+ u8 count = 0;
+ u8 done = 0;
+
+ while (!done) {
+ dbg("acpi_shpchprm: PCI bus 0x%x Resource structure %x.\n", ab->bus, count++);
+ switch (resource->id) {
+ case ACPI_RSTYPE_IRQ:
+ dbg("Irq -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_DMA:
+ dbg("DMA -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_START_DPF:
+ dbg("Start DPF -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_END_DPF:
+ dbg("End DPF -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_IO:
+ acpi_parse_io (ab, &resource->data);
+ break;
+ case ACPI_RSTYPE_FIXED_IO:
+ acpi_parse_fixed_io (ab, &resource->data);
+ break;
+ case ACPI_RSTYPE_VENDOR:
+ dbg("Vendor -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_END_TAG:
+ dbg("End_tag -------- Resource\n");
+ done = 1;
+ break;
+ case ACPI_RSTYPE_MEM24:
+ dbg("Mem24 -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_MEM32:
+ dbg("Mem32 -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_FIXED_MEM32:
+ dbg("Fixed Mem32 -------- Resource\n");
+ break;
+ case ACPI_RSTYPE_ADDRESS16:
+ acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS16);
+ break;
+ case ACPI_RSTYPE_ADDRESS32:
+ acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS32);
+ break;
+ case ACPI_RSTYPE_ADDRESS64:
+ info("Address64 -------- Resource unparsed\n");
+ break;
+ case ACPI_RSTYPE_EXT_IRQ:
+ dbg("Ext Irq -------- Resource\n");
+ break;
+ default:
+ dbg("Invalid -------- resource type 0x%x\n", resource->id);
+ break;
+ }
+
+ resource = (struct acpi_resource *) ((char *)resource + resource->length);
+ }
+
+ return status;
+}
+
+static acpi_status acpi_get_crs( struct acpi_bridge *ab)
+{
+ acpi_status status;
+ struct acpi_resource *crsbuf;
+
+ status = acpi_evaluate_crs(ab->handle, &crsbuf);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_parse_crs(ab, crsbuf);
+ kfree(crsbuf);
+
+ shpchp_resource_sort_and_combine(&ab->bus_head);
+ shpchp_resource_sort_and_combine(&ab->io_head);
+ shpchp_resource_sort_and_combine(&ab->mem_head);
+ shpchp_resource_sort_and_combine(&ab->p_mem_head);
+
+ shpchprm_add_resources (&ab->tbus_head, ab->bus_head);
+ shpchprm_add_resources (&ab->tio_head, ab->io_head);
+ shpchprm_add_resources (&ab->tmem_head, ab->mem_head);
+ shpchprm_add_resources (&ab->tp_mem_head, ab->p_mem_head);
+ }
+
+ return status;
+}
+
+/* find acpi_bridge downword from ab. */
+static struct acpi_bridge *
+find_acpi_bridge_by_bus(
+ struct acpi_bridge *ab,
+ int seg,
+ int bus /* pdev->subordinate->number */
+ )
+{
+ struct acpi_bridge *lab = NULL;
+
+ if (!ab)
+ return NULL;
+
+ if ((ab->bus == bus) && (ab->seg == seg))
+ return ab;
+
+ if (ab->child)
+ lab = find_acpi_bridge_by_bus(ab->child, seg, bus);
+
+ if (!lab)
+ if (ab->next)
+ lab = find_acpi_bridge_by_bus(ab->next, seg, bus);
+
+ return lab;
+}
+
+/*
+ * Build a device tree of ACPI PCI Bridges
+ */
+static void shpchprm_acpi_register_a_bridge (
+ struct acpi_bridge **head,
+ struct acpi_bridge *pab, /* parent bridge to which child bridge is added */
+ struct acpi_bridge *cab /* child bridge to add */
+ )
+{
+ struct acpi_bridge *lpab;
+ struct acpi_bridge *lcab;
+
+ lpab = find_acpi_bridge_by_bus(*head, pab->seg, pab->bus);
+ if (!lpab) {
+ if (!(pab->type & BRIDGE_TYPE_HOST))
+ warn("PCI parent bridge s:b(%x:%x) not in list.\n", pab->seg, pab->bus);
+ pab->next = *head;
+ *head = pab;
+ lpab = pab;
+ }
+
+ if ((cab->type & BRIDGE_TYPE_HOST) && (pab == cab))
+ return;
+
+ lcab = find_acpi_bridge_by_bus(*head, cab->seg, cab->bus);
+ if (lcab) {
+ if ((pab->bus != lcab->parent->bus) || (lcab->bus != cab->bus))
+ err("PCI child bridge s:b(%x:%x) in list with diff parent.\n", cab->seg, cab->bus);
+ return;
+ } else
+ lcab = cab;
+
+ lcab->parent = lpab;
+ lcab->next = lpab->child;
+ lpab->child = lcab;
+}
+
+static acpi_status shpchprm_acpi_build_php_slots_callback(
+ acpi_handle handle,
+ u32 Level,
+ void *context,
+ void **retval
+ )
+{
+ ulong bus_num;
+ ulong seg_num;
+ ulong sun, adr;
+ ulong padr = 0;
+ acpi_handle phandle = NULL;
+ struct acpi_bridge *pab = (struct acpi_bridge *)context;
+ struct acpi_bridge *lab;
+ acpi_status status;
+ u8 *path_name = acpi_path_name(handle);
+
+ /* get _SUN */
+ status = acpi_evaluate_integer(handle, METHOD_NAME__SUN, NULL, &sun);
+ switch(status) {
+ case AE_NOT_FOUND:
+ return AE_OK;
+ default:
+ if (ACPI_FAILURE(status)) {
+ err("acpi_shpchprm:%s _SUN fail=0x%x\n", path_name, status);
+ return status;
+ }
+ }
+
+ /* get _ADR. _ADR must exist if _SUN exists */
+ status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status);
+ return status;
+ }
+
+ dbg("acpi_shpchprm:%s sun=0x%08x adr=0x%08x\n", path_name, (u32)sun, (u32)adr);
+
+ status = acpi_get_parent(handle, &phandle);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_shpchprm:%s get_parent fail=0x%x\n", path_name, status);
+ return (status);
+ }
+
+ bus_num = pab->bus;
+ seg_num = pab->seg;
+
+ if (pab->bus == bus_num) {
+ lab = pab;
+ } else {
+ dbg("WARN: pab is not parent\n");
+ lab = find_acpi_bridge_by_bus(pab, seg_num, bus_num);
+ if (!lab) {
+ dbg("acpi_shpchprm: alloc new P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun);
+ lab = (struct acpi_bridge *)kmalloc(sizeof(struct acpi_bridge), GFP_KERNEL);
+ if (!lab) {
+ err("acpi_shpchprm: alloc for ab fail\n");
+ return AE_NO_MEMORY;
+ }
+ memset(lab, 0, sizeof(struct acpi_bridge));
+
+ lab->handle = phandle;
+ lab->pbus = pab->bus;
+ lab->pdevice = (int)(padr >> 16) & 0xffff;
+ lab->pfunction = (int)(padr & 0xffff);
+ lab->bus = (int)bus_num;
+ lab->scanned = 0;
+ lab->type = BRIDGE_TYPE_P2P;
+
+ shpchprm_acpi_register_a_bridge (&acpi_bridges_head, pab, lab);
+ } else
+ dbg("acpi_shpchprm: found P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun);
+ }
+
+ acpi_add_slot_to_php_slots(lab, (int)bus_num, handle, (u32)adr, (u32)sun);
+ return (status);
+}
+
+static int shpchprm_acpi_build_php_slots(
+ struct acpi_bridge *ab,
+ u32 depth
+ )
+{
+ acpi_status status;
+ u8 *path_name = acpi_path_name(ab->handle);
+
+ /* Walk down this pci bridge to get _SUNs if any behind P2P */
+ status = acpi_walk_namespace ( ACPI_TYPE_DEVICE,
+ ab->handle,
+ depth,
+ shpchprm_acpi_build_php_slots_callback,
+ ab,
+ NULL );
+ if (ACPI_FAILURE(status)) {
+ dbg("acpi_shpchprm:%s walk for _SUN on pci bridge seg:bus(%x:%x) fail=0x%x\n", path_name, ab->seg, ab->bus, status);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void build_a_bridge(
+ struct acpi_bridge *pab,
+ struct acpi_bridge *ab
+ )
+{
+ u8 *path_name = acpi_path_name(ab->handle);
+
+ shpchprm_acpi_register_a_bridge (&acpi_bridges_head, pab, ab);
+
+ switch (ab->type) {
+ case BRIDGE_TYPE_HOST:
+ dbg("acpi_shpchprm: Registered PCI HOST Bridge(%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n",
+ ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name);
+ break;
+ case BRIDGE_TYPE_P2P:
+ dbg("acpi_shpchprm: Registered PCI P2P Bridge(%02x-%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n",
+ ab->pbus, ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name);
+ break;
+ };
+
+ /* build any immediate PHP slots under this pci bridge */
+ shpchprm_acpi_build_php_slots(ab, 1);
+}
+
+static struct acpi_bridge * add_p2p_bridge(
+ acpi_handle handle,
+ struct acpi_bridge *pab, /* parent */
+ ulong adr
+ )
+{
+ struct acpi_bridge *ab;
+ struct pci_dev *pdev;
+ ulong devnum, funcnum;
+ u8 *path_name = acpi_path_name(handle);
+
+ ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL);
+ if (!ab) {
+ err("acpi_shpchprm: alloc for ab fail\n");
+ return NULL;
+ }
+ memset(ab, 0, sizeof(struct acpi_bridge));
+
+ devnum = (adr >> 16) & 0xffff;
+ funcnum = adr & 0xffff;
+
+ pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum));
+ if (!pdev || !pdev->subordinate) {
+ err("acpi_shpchprm:%s is not a P2P Bridge\n", path_name);
+ kfree(ab);
+ return NULL;
+ }
+
+ ab->handle = handle;
+ ab->seg = pab->seg;
+ ab->pbus = pab->bus; /* or pdev->bus->number */
+ ab->pdevice = devnum; /* or PCI_SLOT(pdev->devfn) */
+ ab->pfunction = funcnum; /* or PCI_FUNC(pdev->devfn) */
+ ab->bus = pdev->subordinate->number;
+ ab->scanned = 0;
+ ab->type = BRIDGE_TYPE_P2P;
+
+ dbg("acpi_shpchprm: P2P(%x-%x) on pci=b:d:f(%x:%x:%x) acpi=b:d:f(%x:%x:%x) [%s]\n",
+ pab->bus, ab->bus, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ pab->bus, (u32)devnum, (u32)funcnum, path_name);
+
+ build_a_bridge(pab, ab);
+
+ return ab;
+}
+
+static acpi_status scan_p2p_bridge(
+ acpi_handle handle,
+ u32 Level,
+ void *context,
+ void **retval
+ )
+{
+ struct acpi_bridge *pab = (struct acpi_bridge *)context;
+ struct acpi_bridge *ab;
+ acpi_status status;
+ ulong adr = 0;
+ u8 *path_name = acpi_path_name(handle);
+ ulong devnum, funcnum;
+ struct pci_dev *pdev;
+
+ /* get device, function */
+ status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND)
+ err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status);
+ return AE_OK;
+ }
+
+ devnum = (adr >> 16) & 0xffff;
+ funcnum = adr & 0xffff;
+
+ pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum));
+ if (!pdev)
+ return AE_OK;
+ if (!pdev->subordinate)
+ return AE_OK;
+
+ ab = add_p2p_bridge(handle, pab, adr);
+ if (ab) {
+ status = acpi_walk_namespace ( ACPI_TYPE_DEVICE,
+ handle,
+ (u32)1,
+ scan_p2p_bridge,
+ ab,
+ NULL);
+ if (ACPI_FAILURE(status))
+ dbg("acpi_shpchprm:%s find_p2p fail=0x%x\n", path_name, status);
+ }
+
+ return AE_OK;
+}
+
+static struct acpi_bridge * add_host_bridge(
+ acpi_handle handle,
+ ulong segnum,
+ ulong busnum
+ )
+{
+ ulong adr = 0;
+ acpi_status status;
+ struct acpi_bridge *ab;
+ u8 *path_name = acpi_path_name(handle);
+
+ /* get device, function: host br adr is always 0000 though. */
+ status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status);
+ return NULL;
+ }
+ dbg("acpi_shpchprm: ROOT PCI seg(0x%x)bus(0x%x)dev(0x%x)func(0x%x) [%s]\n", (u32)segnum, (u32)busnum,
+ (u32)(adr >> 16) & 0xffff, (u32)adr & 0xffff, path_name);
+
+ ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL);
+ if (!ab) {
+ err("acpi_shpchprm: alloc for ab fail\n");
+ return NULL;
+ }
+ memset(ab, 0, sizeof(struct acpi_bridge));
+
+ ab->handle = handle;
+ ab->seg = (int)segnum;
+ ab->bus = ab->pbus = (int)busnum;
+ ab->pdevice = (int)(adr >> 16) & 0xffff;
+ ab->pfunction = (int)(adr & 0xffff);
+ ab->scanned = 0;
+ ab->type = BRIDGE_TYPE_HOST;
+
+ /* get root pci bridge's current resources */
+ status = acpi_get_crs(ab);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_shpchprm:%s evaluate _CRS fail=0x%x\n", path_name, status);
+ kfree(ab);
+ return NULL;
+ }
+ build_a_bridge(ab, ab);
+
+ return ab;
+}
+
+static acpi_status acpi_scan_from_root_pci_callback (
+ acpi_handle handle,
+ u32 Level,
+ void *context,
+ void **retval
+ )
+{
+ ulong segnum = 0;
+ ulong busnum = 0;
+ acpi_status status;
+ struct acpi_bridge *ab;
+ u8 *path_name = acpi_path_name(handle);
+
+ /* get bus number of this pci root bridge */
+ status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, &segnum);
+ if (ACPI_FAILURE(status)) {
+ if (status != AE_NOT_FOUND) {
+ err("acpi_shpchprm:%s evaluate _SEG fail=0x%x\n", path_name, status);
+ return status;
+ }
+ segnum = 0;
+ }
+
+ /* get bus number of this pci root bridge */
+ status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL, &busnum);
+ if (ACPI_FAILURE(status)) {
+ err("acpi_shpchprm:%s evaluate _BBN fail=0x%x\n", path_name, status);
+ return (status);
+ }
+
+ ab = add_host_bridge(handle, segnum, busnum);
+ if (ab) {
+ status = acpi_walk_namespace ( ACPI_TYPE_DEVICE,
+ handle,
+ 1,
+ scan_p2p_bridge,
+ ab,
+ NULL);
+ if (ACPI_FAILURE(status))
+ dbg("acpi_shpchprm:%s find_p2p fail=0x%x\n", path_name, status);
+ }
+
+ return AE_OK;
+}
+
+static int shpchprm_acpi_scan_pci (void)
+{
+ acpi_status status;
+
+ /*
+ * TBD: traverse LDM device tree with the help of
+ * unified ACPI augmented for php device population.
+ */
+ status = acpi_get_devices ( PCI_ROOT_HID_STRING,
+ acpi_scan_from_root_pci_callback,
+ NULL,
+ NULL );
+ if (ACPI_FAILURE(status)) {
+ err("acpi_shpchprm:get_device PCI ROOT HID fail=0x%x\n", status);
+ return -1;
+ }
+
+ return 0;
+}
+
+int shpchprm_init(enum php_ctlr_type ctlr_type)
+{
+ int rc;
+
+ if (ctlr_type != PCI)
+ return -ENODEV;
+
+ dbg("shpchprm ACPI init <enter>\n");
+ acpi_bridges_head = NULL;
+
+ /* construct PCI bus:device tree of acpi_handles */
+ rc = shpchprm_acpi_scan_pci();
+ if (rc)
+ return rc;
+
+ dbg("shpchprm ACPI init %s\n", (rc)?"fail":"success");
+ return rc;
+}
+
+static void free_a_slot(struct acpi_php_slot *aps)
+{
+ dbg(" free a php func of slot(0x%02x) on PCI b:d:f=0x%02x:%02x:%02x\n", aps->sun, aps->bus, aps->dev, aps->fun);
+
+ free_pci_resource (aps->io_head);
+ free_pci_resource (aps->bus_head);
+ free_pci_resource (aps->mem_head);
+ free_pci_resource (aps->p_mem_head);
+
+ kfree(aps);
+}
+
+static void free_a_bridge( struct acpi_bridge *ab)
+{
+ struct acpi_php_slot *aps, *next;
+
+ switch (ab->type) {
+ case BRIDGE_TYPE_HOST:
+ dbg("Free ACPI PCI HOST Bridge(%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n",
+ ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction);
+ break;
+ case BRIDGE_TYPE_P2P:
+ dbg("Free ACPI PCI P2P Bridge(%x-%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n",
+ ab->pbus, ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction);
+ break;
+ };
+
+ /* free slots first */
+ for (aps = ab->slots; aps; aps = next) {
+ next = aps->next;
+ free_a_slot(aps);
+ }
+
+ free_pci_resource (ab->io_head);
+ free_pci_resource (ab->tio_head);
+ free_pci_resource (ab->bus_head);
+ free_pci_resource (ab->tbus_head);
+ free_pci_resource (ab->mem_head);
+ free_pci_resource (ab->tmem_head);
+ free_pci_resource (ab->p_mem_head);
+ free_pci_resource (ab->tp_mem_head);
+
+ kfree(ab);
+}
+
+static void shpchprm_free_bridges ( struct acpi_bridge *ab)
+{
+ if (!ab)
+ return;
+
+ if (ab->child)
+ shpchprm_free_bridges (ab->child);
+
+ if (ab->next)
+ shpchprm_free_bridges (ab->next);
+
+ free_a_bridge(ab);
+}
+
+void shpchprm_cleanup(void)
+{
+ shpchprm_free_bridges (acpi_bridges_head);
+}
+
+static int get_number_of_slots (
+ struct acpi_bridge *ab,
+ int selfonly
+ )
+{
+ struct acpi_php_slot *aps;
+ int prev_slot = -1;
+ int slot_num = 0;
+
+ for ( aps = ab->slots; aps; aps = aps->next)
+ if (aps->dev != prev_slot) {
+ prev_slot = aps->dev;
+ slot_num++;
+ }
+
+ if (ab->child)
+ slot_num += get_number_of_slots (ab->child, 0);
+
+ if (selfonly)
+ return slot_num;
+
+ if (ab->next)
+ slot_num += get_number_of_slots (ab->next, 0);
+
+ return slot_num;
+}
+
+static int print_acpi_resources (struct acpi_bridge *ab)
+{
+ struct acpi_php_slot *aps;
+ int i;
+
+ switch (ab->type) {
+ case BRIDGE_TYPE_HOST:
+ dbg("PCI HOST Bridge (%x) [%s]\n", ab->bus, acpi_path_name(ab->handle));
+ break;
+ case BRIDGE_TYPE_P2P:
+ dbg("PCI P2P Bridge (%x-%x) [%s]\n", ab->pbus, ab->bus, acpi_path_name(ab->handle));
+ break;
+ };
+
+ print_pci_resources (ab);
+
+ for ( i = -1, aps = ab->slots; aps; aps = aps->next) {
+ if (aps->dev == i)
+ continue;
+ dbg(" Slot sun(%x) s:b:d:f(%02x:%02x:%02x:%02x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun);
+ print_slot_resources(aps);
+ i = aps->dev;
+ }
+
+ if (ab->child)
+ print_acpi_resources (ab->child);
+
+ if (ab->next)
+ print_acpi_resources (ab->next);
+
+ return 0;
+}
+
+int shpchprm_print_pirt(void)
+{
+ dbg("SHPCHPRM ACPI Slots\n");
+ if (acpi_bridges_head)
+ print_acpi_resources (acpi_bridges_head);
+ return 0;
+}
+
+static struct acpi_php_slot * get_acpi_slot (
+ struct acpi_bridge *ab,
+ u32 sun
+ )
+{
+ struct acpi_php_slot *aps = NULL;
+
+ for ( aps = ab->slots; aps; aps = aps->next)
+ if (aps->sun == sun)
+ return aps;
+
+ if (!aps && ab->child) {
+ aps = (struct acpi_php_slot *)get_acpi_slot (ab->child, sun);
+ if (aps)
+ return aps;
+ }
+
+ if (!aps && ab->next) {
+ aps = (struct acpi_php_slot *)get_acpi_slot (ab->next, sun);
+ if (aps)
+ return aps;
+ }
+
+ return aps;
+
+}
+
+#if 0
+static void * shpchprm_get_slot(struct slot *slot)
+{
+ struct acpi_bridge *ab = acpi_bridges_head;
+ struct acpi_php_slot *aps = get_acpi_slot (ab, slot->number);
+
+ aps->slot = slot;
+
+ dbg("Got acpi slot sun(%x): s:b:d:f(%x:%x:%x:%x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun);
+
+ return (void *)aps;
+}
+#endif
+
+static void shpchprm_dump_func_res( struct pci_func *fun)
+{
+ struct pci_func *func = fun;
+
+ if (func->bus_head) {
+ dbg(": BUS Resources:\n");
+ print_pci_resource (func->bus_head);
+ }
+ if (func->io_head) {
+ dbg(": IO Resources:\n");
+ print_pci_resource (func->io_head);
+ }
+ if (func->mem_head) {
+ dbg(": MEM Resources:\n");
+ print_pci_resource (func->mem_head);
+ }
+ if (func->p_mem_head) {
+ dbg(": PMEM Resources:\n");
+ print_pci_resource (func->p_mem_head);
+ }
+}
+
+static void shpchprm_dump_ctrl_res( struct controller *ctlr)
+{
+ struct controller *ctrl = ctlr;
+
+ if (ctrl->bus_head) {
+ dbg(": BUS Resources:\n");
+ print_pci_resource (ctrl->bus_head);
+ }
+ if (ctrl->io_head) {
+ dbg(": IO Resources:\n");
+ print_pci_resource (ctrl->io_head);
+ }
+ if (ctrl->mem_head) {
+ dbg(": MEM Resources:\n");
+ print_pci_resource (ctrl->mem_head);
+ }
+ if (ctrl->p_mem_head) {
+ dbg(": PMEM Resources:\n");
+ print_pci_resource (ctrl->p_mem_head);
+ }
+}
+
+static int shpchprm_get_used_resources (
+ struct controller *ctrl,
+ struct pci_func *func
+ )
+{
+ return shpchp_save_used_resources (ctrl, func, !DISABLE_CARD);
+}
+
+static int configure_existing_function(
+ struct controller *ctrl,
+ struct pci_func *func
+ )
+{
+ int rc;
+
+ /* see how much resources the func has used. */
+ rc = shpchprm_get_used_resources (ctrl, func);
+
+ if (!rc) {
+ /* subtract the resources used by the func from ctrl resources */
+ rc = shpchprm_delete_resources (&ctrl->bus_head, func->bus_head);
+ rc |= shpchprm_delete_resources (&ctrl->io_head, func->io_head);
+ rc |= shpchprm_delete_resources (&ctrl->mem_head, func->mem_head);
+ rc |= shpchprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head);
+ if (rc)
+ warn("aCEF: cannot del used resources\n");
+ } else
+ err("aCEF: cannot get used resources\n");
+
+ return rc;
+}
+
+static int bind_pci_resources_to_slots ( struct controller *ctrl)
+{
+ struct pci_func *func, new_func;
+ int busn = ctrl->slot_bus;
+ int devn, funn;
+ u32 vid;
+
+ for (devn = 0; devn < 32; devn++) {
+ for (funn = 0; funn < 8; funn++) {
+ /*
+ if (devn == ctrl->device && funn == ctrl->function)
+ continue;
+ */
+ /* find out if this entry is for an occupied slot */
+ vid = 0xFFFFFFFF;
+ pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid);
+
+ if (vid != 0xFFFFFFFF) {
+ func = shpchp_slot_find(busn, devn, funn);
+ if (!func) {
+ memset(&new_func, 0, sizeof(struct pci_func));
+ new_func.bus = busn;
+ new_func.device = devn;
+ new_func.function = funn;
+ new_func.is_a_board = 1;
+ configure_existing_function(ctrl, &new_func);
+ shpchprm_dump_func_res(&new_func);
+ } else {
+ configure_existing_function(ctrl, func);
+ shpchprm_dump_func_res(func);
+ }
+ dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int bind_pci_resources(
+ struct controller *ctrl,
+ struct acpi_bridge *ab
+ )
+{
+ int status = 0;
+
+ if (ab->bus_head) {
+ dbg("bapr: BUS Resources add on PCI 0x%x\n", ab->bus);
+ status = shpchprm_add_resources (&ctrl->bus_head, ab->bus_head);
+ if (shpchprm_delete_resources (&ab->bus_head, ctrl->bus_head))
+ warn("bapr: cannot sub BUS Resource on PCI 0x%x\n", ab->bus);
+ if (status) {
+ err("bapr: BUS Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
+ return status;
+ }
+ } else
+ info("bapr: No BUS Resource on PCI 0x%x.\n", ab->bus);
+
+ if (ab->io_head) {
+ dbg("bapr: IO Resources add on PCI 0x%x\n", ab->bus);
+ status = shpchprm_add_resources (&ctrl->io_head, ab->io_head);
+ if (shpchprm_delete_resources (&ab->io_head, ctrl->io_head))
+ warn("bapr: cannot sub IO Resource on PCI 0x%x\n", ab->bus);
+ if (status) {
+ err("bapr: IO Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
+ return status;
+ }
+ } else
+ info("bapr: No IO Resource on PCI 0x%x.\n", ab->bus);
+
+ if (ab->mem_head) {
+ dbg("bapr: MEM Resources add on PCI 0x%x\n", ab->bus);
+ status = shpchprm_add_resources (&ctrl->mem_head, ab->mem_head);
+ if (shpchprm_delete_resources (&ab->mem_head, ctrl->mem_head))
+ warn("bapr: cannot sub MEM Resource on PCI 0x%x\n", ab->bus);
+ if (status) {
+ err("bapr: MEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
+ return status;
+ }
+ } else
+ info("bapr: No MEM Resource on PCI 0x%x.\n", ab->bus);
+
+ if (ab->p_mem_head) {
+ dbg("bapr: PMEM Resources add on PCI 0x%x\n", ab->bus);
+ status = shpchprm_add_resources (&ctrl->p_mem_head, ab->p_mem_head);
+ if (shpchprm_delete_resources (&ab->p_mem_head, ctrl->p_mem_head))
+ warn("bapr: cannot sub PMEM Resource on PCI 0x%x\n", ab->bus);
+ if (status) {
+ err("bapr: PMEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
+ return status;
+ }
+ } else
+ info("bapr: No PMEM Resource on PCI 0x%x.\n", ab->bus);
+
+ return status;
+}
+
+static int no_pci_resources( struct acpi_bridge *ab)
+{
+ return !(ab->p_mem_head || ab->mem_head || ab->io_head || ab->bus_head);
+}
+
+static int find_pci_bridge_resources (
+ struct controller *ctrl,
+ struct acpi_bridge *ab
+ )
+{
+ int rc = 0;
+ struct pci_func func;
+
+ memset(&func, 0, sizeof(struct pci_func));
+
+ func.bus = ab->pbus;
+ func.device = ab->pdevice;
+ func.function = ab->pfunction;
+ func.is_a_board = 1;
+
+ /* Get used resources for this PCI bridge */
+ rc = shpchp_save_used_resources (ctrl, &func, !DISABLE_CARD);
+
+ ab->io_head = func.io_head;
+ ab->mem_head = func.mem_head;
+ ab->p_mem_head = func.p_mem_head;
+ ab->bus_head = func.bus_head;
+ if (ab->bus_head)
+ shpchprm_delete_resource(&ab->bus_head, ctrl->bus, 1);
+
+ return rc;
+}
+
+static int get_pci_resources_from_bridge(
+ struct controller *ctrl,
+ struct acpi_bridge *ab
+ )
+{
+ int rc = 0;
+
+ dbg("grfb: Get Resources for PCI 0x%x from actual PCI bridge 0x%x.\n", ctrl->bus, ab->bus);
+
+ rc = find_pci_bridge_resources (ctrl, ab);
+
+ shpchp_resource_sort_and_combine(&ab->bus_head);
+ shpchp_resource_sort_and_combine(&ab->io_head);
+ shpchp_resource_sort_and_combine(&ab->mem_head);
+ shpchp_resource_sort_and_combine(&ab->p_mem_head);
+
+ shpchprm_add_resources (&ab->tbus_head, ab->bus_head);
+ shpchprm_add_resources (&ab->tio_head, ab->io_head);
+ shpchprm_add_resources (&ab->tmem_head, ab->mem_head);
+ shpchprm_add_resources (&ab->tp_mem_head, ab->p_mem_head);
+
+ return rc;
+}
+
+static int get_pci_resources(
+ struct controller *ctrl,
+ struct acpi_bridge *ab
+ )
+{
+ int rc = 0;
+
+ if (no_pci_resources(ab)) {
+ dbg("spbr:PCI 0x%x has no resources. Get parent resources.\n", ab->bus);
+ rc = get_pci_resources_from_bridge(ctrl, ab);
+ }
+
+ return rc;
+}
+
+int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum)
+{
+ int offset = devnum - ctrl->slot_device_offset;
+
+ dbg("%s: ctrl->slot_num_inc %d, offset %d\n", __FUNCTION__, ctrl->slot_num_inc, offset);
+ *sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc *offset);
+ return 0;
+}
+
+/*
+ * Get resources for this ctrl.
+ * 1. get total resources from ACPI _CRS or bridge (this ctrl)
+ * 2. find used resources of existing adapters
+ * 3. subtract used resources from total resources
+ */
+int shpchprm_find_available_resources( struct controller *ctrl)
+{
+ int rc = 0;
+ struct acpi_bridge *ab;
+
+ ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->pci_dev->subordinate->number);
+ if (!ab) {
+ err("pfar:cannot locate acpi bridge of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number);
+ return -1;
+ }
+ if (no_pci_resources(ab)) {
+ rc = get_pci_resources(ctrl, ab);
+ if (rc) {
+ err("pfar:cannot get pci resources of PCI 0x%x.\n",ctrl->pci_dev->subordinate->number);
+ return -1;
+ }
+ }
+
+ rc = bind_pci_resources(ctrl, ab);
+ dbg("pfar:pre-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number);
+ shpchprm_dump_ctrl_res(ctrl);
+
+ bind_pci_resources_to_slots (ctrl);
+
+ dbg("pfar:post-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number);
+ shpchprm_dump_ctrl_res(ctrl);
+
+ return rc;
+}
+
+int shpchprm_set_hpp(
+ struct controller *ctrl,
+ struct pci_func *func,
+ u8 card_type
+ )
+{
+ struct acpi_bridge *ab;
+ struct pci_bus lpci_bus, *pci_bus;
+ int rc = 0;
+ unsigned int devfn;
+ u8 cls= 0x08; /* default cache line size */
+ u8 lt = 0x40; /* default latency timer */
+ u8 ep = 0;
+ u8 es = 0;
+
+ memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus);
+
+ if (ab) {
+ if (ab->_hpp) {
+ lt = (u8)ab->_hpp->latency_timer;
+ cls = (u8)ab->_hpp->cache_line_size;
+ ep = (u8)ab->_hpp->enable_perr;
+ es = (u8)ab->_hpp->enable_serr;
+ } else
+ dbg("_hpp: no _hpp for B/D/F=%#x/%#x/%#x. use default value\n", func->bus, func->device, func->function);
+ } else
+ dbg("_hpp: no acpi bridge for B/D/F = %#x/%#x/%#x. use default value\n", func->bus, func->device, func->function);
+
+
+ if (card_type == PCI_HEADER_TYPE_BRIDGE) {
+ /* set subordinate Latency Timer */
+ rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, lt);
+ }
+
+ /* set base Latency Timer */
+ rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, lt);
+ dbg(" set latency timer =0x%02x: %x\n", lt, rc);
+
+ rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, cls);
+ dbg(" set cache_line_size=0x%02x: %x\n", cls, rc);
+
+ return rc;
+}
+
+void shpchprm_enable_card(
+ struct controller *ctrl,
+ struct pci_func *func,
+ u8 card_type)
+{
+ u16 command, cmd, bcommand, bcmd;
+ struct pci_bus lpci_bus, *pci_bus;
+ struct acpi_bridge *ab;
+ unsigned int devfn;
+ int rc;
+
+ memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
+
+ if (card_type == PCI_HEADER_TYPE_BRIDGE) {
+ rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
+ }
+
+ cmd = command = command | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
+ | PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+ bcmd = bcommand = bcommand | PCI_BRIDGE_CTL_NO_ISA;
+
+ ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus);
+ if (ab) {
+ if (ab->_hpp) {
+ if (ab->_hpp->enable_perr) {
+ command |= PCI_COMMAND_PARITY;
+ bcommand |= PCI_BRIDGE_CTL_PARITY;
+ } else {
+ command &= ~PCI_COMMAND_PARITY;
+ bcommand &= ~PCI_BRIDGE_CTL_PARITY;
+ }
+ if (ab->_hpp->enable_serr) {
+ command |= PCI_COMMAND_SERR;
+ bcommand |= PCI_BRIDGE_CTL_SERR;
+ } else {
+ command &= ~PCI_COMMAND_SERR;
+ bcommand &= ~PCI_BRIDGE_CTL_SERR;
+ }
+ } else
+ dbg("no _hpp for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function);
+ } else
+ dbg("no acpi bridge for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function);
+
+ if (command != cmd) {
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
+ }
+ if ((card_type == PCI_HEADER_TYPE_BRIDGE) && (bcommand != bcmd)) {
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
+ }
+}
+
diff --git a/drivers/pci/hotplug/shpchprm_legacy.c b/drivers/pci/hotplug/shpchprm_legacy.c
new file mode 100644
index 000000000000..37fa77a98289
--- /dev/null
+++ b/drivers/pci/hotplug/shpchprm_legacy.c
@@ -0,0 +1,439 @@
+/*
+ * SHPCHPRM Legacy: PHP Resource Manager for Non-ACPI/Legacy platform
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>,<dely.l.sy@intel.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#ifdef CONFIG_IA64
+#include <asm/iosapic.h>
+#endif
+#include "shpchp.h"
+#include "shpchprm.h"
+#include "shpchprm_legacy.h"
+
+static void __iomem *shpchp_rom_start;
+static u16 unused_IRQ;
+
+void shpchprm_cleanup(void)
+{
+ if (shpchp_rom_start)
+ iounmap(shpchp_rom_start);
+}
+
+int shpchprm_print_pirt(void)
+{
+ return 0;
+}
+
+int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum)
+{
+ int offset = devnum - ctrl->slot_device_offset;
+
+ *sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc * offset);
+ return 0;
+}
+
+/* Find the Hot Plug Resource Table in the specified region of memory */
+static void __iomem *detect_HRT_floating_pointer(void __iomem *begin, void __iomem *end)
+{
+ void __iomem *fp;
+ void __iomem *endp;
+ u8 temp1, temp2, temp3, temp4;
+ int status = 0;
+
+ endp = (end - sizeof(struct hrt) + 1);
+
+ for (fp = begin; fp <= endp; fp += 16) {
+ temp1 = readb(fp + SIG0);
+ temp2 = readb(fp + SIG1);
+ temp3 = readb(fp + SIG2);
+ temp4 = readb(fp + SIG3);
+ if (temp1 == '$' && temp2 == 'H' && temp3 == 'R' && temp4 == 'T') {
+ status = 1;
+ break;
+ }
+ }
+
+ if (!status)
+ fp = NULL;
+
+ dbg("Discovered Hotplug Resource Table at %p\n", fp);
+ return fp;
+}
+
+/*
+ * shpchprm_find_available_resources
+ *
+ * Finds available memory, IO, and IRQ resources for programming
+ * devices which may be added to the system
+ * this function is for hot plug ADD!
+ *
+ * returns 0 if success
+ */
+int shpchprm_find_available_resources(struct controller *ctrl)
+{
+ u8 populated_slot;
+ u8 bridged_slot;
+ void __iomem *one_slot;
+ struct pci_func *func = NULL;
+ int i = 10, index = 0;
+ u32 temp_dword, rc;
+ ulong temp_ulong;
+ struct pci_resource *mem_node;
+ struct pci_resource *p_mem_node;
+ struct pci_resource *io_node;
+ struct pci_resource *bus_node;
+ void __iomem *rom_resource_table;
+ struct pci_bus lpci_bus, *pci_bus;
+ u8 cfgspc_irq, temp;
+
+ memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ rom_resource_table = detect_HRT_floating_pointer(shpchp_rom_start, shpchp_rom_start + 0xffff);
+ dbg("rom_resource_table = %p\n", rom_resource_table);
+ if (rom_resource_table == NULL)
+ return -ENODEV;
+
+ /* Sum all resources and setup resource maps */
+ unused_IRQ = readl(rom_resource_table + UNUSED_IRQ);
+ dbg("unused_IRQ = %x\n", unused_IRQ);
+
+ temp = 0;
+ while (unused_IRQ) {
+ if (unused_IRQ & 1) {
+ shpchp_disk_irq = temp;
+ break;
+ }
+ unused_IRQ = unused_IRQ >> 1;
+ temp++;
+ }
+
+ dbg("shpchp_disk_irq= %d\n", shpchp_disk_irq);
+ unused_IRQ = unused_IRQ >> 1;
+ temp++;
+
+ while (unused_IRQ) {
+ if (unused_IRQ & 1) {
+ shpchp_nic_irq = temp;
+ break;
+ }
+ unused_IRQ = unused_IRQ >> 1;
+ temp++;
+ }
+
+ dbg("shpchp_nic_irq= %d\n", shpchp_nic_irq);
+ unused_IRQ = readl(rom_resource_table + PCIIRQ);
+
+ temp = 0;
+
+ pci_read_config_byte(ctrl->pci_dev, PCI_INTERRUPT_LINE, &cfgspc_irq);
+
+ if (!shpchp_nic_irq) {
+ shpchp_nic_irq = cfgspc_irq;
+ }
+
+ if (!shpchp_disk_irq) {
+ shpchp_disk_irq = cfgspc_irq;
+ }
+
+ dbg("shpchp_disk_irq, shpchp_nic_irq= %d, %d\n", shpchp_disk_irq, shpchp_nic_irq);
+
+ one_slot = rom_resource_table + sizeof(struct hrt);
+
+ i = readb(rom_resource_table + NUMBER_OF_ENTRIES);
+ dbg("number_of_entries = %d\n", i);
+
+ if (!readb(one_slot + SECONDARY_BUS))
+ return (1);
+
+ dbg("dev|IO base|length|MEMbase|length|PM base|length|PB SB MB\n");
+
+ while (i && readb(one_slot + SECONDARY_BUS)) {
+ u8 dev_func = readb(one_slot + DEV_FUNC);
+ u8 primary_bus = readb(one_slot + PRIMARY_BUS);
+ u8 secondary_bus = readb(one_slot + SECONDARY_BUS);
+ u8 max_bus = readb(one_slot + MAX_BUS);
+ u16 io_base = readw(one_slot + IO_BASE);
+ u16 io_length = readw(one_slot + IO_LENGTH);
+ u16 mem_base = readw(one_slot + MEM_BASE);
+ u16 mem_length = readw(one_slot + MEM_LENGTH);
+ u16 pre_mem_base = readw(one_slot + PRE_MEM_BASE);
+ u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH);
+
+ dbg("%2.2x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x |%2.2x %2.2x %2.2x\n",
+ dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length,
+ primary_bus, secondary_bus, max_bus);
+
+ /* If this entry isn't for our controller's bus, ignore it */
+ if (primary_bus != ctrl->slot_bus) {
+ i--;
+ one_slot += sizeof(struct slot_rt);
+ continue;
+ }
+ /* find out if this entry is for an occupied slot */
+ temp_dword = 0xFFFFFFFF;
+ pci_bus->number = primary_bus;
+ pci_bus_read_config_dword(pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword);
+
+ dbg("temp_D_word = %x\n", temp_dword);
+
+ if (temp_dword != 0xFFFFFFFF) {
+ index = 0;
+ func = shpchp_slot_find(primary_bus, dev_func >> 3, 0);
+
+ while (func && (func->function != (dev_func & 0x07))) {
+ dbg("func = %p b:d:f(%x:%x:%x)\n", func, primary_bus, dev_func >> 3, index);
+ func = shpchp_slot_find(primary_bus, dev_func >> 3, index++);
+ }
+
+ /* If we can't find a match, skip this table entry */
+ if (!func) {
+ i--;
+ one_slot += sizeof(struct slot_rt);
+ continue;
+ }
+ /* this may not work and shouldn't be used */
+ if (secondary_bus != primary_bus)
+ bridged_slot = 1;
+ else
+ bridged_slot = 0;
+
+ populated_slot = 1;
+ } else {
+ populated_slot = 0;
+ bridged_slot = 0;
+ }
+ dbg("slot populated =%s \n", populated_slot?"yes":"no");
+
+ /* If we've got a valid IO base, use it */
+
+ temp_ulong = io_base + io_length;
+
+ if ((io_base) && (temp_ulong <= 0x10000)) {
+ io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!io_node)
+ return -ENOMEM;
+
+ io_node->base = (ulong)io_base;
+ io_node->length = (ulong)io_length;
+ dbg("found io_node(base, length) = %x, %x\n", io_node->base, io_node->length);
+
+ if (!populated_slot) {
+ io_node->next = ctrl->io_head;
+ ctrl->io_head = io_node;
+ } else {
+ io_node->next = func->io_head;
+ func->io_head = io_node;
+ }
+ }
+
+ /* If we've got a valid memory base, use it */
+ temp_ulong = mem_base + mem_length;
+ if ((mem_base) && (temp_ulong <= 0x10000)) {
+ mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!mem_node)
+ return -ENOMEM;
+
+ mem_node->base = (ulong)mem_base << 16;
+ mem_node->length = (ulong)(mem_length << 16);
+ dbg("found mem_node(base, length) = %x, %x\n", mem_node->base, mem_node->length);
+
+ if (!populated_slot) {
+ mem_node->next = ctrl->mem_head;
+ ctrl->mem_head = mem_node;
+ } else {
+ mem_node->next = func->mem_head;
+ func->mem_head = mem_node;
+ }
+ }
+
+ /*
+ * If we've got a valid prefetchable memory base, and
+ * the base + length isn't greater than 0xFFFF
+ */
+ temp_ulong = pre_mem_base + pre_mem_length;
+ if ((pre_mem_base) && (temp_ulong <= 0x10000)) {
+ p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!p_mem_node)
+ return -ENOMEM;
+
+ p_mem_node->base = (ulong)pre_mem_base << 16;
+ p_mem_node->length = (ulong)pre_mem_length << 16;
+ dbg("found p_mem_node(base, length) = %x, %x\n", p_mem_node->base, p_mem_node->length);
+
+ if (!populated_slot) {
+ p_mem_node->next = ctrl->p_mem_head;
+ ctrl->p_mem_head = p_mem_node;
+ } else {
+ p_mem_node->next = func->p_mem_head;
+ func->p_mem_head = p_mem_node;
+ }
+ }
+
+ /*
+ * If we've got a valid bus number, use it
+ * The second condition is to ignore bus numbers on
+ * populated slots that don't have PCI-PCI bridges
+ */
+ if (secondary_bus && (secondary_bus != primary_bus)) {
+ bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!bus_node)
+ return -ENOMEM;
+
+ bus_node->base = (ulong)secondary_bus;
+ bus_node->length = (ulong)(max_bus - secondary_bus + 1);
+ dbg("found bus_node(base, length) = %x, %x\n", bus_node->base, bus_node->length);
+
+ if (!populated_slot) {
+ bus_node->next = ctrl->bus_head;
+ ctrl->bus_head = bus_node;
+ } else {
+ bus_node->next = func->bus_head;
+ func->bus_head = bus_node;
+ }
+ }
+
+ i--;
+ one_slot += sizeof(struct slot_rt);
+ }
+
+ /* If all of the following fail, we don't have any resources for hot plug add */
+ rc = 1;
+ rc &= shpchp_resource_sort_and_combine(&(ctrl->mem_head));
+ rc &= shpchp_resource_sort_and_combine(&(ctrl->p_mem_head));
+ rc &= shpchp_resource_sort_and_combine(&(ctrl->io_head));
+ rc &= shpchp_resource_sort_and_combine(&(ctrl->bus_head));
+
+ return (rc);
+}
+
+int shpchprm_set_hpp(
+ struct controller *ctrl,
+ struct pci_func *func,
+ u8 card_type)
+{
+ u32 rc;
+ u8 temp_byte;
+ struct pci_bus lpci_bus, *pci_bus;
+ unsigned int devfn;
+ memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ temp_byte = 0x40; /* hard coded value for LT */
+ if (card_type == PCI_HEADER_TYPE_BRIDGE) {
+ /* set subordinate Latency Timer */
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
+ if (rc) {
+ dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus,
+ func->device, func->function);
+ return rc;
+ }
+ }
+
+ /* set base Latency Timer */
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
+ if (rc) {
+ dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
+ return rc;
+ }
+
+ /* set Cache Line size */
+ temp_byte = 0x08; /* hard coded value for CLS */
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
+ if (rc) {
+ dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
+ }
+
+ /* set enable_perr */
+ /* set enable_serr */
+
+ return rc;
+}
+
+void shpchprm_enable_card(
+ struct controller *ctrl,
+ struct pci_func *func,
+ u8 card_type)
+{
+ u16 command, bcommand;
+ struct pci_bus lpci_bus, *pci_bus;
+ unsigned int devfn;
+ int rc;
+
+ memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
+ command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR
+ | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
+ | PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
+
+ if (card_type == PCI_HEADER_TYPE_BRIDGE) {
+ rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
+ bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR
+ | PCI_BRIDGE_CTL_NO_ISA;
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
+ }
+}
+
+static int legacy_shpchprm_init_pci(void)
+{
+ shpchp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN);
+ if (!shpchp_rom_start) {
+ err("Could not ioremap memory region for ROM\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int shpchprm_init(enum php_ctlr_type ctrl_type)
+{
+ int retval;
+
+ switch (ctrl_type) {
+ case PCI:
+ retval = legacy_shpchprm_init_pci();
+ break;
+ default:
+ retval = -ENODEV;
+ break;
+ }
+
+ return retval;
+}
diff --git a/drivers/pci/hotplug/shpchprm_legacy.h b/drivers/pci/hotplug/shpchprm_legacy.h
new file mode 100644
index 000000000000..29ccea5e57e5
--- /dev/null
+++ b/drivers/pci/hotplug/shpchprm_legacy.h
@@ -0,0 +1,113 @@
+/*
+ * SHPCHPRM Legacy: PHP Resource Manager for Non-ACPI/Legacy platform using HRT
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+
+#ifndef _SHPCHPRM_LEGACY_H_
+#define _SHPCHPRM_LEGACY_H_
+
+#define ROM_PHY_ADDR 0x0F0000
+#define ROM_PHY_LEN 0x00FFFF
+
+struct slot_rt {
+ u8 dev_func;
+ u8 primary_bus;
+ u8 secondary_bus;
+ u8 max_bus;
+ u16 io_base;
+ u16 io_length;
+ u16 mem_base;
+ u16 mem_length;
+ u16 pre_mem_base;
+ u16 pre_mem_length;
+} __attribute__ ((packed));
+
+/* offsets to the hotplug slot resource table registers based on the above structure layout */
+enum slot_rt_offsets {
+ DEV_FUNC = offsetof(struct slot_rt, dev_func),
+ PRIMARY_BUS = offsetof(struct slot_rt, primary_bus),
+ SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus),
+ MAX_BUS = offsetof(struct slot_rt, max_bus),
+ IO_BASE = offsetof(struct slot_rt, io_base),
+ IO_LENGTH = offsetof(struct slot_rt, io_length),
+ MEM_BASE = offsetof(struct slot_rt, mem_base),
+ MEM_LENGTH = offsetof(struct slot_rt, mem_length),
+ PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base),
+ PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length),
+};
+
+struct hrt {
+ char sig0;
+ char sig1;
+ char sig2;
+ char sig3;
+ u16 unused_IRQ;
+ u16 PCIIRQ;
+ u8 number_of_entries;
+ u8 revision;
+ u16 reserved1;
+ u32 reserved2;
+} __attribute__ ((packed));
+
+/* offsets to the hotplug resource table registers based on the above structure layout */
+enum hrt_offsets {
+ SIG0 = offsetof(struct hrt, sig0),
+ SIG1 = offsetof(struct hrt, sig1),
+ SIG2 = offsetof(struct hrt, sig2),
+ SIG3 = offsetof(struct hrt, sig3),
+ UNUSED_IRQ = offsetof(struct hrt, unused_IRQ),
+ PCIIRQ = offsetof(struct hrt, PCIIRQ),
+ NUMBER_OF_ENTRIES = offsetof(struct hrt, number_of_entries),
+ REVISION = offsetof(struct hrt, revision),
+ HRT_RESERVED1 = offsetof(struct hrt, reserved1),
+ HRT_RESERVED2 = offsetof(struct hrt, reserved2),
+};
+
+struct irq_info {
+ u8 bus, devfn; /* bus, device and function */
+ struct {
+ u8 link; /* IRQ line ID, chipset dependent, 0=not routed */
+ u16 bitmap; /* Available IRQs */
+ } __attribute__ ((packed)) irq[4];
+ u8 slot; /* slot number, 0=onboard */
+ u8 rfu;
+} __attribute__ ((packed));
+
+struct irq_routing_table {
+ u32 signature; /* PIRQ_SIGNATURE should be here */
+ u16 version; /* PIRQ_VERSION */
+ u16 size; /* Table size in bytes */
+ u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
+ u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
+ u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
+ u32 miniport_data; /* Crap */
+ u8 rfu[11];
+ u8 checksum; /* Modulo 256 checksum must give zero */
+ struct irq_info slots[0];
+} __attribute__ ((packed));
+
+#endif /* _SHPCHPRM_LEGACY_H_ */
diff --git a/drivers/pci/hotplug/shpchprm_nonacpi.c b/drivers/pci/hotplug/shpchprm_nonacpi.c
new file mode 100644
index 000000000000..88f4d9f41886
--- /dev/null
+++ b/drivers/pci/hotplug/shpchprm_nonacpi.c
@@ -0,0 +1,434 @@
+/*
+ * SHPCHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#ifdef CONFIG_IA64
+#include <asm/iosapic.h>
+#endif
+#include "shpchp.h"
+#include "shpchprm.h"
+#include "shpchprm_nonacpi.h"
+
+void shpchprm_cleanup(void)
+{
+ return;
+}
+
+int shpchprm_print_pirt(void)
+{
+ return 0;
+}
+
+int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum)
+{
+ int offset = devnum - ctrl->slot_device_offset;
+
+ dbg("%s: ctrl->slot_num_inc %d, offset %d\n", __FUNCTION__, ctrl->slot_num_inc, offset);
+ *sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc * offset);
+ return 0;
+}
+
+static void print_pci_resource ( struct pci_resource *aprh)
+{
+ struct pci_resource *res;
+
+ for (res = aprh; res; res = res->next)
+ dbg(" base= 0x%x length= 0x%x\n", res->base, res->length);
+}
+
+
+static void phprm_dump_func_res( struct pci_func *fun)
+{
+ struct pci_func *func = fun;
+
+ if (func->bus_head) {
+ dbg(": BUS Resources:\n");
+ print_pci_resource (func->bus_head);
+ }
+ if (func->io_head) {
+ dbg(": IO Resources:\n");
+ print_pci_resource (func->io_head);
+ }
+ if (func->mem_head) {
+ dbg(": MEM Resources:\n");
+ print_pci_resource (func->mem_head);
+ }
+ if (func->p_mem_head) {
+ dbg(": PMEM Resources:\n");
+ print_pci_resource (func->p_mem_head);
+ }
+}
+
+static int phprm_get_used_resources (
+ struct controller *ctrl,
+ struct pci_func *func
+ )
+{
+ return shpchp_save_used_resources (ctrl, func, !DISABLE_CARD);
+}
+
+static int phprm_delete_resource(
+ struct pci_resource **aprh,
+ ulong base,
+ ulong size)
+{
+ struct pci_resource *res;
+ struct pci_resource *prevnode;
+ struct pci_resource *split_node;
+ ulong tbase;
+
+ shpchp_resource_sort_and_combine(aprh);
+
+ for (res = *aprh; res; res = res->next) {
+ if (res->base > base)
+ continue;
+
+ if ((res->base + res->length) < (base + size))
+ continue;
+
+ if (res->base < base) {
+ tbase = base;
+
+ if ((res->length - (tbase - res->base)) < size)
+ continue;
+
+ split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!split_node)
+ return -ENOMEM;
+
+ split_node->base = res->base;
+ split_node->length = tbase - res->base;
+ res->base = tbase;
+ res->length -= split_node->length;
+
+ split_node->next = res->next;
+ res->next = split_node;
+ }
+
+ if (res->length >= size) {
+ split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ if (!split_node)
+ return -ENOMEM;
+
+ split_node->base = res->base + size;
+ split_node->length = res->length - size;
+ res->length = size;
+
+ split_node->next = res->next;
+ res->next = split_node;
+ }
+
+ if (*aprh == res) {
+ *aprh = res->next;
+ } else {
+ prevnode = *aprh;
+ while (prevnode->next != res)
+ prevnode = prevnode->next;
+
+ prevnode->next = res->next;
+ }
+ res->next = NULL;
+ kfree(res);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int phprm_delete_resources(
+ struct pci_resource **aprh,
+ struct pci_resource *this
+ )
+{
+ struct pci_resource *res;
+
+ for (res = this; res; res = res->next)
+ phprm_delete_resource(aprh, res->base, res->length);
+
+ return 0;
+}
+
+
+static int configure_existing_function(
+ struct controller *ctrl,
+ struct pci_func *func
+ )
+{
+ int rc;
+
+ /* see how much resources the func has used. */
+ rc = phprm_get_used_resources (ctrl, func);
+
+ if (!rc) {
+ /* subtract the resources used by the func from ctrl resources */
+ rc = phprm_delete_resources (&ctrl->bus_head, func->bus_head);
+ rc |= phprm_delete_resources (&ctrl->io_head, func->io_head);
+ rc |= phprm_delete_resources (&ctrl->mem_head, func->mem_head);
+ rc |= phprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head);
+ if (rc)
+ warn("aCEF: cannot del used resources\n");
+ } else
+ err("aCEF: cannot get used resources\n");
+
+ return rc;
+}
+
+static int bind_pci_resources_to_slots ( struct controller *ctrl)
+{
+ struct pci_func *func, new_func;
+ int busn = ctrl->slot_bus;
+ int devn, funn;
+ u32 vid;
+
+ for (devn = 0; devn < 32; devn++) {
+ for (funn = 0; funn < 8; funn++) {
+ /*
+ if (devn == ctrl->device && funn == ctrl->function)
+ continue;
+ */
+ /* find out if this entry is for an occupied slot */
+ vid = 0xFFFFFFFF;
+
+ pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid);
+
+ if (vid != 0xFFFFFFFF) {
+ func = shpchp_slot_find(busn, devn, funn);
+ if (!func) {
+ memset(&new_func, 0, sizeof(struct pci_func));
+ new_func.bus = busn;
+ new_func.device = devn;
+ new_func.function = funn;
+ new_func.is_a_board = 1;
+ configure_existing_function(ctrl, &new_func);
+ phprm_dump_func_res(&new_func);
+ } else {
+ configure_existing_function(ctrl, func);
+ phprm_dump_func_res(func);
+ }
+ dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void phprm_dump_ctrl_res( struct controller *ctlr)
+{
+ struct controller *ctrl = ctlr;
+
+ if (ctrl->bus_head) {
+ dbg(": BUS Resources:\n");
+ print_pci_resource (ctrl->bus_head);
+ }
+ if (ctrl->io_head) {
+ dbg(": IO Resources:\n");
+ print_pci_resource (ctrl->io_head);
+ }
+ if (ctrl->mem_head) {
+ dbg(": MEM Resources:\n");
+ print_pci_resource (ctrl->mem_head);
+ }
+ if (ctrl->p_mem_head) {
+ dbg(": PMEM Resources:\n");
+ print_pci_resource (ctrl->p_mem_head);
+ }
+}
+
+/*
+ * phprm_find_available_resources
+ *
+ * Finds available memory, IO, and IRQ resources for programming
+ * devices which may be added to the system
+ * this function is for hot plug ADD!
+ *
+ * returns 0 if success
+ */
+int shpchprm_find_available_resources(struct controller *ctrl)
+{
+ struct pci_func func;
+ u32 rc;
+
+ memset(&func, 0, sizeof(struct pci_func));
+
+ func.bus = ctrl->bus;
+ func.device = ctrl->device;
+ func.function = ctrl->function;
+ func.is_a_board = 1;
+
+ /* Get resources for this PCI bridge */
+ rc = shpchp_save_used_resources (ctrl, &func, !DISABLE_CARD);
+ dbg("%s: shpchp_save_used_resources rc = %d\n", __FUNCTION__, rc);
+
+ if (func.mem_head)
+ func.mem_head->next = ctrl->mem_head;
+ ctrl->mem_head = func.mem_head;
+
+ if (func.p_mem_head)
+ func.p_mem_head->next = ctrl->p_mem_head;
+ ctrl->p_mem_head = func.p_mem_head;
+
+ if (func.io_head)
+ func.io_head->next = ctrl->io_head;
+ ctrl->io_head = func.io_head;
+
+ if(func.bus_head)
+ func.bus_head->next = ctrl->bus_head;
+ ctrl->bus_head = func.bus_head;
+ if (ctrl->bus_head)
+ phprm_delete_resource(&ctrl->bus_head, ctrl->pci_dev->subordinate->number, 1);
+
+ dbg("%s:pre-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
+ phprm_dump_ctrl_res(ctrl);
+ bind_pci_resources_to_slots (ctrl);
+
+ dbg("%s:post-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
+ phprm_dump_ctrl_res(ctrl);
+
+
+ /* If all of the following fail, we don't have any resources for hot plug add */
+ rc = 1;
+ rc &= shpchp_resource_sort_and_combine(&(ctrl->mem_head));
+ rc &= shpchp_resource_sort_and_combine(&(ctrl->p_mem_head));
+ rc &= shpchp_resource_sort_and_combine(&(ctrl->io_head));
+ rc &= shpchp_resource_sort_and_combine(&(ctrl->bus_head));
+
+ return (rc);
+}
+
+int shpchprm_set_hpp(
+ struct controller *ctrl,
+ struct pci_func *func,
+ u8 card_type)
+{
+ u32 rc;
+ u8 temp_byte;
+ struct pci_bus lpci_bus, *pci_bus;
+ unsigned int devfn;
+ memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ temp_byte = 0x40; /* hard coded value for LT */
+ if (card_type == PCI_HEADER_TYPE_BRIDGE) {
+ /* set subordinate Latency Timer */
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
+
+ if (rc) {
+ dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus,
+ func->device, func->function);
+ return rc;
+ }
+ }
+
+ /* set base Latency Timer */
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
+
+ if (rc) {
+ dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
+ return rc;
+ }
+
+ /* set Cache Line size */
+ temp_byte = 0x08; /* hard coded value for CLS */
+
+ rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
+
+ if (rc) {
+ dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
+ }
+
+ /* set enable_perr */
+ /* set enable_serr */
+
+ return rc;
+}
+
+void shpchprm_enable_card(
+ struct controller *ctrl,
+ struct pci_func *func,
+ u8 card_type)
+{
+ u16 command, bcommand;
+ struct pci_bus lpci_bus, *pci_bus;
+ unsigned int devfn;
+ int rc;
+
+ memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
+ pci_bus = &lpci_bus;
+ pci_bus->number = func->bus;
+ devfn = PCI_DEVFN(func->device, func->function);
+
+ rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
+
+ command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR
+ | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
+ | PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
+
+ if (card_type == PCI_HEADER_TYPE_BRIDGE) {
+
+ rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
+
+ bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR
+ | PCI_BRIDGE_CTL_NO_ISA;
+
+ rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
+ }
+}
+
+static int legacy_shpchprm_init_pci(void)
+{
+ return 0;
+}
+
+int shpchprm_init(enum php_ctlr_type ctrl_type)
+{
+ int retval;
+
+ switch (ctrl_type) {
+ case PCI:
+ retval = legacy_shpchprm_init_pci();
+ break;
+ default:
+ retval = -ENODEV;
+ break;
+ }
+
+ return retval;
+}
diff --git a/drivers/pci/hotplug/shpchprm_nonacpi.h b/drivers/pci/hotplug/shpchprm_nonacpi.h
new file mode 100644
index 000000000000..6bc8668023c3
--- /dev/null
+++ b/drivers/pci/hotplug/shpchprm_nonacpi.h
@@ -0,0 +1,56 @@
+/*
+ * SHPCHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * Copyright (C) 2003-2004 Intel Corporation
+ *
+ * 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 <greg@kroah.com>, <dely.l.sy@intel.com>
+ *
+ */
+
+#ifndef _SHPCHPRM_NONACPI_H_
+#define _SHPCHPRM_NONACPI_H_
+
+struct irq_info {
+ u8 bus, devfn; /* bus, device and function */
+ struct {
+ u8 link; /* IRQ line ID, chipset dependent, 0=not routed */
+ u16 bitmap; /* Available IRQs */
+ } __attribute__ ((packed)) irq[4];
+ u8 slot; /* slot number, 0=onboard */
+ u8 rfu;
+} __attribute__ ((packed));
+
+struct irq_routing_table {
+ u32 signature; /* PIRQ_SIGNATURE should be here */
+ u16 version; /* PIRQ_VERSION */
+ u16 size; /* Table size in bytes */
+ u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
+ u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
+ u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
+ u32 miniport_data; /* Crap */
+ u8 rfu[11];
+ u8 checksum; /* Modulo 256 checksum must give zero */
+ struct irq_info slots[0];
+} __attribute__ ((packed));
+
+#endif /* _SHPCHPRM_NONACPI_H_ */