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