xref: /illumos-gate/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_device.c (revision d87d03b4c0f66bf125e607ef8b0d9c5481040d20)
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_device_probe(acpidev_walk_info_t *infop);
36 static acpidev_filter_result_t acpidev_device_filter(acpidev_walk_info_t *infop,
37     char *devname, int maxlen);
38 static ACPI_STATUS acpidev_device_init(acpidev_walk_info_t *infop);
39 
40 static uint32_t acpidev_device_unitaddr = 0;
41 
42 /*
43  * Default class driver for ACPI DEVICE objects.
44  * The default policy for DEVICE objects is to scan child objects without
45  * creating device nodes. But some special DEVICE objects will have device
46  * nodes created for them.
47  */
48 acpidev_class_t acpidev_class_device = {
49 	0,				/* adc_refcnt */
50 	ACPIDEV_CLASS_REV1,		/* adc_version */
51 	ACPIDEV_CLASS_ID_DEVICE,	/* adc_class_id */
52 	"ACPI Device",			/* adc_class_name */
53 	ACPIDEV_TYPE_DEVICE,		/* adc_dev_type */
54 	NULL,				/* adc_private */
55 	NULL,				/* adc_pre_probe */
56 	NULL,				/* adc_post_probe */
57 	acpidev_device_probe,		/* adc_probe */
58 	acpidev_device_filter,		/* adc_filter */
59 	acpidev_device_init,		/* adc_init */
60 	NULL,				/* adc_fini */
61 };
62 
63 /*
64  * List of class drivers which will be called in order when handling
65  * children of ACPI DEVICE objects.
66  */
67 acpidev_class_list_t *acpidev_class_list_device = NULL;
68 
69 /* Filter rule table for boot. */
70 static acpidev_filter_rule_t acpidev_device_filters[] = {
71 	{	/* _SB_ object type is hardcoded to DEVICE by acpica */
72 		NULL,
73 		0,
74 		ACPIDEV_FILTER_DEFAULT,
75 		&acpidev_class_list_device,
76 		1,
77 		1,
78 		ACPIDEV_OBJECT_NAME_SB,
79 		ACPIDEV_NODE_NAME_MODULE_SBD,
80 	},
81 	{	/* Ignore other device objects under ACPI root object */
82 		NULL,
83 		0,
84 		ACPIDEV_FILTER_SKIP,
85 		NULL,
86 		1,
87 		1,
88 		NULL,
89 		NULL,
90 	},
91 	{	/* Scan other device objects not directly under ACPI root */
92 		NULL,
93 		0,
94 		ACPIDEV_FILTER_SKIP,
95 		&acpidev_class_list_device,
96 		2,
97 		INT_MAX,
98 		NULL,
99 		NULL,
100 	}
101 };
102 
103 static ACPI_STATUS
104 acpidev_device_probe(acpidev_walk_info_t *infop)
105 {
106 	ACPI_STATUS rc = AE_OK;
107 	int flags;
108 
109 	ASSERT(infop != NULL);
110 	ASSERT(infop->awi_hdl != NULL);
111 	ASSERT(infop->awi_info != NULL);
112 
113 	if (infop->awi_info->Type != ACPI_TYPE_DEVICE) {
114 		return (AE_OK);
115 	}
116 
117 	flags = ACPIDEV_PROCESS_FLAG_SCAN;
118 	switch (infop->awi_op_type) {
119 	case ACPIDEV_OP_BOOT_PROBE:
120 		flags |= ACPIDEV_PROCESS_FLAG_CREATE;
121 		break;
122 
123 	case ACPIDEV_OP_BOOT_REPROBE:
124 		break;
125 
126 	case ACPIDEV_OP_HOTPLUG_PROBE:
127 		flags |= ACPIDEV_PROCESS_FLAG_CREATE |
128 		    ACPIDEV_PROCESS_FLAG_SYNCSTATUS |
129 		    ACPIDEV_PROCESS_FLAG_HOLDBRANCH;
130 		break;
131 
132 	default:
133 		ACPIDEV_DEBUG(CE_WARN,
134 		    "!acpidev: unknown operation type %u in "
135 		    "acpi_device_probe().", infop->awi_op_type);
136 		rc = AE_BAD_PARAMETER;
137 		break;
138 	}
139 
140 	if (rc == AE_OK) {
141 		rc = acpidev_process_object(infop, flags);
142 	}
143 	if (ACPI_FAILURE(rc) && rc != AE_NOT_EXIST && rc != AE_ALREADY_EXISTS) {
144 		cmn_err(CE_WARN,
145 		    "!acpidev: failed to process device object %s.",
146 		    infop->awi_name);
147 	} else {
148 		rc = AE_OK;
149 	}
150 
151 	return (rc);
152 }
153 
154 static acpidev_filter_result_t
155 acpidev_device_filter(acpidev_walk_info_t *infop, char *devname, int maxlen)
156 {
157 	acpidev_filter_result_t res;
158 
159 	ASSERT(infop != NULL);
160 	if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE ||
161 	    infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE ||
162 	    infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) {
163 		res = acpidev_filter_device(infop, infop->awi_hdl,
164 		    ACPIDEV_ARRAY_PARAM(acpidev_device_filters),
165 		    devname, maxlen);
166 	} else {
167 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: unknown operation type %u "
168 		    "in acpidev_device_filter().", infop->awi_op_type);
169 		res = ACPIDEV_FILTER_FAILED;
170 	}
171 
172 	return (res);
173 }
174 
175 static ACPI_STATUS
176 acpidev_device_init(acpidev_walk_info_t *infop)
177 {
178 	char unitaddr[32];
179 	char *compatible[] = {
180 		ACPIDEV_TYPE_DEVICE,
181 		ACPIDEV_HID_VIRTNEX,
182 		ACPIDEV_TYPE_VIRTNEX,
183 	};
184 
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_device_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