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