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 * Copyright 2013 PALO, Richard. All rights reserved.
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 <acpica/include/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 (void *)(((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 * Examines ACPI node for presence of _PRT object
363 * Check _STA to make sure node is present and/or enabled
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 int sta;
374
375 /*
376 * Return 0 for "no _PRT" if device does not exist
377 * According to ACPI Spec,
378 * 1) setting either bit 0 or bit 3 means that device exists.
379 * 2) Absence of _STA method means all status bits set.
380 */
381 if (ACPI_SUCCESS(acpica_eval_int(ah, "_STA", &sta)) &&
382 !(sta & (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_FUNCTIONING)))
383 return (0);
384
385 return (AcpiGetHandle(ah, "_PRT", &rh) == AE_OK);
386 }
387
388
389 /*
390 * Look first for an ACPI PCI bus node matching busid, then for a _PRT on the
391 * parent node; then drop into the bridge-chasing code (which will also
392 * look for _PRTs on the way up the tree of bridges)
393 *
394 * Stores polarity and sensitivity in the structure pointed to by
395 * intr_flagp, and irqno in the value pointed to by pci_irqp. *
396 * Returns:
397 * ACPI_PSM_SUCCESS on success.
398 * ACPI_PSM_PARTIAL to indicate need to configure the interrupt
399 * link device.
400 * ACPI_PSM_FAILURE if an error prevented the system from
401 * obtaining irq information for dip.
402 */
403 int
404 acpi_translate_pci_irq(dev_info_t *dip, int ipin, int *pci_irqp,
405 iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp)
406 {
407 ACPI_HANDLE pciobj;
408 int status = AE_ERROR;
409 dev_info_t *curdip, *parentdip;
410 int curpin, curbus, curdev;
411
412
413 curpin = ipin;
414 curdip = dip;
415 while (curdip != ddi_root_node()) {
416 parentdip = ddi_get_parent(curdip);
417 ASSERT(parentdip != NULL);
418
419 if (get_bdf(curdip, &curbus, &curdev, NULL) != 0)
420 break;
421
422 status = acpica_get_handle(parentdip, &pciobj);
423 if ((status == AE_OK) && psm_node_has_prt(pciobj)) {
424 return (acpi_get_gsiv(curdip, pciobj, curdev, curpin,
425 pci_irqp, intr_flagp, acpipsmlnkp));
426 }
427
428 /* if we got here, we need to traverse a bridge upwards */
429 if (!psm_is_pci_bridge(parentdip))
430 break;
431
432 /*
433 * This is the rotating scheme that Compaq is using
434 * and documented in the PCI-PCI spec. Also, if the
435 * PCI-PCI bridge is behind another PCI-PCI bridge,
436 * then it needs to keep ascending until an interrupt
437 * entry is found or the top is reached
438 */
439 curpin = (curdev + curpin) % PCI_INTD;
440 curdip = parentdip;
441 }
442
443 /*
444 * We should never, ever get here; didn't find a _PRT
445 */
446 return (ACPI_PSM_FAILURE);
447 }
448
449 /*
450 * Sets the irq resource of the lnk object to the requested irq value.
451 *
452 * Returns ACPI_PSM_SUCCESS on success, ACPI_PSM_FAILURE upon failure.
453 */
454 int
455 acpi_set_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int irq)
456 {
457 ACPI_BUFFER rsb;
458 ACPI_RESOURCE *resp;
459 ACPI_RESOURCE *srsp;
460 ACPI_HANDLE lnkobj;
461 int srs_len, status;
462
463 ASSERT(acpipsmlnkp != NULL);
464
465 lnkobj = acpipsmlnkp->lnkobj;
466
467 /*
468 * Fetch the possible resources for the link
469 */
470
471 rsb.Pointer = NULL;
472 rsb.Length = ACPI_ALLOCATE_BUFFER;
473 status = AcpiGetPossibleResources(lnkobj, &rsb);
474 if (status != AE_OK) {
475 cmn_err(CE_WARN, "!psm: set_irq: _PRS failed");
476 return (ACPI_PSM_FAILURE);
477 }
478
479 /*
480 * Find an IRQ resource descriptor to use as template
481 */
482 srsp = NULL;
483 for (resp = rsb.Pointer; resp->Type != ACPI_RESOURCE_TYPE_END_TAG;
484 resp = ACPI_NEXT_RESOURCE(resp)) {
485 if ((resp->Type == ACPI_RESOURCE_TYPE_IRQ) ||
486 (resp->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ)) {
487 ACPI_RESOURCE *endtag;
488 /*
489 * Allocate enough room for this resource entry
490 * and one end tag following it
491 */
492 srs_len = resp->Length + ACPI_RS_SIZE_NO_DATA;
493 srsp = kmem_zalloc(srs_len, KM_SLEEP);
494 bcopy(resp, srsp, resp->Length);
495 endtag = ACPI_NEXT_RESOURCE(srsp);
496 endtag->Type = ACPI_RESOURCE_TYPE_END_TAG;
497 endtag->Length = ACPI_RS_SIZE_NO_DATA;
498 break; /* drop out of the loop */
499 }
500 }
501
502 /*
503 * We're done with the PRS values, toss 'em lest we forget
504 */
505 AcpiOsFree(rsb.Pointer);
506
507 if (srsp == NULL)
508 return (ACPI_PSM_FAILURE);
509
510 /*
511 * The Interrupts[] array is always at least one entry
512 * long; see the definition of ACPI_RESOURCE.
513 */
514 switch (srsp->Type) {
515 case ACPI_RESOURCE_TYPE_IRQ:
516 srsp->Data.Irq.InterruptCount = 1;
517 srsp->Data.Irq.Interrupts[0] = (uint8_t)irq;
518 break;
519 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
520 srsp->Data.ExtendedIrq.InterruptCount = 1;
521 srsp->Data.ExtendedIrq.Interrupts[0] = irq;
522 break;
523 }
524
525 rsb.Pointer = srsp;
526 rsb.Length = srs_len;
527 status = AcpiSetCurrentResources(lnkobj, &rsb);
528 kmem_free(srsp, srs_len);
529 if (status != AE_OK) {
530 cmn_err(CE_WARN, "!psm: set_irq: _SRS failed");
531 return (ACPI_PSM_FAILURE);
532 }
533
534 if (acpica_eval_int(lnkobj, "_STA", &status) == AE_OK) {
535 acpipsmlnkp->device_status = (uchar_t)status;
536 return (ACPI_PSM_SUCCESS);
537 } else
538 return (ACPI_PSM_FAILURE);
539 }
540
541
542 /*
543 *
544 */
545 static int
546 psm_acpi_edgelevel(UINT32 el)
547 {
548 switch (el) {
549 case ACPI_EDGE_SENSITIVE:
550 return (INTR_EL_EDGE);
551 case ACPI_LEVEL_SENSITIVE:
552 return (INTR_EL_LEVEL);
553 default:
554 /* el is a single bit; should never reach here */
555 return (INTR_EL_CONFORM);
556 }
557 }
558
559
560 /*
561 *
562 */
563 static int
564 psm_acpi_po(UINT32 po)
565 {
566 switch (po) {
567 case ACPI_ACTIVE_HIGH:
568 return (INTR_PO_ACTIVE_HIGH);
569 case ACPI_ACTIVE_LOW:
570 return (INTR_PO_ACTIVE_LOW);
571 default:
572 /* po is a single bit; should never reach here */
573 return (INTR_PO_CONFORM);
574 }
575 }
576
577
578 /*
579 * Retrieves the current irq setting for the interrrupt link device.
580 *
581 * Stores polarity and sensitivity in the structure pointed to by
582 * intr_flagp, and irqno in the value pointed to by pci_irqp.
583 *
584 * Returns ACPI_PSM_SUCCESS on success, ACPI_PSM_FAILURE upon failure.
585 */
586 int
587 acpi_get_current_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int *pci_irqp,
588 iflag_t *intr_flagp)
589 {
590 ACPI_HANDLE lnkobj;
591 ACPI_BUFFER rb;
592 ACPI_RESOURCE *rp;
593 int irq;
594 int status = ACPI_PSM_FAILURE;
595
596 ASSERT(acpipsmlnkp != NULL);
597 lnkobj = acpipsmlnkp->lnkobj;
598
599 if (!(acpipsmlnkp->device_status & STA_PRESENT) ||
600 !(acpipsmlnkp->device_status & STA_ENABLE)) {
601 PSM_VERBOSE_IRQ((CE_WARN, "!psm: crs device either not "
602 "present or disabled, status 0x%x",
603 acpipsmlnkp->device_status));
604 return (ACPI_PSM_FAILURE);
605 }
606
607 rb.Pointer = NULL;
608 rb.Length = ACPI_ALLOCATE_BUFFER;
609 if (AcpiGetCurrentResources(lnkobj, &rb) != AE_OK) {
610 PSM_VERBOSE_IRQ((CE_WARN, "!psm: no crs object found or"
611 " evaluation failed"));
612 return (ACPI_PSM_FAILURE);
613 }
614
615 irq = -1;
616 for (rp = rb.Pointer; rp->Type != ACPI_RESOURCE_TYPE_END_TAG;
617 rp = ACPI_NEXT_RESOURCE(rp)) {
618 if (rp->Type == ACPI_RESOURCE_TYPE_IRQ) {
619 if (irq > 0) {
620 PSM_VERBOSE_IRQ((CE_WARN, "!psm: multiple IRQ"
621 " from _CRS "));
622 status = ACPI_PSM_FAILURE;
623 break;
624 }
625
626 if (rp->Data.Irq.InterruptCount != 1) {
627 PSM_VERBOSE_IRQ((CE_WARN, "!psm: <>1 interrupt"
628 " from _CRS "));
629 status = ACPI_PSM_FAILURE;
630 break;
631 }
632
633 intr_flagp->intr_el = psm_acpi_edgelevel(
634 rp->Data.Irq.Triggering);
635 intr_flagp->intr_po = psm_acpi_po(
636 rp->Data.Irq.Polarity);
637 irq = rp->Data.Irq.Interrupts[0];
638 status = ACPI_PSM_SUCCESS;
639 } else if (rp->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
640 if (irq > 0) {
641 PSM_VERBOSE_IRQ((CE_WARN, "!psm: multiple IRQ"
642 " from _CRS "));
643 status = ACPI_PSM_FAILURE;
644 break;
645 }
646
647 if (rp->Data.ExtendedIrq.InterruptCount != 1) {
648 PSM_VERBOSE_IRQ((CE_WARN, "!psm: <>1 interrupt"
649 " from _CRS "));
650 status = ACPI_PSM_FAILURE;
651 break;
652 }
653
654 intr_flagp->intr_el = psm_acpi_edgelevel(
655 rp->Data.ExtendedIrq.Triggering);
656 intr_flagp->intr_po = psm_acpi_po(
657 rp->Data.ExtendedIrq.Polarity);
658 irq = rp->Data.ExtendedIrq.Interrupts[0];
659 status = ACPI_PSM_SUCCESS;
660 }
661 }
662
663 AcpiOsFree(rb.Pointer);
664 if (status == ACPI_PSM_SUCCESS) {
665 *pci_irqp = irq;
666 }
667
668 return (status);
669 }
670
671 /*
672 * Searches for the given IRQ in the irqlist passed in.
673 *
674 * If multiple matches exist, this returns true on the first match.
675 * Returns the interrupt flags, if a match was found, in `intr_flagp' if
676 * it's passed in non-NULL
677 */
678 int
679 acpi_irqlist_find_irq(acpi_irqlist_t *irqlistp, int irq, iflag_t *intr_flagp)
680 {
681 int found = 0;
682 int i;
683
684 while (irqlistp != NULL && !found) {
685 for (i = 0; i < irqlistp->num_irqs; i++) {
686 if (irqlistp->irqs[i] == irq) {
687 if (intr_flagp)
688 *intr_flagp = irqlistp->intr_flags;
689 found = 1;
690 break; /* out of for() */
691 }
692 }
693 }
694
695 return (found ? ACPI_PSM_SUCCESS : ACPI_PSM_FAILURE);
696 }
697
698 /*
699 * Frees the irqlist allocated by acpi_get_possible_irq_resource.
700 * It takes a count of number of entries in the list.
701 */
702 void
703 acpi_free_irqlist(acpi_irqlist_t *irqlistp)
704 {
705 acpi_irqlist_t *freednode;
706
707 while (irqlistp != NULL) {
708 /* Free the irq list */
709 kmem_free(irqlistp->irqs, irqlistp->num_irqs *
710 sizeof (int32_t));
711
712 freednode = irqlistp;
713 irqlistp = irqlistp->next;
714 kmem_free(freednode, sizeof (acpi_irqlist_t));
715 }
716 }
717
718 /*
719 * Creates a new entry in the given irqlist with the information passed in.
720 */
721 static void
722 acpi_add_irqlist_entry(acpi_irqlist_t **irqlistp, uint32_t *irqlist,
723 int irqlist_len, iflag_t *intr_flagp)
724 {
725 acpi_irqlist_t *newent;
726
727 ASSERT(irqlist != NULL);
728 ASSERT(intr_flagp != NULL);
729
730 newent = kmem_alloc(sizeof (acpi_irqlist_t), KM_SLEEP);
731 newent->intr_flags = *intr_flagp;
732 newent->irqs = irqlist;
733 newent->num_irqs = irqlist_len;
734 newent->next = *irqlistp;
735
736 *irqlistp = newent;
737 }
738
739
740 /*
741 * Retrieves a list of possible interrupt settings for the interrupt link
742 * device.
743 *
744 * Stores polarity and sensitivity in the structure pointed to by intr_flagp.
745 * Updates value pointed to by irqlistp with the address of a table it
746 * allocates. where interrupt numbers are stored. Stores the number of entries
747 * in this table in the value pointed to by num_entriesp;
748 *
749 * Each element in this table is of type int32_t. The table should be later
750 * freed by caller via acpi_free_irq_list().
751 *
752 * Returns ACPI_PSM_SUCCESS on success and ACPI_PSM_FAILURE upon failure
753 */
754 int
755 acpi_get_possible_irq_resources(acpi_psm_lnk_t *acpipsmlnkp,
756 acpi_irqlist_t **irqlistp)
757 {
758 ACPI_HANDLE lnkobj;
759 ACPI_BUFFER rsb;
760 ACPI_RESOURCE *resp;
761 int status;
762
763 int i, el, po, irqlist_len;
764 uint32_t *irqlist;
765 void *tmplist;
766 iflag_t intr_flags;
767
768 ASSERT(acpipsmlnkp != NULL);
769 lnkobj = acpipsmlnkp->lnkobj;
770
771 rsb.Pointer = NULL;
772 rsb.Length = ACPI_ALLOCATE_BUFFER;
773 status = AcpiGetPossibleResources(lnkobj, &rsb);
774 if (status != AE_OK) {
775 cmn_err(CE_WARN, "!psm: get_irq: _PRS failed");
776 return (ACPI_PSM_FAILURE);
777 }
778
779 /*
780 * Scan the resources looking for an interrupt resource
781 */
782 *irqlistp = 0;
783 for (resp = rsb.Pointer; resp->Type != ACPI_RESOURCE_TYPE_END_TAG;
784 resp = ACPI_NEXT_RESOURCE(resp)) {
785 switch (resp->Type) {
786 case ACPI_RESOURCE_TYPE_IRQ:
787 irqlist_len = resp->Data.Irq.InterruptCount;
788 tmplist = resp->Data.Irq.Interrupts;
789 el = resp->Data.Irq.Triggering;
790 po = resp->Data.Irq.Polarity;
791 break;
792 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
793 irqlist_len = resp->Data.ExtendedIrq.InterruptCount;
794 tmplist = resp->Data.ExtendedIrq.Interrupts;
795 el = resp->Data.ExtendedIrq.Triggering;
796 po = resp->Data.ExtendedIrq.Polarity;
797 break;
798 default:
799 continue;
800 }
801
802 if (resp->Type != ACPI_RESOURCE_TYPE_IRQ &&
803 resp->Type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
804 cmn_err(CE_WARN, "!psm: get_irq: no IRQ resource");
805 return (ACPI_PSM_FAILURE);
806 }
807
808 /* NEEDSWORK: move this into add_irqlist_entry someday */
809 irqlist = kmem_zalloc(irqlist_len * sizeof (*irqlist),
810 KM_SLEEP);
811 for (i = 0; i < irqlist_len; i++)
812 if (resp->Type == ACPI_RESOURCE_TYPE_IRQ)
813 irqlist[i] = ((uint8_t *)tmplist)[i];
814 else
815 irqlist[i] = ((uint32_t *)tmplist)[i];
816 intr_flags.intr_el = psm_acpi_edgelevel(el);
817 intr_flags.intr_po = psm_acpi_po(po);
818 acpi_add_irqlist_entry(irqlistp, irqlist, irqlist_len,
819 &intr_flags);
820 }
821
822 AcpiOsFree(rsb.Pointer);
823 return (irqlistp == NULL ? ACPI_PSM_FAILURE : ACPI_PSM_SUCCESS);
824 }
825
826 /*
827 * Adds a new cache entry to the irq cache which maps an irq and
828 * its attributes to PCI bus/dev/ipin and optionally to its associated ACPI
829 * interrupt link device object.
830 */
831 void
832 acpi_new_irq_cache_ent(int bus, int dev, int ipin, int pci_irq,
833 iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp)
834 {
835 int newsize;
836 irq_cache_t *new_arr, *ep;
837
838 mutex_enter(&acpi_irq_cache_mutex);
839 if (irq_cache_valid >= irq_cache_len) {
840 /* initially, or re-, allocate array */
841
842 newsize = (irq_cache_len ?
843 irq_cache_len * 2 : IRQ_CACHE_INITLEN);
844 new_arr = kmem_zalloc(newsize * sizeof (irq_cache_t), KM_SLEEP);
845 if (irq_cache_len != 0) {
846 /* realloc: copy data, free old */
847 bcopy(irq_cache_table, new_arr,
848 irq_cache_len * sizeof (irq_cache_t));
849 kmem_free(irq_cache_table,
850 irq_cache_len * sizeof (irq_cache_t));
851 }
852 irq_cache_len = newsize;
853 irq_cache_table = new_arr;
854 }
855 ep = &irq_cache_table[irq_cache_valid++];
856 ep->bus = (uchar_t)bus;
857 ep->dev = (uchar_t)dev;
858 ep->ipin = (uchar_t)ipin;
859 ep->flags = *intr_flagp;
860 ep->irq = (uchar_t)pci_irq;
861 ASSERT(acpipsmlnkp != NULL);
862 ep->lnkobj = acpipsmlnkp->lnkobj;
863 mutex_exit(&acpi_irq_cache_mutex);
864 }
865
866
867 /*
868 * Searches the irq caches for the given bus/dev/ipin.
869 *
870 * If info is found, stores polarity and sensitivity in the structure
871 * pointed to by intr_flagp, and irqno in the value pointed to by pci_irqp,
872 * and returns ACPI_PSM_SUCCESS.
873 * Otherwise, ACPI_PSM_FAILURE is returned.
874 */
875 int
876 acpi_get_irq_cache_ent(uchar_t bus, uchar_t dev, int ipin,
877 int *pci_irqp, iflag_t *intr_flagp)
878 {
879
880 irq_cache_t *irqcachep;
881 int i;
882 int ret = ACPI_PSM_FAILURE;
883
884 mutex_enter(&acpi_irq_cache_mutex);
885 for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid;
886 irqcachep++, i++)
887 if ((irqcachep->bus == bus) &&
888 (irqcachep->dev == dev) &&
889 (irqcachep->ipin == ipin)) {
890 ASSERT(pci_irqp != NULL && intr_flagp != NULL);
891 *pci_irqp = irqcachep->irq;
892 *intr_flagp = irqcachep->flags;
893 ret = ACPI_PSM_SUCCESS;
894 break;
895 }
896
897 mutex_exit(&acpi_irq_cache_mutex);
898 return (ret);
899 }
900
901 /*
902 * Searches the irq caches for the given interrupt lnk device object.
903 *
904 * If info is found, stores polarity and sensitivity in the structure
905 * pointed to by intr_flagp, and irqno in the value pointed to by pci_irqp,
906 * and returns ACPI_PSM_SUCCESS.
907 * Otherwise, ACPI_PSM_FAILURE is returned.
908 */
909 int
910 acpi_get_irq_lnk_cache_ent(ACPI_HANDLE lnkobj, int *pci_irqp,
911 iflag_t *intr_flagp)
912 {
913
914 irq_cache_t *irqcachep;
915 int i;
916 int ret = ACPI_PSM_FAILURE;
917
918 if (lnkobj == NULL)
919 return (ACPI_PSM_FAILURE);
920
921 mutex_enter(&acpi_irq_cache_mutex);
922 for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid;
923 irqcachep++, i++)
924 if (irqcachep->lnkobj == lnkobj) {
925 ASSERT(pci_irqp != NULL);
926 *pci_irqp = irqcachep->irq;
927 ASSERT(intr_flagp != NULL);
928 *intr_flagp = irqcachep->flags;
929 ret = ACPI_PSM_SUCCESS;
930 break;
931 }
932 mutex_exit(&acpi_irq_cache_mutex);
933 return (ret);
934 }
935
936 /*
937 * Walk the irq_cache_table and re-configure the link device to
938 * the saved state.
939 */
940 void
941 acpi_restore_link_devices(void)
942 {
943 irq_cache_t *irqcachep;
944 acpi_psm_lnk_t psmlnk;
945 int i, status;
946
947 /* XXX: may not need to hold this mutex */
948 mutex_enter(&acpi_irq_cache_mutex);
949 for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid;
950 irqcachep++, i++) {
951 if (irqcachep->lnkobj != NULL) {
952 /* only field used from psmlnk in set_irq is lnkobj */
953 psmlnk.lnkobj = irqcachep->lnkobj;
954 status = acpi_set_irq_resource(&psmlnk, irqcachep->irq);
955 /* warn if set_irq failed; soldier on */
956 if (status != ACPI_PSM_SUCCESS)
957 cmn_err(CE_WARN, "Could not restore interrupt "
958 "link device for IRQ 0x%x: Devices using "
959 "this IRQ may no longer function properly."
960 "\n", irqcachep->irq);
961 }
962 }
963 mutex_exit(&acpi_irq_cache_mutex);
964 }
965
966 int
967 acpi_poweroff(void)
968 {
969 extern int acpica_use_safe_delay;
970 ACPI_STATUS status;
971
972 PSM_VERBOSE_POWEROFF(("acpi_poweroff: starting poweroff\n"));
973
974 acpica_use_safe_delay = 1;
975
976 status = AcpiEnterSleepStatePrep(5);
977 if (status != AE_OK) {
978 PSM_VERBOSE_POWEROFF(("acpi_poweroff: failed to prepare for "
979 "poweroff, status=0x%x\n", status));
980 return (1);
981 }
982 ACPI_DISABLE_IRQS();
983 status = AcpiEnterSleepState(5);
984 ACPI_ENABLE_IRQS();
985
986 /* we should be off; if we get here it's an error */
987 PSM_VERBOSE_POWEROFF(("acpi_poweroff: failed to actually power "
988 "off, status=0x%x\n", status));
989 return (1);
990 }
991
992
993 /*
994 * psm_set_elcr() sets ELCR bit for specified vector
995 */
996 void
997 psm_set_elcr(int vecno, int val)
998 {
999 int elcr_port = ELCR_PORT1 + (vecno >> 3);
1000 int elcr_bit = 1 << (vecno & 0x07);
1001
1002 ASSERT((vecno >= 0) && (vecno < 16));
1003
1004 if (val) {
1005 /* set bit to force level-triggered mode */
1006 outb(elcr_port, inb(elcr_port) | elcr_bit);
1007 } else {
1008 /* clear bit to force edge-triggered mode */
1009 outb(elcr_port, inb(elcr_port) & ~elcr_bit);
1010 }
1011 }
1012
1013 /*
1014 * psm_get_elcr() returns status of ELCR bit for specific vector
1015 */
1016 int
1017 psm_get_elcr(int vecno)
1018 {
1019 int elcr_port = ELCR_PORT1 + (vecno >> 3);
1020 int elcr_bit = 1 << (vecno & 0x07);
1021
1022 ASSERT((vecno >= 0) && (vecno < 16));
1023
1024 return ((inb(elcr_port) & elcr_bit) ? 1 : 0);
1025 }