aboutsummaryrefslogtreecommitdiff
path: root/drivers/acpi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/Kconfig15
-rw-r--r--drivers/acpi/asus_acpi.c62
-rw-r--r--drivers/acpi/button.c223
-rw-r--r--drivers/acpi/dock.c132
-rw-r--r--drivers/acpi/ec.c343
-rw-r--r--drivers/acpi/glue.c20
-rw-r--r--drivers/acpi/ibm_acpi.c1037
-rw-r--r--drivers/acpi/osl.c41
-rw-r--r--drivers/acpi/processor_perflib.c4
-rw-r--r--drivers/acpi/toshiba_acpi.c88
10 files changed, 1446 insertions, 519 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 0f9d4be7ed7..b0897a0b206 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -97,6 +97,7 @@ config ACPI_BATTERY
config ACPI_BUTTON
tristate "Button"
+ depends on INPUT
default y
help
This driver handles events on the power, sleep and lid buttons.
@@ -172,6 +173,7 @@ config ACPI_NUMA
config ACPI_ASUS
tristate "ASUS/Medion Laptop Extras"
depends on X86
+ select BACKLIGHT_CLASS_DEVICE
---help---
This driver provides support for extra features of ACPI-compatible
ASUS laptops. As some of Medion laptops are made by ASUS, it may also
@@ -200,6 +202,7 @@ config ACPI_ASUS
config ACPI_IBM
tristate "IBM ThinkPad Laptop Extras"
depends on X86
+ select BACKLIGHT_CLASS_DEVICE
---help---
This is a Linux ACPI driver for the IBM ThinkPad laptops. It adds
support for Fn-Fx key combinations, Bluetooth control, video
@@ -222,9 +225,21 @@ config ACPI_IBM_DOCK
If you are not sure, say N here.
+config ACPI_IBM_BAY
+ bool "Legacy Removable Bay Support"
+ depends on ACPI_IBM
+ depends on ACPI_BAY=n
+ default n
+ ---help---
+ Allows the ibm_acpi driver to handle removable bays.
+ This support is obsoleted by CONFIG_ACPI_BAY.
+
+ If you are not sure, say N here.
+
config ACPI_TOSHIBA
tristate "Toshiba Laptop Extras"
depends on X86
+ select BACKLIGHT_CLASS_DEVICE
---help---
This driver adds support for access to certain system settings
on "legacy free" Toshiba laptops. These laptops can be recognized by
diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c
index c7ac9297a20..bf7bc25e680 100644
--- a/drivers/acpi/asus_acpi.c
+++ b/drivers/acpi/asus_acpi.c
@@ -35,6 +35,7 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
+#include <linux/backlight.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include <asm/uaccess.h>
@@ -402,6 +403,8 @@ static struct model_data model_conf[END_MODEL] = {
/* procdir we use */
static struct proc_dir_entry *asus_proc_dir;
+static struct backlight_device *asus_backlight_device;
+
/*
* This header is made available to allow proper configuration given model,
* revision number , ... this info cannot go in struct asus_hotk because it is
@@ -779,7 +782,7 @@ proc_write_lcd(struct file *file, const char __user * buffer,
return rv;
}
-static int read_brightness(void)
+static int read_brightness(struct backlight_device *bd)
{
int value;
@@ -801,9 +804,10 @@ static int read_brightness(void)
/*
* Change the brightness level
*/
-static void set_brightness(int value)
+static int set_brightness(int value)
{
acpi_status status = 0;
+ int ret = 0;
/* SPLV laptop */
if (hotk->methods->brightness_set) {
@@ -811,11 +815,12 @@ static void set_brightness(int value)
value, NULL))
printk(KERN_WARNING
"Asus ACPI: Error changing brightness\n");
- return;
+ ret = -EIO;
+ goto out;
}
/* No SPLV method if we are here, act as appropriate */
- value -= read_brightness();
+ value -= read_brightness(NULL);
while (value != 0) {
status = acpi_evaluate_object(NULL, (value > 0) ?
hotk->methods->brightness_up :
@@ -825,15 +830,22 @@ static void set_brightness(int value)
if (ACPI_FAILURE(status))
printk(KERN_WARNING
"Asus ACPI: Error changing brightness\n");
+ ret = -EIO;
}
- return;
+out:
+ return ret;
+}
+
+static int set_brightness_status(struct backlight_device *bd)
+{
+ return set_brightness(bd->props->brightness);
}
static int
proc_read_brn(char *page, char **start, off_t off, int count, int *eof,
void *data)
{
- return sprintf(page, "%d\n", read_brightness());
+ return sprintf(page, "%d\n", read_brightness(NULL));
}
static int
@@ -1333,6 +1345,26 @@ static int asus_hotk_remove(struct acpi_device *device, int type)
return 0;
}
+static struct backlight_properties asus_backlight_data = {
+ .owner = THIS_MODULE,
+ .get_brightness = read_brightness,
+ .update_status = set_brightness_status,
+ .max_brightness = 15,
+};
+
+static void __exit asus_acpi_exit(void)
+{
+ if (asus_backlight_device)
+ backlight_device_unregister(asus_backlight_device);
+
+ acpi_bus_unregister_driver(&asus_hotk_driver);
+ remove_proc_entry(PROC_ASUS, acpi_root_dir);
+
+ kfree(asus_info);
+
+ return;
+}
+
static int __init asus_acpi_init(void)
{
int result;
@@ -1370,17 +1402,15 @@ static int __init asus_acpi_init(void)
return result;
}
- return 0;
-}
-
-static void __exit asus_acpi_exit(void)
-{
- acpi_bus_unregister_driver(&asus_hotk_driver);
- remove_proc_entry(PROC_ASUS, acpi_root_dir);
-
- kfree(asus_info);
+ asus_backlight_device = backlight_device_register("asus", NULL,
+ &asus_backlight_data);
+ if (IS_ERR(asus_backlight_device)) {
+ printk(KERN_ERR "Could not register asus backlight device\n");
+ asus_backlight_device = NULL;
+ asus_acpi_exit();
+ }
- return;
+ return 0;
}
module_init(asus_acpi_init);
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index 5ef885e82c7..ac860583c20 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -29,6 +29,7 @@
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
+#include <linux/input.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
@@ -62,7 +63,7 @@
#define _COMPONENT ACPI_BUTTON_COMPONENT
ACPI_MODULE_NAME("acpi_button")
- MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_AUTHOR("Paul Diefenbaugh");
MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME);
MODULE_LICENSE("GPL");
@@ -78,12 +79,14 @@ static struct acpi_driver acpi_button_driver = {
.ops = {
.add = acpi_button_add,
.remove = acpi_button_remove,
- },
+ },
};
struct acpi_button {
struct acpi_device *device; /* Fixed button kludge */
- u8 type;
+ unsigned int type;
+ struct input_dev *input;
+ char phys[32]; /* for input device */
unsigned long pushed;
};
@@ -109,8 +112,7 @@ static struct proc_dir_entry *acpi_button_dir;
static int acpi_button_info_seq_show(struct seq_file *seq, void *offset)
{
- struct acpi_button *button = (struct acpi_button *)seq->private;
-
+ struct acpi_button *button = seq->private;
if (!button || !button->device)
return 0;
@@ -128,22 +130,17 @@ static int acpi_button_info_open_fs(struct inode *inode, struct file *file)
static int acpi_button_state_seq_show(struct seq_file *seq, void *offset)
{
- struct acpi_button *button = (struct acpi_button *)seq->private;
+ struct acpi_button *button = seq->private;
acpi_status status;
unsigned long state;
-
if (!button || !button->device)
return 0;
status = acpi_evaluate_integer(button->device->handle, "_LID", NULL, &state);
- if (ACPI_FAILURE(status)) {
- seq_printf(seq, "state: unsupported\n");
- } else {
- seq_printf(seq, "state: %s\n",
- (state ? "open" : "closed"));
- }
-
+ seq_printf(seq, "state: %s\n",
+ ACPI_FAILURE(status) ? "unsupported" :
+ (state ? "open" : "closed"));
return 0;
}
@@ -159,8 +156,7 @@ static struct proc_dir_entry *acpi_lid_dir;
static int acpi_button_add_fs(struct acpi_device *device)
{
struct proc_dir_entry *entry = NULL;
- struct acpi_button *button = NULL;
-
+ struct acpi_button *button;
if (!device || !acpi_driver_data(device))
return -EINVAL;
@@ -228,10 +224,8 @@ static int acpi_button_add_fs(struct acpi_device *device)
static int acpi_button_remove_fs(struct acpi_device *device)
{
- struct acpi_button *button = NULL;
-
+ struct acpi_button *button = acpi_driver_data(device);
- button = acpi_driver_data(device);
if (acpi_device_dir(device)) {
if (button->type == ACPI_BUTTON_TYPE_LID)
remove_proc_entry(ACPI_BUTTON_FILE_STATE,
@@ -253,14 +247,34 @@ static int acpi_button_remove_fs(struct acpi_device *device)
static void acpi_button_notify(acpi_handle handle, u32 event, void *data)
{
- struct acpi_button *button = (struct acpi_button *)data;
-
+ struct acpi_button *button = data;
+ struct input_dev *input;
if (!button || !button->device)
return;
switch (event) {
case ACPI_BUTTON_NOTIFY_STATUS:
+ input = button->input;
+
+ if (button->type == ACPI_BUTTON_TYPE_LID) {
+ struct acpi_handle *handle = button->device->handle;
+ unsigned long state;
+
+ if (!ACPI_FAILURE(acpi_evaluate_integer(handle, "_LID",
+ NULL, &state)))
+ input_report_switch(input, SW_LID, !state);
+
+ } else {
+ int keycode = test_bit(KEY_SLEEP, input->keybit) ?
+ KEY_SLEEP : KEY_POWER;
+
+ input_report_key(input, keycode, 1);
+ input_sync(input);
+ input_report_key(input, keycode, 0);
+ }
+ input_sync(input);
+
acpi_bus_generate_event(button->device, event,
++button->pushed);
break;
@@ -275,8 +289,7 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data)
static acpi_status acpi_button_notify_fixed(void *data)
{
- struct acpi_button *button = (struct acpi_button *)data;
-
+ struct acpi_button *button = data;
if (!button)
return AE_BAD_PARAMETER;
@@ -286,24 +299,75 @@ static acpi_status acpi_button_notify_fixed(void *data)
return AE_OK;
}
-static int acpi_button_add(struct acpi_device *device)
+static int acpi_button_install_notify_handlers(struct acpi_button *button)
{
- int result = 0;
- acpi_status status = AE_OK;
- struct acpi_button *button = NULL;
+ acpi_status status;
+ 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->device->handle,
+ ACPI_DEVICE_NOTIFY,
+ acpi_button_notify,
+ button);
+ break;
+ }
+
+ return ACPI_FAILURE(status) ? -ENODEV : 0;
+}
+
+static void acpi_button_remove_notify_handlers(struct acpi_button *button)
+{
+ switch (button->type) {
+ case ACPI_BUTTON_TYPE_POWERF:
+ acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
+ acpi_button_notify_fixed);
+ break;
+ case ACPI_BUTTON_TYPE_SLEEPF:
+ acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
+ acpi_button_notify_fixed);
+ break;
+ default:
+ acpi_remove_notify_handler(button->device->handle,
+ ACPI_DEVICE_NOTIFY,
+ acpi_button_notify);
+ break;
+ }
+}
+
+static int acpi_button_add(struct acpi_device *device)
+{
+ int error;
+ struct acpi_button *button;
+ struct input_dev *input;
if (!device)
return -EINVAL;
- button = kmalloc(sizeof(struct acpi_button), GFP_KERNEL);
+ button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
if (!button)
return -ENOMEM;
- memset(button, 0, sizeof(struct acpi_button));
button->device = device;
acpi_driver_data(device) = button;
+ button->input = input = input_allocate_device();
+ if (!input) {
+ error = -ENOMEM;
+ goto err_free_button;
+ }
+
/*
* Determine the button type (via hid), as fixed-feature buttons
* need to be handled a bit differently than generic-space.
@@ -338,39 +402,48 @@ static int acpi_button_add(struct acpi_device *device)
} else {
printk(KERN_ERR PREFIX "Unsupported hid [%s]\n",
acpi_device_hid(device));
- result = -ENODEV;
- goto end;
+ error = -ENODEV;
+ goto err_free_input;
}
- result = acpi_button_add_fs(device);
- if (result)
- goto end;
+ error = acpi_button_add_fs(device);
+ if (error)
+ goto err_free_input;
+
+ error = acpi_button_install_notify_handlers(button);
+ if (error)
+ goto err_remove_fs;
+
+ snprintf(button->phys, sizeof(button->phys),
+ "%s/button/input0", acpi_device_hid(device));
+
+ input->name = acpi_device_name(device);
+ input->phys = button->phys;
+ input->id.bustype = BUS_HOST;
+ input->id.product = button->type;
switch (button->type) {
+ case ACPI_BUTTON_TYPE_POWER:
case ACPI_BUTTON_TYPE_POWERF:
- status =
- acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
- acpi_button_notify_fixed,
- button);
+ input->evbit[0] = BIT(EV_KEY);
+ set_bit(KEY_POWER, input->keybit);
break;
+
+ case ACPI_BUTTON_TYPE_SLEEP:
case ACPI_BUTTON_TYPE_SLEEPF:
- status =
- acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
- acpi_button_notify_fixed,
- button);
+ input->evbit[0] = BIT(EV_KEY);
+ set_bit(KEY_SLEEP, input->keybit);
break;
- default:
- status = acpi_install_notify_handler(device->handle,
- ACPI_DEVICE_NOTIFY,
- acpi_button_notify,
- button);
+
+ case ACPI_BUTTON_TYPE_LID:
+ input->evbit[0] = BIT(EV_SW);
+ set_bit(SW_LID, input->swbit);
break;
}
- if (ACPI_FAILURE(status)) {
- result = -ENODEV;
- goto end;
- }
+ error = input_register_device(input);
+ if (error)
+ goto err_remove_handlers;
if (device->wakeup.flags.valid) {
/* Button's GPE is run-wake GPE */
@@ -385,47 +458,31 @@ static int acpi_button_add(struct acpi_device *device)
printk(KERN_INFO PREFIX "%s [%s]\n",
acpi_device_name(device), acpi_device_bid(device));
- end:
- if (result) {
- acpi_button_remove_fs(device);
- kfree(button);
- }
+ return 0;
- return result;
+ err_remove_handlers:
+ acpi_button_remove_notify_handlers(button);
+ err_remove_fs:
+ acpi_button_remove_fs(device);
+ err_free_input:
+ input_free_device(input);
+ err_free_button:
+ kfree(button);
+ return error;
}
static int acpi_button_remove(struct acpi_device *device, int type)
{
- acpi_status status = 0;
- struct acpi_button *button = NULL;
-
+ struct acpi_button *button;
if (!device || !acpi_driver_data(device))
return -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(device->handle,
- ACPI_DEVICE_NOTIFY,
- acpi_button_notify);
- break;
- }
-
+ acpi_button_remove_notify_handlers(button);
acpi_button_remove_fs(device);
-
+ input_unregister_device(button->input);
kfree(button);
return 0;
@@ -433,8 +490,7 @@ static int acpi_button_remove(struct acpi_device *device, int type)
static int __init acpi_button_init(void)
{
- int result = 0;
-
+ int result;
acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
if (!acpi_button_dir)
@@ -451,7 +507,6 @@ static int __init acpi_button_init(void)
static void __exit acpi_button_exit(void)
{
-
acpi_bus_unregister_driver(&acpi_button_driver);
if (acpi_power_dir)
@@ -461,8 +516,6 @@ static void __exit acpi_button_exit(void)
if (acpi_lid_dir)
remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
-
- return;
}
module_init(acpi_button_init);
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index 578b99b71d9..215f5b30a1f 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -27,6 +27,8 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/jiffies.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
@@ -38,6 +40,8 @@ MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME);
MODULE_LICENSE("GPL");
static struct atomic_notifier_head dock_notifier_list;
+static struct platform_device dock_device;
+static char dock_device_name[] = "dock";
struct dock_station {
acpi_handle handle;
@@ -322,10 +326,12 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event)
static void dock_event(struct dock_station *ds, u32 event, int num)
{
+ struct device *dev = &dock_device.dev;
/*
- * we don't do events until someone tells me that
- * they would like to have them.
+ * Indicate that the status of the dock station has
+ * changed.
*/
+ kobject_uevent(&dev->kobj, KOBJ_CHANGE);
}
/**
@@ -440,6 +446,9 @@ static int dock_in_progress(struct dock_station *ds)
*/
int register_dock_notifier(struct notifier_block *nb)
{
+ if (!dock_station)
+ return -ENODEV;
+
return atomic_notifier_chain_register(&dock_notifier_list, nb);
}
@@ -451,6 +460,9 @@ EXPORT_SYMBOL_GPL(register_dock_notifier);
*/
void unregister_dock_notifier(struct notifier_block *nb)
{
+ if (!dock_station)
+ return;
+
atomic_notifier_chain_unregister(&dock_notifier_list, nb);
}
@@ -511,6 +523,37 @@ void unregister_hotplug_dock_device(acpi_handle handle)
EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
/**
+ * handle_eject_request - handle an undock request checking for error conditions
+ *
+ * Check to make sure the dock device is still present, then undock and
+ * hotremove all the devices that may need removing.
+ */
+static int handle_eject_request(struct dock_station *ds, u32 event)
+{
+ if (!dock_present(ds))
+ return -ENODEV;
+
+ if (dock_in_progress(ds))
+ return -EBUSY;
+
+ /*
+ * here we need to generate the undock
+ * event prior to actually doing the undock
+ * so that the device struct still exists.
+ */
+ dock_event(ds, event, UNDOCK_EVENT);
+ hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST);
+ undock(ds);
+ eject_dock(ds);
+ if (dock_present(ds)) {
+ printk(KERN_ERR PREFIX "Unable to undock!\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/**
* dock_notify - act upon an acpi dock notification
* @handle: the dock station handle
* @event: the acpi event
@@ -518,9 +561,7 @@ EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
*
* If we are notified to dock, then check to see if the dock is
* present and then dock. Notify all drivers of the dock event,
- * and then hotplug and devices that may need hotplugging. For undock
- * check to make sure the dock device is still present, then undock
- * and hotremove all the devices that may need removing.
+ * and then hotplug and devices that may need hotplugging.
*/
static void dock_notify(acpi_handle handle, u32 event, void *data)
{
@@ -552,19 +593,7 @@ static void dock_notify(acpi_handle handle, u32 event, void *data)
* to the driver who wish to hotplug.
*/
case ACPI_NOTIFY_EJECT_REQUEST:
- if (!dock_in_progress(ds) && dock_present(ds)) {
- /*
- * here we need to generate the undock
- * event prior to actually doing the undock
- * so that the device struct still exists.
- */
- dock_event(ds, event, UNDOCK_EVENT);
- hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST);
- undock(ds);
- eject_dock(ds);
- if (dock_present(ds))
- printk(KERN_ERR PREFIX "Unable to undock!\n");
- }
+ handle_eject_request(ds, event);
break;
default:
printk(KERN_ERR PREFIX "Unknown dock event %d\n", event);
@@ -603,6 +632,33 @@ find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
return AE_OK;
}
+/*
+ * show_docked - read method for "docked" file in sysfs
+ */
+static ssize_t show_docked(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", dock_present(dock_station));
+
+}
+DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
+
+/*
+ * write_undock - write method for "undock" file in sysfs
+ */
+static ssize_t write_undock(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+
+ if (!count)
+ return -EINVAL;
+
+ ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);
+ return ret ? ret: count;
+}
+DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
+
/**
* dock_add - add a new dock station
* @handle: the dock station handle
@@ -628,6 +684,30 @@ static int dock_add(acpi_handle handle)
spin_lock_init(&dock_station->hp_lock);
ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
+ /* initialize platform device stuff */
+ dock_device.name = dock_device_name;
+ ret = platform_device_register(&dock_device);
+ if (ret) {
+ printk(KERN_ERR PREFIX "Error %d registering dock device\n", ret);
+ kfree(dock_station);
+ return ret;
+ }
+ ret = device_create_file(&dock_device.dev, &dev_attr_docked);
+ if (ret) {
+ printk("Error %d adding sysfs file\n", ret);
+ platform_device_unregister(&dock_device);
+ kfree(dock_station);
+ return ret;
+ }
+ ret = device_create_file(&dock_device.dev, &dev_attr_undock);
+ if (ret) {
+ printk("Error %d adding sysfs file\n", ret);
+ device_remove_file(&dock_device.dev, &dev_attr_docked);
+ platform_device_unregister(&dock_device);
+ kfree(dock_station);
+ return ret;
+ }
+
/* Find dependent devices */
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, find_dock_devices, dock_station,
@@ -637,7 +717,8 @@ static int dock_add(acpi_handle handle)
dd = alloc_dock_dependent_device(handle);
if (!dd) {
kfree(dock_station);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto dock_add_err_unregister;
}
add_dock_dependent_device(dock_station, dd);
@@ -657,8 +738,12 @@ static int dock_add(acpi_handle handle)
return 0;
dock_add_err:
- kfree(dock_station);
kfree(dd);
+dock_add_err_unregister:
+ device_remove_file(&dock_device.dev, &dev_attr_docked);
+ device_remove_file(&dock_device.dev, &dev_attr_undock);
+ platform_device_unregister(&dock_device);
+ kfree(dock_station);
return ret;
}
@@ -685,6 +770,11 @@ static int dock_remove(void)
if (ACPI_FAILURE(status))
printk(KERN_ERR "Error removing notify handler\n");
+ /* cleanup sysfs */
+ device_remove_file(&dock_device.dev, &dev_attr_docked);
+ device_remove_file(&dock_device.dev, &dev_attr_undock);
+ platform_device_unregister(&dock_device);
+
/* free dock station memory */
kfree(dock_station);
return 0;
@@ -725,7 +815,7 @@ static int __init dock_init(void)
ACPI_UINT32_MAX, find_dock, &num, NULL);
if (!num)
- return -ENODEV;
+ printk(KERN_INFO "No dock devices found.\n");
return 0;
}
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 7aa600794ae..4a909bfb805 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -45,35 +45,34 @@ ACPI_MODULE_NAME("acpi_ec")
#define ACPI_EC_DRIVER_NAME "ACPI Embedded Controller Driver"
#define ACPI_EC_DEVICE_NAME "Embedded Controller"
#define ACPI_EC_FILE_INFO "info"
-
+#undef PREFIX
+#define PREFIX "ACPI: EC: "
/* EC status register */
#define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */
#define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */
#define ACPI_EC_FLAG_BURST 0x10 /* burst mode */
#define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */
-
/* EC commands */
-#define ACPI_EC_COMMAND_READ 0x80
-#define ACPI_EC_COMMAND_WRITE 0x81
-#define ACPI_EC_BURST_ENABLE 0x82
-#define ACPI_EC_BURST_DISABLE 0x83
-#define ACPI_EC_COMMAND_QUERY 0x84
-
+enum ec_command {
+ ACPI_EC_COMMAND_READ = 0x80,
+ ACPI_EC_COMMAND_WRITE = 0x81,
+ ACPI_EC_BURST_ENABLE = 0x82,
+ ACPI_EC_BURST_DISABLE = 0x83,
+ ACPI_EC_COMMAND_QUERY = 0x84,
+};
/* EC events */
-enum {
+enum ec_event {
ACPI_EC_EVENT_OBF_1 = 1, /* Output buffer full */
- ACPI_EC_EVENT_IBF_0, /* Input buffer empty */
+ ACPI_EC_EVENT_IBF_0, /* Input buffer empty */
};
-#define ACPI_EC_DELAY 50 /* Wait 50ms max. during EC ops */
+#define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */
#define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */
-#define ACPI_EC_UDELAY 100 /* Poll @ 100us increments */
-#define ACPI_EC_UDELAY_COUNT 1000 /* Wait 10ms max. during EC ops */
-enum {
- EC_INTR = 1, /* Output buffer full */
- EC_POLL, /* Input buffer empty */
-};
+static enum ec_mode {
+ EC_INTR = 1, /* Output buffer full */
+ EC_POLL, /* Input buffer empty */
+} acpi_ec_mode = EC_INTR;
static int acpi_ec_remove(struct acpi_device *device, int type);
static int acpi_ec_start(struct acpi_device *device);
@@ -96,19 +95,18 @@ static struct acpi_driver acpi_ec_driver = {
struct acpi_ec {
acpi_handle handle;
unsigned long uid;
- unsigned long gpe_bit;
+ unsigned long gpe;
unsigned long command_addr;
unsigned long data_addr;
unsigned long global_lock;
- struct semaphore sem;
- unsigned int expect_event;
+ struct mutex lock;
+ atomic_t query_pending;
atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */
wait_queue_head_t wait;
} *ec_ecdt;
/* External interfaces use first EC only, so remember */
static struct acpi_device *first_ec;
-static int acpi_ec_mode = EC_INTR;
/* --------------------------------------------------------------------------
Transaction Management
@@ -134,54 +132,41 @@ static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data)
outb(data, ec->data_addr);
}
-static int acpi_ec_check_status(u8 status, u8 event)
+static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event)
{
- switch (event) {
- case ACPI_EC_EVENT_OBF_1:
+ u8 status = acpi_ec_read_status(ec);
+
+ if (event == ACPI_EC_EVENT_OBF_1) {
if (status & ACPI_EC_FLAG_OBF)
return 1;
- break;
- case ACPI_EC_EVENT_IBF_0:
+ } else if (event == ACPI_EC_EVENT_IBF_0) {
if (!(status & ACPI_EC_FLAG_IBF))
return 1;
- break;
- default:
- break;
}
return 0;
}
-static int acpi_ec_wait(struct acpi_ec *ec, u8 event)
+static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event)
{
- int i = (acpi_ec_mode == EC_POLL) ? ACPI_EC_UDELAY_COUNT : 0;
- long time_left;
-
- ec->expect_event = event;
- if (acpi_ec_check_status(acpi_ec_read_status(ec), event)) {
- ec->expect_event = 0;
- return 0;
- }
-
- do {
- if (acpi_ec_mode == EC_POLL) {
- udelay(ACPI_EC_UDELAY);
- } else {
- time_left = wait_event_timeout(ec->wait,
- !ec->expect_event,
- msecs_to_jiffies(ACPI_EC_DELAY));
- if (time_left > 0) {
- ec->expect_event = 0;
+ if (acpi_ec_mode == EC_POLL) {
+ unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY);
+ while (time_before(jiffies, delay)) {
+ if (acpi_ec_check_status(ec, event))
return 0;
- }
}
- if (acpi_ec_check_status(acpi_ec_read_status(ec), event)) {
- ec->expect_event = 0;
+ } else {
+ if (wait_event_timeout(ec->wait,
+ acpi_ec_check_status(ec, event),
+ msecs_to_jiffies(ACPI_EC_DELAY)) ||
+ acpi_ec_check_status(ec, event)) {
return 0;
+ } else {
+ printk(KERN_ERR PREFIX "acpi_ec_wait timeout,"
+ " status = %d, expect_event = %d\n",
+ acpi_ec_read_status(ec), event);
}
- } while (--i > 0);
-
- ec->expect_event = 0;
+ }
return -ETIME;
}
@@ -196,7 +181,6 @@ int acpi_ec_enter_burst_mode(struct acpi_ec *ec)
u8 tmp = 0;
u8 status = 0;
-
status = acpi_ec_read_status(ec);
if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)) {
status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
@@ -212,7 +196,7 @@ int acpi_ec_enter_burst_mode(struct acpi_ec *ec)
atomic_set(&ec->leaving_burst, 0);
return 0;
- end:
+ end:
ACPI_EXCEPTION((AE_INFO, status, "EC wait, burst mode"));
return -1;
}
@@ -221,58 +205,68 @@ int acpi_ec_leave_burst_mode(struct acpi_ec *ec)
{
u8 status = 0;
-
status = acpi_ec_read_status(ec);
- if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)){
+ if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)) {
status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
- if(status)
+ if (status)
goto end;
acpi_ec_write_cmd(ec, ACPI_EC_BURST_DISABLE);
acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
}
atomic_set(&ec->leaving_burst, 1);
return 0;
- end:
+ end:
ACPI_EXCEPTION((AE_INFO, status, "EC leave burst mode"));
return -1;
}
-#endif /* ACPI_FUTURE_USAGE */
+#endif /* ACPI_FUTURE_USAGE */
static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command,
- const u8 *wdata, unsigned wdata_len,
- u8 *rdata, unsigned rdata_len)
+ const u8 * wdata, unsigned wdata_len,
+ u8 * rdata, unsigned rdata_len)
{
- int result;
+ int result = 0;
acpi_ec_write_cmd(ec, command);
- for (; wdata_len > 0; wdata_len --) {
+ for (; wdata_len > 0; --wdata_len) {
result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
- if (result)
- return result;
+ if (result) {
+ printk(KERN_ERR PREFIX
+ "write_cmd timeout, command = %d\n", command);
+ goto end;
+ }
acpi_ec_write_data(ec, *(wdata++));
}
- if (command == ACPI_EC_COMMAND_WRITE) {
+ if (!rdata_len) {
result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
- if (result)
- return result;
+ if (result) {
+ printk(KERN_ERR PREFIX
+ "finish-write timeout, command = %d\n", command);
+ goto end;
+ }
+ } else if (command == ACPI_EC_COMMAND_QUERY) {
+ atomic_set(&ec->query_pending, 0);
}
- for (; rdata_len > 0; rdata_len --) {
+ for (; rdata_len > 0; --rdata_len) {
result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1);
- if (result)
- return result;
+ if (result) {
+ printk(KERN_ERR PREFIX "read timeout, command = %d\n",
+ command);
+ goto end;
+ }
*(rdata++) = acpi_ec_read_data(ec);
}
-
- return 0;
+ end:
+ return result;
}
static int acpi_ec_transaction(struct acpi_ec *ec, u8 command,
- const u8 *wdata, unsigned wdata_len,
- u8 *rdata, unsigned rdata_len)
+ const u8 * wdata, unsigned wdata_len,
+ u8 * rdata, unsigned rdata_len)
{
int status;
u32 glk;
@@ -280,36 +274,40 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command,
if (!ec || (wdata_len && !wdata) || (rdata_len && !rdata))
return -EINVAL;
- if (rdata)
- memset(rdata, 0, rdata_len);
+ if (rdata)
+ memset(rdata, 0, rdata_len);
+ mutex_lock(&ec->lock);
if (ec->global_lock) {
status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
if (ACPI_FAILURE(status))
return -ENODEV;
}
- down(&ec->sem);
+
+ /* Make sure GPE is enabled before doing transaction */
+ acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0);
if (status) {
- printk(KERN_DEBUG PREFIX "read EC, IB not empty\n");
+ printk(KERN_DEBUG PREFIX
+ "input buffer is not empty, aborting transaction\n");
goto end;
}
- status = acpi_ec_transaction_unlocked(ec, command,
- wdata, wdata_len,
- rdata, rdata_len);
+ status = acpi_ec_transaction_unlocked(ec, command,
+ wdata, wdata_len,
+ rdata, rdata_len);
-end:
- up(&ec->sem);
+ end:
if (ec->global_lock)
acpi_release_global_lock(glk);
+ mutex_unlock(&ec->lock);
return status;
}
-static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 *data)
+static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data)
{
int result;
u8 d;
@@ -322,15 +320,15 @@ static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 *data)
static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data)
{
- u8 wdata[2] = { address, data };
- return acpi_ec_transaction(ec, ACPI_EC_COMMAND_WRITE,
+ u8 wdata[2] = { address, data };
+ return acpi_ec_transaction(ec, ACPI_EC_COMMAND_WRITE,
wdata, 2, NULL, 0);
}
/*
* Externally callable EC access functions. For now, assume 1 EC only
*/
-int ec_read(u8 addr, u8 *val)
+int ec_read(u8 addr, u8 * val)
{
struct acpi_ec *ec;
int err;
@@ -370,8 +368,8 @@ int ec_write(u8 addr, u8 val)
EXPORT_SYMBOL(ec_write);
int ec_transaction(u8 command,
- const u8 *wdata, unsigned wdata_len,
- u8 *rdata, unsigned rdata_len)
+ const u8 * wdata, unsigned wdata_len,
+ u8 * rdata, unsigned rdata_len)
{
struct acpi_ec *ec;
@@ -386,65 +384,49 @@ int ec_transaction(u8 command,
EXPORT_SYMBOL(ec_transaction);
-static int acpi_ec_query(struct acpi_ec *ec, u8 *data)
+static int acpi_ec_query(struct acpi_ec *ec, u8 * data)
{
int result;
- u8 d;
+ u8 d;
- if (!ec || !data)
- return -EINVAL;
+ if (!ec || !data)
+ return -EINVAL;
- /*
- * 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).
- */
+ /*
+ * 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).
+ */
- result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_QUERY, NULL, 0, &d, 1);
- if (result)
- return result;
+ result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_QUERY, NULL, 0, &d, 1);
+ if (result)
+ return result;
- if (!d)
- return -ENODATA;
+ if (!d)
+ return -ENODATA;
- *data = d;
- return 0;
+ *data = d;
+ return 0;
}
/* --------------------------------------------------------------------------
Event Management
-------------------------------------------------------------------------- */
-struct acpi_ec_query_data {
- acpi_handle handle;
- u8 data;
-};
-
static void acpi_ec_gpe_query(void *ec_cxt)
{
struct acpi_ec *ec = (struct acpi_ec *)ec_cxt;
u8 value = 0;
- static char object_name[8];
+ char object_name[8];
- if (!ec)
- goto end;
-
- value = acpi_ec_read_status(ec);
-
- if (!(value & ACPI_EC_FLAG_SCI))
- goto end;
-
- if (acpi_ec_query(ec, &value))
- goto end;
+ if (!ec || acpi_ec_query(ec, &value))
+ return;
snprintf(object_name, 8, "_Q%2.2X", value);
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s", object_name));
+ printk(KERN_INFO PREFIX "evaluating %s\n", object_name);
acpi_evaluate_object(ec->handle, object_name, NULL, NULL);
-
- end:
- acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR);
}
static u32 acpi_ec_gpe_handler(void *data)
@@ -453,22 +435,18 @@ static u32 acpi_ec_gpe_handler(void *data)
u8 value;
struct acpi_ec *ec = (struct acpi_ec *)data;
- acpi_clear_gpe(NULL, ec->gpe_bit, ACPI_ISR);
- value = acpi_ec_read_status(ec);
-
if (acpi_ec_mode == EC_INTR) {
- if (acpi_ec_check_status(value, ec->expect_event)) {
- ec->expect_event = 0;
- wake_up(&ec->wait);
- }
+ wake_up(&ec->wait);
}
- if (value & ACPI_EC_FLAG_SCI) {
- status = acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, ec);
- return status == AE_OK ?
- ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
+ value = acpi_ec_read_status(ec);
+ if ((value & ACPI_EC_FLAG_SCI) && !atomic_read(&ec->query_pending)) {
+ atomic_set(&ec->query_pending, 1);
+ status =
+ acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query,
+ ec);
}
- acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_ISR);
+
return status == AE_OK ?
ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
}
@@ -504,7 +482,6 @@ acpi_ec_space_handler(u32 function,
acpi_integer f_v = 0;
int i = 0;
-
if ((address > 0xFF) || !value || !handler_context)
return AE_BAD_PARAMETER;
@@ -518,7 +495,7 @@ acpi_ec_space_handler(u32 function,
switch (function) {
case ACPI_READ:
temp = 0;
- result = acpi_ec_read(ec, (u8) address, (u8 *) &temp);
+ result = acpi_ec_read(ec, (u8) address, (u8 *) & temp);
break;
case ACPI_WRITE:
result = acpi_ec_write(ec, (u8) address, (u8) temp);
@@ -571,18 +548,15 @@ static int acpi_ec_read_info(struct seq_file *seq, void *offset)
{
struct acpi_ec *ec = (struct acpi_ec *)seq->private;
-
if (!ec)
goto end;
- seq_printf(seq, "gpe bit: 0x%02x\n",
- (u32) ec->gpe_bit);
+ seq_printf(seq, "gpe: 0x%02x\n", (u32) ec->gpe);
seq_printf(seq, "ports: 0x%02x, 0x%02x\n",
- (u32) ec->command_addr,
- (u32) ec->data_addr);
+ (u32) ec->command_addr, (u32) ec->data_addr);
seq_printf(seq, "use global lock: %s\n",
ec->global_lock ? "yes" : "no");
- acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR);
+ acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
end:
return 0;
@@ -605,7 +579,6 @@ static int acpi_ec_add_fs(struct acpi_device *device)
{
struct proc_dir_entry *entry = NULL;
-
if (!acpi_device_dir(device)) {
acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
acpi_ec_dir);
@@ -648,7 +621,6 @@ static int acpi_ec_add(struct acpi_device *device)
acpi_status status = AE_OK;
struct acpi_ec *ec = NULL;
-
if (!device)
return -EINVAL;
@@ -659,7 +631,8 @@ static int acpi_ec_add(struct acpi_device *device)
ec->handle = device->handle;
ec->uid = -1;
- init_MUTEX(&ec->sem);
+ mutex_init(&ec->lock);
+ atomic_set(&ec->query_pending, 0);
if (acpi_ec_mode == EC_INTR) {
atomic_set(&ec->leaving_burst, 1);
init_waitqueue_head(&ec->wait);
@@ -669,8 +642,7 @@ static int acpi_ec_add(struct acpi_device *device)
acpi_driver_data(device) = ec;
/* Use the global lock for all EC transactions? */
- acpi_evaluate_integer(ec->handle, "_GLK", NULL,
- &ec->global_lock);
+ acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock);
/* XXX we don't test uids, because on some boxes ecdt uid = 0, see:
http://bugzilla.kernel.org/show_bug.cgi?id=6111 */
@@ -679,7 +651,7 @@ static int acpi_ec_add(struct acpi_device *device)
ACPI_ADR_SPACE_EC,
&acpi_ec_space_handler);
- acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit,
+ acpi_remove_gpe_handler(NULL, ec_ecdt->gpe,
&acpi_ec_gpe_handler);
kfree(ec_ecdt);
@@ -687,11 +659,10 @@ static int acpi_ec_add(struct acpi_device *device)
/* Get GPE bit assignment (EC events). */
/* TODO: Add support for _GPE returning a package */
- status =
- acpi_evaluate_integer(ec->handle, "_GPE", NULL,
- &ec->gpe_bit);
+ status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe);
if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status, "Obtaining GPE bit assignment"));
+ ACPI_EXCEPTION((AE_INFO, status,
+ "Obtaining GPE bit assignment"));
result = -ENODEV;
goto end;
}
@@ -701,13 +672,13 @@ static int acpi_ec_add(struct acpi_device *device)
goto end;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.",
- acpi_device_name(device), acpi_device_bid(device),
- (u32) ec->gpe_bit));
+ acpi_device_name(device), acpi_device_bid(device),
+ (u32) ec->gpe));
if (!first_ec)
first_ec = device;
- end:
+ end:
if (result)
kfree(ec);
@@ -718,7 +689,6 @@ static int acpi_ec_remove(struct acpi_device *device, int type)
{
struct acpi_ec *ec = NULL;
-
if (!device)
return -EINVAL;
@@ -761,7 +731,6 @@ static int acpi_ec_start(struct acpi_device *device)
acpi_status status = AE_OK;
struct acpi_ec *ec = NULL;
-
if (!device)
return -EINVAL;
@@ -782,27 +751,26 @@ static int acpi_ec_start(struct acpi_device *device)
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx",
- ec->gpe_bit, ec->command_addr, ec->data_addr));
+ ec->gpe, ec->command_addr, ec->data_addr));
/*
* Install GPE handler
*/
- status = acpi_install_gpe_handler(NULL, ec->gpe_bit,
+ status = acpi_install_gpe_handler(NULL, ec->gpe,
ACPI_GPE_EDGE_TRIGGERED,
&acpi_ec_gpe_handler, ec);
if (ACPI_FAILURE(status)) {
return -ENODEV;
}
- acpi_set_gpe_type(NULL, ec->gpe_bit, ACPI_GPE_TYPE_RUNTIME);
- acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR);
+ acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME);
+ acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
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_gpe_handler(NULL, ec->gpe_bit,
- &acpi_ec_gpe_handler);
+ acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler);
return -ENODEV;
}
@@ -814,7 +782,6 @@ static int acpi_ec_stop(struct acpi_device *device, int type)
acpi_status status = AE_OK;
struct acpi_ec *ec = NULL;
-
if (!device)
return -EINVAL;
@@ -826,9 +793,7 @@ static int acpi_ec_stop(struct acpi_device *device, int type)
if (ACPI_FAILURE(status))
return -ENODEV;
- status =
- acpi_remove_gpe_handler(NULL, ec->gpe_bit,
- &acpi_ec_gpe_handler);
+ status = acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler);
if (ACPI_FAILURE(status))
return -ENODEV;
@@ -841,7 +806,7 @@ acpi_fake_ecdt_callback(acpi_handle handle,
{
acpi_status status;
- init_MUTEX(&ec_ecdt->sem);
+ mutex_init(&ec_ecdt->lock);
if (acpi_ec_mode == EC_INTR) {
init_waitqueue_head(&ec_ecdt->wait);
}
@@ -853,16 +818,15 @@ acpi_fake_ecdt_callback(acpi_handle handle,
ec_ecdt->uid = -1;
acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid);
- status =
- acpi_evaluate_integer(handle, "_GPE", NULL,
- &ec_ecdt->gpe_bit);
+ status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe);
if (ACPI_FAILURE(status))
return status;
ec_ecdt->global_lock = TRUE;
ec_ecdt->handle = handle;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx",
- ec_ecdt->gpe_bit, ec_ecdt->command_addr, ec_ecdt->data_addr));
+ ec_ecdt->gpe, ec_ecdt->command_addr,
+ ec_ecdt->data_addr));
return AE_CTRL_TERMINATE;
}
@@ -901,7 +865,7 @@ static int __init acpi_ec_fake_ecdt(void)
goto error;
}
return 0;
- error:
+ error:
return ret;
}
@@ -926,25 +890,24 @@ static int __init acpi_ec_get_real_ecdt(void)
return -ENOMEM;
memset(ec_ecdt, 0, sizeof(struct acpi_ec));
- init_MUTEX(&ec_ecdt->sem);
+ mutex_init(&ec_ecdt->lock);
if (acpi_ec_mode == EC_INTR) {
init_waitqueue_head(&ec_ecdt->wait);
}
ec_ecdt->command_addr = ecdt_ptr->ec_control.address;
ec_ecdt->data_addr = ecdt_ptr->ec_data.address;
- ec_ecdt->gpe_bit = ecdt_ptr->gpe_bit;
+ ec_ecdt->gpe = ecdt_ptr->gpe_bit;
/* use the GL just to be safe */
ec_ecdt->global_lock = TRUE;
ec_ecdt->uid = ecdt_ptr->uid;
- status =
- acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->handle);
+ status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->handle);
if (ACPI_FAILURE(status)) {
goto error;
}
return 0;
- error:
+ error:
ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT"));
kfree(ec_ecdt);
ec_ecdt = NULL;
@@ -970,14 +933,14 @@ int __init acpi_ec_ecdt_probe(void)
/*
* Install GPE handler
*/
- status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe_bit,
+ status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe,
ACPI_GPE_EDGE_TRIGGERED,
&acpi_ec_gpe_handler, ec_ecdt);
if (ACPI_FAILURE(status)) {
goto error;
}
- acpi_set_gpe_type(NULL, ec_ecdt->gpe_bit, ACPI_GPE_TYPE_RUNTIME);
- acpi_enable_gpe(NULL, ec_ecdt->gpe_bit, ACPI_NOT_ISR);
+ acpi_set_gpe_type(NULL, ec_ecdt->gpe, ACPI_GPE_TYPE_RUNTIME);
+ acpi_enable_gpe(NULL, ec_ecdt->gpe, ACPI_NOT_ISR);
status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_EC,
@@ -985,7 +948,7 @@ int __init acpi_ec_ecdt_probe(void)
&acpi_ec_space_setup,
ec_ecdt);
if (ACPI_FAILURE(status)) {
- acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit,
+ acpi_remove_gpe_handler(NULL, ec_ecdt->gpe,
&acpi_ec_gpe_handler);
goto error;
}
@@ -1004,7 +967,6 @@ static int __init acpi_ec_init(void)
{
int result = 0;
-
if (acpi_disabled)
return 0;
@@ -1057,7 +1019,8 @@ static int __init acpi_ec_set_intr_mode(char *str)
acpi_ec_mode = EC_POLL;
}
acpi_ec_driver.ops.add = acpi_ec_add;
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "EC %s mode.\n", intr ? "interrupt" : "polling"));
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "EC %s mode.\n",
+ intr ? "interrupt" : "polling"));
return 1;
}
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index ba8fe83c629..4ac2f18e0da 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -271,9 +271,9 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle)
{
acpi_status status;
- if (dev->firmware_data) {
+ if (dev->archdata.acpi_handle) {
printk(KERN_WARNING PREFIX
- "Drivers changed 'firmware_data' for %s\n", dev->bus_id);
+ "Drivers changed 'acpi_handle' for %s\n", dev->bus_id);
return -EINVAL;
}
get_device(dev);
@@ -282,25 +282,26 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle)
put_device(dev);
return -EINVAL;
}
- dev->firmware_data = handle;
+ dev->archdata.acpi_handle = handle;
return 0;
}
static int acpi_unbind_one(struct device *dev)
{
- if (!dev->firmware_data)
+ if (!dev->archdata.acpi_handle)
return 0;
- if (dev == acpi_get_physical_device(dev->firmware_data)) {
+ if (dev == acpi_get_physical_device(dev->archdata.acpi_handle)) {
/* acpi_get_physical_device increase refcnt by one */
put_device(dev);
- acpi_detach_data(dev->firmware_data, acpi_glue_data_handler);
- dev->firmware_data = NULL;
+ acpi_detach_data(dev->archdata.acpi_handle,
+ acpi_glue_data_handler);
+ dev->archdata.acpi_handle = NULL;
/* acpi_bind_one increase refcnt by one */
put_device(dev);
} else {
printk(KERN_ERR PREFIX
- "Oops, 'firmware_data' corrupt for %s\n", dev->bus_id);
+ "Oops, 'acpi_handle' corrupt for %s\n", dev->bus_id);
}
return 0;
}
@@ -332,7 +333,8 @@ static int acpi_platform_notify(struct device *dev)
if (!ret) {
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- acpi_get_name(dev->firmware_data, ACPI_FULL_PATHNAME, &buffer);
+ acpi_get_name(dev->archdata.acpi_handle,
+ ACPI_FULL_PATHNAME, &buffer);
DBG("Device %s -> %s\n", dev->bus_id, (char *)buffer.pointer);
kfree(buffer.pointer);
} else
diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c
index 003a9876c96..ab180073534 100644
--- a/drivers/acpi/ibm_acpi.c
+++ b/drivers/acpi/ibm_acpi.c
@@ -3,6 +3,7 @@
*
*
* Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
+ * Copyright (C) 2006 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
*
* 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
@@ -19,10 +20,14 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#define IBM_VERSION "0.12a"
+#define IBM_VERSION "0.13"
/*
* Changelog:
+ *
+ * 2006-11-22 0.13 new maintainer
+ * changelog now lives in git commit history, and will
+ * not be updated further in-file.
*
* 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels
* 2005-03-17 0.11 support for 600e, 770x
@@ -77,9 +82,16 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
+#include <linux/string.h>
+
#include <linux/proc_fs.h>
+#include <linux/backlight.h>
#include <asm/uaccess.h>
+#include <linux/dmi.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+
#include <acpi/acpi_drivers.h>
#include <acpi/acnamesp.h>
@@ -88,7 +100,7 @@
#define IBM_FILE "ibm_acpi"
#define IBM_URL "http://ibm-acpi.sf.net/"
-MODULE_AUTHOR("Borislav Deianov");
+MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");
MODULE_DESCRIPTION(IBM_DESC);
MODULE_VERSION(IBM_VERSION);
MODULE_LICENSE("GPL");
@@ -116,28 +128,6 @@ static acpi_handle root_handle = NULL;
static char *object##_path; \
static char *object##_paths[] = { paths }
-/*
- * The following models are supported to various degrees:
- *
- * 570, 600e, 600x, 770e, 770x
- * A20m, A21e, A21m, A21p, A22p, A30, A30p, A31, A31p
- * G40, G41
- * R30, R31, R32, R40, R40e, R50, R50e, R50p, R51
- * T20, T21, T22, T23, T30, T40, T40p, T41, T41p, T42, T42p, T43
- * X20, X21, X22, X23, X24, X30, X31, X40
- *
- * The following models have no supported features:
- *
- * 240, 240x, i1400
- *
- * Still missing DSDTs for the following models:
- *
- * A20p, A22e, A22m
- * R52
- * S31
- * T43p
- */
-
IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */
"\\_SB.PCI.ISA.EC", /* 570 */
"\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */
@@ -167,8 +157,10 @@ IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */
"\\_SB.PCI.ISA.SLCE", /* 570 */
); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
#endif
+#ifdef CONFIG_ACPI_IBM_BAY
IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */
"\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */
+ "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */
"\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */
); /* A21e, R30, R31 */
@@ -183,6 +175,7 @@ IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */
"_EJ0", /* 770x */
); /* all others */
+#endif
/* don't list other alternatives as we install a notify handler on the 570 */
IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */
@@ -203,7 +196,7 @@ IBM_HANDLE(led, ec, "SLED", /* 570 */
IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */
IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */
IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */
-IBM_HANDLE(fans, ec, "FANS"); /* X31, X40 */
+IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */
IBM_HANDLE(gfan, ec, "GFAN", /* 570 */
"\\FSPD", /* 600e/x, 770e, 770x */
@@ -216,6 +209,152 @@ IBM_HANDLE(sfan, ec, "SFAN", /* 570 */
#define IBM_HKEY_HID "IBM0068"
#define IBM_PCI_HID "PNP0A03"
+enum thermal_access_mode {
+ IBMACPI_THERMAL_NONE = 0, /* No thermal support */
+ IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */
+ IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */
+ IBMACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */
+ IBMACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */
+};
+
+#define IBMACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */
+struct ibm_thermal_sensors_struct {
+ s32 temp[IBMACPI_MAX_THERMAL_SENSORS];
+};
+
+/*
+ * FAN ACCESS MODES
+ *
+ * IBMACPI_FAN_RD_ACPI_GFAN:
+ * ACPI GFAN method: returns fan level
+ *
+ * see IBMACPI_FAN_WR_ACPI_SFAN
+ * EC 0x2f not available if GFAN exists
+ *
+ * IBMACPI_FAN_WR_ACPI_SFAN:
+ * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)
+ *
+ * EC 0x2f might be available *for reading*, but never for writing.
+ *
+ * IBMACPI_FAN_WR_TPEC:
+ * ThinkPad EC register 0x2f (HFSP): fan control loop mode Supported
+ * on almost all ThinkPads
+ *
+ * Fan speed changes of any sort (including those caused by the
+ * disengaged mode) are usually done slowly by the firmware as the
+ * maximum ammount of fan duty cycle change per second seems to be
+ * limited.
+ *
+ * Reading is not available if GFAN exists.
+ * Writing is not available if SFAN exists.
+ *
+ * Bits
+ * 7 automatic mode engaged;
+ * (default operation mode of the ThinkPad)
+ * fan level is ignored in this mode.
+ * 6 disengage mode (takes precedence over bit 7);
+ * not available on all thinkpads. May disable
+ * the tachometer, and speeds up fan to 100% duty-cycle,
+ * which speeds it up far above the standard RPM
+ * levels. It is not impossible that it could cause
+ * hardware damage.
+ * 5-3 unused in some models. Extra bits for fan level
+ * in others, but still useless as all values above
+ * 7 map to the same speed as level 7 in these models.
+ * 2-0 fan level (0..7 usually)
+ * 0x00 = stop
+ * 0x07 = max (set when temperatures critical)
+ * Some ThinkPads may have other levels, see
+ * IBMACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
+ *
+ * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
+ * boot. Apparently the EC does not intialize it, so unless ACPI DSDT
+ * does so, its initial value is meaningless (0x07).
+ *
+ * For firmware bugs, refer to:
+ * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
+ *
+ * ----
+ *
+ * ThinkPad EC register 0x84 (LSB), 0x85 (MSB):
+ * Main fan tachometer reading (in RPM)
+ *
+ * This register is present on all ThinkPads with a new-style EC, and
+ * it is known not to be present on the A21m/e, and T22, as there is
+ * something else in offset 0x84 according to the ACPI DSDT. Other
+ * ThinkPads from this same time period (and earlier) probably lack the
+ * tachometer as well.
+ *
+ * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare
+ * was never fixed by IBM to report the EC firmware version string
+ * probably support the tachometer (like the early X models), so
+ * detecting it is quite hard. We need more data to know for sure.
+ *
+ * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings
+ * might result.
+ *
+ * FIRMWARE BUG: when EC 0x2f bit 6 is set (disengaged mode), this
+ * register is not invalidated in ThinkPads that disable tachometer
+ * readings. Thus, the tachometer readings go stale.
+ *
+ * For firmware bugs, refer to:
+ * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
+ *
+ * IBMACPI_FAN_WR_ACPI_FANS:
+ * ThinkPad X31, X40, X41. Not available in the X60.
+ *
+ * FANS ACPI handle: takes three arguments: low speed, medium speed,
+ * high speed. ACPI DSDT seems to map these three speeds to levels
+ * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH
+ * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")
+ *
+ * The speeds are stored on handles
+ * (FANA:FAN9), (FANC:FANB), (FANE:FAND).
+ *
+ * There are three default speed sets, acessible as handles:
+ * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
+ *
+ * ACPI DSDT switches which set is in use depending on various
+ * factors.
+ *
+ * IBMACPI_FAN_WR_TPEC is also available and should be used to
+ * command the fan. The X31/X40/X41 seems to have 8 fan levels,
+ * but the ACPI tables just mention level 7.
+ */
+
+enum fan_status_access_mode {
+ IBMACPI_FAN_NONE = 0, /* No fan status or control */
+ IBMACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */
+ IBMACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */
+};
+
+enum fan_control_access_mode {
+ IBMACPI_FAN_WR_NONE = 0, /* No fan control */
+ IBMACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */
+ IBMACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */
+ IBMACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */
+};
+
+enum fan_control_commands {
+ IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */
+ IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */
+ IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd,
+ * and also watchdog cmd */
+};
+
+enum { /* Fan control constants */
+ fan_status_offset = 0x2f, /* EC register 0x2f */
+ fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM)
+ * 0x84 must be read before 0x85 */
+
+ IBMACPI_FAN_EC_DISENGAGED = 0x40, /* EC mode: tachometer
+ * disengaged */
+ IBMACPI_FAN_EC_AUTO = 0x80, /* EC mode: auto fan
+ * control */
+};
+
+static char *ibm_thinkpad_ec_found = NULL;
+
struct ibm_struct {
char *name;
char param[32];
@@ -243,6 +382,8 @@ struct ibm_struct {
static struct proc_dir_entry *proc_dir = NULL;
+static struct backlight_device *ibm_backlight_device = NULL;
+
#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
#define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
@@ -581,8 +722,7 @@ static int wan_status(void)
{
int status;
- if (!wan_supported ||
- !acpi_evalf(hkey_handle, &status, "GWAN", "d"))
+ if (!wan_supported || !acpi_evalf(hkey_handle, &status, "GWAN", "d"))
status = 0;
return status;
@@ -630,12 +770,15 @@ static int wan_write(char *buf)
return 0;
}
-static int video_supported;
-static int video_orig_autosw;
+enum video_access_mode {
+ IBMACPI_VIDEO_NONE = 0,
+ IBMACPI_VIDEO_570, /* 570 */
+ IBMACPI_VIDEO_770, /* 600e/x, 770e, 770x */
+ IBMACPI_VIDEO_NEW, /* all others */
+};
-#define VIDEO_570 1
-#define VIDEO_770 2
-#define VIDEO_NEW 3
+static enum video_access_mode video_supported;
+static int video_orig_autosw;
static int video_init(void)
{
@@ -647,16 +790,16 @@ static int video_init(void)
if (!vid_handle)
/* video switching not supported on R30, R31 */
- video_supported = 0;
+ video_supported = IBMACPI_VIDEO_NONE;
else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
/* 570 */
- video_supported = VIDEO_570;
+ video_supported = IBMACPI_VIDEO_570;
else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
/* 600e/x, 770e, 770x */
- video_supported = VIDEO_770;
+ video_supported = IBMACPI_VIDEO_770;
else
/* all others */
- video_supported = VIDEO_NEW;
+ video_supported = IBMACPI_VIDEO_NEW;
return 0;
}
@@ -666,15 +809,15 @@ static int video_status(void)
int status = 0;
int i;
- if (video_supported == VIDEO_570) {
+ if (video_supported == IBMACPI_VIDEO_570) {
if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87))
status = i & 3;
- } else if (video_supported == VIDEO_770) {
+ } else if (video_supported == IBMACPI_VIDEO_770) {
if (acpi_evalf(NULL, &i, "\\VCDL", "d"))
status |= 0x01 * i;
if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
status |= 0x02 * i;
- } else if (video_supported == VIDEO_NEW) {
+ } else if (video_supported == IBMACPI_VIDEO_NEW) {
acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1);
if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
status |= 0x02 * i;
@@ -693,9 +836,10 @@ static int video_autosw(void)
{
int autosw = 0;
- if (video_supported == VIDEO_570)
+ if (video_supported == IBMACPI_VIDEO_570)
acpi_evalf(vid_handle, &autosw, "SWIT", "d");
- else if (video_supported == VIDEO_770 || video_supported == VIDEO_NEW)
+ else if (video_supported == IBMACPI_VIDEO_770 ||
+ video_supported == IBMACPI_VIDEO_NEW)
acpi_evalf(vid_handle, &autosw, "^VDEE", "d");
return autosw & 1;
@@ -715,12 +859,12 @@ static int video_read(char *p)
len += sprintf(p + len, "status:\t\tsupported\n");
len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
- if (video_supported == VIDEO_NEW)
+ if (video_supported == IBMACPI_VIDEO_NEW)
len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
- if (video_supported == VIDEO_NEW)
+ if (video_supported == IBMACPI_VIDEO_NEW)
len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
@@ -735,7 +879,7 @@ static int video_switch(void)
if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
return -EIO;
- ret = video_supported == VIDEO_570 ?
+ ret = video_supported == IBMACPI_VIDEO_570 ?
acpi_evalf(ec_handle, NULL, "_Q16", "v") :
acpi_evalf(vid_handle, NULL, "VSWT", "v");
acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw);
@@ -745,9 +889,9 @@ static int video_switch(void)
static int video_expand(void)
{
- if (video_supported == VIDEO_570)
+ if (video_supported == IBMACPI_VIDEO_570)
return acpi_evalf(ec_handle, NULL, "_Q17", "v");
- else if (video_supported == VIDEO_770)
+ else if (video_supported == IBMACPI_VIDEO_770)
return acpi_evalf(vid_handle, NULL, "VEXP", "v");
else
return acpi_evalf(NULL, NULL, "\\VEXP", "v");
@@ -757,10 +901,10 @@ static int video_switch2(int status)
{
int ret;
- if (video_supported == VIDEO_570) {
+ if (video_supported == IBMACPI_VIDEO_570) {
ret = acpi_evalf(NULL, NULL,
"\\_SB.PHS2", "vdd", 0x8b, status | 0x80);
- } else if (video_supported == VIDEO_770) {
+ } else if (video_supported == IBMACPI_VIDEO_770) {
int autosw = video_autosw();
if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
return -EIO;
@@ -796,10 +940,10 @@ static int video_write(char *buf)
enable |= 0x02;
} else if (strlencmp(cmd, "crt_disable") == 0) {
disable |= 0x02;
- } else if (video_supported == VIDEO_NEW &&
+ } else if (video_supported == IBMACPI_VIDEO_NEW &&
strlencmp(cmd, "dvi_enable") == 0) {
enable |= 0x08;
- } else if (video_supported == VIDEO_NEW &&
+ } else if (video_supported == IBMACPI_VIDEO_NEW &&
strlencmp(cmd, "dvi_disable") == 0) {
disable |= 0x08;
} else if (strlencmp(cmd, "auto_enable") == 0) {
@@ -898,6 +1042,7 @@ static int light_write(char *buf)
return 0;
}
+#if defined(CONFIG_ACPI_IBM_DOCK) || defined(CONFIG_ACPI_IBM_BAY)
static int _sta(acpi_handle handle)
{
int status;
@@ -907,6 +1052,7 @@ static int _sta(acpi_handle handle)
return status;
}
+#endif
#ifdef CONFIG_ACPI_IBM_DOCK
#define dock_docked() (_sta(dock_handle) & 1)
@@ -972,6 +1118,7 @@ static void dock_notify(struct ibm_struct *ibm, u32 event)
}
#endif
+#ifdef CONFIG_ACPI_IBM_BAY
static int bay_status_supported;
static int bay_status2_supported;
static int bay_eject_supported;
@@ -1047,6 +1194,7 @@ static void bay_notify(struct ibm_struct *ibm, u32 event)
{
acpi_bus_generate_event(ibm->device, event, 0);
}
+#endif
static int cmos_read(char *p)
{
@@ -1094,26 +1242,28 @@ static int cmos_write(char *buf)
return 0;
}
-static int led_supported;
-
-#define LED_570 1
-#define LED_OLD 2
-#define LED_NEW 3
+enum led_access_mode {
+ IBMACPI_LED_NONE = 0,
+ IBMACPI_LED_570, /* 570 */
+ IBMACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
+ IBMACPI_LED_NEW, /* all others */
+};
+static enum led_access_mode led_supported;
static int led_init(void)
{
if (!led_handle)
/* led not supported on R30, R31 */
- led_supported = 0;
+ led_supported = IBMACPI_LED_NONE;
else if (strlencmp(led_path, "SLED") == 0)
/* 570 */
- led_supported = LED_570;
+ led_supported = IBMACPI_LED_570;
else if (strlencmp(led_path, "SYSL") == 0)
/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
- led_supported = LED_OLD;
+ led_supported = IBMACPI_LED_OLD;
else
/* all others */
- led_supported = LED_NEW;
+ led_supported = IBMACPI_LED_NEW;
return 0;
}
@@ -1130,7 +1280,7 @@ static int led_read(char *p)
}
len += sprintf(p + len, "status:\t\tsupported\n");
- if (led_supported == LED_570) {
+ if (led_supported == IBMACPI_LED_570) {
/* 570 */
int i, status;
for (i = 0; i < 8; i++) {
@@ -1179,13 +1329,13 @@ static int led_write(char *buf)
} else
return -EINVAL;
- if (led_supported == LED_570) {
+ if (led_supported == IBMACPI_LED_570) {
/* 570 */
led = 1 << led;
if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
led, led_sled_arg1[ind]))
return -EIO;
- } else if (led_supported == LED_OLD) {
+ } else if (led_supported == IBMACPI_LED_OLD) {
/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
led = 1 << led;
ret = ec_write(EC_HLMS, led);
@@ -1272,50 +1422,142 @@ static int acpi_ec_write(int i, u8 v)
return 1;
}
-static int thermal_tmp_supported;
-static int thermal_updt_supported;
+static enum thermal_access_mode thermal_read_mode;
static int thermal_init(void)
{
- /* temperatures not supported on 570, G4x, R30, R31, R32 */
- thermal_tmp_supported = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
+ u8 t, ta1, ta2;
+ int i;
+ int acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
+
+ if (ibm_thinkpad_ec_found && experimental) {
+ /*
+ * Direct EC access mode: sensors at registers
+ * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for
+ * non-implemented, thermal sensors return 0x80 when
+ * not available
+ */
- /* 600e/x, 770e, 770x */
- thermal_updt_supported = acpi_evalf(ec_handle, NULL, "UPDT", "qv");
+ ta1 = ta2 = 0;
+ for (i = 0; i < 8; i++) {
+ if (likely(acpi_ec_read(0x78 + i, &t))) {
+ ta1 |= t;
+ } else {
+ ta1 = 0;
+ break;
+ }
+ if (likely(acpi_ec_read(0xC0 + i, &t))) {
+ ta2 |= t;
+ } else {
+ ta1 = 0;
+ break;
+ }
+ }
+ if (ta1 == 0) {
+ /* This is sheer paranoia, but we handle it anyway */
+ if (acpi_tmp7) {
+ printk(IBM_ERR
+ "ThinkPad ACPI EC access misbehaving, "
+ "falling back to ACPI TMPx access mode\n");
+ thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07;
+ } else {
+ printk(IBM_ERR
+ "ThinkPad ACPI EC access misbehaving, "
+ "disabling thermal sensors access\n");
+ thermal_read_mode = IBMACPI_THERMAL_NONE;
+ }
+ } else {
+ thermal_read_mode =
+ (ta2 != 0) ?
+ IBMACPI_THERMAL_TPEC_16 : IBMACPI_THERMAL_TPEC_8;
+ }
+ } else if (acpi_tmp7) {
+ if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
+ /* 600e/x, 770e, 770x */
+ thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT;
+ } else {
+ /* Standard ACPI TMPx access, max 8 sensors */
+ thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07;
+ }
+ } else {
+ /* temperatures not supported on 570, G4x, R30, R31, R32 */
+ thermal_read_mode = IBMACPI_THERMAL_NONE;
+ }
return 0;
}
-static int thermal_read(char *p)
+static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
{
- int len = 0;
+ int i, t;
+ s8 tmp;
+ char tmpi[] = "TMPi";
- if (!thermal_tmp_supported)
- len += sprintf(p + len, "temperatures:\tnot supported\n");
- else {
- int i, t;
- char tmpi[] = "TMPi";
- s8 tmp[8];
+ if (!s)
+ return -EINVAL;
- if (thermal_updt_supported)
- if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
+ switch (thermal_read_mode) {
+#if IBMACPI_MAX_THERMAL_SENSORS >= 16
+ case IBMACPI_THERMAL_TPEC_16:
+ for (i = 0; i < 8; i++) {
+ if (!acpi_ec_read(0xC0 + i, &tmp))
+ return -EIO;
+ s->temp[i + 8] = tmp * 1000;
+ }
+ /* fallthrough */
+#endif
+ case IBMACPI_THERMAL_TPEC_8:
+ for (i = 0; i < 8; i++) {
+ if (!acpi_ec_read(0x78 + i, &tmp))
return -EIO;
+ s->temp[i] = tmp * 1000;
+ }
+ return (thermal_read_mode == IBMACPI_THERMAL_TPEC_16) ? 16 : 8;
+ case IBMACPI_THERMAL_ACPI_UPDT:
+ if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
+ return -EIO;
for (i = 0; i < 8; i++) {
tmpi[3] = '0' + i;
if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
return -EIO;
- if (thermal_updt_supported)
- tmp[i] = (t - 2732 + 5) / 10;
- else
- tmp[i] = t;
+ s->temp[i] = (t - 2732) * 100;
}
+ return 8;
- len += sprintf(p + len,
- "temperatures:\t%d %d %d %d %d %d %d %d\n",
- tmp[0], tmp[1], tmp[2], tmp[3],
- tmp[4], tmp[5], tmp[6], tmp[7]);
+ case IBMACPI_THERMAL_ACPI_TMP07:
+ for (i = 0; i < 8; i++) {
+ tmpi[3] = '0' + i;
+ if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
+ return -EIO;
+ s->temp[i] = t * 1000;
+ }
+ return 8;
+
+ case IBMACPI_THERMAL_NONE:
+ default:
+ return 0;
}
+}
+
+static int thermal_read(char *p)
+{
+ int len = 0;
+ int n, i;
+ struct ibm_thermal_sensors_struct t;
+
+ n = thermal_get_sensors(&t);
+ if (unlikely(n < 0))
+ return n;
+
+ len += sprintf(p + len, "temperatures:\t");
+
+ if (n > 0) {
+ for (i = 0; i < (n - 1); i++)
+ len += sprintf(p + len, "%d ", t.temp[i] / 1000);
+ len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
+ } else
+ len += sprintf(p + len, "not supported\n");
return len;
}
@@ -1381,12 +1623,23 @@ static int ecdump_write(char *buf)
static int brightness_offset = 0x31;
+static int brightness_get(struct backlight_device *bd)
+{
+ u8 level;
+ if (!acpi_ec_read(brightness_offset, &level))
+ return -EIO;
+
+ level &= 0x7;
+
+ return level;
+}
+
static int brightness_read(char *p)
{
int len = 0;
- u8 level;
+ int level;
- if (!acpi_ec_read(brightness_offset, &level)) {
+ if ((level = brightness_get(NULL)) < 0) {
len += sprintf(p + len, "level:\t\tunreadable\n");
} else {
len += sprintf(p + len, "level:\t\t%d\n", level & 0x7);
@@ -1401,16 +1654,34 @@ static int brightness_read(char *p)
#define BRIGHTNESS_UP 4
#define BRIGHTNESS_DOWN 5
-static int brightness_write(char *buf)
+static int brightness_set(int value)
{
int cmos_cmd, inc, i;
- u8 level;
+ int current_value = brightness_get(NULL);
+
+ value &= 7;
+
+ cmos_cmd = value > current_value ? BRIGHTNESS_UP : BRIGHTNESS_DOWN;
+ inc = value > current_value ? 1 : -1;
+ for (i = current_value; i != value; i += inc) {
+ if (!cmos_eval(cmos_cmd))
+ return -EIO;
+ if (!acpi_ec_write(brightness_offset, i + inc))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int brightness_write(char *buf)
+{
+ int level;
int new_level;
char *cmd;
while ((cmd = next_cmd(&buf))) {
- if (!acpi_ec_read(brightness_offset, &level))
- return -EIO;
+ if ((level = brightness_get(NULL)) < 0)
+ return level;
level &= 7;
if (strlencmp(cmd, "up") == 0) {
@@ -1423,19 +1694,44 @@ static int brightness_write(char *buf)
} else
return -EINVAL;
- cmos_cmd = new_level > level ? BRIGHTNESS_UP : BRIGHTNESS_DOWN;
- inc = new_level > level ? 1 : -1;
- for (i = level; i != new_level; i += inc) {
- if (!cmos_eval(cmos_cmd))
- return -EIO;
- if (!acpi_ec_write(brightness_offset, i + inc))
- return -EIO;
- }
+ brightness_set(new_level);
}
return 0;
}
+static int brightness_update_status(struct backlight_device *bd)
+{
+ return brightness_set(bd->props->brightness);
+}
+
+static struct backlight_properties ibm_backlight_data = {
+ .owner = THIS_MODULE,
+ .get_brightness = brightness_get,
+ .update_status = brightness_update_status,
+ .max_brightness = 7,
+};
+
+static int brightness_init(void)
+{
+ ibm_backlight_device = backlight_device_register("ibm", NULL,
+ &ibm_backlight_data);
+ if (IS_ERR(ibm_backlight_device)) {
+ printk(IBM_ERR "Could not register backlight device\n");
+ return PTR_ERR(ibm_backlight_device);
+ }
+
+ return 0;
+}
+
+static void brightness_exit(void)
+{
+ if (ibm_backlight_device) {
+ backlight_device_unregister(ibm_backlight_device);
+ ibm_backlight_device = NULL;
+ }
+}
+
static int volume_offset = 0x30;
static int volume_read(char *p)
@@ -1522,86 +1818,482 @@ static int volume_write(char *buf)
return 0;
}
-static int fan_status_offset = 0x2f;
-static int fan_rpm_offset = 0x84;
+static enum fan_status_access_mode fan_status_access_mode;
+static enum fan_control_access_mode fan_control_access_mode;
+static enum fan_control_commands fan_control_commands;
-static int fan_read(char *p)
+static int fan_control_status_known;
+static u8 fan_control_initial_status;
+
+static void fan_watchdog_fire(struct work_struct *ignored);
+static int fan_watchdog_maxinterval;
+static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire);
+
+static int fan_init(void)
{
- int len = 0;
- int s;
- u8 lo, hi, status;
+ fan_status_access_mode = IBMACPI_FAN_NONE;
+ fan_control_access_mode = IBMACPI_FAN_WR_NONE;
+ fan_control_commands = 0;
+ fan_control_status_known = 1;
+ fan_watchdog_maxinterval = 0;
if (gfan_handle) {
/* 570, 600e/x, 770e, 770x */
- if (!acpi_evalf(gfan_handle, &s, NULL, "d"))
- return -EIO;
+ fan_status_access_mode = IBMACPI_FAN_RD_ACPI_GFAN;
+ } else {
+ /* all other ThinkPads: note that even old-style
+ * ThinkPad ECs supports the fan control register */
+ if (likely(acpi_ec_read(fan_status_offset,
+ &fan_control_initial_status))) {
+ fan_status_access_mode = IBMACPI_FAN_RD_TPEC;
+
+ /* In some ThinkPads, neither the EC nor the ACPI
+ * DSDT initialize the fan status, and it ends up
+ * being set to 0x07 when it *could* be either
+ * 0x07 or 0x80.
+ *
+ * Enable for TP-1Y (T43), TP-78 (R51e),
+ * TP-76 (R52), TP-70 (T43, R52), which are known
+ * to be buggy. */
+ if (fan_control_initial_status == 0x07 &&
+ ibm_thinkpad_ec_found &&
+ ((ibm_thinkpad_ec_found[0] == '1' &&
+ ibm_thinkpad_ec_found[1] == 'Y') ||
+ (ibm_thinkpad_ec_found[0] == '7' &&
+ (ibm_thinkpad_ec_found[1] == '6' ||
+ ibm_thinkpad_ec_found[1] == '8' ||
+ ibm_thinkpad_ec_found[1] == '0'))
+ )) {
+ printk(IBM_NOTICE
+ "fan_init: initial fan status is "
+ "unknown, assuming it is in auto "
+ "mode\n");
+ fan_control_status_known = 0;
+ }
+ } else {
+ printk(IBM_ERR
+ "ThinkPad ACPI EC access misbehaving, "
+ "fan status and control unavailable\n");
+ return 0;
+ }
+ }
- len += sprintf(p + len, "level:\t\t%d\n", s);
+ if (sfan_handle) {
+ /* 570, 770x-JL */
+ fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN;
+ fan_control_commands |=
+ IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE;
} else {
+ if (!gfan_handle) {
+ /* gfan without sfan means no fan control */
+ /* all other models implement TP EC 0x2f control */
+
+ if (fans_handle) {
+ /* X31, X40, X41 */
+ fan_control_access_mode =
+ IBMACPI_FAN_WR_ACPI_FANS;
+ fan_control_commands |=
+ IBMACPI_FAN_CMD_SPEED |
+ IBMACPI_FAN_CMD_LEVEL |
+ IBMACPI_FAN_CMD_ENABLE;
+ } else {
+ fan_control_access_mode = IBMACPI_FAN_WR_TPEC;
+ fan_control_commands |=
+ IBMACPI_FAN_CMD_LEVEL |
+ IBMACPI_FAN_CMD_ENABLE;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int fan_get_status(u8 *status)
+{
+ u8 s;
+
+ /* TODO:
+ * Add IBMACPI_FAN_RD_ACPI_FANS ? */
+
+ switch (fan_status_access_mode) {
+ case IBMACPI_FAN_RD_ACPI_GFAN:
+ /* 570, 600e/x, 770e, 770x */
+
+ if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
+ return -EIO;
+
+ if (likely(status))
+ *status = s & 0x07;
+
+ break;
+
+ case IBMACPI_FAN_RD_TPEC:
/* all except 570, 600e/x, 770e, 770x */
- if (!acpi_ec_read(fan_status_offset, &status))
- len += sprintf(p + len, "status:\t\tunreadable\n");
- else
- len += sprintf(p + len, "status:\t\t%s\n",
- enabled(status, 7));
+ if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
+ return -EIO;
- if (!acpi_ec_read(fan_rpm_offset, &lo) ||
- !acpi_ec_read(fan_rpm_offset + 1, &hi))
- len += sprintf(p + len, "speed:\t\tunreadable\n");
- else
- len += sprintf(p + len, "speed:\t\t%d\n",
- (hi << 8) + lo);
+ if (likely(status))
+ *status = s;
+
+ break;
+
+ default:
+ return -ENXIO;
}
- if (sfan_handle)
- /* 570, 770x-JL */
- len += sprintf(p + len, "commands:\tlevel <level>"
- " (<level> is 0-7)\n");
- if (!gfan_handle)
+ return 0;
+}
+
+static int fan_get_speed(unsigned int *speed)
+{
+ u8 hi, lo;
+
+ switch (fan_status_access_mode) {
+ case IBMACPI_FAN_RD_TPEC:
/* all except 570, 600e/x, 770e, 770x */
- len += sprintf(p + len, "commands:\tenable, disable\n");
- if (fans_handle)
- /* X31, X40 */
+ if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
+ !acpi_ec_read(fan_rpm_offset + 1, &hi)))
+ return -EIO;
+
+ if (likely(speed))
+ *speed = (hi << 8) | lo;
+
+ break;
+
+ default:
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static void fan_exit(void)
+{
+ cancel_delayed_work(&fan_watchdog_task);
+ flush_scheduled_work();
+}
+
+static void fan_watchdog_reset(void)
+{
+ static int fan_watchdog_active = 0;
+
+ if (fan_watchdog_active)
+ cancel_delayed_work(&fan_watchdog_task);
+
+ if (fan_watchdog_maxinterval > 0) {
+ fan_watchdog_active = 1;
+ if (!schedule_delayed_work(&fan_watchdog_task,
+ msecs_to_jiffies(fan_watchdog_maxinterval
+ * 1000))) {
+ printk(IBM_ERR "failed to schedule the fan watchdog, "
+ "watchdog will not trigger\n");
+ }
+ } else
+ fan_watchdog_active = 0;
+}
+
+static int fan_read(char *p)
+{
+ int len = 0;
+ int rc;
+ u8 status;
+ unsigned int speed = 0;
+
+ switch (fan_status_access_mode) {
+ case IBMACPI_FAN_RD_ACPI_GFAN:
+ /* 570, 600e/x, 770e, 770x */
+ if ((rc = fan_get_status(&status)) < 0)
+ return rc;
+
+ len += sprintf(p + len, "status:\t\t%s\n"
+ "level:\t\t%d\n",
+ (status != 0) ? "enabled" : "disabled", status);
+ break;
+
+ case IBMACPI_FAN_RD_TPEC:
+ /* all except 570, 600e/x, 770e, 770x */
+ if ((rc = fan_get_status(&status)) < 0)
+ return rc;
+
+ if (unlikely(!fan_control_status_known)) {
+ if (status != fan_control_initial_status)
+ fan_control_status_known = 1;
+ else
+ /* Return most likely status. In fact, it
+ * might be the only possible status */
+ status = IBMACPI_FAN_EC_AUTO;
+ }
+
+ len += sprintf(p + len, "status:\t\t%s\n",
+ (status != 0) ? "enabled" : "disabled");
+
+ /* No ThinkPad boots on disengaged mode, we can safely
+ * assume the tachometer is online if fan control status
+ * was unknown */
+ if ((rc = fan_get_speed(&speed)) < 0)
+ return rc;
+
+ len += sprintf(p + len, "speed:\t\t%d\n", speed);
+
+ if (status & IBMACPI_FAN_EC_DISENGAGED)
+ /* Disengaged mode takes precedence */
+ len += sprintf(p + len, "level:\t\tdisengaged\n");
+ else if (status & IBMACPI_FAN_EC_AUTO)
+ len += sprintf(p + len, "level:\t\tauto\n");
+ else
+ len += sprintf(p + len, "level:\t\t%d\n", status);
+ break;
+
+ case IBMACPI_FAN_NONE:
+ default:
+ len += sprintf(p + len, "status:\t\tnot supported\n");
+ }
+
+ if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) {
+ len += sprintf(p + len, "commands:\tlevel <level>");
+
+ switch (fan_control_access_mode) {
+ case IBMACPI_FAN_WR_ACPI_SFAN:
+ len += sprintf(p + len, " (<level> is 0-7)\n");
+ break;
+
+ default:
+ len += sprintf(p + len, " (<level> is 0-7, "
+ "auto, disengaged)\n");
+ break;
+ }
+ }
+
+ if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE)
+ len += sprintf(p + len, "commands:\tenable, disable\n"
+ "commands:\twatchdog <timeout> (<timeout> is 0 (off), "
+ "1-120 (seconds))\n");
+
+ if (fan_control_commands & IBMACPI_FAN_CMD_SPEED)
len += sprintf(p + len, "commands:\tspeed <speed>"
" (<speed> is 0-65535)\n");
return len;
}
-static int fan_write(char *buf)
+static int fan_set_level(int level)
{
- char *cmd;
- int level, speed;
-
- while ((cmd = next_cmd(&buf))) {
- if (sfan_handle &&
- sscanf(cmd, "level %d", &level) == 1 &&
- level >= 0 && level <= 7) {
- /* 570, 770x-JL */
+ switch (fan_control_access_mode) {
+ case IBMACPI_FAN_WR_ACPI_SFAN:
+ if (level >= 0 && level <= 7) {
if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
return -EIO;
- } else if (!gfan_handle && strlencmp(cmd, "enable") == 0) {
- /* all except 570, 600e/x, 770e, 770x */
- if (!acpi_ec_write(fan_status_offset, 0x80))
- return -EIO;
- } else if (!gfan_handle && strlencmp(cmd, "disable") == 0) {
- /* all except 570, 600e/x, 770e, 770x */
- if (!acpi_ec_write(fan_status_offset, 0x00))
- return -EIO;
- } else if (fans_handle &&
- sscanf(cmd, "speed %d", &speed) == 1 &&
- speed >= 0 && speed <= 65535) {
- /* X31, X40 */
+ } else
+ return -EINVAL;
+ break;
+
+ case IBMACPI_FAN_WR_ACPI_FANS:
+ case IBMACPI_FAN_WR_TPEC:
+ if ((level != IBMACPI_FAN_EC_AUTO) &&
+ (level != IBMACPI_FAN_EC_DISENGAGED) &&
+ ((level < 0) || (level > 7)))
+ return -EINVAL;
+
+ if (!acpi_ec_write(fan_status_offset, level))
+ return -EIO;
+ else
+ fan_control_status_known = 1;
+ break;
+
+ default:
+ return -ENXIO;
+ }
+ return 0;
+}
+
+static int fan_set_enable(void)
+{
+ u8 s;
+ int rc;
+
+ switch (fan_control_access_mode) {
+ case IBMACPI_FAN_WR_ACPI_FANS:
+ case IBMACPI_FAN_WR_TPEC:
+ if ((rc = fan_get_status(&s)) < 0)
+ return rc;
+
+ /* Don't go out of emergency fan mode */
+ if (s != 7)
+ s = IBMACPI_FAN_EC_AUTO;
+
+ if (!acpi_ec_write(fan_status_offset, s))
+ return -EIO;
+ else
+ fan_control_status_known = 1;
+ break;
+
+ case IBMACPI_FAN_WR_ACPI_SFAN:
+ if ((rc = fan_get_status(&s)) < 0)
+ return rc;
+
+ s &= 0x07;
+
+ /* Set fan to at least level 4 */
+ if (s < 4)
+ s = 4;
+
+ if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s))
+ return -EIO;
+ break;
+
+ default:
+ return -ENXIO;
+ }
+ return 0;
+}
+
+static int fan_set_disable(void)
+{
+ switch (fan_control_access_mode) {
+ case IBMACPI_FAN_WR_ACPI_FANS:
+ case IBMACPI_FAN_WR_TPEC:
+ if (!acpi_ec_write(fan_status_offset, 0x00))
+ return -EIO;
+ else
+ fan_control_status_known = 1;
+ break;
+
+ case IBMACPI_FAN_WR_ACPI_SFAN:
+ if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00))
+ return -EIO;
+ break;
+
+ default:
+ return -ENXIO;
+ }
+ return 0;
+}
+
+static int fan_set_speed(int speed)
+{
+ switch (fan_control_access_mode) {
+ case IBMACPI_FAN_WR_ACPI_FANS:
+ if (speed >= 0 && speed <= 65535) {
if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",
speed, speed, speed))
return -EIO;
} else
return -EINVAL;
- }
+ break;
+ default:
+ return -ENXIO;
+ }
return 0;
}
+static int fan_write_cmd_level(const char *cmd, int *rc)
+{
+ int level;
+
+ if (strlencmp(cmd, "level auto") == 0)
+ level = IBMACPI_FAN_EC_AUTO;
+ else if (strlencmp(cmd, "level disengaged") == 0)
+ level = IBMACPI_FAN_EC_DISENGAGED;
+ else if (sscanf(cmd, "level %d", &level) != 1)
+ return 0;
+
+ if ((*rc = fan_set_level(level)) == -ENXIO)
+ printk(IBM_ERR "level command accepted for unsupported "
+ "access mode %d", fan_control_access_mode);
+
+ return 1;
+}
+
+static int fan_write_cmd_enable(const char *cmd, int *rc)
+{
+ if (strlencmp(cmd, "enable") != 0)
+ return 0;
+
+ if ((*rc = fan_set_enable()) == -ENXIO)
+ printk(IBM_ERR "enable command accepted for unsupported "
+ "access mode %d", fan_control_access_mode);
+
+ return 1;
+}
+
+static int fan_write_cmd_disable(const char *cmd, int *rc)
+{
+ if (strlencmp(cmd, "disable") != 0)
+ return 0;
+
+ if ((*rc = fan_set_disable()) == -ENXIO)
+ printk(IBM_ERR "disable command accepted for unsupported "
+ "access mode %d", fan_control_access_mode);
+
+ return 1;
+}
+
+static int fan_write_cmd_speed(const char *cmd, int *rc)
+{
+ int speed;
+
+ /* TODO:
+ * Support speed <low> <medium> <high> ? */
+
+ if (sscanf(cmd, "speed %d", &speed) != 1)
+ return 0;
+
+ if ((*rc = fan_set_speed(speed)) == -ENXIO)
+ printk(IBM_ERR "speed command accepted for unsupported "
+ "access mode %d", fan_control_access_mode);
+
+ return 1;
+}
+
+static int fan_write_cmd_watchdog(const char *cmd, int *rc)
+{
+ int interval;
+
+ if (sscanf(cmd, "watchdog %d", &interval) != 1)
+ return 0;
+
+ if (interval < 0 || interval > 120)
+ *rc = -EINVAL;
+ else
+ fan_watchdog_maxinterval = interval;
+
+ return 1;
+}
+
+static int fan_write(char *buf)
+{
+ char *cmd;
+ int rc = 0;
+
+ while (!rc && (cmd = next_cmd(&buf))) {
+ if (!((fan_control_commands & IBMACPI_FAN_CMD_LEVEL) &&
+ fan_write_cmd_level(cmd, &rc)) &&
+ !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) &&
+ (fan_write_cmd_enable(cmd, &rc) ||
+ fan_write_cmd_disable(cmd, &rc) ||
+ fan_write_cmd_watchdog(cmd, &rc))) &&
+ !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) &&
+ fan_write_cmd_speed(cmd, &rc))
+ )
+ rc = -EINVAL;
+ else if (!rc)
+ fan_watchdog_reset();
+ }
+
+ return rc;
+}
+
+static void fan_watchdog_fire(struct work_struct *ignored)
+{
+ printk(IBM_NOTICE "fan watchdog: enabling fan\n");
+ if (fan_set_enable()) {
+ printk(IBM_ERR "fan watchdog: error while enabling fan\n");
+ /* reschedule for later */
+ fan_watchdog_reset();
+ }
+}
+
static struct ibm_struct ibms[] = {
{
.name = "driver",
@@ -1662,6 +2354,7 @@ static struct ibm_struct ibms[] = {
.type = ACPI_SYSTEM_NOTIFY,
},
#endif
+#ifdef CONFIG_ACPI_IBM_BAY
{
.name = "bay",
.init = bay_init,
@@ -1671,6 +2364,7 @@ static struct ibm_struct ibms[] = {
.handle = &bay_handle,
.type = ACPI_SYSTEM_NOTIFY,
},
+#endif
{
.name = "cmos",
.read = cmos_read,
@@ -1702,6 +2396,8 @@ static struct ibm_struct ibms[] = {
.name = "brightness",
.read = brightness_read,
.write = brightness_write,
+ .init = brightness_init,
+ .exit = brightness_exit,
},
{
.name = "volume",
@@ -1712,6 +2408,8 @@ static struct ibm_struct ibms[] = {
.name = "fan",
.read = fan_read,
.write = fan_write,
+ .init = fan_init,
+ .exit = fan_exit,
.experimental = 1,
},
};
@@ -1825,7 +2523,7 @@ static int __init register_driver(struct ibm_struct *ibm)
}
memset(ibm->driver, 0, sizeof(struct acpi_driver));
- sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name);
+ sprintf(ibm->driver->name, "%s_%s", IBM_NAME, ibm->name);
ibm->driver->ids = ibm->hid;
ibm->driver->ops.add = &ibm_device_add;
@@ -1954,7 +2652,9 @@ IBM_PARAM(light);
#ifdef CONFIG_ACPI_IBM_DOCK
IBM_PARAM(dock);
#endif
+#ifdef CONFIG_ACPI_IBM_BAY
IBM_PARAM(bay);
+#endif
IBM_PARAM(cmos);
IBM_PARAM(led);
IBM_PARAM(beep);
@@ -1971,6 +2671,33 @@ static void acpi_ibm_exit(void)
ibm_exit(&ibms[i]);
remove_proc_entry(IBM_DIR, acpi_root_dir);
+
+ if (ibm_thinkpad_ec_found)
+ kfree(ibm_thinkpad_ec_found);
+}
+
+static char* __init check_dmi_for_ec(void)
+{
+ struct dmi_device *dev = NULL;
+ char ec_fw_string[18];
+
+ /*
+ * ThinkPad T23 or newer, A31 or newer, R50e or newer,
+ * X32 or newer, all Z series; Some models must have an
+ * up-to-date BIOS or they will not be detected.
+ *
+ * See http://thinkwiki.org/wiki/List_of_DMI_IDs
+ */
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
+ if (sscanf(dev->name,
+ "IBM ThinkPad Embedded Controller -[%17c",
+ ec_fw_string) == 1) {
+ ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
+ ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
+ return kstrdup(ec_fw_string, GFP_KERNEL);
+ }
+ }
+ return NULL;
}
static int __init acpi_ibm_init(void)
@@ -1992,6 +2719,12 @@ static int __init acpi_ibm_init(void)
return -ENODEV;
}
+ /* Models with newer firmware report the EC in DMI */
+ ibm_thinkpad_ec_found = check_dmi_for_ec();
+ if (ibm_thinkpad_ec_found)
+ printk(IBM_INFO "ThinkPad EC firmware %s\n",
+ ibm_thinkpad_ec_found);
+
/* these handles are not required */
IBM_HANDLE_INIT(vid);
IBM_HANDLE_INIT(vid2);
@@ -2004,12 +2737,14 @@ static int __init acpi_ibm_init(void)
IBM_HANDLE_INIT(dock);
#endif
IBM_HANDLE_INIT(pci);
+#ifdef CONFIG_ACPI_IBM_BAY
IBM_HANDLE_INIT(bay);
if (bay_handle)
IBM_HANDLE_INIT(bay_ej);
IBM_HANDLE_INIT(bay2);
if (bay2_handle)
IBM_HANDLE_INIT(bay2_ej);
+#endif
IBM_HANDLE_INIT(beep);
IBM_HANDLE_INIT(ecrd);
IBM_HANDLE_INIT(ecwr);
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index c84286cbbe2..02b30ae6a68 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -50,6 +50,7 @@ ACPI_MODULE_NAME("osl")
struct acpi_os_dpc {
acpi_osd_exec_callback function;
void *context;
+ struct work_struct work;
};
#ifdef CONFIG_ACPI_CUSTOM_DSDT
@@ -73,7 +74,6 @@ static unsigned int acpi_irq_irq;
static acpi_osd_handler acpi_irq_handler;
static void *acpi_irq_context;
static struct workqueue_struct *kacpid_wq;
-static struct workqueue_struct *kacpi_notify_wq;
acpi_status acpi_os_initialize(void)
{
@@ -92,9 +92,8 @@ acpi_status acpi_os_initialize1(void)
return AE_NULL_ENTRY;
}
kacpid_wq = create_singlethread_workqueue("kacpid");
- kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify");
BUG_ON(!kacpid_wq);
- BUG_ON(!kacpi_notify_wq);
+
return AE_OK;
}
@@ -106,7 +105,6 @@ acpi_status acpi_os_terminate(void)
}
destroy_workqueue(kacpid_wq);
- destroy_workqueue(kacpi_notify_wq);
return AE_OK;
}
@@ -567,9 +565,9 @@ void acpi_os_derive_pci_id(acpi_handle rhandle, /* upper bound */
acpi_os_derive_pci_id_2(rhandle, chandle, id, &is_bridge, &bus_number);
}
-static void acpi_os_execute_deferred(void *context)
+static void acpi_os_execute_deferred(struct work_struct *work)
{
- struct acpi_os_dpc *dpc = (struct acpi_os_dpc *)context;
+ struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work);
if (!dpc) {
printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
return;
@@ -602,40 +600,41 @@ acpi_status acpi_os_execute(acpi_execute_type type,
{
acpi_status status = AE_OK;
struct acpi_os_dpc *dpc;
- struct work_struct *task;
+
+ ACPI_FUNCTION_TRACE("os_queue_for_execution");
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
"Scheduling function [%p(%p)] for deferred execution.\n",
function, context));
if (!function)
- return AE_BAD_PARAMETER;
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
/*
* Allocate/initialize DPC structure. Note that this memory will be
- * freed by the callee. The kernel handles the tq_struct list in a
+ * freed by the callee. The kernel handles the work_struct list in a
* way that allows us to also free its memory inside the callee.
* Because we may want to schedule several tasks with different
* parameters we can't use the approach some kernel code uses of
- * having a static tq_struct.
- * We can save time and code by allocating the DPC and tq_structs
- * from the same memory.
+ * having a static work_struct.
*/
- dpc = kmalloc(sizeof(struct acpi_os_dpc) +
- sizeof(struct work_struct), GFP_ATOMIC);
+ dpc = kmalloc(sizeof(struct acpi_os_dpc), GFP_ATOMIC);
if (!dpc)
- return AE_NO_MEMORY;
+ return_ACPI_STATUS(AE_NO_MEMORY);
+
dpc->function = function;
dpc->context = context;
- task = (void *)(dpc + 1);
- INIT_WORK(task, acpi_os_execute_deferred, (void *)dpc);
- if (!queue_work((type == OSL_NOTIFY_HANDLER)?
- kacpi_notify_wq : kacpid_wq, task)) {
- status = AE_ERROR;
+
+ INIT_WORK(&dpc->work, acpi_os_execute_deferred);
+ if (!queue_work(kacpid_wq, &dpc->work)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Call to queue_work() failed.\n"));
kfree(dpc);
+ status = AE_ERROR;
}
- return status;
+
+ return_ACPI_STATUS(status);
}
EXPORT_SYMBOL(acpi_os_execute);
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
index 7ba5e49ab30..6fd174a3714 100644
--- a/drivers/acpi/processor_perflib.c
+++ b/drivers/acpi/processor_perflib.c
@@ -83,10 +83,8 @@ static int acpi_processor_ppc_notifier(struct notifier_block *nb,
goto out;
ppc = (unsigned int)pr->performance_platform_limit;
- if (!ppc)
- goto out;
- if (ppc > pr->performance->state_count)
+ if (ppc >= pr->performance->state_count)
goto out;
cpufreq_verify_within_limits(policy, 0,
diff --git a/drivers/acpi/toshiba_acpi.c b/drivers/acpi/toshiba_acpi.c
index 7fe0b7ae973..2f35f891593 100644
--- a/drivers/acpi/toshiba_acpi.c
+++ b/drivers/acpi/toshiba_acpi.c
@@ -41,6 +41,8 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
+#include <linux/backlight.h>
+
#include <asm/uaccess.h>
#include <acpi/acpi_drivers.h>
@@ -210,6 +212,7 @@ static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result)
}
static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
+static struct backlight_device *toshiba_backlight_device;
static int force_fan;
static int last_key_event;
static int key_event_valid;
@@ -271,14 +274,23 @@ dispatch_write(struct file *file, const char __user * buffer,
return result;
}
-static char *read_lcd(char *p)
+static int get_lcd(struct backlight_device *bd)
{
u32 hci_result;
u32 value;
hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);
if (hci_result == HCI_SUCCESS) {
- value = value >> HCI_LCD_BRIGHTNESS_SHIFT;
+ return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
+ } else
+ return -EFAULT;
+}
+
+static char *read_lcd(char *p)
+{
+ int value = get_lcd(NULL);
+
+ if (value >= 0) {
p += sprintf(p, "brightness: %d\n", value);
p += sprintf(p, "brightness_levels: %d\n",
HCI_LCD_BRIGHTNESS_LEVELS);
@@ -289,22 +301,34 @@ static char *read_lcd(char *p)
return p;
}
+static int set_lcd(int value)
+{
+ u32 hci_result;
+
+ value = value << HCI_LCD_BRIGHTNESS_SHIFT;
+ hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result);
+ if (hci_result != HCI_SUCCESS)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int set_lcd_status(struct backlight_device *bd)
+{
+ return set_lcd(bd->props->brightness);
+}
+
static unsigned long write_lcd(const char *buffer, unsigned long count)
{
int value;
- u32 hci_result;
+ int ret = count;
if (sscanf(buffer, " brightness : %i", &value) == 1 &&
- value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) {
- value = value << HCI_LCD_BRIGHTNESS_SHIFT;
- hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result);
- if (hci_result != HCI_SUCCESS)
- return -EFAULT;
- } else {
- return -EINVAL;
- }
-
- return count;
+ value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS)
+ ret = set_lcd(value);
+ else
+ ret = -EINVAL;
+ return ret;
}
static char *read_video(char *p)
@@ -506,6 +530,26 @@ static acpi_status __exit remove_device(void)
return AE_OK;
}
+static struct backlight_properties toshiba_backlight_data = {
+ .owner = THIS_MODULE,
+ .get_brightness = get_lcd,
+ .update_status = set_lcd_status,
+ .max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1,
+};
+
+static void __exit toshiba_acpi_exit(void)
+{
+ if (toshiba_backlight_device)
+ backlight_device_unregister(toshiba_backlight_device);
+
+ remove_device();
+
+ if (toshiba_proc_dir)
+ remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
+
+ return;
+}
+
static int __init toshiba_acpi_init(void)
{
acpi_status status = AE_OK;
@@ -546,17 +590,15 @@ static int __init toshiba_acpi_init(void)
remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
}
- return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
-}
-
-static void __exit toshiba_acpi_exit(void)
-{
- remove_device();
-
- if (toshiba_proc_dir)
- remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
+ toshiba_backlight_device = backlight_device_register("toshiba", NULL,
+ &toshiba_backlight_data);
+ if (IS_ERR(toshiba_backlight_device)) {
+ printk(KERN_ERR "Could not register toshiba backlight device\n");
+ toshiba_backlight_device = NULL;
+ toshiba_acpi_exit();
+ }
- return;
+ return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
}
module_init(toshiba_acpi_init);