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