Annotation of src/sys/dev/acpi/acpi_ec.c, Revision 1.51.4.2
1.51.4.2! yamt 1: /* $NetBSD: acpi_ec.c,v 1.51.4.1 2009/05/04 08:12:33 yamt Exp $ */
1.1 thorpej 2:
1.44 jmcneill 3: /*-
4: * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
1.1 thorpej 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: *
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
1.44 jmcneill 14: * notice, this list of conditions and the following disclaimer in
15: * the documentation and/or other materials provided with the
16: * distribution.
17: *
18: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21: * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22: * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23: * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1.1 thorpej 29: * SUCH DAMAGE.
30: */
31:
1.46 joerg 32: /*
33: * The ACPI Embedded Controller (EC) driver serves two different purposes:
34: * - read and write access from ASL, e.g. to read battery state
35: * - notification of ASL of System Control Interrupts.
36: *
37: * Access to the EC is serialised by sc_access_mtx and optionally the
38: * ACPI global mutex. Both locks are held until the request is fulfilled.
39: * All access to the softc has to hold sc_mtx to serialise against the GPE
40: * handler and the callout. sc_mtx is also used for wakeup conditions.
41: *
42: * SCIs are processed in a kernel thread. Handling gets a bit complicated
43: * by the lock order (sc_mtx must be acquired after sc_access_mtx and the
44: * ACPI global mutex).
45: *
46: * Read and write requests spin around for a short time as many requests
47: * can be handled instantly by the EC. During normal processing interrupt
48: * mode is used exclusively. At boot and resume time interrupts are not
49: * working and the handlers just busy loop.
50: *
51: * A callout is scheduled to compensate for missing interrupts on some
52: * hardware. If the EC doesn't process a request for 5s, it is most likely
53: * in a wedged state. No method to reset the EC is currently known.
54: *
55: * Special care has to be taken to not poll the EC in a busy loop without
56: * delay. This can prevent processing of Power Button events. At least some
57: * Lenovo Thinkpads seem to be implement the Power Button Override in the EC
58: * and the only option to recover on those models is to cut off all power.
59: */
60:
1.3 lukem 61: #include <sys/cdefs.h>
1.51.4.2! yamt 62: __KERNEL_RCSID(0, "$NetBSD: acpi_ec.c,v 1.51.4.1 2009/05/04 08:12:33 yamt Exp $");
1.1 thorpej 63:
64: #include <sys/param.h>
65: #include <sys/systm.h>
1.44 jmcneill 66: #include <sys/condvar.h>
1.1 thorpej 67: #include <sys/device.h>
68: #include <sys/kernel.h>
1.44 jmcneill 69: #include <sys/kthread.h>
70: #include <sys/mutex.h>
1.1 thorpej 71:
1.42 ad 72: #include <sys/bus.h>
1.1 thorpej 73:
74: #include <dev/acpi/acpivar.h>
1.48 jmcneill 75: #include <dev/acpi/acpi_ecvar.h>
1.1 thorpej 76:
1.44 jmcneill 77: /* Maximum time to wait for global ACPI lock in ms */
78: #define EC_LOCK_TIMEOUT 5
1.23 kochi 79:
1.44 jmcneill 80: /* Maximum time to poll for completion of a command in ms */
81: #define EC_POLL_TIMEOUT 5
1.1 thorpej 82:
1.46 joerg 83: /* Maximum time to give a single EC command in s */
84: #define EC_CMD_TIMEOUT 10
85:
1.44 jmcneill 86: /* From ACPI 3.0b, chapter 12.3 */
87: #define EC_COMMAND_READ 0x80
88: #define EC_COMMAND_WRITE 0x81
89: #define EC_COMMAND_BURST_EN 0x82
90: #define EC_COMMAND_BURST_DIS 0x83
91: #define EC_COMMAND_QUERY 0x84
92:
93: /* From ACPI 3.0b, chapter 12.2.1 */
94: #define EC_STATUS_OBF 0x01
95: #define EC_STATUS_IBF 0x02
96: #define EC_STATUS_CMD 0x08
97: #define EC_STATUS_BURST 0x10
98: #define EC_STATUS_SCI 0x20
99: #define EC_STATUS_SMI 0x40
1.1 thorpej 100:
1.44 jmcneill 101: static const char *ec_hid[] = {
102: "PNP0C09",
103: NULL,
104: };
105:
106: enum ec_state_t {
107: EC_STATE_QUERY,
108: EC_STATE_QUERY_VAL,
109: EC_STATE_READ,
110: EC_STATE_READ_ADDR,
111: EC_STATE_READ_VAL,
112: EC_STATE_WRITE,
113: EC_STATE_WRITE_ADDR,
114: EC_STATE_WRITE_VAL,
115: EC_STATE_FREE
116: };
117:
118: struct acpiec_softc {
119: ACPI_HANDLE sc_ech;
1.1 thorpej 120:
1.44 jmcneill 121: ACPI_HANDLE sc_gpeh;
122: UINT8 sc_gpebit;
1.1 thorpej 123:
1.44 jmcneill 124: bus_space_tag_t sc_data_st;
125: bus_space_handle_t sc_data_sh;
1.1 thorpej 126:
1.44 jmcneill 127: bus_space_tag_t sc_csr_st;
128: bus_space_handle_t sc_csr_sh;
1.1 thorpej 129:
1.44 jmcneill 130: bool sc_need_global_lock;
131: UINT32 sc_global_lock;
1.17 mycroft 132:
1.44 jmcneill 133: kmutex_t sc_mtx, sc_access_mtx;
134: kcondvar_t sc_cv, sc_cv_sci;
135: enum ec_state_t sc_state;
136: bool sc_got_sci;
1.46 joerg 137: callout_t sc_pseudo_intr;
1.44 jmcneill 138:
139: uint8_t sc_cur_addr, sc_cur_val;
1.23 kochi 140: };
1.1 thorpej 141:
1.51.4.2! yamt 142: static int acpiecdt_match(device_t, cfdata_t, void *);
1.44 jmcneill 143: static void acpiecdt_attach(device_t, device_t, void *);
144:
1.51.4.2! yamt 145: static int acpiec_match(device_t, cfdata_t, void *);
1.44 jmcneill 146: static void acpiec_attach(device_t, device_t, void *);
1.16 kochi 147:
1.44 jmcneill 148: static void acpiec_common_attach(device_t, device_t, ACPI_HANDLE,
149: bus_addr_t, bus_addr_t, ACPI_HANDLE, uint8_t);
1.1 thorpej 150:
1.51 dyoung 151: static bool acpiec_resume(device_t PMF_FN_PROTO);
152: static bool acpiec_suspend(device_t PMF_FN_PROTO);
1.17 mycroft 153:
1.44 jmcneill 154: static bool acpiec_parse_gpe_package(device_t, ACPI_HANDLE,
155: ACPI_HANDLE *, uint8_t *);
1.20 yamt 156:
1.46 joerg 157: static void acpiec_callout(void *);
1.44 jmcneill 158: static void acpiec_gpe_query(void *);
159: static UINT32 acpiec_gpe_handler(void *);
160: static ACPI_STATUS acpiec_space_setup(ACPI_HANDLE, UINT32, void *, void **);
161: static ACPI_STATUS acpiec_space_handler(UINT32, ACPI_PHYSICAL_ADDRESS,
162: UINT32, ACPI_INTEGER *, void *, void *);
1.20 yamt 163:
1.45 jmcneill 164: static void acpiec_gpe_state_machine(device_t);
1.20 yamt 165:
1.44 jmcneill 166: CFATTACH_DECL_NEW(acpiec, sizeof(struct acpiec_softc),
1.20 yamt 167: acpiec_match, acpiec_attach, NULL, NULL);
168:
1.44 jmcneill 169: CFATTACH_DECL_NEW(acpiecdt, sizeof(struct acpiec_softc),
170: acpiecdt_match, acpiecdt_attach, NULL, NULL);
171:
172: static device_t ec_singleton = NULL;
173: static bool acpiec_cold = false;
1.23 kochi 174:
1.44 jmcneill 175: static bool
176: acpiecdt_find(device_t parent, ACPI_HANDLE *ec_handle,
177: bus_addr_t *cmd_reg, bus_addr_t *data_reg, uint8_t *gpebit)
1.9 tshiozak 178: {
1.44 jmcneill 179: ACPI_TABLE_ECDT *ecdt;
180: ACPI_STATUS rv;
181:
182: rv = AcpiGetTable(ACPI_SIG_ECDT, 1, (ACPI_TABLE_HEADER **)&ecdt);
183: if (ACPI_FAILURE(rv))
184: return false;
185:
186: if (ecdt->Control.BitWidth != 8 || ecdt->Data.BitWidth != 8) {
187: aprint_error_dev(parent,
188: "ECDT register width invalid (%d/%d)\n",
189: ecdt->Control.BitWidth, ecdt->Data.BitWidth);
190: return false;
191: }
1.24 kochi 192:
1.44 jmcneill 193: rv = AcpiGetHandle(ACPI_ROOT_OBJECT, ecdt->Id, ec_handle);
194: if (ACPI_FAILURE(rv)) {
195: aprint_error_dev(parent,
196: "failed to look up EC object %s: %s\n",
197: ecdt->Id, AcpiFormatException(rv));
198: return false;
199: }
200:
201: *cmd_reg = ecdt->Control.Address;
202: *data_reg = ecdt->Data.Address;
203: *gpebit = ecdt->Gpe;
204:
205: return true;
1.9 tshiozak 206: }
1.4 thorpej 207:
1.44 jmcneill 208: static int
1.51.4.2! yamt 209: acpiecdt_match(device_t parent, cfdata_t match, void *aux)
1.1 thorpej 210: {
1.44 jmcneill 211: ACPI_HANDLE ec_handle;
212: bus_addr_t cmd_reg, data_reg;
213: uint8_t gpebit;
1.1 thorpej 214:
1.44 jmcneill 215: if (acpiecdt_find(parent, &ec_handle, &cmd_reg, &data_reg, &gpebit))
216: return 1;
217: else
218: return 0;
1.1 thorpej 219: }
220:
1.44 jmcneill 221: static void
222: acpiecdt_attach(device_t parent, device_t self, void *aux)
1.1 thorpej 223: {
1.44 jmcneill 224: ACPI_HANDLE ec_handle;
225: bus_addr_t cmd_reg, data_reg;
226: uint8_t gpebit;
227:
228: if (!acpiecdt_find(parent, &ec_handle, &cmd_reg, &data_reg, &gpebit))
229: panic("ECDT disappeared");
230:
1.51.4.1 yamt 231: aprint_naive("\n");
1.44 jmcneill 232: aprint_normal(": ACPI Embedded Controller via ECDT\n");
1.20 yamt 233:
1.44 jmcneill 234: acpiec_common_attach(parent, self, ec_handle, cmd_reg, data_reg,
235: NULL, gpebit);
1.1 thorpej 236: }
237:
1.31 kochi 238: static int
1.51.4.2! yamt 239: acpiec_match(device_t parent, cfdata_t match, void *aux)
1.1 thorpej 240: {
241: struct acpi_attach_args *aa = aux;
242:
243: if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
1.25 kochi 244: return 0;
1.1 thorpej 245:
1.25 kochi 246: return acpi_match_hid(aa->aa_node->ad_devinfo, ec_hid);
1.1 thorpej 247: }
248:
1.44 jmcneill 249: static void
250: acpiec_attach(device_t parent, device_t self, void *aux)
1.17 mycroft 251: {
1.44 jmcneill 252: struct acpi_attach_args *aa = aux;
253: struct acpi_resources ec_res;
254: struct acpi_io *io0, *io1;
255: ACPI_HANDLE gpe_handle;
256: uint8_t gpebit;
1.23 kochi 257: ACPI_STATUS rv;
1.17 mycroft 258:
1.44 jmcneill 259: if (ec_singleton != NULL) {
1.51.4.1 yamt 260: aprint_naive(": using %s\n", device_xname(ec_singleton));
261: aprint_normal(": using %s\n", device_xname(ec_singleton));
1.44 jmcneill 262: if (!pmf_device_register(self, NULL, NULL))
263: aprint_error_dev(self, "couldn't establish power handler\n");
264: return;
265: }
266:
267: if (!acpiec_parse_gpe_package(self, aa->aa_node->ad_handle,
268: &gpe_handle, &gpebit))
1.17 mycroft 269: return;
270:
1.44 jmcneill 271: rv = acpi_resource_parse(self, aa->aa_node->ad_handle, "_CRS",
272: &ec_res, &acpi_resource_parse_ops_default);
273: if (rv != AE_OK) {
274: aprint_error_dev(self, "resource parsing failed: %s\n",
275: AcpiFormatException(rv));
1.17 mycroft 276: return;
277: }
278:
1.44 jmcneill 279: if ((io0 = acpi_res_io(&ec_res, 0)) == NULL) {
280: aprint_error_dev(self, "no data register resource\n");
281: goto free_res;
282: }
283: if ((io1 = acpi_res_io(&ec_res, 1)) == NULL) {
284: aprint_error_dev(self, "no CSR register resource\n");
285: goto free_res;
1.23 kochi 286: }
287:
1.44 jmcneill 288: acpiec_common_attach(parent, self, aa->aa_node->ad_handle,
289: io1->ar_base, io0->ar_base, gpe_handle, gpebit);
1.23 kochi 290:
1.44 jmcneill 291: free_res:
292: acpi_resource_cleanup(&ec_res);
293: }
1.23 kochi 294:
1.44 jmcneill 295: static void
296: acpiec_common_attach(device_t parent, device_t self,
297: ACPI_HANDLE ec_handle, bus_addr_t cmd_reg, bus_addr_t data_reg,
298: ACPI_HANDLE gpe_handle, uint8_t gpebit)
299: {
300: struct acpiec_softc *sc = device_private(self);
301: ACPI_STATUS rv;
302: ACPI_INTEGER val;
1.23 kochi 303:
1.44 jmcneill 304: sc->sc_ech = ec_handle;
305: sc->sc_gpeh = gpe_handle;
306: sc->sc_gpebit = gpebit;
307:
308: sc->sc_state = EC_STATE_FREE;
309: mutex_init(&sc->sc_mtx, MUTEX_DRIVER, IPL_TTY);
310: mutex_init(&sc->sc_access_mtx, MUTEX_DEFAULT, IPL_NONE);
311: cv_init(&sc->sc_cv, "eccv");
312: cv_init(&sc->sc_cv_sci, "ecsci");
313:
314: if (bus_space_map(sc->sc_data_st, data_reg, 1, 0,
315: &sc->sc_data_sh) != 0) {
316: aprint_error_dev(self, "unable to map data register\n");
317: return;
318: }
1.23 kochi 319:
1.44 jmcneill 320: if (bus_space_map(sc->sc_csr_st, cmd_reg, 1, 0, &sc->sc_csr_sh) != 0) {
321: aprint_error_dev(self, "unable to map CSR register\n");
322: goto post_data_map;
1.23 kochi 323: }
324:
1.44 jmcneill 325: rv = acpi_eval_integer(sc->sc_ech, "_GLK", &val);
326: if (rv == AE_OK) {
327: sc->sc_need_global_lock = val != 0;
328: } else if (rv != AE_NOT_FOUND) {
329: aprint_error_dev(self, "unable to evaluate _GLK: %s\n",
330: AcpiFormatException(rv));
331: goto post_csr_map;
332: } else {
333: sc->sc_need_global_lock = false;
1.33 kochi 334: }
1.44 jmcneill 335: if (sc->sc_need_global_lock)
336: aprint_normal_dev(self, "using global ACPI lock\n");
1.33 kochi 337:
1.46 joerg 338: callout_init(&sc->sc_pseudo_intr, CALLOUT_MPSAFE);
339: callout_setfunc(&sc->sc_pseudo_intr, acpiec_callout, self);
340:
1.44 jmcneill 341: rv = AcpiInstallAddressSpaceHandler(sc->sc_ech, ACPI_ADR_SPACE_EC,
342: acpiec_space_handler, acpiec_space_setup, self);
343: if (rv != AE_OK) {
344: aprint_error_dev(self,
345: "unable to install address space handler: %s\n",
346: AcpiFormatException(rv));
347: goto post_csr_map;
1.33 kochi 348: }
349:
1.44 jmcneill 350: rv = AcpiInstallGpeHandler(sc->sc_gpeh, sc->sc_gpebit,
351: ACPI_GPE_EDGE_TRIGGERED, acpiec_gpe_handler, self);
352: if (rv != AE_OK) {
353: aprint_error_dev(self, "unable to install GPE handler: %s\n",
1.23 kochi 354: AcpiFormatException(rv));
1.44 jmcneill 355: goto post_csr_map;
1.17 mycroft 356: }
1.23 kochi 357:
1.44 jmcneill 358: rv = AcpiSetGpeType(sc->sc_gpeh, sc->sc_gpebit, ACPI_GPE_TYPE_RUNTIME);
359: if (rv != AE_OK) {
360: aprint_error_dev(self, "unable to set GPE type: %s\n",
361: AcpiFormatException(rv));
362: goto post_csr_map;
363: }
1.23 kochi 364:
1.44 jmcneill 365: rv = AcpiEnableGpe(sc->sc_gpeh, sc->sc_gpebit, ACPI_ISR);
366: if (rv != AE_OK) {
367: aprint_error_dev(self, "unable to enable GPE: %s\n",
368: AcpiFormatException(rv));
369: goto post_csr_map;
370: }
1.23 kochi 371:
1.44 jmcneill 372: if (kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, acpiec_gpe_query,
373: self, NULL, "acpiec sci thread")) {
374: aprint_error_dev(self, "unable to create query kthread\n");
375: goto post_csr_map;
376: }
1.23 kochi 377:
1.44 jmcneill 378: ec_singleton = self;
1.23 kochi 379:
1.44 jmcneill 380: if (!pmf_device_register(self, acpiec_suspend, acpiec_resume))
381: aprint_error_dev(self, "couldn't establish power handler\n");
1.23 kochi 382:
383: return;
1.44 jmcneill 384:
385: post_csr_map:
386: (void)AcpiRemoveGpeHandler(sc->sc_gpeh, sc->sc_gpebit,
387: acpiec_gpe_handler);
388: (void)AcpiRemoveAddressSpaceHandler(sc->sc_ech,
389: ACPI_ADR_SPACE_EC, acpiec_space_handler);
390: bus_space_unmap(sc->sc_csr_st, sc->sc_csr_sh, 1);
391: post_data_map:
392: bus_space_unmap(sc->sc_data_st, sc->sc_data_sh, 1);
1.17 mycroft 393: }
394:
1.44 jmcneill 395: static bool
1.51 dyoung 396: acpiec_suspend(device_t dv PMF_FN_ARGS)
1.1 thorpej 397: {
1.44 jmcneill 398: acpiec_cold = true;
1.1 thorpej 399:
1.44 jmcneill 400: return true;
401: }
1.1 thorpej 402:
1.44 jmcneill 403: static bool
1.51 dyoung 404: acpiec_resume(device_t dv PMF_FN_ARGS)
1.44 jmcneill 405: {
406: acpiec_cold = false;
1.1 thorpej 407:
1.44 jmcneill 408: return true;
409: }
1.17 mycroft 410:
1.44 jmcneill 411: static bool
412: acpiec_parse_gpe_package(device_t self, ACPI_HANDLE ec_handle,
413: ACPI_HANDLE *gpe_handle, uint8_t *gpebit)
414: {
415: ACPI_BUFFER buf;
416: ACPI_OBJECT *p, *c;
417: ACPI_STATUS rv;
1.1 thorpej 418:
1.44 jmcneill 419: rv = acpi_eval_struct(ec_handle, "_GPE", &buf);
420: if (rv != AE_OK) {
421: aprint_error_dev(self, "unable to evaluate _GPE: %s\n",
422: AcpiFormatException(rv));
423: return false;
424: }
1.1 thorpej 425:
1.44 jmcneill 426: p = buf.Pointer;
1.23 kochi 427:
1.44 jmcneill 428: if (p->Type == ACPI_TYPE_INTEGER) {
429: *gpe_handle = NULL;
430: *gpebit = p->Integer.Value;
431: AcpiOsFree(p);
432: return true;
433: }
1.23 kochi 434:
1.44 jmcneill 435: if (p->Type != ACPI_TYPE_PACKAGE) {
436: aprint_error_dev(self, "_GPE is neither integer nor package\n");
437: AcpiOsFree(p);
438: return false;
439: }
440:
441: if (p->Package.Count != 2) {
442: aprint_error_dev(self, "_GPE package does not contain 2 elements\n");
443: AcpiOsFree(p);
444: return false;
1.1 thorpej 445: }
446:
1.44 jmcneill 447: c = &p->Package.Elements[0];
448: switch (c->Type) {
449: case ACPI_TYPE_LOCAL_REFERENCE:
450: case ACPI_TYPE_ANY:
451: *gpe_handle = c->Reference.Handle;
452: break;
453: case ACPI_TYPE_STRING:
454: /* XXX should be using real scope here */
455: rv = AcpiGetHandle(NULL, p->String.Pointer, gpe_handle);
456: if (rv != AE_OK) {
457: aprint_error_dev(self,
458: "_GPE device reference unresolvable\n");
459: AcpiOsFree(p);
460: return false;
461: }
462: break;
463: default:
464: aprint_error_dev(self, "_GPE device reference incorrect\n");
465: AcpiOsFree(p);
466: return false;
467: }
468: c = &p->Package.Elements[1];
469: if (c->Type != ACPI_TYPE_INTEGER) {
470: aprint_error_dev(self,
471: "_GPE package needs integer as 2nd field\n");
472: AcpiOsFree(p);
473: return false;
474: }
475: *gpebit = c->Integer.Value;
476: AcpiOsFree(p);
477: return true;
478: }
1.23 kochi 479:
1.44 jmcneill 480: static uint8_t
481: acpiec_read_data(struct acpiec_softc *sc)
482: {
483: return bus_space_read_1(sc->sc_data_st, sc->sc_data_sh, 0);
484: }
1.17 mycroft 485:
1.44 jmcneill 486: static void
487: acpiec_write_data(struct acpiec_softc *sc, uint8_t val)
488: {
489: bus_space_write_1(sc->sc_data_st, sc->sc_data_sh, 0, val);
490: }
1.1 thorpej 491:
1.44 jmcneill 492: static uint8_t
493: acpiec_read_status(struct acpiec_softc *sc)
494: {
495: return bus_space_read_1(sc->sc_csr_st, sc->sc_csr_sh, 0);
496: }
1.33 kochi 497:
1.44 jmcneill 498: static void
499: acpiec_write_command(struct acpiec_softc *sc, uint8_t cmd)
500: {
501: bus_space_write_1(sc->sc_csr_st, sc->sc_csr_sh, 0, cmd);
502: }
1.33 kochi 503:
1.44 jmcneill 504: static ACPI_STATUS
505: acpiec_space_setup(ACPI_HANDLE region, UINT32 func, void *arg,
506: void **region_arg)
507: {
508: if (func == ACPI_REGION_DEACTIVATE)
509: *region_arg = NULL;
510: else
511: *region_arg = arg;
1.1 thorpej 512:
1.44 jmcneill 513: return AE_OK;
1.1 thorpej 514: }
515:
516: static void
1.44 jmcneill 517: acpiec_lock(device_t dv)
1.1 thorpej 518: {
1.44 jmcneill 519: struct acpiec_softc *sc = device_private(dv);
1.25 kochi 520: ACPI_STATUS rv;
1.1 thorpej 521:
1.44 jmcneill 522: mutex_enter(&sc->sc_access_mtx);
1.1 thorpej 523:
1.44 jmcneill 524: if (sc->sc_need_global_lock) {
525: rv = AcpiAcquireGlobalLock(EC_LOCK_TIMEOUT, &sc->sc_global_lock);
526: if (rv != AE_OK) {
527: aprint_error_dev(dv, "failed to acquire global lock: %s\n",
528: AcpiFormatException(rv));
529: return;
1.1 thorpej 530: }
1.44 jmcneill 531: }
532: }
533:
534: static void
535: acpiec_unlock(device_t dv)
536: {
537: struct acpiec_softc *sc = device_private(dv);
538: ACPI_STATUS rv;
1.1 thorpej 539:
1.44 jmcneill 540: if (sc->sc_need_global_lock) {
541: rv = AcpiReleaseGlobalLock(sc->sc_global_lock);
542: if (rv != AE_OK) {
543: aprint_error_dev(dv, "failed to release global lock: %s\n",
1.25 kochi 544: AcpiFormatException(rv));
1.1 thorpej 545: }
546: }
1.44 jmcneill 547: mutex_exit(&sc->sc_access_mtx);
548: }
549:
550: static ACPI_STATUS
551: acpiec_read(device_t dv, uint8_t addr, uint8_t *val)
552: {
553: struct acpiec_softc *sc = device_private(dv);
1.50 jmcneill 554: int i, timeo = 1000 * EC_CMD_TIMEOUT;
1.1 thorpej 555:
1.44 jmcneill 556: acpiec_lock(dv);
557: mutex_enter(&sc->sc_mtx);
1.1 thorpej 558:
1.44 jmcneill 559: sc->sc_cur_addr = addr;
560: sc->sc_state = EC_STATE_READ;
1.1 thorpej 561:
1.44 jmcneill 562: for (i = 0; i < EC_POLL_TIMEOUT; ++i) {
1.45 jmcneill 563: acpiec_gpe_state_machine(dv);
1.44 jmcneill 564: if (sc->sc_state == EC_STATE_FREE)
565: goto done;
566: delay(1);
567: }
1.1 thorpej 568:
1.44 jmcneill 569: if (cold || acpiec_cold) {
1.49 jmcneill 570: while (sc->sc_state != EC_STATE_FREE && timeo-- > 0) {
1.50 jmcneill 571: delay(1000);
1.45 jmcneill 572: acpiec_gpe_state_machine(dv);
1.1 thorpej 573: }
1.49 jmcneill 574: if (sc->sc_state != EC_STATE_FREE) {
575: mutex_exit(&sc->sc_mtx);
576: AcpiClearGpe(sc->sc_gpeh, sc->sc_gpebit, ACPI_NOT_ISR);
577: acpiec_unlock(dv);
578: aprint_error_dev(dv, "command timed out, state %d\n",
579: sc->sc_state);
580: return AE_ERROR;
581: }
1.51.4.1 yamt 582: } else if (cv_timedwait(&sc->sc_cv, &sc->sc_mtx, EC_CMD_TIMEOUT * hz)) {
1.44 jmcneill 583: mutex_exit(&sc->sc_mtx);
584: AcpiClearGpe(sc->sc_gpeh, sc->sc_gpebit, ACPI_NOT_ISR);
585: acpiec_unlock(dv);
1.46 joerg 586: aprint_error_dev(dv, "command takes over %d sec...\n", EC_CMD_TIMEOUT);
1.44 jmcneill 587: return AE_ERROR;
1.1 thorpej 588: }
1.33 kochi 589:
1.44 jmcneill 590: done:
591: *val = sc->sc_cur_val;
592:
593: mutex_exit(&sc->sc_mtx);
594: acpiec_unlock(dv);
595: return AE_OK;
1.1 thorpej 596: }
597:
598: static ACPI_STATUS
1.44 jmcneill 599: acpiec_write(device_t dv, uint8_t addr, uint8_t val)
1.1 thorpej 600: {
1.44 jmcneill 601: struct acpiec_softc *sc = device_private(dv);
1.50 jmcneill 602: int i, timeo = 1000 * EC_CMD_TIMEOUT;
1.1 thorpej 603:
1.44 jmcneill 604: acpiec_lock(dv);
605: mutex_enter(&sc->sc_mtx);
1.1 thorpej 606:
1.44 jmcneill 607: sc->sc_cur_addr = addr;
608: sc->sc_cur_val = val;
609: sc->sc_state = EC_STATE_WRITE;
610:
611: for (i = 0; i < EC_POLL_TIMEOUT; ++i) {
1.45 jmcneill 612: acpiec_gpe_state_machine(dv);
1.44 jmcneill 613: if (sc->sc_state == EC_STATE_FREE)
614: goto done;
615: delay(1);
616: }
617:
618: if (cold || acpiec_cold) {
1.49 jmcneill 619: while (sc->sc_state != EC_STATE_FREE && timeo-- > 0) {
1.50 jmcneill 620: delay(1000);
1.45 jmcneill 621: acpiec_gpe_state_machine(dv);
1.44 jmcneill 622: }
1.49 jmcneill 623: if (sc->sc_state != EC_STATE_FREE) {
624: mutex_exit(&sc->sc_mtx);
625: AcpiClearGpe(sc->sc_gpeh, sc->sc_gpebit, ACPI_NOT_ISR);
626: acpiec_unlock(dv);
627: aprint_error_dev(dv, "command timed out, state %d\n",
628: sc->sc_state);
629: return AE_ERROR;
630: }
1.51.4.1 yamt 631: } else if (cv_timedwait(&sc->sc_cv, &sc->sc_mtx, EC_CMD_TIMEOUT * hz)) {
1.44 jmcneill 632: mutex_exit(&sc->sc_mtx);
633: AcpiClearGpe(sc->sc_gpeh, sc->sc_gpebit, ACPI_NOT_ISR);
634: acpiec_unlock(dv);
1.46 joerg 635: aprint_error_dev(dv, "command takes over %d sec...\n", EC_CMD_TIMEOUT);
1.44 jmcneill 636: return AE_ERROR;
637: }
1.1 thorpej 638:
1.44 jmcneill 639: done:
640: mutex_exit(&sc->sc_mtx);
641: acpiec_unlock(dv);
642: return AE_OK;
1.1 thorpej 643: }
644:
645: static ACPI_STATUS
1.44 jmcneill 646: acpiec_space_handler(UINT32 func, ACPI_PHYSICAL_ADDRESS paddr,
647: UINT32 width, ACPI_INTEGER *value, void *arg, void *region_arg)
1.1 thorpej 648: {
1.44 jmcneill 649: device_t dv;
650: struct acpiec_softc *sc;
651: ACPI_STATUS rv;
652: uint8_t addr, reg;
653: unsigned int i;
654:
655: if (paddr > 0xff || width % 8 != 0 || value == NULL || arg == NULL ||
656: paddr + width / 8 > 0xff)
657: return AE_BAD_PARAMETER;
1.1 thorpej 658:
1.44 jmcneill 659: addr = paddr;
660: dv = arg;
661: sc = device_private(dv);
1.1 thorpej 662:
1.44 jmcneill 663: rv = AE_OK;
1.1 thorpej 664:
1.44 jmcneill 665: switch (func) {
1.4 thorpej 666: case ACPI_READ:
1.44 jmcneill 667: *value = 0;
668: for (i = 0; i < width; i += 8, ++addr) {
669: rv = acpiec_read(dv, addr, ®);
670: if (rv != AE_OK)
671: break;
672: *value |= (ACPI_INTEGER)reg << i;
673: }
1.1 thorpej 674: break;
1.4 thorpej 675: case ACPI_WRITE:
1.44 jmcneill 676: for (i = 0; i < width; i += 8, ++addr) {
677: reg = (*value >>i) & 0xff;
678: rv = acpiec_write(dv, addr, reg);
679: if (rv != AE_OK)
680: break;
681: }
1.1 thorpej 682: break;
683: default:
1.44 jmcneill 684: aprint_error("%s: invalid Address Space function called: %x\n",
685: device_xname(dv), (unsigned int)func);
686: return AE_BAD_PARAMETER;
1.1 thorpej 687: }
688:
1.44 jmcneill 689: return rv;
1.1 thorpej 690: }
691:
1.44 jmcneill 692: static void
693: acpiec_gpe_query(void *arg)
1.1 thorpej 694: {
1.44 jmcneill 695: device_t dv = arg;
696: struct acpiec_softc *sc = device_private(dv);
697: uint8_t reg;
698: char qxx[5];
699: ACPI_STATUS rv;
1.1 thorpej 700: int i;
701:
1.44 jmcneill 702: loop:
703: mutex_enter(&sc->sc_mtx);
704:
705: if (sc->sc_got_sci == false)
706: cv_wait(&sc->sc_cv_sci, &sc->sc_mtx);
707: mutex_exit(&sc->sc_mtx);
708:
709: acpiec_lock(dv);
710: mutex_enter(&sc->sc_mtx);
711:
712: /* The Query command can always be issued, so be defensive here. */
713: sc->sc_got_sci = false;
714: sc->sc_state = EC_STATE_QUERY;
1.1 thorpej 715:
1.44 jmcneill 716: for (i = 0; i < EC_POLL_TIMEOUT; ++i) {
1.45 jmcneill 717: acpiec_gpe_state_machine(dv);
1.44 jmcneill 718: if (sc->sc_state == EC_STATE_FREE)
719: goto done;
720: delay(1);
1.1 thorpej 721: }
722:
1.44 jmcneill 723: cv_wait(&sc->sc_cv, &sc->sc_mtx);
724:
725: done:
726: reg = sc->sc_cur_val;
1.1 thorpej 727:
1.44 jmcneill 728: mutex_exit(&sc->sc_mtx);
729: acpiec_unlock(dv);
1.1 thorpej 730:
1.44 jmcneill 731: if (reg == 0)
732: goto loop; /* Spurious query result */
1.1 thorpej 733:
734: /*
1.44 jmcneill 735: * Evaluate _Qxx to respond to the controller.
1.1 thorpej 736: */
1.44 jmcneill 737: snprintf(qxx, sizeof(qxx), "_Q%02X", (unsigned int)reg);
738: rv = AcpiEvaluateObject(sc->sc_ech, qxx, NULL, NULL);
739: if (rv != AE_OK && rv != AE_NOT_FOUND) {
740: aprint_error("%s: GPE query method %s failed: %s",
741: device_xname(dv), qxx, AcpiFormatException(rv));
1.1 thorpej 742: }
743:
1.44 jmcneill 744: goto loop;
1.24 kochi 745: }
1.1 thorpej 746:
1.44 jmcneill 747: static void
1.45 jmcneill 748: acpiec_gpe_state_machine(device_t dv)
1.1 thorpej 749: {
1.44 jmcneill 750: struct acpiec_softc *sc = device_private(dv);
751: uint8_t reg;
1.1 thorpej 752:
1.44 jmcneill 753: reg = acpiec_read_status(sc);
1.1 thorpej 754:
1.44 jmcneill 755: if (reg & EC_STATUS_SCI)
756: sc->sc_got_sci = true;
757:
758: switch (sc->sc_state) {
759: case EC_STATE_QUERY:
760: if ((reg & EC_STATUS_IBF) != 0)
761: break; /* Nothing of interest here. */
762: acpiec_write_command(sc, EC_COMMAND_QUERY);
763: sc->sc_state = EC_STATE_QUERY_VAL;
764: break;
1.1 thorpej 765:
1.44 jmcneill 766: case EC_STATE_QUERY_VAL:
767: if ((reg & EC_STATUS_OBF) == 0)
768: break; /* Nothing of interest here. */
1.1 thorpej 769:
1.44 jmcneill 770: sc->sc_cur_val = acpiec_read_data(sc);
771: sc->sc_state = EC_STATE_FREE;
1.1 thorpej 772:
1.44 jmcneill 773: cv_signal(&sc->sc_cv);
1.1 thorpej 774: break;
775:
1.44 jmcneill 776: case EC_STATE_READ:
777: if ((reg & EC_STATUS_IBF) != 0)
778: break; /* Nothing of interest here. */
1.1 thorpej 779:
1.44 jmcneill 780: acpiec_write_command(sc, EC_COMMAND_READ);
781: sc->sc_state = EC_STATE_READ_ADDR;
1.1 thorpej 782: break;
783:
1.44 jmcneill 784: case EC_STATE_READ_ADDR:
785: if ((reg & EC_STATUS_IBF) != 0)
786: break; /* Nothing of interest here. */
1.1 thorpej 787:
1.44 jmcneill 788: acpiec_write_data(sc, sc->sc_cur_addr);
789: sc->sc_state = EC_STATE_READ_VAL;
790: break;
1.1 thorpej 791:
1.44 jmcneill 792: case EC_STATE_READ_VAL:
793: if ((reg & EC_STATUS_OBF) == 0)
794: break; /* Nothing of interest here. */
795: sc->sc_cur_val = acpiec_read_data(sc);
796: sc->sc_state = EC_STATE_FREE;
1.1 thorpej 797:
1.44 jmcneill 798: cv_signal(&sc->sc_cv);
799: break;
1.1 thorpej 800:
1.44 jmcneill 801: case EC_STATE_WRITE:
802: if ((reg & EC_STATUS_IBF) != 0)
803: break; /* Nothing of interest here. */
1.1 thorpej 804:
1.44 jmcneill 805: acpiec_write_command(sc, EC_COMMAND_WRITE);
806: sc->sc_state = EC_STATE_WRITE_ADDR;
807: break;
1.1 thorpej 808:
1.44 jmcneill 809: case EC_STATE_WRITE_ADDR:
810: if ((reg & EC_STATUS_IBF) != 0)
811: break; /* Nothing of interest here. */
812: acpiec_write_data(sc, sc->sc_cur_addr);
813: sc->sc_state = EC_STATE_WRITE_VAL;
814: break;
1.1 thorpej 815:
1.44 jmcneill 816: case EC_STATE_WRITE_VAL:
817: if ((reg & EC_STATUS_IBF) != 0)
818: break; /* Nothing of interest here. */
819: sc->sc_state = EC_STATE_FREE;
820: cv_signal(&sc->sc_cv);
1.1 thorpej 821:
1.44 jmcneill 822: acpiec_write_data(sc, sc->sc_cur_val);
823: break;
1.1 thorpej 824:
1.44 jmcneill 825: case EC_STATE_FREE:
826: if (sc->sc_got_sci)
827: cv_signal(&sc->sc_cv_sci);
828: break;
829: default:
830: panic("invalid state");
831: }
1.46 joerg 832:
833: if (sc->sc_state != EC_STATE_FREE)
834: callout_schedule(&sc->sc_pseudo_intr, 1);
835: }
836:
837: static void
838: acpiec_callout(void *arg)
839: {
840: device_t dv = arg;
841: struct acpiec_softc *sc = device_private(dv);
842:
1.47 joerg 843: AcpiClearGpe(sc->sc_gpeh, sc->sc_gpebit, ACPI_NOT_ISR);
844:
1.46 joerg 845: mutex_enter(&sc->sc_mtx);
846: acpiec_gpe_state_machine(dv);
847: mutex_exit(&sc->sc_mtx);
1.24 kochi 848: }
1.1 thorpej 849:
1.44 jmcneill 850: static UINT32
851: acpiec_gpe_handler(void *arg)
1.1 thorpej 852: {
1.44 jmcneill 853: device_t dv = arg;
854: struct acpiec_softc *sc = device_private(dv);
1.1 thorpej 855:
1.44 jmcneill 856: AcpiClearGpe(sc->sc_gpeh, sc->sc_gpebit, ACPI_ISR);
1.1 thorpej 857:
1.44 jmcneill 858: mutex_enter(&sc->sc_mtx);
1.45 jmcneill 859: acpiec_gpe_state_machine(dv);
1.44 jmcneill 860: mutex_exit(&sc->sc_mtx);
1.1 thorpej 861:
1.44 jmcneill 862: return 0;
1.1 thorpej 863: }
1.48 jmcneill 864:
865: ACPI_STATUS
866: acpiec_bus_read(device_t dv, u_int addr, ACPI_INTEGER *val, int width)
867: {
868: return acpiec_space_handler(ACPI_READ, addr, width * 8, val, dv, NULL);
869: }
870:
871: ACPI_STATUS
872: acpiec_bus_write(device_t dv, u_int addr, ACPI_INTEGER val, int width)
873: {
874: return acpiec_space_handler(ACPI_WRITE, addr, width * 8, &val, dv, NULL);
875: }
876:
877: ACPI_HANDLE
878: acpiec_get_handle(device_t dv)
879: {
880: struct acpiec_softc *sc = device_private(dv);
881:
882: return sc->sc_ech;
883: }
CVSweb <webmaster@jp.NetBSD.org>