patch-2.4.19 linux-2.4.19/drivers/hotplug/pcihp_acpi_glue.c
Next file: linux-2.4.19/drivers/i2c/i2c-adap-ite.c
Previous file: linux-2.4.19/drivers/hotplug/pcihp_acpi_ctrl.c
Back to the patch index
Back to the overall index
- Lines: 758
- Date:
Fri Aug 2 17:39:44 2002
- Orig file:
linux-2.4.18/drivers/hotplug/pcihp_acpi_glue.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -urN linux-2.4.18/drivers/hotplug/pcihp_acpi_glue.c linux-2.4.19/drivers/hotplug/pcihp_acpi_glue.c
@@ -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 <t-kouchi@cq.jp.nec.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#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;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)