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
acpidev_scope_probe(acpidev_walk_info_t * infop)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
acpidev_scope_filter(acpidev_walk_info_t * infop,char * devname,int maxlen)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
acpidev_scope_init(acpidev_walk_info_t * infop)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 }
197