diff options
Diffstat (limited to 'drivers/acpi/ec.c')
-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 | ||