xref: /titanic_51/usr/src/uts/intel/io/acpica/acpica_ec.c (revision 26f3cdf03f1adcc98f6d3d99843ee71e9229a8c0)
1ae115bc7Smrj /*
2ae115bc7Smrj  * CDDL HEADER START
3ae115bc7Smrj  *
4ae115bc7Smrj  * The contents of this file are subject to the terms of the
5ae115bc7Smrj  * Common Development and Distribution License (the "License").
6ae115bc7Smrj  * You may not use this file except in compliance with the License.
7ae115bc7Smrj  *
8ae115bc7Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9ae115bc7Smrj  * or http://www.opensolaris.org/os/licensing.
10ae115bc7Smrj  * See the License for the specific language governing permissions
11ae115bc7Smrj  * and limitations under the License.
12ae115bc7Smrj  *
13ae115bc7Smrj  * When distributing Covered Code, include this CDDL HEADER in each
14ae115bc7Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15ae115bc7Smrj  * If applicable, add the following below this CDDL HEADER, with the
16ae115bc7Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
17ae115bc7Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
18ae115bc7Smrj  *
19ae115bc7Smrj  * CDDL HEADER END
20ae115bc7Smrj  */
21ae115bc7Smrj /*
22a3463f0aSDana Myers  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23ae115bc7Smrj  * Use is subject to license terms.
24*26f3cdf0SGordon Ross  * Copyright 2011 Joyent, Inc.  All rights reserved.
25*26f3cdf0SGordon Ross  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
26ae115bc7Smrj  */
27ae115bc7Smrj /*
28ae115bc7Smrj  * Solaris x86 ACPI CA Embedded Controller operation region handler
29ae115bc7Smrj  */
30ae115bc7Smrj 
31ae115bc7Smrj #include <sys/file.h>
32ae115bc7Smrj #include <sys/errno.h>
33ae115bc7Smrj #include <sys/conf.h>
34ae115bc7Smrj #include <sys/modctl.h>
35ae115bc7Smrj #include <sys/open.h>
36ae115bc7Smrj #include <sys/stat.h>
37ae115bc7Smrj #include <sys/ddi.h>
38ae115bc7Smrj #include <sys/sunddi.h>
39ae115bc7Smrj #include <sys/note.h>
40a3463f0aSDana Myers #include <sys/atomic.h>
41ae115bc7Smrj 
42ae115bc7Smrj #include <sys/acpi/acpi.h>
43ae115bc7Smrj #include <sys/acpica.h>
44ae115bc7Smrj 
45ae115bc7Smrj /*
46ae115bc7Smrj  * EC status bits
47*26f3cdf0SGordon Ross  * Low to high
48*26f3cdf0SGordon Ross  *	Output buffer full?
49*26f3cdf0SGordon Ross  *	Input buffer full?
50*26f3cdf0SGordon Ross  *	<reserved>
51*26f3cdf0SGordon Ross  *	Data register is command byte?
52*26f3cdf0SGordon Ross  *	Burst mode enabled?
53*26f3cdf0SGordon Ross  *	SCI event?
54*26f3cdf0SGordon Ross  *	SMI event?
55*26f3cdf0SGordon Ross  *	<reserved>
56ae115bc7Smrj  */
57ae115bc7Smrj #define	EC_OBF	(0x01)
58*26f3cdf0SGordon Ross #define	EC_IBF	(0x02)
59*26f3cdf0SGordon Ross #define	EC_DRC	(0x08)
60*26f3cdf0SGordon Ross #define	EC_BME	(0x10)
61ae115bc7Smrj #define	EC_SCI	(0x20)
62*26f3cdf0SGordon Ross #define	EC_SMI	(0x40)
63ae115bc7Smrj 
64ae115bc7Smrj /*
65ae115bc7Smrj  * EC commands
66ae115bc7Smrj  */
67ae115bc7Smrj #define	EC_RD	(0x80)
68ae115bc7Smrj #define	EC_WR	(0x81)
69ae115bc7Smrj #define	EC_BE	(0x82)
70ae115bc7Smrj #define	EC_BD	(0x83)
71ae115bc7Smrj #define	EC_QR	(0x84)
72ae115bc7Smrj 
73ae115bc7Smrj #define	IO_PORT_DES (0x47)
74ae115bc7Smrj 
75ae115bc7Smrj /*
76ae115bc7Smrj  * EC softstate
77ae115bc7Smrj  */
78*26f3cdf0SGordon Ross static struct ec_softstate {
79*26f3cdf0SGordon Ross 	uint8_t	 ec_ok;		/* != 0 if we have ec_base, ec_sc */
80ae115bc7Smrj 	uint16_t ec_base;	/* base of EC I/O port - data */
81ae115bc7Smrj 	uint16_t ec_sc;		/* EC status/command */
82*26f3cdf0SGordon Ross 	ACPI_HANDLE ec_dev_hdl;	/* EC device handle */
83*26f3cdf0SGordon Ross 	ACPI_HANDLE ec_gpe_hdl;	/* GPE info */
84*26f3cdf0SGordon Ross 	ACPI_INTEGER ec_gpe_bit;
85ae115bc7Smrj 	kmutex_t    ec_mutex;	/* serialize access to EC */
86ae115bc7Smrj } ec;
87ae115bc7Smrj 
88ae115bc7Smrj /* I/O port range descriptor */
89ae115bc7Smrj typedef struct io_port_des {
90ae115bc7Smrj 	uint8_t type;
91ae115bc7Smrj 	uint8_t decode;
92ae115bc7Smrj 	uint8_t min_base_lo;
93ae115bc7Smrj 	uint8_t min_base_hi;
94ae115bc7Smrj 	uint8_t max_base_lo;
95ae115bc7Smrj 	uint8_t max_base_hi;
96ae115bc7Smrj 	uint8_t align;
97ae115bc7Smrj 	uint8_t len;
98ae115bc7Smrj } io_port_des_t;
99ae115bc7Smrj 
100ae115bc7Smrj /*
101*26f3cdf0SGordon Ross  * Patchable to ignore an ECDT, in case using that
102*26f3cdf0SGordon Ross  * causes problems on someone's system.
103*26f3cdf0SGordon Ross  */
104*26f3cdf0SGordon Ross int ec_ignore_ecdt = 0;
105*26f3cdf0SGordon Ross 
106*26f3cdf0SGordon Ross /*
107a3463f0aSDana Myers  * Patchable timeout values for EC input-buffer-full-clear
108a3463f0aSDana Myers  * and output-buffer-full-set. These are in 10uS units and
109a3463f0aSDana Myers  * default to 1 second.
110a3463f0aSDana Myers  */
111a3463f0aSDana Myers int ibf_clear_timeout = 100000;
112a3463f0aSDana Myers int obf_set_timeout = 100000;
113a3463f0aSDana Myers 
114a3463f0aSDana Myers /*
115*26f3cdf0SGordon Ross  * ACPI CA EC address space handler support functions
116ae115bc7Smrj  */
117ae115bc7Smrj 
118*26f3cdf0SGordon Ross /*
119*26f3cdf0SGordon Ross  * Busy-wait for IBF to clear
120*26f3cdf0SGordon Ross  * return < 0 for time out, 0 for no error
121*26f3cdf0SGordon Ross  */
122*26f3cdf0SGordon Ross static int
123*26f3cdf0SGordon Ross ec_wait_ibf_clear(int sc_addr)
124*26f3cdf0SGordon Ross {
125*26f3cdf0SGordon Ross 	int	cnt;
126*26f3cdf0SGordon Ross 
127*26f3cdf0SGordon Ross 	cnt = ibf_clear_timeout;
128*26f3cdf0SGordon Ross 	while (inb(sc_addr) & EC_IBF) {
129*26f3cdf0SGordon Ross 		if (cnt-- <= 0)
130*26f3cdf0SGordon Ross 			return (-1);
131*26f3cdf0SGordon Ross 		drv_usecwait(10);
132*26f3cdf0SGordon Ross 	}
133*26f3cdf0SGordon Ross 	return (0);
134*26f3cdf0SGordon Ross }
135*26f3cdf0SGordon Ross 
136*26f3cdf0SGordon Ross /*
137*26f3cdf0SGordon Ross  * Busy-wait for OBF to set
138*26f3cdf0SGordon Ross  * return < 0 for time out, 0 for no error
139*26f3cdf0SGordon Ross  */
140*26f3cdf0SGordon Ross static int
141*26f3cdf0SGordon Ross ec_wait_obf_set(int sc_addr)
142*26f3cdf0SGordon Ross {
143*26f3cdf0SGordon Ross 	int	cnt;
144*26f3cdf0SGordon Ross 
145*26f3cdf0SGordon Ross 	cnt = obf_set_timeout;
146*26f3cdf0SGordon Ross 	while (!(inb(sc_addr) & EC_OBF)) {
147*26f3cdf0SGordon Ross 		if (cnt-- <= 0)
148*26f3cdf0SGordon Ross 			return (-1);
149*26f3cdf0SGordon Ross 		drv_usecwait(10);
150*26f3cdf0SGordon Ross 	}
151*26f3cdf0SGordon Ross 	return (0);
152ae115bc7Smrj }
153ae115bc7Smrj 
154a3463f0aSDana Myers /*
155a3463f0aSDana Myers  * Only called from ec_handler(), which validates ec_ok
156a3463f0aSDana Myers  */
157ae115bc7Smrj static int
158ae115bc7Smrj ec_rd(int addr)
159ae115bc7Smrj {
160ae115bc7Smrj 	int	cnt, rv;
161ae115bc7Smrj 	uint8_t sc;
162ae115bc7Smrj 
163ae115bc7Smrj 	mutex_enter(&ec.ec_mutex);
164ae115bc7Smrj 	sc = inb(ec.ec_sc);
165ae115bc7Smrj 
166ae115bc7Smrj #ifdef	DEBUG
167ae115bc7Smrj 	if (sc & EC_IBF) {
168ae115bc7Smrj 		cmn_err(CE_NOTE, "!ec_rd: IBF already set");
169ae115bc7Smrj 	}
170ae115bc7Smrj 
171ae115bc7Smrj 	if (sc & EC_OBF) {
172ae115bc7Smrj 		cmn_err(CE_NOTE, "!ec_rd: OBF already set");
173ae115bc7Smrj 	}
174ae115bc7Smrj #endif
175ae115bc7Smrj 
176ae115bc7Smrj 	outb(ec.ec_sc, EC_RD);	/* output a read command */
177ae115bc7Smrj 	if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
178ae115bc7Smrj 		cmn_err(CE_NOTE, "!ec_rd:1: timed-out waiting "
179ae115bc7Smrj 		    "for IBF to clear");
180ae115bc7Smrj 		mutex_exit(&ec.ec_mutex);
181ae115bc7Smrj 		return (-1);
182ae115bc7Smrj 	}
183ae115bc7Smrj 
184ae115bc7Smrj 	outb(ec.ec_base, addr);	/* output addr */
185ae115bc7Smrj 	if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
186ae115bc7Smrj 		cmn_err(CE_NOTE, "!ec_rd:2: timed-out waiting "
187ae115bc7Smrj 		    "for IBF to clear");
188ae115bc7Smrj 		mutex_exit(&ec.ec_mutex);
189ae115bc7Smrj 		return (-1);
190ae115bc7Smrj 	}
191ae115bc7Smrj 	if (ec_wait_obf_set(ec.ec_sc) < 0) {
192ae115bc7Smrj 		cmn_err(CE_NOTE, "!ec_rd:1: timed-out waiting "
193ae115bc7Smrj 		    "for OBF to set");
194ae115bc7Smrj 		mutex_exit(&ec.ec_mutex);
195ae115bc7Smrj 		return (-1);
196ae115bc7Smrj 	}
197ae115bc7Smrj 
198ae115bc7Smrj 	rv = inb(ec.ec_base);
199ae115bc7Smrj 	mutex_exit(&ec.ec_mutex);
200ae115bc7Smrj 	return (rv);
201ae115bc7Smrj }
202ae115bc7Smrj 
203a3463f0aSDana Myers /*
204a3463f0aSDana Myers  * Only called from ec_handler(), which validates ec_ok
205a3463f0aSDana Myers  */
206ae115bc7Smrj static int
207*26f3cdf0SGordon Ross ec_wr(int addr, uint8_t val)
208ae115bc7Smrj {
209ae115bc7Smrj 	int	cnt;
210ae115bc7Smrj 	uint8_t sc;
211ae115bc7Smrj 
212ae115bc7Smrj 	mutex_enter(&ec.ec_mutex);
213ae115bc7Smrj 	sc = inb(ec.ec_sc);
214ae115bc7Smrj 
215ae115bc7Smrj #ifdef	DEBUG
216ae115bc7Smrj 	if (sc & EC_IBF) {
217ae115bc7Smrj 		cmn_err(CE_NOTE, "!ec_wr: IBF already set");
218ae115bc7Smrj 	}
219ae115bc7Smrj 
220ae115bc7Smrj 	if (sc & EC_OBF) {
221ae115bc7Smrj 		cmn_err(CE_NOTE, "!ec_wr: OBF already set");
222ae115bc7Smrj 	}
223ae115bc7Smrj #endif
224ae115bc7Smrj 
225ae115bc7Smrj 	outb(ec.ec_sc, EC_WR);	/* output a write command */
226ae115bc7Smrj 	if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
227ae115bc7Smrj 		cmn_err(CE_NOTE, "!ec_wr:1: timed-out waiting "
228ae115bc7Smrj 		    "for IBF to clear");
229ae115bc7Smrj 		mutex_exit(&ec.ec_mutex);
230ae115bc7Smrj 		return (-1);
231ae115bc7Smrj 	}
232ae115bc7Smrj 
233ae115bc7Smrj 	outb(ec.ec_base, addr);	/* output addr */
234ae115bc7Smrj 	if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
235ae115bc7Smrj 		cmn_err(CE_NOTE, "!ec_wr:2: timed-out waiting "
236ae115bc7Smrj 		    "for IBF to clear");
237ae115bc7Smrj 		mutex_exit(&ec.ec_mutex);
238ae115bc7Smrj 		return (-1);
239ae115bc7Smrj 	}
240ae115bc7Smrj 
241*26f3cdf0SGordon Ross 	outb(ec.ec_base, val);	/* write data */
242ae115bc7Smrj 	if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
243ae115bc7Smrj 		cmn_err(CE_NOTE, "!ec_wr:3: timed-out waiting "
244ae115bc7Smrj 		    "for IBF to clear");
245ae115bc7Smrj 		mutex_exit(&ec.ec_mutex);
246ae115bc7Smrj 		return (-1);
247ae115bc7Smrj 	}
248ae115bc7Smrj 
249ae115bc7Smrj 	mutex_exit(&ec.ec_mutex);
250ae115bc7Smrj 	return (0);
251ae115bc7Smrj }
252ae115bc7Smrj 
253a3463f0aSDana Myers /*
254a3463f0aSDana Myers  * Only called from ec_gpe_callback(), which validates ec_ok
255a3463f0aSDana Myers  */
256ae115bc7Smrj static int
257ae115bc7Smrj ec_query(void)
258ae115bc7Smrj {
259ae115bc7Smrj 	int	cnt, rv;
260ae115bc7Smrj 	uint8_t	sc;
261ae115bc7Smrj 
262ae115bc7Smrj 	mutex_enter(&ec.ec_mutex);
263ae115bc7Smrj 	outb(ec.ec_sc, EC_QR);	/* output a query command */
264ae115bc7Smrj 	if (ec_wait_ibf_clear(ec.ec_sc) < 0) {
265ae115bc7Smrj 		cmn_err(CE_NOTE, "!ec_query:1: timed-out waiting "
266ae115bc7Smrj 		    "for IBF to clear");
267ae115bc7Smrj 		mutex_exit(&ec.ec_mutex);
268ae115bc7Smrj 		return (-1);
269ae115bc7Smrj 	}
270ae115bc7Smrj 
271ae115bc7Smrj 	if (ec_wait_obf_set(ec.ec_sc) < 0) {
272ae115bc7Smrj 		cmn_err(CE_NOTE, "!ec_query:1: timed-out waiting "
273ae115bc7Smrj 		    "for OBF to set");
274ae115bc7Smrj 		mutex_exit(&ec.ec_mutex);
275ae115bc7Smrj 		return (-1);
276ae115bc7Smrj 	}
277ae115bc7Smrj 
278ae115bc7Smrj 	rv = inb(ec.ec_base);
279ae115bc7Smrj 	mutex_exit(&ec.ec_mutex);
280ae115bc7Smrj 	return (rv);
281ae115bc7Smrj }
282ae115bc7Smrj 
283*26f3cdf0SGordon Ross /*
284*26f3cdf0SGordon Ross  * ACPI CA EC address space handler
285*26f3cdf0SGordon Ross  * Requires: ec.ec_sc, ec.ec_base
286*26f3cdf0SGordon Ross  */
287ae115bc7Smrj static ACPI_STATUS
288ae115bc7Smrj ec_handler(UINT32 func, ACPI_PHYSICAL_ADDRESS addr, UINT32 width,
289*26f3cdf0SGordon Ross 	UINT64 *val, void *context, void *regcontext)
290ae115bc7Smrj {
291ae115bc7Smrj 	_NOTE(ARGUNUSED(context, regcontext))
292*26f3cdf0SGordon Ross 	int i, tw, tmp;
293ae115bc7Smrj 
294a3463f0aSDana Myers 	/* Guard against unexpected invocation */
295a3463f0aSDana Myers 	if (ec.ec_ok == 0)
296a3463f0aSDana Myers 		return (AE_ERROR);
297a3463f0aSDana Myers 
298ae115bc7Smrj 	/*
299ae115bc7Smrj 	 * Add safety checks for BIOSes not strictly compliant
300ae115bc7Smrj 	 * with ACPI spec
301ae115bc7Smrj 	 */
302*26f3cdf0SGordon Ross 	if ((width % 8) != 0) {
303a3463f0aSDana Myers 		cmn_err(CE_NOTE, "!ec_handler: invalid width %d", width);
304*26f3cdf0SGordon Ross 		return (AE_BAD_PARAMETER);
305*26f3cdf0SGordon Ross 	}
306*26f3cdf0SGordon Ross 	if (val == NULL) {
307*26f3cdf0SGordon Ross 		cmn_err(CE_NOTE, "!ec_handler: NULL value pointer");
308*26f3cdf0SGordon Ross 		return (AE_BAD_PARAMETER);
309ae115bc7Smrj 	}
310ae115bc7Smrj 
311*26f3cdf0SGordon Ross 	while (width > 0) {
312*26f3cdf0SGordon Ross 
313*26f3cdf0SGordon Ross 		/* One UINT64 *val at a time. */
314*26f3cdf0SGordon Ross 		tw = min(width, 64);
315*26f3cdf0SGordon Ross 
316*26f3cdf0SGordon Ross 		if (func == ACPI_READ)
317*26f3cdf0SGordon Ross 			*val = 0;
318*26f3cdf0SGordon Ross 
319*26f3cdf0SGordon Ross 		/* Do I/O of up to 64 bits */
320*26f3cdf0SGordon Ross 		for (i = 0; i < tw; i += 8, addr++) {
321ae115bc7Smrj 			switch (func) {
322ae115bc7Smrj 			case ACPI_READ:
323ae115bc7Smrj 				tmp = ec_rd(addr);
324ae115bc7Smrj 				if (tmp < 0)
325ae115bc7Smrj 					return (AE_ERROR);
326*26f3cdf0SGordon Ross 				*val |= ((UINT64)tmp) << i;
327ae115bc7Smrj 				break;
328ae115bc7Smrj 			case ACPI_WRITE:
329*26f3cdf0SGordon Ross 				tmp = ((*val) >> i) & 0xFF;
330*26f3cdf0SGordon Ross 				if (ec_wr(addr, (uint8_t)tmp) < 0)
331ae115bc7Smrj 					return (AE_ERROR);
332ae115bc7Smrj 				break;
333ae115bc7Smrj 			default:
334ae115bc7Smrj 				return (AE_ERROR);
335ae115bc7Smrj 			}
336*26f3cdf0SGordon Ross 		}
337*26f3cdf0SGordon Ross 		val++;
338*26f3cdf0SGordon Ross 		width -= tw;
339*26f3cdf0SGordon Ross 	}
340ae115bc7Smrj 
341ae115bc7Smrj 	return (AE_OK);
342ae115bc7Smrj }
343ae115bc7Smrj 
344*26f3cdf0SGordon Ross /*
345*26f3cdf0SGordon Ross  * Called via taskq entry enqueued by ec_gpe_handler,
346*26f3cdf0SGordon Ross  * which validates ec_ok
347*26f3cdf0SGordon Ross  */
348ae115bc7Smrj static void
349ae115bc7Smrj ec_gpe_callback(void *ctx)
350ae115bc7Smrj {
351ae115bc7Smrj 	_NOTE(ARGUNUSED(ctx))
352ae115bc7Smrj 	char		query_str[5];
353ae115bc7Smrj 	int		query;
354ae115bc7Smrj 
355ae115bc7Smrj 	if (!(inb(ec.ec_sc) & EC_SCI))
356*26f3cdf0SGordon Ross 		goto out;
357a3463f0aSDana Myers 
358ae115bc7Smrj 	query = ec_query();
359*26f3cdf0SGordon Ross 	if (query < 0)
360*26f3cdf0SGordon Ross 		goto out;
361ae115bc7Smrj 
362*26f3cdf0SGordon Ross 	(void) snprintf(query_str, 5, "_Q%02X", (uint8_t)query);
363*26f3cdf0SGordon Ross 	(void) AcpiEvaluateObject(ec.ec_dev_hdl, query_str, NULL, NULL);
364*26f3cdf0SGordon Ross 
365*26f3cdf0SGordon Ross out:
366*26f3cdf0SGordon Ross 	AcpiFinishGpe(ec.ec_gpe_hdl, ec.ec_gpe_bit);
367ae115bc7Smrj }
368ae115bc7Smrj 
369ae115bc7Smrj static UINT32
370*26f3cdf0SGordon Ross ec_gpe_handler(ACPI_HANDLE GpeDevice, UINT32 GpeNumber, void *ctx)
371ae115bc7Smrj {
372*26f3cdf0SGordon Ross 	_NOTE(ARGUNUSED(GpeDevice))
373*26f3cdf0SGordon Ross 	_NOTE(ARGUNUSED(GpeNumber))
374ae115bc7Smrj 	_NOTE(ARGUNUSED(ctx))
375ae115bc7Smrj 
376*26f3cdf0SGordon Ross 	/*
377*26f3cdf0SGordon Ross 	 * With ec_ok==0, we will not install a GPE handler,
378*26f3cdf0SGordon Ross 	 * so this is just paranoia.  But if this were to
379*26f3cdf0SGordon Ross 	 * happen somehow, don't add the taskq entry, and
380*26f3cdf0SGordon Ross 	 * tell the caller we're done with this GPE call.
381*26f3cdf0SGordon Ross 	 */
382*26f3cdf0SGordon Ross 	if (ec.ec_ok == 0)
383*26f3cdf0SGordon Ross 		return (ACPI_REENABLE_GPE);
384*26f3cdf0SGordon Ross 
385ae115bc7Smrj 	AcpiOsExecute(OSL_GPE_HANDLER, ec_gpe_callback, NULL);
386*26f3cdf0SGordon Ross 
387*26f3cdf0SGordon Ross 	/*
388*26f3cdf0SGordon Ross 	 * Returning zero tells the ACPI system that we will
389*26f3cdf0SGordon Ross 	 * handle this event asynchronously.
390*26f3cdf0SGordon Ross 	 */
391ae115bc7Smrj 	return (0);
392ae115bc7Smrj }
393ae115bc7Smrj 
394ae115bc7Smrj /*
395*26f3cdf0SGordon Ross  * Some systems describe the EC using an "ECDT" (table).
396*26f3cdf0SGordon Ross  * If we find one use it (unless ec_ignore_ecdt is set).
397*26f3cdf0SGordon Ross  * Modern systems don't provide an ECDT.
398ae115bc7Smrj  */
399*26f3cdf0SGordon Ross static ACPI_STATUS
400*26f3cdf0SGordon Ross ec_probe_ecdt(void)
401ae115bc7Smrj {
402*26f3cdf0SGordon Ross 	ACPI_TABLE_HEADER *th;
403*26f3cdf0SGordon Ross 	ACPI_TABLE_ECDT *ecdt;
404*26f3cdf0SGordon Ross 	ACPI_HANDLE	dev_hdl;
405*26f3cdf0SGordon Ross 	ACPI_STATUS	status;
406ae115bc7Smrj 
407*26f3cdf0SGordon Ross 	status = AcpiGetTable(ACPI_SIG_ECDT, 1, &th);
408*26f3cdf0SGordon Ross #ifndef DEBUG
409*26f3cdf0SGordon Ross 	if (status == AE_NOT_FOUND)
410*26f3cdf0SGordon Ross 		return (status);
411*26f3cdf0SGordon Ross #endif
412*26f3cdf0SGordon Ross 	if (ACPI_FAILURE(status)) {
413*26f3cdf0SGordon Ross 		cmn_err(CE_NOTE, "!acpica: ECDT not found");
414*26f3cdf0SGordon Ross 		return (status);
415ae115bc7Smrj 	}
416*26f3cdf0SGordon Ross 	if (ec_ignore_ecdt) {
417*26f3cdf0SGordon Ross 		/* pretend it was not found */
418*26f3cdf0SGordon Ross 		cmn_err(CE_NOTE, "!acpica: ECDT ignored");
419*26f3cdf0SGordon Ross 		return (AE_NOT_FOUND);
420*26f3cdf0SGordon Ross 	}
421*26f3cdf0SGordon Ross 
422*26f3cdf0SGordon Ross 	ecdt = (ACPI_TABLE_ECDT *)th;
423*26f3cdf0SGordon Ross 	if (ecdt->Control.BitWidth != 8 ||
424*26f3cdf0SGordon Ross 	    ecdt->Data.BitWidth != 8) {
425*26f3cdf0SGordon Ross 		cmn_err(CE_NOTE, "!acpica: bad ECDT I/O width");
426*26f3cdf0SGordon Ross 		return (AE_BAD_VALUE);
427*26f3cdf0SGordon Ross 	}
428*26f3cdf0SGordon Ross 	status = AcpiGetHandle(NULL, (char *)ecdt->Id, &dev_hdl);
429*26f3cdf0SGordon Ross 	if (ACPI_FAILURE(status)) {
430*26f3cdf0SGordon Ross 		cmn_err(CE_NOTE, "!acpica: no ECDT device handle");
431*26f3cdf0SGordon Ross 		return (status);
432ae115bc7Smrj 	}
433ae115bc7Smrj 
434ae115bc7Smrj 	/*
435*26f3cdf0SGordon Ross 	 * Success.  Save info for attach.
436ae115bc7Smrj 	 */
437*26f3cdf0SGordon Ross 	ec.ec_base = ecdt->Data.Address;
438*26f3cdf0SGordon Ross 	ec.ec_sc = ecdt->Control.Address;
439*26f3cdf0SGordon Ross 	ec.ec_dev_hdl = dev_hdl;
440*26f3cdf0SGordon Ross 	ec.ec_gpe_hdl = NULL;
441*26f3cdf0SGordon Ross 	ec.ec_gpe_bit = ecdt->Gpe;
442*26f3cdf0SGordon Ross 	ec.ec_ok = 1;
443ae115bc7Smrj 
444*26f3cdf0SGordon Ross #ifdef DEBUG
445*26f3cdf0SGordon Ross 	cmn_err(CE_NOTE, "!acpica:ec_probe_ecdt: success");
446*26f3cdf0SGordon Ross #endif
447ae115bc7Smrj 	return (0);
448ae115bc7Smrj }
449ae115bc7Smrj 
450ae115bc7Smrj /*
451ae115bc7Smrj  * Called from AcpiWalkDevices() when an EC device is found
452ae115bc7Smrj  */
453ae115bc7Smrj static ACPI_STATUS
454*26f3cdf0SGordon Ross ec_find(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv)
455ae115bc7Smrj {
456*26f3cdf0SGordon Ross 	_NOTE(ARGUNUSED(nest, rv))
457ae115bc7Smrj 
458*26f3cdf0SGordon Ross 	*((ACPI_HANDLE *)context) = obj;
459*26f3cdf0SGordon Ross 	return (AE_OK);
460*26f3cdf0SGordon Ross }
461ae115bc7Smrj 
462ae115bc7Smrj /*
463*26f3cdf0SGordon Ross  * Normal way to get the details about the EC,
464*26f3cdf0SGordon Ross  * by searching the name space.
465ae115bc7Smrj  */
466*26f3cdf0SGordon Ross static ACPI_STATUS
467*26f3cdf0SGordon Ross ec_probe_ns(void)
468*26f3cdf0SGordon Ross {
469*26f3cdf0SGordon Ross 	ACPI_HANDLE dev_hdl;
470*26f3cdf0SGordon Ross 	ACPI_BUFFER buf, crs;
471*26f3cdf0SGordon Ross 	ACPI_OBJECT *gpe_obj;
472*26f3cdf0SGordon Ross 	ACPI_HANDLE gpe_hdl;
473*26f3cdf0SGordon Ross 	ACPI_INTEGER gpe_bit;
474*26f3cdf0SGordon Ross 	ACPI_STATUS status;
475*26f3cdf0SGordon Ross 	int i, io_port_cnt;
476*26f3cdf0SGordon Ross 	uint16_t ec_sc, ec_base;
477*26f3cdf0SGordon Ross 
478*26f3cdf0SGordon Ross 	dev_hdl = NULL;
479*26f3cdf0SGordon Ross 	(void) AcpiGetDevices("PNP0C09", &ec_find, (void *)&dev_hdl, NULL);
480*26f3cdf0SGordon Ross 	if (dev_hdl == NULL) {
481*26f3cdf0SGordon Ross #ifdef DEBUG
482*26f3cdf0SGordon Ross 		/* Not an error, just no EC on this machine. */
483*26f3cdf0SGordon Ross 		cmn_err(CE_WARN, "!acpica:ec_probe_ns: "
484*26f3cdf0SGordon Ross 		    "PNP0C09 not found");
485*26f3cdf0SGordon Ross #endif
486*26f3cdf0SGordon Ross 		return (AE_NOT_FOUND);
487*26f3cdf0SGordon Ross 	}
488ae115bc7Smrj 
489ae115bc7Smrj 	/*
490ae115bc7Smrj 	 * Find ec_base and ec_sc addresses
491ae115bc7Smrj 	 */
492ae115bc7Smrj 	crs.Length = ACPI_ALLOCATE_BUFFER;
493*26f3cdf0SGordon Ross 	status = AcpiEvaluateObjectTyped(dev_hdl, "_CRS", NULL, &crs,
494*26f3cdf0SGordon Ross 	    ACPI_TYPE_BUFFER);
495*26f3cdf0SGordon Ross 	if (ACPI_FAILURE(status)) {
496*26f3cdf0SGordon Ross 		cmn_err(CE_WARN, "!acpica:ec_probe_ns: "
497*26f3cdf0SGordon Ross 		    "_CRS object evaluate failed");
498*26f3cdf0SGordon Ross 		return (status);
499ae115bc7Smrj 	}
500ae115bc7Smrj 
501db2bae30SDana Myers 	for (i = 0, io_port_cnt = 0;
502db2bae30SDana Myers 	    i < ((ACPI_OBJECT *)crs.Pointer)->Buffer.Length; i++) {
503ae115bc7Smrj 		io_port_des_t *io_port;
504ae115bc7Smrj 		uint8_t *tmp;
505ae115bc7Smrj 
506db2bae30SDana Myers 		tmp = ((ACPI_OBJECT *)crs.Pointer)->Buffer.Pointer + i;
507ae115bc7Smrj 		if (*tmp != IO_PORT_DES)
508ae115bc7Smrj 			continue;
509ae115bc7Smrj 		io_port = (io_port_des_t *)tmp;
510ae115bc7Smrj 		/*
511*26f3cdf0SGordon Ross 		 * first port is ec_base and second is ec_sc
512ae115bc7Smrj 		 */
513*26f3cdf0SGordon Ross 		if (io_port_cnt == 0)
514*26f3cdf0SGordon Ross 			ec_base = (io_port->min_base_hi << 8) |
515ae115bc7Smrj 			    io_port->min_base_lo;
516*26f3cdf0SGordon Ross 		if (io_port_cnt == 1)
517*26f3cdf0SGordon Ross 			ec_sc = (io_port->min_base_hi << 8) |
518ae115bc7Smrj 			    io_port->min_base_lo;
519ae115bc7Smrj 
520ae115bc7Smrj 		io_port_cnt++;
521ae115bc7Smrj 		/*
522ae115bc7Smrj 		 * Increment ahead to next struct.
523ae115bc7Smrj 		 */
524ae115bc7Smrj 		i += 7;
525ae115bc7Smrj 	}
526ae115bc7Smrj 	AcpiOsFree(crs.Pointer);
527*26f3cdf0SGordon Ross 	if (io_port_cnt < 2) {
528*26f3cdf0SGordon Ross 		cmn_err(CE_WARN, "!acpica:ec_probe_ns: "
529*26f3cdf0SGordon Ross 		    "_CRS parse failed");
530*26f3cdf0SGordon Ross 		return (AE_BAD_VALUE);
531*26f3cdf0SGordon Ross 	}
532*26f3cdf0SGordon Ross 
533*26f3cdf0SGordon Ross 	/*
534*26f3cdf0SGordon Ross 	 * Get the GPE info.
535*26f3cdf0SGordon Ross 	 */
536*26f3cdf0SGordon Ross 	buf.Length = ACPI_ALLOCATE_BUFFER;
537*26f3cdf0SGordon Ross 	status = AcpiEvaluateObject(dev_hdl, "_GPE", NULL, &buf);
538*26f3cdf0SGordon Ross 	if (ACPI_FAILURE(status)) {
539*26f3cdf0SGordon Ross 		cmn_err(CE_WARN, "!acpica:ec_probe_ns: "
540*26f3cdf0SGordon Ross 		    "_GPE object evaluate");
541*26f3cdf0SGordon Ross 		return (status);
542*26f3cdf0SGordon Ross 	}
543*26f3cdf0SGordon Ross 	gpe_obj = (ACPI_OBJECT *)buf.Pointer;
544*26f3cdf0SGordon Ross 	/*
545*26f3cdf0SGordon Ross 	 * process the GPE description
546*26f3cdf0SGordon Ross 	 */
547*26f3cdf0SGordon Ross 	switch (gpe_obj->Type) {
548*26f3cdf0SGordon Ross 	case ACPI_TYPE_INTEGER:
549*26f3cdf0SGordon Ross 		gpe_hdl = NULL;
550*26f3cdf0SGordon Ross 		gpe_bit = gpe_obj->Integer.Value;
551*26f3cdf0SGordon Ross 		break;
552*26f3cdf0SGordon Ross 	case ACPI_TYPE_PACKAGE:
553*26f3cdf0SGordon Ross 		if (gpe_obj->Package.Count != 2)
554*26f3cdf0SGordon Ross 			goto bad_gpe;
555*26f3cdf0SGordon Ross 		gpe_obj = gpe_obj->Package.Elements;
556*26f3cdf0SGordon Ross 		if (gpe_obj[1].Type != ACPI_TYPE_INTEGER)
557*26f3cdf0SGordon Ross 			goto bad_gpe;
558*26f3cdf0SGordon Ross 		gpe_hdl = gpe_obj[0].Reference.Handle;
559*26f3cdf0SGordon Ross 		gpe_bit = gpe_obj[1].Integer.Value;
560*26f3cdf0SGordon Ross 		break;
561*26f3cdf0SGordon Ross 	bad_gpe:
562*26f3cdf0SGordon Ross 	default:
563*26f3cdf0SGordon Ross 		status = AE_BAD_VALUE;
564*26f3cdf0SGordon Ross 		break;
565*26f3cdf0SGordon Ross 	}
566*26f3cdf0SGordon Ross 	AcpiOsFree(buf.Pointer);
567*26f3cdf0SGordon Ross 	if (ACPI_FAILURE(status)) {
568*26f3cdf0SGordon Ross 		cmn_err(CE_WARN, "!acpica:ec_probe_ns: "
569*26f3cdf0SGordon Ross 		    "_GPE parse failed");
570*26f3cdf0SGordon Ross 		return (status);
571*26f3cdf0SGordon Ross 	}
572*26f3cdf0SGordon Ross 
573*26f3cdf0SGordon Ross 	/*
574*26f3cdf0SGordon Ross 	 * Success.  Save info for attach.
575*26f3cdf0SGordon Ross 	 */
576*26f3cdf0SGordon Ross 	ec.ec_base = ec_base;
577*26f3cdf0SGordon Ross 	ec.ec_sc = ec_sc;
578*26f3cdf0SGordon Ross 	ec.ec_dev_hdl = dev_hdl;
579*26f3cdf0SGordon Ross 	ec.ec_gpe_hdl = gpe_hdl;
580*26f3cdf0SGordon Ross 	ec.ec_gpe_bit = gpe_bit;
581*26f3cdf0SGordon Ross 	ec.ec_ok = 1;
582*26f3cdf0SGordon Ross 
583*26f3cdf0SGordon Ross #ifdef DEBUG
584*26f3cdf0SGordon Ross 	cmn_err(CE_NOTE, "!acpica:ec_probe_ns: success");
585*26f3cdf0SGordon Ross #endif
586*26f3cdf0SGordon Ross 	return (0);
587*26f3cdf0SGordon Ross }
588*26f3cdf0SGordon Ross 
589*26f3cdf0SGordon Ross /*
590*26f3cdf0SGordon Ross  * Setup the Embedded Controller (EC) address space handler.
591*26f3cdf0SGordon Ross  * Entered only if one of the EC probe methods found an EC.
592*26f3cdf0SGordon Ross  */
593*26f3cdf0SGordon Ross static void
594*26f3cdf0SGordon Ross ec_init(void)
595*26f3cdf0SGordon Ross {
596*26f3cdf0SGordon Ross 	ACPI_STATUS rc;
597*26f3cdf0SGordon Ross 	int x;
598*26f3cdf0SGordon Ross 
599*26f3cdf0SGordon Ross 	/* paranoia */
600*26f3cdf0SGordon Ross 	if (ec.ec_ok == 0)
601*26f3cdf0SGordon Ross 		return;
602ae115bc7Smrj 
603ae115bc7Smrj 	/*
604ae115bc7Smrj 	 * Drain the EC data register if something is left over from
605ae115bc7Smrj 	 * legacy mode
606ae115bc7Smrj 	 */
607ae115bc7Smrj 	if (inb(ec.ec_sc) & EC_OBF) {
608*26f3cdf0SGordon Ross 		x = inb(ec.ec_base);	/* read and discard value */
609*26f3cdf0SGordon Ross #ifdef	DEBUG
610*26f3cdf0SGordon Ross 		cmn_err(CE_NOTE, "!EC had something: 0x%x", x);
611ae115bc7Smrj #endif
612ae115bc7Smrj 	}
613ae115bc7Smrj 
614ae115bc7Smrj 	/*
615*26f3cdf0SGordon Ross 	 * Install an "EC address space" handler.
616*26f3cdf0SGordon Ross 	 *
617*26f3cdf0SGordon Ross 	 * This call does a name space walk under the passed
618*26f3cdf0SGordon Ross 	 * object looking for child objects with an EC space
619*26f3cdf0SGordon Ross 	 * region for which to install this handler.  Using
620*26f3cdf0SGordon Ross 	 * the ROOT object makes sure we find them all.
621*26f3cdf0SGordon Ross 	 *
622*26f3cdf0SGordon Ross 	 * XXX: Some systems return an error from this call
623*26f3cdf0SGordon Ross 	 * after a partial success, i.e. where the NS walk
624*26f3cdf0SGordon Ross 	 * installs on some nodes and fails on other nodes.
625*26f3cdf0SGordon Ross 	 * In such cases, disabling the EC and GPE handlers
626*26f3cdf0SGordon Ross 	 * makes things worse, so just report the error and
627*26f3cdf0SGordon Ross 	 * leave the EC handler enabled.
628*26f3cdf0SGordon Ross 	 *
629*26f3cdf0SGordon Ross 	 * At one point, it seemed that doing this part of
630*26f3cdf0SGordon Ross 	 * EC setup earlier may help, which is why this is
631*26f3cdf0SGordon Ross 	 * now a separate function from ec_attach.  Someone
632*26f3cdf0SGordon Ross 	 * needs to figure our why some systems give us an
633*26f3cdf0SGordon Ross 	 * error return from this call.  (TODO)
634ae115bc7Smrj 	 */
635*26f3cdf0SGordon Ross 	rc = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
636*26f3cdf0SGordon Ross 	    ACPI_ADR_SPACE_EC, &ec_handler, NULL, NULL);
637*26f3cdf0SGordon Ross 	if (rc != AE_OK) {
638*26f3cdf0SGordon Ross 		cmn_err(CE_WARN, "!acpica:ec_init: "
639*26f3cdf0SGordon Ross 		    "install AS handler, rc=0x%x", rc);
640*26f3cdf0SGordon Ross 		return;
641a3463f0aSDana Myers 	}
642*26f3cdf0SGordon Ross #ifdef DEBUG
643*26f3cdf0SGordon Ross 	cmn_err(CE_NOTE, "!acpica:ec_init: success");
644*26f3cdf0SGordon Ross #endif
645*26f3cdf0SGordon Ross }
646*26f3cdf0SGordon Ross 
647*26f3cdf0SGordon Ross /*
648*26f3cdf0SGordon Ross  * Attach the EC General-Purpose Event (GPE) handler.
649*26f3cdf0SGordon Ross  */
650*26f3cdf0SGordon Ross static void
651*26f3cdf0SGordon Ross ec_attach(void)
652*26f3cdf0SGordon Ross {
653*26f3cdf0SGordon Ross 	ACPI_STATUS rc;
654*26f3cdf0SGordon Ross 
655*26f3cdf0SGordon Ross 	/*
656*26f3cdf0SGordon Ross 	 * Guard against call without probe results.
657*26f3cdf0SGordon Ross 	 */
658*26f3cdf0SGordon Ross 	if (ec.ec_ok == 0) {
659*26f3cdf0SGordon Ross 		cmn_err(CE_WARN, "!acpica:ec_attach: "
660*26f3cdf0SGordon Ross 		    "no EC device found");
661*26f3cdf0SGordon Ross 		return;
662*26f3cdf0SGordon Ross 	}
663*26f3cdf0SGordon Ross 
664*26f3cdf0SGordon Ross 	/*
665*26f3cdf0SGordon Ross 	 * Install the GPE handler and enable it.
666*26f3cdf0SGordon Ross 	 */
667*26f3cdf0SGordon Ross 	rc = AcpiInstallGpeHandler(ec.ec_gpe_hdl, ec.ec_gpe_bit,
668*26f3cdf0SGordon Ross 	    ACPI_GPE_EDGE_TRIGGERED, ec_gpe_handler, NULL);
669*26f3cdf0SGordon Ross 	if (rc != AE_OK) {
670*26f3cdf0SGordon Ross 		cmn_err(CE_WARN, "!acpica:ec_attach: "
671*26f3cdf0SGordon Ross 		    "install GPE handler, rc=0x%x", rc);
672*26f3cdf0SGordon Ross 		goto errout;
673*26f3cdf0SGordon Ross 	}
674*26f3cdf0SGordon Ross 
675*26f3cdf0SGordon Ross 	rc = AcpiEnableGpe(ec.ec_gpe_hdl, ec.ec_gpe_bit);
676*26f3cdf0SGordon Ross 	if (rc != AE_OK) {
677*26f3cdf0SGordon Ross 		cmn_err(CE_WARN, "!acpica:ec_attach: "
678*26f3cdf0SGordon Ross 		    "enable GPE handler, rc=0x%x", rc);
679*26f3cdf0SGordon Ross 		goto errout;
680*26f3cdf0SGordon Ross 	}
681*26f3cdf0SGordon Ross 
682*26f3cdf0SGordon Ross #ifdef DEBUG
683*26f3cdf0SGordon Ross 	cmn_err(CE_NOTE, "!acpica:ec_attach: success");
684*26f3cdf0SGordon Ross #endif
685*26f3cdf0SGordon Ross 	return;
686*26f3cdf0SGordon Ross 
687*26f3cdf0SGordon Ross errout:
688*26f3cdf0SGordon Ross 	AcpiRemoveGpeHandler(ec.ec_gpe_hdl, ec.ec_gpe_bit,
689*26f3cdf0SGordon Ross 	    ec_gpe_handler);
690*26f3cdf0SGordon Ross }
691*26f3cdf0SGordon Ross 
692*26f3cdf0SGordon Ross /*
693*26f3cdf0SGordon Ross  * System Management Bus Controller (SMBC)
694*26f3cdf0SGordon Ross  * These also go through the EC.
695*26f3cdf0SGordon Ross  * (not yet supported)
696*26f3cdf0SGordon Ross  */
697*26f3cdf0SGordon Ross static void
698*26f3cdf0SGordon Ross smbus_attach(void)
699*26f3cdf0SGordon Ross {
700*26f3cdf0SGordon Ross #ifdef	DEBUG
701*26f3cdf0SGordon Ross 	ACPI_HANDLE obj;
702*26f3cdf0SGordon Ross 
703*26f3cdf0SGordon Ross 	obj = NULL;
704*26f3cdf0SGordon Ross 	(void) AcpiGetDevices("ACPI0001", &ec_find, (void *)&obj, NULL);
705*26f3cdf0SGordon Ross 	if (obj != NULL) {
706*26f3cdf0SGordon Ross 		cmn_err(CE_NOTE, "!acpica: found an SMBC Version 1.0");
707*26f3cdf0SGordon Ross 	}
708*26f3cdf0SGordon Ross 
709*26f3cdf0SGordon Ross 	obj = NULL;
710*26f3cdf0SGordon Ross 	(void) AcpiGetDevices("ACPI0005", &ec_find, (void *)&obj, NULL);
711*26f3cdf0SGordon Ross 	if (obj != NULL) {
712*26f3cdf0SGordon Ross 		cmn_err(CE_NOTE, "!acpica: found an SMBC Version 2.0");
713*26f3cdf0SGordon Ross 	}
714*26f3cdf0SGordon Ross #endif	/* DEBUG */
715*26f3cdf0SGordon Ross }
716*26f3cdf0SGordon Ross 
717*26f3cdf0SGordon Ross /*
718*26f3cdf0SGordon Ross  * Initialize the EC, if present.
719*26f3cdf0SGordon Ross  */
720*26f3cdf0SGordon Ross void
721*26f3cdf0SGordon Ross acpica_ec_init(void)
722*26f3cdf0SGordon Ross {
723*26f3cdf0SGordon Ross 	ACPI_STATUS rc;
724ae115bc7Smrj 
725ae115bc7Smrj 	/*
726ae115bc7Smrj 	 * Initialize EC mutex here
727ae115bc7Smrj 	 */
728ae115bc7Smrj 	mutex_init(&ec.ec_mutex, NULL, MUTEX_DRIVER, NULL);
729ae115bc7Smrj 
730a3463f0aSDana Myers 	/*
731*26f3cdf0SGordon Ross 	 * First search the ACPI tables for an ECDT, and
732*26f3cdf0SGordon Ross 	 * if not found, search the name space for it.
733a3463f0aSDana Myers 	 */
734*26f3cdf0SGordon Ross 	rc = ec_probe_ecdt();
735*26f3cdf0SGordon Ross 	if (ACPI_FAILURE(rc))
736*26f3cdf0SGordon Ross 		rc = ec_probe_ns();
737*26f3cdf0SGordon Ross 	if (ACPI_SUCCESS(rc)) {
738*26f3cdf0SGordon Ross 		ec_init();
739*26f3cdf0SGordon Ross 		ec_attach();
740ae115bc7Smrj 	}
741*26f3cdf0SGordon Ross 	smbus_attach();
742ae115bc7Smrj }
743