diff options
| author | Alexey Starikovskiy <astarikovskiy@suse.de> | 2008-03-21 10:07:03 -0400 |
|---|---|---|
| committer | Len Brown <len.brown@intel.com> | 2008-03-24 20:48:10 -0400 |
| commit | 845625cdcb17119d5f6c5c8dbe586f2f36e8008a (patch) | |
| tree | 0bd8a2dccbbe07ee61667caaf440e61015944e55 /drivers/acpi | |
| parent | e6e82a3087e6dad619149246082c910623ea9c36 (diff) | |
ACPI: EC: Add poll timer
If we can not use interrupt mode of EC for some reason, start polling
EC for events periodically.
Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi')
| -rw-r--r-- | drivers/acpi/ec.c | 43 |
1 files changed, 39 insertions, 4 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 828c75292cf6..63e0ac2644ad 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
| @@ -84,6 +84,7 @@ enum { | |||
| 84 | EC_FLAGS_NO_WDATA_GPE, /* Don't expect WDATA GPE event */ | 84 | EC_FLAGS_NO_WDATA_GPE, /* Don't expect WDATA GPE event */ |
| 85 | EC_FLAGS_WDATA, /* Data is being written */ | 85 | EC_FLAGS_WDATA, /* Data is being written */ |
| 86 | EC_FLAGS_NO_OBF1_GPE, /* Don't expect GPE before read */ | 86 | EC_FLAGS_NO_OBF1_GPE, /* Don't expect GPE before read */ |
| 87 | EC_FLAGS_RESCHEDULE_POLL /* Re-schedule poll */ | ||
| 87 | }; | 88 | }; |
| 88 | 89 | ||
| 89 | static int acpi_ec_remove(struct acpi_device *device, int type); | 90 | static int acpi_ec_remove(struct acpi_device *device, int type); |
| @@ -130,6 +131,7 @@ static struct acpi_ec { | |||
| 130 | struct mutex lock; | 131 | struct mutex lock; |
| 131 | wait_queue_head_t wait; | 132 | wait_queue_head_t wait; |
| 132 | struct list_head list; | 133 | struct list_head list; |
| 134 | struct delayed_work work; | ||
| 133 | u8 handlers_installed; | 135 | u8 handlers_installed; |
| 134 | } *boot_ec, *first_ec; | 136 | } *boot_ec, *first_ec; |
| 135 | 137 | ||
| @@ -178,6 +180,20 @@ static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event) | |||
| 178 | return 0; | 180 | return 0; |
| 179 | } | 181 | } |
| 180 | 182 | ||
| 183 | static void ec_schedule_ec_poll(struct acpi_ec *ec) | ||
| 184 | { | ||
| 185 | if (test_bit(EC_FLAGS_RESCHEDULE_POLL, &ec->flags)) | ||
| 186 | schedule_delayed_work(&ec->work, | ||
| 187 | msecs_to_jiffies(ACPI_EC_DELAY)); | ||
| 188 | } | ||
| 189 | |||
| 190 | static void ec_switch_to_poll_mode(struct acpi_ec *ec) | ||
| 191 | { | ||
| 192 | clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); | ||
| 193 | acpi_disable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); | ||
| 194 | set_bit(EC_FLAGS_RESCHEDULE_POLL, &ec->flags); | ||
| 195 | } | ||
| 196 | |||
| 181 | static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll) | 197 | static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll) |
| 182 | { | 198 | { |
| 183 | int ret = 0; | 199 | int ret = 0; |
| @@ -218,7 +234,8 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll) | |||
| 218 | if (printk_ratelimit()) | 234 | if (printk_ratelimit()) |
| 219 | pr_info(PREFIX "missing confirmations, " | 235 | pr_info(PREFIX "missing confirmations, " |
| 220 | "switch off interrupt mode.\n"); | 236 | "switch off interrupt mode.\n"); |
| 221 | clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); | 237 | ec_switch_to_poll_mode(ec); |
| 238 | ec_schedule_ec_poll(ec); | ||
| 222 | } | 239 | } |
| 223 | goto end; | 240 | goto end; |
| 224 | } | 241 | } |
| @@ -529,28 +546,37 @@ static u32 acpi_ec_gpe_handler(void *data) | |||
| 529 | { | 546 | { |
| 530 | acpi_status status = AE_OK; | 547 | acpi_status status = AE_OK; |
| 531 | struct acpi_ec *ec = data; | 548 | struct acpi_ec *ec = data; |
| 549 | u8 state = acpi_ec_read_status(ec); | ||
| 532 | 550 | ||
| 533 | pr_debug(PREFIX "~~~> interrupt\n"); | 551 | pr_debug(PREFIX "~~~> interrupt\n"); |
| 534 | clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags); | 552 | clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags); |
| 535 | if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) | 553 | if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) |
| 536 | wake_up(&ec->wait); | 554 | wake_up(&ec->wait); |
| 537 | 555 | ||
| 538 | if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_SCI) { | 556 | if (state & ACPI_EC_FLAG_SCI) { |
| 539 | if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) | 557 | if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) |
| 540 | status = acpi_os_execute(OSL_EC_BURST_HANDLER, | 558 | status = acpi_os_execute(OSL_EC_BURST_HANDLER, |
| 541 | acpi_ec_gpe_query, ec); | 559 | acpi_ec_gpe_query, ec); |
| 542 | } else if (unlikely(!test_bit(EC_FLAGS_GPE_MODE, &ec->flags))) { | 560 | } else if (!test_bit(EC_FLAGS_GPE_MODE, &ec->flags) && |
| 561 | in_interrupt()) { | ||
| 543 | /* this is non-query, must be confirmation */ | 562 | /* this is non-query, must be confirmation */ |
| 544 | if (printk_ratelimit()) | 563 | if (printk_ratelimit()) |
| 545 | pr_info(PREFIX "non-query interrupt received," | 564 | pr_info(PREFIX "non-query interrupt received," |
| 546 | " switching to interrupt mode\n"); | 565 | " switching to interrupt mode\n"); |
| 547 | set_bit(EC_FLAGS_GPE_MODE, &ec->flags); | 566 | set_bit(EC_FLAGS_GPE_MODE, &ec->flags); |
| 567 | clear_bit(EC_FLAGS_RESCHEDULE_POLL, &ec->flags); | ||
| 548 | } | 568 | } |
| 549 | 569 | ec_schedule_ec_poll(ec); | |
| 550 | return ACPI_SUCCESS(status) ? | 570 | return ACPI_SUCCESS(status) ? |
| 551 | ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; | 571 | ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; |
| 552 | } | 572 | } |
| 553 | 573 | ||
| 574 | static void do_ec_poll(struct work_struct *work) | ||
| 575 | { | ||
| 576 | struct acpi_ec *ec = container_of(work, struct acpi_ec, work.work); | ||
| 577 | (void)acpi_ec_gpe_handler(ec); | ||
| 578 | } | ||
| 579 | |||
| 554 | /* -------------------------------------------------------------------------- | 580 | /* -------------------------------------------------------------------------- |
| 555 | Address Space Management | 581 | Address Space Management |
| 556 | -------------------------------------------------------------------------- */ | 582 | -------------------------------------------------------------------------- */ |
| @@ -711,6 +737,7 @@ static struct acpi_ec *make_acpi_ec(void) | |||
| 711 | mutex_init(&ec->lock); | 737 | mutex_init(&ec->lock); |
| 712 | init_waitqueue_head(&ec->wait); | 738 | init_waitqueue_head(&ec->wait); |
| 713 | INIT_LIST_HEAD(&ec->list); | 739 | INIT_LIST_HEAD(&ec->list); |
| 740 | INIT_DELAYED_WORK_DEFERRABLE(&ec->work, do_ec_poll); | ||
| 714 | return ec; | 741 | return ec; |
| 715 | } | 742 | } |
| 716 | 743 | ||
| @@ -752,8 +779,15 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) | |||
| 752 | return AE_CTRL_TERMINATE; | 779 | return AE_CTRL_TERMINATE; |
| 753 | } | 780 | } |
| 754 | 781 | ||
| 782 | static void ec_poll_stop(struct acpi_ec *ec) | ||
| 783 | { | ||
| 784 | clear_bit(EC_FLAGS_RESCHEDULE_POLL, &ec->flags); | ||
| 785 | cancel_delayed_work(&ec->work); | ||
| 786 | } | ||
| 787 | |||
| 755 | static void ec_remove_handlers(struct acpi_ec *ec) | 788 | static void ec_remove_handlers(struct acpi_ec *ec) |
| 756 | { | 789 | { |
| 790 | ec_poll_stop(ec); | ||
| 757 | if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, | 791 | if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, |
| 758 | ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) | 792 | ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) |
| 759 | pr_err(PREFIX "failed to remove space handler\n"); | 793 | pr_err(PREFIX "failed to remove space handler\n"); |
| @@ -899,6 +933,7 @@ static int acpi_ec_start(struct acpi_device *device) | |||
| 899 | 933 | ||
| 900 | /* EC is fully operational, allow queries */ | 934 | /* EC is fully operational, allow queries */ |
| 901 | clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); | 935 | clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); |
| 936 | ec_schedule_ec_poll(ec); | ||
| 902 | return ret; | 937 | return ret; |
| 903 | } | 938 | } |
| 904 | 939 | ||
