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