xref: /freebsd/sys/dev/acpica/acpi_ec.c (revision 68fb6c48583442593812cfc399eaa659cf5b8ef7)
115e32d5dSMike Smith /*-
283dcc133SNate Lawson  * Copyright (c) 2003-2007 Nate Lawson
315e32d5dSMike Smith  * Copyright (c) 2000 Michael Smith
415e32d5dSMike Smith  * Copyright (c) 2000 BSDi
515e32d5dSMike Smith  * All rights reserved.
615e32d5dSMike Smith  *
715e32d5dSMike Smith  * Redistribution and use in source and binary forms, with or without
815e32d5dSMike Smith  * modification, are permitted provided that the following conditions
915e32d5dSMike Smith  * are met:
1015e32d5dSMike Smith  * 1. Redistributions of source code must retain the above copyright
1115e32d5dSMike Smith  *    notice, this list of conditions and the following disclaimer.
1215e32d5dSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
1315e32d5dSMike Smith  *    notice, this list of conditions and the following disclaimer in the
1415e32d5dSMike Smith  *    documentation and/or other materials provided with the distribution.
1515e32d5dSMike Smith  *
1615e32d5dSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1715e32d5dSMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1815e32d5dSMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1915e32d5dSMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2015e32d5dSMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2115e32d5dSMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2215e32d5dSMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2315e32d5dSMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2415e32d5dSMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2515e32d5dSMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2615e32d5dSMike Smith  * SUCH DAMAGE.
2715e32d5dSMike Smith  */
2815e32d5dSMike Smith 
29aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
30aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
31aad970f1SDavid E. O'Brien 
3215e32d5dSMike Smith #include "opt_acpi.h"
3315e32d5dSMike Smith #include <sys/param.h>
3415e32d5dSMike Smith #include <sys/kernel.h>
3515e32d5dSMike Smith #include <sys/bus.h>
364e7f640dSJohn Baldwin #include <sys/lock.h>
370025fb0fSNate Lawson #include <sys/malloc.h>
38b0eefa38SNate Lawson #include <sys/module.h>
39b0eefa38SNate Lawson #include <sys/sx.h>
4015e32d5dSMike Smith 
4115e32d5dSMike Smith #include <machine/bus.h>
4215e32d5dSMike Smith #include <machine/resource.h>
4315e32d5dSMike Smith #include <sys/rman.h>
4415e32d5dSMike Smith 
452a191126SDavid E. O'Brien #include <contrib/dev/acpica/acpi.h>
4615e32d5dSMike Smith #include <dev/acpica/acpivar.h>
4715e32d5dSMike Smith 
48f4b7de15SNate Lawson /* Hooks for the ACPI CA debugging infrastructure */
49a9cf0dffSMike Smith #define _COMPONENT	ACPI_EC
50dbd0058aSMike Smith ACPI_MODULE_NAME("EC")
510ae55423SMike Smith 
52da3b867eSMike Smith /*
53da3b867eSMike Smith  * EC_COMMAND:
54da3b867eSMike Smith  * -----------
55da3b867eSMike Smith  */
56da3b867eSMike Smith typedef UINT8				EC_COMMAND;
57da3b867eSMike Smith 
58da3b867eSMike Smith #define EC_COMMAND_UNKNOWN		((EC_COMMAND) 0x00)
59da3b867eSMike Smith #define EC_COMMAND_READ			((EC_COMMAND) 0x80)
60da3b867eSMike Smith #define EC_COMMAND_WRITE		((EC_COMMAND) 0x81)
61da3b867eSMike Smith #define EC_COMMAND_BURST_ENABLE		((EC_COMMAND) 0x82)
62da3b867eSMike Smith #define EC_COMMAND_BURST_DISABLE	((EC_COMMAND) 0x83)
63da3b867eSMike Smith #define EC_COMMAND_QUERY		((EC_COMMAND) 0x84)
64da3b867eSMike Smith 
65da3b867eSMike Smith /*
66da3b867eSMike Smith  * EC_STATUS:
67da3b867eSMike Smith  * ----------
68da3b867eSMike Smith  * The encoding of the EC status register is illustrated below.
69da3b867eSMike Smith  * Note that a set bit (1) indicates the property is TRUE
70da3b867eSMike Smith  * (e.g. if bit 0 is set then the output buffer is full).
71da3b867eSMike Smith  * +-+-+-+-+-+-+-+-+
72da3b867eSMike Smith  * |7|6|5|4|3|2|1|0|
73da3b867eSMike Smith  * +-+-+-+-+-+-+-+-+
74da3b867eSMike Smith  *  | | | | | | | |
75da3b867eSMike Smith  *  | | | | | | | +- Output Buffer Full?
76da3b867eSMike Smith  *  | | | | | | +--- Input Buffer Full?
77da3b867eSMike Smith  *  | | | | | +----- <reserved>
78da3b867eSMike Smith  *  | | | | +------- Data Register is Command Byte?
79da3b867eSMike Smith  *  | | | +--------- Burst Mode Enabled?
80da3b867eSMike Smith  *  | | +----------- SCI Event?
81da3b867eSMike Smith  *  | +------------- SMI Event?
82ef2374f7SNate Lawson  *  +--------------- <reserved>
83da3b867eSMike Smith  *
84da3b867eSMike Smith  */
85da3b867eSMike Smith typedef UINT8				EC_STATUS;
86da3b867eSMike Smith 
87da3b867eSMike Smith #define EC_FLAG_OUTPUT_BUFFER		((EC_STATUS) 0x01)
88da3b867eSMike Smith #define EC_FLAG_INPUT_BUFFER		((EC_STATUS) 0x02)
89ef2374f7SNate Lawson #define EC_FLAG_DATA_IS_CMD		((EC_STATUS) 0x08)
90da3b867eSMike Smith #define EC_FLAG_BURST_MODE		((EC_STATUS) 0x10)
91da3b867eSMike Smith 
92da3b867eSMike Smith /*
93da3b867eSMike Smith  * EC_EVENT:
94da3b867eSMike Smith  * ---------
95da3b867eSMike Smith  */
96da3b867eSMike Smith typedef UINT8				EC_EVENT;
97da3b867eSMike Smith 
98da3b867eSMike Smith #define EC_EVENT_UNKNOWN		((EC_EVENT) 0x00)
99da3b867eSMike Smith #define EC_EVENT_OUTPUT_BUFFER_FULL	((EC_EVENT) 0x01)
100da3b867eSMike Smith #define EC_EVENT_INPUT_BUFFER_EMPTY	((EC_EVENT) 0x02)
101da3b867eSMike Smith #define EC_EVENT_SCI			((EC_EVENT) 0x20)
102ef2374f7SNate Lawson #define EC_EVENT_SMI			((EC_EVENT) 0x40)
103ef2374f7SNate Lawson 
104ef2374f7SNate Lawson /* Data byte returned after burst enable indicating it was successful. */
105ef2374f7SNate Lawson #define EC_BURST_ACK			0x90
106da3b867eSMike Smith 
107da3b867eSMike Smith /*
108da3b867eSMike Smith  * Register access primitives
109da3b867eSMike Smith  */
110da3b867eSMike Smith #define EC_GET_DATA(sc)							\
111da3b867eSMike Smith 	bus_space_read_1((sc)->ec_data_tag, (sc)->ec_data_handle, 0)
112da3b867eSMike Smith 
113da3b867eSMike Smith #define EC_SET_DATA(sc, v)						\
114da3b867eSMike Smith 	bus_space_write_1((sc)->ec_data_tag, (sc)->ec_data_handle, 0, (v))
115da3b867eSMike Smith 
116da3b867eSMike Smith #define EC_GET_CSR(sc)							\
117da3b867eSMike Smith 	bus_space_read_1((sc)->ec_csr_tag, (sc)->ec_csr_handle, 0)
118da3b867eSMike Smith 
119da3b867eSMike Smith #define EC_SET_CSR(sc, v)						\
120da3b867eSMike Smith 	bus_space_write_1((sc)->ec_csr_tag, (sc)->ec_csr_handle, 0, (v))
121da3b867eSMike Smith 
1220025fb0fSNate Lawson /* Additional params to pass from the probe routine */
1230025fb0fSNate Lawson struct acpi_ec_params {
1240025fb0fSNate Lawson     int		glk;
1250025fb0fSNate Lawson     int		gpe_bit;
1260025fb0fSNate Lawson     ACPI_HANDLE	gpe_handle;
1270025fb0fSNate Lawson     int		uid;
1280025fb0fSNate Lawson };
1290025fb0fSNate Lawson 
130f8335e3aSNate Lawson /* Indicate that this device has already been probed via ECDT. */
13170fa7bc0SNate Lawson #define DEV_ECDT(x)	(acpi_get_magic(x) == (uintptr_t)&acpi_ec_devclass)
132f8335e3aSNate Lawson 
133da3b867eSMike Smith /*
134da3b867eSMike Smith  * Driver softc.
135da3b867eSMike Smith  */
13615e32d5dSMike Smith struct acpi_ec_softc {
13715e32d5dSMike Smith     device_t		ec_dev;
13815e32d5dSMike Smith     ACPI_HANDLE		ec_handle;
1390025fb0fSNate Lawson     int			ec_uid;
1400025fb0fSNate Lawson     ACPI_HANDLE		ec_gpehandle;
1413a371f32SNate Lawson     UINT8		ec_gpebit;
14215e32d5dSMike Smith 
14315e32d5dSMike Smith     int			ec_data_rid;
14415e32d5dSMike Smith     struct resource	*ec_data_res;
14515e32d5dSMike Smith     bus_space_tag_t	ec_data_tag;
14615e32d5dSMike Smith     bus_space_handle_t	ec_data_handle;
14715e32d5dSMike Smith 
14815e32d5dSMike Smith     int			ec_csr_rid;
14915e32d5dSMike Smith     struct resource	*ec_csr_res;
15015e32d5dSMike Smith     bus_space_tag_t	ec_csr_tag;
15115e32d5dSMike Smith     bus_space_handle_t	ec_csr_handle;
15215e32d5dSMike Smith 
1531f04e8f5SNate Lawson     int			ec_glk;
1541f04e8f5SNate Lawson     int			ec_glkhandle;
155ef2374f7SNate Lawson     int			ec_burstactive;
156ef2374f7SNate Lawson     int			ec_sci_pend;
15783dcc133SNate Lawson     u_int		ec_gencount;
1580bfeadedSTakanori Watanabe     int			ec_suspending;
15915e32d5dSMike Smith };
16015e32d5dSMike Smith 
1611f04e8f5SNate Lawson /*
162f4b7de15SNate Lawson  * XXX njl
1631f04e8f5SNate Lawson  * I couldn't find it in the spec but other implementations also use a
1641f04e8f5SNate Lawson  * value of 1 ms for the time to acquire global lock.
1651f04e8f5SNate Lawson  */
1661f04e8f5SNate Lawson #define EC_LOCK_TIMEOUT	1000
1674690674eSMitsuru IWASAKI 
168ef2374f7SNate Lawson /* Default delay in microseconds between each run of the status polling loop. */
16983dcc133SNate Lawson #define EC_POLL_DELAY	5
170ef2374f7SNate Lawson 
171ef2374f7SNate Lawson /* Total time in ms spent waiting for a response from EC. */
17283dcc133SNate Lawson #define EC_TIMEOUT	750
17315e32d5dSMike Smith 
174ff40920eSNate Lawson #define EVENT_READY(event, status)			\
175ff40920eSNate Lawson 	(((event) == EC_EVENT_OUTPUT_BUFFER_FULL &&	\
176ff40920eSNate Lawson 	 ((status) & EC_FLAG_OUTPUT_BUFFER) != 0) ||	\
177ff40920eSNate Lawson 	 ((event) == EC_EVENT_INPUT_BUFFER_EMPTY && 	\
178ff40920eSNate Lawson 	 ((status) & EC_FLAG_INPUT_BUFFER) == 0))
179ff40920eSNate Lawson 
180f4b7de15SNate Lawson ACPI_SERIAL_DECL(ec, "ACPI embedded controller");
181f4b7de15SNate Lawson 
182ef2374f7SNate Lawson SYSCTL_DECL(_debug_acpi);
183ef2374f7SNate Lawson SYSCTL_NODE(_debug_acpi, OID_AUTO, ec, CTLFLAG_RD, NULL, "EC debugging");
184ef2374f7SNate Lawson 
185675e5627SNate Lawson static int	ec_burst_mode;
186ef2374f7SNate Lawson TUNABLE_INT("debug.acpi.ec.burst", &ec_burst_mode);
187675e5627SNate Lawson SYSCTL_INT(_debug_acpi_ec, OID_AUTO, burst, CTLFLAG_RW, &ec_burst_mode, 0,
188ef2374f7SNate Lawson     "Enable use of burst mode (faster for nearly all systems)");
18983dcc133SNate Lawson static int	ec_polled_mode;
19083dcc133SNate Lawson TUNABLE_INT("debug.acpi.ec.polled", &ec_polled_mode);
19183dcc133SNate Lawson SYSCTL_INT(_debug_acpi_ec, OID_AUTO, polled, CTLFLAG_RW, &ec_polled_mode, 0,
19283dcc133SNate Lawson     "Force use of polled mode (only if interrupt mode doesn't work)");
193ef2374f7SNate Lawson static int	ec_timeout = EC_TIMEOUT;
194ef2374f7SNate Lawson TUNABLE_INT("debug.acpi.ec.timeout", &ec_timeout);
195ef2374f7SNate Lawson SYSCTL_INT(_debug_acpi_ec, OID_AUTO, timeout, CTLFLAG_RW, &ec_timeout,
196ef2374f7SNate Lawson     EC_TIMEOUT, "Total time spent waiting for a response (poll+sleep)");
197ef2374f7SNate Lawson 
19883dcc133SNate Lawson static ACPI_STATUS
19983dcc133SNate Lawson EcLock(struct acpi_ec_softc *sc)
20015e32d5dSMike Smith {
201f4b7de15SNate Lawson     ACPI_STATUS	status;
20215e32d5dSMike Smith 
20335440dd3SNate Lawson     /* If _GLK is non-zero, acquire the global lock. */
20435440dd3SNate Lawson     status = AE_OK;
20535440dd3SNate Lawson     if (sc->ec_glk) {
20635440dd3SNate Lawson 	status = AcpiAcquireGlobalLock(EC_LOCK_TIMEOUT, &sc->ec_glkhandle);
20735440dd3SNate Lawson 	if (ACPI_FAILURE(status))
20835440dd3SNate Lawson 	    return (status);
20935440dd3SNate Lawson     }
210f4b7de15SNate Lawson     ACPI_SERIAL_BEGIN(ec);
21115e32d5dSMike Smith     return (status);
21215e32d5dSMike Smith }
21315e32d5dSMike Smith 
21483dcc133SNate Lawson static void
21515e32d5dSMike Smith EcUnlock(struct acpi_ec_softc *sc)
21615e32d5dSMike Smith {
217f4b7de15SNate Lawson     ACPI_SERIAL_END(ec);
21835440dd3SNate Lawson     if (sc->ec_glk)
21935440dd3SNate Lawson 	AcpiReleaseGlobalLock(sc->ec_glkhandle);
22015e32d5dSMike Smith }
22115e32d5dSMike Smith 
222f3fc4f8bSNate Lawson static uint32_t		EcGpeHandler(void *Context);
22315e32d5dSMike Smith static ACPI_STATUS	EcSpaceSetup(ACPI_HANDLE Region, UINT32 Function,
22415e32d5dSMike Smith 				void *Context, void **return_Context);
225464c662eSNate Lawson static ACPI_STATUS	EcSpaceHandler(UINT32 Function,
226464c662eSNate Lawson 				ACPI_PHYSICAL_ADDRESS Address,
227464c662eSNate Lawson 				UINT32 width, ACPI_INTEGER *Value,
22815e32d5dSMike Smith 				void *Context, void *RegionContext);
22983dcc133SNate Lawson static ACPI_STATUS	EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event,
23083dcc133SNate Lawson 				u_int gen_count);
2311f04e8f5SNate Lawson static ACPI_STATUS	EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd);
232464c662eSNate Lawson static ACPI_STATUS	EcRead(struct acpi_ec_softc *sc, UINT8 Address,
233464c662eSNate Lawson 				UINT8 *Data);
234464c662eSNate Lawson static ACPI_STATUS	EcWrite(struct acpi_ec_softc *sc, UINT8 Address,
235464c662eSNate Lawson 				UINT8 *Data);
23615e32d5dSMike Smith static int		acpi_ec_probe(device_t dev);
23715e32d5dSMike Smith static int		acpi_ec_attach(device_t dev);
2380bfeadedSTakanori Watanabe static int		acpi_ec_suspend(device_t dev);
2390bfeadedSTakanori Watanabe static int		acpi_ec_resume(device_t dev);
240340a7f6aSNate Lawson static int		acpi_ec_shutdown(device_t dev);
241e33bea8dSNate Lawson static int		acpi_ec_read_method(device_t dev, u_int addr,
242e33bea8dSNate Lawson 				ACPI_INTEGER *val, int width);
243e33bea8dSNate Lawson static int		acpi_ec_write_method(device_t dev, u_int addr,
244e33bea8dSNate Lawson 				ACPI_INTEGER val, int width);
24515e32d5dSMike Smith 
24615e32d5dSMike Smith static device_method_t acpi_ec_methods[] = {
24715e32d5dSMike Smith     /* Device interface */
24815e32d5dSMike Smith     DEVMETHOD(device_probe,	acpi_ec_probe),
24915e32d5dSMike Smith     DEVMETHOD(device_attach,	acpi_ec_attach),
2500bfeadedSTakanori Watanabe     DEVMETHOD(device_suspend,	acpi_ec_suspend),
2510bfeadedSTakanori Watanabe     DEVMETHOD(device_resume,	acpi_ec_resume),
252340a7f6aSNate Lawson     DEVMETHOD(device_shutdown,	acpi_ec_shutdown),
25315e32d5dSMike Smith 
254e33bea8dSNate Lawson     /* Embedded controller interface */
255e33bea8dSNate Lawson     DEVMETHOD(acpi_ec_read,	acpi_ec_read_method),
256e33bea8dSNate Lawson     DEVMETHOD(acpi_ec_write,	acpi_ec_write_method),
257e33bea8dSNate Lawson 
25815e32d5dSMike Smith     {0, 0}
25915e32d5dSMike Smith };
26015e32d5dSMike Smith 
26115e32d5dSMike Smith static driver_t acpi_ec_driver = {
26215e32d5dSMike Smith     "acpi_ec",
26315e32d5dSMike Smith     acpi_ec_methods,
26415e32d5dSMike Smith     sizeof(struct acpi_ec_softc),
26515e32d5dSMike Smith };
26615e32d5dSMike Smith 
2673273b005SMike Smith static devclass_t acpi_ec_devclass;
26815e32d5dSMike Smith DRIVER_MODULE(acpi_ec, acpi, acpi_ec_driver, acpi_ec_devclass, 0, 0);
26964278df5SNate Lawson MODULE_DEPEND(acpi_ec, acpi, 1, 1, 1);
27015e32d5dSMike Smith 
27115e32d5dSMike Smith /*
272f8335e3aSNate Lawson  * Look for an ECDT and if we find one, set up default GPE and
273f8335e3aSNate Lawson  * space handlers to catch attempts to access EC space before
27415e32d5dSMike Smith  * we have a real driver instance in place.
27583dcc133SNate Lawson  *
27683dcc133SNate Lawson  * TODO: Some old Gateway laptops need us to fake up an ECDT or
27783dcc133SNate Lawson  * otherwise attach early so that _REG methods can run.
27815e32d5dSMike Smith  */
279f8335e3aSNate Lawson void
280f8335e3aSNate Lawson acpi_ec_ecdt_probe(device_t parent)
28115e32d5dSMike Smith {
282f8335e3aSNate Lawson     ACPI_TABLE_ECDT *ecdt;
283f8335e3aSNate Lawson     ACPI_STATUS	     status;
284f8335e3aSNate Lawson     device_t	     child;
285f8335e3aSNate Lawson     ACPI_HANDLE	     h;
2860025fb0fSNate Lawson     struct acpi_ec_params *params;
287f8335e3aSNate Lawson 
288b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
28915e32d5dSMike Smith 
290f8335e3aSNate Lawson     /* Find and validate the ECDT. */
2912be4e471SJung-uk Kim     status = AcpiGetTable(ACPI_SIG_ECDT, 1, (ACPI_TABLE_HEADER **)&ecdt);
292f8335e3aSNate Lawson     if (ACPI_FAILURE(status) ||
2932be4e471SJung-uk Kim 	ecdt->Control.BitWidth != 8 ||
2942be4e471SJung-uk Kim 	ecdt->Data.BitWidth != 8) {
295f8335e3aSNate Lawson 	return;
29615e32d5dSMike Smith     }
29715e32d5dSMike Smith 
298f8335e3aSNate Lawson     /* Create the child device with the given unit number. */
2992be4e471SJung-uk Kim     child = BUS_ADD_CHILD(parent, 0, "acpi_ec", ecdt->Uid);
300f8335e3aSNate Lawson     if (child == NULL) {
3010025fb0fSNate Lawson 	printf("%s: can't add child\n", __func__);
302f8335e3aSNate Lawson 	return;
303f8335e3aSNate Lawson     }
304f8335e3aSNate Lawson 
305f8335e3aSNate Lawson     /* Find and save the ACPI handle for this device. */
3062be4e471SJung-uk Kim     status = AcpiGetHandle(NULL, ecdt->Id, &h);
307f8335e3aSNate Lawson     if (ACPI_FAILURE(status)) {
308f8335e3aSNate Lawson 	device_delete_child(parent, child);
3090025fb0fSNate Lawson 	printf("%s: can't get handle\n", __func__);
310f8335e3aSNate Lawson 	return;
311f8335e3aSNate Lawson     }
312f8335e3aSNate Lawson     acpi_set_handle(child, h);
313f8335e3aSNate Lawson 
314f8335e3aSNate Lawson     /* Set the data and CSR register addresses. */
3152be4e471SJung-uk Kim     bus_set_resource(child, SYS_RES_IOPORT, 0, ecdt->Data.Address,
316f8335e3aSNate Lawson 	/*count*/1);
3172be4e471SJung-uk Kim     bus_set_resource(child, SYS_RES_IOPORT, 1, ecdt->Control.Address,
318f8335e3aSNate Lawson 	/*count*/1);
319f8335e3aSNate Lawson 
32015e32d5dSMike Smith     /*
321f8335e3aSNate Lawson      * Store values for the probe/attach routines to use.  Store the
322c868ac7dSNate Lawson      * ECDT GPE bit and set the global lock flag according to _GLK.
3230025fb0fSNate Lawson      * Note that it is not perfectly correct to be evaluating a method
3240025fb0fSNate Lawson      * before initializing devices, but in practice this function
3250025fb0fSNate Lawson      * should be safe to call at this point.
32615e32d5dSMike Smith      */
3270025fb0fSNate Lawson     params = malloc(sizeof(struct acpi_ec_params), M_TEMP, M_WAITOK | M_ZERO);
3280025fb0fSNate Lawson     params->gpe_handle = NULL;
3292be4e471SJung-uk Kim     params->gpe_bit = ecdt->Gpe;
3302be4e471SJung-uk Kim     params->uid = ecdt->Uid;
3310025fb0fSNate Lawson     acpi_GetInteger(h, "_GLK", &params->glk);
3320025fb0fSNate Lawson     acpi_set_private(child, params);
33370fa7bc0SNate Lawson     acpi_set_magic(child, (uintptr_t)&acpi_ec_devclass);
334f8335e3aSNate Lawson 
335f8335e3aSNate Lawson     /* Finish the attach process. */
336f8335e3aSNate Lawson     if (device_probe_and_attach(child) != 0)
337f8335e3aSNate Lawson 	device_delete_child(parent, child);
338f8335e3aSNate Lawson }
339f8335e3aSNate Lawson 
34015e32d5dSMike Smith static int
34115e32d5dSMike Smith acpi_ec_probe(device_t dev)
34215e32d5dSMike Smith {
3430025fb0fSNate Lawson     ACPI_BUFFER buf;
344f8335e3aSNate Lawson     ACPI_HANDLE h;
3450025fb0fSNate Lawson     ACPI_OBJECT *obj;
346f8335e3aSNate Lawson     ACPI_STATUS status;
347f8335e3aSNate Lawson     device_t	peer;
348f8335e3aSNate Lawson     char	desc[64];
3490025fb0fSNate Lawson     int		ret;
3500025fb0fSNate Lawson     struct acpi_ec_params *params;
3515fcc8a58SNate Lawson     static char *ec_ids[] = { "PNP0C09", NULL };
3520ae55423SMike Smith 
353c72508e2SNate Lawson     /* Check that this is a device and that EC is not disabled. */
354c72508e2SNate Lawson     if (acpi_get_type(dev) != ACPI_TYPE_DEVICE || acpi_disabled("ec"))
355f8335e3aSNate Lawson 	return (ENXIO);
35615e32d5dSMike Smith 
35715e32d5dSMike Smith     /*
358f8335e3aSNate Lawson      * If probed via ECDT, set description and continue.  Otherwise,
359f8335e3aSNate Lawson      * we can access the namespace and make sure this is not a
360f8335e3aSNate Lawson      * duplicate probe.
36115e32d5dSMike Smith      */
3620025fb0fSNate Lawson     ret = ENXIO;
3630025fb0fSNate Lawson     params = NULL;
3640025fb0fSNate Lawson     buf.Pointer = NULL;
3650025fb0fSNate Lawson     buf.Length = ACPI_ALLOCATE_BUFFER;
3660025fb0fSNate Lawson     if (DEV_ECDT(dev)) {
3670025fb0fSNate Lawson 	params = acpi_get_private(dev);
3681f04e8f5SNate Lawson 	ret = 0;
3695fcc8a58SNate Lawson     } else if (!acpi_disabled("ec") &&
3705fcc8a58SNate Lawson 	ACPI_ID_PROBE(device_get_parent(dev), dev, ec_ids)) {
3710025fb0fSNate Lawson 	params = malloc(sizeof(struct acpi_ec_params), M_TEMP,
3720025fb0fSNate Lawson 			M_WAITOK | M_ZERO);
373f8335e3aSNate Lawson 	h = acpi_get_handle(dev);
374f8335e3aSNate Lawson 
375f8335e3aSNate Lawson 	/*
376f8335e3aSNate Lawson 	 * Read the unit ID to check for duplicate attach and the
377f8335e3aSNate Lawson 	 * global lock value to see if we should acquire it when
378f8335e3aSNate Lawson 	 * accessing the EC.
379f8335e3aSNate Lawson 	 */
3800025fb0fSNate Lawson 	status = acpi_GetInteger(h, "_UID", &params->uid);
381f8335e3aSNate Lawson 	if (ACPI_FAILURE(status))
3820025fb0fSNate Lawson 	    params->uid = 0;
3830025fb0fSNate Lawson 	status = acpi_GetInteger(h, "_GLK", &params->glk);
384f8335e3aSNate Lawson 	if (ACPI_FAILURE(status))
3850025fb0fSNate Lawson 	    params->glk = 0;
386f8335e3aSNate Lawson 
387f8335e3aSNate Lawson 	/*
388f8335e3aSNate Lawson 	 * Evaluate the _GPE method to find the GPE bit used by the EC to
3890025fb0fSNate Lawson 	 * signal status (SCI).  If it's a package, it contains a reference
3900025fb0fSNate Lawson 	 * and GPE bit, similar to _PRW.
391f8335e3aSNate Lawson 	 */
3920025fb0fSNate Lawson 	status = AcpiEvaluateObject(h, "_GPE", NULL, &buf);
393f8335e3aSNate Lawson 	if (ACPI_FAILURE(status)) {
394f8335e3aSNate Lawson 	    device_printf(dev, "can't evaluate _GPE - %s\n",
395f8335e3aSNate Lawson 			  AcpiFormatException(status));
396a6761eb3SNate Lawson 	    goto out;
397f8335e3aSNate Lawson 	}
3980025fb0fSNate Lawson 	obj = (ACPI_OBJECT *)buf.Pointer;
3990025fb0fSNate Lawson 	if (obj == NULL)
400a6761eb3SNate Lawson 	    goto out;
4010025fb0fSNate Lawson 
4020025fb0fSNate Lawson 	switch (obj->Type) {
4030025fb0fSNate Lawson 	case ACPI_TYPE_INTEGER:
4040025fb0fSNate Lawson 	    params->gpe_handle = NULL;
4050025fb0fSNate Lawson 	    params->gpe_bit = obj->Integer.Value;
4060025fb0fSNate Lawson 	    break;
4070025fb0fSNate Lawson 	case ACPI_TYPE_PACKAGE:
4080025fb0fSNate Lawson 	    if (!ACPI_PKG_VALID(obj, 2))
4090025fb0fSNate Lawson 		goto out;
4100025fb0fSNate Lawson 	    params->gpe_handle =
4110025fb0fSNate Lawson 		acpi_GetReference(NULL, &obj->Package.Elements[0]);
4120025fb0fSNate Lawson 	    if (params->gpe_handle == NULL ||
4130025fb0fSNate Lawson 		acpi_PkgInt32(obj, 1, &params->gpe_bit) != 0)
4140025fb0fSNate Lawson 		goto out;
4150025fb0fSNate Lawson 	    break;
4160025fb0fSNate Lawson 	default:
4170025fb0fSNate Lawson 	    device_printf(dev, "_GPE has invalid type %d\n", obj->Type);
4180025fb0fSNate Lawson 	    goto out;
4190025fb0fSNate Lawson 	}
420f8335e3aSNate Lawson 
421f8335e3aSNate Lawson 	/* Store the values we got from the namespace for attach. */
4220025fb0fSNate Lawson 	acpi_set_private(dev, params);
423f8335e3aSNate Lawson 
424f8335e3aSNate Lawson 	/*
425f8335e3aSNate Lawson 	 * Check for a duplicate probe.  This can happen when a probe
426c868ac7dSNate Lawson 	 * via ECDT succeeded already.  If this is a duplicate, disable
427c868ac7dSNate Lawson 	 * this device.
428f8335e3aSNate Lawson 	 */
4290025fb0fSNate Lawson 	peer = devclass_get_device(acpi_ec_devclass, params->uid);
430c868ac7dSNate Lawson 	if (peer == NULL || !device_is_alive(peer))
431f8335e3aSNate Lawson 	    ret = 0;
432c868ac7dSNate Lawson 	else
433c868ac7dSNate Lawson 	    device_disable(dev);
434c868ac7dSNate Lawson     }
435f8335e3aSNate Lawson 
4360025fb0fSNate Lawson out:
437c868ac7dSNate Lawson     if (ret == 0) {
438c868ac7dSNate Lawson 	snprintf(desc, sizeof(desc), "Embedded Controller: GPE %#x%s%s",
4390025fb0fSNate Lawson 		 params->gpe_bit, (params->glk) ? ", GLK" : "",
440c868ac7dSNate Lawson 		 DEV_ECDT(dev) ? ", ECDT" : "");
441c868ac7dSNate Lawson 	device_set_desc_copy(dev, desc);
44215e32d5dSMike Smith     }
4431f04e8f5SNate Lawson 
4440025fb0fSNate Lawson     if (ret > 0 && params)
4450025fb0fSNate Lawson 	free(params, M_TEMP);
4460025fb0fSNate Lawson     if (buf.Pointer)
4470025fb0fSNate Lawson 	AcpiOsFree(buf.Pointer);
4481f04e8f5SNate Lawson     return (ret);
44915e32d5dSMike Smith }
45015e32d5dSMike Smith 
45115e32d5dSMike Smith static int
45215e32d5dSMike Smith acpi_ec_attach(device_t dev)
45315e32d5dSMike Smith {
45415e32d5dSMike Smith     struct acpi_ec_softc	*sc;
4550025fb0fSNate Lawson     struct acpi_ec_params	*params;
45615e32d5dSMike Smith     ACPI_STATUS			Status;
457dbd0058aSMike Smith 
458b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
4590ae55423SMike Smith 
4601f04e8f5SNate Lawson     /* Fetch/initialize softc (assumes softc is pre-zeroed). */
46115e32d5dSMike Smith     sc = device_get_softc(dev);
4620025fb0fSNate Lawson     params = acpi_get_private(dev);
46315e32d5dSMike Smith     sc->ec_dev = dev;
46415e32d5dSMike Smith     sc->ec_handle = acpi_get_handle(dev);
46515e32d5dSMike Smith 
466f8335e3aSNate Lawson     /* Retrieve previously probed values via device ivars. */
4670025fb0fSNate Lawson     sc->ec_glk = params->glk;
4680025fb0fSNate Lawson     sc->ec_gpebit = params->gpe_bit;
4690025fb0fSNate Lawson     sc->ec_gpehandle = params->gpe_handle;
4700025fb0fSNate Lawson     sc->ec_uid = params->uid;
47168fb6c48STakanori Watanabe     sc->ec_suspending = FALSE;
4720025fb0fSNate Lawson     free(params, M_TEMP);
473f8335e3aSNate Lawson 
4741f04e8f5SNate Lawson     /* Attach bus resources for data and command/status ports. */
47515e32d5dSMike Smith     sc->ec_data_rid = 0;
4765f96beb9SNate Lawson     sc->ec_data_res = bus_alloc_resource_any(sc->ec_dev, SYS_RES_IOPORT,
4775f96beb9SNate Lawson 			&sc->ec_data_rid, RF_ACTIVE);
478464c662eSNate Lawson     if (sc->ec_data_res == NULL) {
47915e32d5dSMike Smith 	device_printf(dev, "can't allocate data port\n");
480f3fc4f8bSNate Lawson 	goto error;
48115e32d5dSMike Smith     }
48215e32d5dSMike Smith     sc->ec_data_tag = rman_get_bustag(sc->ec_data_res);
48315e32d5dSMike Smith     sc->ec_data_handle = rman_get_bushandle(sc->ec_data_res);
48415e32d5dSMike Smith 
48515e32d5dSMike Smith     sc->ec_csr_rid = 1;
4865f96beb9SNate Lawson     sc->ec_csr_res = bus_alloc_resource_any(sc->ec_dev, SYS_RES_IOPORT,
4875f96beb9SNate Lawson 			&sc->ec_csr_rid, RF_ACTIVE);
488464c662eSNate Lawson     if (sc->ec_csr_res == NULL) {
48915e32d5dSMike Smith 	device_printf(dev, "can't allocate command/status port\n");
490f3fc4f8bSNate Lawson 	goto error;
49115e32d5dSMike Smith     }
49215e32d5dSMike Smith     sc->ec_csr_tag = rman_get_bustag(sc->ec_csr_res);
49315e32d5dSMike Smith     sc->ec_csr_handle = rman_get_bushandle(sc->ec_csr_res);
49415e32d5dSMike Smith 
49515e32d5dSMike Smith     /*
4961f04e8f5SNate Lawson      * Install a handler for this EC's GPE bit.  We want edge-triggered
4971f04e8f5SNate Lawson      * behavior.
49815e32d5dSMike Smith      */
4991f04e8f5SNate Lawson     ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "attaching GPE handler\n"));
5000025fb0fSNate Lawson     Status = AcpiInstallGpeHandler(sc->ec_gpehandle, sc->ec_gpebit,
501b7d13479SNate Lawson 		ACPI_GPE_EDGE_TRIGGERED, &EcGpeHandler, sc);
502464c662eSNate Lawson     if (ACPI_FAILURE(Status)) {
50376d1dff4SMike Smith 	device_printf(dev, "can't install GPE handler for %s - %s\n",
504bfae45aaSMike Smith 		      acpi_name(sc->ec_handle), AcpiFormatException(Status));
505f3fc4f8bSNate Lawson 	goto error;
50615e32d5dSMike Smith     }
50715e32d5dSMike Smith 
50815e32d5dSMike Smith     /*
50915e32d5dSMike Smith      * Install address space handler
51015e32d5dSMike Smith      */
5114c1cdee6SMike Smith     ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "attaching address space handler\n"));
512464c662eSNate Lawson     Status = AcpiInstallAddressSpaceHandler(sc->ec_handle, ACPI_ADR_SPACE_EC,
5131f04e8f5SNate Lawson 		&EcSpaceHandler, &EcSpaceSetup, sc);
514464c662eSNate Lawson     if (ACPI_FAILURE(Status)) {
51576d1dff4SMike Smith 	device_printf(dev, "can't install address space handler for %s - %s\n",
516bfae45aaSMike Smith 		      acpi_name(sc->ec_handle), AcpiFormatException(Status));
517f3fc4f8bSNate Lawson 	goto error;
518f3fc4f8bSNate Lawson     }
519f3fc4f8bSNate Lawson 
520f3fc4f8bSNate Lawson     /* Enable runtime GPEs for the handler. */
521f3fc4f8bSNate Lawson     Status = AcpiSetGpeType(sc->ec_gpehandle, sc->ec_gpebit,
522f3fc4f8bSNate Lawson 			    ACPI_GPE_TYPE_RUNTIME);
523f3fc4f8bSNate Lawson     if (ACPI_FAILURE(Status)) {
524f3fc4f8bSNate Lawson 	device_printf(dev, "AcpiSetGpeType failed: %s\n",
525f3fc4f8bSNate Lawson 		      AcpiFormatException(Status));
526f3fc4f8bSNate Lawson 	goto error;
527f3fc4f8bSNate Lawson     }
528f3fc4f8bSNate Lawson     Status = AcpiEnableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_NOT_ISR);
529f3fc4f8bSNate Lawson     if (ACPI_FAILURE(Status)) {
530f3fc4f8bSNate Lawson 	device_printf(dev, "AcpiEnableGpe failed: %s\n",
531f3fc4f8bSNate Lawson 		      AcpiFormatException(Status));
532f3fc4f8bSNate Lawson 	goto error;
53315e32d5dSMike Smith     }
5341f04e8f5SNate Lawson 
5351f04e8f5SNate Lawson     ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "acpi_ec_attach complete\n"));
5361f04e8f5SNate Lawson     return (0);
537464c662eSNate Lawson 
538f3fc4f8bSNate Lawson error:
539f3fc4f8bSNate Lawson     AcpiRemoveGpeHandler(sc->ec_gpehandle, sc->ec_gpebit, &EcGpeHandler);
540f3fc4f8bSNate Lawson     AcpiRemoveAddressSpaceHandler(sc->ec_handle, ACPI_ADR_SPACE_EC,
541f3fc4f8bSNate Lawson 	EcSpaceHandler);
542f8372adeSTakanori Watanabe     if (sc->ec_csr_res)
543f8372adeSTakanori Watanabe 	bus_release_resource(sc->ec_dev, SYS_RES_IOPORT, sc->ec_csr_rid,
544f8372adeSTakanori Watanabe 			     sc->ec_csr_res);
545f8372adeSTakanori Watanabe     if (sc->ec_data_res)
546f8372adeSTakanori Watanabe 	bus_release_resource(sc->ec_dev, SYS_RES_IOPORT, sc->ec_data_rid,
547f8372adeSTakanori Watanabe 			     sc->ec_data_res);
548f3fc4f8bSNate Lawson     return (ENXIO);
5491f04e8f5SNate Lawson }
5501f04e8f5SNate Lawson 
551340a7f6aSNate Lawson static int
5520bfeadedSTakanori Watanabe acpi_ec_suspend(device_t dev)
5530bfeadedSTakanori Watanabe {
5540bfeadedSTakanori Watanabe     struct acpi_ec_softc	*sc;
5550bfeadedSTakanori Watanabe 
5560bfeadedSTakanori Watanabe     sc = device_get_softc(dev);
55768fb6c48STakanori Watanabe     sc->ec_suspending = TRUE;
5580bfeadedSTakanori Watanabe     return (0);
5590bfeadedSTakanori Watanabe 
5600bfeadedSTakanori Watanabe }
5610bfeadedSTakanori Watanabe 
5620bfeadedSTakanori Watanabe static int
5630bfeadedSTakanori Watanabe acpi_ec_resume(device_t dev)
5640bfeadedSTakanori Watanabe {
5650bfeadedSTakanori Watanabe     struct acpi_ec_softc	*sc;
5660bfeadedSTakanori Watanabe 
5670bfeadedSTakanori Watanabe     sc = device_get_softc(dev);
56868fb6c48STakanori Watanabe     sc->ec_suspending = FALSE;
5690bfeadedSTakanori Watanabe     return (0);
5700bfeadedSTakanori Watanabe 
5710bfeadedSTakanori Watanabe }
5720bfeadedSTakanori Watanabe 
5730bfeadedSTakanori Watanabe static int
574340a7f6aSNate Lawson acpi_ec_shutdown(device_t dev)
575340a7f6aSNate Lawson {
576340a7f6aSNate Lawson     struct acpi_ec_softc	*sc;
577340a7f6aSNate Lawson 
578340a7f6aSNate Lawson     /* Disable the GPE so we don't get EC events during shutdown. */
579340a7f6aSNate Lawson     sc = device_get_softc(dev);
580340a7f6aSNate Lawson     AcpiDisableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_NOT_ISR);
581340a7f6aSNate Lawson     return (0);
582340a7f6aSNate Lawson }
583340a7f6aSNate Lawson 
584e33bea8dSNate Lawson /* Methods to allow other devices (e.g., smbat) to read/write EC space. */
585e33bea8dSNate Lawson static int
586e33bea8dSNate Lawson acpi_ec_read_method(device_t dev, u_int addr, ACPI_INTEGER *val, int width)
587e33bea8dSNate Lawson {
588e33bea8dSNate Lawson     struct acpi_ec_softc *sc;
589e33bea8dSNate Lawson     ACPI_STATUS status;
590e33bea8dSNate Lawson 
591e33bea8dSNate Lawson     sc = device_get_softc(dev);
592e33bea8dSNate Lawson     status = EcSpaceHandler(ACPI_READ, addr, width * 8, val, sc, NULL);
593e33bea8dSNate Lawson     if (ACPI_FAILURE(status))
594e33bea8dSNate Lawson 	return (ENXIO);
595e33bea8dSNate Lawson     return (0);
596e33bea8dSNate Lawson }
597e33bea8dSNate Lawson 
598e33bea8dSNate Lawson static int
599e33bea8dSNate Lawson acpi_ec_write_method(device_t dev, u_int addr, ACPI_INTEGER val, int width)
600e33bea8dSNate Lawson {
601e33bea8dSNate Lawson     struct acpi_ec_softc *sc;
602e33bea8dSNate Lawson     ACPI_STATUS status;
603e33bea8dSNate Lawson 
604e33bea8dSNate Lawson     sc = device_get_softc(dev);
605e33bea8dSNate Lawson     status = EcSpaceHandler(ACPI_WRITE, addr, width * 8, &val, sc, NULL);
606e33bea8dSNate Lawson     if (ACPI_FAILURE(status))
607e33bea8dSNate Lawson 	return (ENXIO);
608e33bea8dSNate Lawson     return (0);
609e33bea8dSNate Lawson }
610e33bea8dSNate Lawson 
61115e32d5dSMike Smith static void
612c07572e7STakanori Watanabe EcGpeQueryHandler(void *Context)
61315e32d5dSMike Smith {
61415e32d5dSMike Smith     struct acpi_ec_softc	*sc = (struct acpi_ec_softc *)Context;
61515e32d5dSMike Smith     UINT8			Data;
61615e32d5dSMike Smith     ACPI_STATUS			Status;
61715e32d5dSMike Smith     char			qxx[5];
61815e32d5dSMike Smith 
619b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
6201f04e8f5SNate Lawson     KASSERT(Context != NULL, ("EcGpeQueryHandler called with NULL"));
62115e32d5dSMike Smith 
622ef2374f7SNate Lawson     /* Serialize user access with EcSpaceHandler(). */
62383dcc133SNate Lawson     Status = EcLock(sc);
6243a371f32SNate Lawson     if (ACPI_FAILURE(Status)) {
62583dcc133SNate Lawson 	device_printf(sc->ec_dev, "GpeQuery lock error: %s\n",
62683dcc133SNate Lawson 	    AcpiFormatException(Status));
6273a371f32SNate Lawson 	return;
6283a371f32SNate Lawson     }
6293a371f32SNate Lawson 
63015e32d5dSMike Smith     /*
6313a371f32SNate Lawson      * Send a query command to the EC to find out which _Qxx call it
6323a371f32SNate Lawson      * wants to make.  This command clears the SCI bit and also the
63383dcc133SNate Lawson      * interrupt source since we are edge-triggered.  To prevent the GPE
63483dcc133SNate Lawson      * that may arise from running the query from causing another query
63583dcc133SNate Lawson      * to be queued, we clear the pending flag only after running it.
6363a371f32SNate Lawson      */
6373a371f32SNate Lawson     Status = EcCommand(sc, EC_COMMAND_QUERY);
63883dcc133SNate Lawson     sc->ec_sci_pend = FALSE;
639dbd0058aSMike Smith     if (ACPI_FAILURE(Status)) {
6403a371f32SNate Lawson 	EcUnlock(sc);
64183dcc133SNate Lawson 	device_printf(sc->ec_dev, "GPE query failed: %s\n",
64283dcc133SNate Lawson 	    AcpiFormatException(Status));
64383dcc133SNate Lawson 	return;
64415e32d5dSMike Smith     }
6453a371f32SNate Lawson     Data = EC_GET_DATA(sc);
646ef2374f7SNate Lawson 
64783dcc133SNate Lawson     /*
64883dcc133SNate Lawson      * We have to unlock before running the _Qxx method below since that
64983dcc133SNate Lawson      * method may attempt to read/write from EC address space, causing
65083dcc133SNate Lawson      * recursive acquisition of the lock.
65183dcc133SNate Lawson      */
6523a371f32SNate Lawson     EcUnlock(sc);
65315e32d5dSMike Smith 
6541f04e8f5SNate Lawson     /* Ignore the value for "no outstanding event". (13.3.5) */
65583dcc133SNate Lawson     CTR2(KTR_ACPI, "ec query ok,%s running _Q%02X", Data ? "" : " not", Data);
6561f04e8f5SNate Lawson     if (Data == 0)
65783dcc133SNate Lawson 	return;
6581f04e8f5SNate Lawson 
6591f04e8f5SNate Lawson     /* Evaluate _Qxx to respond to the controller. */
66083dcc133SNate Lawson     snprintf(qxx, sizeof(qxx), "_Q%02X", Data);
661b3919c8dSMark Santcroos     AcpiUtStrupr(qxx);
662ee785aa9SJohn Baldwin     Status = AcpiEvaluateObject(sc->ec_handle, qxx, NULL, NULL);
6631f04e8f5SNate Lawson     if (ACPI_FAILURE(Status) && Status != AE_NOT_FOUND) {
66483dcc133SNate Lawson 	device_printf(sc->ec_dev, "evaluation of query method %s failed: %s\n",
665bfae45aaSMike Smith 	    qxx, AcpiFormatException(Status));
66615e32d5dSMike Smith     }
667c07572e7STakanori Watanabe }
66842f6d122SMike Smith 
669a9cf0dffSMike Smith /*
67083dcc133SNate Lawson  * The GPE handler is called when IBE/OBF or SCI events occur.  We are
67183dcc133SNate Lawson  * called from an unknown lock context.
672a9cf0dffSMike Smith  */
673f3fc4f8bSNate Lawson static uint32_t
674a9cf0dffSMike Smith EcGpeHandler(void *Context)
675c07572e7STakanori Watanabe {
676c07572e7STakanori Watanabe     struct acpi_ec_softc *sc = Context;
6771f04e8f5SNate Lawson     ACPI_STATUS		       Status;
678ef2374f7SNate Lawson     EC_STATUS		       EcStatus;
679a9cf0dffSMike Smith 
6801f04e8f5SNate Lawson     KASSERT(Context != NULL, ("EcGpeHandler called with NULL"));
68183dcc133SNate Lawson     CTR0(KTR_ACPI, "ec gpe handler start");
6821f04e8f5SNate Lawson 
6831395b555SNate Lawson     /*
68483dcc133SNate Lawson      * Notify EcWaitEvent() that the status register is now fresh.  If we
68583dcc133SNate Lawson      * didn't do this, it wouldn't be possible to distinguish an old IBE
68683dcc133SNate Lawson      * from a new one, for example when doing a write transaction (writing
68783dcc133SNate Lawson      * address and then data values.)
6881395b555SNate Lawson      */
68983dcc133SNate Lawson     atomic_add_int(&sc->ec_gencount, 1);
69083dcc133SNate Lawson     wakeup(&sc->ec_gencount);
691ef2374f7SNate Lawson 
692ef2374f7SNate Lawson     /*
69383dcc133SNate Lawson      * If the EC_SCI bit of the status register is set, queue a query handler.
69483dcc133SNate Lawson      * It will run the query and _Qxx method later, under the lock.
695ef2374f7SNate Lawson      */
696ef2374f7SNate Lawson     EcStatus = EC_GET_CSR(sc);
69783dcc133SNate Lawson     if ((EcStatus & EC_EVENT_SCI) && !sc->ec_sci_pend) {
69883dcc133SNate Lawson 	CTR0(KTR_ACPI, "ec gpe queueing query handler");
6992be4e471SJung-uk Kim 	Status = AcpiOsExecute(OSL_GPE_HANDLER, EcGpeQueryHandler, Context);
70083dcc133SNate Lawson 	if (ACPI_SUCCESS(Status))
701ef2374f7SNate Lawson 	    sc->ec_sci_pend = TRUE;
70283dcc133SNate Lawson 	else
70383dcc133SNate Lawson 	    printf("EcGpeHandler: queuing GPE query handler failed\n");
7046e141df2SNate Lawson     }
705f3fc4f8bSNate Lawson     return (0);
70615e32d5dSMike Smith }
70715e32d5dSMike Smith 
70815e32d5dSMike Smith static ACPI_STATUS
709464c662eSNate Lawson EcSpaceSetup(ACPI_HANDLE Region, UINT32 Function, void *Context,
710464c662eSNate Lawson 	     void **RegionContext)
71115e32d5dSMike Smith {
71242f6d122SMike Smith 
713b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
71442f6d122SMike Smith 
71515e32d5dSMike Smith     /*
71616844a97SNate Lawson      * If deactivating a region, always set the output to NULL.  Otherwise,
71716844a97SNate Lawson      * just pass the context through.
71815e32d5dSMike Smith      */
71916844a97SNate Lawson     if (Function == ACPI_REGION_DEACTIVATE)
72016844a97SNate Lawson 	*RegionContext = NULL;
72116844a97SNate Lawson     else
72215e32d5dSMike Smith 	*RegionContext = Context;
72315e32d5dSMike Smith 
72442f6d122SMike Smith     return_ACPI_STATUS (AE_OK);
72515e32d5dSMike Smith }
72615e32d5dSMike Smith 
72715e32d5dSMike Smith static ACPI_STATUS
728464c662eSNate Lawson EcSpaceHandler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address, UINT32 width,
729464c662eSNate Lawson 	       ACPI_INTEGER *Value, void *Context, void *RegionContext)
73015e32d5dSMike Smith {
73115e32d5dSMike Smith     struct acpi_ec_softc	*sc = (struct acpi_ec_softc *)Context;
7324ed391b8SNate Lawson     ACPI_STATUS			Status;
7331f04e8f5SNate Lawson     UINT8			EcAddr, EcData;
734ee785aa9SJohn Baldwin     int				i;
73515e32d5dSMike Smith 
736b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)Address);
7370ae55423SMike Smith 
7384ed391b8SNate Lawson     if (width % 8 != 0 || Value == NULL || Context == NULL)
7390ae55423SMike Smith 	return_ACPI_STATUS (AE_BAD_PARAMETER);
7404ed391b8SNate Lawson     if (Address + (width / 8) - 1 > 0xFF)
7414ed391b8SNate Lawson 	return_ACPI_STATUS (AE_BAD_ADDRESS);
74215e32d5dSMike Smith 
7434ed391b8SNate Lawson     if (Function == ACPI_READ)
7444ed391b8SNate Lawson 	*Value = 0;
7451f04e8f5SNate Lawson     EcAddr = Address;
7464ed391b8SNate Lawson     Status = AE_ERROR;
7474ed391b8SNate Lawson 
74883dcc133SNate Lawson     /*
74983dcc133SNate Lawson      * If booting, check if we need to run the query handler.  If so, we
75083dcc133SNate Lawson      * we call it directly here since our thread taskq is not active yet.
75183dcc133SNate Lawson      */
75283dcc133SNate Lawson     if (cold || rebooting) {
75383dcc133SNate Lawson 	if ((EC_GET_CSR(sc) & EC_EVENT_SCI)) {
75483dcc133SNate Lawson 	    CTR0(KTR_ACPI, "ec running gpe handler directly");
75583dcc133SNate Lawson 	    EcGpeQueryHandler(sc);
75683dcc133SNate Lawson 	}
75783dcc133SNate Lawson     }
75883dcc133SNate Lawson 
75983dcc133SNate Lawson     /* Serialize with EcGpeQueryHandler() at transaction granularity. */
76083dcc133SNate Lawson     Status = EcLock(sc);
761464c662eSNate Lawson     if (ACPI_FAILURE(Status))
762f4b7de15SNate Lawson 	return_ACPI_STATUS (Status);
7631f04e8f5SNate Lawson 
764f4b7de15SNate Lawson     /* Perform the transaction(s), based on width. */
765f4b7de15SNate Lawson     for (i = 0; i < width; i += 8, EcAddr++) {
7661f04e8f5SNate Lawson 	switch (Function) {
7671f04e8f5SNate Lawson 	case ACPI_READ:
7681f04e8f5SNate Lawson 	    Status = EcRead(sc, EcAddr, &EcData);
7694ed391b8SNate Lawson 	    if (ACPI_SUCCESS(Status))
7704ed391b8SNate Lawson 		*Value |= ((ACPI_INTEGER)EcData) << i;
771ee785aa9SJohn Baldwin 	    break;
7721f04e8f5SNate Lawson 	case ACPI_WRITE:
7731f04e8f5SNate Lawson 	    EcData = (UINT8)((*Value) >> i);
7741f04e8f5SNate Lawson 	    Status = EcWrite(sc, EcAddr, &EcData);
7751f04e8f5SNate Lawson 	    break;
7761f04e8f5SNate Lawson 	default:
7771f04e8f5SNate Lawson 	    device_printf(sc->ec_dev, "invalid EcSpaceHandler function %d\n",
7781f04e8f5SNate Lawson 			  Function);
7791f04e8f5SNate Lawson 	    Status = AE_BAD_PARAMETER;
7801f04e8f5SNate Lawson 	    break;
7811f04e8f5SNate Lawson 	}
7821f04e8f5SNate Lawson 	if (ACPI_FAILURE(Status))
7834ed391b8SNate Lawson 	    break;
784ee785aa9SJohn Baldwin     }
7854ed391b8SNate Lawson 
786f4b7de15SNate Lawson     EcUnlock(sc);
7870ae55423SMike Smith     return_ACPI_STATUS (Status);
78815e32d5dSMike Smith }
7892a4ac806SMike Smith 
79015e32d5dSMike Smith static ACPI_STATUS
79183dcc133SNate Lawson EcCheckStatus(struct acpi_ec_softc *sc, const char *msg, EC_EVENT event)
79215e32d5dSMike Smith {
79383dcc133SNate Lawson     ACPI_STATUS status;
79483dcc133SNate Lawson     EC_STATUS ec_status;
79583dcc133SNate Lawson 
79683dcc133SNate Lawson     status = AE_NO_HARDWARE_RESPONSE;
79783dcc133SNate Lawson     ec_status = EC_GET_CSR(sc);
79883dcc133SNate Lawson     if (sc->ec_burstactive && !(ec_status & EC_FLAG_BURST_MODE)) {
79983dcc133SNate Lawson 	CTR1(KTR_ACPI, "ec burst disabled in waitevent (%s)", msg);
80083dcc133SNate Lawson 	sc->ec_burstactive = FALSE;
80183dcc133SNate Lawson     }
80283dcc133SNate Lawson     if (EVENT_READY(event, ec_status)) {
80383dcc133SNate Lawson 	CTR2(KTR_ACPI, "ec %s wait ready, status %#x", msg, ec_status);
80483dcc133SNate Lawson 	status = AE_OK;
80583dcc133SNate Lawson     }
80683dcc133SNate Lawson     return (status);
80783dcc133SNate Lawson }
80883dcc133SNate Lawson 
80983dcc133SNate Lawson static ACPI_STATUS
81083dcc133SNate Lawson EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event, u_int gen_count)
81183dcc133SNate Lawson {
8121f04e8f5SNate Lawson     ACPI_STATUS	Status;
81383dcc133SNate Lawson     int		count, i, slp_ival;
81415e32d5dSMike Smith 
815f4b7de15SNate Lawson     ACPI_SERIAL_ASSERT(ec);
8161f04e8f5SNate Lawson     Status = AE_NO_HARDWARE_RESPONSE;
8171de5ce99STakanori Watanabe     int need_poll = cold || rebooting || ec_polled_mode || sc->ec_suspending;
81815e32d5dSMike Smith     /*
81983dcc133SNate Lawson      * The main CPU should be much faster than the EC.  So the status should
82083dcc133SNate Lawson      * be "not ready" when we start waiting.  But if the main CPU is really
82183dcc133SNate Lawson      * slow, it's possible we see the current "ready" response.  Since that
82283dcc133SNate Lawson      * can't be distinguished from the previous response in polled mode,
82383dcc133SNate Lawson      * this is a potential issue.  We really should have interrupts enabled
82483dcc133SNate Lawson      * during boot so there is no ambiguity in polled mode.
82583dcc133SNate Lawson      *
82683dcc133SNate Lawson      * If this occurs, we add an additional delay before actually entering
82783dcc133SNate Lawson      * the status checking loop, hopefully to allow the EC to go to work
82883dcc133SNate Lawson      * and produce a non-stale status.
82915e32d5dSMike Smith      */
8301de5ce99STakanori Watanabe     if (need_poll) {
83183dcc133SNate Lawson 	static int	once;
83283dcc133SNate Lawson 
83383dcc133SNate Lawson 	if (EcCheckStatus(sc, "pre-check", Event) == AE_OK) {
83483dcc133SNate Lawson 	    if (!once) {
83583dcc133SNate Lawson 		device_printf(sc->ec_dev,
83683dcc133SNate Lawson 		    "warning: EC done before starting event wait\n");
83783dcc133SNate Lawson 		once = 1;
83883dcc133SNate Lawson 	    }
83983dcc133SNate Lawson 	    AcpiOsStall(10);
84083dcc133SNate Lawson 	}
84183dcc133SNate Lawson     }
84283dcc133SNate Lawson 
84383dcc133SNate Lawson     /* Wait for event by polling or GPE (interrupt). */
8441de5ce99STakanori Watanabe     if (need_poll) {
84583dcc133SNate Lawson 	count = (ec_timeout * 1000) / EC_POLL_DELAY;
84683dcc133SNate Lawson 	if (count == 0)
847ef2374f7SNate Lawson 	    count = 1;
848ef2374f7SNate Lawson 	for (i = 0; i < count; i++) {
84983dcc133SNate Lawson 	    Status = EcCheckStatus(sc, "poll", Event);
85083dcc133SNate Lawson 	    if (Status == AE_OK)
85115e32d5dSMike Smith 		break;
852b0eefa38SNate Lawson 	    AcpiOsStall(EC_POLL_DELAY);
8531f04e8f5SNate Lawson 	}
85483dcc133SNate Lawson     } else {
855ef2374f7SNate Lawson 	slp_ival = hz / 1000;
856ef2374f7SNate Lawson 	if (slp_ival != 0) {
85783dcc133SNate Lawson 	    count = ec_timeout;
858ef2374f7SNate Lawson 	} else {
85983dcc133SNate Lawson 	    /* hz has less than 1 ms resolution so scale timeout. */
860b0eefa38SNate Lawson 	    slp_ival = 1;
861ef2374f7SNate Lawson 	    count = ec_timeout / (1000 / hz);
862ef2374f7SNate Lawson 	}
86383dcc133SNate Lawson 
86483dcc133SNate Lawson 	/*
86583dcc133SNate Lawson 	 * Wait for the GPE to signal the status changed, checking the
86683dcc133SNate Lawson 	 * status register each time we get one.  It's possible to get a
86783dcc133SNate Lawson 	 * GPE for an event we're not interested in here (i.e., SCI for
86883dcc133SNate Lawson 	 * EC query).
86983dcc133SNate Lawson 	 */
870b0eefa38SNate Lawson 	for (i = 0; i < count; i++) {
87183dcc133SNate Lawson 	    if (gen_count != sc->ec_gencount) {
87283dcc133SNate Lawson 		/*
87383dcc133SNate Lawson 		 * Record new generation count.  It's possible the GPE was
87483dcc133SNate Lawson 		 * just to notify us that a query is needed and we need to
87583dcc133SNate Lawson 		 * wait for a second GPE to signal the completion of the
87683dcc133SNate Lawson 		 * event we are actually waiting for.
87783dcc133SNate Lawson 		 */
87883dcc133SNate Lawson 		gen_count = sc->ec_gencount;
87983dcc133SNate Lawson 		Status = EcCheckStatus(sc, "sleep", Event);
88083dcc133SNate Lawson 		if (Status == AE_OK)
881ff40920eSNate Lawson 		    break;
882ff40920eSNate Lawson 	    }
88383dcc133SNate Lawson 	    tsleep(&sc->ec_gencount, PZERO, "ecgpe", slp_ival);
8841f04e8f5SNate Lawson 	}
8851f04e8f5SNate Lawson 
88683dcc133SNate Lawson 	/*
88783dcc133SNate Lawson 	 * We finished waiting for the GPE and it never arrived.  Try to
88883dcc133SNate Lawson 	 * read the register once and trust whatever value we got.  This is
88983dcc133SNate Lawson 	 * the best we can do at this point.  Then, force polled mode on
89083dcc133SNate Lawson 	 * since this system doesn't appear to generate GPEs.
89183dcc133SNate Lawson 	 */
89283dcc133SNate Lawson 	if (Status != AE_OK) {
89383dcc133SNate Lawson 	    Status = EcCheckStatus(sc, "sleep_end", Event);
89483dcc133SNate Lawson 	    device_printf(sc->ec_dev,
89583dcc133SNate Lawson 		"wait timed out (%sresponse), forcing polled mode\n",
89683dcc133SNate Lawson 		Status == AE_OK ? "" : "no ");
89783dcc133SNate Lawson 	    ec_polled_mode = TRUE;
89883dcc133SNate Lawson 	}
89983dcc133SNate Lawson     }
90083dcc133SNate Lawson     if (Status != AE_OK)
90183dcc133SNate Lawson 	    CTR0(KTR_ACPI, "error: ec wait timed out");
9021f04e8f5SNate Lawson     return (Status);
9031f04e8f5SNate Lawson }
9041f04e8f5SNate Lawson 
9051f04e8f5SNate Lawson static ACPI_STATUS
9061f04e8f5SNate Lawson EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd)
9071f04e8f5SNate Lawson {
908ef2374f7SNate Lawson     ACPI_STATUS	status;
909ef2374f7SNate Lawson     EC_EVENT	event;
910ef2374f7SNate Lawson     EC_STATUS	ec_status;
91183dcc133SNate Lawson     u_int	gen_count;
9121f04e8f5SNate Lawson 
913f4b7de15SNate Lawson     ACPI_SERIAL_ASSERT(ec);
9141f04e8f5SNate Lawson 
915ef2374f7SNate Lawson     /* Don't use burst mode if user disabled it. */
916ef2374f7SNate Lawson     if (!ec_burst_mode && cmd == EC_COMMAND_BURST_ENABLE)
917ef2374f7SNate Lawson 	return (AE_ERROR);
918ef2374f7SNate Lawson 
9191f04e8f5SNate Lawson     /* Decide what to wait for based on command type. */
9201f04e8f5SNate Lawson     switch (cmd) {
9211f04e8f5SNate Lawson     case EC_COMMAND_READ:
92215e32d5dSMike Smith     case EC_COMMAND_WRITE:
9231f04e8f5SNate Lawson     case EC_COMMAND_BURST_DISABLE:
924ef2374f7SNate Lawson 	event = EC_EVENT_INPUT_BUFFER_EMPTY;
9251f04e8f5SNate Lawson 	break;
9261f04e8f5SNate Lawson     case EC_COMMAND_QUERY:
9271f04e8f5SNate Lawson     case EC_COMMAND_BURST_ENABLE:
928ef2374f7SNate Lawson 	event = EC_EVENT_OUTPUT_BUFFER_FULL;
92915e32d5dSMike Smith 	break;
93015e32d5dSMike Smith     default:
93183dcc133SNate Lawson 	device_printf(sc->ec_dev, "EcCommand: invalid command %#x\n", cmd);
9321f04e8f5SNate Lawson 	return (AE_BAD_PARAMETER);
933464c662eSNate Lawson     }
9341f04e8f5SNate Lawson 
9351f04e8f5SNate Lawson     /* Run the command and wait for the chosen event. */
936ef2374f7SNate Lawson     CTR1(KTR_ACPI, "ec running command %#x", cmd);
93783dcc133SNate Lawson     gen_count = sc->ec_gencount;
9381f04e8f5SNate Lawson     EC_SET_CSR(sc, cmd);
93983dcc133SNate Lawson     status = EcWaitEvent(sc, event, gen_count);
940ef2374f7SNate Lawson     if (ACPI_SUCCESS(status)) {
941ef2374f7SNate Lawson 	/* If we succeeded, burst flag should now be present. */
942ef2374f7SNate Lawson 	if (cmd == EC_COMMAND_BURST_ENABLE) {
943ef2374f7SNate Lawson 	    ec_status = EC_GET_CSR(sc);
944ef2374f7SNate Lawson 	    if ((ec_status & EC_FLAG_BURST_MODE) == 0)
945ef2374f7SNate Lawson 		status = AE_ERROR;
946ef2374f7SNate Lawson 	}
94783dcc133SNate Lawson     } else
94883dcc133SNate Lawson 	device_printf(sc->ec_dev, "EcCommand: no response to %#x\n", cmd);
949ef2374f7SNate Lawson     return (status);
95015e32d5dSMike Smith }
95115e32d5dSMike Smith 
95215e32d5dSMike Smith static ACPI_STATUS
95315e32d5dSMike Smith EcRead(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data)
95415e32d5dSMike Smith {
955ef2374f7SNate Lawson     ACPI_STATUS	status;
956ef2374f7SNate Lawson     UINT8 data;
95783dcc133SNate Lawson     u_int gen_count;
95815e32d5dSMike Smith 
959f4b7de15SNate Lawson     ACPI_SERIAL_ASSERT(ec);
960c4a9fa45SNate Lawson     CTR1(KTR_ACPI, "ec read from %#x", Address);
96115e32d5dSMike Smith 
9621f04e8f5SNate Lawson     /* If we can't start burst mode, continue anyway. */
963ef2374f7SNate Lawson     status = EcCommand(sc, EC_COMMAND_BURST_ENABLE);
964ef2374f7SNate Lawson     if (status == AE_OK) {
965ef2374f7SNate Lawson     	data = EC_GET_DATA(sc);
966ef2374f7SNate Lawson 	if (data == EC_BURST_ACK) {
967ef2374f7SNate Lawson 	    CTR0(KTR_ACPI, "ec burst enabled");
968ef2374f7SNate Lawson 	    sc->ec_burstactive = TRUE;
969ef2374f7SNate Lawson 	}
970ef2374f7SNate Lawson     }
97115e32d5dSMike Smith 
972ef2374f7SNate Lawson     status = EcCommand(sc, EC_COMMAND_READ);
973ef2374f7SNate Lawson     if (ACPI_FAILURE(status))
974ef2374f7SNate Lawson 	return (status);
97515e32d5dSMike Smith 
97683dcc133SNate Lawson     gen_count = sc->ec_gencount;
97715e32d5dSMike Smith     EC_SET_DATA(sc, Address);
97883dcc133SNate Lawson     status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL, gen_count);
979ef2374f7SNate Lawson     if (ACPI_FAILURE(status)) {
98083dcc133SNate Lawson 	device_printf(sc->ec_dev, "EcRead: failed waiting to get data\n");
981ef2374f7SNate Lawson 	return (status);
98215e32d5dSMike Smith     }
983464c662eSNate Lawson     *Data = EC_GET_DATA(sc);
98415e32d5dSMike Smith 
9851f04e8f5SNate Lawson     if (sc->ec_burstactive) {
98683dcc133SNate Lawson 	sc->ec_burstactive = FALSE;
987ef2374f7SNate Lawson 	status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
988ef2374f7SNate Lawson 	if (ACPI_FAILURE(status))
989ef2374f7SNate Lawson 	    return (status);
990ef2374f7SNate Lawson 	CTR0(KTR_ACPI, "ec disabled burst ok");
9911f04e8f5SNate Lawson     }
99215e32d5dSMike Smith 
99315e32d5dSMike Smith     return (AE_OK);
99415e32d5dSMike Smith }
99515e32d5dSMike Smith 
99615e32d5dSMike Smith static ACPI_STATUS
99715e32d5dSMike Smith EcWrite(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data)
99815e32d5dSMike Smith {
999ef2374f7SNate Lawson     ACPI_STATUS	status;
1000ef2374f7SNate Lawson     UINT8 data;
100183dcc133SNate Lawson     u_int gen_count;
100215e32d5dSMike Smith 
1003f4b7de15SNate Lawson     ACPI_SERIAL_ASSERT(ec);
1004c4a9fa45SNate Lawson     CTR2(KTR_ACPI, "ec write to %#x, data %#x", Address, *Data);
100515e32d5dSMike Smith 
10061f04e8f5SNate Lawson     /* If we can't start burst mode, continue anyway. */
1007ef2374f7SNate Lawson     status = EcCommand(sc, EC_COMMAND_BURST_ENABLE);
1008ef2374f7SNate Lawson     if (status == AE_OK) {
1009ef2374f7SNate Lawson     	data = EC_GET_DATA(sc);
1010ef2374f7SNate Lawson 	if (data == EC_BURST_ACK) {
1011ef2374f7SNate Lawson 	    CTR0(KTR_ACPI, "ec burst enabled");
1012ef2374f7SNate Lawson 	    sc->ec_burstactive = TRUE;
1013ef2374f7SNate Lawson 	}
1014ef2374f7SNate Lawson     }
101515e32d5dSMike Smith 
1016ef2374f7SNate Lawson     status = EcCommand(sc, EC_COMMAND_WRITE);
1017ef2374f7SNate Lawson     if (ACPI_FAILURE(status))
1018ef2374f7SNate Lawson 	return (status);
101915e32d5dSMike Smith 
102083dcc133SNate Lawson     gen_count = sc->ec_gencount;
102115e32d5dSMike Smith     EC_SET_DATA(sc, Address);
102283dcc133SNate Lawson     status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count);
1023ef2374f7SNate Lawson     if (ACPI_FAILURE(status)) {
102483dcc133SNate Lawson 	device_printf(sc->ec_dev, "EcRead: failed waiting for sent address\n");
1025ef2374f7SNate Lawson 	return (status);
102615e32d5dSMike Smith     }
102715e32d5dSMike Smith 
102883dcc133SNate Lawson     gen_count = sc->ec_gencount;
102915e32d5dSMike Smith     EC_SET_DATA(sc, *Data);
103083dcc133SNate Lawson     status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count);
1031ef2374f7SNate Lawson     if (ACPI_FAILURE(status)) {
103283dcc133SNate Lawson 	device_printf(sc->ec_dev, "EcWrite: failed waiting for sent data\n");
1033ef2374f7SNate Lawson 	return (status);
103415e32d5dSMike Smith     }
103515e32d5dSMike Smith 
10361f04e8f5SNate Lawson     if (sc->ec_burstactive) {
103783dcc133SNate Lawson 	sc->ec_burstactive = FALSE;
1038ef2374f7SNate Lawson 	status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
1039ef2374f7SNate Lawson 	if (ACPI_FAILURE(status))
1040ef2374f7SNate Lawson 	    return (status);
1041ef2374f7SNate Lawson 	CTR0(KTR_ACPI, "ec disabled burst ok");
10421f04e8f5SNate Lawson     }
104315e32d5dSMike Smith 
104415e32d5dSMike Smith     return (AE_OK);
104515e32d5dSMike Smith }
1046