ChangeSet 1.1254.4.7, 2003/06/03 20:54:30-07:00, greg@kroah.com PCI Hotplug: move drivers/hotplug/* to drivers/pci/hotplug/* This will let include/linux/pci.h get smaller, and is what I should have done in the first place 2 years ago... drivers/hotplug/Kconfig | 120 - drivers/hotplug/Makefile | 44 drivers/hotplug/acpiphp.h | 262 -- drivers/hotplug/acpiphp_core.c | 505 ----- drivers/hotplug/acpiphp_glue.c | 1335 ------------- drivers/hotplug/acpiphp_pci.c | 510 ----- drivers/hotplug/acpiphp_res.c | 699 ------- drivers/hotplug/cpci_hotplug.h | 100 - drivers/hotplug/cpci_hotplug_core.c | 919 --------- drivers/hotplug/cpci_hotplug_pci.c | 647 ------ drivers/hotplug/cpcihp_generic.c | 290 --- drivers/hotplug/cpcihp_zt5550.c | 306 --- drivers/hotplug/cpcihp_zt5550.h | 79 drivers/hotplug/cpqphp.h | 912 --------- drivers/hotplug/cpqphp_core.c | 1541 --------------- drivers/hotplug/cpqphp_ctrl.c | 3084 -------------------------------- drivers/hotplug/cpqphp_nvram.c | 667 ------ drivers/hotplug/cpqphp_nvram.h | 57 drivers/hotplug/cpqphp_pci.c | 1548 ---------------- drivers/hotplug/cpqphp_sysfs.c | 143 - drivers/hotplug/ibmphp.h | 772 -------- drivers/hotplug/ibmphp_core.c | 1417 -------------- drivers/hotplug/ibmphp_ebda.c | 1228 ------------ drivers/hotplug/ibmphp_hpc.c | 1228 ------------ drivers/hotplug/ibmphp_pci.c | 1758 ------------------ drivers/hotplug/ibmphp_res.c | 2157 ---------------------- drivers/hotplug/pci_hotplug.h | 146 - drivers/hotplug/pci_hotplug_core.c | 666 ------ drivers/hotplug/pcihp_skeleton.c | 432 ---- arch/i386/Kconfig | 2 arch/ia64/Kconfig | 2 arch/m68knommu/Kconfig | 2 arch/ppc64/Kconfig | 2 arch/v850/Kconfig | 2 arch/x86_64/Kconfig | 2 drivers/Makefile | 1 drivers/pci/Makefile | 3 drivers/pci/hotplug/Kconfig | 120 + drivers/pci/hotplug/Makefile | 44 drivers/pci/hotplug/acpiphp.h | 262 ++ drivers/pci/hotplug/acpiphp_core.c | 505 +++++ drivers/pci/hotplug/acpiphp_glue.c | 1335 +++++++++++++ drivers/pci/hotplug/acpiphp_pci.c | 510 +++++ drivers/pci/hotplug/acpiphp_res.c | 699 +++++++ drivers/pci/hotplug/cpci_hotplug.h | 100 + drivers/pci/hotplug/cpci_hotplug_core.c | 919 +++++++++ drivers/pci/hotplug/cpci_hotplug_pci.c | 647 ++++++ drivers/pci/hotplug/cpcihp_generic.c | 290 +++ drivers/pci/hotplug/cpcihp_zt5550.c | 306 +++ drivers/pci/hotplug/cpcihp_zt5550.h | 79 drivers/pci/hotplug/cpqphp.h | 912 +++++++++ drivers/pci/hotplug/cpqphp_core.c | 1541 +++++++++++++++ drivers/pci/hotplug/cpqphp_ctrl.c | 3084 ++++++++++++++++++++++++++++++++ drivers/pci/hotplug/cpqphp_nvram.c | 667 ++++++ drivers/pci/hotplug/cpqphp_nvram.h | 57 drivers/pci/hotplug/cpqphp_pci.c | 1548 ++++++++++++++++ drivers/pci/hotplug/cpqphp_sysfs.c | 143 + drivers/pci/hotplug/ibmphp.h | 772 ++++++++ drivers/pci/hotplug/ibmphp_core.c | 1417 ++++++++++++++ drivers/pci/hotplug/ibmphp_ebda.c | 1228 ++++++++++++ drivers/pci/hotplug/ibmphp_hpc.c | 1228 ++++++++++++ drivers/pci/hotplug/ibmphp_pci.c | 1758 ++++++++++++++++++ drivers/pci/hotplug/ibmphp_res.c | 2157 ++++++++++++++++++++++ drivers/pci/hotplug/pci_hotplug.h | 146 + drivers/pci/hotplug/pci_hotplug_core.c | 666 ++++++ drivers/pci/hotplug/pcihp_skeleton.c | 432 ++++ 66 files changed, 23581 insertions(+), 23579 deletions(-) diff -Nru a/arch/i386/Kconfig b/arch/i386/Kconfig --- a/arch/i386/Kconfig Wed Jun 4 18:11:56 2003 +++ b/arch/i386/Kconfig Wed Jun 4 18:11:56 2003 @@ -1135,7 +1135,7 @@ source "drivers/pcmcia/Kconfig" -source "drivers/hotplug/Kconfig" +source "drivers/pci/hotplug/Kconfig" endmenu diff -Nru a/arch/ia64/Kconfig b/arch/ia64/Kconfig --- a/arch/ia64/Kconfig Wed Jun 4 18:11:56 2003 +++ b/arch/ia64/Kconfig Wed Jun 4 18:11:56 2003 @@ -563,7 +563,7 @@ agent" (/sbin/hotplug) to load modules and set up software needed to use devices as you hotplug them. -source "drivers/hotplug/Kconfig" +source "drivers/pci/hotplug/Kconfig" source "drivers/pcmcia/Kconfig" diff -Nru a/arch/m68knommu/Kconfig b/arch/m68knommu/Kconfig --- a/arch/m68knommu/Kconfig Wed Jun 4 18:11:56 2003 +++ b/arch/m68knommu/Kconfig Wed Jun 4 18:11:56 2003 @@ -488,7 +488,7 @@ source "drivers/pcmcia/Kconfig" -source "drivers/hotplug/Kconfig" +source "drivers/pci/hotplug/Kconfig" endmenu diff -Nru a/arch/ppc64/Kconfig b/arch/ppc64/Kconfig --- a/arch/ppc64/Kconfig Wed Jun 4 18:11:56 2003 +++ b/arch/ppc64/Kconfig Wed Jun 4 18:11:56 2003 @@ -234,7 +234,7 @@ source "drivers/pcmcia/Kconfig" -source "drivers/hotplug/Kconfig" +source "drivers/pci/hotplug/Kconfig" config PROC_DEVICETREE bool "Support for Open Firmware device tree in /proc" diff -Nru a/arch/v850/Kconfig b/arch/v850/Kconfig --- a/arch/v850/Kconfig Wed Jun 4 18:11:56 2003 +++ b/arch/v850/Kconfig Wed Jun 4 18:11:56 2003 @@ -230,7 +230,7 @@ source "drivers/pcmcia/Kconfig" -source "drivers/hotplug/Kconfig" +source "drivers/pci/hotplug/Kconfig" endmenu diff -Nru a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig --- a/arch/x86_64/Kconfig Wed Jun 4 18:11:56 2003 +++ b/arch/x86_64/Kconfig Wed Jun 4 18:11:56 2003 @@ -351,7 +351,7 @@ source "drivers/pcmcia/Kconfig" -source "drivers/hotplug/Kconfig" +source "drivers/pci/hotplug/Kconfig" endmenu diff -Nru a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile Wed Jun 4 18:11:56 2003 +++ b/drivers/Makefile Wed Jun 4 18:11:56 2003 @@ -46,7 +46,6 @@ obj-$(CONFIG_PHONE) += telephony/ obj-$(CONFIG_MD) += md/ obj-$(CONFIG_BT) += bluetooth/ -obj-$(CONFIG_HOTPLUG_PCI) += hotplug/ obj-$(CONFIG_ISDN_BOOL) += isdn/ obj-$(CONFIG_MCA) += mca/ obj-$(CONFIG_EISA) += eisa/ diff -Nru a/drivers/hotplug/Kconfig b/drivers/hotplug/Kconfig --- a/drivers/hotplug/Kconfig Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,120 +0,0 @@ -# -# PCI Hotplug support -# - -menu "PCI Hotplug Support" - depends on HOTPLUG - -config HOTPLUG_PCI - tristate "Support for PCI Hotplug (EXPERIMENTAL)" - depends on PCI && EXPERIMENTAL - ---help--- - Say Y here if you have a motherboard with a PCI Hotplug controller. - This allows you to add and remove PCI cards while the machine is - powered up and running. The file system pcihpfs must be mounted - in order to interact with any PCI Hotplug controllers. - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called pci_hotplug. If you want to compile it - as a module, say M here and read . - - When in doubt, say N. - -config HOTPLUG_PCI_COMPAQ - tristate "Compaq PCI Hotplug driver" - depends on HOTPLUG_PCI && X86 - help - Say Y here if you have a motherboard with a Compaq PCI Hotplug - controller. - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called cpqphp. If you want to compile it - as a module, say M here and read . - - When in doubt, say N. - -config HOTPLUG_PCI_COMPAQ_NVRAM - bool "Save configuration into NVRAM on Compaq servers" - depends on HOTPLUG_PCI_COMPAQ - help - Say Y here if you have a Compaq server that has a PCI Hotplug - controller. This will allow the PCI Hotplug driver to store the PCI - system configuration options in NVRAM. - - When in doubt, say N. - -config HOTPLUG_PCI_IBM - tristate "IBM PCI Hotplug driver" - depends on HOTPLUG_PCI && X86_IO_APIC && X86 - help - Say Y here if you have a motherboard with a IBM PCI Hotplug - controller. - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called cpqphp. If you want to compile it - as a module, say M here and read . - - When in doubt, say N. - -config HOTPLUG_PCI_ACPI - tristate "ACPI PCI Hotplug driver" - depends on ACPI_BUS && HOTPLUG_PCI - help - Say Y here if you have a system that supports PCI Hotplug using - ACPI. - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called acpiphp. If you want to compile it - as a module, say M here and read . - - When in doubt, say N. - -config HOTPLUG_PCI_CPCI - tristate "CompactPCI Hotplug driver" - depends on HOTPLUG_PCI - help - Say Y here if you have a CompactPCI system card with CompactPCI - hotswap support per the PICMG 2.1 specification. - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called cpci_hotplug. If you want to compile it - as a module, say M here and read . - - When in doubt, say N. - -config HOTPLUG_PCI_CPCI_ZT5550 - tristate "Ziatech ZT5550 CompactPCI Hotplug driver" - depends on HOTPLUG_PCI_CPCI && X86 - help - Say Y here if you have an Performance Technologies (formerly Intel, - formerly just Ziatech) Ziatech ZT5550 CompactPCI system card. - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called cpcihp_zt5550. If you want to compile it - as a module, say M here and read . - - When in doubt, say N. - -config HOTPLUG_PCI_CPCI_GENERIC - tristate "Generic port I/O CompactPCI Hotplug driver" - depends on HOTPLUG_PCI_CPCI && X86 - help - Say Y here if you have a CompactPCI system card that exposes the #ENUM - hotswap signal as a bit in a system register that can be read through - standard port I/O. - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called cpcihp_generic. If you want to compile it - as a module, say M here and read . - - When in doubt, say N. - -endmenu - diff -Nru a/drivers/hotplug/Makefile b/drivers/hotplug/Makefile --- a/drivers/hotplug/Makefile Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,44 +0,0 @@ -# -# Makefile for the Linux kernel pci hotplug controller drivers. -# - -obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o -obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o -obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o -obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o -obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o -obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o - -pci_hotplug-objs := pci_hotplug_core.o - -ifdef CONFIG_HOTPLUG_PCI_CPCI -pci_hotplug-objs += cpci_hotplug_core.o \ - cpci_hotplug_pci.o -endif - -cpqphp-objs := cpqphp_core.o \ - cpqphp_ctrl.o \ - cpqphp_sysfs.o \ - cpqphp_pci.o - -ibmphp-objs := ibmphp_core.o \ - ibmphp_ebda.o \ - ibmphp_pci.o \ - ibmphp_res.o \ - ibmphp_hpc.o - -acpiphp-objs := acpiphp_core.o \ - acpiphp_glue.o \ - acpiphp_pci.o \ - acpiphp_res.o - -ifdef CONFIG_HOTPLUG_PCI_ACPI - EXTRA_CFLAGS += -D_LINUX -I$(TOPDIR)/drivers/acpi - ifdef CONFIG_ACPI_DEBUG - EXTRA_CFLAGS += -DACPI_DEBUG_OUTPUT - endif -endif - -ifeq ($(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM),y) - cpqphp-objs += cpqphp_nvram.o -endif diff -Nru a/drivers/hotplug/acpiphp.h b/drivers/hotplug/acpiphp.h --- a/drivers/hotplug/acpiphp.h Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,262 +0,0 @@ -/* - * ACPI PCI Hot Plug Controller Driver - * - * Copyright (c) 1995,2001 Compaq Computer Corporation - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001 IBM Corp. - * Copyright (c) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) - * Copyright (c) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com) - * Copyright (c) 2002 NEC Corporation - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to , - * , - * - * - */ - -#ifndef _ACPIPHP_H -#define _ACPIPHP_H - -#include -#include "pci_hotplug.h" - -#define dbg(format, arg...) \ - do { \ - if (acpiphp_debug) \ - printk(KERN_DEBUG "%s: " format, \ - MY_NAME , ## arg); \ - } while (0) -#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) -#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) - -#define SLOT_MAGIC 0x67267322 -/* name size which is used for entries in pcihpfs */ -#define SLOT_NAME_SIZE 32 /* ACPI{_SUN}-{BUS}:{DEV} */ - -struct acpiphp_bridge; -struct acpiphp_slot; -struct pci_resource; - -/* - * struct slot - slot information for each *physical* slot - */ -struct slot { - u32 magic; - u8 number; - struct hotplug_slot *hotplug_slot; - struct list_head slot_list; - - struct acpiphp_slot *acpi_slot; -}; - -/* - * struct pci_resource - describes pci resource (mem, pfmem, io, bus) - */ -struct pci_resource { - struct pci_resource * next; - u64 base; - u32 length; -}; - -/** - * struct hpp_param - ACPI 2.0 _HPP Hot Plug Parameters - * @cache_line_size in DWORD - * @latency_timer in PCI clock - * @enable_SERR 0 or 1 - * @enable_PERR 0 or 1 - */ -struct hpp_param { - u8 cache_line_size; - u8 latency_timer; - u8 enable_SERR; - u8 enable_PERR; -}; - - -/** - * struct acpiphp_bridge - PCI bridge information - * - * for each bridge device in ACPI namespace - */ -struct acpiphp_bridge { - struct list_head list; - acpi_handle handle; - struct acpiphp_slot *slots; - int type; - int nr_slots; - - u8 seg; - u8 bus; - u8 sub; - - u32 flags; - - /* This bus (host bridge) or Secondary bus (PCI-to-PCI bridge) */ - struct pci_bus *pci_bus; - - /* PCI-to-PCI bridge device */ - struct pci_dev *pci_dev; - - /* ACPI 2.0 _HPP parameters */ - struct hpp_param hpp; - - spinlock_t res_lock; - - /* available resources on this bus */ - struct pci_resource *mem_head; - struct pci_resource *p_mem_head; - struct pci_resource *io_head; - struct pci_resource *bus_head; -}; - - -/** - * struct acpiphp_slot - PCI slot information - * - * PCI slot information for each *physical* PCI slot - */ -struct acpiphp_slot { - struct acpiphp_slot *next; - struct acpiphp_bridge *bridge; /* parent */ - struct list_head funcs; /* one slot may have different - objects (i.e. for each function) */ - struct semaphore crit_sect; - - u32 id; /* slot id (serial #) for hotplug core */ - u8 device; /* pci device# */ - - u32 sun; /* ACPI _SUN (slot unique number) */ - u32 slotno; /* slot number relative to bridge */ - u32 flags; /* see below */ -}; - - -/** - * struct acpiphp_func - PCI function information - * - * PCI function information for each object in ACPI namespace - * typically 8 objects per slot (i.e. for each PCI function) - */ -struct acpiphp_func { - struct acpiphp_slot *slot; /* parent */ - - struct list_head sibling; - struct pci_dev *pci_dev; - - acpi_handle handle; - - u8 function; /* pci function# */ - u32 flags; /* see below */ - - /* resources used for this function */ - struct pci_resource *mem_head; - struct pci_resource *p_mem_head; - struct pci_resource *io_head; - struct pci_resource *bus_head; -}; - - -/* PCI bus bridge HID */ -#define ACPI_PCI_HOST_HID "PNP0A03" - -/* PCI BRIDGE type */ -#define BRIDGE_TYPE_HOST 0 -#define BRIDGE_TYPE_P2P 1 - -/* ACPI _STA method value (ignore bit 4; battery present) */ -#define ACPI_STA_PRESENT (0x00000001) -#define ACPI_STA_ENABLED (0x00000002) -#define ACPI_STA_SHOW_IN_UI (0x00000004) -#define ACPI_STA_FUNCTIONING (0x00000008) -#define ACPI_STA_ALL (0x0000000f) - -/* bridge flags */ -#define BRIDGE_HAS_STA (0x00000001) -#define BRIDGE_HAS_EJ0 (0x00000002) -#define BRIDGE_HAS_HPP (0x00000004) -#define BRIDGE_HAS_PS0 (0x00000010) -#define BRIDGE_HAS_PS1 (0x00000020) -#define BRIDGE_HAS_PS2 (0x00000040) -#define BRIDGE_HAS_PS3 (0x00000080) - -/* slot flags */ - -#define SLOT_POWEREDON (0x00000001) -#define SLOT_ENABLED (0x00000002) -#define SLOT_MULTIFUNCTION (x000000004) - -/* function flags */ - -#define FUNC_HAS_STA (0x00000001) -#define FUNC_HAS_EJ0 (0x00000002) -#define FUNC_HAS_PS0 (0x00000010) -#define FUNC_HAS_PS1 (0x00000020) -#define FUNC_HAS_PS2 (0x00000040) -#define FUNC_HAS_PS3 (0x00000080) - -/* not yet */ -#define SLOT_SUPPORT_66MHZ (0x00010000) -#define SLOT_SUPPORT_100MHZ (0x00020000) -#define SLOT_SUPPORT_133MHZ (0x00040000) -#define SLOT_SUPPORT_PCIX (0x00080000) - -/* function prototypes */ - -/* acpiphp_glue.c */ -extern int acpiphp_glue_init (void); -extern void acpiphp_glue_exit (void); -extern int acpiphp_get_num_slots (void); -extern struct acpiphp_slot *get_slot_from_id (int id); -typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data); -extern int acpiphp_for_each_slot (acpiphp_callback fn, void *data); - -extern int acpiphp_check_bridge (struct acpiphp_bridge *bridge); -extern int acpiphp_enable_slot (struct acpiphp_slot *slot); -extern int acpiphp_disable_slot (struct acpiphp_slot *slot); -extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot); -extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot); -extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot); -extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot); - -/* acpiphp_pci.c */ -extern struct pci_dev *acpiphp_allocate_pcidev (struct pci_bus *pbus, int dev, int fn); -extern int acpiphp_configure_slot (struct acpiphp_slot *slot); -extern int acpiphp_configure_function (struct acpiphp_func *func); -extern int acpiphp_unconfigure_function (struct acpiphp_func *func); -extern int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge); -extern int acpiphp_init_func_resource (struct acpiphp_func *func); - -/* acpiphp_res.c */ -extern struct pci_resource *acpiphp_get_io_resource (struct pci_resource **head, u32 size); -extern struct pci_resource *acpiphp_get_max_resource (struct pci_resource **head, u32 size); -extern struct pci_resource *acpiphp_get_resource (struct pci_resource **head, u32 size); -extern struct pci_resource *acpiphp_get_resource_with_base (struct pci_resource **head, u64 base, u32 size); -extern int acpiphp_resource_sort_and_combine (struct pci_resource **head); -extern struct pci_resource *acpiphp_make_resource (u64 base, u32 length); -extern void acpiphp_move_resource (struct pci_resource **from, struct pci_resource **to); -extern void acpiphp_free_resource (struct pci_resource **res); -extern void acpiphp_dump_resource (struct acpiphp_bridge *bridge); /* debug */ -extern void acpiphp_dump_func_resource (struct acpiphp_func *func); /* debug */ - -/* variables */ -extern int acpiphp_debug; - -#endif /* _ACPIPHP_H */ diff -Nru a/drivers/hotplug/acpiphp_core.c b/drivers/hotplug/acpiphp_core.c --- a/drivers/hotplug/acpiphp_core.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,505 +0,0 @@ -/* - * ACPI PCI Hot Plug Controller Driver - * - * Copyright (c) 1995,2001 Compaq Computer Corporation - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001 IBM Corp. - * Copyright (c) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) - * Copyright (c) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com) - * Copyright (c) 2002 NEC Corporation - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to , - * , - * - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "pci_hotplug.h" -#include "acpiphp.h" - -static LIST_HEAD(slot_list); - -#if !defined(CONFIG_HOTPLUG_PCI_ACPI_MODULE) - #define MY_NAME "acpiphp" -#else - #define MY_NAME THIS_MODULE->name -#endif - -static int debug; -int acpiphp_debug; - -/* local variables */ -static int num_slots; - -#define DRIVER_VERSION "0.4" -#define DRIVER_AUTHOR "Greg Kroah-Hartman , Takayoshi Kochi " -#define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver" - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_PARM(debug, "i"); -MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); - -static int enable_slot (struct hotplug_slot *slot); -static int disable_slot (struct hotplug_slot *slot); -static int set_attention_status (struct hotplug_slot *slot, u8 value); -static int hardware_test (struct hotplug_slot *slot, u32 value); -static int get_power_status (struct hotplug_slot *slot, u8 *value); -static int get_attention_status (struct hotplug_slot *slot, u8 *value); -static int get_latch_status (struct hotplug_slot *slot, u8 *value); -static int get_adapter_status (struct hotplug_slot *slot, u8 *value); -static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value); -static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value); - -static struct hotplug_slot_ops acpi_hotplug_slot_ops = { - .owner = THIS_MODULE, - .enable_slot = enable_slot, - .disable_slot = disable_slot, - .set_attention_status = set_attention_status, - .hardware_test = hardware_test, - .get_power_status = get_power_status, - .get_attention_status = get_attention_status, - .get_latch_status = get_latch_status, - .get_adapter_status = get_adapter_status, - .get_max_bus_speed = get_max_bus_speed, - .get_cur_bus_speed = get_cur_bus_speed, -}; - - -/* 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\n", function); - return -1; - } - if (slot->magic != SLOT_MAGIC) { - dbg("%s - bad magic number for slot\n", function); - return -1; - } - if (!slot->hotplug_slot) { - dbg("%s - slot->hotplug_slot == NULL!\n", 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; -} - - -/** - * enable_slot - power on and enable a slot - * @hotplug_slot: slot to enable - * - * Actual tasks are done in acpiphp_enable_slot() - * - */ -static int enable_slot (struct hotplug_slot *hotplug_slot) -{ - struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - /* enable the specified slot */ - retval = acpiphp_enable_slot(slot->acpi_slot); - - return retval; -} - - -/** - * disable_slot - disable and power off a slot - * @hotplug_slot: slot to disable - * - * Actual tasks are done in acpiphp_disable_slot() - * - */ -static int disable_slot (struct hotplug_slot *hotplug_slot) -{ - struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - /* disable the specified slot */ - retval = acpiphp_disable_slot(slot->acpi_slot); - - return retval; -} - - -/** - * set_attention_status - set attention LED - * - * TBD: - * ACPI doesn't have known method to manipulate - * attention status LED. - * - */ -static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status) -{ - int retval = 0; - - dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - switch (status) { - case 0: - /* FIXME turn light off */ - hotplug_slot->info->attention_status = 0; - break; - - case 1: - default: - /* FIXME turn light on */ - hotplug_slot->info->attention_status = 1; - break; - } - - return retval; -} - - -/** - * hardware_test - hardware test - * - * We have nothing to do for now... - * - */ -static int hardware_test (struct hotplug_slot *hotplug_slot, u32 value) -{ - struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - err("No hardware tests are defined for this driver\n"); - retval = -ENODEV; - - return retval; -} - - -/** - * get_power_status - get power status of a slot - * @hotplug_slot: slot to get status - * @value: pointer to store status - * - * Some platforms may not implement _STA method properly. - * In that case, the value returned may not be reliable. - * - */ -static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value) -{ - struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - *value = acpiphp_get_power_status(slot->acpi_slot); - - return retval; -} - - -/** - * get_attention_status - get attention LED status - * - * TBD: - * ACPI doesn't provide any formal means to access attention LED status. - * - */ -static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value) -{ - int retval = 0; - - dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - *value = hotplug_slot->info->attention_status; - - return retval; -} - - -/** - * get_latch_status - get latch status of a slot - * @hotplug_slot: slot to get status - * @value: pointer to store status - * - * ACPI doesn't provide any formal means to access latch status. - * Instead, we fake latch status from _STA - * - */ -static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value) -{ - struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - *value = acpiphp_get_latch_status(slot->acpi_slot); - - return retval; -} - - -/** - * get_adapter_status - get adapter status of a slot - * @hotplug_slot: slot to get status - * @value: pointer to store status - * - * ACPI doesn't provide any formal means to access adapter status. - * Instead, we fake adapter status from _STA - * - */ -static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) -{ - struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - *value = acpiphp_get_adapter_status(slot->acpi_slot); - - return retval; -} - - -/* return dummy value because ACPI doesn't provide any method... */ -static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) -{ - struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); - - if (slot == NULL) - return -ENODEV; - - *value = PCI_SPEED_UNKNOWN; - - return 0; -} - - -/* return dummy value because ACPI doesn't provide any method... */ -static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) -{ - struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); - - if (slot == NULL) - return -ENODEV; - - *value = PCI_SPEED_UNKNOWN; - - return 0; -} - - -static int init_acpi (void) -{ - int retval; - - /* initialize internal data structure etc. */ - retval = acpiphp_glue_init(); - - /* read initial number of slots */ - if (!retval) { - num_slots = acpiphp_get_num_slots(); - if (num_slots == 0) - retval = -ENODEV; - } - - return retval; -} - - -/** - * make_slot_name - make a slot name that appears in pcihpfs - * @slot: slot to name - * - */ -static void make_slot_name (struct slot *slot) -{ - snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "ACPI%d-%02x:%02x", - slot->acpi_slot->sun, - slot->acpi_slot->bridge->bus, - slot->acpi_slot->device); -} - -/** - * init_slots - initialize 'struct slot' structures for each slot - * - */ -static int init_slots (void) -{ - struct slot *slot; - int retval = 0; - int i; - - for (i = 0; i < num_slots; ++i) { - slot = kmalloc(sizeof(struct slot), GFP_KERNEL); - if (!slot) - return -ENOMEM; - memset(slot, 0, sizeof(struct slot)); - - slot->hotplug_slot = kmalloc(sizeof(struct hotplug_slot), GFP_KERNEL); - if (!slot->hotplug_slot) { - kfree(slot); - return -ENOMEM; - } - memset(slot->hotplug_slot, 0, sizeof(struct hotplug_slot)); - - slot->hotplug_slot->info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); - if (!slot->hotplug_slot->info) { - kfree(slot->hotplug_slot); - kfree(slot); - return -ENOMEM; - } - memset(slot->hotplug_slot->info, 0, sizeof(struct hotplug_slot_info)); - - slot->hotplug_slot->name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL); - if (!slot->hotplug_slot->name) { - kfree(slot->hotplug_slot->info); - kfree(slot->hotplug_slot); - kfree(slot); - return -ENOMEM; - } - - slot->magic = SLOT_MAGIC; - slot->number = i; - - slot->hotplug_slot->private = slot; - slot->hotplug_slot->ops = &acpi_hotplug_slot_ops; - - slot->acpi_slot = get_slot_from_id(i); - slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot); - slot->hotplug_slot->info->attention_status = acpiphp_get_attention_status(slot->acpi_slot); - slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot); - slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot); - - make_slot_name(slot); - - retval = pci_hp_register(slot->hotplug_slot); - if (retval) { - err("pci_hp_register failed with error %d\n", retval); - kfree(slot->hotplug_slot->info); - kfree(slot->hotplug_slot->name); - kfree(slot->hotplug_slot); - kfree(slot); - return retval; - } - - /* add slot to our internal list */ - list_add(&slot->slot_list, &slot_list); - info("Slot [%s] registered\n", slot->hotplug_slot->name); - } - - return retval; -} - - -static void cleanup_slots (void) -{ - struct list_head *tmp, *n; - struct slot *slot; - - list_for_each_safe (tmp, n, &slot_list) { - slot = list_entry(tmp, struct slot, slot_list); - list_del(&slot->slot_list); - pci_hp_deregister(slot->hotplug_slot); - kfree(slot->hotplug_slot->info); - kfree(slot->hotplug_slot->name); - kfree(slot->hotplug_slot); - kfree(slot); - } - - return; -} - - -static int __init acpiphp_init(void) -{ - int retval; - - info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - - acpiphp_debug = debug; - - /* read all the ACPI info from the system */ - retval = init_acpi(); - if (retval) - return retval; - - retval = init_slots(); - if (retval) - return retval; - - return 0; -} - - -static void __exit acpiphp_exit(void) -{ - cleanup_slots(); - /* deallocate internal data structures etc. */ - acpiphp_glue_exit(); -} - -module_init(acpiphp_init); -module_exit(acpiphp_exit); diff -Nru a/drivers/hotplug/acpiphp_glue.c b/drivers/hotplug/acpiphp_glue.c --- a/drivers/hotplug/acpiphp_glue.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1335 +0,0 @@ -/* - * ACPI PCI HotPlug glue functions to ACPI CA subsystem - * - * Copyright (c) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com) - * Copyright (c) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) - * Copyright (c) 2002 NEC Corporation - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "pci_hotplug.h" -#include "acpiphp.h" - -static LIST_HEAD(bridge_list); - -#define MY_NAME "acpiphp_glue" - -static void handle_hotplug_event_bridge (acpi_handle, u32, void *); -static void handle_hotplug_event_func (acpi_handle, u32, void *); - -/* - * initialization & terminatation routines - */ - -/** - * is_ejectable - determine if a slot is ejectable - * @handle: handle to acpi namespace - * - * Ejectable slot should satisfy at least these conditions: - * - * 1. has _ADR method - * 2. has _EJ0 method - * - * optionally - * - * 1. has _STA method - * 2. has _PS0 method - * 3. has _PS3 method - * 4. .. - * - */ -static int is_ejectable (acpi_handle handle) -{ - acpi_status status; - acpi_handle tmp; - - status = acpi_get_handle(handle, "_ADR", &tmp); - if (ACPI_FAILURE(status)) { - return 0; - } - - status = acpi_get_handle(handle, "_EJ0", &tmp); - if (ACPI_FAILURE(status)) { - return 0; - } - - return 1; -} - - -/* callback routine to check the existence of ejectable slots */ -static acpi_status -is_ejectable_slot (acpi_handle handle, u32 lvl, void *context, void **rv) -{ - int *count = (int *)context; - - if (is_ejectable(handle)) { - (*count)++; - /* only one ejectable slot is enough */ - return AE_CTRL_TERMINATE; - } else { - return AE_OK; - } -} - - -/* callback routine to register each ACPI PCI slot object */ -static acpi_status -register_slot (acpi_handle handle, u32 lvl, void *context, void **rv) -{ - struct acpiphp_bridge *bridge = (struct acpiphp_bridge *)context; - struct acpiphp_slot *slot; - struct acpiphp_func *newfunc; - acpi_handle tmp; - acpi_status status = AE_OK; - unsigned long adr, sun; - int device, function; - static int num_slots = 0; /* XXX if we support I/O node hotplug... */ - - status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); - - if (ACPI_FAILURE(status)) - return AE_OK; - - status = acpi_get_handle(handle, "_EJ0", &tmp); - - if (ACPI_FAILURE(status)) - return AE_OK; - - device = (adr >> 16) & 0xffff; - function = adr & 0xffff; - - newfunc = kmalloc(sizeof(struct acpiphp_func), GFP_KERNEL); - if (!newfunc) - return AE_NO_MEMORY; - memset(newfunc, 0, sizeof(struct acpiphp_func)); - - INIT_LIST_HEAD(&newfunc->sibling); - newfunc->handle = handle; - newfunc->function = function; - newfunc->flags = FUNC_HAS_EJ0; - - if (ACPI_SUCCESS(acpi_get_handle(handle, "_STA", &tmp))) - newfunc->flags |= FUNC_HAS_STA; - - if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS0", &tmp))) - newfunc->flags |= FUNC_HAS_PS0; - - if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp))) - newfunc->flags |= FUNC_HAS_PS3; - - status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun); - if (ACPI_FAILURE(status)) - sun = -1; - - /* search for objects that share the same slot */ - for (slot = bridge->slots; slot; slot = slot->next) - if (slot->device == device) { - if (slot->sun != sun) - warn("sibling found, but _SUN doesn't match!\n"); - break; - } - - if (!slot) { - slot = kmalloc(sizeof(struct acpiphp_slot), GFP_KERNEL); - if (!slot) { - kfree(newfunc); - return AE_NO_MEMORY; - } - - memset(slot, 0, sizeof(struct acpiphp_slot)); - slot->bridge = bridge; - slot->id = num_slots++; - slot->device = device; - slot->sun = sun; - INIT_LIST_HEAD(&slot->funcs); - init_MUTEX(&slot->crit_sect); - - slot->next = bridge->slots; - bridge->slots = slot; - - bridge->nr_slots++; - - dbg("found ACPI PCI Hotplug slot at PCI %02x:%02x Slot:%d\n", - slot->bridge->bus, slot->device, slot->sun); - } - - newfunc->slot = slot; - list_add_tail(&newfunc->sibling, &slot->funcs); - - /* associate corresponding pci_dev */ - newfunc->pci_dev = pci_find_slot(bridge->bus, - PCI_DEVFN(device, function)); - if (newfunc->pci_dev) { - if (acpiphp_init_func_resource(newfunc) < 0) { - kfree(newfunc); - return AE_ERROR; - } - slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); - } - - /* install notify handler */ - status = acpi_install_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - handle_hotplug_event_func, - newfunc); - - if (ACPI_FAILURE(status)) { - err("failed to register interrupt notify handler\n"); - kfree(newfunc); - return status; - } - - return AE_OK; -} - - -/* see if it's worth looking at this bridge */ -static int detect_ejectable_slots (acpi_handle *bridge_handle) -{ - acpi_status status; - int count; - - count = 0; - - /* only check slots defined directly below bridge object */ - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1, - is_ejectable_slot, (void *)&count, NULL); - - return count; -} - - -/* decode ACPI _CRS data and convert into our internal resource list - * TBD: _TRA, etc. - */ -static acpi_status -decode_acpi_resource (struct acpi_resource *resource, void *context) -{ - struct acpiphp_bridge *bridge = (struct acpiphp_bridge *) context; - struct acpi_resource_address64 address; - struct pci_resource *res; - - if (resource->id != ACPI_RSTYPE_ADDRESS16 && - resource->id != ACPI_RSTYPE_ADDRESS32 && - resource->id != ACPI_RSTYPE_ADDRESS64) - return AE_OK; - - acpi_resource_to_address64(resource, &address); - - if (address.producer_consumer == ACPI_PRODUCER && address.address_length > 0) { - dbg("resource type: %d: 0x%llx - 0x%llx\n", address.resource_type, address.min_address_range, address.max_address_range); - res = acpiphp_make_resource(address.min_address_range, - address.address_length); - if (!res) { - err("out of memory\n"); - return AE_OK; - } - - switch (address.resource_type) { - case ACPI_MEMORY_RANGE: - if (address.attribute.memory.cache_attribute == ACPI_PREFETCHABLE_MEMORY) { - res->next = bridge->p_mem_head; - bridge->p_mem_head = res; - } else { - res->next = bridge->mem_head; - bridge->mem_head = res; - } - break; - case ACPI_IO_RANGE: - res->next = bridge->io_head; - bridge->io_head = res; - break; - case ACPI_BUS_NUMBER_RANGE: - res->next = bridge->bus_head; - bridge->bus_head = res; - break; - default: - /* invalid type */ - kfree(res); - break; - } - } - - return AE_OK; -} - -/* decode ACPI 2.0 _HPP hot plug parameters */ -static void decode_hpp(struct acpiphp_bridge *bridge) -{ - acpi_status status; - struct acpi_buffer buffer = { .length = ACPI_ALLOCATE_BUFFER, - .pointer = NULL}; - union acpi_object *package; - int i; - - /* default numbers */ - bridge->hpp.cache_line_size = 0x10; - bridge->hpp.latency_timer = 0x40; - bridge->hpp.enable_SERR = 0; - bridge->hpp.enable_PERR = 0; - - status = acpi_evaluate_object(bridge->handle, "_HPP", NULL, &buffer); - - if (ACPI_FAILURE(status)) { - dbg("_HPP evaluation failed\n"); - return; - } - - package = (union acpi_object *) buffer.pointer; - - if (!package || package->type != ACPI_TYPE_PACKAGE || - package->package.count != 4 || !package->package.elements) { - err("invalid _HPP object; ignoring\n"); - goto err_exit; - } - - for (i = 0; i < 4; i++) { - if (package->package.elements[i].type != ACPI_TYPE_INTEGER) { - err("invalid _HPP parameter type; ignoring\n"); - goto err_exit; - } - } - - bridge->hpp.cache_line_size = package->package.elements[0].integer.value; - bridge->hpp.latency_timer = package->package.elements[1].integer.value; - bridge->hpp.enable_SERR = package->package.elements[2].integer.value; - bridge->hpp.enable_PERR = package->package.elements[3].integer.value; - - dbg("_HPP parameter = (%02x, %02x, %02x, %02x)\n", - bridge->hpp.cache_line_size, - bridge->hpp.latency_timer, - bridge->hpp.enable_SERR, - bridge->hpp.enable_PERR); - - bridge->flags |= BRIDGE_HAS_HPP; - - err_exit: - kfree(buffer.pointer); -} - - -/* initialize miscellaneous stuff for both root and PCI-to-PCI bridge */ -static void init_bridge_misc (struct acpiphp_bridge *bridge) -{ - acpi_status status; - - /* decode ACPI 2.0 _HPP (hot plug parameters) */ - decode_hpp(bridge); - - /* subtract all resources already allocated */ - acpiphp_detect_pci_resource(bridge); - - /* register all slot objects under this bridge */ - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, (u32)1, - register_slot, bridge, NULL); - - /* install notify handler */ - status = acpi_install_notify_handler(bridge->handle, - ACPI_SYSTEM_NOTIFY, - handle_hotplug_event_bridge, - bridge); - - if (ACPI_FAILURE(status)) { - err("failed to register interrupt notify handler\n"); - } - - list_add(&bridge->list, &bridge_list); - - dbg("Bridge resource:\n"); - acpiphp_dump_resource(bridge); -} - - -/* allocate and initialize host bridge data structure */ -static void add_host_bridge (acpi_handle *handle, int seg, int bus) -{ - acpi_status status; - struct acpiphp_bridge *bridge; - - bridge = kmalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); - if (bridge == NULL) - return; - - memset(bridge, 0, sizeof(struct acpiphp_bridge)); - - bridge->type = BRIDGE_TYPE_HOST; - bridge->handle = handle; - bridge->seg = seg; - bridge->bus = bus; - - bridge->pci_bus = pci_find_bus(bus); - - bridge->res_lock = SPIN_LOCK_UNLOCKED; - - /* to be overridden when we decode _CRS */ - bridge->sub = bridge->bus; - - /* decode resources */ - - status = acpi_walk_resources(handle, METHOD_NAME__CRS, - decode_acpi_resource, bridge); - - if (ACPI_FAILURE(status)) { - err("failed to decode bridge resources\n"); - kfree(bridge); - return; - } - - acpiphp_resource_sort_and_combine(&bridge->io_head); - acpiphp_resource_sort_and_combine(&bridge->mem_head); - acpiphp_resource_sort_and_combine(&bridge->p_mem_head); - acpiphp_resource_sort_and_combine(&bridge->bus_head); - - dbg("ACPI _CRS resource:\n"); - acpiphp_dump_resource(bridge); - - if (bridge->bus_head) { - bridge->bus = bridge->bus_head->base; - bridge->sub = bridge->bus_head->base + bridge->bus_head->length - 1; - } - - init_bridge_misc(bridge); -} - - -/* allocate and initialize PCI-to-PCI bridge data structure */ -static void add_p2p_bridge (acpi_handle *handle, int seg, int bus, int dev, int fn) -{ - struct acpiphp_bridge *bridge; - u8 tmp8; - u16 tmp16; - u64 base64, limit64; - u32 base, limit, base32u, limit32u; - - bridge = kmalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); - if (bridge == NULL) { - err("out of memory\n"); - return; - } - - memset(bridge, 0, sizeof(struct acpiphp_bridge)); - - bridge->type = BRIDGE_TYPE_P2P; - bridge->handle = handle; - bridge->seg = seg; - - bridge->pci_dev = pci_find_slot(bus, PCI_DEVFN(dev, fn)); - if (!bridge->pci_dev) { - err("Can't get pci_dev\n"); - kfree(bridge); - return; - } - - bridge->pci_bus = bridge->pci_dev->subordinate; - if (!bridge->pci_bus) { - err("This is not a PCI-to-PCI bridge!\n"); - kfree(bridge); - return; - } - - bridge->res_lock = SPIN_LOCK_UNLOCKED; - - bridge->bus = bridge->pci_bus->number; - bridge->sub = bridge->pci_bus->subordinate; - - /* - * decode resources under this P2P bridge - */ - - /* I/O resources */ - pci_read_config_byte(bridge->pci_dev, PCI_IO_BASE, &tmp8); - base = tmp8; - pci_read_config_byte(bridge->pci_dev, PCI_IO_LIMIT, &tmp8); - limit = tmp8; - - switch (base & PCI_IO_RANGE_TYPE_MASK) { - case PCI_IO_RANGE_TYPE_16: - base = (base << 8) & 0xf000; - limit = ((limit << 8) & 0xf000) + 0xfff; - bridge->io_head = acpiphp_make_resource((u64)base, limit - base + 1); - if (!bridge->io_head) { - err("out of memory\n"); - kfree(bridge); - return; - } - dbg("16bit I/O range: %04x-%04x\n", - (u32)bridge->io_head->base, - (u32)(bridge->io_head->base + bridge->io_head->length - 1)); - break; - case PCI_IO_RANGE_TYPE_32: - pci_read_config_word(bridge->pci_dev, PCI_IO_BASE_UPPER16, &tmp16); - base = ((u32)tmp16 << 16) | ((base << 8) & 0xf000); - pci_read_config_word(bridge->pci_dev, PCI_IO_LIMIT_UPPER16, &tmp16); - limit = (((u32)tmp16 << 16) | ((limit << 8) & 0xf000)) + 0xfff; - bridge->io_head = acpiphp_make_resource((u64)base, limit - base + 1); - if (!bridge->io_head) { - err("out of memory\n"); - kfree(bridge); - return; - } - dbg("32bit I/O range: %08x-%08x\n", - (u32)bridge->io_head->base, - (u32)(bridge->io_head->base + bridge->io_head->length - 1)); - break; - case 0x0f: - dbg("I/O space unsupported\n"); - break; - default: - warn("Unknown I/O range type\n"); - } - - /* Memory resources (mandatory for P2P bridge) */ - pci_read_config_word(bridge->pci_dev, PCI_MEMORY_BASE, &tmp16); - base = (tmp16 & 0xfff0) << 16; - pci_read_config_word(bridge->pci_dev, PCI_MEMORY_LIMIT, &tmp16); - limit = ((tmp16 & 0xfff0) << 16) | 0xfffff; - bridge->mem_head = acpiphp_make_resource((u64)base, limit - base + 1); - if (!bridge->mem_head) { - err("out of memory\n"); - kfree(bridge); - return; - } - dbg("32bit Memory range: %08x-%08x\n", - (u32)bridge->mem_head->base, - (u32)(bridge->mem_head->base + bridge->mem_head->length-1)); - - /* Prefetchable Memory resources (optional) */ - pci_read_config_word(bridge->pci_dev, PCI_PREF_MEMORY_BASE, &tmp16); - base = tmp16; - pci_read_config_word(bridge->pci_dev, PCI_PREF_MEMORY_LIMIT, &tmp16); - limit = tmp16; - - switch (base & PCI_MEMORY_RANGE_TYPE_MASK) { - case PCI_PREF_RANGE_TYPE_32: - base = (base & 0xfff0) << 16; - limit = ((limit & 0xfff0) << 16) | 0xfffff; - bridge->p_mem_head = acpiphp_make_resource((u64)base, limit - base + 1); - if (!bridge->p_mem_head) { - err("out of memory\n"); - kfree(bridge); - return; - } - dbg("32bit Prefetchable memory range: %08x-%08x\n", - (u32)bridge->p_mem_head->base, - (u32)(bridge->p_mem_head->base + bridge->p_mem_head->length - 1)); - break; - case PCI_PREF_RANGE_TYPE_64: - pci_read_config_dword(bridge->pci_dev, PCI_PREF_BASE_UPPER32, &base32u); - pci_read_config_dword(bridge->pci_dev, PCI_PREF_LIMIT_UPPER32, &limit32u); - base64 = ((u64)base32u << 32) | ((base & 0xfff0) << 16); - limit64 = (((u64)limit32u << 32) | ((limit & 0xfff0) << 16)) + 0xfffff; - - bridge->p_mem_head = acpiphp_make_resource(base64, limit64 - base64 + 1); - if (!bridge->p_mem_head) { - err("out of memory\n"); - kfree(bridge); - return; - } - dbg("64bit Prefetchable memory range: %08x%08x-%08x%08x\n", - (u32)(bridge->p_mem_head->base >> 32), - (u32)(bridge->p_mem_head->base & 0xffffffff), - (u32)((bridge->p_mem_head->base + bridge->p_mem_head->length - 1) >> 32), - (u32)((bridge->p_mem_head->base + bridge->p_mem_head->length - 1) & 0xffffffff)); - break; - case 0x0f: - break; - default: - warn("Unknown prefetchale memory type\n"); - } - - init_bridge_misc(bridge); -} - - -/* callback routine to find P2P bridges */ -static acpi_status -find_p2p_bridge (acpi_handle handle, u32 lvl, void *context, void **rv) -{ - acpi_status status; - acpi_handle dummy_handle; - unsigned long *segbus = context; - unsigned long tmp; - int seg, bus, device, function; - struct pci_dev *dev; - - /* get PCI address */ - seg = (*segbus >> 8) & 0xff; - bus = *segbus & 0xff; - - status = acpi_get_handle(handle, "_ADR", &dummy_handle); - if (ACPI_FAILURE(status)) - return AE_OK; /* continue */ - - status = acpi_evaluate_integer(handle, "_ADR", NULL, &tmp); - if (ACPI_FAILURE(status)) { - dbg("%s: _ADR evaluation failure\n", __FUNCTION__); - return AE_OK; - } - - device = (tmp >> 16) & 0xffff; - function = tmp & 0xffff; - - dev = pci_find_slot(bus, PCI_DEVFN(device, function)); - - if (!dev) - return AE_OK; - - if (!dev->subordinate) - return AE_OK; - - /* check if this bridge has ejectable slots */ - if (detect_ejectable_slots(handle) > 0) { - dbg("found PCI-to-PCI bridge at PCI %s\n", dev->slot_name); - add_p2p_bridge(handle, seg, bus, device, function); - } - - return AE_OK; -} - - -/* find hot-pluggable slots, and then find P2P bridge */ -static int add_bridges(struct acpi_device *device) -{ - acpi_handle *handle = device->handle; - acpi_status status; - unsigned long tmp; - int seg, bus; - acpi_handle dummy_handle; - - /* if the bridge doesn't have _STA, we assume it is always there */ - status = acpi_get_handle(handle, "_STA", &dummy_handle); - if (ACPI_SUCCESS(status)) { - status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp); - if (ACPI_FAILURE(status)) { - dbg("%s: _STA evaluation failure\n", __FUNCTION__); - return 0; - } - if ((tmp & ACPI_STA_FUNCTIONING) == 0) - /* don't register this object */ - return 0; - } - - /* get PCI segment number */ - status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp); - - seg = ACPI_SUCCESS(status) ? tmp : 0; - - /* get PCI bus number */ - status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp); - - if (ACPI_SUCCESS(status)) { - bus = tmp; - } else { - warn("can't get bus number, assuming 0\n"); - bus = 0; - } - - /* check if this bridge has ejectable slots */ - if (detect_ejectable_slots(handle) > 0) { - dbg("found PCI host-bus bridge with hot-pluggable slots\n"); - add_host_bridge(handle, seg, bus); - return 0; - } - - tmp = seg << 8 | bus; - - /* search P2P bridges under this host bridge */ - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - find_p2p_bridge, &tmp, NULL); - - if (ACPI_FAILURE(status)) - warn("find_p2p_bridge faied (error code = 0x%x)\n",status); - - return 0; -} - - -static int power_on_slot (struct acpiphp_slot *slot) -{ - acpi_status status; - struct acpiphp_func *func; - struct list_head *l; - int retval = 0; - - /* is this already enabled? */ - if (slot->flags & SLOT_POWEREDON) - goto err_exit; - - list_for_each (l, &slot->funcs) { - func = list_entry(l, struct acpiphp_func, sibling); - - if (func->flags & FUNC_HAS_PS0) { - dbg("%s: executing _PS0 on %s\n", __FUNCTION__, - func->pci_dev->slot_name); - status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL); - if (ACPI_FAILURE(status)) { - warn("%s: _PS0 failed\n", __FUNCTION__); - retval = -1; - goto err_exit; - } - } - } - - /* TBD: evaluate _STA to check if the slot is enabled */ - - slot->flags |= SLOT_POWEREDON; - - err_exit: - return retval; -} - - -static int power_off_slot (struct acpiphp_slot *slot) -{ - acpi_status status; - struct acpiphp_func *func; - struct list_head *l; - struct acpi_object_list arg_list; - union acpi_object arg; - - int retval = 0; - - /* is this already enabled? */ - if ((slot->flags & SLOT_POWEREDON) == 0) - goto err_exit; - - list_for_each (l, &slot->funcs) { - func = list_entry(l, struct acpiphp_func, sibling); - - if (func->flags & FUNC_HAS_PS3) { - dbg("%s: executing _PS3 on %s\n", __FUNCTION__, - func->pci_dev->slot_name); - status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL); - if (ACPI_FAILURE(status)) { - warn("%s: _PS3 failed\n", __FUNCTION__); - retval = -1; - goto err_exit; - } - } - } - - list_for_each (l, &slot->funcs) { - func = list_entry(l, struct acpiphp_func, sibling); - - if (func->flags & FUNC_HAS_EJ0) { - dbg("%s: executing _EJ0 on %s\n", __FUNCTION__, - func->pci_dev->slot_name); - - /* _EJ0 method take one argument */ - arg_list.count = 1; - arg_list.pointer = &arg; - arg.type = ACPI_TYPE_INTEGER; - arg.integer.value = 1; - - status = acpi_evaluate_object(func->handle, "_EJ0", &arg_list, NULL); - if (ACPI_FAILURE(status)) { - warn("%s: _EJ0 failed\n", __FUNCTION__); - retval = -1; - goto err_exit; - } - } - } - - /* TBD: evaluate _STA to check if the slot is disabled */ - - slot->flags &= (~SLOT_POWEREDON); - - err_exit: - return retval; -} - - -/** - * enable_device - enable, configure a slot - * @slot: slot to be enabled - * - * This function should be called per *physical slot*, - * not per each slot object in ACPI namespace. - * - */ -static int enable_device (struct acpiphp_slot *slot) -{ - u8 bus; - struct pci_dev *dev; - struct pci_bus *child; - struct list_head *l; - struct acpiphp_func *func; - int retval = 0; - int num; - - if (slot->flags & SLOT_ENABLED) - goto err_exit; - - /* sanity check: dev should be NULL when hot-plugged in */ - dev = pci_find_slot(slot->bridge->bus, PCI_DEVFN(slot->device, 0)); - if (dev) { - /* This case shouldn't happen */ - err("pci_dev structure already exists.\n"); - retval = -1; - goto err_exit; - } - - /* allocate resources to device */ - retval = acpiphp_configure_slot(slot); - if (retval) - goto err_exit; - - /* returned `dev' is the *first function* only! */ - num = pci_scan_slot(slot->bridge->pci_bus, PCI_DEVFN(slot->device, 0)); - if (num) - pci_bus_add_devices(slot->bridge->pci_bus); - dev = pci_find_slot(slot->bridge->bus, PCI_DEVFN(slot->device, 0)); - - if (!dev) { - err("No new device found\n"); - retval = -1; - goto err_exit; - } - - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { - pci_read_config_byte(dev, PCI_SECONDARY_BUS, &bus); - child = (struct pci_bus*) pci_add_new_bus(dev->bus, dev, bus); - pci_do_scan_bus(child); - } - - /* associate pci_dev to our representation */ - list_for_each (l, &slot->funcs) { - func = list_entry(l, struct acpiphp_func, sibling); - - func->pci_dev = pci_find_slot(slot->bridge->bus, - PCI_DEVFN(slot->device, - func->function)); - if (!func->pci_dev) - continue; - - /* configure device */ - retval = acpiphp_configure_function(func); - if (retval) - goto err_exit; - } - - slot->flags |= SLOT_ENABLED; - - dbg("Available resources:\n"); - acpiphp_dump_resource(slot->bridge); - - err_exit: - return retval; -} - - -/** - * disable_device - disable a slot - */ -static int disable_device (struct acpiphp_slot *slot) -{ - int retval = 0; - struct acpiphp_func *func; - struct list_head *l; - - /* is this slot already disabled? */ - if (!(slot->flags & SLOT_ENABLED)) - goto err_exit; - - list_for_each (l, &slot->funcs) { - func = list_entry(l, struct acpiphp_func, sibling); - - if (func->pci_dev) { - if (acpiphp_unconfigure_function(func) == 0) { - func->pci_dev = NULL; - } else { - err("failed to unconfigure device\n"); - retval = -1; - goto err_exit; - } - } - } - - slot->flags &= (~SLOT_ENABLED); - - err_exit: - return retval; -} - - -/** - * get_slot_status - get ACPI slot status - * - * if a slot has _STA for each function and if any one of them - * returned non-zero status, return it - * - * if a slot doesn't have _STA and if any one of its functions' - * configuration space is configured, return 0x0f as a _STA - * - * otherwise return 0 - */ -static unsigned int get_slot_status (struct acpiphp_slot *slot) -{ - acpi_status status; - unsigned long sta = 0; - u32 dvid; - struct list_head *l; - struct acpiphp_func *func; - - list_for_each (l, &slot->funcs) { - func = list_entry(l, struct acpiphp_func, sibling); - - if (func->flags & FUNC_HAS_STA) { - status = acpi_evaluate_integer(func->handle, "_STA", NULL, &sta); - if (ACPI_SUCCESS(status) && sta) - break; - } else { - pci_bus_read_config_dword(slot->bridge->pci_bus, - PCI_DEVFN(slot->device, - func->function), - PCI_VENDOR_ID, &dvid); - if (dvid != 0xffffffff) { - sta = ACPI_STA_ALL; - break; - } - } - } - - return (unsigned int)sta; -} - - -/* - * ACPI event handlers - */ - -/** - * handle_hotplug_event_bridge - handle ACPI event on bridges - * - * @handle: Notify()'ed acpi_handle - * @type: Notify code - * @context: pointer to acpiphp_bridge structure - * - * handles ACPI event notification on {host,p2p} bridges - * - */ -static void handle_hotplug_event_bridge (acpi_handle handle, u32 type, void *context) -{ - struct acpiphp_bridge *bridge; - char objname[64]; - struct acpi_buffer buffer = { .length = sizeof(objname), - .pointer = objname }; - - bridge = (struct acpiphp_bridge *)context; - - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - - switch (type) { - case ACPI_NOTIFY_BUS_CHECK: - /* bus re-enumerate */ - dbg("%s: Bus check notify on %s\n", __FUNCTION__, objname); - acpiphp_check_bridge(bridge); - break; - - case ACPI_NOTIFY_DEVICE_CHECK: - /* device check */ - dbg("%s: Device check notify on %s\n", __FUNCTION__, objname); - acpiphp_check_bridge(bridge); - break; - - case ACPI_NOTIFY_DEVICE_WAKE: - /* wake event */ - dbg("%s: Device wake notify on %s\n", __FUNCTION__, objname); - break; - - case ACPI_NOTIFY_EJECT_REQUEST: - /* request device eject */ - dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname); - break; - - default: - warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); - break; - } -} - - -/** - * handle_hotplug_event_func - handle ACPI event on functions (i.e. slots) - * - * @handle: Notify()'ed acpi_handle - * @type: Notify code - * @context: pointer to acpiphp_func structure - * - * handles ACPI event notification on slots - * - */ -static void handle_hotplug_event_func (acpi_handle handle, u32 type, void *context) -{ - struct acpiphp_func *func; - char objname[64]; - struct acpi_buffer buffer = { .length = sizeof(objname), - .pointer = objname }; - - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - - func = (struct acpiphp_func *)context; - - switch (type) { - case ACPI_NOTIFY_BUS_CHECK: - /* bus re-enumerate */ - dbg("%s: Bus check notify on %s\n", __FUNCTION__, objname); - acpiphp_enable_slot(func->slot); - break; - - case ACPI_NOTIFY_DEVICE_CHECK: - /* device check : re-enumerate from parent bus */ - dbg("%s: Device check notify on %s\n", __FUNCTION__, objname); - acpiphp_check_bridge(func->slot->bridge); - break; - - case ACPI_NOTIFY_DEVICE_WAKE: - /* wake event */ - dbg("%s: Device wake notify on %s\n", __FUNCTION__, objname); - break; - - case ACPI_NOTIFY_EJECT_REQUEST: - /* request device eject */ - dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname); - acpiphp_disable_slot(func->slot); - break; - - default: - warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); - break; - } -} - -static struct acpi_driver acpi_pci_hp_driver = { - .name = "pci_hp", - .class = "", - .ids = ACPI_PCI_HOST_HID, - .ops = { - .add = add_bridges, - } -}; - -/** - * acpiphp_glue_init - initializes all PCI hotplug - ACPI glue data structures - * - */ -int acpiphp_glue_init (void) -{ - acpi_status status; - - if (list_empty(&pci_root_buses)) - return -1; - - status = acpi_bus_register_driver(&acpi_pci_hp_driver); - - if (ACPI_FAILURE(status)) { - err("%s: acpi_walk_namespace() failed\n", __FUNCTION__); - return -1; - } - - return 0; -} - - -/** - * acpiphp_glue_exit - terminates all PCI hotplug - ACPI glue data structures - * - * This function frees all data allocated in acpiphp_glue_init() - */ -void acpiphp_glue_exit (void) -{ - struct list_head *l1, *l2, *n1, *n2; - struct acpiphp_bridge *bridge; - struct acpiphp_slot *slot, *next; - struct acpiphp_func *func; - acpi_status status; - - list_for_each_safe (l1, n1, &bridge_list) { - bridge = (struct acpiphp_bridge *)l1; - slot = bridge->slots; - while (slot) { - next = slot->next; - list_for_each_safe (l2, n2, &slot->funcs) { - func = list_entry(l2, struct acpiphp_func, sibling); - acpiphp_free_resource(&func->io_head); - acpiphp_free_resource(&func->mem_head); - acpiphp_free_resource(&func->p_mem_head); - acpiphp_free_resource(&func->bus_head); - status = acpi_remove_notify_handler(func->handle, - ACPI_SYSTEM_NOTIFY, - handle_hotplug_event_func); - if (ACPI_FAILURE(status)) - err("failed to remove notify handler\n"); - kfree(func); - } - kfree(slot); - slot = next; - } - status = acpi_remove_notify_handler(bridge->handle, ACPI_SYSTEM_NOTIFY, - handle_hotplug_event_bridge); - if (ACPI_FAILURE(status)) - err("failed to remove notify handler\n"); - - acpiphp_free_resource(&bridge->io_head); - acpiphp_free_resource(&bridge->mem_head); - acpiphp_free_resource(&bridge->p_mem_head); - acpiphp_free_resource(&bridge->bus_head); - - kfree(bridge); - } -} - - -/** - * acpiphp_get_num_slots - count number of slots in a system - */ -int acpiphp_get_num_slots (void) -{ - struct list_head *node; - struct acpiphp_bridge *bridge; - int num_slots; - - num_slots = 0; - - list_for_each (node, &bridge_list) { - bridge = (struct acpiphp_bridge *)node; - dbg("Bus%d %dslot(s)\n", bridge->bus, bridge->nr_slots); - num_slots += bridge->nr_slots; - } - - dbg("Total %dslots\n", num_slots); - return num_slots; -} - - -/** - * acpiphp_for_each_slot - call function for each slot - * @fn: callback function - * @data: context to be passed to callback function - * - */ -int acpiphp_for_each_slot(acpiphp_callback fn, void *data) -{ - struct list_head *node; - struct acpiphp_bridge *bridge; - struct acpiphp_slot *slot; - int retval = 0; - - list_for_each (node, &bridge_list) { - bridge = (struct acpiphp_bridge *)node; - for (slot = bridge->slots; slot; slot = slot->next) { - retval = fn(slot, data); - if (!retval) - goto err_exit; - } - } - - err_exit: - return retval; -} - - -/* search matching slot from id */ -struct acpiphp_slot *get_slot_from_id (int id) -{ - struct list_head *node; - struct acpiphp_bridge *bridge; - struct acpiphp_slot *slot; - - list_for_each (node, &bridge_list) { - bridge = (struct acpiphp_bridge *)node; - for (slot = bridge->slots; slot; slot = slot->next) - if (slot->id == id) - return slot; - } - - /* should never happen! */ - err("%s: no object for id %d\n",__FUNCTION__, id); - return 0; -} - - -/** - * acpiphp_enable_slot - power on slot - */ -int acpiphp_enable_slot (struct acpiphp_slot *slot) -{ - int retval; - - down(&slot->crit_sect); - - /* wake up all functions */ - retval = power_on_slot(slot); - if (retval) - goto err_exit; - - if (get_slot_status(slot) == ACPI_STA_ALL) - /* configure all functions */ - retval = enable_device(slot); - - err_exit: - up(&slot->crit_sect); - return retval; -} - - -/** - * acpiphp_disable_slot - power off slot - */ -int acpiphp_disable_slot (struct acpiphp_slot *slot) -{ - int retval = 0; - - down(&slot->crit_sect); - - /* unconfigure all functions */ - retval = disable_device(slot); - if (retval) - goto err_exit; - - /* power off all functions */ - retval = power_off_slot(slot); - if (retval) - goto err_exit; - - acpiphp_resource_sort_and_combine(&slot->bridge->io_head); - acpiphp_resource_sort_and_combine(&slot->bridge->mem_head); - acpiphp_resource_sort_and_combine(&slot->bridge->p_mem_head); - acpiphp_resource_sort_and_combine(&slot->bridge->bus_head); - dbg("Available resources:\n"); - acpiphp_dump_resource(slot->bridge); - - err_exit: - up(&slot->crit_sect); - return retval; -} - - -/** - * acpiphp_check_bridge - re-enumerate devices - */ -int acpiphp_check_bridge (struct acpiphp_bridge *bridge) -{ - struct acpiphp_slot *slot; - unsigned int sta; - int retval = 0; - int enabled, disabled; - - enabled = disabled = 0; - - for (slot = bridge->slots; slot; slot = slot->next) { - sta = get_slot_status(slot); - if (slot->flags & SLOT_ENABLED) { - /* if enabled but not present, disable */ - if (sta != ACPI_STA_ALL) { - retval = acpiphp_disable_slot(slot); - if (retval) { - err("Error occurred in enabling\n"); - up(&slot->crit_sect); - goto err_exit; - } - enabled++; - } - } else { - /* if disabled but present, enable */ - if (sta == ACPI_STA_ALL) { - retval = acpiphp_enable_slot(slot); - if (retval) { - err("Error occurred in enabling\n"); - up(&slot->crit_sect); - goto err_exit; - } - disabled++; - } - } - } - - dbg("%s: %d enabled, %d disabled\n", __FUNCTION__, enabled, disabled); - - err_exit: - return retval; -} - - -/* - * slot enabled: 1 - * slot disabled: 0 - */ -u8 acpiphp_get_power_status (struct acpiphp_slot *slot) -{ - unsigned int sta; - - sta = get_slot_status(slot); - - return (sta & ACPI_STA_ENABLED) ? 1 : 0; -} - - -/* - * attention LED ON: 1 - * OFF: 0 - * - * TBD - * no direct attention led status information via ACPI - * - */ -u8 acpiphp_get_attention_status (struct acpiphp_slot *slot) -{ - return 0; -} - - -/* - * latch closed: 1 - * latch open: 0 - */ -u8 acpiphp_get_latch_status (struct acpiphp_slot *slot) -{ - unsigned int sta; - - sta = get_slot_status(slot); - - return (sta & ACPI_STA_SHOW_IN_UI) ? 1 : 0; -} - - -/* - * adapter presence : 1 - * absence : 0 - */ -u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot) -{ - unsigned int sta; - - sta = get_slot_status(slot); - - return (sta == 0) ? 0 : 1; -} diff -Nru a/drivers/hotplug/acpiphp_pci.c b/drivers/hotplug/acpiphp_pci.c --- a/drivers/hotplug/acpiphp_pci.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,510 +0,0 @@ -/* - * ACPI PCI HotPlug PCI configuration space management - * - * Copyright (c) 1995,2001 Compaq Computer Corporation - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001,2002 IBM Corp. - * Copyright (c) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com) - * Copyright (c) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) - * Copyright (c) 2002 NEC Corporation - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - * - */ - -#include -#include -#include -#include -#include -#include -#include "pci_hotplug.h" -#include "acpiphp.h" - -#define MY_NAME "acpiphp_pci" - - -/* allocate mem/pmem/io resource to a new function */ -static int init_config_space (struct acpiphp_func *func) -{ - u32 bar, len; - u32 address[] = { - PCI_BASE_ADDRESS_0, - PCI_BASE_ADDRESS_1, - PCI_BASE_ADDRESS_2, - PCI_BASE_ADDRESS_3, - PCI_BASE_ADDRESS_4, - PCI_BASE_ADDRESS_5, - 0 - }; - int count; - struct acpiphp_bridge *bridge; - struct pci_resource *res; - struct pci_bus *pbus; - int bus, device, function; - unsigned int devfn; - u16 tmp; - - bridge = func->slot->bridge; - pbus = bridge->pci_bus; - bus = bridge->bus; - device = func->slot->device; - function = func->function; - devfn = PCI_DEVFN(device, function); - - for (count = 0; address[count]; count++) { /* for 6 BARs */ - pci_bus_write_config_dword(pbus, devfn, - address[count], 0xFFFFFFFF); - pci_bus_read_config_dword(pbus, devfn, address[count], &bar); - - if (!bar) /* This BAR is not implemented */ - continue; - - dbg("Device %02x.%02x BAR %d wants %x\n", device, function, count, bar); - - if (bar & PCI_BASE_ADDRESS_SPACE_IO) { - /* This is IO */ - - len = bar & 0xFFFFFFFC; - len = ~len + 1; - - dbg("len in IO %x, BAR %d\n", len, count); - - spin_lock(&bridge->res_lock); - res = acpiphp_get_io_resource(&bridge->io_head, len); - spin_unlock(&bridge->res_lock); - - if (!res) { - err("cannot allocate requested io for %02x:%02x.%d len %x\n", - bus, device, function, len); - return -1; - } - pci_bus_write_config_dword(pbus, devfn, - address[count], - (u32)res->base); - res->next = func->io_head; - func->io_head = res; - - } else { - /* This is Memory */ - if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH) { - /* pfmem */ - - len = bar & 0xFFFFFFF0; - len = ~len + 1; - - dbg("len in PFMEM %x, BAR %d\n", len, count); - - spin_lock(&bridge->res_lock); - res = acpiphp_get_resource(&bridge->p_mem_head, len); - spin_unlock(&bridge->res_lock); - - if (!res) { - err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n", - bus, device, function, len); - return -1; - } - - pci_bus_write_config_dword(pbus, devfn, - address[count], - (u32)res->base); - - if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ - dbg("inside the pfmem 64 case, count %d\n", count); - count += 1; - pci_bus_write_config_dword(pbus, devfn, - address[count], - (u32)(res->base >> 32)); - } - - res->next = func->p_mem_head; - func->p_mem_head = res; - - } else { - /* regular memory */ - - len = bar & 0xFFFFFFF0; - len = ~len + 1; - - dbg("len in MEM %x, BAR %d\n", len, count); - - spin_lock(&bridge->res_lock); - res = acpiphp_get_resource(&bridge->mem_head, len); - spin_unlock(&bridge->res_lock); - - if (!res) { - err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n", - bus, device, function, len); - return -1; - } - - pci_bus_write_config_dword(pbus, devfn, - address[count], - (u32)res->base); - - if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) { - /* takes up another dword */ - dbg("inside mem 64 case, reg. mem, count %d\n", count); - count += 1; - pci_bus_write_config_dword(pbus, devfn, - address[count], - (u32)(res->base >> 32)); - } - - res->next = func->mem_head; - func->mem_head = res; - - } - } - } - - /* disable expansion rom */ - pci_bus_write_config_dword(pbus, devfn, PCI_ROM_ADDRESS, 0x00000000); - - /* set PCI parameters from _HPP */ - pci_bus_write_config_byte(pbus, devfn, PCI_CACHE_LINE_SIZE, - bridge->hpp.cache_line_size); - pci_bus_write_config_byte(pbus, devfn, PCI_LATENCY_TIMER, - bridge->hpp.latency_timer); - - pci_bus_read_config_word(pbus, devfn, PCI_COMMAND, &tmp); - if (bridge->hpp.enable_SERR) - tmp |= PCI_COMMAND_SERR; - if (bridge->hpp.enable_PERR) - tmp |= PCI_COMMAND_PARITY; - pci_bus_write_config_word(pbus, devfn, PCI_COMMAND, tmp); - - return 0; -} - -/* detect_used_resource - subtract resource under dev from bridge */ -static int detect_used_resource (struct acpiphp_bridge *bridge, struct pci_dev *dev) -{ - u32 bar, len; - u64 base; - u32 address[] = { - PCI_BASE_ADDRESS_0, - PCI_BASE_ADDRESS_1, - PCI_BASE_ADDRESS_2, - PCI_BASE_ADDRESS_3, - PCI_BASE_ADDRESS_4, - PCI_BASE_ADDRESS_5, - 0 - }; - int count; - struct pci_resource *res; - - dbg("Device %s\n", dev->slot_name); - - for (count = 0; address[count]; count++) { /* for 6 BARs */ - pci_read_config_dword(dev, address[count], &bar); - - if (!bar) /* This BAR is not implemented */ - continue; - - pci_write_config_dword(dev, address[count], 0xFFFFFFFF); - pci_read_config_dword(dev, address[count], &len); - - if (len & PCI_BASE_ADDRESS_SPACE_IO) { - /* This is IO */ - base = bar & 0xFFFFFFFC; - len &= 0xFFFFFFFC; - len = ~len + 1; - - dbg("BAR[%d] %08x - %08x (IO)\n", count, (u32)base, (u32)base + len - 1); - - spin_lock(&bridge->res_lock); - res = acpiphp_get_resource_with_base(&bridge->io_head, base, len); - spin_unlock(&bridge->res_lock); - if (res) - kfree(res); - } else { - /* This is Memory */ - base = bar & 0xFFFFFFF0; - if (len & PCI_BASE_ADDRESS_MEM_PREFETCH) { - /* pfmem */ - - len &= 0xFFFFFFF0; - len = ~len + 1; - - if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ - dbg("prefetch mem 64\n"); - count += 1; - } - dbg("BAR[%d] %08x - %08x (PMEM)\n", count, (u32)base, (u32)base + len - 1); - spin_lock(&bridge->res_lock); - res = acpiphp_get_resource_with_base(&bridge->p_mem_head, base, len); - spin_unlock(&bridge->res_lock); - if (res) - kfree(res); - } else { - /* regular memory */ - - len &= 0xFFFFFFF0; - len = ~len + 1; - - if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { - /* takes up another dword */ - dbg("mem 64\n"); - count += 1; - } - dbg("BAR[%d] %08x - %08x (MEM)\n", count, (u32)base, (u32)base + len - 1); - spin_lock(&bridge->res_lock); - res = acpiphp_get_resource_with_base(&bridge->mem_head, base, len); - spin_unlock(&bridge->res_lock); - if (res) - kfree(res); - } - } - - pci_write_config_dword(dev, address[count], bar); - } - - return 0; -} - - -/* detect_pci_resource_bus - subtract resource under pci_bus */ -static void detect_used_resource_bus(struct acpiphp_bridge *bridge, struct pci_bus *bus) -{ - struct list_head *l; - struct pci_dev *dev; - - list_for_each (l, &bus->devices) { - dev = pci_dev_b(l); - detect_used_resource(bridge, dev); - /* XXX recursive call */ - if (dev->subordinate) - detect_used_resource_bus(bridge, dev->subordinate); - } -} - - -/** - * acpiphp_detect_pci_resource - detect resources under bridge - * @bridge: detect all resources already used under this bridge - * - * collect all resources already allocated for all devices under a bridge. - */ -int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge) -{ - detect_used_resource_bus(bridge, bridge->pci_bus); - - return 0; -} - - -/** - * acpiphp_init_slot_resource - gather resource usage information of a slot - * @slot: ACPI slot object to be checked, should have valid pci_dev member - * - * TBD: PCI-to-PCI bridge case - * use pci_dev->resource[] - */ -int acpiphp_init_func_resource (struct acpiphp_func *func) -{ - u64 base; - u32 bar, len; - u32 address[] = { - PCI_BASE_ADDRESS_0, - PCI_BASE_ADDRESS_1, - PCI_BASE_ADDRESS_2, - PCI_BASE_ADDRESS_3, - PCI_BASE_ADDRESS_4, - PCI_BASE_ADDRESS_5, - 0 - }; - int count; - struct pci_resource *res; - struct pci_dev *dev; - - dev = func->pci_dev; - dbg("Hot-pluggable device %s\n", dev->slot_name); - - for (count = 0; address[count]; count++) { /* for 6 BARs */ - pci_read_config_dword(dev, address[count], &bar); - - if (!bar) /* This BAR is not implemented */ - continue; - - pci_write_config_dword(dev, address[count], 0xFFFFFFFF); - pci_read_config_dword(dev, address[count], &len); - - if (len & PCI_BASE_ADDRESS_SPACE_IO) { - /* This is IO */ - base = bar & 0xFFFFFFFC; - len &= 0xFFFFFFFC; - len = ~len + 1; - - dbg("BAR[%d] %08x - %08x (IO)\n", count, (u32)base, (u32)base + len - 1); - - res = acpiphp_make_resource(base, len); - if (!res) - goto no_memory; - - res->next = func->io_head; - func->io_head = res; - - } else { - /* This is Memory */ - base = bar & 0xFFFFFFF0; - if (len & PCI_BASE_ADDRESS_MEM_PREFETCH) { - /* pfmem */ - - len &= 0xFFFFFFF0; - len = ~len + 1; - - if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ - dbg("prefetch mem 64\n"); - count += 1; - } - dbg("BAR[%d] %08x - %08x (PMEM)\n", count, (u32)base, (u32)base + len - 1); - res = acpiphp_make_resource(base, len); - if (!res) - goto no_memory; - - res->next = func->p_mem_head; - func->p_mem_head = res; - - } else { - /* regular memory */ - - len &= 0xFFFFFFF0; - len = ~len + 1; - - if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { - /* takes up another dword */ - dbg("mem 64\n"); - count += 1; - } - dbg("BAR[%d] %08x - %08x (MEM)\n", count, (u32)base, (u32)base + len - 1); - res = acpiphp_make_resource(base, len); - if (!res) - goto no_memory; - - res->next = func->mem_head; - func->mem_head = res; - - } - } - - pci_write_config_dword(dev, address[count], bar); - } -#if 1 - acpiphp_dump_func_resource(func); -#endif - - return 0; - - no_memory: - err("out of memory\n"); - acpiphp_free_resource(&func->io_head); - acpiphp_free_resource(&func->mem_head); - acpiphp_free_resource(&func->p_mem_head); - - return -1; -} - - -/** - * acpiphp_configure_slot - allocate PCI resources - * @slot: slot to be configured - * - * initializes a PCI functions on a device inserted - * into the slot - * - */ -int acpiphp_configure_slot (struct acpiphp_slot *slot) -{ - struct acpiphp_func *func; - struct list_head *l; - u8 hdr; - u32 dvid; - int retval = 0; - int is_multi = 0; - - pci_bus_read_config_byte(slot->bridge->pci_bus, - PCI_DEVFN(slot->device, 0), - PCI_HEADER_TYPE, &hdr); - - if (hdr & 0x80) - is_multi = 1; - - list_for_each (l, &slot->funcs) { - func = list_entry(l, struct acpiphp_func, sibling); - if (is_multi || func->function == 0) { - pci_bus_read_config_dword(slot->bridge->pci_bus, - PCI_DEVFN(slot->device, - func->function), - PCI_VENDOR_ID, &dvid); - if (dvid != 0xffffffff) { - retval = init_config_space(func); - if (retval) - break; - } - } - } - - return retval; -} - -/** - * acpiphp_configure_function - configure PCI function - * @func: function to be configured - * - * initializes a PCI functions on a device inserted - * into the slot - * - */ -int acpiphp_configure_function (struct acpiphp_func *func) -{ - /* all handled by the pci core now */ - return 0; -} - -/** - * acpiphp_unconfigure_function - unconfigure PCI function - * @func: function to be unconfigured - * - */ -int acpiphp_unconfigure_function (struct acpiphp_func *func) -{ - struct acpiphp_bridge *bridge; - int retval = 0; - - /* if pci_dev is NULL, ignore it */ - if (!func->pci_dev) - goto err_exit; - - pci_remove_bus_device(func->pci_dev); - - /* free all resources */ - bridge = func->slot->bridge; - - spin_lock(&bridge->res_lock); - acpiphp_move_resource(&func->io_head, &bridge->io_head); - acpiphp_move_resource(&func->mem_head, &bridge->mem_head); - acpiphp_move_resource(&func->p_mem_head, &bridge->p_mem_head); - acpiphp_move_resource(&func->bus_head, &bridge->bus_head); - spin_unlock(&bridge->res_lock); - - err_exit: - return retval; -} diff -Nru a/drivers/hotplug/acpiphp_res.c b/drivers/hotplug/acpiphp_res.c --- a/drivers/hotplug/acpiphp_res.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,699 +0,0 @@ -/* - * ACPI PCI HotPlug Utility functions - * - * Copyright (c) 1995,2001 Compaq Computer Corporation - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001 IBM Corp. - * Copyright (c) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) - * Copyright (c) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com) - * Copyright (c) 2002 NEC Corporation - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to , - * - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "pci_hotplug.h" -#include "acpiphp.h" - -#define MY_NAME "acpiphp_res" - - -/* - * sort_by_size - sort nodes by their length, smallest first - */ -static int sort_by_size(struct pci_resource **head) -{ - struct pci_resource *current_res; - struct pci_resource *next_res; - int out_of_order = 1; - - if (!(*head)) - return 1; - - if (!((*head)->next)) - return 0; - - while (out_of_order) { - out_of_order = 0; - - /* Special case for swapping list head */ - if (((*head)->next) && - ((*head)->length > (*head)->next->length)) { - out_of_order++; - current_res = *head; - *head = (*head)->next; - current_res->next = (*head)->next; - (*head)->next = current_res; - } - - current_res = *head; - - while (current_res->next && current_res->next->next) { - if (current_res->next->length > current_res->next->next->length) { - out_of_order++; - next_res = current_res->next; - current_res->next = current_res->next->next; - current_res = current_res->next; - next_res->next = current_res->next; - current_res->next = next_res; - } else - current_res = current_res->next; - } - } /* End of out_of_order loop */ - - return 0; -} - - -/* - * sort_by_max_size - sort nodes by their length, largest first - */ -static int sort_by_max_size(struct pci_resource **head) -{ - struct pci_resource *current_res; - struct pci_resource *next_res; - int out_of_order = 1; - - if (!(*head)) - return 1; - - if (!((*head)->next)) - return 0; - - while (out_of_order) { - out_of_order = 0; - - /* Special case for swapping list head */ - if (((*head)->next) && - ((*head)->length < (*head)->next->length)) { - out_of_order++; - current_res = *head; - *head = (*head)->next; - current_res->next = (*head)->next; - (*head)->next = current_res; - } - - current_res = *head; - - while (current_res->next && current_res->next->next) { - if (current_res->next->length < current_res->next->next->length) { - out_of_order++; - next_res = current_res->next; - current_res->next = current_res->next->next; - current_res = current_res->next; - next_res->next = current_res->next; - current_res->next = next_res; - } else - current_res = current_res->next; - } - } /* End of out_of_order loop */ - - return 0; -} - -/** - * get_io_resource - get resource for I/O ports - * - * this function sorts the resource list by size and then - * returns the first node of "size" length that is not in the - * ISA aliasing window. If it finds a node larger than "size" - * it will split it up. - * - * size must be a power of two. - * - * difference from get_resource is handling of ISA aliasing space. - * - */ -struct pci_resource *acpiphp_get_io_resource (struct pci_resource **head, u32 size) -{ - struct pci_resource *prevnode; - struct pci_resource *node; - struct pci_resource *split_node; - u64 temp_qword; - - if (!(*head)) - return NULL; - - if (acpiphp_resource_sort_and_combine(head)) - return NULL; - - if (sort_by_size(head)) - return NULL; - - for (node = *head; node; node = node->next) { - if (node->length < size) - continue; - - if (node->base & (size - 1)) { - /* this one isn't base aligned properly - so we'll make a new entry and split it up */ - temp_qword = (node->base | (size-1)) + 1; - - /* Short circuit if adjusted size is too small */ - if ((node->length - (temp_qword - node->base)) < size) - continue; - - split_node = acpiphp_make_resource(node->base, temp_qword - node->base); - - if (!split_node) - return NULL; - - node->base = temp_qword; - node->length -= split_node->length; - - /* Put it in the list */ - split_node->next = node->next; - node->next = split_node; - } /* End of non-aligned base */ - - /* Don't need to check if too small since we already did */ - if (node->length > size) { - /* this one is longer than we need - so we'll make a new entry and split it up */ - split_node = acpiphp_make_resource(node->base + size, node->length - size); - - if (!split_node) - return NULL; - - node->length = size; - - /* Put it in the list */ - split_node->next = node->next; - node->next = split_node; - } /* End of too big on top end */ - - /* For IO make sure it's not in the ISA aliasing space */ - if (node->base & 0x300L) - 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 - get the largest resource - * - * Gets the largest node that is at least "size" big from the - * list pointed to by head. It aligns the node on top and bottom - * to "size" alignment before returning it. - */ -struct pci_resource *acpiphp_get_max_resource (struct pci_resource **head, u32 size) -{ - struct pci_resource *max; - struct pci_resource *temp; - struct pci_resource *split_node; - u64 temp_qword; - - if (!(*head)) - return NULL; - - if (acpiphp_resource_sort_and_combine(head)) - return NULL; - - if (sort_by_max_size(head)) - return NULL; - - for (max = *head;max; max = max->next) { - - /* If not big enough we could probably just bail, - instead we'll continue to the next. */ - if (max->length < size) - continue; - - if (max->base & (size - 1)) { - /* this one isn't base aligned properly - so we'll make a new entry and split it up */ - temp_qword = (max->base | (size-1)) + 1; - - /* Short circuit if adjusted size is too small */ - if ((max->length - (temp_qword - max->base)) < size) - continue; - - split_node = acpiphp_make_resource(max->base, temp_qword - max->base); - - if (!split_node) - return NULL; - - max->base = temp_qword; - max->length -= split_node->length; - - /* Put it next in the list */ - split_node->next = max->next; - max->next = split_node; - } - - if ((max->base + max->length) & (size - 1)) { - /* this one isn't end aligned properly at the top - so we'll make a new entry and split it up */ - temp_qword = ((max->base + max->length) & ~(size - 1)); - - split_node = acpiphp_make_resource(temp_qword, - max->length + max->base - temp_qword); - - if (!split_node) - return NULL; - - max->length -= split_node->length; - - /* Put it in the list */ - split_node->next = max->next; - max->next = split_node; - } - - /* Make sure it didn't shrink too much when we aligned it */ - if (max->length < size) - continue; - - /* Now take it out of the list */ - temp = (struct pci_resource*) *head; - if (temp == max) { - *head = max->next; - } else { - while (temp && temp->next != max) { - temp = temp->next; - } - - temp->next = max->next; - } - - max->next = NULL; - return max; - } - - /* If we get here, we couldn't find one */ - return NULL; -} - - -/** - * get_resource - get resource (mem, pfmem) - * - * this function sorts the resource list by size and then - * returns the first node of "size" length. If it finds a node - * larger than "size" it will split it up. - * - * size must be a power of two. - * - */ -struct pci_resource *acpiphp_get_resource (struct pci_resource **head, u32 size) -{ - struct pci_resource *prevnode; - struct pci_resource *node; - struct pci_resource *split_node; - u64 temp_qword; - - if (!(*head)) - return NULL; - - if (acpiphp_resource_sort_and_combine(head)) - return NULL; - - if (sort_by_size(head)) - return NULL; - - for (node = *head; node; node = node->next) { - dbg("%s: req_size =%x node=%p, base=%x, length=%x\n", - __FUNCTION__, size, node, (u32)node->base, node->length); - if (node->length < size) - continue; - - if (node->base & (size - 1)) { - dbg("%s: not aligned\n", __FUNCTION__); - /* this one isn't base aligned properly - so we'll make a new entry and split it up */ - temp_qword = (node->base | (size-1)) + 1; - - /* Short circuit if adjusted size is too small */ - if ((node->length - (temp_qword - node->base)) < size) - continue; - - split_node = acpiphp_make_resource(node->base, temp_qword - node->base); - - if (!split_node) - return NULL; - - node->base = temp_qword; - node->length -= split_node->length; - - /* Put it in the list */ - split_node->next = node->next; - node->next = split_node; - } /* End of non-aligned base */ - - /* Don't need to check if too small since we already did */ - if (node->length > size) { - dbg("%s: too big\n", __FUNCTION__); - /* this one is longer than we need - so we'll make a new entry and split it up */ - split_node = acpiphp_make_resource(node->base + size, node->length - size); - - if (!split_node) - return NULL; - - node->length = size; - - /* Put it in the list */ - split_node->next = node->next; - node->next = split_node; - } /* End of too big on top end */ - - dbg("%s: got one!!!\n", __FUNCTION__); - /* If we got here, then it is the right size - Now take it out of the list */ - if (*head == node) { - *head = node->next; - } else { - prevnode = *head; - while (prevnode->next != node) - prevnode = prevnode->next; - - prevnode->next = node->next; - } - node->next = NULL; - /* Stop looping */ - break; - } - return node; -} - -/** - * get_resource_with_base - get resource with specific base address - * - * this function - * returns the first node of "size" length located at specified base address. - * If it finds a node larger than "size" it will split it up. - * - * size must be a power of two. - * - */ -struct pci_resource *acpiphp_get_resource_with_base (struct pci_resource **head, u64 base, u32 size) -{ - struct pci_resource *prevnode; - struct pci_resource *node; - struct pci_resource *split_node; - u64 temp_qword; - - if (!(*head)) - return NULL; - - if (acpiphp_resource_sort_and_combine(head)) - return NULL; - - for (node = *head; node; node = node->next) { - dbg(": 1st req_base=%x req_size =%x node=%p, base=%x, length=%x\n", - (u32)base, size, node, (u32)node->base, node->length); - if (node->base > base) - continue; - - if ((node->base + node->length) < (base + size)) - continue; - - if (node->base < base) { - dbg(": split 1\n"); - /* this one isn't base aligned properly - so we'll make a new entry and split it up */ - temp_qword = base; - - /* Short circuit if adjusted size is too small */ - if ((node->length - (temp_qword - node->base)) < size) - continue; - - split_node = acpiphp_make_resource(node->base, temp_qword - node->base); - - if (!split_node) - return NULL; - - node->base = temp_qword; - node->length -= split_node->length; - - /* Put it in the list */ - split_node->next = node->next; - node->next = split_node; - } - - dbg(": 2nd req_base=%x req_size =%x node=%p, base=%x, length=%x\n", - (u32)base, size, node, (u32)node->base, node->length); - - /* Don't need to check if too small since we already did */ - if (node->length > size) { - dbg(": split 2\n"); - /* this one is longer than we need - so we'll make a new entry and split it up */ - split_node = acpiphp_make_resource(node->base + size, node->length - size); - - if (!split_node) - return NULL; - - node->length = size; - - /* Put it in the list */ - split_node->next = node->next; - node->next = split_node; - } /* End of too big on top end */ - - dbg(": got one!!!\n"); - /* If we got here, then it is the right size - Now take it out of the list */ - if (*head == node) { - *head = node->next; - } else { - prevnode = *head; - while (prevnode->next != node) - prevnode = prevnode->next; - - prevnode->next = node->next; - } - node->next = NULL; - /* Stop looping */ - break; - } - return node; -} - - -/** - * acpiphp_resource_sort_and_combine - * - * Sorts all of the nodes in the list in ascending order by - * their base addresses. Also does garbage collection by - * combining adjacent nodes. - * - * returns 0 if success - */ -int acpiphp_resource_sort_and_combine (struct pci_resource **head) -{ - struct pci_resource *node1; - struct pci_resource *node2; - int out_of_order = 1; - - if (!(*head)) - return 1; - - dbg("*head->next = %p\n",(*head)->next); - - if (!(*head)->next) - return 0; /* only one item on the list, already sorted! */ - - dbg("*head->base = 0x%x\n",(u32)(*head)->base); - dbg("*head->next->base = 0x%x\n", (u32)(*head)->next->base); - while (out_of_order) { - out_of_order = 0; - - /* Special case for swapping list head */ - if (((*head)->next) && - ((*head)->base > (*head)->next->base)) { - node1 = *head; - (*head) = (*head)->next; - node1->next = (*head)->next; - (*head)->next = node1; - out_of_order++; - } - - node1 = (*head); - - while (node1->next && node1->next->next) { - if (node1->next->base > node1->next->next->base) { - out_of_order++; - node2 = node1->next; - node1->next = node1->next->next; - node1 = node1->next; - node2->next = node1->next; - node1->next = node2; - } else - node1 = node1->next; - } - } /* End of out_of_order loop */ - - node1 = *head; - - while (node1 && node1->next) { - if ((node1->base + node1->length) == node1->next->base) { - /* Combine */ - dbg("8..\n"); - node1->length += node1->next->length; - node2 = node1->next; - node1->next = node1->next->next; - kfree(node2); - } else - node1 = node1->next; - } - - return 0; -} - - -/** - * acpiphp_make_resource - make resource structure - * @base: base address of a resource - * @length: length of a resource - */ -struct pci_resource *acpiphp_make_resource (u64 base, u32 length) -{ - struct pci_resource *res; - - res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); - if (res) { - memset(res, 0, sizeof(struct pci_resource)); - res->base = base; - res->length = length; - } - - return res; -} - - -/** - * acpiphp_move_resource - move linked resources from one to another - * @from: head of linked resource list - * @to: head of linked resource list - */ -void acpiphp_move_resource (struct pci_resource **from, struct pci_resource **to) -{ - struct pci_resource *tmp; - - while (*from) { - tmp = (*from)->next; - (*from)->next = *to; - *to = *from; - *from = tmp; - } - - /* *from = NULL is guaranteed */ -} - - -/** - * acpiphp_free_resource - free all linked resources - * @res: head of linked resource list - */ -void acpiphp_free_resource (struct pci_resource **res) -{ - struct pci_resource *tmp; - - while (*res) { - tmp = (*res)->next; - kfree(*res); - *res = tmp; - } - - /* *res = NULL is guaranteed */ -} - - -/* debug support functions; will go away sometime :) */ -static void dump_resource(struct pci_resource *head) -{ - struct pci_resource *p; - int cnt; - - p = head; - cnt = 0; - - while (p) { - dbg("[%02d] %08x - %08x\n", - cnt++, (u32)p->base, (u32)p->base + p->length - 1); - p = p->next; - } -} - -void acpiphp_dump_resource(struct acpiphp_bridge *bridge) -{ - dbg("I/O resource:\n"); - dump_resource(bridge->io_head); - dbg("MEM resource:\n"); - dump_resource(bridge->mem_head); - dbg("PMEM resource:\n"); - dump_resource(bridge->p_mem_head); - dbg("BUS resource:\n"); - dump_resource(bridge->bus_head); -} - -void acpiphp_dump_func_resource(struct acpiphp_func *func) -{ - dbg("I/O resource:\n"); - dump_resource(func->io_head); - dbg("MEM resource:\n"); - dump_resource(func->mem_head); - dbg("PMEM resource:\n"); - dump_resource(func->p_mem_head); - dbg("BUS resource:\n"); - dump_resource(func->bus_head); -} diff -Nru a/drivers/hotplug/cpci_hotplug.h b/drivers/hotplug/cpci_hotplug.h --- a/drivers/hotplug/cpci_hotplug.h Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,100 +0,0 @@ -/* - * CompactPCI Hot Plug Core Functions - * - * Copyright (c) 2002 SOMA Networks, Inc. - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001 IBM Corp. - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - */ - -#ifndef _CPCI_HOTPLUG_H -#define _CPCI_HOTPLUG_H - -#include -#include - -/* PICMG 2.12 R2.0 HS CSR bits: */ -#define HS_CSR_INS 0x0080 -#define HS_CSR_EXT 0x0040 -#define HS_CSR_PI 0x0030 -#define HS_CSR_LOO 0x0008 -#define HS_CSR_PIE 0x0004 -#define HS_CSR_EIM 0x0002 -#define HS_CSR_DHA 0x0001 - -#define SLOT_MAGIC 0x67267322 -struct slot { - u32 magic; - u8 number; - unsigned int devfn; - struct pci_bus *bus; - struct pci_dev *dev; - unsigned int extracting; - struct hotplug_slot *hotplug_slot; - struct list_head slot_list; -}; - -struct cpci_hp_controller_ops { - int (*query_enum) (void); - int (*enable_irq) (void); - int (*disable_irq) (void); - int (*check_irq) (void *dev_id); - int (*hardware_test) (struct slot* slot, u32 value); - u8 (*get_power) (struct slot* slot); - int (*set_power) (struct slot* slot, int value); -}; - -struct cpci_hp_controller { - unsigned int irq; - unsigned long irq_flags; - char *devname; - void *dev_id; - char *name; - struct cpci_hp_controller_ops *ops; -}; - -extern int cpci_hp_register_controller(struct cpci_hp_controller *controller); -extern int cpci_hp_unregister_controller(struct cpci_hp_controller *controller); -extern int cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last); -extern int cpci_hp_unregister_bus(struct pci_bus *bus); -extern struct slot *cpci_find_slot(struct pci_bus *bus, unsigned int devfn); -extern int cpci_hp_start(void); -extern int cpci_hp_stop(void); - -/* - * Internal function prototypes, these functions should not be used by - * board/chassis drivers. - */ -extern u8 cpci_get_attention_status(struct slot *slot); -extern u8 cpci_get_latch_status(struct slot *slot); -extern u8 cpci_get_adapter_status(struct slot *slot); -extern u16 cpci_get_hs_csr(struct slot * slot); -extern u16 cpci_set_hs_csr(struct slot * slot, u16 hs_csr); -extern int cpci_set_attention_status(struct slot *slot, int status); -extern int cpci_check_and_clear_ins(struct slot * slot); -extern int cpci_check_ext(struct slot * slot); -extern int cpci_clear_ext(struct slot * slot); -extern int cpci_led_on(struct slot * slot); -extern int cpci_led_off(struct slot * slot); -extern int cpci_configure_slot(struct slot *slot); -extern int cpci_unconfigure_slot(struct slot *slot); - -#endif /* _CPCI_HOTPLUG_H */ diff -Nru a/drivers/hotplug/cpci_hotplug_core.c b/drivers/hotplug/cpci_hotplug_core.c --- a/drivers/hotplug/cpci_hotplug_core.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,919 +0,0 @@ -/* - * CompactPCI Hot Plug Driver - * - * Copyright (c) 2002 SOMA Networks, Inc. - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001 IBM Corp. - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "pci_hotplug.h" -#include "cpci_hotplug.h" - -#define DRIVER_VERSION "0.2" -#define DRIVER_AUTHOR "Scott Murray " -#define DRIVER_DESC "CompactPCI Hot Plug Core" - -#if !defined(CONFIG_HOTPLUG_CPCI_MODULE) -#define MY_NAME "cpci_hotplug" -#else -#define MY_NAME THIS_MODULE->name -#endif - -#define dbg(format, arg...) \ - do { \ - if(cpci_debug) \ - printk (KERN_DEBUG "%s: " format "\n", \ - MY_NAME , ## arg); \ - } while(0) -#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) -#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) - -/* local variables */ -static spinlock_t list_lock; -static LIST_HEAD(slot_list); -static int slots; -int cpci_debug; -static struct cpci_hp_controller *controller; -static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ -static struct semaphore thread_exit; /* guard ensure thread has exited before calling it quits */ -static int thread_finished = 1; - -static int enable_slot(struct hotplug_slot *slot); -static int disable_slot(struct hotplug_slot *slot); -static int set_attention_status(struct hotplug_slot *slot, u8 value); -static int get_power_status(struct hotplug_slot *slot, u8 * value); -static int get_attention_status(struct hotplug_slot *slot, u8 * value); -static 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 cpci_hotplug_slot_ops = { - .owner = THIS_MODULE, - .enable_slot = enable_slot, - .disable_slot = disable_slot, - .set_attention_status = set_attention_status, - .hardware_test = NULL, - .get_power_status = get_power_status, - .get_attention_status = get_attention_status, - .get_latch_status = get_latch_status, - .get_adapter_status = get_adapter_status, -}; - -/* 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", function); - return NULL; - } - - slot = (struct slot *) hotplug_slot->private; - if(slot_paranoia_check(slot, function)) - return NULL; - return slot; -} - -static int -update_latch_status(struct hotplug_slot *hotplug_slot, u8 value) -{ - struct hotplug_slot_info info; - - if(!(hotplug_slot && hotplug_slot->info)) - return -EINVAL; - memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info)); - info.latch_status = value; - return pci_hp_change_slot_info(hotplug_slot, &info); -} - -static int -update_adapter_status(struct hotplug_slot *hotplug_slot, u8 value) -{ - struct hotplug_slot_info info; - - if(!(hotplug_slot && hotplug_slot->info)) - return -EINVAL; - memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info)); - info.adapter_status = value; - return pci_hp_change_slot_info(hotplug_slot, &info); -} - -static int -enable_slot(struct hotplug_slot *hotplug_slot) -{ - struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); - int retval = 0; - - if(slot == NULL) - return -ENODEV; - - dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name); - - if(controller->ops->set_power) { - retval = controller->ops->set_power(slot, 1); - } - - return retval; -} - -static int -disable_slot(struct hotplug_slot *hotplug_slot) -{ - struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); - int retval = 0; - - if(slot == NULL) - return -ENODEV; - - dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name); - - /* Unconfigure device */ - dbg("%s - unconfiguring slot %s", - __FUNCTION__, slot->hotplug_slot->name); - if((retval = cpci_unconfigure_slot(slot))) { - err("%s - could not unconfigure slot %s", - __FUNCTION__, slot->hotplug_slot->name); - return retval; - } - dbg("%s - finished unconfiguring slot %s", - __FUNCTION__, slot->hotplug_slot->name); - - /* Clear EXT (by setting it) */ - if(cpci_clear_ext(slot)) { - err("%s - could not clear EXT for slot %s", - __FUNCTION__, slot->hotplug_slot->name); - retval = -ENODEV; - } - cpci_led_on(slot); - - if(controller->ops->set_power) { - retval = controller->ops->set_power(slot, 0); - } - - if(update_adapter_status(slot->hotplug_slot, 0)) { - warn("failure to update adapter file"); - } - - slot->extracting = 0; - - return retval; -} - -static u8 -cpci_get_power_status(struct slot *slot) -{ - u8 power = 1; - - if(controller->ops->get_power) { - power = controller->ops->get_power(slot); - } - return power; -} - -static int -get_power_status(struct hotplug_slot *hotplug_slot, u8 * value) -{ - struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); - - if(slot == NULL) - return -ENODEV; - *value = cpci_get_power_status(slot); - return 0; -} - -static int -get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value) -{ - struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); - - if(slot == NULL) - return -ENODEV; - *value = cpci_get_attention_status(slot); - return 0; -} - -static int -set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) -{ - struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); - - if(slot == NULL) - return -ENODEV; - switch (status) { - case 0: - cpci_set_attention_status(slot, 0); - break; - - case 1: - default: - cpci_set_attention_status(slot, 1); - break; - } - - return 0; -} - -static int -get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value) -{ - if(hotplug_slot == NULL || hotplug_slot->info == NULL) - return -ENODEV; - *value = hotplug_slot->info->latch_status; - return 0; -} - -static int -get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value) -{ - if(hotplug_slot == NULL || hotplug_slot->info == NULL) - return -ENODEV; - *value = hotplug_slot->info->adapter_status; - return 0; -} - -#define SLOT_NAME_SIZE 6 -static void -make_slot_name(struct slot *slot) -{ - snprintf(slot->hotplug_slot->name, - SLOT_NAME_SIZE, "%02x:%02x", slot->bus->number, slot->number); -} - -int -cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) -{ - struct slot *slot; - struct hotplug_slot *hotplug_slot; - struct hotplug_slot_info *info; - char *name; - int status = 0; - int i; - - if(!(controller && bus)) { - return -ENODEV; - } - if(last < first) { - return -EINVAL; - } - - /* - * Create a structure for each slot, and register that slot - * with the pci_hotplug subsystem. - */ - for (i = first; i <= last; ++i) { - slot = kmalloc(sizeof (struct slot), GFP_KERNEL); - if(!slot) - return -ENOMEM; - memset(slot, 0, sizeof (struct slot)); - - hotplug_slot = - kmalloc(sizeof (struct hotplug_slot), GFP_KERNEL); - if(!hotplug_slot) { - kfree(slot); - return -ENOMEM; - } - memset(hotplug_slot, 0, sizeof (struct hotplug_slot)); - slot->hotplug_slot = hotplug_slot; - - info = kmalloc(sizeof (struct hotplug_slot_info), GFP_KERNEL); - if(!info) { - kfree(hotplug_slot); - kfree(slot); - return -ENOMEM; - } - memset(info, 0, sizeof (struct hotplug_slot_info)); - hotplug_slot->info = info; - - name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL); - if(!name) { - kfree(info); - kfree(hotplug_slot); - kfree(slot); - return -ENOMEM; - } - hotplug_slot->name = name; - - slot->magic = SLOT_MAGIC; - slot->bus = bus; - slot->number = i; - slot->devfn = PCI_DEVFN(i, 0); - - hotplug_slot->private = slot; - make_slot_name(slot); - hotplug_slot->ops = &cpci_hotplug_slot_ops; - - /* - * Initialize the slot info structure with some known - * good values. - */ - dbg("initializing slot %s", slot->hotplug_slot->name); - info->power_status = cpci_get_power_status(slot); - info->attention_status = cpci_get_attention_status(slot); - - dbg("registering slot %s", slot->hotplug_slot->name); - status = pci_hp_register(slot->hotplug_slot); - if(status) { - err("pci_hp_register failed with error %d", status); - kfree(info); - kfree(name); - kfree(hotplug_slot); - kfree(slot); - return status; - } - - /* Add slot to our internal list */ - spin_lock(&list_lock); - list_add(&slot->slot_list, &slot_list); - slots++; - spin_unlock(&list_lock); - } - return status; -} - -int -cpci_hp_unregister_bus(struct pci_bus *bus) -{ - struct slot *slot; - struct list_head *tmp; - int status; - - if(!bus) { - return -ENODEV; - } - - spin_lock(&list_lock); - if(!slots) { - spin_unlock(&list_lock); - return -1; - } - list_for_each(tmp, &slot_list) { - slot = list_entry(tmp, struct slot, slot_list); - if(slot->bus == bus) { - dbg("deregistering slot %s", slot->hotplug_slot->name); - status = pci_hp_deregister(slot->hotplug_slot); - if(status) { - err("pci_hp_deregister failed with error %d", - status); - return status; - } - - list_del(&slot->slot_list); - kfree(slot->hotplug_slot->info); - kfree(slot->hotplug_slot->name); - kfree(slot->hotplug_slot); - kfree(slot); - - slots--; - } - } - spin_unlock(&list_lock); - return 0; -} - -struct slot * -cpci_find_slot(struct pci_bus *bus, unsigned int devfn) -{ - struct slot *slot; - struct slot *found; - struct list_head *tmp; - - if(!bus) { - return NULL; - } - - spin_lock(&list_lock); - if(!slots) { - spin_unlock(&list_lock); - return NULL; - } - found = NULL; - list_for_each(tmp, &slot_list) { - slot = list_entry(tmp, struct slot, slot_list); - if(slot->bus == bus && slot->devfn == devfn) { - found = slot; - break; - } - } - spin_unlock(&list_lock); - return found; -} - -/* This is the interrupt mode interrupt handler */ -irqreturn_t -cpci_hp_intr(int irq, void *data, struct pt_regs *regs) -{ - dbg("entered cpci_hp_intr"); - - /* Check to see if it was our interrupt */ - if((controller->irq_flags & SA_SHIRQ) && - !controller->ops->check_irq(controller->dev_id)) { - dbg("exited cpci_hp_intr, not our interrupt"); - return IRQ_NONE; - } - - /* Disable ENUM interrupt */ - controller->ops->disable_irq(); - - /* Trigger processing by the event thread */ - dbg("Signal event_semaphore"); - up(&event_semaphore); - dbg("exited cpci_hp_intr"); - return IRQ_HANDLED; -} - -/* - * According to PICMG 2.12 R2.0, section 6.3.2, upon - * initialization, the system driver shall clear the - * INS bits of the cold-inserted devices. - */ -static int -init_slots(void) -{ - struct slot *slot; - struct list_head *tmp; - struct pci_dev* dev; - - dbg("%s - enter", __FUNCTION__); - spin_lock(&list_lock); - if(!slots) { - spin_unlock(&list_lock); - return -1; - } - list_for_each(tmp, &slot_list) { - slot = list_entry(tmp, struct slot, slot_list); - dbg("%s - looking at slot %s", - __FUNCTION__, slot->hotplug_slot->name); - if(cpci_check_and_clear_ins(slot)) { - dbg("%s - cleared INS for slot %s", - __FUNCTION__, slot->hotplug_slot->name); - dev = pci_find_slot(slot->bus->number, PCI_DEVFN(slot->number, 0)); - if(dev) { - if(update_adapter_status(slot->hotplug_slot, 1)) { - warn("failure to update adapter file"); - } - if(update_latch_status(slot->hotplug_slot, 1)) { - warn("failure to update latch file"); - } - slot->dev = dev; - } else { - err("%s - no driver attached to device in slot %s", - __FUNCTION__, slot->hotplug_slot->name); - } - } - } - spin_unlock(&list_lock); - dbg("%s - exit", __FUNCTION__); - return 0; -} - -static int -check_slots(void) -{ - struct slot *slot; - struct list_head *tmp; - int extracted; - int inserted; - - spin_lock(&list_lock); - if(!slots) { - spin_unlock(&list_lock); - err("no slots registered, shutting down"); - return -1; - } - extracted = inserted = 0; - list_for_each(tmp, &slot_list) { - slot = list_entry(tmp, struct slot, slot_list); - dbg("%s - looking at slot %s", - __FUNCTION__, slot->hotplug_slot->name); - if(cpci_check_and_clear_ins(slot)) { - u16 hs_csr; - - /* Some broken hardware (e.g. PLX 9054AB) asserts ENUM# twice... */ - if(slot->dev) { - warn("slot %s already inserted", slot->hotplug_slot->name); - inserted++; - continue; - } - - /* Process insertion */ - dbg("%s - slot %s inserted", - __FUNCTION__, slot->hotplug_slot->name); - - /* GSM, debug */ - hs_csr = cpci_get_hs_csr(slot); - dbg("%s - slot %s HS_CSR (1) = %04x", - __FUNCTION__, slot->hotplug_slot->name, hs_csr); - - /* Configure device */ - dbg("%s - configuring slot %s", - __FUNCTION__, slot->hotplug_slot->name); - if(cpci_configure_slot(slot)) { - err("%s - could not configure slot %s", - __FUNCTION__, slot->hotplug_slot->name); - continue; - } - dbg("%s - finished configuring slot %s", - __FUNCTION__, slot->hotplug_slot->name); - - /* GSM, debug */ - hs_csr = cpci_get_hs_csr(slot); - dbg("%s - slot %s HS_CSR (2) = %04x", - __FUNCTION__, slot->hotplug_slot->name, hs_csr); - - if(update_latch_status(slot->hotplug_slot, 1)) { - warn("failure to update latch file"); - } - - if(update_adapter_status(slot->hotplug_slot, 1)) { - warn("failure to update adapter file"); - } - - cpci_led_off(slot); - - /* GSM, debug */ - hs_csr = cpci_get_hs_csr(slot); - dbg("%s - slot %s HS_CSR (3) = %04x", - __FUNCTION__, slot->hotplug_slot->name, hs_csr); - - inserted++; - } else if(cpci_check_ext(slot)) { - u16 hs_csr; - - /* Process extraction request */ - dbg("%s - slot %s extracted", - __FUNCTION__, slot->hotplug_slot->name); - - /* GSM, debug */ - hs_csr = cpci_get_hs_csr(slot); - dbg("%s - slot %s HS_CSR = %04x", - __FUNCTION__, slot->hotplug_slot->name, hs_csr); - - if(!slot->extracting) { - if(update_latch_status(slot->hotplug_slot, 0)) { - warn("failure to update latch file"); - } - slot->extracting = 1; - } - extracted++; - } - } - spin_unlock(&list_lock); - if(inserted || extracted) { - return extracted; - } - else { - err("cannot find ENUM# source, shutting down"); - return -1; - } -} - -/* This is the interrupt mode worker thread body */ -static int -event_thread(void *data) -{ - int rc; - struct slot *slot; - struct list_head *tmp; - - lock_kernel(); - daemonize("cpci_hp_eventd"); - unlock_kernel(); - - dbg("%s - event thread started", __FUNCTION__); - while(1) { - dbg("event thread sleeping"); - down_interruptible(&event_semaphore); - dbg("event thread woken, thread_finished = %d", - thread_finished); - if(thread_finished || signal_pending(current)) - break; - while(controller->ops->query_enum()) { - rc = check_slots(); - if(rc > 0) { - /* Give userspace a chance to handle extraction */ - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 2); - } else if(rc < 0) { - dbg("%s - error checking slots", __FUNCTION__); - thread_finished = 1; - break; - } - } - /* Check for someone yanking out a board */ - list_for_each(tmp, &slot_list) { - slot = list_entry(tmp, struct slot, slot_list); - if(slot->extracting) { - /* - * Hmmm, we're likely hosed at this point, should we - * bother trying to tell the driver or not? - */ - err("card in slot %s was improperly removed", - slot->hotplug_slot->name); - if(update_adapter_status(slot->hotplug_slot, 0)) { - warn("failure to update adapter file"); - } - slot->extracting = 0; - } - } - - /* Re-enable ENUM# interrupt */ - dbg("%s - re-enabling irq", __FUNCTION__); - controller->ops->enable_irq(); - } - - dbg("%s - event thread signals exit", __FUNCTION__); - up(&thread_exit); - return 0; -} - -/* This is the polling mode worker thread body */ -static int -poll_thread(void *data) -{ - int rc; - struct slot *slot; - struct list_head *tmp; - - lock_kernel(); - daemonize("cpci_hp_polld"); - unlock_kernel(); - - while(1) { - if(thread_finished || signal_pending(current)) - break; - - while(controller->ops->query_enum()) { - rc = check_slots(); - if(rc > 0) { - /* Give userspace a chance to handle extraction */ - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 2); - } else if(rc < 0) { - dbg("%s - error checking slots", __FUNCTION__); - thread_finished = 1; - break; - } - } - /* Check for someone yanking out a board */ - list_for_each(tmp, &slot_list) { - slot = list_entry(tmp, struct slot, slot_list); - if(slot->extracting) { - /* - * Hmmm, we're likely hosed at this point, should we - * bother trying to tell the driver or not? - */ - err("card in slot %s was improperly removed", - slot->hotplug_slot->name); - if(update_adapter_status(slot->hotplug_slot, 0)) { - warn("failure to update adapter file"); - } - slot->extracting = 0; - } - } - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 10); - } - dbg("poll thread signals exit"); - up(&thread_exit); - return 0; -} - -static int -cpci_start_thread(void) -{ - int pid; - - /* initialize our semaphores */ - init_MUTEX_LOCKED(&event_semaphore); - init_MUTEX_LOCKED(&thread_exit); - thread_finished = 0; - - if(controller->irq) { - pid = kernel_thread(event_thread, 0, 0); - } else { - pid = kernel_thread(poll_thread, 0, 0); - } - if(pid < 0) { - err("Can't start up our thread"); - return -1; - } - dbg("Our thread pid = %d", pid); - return 0; -} - -static void -cpci_stop_thread(void) -{ - thread_finished = 1; - dbg("thread finish command given"); - if(controller->irq) { - up(&event_semaphore); - } - dbg("wait for thread to exit"); - down(&thread_exit); -} - -int -cpci_hp_register_controller(struct cpci_hp_controller *new_controller) -{ - int status = 0; - - if(!controller) { - controller = new_controller; - if(controller->irq) { - if(request_irq(controller->irq, - cpci_hp_intr, - controller->irq_flags, - MY_NAME, controller->dev_id)) { - err("Can't get irq %d for the hotplug cPCI controller", controller->irq); - status = -ENODEV; - } - dbg("%s - acquired controller irq %d", __FUNCTION__, - controller->irq); - } - } else { - err("cPCI hotplug controller already registered"); - status = -1; - } - return status; -} - -int -cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller) -{ - int status = 0; - - if(controller) { - if(!thread_finished) { - cpci_stop_thread(); - } - if(controller->irq) { - free_irq(controller->irq, controller->dev_id); - } - controller = NULL; - } else { - status = -ENODEV; - } - return status; -} - -int -cpci_hp_start(void) -{ - static int first = 1; - int status; - - dbg("%s - enter", __FUNCTION__); - if(!controller) { - return -ENODEV; - } - - spin_lock(&list_lock); - if(!slots) { - spin_unlock(&list_lock); - return -ENODEV; - } - spin_unlock(&list_lock); - - if(first) { - status = init_slots(); - if(status) { - return status; - } - first = 0; - } - - status = cpci_start_thread(); - if(status) { - return status; - } - dbg("%s - thread started", __FUNCTION__); - - if(controller->irq) { - /* Start enum interrupt processing */ - dbg("%s - enabling irq", __FUNCTION__); - controller->ops->enable_irq(); - } - dbg("%s - exit", __FUNCTION__); - return 0; -} - -int -cpci_hp_stop(void) -{ - if(!controller) { - return -ENODEV; - } - - if(controller->irq) { - /* Stop enum interrupt processing */ - dbg("%s - disabling irq", __FUNCTION__); - controller->ops->disable_irq(); - } - cpci_stop_thread(); - return 0; -} - -static void __exit -cleanup_slots(void) -{ - struct list_head *tmp; - struct slot *slot; - - /* - * Unregister all of our slots with the pci_hotplug subsystem, - * and free up all memory that we had allocated. - */ - spin_lock(&list_lock); - if(!slots) { - goto null_cleanup; - } - list_for_each(tmp, &slot_list) { - slot = list_entry(tmp, struct slot, slot_list); - list_del(&slot->slot_list); - pci_hp_deregister(slot->hotplug_slot); - kfree(slot->hotplug_slot->info); - kfree(slot->hotplug_slot->name); - kfree(slot->hotplug_slot); - kfree(slot); - } - null_cleanup: - spin_unlock(&list_lock); - return; -} - -int __init -cpci_hotplug_init(int debug) -{ - spin_lock_init(&list_lock); - cpci_debug = debug; - - info(DRIVER_DESC " version: " DRIVER_VERSION); - return 0; -} - -void __exit -cpci_hotplug_exit(void) -{ - /* - * Clean everything up. - */ - cleanup_slots(); -} - - -EXPORT_SYMBOL_GPL(cpci_hp_register_controller); -EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller); -EXPORT_SYMBOL_GPL(cpci_hp_register_bus); -EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus); -EXPORT_SYMBOL_GPL(cpci_find_slot); -EXPORT_SYMBOL_GPL(cpci_hp_start); -EXPORT_SYMBOL_GPL(cpci_hp_stop); diff -Nru a/drivers/hotplug/cpci_hotplug_pci.c b/drivers/hotplug/cpci_hotplug_pci.c --- a/drivers/hotplug/cpci_hotplug_pci.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,647 +0,0 @@ -/* - * CompactPCI Hot Plug Driver PCI functions - * - * Copyright (c) 2002 by SOMA Networks, Inc. - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - */ - -#include -#include -#include -#include -#include -#include "pci_hotplug.h" -#include "cpci_hotplug.h" - -#if !defined(CONFIG_HOTPLUG_CPCI_MODULE) -#define MY_NAME "cpci_hotplug" -#else -#define MY_NAME THIS_MODULE->name -#endif - -extern int cpci_debug; - -#define dbg(format, arg...) \ - do { \ - if(cpci_debug) \ - printk (KERN_DEBUG "%s: " format "\n", \ - MY_NAME , ## arg); \ - } while(0) -#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) -#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) - -#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) - - -u8 cpci_get_attention_status(struct slot* slot) -{ - int hs_cap; - u16 hs_csr; - - hs_cap = pci_bus_find_capability(slot->bus, - slot->devfn, - PCI_CAP_ID_CHSWP); - if(!hs_cap) { - return 0; - } - - if(pci_bus_read_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - &hs_csr)) { - return 0; - } - return hs_csr & 0x0008 ? 1 : 0; -} - -int cpci_set_attention_status(struct slot* slot, int status) -{ - int hs_cap; - u16 hs_csr; - - hs_cap = pci_bus_find_capability(slot->bus, - slot->devfn, - PCI_CAP_ID_CHSWP); - if(!hs_cap) { - return 0; - } - - if(pci_bus_read_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - &hs_csr)) { - return 0; - } - if(status) { - hs_csr |= HS_CSR_LOO; - } else { - hs_csr &= ~HS_CSR_LOO; - } - if(pci_bus_write_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - hs_csr)) { - return 0; - } - return 1; -} - -u16 cpci_get_hs_csr(struct slot* slot) -{ - int hs_cap; - u16 hs_csr; - - hs_cap = pci_bus_find_capability(slot->bus, - slot->devfn, - PCI_CAP_ID_CHSWP); - if(!hs_cap) { - return 0xFFFF; - } - - if(pci_bus_read_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - &hs_csr)) { - return 0xFFFF; - } - return hs_csr; -} - -u16 cpci_set_hs_csr(struct slot* slot, u16 hs_csr) -{ - int hs_cap; - u16 new_hs_csr; - - hs_cap = pci_bus_find_capability(slot->bus, - slot->devfn, - PCI_CAP_ID_CHSWP); - if(!hs_cap) { - return 0xFFFF; - } - - /* Write out the new value */ - if(pci_bus_write_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - hs_csr)) { - return 0xFFFF; - } - - /* Read back what we just wrote out */ - if(pci_bus_read_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - &new_hs_csr)) { - return 0xFFFF; - } - return new_hs_csr; -} - -int cpci_check_and_clear_ins(struct slot* slot) -{ - int hs_cap; - u16 hs_csr; - int ins = 0; - - hs_cap = pci_bus_find_capability(slot->bus, - slot->devfn, - PCI_CAP_ID_CHSWP); - if(!hs_cap) { - return 0; - } - if(pci_bus_read_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - &hs_csr)) { - return 0; - } - if(hs_csr & HS_CSR_INS) { - /* Clear INS (by setting it) */ - if(pci_bus_write_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - hs_csr)) { - ins = 0; - } - ins = 1; - } - return ins; -} - -int cpci_check_ext(struct slot* slot) -{ - int hs_cap; - u16 hs_csr; - int ext = 0; - - hs_cap = pci_bus_find_capability(slot->bus, - slot->devfn, - PCI_CAP_ID_CHSWP); - if(!hs_cap) { - return 0; - } - if(pci_bus_read_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - &hs_csr)) { - return 0; - } - if(hs_csr & HS_CSR_EXT) { - ext = 1; - } - return ext; -} - -int cpci_clear_ext(struct slot* slot) -{ - int hs_cap; - u16 hs_csr; - - hs_cap = pci_bus_find_capability(slot->bus, - slot->devfn, - PCI_CAP_ID_CHSWP); - if(!hs_cap) { - return -ENODEV; - } - if(pci_bus_read_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - &hs_csr)) { - return -ENODEV; - } - if(hs_csr & HS_CSR_EXT) { - /* Clear EXT (by setting it) */ - if(pci_bus_write_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - hs_csr)) { - return -ENODEV; - } - } - return 0; -} - -int cpci_led_on(struct slot* slot) -{ - int hs_cap; - u16 hs_csr; - - hs_cap = pci_bus_find_capability(slot->bus, - slot->devfn, - PCI_CAP_ID_CHSWP); - if(!hs_cap) { - return -ENODEV; - } - if(pci_bus_read_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - &hs_csr)) { - return -ENODEV; - } - if((hs_csr & HS_CSR_LOO) != HS_CSR_LOO) { - /* Set LOO */ - hs_csr |= HS_CSR_LOO; - if(pci_bus_write_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - hs_csr)) { - err("Could not set LOO for slot %s", - slot->hotplug_slot->name); - return -ENODEV; - } - } - return 0; -} - -int cpci_led_off(struct slot* slot) -{ - int hs_cap; - u16 hs_csr; - - hs_cap = pci_bus_find_capability(slot->bus, - slot->devfn, - PCI_CAP_ID_CHSWP); - if(!hs_cap) { - return -ENODEV; - } - if(pci_bus_read_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - &hs_csr)) { - return -ENODEV; - } - if(hs_csr & HS_CSR_LOO) { - /* Clear LOO */ - hs_csr &= ~HS_CSR_LOO; - if(pci_bus_write_config_word(slot->bus, - slot->devfn, - hs_cap + 2, - hs_csr)) { - err("Could not clear LOO for slot %s", - slot->hotplug_slot->name); - return -ENODEV; - } - } - return 0; -} - - -/* - * Device configuration functions - */ - -static int cpci_configure_dev(struct pci_bus *bus, struct pci_dev *dev) -{ - u8 irq_pin; - int r; - - dbg("%s - enter", __FUNCTION__); - - /* NOTE: device already setup from prior scan */ - - /* FIXME: How would we know if we need to enable the expansion ROM? */ - pci_write_config_word(dev, PCI_ROM_ADDRESS, 0x00L); - - /* Assign resources */ - dbg("assigning resources for %02x:%02x.%x", - dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - for (r = 0; r < 6; r++) { - struct resource *res = dev->resource + r; - if(res->flags) - pci_assign_resource(dev, r); - } - dbg("finished assigning resources for %02x:%02x.%x", - dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - - /* Does this function have an interrupt at all? */ - dbg("checking for function interrupt"); - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq_pin); - if(irq_pin) { - dbg("function uses interrupt pin %d", irq_pin); - } - - /* - * Need to explicitly set irq field to 0 so that it'll get assigned - * by the pcibios platform dependent code called by pci_enable_device. - */ - dev->irq = 0; - - dbg("enabling device"); - pci_enable_device(dev); /* XXX check return */ - dbg("now dev->irq = %d", dev->irq); - if(irq_pin && dev->irq) { - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); - } - - /* Can't use pci_insert_device at the moment, do it manually for now */ - pci_proc_attach_device(dev); - dbg("notifying drivers"); - //pci_announce_device_to_drivers(dev); - dbg("%s - exit", __FUNCTION__); - return 0; -} - -static int cpci_configure_bridge(struct pci_bus* bus, struct pci_dev* dev) -{ - int rc; - struct pci_bus* child; - struct resource* r; - u8 max, n; - u16 command; - - dbg("%s - enter", __FUNCTION__); - - /* Do basic bridge initialization */ - rc = pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x40); - if(rc) { - printk(KERN_ERR "%s - write of PCI_LATENCY_TIMER failed\n", __FUNCTION__); - } - rc = pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 0x40); - if(rc) { - printk(KERN_ERR "%s - write of PCI_SEC_LATENCY_TIMER failed\n", __FUNCTION__); - } - rc = pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES / 4); - if(rc) { - printk(KERN_ERR "%s - write of PCI_CACHE_LINE_SIZE failed\n", __FUNCTION__); - } - - /* - * Set parent bridge's subordinate field so that configuration space - * access will work in pci_scan_bridge and friends. - */ - max = pci_max_busnr(); - bus->subordinate = max + 1; - pci_write_config_byte(bus->self, PCI_SUBORDINATE_BUS, max + 1); - - /* Scan behind bridge */ - n = pci_scan_bridge(bus, dev, max, 2); - child = pci_find_bus(max + 1); - if (!child) - return -ENODEV; -#ifdef CONFIG_PROC_FS - pci_proc_attach_bus(child); -#endif - /* - * Update parent bridge's subordinate field if there were more bridges - * behind the bridge that was scanned. - */ - if(n > max) { - bus->subordinate = n; - pci_write_config_byte(bus->self, PCI_SUBORDINATE_BUS, n); - } - - /* - * Update the bridge resources of the bridge to accommodate devices - * behind it. - */ - pci_bus_size_bridges(child); - pci_bus_assign_resources(child); - - /* Enable resource mapping via command register */ - command = PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR; - r = child->resource[0]; - if(r && r->start) { - command |= PCI_COMMAND_IO; - } - r = child->resource[1]; - if(r && r->start) { - command |= PCI_COMMAND_MEMORY; - } - r = child->resource[2]; - if(r && r->start) { - command |= PCI_COMMAND_MEMORY; - } - rc = pci_write_config_word(dev, PCI_COMMAND, command); - if(rc) { - err("Error setting command register"); - return rc; - } - - /* Set bridge control register */ - command = PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_NO_ISA; - rc = pci_write_config_word(dev, PCI_BRIDGE_CONTROL, command); - if(rc) { - err("Error setting bridge control register"); - return rc; - } - dbg("%s - exit", __FUNCTION__); - return 0; -} - -static int configure_visit_pci_dev(struct pci_dev_wrapped *wrapped_dev, - struct pci_bus_wrapped *wrapped_bus) -{ - int rc; - struct pci_dev *dev = wrapped_dev->dev; - struct pci_bus *bus = wrapped_bus->bus; - struct slot* slot; - - dbg("%s - enter", __FUNCTION__); - - /* - * We need to fix up the hotplug representation with the Linux - * representation. - */ - slot = cpci_find_slot(dev->bus, dev->devfn); - if(slot) { - slot->dev = dev; - } - - /* If it's a bridge, scan behind it for devices */ - if(dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { - rc = cpci_configure_bridge(bus, dev); - if(rc) - return rc; - } - - /* Actually configure device */ - if(dev) { - rc = cpci_configure_dev(bus, dev); - if(rc) - return rc; - } - dbg("%s - exit", __FUNCTION__); - return 0; -} - -static int unconfigure_visit_pci_dev_phase2(struct pci_dev_wrapped *wrapped_dev, - struct pci_bus_wrapped *wrapped_bus) -{ - struct pci_dev *dev = wrapped_dev->dev; - struct slot* slot; - - dbg("%s - enter", __FUNCTION__); - if(!dev) - return -ENODEV; - - /* Remove the Linux representation */ - if(pci_remove_device_safe(dev) == 0) { - kfree(dev); - } else { - err("Could not remove device\n"); - return -1; - } - - /* - * Now remove the hotplug representation. - */ - slot = cpci_find_slot(dev->bus, dev->devfn); - if(slot) { - slot->dev = NULL; - } else { - dbg("No hotplug representation for %02x:%02x.%x", - dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - } - dbg("%s - exit", __FUNCTION__); - return 0; -} - -static int unconfigure_visit_pci_bus_phase2(struct pci_bus_wrapped *wrapped_bus, - struct pci_dev_wrapped *wrapped_dev) -{ - struct pci_bus *bus = wrapped_bus->bus; - struct pci_bus *parent = bus->self->bus; - - dbg("%s - enter", __FUNCTION__); - - /* The cleanup code for proc entries regarding buses should be in the kernel... */ - if(bus->procdir) - dbg("detach_pci_bus %s", bus->procdir->name); - pci_proc_detach_bus(bus); - - /* The cleanup code should live in the kernel... */ - bus->self->subordinate = NULL; - - /* unlink from parent bus */ - list_del(&bus->node); - - /* Now, remove */ - if(bus) - kfree(bus); - - /* Update parent's subordinate field */ - if(parent) { - u8 n = pci_bus_max_busnr(parent); - if(n < parent->subordinate) { - parent->subordinate = n; - pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, n); - } - } - dbg("%s - exit", __FUNCTION__); - return 0; -} - -static struct pci_visit configure_functions = { - .visit_pci_dev = configure_visit_pci_dev, -}; - -static struct pci_visit unconfigure_functions_phase2 = { - .post_visit_pci_bus = unconfigure_visit_pci_bus_phase2, - .post_visit_pci_dev = unconfigure_visit_pci_dev_phase2 -}; - - -int cpci_configure_slot(struct slot* slot) -{ - int rc = 0; - - dbg("%s - enter", __FUNCTION__); - - if(slot->dev == NULL) { - dbg("pci_dev null, finding %02x:%02x:%x", - slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn)); - slot->dev = pci_find_slot(slot->bus->number, slot->devfn); - } - - /* Still NULL? Well then scan for it! */ - if(slot->dev == NULL) { - dbg("pci_dev still null"); - - /* - * This will generate pci_dev structures for all functions, but - * we will only call this case when lookup fails. - */ - slot->dev = pci_scan_slot(slot->bus, slot->devfn); - if(slot->dev == NULL) { - err("Could not find PCI device for slot %02x", slot->number); - return 0; - } - } - dbg("slot->dev = %p", slot->dev); - if(slot->dev) { - struct pci_dev *dev; - struct pci_dev_wrapped wrapped_dev; - struct pci_bus_wrapped wrapped_bus; - int i; - - memset(&wrapped_dev, 0, sizeof (struct pci_dev_wrapped)); - memset(&wrapped_bus, 0, sizeof (struct pci_bus_wrapped)); - - for (i = 0; i < 8; i++) { - dev = pci_find_slot(slot->bus->number, - PCI_DEVFN(PCI_SLOT(slot->dev->devfn), i)); - if(!dev) - continue; - wrapped_dev.dev = dev; - wrapped_bus.bus = slot->dev->bus; - rc = pci_visit_dev(&configure_functions, &wrapped_dev, &wrapped_bus); - } - } - - dbg("%s - exit, rc = %d", __FUNCTION__, rc); - return rc; -} - -int cpci_unconfigure_slot(struct slot* slot) -{ - int rc = 0; - int i; - struct pci_dev_wrapped wrapped_dev; - struct pci_bus_wrapped wrapped_bus; - struct pci_dev *dev; - - dbg("%s - enter", __FUNCTION__); - - if(!slot->dev) { - err("No device for slot %02x\n", slot->number); - return -ENODEV; - } - - memset(&wrapped_dev, 0, sizeof (struct pci_dev_wrapped)); - memset(&wrapped_bus, 0, sizeof (struct pci_bus_wrapped)); - - for (i = 0; i < 8; i++) { - dev = pci_find_slot(slot->bus->number, - PCI_DEVFN(PCI_SLOT(slot->devfn), i)); - if(dev) { - wrapped_dev.dev = dev; - wrapped_bus.bus = dev->bus; - dbg("%s - unconfigure phase 2", __FUNCTION__); - rc = pci_visit_dev(&unconfigure_functions_phase2, - &wrapped_dev, &wrapped_bus); - if(rc) - break; - } - } - dbg("%s - exit, rc = %d", __FUNCTION__, rc); - return rc; -} diff -Nru a/drivers/hotplug/cpcihp_generic.c b/drivers/hotplug/cpcihp_generic.c --- a/drivers/hotplug/cpcihp_generic.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,290 +0,0 @@ -/* - * cpcihp_generic.c - * - * Generic port I/O CompactPCI driver - * - * Copyright 2002 SOMA Networks, Inc. - * Copyright 2001 Intel San Luis Obispo - * Copyright 2000,2001 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * This generic CompactPCI hotplug driver should allow using the PCI hotplug - * mechanism on any CompactPCI board that exposes the #ENUM signal as a bit - * in a system register that can be read through standard port I/O. - * - * Send feedback to - */ - -#include -#include -#include -#include -#include -#include "cpci_hotplug.h" - -#define DRIVER_VERSION "0.1" -#define DRIVER_AUTHOR "Scott Murray " -#define DRIVER_DESC "Generic port I/O CompactPCI Hot Plug Driver" - -#if !defined(CONFIG_HOTPLUG_CPCI_GENERIC_MODULE) -#define MY_NAME "cpcihp_generic" -#else -#define MY_NAME THIS_MODULE->name -#endif - -#define dbg(format, arg...) \ - do { \ - if(debug) \ - printk (KERN_DEBUG "%s: " format "\n", \ - MY_NAME , ## arg); \ - } while(0) -#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) -#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) - -/* local variables */ -static int debug; -static char* bridge; -static u8 bridge_busnr; -static u8 bridge_slot; -static struct pci_bus *bus; -static u8 first_slot; -static u8 last_slot; -static u16 port; -static unsigned int enum_bit; -static u8 enum_mask; - -static struct cpci_hp_controller_ops generic_hpc_ops; -static struct cpci_hp_controller generic_hpc; - -/* The following allows configuring the driver when it's compiled into the kernel */ -#ifndef MODULE -static int __init cpcihp_generic_setup(char* str) -{ - char* p; - unsigned long tmp; - - if(!str) - return -EINVAL; - bridge = str; - - p = strchr(str, ','); - str = p + 1; - if(!(p && *str && *p == ',')) - goto setup_error; - tmp = simple_strtoul(str, &p, 0); - if(p == str || tmp > 0xff) { - err("hotplug bus first slot number out of range"); - goto setup_error; - } - first_slot = (u8) tmp; - - str = p + 1; - if(!(*str && *p == ',')) - return -EINVAL; - tmp = simple_strtoul(str, &p, 0); - if(p == str || tmp > 0xff) { - err("hotplug bus last slot number out of range"); - goto setup_error; - } - last_slot = (u8) tmp; - - str = p + 1; - if(!(*str && *p == ',')) - goto setup_error; - tmp = simple_strtoul(str, &p, 0); - if(p == str || tmp > 0xffff) { - err("port number out of range"); - goto setup_error; - } - port = (u16) tmp; - - str = p + 1; - if(!(*str && *p == ',')) - goto setup_error; - tmp = simple_strtoul(str, &p, 0); - if(p == str) { - err("invalid #ENUM bit number"); - goto setup_error; - } - enum_bit = (u8) tmp; - - str = p + 1; - if(*str && *p == ',') { - tmp = simple_strtoul(str, &p, 0); - if(p != str) - debug = (int) tmp; - } - return 0; -setup_error: - bridge = NULL; - return -EINVAL; -} - -__setup("cpcihp_generic=", cpcihp_generic_setup); -#endif - -static int __init validate_parameters(void) -{ - char* str; - char* p; - unsigned long tmp; - - if(!bridge) { - info("not configured, disabling."); - return 1; - } - str = bridge; - if(!*str) - return -EINVAL; - - tmp = simple_strtoul(str, &p, 16); - if(p == str || tmp > 0xff) { - err("Invalid hotplug bus bridge device bus number"); - return -EINVAL; - } - bridge_busnr = (u8) tmp; - dbg("bridge_busnr = 0x%02x", bridge_busnr); - if(*p != ':') { - err("Invalid hotplug bus bridge device"); - return -EINVAL; - } - str = p + 1; - tmp = simple_strtoul(str, &p, 16); - if(p == str || tmp > 0x1f) { - err("Invalid hotplug bus bridge device slot number"); - return -EINVAL; - } - bridge_slot = (u8) tmp; - dbg("bridge_slot = 0x%02x", bridge_slot); - - dbg("first_slot = 0x%02x", first_slot); - dbg("last_slot = 0x%02x", last_slot); - if(!(first_slot && last_slot)) { - err("Need to specify first_slot and last_slot"); - return -EINVAL; - } - if(last_slot < first_slot) { - err("first_slot must be less than last_slot"); - return -EINVAL; - } - - dbg("port = 0x%04x", port); - dbg("enum_bit = 0x%02x", enum_bit); - if(enum_bit > 7) { - err("Invalid #ENUM bit"); - return -EINVAL; - } - enum_mask = 1 << enum_bit; - return 0; -} - -static int query_enum(void) -{ - u8 value; - - value = inb_p(port); - return ((value & enum_mask) == enum_mask); -} - -static int __init cpcihp_generic_init(void) -{ - int status; - struct resource* r; - struct pci_dev* dev; - - info(DRIVER_DESC " version: " DRIVER_VERSION); - status = validate_parameters(); - if(status != 0) - return status; - - r = request_region(port, 1, "#ENUM hotswap signal register"); - if(!r) - return -EBUSY; - - dev = pci_find_slot(bridge_busnr, PCI_DEVFN(bridge_slot, 0)); - if(!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { - err("Invalid bridge device %s", bridge); - return -EINVAL; - } - bus = dev->subordinate; - - memset(&generic_hpc, 0, sizeof (struct cpci_hp_controller)); - generic_hpc_ops.query_enum = query_enum; - generic_hpc.ops = &generic_hpc_ops; - - status = cpci_hp_register_controller(&generic_hpc); - if(status != 0) { - err("Could not register cPCI hotplug controller"); - return -ENODEV; - } - dbg("registered controller"); - - status = cpci_hp_register_bus(bus, first_slot, last_slot); - if(status != 0) { - err("Could not register cPCI hotplug bus"); - goto init_bus_register_error; - } - dbg("registered bus"); - - status = cpci_hp_start(); - if(status != 0) { - err("Could not started cPCI hotplug system"); - goto init_start_error; - } - dbg("started cpci hp system"); - return 0; -init_start_error: - cpci_hp_unregister_bus(bus); -init_bus_register_error: - cpci_hp_unregister_controller(&generic_hpc); - err("status = %d", status); - return status; - -} - -static void __exit cpcihp_generic_exit(void) -{ - cpci_hp_stop(); - cpci_hp_unregister_bus(bus); - cpci_hp_unregister_controller(&generic_hpc); - release_region(port, 1); -} - -module_init(cpcihp_generic_init); -module_exit(cpcihp_generic_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_PARM(debug, "i"); -MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); -MODULE_PARM(bridge, "s"); -MODULE_PARM_DESC(bridge, "Hotswap bus bridge device, : (bus and slot are in hexadecimal)"); -MODULE_PARM(first_slot, "b"); -MODULE_PARM_DESC(first_slot, "Hotswap bus first slot number"); -MODULE_PARM(last_slot, "b"); -MODULE_PARM_DESC(last_slot, "Hotswap bus last slot number"); -MODULE_PARM(port, "h"); -MODULE_PARM_DESC(port, "#ENUM signal I/O port"); -MODULE_PARM(enum_bit, "i"); -MODULE_PARM_DESC(enum_bit, "#ENUM signal bit (0-7)"); diff -Nru a/drivers/hotplug/cpcihp_zt5550.c b/drivers/hotplug/cpcihp_zt5550.c --- a/drivers/hotplug/cpcihp_zt5550.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,306 +0,0 @@ -/* - * cpcihp_zt5550.c - * - * Intel/Ziatech ZT5550 CompactPCI Host Controller driver - * - * Copyright 2002 SOMA Networks, Inc. - * Copyright 2001 Intel San Luis Obispo - * Copyright 2000,2001 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - */ - -#include -#include -#include -#include -#include -#include "cpci_hotplug.h" -#include "cpcihp_zt5550.h" - -#define DRIVER_VERSION "0.2" -#define DRIVER_AUTHOR "Scott Murray " -#define DRIVER_DESC "ZT5550 CompactPCI Hot Plug Driver" - -#if !defined(CONFIG_HOTPLUG_PCI_CPCI_ZT5550_MODULE) -#define MY_NAME "cpcihp_zt5550" -#else -#define MY_NAME THIS_MODULE->name -#endif - -#define dbg(format, arg...) \ - do { \ - if(debug) \ - printk (KERN_DEBUG "%s: " format "\n", \ - MY_NAME , ## arg); \ - } while(0) -#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) -#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) - -/* local variables */ -static int debug; -static int poll; -static struct cpci_hp_controller_ops zt5550_hpc_ops; -static struct cpci_hp_controller zt5550_hpc; - -/* Primary cPCI bus bridge device */ -static struct pci_dev *bus0_dev; -static struct pci_bus *bus0; - -/* Host controller device */ -static struct pci_dev *hc_dev; - -/* Host controller register addresses */ -static void *hc_registers; -static void *csr_hc_index; -static void *csr_hc_data; -static void *csr_int_status; -static void *csr_int_mask; - - -static int zt5550_hc_config(struct pci_dev *pdev) -{ - /* Since we know that no boards exist with two HC chips, treat it as an error */ - if(hc_dev) { - err("too many host controller devices?"); - return -EBUSY; - } - hc_dev = pdev; - dbg("hc_dev = %p", hc_dev); - dbg("pci resource start %lx", pci_resource_start(hc_dev, 1)); - dbg("pci resource len %lx", pci_resource_len(hc_dev, 1)); - - if(!request_mem_region(pci_resource_start(hc_dev, 1), - pci_resource_len(hc_dev, 1), MY_NAME)) { - err("cannot reserve MMIO region"); - return -ENOMEM; - } - - hc_registers = - ioremap(pci_resource_start(hc_dev, 1), pci_resource_len(hc_dev, 1)); - if(!hc_registers) { - err("cannot remap MMIO region %lx @ %lx", - pci_resource_len(hc_dev, 1), pci_resource_start(hc_dev, 1)); - release_mem_region(pci_resource_start(hc_dev, 1), - pci_resource_len(hc_dev, 1)); - return -ENODEV; - } - - csr_hc_index = hc_registers + CSR_HCINDEX; - csr_hc_data = hc_registers + CSR_HCDATA; - csr_int_status = hc_registers + CSR_INTSTAT; - csr_int_mask = hc_registers + CSR_INTMASK; - - /* - * Disable host control, fault and serial interrupts - */ - dbg("disabling host control, fault and serial interrupts"); - writeb((u8) HC_INT_MASK_REG, csr_hc_index); - writeb((u8) ALL_INDEXED_INTS_MASK, csr_hc_data); - dbg("disabled host control, fault and serial interrupts"); - - /* - * Disable timer0, timer1 and ENUM interrupts - */ - dbg("disabling timer0, timer1 and ENUM interrupts"); - writeb((u8) ALL_DIRECT_INTS_MASK, csr_int_mask); - dbg("disabled timer0, timer1 and ENUM interrupts"); - return 0; -} - -static int zt5550_hc_cleanup(void) -{ - if(!hc_dev) - return -ENODEV; - release_mem_region(pci_resource_start(hc_dev, 1), - pci_resource_len(hc_dev, 1)); - return 0; -} - -static int zt5550_hc_query_enum(void) -{ - u8 value; - - value = inb_p(ENUM_PORT); - return ((value & ENUM_MASK) == ENUM_MASK); -} - -static int zt5550_hc_check_irq(void *dev_id) -{ - int ret; - u8 reg; - - ret = 0; - if(dev_id == zt5550_hpc.dev_id) { - reg = readb(csr_int_status); - if(reg) - ret = 1; - } - return ret; -} - -static int zt5550_hc_enable_irq(void) -{ - u8 reg; - - if(hc_dev == NULL) { - return -ENODEV; - } - reg = readb(csr_int_mask); - reg = reg & ~ENUM_INT_MASK; - writeb(reg, csr_int_mask); - return 0; -} - -int zt5550_hc_disable_irq(void) -{ - u8 reg; - - if(hc_dev == NULL) { - return -ENODEV; - } - - reg = readb(csr_int_mask); - reg = reg | ENUM_INT_MASK; - writeb(reg, csr_int_mask); - return 0; -} - -static int __devinit zt5550_hc_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int status; - - status = zt5550_hc_config(pdev); - if(status != 0) { - return status; - } - dbg("returned from zt5550_hc_config"); - - memset(&zt5550_hpc, 0, sizeof (struct cpci_hp_controller)); - zt5550_hpc_ops.query_enum = zt5550_hc_query_enum; - zt5550_hpc.ops = &zt5550_hpc_ops; - if(!poll) { - zt5550_hpc.irq = hc_dev->irq; - zt5550_hpc.irq_flags = SA_SHIRQ; - zt5550_hpc.dev_id = hc_dev; - - zt5550_hpc_ops.enable_irq = zt5550_hc_enable_irq; - zt5550_hpc_ops.disable_irq = zt5550_hc_disable_irq; - zt5550_hpc_ops.check_irq = zt5550_hc_check_irq; - } else { - info("using ENUM# polling mode"); - } - - status = cpci_hp_register_controller(&zt5550_hpc); - if(status != 0) { - err("could not register cPCI hotplug controller"); - goto init_hc_error; - } - dbg("registered controller"); - - /* Look for first device matching cPCI bus's bridge vendor and device IDs */ - if(!(bus0_dev = pci_find_device(PCI_VENDOR_ID_DEC, - PCI_DEVICE_ID_DEC_21154, NULL))) { - status = -ENODEV; - goto init_register_error; - } - bus0 = bus0_dev->subordinate; - - status = cpci_hp_register_bus(bus0, 0x0a, 0x0f); - if(status != 0) { - err("could not register cPCI hotplug bus"); - goto init_register_error; - } - dbg("registered bus"); - - status = cpci_hp_start(); - if(status != 0) { - err("could not started cPCI hotplug system"); - cpci_hp_unregister_bus(bus0); - goto init_register_error; - } - dbg("started cpci hp system"); - - return 0; -init_register_error: - cpci_hp_unregister_controller(&zt5550_hpc); -init_hc_error: - err("status = %d", status); - zt5550_hc_cleanup(); - return status; - -} - -static void __devexit zt5550_hc_remove_one(struct pci_dev *pdev) -{ - cpci_hp_stop(); - cpci_hp_unregister_bus(bus0); - cpci_hp_unregister_controller(&zt5550_hpc); - zt5550_hc_cleanup(); -} - - -static struct pci_device_id zt5550_hc_pci_tbl[] __devinitdata = { - { PCI_VENDOR_ID_ZIATECH, PCI_DEVICE_ID_ZIATECH_5550_HC, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, zt5550_hc_pci_tbl); - -static struct pci_driver zt5550_hc_driver = { - .name = "zt5550_hc", - .id_table = zt5550_hc_pci_tbl, - .probe = zt5550_hc_init_one, - .remove = __devexit_p(zt5550_hc_remove_one), -}; - -static int __init zt5550_init(void) -{ - struct resource* r; - - info(DRIVER_DESC " version: " DRIVER_VERSION); - r = request_region(ENUM_PORT, 1, "#ENUM hotswap signal register"); - if(!r) - return -EBUSY; - - return pci_module_init(&zt5550_hc_driver); -} - -static void __exit -zt5550_exit(void) -{ - pci_unregister_driver(&zt5550_hc_driver); - release_region(ENUM_PORT, 1); -} - -module_init(zt5550_init); -module_exit(zt5550_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_PARM(debug, "i"); -MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); -MODULE_PARM(poll, "i"); -MODULE_PARM_DESC(poll, "#ENUM polling mode enabled or not"); diff -Nru a/drivers/hotplug/cpcihp_zt5550.h b/drivers/hotplug/cpcihp_zt5550.h --- a/drivers/hotplug/cpcihp_zt5550.h Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,79 +0,0 @@ -/* - * cpcihp_zt5550.h - * - * Intel/Ziatech ZT5550 CompactPCI Host Controller driver definitions - * - * Copyright 2002 SOMA Networks, Inc. - * Copyright 2001 Intel San Luis Obispo - * Copyright 2000,2001 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - */ - -#ifndef _CPCIHP_ZT5550_H -#define _CPCIHP_ZT5550_H - -/* Direct registers */ -#define CSR_HCINDEX 0x00 -#define CSR_HCDATA 0x04 -#define CSR_INTSTAT 0x08 -#define CSR_INTMASK 0x09 -#define CSR_CNT0CMD 0x0C -#define CSR_CNT1CMD 0x0E -#define CSR_CNT0 0x10 -#define CSR_CNT1 0x14 - -/* Masks for interrupt bits in CSR_INTMASK direct register */ -#define CNT0_INT_MASK 0x01 -#define CNT1_INT_MASK 0x02 -#define ENUM_INT_MASK 0x04 -#define ALL_DIRECT_INTS_MASK 0x07 - -/* Indexed registers (through CSR_INDEX, CSR_DATA) */ -#define HC_INT_MASK_REG 0x04 -#define HC_STATUS_REG 0x08 -#define HC_CMD_REG 0x0C -#define ARB_CONFIG_GNT_REG 0x10 -#define ARB_CONFIG_CFG_REG 0x12 -#define ARB_CONFIG_REG 0x10 -#define ISOL_CONFIG_REG 0x18 -#define FAULT_STATUS_REG 0x20 -#define FAULT_CONFIG_REG 0x24 -#define WD_CONFIG_REG 0x2C -#define HC_DIAG_REG 0x30 -#define SERIAL_COMM_REG 0x34 -#define SERIAL_OUT_REG 0x38 -#define SERIAL_IN_REG 0x3C - -/* Masks for interrupt bits in HC_INT_MASK_REG indexed register */ -#define SERIAL_INT_MASK 0x01 -#define FAULT_INT_MASK 0x02 -#define HCF_INT_MASK 0x04 -#define ALL_INDEXED_INTS_MASK 0x07 - -/* Digital I/O port storing ENUM# */ -#define ENUM_PORT 0xE1 -/* Mask to get to the ENUM# bit on the bus */ -#define ENUM_MASK 0x40 - -#endif /* _CPCIHP_ZT5550_H */ diff -Nru a/drivers/hotplug/cpqphp.h b/drivers/hotplug/cpqphp.h --- a/drivers/hotplug/cpqphp.h Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,912 +0,0 @@ -/* - * 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 - * - */ -#ifndef _CPQPHP_H -#define _CPQPHP_H - -#include "pci_hotplug.h" -#include -#include /* for read? and write? functions */ -#include /* for delays */ - -#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 */ - u8 reserved12; /* 0x2e */ - u8 reserved13; /* 0x2f */ - u8 next_curr_freq; /* 0x30 */ - u8 reset_freq_mode; /* 0x31 */ -} __attribute__ ((packed)); - -/* offsets to the controller registers based on the above structure layout */ -enum ctrl_offsets { - SLOT_RST = offsetof(struct ctrl_reg, slot_RST), - SLOT_ENABLE = offsetof(struct ctrl_reg, slot_enable), - MISC = offsetof(struct ctrl_reg, misc), - LED_CONTROL = offsetof(struct ctrl_reg, led_control), - INT_INPUT_CLEAR = offsetof(struct ctrl_reg, int_input_clear), - INT_MASK = offsetof(struct ctrl_reg, int_mask), - CTRL_RESERVED0 = offsetof(struct ctrl_reg, reserved0), - CTRL_RESERVED1 = offsetof(struct ctrl_reg, reserved1), - CTRL_RESERVED2 = offsetof(struct ctrl_reg, reserved1), - GEN_OUTPUT_AB = offsetof(struct ctrl_reg, gen_output_AB), - NON_INT_INPUT = offsetof(struct ctrl_reg, non_int_input), - CTRL_RESERVED3 = offsetof(struct ctrl_reg, reserved3), - CTRL_RESERVED4 = offsetof(struct ctrl_reg, reserved4), - CTRL_RESERVED5 = offsetof(struct ctrl_reg, reserved5), - CTRL_RESERVED6 = offsetof(struct ctrl_reg, reserved6), - CTRL_RESERVED7 = offsetof(struct ctrl_reg, reserved7), - CTRL_RESERVED8 = offsetof(struct ctrl_reg, reserved8), - SLOT_MASK = offsetof(struct ctrl_reg, slot_mask), - CTRL_RESERVED9 = offsetof(struct ctrl_reg, reserved9), - CTRL_RESERVED10 = offsetof(struct ctrl_reg, reserved10), - CTRL_RESERVED11 = offsetof(struct ctrl_reg, reserved11), - SLOT_SERR = offsetof(struct ctrl_reg, slot_SERR), - SLOT_POWER = offsetof(struct ctrl_reg, slot_power), - NEXT_CURR_FREQ = offsetof(struct ctrl_reg, next_curr_freq), - RESET_FREQ_MODE = offsetof(struct ctrl_reg, reset_freq_mode), -}; - -struct hrt { - char sig0; - char sig1; - char sig2; - char sig3; - u16 unused_IRQ; - u16 PCIIRQ; - u8 number_of_entries; - u8 revision; - u16 reserved1; - u32 reserved2; -} __attribute__ ((packed)); - -/* offsets to the hotplug resource table registers based on the above structure layout */ -enum hrt_offsets { - SIG0 = offsetof(struct hrt, sig0), - SIG1 = offsetof(struct hrt, sig1), - SIG2 = offsetof(struct hrt, sig2), - SIG3 = offsetof(struct hrt, sig3), - UNUSED_IRQ = offsetof(struct hrt, unused_IRQ), - PCIIRQ = offsetof(struct hrt, PCIIRQ), - NUMBER_OF_ENTRIES = offsetof(struct hrt, number_of_entries), - REVISION = offsetof(struct hrt, revision), - HRT_RESERVED1 = offsetof(struct hrt, reserved1), - HRT_RESERVED2 = offsetof(struct hrt, reserved2), -}; - -struct slot_rt { - u8 dev_func; - u8 primary_bus; - u8 secondary_bus; - u8 max_bus; - u16 io_base; - u16 io_length; - u16 mem_base; - u16 mem_length; - u16 pre_mem_base; - u16 pre_mem_length; -} __attribute__ ((packed)); - -/* offsets to the hotplug slot resource table registers based on the above structure layout */ -enum slot_rt_offsets { - DEV_FUNC = offsetof(struct slot_rt, dev_func), - PRIMARY_BUS = offsetof(struct slot_rt, primary_bus), - SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus), - MAX_BUS = offsetof(struct slot_rt, max_bus), - IO_BASE = offsetof(struct slot_rt, io_base), - IO_LENGTH = offsetof(struct slot_rt, io_length), - MEM_BASE = offsetof(struct slot_rt, mem_base), - MEM_LENGTH = offsetof(struct slot_rt, mem_length), - PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base), - PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length), -}; - -struct pci_func { - struct pci_func *next; - u8 bus; - u8 device; - u8 function; - u8 is_a_board; - u16 status; - u8 configured; - u8 switch_save; - u8 presence_save; - u32 base_length[0x06]; - u8 base_type[0x06]; - u16 reserved2; - u32 config_space[0x20]; - struct pci_resource *mem_head; - struct pci_resource *p_mem_head; - struct pci_resource *io_head; - struct pci_resource *bus_head; - struct timer_list *p_task_event; - struct pci_dev* pci_dev; -}; - -#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_bus *pci_bus; - struct event_info event_queue[10]; - struct slot *slot; - u8 next_event; - u8 interrupt; - u8 cfgspc_irq; - u8 bus; /* bus number for the pci hotplug controller */ - u8 rev; - u8 slot_device_offset; - u8 first_slot; - u8 add_support; - u8 push_flag; - enum pci_bus_speed speed; - enum pci_bus_speed speed_capability; - u8 push_button; /* 0 = no pushbutton, 1 = pushbutton present */ - u8 slot_switch_type; /* 0 = no switch, 1 = switch present */ - u8 defeature_PHP; /* 0 = PHP not supported, 1 = PHP supported */ - u8 alternate_base_address; /* 0 = not supported, 1 = supported */ - u8 pci_config_space; /* Index/data access to working registers 0 = not supported, 1 = supported */ - u8 pcix_speed_capability; /* PCI-X */ - u8 pcix_support; /* PCI-X */ - u16 vendor_id; - struct work_struct int_task_event; - wait_queue_head_t queue; /* sleep & wake process */ -}; - -struct irq_mapping { - u8 barber_pole; - u8 valid_INT; - u8 interrupt[4]; -}; - -struct resource_lists { - struct pci_resource *mem_head; - struct pci_resource *p_mem_head; - struct pci_resource *io_head; - struct pci_resource *bus_head; - struct irq_mapping *irqs; -}; - -#define ROM_PHY_ADDR 0x0F0000 -#define ROM_PHY_LEN 0x00ffff - -#define PCI_HPC_ID 0xA0F7 -#define PCI_SUB_HPC_ID 0xA2F7 -#define PCI_SUB_HPC_ID2 0xA2F8 -#define PCI_SUB_HPC_ID3 0xA2F9 -#define PCI_SUB_HPC_ID_INTC 0xA2FA -#define PCI_SUB_HPC_ID4 0xA2FD - -#define INT_BUTTON_IGNORE 0 -#define INT_PRESENCE_ON 1 -#define INT_PRESENCE_OFF 2 -#define INT_SWITCH_CLOSE 3 -#define INT_SWITCH_OPEN 4 -#define INT_POWER_FAULT 5 -#define INT_POWER_FAULT_CLEAR 6 -#define INT_BUTTON_PRESS 7 -#define INT_BUTTON_RELEASE 8 -#define INT_BUTTON_CANCEL 9 - -#define STATIC_STATE 0 -#define BLINKINGON_STATE 1 -#define BLINKINGOFF_STATE 2 -#define POWERON_STATE 3 -#define POWEROFF_STATE 4 - -#define PCISLOT_INTERLOCK_CLOSED 0x00000001 -#define PCISLOT_ADAPTER_PRESENT 0x00000002 -#define PCISLOT_POWERED 0x00000004 -#define PCISLOT_66_MHZ_OPERATION 0x00000008 -#define PCISLOT_64_BIT_OPERATION 0x00000010 -#define PCISLOT_REPLACE_SUPPORTED 0x00000020 -#define PCISLOT_ADD_SUPPORTED 0x00000040 -#define PCISLOT_INTERLOCK_SUPPORTED 0x00000080 -#define PCISLOT_66_MHZ_SUPPORTED 0x00000100 -#define PCISLOT_64_BIT_SUPPORTED 0x00000200 - - - -#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400 - - -#define INTERLOCK_OPEN 0x00000002 -#define ADD_NOT_SUPPORTED 0x00000003 -#define CARD_FUNCTIONING 0x00000005 -#define ADAPTER_NOT_SAME 0x00000006 -#define NO_ADAPTER_PRESENT 0x00000009 -#define NOT_ENOUGH_RESOURCES 0x0000000B -#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C -#define POWER_FAILURE 0x0000000E - -#define REMOVE_NOT_SUPPORTED 0x00000003 - - -/* - * error Messages - */ -#define msg_initialization_err "Initialization failure, error=%d\n" -#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n" -#define msg_HPC_non_compaq_or_intel "The PCI hot plug controller is not supported by this driver.\n" -#define msg_HPC_not_supported "this system is not supported by this version of cpqphpd. Upgrade to a newer version of cpqphpd\n" -#define msg_unable_to_save "unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n" -#define msg_button_on "PCI slot #%d - powering on due to button press.\n" -#define msg_button_off "PCI slot #%d - powering off due to button press.\n" -#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n" -#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n" - - -/* sysfs functions for the hotplug controller info */ -extern void cpqhp_create_ctrl_files (struct controller *ctrl); - -/* controller functions */ -extern void cpqhp_pushbutton_thread (unsigned long event_pointer); -extern irqreturn_t cpqhp_ctrl_intr (int IRQ, void *data, struct pt_regs *regs); -extern int cpqhp_find_available_resources (struct controller *ctrl, void *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); -extern struct slot *cpqhp_find_slot (struct controller *ctrl, u8 device); - -/* 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 &= ~(0x0101L << slot); - led_control |= (0x0001L << slot); - writel(led_control, ctrl->hpc_reg + LED_CONTROL); -} - - -static inline void slot_disable (struct controller *ctrl, u8 slot) -{ - u8 slot_enable; - - slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE); - slot_enable &= ~(0x01 << slot); - writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE); -} - - -static inline void slot_enable (struct controller *ctrl, u8 slot) -{ - u8 slot_enable; - - slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE); - slot_enable |= (0x01 << slot); - writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE); -} - - -static inline u8 is_slot_enabled (struct controller *ctrl, u8 slot) -{ - u8 slot_enable; - - slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE); - slot_enable &= (0x01 << slot); - return slot_enable ? 1 : 0; -} - - -static inline u8 read_slot_enable (struct controller *ctrl) -{ - return readb(ctrl->hpc_reg + SLOT_ENABLE); -} - - -/* - * get_controller_speed - find the current frequency/mode of controller. - * - * @ctrl: controller to get frequency/mode for. - * - * Returns controller speed. - * - */ -static inline u8 get_controller_speed (struct controller *ctrl) -{ - u8 curr_freq; - u16 misc; - - if (ctrl->pcix_support) { - curr_freq = readb(ctrl->hpc_reg + NEXT_CURR_FREQ); - if ((curr_freq & 0xB0) == 0xB0) - return PCI_SPEED_133MHz_PCIX; - if ((curr_freq & 0xA0) == 0xA0) - return PCI_SPEED_100MHz_PCIX; - if ((curr_freq & 0x90) == 0x90) - return PCI_SPEED_66MHz_PCIX; - if (curr_freq & 0x10) - return PCI_SPEED_66MHz; - - return PCI_SPEED_33MHz; - } - - misc = readw(ctrl->hpc_reg + MISC); - return (misc & 0x0800) ? PCI_SPEED_66MHz : PCI_SPEED_33MHz; -} - - -/* - * get_adapter_speed - find the max supported frequency/mode of adapter. - * - * @ctrl: hotplug controller. - * @hp_slot: hotplug slot where adapter is installed. - * - * Returns adapter speed. - * - */ -static inline u8 get_adapter_speed (struct controller *ctrl, u8 hp_slot) -{ - u32 temp_dword = readl(ctrl->hpc_reg + NON_INT_INPUT); - dbg("slot: %d, PCIXCAP: %8x\n", hp_slot, temp_dword); - if (ctrl->pcix_support) { - if (temp_dword & (0x10000 << hp_slot)) - return PCI_SPEED_133MHz_PCIX; - if (temp_dword & (0x100 << hp_slot)) - return PCI_SPEED_66MHz_PCIX; - } - - if (temp_dword & (0x01 << hp_slot)) - return PCI_SPEED_66MHz; - - return PCI_SPEED_33MHz; -} - -static inline void enable_slot_power (struct controller *ctrl, u8 slot) -{ - u8 slot_power; - - slot_power = readb(ctrl->hpc_reg + SLOT_POWER); - slot_power |= (0x01 << slot); - writeb(slot_power, ctrl->hpc_reg + SLOT_POWER); -} - -static inline void disable_slot_power (struct controller *ctrl, u8 slot) -{ - u8 slot_power; - - slot_power = readb(ctrl->hpc_reg + SLOT_POWER); - slot_power &= ~(0x01 << slot); - writeb(slot_power, ctrl->hpc_reg + SLOT_POWER); -} - - -static inline int cpq_get_attention_status (struct controller *ctrl, struct slot *slot) -{ - u8 hp_slot; - - 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("%s: slot->device = %d, ctrl->slot_device_offset = %d \n", - __FUNCTION__, slot->device, ctrl->slot_device_offset); - - status = (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot)); - - return(status == 0) ? 1 : 0; -} - - -static inline int get_presence_status (struct controller *ctrl, struct slot *slot) -{ - int presence_save = 0; - u8 hp_slot; - u32 tempdword; - - 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("%s - start\n", __FUNCTION__); - 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("%s - end\n", __FUNCTION__); - return retval; -} - - -/** - * set_controller_speed - set the frequency and/or mode of a specific - * controller segment. - * - * @ctrl: controller to change frequency/mode for. - * @adapter_speed: the speed of the adapter we want to match. - * @hp_slot: the slot number where the adapter is installed. - * - * Returns 0 if we successfully change frequency and/or mode to match the - * adapter speed. - * - */ -static inline u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_slot) -{ - struct slot *slot; - u8 reg; - u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER); - u16 reg16; - u32 leds = readl(ctrl->hpc_reg + LED_CONTROL); - - if (ctrl->speed == adapter_speed) - return 0; - - /* We don't allow freq/mode changes if we find another adapter running - * in another slot on this controller */ - for(slot = ctrl->slot; slot; slot = slot->next) { - if (slot->device == (hp_slot + ctrl->slot_device_offset)) - continue; - if (!slot->hotplug_slot && !slot->hotplug_slot->info) - continue; - if (slot->hotplug_slot->info->adapter_status == 0) - continue; - /* If another adapter is running on the same segment but at a - * lower speed/mode, we allow the new adapter to function at - * this rate if supported */ - if (ctrl->speed < adapter_speed) - return 0; - - return 1; - } - - /* If the controller doesn't support freq/mode changes and the - * controller is running at a higher mode, we bail */ - if ((ctrl->speed > adapter_speed) && (!ctrl->pcix_speed_capability)) - return 1; - - /* But we allow the adapter to run at a lower rate if possible */ - if ((ctrl->speed < adapter_speed) && (!ctrl->pcix_speed_capability)) - return 0; - - /* We try to set the max speed supported by both the adapter and - * controller */ - if (ctrl->speed_capability < adapter_speed) { - if (ctrl->speed == ctrl->speed_capability) - return 0; - adapter_speed = ctrl->speed_capability; - } - - writel(0x0L, ctrl->hpc_reg + LED_CONTROL); - writeb(0x00, ctrl->hpc_reg + SLOT_ENABLE); - - set_SOGO(ctrl); - wait_for_ctrl_irq(ctrl); - - if (adapter_speed != PCI_SPEED_133MHz_PCIX) - reg = 0xF5; - else - reg = 0xF4; - pci_write_config_byte(ctrl->pci_dev, 0x41, reg); - - reg16 = readw(ctrl->hpc_reg + NEXT_CURR_FREQ); - reg16 &= ~0x000F; - switch(adapter_speed) { - case(PCI_SPEED_133MHz_PCIX): - reg = 0x75; - reg16 |= 0xB; - break; - case(PCI_SPEED_100MHz_PCIX): - reg = 0x74; - reg16 |= 0xA; - break; - case(PCI_SPEED_66MHz_PCIX): - reg = 0x73; - reg16 |= 0x9; - break; - case(PCI_SPEED_66MHz): - reg = 0x73; - reg16 |= 0x1; - break; - default: /* 33MHz PCI 2.2 */ - reg = 0x71; - break; - - } - reg16 |= 0xB << 12; - writew(reg16, ctrl->hpc_reg + NEXT_CURR_FREQ); - - mdelay(5); - - /* Reenable interrupts */ - writel(0, ctrl->hpc_reg + INT_MASK); - - pci_write_config_byte(ctrl->pci_dev, 0x41, reg); - - /* Restart state machine */ - reg = ~0xF; - pci_read_config_byte(ctrl->pci_dev, 0x43, ®); - pci_write_config_byte(ctrl->pci_dev, 0x43, reg); - - /* Only if mode change...*/ - if (((ctrl->speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) || - ((ctrl->speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz))) - set_SOGO(ctrl); - - wait_for_ctrl_irq(ctrl); - mdelay(1100); - - /* Restore LED/Slot state */ - writel(leds, ctrl->hpc_reg + LED_CONTROL); - writeb(slot_power, ctrl->hpc_reg + SLOT_ENABLE); - - set_SOGO(ctrl); - wait_for_ctrl_irq(ctrl); - - ctrl->speed = adapter_speed; - slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); - - info("Successfully changed frequency/mode for adapter in slot %d\n", - slot->number); - return 0; -} - -#endif - diff -Nru a/drivers/hotplug/cpqphp_core.c b/drivers/hotplug/cpqphp_core.c --- a/drivers/hotplug/cpqphp_core.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1541 +0,0 @@ -/* - * 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 - * - * Jan 12, 2003 - Added 66/100/133MHz PCI-X support, - * Torben Mathiasen - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "cpqphp.h" -#include "cpqphp_nvram.h" -#include "../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependent we are... */ - - -/* Global variables */ -int cpqhp_debug; -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.7" -#define DRIVER_AUTHOR "Dan Zink , Greg Kroah-Hartman " -#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 int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); -static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); - -static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = { - .owner = THIS_MODULE, - .set_attention_status = set_attention_status, - .enable_slot = process_SI, - .disable_slot = process_SS, - .hardware_test = hardware_test, - .get_power_status = get_power_status, - .get_attention_status = get_attention_status, - .get_latch_status = get_latch_status, - .get_adapter_status = get_adapter_status, - .get_max_bus_speed = get_max_bus_speed, - .get_cur_bus_speed = get_cur_bus_speed, -}; - - -static inline int is_slot64bit (struct slot *slot) -{ - 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("%s\n", __FUNCTION__); - - tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); - - number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; - slot_device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4; - slot_number = ctrl->first_slot; - - while (number_of_slots) { - new_slot = (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 == PCI_SPEED_66MHz) - new_slot->capabilities |= PCISLOT_66_MHZ_OPERATION; - - ctrl_slot = slot_device - (readb(ctrl->hpc_reg + SLOT_MASK) >> 4); - - // Check presence - new_slot->capabilities |= ((((~tempdword) >> 23) | ((~tempdword) >> 15)) >> ctrl_slot) & 0x02; - // Check the switch state - new_slot->capabilities |= ((~tempdword & 0xFF) >> ctrl_slot) & 0x01; - // Check the slot enable - new_slot->capabilities |= ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04; - - /* register this slot with the hotplug pci core */ - new_slot->hotplug_slot->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); - kfree (new_slot->hotplug_slot->info); - kfree (new_slot->hotplug_slot->name); - kfree (new_slot->hotplug_slot); - kfree (new_slot); - 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->info); - kfree(old_slot->hotplug_slot->name); - 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_bus *bus, u8 bus_num, u8 dev_num, u8 *slot) -{ - struct irq_routing_table *PCIIRQRoutingInfoLength; - u32 work; - long len; - long loop; - - u8 tbus, tdevice, tslot, bridgeSlot; - - dbg("%s: %p, %d, %d, %p\n", __FUNCTION__, bus, bus_num, dev_num, slot); - - bridgeSlot = 0xFF; - - PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table(); - if (!PCIIRQRoutingInfoLength) - return -1; - - len = (PCIIRQRoutingInfoLength->size - - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); - // Make sure I got at least one entry - if (len == 0) { - 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. - bus->number = tbus; - pci_bus_read_config_dword (bus, PCI_DEVFN(tdevice, 0), PCI_REVISION_ID, &work); - - if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { - pci_bus_read_config_dword (bus, PCI_DEVFN(tdevice, 0), PCI_PRIMARY_BUS, &work); - // See if bridge's secondary bus matches target bus. - if (((work >> 8) & 0x000000FF) == (long) bus_num) { - bridgeSlot = tslot; - } - } - } - - } - - // If we got here, we didn't find an entry in the IRQ mapping table - // for the target PCI device. If we did determine that the target - // device is on the other side of a PCI-to-PCI bridge, return the - // slot number for the bridge. - if (bridgeSlot != 0xFF) { - *slot = bridgeSlot; - 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("%s - physical_slot = %s\n", __FUNCTION__, 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("%s - physical_slot = %s\n", __FUNCTION__, 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("%s - physical_slot = %s\n", __FUNCTION__, 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("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - 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("%s - physical_slot = %s\n", __FUNCTION__, 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("%s - physical_slot = %s\n", __FUNCTION__, 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("%s - physical_slot = %s\n", __FUNCTION__, 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("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - ctrl = slot->ctrl; - if (ctrl == NULL) - return -ENODEV; - - *value = get_presence_status (ctrl, slot); - - return 0; -} - -static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) -{ - struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); - struct controller *ctrl; - - if (slot == NULL) - return -ENODEV; - - dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - ctrl = slot->ctrl; - if (ctrl == NULL) - return -ENODEV; - - *value = ctrl->speed_capability; - - return 0; -} - -static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) -{ - struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); - struct controller *ctrl; - - if (slot == NULL) - return -ENODEV; - - dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - ctrl = slot->ctrl; - if (ctrl == NULL) - return -ENODEV; - - *value = ctrl->speed; - - return 0; -} - -static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - u8 num_of_slots = 0; - u8 hp_slot = 0; - u8 device; - u8 rev; - u8 bus_cap; - u16 temp_word; - u16 vendor_id; - u16 subsystem_vid; - u16 subsystem_deviceid; - u32 rc; - struct controller *ctrl; - struct pci_func *func; - - // Need to read VID early b/c it's used to differentiate CPQ and INTC discovery - rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id); - if (rc || ((vendor_id != PCI_VENDOR_ID_COMPAQ) && (vendor_id != PCI_VENDOR_ID_INTEL))) { - err(msg_HPC_non_compaq_or_intel); - return -ENODEV; - } - dbg("Vendor ID: %x\n", vendor_id); - - rc = pci_read_config_byte(pdev, PCI_REVISION_ID, &rev); - dbg("revision: %d\n", rev); - if (rc || ((vendor_id == PCI_VENDOR_ID_COMPAQ) && (!rev))) { - err(msg_HPC_rev_error); - return -ENODEV; - } - - /* Check for the proper subsytem ID's - * Intel uses a different SSID programming model than Compaq. - * For Intel, each SSID bit identifies a PHP capability. - * Also Intel HPC's may have RID=0. - */ - if ((rev > 2) || (vendor_id == PCI_VENDOR_ID_INTEL)) { - // TODO: This code can be made to support non-Compaq or Intel subsystem IDs - rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vid); - if (rc) { - err("%s : pci_read_config_word failed\n", __FUNCTION__); - return rc; - } - dbg("Subsystem Vendor ID: %x\n", subsystem_vid); - if ((subsystem_vid != PCI_VENDOR_ID_COMPAQ) && (subsystem_vid != PCI_VENDOR_ID_INTEL)) { - err(msg_HPC_non_compaq_or_intel); - return -ENODEV; - } - - ctrl = (struct controller *) kmalloc(sizeof(struct controller), GFP_KERNEL); - if (!ctrl) { - err("%s : out of memory\n", __FUNCTION__); - return -ENOMEM; - } - memset(ctrl, 0, sizeof(struct controller)); - - rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsystem_deviceid); - if (rc) { - err("%s : pci_read_config_word failed\n", __FUNCTION__); - goto err_free_ctrl; - } - - info("Hot Plug Subsystem Device ID: %x\n", subsystem_deviceid); - - /* Set Vendor ID, so it can be accessed later from other functions */ - ctrl->vendor_id = vendor_id; - - switch (subsystem_vid) { - case PCI_VENDOR_ID_COMPAQ: - if (rev >= 0x13) { /* CIOBX */ - ctrl->push_flag = 1; - ctrl->slot_switch_type = 1; // Switch is present - 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 = 1; // PCI-X supported - ctrl->pcix_speed_capability = 1; - pci_read_config_byte(pdev, 0x41, &bus_cap); - if (bus_cap & 0x80) { - dbg("bus max supports 133MHz PCI-X\n"); - ctrl->speed_capability = PCI_SPEED_133MHz_PCIX; - break; - } - if (bus_cap & 0x40) { - dbg("bus max supports 100MHz PCI-X\n"); - ctrl->speed_capability = PCI_SPEED_100MHz_PCIX; - break; - } - if (bus_cap & 20) { - dbg("bus max supports 66MHz PCI-X\n"); - ctrl->speed_capability = PCI_SPEED_66MHz_PCIX; - break; - } - if (bus_cap & 10) { - dbg("bus max supports 66MHz PCI\n"); - ctrl->speed_capability = PCI_SPEED_66MHz; - break; - } - - break; - } - - switch (subsystem_deviceid) { - case PCI_SUB_HPC_ID: - /* Original 6500/7000 implementation */ - ctrl->slot_switch_type = 1; // Switch is present - ctrl->speed_capability = PCI_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 = PCI_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 = PCI_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 = PCI_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; - case PCI_SUB_HPC_ID4: - /* First PCI-X implementation, 100MHz */ - ctrl->push_flag = 1; - ctrl->slot_switch_type = 1; // Switch is present - ctrl->speed_capability = PCI_SPEED_100MHz_PCIX; - 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 = 1; // PCI-X supported - ctrl->pcix_speed_capability = 0; - break; - default: - err(msg_HPC_not_supported); - rc = -ENODEV; - goto err_free_ctrl; - } - break; - - case PCI_VENDOR_ID_INTEL: - /* Check for speed capability (0=33, 1=66) */ - if (subsystem_deviceid & 0x0001) { - ctrl->speed_capability = PCI_SPEED_66MHz; - } else { - ctrl->speed_capability = PCI_SPEED_33MHz; - } - - /* Check for push button */ - if (subsystem_deviceid & 0x0002) { - /* no push button */ - ctrl->push_button = 0; - } else { - /* push button supported */ - ctrl->push_button = 1; - } - - /* Check for slot switch type (0=mechanical, 1=not mechanical) */ - if (subsystem_deviceid & 0x0004) { - /* no switch */ - ctrl->slot_switch_type = 0; - } else { - /* switch */ - ctrl->slot_switch_type = 1; - } - - /* PHP Status (0=De-feature PHP, 1=Normal operation) */ - if (subsystem_deviceid & 0x0008) { - ctrl->defeature_PHP = 1; // PHP supported - } else { - ctrl->defeature_PHP = 0; // PHP not supported - } - - /* Alternate Base Address Register Interface (0=not supported, 1=supported) */ - if (subsystem_deviceid & 0x0010) { - ctrl->alternate_base_address = 1; // supported - } else { - ctrl->alternate_base_address = 0; // not supported - } - - /* PCI Config Space Index (0=not supported, 1=supported) */ - if (subsystem_deviceid & 0x0020) { - ctrl->pci_config_space = 1; // supported - } else { - ctrl->pci_config_space = 0; // not supported - } - - /* PCI-X support */ - if (subsystem_deviceid & 0x0080) { - /* PCI-X capable */ - ctrl->pcix_support = 1; - /* Frequency of operation in PCI-X mode */ - if (subsystem_deviceid & 0x0040) { - /* 133MHz PCI-X if bit 7 is 1 */ - ctrl->pcix_speed_capability = 1; - } else { - /* 100MHz PCI-X if bit 7 is 1 and bit 0 is 0, */ - /* 66MHz PCI-X if bit 7 is 1 and bit 0 is 1 */ - ctrl->pcix_speed_capability = 0; - } - } else { - /* Conventional PCI */ - ctrl->pcix_support = 0; - ctrl->pcix_speed_capability = 0; - } - break; - - default: - err(msg_HPC_not_supported); - rc = -ENODEV; - goto err_free_ctrl; - } - - } else { - err(msg_HPC_not_supported); - return -ENODEV; - } - - // Tell the user that we found one. - info("Initializing the PCI hot plug controller residing on PCI bus %d\n", pdev->bus->number); - - dbg ("Hotplug controller capabilities:\n"); - dbg (" speed_capability %d\n", ctrl->speed_capability); - dbg (" slot_switch_type %s\n", ctrl->slot_switch_type == 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; - pci_set_drvdata(pdev, ctrl); - - /* make our own copy of the pci bus structure, as we like tweaking it a lot */ - ctrl->pci_bus = kmalloc (sizeof (*ctrl->pci_bus), GFP_KERNEL); - if (!ctrl->pci_bus) { - err("out of memory\n"); - rc = -ENOMEM; - goto err_free_ctrl; - } - memcpy (ctrl->pci_bus, pdev->bus, sizeof (*ctrl->pci_bus)); - - ctrl->bus = pdev->bus->number; - ctrl->rev = rev; - dbg("bus device function rev: %d %d %d %d\n", ctrl->bus, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), ctrl->rev); - - init_MUTEX(&ctrl->crit_sect); - init_waitqueue_head(&ctrl->queue); - - /* initialize our threads if they haven't already been started up */ - rc = one_time_init(); - if (rc) { - goto err_free_bus; - } - - dbg("pdev = %p\n", pdev); - dbg("pci resource start %lx\n", pci_resource_start(pdev, 0)); - dbg("pci resource len %lx\n", pci_resource_len(pdev, 0)); - - if (!request_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0), MY_NAME)) { - err("cannot reserve MMIO region\n"); - rc = -ENOMEM; - goto err_free_bus; - } - - ctrl->hpc_reg = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); - if (!ctrl->hpc_reg) { - err("cannot remap MMIO region %lx @ %lx\n", pci_resource_len(pdev, 0), pci_resource_start(pdev, 0)); - rc = -ENODEV; - goto err_free_mem_region; - } - - // Check for 66Mhz operation - ctrl->speed = get_controller_speed(ctrl); - - - //************************************************** - // - // Save configuration headers for this and - // subordinate PCI buses - // - //************************************************** - - // find the physical slot number of the first hot plug slot - - // Get slot won't work for devices behind bridges, but - // in this case it will always be called for the "base" - // bus/dev/func of a slot. - // CS: this is leveraging the PCIIRQ routing code from the kernel (pci-pc.c: get_irq_routing_table) - rc = get_slot_mapping(ctrl->pci_bus, pdev->bus->number, (readb(ctrl->hpc_reg + SLOT_MASK) >> 4), &(ctrl->first_slot)); - dbg("get_slot_mapping: first_slot = %d, returned = %d\n", ctrl->first_slot, rc); - if (rc) { - err(msg_initialization_err, rc); - goto err_iounmap; - } - - // Store PCI Config Space for all devices on this bus - rc = cpqhp_save_config(ctrl, ctrl->bus, readb(ctrl->hpc_reg + SLOT_MASK)); - if (rc) { - err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc); - goto err_iounmap; - } - - /* - * Get IO, memory, and IRQ resources for new devices - */ - // The next line is required for cpqhp_find_available_resources - ctrl->interrupt = pdev->irq; - - ctrl->cfgspc_irq = 0; - pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &ctrl->cfgspc_irq); - - rc = cpqhp_find_available_resources(ctrl, cpqhp_rom_start); - ctrl->add_support = !rc; - if (rc) { - dbg("cpqhp_find_available_resources = 0x%x\n", rc); - err("unable to locate PCI configuration resources for hot plug add.\n"); - goto err_iounmap; - } - - /* - * Finish setting up the hot plug ctrl device - */ - ctrl->slot_device_offset = readb(ctrl->hpc_reg + SLOT_MASK) >> 4; - dbg("NumSlots %d \n", ctrl->slot_device_offset); - - ctrl->next_event = 0; - - /* Setup the slot information structures */ - rc = ctrl_slot_setup(ctrl, smbios_start, smbios_table); - if (rc) { - err(msg_initialization_err, 6); - err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc); - goto err_iounmap; - } - - /* Mask all general input interrupts */ - writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_MASK); - - /* set up the interrupt */ - dbg("HPC interrupt = %d \n", ctrl->interrupt); - if (request_irq(ctrl->interrupt, cpqhp_ctrl_intr, - SA_SHIRQ, MY_NAME, ctrl)) { - err("Can't get irq %d for the hotplug pci controller\n", ctrl->interrupt); - rc = -ENODEV; - goto err_iounmap; - } - - /* Enable Shift Out interrupt and clear it, also enable SERR on power fault */ - temp_word = readw(ctrl->hpc_reg + MISC); - temp_word |= 0x4006; - writew(temp_word, ctrl->hpc_reg + MISC); - - // Changed 05/05/97 to clear all interrupts at start - writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_INPUT_CLEAR); - - ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); - - writel(0x0L, ctrl->hpc_reg + INT_MASK); - - if (!cpqhp_ctrl_list) { - cpqhp_ctrl_list = ctrl; - ctrl->next = NULL; - } else { - ctrl->next = cpqhp_ctrl_list; - cpqhp_ctrl_list = ctrl; - } - - // turn off empty slots here unless command line option "ON" set - // Wait for exclusive access to hardware - down(&ctrl->crit_sect); - - num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; - - // find first device number for the ctrl - device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4; - - while (num_of_slots) { - dbg("num_of_slots: %d\n", num_of_slots); - func = cpqhp_slot_find(ctrl->bus, device, 0); - if (!func) - break; - - hp_slot = func->device - ctrl->slot_device_offset; - dbg("hp_slot: %d\n", hp_slot); - - // We have to save the presence info for these slots - temp_word = ctrl->ctrl_int_comp >> 16; - func->presence_save = (temp_word >> hp_slot) & 0x01; - func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; - - if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { - func->switch_save = 0; - } else { - func->switch_save = 0x10; - } - - if (!power_mode) { - if (!func->is_a_board) { - green_LED_off (ctrl, hp_slot); - slot_disable (ctrl, hp_slot); - } - } - - device++; - num_of_slots--; - } - - if (!power_mode) { - set_SOGO(ctrl); - // Wait for SOBS to be unset - wait_for_ctrl_irq (ctrl); - } - - rc = init_SERR(ctrl); - if (rc) { - err("init_SERR failed\n"); - up(&ctrl->crit_sect); - goto err_free_irq; - } - - // Done with exclusive hardware access - up(&ctrl->crit_sect); - - cpqhp_create_ctrl_files (ctrl); - - return 0; - -err_free_irq: - free_irq(ctrl->interrupt, ctrl); -err_iounmap: - iounmap(ctrl->hpc_reg); -err_free_mem_region: - release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); -err_free_bus: - kfree(ctrl->pci_bus); -err_free_ctrl: - kfree(ctrl); - return rc; -} - - -static int one_time_init(void) -{ - int loop; - int retval = 0; - static int initialized = 0; - - if (initialized) - return 0; - - power_mode = 0; - - retval = pci_print_IRQ_route(); - if (retval) - goto error; - - dbg("Initialize + Start the notification mechanism \n"); - - retval = cpqhp_event_start_thread(); - if (retval) - goto error; - - dbg("Initialize slot lists\n"); - for (loop = 0; loop < 256; loop++) { - cpqhp_slot_list[loop] = NULL; - } - - // FIXME: We also need to hook the NMI handler eventually. - // this also needs to be worked with Christoph - // register_NMI_handler(); - - // Map rom address - cpqhp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN); - if (!cpqhp_rom_start) { - err ("Could not ioremap memory region for ROM\n"); - retval = -EIO;; - goto error; - } - - /* Now, map the int15 entry point if we are on compaq specific hardware */ - compaq_nvram_init(cpqhp_rom_start); - - /* Map smbios table entry point structure */ - smbios_table = detect_SMBIOS_pointer(cpqhp_rom_start, cpqhp_rom_start + ROM_PHY_LEN); - if (!smbios_table) { - err ("Could not find the SMBIOS pointer in memory\n"); - retval = -EIO;; - goto error; - } - - 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; - } - - 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) { - if (ctrl->hpc_reg) { - u16 misc; - rc = read_slot_enable (ctrl); - - writeb(0, ctrl->hpc_reg + SLOT_SERR); - writel(0xFFFFFFC0L | ~rc, ctrl->hpc_reg + INT_MASK); - - misc = readw(ctrl->hpc_reg + MISC); - misc &= 0xFFFD; - writew(misc, ctrl->hpc_reg + MISC); - } - - ctrl_slot_cleanup(ctrl); - - res = ctrl->io_head; - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - res = ctrl->mem_head; - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - res = ctrl->p_mem_head; - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - res = ctrl->bus_head; - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - kfree (ctrl->pci_bus); - - tctrl = ctrl; - ctrl = ctrl->next; - kfree(tctrl); - } - - for (loop = 0; loop < 256; loop++) { - next = cpqhp_slot_list[loop]; - while (next != NULL) { - res = next->io_head; - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - res = next->mem_head; - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - res = next->p_mem_head; - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - res = next->bus_head; - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - TempSlot = next; - next = next->next; - kfree(TempSlot); - } - } - - // Stop the notification mechanism - cpqhp_event_stop_thread(); - - //unmap the rom address - if (cpqhp_rom_start) - iounmap(cpqhp_rom_start); - if (smbios_start) - iounmap(smbios_start); -} - - - -static struct pci_device_id hpcd_pci_tbl[] __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("unload_cpqphpd()\n"); - unload_cpqphpd(); - - dbg("pci_unregister_driver\n"); - pci_unregister_driver(&cpqhpc_driver); -} - - -module_init(cpqhpc_init); -module_exit(cpqhpc_cleanup); - - diff -Nru a/drivers/hotplug/cpqphp_ctrl.c b/drivers/hotplug/cpqphp_ctrl.c --- a/drivers/hotplug/cpqphp_ctrl.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,3084 +0,0 @@ -/* - * 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 - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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; -} - - -/* - * cpqhp_find_slot - */ -struct slot *cpqhp_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 = cpqhp_find_slot(ctrl, hp_slot + (readb(ctrl->hpc_reg + SLOT_MASK) >> 4)); - if (!p_slot) - return 0; - - // If the switch closed, must be a button - // If not in button mode, nevermind - if (func->switch_save && (ctrl->push_button == 1)) { - temp_word = ctrl->ctrl_int_comp >> 16; - temp_byte = (temp_word >> hp_slot) & 0x01; - temp_byte |= (temp_word >> (hp_slot + 7)) & 0x02; - - if (temp_byte != func->presence_save) { - //********************************* - // button Pressed (doesn't do anything) - //********************************* - dbg("hp_slot %d button pressed\n", hp_slot); - taskInfo->event_type = INT_BUTTON_PRESS; - } else { - //********************************* - // button Released - TAKE ACTION!!!! - //********************************* - dbg("hp_slot %d button released\n", hp_slot); - taskInfo->event_type = INT_BUTTON_RELEASE; - - // Cancel if we are still blinking - if ((p_slot->state == BLINKINGON_STATE) - || (p_slot->state == BLINKINGOFF_STATE)) { - taskInfo->event_type = INT_BUTTON_CANCEL; - dbg("hp_slot %d button cancel\n", hp_slot); - } else if ((p_slot->state == POWERON_STATE) - || (p_slot->state == POWEROFF_STATE)) { - //info(msg_button_ignore, p_slot->number); - taskInfo->event_type = INT_BUTTON_IGNORE; - dbg("hp_slot %d button ignore\n", hp_slot); - } - } - } else { - // Switch is open, assume a presence change - // Save the presence state - temp_word = ctrl->ctrl_int_comp >> 16; - func->presence_save = (temp_word >> hp_slot) & 0x01; - func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; - - if ((!(ctrl->ctrl_int_comp & (0x010000 << hp_slot))) || - (!(ctrl->ctrl_int_comp & (0x01000000 << hp_slot)))) { - //********************************* - // Present - //********************************* - taskInfo->event_type = INT_PRESENCE_ON; - } else { - //********************************* - // Not Present - //********************************* - taskInfo->event_type = INT_PRESENCE_OFF; - } - } - } - } - - return rc; -} - - -static u8 handle_power_fault(u8 change, struct controller * ctrl) -{ - int hp_slot; - u8 rc = 0; - struct pci_func *func; - struct event_info *taskInfo; - - if (!change) - return 0; - - //********************************* - // power fault - //********************************* - - info("power fault interrupt\n"); - - for (hp_slot = 0; hp_slot < 6; hp_slot++) { - if (change & (0x01 << hp_slot)) { - //********************************* - // this one changed. - //********************************* - func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0); - - taskInfo = &(ctrl->event_queue[ctrl->next_event]); - ctrl->next_event = (ctrl->next_event + 1) % 10; - taskInfo->hp_slot = hp_slot; - - rc++; - - if (ctrl->ctrl_int_comp & (0x00000100 << hp_slot)) { - //********************************* - // power fault Cleared - //********************************* - func->status = 0x00; - - taskInfo->event_type = INT_POWER_FAULT_CLEAR; - } else { - //********************************* - // power fault - //********************************* - taskInfo->event_type = INT_POWER_FAULT; - - if (ctrl->rev < 4) { - amber_LED_on (ctrl, hp_slot); - green_LED_off (ctrl, hp_slot); - set_SOGO (ctrl); - - // this is a fatal condition, we want to crash the - // machine to protect from data corruption - // simulated_NMI shouldn't ever return - //FIXME - //simulated_NMI(hp_slot, ctrl); - - //The following code causes a software crash just in - //case simulated_NMI did return - //FIXME - //panic(msg_power_fault); - } else { - // set power fault status for this board - func->status = 0xFF; - info("power fault bit %x set\n", hp_slot); - } - } - } - } - - return rc; -} - - -/* - * sort_by_size - * - * 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("%s: req_size =%x node=%p, base=%x, length=%x\n", - __FUNCTION__, size, node, node->base, node->length); - if (node->length < size) - continue; - - if (node->base & (size - 1)) { - dbg("%s: not aligned\n", __FUNCTION__); - // this one isn't base aligned properly - // so we'll make a new entry and split it up - temp_dword = (node->base | (size-1)) + 1; - - // Short circuit if adjusted size is too small - if ((node->length - (temp_dword - node->base)) < size) - continue; - - split_node = (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("%s: too big\n", __FUNCTION__); - // this one is longer than we need - // so we'll make a new entry and split it up - split_node = (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("%s: got one!!!\n", __FUNCTION__); - // If we got here, then it is the right size - // Now take it out of the list - if (*head == node) { - *head = node->next; - } else { - prevnode = *head; - while (prevnode->next != node) - prevnode = prevnode->next; - - prevnode->next = node->next; - } - node->next = NULL; - // Stop looping - break; - } - return(node); -} - - -/* - * 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("%s: head = %p, *head = %p\n", __FUNCTION__, head, *head); - - if (!(*head)) - return(1); - - dbg("*head->next = %p\n",(*head)->next); - - if (!(*head)->next) - return(0); /* only one item on the list, already sorted! */ - - dbg("*head->base = 0x%x\n",(*head)->base); - dbg("*head->next->base = 0x%x\n",(*head)->next->base); - while (out_of_order) { - out_of_order = 0; - - // Special case for swapping list head - if (((*head)->next) && - ((*head)->base > (*head)->next->base)) { - node1 = *head; - (*head) = (*head)->next; - node1->next = (*head)->next; - (*head)->next = node1; - out_of_order++; - } - - node1 = (*head); - - while (node1->next && node1->next->next) { - if (node1->next->base > node1->next->next->base) { - out_of_order++; - node2 = node1->next; - node1->next = node1->next->next; - node1 = node1->next; - node2->next = node1->next; - node1->next = node2; - } else - node1 = node1->next; - } - } // End of out_of_order loop - - node1 = *head; - - while (node1 && node1->next) { - if ((node1->base + node1->length) == node1->next->base) { - // Combine - dbg("8..\n"); - node1->length += node1->next->length; - node2 = node1->next; - node1->next = node1->next->next; - kfree(node2); - } else - node1 = node1->next; - } - - return(0); -} - - -irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data, struct pt_regs *regs) -{ - struct controller *ctrl = data; - u8 schedule_flag = 0; - u8 reset; - u16 misc; - u32 Diff; - u32 temp_dword; - - - misc = readw(ctrl->hpc_reg + MISC); - //********************************* - // Check to see if it was our interrupt - //********************************* - if (!(misc & 0x000C)) { - return IRQ_NONE; - } - - if (misc & 0x0004) { - //********************************* - // Serial Output interrupt Pending - //********************************* - - // Clear the interrupt - misc |= 0x0004; - writew(misc, ctrl->hpc_reg + MISC); - - // Read to clear posted writes - misc = readw(ctrl->hpc_reg + MISC); - - dbg ("%s - waking up\n", __FUNCTION__); - wake_up_interruptible(&ctrl->queue); - } - - if (misc & 0x0008) { - // General-interrupt-input interrupt Pending - Diff = readl(ctrl->hpc_reg + INT_INPUT_CLEAR) ^ ctrl->ctrl_int_comp; - - ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); - - // Clear the interrupt - writel(Diff, ctrl->hpc_reg + INT_INPUT_CLEAR); - - // Read it back to clear any posted writes - temp_dword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); - - if (!Diff) { - // Clear all interrupts - writel(0xFFFFFFFF, ctrl->hpc_reg + INT_INPUT_CLEAR); - } - - schedule_flag += handle_switch_change((u8)(Diff & 0xFFL), ctrl); - schedule_flag += handle_presence_change((u16)((Diff & 0xFFFF0000L) >> 16), ctrl); - schedule_flag += handle_power_fault((u8)((Diff & 0xFF00L) >> 8), ctrl); - } - - reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE); - if (reset & 0x40) { - /* Bus reset has completed */ - reset &= 0xCF; - writeb(reset, ctrl->hpc_reg + RESET_FREQ_MODE); - reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE); - wake_up_interruptible(&ctrl->queue); - } - - if (schedule_flag) { - up(&event_semaphore); - dbg("Signal event_semaphore\n"); - } - return IRQ_HANDLED; -} - - -/** - * cpqhp_slot_create - Creates a node and adds it to the proper bus. - * @busnumber - bus where new node is to be located - * - * Returns pointer to the new node or NULL if unsuccessful - */ -struct pci_func *cpqhp_slot_create(u8 busnumber) -{ - struct pci_func *new_slot; - struct pci_func *next; - - new_slot = (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; - u8 adapter_speed; - u32 index; - u32 rc = 0; - u32 src = 8; - - hp_slot = func->device - ctrl->slot_device_offset; - - if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot)) { - //********************************* - // The switch is open. - //********************************* - rc = INTERLOCK_OPEN; - } else if (is_slot_enabled (ctrl, hp_slot)) { - //********************************* - // The board is already on - //********************************* - rc = CARD_FUNCTIONING; - } else { - // 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); - - adapter_speed = get_adapter_speed(ctrl, hp_slot); - if (ctrl->speed != adapter_speed) - if (set_controller_speed(ctrl, adapter_speed, hp_slot)) - rc = WRONG_BUS_FREQUENCY; - - // turn off board without attaching to the bus - disable_slot_power (ctrl, hp_slot); - - set_SOGO(ctrl); - - // Wait for SOBS to be unset - wait_for_ctrl_irq (ctrl); - - // 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; - u8 adapter_speed; - int index; - u32 temp_register = 0xFFFFFFFF; - u32 rc = 0; - struct pci_func *new_slot = NULL; - struct slot *p_slot; - struct resource_lists res_lists; - - hp_slot = func->device - ctrl->slot_device_offset; - dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", - __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot); - - // 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); - - adapter_speed = get_adapter_speed(ctrl, hp_slot); - if (ctrl->speed != adapter_speed) - if (set_controller_speed(ctrl, adapter_speed, hp_slot)) - rc = WRONG_BUS_FREQUENCY; - - // turn off board without attaching to the bus - disable_slot_power (ctrl, hp_slot); - - set_SOGO(ctrl); - - // Wait for SOBS to be unset - wait_for_ctrl_irq (ctrl); - - // Done with exclusive hardware access - up(&ctrl->crit_sect); - - if (rc) - return(rc); - - p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); - - // turn on board and blink green LED - - // Wait for exclusive access to hardware - dbg("%s: before down\n", __FUNCTION__); - down(&ctrl->crit_sect); - dbg("%s: after down\n", __FUNCTION__); - - dbg("%s: before slot_enable\n", __FUNCTION__); - slot_enable (ctrl, hp_slot); - - dbg("%s: before green_LED_blink\n", __FUNCTION__); - green_LED_blink (ctrl, hp_slot); - - dbg("%s: before amber_LED_blink\n", __FUNCTION__); - amber_LED_off (ctrl, hp_slot); - - dbg("%s: before set_SOGO\n", __FUNCTION__); - set_SOGO(ctrl); - - // Wait for SOBS to be unset - dbg("%s: before wait_for_ctrl_irq\n", __FUNCTION__); - wait_for_ctrl_irq (ctrl); - dbg("%s: after wait_for_ctrl_irq\n", __FUNCTION__); - - // Done with exclusive hardware access - dbg("%s: before up\n", __FUNCTION__); - up(&ctrl->crit_sect); - dbg("%s: after up\n", __FUNCTION__); - - // Wait for ~1 second because of hot plug spec - dbg("%s: before long_delay\n", __FUNCTION__); - long_delay(1*HZ); - dbg("%s: after long_delay\n", __FUNCTION__); - - dbg("%s: func status = %x\n", __FUNCTION__, func->status); - // Check for a power fault - if (func->status == 0xFF) { - // power fault occurred, but it was benign - temp_register = 0xFFFFFFFF; - dbg("%s: temp register set to %x by power fault\n", __FUNCTION__, temp_register); - rc = POWER_FAILURE; - func->status = 0; - } else { - // Get vendor/device ID u32 - ctrl->pci_bus->number = func->bus; - rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register); - dbg("%s: pci_read_config_dword returns %d\n", __FUNCTION__, rc); - dbg("%s: temp_register is %x\n", __FUNCTION__, temp_register); - - if (rc != 0) { - // Something's wrong here - temp_register = 0xFFFFFFFF; - dbg("%s: temp register set to %x by error\n", __FUNCTION__, temp_register); - } - // Preset return code. It will be changed later if things go okay. - rc = NO_ADAPTER_PRESENT; - } - - // All F's is an empty slot or an invalid board - if (temp_register != 0xFFFFFFFF) { // Check for a board in the slot - res_lists.io_head = ctrl->io_head; - res_lists.mem_head = ctrl->mem_head; - res_lists.p_mem_head = ctrl->p_mem_head; - res_lists.bus_head = ctrl->bus_head; - res_lists.irqs = NULL; - - rc = configure_new_device(ctrl, func, 0, &res_lists); - - dbg("%s: back from configure_new_device\n", __FUNCTION__); - ctrl->io_head = res_lists.io_head; - ctrl->mem_head = res_lists.mem_head; - ctrl->p_mem_head = res_lists.p_mem_head; - ctrl->bus_head = res_lists.bus_head; - - cpqhp_resource_sort_and_combine(&(ctrl->mem_head)); - cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head)); - cpqhp_resource_sort_and_combine(&(ctrl->io_head)); - cpqhp_resource_sort_and_combine(&(ctrl->bus_head)); - - if (rc) { - // 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("%s: configure linux pci_dev structure\n", __FUNCTION__); - index = 0; - do { - new_slot = cpqhp_slot_find(ctrl->bus, func->device, index++); - if (new_slot && !new_slot->pci_dev) { - cpqhp_configure_device(ctrl, new_slot); - } - } while (new_slot); - - // 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 %s, hp_slot = %d\n", __FUNCTION__, hp_slot); - - // When we get here, it is safe to change base Address Registers. - // We will attempt to save the base Address Register Lengths - if (replace_flag || !ctrl->add_support) - rc = cpqhp_save_base_addr_length(ctrl, func); - else if (!func->bus_head && !func->mem_head && - !func->p_mem_head && !func->io_head) { - // Here we check to see if we've saved any of the board's - // resources already. If so, we'll skip the attempt to - // determine what's being used. - index = 0; - temp_func = cpqhp_slot_find(func->bus, func->device, index++); - while (temp_func) { - if (temp_func->bus_head || temp_func->mem_head - || temp_func->p_mem_head || temp_func->io_head) { - skip = 1; - break; - } - temp_func = cpqhp_slot_find(temp_func->bus, temp_func->device, index++); - } - - if (!skip) - rc = cpqhp_save_used_resources(ctrl, func); - } - // Change status to shutdown - if (func->is_a_board) - func->status = 0x01; - func->configured = 0; - - // 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("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; - int result; - - info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->power_status = get_slot_enabled(ctrl, slot); - info->attention_status = cpq_get_attention_status(ctrl, slot); - info->latch_status = cpq_get_latch_status(ctrl, slot); - info->adapter_status = get_presence_status(ctrl, slot); - result = pci_hp_change_slot_info(slot->hotplug_slot, info); - kfree (info); - return result; -} - -static void interrupt_event_handler(struct controller *ctrl) -{ - int loop = 0; - int change = 1; - struct pci_func *func; - u8 hp_slot; - struct slot *p_slot; - - while (change) { - change = 0; - - for (loop = 0; loop < 10; loop++) { - //dbg("loop %d\n", loop); - if (ctrl->event_queue[loop].event_type != 0) { - hp_slot = ctrl->event_queue[loop].hp_slot; - - func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0); - if (!func) - return; - - p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); - if (!p_slot) - return; - - dbg("hp_slot %d, func %p, p_slot %p\n", - hp_slot, func, p_slot); - - if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) { - dbg("button pressed\n"); - } else if (ctrl->event_queue[loop].event_type == - INT_BUTTON_CANCEL) { - dbg("button cancel\n"); - del_timer(&p_slot->task_event); - - // 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 %s\n", __FUNCTION__); - return ; - } - - if (func != NULL && ctrl != NULL) { - if (cpqhp_process_SS(ctrl, func) != 0) { - amber_LED_on (ctrl, hp_slot); - green_LED_on (ctrl, hp_slot); - - set_SOGO(ctrl); - - // Wait for SOBS to be unset - wait_for_ctrl_irq (ctrl); - } - } - - p_slot->state = STATIC_STATE; - } else { - p_slot->state = POWERON_STATE; - // slot is off - - func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0); - dbg("In add_board, func = %p, ctrl = %p\n", func, ctrl); - if (!func) { - dbg("Error! func NULL in %s\n", __FUNCTION__); - return ; - } - - if (func != NULL && ctrl != NULL) { - if (cpqhp_process_SI(ctrl, func) != 0) { - amber_LED_on (ctrl, hp_slot); - green_LED_off (ctrl, hp_slot); - - set_SOGO(ctrl); - - // Wait for SOBS to be unset - wait_for_ctrl_irq (ctrl); - } - } - - p_slot->state = STATIC_STATE; - } - - return; -} - - -int cpqhp_process_SI (struct controller *ctrl, struct pci_func *func) -{ - u8 device, hp_slot; - u16 temp_word; - u32 tempdword; - int rc; - struct slot* p_slot; - int physical_slot = 0; - - if (!ctrl) - return(1); - - tempdword = 0; - - device = func->device; - hp_slot = device - ctrl->slot_device_offset; - p_slot = cpqhp_find_slot(ctrl, device); - if (p_slot) { - physical_slot = p_slot->number; - } - - // Check to see if the interlock is closed - tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); - - if (tempdword & (0x01 << hp_slot)) { - return(1); - } - - if (func->is_a_board) { - rc = board_replaced(func, ctrl); - } else { - // add board - slot_remove(func); - - func = cpqhp_slot_create(ctrl->bus); - if (func == NULL) { - return(1); - } - - func->bus = ctrl->bus; - func->device = device; - func->function = 0; - func->configured = 0; - func->is_a_board = 1; - - // We have to save the presence info for these slots - temp_word = ctrl->ctrl_int_comp >> 16; - func->presence_save = (temp_word >> hp_slot) & 0x01; - func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; - - if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { - func->switch_save = 0; - } else { - func->switch_save = 0x10; - } - - rc = board_added(func, ctrl); - if (rc) { - if (is_bridge(func)) { - bridge_slot_remove(func); - } else - slot_remove(func); - - // Setup slot structure with entry for empty slot - func = cpqhp_slot_create(ctrl->bus); - - if (func == NULL) { - // 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("%s: rc = %d\n", __FUNCTION__, rc); - } - - if (p_slot) - update_slot_info(ctrl, p_slot); - - return rc; -} - - -int cpqhp_process_SS (struct controller *ctrl, struct pci_func *func) -{ - u8 device, class_code, header_type, BCR; - u8 index = 0; - u8 replace_flag; - u32 rc = 0; - unsigned int devfn; - struct slot* p_slot; - struct pci_bus *pci_bus = ctrl->pci_bus; - int physical_slot=0; - - device = func->device; - func = cpqhp_slot_find(ctrl->bus, device, index++); - p_slot = cpqhp_find_slot(ctrl, device); - if (p_slot) { - physical_slot = p_slot->number; - } - - // Make sure there are no video controllers here - while (func && !rc) { - pci_bus->number = func->bus; - devfn = PCI_DEVFN(func->device, func->function); - - // Check the Class Code - rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); - if (rc) - return rc; - - if (class_code == PCI_BASE_CLASS_DISPLAY) { - /* Display/Video adapter (not supported) */ - rc = REMOVE_NOT_SUPPORTED; - } else { - // See if it's a bridge - rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); - if (rc) - return rc; - - // If it's a bridge, check the VGA Enable bit - if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { - rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR); - if (rc) - return rc; - - // If the VGA Enable bit is set, remove isn't supported - if (BCR & PCI_BRIDGE_CTL_VGA) { - rc = REMOVE_NOT_SUPPORTED; - } - } - } - - func = cpqhp_slot_find(ctrl->bus, device, index++); - } - - func = cpqhp_slot_find(ctrl->bus, device, 0); - if ((func != NULL) && !rc) { - //FIXME: Replace flag should be passed into process_SS - replace_flag = !(ctrl->add_support); - rc = remove_board(func, replace_flag, ctrl); - } else if (!rc) { - rc = 1; - } - - if (p_slot) - update_slot_info(ctrl, p_slot); - - return(rc); -} - - - -/** - * 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("%s\n", __FUNCTION__); - // Check for Multi-function device - ctrl->pci_bus->number = func->bus; - rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte); - if (rc) { - dbg("%s: rc = %d\n", __FUNCTION__, rc); - return rc; - } - - if (temp_byte & 0x80) // Multi-function device - max_functions = 8; - else - max_functions = 1; - - function = 0; - - do { - rc = configure_new_function(ctrl, new_slot, behind_bridge, resources); - - if (rc) { - dbg("configure_new_function failed %d\n",rc); - index = 0; - - while (new_slot) { - new_slot = cpqhp_slot_find(new_slot->bus, new_slot->device, index++); - - if (new_slot) - cpqhp_return_board_resources(new_slot, resources); - } - - return(rc); - } - - function++; - - stop_it = 0; - - // The following loop skips to the next present function - // and creates a board structure - - while ((function < max_functions) && (!stop_it)) { - pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID); - - if (ID == 0xFFFFFFFF) { // There's nothing there. - function++; - } else { // There's something there - // Setup slot structure. - new_slot = cpqhp_slot_create(func->bus); - - if (new_slot == NULL) { - // 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; - unsigned int devfn; - struct pci_resource *mem_node; - struct pci_resource *p_mem_node; - struct pci_resource *io_node; - struct pci_resource *bus_node; - struct pci_resource *hold_mem_node; - struct pci_resource *hold_p_mem_node; - struct pci_resource *hold_IO_node; - struct pci_resource *hold_bus_node; - struct irq_mapping irqs; - struct pci_func *new_slot; - struct pci_bus *pci_bus; - struct resource_lists temp_resources; - - pci_bus = ctrl->pci_bus; - pci_bus->number = func->bus; - devfn = PCI_DEVFN(func->device, func->function); - - // Check for Bridge - rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte); - if (rc) - return rc; - - if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge - // set Primary bus - dbg("set Primary bus = %d\n", func->bus); - rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_PRIMARY_BUS, func->bus); - if (rc) - return rc; - - // find range of busses to use - dbg("find ranges of buses to use\n"); - bus_node = get_max_resource(&resources->bus_head, 1); - - // If we don't have any busses to allocate, we can't continue - if (!bus_node) - return -ENOMEM; - - // set Secondary bus - temp_byte = bus_node->base; - dbg("set Secondary bus = %d\n", bus_node->base); - rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte); - if (rc) - return rc; - - // set subordinate bus - temp_byte = bus_node->base + bus_node->length - 1; - dbg("set subordinate bus = %d\n", bus_node->base + bus_node->length - 1); - rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); - if (rc) - return rc; - - // set subordinate Latency Timer and base Latency Timer - temp_byte = 0x40; - rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte); - if (rc) - return rc; - rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); - if (rc) - return rc; - - // set Cache Line size - temp_byte = 0x08; - rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); - if (rc) - return rc; - - // Setup the IO, memory, and prefetchable windows - - io_node = get_max_resource(&(resources->io_head), 0x1000); - if (!io_node) - return -ENOMEM; - mem_node = get_max_resource(&(resources->mem_head), 0x100000); - if (!mem_node) - return -ENOMEM; - p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000); - if (!p_mem_node) - return -ENOMEM; - dbg("Setup the IO, memory, and prefetchable windows\n"); - dbg("io_node\n"); - dbg("(base, len, next) (%x, %x, %p)\n", io_node->base, io_node->length, io_node->next); - dbg("mem_node\n"); - dbg("(base, len, next) (%x, %x, %p)\n", mem_node->base, mem_node->length, mem_node->next); - dbg("p_mem_node\n"); - dbg("(base, len, next) (%x, %x, %p)\n", p_mem_node->base, p_mem_node->length, p_mem_node->next); - - // set up the IRQ info - if (!resources->irqs) { - irqs.barber_pole = 0; - irqs.interrupt[0] = 0; - irqs.interrupt[1] = 0; - irqs.interrupt[2] = 0; - irqs.interrupt[3] = 0; - irqs.valid_INT = 0; - } else { - irqs.barber_pole = resources->irqs->barber_pole; - irqs.interrupt[0] = resources->irqs->interrupt[0]; - irqs.interrupt[1] = resources->irqs->interrupt[1]; - irqs.interrupt[2] = resources->irqs->interrupt[2]; - irqs.interrupt[3] = resources->irqs->interrupt[3]; - irqs.valid_INT = resources->irqs->valid_INT; - } - - // set up resource lists that are now aligned on top and bottom - // for anything behind the bridge. - temp_resources.bus_head = bus_node; - temp_resources.io_head = io_node; - temp_resources.mem_head = mem_node; - temp_resources.p_mem_head = p_mem_node; - temp_resources.irqs = &irqs; - - // Make copies of the nodes we are going to pass down so that - // if there is a problem,we can just use these to free resources - hold_bus_node = (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_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte); - - temp_byte = (io_node->base + io_node->length - 1) >> 8; - rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte); - } else { - kfree(hold_IO_node); - hold_IO_node = NULL; - } - - // If we have memory resources copy them and fill in the bridge's - // memory range registers. Otherwise, fill in the range - // registers with values that disable them. - if (mem_node) { - memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource)); - mem_node->next = NULL; - - // set Mem base and Limit registers - temp_word = mem_node->base >> 16; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); - - temp_word = (mem_node->base + mem_node->length - 1) >> 16; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); - } else { - temp_word = 0xFFFF; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); - - temp_word = 0x0000; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); - - kfree(hold_mem_node); - hold_mem_node = NULL; - } - - // If we have prefetchable memory resources copy them and - // fill in the bridge's memory range registers. Otherwise, - // fill in the range registers with values that disable them. - if (p_mem_node) { - memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource)); - p_mem_node->next = NULL; - - // set Pre Mem base and Limit registers - temp_word = p_mem_node->base >> 16; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); - - temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); - } else { - temp_word = 0xFFFF; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); - - temp_word = 0x0000; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); - - kfree(hold_p_mem_node); - hold_p_mem_node = NULL; - } - - // Adjust this to compensate for extra adjustment in first loop - irqs.barber_pole--; - - rc = 0; - - // Here we actually find the devices and configure them - for (device = 0; (device <= 0x1F) && !rc; device++) { - irqs.barber_pole = (irqs.barber_pole + 1) & 0x03; - - ID = 0xFFFFFFFF; - pci_bus->number = hold_bus_node->base; - pci_bus_read_config_dword (pci_bus, PCI_DEVFN(device, 0), 0x00, &ID); - pci_bus->number = func->bus; - - if (ID != 0xFFFFFFFF) { // device Present - // Setup slot structure. - new_slot = cpqhp_slot_create(hold_bus_node->base); - - if (new_slot == NULL) { - // 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_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); - - if (temp_resources.bus_head->length == 0) { - kfree(temp_resources.bus_head); - temp_resources.bus_head = NULL; - } else { - return_resource(&(resources->bus_head), temp_resources.bus_head); - } - } - - // If we have IO space available and there is some left, - // return the unused portion - if (hold_IO_node && temp_resources.io_head) { - io_node = do_pre_bridge_resource_split(&(temp_resources.io_head), - &hold_IO_node, 0x1000); - - // Check if we were able to split something off - if (io_node) { - hold_IO_node->base = io_node->base + io_node->length; - - temp_byte = (hold_IO_node->base) >> 8; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_IO_BASE, temp_byte); - - return_resource(&(resources->io_head), io_node); - } - - io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000); - - // Check if we were able to split something off - if (io_node) { - // First use the temporary node to store information for the board - hold_IO_node->length = io_node->base - hold_IO_node->base; - - // If we used any, add it to the board's list - if (hold_IO_node->length) { - hold_IO_node->next = func->io_head; - func->io_head = hold_IO_node; - - temp_byte = (io_node->base - 1) >> 8; - rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte); - - return_resource(&(resources->io_head), io_node); - } else { - // it doesn't need any IO - temp_word = 0x0000; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_IO_LIMIT, temp_word); - - return_resource(&(resources->io_head), io_node); - kfree(hold_IO_node); - } - } else { - // it used most of the range - hold_IO_node->next = func->io_head; - func->io_head = hold_IO_node; - } - } else if (hold_IO_node) { - // it used the whole range - hold_IO_node->next = func->io_head; - func->io_head = hold_IO_node; - } - // If we have memory space available and there is some left, - // return the unused portion - if (hold_mem_node && temp_resources.mem_head) { - mem_node = do_pre_bridge_resource_split(&(temp_resources. mem_head), - &hold_mem_node, 0x100000); - - // Check if we were able to split something off - if (mem_node) { - hold_mem_node->base = mem_node->base + mem_node->length; - - temp_word = (hold_mem_node->base) >> 16; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); - - return_resource(&(resources->mem_head), mem_node); - } - - mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000); - - // Check if we were able to split something off - if (mem_node) { - // First use the temporary node to store information for the board - hold_mem_node->length = mem_node->base - hold_mem_node->base; - - if (hold_mem_node->length) { - hold_mem_node->next = func->mem_head; - func->mem_head = hold_mem_node; - - // configure end address - temp_word = (mem_node->base - 1) >> 16; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); - - // Return unused resources to the pool - return_resource(&(resources->mem_head), mem_node); - } else { - // it doesn't need any Mem - temp_word = 0x0000; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); - - return_resource(&(resources->mem_head), mem_node); - kfree(hold_mem_node); - } - } else { - // it used most of the range - hold_mem_node->next = func->mem_head; - func->mem_head = hold_mem_node; - } - } else if (hold_mem_node) { - // it used the whole range - hold_mem_node->next = func->mem_head; - func->mem_head = hold_mem_node; - } - // If we have prefetchable memory space available and there is some - // left at the end, return the unused portion - if (hold_p_mem_node && temp_resources.p_mem_head) { - p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head), - &hold_p_mem_node, 0x100000); - - // Check if we were able to split something off - if (p_mem_node) { - hold_p_mem_node->base = p_mem_node->base + p_mem_node->length; - - temp_word = (hold_p_mem_node->base) >> 16; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); - - return_resource(&(resources->p_mem_head), p_mem_node); - } - - p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000); - - // Check if we were able to split something off - if (p_mem_node) { - // First use the temporary node to store information for the board - hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base; - - // If we used any, add it to the board's list - if (hold_p_mem_node->length) { - hold_p_mem_node->next = func->p_mem_head; - func->p_mem_head = hold_p_mem_node; - - temp_word = (p_mem_node->base - 1) >> 16; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); - - return_resource(&(resources->p_mem_head), p_mem_node); - } else { - // it doesn't need any PMem - temp_word = 0x0000; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); - - return_resource(&(resources->p_mem_head), p_mem_node); - kfree(hold_p_mem_node); - } - } else { - // it used the most of the range - hold_p_mem_node->next = func->p_mem_head; - func->p_mem_head = hold_p_mem_node; - } - } else if (hold_p_mem_node) { - // it used the whole range - hold_p_mem_node->next = func->p_mem_head; - func->p_mem_head = hold_p_mem_node; - } - // We should be configuring an IRQ and the bridge's base address - // registers if it needs them. Although we have never seen such - // a device - - // enable card - command = 0x0157; // = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_COMMAND, command); - - // set Bridge Control Register - command = 0x07; // = PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_NO_ISA - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_BRIDGE_CONTROL, command); - } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) { - // Standard device - rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); - - if (class_code == PCI_BASE_CLASS_DISPLAY) { - // Display (video) adapter (not supported) - return(DEVICE_TYPE_NOT_SUPPORTED); - } - // Figure out IO and memory needs - for (cloop = 0x10; cloop <= 0x24; cloop += 4) { - temp_register = 0xFFFFFFFF; - - dbg("CND: bus=%d, devfn=%d, offset=%d\n", pci_bus->number, devfn, cloop); - rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); - - rc = pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp_register); - dbg("CND: base = 0x%x\n", temp_register); - - if (temp_register) { // If this register is implemented - if ((temp_register & 0x03L) == 0x01) { - // Map IO - - // set base = amount of IO space - base = temp_register & 0xFFFFFFFC; - base = ~base + 1; - - dbg("CND: length = 0x%x\n", base); - io_node = get_io_resource(&(resources->io_head), base); - dbg("Got io_node start = %8.8x, length = %8.8x next (%p)\n", - io_node->base, io_node->length, io_node->next); - dbg("func (%p) io_head (%p)\n", func, func->io_head); - - // allocate the resource to the board - if (io_node) { - base = io_node->base; - - io_node->next = func->io_head; - func->io_head = io_node; - } else - return -ENOMEM; - } else if ((temp_register & 0x0BL) == 0x08) { - // Map prefetchable memory - base = temp_register & 0xFFFFFFF0; - base = ~base + 1; - - dbg("CND: length = 0x%x\n", base); - p_mem_node = get_resource(&(resources->p_mem_head), base); - - // allocate the resource to the board - if (p_mem_node) { - base = p_mem_node->base; - - p_mem_node->next = func->p_mem_head; - func->p_mem_head = p_mem_node; - } else - return -ENOMEM; - } else if ((temp_register & 0x0BL) == 0x00) { - // Map memory - base = temp_register & 0xFFFFFFF0; - base = ~base + 1; - - dbg("CND: length = 0x%x\n", base); - mem_node = get_resource(&(resources->mem_head), base); - - // allocate the resource to the board - if (mem_node) { - base = mem_node->base; - - mem_node->next = func->mem_head; - func->mem_head = mem_node; - } else - return -ENOMEM; - } else if ((temp_register & 0x0BL) == 0x04) { - // Map memory - base = temp_register & 0xFFFFFFF0; - base = ~base + 1; - - dbg("CND: length = 0x%x\n", base); - mem_node = get_resource(&(resources->mem_head), base); - - // allocate the resource to the board - if (mem_node) { - base = mem_node->base; - - mem_node->next = func->mem_head; - func->mem_head = mem_node; - } else - return -ENOMEM; - } else if ((temp_register & 0x0BL) == 0x06) { - // Those bits are reserved, we can't handle this - return(1); - } else { - // Requesting space below 1M - return(NOT_ENOUGH_RESOURCES); - } - - rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, base); - - // Check for 64-bit base - if ((temp_register & 0x07L) == 0x04) { - cloop += 4; - - // Upper 32 bits of address always zero on today's systems - // FIXME this is probably not true on Alpha and ia64??? - base = 0; - rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, base); - } - } - } // End of base register loop - - // Figure out which interrupt pin this function uses - rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_INTERRUPT_PIN, &temp_byte); - - // If this function needs an interrupt and we are behind a bridge - // and the pin is tied to something that's alread mapped, - // set this one the same - if (temp_byte && resources->irqs && - (resources->irqs->valid_INT & - (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) { - // We have to share with something already set up - IRQ = resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03]; - } else { - // Program IRQ based on card type - rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); - - if (class_code == PCI_BASE_CLASS_STORAGE) { - IRQ = cpqhp_disk_irq; - } else { - IRQ = cpqhp_nic_irq; - } - } - - // IRQ Line - rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ); - - if (!behind_bridge) { - rc = cpqhp_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ); - if (rc) - return(1); - } else { - //TBD - this code may also belong in the other clause of this If statement - resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ; - resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03; - } - - // Latency Timer - temp_byte = 0x40; - rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); - - // Cache Line size - temp_byte = 0x08; - rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); - - // disable ROM base Address - temp_dword = 0x00L; - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_ROM_ADDRESS, temp_dword); - - // enable card - temp_word = 0x0157; // = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR - rc = pci_bus_write_config_word (pci_bus, devfn, PCI_COMMAND, temp_word); - } // 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 -Nru a/drivers/hotplug/cpqphp_nvram.c b/drivers/hotplug/cpqphp_nvram.c --- a/drivers/hotplug/cpqphp_nvram.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,667 +0,0 @@ -/* - * 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 - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 ("%s - returned %d\n", __FUNCTION__, result); - return result; -} - - -static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size) -{ - unsigned long flags; - int op = operation; - int ret_val; - - if (!compaq_int15_entry_point) - return -ENODEV; - - spin_lock_irqsave(&int15_lock, flags); - __asm__ ( - "xorl %%ebx,%%ebx\n" \ - "xorl %%edx,%%edx\n" \ - "pushf\n" \ - "push %%cs\n" \ - "cli\n" \ - "call *%6\n" - : "=c" (*buf_size), "=a" (ret_val) - : "a" (op), "c" (*buf_size), "S" (ev_name), - "D" (buffer), "m" (compaq_int15_entry_point) - : "%ebx", "%edx"); - spin_unlock_irqrestore(&int15_lock, flags); - - return((ret_val & 0xFF00) >> 8); -} - - -/* - * load_HRT - * - * Read the hot plug Resource Table from NVRAM - */ -static int load_HRT (void *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, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available); - if (rc) - return(rc); - - // The function Number - rc = add_byte( &pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available); - if (rc) - return(rc); - - // Skip the number of available entries - rc = add_dword( &pFill, 0, &usedbytes, &available); - if (rc) - return(rc); - - // Figure out memory Available - - resNode = ctrl->mem_head; - - loop = 0; - - while (resNode) { - loop ++; - - // base - rc = add_dword( &pFill, resNode->base, &usedbytes, &available); - if (rc) - return(rc); - - // length - rc = add_dword( &pFill, resNode->length, &usedbytes, &available); - if (rc) - return(rc); - - resNode = resNode->next; - } - - // Fill in the number of entries - p_ev_ctrl->mem_avail = loop; - - // Figure out prefetchable memory Available - - resNode = ctrl->p_mem_head; - - loop = 0; - - while (resNode) { - loop ++; - - // base - rc = add_dword( &pFill, resNode->base, &usedbytes, &available); - if (rc) - return(rc); - - // length - rc = add_dword( &pFill, resNode->length, &usedbytes, &available); - if (rc) - return(rc); - - resNode = resNode->next; - } - - // Fill in the number of entries - p_ev_ctrl->p_mem_avail = loop; - - // Figure out IO Available - - resNode = ctrl->io_head; - - loop = 0; - - while (resNode) { - loop ++; - - // base - rc = add_dword( &pFill, resNode->base, &usedbytes, &available); - if (rc) - return(rc); - - // length - rc = add_dword( &pFill, resNode->length, &usedbytes, &available); - if (rc) - return(rc); - - resNode = resNode->next; - } - - // Fill in the number of entries - p_ev_ctrl->io_avail = loop; - - // Figure out bus Available - - resNode = ctrl->bus_head; - - loop = 0; - - while (resNode) { - loop ++; - - // base - rc = add_dword( &pFill, resNode->base, &usedbytes, &available); - if (rc) - return(rc); - - // length - rc = add_dword( &pFill, resNode->length, &usedbytes, &available); - if (rc) - return(rc); - - resNode = resNode->next; - } - - // Fill in the number of entries - p_ev_ctrl->bus_avail = loop; - - ctrl = ctrl->next; - } - - p_EV_header->num_of_ctrl = numCtrl; - - // Now store the EV - - temp_dword = usedbytes; - - rc = access_EV(WRITE_EV, "CQTHPS", (u8*) buffer, &temp_dword); - - dbg("usedbytes = 0x%x, length = 0x%x\n", usedbytes, temp_dword); - - evbuffer_length = temp_dword; - - if (rc) { - err(msg_unable_to_save); - return(1); - } - - return(0); -} - - -void compaq_nvram_init (void *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 != PCI_SLOT(ctrl->pci_dev->devfn)) || - (function != PCI_FUNC(ctrl->pci_dev->devfn))) { - nummem = p_ev_ctrl->mem_avail; - numpmem = p_ev_ctrl->p_mem_avail; - numio = p_ev_ctrl->io_avail; - numbus = p_ev_ctrl->bus_avail; - - p_byte += 4; - - if (p_byte > ((u8*)p_EV_header + evbuffer_length)) - return 2; - - // Skip forward to the next entry - p_byte += (nummem + numpmem + numio + numbus) * 8; - - if (p_byte > ((u8*)p_EV_header + evbuffer_length)) - return 2; - - p_ev_ctrl = (struct ev_hrt_ctrl *) p_byte; - - p_byte += 3; - - if (p_byte > ((u8*)p_EV_header + evbuffer_length)) - return 2; - - bus = p_ev_ctrl->bus; - device = p_ev_ctrl->device; - function = p_ev_ctrl->function; - } - - nummem = p_ev_ctrl->mem_avail; - numpmem = p_ev_ctrl->p_mem_avail; - numio = p_ev_ctrl->io_avail; - numbus = p_ev_ctrl->bus_avail; - - p_byte += 4; - - if (p_byte > ((u8*)p_EV_header + evbuffer_length)) - return 2; - - while (nummem--) { - mem_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); - - if (!mem_node) - break; - - mem_node->base = *(u32*)p_byte; - dbg("mem base = %8.8x\n",mem_node->base); - p_byte += 4; - - if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { - kfree(mem_node); - return 2; - } - - mem_node->length = *(u32*)p_byte; - dbg("mem length = %8.8x\n",mem_node->length); - p_byte += 4; - - if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { - kfree(mem_node); - return 2; - } - - mem_node->next = ctrl->mem_head; - ctrl->mem_head = mem_node; - } - - while (numpmem--) { - p_mem_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); - - if (!p_mem_node) - break; - - p_mem_node->base = *(u32*)p_byte; - dbg("pre-mem base = %8.8x\n",p_mem_node->base); - p_byte += 4; - - if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { - kfree(p_mem_node); - return 2; - } - - p_mem_node->length = *(u32*)p_byte; - dbg("pre-mem length = %8.8x\n",p_mem_node->length); - p_byte += 4; - - if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { - kfree(p_mem_node); - return 2; - } - - p_mem_node->next = ctrl->p_mem_head; - ctrl->p_mem_head = p_mem_node; - } - - while (numio--) { - io_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); - - if (!io_node) - break; - - io_node->base = *(u32*)p_byte; - dbg("io base = %8.8x\n",io_node->base); - p_byte += 4; - - if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { - kfree(io_node); - return 2; - } - - io_node->length = *(u32*)p_byte; - dbg("io length = %8.8x\n",io_node->length); - p_byte += 4; - - if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { - kfree(io_node); - return 2; - } - - io_node->next = ctrl->io_head; - ctrl->io_head = io_node; - } - - while (numbus--) { - bus_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); - - if (!bus_node) - break; - - bus_node->base = *(u32*)p_byte; - p_byte += 4; - - if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { - kfree(bus_node); - return 2; - } - - bus_node->length = *(u32*)p_byte; - p_byte += 4; - - if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { - kfree(bus_node); - return 2; - } - - bus_node->next = ctrl->bus_head; - ctrl->bus_head = bus_node; - } - - // If all of the following fail, we don't have any resources for - // hot plug add - rc = 1; - rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head)); - rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head)); - rc &= cpqhp_resource_sort_and_combine(&(ctrl->io_head)); - rc &= cpqhp_resource_sort_and_combine(&(ctrl->bus_head)); - - if (rc) - return(rc); - } else { - if ((evbuffer[0] != 0) && (!ctrl->push_flag)) - return 1; - } - - return 0; -} - - -int compaq_nvram_store (void *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 -Nru a/drivers/hotplug/cpqphp_nvram.h b/drivers/hotplug/cpqphp_nvram.h --- a/drivers/hotplug/cpqphp_nvram.h Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,57 +0,0 @@ -/* - * 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 - * - */ - -#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 -Nru a/drivers/hotplug/cpqphp_pci.c b/drivers/hotplug/cpqphp_pci.c --- a/drivers/hotplug/cpqphp_pci.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1548 +0,0 @@ -/* - * 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 - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "cpqphp.h" -#include "cpqphp_nvram.h" -#include "../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependent we are... */ - - -u8 cpqhp_nic_irq; -u8 cpqhp_disk_irq; - -static u16 unused_IRQ; - -/* - * detect_HRT_floating_pointer - * - * find the Hot Plug Resource Table in the specified region of memory. - * - */ -static void *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; -} - - -int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) -{ - unsigned char bus; - struct pci_bus *child; - int num; - - if (func->pci_dev == NULL) - func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); - - /* No pci device, we need to create it then */ - if (func->pci_dev == NULL) { - dbg("INFO: pci_dev still null\n"); - - num = pci_scan_slot(ctrl->pci_dev->bus, PCI_DEVFN(func->device, func->function)); - if (num) - pci_bus_add_devices(ctrl->pci_dev->bus); - - func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); - if (func->pci_dev == NULL) { - dbg("ERROR: pci_dev still null\n"); - return 0; - } - } - - if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { - pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); - child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); - pci_do_scan_bus(child); - } - - return 0; -} - - -int cpqhp_unconfigure_device(struct pci_func* func) -{ - int j; - - dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus, func->device, func->function); - - for (j=0; j<8 ; j++) { - struct pci_dev* temp = pci_find_slot(func->bus, (func->device << 3) | j); - if (temp) - pci_remove_bus_device(temp); - } - return 0; -} - -static int PCI_RefinedAccessConfig(struct pci_bus *bus, unsigned int devfn, u8 offset, u32 *value) -{ - u32 vendID = 0; - - if (pci_bus_read_config_dword (bus, devfn, PCI_VENDOR_ID, &vendID) == -1) - return -1; - if (vendID == 0xffffffff) - return -1; - return pci_bus_read_config_dword (bus, devfn, offset, value); -} - - -/* - * cpqhp_set_irq - * - * @bus_num: bus number of PCI device - * @dev_num: device number of PCI device - * @slot: pointer to u8 where slot number will be returned - */ -int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) -{ - int rc; - u16 temp_word; - struct pci_dev fakedev; - struct pci_bus fakebus; - - fakedev.devfn = dev_num << 3; - fakedev.bus = &fakebus; - fakebus.number = bus_num; - dbg("%s: dev %d, bus %d, pin %d, num %d\n", - __FUNCTION__, dev_num, bus_num, int_pin, irq_num); - rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num); - dbg("%s: rc %d\n", __FUNCTION__, rc); - if (!rc) - return !rc; - - // set the Edge Level Control Register (ELCR) - temp_word = inb(0x4d0); - temp_word |= inb(0x4d1) << 8; - - temp_word |= 0x01 << irq_num; - - // This should only be for x86 as it sets the Edge Level Control Register - outb((u8) (temp_word & 0xFF), 0x4d0); - outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1); - - 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; - - ctrl->pci_bus->number = bus_num; - - for (tdevice = 0; tdevice < 0x100; tdevice++) { - //Scan for access first - if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1) - continue; - dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice); - //Yep we got one. Not a bridge ? - if ((work >> 8) != PCI_TO_PCI_BRIDGE_CLASS) { - *dev_num = tdevice; - dbg("found it !\n"); - return 0; - } - } - for (tdevice = 0; tdevice < 0x100; tdevice++) { - //Scan for access first - if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1) - continue; - dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice); - //Yep we got one. bridge ? - if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { - pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(tdevice, 0), PCI_SECONDARY_BUS, &tbus); - dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice); - if (PCI_ScanBusNonBridge(tbus, tdevice) == 0) - return 0; - } - } - - return -1; -} - - -static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge) -{ - struct irq_routing_table *PCIIRQRoutingInfoLength; - long len; - long loop; - u32 work; - - u8 tbus, tdevice, tslot; - - PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table(); - if (!PCIIRQRoutingInfoLength) - return -1; - - len = (PCIIRQRoutingInfoLength->size - - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); - // Make sure I got at least one entry - if (len == 0) { - if (PCIIRQRoutingInfoLength != NULL) - kfree(PCIIRQRoutingInfoLength ); - return -1; - } - - for (loop = 0; loop < len; ++loop) { - tbus = PCIIRQRoutingInfoLength->slots[loop].bus; - tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn; - tslot = PCIIRQRoutingInfoLength->slots[loop].slot; - - if (tslot == slot) { - *bus_num = tbus; - *dev_num = tdevice; - ctrl->pci_bus->number = tbus; - pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_VENDOR_ID, &work); - if (!nobridge || (work == 0xffffffff)) { - if (PCIIRQRoutingInfoLength != NULL) - kfree(PCIIRQRoutingInfoLength ); - return 0; - } - - dbg("bus_num %d devfn %d\n", *bus_num, *dev_num); - pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_CLASS_REVISION, &work); - dbg("work >> 8 (%x) = BRIDGE (%x)\n", work >> 8, PCI_TO_PCI_BRIDGE_CLASS); - - if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { - pci_bus_read_config_byte (ctrl->pci_bus, *dev_num, PCI_SECONDARY_BUS, &tbus); - dbg("Scan bus for Non Bridge: bus %d\n", tbus); - if (PCI_ScanBusForNonBridge(ctrl, tbus, dev_num) == 0) { - *bus_num = tbus; - if (PCIIRQRoutingInfoLength != NULL) - kfree(PCIIRQRoutingInfoLength ); - return 0; - } - } else { - if (PCIIRQRoutingInfoLength != NULL) - kfree(PCIIRQRoutingInfoLength ); - return 0; - } - - } - } - if (PCIIRQRoutingInfoLength != NULL) - kfree(PCIIRQRoutingInfoLength ); - return -1; -} - - -int cpqhp_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 slot) -{ - return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0); //plain (bridges allowed) -} - - -/* More PCI configuration routines; this time centered around hotplug controller */ - - -/* - * cpqhp_save_config - * - * Reads configuration for all slots in a PCI bus and saves info. - * - * Note: For non-hot plug busses, the slot # saved is the device # - * - * returns 0 if success - */ -int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug) -{ - long rc; - u8 class_code; - u8 header_type; - u32 ID; - u8 secondary_bus; - struct pci_func *new_slot; - int sub_bus; - int FirstSupported; - int LastSupported; - int max_functions; - int function; - u8 DevError; - int device = 0; - int cloop = 0; - int stop_it; - int index; - - // Decide which slots are supported - - if (is_hot_plug) { - //********************************* - // is_hot_plug is the slot mask - //********************************* - FirstSupported = is_hot_plug >> 4; - LastSupported = FirstSupported + (is_hot_plug & 0x0F) - 1; - } else { - FirstSupported = 0; - LastSupported = 0x1F; - } - - // Save PCI configuration space for all devices in supported slots - ctrl->pci_bus->number = busnumber; - for (device = FirstSupported; device <= LastSupported; device++) { - ID = 0xFFFFFFFF; - rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID); - - if (ID != 0xFFFFFFFF) { // device in slot - rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code); - if (rc) - return rc; - - rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type); - if (rc) - return rc; - - // If multi-function device, set max_functions to 8 - if (header_type & 0x80) - max_functions = 8; - else - max_functions = 1; - - function = 0; - - do { - DevError = 0; - - if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // P-P Bridge - // Recurse the subordinate bus - // get the subordinate bus number - rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus); - if (rc) { - return rc; - } else { - sub_bus = (int) secondary_bus; - - // Save secondary bus cfg spc - // with this recursive call. - rc = cpqhp_save_config(ctrl, sub_bus, 0); - if (rc) - return rc; - ctrl->pci_bus->number = busnumber; - } - } - - index = 0; - new_slot = cpqhp_slot_find(busnumber, device, index++); - while (new_slot && - (new_slot->function != (u8) function)) - new_slot = cpqhp_slot_find(busnumber, device, index++); - - if (!new_slot) { - // Setup slot structure. - new_slot = cpqhp_slot_create(busnumber); - - if (new_slot == NULL) - return(1); - } - - new_slot->bus = (u8) busnumber; - new_slot->device = (u8) device; - new_slot->function = (u8) function; - new_slot->is_a_board = 1; - new_slot->switch_save = 0x10; - // In case of unsupported board - new_slot->status = DevError; - new_slot->pci_dev = pci_find_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function); - - for (cloop = 0; cloop < 0x20; cloop++) { - rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop])); - if (rc) - return rc; - } - - function++; - - stop_it = 0; - - // this loop skips to the next present function - // reading in Class Code and Header type. - - while ((function < max_functions)&&(!stop_it)) { - rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID); - if (ID == 0xFFFFFFFF) { // nothing there. - function++; - } else { // Something there - rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code); - if (rc) - return rc; - - rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type); - if (rc) - return rc; - - stop_it++; - } - } - - } while (function < max_functions); - } // End of IF (device in slot?) - else if (is_hot_plug) { - // Setup slot structure with entry for empty slot - new_slot = cpqhp_slot_create(busnumber); - - if (new_slot == NULL) { - return(1); - } - - new_slot->bus = (u8) busnumber; - new_slot->device = (u8) device; - new_slot->function = 0; - new_slot->is_a_board = 0; - new_slot->presence_save = 0; - new_slot->switch_save = 0; - } - } // End of FOR loop - - return(0); -} - - -/* - * cpqhp_save_slot_config - * - * Saves configuration info for all PCI devices in a given slot - * including subordinate busses. - * - * returns 0 if success - */ -int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot) -{ - long rc; - u8 class_code; - u8 header_type; - u32 ID; - u8 secondary_bus; - int sub_bus; - int max_functions; - int function; - int cloop = 0; - int stop_it; - - ID = 0xFFFFFFFF; - - ctrl->pci_bus->number = new_slot->bus; - pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID); - - if (ID != 0xFFFFFFFF) { // device in slot - pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code); - pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type); - - if (header_type & 0x80) // Multi-function device - max_functions = 8; - else - max_functions = 1; - - function = 0; - - do { - if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge - // Recurse the subordinate bus - pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus); - - sub_bus = (int) secondary_bus; - - // Save the config headers for the secondary bus. - rc = cpqhp_save_config(ctrl, sub_bus, 0); - if (rc) - return(rc); - ctrl->pci_bus->number = new_slot->bus; - - } // End of IF - - new_slot->status = 0; - - for (cloop = 0; cloop < 0x20; cloop++) { - pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop])); - } - - function++; - - stop_it = 0; - - // this loop skips to the next present function - // reading in the Class Code and the Header type. - - while ((function < max_functions) && (!stop_it)) { - pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID); - - if (ID == 0xFFFFFFFF) { // nothing there. - function++; - } else { // Something there - pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code); - - pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type); - - stop_it++; - } - } - - } while (function < max_functions); - } // End of IF (device in slot?) - else { - return(2); - } - - return(0); -} - - -/* - * cpqhp_save_base_addr_length - * - * Saves the length of all base address registers for the - * specified slot. this is for hot plug REPLACE - * - * returns 0 if success - */ -int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func) -{ - u8 cloop; - u8 header_type; - u8 secondary_bus; - u8 type; - int sub_bus; - u32 temp_register; - u32 base; - u32 rc; - struct pci_func *next; - int index = 0; - struct pci_bus *pci_bus = ctrl->pci_bus; - unsigned int devfn; - - func = cpqhp_slot_find(func->bus, func->device, index++); - - while (func != NULL) { - pci_bus->number = func->bus; - devfn = PCI_DEVFN(func->device, func->function); - - // Check for Bridge - pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); - - if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { - // PCI-PCI Bridge - pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); - - sub_bus = (int) secondary_bus; - - next = cpqhp_slot_list[sub_bus]; - - while (next != NULL) { - rc = cpqhp_save_base_addr_length(ctrl, next); - - if (rc) - return(rc); - - next = next->next; - } - pci_bus->number = func->bus; - - //FIXME: this loop is duplicated in the non-bridge case. The two could be rolled together - // Figure out IO and memory base lengths - for (cloop = 0x10; cloop <= 0x14; cloop += 4) { - temp_register = 0xFFFFFFFF; - pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); - pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); - - if (base) { // If this register is implemented - if (base & 0x01L) { - // IO base - // set base = amount of IO space requested - base = base & 0xFFFFFFFE; - base = (~base) + 1; - - type = 1; - } else { - // memory base - base = base & 0xFFFFFFF0; - base = (~base) + 1; - - type = 0; - } - } else { - base = 0x0L; - type = 0; - } - - // Save information in slot structure - func->base_length[(cloop - 0x10) >> 2] = - base; - func->base_type[(cloop - 0x10) >> 2] = type; - - } // End of base register loop - - - } else if ((header_type & 0x7F) == 0x00) { // PCI-PCI Bridge - // Figure out IO and memory base lengths - for (cloop = 0x10; cloop <= 0x24; cloop += 4) { - temp_register = 0xFFFFFFFF; - pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); - pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); - - if (base) { // If this register is implemented - if (base & 0x01L) { - // IO base - // base = amount of IO space requested - base = base & 0xFFFFFFFE; - base = (~base) + 1; - - type = 1; - } else { - // memory base - // base = amount of memory space requested - base = base & 0xFFFFFFF0; - base = (~base) + 1; - - type = 0; - } - } else { - base = 0x0L; - type = 0; - } - - // Save information in slot structure - func->base_length[(cloop - 0x10) >> 2] = base; - func->base_type[(cloop - 0x10) >> 2] = type; - - } // End of base register loop - - } else { // Some other unknown header type - } - - // find the next device in this slot - func = cpqhp_slot_find(func->bus, func->device, index++); - } - - return(0); -} - - -/* - * cpqhp_save_used_resources - * - * Stores used resource information for existing boards. this is - * for boards that were in the system when this driver was loaded. - * this function is for hot plug ADD - * - * returns 0 if success - */ -int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) -{ - u8 cloop; - u8 header_type; - u8 secondary_bus; - u8 temp_byte; - u8 b_base; - u8 b_length; - u16 command; - u16 save_command; - u16 w_base; - u16 w_length; - u32 temp_register; - u32 save_base; - u32 base; - int index = 0; - struct pci_resource *mem_node; - struct pci_resource *p_mem_node; - struct pci_resource *io_node; - struct pci_resource *bus_node; - struct pci_bus *pci_bus = ctrl->pci_bus; - unsigned int devfn; - - func = cpqhp_slot_find(func->bus, func->device, index++); - - while ((func != NULL) && func->is_a_board) { - pci_bus->number = func->bus; - devfn = PCI_DEVFN(func->device, func->function); - - // Save the command register - pci_bus_read_config_word (pci_bus, devfn, PCI_COMMAND, &save_command); - - // disable card - command = 0x00; - pci_bus_write_config_word (pci_bus, devfn, PCI_COMMAND, command); - - // Check for Bridge - pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); - - if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge - // Clear Bridge Control Register - command = 0x00; - pci_bus_write_config_word (pci_bus, devfn, PCI_BRIDGE_CONTROL, command); - pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); - pci_bus_read_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte); - - bus_node =(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_bus_read_config_byte (pci_bus, devfn, PCI_IO_BASE, &b_base); - pci_bus_read_config_byte (pci_bus, devfn, PCI_IO_LIMIT, &b_length); - - if ((b_base <= b_length) && (save_command & 0x01)) { - io_node = (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_bus_read_config_word (pci_bus, devfn, PCI_MEMORY_BASE, &w_base); - pci_bus_read_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length); - - if ((w_base <= w_length) && (save_command & 0x02)) { - mem_node = (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_bus_read_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base); - pci_bus_read_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length); - - if ((w_base <= w_length) && (save_command & 0x02)) { - p_mem_node = (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_bus_read_config_dword (pci_bus, devfn, cloop, &save_base); - - temp_register = 0xFFFFFFFF; - pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); - pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); - - temp_register = base; - - if (base) { // If this register is implemented - if (((base & 0x03L) == 0x01) - && (save_command & 0x01)) { - // IO base - // set temp_register = amount of IO space requested - temp_register = base & 0xFFFFFFFE; - temp_register = (~temp_register) + 1; - - io_node = (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_bus_read_config_dword (pci_bus, devfn, cloop, &save_base); - - temp_register = 0xFFFFFFFF; - pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); - pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); - - temp_register = base; - - if (base) { // If this register is implemented - if (((base & 0x03L) == 0x01) - && (save_command & 0x01)) { - // IO base - // set temp_register = amount of IO space requested - temp_register = base & 0xFFFFFFFE; - temp_register = (~temp_register) + 1; - - io_node = (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; - struct pci_bus *pci_bus = ctrl->pci_bus; - unsigned int devfn; - - func = cpqhp_slot_find(func->bus, func->device, index++); - - while (func != NULL) { - pci_bus->number = func->bus; - devfn = PCI_DEVFN(func->device, func->function); - - // Start at the top of config space so that the control - // registers are programmed last - for (cloop = 0x3C; cloop > 0; cloop -= 4) { - pci_bus_write_config_dword (pci_bus, devfn, cloop, func->config_space[cloop >> 2]); - } - - pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); - - // If this is a bridge device, restore subordinate devices - if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge - pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); - - sub_bus = (int) secondary_bus; - - next = cpqhp_slot_list[sub_bus]; - - while (next != NULL) { - rc = cpqhp_configure_board(ctrl, next); - - if (rc) - return rc; - - next = next->next; - } - } else { - - // Check all the base Address Registers to make sure - // they are the same. If not, the board is different. - - for (cloop = 16; cloop < 40; cloop += 4) { - pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp); - - if (temp != func->config_space[cloop >> 2]) { - dbg("Config space compare failure!!! offset = %x\n", cloop); - dbg("bus = %x, device = %x, function = %x\n", func->bus, func->device, func->function); - dbg("temp = %x, config space = %x\n\n", temp, func->config_space[cloop >> 2]); - return 1; - } - } - } - - func->configured = 1; - - func = cpqhp_slot_find(func->bus, func->device, index++); - } - - return 0; -} - - -/* - * cpqhp_valid_replace - * - * this function checks to see if a board is the same as the - * one it is replacing. this check will detect if the device's - * vendor or device id's are the same - * - * returns 0 if the board is the same nonzero otherwise - */ -int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func) -{ - u8 cloop; - u8 header_type; - u8 secondary_bus; - u8 type; - u32 temp_register = 0; - u32 base; - u32 rc; - struct pci_func *next; - int index = 0; - struct pci_bus *pci_bus = ctrl->pci_bus; - unsigned int devfn; - - if (!func->is_a_board) - return(ADD_NOT_SUPPORTED); - - func = cpqhp_slot_find(func->bus, func->device, index++); - - while (func != NULL) { - pci_bus->number = func->bus; - devfn = PCI_DEVFN(func->device, func->function); - - pci_bus_read_config_dword (pci_bus, devfn, PCI_VENDOR_ID, &temp_register); - - // No adapter present - if (temp_register == 0xFFFFFFFF) - return(NO_ADAPTER_PRESENT); - - if (temp_register != func->config_space[0]) - return(ADAPTER_NOT_SAME); - - // Check for same revision number and class code - pci_bus_read_config_dword (pci_bus, devfn, PCI_CLASS_REVISION, &temp_register); - - // Adapter not the same - if (temp_register != func->config_space[0x08 >> 2]) - return(ADAPTER_NOT_SAME); - - // Check for Bridge - pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); - - if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge - // In order to continue checking, we must program the - // bus registers in the bridge to respond to accesses - // for it's subordinate bus(es) - - temp_register = func->config_space[0x18 >> 2]; - pci_bus_write_config_dword (pci_bus, devfn, PCI_PRIMARY_BUS, temp_register); - - secondary_bus = (temp_register >> 8) & 0xFF; - - next = cpqhp_slot_list[secondary_bus]; - - while (next != NULL) { - rc = cpqhp_valid_replace(ctrl, next); - - if (rc) - return(rc); - - next = next->next; - } - - } - // Check to see if it is a standard config header - else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) { - // Check subsystem vendor and ID - pci_bus_read_config_dword (pci_bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &temp_register); - - if (temp_register != func->config_space[0x2C >> 2]) { - // If it's a SMART-2 and the register isn't filled - // in, ignore the difference because - // they just have an old rev of the firmware - - if (!((func->config_space[0] == 0xAE100E11) - && (temp_register == 0x00L))) - return(ADAPTER_NOT_SAME); - } - // Figure out IO and memory base lengths - for (cloop = 0x10; cloop <= 0x24; cloop += 4) { - temp_register = 0xFFFFFFFF; - pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); - pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); - if (base) { // If this register is implemented - if (base & 0x01L) { - // IO base - // set base = amount of IO space requested - base = base & 0xFFFFFFFE; - base = (~base) + 1; - - type = 1; - } else { - // memory base - base = base & 0xFFFFFFF0; - base = (~base) + 1; - - type = 0; - } - } else { - base = 0x0L; - type = 0; - } - - // Check information in slot structure - if (func->base_length[(cloop - 0x10) >> 2] != base) - return(ADAPTER_NOT_SAME); - - if (func->base_type[(cloop - 0x10) >> 2] != type) - return(ADAPTER_NOT_SAME); - - } // End of base register loop - - } // End of (type 0 config space) else - else { - // this is not a type 0 or 1 config space header so - // we don't know how to do it - return(DEVICE_TYPE_NOT_SUPPORTED); - } - - // Get the next function - func = cpqhp_slot_find(func->bus, func->device, index++); - } - - - return(0); -} - - -/* - * cpqhp_find_available_resources - * - * Finds available memory, IO, and IRQ resources for programming - * devices which may be added to the system - * this function is for hot plug ADD! - * - * returns 0 if success - */ -int cpqhp_find_available_resources (struct controller *ctrl, void *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->cfgspc_irq; - } - - if (!cpqhp_disk_irq) { - cpqhp_disk_irq = ctrl->cfgspc_irq; - } - - dbg("cpqhp_disk_irq, cpqhp_nic_irq= %d, %d\n", cpqhp_disk_irq, cpqhp_nic_irq); - - rc = compaq_nvram_load(rom_start, ctrl); - if (rc) - return rc; - - one_slot = rom_resource_table + sizeof (struct hrt); - - i = readb(rom_resource_table + NUMBER_OF_ENTRIES); - dbg("number_of_entries = %d\n", i); - - if (!readb(one_slot + SECONDARY_BUS)) { - return(1); - } - - dbg("dev|IO base|length|Mem base|length|Pre base|length|PB SB MB\n"); - - while (i && readb(one_slot + SECONDARY_BUS)) { - u8 dev_func = readb(one_slot + DEV_FUNC); - u8 primary_bus = readb(one_slot + PRIMARY_BUS); - u8 secondary_bus = readb(one_slot + SECONDARY_BUS); - u8 max_bus = readb(one_slot + MAX_BUS); - u16 io_base = readw(one_slot + IO_BASE); - u16 io_length = readw(one_slot + IO_LENGTH); - u16 mem_base = readw(one_slot + MEM_BASE); - u16 mem_length = readw(one_slot + MEM_LENGTH); - u16 pre_mem_base = readw(one_slot + PRE_MEM_BASE); - u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH); - - dbg("%2.2x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x |%2.2x %2.2x %2.2x\n", - dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length, - primary_bus, secondary_bus, max_bus); - - // If this entry isn't for our controller's bus, ignore it - if (primary_bus != ctrl->bus) { - i--; - one_slot += sizeof (struct slot_rt); - continue; - } - // find out if this entry is for an occupied slot - ctrl->pci_bus->number = primary_bus; - pci_bus_read_config_dword (ctrl->pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword); - dbg("temp_D_word = %x\n", temp_dword); - - if (temp_dword != 0xFFFFFFFF) { - index = 0; - func = cpqhp_slot_find(primary_bus, dev_func >> 3, 0); - - while (func && (func->function != (dev_func & 0x07))) { - dbg("func = %p (bus, dev, fun) = (%d, %d, %d)\n", func, primary_bus, dev_func >> 3, index); - func = cpqhp_slot_find(primary_bus, dev_func >> 3, index++); - } - - // If we can't find a match, skip this table entry - if (!func) { - i--; - one_slot += sizeof (struct slot_rt); - continue; - } - // this may not work and shouldn't be used - if (secondary_bus != primary_bus) - bridged_slot = 1; - else - bridged_slot = 0; - - populated_slot = 1; - } else { - populated_slot = 0; - bridged_slot = 0; - } - - - // If we've got a valid IO base, use it - - temp_dword = io_base + io_length; - - if ((io_base) && (temp_dword < 0x10000)) { - io_node = (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("%s\n", __FUNCTION__); - - if (!func) - return(1); - - node = func->io_head; - func->io_head = NULL; - while (node) { - t_node = node->next; - return_resource(&(resources->io_head), node); - node = t_node; - } - - node = func->mem_head; - func->mem_head = NULL; - while (node) { - t_node = node->next; - return_resource(&(resources->mem_head), node); - node = t_node; - } - - node = func->p_mem_head; - func->p_mem_head = NULL; - while (node) { - t_node = node->next; - return_resource(&(resources->p_mem_head), node); - node = t_node; - } - - node = func->bus_head; - func->bus_head = NULL; - while (node) { - t_node = node->next; - return_resource(&(resources->bus_head), node); - node = t_node; - } - - rc |= cpqhp_resource_sort_and_combine(&(resources->mem_head)); - rc |= cpqhp_resource_sort_and_combine(&(resources->p_mem_head)); - rc |= cpqhp_resource_sort_and_combine(&(resources->io_head)); - rc |= cpqhp_resource_sort_and_combine(&(resources->bus_head)); - - return(rc); -} - - -/* - * cpqhp_destroy_resource_list - * - * Puts node back in the resource list pointed to by head - */ -void cpqhp_destroy_resource_list (struct resource_lists * resources) -{ - struct pci_resource *res, *tres; - - res = resources->io_head; - resources->io_head = NULL; - - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - res = resources->mem_head; - resources->mem_head = NULL; - - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - res = resources->p_mem_head; - resources->p_mem_head = NULL; - - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - res = resources->bus_head; - resources->bus_head = NULL; - - while (res) { - tres = res; - res = res->next; - kfree(tres); - } -} - - -/* - * cpqhp_destroy_board_resources - * - * Puts node back in the resource list pointed to by head - */ -void cpqhp_destroy_board_resources (struct pci_func * func) -{ - struct pci_resource *res, *tres; - - res = func->io_head; - func->io_head = NULL; - - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - res = func->mem_head; - func->mem_head = NULL; - - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - res = func->p_mem_head; - func->p_mem_head = NULL; - - while (res) { - tres = res; - res = res->next; - kfree(tres); - } - - res = func->bus_head; - func->bus_head = NULL; - - while (res) { - tres = res; - res = res->next; - kfree(tres); - } -} - diff -Nru a/drivers/hotplug/cpqphp_sysfs.c b/drivers/hotplug/cpqphp_sysfs.c --- a/drivers/hotplug/cpqphp_sysfs.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,143 +0,0 @@ -/* - * Compaq Hot Plug Controller Driver - * - * Copyright (c) 1995,2001 Compaq Computer Corporation - * Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001 IBM Corp. - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "cpqphp.h" - - -/* A few routines that create sysfs entries for the hot plug controller */ - -static int show_ctrl (struct device *dev, char *buf) -{ - struct pci_dev *pci_dev; - struct controller *ctrl; - char * out = buf; - int index; - struct pci_resource *res; - - pci_dev = container_of (dev, struct pci_dev, dev); - ctrl = pci_get_drvdata(pci_dev); - - out += sprintf(buf, "Free resources: memory\n"); - index = 11; - res = ctrl->mem_head; - while (res && index--) { - out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); - res = res->next; - } - out += sprintf(out, "Free resources: prefetchable memory\n"); - index = 11; - res = ctrl->p_mem_head; - while (res && index--) { - out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); - res = res->next; - } - out += sprintf(out, "Free resources: IO\n"); - index = 11; - res = ctrl->io_head; - while (res && index--) { - out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); - res = res->next; - } - out += sprintf(out, "Free resources: bus numbers\n"); - index = 11; - res = ctrl->bus_head; - while (res && index--) { - out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); - res = res->next; - } - - return out - buf; -} -static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL); - -static int show_dev (struct device *dev, char *buf) -{ - struct pci_dev *pci_dev; - struct controller *ctrl; - char * out = buf; - int index; - struct pci_resource *res; - struct pci_func *new_slot; - struct slot *slot; - - pci_dev = container_of (dev, struct pci_dev, dev); - ctrl = pci_get_drvdata(pci_dev); - - slot=ctrl->slot; - - while (slot) { - new_slot = cpqhp_slot_find(slot->bus, slot->device, 0); - if (!new_slot) - break; - out += sprintf(out, "assigned resources: memory\n"); - index = 11; - res = new_slot->mem_head; - while (res && index--) { - out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); - res = res->next; - } - out += sprintf(out, "assigned resources: prefetchable memory\n"); - index = 11; - res = new_slot->p_mem_head; - while (res && index--) { - out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); - res = res->next; - } - out += sprintf(out, "assigned resources: IO\n"); - index = 11; - res = new_slot->io_head; - while (res && index--) { - out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); - res = res->next; - } - out += sprintf(out, "assigned resources: bus numbers\n"); - index = 11; - res = new_slot->bus_head; - while (res && index--) { - out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); - res = res->next; - } - slot=slot->next; - } - - return out - buf; -} -static DEVICE_ATTR (dev, S_IRUGO, show_dev, NULL); - -void cpqhp_create_ctrl_files (struct controller *ctrl) -{ - device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl); - device_create_file (&ctrl->pci_dev->dev, &dev_attr_dev); -} diff -Nru a/drivers/hotplug/ibmphp.h b/drivers/hotplug/ibmphp.h --- a/drivers/hotplug/ibmphp.h Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,772 +0,0 @@ -#ifndef __IBMPHP_H -#define __IBMPHP_H - -/* - * IBM Hot Plug Controller Driver - * - * Written By: Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation - * - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001,2002 IBM Corp. - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - * - */ - -#include "pci_hotplug.h" - -extern int ibmphp_debug; - -#if !defined(CONFIG_HOTPLUG_PCI_IBM_MODULE) - #define MY_NAME "ibmphpd" -#else - #define MY_NAME THIS_MODULE->name -#endif -#define debug(fmt, arg...) do { if (ibmphp_debug == 1) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0) -#define debug_pci(fmt, arg...) do { if (ibmphp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0) -#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) -#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) - - -/* EBDA stuff */ - -/*********************************************************** -* SLOT CAPABILITY * -***********************************************************/ - -#define EBDA_SLOT_133_MAX 0x20 -#define EBDA_SLOT_100_MAX 0x10 -#define EBDA_SLOT_66_MAX 0x02 -#define EBDA_SLOT_PCIX_CAP 0x08 - - -/************************************************************ -* RESOURE TYPE * -************************************************************/ - -#define EBDA_RSRC_TYPE_MASK 0x03 -#define EBDA_IO_RSRC_TYPE 0x00 -#define EBDA_MEM_RSRC_TYPE 0x01 -#define EBDA_PFM_RSRC_TYPE 0x03 -#define EBDA_RES_RSRC_TYPE 0x02 - - -/************************************************************* -* IO RESTRICTION TYPE * -*************************************************************/ - -#define EBDA_IO_RESTRI_MASK 0x0c -#define EBDA_NO_RESTRI 0x00 -#define EBDA_AVO_VGA_ADDR 0x04 -#define EBDA_AVO_VGA_ADDR_AND_ALIA 0x08 -#define EBDA_AVO_ISA_ADDR 0x0c - - -/************************************************************** -* DEVICE TYPE DEF * -**************************************************************/ - -#define EBDA_DEV_TYPE_MASK 0x10 -#define EBDA_PCI_DEV 0x10 -#define EBDA_NON_PCI_DEV 0x00 - - -/*************************************************************** -* PRIMARY DEF DEFINITION * -***************************************************************/ - -#define EBDA_PRI_DEF_MASK 0x20 -#define EBDA_PRI_PCI_BUS_INFO 0x20 -#define EBDA_NORM_DEV_RSRC_INFO 0x00 - - -//-------------------------------------------------------------- -// RIO TABLE DATA STRUCTURE -//-------------------------------------------------------------- - -struct rio_table_hdr { - u8 ver_num; - u8 scal_count; - u8 riodev_count; - u16 offset; -}; - -//------------------------------------------------------------- -// SCALABILITY DETAIL -//------------------------------------------------------------- - -struct scal_detail { - u8 node_id; - u32 cbar; - u8 port0_node_connect; - u8 port0_port_connect; - u8 port1_node_connect; - u8 port1_port_connect; - u8 port2_node_connect; - u8 port2_port_connect; - u8 chassis_num; -// struct list_head scal_detail_list; -}; - -//-------------------------------------------------------------- -// RIO DETAIL -//-------------------------------------------------------------- - -struct rio_detail { - u8 rio_node_id; - u32 bbar; - u8 rio_type; - u8 owner_id; - u8 port0_node_connect; - u8 port0_port_connect; - u8 port1_node_connect; - u8 port1_port_connect; - u8 first_slot_num; - u8 status; - u8 wpindex; - u8 chassis_num; - struct list_head rio_detail_list; -}; - -struct opt_rio { - u8 rio_type; - u8 chassis_num; - u8 first_slot_num; - u8 middle_num; - struct list_head opt_rio_list; -}; - -struct opt_rio_lo { - u8 rio_type; - u8 chassis_num; - u8 first_slot_num; - u8 middle_num; - u8 pack_count; - struct list_head opt_rio_lo_list; -}; - -/**************************************************************** -* HPC DESCRIPTOR NODE * -****************************************************************/ - -struct ebda_hpc_list { - u8 format; - u16 num_ctlrs; - short phys_addr; -// struct list_head ebda_hpc_list; -}; -/***************************************************************** -* IN HPC DATA STRUCTURE, THE ASSOCIATED SLOT AND BUS * -* STRUCTURE * -*****************************************************************/ - -struct ebda_hpc_slot { - u8 slot_num; - u32 slot_bus_num; - u8 ctl_index; - u8 slot_cap; -}; - -struct ebda_hpc_bus { - u32 bus_num; - u8 slots_at_33_conv; - u8 slots_at_66_conv; - u8 slots_at_66_pcix; - u8 slots_at_100_pcix; - u8 slots_at_133_pcix; -}; - - -/******************************************************************** -* THREE TYPE OF HOT PLUG CONTROLER * -********************************************************************/ - -struct isa_ctlr_access { - u16 io_start; - u16 io_end; -}; - -struct pci_ctlr_access { - u8 bus; - u8 dev_fun; -}; - -struct wpeg_i2c_ctlr_access { - ulong wpegbbar; - u8 i2c_addr; -}; - -#define HPC_DEVICE_ID 0x0246 -#define HPC_SUBSYSTEM_ID 0x0247 -#define HPC_PCI_OFFSET 0x40 -/************************************************************************* -* RSTC DESCRIPTOR NODE * -*************************************************************************/ - -struct ebda_rsrc_list { - u8 format; - u16 num_entries; - u16 phys_addr; - struct ebda_rsrc_list *next; -}; - - -/*************************************************************************** -* PCI RSRC NODE * -***************************************************************************/ - -struct ebda_pci_rsrc { - u8 rsrc_type; - u8 bus_num; - u8 dev_fun; - u32 start_addr; - u32 end_addr; - u8 marked; /* for NVRAM */ - struct list_head ebda_pci_rsrc_list; -}; - - -/*********************************************************** -* BUS_INFO DATE STRUCTURE * -***********************************************************/ - -struct bus_info { - u8 slot_min; - u8 slot_max; - u8 slot_count; - u8 busno; - u8 controller_id; - u8 current_speed; - u8 current_bus_mode; - u8 index; - u8 slots_at_33_conv; - u8 slots_at_66_conv; - u8 slots_at_66_pcix; - u8 slots_at_100_pcix; - u8 slots_at_133_pcix; - struct list_head bus_info_list; -}; - - -/*********************************************************** -* GLOBAL VARIABLES * -***********************************************************/ -extern struct list_head ibmphp_ebda_pci_rsrc_head; -extern struct list_head ibmphp_slot_head; -extern struct list_head ibmphp_res_head; -/*********************************************************** -* FUNCTION PROTOTYPES * -***********************************************************/ - -extern void ibmphp_free_ebda_hpc_queue (void); -extern int ibmphp_access_ebda (void); -extern struct slot *ibmphp_get_slot_from_physical_num (u8); -extern int ibmphp_get_total_hp_slots (void); -extern void ibmphp_free_ibm_slot (struct slot *); -extern void ibmphp_free_bus_info_queue (void); -extern void ibmphp_free_ebda_pci_rsrc_queue (void); -extern struct bus_info *ibmphp_find_same_bus_num (u32); -extern int ibmphp_get_bus_index (u8); -extern u16 ibmphp_get_total_controllers (void); -extern int ibmphp_register_pci (void); - -/* passed parameters */ -#define MEM 0 -#define IO 1 -#define PFMEM 2 - -/* bit masks */ -#define RESTYPE 0x03 -#define IOMASK 0x00 /* will need to take its complement */ -#define MMASK 0x01 -#define PFMASK 0x03 -#define PCIDEVMASK 0x10 /* we should always have PCI devices */ -#define PRIMARYBUSMASK 0x20 - -/* pci specific defines */ -#define PCI_VENDOR_ID_NOTVALID 0xFFFF -#define PCI_HEADER_TYPE_MULTIDEVICE 0x80 -#define PCI_HEADER_TYPE_MULTIBRIDGE 0x81 - -#define LATENCY 0x64 -#define CACHE 64 -#define DEVICEENABLE 0x015F /* CPQ has 0x0157 */ - -#define IOBRIDGE 0x1000 /* 4k */ -#define MEMBRIDGE 0x100000 /* 1M */ - -/* irqs */ -#define SCSI_IRQ 0x09 -#define LAN_IRQ 0x0A -#define OTHER_IRQ 0x0B - -/* Data Structures */ - -/* type is of the form x x xx xx - * | | | |_ 00 - I/O, 01 - Memory, 11 - PFMemory - * | | - 00 - No Restrictions, 01 - Avoid VGA, 10 - Avoid - * | | VGA and their aliases, 11 - Avoid ISA - * | - 1 - PCI device, 0 - non pci device - * - 1 - Primary PCI Bus Information (0 if Normal device) - * the IO restrictions [2:3] are only for primary buses - */ - - -/* we need this struct because there could be several resource blocks - * allocated per primary bus in the EBDA - */ -struct range_node { - int rangeno; - u32 start; - u32 end; - struct range_node *next; -}; - -struct bus_node { - u8 busno; - int noIORanges; - struct range_node *rangeIO; - int noMemRanges; - struct range_node *rangeMem; - int noPFMemRanges; - struct range_node *rangePFMem; - int needIOUpdate; - int needMemUpdate; - int needPFMemUpdate; - struct resource_node *firstIO; /* first IO resource on the Bus */ - struct resource_node *firstMem; /* first memory resource on the Bus */ - struct resource_node *firstPFMem; /* first prefetchable memory resource on the Bus */ - struct resource_node *firstPFMemFromMem; /* when run out of pfmem available, taking from Mem */ - struct list_head bus_list; -}; - -struct resource_node { - int rangeno; - u8 busno; - u8 devfunc; - u32 start; - u32 end; - u32 len; - int type; /* MEM, IO, PFMEM */ - u8 fromMem; /* this is to indicate that the range is from - * from the Memory bucket rather than from PFMem */ - struct resource_node *next; - struct resource_node *nextRange; /* for the other mem range on bus */ -}; - -struct res_needed { - u32 mem; - u32 pfmem; - u32 io; - u8 not_correct; /* needed for return */ - int devices[32]; /* for device numbers behind this bridge */ -}; - -/* functions */ - -extern int ibmphp_rsrc_init (void); -extern int ibmphp_add_resource (struct resource_node *); -extern int ibmphp_remove_resource (struct resource_node *); -extern int ibmphp_find_resource (struct bus_node *, u32, struct resource_node **, int); -extern int ibmphp_check_resource (struct resource_node *, u8); -extern int ibmphp_remove_bus (struct bus_node *, u8); -extern void ibmphp_free_resources (void); -extern int ibmphp_add_pfmem_from_mem (struct resource_node *); -extern struct bus_node *ibmphp_find_res_bus (u8); -extern void ibmphp_print_test (void); /* for debugging purposes */ - -extern void ibmphp_hpc_initvars (void); -extern int ibmphp_hpc_readslot (struct slot *, u8, u8 *); -extern int ibmphp_hpc_writeslot (struct slot *, u8); -extern void ibmphp_lock_operations (void); -extern void ibmphp_unlock_operations (void); -extern int ibmphp_hpc_fillhpslotinfo (struct hotplug_slot *); -extern int ibmphp_hpc_start_poll_thread (void); -extern void ibmphp_hpc_stop_poll_thread (void); - -//---------------------------------------------------------------------------- - - -//---------------------------------------------------------------------------- -// HPC return codes -//---------------------------------------------------------------------------- -#define FALSE 0x00 -#define TRUE 0x01 -#define HPC_ERROR 0xFF - -//----------------------------------------------------------------------------- -// BUS INFO -//----------------------------------------------------------------------------- -#define BUS_SPEED 0x30 -#define BUS_MODE 0x40 -#define BUS_MODE_PCIX 0x01 -#define BUS_MODE_PCI 0x00 -#define BUS_SPEED_2 0x20 -#define BUS_SPEED_1 0x10 -#define BUS_SPEED_33 0x00 -#define BUS_SPEED_66 0x01 -#define BUS_SPEED_100 0x02 -#define BUS_SPEED_133 0x03 -#define BUS_SPEED_66PCIX 0x04 -#define BUS_SPEED_66UNKNOWN 0x05 -#define BUS_STATUS_AVAILABLE 0x01 -#define BUS_CONTROL_AVAILABLE 0x02 -#define SLOT_LATCH_REGS_SUPPORTED 0x10 - -#define PRGM_MODEL_REV_LEVEL 0xF0 -#define MAX_ADAPTER_NONE 0x09 - -//---------------------------------------------------------------------------- -// HPC 'write' operations/commands -//---------------------------------------------------------------------------- -// Command Code State Write to reg -// Machine at index -//------------------------- ---- ------- ------------ -#define HPC_CTLR_ENABLEIRQ 0x00 // N 15 -#define HPC_CTLR_DISABLEIRQ 0x01 // N 15 -#define HPC_SLOT_OFF 0x02 // Y 0-14 -#define HPC_SLOT_ON 0x03 // Y 0-14 -#define HPC_SLOT_ATTNOFF 0x04 // N 0-14 -#define HPC_SLOT_ATTNON 0x05 // N 0-14 -#define HPC_CTLR_CLEARIRQ 0x06 // N 15 -#define HPC_CTLR_RESET 0x07 // Y 15 -#define HPC_CTLR_IRQSTEER 0x08 // N 15 -#define HPC_BUS_33CONVMODE 0x09 // Y 31-34 -#define HPC_BUS_66CONVMODE 0x0A // Y 31-34 -#define HPC_BUS_66PCIXMODE 0x0B // Y 31-34 -#define HPC_BUS_100PCIXMODE 0x0C // Y 31-34 -#define HPC_BUS_133PCIXMODE 0x0D // Y 31-34 -#define HPC_ALLSLOT_OFF 0x11 // Y 15 -#define HPC_ALLSLOT_ON 0x12 // Y 15 -#define HPC_SLOT_BLINKLED 0x13 // N 0-14 - -//---------------------------------------------------------------------------- -// read commands -//---------------------------------------------------------------------------- -#define READ_SLOTSTATUS 0x01 -#define READ_EXTSLOTSTATUS 0x02 -#define READ_BUSSTATUS 0x03 -#define READ_CTLRSTATUS 0x04 -#define READ_ALLSTAT 0x05 -#define READ_ALLSLOT 0x06 -#define READ_SLOTLATCHLOWREG 0x07 -#define READ_REVLEVEL 0x08 -#define READ_HPCOPTIONS 0x09 -//---------------------------------------------------------------------------- -// slot status -//---------------------------------------------------------------------------- -#define HPC_SLOT_POWER 0x01 -#define HPC_SLOT_CONNECT 0x02 -#define HPC_SLOT_ATTN 0x04 -#define HPC_SLOT_PRSNT2 0x08 -#define HPC_SLOT_PRSNT1 0x10 -#define HPC_SLOT_PWRGD 0x20 -#define HPC_SLOT_BUS_SPEED 0x40 -#define HPC_SLOT_LATCH 0x80 - -//---------------------------------------------------------------------------- -// HPC_SLOT_POWER status return codes -//---------------------------------------------------------------------------- -#define HPC_SLOT_POWER_OFF 0x00 -#define HPC_SLOT_POWER_ON 0x01 - -//---------------------------------------------------------------------------- -// HPC_SLOT_CONNECT status return codes -//---------------------------------------------------------------------------- -#define HPC_SLOT_CONNECTED 0x00 -#define HPC_SLOT_DISCONNECTED 0x01 - -//---------------------------------------------------------------------------- -// HPC_SLOT_ATTN status return codes -//---------------------------------------------------------------------------- -#define HPC_SLOT_ATTN_OFF 0x00 -#define HPC_SLOT_ATTN_ON 0x01 -#define HPC_SLOT_ATTN_BLINK 0x02 - -//---------------------------------------------------------------------------- -// HPC_SLOT_PRSNT status return codes -//---------------------------------------------------------------------------- -#define HPC_SLOT_EMPTY 0x00 -#define HPC_SLOT_PRSNT_7 0x01 -#define HPC_SLOT_PRSNT_15 0x02 -#define HPC_SLOT_PRSNT_25 0x03 - -//---------------------------------------------------------------------------- -// HPC_SLOT_PWRGD status return codes -//---------------------------------------------------------------------------- -#define HPC_SLOT_PWRGD_FAULT_NONE 0x00 -#define HPC_SLOT_PWRGD_GOOD 0x01 - -//---------------------------------------------------------------------------- -// HPC_SLOT_BUS_SPEED status return codes -//---------------------------------------------------------------------------- -#define HPC_SLOT_BUS_SPEED_OK 0x00 -#define HPC_SLOT_BUS_SPEED_MISM 0x01 - -//---------------------------------------------------------------------------- -// HPC_SLOT_LATCH status return codes -//---------------------------------------------------------------------------- -#define HPC_SLOT_LATCH_OPEN 0x01 // NOTE : in PCI spec bit off = open -#define HPC_SLOT_LATCH_CLOSED 0x00 // NOTE : in PCI spec bit on = closed - - -//---------------------------------------------------------------------------- -// extended slot status -//---------------------------------------------------------------------------- -#define HPC_SLOT_PCIX 0x01 -#define HPC_SLOT_SPEED1 0x02 -#define HPC_SLOT_SPEED2 0x04 -#define HPC_SLOT_BLINK_ATTN 0x08 -#define HPC_SLOT_RSRVD1 0x10 -#define HPC_SLOT_RSRVD2 0x20 -#define HPC_SLOT_BUS_MODE 0x40 -#define HPC_SLOT_RSRVD3 0x80 - -//---------------------------------------------------------------------------- -// HPC_XSLOT_PCIX_CAP status return codes -//---------------------------------------------------------------------------- -#define HPC_SLOT_PCIX_NO 0x00 -#define HPC_SLOT_PCIX_YES 0x01 - -//---------------------------------------------------------------------------- -// HPC_XSLOT_SPEED status return codes -//---------------------------------------------------------------------------- -#define HPC_SLOT_SPEED_33 0x00 -#define HPC_SLOT_SPEED_66 0x01 -#define HPC_SLOT_SPEED_133 0x02 - -//---------------------------------------------------------------------------- -// HPC_XSLOT_ATTN_BLINK status return codes -//---------------------------------------------------------------------------- -#define HPC_SLOT_ATTN_BLINK_OFF 0x00 -#define HPC_SLOT_ATTN_BLINK_ON 0x01 - -//---------------------------------------------------------------------------- -// HPC_XSLOT_BUS_MODE status return codes -//---------------------------------------------------------------------------- -#define HPC_SLOT_BUS_MODE_OK 0x00 -#define HPC_SLOT_BUS_MODE_MISM 0x01 - -//---------------------------------------------------------------------------- -// Controller status -//---------------------------------------------------------------------------- -#define HPC_CTLR_WORKING 0x01 -#define HPC_CTLR_FINISHED 0x02 -#define HPC_CTLR_RESULT0 0x04 -#define HPC_CTLR_RESULT1 0x08 -#define HPC_CTLR_RESULE2 0x10 -#define HPC_CTLR_RESULT3 0x20 -#define HPC_CTLR_IRQ_ROUTG 0x40 -#define HPC_CTLR_IRQ_PENDG 0x80 - -//---------------------------------------------------------------------------- -// HPC_CTLR_WROKING status return codes -//---------------------------------------------------------------------------- -#define HPC_CTLR_WORKING_NO 0x00 -#define HPC_CTLR_WORKING_YES 0x01 - -//---------------------------------------------------------------------------- -// HPC_CTLR_FINISHED status return codes -//---------------------------------------------------------------------------- -#define HPC_CTLR_FINISHED_NO 0x00 -#define HPC_CTLR_FINISHED_YES 0x01 - -//---------------------------------------------------------------------------- -// HPC_CTLR_RESULT status return codes -//---------------------------------------------------------------------------- -#define HPC_CTLR_RESULT_SUCCESS 0x00 -#define HPC_CTLR_RESULT_FAILED 0x01 -#define HPC_CTLR_RESULT_RSVD 0x02 -#define HPC_CTLR_RESULT_NORESP 0x03 - - -//---------------------------------------------------------------------------- -// macro for slot info -//---------------------------------------------------------------------------- -#define SLOT_POWER(s) ((u8) ((s & HPC_SLOT_POWER) \ - ? HPC_SLOT_POWER_ON : HPC_SLOT_POWER_OFF)) - -#define SLOT_CONNECT(s) ((u8) ((s & HPC_SLOT_CONNECT) \ - ? HPC_SLOT_DISCONNECTED : HPC_SLOT_CONNECTED)) - -#define SLOT_ATTN(s,es) ((u8) ((es & HPC_SLOT_BLINK_ATTN) \ - ? HPC_SLOT_ATTN_BLINK \ - : ((s & HPC_SLOT_ATTN) ? HPC_SLOT_ATTN_ON : HPC_SLOT_ATTN_OFF))) - -#define SLOT_PRESENT(s) ((u8) ((s & HPC_SLOT_PRSNT1) \ - ? ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_EMPTY : HPC_SLOT_PRSNT_15) \ - : ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_PRSNT_25 : HPC_SLOT_PRSNT_7))) - -#define SLOT_PWRGD(s) ((u8) ((s & HPC_SLOT_PWRGD) \ - ? HPC_SLOT_PWRGD_GOOD : HPC_SLOT_PWRGD_FAULT_NONE)) - -#define SLOT_BUS_SPEED(s) ((u8) ((s & HPC_SLOT_BUS_SPEED) \ - ? HPC_SLOT_BUS_SPEED_MISM : HPC_SLOT_BUS_SPEED_OK)) - -#define SLOT_LATCH(s) ((u8) ((s & HPC_SLOT_LATCH) \ - ? HPC_SLOT_LATCH_CLOSED : HPC_SLOT_LATCH_OPEN)) - -#define SLOT_PCIX(es) ((u8) ((es & HPC_SLOT_PCIX) \ - ? HPC_SLOT_PCIX_YES : HPC_SLOT_PCIX_NO)) - -#define SLOT_SPEED(es) ((u8) ((es & HPC_SLOT_SPEED2) \ - ? ((es & HPC_SLOT_SPEED1) ? HPC_SLOT_SPEED_133 \ - : HPC_SLOT_SPEED_66) \ - : HPC_SLOT_SPEED_33)) - -#define SLOT_BUS_MODE(es) ((u8) ((es & HPC_SLOT_BUS_MODE) \ - ? HPC_SLOT_BUS_MODE_MISM : HPC_SLOT_BUS_MODE_OK)) - -//-------------------------------------------------------------------------- -// macro for bus info -//--------------------------------------------------------------------------- -#define CURRENT_BUS_SPEED(s) ((u8) (s & BUS_SPEED_2) \ - ? ((s & BUS_SPEED_1) ? BUS_SPEED_133 : BUS_SPEED_100) \ - : ((s & BUS_SPEED_1) ? BUS_SPEED_66 : BUS_SPEED_33)) - -#define CURRENT_BUS_MODE(s) ((u8) (s & BUS_MODE) ? BUS_MODE_PCIX : BUS_MODE_PCI) - -#define READ_BUS_STATUS(s) ((u8) (s->options & BUS_STATUS_AVAILABLE)) - -#define READ_BUS_MODE(s) ((s->revision & PRGM_MODEL_REV_LEVEL) >= 0x20) - -#define SET_BUS_STATUS(s) ((u8) (s->options & BUS_CONTROL_AVAILABLE)) - -#define READ_SLOT_LATCH(s) ((u8) (s->options & SLOT_LATCH_REGS_SUPPORTED)) - -//---------------------------------------------------------------------------- -// macro for controller info -//---------------------------------------------------------------------------- -#define CTLR_WORKING(c) ((u8) ((c & HPC_CTLR_WORKING) \ - ? HPC_CTLR_WORKING_YES : HPC_CTLR_WORKING_NO)) -#define CTLR_FINISHED(c) ((u8) ((c & HPC_CTLR_FINISHED) \ - ? HPC_CTLR_FINISHED_YES : HPC_CTLR_FINISHED_NO)) -#define CTLR_RESULT(c) ((u8) ((c & HPC_CTLR_RESULT1) \ - ? ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_NORESP \ - : HPC_CTLR_RESULT_RSVD) \ - : ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_FAILED \ - : HPC_CTLR_RESULT_SUCCESS))) - -// command that affect the state machine of HPC -#define NEEDTOCHECK_CMDSTATUS(c) ((c == HPC_SLOT_OFF) || \ - (c == HPC_SLOT_ON) || \ - (c == HPC_CTLR_RESET) || \ - (c == HPC_BUS_33CONVMODE) || \ - (c == HPC_BUS_66CONVMODE) || \ - (c == HPC_BUS_66PCIXMODE) || \ - (c == HPC_BUS_100PCIXMODE) || \ - (c == HPC_BUS_133PCIXMODE) || \ - (c == HPC_ALLSLOT_OFF) || \ - (c == HPC_ALLSLOT_ON)) - - -/* Core part of the driver */ - -#define ENABLE 1 -#define DISABLE 0 - -#define CARD_INFO 0x07 -#define PCIX133 0x07 -#define PCIX66 0x05 -#define PCI66 0x04 - -extern struct pci_bus *ibmphp_pci_bus; - -/* Variables */ - -struct pci_func { - struct pci_dev *dev; /* from the OS */ - u8 busno; - u8 device; - u8 function; - struct resource_node *io[6]; - struct resource_node *mem[6]; - struct resource_node *pfmem[6]; - struct pci_func *next; - int devices[32]; /* for bridge config */ - u8 irq[4]; /* for interrupt config */ - u8 bus; /* flag for unconfiguring, to say if PPB */ -}; - -struct slot { - u8 bus; - u8 device; - u8 number; - u8 real_physical_slot_num; - char name[100]; - u32 capabilities; - u8 supported_speed; - u8 supported_bus_mode; - struct hotplug_slot *hotplug_slot; - struct controller *ctrl; - struct pci_func *func; - u8 irq[4]; - u8 flag; /* this is for disable slot and polling */ - int bit_mode; /* 0 = 32, 1 = 64 */ - u8 ctlr_index; - struct bus_info *bus_on; - struct list_head ibm_slot_list; - u8 status; - u8 ext_status; - u8 busstatus; -}; - -struct controller { - struct ebda_hpc_slot *slots; - struct ebda_hpc_bus *buses; - struct pci_dev *ctrl_dev; /* in case where controller is PCI */ - u8 starting_slot_num; /* starting and ending slot #'s this ctrl controls*/ - u8 ending_slot_num; - u8 revision; - u8 options; /* which options HPC supports */ - u8 status; - u8 ctlr_id; - u8 slot_count; - u8 bus_count; - u8 ctlr_relative_id; - u32 irq; - union { - struct isa_ctlr_access isa_ctlr; - struct pci_ctlr_access pci_ctlr; - struct wpeg_i2c_ctlr_access wpeg_ctlr; - } u; - u8 ctlr_type; - struct list_head ebda_hpc_list; -}; - -/* Functions */ - -extern int ibmphp_init_devno (struct slot **); /* This function is called from EBDA, so we need it not be static */ -extern int ibmphp_disable_slot (struct hotplug_slot *); /* This function is called from HPC, so we need it to not be static */ -extern int ibmphp_do_disable_slot (struct slot *slot_cur); -extern int ibmphp_update_slot_info (struct slot *); /* This function is called from HPC, so we need it to not be be static */ -extern int ibmphp_configure_card (struct pci_func *, u8); -extern int ibmphp_unconfigure_card (struct slot **, int); -extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops; - -static inline void long_delay (int delay) -{ - set_current_state (TASK_INTERRUPTIBLE); - schedule_timeout (delay); -} - -#endif //__IBMPHP_H - diff -Nru a/drivers/hotplug/ibmphp_core.c b/drivers/hotplug/ibmphp_core.c --- a/drivers/hotplug/ibmphp_core.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1417 +0,0 @@ -/* - * IBM Hot Plug Controller Driver - * - * Written By: Chuck Cole, Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation - * - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001,2002 IBM Corp. - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "../../arch/i386/pci/pci.h" /* for struct irq_routing_table */ -#include "ibmphp.h" - -#define attn_on(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNON) -#define attn_off(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNOFF) -#define attn_LED_blink(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_BLINKLED) -#define get_ctrl_revision(sl, rev) ibmphp_hpc_readslot (sl, READ_REVLEVEL, rev) -#define get_hpc_options(sl, opt) ibmphp_hpc_readslot (sl, READ_HPCOPTIONS, opt) - -#define DRIVER_VERSION "0.6" -#define DRIVER_DESC "IBM Hot Plug PCI Controller Driver" - -int ibmphp_debug; - -static int debug; -MODULE_PARM (debug, "i"); -MODULE_PARM_DESC (debug, "Debugging mode enabled or not"); -MODULE_LICENSE ("GPL"); -MODULE_DESCRIPTION (DRIVER_DESC); - -struct pci_bus *ibmphp_pci_bus; -static int max_slots; - -static int irqs[16]; /* PIC mode IRQ's we're using so far (in case MPS tables don't provide default info for empty slots */ - -static int init_flag; - -/* -static int get_max_adapter_speed_1 (struct hotplug_slot *, u8 *, u8); - -static inline int get_max_adapter_speed (struct hotplug_slot *hs, u8 *value) -{ - return get_max_adapter_speed_1 (hs, value, 1); -} -*/ -static inline int get_cur_bus_info (struct slot **sl) -{ - int rc = 1; - struct slot * slot_cur = *sl; - - debug ("options = %x\n", slot_cur->ctrl->options); - debug ("revision = %x\n", slot_cur->ctrl->revision); - - if (READ_BUS_STATUS (slot_cur->ctrl)) - rc = ibmphp_hpc_readslot (slot_cur, READ_BUSSTATUS, NULL); - - if (rc) - return rc; - - slot_cur->bus_on->current_speed = CURRENT_BUS_SPEED (slot_cur->busstatus); - if (READ_BUS_MODE (slot_cur->ctrl)) - slot_cur->bus_on->current_bus_mode = CURRENT_BUS_MODE (slot_cur->busstatus); - else - slot_cur->bus_on->current_bus_mode = 0xFF; - - debug ("busstatus = %x, bus_speed = %x, bus_mode = %x\n", slot_cur->busstatus, slot_cur->bus_on->current_speed, slot_cur->bus_on->current_bus_mode); - - *sl = slot_cur; - return 0; -} - -static inline int slot_update (struct slot **sl) -{ - int rc; - rc = ibmphp_hpc_readslot (*sl, READ_ALLSTAT, NULL); - if (rc) - return rc; - if (!init_flag) - return get_cur_bus_info (sl); - return rc; -} - -static int __init get_max_slots (void) -{ - struct slot * slot_cur; - struct list_head * tmp; - u8 slot_count = 0; - - list_for_each (tmp, &ibmphp_slot_head) { - slot_cur = list_entry (tmp, struct slot, ibm_slot_list); - /* sometimes the hot-pluggable slots start with 4 (not always from 1 */ - slot_count = max (slot_count, slot_cur->number); - } - return slot_count; -} - -/* This routine will put the correct slot->device information per slot. It's - * called from initialization of the slot structures. It will also assign - * interrupt numbers per each slot. - * Parameters: struct slot - * Returns 0 or errors - */ -int ibmphp_init_devno (struct slot **cur_slot) -{ - struct irq_routing_table *rtable; - int len; - int loop; - int i; - - rtable = pcibios_get_irq_routing_table (); - if (!rtable) { - err ("no BIOS routing table...\n"); - return -ENOMEM; - } - - len = (rtable->size - sizeof (struct irq_routing_table)) / sizeof (struct irq_info); - - if (!len) - return -1; - for (loop = 0; loop < len; loop++) { - if ((*cur_slot)->number == rtable->slots[loop].slot) { - if ((*cur_slot)->bus == rtable->slots[loop].bus) { - (*cur_slot)->device = PCI_SLOT (rtable->slots[loop].devfn); - for (i = 0; i < 4; i++) - (*cur_slot)->irq[i] = IO_APIC_get_PCI_irq_vector ((int) (*cur_slot)->bus, (int) (*cur_slot)->device, i); - - debug ("(*cur_slot)->irq[0] = %x\n", (*cur_slot)->irq[0]); - debug ("(*cur_slot)->irq[1] = %x\n", (*cur_slot)->irq[1]); - debug ("(*cur_slot)->irq[2] = %x\n", (*cur_slot)->irq[2]); - debug ("(*cur_slot)->irq[3] = %x\n", (*cur_slot)->irq[3]); - - debug ("rtable->exlusive_irqs = %x\n", rtable->exclusive_irqs); - debug ("rtable->slots[loop].irq[0].bitmap = %x\n", rtable->slots[loop].irq[0].bitmap); - debug ("rtable->slots[loop].irq[1].bitmap = %x\n", rtable->slots[loop].irq[1].bitmap); - debug ("rtable->slots[loop].irq[2].bitmap = %x\n", rtable->slots[loop].irq[2].bitmap); - debug ("rtable->slots[loop].irq[3].bitmap = %x\n", rtable->slots[loop].irq[3].bitmap); - - debug ("rtable->slots[loop].irq[0].link= %x\n", rtable->slots[loop].irq[0].link); - debug ("rtable->slots[loop].irq[1].link = %x\n", rtable->slots[loop].irq[1].link); - debug ("rtable->slots[loop].irq[2].link = %x\n", rtable->slots[loop].irq[2].link); - debug ("rtable->slots[loop].irq[3].link = %x\n", rtable->slots[loop].irq[3].link); - debug ("end of init_devno\n"); - return 0; - } - } - } - - return -1; -} - -static inline int power_on (struct slot *slot_cur) -{ - u8 cmd = HPC_SLOT_ON; - int retval; - - retval = ibmphp_hpc_writeslot (slot_cur, cmd); - if (retval) { - err ("power on failed\n"); - return retval; - } - if (CTLR_RESULT (slot_cur->ctrl->status)) { - err ("command not completed successfully in power_on \n"); - return -EIO; - } - long_delay (3 * HZ); /* For ServeRAID cards, and some 66 PCI */ - return 0; -} - -static inline int power_off (struct slot *slot_cur) -{ - u8 cmd = HPC_SLOT_OFF; - int retval; - - retval = ibmphp_hpc_writeslot (slot_cur, cmd); - if (retval) { - err ("power off failed \n"); - return retval; - } - if (CTLR_RESULT (slot_cur->ctrl->status)) { - err ("command not completed successfully in power_off \n"); - return -EIO; - } - return 0; -} - -static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 value) -{ - int rc = 0; - struct slot *pslot; - u8 cmd; - int hpcrc = 0; - - debug ("set_attention_status - Entry hotplug_slot[%lx] value[%x]\n", (ulong) hotplug_slot, value); - ibmphp_lock_operations (); - cmd = 0x00; // avoid compiler warning - - if (hotplug_slot) { - switch (value) { - case HPC_SLOT_ATTN_OFF: - cmd = HPC_SLOT_ATTNOFF; - break; - case HPC_SLOT_ATTN_ON: - cmd = HPC_SLOT_ATTNON; - break; - case HPC_SLOT_ATTN_BLINK: - cmd = HPC_SLOT_BLINKLED; - break; - default: - rc = -ENODEV; - err ("set_attention_status - Error : invalid input [%x]\n", value); - break; - } - if (rc == 0) { - pslot = (struct slot *) hotplug_slot->private; - if (pslot) - hpcrc = ibmphp_hpc_writeslot (pslot, cmd); - else - rc = -ENODEV; - } - } else - rc = -ENODEV; - - if (hpcrc) - rc = hpcrc; - - ibmphp_unlock_operations (); - - debug ("set_attention_status - Exit rc[%d]\n", rc); - return rc; -} - -static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 * value) -{ - int rc = -ENODEV; - struct slot *pslot; - int hpcrc = 0; - struct slot myslot; - - debug ("get_attention_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); - - ibmphp_lock_operations (); - if (hotplug_slot && value) { - pslot = (struct slot *) hotplug_slot->private; - if (pslot) { - memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); - hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); - if (!hpcrc) - hpcrc = ibmphp_hpc_readslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status)); - if (!hpcrc) { - *value = SLOT_ATTN (myslot.status, myslot.ext_status); - rc = 0; - } - } - } else - rc = -ENODEV; - - if (hpcrc) - rc = hpcrc; - - ibmphp_unlock_operations (); - debug ("get_attention_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); - return rc; -} - -static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 * value) -{ - int rc = -ENODEV; - struct slot *pslot; - int hpcrc = 0; - struct slot myslot; - - debug ("get_latch_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); - ibmphp_lock_operations (); - if (hotplug_slot && value) { - pslot = (struct slot *) hotplug_slot->private; - if (pslot) { - memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); - hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); - if (!hpcrc) { - *value = SLOT_LATCH (myslot.status); - rc = 0; - } - } - } else - rc = -ENODEV; - - if (hpcrc) - rc = hpcrc; - - ibmphp_unlock_operations (); - debug ("get_latch_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); - return rc; -} - - -static int get_power_status (struct hotplug_slot *hotplug_slot, u8 * value) -{ - int rc = -ENODEV; - struct slot *pslot; - int hpcrc = 0; - struct slot myslot; - - debug ("get_power_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); - ibmphp_lock_operations (); - if (hotplug_slot && value) { - pslot = (struct slot *) hotplug_slot->private; - if (pslot) { - memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); - hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); - if (!hpcrc) { - *value = SLOT_PWRGD (myslot.status); - rc = 0; - } - } - } else - rc = -ENODEV; - - if (hpcrc) - rc = hpcrc; - - ibmphp_unlock_operations (); - debug ("get_power_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); - return rc; -} - -static int get_adapter_present (struct hotplug_slot *hotplug_slot, u8 * value) -{ - int rc = -ENODEV; - struct slot *pslot; - u8 present; - int hpcrc = 0; - struct slot myslot; - - debug ("get_adapter_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); - ibmphp_lock_operations (); - if (hotplug_slot && value) { - pslot = (struct slot *) hotplug_slot->private; - if (pslot) { - memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); - hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); - if (!hpcrc) { - present = SLOT_PRESENT (myslot.status); - if (present == HPC_SLOT_EMPTY) - *value = 0; - else - *value = 1; - rc = 0; - } - } - } else - rc = -ENODEV; - if (hpcrc) - rc = hpcrc; - - ibmphp_unlock_operations (); - debug ("get_adapter_present - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); - return rc; -} - -static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) -{ - int rc = -ENODEV; - struct slot *pslot; - u8 mode = 0; - - debug ("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __FUNCTION__, - hotplug_slot, value); - - ibmphp_lock_operations (); - - if (hotplug_slot && value) { - pslot = (struct slot *) hotplug_slot->private; - if (pslot) { - rc = 0; - mode = pslot->supported_bus_mode; - *value = pslot->supported_speed; - switch (*value) { - case BUS_SPEED_33: - break; - case BUS_SPEED_66: - if (mode == BUS_MODE_PCIX) - *value += 0x01; - break; - case BUS_SPEED_100: - case BUS_SPEED_133: - *value = pslot->supported_speed + 0x01; - break; - default: - /* Note (will need to change): there would be soon 256, 512 also */ - rc = -ENODEV; - } - } - } else - rc = -ENODEV; - - ibmphp_unlock_operations (); - debug ("%s - Exit rc[%d] value[%x]\n", __FUNCTION__, rc, *value); - return rc; -} - -static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) -{ - int rc = -ENODEV; - struct slot *pslot; - u8 mode = 0; - - debug ("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __FUNCTION__, - hotplug_slot, value); - - ibmphp_lock_operations (); - - if (hotplug_slot && value) { - pslot = (struct slot *) hotplug_slot->private; - if (pslot) { - rc = get_cur_bus_info (&pslot); - if (!rc) { - mode = pslot->bus_on->current_bus_mode; - *value = pslot->bus_on->current_speed; - switch (*value) { - case BUS_SPEED_33: - break; - case BUS_SPEED_66: - if (mode == BUS_MODE_PCIX) - *value += 0x01; - else if (mode == BUS_MODE_PCI) - ; - else - *value = PCI_SPEED_UNKNOWN; - break; - case BUS_SPEED_100: - case BUS_SPEED_133: - *value += 0x01; - break; - default: - /* Note of change: there would also be 256, 512 soon */ - rc = -ENODEV; - } - } - } - } else - rc = -ENODEV; - - ibmphp_unlock_operations (); - debug ("%s - Exit rc[%d] value[%x]\n", __FUNCTION__, rc, *value); - return rc; -} -/* -static int get_max_adapter_speed_1 (struct hotplug_slot *hotplug_slot, u8 * value, u8 flag) -{ - int rc = -ENODEV; - struct slot *pslot; - int hpcrc = 0; - struct slot myslot; - - debug ("get_max_adapter_speed_1 - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong)hotplug_slot, (ulong) value); - - if (flag) - ibmphp_lock_operations (); - - if (hotplug_slot && value) { - pslot = (struct slot *) hotplug_slot->private; - if (pslot) { - memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); - hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); - - if (!(SLOT_LATCH (myslot.status)) && (SLOT_PRESENT (myslot.status))) { - hpcrc = ibmphp_hpc_readslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status)); - if (!hpcrc) { - *value = SLOT_SPEED (myslot.ext_status); - rc = 0; - } - } else { - *value = MAX_ADAPTER_NONE; - rc = 0; - } - } - } else - rc = -ENODEV; - - if (hpcrc) - rc = hpcrc; - - if (flag) - ibmphp_unlock_operations (); - - debug ("get_max_adapter_speed_1 - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); - return rc; -} - -static int get_bus_name (struct hotplug_slot *hotplug_slot, char * value) -{ - int rc = -ENODEV; - struct slot *pslot = NULL; - - debug ("get_bus_name - Entry hotplug_slot[%lx] \n", (ulong)hotplug_slot); - - ibmphp_lock_operations (); - - if (hotplug_slot) { - pslot = (struct slot *) hotplug_slot->private; - if (pslot) { - rc = 0; - snprintf (value, 100, "Bus %x", pslot->bus); - } - } else - rc = -ENODEV; - - ibmphp_unlock_operations (); - debug ("get_bus_name - Exit rc[%d] value[%x]\n", rc, *value); - return rc; -} -*/ - -/******************************************************************************* - * This routine will initialize the ops data structure used in the validate - * function. It will also power off empty slots that are powered on since BIOS - * leaves those on, albeit disconnected - ******************************************************************************/ -static int __init init_ops (void) -{ - struct slot *slot_cur; - struct list_head *tmp; - int retval; - int rc; - - list_for_each (tmp, &ibmphp_slot_head) { - slot_cur = list_entry (tmp, struct slot, ibm_slot_list); - - if (!slot_cur) - return -ENODEV; - - debug ("BEFORE GETTING SLOT STATUS, slot # %x\n", slot_cur->number); - if (slot_cur->ctrl->revision == 0xFF) - if (get_ctrl_revision (slot_cur, &slot_cur->ctrl->revision)) - return -1; - - if (slot_cur->bus_on->current_speed == 0xFF) - if (get_cur_bus_info (&slot_cur)) - return -1; - - if (slot_cur->ctrl->options == 0xFF) - if (get_hpc_options (slot_cur, &slot_cur->ctrl->options)) - return -1; - - retval = slot_update (&slot_cur); - if (retval) - return retval; - - debug ("status = %x\n", slot_cur->status); - debug ("ext_status = %x\n", slot_cur->ext_status); - debug ("SLOT_POWER = %x\n", SLOT_POWER (slot_cur->status)); - debug ("SLOT_PRESENT = %x\n", SLOT_PRESENT (slot_cur->status)); - debug ("SLOT_LATCH = %x\n", SLOT_LATCH (slot_cur->status)); - - if ((SLOT_PWRGD (slot_cur->status)) && - !(SLOT_PRESENT (slot_cur->status)) && - !(SLOT_LATCH (slot_cur->status))) { - debug ("BEFORE POWER OFF COMMAND\n"); - rc = power_off (slot_cur); - if (rc) - return rc; - - /* retval = slot_update (&slot_cur); - * if (retval) - * return retval; - * ibmphp_update_slot_info (slot_cur); - */ - } - } - init_flag = 0; - return 0; -} - -/* This operation will check whether the slot is within the bounds and - * the operation is valid to perform on that slot - * Parameters: slot, operation - * Returns: 0 or error codes - */ -static int validate (struct slot *slot_cur, int opn) -{ - int number; - int retval; - - if (!slot_cur) - return -ENODEV; - number = slot_cur->number; - if ((number > max_slots) || (number < 0)) - return -EBADSLT; - debug ("slot_number in validate is %d\n", slot_cur->number); - - retval = slot_update (&slot_cur); - if (retval) - return retval; - - switch (opn) { - case ENABLE: - if (!(SLOT_PWRGD (slot_cur->status)) && - (SLOT_PRESENT (slot_cur->status)) && - !(SLOT_LATCH (slot_cur->status))) - return 0; - break; - case DISABLE: - if ((SLOT_PWRGD (slot_cur->status)) && - (SLOT_PRESENT (slot_cur->status)) && - !(SLOT_LATCH (slot_cur->status))) - return 0; - break; - default: - break; - } - err ("validate failed....\n"); - return -EINVAL; -} - -/******************************************************************************** - * This routine is for updating the data structures in the hotplug core - * Parameters: struct slot - * Returns: 0 or error - *******************************************************************************/ -int ibmphp_update_slot_info (struct slot *slot_cur) -{ - struct hotplug_slot_info *info; - int rc; - u8 bus_speed; - u8 mode; - - info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); - if (!info) { - err ("out of system memory \n"); - return -ENOMEM; - } - - info->power_status = SLOT_PWRGD (slot_cur->status); - info->attention_status = SLOT_ATTN (slot_cur->status, slot_cur->ext_status); - info->latch_status = SLOT_LATCH (slot_cur->status); - if (!SLOT_PRESENT (slot_cur->status)) { - info->adapter_status = 0; -// info->max_adapter_speed_status = MAX_ADAPTER_NONE; - } else { - info->adapter_status = 1; -// get_max_adapter_speed_1 (slot_cur->hotplug_slot, &info->max_adapter_speed_status, 0); - } - - bus_speed = slot_cur->bus_on->current_speed; - mode = slot_cur->bus_on->current_bus_mode; - - switch (bus_speed) { - case BUS_SPEED_33: - break; - case BUS_SPEED_66: - if (mode == BUS_MODE_PCIX) - bus_speed += 0x01; - else if (mode == BUS_MODE_PCI) - ; - else - bus_speed = PCI_SPEED_UNKNOWN; - break; - case BUS_SPEED_100: - case BUS_SPEED_133: - bus_speed += 0x01; - break; - default: - bus_speed = PCI_SPEED_UNKNOWN; - } - - info->cur_bus_speed = bus_speed; - info->max_bus_speed = slot_cur->hotplug_slot->info->max_bus_speed; - // To do: bus_names - - rc = pci_hp_change_slot_info (slot_cur->hotplug_slot, info); - kfree (info); - return rc; -} - - -/****************************************************************************** - * This function will return the pci_func, given bus and devfunc, or NULL. It - * is called from visit routines - ******************************************************************************/ - -static struct pci_func *ibm_slot_find (u8 busno, u8 device, u8 function) -{ - struct pci_func *func_cur; - struct slot *slot_cur; - struct list_head * tmp; - list_for_each (tmp, &ibmphp_slot_head) { - slot_cur = list_entry (tmp, struct slot, ibm_slot_list); - if (slot_cur->func) { - func_cur = slot_cur->func; - while (func_cur) { - if ((func_cur->busno == busno) && (func_cur->device == device) && (func_cur->function == function)) - return func_cur; - func_cur = func_cur->next; - } - } - } - return NULL; -} - -/************************************************************* - * This routine frees up memory used by struct slot, including - * the pointers to pci_func, bus, hotplug_slot, controller, - * and deregistering from the hotplug core - *************************************************************/ -static void free_slots (void) -{ - struct slot *slot_cur; - struct list_head * tmp; - struct list_head * next; - - debug ("%s -- enter\n", __FUNCTION__); - - list_for_each_safe (tmp, next, &ibmphp_slot_head) { - - slot_cur = list_entry (tmp, struct slot, ibm_slot_list); - - pci_hp_deregister (slot_cur->hotplug_slot); - - if (slot_cur->hotplug_slot) { - kfree (slot_cur->hotplug_slot); - slot_cur->hotplug_slot = NULL; - } - - if (slot_cur->ctrl) - slot_cur->ctrl = NULL; - - if (slot_cur->bus_on) - slot_cur->bus_on = NULL; - - ibmphp_unconfigure_card (&slot_cur, -1); /* we don't want to actually remove the resources, since free_resources will do just that */ - - kfree (slot_cur); - slot_cur = NULL; - } - debug ("%s -- exit\n", __FUNCTION__); -} - -static int ibm_unconfigure_device (struct pci_func *func) -{ - struct pci_dev *temp; - u8 j; - - debug ("inside %s\n", __FUNCTION__); - debug ("func->device = %x, func->function = %x\n", func->device, func->function); - debug ("func->device << 3 | 0x0 = %x\n", func->device << 3 | 0x0); - - for (j = 0; j < 0x08; j++) { - temp = pci_find_slot (func->busno, (func->device << 3) | j); - if (temp) - pci_remove_bus_device(temp); - } - return 0; -} - -/* - * The following function is to fix kernel bug regarding - * getting bus entries, here we manually add those primary - * bus entries to kernel bus structure whenever apply - */ - -static u8 bus_structure_fixup (u8 busno) -{ - struct pci_bus *bus; - struct pci_dev *dev; - u16 l; - - if (pci_find_bus(busno) || !(ibmphp_find_same_bus_num (busno))) - return 1; - - bus = kmalloc (sizeof (*bus), GFP_KERNEL); - if (!bus) { - err ("%s - out of memory\n", __FUNCTION__); - return 1; - } - dev = kmalloc (sizeof (*dev), GFP_KERNEL); - if (!dev) { - kfree (bus); - err ("%s - out of memory\n", __FUNCTION__); - return 1; - } - - bus->number = busno; - bus->ops = ibmphp_pci_bus->ops; - dev->bus = bus; - for (dev->devfn = 0; dev->devfn < 256; dev->devfn += 8) { - if (!pci_read_config_word (dev, PCI_VENDOR_ID, &l) && l != 0x0000 && l != 0xffff) { - debug ("%s - Inside bus_struture_fixup() \n", __FUNCTION__); - pci_scan_bus (busno, ibmphp_pci_bus->ops, NULL); - break; - } - } - - kfree (dev); - kfree (bus); - - return 0; -} - -static int ibm_configure_device (struct pci_func *func) -{ - unsigned char bus; - struct pci_bus *child; - int num; - int flag = 0; /* this is to make sure we don't double scan the bus, for bridged devices primarily */ - - if (!(bus_structure_fixup (func->busno))) - flag = 1; - if (func->dev == NULL) - func->dev = pci_find_slot (func->busno, PCI_DEVFN(func->device, func->function)); - - if (func->dev == NULL) { - struct pci_bus *bus = pci_find_bus(func->busno); - if (!bus) - return 0; - - num = pci_scan_slot(bus, PCI_DEVFN(func->device, func->function)); - if (num) - pci_bus_add_devices(bus); - - func->dev = pci_find_slot(func->busno, PCI_DEVFN(func->device, func->function)); - if (func->dev == NULL) { - err ("ERROR... : pci_dev still NULL \n"); - return 0; - } - } - if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) { - pci_read_config_byte (func->dev, PCI_SECONDARY_BUS, &bus); - child = (struct pci_bus *) pci_add_new_bus (func->dev->bus, (func->dev), bus); - pci_do_scan_bus (child); - } - - return 0; -} - -/******************************************************* - * Returns whether the bus is empty or not - *******************************************************/ -static int is_bus_empty (struct slot * slot_cur) -{ - int rc; - struct slot * tmp_slot; - u8 i = slot_cur->bus_on->slot_min; - - while (i <= slot_cur->bus_on->slot_max) { - if (i == slot_cur->number) { - i++; - continue; - } - tmp_slot = ibmphp_get_slot_from_physical_num (i); - if (!tmp_slot) - return 0; - rc = slot_update (&tmp_slot); - if (rc) - return 0; - if (SLOT_PRESENT (tmp_slot->status) && SLOT_PWRGD (tmp_slot->status)) - return 0; - i++; - } - return 1; -} - -/*********************************************************** - * If the HPC permits and the bus currently empty, tries to set the - * bus speed and mode at the maximum card and bus capability - * Parameters: slot - * Returns: bus is set (0) or error code - ***********************************************************/ -static int set_bus (struct slot * slot_cur) -{ - int rc; - u8 speed; - u8 cmd = 0x0; - struct pci_dev * dev; - int retval; - - debug ("%s - entry slot # %d \n", __FUNCTION__, slot_cur->number); - if (SET_BUS_STATUS (slot_cur->ctrl) && is_bus_empty (slot_cur)) { - rc = slot_update (&slot_cur); - if (rc) - return rc; - speed = SLOT_SPEED (slot_cur->ext_status); - debug ("ext_status = %x, speed = %x\n", slot_cur->ext_status, speed); - switch (speed) { - case HPC_SLOT_SPEED_33: - cmd = HPC_BUS_33CONVMODE; - break; - case HPC_SLOT_SPEED_66: - if (SLOT_PCIX (slot_cur->ext_status)) { - if ((slot_cur->supported_speed >= BUS_SPEED_66) && (slot_cur->supported_bus_mode == BUS_MODE_PCIX)) - cmd = HPC_BUS_66PCIXMODE; - else if (!SLOT_BUS_MODE (slot_cur->ext_status)) - /* if max slot/bus capability is 66 pci - and there's no bus mode mismatch, then - the adapter supports 66 pci */ - cmd = HPC_BUS_66CONVMODE; - else - cmd = HPC_BUS_33CONVMODE; - } else { - if (slot_cur->supported_speed >= BUS_SPEED_66) - cmd = HPC_BUS_66CONVMODE; - else - cmd = HPC_BUS_33CONVMODE; - } - break; - case HPC_SLOT_SPEED_133: - switch (slot_cur->supported_speed) { - case BUS_SPEED_33: - cmd = HPC_BUS_33CONVMODE; - break; - case BUS_SPEED_66: - if (slot_cur->supported_bus_mode == BUS_MODE_PCIX) - cmd = HPC_BUS_66PCIXMODE; - else - cmd = HPC_BUS_66CONVMODE; - break; - case BUS_SPEED_100: - cmd = HPC_BUS_100PCIXMODE; - break; - case BUS_SPEED_133: - /* This is to take care of the bug in CIOBX chip */ - pci_for_each_dev(dev) { - if ((dev->vendor == PCI_VENDOR_ID_SERVERWORKS) && - (dev->device == 0x0101)) - ibmphp_hpc_writeslot (slot_cur, HPC_BUS_100PCIXMODE); - } - cmd = HPC_BUS_133PCIXMODE; - break; - default: - err ("Wrong bus speed \n"); - return -ENODEV; - } - break; - default: - err ("wrong slot speed \n"); - return -ENODEV; - } - debug ("setting bus speed for slot %d, cmd %x\n", slot_cur->number, cmd); - retval = ibmphp_hpc_writeslot (slot_cur, cmd); - if (retval) { - err ("setting bus speed failed\n"); - return retval; - } - if (CTLR_RESULT (slot_cur->ctrl->status)) { - err ("command not completed successfully in set_bus \n"); - return -EIO; - } - } - /* This is for x440, once Brandon fixes the firmware, - will not need this delay */ - long_delay (1 * HZ); - debug ("%s -Exit \n", __FUNCTION__); - return 0; -} - -/* This routine checks the bus limitations that the slot is on from the BIOS. - * This is used in deciding whether or not to power up the slot. - * (electrical/spec limitations. For example, >1 133 MHz or >2 66 PCI cards on - * same bus) - * Parameters: slot - * Returns: 0 = no limitations, -EINVAL = exceeded limitations on the bus - */ -static int check_limitations (struct slot *slot_cur) -{ - u8 i; - struct slot * tmp_slot; - u8 count = 0; - u8 limitation = 0; - - for (i = slot_cur->bus_on->slot_min; i <= slot_cur->bus_on->slot_max; i++) { - tmp_slot = ibmphp_get_slot_from_physical_num (i); - if (!tmp_slot) - return -ENODEV; - if ((SLOT_PWRGD (tmp_slot->status)) && !(SLOT_CONNECT (tmp_slot->status))) - count++; - } - get_cur_bus_info (&slot_cur); - switch (slot_cur->bus_on->current_speed) { - case BUS_SPEED_33: - limitation = slot_cur->bus_on->slots_at_33_conv; - break; - case BUS_SPEED_66: - if (slot_cur->bus_on->current_bus_mode == BUS_MODE_PCIX) - limitation = slot_cur->bus_on->slots_at_66_pcix; - else - limitation = slot_cur->bus_on->slots_at_66_conv; - break; - case BUS_SPEED_100: - limitation = slot_cur->bus_on->slots_at_100_pcix; - break; - case BUS_SPEED_133: - limitation = slot_cur->bus_on->slots_at_133_pcix; - break; - } - - if ((count + 1) > limitation) - return -EINVAL; - return 0; -} - -static inline void print_card_capability (struct slot *slot_cur) -{ - info ("capability of the card is "); - if ((slot_cur->ext_status & CARD_INFO) == PCIX133) - info (" 133 MHz PCI-X \n"); - else if ((slot_cur->ext_status & CARD_INFO) == PCIX66) - info (" 66 MHz PCI-X \n"); - else if ((slot_cur->ext_status & CARD_INFO) == PCI66) - info (" 66 MHz PCI \n"); - else - info (" 33 MHz PCI \n"); - -} - -/* This routine will power on the slot, configure the device(s) and find the - * drivers for them. - * Parameters: hotplug_slot - * Returns: 0 or failure codes - */ -static int enable_slot (struct hotplug_slot *hs) -{ - int rc, i, rcpr; - struct slot *slot_cur; - u8 function; - struct pci_func *tmp_func; - - ibmphp_lock_operations (); - - debug ("ENABLING SLOT........ \n"); - slot_cur = (struct slot *) hs->private; - - if ((rc = validate (slot_cur, ENABLE))) { - err ("validate function failed \n"); - goto error_nopower; - } - - attn_LED_blink (slot_cur); - - rc = set_bus (slot_cur); - if (rc) { - err ("was not able to set the bus \n"); - goto error_nopower; - } - - /*-----------------debugging------------------------------*/ - get_cur_bus_info (&slot_cur); - debug ("the current bus speed right after set_bus = %x \n", slot_cur->bus_on->current_speed); - /*----------------------------------------------------------*/ - - rc = check_limitations (slot_cur); - if (rc) { - err ("Adding this card exceeds the limitations of this bus.\n"); - err ("(i.e., >1 133MHz cards running on same bus, or " - ">2 66 PCI cards running on same bus\n."); - err ("Try hot-adding into another bus \n"); - rc = -EINVAL; - goto error_nopower; - } - - rc = power_on (slot_cur); - - if (rc) { - err ("something wrong when powering up... please see below for details\n"); - /* need to turn off before on, otherwise, blinking overwrites */ - attn_off(slot_cur); - attn_on (slot_cur); - if (slot_update (&slot_cur)) { - attn_off (slot_cur); - attn_on (slot_cur); - rc = -ENODEV; - goto exit; - } - /* Check to see the error of why it failed */ - if ((SLOT_POWER (slot_cur->status)) && !(SLOT_PWRGD (slot_cur->status))) - err ("power fault occurred trying to power up \n"); - else if (SLOT_BUS_SPEED (slot_cur->status)) { - err ("bus speed mismatch occurred. please check current bus speed and card capability \n"); - print_card_capability (slot_cur); - } else if (SLOT_BUS_MODE (slot_cur->ext_status)) { - err ("bus mode mismatch occurred. please check current bus mode and card capability \n"); - print_card_capability (slot_cur); - } - ibmphp_update_slot_info (slot_cur); - goto exit; - } - debug ("after power_on\n"); - /*-----------------------debugging---------------------------*/ - get_cur_bus_info (&slot_cur); - debug ("the current bus speed right after power_on = %x \n", slot_cur->bus_on->current_speed); - /*----------------------------------------------------------*/ - - rc = slot_update (&slot_cur); - if (rc) - goto error_power; - - rc = -EINVAL; - if (SLOT_POWER (slot_cur->status) && !(SLOT_PWRGD (slot_cur->status))) { - err ("power fault occurred trying to power up... \n"); - goto error_power; - } - if (SLOT_POWER (slot_cur->status) && (SLOT_BUS_SPEED (slot_cur->status))) { - err ("bus speed mismatch occurred. please check current bus speed and card capability \n"); - print_card_capability (slot_cur); - goto error_power; - } - /* Don't think this case will happen after above checks... but just in case, for paranoia sake */ - if (!(SLOT_POWER (slot_cur->status))) { - err ("power on failed... \n"); - goto error_power; - } - - slot_cur->func = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); - if (!slot_cur->func) { - /* We cannot do update_slot_info here, since no memory for - * kmalloc n.e.ways, and update_slot_info allocates some */ - err ("out of system memory \n"); - rc = -ENOMEM; - goto error_power; - } - memset (slot_cur->func, 0, sizeof (struct pci_func)); - slot_cur->func->busno = slot_cur->bus; - slot_cur->func->device = slot_cur->device; - for (i = 0; i < 4; i++) - slot_cur->func->irq[i] = slot_cur->irq[i]; - - debug ("b4 configure_card, slot_cur->bus = %x, slot_cur->device = %x\n", slot_cur->bus, slot_cur->device); - - if (ibmphp_configure_card (slot_cur->func, slot_cur->number)) { - err ("configure_card was unsuccessful... \n"); - ibmphp_unconfigure_card (&slot_cur, 1); /* true because don't need to actually deallocate resources, just remove references */ - debug ("after unconfigure_card\n"); - slot_cur->func = NULL; - rc = -ENOMEM; - goto error_power; - } - - function = 0x00; - do { - tmp_func = ibm_slot_find (slot_cur->bus, slot_cur->func->device, function++); - if (tmp_func && !(tmp_func->dev)) - ibm_configure_device (tmp_func); - } while (tmp_func); - - attn_off (slot_cur); - if (slot_update (&slot_cur)) { - rc = -EFAULT; - goto exit; - } - ibmphp_print_test (); - rc = ibmphp_update_slot_info (slot_cur); -exit: - ibmphp_unlock_operations(); - return rc; - -error_nopower: - attn_off (slot_cur); /* need to turn off if was blinking b4 */ - attn_on (slot_cur); -error_cont: - rcpr = slot_update (&slot_cur); - if (rcpr) { - rc = rcpr; - goto exit; - } - ibmphp_update_slot_info (slot_cur); - goto exit; - -error_power: - attn_off (slot_cur); /* need to turn off if was blinking b4 */ - attn_on (slot_cur); - rcpr = power_off (slot_cur); - if (rcpr) { - rc = rcpr; - goto exit; - } - goto error_cont; -} - -/************************************************************** -* HOT REMOVING ADAPTER CARD * -* INPUT: POINTER TO THE HOTPLUG SLOT STRUCTURE * -* OUTPUT: SUCCESS 0 ; FAILURE: UNCONFIGURE , VALIDATE * - DISABLE POWER , * -**************************************************************/ -int ibmphp_disable_slot (struct hotplug_slot *hotplug_slot) -{ - struct slot *slot = hotplug_slot->private; - int rc; - - ibmphp_lock_operations(); - rc = ibmphp_do_disable_slot(slot); - ibmphp_unlock_operations(); - return rc; -} - -int ibmphp_do_disable_slot (struct slot *slot_cur) -{ - int rc; - u8 flag; - int parm = 0; - - debug ("DISABLING SLOT... \n"); - - if ((slot_cur == NULL) || (slot_cur->ctrl == NULL)) { - return -ENODEV; - } - - flag = slot_cur->flag; - slot_cur->flag = TRUE; - - if (flag == TRUE) { - rc = validate (slot_cur, DISABLE); /* checking if powered off already & valid slot # */ - if (rc) - goto error; - } - attn_LED_blink (slot_cur); - - if (slot_cur->func == NULL) { - /* We need this for fncs's that were there on bootup */ - slot_cur->func = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); - if (!slot_cur->func) { - err ("out of system memory \n"); - rc = -ENOMEM; - goto error; - } - memset (slot_cur->func, 0, sizeof (struct pci_func)); - slot_cur->func->busno = slot_cur->bus; - slot_cur->func->device = slot_cur->device; - } - - if ((rc = ibm_unconfigure_device (slot_cur->func))) { - err ("removing from kernel failed... \n"); - err ("Please check to see if it was statically linked or is " - "in use otherwise. (perhaps the driver is not 'hot-removable')\n"); - goto error; - } - - /* If we got here from latch suddenly opening on operating card or - a power fault, there's no power to the card, so cannot - read from it to determine what resources it occupied. This operation - is forbidden anyhow. The best we can do is remove it from kernel - lists at least */ - - if (!flag) { - attn_off (slot_cur); - return 0; - } - - rc = ibmphp_unconfigure_card (&slot_cur, parm); - slot_cur->func = NULL; - debug ("in disable_slot. after unconfigure_card\n"); - if (rc) { - err ("could not unconfigure card.\n"); - goto error; - } - - rc = ibmphp_hpc_writeslot (slot_cur, HPC_SLOT_OFF); - if (rc) - goto error; - - attn_off (slot_cur); - rc = slot_update (&slot_cur); - if (rc) - goto exit; - - rc = ibmphp_update_slot_info (slot_cur); - ibmphp_print_test (); -exit: - return rc; - -error: - /* Need to turn off if was blinking b4 */ - attn_off (slot_cur); - attn_on (slot_cur); - if (slot_update (&slot_cur)) { - rc = -EFAULT; - goto exit; - } - if (flag) - ibmphp_update_slot_info (slot_cur); - goto exit; -} - -struct hotplug_slot_ops ibmphp_hotplug_slot_ops = { - .owner = THIS_MODULE, - .set_attention_status = set_attention_status, - .enable_slot = enable_slot, - .disable_slot = ibmphp_disable_slot, - .hardware_test = NULL, - .get_power_status = get_power_status, - .get_attention_status = get_attention_status, - .get_latch_status = get_latch_status, - .get_adapter_status = get_adapter_present, - .get_max_bus_speed = get_max_bus_speed, - .get_cur_bus_speed = get_cur_bus_speed, -/* .get_max_adapter_speed = get_max_adapter_speed, - .get_bus_name_status = get_bus_name, -*/ -}; - -static void ibmphp_unload (void) -{ - free_slots (); - debug ("after slots \n"); - ibmphp_free_resources (); - debug ("after resources \n"); - ibmphp_free_bus_info_queue (); - debug ("after bus info \n"); - ibmphp_free_ebda_hpc_queue (); - debug ("after ebda hpc \n"); - ibmphp_free_ebda_pci_rsrc_queue (); - debug ("after ebda pci rsrc \n"); - kfree (ibmphp_pci_bus); -} - -static int __init ibmphp_init (void) -{ - struct pci_bus *bus; - int i = 0; - int rc = 0; - - init_flag = 1; - - info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); - - ibmphp_pci_bus = kmalloc (sizeof (*ibmphp_pci_bus), GFP_KERNEL); - if (!ibmphp_pci_bus) { - err ("out of memory\n"); - rc = -ENOMEM; - goto exit; - } - - bus = pci_find_bus(0); - if (!bus) { - err ("Can't find the root pci bus, can not continue\n"); - rc = -ENODEV; - goto error; - } - memcpy (ibmphp_pci_bus, bus, sizeof (*ibmphp_pci_bus)); - - ibmphp_debug = debug; - - ibmphp_hpc_initvars (); - - for (i = 0; i < 16; i++) - irqs[i] = 0; - - if ((rc = ibmphp_access_ebda ())) - goto error; - debug ("after ibmphp_access_ebda ()\n"); - - if ((rc = ibmphp_rsrc_init ())) - goto error; - debug ("AFTER Resource & EBDA INITIALIZATIONS\n"); - - max_slots = get_max_slots (); - - if ((rc = ibmphp_register_pci ())) - goto error; - - if (init_ops ()) { - rc = -ENODEV; - goto error; - } - - ibmphp_print_test (); - if ((rc = ibmphp_hpc_start_poll_thread ())) { - goto error; - } - - /* lock ourselves into memory with a module - * count of -1 so that no one can unload us. */ - module_put(THIS_MODULE); - -exit: - return rc; - -error: - ibmphp_unload (); - goto exit; -} - -static void __exit ibmphp_exit (void) -{ - ibmphp_hpc_stop_poll_thread (); - debug ("after polling\n"); - ibmphp_unload (); - debug ("done\n"); -} - -module_init (ibmphp_init); -module_exit (ibmphp_exit); diff -Nru a/drivers/hotplug/ibmphp_ebda.c b/drivers/hotplug/ibmphp_ebda.c --- a/drivers/hotplug/ibmphp_ebda.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1228 +0,0 @@ -/* - * IBM Hot Plug Controller Driver - * - * Written By: Tong Yu, IBM Corporation - * - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001,2002 IBM Corp. - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "ibmphp.h" - -/* - * POST builds data blocks(in this data block definition, a char-1 - * byte, short(or word)-2 byte, long(dword)-4 byte) in the Extended - * BIOS Data Area which describe the configuration of the hot-plug - * controllers and resources used by the PCI Hot-Plug devices. - * - * This file walks EBDA, maps data block from physical addr, - * reconstruct linked lists about all system resource(MEM, PFM, IO) - * already assigned by POST, as well as linked lists about hot plug - * controllers (ctlr#, slot#, bus&slot features...) - */ - -/* Global lists */ -LIST_HEAD (ibmphp_ebda_pci_rsrc_head); -LIST_HEAD (ibmphp_slot_head); - -/* Local variables */ -static struct ebda_hpc_list *hpc_list_ptr; -static struct ebda_rsrc_list *rsrc_list_ptr; -static struct rio_table_hdr *rio_table_ptr = NULL; -static LIST_HEAD (ebda_hpc_head); -static LIST_HEAD (bus_info_head); -static LIST_HEAD (rio_vg_head); -static LIST_HEAD (rio_lo_head); -static LIST_HEAD (opt_vg_head); -static LIST_HEAD (opt_lo_head); -static void *io_mem; - -/* Local functions */ -static int ebda_rsrc_controller (void); -static int ebda_rsrc_rsrc (void); -static int ebda_rio_table (void); - -static struct ebda_hpc_list * __init alloc_ebda_hpc_list (void) -{ - struct ebda_hpc_list *list; - - list = kmalloc (sizeof (struct ebda_hpc_list), GFP_KERNEL); - if (!list) - return NULL; - memset (list, 0, sizeof (*list)); - return list; -} - -static struct controller *alloc_ebda_hpc (u32 slot_count, u32 bus_count) -{ - struct controller *controller; - struct ebda_hpc_slot *slots; - struct ebda_hpc_bus *buses; - - controller = kmalloc (sizeof (struct controller), GFP_KERNEL); - if (!controller) - return NULL; - memset (controller, 0, sizeof (*controller)); - - slots = kmalloc (sizeof (struct ebda_hpc_slot) * slot_count, GFP_KERNEL); - if (!slots) { - kfree (controller); - return NULL; - } - memset (slots, 0, sizeof (*slots) * slot_count); - controller->slots = slots; - - buses = kmalloc (sizeof (struct ebda_hpc_bus) * bus_count, GFP_KERNEL); - if (!buses) { - kfree (controller->slots); - kfree (controller); - return NULL; - } - memset (buses, 0, sizeof (*buses) * bus_count); - controller->buses = buses; - - return controller; -} - -static void free_ebda_hpc (struct controller *controller) -{ - kfree (controller->slots); - controller->slots = NULL; - kfree (controller->buses); - controller->buses = NULL; - controller->ctrl_dev = NULL; - kfree (controller); -} - -static struct ebda_rsrc_list * __init alloc_ebda_rsrc_list (void) -{ - struct ebda_rsrc_list *list; - - list = kmalloc (sizeof (struct ebda_rsrc_list), GFP_KERNEL); - if (!list) - return NULL; - memset (list, 0, sizeof (*list)); - return list; -} - -static struct ebda_pci_rsrc *alloc_ebda_pci_rsrc (void) -{ - struct ebda_pci_rsrc *resource; - - resource = kmalloc (sizeof (struct ebda_pci_rsrc), GFP_KERNEL); - if (!resource) - return NULL; - memset (resource, 0, sizeof (*resource)); - return resource; -} - -static void __init print_bus_info (void) -{ - struct bus_info *ptr; - struct list_head *ptr1; - - list_for_each (ptr1, &bus_info_head) { - ptr = list_entry (ptr1, struct bus_info, bus_info_list); - debug ("%s - slot_min = %x\n", __FUNCTION__, ptr->slot_min); - debug ("%s - slot_max = %x\n", __FUNCTION__, ptr->slot_max); - debug ("%s - slot_count = %x\n", __FUNCTION__, ptr->slot_count); - debug ("%s - bus# = %x\n", __FUNCTION__, ptr->busno); - debug ("%s - current_speed = %x\n", __FUNCTION__, ptr->current_speed); - debug ("%s - controller_id = %x\n", __FUNCTION__, ptr->controller_id); - - debug ("%s - slots_at_33_conv = %x\n", __FUNCTION__, ptr->slots_at_33_conv); - debug ("%s - slots_at_66_conv = %x\n", __FUNCTION__, ptr->slots_at_66_conv); - debug ("%s - slots_at_66_pcix = %x\n", __FUNCTION__, ptr->slots_at_66_pcix); - debug ("%s - slots_at_100_pcix = %x\n", __FUNCTION__, ptr->slots_at_100_pcix); - debug ("%s - slots_at_133_pcix = %x\n", __FUNCTION__, ptr->slots_at_133_pcix); - - } -} - -static void print_lo_info (void) -{ - struct rio_detail *ptr; - struct list_head *ptr1; - debug ("print_lo_info ---- \n"); - list_for_each (ptr1, &rio_lo_head) { - ptr = list_entry (ptr1, struct rio_detail, rio_detail_list); - debug ("%s - rio_node_id = %x\n", __FUNCTION__, ptr->rio_node_id); - debug ("%s - rio_type = %x\n", __FUNCTION__, ptr->rio_type); - debug ("%s - owner_id = %x\n", __FUNCTION__, ptr->owner_id); - debug ("%s - first_slot_num = %x\n", __FUNCTION__, ptr->first_slot_num); - debug ("%s - wpindex = %x\n", __FUNCTION__, ptr->wpindex); - debug ("%s - chassis_num = %x\n", __FUNCTION__, ptr->chassis_num); - - } -} - -static void print_vg_info (void) -{ - struct rio_detail *ptr; - struct list_head *ptr1; - debug ("%s --- \n", __FUNCTION__); - list_for_each (ptr1, &rio_vg_head) { - ptr = list_entry (ptr1, struct rio_detail, rio_detail_list); - debug ("%s - rio_node_id = %x\n", __FUNCTION__, ptr->rio_node_id); - debug ("%s - rio_type = %x\n", __FUNCTION__, ptr->rio_type); - debug ("%s - owner_id = %x\n", __FUNCTION__, ptr->owner_id); - debug ("%s - first_slot_num = %x\n", __FUNCTION__, ptr->first_slot_num); - debug ("%s - wpindex = %x\n", __FUNCTION__, ptr->wpindex); - debug ("%s - chassis_num = %x\n", __FUNCTION__, ptr->chassis_num); - - } -} - -static void __init print_ebda_pci_rsrc (void) -{ - struct ebda_pci_rsrc *ptr; - struct list_head *ptr1; - - list_for_each (ptr1, &ibmphp_ebda_pci_rsrc_head) { - ptr = list_entry (ptr1, struct ebda_pci_rsrc, ebda_pci_rsrc_list); - debug ("%s - rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n", - __FUNCTION__, ptr->rsrc_type ,ptr->bus_num, ptr->dev_fun,ptr->start_addr, ptr->end_addr); - } -} - -static void __init print_ibm_slot (void) -{ - struct slot *ptr; - struct list_head *ptr1; - - list_for_each (ptr1, &ibmphp_slot_head) { - ptr = list_entry (ptr1, struct slot, ibm_slot_list); - debug ("%s - slot_number: %x \n", __FUNCTION__, ptr->number); - } -} - -static void __init print_opt_vg (void) -{ - struct opt_rio *ptr; - struct list_head *ptr1; - debug ("%s --- \n", __FUNCTION__); - list_for_each (ptr1, &opt_vg_head) { - ptr = list_entry (ptr1, struct opt_rio, opt_rio_list); - debug ("%s - rio_type %x \n", __FUNCTION__, ptr->rio_type); - debug ("%s - chassis_num: %x \n", __FUNCTION__, ptr->chassis_num); - debug ("%s - first_slot_num: %x \n", __FUNCTION__, ptr->first_slot_num); - debug ("%s - middle_num: %x \n", __FUNCTION__, ptr->middle_num); - } -} - -static void __init print_ebda_hpc (void) -{ - struct controller *hpc_ptr; - struct list_head *ptr1; - u16 index; - - list_for_each (ptr1, &ebda_hpc_head) { - - hpc_ptr = list_entry (ptr1, struct controller, ebda_hpc_list); - - for (index = 0; index < hpc_ptr->slot_count; index++) { - debug ("%s - physical slot#: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_num); - debug ("%s - pci bus# of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_bus_num); - debug ("%s - index into ctlr addr: %x\n", __FUNCTION__, hpc_ptr->slots[index].ctl_index); - debug ("%s - cap of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_cap); - } - - for (index = 0; index < hpc_ptr->bus_count; index++) { - debug ("%s - bus# of each bus controlled by this ctlr: %x\n", __FUNCTION__, hpc_ptr->buses[index].bus_num); - } - - debug ("%s - type of hpc: %x\n", __FUNCTION__, hpc_ptr->ctlr_type); - switch (hpc_ptr->ctlr_type) { - case 1: - debug ("%s - bus: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.bus); - debug ("%s - dev_fun: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.dev_fun); - debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq); - break; - - case 0: - debug ("%s - io_start: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_start); - debug ("%s - io_end: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_end); - debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq); - break; - - case 2: - case 4: - debug ("%s - wpegbbar: %lx\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.wpegbbar); - debug ("%s - i2c_addr: %x\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.i2c_addr); - debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq); - break; - } - } -} - -int __init ibmphp_access_ebda (void) -{ - u8 format, num_ctlrs, rio_complete, hs_complete; - u16 ebda_seg, num_entries, next_offset, offset, blk_id, sub_addr, rc, re, rc_id, re_id, base; - - - rio_complete = 0; - hs_complete = 0; - - io_mem = ioremap ((0x40 << 4) + 0x0e, 2); - if (!io_mem ) - return -ENOMEM; - ebda_seg = readw (io_mem); - iounmap (io_mem); - debug ("returned ebda segment: %x\n", ebda_seg); - - io_mem = ioremap (ebda_seg<<4, 65000); - if (!io_mem ) - return -ENOMEM; - next_offset = 0x180; - - for (;;) { - offset = next_offset; - next_offset = readw (io_mem + offset); /* offset of next blk */ - - offset += 2; - if (next_offset == 0) /* 0 indicate it's last blk */ - break; - blk_id = readw (io_mem + offset); /* this blk id */ - - offset += 2; - /* check if it is hot swap block or rio block */ - if (blk_id != 0x4853 && blk_id != 0x4752) - continue; - /* found hs table */ - if (blk_id == 0x4853) { - debug ("now enter hot swap block---\n"); - debug ("hot blk id: %x\n", blk_id); - format = readb (io_mem + offset); - - offset += 1; - if (format != 4) { - iounmap (io_mem); - return -ENODEV; - } - debug ("hot blk format: %x\n", format); - /* hot swap sub blk */ - base = offset; - - sub_addr = base; - re = readw (io_mem + sub_addr); /* next sub blk */ - - sub_addr += 2; - rc_id = readw (io_mem + sub_addr); /* sub blk id */ - - sub_addr += 2; - if (rc_id != 0x5243) { - iounmap (io_mem); - return -ENODEV; - } - /* rc sub blk signature */ - num_ctlrs = readb (io_mem + sub_addr); - - sub_addr += 1; - hpc_list_ptr = alloc_ebda_hpc_list (); - if (!hpc_list_ptr) { - iounmap (io_mem); - return -ENOMEM; - } - hpc_list_ptr->format = format; - hpc_list_ptr->num_ctlrs = num_ctlrs; - hpc_list_ptr->phys_addr = sub_addr; /* offset of RSRC_CONTROLLER blk */ - debug ("info about hpc descriptor---\n"); - debug ("hot blk format: %x\n", format); - debug ("num of controller: %x\n", num_ctlrs); - debug ("offset of hpc data structure enteries: %x\n ", sub_addr); - - sub_addr = base + re; /* re sub blk */ - rc = readw (io_mem + sub_addr); /* next sub blk */ - - sub_addr += 2; - re_id = readw (io_mem + sub_addr); /* sub blk id */ - - sub_addr += 2; - if (re_id != 0x5245) { - iounmap (io_mem); - return -ENODEV; - } - - /* signature of re */ - num_entries = readw (io_mem + sub_addr); - - sub_addr += 2; /* offset of RSRC_ENTRIES blk */ - rsrc_list_ptr = alloc_ebda_rsrc_list (); - if (!rsrc_list_ptr ) { - iounmap (io_mem); - return -ENOMEM; - } - rsrc_list_ptr->format = format; - rsrc_list_ptr->num_entries = num_entries; - rsrc_list_ptr->phys_addr = sub_addr; - - debug ("info about rsrc descriptor---\n"); - debug ("format: %x\n", format); - debug ("num of rsrc: %x\n", num_entries); - debug ("offset of rsrc data structure enteries: %x\n ", sub_addr); - - hs_complete = 1; - } - /* found rio table */ - else if (blk_id == 0x4752) { - debug ("now enter io table ---\n"); - debug ("rio blk id: %x\n", blk_id); - - rio_table_ptr = kmalloc (sizeof (struct rio_table_hdr), GFP_KERNEL); - if (!rio_table_ptr) - return -ENOMEM; - memset (rio_table_ptr, 0, sizeof (struct rio_table_hdr) ); - rio_table_ptr->ver_num = readb (io_mem + offset); - rio_table_ptr->scal_count = readb (io_mem + offset + 1); - rio_table_ptr->riodev_count = readb (io_mem + offset + 2); - rio_table_ptr->offset = offset +3 ; - - debug ("info about rio table hdr ---\n"); - debug ("ver_num: %x\nscal_count: %x\nriodev_count: %x\noffset of rio table: %x\n ", rio_table_ptr->ver_num, rio_table_ptr->scal_count, rio_table_ptr->riodev_count, rio_table_ptr->offset); - - rio_complete = 1; - } - } - - if (!hs_complete && !rio_complete) { - iounmap (io_mem); - return -ENODEV; - } - - if (rio_table_ptr) { - if (rio_complete == 1 && rio_table_ptr->ver_num == 3) { - rc = ebda_rio_table (); - if (rc) { - iounmap (io_mem); - return rc; - } - } - } - rc = ebda_rsrc_controller (); - if (rc) { - iounmap (io_mem); - return rc; - } - - rc = ebda_rsrc_rsrc (); - if (rc) { - iounmap (io_mem); - return rc; - } - - iounmap (io_mem); - return 0; -} - -/* - * map info of scalability details and rio details from physical address - */ -static int __init ebda_rio_table (void) -{ - u16 offset; - u8 i; - struct rio_detail *rio_detail_ptr; - - offset = rio_table_ptr->offset; - offset += 12 * rio_table_ptr->scal_count; - - // we do concern about rio details - for (i = 0; i < rio_table_ptr->riodev_count; i++) { - rio_detail_ptr = kmalloc (sizeof (struct rio_detail), GFP_KERNEL); - if (!rio_detail_ptr) - return -ENOMEM; - memset (rio_detail_ptr, 0, sizeof (struct rio_detail)); - rio_detail_ptr->rio_node_id = readb (io_mem + offset); - rio_detail_ptr->bbar = readl (io_mem + offset + 1); - rio_detail_ptr->rio_type = readb (io_mem + offset + 5); - rio_detail_ptr->owner_id = readb (io_mem + offset + 6); - rio_detail_ptr->port0_node_connect = readb (io_mem + offset + 7); - rio_detail_ptr->port0_port_connect = readb (io_mem + offset + 8); - rio_detail_ptr->port1_node_connect = readb (io_mem + offset + 9); - rio_detail_ptr->port1_port_connect = readb (io_mem + offset + 10); - rio_detail_ptr->first_slot_num = readb (io_mem + offset + 11); - rio_detail_ptr->status = readb (io_mem + offset + 12); - rio_detail_ptr->wpindex = readb (io_mem + offset + 13); - rio_detail_ptr->chassis_num = readb (io_mem + offset + 14); -// debug ("rio_node_id: %x\nbbar: %x\nrio_type: %x\nowner_id: %x\nport0_node: %x\nport0_port: %x\nport1_node: %x\nport1_port: %x\nfirst_slot_num: %x\nstatus: %x\n", rio_detail_ptr->rio_node_id, rio_detail_ptr->bbar, rio_detail_ptr->rio_type, rio_detail_ptr->owner_id, rio_detail_ptr->port0_node_connect, rio_detail_ptr->port0_port_connect, rio_detail_ptr->port1_node_connect, rio_detail_ptr->port1_port_connect, rio_detail_ptr->first_slot_num, rio_detail_ptr->status); - //create linked list of chassis - if (rio_detail_ptr->rio_type == 4 || rio_detail_ptr->rio_type == 5) - list_add (&rio_detail_ptr->rio_detail_list, &rio_vg_head); - //create linked list of expansion box - else if (rio_detail_ptr->rio_type == 6 || rio_detail_ptr->rio_type == 7) - list_add (&rio_detail_ptr->rio_detail_list, &rio_lo_head); - else - // not in my concern - kfree (rio_detail_ptr); - offset += 15; - } - print_lo_info (); - print_vg_info (); - return 0; -} - -/* - * reorganizing linked list of chassis - */ -static struct opt_rio *search_opt_vg (u8 chassis_num) -{ - struct opt_rio *ptr; - struct list_head *ptr1; - list_for_each (ptr1, &opt_vg_head) { - ptr = list_entry (ptr1, struct opt_rio, opt_rio_list); - if (ptr->chassis_num == chassis_num) - return ptr; - } - return NULL; -} - -static int __init combine_wpg_for_chassis (void) -{ - struct opt_rio *opt_rio_ptr = NULL; - struct rio_detail *rio_detail_ptr = NULL; - struct list_head *list_head_ptr = NULL; - - list_for_each (list_head_ptr, &rio_vg_head) { - rio_detail_ptr = list_entry (list_head_ptr, struct rio_detail, rio_detail_list); - opt_rio_ptr = search_opt_vg (rio_detail_ptr->chassis_num); - if (!opt_rio_ptr) { - opt_rio_ptr = (struct opt_rio *) kmalloc (sizeof (struct opt_rio), GFP_KERNEL); - if (!opt_rio_ptr) - return -ENOMEM; - memset (opt_rio_ptr, 0, sizeof (struct opt_rio)); - opt_rio_ptr->rio_type = rio_detail_ptr->rio_type; - opt_rio_ptr->chassis_num = rio_detail_ptr->chassis_num; - opt_rio_ptr->first_slot_num = rio_detail_ptr->first_slot_num; - opt_rio_ptr->middle_num = rio_detail_ptr->first_slot_num; - list_add (&opt_rio_ptr->opt_rio_list, &opt_vg_head); - } else { - opt_rio_ptr->first_slot_num = min (opt_rio_ptr->first_slot_num, rio_detail_ptr->first_slot_num); - opt_rio_ptr->middle_num = max (opt_rio_ptr->middle_num, rio_detail_ptr->first_slot_num); - } - } - print_opt_vg (); - return 0; -} - -/* - * reorgnizing linked list of expansion box - */ -static struct opt_rio_lo *search_opt_lo (u8 chassis_num) -{ - struct opt_rio_lo *ptr; - struct list_head *ptr1; - list_for_each (ptr1, &opt_lo_head) { - ptr = list_entry (ptr1, struct opt_rio_lo, opt_rio_lo_list); - if (ptr->chassis_num == chassis_num) - return ptr; - } - return NULL; -} - -static int combine_wpg_for_expansion (void) -{ - struct opt_rio_lo *opt_rio_lo_ptr = NULL; - struct rio_detail *rio_detail_ptr = NULL; - struct list_head *list_head_ptr = NULL; - - list_for_each (list_head_ptr, &rio_lo_head) { - rio_detail_ptr = list_entry (list_head_ptr, struct rio_detail, rio_detail_list); - opt_rio_lo_ptr = search_opt_lo (rio_detail_ptr->chassis_num); - if (!opt_rio_lo_ptr) { - opt_rio_lo_ptr = (struct opt_rio_lo *) kmalloc (sizeof (struct opt_rio_lo), GFP_KERNEL); - if (!opt_rio_lo_ptr) - return -ENOMEM; - memset (opt_rio_lo_ptr, 0, sizeof (struct opt_rio_lo)); - opt_rio_lo_ptr->rio_type = rio_detail_ptr->rio_type; - opt_rio_lo_ptr->chassis_num = rio_detail_ptr->chassis_num; - opt_rio_lo_ptr->first_slot_num = rio_detail_ptr->first_slot_num; - opt_rio_lo_ptr->middle_num = rio_detail_ptr->first_slot_num; - opt_rio_lo_ptr->pack_count = 1; - - list_add (&opt_rio_lo_ptr->opt_rio_lo_list, &opt_lo_head); - } else { - opt_rio_lo_ptr->first_slot_num = min (opt_rio_lo_ptr->first_slot_num, rio_detail_ptr->first_slot_num); - opt_rio_lo_ptr->middle_num = max (opt_rio_lo_ptr->middle_num, rio_detail_ptr->first_slot_num); - opt_rio_lo_ptr->pack_count = 2; - } - } - return 0; -} - - -/* Since we don't know the max slot number per each chassis, hence go - * through the list of all chassis to find out the range - * Arguments: slot_num, 1st slot number of the chassis we think we are on, - * var (0 = chassis, 1 = expansion box) - */ -static int first_slot_num (u8 slot_num, u8 first_slot, u8 var) -{ - struct opt_rio *opt_vg_ptr = NULL; - struct opt_rio_lo *opt_lo_ptr = NULL; - struct list_head *ptr = NULL; - int rc = 0; - - if (!var) { - list_for_each (ptr, &opt_vg_head) { - opt_vg_ptr = list_entry (ptr, struct opt_rio, opt_rio_list); - if ((first_slot < opt_vg_ptr->first_slot_num) && (slot_num >= opt_vg_ptr->first_slot_num)) { - rc = -ENODEV; - break; - } - } - } else { - list_for_each (ptr, &opt_lo_head) { - opt_lo_ptr = list_entry (ptr, struct opt_rio_lo, opt_rio_lo_list); - if ((first_slot < opt_lo_ptr->first_slot_num) && (slot_num >= opt_lo_ptr->first_slot_num)) { - rc = -ENODEV; - break; - } - } - } - return rc; -} - -static struct opt_rio_lo * find_rxe_num (u8 slot_num) -{ - struct opt_rio_lo *opt_lo_ptr; - struct list_head *ptr; - - list_for_each (ptr, &opt_lo_head) { - opt_lo_ptr = list_entry (ptr, struct opt_rio_lo, opt_rio_lo_list); - //check to see if this slot_num belongs to expansion box - if ((slot_num >= opt_lo_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_lo_ptr->first_slot_num, 1))) - return opt_lo_ptr; - } - return NULL; -} - -static struct opt_rio * find_chassis_num (u8 slot_num) -{ - struct opt_rio *opt_vg_ptr; - struct list_head *ptr; - - list_for_each (ptr, &opt_vg_head) { - opt_vg_ptr = list_entry (ptr, struct opt_rio, opt_rio_list); - //check to see if this slot_num belongs to chassis - if ((slot_num >= opt_vg_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_vg_ptr->first_slot_num, 0))) - return opt_vg_ptr; - } - return NULL; -} - -/* This routine will find out how many slots are in the chassis, so that - * the slot numbers for rxe100 would start from 1, and not from 7, or 6 etc - */ -static u8 calculate_first_slot (u8 slot_num) -{ - u8 first_slot = 1; - struct list_head * list; - struct slot * slot_cur; - - list_for_each (list, &ibmphp_slot_head) { - slot_cur = list_entry (list, struct slot, ibm_slot_list); - if (slot_cur->ctrl) { - if ((slot_cur->ctrl->ctlr_type != 4) && (slot_cur->ctrl->ending_slot_num > first_slot) && (slot_num > slot_cur->ctrl->ending_slot_num)) - first_slot = slot_cur->ctrl->ending_slot_num; - } - } - return first_slot + 1; - -} -static char *create_file_name (struct slot * slot_cur) -{ - struct opt_rio *opt_vg_ptr = NULL; - struct opt_rio_lo *opt_lo_ptr = NULL; - static char str[30]; - int which = 0; /* rxe = 1, chassis = 0 */ - u8 number = 1; /* either chassis or rxe # */ - u8 first_slot = 1; - u8 slot_num; - u8 flag = 0; - - if (!slot_cur) { - err ("Structure passed is empty \n"); - return NULL; - } - - slot_num = slot_cur->number; - - memset (str, 0, sizeof(str)); - - if (rio_table_ptr) { - if (rio_table_ptr->ver_num == 3) { - opt_vg_ptr = find_chassis_num (slot_num); - opt_lo_ptr = find_rxe_num (slot_num); - } - } - if (opt_vg_ptr) { - if (opt_lo_ptr) { - if ((slot_num - opt_vg_ptr->first_slot_num) > (slot_num - opt_lo_ptr->first_slot_num)) { - number = opt_lo_ptr->chassis_num; - first_slot = opt_lo_ptr->first_slot_num; - which = 1; /* it is RXE */ - } else { - first_slot = opt_vg_ptr->first_slot_num; - number = opt_vg_ptr->chassis_num; - which = 0; - } - } else { - first_slot = opt_vg_ptr->first_slot_num; - number = opt_vg_ptr->chassis_num; - which = 0; - } - ++flag; - } else if (opt_lo_ptr) { - number = opt_lo_ptr->chassis_num; - first_slot = opt_lo_ptr->first_slot_num; - which = 1; - ++flag; - } else if (rio_table_ptr) { - if (rio_table_ptr->ver_num == 3) { - /* if both NULL and we DO have correct RIO table in BIOS */ - return NULL; - } - } - if (!flag) { - if (slot_cur->ctrl->ctlr_type == 4) { - first_slot = calculate_first_slot (slot_num); - which = 1; - } else { - which = 0; - } - } - - sprintf(str, "%s%dslot%d", - which == 0 ? "chassis" : "rxe", - number, slot_num - first_slot + 1); - return str; -} - -static struct pci_driver ibmphp_driver; - -/* - * map info (ctlr-id, slot count, slot#.. bus count, bus#, ctlr type...) of - * each hpc from physical address to a list of hot plug controllers based on - * hpc descriptors. - */ -static int __init ebda_rsrc_controller (void) -{ - u16 addr, addr_slot, addr_bus; - u8 ctlr_id, temp, bus_index; - u16 ctlr, slot, bus; - u16 slot_num, bus_num, index; - struct hotplug_slot *hp_slot_ptr; - struct controller *hpc_ptr; - struct ebda_hpc_bus *bus_ptr; - struct ebda_hpc_slot *slot_ptr; - struct bus_info *bus_info_ptr1, *bus_info_ptr2; - int rc; - struct slot *tmp_slot; - struct list_head *list; - - addr = hpc_list_ptr->phys_addr; - for (ctlr = 0; ctlr < hpc_list_ptr->num_ctlrs; ctlr++) { - bus_index = 1; - ctlr_id = readb (io_mem + addr); - addr += 1; - slot_num = readb (io_mem + addr); - - addr += 1; - addr_slot = addr; /* offset of slot structure */ - addr += (slot_num * 4); - - bus_num = readb (io_mem + addr); - - addr += 1; - addr_bus = addr; /* offset of bus */ - addr += (bus_num * 9); /* offset of ctlr_type */ - temp = readb (io_mem + addr); - - addr += 1; - /* init hpc structure */ - hpc_ptr = alloc_ebda_hpc (slot_num, bus_num); - if (!hpc_ptr ) { - rc = -ENOMEM; - goto error_no_hpc; - } - hpc_ptr->ctlr_id = ctlr_id; - hpc_ptr->ctlr_relative_id = ctlr; - hpc_ptr->slot_count = slot_num; - hpc_ptr->bus_count = bus_num; - debug ("now enter ctlr data struture ---\n"); - debug ("ctlr id: %x\n", ctlr_id); - debug ("ctlr_relative_id: %x\n", hpc_ptr->ctlr_relative_id); - debug ("count of slots controlled by this ctlr: %x\n", slot_num); - debug ("count of buses controlled by this ctlr: %x\n", bus_num); - - /* init slot structure, fetch slot, bus, cap... */ - slot_ptr = hpc_ptr->slots; - for (slot = 0; slot < slot_num; slot++) { - slot_ptr->slot_num = readb (io_mem + addr_slot); - slot_ptr->slot_bus_num = readb (io_mem + addr_slot + slot_num); - slot_ptr->ctl_index = readb (io_mem + addr_slot + 2*slot_num); - slot_ptr->slot_cap = readb (io_mem + addr_slot + 3*slot_num); - - // create bus_info lined list --- if only one slot per bus: slot_min = slot_max - - bus_info_ptr2 = ibmphp_find_same_bus_num (slot_ptr->slot_bus_num); - if (!bus_info_ptr2) { - bus_info_ptr1 = (struct bus_info *) kmalloc (sizeof (struct bus_info), GFP_KERNEL); - if (!bus_info_ptr1) { - rc = -ENOMEM; - goto error_no_hp_slot; - } - memset (bus_info_ptr1, 0, sizeof (struct bus_info)); - bus_info_ptr1->slot_min = slot_ptr->slot_num; - bus_info_ptr1->slot_max = slot_ptr->slot_num; - bus_info_ptr1->slot_count += 1; - bus_info_ptr1->busno = slot_ptr->slot_bus_num; - bus_info_ptr1->index = bus_index++; - bus_info_ptr1->current_speed = 0xff; - bus_info_ptr1->current_bus_mode = 0xff; - - bus_info_ptr1->controller_id = hpc_ptr->ctlr_id; - - list_add_tail (&bus_info_ptr1->bus_info_list, &bus_info_head); - - } else { - bus_info_ptr2->slot_min = min (bus_info_ptr2->slot_min, slot_ptr->slot_num); - bus_info_ptr2->slot_max = max (bus_info_ptr2->slot_max, slot_ptr->slot_num); - bus_info_ptr2->slot_count += 1; - - } - - // end of creating the bus_info linked list - - slot_ptr++; - addr_slot += 1; - } - - /* init bus structure */ - bus_ptr = hpc_ptr->buses; - for (bus = 0; bus < bus_num; bus++) { - bus_ptr->bus_num = readb (io_mem + addr_bus + bus); - bus_ptr->slots_at_33_conv = readb (io_mem + addr_bus + bus_num + 8 * bus); - bus_ptr->slots_at_66_conv = readb (io_mem + addr_bus + bus_num + 8 * bus + 1); - - bus_ptr->slots_at_66_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 2); - - bus_ptr->slots_at_100_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 3); - - bus_ptr->slots_at_133_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 4); - - bus_info_ptr2 = ibmphp_find_same_bus_num (bus_ptr->bus_num); - if (bus_info_ptr2) { - bus_info_ptr2->slots_at_33_conv = bus_ptr->slots_at_33_conv; - bus_info_ptr2->slots_at_66_conv = bus_ptr->slots_at_66_conv; - bus_info_ptr2->slots_at_66_pcix = bus_ptr->slots_at_66_pcix; - bus_info_ptr2->slots_at_100_pcix = bus_ptr->slots_at_100_pcix; - bus_info_ptr2->slots_at_133_pcix = bus_ptr->slots_at_133_pcix; - } - bus_ptr++; - } - - hpc_ptr->ctlr_type = temp; - - switch (hpc_ptr->ctlr_type) { - case 1: - hpc_ptr->u.pci_ctlr.bus = readb (io_mem + addr); - hpc_ptr->u.pci_ctlr.dev_fun = readb (io_mem + addr + 1); - hpc_ptr->irq = readb (io_mem + addr + 2); - addr += 3; - debug ("ctrl bus = %x, ctlr devfun = %x, irq = %x\n", - hpc_ptr->u.pci_ctlr.bus, - hpc_ptr->u.pci_ctlr.dev_fun, hpc_ptr->irq); - break; - - case 0: - hpc_ptr->u.isa_ctlr.io_start = readw (io_mem + addr); - hpc_ptr->u.isa_ctlr.io_end = readw (io_mem + addr + 2); - if (!request_region (hpc_ptr->u.isa_ctlr.io_start, - (hpc_ptr->u.isa_ctlr.io_end - hpc_ptr->u.isa_ctlr.io_start + 1), - "ibmphp")) { - rc = -ENODEV; - goto error_no_hp_slot; - } - hpc_ptr->irq = readb (io_mem + addr + 4); - addr += 5; - break; - - case 2: - case 4: - hpc_ptr->u.wpeg_ctlr.wpegbbar = readl (io_mem + addr); - hpc_ptr->u.wpeg_ctlr.i2c_addr = readb (io_mem + addr + 4); - hpc_ptr->irq = readb (io_mem + addr + 5); - addr += 6; - break; - default: - rc = -ENODEV; - goto error_no_hp_slot; - } - - //reorganize chassis' linked list - combine_wpg_for_chassis (); - combine_wpg_for_expansion (); - hpc_ptr->revision = 0xff; - hpc_ptr->options = 0xff; - hpc_ptr->starting_slot_num = hpc_ptr->slots[0].slot_num; - hpc_ptr->ending_slot_num = hpc_ptr->slots[slot_num-1].slot_num; - - // register slots with hpc core as well as create linked list of ibm slot - for (index = 0; index < hpc_ptr->slot_count; index++) { - - hp_slot_ptr = (struct hotplug_slot *) kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL); - if (!hp_slot_ptr) { - rc = -ENOMEM; - goto error_no_hp_slot; - } - memset (hp_slot_ptr, 0, sizeof (struct hotplug_slot)); - - hp_slot_ptr->info = (struct hotplug_slot_info *) kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); - if (!hp_slot_ptr->info) { - rc = -ENOMEM; - goto error_no_hp_info; - } - memset (hp_slot_ptr->info, 0, sizeof (struct hotplug_slot_info)); - - hp_slot_ptr->name = (char *) kmalloc (30, GFP_KERNEL); - if (!hp_slot_ptr->name) { - rc = -ENOMEM; - goto error_no_hp_name; - } - - tmp_slot = kmalloc (sizeof (struct slot), GFP_KERNEL); - if (!tmp_slot) { - rc = -ENOMEM; - goto error_no_slot; - } - memset (tmp_slot, 0, sizeof (*tmp_slot)); - - tmp_slot->flag = TRUE; - - tmp_slot->capabilities = hpc_ptr->slots[index].slot_cap; - if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_133_MAX) == EBDA_SLOT_133_MAX) - tmp_slot->supported_speed = 3; - else if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_100_MAX) == EBDA_SLOT_100_MAX) - tmp_slot->supported_speed = 2; - else if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_66_MAX) == EBDA_SLOT_66_MAX) - tmp_slot->supported_speed = 1; - - if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_PCIX_CAP) == EBDA_SLOT_PCIX_CAP) - tmp_slot->supported_bus_mode = 1; - else - tmp_slot->supported_bus_mode = 0; - - - tmp_slot->bus = hpc_ptr->slots[index].slot_bus_num; - - bus_info_ptr1 = ibmphp_find_same_bus_num (hpc_ptr->slots[index].slot_bus_num); - if (!bus_info_ptr1) { - rc = -ENODEV; - goto error; - } - tmp_slot->bus_on = bus_info_ptr1; - bus_info_ptr1 = NULL; - tmp_slot->ctrl = hpc_ptr; - - tmp_slot->ctlr_index = hpc_ptr->slots[index].ctl_index; - tmp_slot->number = hpc_ptr->slots[index].slot_num; - tmp_slot->hotplug_slot = hp_slot_ptr; - - hp_slot_ptr->private = tmp_slot; - - rc = ibmphp_hpc_fillhpslotinfo (hp_slot_ptr); - if (rc) - goto error; - - rc = ibmphp_init_devno ((struct slot **) &hp_slot_ptr->private); - if (rc) - goto error; - hp_slot_ptr->ops = &ibmphp_hotplug_slot_ops; - - // end of registering ibm slot with hotplug core - - list_add (& ((struct slot *)(hp_slot_ptr->private))->ibm_slot_list, &ibmphp_slot_head); - } - - print_bus_info (); - list_add (&hpc_ptr->ebda_hpc_list, &ebda_hpc_head ); - - } /* each hpc */ - - list_for_each (list, &ibmphp_slot_head) { - tmp_slot = list_entry (list, struct slot, ibm_slot_list); - - snprintf (tmp_slot->hotplug_slot->name, 30, "%s", create_file_name (tmp_slot)); - pci_hp_register (tmp_slot->hotplug_slot); - } - - print_ebda_hpc (); - print_ibm_slot (); - return 0; - -error: - kfree (hp_slot_ptr->private); -error_no_slot: - kfree (hp_slot_ptr->name); -error_no_hp_name: - kfree (hp_slot_ptr->info); -error_no_hp_info: - kfree (hp_slot_ptr); -error_no_hp_slot: - free_ebda_hpc (hpc_ptr); -error_no_hpc: - iounmap (io_mem); - return rc; -} - -/* - * map info (bus, devfun, start addr, end addr..) of i/o, memory, - * pfm from the physical addr to a list of resource. - */ -static int __init ebda_rsrc_rsrc (void) -{ - u16 addr; - short rsrc; - u8 type, rsrc_type; - struct ebda_pci_rsrc *rsrc_ptr; - - addr = rsrc_list_ptr->phys_addr; - debug ("now entering rsrc land\n"); - debug ("offset of rsrc: %x\n", rsrc_list_ptr->phys_addr); - - for (rsrc = 0; rsrc < rsrc_list_ptr->num_entries; rsrc++) { - type = readb (io_mem + addr); - - addr += 1; - rsrc_type = type & EBDA_RSRC_TYPE_MASK; - - if (rsrc_type == EBDA_IO_RSRC_TYPE) { - rsrc_ptr = alloc_ebda_pci_rsrc (); - if (!rsrc_ptr) { - iounmap (io_mem); - return -ENOMEM; - } - rsrc_ptr->rsrc_type = type; - - rsrc_ptr->bus_num = readb (io_mem + addr); - rsrc_ptr->dev_fun = readb (io_mem + addr + 1); - rsrc_ptr->start_addr = readw (io_mem + addr + 2); - rsrc_ptr->end_addr = readw (io_mem + addr + 4); - addr += 6; - - debug ("rsrc from io type ----\n"); - debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n", - rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr); - - list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head); - } - - if (rsrc_type == EBDA_MEM_RSRC_TYPE || rsrc_type == EBDA_PFM_RSRC_TYPE) { - rsrc_ptr = alloc_ebda_pci_rsrc (); - if (!rsrc_ptr ) { - iounmap (io_mem); - return -ENOMEM; - } - rsrc_ptr->rsrc_type = type; - - rsrc_ptr->bus_num = readb (io_mem + addr); - rsrc_ptr->dev_fun = readb (io_mem + addr + 1); - rsrc_ptr->start_addr = readl (io_mem + addr + 2); - rsrc_ptr->end_addr = readl (io_mem + addr + 6); - addr += 10; - - debug ("rsrc from mem or pfm ---\n"); - debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n", - rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr); - - list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head); - } - } - kfree (rsrc_list_ptr); - rsrc_list_ptr = NULL; - print_ebda_pci_rsrc (); - return 0; -} - -u16 ibmphp_get_total_controllers (void) -{ - return hpc_list_ptr->num_ctlrs; -} - -struct slot *ibmphp_get_slot_from_physical_num (u8 physical_num) -{ - struct slot *slot; - struct list_head *list; - - list_for_each (list, &ibmphp_slot_head) { - slot = list_entry (list, struct slot, ibm_slot_list); - if (slot->number == physical_num) - return slot; - } - return NULL; -} - -/* To find: - * - the smallest slot number - * - the largest slot number - * - the total number of the slots based on each bus - * (if only one slot per bus slot_min = slot_max ) - */ -struct bus_info *ibmphp_find_same_bus_num (u32 num) -{ - struct bus_info *ptr; - struct list_head *ptr1; - - list_for_each (ptr1, &bus_info_head) { - ptr = list_entry (ptr1, struct bus_info, bus_info_list); - if (ptr->busno == num) - return ptr; - } - return NULL; -} - -/* Finding relative bus number, in order to map corresponding - * bus register - */ -int ibmphp_get_bus_index (u8 num) -{ - struct bus_info *ptr; - struct list_head *ptr1; - - list_for_each (ptr1, &bus_info_head) { - ptr = list_entry (ptr1, struct bus_info, bus_info_list); - if (ptr->busno == num) - return ptr->index; - } - return -ENODEV; -} - -void ibmphp_free_bus_info_queue (void) -{ - struct bus_info *bus_info; - struct list_head *list; - struct list_head *next; - - list_for_each_safe (list, next, &bus_info_head ) { - bus_info = list_entry (list, struct bus_info, bus_info_list); - kfree (bus_info); - } -} - -void ibmphp_free_ebda_hpc_queue (void) -{ - struct controller *controller = NULL; - struct list_head *list; - struct list_head *next; - int pci_flag = 0; - - list_for_each_safe (list, next, &ebda_hpc_head) { - controller = list_entry (list, struct controller, ebda_hpc_list); - if (controller->ctlr_type == 0) - release_region (controller->u.isa_ctlr.io_start, (controller->u.isa_ctlr.io_end - controller->u.isa_ctlr.io_start + 1)); - else if ((controller->ctlr_type == 1) && (!pci_flag)) { - ++pci_flag; - pci_unregister_driver (&ibmphp_driver); - } - free_ebda_hpc (controller); - } -} - -void ibmphp_free_ebda_pci_rsrc_queue (void) -{ - struct ebda_pci_rsrc *resource; - struct list_head *list; - struct list_head *next; - - list_for_each_safe (list, next, &ibmphp_ebda_pci_rsrc_head) { - resource = list_entry (list, struct ebda_pci_rsrc, ebda_pci_rsrc_list); - kfree (resource); - resource = NULL; - } -} - -static struct pci_device_id id_table[] __devinitdata = { - { - .vendor = PCI_VENDOR_ID_IBM, - .device = HPC_DEVICE_ID, - .subvendor = PCI_VENDOR_ID_IBM, - .subdevice = HPC_SUBSYSTEM_ID, - .class = ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00), - }, {} -}; - -MODULE_DEVICE_TABLE(pci, id_table); - -static int ibmphp_probe (struct pci_dev *, const struct pci_device_id *); -static struct pci_driver ibmphp_driver = { - .name = "ibmphp", - .id_table = id_table, - .probe = ibmphp_probe, -}; - -int ibmphp_register_pci (void) -{ - struct controller *ctrl; - struct list_head *tmp; - int rc = 0; - - list_for_each (tmp, &ebda_hpc_head) { - ctrl = list_entry (tmp, struct controller, ebda_hpc_list); - if (ctrl->ctlr_type == 1) { - rc = pci_module_init (&ibmphp_driver); - break; - } - } - return rc; -} -static int ibmphp_probe (struct pci_dev * dev, const struct pci_device_id *ids) -{ - struct controller *ctrl; - struct list_head *tmp; - - debug ("inside ibmphp_probe \n"); - - list_for_each (tmp, &ebda_hpc_head) { - ctrl = list_entry (tmp, struct controller, ebda_hpc_list); - if (ctrl->ctlr_type == 1) { - if ((dev->devfn == ctrl->u.pci_ctlr.dev_fun) && (dev->bus->number == ctrl->u.pci_ctlr.bus)) { - ctrl->ctrl_dev = dev; - debug ("found device!!! \n"); - debug ("dev->device = %x, dev->subsystem_device = %x\n", dev->device, dev->subsystem_device); - return 0; - } - } - } - return -ENODEV; -} - diff -Nru a/drivers/hotplug/ibmphp_hpc.c b/drivers/hotplug/ibmphp_hpc.c --- a/drivers/hotplug/ibmphp_hpc.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1228 +0,0 @@ -/* - * IBM Hot Plug Controller Driver - * - * Written By: Jyoti Shah, IBM Corporation - * - * Copyright (c) 2001-2002 IBM Corp. - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - * - * - */ - -#include -#include -#include -#include -#include -#include -#include "ibmphp.h" - -static int to_debug = FALSE; -#define debug_polling(fmt, arg...) do { if (to_debug) debug (fmt, arg); } while (0) - -//---------------------------------------------------------------------------- -// timeout values -//---------------------------------------------------------------------------- -#define CMD_COMPLETE_TOUT_SEC 60 // give HPC 60 sec to finish cmd -#define HPC_CTLR_WORKING_TOUT 60 // give HPC 60 sec to finish cmd -#define HPC_GETACCESS_TIMEOUT 60 // seconds -#define POLL_INTERVAL_SEC 2 // poll HPC every 2 seconds -#define POLL_LATCH_CNT 5 // poll latch 5 times, then poll slots - -//---------------------------------------------------------------------------- -// Winnipeg Architected Register Offsets -//---------------------------------------------------------------------------- -#define WPG_I2CMBUFL_OFFSET 0x08 // I2C Message Buffer Low -#define WPG_I2CMOSUP_OFFSET 0x10 // I2C Master Operation Setup Reg -#define WPG_I2CMCNTL_OFFSET 0x20 // I2C Master Control Register -#define WPG_I2CPARM_OFFSET 0x40 // I2C Parameter Register -#define WPG_I2CSTAT_OFFSET 0x70 // I2C Status Register - -//---------------------------------------------------------------------------- -// Winnipeg Store Type commands (Add this commands to the register offset) -//---------------------------------------------------------------------------- -#define WPG_I2C_AND 0x1000 // I2C AND operation -#define WPG_I2C_OR 0x2000 // I2C OR operation - -//---------------------------------------------------------------------------- -// Command set for I2C Master Operation Setup Regisetr -//---------------------------------------------------------------------------- -#define WPG_READATADDR_MASK 0x00010000 // read,bytes,I2C shifted,index -#define WPG_WRITEATADDR_MASK 0x40010000 // write,bytes,I2C shifted,index -#define WPG_READDIRECT_MASK 0x10010000 -#define WPG_WRITEDIRECT_MASK 0x60010000 - - -//---------------------------------------------------------------------------- -// bit masks for I2C Master Control Register -//---------------------------------------------------------------------------- -#define WPG_I2CMCNTL_STARTOP_MASK 0x00000002 // Start the Operation - -//---------------------------------------------------------------------------- -// -//---------------------------------------------------------------------------- -#define WPG_I2C_IOREMAP_SIZE 0x2044 // size of linear address interval - -//---------------------------------------------------------------------------- -// command index -//---------------------------------------------------------------------------- -#define WPG_1ST_SLOT_INDEX 0x01 // index - 1st slot for ctlr -#define WPG_CTLR_INDEX 0x0F // index - ctlr -#define WPG_1ST_EXTSLOT_INDEX 0x10 // index - 1st ext slot for ctlr -#define WPG_1ST_BUS_INDEX 0x1F // index - 1st bus for ctlr - -//---------------------------------------------------------------------------- -// macro utilities -//---------------------------------------------------------------------------- -// if bits 20,22,25,26,27,29,30 are OFF return TRUE -#define HPC_I2CSTATUS_CHECK(s) ((u8)((s & 0x00000A76) ? FALSE : TRUE)) - -//---------------------------------------------------------------------------- -// global variables -//---------------------------------------------------------------------------- -static int ibmphp_shutdown; -static int tid_poll; -static struct semaphore sem_hpcaccess; // lock access to HPC -static struct semaphore semOperations; // lock all operations and - // access to data structures -static struct semaphore sem_exit; // make sure polling thread goes away -//---------------------------------------------------------------------------- -// local function prototypes -//---------------------------------------------------------------------------- -static u8 i2c_ctrl_read (struct controller *, void *, u8); -static u8 i2c_ctrl_write (struct controller *, void *, u8, u8); -static u8 hpc_writecmdtoindex (u8, u8); -static u8 hpc_readcmdtoindex (u8, u8); -static void get_hpc_access (void); -static void free_hpc_access (void); -static void poll_hpc (void); -static int update_slot (struct slot *, u8); -static int process_changeinstatus (struct slot *, struct slot *); -static int process_changeinlatch (u8, u8, struct controller *); -static int hpc_poll_thread (void *); -static int hpc_wait_ctlr_notworking (int, struct controller *, void *, u8 *); -//---------------------------------------------------------------------------- - - -/*---------------------------------------------------------------------- -* Name: ibmphp_hpc_initvars -* -* Action: initialize semaphores and variables -*---------------------------------------------------------------------*/ -void __init ibmphp_hpc_initvars (void) -{ - debug ("%s - Entry\n", __FUNCTION__); - - init_MUTEX (&sem_hpcaccess); - init_MUTEX (&semOperations); - init_MUTEX_LOCKED (&sem_exit); - to_debug = FALSE; - ibmphp_shutdown = FALSE; - tid_poll = 0; - - debug ("%s - Exit\n", __FUNCTION__); -} - -/*---------------------------------------------------------------------- -* Name: i2c_ctrl_read -* -* Action: read from HPC over I2C -* -*---------------------------------------------------------------------*/ -static u8 i2c_ctrl_read (struct controller *ctlr_ptr, void *WPGBbar, u8 index) -{ - u8 status; - int i; - void *wpg_addr; // base addr + offset - ulong wpg_data, // data to/from WPG LOHI format - ultemp, data; // actual data HILO format - - - debug_polling ("%s - Entry WPGBbar[%lx] index[%x] \n", __FUNCTION__, (ulong) WPGBbar, index); - - //-------------------------------------------------------------------- - // READ - step 1 - // read at address, byte length, I2C address (shifted), index - // or read direct, byte length, index - if (ctlr_ptr->ctlr_type == 0x02) { - data = WPG_READATADDR_MASK; - // fill in I2C address - ultemp = (ulong) ctlr_ptr->u.wpeg_ctlr.i2c_addr; - ultemp = ultemp >> 1; - data |= (ultemp << 8); - - // fill in index - data |= (ulong) index; - } else if (ctlr_ptr->ctlr_type == 0x04) { - data = WPG_READDIRECT_MASK; - - // fill in index - ultemp = (ulong) index; - ultemp = ultemp << 8; - data |= ultemp; - } else { - err ("this controller type is not supported \n"); - return HPC_ERROR; - } - - wpg_data = swab32 (data); // swap data before writing - (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMOSUP_OFFSET; - writel (wpg_data, wpg_addr); - - //-------------------------------------------------------------------- - // READ - step 2 : clear the message buffer - data = 0x00000000; - wpg_data = swab32 (data); - (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET; - writel (wpg_data, wpg_addr); - - //-------------------------------------------------------------------- - // READ - step 3 : issue start operation, I2C master control bit 30:ON - // 2020 : [20] OR operation at [20] offset 0x20 - data = WPG_I2CMCNTL_STARTOP_MASK; - wpg_data = swab32 (data); - (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET + (ulong) WPG_I2C_OR; - writel (wpg_data, wpg_addr); - - //-------------------------------------------------------------------- - // READ - step 4 : wait until start operation bit clears - i = CMD_COMPLETE_TOUT_SEC; - while (i) { - long_delay (1 * HZ / 100); - (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET; - wpg_data = readl (wpg_addr); - data = swab32 (wpg_data); - if (!(data & WPG_I2CMCNTL_STARTOP_MASK)) - break; - i--; - } - if (i == 0) { - debug ("%s - Error : WPG timeout\n", __FUNCTION__); - return HPC_ERROR; - } - //-------------------------------------------------------------------- - // READ - step 5 : read I2C status register - i = CMD_COMPLETE_TOUT_SEC; - while (i) { - long_delay (1 * HZ / 100); - (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CSTAT_OFFSET; - wpg_data = readl (wpg_addr); - data = swab32 (wpg_data); - if (HPC_I2CSTATUS_CHECK (data)) - break; - i--; - } - if (i == 0) { - debug ("ctrl_read - Exit Error:I2C timeout\n"); - return HPC_ERROR; - } - - //-------------------------------------------------------------------- - // READ - step 6 : get DATA - (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET; - wpg_data = readl (wpg_addr); - data = swab32 (wpg_data); - - status = (u8) data; - - debug_polling ("%s - Exit index[%x] status[%x]\n", __FUNCTION__, index, status); - - return (status); -} - -/*---------------------------------------------------------------------- -* Name: i2c_ctrl_write -* -* Action: write to HPC over I2C -* -* Return 0 or error codes -*---------------------------------------------------------------------*/ -static u8 i2c_ctrl_write (struct controller *ctlr_ptr, void *WPGBbar, u8 index, u8 cmd) -{ - u8 rc; - void *wpg_addr; // base addr + offset - ulong wpg_data, // data to/from WPG LOHI format - ultemp, data; // actual data HILO format - int i; - - - debug_polling ("%s - Entry WPGBbar[%lx] index[%x] cmd[%x]\n", __FUNCTION__, (ulong) WPGBbar, index, cmd); - - rc = 0; - //-------------------------------------------------------------------- - // WRITE - step 1 - // write at address, byte length, I2C address (shifted), index - // or write direct, byte length, index - data = 0x00000000; - - if (ctlr_ptr->ctlr_type == 0x02) { - data = WPG_WRITEATADDR_MASK; - // fill in I2C address - ultemp = (ulong) ctlr_ptr->u.wpeg_ctlr.i2c_addr; - ultemp = ultemp >> 1; - data |= (ultemp << 8); - - // fill in index - data |= (ulong) index; - } else if (ctlr_ptr->ctlr_type == 0x04) { - data = WPG_WRITEDIRECT_MASK; - - // fill in index - ultemp = (ulong) index; - ultemp = ultemp << 8; - data |= ultemp; - } else { - err ("this controller type is not supported \n"); - return HPC_ERROR; - } - - wpg_data = swab32 (data); // swap data before writing - (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMOSUP_OFFSET; - writel (wpg_data, wpg_addr); - - //-------------------------------------------------------------------- - // WRITE - step 2 : clear the message buffer - data = 0x00000000 | (ulong) cmd; - wpg_data = swab32 (data); - (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET; - writel (wpg_data, wpg_addr); - - //-------------------------------------------------------------------- - // WRITE - step 3 : issue start operation,I2C master control bit 30:ON - // 2020 : [20] OR operation at [20] offset 0x20 - data = WPG_I2CMCNTL_STARTOP_MASK; - wpg_data = swab32 (data); - (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET + (ulong) WPG_I2C_OR; - writel (wpg_data, wpg_addr); - - //-------------------------------------------------------------------- - // WRITE - step 4 : wait until start operation bit clears - i = CMD_COMPLETE_TOUT_SEC; - while (i) { - long_delay (1 * HZ / 100); - (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET; - wpg_data = readl (wpg_addr); - data = swab32 (wpg_data); - if (!(data & WPG_I2CMCNTL_STARTOP_MASK)) - break; - i--; - } - if (i == 0) { - debug ("%s - Exit Error:WPG timeout\n", __FUNCTION__); - rc = HPC_ERROR; - } - - //-------------------------------------------------------------------- - // WRITE - step 5 : read I2C status register - i = CMD_COMPLETE_TOUT_SEC; - while (i) { - long_delay (1 * HZ / 100); - (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CSTAT_OFFSET; - wpg_data = readl (wpg_addr); - data = swab32 (wpg_data); - if (HPC_I2CSTATUS_CHECK (data)) - break; - i--; - } - if (i == 0) { - debug ("ctrl_read - Error : I2C timeout\n"); - rc = HPC_ERROR; - } - - debug_polling ("%s Exit rc[%x]\n", __FUNCTION__, rc); - return (rc); -} - -//------------------------------------------------------------ -// Read from ISA type HPC -//------------------------------------------------------------ -static u8 isa_ctrl_read (struct controller *ctlr_ptr, u8 offset) -{ - u16 start_address; - u16 end_address; - u8 data; - - start_address = ctlr_ptr->u.isa_ctlr.io_start; - end_address = ctlr_ptr->u.isa_ctlr.io_end; - data = inb (start_address + offset); - return data; -} - -//-------------------------------------------------------------- -// Write to ISA type HPC -//-------------------------------------------------------------- -static void isa_ctrl_write (struct controller *ctlr_ptr, u8 offset, u8 data) -{ - u16 start_address; - u16 port_address; - - start_address = ctlr_ptr->u.isa_ctlr.io_start; - port_address = start_address + (u16) offset; - outb (data, port_address); -} - -static u8 pci_ctrl_read (struct controller *ctrl, u8 offset) -{ - u8 data = 0x00; - debug ("inside pci_ctrl_read\n"); - if (ctrl->ctrl_dev) - pci_read_config_byte (ctrl->ctrl_dev, HPC_PCI_OFFSET + offset, &data); - return data; -} - -static u8 pci_ctrl_write (struct controller *ctrl, u8 offset, u8 data) -{ - u8 rc = -ENODEV; - debug ("inside pci_ctrl_write\n"); - if (ctrl->ctrl_dev) { - pci_write_config_byte (ctrl->ctrl_dev, HPC_PCI_OFFSET + offset, data); - rc = 0; - } - return rc; -} - -static u8 ctrl_read (struct controller *ctlr, void *base, u8 offset) -{ - u8 rc; - switch (ctlr->ctlr_type) { - case 0: - rc = isa_ctrl_read (ctlr, offset); - break; - case 1: - rc = pci_ctrl_read (ctlr, offset); - break; - case 2: - case 4: - rc = i2c_ctrl_read (ctlr, base, offset); - break; - default: - return -ENODEV; - } - return rc; -} - -static u8 ctrl_write (struct controller *ctlr, void *base, u8 offset, u8 data) -{ - u8 rc = 0; - switch (ctlr->ctlr_type) { - case 0: - isa_ctrl_write(ctlr, offset, data); - break; - case 1: - rc = pci_ctrl_write (ctlr, offset, data); - break; - case 2: - case 4: - rc = i2c_ctrl_write(ctlr, base, offset, data); - break; - default: - return -ENODEV; - } - return rc; -} -/*---------------------------------------------------------------------- -* Name: hpc_writecmdtoindex() -* -* Action: convert a write command to proper index within a controller -* -* Return index, HPC_ERROR -*---------------------------------------------------------------------*/ -static u8 hpc_writecmdtoindex (u8 cmd, u8 index) -{ - u8 rc; - - switch (cmd) { - case HPC_CTLR_ENABLEIRQ: // 0x00.N.15 - case HPC_CTLR_CLEARIRQ: // 0x06.N.15 - case HPC_CTLR_RESET: // 0x07.N.15 - case HPC_CTLR_IRQSTEER: // 0x08.N.15 - case HPC_CTLR_DISABLEIRQ: // 0x01.N.15 - case HPC_ALLSLOT_ON: // 0x11.N.15 - case HPC_ALLSLOT_OFF: // 0x12.N.15 - rc = 0x0F; - break; - - case HPC_SLOT_OFF: // 0x02.Y.0-14 - case HPC_SLOT_ON: // 0x03.Y.0-14 - case HPC_SLOT_ATTNOFF: // 0x04.N.0-14 - case HPC_SLOT_ATTNON: // 0x05.N.0-14 - case HPC_SLOT_BLINKLED: // 0x13.N.0-14 - rc = index; - break; - - case HPC_BUS_33CONVMODE: - case HPC_BUS_66CONVMODE: - case HPC_BUS_66PCIXMODE: - case HPC_BUS_100PCIXMODE: - case HPC_BUS_133PCIXMODE: - rc = index + WPG_1ST_BUS_INDEX - 1; - break; - - default: - err ("hpc_writecmdtoindex - Error invalid cmd[%x]\n", cmd); - rc = HPC_ERROR; - } - - return rc; -} - -/*---------------------------------------------------------------------- -* Name: hpc_readcmdtoindex() -* -* Action: convert a read command to proper index within a controller -* -* Return index, HPC_ERROR -*---------------------------------------------------------------------*/ -static u8 hpc_readcmdtoindex (u8 cmd, u8 index) -{ - u8 rc; - - switch (cmd) { - case READ_CTLRSTATUS: - rc = 0x0F; - break; - case READ_SLOTSTATUS: - case READ_ALLSTAT: - rc = index; - break; - case READ_EXTSLOTSTATUS: - rc = index + WPG_1ST_EXTSLOT_INDEX; - break; - case READ_BUSSTATUS: - rc = index + WPG_1ST_BUS_INDEX - 1; - break; - case READ_SLOTLATCHLOWREG: - rc = 0x28; - break; - case READ_REVLEVEL: - rc = 0x25; - break; - case READ_HPCOPTIONS: - rc = 0x27; - break; - default: - rc = HPC_ERROR; - } - return rc; -} - -/*---------------------------------------------------------------------- -* Name: HPCreadslot() -* -* Action: issue a READ command to HPC -* -* Input: pslot - can not be NULL for READ_ALLSTAT -* pstatus - can be NULL for READ_ALLSTAT -* -* Return 0 or error codes -*---------------------------------------------------------------------*/ -int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus) -{ - void *wpg_bbar = NULL; - struct controller *ctlr_ptr; - struct list_head *pslotlist; - u8 index, status; - int rc = 0; - int busindex; - - debug_polling ("%s - Entry pslot[%lx] cmd[%x] pstatus[%lx]\n", __FUNCTION__, (ulong) pslot, cmd, (ulong) pstatus); - - if ((pslot == NULL) - || ((pstatus == NULL) && (cmd != READ_ALLSTAT) && (cmd != READ_BUSSTATUS))) { - rc = -EINVAL; - err ("%s - Error invalid pointer, rc[%d]\n", __FUNCTION__, rc); - return rc; - } - - if (cmd == READ_BUSSTATUS) { - busindex = ibmphp_get_bus_index (pslot->bus); - if (busindex < 0) { - rc = -EINVAL; - err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc); - return rc; - } else - index = (u8) busindex; - } else - index = pslot->ctlr_index; - - index = hpc_readcmdtoindex (cmd, index); - - if (index == HPC_ERROR) { - rc = -EINVAL; - err ("%s - Exit Error:invalid index, rc[%d]\n", __FUNCTION__, rc); - return rc; - } - - ctlr_ptr = pslot->ctrl; - - get_hpc_access (); - - //-------------------------------------------------------------------- - // map physical address to logical address - //-------------------------------------------------------------------- - if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) - wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE); - - //-------------------------------------------------------------------- - // check controller status before reading - //-------------------------------------------------------------------- - rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status); - if (!rc) { - switch (cmd) { - case READ_ALLSTAT: - // update the slot structure - pslot->ctrl->status = status; - pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index); - rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, - &status); - if (!rc) - pslot->ext_status = ctrl_read (ctlr_ptr, wpg_bbar, index + WPG_1ST_EXTSLOT_INDEX); - - break; - - case READ_SLOTSTATUS: - // DO NOT update the slot structure - *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); - break; - - case READ_EXTSLOTSTATUS: - // DO NOT update the slot structure - *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); - break; - - case READ_CTLRSTATUS: - // DO NOT update the slot structure - *pstatus = status; - break; - - case READ_BUSSTATUS: - pslot->busstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); - break; - case READ_REVLEVEL: - *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); - break; - case READ_HPCOPTIONS: - *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); - break; - case READ_SLOTLATCHLOWREG: - // DO NOT update the slot structure - *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); - break; - - // Not used - case READ_ALLSLOT: - list_for_each (pslotlist, &ibmphp_slot_head) { - pslot = list_entry (pslotlist, struct slot, ibm_slot_list); - index = pslot->ctlr_index; - rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, - wpg_bbar, &status); - if (!rc) { - pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index); - rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, - ctlr_ptr, wpg_bbar, &status); - if (!rc) - pslot->ext_status = - ctrl_read (ctlr_ptr, wpg_bbar, - index + WPG_1ST_EXTSLOT_INDEX); - } else { - err ("%s - Error ctrl_read failed\n", __FUNCTION__); - rc = -EINVAL; - break; - } - } - break; - default: - rc = -EINVAL; - break; - } - } - //-------------------------------------------------------------------- - // cleanup - //-------------------------------------------------------------------- - - // remove physical to logical address mapping - if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) - iounmap (wpg_bbar); - - free_hpc_access (); - - debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc); - return rc; -} - -/*---------------------------------------------------------------------- -* Name: ibmphp_hpc_writeslot() -* -* Action: issue a WRITE command to HPC -*---------------------------------------------------------------------*/ -int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd) -{ - void *wpg_bbar = NULL; - struct controller *ctlr_ptr; - u8 index, status; - int busindex; - u8 done; - int rc = 0; - int timeout; - - debug_polling ("%s - Entry pslot[%lx] cmd[%x]\n", __FUNCTION__, (ulong) pslot, cmd); - if (pslot == NULL) { - rc = -EINVAL; - err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc); - return rc; - } - - if ((cmd == HPC_BUS_33CONVMODE) || (cmd == HPC_BUS_66CONVMODE) || - (cmd == HPC_BUS_66PCIXMODE) || (cmd == HPC_BUS_100PCIXMODE) || - (cmd == HPC_BUS_133PCIXMODE)) { - busindex = ibmphp_get_bus_index (pslot->bus); - if (busindex < 0) { - rc = -EINVAL; - err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc); - return rc; - } else - index = (u8) busindex; - } else - index = pslot->ctlr_index; - - index = hpc_writecmdtoindex (cmd, index); - - if (index == HPC_ERROR) { - rc = -EINVAL; - err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc); - return rc; - } - - ctlr_ptr = pslot->ctrl; - - get_hpc_access (); - - //-------------------------------------------------------------------- - // map physical address to logical address - //-------------------------------------------------------------------- - if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) { - wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE); - - debug ("%s - ctlr id[%x] physical[%lx] logical[%lx] i2c[%x]\n", __FUNCTION__, - ctlr_ptr->ctlr_id, (ulong) (ctlr_ptr->u.wpeg_ctlr.wpegbbar), (ulong) wpg_bbar, - ctlr_ptr->u.wpeg_ctlr.i2c_addr); - } - //-------------------------------------------------------------------- - // check controller status before writing - //-------------------------------------------------------------------- - rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status); - if (!rc) { - - ctrl_write (ctlr_ptr, wpg_bbar, index, cmd); - - //-------------------------------------------------------------------- - // check controller is still not working on the command - //-------------------------------------------------------------------- - timeout = CMD_COMPLETE_TOUT_SEC; - done = FALSE; - while (!done) { - rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, - &status); - if (!rc) { - if (NEEDTOCHECK_CMDSTATUS (cmd)) { - if (CTLR_FINISHED (status) == HPC_CTLR_FINISHED_YES) - done = TRUE; - } else - done = TRUE; - } - if (!done) { - long_delay (1 * HZ); - if (timeout < 1) { - done = TRUE; - err ("%s - Error command complete timeout\n", __FUNCTION__); - rc = -EFAULT; - } else - timeout--; - } - } - ctlr_ptr->status = status; - } - // cleanup - - // remove physical to logical address mapping - if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) - iounmap (wpg_bbar); - free_hpc_access (); - - debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc); - return rc; -} - -/*---------------------------------------------------------------------- -* Name: get_hpc_access() -* -* Action: make sure only one process can access HPC at one time -*---------------------------------------------------------------------*/ -static void get_hpc_access (void) -{ - down (&sem_hpcaccess); -} - -/*---------------------------------------------------------------------- -* Name: free_hpc_access() -*---------------------------------------------------------------------*/ -void free_hpc_access (void) -{ - up (&sem_hpcaccess); -} - -/*---------------------------------------------------------------------- -* Name: ibmphp_lock_operations() -* -* Action: make sure only one process can change the data structure -*---------------------------------------------------------------------*/ -void ibmphp_lock_operations (void) -{ - down (&semOperations); - to_debug = TRUE; -} - -/*---------------------------------------------------------------------- -* Name: ibmphp_unlock_operations() -*---------------------------------------------------------------------*/ -void ibmphp_unlock_operations (void) -{ - debug ("%s - Entry\n", __FUNCTION__); - up (&semOperations); - to_debug = FALSE; - debug ("%s - Exit\n", __FUNCTION__); -} - -/*---------------------------------------------------------------------- -* Name: poll_hpc() -*---------------------------------------------------------------------*/ -#define POLL_LATCH_REGISTER 0 -#define POLL_SLOTS 1 -#define POLL_SLEEP 2 -static void poll_hpc (void) -{ - struct slot myslot; - struct slot *pslot = NULL; - struct list_head *pslotlist; - int rc; - int poll_state = POLL_LATCH_REGISTER; - u8 oldlatchlow = 0x00; - u8 curlatchlow = 0x00; - int poll_count = 0; - u8 ctrl_count = 0x00; - - debug ("%s - Entry\n", __FUNCTION__); - - while (!ibmphp_shutdown) { - if (ibmphp_shutdown) - break; - - /* try to get the lock to do some kind of harware access */ - down (&semOperations); - - switch (poll_state) { - case POLL_LATCH_REGISTER: - oldlatchlow = curlatchlow; - ctrl_count = 0x00; - list_for_each (pslotlist, &ibmphp_slot_head) { - if (ctrl_count >= ibmphp_get_total_controllers()) - break; - pslot = list_entry (pslotlist, struct slot, ibm_slot_list); - if (pslot->ctrl->ctlr_relative_id == ctrl_count) { - ctrl_count++; - if (READ_SLOT_LATCH (pslot->ctrl)) { - rc = ibmphp_hpc_readslot (pslot, - READ_SLOTLATCHLOWREG, - &curlatchlow); - if (oldlatchlow != curlatchlow) - process_changeinlatch (oldlatchlow, - curlatchlow, - pslot->ctrl); - } - } - } - ++poll_count; - poll_state = POLL_SLEEP; - break; - case POLL_SLOTS: - list_for_each (pslotlist, &ibmphp_slot_head) { - pslot = list_entry (pslotlist, struct slot, ibm_slot_list); - // make a copy of the old status - memcpy ((void *) &myslot, (void *) pslot, - sizeof (struct slot)); - rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL); - if ((myslot.status != pslot->status) - || (myslot.ext_status != pslot->ext_status)) - process_changeinstatus (pslot, &myslot); - } - ctrl_count = 0x00; - list_for_each (pslotlist, &ibmphp_slot_head) { - if (ctrl_count >= ibmphp_get_total_controllers()) - break; - pslot = list_entry (pslotlist, struct slot, ibm_slot_list); - if (pslot->ctrl->ctlr_relative_id == ctrl_count) { - ctrl_count++; - if (READ_SLOT_LATCH (pslot->ctrl)) - rc = ibmphp_hpc_readslot (pslot, - READ_SLOTLATCHLOWREG, - &curlatchlow); - } - } - ++poll_count; - poll_state = POLL_SLEEP; - break; - case POLL_SLEEP: - /* don't sleep with a lock on the hardware */ - up (&semOperations); - long_delay (POLL_INTERVAL_SEC * HZ); - - if (ibmphp_shutdown) - break; - - down (&semOperations); - - if (poll_count >= POLL_LATCH_CNT) { - poll_count = 0; - poll_state = POLL_SLOTS; - } else - poll_state = POLL_LATCH_REGISTER; - break; - } - /* give up the harware semaphore */ - up (&semOperations); - /* sleep for a short time just for good measure */ - set_current_state (TASK_INTERRUPTIBLE); - schedule_timeout (HZ/10); - } - up (&sem_exit); - debug ("%s - Exit\n", __FUNCTION__); -} - - -/* ---------------------------------------------------------------------- - * Name: ibmphp_hpc_fillhpslotinfo(hotplug_slot * phpslot) - * - * Action: fill out the hotplug_slot info - * - * Input: pointer to hotplug_slot - * - * Return - * Value: 0 or error codes - *-----------------------------------------------------------------------*/ -int ibmphp_hpc_fillhpslotinfo (struct hotplug_slot *phpslot) -{ - int rc = 0; - struct slot *pslot; - - if (phpslot && phpslot->private) { - pslot = (struct slot *) phpslot->private; - rc = update_slot (pslot, (u8) TRUE); - if (!rc) { - - // power - enabled:1 not:0 - phpslot->info->power_status = SLOT_POWER (pslot->status); - - // attention - off:0, on:1, blinking:2 - phpslot->info->attention_status = SLOT_ATTN (pslot->status, pslot->ext_status); - - // latch - open:1 closed:0 - phpslot->info->latch_status = SLOT_LATCH (pslot->status); - - // pci board - present:1 not:0 - if (SLOT_PRESENT (pslot->status)) - phpslot->info->adapter_status = 1; - else - phpslot->info->adapter_status = 0; -/* - if (pslot->bus_on->supported_bus_mode - && (pslot->bus_on->supported_speed == BUS_SPEED_66)) - phpslot->info->max_bus_speed_status = BUS_SPEED_66PCIX; - else - phpslot->info->max_bus_speed_status = pslot->bus_on->supported_speed; -*/ } else - rc = -EINVAL; - } else - rc = -EINVAL; - - return rc; -} - -/*---------------------------------------------------------------------- -* Name: update_slot -* -* Action: fill out slot status and extended status, controller status -* -* Input: pointer to slot struct -*---------------------------------------------------------------------*/ -static int update_slot (struct slot *pslot, u8 update) -{ - int rc = 0; - - debug ("%s - Entry pslot[%lx]\n", __FUNCTION__, (ulong) pslot); - rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL); - debug ("%s - Exit rc[%d]\n", __FUNCTION__, rc); - return rc; -} - -/*---------------------------------------------------------------------- -* Name: process_changeinstatus -* -* Action: compare old and new slot status, process the change in status -* -* Input: pointer to slot struct, old slot struct -* -* Return 0 or error codes -* Value: -* -* Side -* Effects: None. -* -* Notes: -*---------------------------------------------------------------------*/ -static int process_changeinstatus (struct slot *pslot, struct slot *poldslot) -{ - u8 status; - int rc = 0; - u8 disable = FALSE; - u8 update = FALSE; - - debug ("process_changeinstatus - Entry pslot[%lx], poldslot[%lx]\n", (ulong) pslot, - (ulong) poldslot); - - // bit 0 - HPC_SLOT_POWER - if ((pslot->status & 0x01) != (poldslot->status & 0x01)) - update = TRUE; - - // bit 1 - HPC_SLOT_CONNECT - // ignore - - // bit 2 - HPC_SLOT_ATTN - if ((pslot->status & 0x04) != (poldslot->status & 0x04)) - update = TRUE; - - // bit 3 - HPC_SLOT_PRSNT2 - // bit 4 - HPC_SLOT_PRSNT1 - if (((pslot->status & 0x08) != (poldslot->status & 0x08)) - || ((pslot->status & 0x10) != (poldslot->status & 0x10))) - update = TRUE; - - // bit 5 - HPC_SLOT_PWRGD - if ((pslot->status & 0x20) != (poldslot->status & 0x20)) - // OFF -> ON: ignore, ON -> OFF: disable slot - if ((poldslot->status & 0x20) && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status))) - disable = TRUE; - - // bit 6 - HPC_SLOT_BUS_SPEED - // ignore - - // bit 7 - HPC_SLOT_LATCH - if ((pslot->status & 0x80) != (poldslot->status & 0x80)) { - update = TRUE; - // OPEN -> CLOSE - if (pslot->status & 0x80) { - if (SLOT_PWRGD (pslot->status)) { - // power goes on and off after closing latch - // check again to make sure power is still ON - long_delay (1 * HZ); - rc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &status); - if (SLOT_PWRGD (status)) - update = TRUE; - else // overwrite power in pslot to OFF - pslot->status &= ~HPC_SLOT_POWER; - } - } - // CLOSE -> OPEN - else if ((SLOT_PWRGD (poldslot->status) == HPC_SLOT_PWRGD_GOOD) - && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status))) { - disable = TRUE; - } - // else - ignore - } - // bit 4 - HPC_SLOT_BLINK_ATTN - if ((pslot->ext_status & 0x08) != (poldslot->ext_status & 0x08)) - update = TRUE; - - if (disable) { - debug ("process_changeinstatus - disable slot\n"); - pslot->flag = FALSE; - rc = ibmphp_do_disable_slot (pslot); - } - - if (update || disable) { - ibmphp_update_slot_info (pslot); - } - - debug ("%s - Exit rc[%d] disable[%x] update[%x]\n", __FUNCTION__, rc, disable, update); - - return rc; -} - -/*---------------------------------------------------------------------- -* Name: process_changeinlatch -* -* Action: compare old and new latch reg status, process the change -* -* Input: old and current latch register status -* -* Return 0 or error codes -* Value: -*---------------------------------------------------------------------*/ -static int process_changeinlatch (u8 old, u8 new, struct controller *ctrl) -{ - struct slot myslot, *pslot; - u8 i; - u8 mask; - int rc = 0; - - debug ("%s - Entry old[%x], new[%x]\n", __FUNCTION__, old, new); - // bit 0 reserved, 0 is LSB, check bit 1-6 for 6 slots - - for (i = ctrl->starting_slot_num; i <= ctrl->ending_slot_num; i++) { - mask = 0x01 << i; - if ((mask & old) != (mask & new)) { - pslot = ibmphp_get_slot_from_physical_num (i); - if (pslot) { - memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); - rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL); - debug ("%s - call process_changeinstatus for slot[%d]\n", __FUNCTION__, i); - process_changeinstatus (pslot, &myslot); - } else { - rc = -EINVAL; - err ("%s - Error bad pointer for slot[%d]\n", __FUNCTION__, i); - } - } - } - debug ("%s - Exit rc[%d]\n", __FUNCTION__, rc); - return rc; -} - -/*---------------------------------------------------------------------- -* Name: hpc_poll_thread -* -* Action: polling -* -* Return 0 -* Value: -*---------------------------------------------------------------------*/ -static int hpc_poll_thread (void *data) -{ - debug ("%s - Entry\n", __FUNCTION__); - - daemonize("hpc_poll"); - allow_signal(SIGKILL); - - poll_hpc (); - - tid_poll = 0; - debug ("%s - Exit\n", __FUNCTION__); - return 0; -} - - -/*---------------------------------------------------------------------- -* Name: ibmphp_hpc_start_poll_thread -* -* Action: start polling thread -*---------------------------------------------------------------------*/ -int __init ibmphp_hpc_start_poll_thread (void) -{ - int rc = 0; - - debug ("%s - Entry\n", __FUNCTION__); - - tid_poll = kernel_thread (hpc_poll_thread, 0, 0); - if (tid_poll < 0) { - err ("%s - Error, thread not started\n", __FUNCTION__); - rc = -1; - } - - debug ("%s - Exit tid_poll[%d] rc[%d]\n", __FUNCTION__, tid_poll, rc); - return rc; -} - -/*---------------------------------------------------------------------- -* Name: ibmphp_hpc_stop_poll_thread -* -* Action: stop polling thread and cleanup -*---------------------------------------------------------------------*/ -void __exit ibmphp_hpc_stop_poll_thread (void) -{ - debug ("%s - Entry\n", __FUNCTION__); - - ibmphp_shutdown = TRUE; - debug ("before locking operations \n"); - ibmphp_lock_operations (); - debug ("after locking operations \n"); - - // wait for poll thread to exit - debug ("before sem_exit down \n"); - down (&sem_exit); - debug ("after sem_exit down \n"); - - // cleanup - debug ("before free_hpc_access \n"); - free_hpc_access (); - debug ("after free_hpc_access \n"); - ibmphp_unlock_operations (); - debug ("after unlock operations \n"); - up (&sem_exit); - debug ("after sem exit up\n"); - - debug ("%s - Exit\n", __FUNCTION__); -} - -/*---------------------------------------------------------------------- -* Name: hpc_wait_ctlr_notworking -* -* Action: wait until the controller is in a not working state -* -* Return 0, HPC_ERROR -* Value: -*---------------------------------------------------------------------*/ -static int hpc_wait_ctlr_notworking (int timeout, struct controller *ctlr_ptr, void *wpg_bbar, - u8 * pstatus) -{ - int rc = 0; - u8 done = FALSE; - - debug_polling ("hpc_wait_ctlr_notworking - Entry timeout[%d]\n", timeout); - - while (!done) { - *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, WPG_CTLR_INDEX); - if (*pstatus == HPC_ERROR) { - rc = HPC_ERROR; - done = TRUE; - } - if (CTLR_WORKING (*pstatus) == HPC_CTLR_WORKING_NO) - done = TRUE; - if (!done) { - long_delay (1 * HZ); - if (timeout < 1) { - done = TRUE; - err ("HPCreadslot - Error ctlr timeout\n"); - rc = HPC_ERROR; - } else - timeout--; - } - } - debug_polling ("hpc_wait_ctlr_notworking - Exit rc[%x] status[%x]\n", rc, *pstatus); - return rc; -} diff -Nru a/drivers/hotplug/ibmphp_pci.c b/drivers/hotplug/ibmphp_pci.c --- a/drivers/hotplug/ibmphp_pci.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1758 +0,0 @@ -/* - * IBM Hot Plug Controller Driver - * - * Written By: Irene Zubarev, IBM Corporation - * - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001,2002 IBM Corp. - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - * - */ - -#include -#include -#include -#include -#include "ibmphp.h" - - -static int configure_device(struct pci_func *); -static int configure_bridge(struct pci_func **, u8); -static struct res_needed *scan_behind_bridge(struct pci_func *, u8); -static int add_new_bus (struct bus_node *, struct resource_node *, struct resource_node *, struct resource_node *, u8); -static u8 find_sec_number (u8 primary_busno, u8 slotno); - -/* - * NOTE..... If BIOS doesn't provide default routing, we assign: - * 9 for SCSI, 10 for LAN adapters, and 11 for everything else. - * If adapter is bridged, then we assign 11 to it and devices behind it. - * We also assign the same irq numbers for multi function devices. - * These are PIC mode, so shouldn't matter n.e.ways (hopefully) - */ -static void assign_alt_irq (struct pci_func * cur_func, u8 class_code) -{ - int j = 0; - for (j = 0; j < 4; j++) { - if (cur_func->irq[j] == 0xff) { - switch (class_code) { - case PCI_BASE_CLASS_STORAGE: - cur_func->irq[j] = SCSI_IRQ; - break; - case PCI_BASE_CLASS_NETWORK: - cur_func->irq[j] = LAN_IRQ; - break; - default: - cur_func->irq[j] = OTHER_IRQ; - break; - } - } - } -} - -/* - * Configures the device to be added (will allocate needed resources if it - * can), the device can be a bridge or a regular pci device, can also be - * multi-functional - * - * Input: function to be added - * - * TO DO: The error case with Multifunction device or multi function bridge, - * if there is an error, will need to go through all previous functions and - * unconfigure....or can add some code into unconfigure_card.... - */ -int ibmphp_configure_card (struct pci_func *func, u8 slotno) -{ - u16 vendor_id; - u32 class; - u8 class_code; - u8 hdr_type, device, sec_number; - u8 function; - struct pci_func *newfunc; /* for multi devices */ - struct pci_func *cur_func, *prev_func; - int rc, i, j; - int cleanup_count; - u8 flag; - u8 valid_device = 0x00; /* to see if we are able to read from card any device info at all */ - - debug ("inside configure_card, func->busno = %x \n", func->busno); - - device = func->device; - cur_func = func; - - /* We only get bus and device from IRQ routing table. So at this point, - * func->busno is correct, and func->device contains only device (at the 5 - * highest bits) - */ - - /* For every function on the card */ - for (function = 0x00; function < 0x08; function++) { - unsigned int devfn = PCI_DEVFN(device, function); - ibmphp_pci_bus->number = cur_func->busno; - - cur_func->function = function; - - debug ("inside the loop, cur_func->busno = %x, cur_func->device = %x, cur_func->funcion = %x\n", - cur_func->busno, cur_func->device, cur_func->function); - - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); - - debug ("vendor_id is %x\n", vendor_id); - if (vendor_id != PCI_VENDOR_ID_NOTVALID) { - /* found correct device!!! */ - debug ("found valid device, vendor_id = %x\n", vendor_id); - - ++valid_device; - - /* header: x x x x x x x x - * | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge - * |_=> 0 = single function device, 1 = multi-function device - */ - - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class); - - class_code = class >> 24; - debug ("hrd_type = %x, class = %x, class_code %x \n", hdr_type, class, class_code); - class >>= 8; /* to take revision out, class = class.subclass.prog i/f */ - if (class == PCI_CLASS_NOT_DEFINED_VGA) { - err ("The device %x is VGA compatible and as is not supported for hot plugging. " - "Please choose another device.\n", cur_func->device); - return -ENODEV; - } else if (class == PCI_CLASS_DISPLAY_VGA) { - err ("The device %x is not supported for hot plugging. " - "Please choose another device.\n", cur_func->device); - return -ENODEV; - } - switch (hdr_type) { - case PCI_HEADER_TYPE_NORMAL: - debug ("single device case.... vendor id = %x, hdr_type = %x, class = %x\n", vendor_id, hdr_type, class); - assign_alt_irq (cur_func, class_code); - if ((rc = configure_device (cur_func)) < 0) { - /* We need to do this in case some other BARs were properly inserted */ - err ("was not able to configure devfunc %x on bus %x. \n", - cur_func->device, cur_func->busno); - cleanup_count = 6; - goto error; - } - cur_func->next = NULL; - function = 0x8; - break; - case PCI_HEADER_TYPE_MULTIDEVICE: - assign_alt_irq (cur_func, class_code); - if ((rc = configure_device (cur_func)) < 0) { - /* We need to do this in case some other BARs were properly inserted */ - err ("was not able to configure devfunc %x on bus %x...bailing out\n", - cur_func->device, cur_func->busno); - cleanup_count = 6; - goto error; - } - newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); - if (!newfunc) { - err ("out of system memory \n"); - return -ENOMEM; - } - memset (newfunc, 0, sizeof (struct pci_func)); - newfunc->busno = cur_func->busno; - newfunc->device = device; - cur_func->next = newfunc; - cur_func = newfunc; - for (j = 0; j < 4; j++) - newfunc->irq[j] = cur_func->irq[j]; - break; - case PCI_HEADER_TYPE_MULTIBRIDGE: - class >>= 8; - if (class != PCI_CLASS_BRIDGE_PCI) { - err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. " - "Please insert another card.\n", cur_func->device); - return -ENODEV; - } - assign_alt_irq (cur_func, class_code); - rc = configure_bridge (&cur_func, slotno); - if (rc == -ENODEV) { - err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n"); - err ("Bus %x, devfunc %x \n", cur_func->busno, cur_func->device); - return rc; - } - if (rc) { - /* We need to do this in case some other BARs were properly inserted */ - err ("was not able to hot-add PPB properly.\n"); - func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ - cleanup_count = 2; - goto error; - } - - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); - flag = FALSE; - for (i = 0; i < 32; i++) { - if (func->devices[i]) { - newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); - if (!newfunc) { - err ("out of system memory \n"); - return -ENOMEM; - } - memset (newfunc, 0, sizeof (struct pci_func)); - newfunc->busno = sec_number; - newfunc->device = (u8) i; - for (j = 0; j < 4; j++) - newfunc->irq[j] = cur_func->irq[j]; - - if (flag) { - for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; - prev_func->next = newfunc; - } else - cur_func->next = newfunc; - - rc = ibmphp_configure_card (newfunc, slotno); - /* This could only happen if kmalloc failed */ - if (rc) { - /* We need to do this in case bridge itself got configured properly, but devices behind it failed */ - func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ - cleanup_count = 2; - goto error; - } - flag = TRUE; - } - } - - newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); - if (!newfunc) { - err ("out of system memory \n"); - return -ENOMEM; - } - memset (newfunc, 0, sizeof (struct pci_func)); - newfunc->busno = cur_func->busno; - newfunc->device = device; - for (j = 0; j < 4; j++) - newfunc->irq[j] = cur_func->irq[j]; - for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; - prev_func->next = newfunc; - cur_func = newfunc; - break; - case PCI_HEADER_TYPE_BRIDGE: - class >>= 8; - debug ("class now is %x\n", class); - if (class != PCI_CLASS_BRIDGE_PCI) { - err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. " - "Please insert another card.\n", cur_func->device); - return -ENODEV; - } - - assign_alt_irq (cur_func, class_code); - - debug ("cur_func->busno b4 configure_bridge is %x\n", cur_func->busno); - rc = configure_bridge (&cur_func, slotno); - if (rc == -ENODEV) { - err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n"); - err ("Bus %x, devfunc %x \n", cur_func->busno, cur_func->device); - return rc; - } - if (rc) { - /* We need to do this in case some other BARs were properly inserted */ - func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ - err ("was not able to hot-add PPB properly.\n"); - cleanup_count = 2; - goto error; - } - debug ("cur_func->busno = %x, device = %x, function = %x\n", - cur_func->busno, device, function); - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); - debug ("after configuring bridge..., sec_number = %x\n", sec_number); - flag = FALSE; - for (i = 0; i < 32; i++) { - if (func->devices[i]) { - debug ("inside for loop, device is %x\n", i); - newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); - if (!newfunc) { - err (" out of system memory \n"); - return -ENOMEM; - } - memset (newfunc, 0, sizeof (struct pci_func)); - newfunc->busno = sec_number; - newfunc->device = (u8) i; - for (j = 0; j < 4; j++) - newfunc->irq[j] = cur_func->irq[j]; - - if (flag) { - for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; - prev_func->next = newfunc; - } else - cur_func->next = newfunc; - - rc = ibmphp_configure_card (newfunc, slotno); - - /* Again, this case should not happen... For complete paranoia, will need to call remove_bus */ - if (rc) { - /* We need to do this in case some other BARs were properly inserted */ - func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ - cleanup_count = 2; - goto error; - } - flag = TRUE; - } - } - - function = 0x8; - break; - default: - err ("MAJOR PROBLEM!!!!, header type not supported? %x\n", hdr_type); - return -ENXIO; - break; - } /* end of switch */ - } /* end of valid device */ - } /* end of for */ - - if (!valid_device) { - err ("Cannot find any valid devices on the card. Or unable to read from card.\n"); - return -ENODEV; - } - - return 0; - -error: - for (i = 0; i < cleanup_count; i++) { - if (cur_func->io[i]) { - ibmphp_remove_resource (cur_func->io[i]); - cur_func->io[i] = NULL; - } else if (cur_func->pfmem[i]) { - ibmphp_remove_resource (cur_func->pfmem[i]); - cur_func->pfmem[i] = NULL; - } else if (cur_func->mem[i]) { - ibmphp_remove_resource (cur_func->mem[i]); - cur_func->mem[i] = NULL; - } - } - return rc; -} - -/* - * This function configures the pci BARs of a single device. - * Input: pointer to the pci_func - * Output: configured PCI, 0, or error - */ -static int configure_device (struct pci_func *func) -{ - u32 bar[6]; - u32 address[] = { - PCI_BASE_ADDRESS_0, - PCI_BASE_ADDRESS_1, - PCI_BASE_ADDRESS_2, - PCI_BASE_ADDRESS_3, - PCI_BASE_ADDRESS_4, - PCI_BASE_ADDRESS_5, - 0 - }; - u8 irq; - int count; - int len[6]; - struct resource_node *io[6]; - struct resource_node *mem[6]; - struct resource_node *mem_tmp; - struct resource_node *pfmem[6]; - unsigned int devfn; - - debug ("%s - inside\n", __FUNCTION__); - - devfn = PCI_DEVFN(func->device, func->function); - ibmphp_pci_bus->number = func->busno; - - for (count = 0; address[count]; count++) { /* for 6 BARs */ - - /* not sure if i need this. per scott, said maybe need smth like this - if devices don't adhere 100% to the spec, so don't want to write - to the reserved bits - - pcibios_read_config_byte(cur_func->busno, cur_func->device, - PCI_BASE_ADDRESS_0 + 4 * count, &tmp); - if (tmp & 0x01) // IO - pcibios_write_config_dword(cur_func->busno, cur_func->device, - PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFD); - else // Memory - pcibios_write_config_dword(cur_func->busno, cur_func->device, - PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFF); - */ - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); - - if (!bar[count]) /* This BAR is not implemented */ - continue; - - debug ("Device %x BAR %d wants %x\n", func->device, count, bar[count]); - - if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) { - /* This is IO */ - debug ("inside IO SPACE\n"); - - len[count] = bar[count] & 0xFFFFFFFC; - len[count] = ~len[count] + 1; - - debug ("len[count] in IO %x, count %d\n", len[count], count); - - io[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - - if (!io[count]) { - err ("out of system memory \n"); - return -ENOMEM; - } - memset (io[count], 0, sizeof (struct resource_node)); - io[count]->type = IO; - io[count]->busno = func->busno; - io[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); - io[count]->len = len[count]; - if (ibmphp_check_resource(io[count], 0) == 0) { - ibmphp_add_resource (io[count]); - func->io[count] = io[count]; - } else { - err ("cannot allocate requested io for bus %x device %x function %x len %x\n", - func->busno, func->device, func->function, len[count]); - kfree (io[count]); - return -EIO; - } - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->io[count]->start); - - /* _______________This is for debugging purposes only_____________________ */ - debug ("b4 writing, the IO address is %x\n", func->io[count]->start); - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); - debug ("after writing.... the start address is %x\n", bar[count]); - /* _________________________________________________________________________*/ - - } else { - /* This is Memory */ - if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) { - /* pfmem */ - debug ("PFMEM SPACE\n"); - - len[count] = bar[count] & 0xFFFFFFF0; - len[count] = ~len[count] + 1; - - debug ("len[count] in PFMEM %x, count %d\n", len[count], count); - - pfmem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!pfmem[count]) { - err ("out of system memory \n"); - return -ENOMEM; - } - memset (pfmem[count], 0, sizeof (struct resource_node)); - pfmem[count]->type = PFMEM; - pfmem[count]->busno = func->busno; - pfmem[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); - pfmem[count]->len = len[count]; - pfmem[count]->fromMem = FALSE; - if (ibmphp_check_resource (pfmem[count], 0) == 0) { - ibmphp_add_resource (pfmem[count]); - func->pfmem[count] = pfmem[count]; - } else { - mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!mem_tmp) { - err ("out of system memory \n"); - kfree (pfmem[count]); - return -ENOMEM; - } - memset (mem_tmp, 0, sizeof (struct resource_node)); - mem_tmp->type = MEM; - mem_tmp->busno = pfmem[count]->busno; - mem_tmp->devfunc = pfmem[count]->devfunc; - mem_tmp->len = pfmem[count]->len; - debug ("there's no pfmem... going into mem.\n"); - if (ibmphp_check_resource (mem_tmp, 0) == 0) { - ibmphp_add_resource (mem_tmp); - pfmem[count]->fromMem = TRUE; - pfmem[count]->rangeno = mem_tmp->rangeno; - pfmem[count]->start = mem_tmp->start; - pfmem[count]->end = mem_tmp->end; - ibmphp_add_pfmem_from_mem (pfmem[count]); - func->pfmem[count] = pfmem[count]; - } else { - err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n", - func->busno, func->device, len[count]); - kfree (mem_tmp); - kfree (pfmem[count]); - return -EIO; - } - } - - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->pfmem[count]->start); - - /*_______________This is for debugging purposes only______________________________*/ - debug ("b4 writing, start address is %x\n", func->pfmem[count]->start); - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); - debug ("after writing, start address is %x\n", bar[count]); - /*_________________________________________________________________________________*/ - - if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ - debug ("inside the mem 64 case, count %d\n", count); - count += 1; - /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000); - } - } else { - /* regular memory */ - debug ("REGULAR MEM SPACE\n"); - - len[count] = bar[count] & 0xFFFFFFF0; - len[count] = ~len[count] + 1; - - debug ("len[count] in Mem %x, count %d\n", len[count], count); - - mem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!mem[count]) { - err ("out of system memory \n"); - return -ENOMEM; - } - memset (mem[count], 0, sizeof (struct resource_node)); - mem[count]->type = MEM; - mem[count]->busno = func->busno; - mem[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); - mem[count]->len = len[count]; - if (ibmphp_check_resource (mem[count], 0) == 0) { - ibmphp_add_resource (mem[count]); - func->mem[count] = mem[count]; - } else { - err ("cannot allocate requested mem for bus %x, device %x, len %x\n", - func->busno, func->device, len[count]); - kfree (mem[count]); - return -EIO; - } - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->mem[count]->start); - /* _______________________This is for debugging purposes only _______________________*/ - debug ("b4 writing, start address is %x\n", func->mem[count]->start); - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); - debug ("after writing, the address is %x\n", bar[count]); - /* __________________________________________________________________________________*/ - - if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { - /* takes up another dword */ - debug ("inside mem 64 case, reg. mem, count %d\n", count); - count += 1; - /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000); - } - } - } /* end of mem */ - } /* end of for */ - - func->bus = 0; /* To indicate that this is not a PPB */ - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_PIN, &irq); - if ((irq > 0x00) && (irq < 0x05)) - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_LINE, func->irq[irq - 1]); - - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_CACHE_LINE_SIZE, CACHE); - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_LATENCY_TIMER, LATENCY); - - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_ROM_ADDRESS, 0x00L); - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_COMMAND, DEVICEENABLE); - - return 0; -} - -/****************************************************************************** - * This routine configures a PCI-2-PCI bridge and the functions behind it - * Parameters: pci_func - * Returns: - ******************************************************************************/ -static int configure_bridge (struct pci_func **func_passed, u8 slotno) -{ - int count; - int i; - int rc; - u8 sec_number; - u8 io_base; - u16 pfmem_base; - u32 bar[2]; - u32 len[2]; - u8 flag_io = FALSE; - u8 flag_mem = FALSE; - u8 flag_pfmem = FALSE; - u8 need_io_upper = FALSE; - u8 need_pfmem_upper = FALSE; - struct res_needed *amount_needed = NULL; - struct resource_node *io = NULL; - struct resource_node *bus_io[2] = {NULL, NULL}; - struct resource_node *mem = NULL; - struct resource_node *bus_mem[2] = {NULL, NULL}; - struct resource_node *mem_tmp = NULL; - struct resource_node *pfmem = NULL; - struct resource_node *bus_pfmem[2] = {NULL, NULL}; - struct bus_node *bus; - u32 address[] = { - PCI_BASE_ADDRESS_0, - PCI_BASE_ADDRESS_1, - 0 - }; - struct pci_func *func = *func_passed; - unsigned int devfn; - u8 irq; - int retval; - - debug ("%s - enter\n", __FUNCTION__); - - devfn = PCI_DEVFN(func->function, func->device); - ibmphp_pci_bus->number = func->busno; - - /* Configuring necessary info for the bridge so that we could see the devices - * behind it - */ - - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, func->busno); - - /* _____________________For debugging purposes only __________________________ - pci_bus_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, &pri_number); - debug ("primary # written into the bridge is %x\n", pri_number); - ___________________________________________________________________________*/ - - /* in EBDA, only get allocated 1 additional bus # per slot */ - sec_number = find_sec_number (func->busno, slotno); - if (sec_number == 0xff) { - err ("cannot allocate secondary bus number for the bridged device \n"); - return -EINVAL; - } - - debug ("after find_sec_number, the number we got is %x\n", sec_number); - debug ("AFTER FIND_SEC_NUMBER, func->busno IS %x\n", func->busno); - - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, sec_number); - - /* __________________For debugging purposes only __________________________________ - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); - debug ("sec_number after write/read is %x\n", sec_number); - ________________________________________________________________________________*/ - - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, sec_number); - - /* __________________For debugging purposes only ____________________________________ - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, &sec_number); - debug ("subordinate number after write/read is %x\n", sec_number); - __________________________________________________________________________________*/ - - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_CACHE_LINE_SIZE, CACHE); - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_LATENCY_TIMER, LATENCY); - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SEC_LATENCY_TIMER, LATENCY); - - debug ("func->busno is %x\n", func->busno); - debug ("sec_number after writing is %x\n", sec_number); - - - /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !!!!!!!!!!!!!!!NEED TO ADD!!! FAST BACK-TO-BACK ENABLE!!!!!!!!!!!!!!!!!!!! - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ - - - /* First we need to allocate mem/io for the bridge itself in case it needs it */ - for (count = 0; address[count]; count++) { /* for 2 BARs */ - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); - - if (!bar[count]) { - /* This BAR is not implemented */ - debug ("so we come here then, eh?, count = %d\n", count); - continue; - } - // tmp_bar = bar[count]; - - debug ("Bar %d wants %x\n", count, bar[count]); - - if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) { - /* This is IO */ - len[count] = bar[count] & 0xFFFFFFFC; - len[count] = ~len[count] + 1; - - debug ("len[count] in IO = %x\n", len[count]); - - bus_io[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - - if (!bus_io[count]) { - err ("out of system memory \n"); - retval = -ENOMEM; - goto error; - } - memset (bus_io[count], 0, sizeof (struct resource_node)); - bus_io[count]->type = IO; - bus_io[count]->busno = func->busno; - bus_io[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); - bus_io[count]->len = len[count]; - if (ibmphp_check_resource (bus_io[count], 0) == 0) { - ibmphp_add_resource (bus_io[count]); - func->io[count] = bus_io[count]; - } else { - err ("cannot allocate requested io for bus %x, device %x, len %x\n", - func->busno, func->device, len[count]); - kfree (bus_io[count]); - return -EIO; - } - - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->io[count]->start); - - } else { - /* This is Memory */ - if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) { - /* pfmem */ - len[count] = bar[count] & 0xFFFFFFF0; - len[count] = ~len[count] + 1; - - debug ("len[count] in PFMEM = %x\n", len[count]); - - bus_pfmem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!bus_pfmem[count]) { - err ("out of system memory \n"); - retval = -ENOMEM; - goto error; - } - memset (bus_pfmem[count], 0, sizeof (struct resource_node)); - bus_pfmem[count]->type = PFMEM; - bus_pfmem[count]->busno = func->busno; - bus_pfmem[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); - bus_pfmem[count]->len = len[count]; - bus_pfmem[count]->fromMem = FALSE; - if (ibmphp_check_resource (bus_pfmem[count], 0) == 0) { - ibmphp_add_resource (bus_pfmem[count]); - func->pfmem[count] = bus_pfmem[count]; - } else { - mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!mem_tmp) { - err ("out of system memory \n"); - retval = -ENOMEM; - goto error; - } - memset (mem_tmp, 0, sizeof (struct resource_node)); - mem_tmp->type = MEM; - mem_tmp->busno = bus_pfmem[count]->busno; - mem_tmp->devfunc = bus_pfmem[count]->devfunc; - mem_tmp->len = bus_pfmem[count]->len; - if (ibmphp_check_resource (mem_tmp, 0) == 0) { - ibmphp_add_resource (mem_tmp); - bus_pfmem[count]->fromMem = TRUE; - bus_pfmem[count]->rangeno = mem_tmp->rangeno; - ibmphp_add_pfmem_from_mem (bus_pfmem[count]); - func->pfmem[count] = bus_pfmem[count]; - } else { - err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n", - func->busno, func->device, len[count]); - kfree (mem_tmp); - kfree (bus_pfmem[count]); - return -EIO; - } - } - - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->pfmem[count]->start); - - if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { - /* takes up another dword */ - count += 1; - /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000); - - } - } else { - /* regular memory */ - len[count] = bar[count] & 0xFFFFFFF0; - len[count] = ~len[count] + 1; - - debug ("len[count] in Memory is %x\n", len[count]); - - bus_mem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!bus_mem[count]) { - err ("out of system memory \n"); - retval = -ENOMEM; - goto error; - } - memset (bus_mem[count], 0, sizeof (struct resource_node)); - bus_mem[count]->type = MEM; - bus_mem[count]->busno = func->busno; - bus_mem[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); - bus_mem[count]->len = len[count]; - if (ibmphp_check_resource (bus_mem[count], 0) == 0) { - ibmphp_add_resource (bus_mem[count]); - func->mem[count] = bus_mem[count]; - } else { - err ("cannot allocate requested mem for bus %x, device %x, len %x\n", - func->busno, func->device, len[count]); - kfree (bus_mem[count]); - return -EIO; - } - - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->mem[count]->start); - - if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { - /* takes up another dword */ - count += 1; - /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000); - - } - } - } /* end of mem */ - } /* end of for */ - - /* Now need to see how much space the devices behind the bridge needed */ - amount_needed = scan_behind_bridge (func, sec_number); - if (amount_needed == NULL) - return -ENOMEM; - - ibmphp_pci_bus->number = func->busno; - debug ("after coming back from scan_behind_bridge\n"); - debug ("amount_needed->not_correct = %x\n", amount_needed->not_correct); - debug ("amount_needed->io = %x\n", amount_needed->io); - debug ("amount_needed->mem = %x\n", amount_needed->mem); - debug ("amount_needed->pfmem = %x\n", amount_needed->pfmem); - - if (amount_needed->not_correct) { - debug ("amount_needed is not correct \n"); - for (count = 0; address[count]; count++) { - /* for 2 BARs */ - if (bus_io[count]) { - ibmphp_remove_resource (bus_io[count]); - func->io[count] = NULL; - } else if (bus_pfmem[count]) { - ibmphp_remove_resource (bus_pfmem[count]); - func->pfmem[count] = NULL; - } else if (bus_mem[count]) { - ibmphp_remove_resource (bus_mem[count]); - func->mem[count] = NULL; - } - } - kfree (amount_needed); - return -ENODEV; - } - - if (!amount_needed->io) { - debug ("it doesn't want IO?\n"); - flag_io = TRUE; - } else { - debug ("it wants %x IO behind the bridge \n", amount_needed->io); - io = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - - if (!io) { - err ("out of system memory \n"); - retval = -ENOMEM; - goto error; - } - memset (io, 0, sizeof (struct resource_node)); - io->type = IO; - io->busno = func->busno; - io->devfunc = ((func->device << 3) | (func->function & 0x7)); - io->len = amount_needed->io; - if (ibmphp_check_resource (io, 1) == 0) { - debug ("were we able to add io\n"); - ibmphp_add_resource (io); - flag_io = TRUE; - } - } - - if (!amount_needed->mem) { - debug ("it doesn't want n.e.memory?\n"); - flag_mem = TRUE; - } else { - debug ("it wants %x memory behind the bridge\n", amount_needed->mem); - mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!mem) { - err ("out of system memory \n"); - retval = -ENOMEM; - goto error; - } - memset (mem, 0, sizeof (struct resource_node)); - mem->type = MEM; - mem->busno = func->busno; - mem->devfunc = ((func->device << 3) | (func->function & 0x7)); - mem->len = amount_needed->mem; - if (ibmphp_check_resource (mem, 1) == 0) { - ibmphp_add_resource (mem); - flag_mem = TRUE; - debug ("were we able to add mem\n"); - } - } - - if (!amount_needed->pfmem) { - debug ("it doesn't want n.e.pfmem mem?\n"); - flag_pfmem = TRUE; - } else { - debug ("it wants %x pfmemory behind the bridge\n", amount_needed->pfmem); - pfmem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!pfmem) { - err ("out of system memory \n"); - retval = -ENOMEM; - goto error; - } - memset (pfmem, 0, sizeof (struct resource_node)); - pfmem->type = PFMEM; - pfmem->busno = func->busno; - pfmem->devfunc = ((func->device << 3) | (func->function & 0x7)); - pfmem->len = amount_needed->pfmem; - pfmem->fromMem = FALSE; - if (ibmphp_check_resource (pfmem, 1) == 0) { - ibmphp_add_resource (pfmem); - flag_pfmem = TRUE; - } else { - mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!mem_tmp) { - err ("out of system memory \n"); - retval = -ENOMEM; - goto error; - } - memset (mem_tmp, 0, sizeof (struct resource_node)); - mem_tmp->type = MEM; - mem_tmp->busno = pfmem->busno; - mem_tmp->devfunc = pfmem->devfunc; - mem_tmp->len = pfmem->len; - if (ibmphp_check_resource (mem_tmp, 1) == 0) { - ibmphp_add_resource (mem_tmp); - pfmem->fromMem = TRUE; - pfmem->rangeno = mem_tmp->rangeno; - ibmphp_add_pfmem_from_mem (pfmem); - flag_pfmem = TRUE; - } - } - } - - debug ("b4 if (flag_io && flag_mem && flag_pfmem)\n"); - debug ("flag_io = %x, flag_mem = %x, flag_pfmem = %x\n", flag_io, flag_mem, flag_pfmem); - - if (flag_io && flag_mem && flag_pfmem) { - /* If on bootup, there was a bridged card in this slot, - * then card was removed and ibmphp got unloaded and loaded - * back again, there's no way for us to remove the bus - * struct, so no need to kmalloc, can use existing node - */ - bus = ibmphp_find_res_bus (sec_number); - if (!bus) { - bus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); - if (!bus) { - err ("out of system memory \n"); - retval = -ENOMEM; - goto error; - } - memset (bus, 0, sizeof (struct bus_node)); - bus->busno = sec_number; - debug ("b4 adding new bus\n"); - rc = add_new_bus (bus, io, mem, pfmem, func->busno); - } else if (!(bus->rangeIO) && !(bus->rangeMem) && !(bus->rangePFMem)) - rc = add_new_bus (bus, io, mem, pfmem, 0xFF); - else { - err ("expected bus structure not empty? \n"); - retval = -EIO; - goto error; - } - if (rc) { - if (rc == -ENOMEM) { - ibmphp_remove_bus (bus, func->busno); - kfree (amount_needed); - return rc; - } - retval = rc; - goto error; - } - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &io_base); - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &pfmem_base); - - if ((io_base & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { - debug ("io 32\n"); - need_io_upper = TRUE; - } - if ((io_base & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { - debug ("pfmem 64\n"); - need_pfmem_upper = TRUE; - } - - if (bus->noIORanges) { - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00 | bus->rangeIO->start >> 8); - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00 | bus->rangeIO->end >> 8); - - /* _______________This is for debugging purposes only ____________________ - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &temp); - debug ("io_base = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8); - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &temp); - debug ("io_limit = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8); - ________________________________________________________________________*/ - - if (need_io_upper) { /* since can't support n.e.ways */ - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, 0x0000); - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, 0x0000); - } - } else { - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00); - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00); - } - - if (bus->noMemRanges) { - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0x0000 | bus->rangeMem->start >> 16); - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000 | bus->rangeMem->end >> 16); - - /* ____________________This is for debugging purposes only ________________________ - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &temp); - debug ("mem_base = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &temp); - debug ("mem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); - __________________________________________________________________________________*/ - - } else { - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0xffff); - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000); - } - if (bus->noPFMemRanges) { - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, 0x0000 | bus->rangePFMem->start >> 16); - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, 0x0000 | bus->rangePFMem->end >> 16); - - /* __________________________This is for debugging purposes only _______________________ - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &temp); - debug ("pfmem_base = %x", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &temp); - debug ("pfmem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); - ______________________________________________________________________________________*/ - - if (need_pfmem_upper) { /* since can't support n.e.ways */ - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, 0x00000000); - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, 0x00000000); - } - } else { - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, 0xffff); - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, 0x0000); - } - - debug ("b4 writing control information\n"); - - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_PIN, &irq); - if ((irq > 0x00) && (irq < 0x05)) - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_LINE, func->irq[irq - 1]); - /* - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, ctrl); - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_PARITY); - pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_SERR); - */ - - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_COMMAND, DEVICEENABLE); - pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, 0x07); - for (i = 0; i < 32; i++) { - if (amount_needed->devices[i]) { - debug ("device where devices[i] is 1 = %x\n", i); - func->devices[i] = 1; - } - } - func->bus = 1; /* For unconfiguring, to indicate it's PPB */ - func_passed = &func; - debug ("func->busno b4 returning is %x\n", func->busno); - debug ("func->busno b4 returning in the other structure is %x\n", (*func_passed)->busno); - kfree (amount_needed); - return 0; - } else { - err ("Configuring bridge was unsuccessful... \n"); - mem_tmp = NULL; - retval = -EIO; - goto error; - } - -error: - if (amount_needed) - kfree (amount_needed); - if (pfmem) - ibmphp_remove_resource (pfmem); - if (io) - ibmphp_remove_resource (io); - if (mem) - ibmphp_remove_resource (mem); - for (i = 0; i < 2; i++) { /* for 2 BARs */ - if (bus_io[i]) { - ibmphp_remove_resource (bus_io[i]); - func->io[i] = NULL; - } else if (bus_pfmem[i]) { - ibmphp_remove_resource (bus_pfmem[i]); - func->pfmem[i] = NULL; - } else if (bus_mem[i]) { - ibmphp_remove_resource (bus_mem[i]); - func->mem[i] = NULL; - } - } - return retval; -} - -/***************************************************************************** - * This function adds up the amount of resources needed behind the PPB bridge - * and passes it to the configure_bridge function - * Input: bridge function - * Ouput: amount of resources needed - *****************************************************************************/ -static struct res_needed *scan_behind_bridge (struct pci_func * func, u8 busno) -{ - int count, len[6]; - u16 vendor_id; - u8 hdr_type; - u8 device, function; - unsigned int devfn; - int howmany = 0; /*this is to see if there are any devices behind the bridge */ - - u32 bar[6], class; - u32 address[] = { - PCI_BASE_ADDRESS_0, - PCI_BASE_ADDRESS_1, - PCI_BASE_ADDRESS_2, - PCI_BASE_ADDRESS_3, - PCI_BASE_ADDRESS_4, - PCI_BASE_ADDRESS_5, - 0 - }; - struct res_needed *amount; - - amount = kmalloc (sizeof (struct res_needed), GFP_KERNEL); - if (amount == NULL) - return NULL; - memset (amount, 0, sizeof (struct res_needed)); - - ibmphp_pci_bus->number = busno; - - debug ("the bus_no behind the bridge is %x\n", busno); - debug ("scanning devices behind the bridge...\n"); - for (device = 0; device < 32; device++) { - amount->devices[device] = 0; - for (function = 0; function < 8; function++) { - devfn = PCI_DEVFN(device, function); - - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); - - if (vendor_id != PCI_VENDOR_ID_NOTVALID) { - /* found correct device!!! */ - howmany++; - - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class); - - debug ("hdr_type behind the bridge is %x\n", hdr_type); - if (hdr_type & PCI_HEADER_TYPE_BRIDGE) { - err ("embedded bridges not supported for hot-plugging.\n"); - amount->not_correct = TRUE; - return amount; - } - - class >>= 8; /* to take revision out, class = class.subclass.prog i/f */ - if (class == PCI_CLASS_NOT_DEFINED_VGA) { - err ("The device %x is VGA compatible and as is not supported for hot plugging. " - "Please choose another device.\n", device); - amount->not_correct = TRUE; - return amount; - } else if (class == PCI_CLASS_DISPLAY_VGA) { - err ("The device %x is not supported for hot plugging. " - "Please choose another device.\n", device); - amount->not_correct = TRUE; - return amount; - } - - amount->devices[device] = 1; - - for (count = 0; address[count]; count++) { - /* for 6 BARs */ - /* - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, address[count], &tmp); - if (tmp & 0x01) // IO - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFD); - else // MEMORY - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); - */ - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); - - debug ("what is bar[count]? %x, count = %d\n", bar[count], count); - - if (!bar[count]) /* This BAR is not implemented */ - continue; - - //tmp_bar = bar[count]; - - debug ("count %d device %x function %x wants %x resources \n", count, device, function, bar[count]); - - if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) { - /* This is IO */ - len[count] = bar[count] & 0xFFFFFFFC; - len[count] = ~len[count] + 1; - amount->io += len[count]; - } else { - /* This is Memory */ - if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) { - /* pfmem */ - len[count] = bar[count] & 0xFFFFFFF0; - len[count] = ~len[count] + 1; - amount->pfmem += len[count]; - if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) - /* takes up another dword */ - count += 1; - - } else { - /* regular memory */ - len[count] = bar[count] & 0xFFFFFFF0; - len[count] = ~len[count] + 1; - amount->mem += len[count]; - if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { - /* takes up another dword */ - count += 1; - } - } - } - } /* end for */ - } /* end if (valid) */ - } /* end for */ - } /* end for */ - - if (!howmany) - amount->not_correct = TRUE; - else - amount->not_correct = FALSE; - if ((amount->io) && (amount->io < IOBRIDGE)) - amount->io = IOBRIDGE; - if ((amount->mem) && (amount->mem < MEMBRIDGE)) - amount->mem = MEMBRIDGE; - if ((amount->pfmem) && (amount->pfmem < MEMBRIDGE)) - amount->pfmem = MEMBRIDGE; - return amount; -} - -/* The following 3 unconfigure_boot_ routines deal with the case when we had the card - * upon bootup in the system, since we don't allocate func to such case, we need to read - * the start addresses from pci config space and then find the corresponding entries in - * our resource lists. The functions return either 0, -ENODEV, or -1 (general failure) - * Change: we also call these functions even if we configured the card ourselves (i.e., not - * the bootup case), since it should work same way - */ -static int unconfigure_boot_device (u8 busno, u8 device, u8 function) -{ - u32 start_address; - u32 address[] = { - PCI_BASE_ADDRESS_0, - PCI_BASE_ADDRESS_1, - PCI_BASE_ADDRESS_2, - PCI_BASE_ADDRESS_3, - PCI_BASE_ADDRESS_4, - PCI_BASE_ADDRESS_5, - 0 - }; - int count; - struct resource_node *io; - struct resource_node *mem; - struct resource_node *pfmem; - struct bus_node *bus; - u32 end_address; - u32 temp_end; - u32 size; - u32 tmp_address; - unsigned int devfn; - - debug ("%s - enter\n", __FUNCTION__); - - bus = ibmphp_find_res_bus (busno); - if (!bus) { - debug ("cannot find corresponding bus.\n"); - return -EINVAL; - } - - devfn = PCI_DEVFN(device, function); - ibmphp_pci_bus->number = busno; - for (count = 0; address[count]; count++) { /* for 6 BARs */ - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &start_address); - - /* We can do this here, b/c by that time the device driver of the card has been stopped */ - - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &size); - pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], start_address); - - debug ("start_address is %x\n", start_address); - debug ("busno, device, function %x %x %x\n", busno, device, function); - if (!size) { - /* This BAR is not implemented */ - debug ("is this bar no implemented?, count = %d\n", count); - continue; - } - tmp_address = start_address; - if (start_address & PCI_BASE_ADDRESS_SPACE_IO) { - /* This is IO */ - start_address &= PCI_BASE_ADDRESS_IO_MASK; - size = size & 0xFFFFFFFC; - size = ~size + 1; - end_address = start_address + size - 1; - if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) { - err ("cannot find corresponding IO resource to remove\n"); - return -EIO; - } - debug ("io->start = %x\n", io->start); - temp_end = io->end; - start_address = io->end + 1; - ibmphp_remove_resource (io); - /* This is needed b/c of the old I/O restrictions in the BIOS */ - while (temp_end < end_address) { - if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) { - err ("cannot find corresponding IO resource to remove\n"); - return -EIO; - } - debug ("io->start = %x\n", io->start); - temp_end = io->end; - start_address = io->end + 1; - ibmphp_remove_resource (io); - } - - /* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */ - } else { - /* This is Memory */ - if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) { - /* pfmem */ - start_address &= PCI_BASE_ADDRESS_MEM_MASK; - debug ("start address of pfmem is %x\n", start_address); - - if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) { - err ("cannot find corresponding PFMEM resource to remove\n"); - return -EIO; - } - if (pfmem) - debug ("pfmem->start = %x\n", pfmem->start); - - ibmphp_remove_resource (pfmem); - - if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { - /* takes up another dword */ - count += 1; - } - - } else { - /* regular memory */ - start_address &= PCI_BASE_ADDRESS_MEM_MASK; - debug ("start address of mem is %x\n", start_address); - if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) { - err ("cannot find corresponding MEM resource to remove\n"); - return -EIO; - } - if (mem) - debug ("mem->start = %x\n", mem->start); - - ibmphp_remove_resource (mem); - - if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { - /* takes up another dword */ - count += 1; - } - } - } /* end of mem */ - } /* end of for */ - - return 0; -} - -static int unconfigure_boot_bridge (u8 busno, u8 device, u8 function) -{ - int count; - int bus_no, pri_no, sub_no, sec_no = 0; - u32 start_address, tmp_address; - u8 sec_number, sub_number, pri_number; - struct resource_node *io = NULL; - struct resource_node *mem = NULL; - struct resource_node *pfmem = NULL; - struct bus_node *bus; - u32 address[] = { - PCI_BASE_ADDRESS_0, - PCI_BASE_ADDRESS_1, - 0 - }; - unsigned int devfn; - - devfn = PCI_DEVFN(device, function); - ibmphp_pci_bus->number = busno; - bus_no = (int) busno; - debug ("busno is %x\n", busno); - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, &pri_number); - debug ("%s - busno = %x, primary_number = %x\n", __FUNCTION__, busno, pri_number); - - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); - debug ("sec_number is %x\n", sec_number); - sec_no = (int) sec_number; - pri_no = (int) pri_number; - if (pri_no != bus_no) { - err ("primary numbers in our structures and pci config space don't match.\n"); - return -EINVAL; - } - - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); - sec_no = (int) sec_no; - - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, &sub_number); - sub_no = (int) sub_number; - debug ("sub_no is %d, sec_no is %d\n", sub_no, sec_no); - if (sec_no != sub_number) { - err ("there're more buses behind this bridge. Hot removal is not supported. Please choose another card\n"); - return -ENODEV; - } - - bus = ibmphp_find_res_bus (sec_number); - debug ("bus->busno is %x\n", bus->busno); - debug ("sec_number is %x\n", sec_number); - if (!bus) { - err ("cannot find Bus structure for the bridged device\n"); - return -EINVAL; - } - - ibmphp_remove_bus (bus, busno); - - for (count = 0; address[count]; count++) { - /* for 2 BARs */ - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &start_address); - - if (!start_address) { - /* This BAR is not implemented */ - continue; - } - - tmp_address = start_address; - - if (start_address & PCI_BASE_ADDRESS_SPACE_IO) { - /* This is IO */ - start_address &= PCI_BASE_ADDRESS_IO_MASK; - if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) { - err ("cannot find corresponding IO resource to remove\n"); - return -EIO; - } - if (io) - debug ("io->start = %x\n", io->start); - - ibmphp_remove_resource (io); - - /* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */ - } else { - /* This is Memory */ - if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) { - /* pfmem */ - start_address &= PCI_BASE_ADDRESS_MEM_MASK; - if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) { - err ("cannot find corresponding PFMEM resource to remove\n"); - return -EINVAL; - } - if (pfmem) - debug ("pfmem->start = %x\n", pfmem->start); - - ibmphp_remove_resource (pfmem); - - if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { - /* takes up another dword */ - count += 1; - } - - } else { - /* regular memory */ - start_address &= PCI_BASE_ADDRESS_MEM_MASK; - if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) { - err ("cannot find corresponding MEM resource to remove\n"); - return -EINVAL; - } - if (mem) - debug ("mem->start = %x\n", mem->start); - - ibmphp_remove_resource (mem); - - if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { - /* takes up another dword */ - count += 1; - } - } - } /* end of mem */ - } /* end of for */ - debug ("%s - exiting, returning success\n", __FUNCTION__); - return 0; -} - -static int unconfigure_boot_card (struct slot *slot_cur) -{ - u16 vendor_id; - u32 class; - u8 hdr_type; - u8 device; - u8 busno; - u8 function; - int rc; - unsigned int devfn; - u8 valid_device = 0x00; /* To see if we are ever able to find valid device and read it */ - - debug ("%s - enter\n", __FUNCTION__); - - device = slot_cur->device; - busno = slot_cur->bus; - - debug ("b4 for loop, device is %x\n", device); - /* For every function on the card */ - for (function = 0x0; function < 0x08; function++) { - devfn = PCI_DEVFN(device, function); - ibmphp_pci_bus->number = busno; - - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); - - if (vendor_id != PCI_VENDOR_ID_NOTVALID) { - /* found correct device!!! */ - ++valid_device; - - debug ("%s - found correct device\n", __FUNCTION__); - - /* header: x x x x x x x x - * | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge - * |_=> 0 = single function device, 1 = multi-function device - */ - - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class); - - debug ("hdr_type %x, class %x\n", hdr_type, class); - class >>= 8; /* to take revision out, class = class.subclass.prog i/f */ - if (class == PCI_CLASS_NOT_DEFINED_VGA) { - err ("The device %x function %x is VGA compatible and is not supported for hot removing. " - "Please choose another device.\n", device, function); - return -ENODEV; - } else if (class == PCI_CLASS_DISPLAY_VGA) { - err ("The device %x function %x is not supported for hot removing. " - "Please choose another device.\n", device, function); - return -ENODEV; - } - - switch (hdr_type) { - case PCI_HEADER_TYPE_NORMAL: - rc = unconfigure_boot_device (busno, device, function); - if (rc) { - err ("was not able to unconfigure device %x func %x on bus %x. bailing out... \n", - device, function, busno); - return rc; - } - function = 0x8; - break; - case PCI_HEADER_TYPE_MULTIDEVICE: - rc = unconfigure_boot_device (busno, device, function); - if (rc) { - err ("was not able to unconfigure device %x func %x on bus %x. bailing out... \n", - device, function, busno); - return rc; - } - break; - case PCI_HEADER_TYPE_BRIDGE: - class >>= 8; - if (class != PCI_CLASS_BRIDGE_PCI) { - err ("This device %x function %x is not PCI-to-PCI bridge, " - "and is not supported for hot-removing. " - "Please try another card.\n", device, function); - return -ENODEV; - } - rc = unconfigure_boot_bridge (busno, device, function); - if (rc != 0) { - err ("was not able to hot-remove PPB properly.\n"); - return rc; - } - - function = 0x8; - break; - case PCI_HEADER_TYPE_MULTIBRIDGE: - class >>= 8; - if (class != PCI_CLASS_BRIDGE_PCI) { - err ("This device %x function %x is not PCI-to-PCI bridge, " - "and is not supported for hot-removing. " - "Please try another card.\n", device, function); - return -ENODEV; - } - rc = unconfigure_boot_bridge (busno, device, function); - if (rc != 0) { - err ("was not able to hot-remove PPB properly.\n"); - return rc; - } - break; - default: - err ("MAJOR PROBLEM!!!! Cannot read device's header \n"); - return -1; - break; - } /* end of switch */ - } /* end of valid device */ - } /* end of for */ - - if (!valid_device) { - err ("Could not find device to unconfigure. Or could not read the card. \n"); - return -1; - } - return 0; -} - -/* - * free the resources of the card (multi, single, or bridged) - * Parameters: slot, flag to say if this is for removing entire module or just - * unconfiguring the device - * TO DO: will probably need to add some code in case there was some resource, - * to remove it... this is from when we have errors in the configure_card... - * !!!!!!!!!!!!!!!!!!!!!!!!!FOR BUSES!!!!!!!!!!!! - * Returns: 0, -1, -ENODEV - */ -int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end) -{ - int i; - int count; - int rc; - struct slot *sl = *slot_cur; - struct pci_func *cur_func = NULL; - struct pci_func *temp_func; - - debug ("%s - enter\n", __FUNCTION__); - - if (!the_end) { - /* Need to unconfigure the card */ - rc = unconfigure_boot_card (sl); - if ((rc == -ENODEV) || (rc == -EIO) || (rc == -EINVAL)) { - /* In all other cases, will still need to get rid of func structure if it exists */ - return rc; - } - } - - if (sl->func) { - cur_func = sl->func; - while (cur_func) { - /* TO DO: WILL MOST LIKELY NEED TO GET RID OF THE BUS STRUCTURE FROM RESOURCES AS WELL */ - if (cur_func->bus) { - /* in other words, it's a PPB */ - count = 2; - } else { - count = 6; - } - - for (i = 0; i < count; i++) { - if (cur_func->io[i]) { - debug ("io[%d] exists \n", i); - if (the_end > 0) - ibmphp_remove_resource (cur_func->io[i]); - cur_func->io[i] = NULL; - } - if (cur_func->mem[i]) { - debug ("mem[%d] exists \n", i); - if (the_end > 0) - ibmphp_remove_resource (cur_func->mem[i]); - cur_func->mem[i] = NULL; - } - if (cur_func->pfmem[i]) { - debug ("pfmem[%d] exists \n", i); - if (the_end > 0) - ibmphp_remove_resource (cur_func->pfmem[i]); - cur_func->pfmem[i] = NULL; - } - } - - temp_func = cur_func->next; - kfree (cur_func); - cur_func = temp_func; - } - } - - sl->func = NULL; - *slot_cur = sl; - debug ("%s - exit\n", __FUNCTION__); - return 0; -} - -/* - * add a new bus resulting from hot-plugging a PPB bridge with devices - * - * Input: bus and the amount of resources needed (we know we can assign those, - * since they've been checked already - * Output: bus added to the correct spot - * 0, -1, error - */ -static int add_new_bus (struct bus_node *bus, struct resource_node *io, struct resource_node *mem, struct resource_node *pfmem, u8 parent_busno) -{ - struct range_node *io_range = NULL; - struct range_node *mem_range = NULL; - struct range_node *pfmem_range = NULL; - struct bus_node *cur_bus = NULL; - - /* Trying to find the parent bus number */ - if (parent_busno != 0xFF) { - cur_bus = ibmphp_find_res_bus (parent_busno); - if (!cur_bus) { - err ("strange, cannot find bus which is supposed to be at the system... something is terribly wrong...\n"); - return -ENODEV; - } - - list_add (&bus->bus_list, &cur_bus->bus_list); - } - if (io) { - io_range = kmalloc (sizeof (struct range_node), GFP_KERNEL); - if (!io_range) { - err ("out of system memory \n"); - return -ENOMEM; - } - memset (io_range, 0, sizeof (struct range_node)); - io_range->start = io->start; - io_range->end = io->end; - io_range->rangeno = 1; - bus->noIORanges = 1; - bus->rangeIO = io_range; - } - if (mem) { - mem_range = kmalloc (sizeof (struct range_node), GFP_KERNEL); - if (!mem_range) { - err ("out of system memory \n"); - return -ENOMEM; - } - memset (mem_range, 0, sizeof (struct range_node)); - mem_range->start = mem->start; - mem_range->end = mem->end; - mem_range->rangeno = 1; - bus->noMemRanges = 1; - bus->rangeMem = mem_range; - } - if (pfmem) { - pfmem_range = kmalloc (sizeof (struct range_node), GFP_KERNEL); - if (!pfmem_range) { - err ("out of system memory \n"); - return -ENOMEM; - } - memset (pfmem_range, 0, sizeof (struct range_node)); - pfmem_range->start = pfmem->start; - pfmem_range->end = pfmem->end; - pfmem_range->rangeno = 1; - bus->noPFMemRanges = 1; - bus->rangePFMem = pfmem_range; - } - return 0; -} - -/* - * find the 1st available bus number for PPB to set as its secondary bus - * Parameters: bus_number of the primary bus - * Returns: bus_number of the secondary bus or 0xff in case of failure - */ -static u8 find_sec_number (u8 primary_busno, u8 slotno) -{ - int min, max; - u8 busno; - struct bus_info *bus; - struct bus_node *bus_cur; - - bus = ibmphp_find_same_bus_num (primary_busno); - if (!bus) { - err ("cannot get slot range of the bus from the BIOS\n"); - return 0xff; - } - max = bus->slot_max; - min = bus->slot_min; - if ((slotno > max) || (slotno < min)) { - err ("got the wrong range\n"); - return 0xff; - } - busno = (u8) (slotno - (u8) min); - busno += primary_busno + 0x01; - bus_cur = ibmphp_find_res_bus (busno); - /* either there is no such bus number, or there are no ranges, which - * can only happen if we removed the bridged device in previous load - * of the driver, and now only have the skeleton bus struct - */ - if ((!bus_cur) || (!(bus_cur->rangeIO) && !(bus_cur->rangeMem) && !(bus_cur->rangePFMem))) - return busno; - return 0xff; -} - diff -Nru a/drivers/hotplug/ibmphp_res.c b/drivers/hotplug/ibmphp_res.c --- a/drivers/hotplug/ibmphp_res.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2157 +0,0 @@ -/* - * IBM Hot Plug Controller Driver - * - * Written By: Irene Zubarev, IBM Corporation - * - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001,2002 IBM Corp. - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - * - */ - -#include -#include -#include -#include -#include -#include "ibmphp.h" - -static int flags = 0; /* for testing */ - -static void update_resources (struct bus_node *bus_cur, int type, int rangeno); -static int once_over (void); -static int remove_ranges (struct bus_node *, struct bus_node *); -static int update_bridge_ranges (struct bus_node **); -static int add_range (int type, struct range_node *, struct bus_node *); -static void fix_resources (struct bus_node *); -static inline struct bus_node *find_bus_wprev (u8, struct bus_node **, u8); - -static LIST_HEAD(gbuses); -LIST_HEAD(ibmphp_res_head); - -static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc * curr, u8 busno, int flag) -{ - struct bus_node * newbus; - - if (!(curr) && !(flag)) { - err ("NULL pointer passed \n"); - return NULL; - } - - newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); - if (!newbus) { - err ("out of system memory \n"); - return NULL; - } - - memset (newbus, 0, sizeof (struct bus_node)); - if (flag) - newbus->busno = busno; - else - newbus->busno = curr->bus_num; - list_add_tail (&newbus->bus_list, &gbuses); - return newbus; -} - -static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc * curr) -{ - struct resource_node *rs; - - if (!curr) { - err ("NULL passed to allocate \n"); - return NULL; - } - - rs = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!rs) { - err ("out of system memory \n"); - return NULL; - } - memset (rs, 0, sizeof (struct resource_node)); - rs->busno = curr->bus_num; - rs->devfunc = curr->dev_fun; - rs->start = curr->start_addr; - rs->end = curr->end_addr; - rs->len = curr->end_addr - curr->start_addr + 1; - return rs; -} - -static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node **new_range, struct ebda_pci_rsrc *curr, int flag, u8 first_bus) -{ - struct bus_node * newbus; - struct range_node *newrange; - u8 num_ranges = 0; - - if (first_bus) { - newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); - if (!newbus) { - err ("out of system memory. \n"); - return -ENOMEM; - } - memset (newbus, 0, sizeof (struct bus_node)); - newbus->busno = curr->bus_num; - } else { - newbus = *new_bus; - switch (flag) { - case MEM: - num_ranges = newbus->noMemRanges; - break; - case PFMEM: - num_ranges = newbus->noPFMemRanges; - break; - case IO: - num_ranges = newbus->noIORanges; - break; - } - } - - newrange = kmalloc (sizeof (struct range_node), GFP_KERNEL); - if (!newrange) { - if (first_bus) - kfree (newbus); - err ("out of system memory \n"); - return -ENOMEM; - } - memset (newrange, 0, sizeof (struct range_node)); - newrange->start = curr->start_addr; - newrange->end = curr->end_addr; - - if (first_bus || (!num_ranges)) - newrange->rangeno = 1; - else { - /* need to insert our range */ - add_range (flag, newrange, newbus); - debug ("%d resource Primary Bus inserted on bus %x [%x - %x]\n", flag, newbus->busno, newrange->start, newrange->end); - } - - switch (flag) { - case MEM: - newbus->rangeMem = newrange; - if (first_bus) - newbus->noMemRanges = 1; - else { - debug ("First Memory Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end); - ++newbus->noMemRanges; - fix_resources (newbus); - } - break; - case IO: - newbus->rangeIO = newrange; - if (first_bus) - newbus->noIORanges = 1; - else { - debug ("First IO Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end); - ++newbus->noIORanges; - fix_resources (newbus); - } - break; - case PFMEM: - newbus->rangePFMem = newrange; - if (first_bus) - newbus->noPFMemRanges = 1; - else { - debug ("1st PFMemory Primary on Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); - ++newbus->noPFMemRanges; - fix_resources (newbus); - } - - break; - } - - *new_bus = newbus; - *new_range = newrange; - return 0; -} - - -/* Notes: - * 1. The ranges are ordered. The buses are not ordered. (First come) - * - * 2. If cannot allocate out of PFMem range, allocate from Mem ranges. PFmemFromMem - * are not sorted. (no need since use mem node). To not change the entire code, we - * also add mem node whenever this case happens so as not to change - * ibmphp_check_mem_resource etc (and since it really is taking Mem resource) - */ - -/***************************************************************************** - * This is the Resource Management initialization function. It will go through - * the Resource list taken from EBDA and fill in this module's data structures - * - * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES, - * SINCE WE'RE GOING TO ASSUME FOR NOW WE DON'T HAVE THOSE ON OUR BUSES FOR NOW - * - * Input: ptr to the head of the resource list from EBDA - * Output: 0, -1 or error codes - ***************************************************************************/ -int __init ibmphp_rsrc_init (void) -{ - struct ebda_pci_rsrc *curr; - struct range_node *newrange = NULL; - struct bus_node *newbus = NULL; - struct bus_node *bus_cur; - struct bus_node *bus_prev; - struct list_head *tmp; - struct resource_node *new_io = NULL; - struct resource_node *new_mem = NULL; - struct resource_node *new_pfmem = NULL; - int rc; - struct list_head *tmp_ebda; - - list_for_each (tmp_ebda, &ibmphp_ebda_pci_rsrc_head) { - curr = list_entry (tmp_ebda, struct ebda_pci_rsrc, ebda_pci_rsrc_list); - if (!(curr->rsrc_type & PCIDEVMASK)) { - /* EBDA still lists non PCI devices, so ignore... */ - debug ("this is not a PCI DEVICE in rsrc_init, please take care\n"); - // continue; - } - - /* this is a primary bus resource */ - if (curr->rsrc_type & PRIMARYBUSMASK) { - /* memory */ - if ((curr->rsrc_type & RESTYPE) == MMASK) { - /* no bus structure exists in place yet */ - if (list_empty (&gbuses)) { - if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1))) - return rc; - list_add_tail (&newbus->bus_list, &gbuses); - debug ("gbuses = NULL, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); - } else { - bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); - /* found our bus */ - if (bus_cur) { - rc = alloc_bus_range (&bus_cur, &newrange, curr, MEM, 0); - if (rc) - return rc; - } else { - /* went through all the buses and didn't find ours, need to create a new bus node */ - if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1))) - return rc; - - list_add_tail (&newbus->bus_list, &gbuses); - debug ("New Bus, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); - } - } - } else if ((curr->rsrc_type & RESTYPE) == PFMASK) { - /* prefetchable memory */ - if (list_empty (&gbuses)) { - /* no bus structure exists in place yet */ - if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1))) - return rc; - list_add_tail (&newbus->bus_list, &gbuses); - debug ("gbuses = NULL, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); - } else { - bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); - if (bus_cur) { - /* found our bus */ - rc = alloc_bus_range (&bus_cur, &newrange, curr, PFMEM, 0); - if (rc) - return rc; - } else { - /* went through all the buses and didn't find ours, need to create a new bus node */ - if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1))) - return rc; - list_add_tail (&newbus->bus_list, &gbuses); - debug ("1st Bus, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); - } - } - } else if ((curr->rsrc_type & RESTYPE) == IOMASK) { - /* IO */ - if (list_empty (&gbuses)) { - /* no bus structure exists in place yet */ - if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1))) - return rc; - list_add_tail (&newbus->bus_list, &gbuses); - debug ("gbuses = NULL, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); - } else { - bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); - if (bus_cur) { - rc = alloc_bus_range (&bus_cur, &newrange, curr, IO, 0); - if (rc) - return rc; - } else { - /* went through all the buses and didn't find ours, need to create a new bus node */ - if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1))) - return rc; - list_add_tail (&newbus->bus_list, &gbuses); - debug ("1st Bus, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); - } - } - - } else { - ; /* type is reserved WHAT TO DO IN THIS CASE??? - NOTHING TO DO??? */ - } - } else { - /* regular pci device resource */ - if ((curr->rsrc_type & RESTYPE) == MMASK) { - /* Memory resource */ - new_mem = alloc_resources (curr); - if (!new_mem) - return -ENOMEM; - new_mem->type = MEM; - /* - * if it didn't find the bus, means PCI dev - * came b4 the Primary Bus info, so need to - * create a bus rangeno becomes a problem... - * assign a -1 and then update once the range - * actually appears... - */ - if (ibmphp_add_resource (new_mem) < 0) { - newbus = alloc_error_bus (curr, 0, 0); - if (!newbus) - return -ENOMEM; - newbus->firstMem = new_mem; - ++newbus->needMemUpdate; - new_mem->rangeno = -1; - } - debug ("Memory resource for device %x, bus %x, [%x - %x]\n", new_mem->devfunc, new_mem->busno, new_mem->start, new_mem->end); - - } else if ((curr->rsrc_type & RESTYPE) == PFMASK) { - /* PFMemory resource */ - new_pfmem = alloc_resources (curr); - if (!new_pfmem) - return -ENOMEM; - new_pfmem->type = PFMEM; - new_pfmem->fromMem = FALSE; - if (ibmphp_add_resource (new_pfmem) < 0) { - newbus = alloc_error_bus (curr, 0, 0); - if (!newbus) - return -ENOMEM; - newbus->firstPFMem = new_pfmem; - ++newbus->needPFMemUpdate; - new_pfmem->rangeno = -1; - } - - debug ("PFMemory resource for device %x, bus %x, [%x - %x]\n", new_pfmem->devfunc, new_pfmem->busno, new_pfmem->start, new_pfmem->end); - } else if ((curr->rsrc_type & RESTYPE) == IOMASK) { - /* IO resource */ - new_io = alloc_resources (curr); - if (!new_io) - return -ENOMEM; - new_io->type = IO; - - /* - * if it didn't find the bus, means PCI dev - * came b4 the Primary Bus info, so need to - * create a bus rangeno becomes a problem... - * Can assign a -1 and then update once the - * range actually appears... - */ - if (ibmphp_add_resource (new_io) < 0) { - newbus = alloc_error_bus (curr, 0, 0); - if (!newbus) - return -ENOMEM; - newbus->firstIO = new_io; - ++newbus->needIOUpdate; - new_io->rangeno = -1; - } - debug ("IO resource for device %x, bus %x, [%x - %x]\n", new_io->devfunc, new_io->busno, new_io->start, new_io->end); - } - } - } - - list_for_each (tmp, &gbuses) { - bus_cur = list_entry (tmp, struct bus_node, bus_list); - /* This is to get info about PPB resources, since EBDA doesn't put this info into the primary bus info */ - rc = update_bridge_ranges (&bus_cur); - if (rc) - return rc; - } - rc = once_over (); /* This is to align ranges (so no -1) */ - if (rc) - return rc; - return 0; -} - -/******************************************************************************** - * This function adds a range into a sorted list of ranges per bus for a particular - * range type, it then calls another routine to update the range numbers on the - * pci devices' resources for the appropriate resource - * - * Input: type of the resource, range to add, current bus - * Output: 0 or -1, bus and range ptrs - ********************************************************************************/ -static int add_range (int type, struct range_node *range, struct bus_node *bus_cur) -{ - struct range_node *range_cur = NULL; - struct range_node *range_prev; - int count = 0, i_init; - int noRanges = 0; - - switch (type) { - case MEM: - range_cur = bus_cur->rangeMem; - noRanges = bus_cur->noMemRanges; - break; - case PFMEM: - range_cur = bus_cur->rangePFMem; - noRanges = bus_cur->noPFMemRanges; - break; - case IO: - range_cur = bus_cur->rangeIO; - noRanges = bus_cur->noIORanges; - break; - } - - range_prev = NULL; - while (range_cur) { - if (range->start < range_cur->start) - break; - range_prev = range_cur; - range_cur = range_cur->next; - count = count + 1; - } - if (!count) { - /* our range will go at the beginning of the list */ - switch (type) { - case MEM: - bus_cur->rangeMem = range; - break; - case PFMEM: - bus_cur->rangePFMem = range; - break; - case IO: - bus_cur->rangeIO = range; - break; - } - range->next = range_cur; - range->rangeno = 1; - i_init = 0; - } else if (!range_cur) { - /* our range will go at the end of the list */ - range->next = NULL; - range_prev->next = range; - range->rangeno = range_prev->rangeno + 1; - return 0; - } else { - /* the range is in the middle */ - range_prev->next = range; - range->next = range_cur; - range->rangeno = range_cur->rangeno; - i_init = range_prev->rangeno; - } - - for (count = i_init; count < noRanges; ++count) { - ++range_cur->rangeno; - range_cur = range_cur->next; - } - - update_resources (bus_cur, type, i_init + 1); - return 0; -} - -/******************************************************************************* - * This routine goes through the list of resources of type 'type' and updates - * the range numbers that they correspond to. It was called from add_range fnc - * - * Input: bus, type of the resource, the rangeno starting from which to update - ******************************************************************************/ -static void update_resources (struct bus_node *bus_cur, int type, int rangeno) -{ - struct resource_node *res = NULL; - u8 eol = FALSE; /* end of list indicator */ - - switch (type) { - case MEM: - if (bus_cur->firstMem) - res = bus_cur->firstMem; - break; - case PFMEM: - if (bus_cur->firstPFMem) - res = bus_cur->firstPFMem; - break; - case IO: - if (bus_cur->firstIO) - res = bus_cur->firstIO; - break; - } - - if (res) { - while (res) { - if (res->rangeno == rangeno) - break; - if (res->next) - res = res->next; - else if (res->nextRange) - res = res->nextRange; - else { - eol = TRUE; - break; - } - } - - if (!eol) { - /* found the range */ - while (res) { - ++res->rangeno; - res = res->next; - } - } - } -} - -static void fix_me (struct resource_node *res, struct bus_node *bus_cur, struct range_node *range) -{ - char * str = ""; - switch (res->type) { - case IO: - str = "io"; - break; - case MEM: - str = "mem"; - break; - case PFMEM: - str = "pfmem"; - break; - } - - while (res) { - if (res->rangeno == -1) { - while (range) { - if ((res->start >= range->start) && (res->end <= range->end)) { - res->rangeno = range->rangeno; - debug ("%s->rangeno in fix_resources is %d\n", str, res->rangeno); - switch (res->type) { - case IO: - --bus_cur->needIOUpdate; - break; - case MEM: - --bus_cur->needMemUpdate; - break; - case PFMEM: - --bus_cur->needPFMemUpdate; - break; - } - break; - } - range = range->next; - } - } - if (res->next) - res = res->next; - else - res = res->nextRange; - } - -} - -/***************************************************************************** - * This routine reassigns the range numbers to the resources that had a -1 - * This case can happen only if upon initialization, resources taken by pci dev - * appear in EBDA before the resources allocated for that bus, since we don't - * know the range, we assign -1, and this routine is called after a new range - * is assigned to see the resources with unknown range belong to the added range - * - * Input: current bus - * Output: none, list of resources for that bus are fixed if can be - *******************************************************************************/ -static void fix_resources (struct bus_node *bus_cur) -{ - struct range_node *range; - struct resource_node *res; - - debug ("%s - bus_cur->busno = %d\n", __FUNCTION__, bus_cur->busno); - - if (bus_cur->needIOUpdate) { - res = bus_cur->firstIO; - range = bus_cur->rangeIO; - fix_me (res, bus_cur, range); - } - if (bus_cur->needMemUpdate) { - res = bus_cur->firstMem; - range = bus_cur->rangeMem; - fix_me (res, bus_cur, range); - } - if (bus_cur->needPFMemUpdate) { - res = bus_cur->firstPFMem; - range = bus_cur->rangePFMem; - fix_me (res, bus_cur, range); - } -} - -/******************************************************************************* - * This routine adds a resource to the list of resources to the appropriate bus - * based on their resource type and sorted by their starting addresses. It assigns - * the ptrs to next and nextRange if needed. - * - * Input: resource ptr - * Output: ptrs assigned (to the node) - * 0 or -1 - *******************************************************************************/ -int ibmphp_add_resource (struct resource_node *res) -{ - struct resource_node *res_cur; - struct resource_node *res_prev; - struct bus_node *bus_cur; - struct range_node *range_cur = NULL; - struct resource_node *res_start = NULL; - - debug ("%s - enter\n", __FUNCTION__); - - if (!res) { - err ("NULL passed to add \n"); - return -ENODEV; - } - - bus_cur = find_bus_wprev (res->busno, NULL, 0); - - if (!bus_cur) { - /* didn't find a bus, smth's wrong!!! */ - debug ("no bus in the system, either pci_dev's wrong or allocation failed\n"); - return -ENODEV; - } - - /* Normal case */ - switch (res->type) { - case IO: - range_cur = bus_cur->rangeIO; - res_start = bus_cur->firstIO; - break; - case MEM: - range_cur = bus_cur->rangeMem; - res_start = bus_cur->firstMem; - break; - case PFMEM: - range_cur = bus_cur->rangePFMem; - res_start = bus_cur->firstPFMem; - break; - default: - err ("cannot read the type of the resource to add... problem \n"); - return -EINVAL; - } - while (range_cur) { - if ((res->start >= range_cur->start) && (res->end <= range_cur->end)) { - res->rangeno = range_cur->rangeno; - break; - } - range_cur = range_cur->next; - } - - /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * this is again the case of rangeno = -1 - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - */ - - if (!range_cur) { - switch (res->type) { - case IO: - ++bus_cur->needIOUpdate; - break; - case MEM: - ++bus_cur->needMemUpdate; - break; - case PFMEM: - ++bus_cur->needPFMemUpdate; - break; - } - res->rangeno = -1; - } - - debug ("The range is %d\n", res->rangeno); - if (!res_start) { - /* no first{IO,Mem,Pfmem} on the bus, 1st IO/Mem/Pfmem resource ever */ - switch (res->type) { - case IO: - bus_cur->firstIO = res; - break; - case MEM: - bus_cur->firstMem = res; - break; - case PFMEM: - bus_cur->firstPFMem = res; - break; - } - res->next = NULL; - res->nextRange = NULL; - } else { - res_cur = res_start; - res_prev = NULL; - - debug ("res_cur->rangeno is %d\n", res_cur->rangeno); - - while (res_cur) { - if (res_cur->rangeno >= res->rangeno) - break; - res_prev = res_cur; - if (res_cur->next) - res_cur = res_cur->next; - else - res_cur = res_cur->nextRange; - } - - if (!res_cur) { - /* at the end of the resource list */ - debug ("i should be here, [%x - %x]\n", res->start, res->end); - res_prev->nextRange = res; - res->next = NULL; - res->nextRange = NULL; - } else if (res_cur->rangeno == res->rangeno) { - /* in the same range */ - while (res_cur) { - if (res->start < res_cur->start) - break; - res_prev = res_cur; - res_cur = res_cur->next; - } - if (!res_cur) { - /* the last resource in this range */ - res_prev->next = res; - res->next = NULL; - res->nextRange = res_prev->nextRange; - res_prev->nextRange = NULL; - } else if (res->start < res_cur->start) { - /* at the beginning or middle of the range */ - if (!res_prev) { - switch (res->type) { - case IO: - bus_cur->firstIO = res; - break; - case MEM: - bus_cur->firstMem = res; - break; - case PFMEM: - bus_cur->firstPFMem = res; - break; - } - } else if (res_prev->rangeno == res_cur->rangeno) - res_prev->next = res; - else - res_prev->nextRange = res; - - res->next = res_cur; - res->nextRange = NULL; - } - } else { - /* this is the case where it is 1st occurrence of the range */ - if (!res_prev) { - /* at the beginning of the resource list */ - res->next = NULL; - switch (res->type) { - case IO: - res->nextRange = bus_cur->firstIO; - bus_cur->firstIO = res; - break; - case MEM: - res->nextRange = bus_cur->firstMem; - bus_cur->firstMem = res; - break; - case PFMEM: - res->nextRange = bus_cur->firstPFMem; - bus_cur->firstPFMem = res; - break; - } - } else if (res_cur->rangeno > res->rangeno) { - /* in the middle of the resource list */ - res_prev->nextRange = res; - res->next = NULL; - res->nextRange = res_cur; - } - } - } - - debug ("%s - exit\n", __FUNCTION__); - return 0; -} - -/**************************************************************************** - * This routine will remove the resource from the list of resources - * - * Input: io, mem, and/or pfmem resource to be deleted - * Ouput: modified resource list - * 0 or error code - ****************************************************************************/ -int ibmphp_remove_resource (struct resource_node *res) -{ - struct bus_node *bus_cur; - struct resource_node *res_cur = NULL; - struct resource_node *res_prev; - struct resource_node *mem_cur; - char * type = ""; - - if (!res) { - err ("resource to remove is NULL \n"); - return -ENODEV; - } - - bus_cur = find_bus_wprev (res->busno, NULL, 0); - - if (!bus_cur) { - err ("cannot find corresponding bus of the io resource to remove " - "bailing out...\n"); - return -ENODEV; - } - - switch (res->type) { - case IO: - res_cur = bus_cur->firstIO; - type = "io"; - break; - case MEM: - res_cur = bus_cur->firstMem; - type = "mem"; - break; - case PFMEM: - res_cur = bus_cur->firstPFMem; - type = "pfmem"; - break; - default: - err ("unknown type for resource to remove \n"); - return -EINVAL; - } - res_prev = NULL; - - while (res_cur) { - if ((res_cur->start == res->start) && (res_cur->end == res->end)) - break; - res_prev = res_cur; - if (res_cur->next) - res_cur = res_cur->next; - else - res_cur = res_cur->nextRange; - } - - if (!res_cur) { - if (res->type == PFMEM) { - /* - * case where pfmem might be in the PFMemFromMem list - * so will also need to remove the corresponding mem - * entry - */ - res_cur = bus_cur->firstPFMemFromMem; - res_prev = NULL; - - while (res_cur) { - if ((res_cur->start == res->start) && (res_cur->end == res->end)) { - mem_cur = bus_cur->firstMem; - while (mem_cur) { - if ((mem_cur->start == res_cur->start) - && (mem_cur->end == res_cur->end)) - break; - if (mem_cur->next) - mem_cur = mem_cur->next; - else - mem_cur = mem_cur->nextRange; - } - if (!mem_cur) { - err ("cannot find corresponding mem node for pfmem...\n"); - return -EINVAL; - } - - ibmphp_remove_resource (mem_cur); - if (!res_prev) - bus_cur->firstPFMemFromMem = res_cur->next; - else - res_prev->next = res_cur->next; - kfree (res_cur); - return 0; - } - res_prev = res_cur; - if (res_cur->next) - res_cur = res_cur->next; - else - res_cur = res_cur->nextRange; - } - if (!res_cur) { - err ("cannot find pfmem to delete...\n"); - return -EINVAL; - } - } else { - err ("the %s resource is not in the list to be deleted...\n", type); - return -EINVAL; - } - } - if (!res_prev) { - /* first device to be deleted */ - if (res_cur->next) { - switch (res->type) { - case IO: - bus_cur->firstIO = res_cur->next; - break; - case MEM: - bus_cur->firstMem = res_cur->next; - break; - case PFMEM: - bus_cur->firstPFMem = res_cur->next; - break; - } - } else if (res_cur->nextRange) { - switch (res->type) { - case IO: - bus_cur->firstIO = res_cur->nextRange; - break; - case MEM: - bus_cur->firstMem = res_cur->nextRange; - break; - case PFMEM: - bus_cur->firstPFMem = res_cur->nextRange; - break; - } - } else { - switch (res->type) { - case IO: - bus_cur->firstIO = NULL; - break; - case MEM: - bus_cur->firstMem = NULL; - break; - case PFMEM: - bus_cur->firstPFMem = NULL; - break; - } - } - kfree (res_cur); - return 0; - } else { - if (res_cur->next) { - if (res_prev->rangeno == res_cur->rangeno) - res_prev->next = res_cur->next; - else - res_prev->nextRange = res_cur->next; - } else if (res_cur->nextRange) { - res_prev->next = NULL; - res_prev->nextRange = res_cur->nextRange; - } else { - res_prev->next = NULL; - res_prev->nextRange = NULL; - } - kfree (res_cur); - return 0; - } - - return 0; -} - -static struct range_node * find_range (struct bus_node *bus_cur, struct resource_node * res) -{ - struct range_node * range = NULL; - - switch (res->type) { - case IO: - range = bus_cur->rangeIO; - break; - case MEM: - range = bus_cur->rangeMem; - break; - case PFMEM: - range = bus_cur->rangePFMem; - break; - default: - err ("cannot read resource type in find_range \n"); - } - - while (range) { - if (res->rangeno == range->rangeno) - break; - range = range->next; - } - return range; -} - -/***************************************************************************** - * This routine will check to make sure the io/mem/pfmem->len that the device asked for - * can fit w/i our list of available IO/MEM/PFMEM resources. If cannot, returns -EINVAL, - * otherwise, returns 0 - * - * Input: resource - * Ouput: the correct start and end address are inputted into the resource node, - * 0 or -EINVAL - *****************************************************************************/ -int ibmphp_check_resource (struct resource_node *res, u8 bridge) -{ - struct bus_node *bus_cur; - struct range_node *range = NULL; - struct resource_node *res_prev; - struct resource_node *res_cur = NULL; - u32 len_cur = 0, start_cur = 0, len_tmp = 0; - int noranges = 0; - u32 tmp_start; /* this is to make sure start address is divisible by the length needed */ - u32 tmp_divide; - u8 flag = FALSE; - - if (!res) - return -EINVAL; - - if (bridge) { - /* The rules for bridges are different, 4K divisible for IO, 1M for (pf)mem*/ - if (res->type == IO) - tmp_divide = IOBRIDGE; - else - tmp_divide = MEMBRIDGE; - } else - tmp_divide = res->len; - - bus_cur = find_bus_wprev (res->busno, NULL, 0); - - if (!bus_cur) { - /* didn't find a bus, smth's wrong!!! */ - debug ("no bus in the system, either pci_dev's wrong or allocation failed \n"); - return -EINVAL; - } - - debug ("%s - enter\n", __FUNCTION__); - debug ("bus_cur->busno is %d\n", bus_cur->busno); - - /* This is a quick fix to not mess up with the code very much. i.e., - * 2000-2fff, len = 1000, but when we compare, we need it to be fff */ - res->len -= 1; - - switch (res->type) { - case IO: - res_cur = bus_cur->firstIO; - noranges = bus_cur->noIORanges; - break; - case MEM: - res_cur = bus_cur->firstMem; - noranges = bus_cur->noMemRanges; - break; - case PFMEM: - res_cur = bus_cur->firstPFMem; - noranges = bus_cur->noPFMemRanges; - break; - default: - err ("wrong type of resource to check \n"); - return -EINVAL; - } - res_prev = NULL; - - while (res_cur) { - range = find_range (bus_cur, res_cur); - debug ("%s - rangeno = %d\n", __FUNCTION__, res_cur->rangeno); - - if (!range) { - err ("no range for the device exists... bailing out...\n"); - return -EINVAL; - } - - /* found our range */ - if (!res_prev) { - /* first time in the loop */ - if ((res_cur->start != range->start) && ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) { - debug ("len_tmp = %x\n", len_tmp); - - if ((len_tmp < len_cur) || (len_cur == 0)) { - - if ((range->start % tmp_divide) == 0) { - /* just perfect, starting address is divisible by length */ - flag = TRUE; - len_cur = len_tmp; - start_cur = range->start; - } else { - /* Needs adjusting */ - tmp_start = range->start; - flag = FALSE; - - while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { - if ((tmp_start % tmp_divide) == 0) { - flag = TRUE; - len_cur = len_tmp; - start_cur = tmp_start; - break; - } - tmp_start += tmp_divide - tmp_start % tmp_divide; - if (tmp_start >= res_cur->start - 1) - break; - } - } - - if (flag && len_cur == res->len) { - debug ("but we are not here, right?\n"); - res->start = start_cur; - res->len += 1; /* To restore the balance */ - res->end = res->start + res->len - 1; - return 0; - } - } - } - } - if (!res_cur->next) { - /* last device on the range */ - if ((range->end != res_cur->end) && ((len_tmp = range->end - (res_cur->end + 1)) >= res->len)) { - debug ("len_tmp = %x\n", len_tmp); - if ((len_tmp < len_cur) || (len_cur == 0)) { - - if (((res_cur->end + 1) % tmp_divide) == 0) { - /* just perfect, starting address is divisible by length */ - flag = TRUE; - len_cur = len_tmp; - start_cur = res_cur->end + 1; - } else { - /* Needs adjusting */ - tmp_start = res_cur->end + 1; - flag = FALSE; - - while ((len_tmp = range->end - tmp_start) >= res->len) { - if ((tmp_start % tmp_divide) == 0) { - flag = TRUE; - len_cur = len_tmp; - start_cur = tmp_start; - break; - } - tmp_start += tmp_divide - tmp_start % tmp_divide; - if (tmp_start >= range->end) - break; - } - } - if (flag && len_cur == res->len) { - res->start = start_cur; - res->len += 1; /* To restore the balance */ - res->end = res->start + res->len - 1; - return 0; - } - } - } - } - - if (res_prev) { - if (res_prev->rangeno != res_cur->rangeno) { - /* 1st device on this range */ - if ((res_cur->start != range->start) && - ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) { - if ((len_tmp < len_cur) || (len_cur == 0)) { - if ((range->start % tmp_divide) == 0) { - /* just perfect, starting address is divisible by length */ - flag = TRUE; - len_cur = len_tmp; - start_cur = range->start; - } else { - /* Needs adjusting */ - tmp_start = range->start; - flag = FALSE; - - while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { - if ((tmp_start % tmp_divide) == 0) { - flag = TRUE; - len_cur = len_tmp; - start_cur = tmp_start; - break; - } - tmp_start += tmp_divide - tmp_start % tmp_divide; - if (tmp_start >= res_cur->start - 1) - break; - } - } - - if (flag && len_cur == res->len) { - res->start = start_cur; - res->len += 1; /* To restore the balance */ - res->end = res->start + res->len - 1; - return 0; - } - } - } - } else { - /* in the same range */ - if ((len_tmp = res_cur->start - 1 - res_prev->end - 1) >= res->len) { - if ((len_tmp < len_cur) || (len_cur == 0)) { - if (((res_prev->end + 1) % tmp_divide) == 0) { - /* just perfect, starting address's divisible by length */ - flag = TRUE; - len_cur = len_tmp; - start_cur = res_prev->end + 1; - } else { - /* Needs adjusting */ - tmp_start = res_prev->end + 1; - flag = FALSE; - - while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { - if ((tmp_start % tmp_divide) == 0) { - flag = TRUE; - len_cur = len_tmp; - start_cur = tmp_start; - break; - } - tmp_start += tmp_divide - tmp_start % tmp_divide; - if (tmp_start >= res_cur->start - 1) - break; - } - } - - if (flag && len_cur == res->len) { - res->start = start_cur; - res->len += 1; /* To restore the balance */ - res->end = res->start + res->len - 1; - return 0; - } - } - } - } - } - /* end if (res_prev) */ - res_prev = res_cur; - if (res_cur->next) - res_cur = res_cur->next; - else - res_cur = res_cur->nextRange; - } /* end of while */ - - - if (!res_prev) { - /* 1st device ever */ - /* need to find appropriate range */ - switch (res->type) { - case IO: - range = bus_cur->rangeIO; - break; - case MEM: - range = bus_cur->rangeMem; - break; - case PFMEM: - range = bus_cur->rangePFMem; - break; - } - while (range) { - if ((len_tmp = range->end - range->start) >= res->len) { - if ((len_tmp < len_cur) || (len_cur == 0)) { - if ((range->start % tmp_divide) == 0) { - /* just perfect, starting address's divisible by length */ - flag = TRUE; - len_cur = len_tmp; - start_cur = range->start; - } else { - /* Needs adjusting */ - tmp_start = range->start; - flag = FALSE; - - while ((len_tmp = range->end - tmp_start) >= res->len) { - if ((tmp_start % tmp_divide) == 0) { - flag = TRUE; - len_cur = len_tmp; - start_cur = tmp_start; - break; - } - tmp_start += tmp_divide - tmp_start % tmp_divide; - if (tmp_start >= range->end) - break; - } - } - - if (flag && len_cur == res->len) { - res->start = start_cur; - res->len += 1; /* To restore the balance */ - res->end = res->start + res->len - 1; - return 0; - } - } - } - range = range->next; - } /* end of while */ - - if ((!range) && (len_cur == 0)) { - /* have gone through the list of devices and ranges and haven't found n.e.thing */ - err ("no appropriate range.. bailing out...\n"); - return -EINVAL; - } else if (len_cur) { - res->start = start_cur; - res->len += 1; /* To restore the balance */ - res->end = res->start + res->len - 1; - return 0; - } - } - - if (!res_cur) { - debug ("prev->rangeno = %d, noranges = %d\n", res_prev->rangeno, noranges); - if (res_prev->rangeno < noranges) { - /* if there're more ranges out there to check */ - switch (res->type) { - case IO: - range = bus_cur->rangeIO; - break; - case MEM: - range = bus_cur->rangeMem; - break; - case PFMEM: - range = bus_cur->rangePFMem; - break; - } - while (range) { - if ((len_tmp = range->end - range->start) >= res->len) { - if ((len_tmp < len_cur) || (len_cur == 0)) { - if ((range->start % tmp_divide) == 0) { - /* just perfect, starting address's divisible by length */ - flag = TRUE; - len_cur = len_tmp; - start_cur = range->start; - } else { - /* Needs adjusting */ - tmp_start = range->start; - flag = FALSE; - - while ((len_tmp = range->end - tmp_start) >= res->len) { - if ((tmp_start % tmp_divide) == 0) { - flag = TRUE; - len_cur = len_tmp; - start_cur = tmp_start; - break; - } - tmp_start += tmp_divide - tmp_start % tmp_divide; - if (tmp_start >= range->end) - break; - } - } - - if (flag && len_cur == res->len) { - res->start = start_cur; - res->len += 1; /* To restore the balance */ - res->end = res->start + res->len - 1; - return 0; - } - } - } - range = range->next; - } /* end of while */ - - if ((!range) && (len_cur == 0)) { - /* have gone through the list of devices and ranges and haven't found n.e.thing */ - err ("no appropriate range.. bailing out...\n"); - return -EINVAL; - } else if (len_cur) { - res->start = start_cur; - res->len += 1; /* To restore the balance */ - res->end = res->start + res->len - 1; - return 0; - } - } else { - /* no more ranges to check on */ - if (len_cur) { - res->start = start_cur; - res->len += 1; /* To restore the balance */ - res->end = res->start + res->len - 1; - return 0; - } else { - /* have gone through the list of devices and haven't found n.e.thing */ - err ("no appropriate range.. bailing out...\n"); - return -EINVAL; - } - } - } /* end if(!res_cur) */ - return -EINVAL; -} - -/******************************************************************************** - * This routine is called from remove_card if the card contained PPB. - * It will remove all the resources on the bus as well as the bus itself - * Input: Bus - * Ouput: 0, -ENODEV - ********************************************************************************/ -int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno) -{ - struct resource_node *res_cur; - struct resource_node *res_tmp; - struct bus_node *prev_bus; - int rc; - - prev_bus = find_bus_wprev (parent_busno, NULL, 0); - - if (!prev_bus) { - debug ("something terribly wrong. Cannot find parent bus to the one to remove\n"); - return -ENODEV; - } - - debug ("In ibmphp_remove_bus... prev_bus->busno is %x\n", prev_bus->busno); - - rc = remove_ranges (bus, prev_bus); - if (rc) - return rc; - - if (bus->firstIO) { - res_cur = bus->firstIO; - while (res_cur) { - res_tmp = res_cur; - if (res_cur->next) - res_cur = res_cur->next; - else - res_cur = res_cur->nextRange; - kfree (res_tmp); - res_tmp = NULL; - } - bus->firstIO = NULL; - } - if (bus->firstMem) { - res_cur = bus->firstMem; - while (res_cur) { - res_tmp = res_cur; - if (res_cur->next) - res_cur = res_cur->next; - else - res_cur = res_cur->nextRange; - kfree (res_tmp); - res_tmp = NULL; - } - bus->firstMem = NULL; - } - if (bus->firstPFMem) { - res_cur = bus->firstPFMem; - while (res_cur) { - res_tmp = res_cur; - if (res_cur->next) - res_cur = res_cur->next; - else - res_cur = res_cur->nextRange; - kfree (res_tmp); - res_tmp = NULL; - } - bus->firstPFMem = NULL; - } - - if (bus->firstPFMemFromMem) { - res_cur = bus->firstPFMemFromMem; - while (res_cur) { - res_tmp = res_cur; - res_cur = res_cur->next; - - kfree (res_tmp); - res_tmp = NULL; - } - bus->firstPFMemFromMem = NULL; - } - - list_del (&bus->bus_list); - kfree (bus); - return 0; -} - -/****************************************************************************** - * This routine deletes the ranges from a given bus, and the entries from the - * parent's bus in the resources - * Input: current bus, previous bus - * Output: 0, -EINVAL - ******************************************************************************/ -static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev) -{ - struct range_node *range_cur; - struct range_node *range_tmp; - int i; - struct resource_node *res = NULL; - - if (bus_cur->noIORanges) { - range_cur = bus_cur->rangeIO; - for (i = 0; i < bus_cur->noIORanges; i++) { - if (ibmphp_find_resource (bus_prev, range_cur->start, &res, IO) < 0) - return -EINVAL; - ibmphp_remove_resource (res); - - range_tmp = range_cur; - range_cur = range_cur->next; - kfree (range_tmp); - range_tmp = NULL; - } - bus_cur->rangeIO = NULL; - } - if (bus_cur->noMemRanges) { - range_cur = bus_cur->rangeMem; - for (i = 0; i < bus_cur->noMemRanges; i++) { - if (ibmphp_find_resource (bus_prev, range_cur->start, &res, MEM) < 0) - return -EINVAL; - - ibmphp_remove_resource (res); - range_tmp = range_cur; - range_cur = range_cur->next; - kfree (range_tmp); - range_tmp = NULL; - } - bus_cur->rangeMem = NULL; - } - if (bus_cur->noPFMemRanges) { - range_cur = bus_cur->rangePFMem; - for (i = 0; i < bus_cur->noPFMemRanges; i++) { - if (ibmphp_find_resource (bus_prev, range_cur->start, &res, PFMEM) < 0) - return -EINVAL; - - ibmphp_remove_resource (res); - range_tmp = range_cur; - range_cur = range_cur->next; - kfree (range_tmp); - range_tmp = NULL; - } - bus_cur->rangePFMem = NULL; - } - return 0; -} - -/* - * find the resource node in the bus - * Input: Resource needed, start address of the resource, type of resource - */ -int ibmphp_find_resource (struct bus_node *bus, u32 start_address, struct resource_node **res, int flag) -{ - struct resource_node *res_cur = NULL; - char * type = ""; - - if (!bus) { - err ("The bus passed in NULL to find resource \n"); - return -ENODEV; - } - - switch (flag) { - case IO: - res_cur = bus->firstIO; - type = "io"; - break; - case MEM: - res_cur = bus->firstMem; - type = "mem"; - break; - case PFMEM: - res_cur = bus->firstPFMem; - type = "pfmem"; - break; - default: - err ("wrong type of flag \n"); - return -EINVAL; - } - - while (res_cur) { - if (res_cur->start == start_address) { - *res = res_cur; - break; - } - if (res_cur->next) - res_cur = res_cur->next; - else - res_cur = res_cur->nextRange; - } - - if (!res_cur) { - if (flag == PFMEM) { - res_cur = bus->firstPFMemFromMem; - while (res_cur) { - if (res_cur->start == start_address) { - *res = res_cur; - break; - } - res_cur = res_cur->next; - } - if (!res_cur) { - debug ("SOS...cannot find %s resource in the bus. \n", type); - return -EINVAL; - } - } else { - debug ("SOS... cannot find %s resource in the bus. \n", type); - return -EINVAL; - } - } - - if (*res) - debug ("*res->start = %x \n", (*res)->start); - - return 0; -} - -/*********************************************************************** - * This routine will free the resource structures used by the - * system. It is called from cleanup routine for the module - * Parameters: none - * Returns: none - ***********************************************************************/ -void ibmphp_free_resources (void) -{ - struct bus_node *bus_cur = NULL; - struct bus_node *bus_tmp; - struct range_node *range_cur; - struct range_node *range_tmp; - struct resource_node *res_cur; - struct resource_node *res_tmp; - struct list_head *tmp; - struct list_head *next; - int i = 0; - flags = 1; - - list_for_each_safe (tmp, next, &gbuses) { - bus_cur = list_entry (tmp, struct bus_node, bus_list); - if (bus_cur->noIORanges) { - range_cur = bus_cur->rangeIO; - for (i = 0; i < bus_cur->noIORanges; i++) { - if (!range_cur) - break; - range_tmp = range_cur; - range_cur = range_cur->next; - kfree (range_tmp); - range_tmp = NULL; - } - } - if (bus_cur->noMemRanges) { - range_cur = bus_cur->rangeMem; - for (i = 0; i < bus_cur->noMemRanges; i++) { - if (!range_cur) - break; - range_tmp = range_cur; - range_cur = range_cur->next; - kfree (range_tmp); - range_tmp = NULL; - } - } - if (bus_cur->noPFMemRanges) { - range_cur = bus_cur->rangePFMem; - for (i = 0; i < bus_cur->noPFMemRanges; i++) { - if (!range_cur) - break; - range_tmp = range_cur; - range_cur = range_cur->next; - kfree (range_tmp); - range_tmp = NULL; - } - } - - if (bus_cur->firstIO) { - res_cur = bus_cur->firstIO; - while (res_cur) { - res_tmp = res_cur; - if (res_cur->next) - res_cur = res_cur->next; - else - res_cur = res_cur->nextRange; - kfree (res_tmp); - res_tmp = NULL; - } - bus_cur->firstIO = NULL; - } - if (bus_cur->firstMem) { - res_cur = bus_cur->firstMem; - while (res_cur) { - res_tmp = res_cur; - if (res_cur->next) - res_cur = res_cur->next; - else - res_cur = res_cur->nextRange; - kfree (res_tmp); - res_tmp = NULL; - } - bus_cur->firstMem = NULL; - } - if (bus_cur->firstPFMem) { - res_cur = bus_cur->firstPFMem; - while (res_cur) { - res_tmp = res_cur; - if (res_cur->next) - res_cur = res_cur->next; - else - res_cur = res_cur->nextRange; - kfree (res_tmp); - res_tmp = NULL; - } - bus_cur->firstPFMem = NULL; - } - - if (bus_cur->firstPFMemFromMem) { - res_cur = bus_cur->firstPFMemFromMem; - while (res_cur) { - res_tmp = res_cur; - res_cur = res_cur->next; - - kfree (res_tmp); - res_tmp = NULL; - } - bus_cur->firstPFMemFromMem = NULL; - } - - bus_tmp = bus_cur; - list_del (&bus_cur->bus_list); - kfree (bus_tmp); - bus_tmp = NULL; - } -} - -/********************************************************************************* - * This function will go over the PFmem resources to check if the EBDA allocated - * pfmem out of memory buckets of the bus. If so, it will change the range numbers - * and a flag to indicate that this resource is out of memory. It will also move the - * Pfmem out of the pfmem resource list to the PFMemFromMem list, and will create - * a new Mem node - * This routine is called right after initialization - *******************************************************************************/ -static int __init once_over (void) -{ - struct resource_node *pfmem_cur; - struct resource_node *pfmem_prev; - struct resource_node *mem; - struct bus_node *bus_cur; - struct list_head *tmp; - - list_for_each (tmp, &gbuses) { - bus_cur = list_entry (tmp, struct bus_node, bus_list); - if ((!bus_cur->rangePFMem) && (bus_cur->firstPFMem)) { - for (pfmem_cur = bus_cur->firstPFMem, pfmem_prev = NULL; pfmem_cur; pfmem_prev = pfmem_cur, pfmem_cur = pfmem_cur->next) { - pfmem_cur->fromMem = TRUE; - if (pfmem_prev) - pfmem_prev->next = pfmem_cur->next; - else - bus_cur->firstPFMem = pfmem_cur->next; - - if (!bus_cur->firstPFMemFromMem) - pfmem_cur->next = NULL; - else - /* we don't need to sort PFMemFromMem since we're using mem node for - all the real work anyways, so just insert at the beginning of the - list - */ - pfmem_cur->next = bus_cur->firstPFMemFromMem; - - bus_cur->firstPFMemFromMem = pfmem_cur; - - mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!mem) { - err ("out of system memory \n"); - return -ENOMEM; - } - memset (mem, 0, sizeof (struct resource_node)); - mem->type = MEM; - mem->busno = pfmem_cur->busno; - mem->devfunc = pfmem_cur->devfunc; - mem->start = pfmem_cur->start; - mem->end = pfmem_cur->end; - mem->len = pfmem_cur->len; - if (ibmphp_add_resource (mem) < 0) - err ("Trouble...trouble... EBDA allocated pfmem from mem, but system doesn't display it has this space... unless not PCI device...\n"); - pfmem_cur->rangeno = mem->rangeno; - } /* end for pfmem */ - } /* end if */ - } /* end list_for_each bus */ - return 0; -} - -int ibmphp_add_pfmem_from_mem (struct resource_node *pfmem) -{ - struct bus_node *bus_cur = find_bus_wprev (pfmem->busno, NULL, 0); - - if (!bus_cur) { - err ("cannot find bus of pfmem to add...\n"); - return -ENODEV; - } - - if (bus_cur->firstPFMemFromMem) - pfmem->next = bus_cur->firstPFMemFromMem; - else - pfmem->next = NULL; - - bus_cur->firstPFMemFromMem = pfmem; - - return 0; -} - -/* This routine just goes through the buses to see if the bus already exists. - * It is called from ibmphp_find_sec_number, to find out a secondary bus number for - * bridged cards - * Parameters: bus_number - * Returns: Bus pointer or NULL - */ -struct bus_node *ibmphp_find_res_bus (u8 bus_number) -{ - return find_bus_wprev (bus_number, NULL, 0); -} - -static inline struct bus_node *find_bus_wprev (u8 bus_number, struct bus_node **prev, u8 flag) -{ - struct bus_node *bus_cur; - struct list_head *tmp; - struct list_head *tmp_prev; - - list_for_each (tmp, &gbuses) { - tmp_prev = tmp->prev; - bus_cur = list_entry (tmp, struct bus_node, bus_list); - if (flag) - *prev = list_entry (tmp_prev, struct bus_node, bus_list); - if (bus_cur->busno == bus_number) - return bus_cur; - } - - return NULL; -} - -void ibmphp_print_test (void) -{ - int i = 0; - struct bus_node *bus_cur = NULL; - struct range_node *range; - struct resource_node *res; - struct list_head *tmp; - - debug_pci ("*****************START**********************\n"); - - if ((!list_empty(&gbuses)) && flags) { - err ("The GBUSES is not NULL?!?!?!?!?\n"); - return; - } - - list_for_each (tmp, &gbuses) { - bus_cur = list_entry (tmp, struct bus_node, bus_list); - debug_pci ("This is bus # %d. There are \n", bus_cur->busno); - debug_pci ("IORanges = %d\t", bus_cur->noIORanges); - debug_pci ("MemRanges = %d\t", bus_cur->noMemRanges); - debug_pci ("PFMemRanges = %d\n", bus_cur->noPFMemRanges); - debug_pci ("The IO Ranges are as follows:\n"); - if (bus_cur->rangeIO) { - range = bus_cur->rangeIO; - for (i = 0; i < bus_cur->noIORanges; i++) { - debug_pci ("rangeno is %d\n", range->rangeno); - debug_pci ("[%x - %x]\n", range->start, range->end); - range = range->next; - } - } - - debug_pci ("The Mem Ranges are as follows:\n"); - if (bus_cur->rangeMem) { - range = bus_cur->rangeMem; - for (i = 0; i < bus_cur->noMemRanges; i++) { - debug_pci ("rangeno is %d\n", range->rangeno); - debug_pci ("[%x - %x]\n", range->start, range->end); - range = range->next; - } - } - - debug_pci ("The PFMem Ranges are as follows:\n"); - - if (bus_cur->rangePFMem) { - range = bus_cur->rangePFMem; - for (i = 0; i < bus_cur->noPFMemRanges; i++) { - debug_pci ("rangeno is %d\n", range->rangeno); - debug_pci ("[%x - %x]\n", range->start, range->end); - range = range->next; - } - } - - debug_pci ("The resources on this bus are as follows\n"); - - debug_pci ("IO...\n"); - if (bus_cur->firstIO) { - res = bus_cur->firstIO; - while (res) { - debug_pci ("The range # is %d\n", res->rangeno); - debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); - debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); - if (res->next) - res = res->next; - else if (res->nextRange) - res = res->nextRange; - else - break; - } - } - debug_pci ("Mem...\n"); - if (bus_cur->firstMem) { - res = bus_cur->firstMem; - while (res) { - debug_pci ("The range # is %d\n", res->rangeno); - debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); - debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); - if (res->next) - res = res->next; - else if (res->nextRange) - res = res->nextRange; - else - break; - } - } - debug_pci ("PFMem...\n"); - if (bus_cur->firstPFMem) { - res = bus_cur->firstPFMem; - while (res) { - debug_pci ("The range # is %d\n", res->rangeno); - debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); - debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); - if (res->next) - res = res->next; - else if (res->nextRange) - res = res->nextRange; - else - break; - } - } - - debug_pci ("PFMemFromMem...\n"); - if (bus_cur->firstPFMemFromMem) { - res = bus_cur->firstPFMemFromMem; - while (res) { - debug_pci ("The range # is %d\n", res->rangeno); - debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); - debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); - res = res->next; - } - } - } - debug_pci ("***********************END***********************\n"); -} - -static int range_exists_already (struct range_node * range, struct bus_node * bus_cur, u8 type) -{ - struct range_node * range_cur = NULL; - switch (type) { - case IO: - range_cur = bus_cur->rangeIO; - break; - case MEM: - range_cur = bus_cur->rangeMem; - break; - case PFMEM: - range_cur = bus_cur->rangePFMem; - break; - default: - err ("wrong type passed to find out if range already exists \n"); - return -ENODEV; - } - - while (range_cur) { - if ((range_cur->start == range->start) && (range_cur->end == range->end)) - return 1; - range_cur = range_cur->next; - } - - return 0; -} - -/* This routine will read the windows for any PPB we have and update the - * range info for the secondary bus, and will also input this info into - * primary bus, since BIOS doesn't. This is for PPB that are in the system - * on bootup. For bridged cards that were added during previous load of the - * driver, only the ranges and the bus structure are added, the devices are - * added from NVRAM - * Input: primary busno - * Returns: none - * Note: this function doesn't take into account IO restrictions etc, - * so will only work for bridges with no video/ISA devices behind them It - * also will not work for onboard PPB's that can have more than 1 *bus - * behind them All these are TO DO. - * Also need to add more error checkings... (from fnc returns etc) - */ -static int __init update_bridge_ranges (struct bus_node **bus) -{ - u8 sec_busno, device, function, hdr_type, start_io_address, end_io_address; - u16 vendor_id, upper_io_start, upper_io_end, start_mem_address, end_mem_address; - u32 start_address, end_address, upper_start, upper_end; - struct bus_node *bus_sec; - struct bus_node *bus_cur; - struct resource_node *io; - struct resource_node *mem; - struct resource_node *pfmem; - struct range_node *range; - unsigned int devfn; - - bus_cur = *bus; - if (!bus_cur) - return -ENODEV; - ibmphp_pci_bus->number = bus_cur->busno; - - debug ("inside %s \n", __FUNCTION__); - debug ("bus_cur->busno = %x\n", bus_cur->busno); - - for (device = 0; device < 32; device++) { - for (function = 0x00; function < 0x08; function++) { - devfn = PCI_DEVFN(device, function); - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); - - if (vendor_id != PCI_VENDOR_ID_NOTVALID) { - /* found correct device!!! */ - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); - - switch (hdr_type) { - case PCI_HEADER_TYPE_NORMAL: - function = 0x8; - break; - case PCI_HEADER_TYPE_MULTIDEVICE: - break; - case PCI_HEADER_TYPE_BRIDGE: - function = 0x8; - case PCI_HEADER_TYPE_MULTIBRIDGE: - /* We assume here that only 1 bus behind the bridge - TO DO: add functionality for several: - temp = secondary; - while (temp < subordinate) { - ... - temp++; - } - */ - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_busno); - bus_sec = find_bus_wprev (sec_busno, NULL, 0); - /* this bus structure doesn't exist yet, PPB was configured during previous loading of ibmphp */ - if (!bus_sec) { - bus_sec = alloc_error_bus (NULL, sec_busno, 1); - /* the rest will be populated during NVRAM call */ - return 0; - } - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &start_io_address); - pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &end_io_address); - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, &upper_io_start); - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, &upper_io_end); - start_address = (start_io_address & PCI_IO_RANGE_MASK) << 8; - start_address |= (upper_io_start << 16); - end_address = (end_io_address & PCI_IO_RANGE_MASK) << 8; - end_address |= (upper_io_end << 16); - - if ((start_address) && (start_address <= end_address)) { - range = kmalloc (sizeof (struct range_node), GFP_KERNEL); - if (!range) { - err ("out of system memory \n"); - return -ENOMEM; - } - memset (range, 0, sizeof (struct range_node)); - range->start = start_address; - range->end = end_address + 0xfff; - - if (bus_sec->noIORanges > 0) { - if (!range_exists_already (range, bus_sec, IO)) { - add_range (IO, range, bus_sec); - ++bus_sec->noIORanges; - } else { - kfree (range); - range = NULL; - } - } else { - /* 1st IO Range on the bus */ - range->rangeno = 1; - bus_sec->rangeIO = range; - ++bus_sec->noIORanges; - } - fix_resources (bus_sec); - - if (ibmphp_find_resource (bus_cur, start_address, &io, IO)) { - io = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!io) { - kfree (range); - err ("out of system memory \n"); - return -ENOMEM; - } - memset (io, 0, sizeof (struct resource_node)); - io->type = IO; - io->busno = bus_cur->busno; - io->devfunc = ((device << 3) | (function & 0x7)); - io->start = start_address; - io->end = end_address + 0xfff; - io->len = io->end - io->start + 1; - ibmphp_add_resource (io); - } - } - - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &start_mem_address); - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &end_mem_address); - - start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16; - end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16; - - if ((start_address) && (start_address <= end_address)) { - - range = kmalloc (sizeof (struct range_node), GFP_KERNEL); - if (!range) { - err ("out of system memory \n"); - return -ENOMEM; - } - memset (range, 0, sizeof (struct range_node)); - range->start = start_address; - range->end = end_address + 0xfffff; - - if (bus_sec->noMemRanges > 0) { - if (!range_exists_already (range, bus_sec, MEM)) { - add_range (MEM, range, bus_sec); - ++bus_sec->noMemRanges; - } else { - kfree (range); - range = NULL; - } - } else { - /* 1st Mem Range on the bus */ - range->rangeno = 1; - bus_sec->rangeMem = range; - ++bus_sec->noMemRanges; - } - - fix_resources (bus_sec); - - if (ibmphp_find_resource (bus_cur, start_address, &mem, MEM)) { - mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!mem) { - kfree (range); - err ("out of system memory \n"); - return -ENOMEM; - } - memset (mem, 0, sizeof (struct resource_node)); - mem->type = MEM; - mem->busno = bus_cur->busno; - mem->devfunc = ((device << 3) | (function & 0x7)); - mem->start = start_address; - mem->end = end_address + 0xfffff; - mem->len = mem->end - mem->start + 1; - ibmphp_add_resource (mem); - } - } - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &start_mem_address); - pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &end_mem_address); - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, &upper_start); - pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, &upper_end); - start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16; - end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16; -#if BITS_PER_LONG == 64 - start_address |= ((long) upper_start) << 32; - end_address |= ((long) upper_end) << 32; -#endif - - if ((start_address) && (start_address <= end_address)) { - - range = kmalloc (sizeof (struct range_node), GFP_KERNEL); - if (!range) { - err ("out of system memory \n"); - return -ENOMEM; - } - memset (range, 0, sizeof (struct range_node)); - range->start = start_address; - range->end = end_address + 0xfffff; - - if (bus_sec->noPFMemRanges > 0) { - if (!range_exists_already (range, bus_sec, PFMEM)) { - add_range (PFMEM, range, bus_sec); - ++bus_sec->noPFMemRanges; - } else { - kfree (range); - range = NULL; - } - } else { - /* 1st PFMem Range on the bus */ - range->rangeno = 1; - bus_sec->rangePFMem = range; - ++bus_sec->noPFMemRanges; - } - - fix_resources (bus_sec); - if (ibmphp_find_resource (bus_cur, start_address, &pfmem, PFMEM)) { - pfmem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); - if (!pfmem) { - kfree (range); - err ("out of system memory \n"); - return -ENOMEM; - } - memset (pfmem, 0, sizeof (struct resource_node)); - pfmem->type = PFMEM; - pfmem->busno = bus_cur->busno; - pfmem->devfunc = ((device << 3) | (function & 0x7)); - pfmem->start = start_address; - pfmem->end = end_address + 0xfffff; - pfmem->len = pfmem->end - pfmem->start + 1; - pfmem->fromMem = FALSE; - - ibmphp_add_resource (pfmem); - } - } - break; - } /* end of switch */ - } /* end if vendor */ - } /* end for function */ - } /* end for device */ - - bus = &bus_cur; - return 0; -} diff -Nru a/drivers/hotplug/pci_hotplug.h b/drivers/hotplug/pci_hotplug.h --- a/drivers/hotplug/pci_hotplug.h Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,146 +0,0 @@ -/* - * 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 - * - */ -#ifndef _PCI_HOTPLUG_H -#define _PCI_HOTPLUG_H - - -/* These values come from the PCI Hotplug Spec */ -enum pci_bus_speed { - PCI_SPEED_33MHz = 0x00, - PCI_SPEED_66MHz = 0x01, - PCI_SPEED_66MHz_PCIX = 0x02, - PCI_SPEED_100MHz_PCIX = 0x03, - PCI_SPEED_133MHz_PCIX = 0x04, - PCI_SPEED_66MHz_PCIX_266 = 0x09, - PCI_SPEED_100MHz_PCIX_266 = 0x0a, - PCI_SPEED_133MHz_PCIX_266 = 0x0b, - PCI_SPEED_66MHz_PCIX_533 = 0x11, - PCI_SPEED_100MHz_PCIX_533 = 0X12, - PCI_SPEED_133MHz_PCIX_533 = 0x13, - PCI_SPEED_UNKNOWN = 0xff, -}; - -struct hotplug_slot; -struct hotplug_slot_attribute { - struct attribute attr; - ssize_t (*show)(struct hotplug_slot *, char *); - ssize_t (*store)(struct hotplug_slot *, const char *, size_t); -}; -/** - * struct hotplug_slot_ops -the callbacks that the hotplug pci core can use - * @owner: The module owner of this structure - * @enable_slot: Called when the user wants to enable a specific pci slot - * @disable_slot: Called when the user wants to disable a specific pci slot - * @set_attention_status: Called to set the specific slot's attention LED to - * the specified value - * @hardware_test: Called to run a specified hardware test on the specified - * slot. - * @get_power_status: Called to get the current power status of a slot. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. - * @get_attention_status: Called to get the current attention status of a slot. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. - * @get_latch_status: Called to get the current latch status of a slot. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. - * @get_adapter_status: Called to get see if an adapter is present in the slot or not. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. - * @get_max_bus_speed: Called to get the max bus speed for a slot. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. - * @get_cur_bus_speed: Called to get the current bus speed for a slot. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. - * - * The table of function pointers that is passed to the hotplug pci core by a - * hotplug pci driver. These functions are called by the hotplug pci core when - * the user wants to do something to a specific slot (query it for information, - * set an LED, enable / disable power, etc.) - */ -struct hotplug_slot_ops { - struct module *owner; - int (*enable_slot) (struct hotplug_slot *slot); - int (*disable_slot) (struct hotplug_slot *slot); - int (*set_attention_status) (struct hotplug_slot *slot, u8 value); - int (*hardware_test) (struct hotplug_slot *slot, u32 value); - int (*get_power_status) (struct hotplug_slot *slot, u8 *value); - int (*get_attention_status) (struct hotplug_slot *slot, u8 *value); - int (*get_latch_status) (struct hotplug_slot *slot, u8 *value); - int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value); - int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); - int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); -}; - -/** - * struct hotplug_slot_info - used to notify the hotplug pci core of the state of the slot - * @power: if power is enabled or not (1/0) - * @attention_status: if the attention light is enabled or not (1/0) - * @latch_status: if the latch (if any) is open or closed (1/0) - * @adapter_present: if there is a pci board present in the slot or not (1/0) - * - * 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; - enum pci_bus_speed max_bus_speed; - enum pci_bus_speed cur_bus_speed; -}; - -/** - * struct hotplug_slot - used to register a physical slot with the hotplug pci core - * @name: the name of the slot being registered. This string must - * be unique amoung slots registered on this system. - * @ops: pointer to the &struct hotplug_slot_ops to be used for this slot - * @info: pointer to the &struct hotplug_slot_info for the inital values for - * this slot. - * @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 kobject kobj; -}; - -extern int pci_hp_register (struct hotplug_slot *slot); -extern int pci_hp_deregister (struct hotplug_slot *slot); -extern int pci_hp_change_slot_info (struct hotplug_slot *slot, - struct hotplug_slot_info *info); - -#endif - diff -Nru a/drivers/hotplug/pci_hotplug_core.c b/drivers/hotplug/pci_hotplug_core.c --- a/drivers/hotplug/pci_hotplug_core.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,666 +0,0 @@ -/* - * PCI HotPlug Controller Core - * - * Copyright (c) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001-2002 IBM Corp. - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to - * - * Filesystem portion based on work done by Pat Mochel on ddfs/driverfs - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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: %s: " fmt , MY_NAME , __FUNCTION__ , ## arg); } while (0) -#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) -#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) - - -/* local variables */ -static int debug; - -#define DRIVER_VERSION "0.5" -#define DRIVER_AUTHOR "Greg Kroah-Hartman , Scott Murray " -#define DRIVER_DESC "PCI Hot Plug PCI Core" - - -////////////////////////////////////////////////////////////////// - -static LIST_HEAD(pci_hotplug_slot_list); - -static struct subsystem hotplug_slots_subsys; - -static ssize_t hotplug_slot_attr_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct hotplug_slot *slot=container_of(kobj, - struct hotplug_slot,kobj); - struct hotplug_slot_attribute *attribute = - container_of(attr, struct hotplug_slot_attribute, attr); - return attribute->show ? attribute->show(slot, buf) : 0; -} - -static ssize_t hotplug_slot_attr_store(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t len) -{ - struct hotplug_slot *slot=container_of(kobj, - struct hotplug_slot,kobj); - struct hotplug_slot_attribute *attribute = - container_of(attr, struct hotplug_slot_attribute, attr); - return attribute->store ? attribute->store(slot, buf, len) : 0; -} - -static struct sysfs_ops hotplug_slot_sysfs_ops = { - .show = hotplug_slot_attr_show, - .store = hotplug_slot_attr_store, -}; - -static struct kobj_type hotplug_slot_ktype = { - .sysfs_ops = &hotplug_slot_sysfs_ops -}; - -static decl_subsys(hotplug_slots, &hotplug_slot_ktype, NULL); - - -/* these strings match up with the values in pci_bus_speed */ -static char *pci_bus_speed_strings[] = { - "33 MHz PCI", /* 0x00 */ - "66 MHz PCI", /* 0x01 */ - "66 MHz PCIX", /* 0x02 */ - "100 MHz PCIX", /* 0x03 */ - "133 MHz PCIX", /* 0x04 */ - NULL, /* 0x05 */ - NULL, /* 0x06 */ - NULL, /* 0x07 */ - NULL, /* 0x08 */ - "66 MHz PCIX 266", /* 0x09 */ - "100 MHz PCIX 266", /* 0x0a */ - "133 MHz PCIX 266", /* 0x0b */ - NULL, /* 0x0c */ - NULL, /* 0x0d */ - NULL, /* 0x0e */ - NULL, /* 0x0f */ - NULL, /* 0x10 */ - "66 MHz PCIX 533", /* 0x11 */ - "100 MHz PCIX 533", /* 0x12 */ - "133 MHz PCIX 533", /* 0x13 */ -}; - -#ifdef CONFIG_HOTPLUG_PCI_CPCI -extern int cpci_hotplug_init(int debug); -extern void cpci_hotplug_exit(void); -#else -static inline int cpci_hotplug_init(int debug) { return 0; } -static inline void cpci_hotplug_exit(void) { } -#endif - -/* Weee, fun with macros... */ -#define GET_STATUS(name,type) \ -static int get_##name (struct hotplug_slot *slot, type *value) \ -{ \ - struct hotplug_slot_ops *ops = slot->ops; \ - int retval = 0; \ - if (try_module_get(ops->owner)) { \ - if (ops->get_##name) \ - retval = ops->get_##name (slot, value); \ - else \ - *value = slot->info->name; \ - module_put(ops->owner); \ - } \ - return retval; \ -} - -GET_STATUS(power_status, u8) -GET_STATUS(attention_status, u8) -GET_STATUS(latch_status, u8) -GET_STATUS(adapter_status, u8) -GET_STATUS(max_bus_speed, enum pci_bus_speed) -GET_STATUS(cur_bus_speed, enum pci_bus_speed) - -static ssize_t power_read_file (struct hotplug_slot *slot, char *buf) -{ - int retval; - u8 value; - - retval = get_power_status (slot, &value); - if (retval) - goto exit; - retval = sprintf (buf, "%d\n", value); -exit: - return retval; -} - -static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf, - size_t count) -{ - unsigned long lpower; - u8 power; - int retval = 0; - - lpower = simple_strtoul (buf, NULL, 10); - power = (u8)(lpower & 0xff); - dbg ("power = %d\n", power); - - if (!try_module_get(slot->ops->owner)) { - retval = -ENODEV; - goto exit; - } - switch (power) { - case 0: - if (slot->ops->disable_slot) - retval = slot->ops->disable_slot(slot); - break; - - case 1: - if (slot->ops->enable_slot) - retval = slot->ops->enable_slot(slot); - break; - - default: - err ("Illegal value specified for power\n"); - retval = -EINVAL; - } - module_put(slot->ops->owner); - -exit: - if (retval) - return retval; - return count; -} - -static struct hotplug_slot_attribute hotplug_slot_attr_power = { - .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR}, - .show = power_read_file, - .store = power_write_file -}; - -static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf) -{ - int retval; - u8 value; - - retval = get_attention_status (slot, &value); - if (retval) - goto exit; - retval = sprintf (buf, "%d\n", value); - -exit: - return retval; -} - -static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf, - size_t count) -{ - unsigned long lattention; - u8 attention; - int retval = 0; - - lattention = simple_strtoul (buf, NULL, 10); - attention = (u8)(lattention & 0xff); - dbg (" - attention = %d\n", attention); - - if (!try_module_get(slot->ops->owner)) { - retval = -ENODEV; - goto exit; - } - if (slot->ops->set_attention_status) - retval = slot->ops->set_attention_status(slot, attention); - module_put(slot->ops->owner); - -exit: - if (retval) - return retval; - return count; -} - -static struct hotplug_slot_attribute hotplug_slot_attr_attention = { - .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR}, - .show = attention_read_file, - .store = attention_write_file -}; - -static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf) -{ - int retval; - u8 value; - - retval = get_latch_status (slot, &value); - if (retval) - goto exit; - retval = sprintf (buf, "%d\n", value); - -exit: - return retval; -} - -static struct hotplug_slot_attribute hotplug_slot_attr_latch = { - .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO | S_IWUSR}, - .show = latch_read_file, -}; - -static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf) -{ - int retval; - u8 value; - - retval = get_adapter_status (slot, &value); - if (retval) - goto exit; - retval = sprintf (buf, "%d\n", value); - -exit: - return retval; -} - -static struct hotplug_slot_attribute hotplug_slot_attr_presence = { - .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO | S_IWUSR}, - .show = presence_read_file, -}; - -static char *unknown_speed = "Unknown bus speed"; - -static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf) -{ - char *speed_string; - int retval; - enum pci_bus_speed value; - - retval = get_max_bus_speed (slot, &value); - if (retval) - goto exit; - - if (value == PCI_SPEED_UNKNOWN) - speed_string = unknown_speed; - else - speed_string = pci_bus_speed_strings[value]; - - retval = sprintf (buf, "%s\n", speed_string); - -exit: - return retval; -} - -static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = { - .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO | S_IWUSR}, - .show = max_bus_speed_read_file, -}; - -static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf) -{ - char *speed_string; - int retval; - enum pci_bus_speed value; - - retval = get_cur_bus_speed (slot, &value); - if (retval) - goto exit; - - if (value == PCI_SPEED_UNKNOWN) - speed_string = unknown_speed; - else - speed_string = pci_bus_speed_strings[value]; - - retval = sprintf (buf, "%s\n", speed_string); - -exit: - return retval; -} - -static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = { - .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO | S_IWUSR}, - .show = cur_bus_speed_read_file, -}; - -static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf, - size_t count) -{ - unsigned long ltest; - u32 test; - int retval = 0; - - ltest = simple_strtoul (buf, NULL, 10); - test = (u32)(ltest & 0xffffffff); - dbg ("test = %d\n", test); - - if (!try_module_get(slot->ops->owner)) { - retval = -ENODEV; - goto exit; - } - if (slot->ops->hardware_test) - retval = slot->ops->hardware_test(slot, test); - module_put(slot->ops->owner); - -exit: - if (retval) - return retval; - return count; -} - -static struct hotplug_slot_attribute hotplug_slot_attr_test = { - .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR}, - .store = test_write_file -}; - -static int has_power_file (struct hotplug_slot *slot) -{ - if ((!slot) || (!slot->ops)) - return -ENODEV; - if ((slot->ops->enable_slot) || - (slot->ops->disable_slot) || - (slot->ops->get_power_status)) - return 0; - return -ENOENT; -} - -static int has_attention_file (struct hotplug_slot *slot) -{ - if ((!slot) || (!slot->ops)) - return -ENODEV; - if ((slot->ops->set_attention_status) || - (slot->ops->get_attention_status)) - return 0; - return -ENOENT; -} - -static int has_latch_file (struct hotplug_slot *slot) -{ - if ((!slot) || (!slot->ops)) - return -ENODEV; - if (slot->ops->get_latch_status) - return 0; - return -ENOENT; -} - -static int has_adapter_file (struct hotplug_slot *slot) -{ - if ((!slot) || (!slot->ops)) - return -ENODEV; - if (slot->ops->get_adapter_status) - return 0; - return -ENOENT; -} - -static int has_max_bus_speed_file (struct hotplug_slot *slot) -{ - if ((!slot) || (!slot->ops)) - return -ENODEV; - if (slot->ops->get_max_bus_speed) - return 0; - return -ENOENT; -} - -static int has_cur_bus_speed_file (struct hotplug_slot *slot) -{ - if ((!slot) || (!slot->ops)) - return -ENODEV; - if (slot->ops->get_cur_bus_speed) - return 0; - return -ENOENT; -} - -static int has_test_file (struct hotplug_slot *slot) -{ - if ((!slot) || (!slot->ops)) - return -ENODEV; - if (slot->ops->hardware_test) - return 0; - return -ENOENT; -} - -static int fs_add_slot (struct hotplug_slot *slot) -{ - if (has_power_file(slot) == 0) - sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr); - - if (has_attention_file(slot) == 0) - sysfs_create_file(&slot->kobj, &hotplug_slot_attr_attention.attr); - - if (has_latch_file(slot) == 0) - sysfs_create_file(&slot->kobj, &hotplug_slot_attr_latch.attr); - - if (has_adapter_file(slot) == 0) - sysfs_create_file(&slot->kobj, &hotplug_slot_attr_presence.attr); - - if (has_max_bus_speed_file(slot) == 0) - sysfs_create_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); - - if (has_cur_bus_speed_file(slot) == 0) - sysfs_create_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); - - if (has_test_file(slot) == 0) - sysfs_create_file(&slot->kobj, &hotplug_slot_attr_test.attr); - - return 0; -} - -static void fs_remove_slot (struct hotplug_slot *slot) -{ - if (has_power_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); - - if (has_attention_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr); - - if (has_latch_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); - - if (has_adapter_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); - - if (has_max_bus_speed_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); - - if (has_cur_bus_speed_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); - - if (has_test_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr); -} - -static struct hotplug_slot *get_slot_from_name (const char *name) -{ - struct hotplug_slot *slot; - struct list_head *tmp; - - list_for_each (tmp, &pci_hotplug_slot_list) { - slot = list_entry (tmp, struct hotplug_slot, slot_list); - if (strcmp(slot->name, name) == 0) - return slot; - } - return NULL; -} - -/** - * pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem - * @slot: pointer to the &struct hotplug_slot to register - * - * Registers a hotplug slot with the pci hotplug subsystem, which will allow - * userspace interaction to the slot. - * - * Returns 0 if successful, anything else for an error. - */ -int pci_hp_register (struct hotplug_slot *slot) -{ - int result; - - if (slot == NULL) - return -ENODEV; - if ((slot->info == NULL) || (slot->ops == NULL)) - return -EINVAL; - - strlcpy(slot->kobj.name, slot->name, KOBJ_NAME_LEN); - kobj_set_kset_s(slot, hotplug_slots_subsys); - - /* this can fail if we have already registered a slot with the same name */ - if (kobject_register(&slot->kobj)) { - err("Unable to register kobject"); - return -EINVAL; - } - - list_add (&slot->slot_list, &pci_hotplug_slot_list); - - result = fs_add_slot (slot); - dbg ("Added slot %s to the list\n", slot->name); - return result; -} - -/** - * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem - * @slot: pointer to the &struct hotplug_slot to deregister - * - * The @slot must have been registered with the pci hotplug subsystem - * previously with a call to pci_hp_register(). - * - * Returns 0 if successful, anything else for an error. - */ -int pci_hp_deregister (struct hotplug_slot *slot) -{ - struct hotplug_slot *temp; - - if (slot == NULL) - return -ENODEV; - - temp = get_slot_from_name (slot->name); - if (temp != slot) { - return -ENODEV; - } - list_del (&slot->slot_list); - - fs_remove_slot (slot); - dbg ("Removed slot %s from the list\n", slot->name); - kobject_unregister(&slot->kobj); - return 0; -} - -/** - * pci_hp_change_slot_info - changes the slot's information structure in the core - * @slot: pointer to the slot whose info has changed - * @info: pointer to the info copy into the slot's info structure - * - * @slot must have been registered with the pci - * hotplug subsystem previously with a call to pci_hp_register(). - * - * Returns 0 if successful, anything else for an error. - */ -int pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info *info) -{ - if ((slot == NULL) || (info == NULL)) - return -ENODEV; - - /* - * check all fields in the info structure, and update timestamps - * for the files referring to the fields that have now changed. - */ - if ((has_power_file(slot) == 0) && - (slot->info->power_status != info->power_status)) - sysfs_update_file(&slot->kobj, &hotplug_slot_attr_power.attr); - - if ((has_attention_file(slot) == 0) && - (slot->info->attention_status != info->attention_status)) - sysfs_update_file(&slot->kobj, &hotplug_slot_attr_attention.attr); - - if ((has_latch_file(slot) == 0) && - (slot->info->latch_status != info->latch_status)) - sysfs_update_file(&slot->kobj, &hotplug_slot_attr_latch.attr); - - if ((has_adapter_file(slot) == 0) && - (slot->info->adapter_status != info->adapter_status)) - sysfs_update_file(&slot->kobj, &hotplug_slot_attr_presence.attr); - - if ((has_max_bus_speed_file(slot) == 0) && - (slot->info->max_bus_speed != info->max_bus_speed)) - sysfs_update_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); - - if ((has_cur_bus_speed_file(slot) == 0) && - (slot->info->cur_bus_speed != info->cur_bus_speed)) - sysfs_update_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); - - memcpy (slot->info, info, sizeof (struct hotplug_slot_info)); - - return 0; -} - -static int __init pci_hotplug_init (void) -{ - int result; - - kset_set_kset_s(&hotplug_slots_subsys, pci_bus_type.subsys); - result = subsystem_register(&hotplug_slots_subsys); - if (result) { - err("Register subsys with error %d\n", result); - goto exit; - } - result = cpci_hotplug_init(debug); - if (result) { - err ("cpci_hotplug_init with error %d\n", result); - goto err_subsys; - } - - info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); - goto exit; - -err_subsys: - subsystem_unregister(&hotplug_slots_subsys); -exit: - return result; -} - -static void __exit pci_hotplug_exit (void) -{ - cpci_hotplug_exit(); - subsystem_unregister(&hotplug_slots_subsys); -} - -module_init(pci_hotplug_init); -module_exit(pci_hotplug_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_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 -Nru a/drivers/hotplug/pcihp_skeleton.c b/drivers/hotplug/pcihp_skeleton.c --- a/drivers/hotplug/pcihp_skeleton.c Wed Jun 4 18:11:56 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,432 +0,0 @@ -/* - * PCI Hot Plug Controller Skeleton Driver - 0.1 - * - * 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. - * - * This driver is to be used as a skeleton driver to be show how to interface - * with the pci hotplug core easily. - * - * Send feedback to - * - */ - -#include -#include -#include -#include -#include -#include -#include "pci_hotplug.h" - - -#define SLOT_MAGIC 0x67267322 -struct slot { - u32 magic; - u8 number; - struct hotplug_slot *hotplug_slot; - struct list_head slot_list; -}; - -static LIST_HEAD(slot_list); - -#if !defined(CONFIG_HOTPLUG_PCI_SKELETON_MODULE) - #define MY_NAME "pcihp_skeleton" -#else - #define MY_NAME THIS_MODULE->name -#endif - -#define dbg(format, arg...) \ - do { \ - if (debug) \ - printk (KERN_DEBUG "%s: " format "\n", \ - MY_NAME , ## arg); \ - } while (0) -#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) -#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) - - - -/* local variables */ -static int debug; -static int num_slots; - -#define DRIVER_VERSION "0.1" -#define DRIVER_AUTHOR "Greg Kroah-Hartman " -#define DRIVER_DESC "Hot Plug PCI Controller Skeleton Driver" - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_PARM(debug, "i"); -MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); - -static int enable_slot (struct hotplug_slot *slot); -static int disable_slot (struct hotplug_slot *slot); -static int set_attention_status (struct hotplug_slot *slot, u8 value); -static int hardware_test (struct hotplug_slot *slot, u32 value); -static int get_power_status (struct hotplug_slot *slot, u8 *value); -static int get_attention_status (struct hotplug_slot *slot, u8 *value); -static int get_latch_status (struct hotplug_slot *slot, u8 *value); -static int get_adapter_status (struct hotplug_slot *slot, u8 *value); - -static struct hotplug_slot_ops skel_hotplug_slot_ops = { - .owner = THIS_MODULE, - .enable_slot = enable_slot, - .disable_slot = disable_slot, - .set_attention_status = set_attention_status, - .hardware_test = hardware_test, - .get_power_status = get_power_status, - .get_attention_status = get_attention_status, - .get_latch_status = get_latch_status, - .get_adapter_status = get_adapter_status, -}; - - -/* Inline functions to check the sanity of a pointer that is passed to us */ -static inline int slot_paranoia_check (struct slot *slot, const char *function) -{ - if (!slot) { - dbg("%s - slot == NULL", function); - return -1; - } - if (slot->magic != SLOT_MAGIC) { - dbg("%s - bad magic number for slot", function); - return -1; - } - if (!slot->hotplug_slot) { - dbg("%s - slot->hotplug_slot == NULL!", function); - return -1; - } - return 0; -} - -static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function) -{ - struct slot *slot; - - if (!hotplug_slot) { - dbg("%s - hotplug_slot == NULL\n", function); - return NULL; - } - - slot = (struct slot *)hotplug_slot->private; - if (slot_paranoia_check (slot, function)) - return NULL; - return slot; -} - - -static int enable_slot (struct hotplug_slot *hotplug_slot) -{ - struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - /* - * Fill in code here to enable the specified slot - */ - - return retval; -} - - -static int disable_slot (struct hotplug_slot *hotplug_slot) -{ - struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - /* - * Fill in code here to disable the specified slot - */ - - return retval; -} - -static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status) -{ - struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - switch (status) { - case 0: - /* - * Fill in code here to turn light off - */ - break; - - case 1: - default: - /* - * Fill in code here to turn light on - */ - break; - } - - return retval; -} - -static int hardware_test (struct hotplug_slot *hotplug_slot, u32 value) -{ - struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); - - err ("No hardware tests are defined for this driver"); - retval = -ENODEV; - - /* Or you can specify a test if you want to */ - - return retval; -} - -static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value) -{ - struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name); - - /* - * Fill in logic to get the current power status of the specific - * slot and store it in the *value location. - */ - - return retval; -} - -static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value) -{ - struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name); - - /* - * Fill in logic to get the current attention status of the specific - * slot and store it in the *value location. - */ - - return retval; -} - -static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value) -{ - struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name); - - /* - * Fill in logic to get the current latch status of the specific - * slot and store it in the *value location. - */ - - return retval; -} - -static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) -{ - struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); - int retval = 0; - - if (slot == NULL) - return -ENODEV; - - dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name); - - /* - * Fill in logic to get the current adapter status of the specific - * slot and store it in the *value location. - */ - - return retval; -} - -#define SLOT_NAME_SIZE 10 -static void make_slot_name (struct slot *slot) -{ - /* - * Stupid way to make a filename out of the slot name. - * replace this if your hardware provides a better way to name slots. - */ - snprintf (slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d", slot->number); -} - -static int init_slots (void) -{ - struct slot *slot; - struct hotplug_slot *hotplug_slot; - struct hotplug_slot_info *info; - char *name; - int retval = 0; - int i; - - /* - * Create a structure for each slot, and register that slot - * with the pci_hotplug subsystem. - */ - for (i = 0; i < num_slots; ++i) { - slot = kmalloc (sizeof (struct slot), GFP_KERNEL); - if (!slot) - return -ENOMEM; - memset(slot, 0, sizeof(struct slot)); - - hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL); - if (!hotplug_slot) { - kfree (slot); - return -ENOMEM; - } - memset(hotplug_slot, 0, sizeof (struct hotplug_slot)); - slot->hotplug_slot = hotplug_slot; - - info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); - if (!info) { - kfree (hotplug_slot); - kfree (slot); - return -ENOMEM; - } - memset(info, 0, sizeof (struct hotplug_slot_info)); - hotplug_slot->info = info; - - name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL); - if (!name) { - kfree (info); - kfree (hotplug_slot); - kfree (slot); - return -ENOMEM; - } - hotplug_slot->name = name; - - slot->magic = SLOT_MAGIC; - slot->number = i; - - hotplug_slot->private = slot; - make_slot_name (slot); - hotplug_slot->ops = &skel_hotplug_slot_ops; - - /* - * Initilize the slot info structure with some known - * good values. - */ - info->power_status = get_skel_power_status(slot); - info->attention_status = get_skel_attention_status(slot); - info->latch_status = get_skel_latch_status(slot); - info->adapter_status = get_skel_adapter_status(slot); - - dbg ("registering slot %d\n", i); - retval = pci_hp_register (slot->hotplug_slot); - if (retval) { - err ("pci_hp_register failed with error %d\n", retval); - kfree (info); - kfree (name); - kfree (hotplug_slot); - kfree (slot); - return retval; - } - - /* add slot to our internal list */ - list_add (&slot->slot_list, &slot_list); - } - - return retval; -} - -static void cleanup_slots (void) -{ - struct list_head *tmp; - struct slot *slot; - - /* - * Unregister all of our slots with the pci_hotplug subsystem, - * and free up all memory that we had allocated. - */ - list_for_each (tmp, &slot_list) { - slot = list_entry (tmp, struct slot, slot_list); - list_del (&slot->slot_list); - pci_hp_deregister (slot->hotplug_slot); - kfree (slot->hotplug_slot->info); - kfree (slot->hotplug_slot->name); - kfree (slot->hotplug_slot); - kfree (slot); - } - - return; -} - -static int __init pcihp_skel_init(void) -{ - int retval; - - /* - * Do specific initialization stuff for your driver here - * Like initilizing your controller hardware (if any) and - * determining the number of slots you have in the system - * right now. - */ - num_slots = 5; - - retval = init_slots(); - if (retval) - return retval; - - info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); - return 0; -} - -static void __exit pcihp_skel_exit(void) -{ - /* - * Clean everything up. - */ - cleanup_slots(); -} - -module_init(pcihp_skel_init); -module_exit(pcihp_skel_exit); - diff -Nru a/drivers/pci/Makefile b/drivers/pci/Makefile --- a/drivers/pci/Makefile Wed Jun 4 18:11:56 2003 +++ b/drivers/pci/Makefile Wed Jun 4 18:11:56 2003 @@ -12,6 +12,9 @@ obj-$(CONFIG_PCI) += setup-res.o endif +# Build the PCI Hotplug drivers if we were asked to +obj-$(CONFIG_HOTPLUG_PCI) += hotplug/ + # # Some architectures use the generic PCI setup functions # diff -Nru a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/Kconfig Wed Jun 4 18:11:56 2003 @@ -0,0 +1,120 @@ +# +# PCI Hotplug support +# + +menu "PCI Hotplug Support" + depends on HOTPLUG + +config HOTPLUG_PCI + tristate "Support for PCI Hotplug (EXPERIMENTAL)" + depends on PCI && EXPERIMENTAL + ---help--- + Say Y here if you have a motherboard with a PCI Hotplug controller. + This allows you to add and remove PCI cards while the machine is + powered up and running. The file system pcihpfs must be mounted + in order to interact with any PCI Hotplug controllers. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called pci_hotplug. If you want to compile it + as a module, say M here and read . + + When in doubt, say N. + +config HOTPLUG_PCI_COMPAQ + tristate "Compaq PCI Hotplug driver" + depends on HOTPLUG_PCI && X86 + help + Say Y here if you have a motherboard with a Compaq PCI Hotplug + controller. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called cpqphp. If you want to compile it + as a module, say M here and read . + + When in doubt, say N. + +config HOTPLUG_PCI_COMPAQ_NVRAM + bool "Save configuration into NVRAM on Compaq servers" + depends on HOTPLUG_PCI_COMPAQ + help + Say Y here if you have a Compaq server that has a PCI Hotplug + controller. This will allow the PCI Hotplug driver to store the PCI + system configuration options in NVRAM. + + When in doubt, say N. + +config HOTPLUG_PCI_IBM + tristate "IBM PCI Hotplug driver" + depends on HOTPLUG_PCI && X86_IO_APIC && X86 + help + Say Y here if you have a motherboard with a IBM PCI Hotplug + controller. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called cpqphp. If you want to compile it + as a module, say M here and read . + + When in doubt, say N. + +config HOTPLUG_PCI_ACPI + tristate "ACPI PCI Hotplug driver" + depends on ACPI_BUS && HOTPLUG_PCI + help + Say Y here if you have a system that supports PCI Hotplug using + ACPI. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called acpiphp. If you want to compile it + as a module, say M here and read . + + When in doubt, say N. + +config HOTPLUG_PCI_CPCI + tristate "CompactPCI Hotplug driver" + depends on HOTPLUG_PCI + help + Say Y here if you have a CompactPCI system card with CompactPCI + hotswap support per the PICMG 2.1 specification. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called cpci_hotplug. If you want to compile it + as a module, say M here and read . + + When in doubt, say N. + +config HOTPLUG_PCI_CPCI_ZT5550 + tristate "Ziatech ZT5550 CompactPCI Hotplug driver" + depends on HOTPLUG_PCI_CPCI && X86 + help + Say Y here if you have an Performance Technologies (formerly Intel, + formerly just Ziatech) Ziatech ZT5550 CompactPCI system card. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called cpcihp_zt5550. If you want to compile it + as a module, say M here and read . + + When in doubt, say N. + +config HOTPLUG_PCI_CPCI_GENERIC + tristate "Generic port I/O CompactPCI Hotplug driver" + depends on HOTPLUG_PCI_CPCI && X86 + help + Say Y here if you have a CompactPCI system card that exposes the #ENUM + hotswap signal as a bit in a system register that can be read through + standard port I/O. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called cpcihp_generic. If you want to compile it + as a module, say M here and read . + + When in doubt, say N. + +endmenu + diff -Nru a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/Makefile Wed Jun 4 18:11:56 2003 @@ -0,0 +1,44 @@ +# +# Makefile for the Linux kernel pci hotplug controller drivers. +# + +obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o +obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o +obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o +obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o +obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o +obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o + +pci_hotplug-objs := pci_hotplug_core.o + +ifdef CONFIG_HOTPLUG_PCI_CPCI +pci_hotplug-objs += cpci_hotplug_core.o \ + cpci_hotplug_pci.o +endif + +cpqphp-objs := cpqphp_core.o \ + cpqphp_ctrl.o \ + cpqphp_sysfs.o \ + cpqphp_pci.o + +ibmphp-objs := ibmphp_core.o \ + ibmphp_ebda.o \ + ibmphp_pci.o \ + ibmphp_res.o \ + ibmphp_hpc.o + +acpiphp-objs := acpiphp_core.o \ + acpiphp_glue.o \ + acpiphp_pci.o \ + acpiphp_res.o + +ifdef CONFIG_HOTPLUG_PCI_ACPI + EXTRA_CFLAGS += -D_LINUX -I$(TOPDIR)/drivers/acpi + ifdef CONFIG_ACPI_DEBUG + EXTRA_CFLAGS += -DACPI_DEBUG_OUTPUT + endif +endif + +ifeq ($(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM),y) + cpqphp-objs += cpqphp_nvram.o +endif diff -Nru a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/acpiphp.h Wed Jun 4 18:11:56 2003 @@ -0,0 +1,262 @@ +/* + * ACPI PCI Hot Plug Controller Driver + * + * Copyright (c) 1995,2001 Compaq Computer Corporation + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001 IBM Corp. + * Copyright (c) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) + * Copyright (c) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com) + * Copyright (c) 2002 NEC Corporation + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * , + * + * + */ + +#ifndef _ACPIPHP_H +#define _ACPIPHP_H + +#include +#include "pci_hotplug.h" + +#define dbg(format, arg...) \ + do { \ + if (acpiphp_debug) \ + printk(KERN_DEBUG "%s: " format, \ + MY_NAME , ## arg); \ + } while (0) +#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) + +#define SLOT_MAGIC 0x67267322 +/* name size which is used for entries in pcihpfs */ +#define SLOT_NAME_SIZE 32 /* ACPI{_SUN}-{BUS}:{DEV} */ + +struct acpiphp_bridge; +struct acpiphp_slot; +struct pci_resource; + +/* + * struct slot - slot information for each *physical* slot + */ +struct slot { + u32 magic; + u8 number; + struct hotplug_slot *hotplug_slot; + struct list_head slot_list; + + struct acpiphp_slot *acpi_slot; +}; + +/* + * struct pci_resource - describes pci resource (mem, pfmem, io, bus) + */ +struct pci_resource { + struct pci_resource * next; + u64 base; + u32 length; +}; + +/** + * struct hpp_param - ACPI 2.0 _HPP Hot Plug Parameters + * @cache_line_size in DWORD + * @latency_timer in PCI clock + * @enable_SERR 0 or 1 + * @enable_PERR 0 or 1 + */ +struct hpp_param { + u8 cache_line_size; + u8 latency_timer; + u8 enable_SERR; + u8 enable_PERR; +}; + + +/** + * struct acpiphp_bridge - PCI bridge information + * + * for each bridge device in ACPI namespace + */ +struct acpiphp_bridge { + struct list_head list; + acpi_handle handle; + struct acpiphp_slot *slots; + int type; + int nr_slots; + + u8 seg; + u8 bus; + u8 sub; + + u32 flags; + + /* This bus (host bridge) or Secondary bus (PCI-to-PCI bridge) */ + struct pci_bus *pci_bus; + + /* PCI-to-PCI bridge device */ + struct pci_dev *pci_dev; + + /* ACPI 2.0 _HPP parameters */ + struct hpp_param hpp; + + spinlock_t res_lock; + + /* available resources on this bus */ + struct pci_resource *mem_head; + struct pci_resource *p_mem_head; + struct pci_resource *io_head; + struct pci_resource *bus_head; +}; + + +/** + * struct acpiphp_slot - PCI slot information + * + * PCI slot information for each *physical* PCI slot + */ +struct acpiphp_slot { + struct acpiphp_slot *next; + struct acpiphp_bridge *bridge; /* parent */ + struct list_head funcs; /* one slot may have different + objects (i.e. for each function) */ + struct semaphore crit_sect; + + u32 id; /* slot id (serial #) for hotplug core */ + u8 device; /* pci device# */ + + u32 sun; /* ACPI _SUN (slot unique number) */ + u32 slotno; /* slot number relative to bridge */ + u32 flags; /* see below */ +}; + + +/** + * struct acpiphp_func - PCI function information + * + * PCI function information for each object in ACPI namespace + * typically 8 objects per slot (i.e. for each PCI function) + */ +struct acpiphp_func { + struct acpiphp_slot *slot; /* parent */ + + struct list_head sibling; + struct pci_dev *pci_dev; + + acpi_handle handle; + + u8 function; /* pci function# */ + u32 flags; /* see below */ + + /* resources used for this function */ + struct pci_resource *mem_head; + struct pci_resource *p_mem_head; + struct pci_resource *io_head; + struct pci_resource *bus_head; +}; + + +/* PCI bus bridge HID */ +#define ACPI_PCI_HOST_HID "PNP0A03" + +/* PCI BRIDGE type */ +#define BRIDGE_TYPE_HOST 0 +#define BRIDGE_TYPE_P2P 1 + +/* ACPI _STA method value (ignore bit 4; battery present) */ +#define ACPI_STA_PRESENT (0x00000001) +#define ACPI_STA_ENABLED (0x00000002) +#define ACPI_STA_SHOW_IN_UI (0x00000004) +#define ACPI_STA_FUNCTIONING (0x00000008) +#define ACPI_STA_ALL (0x0000000f) + +/* bridge flags */ +#define BRIDGE_HAS_STA (0x00000001) +#define BRIDGE_HAS_EJ0 (0x00000002) +#define BRIDGE_HAS_HPP (0x00000004) +#define BRIDGE_HAS_PS0 (0x00000010) +#define BRIDGE_HAS_PS1 (0x00000020) +#define BRIDGE_HAS_PS2 (0x00000040) +#define BRIDGE_HAS_PS3 (0x00000080) + +/* slot flags */ + +#define SLOT_POWEREDON (0x00000001) +#define SLOT_ENABLED (0x00000002) +#define SLOT_MULTIFUNCTION (x000000004) + +/* function flags */ + +#define FUNC_HAS_STA (0x00000001) +#define FUNC_HAS_EJ0 (0x00000002) +#define FUNC_HAS_PS0 (0x00000010) +#define FUNC_HAS_PS1 (0x00000020) +#define FUNC_HAS_PS2 (0x00000040) +#define FUNC_HAS_PS3 (0x00000080) + +/* not yet */ +#define SLOT_SUPPORT_66MHZ (0x00010000) +#define SLOT_SUPPORT_100MHZ (0x00020000) +#define SLOT_SUPPORT_133MHZ (0x00040000) +#define SLOT_SUPPORT_PCIX (0x00080000) + +/* function prototypes */ + +/* acpiphp_glue.c */ +extern int acpiphp_glue_init (void); +extern void acpiphp_glue_exit (void); +extern int acpiphp_get_num_slots (void); +extern struct acpiphp_slot *get_slot_from_id (int id); +typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data); +extern int acpiphp_for_each_slot (acpiphp_callback fn, void *data); + +extern int acpiphp_check_bridge (struct acpiphp_bridge *bridge); +extern int acpiphp_enable_slot (struct acpiphp_slot *slot); +extern int acpiphp_disable_slot (struct acpiphp_slot *slot); +extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot); +extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot); +extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot); +extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot); + +/* acpiphp_pci.c */ +extern struct pci_dev *acpiphp_allocate_pcidev (struct pci_bus *pbus, int dev, int fn); +extern int acpiphp_configure_slot (struct acpiphp_slot *slot); +extern int acpiphp_configure_function (struct acpiphp_func *func); +extern int acpiphp_unconfigure_function (struct acpiphp_func *func); +extern int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge); +extern int acpiphp_init_func_resource (struct acpiphp_func *func); + +/* acpiphp_res.c */ +extern struct pci_resource *acpiphp_get_io_resource (struct pci_resource **head, u32 size); +extern struct pci_resource *acpiphp_get_max_resource (struct pci_resource **head, u32 size); +extern struct pci_resource *acpiphp_get_resource (struct pci_resource **head, u32 size); +extern struct pci_resource *acpiphp_get_resource_with_base (struct pci_resource **head, u64 base, u32 size); +extern int acpiphp_resource_sort_and_combine (struct pci_resource **head); +extern struct pci_resource *acpiphp_make_resource (u64 base, u32 length); +extern void acpiphp_move_resource (struct pci_resource **from, struct pci_resource **to); +extern void acpiphp_free_resource (struct pci_resource **res); +extern void acpiphp_dump_resource (struct acpiphp_bridge *bridge); /* debug */ +extern void acpiphp_dump_func_resource (struct acpiphp_func *func); /* debug */ + +/* variables */ +extern int acpiphp_debug; + +#endif /* _ACPIPHP_H */ diff -Nru a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/acpiphp_core.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,505 @@ +/* + * ACPI PCI Hot Plug Controller Driver + * + * Copyright (c) 1995,2001 Compaq Computer Corporation + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001 IBM Corp. + * Copyright (c) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) + * Copyright (c) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com) + * Copyright (c) 2002 NEC Corporation + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * , + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pci_hotplug.h" +#include "acpiphp.h" + +static LIST_HEAD(slot_list); + +#if !defined(CONFIG_HOTPLUG_PCI_ACPI_MODULE) + #define MY_NAME "acpiphp" +#else + #define MY_NAME THIS_MODULE->name +#endif + +static int debug; +int acpiphp_debug; + +/* local variables */ +static int num_slots; + +#define DRIVER_VERSION "0.4" +#define DRIVER_AUTHOR "Greg Kroah-Hartman , Takayoshi Kochi " +#define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); + +static int enable_slot (struct hotplug_slot *slot); +static int disable_slot (struct hotplug_slot *slot); +static int set_attention_status (struct hotplug_slot *slot, u8 value); +static int hardware_test (struct hotplug_slot *slot, u32 value); +static int get_power_status (struct hotplug_slot *slot, u8 *value); +static int get_attention_status (struct hotplug_slot *slot, u8 *value); +static int get_latch_status (struct hotplug_slot *slot, u8 *value); +static int get_adapter_status (struct hotplug_slot *slot, u8 *value); +static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value); +static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value); + +static struct hotplug_slot_ops acpi_hotplug_slot_ops = { + .owner = THIS_MODULE, + .enable_slot = enable_slot, + .disable_slot = disable_slot, + .set_attention_status = set_attention_status, + .hardware_test = hardware_test, + .get_power_status = get_power_status, + .get_attention_status = get_attention_status, + .get_latch_status = get_latch_status, + .get_adapter_status = get_adapter_status, + .get_max_bus_speed = get_max_bus_speed, + .get_cur_bus_speed = get_cur_bus_speed, +}; + + +/* 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\n", function); + return -1; + } + if (slot->magic != SLOT_MAGIC) { + dbg("%s - bad magic number for slot\n", function); + return -1; + } + if (!slot->hotplug_slot) { + dbg("%s - slot->hotplug_slot == NULL!\n", 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; +} + + +/** + * enable_slot - power on and enable a slot + * @hotplug_slot: slot to enable + * + * Actual tasks are done in acpiphp_enable_slot() + * + */ +static int enable_slot (struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + /* enable the specified slot */ + retval = acpiphp_enable_slot(slot->acpi_slot); + + return retval; +} + + +/** + * disable_slot - disable and power off a slot + * @hotplug_slot: slot to disable + * + * Actual tasks are done in acpiphp_disable_slot() + * + */ +static int disable_slot (struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + /* disable the specified slot */ + retval = acpiphp_disable_slot(slot->acpi_slot); + + return retval; +} + + +/** + * set_attention_status - set attention LED + * + * TBD: + * ACPI doesn't have known method to manipulate + * attention status LED. + * + */ +static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status) +{ + int retval = 0; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + switch (status) { + case 0: + /* FIXME turn light off */ + hotplug_slot->info->attention_status = 0; + break; + + case 1: + default: + /* FIXME turn light on */ + hotplug_slot->info->attention_status = 1; + break; + } + + return retval; +} + + +/** + * hardware_test - hardware test + * + * We have nothing to do for now... + * + */ +static int hardware_test (struct hotplug_slot *hotplug_slot, u32 value) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + err("No hardware tests are defined for this driver\n"); + retval = -ENODEV; + + return retval; +} + + +/** + * get_power_status - get power status of a slot + * @hotplug_slot: slot to get status + * @value: pointer to store status + * + * Some platforms may not implement _STA method properly. + * In that case, the value returned may not be reliable. + * + */ +static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + *value = acpiphp_get_power_status(slot->acpi_slot); + + return retval; +} + + +/** + * get_attention_status - get attention LED status + * + * TBD: + * ACPI doesn't provide any formal means to access attention LED status. + * + */ +static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + int retval = 0; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + *value = hotplug_slot->info->attention_status; + + return retval; +} + + +/** + * get_latch_status - get latch status of a slot + * @hotplug_slot: slot to get status + * @value: pointer to store status + * + * ACPI doesn't provide any formal means to access latch status. + * Instead, we fake latch status from _STA + * + */ +static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + *value = acpiphp_get_latch_status(slot->acpi_slot); + + return retval; +} + + +/** + * get_adapter_status - get adapter status of a slot + * @hotplug_slot: slot to get status + * @value: pointer to store status + * + * ACPI doesn't provide any formal means to access adapter status. + * Instead, we fake adapter status from _STA + * + */ +static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + *value = acpiphp_get_adapter_status(slot->acpi_slot); + + return retval; +} + + +/* return dummy value because ACPI doesn't provide any method... */ +static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + + if (slot == NULL) + return -ENODEV; + + *value = PCI_SPEED_UNKNOWN; + + return 0; +} + + +/* return dummy value because ACPI doesn't provide any method... */ +static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + + if (slot == NULL) + return -ENODEV; + + *value = PCI_SPEED_UNKNOWN; + + return 0; +} + + +static int init_acpi (void) +{ + int retval; + + /* initialize internal data structure etc. */ + retval = acpiphp_glue_init(); + + /* read initial number of slots */ + if (!retval) { + num_slots = acpiphp_get_num_slots(); + if (num_slots == 0) + retval = -ENODEV; + } + + return retval; +} + + +/** + * make_slot_name - make a slot name that appears in pcihpfs + * @slot: slot to name + * + */ +static void make_slot_name (struct slot *slot) +{ + snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "ACPI%d-%02x:%02x", + slot->acpi_slot->sun, + slot->acpi_slot->bridge->bus, + slot->acpi_slot->device); +} + +/** + * init_slots - initialize 'struct slot' structures for each slot + * + */ +static int init_slots (void) +{ + struct slot *slot; + int retval = 0; + int i; + + for (i = 0; i < num_slots; ++i) { + slot = kmalloc(sizeof(struct slot), GFP_KERNEL); + if (!slot) + return -ENOMEM; + memset(slot, 0, sizeof(struct slot)); + + slot->hotplug_slot = kmalloc(sizeof(struct hotplug_slot), GFP_KERNEL); + if (!slot->hotplug_slot) { + kfree(slot); + return -ENOMEM; + } + memset(slot->hotplug_slot, 0, sizeof(struct hotplug_slot)); + + slot->hotplug_slot->info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); + if (!slot->hotplug_slot->info) { + kfree(slot->hotplug_slot); + kfree(slot); + return -ENOMEM; + } + memset(slot->hotplug_slot->info, 0, sizeof(struct hotplug_slot_info)); + + slot->hotplug_slot->name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL); + if (!slot->hotplug_slot->name) { + kfree(slot->hotplug_slot->info); + kfree(slot->hotplug_slot); + kfree(slot); + return -ENOMEM; + } + + slot->magic = SLOT_MAGIC; + slot->number = i; + + slot->hotplug_slot->private = slot; + slot->hotplug_slot->ops = &acpi_hotplug_slot_ops; + + slot->acpi_slot = get_slot_from_id(i); + slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot); + slot->hotplug_slot->info->attention_status = acpiphp_get_attention_status(slot->acpi_slot); + slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot); + slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot); + + make_slot_name(slot); + + retval = pci_hp_register(slot->hotplug_slot); + if (retval) { + err("pci_hp_register failed with error %d\n", retval); + kfree(slot->hotplug_slot->info); + kfree(slot->hotplug_slot->name); + kfree(slot->hotplug_slot); + kfree(slot); + return retval; + } + + /* add slot to our internal list */ + list_add(&slot->slot_list, &slot_list); + info("Slot [%s] registered\n", slot->hotplug_slot->name); + } + + return retval; +} + + +static void cleanup_slots (void) +{ + struct list_head *tmp, *n; + struct slot *slot; + + list_for_each_safe (tmp, n, &slot_list) { + slot = list_entry(tmp, struct slot, slot_list); + list_del(&slot->slot_list); + pci_hp_deregister(slot->hotplug_slot); + kfree(slot->hotplug_slot->info); + kfree(slot->hotplug_slot->name); + kfree(slot->hotplug_slot); + kfree(slot); + } + + return; +} + + +static int __init acpiphp_init(void) +{ + int retval; + + info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); + + acpiphp_debug = debug; + + /* read all the ACPI info from the system */ + retval = init_acpi(); + if (retval) + return retval; + + retval = init_slots(); + if (retval) + return retval; + + return 0; +} + + +static void __exit acpiphp_exit(void) +{ + cleanup_slots(); + /* deallocate internal data structures etc. */ + acpiphp_glue_exit(); +} + +module_init(acpiphp_init); +module_exit(acpiphp_exit); diff -Nru a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/acpiphp_glue.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,1335 @@ +/* + * ACPI PCI HotPlug glue functions to ACPI CA subsystem + * + * Copyright (c) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com) + * Copyright (c) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) + * Copyright (c) 2002 NEC Corporation + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pci_hotplug.h" +#include "acpiphp.h" + +static LIST_HEAD(bridge_list); + +#define MY_NAME "acpiphp_glue" + +static void handle_hotplug_event_bridge (acpi_handle, u32, void *); +static void handle_hotplug_event_func (acpi_handle, u32, void *); + +/* + * initialization & terminatation routines + */ + +/** + * is_ejectable - determine if a slot is ejectable + * @handle: handle to acpi namespace + * + * Ejectable slot should satisfy at least these conditions: + * + * 1. has _ADR method + * 2. has _EJ0 method + * + * optionally + * + * 1. has _STA method + * 2. has _PS0 method + * 3. has _PS3 method + * 4. .. + * + */ +static int is_ejectable (acpi_handle handle) +{ + acpi_status status; + acpi_handle tmp; + + status = acpi_get_handle(handle, "_ADR", &tmp); + if (ACPI_FAILURE(status)) { + return 0; + } + + status = acpi_get_handle(handle, "_EJ0", &tmp); + if (ACPI_FAILURE(status)) { + return 0; + } + + return 1; +} + + +/* callback routine to check the existence of ejectable slots */ +static acpi_status +is_ejectable_slot (acpi_handle handle, u32 lvl, void *context, void **rv) +{ + int *count = (int *)context; + + if (is_ejectable(handle)) { + (*count)++; + /* only one ejectable slot is enough */ + return AE_CTRL_TERMINATE; + } else { + return AE_OK; + } +} + + +/* callback routine to register each ACPI PCI slot object */ +static acpi_status +register_slot (acpi_handle handle, u32 lvl, void *context, void **rv) +{ + struct acpiphp_bridge *bridge = (struct acpiphp_bridge *)context; + struct acpiphp_slot *slot; + struct acpiphp_func *newfunc; + acpi_handle tmp; + acpi_status status = AE_OK; + unsigned long adr, sun; + int device, function; + static int num_slots = 0; /* XXX if we support I/O node hotplug... */ + + status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); + + if (ACPI_FAILURE(status)) + return AE_OK; + + status = acpi_get_handle(handle, "_EJ0", &tmp); + + if (ACPI_FAILURE(status)) + return AE_OK; + + device = (adr >> 16) & 0xffff; + function = adr & 0xffff; + + newfunc = kmalloc(sizeof(struct acpiphp_func), GFP_KERNEL); + if (!newfunc) + return AE_NO_MEMORY; + memset(newfunc, 0, sizeof(struct acpiphp_func)); + + INIT_LIST_HEAD(&newfunc->sibling); + newfunc->handle = handle; + newfunc->function = function; + newfunc->flags = FUNC_HAS_EJ0; + + if (ACPI_SUCCESS(acpi_get_handle(handle, "_STA", &tmp))) + newfunc->flags |= FUNC_HAS_STA; + + if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS0", &tmp))) + newfunc->flags |= FUNC_HAS_PS0; + + if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp))) + newfunc->flags |= FUNC_HAS_PS3; + + status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun); + if (ACPI_FAILURE(status)) + sun = -1; + + /* search for objects that share the same slot */ + for (slot = bridge->slots; slot; slot = slot->next) + if (slot->device == device) { + if (slot->sun != sun) + warn("sibling found, but _SUN doesn't match!\n"); + break; + } + + if (!slot) { + slot = kmalloc(sizeof(struct acpiphp_slot), GFP_KERNEL); + if (!slot) { + kfree(newfunc); + return AE_NO_MEMORY; + } + + memset(slot, 0, sizeof(struct acpiphp_slot)); + slot->bridge = bridge; + slot->id = num_slots++; + slot->device = device; + slot->sun = sun; + INIT_LIST_HEAD(&slot->funcs); + init_MUTEX(&slot->crit_sect); + + slot->next = bridge->slots; + bridge->slots = slot; + + bridge->nr_slots++; + + dbg("found ACPI PCI Hotplug slot at PCI %02x:%02x Slot:%d\n", + slot->bridge->bus, slot->device, slot->sun); + } + + newfunc->slot = slot; + list_add_tail(&newfunc->sibling, &slot->funcs); + + /* associate corresponding pci_dev */ + newfunc->pci_dev = pci_find_slot(bridge->bus, + PCI_DEVFN(device, function)); + if (newfunc->pci_dev) { + if (acpiphp_init_func_resource(newfunc) < 0) { + kfree(newfunc); + return AE_ERROR; + } + slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); + } + + /* install notify handler */ + status = acpi_install_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, + handle_hotplug_event_func, + newfunc); + + if (ACPI_FAILURE(status)) { + err("failed to register interrupt notify handler\n"); + kfree(newfunc); + return status; + } + + return AE_OK; +} + + +/* see if it's worth looking at this bridge */ +static int detect_ejectable_slots (acpi_handle *bridge_handle) +{ + acpi_status status; + int count; + + count = 0; + + /* only check slots defined directly below bridge object */ + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1, + is_ejectable_slot, (void *)&count, NULL); + + return count; +} + + +/* decode ACPI _CRS data and convert into our internal resource list + * TBD: _TRA, etc. + */ +static acpi_status +decode_acpi_resource (struct acpi_resource *resource, void *context) +{ + struct acpiphp_bridge *bridge = (struct acpiphp_bridge *) context; + struct acpi_resource_address64 address; + struct pci_resource *res; + + if (resource->id != ACPI_RSTYPE_ADDRESS16 && + resource->id != ACPI_RSTYPE_ADDRESS32 && + resource->id != ACPI_RSTYPE_ADDRESS64) + return AE_OK; + + acpi_resource_to_address64(resource, &address); + + if (address.producer_consumer == ACPI_PRODUCER && address.address_length > 0) { + dbg("resource type: %d: 0x%llx - 0x%llx\n", address.resource_type, address.min_address_range, address.max_address_range); + res = acpiphp_make_resource(address.min_address_range, + address.address_length); + if (!res) { + err("out of memory\n"); + return AE_OK; + } + + switch (address.resource_type) { + case ACPI_MEMORY_RANGE: + if (address.attribute.memory.cache_attribute == ACPI_PREFETCHABLE_MEMORY) { + res->next = bridge->p_mem_head; + bridge->p_mem_head = res; + } else { + res->next = bridge->mem_head; + bridge->mem_head = res; + } + break; + case ACPI_IO_RANGE: + res->next = bridge->io_head; + bridge->io_head = res; + break; + case ACPI_BUS_NUMBER_RANGE: + res->next = bridge->bus_head; + bridge->bus_head = res; + break; + default: + /* invalid type */ + kfree(res); + break; + } + } + + return AE_OK; +} + +/* decode ACPI 2.0 _HPP hot plug parameters */ +static void decode_hpp(struct acpiphp_bridge *bridge) +{ + acpi_status status; + struct acpi_buffer buffer = { .length = ACPI_ALLOCATE_BUFFER, + .pointer = NULL}; + union acpi_object *package; + int i; + + /* default numbers */ + bridge->hpp.cache_line_size = 0x10; + bridge->hpp.latency_timer = 0x40; + bridge->hpp.enable_SERR = 0; + bridge->hpp.enable_PERR = 0; + + status = acpi_evaluate_object(bridge->handle, "_HPP", NULL, &buffer); + + if (ACPI_FAILURE(status)) { + dbg("_HPP evaluation failed\n"); + return; + } + + package = (union acpi_object *) buffer.pointer; + + if (!package || package->type != ACPI_TYPE_PACKAGE || + package->package.count != 4 || !package->package.elements) { + err("invalid _HPP object; ignoring\n"); + goto err_exit; + } + + for (i = 0; i < 4; i++) { + if (package->package.elements[i].type != ACPI_TYPE_INTEGER) { + err("invalid _HPP parameter type; ignoring\n"); + goto err_exit; + } + } + + bridge->hpp.cache_line_size = package->package.elements[0].integer.value; + bridge->hpp.latency_timer = package->package.elements[1].integer.value; + bridge->hpp.enable_SERR = package->package.elements[2].integer.value; + bridge->hpp.enable_PERR = package->package.elements[3].integer.value; + + dbg("_HPP parameter = (%02x, %02x, %02x, %02x)\n", + bridge->hpp.cache_line_size, + bridge->hpp.latency_timer, + bridge->hpp.enable_SERR, + bridge->hpp.enable_PERR); + + bridge->flags |= BRIDGE_HAS_HPP; + + err_exit: + kfree(buffer.pointer); +} + + +/* initialize miscellaneous stuff for both root and PCI-to-PCI bridge */ +static void init_bridge_misc (struct acpiphp_bridge *bridge) +{ + acpi_status status; + + /* decode ACPI 2.0 _HPP (hot plug parameters) */ + decode_hpp(bridge); + + /* subtract all resources already allocated */ + acpiphp_detect_pci_resource(bridge); + + /* register all slot objects under this bridge */ + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, (u32)1, + register_slot, bridge, NULL); + + /* install notify handler */ + status = acpi_install_notify_handler(bridge->handle, + ACPI_SYSTEM_NOTIFY, + handle_hotplug_event_bridge, + bridge); + + if (ACPI_FAILURE(status)) { + err("failed to register interrupt notify handler\n"); + } + + list_add(&bridge->list, &bridge_list); + + dbg("Bridge resource:\n"); + acpiphp_dump_resource(bridge); +} + + +/* allocate and initialize host bridge data structure */ +static void add_host_bridge (acpi_handle *handle, int seg, int bus) +{ + acpi_status status; + struct acpiphp_bridge *bridge; + + bridge = kmalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); + if (bridge == NULL) + return; + + memset(bridge, 0, sizeof(struct acpiphp_bridge)); + + bridge->type = BRIDGE_TYPE_HOST; + bridge->handle = handle; + bridge->seg = seg; + bridge->bus = bus; + + bridge->pci_bus = pci_find_bus(bus); + + bridge->res_lock = SPIN_LOCK_UNLOCKED; + + /* to be overridden when we decode _CRS */ + bridge->sub = bridge->bus; + + /* decode resources */ + + status = acpi_walk_resources(handle, METHOD_NAME__CRS, + decode_acpi_resource, bridge); + + if (ACPI_FAILURE(status)) { + err("failed to decode bridge resources\n"); + kfree(bridge); + return; + } + + acpiphp_resource_sort_and_combine(&bridge->io_head); + acpiphp_resource_sort_and_combine(&bridge->mem_head); + acpiphp_resource_sort_and_combine(&bridge->p_mem_head); + acpiphp_resource_sort_and_combine(&bridge->bus_head); + + dbg("ACPI _CRS resource:\n"); + acpiphp_dump_resource(bridge); + + if (bridge->bus_head) { + bridge->bus = bridge->bus_head->base; + bridge->sub = bridge->bus_head->base + bridge->bus_head->length - 1; + } + + init_bridge_misc(bridge); +} + + +/* allocate and initialize PCI-to-PCI bridge data structure */ +static void add_p2p_bridge (acpi_handle *handle, int seg, int bus, int dev, int fn) +{ + struct acpiphp_bridge *bridge; + u8 tmp8; + u16 tmp16; + u64 base64, limit64; + u32 base, limit, base32u, limit32u; + + bridge = kmalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); + if (bridge == NULL) { + err("out of memory\n"); + return; + } + + memset(bridge, 0, sizeof(struct acpiphp_bridge)); + + bridge->type = BRIDGE_TYPE_P2P; + bridge->handle = handle; + bridge->seg = seg; + + bridge->pci_dev = pci_find_slot(bus, PCI_DEVFN(dev, fn)); + if (!bridge->pci_dev) { + err("Can't get pci_dev\n"); + kfree(bridge); + return; + } + + bridge->pci_bus = bridge->pci_dev->subordinate; + if (!bridge->pci_bus) { + err("This is not a PCI-to-PCI bridge!\n"); + kfree(bridge); + return; + } + + bridge->res_lock = SPIN_LOCK_UNLOCKED; + + bridge->bus = bridge->pci_bus->number; + bridge->sub = bridge->pci_bus->subordinate; + + /* + * decode resources under this P2P bridge + */ + + /* I/O resources */ + pci_read_config_byte(bridge->pci_dev, PCI_IO_BASE, &tmp8); + base = tmp8; + pci_read_config_byte(bridge->pci_dev, PCI_IO_LIMIT, &tmp8); + limit = tmp8; + + switch (base & PCI_IO_RANGE_TYPE_MASK) { + case PCI_IO_RANGE_TYPE_16: + base = (base << 8) & 0xf000; + limit = ((limit << 8) & 0xf000) + 0xfff; + bridge->io_head = acpiphp_make_resource((u64)base, limit - base + 1); + if (!bridge->io_head) { + err("out of memory\n"); + kfree(bridge); + return; + } + dbg("16bit I/O range: %04x-%04x\n", + (u32)bridge->io_head->base, + (u32)(bridge->io_head->base + bridge->io_head->length - 1)); + break; + case PCI_IO_RANGE_TYPE_32: + pci_read_config_word(bridge->pci_dev, PCI_IO_BASE_UPPER16, &tmp16); + base = ((u32)tmp16 << 16) | ((base << 8) & 0xf000); + pci_read_config_word(bridge->pci_dev, PCI_IO_LIMIT_UPPER16, &tmp16); + limit = (((u32)tmp16 << 16) | ((limit << 8) & 0xf000)) + 0xfff; + bridge->io_head = acpiphp_make_resource((u64)base, limit - base + 1); + if (!bridge->io_head) { + err("out of memory\n"); + kfree(bridge); + return; + } + dbg("32bit I/O range: %08x-%08x\n", + (u32)bridge->io_head->base, + (u32)(bridge->io_head->base + bridge->io_head->length - 1)); + break; + case 0x0f: + dbg("I/O space unsupported\n"); + break; + default: + warn("Unknown I/O range type\n"); + } + + /* Memory resources (mandatory for P2P bridge) */ + pci_read_config_word(bridge->pci_dev, PCI_MEMORY_BASE, &tmp16); + base = (tmp16 & 0xfff0) << 16; + pci_read_config_word(bridge->pci_dev, PCI_MEMORY_LIMIT, &tmp16); + limit = ((tmp16 & 0xfff0) << 16) | 0xfffff; + bridge->mem_head = acpiphp_make_resource((u64)base, limit - base + 1); + if (!bridge->mem_head) { + err("out of memory\n"); + kfree(bridge); + return; + } + dbg("32bit Memory range: %08x-%08x\n", + (u32)bridge->mem_head->base, + (u32)(bridge->mem_head->base + bridge->mem_head->length-1)); + + /* Prefetchable Memory resources (optional) */ + pci_read_config_word(bridge->pci_dev, PCI_PREF_MEMORY_BASE, &tmp16); + base = tmp16; + pci_read_config_word(bridge->pci_dev, PCI_PREF_MEMORY_LIMIT, &tmp16); + limit = tmp16; + + switch (base & PCI_MEMORY_RANGE_TYPE_MASK) { + case PCI_PREF_RANGE_TYPE_32: + base = (base & 0xfff0) << 16; + limit = ((limit & 0xfff0) << 16) | 0xfffff; + bridge->p_mem_head = acpiphp_make_resource((u64)base, limit - base + 1); + if (!bridge->p_mem_head) { + err("out of memory\n"); + kfree(bridge); + return; + } + dbg("32bit Prefetchable memory range: %08x-%08x\n", + (u32)bridge->p_mem_head->base, + (u32)(bridge->p_mem_head->base + bridge->p_mem_head->length - 1)); + break; + case PCI_PREF_RANGE_TYPE_64: + pci_read_config_dword(bridge->pci_dev, PCI_PREF_BASE_UPPER32, &base32u); + pci_read_config_dword(bridge->pci_dev, PCI_PREF_LIMIT_UPPER32, &limit32u); + base64 = ((u64)base32u << 32) | ((base & 0xfff0) << 16); + limit64 = (((u64)limit32u << 32) | ((limit & 0xfff0) << 16)) + 0xfffff; + + bridge->p_mem_head = acpiphp_make_resource(base64, limit64 - base64 + 1); + if (!bridge->p_mem_head) { + err("out of memory\n"); + kfree(bridge); + return; + } + dbg("64bit Prefetchable memory range: %08x%08x-%08x%08x\n", + (u32)(bridge->p_mem_head->base >> 32), + (u32)(bridge->p_mem_head->base & 0xffffffff), + (u32)((bridge->p_mem_head->base + bridge->p_mem_head->length - 1) >> 32), + (u32)((bridge->p_mem_head->base + bridge->p_mem_head->length - 1) & 0xffffffff)); + break; + case 0x0f: + break; + default: + warn("Unknown prefetchale memory type\n"); + } + + init_bridge_misc(bridge); +} + + +/* callback routine to find P2P bridges */ +static acpi_status +find_p2p_bridge (acpi_handle handle, u32 lvl, void *context, void **rv) +{ + acpi_status status; + acpi_handle dummy_handle; + unsigned long *segbus = context; + unsigned long tmp; + int seg, bus, device, function; + struct pci_dev *dev; + + /* get PCI address */ + seg = (*segbus >> 8) & 0xff; + bus = *segbus & 0xff; + + status = acpi_get_handle(handle, "_ADR", &dummy_handle); + if (ACPI_FAILURE(status)) + return AE_OK; /* continue */ + + status = acpi_evaluate_integer(handle, "_ADR", NULL, &tmp); + if (ACPI_FAILURE(status)) { + dbg("%s: _ADR evaluation failure\n", __FUNCTION__); + return AE_OK; + } + + device = (tmp >> 16) & 0xffff; + function = tmp & 0xffff; + + dev = pci_find_slot(bus, PCI_DEVFN(device, function)); + + if (!dev) + return AE_OK; + + if (!dev->subordinate) + return AE_OK; + + /* check if this bridge has ejectable slots */ + if (detect_ejectable_slots(handle) > 0) { + dbg("found PCI-to-PCI bridge at PCI %s\n", dev->slot_name); + add_p2p_bridge(handle, seg, bus, device, function); + } + + return AE_OK; +} + + +/* find hot-pluggable slots, and then find P2P bridge */ +static int add_bridges(struct acpi_device *device) +{ + acpi_handle *handle = device->handle; + acpi_status status; + unsigned long tmp; + int seg, bus; + acpi_handle dummy_handle; + + /* if the bridge doesn't have _STA, we assume it is always there */ + status = acpi_get_handle(handle, "_STA", &dummy_handle); + if (ACPI_SUCCESS(status)) { + status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp); + if (ACPI_FAILURE(status)) { + dbg("%s: _STA evaluation failure\n", __FUNCTION__); + return 0; + } + if ((tmp & ACPI_STA_FUNCTIONING) == 0) + /* don't register this object */ + return 0; + } + + /* get PCI segment number */ + status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp); + + seg = ACPI_SUCCESS(status) ? tmp : 0; + + /* get PCI bus number */ + status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp); + + if (ACPI_SUCCESS(status)) { + bus = tmp; + } else { + warn("can't get bus number, assuming 0\n"); + bus = 0; + } + + /* check if this bridge has ejectable slots */ + if (detect_ejectable_slots(handle) > 0) { + dbg("found PCI host-bus bridge with hot-pluggable slots\n"); + add_host_bridge(handle, seg, bus); + return 0; + } + + tmp = seg << 8 | bus; + + /* search P2P bridges under this host bridge */ + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, + find_p2p_bridge, &tmp, NULL); + + if (ACPI_FAILURE(status)) + warn("find_p2p_bridge faied (error code = 0x%x)\n",status); + + return 0; +} + + +static int power_on_slot (struct acpiphp_slot *slot) +{ + acpi_status status; + struct acpiphp_func *func; + struct list_head *l; + int retval = 0; + + /* is this already enabled? */ + if (slot->flags & SLOT_POWEREDON) + goto err_exit; + + list_for_each (l, &slot->funcs) { + func = list_entry(l, struct acpiphp_func, sibling); + + if (func->flags & FUNC_HAS_PS0) { + dbg("%s: executing _PS0 on %s\n", __FUNCTION__, + func->pci_dev->slot_name); + status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL); + if (ACPI_FAILURE(status)) { + warn("%s: _PS0 failed\n", __FUNCTION__); + retval = -1; + goto err_exit; + } + } + } + + /* TBD: evaluate _STA to check if the slot is enabled */ + + slot->flags |= SLOT_POWEREDON; + + err_exit: + return retval; +} + + +static int power_off_slot (struct acpiphp_slot *slot) +{ + acpi_status status; + struct acpiphp_func *func; + struct list_head *l; + struct acpi_object_list arg_list; + union acpi_object arg; + + int retval = 0; + + /* is this already enabled? */ + if ((slot->flags & SLOT_POWEREDON) == 0) + goto err_exit; + + list_for_each (l, &slot->funcs) { + func = list_entry(l, struct acpiphp_func, sibling); + + if (func->flags & FUNC_HAS_PS3) { + dbg("%s: executing _PS3 on %s\n", __FUNCTION__, + func->pci_dev->slot_name); + status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL); + if (ACPI_FAILURE(status)) { + warn("%s: _PS3 failed\n", __FUNCTION__); + retval = -1; + goto err_exit; + } + } + } + + list_for_each (l, &slot->funcs) { + func = list_entry(l, struct acpiphp_func, sibling); + + if (func->flags & FUNC_HAS_EJ0) { + dbg("%s: executing _EJ0 on %s\n", __FUNCTION__, + func->pci_dev->slot_name); + + /* _EJ0 method take one argument */ + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = 1; + + status = acpi_evaluate_object(func->handle, "_EJ0", &arg_list, NULL); + if (ACPI_FAILURE(status)) { + warn("%s: _EJ0 failed\n", __FUNCTION__); + retval = -1; + goto err_exit; + } + } + } + + /* TBD: evaluate _STA to check if the slot is disabled */ + + slot->flags &= (~SLOT_POWEREDON); + + err_exit: + return retval; +} + + +/** + * enable_device - enable, configure a slot + * @slot: slot to be enabled + * + * This function should be called per *physical slot*, + * not per each slot object in ACPI namespace. + * + */ +static int enable_device (struct acpiphp_slot *slot) +{ + u8 bus; + struct pci_dev *dev; + struct pci_bus *child; + struct list_head *l; + struct acpiphp_func *func; + int retval = 0; + int num; + + if (slot->flags & SLOT_ENABLED) + goto err_exit; + + /* sanity check: dev should be NULL when hot-plugged in */ + dev = pci_find_slot(slot->bridge->bus, PCI_DEVFN(slot->device, 0)); + if (dev) { + /* This case shouldn't happen */ + err("pci_dev structure already exists.\n"); + retval = -1; + goto err_exit; + } + + /* allocate resources to device */ + retval = acpiphp_configure_slot(slot); + if (retval) + goto err_exit; + + /* returned `dev' is the *first function* only! */ + num = pci_scan_slot(slot->bridge->pci_bus, PCI_DEVFN(slot->device, 0)); + if (num) + pci_bus_add_devices(slot->bridge->pci_bus); + dev = pci_find_slot(slot->bridge->bus, PCI_DEVFN(slot->device, 0)); + + if (!dev) { + err("No new device found\n"); + retval = -1; + goto err_exit; + } + + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + pci_read_config_byte(dev, PCI_SECONDARY_BUS, &bus); + child = (struct pci_bus*) pci_add_new_bus(dev->bus, dev, bus); + pci_do_scan_bus(child); + } + + /* associate pci_dev to our representation */ + list_for_each (l, &slot->funcs) { + func = list_entry(l, struct acpiphp_func, sibling); + + func->pci_dev = pci_find_slot(slot->bridge->bus, + PCI_DEVFN(slot->device, + func->function)); + if (!func->pci_dev) + continue; + + /* configure device */ + retval = acpiphp_configure_function(func); + if (retval) + goto err_exit; + } + + slot->flags |= SLOT_ENABLED; + + dbg("Available resources:\n"); + acpiphp_dump_resource(slot->bridge); + + err_exit: + return retval; +} + + +/** + * disable_device - disable a slot + */ +static int disable_device (struct acpiphp_slot *slot) +{ + int retval = 0; + struct acpiphp_func *func; + struct list_head *l; + + /* is this slot already disabled? */ + if (!(slot->flags & SLOT_ENABLED)) + goto err_exit; + + list_for_each (l, &slot->funcs) { + func = list_entry(l, struct acpiphp_func, sibling); + + if (func->pci_dev) { + if (acpiphp_unconfigure_function(func) == 0) { + func->pci_dev = NULL; + } else { + err("failed to unconfigure device\n"); + retval = -1; + goto err_exit; + } + } + } + + slot->flags &= (~SLOT_ENABLED); + + err_exit: + return retval; +} + + +/** + * get_slot_status - get ACPI slot status + * + * if a slot has _STA for each function and if any one of them + * returned non-zero status, return it + * + * if a slot doesn't have _STA and if any one of its functions' + * configuration space is configured, return 0x0f as a _STA + * + * otherwise return 0 + */ +static unsigned int get_slot_status (struct acpiphp_slot *slot) +{ + acpi_status status; + unsigned long sta = 0; + u32 dvid; + struct list_head *l; + struct acpiphp_func *func; + + list_for_each (l, &slot->funcs) { + func = list_entry(l, struct acpiphp_func, sibling); + + if (func->flags & FUNC_HAS_STA) { + status = acpi_evaluate_integer(func->handle, "_STA", NULL, &sta); + if (ACPI_SUCCESS(status) && sta) + break; + } else { + pci_bus_read_config_dword(slot->bridge->pci_bus, + PCI_DEVFN(slot->device, + func->function), + PCI_VENDOR_ID, &dvid); + if (dvid != 0xffffffff) { + sta = ACPI_STA_ALL; + break; + } + } + } + + return (unsigned int)sta; +} + + +/* + * ACPI event handlers + */ + +/** + * handle_hotplug_event_bridge - handle ACPI event on bridges + * + * @handle: Notify()'ed acpi_handle + * @type: Notify code + * @context: pointer to acpiphp_bridge structure + * + * handles ACPI event notification on {host,p2p} bridges + * + */ +static void handle_hotplug_event_bridge (acpi_handle handle, u32 type, void *context) +{ + struct acpiphp_bridge *bridge; + char objname[64]; + struct acpi_buffer buffer = { .length = sizeof(objname), + .pointer = objname }; + + bridge = (struct acpiphp_bridge *)context; + + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + /* bus re-enumerate */ + dbg("%s: Bus check notify on %s\n", __FUNCTION__, objname); + acpiphp_check_bridge(bridge); + break; + + case ACPI_NOTIFY_DEVICE_CHECK: + /* device check */ + dbg("%s: Device check notify on %s\n", __FUNCTION__, objname); + acpiphp_check_bridge(bridge); + break; + + case ACPI_NOTIFY_DEVICE_WAKE: + /* wake event */ + dbg("%s: Device wake notify on %s\n", __FUNCTION__, objname); + break; + + case ACPI_NOTIFY_EJECT_REQUEST: + /* request device eject */ + dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname); + break; + + default: + warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); + break; + } +} + + +/** + * handle_hotplug_event_func - handle ACPI event on functions (i.e. slots) + * + * @handle: Notify()'ed acpi_handle + * @type: Notify code + * @context: pointer to acpiphp_func structure + * + * handles ACPI event notification on slots + * + */ +static void handle_hotplug_event_func (acpi_handle handle, u32 type, void *context) +{ + struct acpiphp_func *func; + char objname[64]; + struct acpi_buffer buffer = { .length = sizeof(objname), + .pointer = objname }; + + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + + func = (struct acpiphp_func *)context; + + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + /* bus re-enumerate */ + dbg("%s: Bus check notify on %s\n", __FUNCTION__, objname); + acpiphp_enable_slot(func->slot); + break; + + case ACPI_NOTIFY_DEVICE_CHECK: + /* device check : re-enumerate from parent bus */ + dbg("%s: Device check notify on %s\n", __FUNCTION__, objname); + acpiphp_check_bridge(func->slot->bridge); + break; + + case ACPI_NOTIFY_DEVICE_WAKE: + /* wake event */ + dbg("%s: Device wake notify on %s\n", __FUNCTION__, objname); + break; + + case ACPI_NOTIFY_EJECT_REQUEST: + /* request device eject */ + dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname); + acpiphp_disable_slot(func->slot); + break; + + default: + warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); + break; + } +} + +static struct acpi_driver acpi_pci_hp_driver = { + .name = "pci_hp", + .class = "", + .ids = ACPI_PCI_HOST_HID, + .ops = { + .add = add_bridges, + } +}; + +/** + * acpiphp_glue_init - initializes all PCI hotplug - ACPI glue data structures + * + */ +int acpiphp_glue_init (void) +{ + acpi_status status; + + if (list_empty(&pci_root_buses)) + return -1; + + status = acpi_bus_register_driver(&acpi_pci_hp_driver); + + if (ACPI_FAILURE(status)) { + err("%s: acpi_walk_namespace() failed\n", __FUNCTION__); + return -1; + } + + return 0; +} + + +/** + * acpiphp_glue_exit - terminates all PCI hotplug - ACPI glue data structures + * + * This function frees all data allocated in acpiphp_glue_init() + */ +void acpiphp_glue_exit (void) +{ + struct list_head *l1, *l2, *n1, *n2; + struct acpiphp_bridge *bridge; + struct acpiphp_slot *slot, *next; + struct acpiphp_func *func; + acpi_status status; + + list_for_each_safe (l1, n1, &bridge_list) { + bridge = (struct acpiphp_bridge *)l1; + slot = bridge->slots; + while (slot) { + next = slot->next; + list_for_each_safe (l2, n2, &slot->funcs) { + func = list_entry(l2, struct acpiphp_func, sibling); + acpiphp_free_resource(&func->io_head); + acpiphp_free_resource(&func->mem_head); + acpiphp_free_resource(&func->p_mem_head); + acpiphp_free_resource(&func->bus_head); + status = acpi_remove_notify_handler(func->handle, + ACPI_SYSTEM_NOTIFY, + handle_hotplug_event_func); + if (ACPI_FAILURE(status)) + err("failed to remove notify handler\n"); + kfree(func); + } + kfree(slot); + slot = next; + } + status = acpi_remove_notify_handler(bridge->handle, ACPI_SYSTEM_NOTIFY, + handle_hotplug_event_bridge); + if (ACPI_FAILURE(status)) + err("failed to remove notify handler\n"); + + acpiphp_free_resource(&bridge->io_head); + acpiphp_free_resource(&bridge->mem_head); + acpiphp_free_resource(&bridge->p_mem_head); + acpiphp_free_resource(&bridge->bus_head); + + kfree(bridge); + } +} + + +/** + * acpiphp_get_num_slots - count number of slots in a system + */ +int acpiphp_get_num_slots (void) +{ + struct list_head *node; + struct acpiphp_bridge *bridge; + int num_slots; + + num_slots = 0; + + list_for_each (node, &bridge_list) { + bridge = (struct acpiphp_bridge *)node; + dbg("Bus%d %dslot(s)\n", bridge->bus, bridge->nr_slots); + num_slots += bridge->nr_slots; + } + + dbg("Total %dslots\n", num_slots); + return num_slots; +} + + +/** + * acpiphp_for_each_slot - call function for each slot + * @fn: callback function + * @data: context to be passed to callback function + * + */ +int acpiphp_for_each_slot(acpiphp_callback fn, void *data) +{ + struct list_head *node; + struct acpiphp_bridge *bridge; + struct acpiphp_slot *slot; + int retval = 0; + + list_for_each (node, &bridge_list) { + bridge = (struct acpiphp_bridge *)node; + for (slot = bridge->slots; slot; slot = slot->next) { + retval = fn(slot, data); + if (!retval) + goto err_exit; + } + } + + err_exit: + return retval; +} + + +/* search matching slot from id */ +struct acpiphp_slot *get_slot_from_id (int id) +{ + struct list_head *node; + struct acpiphp_bridge *bridge; + struct acpiphp_slot *slot; + + list_for_each (node, &bridge_list) { + bridge = (struct acpiphp_bridge *)node; + for (slot = bridge->slots; slot; slot = slot->next) + if (slot->id == id) + return slot; + } + + /* should never happen! */ + err("%s: no object for id %d\n",__FUNCTION__, id); + return 0; +} + + +/** + * acpiphp_enable_slot - power on slot + */ +int acpiphp_enable_slot (struct acpiphp_slot *slot) +{ + int retval; + + down(&slot->crit_sect); + + /* wake up all functions */ + retval = power_on_slot(slot); + if (retval) + goto err_exit; + + if (get_slot_status(slot) == ACPI_STA_ALL) + /* configure all functions */ + retval = enable_device(slot); + + err_exit: + up(&slot->crit_sect); + return retval; +} + + +/** + * acpiphp_disable_slot - power off slot + */ +int acpiphp_disable_slot (struct acpiphp_slot *slot) +{ + int retval = 0; + + down(&slot->crit_sect); + + /* unconfigure all functions */ + retval = disable_device(slot); + if (retval) + goto err_exit; + + /* power off all functions */ + retval = power_off_slot(slot); + if (retval) + goto err_exit; + + acpiphp_resource_sort_and_combine(&slot->bridge->io_head); + acpiphp_resource_sort_and_combine(&slot->bridge->mem_head); + acpiphp_resource_sort_and_combine(&slot->bridge->p_mem_head); + acpiphp_resource_sort_and_combine(&slot->bridge->bus_head); + dbg("Available resources:\n"); + acpiphp_dump_resource(slot->bridge); + + err_exit: + up(&slot->crit_sect); + return retval; +} + + +/** + * acpiphp_check_bridge - re-enumerate devices + */ +int acpiphp_check_bridge (struct acpiphp_bridge *bridge) +{ + struct acpiphp_slot *slot; + unsigned int sta; + int retval = 0; + int enabled, disabled; + + enabled = disabled = 0; + + for (slot = bridge->slots; slot; slot = slot->next) { + sta = get_slot_status(slot); + if (slot->flags & SLOT_ENABLED) { + /* if enabled but not present, disable */ + if (sta != ACPI_STA_ALL) { + retval = acpiphp_disable_slot(slot); + if (retval) { + err("Error occurred in enabling\n"); + up(&slot->crit_sect); + goto err_exit; + } + enabled++; + } + } else { + /* if disabled but present, enable */ + if (sta == ACPI_STA_ALL) { + retval = acpiphp_enable_slot(slot); + if (retval) { + err("Error occurred in enabling\n"); + up(&slot->crit_sect); + goto err_exit; + } + disabled++; + } + } + } + + dbg("%s: %d enabled, %d disabled\n", __FUNCTION__, enabled, disabled); + + err_exit: + return retval; +} + + +/* + * slot enabled: 1 + * slot disabled: 0 + */ +u8 acpiphp_get_power_status (struct acpiphp_slot *slot) +{ + unsigned int sta; + + sta = get_slot_status(slot); + + return (sta & ACPI_STA_ENABLED) ? 1 : 0; +} + + +/* + * attention LED ON: 1 + * OFF: 0 + * + * TBD + * no direct attention led status information via ACPI + * + */ +u8 acpiphp_get_attention_status (struct acpiphp_slot *slot) +{ + return 0; +} + + +/* + * latch closed: 1 + * latch open: 0 + */ +u8 acpiphp_get_latch_status (struct acpiphp_slot *slot) +{ + unsigned int sta; + + sta = get_slot_status(slot); + + return (sta & ACPI_STA_SHOW_IN_UI) ? 1 : 0; +} + + +/* + * adapter presence : 1 + * absence : 0 + */ +u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot) +{ + unsigned int sta; + + sta = get_slot_status(slot); + + return (sta == 0) ? 0 : 1; +} diff -Nru a/drivers/pci/hotplug/acpiphp_pci.c b/drivers/pci/hotplug/acpiphp_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/acpiphp_pci.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,510 @@ +/* + * ACPI PCI HotPlug PCI configuration space management + * + * Copyright (c) 1995,2001 Compaq Computer Corporation + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2002 IBM Corp. + * Copyright (c) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com) + * Copyright (c) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) + * Copyright (c) 2002 NEC Corporation + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include +#include "pci_hotplug.h" +#include "acpiphp.h" + +#define MY_NAME "acpiphp_pci" + + +/* allocate mem/pmem/io resource to a new function */ +static int init_config_space (struct acpiphp_func *func) +{ + u32 bar, len; + u32 address[] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + PCI_BASE_ADDRESS_2, + PCI_BASE_ADDRESS_3, + PCI_BASE_ADDRESS_4, + PCI_BASE_ADDRESS_5, + 0 + }; + int count; + struct acpiphp_bridge *bridge; + struct pci_resource *res; + struct pci_bus *pbus; + int bus, device, function; + unsigned int devfn; + u16 tmp; + + bridge = func->slot->bridge; + pbus = bridge->pci_bus; + bus = bridge->bus; + device = func->slot->device; + function = func->function; + devfn = PCI_DEVFN(device, function); + + for (count = 0; address[count]; count++) { /* for 6 BARs */ + pci_bus_write_config_dword(pbus, devfn, + address[count], 0xFFFFFFFF); + pci_bus_read_config_dword(pbus, devfn, address[count], &bar); + + if (!bar) /* This BAR is not implemented */ + continue; + + dbg("Device %02x.%02x BAR %d wants %x\n", device, function, count, bar); + + if (bar & PCI_BASE_ADDRESS_SPACE_IO) { + /* This is IO */ + + len = bar & 0xFFFFFFFC; + len = ~len + 1; + + dbg("len in IO %x, BAR %d\n", len, count); + + spin_lock(&bridge->res_lock); + res = acpiphp_get_io_resource(&bridge->io_head, len); + spin_unlock(&bridge->res_lock); + + if (!res) { + err("cannot allocate requested io for %02x:%02x.%d len %x\n", + bus, device, function, len); + return -1; + } + pci_bus_write_config_dword(pbus, devfn, + address[count], + (u32)res->base); + res->next = func->io_head; + func->io_head = res; + + } else { + /* This is Memory */ + if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH) { + /* pfmem */ + + len = bar & 0xFFFFFFF0; + len = ~len + 1; + + dbg("len in PFMEM %x, BAR %d\n", len, count); + + spin_lock(&bridge->res_lock); + res = acpiphp_get_resource(&bridge->p_mem_head, len); + spin_unlock(&bridge->res_lock); + + if (!res) { + err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n", + bus, device, function, len); + return -1; + } + + pci_bus_write_config_dword(pbus, devfn, + address[count], + (u32)res->base); + + if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ + dbg("inside the pfmem 64 case, count %d\n", count); + count += 1; + pci_bus_write_config_dword(pbus, devfn, + address[count], + (u32)(res->base >> 32)); + } + + res->next = func->p_mem_head; + func->p_mem_head = res; + + } else { + /* regular memory */ + + len = bar & 0xFFFFFFF0; + len = ~len + 1; + + dbg("len in MEM %x, BAR %d\n", len, count); + + spin_lock(&bridge->res_lock); + res = acpiphp_get_resource(&bridge->mem_head, len); + spin_unlock(&bridge->res_lock); + + if (!res) { + err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n", + bus, device, function, len); + return -1; + } + + pci_bus_write_config_dword(pbus, devfn, + address[count], + (u32)res->base); + + if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + dbg("inside mem 64 case, reg. mem, count %d\n", count); + count += 1; + pci_bus_write_config_dword(pbus, devfn, + address[count], + (u32)(res->base >> 32)); + } + + res->next = func->mem_head; + func->mem_head = res; + + } + } + } + + /* disable expansion rom */ + pci_bus_write_config_dword(pbus, devfn, PCI_ROM_ADDRESS, 0x00000000); + + /* set PCI parameters from _HPP */ + pci_bus_write_config_byte(pbus, devfn, PCI_CACHE_LINE_SIZE, + bridge->hpp.cache_line_size); + pci_bus_write_config_byte(pbus, devfn, PCI_LATENCY_TIMER, + bridge->hpp.latency_timer); + + pci_bus_read_config_word(pbus, devfn, PCI_COMMAND, &tmp); + if (bridge->hpp.enable_SERR) + tmp |= PCI_COMMAND_SERR; + if (bridge->hpp.enable_PERR) + tmp |= PCI_COMMAND_PARITY; + pci_bus_write_config_word(pbus, devfn, PCI_COMMAND, tmp); + + return 0; +} + +/* detect_used_resource - subtract resource under dev from bridge */ +static int detect_used_resource (struct acpiphp_bridge *bridge, struct pci_dev *dev) +{ + u32 bar, len; + u64 base; + u32 address[] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + PCI_BASE_ADDRESS_2, + PCI_BASE_ADDRESS_3, + PCI_BASE_ADDRESS_4, + PCI_BASE_ADDRESS_5, + 0 + }; + int count; + struct pci_resource *res; + + dbg("Device %s\n", dev->slot_name); + + for (count = 0; address[count]; count++) { /* for 6 BARs */ + pci_read_config_dword(dev, address[count], &bar); + + if (!bar) /* This BAR is not implemented */ + continue; + + pci_write_config_dword(dev, address[count], 0xFFFFFFFF); + pci_read_config_dword(dev, address[count], &len); + + if (len & PCI_BASE_ADDRESS_SPACE_IO) { + /* This is IO */ + base = bar & 0xFFFFFFFC; + len &= 0xFFFFFFFC; + len = ~len + 1; + + dbg("BAR[%d] %08x - %08x (IO)\n", count, (u32)base, (u32)base + len - 1); + + spin_lock(&bridge->res_lock); + res = acpiphp_get_resource_with_base(&bridge->io_head, base, len); + spin_unlock(&bridge->res_lock); + if (res) + kfree(res); + } else { + /* This is Memory */ + base = bar & 0xFFFFFFF0; + if (len & PCI_BASE_ADDRESS_MEM_PREFETCH) { + /* pfmem */ + + len &= 0xFFFFFFF0; + len = ~len + 1; + + if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ + dbg("prefetch mem 64\n"); + count += 1; + } + dbg("BAR[%d] %08x - %08x (PMEM)\n", count, (u32)base, (u32)base + len - 1); + spin_lock(&bridge->res_lock); + res = acpiphp_get_resource_with_base(&bridge->p_mem_head, base, len); + spin_unlock(&bridge->res_lock); + if (res) + kfree(res); + } else { + /* regular memory */ + + len &= 0xFFFFFFF0; + len = ~len + 1; + + if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + dbg("mem 64\n"); + count += 1; + } + dbg("BAR[%d] %08x - %08x (MEM)\n", count, (u32)base, (u32)base + len - 1); + spin_lock(&bridge->res_lock); + res = acpiphp_get_resource_with_base(&bridge->mem_head, base, len); + spin_unlock(&bridge->res_lock); + if (res) + kfree(res); + } + } + + pci_write_config_dword(dev, address[count], bar); + } + + return 0; +} + + +/* detect_pci_resource_bus - subtract resource under pci_bus */ +static void detect_used_resource_bus(struct acpiphp_bridge *bridge, struct pci_bus *bus) +{ + struct list_head *l; + struct pci_dev *dev; + + list_for_each (l, &bus->devices) { + dev = pci_dev_b(l); + detect_used_resource(bridge, dev); + /* XXX recursive call */ + if (dev->subordinate) + detect_used_resource_bus(bridge, dev->subordinate); + } +} + + +/** + * acpiphp_detect_pci_resource - detect resources under bridge + * @bridge: detect all resources already used under this bridge + * + * collect all resources already allocated for all devices under a bridge. + */ +int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge) +{ + detect_used_resource_bus(bridge, bridge->pci_bus); + + return 0; +} + + +/** + * acpiphp_init_slot_resource - gather resource usage information of a slot + * @slot: ACPI slot object to be checked, should have valid pci_dev member + * + * TBD: PCI-to-PCI bridge case + * use pci_dev->resource[] + */ +int acpiphp_init_func_resource (struct acpiphp_func *func) +{ + u64 base; + u32 bar, len; + u32 address[] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + PCI_BASE_ADDRESS_2, + PCI_BASE_ADDRESS_3, + PCI_BASE_ADDRESS_4, + PCI_BASE_ADDRESS_5, + 0 + }; + int count; + struct pci_resource *res; + struct pci_dev *dev; + + dev = func->pci_dev; + dbg("Hot-pluggable device %s\n", dev->slot_name); + + for (count = 0; address[count]; count++) { /* for 6 BARs */ + pci_read_config_dword(dev, address[count], &bar); + + if (!bar) /* This BAR is not implemented */ + continue; + + pci_write_config_dword(dev, address[count], 0xFFFFFFFF); + pci_read_config_dword(dev, address[count], &len); + + if (len & PCI_BASE_ADDRESS_SPACE_IO) { + /* This is IO */ + base = bar & 0xFFFFFFFC; + len &= 0xFFFFFFFC; + len = ~len + 1; + + dbg("BAR[%d] %08x - %08x (IO)\n", count, (u32)base, (u32)base + len - 1); + + res = acpiphp_make_resource(base, len); + if (!res) + goto no_memory; + + res->next = func->io_head; + func->io_head = res; + + } else { + /* This is Memory */ + base = bar & 0xFFFFFFF0; + if (len & PCI_BASE_ADDRESS_MEM_PREFETCH) { + /* pfmem */ + + len &= 0xFFFFFFF0; + len = ~len + 1; + + if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ + dbg("prefetch mem 64\n"); + count += 1; + } + dbg("BAR[%d] %08x - %08x (PMEM)\n", count, (u32)base, (u32)base + len - 1); + res = acpiphp_make_resource(base, len); + if (!res) + goto no_memory; + + res->next = func->p_mem_head; + func->p_mem_head = res; + + } else { + /* regular memory */ + + len &= 0xFFFFFFF0; + len = ~len + 1; + + if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + dbg("mem 64\n"); + count += 1; + } + dbg("BAR[%d] %08x - %08x (MEM)\n", count, (u32)base, (u32)base + len - 1); + res = acpiphp_make_resource(base, len); + if (!res) + goto no_memory; + + res->next = func->mem_head; + func->mem_head = res; + + } + } + + pci_write_config_dword(dev, address[count], bar); + } +#if 1 + acpiphp_dump_func_resource(func); +#endif + + return 0; + + no_memory: + err("out of memory\n"); + acpiphp_free_resource(&func->io_head); + acpiphp_free_resource(&func->mem_head); + acpiphp_free_resource(&func->p_mem_head); + + return -1; +} + + +/** + * acpiphp_configure_slot - allocate PCI resources + * @slot: slot to be configured + * + * initializes a PCI functions on a device inserted + * into the slot + * + */ +int acpiphp_configure_slot (struct acpiphp_slot *slot) +{ + struct acpiphp_func *func; + struct list_head *l; + u8 hdr; + u32 dvid; + int retval = 0; + int is_multi = 0; + + pci_bus_read_config_byte(slot->bridge->pci_bus, + PCI_DEVFN(slot->device, 0), + PCI_HEADER_TYPE, &hdr); + + if (hdr & 0x80) + is_multi = 1; + + list_for_each (l, &slot->funcs) { + func = list_entry(l, struct acpiphp_func, sibling); + if (is_multi || func->function == 0) { + pci_bus_read_config_dword(slot->bridge->pci_bus, + PCI_DEVFN(slot->device, + func->function), + PCI_VENDOR_ID, &dvid); + if (dvid != 0xffffffff) { + retval = init_config_space(func); + if (retval) + break; + } + } + } + + return retval; +} + +/** + * acpiphp_configure_function - configure PCI function + * @func: function to be configured + * + * initializes a PCI functions on a device inserted + * into the slot + * + */ +int acpiphp_configure_function (struct acpiphp_func *func) +{ + /* all handled by the pci core now */ + return 0; +} + +/** + * acpiphp_unconfigure_function - unconfigure PCI function + * @func: function to be unconfigured + * + */ +int acpiphp_unconfigure_function (struct acpiphp_func *func) +{ + struct acpiphp_bridge *bridge; + int retval = 0; + + /* if pci_dev is NULL, ignore it */ + if (!func->pci_dev) + goto err_exit; + + pci_remove_bus_device(func->pci_dev); + + /* free all resources */ + bridge = func->slot->bridge; + + spin_lock(&bridge->res_lock); + acpiphp_move_resource(&func->io_head, &bridge->io_head); + acpiphp_move_resource(&func->mem_head, &bridge->mem_head); + acpiphp_move_resource(&func->p_mem_head, &bridge->p_mem_head); + acpiphp_move_resource(&func->bus_head, &bridge->bus_head); + spin_unlock(&bridge->res_lock); + + err_exit: + return retval; +} diff -Nru a/drivers/pci/hotplug/acpiphp_res.c b/drivers/pci/hotplug/acpiphp_res.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/acpiphp_res.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,699 @@ +/* + * ACPI PCI HotPlug Utility functions + * + * Copyright (c) 1995,2001 Compaq Computer Corporation + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001 IBM Corp. + * Copyright (c) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com) + * Copyright (c) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com) + * Copyright (c) 2002 NEC Corporation + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "pci_hotplug.h" +#include "acpiphp.h" + +#define MY_NAME "acpiphp_res" + + +/* + * sort_by_size - sort nodes by their length, smallest first + */ +static int sort_by_size(struct pci_resource **head) +{ + struct pci_resource *current_res; + struct pci_resource *next_res; + int out_of_order = 1; + + if (!(*head)) + return 1; + + if (!((*head)->next)) + return 0; + + while (out_of_order) { + out_of_order = 0; + + /* Special case for swapping list head */ + if (((*head)->next) && + ((*head)->length > (*head)->next->length)) { + out_of_order++; + current_res = *head; + *head = (*head)->next; + current_res->next = (*head)->next; + (*head)->next = current_res; + } + + current_res = *head; + + while (current_res->next && current_res->next->next) { + if (current_res->next->length > current_res->next->next->length) { + out_of_order++; + next_res = current_res->next; + current_res->next = current_res->next->next; + current_res = current_res->next; + next_res->next = current_res->next; + current_res->next = next_res; + } else + current_res = current_res->next; + } + } /* End of out_of_order loop */ + + return 0; +} + + +/* + * sort_by_max_size - sort nodes by their length, largest first + */ +static int sort_by_max_size(struct pci_resource **head) +{ + struct pci_resource *current_res; + struct pci_resource *next_res; + int out_of_order = 1; + + if (!(*head)) + return 1; + + if (!((*head)->next)) + return 0; + + while (out_of_order) { + out_of_order = 0; + + /* Special case for swapping list head */ + if (((*head)->next) && + ((*head)->length < (*head)->next->length)) { + out_of_order++; + current_res = *head; + *head = (*head)->next; + current_res->next = (*head)->next; + (*head)->next = current_res; + } + + current_res = *head; + + while (current_res->next && current_res->next->next) { + if (current_res->next->length < current_res->next->next->length) { + out_of_order++; + next_res = current_res->next; + current_res->next = current_res->next->next; + current_res = current_res->next; + next_res->next = current_res->next; + current_res->next = next_res; + } else + current_res = current_res->next; + } + } /* End of out_of_order loop */ + + return 0; +} + +/** + * get_io_resource - get resource for I/O ports + * + * this function sorts the resource list by size and then + * returns the first node of "size" length that is not in the + * ISA aliasing window. If it finds a node larger than "size" + * it will split it up. + * + * size must be a power of two. + * + * difference from get_resource is handling of ISA aliasing space. + * + */ +struct pci_resource *acpiphp_get_io_resource (struct pci_resource **head, u32 size) +{ + struct pci_resource *prevnode; + struct pci_resource *node; + struct pci_resource *split_node; + u64 temp_qword; + + if (!(*head)) + return NULL; + + if (acpiphp_resource_sort_and_combine(head)) + return NULL; + + if (sort_by_size(head)) + return NULL; + + for (node = *head; node; node = node->next) { + if (node->length < size) + continue; + + if (node->base & (size - 1)) { + /* this one isn't base aligned properly + so we'll make a new entry and split it up */ + temp_qword = (node->base | (size-1)) + 1; + + /* Short circuit if adjusted size is too small */ + if ((node->length - (temp_qword - node->base)) < size) + continue; + + split_node = acpiphp_make_resource(node->base, temp_qword - node->base); + + if (!split_node) + return NULL; + + node->base = temp_qword; + node->length -= split_node->length; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } /* End of non-aligned base */ + + /* Don't need to check if too small since we already did */ + if (node->length > size) { + /* this one is longer than we need + so we'll make a new entry and split it up */ + split_node = acpiphp_make_resource(node->base + size, node->length - size); + + if (!split_node) + return NULL; + + node->length = size; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } /* End of too big on top end */ + + /* For IO make sure it's not in the ISA aliasing space */ + if (node->base & 0x300L) + 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 - get the largest resource + * + * Gets the largest node that is at least "size" big from the + * list pointed to by head. It aligns the node on top and bottom + * to "size" alignment before returning it. + */ +struct pci_resource *acpiphp_get_max_resource (struct pci_resource **head, u32 size) +{ + struct pci_resource *max; + struct pci_resource *temp; + struct pci_resource *split_node; + u64 temp_qword; + + if (!(*head)) + return NULL; + + if (acpiphp_resource_sort_and_combine(head)) + return NULL; + + if (sort_by_max_size(head)) + return NULL; + + for (max = *head;max; max = max->next) { + + /* If not big enough we could probably just bail, + instead we'll continue to the next. */ + if (max->length < size) + continue; + + if (max->base & (size - 1)) { + /* this one isn't base aligned properly + so we'll make a new entry and split it up */ + temp_qword = (max->base | (size-1)) + 1; + + /* Short circuit if adjusted size is too small */ + if ((max->length - (temp_qword - max->base)) < size) + continue; + + split_node = acpiphp_make_resource(max->base, temp_qword - max->base); + + if (!split_node) + return NULL; + + max->base = temp_qword; + max->length -= split_node->length; + + /* Put it next in the list */ + split_node->next = max->next; + max->next = split_node; + } + + if ((max->base + max->length) & (size - 1)) { + /* this one isn't end aligned properly at the top + so we'll make a new entry and split it up */ + temp_qword = ((max->base + max->length) & ~(size - 1)); + + split_node = acpiphp_make_resource(temp_qword, + max->length + max->base - temp_qword); + + if (!split_node) + return NULL; + + max->length -= split_node->length; + + /* Put it in the list */ + split_node->next = max->next; + max->next = split_node; + } + + /* Make sure it didn't shrink too much when we aligned it */ + if (max->length < size) + continue; + + /* Now take it out of the list */ + temp = (struct pci_resource*) *head; + if (temp == max) { + *head = max->next; + } else { + while (temp && temp->next != max) { + temp = temp->next; + } + + temp->next = max->next; + } + + max->next = NULL; + return max; + } + + /* If we get here, we couldn't find one */ + return NULL; +} + + +/** + * get_resource - get resource (mem, pfmem) + * + * this function sorts the resource list by size and then + * returns the first node of "size" length. If it finds a node + * larger than "size" it will split it up. + * + * size must be a power of two. + * + */ +struct pci_resource *acpiphp_get_resource (struct pci_resource **head, u32 size) +{ + struct pci_resource *prevnode; + struct pci_resource *node; + struct pci_resource *split_node; + u64 temp_qword; + + if (!(*head)) + return NULL; + + if (acpiphp_resource_sort_and_combine(head)) + return NULL; + + if (sort_by_size(head)) + return NULL; + + for (node = *head; node; node = node->next) { + dbg("%s: req_size =%x node=%p, base=%x, length=%x\n", + __FUNCTION__, size, node, (u32)node->base, node->length); + if (node->length < size) + continue; + + if (node->base & (size - 1)) { + dbg("%s: not aligned\n", __FUNCTION__); + /* this one isn't base aligned properly + so we'll make a new entry and split it up */ + temp_qword = (node->base | (size-1)) + 1; + + /* Short circuit if adjusted size is too small */ + if ((node->length - (temp_qword - node->base)) < size) + continue; + + split_node = acpiphp_make_resource(node->base, temp_qword - node->base); + + if (!split_node) + return NULL; + + node->base = temp_qword; + node->length -= split_node->length; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } /* End of non-aligned base */ + + /* Don't need to check if too small since we already did */ + if (node->length > size) { + dbg("%s: too big\n", __FUNCTION__); + /* this one is longer than we need + so we'll make a new entry and split it up */ + split_node = acpiphp_make_resource(node->base + size, node->length - size); + + if (!split_node) + return NULL; + + node->length = size; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } /* End of too big on top end */ + + dbg("%s: got one!!!\n", __FUNCTION__); + /* If we got here, then it is the right size + Now take it out of the list */ + if (*head == node) { + *head = node->next; + } else { + prevnode = *head; + while (prevnode->next != node) + prevnode = prevnode->next; + + prevnode->next = node->next; + } + node->next = NULL; + /* Stop looping */ + break; + } + return node; +} + +/** + * get_resource_with_base - get resource with specific base address + * + * this function + * returns the first node of "size" length located at specified base address. + * If it finds a node larger than "size" it will split it up. + * + * size must be a power of two. + * + */ +struct pci_resource *acpiphp_get_resource_with_base (struct pci_resource **head, u64 base, u32 size) +{ + struct pci_resource *prevnode; + struct pci_resource *node; + struct pci_resource *split_node; + u64 temp_qword; + + if (!(*head)) + return NULL; + + if (acpiphp_resource_sort_and_combine(head)) + return NULL; + + for (node = *head; node; node = node->next) { + dbg(": 1st req_base=%x req_size =%x node=%p, base=%x, length=%x\n", + (u32)base, size, node, (u32)node->base, node->length); + if (node->base > base) + continue; + + if ((node->base + node->length) < (base + size)) + continue; + + if (node->base < base) { + dbg(": split 1\n"); + /* this one isn't base aligned properly + so we'll make a new entry and split it up */ + temp_qword = base; + + /* Short circuit if adjusted size is too small */ + if ((node->length - (temp_qword - node->base)) < size) + continue; + + split_node = acpiphp_make_resource(node->base, temp_qword - node->base); + + if (!split_node) + return NULL; + + node->base = temp_qword; + node->length -= split_node->length; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } + + dbg(": 2nd req_base=%x req_size =%x node=%p, base=%x, length=%x\n", + (u32)base, size, node, (u32)node->base, node->length); + + /* Don't need to check if too small since we already did */ + if (node->length > size) { + dbg(": split 2\n"); + /* this one is longer than we need + so we'll make a new entry and split it up */ + split_node = acpiphp_make_resource(node->base + size, node->length - size); + + if (!split_node) + return NULL; + + node->length = size; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } /* End of too big on top end */ + + dbg(": got one!!!\n"); + /* If we got here, then it is the right size + Now take it out of the list */ + if (*head == node) { + *head = node->next; + } else { + prevnode = *head; + while (prevnode->next != node) + prevnode = prevnode->next; + + prevnode->next = node->next; + } + node->next = NULL; + /* Stop looping */ + break; + } + return node; +} + + +/** + * acpiphp_resource_sort_and_combine + * + * Sorts all of the nodes in the list in ascending order by + * their base addresses. Also does garbage collection by + * combining adjacent nodes. + * + * returns 0 if success + */ +int acpiphp_resource_sort_and_combine (struct pci_resource **head) +{ + struct pci_resource *node1; + struct pci_resource *node2; + int out_of_order = 1; + + if (!(*head)) + return 1; + + dbg("*head->next = %p\n",(*head)->next); + + if (!(*head)->next) + return 0; /* only one item on the list, already sorted! */ + + dbg("*head->base = 0x%x\n",(u32)(*head)->base); + dbg("*head->next->base = 0x%x\n", (u32)(*head)->next->base); + while (out_of_order) { + out_of_order = 0; + + /* Special case for swapping list head */ + if (((*head)->next) && + ((*head)->base > (*head)->next->base)) { + node1 = *head; + (*head) = (*head)->next; + node1->next = (*head)->next; + (*head)->next = node1; + out_of_order++; + } + + node1 = (*head); + + while (node1->next && node1->next->next) { + if (node1->next->base > node1->next->next->base) { + out_of_order++; + node2 = node1->next; + node1->next = node1->next->next; + node1 = node1->next; + node2->next = node1->next; + node1->next = node2; + } else + node1 = node1->next; + } + } /* End of out_of_order loop */ + + node1 = *head; + + while (node1 && node1->next) { + if ((node1->base + node1->length) == node1->next->base) { + /* Combine */ + dbg("8..\n"); + node1->length += node1->next->length; + node2 = node1->next; + node1->next = node1->next->next; + kfree(node2); + } else + node1 = node1->next; + } + + return 0; +} + + +/** + * acpiphp_make_resource - make resource structure + * @base: base address of a resource + * @length: length of a resource + */ +struct pci_resource *acpiphp_make_resource (u64 base, u32 length) +{ + struct pci_resource *res; + + res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (res) { + memset(res, 0, sizeof(struct pci_resource)); + res->base = base; + res->length = length; + } + + return res; +} + + +/** + * acpiphp_move_resource - move linked resources from one to another + * @from: head of linked resource list + * @to: head of linked resource list + */ +void acpiphp_move_resource (struct pci_resource **from, struct pci_resource **to) +{ + struct pci_resource *tmp; + + while (*from) { + tmp = (*from)->next; + (*from)->next = *to; + *to = *from; + *from = tmp; + } + + /* *from = NULL is guaranteed */ +} + + +/** + * acpiphp_free_resource - free all linked resources + * @res: head of linked resource list + */ +void acpiphp_free_resource (struct pci_resource **res) +{ + struct pci_resource *tmp; + + while (*res) { + tmp = (*res)->next; + kfree(*res); + *res = tmp; + } + + /* *res = NULL is guaranteed */ +} + + +/* debug support functions; will go away sometime :) */ +static void dump_resource(struct pci_resource *head) +{ + struct pci_resource *p; + int cnt; + + p = head; + cnt = 0; + + while (p) { + dbg("[%02d] %08x - %08x\n", + cnt++, (u32)p->base, (u32)p->base + p->length - 1); + p = p->next; + } +} + +void acpiphp_dump_resource(struct acpiphp_bridge *bridge) +{ + dbg("I/O resource:\n"); + dump_resource(bridge->io_head); + dbg("MEM resource:\n"); + dump_resource(bridge->mem_head); + dbg("PMEM resource:\n"); + dump_resource(bridge->p_mem_head); + dbg("BUS resource:\n"); + dump_resource(bridge->bus_head); +} + +void acpiphp_dump_func_resource(struct acpiphp_func *func) +{ + dbg("I/O resource:\n"); + dump_resource(func->io_head); + dbg("MEM resource:\n"); + dump_resource(func->mem_head); + dbg("PMEM resource:\n"); + dump_resource(func->p_mem_head); + dbg("BUS resource:\n"); + dump_resource(func->bus_head); +} diff -Nru a/drivers/pci/hotplug/cpci_hotplug.h b/drivers/pci/hotplug/cpci_hotplug.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/cpci_hotplug.h Wed Jun 4 18:11:56 2003 @@ -0,0 +1,100 @@ +/* + * CompactPCI Hot Plug Core Functions + * + * Copyright (c) 2002 SOMA Networks, Inc. + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001 IBM Corp. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + */ + +#ifndef _CPCI_HOTPLUG_H +#define _CPCI_HOTPLUG_H + +#include +#include + +/* PICMG 2.12 R2.0 HS CSR bits: */ +#define HS_CSR_INS 0x0080 +#define HS_CSR_EXT 0x0040 +#define HS_CSR_PI 0x0030 +#define HS_CSR_LOO 0x0008 +#define HS_CSR_PIE 0x0004 +#define HS_CSR_EIM 0x0002 +#define HS_CSR_DHA 0x0001 + +#define SLOT_MAGIC 0x67267322 +struct slot { + u32 magic; + u8 number; + unsigned int devfn; + struct pci_bus *bus; + struct pci_dev *dev; + unsigned int extracting; + struct hotplug_slot *hotplug_slot; + struct list_head slot_list; +}; + +struct cpci_hp_controller_ops { + int (*query_enum) (void); + int (*enable_irq) (void); + int (*disable_irq) (void); + int (*check_irq) (void *dev_id); + int (*hardware_test) (struct slot* slot, u32 value); + u8 (*get_power) (struct slot* slot); + int (*set_power) (struct slot* slot, int value); +}; + +struct cpci_hp_controller { + unsigned int irq; + unsigned long irq_flags; + char *devname; + void *dev_id; + char *name; + struct cpci_hp_controller_ops *ops; +}; + +extern int cpci_hp_register_controller(struct cpci_hp_controller *controller); +extern int cpci_hp_unregister_controller(struct cpci_hp_controller *controller); +extern int cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last); +extern int cpci_hp_unregister_bus(struct pci_bus *bus); +extern struct slot *cpci_find_slot(struct pci_bus *bus, unsigned int devfn); +extern int cpci_hp_start(void); +extern int cpci_hp_stop(void); + +/* + * Internal function prototypes, these functions should not be used by + * board/chassis drivers. + */ +extern u8 cpci_get_attention_status(struct slot *slot); +extern u8 cpci_get_latch_status(struct slot *slot); +extern u8 cpci_get_adapter_status(struct slot *slot); +extern u16 cpci_get_hs_csr(struct slot * slot); +extern u16 cpci_set_hs_csr(struct slot * slot, u16 hs_csr); +extern int cpci_set_attention_status(struct slot *slot, int status); +extern int cpci_check_and_clear_ins(struct slot * slot); +extern int cpci_check_ext(struct slot * slot); +extern int cpci_clear_ext(struct slot * slot); +extern int cpci_led_on(struct slot * slot); +extern int cpci_led_off(struct slot * slot); +extern int cpci_configure_slot(struct slot *slot); +extern int cpci_unconfigure_slot(struct slot *slot); + +#endif /* _CPCI_HOTPLUG_H */ diff -Nru a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/cpci_hotplug_core.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,919 @@ +/* + * CompactPCI Hot Plug Driver + * + * Copyright (c) 2002 SOMA Networks, Inc. + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001 IBM Corp. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pci_hotplug.h" +#include "cpci_hotplug.h" + +#define DRIVER_VERSION "0.2" +#define DRIVER_AUTHOR "Scott Murray " +#define DRIVER_DESC "CompactPCI Hot Plug Core" + +#if !defined(CONFIG_HOTPLUG_CPCI_MODULE) +#define MY_NAME "cpci_hotplug" +#else +#define MY_NAME THIS_MODULE->name +#endif + +#define dbg(format, arg...) \ + do { \ + if(cpci_debug) \ + printk (KERN_DEBUG "%s: " format "\n", \ + MY_NAME , ## arg); \ + } while(0) +#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) + +/* local variables */ +static spinlock_t list_lock; +static LIST_HEAD(slot_list); +static int slots; +int cpci_debug; +static struct cpci_hp_controller *controller; +static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ +static struct semaphore thread_exit; /* guard ensure thread has exited before calling it quits */ +static int thread_finished = 1; + +static int enable_slot(struct hotplug_slot *slot); +static int disable_slot(struct hotplug_slot *slot); +static int set_attention_status(struct hotplug_slot *slot, u8 value); +static int get_power_status(struct hotplug_slot *slot, u8 * value); +static int get_attention_status(struct hotplug_slot *slot, u8 * value); +static 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 cpci_hotplug_slot_ops = { + .owner = THIS_MODULE, + .enable_slot = enable_slot, + .disable_slot = disable_slot, + .set_attention_status = set_attention_status, + .hardware_test = NULL, + .get_power_status = get_power_status, + .get_attention_status = get_attention_status, + .get_latch_status = get_latch_status, + .get_adapter_status = get_adapter_status, +}; + +/* 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", function); + return NULL; + } + + slot = (struct slot *) hotplug_slot->private; + if(slot_paranoia_check(slot, function)) + return NULL; + return slot; +} + +static int +update_latch_status(struct hotplug_slot *hotplug_slot, u8 value) +{ + struct hotplug_slot_info info; + + if(!(hotplug_slot && hotplug_slot->info)) + return -EINVAL; + memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info)); + info.latch_status = value; + return pci_hp_change_slot_info(hotplug_slot, &info); +} + +static int +update_adapter_status(struct hotplug_slot *hotplug_slot, u8 value) +{ + struct hotplug_slot_info info; + + if(!(hotplug_slot && hotplug_slot->info)) + return -EINVAL; + memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info)); + info.adapter_status = value; + return pci_hp_change_slot_info(hotplug_slot, &info); +} + +static int +enable_slot(struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + int retval = 0; + + if(slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name); + + if(controller->ops->set_power) { + retval = controller->ops->set_power(slot, 1); + } + + return retval; +} + +static int +disable_slot(struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + int retval = 0; + + if(slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name); + + /* Unconfigure device */ + dbg("%s - unconfiguring slot %s", + __FUNCTION__, slot->hotplug_slot->name); + if((retval = cpci_unconfigure_slot(slot))) { + err("%s - could not unconfigure slot %s", + __FUNCTION__, slot->hotplug_slot->name); + return retval; + } + dbg("%s - finished unconfiguring slot %s", + __FUNCTION__, slot->hotplug_slot->name); + + /* Clear EXT (by setting it) */ + if(cpci_clear_ext(slot)) { + err("%s - could not clear EXT for slot %s", + __FUNCTION__, slot->hotplug_slot->name); + retval = -ENODEV; + } + cpci_led_on(slot); + + if(controller->ops->set_power) { + retval = controller->ops->set_power(slot, 0); + } + + if(update_adapter_status(slot->hotplug_slot, 0)) { + warn("failure to update adapter file"); + } + + slot->extracting = 0; + + return retval; +} + +static u8 +cpci_get_power_status(struct slot *slot) +{ + u8 power = 1; + + if(controller->ops->get_power) { + power = controller->ops->get_power(slot); + } + return power; +} + +static int +get_power_status(struct hotplug_slot *hotplug_slot, u8 * value) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + + if(slot == NULL) + return -ENODEV; + *value = cpci_get_power_status(slot); + return 0; +} + +static int +get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + + if(slot == NULL) + return -ENODEV; + *value = cpci_get_attention_status(slot); + return 0; +} + +static int +set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + + if(slot == NULL) + return -ENODEV; + switch (status) { + case 0: + cpci_set_attention_status(slot, 0); + break; + + case 1: + default: + cpci_set_attention_status(slot, 1); + break; + } + + return 0; +} + +static int +get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value) +{ + if(hotplug_slot == NULL || hotplug_slot->info == NULL) + return -ENODEV; + *value = hotplug_slot->info->latch_status; + return 0; +} + +static int +get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value) +{ + if(hotplug_slot == NULL || hotplug_slot->info == NULL) + return -ENODEV; + *value = hotplug_slot->info->adapter_status; + return 0; +} + +#define SLOT_NAME_SIZE 6 +static void +make_slot_name(struct slot *slot) +{ + snprintf(slot->hotplug_slot->name, + SLOT_NAME_SIZE, "%02x:%02x", slot->bus->number, slot->number); +} + +int +cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) +{ + struct slot *slot; + struct hotplug_slot *hotplug_slot; + struct hotplug_slot_info *info; + char *name; + int status = 0; + int i; + + if(!(controller && bus)) { + return -ENODEV; + } + if(last < first) { + return -EINVAL; + } + + /* + * Create a structure for each slot, and register that slot + * with the pci_hotplug subsystem. + */ + for (i = first; i <= last; ++i) { + slot = kmalloc(sizeof (struct slot), GFP_KERNEL); + if(!slot) + return -ENOMEM; + memset(slot, 0, sizeof (struct slot)); + + hotplug_slot = + kmalloc(sizeof (struct hotplug_slot), GFP_KERNEL); + if(!hotplug_slot) { + kfree(slot); + return -ENOMEM; + } + memset(hotplug_slot, 0, sizeof (struct hotplug_slot)); + slot->hotplug_slot = hotplug_slot; + + info = kmalloc(sizeof (struct hotplug_slot_info), GFP_KERNEL); + if(!info) { + kfree(hotplug_slot); + kfree(slot); + return -ENOMEM; + } + memset(info, 0, sizeof (struct hotplug_slot_info)); + hotplug_slot->info = info; + + name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL); + if(!name) { + kfree(info); + kfree(hotplug_slot); + kfree(slot); + return -ENOMEM; + } + hotplug_slot->name = name; + + slot->magic = SLOT_MAGIC; + slot->bus = bus; + slot->number = i; + slot->devfn = PCI_DEVFN(i, 0); + + hotplug_slot->private = slot; + make_slot_name(slot); + hotplug_slot->ops = &cpci_hotplug_slot_ops; + + /* + * Initialize the slot info structure with some known + * good values. + */ + dbg("initializing slot %s", slot->hotplug_slot->name); + info->power_status = cpci_get_power_status(slot); + info->attention_status = cpci_get_attention_status(slot); + + dbg("registering slot %s", slot->hotplug_slot->name); + status = pci_hp_register(slot->hotplug_slot); + if(status) { + err("pci_hp_register failed with error %d", status); + kfree(info); + kfree(name); + kfree(hotplug_slot); + kfree(slot); + return status; + } + + /* Add slot to our internal list */ + spin_lock(&list_lock); + list_add(&slot->slot_list, &slot_list); + slots++; + spin_unlock(&list_lock); + } + return status; +} + +int +cpci_hp_unregister_bus(struct pci_bus *bus) +{ + struct slot *slot; + struct list_head *tmp; + int status; + + if(!bus) { + return -ENODEV; + } + + spin_lock(&list_lock); + if(!slots) { + spin_unlock(&list_lock); + return -1; + } + list_for_each(tmp, &slot_list) { + slot = list_entry(tmp, struct slot, slot_list); + if(slot->bus == bus) { + dbg("deregistering slot %s", slot->hotplug_slot->name); + status = pci_hp_deregister(slot->hotplug_slot); + if(status) { + err("pci_hp_deregister failed with error %d", + status); + return status; + } + + list_del(&slot->slot_list); + kfree(slot->hotplug_slot->info); + kfree(slot->hotplug_slot->name); + kfree(slot->hotplug_slot); + kfree(slot); + + slots--; + } + } + spin_unlock(&list_lock); + return 0; +} + +struct slot * +cpci_find_slot(struct pci_bus *bus, unsigned int devfn) +{ + struct slot *slot; + struct slot *found; + struct list_head *tmp; + + if(!bus) { + return NULL; + } + + spin_lock(&list_lock); + if(!slots) { + spin_unlock(&list_lock); + return NULL; + } + found = NULL; + list_for_each(tmp, &slot_list) { + slot = list_entry(tmp, struct slot, slot_list); + if(slot->bus == bus && slot->devfn == devfn) { + found = slot; + break; + } + } + spin_unlock(&list_lock); + return found; +} + +/* This is the interrupt mode interrupt handler */ +irqreturn_t +cpci_hp_intr(int irq, void *data, struct pt_regs *regs) +{ + dbg("entered cpci_hp_intr"); + + /* Check to see if it was our interrupt */ + if((controller->irq_flags & SA_SHIRQ) && + !controller->ops->check_irq(controller->dev_id)) { + dbg("exited cpci_hp_intr, not our interrupt"); + return IRQ_NONE; + } + + /* Disable ENUM interrupt */ + controller->ops->disable_irq(); + + /* Trigger processing by the event thread */ + dbg("Signal event_semaphore"); + up(&event_semaphore); + dbg("exited cpci_hp_intr"); + return IRQ_HANDLED; +} + +/* + * According to PICMG 2.12 R2.0, section 6.3.2, upon + * initialization, the system driver shall clear the + * INS bits of the cold-inserted devices. + */ +static int +init_slots(void) +{ + struct slot *slot; + struct list_head *tmp; + struct pci_dev* dev; + + dbg("%s - enter", __FUNCTION__); + spin_lock(&list_lock); + if(!slots) { + spin_unlock(&list_lock); + return -1; + } + list_for_each(tmp, &slot_list) { + slot = list_entry(tmp, struct slot, slot_list); + dbg("%s - looking at slot %s", + __FUNCTION__, slot->hotplug_slot->name); + if(cpci_check_and_clear_ins(slot)) { + dbg("%s - cleared INS for slot %s", + __FUNCTION__, slot->hotplug_slot->name); + dev = pci_find_slot(slot->bus->number, PCI_DEVFN(slot->number, 0)); + if(dev) { + if(update_adapter_status(slot->hotplug_slot, 1)) { + warn("failure to update adapter file"); + } + if(update_latch_status(slot->hotplug_slot, 1)) { + warn("failure to update latch file"); + } + slot->dev = dev; + } else { + err("%s - no driver attached to device in slot %s", + __FUNCTION__, slot->hotplug_slot->name); + } + } + } + spin_unlock(&list_lock); + dbg("%s - exit", __FUNCTION__); + return 0; +} + +static int +check_slots(void) +{ + struct slot *slot; + struct list_head *tmp; + int extracted; + int inserted; + + spin_lock(&list_lock); + if(!slots) { + spin_unlock(&list_lock); + err("no slots registered, shutting down"); + return -1; + } + extracted = inserted = 0; + list_for_each(tmp, &slot_list) { + slot = list_entry(tmp, struct slot, slot_list); + dbg("%s - looking at slot %s", + __FUNCTION__, slot->hotplug_slot->name); + if(cpci_check_and_clear_ins(slot)) { + u16 hs_csr; + + /* Some broken hardware (e.g. PLX 9054AB) asserts ENUM# twice... */ + if(slot->dev) { + warn("slot %s already inserted", slot->hotplug_slot->name); + inserted++; + continue; + } + + /* Process insertion */ + dbg("%s - slot %s inserted", + __FUNCTION__, slot->hotplug_slot->name); + + /* GSM, debug */ + hs_csr = cpci_get_hs_csr(slot); + dbg("%s - slot %s HS_CSR (1) = %04x", + __FUNCTION__, slot->hotplug_slot->name, hs_csr); + + /* Configure device */ + dbg("%s - configuring slot %s", + __FUNCTION__, slot->hotplug_slot->name); + if(cpci_configure_slot(slot)) { + err("%s - could not configure slot %s", + __FUNCTION__, slot->hotplug_slot->name); + continue; + } + dbg("%s - finished configuring slot %s", + __FUNCTION__, slot->hotplug_slot->name); + + /* GSM, debug */ + hs_csr = cpci_get_hs_csr(slot); + dbg("%s - slot %s HS_CSR (2) = %04x", + __FUNCTION__, slot->hotplug_slot->name, hs_csr); + + if(update_latch_status(slot->hotplug_slot, 1)) { + warn("failure to update latch file"); + } + + if(update_adapter_status(slot->hotplug_slot, 1)) { + warn("failure to update adapter file"); + } + + cpci_led_off(slot); + + /* GSM, debug */ + hs_csr = cpci_get_hs_csr(slot); + dbg("%s - slot %s HS_CSR (3) = %04x", + __FUNCTION__, slot->hotplug_slot->name, hs_csr); + + inserted++; + } else if(cpci_check_ext(slot)) { + u16 hs_csr; + + /* Process extraction request */ + dbg("%s - slot %s extracted", + __FUNCTION__, slot->hotplug_slot->name); + + /* GSM, debug */ + hs_csr = cpci_get_hs_csr(slot); + dbg("%s - slot %s HS_CSR = %04x", + __FUNCTION__, slot->hotplug_slot->name, hs_csr); + + if(!slot->extracting) { + if(update_latch_status(slot->hotplug_slot, 0)) { + warn("failure to update latch file"); + } + slot->extracting = 1; + } + extracted++; + } + } + spin_unlock(&list_lock); + if(inserted || extracted) { + return extracted; + } + else { + err("cannot find ENUM# source, shutting down"); + return -1; + } +} + +/* This is the interrupt mode worker thread body */ +static int +event_thread(void *data) +{ + int rc; + struct slot *slot; + struct list_head *tmp; + + lock_kernel(); + daemonize("cpci_hp_eventd"); + unlock_kernel(); + + dbg("%s - event thread started", __FUNCTION__); + while(1) { + dbg("event thread sleeping"); + down_interruptible(&event_semaphore); + dbg("event thread woken, thread_finished = %d", + thread_finished); + if(thread_finished || signal_pending(current)) + break; + while(controller->ops->query_enum()) { + rc = check_slots(); + if(rc > 0) { + /* Give userspace a chance to handle extraction */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 2); + } else if(rc < 0) { + dbg("%s - error checking slots", __FUNCTION__); + thread_finished = 1; + break; + } + } + /* Check for someone yanking out a board */ + list_for_each(tmp, &slot_list) { + slot = list_entry(tmp, struct slot, slot_list); + if(slot->extracting) { + /* + * Hmmm, we're likely hosed at this point, should we + * bother trying to tell the driver or not? + */ + err("card in slot %s was improperly removed", + slot->hotplug_slot->name); + if(update_adapter_status(slot->hotplug_slot, 0)) { + warn("failure to update adapter file"); + } + slot->extracting = 0; + } + } + + /* Re-enable ENUM# interrupt */ + dbg("%s - re-enabling irq", __FUNCTION__); + controller->ops->enable_irq(); + } + + dbg("%s - event thread signals exit", __FUNCTION__); + up(&thread_exit); + return 0; +} + +/* This is the polling mode worker thread body */ +static int +poll_thread(void *data) +{ + int rc; + struct slot *slot; + struct list_head *tmp; + + lock_kernel(); + daemonize("cpci_hp_polld"); + unlock_kernel(); + + while(1) { + if(thread_finished || signal_pending(current)) + break; + + while(controller->ops->query_enum()) { + rc = check_slots(); + if(rc > 0) { + /* Give userspace a chance to handle extraction */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 2); + } else if(rc < 0) { + dbg("%s - error checking slots", __FUNCTION__); + thread_finished = 1; + break; + } + } + /* Check for someone yanking out a board */ + list_for_each(tmp, &slot_list) { + slot = list_entry(tmp, struct slot, slot_list); + if(slot->extracting) { + /* + * Hmmm, we're likely hosed at this point, should we + * bother trying to tell the driver or not? + */ + err("card in slot %s was improperly removed", + slot->hotplug_slot->name); + if(update_adapter_status(slot->hotplug_slot, 0)) { + warn("failure to update adapter file"); + } + slot->extracting = 0; + } + } + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 10); + } + dbg("poll thread signals exit"); + up(&thread_exit); + return 0; +} + +static int +cpci_start_thread(void) +{ + int pid; + + /* initialize our semaphores */ + init_MUTEX_LOCKED(&event_semaphore); + init_MUTEX_LOCKED(&thread_exit); + thread_finished = 0; + + if(controller->irq) { + pid = kernel_thread(event_thread, 0, 0); + } else { + pid = kernel_thread(poll_thread, 0, 0); + } + if(pid < 0) { + err("Can't start up our thread"); + return -1; + } + dbg("Our thread pid = %d", pid); + return 0; +} + +static void +cpci_stop_thread(void) +{ + thread_finished = 1; + dbg("thread finish command given"); + if(controller->irq) { + up(&event_semaphore); + } + dbg("wait for thread to exit"); + down(&thread_exit); +} + +int +cpci_hp_register_controller(struct cpci_hp_controller *new_controller) +{ + int status = 0; + + if(!controller) { + controller = new_controller; + if(controller->irq) { + if(request_irq(controller->irq, + cpci_hp_intr, + controller->irq_flags, + MY_NAME, controller->dev_id)) { + err("Can't get irq %d for the hotplug cPCI controller", controller->irq); + status = -ENODEV; + } + dbg("%s - acquired controller irq %d", __FUNCTION__, + controller->irq); + } + } else { + err("cPCI hotplug controller already registered"); + status = -1; + } + return status; +} + +int +cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller) +{ + int status = 0; + + if(controller) { + if(!thread_finished) { + cpci_stop_thread(); + } + if(controller->irq) { + free_irq(controller->irq, controller->dev_id); + } + controller = NULL; + } else { + status = -ENODEV; + } + return status; +} + +int +cpci_hp_start(void) +{ + static int first = 1; + int status; + + dbg("%s - enter", __FUNCTION__); + if(!controller) { + return -ENODEV; + } + + spin_lock(&list_lock); + if(!slots) { + spin_unlock(&list_lock); + return -ENODEV; + } + spin_unlock(&list_lock); + + if(first) { + status = init_slots(); + if(status) { + return status; + } + first = 0; + } + + status = cpci_start_thread(); + if(status) { + return status; + } + dbg("%s - thread started", __FUNCTION__); + + if(controller->irq) { + /* Start enum interrupt processing */ + dbg("%s - enabling irq", __FUNCTION__); + controller->ops->enable_irq(); + } + dbg("%s - exit", __FUNCTION__); + return 0; +} + +int +cpci_hp_stop(void) +{ + if(!controller) { + return -ENODEV; + } + + if(controller->irq) { + /* Stop enum interrupt processing */ + dbg("%s - disabling irq", __FUNCTION__); + controller->ops->disable_irq(); + } + cpci_stop_thread(); + return 0; +} + +static void __exit +cleanup_slots(void) +{ + struct list_head *tmp; + struct slot *slot; + + /* + * Unregister all of our slots with the pci_hotplug subsystem, + * and free up all memory that we had allocated. + */ + spin_lock(&list_lock); + if(!slots) { + goto null_cleanup; + } + list_for_each(tmp, &slot_list) { + slot = list_entry(tmp, struct slot, slot_list); + list_del(&slot->slot_list); + pci_hp_deregister(slot->hotplug_slot); + kfree(slot->hotplug_slot->info); + kfree(slot->hotplug_slot->name); + kfree(slot->hotplug_slot); + kfree(slot); + } + null_cleanup: + spin_unlock(&list_lock); + return; +} + +int __init +cpci_hotplug_init(int debug) +{ + spin_lock_init(&list_lock); + cpci_debug = debug; + + info(DRIVER_DESC " version: " DRIVER_VERSION); + return 0; +} + +void __exit +cpci_hotplug_exit(void) +{ + /* + * Clean everything up. + */ + cleanup_slots(); +} + + +EXPORT_SYMBOL_GPL(cpci_hp_register_controller); +EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller); +EXPORT_SYMBOL_GPL(cpci_hp_register_bus); +EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus); +EXPORT_SYMBOL_GPL(cpci_find_slot); +EXPORT_SYMBOL_GPL(cpci_hp_start); +EXPORT_SYMBOL_GPL(cpci_hp_stop); diff -Nru a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,647 @@ +/* + * CompactPCI Hot Plug Driver PCI functions + * + * Copyright (c) 2002 by SOMA Networks, Inc. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + */ + +#include +#include +#include +#include +#include +#include "pci_hotplug.h" +#include "cpci_hotplug.h" + +#if !defined(CONFIG_HOTPLUG_CPCI_MODULE) +#define MY_NAME "cpci_hotplug" +#else +#define MY_NAME THIS_MODULE->name +#endif + +extern int cpci_debug; + +#define dbg(format, arg...) \ + do { \ + if(cpci_debug) \ + printk (KERN_DEBUG "%s: " format "\n", \ + MY_NAME , ## arg); \ + } while(0) +#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) + +#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) + + +u8 cpci_get_attention_status(struct slot* slot) +{ + int hs_cap; + u16 hs_csr; + + hs_cap = pci_bus_find_capability(slot->bus, + slot->devfn, + PCI_CAP_ID_CHSWP); + if(!hs_cap) { + return 0; + } + + if(pci_bus_read_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + &hs_csr)) { + return 0; + } + return hs_csr & 0x0008 ? 1 : 0; +} + +int cpci_set_attention_status(struct slot* slot, int status) +{ + int hs_cap; + u16 hs_csr; + + hs_cap = pci_bus_find_capability(slot->bus, + slot->devfn, + PCI_CAP_ID_CHSWP); + if(!hs_cap) { + return 0; + } + + if(pci_bus_read_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + &hs_csr)) { + return 0; + } + if(status) { + hs_csr |= HS_CSR_LOO; + } else { + hs_csr &= ~HS_CSR_LOO; + } + if(pci_bus_write_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + hs_csr)) { + return 0; + } + return 1; +} + +u16 cpci_get_hs_csr(struct slot* slot) +{ + int hs_cap; + u16 hs_csr; + + hs_cap = pci_bus_find_capability(slot->bus, + slot->devfn, + PCI_CAP_ID_CHSWP); + if(!hs_cap) { + return 0xFFFF; + } + + if(pci_bus_read_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + &hs_csr)) { + return 0xFFFF; + } + return hs_csr; +} + +u16 cpci_set_hs_csr(struct slot* slot, u16 hs_csr) +{ + int hs_cap; + u16 new_hs_csr; + + hs_cap = pci_bus_find_capability(slot->bus, + slot->devfn, + PCI_CAP_ID_CHSWP); + if(!hs_cap) { + return 0xFFFF; + } + + /* Write out the new value */ + if(pci_bus_write_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + hs_csr)) { + return 0xFFFF; + } + + /* Read back what we just wrote out */ + if(pci_bus_read_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + &new_hs_csr)) { + return 0xFFFF; + } + return new_hs_csr; +} + +int cpci_check_and_clear_ins(struct slot* slot) +{ + int hs_cap; + u16 hs_csr; + int ins = 0; + + hs_cap = pci_bus_find_capability(slot->bus, + slot->devfn, + PCI_CAP_ID_CHSWP); + if(!hs_cap) { + return 0; + } + if(pci_bus_read_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + &hs_csr)) { + return 0; + } + if(hs_csr & HS_CSR_INS) { + /* Clear INS (by setting it) */ + if(pci_bus_write_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + hs_csr)) { + ins = 0; + } + ins = 1; + } + return ins; +} + +int cpci_check_ext(struct slot* slot) +{ + int hs_cap; + u16 hs_csr; + int ext = 0; + + hs_cap = pci_bus_find_capability(slot->bus, + slot->devfn, + PCI_CAP_ID_CHSWP); + if(!hs_cap) { + return 0; + } + if(pci_bus_read_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + &hs_csr)) { + return 0; + } + if(hs_csr & HS_CSR_EXT) { + ext = 1; + } + return ext; +} + +int cpci_clear_ext(struct slot* slot) +{ + int hs_cap; + u16 hs_csr; + + hs_cap = pci_bus_find_capability(slot->bus, + slot->devfn, + PCI_CAP_ID_CHSWP); + if(!hs_cap) { + return -ENODEV; + } + if(pci_bus_read_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + &hs_csr)) { + return -ENODEV; + } + if(hs_csr & HS_CSR_EXT) { + /* Clear EXT (by setting it) */ + if(pci_bus_write_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + hs_csr)) { + return -ENODEV; + } + } + return 0; +} + +int cpci_led_on(struct slot* slot) +{ + int hs_cap; + u16 hs_csr; + + hs_cap = pci_bus_find_capability(slot->bus, + slot->devfn, + PCI_CAP_ID_CHSWP); + if(!hs_cap) { + return -ENODEV; + } + if(pci_bus_read_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + &hs_csr)) { + return -ENODEV; + } + if((hs_csr & HS_CSR_LOO) != HS_CSR_LOO) { + /* Set LOO */ + hs_csr |= HS_CSR_LOO; + if(pci_bus_write_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + hs_csr)) { + err("Could not set LOO for slot %s", + slot->hotplug_slot->name); + return -ENODEV; + } + } + return 0; +} + +int cpci_led_off(struct slot* slot) +{ + int hs_cap; + u16 hs_csr; + + hs_cap = pci_bus_find_capability(slot->bus, + slot->devfn, + PCI_CAP_ID_CHSWP); + if(!hs_cap) { + return -ENODEV; + } + if(pci_bus_read_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + &hs_csr)) { + return -ENODEV; + } + if(hs_csr & HS_CSR_LOO) { + /* Clear LOO */ + hs_csr &= ~HS_CSR_LOO; + if(pci_bus_write_config_word(slot->bus, + slot->devfn, + hs_cap + 2, + hs_csr)) { + err("Could not clear LOO for slot %s", + slot->hotplug_slot->name); + return -ENODEV; + } + } + return 0; +} + + +/* + * Device configuration functions + */ + +static int cpci_configure_dev(struct pci_bus *bus, struct pci_dev *dev) +{ + u8 irq_pin; + int r; + + dbg("%s - enter", __FUNCTION__); + + /* NOTE: device already setup from prior scan */ + + /* FIXME: How would we know if we need to enable the expansion ROM? */ + pci_write_config_word(dev, PCI_ROM_ADDRESS, 0x00L); + + /* Assign resources */ + dbg("assigning resources for %02x:%02x.%x", + dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + for (r = 0; r < 6; r++) { + struct resource *res = dev->resource + r; + if(res->flags) + pci_assign_resource(dev, r); + } + dbg("finished assigning resources for %02x:%02x.%x", + dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + + /* Does this function have an interrupt at all? */ + dbg("checking for function interrupt"); + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq_pin); + if(irq_pin) { + dbg("function uses interrupt pin %d", irq_pin); + } + + /* + * Need to explicitly set irq field to 0 so that it'll get assigned + * by the pcibios platform dependent code called by pci_enable_device. + */ + dev->irq = 0; + + dbg("enabling device"); + pci_enable_device(dev); /* XXX check return */ + dbg("now dev->irq = %d", dev->irq); + if(irq_pin && dev->irq) { + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + } + + /* Can't use pci_insert_device at the moment, do it manually for now */ + pci_proc_attach_device(dev); + dbg("notifying drivers"); + //pci_announce_device_to_drivers(dev); + dbg("%s - exit", __FUNCTION__); + return 0; +} + +static int cpci_configure_bridge(struct pci_bus* bus, struct pci_dev* dev) +{ + int rc; + struct pci_bus* child; + struct resource* r; + u8 max, n; + u16 command; + + dbg("%s - enter", __FUNCTION__); + + /* Do basic bridge initialization */ + rc = pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x40); + if(rc) { + printk(KERN_ERR "%s - write of PCI_LATENCY_TIMER failed\n", __FUNCTION__); + } + rc = pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 0x40); + if(rc) { + printk(KERN_ERR "%s - write of PCI_SEC_LATENCY_TIMER failed\n", __FUNCTION__); + } + rc = pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES / 4); + if(rc) { + printk(KERN_ERR "%s - write of PCI_CACHE_LINE_SIZE failed\n", __FUNCTION__); + } + + /* + * Set parent bridge's subordinate field so that configuration space + * access will work in pci_scan_bridge and friends. + */ + max = pci_max_busnr(); + bus->subordinate = max + 1; + pci_write_config_byte(bus->self, PCI_SUBORDINATE_BUS, max + 1); + + /* Scan behind bridge */ + n = pci_scan_bridge(bus, dev, max, 2); + child = pci_find_bus(max + 1); + if (!child) + return -ENODEV; +#ifdef CONFIG_PROC_FS + pci_proc_attach_bus(child); +#endif + /* + * Update parent bridge's subordinate field if there were more bridges + * behind the bridge that was scanned. + */ + if(n > max) { + bus->subordinate = n; + pci_write_config_byte(bus->self, PCI_SUBORDINATE_BUS, n); + } + + /* + * Update the bridge resources of the bridge to accommodate devices + * behind it. + */ + pci_bus_size_bridges(child); + pci_bus_assign_resources(child); + + /* Enable resource mapping via command register */ + command = PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR; + r = child->resource[0]; + if(r && r->start) { + command |= PCI_COMMAND_IO; + } + r = child->resource[1]; + if(r && r->start) { + command |= PCI_COMMAND_MEMORY; + } + r = child->resource[2]; + if(r && r->start) { + command |= PCI_COMMAND_MEMORY; + } + rc = pci_write_config_word(dev, PCI_COMMAND, command); + if(rc) { + err("Error setting command register"); + return rc; + } + + /* Set bridge control register */ + command = PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_NO_ISA; + rc = pci_write_config_word(dev, PCI_BRIDGE_CONTROL, command); + if(rc) { + err("Error setting bridge control register"); + return rc; + } + dbg("%s - exit", __FUNCTION__); + return 0; +} + +static int configure_visit_pci_dev(struct pci_dev_wrapped *wrapped_dev, + struct pci_bus_wrapped *wrapped_bus) +{ + int rc; + struct pci_dev *dev = wrapped_dev->dev; + struct pci_bus *bus = wrapped_bus->bus; + struct slot* slot; + + dbg("%s - enter", __FUNCTION__); + + /* + * We need to fix up the hotplug representation with the Linux + * representation. + */ + slot = cpci_find_slot(dev->bus, dev->devfn); + if(slot) { + slot->dev = dev; + } + + /* If it's a bridge, scan behind it for devices */ + if(dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + rc = cpci_configure_bridge(bus, dev); + if(rc) + return rc; + } + + /* Actually configure device */ + if(dev) { + rc = cpci_configure_dev(bus, dev); + if(rc) + return rc; + } + dbg("%s - exit", __FUNCTION__); + return 0; +} + +static int unconfigure_visit_pci_dev_phase2(struct pci_dev_wrapped *wrapped_dev, + struct pci_bus_wrapped *wrapped_bus) +{ + struct pci_dev *dev = wrapped_dev->dev; + struct slot* slot; + + dbg("%s - enter", __FUNCTION__); + if(!dev) + return -ENODEV; + + /* Remove the Linux representation */ + if(pci_remove_device_safe(dev) == 0) { + kfree(dev); + } else { + err("Could not remove device\n"); + return -1; + } + + /* + * Now remove the hotplug representation. + */ + slot = cpci_find_slot(dev->bus, dev->devfn); + if(slot) { + slot->dev = NULL; + } else { + dbg("No hotplug representation for %02x:%02x.%x", + dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + } + dbg("%s - exit", __FUNCTION__); + return 0; +} + +static int unconfigure_visit_pci_bus_phase2(struct pci_bus_wrapped *wrapped_bus, + struct pci_dev_wrapped *wrapped_dev) +{ + struct pci_bus *bus = wrapped_bus->bus; + struct pci_bus *parent = bus->self->bus; + + dbg("%s - enter", __FUNCTION__); + + /* The cleanup code for proc entries regarding buses should be in the kernel... */ + if(bus->procdir) + dbg("detach_pci_bus %s", bus->procdir->name); + pci_proc_detach_bus(bus); + + /* The cleanup code should live in the kernel... */ + bus->self->subordinate = NULL; + + /* unlink from parent bus */ + list_del(&bus->node); + + /* Now, remove */ + if(bus) + kfree(bus); + + /* Update parent's subordinate field */ + if(parent) { + u8 n = pci_bus_max_busnr(parent); + if(n < parent->subordinate) { + parent->subordinate = n; + pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, n); + } + } + dbg("%s - exit", __FUNCTION__); + return 0; +} + +static struct pci_visit configure_functions = { + .visit_pci_dev = configure_visit_pci_dev, +}; + +static struct pci_visit unconfigure_functions_phase2 = { + .post_visit_pci_bus = unconfigure_visit_pci_bus_phase2, + .post_visit_pci_dev = unconfigure_visit_pci_dev_phase2 +}; + + +int cpci_configure_slot(struct slot* slot) +{ + int rc = 0; + + dbg("%s - enter", __FUNCTION__); + + if(slot->dev == NULL) { + dbg("pci_dev null, finding %02x:%02x:%x", + slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn)); + slot->dev = pci_find_slot(slot->bus->number, slot->devfn); + } + + /* Still NULL? Well then scan for it! */ + if(slot->dev == NULL) { + dbg("pci_dev still null"); + + /* + * This will generate pci_dev structures for all functions, but + * we will only call this case when lookup fails. + */ + slot->dev = pci_scan_slot(slot->bus, slot->devfn); + if(slot->dev == NULL) { + err("Could not find PCI device for slot %02x", slot->number); + return 0; + } + } + dbg("slot->dev = %p", slot->dev); + if(slot->dev) { + struct pci_dev *dev; + struct pci_dev_wrapped wrapped_dev; + struct pci_bus_wrapped wrapped_bus; + int i; + + memset(&wrapped_dev, 0, sizeof (struct pci_dev_wrapped)); + memset(&wrapped_bus, 0, sizeof (struct pci_bus_wrapped)); + + for (i = 0; i < 8; i++) { + dev = pci_find_slot(slot->bus->number, + PCI_DEVFN(PCI_SLOT(slot->dev->devfn), i)); + if(!dev) + continue; + wrapped_dev.dev = dev; + wrapped_bus.bus = slot->dev->bus; + rc = pci_visit_dev(&configure_functions, &wrapped_dev, &wrapped_bus); + } + } + + dbg("%s - exit, rc = %d", __FUNCTION__, rc); + return rc; +} + +int cpci_unconfigure_slot(struct slot* slot) +{ + int rc = 0; + int i; + struct pci_dev_wrapped wrapped_dev; + struct pci_bus_wrapped wrapped_bus; + struct pci_dev *dev; + + dbg("%s - enter", __FUNCTION__); + + if(!slot->dev) { + err("No device for slot %02x\n", slot->number); + return -ENODEV; + } + + memset(&wrapped_dev, 0, sizeof (struct pci_dev_wrapped)); + memset(&wrapped_bus, 0, sizeof (struct pci_bus_wrapped)); + + for (i = 0; i < 8; i++) { + dev = pci_find_slot(slot->bus->number, + PCI_DEVFN(PCI_SLOT(slot->devfn), i)); + if(dev) { + wrapped_dev.dev = dev; + wrapped_bus.bus = dev->bus; + dbg("%s - unconfigure phase 2", __FUNCTION__); + rc = pci_visit_dev(&unconfigure_functions_phase2, + &wrapped_dev, &wrapped_bus); + if(rc) + break; + } + } + dbg("%s - exit, rc = %d", __FUNCTION__, rc); + return rc; +} diff -Nru a/drivers/pci/hotplug/cpcihp_generic.c b/drivers/pci/hotplug/cpcihp_generic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/cpcihp_generic.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,290 @@ +/* + * cpcihp_generic.c + * + * Generic port I/O CompactPCI driver + * + * Copyright 2002 SOMA Networks, Inc. + * Copyright 2001 Intel San Luis Obispo + * Copyright 2000,2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This generic CompactPCI hotplug driver should allow using the PCI hotplug + * mechanism on any CompactPCI board that exposes the #ENUM signal as a bit + * in a system register that can be read through standard port I/O. + * + * Send feedback to + */ + +#include +#include +#include +#include +#include +#include "cpci_hotplug.h" + +#define DRIVER_VERSION "0.1" +#define DRIVER_AUTHOR "Scott Murray " +#define DRIVER_DESC "Generic port I/O CompactPCI Hot Plug Driver" + +#if !defined(CONFIG_HOTPLUG_CPCI_GENERIC_MODULE) +#define MY_NAME "cpcihp_generic" +#else +#define MY_NAME THIS_MODULE->name +#endif + +#define dbg(format, arg...) \ + do { \ + if(debug) \ + printk (KERN_DEBUG "%s: " format "\n", \ + MY_NAME , ## arg); \ + } while(0) +#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) + +/* local variables */ +static int debug; +static char* bridge; +static u8 bridge_busnr; +static u8 bridge_slot; +static struct pci_bus *bus; +static u8 first_slot; +static u8 last_slot; +static u16 port; +static unsigned int enum_bit; +static u8 enum_mask; + +static struct cpci_hp_controller_ops generic_hpc_ops; +static struct cpci_hp_controller generic_hpc; + +/* The following allows configuring the driver when it's compiled into the kernel */ +#ifndef MODULE +static int __init cpcihp_generic_setup(char* str) +{ + char* p; + unsigned long tmp; + + if(!str) + return -EINVAL; + bridge = str; + + p = strchr(str, ','); + str = p + 1; + if(!(p && *str && *p == ',')) + goto setup_error; + tmp = simple_strtoul(str, &p, 0); + if(p == str || tmp > 0xff) { + err("hotplug bus first slot number out of range"); + goto setup_error; + } + first_slot = (u8) tmp; + + str = p + 1; + if(!(*str && *p == ',')) + return -EINVAL; + tmp = simple_strtoul(str, &p, 0); + if(p == str || tmp > 0xff) { + err("hotplug bus last slot number out of range"); + goto setup_error; + } + last_slot = (u8) tmp; + + str = p + 1; + if(!(*str && *p == ',')) + goto setup_error; + tmp = simple_strtoul(str, &p, 0); + if(p == str || tmp > 0xffff) { + err("port number out of range"); + goto setup_error; + } + port = (u16) tmp; + + str = p + 1; + if(!(*str && *p == ',')) + goto setup_error; + tmp = simple_strtoul(str, &p, 0); + if(p == str) { + err("invalid #ENUM bit number"); + goto setup_error; + } + enum_bit = (u8) tmp; + + str = p + 1; + if(*str && *p == ',') { + tmp = simple_strtoul(str, &p, 0); + if(p != str) + debug = (int) tmp; + } + return 0; +setup_error: + bridge = NULL; + return -EINVAL; +} + +__setup("cpcihp_generic=", cpcihp_generic_setup); +#endif + +static int __init validate_parameters(void) +{ + char* str; + char* p; + unsigned long tmp; + + if(!bridge) { + info("not configured, disabling."); + return 1; + } + str = bridge; + if(!*str) + return -EINVAL; + + tmp = simple_strtoul(str, &p, 16); + if(p == str || tmp > 0xff) { + err("Invalid hotplug bus bridge device bus number"); + return -EINVAL; + } + bridge_busnr = (u8) tmp; + dbg("bridge_busnr = 0x%02x", bridge_busnr); + if(*p != ':') { + err("Invalid hotplug bus bridge device"); + return -EINVAL; + } + str = p + 1; + tmp = simple_strtoul(str, &p, 16); + if(p == str || tmp > 0x1f) { + err("Invalid hotplug bus bridge device slot number"); + return -EINVAL; + } + bridge_slot = (u8) tmp; + dbg("bridge_slot = 0x%02x", bridge_slot); + + dbg("first_slot = 0x%02x", first_slot); + dbg("last_slot = 0x%02x", last_slot); + if(!(first_slot && last_slot)) { + err("Need to specify first_slot and last_slot"); + return -EINVAL; + } + if(last_slot < first_slot) { + err("first_slot must be less than last_slot"); + return -EINVAL; + } + + dbg("port = 0x%04x", port); + dbg("enum_bit = 0x%02x", enum_bit); + if(enum_bit > 7) { + err("Invalid #ENUM bit"); + return -EINVAL; + } + enum_mask = 1 << enum_bit; + return 0; +} + +static int query_enum(void) +{ + u8 value; + + value = inb_p(port); + return ((value & enum_mask) == enum_mask); +} + +static int __init cpcihp_generic_init(void) +{ + int status; + struct resource* r; + struct pci_dev* dev; + + info(DRIVER_DESC " version: " DRIVER_VERSION); + status = validate_parameters(); + if(status != 0) + return status; + + r = request_region(port, 1, "#ENUM hotswap signal register"); + if(!r) + return -EBUSY; + + dev = pci_find_slot(bridge_busnr, PCI_DEVFN(bridge_slot, 0)); + if(!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { + err("Invalid bridge device %s", bridge); + return -EINVAL; + } + bus = dev->subordinate; + + memset(&generic_hpc, 0, sizeof (struct cpci_hp_controller)); + generic_hpc_ops.query_enum = query_enum; + generic_hpc.ops = &generic_hpc_ops; + + status = cpci_hp_register_controller(&generic_hpc); + if(status != 0) { + err("Could not register cPCI hotplug controller"); + return -ENODEV; + } + dbg("registered controller"); + + status = cpci_hp_register_bus(bus, first_slot, last_slot); + if(status != 0) { + err("Could not register cPCI hotplug bus"); + goto init_bus_register_error; + } + dbg("registered bus"); + + status = cpci_hp_start(); + if(status != 0) { + err("Could not started cPCI hotplug system"); + goto init_start_error; + } + dbg("started cpci hp system"); + return 0; +init_start_error: + cpci_hp_unregister_bus(bus); +init_bus_register_error: + cpci_hp_unregister_controller(&generic_hpc); + err("status = %d", status); + return status; + +} + +static void __exit cpcihp_generic_exit(void) +{ + cpci_hp_stop(); + cpci_hp_unregister_bus(bus); + cpci_hp_unregister_controller(&generic_hpc); + release_region(port, 1); +} + +module_init(cpcihp_generic_init); +module_exit(cpcihp_generic_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); +MODULE_PARM(bridge, "s"); +MODULE_PARM_DESC(bridge, "Hotswap bus bridge device, : (bus and slot are in hexadecimal)"); +MODULE_PARM(first_slot, "b"); +MODULE_PARM_DESC(first_slot, "Hotswap bus first slot number"); +MODULE_PARM(last_slot, "b"); +MODULE_PARM_DESC(last_slot, "Hotswap bus last slot number"); +MODULE_PARM(port, "h"); +MODULE_PARM_DESC(port, "#ENUM signal I/O port"); +MODULE_PARM(enum_bit, "i"); +MODULE_PARM_DESC(enum_bit, "#ENUM signal bit (0-7)"); diff -Nru a/drivers/pci/hotplug/cpcihp_zt5550.c b/drivers/pci/hotplug/cpcihp_zt5550.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/cpcihp_zt5550.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,306 @@ +/* + * cpcihp_zt5550.c + * + * Intel/Ziatech ZT5550 CompactPCI Host Controller driver + * + * Copyright 2002 SOMA Networks, Inc. + * Copyright 2001 Intel San Luis Obispo + * Copyright 2000,2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + */ + +#include +#include +#include +#include +#include +#include "cpci_hotplug.h" +#include "cpcihp_zt5550.h" + +#define DRIVER_VERSION "0.2" +#define DRIVER_AUTHOR "Scott Murray " +#define DRIVER_DESC "ZT5550 CompactPCI Hot Plug Driver" + +#if !defined(CONFIG_HOTPLUG_PCI_CPCI_ZT5550_MODULE) +#define MY_NAME "cpcihp_zt5550" +#else +#define MY_NAME THIS_MODULE->name +#endif + +#define dbg(format, arg...) \ + do { \ + if(debug) \ + printk (KERN_DEBUG "%s: " format "\n", \ + MY_NAME , ## arg); \ + } while(0) +#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) + +/* local variables */ +static int debug; +static int poll; +static struct cpci_hp_controller_ops zt5550_hpc_ops; +static struct cpci_hp_controller zt5550_hpc; + +/* Primary cPCI bus bridge device */ +static struct pci_dev *bus0_dev; +static struct pci_bus *bus0; + +/* Host controller device */ +static struct pci_dev *hc_dev; + +/* Host controller register addresses */ +static void *hc_registers; +static void *csr_hc_index; +static void *csr_hc_data; +static void *csr_int_status; +static void *csr_int_mask; + + +static int zt5550_hc_config(struct pci_dev *pdev) +{ + /* Since we know that no boards exist with two HC chips, treat it as an error */ + if(hc_dev) { + err("too many host controller devices?"); + return -EBUSY; + } + hc_dev = pdev; + dbg("hc_dev = %p", hc_dev); + dbg("pci resource start %lx", pci_resource_start(hc_dev, 1)); + dbg("pci resource len %lx", pci_resource_len(hc_dev, 1)); + + if(!request_mem_region(pci_resource_start(hc_dev, 1), + pci_resource_len(hc_dev, 1), MY_NAME)) { + err("cannot reserve MMIO region"); + return -ENOMEM; + } + + hc_registers = + ioremap(pci_resource_start(hc_dev, 1), pci_resource_len(hc_dev, 1)); + if(!hc_registers) { + err("cannot remap MMIO region %lx @ %lx", + pci_resource_len(hc_dev, 1), pci_resource_start(hc_dev, 1)); + release_mem_region(pci_resource_start(hc_dev, 1), + pci_resource_len(hc_dev, 1)); + return -ENODEV; + } + + csr_hc_index = hc_registers + CSR_HCINDEX; + csr_hc_data = hc_registers + CSR_HCDATA; + csr_int_status = hc_registers + CSR_INTSTAT; + csr_int_mask = hc_registers + CSR_INTMASK; + + /* + * Disable host control, fault and serial interrupts + */ + dbg("disabling host control, fault and serial interrupts"); + writeb((u8) HC_INT_MASK_REG, csr_hc_index); + writeb((u8) ALL_INDEXED_INTS_MASK, csr_hc_data); + dbg("disabled host control, fault and serial interrupts"); + + /* + * Disable timer0, timer1 and ENUM interrupts + */ + dbg("disabling timer0, timer1 and ENUM interrupts"); + writeb((u8) ALL_DIRECT_INTS_MASK, csr_int_mask); + dbg("disabled timer0, timer1 and ENUM interrupts"); + return 0; +} + +static int zt5550_hc_cleanup(void) +{ + if(!hc_dev) + return -ENODEV; + release_mem_region(pci_resource_start(hc_dev, 1), + pci_resource_len(hc_dev, 1)); + return 0; +} + +static int zt5550_hc_query_enum(void) +{ + u8 value; + + value = inb_p(ENUM_PORT); + return ((value & ENUM_MASK) == ENUM_MASK); +} + +static int zt5550_hc_check_irq(void *dev_id) +{ + int ret; + u8 reg; + + ret = 0; + if(dev_id == zt5550_hpc.dev_id) { + reg = readb(csr_int_status); + if(reg) + ret = 1; + } + return ret; +} + +static int zt5550_hc_enable_irq(void) +{ + u8 reg; + + if(hc_dev == NULL) { + return -ENODEV; + } + reg = readb(csr_int_mask); + reg = reg & ~ENUM_INT_MASK; + writeb(reg, csr_int_mask); + return 0; +} + +int zt5550_hc_disable_irq(void) +{ + u8 reg; + + if(hc_dev == NULL) { + return -ENODEV; + } + + reg = readb(csr_int_mask); + reg = reg | ENUM_INT_MASK; + writeb(reg, csr_int_mask); + return 0; +} + +static int __devinit zt5550_hc_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int status; + + status = zt5550_hc_config(pdev); + if(status != 0) { + return status; + } + dbg("returned from zt5550_hc_config"); + + memset(&zt5550_hpc, 0, sizeof (struct cpci_hp_controller)); + zt5550_hpc_ops.query_enum = zt5550_hc_query_enum; + zt5550_hpc.ops = &zt5550_hpc_ops; + if(!poll) { + zt5550_hpc.irq = hc_dev->irq; + zt5550_hpc.irq_flags = SA_SHIRQ; + zt5550_hpc.dev_id = hc_dev; + + zt5550_hpc_ops.enable_irq = zt5550_hc_enable_irq; + zt5550_hpc_ops.disable_irq = zt5550_hc_disable_irq; + zt5550_hpc_ops.check_irq = zt5550_hc_check_irq; + } else { + info("using ENUM# polling mode"); + } + + status = cpci_hp_register_controller(&zt5550_hpc); + if(status != 0) { + err("could not register cPCI hotplug controller"); + goto init_hc_error; + } + dbg("registered controller"); + + /* Look for first device matching cPCI bus's bridge vendor and device IDs */ + if(!(bus0_dev = pci_find_device(PCI_VENDOR_ID_DEC, + PCI_DEVICE_ID_DEC_21154, NULL))) { + status = -ENODEV; + goto init_register_error; + } + bus0 = bus0_dev->subordinate; + + status = cpci_hp_register_bus(bus0, 0x0a, 0x0f); + if(status != 0) { + err("could not register cPCI hotplug bus"); + goto init_register_error; + } + dbg("registered bus"); + + status = cpci_hp_start(); + if(status != 0) { + err("could not started cPCI hotplug system"); + cpci_hp_unregister_bus(bus0); + goto init_register_error; + } + dbg("started cpci hp system"); + + return 0; +init_register_error: + cpci_hp_unregister_controller(&zt5550_hpc); +init_hc_error: + err("status = %d", status); + zt5550_hc_cleanup(); + return status; + +} + +static void __devexit zt5550_hc_remove_one(struct pci_dev *pdev) +{ + cpci_hp_stop(); + cpci_hp_unregister_bus(bus0); + cpci_hp_unregister_controller(&zt5550_hpc); + zt5550_hc_cleanup(); +} + + +static struct pci_device_id zt5550_hc_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_ZIATECH, PCI_DEVICE_ID_ZIATECH_5550_HC, PCI_ANY_ID, PCI_ANY_ID, }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, zt5550_hc_pci_tbl); + +static struct pci_driver zt5550_hc_driver = { + .name = "zt5550_hc", + .id_table = zt5550_hc_pci_tbl, + .probe = zt5550_hc_init_one, + .remove = __devexit_p(zt5550_hc_remove_one), +}; + +static int __init zt5550_init(void) +{ + struct resource* r; + + info(DRIVER_DESC " version: " DRIVER_VERSION); + r = request_region(ENUM_PORT, 1, "#ENUM hotswap signal register"); + if(!r) + return -EBUSY; + + return pci_module_init(&zt5550_hc_driver); +} + +static void __exit +zt5550_exit(void) +{ + pci_unregister_driver(&zt5550_hc_driver); + release_region(ENUM_PORT, 1); +} + +module_init(zt5550_init); +module_exit(zt5550_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); +MODULE_PARM(poll, "i"); +MODULE_PARM_DESC(poll, "#ENUM polling mode enabled or not"); diff -Nru a/drivers/pci/hotplug/cpcihp_zt5550.h b/drivers/pci/hotplug/cpcihp_zt5550.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/cpcihp_zt5550.h Wed Jun 4 18:11:56 2003 @@ -0,0 +1,79 @@ +/* + * cpcihp_zt5550.h + * + * Intel/Ziatech ZT5550 CompactPCI Host Controller driver definitions + * + * Copyright 2002 SOMA Networks, Inc. + * Copyright 2001 Intel San Luis Obispo + * Copyright 2000,2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + */ + +#ifndef _CPCIHP_ZT5550_H +#define _CPCIHP_ZT5550_H + +/* Direct registers */ +#define CSR_HCINDEX 0x00 +#define CSR_HCDATA 0x04 +#define CSR_INTSTAT 0x08 +#define CSR_INTMASK 0x09 +#define CSR_CNT0CMD 0x0C +#define CSR_CNT1CMD 0x0E +#define CSR_CNT0 0x10 +#define CSR_CNT1 0x14 + +/* Masks for interrupt bits in CSR_INTMASK direct register */ +#define CNT0_INT_MASK 0x01 +#define CNT1_INT_MASK 0x02 +#define ENUM_INT_MASK 0x04 +#define ALL_DIRECT_INTS_MASK 0x07 + +/* Indexed registers (through CSR_INDEX, CSR_DATA) */ +#define HC_INT_MASK_REG 0x04 +#define HC_STATUS_REG 0x08 +#define HC_CMD_REG 0x0C +#define ARB_CONFIG_GNT_REG 0x10 +#define ARB_CONFIG_CFG_REG 0x12 +#define ARB_CONFIG_REG 0x10 +#define ISOL_CONFIG_REG 0x18 +#define FAULT_STATUS_REG 0x20 +#define FAULT_CONFIG_REG 0x24 +#define WD_CONFIG_REG 0x2C +#define HC_DIAG_REG 0x30 +#define SERIAL_COMM_REG 0x34 +#define SERIAL_OUT_REG 0x38 +#define SERIAL_IN_REG 0x3C + +/* Masks for interrupt bits in HC_INT_MASK_REG indexed register */ +#define SERIAL_INT_MASK 0x01 +#define FAULT_INT_MASK 0x02 +#define HCF_INT_MASK 0x04 +#define ALL_INDEXED_INTS_MASK 0x07 + +/* Digital I/O port storing ENUM# */ +#define ENUM_PORT 0xE1 +/* Mask to get to the ENUM# bit on the bus */ +#define ENUM_MASK 0x40 + +#endif /* _CPCIHP_ZT5550_H */ diff -Nru a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/cpqphp.h Wed Jun 4 18:11:56 2003 @@ -0,0 +1,912 @@ +/* + * 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 + * + */ +#ifndef _CPQPHP_H +#define _CPQPHP_H + +#include "pci_hotplug.h" +#include +#include /* for read? and write? functions */ +#include /* for delays */ + +#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 */ + u8 reserved12; /* 0x2e */ + u8 reserved13; /* 0x2f */ + u8 next_curr_freq; /* 0x30 */ + u8 reset_freq_mode; /* 0x31 */ +} __attribute__ ((packed)); + +/* offsets to the controller registers based on the above structure layout */ +enum ctrl_offsets { + SLOT_RST = offsetof(struct ctrl_reg, slot_RST), + SLOT_ENABLE = offsetof(struct ctrl_reg, slot_enable), + MISC = offsetof(struct ctrl_reg, misc), + LED_CONTROL = offsetof(struct ctrl_reg, led_control), + INT_INPUT_CLEAR = offsetof(struct ctrl_reg, int_input_clear), + INT_MASK = offsetof(struct ctrl_reg, int_mask), + CTRL_RESERVED0 = offsetof(struct ctrl_reg, reserved0), + CTRL_RESERVED1 = offsetof(struct ctrl_reg, reserved1), + CTRL_RESERVED2 = offsetof(struct ctrl_reg, reserved1), + GEN_OUTPUT_AB = offsetof(struct ctrl_reg, gen_output_AB), + NON_INT_INPUT = offsetof(struct ctrl_reg, non_int_input), + CTRL_RESERVED3 = offsetof(struct ctrl_reg, reserved3), + CTRL_RESERVED4 = offsetof(struct ctrl_reg, reserved4), + CTRL_RESERVED5 = offsetof(struct ctrl_reg, reserved5), + CTRL_RESERVED6 = offsetof(struct ctrl_reg, reserved6), + CTRL_RESERVED7 = offsetof(struct ctrl_reg, reserved7), + CTRL_RESERVED8 = offsetof(struct ctrl_reg, reserved8), + SLOT_MASK = offsetof(struct ctrl_reg, slot_mask), + CTRL_RESERVED9 = offsetof(struct ctrl_reg, reserved9), + CTRL_RESERVED10 = offsetof(struct ctrl_reg, reserved10), + CTRL_RESERVED11 = offsetof(struct ctrl_reg, reserved11), + SLOT_SERR = offsetof(struct ctrl_reg, slot_SERR), + SLOT_POWER = offsetof(struct ctrl_reg, slot_power), + NEXT_CURR_FREQ = offsetof(struct ctrl_reg, next_curr_freq), + RESET_FREQ_MODE = offsetof(struct ctrl_reg, reset_freq_mode), +}; + +struct hrt { + char sig0; + char sig1; + char sig2; + char sig3; + u16 unused_IRQ; + u16 PCIIRQ; + u8 number_of_entries; + u8 revision; + u16 reserved1; + u32 reserved2; +} __attribute__ ((packed)); + +/* offsets to the hotplug resource table registers based on the above structure layout */ +enum hrt_offsets { + SIG0 = offsetof(struct hrt, sig0), + SIG1 = offsetof(struct hrt, sig1), + SIG2 = offsetof(struct hrt, sig2), + SIG3 = offsetof(struct hrt, sig3), + UNUSED_IRQ = offsetof(struct hrt, unused_IRQ), + PCIIRQ = offsetof(struct hrt, PCIIRQ), + NUMBER_OF_ENTRIES = offsetof(struct hrt, number_of_entries), + REVISION = offsetof(struct hrt, revision), + HRT_RESERVED1 = offsetof(struct hrt, reserved1), + HRT_RESERVED2 = offsetof(struct hrt, reserved2), +}; + +struct slot_rt { + u8 dev_func; + u8 primary_bus; + u8 secondary_bus; + u8 max_bus; + u16 io_base; + u16 io_length; + u16 mem_base; + u16 mem_length; + u16 pre_mem_base; + u16 pre_mem_length; +} __attribute__ ((packed)); + +/* offsets to the hotplug slot resource table registers based on the above structure layout */ +enum slot_rt_offsets { + DEV_FUNC = offsetof(struct slot_rt, dev_func), + PRIMARY_BUS = offsetof(struct slot_rt, primary_bus), + SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus), + MAX_BUS = offsetof(struct slot_rt, max_bus), + IO_BASE = offsetof(struct slot_rt, io_base), + IO_LENGTH = offsetof(struct slot_rt, io_length), + MEM_BASE = offsetof(struct slot_rt, mem_base), + MEM_LENGTH = offsetof(struct slot_rt, mem_length), + PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base), + PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length), +}; + +struct pci_func { + struct pci_func *next; + u8 bus; + u8 device; + u8 function; + u8 is_a_board; + u16 status; + u8 configured; + u8 switch_save; + u8 presence_save; + u32 base_length[0x06]; + u8 base_type[0x06]; + u16 reserved2; + u32 config_space[0x20]; + struct pci_resource *mem_head; + struct pci_resource *p_mem_head; + struct pci_resource *io_head; + struct pci_resource *bus_head; + struct timer_list *p_task_event; + struct pci_dev* pci_dev; +}; + +#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_bus *pci_bus; + struct event_info event_queue[10]; + struct slot *slot; + u8 next_event; + u8 interrupt; + u8 cfgspc_irq; + u8 bus; /* bus number for the pci hotplug controller */ + u8 rev; + u8 slot_device_offset; + u8 first_slot; + u8 add_support; + u8 push_flag; + enum pci_bus_speed speed; + enum pci_bus_speed speed_capability; + u8 push_button; /* 0 = no pushbutton, 1 = pushbutton present */ + u8 slot_switch_type; /* 0 = no switch, 1 = switch present */ + u8 defeature_PHP; /* 0 = PHP not supported, 1 = PHP supported */ + u8 alternate_base_address; /* 0 = not supported, 1 = supported */ + u8 pci_config_space; /* Index/data access to working registers 0 = not supported, 1 = supported */ + u8 pcix_speed_capability; /* PCI-X */ + u8 pcix_support; /* PCI-X */ + u16 vendor_id; + struct work_struct int_task_event; + wait_queue_head_t queue; /* sleep & wake process */ +}; + +struct irq_mapping { + u8 barber_pole; + u8 valid_INT; + u8 interrupt[4]; +}; + +struct resource_lists { + struct pci_resource *mem_head; + struct pci_resource *p_mem_head; + struct pci_resource *io_head; + struct pci_resource *bus_head; + struct irq_mapping *irqs; +}; + +#define ROM_PHY_ADDR 0x0F0000 +#define ROM_PHY_LEN 0x00ffff + +#define PCI_HPC_ID 0xA0F7 +#define PCI_SUB_HPC_ID 0xA2F7 +#define PCI_SUB_HPC_ID2 0xA2F8 +#define PCI_SUB_HPC_ID3 0xA2F9 +#define PCI_SUB_HPC_ID_INTC 0xA2FA +#define PCI_SUB_HPC_ID4 0xA2FD + +#define INT_BUTTON_IGNORE 0 +#define INT_PRESENCE_ON 1 +#define INT_PRESENCE_OFF 2 +#define INT_SWITCH_CLOSE 3 +#define INT_SWITCH_OPEN 4 +#define INT_POWER_FAULT 5 +#define INT_POWER_FAULT_CLEAR 6 +#define INT_BUTTON_PRESS 7 +#define INT_BUTTON_RELEASE 8 +#define INT_BUTTON_CANCEL 9 + +#define STATIC_STATE 0 +#define BLINKINGON_STATE 1 +#define BLINKINGOFF_STATE 2 +#define POWERON_STATE 3 +#define POWEROFF_STATE 4 + +#define PCISLOT_INTERLOCK_CLOSED 0x00000001 +#define PCISLOT_ADAPTER_PRESENT 0x00000002 +#define PCISLOT_POWERED 0x00000004 +#define PCISLOT_66_MHZ_OPERATION 0x00000008 +#define PCISLOT_64_BIT_OPERATION 0x00000010 +#define PCISLOT_REPLACE_SUPPORTED 0x00000020 +#define PCISLOT_ADD_SUPPORTED 0x00000040 +#define PCISLOT_INTERLOCK_SUPPORTED 0x00000080 +#define PCISLOT_66_MHZ_SUPPORTED 0x00000100 +#define PCISLOT_64_BIT_SUPPORTED 0x00000200 + + + +#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400 + + +#define INTERLOCK_OPEN 0x00000002 +#define ADD_NOT_SUPPORTED 0x00000003 +#define CARD_FUNCTIONING 0x00000005 +#define ADAPTER_NOT_SAME 0x00000006 +#define NO_ADAPTER_PRESENT 0x00000009 +#define NOT_ENOUGH_RESOURCES 0x0000000B +#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C +#define POWER_FAILURE 0x0000000E + +#define REMOVE_NOT_SUPPORTED 0x00000003 + + +/* + * error Messages + */ +#define msg_initialization_err "Initialization failure, error=%d\n" +#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n" +#define msg_HPC_non_compaq_or_intel "The PCI hot plug controller is not supported by this driver.\n" +#define msg_HPC_not_supported "this system is not supported by this version of cpqphpd. Upgrade to a newer version of cpqphpd\n" +#define msg_unable_to_save "unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n" +#define msg_button_on "PCI slot #%d - powering on due to button press.\n" +#define msg_button_off "PCI slot #%d - powering off due to button press.\n" +#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n" +#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n" + + +/* sysfs functions for the hotplug controller info */ +extern void cpqhp_create_ctrl_files (struct controller *ctrl); + +/* controller functions */ +extern void cpqhp_pushbutton_thread (unsigned long event_pointer); +extern irqreturn_t cpqhp_ctrl_intr (int IRQ, void *data, struct pt_regs *regs); +extern int cpqhp_find_available_resources (struct controller *ctrl, void *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); +extern struct slot *cpqhp_find_slot (struct controller *ctrl, u8 device); + +/* 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 &= ~(0x0101L << slot); + led_control |= (0x0001L << slot); + writel(led_control, ctrl->hpc_reg + LED_CONTROL); +} + + +static inline void slot_disable (struct controller *ctrl, u8 slot) +{ + u8 slot_enable; + + slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE); + slot_enable &= ~(0x01 << slot); + writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE); +} + + +static inline void slot_enable (struct controller *ctrl, u8 slot) +{ + u8 slot_enable; + + slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE); + slot_enable |= (0x01 << slot); + writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE); +} + + +static inline u8 is_slot_enabled (struct controller *ctrl, u8 slot) +{ + u8 slot_enable; + + slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE); + slot_enable &= (0x01 << slot); + return slot_enable ? 1 : 0; +} + + +static inline u8 read_slot_enable (struct controller *ctrl) +{ + return readb(ctrl->hpc_reg + SLOT_ENABLE); +} + + +/* + * get_controller_speed - find the current frequency/mode of controller. + * + * @ctrl: controller to get frequency/mode for. + * + * Returns controller speed. + * + */ +static inline u8 get_controller_speed (struct controller *ctrl) +{ + u8 curr_freq; + u16 misc; + + if (ctrl->pcix_support) { + curr_freq = readb(ctrl->hpc_reg + NEXT_CURR_FREQ); + if ((curr_freq & 0xB0) == 0xB0) + return PCI_SPEED_133MHz_PCIX; + if ((curr_freq & 0xA0) == 0xA0) + return PCI_SPEED_100MHz_PCIX; + if ((curr_freq & 0x90) == 0x90) + return PCI_SPEED_66MHz_PCIX; + if (curr_freq & 0x10) + return PCI_SPEED_66MHz; + + return PCI_SPEED_33MHz; + } + + misc = readw(ctrl->hpc_reg + MISC); + return (misc & 0x0800) ? PCI_SPEED_66MHz : PCI_SPEED_33MHz; +} + + +/* + * get_adapter_speed - find the max supported frequency/mode of adapter. + * + * @ctrl: hotplug controller. + * @hp_slot: hotplug slot where adapter is installed. + * + * Returns adapter speed. + * + */ +static inline u8 get_adapter_speed (struct controller *ctrl, u8 hp_slot) +{ + u32 temp_dword = readl(ctrl->hpc_reg + NON_INT_INPUT); + dbg("slot: %d, PCIXCAP: %8x\n", hp_slot, temp_dword); + if (ctrl->pcix_support) { + if (temp_dword & (0x10000 << hp_slot)) + return PCI_SPEED_133MHz_PCIX; + if (temp_dword & (0x100 << hp_slot)) + return PCI_SPEED_66MHz_PCIX; + } + + if (temp_dword & (0x01 << hp_slot)) + return PCI_SPEED_66MHz; + + return PCI_SPEED_33MHz; +} + +static inline void enable_slot_power (struct controller *ctrl, u8 slot) +{ + u8 slot_power; + + slot_power = readb(ctrl->hpc_reg + SLOT_POWER); + slot_power |= (0x01 << slot); + writeb(slot_power, ctrl->hpc_reg + SLOT_POWER); +} + +static inline void disable_slot_power (struct controller *ctrl, u8 slot) +{ + u8 slot_power; + + slot_power = readb(ctrl->hpc_reg + SLOT_POWER); + slot_power &= ~(0x01 << slot); + writeb(slot_power, ctrl->hpc_reg + SLOT_POWER); +} + + +static inline int cpq_get_attention_status (struct controller *ctrl, struct slot *slot) +{ + u8 hp_slot; + + 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("%s: slot->device = %d, ctrl->slot_device_offset = %d \n", + __FUNCTION__, slot->device, ctrl->slot_device_offset); + + status = (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot)); + + return(status == 0) ? 1 : 0; +} + + +static inline int get_presence_status (struct controller *ctrl, struct slot *slot) +{ + int presence_save = 0; + u8 hp_slot; + u32 tempdword; + + 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("%s - start\n", __FUNCTION__); + 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("%s - end\n", __FUNCTION__); + return retval; +} + + +/** + * set_controller_speed - set the frequency and/or mode of a specific + * controller segment. + * + * @ctrl: controller to change frequency/mode for. + * @adapter_speed: the speed of the adapter we want to match. + * @hp_slot: the slot number where the adapter is installed. + * + * Returns 0 if we successfully change frequency and/or mode to match the + * adapter speed. + * + */ +static inline u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_slot) +{ + struct slot *slot; + u8 reg; + u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER); + u16 reg16; + u32 leds = readl(ctrl->hpc_reg + LED_CONTROL); + + if (ctrl->speed == adapter_speed) + return 0; + + /* We don't allow freq/mode changes if we find another adapter running + * in another slot on this controller */ + for(slot = ctrl->slot; slot; slot = slot->next) { + if (slot->device == (hp_slot + ctrl->slot_device_offset)) + continue; + if (!slot->hotplug_slot && !slot->hotplug_slot->info) + continue; + if (slot->hotplug_slot->info->adapter_status == 0) + continue; + /* If another adapter is running on the same segment but at a + * lower speed/mode, we allow the new adapter to function at + * this rate if supported */ + if (ctrl->speed < adapter_speed) + return 0; + + return 1; + } + + /* If the controller doesn't support freq/mode changes and the + * controller is running at a higher mode, we bail */ + if ((ctrl->speed > adapter_speed) && (!ctrl->pcix_speed_capability)) + return 1; + + /* But we allow the adapter to run at a lower rate if possible */ + if ((ctrl->speed < adapter_speed) && (!ctrl->pcix_speed_capability)) + return 0; + + /* We try to set the max speed supported by both the adapter and + * controller */ + if (ctrl->speed_capability < adapter_speed) { + if (ctrl->speed == ctrl->speed_capability) + return 0; + adapter_speed = ctrl->speed_capability; + } + + writel(0x0L, ctrl->hpc_reg + LED_CONTROL); + writeb(0x00, ctrl->hpc_reg + SLOT_ENABLE); + + set_SOGO(ctrl); + wait_for_ctrl_irq(ctrl); + + if (adapter_speed != PCI_SPEED_133MHz_PCIX) + reg = 0xF5; + else + reg = 0xF4; + pci_write_config_byte(ctrl->pci_dev, 0x41, reg); + + reg16 = readw(ctrl->hpc_reg + NEXT_CURR_FREQ); + reg16 &= ~0x000F; + switch(adapter_speed) { + case(PCI_SPEED_133MHz_PCIX): + reg = 0x75; + reg16 |= 0xB; + break; + case(PCI_SPEED_100MHz_PCIX): + reg = 0x74; + reg16 |= 0xA; + break; + case(PCI_SPEED_66MHz_PCIX): + reg = 0x73; + reg16 |= 0x9; + break; + case(PCI_SPEED_66MHz): + reg = 0x73; + reg16 |= 0x1; + break; + default: /* 33MHz PCI 2.2 */ + reg = 0x71; + break; + + } + reg16 |= 0xB << 12; + writew(reg16, ctrl->hpc_reg + NEXT_CURR_FREQ); + + mdelay(5); + + /* Reenable interrupts */ + writel(0, ctrl->hpc_reg + INT_MASK); + + pci_write_config_byte(ctrl->pci_dev, 0x41, reg); + + /* Restart state machine */ + reg = ~0xF; + pci_read_config_byte(ctrl->pci_dev, 0x43, ®); + pci_write_config_byte(ctrl->pci_dev, 0x43, reg); + + /* Only if mode change...*/ + if (((ctrl->speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) || + ((ctrl->speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz))) + set_SOGO(ctrl); + + wait_for_ctrl_irq(ctrl); + mdelay(1100); + + /* Restore LED/Slot state */ + writel(leds, ctrl->hpc_reg + LED_CONTROL); + writeb(slot_power, ctrl->hpc_reg + SLOT_ENABLE); + + set_SOGO(ctrl); + wait_for_ctrl_irq(ctrl); + + ctrl->speed = adapter_speed; + slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + + info("Successfully changed frequency/mode for adapter in slot %d\n", + slot->number); + return 0; +} + +#endif + diff -Nru a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/cpqphp_core.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,1541 @@ +/* + * 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 + * + * Jan 12, 2003 - Added 66/100/133MHz PCI-X support, + * Torben Mathiasen + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "cpqphp.h" +#include "cpqphp_nvram.h" +#include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependent we are... */ + + +/* Global variables */ +int cpqhp_debug; +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.7" +#define DRIVER_AUTHOR "Dan Zink , Greg Kroah-Hartman " +#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 int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); +static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); + +static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = { + .owner = THIS_MODULE, + .set_attention_status = set_attention_status, + .enable_slot = process_SI, + .disable_slot = process_SS, + .hardware_test = hardware_test, + .get_power_status = get_power_status, + .get_attention_status = get_attention_status, + .get_latch_status = get_latch_status, + .get_adapter_status = get_adapter_status, + .get_max_bus_speed = get_max_bus_speed, + .get_cur_bus_speed = get_cur_bus_speed, +}; + + +static inline int is_slot64bit (struct slot *slot) +{ + 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("%s\n", __FUNCTION__); + + tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); + + number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; + slot_device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4; + slot_number = ctrl->first_slot; + + while (number_of_slots) { + new_slot = (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 == PCI_SPEED_66MHz) + new_slot->capabilities |= PCISLOT_66_MHZ_OPERATION; + + ctrl_slot = slot_device - (readb(ctrl->hpc_reg + SLOT_MASK) >> 4); + + // Check presence + new_slot->capabilities |= ((((~tempdword) >> 23) | ((~tempdword) >> 15)) >> ctrl_slot) & 0x02; + // Check the switch state + new_slot->capabilities |= ((~tempdword & 0xFF) >> ctrl_slot) & 0x01; + // Check the slot enable + new_slot->capabilities |= ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04; + + /* register this slot with the hotplug pci core */ + new_slot->hotplug_slot->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); + kfree (new_slot->hotplug_slot->info); + kfree (new_slot->hotplug_slot->name); + kfree (new_slot->hotplug_slot); + kfree (new_slot); + 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->info); + kfree(old_slot->hotplug_slot->name); + 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_bus *bus, u8 bus_num, u8 dev_num, u8 *slot) +{ + struct irq_routing_table *PCIIRQRoutingInfoLength; + u32 work; + long len; + long loop; + + u8 tbus, tdevice, tslot, bridgeSlot; + + dbg("%s: %p, %d, %d, %p\n", __FUNCTION__, bus, bus_num, dev_num, slot); + + bridgeSlot = 0xFF; + + PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table(); + if (!PCIIRQRoutingInfoLength) + return -1; + + len = (PCIIRQRoutingInfoLength->size - + sizeof(struct irq_routing_table)) / sizeof(struct irq_info); + // Make sure I got at least one entry + if (len == 0) { + 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. + bus->number = tbus; + pci_bus_read_config_dword (bus, PCI_DEVFN(tdevice, 0), PCI_REVISION_ID, &work); + + if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { + pci_bus_read_config_dword (bus, PCI_DEVFN(tdevice, 0), PCI_PRIMARY_BUS, &work); + // See if bridge's secondary bus matches target bus. + if (((work >> 8) & 0x000000FF) == (long) bus_num) { + bridgeSlot = tslot; + } + } + } + + } + + // If we got here, we didn't find an entry in the IRQ mapping table + // for the target PCI device. If we did determine that the target + // device is on the other side of a PCI-to-PCI bridge, return the + // slot number for the bridge. + if (bridgeSlot != 0xFF) { + *slot = bridgeSlot; + 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("%s - physical_slot = %s\n", __FUNCTION__, 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("%s - physical_slot = %s\n", __FUNCTION__, 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("%s - physical_slot = %s\n", __FUNCTION__, 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("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + 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("%s - physical_slot = %s\n", __FUNCTION__, 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("%s - physical_slot = %s\n", __FUNCTION__, 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("%s - physical_slot = %s\n", __FUNCTION__, 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("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + ctrl = slot->ctrl; + if (ctrl == NULL) + return -ENODEV; + + *value = get_presence_status (ctrl, slot); + + return 0; +} + +static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + struct controller *ctrl; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + ctrl = slot->ctrl; + if (ctrl == NULL) + return -ENODEV; + + *value = ctrl->speed_capability; + + return 0; +} + +static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + struct controller *ctrl; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + ctrl = slot->ctrl; + if (ctrl == NULL) + return -ENODEV; + + *value = ctrl->speed; + + return 0; +} + +static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + u8 num_of_slots = 0; + u8 hp_slot = 0; + u8 device; + u8 rev; + u8 bus_cap; + u16 temp_word; + u16 vendor_id; + u16 subsystem_vid; + u16 subsystem_deviceid; + u32 rc; + struct controller *ctrl; + struct pci_func *func; + + // Need to read VID early b/c it's used to differentiate CPQ and INTC discovery + rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id); + if (rc || ((vendor_id != PCI_VENDOR_ID_COMPAQ) && (vendor_id != PCI_VENDOR_ID_INTEL))) { + err(msg_HPC_non_compaq_or_intel); + return -ENODEV; + } + dbg("Vendor ID: %x\n", vendor_id); + + rc = pci_read_config_byte(pdev, PCI_REVISION_ID, &rev); + dbg("revision: %d\n", rev); + if (rc || ((vendor_id == PCI_VENDOR_ID_COMPAQ) && (!rev))) { + err(msg_HPC_rev_error); + return -ENODEV; + } + + /* Check for the proper subsytem ID's + * Intel uses a different SSID programming model than Compaq. + * For Intel, each SSID bit identifies a PHP capability. + * Also Intel HPC's may have RID=0. + */ + if ((rev > 2) || (vendor_id == PCI_VENDOR_ID_INTEL)) { + // TODO: This code can be made to support non-Compaq or Intel subsystem IDs + rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vid); + if (rc) { + err("%s : pci_read_config_word failed\n", __FUNCTION__); + return rc; + } + dbg("Subsystem Vendor ID: %x\n", subsystem_vid); + if ((subsystem_vid != PCI_VENDOR_ID_COMPAQ) && (subsystem_vid != PCI_VENDOR_ID_INTEL)) { + err(msg_HPC_non_compaq_or_intel); + return -ENODEV; + } + + ctrl = (struct controller *) kmalloc(sizeof(struct controller), GFP_KERNEL); + if (!ctrl) { + err("%s : out of memory\n", __FUNCTION__); + return -ENOMEM; + } + memset(ctrl, 0, sizeof(struct controller)); + + rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsystem_deviceid); + if (rc) { + err("%s : pci_read_config_word failed\n", __FUNCTION__); + goto err_free_ctrl; + } + + info("Hot Plug Subsystem Device ID: %x\n", subsystem_deviceid); + + /* Set Vendor ID, so it can be accessed later from other functions */ + ctrl->vendor_id = vendor_id; + + switch (subsystem_vid) { + case PCI_VENDOR_ID_COMPAQ: + if (rev >= 0x13) { /* CIOBX */ + ctrl->push_flag = 1; + ctrl->slot_switch_type = 1; // Switch is present + 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 = 1; // PCI-X supported + ctrl->pcix_speed_capability = 1; + pci_read_config_byte(pdev, 0x41, &bus_cap); + if (bus_cap & 0x80) { + dbg("bus max supports 133MHz PCI-X\n"); + ctrl->speed_capability = PCI_SPEED_133MHz_PCIX; + break; + } + if (bus_cap & 0x40) { + dbg("bus max supports 100MHz PCI-X\n"); + ctrl->speed_capability = PCI_SPEED_100MHz_PCIX; + break; + } + if (bus_cap & 20) { + dbg("bus max supports 66MHz PCI-X\n"); + ctrl->speed_capability = PCI_SPEED_66MHz_PCIX; + break; + } + if (bus_cap & 10) { + dbg("bus max supports 66MHz PCI\n"); + ctrl->speed_capability = PCI_SPEED_66MHz; + break; + } + + break; + } + + switch (subsystem_deviceid) { + case PCI_SUB_HPC_ID: + /* Original 6500/7000 implementation */ + ctrl->slot_switch_type = 1; // Switch is present + ctrl->speed_capability = PCI_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 = PCI_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 = PCI_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 = PCI_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; + case PCI_SUB_HPC_ID4: + /* First PCI-X implementation, 100MHz */ + ctrl->push_flag = 1; + ctrl->slot_switch_type = 1; // Switch is present + ctrl->speed_capability = PCI_SPEED_100MHz_PCIX; + 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 = 1; // PCI-X supported + ctrl->pcix_speed_capability = 0; + break; + default: + err(msg_HPC_not_supported); + rc = -ENODEV; + goto err_free_ctrl; + } + break; + + case PCI_VENDOR_ID_INTEL: + /* Check for speed capability (0=33, 1=66) */ + if (subsystem_deviceid & 0x0001) { + ctrl->speed_capability = PCI_SPEED_66MHz; + } else { + ctrl->speed_capability = PCI_SPEED_33MHz; + } + + /* Check for push button */ + if (subsystem_deviceid & 0x0002) { + /* no push button */ + ctrl->push_button = 0; + } else { + /* push button supported */ + ctrl->push_button = 1; + } + + /* Check for slot switch type (0=mechanical, 1=not mechanical) */ + if (subsystem_deviceid & 0x0004) { + /* no switch */ + ctrl->slot_switch_type = 0; + } else { + /* switch */ + ctrl->slot_switch_type = 1; + } + + /* PHP Status (0=De-feature PHP, 1=Normal operation) */ + if (subsystem_deviceid & 0x0008) { + ctrl->defeature_PHP = 1; // PHP supported + } else { + ctrl->defeature_PHP = 0; // PHP not supported + } + + /* Alternate Base Address Register Interface (0=not supported, 1=supported) */ + if (subsystem_deviceid & 0x0010) { + ctrl->alternate_base_address = 1; // supported + } else { + ctrl->alternate_base_address = 0; // not supported + } + + /* PCI Config Space Index (0=not supported, 1=supported) */ + if (subsystem_deviceid & 0x0020) { + ctrl->pci_config_space = 1; // supported + } else { + ctrl->pci_config_space = 0; // not supported + } + + /* PCI-X support */ + if (subsystem_deviceid & 0x0080) { + /* PCI-X capable */ + ctrl->pcix_support = 1; + /* Frequency of operation in PCI-X mode */ + if (subsystem_deviceid & 0x0040) { + /* 133MHz PCI-X if bit 7 is 1 */ + ctrl->pcix_speed_capability = 1; + } else { + /* 100MHz PCI-X if bit 7 is 1 and bit 0 is 0, */ + /* 66MHz PCI-X if bit 7 is 1 and bit 0 is 1 */ + ctrl->pcix_speed_capability = 0; + } + } else { + /* Conventional PCI */ + ctrl->pcix_support = 0; + ctrl->pcix_speed_capability = 0; + } + break; + + default: + err(msg_HPC_not_supported); + rc = -ENODEV; + goto err_free_ctrl; + } + + } else { + err(msg_HPC_not_supported); + return -ENODEV; + } + + // Tell the user that we found one. + info("Initializing the PCI hot plug controller residing on PCI bus %d\n", pdev->bus->number); + + dbg ("Hotplug controller capabilities:\n"); + dbg (" speed_capability %d\n", ctrl->speed_capability); + dbg (" slot_switch_type %s\n", ctrl->slot_switch_type == 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; + pci_set_drvdata(pdev, ctrl); + + /* make our own copy of the pci bus structure, as we like tweaking it a lot */ + ctrl->pci_bus = kmalloc (sizeof (*ctrl->pci_bus), GFP_KERNEL); + if (!ctrl->pci_bus) { + err("out of memory\n"); + rc = -ENOMEM; + goto err_free_ctrl; + } + memcpy (ctrl->pci_bus, pdev->bus, sizeof (*ctrl->pci_bus)); + + ctrl->bus = pdev->bus->number; + ctrl->rev = rev; + dbg("bus device function rev: %d %d %d %d\n", ctrl->bus, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), ctrl->rev); + + init_MUTEX(&ctrl->crit_sect); + init_waitqueue_head(&ctrl->queue); + + /* initialize our threads if they haven't already been started up */ + rc = one_time_init(); + if (rc) { + goto err_free_bus; + } + + dbg("pdev = %p\n", pdev); + dbg("pci resource start %lx\n", pci_resource_start(pdev, 0)); + dbg("pci resource len %lx\n", pci_resource_len(pdev, 0)); + + if (!request_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0), MY_NAME)) { + err("cannot reserve MMIO region\n"); + rc = -ENOMEM; + goto err_free_bus; + } + + ctrl->hpc_reg = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); + if (!ctrl->hpc_reg) { + err("cannot remap MMIO region %lx @ %lx\n", pci_resource_len(pdev, 0), pci_resource_start(pdev, 0)); + rc = -ENODEV; + goto err_free_mem_region; + } + + // Check for 66Mhz operation + ctrl->speed = get_controller_speed(ctrl); + + + //************************************************** + // + // Save configuration headers for this and + // subordinate PCI buses + // + //************************************************** + + // find the physical slot number of the first hot plug slot + + // Get slot won't work for devices behind bridges, but + // in this case it will always be called for the "base" + // bus/dev/func of a slot. + // CS: this is leveraging the PCIIRQ routing code from the kernel (pci-pc.c: get_irq_routing_table) + rc = get_slot_mapping(ctrl->pci_bus, pdev->bus->number, (readb(ctrl->hpc_reg + SLOT_MASK) >> 4), &(ctrl->first_slot)); + dbg("get_slot_mapping: first_slot = %d, returned = %d\n", ctrl->first_slot, rc); + if (rc) { + err(msg_initialization_err, rc); + goto err_iounmap; + } + + // Store PCI Config Space for all devices on this bus + rc = cpqhp_save_config(ctrl, ctrl->bus, readb(ctrl->hpc_reg + SLOT_MASK)); + if (rc) { + err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc); + goto err_iounmap; + } + + /* + * Get IO, memory, and IRQ resources for new devices + */ + // The next line is required for cpqhp_find_available_resources + ctrl->interrupt = pdev->irq; + + ctrl->cfgspc_irq = 0; + pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &ctrl->cfgspc_irq); + + rc = cpqhp_find_available_resources(ctrl, cpqhp_rom_start); + ctrl->add_support = !rc; + if (rc) { + dbg("cpqhp_find_available_resources = 0x%x\n", rc); + err("unable to locate PCI configuration resources for hot plug add.\n"); + goto err_iounmap; + } + + /* + * Finish setting up the hot plug ctrl device + */ + ctrl->slot_device_offset = readb(ctrl->hpc_reg + SLOT_MASK) >> 4; + dbg("NumSlots %d \n", ctrl->slot_device_offset); + + ctrl->next_event = 0; + + /* Setup the slot information structures */ + rc = ctrl_slot_setup(ctrl, smbios_start, smbios_table); + if (rc) { + err(msg_initialization_err, 6); + err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc); + goto err_iounmap; + } + + /* Mask all general input interrupts */ + writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_MASK); + + /* set up the interrupt */ + dbg("HPC interrupt = %d \n", ctrl->interrupt); + if (request_irq(ctrl->interrupt, cpqhp_ctrl_intr, + SA_SHIRQ, MY_NAME, ctrl)) { + err("Can't get irq %d for the hotplug pci controller\n", ctrl->interrupt); + rc = -ENODEV; + goto err_iounmap; + } + + /* Enable Shift Out interrupt and clear it, also enable SERR on power fault */ + temp_word = readw(ctrl->hpc_reg + MISC); + temp_word |= 0x4006; + writew(temp_word, ctrl->hpc_reg + MISC); + + // Changed 05/05/97 to clear all interrupts at start + writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_INPUT_CLEAR); + + ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); + + writel(0x0L, ctrl->hpc_reg + INT_MASK); + + if (!cpqhp_ctrl_list) { + cpqhp_ctrl_list = ctrl; + ctrl->next = NULL; + } else { + ctrl->next = cpqhp_ctrl_list; + cpqhp_ctrl_list = ctrl; + } + + // turn off empty slots here unless command line option "ON" set + // Wait for exclusive access to hardware + down(&ctrl->crit_sect); + + num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; + + // find first device number for the ctrl + device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4; + + while (num_of_slots) { + dbg("num_of_slots: %d\n", num_of_slots); + func = cpqhp_slot_find(ctrl->bus, device, 0); + if (!func) + break; + + hp_slot = func->device - ctrl->slot_device_offset; + dbg("hp_slot: %d\n", hp_slot); + + // We have to save the presence info for these slots + temp_word = ctrl->ctrl_int_comp >> 16; + func->presence_save = (temp_word >> hp_slot) & 0x01; + func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; + + if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { + func->switch_save = 0; + } else { + func->switch_save = 0x10; + } + + if (!power_mode) { + if (!func->is_a_board) { + green_LED_off (ctrl, hp_slot); + slot_disable (ctrl, hp_slot); + } + } + + device++; + num_of_slots--; + } + + if (!power_mode) { + set_SOGO(ctrl); + // Wait for SOBS to be unset + wait_for_ctrl_irq (ctrl); + } + + rc = init_SERR(ctrl); + if (rc) { + err("init_SERR failed\n"); + up(&ctrl->crit_sect); + goto err_free_irq; + } + + // Done with exclusive hardware access + up(&ctrl->crit_sect); + + cpqhp_create_ctrl_files (ctrl); + + return 0; + +err_free_irq: + free_irq(ctrl->interrupt, ctrl); +err_iounmap: + iounmap(ctrl->hpc_reg); +err_free_mem_region: + release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); +err_free_bus: + kfree(ctrl->pci_bus); +err_free_ctrl: + kfree(ctrl); + return rc; +} + + +static int one_time_init(void) +{ + int loop; + int retval = 0; + static int initialized = 0; + + if (initialized) + return 0; + + power_mode = 0; + + retval = pci_print_IRQ_route(); + if (retval) + goto error; + + dbg("Initialize + Start the notification mechanism \n"); + + retval = cpqhp_event_start_thread(); + if (retval) + goto error; + + dbg("Initialize slot lists\n"); + for (loop = 0; loop < 256; loop++) { + cpqhp_slot_list[loop] = NULL; + } + + // FIXME: We also need to hook the NMI handler eventually. + // this also needs to be worked with Christoph + // register_NMI_handler(); + + // Map rom address + cpqhp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN); + if (!cpqhp_rom_start) { + err ("Could not ioremap memory region for ROM\n"); + retval = -EIO;; + goto error; + } + + /* Now, map the int15 entry point if we are on compaq specific hardware */ + compaq_nvram_init(cpqhp_rom_start); + + /* Map smbios table entry point structure */ + smbios_table = detect_SMBIOS_pointer(cpqhp_rom_start, cpqhp_rom_start + ROM_PHY_LEN); + if (!smbios_table) { + err ("Could not find the SMBIOS pointer in memory\n"); + retval = -EIO;; + goto error; + } + + 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; + } + + 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) { + if (ctrl->hpc_reg) { + u16 misc; + rc = read_slot_enable (ctrl); + + writeb(0, ctrl->hpc_reg + SLOT_SERR); + writel(0xFFFFFFC0L | ~rc, ctrl->hpc_reg + INT_MASK); + + misc = readw(ctrl->hpc_reg + MISC); + misc &= 0xFFFD; + writew(misc, ctrl->hpc_reg + MISC); + } + + ctrl_slot_cleanup(ctrl); + + res = ctrl->io_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = ctrl->mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = ctrl->p_mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = ctrl->bus_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + kfree (ctrl->pci_bus); + + tctrl = ctrl; + ctrl = ctrl->next; + kfree(tctrl); + } + + for (loop = 0; loop < 256; loop++) { + next = cpqhp_slot_list[loop]; + while (next != NULL) { + res = next->io_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = next->mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = next->p_mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = next->bus_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + TempSlot = next; + next = next->next; + kfree(TempSlot); + } + } + + // Stop the notification mechanism + cpqhp_event_stop_thread(); + + //unmap the rom address + if (cpqhp_rom_start) + iounmap(cpqhp_rom_start); + if (smbios_start) + iounmap(smbios_start); +} + + + +static struct pci_device_id hpcd_pci_tbl[] __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("unload_cpqphpd()\n"); + unload_cpqphpd(); + + dbg("pci_unregister_driver\n"); + pci_unregister_driver(&cpqhpc_driver); +} + + +module_init(cpqhpc_init); +module_exit(cpqhpc_cleanup); + + diff -Nru a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/cpqphp_ctrl.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,3084 @@ +/* + * 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} + + +/* + * cpqhp_find_slot + */ +struct slot *cpqhp_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 = cpqhp_find_slot(ctrl, hp_slot + (readb(ctrl->hpc_reg + SLOT_MASK) >> 4)); + if (!p_slot) + return 0; + + // If the switch closed, must be a button + // If not in button mode, nevermind + if (func->switch_save && (ctrl->push_button == 1)) { + temp_word = ctrl->ctrl_int_comp >> 16; + temp_byte = (temp_word >> hp_slot) & 0x01; + temp_byte |= (temp_word >> (hp_slot + 7)) & 0x02; + + if (temp_byte != func->presence_save) { + //********************************* + // button Pressed (doesn't do anything) + //********************************* + dbg("hp_slot %d button pressed\n", hp_slot); + taskInfo->event_type = INT_BUTTON_PRESS; + } else { + //********************************* + // button Released - TAKE ACTION!!!! + //********************************* + dbg("hp_slot %d button released\n", hp_slot); + taskInfo->event_type = INT_BUTTON_RELEASE; + + // Cancel if we are still blinking + if ((p_slot->state == BLINKINGON_STATE) + || (p_slot->state == BLINKINGOFF_STATE)) { + taskInfo->event_type = INT_BUTTON_CANCEL; + dbg("hp_slot %d button cancel\n", hp_slot); + } else if ((p_slot->state == POWERON_STATE) + || (p_slot->state == POWEROFF_STATE)) { + //info(msg_button_ignore, p_slot->number); + taskInfo->event_type = INT_BUTTON_IGNORE; + dbg("hp_slot %d button ignore\n", hp_slot); + } + } + } else { + // Switch is open, assume a presence change + // Save the presence state + temp_word = ctrl->ctrl_int_comp >> 16; + func->presence_save = (temp_word >> hp_slot) & 0x01; + func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; + + if ((!(ctrl->ctrl_int_comp & (0x010000 << hp_slot))) || + (!(ctrl->ctrl_int_comp & (0x01000000 << hp_slot)))) { + //********************************* + // Present + //********************************* + taskInfo->event_type = INT_PRESENCE_ON; + } else { + //********************************* + // Not Present + //********************************* + taskInfo->event_type = INT_PRESENCE_OFF; + } + } + } + } + + return rc; +} + + +static u8 handle_power_fault(u8 change, struct controller * ctrl) +{ + int hp_slot; + u8 rc = 0; + struct pci_func *func; + struct event_info *taskInfo; + + if (!change) + return 0; + + //********************************* + // power fault + //********************************* + + info("power fault interrupt\n"); + + for (hp_slot = 0; hp_slot < 6; hp_slot++) { + if (change & (0x01 << hp_slot)) { + //********************************* + // this one changed. + //********************************* + func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0); + + taskInfo = &(ctrl->event_queue[ctrl->next_event]); + ctrl->next_event = (ctrl->next_event + 1) % 10; + taskInfo->hp_slot = hp_slot; + + rc++; + + if (ctrl->ctrl_int_comp & (0x00000100 << hp_slot)) { + //********************************* + // power fault Cleared + //********************************* + func->status = 0x00; + + taskInfo->event_type = INT_POWER_FAULT_CLEAR; + } else { + //********************************* + // power fault + //********************************* + taskInfo->event_type = INT_POWER_FAULT; + + if (ctrl->rev < 4) { + amber_LED_on (ctrl, hp_slot); + green_LED_off (ctrl, hp_slot); + set_SOGO (ctrl); + + // this is a fatal condition, we want to crash the + // machine to protect from data corruption + // simulated_NMI shouldn't ever return + //FIXME + //simulated_NMI(hp_slot, ctrl); + + //The following code causes a software crash just in + //case simulated_NMI did return + //FIXME + //panic(msg_power_fault); + } else { + // set power fault status for this board + func->status = 0xFF; + info("power fault bit %x set\n", hp_slot); + } + } + } + } + + return rc; +} + + +/* + * sort_by_size + * + * 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("%s: req_size =%x node=%p, base=%x, length=%x\n", + __FUNCTION__, size, node, node->base, node->length); + if (node->length < size) + continue; + + if (node->base & (size - 1)) { + dbg("%s: not aligned\n", __FUNCTION__); + // this one isn't base aligned properly + // so we'll make a new entry and split it up + temp_dword = (node->base | (size-1)) + 1; + + // Short circuit if adjusted size is too small + if ((node->length - (temp_dword - node->base)) < size) + continue; + + split_node = (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("%s: too big\n", __FUNCTION__); + // this one is longer than we need + // so we'll make a new entry and split it up + split_node = (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("%s: got one!!!\n", __FUNCTION__); + // If we got here, then it is the right size + // Now take it out of the list + if (*head == node) { + *head = node->next; + } else { + prevnode = *head; + while (prevnode->next != node) + prevnode = prevnode->next; + + prevnode->next = node->next; + } + node->next = NULL; + // Stop looping + break; + } + return(node); +} + + +/* + * 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("%s: head = %p, *head = %p\n", __FUNCTION__, head, *head); + + if (!(*head)) + return(1); + + dbg("*head->next = %p\n",(*head)->next); + + if (!(*head)->next) + return(0); /* only one item on the list, already sorted! */ + + dbg("*head->base = 0x%x\n",(*head)->base); + dbg("*head->next->base = 0x%x\n",(*head)->next->base); + while (out_of_order) { + out_of_order = 0; + + // Special case for swapping list head + if (((*head)->next) && + ((*head)->base > (*head)->next->base)) { + node1 = *head; + (*head) = (*head)->next; + node1->next = (*head)->next; + (*head)->next = node1; + out_of_order++; + } + + node1 = (*head); + + while (node1->next && node1->next->next) { + if (node1->next->base > node1->next->next->base) { + out_of_order++; + node2 = node1->next; + node1->next = node1->next->next; + node1 = node1->next; + node2->next = node1->next; + node1->next = node2; + } else + node1 = node1->next; + } + } // End of out_of_order loop + + node1 = *head; + + while (node1 && node1->next) { + if ((node1->base + node1->length) == node1->next->base) { + // Combine + dbg("8..\n"); + node1->length += node1->next->length; + node2 = node1->next; + node1->next = node1->next->next; + kfree(node2); + } else + node1 = node1->next; + } + + return(0); +} + + +irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data, struct pt_regs *regs) +{ + struct controller *ctrl = data; + u8 schedule_flag = 0; + u8 reset; + u16 misc; + u32 Diff; + u32 temp_dword; + + + misc = readw(ctrl->hpc_reg + MISC); + //********************************* + // Check to see if it was our interrupt + //********************************* + if (!(misc & 0x000C)) { + return IRQ_NONE; + } + + if (misc & 0x0004) { + //********************************* + // Serial Output interrupt Pending + //********************************* + + // Clear the interrupt + misc |= 0x0004; + writew(misc, ctrl->hpc_reg + MISC); + + // Read to clear posted writes + misc = readw(ctrl->hpc_reg + MISC); + + dbg ("%s - waking up\n", __FUNCTION__); + wake_up_interruptible(&ctrl->queue); + } + + if (misc & 0x0008) { + // General-interrupt-input interrupt Pending + Diff = readl(ctrl->hpc_reg + INT_INPUT_CLEAR) ^ ctrl->ctrl_int_comp; + + ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); + + // Clear the interrupt + writel(Diff, ctrl->hpc_reg + INT_INPUT_CLEAR); + + // Read it back to clear any posted writes + temp_dword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); + + if (!Diff) { + // Clear all interrupts + writel(0xFFFFFFFF, ctrl->hpc_reg + INT_INPUT_CLEAR); + } + + schedule_flag += handle_switch_change((u8)(Diff & 0xFFL), ctrl); + schedule_flag += handle_presence_change((u16)((Diff & 0xFFFF0000L) >> 16), ctrl); + schedule_flag += handle_power_fault((u8)((Diff & 0xFF00L) >> 8), ctrl); + } + + reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE); + if (reset & 0x40) { + /* Bus reset has completed */ + reset &= 0xCF; + writeb(reset, ctrl->hpc_reg + RESET_FREQ_MODE); + reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE); + wake_up_interruptible(&ctrl->queue); + } + + if (schedule_flag) { + up(&event_semaphore); + dbg("Signal event_semaphore\n"); + } + return IRQ_HANDLED; +} + + +/** + * cpqhp_slot_create - Creates a node and adds it to the proper bus. + * @busnumber - bus where new node is to be located + * + * Returns pointer to the new node or NULL if unsuccessful + */ +struct pci_func *cpqhp_slot_create(u8 busnumber) +{ + struct pci_func *new_slot; + struct pci_func *next; + + new_slot = (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; + u8 adapter_speed; + u32 index; + u32 rc = 0; + u32 src = 8; + + hp_slot = func->device - ctrl->slot_device_offset; + + if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot)) { + //********************************* + // The switch is open. + //********************************* + rc = INTERLOCK_OPEN; + } else if (is_slot_enabled (ctrl, hp_slot)) { + //********************************* + // The board is already on + //********************************* + rc = CARD_FUNCTIONING; + } else { + // 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); + + adapter_speed = get_adapter_speed(ctrl, hp_slot); + if (ctrl->speed != adapter_speed) + if (set_controller_speed(ctrl, adapter_speed, hp_slot)) + rc = WRONG_BUS_FREQUENCY; + + // turn off board without attaching to the bus + disable_slot_power (ctrl, hp_slot); + + set_SOGO(ctrl); + + // Wait for SOBS to be unset + wait_for_ctrl_irq (ctrl); + + // 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; + u8 adapter_speed; + int index; + u32 temp_register = 0xFFFFFFFF; + u32 rc = 0; + struct pci_func *new_slot = NULL; + struct slot *p_slot; + struct resource_lists res_lists; + + hp_slot = func->device - ctrl->slot_device_offset; + dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", + __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot); + + // 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); + + adapter_speed = get_adapter_speed(ctrl, hp_slot); + if (ctrl->speed != adapter_speed) + if (set_controller_speed(ctrl, adapter_speed, hp_slot)) + rc = WRONG_BUS_FREQUENCY; + + // turn off board without attaching to the bus + disable_slot_power (ctrl, hp_slot); + + set_SOGO(ctrl); + + // Wait for SOBS to be unset + wait_for_ctrl_irq (ctrl); + + // Done with exclusive hardware access + up(&ctrl->crit_sect); + + if (rc) + return(rc); + + p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + + // turn on board and blink green LED + + // Wait for exclusive access to hardware + dbg("%s: before down\n", __FUNCTION__); + down(&ctrl->crit_sect); + dbg("%s: after down\n", __FUNCTION__); + + dbg("%s: before slot_enable\n", __FUNCTION__); + slot_enable (ctrl, hp_slot); + + dbg("%s: before green_LED_blink\n", __FUNCTION__); + green_LED_blink (ctrl, hp_slot); + + dbg("%s: before amber_LED_blink\n", __FUNCTION__); + amber_LED_off (ctrl, hp_slot); + + dbg("%s: before set_SOGO\n", __FUNCTION__); + set_SOGO(ctrl); + + // Wait for SOBS to be unset + dbg("%s: before wait_for_ctrl_irq\n", __FUNCTION__); + wait_for_ctrl_irq (ctrl); + dbg("%s: after wait_for_ctrl_irq\n", __FUNCTION__); + + // Done with exclusive hardware access + dbg("%s: before up\n", __FUNCTION__); + up(&ctrl->crit_sect); + dbg("%s: after up\n", __FUNCTION__); + + // Wait for ~1 second because of hot plug spec + dbg("%s: before long_delay\n", __FUNCTION__); + long_delay(1*HZ); + dbg("%s: after long_delay\n", __FUNCTION__); + + dbg("%s: func status = %x\n", __FUNCTION__, func->status); + // Check for a power fault + if (func->status == 0xFF) { + // power fault occurred, but it was benign + temp_register = 0xFFFFFFFF; + dbg("%s: temp register set to %x by power fault\n", __FUNCTION__, temp_register); + rc = POWER_FAILURE; + func->status = 0; + } else { + // Get vendor/device ID u32 + ctrl->pci_bus->number = func->bus; + rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register); + dbg("%s: pci_read_config_dword returns %d\n", __FUNCTION__, rc); + dbg("%s: temp_register is %x\n", __FUNCTION__, temp_register); + + if (rc != 0) { + // Something's wrong here + temp_register = 0xFFFFFFFF; + dbg("%s: temp register set to %x by error\n", __FUNCTION__, temp_register); + } + // Preset return code. It will be changed later if things go okay. + rc = NO_ADAPTER_PRESENT; + } + + // All F's is an empty slot or an invalid board + if (temp_register != 0xFFFFFFFF) { // Check for a board in the slot + res_lists.io_head = ctrl->io_head; + res_lists.mem_head = ctrl->mem_head; + res_lists.p_mem_head = ctrl->p_mem_head; + res_lists.bus_head = ctrl->bus_head; + res_lists.irqs = NULL; + + rc = configure_new_device(ctrl, func, 0, &res_lists); + + dbg("%s: back from configure_new_device\n", __FUNCTION__); + ctrl->io_head = res_lists.io_head; + ctrl->mem_head = res_lists.mem_head; + ctrl->p_mem_head = res_lists.p_mem_head; + ctrl->bus_head = res_lists.bus_head; + + cpqhp_resource_sort_and_combine(&(ctrl->mem_head)); + cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head)); + cpqhp_resource_sort_and_combine(&(ctrl->io_head)); + cpqhp_resource_sort_and_combine(&(ctrl->bus_head)); + + if (rc) { + // 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("%s: configure linux pci_dev structure\n", __FUNCTION__); + index = 0; + do { + new_slot = cpqhp_slot_find(ctrl->bus, func->device, index++); + if (new_slot && !new_slot->pci_dev) { + cpqhp_configure_device(ctrl, new_slot); + } + } while (new_slot); + + // 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 %s, hp_slot = %d\n", __FUNCTION__, hp_slot); + + // When we get here, it is safe to change base Address Registers. + // We will attempt to save the base Address Register Lengths + if (replace_flag || !ctrl->add_support) + rc = cpqhp_save_base_addr_length(ctrl, func); + else if (!func->bus_head && !func->mem_head && + !func->p_mem_head && !func->io_head) { + // Here we check to see if we've saved any of the board's + // resources already. If so, we'll skip the attempt to + // determine what's being used. + index = 0; + temp_func = cpqhp_slot_find(func->bus, func->device, index++); + while (temp_func) { + if (temp_func->bus_head || temp_func->mem_head + || temp_func->p_mem_head || temp_func->io_head) { + skip = 1; + break; + } + temp_func = cpqhp_slot_find(temp_func->bus, temp_func->device, index++); + } + + if (!skip) + rc = cpqhp_save_used_resources(ctrl, func); + } + // Change status to shutdown + if (func->is_a_board) + func->status = 0x01; + func->configured = 0; + + // 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("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; + int result; + + info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->power_status = get_slot_enabled(ctrl, slot); + info->attention_status = cpq_get_attention_status(ctrl, slot); + info->latch_status = cpq_get_latch_status(ctrl, slot); + info->adapter_status = get_presence_status(ctrl, slot); + result = pci_hp_change_slot_info(slot->hotplug_slot, info); + kfree (info); + return result; +} + +static void interrupt_event_handler(struct controller *ctrl) +{ + int loop = 0; + int change = 1; + struct pci_func *func; + u8 hp_slot; + struct slot *p_slot; + + while (change) { + change = 0; + + for (loop = 0; loop < 10; loop++) { + //dbg("loop %d\n", loop); + if (ctrl->event_queue[loop].event_type != 0) { + hp_slot = ctrl->event_queue[loop].hp_slot; + + func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0); + if (!func) + return; + + p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + if (!p_slot) + return; + + dbg("hp_slot %d, func %p, p_slot %p\n", + hp_slot, func, p_slot); + + if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) { + dbg("button pressed\n"); + } else if (ctrl->event_queue[loop].event_type == + INT_BUTTON_CANCEL) { + dbg("button cancel\n"); + del_timer(&p_slot->task_event); + + // 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 %s\n", __FUNCTION__); + return ; + } + + if (func != NULL && ctrl != NULL) { + if (cpqhp_process_SS(ctrl, func) != 0) { + amber_LED_on (ctrl, hp_slot); + green_LED_on (ctrl, hp_slot); + + set_SOGO(ctrl); + + // Wait for SOBS to be unset + wait_for_ctrl_irq (ctrl); + } + } + + p_slot->state = STATIC_STATE; + } else { + p_slot->state = POWERON_STATE; + // slot is off + + func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0); + dbg("In add_board, func = %p, ctrl = %p\n", func, ctrl); + if (!func) { + dbg("Error! func NULL in %s\n", __FUNCTION__); + return ; + } + + if (func != NULL && ctrl != NULL) { + if (cpqhp_process_SI(ctrl, func) != 0) { + amber_LED_on (ctrl, hp_slot); + green_LED_off (ctrl, hp_slot); + + set_SOGO(ctrl); + + // Wait for SOBS to be unset + wait_for_ctrl_irq (ctrl); + } + } + + p_slot->state = STATIC_STATE; + } + + return; +} + + +int cpqhp_process_SI (struct controller *ctrl, struct pci_func *func) +{ + u8 device, hp_slot; + u16 temp_word; + u32 tempdword; + int rc; + struct slot* p_slot; + int physical_slot = 0; + + if (!ctrl) + return(1); + + tempdword = 0; + + device = func->device; + hp_slot = device - ctrl->slot_device_offset; + p_slot = cpqhp_find_slot(ctrl, device); + if (p_slot) { + physical_slot = p_slot->number; + } + + // Check to see if the interlock is closed + tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); + + if (tempdword & (0x01 << hp_slot)) { + return(1); + } + + if (func->is_a_board) { + rc = board_replaced(func, ctrl); + } else { + // add board + slot_remove(func); + + func = cpqhp_slot_create(ctrl->bus); + if (func == NULL) { + return(1); + } + + func->bus = ctrl->bus; + func->device = device; + func->function = 0; + func->configured = 0; + func->is_a_board = 1; + + // We have to save the presence info for these slots + temp_word = ctrl->ctrl_int_comp >> 16; + func->presence_save = (temp_word >> hp_slot) & 0x01; + func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; + + if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { + func->switch_save = 0; + } else { + func->switch_save = 0x10; + } + + rc = board_added(func, ctrl); + if (rc) { + if (is_bridge(func)) { + bridge_slot_remove(func); + } else + slot_remove(func); + + // Setup slot structure with entry for empty slot + func = cpqhp_slot_create(ctrl->bus); + + if (func == NULL) { + // 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("%s: rc = %d\n", __FUNCTION__, rc); + } + + if (p_slot) + update_slot_info(ctrl, p_slot); + + return rc; +} + + +int cpqhp_process_SS (struct controller *ctrl, struct pci_func *func) +{ + u8 device, class_code, header_type, BCR; + u8 index = 0; + u8 replace_flag; + u32 rc = 0; + unsigned int devfn; + struct slot* p_slot; + struct pci_bus *pci_bus = ctrl->pci_bus; + int physical_slot=0; + + device = func->device; + func = cpqhp_slot_find(ctrl->bus, device, index++); + p_slot = cpqhp_find_slot(ctrl, device); + if (p_slot) { + physical_slot = p_slot->number; + } + + // Make sure there are no video controllers here + while (func && !rc) { + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + // Check the Class Code + rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); + if (rc) + return rc; + + if (class_code == PCI_BASE_CLASS_DISPLAY) { + /* Display/Video adapter (not supported) */ + rc = REMOVE_NOT_SUPPORTED; + } else { + // See if it's a bridge + rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + if (rc) + return rc; + + // If it's a bridge, check the VGA Enable bit + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { + rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR); + if (rc) + return rc; + + // If the VGA Enable bit is set, remove isn't supported + if (BCR & PCI_BRIDGE_CTL_VGA) { + rc = REMOVE_NOT_SUPPORTED; + } + } + } + + func = cpqhp_slot_find(ctrl->bus, device, index++); + } + + func = cpqhp_slot_find(ctrl->bus, device, 0); + if ((func != NULL) && !rc) { + //FIXME: Replace flag should be passed into process_SS + replace_flag = !(ctrl->add_support); + rc = remove_board(func, replace_flag, ctrl); + } else if (!rc) { + rc = 1; + } + + if (p_slot) + update_slot_info(ctrl, p_slot); + + return(rc); +} + + + +/** + * 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("%s\n", __FUNCTION__); + // Check for Multi-function device + ctrl->pci_bus->number = func->bus; + rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte); + if (rc) { + dbg("%s: rc = %d\n", __FUNCTION__, rc); + return rc; + } + + if (temp_byte & 0x80) // Multi-function device + max_functions = 8; + else + max_functions = 1; + + function = 0; + + do { + rc = configure_new_function(ctrl, new_slot, behind_bridge, resources); + + if (rc) { + dbg("configure_new_function failed %d\n",rc); + index = 0; + + while (new_slot) { + new_slot = cpqhp_slot_find(new_slot->bus, new_slot->device, index++); + + if (new_slot) + cpqhp_return_board_resources(new_slot, resources); + } + + return(rc); + } + + function++; + + stop_it = 0; + + // The following loop skips to the next present function + // and creates a board structure + + while ((function < max_functions) && (!stop_it)) { + pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID); + + if (ID == 0xFFFFFFFF) { // There's nothing there. + function++; + } else { // There's something there + // Setup slot structure. + new_slot = cpqhp_slot_create(func->bus); + + if (new_slot == NULL) { + // 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; + unsigned int devfn; + struct pci_resource *mem_node; + struct pci_resource *p_mem_node; + struct pci_resource *io_node; + struct pci_resource *bus_node; + struct pci_resource *hold_mem_node; + struct pci_resource *hold_p_mem_node; + struct pci_resource *hold_IO_node; + struct pci_resource *hold_bus_node; + struct irq_mapping irqs; + struct pci_func *new_slot; + struct pci_bus *pci_bus; + struct resource_lists temp_resources; + + pci_bus = ctrl->pci_bus; + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + // Check for Bridge + rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte); + if (rc) + return rc; + + if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge + // set Primary bus + dbg("set Primary bus = %d\n", func->bus); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_PRIMARY_BUS, func->bus); + if (rc) + return rc; + + // find range of busses to use + dbg("find ranges of buses to use\n"); + bus_node = get_max_resource(&resources->bus_head, 1); + + // If we don't have any busses to allocate, we can't continue + if (!bus_node) + return -ENOMEM; + + // set Secondary bus + temp_byte = bus_node->base; + dbg("set Secondary bus = %d\n", bus_node->base); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte); + if (rc) + return rc; + + // set subordinate bus + temp_byte = bus_node->base + bus_node->length - 1; + dbg("set subordinate bus = %d\n", bus_node->base + bus_node->length - 1); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); + if (rc) + return rc; + + // set subordinate Latency Timer and base Latency Timer + temp_byte = 0x40; + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte); + if (rc) + return rc; + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); + if (rc) + return rc; + + // set Cache Line size + temp_byte = 0x08; + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); + if (rc) + return rc; + + // Setup the IO, memory, and prefetchable windows + + io_node = get_max_resource(&(resources->io_head), 0x1000); + if (!io_node) + return -ENOMEM; + mem_node = get_max_resource(&(resources->mem_head), 0x100000); + if (!mem_node) + return -ENOMEM; + p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000); + if (!p_mem_node) + return -ENOMEM; + dbg("Setup the IO, memory, and prefetchable windows\n"); + dbg("io_node\n"); + dbg("(base, len, next) (%x, %x, %p)\n", io_node->base, io_node->length, io_node->next); + dbg("mem_node\n"); + dbg("(base, len, next) (%x, %x, %p)\n", mem_node->base, mem_node->length, mem_node->next); + dbg("p_mem_node\n"); + dbg("(base, len, next) (%x, %x, %p)\n", p_mem_node->base, p_mem_node->length, p_mem_node->next); + + // set up the IRQ info + if (!resources->irqs) { + irqs.barber_pole = 0; + irqs.interrupt[0] = 0; + irqs.interrupt[1] = 0; + irqs.interrupt[2] = 0; + irqs.interrupt[3] = 0; + irqs.valid_INT = 0; + } else { + irqs.barber_pole = resources->irqs->barber_pole; + irqs.interrupt[0] = resources->irqs->interrupt[0]; + irqs.interrupt[1] = resources->irqs->interrupt[1]; + irqs.interrupt[2] = resources->irqs->interrupt[2]; + irqs.interrupt[3] = resources->irqs->interrupt[3]; + irqs.valid_INT = resources->irqs->valid_INT; + } + + // set up resource lists that are now aligned on top and bottom + // for anything behind the bridge. + temp_resources.bus_head = bus_node; + temp_resources.io_head = io_node; + temp_resources.mem_head = mem_node; + temp_resources.p_mem_head = p_mem_node; + temp_resources.irqs = &irqs; + + // Make copies of the nodes we are going to pass down so that + // if there is a problem,we can just use these to free resources + hold_bus_node = (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_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte); + + temp_byte = (io_node->base + io_node->length - 1) >> 8; + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte); + } else { + kfree(hold_IO_node); + hold_IO_node = NULL; + } + + // If we have memory resources copy them and fill in the bridge's + // memory range registers. Otherwise, fill in the range + // registers with values that disable them. + if (mem_node) { + memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource)); + mem_node->next = NULL; + + // set Mem base and Limit registers + temp_word = mem_node->base >> 16; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + + temp_word = (mem_node->base + mem_node->length - 1) >> 16; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + } else { + temp_word = 0xFFFF; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + + temp_word = 0x0000; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + + kfree(hold_mem_node); + hold_mem_node = NULL; + } + + // If we have prefetchable memory resources copy them and + // fill in the bridge's memory range registers. Otherwise, + // fill in the range registers with values that disable them. + if (p_mem_node) { + memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource)); + p_mem_node->next = NULL; + + // set Pre Mem base and Limit registers + temp_word = p_mem_node->base >> 16; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); + + temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + } else { + temp_word = 0xFFFF; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); + + temp_word = 0x0000; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + + kfree(hold_p_mem_node); + hold_p_mem_node = NULL; + } + + // Adjust this to compensate for extra adjustment in first loop + irqs.barber_pole--; + + rc = 0; + + // Here we actually find the devices and configure them + for (device = 0; (device <= 0x1F) && !rc; device++) { + irqs.barber_pole = (irqs.barber_pole + 1) & 0x03; + + ID = 0xFFFFFFFF; + pci_bus->number = hold_bus_node->base; + pci_bus_read_config_dword (pci_bus, PCI_DEVFN(device, 0), 0x00, &ID); + pci_bus->number = func->bus; + + if (ID != 0xFFFFFFFF) { // device Present + // Setup slot structure. + new_slot = cpqhp_slot_create(hold_bus_node->base); + + if (new_slot == NULL) { + // 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_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); + + if (temp_resources.bus_head->length == 0) { + kfree(temp_resources.bus_head); + temp_resources.bus_head = NULL; + } else { + return_resource(&(resources->bus_head), temp_resources.bus_head); + } + } + + // If we have IO space available and there is some left, + // return the unused portion + if (hold_IO_node && temp_resources.io_head) { + io_node = do_pre_bridge_resource_split(&(temp_resources.io_head), + &hold_IO_node, 0x1000); + + // Check if we were able to split something off + if (io_node) { + hold_IO_node->base = io_node->base + io_node->length; + + temp_byte = (hold_IO_node->base) >> 8; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_IO_BASE, temp_byte); + + return_resource(&(resources->io_head), io_node); + } + + io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000); + + // Check if we were able to split something off + if (io_node) { + // First use the temporary node to store information for the board + hold_IO_node->length = io_node->base - hold_IO_node->base; + + // If we used any, add it to the board's list + if (hold_IO_node->length) { + hold_IO_node->next = func->io_head; + func->io_head = hold_IO_node; + + temp_byte = (io_node->base - 1) >> 8; + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte); + + return_resource(&(resources->io_head), io_node); + } else { + // it doesn't need any IO + temp_word = 0x0000; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_IO_LIMIT, temp_word); + + return_resource(&(resources->io_head), io_node); + kfree(hold_IO_node); + } + } else { + // it used most of the range + hold_IO_node->next = func->io_head; + func->io_head = hold_IO_node; + } + } else if (hold_IO_node) { + // it used the whole range + hold_IO_node->next = func->io_head; + func->io_head = hold_IO_node; + } + // If we have memory space available and there is some left, + // return the unused portion + if (hold_mem_node && temp_resources.mem_head) { + mem_node = do_pre_bridge_resource_split(&(temp_resources. mem_head), + &hold_mem_node, 0x100000); + + // Check if we were able to split something off + if (mem_node) { + hold_mem_node->base = mem_node->base + mem_node->length; + + temp_word = (hold_mem_node->base) >> 16; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + + return_resource(&(resources->mem_head), mem_node); + } + + mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000); + + // Check if we were able to split something off + if (mem_node) { + // First use the temporary node to store information for the board + hold_mem_node->length = mem_node->base - hold_mem_node->base; + + if (hold_mem_node->length) { + hold_mem_node->next = func->mem_head; + func->mem_head = hold_mem_node; + + // configure end address + temp_word = (mem_node->base - 1) >> 16; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + + // Return unused resources to the pool + return_resource(&(resources->mem_head), mem_node); + } else { + // it doesn't need any Mem + temp_word = 0x0000; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + + return_resource(&(resources->mem_head), mem_node); + kfree(hold_mem_node); + } + } else { + // it used most of the range + hold_mem_node->next = func->mem_head; + func->mem_head = hold_mem_node; + } + } else if (hold_mem_node) { + // it used the whole range + hold_mem_node->next = func->mem_head; + func->mem_head = hold_mem_node; + } + // If we have prefetchable memory space available and there is some + // left at the end, return the unused portion + if (hold_p_mem_node && temp_resources.p_mem_head) { + p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head), + &hold_p_mem_node, 0x100000); + + // Check if we were able to split something off + if (p_mem_node) { + hold_p_mem_node->base = p_mem_node->base + p_mem_node->length; + + temp_word = (hold_p_mem_node->base) >> 16; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); + + return_resource(&(resources->p_mem_head), p_mem_node); + } + + p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000); + + // Check if we were able to split something off + if (p_mem_node) { + // First use the temporary node to store information for the board + hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base; + + // If we used any, add it to the board's list + if (hold_p_mem_node->length) { + hold_p_mem_node->next = func->p_mem_head; + func->p_mem_head = hold_p_mem_node; + + temp_word = (p_mem_node->base - 1) >> 16; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + + return_resource(&(resources->p_mem_head), p_mem_node); + } else { + // it doesn't need any PMem + temp_word = 0x0000; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + + return_resource(&(resources->p_mem_head), p_mem_node); + kfree(hold_p_mem_node); + } + } else { + // it used the most of the range + hold_p_mem_node->next = func->p_mem_head; + func->p_mem_head = hold_p_mem_node; + } + } else if (hold_p_mem_node) { + // it used the whole range + hold_p_mem_node->next = func->p_mem_head; + func->p_mem_head = hold_p_mem_node; + } + // We should be configuring an IRQ and the bridge's base address + // registers if it needs them. Although we have never seen such + // a device + + // enable card + command = 0x0157; // = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_COMMAND, command); + + // set Bridge Control Register + command = 0x07; // = PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_NO_ISA + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_BRIDGE_CONTROL, command); + } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) { + // Standard device + rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); + + if (class_code == PCI_BASE_CLASS_DISPLAY) { + // Display (video) adapter (not supported) + return(DEVICE_TYPE_NOT_SUPPORTED); + } + // Figure out IO and memory needs + for (cloop = 0x10; cloop <= 0x24; cloop += 4) { + temp_register = 0xFFFFFFFF; + + dbg("CND: bus=%d, devfn=%d, offset=%d\n", pci_bus->number, devfn, cloop); + rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); + + rc = pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp_register); + dbg("CND: base = 0x%x\n", temp_register); + + if (temp_register) { // If this register is implemented + if ((temp_register & 0x03L) == 0x01) { + // Map IO + + // set base = amount of IO space + base = temp_register & 0xFFFFFFFC; + base = ~base + 1; + + dbg("CND: length = 0x%x\n", base); + io_node = get_io_resource(&(resources->io_head), base); + dbg("Got io_node start = %8.8x, length = %8.8x next (%p)\n", + io_node->base, io_node->length, io_node->next); + dbg("func (%p) io_head (%p)\n", func, func->io_head); + + // allocate the resource to the board + if (io_node) { + base = io_node->base; + + io_node->next = func->io_head; + func->io_head = io_node; + } else + return -ENOMEM; + } else if ((temp_register & 0x0BL) == 0x08) { + // Map prefetchable memory + base = temp_register & 0xFFFFFFF0; + base = ~base + 1; + + dbg("CND: length = 0x%x\n", base); + p_mem_node = get_resource(&(resources->p_mem_head), base); + + // allocate the resource to the board + if (p_mem_node) { + base = p_mem_node->base; + + p_mem_node->next = func->p_mem_head; + func->p_mem_head = p_mem_node; + } else + return -ENOMEM; + } else if ((temp_register & 0x0BL) == 0x00) { + // Map memory + base = temp_register & 0xFFFFFFF0; + base = ~base + 1; + + dbg("CND: length = 0x%x\n", base); + mem_node = get_resource(&(resources->mem_head), base); + + // allocate the resource to the board + if (mem_node) { + base = mem_node->base; + + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } else + return -ENOMEM; + } else if ((temp_register & 0x0BL) == 0x04) { + // Map memory + base = temp_register & 0xFFFFFFF0; + base = ~base + 1; + + dbg("CND: length = 0x%x\n", base); + mem_node = get_resource(&(resources->mem_head), base); + + // allocate the resource to the board + if (mem_node) { + base = mem_node->base; + + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } else + return -ENOMEM; + } else if ((temp_register & 0x0BL) == 0x06) { + // Those bits are reserved, we can't handle this + return(1); + } else { + // Requesting space below 1M + return(NOT_ENOUGH_RESOURCES); + } + + rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, base); + + // Check for 64-bit base + if ((temp_register & 0x07L) == 0x04) { + cloop += 4; + + // Upper 32 bits of address always zero on today's systems + // FIXME this is probably not true on Alpha and ia64??? + base = 0; + rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, base); + } + } + } // End of base register loop + + // Figure out which interrupt pin this function uses + rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_INTERRUPT_PIN, &temp_byte); + + // If this function needs an interrupt and we are behind a bridge + // and the pin is tied to something that's alread mapped, + // set this one the same + if (temp_byte && resources->irqs && + (resources->irqs->valid_INT & + (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) { + // We have to share with something already set up + IRQ = resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03]; + } else { + // Program IRQ based on card type + rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); + + if (class_code == PCI_BASE_CLASS_STORAGE) { + IRQ = cpqhp_disk_irq; + } else { + IRQ = cpqhp_nic_irq; + } + } + + // IRQ Line + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ); + + if (!behind_bridge) { + rc = cpqhp_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ); + if (rc) + return(1); + } else { + //TBD - this code may also belong in the other clause of this If statement + resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ; + resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03; + } + + // Latency Timer + temp_byte = 0x40; + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); + + // Cache Line size + temp_byte = 0x08; + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); + + // disable ROM base Address + temp_dword = 0x00L; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_ROM_ADDRESS, temp_dword); + + // enable card + temp_word = 0x0157; // = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_COMMAND, temp_word); + } // 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 -Nru a/drivers/pci/hotplug/cpqphp_nvram.c b/drivers/pci/hotplug/cpqphp_nvram.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/cpqphp_nvram.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,667 @@ +/* + * 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 ("%s - returned %d\n", __FUNCTION__, result); + return result; +} + + +static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size) +{ + unsigned long flags; + int op = operation; + int ret_val; + + if (!compaq_int15_entry_point) + return -ENODEV; + + spin_lock_irqsave(&int15_lock, flags); + __asm__ ( + "xorl %%ebx,%%ebx\n" \ + "xorl %%edx,%%edx\n" \ + "pushf\n" \ + "push %%cs\n" \ + "cli\n" \ + "call *%6\n" + : "=c" (*buf_size), "=a" (ret_val) + : "a" (op), "c" (*buf_size), "S" (ev_name), + "D" (buffer), "m" (compaq_int15_entry_point) + : "%ebx", "%edx"); + spin_unlock_irqrestore(&int15_lock, flags); + + return((ret_val & 0xFF00) >> 8); +} + + +/* + * load_HRT + * + * Read the hot plug Resource Table from NVRAM + */ +static int load_HRT (void *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, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available); + if (rc) + return(rc); + + // The function Number + rc = add_byte( &pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available); + if (rc) + return(rc); + + // Skip the number of available entries + rc = add_dword( &pFill, 0, &usedbytes, &available); + if (rc) + return(rc); + + // Figure out memory Available + + resNode = ctrl->mem_head; + + loop = 0; + + while (resNode) { + loop ++; + + // base + rc = add_dword( &pFill, resNode->base, &usedbytes, &available); + if (rc) + return(rc); + + // length + rc = add_dword( &pFill, resNode->length, &usedbytes, &available); + if (rc) + return(rc); + + resNode = resNode->next; + } + + // Fill in the number of entries + p_ev_ctrl->mem_avail = loop; + + // Figure out prefetchable memory Available + + resNode = ctrl->p_mem_head; + + loop = 0; + + while (resNode) { + loop ++; + + // base + rc = add_dword( &pFill, resNode->base, &usedbytes, &available); + if (rc) + return(rc); + + // length + rc = add_dword( &pFill, resNode->length, &usedbytes, &available); + if (rc) + return(rc); + + resNode = resNode->next; + } + + // Fill in the number of entries + p_ev_ctrl->p_mem_avail = loop; + + // Figure out IO Available + + resNode = ctrl->io_head; + + loop = 0; + + while (resNode) { + loop ++; + + // base + rc = add_dword( &pFill, resNode->base, &usedbytes, &available); + if (rc) + return(rc); + + // length + rc = add_dword( &pFill, resNode->length, &usedbytes, &available); + if (rc) + return(rc); + + resNode = resNode->next; + } + + // Fill in the number of entries + p_ev_ctrl->io_avail = loop; + + // Figure out bus Available + + resNode = ctrl->bus_head; + + loop = 0; + + while (resNode) { + loop ++; + + // base + rc = add_dword( &pFill, resNode->base, &usedbytes, &available); + if (rc) + return(rc); + + // length + rc = add_dword( &pFill, resNode->length, &usedbytes, &available); + if (rc) + return(rc); + + resNode = resNode->next; + } + + // Fill in the number of entries + p_ev_ctrl->bus_avail = loop; + + ctrl = ctrl->next; + } + + p_EV_header->num_of_ctrl = numCtrl; + + // Now store the EV + + temp_dword = usedbytes; + + rc = access_EV(WRITE_EV, "CQTHPS", (u8*) buffer, &temp_dword); + + dbg("usedbytes = 0x%x, length = 0x%x\n", usedbytes, temp_dword); + + evbuffer_length = temp_dword; + + if (rc) { + err(msg_unable_to_save); + return(1); + } + + return(0); +} + + +void compaq_nvram_init (void *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 != PCI_SLOT(ctrl->pci_dev->devfn)) || + (function != PCI_FUNC(ctrl->pci_dev->devfn))) { + nummem = p_ev_ctrl->mem_avail; + numpmem = p_ev_ctrl->p_mem_avail; + numio = p_ev_ctrl->io_avail; + numbus = p_ev_ctrl->bus_avail; + + p_byte += 4; + + if (p_byte > ((u8*)p_EV_header + evbuffer_length)) + return 2; + + // Skip forward to the next entry + p_byte += (nummem + numpmem + numio + numbus) * 8; + + if (p_byte > ((u8*)p_EV_header + evbuffer_length)) + return 2; + + p_ev_ctrl = (struct ev_hrt_ctrl *) p_byte; + + p_byte += 3; + + if (p_byte > ((u8*)p_EV_header + evbuffer_length)) + return 2; + + bus = p_ev_ctrl->bus; + device = p_ev_ctrl->device; + function = p_ev_ctrl->function; + } + + nummem = p_ev_ctrl->mem_avail; + numpmem = p_ev_ctrl->p_mem_avail; + numio = p_ev_ctrl->io_avail; + numbus = p_ev_ctrl->bus_avail; + + p_byte += 4; + + if (p_byte > ((u8*)p_EV_header + evbuffer_length)) + return 2; + + while (nummem--) { + mem_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!mem_node) + break; + + mem_node->base = *(u32*)p_byte; + dbg("mem base = %8.8x\n",mem_node->base); + p_byte += 4; + + if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { + kfree(mem_node); + return 2; + } + + mem_node->length = *(u32*)p_byte; + dbg("mem length = %8.8x\n",mem_node->length); + p_byte += 4; + + if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { + kfree(mem_node); + return 2; + } + + mem_node->next = ctrl->mem_head; + ctrl->mem_head = mem_node; + } + + while (numpmem--) { + p_mem_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!p_mem_node) + break; + + p_mem_node->base = *(u32*)p_byte; + dbg("pre-mem base = %8.8x\n",p_mem_node->base); + p_byte += 4; + + if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { + kfree(p_mem_node); + return 2; + } + + p_mem_node->length = *(u32*)p_byte; + dbg("pre-mem length = %8.8x\n",p_mem_node->length); + p_byte += 4; + + if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { + kfree(p_mem_node); + return 2; + } + + p_mem_node->next = ctrl->p_mem_head; + ctrl->p_mem_head = p_mem_node; + } + + while (numio--) { + io_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!io_node) + break; + + io_node->base = *(u32*)p_byte; + dbg("io base = %8.8x\n",io_node->base); + p_byte += 4; + + if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { + kfree(io_node); + return 2; + } + + io_node->length = *(u32*)p_byte; + dbg("io length = %8.8x\n",io_node->length); + p_byte += 4; + + if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { + kfree(io_node); + return 2; + } + + io_node->next = ctrl->io_head; + ctrl->io_head = io_node; + } + + while (numbus--) { + bus_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!bus_node) + break; + + bus_node->base = *(u32*)p_byte; + p_byte += 4; + + if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { + kfree(bus_node); + return 2; + } + + bus_node->length = *(u32*)p_byte; + p_byte += 4; + + if (p_byte > ((u8*)p_EV_header + evbuffer_length)) { + kfree(bus_node); + return 2; + } + + bus_node->next = ctrl->bus_head; + ctrl->bus_head = bus_node; + } + + // If all of the following fail, we don't have any resources for + // hot plug add + rc = 1; + rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head)); + rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head)); + rc &= cpqhp_resource_sort_and_combine(&(ctrl->io_head)); + rc &= cpqhp_resource_sort_and_combine(&(ctrl->bus_head)); + + if (rc) + return(rc); + } else { + if ((evbuffer[0] != 0) && (!ctrl->push_flag)) + return 1; + } + + return 0; +} + + +int compaq_nvram_store (void *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 -Nru a/drivers/pci/hotplug/cpqphp_nvram.h b/drivers/pci/hotplug/cpqphp_nvram.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/cpqphp_nvram.h Wed Jun 4 18:11:56 2003 @@ -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 + * + */ + +#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 -Nru a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/cpqphp_pci.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,1548 @@ +/* + * 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpqphp.h" +#include "cpqphp_nvram.h" +#include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependent we are... */ + + +u8 cpqhp_nic_irq; +u8 cpqhp_disk_irq; + +static u16 unused_IRQ; + +/* + * detect_HRT_floating_pointer + * + * find the Hot Plug Resource Table in the specified region of memory. + * + */ +static void *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; +} + + +int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) +{ + unsigned char bus; + struct pci_bus *child; + int num; + + if (func->pci_dev == NULL) + func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); + + /* No pci device, we need to create it then */ + if (func->pci_dev == NULL) { + dbg("INFO: pci_dev still null\n"); + + num = pci_scan_slot(ctrl->pci_dev->bus, PCI_DEVFN(func->device, func->function)); + if (num) + pci_bus_add_devices(ctrl->pci_dev->bus); + + func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); + if (func->pci_dev == NULL) { + dbg("ERROR: pci_dev still null\n"); + return 0; + } + } + + if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); + child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); + pci_do_scan_bus(child); + } + + return 0; +} + + +int cpqhp_unconfigure_device(struct pci_func* func) +{ + int j; + + dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus, func->device, func->function); + + for (j=0; j<8 ; j++) { + struct pci_dev* temp = pci_find_slot(func->bus, (func->device << 3) | j); + if (temp) + pci_remove_bus_device(temp); + } + return 0; +} + +static int PCI_RefinedAccessConfig(struct pci_bus *bus, unsigned int devfn, u8 offset, u32 *value) +{ + u32 vendID = 0; + + if (pci_bus_read_config_dword (bus, devfn, PCI_VENDOR_ID, &vendID) == -1) + return -1; + if (vendID == 0xffffffff) + return -1; + return pci_bus_read_config_dword (bus, devfn, offset, value); +} + + +/* + * cpqhp_set_irq + * + * @bus_num: bus number of PCI device + * @dev_num: device number of PCI device + * @slot: pointer to u8 where slot number will be returned + */ +int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) +{ + int rc; + u16 temp_word; + struct pci_dev fakedev; + struct pci_bus fakebus; + + fakedev.devfn = dev_num << 3; + fakedev.bus = &fakebus; + fakebus.number = bus_num; + dbg("%s: dev %d, bus %d, pin %d, num %d\n", + __FUNCTION__, dev_num, bus_num, int_pin, irq_num); + rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num); + dbg("%s: rc %d\n", __FUNCTION__, rc); + if (!rc) + return !rc; + + // set the Edge Level Control Register (ELCR) + temp_word = inb(0x4d0); + temp_word |= inb(0x4d1) << 8; + + temp_word |= 0x01 << irq_num; + + // This should only be for x86 as it sets the Edge Level Control Register + outb((u8) (temp_word & 0xFF), 0x4d0); + outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1); + + 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; + + ctrl->pci_bus->number = bus_num; + + for (tdevice = 0; tdevice < 0x100; tdevice++) { + //Scan for access first + if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1) + continue; + dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice); + //Yep we got one. Not a bridge ? + if ((work >> 8) != PCI_TO_PCI_BRIDGE_CLASS) { + *dev_num = tdevice; + dbg("found it !\n"); + return 0; + } + } + for (tdevice = 0; tdevice < 0x100; tdevice++) { + //Scan for access first + if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1) + continue; + dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice); + //Yep we got one. bridge ? + if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { + pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(tdevice, 0), PCI_SECONDARY_BUS, &tbus); + dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice); + if (PCI_ScanBusNonBridge(tbus, tdevice) == 0) + return 0; + } + } + + return -1; +} + + +static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge) +{ + struct irq_routing_table *PCIIRQRoutingInfoLength; + long len; + long loop; + u32 work; + + u8 tbus, tdevice, tslot; + + PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table(); + if (!PCIIRQRoutingInfoLength) + return -1; + + len = (PCIIRQRoutingInfoLength->size - + sizeof(struct irq_routing_table)) / sizeof(struct irq_info); + // Make sure I got at least one entry + if (len == 0) { + if (PCIIRQRoutingInfoLength != NULL) + kfree(PCIIRQRoutingInfoLength ); + return -1; + } + + for (loop = 0; loop < len; ++loop) { + tbus = PCIIRQRoutingInfoLength->slots[loop].bus; + tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn; + tslot = PCIIRQRoutingInfoLength->slots[loop].slot; + + if (tslot == slot) { + *bus_num = tbus; + *dev_num = tdevice; + ctrl->pci_bus->number = tbus; + pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_VENDOR_ID, &work); + if (!nobridge || (work == 0xffffffff)) { + if (PCIIRQRoutingInfoLength != NULL) + kfree(PCIIRQRoutingInfoLength ); + return 0; + } + + dbg("bus_num %d devfn %d\n", *bus_num, *dev_num); + pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_CLASS_REVISION, &work); + dbg("work >> 8 (%x) = BRIDGE (%x)\n", work >> 8, PCI_TO_PCI_BRIDGE_CLASS); + + if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { + pci_bus_read_config_byte (ctrl->pci_bus, *dev_num, PCI_SECONDARY_BUS, &tbus); + dbg("Scan bus for Non Bridge: bus %d\n", tbus); + if (PCI_ScanBusForNonBridge(ctrl, tbus, dev_num) == 0) { + *bus_num = tbus; + if (PCIIRQRoutingInfoLength != NULL) + kfree(PCIIRQRoutingInfoLength ); + return 0; + } + } else { + if (PCIIRQRoutingInfoLength != NULL) + kfree(PCIIRQRoutingInfoLength ); + return 0; + } + + } + } + if (PCIIRQRoutingInfoLength != NULL) + kfree(PCIIRQRoutingInfoLength ); + return -1; +} + + +int cpqhp_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 slot) +{ + return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0); //plain (bridges allowed) +} + + +/* More PCI configuration routines; this time centered around hotplug controller */ + + +/* + * cpqhp_save_config + * + * Reads configuration for all slots in a PCI bus and saves info. + * + * Note: For non-hot plug busses, the slot # saved is the device # + * + * returns 0 if success + */ +int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug) +{ + long rc; + u8 class_code; + u8 header_type; + u32 ID; + u8 secondary_bus; + struct pci_func *new_slot; + int sub_bus; + int FirstSupported; + int LastSupported; + int max_functions; + int function; + u8 DevError; + int device = 0; + int cloop = 0; + int stop_it; + int index; + + // Decide which slots are supported + + if (is_hot_plug) { + //********************************* + // is_hot_plug is the slot mask + //********************************* + FirstSupported = is_hot_plug >> 4; + LastSupported = FirstSupported + (is_hot_plug & 0x0F) - 1; + } else { + FirstSupported = 0; + LastSupported = 0x1F; + } + + // Save PCI configuration space for all devices in supported slots + ctrl->pci_bus->number = busnumber; + for (device = FirstSupported; device <= LastSupported; device++) { + ID = 0xFFFFFFFF; + rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID); + + if (ID != 0xFFFFFFFF) { // device in slot + rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code); + if (rc) + return rc; + + rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type); + if (rc) + return rc; + + // If multi-function device, set max_functions to 8 + if (header_type & 0x80) + max_functions = 8; + else + max_functions = 1; + + function = 0; + + do { + DevError = 0; + + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // P-P Bridge + // Recurse the subordinate bus + // get the subordinate bus number + rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus); + if (rc) { + return rc; + } else { + sub_bus = (int) secondary_bus; + + // Save secondary bus cfg spc + // with this recursive call. + rc = cpqhp_save_config(ctrl, sub_bus, 0); + if (rc) + return rc; + ctrl->pci_bus->number = busnumber; + } + } + + index = 0; + new_slot = cpqhp_slot_find(busnumber, device, index++); + while (new_slot && + (new_slot->function != (u8) function)) + new_slot = cpqhp_slot_find(busnumber, device, index++); + + if (!new_slot) { + // Setup slot structure. + new_slot = cpqhp_slot_create(busnumber); + + if (new_slot == NULL) + return(1); + } + + new_slot->bus = (u8) busnumber; + new_slot->device = (u8) device; + new_slot->function = (u8) function; + new_slot->is_a_board = 1; + new_slot->switch_save = 0x10; + // In case of unsupported board + new_slot->status = DevError; + new_slot->pci_dev = pci_find_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function); + + for (cloop = 0; cloop < 0x20; cloop++) { + rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop])); + if (rc) + return rc; + } + + function++; + + stop_it = 0; + + // this loop skips to the next present function + // reading in Class Code and Header type. + + while ((function < max_functions)&&(!stop_it)) { + rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID); + if (ID == 0xFFFFFFFF) { // nothing there. + function++; + } else { // Something there + rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code); + if (rc) + return rc; + + rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type); + if (rc) + return rc; + + stop_it++; + } + } + + } while (function < max_functions); + } // End of IF (device in slot?) + else if (is_hot_plug) { + // Setup slot structure with entry for empty slot + new_slot = cpqhp_slot_create(busnumber); + + if (new_slot == NULL) { + return(1); + } + + new_slot->bus = (u8) busnumber; + new_slot->device = (u8) device; + new_slot->function = 0; + new_slot->is_a_board = 0; + new_slot->presence_save = 0; + new_slot->switch_save = 0; + } + } // End of FOR loop + + return(0); +} + + +/* + * cpqhp_save_slot_config + * + * Saves configuration info for all PCI devices in a given slot + * including subordinate busses. + * + * returns 0 if success + */ +int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot) +{ + long rc; + u8 class_code; + u8 header_type; + u32 ID; + u8 secondary_bus; + int sub_bus; + int max_functions; + int function; + int cloop = 0; + int stop_it; + + ID = 0xFFFFFFFF; + + ctrl->pci_bus->number = new_slot->bus; + pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID); + + if (ID != 0xFFFFFFFF) { // device in slot + pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code); + pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type); + + if (header_type & 0x80) // Multi-function device + max_functions = 8; + else + max_functions = 1; + + function = 0; + + do { + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge + // Recurse the subordinate bus + pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus); + + sub_bus = (int) secondary_bus; + + // Save the config headers for the secondary bus. + rc = cpqhp_save_config(ctrl, sub_bus, 0); + if (rc) + return(rc); + ctrl->pci_bus->number = new_slot->bus; + + } // End of IF + + new_slot->status = 0; + + for (cloop = 0; cloop < 0x20; cloop++) { + pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop])); + } + + function++; + + stop_it = 0; + + // this loop skips to the next present function + // reading in the Class Code and the Header type. + + while ((function < max_functions) && (!stop_it)) { + pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID); + + if (ID == 0xFFFFFFFF) { // nothing there. + function++; + } else { // Something there + pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code); + + pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type); + + stop_it++; + } + } + + } while (function < max_functions); + } // End of IF (device in slot?) + else { + return(2); + } + + return(0); +} + + +/* + * cpqhp_save_base_addr_length + * + * Saves the length of all base address registers for the + * specified slot. this is for hot plug REPLACE + * + * returns 0 if success + */ +int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func) +{ + u8 cloop; + u8 header_type; + u8 secondary_bus; + u8 type; + int sub_bus; + u32 temp_register; + u32 base; + u32 rc; + struct pci_func *next; + int index = 0; + struct pci_bus *pci_bus = ctrl->pci_bus; + unsigned int devfn; + + func = cpqhp_slot_find(func->bus, func->device, index++); + + while (func != NULL) { + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + // Check for Bridge + pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { + // PCI-PCI Bridge + pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); + + sub_bus = (int) secondary_bus; + + next = cpqhp_slot_list[sub_bus]; + + while (next != NULL) { + rc = cpqhp_save_base_addr_length(ctrl, next); + + if (rc) + return(rc); + + next = next->next; + } + pci_bus->number = func->bus; + + //FIXME: this loop is duplicated in the non-bridge case. The two could be rolled together + // Figure out IO and memory base lengths + for (cloop = 0x10; cloop <= 0x14; cloop += 4) { + temp_register = 0xFFFFFFFF; + pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); + pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); + + if (base) { // If this register is implemented + if (base & 0x01L) { + // IO base + // set base = amount of IO space requested + base = base & 0xFFFFFFFE; + base = (~base) + 1; + + type = 1; + } else { + // memory base + base = base & 0xFFFFFFF0; + base = (~base) + 1; + + type = 0; + } + } else { + base = 0x0L; + type = 0; + } + + // Save information in slot structure + func->base_length[(cloop - 0x10) >> 2] = + base; + func->base_type[(cloop - 0x10) >> 2] = type; + + } // End of base register loop + + + } else if ((header_type & 0x7F) == 0x00) { // PCI-PCI Bridge + // Figure out IO and memory base lengths + for (cloop = 0x10; cloop <= 0x24; cloop += 4) { + temp_register = 0xFFFFFFFF; + pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); + pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); + + if (base) { // If this register is implemented + if (base & 0x01L) { + // IO base + // base = amount of IO space requested + base = base & 0xFFFFFFFE; + base = (~base) + 1; + + type = 1; + } else { + // memory base + // base = amount of memory space requested + base = base & 0xFFFFFFF0; + base = (~base) + 1; + + type = 0; + } + } else { + base = 0x0L; + type = 0; + } + + // Save information in slot structure + func->base_length[(cloop - 0x10) >> 2] = base; + func->base_type[(cloop - 0x10) >> 2] = type; + + } // End of base register loop + + } else { // Some other unknown header type + } + + // find the next device in this slot + func = cpqhp_slot_find(func->bus, func->device, index++); + } + + return(0); +} + + +/* + * cpqhp_save_used_resources + * + * Stores used resource information for existing boards. this is + * for boards that were in the system when this driver was loaded. + * this function is for hot plug ADD + * + * returns 0 if success + */ +int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) +{ + u8 cloop; + u8 header_type; + u8 secondary_bus; + u8 temp_byte; + u8 b_base; + u8 b_length; + u16 command; + u16 save_command; + u16 w_base; + u16 w_length; + u32 temp_register; + u32 save_base; + u32 base; + int index = 0; + struct pci_resource *mem_node; + struct pci_resource *p_mem_node; + struct pci_resource *io_node; + struct pci_resource *bus_node; + struct pci_bus *pci_bus = ctrl->pci_bus; + unsigned int devfn; + + func = cpqhp_slot_find(func->bus, func->device, index++); + + while ((func != NULL) && func->is_a_board) { + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + // Save the command register + pci_bus_read_config_word (pci_bus, devfn, PCI_COMMAND, &save_command); + + // disable card + command = 0x00; + pci_bus_write_config_word (pci_bus, devfn, PCI_COMMAND, command); + + // Check for Bridge + pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge + // Clear Bridge Control Register + command = 0x00; + pci_bus_write_config_word (pci_bus, devfn, PCI_BRIDGE_CONTROL, command); + pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); + pci_bus_read_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte); + + bus_node =(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_bus_read_config_byte (pci_bus, devfn, PCI_IO_BASE, &b_base); + pci_bus_read_config_byte (pci_bus, devfn, PCI_IO_LIMIT, &b_length); + + if ((b_base <= b_length) && (save_command & 0x01)) { + io_node = (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_bus_read_config_word (pci_bus, devfn, PCI_MEMORY_BASE, &w_base); + pci_bus_read_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length); + + if ((w_base <= w_length) && (save_command & 0x02)) { + mem_node = (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_bus_read_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base); + pci_bus_read_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length); + + if ((w_base <= w_length) && (save_command & 0x02)) { + p_mem_node = (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_bus_read_config_dword (pci_bus, devfn, cloop, &save_base); + + temp_register = 0xFFFFFFFF; + pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); + pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); + + temp_register = base; + + if (base) { // If this register is implemented + if (((base & 0x03L) == 0x01) + && (save_command & 0x01)) { + // IO base + // set temp_register = amount of IO space requested + temp_register = base & 0xFFFFFFFE; + temp_register = (~temp_register) + 1; + + io_node = (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_bus_read_config_dword (pci_bus, devfn, cloop, &save_base); + + temp_register = 0xFFFFFFFF; + pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); + pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); + + temp_register = base; + + if (base) { // If this register is implemented + if (((base & 0x03L) == 0x01) + && (save_command & 0x01)) { + // IO base + // set temp_register = amount of IO space requested + temp_register = base & 0xFFFFFFFE; + temp_register = (~temp_register) + 1; + + io_node = (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; + struct pci_bus *pci_bus = ctrl->pci_bus; + unsigned int devfn; + + func = cpqhp_slot_find(func->bus, func->device, index++); + + while (func != NULL) { + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + // Start at the top of config space so that the control + // registers are programmed last + for (cloop = 0x3C; cloop > 0; cloop -= 4) { + pci_bus_write_config_dword (pci_bus, devfn, cloop, func->config_space[cloop >> 2]); + } + + pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + + // If this is a bridge device, restore subordinate devices + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge + pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); + + sub_bus = (int) secondary_bus; + + next = cpqhp_slot_list[sub_bus]; + + while (next != NULL) { + rc = cpqhp_configure_board(ctrl, next); + + if (rc) + return rc; + + next = next->next; + } + } else { + + // Check all the base Address Registers to make sure + // they are the same. If not, the board is different. + + for (cloop = 16; cloop < 40; cloop += 4) { + pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp); + + if (temp != func->config_space[cloop >> 2]) { + dbg("Config space compare failure!!! offset = %x\n", cloop); + dbg("bus = %x, device = %x, function = %x\n", func->bus, func->device, func->function); + dbg("temp = %x, config space = %x\n\n", temp, func->config_space[cloop >> 2]); + return 1; + } + } + } + + func->configured = 1; + + func = cpqhp_slot_find(func->bus, func->device, index++); + } + + return 0; +} + + +/* + * cpqhp_valid_replace + * + * this function checks to see if a board is the same as the + * one it is replacing. this check will detect if the device's + * vendor or device id's are the same + * + * returns 0 if the board is the same nonzero otherwise + */ +int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func) +{ + u8 cloop; + u8 header_type; + u8 secondary_bus; + u8 type; + u32 temp_register = 0; + u32 base; + u32 rc; + struct pci_func *next; + int index = 0; + struct pci_bus *pci_bus = ctrl->pci_bus; + unsigned int devfn; + + if (!func->is_a_board) + return(ADD_NOT_SUPPORTED); + + func = cpqhp_slot_find(func->bus, func->device, index++); + + while (func != NULL) { + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + pci_bus_read_config_dword (pci_bus, devfn, PCI_VENDOR_ID, &temp_register); + + // No adapter present + if (temp_register == 0xFFFFFFFF) + return(NO_ADAPTER_PRESENT); + + if (temp_register != func->config_space[0]) + return(ADAPTER_NOT_SAME); + + // Check for same revision number and class code + pci_bus_read_config_dword (pci_bus, devfn, PCI_CLASS_REVISION, &temp_register); + + // Adapter not the same + if (temp_register != func->config_space[0x08 >> 2]) + return(ADAPTER_NOT_SAME); + + // Check for Bridge + pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge + // In order to continue checking, we must program the + // bus registers in the bridge to respond to accesses + // for it's subordinate bus(es) + + temp_register = func->config_space[0x18 >> 2]; + pci_bus_write_config_dword (pci_bus, devfn, PCI_PRIMARY_BUS, temp_register); + + secondary_bus = (temp_register >> 8) & 0xFF; + + next = cpqhp_slot_list[secondary_bus]; + + while (next != NULL) { + rc = cpqhp_valid_replace(ctrl, next); + + if (rc) + return(rc); + + next = next->next; + } + + } + // Check to see if it is a standard config header + else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) { + // Check subsystem vendor and ID + pci_bus_read_config_dword (pci_bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &temp_register); + + if (temp_register != func->config_space[0x2C >> 2]) { + // If it's a SMART-2 and the register isn't filled + // in, ignore the difference because + // they just have an old rev of the firmware + + if (!((func->config_space[0] == 0xAE100E11) + && (temp_register == 0x00L))) + return(ADAPTER_NOT_SAME); + } + // Figure out IO and memory base lengths + for (cloop = 0x10; cloop <= 0x24; cloop += 4) { + temp_register = 0xFFFFFFFF; + pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); + pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); + if (base) { // If this register is implemented + if (base & 0x01L) { + // IO base + // set base = amount of IO space requested + base = base & 0xFFFFFFFE; + base = (~base) + 1; + + type = 1; + } else { + // memory base + base = base & 0xFFFFFFF0; + base = (~base) + 1; + + type = 0; + } + } else { + base = 0x0L; + type = 0; + } + + // Check information in slot structure + if (func->base_length[(cloop - 0x10) >> 2] != base) + return(ADAPTER_NOT_SAME); + + if (func->base_type[(cloop - 0x10) >> 2] != type) + return(ADAPTER_NOT_SAME); + + } // End of base register loop + + } // End of (type 0 config space) else + else { + // this is not a type 0 or 1 config space header so + // we don't know how to do it + return(DEVICE_TYPE_NOT_SUPPORTED); + } + + // Get the next function + func = cpqhp_slot_find(func->bus, func->device, index++); + } + + + return(0); +} + + +/* + * cpqhp_find_available_resources + * + * Finds available memory, IO, and IRQ resources for programming + * devices which may be added to the system + * this function is for hot plug ADD! + * + * returns 0 if success + */ +int cpqhp_find_available_resources (struct controller *ctrl, void *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->cfgspc_irq; + } + + if (!cpqhp_disk_irq) { + cpqhp_disk_irq = ctrl->cfgspc_irq; + } + + dbg("cpqhp_disk_irq, cpqhp_nic_irq= %d, %d\n", cpqhp_disk_irq, cpqhp_nic_irq); + + rc = compaq_nvram_load(rom_start, ctrl); + if (rc) + return rc; + + one_slot = rom_resource_table + sizeof (struct hrt); + + i = readb(rom_resource_table + NUMBER_OF_ENTRIES); + dbg("number_of_entries = %d\n", i); + + if (!readb(one_slot + SECONDARY_BUS)) { + return(1); + } + + dbg("dev|IO base|length|Mem base|length|Pre base|length|PB SB MB\n"); + + while (i && readb(one_slot + SECONDARY_BUS)) { + u8 dev_func = readb(one_slot + DEV_FUNC); + u8 primary_bus = readb(one_slot + PRIMARY_BUS); + u8 secondary_bus = readb(one_slot + SECONDARY_BUS); + u8 max_bus = readb(one_slot + MAX_BUS); + u16 io_base = readw(one_slot + IO_BASE); + u16 io_length = readw(one_slot + IO_LENGTH); + u16 mem_base = readw(one_slot + MEM_BASE); + u16 mem_length = readw(one_slot + MEM_LENGTH); + u16 pre_mem_base = readw(one_slot + PRE_MEM_BASE); + u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH); + + dbg("%2.2x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x |%2.2x %2.2x %2.2x\n", + dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length, + primary_bus, secondary_bus, max_bus); + + // If this entry isn't for our controller's bus, ignore it + if (primary_bus != ctrl->bus) { + i--; + one_slot += sizeof (struct slot_rt); + continue; + } + // find out if this entry is for an occupied slot + ctrl->pci_bus->number = primary_bus; + pci_bus_read_config_dword (ctrl->pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword); + dbg("temp_D_word = %x\n", temp_dword); + + if (temp_dword != 0xFFFFFFFF) { + index = 0; + func = cpqhp_slot_find(primary_bus, dev_func >> 3, 0); + + while (func && (func->function != (dev_func & 0x07))) { + dbg("func = %p (bus, dev, fun) = (%d, %d, %d)\n", func, primary_bus, dev_func >> 3, index); + func = cpqhp_slot_find(primary_bus, dev_func >> 3, index++); + } + + // If we can't find a match, skip this table entry + if (!func) { + i--; + one_slot += sizeof (struct slot_rt); + continue; + } + // this may not work and shouldn't be used + if (secondary_bus != primary_bus) + bridged_slot = 1; + else + bridged_slot = 0; + + populated_slot = 1; + } else { + populated_slot = 0; + bridged_slot = 0; + } + + + // If we've got a valid IO base, use it + + temp_dword = io_base + io_length; + + if ((io_base) && (temp_dword < 0x10000)) { + io_node = (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("%s\n", __FUNCTION__); + + if (!func) + return(1); + + node = func->io_head; + func->io_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->io_head), node); + node = t_node; + } + + node = func->mem_head; + func->mem_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->mem_head), node); + node = t_node; + } + + node = func->p_mem_head; + func->p_mem_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->p_mem_head), node); + node = t_node; + } + + node = func->bus_head; + func->bus_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->bus_head), node); + node = t_node; + } + + rc |= cpqhp_resource_sort_and_combine(&(resources->mem_head)); + rc |= cpqhp_resource_sort_and_combine(&(resources->p_mem_head)); + rc |= cpqhp_resource_sort_and_combine(&(resources->io_head)); + rc |= cpqhp_resource_sort_and_combine(&(resources->bus_head)); + + return(rc); +} + + +/* + * cpqhp_destroy_resource_list + * + * Puts node back in the resource list pointed to by head + */ +void cpqhp_destroy_resource_list (struct resource_lists * resources) +{ + struct pci_resource *res, *tres; + + res = resources->io_head; + resources->io_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = resources->mem_head; + resources->mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = resources->p_mem_head; + resources->p_mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = resources->bus_head; + resources->bus_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } +} + + +/* + * cpqhp_destroy_board_resources + * + * Puts node back in the resource list pointed to by head + */ +void cpqhp_destroy_board_resources (struct pci_func * func) +{ + struct pci_resource *res, *tres; + + res = func->io_head; + func->io_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = func->mem_head; + func->mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = func->p_mem_head; + func->p_mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = func->bus_head; + func->bus_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } +} + diff -Nru a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/cpqphp_sysfs.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,143 @@ +/* + * Compaq Hot Plug Controller Driver + * + * Copyright (c) 1995,2001 Compaq Computer Corporation + * Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001 IBM Corp. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "cpqphp.h" + + +/* A few routines that create sysfs entries for the hot plug controller */ + +static int show_ctrl (struct device *dev, char *buf) +{ + struct pci_dev *pci_dev; + struct controller *ctrl; + char * out = buf; + int index; + struct pci_resource *res; + + pci_dev = container_of (dev, struct pci_dev, dev); + ctrl = pci_get_drvdata(pci_dev); + + out += sprintf(buf, "Free resources: memory\n"); + index = 11; + res = ctrl->mem_head; + while (res && index--) { + out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); + res = res->next; + } + out += sprintf(out, "Free resources: prefetchable memory\n"); + index = 11; + res = ctrl->p_mem_head; + while (res && index--) { + out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); + res = res->next; + } + out += sprintf(out, "Free resources: IO\n"); + index = 11; + res = ctrl->io_head; + while (res && index--) { + out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); + res = res->next; + } + out += sprintf(out, "Free resources: bus numbers\n"); + index = 11; + res = ctrl->bus_head; + while (res && index--) { + out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); + res = res->next; + } + + return out - buf; +} +static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL); + +static int show_dev (struct device *dev, char *buf) +{ + struct pci_dev *pci_dev; + struct controller *ctrl; + char * out = buf; + int index; + struct pci_resource *res; + struct pci_func *new_slot; + struct slot *slot; + + pci_dev = container_of (dev, struct pci_dev, dev); + ctrl = pci_get_drvdata(pci_dev); + + slot=ctrl->slot; + + while (slot) { + new_slot = cpqhp_slot_find(slot->bus, slot->device, 0); + if (!new_slot) + break; + out += sprintf(out, "assigned resources: memory\n"); + index = 11; + res = new_slot->mem_head; + while (res && index--) { + out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); + res = res->next; + } + out += sprintf(out, "assigned resources: prefetchable memory\n"); + index = 11; + res = new_slot->p_mem_head; + while (res && index--) { + out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); + res = res->next; + } + out += sprintf(out, "assigned resources: IO\n"); + index = 11; + res = new_slot->io_head; + while (res && index--) { + out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); + res = res->next; + } + out += sprintf(out, "assigned resources: bus numbers\n"); + index = 11; + res = new_slot->bus_head; + while (res && index--) { + out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); + res = res->next; + } + slot=slot->next; + } + + return out - buf; +} +static DEVICE_ATTR (dev, S_IRUGO, show_dev, NULL); + +void cpqhp_create_ctrl_files (struct controller *ctrl) +{ + device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl); + device_create_file (&ctrl->pci_dev->dev, &dev_attr_dev); +} diff -Nru a/drivers/pci/hotplug/ibmphp.h b/drivers/pci/hotplug/ibmphp.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/ibmphp.h Wed Jun 4 18:11:56 2003 @@ -0,0 +1,772 @@ +#ifndef __IBMPHP_H +#define __IBMPHP_H + +/* + * IBM Hot Plug Controller Driver + * + * Written By: Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation + * + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2002 IBM Corp. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + */ + +#include "pci_hotplug.h" + +extern int ibmphp_debug; + +#if !defined(CONFIG_HOTPLUG_PCI_IBM_MODULE) + #define MY_NAME "ibmphpd" +#else + #define MY_NAME THIS_MODULE->name +#endif +#define debug(fmt, arg...) do { if (ibmphp_debug == 1) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0) +#define debug_pci(fmt, arg...) do { if (ibmphp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0) +#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) + + +/* EBDA stuff */ + +/*********************************************************** +* SLOT CAPABILITY * +***********************************************************/ + +#define EBDA_SLOT_133_MAX 0x20 +#define EBDA_SLOT_100_MAX 0x10 +#define EBDA_SLOT_66_MAX 0x02 +#define EBDA_SLOT_PCIX_CAP 0x08 + + +/************************************************************ +* RESOURE TYPE * +************************************************************/ + +#define EBDA_RSRC_TYPE_MASK 0x03 +#define EBDA_IO_RSRC_TYPE 0x00 +#define EBDA_MEM_RSRC_TYPE 0x01 +#define EBDA_PFM_RSRC_TYPE 0x03 +#define EBDA_RES_RSRC_TYPE 0x02 + + +/************************************************************* +* IO RESTRICTION TYPE * +*************************************************************/ + +#define EBDA_IO_RESTRI_MASK 0x0c +#define EBDA_NO_RESTRI 0x00 +#define EBDA_AVO_VGA_ADDR 0x04 +#define EBDA_AVO_VGA_ADDR_AND_ALIA 0x08 +#define EBDA_AVO_ISA_ADDR 0x0c + + +/************************************************************** +* DEVICE TYPE DEF * +**************************************************************/ + +#define EBDA_DEV_TYPE_MASK 0x10 +#define EBDA_PCI_DEV 0x10 +#define EBDA_NON_PCI_DEV 0x00 + + +/*************************************************************** +* PRIMARY DEF DEFINITION * +***************************************************************/ + +#define EBDA_PRI_DEF_MASK 0x20 +#define EBDA_PRI_PCI_BUS_INFO 0x20 +#define EBDA_NORM_DEV_RSRC_INFO 0x00 + + +//-------------------------------------------------------------- +// RIO TABLE DATA STRUCTURE +//-------------------------------------------------------------- + +struct rio_table_hdr { + u8 ver_num; + u8 scal_count; + u8 riodev_count; + u16 offset; +}; + +//------------------------------------------------------------- +// SCALABILITY DETAIL +//------------------------------------------------------------- + +struct scal_detail { + u8 node_id; + u32 cbar; + u8 port0_node_connect; + u8 port0_port_connect; + u8 port1_node_connect; + u8 port1_port_connect; + u8 port2_node_connect; + u8 port2_port_connect; + u8 chassis_num; +// struct list_head scal_detail_list; +}; + +//-------------------------------------------------------------- +// RIO DETAIL +//-------------------------------------------------------------- + +struct rio_detail { + u8 rio_node_id; + u32 bbar; + u8 rio_type; + u8 owner_id; + u8 port0_node_connect; + u8 port0_port_connect; + u8 port1_node_connect; + u8 port1_port_connect; + u8 first_slot_num; + u8 status; + u8 wpindex; + u8 chassis_num; + struct list_head rio_detail_list; +}; + +struct opt_rio { + u8 rio_type; + u8 chassis_num; + u8 first_slot_num; + u8 middle_num; + struct list_head opt_rio_list; +}; + +struct opt_rio_lo { + u8 rio_type; + u8 chassis_num; + u8 first_slot_num; + u8 middle_num; + u8 pack_count; + struct list_head opt_rio_lo_list; +}; + +/**************************************************************** +* HPC DESCRIPTOR NODE * +****************************************************************/ + +struct ebda_hpc_list { + u8 format; + u16 num_ctlrs; + short phys_addr; +// struct list_head ebda_hpc_list; +}; +/***************************************************************** +* IN HPC DATA STRUCTURE, THE ASSOCIATED SLOT AND BUS * +* STRUCTURE * +*****************************************************************/ + +struct ebda_hpc_slot { + u8 slot_num; + u32 slot_bus_num; + u8 ctl_index; + u8 slot_cap; +}; + +struct ebda_hpc_bus { + u32 bus_num; + u8 slots_at_33_conv; + u8 slots_at_66_conv; + u8 slots_at_66_pcix; + u8 slots_at_100_pcix; + u8 slots_at_133_pcix; +}; + + +/******************************************************************** +* THREE TYPE OF HOT PLUG CONTROLER * +********************************************************************/ + +struct isa_ctlr_access { + u16 io_start; + u16 io_end; +}; + +struct pci_ctlr_access { + u8 bus; + u8 dev_fun; +}; + +struct wpeg_i2c_ctlr_access { + ulong wpegbbar; + u8 i2c_addr; +}; + +#define HPC_DEVICE_ID 0x0246 +#define HPC_SUBSYSTEM_ID 0x0247 +#define HPC_PCI_OFFSET 0x40 +/************************************************************************* +* RSTC DESCRIPTOR NODE * +*************************************************************************/ + +struct ebda_rsrc_list { + u8 format; + u16 num_entries; + u16 phys_addr; + struct ebda_rsrc_list *next; +}; + + +/*************************************************************************** +* PCI RSRC NODE * +***************************************************************************/ + +struct ebda_pci_rsrc { + u8 rsrc_type; + u8 bus_num; + u8 dev_fun; + u32 start_addr; + u32 end_addr; + u8 marked; /* for NVRAM */ + struct list_head ebda_pci_rsrc_list; +}; + + +/*********************************************************** +* BUS_INFO DATE STRUCTURE * +***********************************************************/ + +struct bus_info { + u8 slot_min; + u8 slot_max; + u8 slot_count; + u8 busno; + u8 controller_id; + u8 current_speed; + u8 current_bus_mode; + u8 index; + u8 slots_at_33_conv; + u8 slots_at_66_conv; + u8 slots_at_66_pcix; + u8 slots_at_100_pcix; + u8 slots_at_133_pcix; + struct list_head bus_info_list; +}; + + +/*********************************************************** +* GLOBAL VARIABLES * +***********************************************************/ +extern struct list_head ibmphp_ebda_pci_rsrc_head; +extern struct list_head ibmphp_slot_head; +extern struct list_head ibmphp_res_head; +/*********************************************************** +* FUNCTION PROTOTYPES * +***********************************************************/ + +extern void ibmphp_free_ebda_hpc_queue (void); +extern int ibmphp_access_ebda (void); +extern struct slot *ibmphp_get_slot_from_physical_num (u8); +extern int ibmphp_get_total_hp_slots (void); +extern void ibmphp_free_ibm_slot (struct slot *); +extern void ibmphp_free_bus_info_queue (void); +extern void ibmphp_free_ebda_pci_rsrc_queue (void); +extern struct bus_info *ibmphp_find_same_bus_num (u32); +extern int ibmphp_get_bus_index (u8); +extern u16 ibmphp_get_total_controllers (void); +extern int ibmphp_register_pci (void); + +/* passed parameters */ +#define MEM 0 +#define IO 1 +#define PFMEM 2 + +/* bit masks */ +#define RESTYPE 0x03 +#define IOMASK 0x00 /* will need to take its complement */ +#define MMASK 0x01 +#define PFMASK 0x03 +#define PCIDEVMASK 0x10 /* we should always have PCI devices */ +#define PRIMARYBUSMASK 0x20 + +/* pci specific defines */ +#define PCI_VENDOR_ID_NOTVALID 0xFFFF +#define PCI_HEADER_TYPE_MULTIDEVICE 0x80 +#define PCI_HEADER_TYPE_MULTIBRIDGE 0x81 + +#define LATENCY 0x64 +#define CACHE 64 +#define DEVICEENABLE 0x015F /* CPQ has 0x0157 */ + +#define IOBRIDGE 0x1000 /* 4k */ +#define MEMBRIDGE 0x100000 /* 1M */ + +/* irqs */ +#define SCSI_IRQ 0x09 +#define LAN_IRQ 0x0A +#define OTHER_IRQ 0x0B + +/* Data Structures */ + +/* type is of the form x x xx xx + * | | | |_ 00 - I/O, 01 - Memory, 11 - PFMemory + * | | - 00 - No Restrictions, 01 - Avoid VGA, 10 - Avoid + * | | VGA and their aliases, 11 - Avoid ISA + * | - 1 - PCI device, 0 - non pci device + * - 1 - Primary PCI Bus Information (0 if Normal device) + * the IO restrictions [2:3] are only for primary buses + */ + + +/* we need this struct because there could be several resource blocks + * allocated per primary bus in the EBDA + */ +struct range_node { + int rangeno; + u32 start; + u32 end; + struct range_node *next; +}; + +struct bus_node { + u8 busno; + int noIORanges; + struct range_node *rangeIO; + int noMemRanges; + struct range_node *rangeMem; + int noPFMemRanges; + struct range_node *rangePFMem; + int needIOUpdate; + int needMemUpdate; + int needPFMemUpdate; + struct resource_node *firstIO; /* first IO resource on the Bus */ + struct resource_node *firstMem; /* first memory resource on the Bus */ + struct resource_node *firstPFMem; /* first prefetchable memory resource on the Bus */ + struct resource_node *firstPFMemFromMem; /* when run out of pfmem available, taking from Mem */ + struct list_head bus_list; +}; + +struct resource_node { + int rangeno; + u8 busno; + u8 devfunc; + u32 start; + u32 end; + u32 len; + int type; /* MEM, IO, PFMEM */ + u8 fromMem; /* this is to indicate that the range is from + * from the Memory bucket rather than from PFMem */ + struct resource_node *next; + struct resource_node *nextRange; /* for the other mem range on bus */ +}; + +struct res_needed { + u32 mem; + u32 pfmem; + u32 io; + u8 not_correct; /* needed for return */ + int devices[32]; /* for device numbers behind this bridge */ +}; + +/* functions */ + +extern int ibmphp_rsrc_init (void); +extern int ibmphp_add_resource (struct resource_node *); +extern int ibmphp_remove_resource (struct resource_node *); +extern int ibmphp_find_resource (struct bus_node *, u32, struct resource_node **, int); +extern int ibmphp_check_resource (struct resource_node *, u8); +extern int ibmphp_remove_bus (struct bus_node *, u8); +extern void ibmphp_free_resources (void); +extern int ibmphp_add_pfmem_from_mem (struct resource_node *); +extern struct bus_node *ibmphp_find_res_bus (u8); +extern void ibmphp_print_test (void); /* for debugging purposes */ + +extern void ibmphp_hpc_initvars (void); +extern int ibmphp_hpc_readslot (struct slot *, u8, u8 *); +extern int ibmphp_hpc_writeslot (struct slot *, u8); +extern void ibmphp_lock_operations (void); +extern void ibmphp_unlock_operations (void); +extern int ibmphp_hpc_fillhpslotinfo (struct hotplug_slot *); +extern int ibmphp_hpc_start_poll_thread (void); +extern void ibmphp_hpc_stop_poll_thread (void); + +//---------------------------------------------------------------------------- + + +//---------------------------------------------------------------------------- +// HPC return codes +//---------------------------------------------------------------------------- +#define FALSE 0x00 +#define TRUE 0x01 +#define HPC_ERROR 0xFF + +//----------------------------------------------------------------------------- +// BUS INFO +//----------------------------------------------------------------------------- +#define BUS_SPEED 0x30 +#define BUS_MODE 0x40 +#define BUS_MODE_PCIX 0x01 +#define BUS_MODE_PCI 0x00 +#define BUS_SPEED_2 0x20 +#define BUS_SPEED_1 0x10 +#define BUS_SPEED_33 0x00 +#define BUS_SPEED_66 0x01 +#define BUS_SPEED_100 0x02 +#define BUS_SPEED_133 0x03 +#define BUS_SPEED_66PCIX 0x04 +#define BUS_SPEED_66UNKNOWN 0x05 +#define BUS_STATUS_AVAILABLE 0x01 +#define BUS_CONTROL_AVAILABLE 0x02 +#define SLOT_LATCH_REGS_SUPPORTED 0x10 + +#define PRGM_MODEL_REV_LEVEL 0xF0 +#define MAX_ADAPTER_NONE 0x09 + +//---------------------------------------------------------------------------- +// HPC 'write' operations/commands +//---------------------------------------------------------------------------- +// Command Code State Write to reg +// Machine at index +//------------------------- ---- ------- ------------ +#define HPC_CTLR_ENABLEIRQ 0x00 // N 15 +#define HPC_CTLR_DISABLEIRQ 0x01 // N 15 +#define HPC_SLOT_OFF 0x02 // Y 0-14 +#define HPC_SLOT_ON 0x03 // Y 0-14 +#define HPC_SLOT_ATTNOFF 0x04 // N 0-14 +#define HPC_SLOT_ATTNON 0x05 // N 0-14 +#define HPC_CTLR_CLEARIRQ 0x06 // N 15 +#define HPC_CTLR_RESET 0x07 // Y 15 +#define HPC_CTLR_IRQSTEER 0x08 // N 15 +#define HPC_BUS_33CONVMODE 0x09 // Y 31-34 +#define HPC_BUS_66CONVMODE 0x0A // Y 31-34 +#define HPC_BUS_66PCIXMODE 0x0B // Y 31-34 +#define HPC_BUS_100PCIXMODE 0x0C // Y 31-34 +#define HPC_BUS_133PCIXMODE 0x0D // Y 31-34 +#define HPC_ALLSLOT_OFF 0x11 // Y 15 +#define HPC_ALLSLOT_ON 0x12 // Y 15 +#define HPC_SLOT_BLINKLED 0x13 // N 0-14 + +//---------------------------------------------------------------------------- +// read commands +//---------------------------------------------------------------------------- +#define READ_SLOTSTATUS 0x01 +#define READ_EXTSLOTSTATUS 0x02 +#define READ_BUSSTATUS 0x03 +#define READ_CTLRSTATUS 0x04 +#define READ_ALLSTAT 0x05 +#define READ_ALLSLOT 0x06 +#define READ_SLOTLATCHLOWREG 0x07 +#define READ_REVLEVEL 0x08 +#define READ_HPCOPTIONS 0x09 +//---------------------------------------------------------------------------- +// slot status +//---------------------------------------------------------------------------- +#define HPC_SLOT_POWER 0x01 +#define HPC_SLOT_CONNECT 0x02 +#define HPC_SLOT_ATTN 0x04 +#define HPC_SLOT_PRSNT2 0x08 +#define HPC_SLOT_PRSNT1 0x10 +#define HPC_SLOT_PWRGD 0x20 +#define HPC_SLOT_BUS_SPEED 0x40 +#define HPC_SLOT_LATCH 0x80 + +//---------------------------------------------------------------------------- +// HPC_SLOT_POWER status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_POWER_OFF 0x00 +#define HPC_SLOT_POWER_ON 0x01 + +//---------------------------------------------------------------------------- +// HPC_SLOT_CONNECT status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_CONNECTED 0x00 +#define HPC_SLOT_DISCONNECTED 0x01 + +//---------------------------------------------------------------------------- +// HPC_SLOT_ATTN status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_ATTN_OFF 0x00 +#define HPC_SLOT_ATTN_ON 0x01 +#define HPC_SLOT_ATTN_BLINK 0x02 + +//---------------------------------------------------------------------------- +// HPC_SLOT_PRSNT status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_EMPTY 0x00 +#define HPC_SLOT_PRSNT_7 0x01 +#define HPC_SLOT_PRSNT_15 0x02 +#define HPC_SLOT_PRSNT_25 0x03 + +//---------------------------------------------------------------------------- +// HPC_SLOT_PWRGD status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_PWRGD_FAULT_NONE 0x00 +#define HPC_SLOT_PWRGD_GOOD 0x01 + +//---------------------------------------------------------------------------- +// HPC_SLOT_BUS_SPEED status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_BUS_SPEED_OK 0x00 +#define HPC_SLOT_BUS_SPEED_MISM 0x01 + +//---------------------------------------------------------------------------- +// HPC_SLOT_LATCH status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_LATCH_OPEN 0x01 // NOTE : in PCI spec bit off = open +#define HPC_SLOT_LATCH_CLOSED 0x00 // NOTE : in PCI spec bit on = closed + + +//---------------------------------------------------------------------------- +// extended slot status +//---------------------------------------------------------------------------- +#define HPC_SLOT_PCIX 0x01 +#define HPC_SLOT_SPEED1 0x02 +#define HPC_SLOT_SPEED2 0x04 +#define HPC_SLOT_BLINK_ATTN 0x08 +#define HPC_SLOT_RSRVD1 0x10 +#define HPC_SLOT_RSRVD2 0x20 +#define HPC_SLOT_BUS_MODE 0x40 +#define HPC_SLOT_RSRVD3 0x80 + +//---------------------------------------------------------------------------- +// HPC_XSLOT_PCIX_CAP status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_PCIX_NO 0x00 +#define HPC_SLOT_PCIX_YES 0x01 + +//---------------------------------------------------------------------------- +// HPC_XSLOT_SPEED status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_SPEED_33 0x00 +#define HPC_SLOT_SPEED_66 0x01 +#define HPC_SLOT_SPEED_133 0x02 + +//---------------------------------------------------------------------------- +// HPC_XSLOT_ATTN_BLINK status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_ATTN_BLINK_OFF 0x00 +#define HPC_SLOT_ATTN_BLINK_ON 0x01 + +//---------------------------------------------------------------------------- +// HPC_XSLOT_BUS_MODE status return codes +//---------------------------------------------------------------------------- +#define HPC_SLOT_BUS_MODE_OK 0x00 +#define HPC_SLOT_BUS_MODE_MISM 0x01 + +//---------------------------------------------------------------------------- +// Controller status +//---------------------------------------------------------------------------- +#define HPC_CTLR_WORKING 0x01 +#define HPC_CTLR_FINISHED 0x02 +#define HPC_CTLR_RESULT0 0x04 +#define HPC_CTLR_RESULT1 0x08 +#define HPC_CTLR_RESULE2 0x10 +#define HPC_CTLR_RESULT3 0x20 +#define HPC_CTLR_IRQ_ROUTG 0x40 +#define HPC_CTLR_IRQ_PENDG 0x80 + +//---------------------------------------------------------------------------- +// HPC_CTLR_WROKING status return codes +//---------------------------------------------------------------------------- +#define HPC_CTLR_WORKING_NO 0x00 +#define HPC_CTLR_WORKING_YES 0x01 + +//---------------------------------------------------------------------------- +// HPC_CTLR_FINISHED status return codes +//---------------------------------------------------------------------------- +#define HPC_CTLR_FINISHED_NO 0x00 +#define HPC_CTLR_FINISHED_YES 0x01 + +//---------------------------------------------------------------------------- +// HPC_CTLR_RESULT status return codes +//---------------------------------------------------------------------------- +#define HPC_CTLR_RESULT_SUCCESS 0x00 +#define HPC_CTLR_RESULT_FAILED 0x01 +#define HPC_CTLR_RESULT_RSVD 0x02 +#define HPC_CTLR_RESULT_NORESP 0x03 + + +//---------------------------------------------------------------------------- +// macro for slot info +//---------------------------------------------------------------------------- +#define SLOT_POWER(s) ((u8) ((s & HPC_SLOT_POWER) \ + ? HPC_SLOT_POWER_ON : HPC_SLOT_POWER_OFF)) + +#define SLOT_CONNECT(s) ((u8) ((s & HPC_SLOT_CONNECT) \ + ? HPC_SLOT_DISCONNECTED : HPC_SLOT_CONNECTED)) + +#define SLOT_ATTN(s,es) ((u8) ((es & HPC_SLOT_BLINK_ATTN) \ + ? HPC_SLOT_ATTN_BLINK \ + : ((s & HPC_SLOT_ATTN) ? HPC_SLOT_ATTN_ON : HPC_SLOT_ATTN_OFF))) + +#define SLOT_PRESENT(s) ((u8) ((s & HPC_SLOT_PRSNT1) \ + ? ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_EMPTY : HPC_SLOT_PRSNT_15) \ + : ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_PRSNT_25 : HPC_SLOT_PRSNT_7))) + +#define SLOT_PWRGD(s) ((u8) ((s & HPC_SLOT_PWRGD) \ + ? HPC_SLOT_PWRGD_GOOD : HPC_SLOT_PWRGD_FAULT_NONE)) + +#define SLOT_BUS_SPEED(s) ((u8) ((s & HPC_SLOT_BUS_SPEED) \ + ? HPC_SLOT_BUS_SPEED_MISM : HPC_SLOT_BUS_SPEED_OK)) + +#define SLOT_LATCH(s) ((u8) ((s & HPC_SLOT_LATCH) \ + ? HPC_SLOT_LATCH_CLOSED : HPC_SLOT_LATCH_OPEN)) + +#define SLOT_PCIX(es) ((u8) ((es & HPC_SLOT_PCIX) \ + ? HPC_SLOT_PCIX_YES : HPC_SLOT_PCIX_NO)) + +#define SLOT_SPEED(es) ((u8) ((es & HPC_SLOT_SPEED2) \ + ? ((es & HPC_SLOT_SPEED1) ? HPC_SLOT_SPEED_133 \ + : HPC_SLOT_SPEED_66) \ + : HPC_SLOT_SPEED_33)) + +#define SLOT_BUS_MODE(es) ((u8) ((es & HPC_SLOT_BUS_MODE) \ + ? HPC_SLOT_BUS_MODE_MISM : HPC_SLOT_BUS_MODE_OK)) + +//-------------------------------------------------------------------------- +// macro for bus info +//--------------------------------------------------------------------------- +#define CURRENT_BUS_SPEED(s) ((u8) (s & BUS_SPEED_2) \ + ? ((s & BUS_SPEED_1) ? BUS_SPEED_133 : BUS_SPEED_100) \ + : ((s & BUS_SPEED_1) ? BUS_SPEED_66 : BUS_SPEED_33)) + +#define CURRENT_BUS_MODE(s) ((u8) (s & BUS_MODE) ? BUS_MODE_PCIX : BUS_MODE_PCI) + +#define READ_BUS_STATUS(s) ((u8) (s->options & BUS_STATUS_AVAILABLE)) + +#define READ_BUS_MODE(s) ((s->revision & PRGM_MODEL_REV_LEVEL) >= 0x20) + +#define SET_BUS_STATUS(s) ((u8) (s->options & BUS_CONTROL_AVAILABLE)) + +#define READ_SLOT_LATCH(s) ((u8) (s->options & SLOT_LATCH_REGS_SUPPORTED)) + +//---------------------------------------------------------------------------- +// macro for controller info +//---------------------------------------------------------------------------- +#define CTLR_WORKING(c) ((u8) ((c & HPC_CTLR_WORKING) \ + ? HPC_CTLR_WORKING_YES : HPC_CTLR_WORKING_NO)) +#define CTLR_FINISHED(c) ((u8) ((c & HPC_CTLR_FINISHED) \ + ? HPC_CTLR_FINISHED_YES : HPC_CTLR_FINISHED_NO)) +#define CTLR_RESULT(c) ((u8) ((c & HPC_CTLR_RESULT1) \ + ? ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_NORESP \ + : HPC_CTLR_RESULT_RSVD) \ + : ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_FAILED \ + : HPC_CTLR_RESULT_SUCCESS))) + +// command that affect the state machine of HPC +#define NEEDTOCHECK_CMDSTATUS(c) ((c == HPC_SLOT_OFF) || \ + (c == HPC_SLOT_ON) || \ + (c == HPC_CTLR_RESET) || \ + (c == HPC_BUS_33CONVMODE) || \ + (c == HPC_BUS_66CONVMODE) || \ + (c == HPC_BUS_66PCIXMODE) || \ + (c == HPC_BUS_100PCIXMODE) || \ + (c == HPC_BUS_133PCIXMODE) || \ + (c == HPC_ALLSLOT_OFF) || \ + (c == HPC_ALLSLOT_ON)) + + +/* Core part of the driver */ + +#define ENABLE 1 +#define DISABLE 0 + +#define CARD_INFO 0x07 +#define PCIX133 0x07 +#define PCIX66 0x05 +#define PCI66 0x04 + +extern struct pci_bus *ibmphp_pci_bus; + +/* Variables */ + +struct pci_func { + struct pci_dev *dev; /* from the OS */ + u8 busno; + u8 device; + u8 function; + struct resource_node *io[6]; + struct resource_node *mem[6]; + struct resource_node *pfmem[6]; + struct pci_func *next; + int devices[32]; /* for bridge config */ + u8 irq[4]; /* for interrupt config */ + u8 bus; /* flag for unconfiguring, to say if PPB */ +}; + +struct slot { + u8 bus; + u8 device; + u8 number; + u8 real_physical_slot_num; + char name[100]; + u32 capabilities; + u8 supported_speed; + u8 supported_bus_mode; + struct hotplug_slot *hotplug_slot; + struct controller *ctrl; + struct pci_func *func; + u8 irq[4]; + u8 flag; /* this is for disable slot and polling */ + int bit_mode; /* 0 = 32, 1 = 64 */ + u8 ctlr_index; + struct bus_info *bus_on; + struct list_head ibm_slot_list; + u8 status; + u8 ext_status; + u8 busstatus; +}; + +struct controller { + struct ebda_hpc_slot *slots; + struct ebda_hpc_bus *buses; + struct pci_dev *ctrl_dev; /* in case where controller is PCI */ + u8 starting_slot_num; /* starting and ending slot #'s this ctrl controls*/ + u8 ending_slot_num; + u8 revision; + u8 options; /* which options HPC supports */ + u8 status; + u8 ctlr_id; + u8 slot_count; + u8 bus_count; + u8 ctlr_relative_id; + u32 irq; + union { + struct isa_ctlr_access isa_ctlr; + struct pci_ctlr_access pci_ctlr; + struct wpeg_i2c_ctlr_access wpeg_ctlr; + } u; + u8 ctlr_type; + struct list_head ebda_hpc_list; +}; + +/* Functions */ + +extern int ibmphp_init_devno (struct slot **); /* This function is called from EBDA, so we need it not be static */ +extern int ibmphp_disable_slot (struct hotplug_slot *); /* This function is called from HPC, so we need it to not be static */ +extern int ibmphp_do_disable_slot (struct slot *slot_cur); +extern int ibmphp_update_slot_info (struct slot *); /* This function is called from HPC, so we need it to not be be static */ +extern int ibmphp_configure_card (struct pci_func *, u8); +extern int ibmphp_unconfigure_card (struct slot **, int); +extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops; + +static inline void long_delay (int delay) +{ + set_current_state (TASK_INTERRUPTIBLE); + schedule_timeout (delay); +} + +#endif //__IBMPHP_H + diff -Nru a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/ibmphp_core.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,1417 @@ +/* + * IBM Hot Plug Controller Driver + * + * Written By: Chuck Cole, Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation + * + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2002 IBM Corp. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../arch/i386/pci/pci.h" /* for struct irq_routing_table */ +#include "ibmphp.h" + +#define attn_on(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNON) +#define attn_off(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNOFF) +#define attn_LED_blink(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_BLINKLED) +#define get_ctrl_revision(sl, rev) ibmphp_hpc_readslot (sl, READ_REVLEVEL, rev) +#define get_hpc_options(sl, opt) ibmphp_hpc_readslot (sl, READ_HPCOPTIONS, opt) + +#define DRIVER_VERSION "0.6" +#define DRIVER_DESC "IBM Hot Plug PCI Controller Driver" + +int ibmphp_debug; + +static int debug; +MODULE_PARM (debug, "i"); +MODULE_PARM_DESC (debug, "Debugging mode enabled or not"); +MODULE_LICENSE ("GPL"); +MODULE_DESCRIPTION (DRIVER_DESC); + +struct pci_bus *ibmphp_pci_bus; +static int max_slots; + +static int irqs[16]; /* PIC mode IRQ's we're using so far (in case MPS tables don't provide default info for empty slots */ + +static int init_flag; + +/* +static int get_max_adapter_speed_1 (struct hotplug_slot *, u8 *, u8); + +static inline int get_max_adapter_speed (struct hotplug_slot *hs, u8 *value) +{ + return get_max_adapter_speed_1 (hs, value, 1); +} +*/ +static inline int get_cur_bus_info (struct slot **sl) +{ + int rc = 1; + struct slot * slot_cur = *sl; + + debug ("options = %x\n", slot_cur->ctrl->options); + debug ("revision = %x\n", slot_cur->ctrl->revision); + + if (READ_BUS_STATUS (slot_cur->ctrl)) + rc = ibmphp_hpc_readslot (slot_cur, READ_BUSSTATUS, NULL); + + if (rc) + return rc; + + slot_cur->bus_on->current_speed = CURRENT_BUS_SPEED (slot_cur->busstatus); + if (READ_BUS_MODE (slot_cur->ctrl)) + slot_cur->bus_on->current_bus_mode = CURRENT_BUS_MODE (slot_cur->busstatus); + else + slot_cur->bus_on->current_bus_mode = 0xFF; + + debug ("busstatus = %x, bus_speed = %x, bus_mode = %x\n", slot_cur->busstatus, slot_cur->bus_on->current_speed, slot_cur->bus_on->current_bus_mode); + + *sl = slot_cur; + return 0; +} + +static inline int slot_update (struct slot **sl) +{ + int rc; + rc = ibmphp_hpc_readslot (*sl, READ_ALLSTAT, NULL); + if (rc) + return rc; + if (!init_flag) + return get_cur_bus_info (sl); + return rc; +} + +static int __init get_max_slots (void) +{ + struct slot * slot_cur; + struct list_head * tmp; + u8 slot_count = 0; + + list_for_each (tmp, &ibmphp_slot_head) { + slot_cur = list_entry (tmp, struct slot, ibm_slot_list); + /* sometimes the hot-pluggable slots start with 4 (not always from 1 */ + slot_count = max (slot_count, slot_cur->number); + } + return slot_count; +} + +/* This routine will put the correct slot->device information per slot. It's + * called from initialization of the slot structures. It will also assign + * interrupt numbers per each slot. + * Parameters: struct slot + * Returns 0 or errors + */ +int ibmphp_init_devno (struct slot **cur_slot) +{ + struct irq_routing_table *rtable; + int len; + int loop; + int i; + + rtable = pcibios_get_irq_routing_table (); + if (!rtable) { + err ("no BIOS routing table...\n"); + return -ENOMEM; + } + + len = (rtable->size - sizeof (struct irq_routing_table)) / sizeof (struct irq_info); + + if (!len) + return -1; + for (loop = 0; loop < len; loop++) { + if ((*cur_slot)->number == rtable->slots[loop].slot) { + if ((*cur_slot)->bus == rtable->slots[loop].bus) { + (*cur_slot)->device = PCI_SLOT (rtable->slots[loop].devfn); + for (i = 0; i < 4; i++) + (*cur_slot)->irq[i] = IO_APIC_get_PCI_irq_vector ((int) (*cur_slot)->bus, (int) (*cur_slot)->device, i); + + debug ("(*cur_slot)->irq[0] = %x\n", (*cur_slot)->irq[0]); + debug ("(*cur_slot)->irq[1] = %x\n", (*cur_slot)->irq[1]); + debug ("(*cur_slot)->irq[2] = %x\n", (*cur_slot)->irq[2]); + debug ("(*cur_slot)->irq[3] = %x\n", (*cur_slot)->irq[3]); + + debug ("rtable->exlusive_irqs = %x\n", rtable->exclusive_irqs); + debug ("rtable->slots[loop].irq[0].bitmap = %x\n", rtable->slots[loop].irq[0].bitmap); + debug ("rtable->slots[loop].irq[1].bitmap = %x\n", rtable->slots[loop].irq[1].bitmap); + debug ("rtable->slots[loop].irq[2].bitmap = %x\n", rtable->slots[loop].irq[2].bitmap); + debug ("rtable->slots[loop].irq[3].bitmap = %x\n", rtable->slots[loop].irq[3].bitmap); + + debug ("rtable->slots[loop].irq[0].link= %x\n", rtable->slots[loop].irq[0].link); + debug ("rtable->slots[loop].irq[1].link = %x\n", rtable->slots[loop].irq[1].link); + debug ("rtable->slots[loop].irq[2].link = %x\n", rtable->slots[loop].irq[2].link); + debug ("rtable->slots[loop].irq[3].link = %x\n", rtable->slots[loop].irq[3].link); + debug ("end of init_devno\n"); + return 0; + } + } + } + + return -1; +} + +static inline int power_on (struct slot *slot_cur) +{ + u8 cmd = HPC_SLOT_ON; + int retval; + + retval = ibmphp_hpc_writeslot (slot_cur, cmd); + if (retval) { + err ("power on failed\n"); + return retval; + } + if (CTLR_RESULT (slot_cur->ctrl->status)) { + err ("command not completed successfully in power_on \n"); + return -EIO; + } + long_delay (3 * HZ); /* For ServeRAID cards, and some 66 PCI */ + return 0; +} + +static inline int power_off (struct slot *slot_cur) +{ + u8 cmd = HPC_SLOT_OFF; + int retval; + + retval = ibmphp_hpc_writeslot (slot_cur, cmd); + if (retval) { + err ("power off failed \n"); + return retval; + } + if (CTLR_RESULT (slot_cur->ctrl->status)) { + err ("command not completed successfully in power_off \n"); + return -EIO; + } + return 0; +} + +static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 value) +{ + int rc = 0; + struct slot *pslot; + u8 cmd; + int hpcrc = 0; + + debug ("set_attention_status - Entry hotplug_slot[%lx] value[%x]\n", (ulong) hotplug_slot, value); + ibmphp_lock_operations (); + cmd = 0x00; // avoid compiler warning + + if (hotplug_slot) { + switch (value) { + case HPC_SLOT_ATTN_OFF: + cmd = HPC_SLOT_ATTNOFF; + break; + case HPC_SLOT_ATTN_ON: + cmd = HPC_SLOT_ATTNON; + break; + case HPC_SLOT_ATTN_BLINK: + cmd = HPC_SLOT_BLINKLED; + break; + default: + rc = -ENODEV; + err ("set_attention_status - Error : invalid input [%x]\n", value); + break; + } + if (rc == 0) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) + hpcrc = ibmphp_hpc_writeslot (pslot, cmd); + else + rc = -ENODEV; + } + } else + rc = -ENODEV; + + if (hpcrc) + rc = hpcrc; + + ibmphp_unlock_operations (); + + debug ("set_attention_status - Exit rc[%d]\n", rc); + return rc; +} + +static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 * value) +{ + int rc = -ENODEV; + struct slot *pslot; + int hpcrc = 0; + struct slot myslot; + + debug ("get_attention_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); + + ibmphp_lock_operations (); + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + if (!hpcrc) + hpcrc = ibmphp_hpc_readslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status)); + if (!hpcrc) { + *value = SLOT_ATTN (myslot.status, myslot.ext_status); + rc = 0; + } + } + } else + rc = -ENODEV; + + if (hpcrc) + rc = hpcrc; + + ibmphp_unlock_operations (); + debug ("get_attention_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); + return rc; +} + +static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 * value) +{ + int rc = -ENODEV; + struct slot *pslot; + int hpcrc = 0; + struct slot myslot; + + debug ("get_latch_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); + ibmphp_lock_operations (); + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + if (!hpcrc) { + *value = SLOT_LATCH (myslot.status); + rc = 0; + } + } + } else + rc = -ENODEV; + + if (hpcrc) + rc = hpcrc; + + ibmphp_unlock_operations (); + debug ("get_latch_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); + return rc; +} + + +static int get_power_status (struct hotplug_slot *hotplug_slot, u8 * value) +{ + int rc = -ENODEV; + struct slot *pslot; + int hpcrc = 0; + struct slot myslot; + + debug ("get_power_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); + ibmphp_lock_operations (); + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + if (!hpcrc) { + *value = SLOT_PWRGD (myslot.status); + rc = 0; + } + } + } else + rc = -ENODEV; + + if (hpcrc) + rc = hpcrc; + + ibmphp_unlock_operations (); + debug ("get_power_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); + return rc; +} + +static int get_adapter_present (struct hotplug_slot *hotplug_slot, u8 * value) +{ + int rc = -ENODEV; + struct slot *pslot; + u8 present; + int hpcrc = 0; + struct slot myslot; + + debug ("get_adapter_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); + ibmphp_lock_operations (); + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + if (!hpcrc) { + present = SLOT_PRESENT (myslot.status); + if (present == HPC_SLOT_EMPTY) + *value = 0; + else + *value = 1; + rc = 0; + } + } + } else + rc = -ENODEV; + if (hpcrc) + rc = hpcrc; + + ibmphp_unlock_operations (); + debug ("get_adapter_present - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); + return rc; +} + +static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +{ + int rc = -ENODEV; + struct slot *pslot; + u8 mode = 0; + + debug ("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __FUNCTION__, + hotplug_slot, value); + + ibmphp_lock_operations (); + + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + rc = 0; + mode = pslot->supported_bus_mode; + *value = pslot->supported_speed; + switch (*value) { + case BUS_SPEED_33: + break; + case BUS_SPEED_66: + if (mode == BUS_MODE_PCIX) + *value += 0x01; + break; + case BUS_SPEED_100: + case BUS_SPEED_133: + *value = pslot->supported_speed + 0x01; + break; + default: + /* Note (will need to change): there would be soon 256, 512 also */ + rc = -ENODEV; + } + } + } else + rc = -ENODEV; + + ibmphp_unlock_operations (); + debug ("%s - Exit rc[%d] value[%x]\n", __FUNCTION__, rc, *value); + return rc; +} + +static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +{ + int rc = -ENODEV; + struct slot *pslot; + u8 mode = 0; + + debug ("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __FUNCTION__, + hotplug_slot, value); + + ibmphp_lock_operations (); + + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + rc = get_cur_bus_info (&pslot); + if (!rc) { + mode = pslot->bus_on->current_bus_mode; + *value = pslot->bus_on->current_speed; + switch (*value) { + case BUS_SPEED_33: + break; + case BUS_SPEED_66: + if (mode == BUS_MODE_PCIX) + *value += 0x01; + else if (mode == BUS_MODE_PCI) + ; + else + *value = PCI_SPEED_UNKNOWN; + break; + case BUS_SPEED_100: + case BUS_SPEED_133: + *value += 0x01; + break; + default: + /* Note of change: there would also be 256, 512 soon */ + rc = -ENODEV; + } + } + } + } else + rc = -ENODEV; + + ibmphp_unlock_operations (); + debug ("%s - Exit rc[%d] value[%x]\n", __FUNCTION__, rc, *value); + return rc; +} +/* +static int get_max_adapter_speed_1 (struct hotplug_slot *hotplug_slot, u8 * value, u8 flag) +{ + int rc = -ENODEV; + struct slot *pslot; + int hpcrc = 0; + struct slot myslot; + + debug ("get_max_adapter_speed_1 - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong)hotplug_slot, (ulong) value); + + if (flag) + ibmphp_lock_operations (); + + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + + if (!(SLOT_LATCH (myslot.status)) && (SLOT_PRESENT (myslot.status))) { + hpcrc = ibmphp_hpc_readslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status)); + if (!hpcrc) { + *value = SLOT_SPEED (myslot.ext_status); + rc = 0; + } + } else { + *value = MAX_ADAPTER_NONE; + rc = 0; + } + } + } else + rc = -ENODEV; + + if (hpcrc) + rc = hpcrc; + + if (flag) + ibmphp_unlock_operations (); + + debug ("get_max_adapter_speed_1 - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value); + return rc; +} + +static int get_bus_name (struct hotplug_slot *hotplug_slot, char * value) +{ + int rc = -ENODEV; + struct slot *pslot = NULL; + + debug ("get_bus_name - Entry hotplug_slot[%lx] \n", (ulong)hotplug_slot); + + ibmphp_lock_operations (); + + if (hotplug_slot) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + rc = 0; + snprintf (value, 100, "Bus %x", pslot->bus); + } + } else + rc = -ENODEV; + + ibmphp_unlock_operations (); + debug ("get_bus_name - Exit rc[%d] value[%x]\n", rc, *value); + return rc; +} +*/ + +/******************************************************************************* + * This routine will initialize the ops data structure used in the validate + * function. It will also power off empty slots that are powered on since BIOS + * leaves those on, albeit disconnected + ******************************************************************************/ +static int __init init_ops (void) +{ + struct slot *slot_cur; + struct list_head *tmp; + int retval; + int rc; + + list_for_each (tmp, &ibmphp_slot_head) { + slot_cur = list_entry (tmp, struct slot, ibm_slot_list); + + if (!slot_cur) + return -ENODEV; + + debug ("BEFORE GETTING SLOT STATUS, slot # %x\n", slot_cur->number); + if (slot_cur->ctrl->revision == 0xFF) + if (get_ctrl_revision (slot_cur, &slot_cur->ctrl->revision)) + return -1; + + if (slot_cur->bus_on->current_speed == 0xFF) + if (get_cur_bus_info (&slot_cur)) + return -1; + + if (slot_cur->ctrl->options == 0xFF) + if (get_hpc_options (slot_cur, &slot_cur->ctrl->options)) + return -1; + + retval = slot_update (&slot_cur); + if (retval) + return retval; + + debug ("status = %x\n", slot_cur->status); + debug ("ext_status = %x\n", slot_cur->ext_status); + debug ("SLOT_POWER = %x\n", SLOT_POWER (slot_cur->status)); + debug ("SLOT_PRESENT = %x\n", SLOT_PRESENT (slot_cur->status)); + debug ("SLOT_LATCH = %x\n", SLOT_LATCH (slot_cur->status)); + + if ((SLOT_PWRGD (slot_cur->status)) && + !(SLOT_PRESENT (slot_cur->status)) && + !(SLOT_LATCH (slot_cur->status))) { + debug ("BEFORE POWER OFF COMMAND\n"); + rc = power_off (slot_cur); + if (rc) + return rc; + + /* retval = slot_update (&slot_cur); + * if (retval) + * return retval; + * ibmphp_update_slot_info (slot_cur); + */ + } + } + init_flag = 0; + return 0; +} + +/* This operation will check whether the slot is within the bounds and + * the operation is valid to perform on that slot + * Parameters: slot, operation + * Returns: 0 or error codes + */ +static int validate (struct slot *slot_cur, int opn) +{ + int number; + int retval; + + if (!slot_cur) + return -ENODEV; + number = slot_cur->number; + if ((number > max_slots) || (number < 0)) + return -EBADSLT; + debug ("slot_number in validate is %d\n", slot_cur->number); + + retval = slot_update (&slot_cur); + if (retval) + return retval; + + switch (opn) { + case ENABLE: + if (!(SLOT_PWRGD (slot_cur->status)) && + (SLOT_PRESENT (slot_cur->status)) && + !(SLOT_LATCH (slot_cur->status))) + return 0; + break; + case DISABLE: + if ((SLOT_PWRGD (slot_cur->status)) && + (SLOT_PRESENT (slot_cur->status)) && + !(SLOT_LATCH (slot_cur->status))) + return 0; + break; + default: + break; + } + err ("validate failed....\n"); + return -EINVAL; +} + +/******************************************************************************** + * This routine is for updating the data structures in the hotplug core + * Parameters: struct slot + * Returns: 0 or error + *******************************************************************************/ +int ibmphp_update_slot_info (struct slot *slot_cur) +{ + struct hotplug_slot_info *info; + int rc; + u8 bus_speed; + u8 mode; + + info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); + if (!info) { + err ("out of system memory \n"); + return -ENOMEM; + } + + info->power_status = SLOT_PWRGD (slot_cur->status); + info->attention_status = SLOT_ATTN (slot_cur->status, slot_cur->ext_status); + info->latch_status = SLOT_LATCH (slot_cur->status); + if (!SLOT_PRESENT (slot_cur->status)) { + info->adapter_status = 0; +// info->max_adapter_speed_status = MAX_ADAPTER_NONE; + } else { + info->adapter_status = 1; +// get_max_adapter_speed_1 (slot_cur->hotplug_slot, &info->max_adapter_speed_status, 0); + } + + bus_speed = slot_cur->bus_on->current_speed; + mode = slot_cur->bus_on->current_bus_mode; + + switch (bus_speed) { + case BUS_SPEED_33: + break; + case BUS_SPEED_66: + if (mode == BUS_MODE_PCIX) + bus_speed += 0x01; + else if (mode == BUS_MODE_PCI) + ; + else + bus_speed = PCI_SPEED_UNKNOWN; + break; + case BUS_SPEED_100: + case BUS_SPEED_133: + bus_speed += 0x01; + break; + default: + bus_speed = PCI_SPEED_UNKNOWN; + } + + info->cur_bus_speed = bus_speed; + info->max_bus_speed = slot_cur->hotplug_slot->info->max_bus_speed; + // To do: bus_names + + rc = pci_hp_change_slot_info (slot_cur->hotplug_slot, info); + kfree (info); + return rc; +} + + +/****************************************************************************** + * This function will return the pci_func, given bus and devfunc, or NULL. It + * is called from visit routines + ******************************************************************************/ + +static struct pci_func *ibm_slot_find (u8 busno, u8 device, u8 function) +{ + struct pci_func *func_cur; + struct slot *slot_cur; + struct list_head * tmp; + list_for_each (tmp, &ibmphp_slot_head) { + slot_cur = list_entry (tmp, struct slot, ibm_slot_list); + if (slot_cur->func) { + func_cur = slot_cur->func; + while (func_cur) { + if ((func_cur->busno == busno) && (func_cur->device == device) && (func_cur->function == function)) + return func_cur; + func_cur = func_cur->next; + } + } + } + return NULL; +} + +/************************************************************* + * This routine frees up memory used by struct slot, including + * the pointers to pci_func, bus, hotplug_slot, controller, + * and deregistering from the hotplug core + *************************************************************/ +static void free_slots (void) +{ + struct slot *slot_cur; + struct list_head * tmp; + struct list_head * next; + + debug ("%s -- enter\n", __FUNCTION__); + + list_for_each_safe (tmp, next, &ibmphp_slot_head) { + + slot_cur = list_entry (tmp, struct slot, ibm_slot_list); + + pci_hp_deregister (slot_cur->hotplug_slot); + + if (slot_cur->hotplug_slot) { + kfree (slot_cur->hotplug_slot); + slot_cur->hotplug_slot = NULL; + } + + if (slot_cur->ctrl) + slot_cur->ctrl = NULL; + + if (slot_cur->bus_on) + slot_cur->bus_on = NULL; + + ibmphp_unconfigure_card (&slot_cur, -1); /* we don't want to actually remove the resources, since free_resources will do just that */ + + kfree (slot_cur); + slot_cur = NULL; + } + debug ("%s -- exit\n", __FUNCTION__); +} + +static int ibm_unconfigure_device (struct pci_func *func) +{ + struct pci_dev *temp; + u8 j; + + debug ("inside %s\n", __FUNCTION__); + debug ("func->device = %x, func->function = %x\n", func->device, func->function); + debug ("func->device << 3 | 0x0 = %x\n", func->device << 3 | 0x0); + + for (j = 0; j < 0x08; j++) { + temp = pci_find_slot (func->busno, (func->device << 3) | j); + if (temp) + pci_remove_bus_device(temp); + } + return 0; +} + +/* + * The following function is to fix kernel bug regarding + * getting bus entries, here we manually add those primary + * bus entries to kernel bus structure whenever apply + */ + +static u8 bus_structure_fixup (u8 busno) +{ + struct pci_bus *bus; + struct pci_dev *dev; + u16 l; + + if (pci_find_bus(busno) || !(ibmphp_find_same_bus_num (busno))) + return 1; + + bus = kmalloc (sizeof (*bus), GFP_KERNEL); + if (!bus) { + err ("%s - out of memory\n", __FUNCTION__); + return 1; + } + dev = kmalloc (sizeof (*dev), GFP_KERNEL); + if (!dev) { + kfree (bus); + err ("%s - out of memory\n", __FUNCTION__); + return 1; + } + + bus->number = busno; + bus->ops = ibmphp_pci_bus->ops; + dev->bus = bus; + for (dev->devfn = 0; dev->devfn < 256; dev->devfn += 8) { + if (!pci_read_config_word (dev, PCI_VENDOR_ID, &l) && l != 0x0000 && l != 0xffff) { + debug ("%s - Inside bus_struture_fixup() \n", __FUNCTION__); + pci_scan_bus (busno, ibmphp_pci_bus->ops, NULL); + break; + } + } + + kfree (dev); + kfree (bus); + + return 0; +} + +static int ibm_configure_device (struct pci_func *func) +{ + unsigned char bus; + struct pci_bus *child; + int num; + int flag = 0; /* this is to make sure we don't double scan the bus, for bridged devices primarily */ + + if (!(bus_structure_fixup (func->busno))) + flag = 1; + if (func->dev == NULL) + func->dev = pci_find_slot (func->busno, PCI_DEVFN(func->device, func->function)); + + if (func->dev == NULL) { + struct pci_bus *bus = pci_find_bus(func->busno); + if (!bus) + return 0; + + num = pci_scan_slot(bus, PCI_DEVFN(func->device, func->function)); + if (num) + pci_bus_add_devices(bus); + + func->dev = pci_find_slot(func->busno, PCI_DEVFN(func->device, func->function)); + if (func->dev == NULL) { + err ("ERROR... : pci_dev still NULL \n"); + return 0; + } + } + if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) { + pci_read_config_byte (func->dev, PCI_SECONDARY_BUS, &bus); + child = (struct pci_bus *) pci_add_new_bus (func->dev->bus, (func->dev), bus); + pci_do_scan_bus (child); + } + + return 0; +} + +/******************************************************* + * Returns whether the bus is empty or not + *******************************************************/ +static int is_bus_empty (struct slot * slot_cur) +{ + int rc; + struct slot * tmp_slot; + u8 i = slot_cur->bus_on->slot_min; + + while (i <= slot_cur->bus_on->slot_max) { + if (i == slot_cur->number) { + i++; + continue; + } + tmp_slot = ibmphp_get_slot_from_physical_num (i); + if (!tmp_slot) + return 0; + rc = slot_update (&tmp_slot); + if (rc) + return 0; + if (SLOT_PRESENT (tmp_slot->status) && SLOT_PWRGD (tmp_slot->status)) + return 0; + i++; + } + return 1; +} + +/*********************************************************** + * If the HPC permits and the bus currently empty, tries to set the + * bus speed and mode at the maximum card and bus capability + * Parameters: slot + * Returns: bus is set (0) or error code + ***********************************************************/ +static int set_bus (struct slot * slot_cur) +{ + int rc; + u8 speed; + u8 cmd = 0x0; + struct pci_dev * dev; + int retval; + + debug ("%s - entry slot # %d \n", __FUNCTION__, slot_cur->number); + if (SET_BUS_STATUS (slot_cur->ctrl) && is_bus_empty (slot_cur)) { + rc = slot_update (&slot_cur); + if (rc) + return rc; + speed = SLOT_SPEED (slot_cur->ext_status); + debug ("ext_status = %x, speed = %x\n", slot_cur->ext_status, speed); + switch (speed) { + case HPC_SLOT_SPEED_33: + cmd = HPC_BUS_33CONVMODE; + break; + case HPC_SLOT_SPEED_66: + if (SLOT_PCIX (slot_cur->ext_status)) { + if ((slot_cur->supported_speed >= BUS_SPEED_66) && (slot_cur->supported_bus_mode == BUS_MODE_PCIX)) + cmd = HPC_BUS_66PCIXMODE; + else if (!SLOT_BUS_MODE (slot_cur->ext_status)) + /* if max slot/bus capability is 66 pci + and there's no bus mode mismatch, then + the adapter supports 66 pci */ + cmd = HPC_BUS_66CONVMODE; + else + cmd = HPC_BUS_33CONVMODE; + } else { + if (slot_cur->supported_speed >= BUS_SPEED_66) + cmd = HPC_BUS_66CONVMODE; + else + cmd = HPC_BUS_33CONVMODE; + } + break; + case HPC_SLOT_SPEED_133: + switch (slot_cur->supported_speed) { + case BUS_SPEED_33: + cmd = HPC_BUS_33CONVMODE; + break; + case BUS_SPEED_66: + if (slot_cur->supported_bus_mode == BUS_MODE_PCIX) + cmd = HPC_BUS_66PCIXMODE; + else + cmd = HPC_BUS_66CONVMODE; + break; + case BUS_SPEED_100: + cmd = HPC_BUS_100PCIXMODE; + break; + case BUS_SPEED_133: + /* This is to take care of the bug in CIOBX chip */ + pci_for_each_dev(dev) { + if ((dev->vendor == PCI_VENDOR_ID_SERVERWORKS) && + (dev->device == 0x0101)) + ibmphp_hpc_writeslot (slot_cur, HPC_BUS_100PCIXMODE); + } + cmd = HPC_BUS_133PCIXMODE; + break; + default: + err ("Wrong bus speed \n"); + return -ENODEV; + } + break; + default: + err ("wrong slot speed \n"); + return -ENODEV; + } + debug ("setting bus speed for slot %d, cmd %x\n", slot_cur->number, cmd); + retval = ibmphp_hpc_writeslot (slot_cur, cmd); + if (retval) { + err ("setting bus speed failed\n"); + return retval; + } + if (CTLR_RESULT (slot_cur->ctrl->status)) { + err ("command not completed successfully in set_bus \n"); + return -EIO; + } + } + /* This is for x440, once Brandon fixes the firmware, + will not need this delay */ + long_delay (1 * HZ); + debug ("%s -Exit \n", __FUNCTION__); + return 0; +} + +/* This routine checks the bus limitations that the slot is on from the BIOS. + * This is used in deciding whether or not to power up the slot. + * (electrical/spec limitations. For example, >1 133 MHz or >2 66 PCI cards on + * same bus) + * Parameters: slot + * Returns: 0 = no limitations, -EINVAL = exceeded limitations on the bus + */ +static int check_limitations (struct slot *slot_cur) +{ + u8 i; + struct slot * tmp_slot; + u8 count = 0; + u8 limitation = 0; + + for (i = slot_cur->bus_on->slot_min; i <= slot_cur->bus_on->slot_max; i++) { + tmp_slot = ibmphp_get_slot_from_physical_num (i); + if (!tmp_slot) + return -ENODEV; + if ((SLOT_PWRGD (tmp_slot->status)) && !(SLOT_CONNECT (tmp_slot->status))) + count++; + } + get_cur_bus_info (&slot_cur); + switch (slot_cur->bus_on->current_speed) { + case BUS_SPEED_33: + limitation = slot_cur->bus_on->slots_at_33_conv; + break; + case BUS_SPEED_66: + if (slot_cur->bus_on->current_bus_mode == BUS_MODE_PCIX) + limitation = slot_cur->bus_on->slots_at_66_pcix; + else + limitation = slot_cur->bus_on->slots_at_66_conv; + break; + case BUS_SPEED_100: + limitation = slot_cur->bus_on->slots_at_100_pcix; + break; + case BUS_SPEED_133: + limitation = slot_cur->bus_on->slots_at_133_pcix; + break; + } + + if ((count + 1) > limitation) + return -EINVAL; + return 0; +} + +static inline void print_card_capability (struct slot *slot_cur) +{ + info ("capability of the card is "); + if ((slot_cur->ext_status & CARD_INFO) == PCIX133) + info (" 133 MHz PCI-X \n"); + else if ((slot_cur->ext_status & CARD_INFO) == PCIX66) + info (" 66 MHz PCI-X \n"); + else if ((slot_cur->ext_status & CARD_INFO) == PCI66) + info (" 66 MHz PCI \n"); + else + info (" 33 MHz PCI \n"); + +} + +/* This routine will power on the slot, configure the device(s) and find the + * drivers for them. + * Parameters: hotplug_slot + * Returns: 0 or failure codes + */ +static int enable_slot (struct hotplug_slot *hs) +{ + int rc, i, rcpr; + struct slot *slot_cur; + u8 function; + struct pci_func *tmp_func; + + ibmphp_lock_operations (); + + debug ("ENABLING SLOT........ \n"); + slot_cur = (struct slot *) hs->private; + + if ((rc = validate (slot_cur, ENABLE))) { + err ("validate function failed \n"); + goto error_nopower; + } + + attn_LED_blink (slot_cur); + + rc = set_bus (slot_cur); + if (rc) { + err ("was not able to set the bus \n"); + goto error_nopower; + } + + /*-----------------debugging------------------------------*/ + get_cur_bus_info (&slot_cur); + debug ("the current bus speed right after set_bus = %x \n", slot_cur->bus_on->current_speed); + /*----------------------------------------------------------*/ + + rc = check_limitations (slot_cur); + if (rc) { + err ("Adding this card exceeds the limitations of this bus.\n"); + err ("(i.e., >1 133MHz cards running on same bus, or " + ">2 66 PCI cards running on same bus\n."); + err ("Try hot-adding into another bus \n"); + rc = -EINVAL; + goto error_nopower; + } + + rc = power_on (slot_cur); + + if (rc) { + err ("something wrong when powering up... please see below for details\n"); + /* need to turn off before on, otherwise, blinking overwrites */ + attn_off(slot_cur); + attn_on (slot_cur); + if (slot_update (&slot_cur)) { + attn_off (slot_cur); + attn_on (slot_cur); + rc = -ENODEV; + goto exit; + } + /* Check to see the error of why it failed */ + if ((SLOT_POWER (slot_cur->status)) && !(SLOT_PWRGD (slot_cur->status))) + err ("power fault occurred trying to power up \n"); + else if (SLOT_BUS_SPEED (slot_cur->status)) { + err ("bus speed mismatch occurred. please check current bus speed and card capability \n"); + print_card_capability (slot_cur); + } else if (SLOT_BUS_MODE (slot_cur->ext_status)) { + err ("bus mode mismatch occurred. please check current bus mode and card capability \n"); + print_card_capability (slot_cur); + } + ibmphp_update_slot_info (slot_cur); + goto exit; + } + debug ("after power_on\n"); + /*-----------------------debugging---------------------------*/ + get_cur_bus_info (&slot_cur); + debug ("the current bus speed right after power_on = %x \n", slot_cur->bus_on->current_speed); + /*----------------------------------------------------------*/ + + rc = slot_update (&slot_cur); + if (rc) + goto error_power; + + rc = -EINVAL; + if (SLOT_POWER (slot_cur->status) && !(SLOT_PWRGD (slot_cur->status))) { + err ("power fault occurred trying to power up... \n"); + goto error_power; + } + if (SLOT_POWER (slot_cur->status) && (SLOT_BUS_SPEED (slot_cur->status))) { + err ("bus speed mismatch occurred. please check current bus speed and card capability \n"); + print_card_capability (slot_cur); + goto error_power; + } + /* Don't think this case will happen after above checks... but just in case, for paranoia sake */ + if (!(SLOT_POWER (slot_cur->status))) { + err ("power on failed... \n"); + goto error_power; + } + + slot_cur->func = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!slot_cur->func) { + /* We cannot do update_slot_info here, since no memory for + * kmalloc n.e.ways, and update_slot_info allocates some */ + err ("out of system memory \n"); + rc = -ENOMEM; + goto error_power; + } + memset (slot_cur->func, 0, sizeof (struct pci_func)); + slot_cur->func->busno = slot_cur->bus; + slot_cur->func->device = slot_cur->device; + for (i = 0; i < 4; i++) + slot_cur->func->irq[i] = slot_cur->irq[i]; + + debug ("b4 configure_card, slot_cur->bus = %x, slot_cur->device = %x\n", slot_cur->bus, slot_cur->device); + + if (ibmphp_configure_card (slot_cur->func, slot_cur->number)) { + err ("configure_card was unsuccessful... \n"); + ibmphp_unconfigure_card (&slot_cur, 1); /* true because don't need to actually deallocate resources, just remove references */ + debug ("after unconfigure_card\n"); + slot_cur->func = NULL; + rc = -ENOMEM; + goto error_power; + } + + function = 0x00; + do { + tmp_func = ibm_slot_find (slot_cur->bus, slot_cur->func->device, function++); + if (tmp_func && !(tmp_func->dev)) + ibm_configure_device (tmp_func); + } while (tmp_func); + + attn_off (slot_cur); + if (slot_update (&slot_cur)) { + rc = -EFAULT; + goto exit; + } + ibmphp_print_test (); + rc = ibmphp_update_slot_info (slot_cur); +exit: + ibmphp_unlock_operations(); + return rc; + +error_nopower: + attn_off (slot_cur); /* need to turn off if was blinking b4 */ + attn_on (slot_cur); +error_cont: + rcpr = slot_update (&slot_cur); + if (rcpr) { + rc = rcpr; + goto exit; + } + ibmphp_update_slot_info (slot_cur); + goto exit; + +error_power: + attn_off (slot_cur); /* need to turn off if was blinking b4 */ + attn_on (slot_cur); + rcpr = power_off (slot_cur); + if (rcpr) { + rc = rcpr; + goto exit; + } + goto error_cont; +} + +/************************************************************** +* HOT REMOVING ADAPTER CARD * +* INPUT: POINTER TO THE HOTPLUG SLOT STRUCTURE * +* OUTPUT: SUCCESS 0 ; FAILURE: UNCONFIGURE , VALIDATE * + DISABLE POWER , * +**************************************************************/ +int ibmphp_disable_slot (struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = hotplug_slot->private; + int rc; + + ibmphp_lock_operations(); + rc = ibmphp_do_disable_slot(slot); + ibmphp_unlock_operations(); + return rc; +} + +int ibmphp_do_disable_slot (struct slot *slot_cur) +{ + int rc; + u8 flag; + int parm = 0; + + debug ("DISABLING SLOT... \n"); + + if ((slot_cur == NULL) || (slot_cur->ctrl == NULL)) { + return -ENODEV; + } + + flag = slot_cur->flag; + slot_cur->flag = TRUE; + + if (flag == TRUE) { + rc = validate (slot_cur, DISABLE); /* checking if powered off already & valid slot # */ + if (rc) + goto error; + } + attn_LED_blink (slot_cur); + + if (slot_cur->func == NULL) { + /* We need this for fncs's that were there on bootup */ + slot_cur->func = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!slot_cur->func) { + err ("out of system memory \n"); + rc = -ENOMEM; + goto error; + } + memset (slot_cur->func, 0, sizeof (struct pci_func)); + slot_cur->func->busno = slot_cur->bus; + slot_cur->func->device = slot_cur->device; + } + + if ((rc = ibm_unconfigure_device (slot_cur->func))) { + err ("removing from kernel failed... \n"); + err ("Please check to see if it was statically linked or is " + "in use otherwise. (perhaps the driver is not 'hot-removable')\n"); + goto error; + } + + /* If we got here from latch suddenly opening on operating card or + a power fault, there's no power to the card, so cannot + read from it to determine what resources it occupied. This operation + is forbidden anyhow. The best we can do is remove it from kernel + lists at least */ + + if (!flag) { + attn_off (slot_cur); + return 0; + } + + rc = ibmphp_unconfigure_card (&slot_cur, parm); + slot_cur->func = NULL; + debug ("in disable_slot. after unconfigure_card\n"); + if (rc) { + err ("could not unconfigure card.\n"); + goto error; + } + + rc = ibmphp_hpc_writeslot (slot_cur, HPC_SLOT_OFF); + if (rc) + goto error; + + attn_off (slot_cur); + rc = slot_update (&slot_cur); + if (rc) + goto exit; + + rc = ibmphp_update_slot_info (slot_cur); + ibmphp_print_test (); +exit: + return rc; + +error: + /* Need to turn off if was blinking b4 */ + attn_off (slot_cur); + attn_on (slot_cur); + if (slot_update (&slot_cur)) { + rc = -EFAULT; + goto exit; + } + if (flag) + ibmphp_update_slot_info (slot_cur); + goto exit; +} + +struct hotplug_slot_ops ibmphp_hotplug_slot_ops = { + .owner = THIS_MODULE, + .set_attention_status = set_attention_status, + .enable_slot = enable_slot, + .disable_slot = ibmphp_disable_slot, + .hardware_test = NULL, + .get_power_status = get_power_status, + .get_attention_status = get_attention_status, + .get_latch_status = get_latch_status, + .get_adapter_status = get_adapter_present, + .get_max_bus_speed = get_max_bus_speed, + .get_cur_bus_speed = get_cur_bus_speed, +/* .get_max_adapter_speed = get_max_adapter_speed, + .get_bus_name_status = get_bus_name, +*/ +}; + +static void ibmphp_unload (void) +{ + free_slots (); + debug ("after slots \n"); + ibmphp_free_resources (); + debug ("after resources \n"); + ibmphp_free_bus_info_queue (); + debug ("after bus info \n"); + ibmphp_free_ebda_hpc_queue (); + debug ("after ebda hpc \n"); + ibmphp_free_ebda_pci_rsrc_queue (); + debug ("after ebda pci rsrc \n"); + kfree (ibmphp_pci_bus); +} + +static int __init ibmphp_init (void) +{ + struct pci_bus *bus; + int i = 0; + int rc = 0; + + init_flag = 1; + + info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); + + ibmphp_pci_bus = kmalloc (sizeof (*ibmphp_pci_bus), GFP_KERNEL); + if (!ibmphp_pci_bus) { + err ("out of memory\n"); + rc = -ENOMEM; + goto exit; + } + + bus = pci_find_bus(0); + if (!bus) { + err ("Can't find the root pci bus, can not continue\n"); + rc = -ENODEV; + goto error; + } + memcpy (ibmphp_pci_bus, bus, sizeof (*ibmphp_pci_bus)); + + ibmphp_debug = debug; + + ibmphp_hpc_initvars (); + + for (i = 0; i < 16; i++) + irqs[i] = 0; + + if ((rc = ibmphp_access_ebda ())) + goto error; + debug ("after ibmphp_access_ebda ()\n"); + + if ((rc = ibmphp_rsrc_init ())) + goto error; + debug ("AFTER Resource & EBDA INITIALIZATIONS\n"); + + max_slots = get_max_slots (); + + if ((rc = ibmphp_register_pci ())) + goto error; + + if (init_ops ()) { + rc = -ENODEV; + goto error; + } + + ibmphp_print_test (); + if ((rc = ibmphp_hpc_start_poll_thread ())) { + goto error; + } + + /* lock ourselves into memory with a module + * count of -1 so that no one can unload us. */ + module_put(THIS_MODULE); + +exit: + return rc; + +error: + ibmphp_unload (); + goto exit; +} + +static void __exit ibmphp_exit (void) +{ + ibmphp_hpc_stop_poll_thread (); + debug ("after polling\n"); + ibmphp_unload (); + debug ("done\n"); +} + +module_init (ibmphp_init); +module_exit (ibmphp_exit); diff -Nru a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/ibmphp_ebda.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,1228 @@ +/* + * IBM Hot Plug Controller Driver + * + * Written By: Tong Yu, IBM Corporation + * + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2002 IBM Corp. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ibmphp.h" + +/* + * POST builds data blocks(in this data block definition, a char-1 + * byte, short(or word)-2 byte, long(dword)-4 byte) in the Extended + * BIOS Data Area which describe the configuration of the hot-plug + * controllers and resources used by the PCI Hot-Plug devices. + * + * This file walks EBDA, maps data block from physical addr, + * reconstruct linked lists about all system resource(MEM, PFM, IO) + * already assigned by POST, as well as linked lists about hot plug + * controllers (ctlr#, slot#, bus&slot features...) + */ + +/* Global lists */ +LIST_HEAD (ibmphp_ebda_pci_rsrc_head); +LIST_HEAD (ibmphp_slot_head); + +/* Local variables */ +static struct ebda_hpc_list *hpc_list_ptr; +static struct ebda_rsrc_list *rsrc_list_ptr; +static struct rio_table_hdr *rio_table_ptr = NULL; +static LIST_HEAD (ebda_hpc_head); +static LIST_HEAD (bus_info_head); +static LIST_HEAD (rio_vg_head); +static LIST_HEAD (rio_lo_head); +static LIST_HEAD (opt_vg_head); +static LIST_HEAD (opt_lo_head); +static void *io_mem; + +/* Local functions */ +static int ebda_rsrc_controller (void); +static int ebda_rsrc_rsrc (void); +static int ebda_rio_table (void); + +static struct ebda_hpc_list * __init alloc_ebda_hpc_list (void) +{ + struct ebda_hpc_list *list; + + list = kmalloc (sizeof (struct ebda_hpc_list), GFP_KERNEL); + if (!list) + return NULL; + memset (list, 0, sizeof (*list)); + return list; +} + +static struct controller *alloc_ebda_hpc (u32 slot_count, u32 bus_count) +{ + struct controller *controller; + struct ebda_hpc_slot *slots; + struct ebda_hpc_bus *buses; + + controller = kmalloc (sizeof (struct controller), GFP_KERNEL); + if (!controller) + return NULL; + memset (controller, 0, sizeof (*controller)); + + slots = kmalloc (sizeof (struct ebda_hpc_slot) * slot_count, GFP_KERNEL); + if (!slots) { + kfree (controller); + return NULL; + } + memset (slots, 0, sizeof (*slots) * slot_count); + controller->slots = slots; + + buses = kmalloc (sizeof (struct ebda_hpc_bus) * bus_count, GFP_KERNEL); + if (!buses) { + kfree (controller->slots); + kfree (controller); + return NULL; + } + memset (buses, 0, sizeof (*buses) * bus_count); + controller->buses = buses; + + return controller; +} + +static void free_ebda_hpc (struct controller *controller) +{ + kfree (controller->slots); + controller->slots = NULL; + kfree (controller->buses); + controller->buses = NULL; + controller->ctrl_dev = NULL; + kfree (controller); +} + +static struct ebda_rsrc_list * __init alloc_ebda_rsrc_list (void) +{ + struct ebda_rsrc_list *list; + + list = kmalloc (sizeof (struct ebda_rsrc_list), GFP_KERNEL); + if (!list) + return NULL; + memset (list, 0, sizeof (*list)); + return list; +} + +static struct ebda_pci_rsrc *alloc_ebda_pci_rsrc (void) +{ + struct ebda_pci_rsrc *resource; + + resource = kmalloc (sizeof (struct ebda_pci_rsrc), GFP_KERNEL); + if (!resource) + return NULL; + memset (resource, 0, sizeof (*resource)); + return resource; +} + +static void __init print_bus_info (void) +{ + struct bus_info *ptr; + struct list_head *ptr1; + + list_for_each (ptr1, &bus_info_head) { + ptr = list_entry (ptr1, struct bus_info, bus_info_list); + debug ("%s - slot_min = %x\n", __FUNCTION__, ptr->slot_min); + debug ("%s - slot_max = %x\n", __FUNCTION__, ptr->slot_max); + debug ("%s - slot_count = %x\n", __FUNCTION__, ptr->slot_count); + debug ("%s - bus# = %x\n", __FUNCTION__, ptr->busno); + debug ("%s - current_speed = %x\n", __FUNCTION__, ptr->current_speed); + debug ("%s - controller_id = %x\n", __FUNCTION__, ptr->controller_id); + + debug ("%s - slots_at_33_conv = %x\n", __FUNCTION__, ptr->slots_at_33_conv); + debug ("%s - slots_at_66_conv = %x\n", __FUNCTION__, ptr->slots_at_66_conv); + debug ("%s - slots_at_66_pcix = %x\n", __FUNCTION__, ptr->slots_at_66_pcix); + debug ("%s - slots_at_100_pcix = %x\n", __FUNCTION__, ptr->slots_at_100_pcix); + debug ("%s - slots_at_133_pcix = %x\n", __FUNCTION__, ptr->slots_at_133_pcix); + + } +} + +static void print_lo_info (void) +{ + struct rio_detail *ptr; + struct list_head *ptr1; + debug ("print_lo_info ---- \n"); + list_for_each (ptr1, &rio_lo_head) { + ptr = list_entry (ptr1, struct rio_detail, rio_detail_list); + debug ("%s - rio_node_id = %x\n", __FUNCTION__, ptr->rio_node_id); + debug ("%s - rio_type = %x\n", __FUNCTION__, ptr->rio_type); + debug ("%s - owner_id = %x\n", __FUNCTION__, ptr->owner_id); + debug ("%s - first_slot_num = %x\n", __FUNCTION__, ptr->first_slot_num); + debug ("%s - wpindex = %x\n", __FUNCTION__, ptr->wpindex); + debug ("%s - chassis_num = %x\n", __FUNCTION__, ptr->chassis_num); + + } +} + +static void print_vg_info (void) +{ + struct rio_detail *ptr; + struct list_head *ptr1; + debug ("%s --- \n", __FUNCTION__); + list_for_each (ptr1, &rio_vg_head) { + ptr = list_entry (ptr1, struct rio_detail, rio_detail_list); + debug ("%s - rio_node_id = %x\n", __FUNCTION__, ptr->rio_node_id); + debug ("%s - rio_type = %x\n", __FUNCTION__, ptr->rio_type); + debug ("%s - owner_id = %x\n", __FUNCTION__, ptr->owner_id); + debug ("%s - first_slot_num = %x\n", __FUNCTION__, ptr->first_slot_num); + debug ("%s - wpindex = %x\n", __FUNCTION__, ptr->wpindex); + debug ("%s - chassis_num = %x\n", __FUNCTION__, ptr->chassis_num); + + } +} + +static void __init print_ebda_pci_rsrc (void) +{ + struct ebda_pci_rsrc *ptr; + struct list_head *ptr1; + + list_for_each (ptr1, &ibmphp_ebda_pci_rsrc_head) { + ptr = list_entry (ptr1, struct ebda_pci_rsrc, ebda_pci_rsrc_list); + debug ("%s - rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n", + __FUNCTION__, ptr->rsrc_type ,ptr->bus_num, ptr->dev_fun,ptr->start_addr, ptr->end_addr); + } +} + +static void __init print_ibm_slot (void) +{ + struct slot *ptr; + struct list_head *ptr1; + + list_for_each (ptr1, &ibmphp_slot_head) { + ptr = list_entry (ptr1, struct slot, ibm_slot_list); + debug ("%s - slot_number: %x \n", __FUNCTION__, ptr->number); + } +} + +static void __init print_opt_vg (void) +{ + struct opt_rio *ptr; + struct list_head *ptr1; + debug ("%s --- \n", __FUNCTION__); + list_for_each (ptr1, &opt_vg_head) { + ptr = list_entry (ptr1, struct opt_rio, opt_rio_list); + debug ("%s - rio_type %x \n", __FUNCTION__, ptr->rio_type); + debug ("%s - chassis_num: %x \n", __FUNCTION__, ptr->chassis_num); + debug ("%s - first_slot_num: %x \n", __FUNCTION__, ptr->first_slot_num); + debug ("%s - middle_num: %x \n", __FUNCTION__, ptr->middle_num); + } +} + +static void __init print_ebda_hpc (void) +{ + struct controller *hpc_ptr; + struct list_head *ptr1; + u16 index; + + list_for_each (ptr1, &ebda_hpc_head) { + + hpc_ptr = list_entry (ptr1, struct controller, ebda_hpc_list); + + for (index = 0; index < hpc_ptr->slot_count; index++) { + debug ("%s - physical slot#: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_num); + debug ("%s - pci bus# of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_bus_num); + debug ("%s - index into ctlr addr: %x\n", __FUNCTION__, hpc_ptr->slots[index].ctl_index); + debug ("%s - cap of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_cap); + } + + for (index = 0; index < hpc_ptr->bus_count; index++) { + debug ("%s - bus# of each bus controlled by this ctlr: %x\n", __FUNCTION__, hpc_ptr->buses[index].bus_num); + } + + debug ("%s - type of hpc: %x\n", __FUNCTION__, hpc_ptr->ctlr_type); + switch (hpc_ptr->ctlr_type) { + case 1: + debug ("%s - bus: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.bus); + debug ("%s - dev_fun: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.dev_fun); + debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq); + break; + + case 0: + debug ("%s - io_start: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_start); + debug ("%s - io_end: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_end); + debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq); + break; + + case 2: + case 4: + debug ("%s - wpegbbar: %lx\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.wpegbbar); + debug ("%s - i2c_addr: %x\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.i2c_addr); + debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq); + break; + } + } +} + +int __init ibmphp_access_ebda (void) +{ + u8 format, num_ctlrs, rio_complete, hs_complete; + u16 ebda_seg, num_entries, next_offset, offset, blk_id, sub_addr, rc, re, rc_id, re_id, base; + + + rio_complete = 0; + hs_complete = 0; + + io_mem = ioremap ((0x40 << 4) + 0x0e, 2); + if (!io_mem ) + return -ENOMEM; + ebda_seg = readw (io_mem); + iounmap (io_mem); + debug ("returned ebda segment: %x\n", ebda_seg); + + io_mem = ioremap (ebda_seg<<4, 65000); + if (!io_mem ) + return -ENOMEM; + next_offset = 0x180; + + for (;;) { + offset = next_offset; + next_offset = readw (io_mem + offset); /* offset of next blk */ + + offset += 2; + if (next_offset == 0) /* 0 indicate it's last blk */ + break; + blk_id = readw (io_mem + offset); /* this blk id */ + + offset += 2; + /* check if it is hot swap block or rio block */ + if (blk_id != 0x4853 && blk_id != 0x4752) + continue; + /* found hs table */ + if (blk_id == 0x4853) { + debug ("now enter hot swap block---\n"); + debug ("hot blk id: %x\n", blk_id); + format = readb (io_mem + offset); + + offset += 1; + if (format != 4) { + iounmap (io_mem); + return -ENODEV; + } + debug ("hot blk format: %x\n", format); + /* hot swap sub blk */ + base = offset; + + sub_addr = base; + re = readw (io_mem + sub_addr); /* next sub blk */ + + sub_addr += 2; + rc_id = readw (io_mem + sub_addr); /* sub blk id */ + + sub_addr += 2; + if (rc_id != 0x5243) { + iounmap (io_mem); + return -ENODEV; + } + /* rc sub blk signature */ + num_ctlrs = readb (io_mem + sub_addr); + + sub_addr += 1; + hpc_list_ptr = alloc_ebda_hpc_list (); + if (!hpc_list_ptr) { + iounmap (io_mem); + return -ENOMEM; + } + hpc_list_ptr->format = format; + hpc_list_ptr->num_ctlrs = num_ctlrs; + hpc_list_ptr->phys_addr = sub_addr; /* offset of RSRC_CONTROLLER blk */ + debug ("info about hpc descriptor---\n"); + debug ("hot blk format: %x\n", format); + debug ("num of controller: %x\n", num_ctlrs); + debug ("offset of hpc data structure enteries: %x\n ", sub_addr); + + sub_addr = base + re; /* re sub blk */ + rc = readw (io_mem + sub_addr); /* next sub blk */ + + sub_addr += 2; + re_id = readw (io_mem + sub_addr); /* sub blk id */ + + sub_addr += 2; + if (re_id != 0x5245) { + iounmap (io_mem); + return -ENODEV; + } + + /* signature of re */ + num_entries = readw (io_mem + sub_addr); + + sub_addr += 2; /* offset of RSRC_ENTRIES blk */ + rsrc_list_ptr = alloc_ebda_rsrc_list (); + if (!rsrc_list_ptr ) { + iounmap (io_mem); + return -ENOMEM; + } + rsrc_list_ptr->format = format; + rsrc_list_ptr->num_entries = num_entries; + rsrc_list_ptr->phys_addr = sub_addr; + + debug ("info about rsrc descriptor---\n"); + debug ("format: %x\n", format); + debug ("num of rsrc: %x\n", num_entries); + debug ("offset of rsrc data structure enteries: %x\n ", sub_addr); + + hs_complete = 1; + } + /* found rio table */ + else if (blk_id == 0x4752) { + debug ("now enter io table ---\n"); + debug ("rio blk id: %x\n", blk_id); + + rio_table_ptr = kmalloc (sizeof (struct rio_table_hdr), GFP_KERNEL); + if (!rio_table_ptr) + return -ENOMEM; + memset (rio_table_ptr, 0, sizeof (struct rio_table_hdr) ); + rio_table_ptr->ver_num = readb (io_mem + offset); + rio_table_ptr->scal_count = readb (io_mem + offset + 1); + rio_table_ptr->riodev_count = readb (io_mem + offset + 2); + rio_table_ptr->offset = offset +3 ; + + debug ("info about rio table hdr ---\n"); + debug ("ver_num: %x\nscal_count: %x\nriodev_count: %x\noffset of rio table: %x\n ", rio_table_ptr->ver_num, rio_table_ptr->scal_count, rio_table_ptr->riodev_count, rio_table_ptr->offset); + + rio_complete = 1; + } + } + + if (!hs_complete && !rio_complete) { + iounmap (io_mem); + return -ENODEV; + } + + if (rio_table_ptr) { + if (rio_complete == 1 && rio_table_ptr->ver_num == 3) { + rc = ebda_rio_table (); + if (rc) { + iounmap (io_mem); + return rc; + } + } + } + rc = ebda_rsrc_controller (); + if (rc) { + iounmap (io_mem); + return rc; + } + + rc = ebda_rsrc_rsrc (); + if (rc) { + iounmap (io_mem); + return rc; + } + + iounmap (io_mem); + return 0; +} + +/* + * map info of scalability details and rio details from physical address + */ +static int __init ebda_rio_table (void) +{ + u16 offset; + u8 i; + struct rio_detail *rio_detail_ptr; + + offset = rio_table_ptr->offset; + offset += 12 * rio_table_ptr->scal_count; + + // we do concern about rio details + for (i = 0; i < rio_table_ptr->riodev_count; i++) { + rio_detail_ptr = kmalloc (sizeof (struct rio_detail), GFP_KERNEL); + if (!rio_detail_ptr) + return -ENOMEM; + memset (rio_detail_ptr, 0, sizeof (struct rio_detail)); + rio_detail_ptr->rio_node_id = readb (io_mem + offset); + rio_detail_ptr->bbar = readl (io_mem + offset + 1); + rio_detail_ptr->rio_type = readb (io_mem + offset + 5); + rio_detail_ptr->owner_id = readb (io_mem + offset + 6); + rio_detail_ptr->port0_node_connect = readb (io_mem + offset + 7); + rio_detail_ptr->port0_port_connect = readb (io_mem + offset + 8); + rio_detail_ptr->port1_node_connect = readb (io_mem + offset + 9); + rio_detail_ptr->port1_port_connect = readb (io_mem + offset + 10); + rio_detail_ptr->first_slot_num = readb (io_mem + offset + 11); + rio_detail_ptr->status = readb (io_mem + offset + 12); + rio_detail_ptr->wpindex = readb (io_mem + offset + 13); + rio_detail_ptr->chassis_num = readb (io_mem + offset + 14); +// debug ("rio_node_id: %x\nbbar: %x\nrio_type: %x\nowner_id: %x\nport0_node: %x\nport0_port: %x\nport1_node: %x\nport1_port: %x\nfirst_slot_num: %x\nstatus: %x\n", rio_detail_ptr->rio_node_id, rio_detail_ptr->bbar, rio_detail_ptr->rio_type, rio_detail_ptr->owner_id, rio_detail_ptr->port0_node_connect, rio_detail_ptr->port0_port_connect, rio_detail_ptr->port1_node_connect, rio_detail_ptr->port1_port_connect, rio_detail_ptr->first_slot_num, rio_detail_ptr->status); + //create linked list of chassis + if (rio_detail_ptr->rio_type == 4 || rio_detail_ptr->rio_type == 5) + list_add (&rio_detail_ptr->rio_detail_list, &rio_vg_head); + //create linked list of expansion box + else if (rio_detail_ptr->rio_type == 6 || rio_detail_ptr->rio_type == 7) + list_add (&rio_detail_ptr->rio_detail_list, &rio_lo_head); + else + // not in my concern + kfree (rio_detail_ptr); + offset += 15; + } + print_lo_info (); + print_vg_info (); + return 0; +} + +/* + * reorganizing linked list of chassis + */ +static struct opt_rio *search_opt_vg (u8 chassis_num) +{ + struct opt_rio *ptr; + struct list_head *ptr1; + list_for_each (ptr1, &opt_vg_head) { + ptr = list_entry (ptr1, struct opt_rio, opt_rio_list); + if (ptr->chassis_num == chassis_num) + return ptr; + } + return NULL; +} + +static int __init combine_wpg_for_chassis (void) +{ + struct opt_rio *opt_rio_ptr = NULL; + struct rio_detail *rio_detail_ptr = NULL; + struct list_head *list_head_ptr = NULL; + + list_for_each (list_head_ptr, &rio_vg_head) { + rio_detail_ptr = list_entry (list_head_ptr, struct rio_detail, rio_detail_list); + opt_rio_ptr = search_opt_vg (rio_detail_ptr->chassis_num); + if (!opt_rio_ptr) { + opt_rio_ptr = (struct opt_rio *) kmalloc (sizeof (struct opt_rio), GFP_KERNEL); + if (!opt_rio_ptr) + return -ENOMEM; + memset (opt_rio_ptr, 0, sizeof (struct opt_rio)); + opt_rio_ptr->rio_type = rio_detail_ptr->rio_type; + opt_rio_ptr->chassis_num = rio_detail_ptr->chassis_num; + opt_rio_ptr->first_slot_num = rio_detail_ptr->first_slot_num; + opt_rio_ptr->middle_num = rio_detail_ptr->first_slot_num; + list_add (&opt_rio_ptr->opt_rio_list, &opt_vg_head); + } else { + opt_rio_ptr->first_slot_num = min (opt_rio_ptr->first_slot_num, rio_detail_ptr->first_slot_num); + opt_rio_ptr->middle_num = max (opt_rio_ptr->middle_num, rio_detail_ptr->first_slot_num); + } + } + print_opt_vg (); + return 0; +} + +/* + * reorgnizing linked list of expansion box + */ +static struct opt_rio_lo *search_opt_lo (u8 chassis_num) +{ + struct opt_rio_lo *ptr; + struct list_head *ptr1; + list_for_each (ptr1, &opt_lo_head) { + ptr = list_entry (ptr1, struct opt_rio_lo, opt_rio_lo_list); + if (ptr->chassis_num == chassis_num) + return ptr; + } + return NULL; +} + +static int combine_wpg_for_expansion (void) +{ + struct opt_rio_lo *opt_rio_lo_ptr = NULL; + struct rio_detail *rio_detail_ptr = NULL; + struct list_head *list_head_ptr = NULL; + + list_for_each (list_head_ptr, &rio_lo_head) { + rio_detail_ptr = list_entry (list_head_ptr, struct rio_detail, rio_detail_list); + opt_rio_lo_ptr = search_opt_lo (rio_detail_ptr->chassis_num); + if (!opt_rio_lo_ptr) { + opt_rio_lo_ptr = (struct opt_rio_lo *) kmalloc (sizeof (struct opt_rio_lo), GFP_KERNEL); + if (!opt_rio_lo_ptr) + return -ENOMEM; + memset (opt_rio_lo_ptr, 0, sizeof (struct opt_rio_lo)); + opt_rio_lo_ptr->rio_type = rio_detail_ptr->rio_type; + opt_rio_lo_ptr->chassis_num = rio_detail_ptr->chassis_num; + opt_rio_lo_ptr->first_slot_num = rio_detail_ptr->first_slot_num; + opt_rio_lo_ptr->middle_num = rio_detail_ptr->first_slot_num; + opt_rio_lo_ptr->pack_count = 1; + + list_add (&opt_rio_lo_ptr->opt_rio_lo_list, &opt_lo_head); + } else { + opt_rio_lo_ptr->first_slot_num = min (opt_rio_lo_ptr->first_slot_num, rio_detail_ptr->first_slot_num); + opt_rio_lo_ptr->middle_num = max (opt_rio_lo_ptr->middle_num, rio_detail_ptr->first_slot_num); + opt_rio_lo_ptr->pack_count = 2; + } + } + return 0; +} + + +/* Since we don't know the max slot number per each chassis, hence go + * through the list of all chassis to find out the range + * Arguments: slot_num, 1st slot number of the chassis we think we are on, + * var (0 = chassis, 1 = expansion box) + */ +static int first_slot_num (u8 slot_num, u8 first_slot, u8 var) +{ + struct opt_rio *opt_vg_ptr = NULL; + struct opt_rio_lo *opt_lo_ptr = NULL; + struct list_head *ptr = NULL; + int rc = 0; + + if (!var) { + list_for_each (ptr, &opt_vg_head) { + opt_vg_ptr = list_entry (ptr, struct opt_rio, opt_rio_list); + if ((first_slot < opt_vg_ptr->first_slot_num) && (slot_num >= opt_vg_ptr->first_slot_num)) { + rc = -ENODEV; + break; + } + } + } else { + list_for_each (ptr, &opt_lo_head) { + opt_lo_ptr = list_entry (ptr, struct opt_rio_lo, opt_rio_lo_list); + if ((first_slot < opt_lo_ptr->first_slot_num) && (slot_num >= opt_lo_ptr->first_slot_num)) { + rc = -ENODEV; + break; + } + } + } + return rc; +} + +static struct opt_rio_lo * find_rxe_num (u8 slot_num) +{ + struct opt_rio_lo *opt_lo_ptr; + struct list_head *ptr; + + list_for_each (ptr, &opt_lo_head) { + opt_lo_ptr = list_entry (ptr, struct opt_rio_lo, opt_rio_lo_list); + //check to see if this slot_num belongs to expansion box + if ((slot_num >= opt_lo_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_lo_ptr->first_slot_num, 1))) + return opt_lo_ptr; + } + return NULL; +} + +static struct opt_rio * find_chassis_num (u8 slot_num) +{ + struct opt_rio *opt_vg_ptr; + struct list_head *ptr; + + list_for_each (ptr, &opt_vg_head) { + opt_vg_ptr = list_entry (ptr, struct opt_rio, opt_rio_list); + //check to see if this slot_num belongs to chassis + if ((slot_num >= opt_vg_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_vg_ptr->first_slot_num, 0))) + return opt_vg_ptr; + } + return NULL; +} + +/* This routine will find out how many slots are in the chassis, so that + * the slot numbers for rxe100 would start from 1, and not from 7, or 6 etc + */ +static u8 calculate_first_slot (u8 slot_num) +{ + u8 first_slot = 1; + struct list_head * list; + struct slot * slot_cur; + + list_for_each (list, &ibmphp_slot_head) { + slot_cur = list_entry (list, struct slot, ibm_slot_list); + if (slot_cur->ctrl) { + if ((slot_cur->ctrl->ctlr_type != 4) && (slot_cur->ctrl->ending_slot_num > first_slot) && (slot_num > slot_cur->ctrl->ending_slot_num)) + first_slot = slot_cur->ctrl->ending_slot_num; + } + } + return first_slot + 1; + +} +static char *create_file_name (struct slot * slot_cur) +{ + struct opt_rio *opt_vg_ptr = NULL; + struct opt_rio_lo *opt_lo_ptr = NULL; + static char str[30]; + int which = 0; /* rxe = 1, chassis = 0 */ + u8 number = 1; /* either chassis or rxe # */ + u8 first_slot = 1; + u8 slot_num; + u8 flag = 0; + + if (!slot_cur) { + err ("Structure passed is empty \n"); + return NULL; + } + + slot_num = slot_cur->number; + + memset (str, 0, sizeof(str)); + + if (rio_table_ptr) { + if (rio_table_ptr->ver_num == 3) { + opt_vg_ptr = find_chassis_num (slot_num); + opt_lo_ptr = find_rxe_num (slot_num); + } + } + if (opt_vg_ptr) { + if (opt_lo_ptr) { + if ((slot_num - opt_vg_ptr->first_slot_num) > (slot_num - opt_lo_ptr->first_slot_num)) { + number = opt_lo_ptr->chassis_num; + first_slot = opt_lo_ptr->first_slot_num; + which = 1; /* it is RXE */ + } else { + first_slot = opt_vg_ptr->first_slot_num; + number = opt_vg_ptr->chassis_num; + which = 0; + } + } else { + first_slot = opt_vg_ptr->first_slot_num; + number = opt_vg_ptr->chassis_num; + which = 0; + } + ++flag; + } else if (opt_lo_ptr) { + number = opt_lo_ptr->chassis_num; + first_slot = opt_lo_ptr->first_slot_num; + which = 1; + ++flag; + } else if (rio_table_ptr) { + if (rio_table_ptr->ver_num == 3) { + /* if both NULL and we DO have correct RIO table in BIOS */ + return NULL; + } + } + if (!flag) { + if (slot_cur->ctrl->ctlr_type == 4) { + first_slot = calculate_first_slot (slot_num); + which = 1; + } else { + which = 0; + } + } + + sprintf(str, "%s%dslot%d", + which == 0 ? "chassis" : "rxe", + number, slot_num - first_slot + 1); + return str; +} + +static struct pci_driver ibmphp_driver; + +/* + * map info (ctlr-id, slot count, slot#.. bus count, bus#, ctlr type...) of + * each hpc from physical address to a list of hot plug controllers based on + * hpc descriptors. + */ +static int __init ebda_rsrc_controller (void) +{ + u16 addr, addr_slot, addr_bus; + u8 ctlr_id, temp, bus_index; + u16 ctlr, slot, bus; + u16 slot_num, bus_num, index; + struct hotplug_slot *hp_slot_ptr; + struct controller *hpc_ptr; + struct ebda_hpc_bus *bus_ptr; + struct ebda_hpc_slot *slot_ptr; + struct bus_info *bus_info_ptr1, *bus_info_ptr2; + int rc; + struct slot *tmp_slot; + struct list_head *list; + + addr = hpc_list_ptr->phys_addr; + for (ctlr = 0; ctlr < hpc_list_ptr->num_ctlrs; ctlr++) { + bus_index = 1; + ctlr_id = readb (io_mem + addr); + addr += 1; + slot_num = readb (io_mem + addr); + + addr += 1; + addr_slot = addr; /* offset of slot structure */ + addr += (slot_num * 4); + + bus_num = readb (io_mem + addr); + + addr += 1; + addr_bus = addr; /* offset of bus */ + addr += (bus_num * 9); /* offset of ctlr_type */ + temp = readb (io_mem + addr); + + addr += 1; + /* init hpc structure */ + hpc_ptr = alloc_ebda_hpc (slot_num, bus_num); + if (!hpc_ptr ) { + rc = -ENOMEM; + goto error_no_hpc; + } + hpc_ptr->ctlr_id = ctlr_id; + hpc_ptr->ctlr_relative_id = ctlr; + hpc_ptr->slot_count = slot_num; + hpc_ptr->bus_count = bus_num; + debug ("now enter ctlr data struture ---\n"); + debug ("ctlr id: %x\n", ctlr_id); + debug ("ctlr_relative_id: %x\n", hpc_ptr->ctlr_relative_id); + debug ("count of slots controlled by this ctlr: %x\n", slot_num); + debug ("count of buses controlled by this ctlr: %x\n", bus_num); + + /* init slot structure, fetch slot, bus, cap... */ + slot_ptr = hpc_ptr->slots; + for (slot = 0; slot < slot_num; slot++) { + slot_ptr->slot_num = readb (io_mem + addr_slot); + slot_ptr->slot_bus_num = readb (io_mem + addr_slot + slot_num); + slot_ptr->ctl_index = readb (io_mem + addr_slot + 2*slot_num); + slot_ptr->slot_cap = readb (io_mem + addr_slot + 3*slot_num); + + // create bus_info lined list --- if only one slot per bus: slot_min = slot_max + + bus_info_ptr2 = ibmphp_find_same_bus_num (slot_ptr->slot_bus_num); + if (!bus_info_ptr2) { + bus_info_ptr1 = (struct bus_info *) kmalloc (sizeof (struct bus_info), GFP_KERNEL); + if (!bus_info_ptr1) { + rc = -ENOMEM; + goto error_no_hp_slot; + } + memset (bus_info_ptr1, 0, sizeof (struct bus_info)); + bus_info_ptr1->slot_min = slot_ptr->slot_num; + bus_info_ptr1->slot_max = slot_ptr->slot_num; + bus_info_ptr1->slot_count += 1; + bus_info_ptr1->busno = slot_ptr->slot_bus_num; + bus_info_ptr1->index = bus_index++; + bus_info_ptr1->current_speed = 0xff; + bus_info_ptr1->current_bus_mode = 0xff; + + bus_info_ptr1->controller_id = hpc_ptr->ctlr_id; + + list_add_tail (&bus_info_ptr1->bus_info_list, &bus_info_head); + + } else { + bus_info_ptr2->slot_min = min (bus_info_ptr2->slot_min, slot_ptr->slot_num); + bus_info_ptr2->slot_max = max (bus_info_ptr2->slot_max, slot_ptr->slot_num); + bus_info_ptr2->slot_count += 1; + + } + + // end of creating the bus_info linked list + + slot_ptr++; + addr_slot += 1; + } + + /* init bus structure */ + bus_ptr = hpc_ptr->buses; + for (bus = 0; bus < bus_num; bus++) { + bus_ptr->bus_num = readb (io_mem + addr_bus + bus); + bus_ptr->slots_at_33_conv = readb (io_mem + addr_bus + bus_num + 8 * bus); + bus_ptr->slots_at_66_conv = readb (io_mem + addr_bus + bus_num + 8 * bus + 1); + + bus_ptr->slots_at_66_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 2); + + bus_ptr->slots_at_100_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 3); + + bus_ptr->slots_at_133_pcix = readb (io_mem + addr_bus + bus_num + 8 * bus + 4); + + bus_info_ptr2 = ibmphp_find_same_bus_num (bus_ptr->bus_num); + if (bus_info_ptr2) { + bus_info_ptr2->slots_at_33_conv = bus_ptr->slots_at_33_conv; + bus_info_ptr2->slots_at_66_conv = bus_ptr->slots_at_66_conv; + bus_info_ptr2->slots_at_66_pcix = bus_ptr->slots_at_66_pcix; + bus_info_ptr2->slots_at_100_pcix = bus_ptr->slots_at_100_pcix; + bus_info_ptr2->slots_at_133_pcix = bus_ptr->slots_at_133_pcix; + } + bus_ptr++; + } + + hpc_ptr->ctlr_type = temp; + + switch (hpc_ptr->ctlr_type) { + case 1: + hpc_ptr->u.pci_ctlr.bus = readb (io_mem + addr); + hpc_ptr->u.pci_ctlr.dev_fun = readb (io_mem + addr + 1); + hpc_ptr->irq = readb (io_mem + addr + 2); + addr += 3; + debug ("ctrl bus = %x, ctlr devfun = %x, irq = %x\n", + hpc_ptr->u.pci_ctlr.bus, + hpc_ptr->u.pci_ctlr.dev_fun, hpc_ptr->irq); + break; + + case 0: + hpc_ptr->u.isa_ctlr.io_start = readw (io_mem + addr); + hpc_ptr->u.isa_ctlr.io_end = readw (io_mem + addr + 2); + if (!request_region (hpc_ptr->u.isa_ctlr.io_start, + (hpc_ptr->u.isa_ctlr.io_end - hpc_ptr->u.isa_ctlr.io_start + 1), + "ibmphp")) { + rc = -ENODEV; + goto error_no_hp_slot; + } + hpc_ptr->irq = readb (io_mem + addr + 4); + addr += 5; + break; + + case 2: + case 4: + hpc_ptr->u.wpeg_ctlr.wpegbbar = readl (io_mem + addr); + hpc_ptr->u.wpeg_ctlr.i2c_addr = readb (io_mem + addr + 4); + hpc_ptr->irq = readb (io_mem + addr + 5); + addr += 6; + break; + default: + rc = -ENODEV; + goto error_no_hp_slot; + } + + //reorganize chassis' linked list + combine_wpg_for_chassis (); + combine_wpg_for_expansion (); + hpc_ptr->revision = 0xff; + hpc_ptr->options = 0xff; + hpc_ptr->starting_slot_num = hpc_ptr->slots[0].slot_num; + hpc_ptr->ending_slot_num = hpc_ptr->slots[slot_num-1].slot_num; + + // register slots with hpc core as well as create linked list of ibm slot + for (index = 0; index < hpc_ptr->slot_count; index++) { + + hp_slot_ptr = (struct hotplug_slot *) kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL); + if (!hp_slot_ptr) { + rc = -ENOMEM; + goto error_no_hp_slot; + } + memset (hp_slot_ptr, 0, sizeof (struct hotplug_slot)); + + hp_slot_ptr->info = (struct hotplug_slot_info *) kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); + if (!hp_slot_ptr->info) { + rc = -ENOMEM; + goto error_no_hp_info; + } + memset (hp_slot_ptr->info, 0, sizeof (struct hotplug_slot_info)); + + hp_slot_ptr->name = (char *) kmalloc (30, GFP_KERNEL); + if (!hp_slot_ptr->name) { + rc = -ENOMEM; + goto error_no_hp_name; + } + + tmp_slot = kmalloc (sizeof (struct slot), GFP_KERNEL); + if (!tmp_slot) { + rc = -ENOMEM; + goto error_no_slot; + } + memset (tmp_slot, 0, sizeof (*tmp_slot)); + + tmp_slot->flag = TRUE; + + tmp_slot->capabilities = hpc_ptr->slots[index].slot_cap; + if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_133_MAX) == EBDA_SLOT_133_MAX) + tmp_slot->supported_speed = 3; + else if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_100_MAX) == EBDA_SLOT_100_MAX) + tmp_slot->supported_speed = 2; + else if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_66_MAX) == EBDA_SLOT_66_MAX) + tmp_slot->supported_speed = 1; + + if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_PCIX_CAP) == EBDA_SLOT_PCIX_CAP) + tmp_slot->supported_bus_mode = 1; + else + tmp_slot->supported_bus_mode = 0; + + + tmp_slot->bus = hpc_ptr->slots[index].slot_bus_num; + + bus_info_ptr1 = ibmphp_find_same_bus_num (hpc_ptr->slots[index].slot_bus_num); + if (!bus_info_ptr1) { + rc = -ENODEV; + goto error; + } + tmp_slot->bus_on = bus_info_ptr1; + bus_info_ptr1 = NULL; + tmp_slot->ctrl = hpc_ptr; + + tmp_slot->ctlr_index = hpc_ptr->slots[index].ctl_index; + tmp_slot->number = hpc_ptr->slots[index].slot_num; + tmp_slot->hotplug_slot = hp_slot_ptr; + + hp_slot_ptr->private = tmp_slot; + + rc = ibmphp_hpc_fillhpslotinfo (hp_slot_ptr); + if (rc) + goto error; + + rc = ibmphp_init_devno ((struct slot **) &hp_slot_ptr->private); + if (rc) + goto error; + hp_slot_ptr->ops = &ibmphp_hotplug_slot_ops; + + // end of registering ibm slot with hotplug core + + list_add (& ((struct slot *)(hp_slot_ptr->private))->ibm_slot_list, &ibmphp_slot_head); + } + + print_bus_info (); + list_add (&hpc_ptr->ebda_hpc_list, &ebda_hpc_head ); + + } /* each hpc */ + + list_for_each (list, &ibmphp_slot_head) { + tmp_slot = list_entry (list, struct slot, ibm_slot_list); + + snprintf (tmp_slot->hotplug_slot->name, 30, "%s", create_file_name (tmp_slot)); + pci_hp_register (tmp_slot->hotplug_slot); + } + + print_ebda_hpc (); + print_ibm_slot (); + return 0; + +error: + kfree (hp_slot_ptr->private); +error_no_slot: + kfree (hp_slot_ptr->name); +error_no_hp_name: + kfree (hp_slot_ptr->info); +error_no_hp_info: + kfree (hp_slot_ptr); +error_no_hp_slot: + free_ebda_hpc (hpc_ptr); +error_no_hpc: + iounmap (io_mem); + return rc; +} + +/* + * map info (bus, devfun, start addr, end addr..) of i/o, memory, + * pfm from the physical addr to a list of resource. + */ +static int __init ebda_rsrc_rsrc (void) +{ + u16 addr; + short rsrc; + u8 type, rsrc_type; + struct ebda_pci_rsrc *rsrc_ptr; + + addr = rsrc_list_ptr->phys_addr; + debug ("now entering rsrc land\n"); + debug ("offset of rsrc: %x\n", rsrc_list_ptr->phys_addr); + + for (rsrc = 0; rsrc < rsrc_list_ptr->num_entries; rsrc++) { + type = readb (io_mem + addr); + + addr += 1; + rsrc_type = type & EBDA_RSRC_TYPE_MASK; + + if (rsrc_type == EBDA_IO_RSRC_TYPE) { + rsrc_ptr = alloc_ebda_pci_rsrc (); + if (!rsrc_ptr) { + iounmap (io_mem); + return -ENOMEM; + } + rsrc_ptr->rsrc_type = type; + + rsrc_ptr->bus_num = readb (io_mem + addr); + rsrc_ptr->dev_fun = readb (io_mem + addr + 1); + rsrc_ptr->start_addr = readw (io_mem + addr + 2); + rsrc_ptr->end_addr = readw (io_mem + addr + 4); + addr += 6; + + debug ("rsrc from io type ----\n"); + debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n", + rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr); + + list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head); + } + + if (rsrc_type == EBDA_MEM_RSRC_TYPE || rsrc_type == EBDA_PFM_RSRC_TYPE) { + rsrc_ptr = alloc_ebda_pci_rsrc (); + if (!rsrc_ptr ) { + iounmap (io_mem); + return -ENOMEM; + } + rsrc_ptr->rsrc_type = type; + + rsrc_ptr->bus_num = readb (io_mem + addr); + rsrc_ptr->dev_fun = readb (io_mem + addr + 1); + rsrc_ptr->start_addr = readl (io_mem + addr + 2); + rsrc_ptr->end_addr = readl (io_mem + addr + 6); + addr += 10; + + debug ("rsrc from mem or pfm ---\n"); + debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n", + rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr); + + list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head); + } + } + kfree (rsrc_list_ptr); + rsrc_list_ptr = NULL; + print_ebda_pci_rsrc (); + return 0; +} + +u16 ibmphp_get_total_controllers (void) +{ + return hpc_list_ptr->num_ctlrs; +} + +struct slot *ibmphp_get_slot_from_physical_num (u8 physical_num) +{ + struct slot *slot; + struct list_head *list; + + list_for_each (list, &ibmphp_slot_head) { + slot = list_entry (list, struct slot, ibm_slot_list); + if (slot->number == physical_num) + return slot; + } + return NULL; +} + +/* To find: + * - the smallest slot number + * - the largest slot number + * - the total number of the slots based on each bus + * (if only one slot per bus slot_min = slot_max ) + */ +struct bus_info *ibmphp_find_same_bus_num (u32 num) +{ + struct bus_info *ptr; + struct list_head *ptr1; + + list_for_each (ptr1, &bus_info_head) { + ptr = list_entry (ptr1, struct bus_info, bus_info_list); + if (ptr->busno == num) + return ptr; + } + return NULL; +} + +/* Finding relative bus number, in order to map corresponding + * bus register + */ +int ibmphp_get_bus_index (u8 num) +{ + struct bus_info *ptr; + struct list_head *ptr1; + + list_for_each (ptr1, &bus_info_head) { + ptr = list_entry (ptr1, struct bus_info, bus_info_list); + if (ptr->busno == num) + return ptr->index; + } + return -ENODEV; +} + +void ibmphp_free_bus_info_queue (void) +{ + struct bus_info *bus_info; + struct list_head *list; + struct list_head *next; + + list_for_each_safe (list, next, &bus_info_head ) { + bus_info = list_entry (list, struct bus_info, bus_info_list); + kfree (bus_info); + } +} + +void ibmphp_free_ebda_hpc_queue (void) +{ + struct controller *controller = NULL; + struct list_head *list; + struct list_head *next; + int pci_flag = 0; + + list_for_each_safe (list, next, &ebda_hpc_head) { + controller = list_entry (list, struct controller, ebda_hpc_list); + if (controller->ctlr_type == 0) + release_region (controller->u.isa_ctlr.io_start, (controller->u.isa_ctlr.io_end - controller->u.isa_ctlr.io_start + 1)); + else if ((controller->ctlr_type == 1) && (!pci_flag)) { + ++pci_flag; + pci_unregister_driver (&ibmphp_driver); + } + free_ebda_hpc (controller); + } +} + +void ibmphp_free_ebda_pci_rsrc_queue (void) +{ + struct ebda_pci_rsrc *resource; + struct list_head *list; + struct list_head *next; + + list_for_each_safe (list, next, &ibmphp_ebda_pci_rsrc_head) { + resource = list_entry (list, struct ebda_pci_rsrc, ebda_pci_rsrc_list); + kfree (resource); + resource = NULL; + } +} + +static struct pci_device_id id_table[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_IBM, + .device = HPC_DEVICE_ID, + .subvendor = PCI_VENDOR_ID_IBM, + .subdevice = HPC_SUBSYSTEM_ID, + .class = ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00), + }, {} +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static int ibmphp_probe (struct pci_dev *, const struct pci_device_id *); +static struct pci_driver ibmphp_driver = { + .name = "ibmphp", + .id_table = id_table, + .probe = ibmphp_probe, +}; + +int ibmphp_register_pci (void) +{ + struct controller *ctrl; + struct list_head *tmp; + int rc = 0; + + list_for_each (tmp, &ebda_hpc_head) { + ctrl = list_entry (tmp, struct controller, ebda_hpc_list); + if (ctrl->ctlr_type == 1) { + rc = pci_module_init (&ibmphp_driver); + break; + } + } + return rc; +} +static int ibmphp_probe (struct pci_dev * dev, const struct pci_device_id *ids) +{ + struct controller *ctrl; + struct list_head *tmp; + + debug ("inside ibmphp_probe \n"); + + list_for_each (tmp, &ebda_hpc_head) { + ctrl = list_entry (tmp, struct controller, ebda_hpc_list); + if (ctrl->ctlr_type == 1) { + if ((dev->devfn == ctrl->u.pci_ctlr.dev_fun) && (dev->bus->number == ctrl->u.pci_ctlr.bus)) { + ctrl->ctrl_dev = dev; + debug ("found device!!! \n"); + debug ("dev->device = %x, dev->subsystem_device = %x\n", dev->device, dev->subsystem_device); + return 0; + } + } + } + return -ENODEV; +} + diff -Nru a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/ibmphp_hpc.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,1228 @@ +/* + * IBM Hot Plug Controller Driver + * + * Written By: Jyoti Shah, IBM Corporation + * + * Copyright (c) 2001-2002 IBM Corp. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + * + */ + +#include +#include +#include +#include +#include +#include +#include "ibmphp.h" + +static int to_debug = FALSE; +#define debug_polling(fmt, arg...) do { if (to_debug) debug (fmt, arg); } while (0) + +//---------------------------------------------------------------------------- +// timeout values +//---------------------------------------------------------------------------- +#define CMD_COMPLETE_TOUT_SEC 60 // give HPC 60 sec to finish cmd +#define HPC_CTLR_WORKING_TOUT 60 // give HPC 60 sec to finish cmd +#define HPC_GETACCESS_TIMEOUT 60 // seconds +#define POLL_INTERVAL_SEC 2 // poll HPC every 2 seconds +#define POLL_LATCH_CNT 5 // poll latch 5 times, then poll slots + +//---------------------------------------------------------------------------- +// Winnipeg Architected Register Offsets +//---------------------------------------------------------------------------- +#define WPG_I2CMBUFL_OFFSET 0x08 // I2C Message Buffer Low +#define WPG_I2CMOSUP_OFFSET 0x10 // I2C Master Operation Setup Reg +#define WPG_I2CMCNTL_OFFSET 0x20 // I2C Master Control Register +#define WPG_I2CPARM_OFFSET 0x40 // I2C Parameter Register +#define WPG_I2CSTAT_OFFSET 0x70 // I2C Status Register + +//---------------------------------------------------------------------------- +// Winnipeg Store Type commands (Add this commands to the register offset) +//---------------------------------------------------------------------------- +#define WPG_I2C_AND 0x1000 // I2C AND operation +#define WPG_I2C_OR 0x2000 // I2C OR operation + +//---------------------------------------------------------------------------- +// Command set for I2C Master Operation Setup Regisetr +//---------------------------------------------------------------------------- +#define WPG_READATADDR_MASK 0x00010000 // read,bytes,I2C shifted,index +#define WPG_WRITEATADDR_MASK 0x40010000 // write,bytes,I2C shifted,index +#define WPG_READDIRECT_MASK 0x10010000 +#define WPG_WRITEDIRECT_MASK 0x60010000 + + +//---------------------------------------------------------------------------- +// bit masks for I2C Master Control Register +//---------------------------------------------------------------------------- +#define WPG_I2CMCNTL_STARTOP_MASK 0x00000002 // Start the Operation + +//---------------------------------------------------------------------------- +// +//---------------------------------------------------------------------------- +#define WPG_I2C_IOREMAP_SIZE 0x2044 // size of linear address interval + +//---------------------------------------------------------------------------- +// command index +//---------------------------------------------------------------------------- +#define WPG_1ST_SLOT_INDEX 0x01 // index - 1st slot for ctlr +#define WPG_CTLR_INDEX 0x0F // index - ctlr +#define WPG_1ST_EXTSLOT_INDEX 0x10 // index - 1st ext slot for ctlr +#define WPG_1ST_BUS_INDEX 0x1F // index - 1st bus for ctlr + +//---------------------------------------------------------------------------- +// macro utilities +//---------------------------------------------------------------------------- +// if bits 20,22,25,26,27,29,30 are OFF return TRUE +#define HPC_I2CSTATUS_CHECK(s) ((u8)((s & 0x00000A76) ? FALSE : TRUE)) + +//---------------------------------------------------------------------------- +// global variables +//---------------------------------------------------------------------------- +static int ibmphp_shutdown; +static int tid_poll; +static struct semaphore sem_hpcaccess; // lock access to HPC +static struct semaphore semOperations; // lock all operations and + // access to data structures +static struct semaphore sem_exit; // make sure polling thread goes away +//---------------------------------------------------------------------------- +// local function prototypes +//---------------------------------------------------------------------------- +static u8 i2c_ctrl_read (struct controller *, void *, u8); +static u8 i2c_ctrl_write (struct controller *, void *, u8, u8); +static u8 hpc_writecmdtoindex (u8, u8); +static u8 hpc_readcmdtoindex (u8, u8); +static void get_hpc_access (void); +static void free_hpc_access (void); +static void poll_hpc (void); +static int update_slot (struct slot *, u8); +static int process_changeinstatus (struct slot *, struct slot *); +static int process_changeinlatch (u8, u8, struct controller *); +static int hpc_poll_thread (void *); +static int hpc_wait_ctlr_notworking (int, struct controller *, void *, u8 *); +//---------------------------------------------------------------------------- + + +/*---------------------------------------------------------------------- +* Name: ibmphp_hpc_initvars +* +* Action: initialize semaphores and variables +*---------------------------------------------------------------------*/ +void __init ibmphp_hpc_initvars (void) +{ + debug ("%s - Entry\n", __FUNCTION__); + + init_MUTEX (&sem_hpcaccess); + init_MUTEX (&semOperations); + init_MUTEX_LOCKED (&sem_exit); + to_debug = FALSE; + ibmphp_shutdown = FALSE; + tid_poll = 0; + + debug ("%s - Exit\n", __FUNCTION__); +} + +/*---------------------------------------------------------------------- +* Name: i2c_ctrl_read +* +* Action: read from HPC over I2C +* +*---------------------------------------------------------------------*/ +static u8 i2c_ctrl_read (struct controller *ctlr_ptr, void *WPGBbar, u8 index) +{ + u8 status; + int i; + void *wpg_addr; // base addr + offset + ulong wpg_data, // data to/from WPG LOHI format + ultemp, data; // actual data HILO format + + + debug_polling ("%s - Entry WPGBbar[%lx] index[%x] \n", __FUNCTION__, (ulong) WPGBbar, index); + + //-------------------------------------------------------------------- + // READ - step 1 + // read at address, byte length, I2C address (shifted), index + // or read direct, byte length, index + if (ctlr_ptr->ctlr_type == 0x02) { + data = WPG_READATADDR_MASK; + // fill in I2C address + ultemp = (ulong) ctlr_ptr->u.wpeg_ctlr.i2c_addr; + ultemp = ultemp >> 1; + data |= (ultemp << 8); + + // fill in index + data |= (ulong) index; + } else if (ctlr_ptr->ctlr_type == 0x04) { + data = WPG_READDIRECT_MASK; + + // fill in index + ultemp = (ulong) index; + ultemp = ultemp << 8; + data |= ultemp; + } else { + err ("this controller type is not supported \n"); + return HPC_ERROR; + } + + wpg_data = swab32 (data); // swap data before writing + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMOSUP_OFFSET; + writel (wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // READ - step 2 : clear the message buffer + data = 0x00000000; + wpg_data = swab32 (data); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET; + writel (wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // READ - step 3 : issue start operation, I2C master control bit 30:ON + // 2020 : [20] OR operation at [20] offset 0x20 + data = WPG_I2CMCNTL_STARTOP_MASK; + wpg_data = swab32 (data); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET + (ulong) WPG_I2C_OR; + writel (wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // READ - step 4 : wait until start operation bit clears + i = CMD_COMPLETE_TOUT_SEC; + while (i) { + long_delay (1 * HZ / 100); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET; + wpg_data = readl (wpg_addr); + data = swab32 (wpg_data); + if (!(data & WPG_I2CMCNTL_STARTOP_MASK)) + break; + i--; + } + if (i == 0) { + debug ("%s - Error : WPG timeout\n", __FUNCTION__); + return HPC_ERROR; + } + //-------------------------------------------------------------------- + // READ - step 5 : read I2C status register + i = CMD_COMPLETE_TOUT_SEC; + while (i) { + long_delay (1 * HZ / 100); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CSTAT_OFFSET; + wpg_data = readl (wpg_addr); + data = swab32 (wpg_data); + if (HPC_I2CSTATUS_CHECK (data)) + break; + i--; + } + if (i == 0) { + debug ("ctrl_read - Exit Error:I2C timeout\n"); + return HPC_ERROR; + } + + //-------------------------------------------------------------------- + // READ - step 6 : get DATA + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET; + wpg_data = readl (wpg_addr); + data = swab32 (wpg_data); + + status = (u8) data; + + debug_polling ("%s - Exit index[%x] status[%x]\n", __FUNCTION__, index, status); + + return (status); +} + +/*---------------------------------------------------------------------- +* Name: i2c_ctrl_write +* +* Action: write to HPC over I2C +* +* Return 0 or error codes +*---------------------------------------------------------------------*/ +static u8 i2c_ctrl_write (struct controller *ctlr_ptr, void *WPGBbar, u8 index, u8 cmd) +{ + u8 rc; + void *wpg_addr; // base addr + offset + ulong wpg_data, // data to/from WPG LOHI format + ultemp, data; // actual data HILO format + int i; + + + debug_polling ("%s - Entry WPGBbar[%lx] index[%x] cmd[%x]\n", __FUNCTION__, (ulong) WPGBbar, index, cmd); + + rc = 0; + //-------------------------------------------------------------------- + // WRITE - step 1 + // write at address, byte length, I2C address (shifted), index + // or write direct, byte length, index + data = 0x00000000; + + if (ctlr_ptr->ctlr_type == 0x02) { + data = WPG_WRITEATADDR_MASK; + // fill in I2C address + ultemp = (ulong) ctlr_ptr->u.wpeg_ctlr.i2c_addr; + ultemp = ultemp >> 1; + data |= (ultemp << 8); + + // fill in index + data |= (ulong) index; + } else if (ctlr_ptr->ctlr_type == 0x04) { + data = WPG_WRITEDIRECT_MASK; + + // fill in index + ultemp = (ulong) index; + ultemp = ultemp << 8; + data |= ultemp; + } else { + err ("this controller type is not supported \n"); + return HPC_ERROR; + } + + wpg_data = swab32 (data); // swap data before writing + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMOSUP_OFFSET; + writel (wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // WRITE - step 2 : clear the message buffer + data = 0x00000000 | (ulong) cmd; + wpg_data = swab32 (data); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET; + writel (wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // WRITE - step 3 : issue start operation,I2C master control bit 30:ON + // 2020 : [20] OR operation at [20] offset 0x20 + data = WPG_I2CMCNTL_STARTOP_MASK; + wpg_data = swab32 (data); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET + (ulong) WPG_I2C_OR; + writel (wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // WRITE - step 4 : wait until start operation bit clears + i = CMD_COMPLETE_TOUT_SEC; + while (i) { + long_delay (1 * HZ / 100); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET; + wpg_data = readl (wpg_addr); + data = swab32 (wpg_data); + if (!(data & WPG_I2CMCNTL_STARTOP_MASK)) + break; + i--; + } + if (i == 0) { + debug ("%s - Exit Error:WPG timeout\n", __FUNCTION__); + rc = HPC_ERROR; + } + + //-------------------------------------------------------------------- + // WRITE - step 5 : read I2C status register + i = CMD_COMPLETE_TOUT_SEC; + while (i) { + long_delay (1 * HZ / 100); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CSTAT_OFFSET; + wpg_data = readl (wpg_addr); + data = swab32 (wpg_data); + if (HPC_I2CSTATUS_CHECK (data)) + break; + i--; + } + if (i == 0) { + debug ("ctrl_read - Error : I2C timeout\n"); + rc = HPC_ERROR; + } + + debug_polling ("%s Exit rc[%x]\n", __FUNCTION__, rc); + return (rc); +} + +//------------------------------------------------------------ +// Read from ISA type HPC +//------------------------------------------------------------ +static u8 isa_ctrl_read (struct controller *ctlr_ptr, u8 offset) +{ + u16 start_address; + u16 end_address; + u8 data; + + start_address = ctlr_ptr->u.isa_ctlr.io_start; + end_address = ctlr_ptr->u.isa_ctlr.io_end; + data = inb (start_address + offset); + return data; +} + +//-------------------------------------------------------------- +// Write to ISA type HPC +//-------------------------------------------------------------- +static void isa_ctrl_write (struct controller *ctlr_ptr, u8 offset, u8 data) +{ + u16 start_address; + u16 port_address; + + start_address = ctlr_ptr->u.isa_ctlr.io_start; + port_address = start_address + (u16) offset; + outb (data, port_address); +} + +static u8 pci_ctrl_read (struct controller *ctrl, u8 offset) +{ + u8 data = 0x00; + debug ("inside pci_ctrl_read\n"); + if (ctrl->ctrl_dev) + pci_read_config_byte (ctrl->ctrl_dev, HPC_PCI_OFFSET + offset, &data); + return data; +} + +static u8 pci_ctrl_write (struct controller *ctrl, u8 offset, u8 data) +{ + u8 rc = -ENODEV; + debug ("inside pci_ctrl_write\n"); + if (ctrl->ctrl_dev) { + pci_write_config_byte (ctrl->ctrl_dev, HPC_PCI_OFFSET + offset, data); + rc = 0; + } + return rc; +} + +static u8 ctrl_read (struct controller *ctlr, void *base, u8 offset) +{ + u8 rc; + switch (ctlr->ctlr_type) { + case 0: + rc = isa_ctrl_read (ctlr, offset); + break; + case 1: + rc = pci_ctrl_read (ctlr, offset); + break; + case 2: + case 4: + rc = i2c_ctrl_read (ctlr, base, offset); + break; + default: + return -ENODEV; + } + return rc; +} + +static u8 ctrl_write (struct controller *ctlr, void *base, u8 offset, u8 data) +{ + u8 rc = 0; + switch (ctlr->ctlr_type) { + case 0: + isa_ctrl_write(ctlr, offset, data); + break; + case 1: + rc = pci_ctrl_write (ctlr, offset, data); + break; + case 2: + case 4: + rc = i2c_ctrl_write(ctlr, base, offset, data); + break; + default: + return -ENODEV; + } + return rc; +} +/*---------------------------------------------------------------------- +* Name: hpc_writecmdtoindex() +* +* Action: convert a write command to proper index within a controller +* +* Return index, HPC_ERROR +*---------------------------------------------------------------------*/ +static u8 hpc_writecmdtoindex (u8 cmd, u8 index) +{ + u8 rc; + + switch (cmd) { + case HPC_CTLR_ENABLEIRQ: // 0x00.N.15 + case HPC_CTLR_CLEARIRQ: // 0x06.N.15 + case HPC_CTLR_RESET: // 0x07.N.15 + case HPC_CTLR_IRQSTEER: // 0x08.N.15 + case HPC_CTLR_DISABLEIRQ: // 0x01.N.15 + case HPC_ALLSLOT_ON: // 0x11.N.15 + case HPC_ALLSLOT_OFF: // 0x12.N.15 + rc = 0x0F; + break; + + case HPC_SLOT_OFF: // 0x02.Y.0-14 + case HPC_SLOT_ON: // 0x03.Y.0-14 + case HPC_SLOT_ATTNOFF: // 0x04.N.0-14 + case HPC_SLOT_ATTNON: // 0x05.N.0-14 + case HPC_SLOT_BLINKLED: // 0x13.N.0-14 + rc = index; + break; + + case HPC_BUS_33CONVMODE: + case HPC_BUS_66CONVMODE: + case HPC_BUS_66PCIXMODE: + case HPC_BUS_100PCIXMODE: + case HPC_BUS_133PCIXMODE: + rc = index + WPG_1ST_BUS_INDEX - 1; + break; + + default: + err ("hpc_writecmdtoindex - Error invalid cmd[%x]\n", cmd); + rc = HPC_ERROR; + } + + return rc; +} + +/*---------------------------------------------------------------------- +* Name: hpc_readcmdtoindex() +* +* Action: convert a read command to proper index within a controller +* +* Return index, HPC_ERROR +*---------------------------------------------------------------------*/ +static u8 hpc_readcmdtoindex (u8 cmd, u8 index) +{ + u8 rc; + + switch (cmd) { + case READ_CTLRSTATUS: + rc = 0x0F; + break; + case READ_SLOTSTATUS: + case READ_ALLSTAT: + rc = index; + break; + case READ_EXTSLOTSTATUS: + rc = index + WPG_1ST_EXTSLOT_INDEX; + break; + case READ_BUSSTATUS: + rc = index + WPG_1ST_BUS_INDEX - 1; + break; + case READ_SLOTLATCHLOWREG: + rc = 0x28; + break; + case READ_REVLEVEL: + rc = 0x25; + break; + case READ_HPCOPTIONS: + rc = 0x27; + break; + default: + rc = HPC_ERROR; + } + return rc; +} + +/*---------------------------------------------------------------------- +* Name: HPCreadslot() +* +* Action: issue a READ command to HPC +* +* Input: pslot - can not be NULL for READ_ALLSTAT +* pstatus - can be NULL for READ_ALLSTAT +* +* Return 0 or error codes +*---------------------------------------------------------------------*/ +int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus) +{ + void *wpg_bbar = NULL; + struct controller *ctlr_ptr; + struct list_head *pslotlist; + u8 index, status; + int rc = 0; + int busindex; + + debug_polling ("%s - Entry pslot[%lx] cmd[%x] pstatus[%lx]\n", __FUNCTION__, (ulong) pslot, cmd, (ulong) pstatus); + + if ((pslot == NULL) + || ((pstatus == NULL) && (cmd != READ_ALLSTAT) && (cmd != READ_BUSSTATUS))) { + rc = -EINVAL; + err ("%s - Error invalid pointer, rc[%d]\n", __FUNCTION__, rc); + return rc; + } + + if (cmd == READ_BUSSTATUS) { + busindex = ibmphp_get_bus_index (pslot->bus); + if (busindex < 0) { + rc = -EINVAL; + err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc); + return rc; + } else + index = (u8) busindex; + } else + index = pslot->ctlr_index; + + index = hpc_readcmdtoindex (cmd, index); + + if (index == HPC_ERROR) { + rc = -EINVAL; + err ("%s - Exit Error:invalid index, rc[%d]\n", __FUNCTION__, rc); + return rc; + } + + ctlr_ptr = pslot->ctrl; + + get_hpc_access (); + + //-------------------------------------------------------------------- + // map physical address to logical address + //-------------------------------------------------------------------- + if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) + wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE); + + //-------------------------------------------------------------------- + // check controller status before reading + //-------------------------------------------------------------------- + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status); + if (!rc) { + switch (cmd) { + case READ_ALLSTAT: + // update the slot structure + pslot->ctrl->status = status; + pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index); + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, + &status); + if (!rc) + pslot->ext_status = ctrl_read (ctlr_ptr, wpg_bbar, index + WPG_1ST_EXTSLOT_INDEX); + + break; + + case READ_SLOTSTATUS: + // DO NOT update the slot structure + *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); + break; + + case READ_EXTSLOTSTATUS: + // DO NOT update the slot structure + *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); + break; + + case READ_CTLRSTATUS: + // DO NOT update the slot structure + *pstatus = status; + break; + + case READ_BUSSTATUS: + pslot->busstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); + break; + case READ_REVLEVEL: + *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); + break; + case READ_HPCOPTIONS: + *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); + break; + case READ_SLOTLATCHLOWREG: + // DO NOT update the slot structure + *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index); + break; + + // Not used + case READ_ALLSLOT: + list_for_each (pslotlist, &ibmphp_slot_head) { + pslot = list_entry (pslotlist, struct slot, ibm_slot_list); + index = pslot->ctlr_index; + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, + wpg_bbar, &status); + if (!rc) { + pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index); + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, + ctlr_ptr, wpg_bbar, &status); + if (!rc) + pslot->ext_status = + ctrl_read (ctlr_ptr, wpg_bbar, + index + WPG_1ST_EXTSLOT_INDEX); + } else { + err ("%s - Error ctrl_read failed\n", __FUNCTION__); + rc = -EINVAL; + break; + } + } + break; + default: + rc = -EINVAL; + break; + } + } + //-------------------------------------------------------------------- + // cleanup + //-------------------------------------------------------------------- + + // remove physical to logical address mapping + if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) + iounmap (wpg_bbar); + + free_hpc_access (); + + debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: ibmphp_hpc_writeslot() +* +* Action: issue a WRITE command to HPC +*---------------------------------------------------------------------*/ +int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd) +{ + void *wpg_bbar = NULL; + struct controller *ctlr_ptr; + u8 index, status; + int busindex; + u8 done; + int rc = 0; + int timeout; + + debug_polling ("%s - Entry pslot[%lx] cmd[%x]\n", __FUNCTION__, (ulong) pslot, cmd); + if (pslot == NULL) { + rc = -EINVAL; + err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc); + return rc; + } + + if ((cmd == HPC_BUS_33CONVMODE) || (cmd == HPC_BUS_66CONVMODE) || + (cmd == HPC_BUS_66PCIXMODE) || (cmd == HPC_BUS_100PCIXMODE) || + (cmd == HPC_BUS_133PCIXMODE)) { + busindex = ibmphp_get_bus_index (pslot->bus); + if (busindex < 0) { + rc = -EINVAL; + err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc); + return rc; + } else + index = (u8) busindex; + } else + index = pslot->ctlr_index; + + index = hpc_writecmdtoindex (cmd, index); + + if (index == HPC_ERROR) { + rc = -EINVAL; + err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc); + return rc; + } + + ctlr_ptr = pslot->ctrl; + + get_hpc_access (); + + //-------------------------------------------------------------------- + // map physical address to logical address + //-------------------------------------------------------------------- + if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) { + wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE); + + debug ("%s - ctlr id[%x] physical[%lx] logical[%lx] i2c[%x]\n", __FUNCTION__, + ctlr_ptr->ctlr_id, (ulong) (ctlr_ptr->u.wpeg_ctlr.wpegbbar), (ulong) wpg_bbar, + ctlr_ptr->u.wpeg_ctlr.i2c_addr); + } + //-------------------------------------------------------------------- + // check controller status before writing + //-------------------------------------------------------------------- + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status); + if (!rc) { + + ctrl_write (ctlr_ptr, wpg_bbar, index, cmd); + + //-------------------------------------------------------------------- + // check controller is still not working on the command + //-------------------------------------------------------------------- + timeout = CMD_COMPLETE_TOUT_SEC; + done = FALSE; + while (!done) { + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, + &status); + if (!rc) { + if (NEEDTOCHECK_CMDSTATUS (cmd)) { + if (CTLR_FINISHED (status) == HPC_CTLR_FINISHED_YES) + done = TRUE; + } else + done = TRUE; + } + if (!done) { + long_delay (1 * HZ); + if (timeout < 1) { + done = TRUE; + err ("%s - Error command complete timeout\n", __FUNCTION__); + rc = -EFAULT; + } else + timeout--; + } + } + ctlr_ptr->status = status; + } + // cleanup + + // remove physical to logical address mapping + if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4)) + iounmap (wpg_bbar); + free_hpc_access (); + + debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: get_hpc_access() +* +* Action: make sure only one process can access HPC at one time +*---------------------------------------------------------------------*/ +static void get_hpc_access (void) +{ + down (&sem_hpcaccess); +} + +/*---------------------------------------------------------------------- +* Name: free_hpc_access() +*---------------------------------------------------------------------*/ +void free_hpc_access (void) +{ + up (&sem_hpcaccess); +} + +/*---------------------------------------------------------------------- +* Name: ibmphp_lock_operations() +* +* Action: make sure only one process can change the data structure +*---------------------------------------------------------------------*/ +void ibmphp_lock_operations (void) +{ + down (&semOperations); + to_debug = TRUE; +} + +/*---------------------------------------------------------------------- +* Name: ibmphp_unlock_operations() +*---------------------------------------------------------------------*/ +void ibmphp_unlock_operations (void) +{ + debug ("%s - Entry\n", __FUNCTION__); + up (&semOperations); + to_debug = FALSE; + debug ("%s - Exit\n", __FUNCTION__); +} + +/*---------------------------------------------------------------------- +* Name: poll_hpc() +*---------------------------------------------------------------------*/ +#define POLL_LATCH_REGISTER 0 +#define POLL_SLOTS 1 +#define POLL_SLEEP 2 +static void poll_hpc (void) +{ + struct slot myslot; + struct slot *pslot = NULL; + struct list_head *pslotlist; + int rc; + int poll_state = POLL_LATCH_REGISTER; + u8 oldlatchlow = 0x00; + u8 curlatchlow = 0x00; + int poll_count = 0; + u8 ctrl_count = 0x00; + + debug ("%s - Entry\n", __FUNCTION__); + + while (!ibmphp_shutdown) { + if (ibmphp_shutdown) + break; + + /* try to get the lock to do some kind of harware access */ + down (&semOperations); + + switch (poll_state) { + case POLL_LATCH_REGISTER: + oldlatchlow = curlatchlow; + ctrl_count = 0x00; + list_for_each (pslotlist, &ibmphp_slot_head) { + if (ctrl_count >= ibmphp_get_total_controllers()) + break; + pslot = list_entry (pslotlist, struct slot, ibm_slot_list); + if (pslot->ctrl->ctlr_relative_id == ctrl_count) { + ctrl_count++; + if (READ_SLOT_LATCH (pslot->ctrl)) { + rc = ibmphp_hpc_readslot (pslot, + READ_SLOTLATCHLOWREG, + &curlatchlow); + if (oldlatchlow != curlatchlow) + process_changeinlatch (oldlatchlow, + curlatchlow, + pslot->ctrl); + } + } + } + ++poll_count; + poll_state = POLL_SLEEP; + break; + case POLL_SLOTS: + list_for_each (pslotlist, &ibmphp_slot_head) { + pslot = list_entry (pslotlist, struct slot, ibm_slot_list); + // make a copy of the old status + memcpy ((void *) &myslot, (void *) pslot, + sizeof (struct slot)); + rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL); + if ((myslot.status != pslot->status) + || (myslot.ext_status != pslot->ext_status)) + process_changeinstatus (pslot, &myslot); + } + ctrl_count = 0x00; + list_for_each (pslotlist, &ibmphp_slot_head) { + if (ctrl_count >= ibmphp_get_total_controllers()) + break; + pslot = list_entry (pslotlist, struct slot, ibm_slot_list); + if (pslot->ctrl->ctlr_relative_id == ctrl_count) { + ctrl_count++; + if (READ_SLOT_LATCH (pslot->ctrl)) + rc = ibmphp_hpc_readslot (pslot, + READ_SLOTLATCHLOWREG, + &curlatchlow); + } + } + ++poll_count; + poll_state = POLL_SLEEP; + break; + case POLL_SLEEP: + /* don't sleep with a lock on the hardware */ + up (&semOperations); + long_delay (POLL_INTERVAL_SEC * HZ); + + if (ibmphp_shutdown) + break; + + down (&semOperations); + + if (poll_count >= POLL_LATCH_CNT) { + poll_count = 0; + poll_state = POLL_SLOTS; + } else + poll_state = POLL_LATCH_REGISTER; + break; + } + /* give up the harware semaphore */ + up (&semOperations); + /* sleep for a short time just for good measure */ + set_current_state (TASK_INTERRUPTIBLE); + schedule_timeout (HZ/10); + } + up (&sem_exit); + debug ("%s - Exit\n", __FUNCTION__); +} + + +/* ---------------------------------------------------------------------- + * Name: ibmphp_hpc_fillhpslotinfo(hotplug_slot * phpslot) + * + * Action: fill out the hotplug_slot info + * + * Input: pointer to hotplug_slot + * + * Return + * Value: 0 or error codes + *-----------------------------------------------------------------------*/ +int ibmphp_hpc_fillhpslotinfo (struct hotplug_slot *phpslot) +{ + int rc = 0; + struct slot *pslot; + + if (phpslot && phpslot->private) { + pslot = (struct slot *) phpslot->private; + rc = update_slot (pslot, (u8) TRUE); + if (!rc) { + + // power - enabled:1 not:0 + phpslot->info->power_status = SLOT_POWER (pslot->status); + + // attention - off:0, on:1, blinking:2 + phpslot->info->attention_status = SLOT_ATTN (pslot->status, pslot->ext_status); + + // latch - open:1 closed:0 + phpslot->info->latch_status = SLOT_LATCH (pslot->status); + + // pci board - present:1 not:0 + if (SLOT_PRESENT (pslot->status)) + phpslot->info->adapter_status = 1; + else + phpslot->info->adapter_status = 0; +/* + if (pslot->bus_on->supported_bus_mode + && (pslot->bus_on->supported_speed == BUS_SPEED_66)) + phpslot->info->max_bus_speed_status = BUS_SPEED_66PCIX; + else + phpslot->info->max_bus_speed_status = pslot->bus_on->supported_speed; +*/ } else + rc = -EINVAL; + } else + rc = -EINVAL; + + return rc; +} + +/*---------------------------------------------------------------------- +* Name: update_slot +* +* Action: fill out slot status and extended status, controller status +* +* Input: pointer to slot struct +*---------------------------------------------------------------------*/ +static int update_slot (struct slot *pslot, u8 update) +{ + int rc = 0; + + debug ("%s - Entry pslot[%lx]\n", __FUNCTION__, (ulong) pslot); + rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL); + debug ("%s - Exit rc[%d]\n", __FUNCTION__, rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: process_changeinstatus +* +* Action: compare old and new slot status, process the change in status +* +* Input: pointer to slot struct, old slot struct +* +* Return 0 or error codes +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +static int process_changeinstatus (struct slot *pslot, struct slot *poldslot) +{ + u8 status; + int rc = 0; + u8 disable = FALSE; + u8 update = FALSE; + + debug ("process_changeinstatus - Entry pslot[%lx], poldslot[%lx]\n", (ulong) pslot, + (ulong) poldslot); + + // bit 0 - HPC_SLOT_POWER + if ((pslot->status & 0x01) != (poldslot->status & 0x01)) + update = TRUE; + + // bit 1 - HPC_SLOT_CONNECT + // ignore + + // bit 2 - HPC_SLOT_ATTN + if ((pslot->status & 0x04) != (poldslot->status & 0x04)) + update = TRUE; + + // bit 3 - HPC_SLOT_PRSNT2 + // bit 4 - HPC_SLOT_PRSNT1 + if (((pslot->status & 0x08) != (poldslot->status & 0x08)) + || ((pslot->status & 0x10) != (poldslot->status & 0x10))) + update = TRUE; + + // bit 5 - HPC_SLOT_PWRGD + if ((pslot->status & 0x20) != (poldslot->status & 0x20)) + // OFF -> ON: ignore, ON -> OFF: disable slot + if ((poldslot->status & 0x20) && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status))) + disable = TRUE; + + // bit 6 - HPC_SLOT_BUS_SPEED + // ignore + + // bit 7 - HPC_SLOT_LATCH + if ((pslot->status & 0x80) != (poldslot->status & 0x80)) { + update = TRUE; + // OPEN -> CLOSE + if (pslot->status & 0x80) { + if (SLOT_PWRGD (pslot->status)) { + // power goes on and off after closing latch + // check again to make sure power is still ON + long_delay (1 * HZ); + rc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &status); + if (SLOT_PWRGD (status)) + update = TRUE; + else // overwrite power in pslot to OFF + pslot->status &= ~HPC_SLOT_POWER; + } + } + // CLOSE -> OPEN + else if ((SLOT_PWRGD (poldslot->status) == HPC_SLOT_PWRGD_GOOD) + && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status))) { + disable = TRUE; + } + // else - ignore + } + // bit 4 - HPC_SLOT_BLINK_ATTN + if ((pslot->ext_status & 0x08) != (poldslot->ext_status & 0x08)) + update = TRUE; + + if (disable) { + debug ("process_changeinstatus - disable slot\n"); + pslot->flag = FALSE; + rc = ibmphp_do_disable_slot (pslot); + } + + if (update || disable) { + ibmphp_update_slot_info (pslot); + } + + debug ("%s - Exit rc[%d] disable[%x] update[%x]\n", __FUNCTION__, rc, disable, update); + + return rc; +} + +/*---------------------------------------------------------------------- +* Name: process_changeinlatch +* +* Action: compare old and new latch reg status, process the change +* +* Input: old and current latch register status +* +* Return 0 or error codes +* Value: +*---------------------------------------------------------------------*/ +static int process_changeinlatch (u8 old, u8 new, struct controller *ctrl) +{ + struct slot myslot, *pslot; + u8 i; + u8 mask; + int rc = 0; + + debug ("%s - Entry old[%x], new[%x]\n", __FUNCTION__, old, new); + // bit 0 reserved, 0 is LSB, check bit 1-6 for 6 slots + + for (i = ctrl->starting_slot_num; i <= ctrl->ending_slot_num; i++) { + mask = 0x01 << i; + if ((mask & old) != (mask & new)) { + pslot = ibmphp_get_slot_from_physical_num (i); + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL); + debug ("%s - call process_changeinstatus for slot[%d]\n", __FUNCTION__, i); + process_changeinstatus (pslot, &myslot); + } else { + rc = -EINVAL; + err ("%s - Error bad pointer for slot[%d]\n", __FUNCTION__, i); + } + } + } + debug ("%s - Exit rc[%d]\n", __FUNCTION__, rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: hpc_poll_thread +* +* Action: polling +* +* Return 0 +* Value: +*---------------------------------------------------------------------*/ +static int hpc_poll_thread (void *data) +{ + debug ("%s - Entry\n", __FUNCTION__); + + daemonize("hpc_poll"); + allow_signal(SIGKILL); + + poll_hpc (); + + tid_poll = 0; + debug ("%s - Exit\n", __FUNCTION__); + return 0; +} + + +/*---------------------------------------------------------------------- +* Name: ibmphp_hpc_start_poll_thread +* +* Action: start polling thread +*---------------------------------------------------------------------*/ +int __init ibmphp_hpc_start_poll_thread (void) +{ + int rc = 0; + + debug ("%s - Entry\n", __FUNCTION__); + + tid_poll = kernel_thread (hpc_poll_thread, 0, 0); + if (tid_poll < 0) { + err ("%s - Error, thread not started\n", __FUNCTION__); + rc = -1; + } + + debug ("%s - Exit tid_poll[%d] rc[%d]\n", __FUNCTION__, tid_poll, rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: ibmphp_hpc_stop_poll_thread +* +* Action: stop polling thread and cleanup +*---------------------------------------------------------------------*/ +void __exit ibmphp_hpc_stop_poll_thread (void) +{ + debug ("%s - Entry\n", __FUNCTION__); + + ibmphp_shutdown = TRUE; + debug ("before locking operations \n"); + ibmphp_lock_operations (); + debug ("after locking operations \n"); + + // wait for poll thread to exit + debug ("before sem_exit down \n"); + down (&sem_exit); + debug ("after sem_exit down \n"); + + // cleanup + debug ("before free_hpc_access \n"); + free_hpc_access (); + debug ("after free_hpc_access \n"); + ibmphp_unlock_operations (); + debug ("after unlock operations \n"); + up (&sem_exit); + debug ("after sem exit up\n"); + + debug ("%s - Exit\n", __FUNCTION__); +} + +/*---------------------------------------------------------------------- +* Name: hpc_wait_ctlr_notworking +* +* Action: wait until the controller is in a not working state +* +* Return 0, HPC_ERROR +* Value: +*---------------------------------------------------------------------*/ +static int hpc_wait_ctlr_notworking (int timeout, struct controller *ctlr_ptr, void *wpg_bbar, + u8 * pstatus) +{ + int rc = 0; + u8 done = FALSE; + + debug_polling ("hpc_wait_ctlr_notworking - Entry timeout[%d]\n", timeout); + + while (!done) { + *pstatus = ctrl_read (ctlr_ptr, wpg_bbar, WPG_CTLR_INDEX); + if (*pstatus == HPC_ERROR) { + rc = HPC_ERROR; + done = TRUE; + } + if (CTLR_WORKING (*pstatus) == HPC_CTLR_WORKING_NO) + done = TRUE; + if (!done) { + long_delay (1 * HZ); + if (timeout < 1) { + done = TRUE; + err ("HPCreadslot - Error ctlr timeout\n"); + rc = HPC_ERROR; + } else + timeout--; + } + } + debug_polling ("hpc_wait_ctlr_notworking - Exit rc[%x] status[%x]\n", rc, *pstatus); + return rc; +} diff -Nru a/drivers/pci/hotplug/ibmphp_pci.c b/drivers/pci/hotplug/ibmphp_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/ibmphp_pci.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,1758 @@ +/* + * IBM Hot Plug Controller Driver + * + * Written By: Irene Zubarev, IBM Corporation + * + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2002 IBM Corp. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include "ibmphp.h" + + +static int configure_device(struct pci_func *); +static int configure_bridge(struct pci_func **, u8); +static struct res_needed *scan_behind_bridge(struct pci_func *, u8); +static int add_new_bus (struct bus_node *, struct resource_node *, struct resource_node *, struct resource_node *, u8); +static u8 find_sec_number (u8 primary_busno, u8 slotno); + +/* + * NOTE..... If BIOS doesn't provide default routing, we assign: + * 9 for SCSI, 10 for LAN adapters, and 11 for everything else. + * If adapter is bridged, then we assign 11 to it and devices behind it. + * We also assign the same irq numbers for multi function devices. + * These are PIC mode, so shouldn't matter n.e.ways (hopefully) + */ +static void assign_alt_irq (struct pci_func * cur_func, u8 class_code) +{ + int j = 0; + for (j = 0; j < 4; j++) { + if (cur_func->irq[j] == 0xff) { + switch (class_code) { + case PCI_BASE_CLASS_STORAGE: + cur_func->irq[j] = SCSI_IRQ; + break; + case PCI_BASE_CLASS_NETWORK: + cur_func->irq[j] = LAN_IRQ; + break; + default: + cur_func->irq[j] = OTHER_IRQ; + break; + } + } + } +} + +/* + * Configures the device to be added (will allocate needed resources if it + * can), the device can be a bridge or a regular pci device, can also be + * multi-functional + * + * Input: function to be added + * + * TO DO: The error case with Multifunction device or multi function bridge, + * if there is an error, will need to go through all previous functions and + * unconfigure....or can add some code into unconfigure_card.... + */ +int ibmphp_configure_card (struct pci_func *func, u8 slotno) +{ + u16 vendor_id; + u32 class; + u8 class_code; + u8 hdr_type, device, sec_number; + u8 function; + struct pci_func *newfunc; /* for multi devices */ + struct pci_func *cur_func, *prev_func; + int rc, i, j; + int cleanup_count; + u8 flag; + u8 valid_device = 0x00; /* to see if we are able to read from card any device info at all */ + + debug ("inside configure_card, func->busno = %x \n", func->busno); + + device = func->device; + cur_func = func; + + /* We only get bus and device from IRQ routing table. So at this point, + * func->busno is correct, and func->device contains only device (at the 5 + * highest bits) + */ + + /* For every function on the card */ + for (function = 0x00; function < 0x08; function++) { + unsigned int devfn = PCI_DEVFN(device, function); + ibmphp_pci_bus->number = cur_func->busno; + + cur_func->function = function; + + debug ("inside the loop, cur_func->busno = %x, cur_func->device = %x, cur_func->funcion = %x\n", + cur_func->busno, cur_func->device, cur_func->function); + + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); + + debug ("vendor_id is %x\n", vendor_id); + if (vendor_id != PCI_VENDOR_ID_NOTVALID) { + /* found correct device!!! */ + debug ("found valid device, vendor_id = %x\n", vendor_id); + + ++valid_device; + + /* header: x x x x x x x x + * | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge + * |_=> 0 = single function device, 1 = multi-function device + */ + + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class); + + class_code = class >> 24; + debug ("hrd_type = %x, class = %x, class_code %x \n", hdr_type, class, class_code); + class >>= 8; /* to take revision out, class = class.subclass.prog i/f */ + if (class == PCI_CLASS_NOT_DEFINED_VGA) { + err ("The device %x is VGA compatible and as is not supported for hot plugging. " + "Please choose another device.\n", cur_func->device); + return -ENODEV; + } else if (class == PCI_CLASS_DISPLAY_VGA) { + err ("The device %x is not supported for hot plugging. " + "Please choose another device.\n", cur_func->device); + return -ENODEV; + } + switch (hdr_type) { + case PCI_HEADER_TYPE_NORMAL: + debug ("single device case.... vendor id = %x, hdr_type = %x, class = %x\n", vendor_id, hdr_type, class); + assign_alt_irq (cur_func, class_code); + if ((rc = configure_device (cur_func)) < 0) { + /* We need to do this in case some other BARs were properly inserted */ + err ("was not able to configure devfunc %x on bus %x. \n", + cur_func->device, cur_func->busno); + cleanup_count = 6; + goto error; + } + cur_func->next = NULL; + function = 0x8; + break; + case PCI_HEADER_TYPE_MULTIDEVICE: + assign_alt_irq (cur_func, class_code); + if ((rc = configure_device (cur_func)) < 0) { + /* We need to do this in case some other BARs were properly inserted */ + err ("was not able to configure devfunc %x on bus %x...bailing out\n", + cur_func->device, cur_func->busno); + cleanup_count = 6; + goto error; + } + newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!newfunc) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (newfunc, 0, sizeof (struct pci_func)); + newfunc->busno = cur_func->busno; + newfunc->device = device; + cur_func->next = newfunc; + cur_func = newfunc; + for (j = 0; j < 4; j++) + newfunc->irq[j] = cur_func->irq[j]; + break; + case PCI_HEADER_TYPE_MULTIBRIDGE: + class >>= 8; + if (class != PCI_CLASS_BRIDGE_PCI) { + err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. " + "Please insert another card.\n", cur_func->device); + return -ENODEV; + } + assign_alt_irq (cur_func, class_code); + rc = configure_bridge (&cur_func, slotno); + if (rc == -ENODEV) { + err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n"); + err ("Bus %x, devfunc %x \n", cur_func->busno, cur_func->device); + return rc; + } + if (rc) { + /* We need to do this in case some other BARs were properly inserted */ + err ("was not able to hot-add PPB properly.\n"); + func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ + cleanup_count = 2; + goto error; + } + + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); + flag = FALSE; + for (i = 0; i < 32; i++) { + if (func->devices[i]) { + newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!newfunc) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (newfunc, 0, sizeof (struct pci_func)); + newfunc->busno = sec_number; + newfunc->device = (u8) i; + for (j = 0; j < 4; j++) + newfunc->irq[j] = cur_func->irq[j]; + + if (flag) { + for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; + prev_func->next = newfunc; + } else + cur_func->next = newfunc; + + rc = ibmphp_configure_card (newfunc, slotno); + /* This could only happen if kmalloc failed */ + if (rc) { + /* We need to do this in case bridge itself got configured properly, but devices behind it failed */ + func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ + cleanup_count = 2; + goto error; + } + flag = TRUE; + } + } + + newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!newfunc) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (newfunc, 0, sizeof (struct pci_func)); + newfunc->busno = cur_func->busno; + newfunc->device = device; + for (j = 0; j < 4; j++) + newfunc->irq[j] = cur_func->irq[j]; + for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; + prev_func->next = newfunc; + cur_func = newfunc; + break; + case PCI_HEADER_TYPE_BRIDGE: + class >>= 8; + debug ("class now is %x\n", class); + if (class != PCI_CLASS_BRIDGE_PCI) { + err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. " + "Please insert another card.\n", cur_func->device); + return -ENODEV; + } + + assign_alt_irq (cur_func, class_code); + + debug ("cur_func->busno b4 configure_bridge is %x\n", cur_func->busno); + rc = configure_bridge (&cur_func, slotno); + if (rc == -ENODEV) { + err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n"); + err ("Bus %x, devfunc %x \n", cur_func->busno, cur_func->device); + return rc; + } + if (rc) { + /* We need to do this in case some other BARs were properly inserted */ + func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ + err ("was not able to hot-add PPB properly.\n"); + cleanup_count = 2; + goto error; + } + debug ("cur_func->busno = %x, device = %x, function = %x\n", + cur_func->busno, device, function); + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); + debug ("after configuring bridge..., sec_number = %x\n", sec_number); + flag = FALSE; + for (i = 0; i < 32; i++) { + if (func->devices[i]) { + debug ("inside for loop, device is %x\n", i); + newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!newfunc) { + err (" out of system memory \n"); + return -ENOMEM; + } + memset (newfunc, 0, sizeof (struct pci_func)); + newfunc->busno = sec_number; + newfunc->device = (u8) i; + for (j = 0; j < 4; j++) + newfunc->irq[j] = cur_func->irq[j]; + + if (flag) { + for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ; + prev_func->next = newfunc; + } else + cur_func->next = newfunc; + + rc = ibmphp_configure_card (newfunc, slotno); + + /* Again, this case should not happen... For complete paranoia, will need to call remove_bus */ + if (rc) { + /* We need to do this in case some other BARs were properly inserted */ + func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ + cleanup_count = 2; + goto error; + } + flag = TRUE; + } + } + + function = 0x8; + break; + default: + err ("MAJOR PROBLEM!!!!, header type not supported? %x\n", hdr_type); + return -ENXIO; + break; + } /* end of switch */ + } /* end of valid device */ + } /* end of for */ + + if (!valid_device) { + err ("Cannot find any valid devices on the card. Or unable to read from card.\n"); + return -ENODEV; + } + + return 0; + +error: + for (i = 0; i < cleanup_count; i++) { + if (cur_func->io[i]) { + ibmphp_remove_resource (cur_func->io[i]); + cur_func->io[i] = NULL; + } else if (cur_func->pfmem[i]) { + ibmphp_remove_resource (cur_func->pfmem[i]); + cur_func->pfmem[i] = NULL; + } else if (cur_func->mem[i]) { + ibmphp_remove_resource (cur_func->mem[i]); + cur_func->mem[i] = NULL; + } + } + return rc; +} + +/* + * This function configures the pci BARs of a single device. + * Input: pointer to the pci_func + * Output: configured PCI, 0, or error + */ +static int configure_device (struct pci_func *func) +{ + u32 bar[6]; + u32 address[] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + PCI_BASE_ADDRESS_2, + PCI_BASE_ADDRESS_3, + PCI_BASE_ADDRESS_4, + PCI_BASE_ADDRESS_5, + 0 + }; + u8 irq; + int count; + int len[6]; + struct resource_node *io[6]; + struct resource_node *mem[6]; + struct resource_node *mem_tmp; + struct resource_node *pfmem[6]; + unsigned int devfn; + + debug ("%s - inside\n", __FUNCTION__); + + devfn = PCI_DEVFN(func->device, func->function); + ibmphp_pci_bus->number = func->busno; + + for (count = 0; address[count]; count++) { /* for 6 BARs */ + + /* not sure if i need this. per scott, said maybe need smth like this + if devices don't adhere 100% to the spec, so don't want to write + to the reserved bits + + pcibios_read_config_byte(cur_func->busno, cur_func->device, + PCI_BASE_ADDRESS_0 + 4 * count, &tmp); + if (tmp & 0x01) // IO + pcibios_write_config_dword(cur_func->busno, cur_func->device, + PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFD); + else // Memory + pcibios_write_config_dword(cur_func->busno, cur_func->device, + PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFF); + */ + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); + + if (!bar[count]) /* This BAR is not implemented */ + continue; + + debug ("Device %x BAR %d wants %x\n", func->device, count, bar[count]); + + if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) { + /* This is IO */ + debug ("inside IO SPACE\n"); + + len[count] = bar[count] & 0xFFFFFFFC; + len[count] = ~len[count] + 1; + + debug ("len[count] in IO %x, count %d\n", len[count], count); + + io[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + + if (!io[count]) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (io[count], 0, sizeof (struct resource_node)); + io[count]->type = IO; + io[count]->busno = func->busno; + io[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); + io[count]->len = len[count]; + if (ibmphp_check_resource(io[count], 0) == 0) { + ibmphp_add_resource (io[count]); + func->io[count] = io[count]; + } else { + err ("cannot allocate requested io for bus %x device %x function %x len %x\n", + func->busno, func->device, func->function, len[count]); + kfree (io[count]); + return -EIO; + } + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->io[count]->start); + + /* _______________This is for debugging purposes only_____________________ */ + debug ("b4 writing, the IO address is %x\n", func->io[count]->start); + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); + debug ("after writing.... the start address is %x\n", bar[count]); + /* _________________________________________________________________________*/ + + } else { + /* This is Memory */ + if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) { + /* pfmem */ + debug ("PFMEM SPACE\n"); + + len[count] = bar[count] & 0xFFFFFFF0; + len[count] = ~len[count] + 1; + + debug ("len[count] in PFMEM %x, count %d\n", len[count], count); + + pfmem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!pfmem[count]) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (pfmem[count], 0, sizeof (struct resource_node)); + pfmem[count]->type = PFMEM; + pfmem[count]->busno = func->busno; + pfmem[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); + pfmem[count]->len = len[count]; + pfmem[count]->fromMem = FALSE; + if (ibmphp_check_resource (pfmem[count], 0) == 0) { + ibmphp_add_resource (pfmem[count]); + func->pfmem[count] = pfmem[count]; + } else { + mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem_tmp) { + err ("out of system memory \n"); + kfree (pfmem[count]); + return -ENOMEM; + } + memset (mem_tmp, 0, sizeof (struct resource_node)); + mem_tmp->type = MEM; + mem_tmp->busno = pfmem[count]->busno; + mem_tmp->devfunc = pfmem[count]->devfunc; + mem_tmp->len = pfmem[count]->len; + debug ("there's no pfmem... going into mem.\n"); + if (ibmphp_check_resource (mem_tmp, 0) == 0) { + ibmphp_add_resource (mem_tmp); + pfmem[count]->fromMem = TRUE; + pfmem[count]->rangeno = mem_tmp->rangeno; + pfmem[count]->start = mem_tmp->start; + pfmem[count]->end = mem_tmp->end; + ibmphp_add_pfmem_from_mem (pfmem[count]); + func->pfmem[count] = pfmem[count]; + } else { + err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n", + func->busno, func->device, len[count]); + kfree (mem_tmp); + kfree (pfmem[count]); + return -EIO; + } + } + + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->pfmem[count]->start); + + /*_______________This is for debugging purposes only______________________________*/ + debug ("b4 writing, start address is %x\n", func->pfmem[count]->start); + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); + debug ("after writing, start address is %x\n", bar[count]); + /*_________________________________________________________________________________*/ + + if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */ + debug ("inside the mem 64 case, count %d\n", count); + count += 1; + /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000); + } + } else { + /* regular memory */ + debug ("REGULAR MEM SPACE\n"); + + len[count] = bar[count] & 0xFFFFFFF0; + len[count] = ~len[count] + 1; + + debug ("len[count] in Mem %x, count %d\n", len[count], count); + + mem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem[count]) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (mem[count], 0, sizeof (struct resource_node)); + mem[count]->type = MEM; + mem[count]->busno = func->busno; + mem[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); + mem[count]->len = len[count]; + if (ibmphp_check_resource (mem[count], 0) == 0) { + ibmphp_add_resource (mem[count]); + func->mem[count] = mem[count]; + } else { + err ("cannot allocate requested mem for bus %x, device %x, len %x\n", + func->busno, func->device, len[count]); + kfree (mem[count]); + return -EIO; + } + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->mem[count]->start); + /* _______________________This is for debugging purposes only _______________________*/ + debug ("b4 writing, start address is %x\n", func->mem[count]->start); + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); + debug ("after writing, the address is %x\n", bar[count]); + /* __________________________________________________________________________________*/ + + if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + debug ("inside mem 64 case, reg. mem, count %d\n", count); + count += 1; + /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000); + } + } + } /* end of mem */ + } /* end of for */ + + func->bus = 0; /* To indicate that this is not a PPB */ + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_PIN, &irq); + if ((irq > 0x00) && (irq < 0x05)) + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_LINE, func->irq[irq - 1]); + + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_CACHE_LINE_SIZE, CACHE); + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_LATENCY_TIMER, LATENCY); + + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_ROM_ADDRESS, 0x00L); + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_COMMAND, DEVICEENABLE); + + return 0; +} + +/****************************************************************************** + * This routine configures a PCI-2-PCI bridge and the functions behind it + * Parameters: pci_func + * Returns: + ******************************************************************************/ +static int configure_bridge (struct pci_func **func_passed, u8 slotno) +{ + int count; + int i; + int rc; + u8 sec_number; + u8 io_base; + u16 pfmem_base; + u32 bar[2]; + u32 len[2]; + u8 flag_io = FALSE; + u8 flag_mem = FALSE; + u8 flag_pfmem = FALSE; + u8 need_io_upper = FALSE; + u8 need_pfmem_upper = FALSE; + struct res_needed *amount_needed = NULL; + struct resource_node *io = NULL; + struct resource_node *bus_io[2] = {NULL, NULL}; + struct resource_node *mem = NULL; + struct resource_node *bus_mem[2] = {NULL, NULL}; + struct resource_node *mem_tmp = NULL; + struct resource_node *pfmem = NULL; + struct resource_node *bus_pfmem[2] = {NULL, NULL}; + struct bus_node *bus; + u32 address[] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + 0 + }; + struct pci_func *func = *func_passed; + unsigned int devfn; + u8 irq; + int retval; + + debug ("%s - enter\n", __FUNCTION__); + + devfn = PCI_DEVFN(func->function, func->device); + ibmphp_pci_bus->number = func->busno; + + /* Configuring necessary info for the bridge so that we could see the devices + * behind it + */ + + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, func->busno); + + /* _____________________For debugging purposes only __________________________ + pci_bus_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, &pri_number); + debug ("primary # written into the bridge is %x\n", pri_number); + ___________________________________________________________________________*/ + + /* in EBDA, only get allocated 1 additional bus # per slot */ + sec_number = find_sec_number (func->busno, slotno); + if (sec_number == 0xff) { + err ("cannot allocate secondary bus number for the bridged device \n"); + return -EINVAL; + } + + debug ("after find_sec_number, the number we got is %x\n", sec_number); + debug ("AFTER FIND_SEC_NUMBER, func->busno IS %x\n", func->busno); + + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, sec_number); + + /* __________________For debugging purposes only __________________________________ + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); + debug ("sec_number after write/read is %x\n", sec_number); + ________________________________________________________________________________*/ + + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, sec_number); + + /* __________________For debugging purposes only ____________________________________ + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, &sec_number); + debug ("subordinate number after write/read is %x\n", sec_number); + __________________________________________________________________________________*/ + + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_CACHE_LINE_SIZE, CACHE); + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_LATENCY_TIMER, LATENCY); + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SEC_LATENCY_TIMER, LATENCY); + + debug ("func->busno is %x\n", func->busno); + debug ("sec_number after writing is %x\n", sec_number); + + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !!!!!!!!!!!!!!!NEED TO ADD!!! FAST BACK-TO-BACK ENABLE!!!!!!!!!!!!!!!!!!!! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + + + /* First we need to allocate mem/io for the bridge itself in case it needs it */ + for (count = 0; address[count]; count++) { /* for 2 BARs */ + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); + + if (!bar[count]) { + /* This BAR is not implemented */ + debug ("so we come here then, eh?, count = %d\n", count); + continue; + } + // tmp_bar = bar[count]; + + debug ("Bar %d wants %x\n", count, bar[count]); + + if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) { + /* This is IO */ + len[count] = bar[count] & 0xFFFFFFFC; + len[count] = ~len[count] + 1; + + debug ("len[count] in IO = %x\n", len[count]); + + bus_io[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + + if (!bus_io[count]) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (bus_io[count], 0, sizeof (struct resource_node)); + bus_io[count]->type = IO; + bus_io[count]->busno = func->busno; + bus_io[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); + bus_io[count]->len = len[count]; + if (ibmphp_check_resource (bus_io[count], 0) == 0) { + ibmphp_add_resource (bus_io[count]); + func->io[count] = bus_io[count]; + } else { + err ("cannot allocate requested io for bus %x, device %x, len %x\n", + func->busno, func->device, len[count]); + kfree (bus_io[count]); + return -EIO; + } + + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->io[count]->start); + + } else { + /* This is Memory */ + if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) { + /* pfmem */ + len[count] = bar[count] & 0xFFFFFFF0; + len[count] = ~len[count] + 1; + + debug ("len[count] in PFMEM = %x\n", len[count]); + + bus_pfmem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!bus_pfmem[count]) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (bus_pfmem[count], 0, sizeof (struct resource_node)); + bus_pfmem[count]->type = PFMEM; + bus_pfmem[count]->busno = func->busno; + bus_pfmem[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); + bus_pfmem[count]->len = len[count]; + bus_pfmem[count]->fromMem = FALSE; + if (ibmphp_check_resource (bus_pfmem[count], 0) == 0) { + ibmphp_add_resource (bus_pfmem[count]); + func->pfmem[count] = bus_pfmem[count]; + } else { + mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem_tmp) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (mem_tmp, 0, sizeof (struct resource_node)); + mem_tmp->type = MEM; + mem_tmp->busno = bus_pfmem[count]->busno; + mem_tmp->devfunc = bus_pfmem[count]->devfunc; + mem_tmp->len = bus_pfmem[count]->len; + if (ibmphp_check_resource (mem_tmp, 0) == 0) { + ibmphp_add_resource (mem_tmp); + bus_pfmem[count]->fromMem = TRUE; + bus_pfmem[count]->rangeno = mem_tmp->rangeno; + ibmphp_add_pfmem_from_mem (bus_pfmem[count]); + func->pfmem[count] = bus_pfmem[count]; + } else { + err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n", + func->busno, func->device, len[count]); + kfree (mem_tmp); + kfree (bus_pfmem[count]); + return -EIO; + } + } + + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->pfmem[count]->start); + + if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000); + + } + } else { + /* regular memory */ + len[count] = bar[count] & 0xFFFFFFF0; + len[count] = ~len[count] + 1; + + debug ("len[count] in Memory is %x\n", len[count]); + + bus_mem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!bus_mem[count]) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (bus_mem[count], 0, sizeof (struct resource_node)); + bus_mem[count]->type = MEM; + bus_mem[count]->busno = func->busno; + bus_mem[count]->devfunc = ((func->device << 3) | (func->function & 0x7)); + bus_mem[count]->len = len[count]; + if (ibmphp_check_resource (bus_mem[count], 0) == 0) { + ibmphp_add_resource (bus_mem[count]); + func->mem[count] = bus_mem[count]; + } else { + err ("cannot allocate requested mem for bus %x, device %x, len %x\n", + func->busno, func->device, len[count]); + kfree (bus_mem[count]); + return -EIO; + } + + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->mem[count]->start); + + if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + /* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */ + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0x00000000); + + } + } + } /* end of mem */ + } /* end of for */ + + /* Now need to see how much space the devices behind the bridge needed */ + amount_needed = scan_behind_bridge (func, sec_number); + if (amount_needed == NULL) + return -ENOMEM; + + ibmphp_pci_bus->number = func->busno; + debug ("after coming back from scan_behind_bridge\n"); + debug ("amount_needed->not_correct = %x\n", amount_needed->not_correct); + debug ("amount_needed->io = %x\n", amount_needed->io); + debug ("amount_needed->mem = %x\n", amount_needed->mem); + debug ("amount_needed->pfmem = %x\n", amount_needed->pfmem); + + if (amount_needed->not_correct) { + debug ("amount_needed is not correct \n"); + for (count = 0; address[count]; count++) { + /* for 2 BARs */ + if (bus_io[count]) { + ibmphp_remove_resource (bus_io[count]); + func->io[count] = NULL; + } else if (bus_pfmem[count]) { + ibmphp_remove_resource (bus_pfmem[count]); + func->pfmem[count] = NULL; + } else if (bus_mem[count]) { + ibmphp_remove_resource (bus_mem[count]); + func->mem[count] = NULL; + } + } + kfree (amount_needed); + return -ENODEV; + } + + if (!amount_needed->io) { + debug ("it doesn't want IO?\n"); + flag_io = TRUE; + } else { + debug ("it wants %x IO behind the bridge \n", amount_needed->io); + io = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + + if (!io) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (io, 0, sizeof (struct resource_node)); + io->type = IO; + io->busno = func->busno; + io->devfunc = ((func->device << 3) | (func->function & 0x7)); + io->len = amount_needed->io; + if (ibmphp_check_resource (io, 1) == 0) { + debug ("were we able to add io\n"); + ibmphp_add_resource (io); + flag_io = TRUE; + } + } + + if (!amount_needed->mem) { + debug ("it doesn't want n.e.memory?\n"); + flag_mem = TRUE; + } else { + debug ("it wants %x memory behind the bridge\n", amount_needed->mem); + mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (mem, 0, sizeof (struct resource_node)); + mem->type = MEM; + mem->busno = func->busno; + mem->devfunc = ((func->device << 3) | (func->function & 0x7)); + mem->len = amount_needed->mem; + if (ibmphp_check_resource (mem, 1) == 0) { + ibmphp_add_resource (mem); + flag_mem = TRUE; + debug ("were we able to add mem\n"); + } + } + + if (!amount_needed->pfmem) { + debug ("it doesn't want n.e.pfmem mem?\n"); + flag_pfmem = TRUE; + } else { + debug ("it wants %x pfmemory behind the bridge\n", amount_needed->pfmem); + pfmem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!pfmem) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (pfmem, 0, sizeof (struct resource_node)); + pfmem->type = PFMEM; + pfmem->busno = func->busno; + pfmem->devfunc = ((func->device << 3) | (func->function & 0x7)); + pfmem->len = amount_needed->pfmem; + pfmem->fromMem = FALSE; + if (ibmphp_check_resource (pfmem, 1) == 0) { + ibmphp_add_resource (pfmem); + flag_pfmem = TRUE; + } else { + mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem_tmp) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (mem_tmp, 0, sizeof (struct resource_node)); + mem_tmp->type = MEM; + mem_tmp->busno = pfmem->busno; + mem_tmp->devfunc = pfmem->devfunc; + mem_tmp->len = pfmem->len; + if (ibmphp_check_resource (mem_tmp, 1) == 0) { + ibmphp_add_resource (mem_tmp); + pfmem->fromMem = TRUE; + pfmem->rangeno = mem_tmp->rangeno; + ibmphp_add_pfmem_from_mem (pfmem); + flag_pfmem = TRUE; + } + } + } + + debug ("b4 if (flag_io && flag_mem && flag_pfmem)\n"); + debug ("flag_io = %x, flag_mem = %x, flag_pfmem = %x\n", flag_io, flag_mem, flag_pfmem); + + if (flag_io && flag_mem && flag_pfmem) { + /* If on bootup, there was a bridged card in this slot, + * then card was removed and ibmphp got unloaded and loaded + * back again, there's no way for us to remove the bus + * struct, so no need to kmalloc, can use existing node + */ + bus = ibmphp_find_res_bus (sec_number); + if (!bus) { + bus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); + if (!bus) { + err ("out of system memory \n"); + retval = -ENOMEM; + goto error; + } + memset (bus, 0, sizeof (struct bus_node)); + bus->busno = sec_number; + debug ("b4 adding new bus\n"); + rc = add_new_bus (bus, io, mem, pfmem, func->busno); + } else if (!(bus->rangeIO) && !(bus->rangeMem) && !(bus->rangePFMem)) + rc = add_new_bus (bus, io, mem, pfmem, 0xFF); + else { + err ("expected bus structure not empty? \n"); + retval = -EIO; + goto error; + } + if (rc) { + if (rc == -ENOMEM) { + ibmphp_remove_bus (bus, func->busno); + kfree (amount_needed); + return rc; + } + retval = rc; + goto error; + } + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &io_base); + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &pfmem_base); + + if ((io_base & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { + debug ("io 32\n"); + need_io_upper = TRUE; + } + if ((io_base & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { + debug ("pfmem 64\n"); + need_pfmem_upper = TRUE; + } + + if (bus->noIORanges) { + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00 | bus->rangeIO->start >> 8); + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00 | bus->rangeIO->end >> 8); + + /* _______________This is for debugging purposes only ____________________ + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &temp); + debug ("io_base = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8); + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &temp); + debug ("io_limit = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8); + ________________________________________________________________________*/ + + if (need_io_upper) { /* since can't support n.e.ways */ + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, 0x0000); + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, 0x0000); + } + } else { + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00); + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00); + } + + if (bus->noMemRanges) { + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0x0000 | bus->rangeMem->start >> 16); + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000 | bus->rangeMem->end >> 16); + + /* ____________________This is for debugging purposes only ________________________ + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &temp); + debug ("mem_base = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &temp); + debug ("mem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); + __________________________________________________________________________________*/ + + } else { + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0xffff); + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000); + } + if (bus->noPFMemRanges) { + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, 0x0000 | bus->rangePFMem->start >> 16); + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, 0x0000 | bus->rangePFMem->end >> 16); + + /* __________________________This is for debugging purposes only _______________________ + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &temp); + debug ("pfmem_base = %x", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &temp); + debug ("pfmem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16); + ______________________________________________________________________________________*/ + + if (need_pfmem_upper) { /* since can't support n.e.ways */ + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, 0x00000000); + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, 0x00000000); + } + } else { + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, 0xffff); + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, 0x0000); + } + + debug ("b4 writing control information\n"); + + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_PIN, &irq); + if ((irq > 0x00) && (irq < 0x05)) + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_LINE, func->irq[irq - 1]); + /* + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, ctrl); + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_PARITY); + pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_SERR); + */ + + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_COMMAND, DEVICEENABLE); + pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, 0x07); + for (i = 0; i < 32; i++) { + if (amount_needed->devices[i]) { + debug ("device where devices[i] is 1 = %x\n", i); + func->devices[i] = 1; + } + } + func->bus = 1; /* For unconfiguring, to indicate it's PPB */ + func_passed = &func; + debug ("func->busno b4 returning is %x\n", func->busno); + debug ("func->busno b4 returning in the other structure is %x\n", (*func_passed)->busno); + kfree (amount_needed); + return 0; + } else { + err ("Configuring bridge was unsuccessful... \n"); + mem_tmp = NULL; + retval = -EIO; + goto error; + } + +error: + if (amount_needed) + kfree (amount_needed); + if (pfmem) + ibmphp_remove_resource (pfmem); + if (io) + ibmphp_remove_resource (io); + if (mem) + ibmphp_remove_resource (mem); + for (i = 0; i < 2; i++) { /* for 2 BARs */ + if (bus_io[i]) { + ibmphp_remove_resource (bus_io[i]); + func->io[i] = NULL; + } else if (bus_pfmem[i]) { + ibmphp_remove_resource (bus_pfmem[i]); + func->pfmem[i] = NULL; + } else if (bus_mem[i]) { + ibmphp_remove_resource (bus_mem[i]); + func->mem[i] = NULL; + } + } + return retval; +} + +/***************************************************************************** + * This function adds up the amount of resources needed behind the PPB bridge + * and passes it to the configure_bridge function + * Input: bridge function + * Ouput: amount of resources needed + *****************************************************************************/ +static struct res_needed *scan_behind_bridge (struct pci_func * func, u8 busno) +{ + int count, len[6]; + u16 vendor_id; + u8 hdr_type; + u8 device, function; + unsigned int devfn; + int howmany = 0; /*this is to see if there are any devices behind the bridge */ + + u32 bar[6], class; + u32 address[] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + PCI_BASE_ADDRESS_2, + PCI_BASE_ADDRESS_3, + PCI_BASE_ADDRESS_4, + PCI_BASE_ADDRESS_5, + 0 + }; + struct res_needed *amount; + + amount = kmalloc (sizeof (struct res_needed), GFP_KERNEL); + if (amount == NULL) + return NULL; + memset (amount, 0, sizeof (struct res_needed)); + + ibmphp_pci_bus->number = busno; + + debug ("the bus_no behind the bridge is %x\n", busno); + debug ("scanning devices behind the bridge...\n"); + for (device = 0; device < 32; device++) { + amount->devices[device] = 0; + for (function = 0; function < 8; function++) { + devfn = PCI_DEVFN(device, function); + + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); + + if (vendor_id != PCI_VENDOR_ID_NOTVALID) { + /* found correct device!!! */ + howmany++; + + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class); + + debug ("hdr_type behind the bridge is %x\n", hdr_type); + if (hdr_type & PCI_HEADER_TYPE_BRIDGE) { + err ("embedded bridges not supported for hot-plugging.\n"); + amount->not_correct = TRUE; + return amount; + } + + class >>= 8; /* to take revision out, class = class.subclass.prog i/f */ + if (class == PCI_CLASS_NOT_DEFINED_VGA) { + err ("The device %x is VGA compatible and as is not supported for hot plugging. " + "Please choose another device.\n", device); + amount->not_correct = TRUE; + return amount; + } else if (class == PCI_CLASS_DISPLAY_VGA) { + err ("The device %x is not supported for hot plugging. " + "Please choose another device.\n", device); + amount->not_correct = TRUE; + return amount; + } + + amount->devices[device] = 1; + + for (count = 0; address[count]; count++) { + /* for 6 BARs */ + /* + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, address[count], &tmp); + if (tmp & 0x01) // IO + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFD); + else // MEMORY + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); + */ + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]); + + debug ("what is bar[count]? %x, count = %d\n", bar[count], count); + + if (!bar[count]) /* This BAR is not implemented */ + continue; + + //tmp_bar = bar[count]; + + debug ("count %d device %x function %x wants %x resources \n", count, device, function, bar[count]); + + if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) { + /* This is IO */ + len[count] = bar[count] & 0xFFFFFFFC; + len[count] = ~len[count] + 1; + amount->io += len[count]; + } else { + /* This is Memory */ + if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) { + /* pfmem */ + len[count] = bar[count] & 0xFFFFFFF0; + len[count] = ~len[count] + 1; + amount->pfmem += len[count]; + if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) + /* takes up another dword */ + count += 1; + + } else { + /* regular memory */ + len[count] = bar[count] & 0xFFFFFFF0; + len[count] = ~len[count] + 1; + amount->mem += len[count]; + if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + } + } + } + } /* end for */ + } /* end if (valid) */ + } /* end for */ + } /* end for */ + + if (!howmany) + amount->not_correct = TRUE; + else + amount->not_correct = FALSE; + if ((amount->io) && (amount->io < IOBRIDGE)) + amount->io = IOBRIDGE; + if ((amount->mem) && (amount->mem < MEMBRIDGE)) + amount->mem = MEMBRIDGE; + if ((amount->pfmem) && (amount->pfmem < MEMBRIDGE)) + amount->pfmem = MEMBRIDGE; + return amount; +} + +/* The following 3 unconfigure_boot_ routines deal with the case when we had the card + * upon bootup in the system, since we don't allocate func to such case, we need to read + * the start addresses from pci config space and then find the corresponding entries in + * our resource lists. The functions return either 0, -ENODEV, or -1 (general failure) + * Change: we also call these functions even if we configured the card ourselves (i.e., not + * the bootup case), since it should work same way + */ +static int unconfigure_boot_device (u8 busno, u8 device, u8 function) +{ + u32 start_address; + u32 address[] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + PCI_BASE_ADDRESS_2, + PCI_BASE_ADDRESS_3, + PCI_BASE_ADDRESS_4, + PCI_BASE_ADDRESS_5, + 0 + }; + int count; + struct resource_node *io; + struct resource_node *mem; + struct resource_node *pfmem; + struct bus_node *bus; + u32 end_address; + u32 temp_end; + u32 size; + u32 tmp_address; + unsigned int devfn; + + debug ("%s - enter\n", __FUNCTION__); + + bus = ibmphp_find_res_bus (busno); + if (!bus) { + debug ("cannot find corresponding bus.\n"); + return -EINVAL; + } + + devfn = PCI_DEVFN(device, function); + ibmphp_pci_bus->number = busno; + for (count = 0; address[count]; count++) { /* for 6 BARs */ + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &start_address); + + /* We can do this here, b/c by that time the device driver of the card has been stopped */ + + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF); + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &size); + pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], start_address); + + debug ("start_address is %x\n", start_address); + debug ("busno, device, function %x %x %x\n", busno, device, function); + if (!size) { + /* This BAR is not implemented */ + debug ("is this bar no implemented?, count = %d\n", count); + continue; + } + tmp_address = start_address; + if (start_address & PCI_BASE_ADDRESS_SPACE_IO) { + /* This is IO */ + start_address &= PCI_BASE_ADDRESS_IO_MASK; + size = size & 0xFFFFFFFC; + size = ~size + 1; + end_address = start_address + size - 1; + if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) { + err ("cannot find corresponding IO resource to remove\n"); + return -EIO; + } + debug ("io->start = %x\n", io->start); + temp_end = io->end; + start_address = io->end + 1; + ibmphp_remove_resource (io); + /* This is needed b/c of the old I/O restrictions in the BIOS */ + while (temp_end < end_address) { + if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) { + err ("cannot find corresponding IO resource to remove\n"); + return -EIO; + } + debug ("io->start = %x\n", io->start); + temp_end = io->end; + start_address = io->end + 1; + ibmphp_remove_resource (io); + } + + /* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */ + } else { + /* This is Memory */ + if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) { + /* pfmem */ + start_address &= PCI_BASE_ADDRESS_MEM_MASK; + debug ("start address of pfmem is %x\n", start_address); + + if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) { + err ("cannot find corresponding PFMEM resource to remove\n"); + return -EIO; + } + if (pfmem) + debug ("pfmem->start = %x\n", pfmem->start); + + ibmphp_remove_resource (pfmem); + + if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + } + + } else { + /* regular memory */ + start_address &= PCI_BASE_ADDRESS_MEM_MASK; + debug ("start address of mem is %x\n", start_address); + if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) { + err ("cannot find corresponding MEM resource to remove\n"); + return -EIO; + } + if (mem) + debug ("mem->start = %x\n", mem->start); + + ibmphp_remove_resource (mem); + + if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + } + } + } /* end of mem */ + } /* end of for */ + + return 0; +} + +static int unconfigure_boot_bridge (u8 busno, u8 device, u8 function) +{ + int count; + int bus_no, pri_no, sub_no, sec_no = 0; + u32 start_address, tmp_address; + u8 sec_number, sub_number, pri_number; + struct resource_node *io = NULL; + struct resource_node *mem = NULL; + struct resource_node *pfmem = NULL; + struct bus_node *bus; + u32 address[] = { + PCI_BASE_ADDRESS_0, + PCI_BASE_ADDRESS_1, + 0 + }; + unsigned int devfn; + + devfn = PCI_DEVFN(device, function); + ibmphp_pci_bus->number = busno; + bus_no = (int) busno; + debug ("busno is %x\n", busno); + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, &pri_number); + debug ("%s - busno = %x, primary_number = %x\n", __FUNCTION__, busno, pri_number); + + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); + debug ("sec_number is %x\n", sec_number); + sec_no = (int) sec_number; + pri_no = (int) pri_number; + if (pri_no != bus_no) { + err ("primary numbers in our structures and pci config space don't match.\n"); + return -EINVAL; + } + + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number); + sec_no = (int) sec_no; + + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, &sub_number); + sub_no = (int) sub_number; + debug ("sub_no is %d, sec_no is %d\n", sub_no, sec_no); + if (sec_no != sub_number) { + err ("there're more buses behind this bridge. Hot removal is not supported. Please choose another card\n"); + return -ENODEV; + } + + bus = ibmphp_find_res_bus (sec_number); + debug ("bus->busno is %x\n", bus->busno); + debug ("sec_number is %x\n", sec_number); + if (!bus) { + err ("cannot find Bus structure for the bridged device\n"); + return -EINVAL; + } + + ibmphp_remove_bus (bus, busno); + + for (count = 0; address[count]; count++) { + /* for 2 BARs */ + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &start_address); + + if (!start_address) { + /* This BAR is not implemented */ + continue; + } + + tmp_address = start_address; + + if (start_address & PCI_BASE_ADDRESS_SPACE_IO) { + /* This is IO */ + start_address &= PCI_BASE_ADDRESS_IO_MASK; + if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) { + err ("cannot find corresponding IO resource to remove\n"); + return -EIO; + } + if (io) + debug ("io->start = %x\n", io->start); + + ibmphp_remove_resource (io); + + /* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */ + } else { + /* This is Memory */ + if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) { + /* pfmem */ + start_address &= PCI_BASE_ADDRESS_MEM_MASK; + if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) { + err ("cannot find corresponding PFMEM resource to remove\n"); + return -EINVAL; + } + if (pfmem) + debug ("pfmem->start = %x\n", pfmem->start); + + ibmphp_remove_resource (pfmem); + + if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + } + + } else { + /* regular memory */ + start_address &= PCI_BASE_ADDRESS_MEM_MASK; + if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) { + err ("cannot find corresponding MEM resource to remove\n"); + return -EINVAL; + } + if (mem) + debug ("mem->start = %x\n", mem->start); + + ibmphp_remove_resource (mem); + + if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) { + /* takes up another dword */ + count += 1; + } + } + } /* end of mem */ + } /* end of for */ + debug ("%s - exiting, returning success\n", __FUNCTION__); + return 0; +} + +static int unconfigure_boot_card (struct slot *slot_cur) +{ + u16 vendor_id; + u32 class; + u8 hdr_type; + u8 device; + u8 busno; + u8 function; + int rc; + unsigned int devfn; + u8 valid_device = 0x00; /* To see if we are ever able to find valid device and read it */ + + debug ("%s - enter\n", __FUNCTION__); + + device = slot_cur->device; + busno = slot_cur->bus; + + debug ("b4 for loop, device is %x\n", device); + /* For every function on the card */ + for (function = 0x0; function < 0x08; function++) { + devfn = PCI_DEVFN(device, function); + ibmphp_pci_bus->number = busno; + + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); + + if (vendor_id != PCI_VENDOR_ID_NOTVALID) { + /* found correct device!!! */ + ++valid_device; + + debug ("%s - found correct device\n", __FUNCTION__); + + /* header: x x x x x x x x + * | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge + * |_=> 0 = single function device, 1 = multi-function device + */ + + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class); + + debug ("hdr_type %x, class %x\n", hdr_type, class); + class >>= 8; /* to take revision out, class = class.subclass.prog i/f */ + if (class == PCI_CLASS_NOT_DEFINED_VGA) { + err ("The device %x function %x is VGA compatible and is not supported for hot removing. " + "Please choose another device.\n", device, function); + return -ENODEV; + } else if (class == PCI_CLASS_DISPLAY_VGA) { + err ("The device %x function %x is not supported for hot removing. " + "Please choose another device.\n", device, function); + return -ENODEV; + } + + switch (hdr_type) { + case PCI_HEADER_TYPE_NORMAL: + rc = unconfigure_boot_device (busno, device, function); + if (rc) { + err ("was not able to unconfigure device %x func %x on bus %x. bailing out... \n", + device, function, busno); + return rc; + } + function = 0x8; + break; + case PCI_HEADER_TYPE_MULTIDEVICE: + rc = unconfigure_boot_device (busno, device, function); + if (rc) { + err ("was not able to unconfigure device %x func %x on bus %x. bailing out... \n", + device, function, busno); + return rc; + } + break; + case PCI_HEADER_TYPE_BRIDGE: + class >>= 8; + if (class != PCI_CLASS_BRIDGE_PCI) { + err ("This device %x function %x is not PCI-to-PCI bridge, " + "and is not supported for hot-removing. " + "Please try another card.\n", device, function); + return -ENODEV; + } + rc = unconfigure_boot_bridge (busno, device, function); + if (rc != 0) { + err ("was not able to hot-remove PPB properly.\n"); + return rc; + } + + function = 0x8; + break; + case PCI_HEADER_TYPE_MULTIBRIDGE: + class >>= 8; + if (class != PCI_CLASS_BRIDGE_PCI) { + err ("This device %x function %x is not PCI-to-PCI bridge, " + "and is not supported for hot-removing. " + "Please try another card.\n", device, function); + return -ENODEV; + } + rc = unconfigure_boot_bridge (busno, device, function); + if (rc != 0) { + err ("was not able to hot-remove PPB properly.\n"); + return rc; + } + break; + default: + err ("MAJOR PROBLEM!!!! Cannot read device's header \n"); + return -1; + break; + } /* end of switch */ + } /* end of valid device */ + } /* end of for */ + + if (!valid_device) { + err ("Could not find device to unconfigure. Or could not read the card. \n"); + return -1; + } + return 0; +} + +/* + * free the resources of the card (multi, single, or bridged) + * Parameters: slot, flag to say if this is for removing entire module or just + * unconfiguring the device + * TO DO: will probably need to add some code in case there was some resource, + * to remove it... this is from when we have errors in the configure_card... + * !!!!!!!!!!!!!!!!!!!!!!!!!FOR BUSES!!!!!!!!!!!! + * Returns: 0, -1, -ENODEV + */ +int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end) +{ + int i; + int count; + int rc; + struct slot *sl = *slot_cur; + struct pci_func *cur_func = NULL; + struct pci_func *temp_func; + + debug ("%s - enter\n", __FUNCTION__); + + if (!the_end) { + /* Need to unconfigure the card */ + rc = unconfigure_boot_card (sl); + if ((rc == -ENODEV) || (rc == -EIO) || (rc == -EINVAL)) { + /* In all other cases, will still need to get rid of func structure if it exists */ + return rc; + } + } + + if (sl->func) { + cur_func = sl->func; + while (cur_func) { + /* TO DO: WILL MOST LIKELY NEED TO GET RID OF THE BUS STRUCTURE FROM RESOURCES AS WELL */ + if (cur_func->bus) { + /* in other words, it's a PPB */ + count = 2; + } else { + count = 6; + } + + for (i = 0; i < count; i++) { + if (cur_func->io[i]) { + debug ("io[%d] exists \n", i); + if (the_end > 0) + ibmphp_remove_resource (cur_func->io[i]); + cur_func->io[i] = NULL; + } + if (cur_func->mem[i]) { + debug ("mem[%d] exists \n", i); + if (the_end > 0) + ibmphp_remove_resource (cur_func->mem[i]); + cur_func->mem[i] = NULL; + } + if (cur_func->pfmem[i]) { + debug ("pfmem[%d] exists \n", i); + if (the_end > 0) + ibmphp_remove_resource (cur_func->pfmem[i]); + cur_func->pfmem[i] = NULL; + } + } + + temp_func = cur_func->next; + kfree (cur_func); + cur_func = temp_func; + } + } + + sl->func = NULL; + *slot_cur = sl; + debug ("%s - exit\n", __FUNCTION__); + return 0; +} + +/* + * add a new bus resulting from hot-plugging a PPB bridge with devices + * + * Input: bus and the amount of resources needed (we know we can assign those, + * since they've been checked already + * Output: bus added to the correct spot + * 0, -1, error + */ +static int add_new_bus (struct bus_node *bus, struct resource_node *io, struct resource_node *mem, struct resource_node *pfmem, u8 parent_busno) +{ + struct range_node *io_range = NULL; + struct range_node *mem_range = NULL; + struct range_node *pfmem_range = NULL; + struct bus_node *cur_bus = NULL; + + /* Trying to find the parent bus number */ + if (parent_busno != 0xFF) { + cur_bus = ibmphp_find_res_bus (parent_busno); + if (!cur_bus) { + err ("strange, cannot find bus which is supposed to be at the system... something is terribly wrong...\n"); + return -ENODEV; + } + + list_add (&bus->bus_list, &cur_bus->bus_list); + } + if (io) { + io_range = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!io_range) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (io_range, 0, sizeof (struct range_node)); + io_range->start = io->start; + io_range->end = io->end; + io_range->rangeno = 1; + bus->noIORanges = 1; + bus->rangeIO = io_range; + } + if (mem) { + mem_range = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!mem_range) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (mem_range, 0, sizeof (struct range_node)); + mem_range->start = mem->start; + mem_range->end = mem->end; + mem_range->rangeno = 1; + bus->noMemRanges = 1; + bus->rangeMem = mem_range; + } + if (pfmem) { + pfmem_range = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!pfmem_range) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (pfmem_range, 0, sizeof (struct range_node)); + pfmem_range->start = pfmem->start; + pfmem_range->end = pfmem->end; + pfmem_range->rangeno = 1; + bus->noPFMemRanges = 1; + bus->rangePFMem = pfmem_range; + } + return 0; +} + +/* + * find the 1st available bus number for PPB to set as its secondary bus + * Parameters: bus_number of the primary bus + * Returns: bus_number of the secondary bus or 0xff in case of failure + */ +static u8 find_sec_number (u8 primary_busno, u8 slotno) +{ + int min, max; + u8 busno; + struct bus_info *bus; + struct bus_node *bus_cur; + + bus = ibmphp_find_same_bus_num (primary_busno); + if (!bus) { + err ("cannot get slot range of the bus from the BIOS\n"); + return 0xff; + } + max = bus->slot_max; + min = bus->slot_min; + if ((slotno > max) || (slotno < min)) { + err ("got the wrong range\n"); + return 0xff; + } + busno = (u8) (slotno - (u8) min); + busno += primary_busno + 0x01; + bus_cur = ibmphp_find_res_bus (busno); + /* either there is no such bus number, or there are no ranges, which + * can only happen if we removed the bridged device in previous load + * of the driver, and now only have the skeleton bus struct + */ + if ((!bus_cur) || (!(bus_cur->rangeIO) && !(bus_cur->rangeMem) && !(bus_cur->rangePFMem))) + return busno; + return 0xff; +} + diff -Nru a/drivers/pci/hotplug/ibmphp_res.c b/drivers/pci/hotplug/ibmphp_res.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/ibmphp_res.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,2157 @@ +/* + * IBM Hot Plug Controller Driver + * + * Written By: Irene Zubarev, IBM Corporation + * + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001,2002 IBM Corp. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include "ibmphp.h" + +static int flags = 0; /* for testing */ + +static void update_resources (struct bus_node *bus_cur, int type, int rangeno); +static int once_over (void); +static int remove_ranges (struct bus_node *, struct bus_node *); +static int update_bridge_ranges (struct bus_node **); +static int add_range (int type, struct range_node *, struct bus_node *); +static void fix_resources (struct bus_node *); +static inline struct bus_node *find_bus_wprev (u8, struct bus_node **, u8); + +static LIST_HEAD(gbuses); +LIST_HEAD(ibmphp_res_head); + +static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc * curr, u8 busno, int flag) +{ + struct bus_node * newbus; + + if (!(curr) && !(flag)) { + err ("NULL pointer passed \n"); + return NULL; + } + + newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); + if (!newbus) { + err ("out of system memory \n"); + return NULL; + } + + memset (newbus, 0, sizeof (struct bus_node)); + if (flag) + newbus->busno = busno; + else + newbus->busno = curr->bus_num; + list_add_tail (&newbus->bus_list, &gbuses); + return newbus; +} + +static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc * curr) +{ + struct resource_node *rs; + + if (!curr) { + err ("NULL passed to allocate \n"); + return NULL; + } + + rs = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!rs) { + err ("out of system memory \n"); + return NULL; + } + memset (rs, 0, sizeof (struct resource_node)); + rs->busno = curr->bus_num; + rs->devfunc = curr->dev_fun; + rs->start = curr->start_addr; + rs->end = curr->end_addr; + rs->len = curr->end_addr - curr->start_addr + 1; + return rs; +} + +static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node **new_range, struct ebda_pci_rsrc *curr, int flag, u8 first_bus) +{ + struct bus_node * newbus; + struct range_node *newrange; + u8 num_ranges = 0; + + if (first_bus) { + newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); + if (!newbus) { + err ("out of system memory. \n"); + return -ENOMEM; + } + memset (newbus, 0, sizeof (struct bus_node)); + newbus->busno = curr->bus_num; + } else { + newbus = *new_bus; + switch (flag) { + case MEM: + num_ranges = newbus->noMemRanges; + break; + case PFMEM: + num_ranges = newbus->noPFMemRanges; + break; + case IO: + num_ranges = newbus->noIORanges; + break; + } + } + + newrange = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!newrange) { + if (first_bus) + kfree (newbus); + err ("out of system memory \n"); + return -ENOMEM; + } + memset (newrange, 0, sizeof (struct range_node)); + newrange->start = curr->start_addr; + newrange->end = curr->end_addr; + + if (first_bus || (!num_ranges)) + newrange->rangeno = 1; + else { + /* need to insert our range */ + add_range (flag, newrange, newbus); + debug ("%d resource Primary Bus inserted on bus %x [%x - %x]\n", flag, newbus->busno, newrange->start, newrange->end); + } + + switch (flag) { + case MEM: + newbus->rangeMem = newrange; + if (first_bus) + newbus->noMemRanges = 1; + else { + debug ("First Memory Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + ++newbus->noMemRanges; + fix_resources (newbus); + } + break; + case IO: + newbus->rangeIO = newrange; + if (first_bus) + newbus->noIORanges = 1; + else { + debug ("First IO Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + ++newbus->noIORanges; + fix_resources (newbus); + } + break; + case PFMEM: + newbus->rangePFMem = newrange; + if (first_bus) + newbus->noPFMemRanges = 1; + else { + debug ("1st PFMemory Primary on Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + ++newbus->noPFMemRanges; + fix_resources (newbus); + } + + break; + } + + *new_bus = newbus; + *new_range = newrange; + return 0; +} + + +/* Notes: + * 1. The ranges are ordered. The buses are not ordered. (First come) + * + * 2. If cannot allocate out of PFMem range, allocate from Mem ranges. PFmemFromMem + * are not sorted. (no need since use mem node). To not change the entire code, we + * also add mem node whenever this case happens so as not to change + * ibmphp_check_mem_resource etc (and since it really is taking Mem resource) + */ + +/***************************************************************************** + * This is the Resource Management initialization function. It will go through + * the Resource list taken from EBDA and fill in this module's data structures + * + * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES, + * SINCE WE'RE GOING TO ASSUME FOR NOW WE DON'T HAVE THOSE ON OUR BUSES FOR NOW + * + * Input: ptr to the head of the resource list from EBDA + * Output: 0, -1 or error codes + ***************************************************************************/ +int __init ibmphp_rsrc_init (void) +{ + struct ebda_pci_rsrc *curr; + struct range_node *newrange = NULL; + struct bus_node *newbus = NULL; + struct bus_node *bus_cur; + struct bus_node *bus_prev; + struct list_head *tmp; + struct resource_node *new_io = NULL; + struct resource_node *new_mem = NULL; + struct resource_node *new_pfmem = NULL; + int rc; + struct list_head *tmp_ebda; + + list_for_each (tmp_ebda, &ibmphp_ebda_pci_rsrc_head) { + curr = list_entry (tmp_ebda, struct ebda_pci_rsrc, ebda_pci_rsrc_list); + if (!(curr->rsrc_type & PCIDEVMASK)) { + /* EBDA still lists non PCI devices, so ignore... */ + debug ("this is not a PCI DEVICE in rsrc_init, please take care\n"); + // continue; + } + + /* this is a primary bus resource */ + if (curr->rsrc_type & PRIMARYBUSMASK) { + /* memory */ + if ((curr->rsrc_type & RESTYPE) == MMASK) { + /* no bus structure exists in place yet */ + if (list_empty (&gbuses)) { + if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1))) + return rc; + list_add_tail (&newbus->bus_list, &gbuses); + debug ("gbuses = NULL, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + } else { + bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); + /* found our bus */ + if (bus_cur) { + rc = alloc_bus_range (&bus_cur, &newrange, curr, MEM, 0); + if (rc) + return rc; + } else { + /* went through all the buses and didn't find ours, need to create a new bus node */ + if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1))) + return rc; + + list_add_tail (&newbus->bus_list, &gbuses); + debug ("New Bus, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + } + } + } else if ((curr->rsrc_type & RESTYPE) == PFMASK) { + /* prefetchable memory */ + if (list_empty (&gbuses)) { + /* no bus structure exists in place yet */ + if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1))) + return rc; + list_add_tail (&newbus->bus_list, &gbuses); + debug ("gbuses = NULL, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + } else { + bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); + if (bus_cur) { + /* found our bus */ + rc = alloc_bus_range (&bus_cur, &newrange, curr, PFMEM, 0); + if (rc) + return rc; + } else { + /* went through all the buses and didn't find ours, need to create a new bus node */ + if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1))) + return rc; + list_add_tail (&newbus->bus_list, &gbuses); + debug ("1st Bus, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + } + } + } else if ((curr->rsrc_type & RESTYPE) == IOMASK) { + /* IO */ + if (list_empty (&gbuses)) { + /* no bus structure exists in place yet */ + if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1))) + return rc; + list_add_tail (&newbus->bus_list, &gbuses); + debug ("gbuses = NULL, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + } else { + bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1); + if (bus_cur) { + rc = alloc_bus_range (&bus_cur, &newrange, curr, IO, 0); + if (rc) + return rc; + } else { + /* went through all the buses and didn't find ours, need to create a new bus node */ + if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1))) + return rc; + list_add_tail (&newbus->bus_list, &gbuses); + debug ("1st Bus, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end); + } + } + + } else { + ; /* type is reserved WHAT TO DO IN THIS CASE??? + NOTHING TO DO??? */ + } + } else { + /* regular pci device resource */ + if ((curr->rsrc_type & RESTYPE) == MMASK) { + /* Memory resource */ + new_mem = alloc_resources (curr); + if (!new_mem) + return -ENOMEM; + new_mem->type = MEM; + /* + * if it didn't find the bus, means PCI dev + * came b4 the Primary Bus info, so need to + * create a bus rangeno becomes a problem... + * assign a -1 and then update once the range + * actually appears... + */ + if (ibmphp_add_resource (new_mem) < 0) { + newbus = alloc_error_bus (curr, 0, 0); + if (!newbus) + return -ENOMEM; + newbus->firstMem = new_mem; + ++newbus->needMemUpdate; + new_mem->rangeno = -1; + } + debug ("Memory resource for device %x, bus %x, [%x - %x]\n", new_mem->devfunc, new_mem->busno, new_mem->start, new_mem->end); + + } else if ((curr->rsrc_type & RESTYPE) == PFMASK) { + /* PFMemory resource */ + new_pfmem = alloc_resources (curr); + if (!new_pfmem) + return -ENOMEM; + new_pfmem->type = PFMEM; + new_pfmem->fromMem = FALSE; + if (ibmphp_add_resource (new_pfmem) < 0) { + newbus = alloc_error_bus (curr, 0, 0); + if (!newbus) + return -ENOMEM; + newbus->firstPFMem = new_pfmem; + ++newbus->needPFMemUpdate; + new_pfmem->rangeno = -1; + } + + debug ("PFMemory resource for device %x, bus %x, [%x - %x]\n", new_pfmem->devfunc, new_pfmem->busno, new_pfmem->start, new_pfmem->end); + } else if ((curr->rsrc_type & RESTYPE) == IOMASK) { + /* IO resource */ + new_io = alloc_resources (curr); + if (!new_io) + return -ENOMEM; + new_io->type = IO; + + /* + * if it didn't find the bus, means PCI dev + * came b4 the Primary Bus info, so need to + * create a bus rangeno becomes a problem... + * Can assign a -1 and then update once the + * range actually appears... + */ + if (ibmphp_add_resource (new_io) < 0) { + newbus = alloc_error_bus (curr, 0, 0); + if (!newbus) + return -ENOMEM; + newbus->firstIO = new_io; + ++newbus->needIOUpdate; + new_io->rangeno = -1; + } + debug ("IO resource for device %x, bus %x, [%x - %x]\n", new_io->devfunc, new_io->busno, new_io->start, new_io->end); + } + } + } + + list_for_each (tmp, &gbuses) { + bus_cur = list_entry (tmp, struct bus_node, bus_list); + /* This is to get info about PPB resources, since EBDA doesn't put this info into the primary bus info */ + rc = update_bridge_ranges (&bus_cur); + if (rc) + return rc; + } + rc = once_over (); /* This is to align ranges (so no -1) */ + if (rc) + return rc; + return 0; +} + +/******************************************************************************** + * This function adds a range into a sorted list of ranges per bus for a particular + * range type, it then calls another routine to update the range numbers on the + * pci devices' resources for the appropriate resource + * + * Input: type of the resource, range to add, current bus + * Output: 0 or -1, bus and range ptrs + ********************************************************************************/ +static int add_range (int type, struct range_node *range, struct bus_node *bus_cur) +{ + struct range_node *range_cur = NULL; + struct range_node *range_prev; + int count = 0, i_init; + int noRanges = 0; + + switch (type) { + case MEM: + range_cur = bus_cur->rangeMem; + noRanges = bus_cur->noMemRanges; + break; + case PFMEM: + range_cur = bus_cur->rangePFMem; + noRanges = bus_cur->noPFMemRanges; + break; + case IO: + range_cur = bus_cur->rangeIO; + noRanges = bus_cur->noIORanges; + break; + } + + range_prev = NULL; + while (range_cur) { + if (range->start < range_cur->start) + break; + range_prev = range_cur; + range_cur = range_cur->next; + count = count + 1; + } + if (!count) { + /* our range will go at the beginning of the list */ + switch (type) { + case MEM: + bus_cur->rangeMem = range; + break; + case PFMEM: + bus_cur->rangePFMem = range; + break; + case IO: + bus_cur->rangeIO = range; + break; + } + range->next = range_cur; + range->rangeno = 1; + i_init = 0; + } else if (!range_cur) { + /* our range will go at the end of the list */ + range->next = NULL; + range_prev->next = range; + range->rangeno = range_prev->rangeno + 1; + return 0; + } else { + /* the range is in the middle */ + range_prev->next = range; + range->next = range_cur; + range->rangeno = range_cur->rangeno; + i_init = range_prev->rangeno; + } + + for (count = i_init; count < noRanges; ++count) { + ++range_cur->rangeno; + range_cur = range_cur->next; + } + + update_resources (bus_cur, type, i_init + 1); + return 0; +} + +/******************************************************************************* + * This routine goes through the list of resources of type 'type' and updates + * the range numbers that they correspond to. It was called from add_range fnc + * + * Input: bus, type of the resource, the rangeno starting from which to update + ******************************************************************************/ +static void update_resources (struct bus_node *bus_cur, int type, int rangeno) +{ + struct resource_node *res = NULL; + u8 eol = FALSE; /* end of list indicator */ + + switch (type) { + case MEM: + if (bus_cur->firstMem) + res = bus_cur->firstMem; + break; + case PFMEM: + if (bus_cur->firstPFMem) + res = bus_cur->firstPFMem; + break; + case IO: + if (bus_cur->firstIO) + res = bus_cur->firstIO; + break; + } + + if (res) { + while (res) { + if (res->rangeno == rangeno) + break; + if (res->next) + res = res->next; + else if (res->nextRange) + res = res->nextRange; + else { + eol = TRUE; + break; + } + } + + if (!eol) { + /* found the range */ + while (res) { + ++res->rangeno; + res = res->next; + } + } + } +} + +static void fix_me (struct resource_node *res, struct bus_node *bus_cur, struct range_node *range) +{ + char * str = ""; + switch (res->type) { + case IO: + str = "io"; + break; + case MEM: + str = "mem"; + break; + case PFMEM: + str = "pfmem"; + break; + } + + while (res) { + if (res->rangeno == -1) { + while (range) { + if ((res->start >= range->start) && (res->end <= range->end)) { + res->rangeno = range->rangeno; + debug ("%s->rangeno in fix_resources is %d\n", str, res->rangeno); + switch (res->type) { + case IO: + --bus_cur->needIOUpdate; + break; + case MEM: + --bus_cur->needMemUpdate; + break; + case PFMEM: + --bus_cur->needPFMemUpdate; + break; + } + break; + } + range = range->next; + } + } + if (res->next) + res = res->next; + else + res = res->nextRange; + } + +} + +/***************************************************************************** + * This routine reassigns the range numbers to the resources that had a -1 + * This case can happen only if upon initialization, resources taken by pci dev + * appear in EBDA before the resources allocated for that bus, since we don't + * know the range, we assign -1, and this routine is called after a new range + * is assigned to see the resources with unknown range belong to the added range + * + * Input: current bus + * Output: none, list of resources for that bus are fixed if can be + *******************************************************************************/ +static void fix_resources (struct bus_node *bus_cur) +{ + struct range_node *range; + struct resource_node *res; + + debug ("%s - bus_cur->busno = %d\n", __FUNCTION__, bus_cur->busno); + + if (bus_cur->needIOUpdate) { + res = bus_cur->firstIO; + range = bus_cur->rangeIO; + fix_me (res, bus_cur, range); + } + if (bus_cur->needMemUpdate) { + res = bus_cur->firstMem; + range = bus_cur->rangeMem; + fix_me (res, bus_cur, range); + } + if (bus_cur->needPFMemUpdate) { + res = bus_cur->firstPFMem; + range = bus_cur->rangePFMem; + fix_me (res, bus_cur, range); + } +} + +/******************************************************************************* + * This routine adds a resource to the list of resources to the appropriate bus + * based on their resource type and sorted by their starting addresses. It assigns + * the ptrs to next and nextRange if needed. + * + * Input: resource ptr + * Output: ptrs assigned (to the node) + * 0 or -1 + *******************************************************************************/ +int ibmphp_add_resource (struct resource_node *res) +{ + struct resource_node *res_cur; + struct resource_node *res_prev; + struct bus_node *bus_cur; + struct range_node *range_cur = NULL; + struct resource_node *res_start = NULL; + + debug ("%s - enter\n", __FUNCTION__); + + if (!res) { + err ("NULL passed to add \n"); + return -ENODEV; + } + + bus_cur = find_bus_wprev (res->busno, NULL, 0); + + if (!bus_cur) { + /* didn't find a bus, smth's wrong!!! */ + debug ("no bus in the system, either pci_dev's wrong or allocation failed\n"); + return -ENODEV; + } + + /* Normal case */ + switch (res->type) { + case IO: + range_cur = bus_cur->rangeIO; + res_start = bus_cur->firstIO; + break; + case MEM: + range_cur = bus_cur->rangeMem; + res_start = bus_cur->firstMem; + break; + case PFMEM: + range_cur = bus_cur->rangePFMem; + res_start = bus_cur->firstPFMem; + break; + default: + err ("cannot read the type of the resource to add... problem \n"); + return -EINVAL; + } + while (range_cur) { + if ((res->start >= range_cur->start) && (res->end <= range_cur->end)) { + res->rangeno = range_cur->rangeno; + break; + } + range_cur = range_cur->next; + } + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * this is again the case of rangeno = -1 + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + + if (!range_cur) { + switch (res->type) { + case IO: + ++bus_cur->needIOUpdate; + break; + case MEM: + ++bus_cur->needMemUpdate; + break; + case PFMEM: + ++bus_cur->needPFMemUpdate; + break; + } + res->rangeno = -1; + } + + debug ("The range is %d\n", res->rangeno); + if (!res_start) { + /* no first{IO,Mem,Pfmem} on the bus, 1st IO/Mem/Pfmem resource ever */ + switch (res->type) { + case IO: + bus_cur->firstIO = res; + break; + case MEM: + bus_cur->firstMem = res; + break; + case PFMEM: + bus_cur->firstPFMem = res; + break; + } + res->next = NULL; + res->nextRange = NULL; + } else { + res_cur = res_start; + res_prev = NULL; + + debug ("res_cur->rangeno is %d\n", res_cur->rangeno); + + while (res_cur) { + if (res_cur->rangeno >= res->rangeno) + break; + res_prev = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + } + + if (!res_cur) { + /* at the end of the resource list */ + debug ("i should be here, [%x - %x]\n", res->start, res->end); + res_prev->nextRange = res; + res->next = NULL; + res->nextRange = NULL; + } else if (res_cur->rangeno == res->rangeno) { + /* in the same range */ + while (res_cur) { + if (res->start < res_cur->start) + break; + res_prev = res_cur; + res_cur = res_cur->next; + } + if (!res_cur) { + /* the last resource in this range */ + res_prev->next = res; + res->next = NULL; + res->nextRange = res_prev->nextRange; + res_prev->nextRange = NULL; + } else if (res->start < res_cur->start) { + /* at the beginning or middle of the range */ + if (!res_prev) { + switch (res->type) { + case IO: + bus_cur->firstIO = res; + break; + case MEM: + bus_cur->firstMem = res; + break; + case PFMEM: + bus_cur->firstPFMem = res; + break; + } + } else if (res_prev->rangeno == res_cur->rangeno) + res_prev->next = res; + else + res_prev->nextRange = res; + + res->next = res_cur; + res->nextRange = NULL; + } + } else { + /* this is the case where it is 1st occurrence of the range */ + if (!res_prev) { + /* at the beginning of the resource list */ + res->next = NULL; + switch (res->type) { + case IO: + res->nextRange = bus_cur->firstIO; + bus_cur->firstIO = res; + break; + case MEM: + res->nextRange = bus_cur->firstMem; + bus_cur->firstMem = res; + break; + case PFMEM: + res->nextRange = bus_cur->firstPFMem; + bus_cur->firstPFMem = res; + break; + } + } else if (res_cur->rangeno > res->rangeno) { + /* in the middle of the resource list */ + res_prev->nextRange = res; + res->next = NULL; + res->nextRange = res_cur; + } + } + } + + debug ("%s - exit\n", __FUNCTION__); + return 0; +} + +/**************************************************************************** + * This routine will remove the resource from the list of resources + * + * Input: io, mem, and/or pfmem resource to be deleted + * Ouput: modified resource list + * 0 or error code + ****************************************************************************/ +int ibmphp_remove_resource (struct resource_node *res) +{ + struct bus_node *bus_cur; + struct resource_node *res_cur = NULL; + struct resource_node *res_prev; + struct resource_node *mem_cur; + char * type = ""; + + if (!res) { + err ("resource to remove is NULL \n"); + return -ENODEV; + } + + bus_cur = find_bus_wprev (res->busno, NULL, 0); + + if (!bus_cur) { + err ("cannot find corresponding bus of the io resource to remove " + "bailing out...\n"); + return -ENODEV; + } + + switch (res->type) { + case IO: + res_cur = bus_cur->firstIO; + type = "io"; + break; + case MEM: + res_cur = bus_cur->firstMem; + type = "mem"; + break; + case PFMEM: + res_cur = bus_cur->firstPFMem; + type = "pfmem"; + break; + default: + err ("unknown type for resource to remove \n"); + return -EINVAL; + } + res_prev = NULL; + + while (res_cur) { + if ((res_cur->start == res->start) && (res_cur->end == res->end)) + break; + res_prev = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + } + + if (!res_cur) { + if (res->type == PFMEM) { + /* + * case where pfmem might be in the PFMemFromMem list + * so will also need to remove the corresponding mem + * entry + */ + res_cur = bus_cur->firstPFMemFromMem; + res_prev = NULL; + + while (res_cur) { + if ((res_cur->start == res->start) && (res_cur->end == res->end)) { + mem_cur = bus_cur->firstMem; + while (mem_cur) { + if ((mem_cur->start == res_cur->start) + && (mem_cur->end == res_cur->end)) + break; + if (mem_cur->next) + mem_cur = mem_cur->next; + else + mem_cur = mem_cur->nextRange; + } + if (!mem_cur) { + err ("cannot find corresponding mem node for pfmem...\n"); + return -EINVAL; + } + + ibmphp_remove_resource (mem_cur); + if (!res_prev) + bus_cur->firstPFMemFromMem = res_cur->next; + else + res_prev->next = res_cur->next; + kfree (res_cur); + return 0; + } + res_prev = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + } + if (!res_cur) { + err ("cannot find pfmem to delete...\n"); + return -EINVAL; + } + } else { + err ("the %s resource is not in the list to be deleted...\n", type); + return -EINVAL; + } + } + if (!res_prev) { + /* first device to be deleted */ + if (res_cur->next) { + switch (res->type) { + case IO: + bus_cur->firstIO = res_cur->next; + break; + case MEM: + bus_cur->firstMem = res_cur->next; + break; + case PFMEM: + bus_cur->firstPFMem = res_cur->next; + break; + } + } else if (res_cur->nextRange) { + switch (res->type) { + case IO: + bus_cur->firstIO = res_cur->nextRange; + break; + case MEM: + bus_cur->firstMem = res_cur->nextRange; + break; + case PFMEM: + bus_cur->firstPFMem = res_cur->nextRange; + break; + } + } else { + switch (res->type) { + case IO: + bus_cur->firstIO = NULL; + break; + case MEM: + bus_cur->firstMem = NULL; + break; + case PFMEM: + bus_cur->firstPFMem = NULL; + break; + } + } + kfree (res_cur); + return 0; + } else { + if (res_cur->next) { + if (res_prev->rangeno == res_cur->rangeno) + res_prev->next = res_cur->next; + else + res_prev->nextRange = res_cur->next; + } else if (res_cur->nextRange) { + res_prev->next = NULL; + res_prev->nextRange = res_cur->nextRange; + } else { + res_prev->next = NULL; + res_prev->nextRange = NULL; + } + kfree (res_cur); + return 0; + } + + return 0; +} + +static struct range_node * find_range (struct bus_node *bus_cur, struct resource_node * res) +{ + struct range_node * range = NULL; + + switch (res->type) { + case IO: + range = bus_cur->rangeIO; + break; + case MEM: + range = bus_cur->rangeMem; + break; + case PFMEM: + range = bus_cur->rangePFMem; + break; + default: + err ("cannot read resource type in find_range \n"); + } + + while (range) { + if (res->rangeno == range->rangeno) + break; + range = range->next; + } + return range; +} + +/***************************************************************************** + * This routine will check to make sure the io/mem/pfmem->len that the device asked for + * can fit w/i our list of available IO/MEM/PFMEM resources. If cannot, returns -EINVAL, + * otherwise, returns 0 + * + * Input: resource + * Ouput: the correct start and end address are inputted into the resource node, + * 0 or -EINVAL + *****************************************************************************/ +int ibmphp_check_resource (struct resource_node *res, u8 bridge) +{ + struct bus_node *bus_cur; + struct range_node *range = NULL; + struct resource_node *res_prev; + struct resource_node *res_cur = NULL; + u32 len_cur = 0, start_cur = 0, len_tmp = 0; + int noranges = 0; + u32 tmp_start; /* this is to make sure start address is divisible by the length needed */ + u32 tmp_divide; + u8 flag = FALSE; + + if (!res) + return -EINVAL; + + if (bridge) { + /* The rules for bridges are different, 4K divisible for IO, 1M for (pf)mem*/ + if (res->type == IO) + tmp_divide = IOBRIDGE; + else + tmp_divide = MEMBRIDGE; + } else + tmp_divide = res->len; + + bus_cur = find_bus_wprev (res->busno, NULL, 0); + + if (!bus_cur) { + /* didn't find a bus, smth's wrong!!! */ + debug ("no bus in the system, either pci_dev's wrong or allocation failed \n"); + return -EINVAL; + } + + debug ("%s - enter\n", __FUNCTION__); + debug ("bus_cur->busno is %d\n", bus_cur->busno); + + /* This is a quick fix to not mess up with the code very much. i.e., + * 2000-2fff, len = 1000, but when we compare, we need it to be fff */ + res->len -= 1; + + switch (res->type) { + case IO: + res_cur = bus_cur->firstIO; + noranges = bus_cur->noIORanges; + break; + case MEM: + res_cur = bus_cur->firstMem; + noranges = bus_cur->noMemRanges; + break; + case PFMEM: + res_cur = bus_cur->firstPFMem; + noranges = bus_cur->noPFMemRanges; + break; + default: + err ("wrong type of resource to check \n"); + return -EINVAL; + } + res_prev = NULL; + + while (res_cur) { + range = find_range (bus_cur, res_cur); + debug ("%s - rangeno = %d\n", __FUNCTION__, res_cur->rangeno); + + if (!range) { + err ("no range for the device exists... bailing out...\n"); + return -EINVAL; + } + + /* found our range */ + if (!res_prev) { + /* first time in the loop */ + if ((res_cur->start != range->start) && ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) { + debug ("len_tmp = %x\n", len_tmp); + + if ((len_tmp < len_cur) || (len_cur == 0)) { + + if ((range->start % tmp_divide) == 0) { + /* just perfect, starting address is divisible by length */ + flag = TRUE; + len_cur = len_tmp; + start_cur = range->start; + } else { + /* Needs adjusting */ + tmp_start = range->start; + flag = FALSE; + + while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { + if ((tmp_start % tmp_divide) == 0) { + flag = TRUE; + len_cur = len_tmp; + start_cur = tmp_start; + break; + } + tmp_start += tmp_divide - tmp_start % tmp_divide; + if (tmp_start >= res_cur->start - 1) + break; + } + } + + if (flag && len_cur == res->len) { + debug ("but we are not here, right?\n"); + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + } + } + if (!res_cur->next) { + /* last device on the range */ + if ((range->end != res_cur->end) && ((len_tmp = range->end - (res_cur->end + 1)) >= res->len)) { + debug ("len_tmp = %x\n", len_tmp); + if ((len_tmp < len_cur) || (len_cur == 0)) { + + if (((res_cur->end + 1) % tmp_divide) == 0) { + /* just perfect, starting address is divisible by length */ + flag = TRUE; + len_cur = len_tmp; + start_cur = res_cur->end + 1; + } else { + /* Needs adjusting */ + tmp_start = res_cur->end + 1; + flag = FALSE; + + while ((len_tmp = range->end - tmp_start) >= res->len) { + if ((tmp_start % tmp_divide) == 0) { + flag = TRUE; + len_cur = len_tmp; + start_cur = tmp_start; + break; + } + tmp_start += tmp_divide - tmp_start % tmp_divide; + if (tmp_start >= range->end) + break; + } + } + if (flag && len_cur == res->len) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + } + } + + if (res_prev) { + if (res_prev->rangeno != res_cur->rangeno) { + /* 1st device on this range */ + if ((res_cur->start != range->start) && + ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) { + if ((len_tmp < len_cur) || (len_cur == 0)) { + if ((range->start % tmp_divide) == 0) { + /* just perfect, starting address is divisible by length */ + flag = TRUE; + len_cur = len_tmp; + start_cur = range->start; + } else { + /* Needs adjusting */ + tmp_start = range->start; + flag = FALSE; + + while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { + if ((tmp_start % tmp_divide) == 0) { + flag = TRUE; + len_cur = len_tmp; + start_cur = tmp_start; + break; + } + tmp_start += tmp_divide - tmp_start % tmp_divide; + if (tmp_start >= res_cur->start - 1) + break; + } + } + + if (flag && len_cur == res->len) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + } + } else { + /* in the same range */ + if ((len_tmp = res_cur->start - 1 - res_prev->end - 1) >= res->len) { + if ((len_tmp < len_cur) || (len_cur == 0)) { + if (((res_prev->end + 1) % tmp_divide) == 0) { + /* just perfect, starting address's divisible by length */ + flag = TRUE; + len_cur = len_tmp; + start_cur = res_prev->end + 1; + } else { + /* Needs adjusting */ + tmp_start = res_prev->end + 1; + flag = FALSE; + + while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) { + if ((tmp_start % tmp_divide) == 0) { + flag = TRUE; + len_cur = len_tmp; + start_cur = tmp_start; + break; + } + tmp_start += tmp_divide - tmp_start % tmp_divide; + if (tmp_start >= res_cur->start - 1) + break; + } + } + + if (flag && len_cur == res->len) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + } + } + } + /* end if (res_prev) */ + res_prev = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + } /* end of while */ + + + if (!res_prev) { + /* 1st device ever */ + /* need to find appropriate range */ + switch (res->type) { + case IO: + range = bus_cur->rangeIO; + break; + case MEM: + range = bus_cur->rangeMem; + break; + case PFMEM: + range = bus_cur->rangePFMem; + break; + } + while (range) { + if ((len_tmp = range->end - range->start) >= res->len) { + if ((len_tmp < len_cur) || (len_cur == 0)) { + if ((range->start % tmp_divide) == 0) { + /* just perfect, starting address's divisible by length */ + flag = TRUE; + len_cur = len_tmp; + start_cur = range->start; + } else { + /* Needs adjusting */ + tmp_start = range->start; + flag = FALSE; + + while ((len_tmp = range->end - tmp_start) >= res->len) { + if ((tmp_start % tmp_divide) == 0) { + flag = TRUE; + len_cur = len_tmp; + start_cur = tmp_start; + break; + } + tmp_start += tmp_divide - tmp_start % tmp_divide; + if (tmp_start >= range->end) + break; + } + } + + if (flag && len_cur == res->len) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + } + range = range->next; + } /* end of while */ + + if ((!range) && (len_cur == 0)) { + /* have gone through the list of devices and ranges and haven't found n.e.thing */ + err ("no appropriate range.. bailing out...\n"); + return -EINVAL; + } else if (len_cur) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + + if (!res_cur) { + debug ("prev->rangeno = %d, noranges = %d\n", res_prev->rangeno, noranges); + if (res_prev->rangeno < noranges) { + /* if there're more ranges out there to check */ + switch (res->type) { + case IO: + range = bus_cur->rangeIO; + break; + case MEM: + range = bus_cur->rangeMem; + break; + case PFMEM: + range = bus_cur->rangePFMem; + break; + } + while (range) { + if ((len_tmp = range->end - range->start) >= res->len) { + if ((len_tmp < len_cur) || (len_cur == 0)) { + if ((range->start % tmp_divide) == 0) { + /* just perfect, starting address's divisible by length */ + flag = TRUE; + len_cur = len_tmp; + start_cur = range->start; + } else { + /* Needs adjusting */ + tmp_start = range->start; + flag = FALSE; + + while ((len_tmp = range->end - tmp_start) >= res->len) { + if ((tmp_start % tmp_divide) == 0) { + flag = TRUE; + len_cur = len_tmp; + start_cur = tmp_start; + break; + } + tmp_start += tmp_divide - tmp_start % tmp_divide; + if (tmp_start >= range->end) + break; + } + } + + if (flag && len_cur == res->len) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } + } + range = range->next; + } /* end of while */ + + if ((!range) && (len_cur == 0)) { + /* have gone through the list of devices and ranges and haven't found n.e.thing */ + err ("no appropriate range.. bailing out...\n"); + return -EINVAL; + } else if (len_cur) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } + } else { + /* no more ranges to check on */ + if (len_cur) { + res->start = start_cur; + res->len += 1; /* To restore the balance */ + res->end = res->start + res->len - 1; + return 0; + } else { + /* have gone through the list of devices and haven't found n.e.thing */ + err ("no appropriate range.. bailing out...\n"); + return -EINVAL; + } + } + } /* end if(!res_cur) */ + return -EINVAL; +} + +/******************************************************************************** + * This routine is called from remove_card if the card contained PPB. + * It will remove all the resources on the bus as well as the bus itself + * Input: Bus + * Ouput: 0, -ENODEV + ********************************************************************************/ +int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno) +{ + struct resource_node *res_cur; + struct resource_node *res_tmp; + struct bus_node *prev_bus; + int rc; + + prev_bus = find_bus_wprev (parent_busno, NULL, 0); + + if (!prev_bus) { + debug ("something terribly wrong. Cannot find parent bus to the one to remove\n"); + return -ENODEV; + } + + debug ("In ibmphp_remove_bus... prev_bus->busno is %x\n", prev_bus->busno); + + rc = remove_ranges (bus, prev_bus); + if (rc) + return rc; + + if (bus->firstIO) { + res_cur = bus->firstIO; + while (res_cur) { + res_tmp = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + kfree (res_tmp); + res_tmp = NULL; + } + bus->firstIO = NULL; + } + if (bus->firstMem) { + res_cur = bus->firstMem; + while (res_cur) { + res_tmp = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + kfree (res_tmp); + res_tmp = NULL; + } + bus->firstMem = NULL; + } + if (bus->firstPFMem) { + res_cur = bus->firstPFMem; + while (res_cur) { + res_tmp = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + kfree (res_tmp); + res_tmp = NULL; + } + bus->firstPFMem = NULL; + } + + if (bus->firstPFMemFromMem) { + res_cur = bus->firstPFMemFromMem; + while (res_cur) { + res_tmp = res_cur; + res_cur = res_cur->next; + + kfree (res_tmp); + res_tmp = NULL; + } + bus->firstPFMemFromMem = NULL; + } + + list_del (&bus->bus_list); + kfree (bus); + return 0; +} + +/****************************************************************************** + * This routine deletes the ranges from a given bus, and the entries from the + * parent's bus in the resources + * Input: current bus, previous bus + * Output: 0, -EINVAL + ******************************************************************************/ +static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev) +{ + struct range_node *range_cur; + struct range_node *range_tmp; + int i; + struct resource_node *res = NULL; + + if (bus_cur->noIORanges) { + range_cur = bus_cur->rangeIO; + for (i = 0; i < bus_cur->noIORanges; i++) { + if (ibmphp_find_resource (bus_prev, range_cur->start, &res, IO) < 0) + return -EINVAL; + ibmphp_remove_resource (res); + + range_tmp = range_cur; + range_cur = range_cur->next; + kfree (range_tmp); + range_tmp = NULL; + } + bus_cur->rangeIO = NULL; + } + if (bus_cur->noMemRanges) { + range_cur = bus_cur->rangeMem; + for (i = 0; i < bus_cur->noMemRanges; i++) { + if (ibmphp_find_resource (bus_prev, range_cur->start, &res, MEM) < 0) + return -EINVAL; + + ibmphp_remove_resource (res); + range_tmp = range_cur; + range_cur = range_cur->next; + kfree (range_tmp); + range_tmp = NULL; + } + bus_cur->rangeMem = NULL; + } + if (bus_cur->noPFMemRanges) { + range_cur = bus_cur->rangePFMem; + for (i = 0; i < bus_cur->noPFMemRanges; i++) { + if (ibmphp_find_resource (bus_prev, range_cur->start, &res, PFMEM) < 0) + return -EINVAL; + + ibmphp_remove_resource (res); + range_tmp = range_cur; + range_cur = range_cur->next; + kfree (range_tmp); + range_tmp = NULL; + } + bus_cur->rangePFMem = NULL; + } + return 0; +} + +/* + * find the resource node in the bus + * Input: Resource needed, start address of the resource, type of resource + */ +int ibmphp_find_resource (struct bus_node *bus, u32 start_address, struct resource_node **res, int flag) +{ + struct resource_node *res_cur = NULL; + char * type = ""; + + if (!bus) { + err ("The bus passed in NULL to find resource \n"); + return -ENODEV; + } + + switch (flag) { + case IO: + res_cur = bus->firstIO; + type = "io"; + break; + case MEM: + res_cur = bus->firstMem; + type = "mem"; + break; + case PFMEM: + res_cur = bus->firstPFMem; + type = "pfmem"; + break; + default: + err ("wrong type of flag \n"); + return -EINVAL; + } + + while (res_cur) { + if (res_cur->start == start_address) { + *res = res_cur; + break; + } + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + } + + if (!res_cur) { + if (flag == PFMEM) { + res_cur = bus->firstPFMemFromMem; + while (res_cur) { + if (res_cur->start == start_address) { + *res = res_cur; + break; + } + res_cur = res_cur->next; + } + if (!res_cur) { + debug ("SOS...cannot find %s resource in the bus. \n", type); + return -EINVAL; + } + } else { + debug ("SOS... cannot find %s resource in the bus. \n", type); + return -EINVAL; + } + } + + if (*res) + debug ("*res->start = %x \n", (*res)->start); + + return 0; +} + +/*********************************************************************** + * This routine will free the resource structures used by the + * system. It is called from cleanup routine for the module + * Parameters: none + * Returns: none + ***********************************************************************/ +void ibmphp_free_resources (void) +{ + struct bus_node *bus_cur = NULL; + struct bus_node *bus_tmp; + struct range_node *range_cur; + struct range_node *range_tmp; + struct resource_node *res_cur; + struct resource_node *res_tmp; + struct list_head *tmp; + struct list_head *next; + int i = 0; + flags = 1; + + list_for_each_safe (tmp, next, &gbuses) { + bus_cur = list_entry (tmp, struct bus_node, bus_list); + if (bus_cur->noIORanges) { + range_cur = bus_cur->rangeIO; + for (i = 0; i < bus_cur->noIORanges; i++) { + if (!range_cur) + break; + range_tmp = range_cur; + range_cur = range_cur->next; + kfree (range_tmp); + range_tmp = NULL; + } + } + if (bus_cur->noMemRanges) { + range_cur = bus_cur->rangeMem; + for (i = 0; i < bus_cur->noMemRanges; i++) { + if (!range_cur) + break; + range_tmp = range_cur; + range_cur = range_cur->next; + kfree (range_tmp); + range_tmp = NULL; + } + } + if (bus_cur->noPFMemRanges) { + range_cur = bus_cur->rangePFMem; + for (i = 0; i < bus_cur->noPFMemRanges; i++) { + if (!range_cur) + break; + range_tmp = range_cur; + range_cur = range_cur->next; + kfree (range_tmp); + range_tmp = NULL; + } + } + + if (bus_cur->firstIO) { + res_cur = bus_cur->firstIO; + while (res_cur) { + res_tmp = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + kfree (res_tmp); + res_tmp = NULL; + } + bus_cur->firstIO = NULL; + } + if (bus_cur->firstMem) { + res_cur = bus_cur->firstMem; + while (res_cur) { + res_tmp = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + kfree (res_tmp); + res_tmp = NULL; + } + bus_cur->firstMem = NULL; + } + if (bus_cur->firstPFMem) { + res_cur = bus_cur->firstPFMem; + while (res_cur) { + res_tmp = res_cur; + if (res_cur->next) + res_cur = res_cur->next; + else + res_cur = res_cur->nextRange; + kfree (res_tmp); + res_tmp = NULL; + } + bus_cur->firstPFMem = NULL; + } + + if (bus_cur->firstPFMemFromMem) { + res_cur = bus_cur->firstPFMemFromMem; + while (res_cur) { + res_tmp = res_cur; + res_cur = res_cur->next; + + kfree (res_tmp); + res_tmp = NULL; + } + bus_cur->firstPFMemFromMem = NULL; + } + + bus_tmp = bus_cur; + list_del (&bus_cur->bus_list); + kfree (bus_tmp); + bus_tmp = NULL; + } +} + +/********************************************************************************* + * This function will go over the PFmem resources to check if the EBDA allocated + * pfmem out of memory buckets of the bus. If so, it will change the range numbers + * and a flag to indicate that this resource is out of memory. It will also move the + * Pfmem out of the pfmem resource list to the PFMemFromMem list, and will create + * a new Mem node + * This routine is called right after initialization + *******************************************************************************/ +static int __init once_over (void) +{ + struct resource_node *pfmem_cur; + struct resource_node *pfmem_prev; + struct resource_node *mem; + struct bus_node *bus_cur; + struct list_head *tmp; + + list_for_each (tmp, &gbuses) { + bus_cur = list_entry (tmp, struct bus_node, bus_list); + if ((!bus_cur->rangePFMem) && (bus_cur->firstPFMem)) { + for (pfmem_cur = bus_cur->firstPFMem, pfmem_prev = NULL; pfmem_cur; pfmem_prev = pfmem_cur, pfmem_cur = pfmem_cur->next) { + pfmem_cur->fromMem = TRUE; + if (pfmem_prev) + pfmem_prev->next = pfmem_cur->next; + else + bus_cur->firstPFMem = pfmem_cur->next; + + if (!bus_cur->firstPFMemFromMem) + pfmem_cur->next = NULL; + else + /* we don't need to sort PFMemFromMem since we're using mem node for + all the real work anyways, so just insert at the beginning of the + list + */ + pfmem_cur->next = bus_cur->firstPFMemFromMem; + + bus_cur->firstPFMemFromMem = pfmem_cur; + + mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (mem, 0, sizeof (struct resource_node)); + mem->type = MEM; + mem->busno = pfmem_cur->busno; + mem->devfunc = pfmem_cur->devfunc; + mem->start = pfmem_cur->start; + mem->end = pfmem_cur->end; + mem->len = pfmem_cur->len; + if (ibmphp_add_resource (mem) < 0) + err ("Trouble...trouble... EBDA allocated pfmem from mem, but system doesn't display it has this space... unless not PCI device...\n"); + pfmem_cur->rangeno = mem->rangeno; + } /* end for pfmem */ + } /* end if */ + } /* end list_for_each bus */ + return 0; +} + +int ibmphp_add_pfmem_from_mem (struct resource_node *pfmem) +{ + struct bus_node *bus_cur = find_bus_wprev (pfmem->busno, NULL, 0); + + if (!bus_cur) { + err ("cannot find bus of pfmem to add...\n"); + return -ENODEV; + } + + if (bus_cur->firstPFMemFromMem) + pfmem->next = bus_cur->firstPFMemFromMem; + else + pfmem->next = NULL; + + bus_cur->firstPFMemFromMem = pfmem; + + return 0; +} + +/* This routine just goes through the buses to see if the bus already exists. + * It is called from ibmphp_find_sec_number, to find out a secondary bus number for + * bridged cards + * Parameters: bus_number + * Returns: Bus pointer or NULL + */ +struct bus_node *ibmphp_find_res_bus (u8 bus_number) +{ + return find_bus_wprev (bus_number, NULL, 0); +} + +static inline struct bus_node *find_bus_wprev (u8 bus_number, struct bus_node **prev, u8 flag) +{ + struct bus_node *bus_cur; + struct list_head *tmp; + struct list_head *tmp_prev; + + list_for_each (tmp, &gbuses) { + tmp_prev = tmp->prev; + bus_cur = list_entry (tmp, struct bus_node, bus_list); + if (flag) + *prev = list_entry (tmp_prev, struct bus_node, bus_list); + if (bus_cur->busno == bus_number) + return bus_cur; + } + + return NULL; +} + +void ibmphp_print_test (void) +{ + int i = 0; + struct bus_node *bus_cur = NULL; + struct range_node *range; + struct resource_node *res; + struct list_head *tmp; + + debug_pci ("*****************START**********************\n"); + + if ((!list_empty(&gbuses)) && flags) { + err ("The GBUSES is not NULL?!?!?!?!?\n"); + return; + } + + list_for_each (tmp, &gbuses) { + bus_cur = list_entry (tmp, struct bus_node, bus_list); + debug_pci ("This is bus # %d. There are \n", bus_cur->busno); + debug_pci ("IORanges = %d\t", bus_cur->noIORanges); + debug_pci ("MemRanges = %d\t", bus_cur->noMemRanges); + debug_pci ("PFMemRanges = %d\n", bus_cur->noPFMemRanges); + debug_pci ("The IO Ranges are as follows:\n"); + if (bus_cur->rangeIO) { + range = bus_cur->rangeIO; + for (i = 0; i < bus_cur->noIORanges; i++) { + debug_pci ("rangeno is %d\n", range->rangeno); + debug_pci ("[%x - %x]\n", range->start, range->end); + range = range->next; + } + } + + debug_pci ("The Mem Ranges are as follows:\n"); + if (bus_cur->rangeMem) { + range = bus_cur->rangeMem; + for (i = 0; i < bus_cur->noMemRanges; i++) { + debug_pci ("rangeno is %d\n", range->rangeno); + debug_pci ("[%x - %x]\n", range->start, range->end); + range = range->next; + } + } + + debug_pci ("The PFMem Ranges are as follows:\n"); + + if (bus_cur->rangePFMem) { + range = bus_cur->rangePFMem; + for (i = 0; i < bus_cur->noPFMemRanges; i++) { + debug_pci ("rangeno is %d\n", range->rangeno); + debug_pci ("[%x - %x]\n", range->start, range->end); + range = range->next; + } + } + + debug_pci ("The resources on this bus are as follows\n"); + + debug_pci ("IO...\n"); + if (bus_cur->firstIO) { + res = bus_cur->firstIO; + while (res) { + debug_pci ("The range # is %d\n", res->rangeno); + debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); + debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); + if (res->next) + res = res->next; + else if (res->nextRange) + res = res->nextRange; + else + break; + } + } + debug_pci ("Mem...\n"); + if (bus_cur->firstMem) { + res = bus_cur->firstMem; + while (res) { + debug_pci ("The range # is %d\n", res->rangeno); + debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); + debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); + if (res->next) + res = res->next; + else if (res->nextRange) + res = res->nextRange; + else + break; + } + } + debug_pci ("PFMem...\n"); + if (bus_cur->firstPFMem) { + res = bus_cur->firstPFMem; + while (res) { + debug_pci ("The range # is %d\n", res->rangeno); + debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); + debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); + if (res->next) + res = res->next; + else if (res->nextRange) + res = res->nextRange; + else + break; + } + } + + debug_pci ("PFMemFromMem...\n"); + if (bus_cur->firstPFMemFromMem) { + res = bus_cur->firstPFMemFromMem; + while (res) { + debug_pci ("The range # is %d\n", res->rangeno); + debug_pci ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); + debug_pci ("[%x - %x], len=%x\n", res->start, res->end, res->len); + res = res->next; + } + } + } + debug_pci ("***********************END***********************\n"); +} + +static int range_exists_already (struct range_node * range, struct bus_node * bus_cur, u8 type) +{ + struct range_node * range_cur = NULL; + switch (type) { + case IO: + range_cur = bus_cur->rangeIO; + break; + case MEM: + range_cur = bus_cur->rangeMem; + break; + case PFMEM: + range_cur = bus_cur->rangePFMem; + break; + default: + err ("wrong type passed to find out if range already exists \n"); + return -ENODEV; + } + + while (range_cur) { + if ((range_cur->start == range->start) && (range_cur->end == range->end)) + return 1; + range_cur = range_cur->next; + } + + return 0; +} + +/* This routine will read the windows for any PPB we have and update the + * range info for the secondary bus, and will also input this info into + * primary bus, since BIOS doesn't. This is for PPB that are in the system + * on bootup. For bridged cards that were added during previous load of the + * driver, only the ranges and the bus structure are added, the devices are + * added from NVRAM + * Input: primary busno + * Returns: none + * Note: this function doesn't take into account IO restrictions etc, + * so will only work for bridges with no video/ISA devices behind them It + * also will not work for onboard PPB's that can have more than 1 *bus + * behind them All these are TO DO. + * Also need to add more error checkings... (from fnc returns etc) + */ +static int __init update_bridge_ranges (struct bus_node **bus) +{ + u8 sec_busno, device, function, hdr_type, start_io_address, end_io_address; + u16 vendor_id, upper_io_start, upper_io_end, start_mem_address, end_mem_address; + u32 start_address, end_address, upper_start, upper_end; + struct bus_node *bus_sec; + struct bus_node *bus_cur; + struct resource_node *io; + struct resource_node *mem; + struct resource_node *pfmem; + struct range_node *range; + unsigned int devfn; + + bus_cur = *bus; + if (!bus_cur) + return -ENODEV; + ibmphp_pci_bus->number = bus_cur->busno; + + debug ("inside %s \n", __FUNCTION__); + debug ("bus_cur->busno = %x\n", bus_cur->busno); + + for (device = 0; device < 32; device++) { + for (function = 0x00; function < 0x08; function++) { + devfn = PCI_DEVFN(device, function); + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id); + + if (vendor_id != PCI_VENDOR_ID_NOTVALID) { + /* found correct device!!! */ + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type); + + switch (hdr_type) { + case PCI_HEADER_TYPE_NORMAL: + function = 0x8; + break; + case PCI_HEADER_TYPE_MULTIDEVICE: + break; + case PCI_HEADER_TYPE_BRIDGE: + function = 0x8; + case PCI_HEADER_TYPE_MULTIBRIDGE: + /* We assume here that only 1 bus behind the bridge + TO DO: add functionality for several: + temp = secondary; + while (temp < subordinate) { + ... + temp++; + } + */ + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_busno); + bus_sec = find_bus_wprev (sec_busno, NULL, 0); + /* this bus structure doesn't exist yet, PPB was configured during previous loading of ibmphp */ + if (!bus_sec) { + bus_sec = alloc_error_bus (NULL, sec_busno, 1); + /* the rest will be populated during NVRAM call */ + return 0; + } + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &start_io_address); + pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &end_io_address); + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, &upper_io_start); + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, &upper_io_end); + start_address = (start_io_address & PCI_IO_RANGE_MASK) << 8; + start_address |= (upper_io_start << 16); + end_address = (end_io_address & PCI_IO_RANGE_MASK) << 8; + end_address |= (upper_io_end << 16); + + if ((start_address) && (start_address <= end_address)) { + range = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!range) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (range, 0, sizeof (struct range_node)); + range->start = start_address; + range->end = end_address + 0xfff; + + if (bus_sec->noIORanges > 0) { + if (!range_exists_already (range, bus_sec, IO)) { + add_range (IO, range, bus_sec); + ++bus_sec->noIORanges; + } else { + kfree (range); + range = NULL; + } + } else { + /* 1st IO Range on the bus */ + range->rangeno = 1; + bus_sec->rangeIO = range; + ++bus_sec->noIORanges; + } + fix_resources (bus_sec); + + if (ibmphp_find_resource (bus_cur, start_address, &io, IO)) { + io = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!io) { + kfree (range); + err ("out of system memory \n"); + return -ENOMEM; + } + memset (io, 0, sizeof (struct resource_node)); + io->type = IO; + io->busno = bus_cur->busno; + io->devfunc = ((device << 3) | (function & 0x7)); + io->start = start_address; + io->end = end_address + 0xfff; + io->len = io->end - io->start + 1; + ibmphp_add_resource (io); + } + } + + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &start_mem_address); + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &end_mem_address); + + start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16; + end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16; + + if ((start_address) && (start_address <= end_address)) { + + range = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!range) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (range, 0, sizeof (struct range_node)); + range->start = start_address; + range->end = end_address + 0xfffff; + + if (bus_sec->noMemRanges > 0) { + if (!range_exists_already (range, bus_sec, MEM)) { + add_range (MEM, range, bus_sec); + ++bus_sec->noMemRanges; + } else { + kfree (range); + range = NULL; + } + } else { + /* 1st Mem Range on the bus */ + range->rangeno = 1; + bus_sec->rangeMem = range; + ++bus_sec->noMemRanges; + } + + fix_resources (bus_sec); + + if (ibmphp_find_resource (bus_cur, start_address, &mem, MEM)) { + mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!mem) { + kfree (range); + err ("out of system memory \n"); + return -ENOMEM; + } + memset (mem, 0, sizeof (struct resource_node)); + mem->type = MEM; + mem->busno = bus_cur->busno; + mem->devfunc = ((device << 3) | (function & 0x7)); + mem->start = start_address; + mem->end = end_address + 0xfffff; + mem->len = mem->end - mem->start + 1; + ibmphp_add_resource (mem); + } + } + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &start_mem_address); + pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &end_mem_address); + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, &upper_start); + pci_bus_read_config_dword (ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, &upper_end); + start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16; + end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16; +#if BITS_PER_LONG == 64 + start_address |= ((long) upper_start) << 32; + end_address |= ((long) upper_end) << 32; +#endif + + if ((start_address) && (start_address <= end_address)) { + + range = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!range) { + err ("out of system memory \n"); + return -ENOMEM; + } + memset (range, 0, sizeof (struct range_node)); + range->start = start_address; + range->end = end_address + 0xfffff; + + if (bus_sec->noPFMemRanges > 0) { + if (!range_exists_already (range, bus_sec, PFMEM)) { + add_range (PFMEM, range, bus_sec); + ++bus_sec->noPFMemRanges; + } else { + kfree (range); + range = NULL; + } + } else { + /* 1st PFMem Range on the bus */ + range->rangeno = 1; + bus_sec->rangePFMem = range; + ++bus_sec->noPFMemRanges; + } + + fix_resources (bus_sec); + if (ibmphp_find_resource (bus_cur, start_address, &pfmem, PFMEM)) { + pfmem = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!pfmem) { + kfree (range); + err ("out of system memory \n"); + return -ENOMEM; + } + memset (pfmem, 0, sizeof (struct resource_node)); + pfmem->type = PFMEM; + pfmem->busno = bus_cur->busno; + pfmem->devfunc = ((device << 3) | (function & 0x7)); + pfmem->start = start_address; + pfmem->end = end_address + 0xfffff; + pfmem->len = pfmem->end - pfmem->start + 1; + pfmem->fromMem = FALSE; + + ibmphp_add_resource (pfmem); + } + } + break; + } /* end of switch */ + } /* end if vendor */ + } /* end for function */ + } /* end for device */ + + bus = &bus_cur; + return 0; +} diff -Nru a/drivers/pci/hotplug/pci_hotplug.h b/drivers/pci/hotplug/pci_hotplug.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/pci_hotplug.h Wed Jun 4 18:11:56 2003 @@ -0,0 +1,146 @@ +/* + * 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 + * + */ +#ifndef _PCI_HOTPLUG_H +#define _PCI_HOTPLUG_H + + +/* These values come from the PCI Hotplug Spec */ +enum pci_bus_speed { + PCI_SPEED_33MHz = 0x00, + PCI_SPEED_66MHz = 0x01, + PCI_SPEED_66MHz_PCIX = 0x02, + PCI_SPEED_100MHz_PCIX = 0x03, + PCI_SPEED_133MHz_PCIX = 0x04, + PCI_SPEED_66MHz_PCIX_266 = 0x09, + PCI_SPEED_100MHz_PCIX_266 = 0x0a, + PCI_SPEED_133MHz_PCIX_266 = 0x0b, + PCI_SPEED_66MHz_PCIX_533 = 0x11, + PCI_SPEED_100MHz_PCIX_533 = 0X12, + PCI_SPEED_133MHz_PCIX_533 = 0x13, + PCI_SPEED_UNKNOWN = 0xff, +}; + +struct hotplug_slot; +struct hotplug_slot_attribute { + struct attribute attr; + ssize_t (*show)(struct hotplug_slot *, char *); + ssize_t (*store)(struct hotplug_slot *, const char *, size_t); +}; +/** + * struct hotplug_slot_ops -the callbacks that the hotplug pci core can use + * @owner: The module owner of this structure + * @enable_slot: Called when the user wants to enable a specific pci slot + * @disable_slot: Called when the user wants to disable a specific pci slot + * @set_attention_status: Called to set the specific slot's attention LED to + * the specified value + * @hardware_test: Called to run a specified hardware test on the specified + * slot. + * @get_power_status: Called to get the current power status of a slot. + * If this field is NULL, the value passed in the struct hotplug_slot_info + * will be used when this value is requested by a user. + * @get_attention_status: Called to get the current attention status of a slot. + * If this field is NULL, the value passed in the struct hotplug_slot_info + * will be used when this value is requested by a user. + * @get_latch_status: Called to get the current latch status of a slot. + * If this field is NULL, the value passed in the struct hotplug_slot_info + * will be used when this value is requested by a user. + * @get_adapter_status: Called to get see if an adapter is present in the slot or not. + * If this field is NULL, the value passed in the struct hotplug_slot_info + * will be used when this value is requested by a user. + * @get_max_bus_speed: Called to get the max bus speed for a slot. + * If this field is NULL, the value passed in the struct hotplug_slot_info + * will be used when this value is requested by a user. + * @get_cur_bus_speed: Called to get the current bus speed for a slot. + * If this field is NULL, the value passed in the struct hotplug_slot_info + * will be used when this value is requested by a user. + * + * The table of function pointers that is passed to the hotplug pci core by a + * hotplug pci driver. These functions are called by the hotplug pci core when + * the user wants to do something to a specific slot (query it for information, + * set an LED, enable / disable power, etc.) + */ +struct hotplug_slot_ops { + struct module *owner; + int (*enable_slot) (struct hotplug_slot *slot); + int (*disable_slot) (struct hotplug_slot *slot); + int (*set_attention_status) (struct hotplug_slot *slot, u8 value); + int (*hardware_test) (struct hotplug_slot *slot, u32 value); + int (*get_power_status) (struct hotplug_slot *slot, u8 *value); + int (*get_attention_status) (struct hotplug_slot *slot, u8 *value); + int (*get_latch_status) (struct hotplug_slot *slot, u8 *value); + int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value); + int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); + int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); +}; + +/** + * struct hotplug_slot_info - used to notify the hotplug pci core of the state of the slot + * @power: if power is enabled or not (1/0) + * @attention_status: if the attention light is enabled or not (1/0) + * @latch_status: if the latch (if any) is open or closed (1/0) + * @adapter_present: if there is a pci board present in the slot or not (1/0) + * + * 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; + enum pci_bus_speed max_bus_speed; + enum pci_bus_speed cur_bus_speed; +}; + +/** + * struct hotplug_slot - used to register a physical slot with the hotplug pci core + * @name: the name of the slot being registered. This string must + * be unique amoung slots registered on this system. + * @ops: pointer to the &struct hotplug_slot_ops to be used for this slot + * @info: pointer to the &struct hotplug_slot_info for the inital values for + * this slot. + * @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 kobject kobj; +}; + +extern int pci_hp_register (struct hotplug_slot *slot); +extern int pci_hp_deregister (struct hotplug_slot *slot); +extern int pci_hp_change_slot_info (struct hotplug_slot *slot, + struct hotplug_slot_info *info); + +#endif + diff -Nru a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/pci_hotplug_core.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,666 @@ +/* + * PCI HotPlug Controller Core + * + * Copyright (c) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001-2002 IBM Corp. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + * Filesystem portion based on work done by Pat Mochel on ddfs/driverfs + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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: %s: " fmt , MY_NAME , __FUNCTION__ , ## arg); } while (0) +#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) + + +/* local variables */ +static int debug; + +#define DRIVER_VERSION "0.5" +#define DRIVER_AUTHOR "Greg Kroah-Hartman , Scott Murray " +#define DRIVER_DESC "PCI Hot Plug PCI Core" + + +////////////////////////////////////////////////////////////////// + +static LIST_HEAD(pci_hotplug_slot_list); + +static struct subsystem hotplug_slots_subsys; + +static ssize_t hotplug_slot_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct hotplug_slot *slot=container_of(kobj, + struct hotplug_slot,kobj); + struct hotplug_slot_attribute *attribute = + container_of(attr, struct hotplug_slot_attribute, attr); + return attribute->show ? attribute->show(slot, buf) : 0; +} + +static ssize_t hotplug_slot_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t len) +{ + struct hotplug_slot *slot=container_of(kobj, + struct hotplug_slot,kobj); + struct hotplug_slot_attribute *attribute = + container_of(attr, struct hotplug_slot_attribute, attr); + return attribute->store ? attribute->store(slot, buf, len) : 0; +} + +static struct sysfs_ops hotplug_slot_sysfs_ops = { + .show = hotplug_slot_attr_show, + .store = hotplug_slot_attr_store, +}; + +static struct kobj_type hotplug_slot_ktype = { + .sysfs_ops = &hotplug_slot_sysfs_ops +}; + +static decl_subsys(hotplug_slots, &hotplug_slot_ktype, NULL); + + +/* these strings match up with the values in pci_bus_speed */ +static char *pci_bus_speed_strings[] = { + "33 MHz PCI", /* 0x00 */ + "66 MHz PCI", /* 0x01 */ + "66 MHz PCIX", /* 0x02 */ + "100 MHz PCIX", /* 0x03 */ + "133 MHz PCIX", /* 0x04 */ + NULL, /* 0x05 */ + NULL, /* 0x06 */ + NULL, /* 0x07 */ + NULL, /* 0x08 */ + "66 MHz PCIX 266", /* 0x09 */ + "100 MHz PCIX 266", /* 0x0a */ + "133 MHz PCIX 266", /* 0x0b */ + NULL, /* 0x0c */ + NULL, /* 0x0d */ + NULL, /* 0x0e */ + NULL, /* 0x0f */ + NULL, /* 0x10 */ + "66 MHz PCIX 533", /* 0x11 */ + "100 MHz PCIX 533", /* 0x12 */ + "133 MHz PCIX 533", /* 0x13 */ +}; + +#ifdef CONFIG_HOTPLUG_PCI_CPCI +extern int cpci_hotplug_init(int debug); +extern void cpci_hotplug_exit(void); +#else +static inline int cpci_hotplug_init(int debug) { return 0; } +static inline void cpci_hotplug_exit(void) { } +#endif + +/* Weee, fun with macros... */ +#define GET_STATUS(name,type) \ +static int get_##name (struct hotplug_slot *slot, type *value) \ +{ \ + struct hotplug_slot_ops *ops = slot->ops; \ + int retval = 0; \ + if (try_module_get(ops->owner)) { \ + if (ops->get_##name) \ + retval = ops->get_##name (slot, value); \ + else \ + *value = slot->info->name; \ + module_put(ops->owner); \ + } \ + return retval; \ +} + +GET_STATUS(power_status, u8) +GET_STATUS(attention_status, u8) +GET_STATUS(latch_status, u8) +GET_STATUS(adapter_status, u8) +GET_STATUS(max_bus_speed, enum pci_bus_speed) +GET_STATUS(cur_bus_speed, enum pci_bus_speed) + +static ssize_t power_read_file (struct hotplug_slot *slot, char *buf) +{ + int retval; + u8 value; + + retval = get_power_status (slot, &value); + if (retval) + goto exit; + retval = sprintf (buf, "%d\n", value); +exit: + return retval; +} + +static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf, + size_t count) +{ + unsigned long lpower; + u8 power; + int retval = 0; + + lpower = simple_strtoul (buf, NULL, 10); + power = (u8)(lpower & 0xff); + dbg ("power = %d\n", power); + + if (!try_module_get(slot->ops->owner)) { + retval = -ENODEV; + goto exit; + } + switch (power) { + case 0: + if (slot->ops->disable_slot) + retval = slot->ops->disable_slot(slot); + break; + + case 1: + if (slot->ops->enable_slot) + retval = slot->ops->enable_slot(slot); + break; + + default: + err ("Illegal value specified for power\n"); + retval = -EINVAL; + } + module_put(slot->ops->owner); + +exit: + if (retval) + return retval; + return count; +} + +static struct hotplug_slot_attribute hotplug_slot_attr_power = { + .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR}, + .show = power_read_file, + .store = power_write_file +}; + +static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf) +{ + int retval; + u8 value; + + retval = get_attention_status (slot, &value); + if (retval) + goto exit; + retval = sprintf (buf, "%d\n", value); + +exit: + return retval; +} + +static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf, + size_t count) +{ + unsigned long lattention; + u8 attention; + int retval = 0; + + lattention = simple_strtoul (buf, NULL, 10); + attention = (u8)(lattention & 0xff); + dbg (" - attention = %d\n", attention); + + if (!try_module_get(slot->ops->owner)) { + retval = -ENODEV; + goto exit; + } + if (slot->ops->set_attention_status) + retval = slot->ops->set_attention_status(slot, attention); + module_put(slot->ops->owner); + +exit: + if (retval) + return retval; + return count; +} + +static struct hotplug_slot_attribute hotplug_slot_attr_attention = { + .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR}, + .show = attention_read_file, + .store = attention_write_file +}; + +static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf) +{ + int retval; + u8 value; + + retval = get_latch_status (slot, &value); + if (retval) + goto exit; + retval = sprintf (buf, "%d\n", value); + +exit: + return retval; +} + +static struct hotplug_slot_attribute hotplug_slot_attr_latch = { + .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO | S_IWUSR}, + .show = latch_read_file, +}; + +static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf) +{ + int retval; + u8 value; + + retval = get_adapter_status (slot, &value); + if (retval) + goto exit; + retval = sprintf (buf, "%d\n", value); + +exit: + return retval; +} + +static struct hotplug_slot_attribute hotplug_slot_attr_presence = { + .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO | S_IWUSR}, + .show = presence_read_file, +}; + +static char *unknown_speed = "Unknown bus speed"; + +static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf) +{ + char *speed_string; + int retval; + enum pci_bus_speed value; + + retval = get_max_bus_speed (slot, &value); + if (retval) + goto exit; + + if (value == PCI_SPEED_UNKNOWN) + speed_string = unknown_speed; + else + speed_string = pci_bus_speed_strings[value]; + + retval = sprintf (buf, "%s\n", speed_string); + +exit: + return retval; +} + +static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = { + .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO | S_IWUSR}, + .show = max_bus_speed_read_file, +}; + +static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf) +{ + char *speed_string; + int retval; + enum pci_bus_speed value; + + retval = get_cur_bus_speed (slot, &value); + if (retval) + goto exit; + + if (value == PCI_SPEED_UNKNOWN) + speed_string = unknown_speed; + else + speed_string = pci_bus_speed_strings[value]; + + retval = sprintf (buf, "%s\n", speed_string); + +exit: + return retval; +} + +static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = { + .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO | S_IWUSR}, + .show = cur_bus_speed_read_file, +}; + +static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf, + size_t count) +{ + unsigned long ltest; + u32 test; + int retval = 0; + + ltest = simple_strtoul (buf, NULL, 10); + test = (u32)(ltest & 0xffffffff); + dbg ("test = %d\n", test); + + if (!try_module_get(slot->ops->owner)) { + retval = -ENODEV; + goto exit; + } + if (slot->ops->hardware_test) + retval = slot->ops->hardware_test(slot, test); + module_put(slot->ops->owner); + +exit: + if (retval) + return retval; + return count; +} + +static struct hotplug_slot_attribute hotplug_slot_attr_test = { + .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR}, + .store = test_write_file +}; + +static int has_power_file (struct hotplug_slot *slot) +{ + if ((!slot) || (!slot->ops)) + return -ENODEV; + if ((slot->ops->enable_slot) || + (slot->ops->disable_slot) || + (slot->ops->get_power_status)) + return 0; + return -ENOENT; +} + +static int has_attention_file (struct hotplug_slot *slot) +{ + if ((!slot) || (!slot->ops)) + return -ENODEV; + if ((slot->ops->set_attention_status) || + (slot->ops->get_attention_status)) + return 0; + return -ENOENT; +} + +static int has_latch_file (struct hotplug_slot *slot) +{ + if ((!slot) || (!slot->ops)) + return -ENODEV; + if (slot->ops->get_latch_status) + return 0; + return -ENOENT; +} + +static int has_adapter_file (struct hotplug_slot *slot) +{ + if ((!slot) || (!slot->ops)) + return -ENODEV; + if (slot->ops->get_adapter_status) + return 0; + return -ENOENT; +} + +static int has_max_bus_speed_file (struct hotplug_slot *slot) +{ + if ((!slot) || (!slot->ops)) + return -ENODEV; + if (slot->ops->get_max_bus_speed) + return 0; + return -ENOENT; +} + +static int has_cur_bus_speed_file (struct hotplug_slot *slot) +{ + if ((!slot) || (!slot->ops)) + return -ENODEV; + if (slot->ops->get_cur_bus_speed) + return 0; + return -ENOENT; +} + +static int has_test_file (struct hotplug_slot *slot) +{ + if ((!slot) || (!slot->ops)) + return -ENODEV; + if (slot->ops->hardware_test) + return 0; + return -ENOENT; +} + +static int fs_add_slot (struct hotplug_slot *slot) +{ + if (has_power_file(slot) == 0) + sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr); + + if (has_attention_file(slot) == 0) + sysfs_create_file(&slot->kobj, &hotplug_slot_attr_attention.attr); + + if (has_latch_file(slot) == 0) + sysfs_create_file(&slot->kobj, &hotplug_slot_attr_latch.attr); + + if (has_adapter_file(slot) == 0) + sysfs_create_file(&slot->kobj, &hotplug_slot_attr_presence.attr); + + if (has_max_bus_speed_file(slot) == 0) + sysfs_create_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); + + if (has_cur_bus_speed_file(slot) == 0) + sysfs_create_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); + + if (has_test_file(slot) == 0) + sysfs_create_file(&slot->kobj, &hotplug_slot_attr_test.attr); + + return 0; +} + +static void fs_remove_slot (struct hotplug_slot *slot) +{ + if (has_power_file(slot) == 0) + sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); + + if (has_attention_file(slot) == 0) + sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr); + + if (has_latch_file(slot) == 0) + sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); + + if (has_adapter_file(slot) == 0) + sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); + + if (has_max_bus_speed_file(slot) == 0) + sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); + + if (has_cur_bus_speed_file(slot) == 0) + sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); + + if (has_test_file(slot) == 0) + sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr); +} + +static struct hotplug_slot *get_slot_from_name (const char *name) +{ + struct hotplug_slot *slot; + struct list_head *tmp; + + list_for_each (tmp, &pci_hotplug_slot_list) { + slot = list_entry (tmp, struct hotplug_slot, slot_list); + if (strcmp(slot->name, name) == 0) + return slot; + } + return NULL; +} + +/** + * pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem + * @slot: pointer to the &struct hotplug_slot to register + * + * Registers a hotplug slot with the pci hotplug subsystem, which will allow + * userspace interaction to the slot. + * + * Returns 0 if successful, anything else for an error. + */ +int pci_hp_register (struct hotplug_slot *slot) +{ + int result; + + if (slot == NULL) + return -ENODEV; + if ((slot->info == NULL) || (slot->ops == NULL)) + return -EINVAL; + + strlcpy(slot->kobj.name, slot->name, KOBJ_NAME_LEN); + kobj_set_kset_s(slot, hotplug_slots_subsys); + + /* this can fail if we have already registered a slot with the same name */ + if (kobject_register(&slot->kobj)) { + err("Unable to register kobject"); + return -EINVAL; + } + + list_add (&slot->slot_list, &pci_hotplug_slot_list); + + result = fs_add_slot (slot); + dbg ("Added slot %s to the list\n", slot->name); + return result; +} + +/** + * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem + * @slot: pointer to the &struct hotplug_slot to deregister + * + * The @slot must have been registered with the pci hotplug subsystem + * previously with a call to pci_hp_register(). + * + * Returns 0 if successful, anything else for an error. + */ +int pci_hp_deregister (struct hotplug_slot *slot) +{ + struct hotplug_slot *temp; + + if (slot == NULL) + return -ENODEV; + + temp = get_slot_from_name (slot->name); + if (temp != slot) { + return -ENODEV; + } + list_del (&slot->slot_list); + + fs_remove_slot (slot); + dbg ("Removed slot %s from the list\n", slot->name); + kobject_unregister(&slot->kobj); + return 0; +} + +/** + * pci_hp_change_slot_info - changes the slot's information structure in the core + * @slot: pointer to the slot whose info has changed + * @info: pointer to the info copy into the slot's info structure + * + * @slot must have been registered with the pci + * hotplug subsystem previously with a call to pci_hp_register(). + * + * Returns 0 if successful, anything else for an error. + */ +int pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info *info) +{ + if ((slot == NULL) || (info == NULL)) + return -ENODEV; + + /* + * check all fields in the info structure, and update timestamps + * for the files referring to the fields that have now changed. + */ + if ((has_power_file(slot) == 0) && + (slot->info->power_status != info->power_status)) + sysfs_update_file(&slot->kobj, &hotplug_slot_attr_power.attr); + + if ((has_attention_file(slot) == 0) && + (slot->info->attention_status != info->attention_status)) + sysfs_update_file(&slot->kobj, &hotplug_slot_attr_attention.attr); + + if ((has_latch_file(slot) == 0) && + (slot->info->latch_status != info->latch_status)) + sysfs_update_file(&slot->kobj, &hotplug_slot_attr_latch.attr); + + if ((has_adapter_file(slot) == 0) && + (slot->info->adapter_status != info->adapter_status)) + sysfs_update_file(&slot->kobj, &hotplug_slot_attr_presence.attr); + + if ((has_max_bus_speed_file(slot) == 0) && + (slot->info->max_bus_speed != info->max_bus_speed)) + sysfs_update_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); + + if ((has_cur_bus_speed_file(slot) == 0) && + (slot->info->cur_bus_speed != info->cur_bus_speed)) + sysfs_update_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); + + memcpy (slot->info, info, sizeof (struct hotplug_slot_info)); + + return 0; +} + +static int __init pci_hotplug_init (void) +{ + int result; + + kset_set_kset_s(&hotplug_slots_subsys, pci_bus_type.subsys); + result = subsystem_register(&hotplug_slots_subsys); + if (result) { + err("Register subsys with error %d\n", result); + goto exit; + } + result = cpci_hotplug_init(debug); + if (result) { + err ("cpci_hotplug_init with error %d\n", result); + goto err_subsys; + } + + info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); + goto exit; + +err_subsys: + subsystem_unregister(&hotplug_slots_subsys); +exit: + return result; +} + +static void __exit pci_hotplug_exit (void) +{ + cpci_hotplug_exit(); + subsystem_unregister(&hotplug_slots_subsys); +} + +module_init(pci_hotplug_init); +module_exit(pci_hotplug_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_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 -Nru a/drivers/pci/hotplug/pcihp_skeleton.c b/drivers/pci/hotplug/pcihp_skeleton.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug/pcihp_skeleton.c Wed Jun 4 18:11:56 2003 @@ -0,0 +1,432 @@ +/* + * PCI Hot Plug Controller Skeleton Driver - 0.1 + * + * 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. + * + * This driver is to be used as a skeleton driver to be show how to interface + * with the pci hotplug core easily. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include +#include "pci_hotplug.h" + + +#define SLOT_MAGIC 0x67267322 +struct slot { + u32 magic; + u8 number; + struct hotplug_slot *hotplug_slot; + struct list_head slot_list; +}; + +static LIST_HEAD(slot_list); + +#if !defined(CONFIG_HOTPLUG_PCI_SKELETON_MODULE) + #define MY_NAME "pcihp_skeleton" +#else + #define MY_NAME THIS_MODULE->name +#endif + +#define dbg(format, arg...) \ + do { \ + if (debug) \ + printk (KERN_DEBUG "%s: " format "\n", \ + MY_NAME , ## arg); \ + } while (0) +#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) + + + +/* local variables */ +static int debug; +static int num_slots; + +#define DRIVER_VERSION "0.1" +#define DRIVER_AUTHOR "Greg Kroah-Hartman " +#define DRIVER_DESC "Hot Plug PCI Controller Skeleton Driver" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); + +static int enable_slot (struct hotplug_slot *slot); +static int disable_slot (struct hotplug_slot *slot); +static int set_attention_status (struct hotplug_slot *slot, u8 value); +static int hardware_test (struct hotplug_slot *slot, u32 value); +static int get_power_status (struct hotplug_slot *slot, u8 *value); +static int get_attention_status (struct hotplug_slot *slot, u8 *value); +static int get_latch_status (struct hotplug_slot *slot, u8 *value); +static int get_adapter_status (struct hotplug_slot *slot, u8 *value); + +static struct hotplug_slot_ops skel_hotplug_slot_ops = { + .owner = THIS_MODULE, + .enable_slot = enable_slot, + .disable_slot = disable_slot, + .set_attention_status = set_attention_status, + .hardware_test = hardware_test, + .get_power_status = get_power_status, + .get_attention_status = get_attention_status, + .get_latch_status = get_latch_status, + .get_adapter_status = get_adapter_status, +}; + + +/* Inline functions to check the sanity of a pointer that is passed to us */ +static inline int slot_paranoia_check (struct slot *slot, const char *function) +{ + if (!slot) { + dbg("%s - slot == NULL", function); + return -1; + } + if (slot->magic != SLOT_MAGIC) { + dbg("%s - bad magic number for slot", function); + return -1; + } + if (!slot->hotplug_slot) { + dbg("%s - slot->hotplug_slot == NULL!", function); + return -1; + } + return 0; +} + +static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function) +{ + struct slot *slot; + + if (!hotplug_slot) { + dbg("%s - hotplug_slot == NULL\n", function); + return NULL; + } + + slot = (struct slot *)hotplug_slot->private; + if (slot_paranoia_check (slot, function)) + return NULL; + return slot; +} + + +static int enable_slot (struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + /* + * Fill in code here to enable the specified slot + */ + + return retval; +} + + +static int disable_slot (struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + /* + * Fill in code here to disable the specified slot + */ + + return retval; +} + +static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + switch (status) { + case 0: + /* + * Fill in code here to turn light off + */ + break; + + case 1: + default: + /* + * Fill in code here to turn light on + */ + break; + } + + return retval; +} + +static int hardware_test (struct hotplug_slot *hotplug_slot, u32 value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + err ("No hardware tests are defined for this driver"); + retval = -ENODEV; + + /* Or you can specify a test if you want to */ + + return retval; +} + +static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name); + + /* + * Fill in logic to get the current power status of the specific + * slot and store it in the *value location. + */ + + return retval; +} + +static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name); + + /* + * Fill in logic to get the current attention status of the specific + * slot and store it in the *value location. + */ + + return retval; +} + +static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name); + + /* + * Fill in logic to get the current latch status of the specific + * slot and store it in the *value location. + */ + + return retval; +} + +static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg(__FUNCTION__" - physical_slot = %s\n", hotplug_slot->name); + + /* + * Fill in logic to get the current adapter status of the specific + * slot and store it in the *value location. + */ + + return retval; +} + +#define SLOT_NAME_SIZE 10 +static void make_slot_name (struct slot *slot) +{ + /* + * Stupid way to make a filename out of the slot name. + * replace this if your hardware provides a better way to name slots. + */ + snprintf (slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d", slot->number); +} + +static int init_slots (void) +{ + struct slot *slot; + struct hotplug_slot *hotplug_slot; + struct hotplug_slot_info *info; + char *name; + int retval = 0; + int i; + + /* + * Create a structure for each slot, and register that slot + * with the pci_hotplug subsystem. + */ + for (i = 0; i < num_slots; ++i) { + slot = kmalloc (sizeof (struct slot), GFP_KERNEL); + if (!slot) + return -ENOMEM; + memset(slot, 0, sizeof(struct slot)); + + hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL); + if (!hotplug_slot) { + kfree (slot); + return -ENOMEM; + } + memset(hotplug_slot, 0, sizeof (struct hotplug_slot)); + slot->hotplug_slot = hotplug_slot; + + info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); + if (!info) { + kfree (hotplug_slot); + kfree (slot); + return -ENOMEM; + } + memset(info, 0, sizeof (struct hotplug_slot_info)); + hotplug_slot->info = info; + + name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL); + if (!name) { + kfree (info); + kfree (hotplug_slot); + kfree (slot); + return -ENOMEM; + } + hotplug_slot->name = name; + + slot->magic = SLOT_MAGIC; + slot->number = i; + + hotplug_slot->private = slot; + make_slot_name (slot); + hotplug_slot->ops = &skel_hotplug_slot_ops; + + /* + * Initilize the slot info structure with some known + * good values. + */ + info->power_status = get_skel_power_status(slot); + info->attention_status = get_skel_attention_status(slot); + info->latch_status = get_skel_latch_status(slot); + info->adapter_status = get_skel_adapter_status(slot); + + dbg ("registering slot %d\n", i); + retval = pci_hp_register (slot->hotplug_slot); + if (retval) { + err ("pci_hp_register failed with error %d\n", retval); + kfree (info); + kfree (name); + kfree (hotplug_slot); + kfree (slot); + return retval; + } + + /* add slot to our internal list */ + list_add (&slot->slot_list, &slot_list); + } + + return retval; +} + +static void cleanup_slots (void) +{ + struct list_head *tmp; + struct slot *slot; + + /* + * Unregister all of our slots with the pci_hotplug subsystem, + * and free up all memory that we had allocated. + */ + list_for_each (tmp, &slot_list) { + slot = list_entry (tmp, struct slot, slot_list); + list_del (&slot->slot_list); + pci_hp_deregister (slot->hotplug_slot); + kfree (slot->hotplug_slot->info); + kfree (slot->hotplug_slot->name); + kfree (slot->hotplug_slot); + kfree (slot); + } + + return; +} + +static int __init pcihp_skel_init(void) +{ + int retval; + + /* + * Do specific initialization stuff for your driver here + * Like initilizing your controller hardware (if any) and + * determining the number of slots you have in the system + * right now. + */ + num_slots = 5; + + retval = init_slots(); + if (retval) + return retval; + + info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); + return 0; +} + +static void __exit pcihp_skel_exit(void) +{ + /* + * Clean everything up. + */ + cleanup_slots(); +} + +module_init(pcihp_skel_init); +module_exit(pcihp_skel_exit); +