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