Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 716bc413 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'acpi-ec'

* acpi-ec:
  ACPI / EC: Add GPE reference counting debugging messages
  ACPI / EC: Add query flushing support
  ACPI / EC: Refine command storm prevention support
  ACPI / EC: Add command flushing support.
  ACPI / EC: Introduce STARTED/STOPPED flags to replace BLOCKED flag
  ACPI / EC: Update revision due to raw handler mode.
  ACPI / EC: Reduce ec_poll() by referencing the last register access timestamp.
  ACPI / EC: Fix several GPE handling issues by deploying ACPI_GPE_DISPATCH_RAW_HANDLER mode.
  ACPI / EC: Cleanup QR_EC related code
  ACPI / EC: Fix issues related to the SCI_EVT handling
  ACPI / EC: Fix a code path that global lock is not held
  ACPI / EC: Fix returning values in acpi_ec_sync_query()
  ACPI / EC: Add reference counting for query handlers
  ACPI / EC: Cleanup transaction wakeup code
parents 55c39fc2 b5bca896
Loading
Loading
Loading
Loading
+417 −131
Original line number Diff line number Diff line
/*
 *  ec.c - ACPI Embedded Controller Driver (v2.2)
 *  ec.c - ACPI Embedded Controller Driver (v3)
 *
 *  Copyright (C) 2001-2014 Intel Corporation
 *    Author: 2014       Lv Zheng <lv.zheng@intel.com>
 *  Copyright (C) 2001-2015 Intel Corporation
 *    Author: 2014, 2015 Lv Zheng <lv.zheng@intel.com>
 *            2006, 2007 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
 *            2006       Denis Sadykov <denis.m.sadykov@intel.com>
 *            2004       Luming Yu <luming.yu@intel.com>
@@ -31,6 +31,7 @@

/* Uncomment next line to get verbose printout */
/* #define DEBUG */
#define DEBUG_REF 0
#define pr_fmt(fmt) "ACPI : EC: " fmt

#include <linux/kernel.h>
@@ -71,20 +72,32 @@ enum ec_command {
#define ACPI_EC_DELAY		500	/* Wait 500ms max. during EC ops */
#define ACPI_EC_UDELAY_GLK	1000	/* Wait 1ms max. to get global lock */
#define ACPI_EC_MSI_UDELAY	550	/* Wait 550us for MSI EC */
#define ACPI_EC_UDELAY_POLL	1000	/* Wait 1ms for EC transaction polling */
#define ACPI_EC_CLEAR_MAX	100	/* Maximum number of events to query
					 * when trying to clear the EC */

enum {
	EC_FLAGS_QUERY_PENDING,		/* Query is pending */
	EC_FLAGS_GPE_STORM,		/* GPE storm detected */
	EC_FLAGS_EVENT_ENABLED,		/* Event is enabled */
	EC_FLAGS_EVENT_PENDING,		/* Event is pending */
	EC_FLAGS_EVENT_DETECTED,	/* Event is 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 */
	EC_FLAGS_COMMAND_STORM,		/* GPE storms occurred to the
					 * current command processing */
};

#define ACPI_EC_COMMAND_POLL		0x01 /* Available for command byte */
#define ACPI_EC_COMMAND_COMPLETE	0x02 /* Completed last byte */

#define ec_debug_ref(ec, fmt, ...)					\
	do {								\
		if (DEBUG_REF)						\
			pr_debug("%lu: " fmt, ec->reference_count,	\
				 ## __VA_ARGS__);			\
	} while (0)

/* ec.c is compiled in acpi namespace so this shows up as acpi.ec_delay param */
static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY;
module_param(ec_delay, uint, 0644);
@@ -105,6 +118,7 @@ struct acpi_ec_query_handler {
	acpi_handle handle;
	void *data;
	u8 query_bit;
	struct kref kref;
};

struct transaction {
@@ -117,8 +131,12 @@ struct transaction {
	u8 wlen;
	u8 rlen;
	u8 flags;
	unsigned long timestamp;
};

static int acpi_ec_query(struct acpi_ec *ec, u8 *data);
static void advance_transaction(struct acpi_ec *ec);

struct acpi_ec *boot_ec, *first_ec;
EXPORT_SYMBOL(first_ec);

@@ -129,7 +147,28 @@ 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 */

/* --------------------------------------------------------------------------
 *                           Transaction Management
 *                           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);
}

static bool acpi_ec_flushed(struct acpi_ec *ec)
{
	return ec->reference_count == 1;
}

static bool acpi_ec_has_pending_event(struct acpi_ec *ec)
{
	return test_bit(EC_FLAGS_EVENT_DETECTED, &ec->flags) ||
	       test_bit(EC_FLAGS_EVENT_PENDING, &ec->flags);
}

/* --------------------------------------------------------------------------
 *                           EC Registers
 * -------------------------------------------------------------------------- */

static inline u8 acpi_ec_read_status(struct acpi_ec *ec)
@@ -151,6 +190,7 @@ static inline u8 acpi_ec_read_data(struct acpi_ec *ec)
{
	u8 x = inb(ec->data_addr);

	ec->curr->timestamp = jiffies;
	pr_debug("EC_DATA(R) = 0x%2.2x\n", x);
	return x;
}
@@ -159,12 +199,14 @@ static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command)
{
	pr_debug("EC_SC(W) = 0x%2.2x\n", command);
	outb(command, ec->command_addr);
	ec->curr->timestamp = jiffies;
}

static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data)
{
	pr_debug("EC_DATA(W) = 0x%2.2x\n", data);
	outb(data, ec->data_addr);
	ec->curr->timestamp = jiffies;
}

#ifdef DEBUG
@@ -188,6 +230,203 @@ static const char *acpi_ec_cmd_string(u8 cmd)
#define acpi_ec_cmd_string(cmd)		"UNDEF"
#endif

/* --------------------------------------------------------------------------
 *                           GPE Registers
 * -------------------------------------------------------------------------- */

static inline bool acpi_ec_is_gpe_raised(struct acpi_ec *ec)
{
	acpi_event_status gpe_status = 0;

	(void)acpi_get_gpe_status(NULL, ec->gpe, &gpe_status);
	return (gpe_status & ACPI_EVENT_FLAG_SET) ? true : false;
}

static inline void acpi_ec_enable_gpe(struct acpi_ec *ec, bool open)
{
	if (open)
		acpi_enable_gpe(NULL, ec->gpe);
	else {
		BUG_ON(ec->reference_count < 1);
		acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE);
	}
	if (acpi_ec_is_gpe_raised(ec)) {
		/*
		 * On some platforms, EN=1 writes cannot trigger GPE. So
		 * software need to manually trigger a pseudo GPE event on
		 * EN=1 writes.
		 */
		pr_debug("***** Polling quirk *****\n");
		advance_transaction(ec);
	}
}

static inline void acpi_ec_disable_gpe(struct acpi_ec *ec, bool close)
{
	if (close)
		acpi_disable_gpe(NULL, ec->gpe);
	else {
		BUG_ON(ec->reference_count < 1);
		acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE);
	}
}

static inline void acpi_ec_clear_gpe(struct acpi_ec *ec)
{
	/*
	 * GPE STS is a W1C register, which means:
	 * 1. Software can clear it without worrying about clearing other
	 *    GPEs' STS bits when the hardware sets them in parallel.
	 * 2. As long as software can ensure only clearing it when it is
	 *    set, hardware won't set it in parallel.
	 * So software can clear GPE in any contexts.
	 * Warning: do not move the check into advance_transaction() as the
	 * EC commands will be sent without GPE raised.
	 */
	if (!acpi_ec_is_gpe_raised(ec))
		return;
	acpi_clear_gpe(NULL, ec->gpe);
}

/* --------------------------------------------------------------------------
 *                           Transaction Management
 * -------------------------------------------------------------------------- */

static void acpi_ec_submit_request(struct acpi_ec *ec)
{
	ec->reference_count++;
	if (ec->reference_count == 1)
		acpi_ec_enable_gpe(ec, true);
}

static void acpi_ec_complete_request(struct acpi_ec *ec)
{
	bool flushed = false;

	ec->reference_count--;
	if (ec->reference_count == 0)
		acpi_ec_disable_gpe(ec, true);
	flushed = acpi_ec_flushed(ec);
	if (flushed)
		wake_up(&ec->wait);
}

static void acpi_ec_set_storm(struct acpi_ec *ec, u8 flag)
{
	if (!test_bit(flag, &ec->flags)) {
		acpi_ec_disable_gpe(ec, false);
		pr_debug("+++++ Polling enabled +++++\n");
		set_bit(flag, &ec->flags);
	}
}

static void acpi_ec_clear_storm(struct acpi_ec *ec, u8 flag)
{
	if (test_bit(flag, &ec->flags)) {
		clear_bit(flag, &ec->flags);
		acpi_ec_enable_gpe(ec, false);
		pr_debug("+++++ Polling disabled +++++\n");
	}
}

/*
 * acpi_ec_submit_flushable_request() - Increase the reference count unless
 *                                      the flush operation is not in
 *                                      progress
 * @ec: the EC device
 * @allow_event: whether event should be handled
 *
 * This function must be used before taking a new action that should hold
 * the reference count.  If this function returns false, then the action
 * must be discarded or it will prevent the flush operation from being
 * completed.
 *
 * During flushing, QR_EC command need to pass this check when there is a
 * pending event, so that the reference count held for the pending event
 * can be decreased by the completion of the QR_EC command.
 */
static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec,
					     bool allow_event)
{
	if (!acpi_ec_started(ec)) {
		if (!allow_event || !acpi_ec_has_pending_event(ec))
			return false;
	}
	acpi_ec_submit_request(ec);
	return true;
}

static void acpi_ec_submit_event(struct acpi_ec *ec)
{
	if (!test_bit(EC_FLAGS_EVENT_DETECTED, &ec->flags) ||
	    !test_bit(EC_FLAGS_EVENT_ENABLED, &ec->flags))
		return;
	/* Hold reference for pending event */
	if (!acpi_ec_submit_flushable_request(ec, true))
		return;
	ec_debug_ref(ec, "Increase event\n");
	if (!test_and_set_bit(EC_FLAGS_EVENT_PENDING, &ec->flags)) {
		pr_debug("***** Event query started *****\n");
		schedule_work(&ec->work);
		return;
	}
	acpi_ec_complete_request(ec);
	ec_debug_ref(ec, "Decrease event\n");
}

static void acpi_ec_complete_event(struct acpi_ec *ec)
{
	if (ec->curr->command == ACPI_EC_COMMAND_QUERY) {
		clear_bit(EC_FLAGS_EVENT_PENDING, &ec->flags);
		pr_debug("***** Event query stopped *****\n");
		/* Unhold reference for pending event */
		acpi_ec_complete_request(ec);
		ec_debug_ref(ec, "Decrease event\n");
		/* Check if there is another SCI_EVT detected */
		acpi_ec_submit_event(ec);
	}
}

static void acpi_ec_submit_detection(struct acpi_ec *ec)
{
	/* Hold reference for query submission */
	if (!acpi_ec_submit_flushable_request(ec, false))
		return;
	ec_debug_ref(ec, "Increase query\n");
	if (!test_and_set_bit(EC_FLAGS_EVENT_DETECTED, &ec->flags)) {
		pr_debug("***** Event detection blocked *****\n");
		acpi_ec_submit_event(ec);
		return;
	}
	acpi_ec_complete_request(ec);
	ec_debug_ref(ec, "Decrease query\n");
}

static void acpi_ec_complete_detection(struct acpi_ec *ec)
{
	if (ec->curr->command == ACPI_EC_COMMAND_QUERY) {
		clear_bit(EC_FLAGS_EVENT_DETECTED, &ec->flags);
		pr_debug("***** Event detetion unblocked *****\n");
		/* Unhold reference for query submission */
		acpi_ec_complete_request(ec);
		ec_debug_ref(ec, "Decrease query\n");
	}
}

static void acpi_ec_enable_event(struct acpi_ec *ec)
{
	unsigned long flags;

	spin_lock_irqsave(&ec->lock, flags);
	set_bit(EC_FLAGS_EVENT_ENABLED, &ec->flags);
	/*
	 * An event may be pending even with SCI_EVT=0, so QR_EC should
	 * always be issued right after started.
	 */
	acpi_ec_submit_detection(ec);
	spin_unlock_irqrestore(&ec->lock, flags);
}

static int ec_transaction_completed(struct acpi_ec *ec)
{
	unsigned long flags;
@@ -200,7 +439,7 @@ static int ec_transaction_completed(struct acpi_ec *ec)
	return ret;
}

static bool advance_transaction(struct acpi_ec *ec)
static void advance_transaction(struct acpi_ec *ec)
{
	struct transaction *t;
	u8 status;
@@ -208,6 +447,12 @@ static bool advance_transaction(struct acpi_ec *ec)

	pr_debug("===== %s (%d) =====\n",
		 in_interrupt() ? "IRQ" : "TASK", smp_processor_id());
	/*
	 * By always clearing STS before handling all indications, we can
	 * ensure a hardware STS 0->1 change after this clearing can always
	 * trigger a GPE interrupt.
	 */
	acpi_ec_clear_gpe(ec);
	status = acpi_ec_read_status(ec);
	t = ec->curr;
	if (!t)
@@ -223,6 +468,7 @@ static bool advance_transaction(struct acpi_ec *ec)
				t->rdata[t->ri++] = acpi_ec_read_data(ec);
				if (t->rlen == t->ri) {
					t->flags |= ACPI_EC_COMMAND_COMPLETE;
					acpi_ec_complete_event(ec);
					if (t->command == ACPI_EC_COMMAND_QUERY)
						pr_debug("***** Command(%s) hardware completion *****\n",
							 acpi_ec_cmd_string(t->command));
@@ -233,25 +479,29 @@ static bool advance_transaction(struct acpi_ec *ec)
		} else if (t->wlen == t->wi &&
			   (status & ACPI_EC_FLAG_IBF) == 0) {
			t->flags |= ACPI_EC_COMMAND_COMPLETE;
			acpi_ec_complete_event(ec);
			wakeup = true;
		}
		return wakeup;
		goto out;
	} else {
		if (EC_FLAGS_QUERY_HANDSHAKE &&
		    !(status & ACPI_EC_FLAG_SCI) &&
		    (t->command == ACPI_EC_COMMAND_QUERY)) {
			t->flags |= ACPI_EC_COMMAND_POLL;
			acpi_ec_complete_detection(ec);
			t->rdata[t->ri++] = 0x00;
			t->flags |= ACPI_EC_COMMAND_COMPLETE;
			acpi_ec_complete_event(ec);
			pr_debug("***** Command(%s) software completion *****\n",
				 acpi_ec_cmd_string(t->command));
			wakeup = true;
		} else if ((status & ACPI_EC_FLAG_IBF) == 0) {
			acpi_ec_write_cmd(ec, t->command);
			t->flags |= ACPI_EC_COMMAND_POLL;
			acpi_ec_complete_detection(ec);
		} else
			goto err;
		return wakeup;
		goto out;
	}
err:
	/*
@@ -259,28 +509,27 @@ static bool advance_transaction(struct acpi_ec *ec)
	 * otherwise will take a not handled IRQ as a false one.
	 */
	if (!(status & ACPI_EC_FLAG_SCI)) {
		if (in_interrupt() && t)
		if (in_interrupt() && t) {
			if (t->irq_count < ec_storm_threshold)
				++t->irq_count;
			/* Allow triggering on 0 threshold */
			if (t->irq_count == ec_storm_threshold)
				acpi_ec_set_storm(ec, EC_FLAGS_COMMAND_STORM);
		}
	return wakeup;
	}
out:
	if (status & ACPI_EC_FLAG_SCI)
		acpi_ec_submit_detection(ec);
	if (wakeup && in_interrupt())
		wake_up(&ec->wait);
}

static void start_transaction(struct acpi_ec *ec)
{
	ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0;
	ec->curr->flags = 0;
	(void)advance_transaction(ec);
}

static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data);

static int ec_check_sci_sync(struct acpi_ec *ec, u8 state)
{
	if (state & ACPI_EC_FLAG_SCI) {
		if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags))
			return acpi_ec_sync_query(ec, NULL);
	}
	return 0;
	ec->curr->timestamp = jiffies;
	advance_transaction(ec);
}

static int ec_poll(struct acpi_ec *ec)
@@ -291,20 +540,25 @@ static int ec_poll(struct acpi_ec *ec)
	while (repeat--) {
		unsigned long delay = jiffies +
			msecs_to_jiffies(ec_delay);
		unsigned long usecs = ACPI_EC_UDELAY_POLL;
		do {
			/* don't sleep with disabled interrupts */
			if (EC_FLAGS_MSI || irqs_disabled()) {
				udelay(ACPI_EC_MSI_UDELAY);
				usecs = ACPI_EC_MSI_UDELAY;
				udelay(usecs);
				if (ec_transaction_completed(ec))
					return 0;
			} else {
				if (wait_event_timeout(ec->wait,
						ec_transaction_completed(ec),
						msecs_to_jiffies(1)))
						usecs_to_jiffies(usecs)))
					return 0;
			}
			spin_lock_irqsave(&ec->lock, flags);
			(void)advance_transaction(ec);
			if (time_after(jiffies,
					ec->curr->timestamp +
					usecs_to_jiffies(usecs)))
				advance_transaction(ec);
			spin_unlock_irqrestore(&ec->lock, flags);
		} while (time_before(jiffies, delay));
		pr_debug("controller reset, restart transaction\n");
@@ -325,21 +579,29 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
		udelay(ACPI_EC_MSI_UDELAY);
	/* start transaction */
	spin_lock_irqsave(&ec->lock, tmp);
	/* Enable GPE for command processing (IBF=0/OBF=1) */
	if (!acpi_ec_submit_flushable_request(ec, true)) {
		ret = -EINVAL;
		goto unlock;
	}
	ec_debug_ref(ec, "Increase command\n");
	/* following two actions should be kept atomic */
	ec->curr = t;
	pr_debug("***** Command(%s) started *****\n",
		 acpi_ec_cmd_string(t->command));
	start_transaction(ec);
	if (ec->curr->command == ACPI_EC_COMMAND_QUERY) {
		clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
		pr_debug("***** Event stopped *****\n");
	}
	spin_unlock_irqrestore(&ec->lock, tmp);
	ret = ec_poll(ec);
	spin_lock_irqsave(&ec->lock, tmp);
	if (t->irq_count == ec_storm_threshold)
		acpi_ec_clear_storm(ec, EC_FLAGS_COMMAND_STORM);
	pr_debug("***** Command(%s) stopped *****\n",
		 acpi_ec_cmd_string(t->command));
	ec->curr = NULL;
	/* Disable GPE for command processing (IBF=0/OBF=1) */
	acpi_ec_complete_request(ec);
	ec_debug_ref(ec, "Decrease command\n");
unlock:
	spin_unlock_irqrestore(&ec->lock, tmp);
	return ret;
}
@@ -354,10 +616,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)) {
@@ -365,26 +623,11 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
			goto unlock;
		}
	}
	/* disable GPE during transaction if storm is detected */
	if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) {
		/* It has to be disabled, so that it doesn't trigger. */
		acpi_disable_gpe(NULL, ec->gpe);
	}

	status = acpi_ec_transaction_unlocked(ec, t);

	/* check if we received SCI during transaction */
	ec_check_sci_sync(ec, acpi_ec_read_status(ec));
	if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) {
	if (test_bit(EC_FLAGS_COMMAND_STORM, &ec->flags))
		msleep(1);
		/* It is safe to enable the GPE outside of the transaction. */
		acpi_enable_gpe(NULL, ec->gpe);
	} else if (t->irq_count > ec_storm_threshold) {
		pr_info("GPE storm detected(%d GPEs), "
			"transactions will use polling mode\n",
			t->irq_count);
		set_bit(EC_FLAGS_GPE_STORM, &ec->flags);
	}
	if (ec->global_lock)
		acpi_release_global_lock(glk);
unlock:
@@ -500,7 +743,7 @@ static void acpi_ec_clear(struct acpi_ec *ec)
	u8 value = 0;

	for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) {
		status = acpi_ec_sync_query(ec, &value);
		status = acpi_ec_query(ec, &value);
		if (status || !value)
			break;
	}
@@ -511,6 +754,57 @@ 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");
		/* Enable GPE for event processing (SCI_EVT=1) */
		if (!resuming) {
			acpi_ec_submit_request(ec);
			ec_debug_ref(ec, "Increase driver\n");
		}
		pr_info("+++++ EC started +++++\n");
	}
	spin_unlock_irqrestore(&ec->lock, flags);
}

static bool acpi_ec_stopped(struct acpi_ec *ec)
{
	unsigned long flags;
	bool flushed;

	spin_lock_irqsave(&ec->lock, flags);
	flushed = acpi_ec_flushed(ec);
	spin_unlock_irqrestore(&ec->lock, flags);
	return flushed;
}

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);
		spin_unlock_irqrestore(&ec->lock, flags);
		wait_event(ec->wait, acpi_ec_stopped(ec));
		spin_lock_irqsave(&ec->lock, flags);
		/* Disable GPE for event processing (SCI_EVT=1) */
		if (!suspending) {
			acpi_ec_complete_request(ec);
			ec_debug_ref(ec, "Decrease driver\n");
		}
		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;
@@ -520,7 +814,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);
}

@@ -531,14 +825,11 @@ void acpi_ec_unblock_transactions(void)
	if (!ec)
		return;

	mutex_lock(&ec->mutex);
	/* 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);

	mutex_unlock(&ec->mutex);
}

void acpi_ec_unblock_transactions_early(void)
@@ -548,36 +839,33 @@ 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);
}

static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 *data)
/* --------------------------------------------------------------------------
                                Event Management
   -------------------------------------------------------------------------- */
static struct acpi_ec_query_handler *
acpi_ec_get_query_handler(struct acpi_ec_query_handler *handler)
{
	int result;
	u8 d;
	struct transaction t = {.command = ACPI_EC_COMMAND_QUERY,
				.wdata = NULL, .rdata = &d,
				.wlen = 0, .rlen = 1};
	if (handler)
		kref_get(&handler->kref);
	return handler;
}

	if (!ec || !data)
		return -EINVAL;
	/*
	 * Query the EC to find out which _Qxx method we need to evaluate.
	 * Note that successful completion of the query causes the ACPI_EC_SCI
	 * bit to be cleared (and thus clearing the interrupt source).
	 */
	result = acpi_ec_transaction_unlocked(ec, &t);
	if (result)
		return result;
	if (!d)
		return -ENODATA;
	*data = d;
	return 0;
static void acpi_ec_query_handler_release(struct kref *kref)
{
	struct acpi_ec_query_handler *handler =
		container_of(kref, struct acpi_ec_query_handler, kref);

	kfree(handler);
}

static void acpi_ec_put_query_handler(struct acpi_ec_query_handler *handler)
{
	kref_put(&handler->kref, acpi_ec_query_handler_release);
}

/* --------------------------------------------------------------------------
                                Event Management
   -------------------------------------------------------------------------- */
int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
			      acpi_handle handle, acpi_ec_query_func func,
			      void *data)
@@ -593,6 +881,7 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
	handler->func = func;
	handler->data = data;
	mutex_lock(&ec->mutex);
	kref_init(&handler->kref);
	list_add(&handler->node, &ec->list);
	mutex_unlock(&ec->mutex);
	return 0;
@@ -602,15 +891,18 @@ EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler);
void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit)
{
	struct acpi_ec_query_handler *handler, *tmp;
	LIST_HEAD(free_list);

	mutex_lock(&ec->mutex);
	list_for_each_entry_safe(handler, tmp, &ec->list, node) {
		if (query_bit == handler->query_bit) {
			list_del(&handler->node);
			kfree(handler);
			list_del_init(&handler->node);
			list_add(&handler->node, &free_list);
		}
	}
	mutex_unlock(&ec->mutex);
	list_for_each_entry(handler, &free_list, node)
		acpi_ec_put_query_handler(handler);
}
EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler);

@@ -626,59 +918,58 @@ static void acpi_ec_run(void *cxt)
	else if (handler->handle)
		acpi_evaluate_object(handler->handle, NULL, NULL, NULL);
	pr_debug("##### Query(0x%02x) stopped #####\n", handler->query_bit);
	kfree(handler);
	acpi_ec_put_query_handler(handler);
}

static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data)
static int acpi_ec_query(struct acpi_ec *ec, u8 *data)
{
	u8 value = 0;
	int status;
	struct acpi_ec_query_handler *handler, *copy;
	int result;
	acpi_status status;
	struct acpi_ec_query_handler *handler;
	struct transaction t = {.command = ACPI_EC_COMMAND_QUERY,
				.wdata = NULL, .rdata = &value,
				.wlen = 0, .rlen = 1};

	status = acpi_ec_query_unlocked(ec, &value);
	/*
	 * Query the EC to find out which _Qxx method we need to evaluate.
	 * Note that successful completion of the query causes the ACPI_EC_SCI
	 * bit to be cleared (and thus clearing the interrupt source).
	 */
	result = acpi_ec_transaction(ec, &t);
	if (result)
		return result;
	if (data)
		*data = value;
	if (status)
		return status;
	if (!value)
		return -ENODATA;

	mutex_lock(&ec->mutex);
	list_for_each_entry(handler, &ec->list, node) {
		if (value == handler->query_bit) {
			/* have custom handler for this bit */
			copy = kmalloc(sizeof(*handler), GFP_KERNEL);
			if (!copy)
				return -ENOMEM;
			memcpy(copy, handler, sizeof(*copy));
			handler = acpi_ec_get_query_handler(handler);
			pr_debug("##### Query(0x%02x) scheduled #####\n",
				 handler->query_bit);
			return acpi_os_execute((copy->func) ?
			status = acpi_os_execute((handler->func) ?
				OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER,
				acpi_ec_run, copy);
		}
				acpi_ec_run, handler);
			if (ACPI_FAILURE(status))
				result = -EBUSY;
			break;
		}
	return 0;
	}

static void acpi_ec_gpe_query(void *ec_cxt)
{
	struct acpi_ec *ec = ec_cxt;

	if (!ec)
		return;
	mutex_lock(&ec->mutex);
	acpi_ec_sync_query(ec, NULL);
	mutex_unlock(&ec->mutex);
	return result;
}

static int ec_check_sci(struct acpi_ec *ec, u8 state)
static void acpi_ec_gpe_poller(struct work_struct *work)
{
	if (state & ACPI_EC_FLAG_SCI) {
		if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
			pr_debug("***** Event started *****\n");
			return acpi_os_execute(OSL_NOTIFY_HANDLER,
				acpi_ec_gpe_query, ec);
		}
	}
	return 0;
	struct acpi_ec *ec = container_of(work, struct acpi_ec, work);

	pr_debug("***** Event poller started *****\n");
	acpi_ec_query(ec, NULL);
	pr_debug("***** Event poller stopped *****\n");
}

static u32 acpi_ec_gpe_handler(acpi_handle gpe_device,
@@ -688,11 +979,9 @@ static u32 acpi_ec_gpe_handler(acpi_handle gpe_device,
	struct acpi_ec *ec = data;

	spin_lock_irqsave(&ec->lock, flags);
	if (advance_transaction(ec))
		wake_up(&ec->wait);
	advance_transaction(ec);
	spin_unlock_irqrestore(&ec->lock, flags);
	ec_check_sci(ec, acpi_ec_read_status(ec));
	return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
	return ACPI_INTERRUPT_HANDLED;
}

/* --------------------------------------------------------------------------
@@ -750,11 +1039,11 @@ static struct acpi_ec *make_acpi_ec(void)

	if (!ec)
		return NULL;
	ec->flags = 1 << EC_FLAGS_QUERY_PENDING;
	mutex_init(&ec->mutex);
	init_waitqueue_head(&ec->wait);
	INIT_LIST_HEAD(&ec->list);
	spin_lock_init(&ec->lock);
	INIT_WORK(&ec->work, acpi_ec_gpe_poller);
	return ec;
}

@@ -810,13 +1099,13 @@ static int ec_install_handlers(struct acpi_ec *ec)

	if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags))
		return 0;
	status = acpi_install_gpe_handler(NULL, ec->gpe,
	status = acpi_install_gpe_raw_handler(NULL, ec->gpe,
				  ACPI_GPE_EDGE_TRIGGERED,
				  &acpi_ec_gpe_handler, ec);
	if (ACPI_FAILURE(status))
		return -ENODEV;

	acpi_enable_gpe(NULL, ec->gpe);
	acpi_ec_start(ec, false);
	status = acpi_install_address_space_handler(ec->handle,
						    ACPI_ADR_SPACE_EC,
						    &acpi_ec_space_handler,
@@ -831,7 +1120,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_disable_gpe(NULL, ec->gpe);
			acpi_ec_stop(ec, false);
			acpi_remove_gpe_handler(NULL, ec->gpe,
				&acpi_ec_gpe_handler);
			return -ENODEV;
@@ -846,7 +1135,7 @@ static void ec_remove_handlers(struct acpi_ec *ec)
{
	if (!test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags))
		return;
	acpi_disable_gpe(NULL, ec->gpe);
	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");
@@ -900,14 +1189,11 @@ static int acpi_ec_add(struct acpi_device *device)
	ret = ec_install_handlers(ec);

	/* EC is fully operational, allow queries */
	clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
	acpi_ec_enable_event(ec);

	/* Clear stale _Q events if hardware might require that */
	if (EC_FLAGS_CLEAR_ON_RESUME) {
		mutex_lock(&ec->mutex);
	if (EC_FLAGS_CLEAR_ON_RESUME)
		acpi_ec_clear(ec);
		mutex_unlock(&ec->mutex);
	}
	return ret;
}

+2 −0

File changed.

Preview size limit exceeded, changes collapsed.