xref: /illumos-gate/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_device.c (revision 672fc84a1840ce8ef60fc752e9ea374723d1135a)
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  * Copyright (c) 2018, Joyent, Inc.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/atomic.h>
29 #include <sys/sunddi.h>
30 #include <sys/sunndi.h>
31 #include <sys/acpi/acpi.h>
32 #include <sys/acpica.h>
33 #include <sys/acpidev.h>
34 #include <sys/acpidev_impl.h>
35 #include <sys/pci.h>
36 
37 static ACPI_STATUS acpidev_device_probe(acpidev_walk_info_t *infop);
38 static acpidev_filter_result_t acpidev_device_filter(acpidev_walk_info_t *infop,
39     char *devname, int maxlen);
40 static acpidev_filter_result_t acpidev_device_filter_usb(acpidev_walk_info_t *,
41     ACPI_HANDLE, acpidev_filter_rule_t *, char *, int);
42 static ACPI_STATUS acpidev_device_init(acpidev_walk_info_t *infop);
43 
44 static uint32_t acpidev_device_unitaddr = 0;
45 
46 /*
47  * Default class driver for ACPI DEVICE objects.
48  * The default policy for DEVICE objects is to scan child objects without
49  * creating device nodes. But some special DEVICE objects will have device
50  * nodes created for them.
51  */
52 acpidev_class_t acpidev_class_device = {
53 	0,				/* adc_refcnt */
54 	ACPIDEV_CLASS_REV1,		/* adc_version */
55 	ACPIDEV_CLASS_ID_DEVICE,	/* adc_class_id */
56 	"ACPI Device",			/* adc_class_name */
57 	ACPIDEV_TYPE_DEVICE,		/* adc_dev_type */
58 	NULL,				/* adc_private */
59 	NULL,				/* adc_pre_probe */
60 	NULL,				/* adc_post_probe */
61 	acpidev_device_probe,		/* adc_probe */
62 	acpidev_device_filter,		/* adc_filter */
63 	acpidev_device_init,		/* adc_init */
64 	NULL,				/* adc_fini */
65 };
66 
67 /*
68  * List of class drivers which will be called in order when handling
69  * children of ACPI DEVICE objects.
70  */
71 acpidev_class_list_t *acpidev_class_list_device = NULL;
72 
73 /* Filter rule table for boot. */
74 static acpidev_filter_rule_t acpidev_device_filters[] = {
75 	{	/* _SB_ object type is hardcoded to DEVICE by acpica */
76 		NULL,
77 		0,
78 		ACPIDEV_FILTER_DEFAULT,
79 		&acpidev_class_list_device,
80 		1,
81 		1,
82 		ACPIDEV_OBJECT_NAME_SB,
83 		ACPIDEV_NODE_NAME_MODULE_SBD,
84 	},
85 	{	/* Ignore other device objects under ACPI root object */
86 		NULL,
87 		0,
88 		ACPIDEV_FILTER_SKIP,
89 		NULL,
90 		1,
91 		1,
92 		NULL,
93 		NULL,
94 	},
95 	{	/* Scan a device attempting to find a USB node */
96 		acpidev_device_filter_usb,
97 		0,
98 		ACPIDEV_FILTER_SCAN,
99 		&acpidev_class_list_usbport,
100 		2,
101 		INT_MAX,
102 		NULL,
103 		NULL
104 	},
105 	{	/* Scan other device objects not directly under ACPI root */
106 		NULL,
107 		0,
108 		ACPIDEV_FILTER_SCAN,
109 		&acpidev_class_list_device,
110 		2,
111 		INT_MAX,
112 		NULL,
113 		NULL,
114 	}
115 };
116 
117 static ACPI_STATUS
acpidev_device_probe(acpidev_walk_info_t * infop)118 acpidev_device_probe(acpidev_walk_info_t *infop)
119 {
120 	ACPI_STATUS rc = AE_OK;
121 	int flags;
122 
123 	ASSERT(infop != NULL);
124 	ASSERT(infop->awi_hdl != NULL);
125 	ASSERT(infop->awi_info != NULL);
126 
127 	if (infop->awi_info->Type != ACPI_TYPE_DEVICE) {
128 		return (AE_OK);
129 	}
130 
131 	flags = ACPIDEV_PROCESS_FLAG_SCAN;
132 	switch (infop->awi_op_type) {
133 	case ACPIDEV_OP_BOOT_PROBE:
134 		flags |= ACPIDEV_PROCESS_FLAG_CREATE;
135 		break;
136 
137 	case ACPIDEV_OP_BOOT_REPROBE:
138 		break;
139 
140 	case ACPIDEV_OP_HOTPLUG_PROBE:
141 		flags |= ACPIDEV_PROCESS_FLAG_CREATE |
142 		    ACPIDEV_PROCESS_FLAG_SYNCSTATUS |
143 		    ACPIDEV_PROCESS_FLAG_HOLDBRANCH;
144 		break;
145 
146 	default:
147 		ACPIDEV_DEBUG(CE_WARN,
148 		    "!acpidev: unknown operation type %u in "
149 		    "acpi_device_probe().", infop->awi_op_type);
150 		rc = AE_BAD_PARAMETER;
151 		break;
152 	}
153 
154 	if (rc == AE_OK) {
155 		rc = acpidev_process_object(infop, flags);
156 	}
157 	if (ACPI_FAILURE(rc) && rc != AE_NOT_EXIST && rc != AE_ALREADY_EXISTS) {
158 		cmn_err(CE_WARN,
159 		    "!acpidev: failed to process device object %s.",
160 		    infop->awi_name);
161 	} else {
162 		rc = AE_OK;
163 	}
164 
165 	return (rc);
166 }
167 
168 /*
169  * Attempt to determine which devices here correspond to an HCI for a USB
170  * controller.
171  */
172 static acpidev_filter_result_t
acpidev_device_filter_usb(acpidev_walk_info_t * infop,ACPI_HANDLE hdl,acpidev_filter_rule_t * afrp,char * devname,int len)173 acpidev_device_filter_usb(acpidev_walk_info_t *infop, ACPI_HANDLE hdl,
174     acpidev_filter_rule_t *afrp, char *devname, int len)
175 {
176 	dev_info_t *dip;
177 	char **compat;
178 	uint_t ncompat, i;
179 
180 	if (infop->awi_op_type != ACPIDEV_OP_BOOT_REPROBE)
181 		return (ACPIDEV_FILTER_SKIP);
182 
183 	/*
184 	 * If we don't find a dip that matches this one, then let's not worry
185 	 * about it. This means that it may not be a device we care about in any
186 	 * way.
187 	 */
188 	if (ACPI_FAILURE(acpica_get_devinfo(hdl, &dip))) {
189 		return (ACPIDEV_FILTER_SKIP);
190 	}
191 
192 	/*
193 	 * To determine if this is a PCI USB class controller, we grab its
194 	 * compatible array and look for an instance of pciclass,0c03 or
195 	 * pciexclass,0c03. The class code 0c03 is used to indicate a USB
196 	 * controller.
197 	 */
198 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
199 	    "compatible", &compat, &ncompat) != DDI_SUCCESS) {
200 		return (ACPIDEV_FILTER_SKIP);
201 	}
202 
203 	for (i = 0; i < ncompat; i++) {
204 		if (strcmp(compat[i], "pciclass,0c03") == 0 ||
205 		    strcmp(compat[i], "pciexclass,0c03") == 0) {
206 			ddi_prop_free(compat);
207 			/*
208 			 * We've found a PCI based USB controller. Switch to the
209 			 * USB specific parser.
210 			 */
211 			return (ACPIDEV_FILTER_SCAN);
212 		}
213 	}
214 
215 	ddi_prop_free(compat);
216 	return (ACPIDEV_FILTER_SKIP);
217 }
218 
219 static acpidev_filter_result_t
acpidev_device_filter(acpidev_walk_info_t * infop,char * devname,int maxlen)220 acpidev_device_filter(acpidev_walk_info_t *infop, char *devname, int maxlen)
221 {
222 	acpidev_filter_result_t res;
223 
224 	ASSERT(infop != NULL);
225 	if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE ||
226 	    infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE ||
227 	    infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) {
228 		res = acpidev_filter_device(infop, infop->awi_hdl,
229 		    ACPIDEV_ARRAY_PARAM(acpidev_device_filters),
230 		    devname, maxlen);
231 	} else {
232 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: unknown operation type %u "
233 		    "in acpidev_device_filter().", infop->awi_op_type);
234 		res = ACPIDEV_FILTER_FAILED;
235 	}
236 
237 	return (res);
238 }
239 
240 static ACPI_STATUS
acpidev_device_init(acpidev_walk_info_t * infop)241 acpidev_device_init(acpidev_walk_info_t *infop)
242 {
243 	char unitaddr[32];
244 	char *compatible[] = {
245 		ACPIDEV_TYPE_DEVICE,
246 		ACPIDEV_HID_VIRTNEX,
247 		ACPIDEV_TYPE_VIRTNEX,
248 	};
249 
250 	if (ACPI_FAILURE(acpidev_set_compatible(infop,
251 	    ACPIDEV_ARRAY_PARAM(compatible)))) {
252 		return (AE_ERROR);
253 	}
254 	(void) snprintf(unitaddr, sizeof (unitaddr), "%u",
255 	    atomic_inc_32_nv(&acpidev_device_unitaddr) - 1);
256 	if (ACPI_FAILURE(acpidev_set_unitaddr(infop, NULL, 0, unitaddr))) {
257 		return (AE_ERROR);
258 	}
259 
260 	return (AE_OK);
261 }
262