|
@@ -80,7 +80,8 @@ enum {
|
|
|
EC_FLAGS_GPE_STORM, /* GPE storm detected */
|
|
|
EC_FLAGS_HANDLERS_INSTALLED, /* Handlers for GPE and
|
|
|
* OpReg are installed */
|
|
|
- EC_FLAGS_BLOCKED, /* Transactions are blocked */
|
|
|
+ EC_FLAGS_STARTED, /* Driver is started */
|
|
|
+ EC_FLAGS_STOPPED, /* Driver is stopped */
|
|
|
};
|
|
|
|
|
|
#define ACPI_EC_COMMAND_POLL 0x01 /* Available for command byte */
|
|
@@ -134,6 +135,16 @@ static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */
|
|
|
static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */
|
|
|
static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */
|
|
|
|
|
|
+/* --------------------------------------------------------------------------
|
|
|
+ * Device Flags
|
|
|
+ * -------------------------------------------------------------------------- */
|
|
|
+
|
|
|
+static bool acpi_ec_started(struct acpi_ec *ec)
|
|
|
+{
|
|
|
+ return test_bit(EC_FLAGS_STARTED, &ec->flags) &&
|
|
|
+ !test_bit(EC_FLAGS_STOPPED, &ec->flags);
|
|
|
+}
|
|
|
+
|
|
|
/* --------------------------------------------------------------------------
|
|
|
* EC Registers
|
|
|
* -------------------------------------------------------------------------- */
|
|
@@ -415,6 +426,10 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
|
|
|
udelay(ACPI_EC_MSI_UDELAY);
|
|
|
/* start transaction */
|
|
|
spin_lock_irqsave(&ec->lock, tmp);
|
|
|
+ if (!acpi_ec_started(ec)) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
/* following two actions should be kept atomic */
|
|
|
ec->curr = t;
|
|
|
pr_debug("***** Command(%s) started *****\n",
|
|
@@ -426,6 +441,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
|
|
|
pr_debug("***** Command(%s) stopped *****\n",
|
|
|
acpi_ec_cmd_string(t->command));
|
|
|
ec->curr = NULL;
|
|
|
+unlock:
|
|
|
spin_unlock_irqrestore(&ec->lock, tmp);
|
|
|
return ret;
|
|
|
}
|
|
@@ -440,10 +456,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
|
|
|
if (t->rdata)
|
|
|
memset(t->rdata, 0, t->rlen);
|
|
|
mutex_lock(&ec->mutex);
|
|
|
- if (test_bit(EC_FLAGS_BLOCKED, &ec->flags)) {
|
|
|
- status = -EINVAL;
|
|
|
- goto unlock;
|
|
|
- }
|
|
|
if (ec->global_lock) {
|
|
|
status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
|
|
|
if (ACPI_FAILURE(status)) {
|
|
@@ -595,6 +607,37 @@ static void acpi_ec_clear(struct acpi_ec *ec)
|
|
|
pr_info("%d stale EC events cleared\n", i);
|
|
|
}
|
|
|
|
|
|
+static void acpi_ec_start(struct acpi_ec *ec, bool resuming)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&ec->lock, flags);
|
|
|
+ if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) {
|
|
|
+ pr_debug("+++++ Starting EC +++++\n");
|
|
|
+ if (!resuming)
|
|
|
+ acpi_ec_enable_gpe(ec, true);
|
|
|
+ pr_info("+++++ EC started +++++\n");
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&ec->lock, flags);
|
|
|
+}
|
|
|
+
|
|
|
+static void acpi_ec_stop(struct acpi_ec *ec, bool suspending)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&ec->lock, flags);
|
|
|
+ if (acpi_ec_started(ec)) {
|
|
|
+ pr_debug("+++++ Stopping EC +++++\n");
|
|
|
+ set_bit(EC_FLAGS_STOPPED, &ec->flags);
|
|
|
+ if (!suspending)
|
|
|
+ acpi_ec_disable_gpe(ec, true);
|
|
|
+ clear_bit(EC_FLAGS_STARTED, &ec->flags);
|
|
|
+ clear_bit(EC_FLAGS_STOPPED, &ec->flags);
|
|
|
+ pr_info("+++++ EC stopped +++++\n");
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&ec->lock, flags);
|
|
|
+}
|
|
|
+
|
|
|
void acpi_ec_block_transactions(void)
|
|
|
{
|
|
|
struct acpi_ec *ec = first_ec;
|
|
@@ -604,7 +647,7 @@ void acpi_ec_block_transactions(void)
|
|
|
|
|
|
mutex_lock(&ec->mutex);
|
|
|
/* Prevent transactions from being carried out */
|
|
|
- set_bit(EC_FLAGS_BLOCKED, &ec->flags);
|
|
|
+ acpi_ec_stop(ec, true);
|
|
|
mutex_unlock(&ec->mutex);
|
|
|
}
|
|
|
|
|
@@ -616,7 +659,7 @@ void acpi_ec_unblock_transactions(void)
|
|
|
return;
|
|
|
|
|
|
/* Allow transactions to be carried out again */
|
|
|
- clear_bit(EC_FLAGS_BLOCKED, &ec->flags);
|
|
|
+ acpi_ec_start(ec, true);
|
|
|
|
|
|
if (EC_FLAGS_CLEAR_ON_RESUME)
|
|
|
acpi_ec_clear(ec);
|
|
@@ -629,7 +672,7 @@ void acpi_ec_unblock_transactions_early(void)
|
|
|
* atomic context during wakeup, so we don't need to acquire the mutex).
|
|
|
*/
|
|
|
if (first_ec)
|
|
|
- clear_bit(EC_FLAGS_BLOCKED, &first_ec->flags);
|
|
|
+ acpi_ec_start(first_ec, true);
|
|
|
}
|
|
|
|
|
|
/* --------------------------------------------------------------------------
|
|
@@ -894,7 +937,7 @@ static int ec_install_handlers(struct acpi_ec *ec)
|
|
|
if (ACPI_FAILURE(status))
|
|
|
return -ENODEV;
|
|
|
|
|
|
- acpi_ec_enable_gpe(ec, true);
|
|
|
+ acpi_ec_start(ec, false);
|
|
|
status = acpi_install_address_space_handler(ec->handle,
|
|
|
ACPI_ADR_SPACE_EC,
|
|
|
&acpi_ec_space_handler,
|
|
@@ -909,7 +952,7 @@ static int ec_install_handlers(struct acpi_ec *ec)
|
|
|
pr_err("Fail in evaluating the _REG object"
|
|
|
" of EC device. Broken bios is suspected.\n");
|
|
|
} else {
|
|
|
- acpi_ec_disable_gpe(ec, true);
|
|
|
+ acpi_ec_stop(ec, false);
|
|
|
acpi_remove_gpe_handler(NULL, ec->gpe,
|
|
|
&acpi_ec_gpe_handler);
|
|
|
return -ENODEV;
|
|
@@ -924,7 +967,7 @@ static void ec_remove_handlers(struct acpi_ec *ec)
|
|
|
{
|
|
|
if (!test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags))
|
|
|
return;
|
|
|
- acpi_ec_disable_gpe(ec, true);
|
|
|
+ acpi_ec_stop(ec, false);
|
|
|
if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle,
|
|
|
ACPI_ADR_SPACE_EC, &acpi_ec_space_handler)))
|
|
|
pr_err("failed to remove space handler\n");
|