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