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