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