summaryrefslogtreecommitdiffstats
path: root/sys/dev/acpica/acpi_ec.c
diff options
context:
space:
mode:
authornjl <njl@FreeBSD.org>2003-07-20 21:11:32 +0000
committernjl <njl@FreeBSD.org>2003-07-20 21:11:32 +0000
commit6418f5291b52b67bb4500603d0dc313f266f9ebe (patch)
tree61de7f4b66f56e2cf6254be9879d8e921b23267c /sys/dev/acpica/acpi_ec.c
parent58a48c4e8775bdff6e5d683f1327ae90d7d1751f (diff)
downloadFreeBSD-src-6418f5291b52b67bb4500603d0dc313f266f9ebe.zip
FreeBSD-src-6418f5291b52b67bb4500603d0dc313f266f9ebe.tar.gz
Close a race condition by passing status retrieved via a non-SCI call
to EcGpeQueryHandler on to any waiting threads through the softc. Similar behavior was in the original version. Also: * Merge EcQuery into EcGpeQueryHandler to simplify locking * Hold EcLock from the initial read of the CSR down to the wakeup or until after the query command has been processed. * ec_gpebit only needs to be a UINT8
Diffstat (limited to 'sys/dev/acpica/acpi_ec.c')
-rw-r--r--sys/dev/acpica/acpi_ec.c75
1 files changed, 32 insertions, 43 deletions
diff --git a/sys/dev/acpica/acpi_ec.c b/sys/dev/acpica/acpi_ec.c
index c4a6617..1f9e4a3 100644
--- a/sys/dev/acpica/acpi_ec.c
+++ b/sys/dev/acpica/acpi_ec.c
@@ -247,7 +247,8 @@ typedef struct {
struct acpi_ec_softc {
device_t ec_dev;
ACPI_HANDLE ec_handle;
- UINT32 ec_gpebit;
+ UINT8 ec_gpebit;
+ UINT8 ec_csrvalue;
int ec_data_rid;
struct resource *ec_data_res;
@@ -262,7 +263,7 @@ struct acpi_ec_softc {
int ec_glk;
int ec_glkhandle;
struct mtx ec_mtx;
- UINT32 ec_polldelay;
+ int ec_polldelay;
};
/*
@@ -321,7 +322,6 @@ static ACPI_STATUS EcSpaceHandler(UINT32 Function,
UINT32 width, ACPI_INTEGER *Value,
void *Context, void *RegionContext);
static ACPI_STATUS EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event);
-static ACPI_STATUS EcQuery(struct acpi_ec_softc *sc, UINT8 *Data);
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);
@@ -590,29 +590,6 @@ acpi_ec_attach(device_t dev)
return (errval);
}
-static ACPI_STATUS
-EcQuery(struct acpi_ec_softc *sc, UINT8 *Data)
-{
- ACPI_STATUS Status;
-
- Status = EcLock(sc);
- if (ACPI_FAILURE(Status))
- return (Status);
-
- /*
- * 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.
- */
- Status = EcCommand(sc, EC_COMMAND_QUERY);
- if (ACPI_SUCCESS(Status))
- *Data = EC_GET_DATA(sc);
-
- EcUnlock(sc);
-
- return (Status);
-}
-
static void
EcGpeQueryHandler(void *Context)
{
@@ -625,32 +602,39 @@ EcGpeQueryHandler(void *Context)
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
KASSERT(Context != NULL, ("EcGpeQueryHandler called with NULL"));
+ 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));
+ return;
+ }
+
/*
- * Check status for EC_SCI.
- *
- * Bail out if the EC_SCI bit of the status register is not set.
- * Note that this function should only be called when
- * this bit is set (polling is used to detect IBE/OBF events).
- *
- * We don't acquire the global lock here but do protect against other
- * running commands (read/write/query) by grabbing ec_mtx.
+ * 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.
*/
- mtx_lock(&sc->ec_mtx);
EcStatus = EC_GET_CSR(sc);
- mtx_unlock(&sc->ec_mtx);
if ((EcStatus & EC_EVENT_SCI) == 0) {
- /* If it's not an SCI, wakeup the EcWaitEvent sleep. */
- wakeup(&sc->ec_polldelay);
+ sc->ec_csrvalue = EcStatus;
+ wakeup(&sc->ec_csrvalue);
+ EcUnlock(sc);
goto re_enable;
}
- /* Find out why the EC is signaling us. */
- Status = EcQuery(sc, &Data);
+ /*
+ * 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.
+ */
+ Status = EcCommand(sc, EC_COMMAND_QUERY);
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;
}
+ Data = EC_GET_DATA(sc);
+ EcUnlock(sc);
/* Ignore the value for "no outstanding event". (13.3.5) */
if (Data == 0)
@@ -770,7 +754,7 @@ EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event)
{
EC_STATUS EcStatus;
ACPI_STATUS Status;
- UINT32 i, period;
+ int i, period, retval;
mtx_assert(&sc->ec_mtx, MA_OWNED);
Status = AE_NO_HARDWARE_RESPONSE;
@@ -812,13 +796,18 @@ EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event)
* for completion, sleeping for chunks of 10 ms.
*/
if (Status != AE_OK) {
+ retval = -1;
for (i = 0; i < EC_POLL_TIMEOUT / 10; i++) {
- EcStatus = EC_GET_CSR(sc);
+ if (retval != 0)
+ EcStatus = EC_GET_CSR(sc);
+ else
+ EcStatus = sc->ec_csrvalue;
if (EVENT_READY(Event, EcStatus)) {
Status = AE_OK;
break;
}
- msleep(&sc->ec_polldelay, &sc->ec_mtx, PZERO, "ecpoll", 10/*ms*/);
+ retval = msleep(&sc->ec_csrvalue, &sc->ec_mtx, PZERO, "ecpoll",
+ 10/*ms*/);
}
}
OpenPOWER on IntegriCloud