xref: /illumos-gate/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_container.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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 /*
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/note.h>
58 #include <sys/sunddi.h>
59 #include <sys/sunndi.h>
60 #include <sys/acpi/acpi.h>
61 #include <sys/acpica.h>
62 #include <sys/acpidev.h>
63 #include <sys/acpidev_dr.h>
64 #include <sys/acpidev_impl.h>
65 
66 static ACPI_STATUS acpidev_container_probe(acpidev_walk_info_t *infop);
67 static acpidev_filter_result_t acpidev_container_filter(
68     acpidev_walk_info_t *infop, char *devname, int maxlen);
69 static ACPI_STATUS acpidev_container_init(acpidev_walk_info_t *infop);
70 static acpidev_filter_result_t acpidev_container_filter_func(
71     acpidev_walk_info_t *infop, ACPI_HANDLE hdl, acpidev_filter_rule_t *rulep,
72     char *devname, int devnamelen);
73 
74 /*
75  * Default class driver for ACPI container objects.
76  */
77 acpidev_class_t acpidev_class_container = {
78 	0,				/* adc_refcnt */
79 	ACPIDEV_CLASS_REV1,		/* adc_version */
80 	ACPIDEV_CLASS_ID_CONTAINER,	/* adc_class_id */
81 	"ACPI Container",		/* adc_class_name */
82 	ACPIDEV_TYPE_CONTAINER,		/* adc_dev_type */
83 	NULL,				/* adc_private */
84 	NULL,				/* adc_pre_probe */
85 	NULL,				/* adc_post_probe */
86 	acpidev_container_probe,	/* adc_probe */
87 	acpidev_container_filter,	/* adc_filter */
88 	acpidev_container_init,		/* adc_init */
89 	NULL,				/* adc_fini */
90 };
91 
92 static char *acpidev_container_device_ids[] = {
93 	ACPIDEV_HID_MODULE,
94 	ACPIDEV_HID_CONTAINER1,
95 	ACPIDEV_HID_CONTAINER2,
96 };
97 
98 static char *acpidev_container_uid_formats[] = {
99 	"CPUSCK%x",
100 };
101 
102 /* Filter rule table for container objects. */
103 static acpidev_filter_rule_t acpidev_container_filters[] = {
104 	{	/* Ignore all container objects under ACPI root object */
105 		NULL,
106 		0,
107 		ACPIDEV_FILTER_SKIP,
108 		NULL,
109 		1,
110 		1,
111 		NULL,
112 		NULL,
113 	},
114 	{	/* Create node and scan child for all other container objects */
115 		acpidev_container_filter_func,
116 		0,
117 		ACPIDEV_FILTER_DEFAULT,
118 		&acpidev_class_list_device,
119 		2,
120 		INT_MAX,
121 		NULL,
122 		ACPIDEV_NODE_NAME_CONTAINER,
123 	}
124 };
125 
126 static ACPI_STATUS
127 acpidev_container_probe(acpidev_walk_info_t *infop)
128 {
129 	ACPI_STATUS rc = AE_OK;
130 	int flags;
131 
132 	ASSERT(infop != NULL);
133 	ASSERT(infop->awi_hdl != NULL);
134 	ASSERT(infop->awi_info != NULL);
135 
136 	if (infop->awi_info->Type != ACPI_TYPE_DEVICE ||
137 	    acpidev_match_device_id(infop->awi_info,
138 	    ACPIDEV_ARRAY_PARAM(acpidev_container_device_ids)) == 0) {
139 		return (AE_OK);
140 	}
141 
142 	flags = ACPIDEV_PROCESS_FLAG_SCAN;
143 	switch (infop->awi_op_type) {
144 	case ACPIDEV_OP_BOOT_PROBE:
145 		if (acpica_get_devcfg_feature(ACPI_DEVCFG_CONTAINER)) {
146 			flags |= ACPIDEV_PROCESS_FLAG_CREATE;
147 			acpidev_dr_check(infop);
148 		}
149 		break;
150 
151 	case ACPIDEV_OP_BOOT_REPROBE:
152 		break;
153 
154 	case ACPIDEV_OP_HOTPLUG_PROBE:
155 		if (acpica_get_devcfg_feature(ACPI_DEVCFG_CONTAINER)) {
156 			flags |= ACPIDEV_PROCESS_FLAG_CREATE |
157 			    ACPIDEV_PROCESS_FLAG_SYNCSTATUS |
158 			    ACPIDEV_PROCESS_FLAG_HOLDBRANCH;
159 		}
160 		break;
161 
162 	default:
163 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: unknown operation type %u in "
164 		    "acpidev_container_probe().", infop->awi_op_type);
165 		rc = AE_BAD_PARAMETER;
166 		break;
167 	}
168 
169 	if (rc == AE_OK) {
170 		rc = acpidev_process_object(infop, flags);
171 	}
172 	if (ACPI_FAILURE(rc) && rc != AE_NOT_EXIST && rc != AE_ALREADY_EXISTS) {
173 		cmn_err(CE_WARN,
174 		    "!acpidev: failed to process container object %s.",
175 		    infop->awi_name);
176 	} else {
177 		rc = AE_OK;
178 	}
179 
180 	return (rc);
181 }
182 
183 static ACPI_STATUS
184 acpidev_container_search_dev(ACPI_HANDLE hdl, UINT32 lvl, void *ctx,
185     void **retval)
186 {
187 	_NOTE(ARGUNUSED(hdl, retval));
188 
189 	int *fp = (int *)ctx;
190 
191 	*fp = lvl;
192 
193 	return (AE_CTRL_TERMINATE);
194 }
195 
196 static acpidev_filter_result_t
197 acpidev_container_filter_func(acpidev_walk_info_t *infop, ACPI_HANDLE hdl,
198     acpidev_filter_rule_t *rulep, char *devname, int devnamelen)
199 {
200 	ACPI_BUFFER buf;
201 	void *retval;
202 	int proc_lvl, cpu_lvl, module_lvl;
203 	acpidev_filter_result_t res;
204 	static char *cpu_hids[] = {
205 		ACPIDEV_HID_CPU,
206 	};
207 	static char *module_hids[] = {
208 		ACPIDEV_HID_MODULE,
209 	};
210 
211 	res = acpidev_filter_default(infop, hdl, rulep, devname, devnamelen);
212 	/* Return if we don't need to generate a device name. */
213 	if (devname == NULL || res == ACPIDEV_FILTER_FAILED ||
214 	    res == ACPIDEV_FILTER_SKIP) {
215 		return (res);
216 	}
217 
218 	/* Try to figure out the most specific device name for the object. */
219 	retval = NULL;
220 	proc_lvl = INT_MAX;
221 	cpu_lvl = INT_MAX;
222 	module_lvl = INT_MAX;
223 
224 	/* Search for ACPI Processor object. */
225 	(void) AcpiWalkNamespace(ACPI_TYPE_PROCESSOR, hdl, 2,
226 	    acpidev_container_search_dev, NULL, &proc_lvl, &retval);
227 
228 	/* Search for CPU Device object. */
229 	(void) acpidev_get_device_by_id(hdl, ACPIDEV_ARRAY_PARAM(cpu_hids), 2,
230 	    B_FALSE, acpidev_container_search_dev, &cpu_lvl, &retval);
231 
232 	/* Search for Module Device object. */
233 	(void) acpidev_get_device_by_id(hdl, ACPIDEV_ARRAY_PARAM(module_hids),
234 	    2, B_FALSE, acpidev_container_search_dev, &module_lvl, &retval);
235 
236 	buf.Pointer = devname;
237 	buf.Length = devnamelen;
238 	if (cpu_lvl > proc_lvl) {
239 		cpu_lvl = proc_lvl;
240 	}
241 	if (cpu_lvl == 1) {
242 		/* CPU as child, most likely a physical CPU. */
243 		(void) strlcpy(devname, ACPIDEV_NODE_NAME_MODULE_CPU,
244 		    devnamelen);
245 	} else if (cpu_lvl == 2 && module_lvl == 1) {
246 		/* CPU as grandchild, most likely a system board. */
247 		(void) strlcpy(devname, ACPIDEV_NODE_NAME_MODULE_SBD,
248 		    devnamelen);
249 	} else if (ACPI_FAILURE(AcpiGetName(infop->awi_hdl,
250 	    ACPI_SINGLE_NAME, &buf))) {
251 		/*
252 		 * Failed to get ACPI object name; use ACPI object name
253 		 * as the default name.
254 		 */
255 		(void) strlcpy(devname, ACPIDEV_NODE_NAME_CONTAINER,
256 		    devnamelen);
257 	}
258 
259 	return (res);
260 }
261 
262 static acpidev_filter_result_t
263 acpidev_container_filter(acpidev_walk_info_t *infop, char *devname, int maxlen)
264 {
265 	acpidev_filter_result_t res;
266 
267 	ASSERT(infop != NULL);
268 	if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE ||
269 	    infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE ||
270 	    infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) {
271 		res = acpidev_filter_device(infop, infop->awi_hdl,
272 		    ACPIDEV_ARRAY_PARAM(acpidev_container_filters),
273 		    devname, maxlen);
274 	} else {
275 		res = ACPIDEV_FILTER_FAILED;
276 	}
277 
278 	return (res);
279 }
280 
281 static ACPI_STATUS
282 acpidev_container_init(acpidev_walk_info_t *infop)
283 {
284 	static char *compatible[] = {
285 		ACPIDEV_TYPE_CONTAINER,
286 		ACPIDEV_HID_VIRTNEX,
287 		ACPIDEV_TYPE_VIRTNEX,
288 	};
289 
290 	ASSERT(infop != NULL);
291 	ASSERT(infop->awi_hdl != NULL);
292 	ASSERT(infop->awi_dip != NULL);
293 
294 	if (ACPI_FAILURE(acpidev_set_compatible(infop,
295 	    ACPIDEV_ARRAY_PARAM(compatible)))) {
296 		return (AE_ERROR);
297 	}
298 	if (ACPI_FAILURE(acpidev_set_unitaddr(infop,
299 	    ACPIDEV_ARRAY_PARAM(acpidev_container_uid_formats), NULL))) {
300 		return (AE_ERROR);
301 	}
302 
303 	return (AE_OK);
304 }
305