summaryrefslogtreecommitdiff
path: root/drivers/hotplug
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@athlon.transmeta.com>2002-02-04 20:32:43 -0800
committerLinus Torvalds <torvalds@athlon.transmeta.com>2002-02-04 20:32:43 -0800
commita8a2069f432c5597bdf9c83ab3045b9ef32ab5e3 (patch)
tree06d1047415e70b9a6cbd0567ae1202433530dd46 /drivers/hotplug
parent5db5272c0a5cd37e5a697e4750fbc4ce6317b7dc (diff)
v2.4.14.1 -> v2.4.14.2
- Ivan Kokshaysky: fix alpha dec_and_lock with modules, for alpha config entry - Kai Germaschewski: ISDN updates - Jeff Garzik: network driver updates, sysv fs update - Kai Mäkisara: SCSI tape update - Alan Cox: large drivers merge - Nikita Danilov: reiserfs procfs information - Andrew Morton: ext3 merge - Christoph Hellwig: vxfs livelock fix - Trond Myklebust: NFS updates - Jens Axboe: cpqarray + cciss dequeue fix - Tim Waugh: parport_serial base_baud setting - Matthew Dharm: usb-storage Freecom driver fixes - Dave McCracken: wait4() thread group race fix
Diffstat (limited to 'drivers/hotplug')
-rw-r--r--drivers/hotplug/Config.in12
-rw-r--r--drivers/hotplug/Makefile34
-rw-r--r--drivers/hotplug/cpqphp.h751
-rw-r--r--drivers/hotplug/cpqphp_core.c1439
-rw-r--r--drivers/hotplug/cpqphp_ctrl.c3047
-rw-r--r--drivers/hotplug/cpqphp_nvram.c652
-rw-r--r--drivers/hotplug/cpqphp_nvram.h57
-rw-r--r--drivers/hotplug/cpqphp_pci.c1726
-rw-r--r--drivers/hotplug/cpqphp_proc.c192
-rw-r--r--drivers/hotplug/pci_hotplug.h160
-rw-r--r--drivers/hotplug/pci_hotplug_core.c1132
-rw-r--r--drivers/hotplug/pci_hotplug_util.c403
12 files changed, 9605 insertions, 0 deletions
diff --git a/drivers/hotplug/Config.in b/drivers/hotplug/Config.in
new file mode 100644
index 000000000000..c47588c2932a
--- /dev/null
+++ b/drivers/hotplug/Config.in
@@ -0,0 +1,12 @@
+#
+# PCI Hotplug support
+#
+mainmenu_option next_comment
+comment 'PCI Hotplug Support'
+
+dep_tristate 'Support for PCI Hotplug (EXPERIMENTAL)' CONFIG_HOTPLUG_PCI $CONFIG_DDFS $CONFIG_EXPERIMENTAL
+
+dep_tristate ' Compaq PCI Hotplug driver' CONFIG_HOTPLUG_PCI_COMPAQ $CONFIG_HOTPLUG_PCI
+dep_mbool ' Save configuration into NVRAM on Compaq servers' CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM $CONFIG_HOTPLUG_PCI_COMPAQ
+
+endmenu
diff --git a/drivers/hotplug/Makefile b/drivers/hotplug/Makefile
new file mode 100644
index 000000000000..63cd2165c076
--- /dev/null
+++ b/drivers/hotplug/Makefile
@@ -0,0 +1,34 @@
+#
+# Makefile for the Linux kernel pci hotplug controller drivers.
+#
+
+O_TARGET := vmlinux-obj.o
+
+list-multi := cpqphp.o pci_hotplug.o
+
+export-objs := pci_hotplug_core.o pci_hotplug_util.o
+
+obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o
+obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o
+
+pci_hotplug-objs := pci_hotplug_core.o \
+ pci_hotplug_util.o
+
+cpqphp-objs := cpqphp_core.o \
+ cpqphp_ctrl.o \
+ cpqphp_proc.o \
+ cpqphp_pci.o
+
+ifeq ($(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM),y)
+ cpqphp-objs += cpqphp_nvram.o
+endif
+
+
+include $(TOPDIR)/Rules.make
+
+pci_hotplug.o: $(pci_hotplug-objs)
+ $(LD) -r -o $@ $(pci_hotplug-objs)
+
+cpqphp.o: $(cpqphp-objs)
+ $(LD) -r -o $@ $(cpqphp-objs)
+
diff --git a/drivers/hotplug/cpqphp.h b/drivers/hotplug/cpqphp.h
new file mode 100644
index 000000000000..618bf2c739d2
--- /dev/null
+++ b/drivers/hotplug/cpqphp.h
@@ -0,0 +1,751 @@
+/*
+ * 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 <asm/io.h> /* for read? and write? functions */
+
+
+#if !defined(CONFIG_HOTPLUG_PCI_COMPAQ_MODULE)
+ #define MY_NAME "cpqphp.o"
+#else
+ #define MY_NAME THIS_MODULE->name
+#endif
+
+#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 */
+} __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),
+};
+
+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;
+};
+
+#define SLOT_MAGIC 0x67267321
+struct slot {
+ u32 magic;
+ 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 *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 *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_ops *pci_ops;
+ struct proc_dir_entry* proc_entry;
+ struct proc_dir_entry* proc_entry2;
+ struct event_info event_queue[10];
+ struct slot *slot;
+ u8 next_event;
+ u8 interrupt;
+ u8 bus;
+ u8 device;
+ u8 function;
+ u8 rev;
+ u8 slot_device_offset;
+ u8 first_slot;
+ u8 add_support;
+ u8 push_flag;
+ u8 speed; /* 0 = 33MHz, 1 = 66MHz */
+ u8 speed_capability; /* 0 = 33MHz, 1 = 66MHz */
+ 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;
+ char proc_name[20];
+ char proc_name2[20];
+ struct tq_struct int_task_event;
+ wait_queue_head_t queue; /* sleep & wake process */
+};
+
+#define CTRL_SPEED_33MHz 0
+#define CTRL_SPEED_66MHz 1
+
+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 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"
+
+
+/* Proc functions for the hotplug controller info */
+#ifdef CONFIG_PROC_FS
+extern int cpqhp_proc_init_ctrl (void);
+extern int cpqhp_proc_destroy_ctrl (void);
+extern int cpqhp_proc_create_ctrl (struct controller *ctrl);
+extern int cpqhp_proc_remove_ctrl (struct controller *ctrl);
+#else
+static inline int cpqhp_proc_init_ctrl (void)
+{
+ return 0;
+}
+static inline int cpqhp_proc_destroy_ctrl (void)
+{
+ return 0;
+}
+static inline int cpqhp_proc_create_ctrl (struct controller *ctrl)
+{
+ return 0;
+}
+static inline int cpqhp_proc_remove_ctrl (struct controller *ctrl)
+{
+ return 0;
+}
+#endif
+
+
+/* controller functions */
+extern void cpqhp_pushbutton_thread (unsigned long event_pointer);
+extern void cpqhp_ctrl_intr (int IRQ, struct controller *ctrl_input, struct pt_regs *regs);
+extern int cpqhp_find_available_resources (struct controller *ctrl, void *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 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 */
+
+
+/* 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;
+}
+
+/*
+ * 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 |= (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);
+}
+
+
+static inline u8 get_controller_speed (struct controller *ctrl)
+{
+ u16 misc;
+
+ misc = readw(ctrl->hpc_reg + MISC);
+ return (misc & 0x0800) ? 1 : 0;
+}
+
+
+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;
+
+ if (slot == NULL)
+ return 1;
+
+ 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;
+
+ if (slot == NULL)
+ return 1;
+
+ 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;
+
+ if (slot == NULL)
+ return 1;
+
+ hp_slot = slot->device - ctrl->slot_device_offset;
+ dbg(__FUNCTION__": slot->device = %d, ctrl->slot_device_offset = %d \n", 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;
+
+ if (slot == NULL)
+ return 0;
+
+ 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(__FUNCTION__" - start\n");
+ add_wait_queue(&ctrl->queue, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ /* Sleep for up to 1 second to wait for the LED to change. */
+ schedule_timeout(1*HZ);
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&ctrl->queue, &wait);
+ if (signal_pending(current))
+ retval = -EINTR;
+
+ dbg(__FUNCTION__" - end\n");
+ return retval;
+}
+
+#endif
+
diff --git a/drivers/hotplug/cpqphp_core.c b/drivers/hotplug/cpqphp_core.c
new file mode 100644
index 000000000000..5b4861915c72
--- /dev/null
+++ b/drivers/hotplug/cpqphp_core.c
@@ -0,0 +1,1439 @@
+/*
+ * 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/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include "cpqphp.h"
+#include "cpqphp_nvram.h"
+#include "../../arch/i386/kernel/pci-i386.h" /* horrible hack showing how processor dependant we are... */
+
+
+/* Global variables */
+int cpqhp_debug;
+struct controller *cpqhp_ctrl_list; /* = NULL */
+struct pci_func *cpqhp_slot_list[256];
+
+/* local variables */
+static void *smbios_table;
+static void *smbios_start;
+static void *cpqhp_rom_start;
+static u8 power_mode;
+static int debug;
+
+#define DRIVER_VERSION "0.9.6"
+#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_PARM(power_mode, "b");
+MODULE_PARM_DESC(power_mode, "Power mode enabled or not");
+
+MODULE_PARM(debug, "i");
+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 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,
+};
+
+
+static inline int is_slot64bit (struct slot *slot)
+{
+ if (!slot || !slot->p_sm_slot)
+ return 0;
+
+ if (readb(slot->p_sm_slot + SMBIOS_SLOT_WIDTH) == 0x06)
+ return 1;
+
+ return 0;
+}
+
+static inline int is_slot66mhz (struct slot *slot)
+{
+ if (!slot || !slot->p_sm_slot)
+ return 0;
+
+ if (readb(slot->p_sm_slot + SMBIOS_SLOT_TYPE) == 0x0E)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * detect_SMBIOS_pointer - find the system Management BIOS Table in the specified region of memory.
+ *
+ * @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 * detect_SMBIOS_pointer(void *begin, void *end)
+{
+ void *fp;
+ void *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
+ *
+ * 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 * get_subsequent_smbios_entry(void *smbios_start, void *smbios_table, void *curr)
+{
+ u8 bail = 0;
+ u8 previous_byte = 1;
+ void *p_temp;
+ void *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 *get_SMBIOS_entry (void *smbios_start, void *smbios_table, u8 type, void * 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 int ctrl_slot_setup (struct controller * ctrl, void *smbios_start, void *smbios_table)
+{
+ struct slot *new_slot;
+ u8 number_of_slots;
+ u8 slot_device;
+ u8 slot_number;
+ u8 ctrl_slot;
+ u32 tempdword;
+ void *slot_entry= NULL;
+ int result;
+
+ dbg(__FUNCTION__"\n");
+
+ 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 = (struct slot *) kmalloc(sizeof(struct slot), GFP_KERNEL);
+ if (!new_slot)
+ return -ENOMEM;
+
+ memset(new_slot, 0, sizeof(struct slot));
+ new_slot->hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL);
+ if (!new_slot->hotplug_slot) {
+ kfree (new_slot);
+ return -ENOMEM;
+ }
+ 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) {
+ kfree (new_slot->hotplug_slot);
+ kfree (new_slot);
+ return -ENOMEM;
+ }
+ 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) {
+ kfree (new_slot->hotplug_slot->info);
+ kfree (new_slot->hotplug_slot);
+ kfree (new_slot);
+ return -ENOMEM;
+ }
+
+ new_slot->magic = SLOT_MAGIC;
+ 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 == 1)
+ 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->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);
+ return result;
+ }
+
+ new_slot->next = ctrl->slot;
+ ctrl->slot = new_slot;
+
+ number_of_slots--;
+ slot_device++;
+ slot_number++;
+ }
+
+ return(0);
+}
+
+
+static int ctrl_slot_cleanup (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);
+ kfree(old_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_ops *ops, 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(__FUNCTION__" %p, %d, %d, %p\n", ops, bus_num, dev_num, slot);
+
+ bridgeSlot = 0xFF;
+
+ PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table();
+
+ 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 >> 3;
+ tslot = PCIIRQRoutingInfoLength->slots[loop].slot;
+
+ if ((tbus == bus_num) && (tdevice == dev_num)) {
+ *slot = tslot;
+
+ if (PCIIRQRoutingInfoLength != NULL) kfree(PCIIRQRoutingInfoLength );
+ return 0;
+ } else {
+ // Didn't 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't
+ // 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.
+ pci_read_config_dword_nodev (ops, tbus, tdevice, 0, PCI_REVISION_ID, &work);
+
+ if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
+ pci_read_config_dword_nodev (ops, tbus, 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;
+ if (PCIIRQRoutingInfoLength != NULL) kfree(PCIIRQRoutingInfoLength );
+ return 0;
+ }
+ if (PCIIRQRoutingInfoLength != NULL) 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;
+
+ hp_slot = func->device - ctrl->slot_device_offset;
+
+ if (func == NULL)
+ return(1);
+
+ // 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 = get_slot (hotplug_slot, __FUNCTION__);
+ struct controller *ctrl;
+ u8 bus;
+ u8 devfn;
+ u8 device;
+ u8 function;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name);
+
+ ctrl = slot->ctrl;
+ if (ctrl == NULL)
+ return -ENODEV;
+
+ 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 = get_slot (hotplug_slot, __FUNCTION__);
+ struct controller *ctrl;
+ u8 bus;
+ u8 devfn;
+ u8 device;
+ u8 function;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name);
+
+ ctrl = slot->ctrl;
+ if (ctrl == NULL)
+ return -ENODEV;
+
+ 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 = get_slot (hotplug_slot, __FUNCTION__);
+ struct controller *ctrl;
+ u8 bus;
+ u8 devfn;
+ u8 device;
+ u8 function;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name);
+
+ ctrl = slot->ctrl;
+ if (ctrl == NULL)
+ return -ENODEV;
+
+ 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 power_down_board, slot_func = %p, ctrl = %p\n", slot_func, ctrl);
+ return cpqhp_process_SS(ctrl, slot_func);
+}
+
+
+static int hardware_test (struct hotplug_slot *hotplug_slot, u32 value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ struct controller *ctrl;
+
+ dbg(__FUNCTION__"\n");
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ ctrl = slot->ctrl;
+ if (ctrl == NULL)
+ return -ENODEV;
+
+ return cpqhp_hardware_test (ctrl, value);
+}
+
+
+static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ struct controller *ctrl;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name);
+
+ ctrl = slot->ctrl;
+ if (ctrl == NULL)
+ return -ENODEV;
+
+ *value = get_slot_enabled(ctrl, slot);
+ return 0;
+}
+
+static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ struct controller *ctrl;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name);
+
+ ctrl = slot->ctrl;
+ if (ctrl == NULL)
+ return -ENODEV;
+
+ *value = cpq_get_attention_status(ctrl, slot);
+ return 0;
+}
+
+static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ struct controller *ctrl;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name);
+
+ ctrl = slot->ctrl;
+ if (ctrl == NULL)
+ return -ENODEV;
+
+ *value = cpq_get_latch_status (ctrl, slot);
+
+ return 0;
+}
+
+static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+ struct controller *ctrl;
+
+ if (slot == NULL)
+ return -ENODEV;
+
+ dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name);
+
+ ctrl = slot->ctrl;
+ if (ctrl == NULL)
+ return -ENODEV;
+
+ *value = get_presence_status (ctrl, slot);
+
+ 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;
+ 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(__FUNCTION__" : pci_read_config_word failed\n");
+ 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(__FUNCTION__" : out of memory\n");
+ return -ENOMEM;
+ }
+ memset(ctrl, 0, sizeof(struct controller));
+
+ rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsystem_deviceid);
+ if (rc) {
+ err(__FUNCTION__" : pci_read_config_word failed\n");
+ 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:
+ switch (subsystem_deviceid) {
+ case PCI_SUB_HPC_ID:
+ /* Original 6500/7000 implementation */
+ ctrl->slot_switch_type = 1; // Switch is present
+ ctrl->speed_capability = CTRL_SPEED_33MHz;
+ ctrl->push_button = 0; // No pushbutton
+ ctrl->pci_config_space = 1; // Index/data access to working registers 0 = not supported, 1 = supported
+ ctrl->defeature_PHP = 1; // PHP is supported
+ ctrl->pcix_support = 0; // PCI-X not supported
+ ctrl->pcix_speed_capability = 0; // N/A since PCI-X not supported
+ break;
+ case PCI_SUB_HPC_ID2:
+ /* First Pushbutton implementation */
+ ctrl->push_flag = 1;
+ ctrl->slot_switch_type = 1; // Switch is present
+ ctrl->speed_capability = CTRL_SPEED_33MHz;
+ ctrl->push_button = 1; // Pushbutton is present
+ ctrl->pci_config_space = 1; // Index/data access to working registers 0 = not supported, 1 = supported
+ ctrl->defeature_PHP = 1; // PHP is supported
+ ctrl->pcix_support = 0; // PCI-X not supported
+ ctrl->pcix_speed_capability = 0; // N/A since PCI-X not supported
+ break;
+ case PCI_SUB_HPC_ID_INTC:
+ /* Third party (6500/7000) */
+ ctrl->slot_switch_type = 1; // Switch is present
+ ctrl->speed_capability = CTRL_SPEED_33MHz;
+ ctrl->push_button = 0; // No pushbutton
+ ctrl->pci_config_space = 1; // Index/data access to working registers 0 = not supported, 1 = supported
+ ctrl->defeature_PHP = 1; // PHP is supported
+ ctrl->pcix_support = 0; // PCI-X not supported
+ ctrl->pcix_speed_capability = 0; // N/A since PCI-X not supported
+ break;
+ case PCI_SUB_HPC_ID3:
+ /* First 66 Mhz implementation */
+ ctrl->push_flag = 1;
+ ctrl->slot_switch_type = 1; // Switch is present
+ ctrl->speed_capability = CTRL_SPEED_66MHz;
+ ctrl->push_button = 1; // Pushbutton is present
+ ctrl->pci_config_space = 1; // Index/data access to working registers 0 = not supported, 1 = supported
+ ctrl->defeature_PHP = 1; // PHP is supported
+ ctrl->pcix_support = 0; // PCI-X not supported
+ ctrl->pcix_speed_capability = 0; // N/A since PCI-X not supported
+ break;
+ default:
+ // TODO: Add SSIDs for CPQ systems that support PCI-X
+ 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 = CTRL_SPEED_66MHz;
+ } else {
+ ctrl->speed_capability = CTRL_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 %s\n", ctrl->speed_capability == CTRL_SPEED_33MHz ? "33MHz" : "66Mhz");
+ dbg (" slot_switch_type %s\n", ctrl->slot_switch_type == 0 ? "no switch" : "switch present");
+ dbg (" defeature_PHP %s\n", ctrl->defeature_PHP == 0 ? "PHP not supported" : "PHP supported");
+ dbg (" alternate_base_address %s\n", ctrl->alternate_base_address == 0 ? "not supported" : "supported");
+ dbg (" pci_config_space %s\n", ctrl->pci_config_space == 0 ? "not supported" : "supported");
+ dbg (" pcix_speed_capability %s\n", ctrl->pcix_speed_capability == 0 ? "not supported" : "supported");
+ dbg (" pcix_support %s\n", ctrl->pcix_support == 0 ? "not supported" : "supported");
+
+ ctrl->pci_dev = pdev;
+ ctrl->pci_ops = pdev->bus->ops;
+ ctrl->bus = pdev->bus->number;
+ ctrl->device = PCI_SLOT(pdev->devfn);
+ ctrl->function = PCI_FUNC(pdev->devfn);
+ ctrl->rev = rev;
+ dbg("bus device function rev: %d %d %d %d\n", ctrl->bus, ctrl->device, ctrl->function, 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_ctrl;
+ }
+
+ 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_ctrl;
+ }
+
+ 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
+ // TODO: Add PCI-X support
+ 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_ops, 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(__FUNCTION__": unable to save PCI configuration data, error %d\n", rc);
+ goto err_iounmap;
+ }
+
+ /*
+ * Get IO, memory, and IRQ resources for new devices
+ */
+ 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(__FUNCTION__": unable to save PCI configuration data, error %d\n", rc);
+ goto err_iounmap;
+ }
+
+ /* Mask all general input interrupts */
+ writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_MASK);
+
+ /* set up the interrupt */
+ ctrl->interrupt = pdev->irq;
+ dbg("HPC interrupt = %d \n", ctrl->interrupt);
+ if (request_irq(ctrl->interrupt,
+ (void (*)(int, void *, struct pt_regs *)) &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);
+
+ rc = cpqhp_proc_create_ctrl (ctrl);
+ if (rc) {
+ err("cpqhp_proc_create_ctrl failed\n");
+ goto err_free_irq;
+ }
+
+ 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_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;
+ }
+
+ 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;
+ }
+
+ retval = cpqhp_proc_init_ctrl();
+ if (retval)
+ goto error;
+
+ initialized = 1;
+
+ return retval;
+
+error:
+ if (cpqhp_rom_start)
+ iounmap(cpqhp_rom_start);
+ if (smbios_start)
+ iounmap(smbios_start);
+
+ return retval;
+}
+
+
+static void 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) {
+ cpqhp_proc_remove_ctrl (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);
+ }
+
+ 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);
+ }
+ }
+
+ remove_proc_entry("hpc", 0);
+
+ // 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[] __devinitdata = {
+ {
+ /* 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: "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;
+
+ result = pci_module_init(&cpqhpc_driver);
+ dbg("pci_module_init = %d\n", result);
+ if (result)
+ return result;
+ info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
+ return 0;
+}
+
+
+static void __exit cpqhpc_cleanup(void)
+{
+ dbg("cleaning up proc entries\n");
+ cpqhp_proc_destroy_ctrl();
+
+ 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/hotplug/cpqphp_ctrl.c b/drivers/hotplug/cpqphp_ctrl.c
new file mode 100644
index 000000000000..5fc061324884
--- /dev/null
+++ b/drivers/hotplug/cpqphp_ctrl.c
@@ -0,0 +1,3047 @@
+/*
+ * 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/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);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(delay);
+ remove_wait_queue(&delay_wait, &wait);
+ set_current_state(TASK_RUNNING);
+
+ 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;
+}
+
+
+/*
+ * find_slot
+ */
+static inline struct slot *find_slot (struct controller * ctrl, u8 device)
+{
+ struct slot *slot;
+
+ if (!ctrl)
+ return NULL;
+
+ 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 = find_slot(ctrl, hp_slot + (readb(ctrl->hpc_reg + SLOT_MASK) >> 4));
+
+ // 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
+ *
+ * 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 = 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 = (struct pci_resource*) 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;
+ 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 = 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) {
+ 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;
+ 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 = (struct pci_resource*) 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 = (struct pci_resource*) 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.
+ */
+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 (!(*head))
+ return(NULL);
+
+ 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 = (struct pci_resource*) 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 = (struct pci_resource*) 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;
+
+ // 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 ( cpqhp_resource_sort_and_combine(head) )
+ return(NULL);
+
+ if ( sort_by_size(head) )
+ return(NULL);
+
+ for (node = *head; node; node = node->next) {
+ dbg(__FUNCTION__": req_size =%x node=%p, base=%x, length=%x\n",
+ size, node, node->base, node->length);
+ if (node->length < size)
+ continue;
+
+ if (node->base & (size - 1)) {
+ dbg(__FUNCTION__": not aligned\n");
+ // 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 = (struct pci_resource*) 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(__FUNCTION__": too big\n");
+ // this one is longer than we need
+ // so we'll make a new entry and split it up
+ split_node = (struct pci_resource*) 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(__FUNCTION__": 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);
+}
+
+
+/*
+ * cpqhp_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 cpqhp_resource_sort_and_combine(struct pci_resource **head)
+{
+ struct pci_resource *node1;
+ struct pci_resource *node2;
+ int out_of_order = 1;
+
+ dbg(__FUNCTION__": head = %p, *head = %p\n", 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);
+}
+
+
+void cpqhp_ctrl_intr(int IRQ, struct controller * ctrl, struct pt_regs *regs)
+{
+ u8 schedule_flag = 0;
+ 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;
+ }
+
+ 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 (__FUNCTION__" - waking up\n");
+ 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);
+ }
+
+ if (schedule_flag) {
+ up(&event_semaphore);
+ dbg("Signal event_semaphore\n");
+ mark_bh(IMMEDIATE_BH);
+ }
+
+}
+
+
+/**
+ * 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 = (struct pci_func *) kmalloc(sizeof(struct pci_func), 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;
+
+ 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 = 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;
+ 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);
+}
+
+
+/**
+ * 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;
+}
+
+
+/* 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;
+ 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 {
+ if (ctrl->speed == 1) {
+ // Wait for exclusive access to hardware
+ 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);
+
+ if (!(readl(ctrl->hpc_reg + NON_INT_INPUT) & (0x01 << 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);
+
+ // Done with exclusive hardware access
+ up(&ctrl->crit_sect);
+
+ if (rc)
+ return(rc);
+ }
+
+ // Wait for exclusive access to hardware
+ 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);
+
+ // Done with exclusive hardware access
+ 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.
+
+ // Wait for exclusive access to hardware
+ 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);
+
+ // Done with exclusive hardware access
+ 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.
+
+ // Wait for exclusive access to hardware
+ 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);
+
+ // Done with exclusive hardware access
+ up(&ctrl->crit_sect);
+
+ return(rc);
+ }
+ // Done configuring so turn LED on full time
+
+ // Wait for exclusive access to hardware
+ down(&ctrl->crit_sect);
+
+ green_LED_on (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ // Wait for SOBS to be unset
+ wait_for_ctrl_irq (ctrl);
+
+ // Done with exclusive hardware access
+ 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.
+
+ // Wait for exclusive access to hardware
+ 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);
+
+ // Done with exclusive hardware access
+ 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;
+ 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(__FUNCTION__": func->device, slot_offset, hp_slot = %d, %d ,%d\n",
+ func->device, ctrl->slot_device_offset, hp_slot);
+
+ if (ctrl->speed == 1) {
+ // Wait for exclusive access to hardware
+ 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);
+
+ if (!(readl(ctrl->hpc_reg + NON_INT_INPUT) & (0x01 << 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);
+
+ // Done with exclusive hardware access
+ up(&ctrl->crit_sect);
+
+ if (rc)
+ return(rc);
+ }
+ p_slot = find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ // turn on board and blink green LED
+
+ // Wait for exclusive access to hardware
+ dbg(__FUNCTION__": before down\n");
+ down(&ctrl->crit_sect);
+ dbg(__FUNCTION__": after down\n");
+
+ dbg(__FUNCTION__": before slot_enable\n");
+ slot_enable (ctrl, hp_slot);
+
+ dbg(__FUNCTION__": before green_LED_blink\n");
+ green_LED_blink (ctrl, hp_slot);
+
+ dbg(__FUNCTION__": before amber_LED_blink\n");
+ amber_LED_off (ctrl, hp_slot);
+
+ dbg(__FUNCTION__": before set_SOGO\n");
+ set_SOGO(ctrl);
+
+ // Wait for SOBS to be unset
+ dbg(__FUNCTION__": before wait_for_ctrl_irq\n");
+ wait_for_ctrl_irq (ctrl);
+ dbg(__FUNCTION__": after wait_for_ctrl_irq\n");
+
+ // Done with exclusive hardware access
+ dbg(__FUNCTION__": before up\n");
+ up(&ctrl->crit_sect);
+ dbg(__FUNCTION__": after up\n");
+
+ // Wait for ~1 second because of hot plug spec
+ dbg(__FUNCTION__": before long_delay\n");
+ long_delay(1*HZ);
+ dbg(__FUNCTION__": after long_delay\n");
+
+ dbg(__FUNCTION__": func status = %x\n", func->status);
+ // Check for a power fault
+ if (func->status == 0xFF) {
+ // power fault occurred, but it was benign
+ temp_register = 0xFFFFFFFF;
+ dbg(__FUNCTION__": temp register set to %x by power fault\n", temp_register);
+ rc = POWER_FAILURE;
+ func->status = 0;
+ } else {
+ // Get vendor/device ID u32
+ rc = pci_read_config_dword_nodev (ctrl->pci_ops, func->bus, func->device, func->function, PCI_VENDOR_ID, &temp_register);
+ dbg(__FUNCTION__": pci_read_config_dword returns %d\n", rc);
+ dbg(__FUNCTION__": temp_register is %x\n", temp_register);
+
+ if (rc != 0) {
+ // Something's wrong here
+ temp_register = 0xFFFFFFFF;
+ dbg(__FUNCTION__": temp register set to %x by error\n", 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(__FUNCTION__": back from configure_new_device\n");
+ 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) {
+ // Wait for exclusive access to hardware
+ 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);
+
+ // Done with exclusive hardware access
+ 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(__FUNCTION__": configure linux pci_dev structure\n");
+ 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);
+
+ // Wait for exclusive access to hardware
+ down(&ctrl->crit_sect);
+
+ green_LED_on (ctrl, hp_slot);
+
+ set_SOGO(ctrl);
+
+ // Wait for SOBS to be unset
+ 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);
+
+ 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);
+
+ // 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, 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 (func == NULL)
+ return(1);
+
+ if (cpqhp_unconfigure_device(func))
+ return(1);
+
+ device = func->device;
+
+ hp_slot = func->device - ctrl->slot_device_offset;
+ dbg("In "__FUNCTION__", hp_slot = %d\n", 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;
+
+ // Wait for exclusive access to hardware
+ 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);
+
+ // Done with exclusive hardware access
+ 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) {
+ // Out of memory
+ 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();
+
+ // New name
+ strcpy(current->comm, "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, 0, 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;
+ 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);
+ 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(buffer, 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);
+
+ p_slot = 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_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);
+
+ // Wait for exclusive access to hardware
+ down(&ctrl->crit_sect);
+
+ if (p_slot->state == BLINKINGOFF_STATE) {
+ // slot is on
+ // turn on green LED
+ dbg("turn on green LED\n");
+ green_LED_on (ctrl, hp_slot);
+ } else if (p_slot->state == BLINKINGON_STATE) {
+ // slot is off
+ // turn off green LED
+ 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);
+
+ // Done with exclusive hardware access
+ 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)) {
+ // 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);
+
+ 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);
+
+ // Done with exclusive hardware access
+ 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 "__FUNCTION__"\n");
+ 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 "__FUNCTION__"\n");
+ 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;
+
+ if (!ctrl)
+ return(1);
+
+ tempdword = 0;
+
+ device = func->device;
+ hp_slot = device - ctrl->slot_device_offset;
+ p_slot = 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) {
+ // Out of memory
+ 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(__FUNCTION__": rc = %d\n", 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;
+ struct slot* p_slot;
+ int physical_slot=0;
+
+ device = func->device;
+ func = cpqhp_slot_find(ctrl->bus, device, index++);
+ p_slot = find_slot(ctrl, device);
+ if (p_slot) {
+ physical_slot = p_slot->number;
+ }
+
+ // Make sure there are no video controllers here
+ while (func && !rc) {
+ // Check the Class Code
+ rc = pci_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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);
+}
+
+
+
+/**
+ * hardware_test - runs hardware tests
+ *
+ * For hot plug ctrl folks to play with.
+ * test_num is the number entered in the GUI
+ *
+ */
+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
+ save_LED = readl(ctrl->hpc_reg + LED_CONTROL); // so we can restore them later
+ work_LED = 0x01010101;
+ 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
+ work_LED = work_LED << 1;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+ long_delay((2*HZ)/10);
+ }
+ for (loop = 0; loop < num_of_slots; loop++) {
+ 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);
+ }
+ for (loop = 0; loop < num_of_slots; loop++) {
+ 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);
+ }
+ for (loop = 0; loop < num_of_slots; loop++) {
+ 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);
+ }
+
+ 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
+ work_LED = work_LED << 1;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+ long_delay((2*HZ)/10);
+ }
+ for (loop = 0; loop < num_of_slots; loop++) {
+ 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);
+ }
+ work_LED = 0x00000101;
+ writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
+ for (loop = 0; loop < num_of_slots; loop++) {
+ 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);
+ }
+ for (loop = 0; loop < num_of_slots; loop++) {
+ 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);
+ }
+
+
+ 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);
+ }
+
+ writel (save_LED, ctrl->hpc_reg + LED_CONTROL); // put it back the way it was
+
+ 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(__FUNCTION__"\n");
+ // Check for Multi-function device
+ rc = pci_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 0x0E, &temp_byte);
+ if (rc) {
+ dbg(__FUNCTION__": rc = %d\n", 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_read_config_dword_nodev (ctrl->pci_ops, func->bus, 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) {
+ // 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)
+{
+ int cloop;
+ u8 IRQ;
+ 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;
+ 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;
+
+ // Check for Bridge
+ rc = pci_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_byte_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_byte_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_byte_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_SUBORDINATE_BUS, temp_byte);
+ if (rc)
+ return rc;
+
+ // set subordinate Latency Timer and base Latency Timer
+ temp_byte = 0x40;
+ rc = pci_write_config_byte_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_SEC_LATENCY_TIMER, temp_byte);
+ if (rc)
+ return rc;
+ rc = pci_write_config_byte_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_LATENCY_TIMER, temp_byte);
+ if (rc)
+ return rc;
+
+ // set Cache Line size
+ temp_byte = 0x08;
+ rc = pci_write_config_byte_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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);
+ mem_node = get_max_resource(&(resources->mem_head), 0x100000);
+ p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000);
+ 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 = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ hold_IO_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ hold_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+ hold_p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+ if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) {
+ if (hold_bus_node)
+ kfree(hold_bus_node);
+ if (hold_IO_node)
+ kfree(hold_IO_node);
+ if (hold_mem_node)
+ kfree(hold_mem_node);
+ if (hold_p_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_write_config_byte_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_IO_BASE, temp_byte);
+
+ temp_byte = (io_node->base + io_node->length - 1) >> 8;
+ rc = pci_write_config_byte_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_MEMORY_BASE, temp_word);
+
+ temp_word = (mem_node->base + mem_node->length - 1) >> 16;
+ rc = pci_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_MEMORY_LIMIT, temp_word);
+ } else {
+ temp_word = 0xFFFF;
+ rc = pci_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_MEMORY_BASE, temp_word);
+
+ temp_word = 0x0000;
+ rc = pci_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_PREF_MEMORY_BASE, temp_word);
+
+ temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16;
+ rc = pci_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_PREF_MEMORY_LIMIT, temp_word);
+ } else {
+ temp_word = 0xFFFF;
+ rc = pci_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_PREF_MEMORY_BASE, temp_word);
+
+ temp_word = 0x0000;
+ rc = pci_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_read_config_dword_nodev (ctrl->pci_ops, hold_bus_node->base, device, 0, 0x00, &ID);
+
+ if (ID != 0xFFFFFFFF) { // device Present
+ // Setup slot structure.
+ new_slot = cpqhp_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);
+ dbg("configure_new_device rc=0x%x\n",rc);
+ } // End of IF (device in slot?)
+ } // End of FOR loop
+
+ if (rc) {
+ 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);
+ }
+ // 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) {
+ 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;
+ }
+ }
+ } // 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_write_config_byte_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_byte_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_IO_LIMIT, temp_byte);
+
+ return_resource(&(resources->io_head), io_node);
+ } else {
+ // it doesn't need any IO
+ temp_word = 0x0000;
+ pci_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_COMMAND, command);
+
+ // set Bridge Control Register
+ command = 0x07; // = PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_NO_ISA
+ rc = pci_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_BRIDGE_CONTROL, command);
+ } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
+ // Standard device
+ rc = pci_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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, device=%d, func=%d, offset=%d\n", func->bus, func->device, func->function, cloop);
+ rc = pci_write_config_dword_nodev(ctrl->pci_ops, func->bus, func->device, func->function, cloop, temp_register);
+
+ rc = pci_read_config_dword_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_dword_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_dword_nodev(ctrl->pci_ops, func->bus, func->device, func->function, cloop, base);
+ }
+ }
+ } // End of base register loop
+
+ // Figure out which interrupt pin this function uses
+ rc = pci_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 0x0B, &class_code);
+
+ if (class_code == PCI_BASE_CLASS_STORAGE) {
+ IRQ = cpqhp_disk_irq;
+ } else {
+ IRQ = cpqhp_nic_irq;
+ }
+ }
+
+ // IRQ Line
+ rc = pci_write_config_byte_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_byte_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_LATENCY_TIMER, temp_byte);
+
+ // Cache Line size
+ temp_byte = 0x08;
+ rc = pci_write_config_byte_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_CACHE_LINE_SIZE, temp_byte);
+
+ // disable ROM base Address
+ temp_dword = 0x00L;
+ rc = pci_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_COMMAND, temp_word);
+ } // 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/hotplug/cpqphp_nvram.c b/drivers/hotplug/cpqphp_nvram.c
new file mode 100644
index 000000000000..13a67d7b6487
--- /dev/null
+++ b/drivers/hotplug/cpqphp_nvram.c
@@ -0,0 +1,652 @@
+/*
+ * 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/miscdevice.h>
+#include <linux/slab.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 *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 *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 (__FUNCTION__" - returned %d\n", 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
+ xorl %%edx,%%edx
+ pushf
+ push %%cs
+ cli
+ call *%6"
+ : "=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 *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 *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, ctrl->device, &usedbytes, &available);
+ if (rc)
+ return(rc);
+
+ // The function Number
+ rc = add_byte( &pFill, ctrl->function, &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 *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 *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 != ctrl->device)
+ || (function != 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);
+
+ // 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))
+ 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))
+ 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))
+ 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))
+ 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))
+ 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))
+ 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))
+ return(2);
+
+ bus_node->length = *(u32*)p_byte;
+ p_byte += 4;
+
+
+ if (p_byte > ((u8*)p_EV_header + evbuffer_length))
+ 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 *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/hotplug/cpqphp_nvram.h b/drivers/hotplug/cpqphp_nvram.h
new file mode 100644
index 000000000000..64b793bcf74e
--- /dev/null
+++ b/drivers/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 *rom_start)
+{
+ return;
+}
+
+static inline int compaq_nvram_load (void *rom_start, struct controller *ctrl)
+{
+ return 0;
+}
+
+static inline int compaq_nvram_store (void *rom_start)
+{
+ return 0;
+}
+
+#else
+
+extern void compaq_nvram_init (void *rom_start);
+extern int compaq_nvram_load (void *rom_start, struct controller *ctrl);
+extern int compaq_nvram_store (void *rom_start);
+
+#endif
+
+#endif
+
diff --git a/drivers/hotplug/cpqphp_pci.c b/drivers/hotplug/cpqphp_pci.c
new file mode 100644
index 000000000000..7fb2401c7727
--- /dev/null
+++ b/drivers/hotplug/cpqphp_pci.c
@@ -0,0 +1,1726 @@
+/*
+ * 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/proc_fs.h>
+#include <linux/pci.h>
+#include "cpqphp.h"
+#include "cpqphp_nvram.h"
+#include "../../arch/i386/kernel/pci-i386.h" /* horrible hack showing how processor dependant we are... */
+
+
+u8 cpqhp_nic_irq;
+u8 cpqhp_disk_irq;
+
+static u16 unused_IRQ;
+
+
+static int is_pci_dev_in_use(struct pci_dev* dev)
+{
+ /*
+ * dev->driver will be set if the device is in use by a new-style
+ * driver -- otherwise, check the device's regions to see if any
+ * driver has claimed them
+ */
+
+ int i, inuse=0;
+
+ if (dev->driver) return 1; //assume driver feels responsible
+
+ for (i = 0; !dev->driver && !inuse && (i < 6); i++) {
+ if (!pci_resource_start(dev, i))
+ continue;
+
+ if (pci_resource_flags(dev, i) & IORESOURCE_IO)
+ inuse = check_region(pci_resource_start(dev, i),
+ pci_resource_len(dev, i));
+ else if (pci_resource_flags(dev, i) & IORESOURCE_MEM)
+ inuse = check_mem_region(pci_resource_start(dev, i),
+ pci_resource_len(dev, i));
+ }
+
+ return inuse;
+
+}
+
+
+static int pci_hp_remove_device(struct pci_dev *dev)
+{
+ if (is_pci_dev_in_use(dev)) {
+ err("***Cannot safely power down device -- "
+ "it appears to be in use***\n");
+ return -EBUSY;
+ }
+ pci_remove_device(dev);
+ return 0;
+}
+
+
+/*
+ * detect_HRT_floating_pointer
+ *
+ * find the Hot Plug Resource Table in the specified region of memory.
+ *
+ */
+static void *detect_HRT_floating_pointer(void *begin, void *end)
+{
+ void *fp;
+ void *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;
+}
+
+static int configure_visit_pci_dev (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus)
+{
+ struct pci_bus* bus = wrapped_bus->bus;
+ struct pci_dev* dev = wrapped_dev->dev;
+ struct pci_func *temp_func;
+ int i=0;
+
+ //We need to fix up the hotplug function representation with the linux representation
+ do {
+ temp_func = cpqhp_slot_find(dev->bus->number, dev->devfn >> 3, i++);
+ } while (temp_func && (temp_func->function != (dev->devfn & 0x07)));
+
+ if (temp_func) {
+ temp_func->pci_dev = dev;
+ } else {
+ //We did not even find a hotplug rep of the function, create it
+ //This code might be taken out if we can guarantee the creation of functions
+ //in parallel (hotplug and Linux at the same time).
+ dbg("@@@@@@@@@@@ cpqhp_slot_create in "__FUNCTION__"\n");
+ temp_func = cpqhp_slot_create(bus->number);
+ if (temp_func == NULL)
+ return -ENOMEM;
+ temp_func->pci_dev = dev;
+ }
+
+ //Create /proc/bus/pci proc entry for this device and bus device is on
+ //Notify the drivers of the change
+ if (temp_func->pci_dev) {
+ pci_proc_attach_device(temp_func->pci_dev);
+ pci_announce_device_to_drivers(temp_func->pci_dev);
+ }
+
+ 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 pci_func *temp_func;
+ int i=0;
+
+ //We need to remove the hotplug function representation with the linux representation
+ do {
+ temp_func = cpqhp_slot_find(dev->bus->number, dev->devfn >> 3, i++);
+ if (temp_func) {
+ dbg("temp_func->function = %d\n", temp_func->function);
+ }
+ } while (temp_func && (temp_func->function != (dev->devfn & 0x07)));
+
+ //Now, remove the Linux Representation
+ if (dev) {
+ if (pci_hp_remove_device(dev) == 0) {
+ kfree(dev); //Now, remove
+ } else {
+ return -1; // problems while freeing, abort visitation
+ }
+ }
+
+ if (temp_func) {
+ temp_func->pci_dev = NULL;
+ } else {
+ dbg("No pci_func representation for bus, devfn = %d, %x\n", dev->bus->number, dev->devfn);
+ }
+
+ 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;
+
+ //The cleanup code for proc entries regarding buses should be in the kernel...
+ if (bus->procdir)
+ dbg("detach_pci_bus %s\n", 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);
+
+ return 0;
+}
+
+
+static int unconfigure_visit_pci_dev_phase1 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus)
+{
+ struct pci_dev* dev = wrapped_dev->dev;
+
+ dbg("attempting removal of driver for device (%x, %x, %x)\n", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+ //Now, remove the Linux Driver Representation
+ if (dev->driver) {
+ if (dev->driver->remove) {
+ dev->driver->remove(dev);
+ dbg("driver was properly removed\n");
+ }
+ dev->driver = NULL;
+ }
+
+ return is_pci_dev_in_use(dev);
+}
+
+
+static struct pci_visit configure_functions = {
+ visit_pci_dev: configure_visit_pci_dev,
+};
+
+
+static struct pci_visit unconfigure_functions_phase1 = {
+ post_visit_pci_dev: unconfigure_visit_pci_dev_phase1
+};
+
+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 cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)
+{
+ unsigned char bus;
+ struct pci_dev dev0;
+ struct pci_bus *child;
+ struct pci_dev* temp;
+ int rc = 0;
+
+ struct pci_dev_wrapped wrapped_dev;
+ struct pci_bus_wrapped wrapped_bus;
+ memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped));
+ memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped));
+
+ memset(&dev0, 0, sizeof(struct pci_dev));
+
+ if (func->pci_dev == NULL)
+ func->pci_dev = pci_find_slot(func->bus, (func->device << 3) | (func->function & 0x7));
+
+ //Still NULL ? Well then scan for it !
+ if (func->pci_dev == NULL) {
+ dbg("INFO: pci_dev still null\n");
+ dev0.bus = ctrl->pci_dev->bus;
+ dev0.devfn = (func->device << 3) + (func->function & 0x7);
+ dev0.sysdata = ctrl->pci_dev->sysdata;
+
+ //this will generate pci_dev structures for all functions, but we will only call this case when lookup fails
+ func->pci_dev = pci_scan_slot(&dev0);
+ 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);
+
+ }
+
+ temp = func->pci_dev;
+
+ if (temp) {
+ wrapped_dev.dev = temp;
+ wrapped_bus.bus = temp->bus;
+ rc = pci_visit_dev(&configure_functions, &wrapped_dev, &wrapped_bus);
+ }
+ return rc;
+}
+
+
+int cpqhp_unconfigure_device(struct pci_func* func)
+{
+ int rc = 0;
+ int j;
+ struct pci_dev_wrapped wrapped_dev;
+ struct pci_bus_wrapped wrapped_bus;
+
+ memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped));
+ memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped));
+
+ dbg(__FUNCTION__": bus/dev/func = %x/%x/%x\n",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) {
+ wrapped_dev.dev = temp;
+ wrapped_bus.bus = temp->bus;
+ rc = pci_visit_dev(&unconfigure_functions_phase1, &wrapped_dev, &wrapped_bus);
+ if (rc)
+ break;
+
+ rc = pci_visit_dev(&unconfigure_functions_phase2, &wrapped_dev, &wrapped_bus);
+ if (rc)
+ break;
+ }
+ }
+ return rc;
+}
+
+static int PCI_RefinedAccessConfig(struct pci_ops *ops, u8 bus, u8 device, u8 function, u8 offset, u32 *value)
+{
+ u32 vendID = 0;
+
+ if (pci_read_config_dword_nodev (ops, bus, device, function, PCI_VENDOR_ID, &vendID) == -1)
+ return -1;
+ if (vendID == 0xffffffff)
+ return -1;
+ return pci_read_config_dword_nodev (ops, bus, device, function, 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;
+ u16 temp_word;
+ struct pci_dev fakedev;
+ struct pci_bus fakebus;
+
+ fakedev.devfn = dev_num << 3;
+ fakedev.bus = &fakebus;
+ fakebus.number = bus_num;
+ dbg(__FUNCTION__": dev %d, bus %d, pin %d, num %d\n",
+ dev_num, bus_num, int_pin, irq_num);
+ rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num);
+ dbg(__FUNCTION__":rc %d\n", 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);
+
+ return 0;
+}
+
+
+/*
+ * 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)
+{
+ u8 tdevice;
+ u32 work;
+ u8 tbus;
+
+ for (tdevice = 0; tdevice < 0x100; tdevice++) {
+ //Scan for access first
+ if (PCI_RefinedAccessConfig(ctrl->pci_ops, bus_num, tdevice >> 3, tdevice & 0x7, 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 < 0x100; tdevice++) {
+ //Scan for access first
+ if (PCI_RefinedAccessConfig(ctrl->pci_ops, bus_num, tdevice >> 3, tdevice & 0x7, 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_read_config_byte_nodev (ctrl->pci_ops, tbus, 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();
+
+ 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;
+ pci_read_config_dword_nodev (ctrl->pci_ops, *bus_num, *dev_num >> 3, *dev_num & 0x7, PCI_VENDOR_ID, &work);
+ if (!nobridge || (work == 0xffffffff)) {
+ if (PCIIRQRoutingInfoLength != NULL)
+ kfree(PCIIRQRoutingInfoLength );
+ return 0;
+ }
+
+ dbg("bus_num %d dev_num %d func_num %d\n", *bus_num, *dev_num >> 3, *dev_num & 0x7);
+ pci_read_config_dword_nodev (ctrl->pci_ops, *bus_num, *dev_num >> 3, *dev_num & 0x7, 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_read_config_byte_nodev (ctrl->pci_ops, *bus_num, *dev_num >> 3, *dev_num & 0x7, 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
+
+ for (device = FirstSupported; device <= LastSupported; device++) {
+ ID = 0xFFFFFFFF;
+ rc = pci_read_config_dword_nodev (ctrl->pci_ops, busnumber, device, 0, PCI_VENDOR_ID, &ID);
+
+ if (ID != 0xFFFFFFFF) { // device in slot
+ rc = pci_read_config_byte_nodev (ctrl->pci_ops, busnumber, device, 0, 0x0B, &class_code);
+ if (rc)
+ return rc;
+
+ rc = pci_read_config_byte_nodev (ctrl->pci_ops, busnumber, 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_read_config_byte_nodev (ctrl->pci_ops, busnumber, 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;
+ }
+ }
+
+ 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_read_config_dword_nodev (ctrl->pci_ops, busnumber, 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_read_config_dword_nodev (ctrl->pci_ops, busnumber, device, function, PCI_VENDOR_ID, &ID);
+ if (ID == 0xFFFFFFFF) { // nothing there.
+ function++;
+ } else { // Something there
+ rc = pci_read_config_byte_nodev (ctrl->pci_ops, busnumber, device, function, 0x0B, &class_code);
+ if (rc)
+ return rc;
+
+ rc = pci_read_config_byte_nodev (ctrl->pci_ops, busnumber, 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;
+
+ pci_read_config_dword_nodev (ctrl->pci_ops, new_slot->bus, new_slot->device, 0, PCI_VENDOR_ID, &ID);
+
+ if (ID != 0xFFFFFFFF) { // device in slot
+ pci_read_config_byte_nodev (ctrl->pci_ops, new_slot->bus, new_slot->device, 0, 0x0B, &class_code);
+
+ pci_read_config_byte_nodev (ctrl->pci_ops, new_slot->bus, 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_read_config_byte_nodev (ctrl->pci_ops, new_slot->bus, 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);
+
+ } // End of IF
+
+ new_slot->status = 0;
+
+ for (cloop = 0; cloop < 0x20; cloop++) {
+ pci_read_config_dword_nodev (ctrl->pci_ops, new_slot->bus, 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_read_config_dword_nodev (ctrl->pci_ops, new_slot->bus, new_slot->device, function, PCI_VENDOR_ID, &ID);
+
+ if (ID == 0xFFFFFFFF) { // nothing there.
+ function++;
+ } else { // Something there
+ pci_read_config_byte_nodev (ctrl->pci_ops, new_slot->bus, new_slot->device, function, 0x0B, &class_code);
+
+ pci_read_config_byte_nodev (ctrl->pci_ops, new_slot->bus, 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;
+
+ func = cpqhp_slot_find(func->bus, func->device, index++);
+
+ while (func != NULL) {
+
+ // Check for Bridge
+ pci_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, PCI_HEADER_TYPE, &header_type);
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+ // PCI-PCI Bridge
+ pci_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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;
+ }
+
+ //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_write_config_dword_nodev(ctrl->pci_ops, func->bus, func->device, func->function, cloop, temp_register);
+ pci_read_config_dword_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_dword_nodev(ctrl->pci_ops, func->bus, func->device, func->function, cloop, temp_register);
+ pci_read_config_dword_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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;
+
+ func = cpqhp_slot_find(func->bus, func->device, index++);
+
+ while ((func != NULL) && func->is_a_board) {
+ // Save the command register
+ pci_read_config_word_nodev (ctrl->pci_ops, func->bus, func->device, func->function, PCI_COMMAND, &save_command);
+
+ // disable card
+ command = 0x00;
+ pci_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_COMMAND, command);
+
+ // Check for Bridge
+ pci_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, PCI_HEADER_TYPE, &header_type);
+
+ if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge
+ // Clear Bridge Control Register
+ command = 0x00;
+ pci_write_config_word_nodev(ctrl->pci_ops, func->bus, func->device, func->function, PCI_BRIDGE_CONTROL, command);
+
+ pci_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, PCI_SECONDARY_BUS, &secondary_bus);
+
+ pci_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, PCI_SUBORDINATE_BUS, &temp_byte);
+
+ bus_node =(struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, PCI_IO_BASE, &b_base);
+
+ pci_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, PCI_IO_LIMIT, &b_length);
+
+ if ((b_base <= b_length) && (save_command & 0x01)) {
+ io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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_read_config_word_nodev (ctrl->pci_ops, func->bus, func->device, func->function, PCI_MEMORY_BASE, &w_base);
+
+ pci_read_config_word_nodev (ctrl->pci_ops, func->bus, func->device, func->function, PCI_MEMORY_LIMIT, &w_length);
+
+ if ((w_base <= w_length) && (save_command & 0x02)) {
+ mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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_read_config_word_nodev (ctrl->pci_ops, func->bus, func->device, func->function, PCI_PREF_MEMORY_BASE, &w_base);
+
+ pci_read_config_word_nodev (ctrl->pci_ops, func->bus, func->device, func->function, PCI_PREF_MEMORY_LIMIT, &w_length);
+
+ if ((w_base <= w_length) && (save_command & 0x02)) {
+ p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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_read_config_dword_nodev (ctrl->pci_ops, func->bus, func->device, func->function, cloop, &save_base);
+
+ temp_register = 0xFFFFFFFF;
+ pci_write_config_dword_nodev(ctrl->pci_ops, func->bus, func->device, func->function, cloop, temp_register);
+
+ pci_read_config_dword_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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 = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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 = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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 = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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_read_config_dword_nodev (ctrl->pci_ops, func->bus, func->device, func->function, cloop, &save_base);
+
+ temp_register = 0xFFFFFFFF;
+ pci_write_config_dword_nodev(ctrl->pci_ops, func->bus, func->device, func->function, cloop, temp_register);
+
+ pci_read_config_dword_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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 = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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 = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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 = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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;
+
+ func = cpqhp_slot_find(func->bus, func->device, index++);
+
+ while (func != NULL) {
+ // Start at the top of config space so that the control
+ // registers are programmed last
+ for (cloop = 0x3C; cloop > 0; cloop -= 4) {
+ pci_write_config_dword_nodev(ctrl->pci_ops, func->bus, func->device, func->function, cloop, func->config_space[cloop >> 2]);
+ }
+
+ pci_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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_read_config_dword_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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]);
+ 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;
+
+ if (!func->is_a_board)
+ return(ADD_NOT_SUPPORTED);
+
+ func = cpqhp_slot_find(func->bus, func->device, index++);
+
+ while (func != NULL) {
+ pci_read_config_dword_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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_read_config_dword_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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_read_config_byte_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_dword_nodev(ctrl->pci_ops, func->bus, func->device, func->function, 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_read_config_dword_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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_write_config_dword_nodev(ctrl->pci_ops, func->bus, func->device, func->function, cloop, temp_register);
+
+ pci_read_config_dword_nodev (ctrl->pci_ops, func->bus, func->device, func->function, 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 *rom_start)
+{
+ u8 temp;
+ u8 populated_slot;
+ u8 bridged_slot;
+ void *one_slot;
+ 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;
+ void *rom_resource_table;
+
+ 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->interrupt;
+ }
+
+ if (!cpqhp_disk_irq) {
+ cpqhp_disk_irq = ctrl->interrupt;
+ }
+
+ 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
+ pci_read_config_dword_nodev (ctrl->pci_ops, primary_bus, dev_func >> 3, dev_func & 0x07, 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 = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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 = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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 = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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 = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), 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(__FUNCTION__"\n");
+
+ 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/hotplug/cpqphp_proc.c b/drivers/hotplug/cpqphp_proc.c
new file mode 100644
index 000000000000..6f260803d456
--- /dev/null
+++ b/drivers/hotplug/cpqphp_proc.c
@@ -0,0 +1,192 @@
+/*
+ * 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/pci.h>
+#include "cpqphp.h"
+
+
+
+static struct proc_dir_entry *ctrl_proc_root;
+
+/* A few routines that create proc entries for the hot plug controller */
+
+static int read_ctrl (char *buf, char **start, off_t offset, int len, int *eof, void *data)
+{
+ struct controller *ctrl = (struct controller *)data;
+ char * out = buf;
+ int index;
+ struct pci_resource *res;
+
+ if (offset > 0) return 0; /* no partial requests */
+ len = 0;
+ *eof = 1;
+
+ out += sprintf(out, "hot plug ctrl Info Page\n");
+ out += sprintf(out, "bus = %d, device = %d, function = %d\n",ctrl->bus,
+ ctrl->device, ctrl->function);
+ out += sprintf(out, "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;
+ }
+
+ *start = buf;
+ len = out-buf;
+
+ return len;
+}
+
+static int read_dev (char *buf, char **start, off_t offset, int len, int *eof, void *data)
+{
+ struct controller *ctrl = (struct controller *)data;
+ char * out = buf;
+ int index;
+ struct pci_resource *res;
+ struct pci_func *new_slot;
+ struct slot *slot;
+
+ if (offset > 0) return 0; /* no partial requests */
+ len = 0;
+ *eof = 1;
+
+ out += sprintf(out, "hot plug ctrl Info Page\n");
+ out += sprintf(out, "bus = %d, device = %d, function = %d\n",ctrl->bus,
+ ctrl->device, ctrl->function);
+
+ slot=ctrl->slot;
+
+ while (slot) {
+ new_slot = cpqhp_slot_find(slot->bus, slot->device, 0);
+ 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;
+ }
+
+ *start = buf;
+ len = out-buf;
+
+ return len;
+}
+
+int cpqhp_proc_create_ctrl (struct controller *ctrl)
+{
+ strcpy(ctrl->proc_name, "hpca");
+ ctrl->proc_name[3] = 'a' + ctrl->bus;
+
+ ctrl->proc_entry = create_proc_entry(ctrl->proc_name, S_IFREG | S_IRUGO, ctrl_proc_root);
+ ctrl->proc_entry->data = ctrl;
+ ctrl->proc_entry->read_proc = &read_ctrl;
+
+ strcpy(ctrl->proc_name2, "slot_a");
+ ctrl->proc_name2[5] = 'a' + ctrl->bus;
+ ctrl->proc_entry2 = create_proc_entry(ctrl->proc_name2, S_IFREG | S_IRUGO, ctrl_proc_root);
+ ctrl->proc_entry2->data = ctrl;
+ ctrl->proc_entry2->read_proc = &read_dev;
+
+ return 0;
+}
+
+int cpqhp_proc_remove_ctrl (struct controller *ctrl)
+{
+ if (ctrl->proc_entry)
+ remove_proc_entry(ctrl->proc_name, ctrl_proc_root);
+ if (ctrl->proc_entry2)
+ remove_proc_entry(ctrl->proc_name2, ctrl_proc_root);
+
+ return 0;
+}
+
+int cpqhp_proc_init_ctrl (void)
+{
+ ctrl_proc_root = proc_mkdir("driver/hpc", NULL);
+ if (!ctrl_proc_root)
+ return -ENOMEM;
+ ctrl_proc_root->owner = THIS_MODULE;
+ return 0;
+}
+
+int cpqhp_proc_destroy_ctrl (void)
+{
+ remove_proc_entry("hpc", proc_root_driver);
+ return 0;
+}
+
diff --git a/drivers/hotplug/pci_hotplug.h b/drivers/hotplug/pci_hotplug.h
new file mode 100644
index 000000000000..4ca357f6590a
--- /dev/null
+++ b/drivers/hotplug/pci_hotplug.h
@@ -0,0 +1,160 @@
+/*
+ * 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
+
+
+struct hotplug_slot;
+struct hotplug_slot_core;
+
+/**
+ * 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_present: 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.
+ *
+ * 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);
+};
+
+/**
+ * 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)
+ *
+ * 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;
+};
+
+/**
+ * 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.
+ * @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 *private;
+
+ /* Variables below this are for use only by the hotplug pci core. */
+ struct list_head slot_list;
+ struct hotplug_slot_core *core_priv;
+};
+
+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 (const char *name,
+ struct hotplug_slot_info *info);
+
+struct pci_dev_wrapped {
+ struct pci_dev *dev;
+ void *data;
+};
+
+struct pci_bus_wrapped {
+ struct pci_bus *bus;
+ void *data;
+};
+
+struct pci_visit {
+ int (* pre_visit_pci_bus) (struct pci_bus_wrapped *,
+ struct pci_dev_wrapped *);
+ int (* post_visit_pci_bus) (struct pci_bus_wrapped *,
+ struct pci_dev_wrapped *);
+
+ int (* pre_visit_pci_dev) (struct pci_dev_wrapped *,
+ struct pci_bus_wrapped *);
+ int (* visit_pci_dev) (struct pci_dev_wrapped *,
+ struct pci_bus_wrapped *);
+ int (* post_visit_pci_dev) (struct pci_dev_wrapped *,
+ struct pci_bus_wrapped *);
+};
+
+extern int pci_visit_dev (struct pci_visit *fn,
+ struct pci_dev_wrapped *wrapped_dev,
+ struct pci_bus_wrapped *wrapped_parent);
+
+extern int pci_read_config_byte_nodev (struct pci_ops *ops, u8 bus, u8 device,
+ u8 function, int where, u8 *val);
+extern int pci_read_config_word_nodev (struct pci_ops *ops, u8 bus, u8 device,
+ u8 function, int where, u16 *val);
+extern int pci_read_config_dword_nodev (struct pci_ops *ops, u8 bus, u8 device,
+ u8 function, int where, u32 *val);
+
+extern int pci_write_config_byte_nodev (struct pci_ops *ops, u8 bus, u8 device,
+ u8 function, int where, u8 val);
+extern int pci_write_config_word_nodev (struct pci_ops *ops, u8 bus, u8 device,
+ u8 function, int where, u16 val);
+extern int pci_write_config_dword_nodev (struct pci_ops *ops, u8 bus, u8 device,
+ u8 function, int where, u32 val);
+
+
+#endif
+
diff --git a/drivers/hotplug/pci_hotplug_core.c b/drivers/hotplug/pci_hotplug_core.c
new file mode 100644
index 000000000000..0b851a24c374
--- /dev/null
+++ b/drivers/hotplug/pci_hotplug_core.c
@@ -0,0 +1,1132 @@
+/*
+ * PCI HotPlug Controller Core
+ *
+ * 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/list.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/uaccess.h>
+#include "pci_hotplug.h"
+
+
+#if !defined(CONFIG_HOTPLUG_PCI_MODULE)
+ #define MY_NAME "pci_hotplug"
+#else
+ #define MY_NAME THIS_MODULE->name
+#endif
+
+#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: "__FUNCTION__": " 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)
+
+
+/* local variables */
+static int debug;
+
+#define DRIVER_VERSION "0.3"
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
+#define DRIVER_DESC "PCI Hot Plug PCI Core"
+
+
+//////////////////////////////////////////////////////////////////
+
+/* Random magic number */
+#define PCIHPFS_MAGIC 0x52454541
+
+struct hotplug_slot_core {
+ struct dentry *dir_dentry;
+ struct dentry *power_dentry;
+ struct dentry *attention_dentry;
+ struct dentry *latch_dentry;
+ struct dentry *adapter_dentry;
+ struct dentry *test_dentry;
+};
+
+static struct super_operations pcihpfs_ops;
+static struct address_space_operations pcihpfs_aops;
+static struct file_operations pcihpfs_dir_operations;
+static struct file_operations default_file_operations;
+static struct inode_operations pcihpfs_dir_inode_operations;
+static struct vfsmount *pcihpfs_mount; /* one of the mounts of our fs for reference counting */
+static int pcihpfs_mount_count; /* times we have mounted our fs */
+static spinlock_t mount_lock; /* protects our mount_count */
+static spinlock_t list_lock;
+
+LIST_HEAD(pci_hotplug_slot_list);
+
+
+static int pcihpfs_statfs (struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = PCIHPFS_MAGIC;
+ buf->f_bsize = PAGE_CACHE_SIZE;
+ buf->f_namelen = 255;
+ return 0;
+}
+
+static struct dentry *pcihpfs_lookup (struct inode *dir, struct dentry *dentry)
+{
+ d_add(dentry, NULL);
+ return NULL;
+}
+
+static struct inode *pcihpfs_get_inode (struct super_block *sb, int mode, int dev)
+{
+ struct inode *inode = new_inode(sb);
+
+ if (inode) {
+ inode->i_mode = mode;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+ inode->i_blksize = PAGE_CACHE_SIZE;
+ inode->i_blocks = 0;
+ inode->i_rdev = NODEV;
+ inode->i_mapping->a_ops = &pcihpfs_aops;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ switch (mode & S_IFMT) {
+ default:
+ init_special_inode(inode, mode, dev);
+ break;
+ case S_IFREG:
+ inode->i_fop = &default_file_operations;
+ break;
+ case S_IFDIR:
+ inode->i_op = &pcihpfs_dir_inode_operations;
+ inode->i_fop = &pcihpfs_dir_operations;
+ break;
+ }
+ }
+ return inode;
+}
+
+static int pcihpfs_mknod (struct inode *dir, struct dentry *dentry, int mode, int dev)
+{
+ struct inode *inode = pcihpfs_get_inode(dir->i_sb, mode, dev);
+ int error = -ENOSPC;
+
+ if (inode) {
+ d_instantiate(dentry, inode);
+ dget(dentry);
+ error = 0;
+ }
+ return error;
+}
+
+static int pcihpfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
+{
+ return pcihpfs_mknod (dir, dentry, mode | S_IFDIR, 0);
+}
+
+static int pcihpfs_create (struct inode *dir, struct dentry *dentry, int mode)
+{
+ return pcihpfs_mknod (dir, dentry, mode | S_IFREG, 0);
+}
+
+static int pcihpfs_link (struct dentry *old_dentry, struct inode *dir,
+ struct dentry *dentry)
+{
+ struct inode *inode = old_dentry->d_inode;
+
+ if(S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ inode->i_nlink++;
+ atomic_inc(&inode->i_count);
+ dget(dentry);
+ d_instantiate(dentry, inode);
+ return 0;
+}
+
+static inline int pcihpfs_positive (struct dentry *dentry)
+{
+ return dentry->d_inode && !d_unhashed(dentry);
+}
+
+static int pcihpfs_empty (struct dentry *dentry)
+{
+ struct list_head *list;
+
+ spin_lock(&dcache_lock);
+
+ list_for_each(list, &dentry->d_subdirs) {
+ struct dentry *de = list_entry(list, struct dentry, d_child);
+ if (pcihpfs_positive(de)) {
+ spin_unlock(&dcache_lock);
+ return 0;
+ }
+ }
+
+ spin_unlock(&dcache_lock);
+ return 1;
+}
+
+static int pcihpfs_unlink (struct inode *dir, struct dentry *dentry)
+{
+ int error = -ENOTEMPTY;
+
+ if (pcihpfs_empty(dentry)) {
+ struct inode *inode = dentry->d_inode;
+
+ inode->i_nlink--;
+ dput(dentry);
+ error = 0;
+ }
+ return error;
+}
+
+static int pcihpfs_rename (struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int error = -ENOTEMPTY;
+
+ if (pcihpfs_empty(new_dentry)) {
+ struct inode *inode = new_dentry->d_inode;
+ if (inode) {
+ inode->i_nlink--;
+ dput(new_dentry);
+ }
+ error = 0;
+ }
+ return error;
+}
+
+#define pcihpfs_rmdir pcihpfs_unlink
+
+/* default file operations */
+static ssize_t default_read_file (struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ dbg ("\n");
+ return 0;
+}
+
+static ssize_t default_write_file (struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+ dbg ("\n");
+ return count;
+}
+
+static loff_t default_file_lseek (struct file *file, loff_t offset, int orig)
+{
+ loff_t retval = -EINVAL;
+
+ switch(orig) {
+ case 0:
+ if (offset > 0) {
+ file->f_pos = offset;
+ retval = file->f_pos;
+ }
+ break;
+ case 1:
+ if ((offset + file->f_pos) > 0) {
+ file->f_pos += offset;
+ retval = file->f_pos;
+ }
+ break;
+ default:
+ break;
+ }
+ return retval;
+}
+
+static int default_open (struct inode *inode, struct file *filp)
+{
+ if (inode->u.generic_ip)
+ filp->private_data = inode->u.generic_ip;
+
+ return 0;
+}
+
+static int default_sync_file (struct file *file, struct dentry *dentry, int datasync)
+{
+ return 0;
+}
+
+static struct address_space_operations pcihpfs_aops = {
+};
+
+static struct file_operations pcihpfs_dir_operations = {
+ read: generic_read_dir,
+ readdir: dcache_readdir,
+ fsync: default_sync_file,
+};
+
+static struct file_operations default_file_operations = {
+ read: default_read_file,
+ write: default_write_file,
+ open: default_open,
+ llseek: default_file_lseek,
+ fsync: default_sync_file,
+ mmap: generic_file_mmap,
+};
+
+/* file ops for the "power" files */
+static ssize_t power_read_file (struct file *file, char *buf, size_t count, loff_t *offset);
+static ssize_t power_write_file (struct file *file, const char *buf, size_t count, loff_t *ppos);
+static struct file_operations power_file_operations = {
+ read: power_read_file,
+ write: power_write_file,
+ open: default_open,
+ llseek: default_file_lseek,
+ fsync: default_sync_file,
+ mmap: generic_file_mmap,
+};
+
+/* file ops for the "attention" files */
+static ssize_t attention_read_file (struct file *file, char *buf, size_t count, loff_t *offset);
+static ssize_t attention_write_file (struct file *file, const char *buf, size_t count, loff_t *ppos);
+static struct file_operations attention_file_operations = {
+ read: attention_read_file,
+ write: attention_write_file,
+ open: default_open,
+ llseek: default_file_lseek,
+ fsync: default_sync_file,
+ mmap: generic_file_mmap,
+};
+
+/* file ops for the "latch" files */
+static ssize_t latch_read_file (struct file *file, char *buf, size_t count, loff_t *offset);
+static struct file_operations latch_file_operations = {
+ read: latch_read_file,
+ write: default_write_file,
+ open: default_open,
+ llseek: default_file_lseek,
+ fsync: default_sync_file,
+ mmap: generic_file_mmap,
+};
+
+/* file ops for the "presence" files */
+static ssize_t presence_read_file (struct file *file, char *buf, size_t count, loff_t *offset);
+static struct file_operations presence_file_operations = {
+ read: presence_read_file,
+ write: default_write_file,
+ open: default_open,
+ llseek: default_file_lseek,
+ fsync: default_sync_file,
+ mmap: generic_file_mmap,
+};
+
+/* file ops for the "test" files */
+static ssize_t test_write_file (struct file *file, const char *buf, size_t count, loff_t *ppos);
+static struct file_operations test_file_operations = {
+ read: default_read_file,
+ write: test_write_file,
+ open: default_open,
+ llseek: default_file_lseek,
+ fsync: default_sync_file,
+ mmap: generic_file_mmap,
+};
+
+static struct inode_operations pcihpfs_dir_inode_operations = {
+ create: pcihpfs_create,
+ lookup: pcihpfs_lookup,
+ link: pcihpfs_link,
+ unlink: pcihpfs_unlink,
+ mkdir: pcihpfs_mkdir,
+ rmdir: pcihpfs_rmdir,
+ mknod: pcihpfs_mknod,
+ rename: pcihpfs_rename,
+};
+
+static struct super_operations pcihpfs_ops = {
+ statfs: pcihpfs_statfs,
+ put_inode: force_delete,
+};
+
+static struct super_block *pcihpfs_read_super (struct super_block *sb, void *data, int silent)
+{
+ struct inode *inode;
+ struct dentry *root;
+
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = PCIHPFS_MAGIC;
+ sb->s_op = &pcihpfs_ops;
+ inode = pcihpfs_get_inode(sb, S_IFDIR | 0755, 0);
+
+ if (!inode) {
+ dbg("%s: could not get inode!\n",__FUNCTION__);
+ return NULL;
+ }
+
+ root = d_alloc_root(inode);
+ if (!root) {
+ dbg("%s: could not get root dentry!\n",__FUNCTION__);
+ iput(inode);
+ return NULL;
+ }
+ sb->s_root = root;
+ return sb;
+}
+
+static DECLARE_FSTYPE(pcihpfs_fs_type, "pcihpfs", pcihpfs_read_super, FS_SINGLE | FS_LITTER);
+
+static int get_mount (void)
+{
+ struct vfsmount *mnt;
+
+ spin_lock (&mount_lock);
+ if (pcihpfs_mount) {
+ mntget(pcihpfs_mount);
+ ++pcihpfs_mount_count;
+ spin_unlock (&mount_lock);
+ goto go_ahead;
+ }
+
+ spin_unlock (&mount_lock);
+ mnt = kern_mount (&pcihpfs_fs_type);
+ if (IS_ERR(mnt)) {
+ err ("could not mount the fs...erroring out!\n");
+ return -ENODEV;
+ }
+ spin_lock (&mount_lock);
+ if (!pcihpfs_mount) {
+ pcihpfs_mount = mnt;
+ ++pcihpfs_mount_count;
+ spin_unlock (&mount_lock);
+ goto go_ahead;
+ }
+ mntget(pcihpfs_mount);
+ ++pcihpfs_mount_count;
+ spin_unlock (&mount_lock);
+ mntput(mnt);
+
+go_ahead:
+ dbg("pcihpfs_mount_count = %d\n", pcihpfs_mount_count);
+ return 0;
+}
+
+static void remove_mount (void)
+{
+ struct vfsmount *mnt;
+
+ spin_lock (&mount_lock);
+ mnt = pcihpfs_mount;
+ --pcihpfs_mount_count;
+ if (!pcihpfs_mount_count)
+ pcihpfs_mount = NULL;
+
+ spin_unlock (&mount_lock);
+ mntput(mnt);
+ dbg("pcihpfs_mount_count = %d\n", pcihpfs_mount_count);
+}
+
+
+/**
+ * pcihpfs_create_by_name - create a file, given a name
+ * @name: name of file
+ * @mode: type of file
+ * @parent: dentry of directory to create it in
+ * @dentry: resulting dentry of file
+ *
+ * There is a bit of overhead in creating a file - basically, we
+ * have to hash the name of the file, then look it up. This will
+ * prevent files of the same name.
+ * We then call the proper vfs_ function to take care of all the
+ * file creation details.
+ * This function handles both regular files and directories.
+ */
+static int pcihpfs_create_by_name (const char *name, mode_t mode,
+ struct dentry *parent, struct dentry **dentry)
+{
+ struct dentry *d = NULL;
+ struct qstr qstr;
+ int error;
+
+ /* If the parent is not specified, we create it in the root.
+ * We need the root dentry to do this, which is in the super
+ * block. A pointer to that is in the struct vfsmount that we
+ * have around.
+ */
+ if (!parent ) {
+ if (pcihpfs_mount && pcihpfs_mount->mnt_sb) {
+ parent = pcihpfs_mount->mnt_sb->s_root;
+ }
+ }
+
+ if (!parent) {
+ dbg("Ah! can not find a parent!\n");
+ return -EFAULT;
+ }
+
+ *dentry = NULL;
+ qstr.name = name;
+ qstr.len = strlen(name);
+ qstr.hash = full_name_hash(name,qstr.len);
+
+ parent = dget(parent);
+
+ down(&parent->d_inode->i_sem);
+
+ d = lookup_hash(&qstr,parent);
+
+ error = PTR_ERR(d);
+ if (!IS_ERR(d)) {
+ switch(mode & S_IFMT) {
+ case 0:
+ case S_IFREG:
+ error = vfs_create(parent->d_inode,d,mode);
+ break;
+ case S_IFDIR:
+ error = vfs_mkdir(parent->d_inode,d,mode);
+ break;
+ default:
+ err("cannot create special files\n");
+ }
+ *dentry = d;
+ }
+ up(&parent->d_inode->i_sem);
+
+ dput(parent);
+ return error;
+}
+
+static struct dentry *fs_create_file (const char *name, mode_t mode,
+ struct dentry *parent, void *data,
+ struct file_operations *fops)
+{
+ struct dentry *dentry;
+ int error;
+
+ dbg("creating file '%s'\n",name);
+
+ error = pcihpfs_create_by_name(name,mode,parent,&dentry);
+ if (error) {
+ dentry = NULL;
+ } else {
+ if (dentry->d_inode) {
+ if (data)
+ dentry->d_inode->u.generic_ip = data;
+ if (fops)
+ dentry->d_inode->i_fop = fops;
+ }
+ }
+
+ return dentry;
+}
+
+static void fs_remove_file (struct dentry *dentry)
+{
+ struct dentry *parent = dentry->d_parent;
+
+ if (!parent || !parent->d_inode)
+ return;
+
+ down(&parent->d_inode->i_sem);
+ if (pcihpfs_positive(dentry)) {
+ if (dentry->d_inode) {
+ if (S_ISDIR(dentry->d_inode->i_mode))
+ vfs_rmdir(parent->d_inode,dentry);
+ else
+ vfs_unlink(parent->d_inode,dentry);
+ }
+
+ dput(dentry);
+ }
+ up(&parent->d_inode->i_sem);
+}
+
+#define GET_STATUS(name) \
+static int get_##name##_status (struct hotplug_slot *slot, u8 *value) \
+{ \
+ struct hotplug_slot_ops *ops = slot->ops; \
+ int retval = 0; \
+ if (ops->owner) \
+ __MOD_INC_USE_COUNT(ops->owner); \
+ if (ops->get_##name##_status) \
+ retval = ops->get_##name##_status (slot, value); \
+ else \
+ *value = slot->info->name##_status; \
+ if (ops->owner) \
+ __MOD_DEC_USE_COUNT(ops->owner); \
+ return retval; \
+}
+
+GET_STATUS(power)
+GET_STATUS(attention)
+GET_STATUS(latch)
+GET_STATUS(adapter)
+
+static ssize_t power_read_file (struct file *file, char *buf, size_t count, loff_t *offset)
+{
+ struct hotplug_slot *slot = file->private_data;
+ unsigned char *page;
+ int retval;
+ int len;
+ u8 value;
+
+ dbg(" count = %d, offset = %lld\n", count, *offset);
+
+ if (*offset < 0)
+ return -EINVAL;
+ if (count <= 0)
+ return 0;
+ if (*offset != 0)
+ return 0;
+
+ if (slot == NULL) {
+ dbg("slot == NULL???\n");
+ return -ENODEV;
+ }
+
+ page = (unsigned char *)__get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ retval = get_power_status (slot, &value);
+ if (retval)
+ goto exit;
+ len = sprintf (page, "%d\n", value);
+
+ if (copy_to_user (buf, page, len)) {
+ retval = -EFAULT;
+ goto exit;
+ }
+ *offset += len;
+ retval = len;
+
+exit:
+ free_page((unsigned long)page);
+ return retval;
+}
+
+static ssize_t power_write_file (struct file *file, const char *ubuff, size_t count, loff_t *offset)
+{
+ struct hotplug_slot *slot = file->private_data;
+ const char *buff;
+ unsigned long lpower;
+ u8 power;
+ int retval = 0;
+
+ if (*offset < 0)
+ return -EINVAL;
+ if (count <= 0)
+ return 0;
+ if (*offset != 0)
+ return 0;
+
+ if (slot == NULL) {
+ dbg("slot == NULL???\n");
+ return -ENODEV;
+ }
+
+ buff = kmalloc (count, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ if (copy_from_user ((void *)buff, (void *)ubuff, count)) {
+ retval = -EFAULT;
+ goto exit;
+ }
+
+ lpower = simple_strtoul (buff, NULL, 10);
+ power = (u8)(lpower & 0xff);
+ dbg ("power = %d\n", power);
+
+ switch (power) {
+ case 0:
+ if (!slot->ops->disable_slot)
+ break;
+ if (slot->ops->owner)
+ __MOD_INC_USE_COUNT(slot->ops->owner);
+ retval = slot->ops->disable_slot(slot);
+ if (slot->ops->owner)
+ __MOD_DEC_USE_COUNT(slot->ops->owner);
+ break;
+
+ case 1:
+ if (!slot->ops->enable_slot)
+ break;
+ if (slot->ops->owner)
+ __MOD_INC_USE_COUNT(slot->ops->owner);
+ retval = slot->ops->enable_slot(slot);
+ if (slot->ops->owner)
+ __MOD_DEC_USE_COUNT(slot->ops->owner);
+ break;
+
+ default:
+ err ("Illegal value specified for power\n");
+ retval = -EFAULT;
+ }
+
+exit:
+ kfree (buff);
+
+ if (retval)
+ return retval;
+ return count;
+}
+
+static ssize_t attention_read_file (struct file *file, char *buf, size_t count, loff_t *offset)
+{
+ struct hotplug_slot *slot = file->private_data;
+ unsigned char *page;
+ int retval;
+ int len;
+ u8 value;
+
+ dbg("count = %d, offset = %lld\n", count, *offset);
+
+ if (*offset < 0)
+ return -EINVAL;
+ if (count <= 0)
+ return 0;
+ if (*offset != 0)
+ return 0;
+
+ if (slot == NULL) {
+ dbg("slot == NULL???\n");
+ return -ENODEV;
+ }
+
+ page = (unsigned char *)__get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ retval = get_attention_status (slot, &value);
+ if (retval)
+ goto exit;
+ len = sprintf (page, "%d\n", value);
+
+ if (copy_to_user (buf, page, len)) {
+ retval = -EFAULT;
+ goto exit;
+ }
+ *offset += len;
+ retval = len;
+
+exit:
+ free_page((unsigned long)page);
+ return retval;
+}
+
+static ssize_t attention_write_file (struct file *file, const char *ubuff, size_t count, loff_t *offset)
+{
+ struct hotplug_slot *slot = file->private_data;
+ const char *buff;
+ unsigned long lattention;
+ u8 attention;
+ int retval = 0;
+
+ if (*offset < 0)
+ return -EINVAL;
+ if (count <= 0)
+ return 0;
+ if (*offset != 0)
+ return 0;
+
+ if (slot == NULL) {
+ dbg("slot == NULL???\n");
+ return -ENODEV;
+ }
+
+ buff = kmalloc (count, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ if (copy_from_user ((void *)buff, (void *)ubuff, count)) {
+ retval = -EFAULT;
+ goto exit;
+ }
+
+ lattention = simple_strtoul (buff, NULL, 10);
+ attention = (u8)(lattention & 0xff);
+ dbg (" - attention = %d\n", attention);
+
+ if (slot->ops->set_attention_status) {
+ if (slot->ops->owner)
+ __MOD_INC_USE_COUNT(slot->ops->owner);
+ retval = slot->ops->set_attention_status(slot, attention);
+ if (slot->ops->owner)
+ __MOD_DEC_USE_COUNT(slot->ops->owner);
+ }
+
+exit:
+ kfree (buff);
+
+ if (retval)
+ return retval;
+ return count;
+}
+
+static ssize_t latch_read_file (struct file *file, char *buf, size_t count, loff_t *offset)
+{
+ struct hotplug_slot *slot = file->private_data;
+ unsigned char *page;
+ int retval;
+ int len;
+ u8 value;
+
+ dbg("count = %d, offset = %lld\n", count, *offset);
+
+ if (*offset < 0)
+ return -EINVAL;
+ if (count <= 0)
+ return 0;
+ if (*offset != 0)
+ return 0;
+
+ if (slot == NULL) {
+ dbg("slot == NULL???\n");
+ return -ENODEV;
+ }
+
+ page = (unsigned char *)__get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ retval = get_latch_status (slot, &value);
+ if (retval)
+ goto exit;
+ len = sprintf (page, "%d\n", value);
+
+ if (copy_to_user (buf, page, len)) {
+ retval = -EFAULT;
+ goto exit;
+ }
+ *offset += len;
+ retval = len;
+
+exit:
+ free_page((unsigned long)page);
+ return retval;
+}
+
+
+static ssize_t presence_read_file (struct file *file, char *buf, size_t count, loff_t *offset)
+{
+ struct hotplug_slot *slot = file->private_data;
+ unsigned char *page;
+ int retval;
+ int len;
+ u8 value;
+
+ dbg("count = %d, offset = %lld\n", count, *offset);
+
+ if (*offset < 0)
+ return -EINVAL;
+ if (count <= 0)
+ return 0;
+ if (*offset != 0)
+ return 0;
+
+ if (slot == NULL) {
+ dbg("slot == NULL???\n");
+ return -ENODEV;
+ }
+
+ page = (unsigned char *)__get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ retval = get_adapter_status (slot, &value);
+ if (retval)
+ goto exit;
+ len = sprintf (page, "%d\n", value);
+
+ if (copy_to_user (buf, page, len)) {
+ retval = -EFAULT;
+ goto exit;
+ }
+ *offset += len;
+ retval = len;
+
+exit:
+ free_page((unsigned long)page);
+ return retval;
+}
+
+static ssize_t test_write_file (struct file *file, const char *ubuff, size_t count, loff_t *offset)
+{
+ struct hotplug_slot *slot = file->private_data;
+ const char *buff;
+ unsigned long ltest;
+ u32 test;
+ int retval = 0;
+
+ if (*offset < 0)
+ return -EINVAL;
+ if (count <= 0)
+ return 0;
+ if (*offset != 0)
+ return 0;
+
+ if (slot == NULL) {
+ dbg("slot == NULL???\n");
+ return -ENODEV;
+ }
+
+ buff = kmalloc (count, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ if (copy_from_user ((void *)buff, (void *)ubuff, count)) {
+ retval = -EFAULT;
+ goto exit;
+ }
+
+ ltest = simple_strtoul (buff, NULL, 10);
+ test = (u32)(ltest & 0xffffffff);
+ dbg ("test = %d\n", test);
+
+ if (slot->ops->hardware_test) {
+ if (slot->ops->owner)
+ __MOD_INC_USE_COUNT(slot->ops->owner);
+ retval = slot->ops->hardware_test(slot, test);
+ if (slot->ops->owner)
+ __MOD_DEC_USE_COUNT(slot->ops->owner);
+ }
+
+exit:
+ kfree (buff);
+
+ if (retval)
+ return retval;
+ return count;
+}
+
+static int fs_add_slot (struct hotplug_slot *slot)
+{
+ struct hotplug_slot_core *core = slot->core_priv;
+ int result;
+
+ result = get_mount();
+ if (result)
+ return result;
+
+ core->dir_dentry = fs_create_file (slot->name,
+ S_IFDIR | S_IXUGO | S_IRUGO,
+ NULL, NULL, NULL);
+ if (core->dir_dentry != NULL) {
+ core->power_dentry = fs_create_file ("power",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ core->dir_dentry, slot,
+ &power_file_operations);
+
+ core->attention_dentry = fs_create_file ("attention",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ core->dir_dentry, slot,
+ &attention_file_operations);
+
+ core->latch_dentry = fs_create_file ("latch",
+ S_IFREG | S_IRUGO,
+ core->dir_dentry, slot,
+ &latch_file_operations);
+
+ core->adapter_dentry = fs_create_file ("adapter",
+ S_IFREG | S_IRUGO,
+ core->dir_dentry, slot,
+ &presence_file_operations);
+
+ core->test_dentry = fs_create_file ("test",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ core->dir_dentry, slot,
+ &test_file_operations);
+ }
+ return 0;
+}
+
+static void fs_remove_slot (struct hotplug_slot *slot)
+{
+ struct hotplug_slot_core *core = slot->core_priv;
+
+ if (core->dir_dentry) {
+ if (core->power_dentry)
+ fs_remove_file (core->power_dentry);
+ if (core->attention_dentry)
+ fs_remove_file (core->attention_dentry);
+ if (core->latch_dentry)
+ fs_remove_file (core->latch_dentry);
+ if (core->adapter_dentry)
+ fs_remove_file (core->adapter_dentry);
+ if (core->test_dentry)
+ fs_remove_file (core->test_dentry);
+ fs_remove_file (core->dir_dentry);
+ }
+
+ remove_mount();
+}
+
+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)
+{
+ struct hotplug_slot_core *core;
+ int result;
+
+ if (slot == NULL)
+ return -ENODEV;
+ if ((slot->info == NULL) || (slot->ops == NULL))
+ return -EFAULT;
+
+ core = kmalloc (sizeof (struct hotplug_slot_core), GFP_KERNEL);
+ if (!core)
+ return -ENOMEM;
+
+ /* make sure we have not already registered this slot */
+ spin_lock (&list_lock);
+ if (get_slot_from_name (slot->name) != NULL) {
+ spin_unlock (&list_lock);
+ kfree (core);
+ return -EFAULT;
+ }
+
+ slot->core_priv = core;
+
+ list_add (&slot->slot_list, &pci_hotplug_slot_list);
+ spin_unlock (&list_lock);
+
+ 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;
+
+ /* make sure we have this slot in our list before trying to delete it */
+ spin_lock (&list_lock);
+ temp = get_slot_from_name (slot->name);
+ if (temp != slot) {
+ spin_unlock (&list_lock);
+ return -ENODEV;
+ }
+
+ list_del (&slot->slot_list);
+ spin_unlock (&list_lock);
+
+ fs_remove_slot (slot);
+ kfree(slot->core_priv);
+ dbg ("Removed slot %s from the list\n", slot->name);
+ return 0;
+}
+
+/**
+ * pci_hp_change_slot_info - changes the slot's information structure in the core
+ * @name: the name of the slot whose info has changed
+ * @info: pointer to the info copy into the slot's info structure
+ *
+ * A slot with @name 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 (const char *name, struct hotplug_slot_info *info)
+{
+ struct hotplug_slot *temp;
+
+ if (info == NULL)
+ return -ENODEV;
+
+ spin_lock (&list_lock);
+ temp = get_slot_from_name (name);
+ if (temp == NULL) {
+ spin_unlock (&list_lock);
+ return -ENODEV;
+ }
+
+ memcpy (temp->info, info, sizeof (struct hotplug_slot_info));
+ spin_unlock (&list_lock);
+ return 0;
+}
+
+static int __init pci_hotplug_init (void)
+{
+ int result;
+
+ spin_lock_init(&mount_lock);
+ spin_lock_init(&list_lock);
+
+ dbg("registering filesystem.\n");
+ result = register_filesystem(&pcihpfs_fs_type);
+ if (result) {
+ err("register_filesystem failed with %d\n", result);
+ goto exit;
+ }
+
+ info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+exit:
+ return result;
+}
+
+static void __exit pci_hotplug_exit (void)
+{
+ unregister_filesystem(&pcihpfs_fs_type);
+}
+
+module_init(pci_hotplug_init);
+module_exit(pci_hotplug_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+
+EXPORT_SYMBOL_GPL(pci_hp_register);
+EXPORT_SYMBOL_GPL(pci_hp_deregister);
+EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
+
diff --git a/drivers/hotplug/pci_hotplug_util.c b/drivers/hotplug/pci_hotplug_util.c
new file mode 100644
index 000000000000..8eb25ebf35e4
--- /dev/null
+++ b/drivers/hotplug/pci_hotplug_util.c
@@ -0,0 +1,403 @@
+/*
+ * 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.
+ *
+ * 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/pci.h>
+#include "pci_hotplug.h"
+
+
+#if !defined(CONFIG_HOTPLUG_PCI_MODULE)
+ #define MY_NAME "pci_hotplug"
+#else
+ #define MY_NAME THIS_MODULE->name
+#endif
+
+#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: "__FUNCTION__": " 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)
+
+
+/* local variables */
+static int debug;
+
+
+static int build_dev (struct pci_ops *ops, u8 bus, u8 slot, u8 function, struct pci_dev **pci_dev)
+{
+ struct pci_dev *my_dev;
+ struct pci_bus *my_bus;
+
+ /* Some validity checks. */
+ if ((function > 7) ||
+ (slot > 31) ||
+ (pci_dev == NULL) ||
+ (ops == NULL))
+ return -ENODEV;
+
+ my_dev = kmalloc (sizeof (struct pci_dev), GFP_KERNEL);
+ if (!my_dev)
+ return -ENOMEM;
+ my_bus = kmalloc (sizeof (struct pci_bus), GFP_KERNEL);
+ if (!my_bus) {
+ kfree (my_dev);
+ return -ENOMEM;
+ }
+ memset(my_dev, 0, sizeof(struct pci_dev));
+ memset(my_bus, 0, sizeof(struct pci_bus));
+
+ my_bus->number = bus;
+ my_bus->ops = ops;
+ my_dev->devfn = PCI_DEVFN(slot, function);
+ my_dev->bus = my_bus;
+ *pci_dev = my_dev;
+ return 0;
+}
+
+/**
+ * pci_read_config_byte_nodev - read a byte from a pci device
+ * @ops: pointer to a &struct pci_ops that will be used to read from the pci device
+ * @bus: the bus of the pci device to read from
+ * @slot: the pci slot number of the pci device to read from
+ * @function: the function of the pci device to read from
+ * @where: the location on the pci address space to read from
+ * @value: pointer to where to place the data read
+ *
+ * Like pci_read_config_byte() but works for pci devices that do not have a
+ * pci_dev structure set up yet.
+ * Returns 0 on success.
+ */
+int pci_read_config_byte_nodev (struct pci_ops *ops, u8 bus, u8 slot, u8 function, int where, u8 *value)
+{
+ struct pci_dev *dev = NULL;
+ int result;
+
+ dbg("%p, %d, %d, %d, %d, %p\n", ops, bus, slot, function, where, value);
+ dev = pci_find_slot(bus, PCI_DEVFN(slot, function));
+ if (dev) {
+ dbg("using native pci_dev\n");
+ return pci_read_config_byte (dev, where, value);
+ }
+
+ result = build_dev (ops, bus, slot, function, &dev);
+ if (result)
+ return result;
+ result = pci_read_config_byte(dev, where, value);
+ kfree (dev->bus);
+ kfree (dev);
+ return result;
+}
+
+/**
+ * pci_read_config_word_nodev - read a word from a pci device
+ * @ops: pointer to a &struct pci_ops that will be used to read from the pci device
+ * @bus: the bus of the pci device to read from
+ * @slot: the pci slot number of the pci device to read from
+ * @function: the function of the pci device to read from
+ * @where: the location on the pci address space to read from
+ * @value: pointer to where to place the data read
+ *
+ * Like pci_read_config_word() but works for pci devices that do not have a
+ * pci_dev structure set up yet.
+ * Returns 0 on success.
+ */
+int pci_read_config_word_nodev (struct pci_ops *ops, u8 bus, u8 slot, u8 function, int where, u16 *value)
+{
+ struct pci_dev *dev = NULL;
+ int result;
+
+ dbg("%p, %d, %d, %d, %d, %p\n", ops, bus, slot, function, where, value);
+ dev = pci_find_slot(bus, PCI_DEVFN(slot, function));
+ if (dev) {
+ dbg("using native pci_dev\n");
+ return pci_read_config_word (dev, where, value);
+ }
+
+ result = build_dev (ops, bus, slot, function, &dev);
+ if (result)
+ return result;
+ result = pci_read_config_word(dev, where, value);
+ kfree (dev->bus);
+ kfree (dev);
+ return result;
+}
+
+/**
+ * pci_read_config_dword_nodev - read a dword from a pci device
+ * @ops: pointer to a &struct pci_ops that will be used to read from the pci
+ * device
+ * @bus: the bus of the pci device to read from
+ * @slot: the pci slot number of the pci device to read from
+ * @function: the function of the pci device to read from
+ * @where: the location on the pci address space to read from
+ * @value: pointer to where to place the data read
+ *
+ * Like pci_read_config_dword() but works for pci devices that do not have a
+ * pci_dev structure set up yet.
+ * Returns 0 on success.
+ */
+int pci_read_config_dword_nodev (struct pci_ops *ops, u8 bus, u8 slot, u8 function, int where, u32 *value)
+{
+ struct pci_dev *dev = NULL;
+ int result;
+
+ dbg("%p, %d, %d, %d, %d, %p\n", ops, bus, slot, function, where, value);
+ dev = pci_find_slot(bus, PCI_DEVFN(slot, function));
+ if (dev) {
+ dbg("using native pci_dev\n");
+ return pci_read_config_dword (dev, where, value);
+ }
+
+ result = build_dev (ops, bus, slot, function, &dev);
+ if (result)
+ return result;
+ result = pci_read_config_dword(dev, where, value);
+ kfree (dev->bus);
+ kfree (dev);
+ return result;
+}
+
+/**
+ * pci_write_config_byte_nodev - write a byte to a pci device
+ * @ops: pointer to a &struct pci_ops that will be used to write to the pci
+ * device
+ * @bus: the bus of the pci device to write to
+ * @slot: the pci slot number of the pci device to write to
+ * @function: the function of the pci device to write to
+ * @where: the location on the pci address space to write to
+ * @value: the value to write to the pci device
+ *
+ * Like pci_write_config_byte() but works for pci devices that do not have a
+ * pci_dev structure set up yet.
+ * Returns 0 on success.
+ */
+int pci_write_config_byte_nodev (struct pci_ops *ops, u8 bus, u8 slot, u8 function, int where, u8 value)
+{
+ struct pci_dev *dev = NULL;
+ int result;
+
+ dbg("%p, %d, %d, %d, %d, %d\n", ops, bus, slot, function, where, value);
+ dev = pci_find_slot(bus, PCI_DEVFN(slot, function));
+ if (dev) {
+ dbg("using native pci_dev\n");
+ return pci_write_config_byte (dev, where, value);
+ }
+
+ result = build_dev (ops, bus, slot, function, &dev);
+ if (result)
+ return result;
+ result = pci_write_config_byte(dev, where, value);
+ kfree (dev->bus);
+ kfree (dev);
+ return result;
+}
+
+/**
+ * pci_write_config_word_nodev - write a word to a pci device
+ * @ops: pointer to a &struct pci_ops that will be used to write to the pci
+ * device
+ * @bus: the bus of the pci device to write to
+ * @slot: the pci slot number of the pci device to write to
+ * @function: the function of the pci device to write to
+ * @where: the location on the pci address space to write to
+ * @value: the value to write to the pci device
+ *
+ * Like pci_write_config_word() but works for pci devices that do not have a
+ * pci_dev structure set up yet.
+ * Returns 0 on success.
+ */
+int pci_write_config_word_nodev (struct pci_ops *ops, u8 bus, u8 slot, u8 function, int where, u16 value)
+{
+ struct pci_dev *dev = NULL;
+ int result;
+
+ dbg("%p, %d, %d, %d, %d, %d\n", ops, bus, slot, function, where, value);
+ dev = pci_find_slot(bus, PCI_DEVFN(slot, function));
+ if (dev) {
+ dbg("using native pci_dev\n");
+ return pci_write_config_word (dev, where, value);
+ }
+
+ result = build_dev (ops, bus, slot, function, &dev);
+ if (result)
+ return result;
+ result = pci_write_config_word(dev, where, value);
+ kfree (dev->bus);
+ kfree (dev);
+ return result;
+}
+
+/**
+ * pci_write_config_dword_nodev - write a dword to a pci device
+ * @ops: pointer to a &struct pci_ops that will be used to write to the pci
+ * device
+ * @bus: the bus of the pci device to write to
+ * @slot: the pci slot number of the pci device to write to
+ * @function: the function of the pci device to write to
+ * @where: the location on the pci address space to write to
+ * @value: the value to write to the pci device
+ *
+ * Like pci_write_config_dword() but works for pci devices that do not have a
+ * pci_dev structure set up yet.
+ * Returns 0 on success.
+ */
+int pci_write_config_dword_nodev (struct pci_ops *ops, u8 bus, u8 slot, u8 function, int where, u32 value)
+{
+ struct pci_dev *dev = NULL;
+ int result;
+
+ dbg("%p, %d, %d, %d, %d, %d\n", ops, bus, slot, function, where, value);
+ dev = pci_find_slot(bus, PCI_DEVFN(slot, function));
+ if (dev) {
+ dbg("using native pci_dev\n");
+ return pci_write_config_dword (dev, where, value);
+ }
+
+ result = build_dev (ops, bus, slot, function, &dev);
+ if (result)
+ return result;
+ result = pci_write_config_dword(dev, where, value);
+ kfree (dev->bus);
+ kfree (dev);
+ return result;
+}
+
+/*
+ * This is code that scans the pci buses.
+ * Every bus and every function is presented to a custom
+ * function that can act upon it.
+ */
+
+static int pci_visit_bus (struct pci_visit * fn, struct pci_bus_wrapped *wrapped_bus, struct pci_dev_wrapped *wrapped_parent)
+{
+ struct list_head *ln;
+ struct pci_dev *dev;
+ struct pci_dev_wrapped wrapped_dev;
+ int result = 0;
+
+ dbg("scanning bus %02x\n", wrapped_bus->bus->number);
+
+ if (fn->pre_visit_pci_bus) {
+ result = fn->pre_visit_pci_bus(wrapped_bus, wrapped_parent);
+ if (result)
+ return result;
+ }
+
+ ln = wrapped_bus->bus->devices.next;
+ while (ln != &wrapped_bus->bus->devices) {
+ dev = pci_dev_b(ln);
+ ln = ln->next;
+
+ memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped));
+ wrapped_dev.dev = dev;
+
+ result = pci_visit_dev(fn, &wrapped_dev, wrapped_bus);
+ if (result)
+ return result;
+ }
+
+ if (fn->post_visit_pci_bus)
+ result = fn->post_visit_pci_bus(wrapped_bus, wrapped_parent);
+
+ return result;
+}
+
+
+static int pci_visit_bridge (struct pci_visit * fn, struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_parent)
+{
+ struct pci_bus *bus = wrapped_dev->dev->subordinate;
+ struct pci_bus_wrapped wrapped_bus;
+ int result;
+
+ memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped));
+ wrapped_bus.bus = bus;
+
+ dbg("scanning bridge %02x, %02x\n", wrapped_dev->dev->devfn >> 3,
+ wrapped_dev->dev->devfn & 0x7);
+
+ if (fn->visit_pci_dev) {
+ result = fn->visit_pci_dev(wrapped_dev, wrapped_parent);
+ if (result)
+ return result;
+ }
+
+ result = pci_visit_bus(fn, &wrapped_bus, wrapped_dev);
+ return result;
+}
+
+
+int pci_visit_dev (struct pci_visit *fn, struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_parent)
+{
+ struct pci_dev* dev = wrapped_dev ? wrapped_dev->dev : NULL;
+ int result = 0;
+
+ if (!dev)
+ return 0;
+
+ if (fn->pre_visit_pci_dev) {
+ result = fn->pre_visit_pci_dev(wrapped_dev, wrapped_parent);
+ if (result)
+ return result;
+ }
+
+ switch (dev->class >> 8) {
+ case PCI_CLASS_BRIDGE_PCI:
+ result = pci_visit_bridge(fn, wrapped_dev,
+ wrapped_parent);
+ if (result)
+ return result;
+ break;
+ default:
+ dbg("scanning device %02x, %02x\n",
+ PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+ if (fn->visit_pci_dev) {
+ result = fn->visit_pci_dev (wrapped_dev,
+ wrapped_parent);
+ if (result)
+ return result;
+ }
+ }
+
+ if (fn->post_visit_pci_dev)
+ result = fn->post_visit_pci_dev(wrapped_dev, wrapped_parent);
+
+ return result;
+}
+
+
+EXPORT_SYMBOL(pci_visit_dev);
+EXPORT_SYMBOL(pci_read_config_byte_nodev);
+EXPORT_SYMBOL(pci_read_config_word_nodev);
+EXPORT_SYMBOL(pci_read_config_dword_nodev);
+EXPORT_SYMBOL(pci_write_config_byte_nodev);
+EXPORT_SYMBOL(pci_write_config_word_nodev);
+EXPORT_SYMBOL(pci_write_config_dword_nodev);
+