1 /*
   2  * Copyright (c) 2009, Intel Corporation.
   3  * All rights reserved.
   4  */
   5 
   6 #include <sys/types.h>
   7 #include <sys/atomic.h>
   8 #include <sys/sunddi.h>
   9 #include <sys/sunndi.h>
  10 #include <sys/acpi/acpi.h>
  11 #include <sys/acpica.h>
  12 #include <sys/acpidev.h>
  13 #include <sys/acpidev_impl.h>
  14 
  15 static ACPI_STATUS acpidev_scope_probe(acpidev_walk_info_t *infop);
  16 static acpidev_filter_result_t acpidev_scope_filter(acpidev_walk_info_t *infop,
  17     char *devname, int maxlen);
  18 static ACPI_STATUS acpidev_scope_init(acpidev_walk_info_t *infop);
  19 
  20 /*
  21  * Default class driver for ACPI scope object.
  22  * This class dirver is used to handle predefined ACPI SCOPE object
  23  * under ACPI root object, such as _PR_, _SB_ and _TZ_ etc.
  24  * Default policy for ACPI SCOPE object is SKIP.
  25  */
  26 acpidev_class_t acpidev_class_scope = {
  27         0,                              /* adc_refcnt */
  28         ACPIDEV_CLASS_REV1,             /* adc_version */
  29         ACPIDEV_CLASS_ID_SCOPE,         /* adc_class_id */
  30         "ACPI Scope",                   /* adc_class_name */
  31         ACPIDEV_TYPE_SCOPE,             /* adc_dev_type */
  32         NULL,                           /* adc_private */
  33         NULL,                           /* adc_pre_probe */
  34         NULL,                           /* adc_post_probe */
  35         acpidev_scope_probe,            /* adc_probe */
  36         acpidev_scope_filter,           /* adc_filter */
  37         acpidev_scope_init,             /* adc_init */
  38         NULL,                           /* adc_fini */
  39 };
  40 
  41 acpidev_class_list_t *acpidev_class_list_scope = NULL;
  42 
  43 /*
  44  * All SCOPE objects share a global pseudo unit address space across system.
  45  */
  46 static uint32_t acpidev_scope_unitaddr = 0;
  47 
  48 /* Filter rule table for ACPI SCOPE object. */
  49 static acpidev_filter_rule_t acpidev_scope_filters[] = {
  50         {       /* For safety, though _SB_ is hardcoded as DEVICE by acpica */
  51                 NULL,
  52                 0,
  53                 ACPIDEV_FILTER_SCAN,
  54                 &acpidev_class_list_device,
  55                 1,
  56                 1,
  57                 ACPIDEV_OBJECT_NAME_SB,
  58                 "",
  59         },
  60         {       /* Handle _PR_ object. */
  61                 NULL,
  62                 0,
  63                 ACPIDEV_FILTER_SCAN,
  64                 &acpidev_class_list_scope,
  65                 1,
  66                 1,
  67                 ACPIDEV_OBJECT_NAME_PR,
  68                 ACPIDEV_NODE_NAME_PROCESSOR,
  69         },
  70         {       /* Ignore all other scope objects. */
  71                 NULL,
  72                 0,
  73                 ACPIDEV_FILTER_SKIP,
  74                 NULL,
  75                 1,
  76                 INT_MAX,
  77                 NULL,
  78                 NULL,
  79         }
  80 };
  81 
  82 static ACPI_STATUS
  83 acpidev_scope_probe(acpidev_walk_info_t *infop)
  84 {
  85         ACPI_STATUS rc;
  86         int flags;
  87 
  88         ASSERT(infop != NULL);
  89         ASSERT(infop->awi_hdl != NULL);
  90         ASSERT(infop->awi_info != NULL);
  91         if (infop->awi_info->Type != ACPI_TYPE_LOCAL_SCOPE) {
  92                 return (AE_OK);
  93         }
  94 
  95         if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) {
  96                 flags = ACPIDEV_PROCESS_FLAG_SCAN | ACPIDEV_PROCESS_FLAG_CREATE;
  97                 rc = acpidev_process_object(infop, flags);
  98         } else if (infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE) {
  99                 flags = ACPIDEV_PROCESS_FLAG_SCAN;
 100                 rc = acpidev_process_object(infop, flags);
 101         } else if (infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) {
 102                 flags = ACPIDEV_PROCESS_FLAG_SCAN;
 103                 rc = acpidev_process_object(infop, flags);
 104         } else {
 105                 ACPIDEV_DEBUG(CE_WARN,
 106                     "acpidev: unknown operation type %u in scope_probe().",
 107                     infop->awi_op_type);
 108                 rc = AE_BAD_PARAMETER;
 109         }
 110         if (ACPI_FAILURE(rc) && rc != AE_NOT_EXIST && rc != AE_ALREADY_EXISTS) {
 111                 cmn_err(CE_CONT,
 112                     "?acpidev: failed to process scope object %s.\n",
 113                     infop->awi_name);
 114         } else {
 115                 rc = AE_OK;
 116         }
 117 
 118         return (rc);
 119 }
 120 
 121 static acpidev_filter_result_t
 122 acpidev_scope_filter(acpidev_walk_info_t *infop, char *devname, int maxlen)
 123 {
 124         acpidev_filter_result_t res;
 125 
 126         ASSERT(infop != NULL);
 127         if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE ||
 128             infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE ||
 129             infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) {
 130                 res = acpidev_filter_device(infop, infop->awi_hdl,
 131                     ACPIDEV_ARRAY_PARAM(acpidev_scope_filters),
 132                     devname, maxlen);
 133         } else {
 134                 ACPIDEV_DEBUG(CE_WARN,
 135                     "acpidev: unknown operation type %u in scope_filter().",
 136                     infop->awi_op_type);
 137                 res = ACPIDEV_FILTER_FAILED;
 138         }
 139 
 140         return (res);
 141 }
 142 
 143 /*ARGSUSED*/
 144 static ACPI_STATUS
 145 acpidev_scope_init(acpidev_walk_info_t *infop)
 146 {
 147         char unitaddr[32];
 148         char *compatibles[] = {
 149                 ACPIDEV_HID_SCOPE,
 150                 ACPIDEV_TYPE_SCOPE,
 151                 ACPIDEV_HID_VIRTNEX,
 152                 ACPIDEV_TYPE_VIRTNEX,
 153         };
 154 
 155         ASSERT(infop != NULL);
 156         ASSERT(infop->awi_hdl != NULL);
 157         ASSERT(infop->awi_dip != NULL);
 158         if (ACPI_FAILURE(acpidev_set_compatibles(infop,
 159             ACPIDEV_ARRAY_PARAM(compatibles)))) {
 160                 return (AE_ERROR);
 161         }
 162         (void) snprintf(unitaddr, sizeof (unitaddr), "%u",
 163             atomic_inc_32_nv(&acpidev_scope_unitaddr) - 1);
 164         if (ACPI_FAILURE(acpidev_set_unitaddr(infop, NULL, 0, unitaddr))) {
 165                 return (AE_ERROR);
 166         }
 167 
 168         return (AE_OK);
 169 }