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 <sys/acpi/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. */
45struct 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
65acpidev_resource_handle_t
66acpidev_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
82void
83acpidev_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
108static void
109acpidev_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
159ACPI_STATUS
160acpidev_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
175ACPI_STATUS
176acpidev_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
203uint_t
204acpidev_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
219ACPI_STATUS
220acpidev_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
235ACPI_STATUS
236acpidev_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
263uint_t
264acpidev_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
279ACPI_STATUS
280acpidev_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
295ACPI_STATUS
296acpidev_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
321uint_t
322acpidev_resource_get_bus_count(acpidev_resource_handle_t rhdl)
323{
324	ASSERT(rhdl != NULL);
325	return (rhdl->acpidev_bus_count);
326}
327
328ACPI_STATUS
329acpidev_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
344ACPI_STATUS
345acpidev_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
370uint_t
371acpidev_resource_get_dma_count(acpidev_resource_handle_t rhdl)
372{
373	ASSERT(rhdl != NULL);
374	return (rhdl->acpidev_dma_count);
375}
376
377ACPI_STATUS
378acpidev_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
393ACPI_STATUS
394acpidev_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
419uint_t
420acpidev_resource_get_irq_count(acpidev_resource_handle_t rhdl)
421{
422	ASSERT(rhdl != NULL);
423	return (rhdl->acpidev_irq_count);
424}
425
426static ACPI_STATUS
427acpidev_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, &reg);
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, &reg);
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
614static ACPI_STATUS
615acpidev_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
734static ACPI_STATUS
735acpidev_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, &reg);
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, &reg);
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
949ACPI_STATUS
950acpidev_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
1008ACPI_STATUS
1009acpidev_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
1108out:
1109	/* Free resources allocated by acpidev_resource_walk. */
1110	acpidev_resource_handle_free(rhdl);
1111
1112	return (rc);
1113}
1114