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