Annotation of src/sys/dev/acpi/acpi_ec.c, Revision 1.41.6.12
1.41.6.12! joerg 1: /* $NetBSD: acpi_ec.c,v 1.41.6.11 2007/10/26 15:44:12 joerg Exp $ */
1.1 thorpej 2:
3: /*-
1.41.6.5 joerg 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:
1.41.6.5 joerg 10: *
1.1 thorpej 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.41.6.5 joerg 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.3 lukem 32: #include <sys/cdefs.h>
1.41.6.12! joerg 33: __KERNEL_RCSID(0, "$NetBSD: acpi_ec.c,v 1.41.6.11 2007/10/26 15:44:12 joerg Exp $");
1.1 thorpej 34:
35: #include <sys/param.h>
36: #include <sys/systm.h>
1.41.6.5 joerg 37: #include <sys/condvar.h>
1.1 thorpej 38: #include <sys/device.h>
39: #include <sys/kernel.h>
1.41.6.5 joerg 40: #include <sys/kthread.h>
41: #include <sys/mutex.h>
1.1 thorpej 42:
1.41.6.11 joerg 43: #include <sys/bus.h>
1.1 thorpej 44:
45: #include <dev/acpi/acpivar.h>
46:
1.41.6.7 joerg 47: /* Maximum time to wait for global ACPI lock in ms */
1.41.6.5 joerg 48: #define EC_LOCK_TIMEOUT 5
1.23 kochi 49:
1.41.6.7 joerg 50: /* Maximum time to poll for completion of a command in ms */
51: #define EC_POLL_TIMEOUT 5
52:
1.41.6.5 joerg 53: /* From ACPI 3.0b, chapter 12.3 */
54: #define EC_COMMAND_READ 0x80
55: #define EC_COMMAND_WRITE 0x81
56: #define EC_COMMAND_BURST_EN 0x82
57: #define EC_COMMAND_BURST_DIS 0x83
58: #define EC_COMMAND_QUERY 0x84
59:
60: /* From ACPI 3.0b, chapter 12.2.1 */
61: #define EC_STATUS_OBF 0x01
62: #define EC_STATUS_IBF 0x02
63: #define EC_STATUS_CMD 0x08
64: #define EC_STATUS_BURST 0x10
65: #define EC_STATUS_SCI 0x20
66: #define EC_STATUS_SMI 0x40
1.1 thorpej 67:
1.41.6.5 joerg 68: static const char *ec_hid[] = {
69: "PNP0C09",
70: NULL,
71: };
1.1 thorpej 72:
1.41.6.5 joerg 73: enum ec_state_t {
74: EC_STATE_QUERY,
1.41.6.7 joerg 75: EC_STATE_QUERY_VAL,
1.41.6.5 joerg 76: EC_STATE_READ,
1.41.6.7 joerg 77: EC_STATE_READ_ADDR,
1.41.6.5 joerg 78: EC_STATE_READ_VAL,
79: EC_STATE_WRITE,
1.41.6.7 joerg 80: EC_STATE_WRITE_ADDR,
1.41.6.5 joerg 81: EC_STATE_WRITE_VAL,
82: EC_STATE_FREE
83: };
1.1 thorpej 84:
1.41.6.5 joerg 85: struct acpiec_softc {
86: ACPI_HANDLE sc_ech;
1.1 thorpej 87:
1.41.6.5 joerg 88: ACPI_HANDLE sc_gpeh;
89: UINT8 sc_gpebit;
1.1 thorpej 90:
1.41.6.5 joerg 91: bus_space_tag_t sc_data_st;
92: bus_space_handle_t sc_data_sh;
1.1 thorpej 93:
1.41.6.5 joerg 94: bus_space_tag_t sc_csr_st;
95: bus_space_handle_t sc_csr_sh;
1.17 mycroft 96:
1.41.6.5 joerg 97: bool sc_need_global_lock;
98: UINT32 sc_global_lock;
1.1 thorpej 99:
1.41.6.5 joerg 100: kmutex_t sc_mtx, sc_access_mtx;
101: kcondvar_t sc_cv, sc_cv_sci;
102: enum ec_state_t sc_state;
103: bool sc_got_sci;
104:
105: uint8_t sc_cur_addr, sc_cur_val;
1.16 kochi 106: };
107:
1.41.6.5 joerg 108: static int acpiecdt_match(device_t, struct cfdata *, void *);
109: static void acpiecdt_attach(device_t, device_t, void *);
1.1 thorpej 110:
1.41.6.5 joerg 111: static int acpiec_match(device_t, struct cfdata *, void *);
112: static void acpiec_attach(device_t, device_t, void *);
113:
114: static void acpiec_common_attach(device_t, device_t, ACPI_HANDLE,
115: bus_addr_t, bus_addr_t, ACPI_HANDLE, uint8_t);
116:
1.41.6.12! joerg 117: static bool acpiec_resume(device_t);
! 118: static bool acpiec_suspend(device_t);
1.17 mycroft 119:
1.41.6.5 joerg 120: static bool acpiec_parse_gpe_package(device_t, ACPI_HANDLE,
121: ACPI_HANDLE *, uint8_t *);
1.20 yamt 122:
1.41.6.5 joerg 123: static void acpiec_gpe_query(void *);
124: static UINT32 acpiec_gpe_handler(void *);
125: static ACPI_STATUS acpiec_space_setup(ACPI_HANDLE, UINT32, void *, void **);
126: static ACPI_STATUS acpiec_space_handler(UINT32, ACPI_PHYSICAL_ADDRESS,
127: UINT32, ACPI_INTEGER *, void *, void *);
1.20 yamt 128:
1.41.6.5 joerg 129: static void acpiec_gpe_state_maschine(device_t);
1.20 yamt 130:
1.41.6.5 joerg 131: CFATTACH_DECL_NEW(acpiec, sizeof(struct acpiec_softc),
1.20 yamt 132: acpiec_match, acpiec_attach, NULL, NULL);
133:
1.41.6.5 joerg 134: CFATTACH_DECL_NEW(acpiecdt, sizeof(struct acpiec_softc),
135: acpiecdt_match, acpiecdt_attach, NULL, NULL);
1.23 kochi 136:
1.41.6.5 joerg 137: static device_t ec_singleton = NULL;
138: static bool acpiec_cold = false;
139:
140: static bool
141: acpiecdt_find(device_t parent, ACPI_HANDLE *ec_handle,
142: bus_addr_t *cmd_reg, bus_addr_t *data_reg, uint8_t *gpebit)
1.9 tshiozak 143: {
1.41.6.6 jmcneill 144: ACPI_TABLE_ECDT *ecdt;
1.41.6.5 joerg 145: ACPI_STATUS rv;
146:
1.41.6.6 jmcneill 147: rv = AcpiGetTable(ACPI_SIG_ECDT, 1, (ACPI_TABLE_HEADER **)&ecdt);
148: if (ACPI_FAILURE(rv))
1.41.6.5 joerg 149: return false;
150:
1.41.6.6 jmcneill 151: if (ecdt->Control.BitWidth != 8 || ecdt->Data.BitWidth != 8) {
1.41.6.5 joerg 152: aprint_error_dev(parent,
153: "ECDT register width invalid (%d/%d)\n",
1.41.6.6 jmcneill 154: ecdt->Control.BitWidth, ecdt->Data.BitWidth);
1.41.6.5 joerg 155: return false;
156: }
157:
1.41.6.6 jmcneill 158: rv = AcpiGetHandle(ACPI_ROOT_OBJECT, ecdt->Id, ec_handle);
159: if (ACPI_FAILURE(rv)) {
1.41.6.5 joerg 160: aprint_error_dev(parent,
161: "failed to look up EC object %s: %s\n",
1.41.6.6 jmcneill 162: ecdt->Id, AcpiFormatException(rv));
1.41.6.5 joerg 163: return false;
164: }
165:
1.41.6.6 jmcneill 166: *cmd_reg = ecdt->Control.Address;
167: *data_reg = ecdt->Data.Address;
168: *gpebit = ecdt->Gpe;
1.24 kochi 169:
1.41.6.5 joerg 170: return true;
1.9 tshiozak 171: }
1.4 thorpej 172:
1.41.6.5 joerg 173: static int
174: acpiecdt_match(device_t parent, struct cfdata *match, void *aux)
1.1 thorpej 175: {
1.41.6.5 joerg 176: ACPI_HANDLE ec_handle;
177: bus_addr_t cmd_reg, data_reg;
178: uint8_t gpebit;
1.20 yamt 179:
1.41.6.5 joerg 180: if (acpiecdt_find(parent, &ec_handle, &cmd_reg, &data_reg, &gpebit))
181: return 1;
182: else
183: return 0;
1.1 thorpej 184: }
185:
1.41.6.5 joerg 186: static void
187: acpiecdt_attach(device_t parent, device_t self, void *aux)
1.1 thorpej 188: {
1.41.6.5 joerg 189: ACPI_HANDLE ec_handle;
190: bus_addr_t cmd_reg, data_reg;
191: uint8_t gpebit;
1.20 yamt 192:
1.41.6.5 joerg 193: if (!acpiecdt_find(parent, &ec_handle, &cmd_reg, &data_reg, &gpebit))
194: panic("ECDT disappeared");
195:
196: aprint_naive(": ACPI Embedded Controller via ECDT\n");
197: aprint_normal(": ACPI Embedded Controller via ECDT\n");
198:
199: acpiec_common_attach(parent, self, ec_handle, cmd_reg, data_reg,
200: NULL, gpebit);
1.1 thorpej 201: }
202:
1.31 kochi 203: static int
1.41.6.5 joerg 204: acpiec_match(device_t parent, struct cfdata *match, void *aux)
1.1 thorpej 205: {
206: struct acpi_attach_args *aa = aux;
207:
208: if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
1.25 kochi 209: return 0;
1.1 thorpej 210:
1.25 kochi 211: return acpi_match_hid(aa->aa_node->ad_devinfo, ec_hid);
1.1 thorpej 212: }
213:
1.41.6.5 joerg 214: static void
215: acpiec_attach(device_t parent, device_t self, void *aux)
1.17 mycroft 216: {
1.41.6.5 joerg 217: struct acpi_attach_args *aa = aux;
218: struct acpi_resources ec_res;
219: struct acpi_io *io0, *io1;
220: ACPI_HANDLE gpe_handle;
221: uint8_t gpebit;
1.23 kochi 222: ACPI_STATUS rv;
1.17 mycroft 223:
1.41.6.5 joerg 224: if (ec_singleton != NULL) {
225: aprint_naive(": ACPI Embedded Controller (disabled)\n");
226: aprint_normal(": ACPI Embedded Controller (disabled)\n");
1.41.6.12! joerg 227: if (!pnp_device_register(self, NULL, NULL))
! 228: aprint_error_dev(self, "couldn't establish power handler\n");
1.23 kochi 229: return;
230: }
231:
1.41.6.5 joerg 232: aprint_naive(": ACPI Embedded Controller\n");
233: aprint_normal(": ACPI Embedded Controller\n");
1.23 kochi 234:
1.41.6.5 joerg 235: if (!acpiec_parse_gpe_package(self, aa->aa_node->ad_handle,
236: &gpe_handle, &gpebit))
237: return;
1.23 kochi 238:
1.41.6.5 joerg 239: rv = acpi_resource_parse(self, aa->aa_node->ad_handle, "_CRS",
240: &ec_res, &acpi_resource_parse_ops_default);
241: if (rv != AE_OK) {
242: aprint_error_dev(self, "resource parsing failed: %s\n",
1.23 kochi 243: AcpiFormatException(rv));
1.41.6.5 joerg 244: return;
1.17 mycroft 245: }
1.23 kochi 246:
1.41.6.5 joerg 247: if ((io0 = acpi_res_io(&ec_res, 0)) == NULL) {
248: aprint_error_dev(self, "no data register resource\n");
249: goto free_res;
250: }
251: if ((io1 = acpi_res_io(&ec_res, 1)) == NULL) {
252: aprint_error_dev(self, "no CSR register resource\n");
253: goto free_res;
254: }
1.23 kochi 255:
1.41.6.5 joerg 256: acpiec_common_attach(parent, self, aa->aa_node->ad_handle,
257: io1->ar_base, io0->ar_base, gpe_handle, gpebit);
1.23 kochi 258:
1.41.6.5 joerg 259: free_res:
260: acpi_resource_cleanup(&ec_res);
1.17 mycroft 261: }
262:
1.31 kochi 263: static void
1.41.6.5 joerg 264: acpiec_common_attach(device_t parent, device_t self,
265: ACPI_HANDLE ec_handle, bus_addr_t cmd_reg, bus_addr_t data_reg,
266: ACPI_HANDLE gpe_handle, uint8_t gpebit)
1.1 thorpej 267: {
1.41.6.5 joerg 268: struct acpiec_softc *sc = device_private(self);
1.1 thorpej 269: ACPI_STATUS rv;
1.41.6.5 joerg 270: ACPI_INTEGER val;
1.1 thorpej 271:
1.41.6.5 joerg 272: sc->sc_ech = ec_handle;
273: sc->sc_gpeh = gpe_handle;
274: sc->sc_gpebit = gpebit;
275:
276: sc->sc_state = EC_STATE_FREE;
277: mutex_init(&sc->sc_mtx, MUTEX_DRIVER, IPL_TTY);
278: mutex_init(&sc->sc_access_mtx, MUTEX_DEFAULT, IPL_NONE);
279: cv_init(&sc->sc_cv, "eccv");
280: cv_init(&sc->sc_cv_sci, "ecsci");
281:
282: if (bus_space_map(sc->sc_data_st, data_reg, 1, 0,
283: &sc->sc_data_sh) != 0) {
284: aprint_error_dev(self, "unable to map data register\n");
285: return;
286: }
1.1 thorpej 287:
1.41.6.5 joerg 288: if (bus_space_map(sc->sc_csr_st, cmd_reg, 1, 0, &sc->sc_csr_sh) != 0) {
289: aprint_error_dev(self, "unable to map CSR register\n");
290: goto post_data_map;
291: }
1.17 mycroft 292:
1.41.6.5 joerg 293: rv = acpi_eval_integer(sc->sc_ech, "_GLK", &val);
294: if (rv == AE_OK) {
295: sc->sc_need_global_lock = val != 0;
296: } else if (rv != AE_NOT_FOUND) {
297: aprint_error_dev(self, "unable to evaluate _GLK: %s\n",
298: AcpiFormatException(rv));
299: goto post_csr_map;
300: } else {
301: sc->sc_need_global_lock = false;
302: }
303: if (sc->sc_need_global_lock)
304: aprint_normal_dev(self, "using global ACPI lock\n");
1.1 thorpej 305:
1.41.6.5 joerg 306: rv = AcpiInstallAddressSpaceHandler(sc->sc_ech, ACPI_ADR_SPACE_EC,
307: acpiec_space_handler, acpiec_space_setup, self);
308: if (rv != AE_OK) {
309: aprint_error_dev(self,
310: "unable to install address space handler: %s\n",
311: AcpiFormatException(rv));
312: goto post_csr_map;
313: }
1.1 thorpej 314:
1.41.6.5 joerg 315: rv = AcpiInstallGpeHandler(sc->sc_gpeh, sc->sc_gpebit,
316: ACPI_GPE_EDGE_TRIGGERED, acpiec_gpe_handler, self);
317: if (rv != AE_OK) {
318: aprint_error_dev(self, "unable to install GPE handler: %s\n",
319: AcpiFormatException(rv));
320: goto post_csr_map;
321: }
1.23 kochi 322:
1.41.6.5 joerg 323: rv = AcpiSetGpeType(sc->sc_gpeh, sc->sc_gpebit, ACPI_GPE_TYPE_RUNTIME);
324: if (rv != AE_OK) {
325: aprint_error_dev(self, "unable to set GPE type: %s\n",
326: AcpiFormatException(rv));
327: goto post_csr_map;
1.1 thorpej 328: }
329:
1.41.6.5 joerg 330: rv = AcpiEnableGpe(sc->sc_gpeh, sc->sc_gpebit, ACPI_ISR);
331: if (rv != AE_OK) {
332: aprint_error_dev(self, "unable to enable GPE: %s\n",
333: AcpiFormatException(rv));
334: goto post_csr_map;
335: }
1.23 kochi 336:
1.41.6.5 joerg 337: if (kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, acpiec_gpe_query,
338: self, NULL, "acpiec sci thread")) {
339: aprint_error_dev(self, "unable to create query kthread\n");
340: goto post_csr_map;
1.17 mycroft 341: }
342:
1.41.6.5 joerg 343: ec_singleton = self;
1.1 thorpej 344:
1.41.6.12! joerg 345: if (!pnp_device_register(self, acpiec_suspend, acpiec_resume))
! 346: aprint_error_dev(self, "couldn't establish power handler\n");
1.1 thorpej 347:
1.41.6.5 joerg 348: return;
1.1 thorpej 349:
1.41.6.5 joerg 350: post_csr_map:
351: (void)AcpiRemoveGpeHandler(sc->sc_gpeh, sc->sc_gpebit,
352: acpiec_gpe_handler);
353: (void)AcpiRemoveAddressSpaceHandler(sc->sc_ech,
354: ACPI_ADR_SPACE_EC, acpiec_space_handler);
355: bus_space_unmap(sc->sc_csr_st, sc->sc_csr_sh, 1);
356: post_data_map:
357: bus_space_unmap(sc->sc_data_st, sc->sc_data_sh, 1);
358: }
359:
1.41.6.12! joerg 360: static bool
! 361: acpiec_suspend(device_t dv)
1.41.6.5 joerg 362: {
1.41.6.12! joerg 363: acpiec_cold = true;
1.41.6.5 joerg 364:
1.41.6.12! joerg 365: return true;
! 366: }
1.1 thorpej 367:
1.41.6.12! joerg 368: static bool
! 369: acpiec_resume(device_t dv)
! 370: {
! 371: acpiec_cold = false;
! 372:
! 373: return true;
1.1 thorpej 374: }
375:
1.41.6.5 joerg 376: static bool
377: acpiec_parse_gpe_package(device_t self, ACPI_HANDLE ec_handle,
378: ACPI_HANDLE *gpe_handle, uint8_t *gpebit)
1.1 thorpej 379: {
1.41.6.5 joerg 380: ACPI_BUFFER buf;
381: ACPI_OBJECT *p, *c;
1.25 kochi 382: ACPI_STATUS rv;
1.1 thorpej 383:
1.41.6.5 joerg 384: rv = acpi_eval_struct(ec_handle, "_GPE", &buf);
385: if (rv != AE_OK) {
386: aprint_error_dev(self, "unable to evaluate _GPE: %s\n",
387: AcpiFormatException(rv));
388: return false;
389: }
390:
391: p = buf.Pointer;
392:
393: if (p->Type == ACPI_TYPE_INTEGER) {
394: *gpe_handle = NULL;
395: *gpebit = p->Integer.Value;
396: AcpiOsFree(p);
397: return true;
398: }
399:
400: if (p->Type != ACPI_TYPE_PACKAGE) {
401: aprint_error_dev(self, "_GPE is neither integer nor package\n");
402: AcpiOsFree(p);
403: return false;
404: }
405:
406: if (p->Package.Count != 2) {
407: aprint_error_dev(self, "_GPE package does not contain 2 elements\n");
408: AcpiOsFree(p);
409: return false;
1.1 thorpej 410: }
1.33 kochi 411:
1.41.6.5 joerg 412: c = &p->Package.Elements[0];
413: switch (c->Type) {
414: case ACPI_TYPE_LOCAL_REFERENCE:
415: case ACPI_TYPE_ANY:
416: *gpe_handle = c->Reference.Handle;
417: break;
418: case ACPI_TYPE_STRING:
419: /* XXX should be using real scope here */
420: rv = AcpiGetHandle(NULL, p->String.Pointer, gpe_handle);
421: if (rv != AE_OK) {
422: aprint_error_dev(self,
423: "_GPE device reference unresolvable\n");
424: AcpiOsFree(p);
425: return false;
426: }
427: break;
428: default:
429: aprint_error_dev(self, "_GPE device reference incorrect\n");
430: AcpiOsFree(p);
431: return false;
432: }
433: c = &p->Package.Elements[1];
434: if (c->Type != ACPI_TYPE_INTEGER) {
435: aprint_error_dev(self,
436: "_GPE package needs integer as 2nd field\n");
437: AcpiOsFree(p);
438: return false;
439: }
440: *gpebit = c->Integer.Value;
441: AcpiOsFree(p);
442: return true;
1.1 thorpej 443: }
444:
1.41.6.5 joerg 445: static uint8_t
446: acpiec_read_data(struct acpiec_softc *sc)
1.1 thorpej 447: {
1.41.6.5 joerg 448: return bus_space_read_1(sc->sc_data_st, sc->sc_data_sh, 0);
449: }
1.1 thorpej 450:
1.41.6.5 joerg 451: static void
452: acpiec_write_data(struct acpiec_softc *sc, uint8_t val)
453: {
454: bus_space_write_1(sc->sc_data_st, sc->sc_data_sh, 0, val);
455: }
1.1 thorpej 456:
1.41.6.5 joerg 457: static uint8_t
458: acpiec_read_status(struct acpiec_softc *sc)
459: {
460: return bus_space_read_1(sc->sc_csr_st, sc->sc_csr_sh, 0);
461: }
1.1 thorpej 462:
1.41.6.5 joerg 463: static void
464: acpiec_write_command(struct acpiec_softc *sc, uint8_t cmd)
465: {
466: bus_space_write_1(sc->sc_csr_st, sc->sc_csr_sh, 0, cmd);
1.1 thorpej 467: }
468:
469: static ACPI_STATUS
1.41.6.5 joerg 470: acpiec_space_setup(ACPI_HANDLE region, UINT32 func, void *arg,
471: void **region_arg)
1.1 thorpej 472: {
1.41.6.5 joerg 473: if (func == ACPI_REGION_DEACTIVATE)
474: *region_arg = NULL;
475: else
476: *region_arg = arg;
1.1 thorpej 477:
1.41.6.5 joerg 478: return AE_OK;
479: }
1.1 thorpej 480:
1.41.6.5 joerg 481: static void
482: acpiec_lock(device_t dv)
483: {
484: struct acpiec_softc *sc = device_private(dv);
485: ACPI_STATUS rv;
1.1 thorpej 486:
1.41.6.5 joerg 487: mutex_enter(&sc->sc_access_mtx);
1.1 thorpej 488:
1.41.6.5 joerg 489: if (sc->sc_need_global_lock) {
490: rv = AcpiAcquireGlobalLock(EC_LOCK_TIMEOUT, &sc->sc_global_lock);
491: if (rv != AE_OK) {
1.41.6.10 joerg 492: aprint_error_dev(dv, "failed to acquire global lock: %s\n",
493: AcpiFormatException(rv));
1.41.6.5 joerg 494: return;
495: }
496: }
497: }
1.1 thorpej 498:
1.41.6.5 joerg 499: static void
500: acpiec_unlock(device_t dv)
501: {
502: struct acpiec_softc *sc = device_private(dv);
503: ACPI_STATUS rv;
504:
505: if (sc->sc_need_global_lock) {
506: rv = AcpiReleaseGlobalLock(sc->sc_global_lock);
507: if (rv != AE_OK) {
1.41.6.10 joerg 508: aprint_error_dev(dv, "failed to release global lock: %s\n",
509: AcpiFormatException(rv));
1.41.6.5 joerg 510: }
1.1 thorpej 511: }
1.41.6.5 joerg 512: mutex_exit(&sc->sc_access_mtx);
513: }
1.1 thorpej 514:
1.41.6.5 joerg 515: static ACPI_STATUS
516: acpiec_read(device_t dv, uint8_t addr, uint8_t *val)
517: {
518: struct acpiec_softc *sc = device_private(dv);
1.41.6.7 joerg 519: int i, timeouts = 0;
1.1 thorpej 520:
1.41.6.5 joerg 521: acpiec_lock(dv);
522: mutex_enter(&sc->sc_mtx);
1.1 thorpej 523:
1.41.6.5 joerg 524: retry:
525: sc->sc_cur_addr = addr;
526: sc->sc_state = EC_STATE_READ;
527:
1.41.6.7 joerg 528: for (i = 0; i < EC_POLL_TIMEOUT; ++i) {
529: acpiec_gpe_state_maschine(dv);
530: if (sc->sc_state == EC_STATE_FREE)
531: goto done;
532: delay(1);
533: }
534:
1.41.6.5 joerg 535: if (cold || acpiec_cold) {
1.41.6.7 joerg 536: while (sc->sc_state != EC_STATE_FREE) {
537: delay(1);
1.41.6.5 joerg 538: acpiec_gpe_state_maschine(dv);
1.41.6.7 joerg 539: }
1.41.6.5 joerg 540: } else while (cv_timedwait(&sc->sc_cv, &sc->sc_mtx, hz)) {
541: mutex_exit(&sc->sc_mtx);
542: AcpiClearGpe(sc->sc_gpeh, sc->sc_gpebit, ACPI_NOT_ISR);
543: mutex_enter(&sc->sc_mtx);
544: if (++timeouts < 5)
545: goto retry;
546: mutex_exit(&sc->sc_mtx);
547: acpiec_unlock(dv);
548: aprint_error_dev(dv, "command takes over 5sec...\n");
549: return AE_ERROR;
550: }
551:
1.41.6.7 joerg 552: done:
553: *val = sc->sc_cur_val;
1.41.6.5 joerg 554:
555: mutex_exit(&sc->sc_mtx);
556: acpiec_unlock(dv);
557: return AE_OK;
1.1 thorpej 558: }
559:
560: static ACPI_STATUS
1.41.6.5 joerg 561: acpiec_write(device_t dv, uint8_t addr, uint8_t val)
1.1 thorpej 562: {
1.41.6.5 joerg 563: struct acpiec_softc *sc = device_private(dv);
1.41.6.7 joerg 564: int i, timeouts = 0;
1.1 thorpej 565:
1.41.6.5 joerg 566: acpiec_lock(dv);
567: mutex_enter(&sc->sc_mtx);
1.1 thorpej 568:
1.41.6.5 joerg 569: retry:
570: sc->sc_cur_addr = addr;
571: sc->sc_cur_val = val;
572: sc->sc_state = EC_STATE_WRITE;
573:
1.41.6.7 joerg 574: for (i = 0; i < EC_POLL_TIMEOUT; ++i) {
575: acpiec_gpe_state_maschine(dv);
576: if (sc->sc_state == EC_STATE_FREE)
577: goto done;
578: delay(1);
579: }
580:
1.41.6.5 joerg 581: if (cold || acpiec_cold) {
1.41.6.7 joerg 582: while (sc->sc_state != EC_STATE_FREE) {
583: delay(1);
1.41.6.5 joerg 584: acpiec_gpe_state_maschine(dv);
1.41.6.7 joerg 585: }
1.41.6.5 joerg 586: } else while (cv_timedwait(&sc->sc_cv, &sc->sc_mtx, hz)) {
587: mutex_exit(&sc->sc_mtx);
588: AcpiClearGpe(sc->sc_gpeh, sc->sc_gpebit, ACPI_NOT_ISR);
589: mutex_enter(&sc->sc_mtx);
590: if (++timeouts < 5)
591: goto retry;
592: mutex_exit(&sc->sc_mtx);
593: acpiec_unlock(dv);
594: aprint_error_dev(dv, "command takes over 5sec...\n");
595: return AE_ERROR;
1.41.6.4 joerg 596: }
597:
1.41.6.7 joerg 598: done:
1.41.6.5 joerg 599: mutex_exit(&sc->sc_mtx);
600: acpiec_unlock(dv);
601: return AE_OK;
1.1 thorpej 602: }
603:
604: static ACPI_STATUS
1.41.6.5 joerg 605: acpiec_space_handler(UINT32 func, ACPI_PHYSICAL_ADDRESS paddr,
606: UINT32 width, ACPI_INTEGER *value, void *arg, void *region_arg)
1.1 thorpej 607: {
1.41.6.5 joerg 608: device_t dv;
609: struct acpiec_softc *sc;
1.25 kochi 610: ACPI_STATUS rv;
1.41.6.5 joerg 611: uint8_t addr, reg;
612: unsigned int i;
613:
614: if (paddr > 0xff || width % 8 != 0 || value == NULL || arg == NULL ||
615: paddr + width / 8 > 0xff)
616: return AE_BAD_PARAMETER;
1.1 thorpej 617:
1.41.6.5 joerg 618: addr = paddr;
619: dv = arg;
620: sc = device_private(dv);
621:
622: rv = AE_OK;
623:
624: switch (func) {
625: case ACPI_READ:
1.41.6.8 joerg 626: *value = 0;
1.41.6.5 joerg 627: for (i = 0; i < width; i += 8, ++addr) {
628: rv = acpiec_read(dv, addr, ®);
629: if (rv != AE_OK)
630: break;
631: *value |= (ACPI_INTEGER)reg << i;
632: }
633: break;
634: case ACPI_WRITE:
635: for (i = 0; i < width; i += 8, ++addr) {
636: reg = (*value >>i) & 0xff;
637: rv = acpiec_write(dv, addr, reg);
638: if (rv != AE_OK)
639: break;
640: }
641: break;
642: default:
643: aprint_error("%s: invalid Address Space function called: %x\n",
644: device_xname(dv), (unsigned int)func);
645: return AE_BAD_PARAMETER;
646: }
1.1 thorpej 647:
1.25 kochi 648: return rv;
1.24 kochi 649: }
1.1 thorpej 650:
1.41.6.5 joerg 651: static void
652: acpiec_gpe_query(void *arg)
1.1 thorpej 653: {
1.41.6.5 joerg 654: device_t dv = arg;
655: struct acpiec_softc *sc = device_private(dv);
656: uint8_t reg;
657: char qxx[5];
1.25 kochi 658: ACPI_STATUS rv;
1.41.6.7 joerg 659: int i;
1.1 thorpej 660:
1.41.6.5 joerg 661: loop:
662: mutex_enter(&sc->sc_mtx);
1.1 thorpej 663:
1.41.6.5 joerg 664: if (sc->sc_got_sci == false)
665: cv_wait(&sc->sc_cv_sci, &sc->sc_mtx);
666: mutex_exit(&sc->sc_mtx);
1.1 thorpej 667:
1.41.6.5 joerg 668: acpiec_lock(dv);
669: mutex_enter(&sc->sc_mtx);
1.1 thorpej 670:
1.41.6.7 joerg 671: /* The Query command can always be issued, so be defensive here. */
672: sc->sc_got_sci = false;
1.41.6.5 joerg 673: sc->sc_state = EC_STATE_QUERY;
1.1 thorpej 674:
1.41.6.7 joerg 675: for (i = 0; i < EC_POLL_TIMEOUT; ++i) {
676: acpiec_gpe_state_maschine(dv);
677: if (sc->sc_state == EC_STATE_FREE)
678: goto done;
679: delay(1);
680: }
681:
1.41.6.5 joerg 682: cv_wait(&sc->sc_cv, &sc->sc_mtx);
1.1 thorpej 683:
1.41.6.7 joerg 684: done:
685: reg = sc->sc_cur_val;
1.1 thorpej 686:
1.41.6.5 joerg 687: mutex_exit(&sc->sc_mtx);
688: acpiec_unlock(dv);
689:
690: if (reg == 0)
691: goto loop; /* Spurious query result */
1.41.6.7 joerg 692:
1.41.6.5 joerg 693: /*
694: * Evaluate _Qxx to respond to the controller.
695: */
696: snprintf(qxx, sizeof(qxx), "_Q%02X", (unsigned int)reg);
697: rv = AcpiEvaluateObject(sc->sc_ech, qxx, NULL, NULL);
698: if (rv != AE_OK && rv != AE_NOT_FOUND) {
699: aprint_error("%s: GPE query method %s failed: %s",
700: device_xname(dv), qxx, AcpiFormatException(rv));
701: }
702:
703: goto loop;
1.24 kochi 704: }
1.1 thorpej 705:
1.41.6.5 joerg 706: static void
707: acpiec_gpe_state_maschine(device_t dv)
1.1 thorpej 708: {
1.41.6.5 joerg 709: struct acpiec_softc *sc = device_private(dv);
710: uint8_t reg;
1.1 thorpej 711:
1.41.6.5 joerg 712: reg = acpiec_read_status(sc);
1.1 thorpej 713:
1.41.6.5 joerg 714: if (reg & EC_STATUS_SCI)
715: sc->sc_got_sci = true;
1.1 thorpej 716:
1.41.6.5 joerg 717: switch (sc->sc_state) {
718: case EC_STATE_QUERY:
1.41.6.7 joerg 719: if ((reg & EC_STATUS_IBF) != 0)
720: break; /* Nothing of interest here. */
721: acpiec_write_command(sc, EC_COMMAND_QUERY);
722: sc->sc_state = EC_STATE_QUERY_VAL;
723: break;
724:
725: case EC_STATE_QUERY_VAL:
1.41.6.5 joerg 726: if ((reg & EC_STATUS_OBF) == 0)
727: break; /* Nothing of interest here. */
1.1 thorpej 728:
1.41.6.7 joerg 729: sc->sc_cur_val = acpiec_read_data(sc);
730: sc->sc_state = EC_STATE_FREE;
731:
1.41.6.5 joerg 732: cv_signal(&sc->sc_cv);
733: break;
1.1 thorpej 734:
1.41.6.5 joerg 735: case EC_STATE_READ:
736: if ((reg & EC_STATUS_IBF) != 0)
737: break; /* Nothing of interest here. */
1.1 thorpej 738:
1.41.6.7 joerg 739: acpiec_write_command(sc, EC_COMMAND_READ);
740: sc->sc_state = EC_STATE_READ_ADDR;
741: break;
742:
743: case EC_STATE_READ_ADDR:
744: if ((reg & EC_STATUS_IBF) != 0)
745: break; /* Nothing of interest here. */
746:
1.41.6.5 joerg 747: acpiec_write_data(sc, sc->sc_cur_addr);
748: sc->sc_state = EC_STATE_READ_VAL;
749: break;
1.1 thorpej 750:
1.41.6.5 joerg 751: case EC_STATE_READ_VAL:
752: if ((reg & EC_STATUS_OBF) == 0)
753: break; /* Nothing of interest here. */
1.41.6.7 joerg 754: sc->sc_cur_val = acpiec_read_data(sc);
755: sc->sc_state = EC_STATE_FREE;
756:
1.41.6.5 joerg 757: cv_signal(&sc->sc_cv);
758: break;
1.41.6.3 joerg 759:
1.41.6.5 joerg 760: case EC_STATE_WRITE:
761: if ((reg & EC_STATUS_IBF) != 0)
762: break; /* Nothing of interest here. */
1.41.6.7 joerg 763:
764: acpiec_write_command(sc, EC_COMMAND_WRITE);
765: sc->sc_state = EC_STATE_WRITE_ADDR;
766: break;
767:
768: case EC_STATE_WRITE_ADDR:
769: if ((reg & EC_STATUS_IBF) != 0)
770: break; /* Nothing of interest here. */
1.41.6.5 joerg 771: acpiec_write_data(sc, sc->sc_cur_addr);
772: sc->sc_state = EC_STATE_WRITE_VAL;
773: break;
1.41.6.3 joerg 774:
1.41.6.5 joerg 775: case EC_STATE_WRITE_VAL:
776: if ((reg & EC_STATUS_IBF) != 0)
777: break; /* Nothing of interest here. */
1.41.6.7 joerg 778: sc->sc_state = EC_STATE_FREE;
1.41.6.5 joerg 779: cv_signal(&sc->sc_cv);
1.41.6.3 joerg 780:
1.41.6.7 joerg 781: acpiec_write_data(sc, sc->sc_cur_val);
1.41.6.3 joerg 782: break;
783:
1.41.6.5 joerg 784: case EC_STATE_FREE:
785: if (sc->sc_got_sci)
786: cv_signal(&sc->sc_cv_sci);
1.41.6.3 joerg 787: break;
1.41.6.5 joerg 788: default:
789: panic("invalid state");
1.41.6.3 joerg 790: }
1.41.6.5 joerg 791: }
1.41.6.3 joerg 792:
1.41.6.5 joerg 793: static UINT32
794: acpiec_gpe_handler(void *arg)
795: {
796: device_t dv = arg;
797: struct acpiec_softc *sc = device_private(dv);
1.41.6.3 joerg 798:
1.41.6.5 joerg 799: AcpiClearGpe(sc->sc_gpeh, sc->sc_gpebit, ACPI_ISR);
800:
801: mutex_enter(&sc->sc_mtx);
802: acpiec_gpe_state_maschine(dv);
803: mutex_exit(&sc->sc_mtx);
804:
805: return 0;
1.41.6.3 joerg 806: }
CVSweb <webmaster@jp.NetBSD.org>