Subject: New acpi_ec driver take 2
To: None <port-i386@netbsd.org>
From: Takayoshi Kochi <kochi@netbsd.org>
List: port-i386
Date: 08/18/2003 00:20:50
----Next_Part(Mon_Aug_18_00:20:50_2003_856)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

From: Takayoshi Kochi <kochi@netbsd.org>
Subject: New acpi_ec driver
Date: Sat, 16 Aug 2003 17:26:22 +0900 (JST)

> I'd like to import the new acpi_ec driver which is a port
> from Nate Lawson's work for FreeBSD.

This is update to acpi_ec driver.  This applies against
-current tree (NOT against my patch post yesterday).

Thanks to Takashi Yamamoto for finding out a potential
deadlock condition and proper usage of lock-related
APIs.

---
Takayoshi Kochi

----Next_Part(Mon_Aug_18_00:20:50_2003_856)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="acpi_ec.c.diff3"

--- acpi_ec.c-orig	2003-08-16 16:40:23.000000000 +0900
+++ acpi_ec.c	2003-08-17 22:29:08.000000000 +0900
@@ -36,6 +36,7 @@
  */
 
 /*-
+ * Copyright (c) 2003 Nate Lawson
  * Copyright (c) 2000 Michael Smith
  * Copyright (c) 2000 BSDi
  * All rights reserved.
@@ -207,11 +208,14 @@
 	int		sc_flags;	/* see below */
 
 	uint32_t	sc_csrvalue;	/* saved control register */
-	UINT32		sc_lockhandle;
+
+	UINT32		sc_glkhandle;
+	struct simplelock sc_lock;
+	uint32_t	sc_polldelay;
+	uint32_t	sc_poll_timeout;
 };
 
-#define	EC_F_LOCKED	0x01		/* EC is locked */
-#define	EC_F_PENDQUERY	0x02		/* query is pending */
+#define EC_F_USEGLK	0x01		/* Global Lock is required */
 
 #define	EC_DATA_READ(sc)						\
 	bus_space_read_1((sc)->sc_data_st, (sc)->sc_data_sh, 0)
@@ -223,43 +227,54 @@
 #define	EC_CSR_WRITE(sc, v)						\
 	bus_space_write_1((sc)->sc_csr_st, (sc)->sc_csr_sh, 0, (v))
 
-static __inline int
-EcIsLocked(struct acpi_ec_softc *sc)
-{
- 
-	return (acpi_is_global_locked() && (sc->sc_flags & EC_F_LOCKED));
-}
+/*
+ * XXX
+ * I couldn't find it in the spec but other implementations also use a
+ * value of 1 ms for the time to acquire global lock.
+ */
+#define EC_LOCK_TIMEOUT 1000
+
+/*
+ * Start with an interval of 1 us for status poll loop.  This delay
+ * will be dynamically adjusted based on the actual time waited.
+ */
+#define EC_POLL_DELAY	1
+
+/* Total time in ms spent in the poll loop waiting for a response. */
+#define EC_POLL_TIMEOUT 50
+
+#define EVENT_READY(event, status)			\
+	(((event) == EC_EVENT_OUTPUT_BUFFER_FULL &&	\
+	 ((status) & EC_FLAG_OUTPUT_BUFFER) != 0) ||	\
+	 ((event) == EC_EVENT_INPUT_BUFFER_EMPTY &&	\
+	 ((status) & EC_FLAG_INPUT_BUFFER) == 0))
 
 static __inline ACPI_STATUS
 EcLock(struct acpi_ec_softc *sc)
 {
-	ACPI_STATUS status;
-	UINT32 handle;
+	ACPI_STATUS status = AE_OK;
 
-	status = acpi_acquire_global_lock(&handle);
-	if (ACPI_SUCCESS(status)) {
-		sc->sc_flags |= EC_F_LOCKED;
-		sc->sc_lockhandle = handle;
+	/* if _GLK is non-zero, also acquire the global lock. */
+	if (sc->sc_flags & EC_F_USEGLK) {
+		/* XXXTK EC_LOCK_TIMEOUT */
+		status = acpi_acquire_global_lock(&sc->sc_glkhandle);
 	}
 
-	return (status);
+	/* Always acquire this EC's mutex */
+	if (ACPI_SUCCESS(status))
+		simple_lock(&sc->sc_lock);
+
+	return status;
 }
 
 static __inline void
 EcUnlock(struct acpi_ec_softc *sc)
 {
-	if (!EcIsLocked(sc))
-		return;
-	sc->sc_flags &= ~EC_F_LOCKED;
-	acpi_release_global_lock(sc->sc_lockhandle);
+	if (sc->sc_flags & EC_F_USEGLK)
+		acpi_release_global_lock(sc->sc_glkhandle);
+	simple_unlock(&sc->sc_lock);
 }
 
-typedef struct {
-	EC_COMMAND	Command;
-	UINT8		Address;
-	UINT8		Data;
-} EC_REQUEST;
-
 static void		EcGpeHandler(void *Context);
 static ACPI_STATUS	EcSpaceSetup(ACPI_HANDLE Region, UINT32 Function,
 			    void *Context, void **return_Context);
@@ -269,13 +284,12 @@
 			    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	EcTransaction(struct acpi_ec_softc *sc,
-			    EC_REQUEST *EcRequest);
+static ACPI_STATUS	EcCommand(struct acpi_ec_softc *sc,
+			    EC_COMMAND Command);
 static ACPI_STATUS	EcRead(struct acpi_ec_softc *sc, UINT8 Address,
 			    UINT8 *Data);
 static ACPI_STATUS	EcWrite(struct acpi_ec_softc *sc, UINT8 Address,
-			    UINT8 *Data);
+			    UINT8 Data);
 
 int	acpiec_match(struct device *, struct cfdata *, void *);
 void	acpiec_attach(struct device *, struct device *, void *);
@@ -294,12 +308,12 @@
 	struct acpi_attach_args *aa = aux;
 
 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
-		return (0);
+		return 0;
 
 	if (strcmp(aa->aa_node->ad_devinfo.HardwareId, "PNP0C09") == 0)
-		return (1);
+		return 1;
 
-	return (0);
+	return 0;
 }
 
 /*
@@ -313,6 +327,7 @@
 	struct acpi_ec_softc *sc = (void *) self;
 	struct acpi_attach_args *aa = aux;
 	struct acpi_io *io0, *io1;
+	UINT32 tmp;
 	ACPI_STATUS rv;
 
 	ACPI_FUNCTION_TRACE(__FUNCTION__);
@@ -320,15 +335,18 @@
 	printf(": ACPI Embedded Controller\n");
 
 	sc->sc_node = aa->aa_node;
+	simple_lock_init(&sc->sc_lock);
+	sc->sc_polldelay = EC_POLL_DELAY;
+	sc->sc_poll_timeout = EC_POLL_TIMEOUT;
 
 	/* Parse our resources. */
 	ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "parsing EC resources\n"));
 	rv = acpi_resource_parse(&sc->sc_dev, sc->sc_node, &sc->sc_res,
 	    &acpi_resource_parse_ops_default);
-	if (rv != AE_OK) {
+	if (ACPI_FAILURE(rv)) {
 		printf("%s: unable to parse resources: %d\n",
 		    sc->sc_dev.dv_xname, rv);
-		return;
+		return_VOID;
 	}
 
 	sc->sc_data_st = aa->aa_iot;
@@ -336,13 +354,13 @@
 	if (io0 == NULL) {
 		printf("%s: unable to find data register resource\n",
 		    sc->sc_dev.dv_xname);
-		return;
+		return_VOID;
 	}
 	if (bus_space_map(sc->sc_data_st, io0->ar_base, io0->ar_length,
 	    0, &sc->sc_data_sh) != 0) {
 		printf("%s: unable to map data register\n",
 		    sc->sc_dev.dv_xname);
-		return;
+		return_VOID;
 	}
 
 	sc->sc_csr_st = aa->aa_iot;
@@ -350,50 +368,53 @@
 	if (io1 == NULL) {
 		printf("%s: unable to find csr register resource\n",
 		    sc->sc_dev.dv_xname);
-		return;
+		return_VOID;
 	}
 	if (bus_space_map(sc->sc_csr_st, io1->ar_base, io1->ar_length,
 	    0, &sc->sc_csr_sh) != 0) {
 		printf("%s: unable to map csr register\n",
 		    sc->sc_dev.dv_xname);
-		return;
+		return_VOID;
 	}
 
+	/* Check if global lock should be used.  If not, leave flag as 0. */
+	rv = acpi_eval_integer(sc->sc_node->ad_handle, "_GLK", &tmp);
+	if (ACPI_SUCCESS(rv) && tmp == 1)
+		sc->sc_flags |= EC_F_USEGLK;
+
 	/*
 	 * Install GPE handler.
 	 *
 	 * We evaluate the _GPE method to find the GPE bit used by the
 	 * Embedded Controller to signal status (SCI).
 	 */
-	if ((rv = acpi_eval_integer(sc->sc_node->ad_handle, "_GPE",
-	     &sc->sc_gpebit)) != AE_OK) {
-		printf("%s: unable to evaluate _GPE: %d\n",
-		    sc->sc_dev.dv_xname, rv);
-		return;
+	rv = acpi_eval_integer(sc->sc_node->ad_handle, "_GPE", &sc->sc_gpebit);
+	if (ACPI_FAILURE(rv)) {
+		printf("%s: unable to evaluate _GPE: %s\n",
+		    sc->sc_dev.dv_xname, AcpiFormatException(rv));
+		return_VOID;
 	}
 
 	/*
-	 * Install a handler for this EC's GPE bit.  Note that EC SCIs are 
-	 * treated as both edge- and level-triggered interrupts; in other words
-	 * we clear the status bit immediately after getting an EC-SCI, then
-	 * again after we're done processing the event.  This guarantees that
-	 * events we cause while performing a transaction (e.g. IBE/OBF) get 
-	 * cleared before re-enabling the GPE.
+	 * Install a handler for this EC's GPE bit.  We want edge-triggered
+	 * behavior
 	 */
-	if ((rv = AcpiInstallGpeHandler(sc->sc_gpebit,
-	     ACPI_EVENT_LEVEL_TRIGGERED | ACPI_EVENT_EDGE_TRIGGERED,
-	     EcGpeHandler, sc)) != AE_OK) {
-		printf("%s: unable to install GPE handler: %d\n",
-		    sc->sc_dev.dv_xname, rv);
-		return;
+	rv = AcpiInstallGpeHandler(sc->sc_gpebit,
+		ACPI_EVENT_EDGE_TRIGGERED,
+		EcGpeHandler, sc);
+	if (ACPI_FAILURE(rv)) {
+		printf("%s: unable to install GPE handler: %s\n",
+		    sc->sc_dev.dv_xname, AcpiFormatException(rv));
+		return_VOID;
 	}
 
 	/* Install address space handler. */
-	if ((rv = AcpiInstallAddressSpaceHandler(sc->sc_node->ad_handle,
-	     ACPI_ADR_SPACE_EC, EcSpaceHandler, EcSpaceSetup, sc)) != AE_OK) {
-		printf("%s: unable to install address space handler: %d\n",
-		    sc->sc_dev.dv_xname, rv);
-		return;
+	rv = AcpiInstallAddressSpaceHandler(sc->sc_node->ad_handle,
+		ACPI_ADR_SPACE_EC, EcSpaceHandler, EcSpaceSetup, sc);
+	if (ACPI_FAILURE(rv)) {
+		printf("%s: unable to install address space handler: %s\n",
+		    sc->sc_dev.dv_xname, AcpiFormatException(rv));
+		return_VOID;
 	}
 
 	return_VOID;
@@ -405,62 +426,72 @@
 	struct acpi_ec_softc *sc = Context;
 	UINT8 Data;
 	ACPI_STATUS Status;
+	EC_STATUS EcStatus;
 	char qxx[5];
 
 	ACPI_FUNCTION_TRACE(__FUNCTION__);
 
-	for (;;) {
-		/*
-		 * Check 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).
-		 *
-		 * It is safe to do this without locking the controller, as
-		 * it's OK to call EcQuery when there's no data ready; in the
-		 * worst case we should just find nothing waiting for us and
-		 * bail.
-		 */
-		if ((EC_CSR_READ(sc) & EC_EVENT_SCI) == 0)
-			break;
+	Status = EcLock(sc);
+	if (ACPI_FAILURE(Status)) {
+		printf("%s: EcGpeQueryHandler lock error: %s\n",
+		       sc->sc_dev.dv_xname, AcpiFormatException(Status));
+		return_VOID;
+	}
 
-		/*
-		 * Find out why the EC is signalling us
-		 */
-		Status = EcQuery(sc, &Data);
-	    
-		/*
-		 * If we failed to get anything from the EC, give up.
-		 */
-		if (Status != AE_OK) {
-			printf("%s: GPE query failed: %d\n",
-			    sc->sc_dev.dv_xname, Status);
-			break;
-		}
+	/*
+	 * 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
+	 * orunning commands (read/write/query) by grabbing sc_lock.
+	 */
+	EcStatus = EC_CSR_READ(sc);
+	if ((EcStatus & EC_EVENT_SCI) == 0) {
+		/* If it's not an SCI, wakeup the EcWaitEvent sleep. */
+		sc->sc_csrvalue = EcStatus;
+		wakeup(&sc->sc_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.
+	 */
+	Status = EcCommand(sc, EC_COMMAND_QUERY);
+	if (ACPI_FAILURE(Status)) {
+		EcUnlock(sc);
+		printf("%s: GPE query failed: %s\n",
+		       sc->sc_dev.dv_xname,
+		       AcpiFormatException(Status));
+		goto re_enable;
+	}
+	Data = EC_DATA_READ(sc);
+	EcUnlock(sc);
 
-		/*
-		 * Evaluate _Qxx to respond to the controller.
-		 */
-		sprintf(qxx, "_Q%02x", Data);
-		strupr(qxx);
-		Status = AcpiEvaluateObject(sc->sc_node->ad_handle, qxx,
+	/* Ignore the value for "no outstanding event". (13.3.5) */
+	if (Data == 0)
+		goto re_enable;
+
+	/* Evaluate _Qxx to respond to the controller. */
+	sprintf(qxx, "_Q%02x", Data);
+	strupr(qxx);
+	Status = AcpiEvaluateObject(sc->sc_node->ad_handle, qxx,
 		    NULL, NULL);
 
-		/*
-		 * Ignore spurious query requests.
-		 */
-		if (Status != AE_OK &&
-		    (Data != 0 || Status != AE_NOT_FOUND)) {
-			printf("%s: evaluation of GPE query method %s "
-			    "failed: %d\n", sc->sc_dev.dv_xname, qxx, Status);
-		}
+	if (ACPI_FAILURE(Status) && Status != AE_NOT_FOUND) {
+		printf("%s: evaluation of GPE query method %s "
+		    "failed: %d\n", sc->sc_dev.dv_xname, qxx, Status);
 	}
 
-	/* I know I request Level trigger cleanup */
-	if (AcpiClearEvent(sc->sc_gpebit, ACPI_EVENT_GPE) != AE_OK)
-		printf("%s: AcpiClearEvent failed\n", sc->sc_dev.dv_xname);
-	if (AcpiEnableEvent(sc->sc_gpebit, ACPI_EVENT_GPE, 0) != AE_OK)
+ re_enable:
+	/* Re-enable the GPE event so we'll get future requests. */
+	Status = AcpiEnableEvent(sc->sc_gpebit, ACPI_EVENT_GPE, 0);
+	if (ACPI_FAILURE(Status))
 		printf("%s: AcpiEnableEvent failed\n", sc->sc_dev.dv_xname);
 
 	return_VOID;
@@ -470,29 +501,19 @@
 EcGpeHandler(void *Context)
 {
 	struct acpi_ec_softc *sc = Context;
-	uint32_t csrvalue;
+	ACPI_STATUS Status;
 
-	/* 
-	 * If EC is locked, the intr must process EcRead/Write wait only.
-	 * Query request must be pending.
-	 */
-	if (EcIsLocked(sc)) {
-		csrvalue = EC_CSR_READ(sc);
-		if (csrvalue & EC_EVENT_SCI)
-			sc->sc_flags |= EC_F_PENDQUERY;
-
-		if ((csrvalue & EC_FLAG_OUTPUT_BUFFER) != 0 ||
-		    (csrvalue & EC_FLAG_INPUT_BUFFER) == 0) {
-			sc->sc_csrvalue = csrvalue;
-			wakeup(&sc->sc_csrvalue);
-		}
-	} else {
-		/* Enqueue GpeQuery handler. */
-		if (AcpiOsQueueForExecution(OSD_PRIORITY_HIGH,
-		    EcGpeQueryHandler, Context) != AE_OK) {
-			printf("%s: failed to enqueue query handler\n",
-			    sc->sc_dev.dv_xname);
-		}
+	/* Disable further GPEs while we handle this one. */
+	AcpiDisableEvent(sc->sc_gpebit, ACPI_EVENT_GPE, 0);
+
+	/* Schedule the GPE query handler. */
+	Status = AcpiOsQueueForExecution(OSD_PRIORITY_GPE, EcGpeQueryHandler,
+		    Context);
+	if (ACPI_FAILURE(Status)) {
+		printf("Queuing GPE query handler failed.\n");
+		Status = AcpiEnableEvent(sc->sc_gpebit, ACPI_EVENT_GPE, 0);
+		if (ACPI_FAILURE(Status))
+			printf("EcGpeHandler: AcpiEnableGpe failed.\n");
 	}
 }
 
@@ -503,9 +524,7 @@
 
 	ACPI_FUNCTION_TRACE(__FUNCTION__);
 
-	/*
-	 * Just pass the context through, there's nothing to do here.
-	 */
+	/* Just pass the context through, there's nothing to do here. */
 	*RegionContext = Context;
 
 	return_ACPI_STATUS(AE_OK);
@@ -517,211 +536,166 @@
 {
 	struct acpi_ec_softc *sc = Context;
 	ACPI_STATUS Status = AE_OK;
-	EC_REQUEST EcRequest;
+	UINT8 EcAddr, EcData;
 	int i;
 
 	ACPI_FUNCTION_TRACE_U32(__FUNCTION__, (UINT32)Address);
 
-	if ((Address > 0xFF) || (width % 8 != 0) || (Value == NULL) ||
-	    (Context == NULL))
-		return_ACPI_STATUS(AE_BAD_PARAMETER);
-
-	switch (Function) {
-	case ACPI_READ:
-		EcRequest.Command = EC_COMMAND_READ;
-		EcRequest.Address = Address;
-		(*Value) = 0;
-		break;
-
-	case ACPI_WRITE:
-		EcRequest.Command = EC_COMMAND_WRITE;
-		EcRequest.Address = Address;
-		break;
-
-	default:
-		printf("%s: invalid Address Space function: %d\n",
-		    sc->sc_dev.dv_xname, Function);
+	if (width % 8 != 0 || Value == NULL || Context == NULL ||
+	    Address + width / 8 > 0x100)
 		return_ACPI_STATUS(AE_BAD_PARAMETER);
-	}
-
-	/*
-	 * Perform the transaction.
-	 */
-	for (i = 0; i < width; i += 8) {
-		if (Function == ACPI_READ)
-			EcRequest.Data = 0;
-		else
-			EcRequest.Data = (UINT8)((*Value) >> i);
 
-		if ((Status = EcTransaction(sc, &EcRequest)) != AE_OK)
+	/* Perform the transaction. */
+	EcAddr = Address;
+	if (Function == ACPI_READ)
+		*Value = (ACPI_INTEGER)0;
+	for (i = 0; i < width / 8; i++) {
+		Status = EcLock(sc);
+		if (ACPI_FAILURE(Status))
+			return Status;
+
+		switch (Function) {
+		case ACPI_READ:
+			EcData = 0;
+			Status = EcRead(sc, EcAddr, &EcData);
 			break;
+		case ACPI_WRITE:
+			EcData = (UINT8)((*Value) >> i);
+			Status = EcWrite(sc, EcAddr, EcData);
+			break;
+		default:
+			printf("%s: invalid Address Space function: %d\n",
+			       sc->sc_dev.dv_xname, Function);
+			Status = AE_BAD_PARAMETER;
+			break;
+		}
 
-		(*Value) |= (UINT32)EcRequest.Data << i;
-		if (++EcRequest.Address == 0)
-			return_ACPI_STATUS(AE_BAD_PARAMETER);
-	}
-
-	return_ACPI_STATUS(Status);
-}
-
-static ACPI_STATUS
-EcWaitEventIntr(struct acpi_ec_softc *sc, EC_EVENT Event)
-{
-	EC_STATUS EcStatus;
-	int i;
-
-	ACPI_FUNCTION_TRACE_U32(__FUNCTION__, (UINT32)Event);
-
-	/* XXX Need better test for "yes, you have interrupts". */
-	if (cold)
-		return_ACPI_STATUS(EcWaitEvent(sc, Event));
-
-	if (EcIsLocked(sc) == 0)
-		printf("%s: EcWaitEventIntr called without EC lock!\n",
-		    sc->sc_dev.dv_xname);
+		EcUnlock(sc);
+		if (ACPI_FAILURE(Status))
+			return_ACPI_STATUS(Status);
 
-	EcStatus = EC_CSR_READ(sc);
-
-	/* Too long? */
-	for (i = 0; i < 10; i++) {
-		/* Check EC status against the desired event. */
-		if ((Event == EC_EVENT_OUTPUT_BUFFER_FULL) &&
-		    (EcStatus & EC_FLAG_OUTPUT_BUFFER) != 0)
-			return_ACPI_STATUS(AE_OK);
-      
-		if ((Event == EC_EVENT_INPUT_BUFFER_EMPTY) &&
-		    (EcStatus & EC_FLAG_INPUT_BUFFER) == 0)
-			return_ACPI_STATUS(AE_OK);
-
-		sc->sc_csrvalue = 0;
-		/* XXXJRT Sleeping with a lock held? */
-		if (tsleep(&sc->sc_csrvalue, 0, "EcWait", 1) != EWOULDBLOCK)
-			EcStatus = sc->sc_csrvalue;
-		else
-			EcStatus = EC_CSR_READ(sc);
+		*Value |= (ACPI_INTEGER)EcData << i;
+		++EcAddr;
 	}
-	return_ACPI_STATUS(AE_ERROR);
+	return_ACPI_STATUS(Status);
 }
 
 static ACPI_STATUS
 EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event)
 {
 	EC_STATUS EcStatus;
-	UINT32 i = 0;
+	ACPI_STATUS Status;
+	int i, period, retval;
+#ifdef ACPI_DEBUG
+	static int EcDbgMaxDelay;
+#endif
 
-	if (EcIsLocked(sc) == 0)
-		printf("%s: EcWaitEvent called without EC lock!\n",
-		    sc->sc_dev.dv_xname);
+	LOCK_ASSERT(simple_lock_held(&sc->sc_lock));
+
+	Status = AE_NO_HARDWARE_RESPONSE;
 
 	/*
-	 * Stall 1us:
-	 * ----------
-	 * Stall for 1 microsecond before reading the status register
-	 * for the first time.  This allows the EC to set the IBF/OBF
-	 * bit to its proper state.
-	 *
-	 * XXX it is not clear why we read the CSR twice.
+	 * 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);
-	EcStatus = EC_CSR_READ(sc);
 
 	/*
-	 * Wait For Event:
-	 * ---------------
 	 * Poll the EC status register to detect completion of the last
-	 * command.  Wait up to 10ms (in 100us chunks) for this to occur.
+	 * command.  First, wait up to 1 ms in chunks of sc->sc_polldelay
+	 * microseconds.
 	 */
-	for (i = 0; i < 100; i++) {
+	for (i = 0; i < 1000 / sc->sc_polldelay; i++) {
 		EcStatus = EC_CSR_READ(sc);
-
-		if ((Event == EC_EVENT_OUTPUT_BUFFER_FULL) &&
-		    (EcStatus & EC_FLAG_OUTPUT_BUFFER) != 0)
-			return (AE_OK);
-
-		if ((Event == EC_EVENT_INPUT_BUFFER_EMPTY) &&
-		    (EcStatus & EC_FLAG_INPUT_BUFFER) == 0)
-			return (AE_OK);
-
-		AcpiOsStall(10);
+		if (EVENT_READY(Event, EcStatus)) {
+			Status = AE_OK;
+			break;
+		}
+		AcpiOsStall(sc->sc_polldelay);
 	}
 
-	return (AE_ERROR);
-}    
+	/* Scale poll delay by the amount of time actually waited. */
+	period = i * sc->sc_polldelay;
+	if (period <= 5)
+		sc->sc_polldelay = 1;
+	else if (period <= 20)
+		sc->sc_polldelay = 5;
+	else if (period <= 100)
+		sc->sc_polldelay = 10;
+	else
+		sc->sc_polldelay = 100;
 
-static ACPI_STATUS
-EcQuery(struct acpi_ec_softc *sc, UINT8 *Data)
-{
-	ACPI_STATUS Status;
-
-	if ((Status = EcLock(sc)) != AE_OK)
-		return (Status);
-
-	EC_CSR_WRITE(sc, EC_COMMAND_QUERY);
-	Status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL);
-	if (Status == AE_OK)
-		*Data = EC_DATA_READ(sc);
-
-	EcUnlock(sc);
+	/*
+	 * If we still don't have a response, wait up to sc_poll_timeout ms
+	 * for completion, sleeping for chunks of 10 ms.
+	 */
+	if (Status != AE_OK) {
+		retval = -1;
+		for (i = 0; i < sc->sc_poll_timeout / 10; i++) {
+			if (retval != 0)
+				EcStatus = EC_CSR_READ(sc);
+			else
+				EcStatus = sc->sc_csrvalue;
+			if (EVENT_READY(Event, EcStatus)) {
+				Status = AE_OK;
+				break;
+			}
+			retval = ltsleep(&sc->sc_csrvalue, 0, "EcWait",
+					 hz / 100 /* 10ms */,
+					 &sc->sc_lock);
+		}
+	}
 
-	if (Status != AE_OK)
-		printf("%s: timed out waiting for EC to respond to "
-		    "EC_COMMAND_QUERY\n", sc->sc_dev.dv_xname);
+	/* Calculate new delay */
+	if (period == 1000)
+		period += i * 10000;
+#ifdef ACPI_DEBUG
+	/* print the new delay if it exceeds the max. */
+	if (period > EcDbgMaxDelay) {
+		EcDbgMaxDelay = period;
+		printf("%s: info: new max delay is %d us\n",
+		       sc->sc_dev.dv_xname, period);
+	}
+#endif
 
-	return (Status);
-}    
+	return Status;
+}
 
 static ACPI_STATUS
-EcTransaction(struct acpi_ec_softc *sc, EC_REQUEST *EcRequest)
+EcCommand(struct acpi_ec_softc *sc, EC_COMMAND Command)
 {
 	ACPI_STATUS Status;
+	EC_EVENT Event;
 
-	if ((Status = EcLock(sc)) != AE_OK)
-		return (Status);
+	LOCK_ASSERT(simple_lock_held(&sc->sc_lock));
 
-	/*
-	 * Perform the transaction.
-	 */
-	switch (EcRequest->Command) {
+	switch (Command) {
 	case EC_COMMAND_READ:
-		Status = EcRead(sc, EcRequest->Address, &(EcRequest->Data));
-		break;
-
 	case EC_COMMAND_WRITE:
-		Status = EcWrite(sc, EcRequest->Address, &(EcRequest->Data));
+	case EC_COMMAND_BURST_DISABLE:
+		Event = EC_EVENT_INPUT_BUFFER_EMPTY;
 		break;
-
-	default:
-		Status = AE_SUPPORT;
+	case EC_COMMAND_QUERY:
+	case EC_COMMAND_BURST_ENABLE:
+		Event = EC_EVENT_OUTPUT_BUFFER_FULL;
 		break;
+	default:
+		printf("%s: EcCommand: Invalid Command  %#x\n",
+		       sc->sc_dev.dv_xname, Command);
+		return AE_BAD_PARAMETER;
 	}
 
-	/*
-	 * Clear & Re-Enable the EC GPE:
-	 * -----------------------------
-	 * 'Consume' any EC GPE events that we generated while performing
-	 * the transaction (e.g. IBF/OBF). Clearing the GPE here shouldn't
-	 * have an adverse affect on outstanding EC-SCI's, as the source
-	 * (EC-SCI) will still be high and thus should trigger the GPE
-	 * immediately after we re-enabling it.
-	 */
-	if (sc->sc_flags & EC_F_PENDQUERY) {
-		if (AcpiOsQueueForExecution(OSD_PRIORITY_HIGH,
-		    EcGpeQueryHandler, sc) != AE_OK)
-			printf("%s: unable to queue pending query\n",
-			    sc->sc_dev.dv_xname);
-		sc->sc_flags &= ~EC_F_PENDQUERY;
-	}
-
-	if (AcpiClearEvent(sc->sc_gpebit, ACPI_EVENT_GPE) != AE_OK)
-		printf("%s: EcRequest: unable to clear EC GPE\n",
-		    sc->sc_dev.dv_xname);
-	if (AcpiEnableEvent(sc->sc_gpebit, ACPI_EVENT_GPE, 0) != AE_OK)
-		printf("%s: EcRequest: unable to reenable EC GPE\n",
-		    sc->sc_dev.dv_xname);
+	/* Run the command and wait for the chosen event. */
+	EC_CSR_WRITE(sc, Command);
+	Status = EcWaitEvent(sc, Event);
 
-	EcUnlock(sc);
+	if (ACPI_FAILURE(Status)) {
+		printf("%s: EcCommand: no response to %#x\n",
+		       sc->sc_dev.dv_xname, Command);
+	}
 
-	return(Status);
+	return Status;
 }
 
 static ACPI_STATUS
@@ -729,71 +703,77 @@
 {
 	ACPI_STATUS Status;
 
-	if (EcIsLocked(sc) == 0)
-		printf("%s: EcRead called without EC lock!\n",
-		    sc->sc_dev.dv_xname);
-
-	/* EcBurstEnable(EmbeddedController); */
+	LOCK_ASSERT(simple_lock_held(&sc->sc_lock));
 
-	EC_CSR_WRITE(sc, EC_COMMAND_READ);
-	if ((Status = EcWaitEventIntr(sc, EC_EVENT_INPUT_BUFFER_EMPTY)) !=
-	    AE_OK) {
-		printf("%s: EcRead: timeout waiting for EC to process "
-		    "read command\n", sc->sc_dev.dv_xname);
-		return (Status);
-	}
+#ifdef notyet
+	/* If we can't start burst mode, continue anyway. */
+	EcCommand(sc, EC_COMMAND_BURST_ENABLE);
+#endif
+
+	Status = EcCommand(sc, EC_COMMAND_READ);
+	if (ACPI_FAILURE(Status))
+		return Status;
 
 	EC_DATA_WRITE(sc, Address);
-	if ((Status = EcWaitEventIntr(sc, EC_EVENT_OUTPUT_BUFFER_FULL)) !=
-	    AE_OK) {
+	Status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL);
+	if (ACPI_FAILURE(Status)) {
 		printf("%s: EcRead: timeout waiting for EC to send data\n",
 		    sc->sc_dev.dv_xname);
-		return (Status);
+		return Status;
 	}
 
-	(*Data) = EC_DATA_READ(sc);
+	*Data = EC_DATA_READ(sc);
 
-	/* EcBurstDisable(EmbeddedController); */
+#ifdef notyet
+	if (sc->sc_burstactive) {
+		Status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
+		if (ACPI_FAILURE(Status))
+			return Status;
+	}
+#endif
 
-	return (AE_OK);
-}    
+	return AE_OK;
+}
 
 static ACPI_STATUS
-EcWrite(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data)
+EcWrite(struct acpi_ec_softc *sc, UINT8 Address, UINT8 Data)
 {
 	ACPI_STATUS Status;
 
-	if (EcIsLocked(sc) == 0)
-		printf("%s: EcWrite called without EC lock!\n",
-		    sc->sc_dev.dv_xname);
+	LOCK_ASSERT(simple_lock_held(&sc->sc_lock));
 
-	/* EcBurstEnable(EmbeddedController); */
-
-	EC_CSR_WRITE(sc, EC_COMMAND_WRITE);
-	if ((Status = EcWaitEventIntr(sc, EC_EVENT_INPUT_BUFFER_EMPTY)) !=
-	    AE_OK) {
-		printf("%s: EcWrite: timeout waiting for EC to process "
-		    "write command\n", sc->sc_dev.dv_xname);
-		return (Status);
-	}
+#ifdef notyet
+	/* If we can't start burst mode, continue anyway. */
+	EcCommand(sc, EC_COMMAND_BURST_ENABLE);
+#endif
+
+	Status = EcCommand(sc, EC_COMMAND_WRITE);
+	if (ACPI_FAILURE(Status))
+		return Status;
 
 	EC_DATA_WRITE(sc, Address);
-	if ((Status = EcWaitEventIntr(sc, EC_EVENT_INPUT_BUFFER_EMPTY)) !=
-	    AE_OK) {
+	Status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY);
+	if (ACPI_FAILURE(Status)) {
 		printf("%s: EcWrite: timeout waiting for EC to process "
 		    "address\n", sc->sc_dev.dv_xname);
-		return (Status);
+		return Status;
 	}
 
-	EC_DATA_WRITE(sc, *Data);
-	if ((Status = EcWaitEventIntr(sc, EC_EVENT_INPUT_BUFFER_EMPTY)) !=
-	    AE_OK) {
+	EC_DATA_WRITE(sc, Data);
+	Status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY);
+	if (ACPI_FAILURE(Status)) {
 		printf("%s: EcWrite: timeout waiting for EC to process "
 		    "data\n", sc->sc_dev.dv_xname);
-		return (Status);
+		return Status;
 	}
 
-	/* EcBurstDisable(EmbeddedController); */
+#ifdef notyet
+	if (sc->ec_burstactive) {
+		Status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
+		if (ACPI_FAILURE(Status))
+			return Status;
+	}
+#endif
 
-	return (AE_OK);
+	return AE_OK;
 }

----Next_Part(Mon_Aug_18_00:20:50_2003_856)----