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
131b72d5b75SMichael Corcoran 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