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