From: Greg KH To: marcelo@conectiva.com.br Cc: pcihpd-discuss@lists.sourceforge.net Subject: [PATCH 5 of 6] PCI Hotplug ACPI driver added Hi, Here's a patch against 2.4.19-pre1 that adds support for ACPI PCI Hotplug controllers. This driver was written by Hiroshi Aono and Takayoshi Kochi, with a bit of help from me. This driver is still quite experimental and has been reported to work on NEC i386 machines and partially on some IBM ia64 machines. thanks, greg k-h diff -Nru a/drivers/hotplug/pcihp_acpi.c b/drivers/hotplug/pcihp_acpi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/pcihp_acpi.c Tue Feb 26 13:19:34 2002 @@ -0,0 +1,397 @@ +/* + * 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 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/pcihp_acpi.h Tue Feb 26 13:19:34 2002 @@ -0,0 +1,231 @@ +/* + * 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 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/pcihp_acpi_ctrl.c Tue Feb 26 13:19:33 2002 @@ -0,0 +1,642 @@ +/* + * 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 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/pcihp_acpi_glue.c Tue Feb 26 13:19:34 2002 @@ -0,0 +1,757 @@ +/* + * 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; +}