Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ACPI: EC: Make the event work state machine visible

The EC driver uses a relatively simple state machine for the event
work handling, but it is not really straightforward to figure out.

The states are as follows:

"Ready": The event handling work can be submitted.

In this state, the EC_FLAGS_QUERY_PENDING flag is clear.

"In progress": The event handling work is pending or is being
processed. It cannot be submitted again.

In ths state, the EC_FLAGS_QUERY_PENDING flag is set and both the
events_to_process count is nonzero and the EC_FLAGS_QUERY_GUARDING
flag is clear.

"Complete": The event handling work has been completed, but it still
cannot be submitted again.

In ths state, the EC_FLAGS_QUERY_PENDING flag is set and the
events_to_process count is zero or the EC_FLAGS_QUERY_GUARDING
flag is set.

The state changes from "Ready" to "In progress" when new event is
detected by advance_transaction() and acpi_ec_submit_event() is
called by it.

Next, the state can change from "In progress" directly to "Ready" in
the following situations:

* ec_event_clearing is ACPI_EC_EVT_TIMING_STATUS and the state of
an ACPI_EC_COMMAND_QUERY transaction becomes ACPI_EC_COMMAND_POLL.

* ec_event_clearing is ACPI_EC_EVT_TIMING_QUERY and the state of
an ACPI_EC_COMMAND_QUERY transaction becomes
ACPI_EC_COMMAND_COMPLETE.

* ec_event_clearing is either ACPI_EC_EVT_TIMING_STATUS or
ACPI_EC_EVT_TIMING_QUERY and there are no more events to
process (ie. ec->events_to_process becomes 0).

If ec_event_clearing is ACPI_EC_EVT_TIMING_EVENT, however, the
state must change from "In progress" to "Complete" before it
can change to "Ready". The changes from "In progress" to
"Complete" in that case occur in the following situations:

* The state of an ACPI_EC_COMMAND_QUERY transaction becomes
ACPI_EC_COMMAND_COMPLETE.

* There are no more events to process (ie. ec->events_to_process
becomes 0).

Finally, the state changes from "Complete" to "Ready" when
advance_transaction() is invoked when the state is "Complete" and
the state of the current transaction is not ACPI_EC_COMMAND_POLL.

To make this state machine visible in the code, add a new
event_state field to struct acpi_ec and modify the code to use
it istead the EC_FLAGS_QUERY_PENDING and EC_FLAGS_QUERY_GUARDING
flags.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

+52 -31
+44 -31
drivers/acpi/ec.c
··· 92 92 93 93 enum { 94 94 EC_FLAGS_QUERY_ENABLED, /* Query is enabled */ 95 - EC_FLAGS_QUERY_PENDING, /* Query is pending */ 96 - EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check */ 97 95 EC_FLAGS_EVENT_HANDLER_INSTALLED, /* Event handler installed */ 98 96 EC_FLAGS_EC_HANDLER_INSTALLED, /* OpReg handler installed */ 99 97 EC_FLAGS_QUERY_METHODS_INSTALLED, /* _Qxx handlers installed */ ··· 448 450 if (!acpi_ec_event_enabled(ec)) 449 451 return false; 450 452 451 - if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { 453 + if (ec->event_state == EC_EVENT_READY) { 452 454 ec_dbg_evt("Command(%s) submitted/blocked", 453 455 acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); 456 + 457 + ec->event_state = EC_EVENT_IN_PROGRESS; 454 458 /* 455 459 * If events_to_process is greqter than 0 at this point, the 456 460 * while () loop in acpi_ec_event_handler() is still running ··· 474 474 return true; 475 475 } 476 476 477 + static void acpi_ec_complete_event(struct acpi_ec *ec) 478 + { 479 + if (ec->event_state == EC_EVENT_IN_PROGRESS) 480 + ec->event_state = EC_EVENT_COMPLETE; 481 + } 482 + 477 483 static void acpi_ec_close_event(struct acpi_ec *ec) 478 484 { 479 - if (test_and_clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) 485 + if (ec->event_state != EC_EVENT_READY) 480 486 ec_dbg_evt("Command(%s) unblocked", 481 487 acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); 488 + 489 + ec->event_state = EC_EVENT_READY; 482 490 acpi_ec_unmask_events(ec); 483 491 } 484 492 ··· 573 565 574 566 static bool acpi_ec_guard_event(struct acpi_ec *ec) 575 567 { 576 - bool guarded = true; 577 568 unsigned long flags; 569 + bool guarded; 578 570 579 571 spin_lock_irqsave(&ec->lock, flags); 580 572 /* ··· 583 575 * evaluating _Qxx, so we need to re-check SCI_EVT after waiting an 584 576 * acceptable period. 585 577 * 586 - * The guarding period begins when EC_FLAGS_QUERY_PENDING is 587 - * flagged, which means SCI_EVT check has just been performed. 588 - * But if the current transaction is ACPI_EC_COMMAND_QUERY, the 589 - * guarding should have already been performed (via 590 - * EC_FLAGS_QUERY_GUARDING) and should not be applied so that the 591 - * ACPI_EC_COMMAND_QUERY transaction can be transitioned into 592 - * ACPI_EC_COMMAND_POLL state immediately. 578 + * The guarding period is applicable if the event state is not 579 + * EC_EVENT_READY, but otherwise if the current transaction is of the 580 + * ACPI_EC_COMMAND_QUERY type, the guarding should have elapsed already 581 + * and it should not be applied to let the transaction transition into 582 + * the ACPI_EC_COMMAND_POLL state immediately. 593 583 */ 594 - if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS || 595 - ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY || 596 - !test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags) || 597 - (ec->curr && ec->curr->command == ACPI_EC_COMMAND_QUERY)) 598 - guarded = false; 584 + guarded = ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT && 585 + ec->event_state != EC_EVENT_READY && 586 + (!ec->curr || ec->curr->command != ACPI_EC_COMMAND_QUERY); 599 587 spin_unlock_irqrestore(&ec->lock, flags); 600 588 return guarded; 601 589 } ··· 623 619 static inline void ec_transaction_transition(struct acpi_ec *ec, unsigned long flag) 624 620 { 625 621 ec->curr->flags |= flag; 626 - if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { 627 - if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS && 628 - flag == ACPI_EC_COMMAND_POLL) 622 + 623 + if (ec->curr->command != ACPI_EC_COMMAND_QUERY) 624 + return; 625 + 626 + switch (ec_event_clearing) { 627 + case ACPI_EC_EVT_TIMING_STATUS: 628 + if (flag == ACPI_EC_COMMAND_POLL) 629 629 acpi_ec_close_event(ec); 630 - if (ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY && 631 - flag == ACPI_EC_COMMAND_COMPLETE) 630 + 631 + return; 632 + 633 + case ACPI_EC_EVT_TIMING_QUERY: 634 + if (flag == ACPI_EC_COMMAND_COMPLETE) 632 635 acpi_ec_close_event(ec); 633 - if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT && 634 - flag == ACPI_EC_COMMAND_COMPLETE) 635 - set_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags); 636 + 637 + return; 638 + 639 + case ACPI_EC_EVT_TIMING_EVENT: 640 + if (flag == ACPI_EC_COMMAND_COMPLETE) 641 + acpi_ec_complete_event(ec); 636 642 } 637 643 } 638 644 ··· 688 674 */ 689 675 if (!t || !(t->flags & ACPI_EC_COMMAND_POLL)) { 690 676 if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT && 691 - (!ec->events_to_process || 692 - test_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags))) { 693 - clear_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags); 677 + ec->event_state == EC_EVENT_COMPLETE) 694 678 acpi_ec_close_event(ec); 695 - } 679 + 696 680 if (!t) 697 681 goto out; 698 682 } ··· 1258 1246 * event handling work again regardless of whether or not the query 1259 1247 * queued up above is processed successfully. 1260 1248 */ 1261 - if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS || 1262 - ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY) 1249 + if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT) 1250 + acpi_ec_complete_event(ec); 1251 + else 1263 1252 acpi_ec_close_event(ec); 1264 1253 1265 1254 spin_unlock_irq(&ec->lock);
+8
drivers/acpi/internal.h
··· 166 166 /* -------------------------------------------------------------------------- 167 167 Embedded Controller 168 168 -------------------------------------------------------------------------- */ 169 + 170 + enum acpi_ec_event_state { 171 + EC_EVENT_READY = 0, /* Event work can be submitted */ 172 + EC_EVENT_IN_PROGRESS, /* Event work is pending or being processed */ 173 + EC_EVENT_COMPLETE, /* Event work processing has completed */ 174 + }; 175 + 169 176 struct acpi_ec { 170 177 acpi_handle handle; 171 178 int gpe; ··· 189 182 spinlock_t lock; 190 183 struct work_struct work; 191 184 unsigned long timestamp; 185 + enum acpi_ec_event_state event_state; 192 186 unsigned int events_to_process; 193 187 unsigned int events_in_progress; 194 188 unsigned int queries_in_progress;