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

Commit 0b80a8ff authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'acpi-ec'

* acpi-ec:
  ACPI / EC: Free saved_ec on error exit path
  ACPI / EC: Add detailed fields debugging support of EC_SC(R).
  ACPI / EC: Update revision due to recent changes
  ACPI / EC: Fix race condition in ec_transaction_completed()
  ACPI / EC: Remove duplicated ec_wait_ibf0() waiter
  ACPI / EC: Add asynchronous command byte write support
  ACPI / EC: Avoid race condition related to advance_transaction()
parents 4488c99b ed4b197d
Loading
Loading
Loading
Loading
+85 −79
Original line number Diff line number Diff line
/*
 *  ec.c - ACPI Embedded Controller Driver (v2.1)
 *  ec.c - ACPI Embedded Controller Driver (v2.2)
 *
 *  Copyright (C) 2006-2008 Alexey Starikovskiy <astarikovskiy@suse.de>
 *  Copyright (C) 2006 Denis Sadykov <denis.m.sadykov@intel.com>
 *  Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 *  Copyright (C) 2001-2014 Intel Corporation
 *    Author: 2014       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>
 *            2001, 2002 Andy Grover <andrew.grover@intel.com>
 *            2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 *  Copyright (C) 2008      Alexey Starikovskiy <astarikovskiy@suse.de>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
@@ -52,6 +55,7 @@
/* EC status register */
#define ACPI_EC_FLAG_OBF	0x01	/* Output buffer full */
#define ACPI_EC_FLAG_IBF	0x02	/* Input buffer full */
#define ACPI_EC_FLAG_CMD	0x08	/* Input buffer contains a command */
#define ACPI_EC_FLAG_BURST	0x10	/* burst mode */
#define ACPI_EC_FLAG_SCI	0x20	/* EC-SCI occurred */

@@ -78,6 +82,9 @@ enum {
	EC_FLAGS_BLOCKED,		/* Transactions are blocked */
};

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

/* 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);
@@ -109,7 +116,7 @@ struct transaction {
	u8 ri;
	u8 wlen;
	u8 rlen;
	bool done;
	u8 flags;
};

struct acpi_ec *boot_ec, *first_ec;
@@ -127,83 +134,104 @@ static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */
static inline u8 acpi_ec_read_status(struct acpi_ec *ec)
{
	u8 x = inb(ec->command_addr);
	pr_debug("---> status = 0x%2.2x\n", x);
	pr_debug("EC_SC(R) = 0x%2.2x "
		 "SCI_EVT=%d BURST=%d CMD=%d IBF=%d OBF=%d\n",
		 x,
		 !!(x & ACPI_EC_FLAG_SCI),
		 !!(x & ACPI_EC_FLAG_BURST),
		 !!(x & ACPI_EC_FLAG_CMD),
		 !!(x & ACPI_EC_FLAG_IBF),
		 !!(x & ACPI_EC_FLAG_OBF));
	return x;
}

static inline u8 acpi_ec_read_data(struct acpi_ec *ec)
{
	u8 x = inb(ec->data_addr);
	pr_debug("---> data = 0x%2.2x\n", x);
	pr_debug("EC_DATA(R) = 0x%2.2x\n", x);
	return x;
}

static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command)
{
	pr_debug("<--- command = 0x%2.2x\n", command);
	pr_debug("EC_SC(W) = 0x%2.2x\n", command);
	outb(command, ec->command_addr);
}

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

static int ec_transaction_done(struct acpi_ec *ec)
static int ec_transaction_completed(struct acpi_ec *ec)
{
	unsigned long flags;
	int ret = 0;
	spin_lock_irqsave(&ec->lock, flags);
	if (!ec->curr || ec->curr->done)
	if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE))
		ret = 1;
	spin_unlock_irqrestore(&ec->lock, flags);
	return ret;
}

static void start_transaction(struct acpi_ec *ec)
{
	ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0;
	ec->curr->done = false;
	acpi_ec_write_cmd(ec, ec->curr->command);
}

static void advance_transaction(struct acpi_ec *ec, u8 status)
static bool advance_transaction(struct acpi_ec *ec)
{
	unsigned long flags;
	struct transaction *t;
	u8 status;
	bool wakeup = false;

	spin_lock_irqsave(&ec->lock, flags);
	pr_debug("===== %s =====\n", in_interrupt() ? "IRQ" : "TASK");
	status = acpi_ec_read_status(ec);
	t = ec->curr;
	if (!t)
		goto unlock;
		goto err;
	if (t->flags & ACPI_EC_COMMAND_POLL) {
		if (t->wlen > t->wi) {
			if ((status & ACPI_EC_FLAG_IBF) == 0)
			acpi_ec_write_data(ec,
				t->wdata[t->wi++]);
				acpi_ec_write_data(ec, t->wdata[t->wi++]);
			else
				goto err;
		} else if (t->rlen > t->ri) {
			if ((status & ACPI_EC_FLAG_OBF) == 1) {
				t->rdata[t->ri++] = acpi_ec_read_data(ec);
			if (t->rlen == t->ri)
				t->done = true;
				if (t->rlen == t->ri) {
					t->flags |= ACPI_EC_COMMAND_COMPLETE;
					wakeup = true;
				}
			} else
				goto err;
		} else if (t->wlen == t->wi &&
		   (status & ACPI_EC_FLAG_IBF) == 0)
		t->done = true;
	goto unlock;
			   (status & ACPI_EC_FLAG_IBF) == 0) {
			t->flags |= ACPI_EC_COMMAND_COMPLETE;
			wakeup = true;
		}
		return wakeup;
	} else {
		if ((status & ACPI_EC_FLAG_IBF) == 0) {
			acpi_ec_write_cmd(ec, t->command);
			t->flags |= ACPI_EC_COMMAND_POLL;
		} else
			goto err;
		return wakeup;
	}
err:
	/*
	 * If SCI bit is set, then don't think it's a false IRQ
	 * otherwise will take a not handled IRQ as a false one.
	 */
	if (in_interrupt() && !(status & ACPI_EC_FLAG_SCI))
	if (!(status & ACPI_EC_FLAG_SCI)) {
		if (in_interrupt() && t)
			++t->irq_count;
	}
	return wakeup;
}

unlock:
	spin_unlock_irqrestore(&ec->lock, flags);
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);
@@ -228,15 +256,17 @@ static int ec_poll(struct acpi_ec *ec)
			/* don't sleep with disabled interrupts */
			if (EC_FLAGS_MSI || irqs_disabled()) {
				udelay(ACPI_EC_MSI_UDELAY);
				if (ec_transaction_done(ec))
				if (ec_transaction_completed(ec))
					return 0;
			} else {
				if (wait_event_timeout(ec->wait,
						ec_transaction_done(ec),
						ec_transaction_completed(ec),
						msecs_to_jiffies(1)))
					return 0;
			}
			advance_transaction(ec, acpi_ec_read_status(ec));
			spin_lock_irqsave(&ec->lock, flags);
			(void)advance_transaction(ec);
			spin_unlock_irqrestore(&ec->lock, flags);
		} while (time_before(jiffies, delay));
		pr_debug("controller reset, restart transaction\n");
		spin_lock_irqsave(&ec->lock, flags);
@@ -268,23 +298,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
	return ret;
}

static int ec_check_ibf0(struct acpi_ec *ec)
{
	u8 status = acpi_ec_read_status(ec);
	return (status & ACPI_EC_FLAG_IBF) == 0;
}

static int ec_wait_ibf0(struct acpi_ec *ec)
{
	unsigned long delay = jiffies + msecs_to_jiffies(ec_delay);
	/* interrupt wait manually if GPE mode is not active */
	while (time_before(jiffies, delay))
		if (wait_event_timeout(ec->wait, ec_check_ibf0(ec),
					msecs_to_jiffies(1)))
			return 0;
	return -ETIME;
}

static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
{
	int status;
@@ -305,12 +318,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
			goto unlock;
		}
	}
	if (ec_wait_ibf0(ec)) {
		pr_err("input buffer is not empty, "
				"aborting transaction\n");
		status = -ETIME;
		goto end;
	}
	pr_debug("transaction start (cmd=0x%02x, addr=0x%02x)\n",
			t->command, t->wdata ? t->wdata[0] : 0);
	/* disable GPE during transaction if storm is detected */
@@ -334,7 +341,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
		set_bit(EC_FLAGS_GPE_STORM, &ec->flags);
	}
	pr_debug("transaction end\n");
end:
	if (ec->global_lock)
		acpi_release_global_lock(glk);
unlock:
@@ -634,17 +640,14 @@ static int ec_check_sci(struct acpi_ec *ec, u8 state)
static u32 acpi_ec_gpe_handler(acpi_handle gpe_device,
	u32 gpe_number, void *data)
{
	unsigned long flags;
	struct acpi_ec *ec = data;
	u8 status = acpi_ec_read_status(ec);

	pr_debug("~~~> interrupt, status:0x%02x\n", status);

	advance_transaction(ec, status);
	if (ec_transaction_done(ec) &&
	    (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) == 0) {
	spin_lock_irqsave(&ec->lock, flags);
	if (advance_transaction(ec))
		wake_up(&ec->wait);
	spin_unlock_irqrestore(&ec->lock, flags);
	ec_check_sci(ec, acpi_ec_read_status(ec));
	}
	return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
}

@@ -1066,8 +1069,10 @@ int __init acpi_ec_ecdt_probe(void)
	/* fall through */
	}

	if (EC_FLAGS_SKIP_DSDT_SCAN)
	if (EC_FLAGS_SKIP_DSDT_SCAN) {
		kfree(saved_ec);
		return -ENODEV;
	}

	/* This workaround is needed only on some broken machines,
	 * which require early EC, but fail to provide ECDT */
@@ -1105,6 +1110,7 @@ int __init acpi_ec_ecdt_probe(void)
	}
error:
	kfree(boot_ec);
	kfree(saved_ec);
	boot_ec = NULL;
	return -ENODEV;
}