xref: /freebsd/sys/dev/acpica/acpi_ec.c (revision a88e22b7ad0a8400875be9c3d02d09d0a8010b9c)
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 
45129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h>
46129d3046SJung-uk Kim #include <contrib/dev/acpica/include/accommon.h>
47129d3046SJung-uk Kim 
4815e32d5dSMike Smith #include <dev/acpica/acpivar.h>
4915e32d5dSMike Smith 
50f4b7de15SNate Lawson /* Hooks for the ACPI CA debugging infrastructure */
51a9cf0dffSMike Smith #define _COMPONENT	ACPI_EC
52dbd0058aSMike Smith ACPI_MODULE_NAME("EC")
530ae55423SMike Smith 
54da3b867eSMike Smith /*
55da3b867eSMike Smith  * EC_COMMAND:
56da3b867eSMike Smith  * -----------
57da3b867eSMike Smith  */
58da3b867eSMike Smith typedef UINT8				EC_COMMAND;
59da3b867eSMike Smith 
60da3b867eSMike Smith #define EC_COMMAND_UNKNOWN		((EC_COMMAND) 0x00)
61da3b867eSMike Smith #define EC_COMMAND_READ			((EC_COMMAND) 0x80)
62da3b867eSMike Smith #define EC_COMMAND_WRITE		((EC_COMMAND) 0x81)
63da3b867eSMike Smith #define EC_COMMAND_BURST_ENABLE		((EC_COMMAND) 0x82)
64da3b867eSMike Smith #define EC_COMMAND_BURST_DISABLE	((EC_COMMAND) 0x83)
65da3b867eSMike Smith #define EC_COMMAND_QUERY		((EC_COMMAND) 0x84)
66da3b867eSMike Smith 
67da3b867eSMike Smith /*
68da3b867eSMike Smith  * EC_STATUS:
69da3b867eSMike Smith  * ----------
70da3b867eSMike Smith  * The encoding of the EC status register is illustrated below.
71da3b867eSMike Smith  * Note that a set bit (1) indicates the property is TRUE
72da3b867eSMike Smith  * (e.g. if bit 0 is set then the output buffer is full).
73da3b867eSMike Smith  * +-+-+-+-+-+-+-+-+
74da3b867eSMike Smith  * |7|6|5|4|3|2|1|0|
75da3b867eSMike Smith  * +-+-+-+-+-+-+-+-+
76da3b867eSMike Smith  *  | | | | | | | |
77da3b867eSMike Smith  *  | | | | | | | +- Output Buffer Full?
78da3b867eSMike Smith  *  | | | | | | +--- Input Buffer Full?
79da3b867eSMike Smith  *  | | | | | +----- <reserved>
80da3b867eSMike Smith  *  | | | | +------- Data Register is Command Byte?
81da3b867eSMike Smith  *  | | | +--------- Burst Mode Enabled?
82da3b867eSMike Smith  *  | | +----------- SCI Event?
83da3b867eSMike Smith  *  | +------------- SMI Event?
84ef2374f7SNate Lawson  *  +--------------- <reserved>
85da3b867eSMike Smith  *
86da3b867eSMike Smith  */
87da3b867eSMike Smith typedef UINT8				EC_STATUS;
88da3b867eSMike Smith 
89da3b867eSMike Smith #define EC_FLAG_OUTPUT_BUFFER		((EC_STATUS) 0x01)
90da3b867eSMike Smith #define EC_FLAG_INPUT_BUFFER		((EC_STATUS) 0x02)
91ef2374f7SNate Lawson #define EC_FLAG_DATA_IS_CMD		((EC_STATUS) 0x08)
92da3b867eSMike Smith #define EC_FLAG_BURST_MODE		((EC_STATUS) 0x10)
93da3b867eSMike Smith 
94da3b867eSMike Smith /*
95da3b867eSMike Smith  * EC_EVENT:
96da3b867eSMike Smith  * ---------
97da3b867eSMike Smith  */
98da3b867eSMike Smith typedef UINT8				EC_EVENT;
99da3b867eSMike Smith 
100da3b867eSMike Smith #define EC_EVENT_UNKNOWN		((EC_EVENT) 0x00)
101da3b867eSMike Smith #define EC_EVENT_OUTPUT_BUFFER_FULL	((EC_EVENT) 0x01)
102da3b867eSMike Smith #define EC_EVENT_INPUT_BUFFER_EMPTY	((EC_EVENT) 0x02)
103da3b867eSMike Smith #define EC_EVENT_SCI			((EC_EVENT) 0x20)
104ef2374f7SNate Lawson #define EC_EVENT_SMI			((EC_EVENT) 0x40)
105ef2374f7SNate Lawson 
106ef2374f7SNate Lawson /* Data byte returned after burst enable indicating it was successful. */
107ef2374f7SNate Lawson #define EC_BURST_ACK			0x90
108da3b867eSMike Smith 
109da3b867eSMike Smith /*
110da3b867eSMike Smith  * Register access primitives
111da3b867eSMike Smith  */
112da3b867eSMike Smith #define EC_GET_DATA(sc)							\
113da3b867eSMike Smith 	bus_space_read_1((sc)->ec_data_tag, (sc)->ec_data_handle, 0)
114da3b867eSMike Smith 
115da3b867eSMike Smith #define EC_SET_DATA(sc, v)						\
116da3b867eSMike Smith 	bus_space_write_1((sc)->ec_data_tag, (sc)->ec_data_handle, 0, (v))
117da3b867eSMike Smith 
118da3b867eSMike Smith #define EC_GET_CSR(sc)							\
119da3b867eSMike Smith 	bus_space_read_1((sc)->ec_csr_tag, (sc)->ec_csr_handle, 0)
120da3b867eSMike Smith 
121da3b867eSMike Smith #define EC_SET_CSR(sc, v)						\
122da3b867eSMike Smith 	bus_space_write_1((sc)->ec_csr_tag, (sc)->ec_csr_handle, 0, (v))
123da3b867eSMike Smith 
1240025fb0fSNate Lawson /* Additional params to pass from the probe routine */
1250025fb0fSNate Lawson struct acpi_ec_params {
1260025fb0fSNate Lawson     int		glk;
1270025fb0fSNate Lawson     int		gpe_bit;
1280025fb0fSNate Lawson     ACPI_HANDLE	gpe_handle;
1290025fb0fSNate Lawson     int		uid;
1300025fb0fSNate Lawson };
1310025fb0fSNate Lawson 
132da3b867eSMike Smith /*
133da3b867eSMike Smith  * Driver softc.
134da3b867eSMike Smith  */
13515e32d5dSMike Smith struct acpi_ec_softc {
13615e32d5dSMike Smith     device_t		ec_dev;
13715e32d5dSMike Smith     ACPI_HANDLE		ec_handle;
1380025fb0fSNate Lawson     int			ec_uid;
1390025fb0fSNate Lawson     ACPI_HANDLE		ec_gpehandle;
1403a371f32SNate Lawson     UINT8		ec_gpebit;
14115e32d5dSMike Smith 
14215e32d5dSMike Smith     int			ec_data_rid;
14315e32d5dSMike Smith     struct resource	*ec_data_res;
14415e32d5dSMike Smith     bus_space_tag_t	ec_data_tag;
14515e32d5dSMike Smith     bus_space_handle_t	ec_data_handle;
14615e32d5dSMike Smith 
14715e32d5dSMike Smith     int			ec_csr_rid;
14815e32d5dSMike Smith     struct resource	*ec_csr_res;
14915e32d5dSMike Smith     bus_space_tag_t	ec_csr_tag;
15015e32d5dSMike Smith     bus_space_handle_t	ec_csr_handle;
15115e32d5dSMike Smith 
1521f04e8f5SNate Lawson     int			ec_glk;
1531f04e8f5SNate Lawson     int			ec_glkhandle;
154ef2374f7SNate Lawson     int			ec_burstactive;
155ef2374f7SNate Lawson     int			ec_sci_pend;
15683dcc133SNate Lawson     u_int		ec_gencount;
1570bfeadedSTakanori Watanabe     int			ec_suspending;
15815e32d5dSMike Smith };
15915e32d5dSMike Smith 
1601f04e8f5SNate Lawson /*
161f4b7de15SNate Lawson  * XXX njl
1621f04e8f5SNate Lawson  * I couldn't find it in the spec but other implementations also use a
1631f04e8f5SNate Lawson  * value of 1 ms for the time to acquire global lock.
1641f04e8f5SNate Lawson  */
1651f04e8f5SNate Lawson #define EC_LOCK_TIMEOUT	1000
1664690674eSMitsuru IWASAKI 
167ef2374f7SNate Lawson /* Default delay in microseconds between each run of the status polling loop. */
16883dcc133SNate Lawson #define EC_POLL_DELAY	5
169ef2374f7SNate Lawson 
170ef2374f7SNate Lawson /* Total time in ms spent waiting for a response from EC. */
17183dcc133SNate Lawson #define EC_TIMEOUT	750
17215e32d5dSMike Smith 
173ff40920eSNate Lawson #define EVENT_READY(event, status)			\
174ff40920eSNate Lawson 	(((event) == EC_EVENT_OUTPUT_BUFFER_FULL &&	\
175ff40920eSNate Lawson 	 ((status) & EC_FLAG_OUTPUT_BUFFER) != 0) ||	\
176ff40920eSNate Lawson 	 ((event) == EC_EVENT_INPUT_BUFFER_EMPTY && 	\
177ff40920eSNate Lawson 	 ((status) & EC_FLAG_INPUT_BUFFER) == 0))
178ff40920eSNate Lawson 
179f4b7de15SNate Lawson ACPI_SERIAL_DECL(ec, "ACPI embedded controller");
180f4b7de15SNate Lawson 
181ef2374f7SNate Lawson SYSCTL_DECL(_debug_acpi);
182ef2374f7SNate Lawson SYSCTL_NODE(_debug_acpi, OID_AUTO, ec, CTLFLAG_RD, NULL, "EC debugging");
183ef2374f7SNate Lawson 
184675e5627SNate Lawson static int	ec_burst_mode;
185ef2374f7SNate Lawson TUNABLE_INT("debug.acpi.ec.burst", &ec_burst_mode);
186675e5627SNate Lawson SYSCTL_INT(_debug_acpi_ec, OID_AUTO, burst, CTLFLAG_RW, &ec_burst_mode, 0,
187ef2374f7SNate Lawson     "Enable use of burst mode (faster for nearly all systems)");
18883dcc133SNate Lawson static int	ec_polled_mode;
18983dcc133SNate Lawson TUNABLE_INT("debug.acpi.ec.polled", &ec_polled_mode);
19083dcc133SNate Lawson SYSCTL_INT(_debug_acpi_ec, OID_AUTO, polled, CTLFLAG_RW, &ec_polled_mode, 0,
19183dcc133SNate Lawson     "Force use of polled mode (only if interrupt mode doesn't work)");
192ef2374f7SNate Lawson static int	ec_timeout = EC_TIMEOUT;
193ef2374f7SNate Lawson TUNABLE_INT("debug.acpi.ec.timeout", &ec_timeout);
194ef2374f7SNate Lawson SYSCTL_INT(_debug_acpi_ec, OID_AUTO, timeout, CTLFLAG_RW, &ec_timeout,
195ef2374f7SNate Lawson     EC_TIMEOUT, "Total time spent waiting for a response (poll+sleep)");
196ef2374f7SNate Lawson 
19783dcc133SNate Lawson static ACPI_STATUS
19883dcc133SNate Lawson EcLock(struct acpi_ec_softc *sc)
19915e32d5dSMike Smith {
200f4b7de15SNate Lawson     ACPI_STATUS	status;
20115e32d5dSMike Smith 
20235440dd3SNate Lawson     /* If _GLK is non-zero, acquire the global lock. */
20335440dd3SNate Lawson     status = AE_OK;
20435440dd3SNate Lawson     if (sc->ec_glk) {
20535440dd3SNate Lawson 	status = AcpiAcquireGlobalLock(EC_LOCK_TIMEOUT, &sc->ec_glkhandle);
20635440dd3SNate Lawson 	if (ACPI_FAILURE(status))
20735440dd3SNate Lawson 	    return (status);
20835440dd3SNate Lawson     }
209f4b7de15SNate Lawson     ACPI_SERIAL_BEGIN(ec);
21015e32d5dSMike Smith     return (status);
21115e32d5dSMike Smith }
21215e32d5dSMike Smith 
21383dcc133SNate Lawson static void
21415e32d5dSMike Smith EcUnlock(struct acpi_ec_softc *sc)
21515e32d5dSMike Smith {
216f4b7de15SNate Lawson     ACPI_SERIAL_END(ec);
21735440dd3SNate Lawson     if (sc->ec_glk)
21835440dd3SNate Lawson 	AcpiReleaseGlobalLock(sc->ec_glkhandle);
21915e32d5dSMike Smith }
22015e32d5dSMike Smith 
221f3fc4f8bSNate Lawson static uint32_t		EcGpeHandler(void *Context);
22215e32d5dSMike Smith static ACPI_STATUS	EcSpaceSetup(ACPI_HANDLE Region, UINT32 Function,
22315e32d5dSMike Smith 				void *Context, void **return_Context);
224464c662eSNate Lawson static ACPI_STATUS	EcSpaceHandler(UINT32 Function,
225464c662eSNate Lawson 				ACPI_PHYSICAL_ADDRESS Address,
226e115a49fSJung-uk Kim 				UINT32 Width, UINT64 *Value,
22715e32d5dSMike Smith 				void *Context, void *RegionContext);
22883dcc133SNate Lawson static ACPI_STATUS	EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event,
22983dcc133SNate Lawson 				u_int gen_count);
2301f04e8f5SNate Lawson static ACPI_STATUS	EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd);
231464c662eSNate Lawson static ACPI_STATUS	EcRead(struct acpi_ec_softc *sc, UINT8 Address,
232464c662eSNate Lawson 				UINT8 *Data);
233464c662eSNate Lawson static ACPI_STATUS	EcWrite(struct acpi_ec_softc *sc, UINT8 Address,
234e115a49fSJung-uk Kim 				UINT8 Data);
23515e32d5dSMike Smith static int		acpi_ec_probe(device_t dev);
23615e32d5dSMike Smith static int		acpi_ec_attach(device_t dev);
2370bfeadedSTakanori Watanabe static int		acpi_ec_suspend(device_t dev);
2380bfeadedSTakanori Watanabe static int		acpi_ec_resume(device_t dev);
239340a7f6aSNate Lawson static int		acpi_ec_shutdown(device_t dev);
240e33bea8dSNate Lawson static int		acpi_ec_read_method(device_t dev, u_int addr,
2419a179dd8SJung-uk Kim 				UINT64 *val, int width);
242e33bea8dSNate Lawson static int		acpi_ec_write_method(device_t dev, u_int addr,
2439a179dd8SJung-uk Kim 				UINT64 val, int width);
24415e32d5dSMike Smith 
24515e32d5dSMike Smith static device_method_t acpi_ec_methods[] = {
24615e32d5dSMike Smith     /* Device interface */
24715e32d5dSMike Smith     DEVMETHOD(device_probe,	acpi_ec_probe),
24815e32d5dSMike Smith     DEVMETHOD(device_attach,	acpi_ec_attach),
2490bfeadedSTakanori Watanabe     DEVMETHOD(device_suspend,	acpi_ec_suspend),
2500bfeadedSTakanori Watanabe     DEVMETHOD(device_resume,	acpi_ec_resume),
251340a7f6aSNate Lawson     DEVMETHOD(device_shutdown,	acpi_ec_shutdown),
25215e32d5dSMike Smith 
253e33bea8dSNate Lawson     /* Embedded controller interface */
254e33bea8dSNate Lawson     DEVMETHOD(acpi_ec_read,	acpi_ec_read_method),
255e33bea8dSNate Lawson     DEVMETHOD(acpi_ec_write,	acpi_ec_write_method),
256e33bea8dSNate Lawson 
25715e32d5dSMike Smith     {0, 0}
25815e32d5dSMike Smith };
25915e32d5dSMike Smith 
26015e32d5dSMike Smith static driver_t acpi_ec_driver = {
26115e32d5dSMike Smith     "acpi_ec",
26215e32d5dSMike Smith     acpi_ec_methods,
26315e32d5dSMike Smith     sizeof(struct acpi_ec_softc),
26415e32d5dSMike Smith };
26515e32d5dSMike Smith 
2663273b005SMike Smith static devclass_t acpi_ec_devclass;
26715e32d5dSMike Smith DRIVER_MODULE(acpi_ec, acpi, acpi_ec_driver, acpi_ec_devclass, 0, 0);
26864278df5SNate Lawson MODULE_DEPEND(acpi_ec, acpi, 1, 1, 1);
26915e32d5dSMike Smith 
27015e32d5dSMike Smith /*
271f8335e3aSNate Lawson  * Look for an ECDT and if we find one, set up default GPE and
272f8335e3aSNate Lawson  * space handlers to catch attempts to access EC space before
27315e32d5dSMike Smith  * we have a real driver instance in place.
27483dcc133SNate Lawson  *
27583dcc133SNate Lawson  * TODO: Some old Gateway laptops need us to fake up an ECDT or
27683dcc133SNate Lawson  * otherwise attach early so that _REG methods can run.
27715e32d5dSMike Smith  */
278f8335e3aSNate Lawson void
279f8335e3aSNate Lawson acpi_ec_ecdt_probe(device_t parent)
28015e32d5dSMike Smith {
281f8335e3aSNate Lawson     ACPI_TABLE_ECDT *ecdt;
282f8335e3aSNate Lawson     ACPI_STATUS	     status;
283f8335e3aSNate Lawson     device_t	     child;
284f8335e3aSNate Lawson     ACPI_HANDLE	     h;
2850025fb0fSNate Lawson     struct acpi_ec_params *params;
286f8335e3aSNate Lawson 
287b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
28815e32d5dSMike Smith 
289f8335e3aSNate Lawson     /* Find and validate the ECDT. */
2902be4e471SJung-uk Kim     status = AcpiGetTable(ACPI_SIG_ECDT, 1, (ACPI_TABLE_HEADER **)&ecdt);
291f8335e3aSNate Lawson     if (ACPI_FAILURE(status) ||
2922be4e471SJung-uk Kim 	ecdt->Control.BitWidth != 8 ||
2932be4e471SJung-uk Kim 	ecdt->Data.BitWidth != 8) {
294f8335e3aSNate Lawson 	return;
29515e32d5dSMike Smith     }
29615e32d5dSMike Smith 
297f8335e3aSNate Lawson     /* Create the child device with the given unit number. */
2982be4e471SJung-uk Kim     child = BUS_ADD_CHILD(parent, 0, "acpi_ec", ecdt->Uid);
299f8335e3aSNate Lawson     if (child == NULL) {
3000025fb0fSNate Lawson 	printf("%s: can't add child\n", __func__);
301f8335e3aSNate Lawson 	return;
302f8335e3aSNate Lawson     }
303f8335e3aSNate Lawson 
304f8335e3aSNate Lawson     /* Find and save the ACPI handle for this device. */
3052be4e471SJung-uk Kim     status = AcpiGetHandle(NULL, ecdt->Id, &h);
306f8335e3aSNate Lawson     if (ACPI_FAILURE(status)) {
307f8335e3aSNate Lawson 	device_delete_child(parent, child);
3080025fb0fSNate Lawson 	printf("%s: can't get handle\n", __func__);
309f8335e3aSNate Lawson 	return;
310f8335e3aSNate Lawson     }
311f8335e3aSNate Lawson     acpi_set_handle(child, h);
312f8335e3aSNate Lawson 
313f8335e3aSNate Lawson     /* Set the data and CSR register addresses. */
3142be4e471SJung-uk Kim     bus_set_resource(child, SYS_RES_IOPORT, 0, ecdt->Data.Address,
315f8335e3aSNate Lawson 	/*count*/1);
3162be4e471SJung-uk Kim     bus_set_resource(child, SYS_RES_IOPORT, 1, ecdt->Control.Address,
317f8335e3aSNate Lawson 	/*count*/1);
318f8335e3aSNate Lawson 
31915e32d5dSMike Smith     /*
320f8335e3aSNate Lawson      * Store values for the probe/attach routines to use.  Store the
321c868ac7dSNate Lawson      * ECDT GPE bit and set the global lock flag according to _GLK.
3220025fb0fSNate Lawson      * Note that it is not perfectly correct to be evaluating a method
3230025fb0fSNate Lawson      * before initializing devices, but in practice this function
3240025fb0fSNate Lawson      * should be safe to call at this point.
32515e32d5dSMike Smith      */
3260025fb0fSNate Lawson     params = malloc(sizeof(struct acpi_ec_params), M_TEMP, M_WAITOK | M_ZERO);
3270025fb0fSNate Lawson     params->gpe_handle = NULL;
3282be4e471SJung-uk Kim     params->gpe_bit = ecdt->Gpe;
3292be4e471SJung-uk Kim     params->uid = ecdt->Uid;
3300025fb0fSNate Lawson     acpi_GetInteger(h, "_GLK", &params->glk);
3310025fb0fSNate Lawson     acpi_set_private(child, params);
332f8335e3aSNate Lawson 
333f8335e3aSNate Lawson     /* Finish the attach process. */
334f8335e3aSNate Lawson     if (device_probe_and_attach(child) != 0)
335f8335e3aSNate Lawson 	device_delete_child(parent, child);
336f8335e3aSNate Lawson }
337f8335e3aSNate Lawson 
33815e32d5dSMike Smith static int
33915e32d5dSMike Smith acpi_ec_probe(device_t dev)
34015e32d5dSMike Smith {
3410025fb0fSNate Lawson     ACPI_BUFFER buf;
342f8335e3aSNate Lawson     ACPI_HANDLE h;
3430025fb0fSNate Lawson     ACPI_OBJECT *obj;
344f8335e3aSNate Lawson     ACPI_STATUS status;
345f8335e3aSNate Lawson     device_t	peer;
346f8335e3aSNate Lawson     char	desc[64];
347f6eb382cSAndriy Gapon     int		ecdt;
3480025fb0fSNate Lawson     int		ret;
3490025fb0fSNate Lawson     struct acpi_ec_params *params;
3505fcc8a58SNate Lawson     static char *ec_ids[] = { "PNP0C09", NULL };
3510ae55423SMike Smith 
352c72508e2SNate Lawson     /* Check that this is a device and that EC is not disabled. */
353c72508e2SNate Lawson     if (acpi_get_type(dev) != ACPI_TYPE_DEVICE || acpi_disabled("ec"))
354f8335e3aSNate Lawson 	return (ENXIO);
35515e32d5dSMike Smith 
35615e32d5dSMike Smith     /*
357f8335e3aSNate Lawson      * If probed via ECDT, set description and continue.  Otherwise,
358f8335e3aSNate Lawson      * we can access the namespace and make sure this is not a
359f8335e3aSNate Lawson      * duplicate probe.
36015e32d5dSMike Smith      */
3610025fb0fSNate Lawson     ret = ENXIO;
362f6eb382cSAndriy Gapon     ecdt = 0;
3630025fb0fSNate Lawson     buf.Pointer = NULL;
3640025fb0fSNate Lawson     buf.Length = ACPI_ALLOCATE_BUFFER;
3650025fb0fSNate Lawson     params = acpi_get_private(dev);
366f6eb382cSAndriy Gapon     if (params != NULL) {
367f6eb382cSAndriy Gapon 	ecdt = 1;
3681f04e8f5SNate Lawson 	ret = 0;
369bfd1961bSAndriy Gapon     } else if (ACPI_ID_PROBE(device_get_parent(dev), dev, ec_ids)) {
3700025fb0fSNate Lawson 	params = malloc(sizeof(struct acpi_ec_params), M_TEMP,
3710025fb0fSNate Lawson 			M_WAITOK | M_ZERO);
372f8335e3aSNate Lawson 	h = acpi_get_handle(dev);
373f8335e3aSNate Lawson 
374f8335e3aSNate Lawson 	/*
375f8335e3aSNate Lawson 	 * Read the unit ID to check for duplicate attach and the
376f8335e3aSNate Lawson 	 * global lock value to see if we should acquire it when
377f8335e3aSNate Lawson 	 * accessing the EC.
378f8335e3aSNate Lawson 	 */
3790025fb0fSNate Lawson 	status = acpi_GetInteger(h, "_UID", &params->uid);
380f8335e3aSNate Lawson 	if (ACPI_FAILURE(status))
3810025fb0fSNate Lawson 	    params->uid = 0;
3820025fb0fSNate Lawson 	status = acpi_GetInteger(h, "_GLK", &params->glk);
383f8335e3aSNate Lawson 	if (ACPI_FAILURE(status))
3840025fb0fSNate Lawson 	    params->glk = 0;
385f8335e3aSNate Lawson 
386f8335e3aSNate Lawson 	/*
387f8335e3aSNate Lawson 	 * Evaluate the _GPE method to find the GPE bit used by the EC to
3880025fb0fSNate Lawson 	 * signal status (SCI).  If it's a package, it contains a reference
3890025fb0fSNate Lawson 	 * and GPE bit, similar to _PRW.
390f8335e3aSNate Lawson 	 */
3910025fb0fSNate Lawson 	status = AcpiEvaluateObject(h, "_GPE", NULL, &buf);
392f8335e3aSNate Lawson 	if (ACPI_FAILURE(status)) {
393f8335e3aSNate Lawson 	    device_printf(dev, "can't evaluate _GPE - %s\n",
394f8335e3aSNate Lawson 			  AcpiFormatException(status));
395a6761eb3SNate Lawson 	    goto out;
396f8335e3aSNate Lawson 	}
3970025fb0fSNate Lawson 	obj = (ACPI_OBJECT *)buf.Pointer;
3980025fb0fSNate Lawson 	if (obj == NULL)
399a6761eb3SNate Lawson 	    goto out;
4000025fb0fSNate Lawson 
4010025fb0fSNate Lawson 	switch (obj->Type) {
4020025fb0fSNate Lawson 	case ACPI_TYPE_INTEGER:
4030025fb0fSNate Lawson 	    params->gpe_handle = NULL;
4040025fb0fSNate Lawson 	    params->gpe_bit = obj->Integer.Value;
4050025fb0fSNate Lawson 	    break;
4060025fb0fSNate Lawson 	case ACPI_TYPE_PACKAGE:
4070025fb0fSNate Lawson 	    if (!ACPI_PKG_VALID(obj, 2))
4080025fb0fSNate Lawson 		goto out;
4090025fb0fSNate Lawson 	    params->gpe_handle =
4100025fb0fSNate Lawson 		acpi_GetReference(NULL, &obj->Package.Elements[0]);
4110025fb0fSNate Lawson 	    if (params->gpe_handle == NULL ||
4120025fb0fSNate Lawson 		acpi_PkgInt32(obj, 1, &params->gpe_bit) != 0)
4130025fb0fSNate Lawson 		goto out;
4140025fb0fSNate Lawson 	    break;
4150025fb0fSNate Lawson 	default:
4160025fb0fSNate Lawson 	    device_printf(dev, "_GPE has invalid type %d\n", obj->Type);
4170025fb0fSNate Lawson 	    goto out;
4180025fb0fSNate Lawson 	}
419f8335e3aSNate Lawson 
420f8335e3aSNate Lawson 	/* Store the values we got from the namespace for attach. */
4210025fb0fSNate Lawson 	acpi_set_private(dev, params);
422f8335e3aSNate Lawson 
423f8335e3aSNate Lawson 	/*
424f8335e3aSNate Lawson 	 * Check for a duplicate probe.  This can happen when a probe
425c868ac7dSNate Lawson 	 * via ECDT succeeded already.  If this is a duplicate, disable
426c868ac7dSNate Lawson 	 * this device.
427f8335e3aSNate Lawson 	 */
4280025fb0fSNate Lawson 	peer = devclass_get_device(acpi_ec_devclass, params->uid);
429c868ac7dSNate Lawson 	if (peer == NULL || !device_is_alive(peer))
430f8335e3aSNate Lawson 	    ret = 0;
431c868ac7dSNate Lawson 	else
432c868ac7dSNate Lawson 	    device_disable(dev);
433c868ac7dSNate Lawson     }
434f8335e3aSNate Lawson 
4350025fb0fSNate Lawson out:
436c868ac7dSNate Lawson     if (ret == 0) {
437c868ac7dSNate Lawson 	snprintf(desc, sizeof(desc), "Embedded Controller: GPE %#x%s%s",
4380025fb0fSNate Lawson 		 params->gpe_bit, (params->glk) ? ", GLK" : "",
439f6eb382cSAndriy Gapon 		 ecdt ? ", ECDT" : "");
440c868ac7dSNate Lawson 	device_set_desc_copy(dev, desc);
44115e32d5dSMike Smith     }
4421f04e8f5SNate Lawson 
4430025fb0fSNate Lawson     if (ret > 0 && params)
4440025fb0fSNate Lawson 	free(params, M_TEMP);
4450025fb0fSNate Lawson     if (buf.Pointer)
4460025fb0fSNate Lawson 	AcpiOsFree(buf.Pointer);
4471f04e8f5SNate Lawson     return (ret);
44815e32d5dSMike Smith }
44915e32d5dSMike Smith 
45015e32d5dSMike Smith static int
45115e32d5dSMike Smith acpi_ec_attach(device_t dev)
45215e32d5dSMike Smith {
45315e32d5dSMike Smith     struct acpi_ec_softc	*sc;
4540025fb0fSNate Lawson     struct acpi_ec_params	*params;
45515e32d5dSMike Smith     ACPI_STATUS			Status;
456dbd0058aSMike Smith 
457b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
4580ae55423SMike Smith 
4591f04e8f5SNate Lawson     /* Fetch/initialize softc (assumes softc is pre-zeroed). */
46015e32d5dSMike Smith     sc = device_get_softc(dev);
4610025fb0fSNate Lawson     params = acpi_get_private(dev);
46215e32d5dSMike Smith     sc->ec_dev = dev;
46315e32d5dSMike Smith     sc->ec_handle = acpi_get_handle(dev);
46415e32d5dSMike Smith 
465f8335e3aSNate Lawson     /* Retrieve previously probed values via device ivars. */
4660025fb0fSNate Lawson     sc->ec_glk = params->glk;
4670025fb0fSNate Lawson     sc->ec_gpebit = params->gpe_bit;
4680025fb0fSNate Lawson     sc->ec_gpehandle = params->gpe_handle;
4690025fb0fSNate Lawson     sc->ec_uid = params->uid;
47068fb6c48STakanori Watanabe     sc->ec_suspending = FALSE;
471943124d3SAndriy Gapon     acpi_set_private(dev, NULL);
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. */
521*a88e22b7SJung-uk Kim     Status = AcpiEnableGpe(sc->ec_gpehandle, sc->ec_gpebit);
522f3fc4f8bSNate Lawson     if (ACPI_FAILURE(Status)) {
523f3fc4f8bSNate Lawson 	device_printf(dev, "AcpiEnableGpe failed: %s\n",
524f3fc4f8bSNate Lawson 		      AcpiFormatException(Status));
525f3fc4f8bSNate Lawson 	goto error;
52615e32d5dSMike Smith     }
5271f04e8f5SNate Lawson 
5281f04e8f5SNate Lawson     ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "acpi_ec_attach complete\n"));
5291f04e8f5SNate Lawson     return (0);
530464c662eSNate Lawson 
531f3fc4f8bSNate Lawson error:
532f3fc4f8bSNate Lawson     AcpiRemoveGpeHandler(sc->ec_gpehandle, sc->ec_gpebit, &EcGpeHandler);
533f3fc4f8bSNate Lawson     AcpiRemoveAddressSpaceHandler(sc->ec_handle, ACPI_ADR_SPACE_EC,
534f3fc4f8bSNate Lawson 	EcSpaceHandler);
535f8372adeSTakanori Watanabe     if (sc->ec_csr_res)
536f8372adeSTakanori Watanabe 	bus_release_resource(sc->ec_dev, SYS_RES_IOPORT, sc->ec_csr_rid,
537f8372adeSTakanori Watanabe 			     sc->ec_csr_res);
538f8372adeSTakanori Watanabe     if (sc->ec_data_res)
539f8372adeSTakanori Watanabe 	bus_release_resource(sc->ec_dev, SYS_RES_IOPORT, sc->ec_data_rid,
540f8372adeSTakanori Watanabe 			     sc->ec_data_res);
541f3fc4f8bSNate Lawson     return (ENXIO);
5421f04e8f5SNate Lawson }
5431f04e8f5SNate Lawson 
544340a7f6aSNate Lawson static int
5450bfeadedSTakanori Watanabe acpi_ec_suspend(device_t dev)
5460bfeadedSTakanori Watanabe {
5470bfeadedSTakanori Watanabe     struct acpi_ec_softc	*sc;
5480bfeadedSTakanori Watanabe 
5490bfeadedSTakanori Watanabe     sc = device_get_softc(dev);
55068fb6c48STakanori Watanabe     sc->ec_suspending = TRUE;
5510bfeadedSTakanori Watanabe     return (0);
5520bfeadedSTakanori Watanabe }
5530bfeadedSTakanori Watanabe 
5540bfeadedSTakanori Watanabe static int
5550bfeadedSTakanori Watanabe acpi_ec_resume(device_t dev)
5560bfeadedSTakanori Watanabe {
5570bfeadedSTakanori Watanabe     struct acpi_ec_softc	*sc;
5580bfeadedSTakanori Watanabe 
5590bfeadedSTakanori Watanabe     sc = device_get_softc(dev);
56068fb6c48STakanori Watanabe     sc->ec_suspending = FALSE;
5610bfeadedSTakanori Watanabe     return (0);
5620bfeadedSTakanori Watanabe }
5630bfeadedSTakanori Watanabe 
5640bfeadedSTakanori Watanabe static int
565340a7f6aSNate Lawson acpi_ec_shutdown(device_t dev)
566340a7f6aSNate Lawson {
567340a7f6aSNate Lawson     struct acpi_ec_softc	*sc;
568340a7f6aSNate Lawson 
569340a7f6aSNate Lawson     /* Disable the GPE so we don't get EC events during shutdown. */
570340a7f6aSNate Lawson     sc = device_get_softc(dev);
571*a88e22b7SJung-uk Kim     AcpiDisableGpe(sc->ec_gpehandle, sc->ec_gpebit);
572340a7f6aSNate Lawson     return (0);
573340a7f6aSNate Lawson }
574340a7f6aSNate Lawson 
575e33bea8dSNate Lawson /* Methods to allow other devices (e.g., smbat) to read/write EC space. */
576e33bea8dSNate Lawson static int
5779a179dd8SJung-uk Kim acpi_ec_read_method(device_t dev, u_int addr, UINT64 *val, int width)
578e33bea8dSNate Lawson {
579e33bea8dSNate Lawson     struct acpi_ec_softc *sc;
580e33bea8dSNate Lawson     ACPI_STATUS status;
581e33bea8dSNate Lawson 
582e33bea8dSNate Lawson     sc = device_get_softc(dev);
583e33bea8dSNate Lawson     status = EcSpaceHandler(ACPI_READ, addr, width * 8, val, sc, NULL);
584e33bea8dSNate Lawson     if (ACPI_FAILURE(status))
585e33bea8dSNate Lawson 	return (ENXIO);
586e33bea8dSNate Lawson     return (0);
587e33bea8dSNate Lawson }
588e33bea8dSNate Lawson 
589e33bea8dSNate Lawson static int
5909a179dd8SJung-uk Kim acpi_ec_write_method(device_t dev, u_int addr, UINT64 val, int width)
591e33bea8dSNate Lawson {
592e33bea8dSNate Lawson     struct acpi_ec_softc *sc;
593e33bea8dSNate Lawson     ACPI_STATUS status;
594e33bea8dSNate Lawson 
595e33bea8dSNate Lawson     sc = device_get_softc(dev);
596e33bea8dSNate Lawson     status = EcSpaceHandler(ACPI_WRITE, addr, width * 8, &val, sc, NULL);
597e33bea8dSNate Lawson     if (ACPI_FAILURE(status))
598e33bea8dSNate Lawson 	return (ENXIO);
599e33bea8dSNate Lawson     return (0);
600e33bea8dSNate Lawson }
601e33bea8dSNate Lawson 
60215e32d5dSMike Smith static void
603c07572e7STakanori Watanabe EcGpeQueryHandler(void *Context)
60415e32d5dSMike Smith {
60515e32d5dSMike Smith     struct acpi_ec_softc	*sc = (struct acpi_ec_softc *)Context;
60615e32d5dSMike Smith     UINT8			Data;
60715e32d5dSMike Smith     ACPI_STATUS			Status;
60815e32d5dSMike Smith     char			qxx[5];
60915e32d5dSMike Smith 
610b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
6111f04e8f5SNate Lawson     KASSERT(Context != NULL, ("EcGpeQueryHandler called with NULL"));
61215e32d5dSMike Smith 
613ef2374f7SNate Lawson     /* Serialize user access with EcSpaceHandler(). */
61483dcc133SNate Lawson     Status = EcLock(sc);
6153a371f32SNate Lawson     if (ACPI_FAILURE(Status)) {
61683dcc133SNate Lawson 	device_printf(sc->ec_dev, "GpeQuery lock error: %s\n",
61783dcc133SNate Lawson 	    AcpiFormatException(Status));
6183a371f32SNate Lawson 	return;
6193a371f32SNate Lawson     }
6203a371f32SNate Lawson 
62115e32d5dSMike Smith     /*
6223a371f32SNate Lawson      * Send a query command to the EC to find out which _Qxx call it
6233a371f32SNate Lawson      * wants to make.  This command clears the SCI bit and also the
62483dcc133SNate Lawson      * interrupt source since we are edge-triggered.  To prevent the GPE
62583dcc133SNate Lawson      * that may arise from running the query from causing another query
62683dcc133SNate Lawson      * to be queued, we clear the pending flag only after running it.
6273a371f32SNate Lawson      */
6283a371f32SNate Lawson     Status = EcCommand(sc, EC_COMMAND_QUERY);
62983dcc133SNate Lawson     sc->ec_sci_pend = FALSE;
630dbd0058aSMike Smith     if (ACPI_FAILURE(Status)) {
6313a371f32SNate Lawson 	EcUnlock(sc);
63283dcc133SNate Lawson 	device_printf(sc->ec_dev, "GPE query failed: %s\n",
63383dcc133SNate Lawson 	    AcpiFormatException(Status));
63483dcc133SNate Lawson 	return;
63515e32d5dSMike Smith     }
6363a371f32SNate Lawson     Data = EC_GET_DATA(sc);
637ef2374f7SNate Lawson 
63883dcc133SNate Lawson     /*
63983dcc133SNate Lawson      * We have to unlock before running the _Qxx method below since that
64083dcc133SNate Lawson      * method may attempt to read/write from EC address space, causing
64183dcc133SNate Lawson      * recursive acquisition of the lock.
64283dcc133SNate Lawson      */
6433a371f32SNate Lawson     EcUnlock(sc);
64415e32d5dSMike Smith 
6451f04e8f5SNate Lawson     /* Ignore the value for "no outstanding event". (13.3.5) */
64683dcc133SNate Lawson     CTR2(KTR_ACPI, "ec query ok,%s running _Q%02X", Data ? "" : " not", Data);
6471f04e8f5SNate Lawson     if (Data == 0)
64883dcc133SNate Lawson 	return;
6491f04e8f5SNate Lawson 
6501f04e8f5SNate Lawson     /* Evaluate _Qxx to respond to the controller. */
65183dcc133SNate Lawson     snprintf(qxx, sizeof(qxx), "_Q%02X", Data);
652b3919c8dSMark Santcroos     AcpiUtStrupr(qxx);
653ee785aa9SJohn Baldwin     Status = AcpiEvaluateObject(sc->ec_handle, qxx, NULL, NULL);
6541f04e8f5SNate Lawson     if (ACPI_FAILURE(Status) && Status != AE_NOT_FOUND) {
65583dcc133SNate Lawson 	device_printf(sc->ec_dev, "evaluation of query method %s failed: %s\n",
656bfae45aaSMike Smith 	    qxx, AcpiFormatException(Status));
65715e32d5dSMike Smith     }
658c07572e7STakanori Watanabe }
65942f6d122SMike Smith 
660a9cf0dffSMike Smith /*
66183dcc133SNate Lawson  * The GPE handler is called when IBE/OBF or SCI events occur.  We are
66283dcc133SNate Lawson  * called from an unknown lock context.
663a9cf0dffSMike Smith  */
664f3fc4f8bSNate Lawson static uint32_t
665a9cf0dffSMike Smith EcGpeHandler(void *Context)
666c07572e7STakanori Watanabe {
667c07572e7STakanori Watanabe     struct acpi_ec_softc *sc = Context;
6681f04e8f5SNate Lawson     ACPI_STATUS		       Status;
669ef2374f7SNate Lawson     EC_STATUS		       EcStatus;
670a9cf0dffSMike Smith 
6711f04e8f5SNate Lawson     KASSERT(Context != NULL, ("EcGpeHandler called with NULL"));
67283dcc133SNate Lawson     CTR0(KTR_ACPI, "ec gpe handler start");
6731f04e8f5SNate Lawson 
6741395b555SNate Lawson     /*
67583dcc133SNate Lawson      * Notify EcWaitEvent() that the status register is now fresh.  If we
67683dcc133SNate Lawson      * didn't do this, it wouldn't be possible to distinguish an old IBE
67783dcc133SNate Lawson      * from a new one, for example when doing a write transaction (writing
67883dcc133SNate Lawson      * address and then data values.)
6791395b555SNate Lawson      */
68083dcc133SNate Lawson     atomic_add_int(&sc->ec_gencount, 1);
68183dcc133SNate Lawson     wakeup(&sc->ec_gencount);
682ef2374f7SNate Lawson 
683ef2374f7SNate Lawson     /*
68483dcc133SNate Lawson      * If the EC_SCI bit of the status register is set, queue a query handler.
68583dcc133SNate Lawson      * It will run the query and _Qxx method later, under the lock.
686ef2374f7SNate Lawson      */
687ef2374f7SNate Lawson     EcStatus = EC_GET_CSR(sc);
68883dcc133SNate Lawson     if ((EcStatus & EC_EVENT_SCI) && !sc->ec_sci_pend) {
68983dcc133SNate Lawson 	CTR0(KTR_ACPI, "ec gpe queueing query handler");
6902be4e471SJung-uk Kim 	Status = AcpiOsExecute(OSL_GPE_HANDLER, EcGpeQueryHandler, Context);
69183dcc133SNate Lawson 	if (ACPI_SUCCESS(Status))
692ef2374f7SNate Lawson 	    sc->ec_sci_pend = TRUE;
69383dcc133SNate Lawson 	else
69483dcc133SNate Lawson 	    printf("EcGpeHandler: queuing GPE query handler failed\n");
6956e141df2SNate Lawson     }
696f3fc4f8bSNate Lawson     return (0);
69715e32d5dSMike Smith }
69815e32d5dSMike Smith 
69915e32d5dSMike Smith static ACPI_STATUS
700464c662eSNate Lawson EcSpaceSetup(ACPI_HANDLE Region, UINT32 Function, void *Context,
701464c662eSNate Lawson 	     void **RegionContext)
70215e32d5dSMike Smith {
70342f6d122SMike Smith 
704b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
70542f6d122SMike Smith 
70615e32d5dSMike Smith     /*
70716844a97SNate Lawson      * If deactivating a region, always set the output to NULL.  Otherwise,
70816844a97SNate Lawson      * just pass the context through.
70915e32d5dSMike Smith      */
71016844a97SNate Lawson     if (Function == ACPI_REGION_DEACTIVATE)
71116844a97SNate Lawson 	*RegionContext = NULL;
71216844a97SNate Lawson     else
71315e32d5dSMike Smith 	*RegionContext = Context;
71415e32d5dSMike Smith 
71542f6d122SMike Smith     return_ACPI_STATUS (AE_OK);
71615e32d5dSMike Smith }
71715e32d5dSMike Smith 
71815e32d5dSMike Smith static ACPI_STATUS
719e115a49fSJung-uk Kim EcSpaceHandler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address, UINT32 Width,
7209a179dd8SJung-uk Kim 	       UINT64 *Value, void *Context, void *RegionContext)
72115e32d5dSMike Smith {
72215e32d5dSMike Smith     struct acpi_ec_softc	*sc = (struct acpi_ec_softc *)Context;
7234ed391b8SNate Lawson     ACPI_STATUS			Status;
724e115a49fSJung-uk Kim     UINT8			*EcData;
725e115a49fSJung-uk Kim     UINT8			EcAddr;
726e115a49fSJung-uk Kim     int				bytes, i;
72715e32d5dSMike Smith 
728b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)Address);
7290ae55423SMike Smith 
730e115a49fSJung-uk Kim     if (Width % 8 != 0 || Value == NULL || Context == NULL)
7310ae55423SMike Smith 	return_ACPI_STATUS (AE_BAD_PARAMETER);
732e115a49fSJung-uk Kim     bytes = Width / 8;
733e115a49fSJung-uk Kim     if (Address + bytes - 1 > 0xFF)
7344ed391b8SNate Lawson 	return_ACPI_STATUS (AE_BAD_ADDRESS);
73515e32d5dSMike Smith 
7364ed391b8SNate Lawson     if (Function == ACPI_READ)
7374ed391b8SNate Lawson 	*Value = 0;
7381f04e8f5SNate Lawson     EcAddr = Address;
739e115a49fSJung-uk Kim     EcData = (UINT8 *)Value;
7404ed391b8SNate Lawson 
74183dcc133SNate Lawson     /*
74283dcc133SNate Lawson      * If booting, check if we need to run the query handler.  If so, we
74383dcc133SNate Lawson      * we call it directly here since our thread taskq is not active yet.
74483dcc133SNate Lawson      */
745c66d2b38SJung-uk Kim     if (cold || rebooting || sc->ec_suspending) {
74683dcc133SNate Lawson 	if ((EC_GET_CSR(sc) & EC_EVENT_SCI)) {
74783dcc133SNate Lawson 	    CTR0(KTR_ACPI, "ec running gpe handler directly");
74883dcc133SNate Lawson 	    EcGpeQueryHandler(sc);
74983dcc133SNate Lawson 	}
75083dcc133SNate Lawson     }
75183dcc133SNate Lawson 
75283dcc133SNate Lawson     /* Serialize with EcGpeQueryHandler() at transaction granularity. */
75383dcc133SNate Lawson     Status = EcLock(sc);
754464c662eSNate Lawson     if (ACPI_FAILURE(Status))
755f4b7de15SNate Lawson 	return_ACPI_STATUS (Status);
7561f04e8f5SNate Lawson 
757e115a49fSJung-uk Kim     /* Perform the transaction(s), based on Width. */
758e115a49fSJung-uk Kim     for (i = 0; i < bytes; i++, EcAddr++, EcData++) {
7591f04e8f5SNate Lawson 	switch (Function) {
7601f04e8f5SNate Lawson 	case ACPI_READ:
761e115a49fSJung-uk Kim 	    Status = EcRead(sc, EcAddr, EcData);
762ee785aa9SJohn Baldwin 	    break;
7631f04e8f5SNate Lawson 	case ACPI_WRITE:
764e115a49fSJung-uk Kim 	    Status = EcWrite(sc, EcAddr, *EcData);
7651f04e8f5SNate Lawson 	    break;
7661f04e8f5SNate Lawson 	default:
7671f04e8f5SNate Lawson 	    device_printf(sc->ec_dev, "invalid EcSpaceHandler function %d\n",
7681f04e8f5SNate Lawson 			  Function);
7691f04e8f5SNate Lawson 	    Status = AE_BAD_PARAMETER;
7701f04e8f5SNate Lawson 	    break;
7711f04e8f5SNate Lawson 	}
7721f04e8f5SNate Lawson 	if (ACPI_FAILURE(Status))
7734ed391b8SNate Lawson 	    break;
774ee785aa9SJohn Baldwin     }
7754ed391b8SNate Lawson 
776f4b7de15SNate Lawson     EcUnlock(sc);
7770ae55423SMike Smith     return_ACPI_STATUS (Status);
77815e32d5dSMike Smith }
7792a4ac806SMike Smith 
78015e32d5dSMike Smith static ACPI_STATUS
78183dcc133SNate Lawson EcCheckStatus(struct acpi_ec_softc *sc, const char *msg, EC_EVENT event)
78215e32d5dSMike Smith {
78383dcc133SNate Lawson     ACPI_STATUS status;
78483dcc133SNate Lawson     EC_STATUS ec_status;
78583dcc133SNate Lawson 
78683dcc133SNate Lawson     status = AE_NO_HARDWARE_RESPONSE;
78783dcc133SNate Lawson     ec_status = EC_GET_CSR(sc);
78883dcc133SNate Lawson     if (sc->ec_burstactive && !(ec_status & EC_FLAG_BURST_MODE)) {
78983dcc133SNate Lawson 	CTR1(KTR_ACPI, "ec burst disabled in waitevent (%s)", msg);
79083dcc133SNate Lawson 	sc->ec_burstactive = FALSE;
79183dcc133SNate Lawson     }
79283dcc133SNate Lawson     if (EVENT_READY(event, ec_status)) {
79383dcc133SNate Lawson 	CTR2(KTR_ACPI, "ec %s wait ready, status %#x", msg, ec_status);
79483dcc133SNate Lawson 	status = AE_OK;
79583dcc133SNate Lawson     }
79683dcc133SNate Lawson     return (status);
79783dcc133SNate Lawson }
79883dcc133SNate Lawson 
79983dcc133SNate Lawson static ACPI_STATUS
80083dcc133SNate Lawson EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event, u_int gen_count)
80183dcc133SNate Lawson {
8021f04e8f5SNate Lawson     ACPI_STATUS	Status;
80383dcc133SNate Lawson     int		count, i, slp_ival;
80415e32d5dSMike Smith 
805f4b7de15SNate Lawson     ACPI_SERIAL_ASSERT(ec);
8061f04e8f5SNate Lawson     Status = AE_NO_HARDWARE_RESPONSE;
8071de5ce99STakanori Watanabe     int need_poll = cold || rebooting || ec_polled_mode || sc->ec_suspending;
80815e32d5dSMike Smith     /*
80983dcc133SNate Lawson      * The main CPU should be much faster than the EC.  So the status should
81083dcc133SNate Lawson      * be "not ready" when we start waiting.  But if the main CPU is really
81183dcc133SNate Lawson      * slow, it's possible we see the current "ready" response.  Since that
81283dcc133SNate Lawson      * can't be distinguished from the previous response in polled mode,
81383dcc133SNate Lawson      * this is a potential issue.  We really should have interrupts enabled
81483dcc133SNate Lawson      * during boot so there is no ambiguity in polled mode.
81583dcc133SNate Lawson      *
81683dcc133SNate Lawson      * If this occurs, we add an additional delay before actually entering
81783dcc133SNate Lawson      * the status checking loop, hopefully to allow the EC to go to work
81883dcc133SNate Lawson      * and produce a non-stale status.
81915e32d5dSMike Smith      */
8201de5ce99STakanori Watanabe     if (need_poll) {
82183dcc133SNate Lawson 	static int	once;
82283dcc133SNate Lawson 
82383dcc133SNate Lawson 	if (EcCheckStatus(sc, "pre-check", Event) == AE_OK) {
82483dcc133SNate Lawson 	    if (!once) {
82583dcc133SNate Lawson 		device_printf(sc->ec_dev,
82683dcc133SNate Lawson 		    "warning: EC done before starting event wait\n");
82783dcc133SNate Lawson 		once = 1;
82883dcc133SNate Lawson 	    }
82983dcc133SNate Lawson 	    AcpiOsStall(10);
83083dcc133SNate Lawson 	}
83183dcc133SNate Lawson     }
83283dcc133SNate Lawson 
83383dcc133SNate Lawson     /* Wait for event by polling or GPE (interrupt). */
8341de5ce99STakanori Watanabe     if (need_poll) {
83583dcc133SNate Lawson 	count = (ec_timeout * 1000) / EC_POLL_DELAY;
83683dcc133SNate Lawson 	if (count == 0)
837ef2374f7SNate Lawson 	    count = 1;
838ef2374f7SNate Lawson 	for (i = 0; i < count; i++) {
83983dcc133SNate Lawson 	    Status = EcCheckStatus(sc, "poll", Event);
84083dcc133SNate Lawson 	    if (Status == AE_OK)
84115e32d5dSMike Smith 		break;
842b0eefa38SNate Lawson 	    AcpiOsStall(EC_POLL_DELAY);
8431f04e8f5SNate Lawson 	}
84483dcc133SNate Lawson     } else {
845ef2374f7SNate Lawson 	slp_ival = hz / 1000;
846ef2374f7SNate Lawson 	if (slp_ival != 0) {
84783dcc133SNate Lawson 	    count = ec_timeout;
848ef2374f7SNate Lawson 	} else {
84983dcc133SNate Lawson 	    /* hz has less than 1 ms resolution so scale timeout. */
850b0eefa38SNate Lawson 	    slp_ival = 1;
851ef2374f7SNate Lawson 	    count = ec_timeout / (1000 / hz);
852ef2374f7SNate Lawson 	}
85383dcc133SNate Lawson 
85483dcc133SNate Lawson 	/*
85583dcc133SNate Lawson 	 * Wait for the GPE to signal the status changed, checking the
85683dcc133SNate Lawson 	 * status register each time we get one.  It's possible to get a
85783dcc133SNate Lawson 	 * GPE for an event we're not interested in here (i.e., SCI for
85883dcc133SNate Lawson 	 * EC query).
85983dcc133SNate Lawson 	 */
860b0eefa38SNate Lawson 	for (i = 0; i < count; i++) {
86183dcc133SNate Lawson 	    if (gen_count != sc->ec_gencount) {
86283dcc133SNate Lawson 		/*
86383dcc133SNate Lawson 		 * Record new generation count.  It's possible the GPE was
86483dcc133SNate Lawson 		 * just to notify us that a query is needed and we need to
86583dcc133SNate Lawson 		 * wait for a second GPE to signal the completion of the
86683dcc133SNate Lawson 		 * event we are actually waiting for.
86783dcc133SNate Lawson 		 */
86883dcc133SNate Lawson 		gen_count = sc->ec_gencount;
86983dcc133SNate Lawson 		Status = EcCheckStatus(sc, "sleep", Event);
87083dcc133SNate Lawson 		if (Status == AE_OK)
871ff40920eSNate Lawson 		    break;
872ff40920eSNate Lawson 	    }
87383dcc133SNate Lawson 	    tsleep(&sc->ec_gencount, PZERO, "ecgpe", slp_ival);
8741f04e8f5SNate Lawson 	}
8751f04e8f5SNate Lawson 
87683dcc133SNate Lawson 	/*
87783dcc133SNate Lawson 	 * We finished waiting for the GPE and it never arrived.  Try to
87883dcc133SNate Lawson 	 * read the register once and trust whatever value we got.  This is
87983dcc133SNate Lawson 	 * the best we can do at this point.  Then, force polled mode on
88083dcc133SNate Lawson 	 * since this system doesn't appear to generate GPEs.
88183dcc133SNate Lawson 	 */
88283dcc133SNate Lawson 	if (Status != AE_OK) {
88383dcc133SNate Lawson 	    Status = EcCheckStatus(sc, "sleep_end", Event);
88483dcc133SNate Lawson 	    device_printf(sc->ec_dev,
88583dcc133SNate Lawson 		"wait timed out (%sresponse), forcing polled mode\n",
88683dcc133SNate Lawson 		Status == AE_OK ? "" : "no ");
88783dcc133SNate Lawson 	    ec_polled_mode = TRUE;
88883dcc133SNate Lawson 	}
88983dcc133SNate Lawson     }
89083dcc133SNate Lawson     if (Status != AE_OK)
89183dcc133SNate Lawson 	    CTR0(KTR_ACPI, "error: ec wait timed out");
8921f04e8f5SNate Lawson     return (Status);
8931f04e8f5SNate Lawson }
8941f04e8f5SNate Lawson 
8951f04e8f5SNate Lawson static ACPI_STATUS
8961f04e8f5SNate Lawson EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd)
8971f04e8f5SNate Lawson {
898ef2374f7SNate Lawson     ACPI_STATUS	status;
899ef2374f7SNate Lawson     EC_EVENT	event;
900ef2374f7SNate Lawson     EC_STATUS	ec_status;
90183dcc133SNate Lawson     u_int	gen_count;
9021f04e8f5SNate Lawson 
903f4b7de15SNate Lawson     ACPI_SERIAL_ASSERT(ec);
9041f04e8f5SNate Lawson 
905ef2374f7SNate Lawson     /* Don't use burst mode if user disabled it. */
906ef2374f7SNate Lawson     if (!ec_burst_mode && cmd == EC_COMMAND_BURST_ENABLE)
907ef2374f7SNate Lawson 	return (AE_ERROR);
908ef2374f7SNate Lawson 
9091f04e8f5SNate Lawson     /* Decide what to wait for based on command type. */
9101f04e8f5SNate Lawson     switch (cmd) {
9111f04e8f5SNate Lawson     case EC_COMMAND_READ:
91215e32d5dSMike Smith     case EC_COMMAND_WRITE:
9131f04e8f5SNate Lawson     case EC_COMMAND_BURST_DISABLE:
914ef2374f7SNate Lawson 	event = EC_EVENT_INPUT_BUFFER_EMPTY;
9151f04e8f5SNate Lawson 	break;
9161f04e8f5SNate Lawson     case EC_COMMAND_QUERY:
9171f04e8f5SNate Lawson     case EC_COMMAND_BURST_ENABLE:
918ef2374f7SNate Lawson 	event = EC_EVENT_OUTPUT_BUFFER_FULL;
91915e32d5dSMike Smith 	break;
92015e32d5dSMike Smith     default:
92183dcc133SNate Lawson 	device_printf(sc->ec_dev, "EcCommand: invalid command %#x\n", cmd);
9221f04e8f5SNate Lawson 	return (AE_BAD_PARAMETER);
923464c662eSNate Lawson     }
9241f04e8f5SNate Lawson 
9251f04e8f5SNate Lawson     /* Run the command and wait for the chosen event. */
926ef2374f7SNate Lawson     CTR1(KTR_ACPI, "ec running command %#x", cmd);
92783dcc133SNate Lawson     gen_count = sc->ec_gencount;
9281f04e8f5SNate Lawson     EC_SET_CSR(sc, cmd);
92983dcc133SNate Lawson     status = EcWaitEvent(sc, event, gen_count);
930ef2374f7SNate Lawson     if (ACPI_SUCCESS(status)) {
931ef2374f7SNate Lawson 	/* If we succeeded, burst flag should now be present. */
932ef2374f7SNate Lawson 	if (cmd == EC_COMMAND_BURST_ENABLE) {
933ef2374f7SNate Lawson 	    ec_status = EC_GET_CSR(sc);
934ef2374f7SNate Lawson 	    if ((ec_status & EC_FLAG_BURST_MODE) == 0)
935ef2374f7SNate Lawson 		status = AE_ERROR;
936ef2374f7SNate Lawson 	}
93783dcc133SNate Lawson     } else
93883dcc133SNate Lawson 	device_printf(sc->ec_dev, "EcCommand: no response to %#x\n", cmd);
939ef2374f7SNate Lawson     return (status);
94015e32d5dSMike Smith }
94115e32d5dSMike Smith 
94215e32d5dSMike Smith static ACPI_STATUS
94315e32d5dSMike Smith EcRead(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data)
94415e32d5dSMike Smith {
945ef2374f7SNate Lawson     ACPI_STATUS	status;
946ef2374f7SNate Lawson     UINT8 data;
94783dcc133SNate Lawson     u_int gen_count;
94815e32d5dSMike Smith 
949f4b7de15SNate Lawson     ACPI_SERIAL_ASSERT(ec);
950c4a9fa45SNate Lawson     CTR1(KTR_ACPI, "ec read from %#x", Address);
95115e32d5dSMike Smith 
9521f04e8f5SNate Lawson     /* If we can't start burst mode, continue anyway. */
953ef2374f7SNate Lawson     status = EcCommand(sc, EC_COMMAND_BURST_ENABLE);
954ef2374f7SNate Lawson     if (status == AE_OK) {
955ef2374f7SNate Lawson     	data = EC_GET_DATA(sc);
956ef2374f7SNate Lawson 	if (data == EC_BURST_ACK) {
957ef2374f7SNate Lawson 	    CTR0(KTR_ACPI, "ec burst enabled");
958ef2374f7SNate Lawson 	    sc->ec_burstactive = TRUE;
959ef2374f7SNate Lawson 	}
960ef2374f7SNate Lawson     }
96115e32d5dSMike Smith 
962ef2374f7SNate Lawson     status = EcCommand(sc, EC_COMMAND_READ);
963ef2374f7SNate Lawson     if (ACPI_FAILURE(status))
964ef2374f7SNate Lawson 	return (status);
96515e32d5dSMike Smith 
96683dcc133SNate Lawson     gen_count = sc->ec_gencount;
96715e32d5dSMike Smith     EC_SET_DATA(sc, Address);
96883dcc133SNate Lawson     status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL, gen_count);
969ef2374f7SNate Lawson     if (ACPI_FAILURE(status)) {
97083dcc133SNate Lawson 	device_printf(sc->ec_dev, "EcRead: failed waiting to get data\n");
971ef2374f7SNate Lawson 	return (status);
97215e32d5dSMike Smith     }
973464c662eSNate Lawson     *Data = EC_GET_DATA(sc);
97415e32d5dSMike Smith 
9751f04e8f5SNate Lawson     if (sc->ec_burstactive) {
97683dcc133SNate Lawson 	sc->ec_burstactive = FALSE;
977ef2374f7SNate Lawson 	status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
978ef2374f7SNate Lawson 	if (ACPI_FAILURE(status))
979ef2374f7SNate Lawson 	    return (status);
980ef2374f7SNate Lawson 	CTR0(KTR_ACPI, "ec disabled burst ok");
9811f04e8f5SNate Lawson     }
98215e32d5dSMike Smith 
98315e32d5dSMike Smith     return (AE_OK);
98415e32d5dSMike Smith }
98515e32d5dSMike Smith 
98615e32d5dSMike Smith static ACPI_STATUS
987e115a49fSJung-uk Kim EcWrite(struct acpi_ec_softc *sc, UINT8 Address, UINT8 Data)
98815e32d5dSMike Smith {
989ef2374f7SNate Lawson     ACPI_STATUS	status;
990ef2374f7SNate Lawson     UINT8 data;
99183dcc133SNate Lawson     u_int gen_count;
99215e32d5dSMike Smith 
993f4b7de15SNate Lawson     ACPI_SERIAL_ASSERT(ec);
994e115a49fSJung-uk Kim     CTR2(KTR_ACPI, "ec write to %#x, data %#x", Address, Data);
99515e32d5dSMike Smith 
9961f04e8f5SNate Lawson     /* If we can't start burst mode, continue anyway. */
997ef2374f7SNate Lawson     status = EcCommand(sc, EC_COMMAND_BURST_ENABLE);
998ef2374f7SNate Lawson     if (status == AE_OK) {
999ef2374f7SNate Lawson     	data = EC_GET_DATA(sc);
1000ef2374f7SNate Lawson 	if (data == EC_BURST_ACK) {
1001ef2374f7SNate Lawson 	    CTR0(KTR_ACPI, "ec burst enabled");
1002ef2374f7SNate Lawson 	    sc->ec_burstactive = TRUE;
1003ef2374f7SNate Lawson 	}
1004ef2374f7SNate Lawson     }
100515e32d5dSMike Smith 
1006ef2374f7SNate Lawson     status = EcCommand(sc, EC_COMMAND_WRITE);
1007ef2374f7SNate Lawson     if (ACPI_FAILURE(status))
1008ef2374f7SNate Lawson 	return (status);
100915e32d5dSMike Smith 
101083dcc133SNate Lawson     gen_count = sc->ec_gencount;
101115e32d5dSMike Smith     EC_SET_DATA(sc, Address);
101283dcc133SNate Lawson     status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count);
1013ef2374f7SNate Lawson     if (ACPI_FAILURE(status)) {
101483dcc133SNate Lawson 	device_printf(sc->ec_dev, "EcRead: failed waiting for sent address\n");
1015ef2374f7SNate Lawson 	return (status);
101615e32d5dSMike Smith     }
101715e32d5dSMike Smith 
101883dcc133SNate Lawson     gen_count = sc->ec_gencount;
1019e115a49fSJung-uk Kim     EC_SET_DATA(sc, Data);
102083dcc133SNate Lawson     status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count);
1021ef2374f7SNate Lawson     if (ACPI_FAILURE(status)) {
102283dcc133SNate Lawson 	device_printf(sc->ec_dev, "EcWrite: failed waiting for sent data\n");
1023ef2374f7SNate Lawson 	return (status);
102415e32d5dSMike Smith     }
102515e32d5dSMike Smith 
10261f04e8f5SNate Lawson     if (sc->ec_burstactive) {
102783dcc133SNate Lawson 	sc->ec_burstactive = FALSE;
1028ef2374f7SNate Lawson 	status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
1029ef2374f7SNate Lawson 	if (ACPI_FAILURE(status))
1030ef2374f7SNate Lawson 	    return (status);
1031ef2374f7SNate Lawson 	CTR0(KTR_ACPI, "ec disabled burst ok");
10321f04e8f5SNate Lawson     }
103315e32d5dSMike Smith 
103415e32d5dSMike Smith     return (AE_OK);
103515e32d5dSMike Smith }
1036