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 }