diff options
-rw-r--r-- | drivers/acpi/ec.c | 151 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 3 |
2 files changed, 119 insertions, 35 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 97e08d0e3192..4fd84fbdac29 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -398,7 +398,7 @@ static void acpi_ec_submit_request(struct acpi_ec *ec) { ec->reference_count++; if (test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags) && - ec->reference_count == 1) + ec->gpe >= 0 && ec->reference_count == 1) acpi_ec_enable_gpe(ec, true); } @@ -408,7 +408,7 @@ static void acpi_ec_complete_request(struct acpi_ec *ec) ec->reference_count--; if (test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags) && - ec->reference_count == 0) + ec->gpe >= 0 && ec->reference_count == 0) acpi_ec_disable_gpe(ec, true); flushed = acpi_ec_flushed(ec); if (flushed) @@ -418,7 +418,11 @@ static void acpi_ec_complete_request(struct acpi_ec *ec) static void acpi_ec_mask_events(struct acpi_ec *ec) { if (!test_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags)) { - acpi_ec_disable_gpe(ec, false); + if (ec->gpe >= 0) + acpi_ec_disable_gpe(ec, false); + else + disable_irq_nosync(ec->irq); + ec_dbg_drv("Polling enabled"); set_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags); } @@ -428,7 +432,11 @@ static void acpi_ec_unmask_events(struct acpi_ec *ec) { if (test_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags)) { clear_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags); - acpi_ec_enable_gpe(ec, false); + if (ec->gpe >= 0) + acpi_ec_enable_gpe(ec, false); + else + enable_irq(ec->irq); + ec_dbg_drv("Polling disabled"); } } @@ -648,7 +656,9 @@ static void advance_transaction(struct acpi_ec *ec) * ensure a hardware STS 0->1 change after this clearing can always * trigger a GPE interrupt. */ - acpi_ec_clear_gpe(ec); + if (ec->gpe >= 0) + acpi_ec_clear_gpe(ec); + status = acpi_ec_read_status(ec); t = ec->curr; /* @@ -1275,18 +1285,28 @@ static void acpi_ec_event_handler(struct work_struct *work) acpi_ec_check_event(ec); } -static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, - u32 gpe_number, void *data) +static void acpi_ec_handle_interrupt(struct acpi_ec *ec) { unsigned long flags; - struct acpi_ec *ec = data; spin_lock_irqsave(&ec->lock, flags); advance_transaction(ec); spin_unlock_irqrestore(&ec->lock, flags); +} + +static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, + u32 gpe_number, void *data) +{ + acpi_ec_handle_interrupt(data); return ACPI_INTERRUPT_HANDLED; } +static irqreturn_t acpi_ec_irq_handler(int irq, void *data) +{ + acpi_ec_handle_interrupt(data); + return IRQ_HANDLED; +} + /* -------------------------------------------------------------------------- * Address Space Management * -------------------------------------------------------------------------- */ @@ -1359,6 +1379,8 @@ static struct acpi_ec *acpi_ec_alloc(void) ec->timestamp = jiffies; ec->busy_polling = true; ec->polling_guard = 0; + ec->gpe = -1; + ec->irq = -1; return ec; } @@ -1406,9 +1428,13 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) /* Get GPE bit assignment (EC events). */ /* TODO: Add support for _GPE returning a package */ status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp); - if (ACPI_FAILURE(status)) - return status; - ec->gpe = tmp; + if (ACPI_SUCCESS(status)) + ec->gpe = tmp; + + /* + * Errors are non-fatal, allowing for ACPI Reduced Hardware + * platforms which use GpioInt instead of GPE. + */ } /* Use the global lock for all EC transactions? */ tmp = 0; @@ -1418,12 +1444,57 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) return AE_CTRL_TERMINATE; } +static void install_gpe_event_handler(struct acpi_ec *ec) +{ + acpi_status status = + acpi_install_gpe_raw_handler(NULL, ec->gpe, + ACPI_GPE_EDGE_TRIGGERED, + &acpi_ec_gpe_handler, + ec); + if (ACPI_SUCCESS(status)) { + /* This is not fatal as we can poll EC events */ + set_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags); + acpi_ec_leave_noirq(ec); + if (test_bit(EC_FLAGS_STARTED, &ec->flags) && + ec->reference_count >= 1) + acpi_ec_enable_gpe(ec, true); + } +} + +/* ACPI reduced hardware platforms use a GpioInt specified in _CRS. */ +static int install_gpio_irq_event_handler(struct acpi_ec *ec, + struct acpi_device *device) +{ + int irq = acpi_dev_gpio_irq_get(device, 0); + int ret; + + if (irq < 0) + return irq; + + ret = request_irq(irq, acpi_ec_irq_handler, IRQF_SHARED, + "ACPI EC", ec); + + /* + * Unlike the GPE case, we treat errors here as fatal, we'll only + * implement GPIO polling if we find a case that needs it. + */ + if (ret < 0) + return ret; + + ec->irq = irq; + set_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags); + acpi_ec_leave_noirq(ec); + + return 0; +} + /* * Note: This function returns an error code only when the address space * handler is not installed, which means "not able to handle * transactions". */ -static int ec_install_handlers(struct acpi_ec *ec, bool handle_events) +static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device, + bool handle_events) { acpi_status status; @@ -1464,16 +1535,15 @@ static int ec_install_handlers(struct acpi_ec *ec, bool handle_events) set_bit(EC_FLAGS_QUERY_METHODS_INSTALLED, &ec->flags); } if (!test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags)) { - status = acpi_install_gpe_raw_handler(NULL, ec->gpe, - ACPI_GPE_EDGE_TRIGGERED, - &acpi_ec_gpe_handler, ec); - /* This is not fatal as we can poll EC events */ - if (ACPI_SUCCESS(status)) { - set_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags); - acpi_ec_leave_noirq(ec); - if (test_bit(EC_FLAGS_STARTED, &ec->flags) && - ec->reference_count >= 1) - acpi_ec_enable_gpe(ec, true); + if (ec->gpe >= 0) { + install_gpe_event_handler(ec); + } else if (device) { + int ret = install_gpio_irq_event_handler(ec, device); + + if (ret) + return ret; + } else { /* No GPE and no GpioInt? */ + return -ENODEV; } } /* EC is fully operational, allow queries */ @@ -1505,9 +1575,14 @@ static void ec_remove_handlers(struct acpi_ec *ec) acpi_ec_stop(ec, false); if (test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags)) { - if (ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe, - &acpi_ec_gpe_handler))) + if (ec->gpe >= 0 && + ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe, + &acpi_ec_gpe_handler))) pr_err("failed to remove gpe handler\n"); + + if (ec->irq >= 0) + free_irq(ec->irq, ec); + clear_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags); } if (test_bit(EC_FLAGS_QUERY_METHODS_INSTALLED, &ec->flags)) { @@ -1516,11 +1591,12 @@ static void ec_remove_handlers(struct acpi_ec *ec) } } -static int acpi_ec_setup(struct acpi_ec *ec, bool handle_events) +static int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device, + bool handle_events) { int ret; - ret = ec_install_handlers(ec, handle_events); + ret = ec_install_handlers(ec, device, handle_events); if (ret) return ret; @@ -1531,8 +1607,8 @@ static int acpi_ec_setup(struct acpi_ec *ec, bool handle_events) } acpi_handle_info(ec->handle, - "GPE=0x%x, EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n", - ec->gpe, ec->command_addr, ec->data_addr); + "GPE=0x%x, IRQ=%d, EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n", + ec->gpe, ec->irq, ec->command_addr, ec->data_addr); return ret; } @@ -1596,7 +1672,7 @@ static int acpi_ec_add(struct acpi_device *device) } } - ret = acpi_ec_setup(ec, true); + ret = acpi_ec_setup(ec, device, true); if (ret) goto err_query; @@ -1716,7 +1792,7 @@ void __init acpi_ec_dsdt_probe(void) * At this point, the GPE is not fully initialized, so do not to * handle the events. */ - ret = acpi_ec_setup(ec, false); + ret = acpi_ec_setup(ec, NULL, false); if (ret) { acpi_ec_free(ec); return; @@ -1889,14 +1965,21 @@ void __init acpi_ec_ecdt_probe(void) ec->command_addr = ecdt_ptr->control.address; ec->data_addr = ecdt_ptr->data.address; } - ec->gpe = ecdt_ptr->gpe; + + /* + * Ignore the GPE value on Reduced Hardware platforms. + * Some products have this set to an erroneous value. + */ + if (!acpi_gbl_reduced_hardware) + ec->gpe = ecdt_ptr->gpe; + ec->handle = ACPI_ROOT_OBJECT; /* * At this point, the namespace is not initialized, so do not find * the namespace objects, or handle the events. */ - ret = acpi_ec_setup(ec, false); + ret = acpi_ec_setup(ec, NULL, false); if (ret) { acpi_ec_free(ec); return; @@ -1928,7 +2011,7 @@ static int acpi_ec_suspend_noirq(struct device *dev) * masked at the low level without side effects. */ if (ec_no_wakeup && test_bit(EC_FLAGS_STARTED, &ec->flags) && - ec->reference_count >= 1) + ec->gpe >= 0 && ec->reference_count >= 1) acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); acpi_ec_enter_noirq(ec); @@ -1943,7 +2026,7 @@ static int acpi_ec_resume_noirq(struct device *dev) acpi_ec_leave_noirq(ec); if (ec_no_wakeup && test_bit(EC_FLAGS_STARTED, &ec->flags) && - ec->reference_count >= 1) + ec->gpe >= 0 && ec->reference_count >= 1) acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); return 0; diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index afe6636f9ad3..3616daec650b 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -165,7 +165,8 @@ static inline void acpi_early_processor_osc(void) {} -------------------------------------------------------------------------- */ struct acpi_ec { acpi_handle handle; - u32 gpe; + int gpe; + int irq; unsigned long command_addr; unsigned long data_addr; bool global_lock; |