Line data Source code
1 : /* $OpenBSD: acpiec.c,v 1.59 2018/07/01 19:40:49 mlarkin Exp $ */
2 : /*
3 : * Copyright (c) 2006 Can Erkin Acar <canacar@openbsd.org>
4 : *
5 : * Permission to use, copy, modify, and distribute this software for any
6 : * purpose with or without fee is hereby granted, provided that the above
7 : * copyright notice and this permission notice appear in all copies.
8 : *
9 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 : */
17 :
18 : #include <sys/param.h>
19 : #include <sys/signalvar.h>
20 : #include <sys/systm.h>
21 : #include <sys/device.h>
22 :
23 : #include <machine/bus.h>
24 :
25 : #include <dev/acpi/acpireg.h>
26 : #include <dev/acpi/acpivar.h>
27 : #include <dev/acpi/acpidev.h>
28 : #include <dev/acpi/amltypes.h>
29 : #include <dev/acpi/dsdt.h>
30 :
31 : #include <sys/sensors.h>
32 :
33 : int acpiec_match(struct device *, void *, void *);
34 : void acpiec_attach(struct device *, struct device *, void *);
35 :
36 : uint8_t acpiec_status(struct acpiec_softc *);
37 : uint8_t acpiec_read_data(struct acpiec_softc *);
38 : void acpiec_write_cmd(struct acpiec_softc *, uint8_t);
39 : void acpiec_write_data(struct acpiec_softc *, uint8_t);
40 : void acpiec_burst_enable(struct acpiec_softc *sc);
41 : void acpiec_burst_disable(struct acpiec_softc *sc);
42 :
43 : uint8_t acpiec_read_1(struct acpiec_softc *, uint8_t);
44 : void acpiec_write_1(struct acpiec_softc *, uint8_t, uint8_t);
45 :
46 : void acpiec_read(struct acpiec_softc *, uint8_t, int, uint8_t *);
47 : void acpiec_write(struct acpiec_softc *, uint8_t, int, uint8_t *);
48 :
49 : int acpiec_getcrs(struct acpiec_softc *,
50 : struct acpi_attach_args *);
51 : int acpiec_parse_resources(int, union acpi_resource *, void *);
52 :
53 : void acpiec_wait(struct acpiec_softc *, uint8_t, uint8_t);
54 : void acpiec_sci_event(struct acpiec_softc *);
55 :
56 : void acpiec_get_events(struct acpiec_softc *);
57 :
58 : int acpiec_gpehandler(struct acpi_softc *, int, void *);
59 :
60 : void acpiec_lock(struct acpiec_softc *);
61 : void acpiec_unlock(struct acpiec_softc *);
62 :
63 : /* EC Status bits */
64 : #define EC_STAT_SMI_EVT 0x40 /* SMI event pending */
65 : #define EC_STAT_SCI_EVT 0x20 /* SCI event pending */
66 : #define EC_STAT_BURST 0x10 /* Controller in burst mode */
67 : #define EC_STAT_CMD 0x08 /* data is command */
68 : #define EC_STAT_IBF 0x02 /* input buffer full */
69 : #define EC_STAT_OBF 0x01 /* output buffer full */
70 :
71 : /* EC Commands */
72 : #define EC_CMD_RD 0x80 /* Read */
73 : #define EC_CMD_WR 0x81 /* Write */
74 : #define EC_CMD_BE 0x82 /* Burst Enable */
75 : #define EC_CMD_BD 0x83 /* Burst Disable */
76 : #define EC_CMD_QR 0x84 /* Query */
77 :
78 : int acpiec_reg(struct acpiec_softc *);
79 :
80 : extern char *hw_vendor, *hw_prod;
81 :
82 : struct cfattach acpiec_ca = {
83 : sizeof(struct acpiec_softc), acpiec_match, acpiec_attach
84 : };
85 :
86 : struct cfdriver acpiec_cd = {
87 : NULL, "acpiec", DV_DULL
88 : };
89 :
90 : const char *acpiec_hids[] = {
91 : ACPI_DEV_ECD,
92 : NULL
93 : };
94 :
95 : void
96 0 : acpiec_wait(struct acpiec_softc *sc, uint8_t mask, uint8_t val)
97 : {
98 : static int acpiecnowait;
99 : uint8_t stat;
100 :
101 : dnprintf(40, "%s: EC wait_ns for: %b == %02x\n",
102 : DEVNAME(sc), (int)mask,
103 : "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF", (int)val);
104 :
105 0 : while (((stat = acpiec_status(sc)) & mask) != val) {
106 0 : if (stat & EC_STAT_SCI_EVT)
107 0 : sc->sc_gotsci = 1;
108 0 : if (cold || (stat & EC_STAT_BURST))
109 0 : delay(1);
110 : else
111 0 : tsleep(&acpiecnowait, PWAIT, "acpiec", 1);
112 : }
113 :
114 : dnprintf(40, "%s: EC wait_ns, stat: %b\n", DEVNAME(sc), (int)stat,
115 : "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF");
116 0 : }
117 :
118 : uint8_t
119 0 : acpiec_status(struct acpiec_softc *sc)
120 : {
121 0 : return (bus_space_read_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0));
122 : }
123 :
124 : void
125 0 : acpiec_write_data(struct acpiec_softc *sc, uint8_t val)
126 : {
127 0 : acpiec_wait(sc, EC_STAT_IBF, 0);
128 : dnprintf(40, "acpiec: write_data -- %d\n", (int)val);
129 0 : bus_space_write_1(sc->sc_data_bt, sc->sc_data_bh, 0, val);
130 0 : }
131 :
132 : void
133 0 : acpiec_write_cmd(struct acpiec_softc *sc, uint8_t val)
134 : {
135 0 : acpiec_wait(sc, EC_STAT_IBF, 0);
136 : dnprintf(40, "acpiec: write_cmd -- %d\n", (int)val);
137 0 : bus_space_write_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0, val);
138 0 : }
139 :
140 : uint8_t
141 0 : acpiec_read_data(struct acpiec_softc *sc)
142 : {
143 : uint8_t val;
144 :
145 0 : acpiec_wait(sc, EC_STAT_OBF, EC_STAT_OBF);
146 0 : val = bus_space_read_1(sc->sc_data_bt, sc->sc_data_bh, 0);
147 :
148 : dnprintf(40, "acpiec: read_data %d\n", (int)val);
149 :
150 0 : return (val);
151 : }
152 :
153 : void
154 0 : acpiec_sci_event(struct acpiec_softc *sc)
155 : {
156 : uint8_t evt;
157 :
158 0 : sc->sc_gotsci = 0;
159 :
160 0 : acpiec_wait(sc, EC_STAT_IBF, 0);
161 0 : bus_space_write_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0, EC_CMD_QR);
162 :
163 0 : acpiec_wait(sc, EC_STAT_OBF, EC_STAT_OBF);
164 0 : evt = bus_space_read_1(sc->sc_data_bt, sc->sc_data_bh, 0);
165 :
166 0 : if (evt) {
167 : dnprintf(10, "%s: sci_event: 0x%02x\n", DEVNAME(sc), (int)evt);
168 0 : aml_evalnode(sc->sc_acpi, sc->sc_events[evt].event, 0, NULL,
169 : NULL);
170 0 : }
171 0 : }
172 :
173 : uint8_t
174 0 : acpiec_read_1(struct acpiec_softc *sc, uint8_t addr)
175 : {
176 : uint8_t val;
177 :
178 0 : if ((acpiec_status(sc) & EC_STAT_SCI_EVT) == EC_STAT_SCI_EVT)
179 0 : sc->sc_gotsci = 1;
180 :
181 0 : acpiec_write_cmd(sc, EC_CMD_RD);
182 0 : acpiec_write_data(sc, addr);
183 :
184 0 : val = acpiec_read_data(sc);
185 :
186 0 : return (val);
187 : }
188 :
189 : void
190 0 : acpiec_write_1(struct acpiec_softc *sc, uint8_t addr, uint8_t data)
191 : {
192 0 : if ((acpiec_status(sc) & EC_STAT_SCI_EVT) == EC_STAT_SCI_EVT)
193 0 : sc->sc_gotsci = 1;
194 :
195 0 : acpiec_write_cmd(sc, EC_CMD_WR);
196 0 : acpiec_write_data(sc, addr);
197 0 : acpiec_write_data(sc, data);
198 0 : }
199 :
200 : void
201 0 : acpiec_burst_enable(struct acpiec_softc *sc)
202 : {
203 0 : if (sc->sc_cantburst)
204 : return;
205 :
206 0 : acpiec_write_cmd(sc, EC_CMD_BE);
207 0 : acpiec_read_data(sc);
208 0 : }
209 :
210 : void
211 0 : acpiec_burst_disable(struct acpiec_softc *sc)
212 : {
213 0 : if (sc->sc_cantburst)
214 : return;
215 :
216 0 : if ((acpiec_status(sc) & EC_STAT_BURST) == EC_STAT_BURST)
217 0 : acpiec_write_cmd(sc, EC_CMD_BD);
218 0 : }
219 :
220 : void
221 0 : acpiec_read(struct acpiec_softc *sc, uint8_t addr, int len, uint8_t *buffer)
222 : {
223 : int reg;
224 :
225 : /*
226 : * this works because everything runs in the acpi thread context.
227 : * at some point add a lock to deal with concurrency so that a
228 : * transaction does not get interrupted.
229 : */
230 : dnprintf(20, "%s: read %d, %d\n", DEVNAME(sc), (int)addr, len);
231 0 : sc->sc_ecbusy = 1;
232 0 : acpiec_burst_enable(sc);
233 0 : for (reg = 0; reg < len; reg++)
234 0 : buffer[reg] = acpiec_read_1(sc, addr + reg);
235 0 : acpiec_burst_disable(sc);
236 0 : sc->sc_ecbusy = 0;
237 0 : }
238 :
239 : void
240 0 : acpiec_write(struct acpiec_softc *sc, uint8_t addr, int len, uint8_t *buffer)
241 : {
242 : int reg;
243 :
244 : /*
245 : * this works because everything runs in the acpi thread context.
246 : * at some point add a lock to deal with concurrency so that a
247 : * transaction does not get interrupted.
248 : */
249 : dnprintf(20, "%s: write %d, %d\n", DEVNAME(sc), (int)addr, len);
250 0 : sc->sc_ecbusy = 1;
251 0 : acpiec_burst_enable(sc);
252 0 : for (reg = 0; reg < len; reg++)
253 0 : acpiec_write_1(sc, addr + reg, buffer[reg]);
254 0 : acpiec_burst_disable(sc);
255 0 : sc->sc_ecbusy = 0;
256 0 : }
257 :
258 : int
259 0 : acpiec_match(struct device *parent, void *match, void *aux)
260 : {
261 0 : struct acpi_attach_args *aa = aux;
262 0 : struct cfdata *cf = match;
263 0 : struct acpi_ecdt *ecdt = aa->aaa_table;
264 0 : struct acpi_softc *acpisc = (struct acpi_softc *)parent;
265 :
266 : /* Check for early ECDT table attach */
267 0 : if (ecdt &&
268 0 : !memcmp(ecdt->hdr.signature, ECDT_SIG, sizeof(ECDT_SIG) - 1))
269 0 : return (1);
270 0 : if (acpisc->sc_ec)
271 0 : return (0);
272 :
273 : /* sanity */
274 0 : return (acpi_matchhids(aa, acpiec_hids, cf->cf_driver->cd_name));
275 0 : }
276 :
277 : void
278 0 : acpiec_attach(struct device *parent, struct device *self, void *aux)
279 : {
280 0 : struct acpiec_softc *sc = (struct acpiec_softc *)self;
281 0 : struct acpi_attach_args *aa = aux;
282 0 : struct aml_value res;
283 0 : int64_t st;
284 :
285 0 : sc->sc_acpi = (struct acpi_softc *)parent;
286 0 : sc->sc_devnode = aa->aaa_node;
287 0 : sc->sc_cantburst = 0;
288 :
289 0 : if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st))
290 0 : st = STA_PRESENT | STA_ENABLED | STA_DEV_OK;
291 0 : if ((st & STA_PRESENT) == 0) {
292 0 : printf(": not present\n");
293 0 : return;
294 : }
295 :
296 0 : printf("\n");
297 0 : if (acpiec_getcrs(sc, aa)) {
298 0 : printf("%s: Failed to read resource settings\n", DEVNAME(sc));
299 0 : return;
300 : }
301 :
302 0 : sc->sc_acpi->sc_ec = sc;
303 :
304 0 : if (acpiec_reg(sc)) {
305 0 : printf("%s: Failed to register address space\n", DEVNAME(sc));
306 0 : return;
307 : }
308 :
309 : /*
310 : * Some Chromebooks using the Google EC do not support burst mode and
311 : * cause us to spin forever waiting for the acknowledgment. Don't use
312 : * burst mode at all on these machines.
313 : */
314 0 : if (hw_vendor != NULL && hw_prod != NULL &&
315 0 : strcmp(hw_vendor, "GOOGLE") == 0 &&
316 0 : strcmp(hw_prod, "Samus") == 0)
317 0 : sc->sc_cantburst = 1;
318 :
319 0 : acpiec_get_events(sc);
320 :
321 : dnprintf(10, "%s: GPE: %d\n", DEVNAME(sc), sc->sc_gpe);
322 :
323 : #ifndef SMALL_KERNEL
324 0 : acpi_set_gpehandler(sc->sc_acpi, sc->sc_gpe, acpiec_gpehandler,
325 0 : sc, 1);
326 : #endif
327 :
328 0 : if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_GLK", 0, NULL, &res))
329 0 : sc->sc_glk = 0;
330 0 : else if (res.type != AML_OBJTYPE_INTEGER)
331 0 : sc->sc_glk = 0;
332 : else
333 0 : sc->sc_glk = res.v_integer ? 1 : 0;
334 0 : }
335 :
336 : void
337 0 : acpiec_get_events(struct acpiec_softc *sc)
338 : {
339 : int idx;
340 0 : char name[16];
341 :
342 0 : memset(sc->sc_events, 0, sizeof(sc->sc_events));
343 0 : for (idx = 0; idx < ACPIEC_MAX_EVENTS; idx++) {
344 0 : snprintf(name, sizeof(name), "_Q%02X", idx);
345 0 : sc->sc_events[idx].event = aml_searchname(sc->sc_devnode, name);
346 : if (sc->sc_events[idx].event != NULL)
347 : dnprintf(10, "%s: Found event %s\n", DEVNAME(sc), name);
348 : }
349 0 : }
350 :
351 : int
352 0 : acpiec_gpehandler(struct acpi_softc *acpi_sc, int gpe, void *arg)
353 : {
354 0 : struct acpiec_softc *sc = arg;
355 : uint8_t mask, stat, en;
356 : int s;
357 :
358 0 : KASSERT(sc->sc_ecbusy == 0);
359 : dnprintf(10, "ACPIEC: got gpe\n");
360 :
361 0 : do {
362 0 : if (sc->sc_gotsci)
363 0 : acpiec_sci_event(sc);
364 :
365 0 : stat = acpiec_status(sc);
366 : dnprintf(40, "%s: EC interrupt, stat: %b\n",
367 : DEVNAME(sc), (int)stat,
368 : "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF");
369 :
370 0 : if (stat & EC_STAT_SCI_EVT)
371 0 : sc->sc_gotsci = 1;
372 : else
373 0 : sc->sc_gotsci = 0;
374 0 : } while (sc->sc_gotsci);
375 :
376 : /* Unmask the GPE which was blocked at interrupt time */
377 0 : s = spltty();
378 0 : mask = (1L << (gpe & 7));
379 0 : en = acpi_read_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3);
380 0 : acpi_write_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3, en | mask);
381 0 : splx(s);
382 :
383 0 : return (0);
384 : }
385 :
386 : int
387 0 : acpiec_parse_resources(int crsidx, union acpi_resource *crs, void *arg)
388 : {
389 0 : struct acpiec_softc *sc = arg;
390 0 : int type = AML_CRSTYPE(crs);
391 :
392 0 : switch (crsidx) {
393 : case 0:
394 0 : if (type != SR_IOPORT) {
395 0 : printf("%s: Unexpected resource #%d type %d\n",
396 0 : DEVNAME(sc), crsidx, type);
397 0 : break;
398 : }
399 0 : sc->sc_data_bt = sc->sc_acpi->sc_iot;
400 0 : sc->sc_ec_data = crs->sr_ioport._max;
401 0 : break;
402 : case 1:
403 0 : if (type != SR_IOPORT) {
404 0 : printf("%s: Unexpected resource #%d type %d\n",
405 0 : DEVNAME(sc), crsidx, type);
406 0 : break;
407 : }
408 0 : sc->sc_cmd_bt = sc->sc_acpi->sc_iot;
409 0 : sc->sc_ec_sc = crs->sr_ioport._max;
410 0 : break;
411 : case 2:
412 0 : if (!sc->sc_acpi->sc_hw_reduced) {
413 0 : printf("%s: Not running on HW-Reduced ACPI type %d\n",
414 0 : DEVNAME(sc), type);
415 0 : break;
416 : }
417 : /* XXX: handle SCI GPIO */
418 : break;
419 : default:
420 0 : printf("%s: invalid resource #%d type %d\n",
421 0 : DEVNAME(sc), crsidx, type);
422 0 : }
423 :
424 0 : return 0;
425 : }
426 :
427 : int
428 0 : acpiec_getcrs(struct acpiec_softc *sc, struct acpi_attach_args *aa)
429 : {
430 0 : struct aml_value res;
431 0 : int64_t gpe;
432 0 : struct acpi_ecdt *ecdt = aa->aaa_table;
433 : extern struct aml_node aml_root;
434 : int rc;
435 :
436 : /* Check if this is ECDT initialization */
437 0 : if (ecdt) {
438 : /* Get GPE, Data and Control segments */
439 0 : sc->sc_gpe = ecdt->gpe_bit;
440 :
441 0 : if (ecdt->ec_control.address_space_id == GAS_SYSTEM_IOSPACE)
442 0 : sc->sc_cmd_bt = sc->sc_acpi->sc_iot;
443 : else
444 0 : sc->sc_cmd_bt = sc->sc_acpi->sc_memt;
445 0 : sc->sc_ec_sc = ecdt->ec_control.address;
446 :
447 0 : if (ecdt->ec_data.address_space_id == GAS_SYSTEM_IOSPACE)
448 0 : sc->sc_data_bt = sc->sc_acpi->sc_iot;
449 : else
450 0 : sc->sc_data_bt = sc->sc_acpi->sc_memt;
451 0 : sc->sc_ec_data = ecdt->ec_data.address;
452 :
453 : /* Get devnode from header */
454 0 : sc->sc_devnode = aml_searchname(&aml_root, ecdt->ec_id);
455 :
456 0 : goto ecdtdone;
457 : }
458 :
459 0 : rc = aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
460 : "_GPE", 0, NULL, &gpe);
461 0 : if (rc) {
462 : dnprintf(10, "%s: no _GPE\n", DEVNAME(sc));
463 0 : return (1);
464 : }
465 :
466 0 : sc->sc_gpe = gpe;
467 :
468 0 : if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CRS", 0, NULL, &res)) {
469 : dnprintf(10, "%s: no _CRS\n", DEVNAME(sc));
470 0 : return (1);
471 : }
472 :
473 : /* Parse CRS to get control and data registers */
474 :
475 0 : if (res.type != AML_OBJTYPE_BUFFER) {
476 : dnprintf(10, "%s: unknown _CRS type %d\n",
477 : DEVNAME(sc), res.type);
478 0 : aml_freevalue(&res);
479 0 : return (1);
480 : }
481 :
482 0 : aml_parse_resource(&res, acpiec_parse_resources, sc);
483 0 : aml_freevalue(&res);
484 0 : if (sc->sc_ec_data == 0 || sc->sc_ec_sc == 0) {
485 0 : printf("%s: failed to read from _CRS\n", DEVNAME(sc));
486 0 : return (1);
487 : }
488 :
489 : ecdtdone:
490 :
491 : dnprintf(10, "%s: Data: 0x%lx, S/C: 0x%lx\n",
492 : DEVNAME(sc), sc->sc_ec_data, sc->sc_ec_sc);
493 :
494 0 : if (bus_space_map(sc->sc_cmd_bt, sc->sc_ec_sc, 1, 0, &sc->sc_cmd_bh)) {
495 : dnprintf(10, "%s: failed to map S/C reg.\n", DEVNAME(sc));
496 0 : return (1);
497 : }
498 :
499 0 : rc = bus_space_map(sc->sc_data_bt, sc->sc_ec_data, 1, 0,
500 0 : &sc->sc_data_bh);
501 0 : if (rc) {
502 : dnprintf(10, "%s: failed to map DATA reg.\n", DEVNAME(sc));
503 0 : bus_space_unmap(sc->sc_cmd_bt, sc->sc_cmd_bh, 1);
504 0 : return (1);
505 : }
506 :
507 0 : return (0);
508 0 : }
509 :
510 : int
511 0 : acpiec_reg(struct acpiec_softc *sc)
512 : {
513 0 : struct aml_value arg[2];
514 : struct aml_node *node;
515 :
516 0 : memset(&arg, 0, sizeof(arg));
517 0 : arg[0].type = AML_OBJTYPE_INTEGER;
518 0 : arg[0].v_integer = ACPI_OPREG_EC;
519 0 : arg[1].type = AML_OBJTYPE_INTEGER;
520 0 : arg[1].v_integer = 1;
521 :
522 0 : node = aml_searchname(sc->sc_devnode, "_REG");
523 0 : if (node && aml_evalnode(sc->sc_acpi, node, 2, arg, NULL)) {
524 : dnprintf(10, "%s: eval method _REG failed\n", DEVNAME(sc));
525 0 : printf("acpiec _REG failed, broken BIOS\n");
526 0 : }
527 :
528 0 : return (0);
529 0 : }
530 :
531 : void
532 0 : acpiec_lock(struct acpiec_softc *sc)
533 : {
534 0 : KASSERT(sc->sc_ecbusy == 0);
535 :
536 0 : sc->sc_ecbusy = 1;
537 :
538 0 : if (sc->sc_glk) {
539 0 : acpi_glk_enter();
540 0 : }
541 0 : }
542 :
543 : void
544 0 : acpiec_unlock(struct acpiec_softc *sc)
545 : {
546 0 : KASSERT(sc->sc_ecbusy == 1);
547 :
548 0 : if (sc->sc_glk) {
549 0 : acpi_glk_leave();
550 0 : }
551 :
552 0 : sc->sc_ecbusy = 0;
553 0 : }
|