]> err.no Git - linux-2.6/blobdiff - drivers/acpi/ec.c
V4L/DVB (7848): Fix dependencies for tuner-xc2028 and em28xx-dvb
[linux-2.6] / drivers / acpi / ec.c
index da67d228c9eabf7e921670bbc8ecc08dd5f51665..0924992187e87031809bf70c1b75c6367cc38a64 100644 (file)
@@ -106,6 +106,7 @@ static struct acpi_ec {
        wait_queue_head_t wait;
        struct list_head list;
        struct delayed_work work;
+       atomic_t irq_count;
        u8 handlers_installed;
 } *boot_ec, *first_ec;
 
@@ -171,6 +172,7 @@ static void ec_switch_to_poll_mode(struct acpi_ec *ec)
 
 static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
 {
+       atomic_set(&ec->irq_count, 0);
        if (likely(test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) &&
            likely(!force_poll)) {
                if (wait_event_timeout(ec->wait, acpi_ec_check_status(ec, event),
@@ -489,6 +491,12 @@ static u32 acpi_ec_gpe_handler(void *data)
        u8 state = acpi_ec_read_status(ec);
 
        pr_debug(PREFIX "~~~> interrupt\n");
+       atomic_inc(&ec->irq_count);
+       if (atomic_read(&ec->irq_count) > 5) {
+               pr_err(PREFIX "GPE storm detected, disabling EC GPE\n");
+               ec_switch_to_poll_mode(ec);
+               goto end;
+       }
        clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
        if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags))
                wake_up(&ec->wait);
@@ -507,6 +515,7 @@ static u32 acpi_ec_gpe_handler(void *data)
                set_bit(EC_FLAGS_GPE_MODE, &ec->flags);
                clear_bit(EC_FLAGS_RESCHEDULE_POLL, &ec->flags);
        }
+end:
        ec_schedule_ec_poll(ec);
        return ACPI_SUCCESS(status) ?
            ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
@@ -515,6 +524,7 @@ static u32 acpi_ec_gpe_handler(void *data)
 static void do_ec_poll(struct work_struct *work)
 {
        struct acpi_ec *ec = container_of(work, struct acpi_ec, work.work);
+       atomic_set(&ec->irq_count, 0);
        (void)acpi_ec_gpe_handler(ec);
 }
 
@@ -522,20 +532,6 @@ static void do_ec_poll(struct work_struct *work)
                              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 = (function != ACPI_REGION_DEACTIVATE) ?
-           handler_context : NULL;
-
-       return AE_OK;
-}
-
 static acpi_status
 acpi_ec_space_handler(u32 function, acpi_physical_address address,
                      u32 bits, acpi_integer *value,
@@ -638,16 +634,11 @@ static int acpi_ec_add_fs(struct acpi_device *device)
                        return -ENODEV;
        }
 
-       entry = create_proc_entry(ACPI_EC_FILE_INFO, S_IRUGO,
-                                 acpi_device_dir(device));
+       entry = proc_create_data(ACPI_EC_FILE_INFO, S_IRUGO,
+                                acpi_device_dir(device),
+                                &acpi_ec_info_ops, acpi_driver_data(device));
        if (!entry)
                return -ENODEV;
-       else {
-               entry->proc_fops = &acpi_ec_info_ops;
-               entry->data = acpi_driver_data(device);
-               entry->owner = THIS_MODULE;
-       }
-
        return 0;
 }
 
@@ -679,6 +670,7 @@ static struct acpi_ec *make_acpi_ec(void)
        init_waitqueue_head(&ec->wait);
        INIT_LIST_HEAD(&ec->list);
        INIT_DELAYED_WORK_DEFERRABLE(&ec->work, do_ec_poll);
+       atomic_set(&ec->irq_count, 0);
        return ec;
 }
 
@@ -711,9 +703,6 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
        status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe);
        if (ACPI_FAILURE(status))
                return status;
-       /* Find and register all query methods */
-       acpi_walk_namespace(ACPI_TYPE_METHOD, handle, 1,
-                           acpi_ec_register_query_methods, ec, NULL);
        /* Use the global lock for all EC transactions? */
        acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock);
        ec->handle = handle;
@@ -748,31 +737,28 @@ static int acpi_ec_add(struct acpi_device *device)
        strcpy(acpi_device_class(device), ACPI_EC_CLASS);
 
        /* Check for boot EC */
-       if (boot_ec) {
-               if (boot_ec->handle == device->handle) {
-                       /* Pre-loaded EC from DSDT, just move pointer */
-                       ec = boot_ec;
-                       boot_ec = NULL;
-                       goto end;
-               } else if (boot_ec->handle == ACPI_ROOT_OBJECT) {
-                       /* ECDT-based EC, time to shut it down */
-                       ec_remove_handlers(boot_ec);
-                       kfree(boot_ec);
-                       first_ec = boot_ec = NULL;
+       if (boot_ec &&
+           (boot_ec->handle == device->handle ||
+            boot_ec->handle == ACPI_ROOT_OBJECT)) {
+               ec = boot_ec;
+               boot_ec = NULL;
+       } else {
+               ec = make_acpi_ec();
+               if (!ec)
+                       return -ENOMEM;
+               if (ec_parse_device(device->handle, 0, ec, NULL) !=
+                   AE_CTRL_TERMINATE) {
+                       kfree(ec);
+                       return -EINVAL;
                }
        }
 
-       ec = make_acpi_ec();
-       if (!ec)
-               return -ENOMEM;
-
-       if (ec_parse_device(device->handle, 0, ec, NULL) !=
-           AE_CTRL_TERMINATE) {
-               kfree(ec);
-               return -EINVAL;
-       }
        ec->handle = device->handle;
-      end:
+
+       /* Find and register all query methods */
+       acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1,
+                           acpi_ec_register_query_methods, ec, NULL);
+
        if (!first_ec)
                first_ec = ec;
        acpi_driver_data(device) = ec;
@@ -847,7 +833,7 @@ static int ec_install_handlers(struct acpi_ec *ec)
        status = acpi_install_address_space_handler(ec->handle,
                                                    ACPI_ADR_SPACE_EC,
                                                    &acpi_ec_space_handler,
-                                                   &acpi_ec_space_setup, ec);
+                                                   NULL, ec);
        if (ACPI_FAILURE(status)) {
                acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler);
                return -ENODEV;
@@ -927,6 +913,7 @@ int __init acpi_ec_ecdt_probe(void)
                boot_ec->data_addr = ecdt_ptr->data.address;
                boot_ec->gpe = ecdt_ptr->gpe;
                boot_ec->handle = ACPI_ROOT_OBJECT;
+               acpi_get_handle(ACPI_ROOT_OBJECT, ecdt_ptr->id, &boot_ec->handle);
        } else {
                /* This workaround is needed only on some broken machines,
                 * which require early EC, but fail to provide ECDT */