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
acpidev_container_probe(acpidev_walk_info_t * infop)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
acpidev_container_search_dev(ACPI_HANDLE hdl,UINT32 lvl,void * ctx,void ** retval)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
acpidev_container_filter_func(acpidev_walk_info_t * infop,ACPI_HANDLE hdl,acpidev_filter_rule_t * rulep,char * devname,int devnamelen)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
acpidev_container_filter(acpidev_walk_info_t * infop,char * devname,int maxlen)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
acpidev_container_init(acpidev_walk_info_t * infop)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