acpica_ec.c revision 7851:e828bbb1689c
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 */
45static int ec_wait_ibf_clear(int sc_addr);
46static 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 */
70struct 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 */
78typedef 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*/
93static ACPI_STATUS
94ec_setup(ACPI_HANDLE reg, UINT32 func, void *context, void **ret)
95{
96
97	return (AE_OK);
98}
99
100static int
101ec_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
146static int
147ec_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
193static int
194ec_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
220static ACPI_STATUS
221ec_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
261static void
262ec_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
280static UINT32
281ec_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 */
293static int
294ec_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 */
313static int
314ec_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 */
334static ACPI_STATUS
335acpica_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*/
448static ACPI_STATUS
449acpica_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*/
457static ACPI_STATUS
458acpica_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
467static void
468prgas(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
475static void
476acpica_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
494void
495acpica_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