xref: /illumos-gate/usr/src/uts/intel/io/acpica/acpica_ec.c (revision 168c213023b7f347f11abfc72f448b0c621ab718)
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 2006 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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30 
31 #include <sys/file.h>
32 #include <sys/errno.h>
33 #include <sys/conf.h>
34 #include <sys/modctl.h>
35 #include <sys/open.h>
36 #include <sys/stat.h>
37 #include <sys/ddi.h>
38 #include <sys/sunddi.h>
39 #include <sys/note.h>
40 
41 #include <sys/acpi/acpi.h>
42 #include <sys/acpica.h>
43 
44 /*
45  * Internal prototypes
46  */
47 static int ec_wait_ibf_clear(int sc_addr);
48 static int ec_wait_obf_set(int sc_addr);
49 
50 /*
51  * EC status bits
52  */
53 #define	EC_IBF	(0x02)
54 #define	EC_OBF	(0x01)
55 #define	EC_SMI	(0x40)
56 #define	EC_SCI	(0x20)
57 
58 /*
59  * EC commands
60  */
61 #define	EC_RD	(0x80)
62 #define	EC_WR	(0x81)
63 #define	EC_BE	(0x82)
64 #define	EC_BD	(0x83)
65 #define	EC_QR	(0x84)
66 
67 #define	IO_PORT_DES (0x47)
68 
69 /*
70  * EC softstate
71  */
72 struct ec_softstate {
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  * ACPI CA address space handler interface functions
93  */
94 /*ARGSUSED*/
95 static ACPI_STATUS
96 ec_setup(ACPI_HANDLE reg, UINT32 func, void *context, void **ret)
97 {
98 
99 	return (AE_OK);
100 }
101 
102 static int
103 ec_rd(int addr)
104 {
105 	int	cnt, rv;
106 	uint8_t sc;
107 
108 	mutex_enter(&ec.ec_mutex);
109 	sc = inb(ec.ec_sc);
110 
111 #ifdef	DEBUG
112 	if (sc & EC_IBF) {
113 		cmn_err(CE_NOTE, "!ec_rd: IBF already set");
114 	}
115 
116 	if (sc & EC_OBF) {
117 		cmn_err(CE_NOTE, "!ec_rd: OBF already set");
118 	}
119 #endif
120 
121 	outb(ec.ec_sc, EC_RD);	/* output a read command */
122 	if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
123 		cmn_err(CE_NOTE, "!ec_rd:1: timed-out waiting "
124 		    "for IBF to clear");
125 		mutex_exit(&ec.ec_mutex);
126 		return (-1);
127 	}
128 
129 	outb(ec.ec_base, addr);	/* output addr */
130 	if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
131 		cmn_err(CE_NOTE, "!ec_rd:2: timed-out waiting "
132 		    "for IBF to clear");
133 		mutex_exit(&ec.ec_mutex);
134 		return (-1);
135 	}
136 	if (ec_wait_obf_set(ec.ec_sc) < 0) {
137 		cmn_err(CE_NOTE, "!ec_rd:1: timed-out waiting "
138 		    "for OBF to set");
139 		mutex_exit(&ec.ec_mutex);
140 		return (-1);
141 	}
142 
143 	rv = inb(ec.ec_base);
144 	mutex_exit(&ec.ec_mutex);
145 	return (rv);
146 }
147 
148 static int
149 ec_wr(int addr, uint8_t *val)
150 {
151 	int	cnt;
152 	uint8_t sc;
153 
154 	mutex_enter(&ec.ec_mutex);
155 	sc = inb(ec.ec_sc);
156 
157 #ifdef	DEBUG
158 	if (sc & EC_IBF) {
159 		cmn_err(CE_NOTE, "!ec_wr: IBF already set");
160 	}
161 
162 	if (sc & EC_OBF) {
163 		cmn_err(CE_NOTE, "!ec_wr: OBF already set");
164 	}
165 #endif
166 
167 	outb(ec.ec_sc, EC_WR);	/* output a write command */
168 	if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
169 		cmn_err(CE_NOTE, "!ec_wr:1: timed-out waiting "
170 		    "for IBF to clear");
171 		mutex_exit(&ec.ec_mutex);
172 		return (-1);
173 	}
174 
175 	outb(ec.ec_base, addr);	/* output addr */
176 	if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
177 		cmn_err(CE_NOTE, "!ec_wr:2: timed-out waiting "
178 		    "for IBF to clear");
179 		mutex_exit(&ec.ec_mutex);
180 		return (-1);
181 	}
182 
183 	outb(ec.ec_base, *val);	/* write data */
184 	if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
185 		cmn_err(CE_NOTE, "!ec_wr:3: timed-out waiting "
186 		    "for IBF to clear");
187 		mutex_exit(&ec.ec_mutex);
188 		return (-1);
189 	}
190 
191 	mutex_exit(&ec.ec_mutex);
192 	return (0);
193 }
194 
195 static int
196 ec_query(void)
197 {
198 	int	cnt, rv;
199 	uint8_t	sc;
200 
201 	mutex_enter(&ec.ec_mutex);
202 	outb(ec.ec_sc, EC_QR);	/* output a query command */
203 	if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
204 		cmn_err(CE_NOTE, "!ec_query:1: timed-out waiting "
205 		    "for IBF to clear");
206 		mutex_exit(&ec.ec_mutex);
207 		return (-1);
208 	}
209 
210 	if (ec_wait_obf_set(ec.ec_sc) < 0) {
211 		cmn_err(CE_NOTE, "!ec_query:1: timed-out waiting "
212 		    "for OBF to set");
213 		mutex_exit(&ec.ec_mutex);
214 		return (-1);
215 	}
216 
217 	rv = inb(ec.ec_base);
218 	mutex_exit(&ec.ec_mutex);
219 	return (rv);
220 }
221 
222 static ACPI_STATUS
223 ec_handler(UINT32 func, ACPI_PHYSICAL_ADDRESS addr, UINT32 width,
224 	    ACPI_INTEGER *val, void *context, void *regcontext)
225 {
226 	_NOTE(ARGUNUSED(context, regcontext))
227 	int tmp;
228 
229 	/*
230 	 * Add safety checks for BIOSes not strictly compliant
231 	 * with ACPI spec
232 	 */
233 	if ((width % 8) != 0) {
234 		cmn_err(CE_NOTE, "!ec_handler: width %d not multiple of 8",
235 		    width);
236 		return (AE_ERROR);
237 	}
238 
239 	if (width > 8) {
240 		cmn_err(CE_NOTE, "!ec_handler: width %d greater than 8", width);
241 		return (AE_ERROR);
242 	}
243 
244 	switch (func) {
245 	case ACPI_READ:
246 		tmp = ec_rd(addr);
247 		if (tmp < 0)
248 			return (AE_ERROR);
249 		*val = tmp;
250 		break;
251 	case ACPI_WRITE:
252 		if (ec_wr(addr, (uint8_t *)val) < 0)
253 			return (AE_ERROR);
254 		break;
255 	default:
256 		return (AE_ERROR);
257 	}
258 
259 	return (AE_OK);
260 }
261 
262 
263 static void
264 ec_gpe_callback(void *ctx)
265 {
266 	_NOTE(ARGUNUSED(ctx))
267 
268 	char		query_str[5];
269 	int		query;
270 
271 	if (!(inb(ec.ec_sc) & EC_SCI))
272 		return;
273 
274 	query = ec_query();
275 	if (query >= 0) {
276 		(void) snprintf(query_str, 5, "_Q%02X", (uint8_t)query);
277 		(void) AcpiEvaluateObject(ec.ec_obj, query_str, NULL, NULL);
278 	}
279 
280 }
281 
282 static UINT32
283 ec_gpe_handler(void *ctx)
284 {
285 	_NOTE(ARGUNUSED(ctx))
286 
287 	AcpiOsExecute(OSL_GPE_HANDLER, ec_gpe_callback, NULL);
288 	return (0);
289 }
290 
291 /*
292  * Busy-wait for IBF to clear
293  * return < 0 for time out, 0 for no error
294  */
295 static int
296 ec_wait_ibf_clear(int sc_addr)
297 {
298 	int	cnt;
299 
300 	cnt = 0;
301 	while (inb(sc_addr) & EC_IBF) {
302 		cnt += 1;
303 		drv_usecwait(10);
304 		if (cnt > 10000) {
305 			return (-1);
306 		}
307 	}
308 	return (0);
309 }
310 
311 /*
312  * Busy-wait for OBF to set
313  * return < 0 for time out, 0 for no error
314  */
315 static int
316 ec_wait_obf_set(int sc_addr)
317 {
318 	int	cnt;
319 
320 	cnt = 0;
321 	while (!(inb(sc_addr) & EC_OBF)) {
322 		cnt += 1;
323 		drv_usecwait(10);
324 		if (cnt > 10000) {
325 			return (-1);
326 		}
327 	}
328 	return (0);
329 }
330 
331 
332 
333 /*
334  * Called from AcpiWalkDevices() when an EC device is found
335  */
336 static ACPI_STATUS
337 acpica_install_ec(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv)
338 {
339 	_NOTE(ARGUNUSED(nest, context, rv))
340 
341 	int status, i;
342 	ACPI_STATUS res;
343 	ACPI_BUFFER buf, crs;
344 	ACPI_OBJECT *gpe_obj, *crs_obj;
345 	ACPI_INTEGER gpe;
346 	int io_port_cnt;
347 
348 	/*
349 	 * Save the one EC object we have
350 	 */
351 	ec.ec_obj = obj;
352 
353 	/*
354 	 * Find ec_base and ec_sc addresses
355 	 */
356 	crs.Length = ACPI_ALLOCATE_BUFFER;
357 	res = AcpiEvaluateObject(obj, "_CRS", NULL, &crs);
358 	if (ACPI_FAILURE(res)) {
359 		cmn_err(CE_WARN, "!acpica_install_ec: _CRS object evaluate"
360 		    "failed");
361 		return (AE_OK);
362 	}
363 	crs_obj = crs.Pointer;
364 	if (crs_obj->Type != ACPI_TYPE_BUFFER) {
365 		cmn_err(CE_WARN, "!acpica_install_ec: not a buffer");
366 		AcpiOsFree(crs.Pointer);
367 		return (AE_OK);
368 	}
369 
370 	for (i = 0, io_port_cnt = 0; i < crs_obj->Buffer.Length; i++) {
371 		io_port_des_t *io_port;
372 		uint8_t *tmp;
373 
374 		tmp = crs_obj->Buffer.Pointer + i;
375 		if (*tmp != IO_PORT_DES)
376 			continue;
377 		io_port = (io_port_des_t *)tmp;
378 		/*
379 		 * Assuming first port is ec_base and second is ec_sc
380 		 */
381 		if (io_port_cnt)
382 			ec.ec_sc = (io_port->min_base_hi << 8) |
383 			    io_port->min_base_lo;
384 		else
385 			ec.ec_base = (io_port->min_base_hi << 8) |
386 			    io_port->min_base_lo;
387 
388 		io_port_cnt++;
389 		/*
390 		 * Increment ahead to next struct.
391 		 */
392 		i += 7;
393 	}
394 	AcpiOsFree(crs.Pointer);
395 
396 	/*
397 	 * Drain the EC data register if something is left over from
398 	 * legacy mode
399 	 */
400 	if (inb(ec.ec_sc) & EC_OBF) {
401 #ifndef	DEBUG
402 		inb(ec.ec_base);	/* read and discard value */
403 #else
404 		cmn_err(CE_NOTE, "!EC had something: 0x%x\n", inb(ec.ec_base));
405 #endif
406 	}
407 
408 	/*
409 	 * Get GPE
410 	 */
411 	buf.Length = ACPI_ALLOCATE_BUFFER;
412 	/*
413 	 * grab contents of GPE object
414 	 */
415 	if (ACPI_FAILURE(AcpiEvaluateObject(obj, "_GPE", NULL, &buf))) {
416 		cmn_err(CE_WARN, "!acpica_install_ec: _GPE object evaluate"
417 		    "failed");
418 		return (AE_OK);
419 	}
420 	gpe_obj = buf.Pointer;
421 	if (gpe_obj->Type != ACPI_TYPE_INTEGER) {
422 		cmn_err(CE_WARN, "!acpica_install_ec: not an int");
423 		AcpiOsFree(buf.Pointer);
424 		return (AE_OK);
425 	}
426 	gpe = gpe_obj->Integer.Value;
427 	AcpiOsFree(buf.Pointer);
428 
429 	/*
430 	 * Initialize EC mutex here
431 	 */
432 	mutex_init(&ec.ec_mutex, NULL, MUTEX_DRIVER, NULL);
433 
434 	if (AcpiInstallAddressSpaceHandler(obj,
435 	    ACPI_ADR_SPACE_EC, &ec_handler, &ec_setup, NULL) != AE_OK) {
436 		cmn_err(CE_WARN, "!acpica: failed to add EC handler\n");
437 		mutex_destroy(&ec.ec_mutex);
438 		return (AE_ERROR);
439 	}
440 
441 	/*
442 	 * Enable EC GPE
443 	 */
444 	if ((status = AcpiInstallGpeHandler(NULL, gpe, ACPI_GPE_EDGE_TRIGGERED,
445 	    ec_gpe_handler, NULL)) != AE_OK) {
446 		cmn_err(CE_WARN, "!acpica: failed to install gpe handler status"
447 		    " = %d", status);
448 		/*
449 		 * don't return an error here - GPE won't work but the EC
450 		 * handler may be OK
451 		 */
452 	}
453 
454 	status = AcpiSetGpeType(NULL, gpe, ACPI_GPE_TYPE_RUNTIME);
455 	status = AcpiEnableGpe(NULL, gpe, ACPI_NOT_ISR);
456 
457 	return (AE_OK);
458 }
459 
460 #ifdef	DEBUG
461 /*ARGSUSED*/
462 static ACPI_STATUS
463 acpica_install_smbus_v1(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv)
464 {
465 
466 	cmn_err(CE_NOTE, "!acpica: found an SMBC Version 1.0\n");
467 	return (AE_OK);
468 }
469 
470 /*ARGSUSED*/
471 static ACPI_STATUS
472 acpica_install_smbus_v2(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv)
473 {
474 
475 	cmn_err(CE_NOTE, "!acpica: found an SMBC Version 2.0\n");
476 	return (AE_OK);
477 }
478 #endif	/* DEBUG */
479 
480 #ifdef	NOTYET
481 static void
482 prgas(ACPI_GENERIC_ADDRESS *gas)
483 {
484 	cmn_err(CE_CONT, "gas: %d %d %d %d %lx",
485 	    gas->AddressSpaceId, gas->RegisterBitWidth, gas->RegisterBitOffset,
486 	    gas->AccessWidth, (long)gas->Address);
487 }
488 
489 static void
490 acpica_probe_ecdt()
491 {
492 	EC_BOOT_RESOURCES *ecdt;
493 
494 
495 	if (AcpiGetFirmwareTable("ECDT", 1, ACPI_LOGICAL_ADDRESSING,
496 				(ACPI_TABLE_HEADER **) &ecdt) != AE_OK) {
497 		cmn_err(CE_NOTE, "!acpica: ECDT not found\n");
498 		return;
499 	}
500 
501 	cmn_err(CE_NOTE, "EcControl: ");
502 	prgas(&ecdt->EcControl);
503 
504 	cmn_err(CE_NOTE, "EcData: ");
505 	prgas(&ecdt->EcData);
506 }
507 #endif	/* NOTYET */
508 
509 void
510 acpica_ec_init(void)
511 {
512 #ifdef	NOTYET
513 	/*
514 	 * Search the ACPI tables for an ECDT; if
515 	 * found, use it to install an EC handler
516 	 */
517 	acpica_probe_ecdt();
518 #endif	/* NOTYET */
519 
520 	/*
521 	 * General model is: use GetDevices callback to install
522 	 * handler(s) when device is present.
523 	 */
524 	(void) AcpiGetDevices("PNP0C09", &acpica_install_ec, NULL, NULL);
525 #ifdef	DEBUG
526 	(void) AcpiGetDevices("ACPI0001", &acpica_install_smbus_v1, NULL, NULL);
527 	(void) AcpiGetDevices("ACPI0005", &acpica_install_smbus_v2, NULL, NULL);
528 #endif	/* DEBUG */
529 }
530