Date: Sun, 09 Sep 2007 13:41:17 -0700 From: Nate Lawson <nate@root.org> To: Kostik Belousov <kostikbel@gmail.com> Cc: acpi@freebsd.org, current@freebsd.org Subject: Re: PATCH: ecng for 6.x and 7.x Message-ID: <46E45A6D.1010805@root.org> In-Reply-To: <20070907133901.GL53667@deviant.kiev.zoral.com.ua> References: <46E0777A.8070901@root.org> <20070907133901.GL53667@deviant.kiev.zoral.com.ua>
next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------040800050701000906010408
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Kostik Belousov wrote:
> On Thu, Sep 06, 2007 at 02:56:10PM -0700, Nate Lawson wrote:
>> I've done some major rework on the EC driver. This should help with
>> various problems, including timeouts while checking battery status or
>> temperature. The attached patches are for 6.x and 7.x. Please test and
>> let me know if you get any new errors on dmesg or if it fixes things for
>> you (especially HP/Compaq laptop owners).
> Ok, I tryed it on Acer 2490-kind of laptop. I see no difference with the
> patch, battery status seems to be reported correctly both before and after
> applying it.
>
> The only thing I want to note is that reboot on the laptop is turned into
> poweroff. You said in the followup to original letter that poweroff is
> turned into reboot, I did not checked it; this is opposite.
Please test this revised version. All it changes is forcing polling
mode back on during reboot/shutdown.
-Nate
--------------040800050701000906010408
Content-Type: text/x-patch;
name="ecng-6b.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="ecng-6b.diff"
--- sys/dev/acpica/acpi_ec.c 4 Sep 2007 22:40:39 -0000 1.65.2.3
+++ sys/dev/acpica/acpi_ec.c 9 Sep 2007 20:35:53 -0000
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2003 Nate Lawson
+ * Copyright (c) 2003-2007 Nate Lawson
* Copyright (c) 2000 Michael Smith
* Copyright (c) 2000 BSDi
* All rights reserved.
@@ -25,115 +25,6 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
-/*-
- ******************************************************************************
- *
- * 1. Copyright Notice
- *
- * Some or all of this work - Copyright (c) 1999, Intel Corp. All rights
- * reserved.
- *
- * 2. License
- *
- * 2.1. This is your license from Intel Corp. under its intellectual property
- * rights. You may have additional license terms from the party that provided
- * you this software, covering your right to use that party's intellectual
- * property rights.
- *
- * 2.2. Intel grants, free of charge, to any person ("Licensee") obtaining a
- * copy of the source code appearing in this file ("Covered Code") an
- * irrevocable, perpetual, worldwide license under Intel's copyrights in the
- * base code distributed originally by Intel ("Original Intel Code") to copy,
- * make derivatives, distribute, use and display any portion of the Covered
- * Code in any form, with the right to sublicense such rights; and
- *
- * 2.3. Intel grants Licensee a non-exclusive and non-transferable patent
- * license (with the right to sublicense), under only those claims of Intel
- * patents that are infringed by the Original Intel Code, to make, use, sell,
- * offer to sell, and import the Covered Code and derivative works thereof
- * solely to the minimum extent necessary to exercise the above copyright
- * license, and in no event shall the patent license extend to any additions
- * to or modifications of the Original Intel Code. No other license or right
- * is granted directly or by implication, estoppel or otherwise;
- *
- * The above copyright and patent license is granted only if the following
- * conditions are met:
- *
- * 3. Conditions
- *
- * 3.1. Redistribution of Source with Rights to Further Distribute Source.
- * Redistribution of source code of any substantial portion of the Covered
- * Code or modification with rights to further distribute source must include
- * the above Copyright Notice, the above License, this list of Conditions,
- * and the following Disclaimer and Export Compliance provision. In addition,
- * Licensee must cause all Covered Code to which Licensee contributes to
- * contain a file documenting the changes Licensee made to create that Covered
- * Code and the date of any change. Licensee must include in that file the
- * documentation of any changes made by any predecessor Licensee. Licensee
- * must include a prominent statement that the modification is derived,
- * directly or indirectly, from Original Intel Code.
- *
- * 3.2. Redistribution of Source with no Rights to Further Distribute Source.
- * Redistribution of source code of any substantial portion of the Covered
- * Code or modification without rights to further distribute source must
- * include the following Disclaimer and Export Compliance provision in the
- * documentation and/or other materials provided with distribution. In
- * addition, Licensee may not authorize further sublicense of source of any
- * portion of the Covered Code, and must include terms to the effect that the
- * license from Licensee to its licensee is limited to the intellectual
- * property embodied in the software Licensee provides to its licensee, and
- * not to intellectual property embodied in modifications its licensee may
- * make.
- *
- * 3.3. Redistribution of Executable. Redistribution in executable form of any
- * substantial portion of the Covered Code or modification must reproduce the
- * above Copyright Notice, and the following Disclaimer and Export Compliance
- * provision in the documentation and/or other materials provided with the
- * distribution.
- *
- * 3.4. Intel retains all right, title, and interest in and to the Original
- * Intel Code.
- *
- * 3.5. Neither the name Intel nor any other trademark owned or controlled by
- * Intel shall be used in advertising or otherwise to promote the sale, use or
- * other dealings in products derived from or relating to the Covered Code
- * without prior written authorization from Intel.
- *
- * 4. Disclaimer and Export Compliance
- *
- * 4.1. INTEL MAKES NO WARRANTY OF ANY KIND REGARDING ANY SOFTWARE PROVIDED
- * HERE. ANY SOFTWARE ORIGINATING FROM INTEL OR DERIVED FROM INTEL SOFTWARE
- * IS PROVIDED "AS IS," AND INTEL WILL NOT PROVIDE ANY SUPPORT, ASSISTANCE,
- * INSTALLATION, TRAINING OR OTHER SERVICES. INTEL WILL NOT PROVIDE ANY
- * UPDATES, ENHANCEMENTS OR EXTENSIONS. INTEL SPECIFICALLY DISCLAIMS ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT AND FITNESS FOR A
- * PARTICULAR PURPOSE.
- *
- * 4.2. IN NO EVENT SHALL INTEL HAVE ANY LIABILITY TO LICENSEE, ITS LICENSEES
- * OR ANY OTHER THIRD PARTY, FOR ANY LOST PROFITS, LOST DATA, LOSS OF USE OR
- * COSTS OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT,
- * SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, UNDER ANY
- * CAUSE OF ACTION OR THEORY OF LIABILITY, AND IRRESPECTIVE OF WHETHER INTEL
- * HAS ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS
- * SHALL APPLY NOTWITHSTANDING THE FAILURE OF THE ESSENTIAL PURPOSE OF ANY
- * LIMITED REMEDY.
- *
- * 4.3. Licensee shall not export, either directly or indirectly, any of this
- * software or system incorporating such software without first obtaining any
- * required license or other approval from the U. S. Department of Commerce or
- * any other agency or department of the United States Government. In the
- * event Licensee exports any such software from the United States or
- * re-exports any such software from a foreign destination, Licensee shall
- * ensure that the distribution and export/re-export of the software is in
- * compliance with all laws, regulations, orders, or other restrictions of the
- * U.S. Export Administration Regulations. Licensee agrees that neither it nor
- * any of its subsidiaries will export/re-export any technical data, process,
- * software, or service, directly or indirectly, to any country for which the
- * United States government or any agency thereof requires an export license,
- * other governmental approval, or letter of assurance, without first obtaining
- * such license, approval or letter.
- *
- *****************************************************************************/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
@@ -142,9 +33,9 @@
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
+#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
-#include <sys/lock.h>
#include <sys/sx.h>
#include <machine/bus.h>
@@ -188,15 +79,15 @@
* | | | +--------- Burst Mode Enabled?
* | | +----------- SCI Event?
* | +------------- SMI Event?
- * +--------------- <Reserved>
+ * +--------------- <reserved>
*
*/
typedef UINT8 EC_STATUS;
#define EC_FLAG_OUTPUT_BUFFER ((EC_STATUS) 0x01)
#define EC_FLAG_INPUT_BUFFER ((EC_STATUS) 0x02)
+#define EC_FLAG_DATA_IS_CMD ((EC_STATUS) 0x08)
#define EC_FLAG_BURST_MODE ((EC_STATUS) 0x10)
-#define EC_FLAG_SCI ((EC_STATUS) 0x20)
/*
* EC_EVENT:
@@ -208,6 +99,10 @@
#define EC_EVENT_OUTPUT_BUFFER_FULL ((EC_EVENT) 0x01)
#define EC_EVENT_INPUT_BUFFER_EMPTY ((EC_EVENT) 0x02)
#define EC_EVENT_SCI ((EC_EVENT) 0x20)
+#define EC_EVENT_SMI ((EC_EVENT) 0x40)
+
+/* Data byte returned after burst enable indicating it was successful. */
+#define EC_BURST_ACK 0x90
/*
* Register access primitives
@@ -227,11 +122,11 @@
/* Embedded Controller Boot Resources Table (ECDT) */
typedef struct {
ACPI_TABLE_HEADER header;
- ACPI_GENERIC_ADDRESS control;
- ACPI_GENERIC_ADDRESS data;
- UINT32 uid;
- UINT8 gpe_bit;
- char ec_id[0];
+ ACPI_GENERIC_ADDRESS Control;
+ ACPI_GENERIC_ADDRESS Data;
+ UINT32 Uid;
+ UINT8 Gpe;
+ char Id[0];
} ACPI_TABLE_ECDT;
/* Additional params to pass from the probe routine */
@@ -243,7 +138,7 @@
};
/* Indicate that this device has already been probed via ECDT. */
-#define DEV_ECDT(x) (acpi_get_magic(x) == (int)&acpi_ec_devclass)
+#define DEV_ECDT(x) (acpi_get_magic(x) == (uintptr_t)&acpi_ec_devclass)
/*
* Driver softc.
@@ -254,7 +149,6 @@
int ec_uid;
ACPI_HANDLE ec_gpehandle;
UINT8 ec_gpebit;
- UINT8 ec_csrvalue;
int ec_data_rid;
struct resource *ec_data_res;
@@ -268,6 +162,9 @@
int ec_glk;
int ec_glkhandle;
+ int ec_burstactive;
+ int ec_sci_pend;
+ u_int ec_gencount;
};
/*
@@ -277,11 +174,11 @@
*/
#define EC_LOCK_TIMEOUT 1000
-/* Default interval in microseconds for the status polling loop. */
-#define EC_POLL_DELAY 10
+/* Default delay in microseconds between each run of the status polling loop. */
+#define EC_POLL_DELAY 5
-/* Total time in ms spent in the poll loop waiting for a response. */
-#define EC_POLL_TIMEOUT 100
+/* Total time in ms spent waiting for a response from EC. */
+#define EC_TIMEOUT 750
#define EVENT_READY(event, status) \
(((event) == EC_EVENT_OUTPUT_BUFFER_FULL && \
@@ -289,36 +186,46 @@
((event) == EC_EVENT_INPUT_BUFFER_EMPTY && \
((status) & EC_FLAG_INPUT_BUFFER) == 0))
-static int ec_poll_timeout = EC_POLL_TIMEOUT;
-TUNABLE_INT("hw.acpi.ec.poll_timeout", &ec_poll_timeout);
-
ACPI_SERIAL_DECL(ec, "ACPI embedded controller");
-static __inline ACPI_STATUS
+SYSCTL_DECL(_debug_acpi);
+SYSCTL_NODE(_debug_acpi, OID_AUTO, ec, CTLFLAG_RD, NULL, "EC debugging");
+
+static int ec_burst_mode;
+TUNABLE_INT("debug.acpi.ec.burst", &ec_burst_mode);
+SYSCTL_INT(_debug_acpi_ec, OID_AUTO, burst, CTLFLAG_RW, &ec_burst_mode, 0,
+ "Enable use of burst mode (faster for nearly all systems)");
+static int ec_polled_mode;
+TUNABLE_INT("debug.acpi.ec.polled", &ec_polled_mode);
+SYSCTL_INT(_debug_acpi_ec, OID_AUTO, polled, CTLFLAG_RW, &ec_polled_mode, 0,
+ "Force use of polled mode (only if interrupt mode doesn't work)");
+static int ec_timeout = EC_TIMEOUT;
+TUNABLE_INT("debug.acpi.ec.timeout", &ec_timeout);
+SYSCTL_INT(_debug_acpi_ec, OID_AUTO, timeout, CTLFLAG_RW, &ec_timeout,
+ EC_TIMEOUT, "Total time spent waiting for a response (poll+sleep)");
+
+static ACPI_STATUS
EcLock(struct acpi_ec_softc *sc)
{
ACPI_STATUS status;
- /* Always acquire the exclusive lock. */
+ /* If _GLK is non-zero, acquire the global lock. */
status = AE_OK;
- ACPI_SERIAL_BEGIN(ec);
-
- /* If _GLK is non-zero, also acquire the global lock. */
if (sc->ec_glk) {
status = AcpiAcquireGlobalLock(EC_LOCK_TIMEOUT, &sc->ec_glkhandle);
if (ACPI_FAILURE(status))
- ACPI_SERIAL_END(ec);
+ return (status);
}
-
+ ACPI_SERIAL_BEGIN(ec);
return (status);
}
-static __inline void
+static void
EcUnlock(struct acpi_ec_softc *sc)
{
+ ACPI_SERIAL_END(ec);
if (sc->ec_glk)
AcpiReleaseGlobalLock(sc->ec_glkhandle);
- ACPI_SERIAL_END(ec);
}
static uint32_t EcGpeHandler(void *Context);
@@ -328,7 +235,8 @@
ACPI_PHYSICAL_ADDRESS Address,
UINT32 width, ACPI_INTEGER *Value,
void *Context, void *RegionContext);
-static ACPI_STATUS EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event);
+static ACPI_STATUS EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event,
+ u_int gen_count);
static ACPI_STATUS EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd);
static ACPI_STATUS EcRead(struct acpi_ec_softc *sc, UINT8 Address,
UINT8 *Data);
@@ -369,7 +277,9 @@
* Look for an ECDT and if we find one, set up default GPE and
* space handlers to catch attempts to access EC space before
* we have a real driver instance in place.
- * TODO: if people report invalid ECDTs, add a tunable to disable them.
+ *
+ * TODO: Some old Gateway laptops need us to fake up an ECDT or
+ * otherwise attach early so that _REG methods can run.
*/
void
acpi_ec_ecdt_probe(device_t parent)
@@ -387,20 +297,20 @@
status = AcpiGetFirmwareTable("ECDT", 1, ACPI_LOGICAL_ADDRESSING, &hdr);
ecdt = (ACPI_TABLE_ECDT *)hdr;
if (ACPI_FAILURE(status) ||
- ecdt->control.RegisterBitWidth != 8 ||
- ecdt->data.RegisterBitWidth != 8) {
+ ecdt->Control.RegisterBitWidth != 8 ||
+ ecdt->Data.RegisterBitWidth != 8) {
return;
}
/* Create the child device with the given unit number. */
- child = BUS_ADD_CHILD(parent, 0, "acpi_ec", ecdt->uid);
+ child = BUS_ADD_CHILD(parent, 0, "acpi_ec", ecdt->Uid);
if (child == NULL) {
printf("%s: can't add child\n", __func__);
return;
}
/* Find and save the ACPI handle for this device. */
- status = AcpiGetHandle(NULL, ecdt->ec_id, &h);
+ status = AcpiGetHandle(NULL, ecdt->Id, &h);
if (ACPI_FAILURE(status)) {
device_delete_child(parent, child);
printf("%s: can't get handle\n", __func__);
@@ -409,9 +319,9 @@
acpi_set_handle(child, h);
/* Set the data and CSR register addresses. */
- bus_set_resource(child, SYS_RES_IOPORT, 0, ecdt->data.Address,
+ bus_set_resource(child, SYS_RES_IOPORT, 0, ecdt->Data.Address,
/*count*/1);
- bus_set_resource(child, SYS_RES_IOPORT, 1, ecdt->control.Address,
+ bus_set_resource(child, SYS_RES_IOPORT, 1, ecdt->Control.Address,
/*count*/1);
/*
@@ -423,11 +333,11 @@
*/
params = malloc(sizeof(struct acpi_ec_params), M_TEMP, M_WAITOK | M_ZERO);
params->gpe_handle = NULL;
- params->gpe_bit = ecdt->gpe_bit;
- params->uid = ecdt->uid;
+ params->gpe_bit = ecdt->Gpe;
+ params->uid = ecdt->Uid;
acpi_GetInteger(h, "_GLK", ¶ms->glk);
acpi_set_private(child, params);
- acpi_set_magic(child, (int)&acpi_ec_devclass);
+ acpi_set_magic(child, (uintptr_t)&acpi_ec_devclass);
/* Finish the attach process. */
if (device_probe_and_attach(child) != 0)
@@ -688,100 +598,92 @@
struct acpi_ec_softc *sc = (struct acpi_ec_softc *)Context;
UINT8 Data;
ACPI_STATUS Status;
- EC_STATUS EcStatus;
char qxx[5];
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
KASSERT(Context != NULL, ("EcGpeQueryHandler called with NULL"));
+ /* Serialize user access with EcSpaceHandler(). */
Status = EcLock(sc);
if (ACPI_FAILURE(Status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "GpeQuery lock error: %s\n", AcpiFormatException(Status));
+ device_printf(sc->ec_dev, "GpeQuery lock error: %s\n",
+ AcpiFormatException(Status));
return;
}
/*
- * If the EC_SCI bit of the status register is not set, then pass
- * it along to any potential waiters as it may be an IBE/OBF event.
- */
- EcStatus = EC_GET_CSR(sc);
- if ((EcStatus & EC_EVENT_SCI) == 0) {
- CTR1(KTR_ACPI, "ec event was not SCI, status %#x", EcStatus);
- sc->ec_csrvalue = EcStatus;
- wakeup(&sc->ec_csrvalue);
- EcUnlock(sc);
- goto re_enable;
- }
-
- /*
* Send a query command to the EC to find out which _Qxx call it
* wants to make. This command clears the SCI bit and also the
- * interrupt source since we are edge-triggered.
+ * interrupt source since we are edge-triggered. To prevent the GPE
+ * that may arise from running the query from causing another query
+ * to be queued, we clear the pending flag only after running it.
*/
Status = EcCommand(sc, EC_COMMAND_QUERY);
+ sc->ec_sci_pend = FALSE;
if (ACPI_FAILURE(Status)) {
EcUnlock(sc);
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "GPE query failed - %s\n", AcpiFormatException(Status));
- goto re_enable;
+ device_printf(sc->ec_dev, "GPE query failed: %s\n",
+ AcpiFormatException(Status));
+ return;
}
Data = EC_GET_DATA(sc);
- EcUnlock(sc);
/* Ignore the value for "no outstanding event". (13.3.5) */
- CTR2(KTR_ACPI, "ec query ok,%s running _Q%02x", Data ? "" : " not", Data);
- if (Data == 0)
- goto re_enable;
+ CTR2(KTR_ACPI, "ec query ok,%s running _Q%02X", Data ? "" : " not", Data);
+ if (Data == 0) {
+ EcUnlock(sc);
+ return;
+ }
/* Evaluate _Qxx to respond to the controller. */
- sprintf(qxx, "_Q%02x", Data);
+ snprintf(qxx, sizeof(qxx), "_Q%02X", Data);
AcpiUtStrupr(qxx);
Status = AcpiEvaluateObject(sc->ec_handle, qxx, NULL, NULL);
if (ACPI_FAILURE(Status) && Status != AE_NOT_FOUND) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "evaluation of GPE query method %s failed - %s\n",
- qxx, AcpiFormatException(Status));
+ device_printf(sc->ec_dev, "evaluation of query method %s failed: %s\n",
+ qxx, AcpiFormatException(Status));
}
-re_enable:
- /* Re-enable the GPE event so we'll get future requests. */
- Status = AcpiEnableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_NOT_ISR);
- if (ACPI_FAILURE(Status))
- printf("EcGpeQueryHandler: AcpiEnableEvent failed\n");
+ EcUnlock(sc);
}
/*
- * Handle a GPE. Currently we only handle SCI events as others must
- * be handled by polling in EcWaitEvent(). This is because some ECs
- * treat events as level when they should be edge-triggered.
+ * The GPE handler is called when IBE/OBF or SCI events occur. We are
+ * called from an unknown lock context.
*/
static uint32_t
EcGpeHandler(void *Context)
{
struct acpi_ec_softc *sc = Context;
ACPI_STATUS Status;
+ EC_STATUS EcStatus;
KASSERT(Context != NULL, ("EcGpeHandler called with NULL"));
+ CTR0(KTR_ACPI, "ec gpe handler start");
/*
- * Disable further GPEs while we handle this one. Since we are directly
- * called by ACPI-CA and it may have unknown locks held, we specify the
- * ACPI_ISR flag to keep it from acquiring any more mutexes (which could
- * potentially sleep.)
+ * Notify EcWaitEvent() that the status register is now fresh. If we
+ * didn't do this, it wouldn't be possible to distinguish an old IBE
+ * from a new one, for example when doing a write transaction (writing
+ * address and then data values.)
*/
- AcpiDisableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_ISR);
+ atomic_add_int(&sc->ec_gencount, 1);
+ wakeup(&sc->ec_gencount);
- /* Schedule the GPE query handler. */
- Status = AcpiOsQueueForExecution(OSD_PRIORITY_GPE, EcGpeQueryHandler,
- Context);
- if (ACPI_FAILURE(Status)) {
- printf("Queuing GPE query handler failed.\n");
- Status = AcpiEnableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_ISR);
- if (ACPI_FAILURE(Status))
- printf("EcGpeHandler: AcpiEnableEvent failed\n");
+ /*
+ * If the EC_SCI bit of the status register is set, queue a query handler.
+ * It will run the query and _Qxx method later, under the lock.
+ */
+ EcStatus = EC_GET_CSR(sc);
+ if ((EcStatus & EC_EVENT_SCI) && !sc->ec_sci_pend) {
+ CTR0(KTR_ACPI, "ec gpe queueing query handler");
+ Status = AcpiOsQueueForExecution(OSD_PRIORITY_GPE, EcGpeQueryHandler,
+ Context);
+ if (ACPI_SUCCESS(Status))
+ sc->ec_sci_pend = TRUE;
+ else
+ printf("EcGpeHandler: queuing GPE query handler failed\n");
}
-
return (0);
}
@@ -825,6 +727,18 @@
EcAddr = Address;
Status = AE_ERROR;
+ /*
+ * If booting, check if we need to run the query handler. If so, we
+ * we call it directly here since our thread taskq is not active yet.
+ */
+ if (cold || rebooting) {
+ if ((EC_GET_CSR(sc) & EC_EVENT_SCI)) {
+ CTR0(KTR_ACPI, "ec running gpe handler directly");
+ EcGpeQueryHandler(sc);
+ }
+ }
+
+ /* Serialize with EcGpeQueryHandler() at transaction granularity. */
Status = EcLock(sc);
if (ACPI_FAILURE(Status))
return_ACPI_STATUS (Status);
@@ -850,149 +764,188 @@
if (ACPI_FAILURE(Status))
break;
}
-
EcUnlock(sc);
+
return_ACPI_STATUS (Status);
}
static ACPI_STATUS
-EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event)
+EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event, u_int gen_count)
{
EC_STATUS EcStatus;
ACPI_STATUS Status;
- int count, i, period, retval, slp_ival;
+ int count, i, slp_ival;
ACPI_SERIAL_ASSERT(ec);
Status = AE_NO_HARDWARE_RESPONSE;
- /*
- * Wait for 1 us before checking the CSR. Testing shows about
- * 50% of requests complete in 1 us and 90% of them complete
- * in 5 us or less.
- */
- AcpiOsStall(1);
-
/*
- * Poll the EC status register for up to 1 ms in chunks of 10 us
- * to detect completion of the last command.
+ * The main CPU should be much faster than the EC. So the status should
+ * be "not ready" when we start waiting. But if the main CPU is really
+ * slow, it's possible we see the current "ready" response. Since that
+ * can't be distinguished from the previous response in polled mode,
+ * this is a potential issue. We really should have interrupts enabled
+ * during boot so there is no ambiguity in polled mode. For now, let's
+ * collect some stats about how many systems actually have this issue.
*/
- for (i = 0; i < 1000 / EC_POLL_DELAY; i++) {
+ if (cold || rebooting || ec_polled_mode) {
+ static int once;
+
EcStatus = EC_GET_CSR(sc);
- if (EVENT_READY(Event, EcStatus)) {
- Status = AE_OK;
- break;
+ if (!once && EVENT_READY(Event, EcStatus)) {
+ device_printf(sc->ec_dev,
+ "warning: EC done before starting event wait\n");
+ once = 1;
}
- AcpiOsStall(EC_POLL_DELAY);
}
- period = i * EC_POLL_DELAY;
- /*
- * If we still don't have a response and we're up and running, wait up
- * to ec_poll_timeout ms for completion, sleeping for chunks of 10 ms.
- */
- slp_ival = 0;
- if (Status != AE_OK) {
- retval = ENXIO;
- count = ec_poll_timeout / 10;
- if (count == 0)
+ /* Wait for event by polling or GPE (interrupt). */
+ if (cold || rebooting || ec_polled_mode) {
+ count = (ec_timeout * 1000) / EC_POLL_DELAY;
+ if (count <= 0)
count = 1;
- slp_ival = hz / 100;
- if (slp_ival == 0)
- slp_ival = 1;
for (i = 0; i < count; i++) {
- if (retval != 0)
- EcStatus = EC_GET_CSR(sc);
- else
- EcStatus = sc->ec_csrvalue;
+ EcStatus = EC_GET_CSR(sc);
+ if (sc->ec_burstactive && !(EcStatus & EC_FLAG_BURST_MODE)) {
+ CTR0(KTR_ACPI, "ec burst disabled in waitevent (poll)");
+ sc->ec_burstactive = FALSE;
+ }
if (EVENT_READY(Event, EcStatus)) {
+ CTR1(KTR_ACPI, "ec poll wait ready, status %#x", EcStatus);
Status = AE_OK;
break;
}
- if (!cold)
- retval = tsleep(&sc->ec_csrvalue, PZERO, "ecpoll", slp_ival);
- else
- AcpiOsStall(10000);
+ AcpiOsStall(EC_POLL_DELAY);
+ }
+ } else {
+ slp_ival = hz / 1000;
+ if (slp_ival != 0) {
+ count = ec_timeout / slp_ival;
+ } else {
+ /* hz has less than 1000 Hz resolution so scale timeout. */
+ slp_ival = 1;
+ count = ec_timeout / (1000 / hz);
}
- }
-
- /* Calculate new delay and log it. */
- if (slp_ival > 0)
- period += i * 10000;
- CTR2(KTR_ACPI, "ec got event %#x after %d us", EcStatus, period);
+ /*
+ * Wait for the GPE to signal the status changed, checking the
+ * status register each time.
+ */
+ for (i = 0; i < count; i++) {
+ if (gen_count != sc->ec_gencount) {
+ /*
+ * Record new generation count. It's possible the GPE was
+ * just to notify us that a query is needed and we need to
+ * wait for a second GPE to signal the completion of the
+ * event we are actually waiting for.
+ */
+ gen_count = sc->ec_gencount;
+ EcStatus = EC_GET_CSR(sc);
+ if (sc->ec_burstactive && !(EcStatus & EC_FLAG_BURST_MODE)) {
+ CTR0(KTR_ACPI, "ec burst disabled in waitevent (sleep)");
+ sc->ec_burstactive = FALSE;
+ }
+ if (EVENT_READY(Event, EcStatus)) {
+ CTR1(KTR_ACPI, "ec sleep wait ready, status %#x", EcStatus);
+ Status = AE_OK;
+ break;
+ }
+ }
+ tsleep(&sc->ec_gencount, PZERO, "ecgpe", slp_ival);
+ }
+ }
+ if (Status != AE_OK)
+ CTR0(KTR_ACPI, "error: ec wait timed out");
return (Status);
-}
+}
static ACPI_STATUS
EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd)
{
- ACPI_STATUS Status;
- EC_EVENT Event;
+ ACPI_STATUS status;
+ EC_EVENT event;
+ EC_STATUS ec_status;
+ u_int gen_count;
ACPI_SERIAL_ASSERT(ec);
+ /* Don't use burst mode if user disabled it. */
+ if (!ec_burst_mode && cmd == EC_COMMAND_BURST_ENABLE)
+ return (AE_ERROR);
+
/* Decide what to wait for based on command type. */
switch (cmd) {
case EC_COMMAND_READ:
case EC_COMMAND_WRITE:
case EC_COMMAND_BURST_DISABLE:
- Event = EC_EVENT_INPUT_BUFFER_EMPTY;
+ event = EC_EVENT_INPUT_BUFFER_EMPTY;
break;
case EC_COMMAND_QUERY:
case EC_COMMAND_BURST_ENABLE:
- Event = EC_EVENT_OUTPUT_BUFFER_FULL;
+ event = EC_EVENT_OUTPUT_BUFFER_FULL;
break;
default:
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcCommand: Invalid command %#x\n", cmd);
+ device_printf(sc->ec_dev, "EcCommand: invalid command %#x\n", cmd);
return (AE_BAD_PARAMETER);
}
/* Run the command and wait for the chosen event. */
+ CTR1(KTR_ACPI, "ec running command %#x", cmd);
+ gen_count = sc->ec_gencount;
EC_SET_CSR(sc, cmd);
- Status = EcWaitEvent(sc, Event);
- if (ACPI_FAILURE(Status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcCommand: no response to %#x\n", cmd);
- }
-
- return (Status);
+ status = EcWaitEvent(sc, event, gen_count);
+ if (ACPI_SUCCESS(status)) {
+ /* If we succeeded, burst flag should now be present. */
+ if (cmd == EC_COMMAND_BURST_ENABLE) {
+ ec_status = EC_GET_CSR(sc);
+ if ((ec_status & EC_FLAG_BURST_MODE) == 0)
+ status = AE_ERROR;
+ }
+ } else
+ device_printf(sc->ec_dev, "EcCommand: no response to %#x\n", cmd);
+ return (status);
}
static ACPI_STATUS
EcRead(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data)
{
- ACPI_STATUS Status;
+ ACPI_STATUS status;
+ UINT8 data;
+ u_int gen_count;
ACPI_SERIAL_ASSERT(ec);
CTR1(KTR_ACPI, "ec read from %#x", Address);
-#ifdef notyet
/* If we can't start burst mode, continue anyway. */
- EcCommand(sc, EC_COMMAND_BURST_ENABLE);
-#endif
+ status = EcCommand(sc, EC_COMMAND_BURST_ENABLE);
+ if (status == AE_OK) {
+ data = EC_GET_DATA(sc);
+ if (data == EC_BURST_ACK) {
+ CTR0(KTR_ACPI, "ec burst enabled");
+ sc->ec_burstactive = TRUE;
+ }
+ }
- Status = EcCommand(sc, EC_COMMAND_READ);
- if (ACPI_FAILURE(Status))
- return (Status);
+ status = EcCommand(sc, EC_COMMAND_READ);
+ if (ACPI_FAILURE(status))
+ return (status);
+ gen_count = sc->ec_gencount;
EC_SET_DATA(sc, Address);
- Status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL);
- if (ACPI_FAILURE(Status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcRead: Failed waiting for EC to send data.\n");
- return (Status);
+ status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL, gen_count);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->ec_dev, "EcRead: failed waiting to get data\n");
+ return (status);
}
-
*Data = EC_GET_DATA(sc);
-#ifdef notyet
if (sc->ec_burstactive) {
- Status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
- if (ACPI_FAILURE(Status))
- return (Status);
+ sc->ec_burstactive = FALSE;
+ status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
+ if (ACPI_FAILURE(status))
+ return (status);
+ CTR0(KTR_ACPI, "ec disabled burst ok");
}
-#endif
return (AE_OK);
}
@@ -1000,43 +953,50 @@
static ACPI_STATUS
EcWrite(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data)
{
- ACPI_STATUS Status;
+ ACPI_STATUS status;
+ UINT8 data;
+ u_int gen_count;
ACPI_SERIAL_ASSERT(ec);
CTR2(KTR_ACPI, "ec write to %#x, data %#x", Address, *Data);
-#ifdef notyet
/* If we can't start burst mode, continue anyway. */
- EcCommand(sc, EC_COMMAND_BURST_ENABLE);
-#endif
+ status = EcCommand(sc, EC_COMMAND_BURST_ENABLE);
+ if (status == AE_OK) {
+ data = EC_GET_DATA(sc);
+ if (data == EC_BURST_ACK) {
+ CTR0(KTR_ACPI, "ec burst enabled");
+ sc->ec_burstactive = TRUE;
+ }
+ }
- Status = EcCommand(sc, EC_COMMAND_WRITE);
- if (ACPI_FAILURE(Status))
- return (Status);
+ status = EcCommand(sc, EC_COMMAND_WRITE);
+ if (ACPI_FAILURE(status))
+ return (status);
+ gen_count = sc->ec_gencount;
EC_SET_DATA(sc, Address);
- Status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY);
- if (ACPI_FAILURE(Status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcRead: Failed waiting for EC to process address\n");
- return (Status);
+ status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->ec_dev, "EcRead: failed waiting for sent address\n");
+ return (status);
}
+ gen_count = sc->ec_gencount;
EC_SET_DATA(sc, *Data);
- Status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY);
- if (ACPI_FAILURE(Status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcWrite: Failed waiting for EC to process data\n");
- return (Status);
+ status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->ec_dev, "EcWrite: failed waiting for sent data\n");
+ return (status);
}
-#ifdef notyet
if (sc->ec_burstactive) {
- Status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
- if (ACPI_FAILURE(Status))
- return (Status);
+ sc->ec_burstactive = FALSE;
+ status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
+ if (ACPI_FAILURE(status))
+ return (status);
+ CTR0(KTR_ACPI, "ec disabled burst ok");
}
-#endif
return (AE_OK);
}
--------------040800050701000906010408
Content-Type: text/x-patch;
name="ecng-7b.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="ecng-7b.diff"
--- sys/dev/acpica/acpi_ec.c 15 Jun 2007 18:02:33 -0000 1.75
+++ sys/dev/acpica/acpi_ec.c 6 Sep 2007 22:23:52 -0000
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2003 Nate Lawson
+ * Copyright (c) 2003-2007 Nate Lawson
* Copyright (c) 2000 Michael Smith
* Copyright (c) 2000 BSDi
* All rights reserved.
@@ -25,115 +25,6 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
-/*-
- ******************************************************************************
- *
- * 1. Copyright Notice
- *
- * Some or all of this work - Copyright (c) 1999, Intel Corp. All rights
- * reserved.
- *
- * 2. License
- *
- * 2.1. This is your license from Intel Corp. under its intellectual property
- * rights. You may have additional license terms from the party that provided
- * you this software, covering your right to use that party's intellectual
- * property rights.
- *
- * 2.2. Intel grants, free of charge, to any person ("Licensee") obtaining a
- * copy of the source code appearing in this file ("Covered Code") an
- * irrevocable, perpetual, worldwide license under Intel's copyrights in the
- * base code distributed originally by Intel ("Original Intel Code") to copy,
- * make derivatives, distribute, use and display any portion of the Covered
- * Code in any form, with the right to sublicense such rights; and
- *
- * 2.3. Intel grants Licensee a non-exclusive and non-transferable patent
- * license (with the right to sublicense), under only those claims of Intel
- * patents that are infringed by the Original Intel Code, to make, use, sell,
- * offer to sell, and import the Covered Code and derivative works thereof
- * solely to the minimum extent necessary to exercise the above copyright
- * license, and in no event shall the patent license extend to any additions
- * to or modifications of the Original Intel Code. No other license or right
- * is granted directly or by implication, estoppel or otherwise;
- *
- * The above copyright and patent license is granted only if the following
- * conditions are met:
- *
- * 3. Conditions
- *
- * 3.1. Redistribution of Source with Rights to Further Distribute Source.
- * Redistribution of source code of any substantial portion of the Covered
- * Code or modification with rights to further distribute source must include
- * the above Copyright Notice, the above License, this list of Conditions,
- * and the following Disclaimer and Export Compliance provision. In addition,
- * Licensee must cause all Covered Code to which Licensee contributes to
- * contain a file documenting the changes Licensee made to create that Covered
- * Code and the date of any change. Licensee must include in that file the
- * documentation of any changes made by any predecessor Licensee. Licensee
- * must include a prominent statement that the modification is derived,
- * directly or indirectly, from Original Intel Code.
- *
- * 3.2. Redistribution of Source with no Rights to Further Distribute Source.
- * Redistribution of source code of any substantial portion of the Covered
- * Code or modification without rights to further distribute source must
- * include the following Disclaimer and Export Compliance provision in the
- * documentation and/or other materials provided with distribution. In
- * addition, Licensee may not authorize further sublicense of source of any
- * portion of the Covered Code, and must include terms to the effect that the
- * license from Licensee to its licensee is limited to the intellectual
- * property embodied in the software Licensee provides to its licensee, and
- * not to intellectual property embodied in modifications its licensee may
- * make.
- *
- * 3.3. Redistribution of Executable. Redistribution in executable form of any
- * substantial portion of the Covered Code or modification must reproduce the
- * above Copyright Notice, and the following Disclaimer and Export Compliance
- * provision in the documentation and/or other materials provided with the
- * distribution.
- *
- * 3.4. Intel retains all right, title, and interest in and to the Original
- * Intel Code.
- *
- * 3.5. Neither the name Intel nor any other trademark owned or controlled by
- * Intel shall be used in advertising or otherwise to promote the sale, use or
- * other dealings in products derived from or relating to the Covered Code
- * without prior written authorization from Intel.
- *
- * 4. Disclaimer and Export Compliance
- *
- * 4.1. INTEL MAKES NO WARRANTY OF ANY KIND REGARDING ANY SOFTWARE PROVIDED
- * HERE. ANY SOFTWARE ORIGINATING FROM INTEL OR DERIVED FROM INTEL SOFTWARE
- * IS PROVIDED "AS IS," AND INTEL WILL NOT PROVIDE ANY SUPPORT, ASSISTANCE,
- * INSTALLATION, TRAINING OR OTHER SERVICES. INTEL WILL NOT PROVIDE ANY
- * UPDATES, ENHANCEMENTS OR EXTENSIONS. INTEL SPECIFICALLY DISCLAIMS ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT AND FITNESS FOR A
- * PARTICULAR PURPOSE.
- *
- * 4.2. IN NO EVENT SHALL INTEL HAVE ANY LIABILITY TO LICENSEE, ITS LICENSEES
- * OR ANY OTHER THIRD PARTY, FOR ANY LOST PROFITS, LOST DATA, LOSS OF USE OR
- * COSTS OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT,
- * SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, UNDER ANY
- * CAUSE OF ACTION OR THEORY OF LIABILITY, AND IRRESPECTIVE OF WHETHER INTEL
- * HAS ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS
- * SHALL APPLY NOTWITHSTANDING THE FAILURE OF THE ESSENTIAL PURPOSE OF ANY
- * LIMITED REMEDY.
- *
- * 4.3. Licensee shall not export, either directly or indirectly, any of this
- * software or system incorporating such software without first obtaining any
- * required license or other approval from the U. S. Department of Commerce or
- * any other agency or department of the United States Government. In the
- * event Licensee exports any such software from the United States or
- * re-exports any such software from a foreign destination, Licensee shall
- * ensure that the distribution and export/re-export of the software is in
- * compliance with all laws, regulations, orders, or other restrictions of the
- * U.S. Export Administration Regulations. Licensee agrees that neither it nor
- * any of its subsidiaries will export/re-export any technical data, process,
- * software, or service, directly or indirectly, to any country for which the
- * United States government or any agency thereof requires an export license,
- * other governmental approval, or letter of assurance, without first obtaining
- * such license, approval or letter.
- *
- *****************************************************************************/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
@@ -248,7 +139,6 @@
int ec_uid;
ACPI_HANDLE ec_gpehandle;
UINT8 ec_gpebit;
- UINT8 ec_csrvalue;
int ec_data_rid;
struct resource *ec_data_res;
@@ -260,11 +150,11 @@
bus_space_tag_t ec_csr_tag;
bus_space_handle_t ec_csr_handle;
- struct mtx ec_mtx;
int ec_glk;
int ec_glkhandle;
int ec_burstactive;
int ec_sci_pend;
+ u_int ec_gencount;
};
/*
@@ -275,13 +165,10 @@
#define EC_LOCK_TIMEOUT 1000
/* Default delay in microseconds between each run of the status polling loop. */
-#define EC_POLL_DELAY 10
-
-/* Default time in microseconds spent polling before sleep waiting. */
-#define EC_POLL_TIME 500
+#define EC_POLL_DELAY 5
/* Total time in ms spent waiting for a response from EC. */
-#define EC_TIMEOUT 500
+#define EC_TIMEOUT 750
#define EVENT_READY(event, status) \
(((event) == EC_EVENT_OUTPUT_BUFFER_FULL && \
@@ -298,17 +185,17 @@
TUNABLE_INT("debug.acpi.ec.burst", &ec_burst_mode);
SYSCTL_INT(_debug_acpi_ec, OID_AUTO, burst, CTLFLAG_RW, &ec_burst_mode, 0,
"Enable use of burst mode (faster for nearly all systems)");
-static int ec_poll_time = EC_POLL_TIME;
-TUNABLE_INT("debug.acpi.ec.poll_time", &ec_poll_time);
-SYSCTL_INT(_debug_acpi_ec, OID_AUTO, poll_time, CTLFLAG_RW, &ec_poll_time,
- EC_POLL_TIME, "Time spent polling vs. sleeping (CPU intensive)");
+static int ec_polled_mode;
+TUNABLE_INT("debug.acpi.ec.polled", &ec_polled_mode);
+SYSCTL_INT(_debug_acpi_ec, OID_AUTO, polled, CTLFLAG_RW, &ec_polled_mode, 0,
+ "Force use of polled mode (only if interrupt mode doesn't work)");
static int ec_timeout = EC_TIMEOUT;
TUNABLE_INT("debug.acpi.ec.timeout", &ec_timeout);
SYSCTL_INT(_debug_acpi_ec, OID_AUTO, timeout, CTLFLAG_RW, &ec_timeout,
EC_TIMEOUT, "Total time spent waiting for a response (poll+sleep)");
-static __inline ACPI_STATUS
-EcLock(struct acpi_ec_softc *sc, int serialize)
+static ACPI_STATUS
+EcLock(struct acpi_ec_softc *sc)
{
ACPI_STATUS status;
@@ -319,25 +206,14 @@
if (ACPI_FAILURE(status))
return (status);
}
-
- /*
- * If caller is executing a series of commands, acquire the exclusive lock
- * to serialize with other users.
- * To sync with bottom-half interrupt handler, always acquire the mutex.
- */
- if (serialize)
- ACPI_SERIAL_BEGIN(ec);
- mtx_lock(&sc->ec_mtx);
-
+ ACPI_SERIAL_BEGIN(ec);
return (status);
}
-static __inline void
+static void
EcUnlock(struct acpi_ec_softc *sc)
{
- mtx_unlock(&sc->ec_mtx);
- if (sx_xlocked(&ec_sxlock))
- ACPI_SERIAL_END(ec);
+ ACPI_SERIAL_END(ec);
if (sc->ec_glk)
AcpiReleaseGlobalLock(sc->ec_glkhandle);
}
@@ -349,7 +225,8 @@
ACPI_PHYSICAL_ADDRESS Address,
UINT32 width, ACPI_INTEGER *Value,
void *Context, void *RegionContext);
-static ACPI_STATUS EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event);
+static ACPI_STATUS EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event,
+ u_int gen_count);
static ACPI_STATUS EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd);
static ACPI_STATUS EcRead(struct acpi_ec_softc *sc, UINT8 Address,
UINT8 *Data);
@@ -390,7 +267,9 @@
* Look for an ECDT and if we find one, set up default GPE and
* space handlers to catch attempts to access EC space before
* we have a real driver instance in place.
- * TODO: if people report invalid ECDTs, add a tunable to disable them.
+ *
+ * TODO: Some old Gateway laptops need us to fake up an ECDT or
+ * otherwise attach early so that _REG methods can run.
*/
void
acpi_ec_ecdt_probe(device_t parent)
@@ -578,7 +457,6 @@
params = acpi_get_private(dev);
sc->ec_dev = dev;
sc->ec_handle = acpi_get_handle(dev);
- mtx_init(&sc->ec_mtx, "ACPI EC lock", NULL, MTX_DEF);
/* Retrieve previously probed values via device ivars. */
sc->ec_glk = params->glk;
@@ -661,7 +539,6 @@
if (sc->ec_data_res)
bus_release_resource(sc->ec_dev, SYS_RES_IOPORT, sc->ec_data_rid,
sc->ec_data_res);
- mtx_destroy(&sc->ec_mtx);
return (ENXIO);
}
@@ -715,57 +592,52 @@
KASSERT(Context != NULL, ("EcGpeQueryHandler called with NULL"));
/* Serialize user access with EcSpaceHandler(). */
- Status = EcLock(sc, TRUE);
+ Status = EcLock(sc);
if (ACPI_FAILURE(Status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "GpeQuery lock error: %s\n", AcpiFormatException(Status));
+ device_printf(sc->ec_dev, "GpeQuery lock error: %s\n",
+ AcpiFormatException(Status));
return;
}
/*
* Send a query command to the EC to find out which _Qxx call it
* wants to make. This command clears the SCI bit and also the
- * interrupt source since we are edge-triggered.
+ * interrupt source since we are edge-triggered. To prevent the GPE
+ * that may arise from running the query from causing another query
+ * to be queued, we clear the pending flag only after running it.
*/
Status = EcCommand(sc, EC_COMMAND_QUERY);
+ sc->ec_sci_pend = FALSE;
if (ACPI_FAILURE(Status)) {
EcUnlock(sc);
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "GPE query failed - %s\n", AcpiFormatException(Status));
- goto re_enable;
+ device_printf(sc->ec_dev, "GPE query failed: %s\n",
+ AcpiFormatException(Status));
+ return;
}
Data = EC_GET_DATA(sc);
- sc->ec_sci_pend = FALSE;
-
- /* Drop locks before evaluating _Qxx method since it may trigger GPEs. */
- EcUnlock(sc);
/* Ignore the value for "no outstanding event". (13.3.5) */
- CTR2(KTR_ACPI, "ec query ok,%s running _Q%02x", Data ? "" : " not", Data);
- if (Data == 0)
- goto re_enable;
+ CTR2(KTR_ACPI, "ec query ok,%s running _Q%02X", Data ? "" : " not", Data);
+ if (Data == 0) {
+ EcUnlock(sc);
+ return;
+ }
/* Evaluate _Qxx to respond to the controller. */
- snprintf(qxx, sizeof(qxx), "_Q%02x", Data);
+ snprintf(qxx, sizeof(qxx), "_Q%02X", Data);
AcpiUtStrupr(qxx);
Status = AcpiEvaluateObject(sc->ec_handle, qxx, NULL, NULL);
if (ACPI_FAILURE(Status) && Status != AE_NOT_FOUND) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "evaluation of GPE query method %s failed - %s\n",
- qxx, AcpiFormatException(Status));
+ device_printf(sc->ec_dev, "evaluation of query method %s failed: %s\n",
+ qxx, AcpiFormatException(Status));
}
-re_enable:
- /* Re-enable the GPE event so we'll get future requests. */
- Status = AcpiEnableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_ISR);
- if (ACPI_FAILURE(Status))
- printf("EcGpeQueryHandler: AcpiEnableEvent failed\n");
+ EcUnlock(sc);
}
/*
- * Handle a GPE. Currently we only handle SCI events as others must
- * be handled by polling in EcWaitEvent(). This is because some ECs
- * treat events as level when they should be edge-triggered.
+ * The GPE handler is called when IBE/OBF or SCI events occur. We are
+ * called from an unknown lock context.
*/
static uint32_t
EcGpeHandler(void *Context)
@@ -773,68 +645,32 @@
struct acpi_ec_softc *sc = Context;
ACPI_STATUS Status;
EC_STATUS EcStatus;
- int query_pend;
KASSERT(Context != NULL, ("EcGpeHandler called with NULL"));
+ CTR0(KTR_ACPI, "ec gpe handler start");
/*
- * Disable further GPEs while we handle this one. Since we are directly
- * called by ACPI-CA and it may have unknown locks held, we specify the
- * ACPI_ISR flag to keep it from acquiring any more mutexes (although
- * sleeping would be ok since we're in an ithread.)
+ * Notify EcWaitEvent() that the status register is now fresh. If we
+ * didn't do this, it wouldn't be possible to distinguish an old IBE
+ * from a new one, for example when doing a write transaction (writing
+ * address and then data values.)
*/
- AcpiDisableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_ISR);
-
- /* For interrupt (GPE) handler, don't acquire serialization lock. */
- Status = EcLock(sc, FALSE);
- if (ACPI_FAILURE(Status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "GpeQuery lock error: %s\n", AcpiFormatException(Status));
- return (-1);
- }
+ atomic_add_int(&sc->ec_gencount, 1);
+ wakeup(&sc->ec_gencount);
/*
- * If burst was active, but the status bit was cleared, the EC had to
- * exit burst mode for some reason. Record this for later.
+ * If the EC_SCI bit of the status register is set, queue a query handler.
+ * It will run the query and _Qxx method later, under the lock.
*/
EcStatus = EC_GET_CSR(sc);
- if (sc->ec_burstactive && (EcStatus & EC_FLAG_BURST_MODE) == 0) {
- CTR0(KTR_ACPI, "ec burst disabled in query handler");
- sc->ec_burstactive = FALSE;
- }
-
- /*
- * If the EC_SCI bit of the status register is not set, then pass
- * it along to any potential waiters as it may be an IBE/OBF event.
- * If it is set, queue a query handler.
- */
- query_pend = FALSE;
- if ((EcStatus & EC_EVENT_SCI) == 0) {
- CTR1(KTR_ACPI, "ec event was IBE/OBF, status %#x", EcStatus);
- sc->ec_csrvalue = EcStatus;
- wakeup(&sc->ec_csrvalue);
- } else if (!sc->ec_sci_pend) {
- /* SCI bit set and no pending query handler, so schedule one. */
- CTR0(KTR_ACPI, "ec queueing gpe handler");
+ if ((EcStatus & EC_EVENT_SCI) && !sc->ec_sci_pend) {
+ CTR0(KTR_ACPI, "ec gpe queueing query handler");
Status = AcpiOsExecute(OSL_GPE_HANDLER, EcGpeQueryHandler, Context);
- if (ACPI_SUCCESS(Status)) {
+ if (ACPI_SUCCESS(Status))
sc->ec_sci_pend = TRUE;
- query_pend = TRUE;
- } else
- printf("Queuing GPE query handler failed.\n");
- }
-
- /*
- * If we didn't queue a query handler, which will eventually re-enable
- * the GPE, re-enable it right now so we can get more events.
- */
- if (!query_pend) {
- Status = AcpiEnableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_ISR);
- if (ACPI_FAILURE(Status))
- printf("EcGpeHandler: AcpiEnableGpe failed\n");
+ else
+ printf("EcGpeHandler: queuing GPE query handler failed\n");
}
-
- EcUnlock(sc);
return (0);
}
@@ -878,8 +714,19 @@
EcAddr = Address;
Status = AE_ERROR;
- /* Grab serialization lock to hold across command sequence. */
- Status = EcLock(sc, TRUE);
+ /*
+ * If booting, check if we need to run the query handler. If so, we
+ * we call it directly here since our thread taskq is not active yet.
+ */
+ if (cold || rebooting) {
+ if ((EC_GET_CSR(sc) & EC_EVENT_SCI)) {
+ CTR0(KTR_ACPI, "ec running gpe handler directly");
+ EcGpeQueryHandler(sc);
+ }
+ }
+
+ /* Serialize with EcGpeQueryHandler() at transaction granularity. */
+ Status = EcLock(sc);
if (ACPI_FAILURE(Status))
return_ACPI_STATUS (Status);
@@ -910,83 +757,94 @@
}
static ACPI_STATUS
-EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event)
+EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event, u_int gen_count)
{
EC_STATUS EcStatus;
ACPI_STATUS Status;
- int count, i, retval, slp_ival;
+ int count, i, slp_ival;
ACPI_SERIAL_ASSERT(ec);
Status = AE_NO_HARDWARE_RESPONSE;
- EcStatus = 0;
/*
- * Poll for up to ec_poll_time microseconds since many ECs complete
- * the command quickly, especially if in burst mode.
+ * The main CPU should be much faster than the EC. So the status should
+ * be "not ready" when we start waiting. But if the main CPU is really
+ * slow, it's possible we see the current "ready" response. Since that
+ * can't be distinguished from the previous response in polled mode,
+ * this is a potential issue. We really should have interrupts enabled
+ * during boot so there is no ambiguity in polled mode. For now, let's
+ * collect some stats about how many systems actually have this issue.
*/
-#if 0 /* Enable this as a possible workaround if EC times out. */
- AcpiOsStall(EC_POLL_DELAY);
-#endif
- count = ec_poll_time / EC_POLL_DELAY;
- if (count <= 0)
- count = 1;
- for (i = 0; i < count; i++) {
+ if (cold || rebooting || ec_polled_mode) {
+ static int once;
+
EcStatus = EC_GET_CSR(sc);
- if (sc->ec_burstactive && (EcStatus & EC_FLAG_BURST_MODE) == 0) {
- CTR0(KTR_ACPI, "ec burst disabled in waitevent (poll)");
- sc->ec_burstactive = FALSE;
+ if (!once && EVENT_READY(Event, EcStatus)) {
+ device_printf(sc->ec_dev,
+ "warning: EC done before starting event wait\n");
+ once = 1;
}
- if (EVENT_READY(Event, EcStatus)) {
- CTR1(KTR_ACPI, "ec poll wait ready, status %#x", EcStatus);
- Status = AE_OK;
- break;
- }
- AcpiOsStall(EC_POLL_DELAY);
}
- /*
- * If we still don't have a response and we're up and running, wait up
- * to ec_timeout ms for completion, sleeping for chunks of 1 ms or the
- * smallest resolution hz supports.
- */
- slp_ival = 0;
- if (Status != AE_OK) {
- retval = ENXIO;
- if (!cold) {
- slp_ival = hz / 1000;
- if (slp_ival != 0) {
- count = ec_timeout / slp_ival;
- } else {
- /* hz has less than 1000 Hz resolution so scale timeout. */
- slp_ival = 1;
- count = ec_timeout / (1000 / hz);
- }
- } else
- count = ec_timeout;
+ /* Wait for event by polling or GPE (interrupt). */
+ if (cold || rebooting || ec_polled_mode) {
+ count = (ec_timeout * 1000) / EC_POLL_DELAY;
+ if (count <= 0)
+ count = 1;
for (i = 0; i < count; i++) {
- if (retval != 0)
- EcStatus = EC_GET_CSR(sc);
- else
- EcStatus = sc->ec_csrvalue;
- if (sc->ec_burstactive && (EcStatus & EC_FLAG_BURST_MODE) == 0) {
- CTR0(KTR_ACPI, "ec burst disabled in waitevent (slp)");
+ EcStatus = EC_GET_CSR(sc);
+ if (sc->ec_burstactive && !(EcStatus & EC_FLAG_BURST_MODE)) {
+ CTR0(KTR_ACPI, "ec burst disabled in waitevent (poll)");
sc->ec_burstactive = FALSE;
}
if (EVENT_READY(Event, EcStatus)) {
- CTR1(KTR_ACPI, "ec sleep wait ready, status %#x", EcStatus);
+ CTR1(KTR_ACPI, "ec poll wait ready, status %#x", EcStatus);
Status = AE_OK;
break;
}
- if (!cold) {
- retval = msleep(&sc->ec_csrvalue, &sc->ec_mtx, PZERO, "ecpoll",
- slp_ival);
- } else
- AcpiOsStall(1000);
+ AcpiOsStall(EC_POLL_DELAY);
+ }
+ } else {
+ slp_ival = hz / 1000;
+ if (slp_ival != 0) {
+ count = ec_timeout / slp_ival;
+ } else {
+ /* hz has less than 1000 Hz resolution so scale timeout. */
+ slp_ival = 1;
+ count = ec_timeout / (1000 / hz);
}
- }
+ /*
+ * Wait for the GPE to signal the status changed, checking the
+ * status register each time.
+ */
+ for (i = 0; i < count; i++) {
+ if (gen_count != sc->ec_gencount) {
+ /*
+ * Record new generation count. It's possible the GPE was
+ * just to notify us that a query is needed and we need to
+ * wait for a second GPE to signal the completion of the
+ * event we are actually waiting for.
+ */
+ gen_count = sc->ec_gencount;
+ EcStatus = EC_GET_CSR(sc);
+ if (sc->ec_burstactive && !(EcStatus & EC_FLAG_BURST_MODE)) {
+ CTR0(KTR_ACPI, "ec burst disabled in waitevent (sleep)");
+ sc->ec_burstactive = FALSE;
+ }
+ if (EVENT_READY(Event, EcStatus)) {
+ CTR1(KTR_ACPI, "ec sleep wait ready, status %#x", EcStatus);
+ Status = AE_OK;
+ break;
+ }
+ }
+ tsleep(&sc->ec_gencount, PZERO, "ecgpe", slp_ival);
+ }
+ }
+ if (Status != AE_OK)
+ CTR0(KTR_ACPI, "error: ec wait timed out");
return (Status);
-}
+}
static ACPI_STATUS
EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd)
@@ -994,6 +852,7 @@
ACPI_STATUS status;
EC_EVENT event;
EC_STATUS ec_status;
+ u_int gen_count;
ACPI_SERIAL_ASSERT(ec);
@@ -1013,15 +872,15 @@
event = EC_EVENT_OUTPUT_BUFFER_FULL;
break;
default:
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcCommand: Invalid command %#x\n", cmd);
+ device_printf(sc->ec_dev, "EcCommand: invalid command %#x\n", cmd);
return (AE_BAD_PARAMETER);
}
/* Run the command and wait for the chosen event. */
CTR1(KTR_ACPI, "ec running command %#x", cmd);
+ gen_count = sc->ec_gencount;
EC_SET_CSR(sc, cmd);
- status = EcWaitEvent(sc, event);
+ status = EcWaitEvent(sc, event, gen_count);
if (ACPI_SUCCESS(status)) {
/* If we succeeded, burst flag should now be present. */
if (cmd == EC_COMMAND_BURST_ENABLE) {
@@ -1029,11 +888,8 @@
if ((ec_status & EC_FLAG_BURST_MODE) == 0)
status = AE_ERROR;
}
- } else {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcCommand: no response to %#x\n", cmd);
- }
-
+ } else
+ device_printf(sc->ec_dev, "EcCommand: no response to %#x\n", cmd);
return (status);
}
@@ -1042,6 +898,7 @@
{
ACPI_STATUS status;
UINT8 data;
+ u_int gen_count;
ACPI_SERIAL_ASSERT(ec);
CTR1(KTR_ACPI, "ec read from %#x", Address);
@@ -1060,21 +917,20 @@
if (ACPI_FAILURE(status))
return (status);
+ gen_count = sc->ec_gencount;
EC_SET_DATA(sc, Address);
- status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL);
+ status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL, gen_count);
if (ACPI_FAILURE(status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcRead: Failed waiting for EC to send data.\n");
+ device_printf(sc->ec_dev, "EcRead: failed waiting to get data\n");
return (status);
}
-
*Data = EC_GET_DATA(sc);
if (sc->ec_burstactive) {
+ sc->ec_burstactive = FALSE;
status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
if (ACPI_FAILURE(status))
return (status);
- sc->ec_burstactive = FALSE;
CTR0(KTR_ACPI, "ec disabled burst ok");
}
@@ -1086,6 +942,7 @@
{
ACPI_STATUS status;
UINT8 data;
+ u_int gen_count;
ACPI_SERIAL_ASSERT(ec);
CTR2(KTR_ACPI, "ec write to %#x, data %#x", Address, *Data);
@@ -1104,27 +961,27 @@
if (ACPI_FAILURE(status))
return (status);
+ gen_count = sc->ec_gencount;
EC_SET_DATA(sc, Address);
- status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY);
+ status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count);
if (ACPI_FAILURE(status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcRead: Failed waiting for EC to process address\n");
+ device_printf(sc->ec_dev, "EcRead: failed waiting for sent address\n");
return (status);
}
+ gen_count = sc->ec_gencount;
EC_SET_DATA(sc, *Data);
- status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY);
+ status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count);
if (ACPI_FAILURE(status)) {
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "EcWrite: Failed waiting for EC to process data\n");
+ device_printf(sc->ec_dev, "EcWrite: failed waiting for sent data\n");
return (status);
}
if (sc->ec_burstactive) {
+ sc->ec_burstactive = FALSE;
status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
if (ACPI_FAILURE(status))
return (status);
- sc->ec_burstactive = FALSE;
CTR0(KTR_ACPI, "ec disabled burst ok");
}
--------------040800050701000906010408--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?46E45A6D.1010805>
