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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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__*/
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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__*/
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+
/*
- * 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);
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);
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);
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
+}
+
+