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