Lee, Chun-Yi 46b2d4
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
Lee, Chun-Yi 46b2d4
Date: Tue, 23 Nov 2021 19:44:46 +0100
Lee, Chun-Yi 46b2d4
Subject: ACPI: EC: Make the event work state machine visible
Lee, Chun-Yi 46b2d4
Patch-mainline: v5.17-rc1
Lee, Chun-Yi 46b2d4
Git-commit: c33676aa48249b007d55198dc8348cd117e3d8cc
Lee, Chun-Yi 46b2d4
References: jsc#PED-1408
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
The EC driver uses a relatively simple state machine for the event
Lee, Chun-Yi 46b2d4
work handling, but it is not really straightforward to figure out.
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
The states are as follows:
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
 "Ready": The event handling work can be submitted.
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
  In this state, the EC_FLAGS_QUERY_PENDING flag is clear.
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
 "In progress": The event handling work is pending or is being
Lee, Chun-Yi 46b2d4
                processed.  It cannot be submitted again.
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
  In ths state, the EC_FLAGS_QUERY_PENDING flag is set and both the
Lee, Chun-Yi 46b2d4
  events_to_process count is nonzero and the EC_FLAGS_QUERY_GUARDING
Lee, Chun-Yi 46b2d4
  flag is clear.
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
 "Complete": The event handling work has been completed, but it still
Lee, Chun-Yi 46b2d4
             cannot be submitted again.
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
  In ths state, the EC_FLAGS_QUERY_PENDING flag is set and the
Lee, Chun-Yi 46b2d4
  events_to_process count is zero or the EC_FLAGS_QUERY_GUARDING
Lee, Chun-Yi 46b2d4
  flag is set.
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
The state changes from "Ready" to "In progress" when new event is
Lee, Chun-Yi 46b2d4
detected by advance_transaction() and acpi_ec_submit_event() is
Lee, Chun-Yi 46b2d4
called by it.
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
Next, the state can change from "In progress" directly to "Ready" in
Lee, Chun-Yi 46b2d4
the following situations:
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
 * ec_event_clearing is ACPI_EC_EVT_TIMING_STATUS and the state of
Lee, Chun-Yi 46b2d4
   an ACPI_EC_COMMAND_QUERY transaction becomes ACPI_EC_COMMAND_POLL.
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
 * ec_event_clearing is ACPI_EC_EVT_TIMING_QUERY and the state of
Lee, Chun-Yi 46b2d4
   an ACPI_EC_COMMAND_QUERY transaction becomes
Lee, Chun-Yi 46b2d4
   ACPI_EC_COMMAND_COMPLETE.
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
 * ec_event_clearing is either ACPI_EC_EVT_TIMING_STATUS or
Lee, Chun-Yi 46b2d4
   ACPI_EC_EVT_TIMING_QUERY and there are no more events to
Lee, Chun-Yi 46b2d4
   process (ie. ec->events_to_process becomes 0).
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
If ec_event_clearing is ACPI_EC_EVT_TIMING_EVENT, however, the
Lee, Chun-Yi 46b2d4
state must change from "In progress" to "Complete" before it
Lee, Chun-Yi 46b2d4
can change to "Ready".  The changes from "In progress" to
Lee, Chun-Yi 46b2d4
"Complete" in that case occur in the following situations:
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
 * The state of an ACPI_EC_COMMAND_QUERY transaction becomes
Lee, Chun-Yi 46b2d4
   ACPI_EC_COMMAND_COMPLETE.
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
 * There are no more events to process (ie. ec->events_to_process
Lee, Chun-Yi 46b2d4
   becomes 0).
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
Finally, the state changes from "Complete" to "Ready" when
Lee, Chun-Yi 46b2d4
advance_transaction() is invoked when the state is "Complete" and
Lee, Chun-Yi 46b2d4
the state of the current transaction is not ACPI_EC_COMMAND_POLL.
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
To make this state machine visible in the code, add a new
Lee, Chun-Yi 46b2d4
event_state field to struct acpi_ec and modify the code to use
Lee, Chun-Yi 46b2d4
it istead the EC_FLAGS_QUERY_PENDING and EC_FLAGS_QUERY_GUARDING
Lee, Chun-Yi 46b2d4
flags.
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Lee, Chun-Yi 46b2d4
Acked-by: Lee, Chun-Yi <jlee@suse.com>
Lee, Chun-Yi 46b2d4
---
Lee, Chun-Yi 46b2d4
 drivers/acpi/ec.c       |   75 ++++++++++++++++++++++++++++--------------------
Lee, Chun-Yi 46b2d4
 drivers/acpi/internal.h |    8 +++++
Lee, Chun-Yi 46b2d4
 2 files changed, 52 insertions(+), 31 deletions(-)
Lee, Chun-Yi 46b2d4
Lee, Chun-Yi 46b2d4
--- a/drivers/acpi/ec.c
Lee, Chun-Yi 46b2d4
+++ b/drivers/acpi/ec.c
Lee, Chun-Yi 46b2d4
@@ -92,8 +92,6 @@ enum ec_command {
Lee, Chun-Yi 46b2d4
 
Lee, Chun-Yi 46b2d4
 enum {
Lee, Chun-Yi 46b2d4
 	EC_FLAGS_QUERY_ENABLED,		/* Query is enabled */
Lee, Chun-Yi 46b2d4
-	EC_FLAGS_QUERY_PENDING,		/* Query is pending */
Lee, Chun-Yi 46b2d4
-	EC_FLAGS_QUERY_GUARDING,	/* Guard for SCI_EVT check */
Lee, Chun-Yi 46b2d4
 	EC_FLAGS_EVENT_HANDLER_INSTALLED,	/* Event handler installed */
Lee, Chun-Yi 46b2d4
 	EC_FLAGS_EC_HANDLER_INSTALLED,	/* OpReg handler installed */
Lee, Chun-Yi 46b2d4
 	EC_FLAGS_QUERY_METHODS_INSTALLED, /* _Qxx handlers installed */
Lee, Chun-Yi 46b2d4
@@ -450,9 +448,11 @@ static bool acpi_ec_submit_event(struct
Lee, Chun-Yi 46b2d4
 	if (!acpi_ec_event_enabled(ec))
Lee, Chun-Yi 46b2d4
 		return false;
Lee, Chun-Yi 46b2d4
 
Lee, Chun-Yi 46b2d4
-	if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
Lee, Chun-Yi 46b2d4
+	if (ec->event_state == EC_EVENT_READY) {
Lee, Chun-Yi 46b2d4
 		ec_dbg_evt("Command(%s) submitted/blocked",
Lee, Chun-Yi 46b2d4
 			   acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
Lee, Chun-Yi 46b2d4
+
Lee, Chun-Yi 46b2d4
+		ec->event_state = EC_EVENT_IN_PROGRESS;
Lee, Chun-Yi 46b2d4
 		/*
Lee, Chun-Yi 46b2d4
 		 * If events_to_process is greqter than 0 at this point, the
Lee, Chun-Yi 46b2d4
 		 * while () loop in acpi_ec_event_handler() is still running
Lee, Chun-Yi 46b2d4
@@ -474,11 +474,19 @@ static bool acpi_ec_submit_event(struct
Lee, Chun-Yi 46b2d4
 	return true;
Lee, Chun-Yi 46b2d4
 }
Lee, Chun-Yi 46b2d4
 
Lee, Chun-Yi 46b2d4
+static void acpi_ec_complete_event(struct acpi_ec *ec)
Lee, Chun-Yi 46b2d4
+{
Lee, Chun-Yi 46b2d4
+	if (ec->event_state == EC_EVENT_IN_PROGRESS)
Lee, Chun-Yi 46b2d4
+		ec->event_state = EC_EVENT_COMPLETE;
Lee, Chun-Yi 46b2d4
+}
Lee, Chun-Yi 46b2d4
+
Lee, Chun-Yi 46b2d4
 static void acpi_ec_close_event(struct acpi_ec *ec)
Lee, Chun-Yi 46b2d4
 {
Lee, Chun-Yi 46b2d4
-	if (test_and_clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags))
Lee, Chun-Yi 46b2d4
+	if (ec->event_state != EC_EVENT_READY)
Lee, Chun-Yi 46b2d4
 		ec_dbg_evt("Command(%s) unblocked",
Lee, Chun-Yi 46b2d4
 			   acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
Lee, Chun-Yi 46b2d4
+
Lee, Chun-Yi 46b2d4
+	ec->event_state = EC_EVENT_READY;
Lee, Chun-Yi 46b2d4
 	acpi_ec_unmask_events(ec);
Lee, Chun-Yi 46b2d4
 }
Lee, Chun-Yi 46b2d4
 
Lee, Chun-Yi 46b2d4
@@ -565,8 +573,8 @@ void acpi_ec_flush_work(void)
Lee, Chun-Yi 46b2d4
 
Lee, Chun-Yi 46b2d4
 static bool acpi_ec_guard_event(struct acpi_ec *ec)
Lee, Chun-Yi 46b2d4
 {
Lee, Chun-Yi 46b2d4
-	bool guarded = true;
Lee, Chun-Yi 46b2d4
 	unsigned long flags;
Lee, Chun-Yi 46b2d4
+	bool guarded;
Lee, Chun-Yi 46b2d4
 
Lee, Chun-Yi 46b2d4
 	spin_lock_irqsave(&ec->lock, flags);
Lee, Chun-Yi 46b2d4
 	/*
Lee, Chun-Yi 46b2d4
@@ -575,19 +583,15 @@ static bool acpi_ec_guard_event(struct a
Lee, Chun-Yi 46b2d4
 	 * evaluating _Qxx, so we need to re-check SCI_EVT after waiting an
Lee, Chun-Yi 46b2d4
 	 * acceptable period.
Lee, Chun-Yi 46b2d4
 	 *
Lee, Chun-Yi 46b2d4
-	 * The guarding period begins when EC_FLAGS_QUERY_PENDING is
Lee, Chun-Yi 46b2d4
-	 * flagged, which means SCI_EVT check has just been performed.
Lee, Chun-Yi 46b2d4
-	 * But if the current transaction is ACPI_EC_COMMAND_QUERY, the
Lee, Chun-Yi 46b2d4
-	 * guarding should have already been performed (via
Lee, Chun-Yi 46b2d4
-	 * EC_FLAGS_QUERY_GUARDING) and should not be applied so that the
Lee, Chun-Yi 46b2d4
-	 * ACPI_EC_COMMAND_QUERY transaction can be transitioned into
Lee, Chun-Yi 46b2d4
-	 * ACPI_EC_COMMAND_POLL state immediately.
Lee, Chun-Yi 46b2d4
+	 * The guarding period is applicable if the event state is not
Lee, Chun-Yi 46b2d4
+	 * EC_EVENT_READY, but otherwise if the current transaction is of the
Lee, Chun-Yi 46b2d4
+	 * ACPI_EC_COMMAND_QUERY type, the guarding should have elapsed already
Lee, Chun-Yi 46b2d4
+	 * and it should not be applied to let the transaction transition into
Lee, Chun-Yi 46b2d4
+	 * the ACPI_EC_COMMAND_POLL state immediately.
Lee, Chun-Yi 46b2d4
 	 */
Lee, Chun-Yi 46b2d4
-	if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS ||
Lee, Chun-Yi 46b2d4
-	    ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY ||
Lee, Chun-Yi 46b2d4
-	    !test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags) ||
Lee, Chun-Yi 46b2d4
-	    (ec->curr && ec->curr->command == ACPI_EC_COMMAND_QUERY))
Lee, Chun-Yi 46b2d4
-		guarded = false;
Lee, Chun-Yi 46b2d4
+	guarded = ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT &&
Lee, Chun-Yi 46b2d4
+		ec->event_state != EC_EVENT_READY &&
Lee, Chun-Yi 46b2d4
+		(!ec->curr || ec->curr->command != ACPI_EC_COMMAND_QUERY);
Lee, Chun-Yi 46b2d4
 	spin_unlock_irqrestore(&ec->lock, flags);
Lee, Chun-Yi 46b2d4
 	return guarded;
Lee, Chun-Yi 46b2d4
 }
Lee, Chun-Yi 46b2d4
@@ -619,16 +623,26 @@ static int ec_transaction_completed(stru
Lee, Chun-Yi 46b2d4
 static inline void ec_transaction_transition(struct acpi_ec *ec, unsigned long flag)
Lee, Chun-Yi 46b2d4
 {
Lee, Chun-Yi 46b2d4
 	ec->curr->flags |= flag;
Lee, Chun-Yi 46b2d4
-	if (ec->curr->command == ACPI_EC_COMMAND_QUERY) {
Lee, Chun-Yi 46b2d4
-		if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS &&
Lee, Chun-Yi 46b2d4
-		    flag == ACPI_EC_COMMAND_POLL)
Lee, Chun-Yi 46b2d4
+
Lee, Chun-Yi 46b2d4
+	if (ec->curr->command != ACPI_EC_COMMAND_QUERY)
Lee, Chun-Yi 46b2d4
+		return;
Lee, Chun-Yi 46b2d4
+
Lee, Chun-Yi 46b2d4
+	switch (ec_event_clearing) {
Lee, Chun-Yi 46b2d4
+	case ACPI_EC_EVT_TIMING_STATUS:
Lee, Chun-Yi 46b2d4
+		if (flag == ACPI_EC_COMMAND_POLL)
Lee, Chun-Yi 46b2d4
 			acpi_ec_close_event(ec);
Lee, Chun-Yi 46b2d4
-		if (ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY &&
Lee, Chun-Yi 46b2d4
-		    flag == ACPI_EC_COMMAND_COMPLETE)
Lee, Chun-Yi 46b2d4
+
Lee, Chun-Yi 46b2d4
+		return;
Lee, Chun-Yi 46b2d4
+
Lee, Chun-Yi 46b2d4
+	case ACPI_EC_EVT_TIMING_QUERY:
Lee, Chun-Yi 46b2d4
+		if (flag == ACPI_EC_COMMAND_COMPLETE)
Lee, Chun-Yi 46b2d4
 			acpi_ec_close_event(ec);
Lee, Chun-Yi 46b2d4
-		if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT &&
Lee, Chun-Yi 46b2d4
-		    flag == ACPI_EC_COMMAND_COMPLETE)
Lee, Chun-Yi 46b2d4
-			set_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags);
Lee, Chun-Yi 46b2d4
+
Lee, Chun-Yi 46b2d4
+		return;
Lee, Chun-Yi 46b2d4
+
Lee, Chun-Yi 46b2d4
+	case ACPI_EC_EVT_TIMING_EVENT:
Lee, Chun-Yi 46b2d4
+		if (flag == ACPI_EC_COMMAND_COMPLETE)
Lee, Chun-Yi 46b2d4
+			acpi_ec_complete_event(ec);
Lee, Chun-Yi 46b2d4
 	}
Lee, Chun-Yi 46b2d4
 }
Lee, Chun-Yi 46b2d4
 
Lee, Chun-Yi 46b2d4
@@ -674,11 +688,9 @@ static bool advance_transaction(struct a
Lee, Chun-Yi 46b2d4
 	 */
Lee, Chun-Yi 46b2d4
 	if (!t || !(t->flags & ACPI_EC_COMMAND_POLL)) {
Lee, Chun-Yi 46b2d4
 		if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT &&
Lee, Chun-Yi 46b2d4
-		    (!ec->events_to_process ||
Lee, Chun-Yi 46b2d4
-		     test_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags))) {
Lee, Chun-Yi 46b2d4
-			clear_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags);
Lee, Chun-Yi 46b2d4
+		    ec->event_state == EC_EVENT_COMPLETE)
Lee, Chun-Yi 46b2d4
 			acpi_ec_close_event(ec);
Lee, Chun-Yi 46b2d4
-		}
Lee, Chun-Yi 46b2d4
+
Lee, Chun-Yi 46b2d4
 		if (!t)
Lee, Chun-Yi 46b2d4
 			goto out;
Lee, Chun-Yi 46b2d4
 	}
Lee, Chun-Yi 46b2d4
@@ -1246,8 +1258,9 @@ static void acpi_ec_event_handler(struct
Lee, Chun-Yi 46b2d4
 	 * event handling work again regardless of whether or not the query
Lee, Chun-Yi 46b2d4
 	 * queued up above is processed successfully.
Lee, Chun-Yi 46b2d4
 	 */
Lee, Chun-Yi 46b2d4
-	if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS ||
Lee, Chun-Yi 46b2d4
-	    ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY)
Lee, Chun-Yi 46b2d4
+	if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT)
Lee, Chun-Yi 46b2d4
+		acpi_ec_complete_event(ec);
Lee, Chun-Yi 46b2d4
+	else
Lee, Chun-Yi 46b2d4
 		acpi_ec_close_event(ec);
Lee, Chun-Yi 46b2d4
 
Lee, Chun-Yi 46b2d4
 	spin_unlock_irq(&ec->lock);
Lee, Chun-Yi 46b2d4
--- a/drivers/acpi/internal.h
Lee, Chun-Yi 46b2d4
+++ b/drivers/acpi/internal.h
Lee, Chun-Yi 46b2d4
@@ -166,6 +166,13 @@ static inline void acpi_early_processor_
Lee, Chun-Yi 46b2d4
 /* --------------------------------------------------------------------------
Lee, Chun-Yi 46b2d4
                                   Embedded Controller
Lee, Chun-Yi 46b2d4
    -------------------------------------------------------------------------- */
Lee, Chun-Yi 46b2d4
+
Lee, Chun-Yi 46b2d4
+enum acpi_ec_event_state {
Lee, Chun-Yi 46b2d4
+	EC_EVENT_READY = 0,	/* Event work can be submitted */
Lee, Chun-Yi 46b2d4
+	EC_EVENT_IN_PROGRESS,	/* Event work is pending or being processed */
Lee, Chun-Yi 46b2d4
+	EC_EVENT_COMPLETE,	/* Event work processing has completed */
Lee, Chun-Yi 46b2d4
+};
Lee, Chun-Yi 46b2d4
+
Lee, Chun-Yi 46b2d4
 struct acpi_ec {
Lee, Chun-Yi 46b2d4
 	acpi_handle handle;
Lee, Chun-Yi 46b2d4
 	int gpe;
Lee, Chun-Yi 46b2d4
@@ -182,6 +189,7 @@ struct acpi_ec {
Lee, Chun-Yi 46b2d4
 	spinlock_t lock;
Lee, Chun-Yi 46b2d4
 	struct work_struct work;
Lee, Chun-Yi 46b2d4
 	unsigned long timestamp;
Lee, Chun-Yi 46b2d4
+	enum acpi_ec_event_state event_state;
Lee, Chun-Yi 46b2d4
 	unsigned int events_to_process;
Lee, Chun-Yi 46b2d4
 	unsigned int events_in_progress;
Lee, Chun-Yi 46b2d4
 	unsigned int queries_in_progress;