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