1 /*
2 * Copyright (c) 2009, Intel Corporation.
3 * All rights reserved.
4 */
5
6 #include <sys/types.h>
7 #include <sys/cmn_err.h>
8 #include <sys/sysmacros.h>
9 #include <sys/sunddi.h>
10 #include <sys/sunndi.h>
11 #include <sys/acpi/acpi.h>
12 #include <sys/acpica.h>
13 #include <sys/acpidev.h>
14 #include <sys/acpidev_rsc.h>
15 #include <sys/acpidev_impl.h>
16
17 #define ACPIDEV_RES_INIT_ITEMS 8
18 #define ACPIDEV_RES_INCR_ITEMS 8
19
20 /* Data structure to hold parsed resources during walking. */
21 struct acpidev_resource_handle {
22 boolean_t acpidev_consumer;
23 int acpidev_reg_count;
24 int acpidev_reg_max;
25 acpidev_phys_spec_t *acpidev_regp;
26 acpidev_phys_spec_t acpidev_regs[ACPIDEV_RES_INIT_ITEMS];
27 int acpidev_range_count;
28 int acpidev_range_max;
29 acpidev_ranges_t *acpidev_rangep;
30 acpidev_ranges_t acpidev_ranges[ACPIDEV_RES_INIT_ITEMS];
31 int acpidev_bus_count;
32 int acpidev_bus_max;
33 acpidev_bus_range_t *acpidev_busp;
34 acpidev_bus_range_t acpidev_buses[ACPIDEV_RES_INIT_ITEMS];
35 int acpidev_irq_count;
36 int acpidev_irqp[ACPIDEV_RES_IRQ_MAX];
37 int acpidev_dma_count;
38 int acpidev_dmap[ACPIDEV_RES_DMA_MAX];
39 };
40
41 acpidev_resource_handle_t
42 acpidev_resource_handle_alloc(boolean_t consumer)
43 {
44 acpidev_resource_handle_t rhdl;
45
46 rhdl = kmem_zalloc(sizeof (*rhdl), KM_SLEEP);
47 rhdl->acpidev_consumer = consumer;
48 rhdl->acpidev_reg_max = ACPIDEV_RES_INIT_ITEMS;
49 rhdl->acpidev_regp = rhdl->acpidev_regs;
50 rhdl->acpidev_range_max = ACPIDEV_RES_INIT_ITEMS;
51 rhdl->acpidev_rangep = rhdl->acpidev_ranges;
52 rhdl->acpidev_bus_max = ACPIDEV_RES_INIT_ITEMS;
53 rhdl->acpidev_busp = rhdl->acpidev_buses;
54
55 return (rhdl);
56 }
57
58 void
59 acpidev_resource_handle_free(acpidev_resource_handle_t rhdl)
60 {
61 size_t sz;
62
63 ASSERT(rhdl != NULL);
64 if (rhdl != NULL) {
65 if (rhdl->acpidev_regp != rhdl->acpidev_regs) {
66 sz = sizeof (acpidev_phys_spec_t) *
67 rhdl->acpidev_reg_max;
68 kmem_free(rhdl->acpidev_regp, sz);
69 }
70 if (rhdl->acpidev_rangep != rhdl->acpidev_ranges) {
71 sz = sizeof (acpidev_ranges_t) *
72 rhdl->acpidev_range_max;
73 kmem_free(rhdl->acpidev_rangep, sz);
74 }
75 if (rhdl->acpidev_busp != rhdl->acpidev_buses) {
76 sz = sizeof (acpidev_bus_range_t) *
77 rhdl->acpidev_bus_max;
78 kmem_free(rhdl->acpidev_busp, sz);
79 }
80 kmem_free(rhdl, sizeof (struct acpidev_resource_handle));
81 }
82 }
83
84 static void
85 acpidev_resource_handle_grow(acpidev_resource_handle_t rhdl)
86 {
87 size_t sz;
88
89 if (rhdl->acpidev_reg_count == rhdl->acpidev_reg_max) {
90 acpidev_phys_spec_t *regp;
91
92 /* Prefer linear incremental here. */
93 rhdl->acpidev_reg_max += ACPIDEV_RES_INCR_ITEMS;
94 sz = sizeof (*regp) * rhdl->acpidev_reg_max;
95 regp = kmem_zalloc(sz, KM_SLEEP);
96 sz = sizeof (*regp) * rhdl->acpidev_reg_count;
97 bcopy(rhdl->acpidev_regp, regp, sz);
98 if (rhdl->acpidev_regp != rhdl->acpidev_regs) {
99 kmem_free(rhdl->acpidev_regp, sz);
100 }
101 rhdl->acpidev_regp = regp;
102 }
103
104 if (rhdl->acpidev_range_count == rhdl->acpidev_range_max) {
105 acpidev_ranges_t *rngp;
106
107 /* Prefer linear incremental here. */
108 rhdl->acpidev_range_max += ACPIDEV_RES_INCR_ITEMS;
109 sz = sizeof (*rngp) * rhdl->acpidev_range_max;
110 rngp = kmem_zalloc(sz, KM_SLEEP);
111 sz = sizeof (*rngp) * rhdl->acpidev_range_count;
112 bcopy(rhdl->acpidev_rangep, rngp, sz);
113 if (rhdl->acpidev_rangep != rhdl->acpidev_ranges) {
114 kmem_free(rhdl->acpidev_rangep, sz);
115 }
116 rhdl->acpidev_rangep = rngp;
117 }
118
119 if (rhdl->acpidev_bus_count == rhdl->acpidev_bus_max) {
120 acpidev_bus_range_t *busp;
121
122 /* Prefer linear incremental here. */
123 rhdl->acpidev_bus_max += ACPIDEV_RES_INCR_ITEMS;
124 sz = sizeof (*busp) * rhdl->acpidev_bus_max;
125 busp = kmem_zalloc(sz, KM_SLEEP);
126 sz = sizeof (*busp) * rhdl->acpidev_bus_count;
127 bcopy(rhdl->acpidev_busp, busp, sz);
128 if (rhdl->acpidev_busp != rhdl->acpidev_buses) {
129 kmem_free(rhdl->acpidev_busp, sz);
130 }
131 rhdl->acpidev_busp = busp;
132 }
133 }
134
135 ACPI_STATUS
136 acpidev_resource_insert_reg(acpidev_resource_handle_t rhdl,
137 acpidev_regspec_t *regp)
138 {
139 ASSERT(rhdl != NULL);
140 ASSERT(regp != NULL);
141 if (rhdl->acpidev_reg_count >= rhdl->acpidev_reg_max) {
142 acpidev_resource_handle_grow(rhdl);
143 }
144 ASSERT(rhdl->acpidev_reg_count < rhdl->acpidev_reg_max);
145 rhdl->acpidev_regp[rhdl->acpidev_reg_count] = *regp;
146 rhdl->acpidev_reg_count++;
147
148 return (AE_OK);
149 }
150
151 ACPI_STATUS
152 acpidev_resourec_get_regs(acpidev_resource_handle_t rhdl,
153 uint_t mask, uint_t value, acpidev_regspec_t *regp, uint_t *cntp)
154 {
155 uint_t i, j;
156
157 ASSERT(rhdl != NULL);
158 ASSERT(cntp != NULL);
159 if (rhdl == NULL || cntp == NULL || (regp == NULL && *cntp != 0)) {
160 return (AE_BAD_PARAMETER);
161 }
162 for (i = 0, j = 0; i < rhdl->acpidev_reg_count; i++) {
163 if ((rhdl->acpidev_regp[i].phys_hi & mask) == value) {
164 if (j < *cntp) {
165 regp[j] = rhdl->acpidev_regp[i];
166 }
167 j++;
168 }
169 }
170 if (j >= *cntp) {
171 *cntp = j;
172 return (AE_LIMIT);
173 } else {
174 *cntp = j;
175 return (AE_OK);
176 }
177 }
178
179 uint_t
180 acpidev_resource_get_reg_count(acpidev_resource_handle_t rhdl,
181 uint_t mask, uint_t value)
182 {
183 uint_t i, j;
184
185 ASSERT(rhdl != NULL);
186 for (i = 0, j = 0; i < rhdl->acpidev_reg_count; i++) {
187 if ((rhdl->acpidev_regp[i].phys_hi & mask) == value) {
188 j++;
189 }
190 }
191
192 return (j);
193 }
194
195 ACPI_STATUS
196 acpidev_resource_insert_range(acpidev_resource_handle_t rhdl,
197 acpidev_ranges_t *rangep)
198 {
199 ASSERT(rhdl != NULL);
200 ASSERT(rangep != NULL);
201 if (rhdl->acpidev_range_count >= rhdl->acpidev_range_max) {
202 acpidev_resource_handle_grow(rhdl);
203 }
204 ASSERT(rhdl->acpidev_range_count < rhdl->acpidev_range_max);
205 rhdl->acpidev_rangep[rhdl->acpidev_range_count] = *rangep;
206 rhdl->acpidev_range_count++;
207
208 return (AE_OK);
209 }
210
211 ACPI_STATUS
212 acpidev_resourec_get_ranges(acpidev_resource_handle_t rhdl,
213 uint_t mask, uint_t value, acpidev_ranges_t *rangep, uint_t *cntp)
214 {
215 uint_t i, j;
216
217 ASSERT(rhdl != NULL);
218 ASSERT(cntp != NULL);
219 if (rhdl == NULL || cntp == NULL || (rangep == NULL && *cntp != 0)) {
220 return (AE_BAD_PARAMETER);
221 }
222 for (i = 0, j = 0; i < rhdl->acpidev_range_count; i++) {
223 if ((rhdl->acpidev_rangep[i].child_hi & mask) == value) {
224 if (j < *cntp) {
225 rangep[j] = rhdl->acpidev_rangep[i];
226 }
227 j++;
228 }
229 }
230 if (j >= *cntp) {
231 *cntp = j;
232 return (AE_LIMIT);
233 } else {
234 *cntp = j;
235 return (AE_OK);
236 }
237 }
238
239 uint_t
240 acpidev_resource_get_range_count(acpidev_resource_handle_t rhdl,
241 uint_t mask, uint_t value)
242 {
243 uint_t i, j;
244
245 ASSERT(rhdl != NULL);
246 for (i = 0, j = 0; i < rhdl->acpidev_range_count; i++) {
247 if ((rhdl->acpidev_rangep[i].child_hi & mask) == value) {
248 j++;
249 }
250 }
251
252 return (j);
253 }
254
255 ACPI_STATUS
256 acpidev_resource_insert_bus(acpidev_resource_handle_t rhdl,
257 acpidev_bus_range_t *busp)
258 {
259 ASSERT(rhdl != NULL);
260 ASSERT(busp != NULL);
261 if (rhdl->acpidev_bus_count >= rhdl->acpidev_bus_max) {
262 acpidev_resource_handle_grow(rhdl);
263 }
264 ASSERT(rhdl->acpidev_bus_count < rhdl->acpidev_bus_max);
265 rhdl->acpidev_busp[rhdl->acpidev_bus_count] = *busp;
266 rhdl->acpidev_bus_count++;
267
268 return (AE_OK);
269 }
270
271 ACPI_STATUS
272 acpidev_resourec_get_buses(acpidev_resource_handle_t rhdl,
273 acpidev_bus_range_t *busp, uint_t *cntp)
274 {
275 uint_t i, j;
276
277 ASSERT(rhdl != NULL);
278 ASSERT(cntp != NULL);
279 if (rhdl == NULL || cntp == NULL || (busp == NULL && *cntp != 0)) {
280 return (AE_BAD_PARAMETER);
281 }
282 for (i = 0, j = 0; i < rhdl->acpidev_bus_count; i++) {
283 if (j < *cntp) {
284 busp[j] = rhdl->acpidev_busp[i];
285 }
286 j++;
287 }
288 if (j >= *cntp) {
289 *cntp = j;
290 return (AE_LIMIT);
291 } else {
292 *cntp = j;
293 return (AE_OK);
294 }
295 }
296
297 uint_t
298 acpidev_resource_get_bus_count(acpidev_resource_handle_t rhdl)
299 {
300 ASSERT(rhdl != NULL);
301 return (rhdl->acpidev_bus_count);
302 }
303
304 ACPI_STATUS
305 acpidev_resource_insert_dma(acpidev_resource_handle_t rhdl, int dma)
306 {
307 ASSERT(rhdl != NULL);
308 if (rhdl->acpidev_dma_count >= ACPIDEV_RES_DMA_MAX) {
309 ACPIDEV_DEBUG(CE_WARN,
310 "acpidev: too many DMA resources, max %u.",
311 ACPIDEV_RES_DMA_MAX);
312 return (AE_LIMIT);
313 }
314 rhdl->acpidev_dmap[rhdl->acpidev_dma_count] = dma;
315 rhdl->acpidev_dma_count++;
316
317 return (AE_OK);
318 }
319
320 ACPI_STATUS
321 acpidev_resourec_get_dmas(acpidev_resource_handle_t rhdl,
322 uint_t *dmap, uint_t *cntp)
323 {
324 uint_t i, j;
325
326 ASSERT(rhdl != NULL);
327 ASSERT(cntp != NULL);
328 if (rhdl == NULL || cntp == NULL || (dmap == NULL && *cntp != 0)) {
329 return (AE_BAD_PARAMETER);
330 }
331 for (i = 0, j = 0; i < rhdl->acpidev_dma_count; i++) {
332 if (j < *cntp) {
333 dmap[j] = rhdl->acpidev_dmap[i];
334 }
335 j++;
336 }
337 if (j >= *cntp) {
338 *cntp = j;
339 return (AE_LIMIT);
340 } else {
341 *cntp = j;
342 return (AE_OK);
343 }
344 }
345
346 uint_t
347 acpidev_resource_get_dma_count(acpidev_resource_handle_t rhdl)
348 {
349 ASSERT(rhdl != NULL);
350 return (rhdl->acpidev_dma_count);
351 }
352
353 ACPI_STATUS
354 acpidev_resource_insert_irq(acpidev_resource_handle_t rhdl, int irq)
355 {
356 ASSERT(rhdl != NULL);
357 if (rhdl->acpidev_irq_count >= ACPIDEV_RES_IRQ_MAX) {
358 ACPIDEV_DEBUG(CE_WARN,
359 "acpidev: too many IRQ resources, max %u.",
360 ACPIDEV_RES_IRQ_MAX);
361 return (AE_LIMIT);
362 }
363 rhdl->acpidev_irqp[rhdl->acpidev_irq_count] = irq;
364 rhdl->acpidev_irq_count++;
365
366 return (AE_OK);
367 }
368
369 ACPI_STATUS
370 acpidev_resourec_get_irqs(acpidev_resource_handle_t rhdl,
371 uint_t *irqp, uint_t *cntp)
372 {
373 uint_t i, j;
374
375 ASSERT(rhdl != NULL);
376 ASSERT(cntp != NULL);
377 if (rhdl == NULL || cntp == NULL || (irqp == NULL && *cntp != 0)) {
378 return (AE_BAD_PARAMETER);
379 }
380 for (i = 0, j = 0; i < rhdl->acpidev_irq_count; i++) {
381 if (j < *cntp) {
382 irqp[j] = rhdl->acpidev_irqp[i];
383 }
384 j++;
385 }
386 if (j >= *cntp) {
387 *cntp = j;
388 return (AE_LIMIT);
389 } else {
390 *cntp = j;
391 return (AE_OK);
392 }
393 }
394
395 uint_t
396 acpidev_resource_get_irq_count(acpidev_resource_handle_t rhdl)
397 {
398 ASSERT(rhdl != NULL);
399 return (rhdl->acpidev_irq_count);
400 }
401
402 static ACPI_STATUS
403 acpidev_resource_address64(acpidev_resource_handle_t rhdl,
404 ACPI_RESOURCE_ADDRESS64 *addrp)
405 {
406 ACPI_STATUS rc = AE_OK;
407 uint_t high;
408
409 ASSERT(addrp != NULL && rhdl != NULL);
410 if (addrp->AddressLength == 0) {
411 return (AE_OK);
412 }
413
414 switch (addrp->ResourceType) {
415 case ACPI_MEMORY_RANGE:
416 high = ACPIDEV_REG_TYPE_MEMORY;
417 if (addrp->Decode == ACPI_SUB_DECODE) {
418 high |= ACPIDEV_REG_SUB_DEC;
419 }
420 if (addrp->Info.Mem.Translation) {
421 high |= ACPIDEV_REG_TRANSLATED;
422 }
423 if (addrp->Info.Mem.Caching == ACPI_NON_CACHEABLE_MEMORY) {
424 high |= ACPIDEV_REG_MEM_COHERENT_NC;
425 } else if (addrp->Info.Mem.Caching == ACPI_CACHABLE_MEMORY) {
426 high |= ACPIDEV_REG_MEM_COHERENT_CA;
427 } else if (addrp->Info.Mem.Caching ==
428 ACPI_WRITE_COMBINING_MEMORY) {
429 high |= ACPIDEV_REG_MEM_COHERENT_WC;
430 } else if (addrp->Info.Mem.Caching ==
431 ACPI_PREFETCHABLE_MEMORY) {
432 high |= ACPIDEV_REG_MEM_COHERENT_PF;
433 } else {
434 ACPIDEV_DEBUG(CE_WARN,
435 "acpidev: unknown memory caching type %u.",
436 addrp->Info.Mem.Caching);
437 rc = AE_ERROR;
438 break;
439 }
440 if (addrp->Info.Mem.WriteProtect == ACPI_READ_WRITE_MEMORY) {
441 high |= ACPIDEV_REG_MEM_WRITABLE;
442 }
443
444 /* Generate 'reg' for producer. */
445 if (addrp->ProducerConsumer == ACPI_CONSUMER &&
446 rhdl->acpidev_consumer == B_TRUE) {
447 acpidev_regspec_t reg;
448
449 reg.phys_hi = high;
450 reg.phys_mid = addrp->Minimum >> 32;
451 reg.phys_low = addrp->Minimum & 0xFFFFFFFF;
452 reg.size_hi = addrp->AddressLength >> 32;
453 reg.size_low = addrp->AddressLength & 0xFFFFFFFF;
454 rc = acpidev_resource_insert_reg(rhdl, ®);
455 if (ACPI_FAILURE(rc)) {
456 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to "
457 "insert regspec into resource handle.");
458 }
459 /* Generate 'ranges' for producer. */
460 } else if (addrp->ProducerConsumer == ACPI_PRODUCER &&
461 rhdl->acpidev_consumer == B_FALSE) {
462 uint64_t paddr;
463 acpidev_ranges_t range;
464
465 range.child_hi = high;
466 range.child_mid = addrp->Minimum >> 32;
467 range.child_low = addrp->Minimum & 0xFFFFFFFF;
468 /* It's IO on parent side if Translation is true. */
469 if (addrp->Info.Mem.Translation) {
470 range.parent_hi = ACPIDEV_REG_TYPE_IO;
471 } else {
472 range.parent_hi = high;
473 }
474 paddr = addrp->Minimum + addrp->TranslationOffset;
475 range.parent_mid = paddr >> 32;
476 range.parent_low = paddr & 0xFFFFFFFF;
477 range.size_hi = addrp->AddressLength >> 32;
478 range.size_low = addrp->AddressLength & 0xFFFFFFFF;
479 rc = acpidev_resource_insert_range(rhdl, &range);
480 if (ACPI_FAILURE(rc)) {
481 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to "
482 "insert range into resource handle.");
483 }
484 }
485 break;
486
487 case ACPI_IO_RANGE:
488 high = ACPIDEV_REG_TYPE_IO;
489 if (addrp->Decode == ACPI_SUB_DECODE) {
490 high |= ACPIDEV_REG_SUB_DEC;
491 }
492 if (addrp->Info.Io.Translation) {
493 high |= ACPIDEV_REG_TRANSLATED;
494 }
495 if (addrp->Info.Io.RangeType == ACPI_NON_ISA_ONLY_RANGES) {
496 high |= ACPIDEV_REG_IO_RANGE_NONISA;
497 } else if (addrp->Info.Io.RangeType == ACPI_ISA_ONLY_RANGES) {
498 high |= ACPIDEV_REG_IO_RANGE_ISA;
499 } else if (addrp->Info.Io.RangeType == ACPI_ENTIRE_RANGE) {
500 high |= ACPIDEV_REG_IO_RANGE_FULL;
501 } else {
502 ACPIDEV_DEBUG(CE_WARN,
503 "acpidev: unknown IO range type %u.",
504 addrp->Info.Io.RangeType);
505 rc = AE_ERROR;
506 break;
507 }
508 if (addrp->Info.Io.TranslationType == ACPI_SPARSE_TRANSLATION) {
509 high |= ACPIDEV_REG_IO_SPARSE;
510 }
511
512 /* Generate 'reg' for producer. */
513 if (addrp->ProducerConsumer == ACPI_CONSUMER &&
514 rhdl->acpidev_consumer == B_TRUE) {
515 acpidev_regspec_t reg;
516
517 reg.phys_hi = high;
518 reg.phys_mid = addrp->Minimum >> 32;
519 reg.phys_low = addrp->Minimum & 0xFFFFFFFF;
520 reg.size_hi = addrp->AddressLength >> 32;
521 reg.size_low = addrp->AddressLength & 0xFFFFFFFF;
522 rc = acpidev_resource_insert_reg(rhdl, ®);
523 if (ACPI_FAILURE(rc)) {
524 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to "
525 "insert regspec into resource handle.");
526 }
527 /* Generate 'ranges' for producer. */
528 } else if (addrp->ProducerConsumer == ACPI_PRODUCER &&
529 rhdl->acpidev_consumer == B_FALSE) {
530 uint64_t paddr;
531 acpidev_ranges_t range;
532
533 range.child_hi = high;
534 range.child_mid = addrp->Minimum >> 32;
535 range.child_low = addrp->Minimum & 0xFFFFFFFF;
536 /* It's Memory on parent side if Translation is true. */
537 if (addrp->Info.Io.Translation) {
538 range.parent_hi = ACPIDEV_REG_TYPE_MEMORY;
539 } else {
540 range.parent_hi = high;
541 }
542 paddr = addrp->Minimum + addrp->TranslationOffset;
543 range.parent_mid = paddr >> 32;
544 range.parent_low = paddr & 0xFFFFFFFF;
545 range.size_hi = addrp->AddressLength >> 32;
546 range.size_low = addrp->AddressLength & 0xFFFFFFFF;
547 rc = acpidev_resource_insert_range(rhdl, &range);
548 if (ACPI_FAILURE(rc)) {
549 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to "
550 "insert range into resource handle.");
551 }
552 }
553 break;
554
555 case ACPI_BUS_NUMBER_RANGE:
556 /* Only support producer of BUS. */
557 if (addrp->ProducerConsumer == ACPI_PRODUCER &&
558 rhdl->acpidev_consumer == B_FALSE) {
559 uint64_t end;
560 acpidev_bus_range_t bus;
561
562 end = addrp->Minimum + addrp->AddressLength;
563 if (end < addrp->Minimum || end > UINT_MAX) {
564 ACPIDEV_DEBUG(CE_WARN, "acpidev: bus range "
565 "in ADDRESS64 is invalid.");
566 rc = AE_ERROR;
567 break;
568 }
569 bus.bus_start = addrp->Minimum & 0xFFFFFFFF;
570 bus.bus_end = end & 0xFFFFFFFF;
571 ASSERT(bus.bus_start <= bus.bus_end);
572 rc = acpidev_resource_insert_bus(rhdl, &bus);
573 if (ACPI_FAILURE(rc)) {
574 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to "
575 "insert bus range into resource handle.");
576 }
577 }
578 break;
579
580 default:
581 ACPIDEV_DEBUG(CE_WARN,
582 "acpidev: unknown resource type %u in ADDRESS64.",
583 addrp->ResourceType);
584 rc = AE_BAD_PARAMETER;
585 }
586
587 return (rc);
588 }
589
590 static ACPI_STATUS
591 acpidev_resource_walk_producer(ACPI_RESOURCE *rscp, void *ctxp)
592 {
593 ACPI_STATUS rc = AE_OK;
594 acpidev_resource_handle_t rhdl;
595
596 ASSERT(ctxp != NULL);
597 rhdl = (acpidev_resource_handle_t)ctxp;
598 ASSERT(rhdl->acpidev_consumer == B_FALSE);
599
600 switch (rscp->Type) {
601 case ACPI_RESOURCE_TYPE_DMA:
602 case ACPI_RESOURCE_TYPE_IRQ:
603 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
604 case ACPI_RESOURCE_TYPE_FIXED_IO:
605 case ACPI_RESOURCE_TYPE_MEMORY24:
606 case ACPI_RESOURCE_TYPE_MEMORY32:
607 case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
608 case ACPI_RESOURCE_TYPE_GENERIC_REGISTER:
609 case ACPI_RESOURCE_TYPE_VENDOR:
610 ACPIDEV_DEBUG(CE_NOTE,
611 "acpidev: unsupported producer resource type %u, ignore.",
612 rscp->Type);
613 break;
614
615 case ACPI_RESOURCE_TYPE_IO:
616 {
617 acpidev_ranges_t range;
618
619 range.child_hi = ACPIDEV_REG_TYPE_IO;
620 range.child_hi |= ACPIDEV_REG_IO_RANGE_FULL;
621 if (rscp->Data.Io.IoDecode == ACPI_DECODE_16) {
622 range.child_hi |= ACPIDEV_REG_IO_DECODE16;
623 }
624 range.parent_hi = range.child_hi;
625 range.parent_mid = range.child_mid = 0;
626 range.parent_low = range.child_low = rscp->Data.Io.Minimum;
627 range.size_hi = 0;
628 range.size_low = rscp->Data.Io.AddressLength;
629 if ((uint64_t)range.child_low + range.size_low > UINT16_MAX) {
630 ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid IO record, "
631 "IO max is out of range.");
632 rc = AE_ERROR;
633 } else if (range.size_low != 0) {
634 rc = acpidev_resource_insert_range(rhdl, &range);
635 if (ACPI_FAILURE(rc)) {
636 ACPIDEV_DEBUG(CE_WARN,"acpidev: failed to "
637 "insert range into resource handle.");
638 }
639 }
640 break;
641 }
642
643 case ACPI_RESOURCE_TYPE_ADDRESS16:
644 case ACPI_RESOURCE_TYPE_ADDRESS32:
645 case ACPI_RESOURCE_TYPE_ADDRESS64:
646 {
647 ACPI_RESOURCE_ADDRESS64 addr64;
648
649 if (rscp->Data.Address.ProducerConsumer != ACPI_PRODUCER) {
650 ACPIDEV_DEBUG(CE_NOTE, "acpidev: producer encounters "
651 "resource CONSUMER, ignore.");
652 } else if (ACPI_FAILURE(AcpiResourceToAddress64(rscp,
653 &addr64))) {
654 ACPIDEV_DEBUG(CE_WARN,
655 "acpidev: failed to convert resource to ADDR64.");
656 } else if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl,
657 &addr64))) {
658 ACPIDEV_DEBUG(CE_WARN,
659 "acpidev: failed to handle ADDRESS resource.");
660 }
661 break;
662 }
663
664 case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
665 {
666 ACPI_RESOURCE_ADDRESS64 addr64;
667
668 if (rscp->Data.ExtAddress64.ProducerConsumer != ACPI_PRODUCER) {
669 ACPIDEV_DEBUG(CE_NOTE, "acpidev: producer encounters "
670 "resource CONSUMER, ignore.");
671 break;
672 }
673
674 *(ACPI_RESOURCE_ADDRESS *)&addr64 = rscp->Data.Address;
675 addr64.Granularity = rscp->Data.ExtAddress64.Granularity;
676 addr64.Minimum = rscp->Data.ExtAddress64.Minimum;
677 addr64.Maximum = rscp->Data.ExtAddress64.Maximum;
678 addr64.TranslationOffset =
679 rscp->Data.ExtAddress64.TranslationOffset;
680 addr64.AddressLength = rscp->Data.ExtAddress64.AddressLength;
681 if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl,
682 &addr64))) {
683 ACPIDEV_DEBUG(CE_WARN,
684 "acpidev: failed to handle EXTADDRESS resource.");
685 }
686 break;
687 }
688
689 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
690 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
691 ACPIDEV_DEBUG(CE_NOTE, "acpidev: producer encounters "
692 "START_DEPENDENT or END_DEPENDENT tag, ignore.");
693 break;
694
695 case ACPI_RESOURCE_TYPE_END_TAG:
696 /* Finish walking when encounter END_TAG. */
697 rc = AE_CTRL_TERMINATE;
698 break;
699
700 default:
701 ACPIDEV_DEBUG(CE_NOTE,
702 "acpidev: unknown ACPI resource type %u, ignore.",
703 rscp->Type);
704 break;
705 }
706
707 return (rc);
708 }
709
710 static ACPI_STATUS
711 acpidev_resource_walk_consumer(ACPI_RESOURCE *rscp, void *ctxp)
712 {
713 ACPI_STATUS rc = AE_OK;
714 acpidev_resource_handle_t rhdl;
715
716 ASSERT(ctxp != NULL);
717 rhdl = (acpidev_resource_handle_t)ctxp;
718 ASSERT(rhdl->acpidev_consumer == B_TRUE);
719
720 switch (rscp->Type) {
721 case ACPI_RESOURCE_TYPE_MEMORY24:
722 case ACPI_RESOURCE_TYPE_GENERIC_REGISTER:
723 case ACPI_RESOURCE_TYPE_VENDOR:
724 ACPIDEV_DEBUG(CE_NOTE,
725 "acpidev: unsupported consumer resource type %u, ignore.",
726 rscp->Type);
727 break;
728
729 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
730 {
731 int i;
732
733 if (rscp->Data.ExtendedIrq.ProducerConsumer != ACPI_CONSUMER) {
734 ACPIDEV_DEBUG(CE_NOTE, "acpidev: consumer encounters "
735 "resource PRODUCER, ignore.");
736 break;
737 }
738 for (i = 0; i < rscp->Data.ExtendedIrq.InterruptCount; i++) {
739 if (ACPI_SUCCESS(acpidev_resource_insert_irq(rhdl,
740 rscp->Data.ExtendedIrq.Interrupts[i]))) {
741 continue;
742 }
743 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to insert"
744 "IRQ into resource handle.");
745 rc = AE_ERROR;
746 break;
747 }
748 break;
749 }
750
751 case ACPI_RESOURCE_TYPE_IRQ:
752 {
753 int i;
754
755 for (i = 0; i < rscp->Data.Irq.InterruptCount; i++) {
756 if (ACPI_SUCCESS(acpidev_resource_insert_irq(rhdl,
757 rscp->Data.Irq.Interrupts[i]))) {
758 continue;
759 }
760 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to insert"
761 "IRQ into resource handle.");
762 rc = AE_ERROR;
763 break;
764 }
765 break;
766 }
767
768 case ACPI_RESOURCE_TYPE_DMA:
769 {
770 int i;
771
772 for (i = 0; i < rscp->Data.Dma.ChannelCount; i++) {
773 if (ACPI_SUCCESS(acpidev_resource_insert_dma(rhdl,
774 rscp->Data.Dma.Channels[i]))) {
775 continue;
776 }
777 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to insert"
778 "dma into resource handle.");
779 rc = AE_ERROR;
780 break;
781 }
782 break;
783 }
784
785 case ACPI_RESOURCE_TYPE_IO:
786 case ACPI_RESOURCE_TYPE_FIXED_IO:
787 {
788 acpidev_regspec_t reg;
789
790 reg.phys_hi = ACPIDEV_REG_TYPE_IO;
791 reg.phys_hi |= ACPIDEV_REG_IO_RANGE_FULL;
792 if (rscp->Type == ACPI_RESOURCE_TYPE_IO) {
793 if (rscp->Data.Io.IoDecode == ACPI_DECODE_16) {
794 reg.phys_hi |= ACPIDEV_REG_IO_DECODE16;
795 }
796 reg.phys_low = rscp->Data.Io.Minimum;
797 reg.size_low = rscp->Data.Io.AddressLength;
798 } else {
799 reg.phys_hi |= ACPIDEV_REG_IO_DECODE16;
800 reg.phys_low = rscp->Data.FixedIo.Address;
801 reg.size_low = rscp->Data.FixedIo.AddressLength;
802 }
803 reg.phys_mid = 0;
804 reg.size_hi = 0;
805 if ((uint64_t)reg.phys_low + reg.size_low > UINT16_MAX) {
806 ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid IO/FIXEDIO "
807 "record, IO max is out of range.");
808 rc = AE_ERROR;
809 } else if (reg.size_low != 0) {
810 rc = acpidev_resource_insert_reg(rhdl, ®);
811 if (ACPI_FAILURE(rc)) {
812 ACPIDEV_DEBUG(CE_WARN,"acpidev: failed to "
813 "insert reg into resource handle.");
814 }
815 }
816 break;
817 }
818
819 case ACPI_RESOURCE_TYPE_MEMORY32:
820 case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
821 {
822 acpidev_regspec_t reg;
823
824 reg.phys_hi = ACPIDEV_REG_TYPE_MEMORY;
825 reg.phys_hi |= ACPIDEV_REG_MEM_COHERENT_CA;
826 if (rscp->Type == ACPI_RESOURCE_TYPE_MEMORY32) {
827 if (rscp->Data.Memory32.WriteProtect ==
828 ACPI_READ_WRITE_MEMORY) {
829 reg.phys_hi |= ACPIDEV_REG_MEM_WRITABLE;
830 }
831 reg.phys_low = rscp->Data.Memory32.Minimum;
832 reg.size_low = rscp->Data.Memory32.AddressLength;
833 } else {
834 if (rscp->Data.FixedMemory32.WriteProtect ==
835 ACPI_READ_WRITE_MEMORY) {
836 reg.phys_hi |= ACPIDEV_REG_MEM_WRITABLE;
837 }
838 reg.phys_low = rscp->Data.FixedMemory32.Address;
839 reg.size_low = rscp->Data.FixedMemory32.AddressLength;
840 }
841 reg.phys_mid = 0;
842 reg.size_hi = 0;
843 if ((uint64_t)reg.phys_low + reg.size_low > UINT32_MAX) {
844 ACPIDEV_DEBUG(CE_WARN,
845 "acpidev: invalid MEMORY32/FIXEDMEMORY32 record, "
846 "memroy max is out of range.");
847 rc = AE_ERROR;
848 } else if (reg.size_low != 0) {
849 rc = acpidev_resource_insert_reg(rhdl, ®);
850 if (ACPI_FAILURE(rc)) {
851 ACPIDEV_DEBUG(CE_WARN,"acpidev: failed to "
852 "insert reg into resource handle.");
853 }
854 }
855 break;
856 }
857
858 case ACPI_RESOURCE_TYPE_ADDRESS16:
859 case ACPI_RESOURCE_TYPE_ADDRESS32:
860 case ACPI_RESOURCE_TYPE_ADDRESS64:
861 {
862 ACPI_RESOURCE_ADDRESS64 addr64;
863
864 if (rscp->Data.Address.ProducerConsumer != ACPI_CONSUMER) {
865 ACPIDEV_DEBUG(CE_NOTE, "acpidev: consumer encounters "
866 "resource PRODUCER, ignore.");
867 } else if (ACPI_FAILURE(AcpiResourceToAddress64(rscp,
868 &addr64))) {
869 ACPIDEV_DEBUG(CE_WARN,
870 "acpidev: failed to convert resource to ADDR64.");
871 } else if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl,
872 &addr64))) {
873 ACPIDEV_DEBUG(CE_WARN,
874 "acpidev: failed to handle ADDRESS resource.");
875 }
876 break;
877 }
878
879 case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
880 {
881 ACPI_RESOURCE_ADDRESS64 addr64;
882
883 if (rscp->Data.ExtAddress64.ProducerConsumer != ACPI_CONSUMER) {
884 ACPIDEV_DEBUG(CE_NOTE, "acpidev: consumer encounters "
885 "resource PRODUCER, ignore.");
886 break;
887 }
888
889 *(ACPI_RESOURCE_ADDRESS *)&addr64 = rscp->Data.Address;
890 addr64.Granularity = rscp->Data.ExtAddress64.Granularity;
891 addr64.Minimum = rscp->Data.ExtAddress64.Minimum;
892 addr64.Maximum = rscp->Data.ExtAddress64.Maximum;
893 addr64.TranslationOffset =
894 rscp->Data.ExtAddress64.TranslationOffset;
895 addr64.AddressLength = rscp->Data.ExtAddress64.AddressLength;
896 if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl,
897 &addr64))) {
898 ACPIDEV_DEBUG(CE_WARN,
899 "acpidev: failed to handle EXTADDRESS resource.");
900 }
901 break;
902 }
903
904 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
905 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
906 ACPIDEV_DEBUG(CE_NOTE, "acpidev: consumer encounters "
907 "START_DEPENDENT or END_DEPENDENT tag, ignore.");
908 break;
909
910 case ACPI_RESOURCE_TYPE_END_TAG:
911 /* Finish walking when encounter END_TAG. */
912 rc = AE_CTRL_TERMINATE;
913 break;
914
915 default:
916 ACPIDEV_DEBUG(CE_NOTE,
917 "acpidev: unknown ACPI resource type %u, ignore.",
918 rscp->Type);
919 break;
920 }
921
922 return (rc);
923 }
924
925 ACPI_STATUS
926 acpidev_resource_walk(ACPI_HANDLE hdl, char *method,
927 boolean_t consumer, acpidev_resource_handle_t *rhdlp)
928 {
929 ACPI_STATUS rc = AE_OK;
930 ACPI_HANDLE mhdl = NULL;
931 acpidev_resource_handle_t rhdl = NULL;
932
933 ASSERT(hdl != NULL);
934 ASSERT(method != NULL);
935 ASSERT(rhdlp != NULL);
936 if (hdl == NULL) {
937 ACPIDEV_DEBUG(CE_WARN,
938 "acpidev: hdl is NULL in walk_resource().");
939 return (AE_BAD_PARAMETER);
940 } else if (method == NULL) {
941 ACPIDEV_DEBUG(CE_WARN,
942 "acpidev: method is NULL in walk_resource().");
943 return (AE_BAD_PARAMETER);
944 } else if (rhdlp == NULL) {
945 ACPIDEV_DEBUG(CE_WARN,
946 "acpidev: resource handle ptr is NULL in walk_resource().");
947 return (AE_BAD_PARAMETER);
948 }
949
950 /* Check whether method exists under object. */
951 if (ACPI_FAILURE(AcpiGetHandle(hdl, method, &mhdl))) {
952 char *objname = acpidev_get_object_name(hdl);
953 ACPIDEV_DEBUG(CE_NOTE,
954 "acpidev: method %s doesn't exist under %s",
955 method, objname);
956 acpidev_free_object_name(hdl);
957 return (AE_NOT_FOUND);
958 }
959
960 /* Walk all resources. */
961 rhdl = acpidev_resource_handle_alloc(consumer);
962 if (consumer) {
963 rc = AcpiWalkResources(hdl, method,
964 acpidev_resource_walk_consumer, rhdl);
965 } else {
966 rc = AcpiWalkResources(hdl, method,
967 acpidev_resource_walk_producer, rhdl);
968 }
969 if (ACPI_SUCCESS(rc)) {
970 *rhdlp = rhdl;
971 } else {
972 acpidev_resource_handle_free(rhdl);
973 }
974 if (ACPI_FAILURE(rc)) {
975 char *objname = acpidev_get_object_name(hdl);
976 ACPIDEV_DEBUG(CE_WARN,
977 "acpidev: failed to walk resource from method %s under %s.",
978 method, objname);
979 acpidev_free_object_name(hdl);
980 }
981
982 return (rc);
983 }
984
985 ACPI_STATUS
986 acpidev_resource_process(acpidev_walk_info_t *infop, boolean_t consumer)
987 {
988 ACPI_STATUS rc;
989 char path[MAXPATHLEN];
990 acpidev_resource_handle_t rhdl = NULL;
991
992 ASSERT(infop != NULL);
993 if (infop == NULL) {
994 ACPIDEV_DEBUG(CE_WARN,
995 "acpidev: invalid parameter to resource_process().");
996 return (AE_BAD_PARAMETER);
997 }
998
999 /* Walk all resources. */
1000 (void) ddi_pathname(infop->awi_dip, path);
1001 rc = acpidev_resource_walk(infop->awi_hdl, METHOD_NAME__CRS,
1002 consumer, &rhdl);
1003 if (ACPI_FAILURE(rc)) {
1004 ACPIDEV_DEBUG(CE_WARN,
1005 "acpidev: failed to walk ACPI resources of %s(%s).",
1006 path, infop->awi_name);
1007 return (rc);
1008 }
1009
1010 /* Create device properties for consumer. */
1011 if (consumer) {
1012 /* Create 'reg' and 'assigned-addresses' properties. */
1013 if (rhdl->acpidev_reg_count > 0 &&
1014 ndi_prop_update_int_array( DDI_DEV_T_NONE, infop->awi_dip,
1015 "reg", (int *)rhdl->acpidev_regp,
1016 rhdl->acpidev_reg_count * sizeof (acpidev_regspec_t) /
1017 sizeof (int)) != NDI_SUCCESS) {
1018 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set "
1019 "'reg' property for %s.", path);
1020 rc = AE_ERROR;
1021 goto out;
1022 }
1023 if (rhdl->acpidev_reg_count > 0 &&
1024 ndi_prop_update_int_array( DDI_DEV_T_NONE, infop->awi_dip,
1025 "assigned-addresses", (int *)rhdl->acpidev_regp,
1026 rhdl->acpidev_reg_count * sizeof (acpidev_regspec_t) /
1027 sizeof (int)) != NDI_SUCCESS) {
1028 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set "
1029 "'assigned-addresses' property for %s.", path);
1030 rc = AE_ERROR;
1031 goto out;
1032 }
1033
1034 /* Create 'interrutps' property. */
1035 if (rhdl->acpidev_irq_count > 0 &&
1036 ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip,
1037 "interrupts", (int *)rhdl->acpidev_irqp,
1038 rhdl->acpidev_irq_count) != NDI_SUCCESS) {
1039 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set "
1040 "'interrupts' property for %s.", path);
1041 rc = AE_ERROR;
1042 goto out;
1043 }
1044
1045 /* Create 'dma-channels' property. */
1046 if (rhdl->acpidev_dma_count > 0 &&
1047 ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip,
1048 "dma-channels", (int *)rhdl->acpidev_dmap,
1049 rhdl->acpidev_dma_count) != NDI_SUCCESS) {
1050 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set "
1051 "'dma-channels' property for %s.", path);
1052 rc = AE_ERROR;
1053 goto out;
1054 }
1055
1056 /* Create device properties for producer. */
1057 } else {
1058 /* Create 'ranges' property. */
1059 if (rhdl->acpidev_range_count > 0 &&
1060 ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip,
1061 "ranges", (int *)rhdl->acpidev_rangep,
1062 rhdl->acpidev_range_count * sizeof (acpidev_ranges_t) /
1063 sizeof (int)) != NDI_SUCCESS) {
1064 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set "
1065 "'ranges' property for %s.", path);
1066 rc = AE_ERROR;
1067 goto out;
1068 }
1069
1070 /* Create 'bus-ranges' property. */
1071 if (rhdl->acpidev_bus_count > 0 &&
1072 ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip,
1073 "bus-range", (int *)rhdl->acpidev_busp,
1074 rhdl->acpidev_bus_count * sizeof (acpidev_bus_range_t) /
1075 sizeof (int)) != NDI_SUCCESS) {
1076 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set "
1077 "'bus-range' property for %s.", path);
1078 rc = AE_ERROR;
1079 goto out;
1080 }
1081 }
1082
1083 out:
1084 /* Free resources allocated by acpidev_resource_walk. */
1085 acpidev_resource_handle_free(rhdl);
1086
1087 return (rc);
1088 }