From: Greg KH To: Alan Cox , t-kouchi@mvf.biglobe.ne.jp Cc: linux-kernel@vger.kernel.org, pcihpd-discuss@lists.sourceforge.net Subject: [PATCH] ACPI PCI Hotplug driver update for 2.4.19-rc2-ac2 Hi, Here's a patch against 2.4.19-rc2-ac2 that updates the ACPI PCI Hotplug driver to the latest version. This patch was written by Takayoshi KOCHI and has been tested on some IBM i386, NEC i386, and some unnammed ia64 machines. thanks, greg k-h diff -Naur -X /home/greg/linux/dontdiff linux-2.4.19-rc2-ac2/Documentation/Configure.help linux-2.4.19-rc2-ac2-greg/Documentation/Configure.help --- linux-2.4.19-rc2-ac2/Documentation/Configure.help Fri Jul 19 11:36:16 2002 +++ linux-2.4.19-rc2-ac2-greg/Documentation/Configure.help Fri Jul 19 12:07:43 2002 @@ -3527,6 +3527,16 @@ When in doubt, say N. +ACPI PCI Hotplug driver +CONFIG_HOTPLUG_PCI_ACPI + 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.o. If you want to compile it + as a module, say M here and read . + MCA support CONFIG_MCA MicroChannel Architecture is found in some IBM PS/2 machines and diff -Naur -X /home/greg/linux/dontdiff linux-2.4.19-rc2-ac2/drivers/hotplug/Makefile linux-2.4.19-rc2-ac2-greg/drivers/hotplug/Makefile --- linux-2.4.19-rc2-ac2/drivers/hotplug/Makefile Fri Jul 19 11:34:52 2002 +++ linux-2.4.19-rc2-ac2-greg/drivers/hotplug/Makefile Fri Jul 19 12:07:43 2002 @@ -4,14 +4,14 @@ O_TARGET := vmlinux-obj.o -list-multi := cpqphp.o pci_hotplug.o ibmphp.o pcihpacpi.o +list-multi := cpqphp.o pci_hotplug.o ibmphp.o acpiphp.o export-objs := pci_hotplug_core.o pci_hotplug_util.o obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o -obj-$(CONFIG_HOTPLUG_PCI_ACPI) += pcihpacpi.o +obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o pci_hotplug-objs := pci_hotplug_core.o \ pci_hotplug_util.o @@ -34,8 +34,10 @@ endif endif -pcihp_acpi_objs := pcihp_acpi.o \ - pcihp_acpi_glue.o +acpiphp_objs := acpiphp_core.o \ + acpiphp_glue.o \ + acpiphp_pci.o \ + acpiphp_res.o ifeq ($(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM),y) cpqphp-objs += cpqphp_nvram.o @@ -53,5 +55,5 @@ ibmphp.o: $(ibmphp-objs) $(LD) -r -o $@ $(ibmphp-objs) -pcihpacpi.o: $(pcihp_acpi_objs) - $(LD) -r -o $@ $(pcihp_acpi_objs) +acpiphp.o: $(acpiphp_objs) + $(LD) -r -o $@ $(acpiphp_objs) diff -Naur -X /home/greg/linux/dontdiff linux-2.4.19-rc2-ac2/drivers/hotplug/acpiphp.h linux-2.4.19-rc2-ac2-greg/drivers/hotplug/acpiphp.h --- linux-2.4.19-rc2-ac2/drivers/hotplug/acpiphp.h Wed Dec 31 16:00:00 1969 +++ linux-2.4.19-rc2-ac2-greg/drivers/hotplug/acpiphp.h Fri Jul 19 12:07:43 2002 @@ -0,0 +1,322 @@ +/* + * 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/acpi.h" +#include "pci_hotplug.h" + +#if ACPI_CA_VERSION < 0x20020201 +/* until we get a new version of the ACPI driver for both ia32 and ia64 ... */ +#define acpi_util_eval_error(h,p,s) + +static acpi_status +acpi_evaluate_integer ( + acpi_handle handle, + acpi_string pathname, + acpi_object_list *arguments, + unsigned long *data) +{ + acpi_status status = AE_OK; + acpi_object element; + acpi_buffer buffer = {sizeof(acpi_object), &element}; + + if (!data) + return AE_BAD_PARAMETER; + + status = acpi_evaluate_object(handle, pathname, arguments, &buffer); + if (ACPI_FAILURE(status)) { + acpi_util_eval_error(handle, pathname, status); + return status; + } + + if (element.type != ACPI_TYPE_INTEGER) { + acpi_util_eval_error(handle, pathname, AE_BAD_DATA); + return AE_BAD_DATA; + } + + *data = element.integer.value; + + return AE_OK; +} +#else /* ACPI_CA_VERSION < 0x20020201 */ +#include "acpi_bus.h" +#endif + +/* compatibility stuff for ACPI CA */ +#ifndef ACPI_MEMORY_RANGE +#define ACPI_MEMORY_RANGE MEMORY_RANGE +#endif + +#ifndef ACPI_IO_RANGE +#define ACPI_IO_RANGE IO_RANGE +#endif + +#ifndef ACPI_BUS_NUMBER_RANGE +#define ACPI_BUS_NUMBER_RANGE BUS_NUMBER_RANGE +#endif + +#ifndef ACPI_PREFETCHABLE_MEMORY +#define ACPI_PREFETCHABLE_MEMORY PREFETCHABLE_MEMORY +#endif + +#ifndef ACPI_PRODUCER +#define ACPI_PRODUCER PRODUCER +#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) + +#define SLOT_MAGIC 0x67267322 +/* name size which is used for entries in pcihpfs */ +#define SLOT_NAME_SIZE 16 /* ACPIxxxx */ + +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; + + /* if there are multiple corresponding ACPI slot objects, + this points to one of them */ + 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; + + struct pci_ops *pci_ops; + + /* 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 acpiphp_func *func; /* functions */ + 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 slot 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 */ + +#endif /* _ACPIPHP_H */ diff -Naur -X /home/greg/linux/dontdiff linux-2.4.19-rc2-ac2/drivers/hotplug/acpiphp_core.c linux-2.4.19-rc2-ac2-greg/drivers/hotplug/acpiphp_core.c --- linux-2.4.19-rc2-ac2/drivers/hotplug/acpiphp_core.c Wed Dec 31 16:00:00 1969 +++ linux-2.4.19-rc2-ac2-greg/drivers/hotplug/acpiphp_core.c Fri Jul 19 12:07:43 2002 @@ -0,0 +1,470 @@ +/* + * 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 + +/* local variables */ +static int debug = 1; /* XXX set to 0 after debug */ +static int num_slots; + +#define DRIVER_VERSION "0.3" +#define DRIVER_AUTHOR "Greg Kroah-Hartman " +#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 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, +}; + + +/* 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; +} + + +/** + * 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", __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", __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", __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", __FUNCTION__, hotplug_slot->name); + + err ("No hardware tests are defined for this driver"); + 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", __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", __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", __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", __FUNCTION__, hotplug_slot->name); + + *value = acpiphp_get_adapter_status (slot->acpi_slot); + + return retval; +} + + +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; +} + + +static void exit_acpi (void) +{ + /* deallocate internal data structures etc. */ + acpiphp_glue_exit(); +} + + +/** + * 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%x", slot->acpi_slot->sun); +} + +/** + * 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", 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); + } + + return retval; +} + + +static void cleanup_slots (void) +{ + struct list_head *tmp; + struct slot *slot; + + 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 acpiphp_init(void) +{ + int retval; + + /* read all the ACPI info from the system */ + retval = init_acpi(); + if (retval) + return retval; + + retval = init_slots(); + if (retval) + return retval; + + info (DRIVER_DESC " version: " DRIVER_VERSION); + return 0; +} + + +static void __exit acpiphp_exit(void) +{ + cleanup_slots(); + exit_acpi(); +} + +module_init(acpiphp_init); +module_exit(acpiphp_exit); diff -Naur -X /home/greg/linux/dontdiff linux-2.4.19-rc2-ac2/drivers/hotplug/acpiphp_glue.c linux-2.4.19-rc2-ac2-greg/drivers/hotplug/acpiphp_glue.c --- linux-2.4.19-rc2-ac2/drivers/hotplug/acpiphp_glue.c Wed Dec 31 16:00:00 1969 +++ linux-2.4.19-rc2-ac2-greg/drivers/hotplug/acpiphp_glue.c Fri Jul 19 12:07:43 2002 @@ -0,0 +1,1513 @@ +/* + * 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); + +static int debug = 1; /* XXX set 0 after debug */ +#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 *); +static struct pci_ops *default_ops; + +/* + * 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) +{ + acpi_status status; + acpi_handle tmp; + 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!"); + 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", + 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"); + 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 void +decode_acpi_resource (acpi_resource *resource, struct acpiphp_bridge *bridge) +{ + acpi_resource_address16 *address16_data; + acpi_resource_address32 *address32_data; + acpi_resource_address64 *address64_data; + struct pci_resource *res; + + u32 resource_type, producer_consumer, address_length; + u64 min_address_range, max_address_range; + u16 cache_attribute = 0; + + int done = 0, found; + + /* shut up gcc */ + resource_type = producer_consumer = address_length = 0; + min_address_range = max_address_range = 0; + + while (!done) { + found = 0; + + switch (resource->id) { + case ACPI_RSTYPE_ADDRESS16: + address16_data = (acpi_resource_address16 *)&resource->data; + resource_type = address16_data->resource_type; + producer_consumer = address16_data->producer_consumer; + min_address_range = address16_data->min_address_range; + max_address_range = address16_data->max_address_range; + address_length = address16_data->address_length; + if (resource_type == ACPI_MEMORY_RANGE) + cache_attribute = address16_data->attribute.memory.cache_attribute; + found = 1; + break; + + case ACPI_RSTYPE_ADDRESS32: + address32_data = (acpi_resource_address32 *)&resource->data; + resource_type = address32_data->resource_type; + producer_consumer = address32_data->producer_consumer; + min_address_range = address32_data->min_address_range; + max_address_range = address32_data->max_address_range; + address_length = address32_data->address_length; + if (resource_type == ACPI_MEMORY_RANGE) + cache_attribute = address32_data->attribute.memory.cache_attribute; + found = 1; + break; + + case ACPI_RSTYPE_ADDRESS64: + address64_data = (acpi_resource_address64 *)&resource->data; + resource_type = address64_data->resource_type; + producer_consumer = address64_data->producer_consumer; + min_address_range = address64_data->min_address_range; + max_address_range = address64_data->max_address_range; + address_length = address64_data->address_length; + if (resource_type == ACPI_MEMORY_RANGE) + cache_attribute = address64_data->attribute.memory.cache_attribute; + found = 1; + break; + + case ACPI_RSTYPE_END_TAG: + done = 1; + break; + + default: + /* ignore */ + break; + } + + resource = (acpi_resource *)((char*)resource + resource->length); + + if (found && producer_consumer == ACPI_PRODUCER && address_length > 0) { + switch (resource_type) { + case ACPI_MEMORY_RANGE: + if (cache_attribute == ACPI_PREFETCHABLE_MEMORY) { + dbg("resource type: prefetchable memory 0x%x - 0x%x", (u32)min_address_range, (u32)max_address_range); + res = acpiphp_make_resource(min_address_range, + address_length); + if (!res) { + err("out of memory"); + return; + } + res->next = bridge->p_mem_head; + bridge->p_mem_head = res; + } else { + dbg("resource type: memory 0x%x - 0x%x", (u32)min_address_range, (u32)max_address_range); + res = acpiphp_make_resource(min_address_range, + address_length); + if (!res) { + err("out of memory"); + return; + } + res->next = bridge->mem_head; + bridge->mem_head = res; + } + break; + case ACPI_IO_RANGE: + dbg("resource type: io 0x%x - 0x%x", (u32)min_address_range, (u32)max_address_range); + res = acpiphp_make_resource(min_address_range, + address_length); + if (!res) { + err("out of memory"); + return; + } + res->next = bridge->io_head; + bridge->io_head = res; + break; + case ACPI_BUS_NUMBER_RANGE: + dbg("resource type: bus number %d - %d", (u32)min_address_range, (u32)max_address_range); + res = acpiphp_make_resource(min_address_range, + address_length); + if (!res) { + err("out of memory"); + return; + } + res->next = bridge->bus_head; + bridge->bus_head = res; + break; + default: + /* invalid type */ + break; + } + } + } + + 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); +#if 1 + info("ACPI _CRS resource:"); + acpiphp_dump_resource(bridge); +#endif +} + + +/* find pci_bus structure associated to specific bus number */ +static struct pci_bus *find_pci_bus(const struct list_head *list, int bus) +{ + const struct list_head *l; + + list_for_each(l, list) { + struct pci_bus *b = pci_bus_b(l); + if (b->number == bus) + return b; + + if (!list_empty(&b->children)) + /* XXX recursive call */ + b = find_pci_bus(&b->children, bus); + + if (b) + return b; + } + + return 0; +} + + +/* decode ACPI 2.0 _HPP hot plug parameters */ +static void decode_hpp(struct acpiphp_bridge *bridge) +{ + acpi_status status; +#if ACPI_CA_VERSION < 0x20020201 + acpi_buffer buffer; +#else + acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; +#endif + 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; + +#if ACPI_CA_VERSION < 0x20020201 + buffer.length = 0; + buffer.pointer = NULL; + + status = acpi_evaluate_object(bridge->handle, "_HPP", NULL, &buffer); + + if (status == AE_BUFFER_OVERFLOW) { + buffer.pointer = kmalloc(buffer.length, GFP_KERNEL); + if (!buffer.pointer) + return; + status = acpi_evaluate_object(bridge->handle, "_HPP", NULL, &buffer); + } +#else + status = acpi_evaluate_object(bridge->handle, "_HPP", NULL, &buffer); +#endif + + if (ACPI_FAILURE(status)) { + dbg("_HPP evaluation failed"); + return; + } + + package = (acpi_object *) buffer.pointer; + + if (!package || package->type != ACPI_TYPE_PACKAGE || + package->package.count != 4 || !package->package.elements) { + err("invalid _HPP object; ignoring"); + goto err_exit; + } + + for (i = 0; i < 4; i++) { + if (package->package.elements[i].type != ACPI_TYPE_INTEGER) { + err("invalid _HPP parameter type; ignoring"); + 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)", + 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"); + } + + list_add(&bridge->list, &bridge_list); + +#if 1 + dbg("Bridge resource:"); + acpiphp_dump_resource(bridge); +#endif +} + + +/* allocate and initialize host bridge data structure */ +static void add_host_bridge (acpi_handle *handle, int seg, int bus) +{ + acpi_status status; +#if ACPI_CA_VERSION < 0x20020201 + acpi_buffer buffer; +#else + acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; +#endif + 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 = find_pci_bus(&pci_root_buses, bus); + bridge->pci_ops = bridge->pci_bus->ops; + + bridge->res_lock = SPIN_LOCK_UNLOCKED; + + /* to be overridden when we decode _CRS */ + bridge->sub = bridge->bus; + + /* decode resources */ + +#if ACPI_CA_VERSION < 0x20020201 + buffer.length = 0; + buffer.pointer = NULL; + + status = acpi_get_current_resources(handle, &buffer); + + if (status == AE_BUFFER_OVERFLOW) { + buffer.pointer = kmalloc(buffer.length, GFP_KERNEL); + if (!buffer.pointer) + return; + status = acpi_get_current_resources(handle, &buffer); + } +#else + status = acpi_get_current_resources(handle, &buffer); +#endif + + if (ACPI_FAILURE(status)) { + err("failed to decode bridge resources"); + kfree(bridge); + return; + } + + decode_acpi_resource(buffer.pointer, bridge); + kfree(buffer.pointer); + + 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) +{ + acpi_status status; + struct acpiphp_bridge *bridge; + u8 tmp8; + u16 tmp16; + u32 tmp; + u64 base64, limit64; + u32 base, limit, base32u, limit32u; + + bridge = kmalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); + if (bridge == NULL) { + err("out of memory"); + 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"); + kfree(bridge); + return; + } + + bridge->pci_bus = bridge->pci_dev->subordinate; + if (!bridge->pci_bus) { + err("This is not a PCI-to-PCI bridge!"); + kfree(bridge); + return; + } + bridge->pci_ops = bridge->pci_bus->ops; + + 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"); + return; + } + dbg("16bit I/O range: %04x-%04x", + (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"); + return; + } + dbg("32bit I/O range: %08x-%08x", + (u32)bridge->io_head->base, + (u32)(bridge->io_head->base + bridge->io_head->length - 1)); + break; + case 0x0f: + dbg("I/O space unsupported"); + break; + default: + warn("Unknown I/O range type"); + } + + /* 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"); + return; + } + dbg("32bit Memory range: %08x-%08x", + (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"); + return; + } + dbg("32bit Prefetchable memory range: %08x-%08x", + (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"); + return; + } + dbg("64bit Prefetchable memory range: %08x%08x-%08x%08x", + (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"); + } + + 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; + u32 val; + + /* 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", __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 %02x:%02x.%d", bus, device, function); + add_p2p_bridge(handle, seg, bus, device, function); + } + + return AE_OK; +} + + +/* find hot-pluggable slots, and then find P2P bridge */ +static int add_bridges (acpi_handle *handle) +{ + acpi_status status; + unsigned long tmp; + int seg, bus; + acpi_handle dummy_handle; + int sta = -1; + + /* 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", __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"); + 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"); + 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)",status); + + return 0; +} + + +/* callback routine to enumerate all the bridges in ACPI namespace */ +static acpi_status +find_host_bridge (acpi_handle handle, u32 lvl, void *context, void **rv) +{ + acpi_status status; + acpi_device_info info; + char objname[5]; + acpi_buffer buffer = { sizeof(objname), objname }; + + status = acpi_get_object_info(handle, &info); + if (ACPI_FAILURE(status)) { + dbg("%s: failed to get bridge information", __FUNCTION__); + return AE_OK; /* continue */ + } + + info.hardware_id[sizeof(info.hardware_id)-1] = '\0'; + + /* TBD check _CID also */ + if (strcmp(info.hardware_id, ACPI_PCI_HOST_HID) == 0) { + + acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + dbg("checking PCI-hotplug capable bridges under [%s]", objname); + add_bridges(handle); + } + return AE_OK; +} + + +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 %02x:%02x.%d", __FUNCTION__, + slot->bridge->bus, slot->device, func->function); + status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL); + if (ACPI_FAILURE(status)) { + warn("%s: _PS0 failed", __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; + acpi_object_list arg_list; + 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 %02x:%02x.%d", __FUNCTION__, + slot->bridge->bus, slot->device, func->function); + status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL); + if (ACPI_FAILURE(status)) { + warn("%s: _PS3 failed", __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 %02x:%02x.%d", __FUNCTION__, + slot->bridge->bus, slot->device, func->function); + + /* _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", __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) +{ + acpi_status status; + u8 bus; + struct pci_dev dev0, *dev; + struct pci_bus *child; + struct list_head *l; + struct acpiphp_func *func; + int retval = 0; + + 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."); + retval = -1; + goto err_exit; + } + + /* allocate resources to device */ + retval = acpiphp_configure_slot(slot); + if (retval) + goto err_exit; + + memset(&dev0, 0, sizeof (struct pci_dev)); + + dev0.bus = slot->bridge->pci_bus; + dev0.devfn = PCI_DEVFN(slot->device, 0); + dev0.sysdata = dev0.bus->sysdata; + + /* returned `dev' is the *first function* only! */ + dev = pci_scan_slot (&dev0); + + if (!dev) { + err("No new device found"); + 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; + +#if 1 + dbg("Available resources:"); + acpiphp_dump_resource(slot->bridge); +#endif + + 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; + acpi_status status; + + /* 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"); + 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; + int fn; + 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_read_config_dword_nodev(default_ops, + slot->bridge->bus, + 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]; + acpi_buffer buffer = { sizeof(objname), 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", __FUNCTION__, objname); + acpiphp_check_bridge(bridge); + break; + + case ACPI_NOTIFY_DEVICE_CHECK: + /* device check */ + dbg("%s: Device check notify on %s", __FUNCTION__, objname); + acpiphp_check_bridge(bridge); + break; + + case ACPI_NOTIFY_DEVICE_WAKE: + /* wake event */ + dbg("%s: Device wake notify on %s", __FUNCTION__, objname); + break; + + case ACPI_NOTIFY_EJECT_REQUEST: + /* request device eject */ + dbg("%s: Device eject notify on %s", __FUNCTION__, objname); + break; + + default: + warn("notify_handler: unknown event type 0x%x for %s", 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]; + acpi_buffer buffer = { sizeof(objname), 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", __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", __FUNCTION__, objname); + acpiphp_check_bridge(func->slot->bridge); + break; + + case ACPI_NOTIFY_DEVICE_WAKE: + /* wake event */ + dbg("%s: Device wake notify on %s", __FUNCTION__, objname); + break; + + case ACPI_NOTIFY_EJECT_REQUEST: + /* request device eject */ + dbg("%s: Device eject notify on %s", __FUNCTION__, objname); + acpiphp_disable_slot(func->slot); + break; + + default: + warn("notify_handler: unknown event type 0x%x for %s", type, objname); + break; + } +} + + +/** + * 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; + + /* set default pci_ops for configuration space operation */ + default_ops = pci_bus_b(pci_root_buses.next)->ops; + if (!default_ops) + return -1; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, find_host_bridge, + NULL, NULL); + + if (ACPI_FAILURE(status)) { + err("%s: acpi_walk_namespace() failed", __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 *node, *shead, *l, *n; + struct acpiphp_bridge *bridge; + struct acpiphp_slot *slot, *next; + struct acpiphp_func *func; + acpi_status status; + + list_for_each(node, &bridge_list) { + bridge = (struct acpiphp_bridge *)node; + slot = bridge->slots; + while (slot) { + next = slot->next; + list_for_each_safe(l, n, &slot->funcs) { + func = list_entry(l, 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"); + 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"); + + 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)", bridge->bus, bridge->nr_slots); + num_slots += bridge->nr_slots; + } + + dbg("Total %dslots", 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",__FUNCTION__, id); + return 0; +} + + +/** + * acpiphp_enable_slot - power on slot + */ +int acpiphp_enable_slot (struct acpiphp_slot *slot) +{ + int retval; + int online = 0; + + 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) +{ + struct list_head *l; + struct acpiphp_func *func; + 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; + +#if 1 + 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:"); + acpiphp_dump_resource(slot->bridge); +#endif + + 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 occured in enabling"); + 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 occured in enabling"); + up(&slot->crit_sect); + goto err_exit; + } + disabled++; + } + } + } + + dbg("%s: %d enabled, %d disabled", __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 -Naur -X /home/greg/linux/dontdiff linux-2.4.19-rc2-ac2/drivers/hotplug/acpiphp_pci.c linux-2.4.19-rc2-ac2-greg/drivers/hotplug/acpiphp_pci.c --- linux-2.4.19-rc2-ac2/drivers/hotplug/acpiphp_pci.c Wed Dec 31 16:00:00 1969 +++ linux-2.4.19-rc2-ac2-greg/drivers/hotplug/acpiphp_pci.c Fri Jul 19 12:07:43 2002 @@ -0,0 +1,763 @@ +/* + * 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 "pci_hotplug.h" +#include "acpiphp.h" + +static int debug = 1; /* XXX set 0 after debug */ +#define MY_NAME "acpiphp_pci" + +static void acpiphp_configure_irq (struct pci_dev *dev); + + +/* allocate mem/pmem/io resource to a new function */ +static int alloc_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 acpiphp_bridge *bridge; + struct pci_resource *res; + struct pci_ops *ops; + int bus, device, function; + + bridge = func->slot->bridge; + bus = bridge->bus; + device = func->slot->device; + function = func->function; + ops = bridge->pci_ops; + + for (count = 0; address[count]; count++) { /* for 6 BARs */ + pci_write_config_dword_nodev (ops, bus, device, function, address[count], 0xFFFFFFFF); + pci_read_config_dword_nodev(ops, bus, device, function, address[count], &bar); + + if (!bar) /* This BAR is not implemented */ + continue; + + dbg("Device %02x.%02x BAR %d wants %x", 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", 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_write_config_dword_nodev(ops, bus, device, function, 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", 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_write_config_dword_nodev(ops, bus, device, function, 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", count); + count += 1; + pci_write_config_dword_nodev(ops, bus, device, function, 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", 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_write_config_dword_nodev(ops, bus, device, function, 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", count); + count += 1; + pci_write_config_dword_nodev(ops, bus, device, function, address[count], (u32)(res->base >> 32)); + } + + res->next = func->mem_head; + func->mem_head = res; + + } + } + } + + /* disable expansion rom */ + pci_write_config_dword_nodev(ops, bus, device, function, PCI_ROM_ADDRESS, 0x00000000); + + return 0; +} + + +/* enable pci_dev */ +static int configure_pci_dev (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) +{ + u16 tmp; + struct acpiphp_func *func; + struct acpiphp_bridge *bridge; + struct pci_dev *dev; + + func = (struct acpiphp_func *)wrapped_dev->data; + bridge = (struct acpiphp_bridge *)wrapped_bus->data; + dev = wrapped_dev->dev; + + /* TBD: support PCI-to-PCI bridge case */ + if (!func || !bridge) + return 0; + + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, bridge->hpp.cache_line_size); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, bridge->hpp.latency_timer); + + pci_read_config_word(dev, PCI_COMMAND, &tmp); + if (bridge->hpp.enable_SERR) + tmp |= PCI_COMMAND_SERR; + if (bridge->hpp.enable_PERR) + tmp |= PCI_COMMAND_PARITY; + //tmp |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + pci_write_config_word(dev, PCI_COMMAND, tmp); + + acpiphp_configure_irq(dev); +#ifdef CONFIG_PROC_FS + pci_proc_attach_device(dev); +#endif + pci_announce_device_to_drivers(dev); + + return 0; +} + + +static int is_pci_dev_in_use (struct pci_dev* dev) +{ + /* + * dev->driver will be set if the device is in use by a new-style + * driver -- otherwise, check the device's regions to see if any + * driver has claimed them + */ + + int i, inuse=0; + + if (dev->driver) return 1; //assume driver feels responsible + + for (i = 0; !dev->driver && !inuse && (i < 6); i++) { + if (!pci_resource_start(dev, i)) + continue; + + if (pci_resource_flags(dev, i) & IORESOURCE_IO) + inuse = check_region(pci_resource_start(dev, i), + pci_resource_len(dev, i)); + else if (pci_resource_flags(dev, i) & IORESOURCE_MEM) + inuse = check_mem_region(pci_resource_start(dev, i), + pci_resource_len(dev, i)); + } + + return inuse; +} + + +static int pci_hp_remove_device (struct pci_dev *dev) +{ + if (is_pci_dev_in_use(dev)) { + err("***Cannot safely power down device -- " + "it appears to be in use***\n"); + return -EBUSY; + } + pci_remove_device(dev); + return 0; +} + + +/* remove device driver */ +static int unconfigure_pci_dev_driver (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) +{ + struct pci_dev *dev = wrapped_dev->dev; + + dbg("attempting removal of driver for device %s", dev->slot_name); + + /* Now, remove the Linux Driver Representation */ + if (dev->driver) { + if (dev->driver->remove) { + dev->driver->remove(dev); + dbg("driver was properly removed"); + } + dev->driver = NULL; + } + + return is_pci_dev_in_use(dev); +} + + +/* remove pci_dev itself from system */ +static int unconfigure_pci_dev (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) +{ + struct pci_dev *dev = wrapped_dev->dev; + + /* Now, remove the Linux Representation */ + if (dev) { + if (pci_hp_remove_device(dev) == 0) { + kfree(dev); /* Now, remove */ + } else { + return -1; /* problems while freeing, abort visitation */ + } + } + + return 0; +} + + +/* remove pci_bus itself from system */ +static int unconfigure_pci_bus (struct pci_bus_wrapped *wrapped_bus, struct pci_dev_wrapped *wrapped_dev) +{ + struct pci_bus *bus = wrapped_bus->bus; + +#ifdef CONFIG_PROC_FS + /* Now, remove the Linux Representation */ + if (bus->procdir) { + pci_proc_detach_bus(bus); + } +#endif + /* the cleanup code should live in the kernel ... */ + bus->self->subordinate = NULL; + /* unlink from parent bus */ + list_del(&bus->node); + + /* Now, remove */ + if (bus) + kfree(bus); + + return 0; +} + + +/* 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, pin; + 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", 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)", 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"); + count += 1; + } + dbg("BAR[%d] %08x - %08x (PMEM)", 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"); + count += 1; + } + dbg("BAR[%d] %08x - %08x (MEM)", 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) +{ + struct list_head *l; + struct pci_dev *dev; + + 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_ops *ops; + struct pci_dev *dev; + + dev = func->pci_dev; + dbg("Hot-pluggable device %s", 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)", 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"); + count += 1; + } + dbg("BAR[%d] %08x - %08x (PMEM)", 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"); + count += 1; + } + dbg("BAR[%d] %08x - %08x (MEM)", 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"); + 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_read_config_byte_nodev(slot->bridge->pci_ops, + slot->bridge->bus, 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_read_config_dword_nodev(slot->bridge->pci_ops, + slot->bridge->bus, + slot->device, + func->function, + PCI_VENDOR_ID, &dvid); + if (dvid != 0xffffffff) { + retval = alloc_resource(func); + if (retval) + break; + } + } + } + + return retval; +} + + +/* for pci_visit_dev() */ +static struct pci_visit configure_functions = { + post_visit_pci_dev: configure_pci_dev +}; + +static struct pci_visit unconfigure_functions_phase1 = { + post_visit_pci_dev: unconfigure_pci_dev_driver +}; + +static struct pci_visit unconfigure_functions_phase2 = { + post_visit_pci_bus: unconfigure_pci_bus, + post_visit_pci_dev: unconfigure_pci_dev +}; + + +/** + * 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) +{ + int retval = 0; + struct pci_dev_wrapped wrapped_dev; + struct pci_bus_wrapped wrapped_bus; + struct acpiphp_bridge *bridge; + + /* if pci_dev is NULL, ignore it */ + if (!func->pci_dev) + goto err_exit; + + bridge = func->slot->bridge; + + memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped)); + memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped)); + wrapped_dev.dev = func->pci_dev; + wrapped_dev.data = func; + wrapped_bus.bus = bridge->pci_bus; + wrapped_bus.data = bridge; + + retval = pci_visit_dev(&configure_functions, &wrapped_dev, &wrapped_bus); + if (retval) + goto err_exit; + + err_exit: + return retval; +} + + +/** + * acpiphp_unconfigure_function - unconfigure PCI function + * @func: function to be unconfigured + * + */ +int acpiphp_unconfigure_function (struct acpiphp_func *func) +{ + struct acpiphp_bridge *bridge; + struct pci_resource *tmp; + struct pci_dev_wrapped wrapped_dev; + struct pci_bus_wrapped wrapped_bus; + int retval = 0; + + /* if pci_dev is NULL, ignore it */ + if (!func->pci_dev) + goto err_exit; + + memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped)); + memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped)); + wrapped_dev.dev = func->pci_dev; + //wrapped_dev.data = func; + wrapped_bus.bus = func->slot->bridge->pci_bus; + //wrapped_bus.data = func->slot->bridge; + + retval = pci_visit_dev(&unconfigure_functions_phase1, &wrapped_dev, &wrapped_bus); + if (retval) + goto err_exit; + + retval = pci_visit_dev(&unconfigure_functions_phase2, &wrapped_dev, &wrapped_bus); + if (retval) + goto err_exit; + + /* 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; +} + + +/* XXX IA64 specific */ +#ifdef CONFIG_IA64 +static int ia64_get_irq(struct pci_dev *dev, int pin) +{ + extern int pci_pin_to_vector(int bus, int slot, int pci_pin); + int irq; + + irq = pci_pin_to_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin); + + if (irq < 0 && dev->bus->parent) { + /* go back to the bridge */ + struct pci_dev *bridge = dev->bus->self; + + if (bridge) { + /* allow for multiple bridges on an adapter */ + do { + /* do the bridge swizzle... */ + pin = (pin + PCI_SLOT(dev->devfn)) % 4; + irq = pci_pin_to_vector(bridge->bus->number, + PCI_SLOT(bridge->devfn), + pin); + } while (irq < 0 && (bridge = bridge->bus->self)); + } + if (irq >= 0) + printk(KERN_WARNING + "PCI: using PPB(B%d,I%d,P%d) to get vector %02x\n", + dev->bus->number, PCI_SLOT(dev->devfn), + pin, irq); + else + printk(KERN_WARNING + "PCI: Couldn't map irq for (B%d,I%d,P%d)\n", + dev->bus->number, PCI_SLOT(dev->devfn), pin); + } + + return irq; +} +#endif + + +/* + * acpiphp_configure_irq - configure PCI_INTERRUPT_PIN + * + * for x86 platforms, pcibios_enable_device calls pcibios_enable_irq, + * which allocates irq for pci_dev + * + * for IA64 platforms, we have to program dev->irq from pci IRQ routing + * information derived from ACPI table + * + * TBD: + * separate architecture dependent part + * (preferably, pci_enable_device() cares for allocating irq...) + */ +static void acpiphp_configure_irq (struct pci_dev *dev) +{ +#if CONFIG_IA64 /* XXX IA64 specific, need i386 version */ + int bus, device, function, irq; + u8 tmp; + + bus = dev->bus->number; + device = PCI_SLOT(dev->devfn); + function = PCI_FUNC(dev->devfn); + + pci_read_config_byte (dev, PCI_INTERRUPT_PIN, &tmp); + + if ((tmp > 0x00) && (tmp < 0x05)) { + irq = ia64_get_irq(dev, tmp - 1); + if (irq > 0) { + dev->irq = irq; + pci_write_config_byte (dev, PCI_INTERRUPT_LINE, irq); + } else { + err("Couldn't get IRQ for INT%c", 'A' + tmp - 1); + } + } +#endif +} diff -Naur -X /home/greg/linux/dontdiff linux-2.4.19-rc2-ac2/drivers/hotplug/acpiphp_res.c linux-2.4.19-rc2-ac2-greg/drivers/hotplug/acpiphp_res.c --- linux-2.4.19-rc2-ac2/drivers/hotplug/acpiphp_res.c Wed Dec 31 16:00:00 1969 +++ linux-2.4.19-rc2-ac2-greg/drivers/hotplug/acpiphp_res.c Fri Jul 19 12:07:43 2002 @@ -0,0 +1,708 @@ +/* + * 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" + +/* local variables */ +static int debug = 0; + +/* + * 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", + __FUNCTION__, size, node, (u32)node->base, node->length); + if (node->length < size) + continue; + + if (node->base & (size - 1)) { + dbg("%s: not aligned", __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", __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!!!", __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", + (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"); + /* 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", + (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"); + /* 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!!!"); + /* 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",(*head)->next); + + if (!(*head)->next) + return 0; /* only one item on the list, already sorted! */ + + dbg("*head->base = 0x%x",(u32)(*head)->base); + dbg("*head->next->base = 0x%x", (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.."); + 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) { + info("[%02d] %08x - %08x", + cnt++, (u32)p->base, (u32)p->base + p->length - 1); + p = p->next; + } +} + +void acpiphp_dump_resource(struct acpiphp_bridge *bridge) +{ + info("I/O resource:"); + dump_resource(bridge->io_head); + info("MEM resource:"); + dump_resource(bridge->mem_head); + info("PMEM resource:"); + dump_resource(bridge->p_mem_head); + info("BUS resource:"); + dump_resource(bridge->bus_head); +} + +void acpiphp_dump_func_resource(struct acpiphp_func *func) +{ + info("I/O resource:"); + dump_resource(func->io_head); + info("MEM resource:"); + dump_resource(func->mem_head); + info("PMEM resource:"); + dump_resource(func->p_mem_head); + info("BUS resource:"); + dump_resource(func->bus_head); +} + +/* +EXPORT_SYMBOL(acpiphp_get_io_resource); +EXPORT_SYMBOL(acpiphp_get_max_resource); +EXPORT_SYMBOL(acpiphp_get_resource); +EXPORT_SYMBOL(acpiphp_resource_sort_and_combine); +*/ diff -Naur -X /home/greg/linux/dontdiff linux-2.4.19-rc2-ac2/drivers/hotplug/pcihp_acpi.c linux-2.4.19-rc2-ac2-greg/drivers/hotplug/pcihp_acpi.c --- linux-2.4.19-rc2-ac2/drivers/hotplug/pcihp_acpi.c Fri Jul 19 11:34:52 2002 +++ linux-2.4.19-rc2-ac2-greg/drivers/hotplug/pcihp_acpi.c Wed Dec 31 16:00:00 1969 @@ -1,397 +0,0 @@ -/* - * ACPI PCI Hot Plug Controller Driver - * - * Copyright (c) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (c) 2001-2002 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 "pci_hotplug.h" -#include "pcihp_acpi.h" - -static LIST_HEAD(slot_list); - -#if !defined(CONFIG_HOTPLUG_PCI_ACPI_MODULE) - #define MY_NAME "pcihp_acpi" -#else - #define MY_NAME THIS_MODULE->name -#endif - -/* local variables */ -static int debug = 1; /* XXX */ -static int num_slots; - -#define DRIVER_VERSION "0.2" -#define DRIVER_AUTHOR "Greg Kroah-Hartman " -#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 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, -}; - - -/* 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 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); - - /* enable the specified slot */ - retval = pcihp_acpi_enable_slot (slot->acpi_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", __FUNCTION__, hotplug_slot->name); - - /* disable the specified slot */ - retval = pcihp_acpi_disable_slot (slot->acpi_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", __FUNCTION__, hotplug_slot->name); - - /* TBD - * ACPI doesn't have known method to manipulate - * attention status LED - */ - switch (status) { - case 0: - /* FIXME turn light off */ - slot->attention_status = 0; - break; - - case 1: - default: - /* FIXME turn light on */ - slot->attention_status = 1; - 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", __FUNCTION__, hotplug_slot->name); - - err ("No hardware tests are defined for this driver"); - retval = -ENODEV; - - 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("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name); - - *value = pcihp_acpi_get_power_status (slot->acpi_slot); - - 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("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name); - - *value = slot->attention_status; - - 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("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name); - - *value = pcihp_acpi_get_latch_status (slot->acpi_slot); - - 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("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name); - - *value = pcihp_acpi_get_adapter_status (slot->acpi_slot); - - return retval; -} - -static int init_acpi (void) -{ - int retval; - - dbg("init_acpi"); /* XXX */ - /* initialize internal data structure etc. */ - retval = pcihp_acpi_glue_init(); - - /* read initial number of slots */ - if (!retval) { - num_slots = pcihp_acpi_get_num_slots(); - if (num_slots == 0) - retval = -ENODEV; - } - - return retval; -} - -static void exit_acpi (void) -{ - /* deallocate internal data structures etc. */ - pcihp_acpi_glue_exit(); -} - -#define SLOT_NAME_SIZE 10 -static void make_slot_name (struct slot *slot) -{ - /* FIXME - get this from the ACPI representation of the slot */ - snprintf (slot->hotplug_slot->name, SLOT_NAME_SIZE, "ACPI%d", slot->number); -} - -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; - make_slot_name (slot); - slot->hotplug_slot->ops = &acpi_hotplug_slot_ops; - - slot->acpi_slot = get_slot_from_id (i); - slot->hotplug_slot->info->power_status = pcihp_acpi_get_power_status(slot->acpi_slot); - slot->hotplug_slot->info->attention_status = pcihp_acpi_get_attention_status(slot->acpi_slot); - slot->hotplug_slot->info->latch_status = pcihp_acpi_get_latch_status(slot->acpi_slot); - slot->hotplug_slot->info->adapter_status = pcihp_acpi_get_adapter_status(slot->acpi_slot); - - dbg ("registering slot %d", i); - retval = pci_hp_register (slot->hotplug_slot); - if (retval) { - err ("pci_hp_register failed with error %d", 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); - } - - return retval; -} - -static void cleanup_slots (void) -{ - struct list_head *tmp; - struct slot *slot; - - 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_acpi_init(void) -{ - int retval; - - /* read all the ACPI info from the system */ - retval = init_acpi(); - if (retval) - return retval; - - retval = init_slots(); - if (retval) - return retval; - - info (DRIVER_DESC " version: " DRIVER_VERSION); - return 0; -} - -static void __exit pcihp_acpi_exit(void) -{ - cleanup_slots(); - exit_acpi(); -} - -module_init(pcihp_acpi_init); -module_exit(pcihp_acpi_exit); diff -Naur -X /home/greg/linux/dontdiff linux-2.4.19-rc2-ac2/drivers/hotplug/pcihp_acpi.h linux-2.4.19-rc2-ac2-greg/drivers/hotplug/pcihp_acpi.h --- linux-2.4.19-rc2-ac2/drivers/hotplug/pcihp_acpi.h Fri Jul 19 11:34:52 2002 +++ linux-2.4.19-rc2-ac2-greg/drivers/hotplug/pcihp_acpi.h Wed Dec 31 16:00:00 1969 @@ -1,231 +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 _PCIHP_ACPI_H -#define _PCIHP_ACPI_H - -#include "include/acpi.h" - -#if ACPI_CA_VERSION < 0x20020201 -/* until we get a new version of the ACPI driver for both ia32 and ia64 ... */ -#define acpi_util_eval_error(h,p,s) - -static acpi_status -acpi_evaluate_integer ( - acpi_handle handle, - acpi_string pathname, - acpi_object_list *arguments, - unsigned long *data) -{ - acpi_status status = AE_OK; - acpi_object element; - acpi_buffer buffer = {sizeof(acpi_object), &element}; - - if (!data) - return AE_BAD_PARAMETER; - - status = acpi_evaluate_object(handle, pathname, arguments, &buffer); - if (ACPI_FAILURE(status)) { - acpi_util_eval_error(handle, pathname, status); - return status; - } - - if (element.type != ACPI_TYPE_INTEGER) { - acpi_util_eval_error(handle, pathname, AE_BAD_DATA); - return AE_BAD_DATA; - } - - *data = element.integer.value; - - return AE_OK; -} -#else /* ACPI_CA_VERSION < 0x20020201 */ -#include "acpi_bus.h" -#endif /* ACPI_CA_VERSION < 0x20020201 */ - -/* compatibility stuff */ -#ifndef ACPI_MEMORY_RANGE -#define ACPI_MEMORY_RANGE MEMORY_RANGE -#endif - -#ifndef ACPI_IO_RANGE -#define ACPI_IO_RANGE IO_RANGE -#endif - -#ifndef ACPI_BUS_NUMBER_RANGE -#define ACPI_BUS_NUMBER_RANGE BUS_NUMBER_RANGE -#endif - -#ifndef ACPI_PREFETCHABLE_MEMORY -#define ACPI_PREFETCHABLE_MEMORY PREFETCHABLE_MEMORY -#endif - -#ifndef ACPI_PRODUCER -#define ACPI_PRODUCER PRODUCER -#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) - -/* - * types and constants - */ - -#define SLOT_MAGIC 0x67267322 - -struct pcihp_acpi_bridge; -struct pcihp_acpi_slot; - -/* slot information for each *physical* slot */ - -struct slot { - u32 magic; - u8 number; - struct hotplug_slot *hotplug_slot; - struct list_head slot_list; - - int attention_status; - - struct pci_resource *mem_head; - struct pci_resource *p_mem_head; - struct pci_resource *io_head; - struct pci_resource *bus_head; - - /* if there are multiple corresponding slot objects, - this point to one of them */ - struct pcihp_acpi_slot *acpi_slot; - - struct pci_dev *pci_dev; -}; - -#define RESOURCE_TYPE_IO (1) -#define RESOURCE_TYPE_MEM (2) -#define RESOURCE_TYPE_PREFETCH (3) -#define RESOURCE_TYPE_BUS (4) - -/* TBD 64bit resource support */ -struct pci_resource { - struct pci_resource * next; - u32 base; - u32 length; -}; - -/* bridge information for each bridge device in ACPI namespace */ - -struct pcihp_acpi_bridge { - struct list_head list; - acpi_handle handle; - struct pcihp_acpi_slot *slots; - int nr_slots; - u8 seg; - u8 bus; - u8 sub; - u32 status; - u32 flags; - - /* resources on this bus (free resources) */ - struct pci_resource *free_io; - struct pci_resource *free_mem; - struct pci_resource *free_prefetch; - struct pci_resource *free_bus; - - /* used resources (embedded or owned resources) */ - struct pci_resource *used_io; - struct pci_resource *used_mem; - struct pci_resource *used_prefetch; - struct pci_resource *used_bus; -}; - -/* - * slot information for each slot object in ACPI namespace - * usually 8 objects per slot (for each PCI function) - */ - -struct pcihp_acpi_slot { - struct pcihp_acpi_slot *next; - struct pcihp_acpi_bridge *bridge; /* this slot located on */ - struct list_head sibling; /* one slot may have different - objects (i.e. for each function) */ - acpi_handle handle; - u32 id; /* slot id (this driver specific) */ - u8 device; /* pci device# */ - u8 function; /* pci function# */ - u8 pin; /* pci interrupt pin */ - u32 sun; /* _SUN */ - u32 flags; /* see below */ - u32 status; /* _STA */ -}; - -/* PCI bus bridge HID */ -#define ACPI_PCI_ROOT_HID "PNP0A03" - -/* 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_FUNCTIONAL (0x00000008) -#define ACPI_STA_ALL (0x0000000f) - -/* bridge flags */ -#define BRIDGE_HAS_STA (0x00000001) - -/* slot flags */ - -#define SLOT_HAS_EJ0 (0x00000001) -#define SLOT_HAS_PS0 (0x00000002) -#define SLOT_HAS_PS3 (0x00000004) - -/* function prototypes */ - -/* pcihp_acpi_glue.c */ -extern int pcihp_acpi_glue_init (void); -extern void pcihp_acpi_glue_exit (void); -extern int pcihp_acpi_get_num_slots (void); -extern struct pcihp_acpi_slot *get_slot_from_id (int id); - -extern int pcihp_acpi_enable_slot (struct pcihp_acpi_slot *slot); -extern int pcihp_acpi_disable_slot (struct pcihp_acpi_slot *slot); -extern u8 pcihp_acpi_get_power_status (struct pcihp_acpi_slot *slot); -extern u8 pcihp_acpi_get_attention_status (struct pcihp_acpi_slot *slot); -extern u8 pcihp_acpi_get_latch_status (struct pcihp_acpi_slot *slot); -extern u8 pcihp_acpi_get_adapter_status (struct pcihp_acpi_slot *slot); - -#endif /* _PCIHP_ACPI_H */ diff -Naur -X /home/greg/linux/dontdiff linux-2.4.19-rc2-ac2/drivers/hotplug/pcihp_acpi_ctrl.c linux-2.4.19-rc2-ac2-greg/drivers/hotplug/pcihp_acpi_ctrl.c --- linux-2.4.19-rc2-ac2/drivers/hotplug/pcihp_acpi_ctrl.c Fri Jul 19 11:34:52 2002 +++ linux-2.4.19-rc2-ac2-greg/drivers/hotplug/pcihp_acpi_ctrl.c Wed Dec 31 16:00:00 1969 @@ -1,642 +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 - -#define _LINUX /* for acpi subcomponent */ - -#include "include/acpi.h" - -#include "pci_hotplug.h" -#include "pchihp_acpi.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_WARNING "%s: "__FUNCTION__": " fmt , MY_NAME , ## arg); } while (0) -#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) -#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) - - -/* local variables */ -static int debug = 0; - -/* - * 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); -} - -/* - * 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. - */ -struct pci_resource *hotplug_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 ( hotplug_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. - */ -struct pci_resource *hotplug_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 (hotplug_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. - */ -struct pci_resource *hotplug_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 ( hotplug_resource_sort_and_combine(head) ) - return(NULL); - - if ( sort_by_size(head) ) - return(NULL); - - for (node = *head; node; node = node->next) { - dbg(__FUNCTION__": req_size =%x node=%p, base=%x, length=%x\n", - size, node, node->base, node->length); - if (node->length < size) - continue; - - if (node->base & (size - 1)) { - dbg(__FUNCTION__": not aligned\n"); - // this one isn't base aligned properly - // so we'll make a new entry and split it up - temp_dword = (node->base | (size-1)) + 1; - - // Short circuit if adjusted size is too small - if ((node->length - (temp_dword - node->base)) < size) - continue; - - split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); - - if (!split_node) - return(NULL); - - split_node->base = node->base; - split_node->length = temp_dword - node->base; - node->base = temp_dword; - node->length -= split_node->length; - - // Put it in the list - split_node->next = node->next; - node->next = split_node; - } // End of non-aligned base - - // Don't need to check if too small since we already did - if (node->length > size) { - dbg(__FUNCTION__": too big\n"); - // this one is longer than we need - // so we'll make a new entry and split it up - split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); - - if (!split_node) - return(NULL); - - split_node->base = node->base + size; - split_node->length = node->length - size; - node->length = size; - - // Put it in the list - split_node->next = node->next; - node->next = split_node; - } // End of too big on top end - - dbg(__FUNCTION__": got one!!!\n"); - // If we got here, then it is the right size - // Now take it out of the list - if (*head == node) { - *head = node->next; - } else { - prevnode = *head; - while (prevnode->next != node) - prevnode = prevnode->next; - - prevnode->next = node->next; - } - node->next = NULL; - // Stop looping - break; - } - return(node); -} - -/* - * get_resource_with_base - * - * 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 *hotplug_get_resource_with_base (struct pci_resource **head, u32 base, u32 size) -{ - struct pci_resource *prevnode; - struct pci_resource *node; - struct pci_resource *split_node; - u32 temp_dword; - - if (!(*head)) - return(NULL); - - if ( hotplug_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", - base, size, node, 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_dword = base; - - // 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; - } - - dbg(": 2st req_base=%x req_size =%x node=%p, base=%x, length=%x\n", - base, size, node, 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 = (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(": 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); -} - -/* - * hotplug_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 hotplug_resource_sort_and_combine(struct pci_resource **head) -{ - struct pci_resource *node1; - struct pci_resource *node2; - int out_of_order = 1; - - dbg(__FUNCTION__": head = %p, *head = %p\n", head, *head); - - if (!(*head)) - return(1); - - dbg("*head->next = %p\n",(*head)->next); - - if (!(*head)->next) - return(0); /* only one item on the list, already sorted! */ - - dbg("*head->base = 0x%x\n",(*head)->base); - dbg("*head->next->base = 0x%x\n",(*head)->next->base); - while (out_of_order) { - out_of_order = 0; - - // Special case for swapping list head - if (((*head)->next) && - ((*head)->base > (*head)->next->base)) { - node1 = *head; - (*head) = (*head)->next; - node1->next = (*head)->next; - (*head)->next = node1; - out_of_order++; - } - - node1 = (*head); - - while (node1->next && node1->next->next) { - if (node1->next->base > node1->next->next->base) { - out_of_order++; - node2 = node1->next; - node1->next = node1->next->next; - node1 = node1->next; - node2->next = node1->next; - node1->next = node2; - } else - node1 = node1->next; - } - } // End of out_of_order loop - - node1 = *head; - - while (node1 && node1->next) { - if ((node1->base + node1->length) == node1->next->base) { - // Combine - dbg("8..\n"); - node1->length += node1->next->length; - node2 = node1->next; - node1->next = node1->next->next; - kfree(node2); - } else - node1 = node1->next; - } - - return(0); -} - -/* -EXPORT_SYMBOL(hotplug_get_io_resource); -EXPORT_SYMBOL(hotplug_get_max_resource); -EXPORT_SYMBOL(hotplug_get_resource); -EXPORT_SYMBOL(hotplug_resource_sort_and_combine); -*/ diff -Naur -X /home/greg/linux/dontdiff linux-2.4.19-rc2-ac2/drivers/hotplug/pcihp_acpi_glue.c linux-2.4.19-rc2-ac2-greg/drivers/hotplug/pcihp_acpi_glue.c --- linux-2.4.19-rc2-ac2/drivers/hotplug/pcihp_acpi_glue.c Fri Jul 19 11:34:52 2002 +++ linux-2.4.19-rc2-ac2-greg/drivers/hotplug/pcihp_acpi_glue.c Wed Dec 31 16:00:00 1969 @@ -1,757 +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 "pcihp_acpi.h" - -/* - * TODO: - * resource management - * irq related interface? (_PRT) - * consider locking - */ - -static LIST_HEAD(bridge_list); - -static int debug = 1; /* XXX set 0 after debug */ -#define MY_NAME "pcihp_acpi_glue" - -static void handle_hotplug_event (acpi_handle, u32, void *); - -/* - * initialization & terminatation routines - */ - -/* - * Ejectable slot satisfies at least these conditions: - * 1. has _ADR method - * 2. has _STA method - * 3. has _EJ0 method - * - * optionally - * 1. has _PS0 method - * 2. has _PS3 method - * 3. TBD... - */ - -/* callback routine to check the existence of ejectable slots */ -static acpi_status -is_ejectable_slot (acpi_handle handle, u32 lvl, void *context, void **rv) -{ - acpi_status status; - acpi_handle tmp; - int *count = (int *)context; - - status = acpi_get_handle(handle, "_ADR", &tmp); - - if (ACPI_FAILURE(status)) { - return AE_OK; - } - - status = acpi_get_handle(handle, "_STA", &tmp); - - if (ACPI_FAILURE(status)) { - return AE_OK; - } - - status = acpi_get_handle(handle, "_EJ0", &tmp); - - if (ACPI_FAILURE(status)) { - return AE_OK; - } - - (*count)++; - - /* only one ejectable slot is enough */ - return AE_CTRL_TERMINATE; -} - - -/* callback routine to register each ACPI PCI slot object */ -static acpi_status -register_slot (acpi_handle handle, u32 lvl, void *context, void **rv) -{ - struct pcihp_acpi_bridge *bridge = (struct pcihp_acpi_bridge *)context; - struct pcihp_acpi_slot *slot, *newslot; - acpi_handle tmp; - acpi_status status = AE_OK; - static int num_slots = 0; /* XXX */ - unsigned long adr, sun, sta; - - 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)) { - dbg("This slot doesn't have _EJ0"); - //return AE_OK; - } - - status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); - - if (ACPI_FAILURE(status)) { - dbg("This slot doesn't have _STA"); - //return AE_OK; - } - - newslot = kmalloc(sizeof(struct pcihp_acpi_slot), GFP_KERNEL); - if (!newslot) { - return AE_NO_MEMORY; - } - - memset(newslot, 0, sizeof(struct pcihp_acpi_slot)); - - INIT_LIST_HEAD(&newslot->sibling); - newslot->bridge = bridge; - newslot->handle = handle; - newslot->device = (adr >> 16) & 0xffff; - newslot->function = adr & 0xffff; - newslot->status = sta; - newslot->sun = -1; - newslot->flags = SLOT_HAS_EJ0; - newslot->id = num_slots++; - bridge->nr_slots++; - - dbg("new slot id=%d device=0x%d function=0x%x", newslot->id, newslot->device, newslot->function); - - status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun); - if (ACPI_SUCCESS(status)) { - newslot->sun = sun; - } - - if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS0", &tmp))) { - newslot->flags |= SLOT_HAS_PS0; - } - - if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp))) { - newslot->flags |= SLOT_HAS_PS3; - } - - /* search for objects that share the same slot */ - for (slot = bridge->slots; slot; slot = slot->next) - if (slot->device == newslot->device) { - dbg("found a sibling slot!"); - list_add(&slot->sibling, &newslot->sibling); - newslot->id = slot->id; - num_slots --; - bridge->nr_slots --; - break; - } - - /* link myself to bridge's slot list */ - newslot->next = bridge->slots; - bridge->slots = newslot; - - return AE_OK; -} - -/* see if it's worth managing this brige */ -static int -detect_ejectable_slots (acpi_handle *root) -{ - acpi_status status; - int count; - - count = 0; - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root, ACPI_UINT32_MAX, - is_ejectable_slot, (void *)&count, NULL); - - dbg("%s: count=%d", __FUNCTION__, count); - return count; -} - - -/* - * push one resource to resource list - * - * TBD: use hotplug_resource_sort_and_combine - * TBD: 64bit resource handling (is it really used?) - */ -static void -push_resource (u32 base, u32 length, struct pci_resource **resource) -{ - struct pci_resource *resp, *newres; - int coalesced = 0; - - if (length == 0) { - dbg("zero sized resource. ignored."); - return; - } - - for (resp = *resource; resp; resp = resp->next) { - - /* coalesce contiguous region */ - - if (resp->base + resp->length == base) { - resp->length += length; - coalesced = 1; - break; - } - } - - if (!coalesced) { - newres = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); - if (!newres) { - /* TBD panic? */ - return; - } - newres->base = base; - newres->length = length; - newres->next = (*resource); - *resource = newres; - } -} - - -/* decode ACPI _CRS data and convert into our internal resource list */ -static void -decode_acpi_resource (acpi_resource *resource, struct pcihp_acpi_bridge *bridge) -{ - acpi_resource_address16 *address16_data; - acpi_resource_address32 *address32_data; - //acpi_resource_address64 *address64_data; - - u32 resource_type, producer_consumer, min_address_range, max_address_range, address_length; - u16 cache_attribute = 0; - - int done = 0, found; - - /* shut up gcc */ - resource_type = producer_consumer = min_address_range = max_address_range = address_length = 0; - - while (!done) { - found = 0; - - switch (resource->id) { - case ACPI_RSTYPE_ADDRESS16: - address16_data = (acpi_resource_address16 *)&resource->data; - resource_type = address16_data->resource_type; - producer_consumer = address16_data->producer_consumer; - min_address_range = address16_data->min_address_range; - max_address_range = address16_data->max_address_range; - address_length = address16_data->address_length; - if (resource_type == ACPI_MEMORY_RANGE) - cache_attribute = address16_data->attribute.memory.cache_attribute; - found = 1; - break; - - case ACPI_RSTYPE_ADDRESS32: - address32_data = (acpi_resource_address32 *)&resource->data; - resource_type = address32_data->resource_type; - producer_consumer = address32_data->producer_consumer; - min_address_range = address32_data->min_address_range; - max_address_range = address32_data->max_address_range; - address_length = address32_data->address_length; - if (resource_type == ACPI_MEMORY_RANGE) - cache_attribute = address32_data->attribute.memory.cache_attribute; - found = 1; - break; -/* - case ACPI_RSTYPE_ADDRESS64: - address64_data = (acpi_resource_address64 *)&resource->data; - resource_type = address64_data->resource_type; - break; -*/ - case ACPI_RSTYPE_END_TAG: - done = 1; - break; - - default: - /* ignore */ - break; - } - - resource = (acpi_resource *)((char*)resource + resource->length); - if (found && producer_consumer == ACPI_PRODUCER) { - switch (resource_type) { - case ACPI_MEMORY_RANGE: - if (cache_attribute == ACPI_PREFETCHABLE_MEMORY) { - dbg("resource type: prefetchable memory 0x%x - 0x%x", min_address_range, max_address_range); - push_resource(min_address_range, - address_length, - &bridge->free_prefetch); - } else { - dbg("resource type: memory 0x%x - 0x%x", min_address_range, max_address_range); - push_resource(min_address_range, - address_length, - &bridge->free_mem); - } - break; - case ACPI_IO_RANGE: - dbg("resource type: io 0x%x - 0x%x", min_address_range, max_address_range); - push_resource(min_address_range, - address_length, - &bridge->free_io); - break; - case ACPI_BUS_NUMBER_RANGE: - dbg("resource type: bus number %d - %d", min_address_range, max_address_range); - push_resource(min_address_range, - address_length, - &bridge->free_bus); - break; - default: - /* invalid type */ - break; - } - } - } -} - - -/* allocate and initialize bridge data structure */ -static int add_bridge (acpi_handle *handle) -{ - struct pcihp_acpi_bridge *bridge; - acpi_status status; - acpi_buffer buffer; - unsigned long tmp; - acpi_handle dummy_handle; - int sta = -1; - - 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", __FUNCTION__); - return 0; - } - sta = tmp; - } - - if (sta >= 0 && !(sta & ACPI_STA_PRESENT)) - /* don't register this object */ - return 0; - - dbg("%s: _STA: 0x%x", __FUNCTION__, (unsigned int)sta); - - /* check if this bridge has ejectable slots */ - - detect_ejectable_slots(handle); - //if (detect_ejectable_slots(handle) == 0) - //return 0; - - /* allocate per-bridge data structure and fill in */ - - bridge = kmalloc(sizeof(struct pcihp_acpi_bridge), GFP_KERNEL); - if (bridge == NULL) - return -ENOMEM; - - memset(bridge, 0, sizeof(struct pcihp_acpi_bridge)); - - if (sta >= 0) - bridge->flags |= BRIDGE_HAS_STA; - - /* get PCI segment number */ - status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp); - - if (ACPI_SUCCESS(status)) { - bridge->seg = tmp; - } else { - bridge->seg = 0; - } - - /* get PCI bus number */ - status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp); - - if (ACPI_SUCCESS(status)) { - bridge->bus = tmp; - } else { - bridge->bus = 0; - } - - /* to be overridden when we decode _CRS */ - bridge->sub = bridge->bus; - - /* register all slot objects under this bridge */ - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, ACPI_UINT32_MAX, - register_slot, bridge, NULL); - - /* decode resources */ - buffer.length = 0; - buffer.pointer = NULL; - - - /* TBD use new ACPI_ALLOCATE_BUFFER */ - status = acpi_get_current_resources(handle, &buffer); - if (status != AE_BUFFER_OVERFLOW) { - return -1; - } - - buffer.pointer = kmalloc(buffer.length, GFP_KERNEL); - if (!buffer.pointer) { - return -1; - } - - status = acpi_get_current_resources(handle, &buffer); - if (ACPI_FAILURE(status)) { - return -1; - } - - decode_acpi_resource(buffer.pointer, bridge); - - /* TBD decode _HPP (hot plug parameters) */ - // decode_hpp(bridge); - - kfree(buffer.pointer); - - /* check already allocated resources */ - /* TBD */ - - /* install notify handler */ - dbg("installing notify handler"); - status = acpi_install_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - handle_hotplug_event, NULL); - - if (ACPI_FAILURE(status)) { - err("failed to register interrupt notify handler"); - } - - list_add(&bridge->list, &bridge_list); - - return 0; -} - - -/* callback routine to enumerate all the bridges in ACPI namespace */ -static acpi_status -check_pci_bridge (acpi_handle handle, u32 lvl, void *context, void **rv) -{ - acpi_status status; - acpi_device_info info; - char objname[5]; - acpi_buffer buffer = { sizeof(objname), objname }; - - status = acpi_get_object_info(handle, &info); - if (ACPI_FAILURE(status)) { - dbg("%s: failed to get bridge information", __FUNCTION__); - return AE_OK; /* continue */ - } - - info.hardware_id[sizeof(info.hardware_id)-1] = '\0'; - - if (strcmp(info.hardware_id, ACPI_PCI_ROOT_HID) == 0) { - - acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); - dbg("%s: found PCI root bridge[%s]", __FUNCTION__, objname); - - add_bridge(handle); - } - return AE_OK; -} - - -/* interrupt handler */ -static void handle_hotplug_event (acpi_handle handle, u32 type, void *data) -{ - char objname[5]; - acpi_buffer buffer = { sizeof(objname), objname }; - - acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); - - switch (type) { - case ACPI_NOTIFY_BUS_CHECK: - /* hot insertion/surprise removal */ - /* TBD */ - dbg("%s: Bus check notify on %s", __FUNCTION__, objname); - break; - - case ACPI_NOTIFY_DEVICE_CHECK: - /* TBD */ - dbg("%s: Device check notify on %s", __FUNCTION__, objname); - break; - - case ACPI_NOTIFY_EJECT_REQUEST: - /* eject button pushed */ - /* TBD */ - dbg("%s: Device eject notify on %s", __FUNCTION__, objname); - break; - - default: - warn("notify_handler: unknown event type 0x%x", type); - break; - } -} - - -/* - * external interfaces - */ - -int pcihp_acpi_glue_init (void) -{ - acpi_status status; - - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, check_pci_bridge, - NULL, NULL); - - if (ACPI_FAILURE(status)) { - dbg("%s: acpi_walk_namespace() failed", __FUNCTION__); - } - - return 0; -} - -static void free_all_resources (struct pcihp_acpi_bridge *bridge) -{ - struct pci_resource *res, *next;; - - for (res = bridge->free_io; res; ) { - next = res->next; - kfree(res); - res = next; - } - - for (res = bridge->free_mem; res; ) { - next = res->next; - kfree(res); - res = next; - } - - for (res = bridge->free_prefetch; res; ) { - next = res->next; - kfree(res); - res = next; - } - - for (res = bridge->free_bus; res; ) { - next = res->next; - kfree(res); - res = next; - } -} - - -void pcihp_acpi_glue_exit (void) -{ - struct list_head *node; - struct pcihp_acpi_bridge *bridge; - struct pcihp_acpi_slot *slot, *next; - - list_for_each(node, &bridge_list) { - bridge = (struct pcihp_acpi_bridge *)node; - slot = bridge->slots; - while (slot) { - next = slot->next; - kfree(slot); - slot = next; - } - free_all_resources(bridge); - kfree(bridge); - } -} - - -int pcihp_acpi_get_num_slots (void) -{ - struct list_head *node; - struct pcihp_acpi_bridge *bridge; - int num_slots; - - num_slots = 0; - - list_for_each(node, &bridge_list) { - bridge = (struct pcihp_acpi_bridge *)node; - dbg("Bus:%d num_slots:%d", bridge->bus, bridge->nr_slots); - num_slots += bridge->nr_slots; - } - - dbg("num_slots = %d", num_slots); - return num_slots; -} - - -/* TBD: improve performance */ -struct pcihp_acpi_slot *get_slot_from_id (int id) -{ - struct list_head *node; - struct pcihp_acpi_bridge *bridge; - struct pcihp_acpi_slot *slot; - - list_for_each(node, &bridge_list) { - bridge = (struct pcihp_acpi_bridge *)node; - for (slot = bridge->slots; slot; slot = slot->next) - if (slot->id == id) - return slot; - } - - /* should never happen! */ - dbg("%s: no object for id %d",__FUNCTION__, id); - return 0; -} - - -/* power on slot */ -int pcihp_acpi_enable_slot (struct pcihp_acpi_slot *slot) -{ - acpi_status status; - - if (slot->flags & SLOT_HAS_PS0) { - dbg("%s: powering on bus%d/dev%d.", __FUNCTION__, - slot->bridge->bus, slot->device); - status = acpi_evaluate_object(slot->handle, "_PS0", NULL, NULL); - if (ACPI_FAILURE(status)) { - warn("%s: powering on bus%d/dev%d failed", - __FUNCTION__, slot->bridge->bus, slot->device); - return -1; - } - } - - return 0; -} - - -/* power off slot */ -int pcihp_acpi_disable_slot (struct pcihp_acpi_slot *slot) -{ - acpi_status status; - - if (slot->flags & SLOT_HAS_PS3) { - dbg("%s: powering off bus%d/dev%d.", __FUNCTION__, - slot->bridge->bus, slot->device); - status = acpi_evaluate_object(slot->handle, "_PS3", NULL, NULL); - if (ACPI_FAILURE(status)) { - warn("%s: _PS3 on bus%d/dev%d failed", - __FUNCTION__, slot->bridge->bus, slot->device); - return -1; - } - } - - if (slot->flags & SLOT_HAS_EJ0) { - dbg("%s: eject bus%d/dev%d.", __FUNCTION__, - slot->bridge->bus, slot->device); - status = acpi_evaluate_object(slot->handle, "_EJ0", NULL, NULL); - if (ACPI_FAILURE(status)) { - warn("%s: _EJ0 bus%d/dev%d failed", - __FUNCTION__, slot->bridge->bus, slot->device); - return -1; - } - } - - /* TBD - * evaluate _STA to check if state is successfully changed - * and update status - */ - - return 0; -} - - -static unsigned int get_slot_status(struct pcihp_acpi_slot *slot) -{ - acpi_status status; - unsigned long sta; - - status = acpi_evaluate_integer(slot->handle, "_STA", NULL, &sta); - - if (ACPI_FAILURE(status)) { - err("%s: _STA evaluation failed", __FUNCTION__); - return 0; - } - - return (int)sta; -} - - -/* - * slot enabled: 1 - * slot disabled: 0 - */ -u8 pcihp_acpi_get_power_status (struct pcihp_acpi_slot *slot) -{ - unsigned int sta; - - /* TBD - * . guarantee check _STA on function# 0 - * . check configuration space before _STA? - */ - - sta = get_slot_status(slot); - - return (sta & ACPI_STA_ENABLED) ? 1 : 0; -} - - -/* XXX this function is not used */ -/* - * attention LED ON: 1 - * OFF: 0 - */ -u8 pcihp_acpi_get_attention_status (struct pcihp_acpi_slot *slot) -{ - /* TBD - * no direct attention led status information via ACPI - */ - - return 0; -} - - -/* - * latch closed: 1 - * latch open: 0 - */ -u8 pcihp_acpi_get_latch_status (struct pcihp_acpi_slot *slot) -{ - unsigned int sta; - - /* TBD - * no direct latch information via ACPI - */ - - sta = get_slot_status(slot); - - return (sta & ACPI_STA_SHOW_IN_UI) ? 1 : 0; -} - - -/* - * adapter presence : 2 - * absence : 0 - */ -u8 pcihp_acpi_get_adapter_status (struct pcihp_acpi_slot *slot) -{ - unsigned int sta; - - /* TBD - * is this information correct? - */ - - sta = get_slot_status(slot); - - return (sta == 0) ? 0 : 2; -}