1 | /* $NetBSD: acpi_pci.c,v 1.19 2015/04/13 18:32:50 christos Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Christoph Egger and Gregoire Sutre. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. The name of the author may not be used to endorse or promote products |
16 | * derived from this software without specific prior written permission. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
23 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
25 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
26 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
28 | * SUCH DAMAGE. |
29 | */ |
30 | |
31 | #include <sys/cdefs.h> |
32 | __KERNEL_RCSID(0, "$NetBSD: acpi_pci.c,v 1.19 2015/04/13 18:32:50 christos Exp $" ); |
33 | |
34 | #include <sys/param.h> |
35 | #include <sys/device.h> |
36 | #include <sys/kmem.h> |
37 | #include <sys/systm.h> |
38 | |
39 | #include <dev/pci/pcireg.h> |
40 | #include <dev/pci/pcivar.h> |
41 | #include <dev/pci/pcidevs.h> |
42 | #include <dev/pci/ppbreg.h> |
43 | |
44 | #include <dev/acpi/acpireg.h> |
45 | #include <dev/acpi/acpivar.h> |
46 | #include <dev/acpi/acpi_pci.h> |
47 | |
48 | #include "locators.h" |
49 | |
50 | #define _COMPONENT ACPI_BUS_COMPONENT |
51 | ACPI_MODULE_NAME ("acpi_pci" ) |
52 | |
53 | #define ACPI_HILODWORD(x) ACPI_HIWORD(ACPI_LODWORD((x))) |
54 | #define ACPI_LOLODWORD(x) ACPI_LOWORD(ACPI_LODWORD((x))) |
55 | |
56 | static ACPI_STATUS acpi_pcidev_pciroot_bus(ACPI_HANDLE, uint16_t *); |
57 | static ACPI_STATUS acpi_pcidev_pciroot_bus_callback(ACPI_RESOURCE *, |
58 | void *); |
59 | |
60 | /* |
61 | * Regarding PCI Segment Groups (ACPI 4.0, p. 277): |
62 | * |
63 | * "The optional _SEG object is located under a PCI host bridge and |
64 | * evaluates to an integer that describes the PCI Segment Group (see PCI |
65 | * Firmware Specification v3.0)." |
66 | * |
67 | * "PCI Segment Group is purely a software concept managed by system |
68 | * firmware and used by OSPM. It is a logical collection of PCI buses |
69 | * (or bus segments). It is a way to logically group the PCI bus segments |
70 | * and PCI Express Hierarchies. _SEG is a level higher than _BBN." |
71 | * |
72 | * "PCI Segment Group supports more than 256 buses in a system by allowing |
73 | * the reuse of the PCI bus numbers. Within each PCI Segment Group, the bus |
74 | * numbers for the PCI buses must be unique. PCI buses in different PCI |
75 | * Segment Group are permitted to have the same bus number." |
76 | */ |
77 | |
78 | /* |
79 | * Regarding PCI Base Bus Numbers (ACPI 4.0, p. 277): |
80 | * |
81 | * "For multi-root PCI platforms, the _BBN object evaluates to the PCI bus |
82 | * number that the BIOS assigns. This is needed to access a PCI_Config |
83 | * operation region for the specified bus. The _BBN object is located under |
84 | * a PCI host bridge and must be unique for every host bridge within a |
85 | * segment since it is the PCI bus number." |
86 | * |
87 | * Moreover, the ACPI FAQ (http://www.acpi.info/acpi_faq.htm) says: |
88 | * |
89 | * "For a multiple root bus machine, _BBN is required for each bus. _BBN |
90 | * should provide the bus number assigned to this bus by the BIOS at boot |
91 | * time." |
92 | */ |
93 | |
94 | /* |
95 | * acpi_pcidev_pciroot_bus: |
96 | * |
97 | * Derive the PCI bus number of a PCI root bridge from its resources. |
98 | * If successful, return AE_OK and fill *busp. Otherwise, return an |
99 | * exception code and leave *busp unchanged. |
100 | */ |
101 | static ACPI_STATUS |
102 | acpi_pcidev_pciroot_bus(ACPI_HANDLE handle, uint16_t *busp) |
103 | { |
104 | ACPI_STATUS rv; |
105 | int32_t bus; |
106 | |
107 | bus = -1; |
108 | |
109 | /* |
110 | * XXX: Use the ACPI resource parsing functions (acpi_resource.c) |
111 | * once bus number ranges have been implemented there. |
112 | */ |
113 | rv = AcpiWalkResources(handle, "_CRS" , |
114 | acpi_pcidev_pciroot_bus_callback, &bus); |
115 | |
116 | if (ACPI_FAILURE(rv)) |
117 | return rv; |
118 | |
119 | if (bus == -1) |
120 | return AE_NOT_EXIST; |
121 | |
122 | /* Here it holds that 0 <= bus <= 0xFFFF. */ |
123 | *busp = (uint16_t)bus; |
124 | |
125 | return rv; |
126 | } |
127 | |
128 | static ACPI_STATUS |
129 | acpi_pcidev_pciroot_bus_callback(ACPI_RESOURCE *res, void *context) |
130 | { |
131 | ACPI_RESOURCE_ADDRESS64 addr64; |
132 | int32_t *bus = context; |
133 | |
134 | /* Always continue the walk by returning AE_OK. */ |
135 | if ((res->Type != ACPI_RESOURCE_TYPE_ADDRESS16) && |
136 | (res->Type != ACPI_RESOURCE_TYPE_ADDRESS32) && |
137 | (res->Type != ACPI_RESOURCE_TYPE_ADDRESS64)) |
138 | return AE_OK; |
139 | |
140 | if (ACPI_FAILURE(AcpiResourceToAddress64(res, &addr64))) |
141 | return AE_OK; |
142 | |
143 | if (addr64.ResourceType != ACPI_BUS_NUMBER_RANGE) |
144 | return AE_OK; |
145 | |
146 | if (*bus != -1) |
147 | return AE_ALREADY_EXISTS; |
148 | |
149 | if (addr64.Address.Minimum > 0xFFFF) |
150 | return AE_BAD_DATA; |
151 | |
152 | *bus = (int32_t)addr64.Address.Minimum; |
153 | |
154 | return AE_OK; |
155 | } |
156 | |
157 | /* |
158 | * acpi_pcidev_scan: |
159 | * |
160 | * Scan the ACPI device tree for PCI devices. A node is detected as a |
161 | * PCI device if it has an ancestor that is a PCI root bridge and such |
162 | * that all intermediate nodes are PCI-to-PCI bridges. Depth-first |
163 | * recursive implementation. |
164 | * |
165 | * PCI root bridges do not necessarily contain an _ADR, since they already |
166 | * contain an _HID (ACPI 4.0a, p. 197). However we require an _ADR for |
167 | * all non-root PCI devices. |
168 | */ |
169 | ACPI_STATUS |
170 | acpi_pcidev_scan(struct acpi_devnode *ad) |
171 | { |
172 | struct acpi_devnode *child; |
173 | struct acpi_pci_info *ap; |
174 | ACPI_INTEGER val; |
175 | ACPI_STATUS rv; |
176 | |
177 | ad->ad_pciinfo = NULL; |
178 | |
179 | /* |
180 | * We attach PCI information only to devices that are present, |
181 | * enabled, and functioning properly. |
182 | * Note: there is a possible race condition, because _STA may |
183 | * have changed since ad->ad_devinfo->CurrentStatus was set. |
184 | */ |
185 | if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE) |
186 | goto rec; |
187 | if ((ad->ad_devinfo->Valid & ACPI_VALID_STA) != 0 && |
188 | (ad->ad_devinfo->CurrentStatus & ACPI_STA_OK) != ACPI_STA_OK) |
189 | goto rec; |
190 | |
191 | if (ad->ad_devinfo->Flags & ACPI_PCI_ROOT_BRIDGE) { |
192 | |
193 | ap = kmem_zalloc(sizeof(*ap), KM_SLEEP); |
194 | |
195 | if (ap == NULL) |
196 | return AE_NO_MEMORY; |
197 | |
198 | /* |
199 | * If no _SEG exist, all PCI bus segments are assumed |
200 | * to be in the PCI segment group 0 (ACPI 4.0, p. 277). |
201 | * The segment group number is conveyed in the lower |
202 | * 16 bits of _SEG (the other bits are all reserved). |
203 | */ |
204 | rv = acpi_eval_integer(ad->ad_handle, "_SEG" , &val); |
205 | |
206 | if (ACPI_SUCCESS(rv)) |
207 | ap->ap_segment = ACPI_LOWORD(val); |
208 | |
209 | /* Try to get downstream bus number using _CRS first. */ |
210 | rv = acpi_pcidev_pciroot_bus(ad->ad_handle, &ap->ap_downbus); |
211 | |
212 | if (ACPI_FAILURE(rv)) { |
213 | rv = acpi_eval_integer(ad->ad_handle, "_BBN" , &val); |
214 | |
215 | if (ACPI_SUCCESS(rv)) |
216 | ap->ap_downbus = ACPI_LOWORD(val); |
217 | } |
218 | |
219 | if (ap->ap_downbus > 255) { |
220 | aprint_error_dev(ad->ad_root, |
221 | "invalid PCI downstream bus for %s\n" , ad->ad_name); |
222 | kmem_free(ap, sizeof(*ap)); |
223 | goto rec; |
224 | } |
225 | |
226 | ap->ap_flags |= ACPI_PCI_INFO_BRIDGE; |
227 | |
228 | /* |
229 | * This ACPI node denotes a PCI root bridge, but it may also |
230 | * denote a PCI device on the bridge's downstream bus segment. |
231 | */ |
232 | if (ad->ad_devinfo->Valid & ACPI_VALID_ADR) { |
233 | ap->ap_bus = ap->ap_downbus; |
234 | ap->ap_device = |
235 | ACPI_HILODWORD(ad->ad_devinfo->Address); |
236 | ap->ap_function = |
237 | ACPI_LOLODWORD(ad->ad_devinfo->Address); |
238 | |
239 | if (ap->ap_device > 31 || |
240 | (ap->ap_function > 7 && ap->ap_function != 0xFFFF)) |
241 | aprint_error_dev(ad->ad_root, |
242 | "invalid PCI address for %s\n" , ad->ad_name); |
243 | else |
244 | ap->ap_flags |= ACPI_PCI_INFO_DEVICE; |
245 | } |
246 | |
247 | ad->ad_pciinfo = ap; |
248 | |
249 | goto rec; |
250 | } |
251 | |
252 | if ((ad->ad_parent != NULL) && |
253 | (ad->ad_parent->ad_pciinfo != NULL) && |
254 | (ad->ad_parent->ad_pciinfo->ap_flags & ACPI_PCI_INFO_BRIDGE) && |
255 | (ad->ad_devinfo->Valid & ACPI_VALID_ADR)) { |
256 | |
257 | /* |
258 | * Our parent is a PCI root bridge or a PCI-to-PCI |
259 | * bridge. We have the same PCI segment number, and |
260 | * our bus number is its downstream bus number. |
261 | */ |
262 | ap = kmem_zalloc(sizeof(*ap), KM_SLEEP); |
263 | |
264 | if (ap == NULL) |
265 | return AE_NO_MEMORY; |
266 | |
267 | ap->ap_segment = ad->ad_parent->ad_pciinfo->ap_segment; |
268 | ap->ap_bus = ad->ad_parent->ad_pciinfo->ap_downbus; |
269 | |
270 | ap->ap_device = ACPI_HILODWORD(ad->ad_devinfo->Address); |
271 | ap->ap_function = ACPI_LOLODWORD(ad->ad_devinfo->Address); |
272 | |
273 | if (ap->ap_device > 31 || |
274 | (ap->ap_function > 7 && ap->ap_function != 0xFFFF)) { |
275 | aprint_error_dev(ad->ad_root, |
276 | "invalid PCI address for %s\n" , ad->ad_name); |
277 | kmem_free(ap, sizeof(*ap)); |
278 | goto rec; |
279 | } |
280 | |
281 | ap->ap_flags |= ACPI_PCI_INFO_DEVICE; |
282 | |
283 | if (ap->ap_function == 0xFFFF) { |
284 | /* |
285 | * Assume that this device is not a PCI-to-PCI bridge. |
286 | * XXX: Do we need to be smarter? |
287 | */ |
288 | } else { |
289 | /* |
290 | * Check whether this device is a PCI-to-PCI |
291 | * bridge and get its secondary bus number. |
292 | */ |
293 | rv = acpi_pcidev_ppb_downbus(ap->ap_segment, ap->ap_bus, |
294 | ap->ap_device, ap->ap_function, &ap->ap_downbus); |
295 | |
296 | if (ACPI_SUCCESS(rv)) |
297 | ap->ap_flags |= ACPI_PCI_INFO_BRIDGE; |
298 | } |
299 | |
300 | ad->ad_pciinfo = ap; |
301 | |
302 | goto rec; |
303 | } |
304 | |
305 | rec: |
306 | SIMPLEQ_FOREACH(child, &ad->ad_child_head, ad_child_list) { |
307 | rv = acpi_pcidev_scan(child); |
308 | |
309 | if (ACPI_FAILURE(rv)) |
310 | return rv; |
311 | } |
312 | |
313 | return AE_OK; |
314 | } |
315 | |
316 | /* |
317 | * acpi_pcidev_ppb_downbus: |
318 | * |
319 | * Retrieve the secondary bus number of the PCI-to-PCI bridge having the |
320 | * given PCI id. If successful, return AE_OK and fill *downbus. |
321 | * Otherwise, return an exception code and leave *downbus unchanged. |
322 | * |
323 | * XXX Need to deal with PCI segment groups (see also acpica/OsdHardware.c). |
324 | */ |
325 | ACPI_STATUS |
326 | acpi_pcidev_ppb_downbus(uint16_t segment, uint16_t bus, uint16_t device, |
327 | uint16_t function, uint16_t *downbus) |
328 | { |
329 | struct acpi_softc *sc = acpi_softc; |
330 | pci_chipset_tag_t pc; |
331 | pcitag_t tag; |
332 | pcireg_t val; |
333 | |
334 | if (bus > 255 || device > 31 || function > 7) |
335 | return AE_BAD_PARAMETER; |
336 | |
337 | pc = sc->sc_pc; |
338 | |
339 | tag = pci_make_tag(pc, bus, device, function); |
340 | |
341 | /* Check that this device exists. */ |
342 | val = pci_conf_read(pc, tag, PCI_ID_REG); |
343 | |
344 | if (PCI_VENDOR(val) == PCI_VENDOR_INVALID || |
345 | PCI_VENDOR(val) == 0) |
346 | return AE_NOT_EXIST; |
347 | |
348 | /* Check that this device is a PCI-to-PCI bridge. */ |
349 | val = pci_conf_read(pc, tag, PCI_BHLC_REG); |
350 | |
351 | if (PCI_HDRTYPE_TYPE(val) != PCI_HDRTYPE_PPB) |
352 | return AE_TYPE; |
353 | |
354 | /* This is a PCI-to-PCI bridge. Get its secondary bus#. */ |
355 | val = pci_conf_read(pc, tag, PPB_REG_BUSINFO); |
356 | *downbus = PPB_BUSINFO_SECONDARY(val); |
357 | |
358 | return AE_OK; |
359 | } |
360 | |
361 | /* |
362 | * acpi_pcidev_find: |
363 | * |
364 | * Finds a PCI device in the ACPI name space. |
365 | * |
366 | * Returns an ACPI device node on success and NULL on failure. |
367 | */ |
368 | struct acpi_devnode * |
369 | acpi_pcidev_find(uint16_t segment, uint16_t bus, |
370 | uint16_t device, uint16_t function) |
371 | { |
372 | struct acpi_softc *sc = acpi_softc; |
373 | struct acpi_devnode *ad; |
374 | |
375 | if (sc == NULL) |
376 | return NULL; |
377 | |
378 | SIMPLEQ_FOREACH(ad, &sc->ad_head, ad_list) { |
379 | |
380 | if (ad->ad_pciinfo != NULL && |
381 | (ad->ad_pciinfo->ap_flags & ACPI_PCI_INFO_DEVICE) && |
382 | ad->ad_pciinfo->ap_segment == segment && |
383 | ad->ad_pciinfo->ap_bus == bus && |
384 | ad->ad_pciinfo->ap_device == device && |
385 | ad->ad_pciinfo->ap_function == function) |
386 | return ad; |
387 | } |
388 | |
389 | return NULL; |
390 | } |
391 | |
392 | |
393 | /* |
394 | * acpi_pcidev_find_dev: |
395 | * |
396 | * Returns the device corresponding to the given PCI info, or NULL |
397 | * if it doesn't exist. |
398 | */ |
399 | device_t |
400 | acpi_pcidev_find_dev(struct acpi_devnode *ad) |
401 | { |
402 | struct acpi_pci_info *ap; |
403 | struct pci_softc *pci; |
404 | device_t dv, pr; |
405 | deviter_t di; |
406 | |
407 | if (ad == NULL) |
408 | return NULL; |
409 | |
410 | if (ad->ad_pciinfo == NULL) |
411 | return NULL; |
412 | |
413 | ap = ad->ad_pciinfo; |
414 | |
415 | if (ap->ap_function == 0xFFFF) |
416 | return NULL; |
417 | |
418 | for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST); |
419 | dv != NULL; dv = deviter_next(&di)) { |
420 | |
421 | pr = device_parent(dv); |
422 | |
423 | if (pr == NULL || device_is_a(pr, "pci" ) != true) |
424 | continue; |
425 | |
426 | if (dv->dv_locators == NULL) /* This should not happen. */ |
427 | continue; |
428 | |
429 | pci = device_private(pr); |
430 | |
431 | if (pci->sc_bus == ap->ap_bus && |
432 | device_locator(dv, PCICF_DEV) == ap->ap_device && |
433 | device_locator(dv, PCICF_FUNCTION) == ap->ap_function) |
434 | break; |
435 | } |
436 | |
437 | deviter_release(&di); |
438 | |
439 | return dv; |
440 | } |
441 | |