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