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 }