# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.684 -> 1.685 # drivers/hotplug/pcihp_acpi.c 1.1 -> (deleted) # drivers/hotplug/Makefile 1.2 -> 1.3 # drivers/hotplug/pcihp_acpi.h 1.1 -> (deleted) # drivers/hotplug/pcihp_acpi_glue.c 1.1 -> (deleted) # drivers/hotplug/pcihp_acpi_ctrl.c 1.1 -> (deleted) # (new) -> 1.1 drivers/hotplug/acpiphp.h # (new) -> 1.1 drivers/hotplug/acpiphp_core.c # (new) -> 1.1 drivers/hotplug/acpiphp_glue.c # (new) -> 1.1 drivers/hotplug/acpiphp_pci.c # (new) -> 1.1 drivers/hotplug/acpiphp_res.c # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/08/06 greg@kroah.com 1.685 # ACPI PCI Hotplug driver update # # This is from Takayoshi KOCHI # -------------------------------------------- # diff -Nru a/drivers/hotplug/Makefile b/drivers/hotplug/Makefile --- a/drivers/hotplug/Makefile Tue Aug 6 12:10:50 2002 +++ b/drivers/hotplug/Makefile Tue Aug 6 12:10:50 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 -Nru a/drivers/hotplug/acpiphp.h b/drivers/hotplug/acpiphp.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/acpiphp.h Tue Aug 6 12:10:50 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 -Nru a/drivers/hotplug/acpiphp_core.c b/drivers/hotplug/acpiphp_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/acpiphp_core.c Tue Aug 6 12:10:50 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 -Nru a/drivers/hotplug/acpiphp_glue.c b/drivers/hotplug/acpiphp_glue.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/acpiphp_glue.c Tue Aug 6 12:10:50 2002 @@ -0,0 +1,1514 @@ +/* + * 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 -Nru a/drivers/hotplug/acpiphp_pci.c b/drivers/hotplug/acpiphp_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/acpiphp_pci.c Tue Aug 6 12:10:50 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 -Nru a/drivers/hotplug/acpiphp_res.c b/drivers/hotplug/acpiphp_res.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/acpiphp_res.c Tue Aug 6 12:10:50 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 -Nru a/drivers/hotplug/pcihp_acpi.c b/drivers/hotplug/pcihp_acpi.c --- a/drivers/hotplug/pcihp_acpi.c Tue Aug 6 12:10:50 2002 +++ /dev/null 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 -Nru a/drivers/hotplug/pcihp_acpi.h b/drivers/hotplug/pcihp_acpi.h --- a/drivers/hotplug/pcihp_acpi.h Tue Aug 6 12:10:50 2002 +++ /dev/null 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 -Nru a/drivers/hotplug/pcihp_acpi_ctrl.c b/drivers/hotplug/pcihp_acpi_ctrl.c --- a/drivers/hotplug/pcihp_acpi_ctrl.c Tue Aug 6 12:10:50 2002 +++ /dev/null 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 -Nru a/drivers/hotplug/pcihp_acpi_glue.c b/drivers/hotplug/pcihp_acpi_glue.c --- a/drivers/hotplug/pcihp_acpi_glue.c Tue Aug 6 12:10:50 2002 +++ /dev/null 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; -}