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