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