xref: /illumos-gate/usr/src/uts/i86pc/io/psm/psm_common.c (revision 3db86aab)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/cmn_err.h>
31 #include <sys/promif.h>
32 #include <sys/acpi/acpi.h>
33 #include <sys/acpica.h>
34 #include <sys/sunddi.h>
35 #include <sys/ddi.h>
36 #include <sys/ddi_impldefs.h>
37 #include <sys/pci.h>
38 #include <sys/debug.h>
39 #include <sys/psm_common.h>
40 #include <sys/sunndi.h>
41 #include <sys/ksynch.h>
42 
43 /*
44  * ACPI Poweroff patchable options in acpi_poweroff_opt for broken BIOS
45  * workarounds.
46  */
47 #define	ACPI_PO_CLRWAK	0x0001
48 #define	ACPI_PO_CLRALL	0x0002
49 #define	ACPI_PO_RSTGPE	0x0004
50 #define	ACPI_PO_DISARB	0x0008
51 #define	ACPI_PO_2NDTRY	0x0010
52 
53 /* Global configurables */
54 
55 char *psm_module_name;	/* used to store name of psm module */
56 
57 /*
58  * acpi_irq_check_elcr: when set elcr will also be consulted for building
59  * the reserved irq list.  When 0 (false), the existing state of the ELCR
60  * is ignored when selecting a vector during IRQ translation, and the ELCR
61  * is programmed to the proper setting for the type of bus (level-triggered
62  * for PCI, edge-triggered for non-PCI).  When non-zero (true), vectors
63  * set to edge-mode will not be used when in PIC-mode.  The default value
64  * is 0 (false).  Note that ACPI's SCI vector is always set to conform to
65  * ACPI-specification regardless of this.
66  *
67  */
68 int acpi_irq_check_elcr = 0;
69 
70 /*
71  * acpi_s5_slp_typ:
72  * If >= 0 then override the \_S5 parameter return value. This is useful
73  * for systems with broken \_S5 methods which return the wrong value for
74  * the chipset in use.
75  */
76 int acpi_s5_slp_typ = -1;
77 int acpi_s5_slp_typ2 = -1;	/* second parameter (only patch if different) */
78 int acpi_poweroff_opt = 0;	/* patchable poweroff options */
79 
80 int psm_verbose = 0;
81 
82 #define	PSM_VERBOSE_IRQ(fmt)	\
83 		if (psm_verbose & PSM_VERBOSE_IRQ_FLAG) \
84 			cmn_err fmt;
85 
86 #define	PSM_VERBOSE_POWEROFF(fmt)  \
87 		if (psm_verbose & PSM_VERBOSE_POWEROFF_FLAG || \
88 		    psm_verbose & PSM_VERBOSE_POWEROFF_PAUSE_FLAG) \
89 			prom_printf fmt;
90 
91 #define	PSM_VERBOSE_POWEROFF_PAUSE(fmt) \
92 		if (psm_verbose & PSM_VERBOSE_POWEROFF_FLAG || \
93 		    psm_verbose & PSM_VERBOSE_POWEROFF_PAUSE_FLAG) {\
94 			prom_printf fmt; \
95 			if (psm_verbose & PSM_VERBOSE_POWEROFF_PAUSE_FLAG) \
96 				(void) goany(); \
97 		}
98 
99 
100 /* Local storage */
101 static ACPI_HANDLE acpi_sbobj = NULL;
102 static kmutex_t acpi_irq_cache_mutex;
103 
104 /*
105  * irq_cache_table is a list that serves a two-key cache. It is used
106  * as a pci busid/devid/ipin <-> irq cache and also as a acpi
107  * interrupt lnk <-> irq cache.
108  */
109 static irq_cache_t *irq_cache_table;
110 
111 #define	IRQ_CACHE_INITLEN	20
112 static int irq_cache_len = 0;
113 static int irq_cache_valid = 0;
114 
115 static int acpi_get_gsiv(dev_info_t *dip, ACPI_HANDLE pciobj, int devno,
116 	int ipin, int *pci_irqp, iflag_t *iflagp,  acpi_psm_lnk_t *acpipsmlnkp);
117 
118 static int acpi_eval_lnk(dev_info_t *dip, char *lnkname,
119     int *pci_irqp, iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp);
120 
121 static int acpi_get_irq_lnk_cache_ent(ACPI_HANDLE lnkobj, int *pci_irqp,
122     iflag_t *intr_flagp);
123 
124 extern int goany(void);
125 
126 
127 #define	NEXT_PRT_ITEM(p)	\
128 		(ACPI_PCI_ROUTING_TABLE *)(((char *)(p)) + (p)->Length)
129 
130 static int
131 acpi_get_gsiv(dev_info_t *dip, ACPI_HANDLE pciobj, int devno, int ipin,
132     int *pci_irqp, iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp)
133 {
134 	ACPI_BUFFER rb;
135 	ACPI_PCI_ROUTING_TABLE *prtp;
136 	int status;
137 	int dev_adr;
138 
139 	/*
140 	 * Get the IRQ routing table
141 	 */
142 	rb.Pointer = NULL;
143 	rb.Length = ACPI_ALLOCATE_BUFFER;
144 	if (AcpiGetIrqRoutingTable(pciobj, &rb) != AE_OK) {
145 		return (ACPI_PSM_FAILURE);
146 	}
147 
148 	status = ACPI_PSM_FAILURE;
149 	dev_adr = (devno << 16 | 0xffff);
150 	for (prtp = rb.Pointer; prtp->Length != 0; prtp = NEXT_PRT_ITEM(prtp)) {
151 		/* look until a matching dev/pin is found */
152 		if (dev_adr != prtp->Address || ipin != prtp->Pin)
153 			continue;
154 
155 		/* NULL Source name means index is GSIV */
156 		if (*prtp->Source == 0) {
157 			intr_flagp->intr_el = TRIGGER_LEVEL;
158 			intr_flagp->intr_po = POLARITY_ACTIVE_LOW;
159 			ASSERT(pci_irqp != NULL);
160 			*pci_irqp = prtp->SourceIndex;
161 			status = ACPI_PSM_SUCCESS;
162 		} else
163 			status = acpi_eval_lnk(dip, prtp->Source, pci_irqp,
164 			    intr_flagp, acpipsmlnkp);
165 
166 		break;
167 
168 	}
169 
170 	AcpiOsFree(rb.Pointer);
171 	return (status);
172 }
173 
174 /*
175  *
176  * If the interrupt link device is already configured,
177  * stores polarity and sensitivity in the structure pointed to by
178  * intr_flagp, and irqno in the value pointed to by pci_irqp.
179  *
180  * Returns:
181  *	ACPI_PSM_SUCCESS if the interrupt link device is already configured.
182  *	ACPI_PSM_PARTIAL if configuration is needed.
183  * 	ACPI_PSM_FAILURE in case of error.
184  *
185  * When two devices share the same interrupt link device, and the
186  * link device is already configured (i.e. found in the irq cache)
187  * we need to use the already configured irq instead of reconfiguring
188  * the link device.
189  */
190 static int
191 acpi_eval_lnk(dev_info_t *dip, char *lnkname, int *pci_irqp,
192 iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp)
193 {
194 	ACPI_HANDLE	tmpobj;
195 	ACPI_HANDLE	lnkobj;
196 	int status;
197 
198 	/*
199 	 * Convert the passed-in link device name to a handle
200 	 */
201 	if (AcpiGetHandle(NULL, lnkname, &lnkobj) != AE_OK) {
202 		return (ACPI_PSM_FAILURE);
203 	}
204 
205 	/*
206 	 * Assume that the link device is invalid if no _CRS method
207 	 * exists, since _CRS method is a required method
208 	 */
209 	if (AcpiGetHandle(lnkobj, "_CRS", &tmpobj) != AE_OK) {
210 		return (ACPI_PSM_FAILURE);
211 	}
212 
213 	ASSERT(acpipsmlnkp != NULL);
214 	acpipsmlnkp->lnkobj = lnkobj;
215 	if ((acpi_get_irq_lnk_cache_ent(lnkobj, pci_irqp, intr_flagp)) ==
216 	    ACPI_PSM_SUCCESS) {
217 		PSM_VERBOSE_IRQ((CE_CONT, "!psm: link object found from cache "
218 		    " for device %s, instance #%d, irq no %d\n",
219 		    ddi_get_name(dip), ddi_get_instance(dip), *pci_irqp));
220 		return (ACPI_PSM_SUCCESS);
221 	} else {
222 		if (acpica_eval_int(lnkobj, "_STA", &status) == AE_OK) {
223 			acpipsmlnkp->device_status = (uchar_t)status;
224 		}
225 
226 		return (ACPI_PSM_PARTIAL);
227 	}
228 }
229 
230 int
231 acpi_psm_init(char *module_name, int verbose_flags)
232 {
233 	psm_module_name = module_name;
234 
235 	psm_verbose = verbose_flags;
236 
237 	if (AcpiGetHandle(NULL, "\\_SB", &acpi_sbobj) != AE_OK) {
238 		cmn_err(CE_WARN, "!psm: get _SB failed");
239 		return (ACPI_PSM_FAILURE);
240 	}
241 
242 	mutex_init(&acpi_irq_cache_mutex, NULL, MUTEX_DEFAULT, NULL);
243 
244 	return (ACPI_PSM_SUCCESS);
245 
246 }
247 
248 /*
249  * Return bus/dev/fn for PCI dip (note: not the parent "pci" node).
250  */
251 
252 int
253 get_bdf(dev_info_t *dip, int *bus, int *device, int *func)
254 {
255 	pci_regspec_t *pci_rp;
256 	int len;
257 
258 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
259 	    "reg", (int **)&pci_rp, (uint_t *)&len) != DDI_SUCCESS)
260 		return (-1);
261 
262 	if (len < (sizeof (pci_regspec_t) / sizeof (int))) {
263 		ddi_prop_free(pci_rp);
264 		return (-1);
265 	}
266 	if (bus != NULL)
267 		*bus = (int)PCI_REG_BUS_G(pci_rp->pci_phys_hi);
268 	if (device != NULL)
269 		*device = (int)PCI_REG_DEV_G(pci_rp->pci_phys_hi);
270 	if (func != NULL)
271 		*func = (int)PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
272 	ddi_prop_free(pci_rp);
273 	return (0);
274 }
275 
276 
277 /*
278  * Build the reserved ISA irq list, and store it in the table pointed to by
279  * reserved_irqs_table. The caller is responsible for allocating this table
280  * with a minimum of MAX_ISA_IRQ + 1 entries.
281  *
282  * The routine looks in the device tree at the subtree rooted at /isa
283  * for each of the devices under that node, if an interrupts property
284  * is present, its values are used to "reserve" irqs so that later ACPI
285  * configuration won't choose those irqs.
286  *
287  * In addition, if acpi_irq_check_elcr is set, will use ELCR register
288  * to identify reserved IRQs.
289  */
290 void
291 build_reserved_irqlist(uchar_t *reserved_irqs_table)
292 {
293 	dev_info_t *isanode = ddi_find_devinfo("isa", -1, 0);
294 	dev_info_t *isa_child = 0;
295 	int i;
296 	uint_t	elcrval;
297 
298 	/* Initialize the reserved ISA IRQs: */
299 	for (i = 0; i <= MAX_ISA_IRQ; i++)
300 		reserved_irqs_table[i] = 0;
301 
302 	if (acpi_irq_check_elcr) {
303 
304 		elcrval = (inb(ELCR_PORT2) << 8) | (inb(ELCR_PORT1));
305 		if (ELCR_EDGE(elcrval, 0) && ELCR_EDGE(elcrval, 1) &&
306 		    ELCR_EDGE(elcrval, 2) && ELCR_EDGE(elcrval, 8) &&
307 		    ELCR_EDGE(elcrval, 13)) {
308 			/* valid ELCR */
309 			for (i = 0; i <= MAX_ISA_IRQ; i++)
310 				if (!ELCR_LEVEL(elcrval, i))
311 					reserved_irqs_table[i] = 1;
312 		}
313 	}
314 
315 	/* always check the isa devinfo nodes */
316 
317 	if (isanode != 0) { /* Found ISA */
318 		uint_t intcnt;		/* Interrupt count */
319 		int *intrs;		/* Interrupt values */
320 
321 		/* Load first child: */
322 		isa_child = ddi_get_child(isanode);
323 		while (isa_child != 0) { /* Iterate over /isa children */
324 			/* if child has any interrupts, save them */
325 			if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, isa_child,
326 			    DDI_PROP_DONTPASS, "interrupts", &intrs, &intcnt)
327 			    == DDI_PROP_SUCCESS) {
328 				/*
329 				 * iterate over child interrupt list, adding
330 				 * them to the reserved irq list
331 				 */
332 				while (intcnt-- > 0) {
333 					/*
334 					 * Each value MUST be <= MAX_ISA_IRQ
335 					 */
336 
337 					if ((intrs[intcnt] > MAX_ISA_IRQ) ||
338 					    (intrs[intcnt] < 0))
339 						continue;
340 
341 					reserved_irqs_table[intrs[intcnt]] = 1;
342 				}
343 				ddi_prop_free(intrs);
344 			}
345 			isa_child = ddi_get_next_sibling(isa_child);
346 		}
347 		/* The isa node was held by ddi_find_devinfo, so release it */
348 		ndi_rele_devi(isanode);
349 	}
350 
351 	/*
352 	 * Reserve IRQ14 & IRQ15 for IDE.  It shouldn't be hard-coded
353 	 * here but there's no other way to find the irqs for
354 	 * legacy-mode ata (since it's hard-coded in pci-ide also).
355 	 */
356 	reserved_irqs_table[14] = 1;
357 	reserved_irqs_table[15] = 1;
358 }
359 
360 /*
361  * Examine devinfo node to determine if it is a PCI-PCI bridge
362  *
363  * Returns:
364  *	0 if not a bridge or error
365  *	1 if a bridge
366  */
367 static int
368 psm_is_pci_bridge(dev_info_t *dip)
369 {
370 	ddi_acc_handle_t cfg_handle;
371 	int rv = 0;
372 
373 	if (pci_config_setup(dip, &cfg_handle) == DDI_SUCCESS) {
374 		rv = ((pci_config_get8(cfg_handle, PCI_CONF_BASCLASS) ==
375 		    PCI_CLASS_BRIDGE) && (pci_config_get8(cfg_handle,
376 		    PCI_CONF_SUBCLASS) == PCI_BRIDGE_PCI));
377 		pci_config_teardown(&cfg_handle);
378 	}
379 
380 	return (rv);
381 }
382 
383 
384 /*
385  * Examines ACPI node for presence of _PRT object
386  *
387  * Returns:
388  *	0 if no _PRT or error
389  *	1 if _PRT is present
390  */
391 static int
392 psm_node_has_prt(ACPI_HANDLE *ah)
393 {
394 	ACPI_HANDLE rh;
395 
396 	return (AcpiGetHandle(ah, "_PRT", &rh) == AE_OK);
397 }
398 
399 
400 /*
401  * Look first for an ACPI PCI bus node matching busid, then for a _PRT on the
402  * parent node; then drop into the bridge-chasing code (which will also
403  * look for _PRTs on the way up the tree of bridges)
404  *
405  * Stores polarity and sensitivity in the structure pointed to by
406  * intr_flagp, and irqno in the value pointed to by pci_irqp.  *
407  * Returns:
408  *  	ACPI_PSM_SUCCESS on success.
409  *	ACPI_PSM_PARTIAL to indicate need to configure the interrupt
410  *	link device.
411  * 	ACPI_PSM_FAILURE  if an error prevented the system from
412  *	obtaining irq information for dip.
413  */
414 int
415 acpi_translate_pci_irq(dev_info_t *dip, int ipin, int *pci_irqp,
416     iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp)
417 {
418 	ACPI_HANDLE pciobj;
419 	int status = AE_ERROR;
420 	dev_info_t *curdip, *parentdip;
421 	int curpin, curbus, curdev;
422 
423 
424 	curpin = ipin;
425 	curdip = dip;
426 	while (curdip != ddi_root_node()) {
427 		parentdip = ddi_get_parent(curdip);
428 		ASSERT(parentdip != NULL);
429 
430 		if (get_bdf(curdip, &curbus, &curdev, NULL) != 0) {
431 			break;
432 		}
433 
434 		status = acpica_find_pciobj(parentdip, &pciobj);
435 		if ((status == AE_OK) && psm_node_has_prt(pciobj)) {
436 			return (acpi_get_gsiv(curdip, pciobj, curdev, curpin,
437 			    pci_irqp, intr_flagp, acpipsmlnkp));
438 		}
439 
440 		/* if we got here, we need to traverse a bridge upwards */
441 		if (!psm_is_pci_bridge(parentdip))
442 			break;
443 
444 		/*
445 		 * This is the rotating scheme that Compaq is using
446 		 * and documented in the PCI-PCI spec.  Also, if the
447 		 * PCI-PCI bridge is behind another PCI-PCI bridge,
448 		 * then it needs to keep ascending until an interrupt
449 		 * entry is found or the top is reached
450 		 */
451 		curpin = (curdev + curpin) % PCI_INTD;
452 		curdip = parentdip;
453 	}
454 
455 	/*
456 	 * We should never, ever get here; didn't find a _PRT
457 	 */
458 	return (ACPI_PSM_FAILURE);
459 }
460 
461 /*
462  * Sets the irq resource of the lnk object to the requested irq value.
463  *
464  * Returns ACPI_PSM_SUCCESS on success, ACPI_PSM_FAILURE upon failure.
465  */
466 int
467 acpi_set_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int irq)
468 {
469 	ACPI_BUFFER	rsb;
470 	ACPI_RESOURCE	*resp;
471 	ACPI_RESOURCE	*srsp;
472 	ACPI_HANDLE lnkobj;
473 	int srs_len, status;
474 
475 	ASSERT(acpipsmlnkp != NULL);
476 
477 	lnkobj = acpipsmlnkp->lnkobj;
478 
479 	/*
480 	 * Fetch the possible resources for the link
481 	 */
482 
483 	rsb.Pointer = NULL;
484 	rsb.Length = ACPI_ALLOCATE_BUFFER;
485 	status = AcpiGetPossibleResources(lnkobj, &rsb);
486 	if (status != AE_OK) {
487 		cmn_err(CE_WARN, "!psm: set_irq: _PRS failed");
488 		return (ACPI_PSM_FAILURE);
489 	}
490 
491 	/*
492 	 * Find an IRQ resource descriptor to use as template
493 	 */
494 	srsp = NULL;
495 	for (resp = rsb.Pointer; resp->Type != ACPI_RESOURCE_TYPE_END_TAG;
496 	    resp = ACPI_NEXT_RESOURCE(resp)) {
497 		if ((resp->Type == ACPI_RESOURCE_TYPE_IRQ) ||
498 		    (resp->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ)) {
499 			ACPI_RESOURCE *endtag;
500 			/*
501 			 * Allocate enough room for this resource entry
502 			 * and one end tag following it
503 			 */
504 			srs_len = resp->Length + sizeof (*endtag);
505 			srsp = kmem_zalloc(srs_len, KM_SLEEP);
506 			bcopy(resp, srsp, resp->Length);
507 			endtag = ACPI_NEXT_RESOURCE(srsp);
508 			endtag->Type = ACPI_RESOURCE_TYPE_END_TAG;
509 			endtag->Length = 0;
510 			break;	/* drop out of the loop */
511 		}
512 	}
513 
514 	/*
515 	 * We're done with the PRS values, toss 'em lest we forget
516 	 */
517 	AcpiOsFree(rsb.Pointer);
518 
519 	if (srsp == NULL)
520 		return (ACPI_PSM_FAILURE);
521 
522 	/*
523 	 * The Interrupts[] array is always at least one entry
524 	 * long; see the definition of ACPI_RESOURCE.
525 	 */
526 	switch (srsp->Type) {
527 	case ACPI_RESOURCE_TYPE_IRQ:
528 		srsp->Data.Irq.InterruptCount = 1;
529 		srsp->Data.Irq.Interrupts[0] = irq;
530 		break;
531 	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
532 		srsp->Data.ExtendedIrq.InterruptCount = 1;
533 		srsp->Data.ExtendedIrq.Interrupts[0] = irq;
534 		break;
535 	}
536 
537 	rsb.Pointer = srsp;
538 	rsb.Length = srs_len;
539 	status = AcpiSetCurrentResources(lnkobj, &rsb);
540 	kmem_free(srsp, srs_len);
541 	if (status != AE_OK) {
542 		cmn_err(CE_WARN, "!psm: set_irq: _SRS failed");
543 		return (ACPI_PSM_FAILURE);
544 	}
545 
546 	if (acpica_eval_int(lnkobj, "_STA", &status) == AE_OK) {
547 		acpipsmlnkp->device_status = (uchar_t)status;
548 		return (ACPI_PSM_SUCCESS);
549 	} else
550 		return (ACPI_PSM_FAILURE);
551 }
552 
553 
554 /*
555  *
556  */
557 static int
558 psm_acpi_edgelevel(UINT32 el)
559 {
560 	switch (el) {
561 	case ACPI_EDGE_SENSITIVE:
562 		return (INTR_EL_EDGE);
563 	case ACPI_LEVEL_SENSITIVE:
564 		return (INTR_EL_LEVEL);
565 	default:
566 		/* el is a single bit; should never reach here */
567 		return (INTR_EL_CONFORM);
568 	}
569 }
570 
571 
572 /*
573  *
574  */
575 static int
576 psm_acpi_po(UINT32 po)
577 {
578 	switch (po) {
579 	case ACPI_ACTIVE_HIGH:
580 		return (INTR_PO_ACTIVE_HIGH);
581 	case ACPI_ACTIVE_LOW:
582 		return (INTR_PO_ACTIVE_LOW);
583 	default:
584 		/* po is a single bit; should never reach here */
585 		return (INTR_PO_CONFORM);
586 	}
587 }
588 
589 
590 /*
591  * Retrieves the current irq setting for the interrrupt link device.
592  *
593  * Stores polarity and sensitivity in the structure pointed to by
594  * intr_flagp, and irqno in the value pointed to by pci_irqp.
595  *
596  * Returns ACPI_PSM_SUCCESS on success, ACPI_PSM_FAILURE upon failure.
597  */
598 int
599 acpi_get_current_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int *pci_irqp,
600     iflag_t *intr_flagp)
601 {
602 	ACPI_HANDLE lnkobj;
603 	ACPI_BUFFER rb;
604 	ACPI_RESOURCE *rp;
605 	int irq;
606 	int status = ACPI_PSM_FAILURE;
607 
608 	ASSERT(acpipsmlnkp != NULL);
609 	lnkobj = acpipsmlnkp->lnkobj;
610 
611 	if (!(acpipsmlnkp->device_status & STA_PRESENT) ||
612 	    !(acpipsmlnkp->device_status & STA_ENABLE)) {
613 		PSM_VERBOSE_IRQ((CE_WARN, "!psm: crs device either not "
614 		    "present or disabled, status 0x%x",
615 		    acpipsmlnkp->device_status));
616 		return (ACPI_PSM_FAILURE);
617 	}
618 
619 	rb.Pointer = NULL;
620 	rb.Length = ACPI_ALLOCATE_BUFFER;
621 	if (AcpiGetCurrentResources(lnkobj, &rb) != AE_OK) {
622 		PSM_VERBOSE_IRQ((CE_WARN, "!psm: no crs object found or"
623 		" evaluation failed"));
624 		return (ACPI_PSM_FAILURE);
625 	}
626 
627 	irq = -1;
628 	for (rp = rb.Pointer; rp->Type != ACPI_RESOURCE_TYPE_END_TAG;
629 	    rp = ACPI_NEXT_RESOURCE(rp)) {
630 		if (rp->Type == ACPI_RESOURCE_TYPE_IRQ) {
631 			if (irq > 0) {
632 				PSM_VERBOSE_IRQ((CE_WARN, "!psm: multiple IRQ"
633 				" from _CRS "));
634 				status = ACPI_PSM_FAILURE;
635 				break;
636 			}
637 
638 			if (rp->Data.Irq.InterruptCount != 1) {
639 				PSM_VERBOSE_IRQ((CE_WARN, "!psm: <>1 interrupt"
640 				" from _CRS "));
641 				status = ACPI_PSM_FAILURE;
642 				break;
643 			}
644 
645 			intr_flagp->intr_el = psm_acpi_edgelevel(
646 					rp->Data.Irq.Triggering);
647 			intr_flagp->intr_po = psm_acpi_po(
648 					rp->Data.Irq.Polarity);
649 			irq = rp->Data.Irq.Interrupts[0];
650 			status = ACPI_PSM_SUCCESS;
651 		} else if (rp->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
652 			if (irq > 0) {
653 				PSM_VERBOSE_IRQ((CE_WARN, "!psm: multiple IRQ"
654 				" from _CRS "));
655 				status = ACPI_PSM_FAILURE;
656 				break;
657 			}
658 
659 			if (rp->Data.ExtendedIrq.InterruptCount != 1) {
660 				PSM_VERBOSE_IRQ((CE_WARN, "!psm: <>1 interrupt"
661 				" from _CRS "));
662 				status = ACPI_PSM_FAILURE;
663 				break;
664 			}
665 
666 			intr_flagp->intr_el = psm_acpi_edgelevel(
667 					rp->Data.ExtendedIrq.Triggering);
668 			intr_flagp->intr_po = psm_acpi_po(
669 					rp->Data.ExtendedIrq.Polarity);
670 			irq = rp->Data.ExtendedIrq.Interrupts[0];
671 			status = ACPI_PSM_SUCCESS;
672 		}
673 	}
674 
675 	AcpiOsFree(rb.Pointer);
676 	if (status == ACPI_PSM_SUCCESS) {
677 		*pci_irqp =  irq;
678 	}
679 
680 	return (status);
681 }
682 
683 /*
684  * Searches for the given IRQ in the irqlist passed in.
685  *
686  * If multiple matches exist, this returns true on the first match.
687  * Returns the interrupt flags, if a match was found, in `intr_flagp' if
688  * it's passed in non-NULL
689  */
690 int
691 acpi_irqlist_find_irq(acpi_irqlist_t *irqlistp, int irq, iflag_t *intr_flagp)
692 {
693 	int found = 0;
694 	int i;
695 
696 	while (irqlistp != NULL && !found) {
697 		for (i = 0; i < irqlistp->num_irqs; i++) {
698 			if (irqlistp->irqs[i] == irq) {
699 				if (intr_flagp)
700 					*intr_flagp = irqlistp->intr_flags;
701 				found = 1;
702 				break;	/* out of for() */
703 			}
704 		}
705 	}
706 
707 	return (found ? ACPI_PSM_SUCCESS : ACPI_PSM_FAILURE);
708 }
709 
710 /*
711  * Frees the irqlist allocated by acpi_get_possible_irq_resource.
712  * It takes a count of number of entries in the list.
713  */
714 void
715 acpi_free_irqlist(acpi_irqlist_t *irqlistp)
716 {
717 	acpi_irqlist_t *freednode;
718 
719 	while (irqlistp != NULL) {
720 		/* Free the irq list */
721 		kmem_free(irqlistp->irqs, irqlistp->num_irqs *
722 		    sizeof (int32_t));
723 
724 		freednode = irqlistp;
725 		irqlistp = irqlistp->next;
726 		kmem_free(freednode, sizeof (acpi_irqlist_t));
727 	}
728 }
729 
730 /*
731  * Creates a new entry in the given irqlist with the information passed in.
732  */
733 static void
734 acpi_add_irqlist_entry(acpi_irqlist_t **irqlistp, uint32_t *irqlist,
735     int irqlist_len, iflag_t *intr_flagp)
736 {
737 	acpi_irqlist_t *newent;
738 
739 	ASSERT(irqlist != NULL);
740 	ASSERT(intr_flagp != NULL);
741 
742 	newent = kmem_alloc(sizeof (acpi_irqlist_t), KM_SLEEP);
743 	newent->intr_flags = *intr_flagp;
744 	newent->irqs = irqlist;
745 	newent->num_irqs = irqlist_len;
746 	newent->next = *irqlistp;
747 
748 	*irqlistp = newent;
749 }
750 
751 
752 /*
753  * Retrieves a list of possible interrupt settings for the interrupt link
754  * device.
755  *
756  * Stores polarity and sensitivity in the structure pointed to by intr_flagp.
757  * Updates value pointed to by irqlistp with the address of a table it
758  * allocates. where interrupt numbers are stored. Stores the number of entries
759  * in this table in the value pointed to by num_entriesp;
760  *
761  * Each element in this table is of type int32_t. The table should be later
762  * freed by caller via acpi_free_irq_list().
763  *
764  * Returns ACPI_PSM_SUCCESS on success and ACPI_PSM_FAILURE upon failure
765  */
766 int
767 acpi_get_possible_irq_resources(acpi_psm_lnk_t *acpipsmlnkp,
768     acpi_irqlist_t **irqlistp)
769 {
770 	ACPI_HANDLE lnkobj;
771 	ACPI_BUFFER rsb;
772 	ACPI_RESOURCE *resp;
773 	int status;
774 
775 	int i, el, po, irqlist_len;
776 	uint32_t *irqlist;
777 	void *tmplist;
778 	iflag_t intr_flags;
779 
780 	ASSERT(acpipsmlnkp != NULL);
781 	lnkobj = acpipsmlnkp->lnkobj;
782 
783 	rsb.Pointer = NULL;
784 	rsb.Length = ACPI_ALLOCATE_BUFFER;
785 	status = AcpiGetPossibleResources(lnkobj, &rsb);
786 	if (status != AE_OK) {
787 		cmn_err(CE_WARN, "!psm: get_irq: _PRS failed");
788 		return (ACPI_PSM_FAILURE);
789 	}
790 
791 	/*
792 	 * Scan the resources looking for an interrupt resource
793 	 */
794 	*irqlistp = 0;
795 	for (resp = rsb.Pointer; resp->Type != ACPI_RESOURCE_TYPE_END_TAG;
796 	    resp = ACPI_NEXT_RESOURCE(resp)) {
797 		switch (resp->Type) {
798 		case ACPI_RESOURCE_TYPE_IRQ:
799 			irqlist_len = resp->Data.Irq.InterruptCount;
800 			tmplist = resp->Data.Irq.Interrupts;
801 			el = resp->Data.Irq.Triggering;
802 			po = resp->Data.Irq.Polarity;
803 			break;
804 		case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
805 			irqlist_len = resp->Data.ExtendedIrq.InterruptCount;
806 			tmplist = resp->Data.ExtendedIrq.Interrupts;
807 			el = resp->Data.ExtendedIrq.Triggering;
808 			po = resp->Data.ExtendedIrq.Polarity;
809 			break;
810 		default:
811 			continue;
812 		}
813 
814 		if (resp->Type != ACPI_RESOURCE_TYPE_IRQ &&
815 		    resp->Type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
816 			cmn_err(CE_WARN, "!psm: get_irq: no IRQ resource");
817 			return (ACPI_PSM_FAILURE);
818 		}
819 
820 		/* NEEDSWORK: move this into add_irqlist_entry someday */
821 		irqlist = kmem_zalloc(irqlist_len * sizeof (*irqlist),
822 					    KM_SLEEP);
823 		for (i = 0; i < irqlist_len; i++)
824 			if (resp->Type == ACPI_RESOURCE_TYPE_IRQ)
825 				irqlist[i] = ((uint8_t *)tmplist)[i];
826 			else
827 				irqlist[i] = ((uint32_t *)tmplist)[i];
828 		intr_flags.intr_el = psm_acpi_edgelevel(el);
829 		intr_flags.intr_po = psm_acpi_po(po);
830 		acpi_add_irqlist_entry(irqlistp, irqlist, irqlist_len,
831 				&intr_flags);
832 	}
833 
834 	AcpiOsFree(rsb.Pointer);
835 	return (irqlistp == NULL ? ACPI_PSM_FAILURE : ACPI_PSM_SUCCESS);
836 }
837 
838 /*
839  * Adds a new cache entry to the irq cache which maps an irq and
840  * its attributes to PCI bus/dev/ipin and optionally to its associated ACPI
841  * interrupt link device object.
842  */
843 void
844 acpi_new_irq_cache_ent(int bus, int dev, int ipin, int pci_irq,
845     iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp)
846 {
847 	int newsize;
848 	irq_cache_t *new_arr, *ep;
849 
850 	mutex_enter(&acpi_irq_cache_mutex);
851 	if (irq_cache_valid >= irq_cache_len) {
852 		/* initially, or re-, allocate array */
853 
854 		newsize = (irq_cache_len ?
855 		    irq_cache_len * 2 : IRQ_CACHE_INITLEN);
856 		new_arr = kmem_zalloc(newsize * sizeof (irq_cache_t), KM_SLEEP);
857 		if (irq_cache_len != 0) {
858 			/* realloc: copy data, free old */
859 			bcopy(irq_cache_table, new_arr,
860 			    irq_cache_len * sizeof (irq_cache_t));
861 			kmem_free(irq_cache_table,
862 			    irq_cache_len * sizeof (irq_cache_t));
863 		}
864 		irq_cache_len = newsize;
865 		irq_cache_table = new_arr;
866 	}
867 	ep = &irq_cache_table[irq_cache_valid++];
868 	ep->bus = (uchar_t)bus;
869 	ep->dev = (uchar_t)dev;
870 	ep->ipin = (uchar_t)ipin;
871 	ep->flags = *intr_flagp;
872 	ep->irq = pci_irq;
873 	ASSERT(acpipsmlnkp != NULL);
874 	ep->lnkobj = acpipsmlnkp->lnkobj;
875 	mutex_exit(&acpi_irq_cache_mutex);
876 }
877 
878 
879 /*
880  * Searches the irq caches for the given bus/dev/ipin.
881  *
882  * If info is found, stores polarity and sensitivity in the structure
883  * pointed to by intr_flagp, and irqno in the value pointed to by pci_irqp,
884  * and returns ACPI_PSM_SUCCESS.
885  * Otherwise, ACPI_PSM_FAILURE is returned.
886  */
887 int
888 acpi_get_irq_cache_ent(uchar_t bus, uchar_t dev, int ipin,
889     int *pci_irqp, iflag_t *intr_flagp)
890 {
891 
892 	irq_cache_t *irqcachep;
893 	int i;
894 	int ret = ACPI_PSM_FAILURE;
895 
896 	mutex_enter(&acpi_irq_cache_mutex);
897 	for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid;
898 	    irqcachep++, i++)
899 		if ((irqcachep->bus == bus) &&
900 		    (irqcachep->dev == dev) &&
901 		    (irqcachep->ipin == ipin)) {
902 			ASSERT(pci_irqp != NULL && intr_flagp != NULL);
903 			*pci_irqp = irqcachep->irq;
904 			*intr_flagp = irqcachep->flags;
905 			ret = ACPI_PSM_SUCCESS;
906 			break;
907 		}
908 
909 	mutex_exit(&acpi_irq_cache_mutex);
910 	return (ret);
911 }
912 
913 /*
914  * Searches the irq caches for the given interrupt lnk device object.
915  *
916  * If info is found, stores polarity and sensitivity in the structure
917  * pointed to by intr_flagp, and irqno in the value pointed to by pci_irqp,
918  * and returns ACPI_PSM_SUCCESS.
919  * Otherwise, ACPI_PSM_FAILURE is returned.
920  */
921 int
922 acpi_get_irq_lnk_cache_ent(ACPI_HANDLE lnkobj, int *pci_irqp,
923     iflag_t *intr_flagp)
924 {
925 
926 	irq_cache_t *irqcachep;
927 	int i;
928 	int ret = ACPI_PSM_FAILURE;
929 
930 	if (lnkobj == NULL)
931 		return (ACPI_PSM_FAILURE);
932 
933 	mutex_enter(&acpi_irq_cache_mutex);
934 	for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid;
935 	    irqcachep++, i++)
936 		if (irqcachep->lnkobj == lnkobj) {
937 			ASSERT(pci_irqp != NULL);
938 			*pci_irqp = irqcachep->irq;
939 			ASSERT(intr_flagp != NULL);
940 			*intr_flagp = irqcachep->flags;
941 			ret = ACPI_PSM_SUCCESS;
942 			break;
943 		}
944 	mutex_exit(&acpi_irq_cache_mutex);
945 	return (ret);
946 }
947 
948 int
949 acpi_poweroff(void)
950 {
951 	extern int acpica_powering_off;
952 
953 	PSM_VERBOSE_POWEROFF(("acpi_poweroff: starting poweroff\n"));
954 
955 	acpica_powering_off = 1;
956 
957 	if (AcpiEnterSleepStatePrep(5) != AE_OK)
958 		return (1);
959 	ACPI_DISABLE_IRQS();
960 	if (AcpiEnterSleepState(5) != AE_OK) {
961 		ACPI_ENABLE_IRQS();
962 		return (1);
963 	}
964 	ACPI_ENABLE_IRQS();
965 
966 	/* we should be off; if we get here it's an error */
967 	PSM_VERBOSE_POWEROFF(("acpi_poweroff: failed to actually power off\n"));
968 	return (1);
969 }
970 
971 
972 /*
973  * psm_set_elcr() sets ELCR bit for specified vector
974  */
975 void
976 psm_set_elcr(int vecno, int val)
977 {
978 	int elcr_port = ELCR_PORT1 + (vecno >> 3);
979 	int elcr_bit = 1 << (vecno & 0x07);
980 
981 	ASSERT((vecno >= 0) && (vecno < 16));
982 
983 	if (val) {
984 		/* set bit to force level-triggered mode */
985 		outb(elcr_port, inb(elcr_port) | elcr_bit);
986 	} else {
987 		/* clear bit to force edge-triggered mode */
988 		outb(elcr_port, inb(elcr_port) & ~elcr_bit);
989 	}
990 }
991 
992 /*
993  * psm_get_elcr() returns status of ELCR bit for specific vector
994  */
995 int
996 psm_get_elcr(int vecno)
997 {
998 	int elcr_port = ELCR_PORT1 + (vecno >> 3);
999 	int elcr_bit = 1 << (vecno & 0x07);
1000 
1001 	ASSERT((vecno >= 0) && (vecno < 16));
1002 
1003 	return ((inb(elcr_port) & elcr_bit) ? 1 : 0);
1004 }
1005