xref: /freebsd/sys/dev/acpica/acpi_ec.c (revision 464c662efa711b28465cc734d0952484907772b3)
115e32d5dSMike Smith /*-
215e32d5dSMike Smith  * Copyright (c) 2000 Michael Smith
315e32d5dSMike Smith  * Copyright (c) 2000 BSDi
415e32d5dSMike Smith  * All rights reserved.
515e32d5dSMike Smith  *
615e32d5dSMike Smith  * Redistribution and use in source and binary forms, with or without
715e32d5dSMike Smith  * modification, are permitted provided that the following conditions
815e32d5dSMike Smith  * are met:
915e32d5dSMike Smith  * 1. Redistributions of source code must retain the above copyright
1015e32d5dSMike Smith  *    notice, this list of conditions and the following disclaimer.
1115e32d5dSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
1215e32d5dSMike Smith  *    notice, this list of conditions and the following disclaimer in the
1315e32d5dSMike Smith  *    documentation and/or other materials provided with the distribution.
1415e32d5dSMike Smith  *
1515e32d5dSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1615e32d5dSMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1715e32d5dSMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1815e32d5dSMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1915e32d5dSMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2015e32d5dSMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2115e32d5dSMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2215e32d5dSMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2315e32d5dSMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2415e32d5dSMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2515e32d5dSMike Smith  * SUCH DAMAGE.
2615e32d5dSMike Smith  *
2715e32d5dSMike Smith  *	$FreeBSD$
2815e32d5dSMike Smith  */
2915e32d5dSMike Smith /******************************************************************************
3015e32d5dSMike Smith  *
3115e32d5dSMike Smith  * 1. Copyright Notice
3215e32d5dSMike Smith  *
3315e32d5dSMike Smith  * Some or all of this work - Copyright (c) 1999, Intel Corp.  All rights
3415e32d5dSMike Smith  * reserved.
3515e32d5dSMike Smith  *
3615e32d5dSMike Smith  * 2. License
3715e32d5dSMike Smith  *
3815e32d5dSMike Smith  * 2.1. This is your license from Intel Corp. under its intellectual property
3915e32d5dSMike Smith  * rights.  You may have additional license terms from the party that provided
4015e32d5dSMike Smith  * you this software, covering your right to use that party's intellectual
4115e32d5dSMike Smith  * property rights.
4215e32d5dSMike Smith  *
4315e32d5dSMike Smith  * 2.2. Intel grants, free of charge, to any person ("Licensee") obtaining a
4415e32d5dSMike Smith  * copy of the source code appearing in this file ("Covered Code") an
4515e32d5dSMike Smith  * irrevocable, perpetual, worldwide license under Intel's copyrights in the
4615e32d5dSMike Smith  * base code distributed originally by Intel ("Original Intel Code") to copy,
4715e32d5dSMike Smith  * make derivatives, distribute, use and display any portion of the Covered
4815e32d5dSMike Smith  * Code in any form, with the right to sublicense such rights; and
4915e32d5dSMike Smith  *
5015e32d5dSMike Smith  * 2.3. Intel grants Licensee a non-exclusive and non-transferable patent
5115e32d5dSMike Smith  * license (with the right to sublicense), under only those claims of Intel
5215e32d5dSMike Smith  * patents that are infringed by the Original Intel Code, to make, use, sell,
5315e32d5dSMike Smith  * offer to sell, and import the Covered Code and derivative works thereof
5415e32d5dSMike Smith  * solely to the minimum extent necessary to exercise the above copyright
5515e32d5dSMike Smith  * license, and in no event shall the patent license extend to any additions
5615e32d5dSMike Smith  * to or modifications of the Original Intel Code.  No other license or right
5715e32d5dSMike Smith  * is granted directly or by implication, estoppel or otherwise;
5815e32d5dSMike Smith  *
5915e32d5dSMike Smith  * The above copyright and patent license is granted only if the following
6015e32d5dSMike Smith  * conditions are met:
6115e32d5dSMike Smith  *
6215e32d5dSMike Smith  * 3. Conditions
6315e32d5dSMike Smith  *
6415e32d5dSMike Smith  * 3.1. Redistribution of Source with Rights to Further Distribute Source.
6515e32d5dSMike Smith  * Redistribution of source code of any substantial portion of the Covered
6615e32d5dSMike Smith  * Code or modification with rights to further distribute source must include
6715e32d5dSMike Smith  * the above Copyright Notice, the above License, this list of Conditions,
6815e32d5dSMike Smith  * and the following Disclaimer and Export Compliance provision.  In addition,
6915e32d5dSMike Smith  * Licensee must cause all Covered Code to which Licensee contributes to
7015e32d5dSMike Smith  * contain a file documenting the changes Licensee made to create that Covered
7115e32d5dSMike Smith  * Code and the date of any change.  Licensee must include in that file the
7215e32d5dSMike Smith  * documentation of any changes made by any predecessor Licensee.  Licensee
7315e32d5dSMike Smith  * must include a prominent statement that the modification is derived,
7415e32d5dSMike Smith  * directly or indirectly, from Original Intel Code.
7515e32d5dSMike Smith  *
7615e32d5dSMike Smith  * 3.2. Redistribution of Source with no Rights to Further Distribute Source.
7715e32d5dSMike Smith  * Redistribution of source code of any substantial portion of the Covered
7815e32d5dSMike Smith  * Code or modification without rights to further distribute source must
7915e32d5dSMike Smith  * include the following Disclaimer and Export Compliance provision in the
8015e32d5dSMike Smith  * documentation and/or other materials provided with distribution.  In
8115e32d5dSMike Smith  * addition, Licensee may not authorize further sublicense of source of any
8215e32d5dSMike Smith  * portion of the Covered Code, and must include terms to the effect that the
8315e32d5dSMike Smith  * license from Licensee to its licensee is limited to the intellectual
8415e32d5dSMike Smith  * property embodied in the software Licensee provides to its licensee, and
8515e32d5dSMike Smith  * not to intellectual property embodied in modifications its licensee may
8615e32d5dSMike Smith  * make.
8715e32d5dSMike Smith  *
8815e32d5dSMike Smith  * 3.3. Redistribution of Executable. Redistribution in executable form of any
8915e32d5dSMike Smith  * substantial portion of the Covered Code or modification must reproduce the
9015e32d5dSMike Smith  * above Copyright Notice, and the following Disclaimer and Export Compliance
9115e32d5dSMike Smith  * provision in the documentation and/or other materials provided with the
9215e32d5dSMike Smith  * distribution.
9315e32d5dSMike Smith  *
9415e32d5dSMike Smith  * 3.4. Intel retains all right, title, and interest in and to the Original
9515e32d5dSMike Smith  * Intel Code.
9615e32d5dSMike Smith  *
9715e32d5dSMike Smith  * 3.5. Neither the name Intel nor any other trademark owned or controlled by
9815e32d5dSMike Smith  * Intel shall be used in advertising or otherwise to promote the sale, use or
9915e32d5dSMike Smith  * other dealings in products derived from or relating to the Covered Code
10015e32d5dSMike Smith  * without prior written authorization from Intel.
10115e32d5dSMike Smith  *
10215e32d5dSMike Smith  * 4. Disclaimer and Export Compliance
10315e32d5dSMike Smith  *
10415e32d5dSMike Smith  * 4.1. INTEL MAKES NO WARRANTY OF ANY KIND REGARDING ANY SOFTWARE PROVIDED
10515e32d5dSMike Smith  * HERE.  ANY SOFTWARE ORIGINATING FROM INTEL OR DERIVED FROM INTEL SOFTWARE
10615e32d5dSMike Smith  * IS PROVIDED "AS IS," AND INTEL WILL NOT PROVIDE ANY SUPPORT,  ASSISTANCE,
10715e32d5dSMike Smith  * INSTALLATION, TRAINING OR OTHER SERVICES.  INTEL WILL NOT PROVIDE ANY
10815e32d5dSMike Smith  * UPDATES, ENHANCEMENTS OR EXTENSIONS.  INTEL SPECIFICALLY DISCLAIMS ANY
10915e32d5dSMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT AND FITNESS FOR A
11015e32d5dSMike Smith  * PARTICULAR PURPOSE.
11115e32d5dSMike Smith  *
11215e32d5dSMike Smith  * 4.2. IN NO EVENT SHALL INTEL HAVE ANY LIABILITY TO LICENSEE, ITS LICENSEES
11315e32d5dSMike Smith  * OR ANY OTHER THIRD PARTY, FOR ANY LOST PROFITS, LOST DATA, LOSS OF USE OR
11415e32d5dSMike Smith  * COSTS OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT,
11515e32d5dSMike Smith  * SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, UNDER ANY
11615e32d5dSMike Smith  * CAUSE OF ACTION OR THEORY OF LIABILITY, AND IRRESPECTIVE OF WHETHER INTEL
11715e32d5dSMike Smith  * HAS ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES.  THESE LIMITATIONS
11815e32d5dSMike Smith  * SHALL APPLY NOTWITHSTANDING THE FAILURE OF THE ESSENTIAL PURPOSE OF ANY
11915e32d5dSMike Smith  * LIMITED REMEDY.
12015e32d5dSMike Smith  *
12115e32d5dSMike Smith  * 4.3. Licensee shall not export, either directly or indirectly, any of this
12215e32d5dSMike Smith  * software or system incorporating such software without first obtaining any
12315e32d5dSMike Smith  * required license or other approval from the U. S. Department of Commerce or
12415e32d5dSMike Smith  * any other agency or department of the United States Government.  In the
12515e32d5dSMike Smith  * event Licensee exports any such software from the United States or
12615e32d5dSMike Smith  * re-exports any such software from a foreign destination, Licensee shall
12715e32d5dSMike Smith  * ensure that the distribution and export/re-export of the software is in
12815e32d5dSMike Smith  * compliance with all laws, regulations, orders, or other restrictions of the
12915e32d5dSMike Smith  * U.S. Export Administration Regulations. Licensee agrees that neither it nor
13015e32d5dSMike Smith  * any of its subsidiaries will export/re-export any technical data, process,
13115e32d5dSMike Smith  * software, or service, directly or indirectly, to any country for which the
13215e32d5dSMike Smith  * United States government or any agency thereof requires an export license,
13315e32d5dSMike Smith  * other governmental approval, or letter of assurance, without first obtaining
13415e32d5dSMike Smith  * such license, approval or letter.
13515e32d5dSMike Smith  *
13615e32d5dSMike Smith  *****************************************************************************/
13715e32d5dSMike Smith 
13815e32d5dSMike Smith #include "opt_acpi.h"
13915e32d5dSMike Smith #include <sys/param.h>
14015e32d5dSMike Smith #include <sys/kernel.h>
14115e32d5dSMike Smith #include <sys/bus.h>
14215e32d5dSMike Smith 
14315e32d5dSMike Smith #include <machine/bus.h>
14415e32d5dSMike Smith #include <machine/resource.h>
14515e32d5dSMike Smith #include <sys/rman.h>
14615e32d5dSMike Smith 
14715e32d5dSMike Smith #include "acpi.h"
14815e32d5dSMike Smith 
14915e32d5dSMike Smith #include <dev/acpica/acpivar.h>
15015e32d5dSMike Smith 
1510ae55423SMike Smith /*
1520ae55423SMike Smith  * Hooks for the ACPI CA debugging infrastructure
1530ae55423SMike Smith  */
154a9cf0dffSMike Smith #define _COMPONENT	ACPI_EC
155dbd0058aSMike Smith ACPI_MODULE_NAME("EC")
1560ae55423SMike Smith 
157da3b867eSMike Smith /*
158da3b867eSMike Smith  * EC_COMMAND:
159da3b867eSMike Smith  * -----------
160da3b867eSMike Smith  */
161da3b867eSMike Smith typedef UINT8				EC_COMMAND;
162da3b867eSMike Smith 
163da3b867eSMike Smith #define EC_COMMAND_UNKNOWN		((EC_COMMAND) 0x00)
164da3b867eSMike Smith #define EC_COMMAND_READ			((EC_COMMAND) 0x80)
165da3b867eSMike Smith #define EC_COMMAND_WRITE		((EC_COMMAND) 0x81)
166da3b867eSMike Smith #define EC_COMMAND_BURST_ENABLE		((EC_COMMAND) 0x82)
167da3b867eSMike Smith #define EC_COMMAND_BURST_DISABLE	((EC_COMMAND) 0x83)
168da3b867eSMike Smith #define EC_COMMAND_QUERY		((EC_COMMAND) 0x84)
169da3b867eSMike Smith 
170da3b867eSMike Smith /*
171da3b867eSMike Smith  * EC_STATUS:
172da3b867eSMike Smith  * ----------
173da3b867eSMike Smith  * The encoding of the EC status register is illustrated below.
174da3b867eSMike Smith  * Note that a set bit (1) indicates the property is TRUE
175da3b867eSMike Smith  * (e.g. if bit 0 is set then the output buffer is full).
176da3b867eSMike Smith  * +-+-+-+-+-+-+-+-+
177da3b867eSMike Smith  * |7|6|5|4|3|2|1|0|
178da3b867eSMike Smith  * +-+-+-+-+-+-+-+-+
179da3b867eSMike Smith  *  | | | | | | | |
180da3b867eSMike Smith  *  | | | | | | | +- Output Buffer Full?
181da3b867eSMike Smith  *  | | | | | | +--- Input Buffer Full?
182da3b867eSMike Smith  *  | | | | | +----- <reserved>
183da3b867eSMike Smith  *  | | | | +------- Data Register is Command Byte?
184da3b867eSMike Smith  *  | | | +--------- Burst Mode Enabled?
185da3b867eSMike Smith  *  | | +----------- SCI Event?
186da3b867eSMike Smith  *  | +------------- SMI Event?
187da3b867eSMike Smith  *  +--------------- <Reserved>
188da3b867eSMike Smith  *
189da3b867eSMike Smith  */
190da3b867eSMike Smith typedef UINT8				EC_STATUS;
191da3b867eSMike Smith 
192da3b867eSMike Smith #define EC_FLAG_OUTPUT_BUFFER		((EC_STATUS) 0x01)
193da3b867eSMike Smith #define EC_FLAG_INPUT_BUFFER		((EC_STATUS) 0x02)
194da3b867eSMike Smith #define EC_FLAG_BURST_MODE		((EC_STATUS) 0x10)
195da3b867eSMike Smith #define EC_FLAG_SCI			((EC_STATUS) 0x20)
196da3b867eSMike Smith 
197da3b867eSMike Smith /*
198da3b867eSMike Smith  * EC_EVENT:
199da3b867eSMike Smith  * ---------
200da3b867eSMike Smith  */
201da3b867eSMike Smith typedef UINT8				EC_EVENT;
202da3b867eSMike Smith 
203da3b867eSMike Smith #define EC_EVENT_UNKNOWN		((EC_EVENT) 0x00)
204da3b867eSMike Smith #define EC_EVENT_OUTPUT_BUFFER_FULL	((EC_EVENT) 0x01)
205da3b867eSMike Smith #define EC_EVENT_INPUT_BUFFER_EMPTY	((EC_EVENT) 0x02)
206da3b867eSMike Smith #define EC_EVENT_SCI			((EC_EVENT) 0x20)
207da3b867eSMike Smith 
208da3b867eSMike Smith /*
209da3b867eSMike Smith  * Register access primitives
210da3b867eSMike Smith  */
211da3b867eSMike Smith #define EC_GET_DATA(sc)							\
212da3b867eSMike Smith 	bus_space_read_1((sc)->ec_data_tag, (sc)->ec_data_handle, 0)
213da3b867eSMike Smith 
214da3b867eSMike Smith #define EC_SET_DATA(sc, v)						\
215da3b867eSMike Smith 	bus_space_write_1((sc)->ec_data_tag, (sc)->ec_data_handle, 0, (v))
216da3b867eSMike Smith 
217da3b867eSMike Smith #define EC_GET_CSR(sc)							\
218da3b867eSMike Smith 	bus_space_read_1((sc)->ec_csr_tag, (sc)->ec_csr_handle, 0)
219da3b867eSMike Smith 
220da3b867eSMike Smith #define EC_SET_CSR(sc, v)						\
221da3b867eSMike Smith 	bus_space_write_1((sc)->ec_csr_tag, (sc)->ec_csr_handle, 0, (v))
222da3b867eSMike Smith 
223da3b867eSMike Smith /*
224da3b867eSMike Smith  * Driver softc.
225da3b867eSMike Smith  */
22615e32d5dSMike Smith struct acpi_ec_softc {
22715e32d5dSMike Smith     device_t		ec_dev;
22815e32d5dSMike Smith     ACPI_HANDLE		ec_handle;
22915e32d5dSMike Smith     UINT32		ec_gpebit;
23015e32d5dSMike Smith 
23115e32d5dSMike Smith     int			ec_data_rid;
23215e32d5dSMike Smith     struct resource	*ec_data_res;
23315e32d5dSMike Smith     bus_space_tag_t	ec_data_tag;
23415e32d5dSMike Smith     bus_space_handle_t	ec_data_handle;
23515e32d5dSMike Smith 
23615e32d5dSMike Smith     int			ec_csr_rid;
23715e32d5dSMike Smith     struct resource	*ec_csr_res;
23815e32d5dSMike Smith     bus_space_tag_t	ec_csr_tag;
23915e32d5dSMike Smith     bus_space_handle_t	ec_csr_handle;
24015e32d5dSMike Smith 
24115e32d5dSMike Smith     int			ec_locked;
242dbd0058aSMike Smith     int			ec_lockhandle;
243c07572e7STakanori Watanabe     int			ec_pendquery;
244c07572e7STakanori Watanabe     int			ec_csrvalue;
24515e32d5dSMike Smith };
24615e32d5dSMike Smith 
2474690674eSMitsuru IWASAKI static int		acpi_ec_event_driven = 0;
2484690674eSMitsuru IWASAKI TUNABLE_INT("hw.acpi.ec.event_driven", &acpi_ec_event_driven);
2494690674eSMitsuru IWASAKI 
25015e32d5dSMike Smith #define EC_LOCK_TIMEOUT	1000	/* 1ms */
25115e32d5dSMike Smith 
25215e32d5dSMike Smith static __inline ACPI_STATUS
25315e32d5dSMike Smith EcLock(struct acpi_ec_softc *sc)
25415e32d5dSMike Smith {
25515e32d5dSMike Smith     ACPI_STATUS	status;
25615e32d5dSMike Smith 
25721b5fd3cSMitsuru IWASAKI     /* XXX ACPI_WAIT_FOREVER is probably a bad idea, what is a better time? */
258464c662eSNate Lawson     status = AcpiAcquireGlobalLock(ACPI_WAIT_FOREVER, &sc->ec_lockhandle);
259464c662eSNate Lawson     if (ACPI_SUCCESS(status))
260464c662eSNate Lawson 	sc->ec_locked = 1;
261c573e654SMitsuru IWASAKI 
26215e32d5dSMike Smith     return (status);
26315e32d5dSMike Smith }
26415e32d5dSMike Smith 
26515e32d5dSMike Smith static __inline void
26615e32d5dSMike Smith EcUnlock(struct acpi_ec_softc *sc)
26715e32d5dSMike Smith {
268464c662eSNate Lawson     sc->ec_locked = 0;
269dbd0058aSMike Smith     AcpiReleaseGlobalLock(sc->ec_lockhandle);
27015e32d5dSMike Smith }
27115e32d5dSMike Smith 
27215e32d5dSMike Smith static __inline int
27315e32d5dSMike Smith EcIsLocked(struct acpi_ec_softc *sc)
27415e32d5dSMike Smith {
275464c662eSNate Lawson     return (sc->ec_locked != 0);
27615e32d5dSMike Smith }
27715e32d5dSMike Smith 
27815e32d5dSMike Smith typedef struct
27915e32d5dSMike Smith {
28015e32d5dSMike Smith     EC_COMMAND		Command;
28115e32d5dSMike Smith     UINT8		Address;
28215e32d5dSMike Smith     UINT8		Data;
28315e32d5dSMike Smith } EC_REQUEST;
28415e32d5dSMike Smith 
28515e32d5dSMike Smith static void		EcGpeHandler(void *Context);
28615e32d5dSMike Smith static ACPI_STATUS	EcSpaceSetup(ACPI_HANDLE Region, UINT32 Function,
28715e32d5dSMike Smith 				void *Context, void **return_Context);
288464c662eSNate Lawson static ACPI_STATUS	EcSpaceHandler(UINT32 Function,
289464c662eSNate Lawson 				ACPI_PHYSICAL_ADDRESS Address,
290464c662eSNate Lawson 				UINT32 width, ACPI_INTEGER *Value,
29115e32d5dSMike Smith 				void *Context, void *RegionContext);
29215e32d5dSMike Smith static ACPI_STATUS	EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event);
29315e32d5dSMike Smith static ACPI_STATUS	EcQuery(struct acpi_ec_softc *sc, UINT8 *Data);
294464c662eSNate Lawson static ACPI_STATUS	EcTransaction(struct acpi_ec_softc *sc,
295464c662eSNate Lawson 				EC_REQUEST *EcRequest);
296464c662eSNate Lawson static ACPI_STATUS	EcRead(struct acpi_ec_softc *sc, UINT8 Address,
297464c662eSNate Lawson 				UINT8 *Data);
298464c662eSNate Lawson static ACPI_STATUS	EcWrite(struct acpi_ec_softc *sc, UINT8 Address,
299464c662eSNate Lawson 				UINT8 *Data);
30015e32d5dSMike Smith static void		acpi_ec_identify(driver_t driver, device_t bus);
30115e32d5dSMike Smith static int		acpi_ec_probe(device_t dev);
30215e32d5dSMike Smith static int		acpi_ec_attach(device_t dev);
30315e32d5dSMike Smith 
30415e32d5dSMike Smith static device_method_t acpi_ec_methods[] = {
30515e32d5dSMike Smith     /* Device interface */
30615e32d5dSMike Smith     DEVMETHOD(device_identify,	acpi_ec_identify),
30715e32d5dSMike Smith     DEVMETHOD(device_probe,	acpi_ec_probe),
30815e32d5dSMike Smith     DEVMETHOD(device_attach,	acpi_ec_attach),
30915e32d5dSMike Smith 
31015e32d5dSMike Smith     {0, 0}
31115e32d5dSMike Smith };
31215e32d5dSMike Smith 
31315e32d5dSMike Smith static driver_t acpi_ec_driver = {
31415e32d5dSMike Smith     "acpi_ec",
31515e32d5dSMike Smith     acpi_ec_methods,
31615e32d5dSMike Smith     sizeof(struct acpi_ec_softc),
31715e32d5dSMike Smith };
31815e32d5dSMike Smith 
3193273b005SMike Smith static devclass_t acpi_ec_devclass;
32015e32d5dSMike Smith DRIVER_MODULE(acpi_ec, acpi, acpi_ec_driver, acpi_ec_devclass, 0, 0);
32115e32d5dSMike Smith 
32215e32d5dSMike Smith /*
32315e32d5dSMike Smith  * Look for an ECDT table and if we find one, set up a default EC
32415e32d5dSMike Smith  * space handler to catch possible attempts to access EC space before
32515e32d5dSMike Smith  * we have a real driver instance in place.
32615e32d5dSMike Smith  * We're not really an identify routine, but because we get called
32715e32d5dSMike Smith  * before most other things, this works out OK.
32815e32d5dSMike Smith  */
32915e32d5dSMike Smith static void
33015e32d5dSMike Smith acpi_ec_identify(driver_t driver, device_t bus)
33115e32d5dSMike Smith {
332b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
33315e32d5dSMike Smith 
33415e32d5dSMike Smith     /* XXX implement - need an ACPI 2.0 system to test this */
33515e32d5dSMike Smith }
33615e32d5dSMike Smith 
33715e32d5dSMike Smith /*
33815e32d5dSMike Smith  * We could setup resources in the probe routine in order to have them printed
33915e32d5dSMike Smith  * when the device is attached.
34015e32d5dSMike Smith  */
34115e32d5dSMike Smith static int
34215e32d5dSMike Smith acpi_ec_probe(device_t dev)
34315e32d5dSMike Smith {
3440ae55423SMike Smith 
345464c662eSNate Lawson     if (acpi_get_type(dev) == ACPI_TYPE_DEVICE && !acpi_disabled("ec") &&
34615e32d5dSMike Smith 	acpi_MatchHid(dev, "PNP0C09")) {
34715e32d5dSMike Smith 
34815e32d5dSMike Smith 	/*
34915e32d5dSMike Smith 	 * Set device description
35015e32d5dSMike Smith 	 */
35115e32d5dSMike Smith 	device_set_desc(dev, "embedded controller");
35215e32d5dSMike Smith 	return (0);
35315e32d5dSMike Smith     }
35415e32d5dSMike Smith     return (ENXIO);
35515e32d5dSMike Smith }
35615e32d5dSMike Smith 
35715e32d5dSMike Smith static int
35815e32d5dSMike Smith acpi_ec_attach(device_t dev)
35915e32d5dSMike Smith {
36015e32d5dSMike Smith     struct acpi_ec_softc	*sc;
36115e32d5dSMike Smith     ACPI_STATUS			Status;
362f8372adeSTakanori Watanabe     int				errval = 0;
363dbd0058aSMike Smith 
364b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
3650ae55423SMike Smith 
36615e32d5dSMike Smith     /*
36715e32d5dSMike Smith      * Fetch/initialise softc
36815e32d5dSMike Smith      */
36915e32d5dSMike Smith     sc = device_get_softc(dev);
370c07572e7STakanori Watanabe     bzero(sc, sizeof(*sc));
37115e32d5dSMike Smith     sc->ec_dev = dev;
37215e32d5dSMike Smith     sc->ec_handle = acpi_get_handle(dev);
37315e32d5dSMike Smith 
37415e32d5dSMike Smith     /*
37515e32d5dSMike Smith      * Attach bus resources
37615e32d5dSMike Smith      */
37715e32d5dSMike Smith     sc->ec_data_rid = 0;
378464c662eSNate Lawson     sc->ec_data_res = bus_alloc_resource(sc->ec_dev, SYS_RES_IOPORT,
379464c662eSNate Lawson 			&sc->ec_data_rid, 0, ~0, 1, RF_ACTIVE);
380464c662eSNate Lawson     if (sc->ec_data_res == NULL) {
38115e32d5dSMike Smith 	device_printf(dev, "can't allocate data port\n");
382f8372adeSTakanori Watanabe 	errval = ENXIO;
383f8372adeSTakanori Watanabe 	goto out;
38415e32d5dSMike Smith     }
38515e32d5dSMike Smith     sc->ec_data_tag = rman_get_bustag(sc->ec_data_res);
38615e32d5dSMike Smith     sc->ec_data_handle = rman_get_bushandle(sc->ec_data_res);
38715e32d5dSMike Smith 
38815e32d5dSMike Smith     sc->ec_csr_rid = 1;
389464c662eSNate Lawson     sc->ec_csr_res = bus_alloc_resource(sc->ec_dev, SYS_RES_IOPORT,
390464c662eSNate Lawson 			&sc->ec_csr_rid, 0, ~0, 1, RF_ACTIVE);
391464c662eSNate Lawson     if (sc->ec_csr_res == NULL) {
39215e32d5dSMike Smith 	device_printf(dev, "can't allocate command/status port\n");
393f8372adeSTakanori Watanabe 	errval = ENXIO;
394f8372adeSTakanori Watanabe 	goto out;
39515e32d5dSMike Smith     }
39615e32d5dSMike Smith     sc->ec_csr_tag = rman_get_bustag(sc->ec_csr_res);
39715e32d5dSMike Smith     sc->ec_csr_handle = rman_get_bushandle(sc->ec_csr_res);
39815e32d5dSMike Smith 
39915e32d5dSMike Smith     /*
40015e32d5dSMike Smith      * Install GPE handler
40115e32d5dSMike Smith      *
40215e32d5dSMike Smith      * Evaluate the _GPE method to find the GPE bit used by the EC to signal
40315e32d5dSMike Smith      * status (SCI).
40415e32d5dSMike Smith      */
4054c1cdee6SMike Smith     ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "attaching GPE\n"));
406464c662eSNate Lawson     Status = acpi_EvaluateInteger(sc->ec_handle, "_GPE", &sc->ec_gpebit);
407464c662eSNate Lawson     if (ACPI_FAILURE(Status)) {
408464c662eSNate Lawson 	device_printf(dev, "can't evaluate _GPE - %s\n",
409464c662eSNate Lawson 		      AcpiFormatException(Status));
410f8372adeSTakanori Watanabe 	errval = ENXIO;
411f8372adeSTakanori Watanabe 	goto out;
41215e32d5dSMike Smith     }
41315e32d5dSMike Smith 
41415e32d5dSMike Smith     /*
41515e32d5dSMike Smith      * Install a handler for this EC's GPE bit.  Note that EC SCIs are
41615e32d5dSMike Smith      * treated as both edge- and level-triggered interrupts; in other words
41715e32d5dSMike Smith      * we clear the status bit immediately after getting an EC-SCI, then
41815e32d5dSMike Smith      * again after we're done processing the event.  This guarantees that
41915e32d5dSMike Smith      * events we cause while performing a transaction (e.g. IBE/OBF) get
42015e32d5dSMike Smith      * cleared before re-enabling the GPE.
42115e32d5dSMike Smith      */
422464c662eSNate Lawson     Status = AcpiInstallGpeHandler(sc->ec_gpebit,
423464c662eSNate Lawson 		ACPI_EVENT_LEVEL_TRIGGERED | ACPI_EVENT_EDGE_TRIGGERED,
424464c662eSNate Lawson 		EcGpeHandler, sc);
425464c662eSNate Lawson     if (ACPI_FAILURE(Status)) {
42676d1dff4SMike Smith 	device_printf(dev, "can't install GPE handler for %s - %s\n",
427bfae45aaSMike Smith 		      acpi_name(sc->ec_handle), AcpiFormatException(Status));
428f8372adeSTakanori Watanabe 	errval = ENXIO;
429f8372adeSTakanori Watanabe 	goto out;
43015e32d5dSMike Smith     }
43115e32d5dSMike Smith 
43215e32d5dSMike Smith     /*
43315e32d5dSMike Smith      * Install address space handler
43415e32d5dSMike Smith      */
4354c1cdee6SMike Smith     ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "attaching address space handler\n"));
436464c662eSNate Lawson     Status = AcpiInstallAddressSpaceHandler(sc->ec_handle, ACPI_ADR_SPACE_EC,
437464c662eSNate Lawson 		EcSpaceHandler, EcSpaceSetup, sc);
438464c662eSNate Lawson     if (ACPI_FAILURE(Status)) {
43976d1dff4SMike Smith 	device_printf(dev, "can't install address space handler for %s - %s\n",
440bfae45aaSMike Smith 		      acpi_name(sc->ec_handle), AcpiFormatException(Status));
44176d1dff4SMike Smith 	panic("very suck");
442f8372adeSTakanori Watanabe 	errval = ENXIO;
443f8372adeSTakanori Watanabe 	goto out;
44415e32d5dSMike Smith     }
4454c1cdee6SMike Smith     ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "attach complete\n"));
4460ae55423SMike Smith     return_VALUE (0);
447464c662eSNate Lawson 
448f8372adeSTakanori Watanabe  out:
449f8372adeSTakanori Watanabe     if (sc->ec_csr_res)
450f8372adeSTakanori Watanabe 	bus_release_resource(sc->ec_dev, SYS_RES_IOPORT, sc->ec_csr_rid,
451f8372adeSTakanori Watanabe 			     sc->ec_csr_res);
452f8372adeSTakanori Watanabe     if (sc->ec_data_res)
453f8372adeSTakanori Watanabe         bus_release_resource(sc->ec_dev, SYS_RES_IOPORT, sc->ec_data_rid,
454f8372adeSTakanori Watanabe 			     sc->ec_data_res);
455f8372adeSTakanori Watanabe     return_VALUE (errval);
45615e32d5dSMike Smith }
45715e32d5dSMike Smith 
45815e32d5dSMike Smith static void
459c07572e7STakanori Watanabe EcGpeQueryHandler(void *Context)
46015e32d5dSMike Smith {
46115e32d5dSMike Smith     struct acpi_ec_softc	*sc = (struct acpi_ec_softc *)Context;
46215e32d5dSMike Smith     UINT8			Data;
46315e32d5dSMike Smith     ACPI_STATUS			Status;
46415e32d5dSMike Smith     char			qxx[5];
46515e32d5dSMike Smith 
466b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
4670ae55423SMike Smith 
46815e32d5dSMike Smith     for (;;) {
46915e32d5dSMike Smith 
47015e32d5dSMike Smith 	/*
47115e32d5dSMike Smith 	 * Check EC_SCI.
47215e32d5dSMike Smith 	 *
47315e32d5dSMike Smith 	 * Bail out if the EC_SCI bit of the status register is not set.
47415e32d5dSMike Smith 	 * Note that this function should only be called when
47515e32d5dSMike Smith 	 * this bit is set (polling is used to detect IBE/OBF events).
47615e32d5dSMike Smith 	 *
47715e32d5dSMike Smith 	 * It is safe to do this without locking the controller, as it's
47815e32d5dSMike Smith 	 * OK to call EcQuery when there's no data ready; in the worst
47915e32d5dSMike Smith 	 * case we should just find nothing waiting for us and bail.
48015e32d5dSMike Smith 	 */
481464c662eSNate Lawson 	if ((EC_GET_CSR(sc) & EC_EVENT_SCI) == 0)
48215e32d5dSMike Smith 	    break;
48315e32d5dSMike Smith 
48415e32d5dSMike Smith 	/*
48515e32d5dSMike Smith 	 * Find out why the EC is signalling us
48615e32d5dSMike Smith 	 */
48715e32d5dSMike Smith 	Status = EcQuery(sc, &Data);
48815e32d5dSMike Smith 
48915e32d5dSMike Smith 	/*
49015e32d5dSMike Smith 	 * If we failed to get anything from the EC, give up
49115e32d5dSMike Smith 	 */
492dbd0058aSMike Smith 	if (ACPI_FAILURE(Status)) {
4936971b3c7SMitsuru IWASAKI 	    ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
4946971b3c7SMitsuru IWASAKI 		"GPE query failed - %s\n", AcpiFormatException(Status));
49515e32d5dSMike Smith 	    break;
49615e32d5dSMike Smith 	}
49715e32d5dSMike Smith 
49815e32d5dSMike Smith 	/*
49915e32d5dSMike Smith 	 * Evaluate _Qxx to respond to the controller.
50015e32d5dSMike Smith 	 */
50115e32d5dSMike Smith 	sprintf(qxx, "_Q%02x", Data);
50215e32d5dSMike Smith 	strupr(qxx);
503ee785aa9SJohn Baldwin 	Status = AcpiEvaluateObject(sc->ec_handle, qxx, NULL, NULL);
504ee785aa9SJohn Baldwin 	/*
505ee785aa9SJohn Baldwin 	 * Ignore spurious query requests.
506ee785aa9SJohn Baldwin 	 */
507dbd0058aSMike Smith 	if (ACPI_FAILURE(Status) && (Data != 0 || Status != AE_NOT_FOUND)) {
5086971b3c7SMitsuru IWASAKI 	    ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
5096971b3c7SMitsuru IWASAKI 	    	"evaluation of GPE query method %s failed - %s\n",
510bfae45aaSMike Smith 		qxx, AcpiFormatException(Status));
51115e32d5dSMike Smith 	}
51215e32d5dSMike Smith     }
513c07572e7STakanori Watanabe         /* I know I request Level trigger cleanup */
514dbd0058aSMike Smith     if (ACPI_FAILURE(AcpiClearEvent(sc->ec_gpebit, ACPI_EVENT_GPE)))
515c07572e7STakanori Watanabe 	printf("EcGpeQueryHandler:ClearEvent Failed\n");
516dbd0058aSMike Smith     if (ACPI_FAILURE(AcpiEnableEvent(sc->ec_gpebit, ACPI_EVENT_GPE, 0)))
517c07572e7STakanori Watanabe 	printf("EcGpeQueryHandler:EnableEvent Failed\n");
518c07572e7STakanori Watanabe }
51942f6d122SMike Smith 
520a9cf0dffSMike Smith /*
521a9cf0dffSMike Smith  * Handle a GPE sent to us.
522a9cf0dffSMike Smith  */
523a9cf0dffSMike Smith static void
524a9cf0dffSMike Smith EcGpeHandler(void *Context)
525c07572e7STakanori Watanabe {
526c07572e7STakanori Watanabe     struct acpi_ec_softc *sc = Context;
527c07572e7STakanori Watanabe     int csrvalue;
528a9cf0dffSMike Smith 
529c07572e7STakanori Watanabe     /*
530c07572e7STakanori Watanabe      * If EC is locked, the intr must process EcRead/Write wait only.
531c07572e7STakanori Watanabe      * Query request must be pending.
532c07572e7STakanori Watanabe      */
533c07572e7STakanori Watanabe     if (EcIsLocked(sc)) {
534c07572e7STakanori Watanabe 	csrvalue = EC_GET_CSR(sc);
535c07572e7STakanori Watanabe 	if (csrvalue & EC_EVENT_SCI)
536c07572e7STakanori Watanabe 	    sc->ec_pendquery = 1;
537464c662eSNate Lawson 	if ((csrvalue & EC_FLAG_OUTPUT_BUFFER) != 0 ||
538464c662eSNate Lawson 	    (csrvalue & EC_FLAG_INPUT_BUFFER) == 0) {
539c07572e7STakanori Watanabe 	    sc->ec_csrvalue = csrvalue;
540464c662eSNate Lawson 	    wakeup(&sc->ec_csrvalue);
541c07572e7STakanori Watanabe 	}
542c07572e7STakanori Watanabe     } else {
543c07572e7STakanori Watanabe 	/* Queue GpeQuery Handler */
544dbd0058aSMike Smith 	if (ACPI_FAILURE(AcpiOsQueueForExecution(OSD_PRIORITY_HIGH,
545dbd0058aSMike Smith 				    EcGpeQueryHandler,Context))) {
546c07572e7STakanori Watanabe 	    printf("QueryHandler Queuing Failed\n");
547c07572e7STakanori Watanabe 	}
548c07572e7STakanori Watanabe     }
54915e32d5dSMike Smith }
55015e32d5dSMike Smith 
55115e32d5dSMike Smith static ACPI_STATUS
552464c662eSNate Lawson EcSpaceSetup(ACPI_HANDLE Region, UINT32 Function, void *Context,
553464c662eSNate Lawson 	     void **RegionContext)
55415e32d5dSMike Smith {
55542f6d122SMike Smith 
556b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
55742f6d122SMike Smith 
55815e32d5dSMike Smith     /*
55915e32d5dSMike Smith      * Just pass the context through, there's nothing to do here.
56015e32d5dSMike Smith      */
56115e32d5dSMike Smith     *RegionContext = Context;
56215e32d5dSMike Smith 
56342f6d122SMike Smith     return_ACPI_STATUS (AE_OK);
56415e32d5dSMike Smith }
56515e32d5dSMike Smith 
56615e32d5dSMike Smith static ACPI_STATUS
567464c662eSNate Lawson EcSpaceHandler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address, UINT32 width,
568464c662eSNate Lawson 	       ACPI_INTEGER *Value, void *Context, void *RegionContext)
56915e32d5dSMike Smith {
57015e32d5dSMike Smith     struct acpi_ec_softc	*sc = (struct acpi_ec_softc *)Context;
57115e32d5dSMike Smith     ACPI_STATUS			Status = AE_OK;
57215e32d5dSMike Smith     EC_REQUEST			EcRequest;
573ee785aa9SJohn Baldwin     int				i;
57415e32d5dSMike Smith 
575b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)Address);
5760ae55423SMike Smith 
577464c662eSNate Lawson     if (Address > 0xFF || width % 8 != 0 || Value == NULL || Context == NULL)
5780ae55423SMike Smith         return_ACPI_STATUS (AE_BAD_PARAMETER);
57915e32d5dSMike Smith 
58015e32d5dSMike Smith     switch (Function) {
58176f2b644SMike Smith     case ACPI_READ:
58215e32d5dSMike Smith         EcRequest.Command = EC_COMMAND_READ;
58315e32d5dSMike Smith         EcRequest.Address = Address;
5842a4ac806SMike Smith 	(*Value) = 0;
58515e32d5dSMike Smith         break;
58676f2b644SMike Smith     case ACPI_WRITE:
58715e32d5dSMike Smith         EcRequest.Command = EC_COMMAND_WRITE;
58815e32d5dSMike Smith         EcRequest.Address = Address;
58915e32d5dSMike Smith         break;
59015e32d5dSMike Smith     default:
591464c662eSNate Lawson 	device_printf(sc->ec_dev, "invalid Address Space function %d\n",
592464c662eSNate Lawson 		      Function);
5930ae55423SMike Smith         return_ACPI_STATUS (AE_BAD_PARAMETER);
59415e32d5dSMike Smith     }
59515e32d5dSMike Smith 
59615e32d5dSMike Smith     /*
59715e32d5dSMike Smith      * Perform the transaction.
59815e32d5dSMike Smith      */
599ee785aa9SJohn Baldwin     for (i = 0; i < width; i += 8) {
60076f2b644SMike Smith 	if (Function == ACPI_READ)
601ee785aa9SJohn Baldwin 	    EcRequest.Data = 0;
602ee785aa9SJohn Baldwin 	else
603ee785aa9SJohn Baldwin 	    EcRequest.Data = (UINT8)((*Value) >> i);
604464c662eSNate Lawson 	Status = EcTransaction(sc, &EcRequest);
605464c662eSNate Lawson 	if (ACPI_FAILURE(Status))
606ee785aa9SJohn Baldwin 	    break;
607464c662eSNate Lawson         *Value |= (ACPI_INTEGER)EcRequest.Data << i;
608ee785aa9SJohn Baldwin 	if (++EcRequest.Address == 0)
609ee785aa9SJohn Baldwin             return_ACPI_STATUS (AE_BAD_PARAMETER);
610ee785aa9SJohn Baldwin     }
6110ae55423SMike Smith     return_ACPI_STATUS (Status);
61215e32d5dSMike Smith }
6132a4ac806SMike Smith 
614a9cf0dffSMike Smith /*
615a9cf0dffSMike Smith  * Wait for an event interrupt for a specific condition.
616a9cf0dffSMike Smith  */
617c07572e7STakanori Watanabe static ACPI_STATUS
618c07572e7STakanori Watanabe EcWaitEventIntr(struct acpi_ec_softc *sc, EC_EVENT Event)
619c07572e7STakanori Watanabe {
620c07572e7STakanori Watanabe     EC_STATUS	EcStatus;
621c07572e7STakanori Watanabe     int		i;
622a9cf0dffSMike Smith 
623b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)Event);
624a9cf0dffSMike Smith 
625a9cf0dffSMike Smith     /* XXX this should test whether interrupts are available some other way */
6264690674eSMitsuru IWASAKI     if (cold || acpi_ec_event_driven)
627a9cf0dffSMike Smith 	return_ACPI_STATUS (EcWaitEvent(sc, Event));
628a9cf0dffSMike Smith 
629464c662eSNate Lawson     if (!EcIsLocked(sc)) {
6306971b3c7SMitsuru IWASAKI 	ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
6316971b3c7SMitsuru IWASAKI 	    "EcWaitEventIntr called without EC lock!\n");
632464c662eSNate Lawson     }
633a9cf0dffSMike Smith 
634c07572e7STakanori Watanabe     EcStatus = EC_GET_CSR(sc);
635a9cf0dffSMike Smith 
636a9cf0dffSMike Smith     /* XXX waiting too long? */
637c07572e7STakanori Watanabe     for (i = 0; i < 10; i++) {
638a9cf0dffSMike Smith 	/*
639a9cf0dffSMike Smith 	 * Check EC status against the desired event.
640a9cf0dffSMike Smith 	 */
641c07572e7STakanori Watanabe     	if ((Event == EC_EVENT_OUTPUT_BUFFER_FULL) &&
642464c662eSNate Lawson 	    (EcStatus & EC_FLAG_OUTPUT_BUFFER) != 0)
643a9cf0dffSMike Smith 	    return_ACPI_STATUS (AE_OK);
644c07572e7STakanori Watanabe 
645c07572e7STakanori Watanabe 	if ((Event == EC_EVENT_INPUT_BUFFER_EMPTY) &&
646464c662eSNate Lawson 	    (EcStatus & EC_FLAG_INPUT_BUFFER) == 0)
647a9cf0dffSMike Smith 	    return_ACPI_STATUS (AE_OK);
648a9cf0dffSMike Smith 
649c07572e7STakanori Watanabe 	sc->ec_csrvalue = 0;
650464c662eSNate Lawson 	/* XXX sleeping with Acpi Global Lock held */
651464c662eSNate Lawson 	if (tsleep(&sc->ec_csrvalue, PZERO, "EcWait", 1) != EWOULDBLOCK) {
652c07572e7STakanori Watanabe 	    EcStatus = sc->ec_csrvalue;
653c07572e7STakanori Watanabe 	} else {
654c07572e7STakanori Watanabe 	    EcStatus = EC_GET_CSR(sc);
655c07572e7STakanori Watanabe 	}
656c07572e7STakanori Watanabe     }
657a9cf0dffSMike Smith     return_ACPI_STATUS (AE_ERROR);
658c07572e7STakanori Watanabe }
65915e32d5dSMike Smith 
66015e32d5dSMike Smith static ACPI_STATUS
66115e32d5dSMike Smith EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event)
66215e32d5dSMike Smith {
66315e32d5dSMike Smith     EC_STATUS	EcStatus;
66415e32d5dSMike Smith     UINT32	i = 0;
66515e32d5dSMike Smith 
666464c662eSNate Lawson     if (!EcIsLocked(sc)) {
6676971b3c7SMitsuru IWASAKI 	ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
6686971b3c7SMitsuru IWASAKI 	    "EcWaitEvent called without EC lock!\n");
669464c662eSNate Lawson     }
67015e32d5dSMike Smith 
67115e32d5dSMike Smith     /*
67215e32d5dSMike Smith      * Stall 1us:
67315e32d5dSMike Smith      * ----------
67415e32d5dSMike Smith      * Stall for 1 microsecond before reading the status register
67515e32d5dSMike Smith      * for the first time.  This allows the EC to set the IBF/OBF
67615e32d5dSMike Smith      * bit to its proper state.
67715e32d5dSMike Smith      *
67815e32d5dSMike Smith      * XXX it is not clear why we read the CSR twice.
67915e32d5dSMike Smith      */
680da3b867eSMike Smith     AcpiOsStall(1);
68115e32d5dSMike Smith     EcStatus = EC_GET_CSR(sc);
68215e32d5dSMike Smith 
68315e32d5dSMike Smith     /*
68415e32d5dSMike Smith      * Wait For Event:
68515e32d5dSMike Smith      * ---------------
68615e32d5dSMike Smith      * Poll the EC status register to detect completion of the last
6872a4ac806SMike Smith      * command.  Wait up to 10ms (in 10us chunks) for this to occur.
68815e32d5dSMike Smith      */
6892a4ac806SMike Smith     for (i = 0; i < 1000; i++) {
69015e32d5dSMike Smith 	EcStatus = EC_GET_CSR(sc);
69115e32d5dSMike Smith 
692464c662eSNate Lawson         if (Event == EC_EVENT_OUTPUT_BUFFER_FULL &&
693464c662eSNate Lawson             (EcStatus & EC_FLAG_OUTPUT_BUFFER) != 0)
69415e32d5dSMike Smith 	    return (AE_OK);
69515e32d5dSMike Smith 
696464c662eSNate Lawson 	if (Event == EC_EVENT_INPUT_BUFFER_EMPTY &&
697464c662eSNate Lawson             (EcStatus & EC_FLAG_INPUT_BUFFER) == 0)
69815e32d5dSMike Smith 	    return(AE_OK);
69915e32d5dSMike Smith 
700da3b867eSMike Smith 	AcpiOsStall(10);
70115e32d5dSMike Smith     }
70215e32d5dSMike Smith 
70315e32d5dSMike Smith     return (AE_ERROR);
70415e32d5dSMike Smith }
70515e32d5dSMike Smith 
70615e32d5dSMike Smith static ACPI_STATUS
70715e32d5dSMike Smith EcQuery(struct acpi_ec_softc *sc, UINT8 *Data)
70815e32d5dSMike Smith {
70915e32d5dSMike Smith     ACPI_STATUS	Status;
71015e32d5dSMike Smith 
711464c662eSNate Lawson     Status = EcLock(sc);
712464c662eSNate Lawson     if (ACPI_FAILURE(Status))
71315e32d5dSMike Smith 	return (Status);
71415e32d5dSMike Smith 
71515e32d5dSMike Smith     EC_SET_CSR(sc, EC_COMMAND_QUERY);
716464c662eSNate Lawson     Status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL);
717464c662eSNate Lawson     if (ACPI_SUCCESS(Status))
71815e32d5dSMike Smith 	*Data = EC_GET_DATA(sc);
71915e32d5dSMike Smith 
72015e32d5dSMike Smith     EcUnlock(sc);
72115e32d5dSMike Smith 
722464c662eSNate Lawson     if (ACPI_FAILURE(Status)) {
7236971b3c7SMitsuru IWASAKI 	ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
7246971b3c7SMitsuru IWASAKI 	    "timeout waiting for EC to respond to EC_COMMAND_QUERY\n");
725464c662eSNate Lawson     }
72615e32d5dSMike Smith     return (Status);
72715e32d5dSMike Smith }
72815e32d5dSMike Smith 
72915e32d5dSMike Smith static ACPI_STATUS
73015e32d5dSMike Smith EcTransaction(struct acpi_ec_softc *sc, EC_REQUEST *EcRequest)
73115e32d5dSMike Smith {
73215e32d5dSMike Smith     ACPI_STATUS	Status;
73315e32d5dSMike Smith 
734464c662eSNate Lawson     Status = EcLock(sc);
735464c662eSNate Lawson     if (ACPI_FAILURE(Status))
73615e32d5dSMike Smith 	return (Status);
73715e32d5dSMike Smith 
73815e32d5dSMike Smith     /*
73915e32d5dSMike Smith      * Perform the transaction.
74015e32d5dSMike Smith      */
74115e32d5dSMike Smith     switch (EcRequest->Command) {
74215e32d5dSMike Smith     case EC_COMMAND_READ:
743464c662eSNate Lawson 	Status = EcRead(sc, EcRequest->Address, &EcRequest->Data);
74415e32d5dSMike Smith 	break;
74515e32d5dSMike Smith     case EC_COMMAND_WRITE:
746464c662eSNate Lawson 	Status = EcWrite(sc, EcRequest->Address, &EcRequest->Data);
74715e32d5dSMike Smith 	break;
74815e32d5dSMike Smith     default:
74915e32d5dSMike Smith 	Status = AE_SUPPORT;
75015e32d5dSMike Smith 	break;
75115e32d5dSMike Smith     }
75215e32d5dSMike Smith 
7532a4ac806SMike Smith     EcUnlock(sc);
7542a4ac806SMike Smith 
7552a4ac806SMike Smith     /*
75615e32d5dSMike Smith      * Clear & Re-Enable the EC GPE:
75715e32d5dSMike Smith      * -----------------------------
75815e32d5dSMike Smith      * 'Consume' any EC GPE events that we generated while performing
75915e32d5dSMike Smith      * the transaction (e.g. IBF/OBF).	Clearing the GPE here shouldn't
76015e32d5dSMike Smith      * have an adverse affect on outstanding EC-SCI's, as the source
76115e32d5dSMike Smith      * (EC-SCI) will still be high and thus should trigger the GPE
76215e32d5dSMike Smith      * immediately after we re-enabling it.
76315e32d5dSMike Smith      */
764c07572e7STakanori Watanabe     if (sc->ec_pendquery) {
765dbd0058aSMike Smith 	if (ACPI_FAILURE(AcpiOsQueueForExecution(OSD_PRIORITY_HIGH,
766dbd0058aSMike Smith 						 EcGpeQueryHandler, sc)))
767c07572e7STakanori Watanabe 	    printf("Pend Query Queuing Failed\n");
768c07572e7STakanori Watanabe 	sc->ec_pendquery = 0;
769c07572e7STakanori Watanabe     }
770c07572e7STakanori Watanabe 
771464c662eSNate Lawson     if (ACPI_FAILURE(AcpiClearEvent(sc->ec_gpebit, ACPI_EVENT_GPE))) {
7726971b3c7SMitsuru IWASAKI 	ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
7736971b3c7SMitsuru IWASAKI 	    "EcRequest: Unable to clear the EC GPE.\n");
774464c662eSNate Lawson     }
775464c662eSNate Lawson     if (ACPI_FAILURE(AcpiEnableEvent(sc->ec_gpebit, ACPI_EVENT_GPE, 0))) {
7766971b3c7SMitsuru IWASAKI 	ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
7776971b3c7SMitsuru IWASAKI 	    "EcRequest: Unable to re-enable the EC GPE.\n");
778464c662eSNate Lawson     }
77915e32d5dSMike Smith 
78015e32d5dSMike Smith     return (Status);
78115e32d5dSMike Smith }
78215e32d5dSMike Smith 
78315e32d5dSMike Smith 
78415e32d5dSMike Smith static ACPI_STATUS
78515e32d5dSMike Smith EcRead(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data)
78615e32d5dSMike Smith {
78715e32d5dSMike Smith     ACPI_STATUS	Status;
78815e32d5dSMike Smith 
789464c662eSNate Lawson     if (!EcIsLocked(sc)) {
7906971b3c7SMitsuru IWASAKI 	ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
7916971b3c7SMitsuru IWASAKI 	    "EcRead called without EC lock!\n");
792464c662eSNate Lawson     }
79315e32d5dSMike Smith 
79415e32d5dSMike Smith     /*EcBurstEnable(EmbeddedController);*/
79515e32d5dSMike Smith 
79615e32d5dSMike Smith     EC_SET_CSR(sc, EC_COMMAND_READ);
797464c662eSNate Lawson     Status = EcWaitEventIntr(sc, EC_EVENT_INPUT_BUFFER_EMPTY);
798464c662eSNate Lawson     if (ACPI_FAILURE(Status)) {
7996971b3c7SMitsuru IWASAKI 	ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
8006971b3c7SMitsuru IWASAKI 	    "EcRead: Failed waiting for EC to process read command.\n");
80115e32d5dSMike Smith 	return (Status);
80215e32d5dSMike Smith     }
80315e32d5dSMike Smith 
80415e32d5dSMike Smith     EC_SET_DATA(sc, Address);
805464c662eSNate Lawson     Status = EcWaitEventIntr(sc, EC_EVENT_OUTPUT_BUFFER_FULL);
806464c662eSNate Lawson     if (ACPI_FAILURE(Status)) {
8076971b3c7SMitsuru IWASAKI 	ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
8086971b3c7SMitsuru IWASAKI 	    "EcRead: Failed waiting for EC to send data.\n");
80915e32d5dSMike Smith 	return (Status);
81015e32d5dSMike Smith     }
81115e32d5dSMike Smith 
812464c662eSNate Lawson     *Data = EC_GET_DATA(sc);
81315e32d5dSMike Smith 
81415e32d5dSMike Smith     /*EcBurstDisable(EmbeddedController);*/
81515e32d5dSMike Smith 
81615e32d5dSMike Smith     return (AE_OK);
81715e32d5dSMike Smith }
81815e32d5dSMike Smith 
81915e32d5dSMike Smith static ACPI_STATUS
82015e32d5dSMike Smith EcWrite(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data)
82115e32d5dSMike Smith {
82215e32d5dSMike Smith     ACPI_STATUS	Status;
82315e32d5dSMike Smith 
824464c662eSNate Lawson     if (!EcIsLocked(sc)) {
8256971b3c7SMitsuru IWASAKI 	ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
8266971b3c7SMitsuru IWASAKI 	    "EcWrite called without EC lock!\n");
827464c662eSNate Lawson     }
82815e32d5dSMike Smith 
82915e32d5dSMike Smith     /*EcBurstEnable(EmbeddedController);*/
83015e32d5dSMike Smith 
83115e32d5dSMike Smith     EC_SET_CSR(sc, EC_COMMAND_WRITE);
832464c662eSNate Lawson     Status = EcWaitEventIntr(sc, EC_EVENT_INPUT_BUFFER_EMPTY);
833464c662eSNate Lawson     if (ACPI_FAILURE(Status)) {
8346971b3c7SMitsuru IWASAKI 	ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
8356971b3c7SMitsuru IWASAKI 	    "EcWrite: Failed waiting for EC to process write command.\n");
83615e32d5dSMike Smith 	return (Status);
83715e32d5dSMike Smith     }
83815e32d5dSMike Smith 
83915e32d5dSMike Smith     EC_SET_DATA(sc, Address);
840464c662eSNate Lawson     Status = EcWaitEventIntr(sc, EC_EVENT_INPUT_BUFFER_EMPTY);
841464c662eSNate Lawson     if (ACPI_FAILURE(Status)) {
8426971b3c7SMitsuru IWASAKI 	ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
8436971b3c7SMitsuru IWASAKI 	    "EcRead: Failed waiting for EC to process address.\n");
84415e32d5dSMike Smith 	return (Status);
84515e32d5dSMike Smith     }
84615e32d5dSMike Smith 
84715e32d5dSMike Smith     EC_SET_DATA(sc, *Data);
848464c662eSNate Lawson     Status = EcWaitEventIntr(sc, EC_EVENT_INPUT_BUFFER_EMPTY);
849464c662eSNate Lawson     if (ACPI_FAILURE(Status)) {
8506971b3c7SMitsuru IWASAKI 	ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
8516971b3c7SMitsuru IWASAKI 	    "EcWrite: Failed waiting for EC to process data.\n");
85215e32d5dSMike Smith 	return (Status);
85315e32d5dSMike Smith     }
85415e32d5dSMike Smith 
85515e32d5dSMike Smith     /*EcBurstDisable(EmbeddedController);*/
85615e32d5dSMike Smith 
85715e32d5dSMike Smith     return (AE_OK);
85815e32d5dSMike Smith }
859