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