xref: /illumos-gate/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_container.c (revision d8a7fe16f62711cdc5c4267da8b34ff24a6b668c)
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, Intel Corporation.
27  * All rights reserved.
28  */
29 
30 /*
31  * There are three types of container objects defined in the ACPI Spec as below.
32  * PNP0A05: Generic Container Device
33  *   A device whose settings are totally controlled by its ACPI resource
34  *   information, and otherwise needs no device or bus-specific driver support.
35  *   This was originally known as Generic ISA Bus Device.
36  *   This ID should only be used for containers that do not produce resources
37  *   for consumption by child devices. Any system resources claimed by a PNP0A05
38  *   device's _CRS object must be consumed by the container itself.
39  * PNP0A06: Generic Container Device
40  *   This device behaves exactly the same as the PNP0A05 device.
41  *   This was originally known as Extended I/O Bus.
42  *   This ID should only be used for containers that do not produce resources
43  *   for consumption by child devices. Any system resources claimed by a PNP0A06
44  *   device's _CRS object must be consumed by the container itself.
45  * ACPI0004: Module Device.
46  *   This device is a container object that acts as a bus node in a namespace.
47  *   A Module Device without any of the _CRS, _PRS and _SRS methods behaves
48  *   the same way as the Generic Container Devices (PNP0A05 or PNP0A06).
49  *   If the Module Device contains a _CRS method, only the resources
50  *   described in the _CRS are available for consumption by its child devices.
51  *   Also, the Module Device can support _PRS and _SRS methods if _CRS is
52  *   supported.
53  */
54 
55 #include <sys/types.h>
56 #include <sys/atomic.h>
57 #include <sys/sunddi.h>
58 #include <sys/sunndi.h>
59 #include <sys/acpi/acpi.h>
60 #include <sys/acpica.h>
61 #include <sys/acpidev.h>
62 #include <sys/acpidev_impl.h>
63 
64 static ACPI_STATUS acpidev_container_probe(acpidev_walk_info_t *infop);
65 static acpidev_filter_result_t acpidev_container_filter(
66     acpidev_walk_info_t *infop, char *devname, int maxlen);
67 static ACPI_STATUS acpidev_container_init(acpidev_walk_info_t *infop);
68 static acpidev_filter_result_t acpidev_container_filter_func(
69     acpidev_walk_info_t *infop, ACPI_HANDLE hdl, acpidev_filter_rule_t *rulep,
70     char *devname, int devnamelen);
71 
72 /*
73  * Default class driver for ACPI container objects.
74  */
75 acpidev_class_t acpidev_class_container = {
76 	0,				/* adc_refcnt */
77 	ACPIDEV_CLASS_REV1,		/* adc_version */
78 	ACPIDEV_CLASS_ID_CONTAINER,	/* adc_class_id */
79 	"ACPI Container",		/* adc_class_name */
80 	ACPIDEV_TYPE_CONTAINER,		/* adc_dev_type */
81 	NULL,				/* adc_private */
82 	NULL,				/* adc_pre_probe */
83 	NULL,				/* adc_post_probe */
84 	acpidev_container_probe,	/* adc_probe */
85 	acpidev_container_filter,	/* adc_filter */
86 	acpidev_container_init,		/* adc_init */
87 	NULL,				/* adc_fini */
88 };
89 
90 static char *acpidev_container_device_ids[] = {
91 	ACPIDEV_HID_MODULE,
92 	ACPIDEV_HID_CONTAINER1,
93 	ACPIDEV_HID_CONTAINER2,
94 };
95 
96 static char *acpidev_container_uid_formats[] = {
97 	"CPUSCK%x",
98 };
99 
100 /* Filter rule table for container objects. */
101 static acpidev_filter_rule_t acpidev_container_filters[] = {
102 	{	/* Ignore all container objects under ACPI root object */
103 		NULL,
104 		0,
105 		ACPIDEV_FILTER_SKIP,
106 		NULL,
107 		1,
108 		1,
109 		NULL,
110 		NULL,
111 	},
112 	{	/* Create node and scan child for all other container objects */
113 		acpidev_container_filter_func,
114 		0,
115 		ACPIDEV_FILTER_DEFAULT,
116 		&acpidev_class_list_device,
117 		2,
118 		INT_MAX,
119 		NULL,
120 		ACPIDEV_NODE_NAME_CONTAINER,
121 	}
122 };
123 
124 static ACPI_STATUS
125 acpidev_container_probe(acpidev_walk_info_t *infop)
126 {
127 	ACPI_STATUS rc;
128 	int flags;
129 
130 	ASSERT(infop != NULL);
131 	ASSERT(infop->awi_hdl != NULL);
132 	ASSERT(infop->awi_info != NULL);
133 
134 	if (infop->awi_info->Type != ACPI_TYPE_DEVICE ||
135 	    acpidev_match_device_id(infop->awi_info,
136 	    ACPIDEV_ARRAY_PARAM(acpidev_container_device_ids)) == 0) {
137 		return (AE_OK);
138 	}
139 
140 	if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) {
141 		flags = ACPIDEV_PROCESS_FLAG_SCAN | ACPIDEV_PROCESS_FLAG_CREATE;
142 		rc = acpidev_process_object(infop, flags);
143 	} else if (infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE) {
144 		flags = ACPIDEV_PROCESS_FLAG_SCAN;
145 		rc = acpidev_process_object(infop, flags);
146 	} else if (infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) {
147 		flags = ACPIDEV_PROCESS_FLAG_SCAN | ACPIDEV_PROCESS_FLAG_CREATE;
148 		rc = acpidev_process_object(infop, flags);
149 	} else {
150 		ACPIDEV_DEBUG(CE_WARN, "acpidev: unknown operation type %u in "
151 		    "acpidev_container_probe().", infop->awi_op_type);
152 		rc = AE_BAD_PARAMETER;
153 	}
154 	if (ACPI_FAILURE(rc) && rc != AE_NOT_EXIST && rc != AE_ALREADY_EXISTS) {
155 		cmn_err(CE_WARN,
156 		    "!acpidev: failed to process container object %s.",
157 		    infop->awi_name);
158 	} else {
159 		rc = AE_OK;
160 	}
161 
162 	return (rc);
163 }
164 
165 /*ARGSUSED*/
166 static ACPI_STATUS
167 acpidev_container_search_dev(ACPI_HANDLE hdl, UINT32 lvl, void *ctx,
168     void **retval)
169 {
170 	int *fp = (int *)ctx;
171 
172 	*fp = lvl;
173 
174 	return (AE_CTRL_TERMINATE);
175 }
176 
177 static acpidev_filter_result_t
178 acpidev_container_filter_func(acpidev_walk_info_t *infop, ACPI_HANDLE hdl,
179     acpidev_filter_rule_t *rulep, char *devname, int devnamelen)
180 {
181 	ACPI_BUFFER buf;
182 	void *retval;
183 	int proc_lvl, cpu_lvl, module_lvl;
184 	acpidev_filter_result_t res;
185 	static char *cpu_hids[] = {
186 		ACPIDEV_HID_CPU,
187 	};
188 	static char *module_hids[] = {
189 		ACPIDEV_HID_MODULE,
190 	};
191 
192 	res = acpidev_filter_default(infop, hdl, rulep, devname, devnamelen);
193 	/* Return if we don't need to generate a device name. */
194 	if (devname == NULL || res == ACPIDEV_FILTER_FAILED ||
195 	    res == ACPIDEV_FILTER_SKIP) {
196 		return (res);
197 	}
198 
199 	/* Try to figure out the most specific device name for the object. */
200 	retval = NULL;
201 	proc_lvl = INT_MAX;
202 	cpu_lvl = INT_MAX;
203 	module_lvl = INT_MAX;
204 
205 	/* Search for ACPI Processor object. */
206 	(void) AcpiWalkNamespace(ACPI_TYPE_PROCESSOR, hdl, 2,
207 	    acpidev_container_search_dev, NULL, &proc_lvl, &retval);
208 
209 	/* Search for CPU Device object. */
210 	(void) acpidev_get_device_by_id(hdl, ACPIDEV_ARRAY_PARAM(cpu_hids), 2,
211 	    B_FALSE, acpidev_container_search_dev, &cpu_lvl, &retval);
212 
213 	/* Search for Module Device object. */
214 	(void) acpidev_get_device_by_id(hdl, ACPIDEV_ARRAY_PARAM(module_hids),
215 	    2, B_FALSE, acpidev_container_search_dev, &module_lvl, &retval);
216 
217 	buf.Pointer = devname;
218 	buf.Length = devnamelen;
219 	if (cpu_lvl > proc_lvl) {
220 		cpu_lvl = proc_lvl;
221 	}
222 	if (cpu_lvl == 1) {
223 		/* CPU as child, most likely a physical CPU. */
224 		(void) strncpy(devname, ACPIDEV_NODE_NAME_MODULE_CPU,
225 		    devnamelen);
226 	} else if (cpu_lvl == 2 && module_lvl == 1) {
227 		/* CPU as grandchild, most likely a system board. */
228 		(void) strncpy(devname, ACPIDEV_NODE_NAME_MODULE_SBD,
229 		    devnamelen);
230 	} else if (ACPI_FAILURE(AcpiGetName(infop->awi_hdl,
231 	    ACPI_SINGLE_NAME, &buf))) {
232 		/*
233 		 * Failed to get ACPI object name; use ACPI object name
234 		 * as the default name.
235 		 */
236 		(void) strncpy(devname, ACPIDEV_NODE_NAME_CONTAINER,
237 		    devnamelen);
238 	}
239 
240 	return (res);
241 }
242 
243 static acpidev_filter_result_t
244 acpidev_container_filter(acpidev_walk_info_t *infop, char *devname, int maxlen)
245 {
246 	acpidev_filter_result_t res;
247 
248 	ASSERT(infop != NULL);
249 	if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE ||
250 	    infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE ||
251 	    infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) {
252 		res = acpidev_filter_device(infop, infop->awi_hdl,
253 		    ACPIDEV_ARRAY_PARAM(acpidev_container_filters),
254 		    devname, maxlen);
255 	} else {
256 		res = ACPIDEV_FILTER_FAILED;
257 	}
258 
259 	return (res);
260 }
261 
262 static ACPI_STATUS
263 acpidev_container_init(acpidev_walk_info_t *infop)
264 {
265 	static char *compatible[] = {
266 		ACPIDEV_TYPE_CONTAINER,
267 		ACPIDEV_HID_VIRTNEX,
268 		ACPIDEV_TYPE_VIRTNEX,
269 	};
270 
271 	ASSERT(infop != NULL);
272 	ASSERT(infop->awi_hdl != NULL);
273 	ASSERT(infop->awi_dip != NULL);
274 
275 	if (ACPI_FAILURE(acpidev_set_compatible(infop,
276 	    ACPIDEV_ARRAY_PARAM(compatible)))) {
277 		return (AE_ERROR);
278 	}
279 	if (ACPI_FAILURE(acpidev_set_unitaddr(infop,
280 	    ACPIDEV_ARRAY_PARAM(acpidev_container_uid_formats), NULL))) {
281 		return (AE_ERROR);
282 	}
283 
284 	return (AE_OK);
285 }
286