| Commit | Line | Data |
|---|---|---|
| 5ed44076 | 1 | /*- |
| 10f97674 | 2 | * Copyright (c) 2003-2007 Nate Lawson |
| 5ed44076 MD |
3 | * Copyright (c) 2000 Michael Smith |
| 4 | * Copyright (c) 2000 BSDi | |
| 5 | * All rights reserved. | |
| 6 | * | |
| 7 | * Redistribution and use in source and binary forms, with or without | |
| 8 | * modification, are permitted provided that the following conditions | |
| 9 | * are met: | |
| 10 | * 1. Redistributions of source code must retain the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer. | |
| 12 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 13 | * notice, this list of conditions and the following disclaimer in the | |
| 14 | * documentation and/or other materials provided with the distribution. | |
| 15 | * | |
| 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 26 | * SUCH DAMAGE. | |
| 32af04f7 SW |
27 | * |
| 28 | * $FreeBSD: src/sys/dev/acpica/acpi_ec.c,v 1.76.2.1.6.1 2009/04/15 03:14:26 kensmith Exp $ | |
| 5ed44076 | 29 | */ |
| 10f97674 | 30 | |
| 5ed44076 MD |
31 | #include "opt_acpi.h" |
| 32 | #include <sys/param.h> | |
| 33 | #include <sys/kernel.h> | |
| 34 | #include <sys/bus.h> | |
| 10f97674 | 35 | #include <sys/lock.h> |
| f9d8cd12 | 36 | #include <sys/malloc.h> |
| 5895535b | 37 | #include <sys/module.h> |
| 5ed44076 MD |
38 | #include <sys/rman.h> |
| 39 | ||
| 40 | #include "acpi.h" | |
| 5ed44076 | 41 | #include <dev/acpica5/acpivar.h> |
| 10f97674 | 42 | #include "acutils.h" |
| 5ed44076 | 43 | |
| 10f97674 | 44 | /* Hooks for the ACPI CA debugging infrastructure */ |
| 5ed44076 MD |
45 | #define _COMPONENT ACPI_EC |
| 46 | ACPI_MODULE_NAME("EC") | |
| 47 | ||
| 10f97674 AP |
48 | #define rebooting 0 |
| 49 | #define PZERO 0 | |
| 5ed44076 MD |
50 | /* |
| 51 | * EC_COMMAND: | |
| 52 | * ----------- | |
| 53 | */ | |
| 54 | typedef UINT8 EC_COMMAND; | |
| 55 | ||
| 56 | #define EC_COMMAND_UNKNOWN ((EC_COMMAND) 0x00) | |
| 57 | #define EC_COMMAND_READ ((EC_COMMAND) 0x80) | |
| 58 | #define EC_COMMAND_WRITE ((EC_COMMAND) 0x81) | |
| 59 | #define EC_COMMAND_BURST_ENABLE ((EC_COMMAND) 0x82) | |
| 60 | #define EC_COMMAND_BURST_DISABLE ((EC_COMMAND) 0x83) | |
| 61 | #define EC_COMMAND_QUERY ((EC_COMMAND) 0x84) | |
| 62 | ||
| 10f97674 | 63 | /* |
| 5ed44076 MD |
64 | * EC_STATUS: |
| 65 | * ---------- | |
| 66 | * The encoding of the EC status register is illustrated below. | |
| 67 | * Note that a set bit (1) indicates the property is TRUE | |
| 68 | * (e.g. if bit 0 is set then the output buffer is full). | |
| 69 | * +-+-+-+-+-+-+-+-+ | |
| 10f97674 | 70 | * |7|6|5|4|3|2|1|0| |
| 5ed44076 MD |
71 | * +-+-+-+-+-+-+-+-+ |
| 72 | * | | | | | | | | | |
| 73 | * | | | | | | | +- Output Buffer Full? | |
| 74 | * | | | | | | +--- Input Buffer Full? | |
| 75 | * | | | | | +----- <reserved> | |
| 76 | * | | | | +------- Data Register is Command Byte? | |
| 77 | * | | | +--------- Burst Mode Enabled? | |
| 78 | * | | +----------- SCI Event? | |
| 79 | * | +------------- SMI Event? | |
| 10f97674 | 80 | * +--------------- <reserved> |
| 5ed44076 MD |
81 | * |
| 82 | */ | |
| 83 | typedef UINT8 EC_STATUS; | |
| 84 | ||
| 85 | #define EC_FLAG_OUTPUT_BUFFER ((EC_STATUS) 0x01) | |
| 86 | #define EC_FLAG_INPUT_BUFFER ((EC_STATUS) 0x02) | |
| 10f97674 | 87 | #define EC_FLAG_DATA_IS_CMD ((EC_STATUS) 0x08) |
| 5ed44076 | 88 | #define EC_FLAG_BURST_MODE ((EC_STATUS) 0x10) |
| 5ed44076 MD |
89 | |
| 90 | /* | |
| 91 | * EC_EVENT: | |
| 92 | * --------- | |
| 93 | */ | |
| 94 | typedef UINT8 EC_EVENT; | |
| 95 | ||
| 96 | #define EC_EVENT_UNKNOWN ((EC_EVENT) 0x00) | |
| 97 | #define EC_EVENT_OUTPUT_BUFFER_FULL ((EC_EVENT) 0x01) | |
| 98 | #define EC_EVENT_INPUT_BUFFER_EMPTY ((EC_EVENT) 0x02) | |
| 99 | #define EC_EVENT_SCI ((EC_EVENT) 0x20) | |
| 10f97674 AP |
100 | #define EC_EVENT_SMI ((EC_EVENT) 0x40) |
| 101 | ||
| 102 | /* Data byte returned after burst enable indicating it was successful. */ | |
| 103 | #define EC_BURST_ACK 0x90 | |
| 5ed44076 MD |
104 | |
| 105 | /* | |
| 106 | * Register access primitives | |
| 107 | */ | |
| 108 | #define EC_GET_DATA(sc) \ | |
| 109 | bus_space_read_1((sc)->ec_data_tag, (sc)->ec_data_handle, 0) | |
| 110 | ||
| 111 | #define EC_SET_DATA(sc, v) \ | |
| 112 | bus_space_write_1((sc)->ec_data_tag, (sc)->ec_data_handle, 0, (v)) | |
| 113 | ||
| 114 | #define EC_GET_CSR(sc) \ | |
| 115 | bus_space_read_1((sc)->ec_csr_tag, (sc)->ec_csr_handle, 0) | |
| 116 | ||
| 117 | #define EC_SET_CSR(sc, v) \ | |
| 118 | bus_space_write_1((sc)->ec_csr_tag, (sc)->ec_csr_handle, 0, (v)) | |
| 119 | ||
| f9d8cd12 MD |
120 | /* Additional params to pass from the probe routine */ |
| 121 | struct acpi_ec_params { | |
| 122 | int glk; | |
| 123 | int gpe_bit; | |
| 124 | ACPI_HANDLE gpe_handle; | |
| 125 | int uid; | |
| 126 | }; | |
| 5ed44076 | 127 | |
| f9d8cd12 | 128 | /* Indicate that this device has already been probed via ECDT. */ |
| 10f97674 | 129 | #define DEV_ECDT(x) (acpi_get_magic(x) == (uintptr_t)&acpi_ec_devclass) |
| 5ed44076 MD |
130 | |
| 131 | /* | |
| 132 | * Driver softc. | |
| 133 | */ | |
| 134 | struct acpi_ec_softc { | |
| 135 | device_t ec_dev; | |
| 136 | ACPI_HANDLE ec_handle; | |
| f9d8cd12 MD |
137 | int ec_uid; |
| 138 | ACPI_HANDLE ec_gpehandle; | |
| 5ed44076 | 139 | UINT8 ec_gpebit; |
| 10f97674 | 140 | |
| 5ed44076 MD |
141 | int ec_data_rid; |
| 142 | struct resource *ec_data_res; | |
| 143 | bus_space_tag_t ec_data_tag; | |
| 144 | bus_space_handle_t ec_data_handle; | |
| 145 | ||
| 146 | int ec_csr_rid; | |
| 147 | struct resource *ec_csr_res; | |
| 148 | bus_space_tag_t ec_csr_tag; | |
| 149 | bus_space_handle_t ec_csr_handle; | |
| 150 | ||
| 151 | int ec_glk; | |
| 152 | int ec_glkhandle; | |
| 10f97674 AP |
153 | int ec_burstactive; |
| 154 | int ec_sci_pend; | |
| 155 | u_int ec_gencount; | |
| 156 | int ec_suspending; | |
| 5ed44076 MD |
157 | }; |
| 158 | ||
| 159 | /* | |
| 10f97674 | 160 | * XXX njl |
| 5ed44076 MD |
161 | * I couldn't find it in the spec but other implementations also use a |
| 162 | * value of 1 ms for the time to acquire global lock. | |
| 163 | */ | |
| 164 | #define EC_LOCK_TIMEOUT 1000 | |
| 165 | ||
| 10f97674 AP |
166 | /* Default delay in microseconds between each run of the status polling loop. */ |
| 167 | #define EC_POLL_DELAY 5 | |
| 5ed44076 | 168 | |
| 10f97674 AP |
169 | /* Total time in ms spent waiting for a response from EC. */ |
| 170 | #define EC_TIMEOUT 750 | |
| 5ed44076 MD |
171 | |
| 172 | #define EVENT_READY(event, status) \ | |
| 173 | (((event) == EC_EVENT_OUTPUT_BUFFER_FULL && \ | |
| 174 | ((status) & EC_FLAG_OUTPUT_BUFFER) != 0) || \ | |
| 175 | ((event) == EC_EVENT_INPUT_BUFFER_EMPTY && \ | |
| 176 | ((status) & EC_FLAG_INPUT_BUFFER) == 0)) | |
| 177 | ||
| 10f97674 AP |
178 | ACPI_SERIAL_DECL(ec, "ACPI embedded controller"); |
| 179 | ||
| 180 | SYSCTL_DECL(_debug_acpi); | |
| 181 | SYSCTL_NODE(_debug_acpi, OID_AUTO, ec, CTLFLAG_RD, NULL, "EC debugging"); | |
| 5ed44076 | 182 | |
| 10f97674 AP |
183 | static int ec_burst_mode; |
| 184 | TUNABLE_INT("debug.acpi.ec.burst", &ec_burst_mode); | |
| 185 | SYSCTL_INT(_debug_acpi_ec, OID_AUTO, burst, CTLFLAG_RW, &ec_burst_mode, 0, | |
| 186 | "Enable use of burst mode (faster for nearly all systems)"); | |
| 187 | static int ec_polled_mode; | |
| 188 | TUNABLE_INT("debug.acpi.ec.polled", &ec_polled_mode); | |
| 189 | SYSCTL_INT(_debug_acpi_ec, OID_AUTO, polled, CTLFLAG_RW, &ec_polled_mode, 0, | |
| 190 | "Force use of polled mode (only if interrupt mode doesn't work)"); | |
| 191 | static int ec_timeout = EC_TIMEOUT; | |
| 192 | TUNABLE_INT("debug.acpi.ec.timeout", &ec_timeout); | |
| 193 | SYSCTL_INT(_debug_acpi_ec, OID_AUTO, timeout, CTLFLAG_RW, &ec_timeout, | |
| 194 | EC_TIMEOUT, "Total time spent waiting for a response (poll+sleep)"); | |
| 195 | ||
| 196 | static ACPI_STATUS | |
| 5ed44076 MD |
197 | EcLock(struct acpi_ec_softc *sc) |
| 198 | { | |
| 10f97674 | 199 | ACPI_STATUS status; |
| 5ed44076 | 200 | |
| af0c5620 | 201 | ACPI_SERIAL_BEGIN(ec); |
| 10f97674 AP |
202 | /* If _GLK is non-zero, acquire the global lock. */ |
| 203 | status = AE_OK; | |
| 5ed44076 MD |
204 | if (sc->ec_glk) { |
| 205 | status = AcpiAcquireGlobalLock(EC_LOCK_TIMEOUT, &sc->ec_glkhandle); | |
| 206 | if (ACPI_FAILURE(status)) | |
| af0c5620 | 207 | ACPI_SERIAL_END(ec); |
| 5ed44076 | 208 | } |
| 5ed44076 MD |
209 | return (status); |
| 210 | } | |
| 211 | ||
| 10f97674 | 212 | static void |
| 5ed44076 MD |
213 | EcUnlock(struct acpi_ec_softc *sc) |
| 214 | { | |
| 215 | if (sc->ec_glk) | |
| 216 | AcpiReleaseGlobalLock(sc->ec_glkhandle); | |
| af0c5620 | 217 | ACPI_SERIAL_END(ec); |
| 5ed44076 MD |
218 | } |
| 219 | ||
| 49e48b8a | 220 | static uint32_t EcGpeHandler(void *Context); |
| 10f97674 | 221 | static ACPI_STATUS EcSpaceSetup(ACPI_HANDLE Region, UINT32 Function, |
| 5ed44076 MD |
222 | void *Context, void **return_Context); |
| 223 | static ACPI_STATUS EcSpaceHandler(UINT32 Function, | |
| 224 | ACPI_PHYSICAL_ADDRESS Address, | |
| 225 | UINT32 width, ACPI_INTEGER *Value, | |
| 226 | void *Context, void *RegionContext); | |
| 10f97674 AP |
227 | static ACPI_STATUS EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event, |
| 228 | u_int gen_count); | |
| 5ed44076 MD |
229 | static ACPI_STATUS EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd); |
| 230 | static ACPI_STATUS EcRead(struct acpi_ec_softc *sc, UINT8 Address, | |
| 231 | UINT8 *Data); | |
| 232 | static ACPI_STATUS EcWrite(struct acpi_ec_softc *sc, UINT8 Address, | |
| 233 | UINT8 *Data); | |
| 234 | static int acpi_ec_probe(device_t dev); | |
| 235 | static int acpi_ec_attach(device_t dev); | |
| 10f97674 AP |
236 | static int acpi_ec_suspend(device_t dev); |
| 237 | static int acpi_ec_resume(device_t dev); | |
| 238 | static int acpi_ec_shutdown(device_t dev); | |
| 676159d4 HT |
239 | static int acpi_ec_read_method(device_t dev, u_int addr, |
| 240 | ACPI_INTEGER *val, int width); | |
| 241 | static int acpi_ec_write_method(device_t dev, u_int addr, | |
| 242 | ACPI_INTEGER val, int width); | |
| 5ed44076 MD |
243 | |
| 244 | static device_method_t acpi_ec_methods[] = { | |
| 245 | /* Device interface */ | |
| 246 | DEVMETHOD(device_probe, acpi_ec_probe), | |
| 247 | DEVMETHOD(device_attach, acpi_ec_attach), | |
| 10f97674 AP |
248 | DEVMETHOD(device_suspend, acpi_ec_suspend), |
| 249 | DEVMETHOD(device_resume, acpi_ec_resume), | |
| 250 | DEVMETHOD(device_shutdown, acpi_ec_shutdown), | |
| 5ed44076 | 251 | |
| 676159d4 | 252 | /* Embedded controller interface */ |
| 10f97674 AP |
253 | DEVMETHOD(acpi_ec_read, acpi_ec_read_method), |
| 254 | DEVMETHOD(acpi_ec_write, acpi_ec_write_method), | |
| 676159d4 | 255 | |
| 5ed44076 MD |
256 | {0, 0} |
| 257 | }; | |
| 258 | ||
| 259 | static driver_t acpi_ec_driver = { | |
| 260 | "acpi_ec", | |
| 261 | acpi_ec_methods, | |
| 262 | sizeof(struct acpi_ec_softc), | |
| 263 | }; | |
| 264 | ||
| 265 | static devclass_t acpi_ec_devclass; | |
| 266 | DRIVER_MODULE(acpi_ec, acpi, acpi_ec_driver, acpi_ec_devclass, 0, 0); | |
| f9d8cd12 | 267 | MODULE_DEPEND(acpi_ec, acpi, 1, 1, 1); |
| 5ed44076 MD |
268 | |
| 269 | /* | |
| 10f97674 | 270 | * Look for an ECDT and if we find one, set up default GPE and |
| 5ed44076 MD |
271 | * space handlers to catch attempts to access EC space before |
| 272 | * we have a real driver instance in place. | |
| 10f97674 AP |
273 | * |
| 274 | * TODO: Some old Gateway laptops need us to fake up an ECDT or | |
| 275 | * otherwise attach early so that _REG methods can run. | |
| 5ed44076 MD |
276 | */ |
| 277 | void | |
| 278 | acpi_ec_ecdt_probe(device_t parent) | |
| 279 | { | |
| 280 | ACPI_TABLE_ECDT *ecdt; | |
| 281 | ACPI_STATUS status; | |
| 282 | device_t child; | |
| 283 | ACPI_HANDLE h; | |
| f9d8cd12 | 284 | struct acpi_ec_params *params; |
| 5ed44076 MD |
285 | |
| 286 | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | |
| 287 | ||
| 288 | /* Find and validate the ECDT. */ | |
| e1eeedd0 | 289 | status = AcpiGetTable(ACPI_SIG_ECDT, 1, (ACPI_TABLE_HEADER **)&ecdt); |
| 5ed44076 | 290 | if (ACPI_FAILURE(status) || |
| 10f97674 AP |
291 | ecdt->Control.BitWidth != 8 || |
| 292 | ecdt->Data.BitWidth != 8) { | |
| 5ed44076 MD |
293 | return; |
| 294 | } | |
| 295 | ||
| 296 | /* Create the child device with the given unit number. */ | |
| e1eeedd0 | 297 | child = BUS_ADD_CHILD(parent, parent, 0, "acpi_ec", ecdt->Uid); |
| 5ed44076 | 298 | if (child == NULL) { |
| e3869ec7 | 299 | kprintf("%s: can't add child\n", __func__); |
| 5ed44076 MD |
300 | return; |
| 301 | } | |
| 302 | ||
| 303 | /* Find and save the ACPI handle for this device. */ | |
| e1eeedd0 | 304 | status = AcpiGetHandle(NULL, ecdt->Id, &h); |
| 5ed44076 MD |
305 | if (ACPI_FAILURE(status)) { |
| 306 | device_delete_child(parent, child); | |
| e3869ec7 | 307 | kprintf("%s: can't get handle\n", __func__); |
| 5ed44076 MD |
308 | return; |
| 309 | } | |
| 310 | acpi_set_handle(child, h); | |
| 311 | ||
| 312 | /* Set the data and CSR register addresses. */ | |
| e1eeedd0 | 313 | bus_set_resource(child, SYS_RES_IOPORT, 0, ecdt->Data.Address, |
| 5ed44076 | 314 | /*count*/1); |
| e1eeedd0 | 315 | bus_set_resource(child, SYS_RES_IOPORT, 1, ecdt->Control.Address, |
| 5ed44076 MD |
316 | /*count*/1); |
| 317 | ||
| 318 | /* | |
| 319 | * Store values for the probe/attach routines to use. Store the | |
| f9d8cd12 MD |
320 | * ECDT GPE bit and set the global lock flag according to _GLK. |
| 321 | * Note that it is not perfectly correct to be evaluating a method | |
| 322 | * before initializing devices, but in practice this function | |
| 323 | * should be safe to call at this point. | |
| 5ed44076 | 324 | */ |
| efda3bd0 | 325 | params = kmalloc(sizeof(struct acpi_ec_params), M_TEMP, M_WAITOK | M_ZERO); |
| f9d8cd12 | 326 | params->gpe_handle = NULL; |
| e1eeedd0 YT |
327 | params->gpe_bit = ecdt->Gpe; |
| 328 | params->uid = ecdt->Uid; | |
| f9d8cd12 MD |
329 | acpi_GetInteger(h, "_GLK", ¶ms->glk); |
| 330 | acpi_set_private(child, params); | |
| 10f97674 | 331 | acpi_set_magic(child, (uintptr_t)&acpi_ec_devclass); |
| 5ed44076 MD |
332 | |
| 333 | /* Finish the attach process. */ | |
| 334 | if (device_probe_and_attach(child) != 0) | |
| 335 | device_delete_child(parent, child); | |
| 336 | } | |
| 337 | ||
| 338 | static int | |
| 339 | acpi_ec_probe(device_t dev) | |
| 340 | { | |
| f9d8cd12 | 341 | ACPI_BUFFER buf; |
| 5ed44076 | 342 | ACPI_HANDLE h; |
| f9d8cd12 | 343 | ACPI_OBJECT *obj; |
| 5ed44076 MD |
344 | ACPI_STATUS status; |
| 345 | device_t peer; | |
| 346 | char desc[64]; | |
| f9d8cd12 MD |
347 | int ret; |
| 348 | struct acpi_ec_params *params; | |
| 10f97674 | 349 | static char *ec_ids[] = { "PNP0C09", NULL }; |
| 5ed44076 MD |
350 | |
| 351 | /* Check that this is a device and that EC is not disabled. */ | |
| 352 | if (acpi_get_type(dev) != ACPI_TYPE_DEVICE || acpi_disabled("ec")) | |
| 353 | return (ENXIO); | |
| 354 | ||
| 355 | /* | |
| 356 | * If probed via ECDT, set description and continue. Otherwise, | |
| 357 | * we can access the namespace and make sure this is not a | |
| 358 | * duplicate probe. | |
| 359 | */ | |
| f9d8cd12 MD |
360 | ret = ENXIO; |
| 361 | params = NULL; | |
| 362 | buf.Pointer = NULL; | |
| 363 | buf.Length = ACPI_ALLOCATE_BUFFER; | |
| 5ed44076 | 364 | if (DEV_ECDT(dev)) { |
| f9d8cd12 | 365 | params = acpi_get_private(dev); |
| 5ed44076 | 366 | ret = 0; |
| 10f97674 AP |
367 | } else if (!acpi_disabled("ec") && |
| 368 | ACPI_ID_PROBE(device_get_parent(dev), dev, ec_ids)) { | |
| efda3bd0 | 369 | params = kmalloc(sizeof(struct acpi_ec_params), M_TEMP, |
| f9d8cd12 | 370 | M_WAITOK | M_ZERO); |
| 5ed44076 MD |
371 | h = acpi_get_handle(dev); |
| 372 | ||
| 373 | /* | |
| 374 | * Read the unit ID to check for duplicate attach and the | |
| 375 | * global lock value to see if we should acquire it when | |
| 376 | * accessing the EC. | |
| 377 | */ | |
| f9d8cd12 | 378 | status = acpi_GetInteger(h, "_UID", ¶ms->uid); |
| 5ed44076 | 379 | if (ACPI_FAILURE(status)) |
| f9d8cd12 MD |
380 | params->uid = 0; |
| 381 | status = acpi_GetInteger(h, "_GLK", ¶ms->glk); | |
| 5ed44076 | 382 | if (ACPI_FAILURE(status)) |
| f9d8cd12 | 383 | params->glk = 0; |
| 5ed44076 MD |
384 | |
| 385 | /* | |
| 386 | * Evaluate the _GPE method to find the GPE bit used by the EC to | |
| f9d8cd12 MD |
387 | * signal status (SCI). If it's a package, it contains a reference |
| 388 | * and GPE bit, similar to _PRW. | |
| 5ed44076 | 389 | */ |
| f9d8cd12 | 390 | status = AcpiEvaluateObject(h, "_GPE", NULL, &buf); |
| 5ed44076 MD |
391 | if (ACPI_FAILURE(status)) { |
| 392 | device_printf(dev, "can't evaluate _GPE - %s\n", | |
| 393 | AcpiFormatException(status)); | |
| 10f97674 | 394 | goto out; |
| 5ed44076 | 395 | } |
| f9d8cd12 MD |
396 | obj = (ACPI_OBJECT *)buf.Pointer; |
| 397 | if (obj == NULL) | |
| 10f97674 | 398 | goto out; |
| f9d8cd12 MD |
399 | |
| 400 | switch (obj->Type) { | |
| 401 | case ACPI_TYPE_INTEGER: | |
| 402 | params->gpe_handle = NULL; | |
| 403 | params->gpe_bit = obj->Integer.Value; | |
| 404 | break; | |
| 405 | case ACPI_TYPE_PACKAGE: | |
| 406 | if (!ACPI_PKG_VALID(obj, 2)) | |
| 407 | goto out; | |
| 408 | params->gpe_handle = | |
| 409 | acpi_GetReference(NULL, &obj->Package.Elements[0]); | |
| 410 | if (params->gpe_handle == NULL || | |
| 411 | acpi_PkgInt32(obj, 1, ¶ms->gpe_bit) != 0) | |
| 412 | goto out; | |
| 413 | break; | |
| 414 | default: | |
| 415 | device_printf(dev, "_GPE has invalid type %d\n", obj->Type); | |
| 416 | goto out; | |
| 417 | } | |
| 5ed44076 MD |
418 | |
| 419 | /* Store the values we got from the namespace for attach. */ | |
| f9d8cd12 | 420 | acpi_set_private(dev, params); |
| 5ed44076 MD |
421 | |
| 422 | /* | |
| 423 | * Check for a duplicate probe. This can happen when a probe | |
| f9d8cd12 MD |
424 | * via ECDT succeeded already. If this is a duplicate, disable |
| 425 | * this device. | |
| 5ed44076 | 426 | */ |
| f9d8cd12 MD |
427 | peer = devclass_get_device(acpi_ec_devclass, params->uid); |
| 428 | if (peer == NULL || !device_is_alive(peer)) | |
| 5ed44076 | 429 | ret = 0; |
| f9d8cd12 MD |
430 | else |
| 431 | device_disable(dev); | |
| 432 | } | |
| 433 | ||
| 434 | out: | |
| 435 | if (ret == 0) { | |
| f8c7a42d | 436 | ksnprintf(desc, sizeof(desc), "Embedded Controller: GPE %#x%s%s", |
| f9d8cd12 MD |
437 | params->gpe_bit, (params->glk) ? ", GLK" : "", |
| 438 | DEV_ECDT(dev) ? ", ECDT" : ""); | |
| 439 | device_set_desc_copy(dev, desc); | |
| 5ed44076 MD |
440 | } |
| 441 | ||
| f9d8cd12 | 442 | if (ret > 0 && params) |
| efda3bd0 | 443 | kfree(params, M_TEMP); |
| f9d8cd12 MD |
444 | if (buf.Pointer) |
| 445 | AcpiOsFree(buf.Pointer); | |
| 5ed44076 MD |
446 | return (ret); |
| 447 | } | |
| 448 | ||
| 449 | static int | |
| 450 | acpi_ec_attach(device_t dev) | |
| 451 | { | |
| 452 | struct acpi_ec_softc *sc; | |
| f9d8cd12 | 453 | struct acpi_ec_params *params; |
| 5ed44076 | 454 | ACPI_STATUS Status; |
| 5ed44076 MD |
455 | |
| 456 | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | |
| 457 | ||
| 458 | /* Fetch/initialize softc (assumes softc is pre-zeroed). */ | |
| 459 | sc = device_get_softc(dev); | |
| f9d8cd12 | 460 | params = acpi_get_private(dev); |
| 5ed44076 MD |
461 | sc->ec_dev = dev; |
| 462 | sc->ec_handle = acpi_get_handle(dev); | |
| 5c7ffd75 | 463 | ACPI_SERIAL_INIT(ec); |
| 5ed44076 MD |
464 | |
| 465 | /* Retrieve previously probed values via device ivars. */ | |
| f9d8cd12 MD |
466 | sc->ec_glk = params->glk; |
| 467 | sc->ec_gpebit = params->gpe_bit; | |
| 468 | sc->ec_gpehandle = params->gpe_handle; | |
| 469 | sc->ec_uid = params->uid; | |
| 10f97674 | 470 | sc->ec_suspending = FALSE; |
| efda3bd0 | 471 | kfree(params, M_TEMP); |
| 5ed44076 MD |
472 | |
| 473 | /* Attach bus resources for data and command/status ports. */ | |
| 474 | sc->ec_data_rid = 0; | |
| f9d8cd12 MD |
475 | sc->ec_data_res = bus_alloc_resource_any(sc->ec_dev, SYS_RES_IOPORT, |
| 476 | &sc->ec_data_rid, RF_ACTIVE); | |
| 5ed44076 MD |
477 | if (sc->ec_data_res == NULL) { |
| 478 | device_printf(dev, "can't allocate data port\n"); | |
| 49e48b8a | 479 | goto error; |
| 5ed44076 MD |
480 | } |
| 481 | sc->ec_data_tag = rman_get_bustag(sc->ec_data_res); | |
| 482 | sc->ec_data_handle = rman_get_bushandle(sc->ec_data_res); | |
| 483 | ||
| 484 | sc->ec_csr_rid = 1; | |
| f9d8cd12 MD |
485 | sc->ec_csr_res = bus_alloc_resource_any(sc->ec_dev, SYS_RES_IOPORT, |
| 486 | &sc->ec_csr_rid, RF_ACTIVE); | |
| 5ed44076 MD |
487 | if (sc->ec_csr_res == NULL) { |
| 488 | device_printf(dev, "can't allocate command/status port\n"); | |
| 49e48b8a | 489 | goto error; |
| 5ed44076 MD |
490 | } |
| 491 | sc->ec_csr_tag = rman_get_bustag(sc->ec_csr_res); | |
| 492 | sc->ec_csr_handle = rman_get_bushandle(sc->ec_csr_res); | |
| 493 | ||
| 494 | /* | |
| 495 | * Install a handler for this EC's GPE bit. We want edge-triggered | |
| 496 | * behavior. | |
| 497 | */ | |
| 498 | ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "attaching GPE handler\n")); | |
| f9d8cd12 MD |
499 | Status = AcpiInstallGpeHandler(sc->ec_gpehandle, sc->ec_gpebit, |
| 500 | ACPI_GPE_EDGE_TRIGGERED, &EcGpeHandler, sc); | |
| 5ed44076 MD |
501 | if (ACPI_FAILURE(Status)) { |
| 502 | device_printf(dev, "can't install GPE handler for %s - %s\n", | |
| 503 | acpi_name(sc->ec_handle), AcpiFormatException(Status)); | |
| 49e48b8a | 504 | goto error; |
| 5ed44076 MD |
505 | } |
| 506 | ||
| 10f97674 | 507 | /* |
| 5ed44076 MD |
508 | * Install address space handler |
| 509 | */ | |
| 510 | ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "attaching address space handler\n")); | |
| 511 | Status = AcpiInstallAddressSpaceHandler(sc->ec_handle, ACPI_ADR_SPACE_EC, | |
| 512 | &EcSpaceHandler, &EcSpaceSetup, sc); | |
| 513 | if (ACPI_FAILURE(Status)) { | |
| 514 | device_printf(dev, "can't install address space handler for %s - %s\n", | |
| 515 | acpi_name(sc->ec_handle), AcpiFormatException(Status)); | |
| 49e48b8a MD |
516 | goto error; |
| 517 | } | |
| 518 | ||
| 519 | /* Enable runtime GPEs for the handler. */ | |
| 520 | Status = AcpiSetGpeType(sc->ec_gpehandle, sc->ec_gpebit, | |
| 521 | ACPI_GPE_TYPE_RUNTIME); | |
| 522 | if (ACPI_FAILURE(Status)) { | |
| 523 | device_printf(dev, "AcpiSetGpeType failed: %s\n", | |
| 524 | AcpiFormatException(Status)); | |
| 525 | goto error; | |
| 526 | } | |
| 527 | Status = AcpiEnableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_NOT_ISR); | |
| 528 | if (ACPI_FAILURE(Status)) { | |
| 529 | device_printf(dev, "AcpiEnableGpe failed: %s\n", | |
| 530 | AcpiFormatException(Status)); | |
| 531 | goto error; | |
| 5ed44076 MD |
532 | } |
| 533 | ||
| 534 | ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "acpi_ec_attach complete\n")); | |
| 535 | return (0); | |
| 536 | ||
| 49e48b8a MD |
537 | error: |
| 538 | AcpiRemoveGpeHandler(sc->ec_gpehandle, sc->ec_gpebit, &EcGpeHandler); | |
| 539 | AcpiRemoveAddressSpaceHandler(sc->ec_handle, ACPI_ADR_SPACE_EC, | |
| 540 | EcSpaceHandler); | |
| 5ed44076 | 541 | if (sc->ec_csr_res) |
| 10f97674 | 542 | bus_release_resource(sc->ec_dev, SYS_RES_IOPORT, sc->ec_csr_rid, |
| 5ed44076 MD |
543 | sc->ec_csr_res); |
| 544 | if (sc->ec_data_res) | |
| 545 | bus_release_resource(sc->ec_dev, SYS_RES_IOPORT, sc->ec_data_rid, | |
| 546 | sc->ec_data_res); | |
| 49e48b8a | 547 | return (ENXIO); |
| 5ed44076 MD |
548 | } |
| 549 | ||
| 10f97674 AP |
550 | static int |
| 551 | acpi_ec_suspend(device_t dev) | |
| 552 | { | |
| 553 | struct acpi_ec_softc *sc; | |
| 554 | ||
| 555 | sc = device_get_softc(dev); | |
| 556 | sc->ec_suspending = TRUE; | |
| 557 | return (0); | |
| 558 | } | |
| 559 | ||
| 560 | static int | |
| 561 | acpi_ec_resume(device_t dev) | |
| 562 | { | |
| 563 | struct acpi_ec_softc *sc; | |
| 564 | ||
| 565 | sc = device_get_softc(dev); | |
| 566 | sc->ec_suspending = FALSE; | |
| 567 | return (0); | |
| 568 | } | |
| 569 | ||
| 570 | static int | |
| 571 | acpi_ec_shutdown(device_t dev) | |
| 572 | { | |
| 573 | struct acpi_ec_softc *sc; | |
| 574 | ||
| 575 | /* Disable the GPE so we don't get EC events during shutdown. */ | |
| 576 | sc = device_get_softc(dev); | |
| 577 | AcpiDisableGpe(sc->ec_gpehandle, sc->ec_gpebit, ACPI_NOT_ISR); | |
| 578 | return (0); | |
| 579 | } | |
| 580 | ||
| 676159d4 HT |
581 | /* Methods to allow other devices (e.g., smbat) to read/write EC space. */ |
| 582 | static int | |
| 583 | acpi_ec_read_method(device_t dev, u_int addr, ACPI_INTEGER *val, int width) | |
| 584 | { | |
| 585 | struct acpi_ec_softc *sc; | |
| 586 | ACPI_STATUS status; | |
| 587 | ||
| 588 | sc = device_get_softc(dev); | |
| 589 | status = EcSpaceHandler(ACPI_READ, addr, width * 8, val, sc, NULL); | |
| 590 | if (ACPI_FAILURE(status)) | |
| 10f97674 | 591 | return (ENXIO); |
| 676159d4 HT |
592 | return (0); |
| 593 | } | |
| 594 | ||
| 595 | static int | |
| 596 | acpi_ec_write_method(device_t dev, u_int addr, ACPI_INTEGER val, int width) | |
| 597 | { | |
| 598 | struct acpi_ec_softc *sc; | |
| 599 | ACPI_STATUS status; | |
| 600 | ||
| 601 | sc = device_get_softc(dev); | |
| 602 | status = EcSpaceHandler(ACPI_WRITE, addr, width * 8, &val, sc, NULL); | |
| 603 | if (ACPI_FAILURE(status)) | |
| 10f97674 | 604 | return (ENXIO); |
| 676159d4 HT |
605 | return (0); |
| 606 | } | |
| 607 | ||
| 5ed44076 MD |
608 | static void |
| 609 | EcGpeQueryHandler(void *Context) | |
| 610 | { | |
| 611 | struct acpi_ec_softc *sc = (struct acpi_ec_softc *)Context; | |
| 612 | UINT8 Data; | |
| 613 | ACPI_STATUS Status; | |
| 5ed44076 MD |
614 | char qxx[5]; |
| 615 | ||
| 616 | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | |
| 617 | KASSERT(Context != NULL, ("EcGpeQueryHandler called with NULL")); | |
| 618 | ||
| 10f97674 | 619 | /* Serialize user access with EcSpaceHandler(). */ |
| 5ed44076 MD |
620 | Status = EcLock(sc); |
| 621 | if (ACPI_FAILURE(Status)) { | |
| ac779aee | 622 | EcUnlock(sc); |
| 10f97674 AP |
623 | device_printf(sc->ec_dev, "GpeQuery lock error: %s\n", |
| 624 | AcpiFormatException(Status)); | |
| 5ed44076 MD |
625 | return; |
| 626 | } | |
| 627 | ||
| 628 | /* | |
| 5ed44076 MD |
629 | * Send a query command to the EC to find out which _Qxx call it |
| 630 | * wants to make. This command clears the SCI bit and also the | |
| 10f97674 AP |
631 | * interrupt source since we are edge-triggered. To prevent the GPE |
| 632 | * that may arise from running the query from causing another query | |
| 633 | * to be queued, we clear the pending flag only after running it. | |
| 5ed44076 MD |
634 | */ |
| 635 | Status = EcCommand(sc, EC_COMMAND_QUERY); | |
| 10f97674 | 636 | sc->ec_sci_pend = FALSE; |
| 5ed44076 MD |
637 | if (ACPI_FAILURE(Status)) { |
| 638 | EcUnlock(sc); | |
| 10f97674 AP |
639 | device_printf(sc->ec_dev, "GPE query failed: %s\n", |
| 640 | AcpiFormatException(Status)); | |
| 641 | return; | |
| 5ed44076 MD |
642 | } |
| 643 | Data = EC_GET_DATA(sc); | |
| 10f97674 AP |
644 | |
| 645 | /* | |
| 646 | * We have to unlock before running the _Qxx method below since that | |
| 647 | * method may attempt to read/write from EC address space, causing | |
| 648 | * recursive acquisition of the lock. | |
| 649 | */ | |
| 5ed44076 MD |
650 | EcUnlock(sc); |
| 651 | ||
| 652 | /* Ignore the value for "no outstanding event". (13.3.5) */ | |
| 10f97674 AP |
653 | #if 0 |
| 654 | CTR2(KTR_ACPI, "ec query ok,%s running _Q%02X", Data ? "" : " not", Data); | |
| 655 | #endif | |
| 5ed44076 | 656 | if (Data == 0) |
| 10f97674 | 657 | return; |
| 5ed44076 MD |
658 | |
| 659 | /* Evaluate _Qxx to respond to the controller. */ | |
| 10f97674 AP |
660 | ksnprintf(qxx, sizeof(qxx), "_Q%02X", Data); |
| 661 | AcpiUtStrupr(qxx); | |
| 5ed44076 MD |
662 | Status = AcpiEvaluateObject(sc->ec_handle, qxx, NULL, NULL); |
| 663 | if (ACPI_FAILURE(Status) && Status != AE_NOT_FOUND) { | |
| 10f97674 AP |
664 | device_printf(sc->ec_dev, "evaluation of query method %s failed: %s\n", |
| 665 | qxx, AcpiFormatException(Status)); | |
| 5ed44076 | 666 | } |
| 5ed44076 MD |
667 | } |
| 668 | ||
| 669 | /* | |
| 10f97674 AP |
670 | * The GPE handler is called when IBE/OBF or SCI events occur. We are |
| 671 | * called from an unknown lock context. | |
| 5ed44076 | 672 | */ |
| 49e48b8a | 673 | static uint32_t |
| 5ed44076 MD |
674 | EcGpeHandler(void *Context) |
| 675 | { | |
| 676 | struct acpi_ec_softc *sc = Context; | |
| 677 | ACPI_STATUS Status; | |
| 10f97674 | 678 | EC_STATUS EcStatus; |
| 5ed44076 MD |
679 | |
| 680 | KASSERT(Context != NULL, ("EcGpeHandler called with NULL")); | |
| 10f97674 AP |
681 | #if 0 |
| 682 | CTR0(KTR_ACPI, "ec gpe handler start"); | |
| 683 | #endif | |
| 684 | /* | |
| 685 | * Notify EcWaitEvent() that the status register is now fresh. If we | |
| 686 | * didn't do this, it wouldn't be possible to distinguish an old IBE | |
| 687 | * from a new one, for example when doing a write transaction (writing | |
| 688 | * address and then data values.) | |
| 689 | */ | |
| 690 | atomic_add_int(&sc->ec_gencount, 1); | |
| 691 | wakeup(&sc->ec_gencount); | |
| 5ed44076 | 692 | |
| 10f97674 AP |
693 | /* |
| 694 | * If the EC_SCI bit of the status register is set, queue a query handler. | |
| 695 | * It will run the query and _Qxx method later, under the lock. | |
| 696 | */ | |
| 697 | EcStatus = EC_GET_CSR(sc); | |
| 698 | if ((EcStatus & EC_EVENT_SCI) && !sc->ec_sci_pend) { | |
| 699 | #if 0 | |
| 700 | CTR0(KTR_ACPI, "ec gpe queueing query handler"); | |
| 701 | #endif | |
| 702 | Status = AcpiOsExecute(OSL_GPE_HANDLER, EcGpeQueryHandler, Context); | |
| 703 | if (ACPI_SUCCESS(Status)) | |
| 704 | sc->ec_sci_pend = TRUE; | |
| 705 | else | |
| 706 | kprintf("EcGpeHandler: queuing GPE query handler failed\n"); | |
| 5ed44076 | 707 | } |
| 49e48b8a | 708 | return (0); |
| 5ed44076 MD |
709 | } |
| 710 | ||
| 711 | static ACPI_STATUS | |
| 712 | EcSpaceSetup(ACPI_HANDLE Region, UINT32 Function, void *Context, | |
| 713 | void **RegionContext) | |
| 714 | { | |
| 715 | ||
| 716 | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | |
| 717 | ||
| 718 | /* | |
| 719 | * If deactivating a region, always set the output to NULL. Otherwise, | |
| 720 | * just pass the context through. | |
| 721 | */ | |
| 722 | if (Function == ACPI_REGION_DEACTIVATE) | |
| 723 | *RegionContext = NULL; | |
| 724 | else | |
| 725 | *RegionContext = Context; | |
| 726 | ||
| 727 | return_ACPI_STATUS (AE_OK); | |
| 728 | } | |
| 729 | ||
| 730 | static ACPI_STATUS | |
| 731 | EcSpaceHandler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address, UINT32 width, | |
| 732 | ACPI_INTEGER *Value, void *Context, void *RegionContext) | |
| 733 | { | |
| 734 | struct acpi_ec_softc *sc = (struct acpi_ec_softc *)Context; | |
| f9d8cd12 | 735 | ACPI_STATUS Status; |
| 5ed44076 MD |
736 | UINT8 EcAddr, EcData; |
| 737 | int i; | |
| 738 | ||
| 739 | ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)Address); | |
| 740 | ||
| f9d8cd12 | 741 | if (width % 8 != 0 || Value == NULL || Context == NULL) |
| 5ed44076 | 742 | return_ACPI_STATUS (AE_BAD_PARAMETER); |
| f9d8cd12 MD |
743 | if (Address + (width / 8) - 1 > 0xFF) |
| 744 | return_ACPI_STATUS (AE_BAD_ADDRESS); | |
| 5ed44076 | 745 | |
| f9d8cd12 MD |
746 | if (Function == ACPI_READ) |
| 747 | *Value = 0; | |
| 5ed44076 | 748 | EcAddr = Address; |
| f9d8cd12 MD |
749 | Status = AE_ERROR; |
| 750 | ||
| 10f97674 AP |
751 | /* |
| 752 | * If booting, check if we need to run the query handler. If so, we | |
| 753 | * we call it directly here since our thread taskq is not active yet. | |
| 754 | */ | |
| 755 | if (cold || rebooting) { | |
| 756 | if ((EC_GET_CSR(sc) & EC_EVENT_SCI)) { | |
| 757 | #if 0 | |
| 758 | CTR0(KTR_ACPI, "ec running gpe handler directly"); | |
| 759 | #endif | |
| 760 | EcGpeQueryHandler(sc); | |
| 761 | } | |
| 762 | } | |
| 763 | ||
| 764 | /* Serialize with EcGpeQueryHandler() at transaction granularity. */ | |
| 765 | Status = EcLock(sc); | |
| ac779aee MD |
766 | if (ACPI_FAILURE(Status)) { |
| 767 | EcUnlock(sc); | |
| 10f97674 | 768 | return_ACPI_STATUS (Status); |
| ac779aee | 769 | } |
| 10f97674 | 770 | |
| f9d8cd12 MD |
771 | /* Perform the transaction(s), based on width. */ |
| 772 | for (i = 0; i < width; i += 8, EcAddr++) { | |
| 5ed44076 MD |
773 | switch (Function) { |
| 774 | case ACPI_READ: | |
| 5ed44076 | 775 | Status = EcRead(sc, EcAddr, &EcData); |
| f9d8cd12 MD |
776 | if (ACPI_SUCCESS(Status)) |
| 777 | *Value |= ((ACPI_INTEGER)EcData) << i; | |
| 5ed44076 MD |
778 | break; |
| 779 | case ACPI_WRITE: | |
| 780 | EcData = (UINT8)((*Value) >> i); | |
| 781 | Status = EcWrite(sc, EcAddr, &EcData); | |
| 782 | break; | |
| 783 | default: | |
| 784 | device_printf(sc->ec_dev, "invalid EcSpaceHandler function %d\n", | |
| 785 | Function); | |
| 786 | Status = AE_BAD_PARAMETER; | |
| 787 | break; | |
| 788 | } | |
| 5ed44076 | 789 | if (ACPI_FAILURE(Status)) |
| f9d8cd12 | 790 | break; |
| 5ed44076 | 791 | } |
| f9d8cd12 | 792 | |
| 10f97674 | 793 | EcUnlock(sc); |
| 5ed44076 MD |
794 | return_ACPI_STATUS (Status); |
| 795 | } | |
| 796 | ||
| 797 | static ACPI_STATUS | |
| 10f97674 | 798 | EcCheckStatus(struct acpi_ec_softc *sc, const char *msg, EC_EVENT event) |
| 5ed44076 | 799 | { |
| 10f97674 AP |
800 | ACPI_STATUS status; |
| 801 | EC_STATUS ec_status; | |
| 5ed44076 | 802 | |
| 10f97674 AP |
803 | status = AE_NO_HARDWARE_RESPONSE; |
| 804 | ec_status = EC_GET_CSR(sc); | |
| 805 | if (sc->ec_burstactive && !(ec_status & EC_FLAG_BURST_MODE)) { | |
| 806 | #if 0 | |
| 807 | CTR1(KTR_ACPI, "ec burst disabled in waitevent (%s)", msg); | |
| 808 | #endif | |
| 809 | sc->ec_burstactive = FALSE; | |
| 810 | } | |
| 811 | if (EVENT_READY(event, ec_status)) { | |
| 812 | #if 0 | |
| 813 | CTR2(KTR_ACPI, "ec %s wait ready, status %#x", msg, ec_status); | |
| 814 | #endif | |
| 815 | status = AE_OK; | |
| 816 | } | |
| 817 | return (status); | |
| 818 | } | |
| 5ed44076 | 819 | |
| 10f97674 AP |
820 | static ACPI_STATUS |
| 821 | EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event, u_int gen_count) | |
| 822 | { | |
| 823 | ACPI_STATUS Status; | |
| 824 | int count, i, slp_ival; | |
| 5ed44076 | 825 | |
| 10f97674 AP |
826 | ACPI_SERIAL_ASSERT(ec); |
| 827 | Status = AE_NO_HARDWARE_RESPONSE; | |
| 828 | int need_poll = cold || rebooting || ec_polled_mode || sc->ec_suspending; | |
| 5ed44076 | 829 | /* |
| 10f97674 AP |
830 | * The main CPU should be much faster than the EC. So the status should |
| 831 | * be "not ready" when we start waiting. But if the main CPU is really | |
| 832 | * slow, it's possible we see the current "ready" response. Since that | |
| 833 | * can't be distinguished from the previous response in polled mode, | |
| 834 | * this is a potential issue. We really should have interrupts enabled | |
| 835 | * during boot so there is no ambiguity in polled mode. | |
| 836 | * | |
| 837 | * If this occurs, we add an additional delay before actually entering | |
| 838 | * the status checking loop, hopefully to allow the EC to go to work | |
| 839 | * and produce a non-stale status. | |
| 5ed44076 | 840 | */ |
| 10f97674 AP |
841 | if (need_poll) { |
| 842 | static int once; | |
| 843 | ||
| 844 | if (EcCheckStatus(sc, "pre-check", Event) == AE_OK) { | |
| 845 | if (!once) { | |
| 846 | device_printf(sc->ec_dev, | |
| 847 | "warning: EC done before starting event wait\n"); | |
| 848 | once = 1; | |
| 849 | } | |
| 850 | AcpiOsStall(10); | |
| 5ed44076 | 851 | } |
| 5ed44076 | 852 | } |
| 5ed44076 | 853 | |
| 10f97674 AP |
854 | /* Wait for event by polling or GPE (interrupt). */ |
| 855 | if (need_poll) { | |
| 856 | count = (ec_timeout * 1000) / EC_POLL_DELAY; | |
| 5895535b YT |
857 | if (count == 0) |
| 858 | count = 1; | |
| 5895535b | 859 | for (i = 0; i < count; i++) { |
| 10f97674 AP |
860 | Status = EcCheckStatus(sc, "poll", Event); |
| 861 | if (Status == AE_OK) | |
| 5ed44076 | 862 | break; |
| 10f97674 AP |
863 | AcpiOsStall(EC_POLL_DELAY); |
| 864 | } | |
| 865 | } else { | |
| 866 | slp_ival = hz / 1000; | |
| 867 | if (slp_ival != 0) { | |
| 868 | count = ec_timeout; | |
| 869 | } else { | |
| 870 | /* hz has less than 1 ms resolution so scale timeout. */ | |
| 871 | slp_ival = 1; | |
| 872 | count = ec_timeout / (1000 / hz); | |
| 873 | } | |
| 874 | ||
| 875 | /* | |
| 876 | * Wait for the GPE to signal the status changed, checking the | |
| 877 | * status register each time we get one. It's possible to get a | |
| 878 | * GPE for an event we're not interested in here (i.e., SCI for | |
| 879 | * EC query). | |
| 880 | */ | |
| 881 | for (i = 0; i < count; i++) { | |
| 882 | if (gen_count != sc->ec_gencount) { | |
| 883 | /* | |
| 884 | * Record new generation count. It's possible the GPE was | |
| 885 | * just to notify us that a query is needed and we need to | |
| 886 | * wait for a second GPE to signal the completion of the | |
| 887 | * event we are actually waiting for. | |
| 888 | */ | |
| 889 | gen_count = sc->ec_gencount; | |
| 890 | Status = EcCheckStatus(sc, "sleep", Event); | |
| 891 | if (Status == AE_OK) | |
| 892 | break; | |
| 5ed44076 | 893 | } |
| 10f97674 | 894 | tsleep(&sc->ec_gencount, PZERO, "ecgpe", slp_ival); |
| 5ed44076 | 895 | } |
| 5ed44076 | 896 | |
| 10f97674 AP |
897 | /* |
| 898 | * We finished waiting for the GPE and it never arrived. Try to | |
| 899 | * read the register once and trust whatever value we got. This is | |
| 900 | * the best we can do at this point. Then, force polled mode on | |
| 901 | * since this system doesn't appear to generate GPEs. | |
| 902 | */ | |
| 903 | if (Status != AE_OK) { | |
| 904 | Status = EcCheckStatus(sc, "sleep_end", Event); | |
| 905 | device_printf(sc->ec_dev, | |
| 906 | "wait timed out (%sresponse), forcing polled mode\n", | |
| 907 | Status == AE_OK ? "" : "no "); | |
| 908 | ec_polled_mode = TRUE; | |
| 909 | } | |
| 5ed44076 | 910 | } |
| 10f97674 AP |
911 | #if 0 |
| 912 | if (Status != AE_OK) | |
| 913 | CTR0(KTR_ACPI, "error: ec wait timed out"); | |
| 914 | #endif | |
| 5ed44076 | 915 | return (Status); |
| 10f97674 | 916 | } |
| 5ed44076 MD |
917 | |
| 918 | static ACPI_STATUS | |
| 919 | EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd) | |
| 920 | { | |
| 10f97674 AP |
921 | ACPI_STATUS status; |
| 922 | EC_EVENT event; | |
| 923 | EC_STATUS ec_status; | |
| 924 | u_int gen_count; | |
| 5ed44076 | 925 | |
| 10f97674 AP |
926 | ACPI_SERIAL_ASSERT(ec); |
| 927 | ||
| 928 | /* Don't use burst mode if user disabled it. */ | |
| 929 | if (!ec_burst_mode && cmd == EC_COMMAND_BURST_ENABLE) | |
| 930 | return (AE_ERROR); | |
| 5ed44076 MD |
931 | |
| 932 | /* Decide what to wait for based on command type. */ | |
| 933 | switch (cmd) { | |
| 934 | case EC_COMMAND_READ: | |
| 935 | case EC_COMMAND_WRITE: | |
| 936 | case EC_COMMAND_BURST_DISABLE: | |
| 10f97674 | 937 | event = EC_EVENT_INPUT_BUFFER_EMPTY; |
| 5ed44076 MD |
938 | break; |
| 939 | case EC_COMMAND_QUERY: | |
| 940 | case EC_COMMAND_BURST_ENABLE: | |
| 10f97674 | 941 | event = EC_EVENT_OUTPUT_BUFFER_FULL; |
| 5ed44076 MD |
942 | break; |
| 943 | default: | |
| 10f97674 | 944 | device_printf(sc->ec_dev, "EcCommand: invalid command %#x\n", cmd); |
| 5ed44076 MD |
945 | return (AE_BAD_PARAMETER); |
| 946 | } | |
| 947 | ||
| 948 | /* Run the command and wait for the chosen event. */ | |
| 10f97674 AP |
949 | #if 0 |
| 950 | CTR1(KTR_ACPI, "ec running command %#x", cmd); | |
| 951 | #endif | |
| 952 | gen_count = sc->ec_gencount; | |
| 5ed44076 | 953 | EC_SET_CSR(sc, cmd); |
| 10f97674 AP |
954 | status = EcWaitEvent(sc, event, gen_count); |
| 955 | if (ACPI_SUCCESS(status)) { | |
| 956 | /* If we succeeded, burst flag should now be present. */ | |
| 957 | if (cmd == EC_COMMAND_BURST_ENABLE) { | |
| 958 | ec_status = EC_GET_CSR(sc); | |
| 959 | if ((ec_status & EC_FLAG_BURST_MODE) == 0) | |
| 960 | status = AE_ERROR; | |
| 961 | } | |
| 962 | } else | |
| 963 | device_printf(sc->ec_dev, "EcCommand: no response to %#x\n", cmd); | |
| 964 | return (status); | |
| 5ed44076 MD |
965 | } |
| 966 | ||
| 967 | static ACPI_STATUS | |
| 968 | EcRead(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data) | |
| 969 | { | |
| 10f97674 AP |
970 | ACPI_STATUS status; |
| 971 | UINT8 data; | |
| 972 | u_int gen_count; | |
| 5ed44076 | 973 | |
| 10f97674 AP |
974 | ACPI_SERIAL_ASSERT(ec); |
| 975 | #if 0 | |
| 976 | CTR1(KTR_ACPI, "ec read from %#x", Address); | |
| 977 | #endif | |
| 5ed44076 | 978 | /* If we can't start burst mode, continue anyway. */ |
| 10f97674 AP |
979 | status = EcCommand(sc, EC_COMMAND_BURST_ENABLE); |
| 980 | if (status == AE_OK) { | |
| 981 | data = EC_GET_DATA(sc); | |
| 982 | if (data == EC_BURST_ACK) { | |
| 983 | #if 0 | |
| 984 | CTR0(KTR_ACPI, "ec burst enabled"); | |
| 5ed44076 | 985 | #endif |
| 10f97674 AP |
986 | sc->ec_burstactive = TRUE; |
| 987 | } | |
| 988 | } | |
| 5ed44076 | 989 | |
| 10f97674 AP |
990 | status = EcCommand(sc, EC_COMMAND_READ); |
| 991 | if (ACPI_FAILURE(status)) | |
| 992 | return (status); | |
| 5ed44076 | 993 | |
| 10f97674 | 994 | gen_count = sc->ec_gencount; |
| 5ed44076 | 995 | EC_SET_DATA(sc, Address); |
| 10f97674 AP |
996 | status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL, gen_count); |
| 997 | if (ACPI_FAILURE(status)) { | |
| 998 | device_printf(sc->ec_dev, "EcRead: failed waiting to get data\n"); | |
| 999 | return (status); | |
| 5ed44076 | 1000 | } |
| 5ed44076 MD |
1001 | *Data = EC_GET_DATA(sc); |
| 1002 | ||
| 5ed44076 | 1003 | if (sc->ec_burstactive) { |
| 10f97674 AP |
1004 | sc->ec_burstactive = FALSE; |
| 1005 | status = EcCommand(sc, EC_COMMAND_BURST_DISABLE); | |
| 1006 | if (ACPI_FAILURE(status)) | |
| 1007 | return (status); | |
| 1008 | #if 0 | |
| 1009 | CTR0(KTR_ACPI, "ec disabled burst ok"); | |
| 5ed44076 | 1010 | #endif |
| 10f97674 | 1011 | } |
| 5ed44076 MD |
1012 | |
| 1013 | return (AE_OK); | |
| 10f97674 | 1014 | } |
| 5ed44076 MD |
1015 | |
| 1016 | static ACPI_STATUS | |
| 1017 | EcWrite(struct acpi_ec_softc *sc, UINT8 Address, UINT8 *Data) | |
| 1018 | { | |
| 10f97674 AP |
1019 | ACPI_STATUS status; |
| 1020 | UINT8 data; | |
| 1021 | u_int gen_count; | |
| 5ed44076 | 1022 | |
| 10f97674 AP |
1023 | ACPI_SERIAL_ASSERT(ec); |
| 1024 | #if 0 | |
| 1025 | CTR2(KTR_ACPI, "ec write to %#x, data %#x", Address, *Data); | |
| 1026 | #endif | |
| 5ed44076 | 1027 | |
| 5ed44076 | 1028 | /* If we can't start burst mode, continue anyway. */ |
| 10f97674 AP |
1029 | status = EcCommand(sc, EC_COMMAND_BURST_ENABLE); |
| 1030 | if (status == AE_OK) { | |
| 1031 | data = EC_GET_DATA(sc); | |
| 1032 | if (data == EC_BURST_ACK) { | |
| 1033 | #if 0 | |
| 1034 | CTR0(KTR_ACPI, "ec burst enabled"); | |
| 5ed44076 | 1035 | #endif |
| 10f97674 AP |
1036 | sc->ec_burstactive = TRUE; |
| 1037 | } | |
| 1038 | } | |
| 5ed44076 | 1039 | |
| 10f97674 AP |
1040 | status = EcCommand(sc, EC_COMMAND_WRITE); |
| 1041 | if (ACPI_FAILURE(status)) | |
| 1042 | return (status); | |
| 5ed44076 | 1043 | |
| 10f97674 | 1044 | gen_count = sc->ec_gencount; |
| 5ed44076 | 1045 | EC_SET_DATA(sc, Address); |
| 10f97674 AP |
1046 | status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count); |
| 1047 | if (ACPI_FAILURE(status)) { | |
| 1048 | device_printf(sc->ec_dev, "EcRead: failed waiting for sent address\n"); | |
| 1049 | return (status); | |
| 5ed44076 MD |
1050 | } |
| 1051 | ||
| 10f97674 | 1052 | gen_count = sc->ec_gencount; |
| 5ed44076 | 1053 | EC_SET_DATA(sc, *Data); |
| 10f97674 AP |
1054 | status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count); |
| 1055 | if (ACPI_FAILURE(status)) { | |
| 1056 | device_printf(sc->ec_dev, "EcWrite: failed waiting for sent data\n"); | |
| 1057 | return (status); | |
| 5ed44076 MD |
1058 | } |
| 1059 | ||
| 5ed44076 | 1060 | if (sc->ec_burstactive) { |
| 10f97674 AP |
1061 | sc->ec_burstactive = FALSE; |
| 1062 | status = EcCommand(sc, EC_COMMAND_BURST_DISABLE); | |
| 1063 | if (ACPI_FAILURE(status)) | |
| 1064 | return (status); | |
| 1065 | #if 0 | |
| 1066 | CTR0(KTR_ACPI, "ec disabled burst ok"); | |
| 5ed44076 | 1067 | #endif |
| 10f97674 | 1068 | } |
| 5ed44076 MD |
1069 | |
| 1070 | return (AE_OK); | |
| 1071 | } |