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