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 <acpica/include/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 }