1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25 /*
26 * Solaris x86 ACPI CA Embedded Controller operation region handler
27 */
28
29 #include <sys/file.h>
30 #include <sys/errno.h>
31 #include <sys/conf.h>
32 #include <sys/modctl.h>
33 #include <sys/open.h>
34 #include <sys/stat.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/note.h>
38 #include <sys/atomic.h>
39
40 #include <sys/acpi/acpi.h>
41 #include <sys/acpica.h>
42
43 /*
44 * Internal prototypes
45 */
46 static int ec_wait_ibf_clear(int sc_addr);
47 static int ec_wait_obf_set(int sc_addr);
48
49 /*
50 * EC status bits
51 */
52 #define EC_IBF (0x02)
53 #define EC_OBF (0x01)
54 #define EC_SMI (0x40)
55 #define EC_SCI (0x20)
56
57 /*
58 * EC commands
59 */
60 #define EC_RD (0x80)
61 #define EC_WR (0x81)
62 #define EC_BE (0x82)
63 #define EC_BD (0x83)
64 #define EC_QR (0x84)
65
66 #define IO_PORT_DES (0x47)
67
68 /*
69 * EC softstate
70 */
71 struct ec_softstate {
72 uint8_t ec_ok; /* != 0 when EC handler is attached */
73 uint16_t ec_base; /* base of EC I/O port - data */
74 uint16_t ec_sc; /* EC status/command */
75 ACPI_HANDLE ec_obj; /* handle to ACPI object for EC */
76 kmutex_t ec_mutex; /* serialize access to EC */
77 } ec;
78
79 /* I/O port range descriptor */
80 typedef struct io_port_des {
81 uint8_t type;
82 uint8_t decode;
83 uint8_t min_base_lo;
84 uint8_t min_base_hi;
85 uint8_t max_base_lo;
86 uint8_t max_base_hi;
87 uint8_t align;
88 uint8_t len;
89 } io_port_des_t;
90
91 /*
92 * Patchable timeout values for EC input-buffer-full-clear
93 * and output-buffer-full-set. These are in 10uS units and
94 * default to 1 second.
95 */
96 int ibf_clear_timeout = 100000;
97 int obf_set_timeout = 100000;
98
99 /*
100 * ACPI CA address space handler interface functions
101 */
102 /*ARGSUSED*/
103 static ACPI_STATUS
ec_setup(ACPI_HANDLE reg,UINT32 func,void * context,void ** ret)104 ec_setup(ACPI_HANDLE reg, UINT32 func, void *context, void **ret)
105 {
106
107 return (AE_OK);
108 }
109
110 /*
111 * Only called from ec_handler(), which validates ec_ok
112 */
113 static int
ec_rd(int addr)114 ec_rd(int addr)
115 {
116 int cnt, rv;
117 uint8_t sc;
118
119 mutex_enter(&ec.ec_mutex);
120 sc = inb(ec.ec_sc);
121
122 #ifdef DEBUG
123 if (sc & EC_IBF) {
124 cmn_err(CE_NOTE, "!ec_rd: IBF already set");
125 }
126
127 if (sc & EC_OBF) {
128 cmn_err(CE_NOTE, "!ec_rd: OBF already set");
129 }
130 #endif
131
132 outb(ec.ec_sc, EC_RD); /* output a read command */
133 if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
134 cmn_err(CE_NOTE, "!ec_rd:1: timed-out waiting "
135 "for IBF to clear");
136 mutex_exit(&ec.ec_mutex);
137 return (-1);
138 }
139
140 outb(ec.ec_base, addr); /* output addr */
141 if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
142 cmn_err(CE_NOTE, "!ec_rd:2: timed-out waiting "
143 "for IBF to clear");
144 mutex_exit(&ec.ec_mutex);
145 return (-1);
146 }
147 if (ec_wait_obf_set(ec.ec_sc) < 0) {
148 cmn_err(CE_NOTE, "!ec_rd:1: timed-out waiting "
149 "for OBF to set");
150 mutex_exit(&ec.ec_mutex);
151 return (-1);
152 }
153
154 rv = inb(ec.ec_base);
155 mutex_exit(&ec.ec_mutex);
156 return (rv);
157 }
158
159 /*
160 * Only called from ec_handler(), which validates ec_ok
161 */
162 static int
ec_wr(int addr,uint8_t * val)163 ec_wr(int addr, uint8_t *val)
164 {
165 int cnt;
166 uint8_t sc;
167
168 mutex_enter(&ec.ec_mutex);
169 sc = inb(ec.ec_sc);
170
171 #ifdef DEBUG
172 if (sc & EC_IBF) {
173 cmn_err(CE_NOTE, "!ec_wr: IBF already set");
174 }
175
176 if (sc & EC_OBF) {
177 cmn_err(CE_NOTE, "!ec_wr: OBF already set");
178 }
179 #endif
180
181 outb(ec.ec_sc, EC_WR); /* output a write command */
182 if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
183 cmn_err(CE_NOTE, "!ec_wr:1: timed-out waiting "
184 "for IBF to clear");
185 mutex_exit(&ec.ec_mutex);
186 return (-1);
187 }
188
189 outb(ec.ec_base, addr); /* output addr */
190 if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
191 cmn_err(CE_NOTE, "!ec_wr:2: timed-out waiting "
192 "for IBF to clear");
193 mutex_exit(&ec.ec_mutex);
194 return (-1);
195 }
196
197 outb(ec.ec_base, *val); /* write data */
198 if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
199 cmn_err(CE_NOTE, "!ec_wr:3: timed-out waiting "
200 "for IBF to clear");
201 mutex_exit(&ec.ec_mutex);
202 return (-1);
203 }
204
205 mutex_exit(&ec.ec_mutex);
206 return (0);
207 }
208
209 /*
210 * Only called from ec_gpe_callback(), which validates ec_ok
211 */
212 static int
ec_query(void)213 ec_query(void)
214 {
215 int cnt, rv;
216 uint8_t sc;
217
218 mutex_enter(&ec.ec_mutex);
219 outb(ec.ec_sc, EC_QR); /* output a query command */
220 if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
221 cmn_err(CE_NOTE, "!ec_query:1: timed-out waiting "
222 "for IBF to clear");
223 mutex_exit(&ec.ec_mutex);
224 return (-1);
225 }
226
227 if (ec_wait_obf_set(ec.ec_sc) < 0) {
228 cmn_err(CE_NOTE, "!ec_query:1: timed-out waiting "
229 "for OBF to set");
230 mutex_exit(&ec.ec_mutex);
231 return (-1);
232 }
233
234 rv = inb(ec.ec_base);
235 mutex_exit(&ec.ec_mutex);
236 return (rv);
237 }
238
239 static ACPI_STATUS
ec_handler(UINT32 func,ACPI_PHYSICAL_ADDRESS addr,UINT32 width,ACPI_INTEGER * val,void * context,void * regcontext)240 ec_handler(UINT32 func, ACPI_PHYSICAL_ADDRESS addr, UINT32 width,
241 ACPI_INTEGER *val, void *context, void *regcontext)
242 {
243 _NOTE(ARGUNUSED(context, regcontext))
244 int tmp;
245
246 /* Guard against unexpected invocation */
247 if (ec.ec_ok == 0)
248 return (AE_ERROR);
249
250 /*
251 * Add safety checks for BIOSes not strictly compliant
252 * with ACPI spec
253 */
254 if (((width % 8) != 0) || (width > 8)) {
255 cmn_err(CE_NOTE, "!ec_handler: invalid width %d", width);
256 return (AE_ERROR);
257 }
258
259 switch (func) {
260 case ACPI_READ:
261 tmp = ec_rd(addr);
262 if (tmp < 0)
263 return (AE_ERROR);
264 *val = tmp;
265 break;
266 case ACPI_WRITE:
267 if (ec_wr(addr, (uint8_t *)val) < 0)
268 return (AE_ERROR);
269 break;
270 default:
271 return (AE_ERROR);
272 }
273
274 return (AE_OK);
275 }
276
277
278 static void
ec_gpe_callback(void * ctx)279 ec_gpe_callback(void *ctx)
280 {
281 _NOTE(ARGUNUSED(ctx))
282
283 char query_str[5];
284 int query;
285
286 if (!(inb(ec.ec_sc) & EC_SCI))
287 return;
288
289 if (ec.ec_ok == 0) {
290 cmn_err(CE_WARN, "!ec_gpe_callback: EC handler unavailable");
291 return;
292 }
293
294 query = ec_query();
295 if (query >= 0) {
296 (void) snprintf(query_str, 5, "_Q%02X", (uint8_t)query);
297 (void) AcpiEvaluateObject(ec.ec_obj, query_str, NULL, NULL);
298 }
299
300 }
301
302 static UINT32
ec_gpe_handler(void * ctx)303 ec_gpe_handler(void *ctx)
304 {
305 _NOTE(ARGUNUSED(ctx))
306
307 AcpiOsExecute(OSL_GPE_HANDLER, ec_gpe_callback, NULL);
308 return (0);
309 }
310
311 /*
312 * Busy-wait for IBF to clear
313 * return < 0 for time out, 0 for no error
314 */
315 static int
ec_wait_ibf_clear(int sc_addr)316 ec_wait_ibf_clear(int sc_addr)
317 {
318 int cnt;
319
320 cnt = ibf_clear_timeout;
321 while (inb(sc_addr) & EC_IBF) {
322 if (cnt-- <= 0)
323 return (-1);
324 drv_usecwait(10);
325 }
326 return (0);
327 }
328
329 /*
330 * Busy-wait for OBF to set
331 * return < 0 for time out, 0 for no error
332 */
333 static int
ec_wait_obf_set(int sc_addr)334 ec_wait_obf_set(int sc_addr)
335 {
336 int cnt;
337
338 cnt = obf_set_timeout;
339 while (!(inb(sc_addr) & EC_OBF)) {
340 if (cnt-- <= 0)
341 return (-1);
342 drv_usecwait(10);
343 }
344 return (0);
345 }
346
347
348
349 /*
350 * Called from AcpiWalkDevices() when an EC device is found
351 */
352 static ACPI_STATUS
acpica_install_ec(ACPI_HANDLE obj,UINT32 nest,void * context,void ** rv)353 acpica_install_ec(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv)
354 {
355 _NOTE(ARGUNUSED(nest, context, rv))
356
357 int status, i;
358 ACPI_BUFFER buf, crs;
359 ACPI_INTEGER gpe;
360 int io_port_cnt;
361
362 /*
363 * Save the one EC object we have
364 */
365 ec.ec_obj = obj;
366
367 /*
368 * Find ec_base and ec_sc addresses
369 */
370 crs.Length = ACPI_ALLOCATE_BUFFER;
371 if (ACPI_FAILURE(AcpiEvaluateObjectTyped(obj, "_CRS", NULL, &crs,
372 ACPI_TYPE_BUFFER))) {
373 cmn_err(CE_WARN, "!acpica_install_ec: _CRS object evaluate"
374 "failed");
375 return (AE_OK);
376 }
377
378 for (i = 0, io_port_cnt = 0;
379 i < ((ACPI_OBJECT *)crs.Pointer)->Buffer.Length; i++) {
380 io_port_des_t *io_port;
381 uint8_t *tmp;
382
383 tmp = ((ACPI_OBJECT *)crs.Pointer)->Buffer.Pointer + i;
384 if (*tmp != IO_PORT_DES)
385 continue;
386 io_port = (io_port_des_t *)tmp;
387 /*
388 * Assuming first port is ec_base and second is ec_sc
389 */
390 if (io_port_cnt)
391 ec.ec_sc = (io_port->min_base_hi << 8) |
392 io_port->min_base_lo;
393 else
394 ec.ec_base = (io_port->min_base_hi << 8) |
395 io_port->min_base_lo;
396
397 io_port_cnt++;
398 /*
399 * Increment ahead to next struct.
400 */
401 i += 7;
402 }
403 AcpiOsFree(crs.Pointer);
404
405 /*
406 * Drain the EC data register if something is left over from
407 * legacy mode
408 */
409 if (inb(ec.ec_sc) & EC_OBF) {
410 #ifndef DEBUG
411 inb(ec.ec_base); /* read and discard value */
412 #else
413 cmn_err(CE_NOTE, "!EC had something: 0x%x\n", inb(ec.ec_base));
414 #endif
415 }
416
417 /*
418 * Get GPE
419 */
420 buf.Length = ACPI_ALLOCATE_BUFFER;
421 /*
422 * grab contents of GPE object
423 */
424 if (ACPI_FAILURE(AcpiEvaluateObjectTyped(obj, "_GPE", NULL, &buf,
425 ACPI_TYPE_INTEGER))) {
426 /* emit a warning but ignore the error */
427 cmn_err(CE_WARN, "!acpica_install_ec: _GPE object evaluate"
428 "failed");
429 gpe = -1;
430 } else {
431 gpe = ((ACPI_OBJECT *)buf.Pointer)->Integer.Value;
432 AcpiOsFree(buf.Pointer);
433 }
434
435 /*
436 * Initialize EC mutex here
437 */
438 mutex_init(&ec.ec_mutex, NULL, MUTEX_DRIVER, NULL);
439
440 /*
441 * Assume the EC handler is available now; the next attempt
442 * to install the address space handler may actually need
443 * to use the EC handler to install it (chicken/egg)
444 */
445 ec.ec_ok = 1;
446
447 if (AcpiInstallAddressSpaceHandler(obj,
448 ACPI_ADR_SPACE_EC, &ec_handler, &ec_setup, NULL) != AE_OK) {
449 cmn_err(CE_WARN, "!acpica: failed to add EC handler\n");
450 ec.ec_ok = 0; /* EC handler failed to install */
451 membar_producer(); /* force ec_ok store before mutex_destroy */
452 mutex_destroy(&ec.ec_mutex);
453 return (AE_ERROR);
454 }
455
456 /*
457 * In case the EC handler was successfully installed but no
458 * GPE was found, return sucess; a warning was emitted above
459 */
460 if (gpe < 0)
461 return (AE_OK);
462
463 /*
464 * Have a valid EC GPE; enable it
465 */
466 if ((status = AcpiInstallGpeHandler(NULL, gpe, ACPI_GPE_EDGE_TRIGGERED,
467 ec_gpe_handler, NULL)) != AE_OK) {
468 cmn_err(CE_WARN, "!acpica: failed to install gpe handler status"
469 " = %d", status);
470 /*
471 * don't return an error here - GPE won't work but the EC
472 * handler may be OK
473 */
474 }
475
476 (void) AcpiSetGpeType(NULL, gpe, ACPI_GPE_TYPE_RUNTIME);
477 (void) AcpiEnableGpe(NULL, gpe, ACPI_NOT_ISR);
478
479 return (AE_OK);
480 }
481
482 #ifdef DEBUG
483 /*ARGSUSED*/
484 static ACPI_STATUS
acpica_install_smbus_v1(ACPI_HANDLE obj,UINT32 nest,void * context,void ** rv)485 acpica_install_smbus_v1(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv)
486 {
487
488 cmn_err(CE_NOTE, "!acpica: found an SMBC Version 1.0\n");
489 return (AE_OK);
490 }
491
492 /*ARGSUSED*/
493 static ACPI_STATUS
acpica_install_smbus_v2(ACPI_HANDLE obj,UINT32 nest,void * context,void ** rv)494 acpica_install_smbus_v2(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv)
495 {
496
497 cmn_err(CE_NOTE, "!acpica: found an SMBC Version 2.0\n");
498 return (AE_OK);
499 }
500 #endif /* DEBUG */
501
502 #ifdef NOTYET
503 static void
prgas(ACPI_GENERIC_ADDRESS * gas)504 prgas(ACPI_GENERIC_ADDRESS *gas)
505 {
506 cmn_err(CE_CONT, "gas: %d %d %d %d %lx",
507 gas->AddressSpaceId, gas->RegisterBitWidth, gas->RegisterBitOffset,
508 gas->AccessWidth, (long)gas->Address);
509 }
510
511 static void
acpica_probe_ecdt()512 acpica_probe_ecdt()
513 {
514 EC_BOOT_RESOURCES *ecdt;
515
516
517 if (AcpiGetTable("ECDT", 1, (ACPI_TABLE_HEADER **) &ecdt) != AE_OK) {
518 cmn_err(CE_NOTE, "!acpica: ECDT not found\n");
519 return;
520 }
521
522 cmn_err(CE_NOTE, "EcControl: ");
523 prgas(&ecdt->EcControl);
524
525 cmn_err(CE_NOTE, "EcData: ");
526 prgas(&ecdt->EcData);
527 }
528 #endif /* NOTYET */
529
530 void
acpica_ec_init(void)531 acpica_ec_init(void)
532 {
533 #ifdef NOTYET
534 /*
535 * Search the ACPI tables for an ECDT; if
536 * found, use it to install an EC handler
537 */
538 acpica_probe_ecdt();
539 #endif /* NOTYET */
540
541 /* EC handler defaults to not available */
542 ec.ec_ok = 0;
543
544 /*
545 * General model is: use GetDevices callback to install
546 * handler(s) when device is present.
547 */
548 (void) AcpiGetDevices("PNP0C09", &acpica_install_ec, NULL, NULL);
549 #ifdef DEBUG
550 (void) AcpiGetDevices("ACPI0001", &acpica_install_smbus_v1, NULL, NULL);
551 (void) AcpiGetDevices("ACPI0005", &acpica_install_smbus_v2, NULL, NULL);
552 #endif /* DEBUG */
553 }
554