]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] ACPI patch 2/9
authorAndy Grover <agrover@groveronline.com>
Fri, 15 Mar 2002 07:28:23 +0000 (23:28 -0800)
committerLinus Torvalds <torvalds@home.transmeta.com>
Fri, 15 Mar 2002 07:28:23 +0000 (23:28 -0800)
This patch adds in the new drivers.

- Support for driverfs
- File/code layout more in the Linux style
- improvements to battery, processor, and thermal support

18 files changed:
drivers/acpi/acpi_ac.c [new file with mode: 0644]
drivers/acpi/acpi_battery.c [new file with mode: 0644]
drivers/acpi/acpi_bus.c [new file with mode: 0644]
drivers/acpi/acpi_bus.h [new file with mode: 0644]
drivers/acpi/acpi_button.c [new file with mode: 0644]
drivers/acpi/acpi_drivers.h [new file with mode: 0644]
drivers/acpi/acpi_ec.c [new file with mode: 0644]
drivers/acpi/acpi_fan.c [new file with mode: 0644]
drivers/acpi/acpi_ksyms.c
drivers/acpi/acpi_osl.c [new file with mode: 0644]
drivers/acpi/acpi_pci_link.c [new file with mode: 0644]
drivers/acpi/acpi_pci_root.c [new file with mode: 0644]
drivers/acpi/acpi_power.c [new file with mode: 0644]
drivers/acpi/acpi_processor.c [new file with mode: 0644]
drivers/acpi/acpi_system.c [new file with mode: 0644]
drivers/acpi/acpi_tables.c [new file with mode: 0644]
drivers/acpi/acpi_thermal.c [new file with mode: 0644]
drivers/acpi/acpi_utils.c [new file with mode: 0644]

diff --git a/drivers/acpi/acpi_ac.c b/drivers/acpi/acpi_ac.c
new file mode 100644 (file)
index 0000000..bba6bdf
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ *  acpi_ac.c - ACPI AC Adapter Driver ($Revision: 22 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include "acpi_bus.h"
+#include "acpi_drivers.h"
+
+
+#define _COMPONENT             ACPI_AC_COMPONENT
+ACPI_MODULE_NAME               ("acpi_ac")
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION(ACPI_AC_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+#define PREFIX                 "ACPI: "
+
+
+int acpi_ac_add (struct acpi_device *device);
+int acpi_ac_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_ac_driver = {
+       name:                   ACPI_AC_DRIVER_NAME,
+       class:                  ACPI_AC_CLASS,
+       ids:                    ACPI_AC_HID,
+       ops:                    {
+                                       add:    acpi_ac_add,
+                                       remove: acpi_ac_remove,
+                               },
+};
+
+struct acpi_ac {
+       acpi_handle             handle;
+       unsigned long           state;
+};
+
+
+/* --------------------------------------------------------------------------
+                               AC Adapter Management
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_ac_get_state (
+       struct acpi_ac          *ac)
+{
+       acpi_status             status = AE_OK;
+
+       ACPI_FUNCTION_TRACE("acpi_ac_get_state");
+
+       if (!ac)
+               return_VALUE(-EINVAL);
+
+       status = acpi_evaluate_integer(ac->handle, "_PSR", NULL, &ac->state);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error reading AC Adapter state\n"));
+               ac->state = ACPI_AC_STATUS_UNKNOWN;
+               return_VALUE(-ENODEV);
+       }
+       
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                              FS Interface (/proc)
+   -------------------------------------------------------------------------- */
+
+#include <linux/compatmac.h>
+#include <linux/proc_fs.h>
+
+struct proc_dir_entry          *acpi_ac_dir = NULL;
+
+static int
+acpi_ac_read_state (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_ac          *ac = (struct acpi_ac *) data;
+       char                    *p = page;
+       int                     len = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_ac_read_state");
+
+       if (!ac || (off != 0))
+               goto end;
+
+       if (0 != acpi_ac_get_state(ac)) {
+               p += sprintf(p, "ERROR: Unable to read AC Adapter state\n");
+               goto end;
+       }
+
+       p += sprintf(p, "state:                   ");
+       switch (ac->state) {
+       case ACPI_AC_STATUS_OFFLINE:
+               p += sprintf(p, "off-line\n");
+               break;
+       case ACPI_AC_STATUS_ONLINE:
+               p += sprintf(p, "on-line\n");
+               break;
+       default:
+               p += sprintf(p, "unknown\n");
+               break;
+       }
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_ac_add_fs (
+       struct acpi_device      *device)
+{
+       struct proc_dir_entry   *entry = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_ac_add_fs");
+
+       if (!acpi_ac_dir) {
+               acpi_ac_dir = proc_mkdir(ACPI_AC_CLASS, acpi_root_dir);
+               if (!acpi_ac_dir)
+                       return_VALUE(-ENODEV);
+       }
+
+       if (!acpi_device_dir(device)) {
+               acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+                       acpi_ac_dir);
+               if (!acpi_device_dir(device))
+                       return_VALUE(-ENODEV);
+       }
+
+       /* 'state' [R] */
+       entry = create_proc_entry(ACPI_AC_FILE_STATE,
+               S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_AC_FILE_STATE));
+       else {
+               entry->read_proc = acpi_ac_read_state;
+               entry->data = acpi_driver_data(device);
+       }
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_ac_remove_fs (
+       struct acpi_device      *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_ac_remove_fs");
+
+       if (!acpi_ac_dir)
+               return_VALUE(-ENODEV);
+
+       if (acpi_device_dir(device))
+               remove_proc_entry(acpi_device_bid(device), acpi_ac_dir);
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                                   Driver Model
+   -------------------------------------------------------------------------- */
+
+void
+acpi_ac_notify (
+       acpi_handle             handle,
+       u32                     event,
+       void                    *data)
+{
+       struct acpi_ac          *ac = (struct acpi_ac *) data;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_ac_notify");
+
+       if (!ac)
+               return;
+
+       if (0 != acpi_bus_get_device(ac->handle, &device))
+               return_VOID;
+
+       switch (event) {
+       case ACPI_AC_NOTIFY_STATUS:
+               acpi_ac_get_state(ac);
+               acpi_bus_generate_event(device, event, (u32) ac->state);
+               break;
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Unsupported event [0x%x]\n", event));
+               break;
+       }
+
+       return_VOID;
+}
+
+
+int
+acpi_ac_add (
+       struct acpi_device      *device)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_ac          *ac = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_ac_add");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       ac = kmalloc(sizeof(struct acpi_ac), GFP_KERNEL);
+       if (!ac)
+               return_VALUE(-ENOMEM);
+       memset(ac, 0, sizeof(struct acpi_ac));
+
+       ac->handle = device->handle;
+       sprintf(acpi_device_name(device), "%s", ACPI_AC_DEVICE_NAME);
+       sprintf(acpi_device_class(device), "%s", ACPI_AC_CLASS);
+       acpi_driver_data(device) = ac;
+
+       result = acpi_ac_get_state(ac);
+       if (0 != result)
+               goto end;
+
+       result = acpi_ac_add_fs(device);
+       if (0 != result)
+               goto end;
+
+       status = acpi_install_notify_handler(ac->handle,
+               ACPI_DEVICE_NOTIFY, acpi_ac_notify, ac);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error installing notify handler\n"));
+               result = -ENODEV;
+               goto end;
+       }
+
+       printk(KERN_INFO PREFIX "%s [%s] (%s)\n", 
+               acpi_device_name(device), acpi_device_bid(device), 
+               ac->state?"on-line":"off-line");
+
+end:
+       if (0 != result) {
+               acpi_ac_remove_fs(device);
+               kfree(ac);
+       }
+
+       return_VALUE(result);
+}
+
+
+int
+acpi_ac_remove (
+       struct acpi_device      *device,
+       int                     type)
+{
+       acpi_status             status = AE_OK;
+       struct acpi_ac          *ac = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_ac_remove");
+       if (!device || !acpi_driver_data(device))
+               return_VALUE(-EINVAL);
+
+       ac = (struct acpi_ac *) acpi_driver_data(device);
+
+       status = acpi_remove_notify_handler(ac->handle,
+               ACPI_DEVICE_NOTIFY, acpi_ac_notify);
+       if (ACPI_FAILURE(status))
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error removing notify handler\n"));
+
+       acpi_ac_remove_fs(device);
+
+       kfree(ac);
+
+       return_VALUE(0);
+}
+
+
+int __init
+acpi_ac_init (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_ac_init");
+
+       result = acpi_bus_register_driver(&acpi_ac_driver);
+       if (0 > result) {
+               remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir);
+               return_VALUE(-ENODEV);
+       }
+
+       return_VALUE(0);
+}
+
+
+void __exit
+acpi_ac_exit (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_ac_exit");
+
+       result = acpi_bus_unregister_driver(&acpi_ac_driver);
+       if (0 == result)
+               remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir);
+
+       return_VOID;
+}
+
+
+module_init(acpi_ac_init);
+module_exit(acpi_ac_exit);
diff --git a/drivers/acpi/acpi_battery.c b/drivers/acpi/acpi_battery.c
new file mode 100644 (file)
index 0000000..388a94e
--- /dev/null
@@ -0,0 +1,819 @@
+/*
+ *  acpi_battery.c - ACPI Battery Driver ($Revision: 31 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include "acpi_bus.h"
+#include "acpi_drivers.h"
+
+
+#define _COMPONENT             ACPI_BATTERY_COMPONENT
+ACPI_MODULE_NAME               ("acpi_battery")
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION(ACPI_BATTERY_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+#define PREFIX                 "ACPI: "
+
+
+#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF
+
+#define ACPI_BATTERY_FORMAT_BIF        "NNNNNNNNNSSSS"
+#define ACPI_BATTERY_FORMAT_BST        "NNNN"
+
+static int acpi_battery_add (struct acpi_device *device);
+static int acpi_battery_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_battery_driver = {
+       name:                   ACPI_BATTERY_DRIVER_NAME,
+       class:                  ACPI_BATTERY_CLASS,
+       ids:                    ACPI_BATTERY_HID,
+       ops:                    {
+                                       add:    acpi_battery_add,
+                                       remove: acpi_battery_remove,
+                               },
+};
+
+struct acpi_battery_status {
+       acpi_integer            state;
+       acpi_integer            present_rate;
+       acpi_integer            remaining_capacity;
+       acpi_integer            present_voltage;
+};
+
+struct acpi_battery_info {
+       acpi_integer            power_unit;
+       acpi_integer            design_capacity;
+       acpi_integer            last_full_capacity;
+       acpi_integer            battery_technology;
+       acpi_integer            design_voltage;
+       acpi_integer            design_capacity_warning;
+       acpi_integer            design_capacity_low;
+       acpi_integer            battery_capacity_granularity_1;
+       acpi_integer            battery_capacity_granularity_2;
+       acpi_string             model_number;
+       acpi_string             serial_number;
+       acpi_string             battery_type;
+       acpi_string             oem_info;
+};
+
+struct acpi_battery_flags {
+       u8                      present:1;      /* Bay occupied? */
+       u8                      power_unit:1;   /* 0=watts, 1=apms */
+       u8                      alarm:1;        /* _BTP present? */
+       u8                      reserved:5;
+};
+
+struct acpi_battery_trips {
+       unsigned long           warning;
+       unsigned long           low;
+};
+
+struct acpi_battery {
+       acpi_handle             handle;
+       struct acpi_battery_flags flags;
+       struct acpi_battery_trips trips;
+       unsigned long           alarm;
+       struct acpi_battery_info *info;
+};
+
+
+/* --------------------------------------------------------------------------
+                               Battery Management
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_battery_get_info (
+       struct acpi_battery     *battery,
+       struct acpi_battery_info **bif)
+{
+       acpi_status             status = 0;
+       acpi_buffer             buffer = {0, NULL};
+       acpi_buffer             format = {sizeof(ACPI_BATTERY_FORMAT_BIF),
+                                               ACPI_BATTERY_FORMAT_BIF};
+       acpi_buffer             data = {0, NULL};
+       acpi_object             *package = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_battery_get_info");
+
+       if (!battery || !bif)
+               return_VALUE(-EINVAL);
+
+       /* Evalute _BIF */
+
+       status = acpi_evaluate(battery->handle, "_BIF", NULL, &buffer);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       package = (acpi_object *) buffer.pointer;
+
+       /* Extract Package Data */
+
+       status = acpi_extract_package(package, &format, &data);
+       if (status != AE_BUFFER_OVERFLOW) {
+               kfree(buffer.pointer);
+               return_VALUE(-ENODEV);
+       }
+
+       data.pointer = kmalloc(data.length, GFP_KERNEL);
+       if (!data.pointer) {
+               kfree(buffer.pointer);
+               return_VALUE(-ENOMEM);
+       }
+       memset(data.pointer, 0, data.length);
+
+       status = acpi_extract_package(package, &format, &data);
+       if (ACPI_FAILURE(status)) {
+               kfree(buffer.pointer);
+               kfree(data.pointer);
+               return_VALUE(-ENODEV);
+       }
+
+       kfree(buffer.pointer);
+
+       (*bif) = data.pointer;
+
+       return_VALUE(0);
+}
+
+static int
+acpi_battery_get_status (
+       struct acpi_battery     *battery,
+       struct acpi_battery_status **bst)
+{
+       acpi_status             status = 0;
+       acpi_buffer             buffer = {0, NULL};
+       acpi_buffer             format = {sizeof(ACPI_BATTERY_FORMAT_BST),
+                                               ACPI_BATTERY_FORMAT_BST};
+       acpi_buffer             data = {0, NULL};
+       acpi_object             *package = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_battery_get_status");
+
+       if (!battery || !bst)
+               return_VALUE(-EINVAL);
+
+       /* Evalute _BST */
+
+       status = acpi_evaluate(battery->handle, "_BST", NULL, &buffer);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       package = (acpi_object *) buffer.pointer;
+
+       /* Extract Package Data */
+
+       status = acpi_extract_package(package, &format, &data);
+       if (status != AE_BUFFER_OVERFLOW) {
+               kfree(buffer.pointer);
+               return_VALUE(-ENODEV);
+       }
+
+       data.pointer = kmalloc(data.length, GFP_KERNEL);
+       if (!data.pointer) {
+               kfree(buffer.pointer);
+               return_VALUE(-ENOMEM);
+       }
+       memset(data.pointer, 0, data.length);
+
+       status = acpi_extract_package(package, &format, &data);
+       if (ACPI_FAILURE(status)) {
+               kfree(buffer.pointer);
+               kfree(data.pointer);
+               return_VALUE(-ENODEV);
+       }
+
+       kfree(buffer.pointer);
+
+       (*bst) = data.pointer;
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_battery_set_alarm (
+       struct acpi_battery     *battery,
+       unsigned long           alarm)
+{
+       acpi_status             status = 0;
+       acpi_object             arg0 = {ACPI_TYPE_INTEGER};
+       acpi_object_list        arg_list = {1, &arg0};
+
+       ACPI_FUNCTION_TRACE("acpi_battery_set_alarm");
+
+       if (!battery)
+               return_VALUE(-EINVAL);
+
+       if (!battery->flags.alarm)
+               return_VALUE(-ENODEV);
+
+       arg0.integer.value = alarm;
+
+       status = acpi_evaluate(battery->handle, "_BTP", &arg_list, NULL);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", (u32) alarm));
+
+       battery->alarm = alarm;
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_battery_check (
+       struct acpi_battery     *battery)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       acpi_handle             handle = NULL;
+       struct acpi_device      *device = NULL;
+       struct acpi_battery_info *bif = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_battery_check");
+       
+       if (!battery)
+               return_VALUE(-EINVAL);
+
+       result = acpi_bus_get_device(battery->handle, &device);
+       if (0 != result)
+               return_VALUE(result);
+
+       result = acpi_bus_get_status(device);
+       if (0 != result)
+               return_VALUE(result);
+
+       /* Insertion? */
+
+       if (!battery->flags.present && device->status.battery_present) {
+
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Battery inserted\n"));
+
+               /* Evalute _BIF to get certain static information */
+
+               result = acpi_battery_get_info(battery, &bif);
+               if (0 != result)
+                       return_VALUE(result);
+
+               battery->flags.power_unit = bif->power_unit;
+               battery->trips.warning = bif->design_capacity_warning;
+               battery->trips.low = bif->design_capacity_low;
+               kfree(bif);
+
+               /* See if alarms are supported, and if so, set default */
+
+               status = acpi_get_handle(battery->handle, "_BTP", &handle);
+               if (ACPI_SUCCESS(status)) {
+                       battery->flags.alarm = 1;
+                       acpi_battery_set_alarm(battery, battery->trips.warning);
+               }
+       }
+
+       /* Removal? */
+
+       else if (battery->flags.present && !device->status.battery_present) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Battery removed\n"));
+       }
+
+       battery->flags.present = device->status.battery_present;
+
+       return_VALUE(result);
+}
+
+
+/* --------------------------------------------------------------------------
+                              FS Interface (/proc)
+   -------------------------------------------------------------------------- */
+
+#include <linux/compatmac.h>
+#include <linux/proc_fs.h>
+
+struct proc_dir_entry          *acpi_battery_dir = NULL;
+
+static int
+acpi_battery_read_info (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       int                     result = 0;
+       struct acpi_battery     *battery = (struct acpi_battery *) data;
+       struct acpi_battery_info *bif = 0;
+       char                    *units = "?";
+       char                    *p = page;
+       int                     len = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_battery_read_info");
+
+       if (!battery)
+               goto end;
+
+       if (battery->flags.present)
+               p += sprintf(p, "present:                 yes\n");
+       else {
+               p += sprintf(p, "present:                 no\n");
+               goto end;
+       }
+
+       /* Battery Info (_BIF) */
+
+       result = acpi_battery_get_info(battery, &bif);
+       if ((0 != result) || !bif) {
+               p += sprintf(p, "ERROR: Unable to read battery information\n");
+               goto end;
+       }
+
+       units = bif->power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS;
+                                       
+       if (bif->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
+               p += sprintf(p, "design capacity:         unknown\n");
+       else
+               p += sprintf(p, "design capacity:         %d %sh\n",
+                       (u32) bif->design_capacity, units);
+
+       if (bif->last_full_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
+               p += sprintf(p, "last full capacity:      unknown\n");
+       else
+               p += sprintf(p, "last full capacity:      %d %sh\n",
+                       (u32) bif->last_full_capacity, units);
+
+       switch ((u32) bif->battery_technology) {
+       case 0:
+               p += sprintf(p, "battery technology:      non-rechargeable\n");
+               break;
+       case 1:
+               p += sprintf(p, "battery technology:      rechargeable\n");
+               break;
+       default:
+               p += sprintf(p, "battery technology:      unknown\n");
+               break;
+       }
+
+       if (bif->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
+               p += sprintf(p, "design voltage:          unknown\n");
+       else
+               p += sprintf(p, "design voltage:          %d mV\n",
+                       (u32) bif->design_voltage);
+       
+       p += sprintf(p, "design capacity warning: %d %sh\n",
+               (u32) bif->design_capacity_warning, units);
+       p += sprintf(p, "design capacity low:     %d %sh\n",
+               (u32) bif->design_capacity_low, units);
+       p += sprintf(p, "capacity granularity 1:  %d %sh\n",
+               (u32) bif->battery_capacity_granularity_1, units);
+       p += sprintf(p, "capacity granularity 2:  %d %sh\n",
+               (u32) bif->battery_capacity_granularity_2, units);
+       p += sprintf(p, "model number:            %s\n",
+               bif->model_number);
+       p += sprintf(p, "serial number:           %s\n",
+               bif->serial_number);
+       p += sprintf(p, "battery type:            %s\n",
+               bif->battery_type);
+       p += sprintf(p, "OEM info:                %s\n",
+               bif->oem_info);
+
+end:
+       if (bif)
+               kfree(bif);
+
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_battery_read_state (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       int                     result = 0;
+       struct acpi_battery     *battery = (struct acpi_battery *) data;
+       struct acpi_battery_status *bst = NULL;
+       char                    *units = "?";
+       char                    *p = page;
+       int                     len = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_battery_read_state");
+
+       if (!battery)
+               goto end;
+
+       if (battery->flags.present)
+               p += sprintf(p, "present:                 yes\n");
+       else {
+               p += sprintf(p, "present:                 no\n");
+               goto end;
+       }
+
+       /* Battery Units */
+
+       units = battery->flags.power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS;
+
+       /* Battery Status (_BST) */
+
+       result = acpi_battery_get_status(battery, &bst);
+       if ((0 != result) || !bst) {
+               p += sprintf(p, "ERROR: Unable to read battery status\n");
+               goto end;
+       }
+
+       if (!(bst->state & 0x04))
+               p += sprintf(p, "capacity state:          ok\n");
+       else
+               p += sprintf(p, "capacity state:          critical\n");
+
+       if ((bst->state & 0x01) && (bst->state & 0x02))
+               p += sprintf(p, "charging state:          charging/discharging\n");
+       else if (bst->state & 0x01)
+               p += sprintf(p, "charging state:          discharging\n");
+       else if (bst->state & 0x02)
+               p += sprintf(p, "charging state:          charging\n");
+       else
+               p += sprintf(p, "charging state:          unknown\n");
+
+       if (bst->present_rate == ACPI_BATTERY_VALUE_UNKNOWN)
+               p += sprintf(p, "present rate:            unknown\n");
+       else
+               p += sprintf(p, "present rate:            %d %s\n",
+                       (u32) bst->present_rate, units);
+
+       if (bst->remaining_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
+               p += sprintf(p, "remaining capacity:      unknown\n");
+       else
+               p += sprintf(p, "remaining capacity:      %d %sh\n",
+                       (u32) bst->remaining_capacity, units);
+
+       if (bst->present_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
+               p += sprintf(p, "present voltage:         unknown\n");
+       else
+               p += sprintf(p, "present voltage:         %d mV\n",
+                       (u32) bst->present_voltage);
+
+end:
+       if (bst)
+               kfree(bst);
+
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_battery_read_alarm (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_battery     *battery = (struct acpi_battery *) data;
+       char                    *units = "?";
+       char                    *p = page;
+       int                     len = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_battery_read_alarm");
+
+       if (!battery)
+               goto end;
+
+       if (!battery->flags.present) {
+               p += sprintf(p, "present:                 no\n");
+               goto end;
+       }
+
+       /* Battery Units */
+       
+       units = battery->flags.power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS;
+
+       /* Battery Alarm */
+
+       p += sprintf(p, "alarm:                   ");
+       if (!battery->alarm)
+               p += sprintf(p, "unsupported\n");
+       else
+               p += sprintf(p, "%d %sh\n", (u32) battery->alarm, units);
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_battery_write_alarm (
+       struct file             *file,
+       const char              *buffer,
+       unsigned long           count,
+       void                    *data)
+{
+       int                     result = 0;
+       struct acpi_battery     *battery = (struct acpi_battery *) data;
+       char                    alarm_string[12] = {'\0'};
+
+       ACPI_FUNCTION_TRACE("acpi_battery_write_alarm");
+
+       if (!battery || (count > sizeof(alarm_string) - 1))
+               return_VALUE(-EINVAL);
+
+       if (!battery->flags.present)
+               return_VALUE(-ENODEV);
+
+       if (copy_from_user(alarm_string, buffer, count))
+               return_VALUE(-EFAULT);
+       
+       alarm_string[count] = '\0';
+
+       result = acpi_battery_set_alarm(battery, 
+               simple_strtoul(alarm_string, NULL, 0));
+       if (0 != result)
+               return_VALUE(result);
+
+       return_VALUE(count);
+}
+
+
+static int
+acpi_battery_add_fs (
+       struct acpi_device      *device)
+{
+       struct proc_dir_entry   *entry = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_battery_add_fs");
+
+       if (!acpi_battery_dir) {
+               acpi_battery_dir = proc_mkdir(ACPI_BATTERY_CLASS, acpi_root_dir);
+               if (!acpi_battery_dir)
+                       return_VALUE(-ENODEV);
+       }
+
+       if (!acpi_device_dir(device)) {
+               acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+                       acpi_battery_dir);
+               if (!acpi_device_dir(device))
+                       return_VALUE(-ENODEV);
+       }
+
+       /* 'info' [R] */
+       entry = create_proc_entry(ACPI_BATTERY_FILE_INFO,
+               S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_BATTERY_FILE_INFO));
+       else {
+               entry->read_proc = acpi_battery_read_info;
+               entry->data = acpi_driver_data(device);
+       }
+
+       /* 'status' [R] */
+       entry = create_proc_entry(ACPI_BATTERY_FILE_STATUS,
+               S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_BATTERY_FILE_STATUS));
+       else {
+               entry->read_proc = acpi_battery_read_state;
+               entry->data = acpi_driver_data(device);
+       }
+
+       /* 'alarm' [R/W] */
+       entry = create_proc_entry(ACPI_BATTERY_FILE_ALARM,
+               S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_BATTERY_FILE_ALARM));
+       else {
+               entry->read_proc = acpi_battery_read_alarm;
+               entry->write_proc = acpi_battery_write_alarm;
+               entry->data = acpi_driver_data(device);
+       }
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_battery_remove_fs (
+       struct acpi_device      *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_battery_remove_fs");
+
+       if (!acpi_battery_dir)
+               return_VALUE(-ENODEV);
+
+       if (acpi_device_dir(device))
+               remove_proc_entry(acpi_device_bid(device), acpi_battery_dir);
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                                 Driver Interface
+   -------------------------------------------------------------------------- */
+
+static void
+acpi_battery_notify (
+       acpi_handle             handle,
+       u32                     event,
+       void                    *data)
+{
+       struct acpi_battery     *battery = (struct acpi_battery *) data;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_battery_notify");
+
+       if (!battery)
+               return_VOID;
+
+       if (0 != acpi_bus_get_device(handle, &device))
+               return_VOID;
+
+       switch (event) {
+       case ACPI_BATTERY_NOTIFY_STATUS:
+       case ACPI_BATTERY_NOTIFY_INFO:
+               acpi_battery_check(battery);
+               acpi_bus_generate_event(device, event, battery->flags.present);
+               break;
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Unsupported event [0x%x]\n", event));
+               break;
+       }
+
+       return_VOID;
+}
+
+
+static int
+acpi_battery_add (
+       struct acpi_device      *device)
+{
+       int                     result = 0;
+       acpi_status             status = 0;
+       struct acpi_battery     *battery = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_battery_add");
+       
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       battery = kmalloc(sizeof(struct acpi_battery), GFP_KERNEL);
+       if (!battery)
+               return_VALUE(-ENOMEM);
+       memset(battery, 0, sizeof(struct acpi_battery));
+
+       battery->handle = device->handle;
+       sprintf(acpi_device_name(device), "%s", ACPI_BATTERY_DEVICE_NAME);
+       sprintf(acpi_device_class(device), "%s", ACPI_BATTERY_CLASS);
+       acpi_driver_data(device) = battery;
+
+       result = acpi_battery_check(battery);
+       if (0 != result)
+               goto end;
+
+       result = acpi_battery_add_fs(device);
+       if (0 != result)
+               goto end;
+
+       status = acpi_install_notify_handler(battery->handle,
+               ACPI_DEVICE_NOTIFY, acpi_battery_notify, battery);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error installing notify handler\n"));
+               result = -ENODEV;
+               goto end;
+       }
+
+       printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
+               ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
+               device->status.battery_present?"present":"absent");
+               
+end:
+       if (0 != result) {
+               acpi_battery_remove_fs(device);
+               kfree(battery);
+       }
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_battery_remove (
+       struct acpi_device      *device,
+       int                     type)
+{
+       acpi_status             status = 0;
+       struct acpi_battery     *battery = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_battery_remove");
+
+       if (!device || !acpi_driver_data(device))
+               return_VALUE(-EINVAL);
+
+       battery = (struct acpi_battery *) acpi_driver_data(device);
+
+       status = acpi_remove_notify_handler(battery->handle,
+               ACPI_DEVICE_NOTIFY, acpi_battery_notify);
+       if (ACPI_FAILURE(status))
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error removing notify handler\n"));
+
+       acpi_battery_remove_fs(device);
+
+       kfree(battery);
+
+       return_VALUE(0);
+}
+
+
+static int __init
+acpi_battery_init (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_battery_init");
+
+       result = acpi_bus_register_driver(&acpi_battery_driver);
+       if (0 > result) {
+               remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir);
+               return_VALUE(-ENODEV);
+       }
+
+       return_VALUE(0);
+}
+
+
+static void __exit
+acpi_battery_exit (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_battery_exit");
+
+       result = acpi_bus_unregister_driver(&acpi_battery_driver);
+       if (0 == result)
+               remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir);
+
+       return_VOID;
+}
+
+
+module_init(acpi_battery_init);
+module_exit(acpi_battery_exit);
diff --git a/drivers/acpi/acpi_bus.c b/drivers/acpi/acpi_bus.c
new file mode 100644 (file)
index 0000000..c900ce7
--- /dev/null
@@ -0,0 +1,2145 @@
+/*
+ *  acpi_bus.c - ACPI Bus Driver ($Revision: 56 $)
+ *
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/ioport.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/proc_fs.h>
+#include "acpi_bus.h"
+#include "acpi_drivers.h"
+#include "include/acinterp.h"  /* for acpi_ex_eisa_id_to_string() */
+
+
+#define _COMPONENT             ACPI_BUS_COMPONENT
+ACPI_MODULE_NAME               ("acpi_bus")
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION(ACPI_BUS_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+#define        PREFIX                  "ACPI: "
+
+FADT_DESCRIPTOR                        acpi_fadt;
+static u8                      acpi_disabled = 0;
+struct acpi_device             *acpi_root = NULL;
+struct proc_dir_entry          *acpi_root_dir = NULL;
+
+#define STRUCT_TO_INT(s)       (*((int*)&s))
+
+/*
+ * POLICY: If *anything* doesn't work, put it on the blacklist.
+ *        If they are critical errors, mark it critical, and abort driver load.
+ */
+static struct acpi_blacklist_item acpi_blacklist[] __initdata =
+{
+       /* Portege 7020, BIOS 8.10 */
+       {"TOSHIB", "7020CT  ", 0x19991112, ACPI_TABLE_DSDT, all_versions, "Implicit Return", 0},
+       /* Portege 4030 */
+       {"TOSHIB", "4030    ", 0x19991112, ACPI_TABLE_DSDT, all_versions, "Implicit Return", 0},
+       /* Portege 310/320, BIOS 7.1 */
+       {"TOSHIB", "310     ", 0x19990511, ACPI_TABLE_DSDT, all_versions, "Implicit Return", 0},
+       /* Seattle 2, old bios rev. */
+       {"INTEL ", "440BX   ", 0x00001000, ACPI_TABLE_DSDT, less_than_or_equal, "Field beyond end of region", 0},
+       /* ASUS K7M */
+       {"ASUS  ", "K7M     ", 0x00001000, ACPI_TABLE_DSDT, less_than_or_equal, "Field beyond end of region", 0},
+       /* Intel 810 Motherboard? */
+       {"MNTRAL", "MO81010A", 0x00000012, ACPI_TABLE_DSDT, less_than_or_equal, "Field beyond end of region", 0},
+       /* Compaq Presario 1700 */
+       {"PTLTD ", "  DSDT  ", 0x06040000, ACPI_TABLE_DSDT, less_than_or_equal, "Multiple problems", 1},
+       /* Sony FX120, FX140, FX150? */
+       {"SONY  ", "U0      ", 0x20010313, ACPI_TABLE_DSDT, less_than_or_equal, "ACPI driver problem", 1},
+       /* Compaq Presario 800, Insyde BIOS */
+       {"INT440", "SYSFexxx", 0x00001001, ACPI_TABLE_DSDT, less_than_or_equal, "Does not use _REG to protect EC OpRegions", 1},
+       {""}
+};
+
+
+/* --------------------------------------------------------------------------
+                          Linux Driver Model (LDM) Support
+   -------------------------------------------------------------------------- */
+
+#ifdef CONFIG_LDM
+
+static int acpi_device_probe(struct device *dev);
+static int acpi_device_remove(struct device *dev, u32 flags);
+static int acpi_device_suspend(struct device *dev, u32 state, u32 stage);
+static int acpi_device_resume(struct device *dev, u32 stage);
+
+static struct device_driver acpi_bus_driver = {
+       probe: acpi_device_probe,
+       remove: acpi_device_remove,     
+       suspend: acpi_device_suspend,
+       resume: acpi_device_resume,
+};
+
+
+static int
+acpi_device_probe (
+       struct device           *dev)
+{
+       ACPI_FUNCTION_TRACE("acpi_device_probe");
+
+       if (!dev)
+               return_VALUE(-EINVAL);
+
+       /* TBD */
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_device_remove (
+       struct device           *dev,
+       u32                     flags)
+{
+       ACPI_FUNCTION_TRACE("acpi_device_remove");
+
+       if (!dev)
+               return_VALUE(-EINVAL);
+
+       /* TBD */
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_device_suspend (
+       struct device           *dev,
+       u32                     state,
+       u32                     stage)
+{
+       ACPI_FUNCTION_TRACE("acpi_device_suspend");
+
+       if (!dev)
+               return_VALUE(-EINVAL);
+
+       /* TBD */
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_device_resume (
+       struct device           *dev,
+       u32                     stage)
+{
+       ACPI_FUNCTION_TRACE("acpi_device_resume");
+
+       if (!dev)
+               return_VALUE(-EINVAL);
+
+       /* TBD */
+
+       return_VALUE(0);
+}
+
+#if 0 /* not used ATM */
+static int
+acpi_platform_add (
+       struct device           *dev)
+{
+       ACPI_FUNCTION_TRACE("acpi_platform_add");
+
+       if (!dev)
+               return -EINVAL;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device %s (%s) added\n",
+               dev->name, dev->bus_id));
+
+       /* TBD */
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_platform_remove (
+       struct device           *dev)
+{
+       ACPI_FUNCTION_TRACE("acpi_platform_add");
+
+       if (!dev)
+               return -EINVAL;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device %s (%s) removed\n",
+               dev->name, dev->bus_id));
+
+       /* TBD */
+
+       return_VALUE(0);
+}
+#endif /* unused */
+
+
+#endif /*CONFIG_LDM*/
+
+
+static int
+acpi_device_register (
+       struct acpi_device      *device,
+       struct acpi_device      *parent)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_device_register");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+#ifdef CONFIG_LDM
+       sprintf(device->dev.name, "ACPI device %s:%s", 
+               device->pnp.hardware_id, device->pnp.unique_id);
+       strncpy(device->dev.bus_id, device->pnp.bus_id, sizeof(acpi_bus_id));
+       if (parent)
+               device->dev.parent = &parent->dev;
+       device->dev.driver = &acpi_bus_driver;
+
+       result = device_register(&device->dev);
+#endif /*CONFIG_LDM*/
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_device_unregister (
+       struct acpi_device      *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_device_unregister");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+#ifdef CONFIG_LDM
+       put_device(&device->dev);
+#endif /*CONFIG_LDM*/
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                                Device Management
+   -------------------------------------------------------------------------- */
+
+static void
+acpi_bus_data_handler (
+       acpi_handle             handle,
+       u32                     function,
+       void                    *context)
+{
+       ACPI_FUNCTION_TRACE("acpi_bus_data_handler");
+
+       /* TBD */
+
+       return_VOID;
+}
+
+
+static void
+acpi_bus_data_handler_powerf (
+       acpi_handle             handle,
+       u32                     function,
+       void                    *context)
+{
+       ACPI_FUNCTION_TRACE("acpi_bus_data_handler_powerf");
+
+       /* TBD */
+
+       return_VOID;
+}
+
+
+static void
+acpi_bus_data_handler_sleepf (
+       acpi_handle             handle,
+       u32                     function,
+       void                    *context)
+{
+       ACPI_FUNCTION_TRACE("acpi_bus_data_handler_sleepf");
+
+       /* TBD */
+
+       return_VOID;
+}
+
+
+int
+acpi_bus_get_device (
+       acpi_handle             handle,
+       struct acpi_device      **device)
+{
+       acpi_status             status = AE_OK;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_get_device");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       /* TBD: Support fixed-feature devices */
+
+       status = acpi_get_data(handle, acpi_bus_data_handler, (void**) device);
+       if (ACPI_FAILURE(status) || !*device) {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error getting context for object [%p]\n",
+                       handle));
+               return_VALUE(-ENODEV);
+       }
+
+       return_VALUE(0);
+}
+
+int
+acpi_bus_get_status (
+       struct acpi_device      *device)
+{
+       acpi_status             status = AE_OK;
+       unsigned long           sta = 0;
+       
+       ACPI_FUNCTION_TRACE("acpi_bus_get_status");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       /*
+        * Evaluate _STA if present.
+        */
+       if (device->flags.dynamic_status) {
+               status = acpi_evaluate_integer(device->handle, "_STA", NULL, &sta);
+               if (ACPI_FAILURE(status))
+                       return_VALUE(-ENODEV);
+               STRUCT_TO_INT(device->status) = (int) sta;
+       }
+
+       /*
+        * Otherwise we assume the status of our parent (unless we don't
+        * have one, in which case status is implied).
+        */
+       else if (device->parent)
+               device->status = device->parent->status;
+       else
+               STRUCT_TO_INT(device->status) = 0x0F;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]\n", 
+               device->pnp.bus_id, (u32) STRUCT_TO_INT(device->status)));
+
+       return_VALUE(0);
+}
+
+
+/*
+static int
+acpi_bus_create_device_fs (struct device *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_bus_create_device_fs");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       if (device->dir.entry)
+               return_VALUE(-EEXIST);
+
+       if (!device->parent)
+               device->dir.entry = proc_mkdir(device->pnp.bus_id, NULL);
+       else
+               device->dir.entry = proc_mkdir(device->pnp.bus_id,
+                       device->parent->fs.entry);
+
+       if (!device->dir.entry) {
+               printk(KERN_ERR PREFIX "Unable to create fs entry '%s'\n",
+                       device->pnp.bus_id);
+               return_VALUE(-ENODEV);
+       }
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_bus_remove_device_fs (struct device *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_bus_create_device_fs");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       if (!device->dir.entry)
+               return_VALUE(-ENODEV);
+
+       if (!device->parent)
+               remove_proc_entry(device->pnp_bus_id, NULL);
+       else
+               remove_proc_entry(device->pnp.bus_id, device->parent->fs.entry);
+
+       device->dir.entry = NULL;
+
+       return_VALUE(0);
+}
+*/
+
+
+/* --------------------------------------------------------------------------
+                                 Power Management
+   -------------------------------------------------------------------------- */
+
+int
+acpi_bus_get_power (
+       acpi_handle             handle,
+       int                     *state)
+{
+       int                     result = 0;
+       acpi_status             status = 0;
+       struct acpi_device      *device = NULL;
+       unsigned long           psc = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_get_power");
+
+       result = acpi_bus_get_device(handle, &device);
+       if (0 != result)
+               return_VALUE(result);
+
+       *state = ACPI_STATE_UNKNOWN;
+
+       if (!device->flags.power_manageable) {
+               /* TBD: Non-recursive algorithm for walking up hierarchy */
+               if (device->parent)
+                       *state = device->parent->power.state;
+               else
+                       *state = ACPI_STATE_D0;
+       }
+       else {
+               /*
+                * Get the device's power state either directly (via _PSC) or 
+                * indirectly (via power resources).
+                */
+               if (device->power.flags.explicit_get) {
+                       status = acpi_evaluate_integer(device->handle, "_PSC", 
+                               NULL, &psc);
+                       if (ACPI_FAILURE(status))
+                               return_VALUE(-ENODEV);
+                       device->power.state = (int) psc;
+               }
+               else if (device->power.flags.power_resources) {
+                       result = acpi_power_get_inferred_state(device);
+                       if (0 != result)
+                               return_VALUE(result);
+               }
+
+               *state = device->power.state;
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n",
+               device->pnp.bus_id, device->power.state));
+
+       return_VALUE(0);
+}
+
+
+int
+acpi_bus_set_power (
+       acpi_handle             handle,
+       int                     state)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_device      *device = NULL;
+       char                    object_name[5] = {'_','P','S','0'+state,'\0'};
+
+       ACPI_FUNCTION_TRACE("acpi_bus_set_power");
+
+       result = acpi_bus_get_device(handle, &device);
+       if (0 != result)
+               return_VALUE(result);
+
+       if ((state < ACPI_STATE_D0) || (state > ACPI_STATE_D3))
+               return_VALUE(-EINVAL);
+
+       /* Make sure this is a valid target state */
+
+       if (!device->flags.power_manageable) {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Device is not power manageable\n"));
+               return_VALUE(-ENODEV);
+       }
+       if (state == device->power.state) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", state));
+               return_VALUE(0);
+       }
+       if (!device->power.states[state].flags.valid) {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Device does not support D%d\n", state));
+               return_VALUE(-ENODEV);
+       }
+       if (device->parent && (state < device->parent->power.state)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Cannot set device to a higher-powered state than parent\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       /*
+        * Transition Power
+        * ----------------
+        * On transitions to a high-powered state we first apply power (via
+        * power resources) then evalute _PSx.  Conversly for transitions to
+        * a lower-powered state.
+        */ 
+       if (state < device->power.state) {
+               if (device->power.flags.power_resources) {
+                       result = acpi_power_transition(device, state);
+                       if (0 != result)
+                               goto end;
+               }
+               if (device->power.states[state].flags.explicit_set) {
+                       status = acpi_evaluate_object(device->handle, 
+                               object_name, NULL, NULL);
+                       if (ACPI_FAILURE(status)) {
+                               result = -ENODEV;
+                               goto end;
+                       }
+               }
+       }
+       else {
+               if (device->power.states[state].flags.explicit_set) {
+                       status = acpi_evaluate_object(device->handle, 
+                               object_name, NULL, NULL);
+                       if (ACPI_FAILURE(status)) {
+                               result = -ENODEV;
+                               goto end;
+                       }
+               }
+               if (device->power.flags.power_resources) {
+                       result = acpi_power_transition(device, state);
+                       if (0 != result)
+                               goto end;
+               }
+       }
+
+end:
+       if (0 != result)
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error transitioning device [%s] to D%d\n",
+                       device->pnp.bus_id, state));
+       else
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] transitioned to D%d\n",
+                       device->pnp.bus_id, state));
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_bus_get_power_flags (
+       struct acpi_device      *device)
+{
+       acpi_status             status = 0;
+       acpi_handle             handle = 0;
+       u32                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_get_power_flags");
+
+       if (!device)
+               return -ENODEV;
+
+       /*
+        * Power Management Flags
+        */
+       status = acpi_get_handle(device->handle, "_PSC", &handle);
+       if (ACPI_SUCCESS(status))
+               device->power.flags.explicit_get = 1;
+       status = acpi_get_handle(device->handle, "_IRC", &handle);
+       if (ACPI_SUCCESS(status))
+               device->power.flags.inrush_current = 1;
+       status = acpi_get_handle(device->handle, "_PRW", &handle);
+       if (ACPI_SUCCESS(status))
+               device->power.flags.wake_capable = 1;
+
+       /*
+        * Enumerate supported power management states
+        */
+       for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3; i++) {
+               struct acpi_device_power_state *ps = &device->power.states[i];
+               char            object_name[5] = {'_','P','R','0'+i,'\0'};
+
+               /* Evaluate "_PRx" to se if power resources are referenced */
+               acpi_evaluate_reference(device->handle, object_name, NULL,
+                       &ps->resources);
+               if (ps->resources.count) {
+                       device->power.flags.power_resources = 1;
+                       ps->flags.valid = 1;
+               }
+
+               /* Evaluate "_PSx" to see if we can do explicit sets */
+               object_name[2] = 'S';
+               status = acpi_get_handle(device->handle, object_name, &handle);
+               if (ACPI_SUCCESS(status)) {
+                       ps->flags.explicit_set = 1;
+                       ps->flags.valid = 1;
+               }
+
+               /* State is valid if we have some power control */
+               if (ps->resources.count || ps->flags.explicit_set)
+                       ps->flags.valid = 1;
+
+               ps->power = -1;         /* Unknown - driver assigned */
+               ps->latency = -1;       /* Unknown - driver assigned */
+       }
+
+       /* Set defaults for D0 and D3 states (always valid) */
+       device->power.states[ACPI_STATE_D0].flags.valid = 1;
+       device->power.states[ACPI_STATE_D0].power = 100;
+       device->power.states[ACPI_STATE_D3].flags.valid = 1;
+       device->power.states[ACPI_STATE_D3].power = 0;
+
+       /*
+        * System Power States
+        * -------------------
+        */
+       /* TBD: S1-S4 power state support and resource requirements. */
+       /*
+       for (i=ACPI_STATE_S1; i<ACPI_STATE_S5; i++) {
+               char name[5] = {'_','S',('0'+i),'D','\0'};
+               status = acpi_evaluate_integer(device->handle, name, NULL,
+                       &state);
+               if (ACPI_FAILURE(status))
+                       continue;
+       }
+       */
+
+       /* TBD: System wake support and resource requirements. */
+
+       device->power.state = ACPI_STATE_UNKNOWN;
+
+       return 0;
+}
+
+
+/* --------------------------------------------------------------------------
+                              Performance Management
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_bus_get_perf_flags (
+       struct acpi_device      *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_bus_get_perf_flags");
+
+       if (!device)
+               return -ENODEV;
+
+       device->performance.state = ACPI_STATE_UNKNOWN;
+
+       return 0;
+}
+
+
+/* --------------------------------------------------------------------------
+                                Event Management
+   -------------------------------------------------------------------------- */
+
+static spinlock_t              acpi_bus_event_lock = SPIN_LOCK_UNLOCKED;
+
+LIST_HEAD(acpi_bus_event_list);
+DECLARE_WAIT_QUEUE_HEAD(acpi_bus_event_queue);
+
+extern int                     event_is_open;
+
+int
+acpi_bus_generate_event (
+       struct acpi_device      *device,
+       u8                      type,
+       int                     data)
+{
+       struct acpi_bus_event   *event = NULL;
+       u32                     flags = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_generate_event");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       /* drop event on the floor if no one's listening */
+       if (!event_is_open)
+               return_VALUE(0);
+
+       event = kmalloc(sizeof(struct acpi_bus_event), GFP_KERNEL);
+       if (!event)
+               return_VALUE(-ENOMEM);
+
+       sprintf(event->device_class, "%s", device->pnp.device_class);
+       sprintf(event->bus_id, "%s", device->pnp.bus_id);
+       event->type = type;
+       event->data = data;
+
+       spin_lock_irqsave(&acpi_bus_event_lock, flags);
+       list_add_tail(&event->node, &acpi_bus_event_list);
+       spin_unlock_irqrestore(&acpi_bus_event_lock, flags);
+
+       wake_up_interruptible(&acpi_bus_event_queue);
+
+       return_VALUE(0);
+}
+
+int
+acpi_bus_receive_event (
+       struct acpi_bus_event   *event)
+{
+       u32                     flags = 0;
+       struct acpi_bus_event   *entry = NULL;
+
+       DECLARE_WAITQUEUE(wait, current);
+
+       ACPI_FUNCTION_TRACE("acpi_bus_receive_event");
+
+       if (!event)
+               return -EINVAL;
+
+       if (list_empty(&acpi_bus_event_list)) {
+
+               set_current_state(TASK_INTERRUPTIBLE);
+               add_wait_queue(&acpi_bus_event_queue, &wait);
+
+               if (list_empty(&acpi_bus_event_list))
+                       schedule();
+
+               remove_wait_queue(&acpi_bus_event_queue, &wait);
+               set_current_state(TASK_RUNNING);
+
+               if (signal_pending(current))
+                       return_VALUE(-ERESTARTSYS);
+       }
+
+       spin_lock_irqsave(&acpi_bus_event_lock, flags);
+       entry = list_entry(acpi_bus_event_list.next, struct acpi_bus_event, node);
+       if (entry)
+               list_del(&entry->node);
+       spin_unlock_irqrestore(&acpi_bus_event_lock, flags);
+
+       if (!entry)
+               return_VALUE(-ENODEV);
+
+       memcpy(event, entry, sizeof(struct acpi_bus_event));
+
+       kfree(entry);
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                               Namespace Management
+   -------------------------------------------------------------------------- */
+
+#define WALK_UP                        0
+#define WALK_DOWN              1
+
+typedef int (*acpi_bus_walk_callback)(struct acpi_device*, int, void*);
+
+#define HAS_CHILDREN(d)                ((d)->children.next != &((d)->children))
+#define HAS_SIBLINGS(d)                (((d)->parent) && ((d)->node.next != &(d)->parent->children))
+#define NODE_TO_DEVICE(n)      (list_entry(n, struct acpi_device, node))
+
+
+/**
+ * acpi_bus_walk
+ * -------------
+ * Used to walk the ACPI Bus's device namespace.  Can walk down (depth-first)
+ * or up.  Able to parse starting at any node in the namespace.  Note that a
+ * callback return value of -ELOOP will terminate the walk.
+ *
+ * @start:     starting point
+ * callback:   function to call for every device encountered while parsing
+ * direction:  direction to parse (up or down)
+ * @data:      context for this search operation
+ */
+static int
+acpi_bus_walk (
+       struct acpi_device      *start, 
+       acpi_bus_walk_callback  callback, 
+       int                     direction, 
+       void                    *data)
+{
+       int                     result = 0;
+       int                     level = 0;
+       struct acpi_device      *device = NULL;
+
+       if (!start || !callback)
+               return -EINVAL;
+
+       device = start;
+
+       /*
+        * Parse Namespace
+        * ---------------
+        * Parse a given subtree (specified by start) in the given direction.
+        * Walking 'up' simply means that we execute the callback on leaf
+        * devices prior to their parents (useful for things like removing
+        * or powering down a subtree).
+        */
+
+       while (device) {
+
+               if (direction == WALK_DOWN)
+                       if (-ELOOP == callback(device, level, data))
+                               break;
+
+               /* Depth First */
+
+               if (HAS_CHILDREN(device)) {
+                       device = NODE_TO_DEVICE(device->children.next);
+                       ++level;
+                       continue;
+               }
+
+               if (direction == WALK_UP)
+                       if (-ELOOP == callback(device, level, data))
+                               break;
+
+               /* Now Breadth */
+
+               if (HAS_SIBLINGS(device)) {
+                       device = NODE_TO_DEVICE(device->node.next);
+                       continue;
+               }
+
+               /* Scope Exhausted - Find Next */
+
+               while ((device = device->parent)) {
+                       --level;
+                       if (HAS_SIBLINGS(device)) {
+                               device = NODE_TO_DEVICE(device->node.next);
+                               break;
+                       }
+               }
+       }
+
+       if ((direction == WALK_UP) && (result == 0))
+               callback(start, level, data);
+
+       return result;
+}
+
+
+/* --------------------------------------------------------------------------
+                             Notification Handling
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_bus_check_device (
+       struct acpi_device      *device,
+       int                     *status_changed)
+{
+       acpi_status             status = 0;
+       struct acpi_device_status old_status;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_check_device");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       if (status_changed)
+               *status_changed = 0;
+
+       old_status = device->status;
+
+       /*
+        * Make sure this device's parent is present before we go about
+        * messing with the device.
+        */
+       if (device->parent && !device->parent->status.present) {
+               device->status = device->parent->status;
+               if (STRUCT_TO_INT(old_status) != STRUCT_TO_INT(device->status)) {
+                       if (status_changed)
+                               *status_changed = 1;
+               }
+               return_VALUE(0);
+       }
+
+       status = acpi_bus_get_status(device);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       if (STRUCT_TO_INT(old_status) == STRUCT_TO_INT(device->status))
+               return_VALUE(0);
+
+       if (status_changed)
+               *status_changed = 1;
+       
+       /*
+        * Device Insertion/Removal
+        */
+       if ((device->status.present) && !(old_status.present)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device insertion detected\n"));
+               /* TBD: Handle device insertion */
+       }
+       else if (!(device->status.present) && (old_status.present)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device removal detected\n"));
+               /* TBD: Handle device removal */
+       }
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_bus_check_scope (
+       struct acpi_device      *device)
+{
+       int                     result = 0;
+       int                     status_changed = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_check_scope");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       /* Status Change? */
+       result = acpi_bus_check_device(device, &status_changed);
+       if (0 != result)
+               return_VALUE(result);
+
+       if (!status_changed)
+               return_VALUE(0);
+
+       /*
+        * TBD: Enumerate child devices within this device's scope and
+        *       run acpi_bus_check_device()'s on them.
+        */
+
+       return_VALUE(0);
+}
+
+
+/**
+ * acpi_bus_notify
+ * ---------------
+ * Callback for all 'system-level' device notifications (values 0x00-0x7F).
+ */
+static void 
+acpi_bus_notify (
+       acpi_handle             handle,
+       u32                     type,
+       void                    *data)
+{
+       int                     result = 0;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_notify");
+
+       if (0 != acpi_bus_get_device(handle, &device))
+               return_VOID;
+
+       switch (type) {
+
+       case ACPI_NOTIFY_BUS_CHECK:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received BUS CHECK notification for device [%s]\n", 
+                       device->pnp.bus_id));
+               result = acpi_bus_check_scope(device);
+               /* 
+                * TBD: We'll need to outsource certain events to non-ACPI
+                *      drivers via the device manager (device.c).
+                */
+               break;
+
+       case ACPI_NOTIFY_DEVICE_CHECK:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received DEVICE CHECK notification for device [%s]\n", 
+                       device->pnp.bus_id));
+               result = acpi_bus_check_device(device, NULL);
+               /* 
+                * TBD: We'll need to outsource certain events to non-ACPI
+                *      drivers via the device manager (device.c).
+                */
+               break;
+
+       case ACPI_NOTIFY_DEVICE_WAKE:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received DEVICE WAKE notification for device [%s]\n", 
+                       device->pnp.bus_id));
+               /* TBD */
+               break;
+
+       case ACPI_NOTIFY_EJECT_REQUEST:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received EJECT REQUEST notification for device [%s]\n", 
+                       device->pnp.bus_id));
+               /* TBD */
+               break;
+
+       case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received DEVICE CHECK LIGHT notification for device [%s]\n", 
+                       device->pnp.bus_id));
+               /* TBD: Exactly what does 'light' mean? */
+               break;
+
+       case ACPI_NOTIFY_FREQUENCY_MISMATCH:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received FREQUENCY MISMATCH notification for device [%s]\n", 
+                       device->pnp.bus_id));
+               /* TBD */
+               break;
+
+       case ACPI_NOTIFY_BUS_MODE_MISMATCH:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received BUS MODE MISMATCH notification for device [%s]\n", 
+                       device->pnp.bus_id));
+               /* TBD */
+               break;
+
+       case ACPI_NOTIFY_POWER_FAULT:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received POWER FAULT notification for device [%s]\n", 
+                       device->pnp.bus_id));
+               /* TBD */
+               break;
+
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received unknown/unsupported notification [%08x]\n", 
+                       type));
+               break;
+       }
+
+       return_VOID;
+}
+
+
+/* --------------------------------------------------------------------------
+                                 Driver Management
+   -------------------------------------------------------------------------- */
+
+static LIST_HEAD(acpi_bus_drivers);
+static DECLARE_MUTEX(acpi_bus_drivers_lock);
+
+
+/**
+ * acpi_bus_match 
+ * --------------
+ * Checks the device's hardware (_HID) or compatible (_CID) ids to see if it
+ * matches the specified driver's criteria.
+ */
+static int
+acpi_bus_match (
+       struct acpi_device      *device,
+       struct acpi_driver      *driver)
+{
+
+       if (!device || !driver)
+               return -EINVAL;
+
+       if (0 != strstr(driver->ids, device->pnp.hardware_id))
+               return 0;
+
+       if (device->flags.compatible_ids) {
+               acpi_status     status = AE_OK;
+               acpi_buffer     buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+               acpi_object     *object = NULL;
+               char            cid[256];
+
+               memset(cid, 0, sizeof(cid));
+
+               status = acpi_evaluate_object(device->handle, "_CID", 
+                       NULL, &buffer);
+               if (ACPI_FAILURE(status) || !buffer.pointer)
+                       return -ENOENT;
+
+               object = (acpi_object *) buffer.pointer;
+
+               switch (object->type) {
+               case ACPI_TYPE_INTEGER:
+                       acpi_ex_eisa_id_to_string((u32) object->integer.value, 
+                               cid);
+                       break;
+               case ACPI_TYPE_STRING:
+                       strncpy(cid, object->string.pointer, sizeof(cid) - 1);
+                       break;
+               case ACPI_TYPE_PACKAGE:
+                       /* TBD: Support CID packages */
+               default:
+                       return -ENOENT;
+               }
+
+               if (0 != strstr(cid, device->pnp.hardware_id))
+                       return 0;
+       }
+
+       return -ENOENT;
+}
+
+
+/**
+ * acpi_bus_driver_init 
+ * --------------------
+ * Used to initialize a device via its device driver.  Called whenever a 
+ * driver is bound to a device.  Invokes the driver's add() and start() ops.
+ */
+static int
+acpi_bus_driver_init (
+       struct acpi_device      *device, 
+       struct acpi_driver      *driver)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_driver_init");
+
+       if (!device || !driver)
+               return_VALUE(-EINVAL);
+
+       if (!driver->ops.add)
+               return_VALUE(-ENOSYS);
+
+       result = driver->ops.add(device);
+       if (0 != result) {
+               device->driver = NULL;
+               acpi_driver_data(device) = NULL;
+               return_VALUE(result);
+       }
+
+       /*
+        * TBD - Configuration Management: Assign resources to device based
+        * upon possible configuration and currently allocated resources.
+        */
+
+       if (driver->ops.start) {
+               result = driver->ops.start(device);
+               if ((0 != result) && (driver->ops.remove))
+                       driver->ops.remove(device, ACPI_BUS_REMOVAL_NORMAL);
+               return_VALUE(result);
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Driver successfully bound to device\n"));
+
+#ifdef CONFIG_LDM
+       /* 
+        * Update the device information (in the global device hierarchy) now
+        * that there's a driver bound to it.
+        */
+       strncpy(device->dev.name, device->pnp.device_name, 
+               sizeof(device->dev.name));
+#endif
+
+       if (driver->ops.scan) {
+               driver->ops.scan(device);
+       }
+
+       return_VALUE(0);
+}
+
+
+/**
+ * acpi_bus_bind 
+ * -------------
+ * Callback for acpi_bus_walk() used to find devices that match a specific 
+ * driver's criteria and bind the driver to the device.
+ */
+static int
+acpi_bus_bind (
+       struct acpi_device      *device, 
+       int                     level, 
+       void                    *data)
+{
+       int                     result = 0;
+       struct acpi_driver      *driver = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_bind");
+
+       if (!device || !data)
+               return_VALUE(-EINVAL);
+
+       driver = (struct acpi_driver *) data;
+
+       if (device->driver)
+               return_VALUE(-EEXIST);
+
+       if (!device->status.present)
+               return_VALUE(-ENODEV);
+
+       result = acpi_bus_match(device, driver);
+       if (0 != result)
+               return_VALUE(result);
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found driver [%s] for device [%s]\n",
+               driver->name, device->pnp.bus_id));
+       
+       result = acpi_bus_driver_init(device, driver);
+       if (0 != result)
+               return_VALUE(result);
+
+       down(&acpi_bus_drivers_lock);
+       ++driver->references;
+       up(&acpi_bus_drivers_lock);
+
+       return_VALUE(0);
+}
+
+
+/**
+ * acpi_bus_unbind 
+ * ---------------
+ * Callback for acpi_bus_walk() used to find devices that match a specific 
+ * driver's criteria and unbind the driver from the device.
+ */
+static int
+acpi_bus_unbind (
+       struct acpi_device      *device, 
+       int                     level, 
+       void                    *data)
+{
+       int                     result = 0;
+       struct acpi_driver      *driver = (struct acpi_driver *) data;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_unbind");
+
+       if (!device || !driver)
+               return_VALUE(-EINVAL);
+
+       if (device->driver != driver)
+               return_VALUE(-ENOENT);
+
+       if (!driver->ops.remove)
+               return_VALUE(-ENOSYS);
+
+       result = driver->ops.remove(device, ACPI_BUS_REMOVAL_NORMAL);
+       if (0 != result)
+               return_VALUE(result);
+
+       device->driver = NULL;
+       acpi_driver_data(device) = NULL;
+
+       down(&acpi_bus_drivers_lock);
+       driver->references--;
+       up(&acpi_bus_drivers_lock);
+
+       return_VALUE(0);
+}
+
+
+/**
+ * acpi_bus_find_driver 
+ * --------------------
+ * Parses the list of registered drivers looking for a driver applicable for
+ * the specified device.
+ */
+static int
+acpi_bus_find_driver (
+       struct acpi_device      *device)
+{
+       int                     result = -ENODEV;
+       struct list_head        *entry = NULL;
+       struct acpi_driver      *driver = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_find_driver");
+
+       if (!device || device->driver)
+               return_VALUE(-EINVAL);
+
+       down(&acpi_bus_drivers_lock);
+
+       list_for_each(entry, &acpi_bus_drivers) {
+
+               driver = list_entry(entry, struct acpi_driver, node);
+
+               if (0 != acpi_bus_match(device, driver))
+                       continue;
+
+               result = acpi_bus_driver_init(device, driver);
+               if (0 == result)
+                       ++driver->references;
+
+               break;
+       }
+
+       up(&acpi_bus_drivers_lock);
+
+       return_VALUE(result);
+}
+
+
+/**
+ * acpi_bus_register_driver 
+ * ------------------------ 
+ * Registers a driver with the ACPI bus.  Searches the namespace for all
+ * devices that match the driver's criteria and binds.
+ */
+int
+acpi_bus_register_driver (
+       struct acpi_driver      *driver)
+{
+       ACPI_FUNCTION_TRACE("acpi_bus_register_driver");
+
+       if (!driver)
+               return_VALUE(-EINVAL);
+
+       down(&acpi_bus_drivers_lock);
+       list_add_tail(&driver->node, &acpi_bus_drivers);
+       up(&acpi_bus_drivers_lock);
+
+       acpi_bus_walk(acpi_root, acpi_bus_bind, 
+               WALK_DOWN, driver);
+
+       return_VALUE(driver->references);
+}
+
+
+/**
+ * acpi_bus_unregister_driver 
+ * --------------------------
+ * Unregisters a driver with the ACPI bus.  Searches the namespace for all
+ * devices that match the driver's criteria and unbinds.
+ */
+int
+acpi_bus_unregister_driver (
+       struct acpi_driver      *driver)
+{
+       ACPI_FUNCTION_TRACE("acpi_bus_unregister_driver");
+
+       if (!driver)
+               return_VALUE(-EINVAL);
+
+       acpi_bus_walk(acpi_root, acpi_bus_unbind, WALK_UP, driver);
+
+       if (driver->references)
+               return_VALUE(driver->references);
+
+       down(&acpi_bus_drivers_lock);
+       list_del(&driver->node);
+       up(&acpi_bus_drivers_lock);
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                                 Device Enumeration
+   -------------------------------------------------------------------------- */
+
+static int 
+acpi_bus_get_flags (
+       struct acpi_device      *device)
+{
+       acpi_status             status = AE_OK;
+       acpi_handle             temp = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_get_flags");
+
+       /* Presence of _STA indicates 'dynamic_status' */
+       status = acpi_get_handle(device->handle, "_STA", &temp);
+       if (ACPI_SUCCESS(status))
+               device->flags.dynamic_status = 1;
+
+       /* Presence of _CID indicates 'compatible_ids' */
+       status = acpi_get_handle(device->handle, "_CID", &temp);
+       if (ACPI_SUCCESS(status))
+               device->flags.compatible_ids = 1;
+
+       /* Presence of _RMV indicates 'removable' */
+       status = acpi_get_handle(device->handle, "_RMV", &temp);
+       if (ACPI_SUCCESS(status))
+               device->flags.removable = 1;
+
+       /* Presence of _EJD|_EJ0 indicates 'ejectable' */
+       status = acpi_get_handle(device->handle, "_EJD", &temp);
+       if (ACPI_SUCCESS(status))
+               device->flags.ejectable = 1;
+       else {
+               status = acpi_get_handle(device->handle, "_EJ0", &temp);
+               if (ACPI_SUCCESS(status))
+                       device->flags.ejectable = 1;
+       }
+
+       /* Presence of _LCK indicates 'lockable' */
+       status = acpi_get_handle(device->handle, "_LCK", &temp);
+       if (ACPI_SUCCESS(status))
+               device->flags.lockable = 1;
+
+       /* Presence of _PS0|_PR0 indicates 'power manageable' */
+       status = acpi_get_handle(device->handle, "_PS0", &temp);
+       if (ACPI_FAILURE(status))
+               status = acpi_get_handle(device->handle, "_PR0", &temp);
+       if (ACPI_SUCCESS(status))
+               device->flags.power_manageable = 1;
+
+       /* TBD: Peformance management */
+
+       return_VALUE(0);
+}
+
+
+static int 
+acpi_bus_add (
+       struct acpi_device      **child,
+       struct acpi_device      *parent,
+       acpi_handle             handle,
+       int                     type)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_device      *device = NULL;
+       char                    bus_id[5] = {'?',0};
+       acpi_buffer             buffer = {sizeof(bus_id), bus_id};
+       acpi_device_info        info;
+       char                    *hid = "?";
+       char                    *uid = "0";
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_add");
+
+       if (!child)
+               return_VALUE(-EINVAL);
+
+       device = kmalloc(sizeof(struct acpi_device), GFP_KERNEL);
+       if (!device) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Memory allocation error\n"));
+               return_VALUE(-ENOMEM);
+       }
+       memset(device, 0, sizeof(struct acpi_device));
+
+       device->handle = handle;
+       device->parent = parent;
+
+       /*
+        * Flags
+        * -----
+        * Get prior to calling acpi_bus_get_status() so we know whether
+        * or not _STA is present.  Note that we only look for object
+        * handles -- cannot evaluate objects until we know the device is
+        * present and properly initialized.
+        */
+       result = acpi_bus_get_flags(device);
+       if (0 != result)
+               goto end;
+
+       /*
+        * Status
+        * ------
+        * See if the device is present.  We always assume that non-Device()
+        * objects (e.g. thermal zones, power resources, processors, etc.) are
+        * present, functioning, etc. (at least when parent object is present).
+        * Note that _STA has a different meaning for some objects (e.g.
+        * power resources) so we need to be careful how we use it.
+        */
+       switch (type) {
+       case ACPI_BUS_TYPE_DEVICE:
+               result = acpi_bus_get_status(device);
+               if (0 != result)
+                       goto end;
+               break;
+       default:
+               STRUCT_TO_INT(device->status) = 0x0F;
+               break;
+       }
+       if (!device->status.present) {
+               result = -ENOENT;
+               goto end;
+       }
+
+       /*
+        * Initialize Device
+        * -----------------
+        * TBD: Synch with Core's enumeration/initialization process.
+        */
+
+       /*
+        * Bus ID
+        * ------
+        * The device's Bus ID is simply the object name.
+        * TBD: Shouldn't this value be unique (within the ACPI namespace)?
+        */
+       switch (type) {
+       case ACPI_BUS_TYPE_SYSTEM:
+               sprintf(device->pnp.bus_id, "%s", "root");
+               break;
+       case ACPI_BUS_TYPE_POWER_BUTTON:
+               sprintf(device->pnp.bus_id, "%s", "PWRF");
+               break;
+       case ACPI_BUS_TYPE_SLEEP_BUTTON:
+               sprintf(device->pnp.bus_id, "%s", "SLPF");
+               break;
+       default:
+               acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
+               /* Clean up trailing underscores (if any) */
+               for (i = 3; i > 1; i--) {
+                       if (bus_id[i] == '_')
+                               bus_id[i] = '\0';
+                       else
+                               break;
+               }
+               sprintf(device->pnp.bus_id, "%s", bus_id);
+               break;
+       }
+
+       /*
+        * Hardware ID, Unique ID, & Bus Address
+        * -------------------------------------
+        */
+       switch (type) {
+       case ACPI_BUS_TYPE_DEVICE:
+               status = acpi_get_object_info(handle, &info);
+               if (ACPI_FAILURE(status)) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                               "Error reading device info\n"));
+                       result = -ENODEV;
+                       goto end;
+               }
+               /* Clean up info strings (not NULL terminated) */
+               info.hardware_id[sizeof(info.hardware_id)-1] = '\0';
+               info.unique_id[sizeof(info.unique_id)-1] = '\0';
+               if (info.valid & ACPI_VALID_HID)
+                       hid = info.hardware_id;
+               if (info.valid & ACPI_VALID_UID)
+                       uid = info.unique_id;
+               if (info.valid & ACPI_VALID_ADR)
+                       device->pnp.bus_address = info.address;
+               break;
+       case ACPI_BUS_TYPE_POWER:
+               hid = ACPI_POWER_HID;
+               break;
+       case ACPI_BUS_TYPE_PROCESSOR:
+               hid = ACPI_PROCESSOR_HID;
+               break;
+       case ACPI_BUS_TYPE_SYSTEM:
+               hid = ACPI_SYSTEM_HID;
+               break;
+       case ACPI_BUS_TYPE_THERMAL:
+               hid = ACPI_THERMAL_HID;
+               break;
+       case ACPI_BUS_TYPE_POWER_BUTTON:
+               hid = ACPI_BUTTON_HID_POWERF;
+               break;
+       case ACPI_BUS_TYPE_SLEEP_BUTTON:
+               hid = ACPI_BUTTON_HID_SLEEPF;
+               break;
+       }
+
+       sprintf(device->pnp.hardware_id, "%s", hid);
+       sprintf(device->pnp.unique_id, "%s", uid);
+
+       /*
+        * Power Management
+        * ----------------
+        */
+       if (device->flags.power_manageable) {
+               result = acpi_bus_get_power_flags(device);
+               if (0 != result)
+                       goto end;
+       }
+
+       /*
+        * Performance Management
+        * ----------------------
+        */
+       if (device->flags.performance_manageable) {
+               result = acpi_bus_get_perf_flags(device);
+               if (0 != result)
+                       goto end;
+       }
+
+       /*
+        * Context
+        * -------
+        * Attach this 'struct acpi_device' to the ACPI object.  This makes
+        * resolutions from handle->device very efficient.  Note that since we
+        * attach multiple context to the root object (system device plus all
+        * fixed-feature devices) we must use alternate handlers.
+        */
+       switch (type) {
+       case ACPI_BUS_TYPE_POWER_BUTTON:
+               status = acpi_attach_data(device->handle,
+                       acpi_bus_data_handler_powerf, device);
+               break;
+       case ACPI_BUS_TYPE_SLEEP_BUTTON:
+               status = acpi_attach_data(device->handle,
+                       acpi_bus_data_handler_sleepf, device);
+               break;
+       default:
+               status = acpi_attach_data(device->handle,
+                       acpi_bus_data_handler, device);
+               break;
+       }
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error attaching device data\n"));
+               result = -ENODEV;
+               goto end;
+       }
+
+       /*
+        * Linkage
+        * -------
+        * Link this device to its parent and siblings.
+        */
+       INIT_LIST_HEAD(&device->children);
+       if (!device->parent)
+               INIT_LIST_HEAD(&device->node);
+       else
+               list_add_tail(&device->node, &device->parent->children);
+
+       /* 
+        * \_SB
+        * ----
+        * Fix for the system root bus device -- the only root-level device.
+        */
+       if ((type == ACPI_BUS_TYPE_DEVICE) && (parent == ACPI_ROOT_OBJECT)) {
+               sprintf(device->pnp.hardware_id, "%s", ACPI_BUS_HID);
+               sprintf(device->pnp.device_name, "%s", ACPI_BUS_DEVICE_NAME);
+               sprintf(device->pnp.device_class, "%s", ACPI_BUS_CLASS);
+       }
+
+#ifdef CONFIG_ACPI_DEBUG
+       {
+               char            *type_string = NULL;
+               char            *message = NULL;
+               char            name[80] = {'?','\0'};
+               acpi_buffer     buffer = {sizeof(name), name};
+
+               acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+
+               switch (type) {
+               case ACPI_BUS_TYPE_DEVICE:
+                       type_string = "Device";
+                       break;
+               case ACPI_BUS_TYPE_POWER:
+                       type_string = "Power Resource";
+                       break;
+               case ACPI_BUS_TYPE_PROCESSOR:
+                       type_string = "Processor";
+                       break;
+               case ACPI_BUS_TYPE_SYSTEM:
+                       type_string = "System";
+                       break;
+               case ACPI_BUS_TYPE_THERMAL:
+                       type_string = "Thermal Zone";
+                       break;
+               case ACPI_BUS_TYPE_POWER_BUTTON:
+                       type_string = "Power Button";
+                       sprintf(name, "PWRB");
+                       break;
+               case ACPI_BUS_TYPE_SLEEP_BUTTON:
+                       type_string = "Sleep Button";
+                       sprintf(name, "SLPB");
+                       break;
+               }
+
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %s %s [%p]\n", 
+                       type_string, name, handle));
+       }
+#endif
+
+       /*
+        * Global Device Hierarchy:
+        * ------------------------
+        * Register this device with the global device hierarchy.
+        */
+       acpi_device_register(device, parent);
+
+       /*
+        * Find Driver
+        * -----------
+        * Check to see if there's a driver installed for this kind of device.
+        */
+       acpi_bus_find_driver(device);
+
+end:
+
+       if (0 != result) {
+               kfree(device);
+               return_VALUE(result);
+       }
+
+       *child = device;
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_bus_remove (
+       struct acpi_device      *device, 
+       int                     type)
+{
+       ACPI_FUNCTION_TRACE("acpi_bus_remove");
+
+       if (!device)
+               return_VALUE(-ENODEV);
+
+       acpi_device_unregister(device);
+
+       kfree(device);
+
+       return_VALUE(0);
+}
+
+
+int
+acpi_bus_scan (
+       struct acpi_device      *start)
+{
+       acpi_status             status = AE_OK;
+       struct acpi_device      *parent = NULL;
+       struct acpi_device      *child = NULL;
+       acpi_handle             phandle = 0;
+       acpi_handle             chandle = 0;
+       acpi_object_type        type = 0;
+       u32                     level = 1;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_scan");
+
+       if (!start)
+               return_VALUE(-EINVAL);
+
+       parent = start;
+       phandle = start->handle;
+       
+       /*
+        * Parse through the ACPI namespace, identify all 'devices', and
+        * create a new 'struct acpi_device' for each.
+        */
+       while ((level > 0) && parent) {
+
+               status = acpi_get_next_object(ACPI_TYPE_ANY, phandle,
+                       chandle, &chandle);
+
+               /*
+                * If this scope is exhausted then move our way back up.
+                */
+               if (ACPI_FAILURE(status)) {
+                       level--;
+                       chandle = phandle;
+                       acpi_get_parent(phandle, &phandle);
+                       if (parent->parent)
+                               parent = parent->parent;
+                       continue;
+               }
+
+               status = acpi_get_type(chandle, &type);
+               if (ACPI_FAILURE(status))
+                       continue;
+
+               /*
+                * If this is a scope object then parse it (depth-first).
+                */
+               if (type == ACPI_TYPE_ANY) {
+                       /* Hack to get around scope identity problem */
+                       status = acpi_get_next_object(ACPI_TYPE_ANY, chandle, 0, NULL);
+                       if (ACPI_SUCCESS(status)) {
+                               level++;
+                               phandle = chandle;
+                               chandle = 0;
+                       }
+                       continue;
+               }
+
+               /*
+                * We're only interested in objects that we consider 'devices'.
+                */
+               switch (type) {
+               case ACPI_TYPE_DEVICE:
+                       type = ACPI_BUS_TYPE_DEVICE;
+                       break;
+               case ACPI_TYPE_PROCESSOR:
+                       type = ACPI_BUS_TYPE_PROCESSOR;
+                       break;
+               case ACPI_TYPE_THERMAL:
+                       type = ACPI_BUS_TYPE_THERMAL;
+                       break;
+               case ACPI_TYPE_POWER:
+                       type = ACPI_BUS_TYPE_POWER;
+                       break;
+               default:
+                       continue;
+               }
+
+               status = acpi_bus_add(&child, parent, chandle, type);
+               if (ACPI_FAILURE(status))
+                       continue;
+
+               /*
+                * If the device is present, enabled, and functioning then
+                * parse its scope (depth-first).  Note that we need to
+                * represent absent devices to facilitate PnP notifications
+                * -- but we only the subtree head (not all of its children,
+                * which will be enumerated when the parent is inserted).
+                *
+                * TBD: Need notifications and other detection mechanisms
+                *      in place before we can fully implement this.
+                *
+                * if (STRUCT_TO_INT(child->status) & 0x0B) {
+                */
+               if (child->status.present) {
+                       status = acpi_get_next_object(ACPI_TYPE_ANY, chandle,
+                               0, NULL);
+                       if (ACPI_SUCCESS(status)) {
+                               level++;
+                               phandle = chandle;
+                               chandle = 0;
+                               parent = child;
+                       }
+               }
+       }
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_bus_scan_fixed (
+       struct acpi_device      *root)
+{
+       int                     result = 0;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_scan");
+
+       if (!root)
+               return_VALUE(-ENODEV);
+
+       /*
+        * Enumerate all fixed-feature devices.
+        */
+       if (acpi_fadt.pwr_button == 0)
+               result = acpi_bus_add(&device, acpi_root, 
+                       ACPI_ROOT_OBJECT, ACPI_BUS_TYPE_POWER_BUTTON);
+
+       if (acpi_fadt.sleep_button == 0)
+               result = acpi_bus_add(&device, acpi_root, 
+                       ACPI_ROOT_OBJECT, ACPI_BUS_TYPE_SLEEP_BUTTON);
+
+       return_VALUE(result);
+}
+
+
+/* --------------------------------------------------------------------------
+                             Initialization/Cleanup
+   -------------------------------------------------------------------------- */
+
+int __init
+acpi_blacklisted(void)
+{
+       int i = 0;
+       int blacklisted = 0;
+       acpi_table_header table_header;
+
+       while (acpi_blacklist[i].oem_id[0] != '\0')
+       {
+               if (!ACPI_SUCCESS(acpi_get_table_header(acpi_blacklist[i].table, 1, &table_header))) {
+                       i++;
+                       continue;
+               }
+
+               if (strncmp(acpi_blacklist[i].oem_id, table_header.oem_id, 6)) {
+                       i++;
+                       continue;
+               }
+
+               if (strncmp(acpi_blacklist[i].oem_table_id, table_header.oem_table_id, 8)) {
+                       i++;
+                       continue;
+               }
+
+               if ((acpi_blacklist[i].oem_revision_predicate == all_versions)
+                   || (acpi_blacklist[i].oem_revision_predicate == less_than_or_equal
+                       && table_header.oem_revision <= acpi_blacklist[i].oem_revision)
+                   || (acpi_blacklist[i].oem_revision_predicate == greater_than_or_equal
+                       && table_header.oem_revision >= acpi_blacklist[i].oem_revision)
+                   || (acpi_blacklist[i].oem_revision_predicate == equal
+                       && table_header.oem_revision == acpi_blacklist[i].oem_revision)) {
+
+                       printk(KERN_ERR PREFIX "Vendor \"%6.6s\" System \"%8.8s\" "
+                               "Revision 0x%x has a known ACPI BIOS problem.\n",
+                               acpi_blacklist[i].oem_id,
+                               acpi_blacklist[i].oem_table_id,
+                               acpi_blacklist[i].oem_revision);
+
+                       printk(KERN_ERR PREFIX "Reason: %s. This is a %s error\n",
+                               acpi_blacklist[i].reason,
+                               (acpi_blacklist[i].is_critical_error ? "non-recoverable" : "recoverable"));
+
+                       blacklisted = acpi_blacklist[i].is_critical_error;
+                       break;
+               }
+               else {
+                       i++;
+               }
+       }
+
+       return blacklisted;
+}
+
+
+static int __init
+acpi_bus_init (void)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       acpi_buffer             buffer = {sizeof(acpi_fadt), &acpi_fadt};
+       int                     progress = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_init");
+
+       /*
+        * [0] Initailize the ACPI Core Subsystem.
+        */
+       status = acpi_initialize_subsystem();
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_ERR PREFIX "Unable to initialize the ACPI Interpreter\n");
+               result = -ENODEV;
+               goto end;
+       }
+
+       progress++;
+
+       /*
+        * [1] Load the ACPI tables.
+        */
+       status = acpi_load_tables();
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_ERR PREFIX "Unable to load the System Description Tables\n");
+               result = -ENODEV;
+               goto end;
+       }
+
+       progress++;
+
+       /*
+        * [2] Check the blacklist
+        */
+       if (acpi_blacklisted()) {
+               result = -ENODEV;
+               goto end;
+       }
+
+       progress++;
+
+       /*
+        * [2] Get a separate copy of the FADT for use by other drivers.
+        */
+       status = acpi_get_table(ACPI_TABLE_FADT, 1, &buffer);
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_ERR PREFIX "Unable to get the FADT\n");
+               result = -ENODEV;
+               goto end;
+       }
+
+       progress++;
+
+       /*
+        * [3] Enable the ACPI Core Subsystem.
+        */
+       status = acpi_enable_subsystem(ACPI_FULL_INITIALIZATION);
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_ERR PREFIX "Unable to start the ACPI Interpreter\n");
+               result = -ENODEV;
+               goto end;
+       }
+
+       printk(KERN_INFO PREFIX "Interpreter enabled\n");
+
+       progress++;
+
+       /*
+        * [4] Register for all standard device notifications.
+        */
+       status = acpi_install_notify_handler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY, &acpi_bus_notify, NULL);
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_ERR PREFIX "Unable to register for device notifications\n");
+               result = -ENODEV;
+               goto end;
+       }
+
+       progress++;
+
+       /*
+        * [5] Create the root device.
+        */
+       result = acpi_bus_add(&acpi_root, NULL, ACPI_ROOT_OBJECT, 
+               ACPI_BUS_TYPE_SYSTEM);
+       if (0 != result)
+               goto end;
+
+       progress++;
+
+       /*
+        * [6] Create the root file system.
+        */
+       acpi_device_dir(acpi_root) = proc_mkdir(ACPI_BUS_FILE_ROOT, NULL);
+       if (!acpi_root) {
+               result = -ENODEV;
+               goto end;
+       }
+       acpi_root_dir = acpi_device_dir(acpi_root);
+
+       progress++;
+
+       /*
+        * [7] Install drivers required for proper enumeration of the
+        *     ACPI namespace.
+        */
+       acpi_system_init();     /* ACPI System */
+       acpi_power_init();      /* ACPI Bus Power Management */
+#ifdef CONFIG_ACPI_EC
+       acpi_ec_init();         /* ACPI Embedded Controller */
+#endif
+#ifdef CONFIG_ACPI_PCI
+       acpi_pci_link_init();   /* ACPI PCI Interrupt Link */
+       acpi_pci_root_init();   /* ACPI PCI Root Bridge */
+#endif
+       progress++;
+
+       /*
+        * [8] Enumerate devices in the ACPI namespace.
+        */
+
+       result = acpi_bus_scan_fixed(acpi_root);
+       if (0 != result)
+               goto end;
+
+       result = acpi_bus_scan(acpi_root);
+       if (0 != result)
+               goto end;
+
+end:
+       if (0 != result) {
+               switch (progress) {
+               case 9:
+               case 8: remove_proc_entry("ACPI", NULL);
+               case 7: acpi_bus_remove(acpi_root, ACPI_BUS_REMOVAL_NORMAL);
+               case 6: acpi_remove_notify_handler(ACPI_ROOT_OBJECT,
+                               ACPI_SYSTEM_NOTIFY, &acpi_bus_notify);
+               case 5:
+               case 4:
+               case 3:
+               case 2: acpi_terminate();
+               case 1:
+               case 0:
+               default: return_VALUE(-ENODEV);
+               }
+       }
+
+       return_VALUE(0);
+}
+
+
+static void __exit
+acpi_bus_exit (void)
+{
+       acpi_status             status = AE_OK;
+
+       ACPI_FUNCTION_TRACE("acpi_bus_exit");
+
+       status = acpi_remove_notify_handler(ACPI_ROOT_OBJECT,
+               ACPI_SYSTEM_NOTIFY, acpi_bus_notify);
+       if (ACPI_FAILURE(status))
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error removing notify handler\n"));
+
+#ifdef CONFIG_ACPI_PCI
+       acpi_pci_root_exit();
+       acpi_pci_link_exit();
+#endif
+#ifdef CONFIG_ACPI_EC
+       acpi_ec_exit();
+#endif
+       acpi_power_exit();
+       acpi_system_exit();
+
+       acpi_bus_remove(acpi_root, ACPI_BUS_REMOVAL_NORMAL);
+
+       remove_proc_entry(ACPI_BUS_FILE_ROOT, NULL);
+
+       status = acpi_terminate();
+       if (ACPI_FAILURE(status))
+               printk(KERN_ERR PREFIX "Unable to terminate the ACPI Interpreter\n");
+       else
+               printk(KERN_ERR PREFIX "Interpreter disabled\n");
+
+       return_VOID;
+}
+
+
+int __init
+acpi_init (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_init");
+
+       printk(KERN_INFO PREFIX "Bus Driver revision %08x\n",
+               ACPI_DRIVER_VERSION);
+       printk(KERN_INFO PREFIX "Core Subsystem revision %08x\n",
+               ACPI_CA_VERSION);
+
+       if (acpi_disabled) {
+               printk(KERN_INFO PREFIX "Disabled via command line (acpi=off)\n");
+               return -ENODEV;
+       }
+
+#ifdef CONFIG_PM
+       if (PM_IS_ACTIVE()) {
+               printk(KERN_INFO PREFIX "APM is already active, exiting\n");
+               return -ENODEV;
+       }
+#endif
+
+       result = acpi_bus_init();
+       if (0 != result)
+               return_VALUE(result);
+
+#ifdef CONFIG_PM
+       pm_active = 1;
+#endif
+
+       return_VALUE(0);
+}
+
+
+void __exit
+acpi_exit (void)
+{
+       ACPI_FUNCTION_TRACE("acpi_exit");
+
+#ifdef CONFIG_PM
+       pm_active = 0;
+#endif
+
+       acpi_bus_exit();
+
+       return_VOID;
+}
+
+
+int __init
+acpi_setup(char *str)
+{
+       while (str && *str) {
+               if (strncmp(str, "off", 3) == 0)
+                       acpi_disabled = 1;
+               str = strchr(str, ',');
+               if (str)
+                       str += strspn(str, ", \t");
+       }
+       return 1;
+}
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+subsys_initcall(acpi_init);
+#endif
+
+__setup("acpi=", acpi_setup);
diff --git a/drivers/acpi/acpi_bus.h b/drivers/acpi/acpi_bus.h
new file mode 100644 (file)
index 0000000..05326a4
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ *  acpi_bus.h - ACPI Bus Driver ($Revision: 17 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef __ACPI_BUS_H__
+#define __ACPI_BUS_H__
+
+#include <linux/version.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,4))
+#include <linux/device.h>
+#define CONFIG_LDM
+#endif
+
+#include "include/acpi.h"
+
+
+/* TBD: Make dynamic */
+#define ACPI_MAX_HANDLES       10
+struct acpi_handle_list {
+       u32                     count;
+       acpi_handle             handles[ACPI_MAX_HANDLES];
+};
+
+
+/* acpi_utils.h */
+acpi_status acpi_extract_package (acpi_object *, acpi_buffer *, acpi_buffer *);
+acpi_status acpi_evaluate (acpi_handle, acpi_string, acpi_object_list *, acpi_buffer *);
+acpi_status acpi_evaluate_integer (acpi_handle, acpi_string, acpi_object_list *, unsigned long *);
+acpi_status acpi_evaluate_reference (acpi_handle, acpi_string, acpi_object_list *, struct acpi_handle_list *);
+
+
+#ifdef CONFIG_ACPI_BUS
+
+#include <linux/proc_fs.h>
+
+#define ACPI_BUS_FILE_ROOT     "acpi"
+
+extern struct proc_dir_entry   *acpi_root_dir;
+
+extern FADT_DESCRIPTOR         acpi_fadt;
+
+enum acpi_bus_removal_type {
+       ACPI_BUS_REMOVAL_NORMAL = 0,
+       ACPI_BUS_REMOVAL_EJECT,
+       ACPI_BUS_REMOVAL_SUPRISE,
+       ACPI_BUS_REMOVAL_TYPE_COUNT
+};
+
+enum acpi_bus_device_type {
+       ACPI_BUS_TYPE_DEVICE    = 0,
+       ACPI_BUS_TYPE_POWER,
+       ACPI_BUS_TYPE_PROCESSOR,
+       ACPI_BUS_TYPE_THERMAL,
+       ACPI_BUS_TYPE_SYSTEM,
+       ACPI_BUS_TYPE_POWER_BUTTON,
+       ACPI_BUS_TYPE_SLEEP_BUTTON,
+       ACPI_BUS_DEVICE_TYPE_COUNT
+};
+
+struct acpi_driver;
+struct acpi_device;
+
+
+/*
+ * ACPI Driver
+ * -----------
+ */
+
+typedef int (*acpi_op_add)     (struct acpi_device *device);
+typedef int (*acpi_op_remove)  (struct acpi_device *device, int type);
+typedef int (*acpi_op_lock)    (struct acpi_device *device, int type);
+typedef int (*acpi_op_start)   (struct acpi_device *device);
+typedef int (*acpi_op_stop)    (struct acpi_device *device, int type);
+typedef int (*acpi_op_suspend) (struct acpi_device *device, int state);
+typedef int (*acpi_op_resume)  (struct acpi_device *device, int state);
+typedef int (*acpi_op_scan)    (struct acpi_device *device);
+
+struct acpi_driver_ops {
+       acpi_op_add             add;
+       acpi_op_remove          remove;
+       acpi_op_lock            lock;
+       acpi_op_start           start;
+       acpi_op_stop            stop;
+       acpi_op_suspend         suspend;
+       acpi_op_resume          resume;
+       acpi_op_scan            scan;
+};
+
+struct acpi_driver {
+       struct list_head        node;
+       char                    name[80];
+       char                    class[80];
+       int                     references;
+       char                    *ids;           /* Supported Hardware IDs */
+       struct acpi_driver_ops  ops;
+};
+
+enum acpi_blacklist_predicates
+{
+       all_versions,
+       less_than_or_equal,
+       equal,
+       greater_than_or_equal,
+};
+
+struct acpi_blacklist_item
+{
+       char            oem_id[7];
+       char            oem_table_id[9];
+       u32             oem_revision;
+       acpi_table_type table;
+       enum acpi_blacklist_predicates oem_revision_predicate;
+       char            *reason;
+       u32             is_critical_error;
+};
+
+
+/*
+ * ACPI Device
+ * -----------
+ */
+
+/* Status (_STA) */
+
+struct acpi_device_status {
+       u32                     present:1;
+       u32                     enabled:1;
+       u32                     show_in_ui:1;
+       u32                     functional:1;
+       u32                     battery_present:1;
+       u32                     reserved:27;
+};
+
+
+/* Flags */
+
+struct acpi_device_flags {
+       u8                      dynamic_status:1;
+       u8                      compatible_ids:1;
+       u8                      removable:1;
+       u8                      ejectable:1;
+       u8                      lockable:1;
+       u8                      suprise_removal_ok:1;
+       u8                      power_manageable:1;
+       u8                      performance_manageable:1;
+};
+
+
+/* File System */
+
+struct acpi_device_dir {
+       struct proc_dir_entry   *entry;
+};
+
+#define acpi_device_dir(d)     ((d)->dir.entry)
+
+
+/* Plug and Play */
+
+typedef char                   acpi_bus_id[5];
+typedef unsigned long          acpi_bus_address;
+typedef char                   acpi_hardware_id[9];
+typedef char                   acpi_unique_id[9];
+typedef char                   acpi_device_name[40];
+typedef char                   acpi_device_class[20];
+
+struct acpi_device_pnp {
+       acpi_bus_id             bus_id;                        /* Object name */
+       acpi_bus_address        bus_address;                          /* _ADR */
+       acpi_hardware_id        hardware_id;                          /* _HID */
+       acpi_unique_id          unique_id;                            /* _UID */
+       acpi_device_name        device_name;             /* Driver-determined */
+       acpi_device_class       device_class;            /*        "          */
+};
+
+#define acpi_device_bid(d)     ((d)->pnp.bus_id)
+#define acpi_device_adr(d)     ((d)->pnp.bus_address)
+#define acpi_device_hid(d)     ((d)->pnp.hardware_id)
+#define acpi_device_uid(d)     ((d)->pnp.unique_id)
+#define acpi_device_name(d)    ((d)->pnp.device_name)
+#define acpi_device_class(d)   ((d)->pnp.device_class)
+
+
+/* Power Management */
+
+struct acpi_device_power_flags {
+       u8                      explicit_get:1;              /* _PSC present? */
+       u8                      power_resources:1;         /* Power resources */
+       u8                      inrush_current:1;         /* Serialize Dx->D0 */
+       u8                      wake_capable:1;          /* Wakeup supported? */
+       u8                      wake_enabled:1;         /* Enabled for wakeup */
+       u8                      power_removed:1;           /* Optimize Dx->D0 */
+       u8                      reserved:2;
+};
+
+struct acpi_device_power_state {
+       struct {
+               u8                      valid:1;        
+               u8                      explicit_set:1;      /* _PSx present? */
+               u8                      reserved:6;
+       }                       flags;
+       int                     power;            /* % Power (compared to D0) */
+       int                     latency;        /* Dx->D0 time (microseconds) */
+       struct acpi_handle_list resources;      /* Power resources referenced */
+};
+
+struct acpi_device_power {
+       int                     state;                       /* Current state */
+       struct acpi_device_power_flags flags;
+       struct acpi_device_power_state states[4];     /* Power states (D0-D3) */
+};
+
+
+/* Performance Management */
+
+struct acpi_device_perf_flags {
+       u8                      reserved:8;
+};
+
+struct acpi_device_perf_state {
+       struct {
+               u8                      valid:1;        
+               u8                      reserved:7;
+       }                       flags;
+       u8                      power;            /* % Power (compared to P0) */
+       u8                      performance;      /* % Performance (    "   ) */
+       int                     latency;        /* Px->P0 time (microseconds) */
+};
+
+struct acpi_device_perf {
+       int                     state;
+       struct acpi_device_perf_flags flags;
+       int                     state_count;
+       struct acpi_device_perf_state *states;
+};
+
+
+/* Device */
+
+struct acpi_device {
+       acpi_handle             handle;
+       struct acpi_device      *parent;
+       struct list_head        children;
+       struct list_head        node;
+       struct acpi_device_status status;
+       struct acpi_device_flags flags;
+       struct acpi_device_pnp  pnp;
+       struct acpi_device_power power;
+       struct acpi_device_perf performance;
+       struct acpi_device_dir  dir;
+       struct acpi_driver      *driver;
+       void                    *driver_data;
+#ifdef CONFIG_LDM
+       struct device           dev;
+#endif
+};
+
+#define acpi_driver_data(d)    ((d)->driver_data)
+
+
+/*
+ * Events
+ * ------
+ */
+
+struct acpi_bus_event {
+       struct list_head        node;
+       acpi_device_class       device_class;
+       acpi_bus_id             bus_id;
+       u32                     type;
+       u32                     data;
+};
+
+
+/*
+ * External Functions
+ */
+
+int acpi_bus_get_device(acpi_handle, struct acpi_device **device);
+int acpi_bus_get_status (struct acpi_device *device);
+int acpi_bus_get_power (acpi_handle handle, int *state);
+int acpi_bus_set_power (acpi_handle handle, int state);
+int acpi_bus_generate_event (struct acpi_device *device, u8 type, int data);
+int acpi_bus_receive_event (struct acpi_bus_event *event);
+int acpi_bus_register_driver (struct acpi_driver *driver);
+int acpi_bus_unregister_driver (struct acpi_driver *driver);
+int acpi_bus_scan (struct acpi_device *device);
+int acpi_init (void);
+void acpi_exit (void);
+
+
+#endif /*CONFIG_ACPI_BUS*/
+
+#endif /*__ACPI_BUS_H__*/
diff --git a/drivers/acpi/acpi_button.c b/drivers/acpi/acpi_button.c
new file mode 100644 (file)
index 0000000..841b316
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ *  acpi_button.c - ACPI Button Driver ($Revision: 22 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include "acpi_bus.h"
+#include "acpi_drivers.h"
+
+
+#define _COMPONENT             ACPI_BUTTON_COMPONENT
+ACPI_MODULE_NAME               ("acpi_button")
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+#define PREFIX                 "ACPI: "
+
+
+int acpi_button_add (struct acpi_device *device);
+int acpi_button_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_button_driver = {
+       name:                   ACPI_BUTTON_DRIVER_NAME,
+       class:                  ACPI_BUTTON_CLASS,
+       ids:                    "ACPI_FPB,ACPI_FSB,PNP0C0D,PNP0C0C,PNP0C0E",
+       ops:                    {
+                                       add:    acpi_button_add,
+                                       remove: acpi_button_remove,
+                               },
+};
+
+struct acpi_button {
+       acpi_handle             handle;
+       struct acpi_device      *device;        /* Fixed button kludge */
+       u8                      type;
+       unsigned long           pushed;
+};
+
+
+/* --------------------------------------------------------------------------
+                              FS Interface (/proc)
+   -------------------------------------------------------------------------- */
+
+#include <linux/compatmac.h>
+#include <linux/proc_fs.h>
+
+struct proc_dir_entry          *acpi_button_dir = NULL;
+
+
+static int
+acpi_button_read_info (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       int                     result = 0;
+       struct acpi_button      *button = (struct acpi_button *) data;
+       char                    *p = page;
+       int                     len = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_button_read_info");
+
+       if (!button || !button->device)
+               goto end;
+
+       p += sprintf(p, "type:                    %s\n", 
+               acpi_device_name(button->device));
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_button_add_fs (
+       struct acpi_device      *device)
+{
+       struct proc_dir_entry   *entry = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_button_add_fs");
+
+       if (!acpi_button_dir) {
+               acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
+               if (!acpi_button_dir)
+                       return_VALUE(-ENODEV);
+       }
+
+       if (!acpi_device_dir(device)) {
+               acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+                       acpi_button_dir);
+               if (!acpi_device_dir(device))
+                       return_VALUE(-ENODEV);
+       }
+
+       /* 'info' [R] */
+       entry = create_proc_entry(ACPI_BUTTON_FILE_INFO,
+               S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_BUTTON_FILE_INFO));
+       else {
+               entry->read_proc = acpi_button_read_info;
+               entry->data = acpi_driver_data(device);
+       }
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_button_remove_fs (
+       struct acpi_device      *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_button_remove_fs");
+
+       if (!acpi_button_dir)
+               return_VALUE(-ENODEV);
+
+       if (acpi_device_dir(device))
+               remove_proc_entry(acpi_device_bid(device), acpi_button_dir);
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                                Driver Interface
+   -------------------------------------------------------------------------- */
+
+void
+acpi_button_notify (
+       acpi_handle             handle,
+       u32                     event,
+       void                    *data)
+{
+       struct acpi_button      *button = (struct acpi_button *) data;
+
+       ACPI_FUNCTION_TRACE("acpi_button_notify");
+
+       if (!button || !button->device)
+               return_VOID;
+
+       switch (event) {
+       case ACPI_BUTTON_NOTIFY_STATUS:
+               acpi_bus_generate_event(button->device, event, ++button->pushed);
+               break;
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Unsupported event [0x%x]\n", event));
+               break;
+       }
+
+       return_VOID;
+}
+
+
+acpi_status
+acpi_button_notify_fixed (
+       void                    *data)
+{
+       struct acpi_button      *button = (struct acpi_button *) data;
+       
+       ACPI_FUNCTION_TRACE("acpi_button_notify_fixed");
+
+       if (!button)
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+       acpi_button_notify(button->handle, ACPI_BUTTON_NOTIFY_STATUS, button);
+
+       return_ACPI_STATUS(AE_OK);
+}
+
+
+int
+acpi_button_add (
+       struct acpi_device      *device)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_button      *button = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_button_add");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       button = kmalloc(sizeof(struct acpi_button), GFP_KERNEL);
+       if (!button)
+               return_VALUE(-ENOMEM);
+       memset(button, 0, sizeof(struct acpi_button));
+
+       button->device = device;
+       button->handle = device->handle;
+       sprintf(acpi_device_class(device), "%s", ACPI_BUTTON_CLASS);
+       acpi_driver_data(device) = button;
+
+       /*
+        * Determine the button type (via hid), as fixed-feature buttons
+        * need to be handled a bit differently than generic-space.
+        */
+       if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) {
+               button->type = ACPI_BUTTON_TYPE_POWERF;
+               sprintf(acpi_device_name(device), "%s",
+                       ACPI_BUTTON_DEVICE_NAME_POWERF);
+       }
+       else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) {
+               button->type = ACPI_BUTTON_TYPE_SLEEPF;
+               sprintf(acpi_device_name(device), "%s",
+                       ACPI_BUTTON_DEVICE_NAME_SLEEPF);
+       }
+       else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) {
+               button->type = ACPI_BUTTON_TYPE_POWER;
+               sprintf(acpi_device_name(device), "%s",
+                       ACPI_BUTTON_DEVICE_NAME_POWER);
+       }
+       else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) {
+               button->type = ACPI_BUTTON_TYPE_SLEEP;
+               sprintf(acpi_device_name(device), "%s",
+                       ACPI_BUTTON_DEVICE_NAME_SLEEP);
+       }
+       else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) {
+               button->type = ACPI_BUTTON_TYPE_LID;
+               sprintf(acpi_device_name(device), "%s",
+                       ACPI_BUTTON_DEVICE_NAME_LID);
+       }
+       else {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported hid [%s]\n",
+                       acpi_device_hid(device)));
+               result = -ENODEV;
+               goto end;
+       }
+
+       result = acpi_button_add_fs(device);
+       if (0 != result)
+               goto end;
+
+       switch (button->type) {
+       case ACPI_BUTTON_TYPE_POWERF:
+               status = acpi_install_fixed_event_handler (
+                       ACPI_EVENT_POWER_BUTTON,
+                       acpi_button_notify_fixed,
+                       button);
+               break;
+       case ACPI_BUTTON_TYPE_SLEEPF:
+               status = acpi_install_fixed_event_handler (
+                       ACPI_EVENT_SLEEP_BUTTON,
+                       acpi_button_notify_fixed,
+                       button);
+               break;
+       default:
+               status = acpi_install_notify_handler (
+                       button->handle,
+                       ACPI_DEVICE_NOTIFY,
+                       acpi_button_notify,
+                       button);
+               break;
+       }
+
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error installing notify handler\n"));
+               result = -ENODEV;
+               goto end;
+       }
+
+       printk(KERN_INFO PREFIX "%s [%s]\n", 
+               acpi_device_name(device), acpi_device_bid(device));
+
+end:
+       if (0 != result) {
+               acpi_button_remove_fs(device);
+               kfree(button);
+       }
+
+       return_VALUE(result);
+}
+
+
+int
+acpi_button_remove (struct acpi_device *device, int type)
+{
+       acpi_status             status = 0;
+       struct acpi_button      *button = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_button_remove");
+
+       if (!device || !acpi_driver_data(device))
+               return_VALUE(-EINVAL);
+
+       button = acpi_driver_data(device);
+
+       /* Unregister for device notifications. */
+       switch (button->type) {
+       case ACPI_BUTTON_TYPE_POWERF:
+               status = acpi_remove_fixed_event_handler(
+                       ACPI_EVENT_POWER_BUTTON, acpi_button_notify_fixed);
+               break;
+       case ACPI_BUTTON_TYPE_SLEEPF:
+               status = acpi_remove_fixed_event_handler(
+                       ACPI_EVENT_SLEEP_BUTTON, acpi_button_notify_fixed);
+               break;
+       default:
+               status = acpi_remove_notify_handler(button->handle,
+                       ACPI_DEVICE_NOTIFY, acpi_button_notify);
+               break;
+       }
+
+       if (ACPI_FAILURE(status))
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error removing notify handler\n"));
+
+       acpi_button_remove_fs(device);  
+
+       kfree(button);
+
+       return_VALUE(0);
+}
+
+
+static int __init
+acpi_button_init (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_button_init");
+
+       result = acpi_bus_register_driver(&acpi_button_driver);
+       if (0 > result)
+               return_VALUE(-ENODEV);
+
+       return_VALUE(0);
+}
+
+
+static void __exit
+acpi_button_exit (void)
+{
+       ACPI_FUNCTION_TRACE("acpi_button_exit");
+
+       acpi_bus_unregister_driver(&acpi_button_driver);
+
+       return_VOID;
+}
+
+
+module_init(acpi_button_init);
+module_exit(acpi_button_exit);
diff --git a/drivers/acpi/acpi_drivers.h b/drivers/acpi/acpi_drivers.h
new file mode 100644 (file)
index 0000000..67a8d5a
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ *  acpi_drivers.h  ($Revision: 17 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef __ACPI_DRIVERS_H__
+#define __ACPI_DRIVERS_H__
+
+#include <linux/acpi.h>
+#include "acpi_bus.h"
+
+
+#define ACPI_DRIVER_VERSION            0x20020308
+#define ACPI_MAX_STRING                        80
+
+
+/* --------------------------------------------------------------------------
+                                    ACPI Bus
+   -------------------------------------------------------------------------- */
+
+#define ACPI_BUS_COMPONENT             0x00010000
+#define ACPI_BUS_CLASS                 "system_bus"
+#define ACPI_BUS_HID                   "ACPI_BUS"
+#define ACPI_BUS_DRIVER_NAME           "ACPI Bus Driver"
+#define ACPI_BUS_DEVICE_NAME           "System Bus"
+
+
+/* --------------------------------------------------------------------------
+                                  AC Adapter
+   -------------------------------------------------------------------------- */
+
+#define ACPI_AC_COMPONENT              0x00020000
+#define ACPI_AC_CLASS                  "ac_adapter"
+#define ACPI_AC_HID                    "ACPI0003"
+#define ACPI_AC_DRIVER_NAME            "ACPI AC Adapter Driver"
+#define ACPI_AC_DEVICE_NAME            "AC Adapter"
+#define ACPI_AC_FILE_STATE             "state"
+#define ACPI_AC_NOTIFY_STATUS          0x80
+#define ACPI_AC_STATUS_OFFLINE         0x00
+#define ACPI_AC_STATUS_ONLINE          0x01
+#define ACPI_AC_STATUS_UNKNOWN         0xFF
+
+
+/* --------------------------------------------------------------------------
+                                     Battery
+   -------------------------------------------------------------------------- */
+
+#define ACPI_BATTERY_COMPONENT         0x00040000
+#define ACPI_BATTERY_CLASS             "battery"
+#define ACPI_BATTERY_HID               "PNP0C0A"
+#define ACPI_BATTERY_DRIVER_NAME       "ACPI Battery Driver"
+#define ACPI_BATTERY_DEVICE_NAME       "Battery"
+#define ACPI_BATTERY_FILE_INFO         "info"
+#define ACPI_BATTERY_FILE_STATUS       "state"
+#define ACPI_BATTERY_FILE_ALARM                "alarm"
+#define ACPI_BATTERY_NOTIFY_STATUS     0x80
+#define ACPI_BATTERY_NOTIFY_INFO       0x81
+#define ACPI_BATTERY_UNITS_WATTS       "mW"
+#define ACPI_BATTERY_UNITS_AMPS                "mA"
+
+
+/* --------------------------------------------------------------------------
+                                      Button
+   -------------------------------------------------------------------------- */
+
+#define ACPI_BUTTON_COMPONENT          0x00080000
+#define ACPI_BUTTON_CLASS              "button"
+#define ACPI_BUTTON_HID_LID            "PNP0C0D"
+#define ACPI_BUTTON_HID_POWER          "PNP0C0C"       
+#define ACPI_BUTTON_HID_POWERF         "ACPI_FPB"
+#define ACPI_BUTTON_HID_SLEEP          "PNP0C0E"
+#define ACPI_BUTTON_HID_SLEEPF         "ACPI_FSB"
+#define ACPI_BUTTON_DRIVER_NAME                "ACPI Button Driver"
+#define ACPI_BUTTON_DEVICE_NAME_POWER  "Power Button"
+#define ACPI_BUTTON_DEVICE_NAME_POWERF "Power Button"
+#define ACPI_BUTTON_DEVICE_NAME_SLEEP  "Sleep Button"
+#define ACPI_BUTTON_DEVICE_NAME_SLEEPF "Sleep Button"
+#define ACPI_BUTTON_DEVICE_NAME_LID    "Lid Switch"
+#define ACPI_BUTTON_FILE_INFO          "info"
+#define ACPI_BUTTON_TYPE_UNKNOWN       0x00
+#define ACPI_BUTTON_TYPE_POWER         0x01
+#define ACPI_BUTTON_TYPE_POWERF                0x02
+#define ACPI_BUTTON_TYPE_SLEEP         0x03
+#define ACPI_BUTTON_TYPE_SLEEPF                0x04
+#define ACPI_BUTTON_TYPE_LID           0x05
+#define ACPI_BUTTON_NOTIFY_STATUS      0x80
+
+
+/* --------------------------------------------------------------------------
+                                Embedded Controller
+   -------------------------------------------------------------------------- */
+
+#define ACPI_EC_COMPONENT              0x00100000
+#define ACPI_EC_CLASS                  "embedded_controller"
+#define ACPI_EC_HID                    "PNP0C09"
+#define ACPI_EC_DRIVER_NAME            "ACPI Embedded Controller Driver"
+#define ACPI_EC_DEVICE_NAME            "Embedded Controller"
+#define ACPI_EC_FILE_INFO              "info"
+
+#ifdef CONFIG_ACPI_EC
+
+int acpi_ec_init (void);
+void acpi_ec_exit (void);
+
+#endif
+
+
+/* --------------------------------------------------------------------------
+                                       Fan
+   -------------------------------------------------------------------------- */
+
+#define ACPI_FAN_COMPONENT             0x00200000
+#define ACPI_FAN_CLASS                 "fan"
+#define ACPI_FAN_HID                   "PNP0C0B"
+#define ACPI_FAN_DRIVER_NAME           "ACPI Fan Driver"
+#define ACPI_FAN_DEVICE_NAME           "Fan"
+#define ACPI_FAN_FILE_STATE            "state"
+#define ACPI_FAN_NOTIFY_STATUS         0x80
+
+
+/* --------------------------------------------------------------------------
+                                       PCI
+   -------------------------------------------------------------------------- */
+
+#define ACPI_PCI_LINK_COMPONENT                0x00400000
+#define ACPI_PCI_LINK_CLASS            "irq_routing"
+#define ACPI_PCI_LINK_HID              "PNP0C0F"
+#define ACPI_PCI_LINK_DRIVER_NAME      "ACPI PCI Interrupt Link Driver"
+#define ACPI_PCI_LINK_DEVICE_NAME      "PCI Interrupt Link"
+#define ACPI_PCI_LINK_FILE_INFO                "info"
+#define ACPI_PCI_LINK_FILE_STATUS      "state"
+
+#define ACPI_PCI_ROOT_COMPONENT                0x00800000
+#define ACPI_PCI_ROOT_CLASS            "bridge"
+#define ACPI_PCI_ROOT_HID              "PNP0A03"
+#define ACPI_PCI_ROOT_DRIVER_NAME      "ACPI PCI Root Bridge Driver"
+#define ACPI_PCI_ROOT_DEVICE_NAME      "PCI Root Bridge"
+
+#define ACPI_PCI_PRT_DEVICE_NAME       "PCI Interrupt Routing Table"
+
+#ifdef CONFIG_ACPI_PCI
+
+
+int acpi_pci_link_get_irq (struct acpi_prt_entry *entry, int *irq);
+int acpi_pci_link_set_irq (struct acpi_prt_entry *entry, int irq);
+int acpi_pci_link_init (void);
+void acpi_pci_link_exit (void);
+
+int acpi_pci_root_init (void);
+void acpi_pci_root_exit (void);
+
+#endif
+
+
+/* --------------------------------------------------------------------------
+                                  Power Resource
+   -------------------------------------------------------------------------- */
+
+#define ACPI_POWER_COMPONENT           0x01000000
+#define ACPI_POWER_CLASS               "power_resource"
+#define ACPI_POWER_HID                 "ACPI_PWR"
+#define ACPI_POWER_DRIVER_NAME         "ACPI Power Resource Driver"
+#define ACPI_POWER_DEVICE_NAME         "Power Resource"
+#define ACPI_POWER_FILE_INFO           "info"
+#define ACPI_POWER_FILE_STATUS         "state"
+#define ACPI_POWER_RESOURCE_STATE_OFF  0x00
+#define ACPI_POWER_RESOURCE_STATE_ON   0x01
+#define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
+
+#ifdef CONFIG_ACPI_POWER
+
+int acpi_power_get_inferred_state (struct acpi_device *device);
+int acpi_power_transition (struct acpi_device *device, int state);
+int acpi_power_init (void);
+void acpi_power_exit (void);
+
+#endif
+
+
+/* --------------------------------------------------------------------------
+                                    Processor
+   -------------------------------------------------------------------------- */
+
+#define ACPI_PROCESSOR_COMPONENT       0x02000000
+#define ACPI_PROCESSOR_CLASS           "processor"
+#define ACPI_PROCESSOR_HID             "ACPI_CPU"
+#define ACPI_PROCESSOR_DRIVER_NAME     "ACPI Processor Driver"
+#define ACPI_PROCESSOR_DEVICE_NAME     "Processor"
+#define ACPI_PROCESSOR_FILE_INFO       "info"
+#define ACPI_PROCESSOR_FILE_POWER      "power"
+#define ACPI_PROCESSOR_FILE_PERFORMANCE        "performance"
+#define ACPI_PROCESSOR_FILE_THROTTLING "throttling"
+#define ACPI_PROCESSOR_FILE_LIMIT      "limit"
+#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
+#define ACPI_PROCESSOR_NOTIFY_POWER    0x81
+#define ACPI_PROCESSOR_LIMIT_NONE      0x00
+#define ACPI_PROCESSOR_LIMIT_INCREMENT 0x01
+#define ACPI_PROCESSOR_LIMIT_DECREMENT 0x02
+
+int acpi_processor_set_limit(acpi_handle handle, int flags, int *state);
+
+
+/* --------------------------------------------------------------------------
+                                     System
+   -------------------------------------------------------------------------- */
+
+#define ACPI_SYSTEM_COMPONENT          0x04000000
+#define ACPI_SYSTEM_CLASS              "system"
+#define ACPI_SYSTEM_HID                        "ACPI_SYS"
+#define ACPI_SYSTEM_DRIVER_NAME                "ACPI System Driver"
+#define ACPI_SYSTEM_DEVICE_NAME                "System"
+#define ACPI_SYSTEM_FILE_INFO          "info"
+#define ACPI_SYSTEM_FILE_EVENT         "event"
+#define ACPI_SYSTEM_FILE_ALARM         "alarm"
+#define ACPI_SYSTEM_FILE_DSDT          "dsdt"
+#define ACPI_SYSTEM_FILE_FADT          "fadt"
+#define ACPI_SYSTEM_FILE_SLEEP         "sleep"
+#define ACPI_SYSTEM_FILE_DEBUG_LAYER   "debug_layer"
+#define ACPI_SYSTEM_FILE_DEBUG_LEVEL   "debug_level"
+
+#ifdef CONFIG_ACPI_SYSTEM
+
+int acpi_system_init (void);
+void acpi_system_exit (void);
+
+#endif
+
+
+/* --------------------------------------------------------------------------
+                                 Thermal Zone
+   -------------------------------------------------------------------------- */
+
+#define ACPI_THERMAL_COMPONENT         0x08000000
+#define ACPI_THERMAL_CLASS             "thermal_zone"
+#define ACPI_THERMAL_HID               "ACPI_THM"
+#define ACPI_THERMAL_DRIVER_NAME       "ACPI Thermal Zone Driver"
+#define ACPI_THERMAL_DEVICE_NAME       "Thermal Zone"
+#define ACPI_THERMAL_FILE_STATE                "state"
+#define ACPI_THERMAL_FILE_TEMPERATURE  "temperature"
+#define ACPI_THERMAL_FILE_TRIP_POINTS  "trip_points"
+#define ACPI_THERMAL_FILE_COOLING_MODE "cooling_mode"
+#define ACPI_THERMAL_FILE_POLLING_FREQ "polling_frequency"
+#define ACPI_THERMAL_NOTIFY_TEMPERATURE        0x80
+#define ACPI_THERMAL_NOTIFY_THRESHOLDS 0x81
+#define ACPI_THERMAL_NOTIFY_DEVICES    0x82
+#define ACPI_THERMAL_NOTIFY_CRITICAL   0xF0
+#define ACPI_THERMAL_NOTIFY_HOT                0xF1
+#define ACPI_THERMAL_MODE_ACTIVE       0x00
+#define ACPI_THERMAL_MODE_PASSIVE      0x01
+#define ACPI_THERMAL_PATH_POWEROFF     "/sbin/poweroff"
+
+
+/* --------------------------------------------------------------------------
+                                Debug Support
+   -------------------------------------------------------------------------- */
+
+#define ACPI_DEBUG_RESTORE     0
+#define ACPI_DEBUG_LOW         1
+#define ACPI_DEBUG_MEDIUM      2
+#define ACPI_DEBUG_HIGH                3
+#define ACPI_DEBUG_DRIVERS     4
+
+extern u32 acpi_dbg_level;
+extern u32 acpi_dbg_layer;
+
+static inline void
+acpi_set_debug (
+       u32                     flag)
+{
+       static u32              layer_save;
+       static u32              level_save;
+
+       switch (flag) {
+       case ACPI_DEBUG_RESTORE:
+               acpi_dbg_layer = layer_save;
+               acpi_dbg_level = level_save;
+               break;
+       case ACPI_DEBUG_LOW:
+       case ACPI_DEBUG_MEDIUM:
+       case ACPI_DEBUG_HIGH:
+       case ACPI_DEBUG_DRIVERS:
+               layer_save = acpi_dbg_layer;
+               level_save = acpi_dbg_level;
+               break;
+       }
+
+       switch (flag) {
+       case ACPI_DEBUG_LOW:
+               acpi_dbg_layer = ACPI_COMPONENT_DEFAULT | ACPI_ALL_DRIVERS;
+               acpi_dbg_level = DEBUG_DEFAULT;
+               break;
+       case ACPI_DEBUG_MEDIUM:
+               acpi_dbg_layer = ACPI_COMPONENT_DEFAULT | ACPI_ALL_DRIVERS;
+               acpi_dbg_level = ACPI_LV_FUNCTIONS | ACPI_LV_ALL_EXCEPTIONS;
+               break;
+       case ACPI_DEBUG_HIGH:
+               acpi_dbg_layer = 0xFFFFFFFF;
+               acpi_dbg_level = 0xFFFFFFFF;
+               break;
+       case ACPI_DEBUG_DRIVERS:
+               acpi_dbg_layer = ACPI_ALL_DRIVERS;
+               acpi_dbg_level = 0xFFFFFFFF;
+               break;
+       }
+}
+
+
+#endif /*__ACPI_DRIVERS_H__*/
diff --git a/drivers/acpi/acpi_ec.c b/drivers/acpi/acpi_ec.c
new file mode 100644 (file)
index 0000000..4a9cbf3
--- /dev/null
@@ -0,0 +1,736 @@
+/*
+ *  acpi_ec.c - ACPI Embedded Controller Driver ($Revision: 27 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include "acpi_bus.h"
+#include "acpi_drivers.h"
+
+
+#define _COMPONENT             ACPI_EC_COMPONENT
+ACPI_MODULE_NAME               ("acpi_ec")
+
+#define PREFIX                 "ACPI: "
+
+
+#define ACPI_EC_FLAG_OBF       0x01    /* Output buffer full */
+#define ACPI_EC_FLAG_IBF       0x02    /* Input buffer full */
+#define ACPI_EC_FLAG_SCI       0x20    /* EC-SCI occurred */
+
+#define ACPI_EC_EVENT_OBF      0x01    /* Output buffer full */
+#define ACPI_EC_EVENT_IBE      0x02    /* Input buffer empty */
+
+#define ACPI_EC_UDELAY         100     /* Poll @ 100us increments */
+#define ACPI_EC_UDELAY_COUNT   1000    /* Wait 10ms max. during EC ops */
+#define ACPI_EC_UDELAY_GLK     1000    /* Wait 1ms max. to get global lock */
+
+#define ACPI_EC_COMMAND_READ   0x80
+#define ACPI_EC_COMMAND_WRITE  0x81
+#define ACPI_EC_COMMAND_QUERY  0x84
+
+static int acpi_ec_add (struct acpi_device *device);
+static int acpi_ec_remove (struct acpi_device *device, int type);
+static int acpi_ec_start (struct acpi_device *device);
+static int acpi_ec_stop (struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_ec_driver = {
+       name:                   ACPI_EC_DRIVER_NAME,
+       class:                  ACPI_EC_CLASS,
+       ids:                    ACPI_EC_HID,
+       ops:                    {
+                                       add:    acpi_ec_add,
+                                       remove: acpi_ec_remove,
+                                       start:  acpi_ec_start,
+                                       stop:   acpi_ec_stop,
+                               },
+};
+
+struct acpi_ec {
+       acpi_handle             handle;
+       unsigned long           gpe_bit;
+       unsigned long           status_port;
+       unsigned long           command_port;
+       unsigned long           data_port;
+       unsigned long           global_lock;
+       spinlock_t              lock;
+};
+
+
+/* --------------------------------------------------------------------------
+                             Transaction Management
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_ec_wait (
+       struct acpi_ec          *ec,
+       u8                      event)
+{
+       u8                      acpi_ec_status = 0;
+       u32                     i = ACPI_EC_UDELAY_COUNT;
+
+       if (!ec)
+               return -EINVAL;
+
+       /* Poll the EC status register waiting for the event to occur. */
+       switch (event) {
+       case ACPI_EC_EVENT_OBF:
+               do {
+                       acpi_ec_status = inb(ec->status_port);
+                       if (acpi_ec_status & ACPI_EC_FLAG_OBF)
+                               return 0;
+                       udelay(ACPI_EC_UDELAY);
+               } while (--i>0);
+               break;
+       case ACPI_EC_EVENT_IBE:
+               do {
+                       acpi_ec_status = inb(ec->status_port);
+                       if (!(acpi_ec_status & ACPI_EC_FLAG_IBF))
+                               return 0;
+                       udelay(ACPI_EC_UDELAY);
+               } while (--i>0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return -ETIME;
+}
+
+
+static int
+acpi_ec_read (
+       struct acpi_ec          *ec,
+       u8                      address,
+       u8                      *data)
+{
+       acpi_status             status = AE_OK;
+       int                     result = 0;
+       unsigned long           flags = 0;
+       u32                     glk = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_ec_read");
+
+       if (!ec || !data)
+               return_VALUE(-EINVAL);
+
+       *data = 0;
+
+       if (ec->global_lock) {
+               status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
+               if (ACPI_FAILURE(status))
+                       return_VALUE(-ENODEV);
+       }
+       
+       spin_lock_irqsave(&ec->lock, flags);
+
+       outb(ACPI_EC_COMMAND_READ, ec->command_port);
+       result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
+       if (0 != result)
+               goto end;
+
+       outb(address, ec->data_port);
+       result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);
+       if (0 != result)
+               goto end;
+
+       *data = inb(ec->data_port);
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n",
+               *data, address));
+
+end:
+       spin_unlock_irqrestore(&ec->lock, flags);
+
+       if (ec->global_lock)
+               acpi_release_global_lock(glk);
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_ec_write (
+       struct acpi_ec          *ec,
+       u8                      address,
+       u8                      data)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       unsigned long           flags = 0;
+       u32                     glk = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_ec_write");
+
+       if (!ec)
+               return_VALUE(-EINVAL);
+
+       if (ec->global_lock) {
+               status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
+               if (ACPI_FAILURE(status))
+                       return_VALUE(-ENODEV);
+       }
+
+       spin_lock_irqsave(&ec->lock, flags);
+
+       outb(ACPI_EC_COMMAND_WRITE, ec->command_port);
+       result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
+       if (0 != result)
+               goto end;
+
+       outb(address, ec->data_port);
+       result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
+       if (0 != result)
+               goto end;
+
+       outb(data, ec->data_port);
+       result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
+       if (0 != result)
+               goto end;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Wrote [%02x] to address [%02x]\n",
+               data, address));
+
+end:
+       spin_unlock_irqrestore(&ec->lock, flags);
+
+       if (ec->global_lock)
+               acpi_release_global_lock(glk);
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_ec_query (
+       struct acpi_ec          *ec,
+       u8                      *data)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       unsigned long           flags = 0;
+       u32                     glk = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_ec_query");
+
+       if (!ec || !data)
+               return_VALUE(-EINVAL);
+
+       *data = 0;
+
+       if (ec->global_lock) {
+               status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
+               if (ACPI_FAILURE(status))
+                       return_VALUE(-ENODEV);
+       }
+
+       /*
+        * Query the EC to find out which _Qxx method we need to evaluate.
+        * Note that successful completion of the query causes the ACPI_EC_SCI
+        * bit to be cleared (and thus clearing the interrupt source).
+        */
+
+       spin_lock_irqsave(&ec->lock, flags);
+
+       outb(ACPI_EC_COMMAND_QUERY, ec->command_port);
+       result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);
+       if (0 != result)
+               goto end;
+       
+       *data = inb(ec->data_port);
+       if (!*data)
+               result = -ENODATA;
+
+end:
+       spin_unlock_irqrestore(&ec->lock, flags);
+
+       if (ec->global_lock)
+               acpi_release_global_lock(glk);
+
+       return_VALUE(result);
+}
+
+
+/* --------------------------------------------------------------------------
+                                Event Management
+   -------------------------------------------------------------------------- */
+
+struct acpi_ec_query_data {
+       acpi_handle             handle;
+       u8                      data;
+};
+
+
+static void
+acpi_ec_gpe_query (
+       void                    *data)
+{
+       struct acpi_ec_query_data *query_data = NULL;
+       static char             object_name[5] = {'_','Q','0','0','\0'};
+       const char              hex[] = {'0','1','2','3','4','5','6','7',
+                                        '8','9','A','B','C','D','E','F'};
+
+       ACPI_FUNCTION_TRACE("acpi_ec_gpe_query");
+
+       if (!data)
+               return;
+
+       query_data = (struct acpi_ec_query_data *) data;
+
+       object_name[2] = hex[((query_data->data >> 4) & 0x0F)];
+       object_name[3] = hex[(query_data->data & 0x0F)];
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name));
+
+       acpi_evaluate(query_data->handle, object_name, NULL, NULL);
+
+       kfree(query_data);
+
+       return;
+}
+
+
+static void
+acpi_ec_gpe_handler (
+       void                    *data)
+{
+       acpi_status             status = AE_OK;
+       struct acpi_ec          *ec = (struct acpi_ec *) data;
+       u8                      value = 0;
+       unsigned long           flags = 0;
+       struct acpi_ec_query_data *query_data = NULL;
+
+       if (!ec)
+               return;
+
+       spin_lock_irqsave(&ec->lock, flags);
+       value = inb(ec->command_port);
+       spin_unlock_irqrestore(&ec->lock, flags);
+
+       /* TBD: Implement asynch events!
+        * NOTE: All we care about are EC-SCI's.  Other EC events are
+        *       handled via polling (yuck!).  This is because some systems
+        *       treat EC-SCIs as level (versus EDGE!) triggered, preventing
+        *       a purely interrupt-driven approach (grumble, grumble).
+        */
+
+       if (!(value & ACPI_EC_FLAG_SCI))
+               return;
+
+       if (0 != acpi_ec_query(ec, &value))
+               return;
+
+       query_data = kmalloc(sizeof(struct acpi_ec_query_data), GFP_KERNEL);
+       if (!query_data)
+               return;
+       query_data->handle = ec->handle;
+       query_data->data = value;
+
+       status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE,
+               acpi_ec_gpe_query, query_data);
+       if (ACPI_FAILURE(status))
+               kfree(query_data);
+
+       return;
+}
+
+
+/* --------------------------------------------------------------------------
+                             Address Space Management
+   -------------------------------------------------------------------------- */
+
+static acpi_status
+acpi_ec_space_setup (
+       acpi_handle             region_handle,
+       u32                     function,
+       void                    *handler_context,
+       void                    **return_context)
+{
+       /*
+        * The EC object is in the handler context and is needed
+        * when calling the acpi_ec_space_handler.
+        */
+       *return_context = handler_context;
+
+       return AE_OK;
+}
+
+
+static acpi_status
+acpi_ec_space_handler (
+       u32                     function,
+       ACPI_PHYSICAL_ADDRESS   address,
+       u32                     bit_width,
+       acpi_integer            *value,
+       void                    *handler_context,
+       void                    *region_context)
+{
+       int                     result = 0;
+       struct acpi_ec          *ec = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_ec_space_handler");
+
+       if ((address > 0xFF) || (bit_width != 8) || !value || !handler_context)
+               return_VALUE(AE_BAD_PARAMETER);
+
+       ec = (struct acpi_ec *) handler_context;
+
+       switch (function) {
+       case ACPI_READ:
+               result = acpi_ec_read(ec, (u8) address, (u8*) value);
+               break;
+       case ACPI_WRITE:
+               result = acpi_ec_write(ec, (u8) address, (u8) *value);
+               break;
+       default:
+               result = -EINVAL;
+               break;
+       }
+
+       switch (result) {
+       case -EINVAL:
+               return_VALUE(AE_BAD_PARAMETER);
+               break;
+       case -ENODEV:
+               return_VALUE(AE_NOT_FOUND);
+               break;
+       case -ETIME:
+               return_VALUE(AE_TIME);
+               break;
+       default:
+               return_VALUE(AE_OK);
+       }
+
+}
+
+
+/* --------------------------------------------------------------------------
+                              FS Interface (/proc)
+   -------------------------------------------------------------------------- */
+
+#include <linux/compatmac.h>
+#include <linux/proc_fs.h>
+
+struct proc_dir_entry          *acpi_ec_dir = NULL;
+
+
+static int
+acpi_ec_read_info (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_ec          *ec = (struct acpi_ec *) data;
+       char                    *p = page;
+       int                     len = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_ec_read_info");
+
+       if (!ec || (off != 0))
+               goto end;
+
+       p += sprintf(p, "gpe bit:                 0x%02x\n",
+               (u32) ec->gpe_bit);
+       p += sprintf(p, "ports:                   0x%02x, 0x%02x\n",
+               (u32) ec->status_port, (u32) ec->data_port);
+       p += sprintf(p, "use global lock:         %s\n",
+               ec->global_lock?"yes":"no");
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_ec_add_fs (
+       struct acpi_device      *device)
+{
+       struct proc_dir_entry   *entry = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_ec_add_fs");
+
+       if (!acpi_ec_dir) {
+               acpi_ec_dir = proc_mkdir(ACPI_EC_CLASS, acpi_root_dir);
+               if (!acpi_ec_dir)
+                       return_VALUE(-ENODEV);
+       }
+
+       if (!acpi_device_dir(device)) {
+               acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+                       acpi_ec_dir);
+               if (!acpi_device_dir(device))
+                       return_VALUE(-ENODEV);
+       }
+
+       entry = create_proc_read_entry(ACPI_EC_FILE_INFO, S_IRUGO,
+               acpi_device_dir(device), acpi_ec_read_info,
+               acpi_driver_data(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_EC_FILE_INFO));
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_ec_remove_fs (
+       struct acpi_device      *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_ec_remove_fs");
+
+       if (!acpi_ec_dir)
+               return_VALUE(-ENODEV);
+
+       if (acpi_device_dir(device))
+               remove_proc_entry(acpi_device_bid(device), acpi_ec_dir);
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                               Driver Interface
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_ec_add (
+       struct acpi_device      *device)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_ec          *ec = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_ec_add");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       ec = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL);
+       if (!ec)
+               return_VALUE(-ENOMEM);
+       memset(ec, 0, sizeof(struct acpi_ec));
+
+       ec->handle = device->handle;
+       ec->lock = SPIN_LOCK_UNLOCKED;
+       sprintf(acpi_device_name(device), "%s", ACPI_EC_DEVICE_NAME);
+       sprintf(acpi_device_class(device), "%s", ACPI_EC_CLASS);
+       acpi_driver_data(device) = ec;
+
+       /* Use the global lock for all EC transactions? */
+       acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock);
+
+       /* Get GPE bit assignment (EC events). */
+       status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe_bit);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error obtaining GPE bit assignment\n"));
+               result = -ENODEV;
+               goto end;
+       }
+
+       result = acpi_ec_add_fs(device);
+       if (0 != result)
+               goto end;
+
+       printk(KERN_INFO PREFIX "%s [%s] (gpe %d)\n",
+               acpi_device_name(device), acpi_device_bid(device),
+               (u32) ec->gpe_bit);
+
+end:
+       if (0 != result)
+               kfree(ec);
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_ec_remove (
+       struct acpi_device      *device,
+       int                     type)
+{
+       struct acpi_ec          *ec = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_ec_remove");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       ec = (struct acpi_ec *) acpi_driver_data(device);
+
+       acpi_ec_remove_fs(device);
+
+       kfree(ec);
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_ec_start (
+       struct acpi_device      *device)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_ec          *ec = NULL;
+       acpi_buffer             buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       acpi_resource           *resource = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_ec_start");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       ec = (struct acpi_ec *) acpi_driver_data(device);
+
+       if (!ec)
+               return_VALUE(-EINVAL);
+
+       /*
+        * Get I/O port addresses
+        */
+
+       status = acpi_get_current_resources(ec->handle, &buffer);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error getting I/O port addresses"));
+               return_VALUE(-ENODEV);
+       }
+
+       resource = (acpi_resource *) buffer.pointer;
+       if (!resource || (resource->id != ACPI_RSTYPE_IO)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid or missing resource\n"));
+               result = -ENODEV;
+               goto end;
+       }
+       ec->data_port = resource->data.io.min_base_address;
+
+       resource = ACPI_NEXT_RESOURCE(resource);
+       if (!resource || (resource->id != ACPI_RSTYPE_IO)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid or missing resource\n"));
+               result = -ENODEV;
+               goto end;
+       }
+       ec->command_port = ec->status_port = resource->data.io.min_base_address;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02x, ports=0x%2x,0x%2x\n",
+               (u32) ec->gpe_bit, (u32) ec->command_port, (u32) ec->data_port));
+
+       /*
+        * Install GPE handler
+        */
+
+       status = acpi_install_gpe_handler(ec->gpe_bit,
+               ACPI_EVENT_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       status = acpi_install_address_space_handler (ec->handle,
+                       ACPI_ADR_SPACE_EC, &acpi_ec_space_handler,
+                       &acpi_ec_space_setup, ec);
+       if (ACPI_FAILURE(status)) {
+               acpi_remove_address_space_handler(ec->handle,
+                       ACPI_ADR_SPACE_EC, &acpi_ec_space_handler);
+               return_VALUE(-ENODEV);
+       }
+end:
+       kfree(buffer.pointer);
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_ec_stop (
+       struct acpi_device      *device,
+       int                     type)
+{
+       acpi_status             status = AE_OK;
+       struct acpi_ec          *ec = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_ec_stop");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       ec = (struct acpi_ec *) acpi_driver_data(device);
+
+       status = acpi_remove_address_space_handler(ec->handle,
+               ACPI_ADR_SPACE_EC, &acpi_ec_space_handler);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       status = acpi_remove_gpe_handler(ec->gpe_bit, &acpi_ec_gpe_handler);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       return_VALUE(0);
+}
+
+
+int __init
+acpi_ec_init (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_ec_init");
+
+       result = acpi_bus_register_driver(&acpi_ec_driver);
+       if (0 > result) {
+               remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);
+               return_VALUE(-ENODEV);
+       }
+
+       return_VALUE(0);
+}
+
+
+void __exit
+acpi_ec_exit (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_ec_exit");
+
+       result = acpi_bus_unregister_driver(&acpi_ec_driver);
+       if (0 == result)
+               remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);
+
+       return_VOID;
+}
diff --git a/drivers/acpi/acpi_fan.c b/drivers/acpi/acpi_fan.c
new file mode 100644 (file)
index 0000000..ed3f39c
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ *  acpi_fan.c - ACPI Fan Driver ($Revision: 25 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include "acpi_bus.h"
+#include "acpi_drivers.h"
+
+
+#define _COMPONENT             ACPI_FAN_COMPONENT
+ACPI_MODULE_NAME               ("acpi_fan")
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION(ACPI_FAN_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+#define PREFIX                 "ACPI: "
+
+
+int acpi_fan_add (struct acpi_device *device);
+int acpi_fan_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_fan_driver = {
+       name:                   ACPI_FAN_DRIVER_NAME,
+       class:                  ACPI_FAN_CLASS,
+       ids:                    ACPI_FAN_HID,
+       ops:                    {
+                                       add:    acpi_fan_add,
+                                       remove: acpi_fan_remove,
+                               },
+};
+
+struct acpi_fan {
+       acpi_handle             handle;
+};
+
+
+/* --------------------------------------------------------------------------
+                              FS Interface (/proc)
+   -------------------------------------------------------------------------- */
+
+#include <linux/compatmac.h>
+#include <linux/proc_fs.h>
+
+struct proc_dir_entry          *acpi_fan_dir = NULL;
+
+
+static int
+acpi_fan_read_state (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_fan         *fan = (struct acpi_fan *) data;
+       char                    *p = page;
+       int                     len = 0;
+       int                     state = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_fan_read_state");
+
+       if (!fan || (off != 0))
+               goto end;
+
+       if (0 != acpi_bus_get_power(fan->handle, &state))
+               goto end;
+
+       p += sprintf(p, "status:                  %s\n",
+               !state?"on":"off");
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_fan_write_state (
+       struct file             *file,
+       const char              *buffer,
+       unsigned long           count,
+       void                    *data)
+{
+       int                     result = 0;
+       struct acpi_fan         *fan = (struct acpi_fan *) data;
+       char                    state_string[12] = {'\0'};
+
+       ACPI_FUNCTION_TRACE("acpi_fan_write_state");
+
+       if (!fan || (count > sizeof(state_string) - 1))
+               return_VALUE(-EINVAL);
+       
+       if (copy_from_user(state_string, buffer, count))
+               return_VALUE(-EFAULT);
+       
+       state_string[count] = '\0';
+       
+       result = acpi_bus_set_power(fan->handle, 
+               simple_strtoul(state_string, NULL, 0));
+       if (0 != result)
+               return_VALUE(result);
+
+       return_VALUE(count);
+}
+
+
+static int
+acpi_fan_add_fs (
+       struct acpi_device      *device)
+{
+       struct proc_dir_entry   *entry = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_fan_add_fs");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       if (!acpi_fan_dir) {
+               acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir);
+               if (!acpi_fan_dir)
+                       return_VALUE(-ENODEV);
+       }
+
+       if (!acpi_device_dir(device)) {
+               acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+                       acpi_fan_dir);
+               if (!acpi_device_dir(device))
+                       return_VALUE(-ENODEV);
+       }
+
+       /* 'status' [R/W] */
+       entry = create_proc_entry(ACPI_FAN_FILE_STATE,
+               S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_FAN_FILE_STATE));
+       else {
+               entry->read_proc = acpi_fan_read_state;
+               entry->write_proc = acpi_fan_write_state;
+               entry->data = acpi_driver_data(device);
+       }
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_fan_remove_fs (
+       struct acpi_device      *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_fan_remove_fs");
+
+       if (!acpi_fan_dir)
+               return_VALUE(-ENODEV);
+
+       if (acpi_device_dir(device))
+               remove_proc_entry(acpi_device_bid(device), acpi_fan_dir);
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                                 Driver Interface
+   -------------------------------------------------------------------------- */
+
+int
+acpi_fan_add (
+       struct acpi_device      *device)
+{
+       int                     result = 0;
+       struct acpi_fan         *fan = NULL;
+       int                     state = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_fan_add");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       fan = kmalloc(sizeof(struct acpi_fan), GFP_KERNEL);
+       if (!fan)
+               return_VALUE(-ENOMEM);
+       memset(fan, 0, sizeof(struct acpi_fan));
+
+       fan->handle = device->handle;
+       sprintf(acpi_device_name(device), "%s", ACPI_FAN_DEVICE_NAME);
+       sprintf(acpi_device_class(device), "%s", ACPI_FAN_CLASS);
+       acpi_driver_data(device) = fan;
+
+       result = acpi_bus_get_power(fan->handle, &state);
+       if (0 != result) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error reading power state\n"));
+               goto end;
+       }
+
+       result = acpi_fan_add_fs(device);
+       if (0 != result)
+               goto end;
+
+       printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
+               acpi_device_name(device), acpi_device_bid(device),
+               !device->power.state?"on":"off");
+
+end:
+       if (0 != result)
+               kfree(fan);
+
+       return_VALUE(result);
+}
+
+
+int
+acpi_fan_remove (
+       struct acpi_device      *device,
+       int                     type)
+{
+       struct acpi_fan         *fan = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_fan_remove");
+
+       if (!device || !acpi_driver_data(device))
+               return_VALUE(-EINVAL);
+
+       fan = (struct acpi_fan *) acpi_driver_data(device);
+
+       acpi_fan_remove_fs(device);
+
+       kfree(fan);
+
+       return_VALUE(0);
+}
+
+
+int __init
+acpi_fan_init (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_fan_init");
+
+       result = acpi_bus_register_driver(&acpi_fan_driver);
+       if (0 > result)
+               return_VALUE(-ENODEV);
+
+       return_VALUE(0);
+}
+
+
+void __exit
+acpi_fan_exit (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_fan_exit");
+
+       result = acpi_bus_unregister_driver(&acpi_fan_driver);
+       if (0 == result)
+               remove_proc_entry(ACPI_FAN_CLASS, acpi_root_dir);
+
+       return_VOID;
+}
+
+
+module_init(acpi_fan_init);
+module_exit(acpi_fan_exit);
+
index a289c5b2f05c46b5b2b31103f69ffda483bc6224..220087d007b4a7cdf459dbe78127394aaf18e7a2 100644 (file)
@@ -1,64 +1,58 @@
 /*
- *  ksyms.c - ACPI exported symbols
+ *  acpi_ksyms.c - ACPI Kernel Symbols ($Revision: 13 $)
  *
- *  Copyright (C) 2000 Andrew Grover
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  *
  *  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.
+ *  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.  See the GNU
+ *  General Public License for more details.
  *
- *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  *
- *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/acpi.h>
-#include "acpi.h"
-#include "acdebug.h"
+#include "include/acpi.h"
+#include "acpi_bus.h"
+
 
-extern int acpi_in_debugger;
-extern FADT_DESCRIPTOR acpi_fadt;
+#ifdef CONFIG_ACPI_INTERPRETER
 
-#define _COMPONENT     OS_DEPENDENT
-       MODULE_NAME     ("symbols")
+/* ACPI Debugger */
 
 #ifdef ENABLE_DEBUGGER
+
+extern int                     acpi_in_debugger;
+
 EXPORT_SYMBOL(acpi_in_debugger);
 EXPORT_SYMBOL(acpi_db_user_commands);
-#endif
+
+#endif /* ENABLE_DEBUGGER */
+
+/* ACPI Core Subsystem */
 
 #ifdef ACPI_DEBUG
+EXPORT_SYMBOL(acpi_dbg_layer);
+EXPORT_SYMBOL(acpi_dbg_level);
 EXPORT_SYMBOL(acpi_ut_debug_print_raw);
 EXPORT_SYMBOL(acpi_ut_debug_print);
 EXPORT_SYMBOL(acpi_ut_status_exit);
+EXPORT_SYMBOL(acpi_ut_value_exit);
 EXPORT_SYMBOL(acpi_ut_exit);
 EXPORT_SYMBOL(acpi_ut_trace);
-#endif
-
-EXPORT_SYMBOL(acpi_gbl_FADT);
-
-EXPORT_SYMBOL(acpi_os_free);
-EXPORT_SYMBOL(acpi_os_printf);
-EXPORT_SYMBOL(acpi_os_callocate);
-EXPORT_SYMBOL(acpi_os_sleep);
-EXPORT_SYMBOL(acpi_os_stall);
-EXPORT_SYMBOL(acpi_os_queue_for_execution);
-
-EXPORT_SYMBOL(acpi_dbg_layer);
-EXPORT_SYMBOL(acpi_dbg_level);
-
-EXPORT_SYMBOL(acpi_format_exception);
+#endif /*ACPI_DEBUG*/
 
 EXPORT_SYMBOL(acpi_get_handle);
 EXPORT_SYMBOL(acpi_get_parent);
@@ -68,7 +62,6 @@ EXPORT_SYMBOL(acpi_get_object_info);
 EXPORT_SYMBOL(acpi_get_next_object);
 EXPORT_SYMBOL(acpi_evaluate_object);
 EXPORT_SYMBOL(acpi_get_table);
-
 EXPORT_SYMBOL(acpi_install_notify_handler);
 EXPORT_SYMBOL(acpi_remove_notify_handler);
 EXPORT_SYMBOL(acpi_install_gpe_handler);
@@ -77,39 +70,61 @@ EXPORT_SYMBOL(acpi_install_address_space_handler);
 EXPORT_SYMBOL(acpi_remove_address_space_handler);
 EXPORT_SYMBOL(acpi_install_fixed_event_handler);
 EXPORT_SYMBOL(acpi_remove_fixed_event_handler);
-
 EXPORT_SYMBOL(acpi_acquire_global_lock);
 EXPORT_SYMBOL(acpi_release_global_lock);
-
 EXPORT_SYMBOL(acpi_get_current_resources);
 EXPORT_SYMBOL(acpi_get_possible_resources);
 EXPORT_SYMBOL(acpi_set_current_resources);
-
 EXPORT_SYMBOL(acpi_enable_event);
 EXPORT_SYMBOL(acpi_disable_event);
 EXPORT_SYMBOL(acpi_clear_event);
-
 EXPORT_SYMBOL(acpi_get_timer_duration);
 EXPORT_SYMBOL(acpi_get_timer);
+EXPORT_SYMBOL(acpi_hw_get_sleep_type_data);
+EXPORT_SYMBOL(acpi_hw_bit_register_read);
+EXPORT_SYMBOL(acpi_hw_bit_register_write);
+EXPORT_SYMBOL(acpi_enter_sleep_state);
+EXPORT_SYMBOL(acpi_get_system_info);
 
+/* ACPI OS Services Layer (acpi_osl.c) */
+
+EXPORT_SYMBOL(acpi_os_free);
+EXPORT_SYMBOL(acpi_os_printf);
+EXPORT_SYMBOL(acpi_os_sleep);
+EXPORT_SYMBOL(acpi_os_stall);
+EXPORT_SYMBOL(acpi_os_signal);
+EXPORT_SYMBOL(acpi_os_queue_for_execution);
 EXPORT_SYMBOL(acpi_os_signal_semaphore);
 EXPORT_SYMBOL(acpi_os_create_semaphore);
 EXPORT_SYMBOL(acpi_os_delete_semaphore);
 EXPORT_SYMBOL(acpi_os_wait_semaphore);
 
-EXPORT_SYMBOL(acpi_os_read_port);
-EXPORT_SYMBOL(acpi_os_write_port);
+/* ACPI Utilities (acpi_utils.c) */
+
+EXPORT_SYMBOL(acpi_extract_package);
+EXPORT_SYMBOL(acpi_evaluate);
+EXPORT_SYMBOL(acpi_evaluate_integer);
+EXPORT_SYMBOL(acpi_evaluate_reference);
+
+#endif /*CONFIG_ACPI_INTERPRETER*/
+
+
+/* ACPI Bus Driver (acpi_bus.c) */
+
+#ifdef CONFIG_ACPI_BUS
 
 EXPORT_SYMBOL(acpi_fadt);
-EXPORT_SYMBOL(acpi_hw_register_bit_access);
-EXPORT_SYMBOL(acpi_hw_obtain_sleep_type_register_data);
-EXPORT_SYMBOL(acpi_enter_sleep_state);
-EXPORT_SYMBOL(acpi_get_system_info);
-EXPORT_SYMBOL(acpi_leave_sleep_state);
-/*EXPORT_SYMBOL(acpi_save_state_mem);*/
-/*EXPORT_SYMBOL(acpi_save_state_disk);*/
-EXPORT_SYMBOL(acpi_hw_register_read);
-EXPORT_SYMBOL(acpi_set_firmware_waking_vector);
-EXPORT_SYMBOL(acpi_subsystem_status);
+EXPORT_SYMBOL(acpi_root_dir);
+EXPORT_SYMBOL(acpi_bus_get_device);
+EXPORT_SYMBOL(acpi_bus_get_status);
+EXPORT_SYMBOL(acpi_bus_get_power);
+EXPORT_SYMBOL(acpi_bus_set_power);
+EXPORT_SYMBOL(acpi_bus_generate_event);
+EXPORT_SYMBOL(acpi_bus_receive_event);
+EXPORT_SYMBOL(acpi_bus_register_driver);
+EXPORT_SYMBOL(acpi_bus_unregister_driver);
+EXPORT_SYMBOL(acpi_bus_scan);
+EXPORT_SYMBOL(acpi_init);
+
+#endif /*CONFIG_ACPI_BUS*/
 
-EXPORT_SYMBOL(acpi_os_signal);
diff --git a/drivers/acpi/acpi_osl.c b/drivers/acpi/acpi_osl.c
new file mode 100644 (file)
index 0000000..0748dcd
--- /dev/null
@@ -0,0 +1,830 @@
+/*
+ *  acpi_osl.c - OS-dependent functions ($Revision: 65 $)
+ *
+ *  Copyright (C) 2000 Andrew Henroid
+ *  Copyright (C) 2001 Andrew Grover
+ *  Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/kmod.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include "acpi.h"
+
+#ifdef CONFIG_ACPI_EFI
+#include <asm/efi.h>
+#endif
+
+#ifdef _IA64
+#include <asm/hw_irq.h>
+#include <asm/delay.h>
+#endif
+
+
+#define _COMPONENT             ACPI_OS_SERVICES
+ACPI_MODULE_NAME       ("osl")
+
+#define PREFIX         "ACPI: "
+
+typedef struct
+{
+    OSD_EXECUTION_CALLBACK  function;
+    void                   *context;
+} ACPI_OS_DPC;
+
+
+#ifdef ENABLE_DEBUGGER
+#include <linux/kdb.h>
+/* stuff for debugger support */
+int acpi_in_debugger = 0;
+extern NATIVE_CHAR line_buf[80];
+#endif /*ENABLE_DEBUGGER*/
+
+static int acpi_irq_irq = 0;
+static OSD_HANDLER acpi_irq_handler = NULL;
+static void *acpi_irq_context = NULL;
+
+
+acpi_status
+acpi_os_initialize(void)
+{
+       /*
+        * Initialize PCI configuration space access, as we'll need to access
+        * it while walking the namespace (bus 0 and root bridges w/ _BBNs).
+        */
+       pcibios_config_init();
+       if (!pci_config_read || !pci_config_write) {
+               printk(KERN_ERR PREFIX "Access to PCI configuration space unavailable\n");
+               return AE_NULL_ENTRY;
+       }
+
+       return AE_OK;
+}
+
+acpi_status
+acpi_os_terminate(void)
+{
+       if (acpi_irq_handler) {
+               acpi_os_remove_interrupt_handler(acpi_irq_irq,
+                                                acpi_irq_handler);
+       }
+
+       return AE_OK;
+}
+
+void
+acpi_os_printf(const NATIVE_CHAR *fmt,...)
+{
+       va_list args;
+       va_start(args, fmt);
+       acpi_os_vprintf(fmt, args);
+       va_end(args);
+}
+
+void
+acpi_os_vprintf(const NATIVE_CHAR *fmt, va_list args)
+{
+       static char buffer[512];
+       
+       vsprintf(buffer, fmt, args);
+
+#ifdef ENABLE_DEBUGGER
+       if (acpi_in_debugger) {
+               kdb_printf("%s", buffer);
+       } else {
+               printk("%s", buffer);
+       }
+#else
+       printk("%s", buffer);
+#endif
+}
+
+void *
+acpi_os_allocate(u32 size)
+{
+       return kmalloc(size, GFP_KERNEL);
+}
+
+void *
+acpi_os_callocate(u32 size)
+{
+       void *ptr = acpi_os_allocate(size);
+       if (ptr)
+               memset(ptr, 0, size);
+
+       return ptr;
+}
+
+void
+acpi_os_free(void *ptr)
+{
+       kfree(ptr);
+}
+
+
+acpi_status
+acpi_os_get_root_pointer(u32 flags, ACPI_PHYSICAL_ADDRESS *phys_addr)
+{
+#ifndef CONFIG_ACPI_EFI
+       if (ACPI_FAILURE(acpi_find_root_pointer(flags, phys_addr))) {
+               printk(KERN_ERR PREFIX "System description tables not found\n");
+               return AE_NOT_FOUND;
+       }
+#else /*CONFIG_ACPI_EFI*/
+       if (efi.acpi20)
+               *phys_addr = (ACPI_PHYSICAL_ADDRESS) efi.acpi20;
+       else if (efi.acpi)
+               *phys_addr = (ACPI_PHYSICAL_ADDRESS) efi.acpi;
+       else {
+               printk(KERN_ERR PREFIX "System description tables not found\n");
+               *phys_addr = 0;
+               return AE_NOT_FOUND;
+       }
+#endif /*CONFIG_ACPI_EFI*/
+
+       return AE_OK;
+}
+
+acpi_status
+acpi_os_map_memory(ACPI_PHYSICAL_ADDRESS phys, u32 size, void **virt)
+{
+       if (phys > ULONG_MAX) {
+               printk(KERN_ERR PREFIX "Cannot map memory that high\n");
+               return AE_BAD_PARAMETER;
+       }
+
+       /*
+        * ioremap already checks to ensure this is in reserved space
+        */
+       *virt = ioremap((unsigned long) phys, size);
+       if (!*virt)
+               return AE_NO_MEMORY;
+
+       return AE_OK;
+}
+
+void
+acpi_os_unmap_memory(void *virt, u32 size)
+{
+       iounmap(virt);
+}
+
+acpi_status
+acpi_os_get_physical_address(void *virt, ACPI_PHYSICAL_ADDRESS *phys)
+{
+       if(!phys || !virt)
+               return AE_BAD_PARAMETER;
+
+       *phys = virt_to_phys(virt);
+
+       return AE_OK;
+}
+
+static void
+acpi_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+       (*acpi_irq_handler)(acpi_irq_context);
+}
+
+acpi_status
+acpi_os_install_interrupt_handler(u32 irq, OSD_HANDLER handler, void *context)
+{
+#ifdef _IA64
+       irq = isa_irq_to_vector(irq);
+#endif /*_IA64*/
+       acpi_irq_irq = irq;
+       acpi_irq_handler = handler;
+       acpi_irq_context = context;
+       if (request_irq(irq, acpi_irq, SA_SHIRQ, "acpi", acpi_irq)) {
+               printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq);
+               return AE_NOT_ACQUIRED;
+       }
+
+       return AE_OK;
+}
+
+acpi_status
+acpi_os_remove_interrupt_handler(u32 irq, OSD_HANDLER handler)
+{
+       if (acpi_irq_handler) {
+#ifdef _IA64
+               irq = isa_irq_to_vector(irq);
+#endif /*_IA64*/
+               free_irq(irq, acpi_irq);
+               acpi_irq_handler = NULL;
+       }
+
+       return AE_OK;
+}
+
+/*
+ * Running in interpreter thread context, safe to sleep
+ */
+
+void
+acpi_os_sleep(u32 sec, u32 ms)
+{
+       current->state = TASK_INTERRUPTIBLE;
+       schedule_timeout(HZ * sec + (ms * HZ) / 1000);
+}
+
+void
+acpi_os_stall(u32 us)
+{
+       if (us > 10000) {
+               mdelay(us / 1000);
+       }
+       else {
+               udelay(us);
+       }
+}
+
+acpi_status
+acpi_os_read_port(
+       ACPI_IO_ADDRESS port,
+       void            *value,
+       u32             width)
+{
+       u32 dummy;
+
+       if (!value)
+               value = &dummy;
+
+       switch (width)
+       {
+       case 8:
+               *(u8*)  value = inb(port);
+               break;
+       case 16:
+               *(u16*) value = inw(port);
+               break;
+       case 32:
+               *(u32*) value = inl(port);
+               break;
+       default:
+               BUG();
+       }
+
+       return AE_OK;
+}
+
+acpi_status
+acpi_os_write_port(
+       ACPI_IO_ADDRESS port,
+       acpi_integer    value,
+       u32             width)
+{
+       switch (width)
+       {
+       case 8:
+               outb(value, port);
+               break;
+       case 16:
+               outw(value, port);
+               break;
+       case 32:
+               outl(value, port);
+               break;
+       default:
+               BUG();
+       }
+
+       return AE_OK;
+}
+
+acpi_status
+acpi_os_read_memory(
+       ACPI_PHYSICAL_ADDRESS   phys_addr,
+       void                    *value,
+       u32                     width)
+{
+       u32 dummy;
+
+       if (!value)
+               value = &dummy;
+
+       switch (width)
+       {
+       case 8:
+               *(u8*) value = *(u8*) phys_to_virt(phys_addr);
+               break;
+       case 16:
+               *(u16*) value = *(u16*) phys_to_virt(phys_addr);
+               break;
+       case 32:
+               *(u32*) value = *(u32*) phys_to_virt(phys_addr);
+               break;
+       default:
+               BUG();
+       }
+
+       return AE_OK;
+}
+
+acpi_status
+acpi_os_write_memory(
+       ACPI_PHYSICAL_ADDRESS   phys_addr,
+       acpi_integer            value,
+       u32                     width)
+{
+       switch (width)
+       {
+       case 8:
+               *(u8*) phys_to_virt(phys_addr) = value;
+               break;
+       case 16:
+               *(u16*) phys_to_virt(phys_addr) = value;
+               break;
+       case 32:
+               *(u32*) phys_to_virt(phys_addr) = value;
+               break;
+       default:
+               BUG();
+       }
+
+       return AE_OK;
+}
+
+
+acpi_status
+acpi_os_read_pci_configuration (
+       acpi_pci_id             *pci_id,
+       u32                     reg,
+       void                    *value,
+       u32                     width)
+{
+       int                     result = 0;
+       if (!value)
+               return AE_BAD_PARAMETER;
+
+       switch (width)
+       {
+       case 8:
+               result = pci_config_read(pci_id->segment, pci_id->bus,
+                       pci_id->device, pci_id->function, reg, 1, value);
+               break;
+       case 16:
+               result = pci_config_read(pci_id->segment, pci_id->bus,
+                       pci_id->device, pci_id->function, reg, 2, value);
+               break;
+       case 32:
+               result = pci_config_read(pci_id->segment, pci_id->bus,
+                       pci_id->device, pci_id->function, reg, 4, value);
+               break;
+       default:
+               BUG();
+       }
+
+       return (result ? AE_ERROR : AE_OK);
+}
+
+acpi_status
+acpi_os_write_pci_configuration (
+       acpi_pci_id             *pci_id,
+       u32                     reg,
+       acpi_integer            value,
+       u32                     width)
+{
+       int                     result = 0;
+
+       switch (width)
+       {
+       case 8:
+               result = pci_config_write(pci_id->segment, pci_id->bus,
+                       pci_id->device, pci_id->function, reg, 1, value);
+               break;
+       case 16:
+               result = pci_config_write(pci_id->segment, pci_id->bus,
+                       pci_id->device, pci_id->function, reg, 2, value);
+               break;
+       case 32:
+               result = pci_config_write(pci_id->segment, pci_id->bus,
+                       pci_id->device, pci_id->function, reg, 4, value);
+               break;
+       default:
+               BUG();
+       }
+
+       return (result ? AE_ERROR : AE_OK);
+}
+
+
+acpi_status
+acpi_os_load_module (
+       char *module_name)
+{
+       ACPI_FUNCTION_TRACE ("os_load_module");
+
+       if (!module_name)
+               return AE_BAD_PARAMETER;
+
+       if (0 > request_module(module_name)) {
+               ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Unable to load module [%s].\n", module_name));
+               return AE_ERROR;
+       }
+
+       return AE_OK;
+}
+
+acpi_status
+acpi_os_unload_module (
+       char *module_name)
+{
+       if (!module_name)
+               return AE_BAD_PARAMETER;
+
+       /* TODO: How on Linux? */
+       /* this is done automatically for all modules with
+       use_count = 0, I think. see: MOD_INC_USE_COUNT -ASG */
+
+       return AE_OK;
+}
+
+
+/*
+ * See acpi_os_queue_for_execution()
+ */
+static int
+acpi_os_queue_exec (
+       void *context)
+{
+       ACPI_OS_DPC             *dpc = (ACPI_OS_DPC*)context;
+
+       ACPI_FUNCTION_TRACE ("os_queue_exec");
+
+       daemonize();
+       strcpy(current->comm, "kacpidpc");
+
+       if (!dpc || !dpc->function)
+               return AE_BAD_PARAMETER;
+
+       ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Executing function [%p(%p)].\n", dpc->function, dpc->context));
+
+       dpc->function(dpc->context);
+
+       kfree(dpc);
+
+       return 1;
+}
+
+static void
+acpi_os_schedule_exec (
+       void *context)
+{
+       ACPI_OS_DPC             *dpc = NULL;
+       int                     thread_pid = -1;
+
+       ACPI_FUNCTION_TRACE ("os_schedule_exec");
+
+       dpc = (ACPI_OS_DPC*)context;
+       if (!dpc) {
+               ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) context.\n"));
+               return;
+       }
+
+       ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Creating new thread to run function [%p(%p)].\n", dpc->function, dpc->context));
+
+       thread_pid = kernel_thread(acpi_os_queue_exec, dpc,
+               (CLONE_FS | CLONE_FILES | SIGCHLD));
+       if (thread_pid < 0) {
+               ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Call to kernel_thread() failed.\n"));
+               acpi_os_free(dpc);
+       }
+}
+
+acpi_status
+acpi_os_queue_for_execution(
+       u32                     priority,
+       OSD_EXECUTION_CALLBACK  function,
+       void                    *context)
+{
+       acpi_status             status = AE_OK;
+       ACPI_OS_DPC             *dpc = NULL;
+
+       ACPI_FUNCTION_TRACE ("os_queue_for_execution");
+
+       ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Scheduling function [%p(%p)] for deferred execution.\n", function, context));
+
+       if (!function)
+               return AE_BAD_PARAMETER;
+
+       /*
+        * Queue via DPC:
+        * --------------
+        * Note that we have to use two different processes for queuing DPCs:
+        *       Interrupt-Level: Use schedule_task; can't spawn a new thread.
+        *          Kernel-Level: Spawn a new kernel thread, as schedule_task has
+        *                        its limitations (e.g. single-threaded model), and
+        *                        all other task queues run at interrupt-level.
+        */
+       switch (priority) {
+
+       case OSD_PRIORITY_GPE:
+       {
+               static struct tq_struct task;
+
+               /*
+                * Allocate/initialize DPC structure.  Note that this memory will be
+                * freed by the callee.
+                */
+               dpc = kmalloc(sizeof(ACPI_OS_DPC), GFP_ATOMIC);
+               if (!dpc)
+                       return AE_NO_MEMORY;
+
+               dpc->function = function;
+               dpc->context = context;
+
+               memset(&task, 0, sizeof(struct tq_struct));
+
+               task.routine = acpi_os_schedule_exec;
+               task.data = (void*)dpc;
+
+               if (schedule_task(&task) < 0) {
+                       ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Call to schedule_task() failed.\n"));
+                       status = AE_ERROR;
+               }
+       }
+       break;
+
+       default:
+               /*
+                * Allocate/initialize DPC structure.  Note that this memory will be
+                * freed by the callee.
+                */
+               dpc = kmalloc(sizeof(ACPI_OS_DPC), GFP_KERNEL);
+               if (!dpc)
+                       return AE_NO_MEMORY;
+
+               dpc->function = function;
+               dpc->context = context;
+
+               acpi_os_schedule_exec(dpc);
+               break;
+       }
+
+       return status;
+}
+
+
+acpi_status
+acpi_os_create_semaphore(
+       u32             max_units,
+       u32             initial_units,
+       acpi_handle     *handle)
+{
+       struct semaphore        *sem = NULL;
+
+       ACPI_FUNCTION_TRACE ("os_create_semaphore");
+
+       sem = acpi_os_callocate(sizeof(struct semaphore));
+       if (!sem)
+               return AE_NO_MEMORY;
+
+       sema_init(sem, initial_units);
+
+       *handle = (acpi_handle*)sem;
+
+       ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Creating semaphore[%p|%d].\n", *handle, initial_units));
+
+       return AE_OK;
+}
+
+
+/*
+ * TODO: A better way to delete semaphores?  Linux doesn't have a
+ * 'delete_semaphore()' function -- may result in an invalid
+ * pointer dereference for non-synchronized consumers. Should
+ * we at least check for blocked threads and signal/cancel them?
+ */
+
+acpi_status
+acpi_os_delete_semaphore(
+       acpi_handle     handle)
+{
+       struct semaphore *sem = (struct semaphore*) handle;
+
+       ACPI_FUNCTION_TRACE ("os_delete_semaphore");
+
+       if (!sem)
+               return AE_BAD_PARAMETER;
+
+       ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Deleting semaphore[%p].\n", handle));
+
+       acpi_os_free(sem); sem =  NULL;
+
+       return AE_OK;
+}
+
+
+/*
+ * TODO: The kernel doesn't have a 'down_timeout' function -- had to
+ * improvise.  The process is to sleep for one scheduler quantum
+ * until the semaphore becomes available.  Downside is that this
+ * may result in starvation for timeout-based waits when there's
+ * lots of semaphore activity.
+ *
+ * TODO: Support for units > 1?
+ */
+acpi_status
+acpi_os_wait_semaphore(
+       acpi_handle             handle,
+       u32                     units,
+       u32                     timeout)
+{
+       acpi_status             status = AE_OK;
+       struct semaphore        *sem = (struct semaphore*)handle;
+       int                     ret = 0;
+
+       ACPI_FUNCTION_TRACE ("os_wait_semaphore");
+
+       if (!sem || (units < 1))
+               return AE_BAD_PARAMETER;
+
+       if (units > 1)
+               return AE_SUPPORT;
+
+       ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Waiting for semaphore[%p|%d|%d]\n", handle, units, timeout));
+
+       switch (timeout)
+       {
+               /*
+                * No Wait:
+                * --------
+                * A zero timeout value indicates that we shouldn't wait - just
+                * acquire the semaphore if available otherwise return AE_TIME
+                * (a.k.a. 'would block').
+                */
+               case 0:
+               if(down_trylock(sem))
+                       status = AE_TIME;
+               break;
+
+               /*
+                * Wait Indefinitely:
+                * ------------------
+                */
+               case WAIT_FOREVER:
+               ret = down_interruptible(sem);
+               if (ret < 0)
+                       status = AE_ERROR;
+               break;
+
+               /*
+                * Wait w/ Timeout:
+                * ----------------
+                */
+               default:
+               // TODO: A better timeout algorithm?
+               {
+                       int i = 0;
+                       static const int quantum_ms = 1000/HZ;
+
+                       ret = down_trylock(sem);
+                       for (i = timeout; (i > 0 && ret < 0); i -= quantum_ms) {
+                               current->state = TASK_INTERRUPTIBLE;
+                               schedule_timeout(1);
+                               ret = down_trylock(sem);
+                       }
+       
+                       if (ret != 0)
+                        status = AE_TIME;
+                       }
+               break;
+       }
+
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Failed to acquire semaphore[%p|%d|%d]\n", handle, units, timeout));
+       }
+       else {
+               ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Acquired semaphore[%p|%d|%d]\n", handle, units, timeout));
+       }
+
+       return status;
+}
+
+
+/*
+ * TODO: Support for units > 1?
+ */
+acpi_status
+acpi_os_signal_semaphore(
+    acpi_handle            handle,
+    u32                    units)
+{
+       struct semaphore *sem = (struct semaphore *) handle;
+
+       ACPI_FUNCTION_TRACE ("os_signal_semaphore");
+
+       if (!sem || (units < 1))
+               return AE_BAD_PARAMETER;
+
+       if (units > 1)
+               return AE_SUPPORT;
+
+       ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Signaling semaphore[%p|%d]\n", handle, units));
+
+       up(sem);
+
+       return AE_OK;
+}
+
+u32
+acpi_os_get_line(NATIVE_CHAR *buffer)
+{
+
+#ifdef ENABLE_DEBUGGER
+       if (acpi_in_debugger) {
+               u32 chars;
+
+               kdb_read(buffer, sizeof(line_buf));
+
+               /* remove the CR kdb includes */
+               chars = strlen(buffer) - 1;
+               buffer[chars] = '\0';
+       }
+#endif
+
+       return 0;
+}
+
+/*
+ * We just have to assume we're dealing with valid memory
+ */
+
+BOOLEAN
+acpi_os_readable(void *ptr, u32 len)
+{
+       return 1;
+}
+
+BOOLEAN
+acpi_os_writable(void *ptr, u32 len)
+{
+       return 1;
+}
+
+u32
+acpi_os_get_thread_id (void)
+{
+       if (!in_interrupt())
+               return current->pid;
+
+       return 0;
+}
+
+acpi_status
+acpi_os_signal (
+    u32                function,
+    void       *info)
+{
+       switch (function)
+       {
+       case ACPI_SIGNAL_FATAL:
+               printk(KERN_ERR PREFIX "Fatal opcode executed\n");
+               break;
+       case ACPI_SIGNAL_BREAKPOINT:
+               {
+                       char *bp_info = (char*) info;
+
+                       printk(KERN_ERR "ACPI breakpoint: %s\n", bp_info);
+               }
+       default:
+               break;
+       }
+
+       return AE_OK;
+}
+
+acpi_status
+acpi_os_breakpoint(NATIVE_CHAR *msg)
+{
+       acpi_os_printf("breakpoint: %s", msg);
+       
+       return AE_OK;
+}
+
diff --git a/drivers/acpi/acpi_pci_link.c b/drivers/acpi/acpi_pci_link.c
new file mode 100644 (file)
index 0000000..a3e8a6c
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ *  acpi_pci_link.c - ACPI PCI Interrupt Link Device Driver ($Revision: 19 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * TBD: 
+ *      1. Support more than one IRQ resource entry per link device.
+ *     2. Implement start/stop mechanism and use ACPI Bus Driver facilities
+ *        for IRQ management (e.g. start()->_SRS).
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/pci.h>
+
+#include "acpi_bus.h"
+#include "acpi_drivers.h"
+
+
+#define _COMPONENT             ACPI_PCI_LINK_COMPONENT
+ACPI_MODULE_NAME               ("acpi_pci_link")
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION(ACPI_PCI_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+#define PREFIX                 "ACPI: "
+
+#define ACPI_PCI_LINK_MAX_IRQS 16
+
+
+static int acpi_pci_link_add (struct acpi_device *device);
+static int acpi_pci_link_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_pci_link_driver = {
+        name:                   ACPI_PCI_LINK_DRIVER_NAME,
+        class:                  ACPI_PCI_LINK_CLASS,
+        ids:                    ACPI_PCI_LINK_HID,
+        ops:                    {
+                                        add:    acpi_pci_link_add,
+                                        remove: acpi_pci_link_remove,
+                                },
+};
+
+struct acpi_pci_link_irq {
+       u8                      active;                 /* Current IRQ */
+       u8                      possible_count;
+       u8                      possible[ACPI_PCI_LINK_MAX_IRQS];
+       struct {
+               u8                      valid:1;
+               u8                      enabled:1;
+               u8                      shareable:1;    /* 0 = Exclusive */
+               u8                      polarity:1;     /* 0 = Active-High */
+               u8                      trigger:1;      /* 0 = Level-Triggered */
+               u8                      producer:1;     /* 0 = Consumer-Only */
+               u8                      reserved:2;
+       }                       flags;
+};
+
+struct acpi_pci_link {
+       acpi_handle             handle;
+       struct acpi_pci_link_irq irq;
+};
+
+
+/* --------------------------------------------------------------------------
+                            PCI Link Device Management
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_pci_link_get_current (
+       struct acpi_pci_link    *link)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       acpi_buffer             buffer = {0, NULL};
+       acpi_resource           *resource = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_pci_link_get_current");
+
+       if (!link || !link->handle)
+               return_VALUE(-EINVAL);
+
+       link->irq.active = 0;
+
+       status = acpi_get_current_resources(link->handle, &buffer);
+       if (status != AE_BUFFER_OVERFLOW) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _CRS\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       buffer.pointer = kmalloc(buffer.length, GFP_KERNEL);
+       if (!buffer.pointer)
+               return_VALUE(-ENOMEM);
+       memset(buffer.pointer, 0, buffer.length);
+
+       status = acpi_get_current_resources(link->handle, &buffer);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _CRS\n"));
+               result = -ENODEV;
+               goto end;
+       }
+
+       resource = (acpi_resource *) buffer.pointer;
+
+       switch (resource->id) {
+       case ACPI_RSTYPE_IRQ:
+       {
+               acpi_resource_irq *p = &resource->data.irq;
+               if (p->number_of_interrupts > 0)
+                       link->irq.active = p->interrupts[0];
+               else {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Blank IRQ resource\n"));
+                       result = -ENODEV;
+                       goto end;
+               }
+               break;
+       }
+       case ACPI_RSTYPE_EXT_IRQ:
+       {
+               acpi_resource_ext_irq *p = &resource->data.extended_irq;
+               if (p->number_of_interrupts > 0)
+                       link->irq.active = p->interrupts[0];
+               else {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Blank IRQ resource\n"));
+                       result = -ENODEV;
+                       goto end;
+               }
+               break;
+       }
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 
+                       "First resource is not an IRQ entry\n"));
+               break;
+       }
+
+end:
+       kfree(buffer.pointer);
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_pci_link_get_possible (
+       struct acpi_pci_link    *link)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       acpi_buffer             buffer = {0, NULL};
+       acpi_resource           *resource = NULL;
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_pci_link_get_possible");
+
+       if (!link)
+               return_VALUE(-EINVAL);
+
+       status = acpi_get_possible_resources(link->handle, &buffer);
+       if (status != AE_BUFFER_OVERFLOW) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRS\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       buffer.pointer = kmalloc(buffer.length, GFP_KERNEL);
+       if (!buffer.pointer)
+               return_VALUE(-ENOMEM);
+       memset(buffer.pointer, 0, buffer.length);
+
+       status = acpi_get_possible_resources(link->handle, &buffer);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRS\n"));
+               result = -ENODEV;
+               goto end;
+       }
+
+       resource = (acpi_resource *) buffer.pointer;
+
+       switch (resource->id) {
+       case ACPI_RSTYPE_IRQ:
+       {
+               acpi_resource_irq *p = &resource->data.irq;
+
+               for (i = 0; (i<p->number_of_interrupts && i<ACPI_PCI_LINK_MAX_IRQS); i++) {
+                       link->irq.possible[i] = p->interrupts[i];
+                       link->irq.possible_count++;
+               }
+
+               if (!p->number_of_interrupts) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Blank IRQ resource\n"));
+                       result = -ENODEV;
+                       goto end;
+               }
+
+               link->irq.flags.trigger = p->edge_level;
+               link->irq.flags.polarity = p->active_high_low;
+               link->irq.flags.shareable = p->shared_exclusive;
+
+               break;
+       }
+       case ACPI_RSTYPE_EXT_IRQ:
+       {
+               acpi_resource_ext_irq *p = &resource->data.extended_irq;
+
+               for (i = 0; (i<p->number_of_interrupts && i<ACPI_PCI_LINK_MAX_IRQS); i++) {
+                       link->irq.possible[i] = p->interrupts[i];
+                       link->irq.possible_count++;
+               }
+
+               if (!p->number_of_interrupts) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Blank IRQ resource\n"));
+                       result = -ENODEV;
+                       goto end;
+               }
+
+               link->irq.flags.trigger = p->edge_level;
+               link->irq.flags.polarity = p->active_high_low;
+               link->irq.flags.shareable = p->shared_exclusive;
+
+               break;
+       }
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 
+                       "First resource is not an IRQ entry\n"));
+               break;
+       }
+
+end:
+       kfree(buffer.pointer);
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_pci_link_set (
+       struct acpi_pci_link    *link,
+       u32                     irq)
+{
+       acpi_status             status = AE_OK;
+       struct {
+               acpi_resource   res;
+               acpi_resource   end;
+       }                       resource;
+       acpi_buffer             buffer = {sizeof(resource)+1, &resource};
+
+       ACPI_FUNCTION_TRACE("acpi_pci_link_set");
+
+       if (!link || !irq)
+               return_VALUE(-EINVAL);
+
+       memset(&resource, 0, sizeof(resource));
+
+       resource.res.id = ACPI_RSTYPE_IRQ;
+       resource.res.length = sizeof(acpi_resource);
+       resource.res.data.irq.edge_level = link->irq.flags.trigger;
+       resource.res.data.irq.active_high_low = link->irq.flags.polarity;
+       resource.res.data.irq.shared_exclusive = link->irq.flags.shareable;
+       resource.res.data.irq.number_of_interrupts = 1;
+       resource.res.data.irq.interrupts[0] = irq;
+       resource.end.id = ACPI_RSTYPE_END_TAG;
+
+       status = acpi_set_current_resources(link->handle, &buffer);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _SRS\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       return_VALUE(0);
+}
+
+
+int
+acpi_pci_link_get_irq (
+       struct acpi_prt_entry   *entry,
+       int                     *irq)
+{
+       int                     result = -ENODEV;
+       struct acpi_device      *device = NULL;
+       struct acpi_pci_link    *link = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_pci_link_get_irq");
+
+       if (!entry || !entry->source.handle || !irq)
+               return_VALUE(-EINVAL);
+
+       /* TBD: Support multiple index values (not just first). */
+       if (0 != entry->source.index) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 
+                       "Unsupported resource index [%d]\n", 
+                       entry->source.index));
+               return_VALUE(-ENODEV);
+       }
+
+       result = acpi_bus_get_device(entry->source.handle, &device);
+       if (0 != result) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link IRQ invalid\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       link = (struct acpi_pci_link *) acpi_driver_data(device);
+       if (!link) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       if (!link->irq.flags.valid) 
+               return_VALUE(-ENODEV);
+
+       /* TBD: Support multiple index (IRQ) entries per Link Device */
+       if (0 != entry->source.index) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 
+                       "Unsupported IRQ resource index [%d]\n", 
+                       entry->source.index));
+               return_VALUE(-EFAULT);
+       }
+
+       *irq = link->irq.active;
+
+       return_VALUE(0);
+}
+
+
+int
+acpi_pci_link_set_irq (
+       struct acpi_prt_entry   *entry,
+       int                     irq)
+{
+       int                     result = 0;
+       int                     i = 0;
+       int                     valid = 0;
+       struct acpi_device      *device = NULL;
+       struct acpi_pci_link    *link = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_pci_link_set_irq");
+
+       if (!entry || !entry->source.handle || !irq)
+               return_VALUE(-EINVAL);
+
+       /* TBD: Support multiple index values (not just first). */
+       if (0 != entry->source.index) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 
+                       "Unsupported resource index [%d]\n", 
+                       entry->source.index));
+               return_VALUE(-ENODEV);
+       }
+
+       result = acpi_bus_get_device(entry->source.handle, &device);
+       if (0 != result)
+               return_VALUE(result);
+
+       link = (struct acpi_pci_link *) acpi_driver_data(device);
+       if (!link) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       if (!link->irq.flags.valid) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link IRQ invalid\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       /* Is the target IRQ the same as the currently enabled IRQ? */
+       if (link->irq.flags.enabled && (irq == link->irq.active)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link IRQ already at target\n"));
+               return_VALUE(0);
+       }
+
+       /* Is the target IRQ in the list of possible IRQs? */
+       for (i=0; i<link->irq.possible_count; i++) {
+               if (irq == link->irq.possible[i])
+                       valid = 1;
+       }
+       if (!valid) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Target IRQ invalid\n"));
+               return_VALUE(-EINVAL);
+       }
+
+       /* TBD: Do we need to disable this link device before resetting? */
+
+       /* Set the new IRQ */
+       result = acpi_pci_link_set(link, irq);
+       if (0 != result)
+               return_VALUE(result);
+
+       link->irq.active = irq;
+
+       return_VALUE(result);
+}
+
+
+/* --------------------------------------------------------------------------
+                                 Driver Interface
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_pci_link_add (
+       struct acpi_device *device)
+{
+       int                     result = 0;
+       struct acpi_pci_link    *link = NULL;
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_pci_link_add");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       link = kmalloc(sizeof(struct acpi_pci_link), GFP_KERNEL);
+       if (!link)
+               return_VALUE(-ENOMEM);
+       memset(link, 0, sizeof(struct acpi_pci_link));
+
+       link->handle = device->handle;
+       sprintf(acpi_device_name(device), "%s", ACPI_PCI_LINK_DEVICE_NAME);
+       sprintf(acpi_device_class(device), "%s", ACPI_PCI_LINK_CLASS);
+       acpi_driver_data(device) = link;
+
+       result = acpi_pci_link_get_possible(link);
+       if (0 != result)
+               return_VALUE(result);
+
+       if (!device->status.enabled) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Enabling at IRQ [%d]\n",
+                       link->irq.possible[0]));
+
+               result = acpi_pci_link_set(link, link->irq.possible[0]);
+               if (0 != result)
+                       return_VALUE(result);
+
+               result = acpi_bus_get_status(device);
+               if (0 != result)
+                       return_VALUE(result);
+
+               if (!device->status.enabled) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 
+                               "Enable failed\n"));
+                       return_VALUE(-EFAULT);
+               }
+       }
+
+       result = acpi_pci_link_get_current(link);
+       if (0 != result)
+               return_VALUE(result);
+
+       link->irq.flags.valid = 1;
+
+       printk(PREFIX "%s [%s] (IRQs", 
+               acpi_device_name(device), acpi_device_bid(device));
+       for (i = 0; i < link->irq.possible_count; i++)
+               printk("%s%d", 
+                       (link->irq.active==link->irq.possible[i])?" *":" ",
+                       link->irq.possible[i]);
+       printk(")\n");
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_pci_link_remove (
+       struct acpi_device      *device,
+       int                     type)
+{
+       struct acpi_pci_link *link = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_pci_link_remove");
+
+       if (!device || !acpi_driver_data(device))
+               return_VALUE(-EINVAL);
+
+       link = (struct acpi_pci_link *) acpi_driver_data(device);
+
+       kfree(link);
+
+       return_VALUE(0);
+}
+
+
+int __init
+acpi_pci_link_init (void)
+{
+       ACPI_FUNCTION_TRACE("acpi_pci_link_init");
+
+       if (0 > acpi_bus_register_driver(&acpi_pci_link_driver))
+               return_VALUE(-ENODEV);
+
+       return_VALUE(0);
+}
+
+
+void __exit
+acpi_pci_link_exit (void)
+{
+       ACPI_FUNCTION_TRACE("acpi_pci_link_init");
+
+       acpi_bus_unregister_driver(&acpi_pci_link_driver);
+
+       return_VOID;
+}
diff --git a/drivers/acpi/acpi_pci_root.c b/drivers/acpi/acpi_pci_root.c
new file mode 100644 (file)
index 0000000..213e2ea
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ *  acpi_pci_root.c - ACPI PCI Root Bridge Driver ($Revision: 22 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include "acpi_bus.h"
+#include "acpi_drivers.h"
+
+
+#define _COMPONENT             ACPI_PCI_ROOT_COMPONENT
+ACPI_MODULE_NAME               ("pci_root")
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION(ACPI_PCI_ROOT_DRIVER_NAME);
+
+extern struct pci_ops *pci_root_ops;
+
+#define PREFIX                 "ACPI: "
+
+static int acpi_pci_root_add (struct acpi_device *device);
+static int acpi_pci_root_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_pci_root_driver = {
+        name:                   ACPI_PCI_ROOT_DRIVER_NAME,
+        class:                  ACPI_PCI_ROOT_CLASS,
+        ids:                    ACPI_PCI_ROOT_HID,
+        ops:                    {
+                                        add:    acpi_pci_root_add,
+                                        remove: acpi_pci_root_remove,
+                                },
+};
+
+struct acpi_pci_root_context {
+       acpi_handle             handle;
+       struct {
+               u8                      seg;    /* Root's segment number */
+               u8                      bus;    /* Root's bus number */
+               u8                      sub;    /* Max subordinate bus */
+       }                       id;
+       struct pci_bus          *bus;
+       struct pci_dev          *dev;
+};
+
+struct acpi_prt_list           acpi_prts;
+
+
+/* --------------------------------------------------------------------------
+                        PCI Routing Table (PRT) Support
+   -------------------------------------------------------------------------- */
+
+static struct acpi_prt_entry *
+acpi_prt_find_entry (
+       struct pci_dev          *dev,
+       u8                      pin)
+{
+       struct acpi_prt_entry   *entry = NULL;
+       struct list_head        *node = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_prt_find_entry");
+
+       /* TBD: Locking */
+       list_for_each(node, &acpi_prts.entries) {
+               entry = list_entry(node, struct acpi_prt_entry, node);
+               if ((entry->id.dev == PCI_SLOT(dev->devfn)) 
+                       && (entry->id.pin == pin))
+                       return_PTR(entry);
+       }
+
+       return_PTR(NULL);
+}
+
+
+int
+acpi_prt_get_irq (
+       struct pci_dev          *dev,
+       u8                      pin,
+       int                     *irq)
+{
+       int                     result = 0;
+       struct acpi_prt_entry   *entry = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_pci_get_current_irq");
+
+       if (!dev || !irq)
+               return_VALUE(-ENODEV);
+
+       entry = acpi_prt_find_entry(dev, pin);
+       if (!entry)
+               return_VALUE(-ENODEV);
+
+       /* Type 1: Dynamic (e.g. PCI Link Device) */
+       if (entry->source.handle)
+               result = acpi_pci_link_get_irq(entry, irq);
+       /* Type 2: Static (e.g. I/O [S]APIC Direct) */
+       else
+               *irq = entry->source.index;
+       
+       return_VALUE(0);
+}
+
+
+int
+acpi_prt_set_irq (
+       struct pci_dev          *dev,
+       u8                      pin,
+       int                     irq)
+{
+       int                     result = 0;
+       int                     i = 0;
+       int                     valid = 0;
+       struct acpi_prt_entry   *entry = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_pci_set_current_irq");
+
+       if (!dev || !irq)
+               return_VALUE(-EINVAL);
+
+       entry = acpi_prt_find_entry(dev, pin);
+       if (!entry)
+               return_VALUE(-ENODEV);
+
+       /* Type 1: Dynamic (e.g. PCI Link Device) */
+       if (entry->source.handle)
+               result = acpi_pci_link_set_irq(entry, irq);
+       /* Type 2: Static (e.g. I/O [S]APIC Direct) */
+       else
+               result = -EFAULT;
+       
+       return_VALUE(result);
+}
+
+
+static int
+acpi_prt_add_entry (
+       acpi_handle             handle,
+       u8                      seg,
+       u8                      bus,
+       acpi_pci_routing_table  *prt)
+{
+       struct acpi_prt_entry   *entry = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_prt_add_entry");
+
+       if (!prt)
+               return_VALUE(-EINVAL);
+
+       entry = kmalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL);
+       if (!entry)
+               return_VALUE(-ENOMEM);
+       memset(entry, 0, sizeof(struct acpi_prt_entry));
+
+       entry->id.seg = seg;
+       entry->id.bus = bus;
+       entry->id.dev = prt->address >> 16;
+       entry->id.pin = prt->pin;
+
+       /*
+        * Type 1: Dynamic
+        * ---------------
+        * The 'source' field specifies the PCI interrupt link device used to
+        * configure the IRQ assigned to this slot|dev|pin.  The 'source_index'
+        * indicates which resource descriptor in the resource template (of
+        * the link device) this interrupt is allocated from.
+        */
+       if (prt->source)
+               acpi_get_handle(handle, prt->source, &entry->source.handle);
+       /*
+        * Type 2: Static
+        * --------------
+        * The 'source' field is NULL, and the 'source_index' field specifies
+        * the IRQ value, which is hardwired to specific interrupt inputs on
+        * the interrupt controller.
+        */
+       else
+               entry->source.handle = NULL;
+
+       entry->source.index = prt->source_index;
+
+       /* 
+        * NOTE: Don't query the Link Device for IRQ information at this time
+        *       because Link Device enumeration may not have occurred yet
+        *       (e.g. exists somewhere 'below' this _PRT entry in the ACPI
+        *       namespace).
+        */
+
+       printk(KERN_INFO "      %02X:%02X:%02X[%c] -> %s[%d]\n", 
+               entry->id.seg, entry->id.bus, entry->id.dev, 
+               ('A' + entry->id.pin), prt->source, entry->source.index);
+
+       /* TBD: Acquire/release lock */
+       list_add_tail(&entry->node, &acpi_prts.entries);
+
+       acpi_prts.count++;
+
+       return_VALUE(0);
+}
+
+
+static acpi_status
+acpi_prt_callback (
+       acpi_handle             handle,
+       u32                     nesting_level,
+       void                    *context,
+       void                    **return_value)
+{
+       acpi_status             status = AE_OK;
+       acpi_handle             prt_handle = NULL;
+       char                    pathname[PATHNAME_MAX] = {0};
+       acpi_buffer             buffer = {0, NULL};
+       acpi_pci_routing_table  *prt = NULL;
+       unsigned long           sta = 0;
+       struct acpi_pci_root_context *root = (struct acpi_pci_root_context *) context;
+       u8                      bus_number = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_prt_callback");
+
+       if (!root)
+               return_VALUE(AE_BAD_PARAMETER);
+
+       status = acpi_get_handle(handle, METHOD_NAME__PRT, &prt_handle);
+       if (ACPI_FAILURE(status))
+               return_VALUE(AE_OK);
+
+       buffer.length = sizeof(pathname);
+       buffer.pointer = pathname;
+       acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+
+       /* 
+        * Evalute _STA to see if the device containing this _PRT is present.
+        */
+       status = acpi_evaluate_integer(handle, METHOD_NAME__STA, NULL, &sta);
+       switch (status) {
+       case AE_OK:
+               if (!(sta & 0x01)) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                               "Found _PRT but device [%s] not present\n", 
+                               pathname));
+                       return_VALUE(AE_OK);
+               }
+               break;
+       case AE_NOT_FOUND:
+               /* assume present */
+               break;
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Error evaluating %s._STA [%s]\n", 
+                       pathname, acpi_format_exception(status)));
+               return_VALUE(status);
+       }
+
+       printk(KERN_INFO PREFIX "%s [%s._PRT]\n", ACPI_PCI_PRT_DEVICE_NAME, 
+               pathname);
+
+       /* 
+        * Evaluate this _PRT and add all entries to our global list.
+        */
+
+       buffer.length = 0;
+       buffer.pointer = NULL;
+       status = acpi_get_irq_routing_table(handle, &buffer);
+       if (status != AE_BUFFER_OVERFLOW) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Error evaluating _PRT [%s]\n", 
+                       acpi_format_exception(status)));
+               return_VALUE(status);
+       }
+
+       prt = kmalloc(buffer.length, GFP_KERNEL);
+       if (!prt)
+               return_VALUE(-ENOMEM);
+       memset(prt, 0, buffer.length);
+       buffer.pointer = prt;
+
+       status = acpi_get_irq_routing_table(handle, &buffer);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Error evaluating _PRT [%s]\n", 
+                       acpi_format_exception(status)));
+               kfree(buffer.pointer);
+               return_VALUE(status);
+       }
+
+       if (root->handle == handle)
+               bus_number = root->id.bus;
+       else {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Need to get subordinate bus number!\n"));
+               /* get the bus number for this device */
+       }
+
+       while (prt && (prt->length > 0)) {
+               acpi_prt_add_entry(handle, root->id.seg, bus_number, prt);
+               prt = (acpi_pci_routing_table*)((unsigned long)prt + prt->length);
+       }
+
+       return_VALUE(AE_OK);
+}
+
+
+/* --------------------------------------------------------------------------
+                                Driver Interface
+   -------------------------------------------------------------------------- */
+
+int
+acpi_pci_root_add (
+       struct acpi_device      *device)
+{
+       acpi_status             status = AE_OK;
+       struct acpi_pci_root_context *context = NULL;
+       unsigned long           temp = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_pci_root_add");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       context = kmalloc(sizeof(struct acpi_pci_root_context), GFP_KERNEL);
+       if (!context)
+               return_VALUE(-ENOMEM);
+       memset(context, 0, sizeof(struct acpi_pci_root_context));
+
+       context->handle = device->handle;
+       sprintf(acpi_device_name(device), "%s", ACPI_PCI_ROOT_DEVICE_NAME);
+       sprintf(acpi_device_class(device), "%s", ACPI_PCI_ROOT_CLASS);
+       acpi_driver_data(device) = context;
+
+       status = acpi_evaluate_integer(context->handle, METHOD_NAME__SEG, 
+               NULL, &temp);
+       if (ACPI_SUCCESS(status))
+               context->id.seg = temp;
+       else
+               context->id.seg = 0;
+
+       status = acpi_evaluate_integer(context->handle, METHOD_NAME__BBN, 
+               NULL, &temp);
+       if (ACPI_SUCCESS(status))
+               context->id.bus = temp;
+       else
+               context->id.bus = 0;
+
+       /* TBD: Evaluate _CRS for bus range of child P2P (bus min/max/len) */
+
+       printk(KERN_INFO PREFIX "%s [%s] (%02x:%02x)\n", 
+               acpi_device_name(device), acpi_device_bid(device),
+               context->id.seg, context->id.bus);
+
+       /*
+        * Scan all devices on this root bridge.  Note that this must occur
+        * now to get the correct bus number assignments for subordinate
+        * PCI-PCI bridges.
+        */
+       pci_scan_bus(context->id.bus, pci_root_ops, NULL);
+
+       /* Evaluate _PRT for this root bridge. */
+       acpi_prt_callback(context->handle, 0, context, NULL);
+
+       /* Evaluate all subordinate _PRTs. */
+       acpi_walk_namespace(ACPI_TYPE_DEVICE, context->handle, ACPI_UINT32_MAX, 
+               acpi_prt_callback, context, NULL);
+
+       return_VALUE(0);
+}
+
+
+int
+acpi_pci_root_remove (
+       struct acpi_device      *device,
+       int                     type)
+{
+       struct acpi_pci_dev_context *context = NULL;
+
+       if (!device)
+               return -EINVAL;
+
+       if (device->driver_data)
+               /* Root bridge */
+               kfree(device->driver_data);
+       else {
+               /* Standard PCI device */
+               context = acpi_driver_data(device);
+               if (context)
+                       kfree(context);
+       }
+
+       return 0;
+}
+
+
+int __init 
+acpi_pci_irq_init (void)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       acpi_object             arg = {ACPI_TYPE_INTEGER};
+       acpi_object_list        arg_list = {1, &arg};
+       int                     irq_model = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_pci_irq_init");
+
+       /* 
+        * Let the system know what interrupt model we are using by
+        * evaluating the \_PIC object, if exists.
+        */
+
+       result = acpi_get_interrupt_model(&irq_model);
+       if (0 != result)
+               return_VALUE(result);
+
+       arg.integer.value = irq_model;
+
+       status = acpi_evaluate_object(NULL, "\\_PIC", &arg_list, NULL);
+       if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PIC\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       return_VALUE(0);
+}
+
+
+int __init
+acpi_pci_root_init (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_pci_root_init");
+
+       acpi_prts.count = 0;
+       INIT_LIST_HEAD(&acpi_prts.entries);
+
+       result = acpi_pci_irq_init();
+       if (0 != result)
+               return_VALUE(result);
+
+       if (0 > acpi_bus_register_driver(&acpi_pci_root_driver))
+               return_VALUE(-ENODEV);
+
+       return_VALUE(0);
+}
+
+
+void __exit
+acpi_pci_root_exit (void)
+{
+       ACPI_FUNCTION_TRACE("acpi_pci_root_exit");
+
+       acpi_bus_unregister_driver(&acpi_pci_root_driver);
+}
+
diff --git a/drivers/acpi/acpi_power.c b/drivers/acpi/acpi_power.c
new file mode 100644 (file)
index 0000000..5921070
--- /dev/null
@@ -0,0 +1,610 @@
+/*
+ *  acpi_power.c - ACPI Bus Power Management ($Revision: 34 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include "acpi_bus.h"
+#include "acpi_drivers.h"
+
+
+#define _COMPONENT             ACPI_POWER_COMPONENT
+ACPI_MODULE_NAME               ("acpi_power")
+
+#define PREFIX                 "ACPI: "
+
+
+int acpi_power_add (struct acpi_device *device);
+int acpi_power_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_power_driver = {
+       name:                   ACPI_POWER_DRIVER_NAME,
+       class:                  ACPI_POWER_CLASS,
+       ids:                    ACPI_POWER_HID,
+       ops:                    {
+                                       add:    acpi_power_add,
+                                       remove: acpi_power_remove,
+                               },
+};
+
+struct acpi_power_resource
+{
+       acpi_handle             handle;
+       acpi_bus_id             name;
+       u32                     system_level;
+       u32                     order;
+       int                     state;
+       int                     references;
+};
+
+static struct list_head                acpi_power_resource_list;
+
+
+/* --------------------------------------------------------------------------
+                             Power Resource Management
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_power_get_context (
+       acpi_handle             handle,
+       struct acpi_power_resource **resource)
+{
+       int                     result = 0;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_power_get_context");
+
+       if (!resource)
+               return_VALUE(-ENODEV);
+
+       result = acpi_bus_get_device(handle, &device);
+       if (0 != result) {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error getting context [%p]\n",
+                       handle));
+               return_VALUE(result);
+       }
+
+       *resource = (struct acpi_power_resource *) acpi_driver_data(device);
+       if (!resource)
+               return_VALUE(-ENODEV);
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_power_get_state (
+       struct acpi_power_resource *resource)
+{
+       acpi_status             status = AE_OK;
+       unsigned long           sta = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_power_get_state");
+
+       if (!resource)
+               return_VALUE(-EINVAL);
+
+       status = acpi_evaluate_integer(resource->handle, "_STA", NULL, &sta);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       if (sta & 0x01)
+               resource->state = ACPI_POWER_RESOURCE_STATE_ON;
+       else
+               resource->state = ACPI_POWER_RESOURCE_STATE_OFF;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n",
+               resource->name, resource->state?"on":"off"));
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_power_get_list_state (
+       struct acpi_handle_list *list,
+       int                     *state)
+{
+       int                     result = 0;
+       struct acpi_power_resource *resource = NULL;
+       u32                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_power_get_list_state");
+
+       if (!list || !state)
+               return_VALUE(-EINVAL);
+
+       /* The state of the list is 'on' IFF all resources are 'on'. */
+
+       for (i=0; i<list->count; i++) {
+               result = acpi_power_get_context(list->handles[i], &resource);
+               if (0 != result)
+                       return_VALUE(result);
+               result = acpi_power_get_state(resource);
+               if (0 != result)
+                       return_VALUE(result);
+
+               *state = resource->state;
+
+               if (*state != ACPI_POWER_RESOURCE_STATE_ON)
+                       break;
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource list is %s\n",
+               *state?"on":"off"));
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_power_on (
+       acpi_handle             handle)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_device      *device = NULL;
+       struct acpi_power_resource *resource = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_power_on");
+
+       result = acpi_power_get_context(handle, &resource);
+       if (0 != result)
+               return_VALUE(result);
+
+       resource->references++;
+
+       if ((resource->references > 1) 
+               || (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n",
+                       resource->name));
+               return_VALUE(0);
+       }
+
+       status = acpi_evaluate(resource->handle, "_ON", NULL, NULL);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       result = acpi_power_get_state(resource);
+       if (0 != result)
+               return_VALUE(result);
+       if (resource->state != ACPI_POWER_RESOURCE_STATE_ON)
+               return_VALUE(-ENOEXEC);
+
+       /* Update the power resource's _device_ power state */
+       result = acpi_bus_get_device(resource->handle, &device);
+       if (0 != result)
+               return_VALUE(result);
+       device->power.state = ACPI_STATE_D0;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
+               resource->name));
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_power_off (
+       acpi_handle             handle)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_device      *device = NULL;
+       struct acpi_power_resource *resource = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_power_off");
+
+       result = acpi_power_get_context(handle, &resource);
+       if (0 != result)
+               return_VALUE(result);
+
+       if (resource->references)
+               resource->references--;
+
+       if (resource->references) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, 
+                       "Resource [%s] is still in use, dereferencing\n",
+                       device->pnp.bus_id));
+               return_VALUE(0);
+       }
+
+       if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n",
+                       device->pnp.bus_id));
+               return_VALUE(0);
+       }
+
+       status = acpi_evaluate(resource->handle, "_OFF", NULL, NULL);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       result = acpi_power_get_state(resource);
+       if (0 != result)
+               return_VALUE(result);
+       if (resource->state != ACPI_POWER_RESOURCE_STATE_OFF)
+               return_VALUE(-ENOEXEC);
+
+       /* Update the power resource's _device_ power state */
+       result = acpi_bus_get_device(resource->handle, &device);
+       if (0 != result)
+               return_VALUE(result);
+       device->power.state = ACPI_STATE_D3;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n",
+               resource->name));
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                             Device Power Management
+   -------------------------------------------------------------------------- */
+
+int
+acpi_power_get_inferred_state (
+       struct acpi_device      *device)
+{
+       int                     result = 0;
+       struct acpi_handle_list *list = NULL;
+       int                     list_state = 0;
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_power_get_inferred_state");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       device->power.state = ACPI_STATE_UNKNOWN;
+
+       /*
+        * We know a device's inferred power state when all the resources
+        * required for a given D-state are 'on'.
+        */
+       for (i=ACPI_STATE_D0; i<ACPI_STATE_D3; i++) {
+               list = &device->power.states[i].resources;
+               if (list->count < 1)
+                       continue;
+
+               result = acpi_power_get_list_state(list, &list_state);
+               if (0 != result)
+                       return_VALUE(result);
+
+               if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
+                       device->power.state = i;
+                       return_VALUE(0);
+               }
+       }
+
+       device->power.state = ACPI_STATE_D3;
+
+       return_VALUE(0);
+}
+
+
+int
+acpi_power_transition (
+       struct acpi_device      *device,
+       int                     state)
+{
+       int                     result = 0;
+       struct acpi_handle_list *cl = NULL;     /* Current Resources */
+       struct acpi_handle_list *tl = NULL;     /* Target Resources */
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_power_transition");
+
+       if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3))
+               return_VALUE(-EINVAL);
+
+       cl = &device->power.states[device->power.state].resources;
+       tl = &device->power.states[state].resources;
+
+       device->power.state = ACPI_STATE_UNKNOWN;
+
+       if (!cl->count && !tl->count) {
+               result = -ENODEV;
+               goto end;
+       }
+
+       /* TBD: Resources must be ordered. */
+
+       /*
+        * First we reference all power resources required in the target list
+        * (e.g. so the device doesn't loose power while transitioning).
+        */
+       for (i=0; i<tl->count; i++) {
+               result = acpi_power_on(tl->handles[i]);
+               if (0 != result)
+                       goto end;
+       }
+
+       device->power.state = state;
+
+       /*
+        * Then we dereference all power resources used in the current list.
+        */
+       for (i=0; i<cl->count; i++) {
+               result = acpi_power_off(cl->handles[i]);
+               if (0 != result)
+                       goto end;
+       }
+
+end:
+       if (0 != result)
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, 
+                       "Error transitioning device [%s] to D%d\n",
+                       device->pnp.bus_id, state));
+
+       return_VALUE(result);
+}
+
+
+/* --------------------------------------------------------------------------
+                              FS Interface (/proc)
+   -------------------------------------------------------------------------- */
+
+#include <linux/compatmac.h>
+#include <linux/proc_fs.h>
+
+struct proc_dir_entry          *acpi_power_dir = NULL;
+
+
+static int
+acpi_power_read_status (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_power_resource *resource = NULL;
+       char                    *p = page;
+       int                     len;
+
+       ACPI_FUNCTION_TRACE("acpi_power_read_status");
+
+       if (!data || (off != 0))
+               goto end;
+
+       resource = (struct acpi_power_resource *) data;
+
+       p += sprintf(p, "state:                   ");
+       switch (resource->state) {
+       case ACPI_POWER_RESOURCE_STATE_ON:
+               p += sprintf(p, "on\n");
+               break;
+       case ACPI_POWER_RESOURCE_STATE_OFF:
+               p += sprintf(p, "off\n");
+               break;
+       default:
+               p += sprintf(p, "unknown\n");
+               break;
+       }
+
+       p += sprintf(p, "system level:            S%d\n",
+               resource->system_level);
+       p += sprintf(p, "order:                   %d\n",
+               resource->order);
+       p += sprintf(p, "reference count:         %d\n",
+               resource->references);
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_power_add_fs (
+       struct acpi_device      *device)
+{
+       struct proc_dir_entry   *entry = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_power_add_fs");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       if (!acpi_power_dir) {
+               acpi_power_dir = proc_mkdir(ACPI_POWER_CLASS, acpi_root_dir);
+               if (!acpi_power_dir)
+                       return_VALUE(-ENODEV);
+       }
+
+       if (!acpi_device_dir(device)) {
+               acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+                       acpi_power_dir);
+               if (!acpi_device_dir(device))
+                       return_VALUE(-ENODEV);
+       }
+
+       /* 'status' [R] */
+       entry = create_proc_entry(ACPI_POWER_FILE_STATUS,
+               S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_POWER_FILE_STATUS));
+       else {
+               entry->read_proc = acpi_power_read_status;
+               entry->data = acpi_driver_data(device);
+       }
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_power_remove_fs (
+       struct acpi_device      *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_power_remove_fs");
+
+       if (!acpi_power_dir)
+               return_VALUE(-ENODEV);
+
+       if (acpi_device_dir(device))
+               remove_proc_entry(acpi_device_bid(device), acpi_power_dir);
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                                Driver Interface
+   -------------------------------------------------------------------------- */
+
+int
+acpi_power_add (
+       struct acpi_device      *device)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_power_resource *resource = NULL;
+       acpi_object             acpi_object;
+       acpi_buffer             buffer = {sizeof(acpi_object), &acpi_object};
+
+       ACPI_FUNCTION_TRACE("acpi_power_add");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       resource = kmalloc(sizeof(struct acpi_power_resource), GFP_KERNEL);
+       if (!resource)
+               return_VALUE(-ENOMEM);
+       memset(resource, 0, sizeof(struct acpi_power_resource));
+
+       resource->handle = device->handle;
+       sprintf(resource->name, "%s", device->pnp.bus_id);
+       sprintf(acpi_device_name(device), "%s", ACPI_POWER_DEVICE_NAME);
+       sprintf(acpi_device_class(device), "%s", ACPI_POWER_CLASS);
+       acpi_driver_data(device) = resource;
+
+       /* Evalute the object to get the system level and resource order. */
+       status = acpi_evaluate_object(resource->handle, NULL, NULL, &buffer);
+       if (ACPI_FAILURE(status)) {
+               result = -ENODEV;
+               goto end;
+       }
+       resource->system_level = acpi_object.power_resource.system_level;
+       resource->order = acpi_object.power_resource.resource_order;
+
+       result = acpi_power_get_state(resource);
+       if (0 != result)
+               goto end;
+
+       switch (resource->state) {
+       case ACPI_POWER_RESOURCE_STATE_ON:
+               device->power.state = ACPI_STATE_D0;
+               break;
+       case ACPI_POWER_RESOURCE_STATE_OFF:
+               device->power.state = ACPI_STATE_D3;
+               break;
+       default:
+               device->power.state = ACPI_STATE_UNKNOWN;
+               break;
+       }
+
+       result = acpi_power_add_fs(device);
+       if (0 != result)
+               goto end;
+       
+       printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device),
+               acpi_device_bid(device), resource->state?"on":"off");
+
+end:
+       if (0 != result)
+               kfree(resource);
+       
+       return_VALUE(result);
+}
+
+
+int
+acpi_power_remove (
+       struct acpi_device      *device,
+       int                     type)
+{
+       struct acpi_power_resource *resource = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_power_remove");
+
+       if (!device || !acpi_driver_data(device))
+               return_VALUE(-EINVAL);
+
+       resource = (struct acpi_power_resource *) acpi_driver_data(device);
+
+       acpi_power_remove_fs(device);
+
+       kfree(resource);
+
+       return_VALUE(0);
+}
+
+
+int __init
+acpi_power_init (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_power_init");
+
+       INIT_LIST_HEAD(&acpi_power_resource_list);
+
+       result = acpi_bus_register_driver(&acpi_power_driver);
+       if (0 > result) {
+               remove_proc_entry(ACPI_POWER_CLASS, acpi_root_dir);
+               return_VALUE(-ENODEV);
+       }
+
+       return_VALUE(0);
+}
+
+
+void __exit
+acpi_power_exit (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_power_exit");
+
+       /* TBD: Empty acpi_power_resource_list */
+
+       result = acpi_bus_unregister_driver(&acpi_power_driver);
+       if (0 == result)
+               remove_proc_entry(ACPI_POWER_CLASS, acpi_root_dir);
+
+       return_VOID;
+}
diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
new file mode 100644 (file)
index 0000000..4fd7efb
--- /dev/null
@@ -0,0 +1,1870 @@
+/*
+ * acpi_processor.c - ACPI Processor Driver ($Revision: 50 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *  TBD:
+ *     1. Make # power/performance states dynamic.
+ *     2. Includes support for _real_ performance states (not just throttle).
+ *     3. Support duty_cycle values that span bit 4.
+ *     4. Optimize by having scheduler determine business instead of
+ *         having us try to calculate it here.
+ *      5. Need C1 timing -- must modify kernel (IRQ handler) to get this.
+ *     6. Convert time values to ticks (initially) to avoid having to do
+ *         the math (acpi_get_timer_duration).
+ *      7. What is a good default value for the OS busy_metric?
+ *      8. Support both thermal and power limits.
+ *      9. Resolve PIIX4 BMISX errata issue (getting an I/O port value of 0).
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include "acpi_bus.h"
+#include "acpi_drivers.h"
+
+
+#define _COMPONENT             ACPI_PROCESSOR_COMPONENT
+ACPI_MODULE_NAME               ("acpi_processor")
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION(ACPI_PROCESSOR_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+#define PREFIX                         "ACPI: "
+
+
+#define ACPI_PROCESSOR_MAX_POWER       ACPI_C_STATE_COUNT
+#define ACPI_PROCESSOR_MAX_C2_LATENCY  100
+#define ACPI_PROCESSOR_MAX_C3_LATENCY  1000
+
+#define ACPI_PROCESSOR_MAX_PERFORMANCE 4
+
+#define ACPI_PROCESSOR_MAX_THROTTLING  16
+#define ACPI_PROCESSOR_MAX_THROTTLE    500     /* 50% */
+#define ACPI_PROCESSOR_MAX_DUTY_WIDTH  4
+
+const u32 POWER_OF_2[] = {1,2,4,8,16,32,64};
+
+#define ACPI_PROCESSOR_MAX_LIMIT       20
+
+static int acpi_processor_add (struct acpi_device *device);
+static int acpi_processor_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_processor_driver = {
+       name:                   ACPI_PROCESSOR_DRIVER_NAME,
+       class:                  ACPI_PROCESSOR_CLASS,
+       ids:                    ACPI_PROCESSOR_HID,
+       ops:                    {
+                                       add:    acpi_processor_add,
+                                       remove: acpi_processor_remove,
+                               },
+};
+
+/* Power Management */
+
+struct acpi_processor_cx_policy {
+       u32                     count;
+       int                     state;
+       struct {
+               u32                     time;
+               u32                     count;
+               u32                     bm;
+       }                       threshold;
+};
+
+struct acpi_processor_cx {
+       u8                      valid;
+       u32                     address;
+       u32                     latency;
+       u32                     power;
+       u32                     usage;
+       struct acpi_processor_cx_policy promotion;
+       struct acpi_processor_cx_policy demotion;
+};
+
+struct acpi_processor_power {
+       int                     state;
+       int                     default_state;
+       u32                     bm_activity;
+       u32                     busy_metric;
+       struct acpi_processor_cx states[ACPI_PROCESSOR_MAX_POWER];
+};
+
+/* Performance Management */
+
+struct acpi_processor_px {
+       u8                      valid;
+       u32                     core_frequency;
+       u32                     power;
+       u32                     transition_latency;
+       u32                     bus_master_latency;
+       u32                     control;
+       u32                     status;
+};
+
+struct acpi_processor_performance {
+       int                     state;
+       int                     state_count;
+       struct acpi_processor_px states[ACPI_PROCESSOR_MAX_PERFORMANCE];
+};
+
+
+/* Throttling Control */
+
+struct acpi_processor_tx {
+       u8                      valid;
+       u16                     power;
+       u16                     performance;
+};
+
+struct acpi_processor_throttling {
+       int                     state;
+       u32                     address;
+       u8                      duty_offset;
+       u8                      duty_width;
+       int                     state_count;
+       struct acpi_processor_tx states[ACPI_PROCESSOR_MAX_THROTTLING];
+};
+
+/* Limit Interface */
+
+struct acpi_processor_lx {
+       u8                      valid;
+       u16                     performance;
+       int                     px;
+       int                     tx;
+};
+
+struct acpi_processor_limit {
+       int                     state;
+       int                     state_count;
+       struct {
+               u8                      valid;
+               u16                     performance;
+               int                     px;
+               int                     tx;
+       }                       states[ACPI_PROCESSOR_MAX_LIMIT];
+};
+
+struct acpi_processor_flags {
+       u8                      bm_control:1;
+       u8                      power:1;
+       u8                      performance:1;
+       u8                      throttling:1;
+       u8                      limit:1;
+       u8                      reserved:3;
+};
+
+struct acpi_processor_errata {
+       struct {
+               u8                      reverse_throttle;
+               u32                     bmisx;
+       }                       piix4;
+};
+
+struct acpi_processor {
+       acpi_handle             handle;
+       u32                     acpi_id;
+       u32                     id;
+       struct acpi_processor_flags flags;
+       struct acpi_processor_errata errata;
+       struct acpi_processor_power power;
+       struct acpi_processor_performance performance;
+       struct acpi_processor_throttling throttling;
+       struct acpi_processor_limit limit;
+};
+
+
+static u8                      acpi_processor_smp = 0;
+
+
+/* --------------------------------------------------------------------------
+                                Errata Handling
+   -------------------------------------------------------------------------- */
+
+int
+acpi_processor_get_errata (
+       struct acpi_processor   *pr)
+{
+       struct pci_dev          *dev = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_errata");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       /*
+        * PIIX4
+        * -----
+        */
+       dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
+               PCI_DEVICE_ID_INTEL_82371AB_3, PCI_ANY_ID, PCI_ANY_ID, dev);
+       if (dev) {
+               u8              rev = 0;
+
+               pci_read_config_byte(dev, PCI_REVISION_ID, &rev);
+
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PIIX4 ACPI rev %d\n", rev));
+
+               switch (rev) {
+
+               case 0:         /* PIIX4 A-step */
+               case 1:         /* PIIX4 B-step */
+                       /*
+                        * Workaround for reverse-notation on throttling states
+                        * used by early PIIX4 models.
+                        */
+                       pr->errata.piix4.reverse_throttle = 1;
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                               "Reverse-throttle errata enabled\n"));
+
+               case 2:         /* PIIX4E */
+               case 3:         /* PIIX4M */
+                       /*
+                        * Workaround for errata #18 "C3 Power State/BMIDE and
+                        * Type-F DMA Livelock" from the July 2001 PIIX4
+                        * specification update.  Applies to all PIIX4 models.
+                        */
+                       /* TBD: Why is the bmisx value always ZERO? */
+                       pr->errata.piix4.bmisx = pci_resource_start(dev, 4);
+                       if (pr->errata.piix4.bmisx)
+                               ACPI_DEBUG_PRINT((ACPI_DB_INFO, 
+                                       "BM-IDE errata enabled\n"));
+                       break;
+               }
+       }
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                                Power Management
+   -------------------------------------------------------------------------- */
+
+static struct acpi_processor *acpi_processor_list[NR_CPUS];
+static void (*pm_idle_save)(void) = NULL;
+
+static void
+acpi_processor_power_activate (
+       struct acpi_processor   *pr,
+       int                     state)
+{
+       if (!pr)
+               return;
+
+       pr->power.states[pr->power.state].promotion.count = 0;
+       pr->power.states[pr->power.state].demotion.count = 0;
+
+       /* Cleanup from old state. */
+       switch (pr->power.state) {
+       case ACPI_STATE_C3:
+               /* Disable bus master reload */
+               acpi_hw_bit_register_write(ACPI_BITREG_BUS_MASTER_RLD, 0, ACPI_MTX_DO_NOT_LOCK);
+               break;
+       }
+
+       /* Prepare to use new state. */
+       switch (state) {
+       case ACPI_STATE_C3:
+               /* Enable bus master reload */
+               acpi_hw_bit_register_write(ACPI_BITREG_BUS_MASTER_RLD, 1, ACPI_MTX_DO_NOT_LOCK);
+               break;
+       }
+
+       pr->power.state = state;
+
+       return;
+}
+
+
+static void
+acpi_processor_idle (void)
+{
+       struct acpi_processor   *pr = NULL;
+       struct acpi_processor_cx *cx = NULL;
+       int                     next_state = 0;
+       u32                     start_ticks = 0;
+       u32                     end_ticks = 0;
+       u32                     time_elapsed = 0;
+       static unsigned long    last_idle_jiffies = 0;
+
+       pr = acpi_processor_list[smp_processor_id()];
+       if (!pr)
+               return;
+
+       /*
+        * Interrupts must be disabled during bus mastering calculations and
+        * for C2/C3 transitions.
+        */
+       __cli();
+
+       next_state = pr->power.state;
+
+       /*
+        * Check OS Idleness:
+        * ------------------
+        * If the OS has been busy (hasn't called the idle handler in a while)
+        * then automatically demote to the default power state (e.g. C1).
+        *
+        * TBD: Optimize by having scheduler determine business instead
+        *      of having us try to calculate it here.
+        */
+       if (pr->power.state != pr->power.default_state) {
+               if ((jiffies - last_idle_jiffies) >= pr->power.busy_metric) {
+                       next_state = pr->power.default_state;
+                       if (next_state != pr->power.state)
+                               acpi_processor_power_activate(pr, next_state);
+               }
+       }
+
+       /*
+        * Log BM Activity:
+        * ----------------
+        * Read BM_STS and record its value for later use by C3 policy.
+        * (Note that we save the BM_STS values for the last 32 cycles).
+        */
+       if (pr->flags.bm_control) {
+               pr->power.bm_activity <<= 1;
+               if (acpi_hw_bit_register_read(ACPI_BITREG_BUS_MASTER_STATUS, ACPI_MTX_DO_NOT_LOCK)) {
+                       pr->power.bm_activity |= 1;
+                       acpi_hw_bit_register_write(ACPI_BITREG_BUS_MASTER_STATUS,
+                               1, ACPI_MTX_DO_NOT_LOCK);
+               }
+               /*
+                * PIIX4 Errata:
+                * -------------
+                * This code is a workaround for errata #18 "C3 Power State/
+                * BMIDE and Type-F DMA Livelock" from the July '01 PIIX4
+                * specification update.  Note that BM_STS doesn't always
+                * reflect the true state of bus mastering activity; forcing
+                * us to manually check the BMIDEA bit of each IDE channel.
+                */
+               else if (pr->errata.piix4.bmisx) {
+                       if ((inb_p(pr->errata.piix4.bmisx + 0x02) & 0x01) ||
+                               (inb_p(pr->errata.piix4.bmisx + 0x0A) & 0x01))
+                               pr->power.bm_activity |= 1;
+               }
+       }
+
+       cx = &(pr->power.states[pr->power.state]);
+       cx->usage++;
+
+       /*
+        * Sleep:
+        * ------
+        * Invoke the current Cx state to put the processor to sleep.
+        */
+       switch (pr->power.state) {
+
+       case ACPI_STATE_C1:
+               /* Invoke C1. */
+               safe_halt();
+               /*
+                 * TBD: Can't get time duration while in C1, as resumes
+                *      go to an ISR rather than here.  Need to instrument
+                *      base interrupt handler.
+                */
+               time_elapsed = 0xFFFFFFFF;
+               break;
+
+       case ACPI_STATE_C2:
+               /* See how long we're asleep for */
+               start_ticks = inl(acpi_fadt.Xpm_tmr_blk.address);
+               /* Invoke C2 */
+               inb(pr->power.states[ACPI_STATE_C2].address);
+               /* Dummy op - must do something useless after P_LVL2 read */
+               end_ticks = inl(acpi_fadt.Xpm_tmr_blk.address);
+               /* Compute time elapsed */
+               end_ticks = inl(acpi_fadt.Xpm_tmr_blk.address);
+               /* Re-enable interrupts */
+               __sti();
+               /*
+                * Compute the amount of time asleep (in the Cx state).
+                * TBD: Convert to PM timer ticks initially to avoid having
+                *      to do the math (acpi_get_timer_duration).
+                */
+               acpi_get_timer_duration(start_ticks, end_ticks, &time_elapsed);
+               break;
+
+       case ACPI_STATE_C3:
+               /* Disable bus master arbitration */
+               acpi_hw_bit_register_write(ACPI_BITREG_ARB_DISABLE, 1, ACPI_MTX_DO_NOT_LOCK);
+               /* See how long we're asleep for */
+               start_ticks = inl(acpi_fadt.Xpm_tmr_blk.address);
+               /* Invoke C2 */
+               inb(pr->power.states[ACPI_STATE_C3].address);
+               /* Dummy op - must do something useless after P_LVL3 read */
+               end_ticks = inl(acpi_fadt.Xpm_tmr_blk.address);
+               /* Compute time elapsed */
+               end_ticks = inl(acpi_fadt.Xpm_tmr_blk.address);
+               /* Enable bus master arbitration */
+               acpi_hw_bit_register_write(ACPI_BITREG_ARB_DISABLE, 0, ACPI_MTX_DO_NOT_LOCK);
+               /* Re-enable interrupts */
+               __sti();
+               /*
+                * Compute the amount of time asleep (in the Cx state).
+                * TBD: Convert to PM timer ticks initially to avoid having
+                *      to do the math (acpi_get_timer_duration).
+                */
+               acpi_get_timer_duration(start_ticks, end_ticks, &time_elapsed);
+               break;
+
+       default:
+               __sti();
+               return;
+       }
+
+       /*
+        * Promotion?
+        * ----------
+        * Track the number of longs (time asleep is greater than threshold)
+        * and promote when the count threshold is reached.  Note that bus
+        * mastering activity may prevent promotions.
+        */
+       if (cx->promotion.state) {
+               if (time_elapsed >= cx->promotion.threshold.time) {
+                       cx->promotion.count++;
+                       cx->demotion.count = 0;
+                       if (cx->promotion.count >= cx->promotion.threshold.count) {
+                               if (pr->flags.bm_control) {
+                                       if (!(pr->power.bm_activity & cx->promotion.threshold.bm))
+                                               next_state = cx->promotion.state;
+                               }
+                               else
+                                       next_state = cx->promotion.state;
+                       }
+               }
+       }
+
+       /*
+        * Demotion?
+        * ---------
+        * Track the number of shorts (time asleep is less than time threshold)
+        * and demote when the usage threshold is reached.  Note that bus
+        * mastering activity may cause immediate demotions.
+        */
+       if (cx->demotion.state) {
+               if (time_elapsed < cx->demotion.threshold.time) {
+                       cx->demotion.count++;
+                       cx->promotion.count = 0;
+                       if (cx->demotion.count >= cx->demotion.threshold.count)
+                               next_state = cx->demotion.state;
+               }
+               if (pr->flags.bm_control) {
+                       if (pr->power.bm_activity & cx->demotion.threshold.bm)
+                               next_state = cx->demotion.state;
+               }
+       }
+
+       /*
+        * New Cx State?
+        * -------------
+        * If we're going to start using a new Cx state we must clean up
+        * from the previous and prepare to use the new.
+        */
+       if (next_state != pr->power.state)
+               acpi_processor_power_activate(pr, next_state);
+
+       /*
+        * Track OS Idleness:
+        * ------------------
+        * Record a jiffies timestamp to compute time elapsed between calls
+        * to the idle handler.
+        */
+       last_idle_jiffies = jiffies;
+
+       return;
+}
+
+
+static int
+acpi_processor_set_power_policy (
+       struct acpi_processor   *pr)
+{
+       ACPI_FUNCTION_TRACE("acpi_processor_set_power_policy");
+
+       /*
+        * This function sets the default Cx state policy (OS idle handler).
+        * Our scheme is to promote quickly to C2 but more conservatively
+        * to C3.  We're favoring C2  for its characteristics of low latency
+        * (quick response), good power savings, and ability to allow bus
+        * mastering activity.  Note that the Cx state policy is completely
+        * customizable and can be altered dynamically.
+        */
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       /*
+        * The Busy Metric is used to determine when the OS has been busy
+        * and thus when policy should return to using the default Cx state
+        * (e.g. C1).  On Linux we use the number of jiffies (scheduler
+        * quantums) that transpire between calls to the idle handler.
+        *
+        * TBD: What is a good value for the OS busy_metric?
+        */
+       pr->power.busy_metric = 2;
+
+       /*
+        * C0/C1
+        * -----
+        */
+       if (pr->power.states[ACPI_STATE_C1].valid) {
+               pr->power.state = ACPI_STATE_C1;
+               pr->power.default_state = ACPI_STATE_C1;
+       }
+       else {
+               pr->power.state = ACPI_STATE_C0;
+               pr->power.default_state = ACPI_STATE_C0;
+               return_VALUE(0);
+       }
+
+       /*
+        * C1/C2
+        * -----
+        * Set the default C1 promotion and C2 demotion policies, where we
+        * promote from C1 to C2 anytime we're asleep in C1 for longer than
+        * two times the C2 latency (to amortize cost of transitions). Demote
+        * from C2 to C1 anytime we're asleep in C2 for less than this time.
+        */
+       if (pr->power.states[ACPI_STATE_C2].valid) {
+               pr->power.states[ACPI_STATE_C1].promotion.threshold.count = 10;
+               pr->power.states[ACPI_STATE_C1].promotion.threshold.time =
+                       (2 * pr->power.states[ACPI_STATE_C2].latency);
+               pr->power.states[ACPI_STATE_C1].promotion.state = ACPI_STATE_C2;
+
+               pr->power.states[ACPI_STATE_C2].demotion.threshold.count = 1;
+               pr->power.states[ACPI_STATE_C2].demotion.threshold.time =
+                       (2 * pr->power.states[ACPI_STATE_C2].latency);
+               pr->power.states[ACPI_STATE_C2].demotion.state = ACPI_STATE_C1;
+       }
+
+       /*
+        * C2/C3
+        * -----
+        * Set default C2 promotion and C3 demotion policies, where we promote
+        * from C2 to C3 after 4 cycles (0x0F) of no bus mastering activity
+        * (while maintaining sleep time criteria).  Demote immediately on a
+        * short or whenever bus mastering activity occurs.
+        */
+       if ((pr->power.states[ACPI_STATE_C2].valid) &&
+               (pr->power.states[ACPI_STATE_C3].valid)) {
+               pr->power.states[ACPI_STATE_C2].promotion.threshold.count = 1;
+               pr->power.states[ACPI_STATE_C2].promotion.threshold.time =
+                       (2 * pr->power.states[ACPI_STATE_C3].latency);
+               pr->power.states[ACPI_STATE_C2].promotion.threshold.bm = 0x0F;
+               pr->power.states[ACPI_STATE_C2].promotion.state = ACPI_STATE_C3;
+
+               pr->power.states[ACPI_STATE_C3].demotion.threshold.count = 1;
+               pr->power.states[ACPI_STATE_C3].demotion.threshold.time =
+                       (2 * pr->power.states[ACPI_STATE_C3].latency);
+               pr->power.states[ACPI_STATE_C3].demotion.threshold.bm = 0x0F;
+               pr->power.states[ACPI_STATE_C3].demotion.state = ACPI_STATE_C2;
+       }
+
+       return_VALUE(0);
+}
+
+
+int
+acpi_processor_get_power_info (
+       struct acpi_processor   *pr)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_power_info");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+               "lvl2[0x%08x] lvl3[0x%08x]\n",
+               pr->power.states[ACPI_STATE_C2].address,
+               pr->power.states[ACPI_STATE_C3].address));
+
+       /* TBD: Support ACPI 2.0 objects */
+
+       /*
+        * C0
+        * --
+        * This state exists only as filler in our array.
+        */
+       pr->power.states[ACPI_STATE_C0].valid = TRUE;
+
+       /*
+        * C1
+        * --
+        * ACPI requires C1 support for all processors.
+        *
+        * TBD: What about PROC_C1?
+        */
+       pr->power.states[ACPI_STATE_C1].valid = TRUE;
+
+       /*
+        * C2
+        * --
+        * We're (currently) only supporting C2 on UP systems.
+        *
+        * TBD: Support for C2 on MP (P_LVL2_UP).
+        */
+       if (pr->power.states[ACPI_STATE_C2].address) {
+               pr->power.states[ACPI_STATE_C2].latency = acpi_fadt.plvl2_lat;
+               if (acpi_fadt.plvl2_lat > ACPI_PROCESSOR_MAX_C2_LATENCY)
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                               "C2 latency too large [%d]\n",
+                               acpi_fadt.plvl2_lat));
+               else if (!acpi_processor_smp)
+                       pr->power.states[ACPI_STATE_C2].valid = TRUE;
+       }
+
+       /*
+        * C3
+        * --
+        * We're (currently) only supporting C3 on UP systems that include
+        * bus mastering arbitration control.  Note that this method of
+        * maintaining cache coherency (disabling of bus mastering) cannot be
+        * used on SMP systems, and flushing caches (e.g. WBINVD) is simply
+        * too costly (at this time).
+        */
+       if (pr->power.states[ACPI_STATE_C3].address) {
+               pr->power.states[ACPI_STATE_C3].latency = acpi_fadt.plvl3_lat;
+               if (acpi_fadt.plvl3_lat > ACPI_PROCESSOR_MAX_C3_LATENCY)
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                               "C3 latency too large [%d]\n", 
+                               acpi_fadt.plvl3_lat));
+               else if (!acpi_processor_smp && pr->flags.bm_control)
+                       pr->power.states[ACPI_STATE_C3].valid = 1;
+       }
+
+       /*
+        * Set Default Policy
+        * ------------------
+        * Now that we know which state are supported, set the default
+        * policy.  Note that this policy can be changed dynamically
+        * (e.g. encourage deeper sleeps to conserve battery life when
+        * not on AC).
+        */
+       result = acpi_processor_set_power_policy(pr);
+       if (0 != result)
+               return_VALUE(result);
+
+       /*
+        * If this processor supports C2 or C3 we denote it as being 'power
+        * manageable'.  Note that there's really no policy involved for
+        * when only C1 is supported.
+        */
+       if (pr->power.states[ACPI_STATE_C2].valid
+               || pr->power.states[ACPI_STATE_C3].valid)
+               pr->flags.power = 1;
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                              Performance Management
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_processor_get_performance (
+       struct acpi_processor   *pr)
+{
+       ACPI_FUNCTION_TRACE("acpi_processor_get_performance_state");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       if (!pr->flags.performance)
+               return_VALUE(0);
+
+       /* TBD */
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_processor_set_performance (
+       struct acpi_processor   *pr,
+       int                     state)
+{
+       ACPI_FUNCTION_TRACE("acpi_processor_set_performance_state");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       if (!pr->flags.performance)
+               return_VALUE(-ENODEV);
+
+       if (state >= pr->performance.state_count)
+               return_VALUE(-ENODEV);
+
+       if (state == pr->performance.state)
+               return_VALUE(0);
+
+       /* TBD */
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_processor_get_performance_info (
+       struct acpi_processor   *pr)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_performance_info");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       /* TBD: Support ACPI 2.0 objects */
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                              Throttling Control
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_processor_get_throttling (
+       struct acpi_processor   *pr)
+{
+       int                     state = 0;
+       u32                     value = 0;
+       u32                     duty_mask = 0;
+       u32                     duty_value = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_throttling");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       if (!pr->flags.throttling)
+               return_VALUE(-ENODEV);
+
+       pr->throttling.state = 0;
+
+       __cli();
+
+       duty_mask = pr->throttling.state_count - 1;
+       duty_mask <<= pr->throttling.duty_offset;
+
+       value = inb(pr->throttling.address);
+
+       /*
+        * Compute the current throttling state when throttling is enabled
+        * (bit 4 is on).  Note that the reverse_throttling flag indicates
+        * that the duty_value is opposite of that specified by ACPI.
+        */
+       if (value & 0x10) {
+               duty_value = value & duty_mask;
+               duty_value >>= pr->throttling.duty_offset;
+
+               if (duty_value) {
+                       if (pr->errata.piix4.reverse_throttle)
+                               state = duty_value;
+                       else
+                               state = pr->throttling.state_count-duty_value;
+               }
+       }
+
+       pr->throttling.state = state;
+
+       __sti();
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Throttling state is T%d (%d%% throttling applied)\n",
+               state, pr->throttling.states[state].performance));
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_processor_set_throttling (
+       struct acpi_processor   *pr,
+       int                     state)
+{
+       u32                     value = 0;
+       u32                     duty_mask = 0;
+       u32                     duty_value = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_set_throttling");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       if ((state < 0) || (state > (pr->throttling.state_count - 1)))
+               return_VALUE(-EINVAL);
+
+       if (!pr->flags.throttling || !pr->throttling.states[state].valid)
+               return_VALUE(-ENODEV);
+
+       if (state == pr->throttling.state)
+               return_VALUE(0);
+
+       __cli();
+
+       /*
+        * Calculate the duty_value and duty_mask.  Note that the
+        * reverse_throttling flag indicates that the duty_value is
+        * opposite of that specified by ACPI.
+        */
+       if (state) {
+               if (pr->errata.piix4.reverse_throttle)
+                       duty_value = state;
+               else
+                       duty_value = pr->throttling.state_count - state;
+
+               duty_value <<= pr->throttling.duty_offset;
+
+               /* Used to clear all duty_value bits */
+               duty_mask = pr->performance.state_count - 1;
+               duty_mask <<= acpi_fadt.duty_offset;
+               duty_mask = ~duty_mask;
+       }
+
+       /*
+        * Disable throttling by writing a 0 to bit 4.  Note that we must
+        * turn it off before you can change the duty_value.
+        */
+       value = inb(pr->throttling.address);
+       if (value & 0x10) {
+               value &= 0xFFFFFFEF;
+               outl(value, pr->throttling.address);
+       }
+
+       /*
+        * Write the new duty_value and then enable throttling.  Note
+        * that a state value of 0 leaves throttling disabled.
+        */
+       if (state) {
+               value &= duty_mask;
+               value |= duty_value;
+               outl(value, pr->throttling.address);
+
+               value |= 0x00000010;
+               outl(value, pr->throttling.address);
+       }
+
+       pr->throttling.state = state;
+
+       __sti();
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Throttling state set to T%d (%d%%)\n",
+               state, (pr->throttling.states[state].performance?pr->throttling.states[state].performance/10:0)));
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_processor_get_throttling_info (
+       struct acpi_processor   *pr)
+{
+       int                     result = 0;
+       int                     step = 0;
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_throttling_info");
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+               "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n",
+               pr->throttling.address,
+               pr->throttling.duty_offset,
+               pr->throttling.duty_width));
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       /* TBD: Support ACPI 2.0 objects */
+
+       if (!pr->throttling.address) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling register\n"));
+               return_VALUE(0);
+       }
+       else if (!pr->throttling.duty_width) {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid duty_width\n"));
+               return_VALUE(-EFAULT);
+       }
+       /* TBD: Support duty_cycle values that span bit 4. */
+       else if ((pr->throttling.duty_offset
+               + pr->throttling.duty_width) > 4) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "duty_cycle spans bit 4\n"));
+               return_VALUE(0);
+       }
+
+       pr->throttling.state_count = POWER_OF_2[acpi_fadt.duty_width];
+
+       /*
+        * Compute state values. Note that throttling displays a linear power/
+        * performance relationship (at 50% performance the CPU will consume
+        * 50% power).  Values are in 1/10th of a percent to preserve accuracy.
+        */
+
+       step = (1000 / pr->throttling.state_count);
+
+       for (i=0; i<pr->throttling.state_count; i++) {
+               pr->throttling.states[i].performance = step * i;
+               pr->throttling.states[i].power = step * i;
+               pr->throttling.states[i].valid = 1;
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d throttling states\n", 
+               pr->throttling.state_count));
+
+       pr->flags.throttling = 1;
+
+       /*
+        * Disable throttling (if enabled).  We'll let subsequent policy (e.g. 
+        * thermal) decide to lower performance if it so chooses, but for now 
+        * we'll crank up the speed.
+        */
+
+       result = acpi_processor_get_throttling(pr);
+       if (0 != result)
+               goto end;
+
+       if (pr->throttling.state) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Disabling throttling (was T%d)\n", 
+                       pr->throttling.state));
+               result = acpi_processor_set_throttling(pr, 0);
+               if (0 != result)
+                       goto end;
+       }
+
+end:
+       if (0 != result)
+               pr->flags.throttling = 0;
+
+       return_VALUE(result);
+}
+
+
+/* --------------------------------------------------------------------------
+                                 Limit Interface
+   -------------------------------------------------------------------------- */
+
+int
+acpi_processor_set_limit (
+       acpi_handle             handle,
+       int                     type,
+       int                     *state)
+{
+       int                     result = 0;
+       struct acpi_processor   *pr = NULL;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_set_limit");
+
+       if (!state)
+               return_VALUE(-EINVAL);
+
+       result = acpi_bus_get_device(handle, &device);
+       if (0 != result)
+               return_VALUE(result);
+
+       pr = (struct acpi_processor *) acpi_driver_data(device);
+       if (!pr)
+               return_VALUE(-ENODEV);
+
+       if (!pr->flags.limit)
+               return_VALUE(-ENODEV);
+
+       switch (type) {
+       case ACPI_PROCESSOR_LIMIT_NONE:
+               *state = 0;
+               pr->limit.state = 0;
+               break;
+       case ACPI_PROCESSOR_LIMIT_INCREMENT:
+               if (*state == (pr->limit.state_count - 1)) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Already at maximum limit state\n"));
+                       return_VALUE(1);
+               }
+               *state = ++pr->limit.state;
+               break;
+       case ACPI_PROCESSOR_LIMIT_DECREMENT:
+               if (*state == 0) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Already at minimum limit state\n"));
+                       return_VALUE(1);
+               }
+               *state = --pr->limit.state;
+               break;
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid limit type [%d]\n",
+                       type));
+               *state = pr->limit.state;
+               return_VALUE(-EINVAL);
+               break;
+       }
+
+       if (pr->flags.performance) {
+               result = acpi_processor_set_performance(pr, 
+                       pr->limit.states[*state].px);
+               if (0 != result)
+                       goto end;
+       }
+
+       if (pr->flags.throttling) {
+               result = acpi_processor_set_throttling(pr, 
+                       pr->limit.states[*state].tx);
+               if (0 != result)
+                       goto end;
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d] limit now %d%% (P%d:T%d)\n",
+               pr->id,
+               pr->limit.states[*state].performance / 10,
+               pr->limit.states[*state].px,
+               pr->limit.states[*state].tx));
+
+end:
+       if (0 != result)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to set limit\n"));
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_processor_get_limit_info (
+       struct acpi_processor   *pr)
+{
+       int                     i = 0;
+       int                     px = 0;
+       int                     tx = 0;
+       int                     base_perf = 1000;
+       int                     throttle = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_limit_info");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       /*
+        * Limit
+        * -----
+        * Our default policy is to only use throttling at the lowest
+        * performance state.  This is enforced by adding throttling states 
+        * after perormance states.  We also only expose throttling states 
+        * less than the maximum throttle value (e.g. 50%).
+        */
+
+       if (pr->flags.performance) {
+               for (px=0; px<pr->performance.state_count; px++) {
+                       if (!pr->performance.states[px].valid)
+                               continue;
+                       i = pr->limit.state_count++;
+                       pr->limit.states[i].px = px;
+                       pr->limit.states[i].performance = (pr->performance.states[px].core_frequency / pr->performance.states[0].core_frequency) * 1000;
+                       pr->limit.states[i].valid = 1;
+               }
+               px--;
+               base_perf = pr->limit.states[i].performance;
+       }
+
+       if (pr->flags.throttling) {
+               for (tx=0; tx<pr->throttling.state_count; tx++) {
+                       if (!pr->throttling.states[tx].valid)
+                               continue;
+                       if (pr->throttling.states[tx].performance > ACPI_PROCESSOR_MAX_THROTTLE)
+                               continue;
+                       i = pr->limit.state_count++;
+                       pr->limit.states[i].px = px;
+                       pr->limit.states[i].tx = tx;
+                       throttle = (base_perf * pr->throttling.states[tx].performance) / 1000;
+                       pr->limit.states[i].performance = base_perf - throttle;
+                       pr->limit.states[i].valid = 1;
+               }
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d limit states\n", 
+               pr->limit.state_count));
+
+       if (pr->limit.state_count)
+               pr->flags.limit = 1;
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                              FS Interface (/proc)
+   -------------------------------------------------------------------------- */
+
+#include <linux/compatmac.h>
+#include <linux/proc_fs.h>
+
+struct proc_dir_entry          *acpi_processor_dir = NULL;
+
+static int
+acpi_processor_read_info (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_processor   *pr = (struct acpi_processor *) data;
+       char                    *p = page;
+       int                     len = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_read_info");
+
+       if (!pr || (off != 0))
+               goto end;
+
+       p += sprintf(p, "processor id:            %d\n",
+               pr->id);
+
+       p += sprintf(p, "acpi id:                 %d\n",
+               pr->acpi_id);
+
+       p += sprintf(p, "bus mastering control:   %s\n",
+               pr->flags.bm_control ? "yes" : "no");
+
+       p += sprintf(p, "power management:        %s\n",
+               pr->flags.power ? "yes" : "no");
+
+       p += sprintf(p, "throttling control:      %s\n",
+               pr->flags.throttling ? "yes" : "no");
+
+       p += sprintf(p, "performance management:  %s\n",
+               pr->flags.performance ? "yes" : "no");
+
+       p += sprintf(p, "limit interface:         %s\n",
+               pr->flags.limit ? "yes" : "no");
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_processor_read_power (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_processor   *pr = (struct acpi_processor *) data;
+       char                    *p = page;
+       int                     len = 0;
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_read_power");
+
+       if (!pr || (off != 0))
+               goto end;
+
+       p += sprintf(p, "active state:            C%d\n",
+               pr->power.state);
+
+       p += sprintf(p, "default state:           C%d\n",
+               pr->power.default_state);
+
+       p += sprintf(p, "bus master activity:     %08x\n",
+               pr->power.bm_activity);
+
+       p += sprintf(p, "states:\n");
+
+       for (i=1; i<ACPI_C_STATE_COUNT; i++) {
+
+               p += sprintf(p, "   %cC%d:                  ", 
+                       (i == pr->power.state?'*':' '), i);
+
+               if (!pr->power.states[i].valid) {
+                       p += sprintf(p, "<not supported>\n");
+                       continue;
+               }
+
+               if (pr->power.states[i].promotion.state)
+                       p += sprintf(p, "promotion[C%d] ",
+                               pr->power.states[i].promotion.state);
+               else
+                       p += sprintf(p, "promotion[--] ");
+
+               if (pr->power.states[i].demotion.state)
+                       p += sprintf(p, "demotion[C%d] ",
+                               pr->power.states[i].demotion.state);
+               else
+                       p += sprintf(p, "demotion[--] ");
+
+               p += sprintf(p, "latency[%03d] usage[%08d]\n",
+                       pr->power.states[i].latency,
+                       pr->power.states[i].usage);
+       }
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_processor_read_performance (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_processor   *pr = (struct acpi_processor *) data;
+       char                    *p = page;
+       int                     len = 0;
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_read_performance");
+
+       if (!pr || (off != 0))
+               goto end;
+
+       if (!pr->flags.performance) {
+               p += sprintf(p, "<not supported>\n");
+               goto end;
+       }
+
+       p += sprintf(p, "state count:             %d\n",
+               pr->performance.state_count);
+
+       p += sprintf(p, "active state:            P%d\n",
+               pr->performance.state);
+
+       p += sprintf(p, "states:\n");
+
+       for (i=0; i<pr->performance.state_count; i++)
+               p += sprintf(p, "   %cP%d:                %d Mhz, %d mW %s\n",
+                       (i == pr->performance.state?'*':' '), i,
+                       pr->performance.states[i].core_frequency,
+                       pr->performance.states[i].power,
+                       (pr->performance.states[i].valid?"":"(disabled)"));
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_processor_write_performance (
+        struct file            *file,
+        const char             *buffer,
+        unsigned long          count,
+        void                   *data)
+{
+       int                     result = 0;
+       struct acpi_processor   *pr = (struct acpi_processor *) data;
+       char                    state_string[12] = {'\0'};
+
+       ACPI_FUNCTION_TRACE("acpi_processor_write_performance");
+
+       if (!pr || (count > sizeof(state_string) - 1))
+               return_VALUE(-EINVAL);
+       
+       if (copy_from_user(state_string, buffer, count))
+               return_VALUE(-EFAULT);
+       
+       state_string[count] = '\0';
+       
+       result = acpi_processor_set_throttling(pr, 
+               simple_strtoul(state_string, NULL, 0));
+       if (0 != result)
+               return_VALUE(result);
+
+       return_VALUE(count);
+}
+
+
+static int
+acpi_processor_read_throttling (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_processor   *pr = (struct acpi_processor *) data;
+       char                    *p = page;
+       int                     len = 0;
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_read_throttling");
+
+       if (!pr || (off != 0))
+               goto end;
+
+       if (!(pr->throttling.state_count > 0)) {
+               p += sprintf(p, "<not supported>\n");
+               goto end;
+       }
+
+       p += sprintf(p, "state count:             %d\n",
+               pr->throttling.state_count);
+
+       p += sprintf(p, "active state:            T%d\n",
+               pr->throttling.state);
+
+       p += sprintf(p, "states:\n");
+
+       for (i=0; i<pr->throttling.state_count; i++)
+               p += sprintf(p, "   %cT%d:                  %02d%% %s\n",
+                       (i == pr->throttling.state?'*':' '), i,
+                       (pr->throttling.states[i].performance?pr->throttling.states[i].performance/10:0),
+                       (pr->throttling.states[i].valid?"":"(disabled)"));
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_processor_write_throttling (
+        struct file            *file,
+        const char             *buffer,
+        unsigned long          count,
+        void                   *data)
+{
+       int                     result = 0;
+       struct acpi_processor   *pr = (struct acpi_processor *) data;
+       char                    state_string[12] = {'\0'};
+
+       ACPI_FUNCTION_TRACE("acpi_processor_write_throttling");
+
+       if (!pr || (count > sizeof(state_string) - 1))
+               return_VALUE(-EINVAL);
+       
+       if (copy_from_user(state_string, buffer, count))
+               return_VALUE(-EFAULT);
+       
+       state_string[count] = '\0';
+       
+       result = acpi_processor_set_throttling(pr, 
+               simple_strtoul(state_string, NULL, 0));
+       if (0 != result)
+               return_VALUE(result);
+
+       return_VALUE(count);
+}
+
+
+static int
+acpi_processor_read_limit (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_processor   *pr = (struct acpi_processor *) data;
+       char                    *p = page;
+       int                     len = 0;
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_read_limit");
+
+       if (!pr || (off != 0))
+               goto end;
+
+       if (!pr->flags.limit) {
+               p += sprintf(p, "<not supported>\n");
+               goto end;
+       }
+
+       p += sprintf(p, "state count:             %d\n",
+               pr->limit.state_count);
+
+       p += sprintf(p, "active state:            L%d\n",
+               pr->limit.state);
+
+       p += sprintf(p, "states:\n");
+
+       for (i=0; i<pr->limit.state_count; i++)
+               p += sprintf(p, "   %cL%d:                  %02d%% [P%d:T%d] %s\n",
+                       (i == pr->limit.state?'*':' '),
+                       i,
+                       pr->limit.states[i].performance / 10,
+                       pr->limit.states[i].px,
+                       pr->limit.states[i].tx,
+                       pr->limit.states[i].valid?"":"(disabled)");
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_processor_write_limit (
+        struct file            *file,
+        const char             *buffer,
+        unsigned long          count,
+        void                   *data)
+{
+       int                     result = 0;
+       struct acpi_processor   *pr = (struct acpi_processor *) data;
+       char                    limit_string[12] = {'\0'};
+       int                     limit = 0;
+       int                     state = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_write_limit");
+
+       if (!pr || (count > sizeof(limit_string) - 1))
+               return_VALUE(-EINVAL);
+       
+       if (copy_from_user(limit_string, buffer, count))
+               return_VALUE(-EFAULT);
+       
+       limit_string[count] = '\0';
+
+       limit = simple_strtoul(limit_string, NULL, 0);
+       
+       result = acpi_processor_set_limit(pr->handle, limit, &state);
+       if (0 != result)
+               return_VALUE(result);
+
+       return_VALUE(count);
+}
+
+
+static int
+acpi_processor_add_fs (
+       struct acpi_device      *device)
+{
+       struct proc_dir_entry   *entry = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_add_fs");
+
+       if (!acpi_processor_dir) {
+               acpi_processor_dir = proc_mkdir(ACPI_PROCESSOR_CLASS, 
+                       acpi_root_dir);
+               if (!acpi_processor_dir)
+                       return_VALUE(-ENODEV);
+       }
+
+       if (!acpi_device_dir(device)) {
+               acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+                       acpi_processor_dir);
+               if (!acpi_device_dir(device))
+                       return_VALUE(-ENODEV);
+       }
+
+       /* 'info' [R] */
+       entry = create_proc_entry(ACPI_PROCESSOR_FILE_INFO,
+               S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_PROCESSOR_FILE_INFO));
+       else {
+               entry->read_proc = acpi_processor_read_info;
+               entry->data = acpi_driver_data(device);
+       }
+
+       /* 'power' [R] */
+       entry = create_proc_entry(ACPI_PROCESSOR_FILE_POWER,
+               S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_PROCESSOR_FILE_POWER));
+       else {
+               entry->read_proc = acpi_processor_read_power;
+               entry->data = acpi_driver_data(device);
+       }
+
+       /* 'performance' [R/W] */
+       entry = create_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
+               S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_PROCESSOR_FILE_PERFORMANCE));
+       else {
+               entry->read_proc = acpi_processor_read_performance;
+               entry->write_proc = acpi_processor_write_performance;
+               entry->data = acpi_driver_data(device);
+       }
+
+       /* 'throttling' [R/W] */
+       entry = create_proc_entry(ACPI_PROCESSOR_FILE_THROTTLING,
+               S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_PROCESSOR_FILE_THROTTLING));
+       else {
+               entry->read_proc = acpi_processor_read_throttling;
+               entry->write_proc = acpi_processor_write_throttling;
+               entry->data = acpi_driver_data(device);
+       }
+
+       /* 'thermal_limit' [R/W] */
+       entry = create_proc_entry(ACPI_PROCESSOR_FILE_LIMIT,
+               S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_PROCESSOR_FILE_LIMIT));
+       else {
+               entry->read_proc = acpi_processor_read_limit;
+               entry->write_proc = acpi_processor_write_limit;
+               entry->data = acpi_driver_data(device);
+       }
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_processor_remove_fs (
+       struct acpi_device      *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_processor_remove_fs");
+
+       if (!acpi_processor_dir)
+               return_VALUE(-ENODEV);
+
+       if (acpi_device_dir(device))
+               remove_proc_entry(acpi_device_bid(device), acpi_processor_dir);
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                                 Driver Interface
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_processor_get_info (
+       struct acpi_processor   *pr)
+{
+       acpi_status             status = 0;
+       acpi_object             object = {0};
+       acpi_buffer             buffer = {sizeof(acpi_object), &object};
+       static int              cpu_count = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_info");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+#ifdef CONFIG_SMP
+       if (smp_num_cpus > 1)
+               acpi_processor_smp = smp_num_cpus;
+#endif
+
+       acpi_processor_get_errata(pr);
+
+       /*
+        * Check to see if we have bus mastering arbitration control.  This
+        * is required for proper C3 usage (to maintain cache coherency).
+        */
+       if (acpi_fadt.V1_pm2_cnt_blk && acpi_fadt.pm2_cnt_len) {
+               pr->flags.bm_control = 1;
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Bus mastering arbitration control present\n"));
+       }
+       else
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "No bus mastering arbitration control\n"));
+
+       /*
+        * Evalute the processor object.  Note that it is common on SMP to
+        * have the first (boot) processor with a valid PBLK address while
+        * all others have a NULL address.
+        */
+       status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error evaluating processor object\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       /*
+        * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP.
+        *      >>> 'acpi_get_processor_id(acpi_id, &id)' in arch/xxx/acpi.c
+        */
+       pr->id = cpu_count++;
+       pr->acpi_id = object.processor.proc_id;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id, 
+               pr->acpi_id));
+
+       if (!object.processor.pblk_address)
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No PBLK (NULL address)\n"));
+       else if (object.processor.pblk_length < 6)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid PBLK length [%d]\n",
+                       object.processor.pblk_length));
+       else {
+               pr->throttling.address = object.processor.pblk_address;
+               pr->throttling.duty_offset = acpi_fadt.duty_offset;
+               pr->throttling.duty_width = acpi_fadt.duty_width;
+               pr->power.states[ACPI_STATE_C2].address =
+                       object.processor.pblk_address + 4;
+               pr->power.states[ACPI_STATE_C3].address =
+                       object.processor.pblk_address + 5;
+       }
+
+       acpi_processor_get_power_info(pr);
+       acpi_processor_get_performance_info(pr);
+       acpi_processor_get_throttling_info(pr);
+       acpi_processor_get_limit_info(pr);
+
+       return_VALUE(0);
+}
+
+
+static void
+acpi_processor_notify (
+       acpi_handle             handle,
+       u32                     event,
+       void                    *data)
+{
+       struct acpi_processor   *pr = (struct acpi_processor *) data;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_notify");
+
+       if (!pr)
+               return_VOID;
+
+       if (0 != acpi_bus_get_device(pr->handle, &device))
+               return_VOID;
+
+       switch (event) {
+       case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
+       case ACPI_PROCESSOR_NOTIFY_POWER:
+               acpi_bus_generate_event(device, event, 0);
+               break;
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Unsupported event [0x%x]\n", event));
+               break;
+       }
+
+       return_VOID;
+}
+
+
+static int
+acpi_processor_add (
+       struct acpi_device      *device)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_processor   *pr = NULL;
+       u32                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_add");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       pr = kmalloc(sizeof(struct acpi_processor), GFP_KERNEL);
+       if (!pr)
+               return_VALUE(-ENOMEM);
+       memset(pr, 0, sizeof(struct acpi_processor));
+
+       pr->handle = device->handle;
+       sprintf(acpi_device_name(device), "%s", ACPI_PROCESSOR_DEVICE_NAME);
+       sprintf(acpi_device_class(device), "%s", ACPI_PROCESSOR_CLASS);
+       acpi_driver_data(device) = pr;
+
+       result = acpi_processor_get_info(pr);
+       if (0 != result)
+               goto end;
+
+       result = acpi_processor_add_fs(device);
+       if (0 != result)
+               goto end;
+
+       /*
+        * TBD: Fix notify handler installation for processors.
+        *
+       status = acpi_install_notify_handler(pr->handle, ACPI_DEVICE_NOTIFY, 
+               acpi_processor_notify, pr);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 
+                       "Error installing notify handler\n"));
+               result = -ENODEV;
+               goto end;
+       }
+       */
+
+       acpi_processor_list[pr->id] = pr;
+
+       /*
+        * Set Idle Handler
+        * ----------------
+        * Install the idle handler if power management (states other than C1)
+        * is supported.  Note that the default idle handler (default_idle)
+        * will be used on platforms that only support C1.
+        */
+       if ((pr->id == 0) && (pr->flags.power)) {
+               pm_idle_save = pm_idle;
+               pm_idle = acpi_processor_idle;
+       }
+       
+       printk(KERN_INFO PREFIX "%s [%s] (supports",
+               acpi_device_name(device), acpi_device_bid(device));
+       for (i=1; i<ACPI_C_STATE_COUNT; i++)
+               if (pr->power.states[i].valid)
+                       printk(" C%d", i);
+       if (pr->flags.performance)
+               printk(", %d performance states", pr->performance.state_count);
+       if (pr->flags.throttling)
+               printk(", %d throttling states", pr->throttling.state_count);
+       if (pr->errata.piix4.bmisx)
+               printk(", PIIX4 errata");
+       printk(")\n");
+
+end:
+       if (0 != result) {
+               acpi_processor_remove_fs(device);
+               kfree(pr);
+       }
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_processor_remove (
+       struct acpi_device      *device,
+       int                     type)
+{
+       acpi_status             status = AE_OK;
+       struct acpi_processor   *pr = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_remove");
+
+       if (!device || !acpi_driver_data(device))
+               return_VALUE(-EINVAL);
+
+       pr = (struct acpi_processor *) acpi_driver_data(device);
+
+       /*
+       status = acpi_remove_notify_handler(pr->handle, ACPI_DEVICE_NOTIFY, 
+               acpi_processor_notify);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 
+                       "Error removing notify handler\n"));
+               return_VALUE(-ENODEV);
+       }
+       */
+
+       /* Unregister the idle handler when processor #0 is removed. */
+       if (pr->id == 0)
+               pm_idle = pm_idle_save;
+
+       acpi_processor_remove_fs(device);
+
+       acpi_processor_list[pr->id] = NULL;
+
+       kfree(pr);
+
+       return_VALUE(0);
+}
+
+
+static int __init
+acpi_processor_init (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_init");
+
+       memset(&acpi_processor_list, 0, sizeof(acpi_processor_list));
+
+       result = acpi_bus_register_driver(&acpi_processor_driver);
+       if (0 > result)
+               return_VALUE(-ENODEV);
+
+       return_VALUE(0);
+}
+
+
+static void __exit
+acpi_processor_exit (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_exit");
+
+       result = acpi_bus_unregister_driver(&acpi_processor_driver);
+       if (0 == result)
+               remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir);
+
+       return_VOID;
+}
+
+
+module_init(acpi_processor_init);
+module_exit(acpi_processor_exit);
diff --git a/drivers/acpi/acpi_system.c b/drivers/acpi/acpi_system.c
new file mode 100644 (file)
index 0000000..e69da06
--- /dev/null
@@ -0,0 +1,1232 @@
+/*
+ *  acpi_system.c - ACPI System Driver ($Revision: 40 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <asm/uaccess.h>
+#include <asm/acpi.h>
+#include "acpi_bus.h"
+#include "acpi_drivers.h"
+
+#ifdef CONFIG_X86
+#ifdef CONFIG_ACPI_SLEEP
+#include <linux/mc146818rtc.h>
+#endif
+#endif
+
+
+#define _COMPONENT             ACPI_SYSTEM_COMPONENT
+ACPI_MODULE_NAME               ("acpi_system")
+
+#define PREFIX                 "ACPI: "
+
+extern FADT_DESCRIPTOR         acpi_fadt;
+
+static int acpi_system_add (struct acpi_device *device);
+static int acpi_system_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_system_driver = {
+       name:                   ACPI_SYSTEM_DRIVER_NAME,
+       class:                  ACPI_SYSTEM_CLASS,
+       ids:                    ACPI_SYSTEM_HID,
+       ops:                    {
+                                       add:    acpi_system_add,
+                                       remove: acpi_system_remove
+                               },
+};
+
+struct acpi_system
+{
+       acpi_handle             handle;
+       u8                      states[ACPI_S_STATE_COUNT];
+};
+
+/* Global vars for handling event proc entry */
+static spinlock_t              acpi_system_event_lock = SPIN_LOCK_UNLOCKED;
+int                            event_is_open = 0;
+extern struct list_head                acpi_bus_event_list;
+extern wait_queue_head_t       acpi_bus_event_queue;
+
+/* --------------------------------------------------------------------------
+                                  System Sleep
+   -------------------------------------------------------------------------- */
+
+#ifdef CONFIG_PM
+
+static void
+acpi_power_off (void)
+{
+       acpi_enter_sleep_state_prep(ACPI_STATE_S5);
+       acpi_disable_irqs();
+       acpi_enter_sleep_state(ACPI_STATE_S5);
+       acpi_disable_irqs();
+}
+
+#endif /*CONFIG_PM*/
+
+
+#ifdef CONFIG_ACPI_SLEEP
+
+/**
+ * acpi_system_restore_state - OS-specific restoration of state
+ * @state:     sleep state we're exiting
+ *
+ * Note that if we're coming back from S4, the memory image should have already
+ * been loaded from the disk and is already in place. (Otherwise how else would we
+ * be here?).
+ */
+acpi_status
+acpi_system_restore_state (
+       u32                     state)
+{
+       /* restore processor state
+        * We should only be here if we're coming back from STR or STD.
+        * And, in the case of the latter, the memory image should have already
+        * been loaded from disk.
+        */
+       if (state > ACPI_STATE_S1)
+               acpi_restore_state_mem();
+
+       /* wait for power to come back */
+       mdelay(10);
+#ifdef HAVE_NEW_DEVICE_MODEL
+       /* turn all the devices back on */
+       device_resume(RESUME_POWER_ON);
+
+       /* enable interrupts once again */
+       acpi_enable_irqs();
+
+       /* restore device context */
+       device_resume(RESUME_RESTORE_STATE);
+#endif
+       return AE_OK;
+}
+
+/**
+ * acpi_system_save_state - save OS specific state and power down devices
+ * @state:     sleep state we're entering.
+ *
+ * This handles saving all context to memory, and possibly disk.
+ * First, we call to the device driver layer to save device state.
+ * Once we have that, we save whatevery processor and kernel state we
+ * need to memory.
+ * If we're entering S4, we then write the memory image to disk.
+ *
+ * Only then is it safe for us to power down devices, since we may need
+ * the disks and upstream buses to write to.
+ */
+acpi_status
+acpi_system_save_state(
+       u32                     state)
+{
+       int                     error = 0;
+
+#ifdef HAVE_NEW_DEVICE_MODEL
+       /* Send notification to devices that they will be suspended.
+        * If any device or driver cannot make the transition, either up
+        * or down, we'll get an error back.
+        */
+       error = device_suspend(state, SUSPEND_NOTIFY);
+       if (error)
+               return AE_ERROR;
+#endif
+       if (state < ACPI_STATE_S5) {
+
+#ifdef HAVE_NEW_DEVICE_MODEL
+               /* Tell devices to stop I/O and actually save their state.
+                * It is theoretically possible that something could fail,
+                * so handle that gracefully..
+                */
+               error = device_suspend(state, SUSPEND_SAVE_STATE);
+               if (error) {
+                       /* tell devices to restore state if they have
+                        * it saved and to start taking I/O requests.
+                        */
+                       device_resume(RESUME_RESTORE_STATE);
+                       return error;
+               }
+#endif
+
+               /* flush caches */
+               wbinvd();
+
+               /* Do arch specific saving of state. */
+               if (state > ACPI_STATE_S1) {
+                       error = acpi_save_state_mem();
+
+                       if (!error && (state == ACPI_STATE_S4))
+                               error = acpi_save_state_disk();
+
+#ifdef HAVE_NEW_DEVICE_MODEL
+                       if (error) {
+                               device_resume(RESUME_RESTORE_STATE);
+                               return error;
+                       }
+#endif
+               }
+       }
+#ifdef HAVE_NEW_DEVICE_MODEL
+       /* disable interrupts
+        * Note that acpi_suspend -- our caller -- will do this once we return.
+        * But, we want it done early, so we don't get any suprises during
+        * the device suspend sequence.
+        */
+       acpi_disable_irqs();
+
+       /* Unconditionally turn off devices.
+        * Obvious if we enter a sleep state.
+        * If entering S5 (soft off), this should put devices in a
+        * quiescent state.
+        */
+       error = device_suspend(state, SUSPEND_POWER_DOWN);
+
+       /* We're pretty screwed if we got an error from this.
+        * We try to recover by simply calling our own restore_state
+        * function; see above for definition.
+        *
+        * If it's S5 though, go through with it anyway..
+        */
+       if (error && state != ACPI_STATE_S5)
+               acpi_system_restore_state(state);
+#endif
+       return error ? AE_ERROR : AE_OK;
+}
+
+
+/****************************************************************************
+ *
+ * FUNCTION:    acpi_system_suspend
+ *
+ * PARAMETERS:  %state: Sleep state to enter.
+ *
+ * RETURN:      acpi_status, whether or not we successfully entered and
+ *              exited sleep.
+ *
+ * DESCRIPTION: Perform OS-specific action to enter sleep state.
+ *              This is the final step in going to sleep, per spec.  If we
+ *              know we're coming back (i.e. not entering S5), we save the
+ *              processor flags. [ We'll have to save and restore them anyway,
+ *              so we use the arch-agnostic save_flags and restore_flags
+ *              here.]  We then set the place to return to in arch-specific
+ *              globals using arch_set_return_point. Finally, we call the
+ *              ACPI function to write the proper values to I/O ports.
+ *
+ ****************************************************************************/
+
+acpi_status
+acpi_system_suspend(
+       u32                     state)
+{
+       acpi_status             status = AE_ERROR;
+       unsigned long           flags = 0;
+
+       save_flags(flags);
+       
+       switch (state)
+       {
+       case ACPI_STATE_S1:
+               /* do nothing */
+               break;
+
+       case ACPI_STATE_S2:
+       case ACPI_STATE_S3:
+               acpi_save_register_state((unsigned long)&&acpi_sleep_done);
+               break;
+       }
+
+       barrier();
+       status = acpi_enter_sleep_state(state);
+
+acpi_sleep_done:
+
+       acpi_restore_register_state();
+       restore_flags(flags);
+
+       return status;
+}
+
+
+/**
+ * acpi_suspend - OS-agnostic system suspend/resume support (S? states)
+ * @state:     state we're entering
+ *
+ */
+acpi_status
+acpi_suspend (
+       u32                     state)
+{
+       acpi_status status;
+
+       /* get out if state is invalid */
+       if (state < ACPI_STATE_S1 || state > ACPI_STATE_S5)
+               return AE_ERROR;
+
+       /* do we have a wakeup address for S2 and S3? */
+       if (state == ACPI_STATE_S2 || state == ACPI_STATE_S3) {
+               if (!acpi_wakeup_address)
+                       return AE_ERROR;
+               acpi_set_firmware_waking_vector((ACPI_PHYSICAL_ADDRESS) acpi_wakeup_address);
+       }
+
+       acpi_enter_sleep_state_prep(state);
+
+       status = acpi_system_save_state(state);
+       if (!ACPI_SUCCESS(status))
+               return status;
+
+       /* disable interrupts and flush caches */
+       acpi_disable_irqs();
+       wbinvd();
+
+       /* perform OS-specific sleep actions */
+       status = acpi_system_suspend(state);
+
+       /* Even if we failed to go to sleep, all of the devices are in an suspended
+        * mode. So, we run these unconditionaly to make sure we have a usable system
+        * no matter what.
+        */
+       acpi_system_restore_state(state);
+       acpi_leave_sleep_state(state);
+
+       /* make sure interrupts are enabled */
+       acpi_enable_irqs();
+
+       /* reset firmware waking vector */
+       acpi_set_firmware_waking_vector((ACPI_PHYSICAL_ADDRESS) 0);
+
+       return status;
+}
+
+#endif /* CONFIG_ACPI_SLEEP */
+
+
+/* --------------------------------------------------------------------------
+                              FS Interface (/proc)
+   -------------------------------------------------------------------------- */
+
+#include <linux/compatmac.h>
+#include <linux/proc_fs.h>
+
+
+static int
+acpi_system_read_info (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_system      *system = (struct acpi_system *) data;
+       char                    *p = page;
+       int                     size = 0;
+       u32                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_system_read_info");
+
+       if (!system || (off != 0))
+               goto end;
+
+       p += sprintf(p, "version:                 %x\n", ACPI_CA_VERSION);
+
+       p += sprintf(p, "states:                  ");
+       for (i=0; i<ACPI_S_STATE_COUNT; i++) {
+               if (system->states[i])
+                       p += sprintf(p, "S%d ", i);
+       }
+       p += sprintf(p, "\n");
+
+end:
+       size = (p - page);
+       if (size <= off+count) *eof = 1;
+       *start = page + off;
+       size -= off;
+       if (size>count) size = count;
+       if (size<0) size = 0;
+
+       return_VALUE(size);
+}
+
+static int acpi_system_open_event(struct inode *inode, struct file *file);
+static ssize_t acpi_system_read_event (struct file*, char*, size_t, loff_t*);
+static int acpi_system_close_event(struct inode *inode, struct file *file);
+static unsigned int acpi_system_poll_event(struct file *file, poll_table *wait);
+
+
+static struct file_operations acpi_system_event_ops = {
+       open:           acpi_system_open_event,
+       read:           acpi_system_read_event,
+       release:        acpi_system_close_event,
+       poll:           acpi_system_poll_event,
+};
+
+static int
+acpi_system_open_event(struct inode *inode, struct file *file)
+{
+       spin_lock_irq (&acpi_system_event_lock);
+
+       if(event_is_open)
+               goto out_busy;
+
+       event_is_open = 1;
+
+       spin_unlock_irq (&acpi_system_event_lock);
+       return 0;
+
+out_busy:
+       spin_unlock_irq (&acpi_system_event_lock);
+       return -EBUSY;
+}
+
+static ssize_t
+acpi_system_read_event (
+       struct file             *file,
+       char                    *buffer,
+       size_t                  count,
+       loff_t                  *ppos)
+{
+       int                     result = 0;
+       char                    outbuf[ACPI_MAX_STRING];
+       int                     size = 0;
+       struct acpi_bus_event   event;
+
+       ACPI_FUNCTION_TRACE("acpi_system_read_event");
+
+       memset(&event, 0, sizeof(struct acpi_bus_event));
+
+       if (count < ACPI_MAX_STRING)
+               goto end;
+
+       if ((file->f_flags & O_NONBLOCK)
+           && (list_empty(&acpi_bus_event_list)))
+               return_VALUE(-EAGAIN);
+
+       result = acpi_bus_receive_event(&event);
+       if (0 != result) {
+               size = sprintf(outbuf, "error\n");
+               goto end;
+       }
+
+       size = sprintf(outbuf, "%s %s %08x %08x\n", 
+               event.device_class?event.device_class:"<unknown>",
+               event.bus_id?event.bus_id:"<unknown>", 
+               event.type, 
+               event.data);
+
+end:
+       if (copy_to_user(buffer, outbuf, size))
+               return_VALUE(-EFAULT);
+
+       *ppos += size;
+
+       return_VALUE(size);
+}
+
+static int
+acpi_system_close_event(struct inode *inode, struct file *file)
+{
+       spin_lock_irq (&acpi_system_event_lock);
+       event_is_open = 0;
+       spin_unlock_irq (&acpi_system_event_lock);
+       return 0;
+}
+
+static unsigned int
+acpi_system_poll_event(
+       struct file             *file,
+       poll_table              *wait)
+{
+       poll_wait(file, &acpi_bus_event_queue, wait);
+       if (!list_empty(&acpi_bus_event_list))
+               return POLLIN | POLLRDNORM;
+       return 0;
+}
+
+static ssize_t acpi_system_read_dsdt (struct file*, char*, size_t, loff_t*);
+
+static struct file_operations acpi_system_dsdt_ops = {
+       read:                   acpi_system_read_dsdt,
+};
+
+static ssize_t
+acpi_system_read_dsdt (
+       struct file             *file,
+       char                    *buffer,
+       size_t                  count,
+       loff_t                  *ppos)
+{
+       acpi_status             status = AE_OK;
+       acpi_buffer             dsdt = {ACPI_ALLOCATE_BUFFER, NULL};
+       void                    *data = 0;
+       size_t                  size = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_system_read_dsdt");
+
+       status = acpi_get_table(ACPI_TABLE_DSDT, 1, &dsdt);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       if (*ppos < dsdt.length) {
+               data = dsdt.pointer + file->f_pos;
+               size = dsdt.length - file->f_pos;
+               if (size > count)
+                       size = count;
+               if (copy_to_user(buffer, data, size)) {
+                       kfree(dsdt.pointer);
+                       return_VALUE(-EFAULT);
+               }
+       }
+
+       kfree(dsdt.pointer);
+
+       *ppos += size;
+
+       return_VALUE(size);
+}
+
+
+static ssize_t acpi_system_read_fadt (struct file*, char*, size_t, loff_t*);
+
+static struct file_operations acpi_system_fadt_ops = {
+       read:                   acpi_system_read_fadt,
+};
+
+static ssize_t
+acpi_system_read_fadt (
+       struct file             *file,
+       char                    *buffer,
+       size_t                  count,
+       loff_t                  *ppos)
+{
+       acpi_status             status = AE_OK;
+       acpi_buffer             fadt = {ACPI_ALLOCATE_BUFFER, NULL};
+       void                    *data = 0;
+       size_t                  size = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_system_read_fadt");
+
+       status = acpi_get_table(ACPI_TABLE_FADT, 1, &fadt);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       if (*ppos < fadt.length) {
+               data = fadt.pointer + file->f_pos;
+               size = fadt.length - file->f_pos;
+               if (size > count)
+                       size = count;
+               if (copy_to_user(buffer, data, size)) {
+                       kfree(fadt.pointer);
+                       return_VALUE(-EFAULT);
+               }
+       }
+
+       kfree(fadt.pointer);
+
+       *ppos += size;
+
+       return_VALUE(size);
+}
+
+
+#ifdef ACPI_DEBUG
+
+static int
+acpi_system_read_debug (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       char                    *p = page;
+       int                     size = 0;
+       u32                     var;
+
+       if (off != 0)
+               goto end;
+
+       switch ((unsigned long) data) {
+       case 0:
+               p += sprintf(p, "0x%08x\n", acpi_dbg_layer);
+               break;
+       case 1:
+               p += sprintf(p, "0x%08x\n", acpi_dbg_level);
+               break;
+       default:
+               p += sprintf(p, "Invalid debug option\n");
+               break;
+       }
+       
+end:
+       size = (p - page);
+       if (size <= off+count) *eof = 1;
+       *start = page + off;
+       size -= off;
+       if (size>count) size = count;
+       if (size<0) size = 0;
+
+       return size;
+}
+
+
+static int
+acpi_system_write_debug (
+       struct file             *file,
+        const char              *buffer,
+       unsigned long           count,
+        void                    *data)
+{
+       char                    debug_string[12] = {'\0'};
+       u32                     *pvar;
+
+       ACPI_FUNCTION_TRACE("acpi_system_write_debug");
+
+       if (count > sizeof(debug_string) - 1)
+               return_VALUE(-EINVAL);
+
+       if (copy_from_user(debug_string, buffer, count))
+               return_VALUE(-EFAULT);
+
+       debug_string[count] = '\0';
+
+       switch ((unsigned long) data) {
+       case 0:
+               acpi_dbg_layer = simple_strtoul(debug_string, NULL, 0);
+               break;
+       case 1:
+               acpi_dbg_level = simple_strtoul(debug_string, NULL, 0);
+               break;
+       default:
+               return_VALUE(-EINVAL);
+       }
+
+       return_VALUE(count);
+}
+
+#endif /* ACPI_DEBUG */
+
+
+#ifdef CONFIG_ACPI_SLEEP
+
+static int
+acpi_system_read_sleep (
+        char                    *page,
+        char                    **start,
+        off_t                   off,
+        int                     count,
+        int                     *eof,
+        void                    *data)
+{
+       struct acpi_system      *system = (struct acpi_system *) data;
+       char                    *p = page;
+       int                     size;
+       int                     i;
+
+       ACPI_FUNCTION_TRACE("acpi_system_read_sleep");
+
+       if (!system || (off != 0))
+               goto end;
+
+       for (i = 0; i <= ACPI_STATE_S5; i++) {
+               if (system->states[i])
+                       p += sprintf(p,"S%d ", i);
+       }
+
+       p += sprintf(p, "\n");
+
+end:
+       size = (p - page);
+       if (size <= off+count) *eof = 1;
+       *start = page + off;
+       size -= off;
+       if (size>count) size = count;
+       if (size<0) size = 0;
+
+       return_VALUE(size);
+}
+
+
+static int
+acpi_system_write_sleep (
+       struct file             *file,
+       const char              *buffer,
+       unsigned long           count,
+       void                    *data)
+{
+       acpi_status             status = AE_OK;
+       struct acpi_system      *system = (struct acpi_system *) data;
+       char                    state_string[12] = {'\0'};
+       u32                     state = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_system_write_sleep");
+
+       if (!system || (count > sizeof(state_string) - 1))
+               return_VALUE(-EINVAL);
+
+       if (copy_from_user(state_string, buffer, count))
+               return_VALUE(-EFAULT);
+       
+       state_string[count] = '\0';
+       
+       state = simple_strtoul(state_string, NULL, 0);
+       
+       if (!system->states[state])
+               return_VALUE(-ENODEV);
+       
+       status = acpi_suspend(state);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+       
+       return_VALUE(count);
+}
+
+
+static int
+acpi_system_read_alarm (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *context)
+{
+       char                    *p = page;
+       int                     size = 0;
+       u32                     sec, min, hr;
+       u32                     day, mo, yr;
+
+       ACPI_FUNCTION_TRACE("acpi_system_read_alarm");
+
+       if (off != 0)
+               goto end;
+
+       spin_lock(&rtc_lock);
+
+       sec = CMOS_READ(RTC_SECONDS_ALARM);
+       min = CMOS_READ(RTC_MINUTES_ALARM);
+       hr = CMOS_READ(RTC_HOURS_ALARM);
+
+#if 0  /* If we ever get an FACP with proper values... */
+       if (acpi_gbl_FADT->day_alrm)
+               day = CMOS_READ(acpi_gbl_FADT->day_alrm);
+       else
+               day =  CMOS_READ(RTC_DAY_OF_MONTH);
+       if (acpi_gbl_FADT->mon_alrm)
+               mo = CMOS_READ(acpi_gbl_FADT->mon_alrm);
+       else
+               mo = CMOS_READ(RTC_MONTH);;
+       if (acpi_gbl_FADT->century)
+               yr = CMOS_READ(acpi_gbl_FADT->century) * 100 + CMOS_READ(RTC_YEAR);
+       else
+               yr = CMOS_READ(RTC_YEAR);
+#else
+       day = CMOS_READ(RTC_DAY_OF_MONTH);
+       mo = CMOS_READ(RTC_MONTH);
+       yr = CMOS_READ(RTC_YEAR);
+#endif
+
+       spin_unlock(&rtc_lock);
+
+       BCD_TO_BIN(sec);
+       BCD_TO_BIN(min);
+       BCD_TO_BIN(hr);
+       BCD_TO_BIN(day);
+       BCD_TO_BIN(mo);
+       BCD_TO_BIN(yr);
+
+       p += sprintf(p,"%4.4u-", yr);
+       p += (mo > 12)  ? sprintf(p, "**-")  : sprintf(p, "%2.2u-", mo);
+       p += (day > 31) ? sprintf(p, "** ")  : sprintf(p, "%2.2u ", day);
+       p += (hr > 23)  ? sprintf(p, "**:")  : sprintf(p, "%2.2u:", hr);
+       p += (min > 59) ? sprintf(p, "**:")  : sprintf(p, "%2.2u:", min);
+       p += (sec > 59) ? sprintf(p, "**\n") : sprintf(p, "%2.2u\n", sec);
+
+ end:
+       size = p - page;
+       if (size < count) *eof = 1;
+       else if (size > count) size = count;
+       if (size < 0) size = 0;
+       *start = page;
+
+       return_VALUE(size);
+}
+
+
+static int
+get_date_field (
+       char                    **p,
+       u32                     *value)
+{
+       char                    *next = NULL;
+       char                    *string_end = NULL;
+       int                     result = -EINVAL;
+
+       /*
+        * Try to find delimeter, only to insert null.  The end of the
+        * string won't have one, but is still valid.
+        */
+       next = strpbrk(*p, "- :");
+       if (next)
+               *next++ = '\0';
+
+       *value = simple_strtoul(*p, &string_end, 10);
+
+       /* Signal success if we got a good digit */
+       if (string_end != *p)
+               result = 0;
+
+       if (next)
+               *p = next;
+
+       return result;
+}
+
+
+static int
+acpi_system_write_alarm (
+       struct file             *file,
+       const char              *buffer,
+       unsigned long           count,
+       void                    *data)
+{
+       int                     result = 0;
+       char                    alarm_string[30] = {'\0'};
+       char                    *p = alarm_string;
+       u32                     sec, min, hr, day, mo, yr;
+       int                     adjust = 0;
+       unsigned char           rtc_control = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_system_write_alarm");
+
+       if (count > sizeof(alarm_string) - 1)
+               return_VALUE(-EINVAL);
+       
+       if (copy_from_user(alarm_string, buffer, count))
+               return_VALUE(-EFAULT);
+
+       alarm_string[count] = '\0';
+
+       /* check for time adjustment */
+       if (alarm_string[0] == '+') {
+               p++;
+               adjust = 1;
+       }
+
+       if ((result = get_date_field(&p, &yr)))
+               goto end;
+       if ((result = get_date_field(&p, &mo)))
+               goto end;
+       if ((result = get_date_field(&p, &day)))
+               goto end;
+       if ((result = get_date_field(&p, &hr)))
+               goto end;
+       if ((result = get_date_field(&p, &min)))
+               goto end;
+       if ((result = get_date_field(&p, &sec)))
+               goto end;
+
+       if (sec > 59) {
+               min += 1;
+               sec -= 60;
+       }
+       if (min > 59) {
+               hr += 1;
+               min -= 60;
+       }
+       if (hr > 23) {
+               day += 1;
+               hr -= 24;
+       }
+       if (day > 31) {
+               mo += 1;
+               day -= 31;
+       }
+       if (mo > 12) {
+               yr += 1;
+               mo -= 12;
+       }
+
+       spin_lock_irq(&rtc_lock);
+
+       rtc_control = CMOS_READ(RTC_CONTROL);
+       if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+               BIN_TO_BCD(yr);
+               BIN_TO_BCD(mo);
+               BIN_TO_BCD(day);
+               BIN_TO_BCD(hr);
+               BIN_TO_BCD(min);
+               BIN_TO_BCD(sec);
+       }
+
+       if (adjust) {
+               yr  += CMOS_READ(RTC_YEAR);
+               mo  += CMOS_READ(RTC_MONTH);
+               day += CMOS_READ(RTC_DAY_OF_MONTH);
+               hr  += CMOS_READ(RTC_HOURS);
+               min += CMOS_READ(RTC_MINUTES);
+               sec += CMOS_READ(RTC_SECONDS);
+       }
+
+       spin_unlock_irq(&rtc_lock);
+
+       if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+               BCD_TO_BIN(yr);
+               BCD_TO_BIN(mo);
+               BCD_TO_BIN(day);
+               BCD_TO_BIN(hr);
+               BCD_TO_BIN(min);
+               BCD_TO_BIN(sec);
+       }
+
+       if (sec > 59) {
+               min++;
+               sec -= 60;
+       }
+       if (min > 59) {
+               hr++;
+               min -= 60;
+       }
+       if (hr > 23) {
+               day++;
+               hr -= 24;
+       }
+       if (day > 31) {
+               mo++;
+               day -= 31;
+       }
+       if (mo > 12) {
+               yr++;
+               mo -= 12;
+       }
+       if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+               BIN_TO_BCD(yr);
+               BIN_TO_BCD(mo);
+               BIN_TO_BCD(day);
+               BIN_TO_BCD(hr);
+               BIN_TO_BCD(min);
+               BIN_TO_BCD(sec);
+       }
+
+       spin_lock_irq(&rtc_lock);
+
+       /* write the fields the rtc knows about */
+       CMOS_WRITE(hr, RTC_HOURS_ALARM);
+       CMOS_WRITE(min, RTC_MINUTES_ALARM);
+       CMOS_WRITE(sec, RTC_SECONDS_ALARM);
+
+       /*
+        * If the system supports an enhanced alarm it will have non-zero
+        * offsets into the CMOS RAM here -- which for some reason are pointing
+        * to the RTC area of memory.
+        */
+#if 0
+       if (acpi_gbl_FADT->day_alrm)
+               CMOS_WRITE(day, acpi_gbl_FADT->day_alrm);
+       if (acpi_gbl_FADT->mon_alrm)
+               CMOS_WRITE(mo, acpi_gbl_FADT->mon_alrm);
+       if (acpi_gbl_FADT->century)
+               CMOS_WRITE(yr/100, acpi_gbl_FADT->century);
+#endif
+       /* enable the rtc alarm interrupt */
+       if (!(rtc_control & RTC_AIE)) {
+               rtc_control |= RTC_AIE;
+               CMOS_WRITE(rtc_control,RTC_CONTROL);
+               CMOS_READ(RTC_INTR_FLAGS);
+       }
+
+       spin_unlock_irq(&rtc_lock);
+
+       acpi_hw_bit_register_write(ACPI_BITREG_RT_CLOCK_ENABLE, 1, ACPI_MTX_LOCK);
+
+       file->f_pos += count;
+
+       result = 0;
+end:
+       return_VALUE(result ? result : count);
+}
+
+#endif /*CONFIG_ACPI_SLEEP*/
+
+
+static int
+acpi_system_add_fs (
+       struct acpi_device      *device)
+{
+       struct proc_dir_entry   *entry = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_system_add_fs");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       /* 'info' [R] */
+       entry = create_proc_entry(ACPI_SYSTEM_FILE_INFO,
+               S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_SYSTEM_FILE_INFO));
+       else {
+               entry->read_proc = acpi_system_read_info;
+               entry->data = acpi_driver_data(device);
+       }
+
+       /* 'dsdt' [R] */
+       entry = create_proc_entry(ACPI_SYSTEM_FILE_DSDT,
+               S_IRUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_SYSTEM_FILE_DSDT));
+       else
+               entry->proc_fops = &acpi_system_dsdt_ops;
+
+       /* 'fadt' [R] */
+       entry = create_proc_entry(ACPI_SYSTEM_FILE_FADT,
+               S_IRUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_SYSTEM_FILE_FADT));
+       else
+               entry->proc_fops = &acpi_system_fadt_ops;
+
+       /* 'event' [R] */
+       entry = create_proc_entry(ACPI_SYSTEM_FILE_EVENT,
+               S_IRUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_SYSTEM_FILE_EVENT));
+       else
+               entry->proc_fops = &acpi_system_event_ops;
+
+#ifdef CONFIG_ACPI_SLEEP
+
+       /* 'sleep' [R/W]*/
+       entry = create_proc_entry(ACPI_SYSTEM_FILE_SLEEP,
+               S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_SYSTEM_FILE_SLEEP));
+       else {
+               entry->read_proc = acpi_system_read_sleep;
+               entry->write_proc = acpi_system_write_sleep;
+               entry->data = acpi_driver_data(device);
+       }
+
+       /* 'alarm' [R/W] */
+       entry = create_proc_entry(ACPI_SYSTEM_FILE_ALARM,
+               S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_SYSTEM_FILE_ALARM));
+       else {
+               entry->read_proc = acpi_system_read_alarm;
+               entry->write_proc = acpi_system_write_alarm;
+               entry->data = acpi_driver_data(device);
+       }
+
+#endif /*CONFIG_ACPI_SLEEP*/
+
+#ifdef ACPI_DEBUG
+
+       /* 'debug_layer' [R/W] */
+       entry = create_proc_entry(ACPI_SYSTEM_FILE_DEBUG_LAYER,
+               S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_SYSTEM_FILE_DEBUG_LAYER));
+       else {
+               entry->read_proc  = acpi_system_read_debug;
+               entry->write_proc = acpi_system_write_debug;
+               entry->data = (void *) 0;
+       }
+
+       /* 'debug_level' [R/W] */
+       entry = create_proc_entry(ACPI_SYSTEM_FILE_DEBUG_LEVEL,
+               S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_SYSTEM_FILE_DEBUG_LEVEL));
+       else {
+               entry->read_proc  = acpi_system_read_debug;
+               entry->write_proc = acpi_system_write_debug;
+               entry->data = (void *) 1;
+       }
+
+#endif /*ACPI_DEBUG*/
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_system_remove_fs (
+       struct acpi_device      *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_system_remove_fs");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       remove_proc_entry(ACPI_SYSTEM_FILE_INFO, acpi_device_dir(device));
+       remove_proc_entry(ACPI_SYSTEM_FILE_DSDT, acpi_device_dir(device));
+       remove_proc_entry(ACPI_SYSTEM_FILE_EVENT, acpi_device_dir(device));
+#ifdef CONFIG_ACPI_SLEEP
+       remove_proc_entry(ACPI_SYSTEM_FILE_SLEEP, acpi_device_dir(device));
+       remove_proc_entry(ACPI_SYSTEM_FILE_ALARM, acpi_device_dir(device));
+#endif
+#ifdef ACPI_DEBUG
+       remove_proc_entry(ACPI_SYSTEM_FILE_DEBUG_LAYER,
+               acpi_device_dir(device));
+       remove_proc_entry(ACPI_SYSTEM_FILE_DEBUG_LEVEL,
+               acpi_device_dir(device));
+#endif
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                                 Driver Interface
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_system_add (
+       struct acpi_device      *device)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_system      *system = NULL;
+       u8                      i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_system_add");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       system = kmalloc(sizeof(struct acpi_system), GFP_KERNEL);
+       if (!system)
+               return_VALUE(-ENOMEM);
+       memset(system, 0, sizeof(struct acpi_system));
+
+       system->handle = device->handle;
+       sprintf(acpi_device_name(device), "%s", ACPI_SYSTEM_DEVICE_NAME);
+       sprintf(acpi_device_class(device), "%s", ACPI_SYSTEM_CLASS);
+       acpi_driver_data(device) = system;
+
+       result = acpi_system_add_fs(device);
+       if (0 != result)
+               goto end;
+
+       printk(KERN_INFO PREFIX "%s [%s] (supports", 
+               acpi_device_name(device), acpi_device_bid(device));
+       for (i=0; i<ACPI_S_STATE_COUNT; i++) {
+               u8 type_a, type_b;
+               status = acpi_hw_get_sleep_type_data(i, &type_a, &type_b);
+               if (ACPI_SUCCESS(status)) {
+                       system->states[i] = 1;
+                       printk(" S%d", i);
+               }
+       }
+       printk(")\n");
+
+#ifdef CONFIG_PM
+       /* Install the soft-off (S5) handler. */
+       if (system->states[ACPI_STATE_S5])
+               pm_power_off = acpi_power_off;
+#endif
+
+end:
+       if (0 != result)
+               kfree(system);
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_system_remove (
+       struct acpi_device      *device,
+       int                     type)
+{
+       struct acpi_system      *system = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_system_remove");
+
+       if (!device || !acpi_driver_data(device))
+               return_VALUE(-EINVAL);
+
+       system = (struct acpi_system *) acpi_driver_data(device);
+
+#ifdef CONFIG_PM
+       /* Remove the soft-off (S5) handler. */
+       if (system->states[ACPI_STATE_S5])
+               pm_power_off = NULL;
+#endif
+
+       acpi_system_remove_fs(device);
+
+       kfree(system);
+
+       return 0;
+}
+
+
+int __init
+acpi_system_init (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_system_init");
+
+       result = acpi_bus_register_driver(&acpi_system_driver);
+       if (0 > result)
+               return_VALUE(-ENODEV);
+
+       return_VALUE(0);
+}
+
+
+void __exit
+acpi_system_exit (void)
+{
+       ACPI_FUNCTION_TRACE("acpi_system_exit");
+       acpi_bus_unregister_driver(&acpi_system_driver);
+       return_VOID;
+}
diff --git a/drivers/acpi/acpi_tables.c b/drivers/acpi/acpi_tables.c
new file mode 100644 (file)
index 0000000..490e488
--- /dev/null
@@ -0,0 +1,514 @@
+/*
+ *  acpi_tables.c - ACPI Boot-Time Table Parsing
+ *
+ *  Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/irq.h>
+#include <linux/acpi.h>
+
+#define PREFIX                 "ACPI: "
+
+#define ACPI_MAX_TABLES                ACPI_TABLE_COUNT
+
+static char *acpi_table_signatures[ACPI_TABLE_COUNT] = {
+       [ACPI_TABLE_UNKNOWN]    = "????",
+       [ACPI_APIC]             = "APIC",
+       [ACPI_BOOT]             = "BOOT",
+       [ACPI_DBGP]             = "DBGP",
+       [ACPI_DSDT]             = "DSDT",
+       [ACPI_ECDT]             = "ECDT",
+       [ACPI_ETDT]             = "ETDT",
+       [ACPI_FACP]             = "FACP",
+       [ACPI_FACS]             = "FACS",
+       [ACPI_OEMX]             = "OEM",
+       [ACPI_PSDT]             = "PSDT",
+       [ACPI_SBST]             = "SBST",
+       [ACPI_SLIT]             = "SLIT",
+       [ACPI_SPCR]             = "SPCR",
+       [ACPI_SRAT]             = "SRAT",
+       [ACPI_SSDT]             = "SSDT",
+       [ACPI_SPMI]             = "SPMI"
+};
+
+/* System Description Table (RSDT/XSDT) */
+struct acpi_table_sdt {
+       unsigned long           pa;             /* Physical Address */
+       unsigned long           count;          /* Table count */
+       struct {
+               unsigned long           pa;
+               enum acpi_table_id      id;
+               unsigned long           size;
+       }                       entry[ACPI_MAX_TABLES];
+} __attribute__ ((packed));
+
+static struct acpi_table_sdt   sdt;
+
+acpi_madt_entry_handler                madt_handlers[ACPI_MADT_ENTRY_COUNT];
+
+struct acpi_boot_flags acpi_boot = {1, 0}; /* Enabled by default */
+
+
+void
+acpi_table_print (
+       struct acpi_table_header *header,
+       unsigned long           phys_addr)
+{
+       char                    *name = NULL;
+
+       if (!header)
+               return;
+
+       /* Some table signatures aren't good table names */
+
+       if (0 == strncmp((char *) &header->signature,
+               acpi_table_signatures[ACPI_APIC],
+               sizeof(header->signature))) {
+               name = "MADT";
+       }
+       else if (0 == strncmp((char *) &header->signature,
+               acpi_table_signatures[ACPI_FACP],
+               sizeof(header->signature))) {
+               name = "FADT";
+       }
+       else
+               name = header->signature;
+
+       printk(KERN_INFO PREFIX "%.4s (v%3.3d %6.6s %8.8s %5.5d.%5.5d) @ 0x%p\n",
+               name, header->revision, header->oem_id,
+               header->oem_table_id, header->oem_revision >> 16,
+               header->oem_revision & 0xffff, (void *) phys_addr);
+}
+
+
+void
+acpi_table_print_madt_entry (
+       acpi_table_entry_header *header)
+{
+       if (!header)
+               return;
+
+       switch (header->type) {
+
+       case ACPI_MADT_LAPIC:
+       {
+               struct acpi_table_lapic *p =
+                       (struct acpi_table_lapic*) header;
+               printk(KERN_INFO PREFIX "LAPIC (acpi_id[0x%02x] lapic_id[0x%02x] %s)\n",
+                       p->acpi_id, p->id, p->flags.enabled?"enabled":"disabled");
+       }
+               break;
+
+       case ACPI_MADT_IOAPIC:
+       {
+               struct acpi_table_ioapic *p =
+                       (struct acpi_table_ioapic*) header;
+               printk(KERN_INFO PREFIX "IOAPIC (id[0x%02x] address[0x%08x] global_irq_base[0x%x])\n",
+                       p->id, p->address, p->global_irq_base);
+       }
+               break;
+
+       case ACPI_MADT_INT_SRC_OVR:
+       {
+               struct acpi_table_int_src_ovr *p =
+                       (struct acpi_table_int_src_ovr*) header;
+               printk(KERN_INFO PREFIX "INT_SRC_OVR (bus[%d] irq[0x%x] global_irq[0x%x] polarity[0x%x] trigger[0x%x])\n",
+                       p->bus, p->bus_irq, p->global_irq, p->flags.polarity, p->flags.trigger);
+       }
+               break;
+
+       case ACPI_MADT_NMI_SRC:
+       {
+               struct acpi_table_nmi_src *p =
+                       (struct acpi_table_nmi_src*) header;
+               printk(KERN_INFO PREFIX "NMI_SRC (polarity[0x%x] trigger[0x%x] global_irq[0x%x])\n",
+                       p->flags.polarity, p->flags.trigger, p->global_irq);
+       }
+               break;
+
+       case ACPI_MADT_LAPIC_NMI:
+       {
+               struct acpi_table_lapic_nmi *p =
+                       (struct acpi_table_lapic_nmi*) header;
+               printk(KERN_INFO PREFIX "LAPIC_NMI (acpi_id[0x%02x] polarity[0x%x] trigger[0x%x] lint[0x%x])\n",
+                       p->acpi_id, p->flags.polarity, p->flags.trigger, p->lint);
+       }
+               break;
+
+       case ACPI_MADT_LAPIC_ADDR_OVR:
+       {
+               struct acpi_table_lapic_addr_ovr *p =
+                       (struct acpi_table_lapic_addr_ovr*) header;
+               printk(KERN_INFO PREFIX "LAPIC_ADDR_OVR (address[0x%016Lx])\n",
+                       p->address);
+       }
+               break;
+
+       case ACPI_MADT_IOSAPIC:
+       {
+               struct acpi_table_iosapic *p =
+                       (struct acpi_table_iosapic*) header;
+               printk(KERN_INFO PREFIX "IOSAPIC (id[0x%x] global_irq_base[0x%x] address[0x%016Lx])\n",
+                       p->id, p->global_irq_base, p->address);
+       }
+               break;
+
+       case ACPI_MADT_LSAPIC:
+       {
+               struct acpi_table_lsapic *p =
+                       (struct acpi_table_lsapic*) header;
+               printk(KERN_INFO PREFIX "LSAPIC (acpi_id[0x%02x] lsapic_id[0x%02x] lsapic_eid[0x%02x] %s)\n",
+                       p->acpi_id, p->id, p->eid, p->flags.enabled?"enabled":"disabled");
+       }
+               break;
+
+       case ACPI_MADT_PLAT_INT_SRC:
+       {
+               struct acpi_table_plat_int_src *p =
+                       (struct acpi_table_plat_int_src*) header;
+               printk(KERN_INFO PREFIX "PLAT_INT_SRC (polarity[0x%x] trigger[0x%x] type[0x%x] id[0x%04x] eid[0x%x] iosapic_vector[0x%x] global_irq[0x%x]\n",
+                       p->flags.polarity, p->flags.trigger, p->type, p->id, p->eid, p->iosapic_vector, p->global_irq);
+       }
+               break;
+
+       default:
+               printk(KERN_WARNING PREFIX "Found unsupported MADT entry (type = 0x%x)\n",
+                       header->type);
+               break;
+       }
+}
+
+
+static int
+acpi_table_compute_checksum (
+       void                    *table_pointer,
+       unsigned long           length)
+{
+       u8                      *p = (u8 *) table_pointer;
+       unsigned long           remains = length;
+       unsigned long           sum = 0;
+
+       if (!p || !length)
+               return -EINVAL;
+
+       while (remains--)
+               sum += *p++;
+
+       return (sum & 0xFF);
+}
+
+
+int __init
+acpi_table_parse_madt (
+       enum acpi_table_id      id,
+       acpi_madt_entry_handler handler)
+{
+       struct acpi_table_madt  *madt = NULL;
+       acpi_table_entry_header *entry = NULL;
+       unsigned long           count = 0;
+       unsigned long           madt_end = 0;
+       int                     i = 0;
+
+       if (!handler)
+               return -EINVAL;
+
+       /* Locate the MADT (if exists). There should only be one. */
+
+       for (i = 0; i < sdt.count; i++) {
+               if (sdt.entry[i].id != ACPI_APIC)
+                       continue;
+               madt = (struct acpi_table_madt *)
+                       __acpi_map_table(sdt.entry[i].pa, sdt.entry[i].size);
+               if (!madt) {
+                       printk(KERN_WARNING PREFIX "Unable to map MADT\n");
+                       return -ENODEV;
+               }
+               break;
+       }
+
+       if (!madt) {
+               printk(KERN_WARNING PREFIX "MADT not present\n");
+               return -ENODEV;
+       }
+
+       madt_end = (unsigned long) madt + sdt.entry[i].size;
+
+       /* Parse all entries looking for a match. */
+
+       entry = (acpi_table_entry_header *)
+               ((unsigned long) madt + sizeof(struct acpi_table_madt));
+
+       while (((unsigned long) entry) < madt_end) {
+               if (entry->type == id) {
+                       count++;
+                       handler(entry);
+               }
+               entry = (acpi_table_entry_header *)
+                       ((unsigned long) entry += entry->length);
+       }
+
+       return count;
+}
+
+
+int __init
+acpi_table_parse (
+       enum acpi_table_id      id,
+       acpi_table_handler      handler)
+{
+       int                     count = 0;
+       int                     i = 0;
+
+       if (!handler)
+               return -EINVAL;
+
+       for (i = 0; i < sdt.count; i++) {
+               if (sdt.entry[i].id != id)
+                       continue;
+               handler(sdt.entry[i].pa, sdt.entry[i].size);
+               count++;
+       }
+
+       return count;
+}
+
+
+static int __init
+acpi_table_get_sdt (
+       struct acpi_table_rsdp  *rsdp)
+{
+       struct acpi_table_header *header = NULL;
+       int                     i, id = 0;
+
+       if (!rsdp)
+               return -EINVAL;
+
+       /* First check XSDT (but only on ACPI 2.0-compatible systems) */
+
+       if ((rsdp->revision >= 2) &&
+               (((struct acpi20_table_rsdp*)rsdp)->xsdt_address)) {
+                       
+               struct acpi_table_xsdt  *mapped_xsdt = NULL;
+
+               sdt.pa = ((struct acpi20_table_rsdp*)rsdp)->xsdt_address;
+
+               header = (struct acpi_table_header *)
+                       __acpi_map_table(sdt.pa, sizeof(struct acpi_table_header));
+
+               if (!header) {
+                       printk(KERN_WARNING PREFIX "Unable to map XSDT header\n");
+                       return -ENODEV;
+               }
+
+               if (strncmp(header->signature, "XSDT", 4)) {
+                       printk(KERN_WARNING PREFIX "XSDT signature incorrect\n");
+                       return -ENODEV;
+               }
+
+               sdt.count = (header->length - sizeof(struct acpi_table_header)) >> 3;
+               if (sdt.count > ACPI_MAX_TABLES) {
+                       printk(KERN_WARNING PREFIX "Truncated %lu XSDT entries\n",
+                               (ACPI_MAX_TABLES - sdt.count));
+                       sdt.count = ACPI_MAX_TABLES;
+               }
+
+               mapped_xsdt = (struct acpi_table_xsdt *)
+                       __acpi_map_table(sdt.pa, header->length);
+               if (!mapped_xsdt) {
+                       printk(KERN_WARNING PREFIX "Unable to map XSDT\n");
+                       return -ENODEV;
+               }
+
+               header = &mapped_xsdt->header;
+
+               for (i = 0; i < sdt.count; i++)
+                       sdt.entry[i].pa = (unsigned long) mapped_xsdt->entry[i];
+       }
+
+       /* Then check RSDT */
+
+       else if (rsdp->rsdt_address) {
+
+               struct acpi_table_rsdt  *mapped_rsdt = NULL;
+
+               sdt.pa = rsdp->rsdt_address;
+
+               header = (struct acpi_table_header *)
+                       __acpi_map_table(sdt.pa, sizeof(struct acpi_table_header));
+               if (!header) {
+                       printk(KERN_WARNING PREFIX "Unable to map RSDT header\n");
+                       return -ENODEV;
+               }
+
+               if (strncmp(header->signature, "RSDT", 4)) {
+                       printk(KERN_WARNING PREFIX "RSDT signature incorrect\n");
+                       return -ENODEV;
+               }
+
+               sdt.count = (header->length - sizeof(struct acpi_table_header)) >> 2;
+               if (sdt.count > ACPI_MAX_TABLES) {
+                       printk(KERN_WARNING PREFIX "Truncated %lu RSDT entries\n",
+                               (ACPI_TABLE_COUNT - sdt.count));
+                       sdt.count = ACPI_MAX_TABLES;
+               }
+
+               mapped_rsdt = (struct acpi_table_rsdt *)
+                       __acpi_map_table(sdt.pa, header->length);
+               if (!mapped_rsdt) {
+                       printk(KERN_WARNING PREFIX "Unable to map RSDT\n");
+                       return -ENODEV;
+               }
+
+               header = &mapped_rsdt->header;
+
+               for (i = 0; i < sdt.count; i++)
+                       sdt.entry[i].pa = (unsigned long) mapped_rsdt->entry[i];
+       }
+
+       else {
+               printk(KERN_WARNING PREFIX "No System Description Table (RSDT/XSDT) specified in RSDP\n");
+               return -ENODEV;
+       }
+
+       acpi_table_print(header, sdt.pa);
+
+       for (i = 0; i < sdt.count; i++) {
+
+               header = (struct acpi_table_header *)
+                       __acpi_map_table(sdt.entry[i].pa,
+                               sizeof(struct acpi_table_header));
+               if (!header)
+                       continue;
+
+               acpi_table_print(header, sdt.entry[i].pa);
+
+               if (0 != acpi_table_compute_checksum(header, header->length)) {
+                       printk(KERN_WARNING "  >>> ERROR: Invalid checksum\n");
+                       continue;
+               }
+
+               sdt.entry[i].size = header->length;
+
+               for (id = 0; id < ACPI_TABLE_COUNT; id++) {
+                       if (0 == strncmp((char *) &header->signature,
+                               acpi_table_signatures[id],
+                               sizeof(header->signature))) {
+                               sdt.entry[i].id = id;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+
+static void __init
+acpi_table_parse_cmdline (
+       char            *cmdline)
+{
+       char            *p = NULL;
+
+       /* NOTE: We're called too early in the boot process to use __setup */
+
+       if (!cmdline || !(p = strstr(cmdline, "acpi_boot=")))
+               return;
+
+       p += 10;
+
+       while (*p && (*p != ' ')) {
+               if (0 == memcmp(p, "madt", 4)) {
+                       printk(KERN_INFO PREFIX "MADT processing enabled\n");
+                       acpi_boot.madt = 1;
+                       p += 4;
+               }
+               else if (0 == memcmp(p, "on", 2)) {
+                       printk(KERN_INFO PREFIX "Boot-time table processing enabled\n");
+                       acpi_boot.madt = 1;
+                       p += 2;
+               }
+               else if (0 == memcmp(p, "off", 2)) {
+                       printk(KERN_INFO PREFIX "Boot-time table processing disabled\n");
+                       acpi_boot.madt = 0;
+                       p += 3;
+               }
+               else
+                       p++;
+
+               if (*p == ',')
+                       p ++;
+       }
+}
+
+
+int __init
+acpi_table_init (
+       char                    *cmdline)
+{
+       struct acpi_table_rsdp  *rsdp = NULL;
+       unsigned long           rsdp_phys = 0;
+       int                     result = 0;
+
+       memset(&sdt, 0, sizeof(struct acpi_table_sdt));
+       memset(&madt_handlers, 0, sizeof(madt_handlers));
+
+       acpi_table_parse_cmdline(cmdline);
+
+       /* Locate and map the Root System Description Table (RSDP) */
+
+       if ((0 != acpi_find_rsdp(&rsdp_phys)) || !rsdp_phys) {
+               printk(KERN_ERR PREFIX "Unable to locate RSDP\n");
+               return -ENODEV;
+       }
+
+       rsdp = (struct acpi_table_rsdp *) __va(rsdp_phys);
+       if (!rsdp) {
+               printk(KERN_WARNING PREFIX "Unable to map RSDP\n");
+               return -ENODEV;
+       }
+
+       printk(KERN_INFO PREFIX "RSDP (v%3.3d %6.6s                     ) @ 0x%p\n",
+               rsdp->revision, rsdp->oem_id, (void *) rsdp_phys);
+
+       if (rsdp->revision < 2)
+               result = acpi_table_compute_checksum(rsdp, sizeof(struct acpi_table_rsdp));
+       else
+               result = acpi_table_compute_checksum(rsdp, ((struct acpi20_table_rsdp *)rsdp)->length);
+
+       if (0 != result) {
+               printk(KERN_WARNING "  >>> ERROR: Invalid checksum\n");
+               return -ENODEV;
+       }
+
+       /* Locate and map the System Description table (RSDT/XSDT) */
+
+       if (0 != acpi_table_get_sdt(rsdp))
+               return -ENODEV;
+
+       return 0;
+}
diff --git a/drivers/acpi/acpi_thermal.c b/drivers/acpi/acpi_thermal.c
new file mode 100644 (file)
index 0000000..d826334
--- /dev/null
@@ -0,0 +1,1307 @@
+/*
+ *  acpi_thermal.c - ACPI Thermal Zone Driver ($Revision: 33 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This driver fully implements the ACPI thermal policy as described in the
+ *  ACPI 2.0 Specification.
+ *
+ *  TBD: 1. Implement passive cooling hysteresis.
+ *       2. Enhance passive cooling (CPU) states/limit interface to support
+ *          concepts of 'multiple limiters', upper/lower limits, etc.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kmod.h>
+#include "acpi_bus.h"
+#include "acpi_drivers.h"
+
+
+#define _COMPONENT             ACPI_THERMAL_COMPONENT
+ACPI_MODULE_NAME               ("acpi_thermal")
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION(ACPI_THERMAL_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+static int tzp = 0;
+MODULE_PARM(tzp, "i");
+MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.\n");
+
+#define PREFIX                 "ACPI: "
+
+
+#define ACPI_THERMAL_MAX_ACTIVE        10
+
+#define KELVIN_TO_CELSIUS(t)   ((t-2732+5)/10)
+
+static int acpi_thermal_add (struct acpi_device *device);
+static int acpi_thermal_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_thermal_driver = {
+       name:                   ACPI_THERMAL_DRIVER_NAME,
+       class:                  ACPI_THERMAL_CLASS,
+       ids:                    ACPI_THERMAL_HID,
+       ops:                    {
+                                       add:    acpi_thermal_add,
+                                       remove: acpi_thermal_remove,
+                               },
+};
+
+struct acpi_thermal_state {
+       u8                      critical:1;
+       u8                      hot:1;
+       u8                      passive:1;
+       u8                      active:1;
+       u8                      reserved:4;
+       int                     passive_index;          /* a.k.a. limit state */
+       int                     active_index;
+};
+
+struct acpi_thermal_state_flags {
+       u8                      valid:1;
+       u8                      enabled:1;
+       u8                      reserved:6;
+};
+
+struct acpi_thermal_critical {
+       struct acpi_thermal_state_flags flags;
+       unsigned long           temperature;
+};
+
+struct acpi_thermal_hot {
+       struct acpi_thermal_state_flags flags;
+       unsigned long           temperature;
+};
+
+struct acpi_thermal_passive {
+       struct acpi_thermal_state_flags flags;
+       unsigned long           temperature;
+       unsigned long           tc1;
+       unsigned long           tc2;
+       unsigned long           tsp;
+       struct acpi_handle_list devices;
+};
+
+struct acpi_thermal_active {
+       struct acpi_thermal_state_flags flags;
+       unsigned long           temperature;
+       struct acpi_handle_list devices;
+};
+
+struct acpi_thermal_trips {
+       struct acpi_thermal_critical critical;
+       struct acpi_thermal_hot hot;
+       struct acpi_thermal_passive passive;
+       struct acpi_thermal_active active[ACPI_THERMAL_MAX_ACTIVE];
+};
+
+struct acpi_thermal_flags {
+       u8                      cooling_mode:1;         /* _SCP */
+       u8                      devices:1;              /* _TZD */
+       u8                      reserved:6;
+};
+
+struct acpi_thermal {
+       acpi_handle             handle;
+       acpi_bus_id             name;
+       unsigned long           temperature;
+       unsigned long           last_temperature;
+       unsigned long           polling_frequency;
+       u8                      cooling_mode;
+       struct acpi_thermal_flags flags;
+       struct acpi_thermal_state state;
+       struct acpi_thermal_trips trips;
+       struct acpi_handle_list devices;
+       struct timer_list       timer;
+};
+
+
+/* --------------------------------------------------------------------------
+                             Thermal Zone Management
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_thermal_get_temperature (
+       struct acpi_thermal *tz)
+{
+       acpi_status             status = AE_OK;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_get_temperature");
+
+       if (!tz)
+               return_VALUE(-EINVAL);
+
+       tz->last_temperature = tz->temperature;
+
+       status = acpi_evaluate_integer(tz->handle, "_TMP", NULL, &tz->temperature);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Temperature is %lu dK\n", tz->temperature));
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_thermal_get_polling_frequency (
+       struct acpi_thermal     *tz)
+{
+       acpi_status             status = AE_OK;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_get_polling_frequency");
+
+       if (!tz)
+               return_VALUE(-EINVAL);
+
+       status = acpi_evaluate_integer(tz->handle, "_TZP", NULL, &tz->polling_frequency);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency is %lu dS\n", tz->polling_frequency));
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_thermal_set_polling (
+       struct acpi_thermal     *tz,
+       int                     seconds)
+{
+       ACPI_FUNCTION_TRACE("acpi_thermal_set_polling");
+
+       if (!tz)
+               return_VALUE(-EINVAL);
+
+       tz->polling_frequency = seconds * 10;   /* Convert value to deci-seconds */
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency set to %lu seconds\n", tz->polling_frequency));
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_thermal_set_cooling_mode (
+       struct acpi_thermal     *tz,
+       int                     mode)
+{
+       acpi_status             status = AE_OK;
+       acpi_object             arg0 = {ACPI_TYPE_INTEGER};
+       acpi_object_list        arg_list= {1, &arg0};
+       acpi_handle             handle = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_set_cooling_mode");
+
+       if (!tz)
+               return_VALUE(-EINVAL);
+
+       status = acpi_get_handle(tz->handle, "_SCP", &handle);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_SCP not present\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       arg0.integer.value = mode;
+
+       status = acpi_evaluate(handle, NULL, &arg_list, NULL);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       tz->cooling_mode = mode;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling mode [%s]\n", 
+               mode?"passive":"active"));
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_thermal_get_trip_points (
+       struct acpi_thermal *tz)
+{
+       acpi_status             status = AE_OK;
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_get_trip_points");
+
+       if (!tz)
+               return_VALUE(-EINVAL);
+
+       /* Critical Shutdown (required) */
+
+       status = acpi_evaluate_integer(tz->handle, "_CRT", NULL, 
+               &tz->trips.critical.temperature);
+       if (ACPI_FAILURE(status)) {
+               tz->trips.critical.flags.valid = 0;
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "No critical threshold\n"));
+               return -ENODEV;
+       }
+       else {
+               tz->trips.critical.flags.valid = 1;
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found critical threshold [%lu]\n", tz->trips.critical.temperature));
+       }
+
+       /* Critical Sleep (optional) */
+
+       status = acpi_evaluate_integer(tz->handle, "_HOT", NULL, &tz->trips.hot.temperature);
+       if (ACPI_FAILURE(status)) {
+               tz->trips.hot.flags.valid = 0;
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No hot threshold\n"));
+       }
+       else {
+               tz->trips.hot.flags.valid = 1;
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n", tz->trips.hot.temperature));
+       }
+
+       /* Passive: Processors (optional) */
+
+       status = acpi_evaluate_integer(tz->handle, "_PSV", NULL, &tz->trips.passive.temperature);
+       if (ACPI_FAILURE(status)) {
+               tz->trips.passive.flags.valid = 0;
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n"));
+       }
+       else {
+               tz->trips.passive.flags.valid = 1;
+
+               status = acpi_evaluate_integer(tz->handle, "_TC1", NULL, &tz->trips.passive.tc1);
+               if (ACPI_FAILURE(status))
+                       tz->trips.passive.flags.valid = 0;
+
+               status = acpi_evaluate_integer(tz->handle, "_TC2", NULL, &tz->trips.passive.tc2);
+               if (ACPI_FAILURE(status))
+                       tz->trips.passive.flags.valid = 0;
+
+               status = acpi_evaluate_integer(tz->handle, "_TSP", NULL, &tz->trips.passive.tsp);
+               if (ACPI_FAILURE(status))
+                       tz->trips.passive.flags.valid = 0;
+
+               status = acpi_evaluate_reference(tz->handle, "_PSL", NULL, &tz->trips.passive.devices);
+               if (ACPI_FAILURE(status))
+                       tz->trips.passive.flags.valid = 0;
+
+               if (!tz->trips.passive.flags.valid)
+                       ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid passive threshold\n"));
+               else
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found passive threshold [%lu]\n", tz->trips.passive.temperature));
+       }
+
+       /* Active: Fans, etc. (optional) */
+
+       for (i=0; i<ACPI_THERMAL_MAX_ACTIVE; i++) {
+
+               char name[5] = {'_','A','C',('0'+i),'\0'};
+
+               status = acpi_evaluate_integer(tz->handle, name, NULL, &tz->trips.active[i].temperature);
+               if (ACPI_FAILURE(status))
+                       break;
+
+               name[2] = 'L';
+               status = acpi_evaluate_reference(tz->handle, name, NULL, &tz->trips.active[i].devices);
+               if (ACPI_SUCCESS(status)) {
+                       tz->trips.active[i].flags.valid = 1;
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found active threshold [%d]:[%lu]\n", i, tz->trips.active[i].temperature));
+               }
+               else
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid active threshold [%d]\n", i));
+       }
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_thermal_get_devices (
+       struct acpi_thermal     *tz)
+{
+       acpi_status             status = AE_OK;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_get_devices");
+
+       if (!tz)
+               return_VALUE(-EINVAL);
+
+       status = acpi_evaluate_reference(tz->handle, "_TZD", NULL, &tz->devices);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_thermal_call_usermode (
+       char                    *path)
+{
+       char                    *argv[2] = {NULL, NULL};
+       char                    *envp[3] = {NULL, NULL, NULL};
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_call_usermode");
+
+       if (!path)
+               return_VALUE(-EINVAL);;
+
+       argv[0] = path;
+
+       /* minimal command environment */
+       envp[0] = "HOME=/";
+       envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+       
+       call_usermodehelper(argv[0], argv, envp);
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_thermal_critical (
+       struct acpi_thermal     *tz)
+{
+       int                     result = 0;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_critical");
+
+       if (!tz || !tz->trips.critical.flags.valid)
+               return_VALUE(-EINVAL);
+
+       if (tz->temperature >= tz->trips.critical.temperature) {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Critical trip point\n"));
+               tz->trips.critical.flags.enabled = 1;
+       }
+       else if (tz->trips.critical.flags.enabled)
+               tz->trips.critical.flags.enabled = 0;
+
+       result = acpi_bus_get_device(tz->handle, &device);
+       if (0 != result)
+               return_VALUE(result);
+
+       acpi_bus_generate_event(device, ACPI_THERMAL_NOTIFY_CRITICAL, tz->trips.critical.flags.enabled);
+
+       acpi_thermal_call_usermode(ACPI_THERMAL_PATH_POWEROFF);
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_thermal_hot (
+       struct acpi_thermal     *tz)
+{
+       int                     result = 0;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_hot");
+
+       if (!tz || !tz->trips.hot.flags.valid)
+               return_VALUE(-EINVAL);
+
+       if (tz->temperature >= tz->trips.hot.temperature) {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Hot trip point\n"));
+               tz->trips.hot.flags.enabled = 1;
+       }
+       else if (tz->trips.hot.flags.enabled)
+               tz->trips.hot.flags.enabled = 0;
+
+       result = acpi_bus_get_device(tz->handle, &device);
+       if (0 != result)
+               return_VALUE(result);
+
+       acpi_bus_generate_event(device, ACPI_THERMAL_NOTIFY_HOT, tz->trips.hot.flags.enabled);
+
+       /* TBD: Call user-mode "sleep(S4)" function */
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_thermal_passive (
+       struct acpi_thermal     *tz)
+{
+       int                     result = 0;
+       struct acpi_thermal_passive *passive = NULL;
+       int                     trend = 0;
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_passive");
+
+       if (!tz || !tz->trips.passive.flags.valid)
+               return_VALUE(-EINVAL);
+
+       passive = &(tz->trips.passive);
+
+       /*
+        * Above Trip?
+        * -----------
+        * Calculate the thermal trend (using the passive cooling equation)
+        * and modify the performance limit for all passive cooling devices
+        * accordingly.  Note that we assume symmetry.
+        */
+       if (tz->temperature >= passive->temperature) {
+               trend = (passive->tc1 * (tz->temperature - tz->last_temperature)) + (passive->tc2 * (tz->temperature - passive->temperature));
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "trend[%d]=(tc1[%lu]*(tmp[%lu]-last[%lu]))+(tc2[%lu]*(tmp[%lu]-psv[%lu]))\n", trend, passive->tc1, tz->temperature, tz->last_temperature, passive->tc2, tz->temperature, passive->temperature));
+               /* Heating up? */
+               if (trend > 0)
+                       for (i=0; i<passive->devices.count; i++)
+                               acpi_processor_set_limit(passive->devices.handles[i], ACPI_PROCESSOR_LIMIT_INCREMENT, &tz->state.passive_index);
+               /* Cooling off? */
+               else if (trend < 0)
+                       for (i=0; i<passive->devices.count; i++)
+                               acpi_processor_set_limit(passive->devices.handles[i], ACPI_PROCESSOR_LIMIT_DECREMENT, &tz->state.passive_index);
+       }
+
+       /*
+        * Below Trip?
+        * -----------
+        * Implement passive cooling hysteresis to slowly increase performance
+        * and avoid thrashing around the passive trip point.  Note that we
+        * assume symmetry.
+        */
+       else if (tz->trips.passive.flags.enabled) {
+               for (i=0; i<passive->devices.count; i++)
+                       acpi_processor_set_limit(passive->devices.handles[i], ACPI_PROCESSOR_LIMIT_DECREMENT, &tz->state.passive_index);
+               if (0 == tz->state.passive_index) {
+                       tz->trips.passive.flags.enabled = 0;
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Disabling passive cooling (zone is cool)\n"));
+               }
+       }
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_thermal_active (
+       struct acpi_thermal     *tz)
+{
+       int                     result = 0;
+       struct acpi_thermal_active *active = NULL;
+       int                     i = 0;
+       int                     j = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_active");
+
+       if (!tz)
+               return_VALUE(-EINVAL);
+
+       for (i=0; i<ACPI_THERMAL_MAX_ACTIVE; i++) {
+
+               active = &(tz->trips.active[i]);
+               if (!active || !active->flags.valid)
+                       break;
+
+               /*
+                * Above Threshold?
+                * ----------------
+                * If not already enabled, turn ON all cooling devices
+                * associated with this active threshold.
+                */
+               if (tz->temperature >= active->temperature) {
+                       tz->state.active_index = i;
+                       if (!active->flags.enabled) {
+                               for (j = 0; j < active->devices.count; j++) {
+                                       result = acpi_bus_set_power(active->devices.handles[j], ACPI_STATE_D0);
+                                       if (0 != result) {
+                                               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to turn cooling device [%p] 'on'\n", active->devices.handles[j]));
+                                               continue;
+                                       }
+                                       active->flags.enabled = 1;
+                                       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling device [%p] now 'on'\n", active->devices.handles[j]));
+                               }
+                       }
+               }
+               /*
+                * Below Threshold?
+                * ----------------
+                * Turn OFF all cooling devices associated with this
+                * threshold.
+                */
+               else if (active->flags.enabled) {
+                       for (j = 0; j < active->devices.count; j++) {
+                               result = acpi_bus_set_power(active->devices.handles[j], ACPI_STATE_D3);
+                               if (0 != result) {
+                                       ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to turn cooling device [%p] 'off'\n", active->devices.handles[j]));
+                                       continue;
+                               }
+                               active->flags.enabled = 0;
+                               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling device [%p] now 'off'\n", active->devices.handles[j]));
+                       }
+               }
+       }
+
+       return_VALUE(0);
+}
+
+
+static void acpi_thermal_check (void *context);
+
+static void
+acpi_thermal_run (
+       unsigned long           data)
+{
+       acpi_os_queue_for_execution(OSD_PRIORITY_GPE,  acpi_thermal_check, (void *) data);
+}
+
+
+static void
+acpi_thermal_check (
+       void                    *data)
+{
+       int                     result = 0;
+       struct acpi_thermal     *tz = (struct acpi_thermal *) data;
+       unsigned long           sleep_time = 0;
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_check");
+
+       if (!tz) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid (NULL) context.\n"));
+               return_VOID;
+       }
+
+       result = acpi_thermal_get_temperature(tz);
+       if (0 != result)
+               return_VOID;
+       
+       memset(&tz->state, 0, sizeof(tz->state));
+       
+       /*
+        * Check Trip Points
+        * -----------------
+        * Compare the current temperature to the trip point values to see
+        * if we've entered one of the thermal policy states.  Note that
+        * this function determines when a state is entered, but the 
+        * individual policy decides when it is exited (e.g. hysteresis).
+        */
+       if ((tz->trips.critical.flags.valid) && (tz->temperature >= tz->trips.critical.temperature))
+               tz->trips.critical.flags.enabled = 1;
+       if ((tz->trips.hot.flags.valid) && (tz->temperature >= tz->trips.hot.temperature))
+               tz->trips.hot.flags.enabled = 1;
+       if ((tz->trips.passive.flags.valid) && (tz->temperature >= tz->trips.passive.temperature))
+               tz->trips.passive.flags.enabled = 1;
+       for (i=0; i<ACPI_THERMAL_MAX_ACTIVE; i++)
+               if ((tz->trips.active[i].flags.valid) && (tz->temperature >= tz->trips.active[i].temperature))
+                       tz->trips.active[i].flags.enabled = 1;
+
+       /*
+        * Invoke Policy
+        * -------------
+        * Separated from the above check to allow individual policy to 
+        * determine when to exit a given state.
+        */
+       if (tz->trips.critical.flags.enabled)
+               acpi_thermal_critical(tz);
+       if (tz->trips.hot.flags.enabled)
+               acpi_thermal_hot(tz);
+       if (tz->trips.passive.flags.enabled)
+               acpi_thermal_passive(tz);
+       if (tz->trips.active[0].flags.enabled)
+               acpi_thermal_active(tz);
+
+       /*
+        * Calculate State
+        * ---------------
+        * Again, separated from the above two to allow independent policy
+        * decisions.
+        */
+       if (tz->trips.critical.flags.enabled)
+               tz->state.critical = 1;
+       if (tz->trips.hot.flags.enabled)
+               tz->state.hot = 1;
+       if (tz->trips.passive.flags.enabled)
+               tz->state.passive = 1;
+       for (i=0; i<ACPI_THERMAL_MAX_ACTIVE; i++)
+               if (tz->trips.active[i].flags.enabled)
+                       tz->state.active = 1;
+
+       /*
+        * Calculate Sleep Time
+        * --------------------
+        * If we're in the passive state, use _TSP's value.  Otherwise
+        * use the default polling frequency (e.g. _TZP).  If no polling
+        * frequency is specified then we'll wait forever (at least until
+        * a thermal event occurs).  Note that _TSP and _TZD values are
+        * given in 1/10th seconds (we must covert to milliseconds).
+        */
+       if (tz->state.passive)
+               sleep_time = tz->trips.passive.tsp * 100;
+       else if (tz->polling_frequency > 0)
+               sleep_time = tz->polling_frequency * 100;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: temperature[%lu] sleep[%lu]\n", 
+               tz->name, tz->temperature, sleep_time));
+
+       /*
+        * Schedule Next Poll
+        * ------------------
+        */
+       if (!sleep_time) {
+               if (timer_pending(&(tz->timer)))
+                       del_timer(&(tz->timer));
+       }
+       else {
+               if (timer_pending(&(tz->timer)))
+                       mod_timer(&(tz->timer), (HZ * sleep_time) / 1000);
+               else {
+                       tz->timer.data = (u32) tz;
+                       tz->timer.function = acpi_thermal_run;
+                       tz->timer.expires = jiffies + (HZ * sleep_time) / 1000;
+                       add_timer(&(tz->timer));
+               }
+       }
+
+       return_VOID;
+}
+
+
+/* --------------------------------------------------------------------------
+                              FS Interface (/proc)
+   -------------------------------------------------------------------------- */
+
+#include <linux/compatmac.h>
+#include <linux/proc_fs.h>
+
+struct proc_dir_entry          *acpi_thermal_dir = NULL;
+
+
+static int
+acpi_thermal_read_state (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_thermal     *tz = (struct acpi_thermal *) data;
+       char                    *p = page;
+       int                     len = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_read_state");
+
+       if (!tz || (off != 0))
+               goto end;
+
+       p += sprintf(p, "state:                   ");
+
+       if (!tz->state.critical && !tz->state.hot && !tz->state.passive && !tz->state.active)
+               p += sprintf(p, "ok\n");
+       else {
+               if (tz->state.critical)
+                       p += sprintf(p, "critical ");
+               if (tz->state.hot)
+                       p += sprintf(p, "hot ");
+               if (tz->state.passive)
+                       p += sprintf(p, "passive[%d] ", tz->state.passive_index);
+               if (tz->state.active)
+                       p += sprintf(p, "active[%d]", tz->state.active_index);
+               p += sprintf(p, "\n");
+       }
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_thermal_read_temperature (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       int                     result = 0;
+       struct acpi_thermal     *tz = (struct acpi_thermal *) data;
+       char                    *p = page;
+       int                     len = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_read_temperature");
+
+       if (!tz || (off != 0))
+               goto end;
+
+       result = acpi_thermal_get_temperature(tz);
+       if (0 != result)
+               goto end;
+
+       p += sprintf(p, "temperature:             %lu C\n", 
+               KELVIN_TO_CELSIUS(tz->temperature));
+       
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_thermal_read_trip_points (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_thermal     *tz = (struct acpi_thermal *) data;
+       char                    *p = page;
+       int                     len = 0;
+       int                     i = 0;
+       int                     j = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_read_trip_points");
+
+       if (!tz || (off != 0))
+               goto end;
+
+       if (tz->trips.critical.flags.valid)
+               p += sprintf(p, "critical (S5):           %lu C\n",
+                       KELVIN_TO_CELSIUS(tz->trips.critical.temperature));
+
+       if (tz->trips.hot.flags.valid)
+               p += sprintf(p, "hot (S4):                %lu C\n",
+                       KELVIN_TO_CELSIUS(tz->trips.hot.temperature));
+
+       if (tz->trips.passive.flags.valid) {
+               p += sprintf(p, "passive:                 %lu C: tc1=%lu tc2=%lu tsp=%lu devices=",
+                       KELVIN_TO_CELSIUS(tz->trips.passive.temperature),
+                       tz->trips.passive.tc1,
+                       tz->trips.passive.tc2, 
+                       tz->trips.passive.tsp);
+               for (j=0; j<tz->trips.passive.devices.count; j++) 
+                       p += sprintf(p, "0x%p ", 
+                               tz->trips.passive.devices.handles[j]);
+               p += sprintf(p, "\n");
+       }
+
+       for (i=0; i<ACPI_THERMAL_MAX_ACTIVE; i++) {
+               if (!(tz->trips.active[i].flags.valid))
+                       break;
+               p += sprintf(p, "active[%d]:               %lu C: devices=",
+                       i, KELVIN_TO_CELSIUS(tz->trips.active[i].temperature));
+               for (j=0; j<tz->trips.active[i].devices.count; j++) 
+                       p += sprintf(p, "0x%p ",
+                               tz->trips.active[i].devices.handles[j]);
+               p += sprintf(p, "\n");
+       }
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_thermal_read_cooling_mode (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_thermal     *tz = (struct acpi_thermal *) data;
+       char                    *p = page;
+       int                     len = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_read_cooling_mode");
+
+       if (!tz || (off != 0))
+               goto end;
+
+       if (!tz->flags.cooling_mode) {
+               p += sprintf(p, "<not supported>\n");
+               goto end;
+       }
+
+       p += sprintf(p, "cooling mode:            %s\n",
+               tz->cooling_mode?"passive":"active");
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_thermal_write_cooling_mode (
+       struct file             *file,
+       const char              *buffer,
+       unsigned long           count,
+       void                    *data)
+{
+       int                     result = 0;
+       struct acpi_thermal     *tz = (struct acpi_thermal *) data;
+       char                    mode_string[12] = {'\0'};
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_write_cooling_mode");
+
+       if (!tz || (count > sizeof(mode_string) - 1))
+               return_VALUE(-EINVAL);
+
+       if (!tz->flags.cooling_mode)
+               return_VALUE(-ENODEV);
+
+       if (copy_from_user(mode_string, buffer, count))
+               return_VALUE(-EFAULT);
+       
+       mode_string[count] = '\0';
+       
+       result = acpi_thermal_set_cooling_mode(tz, 
+               simple_strtoul(mode_string, NULL, 0));
+       if (0 != result)
+               return_VALUE(result);
+
+       return_VALUE(count);
+}
+
+
+static int
+acpi_thermal_read_polling (
+       char                    *page,
+       char                    **start,
+       off_t                   off,
+       int                     count,
+       int                     *eof,
+       void                    *data)
+{
+       struct acpi_thermal     *tz = (struct acpi_thermal *) data;
+       char                    *p = page;
+       int                     len = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_read_polling");
+
+       if (!tz || (off != 0))
+               goto end;
+
+       if (!tz->polling_frequency) {
+               p += sprintf(p, "<polling disabled>\n");
+               goto end;
+       }
+
+       p += sprintf(p, "polling frequency:       %lu seconds\n",
+               (tz->polling_frequency / 10));
+
+end:
+       len = (p - page);
+       if (len <= off+count) *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len>count) len = count;
+       if (len<0) len = 0;
+
+       return_VALUE(len);
+}
+
+
+static int
+acpi_thermal_write_polling (
+       struct file             *file,
+       const char              *buffer,
+       unsigned long           count,
+       void                    *data)
+{
+       int                     result = 0;
+       struct acpi_thermal     *tz = (struct acpi_thermal *) data;
+       char                    polling_string[12] = {'\0'};
+       int                     seconds = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_write_polling");
+
+       if (!tz || (count > sizeof(polling_string) - 1))
+               return_VALUE(-EINVAL);
+       
+       if (copy_from_user(polling_string, buffer, count))
+               return_VALUE(-EFAULT);
+       
+       polling_string[count] = '\0';
+
+       seconds = simple_strtoul(polling_string, NULL, 0);
+       
+       result = acpi_thermal_set_polling(tz, seconds);
+       if (0 != result)
+               return_VALUE(result);
+
+       acpi_thermal_check(tz);
+
+       return_VALUE(count);
+}
+
+
+static int
+acpi_thermal_add_fs (
+       struct acpi_device      *device)
+{
+       struct proc_dir_entry   *entry = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_add_fs");
+
+       if (!acpi_thermal_dir) {
+               acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, 
+                       acpi_root_dir);
+               if (!acpi_thermal_dir)
+                       return_VALUE(-ENODEV);
+       }
+
+       if (!acpi_device_dir(device)) {
+               acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+                       acpi_thermal_dir);
+               if (!acpi_device_dir(device))
+                       return_VALUE(-ENODEV);
+       }
+
+       /* 'state' [R] */
+       entry = create_proc_entry(ACPI_THERMAL_FILE_STATE,
+               S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_THERMAL_FILE_STATE));
+       else {
+               entry->read_proc = acpi_thermal_read_state;
+               entry->data = acpi_driver_data(device);
+       }
+
+       /* 'temperature' [R] */
+       entry = create_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE,
+               S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_THERMAL_FILE_TEMPERATURE));
+       else {
+               entry->read_proc = acpi_thermal_read_temperature;
+               entry->data = acpi_driver_data(device);
+       }
+
+       /* 'trip_points' [R] */
+       entry = create_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS,
+               S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_THERMAL_FILE_POLLING_FREQ));
+       else {
+               entry->read_proc = acpi_thermal_read_trip_points;
+               entry->data = acpi_driver_data(device);
+       }
+
+       /* 'cooling_mode' [R/W] */
+       entry = create_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE,
+               S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_THERMAL_FILE_COOLING_MODE));
+       else {
+               entry->read_proc = acpi_thermal_read_cooling_mode;
+               entry->write_proc = acpi_thermal_write_cooling_mode;
+               entry->data = acpi_driver_data(device);
+       }
+
+       /* 'polling_frequency' [R/W] */
+       entry = create_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ,
+               S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_THERMAL_FILE_POLLING_FREQ));
+       else {
+               entry->read_proc = acpi_thermal_read_polling;
+               entry->write_proc = acpi_thermal_write_polling;
+               entry->data = acpi_driver_data(device);
+       }
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_thermal_remove_fs (
+       struct acpi_device      *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_thermal_remove_fs");
+
+       if (!acpi_thermal_dir)
+               return_VALUE(-ENODEV);
+
+       if (acpi_device_dir(device))
+               remove_proc_entry(acpi_device_bid(device), acpi_thermal_dir);
+
+       return_VALUE(0);
+}
+
+
+/* --------------------------------------------------------------------------
+                                 Driver Interface
+   -------------------------------------------------------------------------- */
+
+static void
+acpi_thermal_notify (
+       acpi_handle             handle,
+       u32                     event,
+       void                    *data)
+{
+       struct acpi_thermal     *tz = (struct acpi_thermal *) data;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_notify");
+
+       if (!tz)
+               return_VOID;
+
+       if (0 != acpi_bus_get_device(tz->handle, &device))
+               return_VOID;
+
+       switch (event) {
+       case ACPI_THERMAL_NOTIFY_TEMPERATURE:
+               acpi_thermal_check(tz);
+               break;
+       case ACPI_THERMAL_NOTIFY_THRESHOLDS:
+               acpi_thermal_get_trip_points(tz);
+               acpi_thermal_check(tz);
+               acpi_bus_generate_event(device, event, 0);
+               break;
+       case ACPI_THERMAL_NOTIFY_DEVICES:
+               if (tz->flags.devices)
+                       acpi_thermal_get_devices(tz);
+               acpi_bus_generate_event(device, event, 0);
+               break;
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Unsupported event [0x%x]\n", event));
+               break;
+       }
+
+       return_VOID;
+}
+
+
+static int
+acpi_thermal_get_info (
+       struct acpi_thermal     *tz)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_get_info");
+
+       if (!tz)
+               return_VALUE(-EINVAL);
+
+       /* Get temperature [_TMP] (required) */
+       result = acpi_thermal_get_temperature(tz);
+       if (0 != result)
+               return_VALUE(result);
+
+       /* Set the cooling mode [_SCP] to active cooling (default) */
+       result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE);
+       if (0 == result)
+               tz->flags.cooling_mode = 1;
+
+       /* Get trip points [_CRT, _PSV, etc.] (required) */
+       result = acpi_thermal_get_trip_points(tz);
+       if (0 != result)
+               return_VALUE(result);
+
+       /* Get default polling frequency [_TZP] (optional) */
+       if (tzp)
+               tz->polling_frequency = tzp;
+       else
+               acpi_thermal_get_polling_frequency(tz);
+
+       /* Get devices in this thermal zone [_TZD] (optional) */
+       result = acpi_thermal_get_devices(tz);
+       if (0 == result)
+               tz->flags.devices = 1;
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_thermal_add (
+       struct acpi_device              *device)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_thermal     *tz = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_add");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       tz = kmalloc(sizeof(struct acpi_thermal), GFP_KERNEL);
+       if (!tz)
+               return_VALUE(-ENOMEM);
+       memset(tz, 0, sizeof(struct acpi_thermal));
+
+       tz->handle = device->handle;
+       sprintf(tz->name, "%s", device->pnp.bus_id);
+       sprintf(acpi_device_name(device), "%s", ACPI_THERMAL_DEVICE_NAME);
+       sprintf(acpi_device_class(device), "%s", ACPI_THERMAL_CLASS);
+       acpi_driver_data(device) = tz;
+
+       result = acpi_thermal_get_info(tz);
+       if (0 != result)
+               goto end;
+
+       result = acpi_thermal_add_fs(device);
+       if (0 != result)
+               return_VALUE(result);
+
+       acpi_thermal_check(tz);
+
+       status = acpi_install_notify_handler(tz->handle,
+               ACPI_DEVICE_NOTIFY, acpi_thermal_notify, tz);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error installing notify handler\n"));
+               result = -ENODEV;
+               goto end;
+       }
+
+       init_timer(&tz->timer);
+
+       printk(KERN_INFO PREFIX "%s [%s] (%lu C)\n",
+               acpi_device_name(device), acpi_device_bid(device),
+               KELVIN_TO_CELSIUS(tz->temperature));
+
+end:
+       if (result) {
+               acpi_thermal_remove_fs(device);
+               kfree(tz);
+       }
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_thermal_remove (
+       struct acpi_device      *device,
+       int                     type)
+{
+       acpi_status             status = AE_OK;
+       struct acpi_thermal     *tz = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_remove");
+
+       if (!device || !acpi_driver_data(device))
+               return_VALUE(-EINVAL);
+
+       tz = (struct acpi_thermal *) acpi_driver_data(device);
+
+       if (timer_pending(&(tz->timer)))
+               del_timer(&(tz->timer));
+
+       status = acpi_remove_notify_handler(tz->handle,
+               ACPI_DEVICE_NOTIFY, acpi_thermal_notify);
+       if (ACPI_FAILURE(status))
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error removing notify handler\n"));
+
+       /* Terminate policy */
+       if (tz->trips.passive.flags.valid
+               && tz->trips.passive.flags.enabled) {
+               tz->trips.passive.flags.enabled = 0;
+               acpi_thermal_passive(tz);
+       }
+       if (tz->trips.active[0].flags.valid
+               && tz->trips.active[0].flags.enabled) {
+               tz->trips.active[0].flags.enabled = 0;
+               acpi_thermal_active(tz);
+       }
+
+       acpi_thermal_remove_fs(device);
+
+       return_VALUE(0);
+}
+
+
+static int __init
+acpi_thermal_init (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_init");
+
+       result = acpi_bus_register_driver(&acpi_thermal_driver);
+       if (0 > result)
+               return_VALUE(-ENODEV);
+
+       return_VALUE(0);
+}
+
+
+static void __exit
+acpi_thermal_exit (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_thermal_exit");
+
+       result = acpi_bus_unregister_driver(&acpi_thermal_driver);
+       if (0 == result)
+               remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir);
+
+       return_VOID;
+}
+
+
+module_init(acpi_thermal_init);
+module_exit(acpi_thermal_exit);
diff --git a/drivers/acpi/acpi_utils.c b/drivers/acpi/acpi_utils.c
new file mode 100644 (file)
index 0000000..9df4971
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ *  acpi_utils.c - ACPI Utility Functions ($Revision: 5 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.  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.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include "acpi_bus.h"
+#include "acpi_drivers.h"
+
+
+#define _COMPONENT             ACPI_BUS_COMPONENT
+ACPI_MODULE_NAME               ("acpi_utils")
+
+
+/* --------------------------------------------------------------------------
+                            Object Evaluation Helpers
+   -------------------------------------------------------------------------- */
+
+#ifdef ACPI_DEBUG
+#define acpi_util_eval_error(h,p,s) {\
+       char prefix[80] = {'\0'};\
+       acpi_buffer buffer = {sizeof(prefix), prefix};\
+       acpi_get_name(h, ACPI_FULL_PATHNAME, &buffer);\
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluate [%s.%s]: %s\n",\
+               (char *) prefix, p, acpi_format_exception(s))); }
+#else
+#define acpi_util_eval_error(h,p,s)
+#endif
+
+
+acpi_status
+acpi_extract_package (
+       acpi_object             *package,
+       acpi_buffer             *format,
+       acpi_buffer             *buffer)
+{
+       u32                     size_required = 0;
+       u32                     tail_offset = 0;
+       char                    *format_string = NULL;
+       u32                     format_count = 0;
+       u32                     i = 0;
+       u8                      *head = NULL;
+       u8                      *tail = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_extract_package");
+
+       if (!package || (package->type != ACPI_TYPE_PACKAGE) || (package->package.count < 1)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid 'package' argument\n"));
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+       }
+
+       if (!format || !format->pointer || (format->length < 1)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid 'format' argument\n"));
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+       }
+
+       if (!buffer) {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid 'buffer' argument\n"));
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+       }
+
+       format_count = (format->length/sizeof(char)) - 1;
+       if (format_count > package->package.count) {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Format specifies more objects [%d] than exist in package [%d].", format_count, package->package.count));
+               return_ACPI_STATUS(AE_BAD_DATA);
+       }
+
+       format_string = (char*)format->pointer;
+
+       /*
+        * Calculate size_required.
+        */
+       for (i=0; i<format_count; i++) {
+
+               acpi_object *element = &(package->package.elements[i]);
+
+               if (!element) {
+                       return_ACPI_STATUS(AE_BAD_DATA);
+               }
+
+               switch (element->type) {
+
+               case ACPI_TYPE_INTEGER:
+                       switch (format_string[i]) {
+                       case 'N':
+                               size_required += sizeof(acpi_integer);
+                               tail_offset += sizeof(acpi_integer);
+                               break;
+                       case 'S':
+                               size_required += sizeof(char*) + sizeof(acpi_integer) + sizeof(char);
+                               tail_offset += sizeof(char*);
+                               break;
+                       default:
+                               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid package element [%d]: got number, expecing [%c].\n", i, format_string[i]));
+                               return_ACPI_STATUS(AE_BAD_DATA);
+                               break;
+                       }
+                       break;
+
+               case ACPI_TYPE_STRING:
+               case ACPI_TYPE_BUFFER:
+                       switch (format_string[i]) {
+                       case 'S':
+                               size_required += sizeof(char*) + (element->string.length * sizeof(char)) + sizeof(char);
+                               tail_offset += sizeof(char*);
+                               break;
+                       case 'B':
+                               size_required += sizeof(u8*) + (element->buffer.length * sizeof(u8));
+                               tail_offset += sizeof(u8*);
+                               break;
+                       default:
+                               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid package element [%d] got string/buffer, expecing [%c].\n", i, format_string[i]));
+                               return_ACPI_STATUS(AE_BAD_DATA);
+                               break;
+                       }
+                       break;
+
+               case ACPI_TYPE_PACKAGE:
+               default:
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found unsupported element at index=%d\n", i));
+                       /* TBD: handle nested packages... */
+                       return_ACPI_STATUS(AE_SUPPORT);
+                       break;
+               }
+       }
+
+       /*
+        * Validate output buffer.
+        */
+       if (buffer->length < size_required) {
+               buffer->length = size_required;
+               return_ACPI_STATUS(AE_BUFFER_OVERFLOW);
+       }
+       else if (buffer->length != size_required || !buffer->pointer) {
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+       }
+
+       head = buffer->pointer;
+       tail = buffer->pointer + tail_offset;
+
+       /*
+        * Extract package data.
+        */
+       for (i=0; i<format_count; i++) {
+
+               u8 **pointer = NULL;
+               acpi_object *element = &(package->package.elements[i]);
+
+               if (!element) {
+                       return_ACPI_STATUS(AE_BAD_DATA);
+               }
+
+               switch (element->type) {
+
+               case ACPI_TYPE_INTEGER:
+                       switch (format_string[i]) {
+                       case 'N':
+                               *((acpi_integer*)head) = element->integer.value;
+                               head += sizeof(acpi_integer);
+                               break;
+                       case 'S':
+                               pointer = (u8**)head;
+                               *pointer = tail;
+                               *((acpi_integer*)tail) = element->integer.value;
+                               head += sizeof(acpi_integer*);
+                               tail += sizeof(acpi_integer);
+                               /* NULL terminate string */
+                               *tail = (char)0;
+                               tail += sizeof(char);
+                               break;
+                       default:
+                               /* Should never get here */
+                               break;
+                       }
+                       break;
+
+               case ACPI_TYPE_STRING:
+               case ACPI_TYPE_BUFFER:
+                       switch (format_string[i]) {
+                       case 'S':
+                               pointer = (u8**)head;
+                               *pointer = tail;
+                               memcpy(tail, element->string.pointer, element->string.length);
+                               head += sizeof(char*);
+                               tail += element->string.length * sizeof(char);
+                               /* NULL terminate string */
+                               *tail = (char)0;
+                               tail += sizeof(char);
+                               break;
+                       case 'B':
+                               pointer = (u8**)head;
+                               *pointer = tail;
+                               memcpy(tail, element->buffer.pointer, element->buffer.length);
+                               head += sizeof(u8*);
+                               tail += element->buffer.length * sizeof(u8);
+                               break;
+                       default:
+                               /* Should never get here */
+                               break;
+                       }
+                       break;
+
+               case ACPI_TYPE_PACKAGE:
+                       /* TBD: handle nested packages... */
+               default:
+                       /* Should never get here */
+                       break;
+               }
+       }
+
+       return_ACPI_STATUS(AE_OK);
+}
+
+
+acpi_status
+acpi_evaluate (
+       acpi_handle             handle,
+       acpi_string             pathname,
+       acpi_object_list        *arguments,
+       acpi_buffer             *buffer)
+{
+       acpi_status             status = AE_OK;
+
+       ACPI_FUNCTION_TRACE("acpi_evaluate");
+
+       /* If caller provided a buffer it must be unallocated/zero'd. */
+       if (buffer && (buffer->length != 0 || buffer->pointer))
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+       /*
+        * Evalute object. The first attempt is just to get the size of the
+        * object data (that is unless there's no return data); the second
+        * gets the data.
+        */
+       status = acpi_evaluate_object(handle, pathname, arguments, buffer);
+
+       if (ACPI_SUCCESS(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       else if (buffer && (status == AE_BUFFER_OVERFLOW)) {
+
+               /* Gotta allocate - CALLER MUST FREE! */
+               buffer->pointer = kmalloc(buffer->length, GFP_KERNEL);
+               if (!buffer->pointer) {
+                       return_ACPI_STATUS(AE_NO_MEMORY);
+               }
+               memset(buffer->pointer, 0, buffer->length);
+
+               /* Re-evaluate - this time it should work. */
+               status = acpi_evaluate_object(handle, pathname, arguments,
+                       buffer);
+       }
+
+       if (ACPI_FAILURE(status)) {
+               if (status != AE_NOT_FOUND)
+                       acpi_util_eval_error(handle, pathname, status);
+               if (buffer && buffer->pointer) {
+                       kfree(buffer->pointer);
+                       buffer->length = 0;
+               }
+       }
+
+       return_ACPI_STATUS(status);
+}
+
+
+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};
+
+       ACPI_FUNCTION_TRACE("acpi_evaluate_integer");
+
+       if (!data)
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+       status = acpi_evaluate_object(handle, pathname, arguments, &buffer);
+       if (ACPI_FAILURE(status)) {
+               acpi_util_eval_error(handle, pathname, status);
+               return_ACPI_STATUS(status);
+       }
+
+       if (element.type != ACPI_TYPE_INTEGER) {
+               acpi_util_eval_error(handle, pathname, AE_BAD_DATA);
+               return_ACPI_STATUS(AE_BAD_DATA);
+       }
+
+       *data = element.integer.value;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Return value [%lu]\n", *data));
+
+       return_ACPI_STATUS(AE_OK);
+}
+
+
+#if 0
+acpi_status
+acpi_evaluate_string (
+       acpi_handle             handle,
+       acpi_string             pathname,
+       acpi_object_list        *arguments,
+       acpi_string             *data)
+{
+       acpi_status             status = AE_OK;
+       acpi_object             *element = NULL;
+       acpi_buffer             buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+
+       ACPI_FUNCTION_TRACE("acpi_evaluate_string");
+
+       if (!data)
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+
+       status = acpi_evaluate_object(handle, pathname, arguments, &buffer);
+       if (ACPI_FAILURE(status)) {
+               acpi_util_eval_error(handle, pathname, status);
+               return_ACPI_STATUS(status);
+       }
+
+       element = (acpi_object *) buffer.pointer;
+
+       if ((element->type != ACPI_TYPE_STRING) 
+               || (element->type != ACPI_TYPE_BUFFER)
+               || !element->string.length) {
+               acpi_util_eval_error(handle, pathname, AE_BAD_DATA);
+               return_ACPI_STATUS(AE_BAD_DATA);
+       }
+
+       *data = kmalloc(element->string.length + 1, GFP_KERNEL);
+       if (!data) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Memory allocation error\n"));
+               return_VALUE(-ENOMEM);
+       }
+       memset(*data, 0, element->string.length + 1);
+
+       memcpy(*data, element->string.pointer, element->string.length);
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Return value [%s]\n", *data));
+
+       return_ACPI_STATUS(AE_OK);
+}
+#endif
+
+
+acpi_status
+acpi_evaluate_reference (
+       acpi_handle             handle,
+       acpi_string             pathname,
+       acpi_object_list        *arguments,
+       struct acpi_handle_list *list)
+{
+       acpi_status             status = AE_OK;
+       acpi_object             *package = NULL;
+       acpi_object             *element = NULL;
+       acpi_buffer             buffer = {0, NULL};
+       u32                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_evaluate_reference");
+
+       if (!list) {
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+       }
+
+       /* Evaluate object. */
+
+       status = acpi_evaluate(handle, pathname, arguments, &buffer);
+       if (ACPI_FAILURE(status))
+               goto end;
+
+       package = (acpi_object *) buffer.pointer;
+
+       if (!package || (package->type != ACPI_TYPE_PACKAGE)
+               || (package->package.count == 0)) {
+               status = AE_BAD_DATA;
+               acpi_util_eval_error(handle, pathname, status);
+               goto end;
+       }
+
+       /* Allocate list - CALLER MUST FREE! */
+       list->count = package->package.count;
+       if (list->count > 10) {
+               return AE_NO_MEMORY;
+       }
+       /* TBD: dynamically allocate */
+       /*
+       list->handles = kmalloc(sizeof(acpi_handle)*(list->count), GFP_KERNEL);
+       if (!list->handles) {
+               return_ACPI_STATUS(AE_NO_MEMORY);
+       }
+       memset(list->handles, 0, sizeof(acpi_handle)*(list->count));
+       */
+
+       /* Parse package data. */
+
+       for (i = 0; i < list->count; i++) {
+
+               element = &(package->package.elements[i]);
+
+               if (!element || (element->type != ACPI_TYPE_STRING)) {
+                       status = AE_BAD_DATA;
+                       ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid element in package (not a device reference)\n"));
+                       acpi_util_eval_error(handle, pathname, status);
+                       break;
+               }
+
+               /* Convert reference (e.g. "\_PR_.CPU_") to acpi_handle. */
+
+               status = acpi_get_handle(handle, element->string.pointer,
+                       &(list->handles[i]));
+               if (ACPI_FAILURE(status)) {
+                       status = AE_BAD_DATA;
+                       ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to resolve device reference [%s]\n", element->string.pointer));
+                       acpi_util_eval_error(handle, pathname, status);
+                       break;
+               }
+
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resolved reference [%s]->[%p]\n", element->string.pointer, list->handles[i]));
+       }
+
+end:
+       if (ACPI_FAILURE(status)) {
+               list->count = 0;
+               //kfree(list->handles);
+       }
+
+       kfree(buffer.pointer);
+
+       return_ACPI_STATUS(status);
+}
+
+