diff options
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r-- | drivers/pci/pci.c | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..1ccf7d49f522 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -2360,6 +2360,75 @@ void pci_enable_acs(struct pci_dev *dev) | |||
2360 | } | 2360 | } |
2361 | 2361 | ||
2362 | /** | 2362 | /** |
2363 | * pci_acs_enabled - test ACS against required flags for a given device | ||
2364 | * @pdev: device to test | ||
2365 | * @acs_flags: required PCI ACS flags | ||
2366 | * | ||
2367 | * Return true if the device supports the provided flags. Automatically | ||
2368 | * filters out flags that are not implemented on multifunction devices. | ||
2369 | */ | ||
2370 | bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags) | ||
2371 | { | ||
2372 | int pos, ret; | ||
2373 | u16 ctrl; | ||
2374 | |||
2375 | ret = pci_dev_specific_acs_enabled(pdev, acs_flags); | ||
2376 | if (ret >= 0) | ||
2377 | return ret > 0; | ||
2378 | |||
2379 | if (!pci_is_pcie(pdev)) | ||
2380 | return false; | ||
2381 | |||
2382 | /* Filter out flags not applicable to multifunction */ | ||
2383 | if (pdev->multifunction) | ||
2384 | acs_flags &= (PCI_ACS_RR | PCI_ACS_CR | | ||
2385 | PCI_ACS_EC | PCI_ACS_DT); | ||
2386 | |||
2387 | if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM || | ||
2388 | pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT || | ||
2389 | pdev->multifunction) { | ||
2390 | pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS); | ||
2391 | if (!pos) | ||
2392 | return false; | ||
2393 | |||
2394 | pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl); | ||
2395 | if ((ctrl & acs_flags) != acs_flags) | ||
2396 | return false; | ||
2397 | } | ||
2398 | |||
2399 | return true; | ||
2400 | } | ||
2401 | |||
2402 | /** | ||
2403 | * pci_acs_path_enable - test ACS flags from start to end in a hierarchy | ||
2404 | * @start: starting downstream device | ||
2405 | * @end: ending upstream device or NULL to search to the root bus | ||
2406 | * @acs_flags: required flags | ||
2407 | * | ||
2408 | * Walk up a device tree from start to end testing PCI ACS support. If | ||
2409 | * any step along the way does not support the required flags, return false. | ||
2410 | */ | ||
2411 | bool pci_acs_path_enabled(struct pci_dev *start, | ||
2412 | struct pci_dev *end, u16 acs_flags) | ||
2413 | { | ||
2414 | struct pci_dev *pdev, *parent = start; | ||
2415 | |||
2416 | do { | ||
2417 | pdev = parent; | ||
2418 | |||
2419 | if (!pci_acs_enabled(pdev, acs_flags)) | ||
2420 | return false; | ||
2421 | |||
2422 | if (pci_is_root_bus(pdev->bus)) | ||
2423 | return (end == NULL); | ||
2424 | |||
2425 | parent = pdev->bus->self; | ||
2426 | } while (pdev != end); | ||
2427 | |||
2428 | return true; | ||
2429 | } | ||
2430 | |||
2431 | /** | ||
2363 | * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge | 2432 | * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge |
2364 | * @dev: the PCI device | 2433 | * @dev: the PCI device |
2365 | * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTD, 4=INTD) | 2434 | * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTD, 4=INTD) |