xref: /illumos-gate/usr/src/uts/intel/io/acpica/acpica_ec.c (revision 56e2cc86321ec889bf83a888d902c60d6fb2ef8d)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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