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 a23b071798f6..b743a9afb4dd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -2293,6 +2293,75 @@ void pci_enable_acs(struct pci_dev *dev) | |||
2293 | } | 2293 | } |
2294 | 2294 | ||
2295 | /** | 2295 | /** |
2296 | * pci_acs_enabled - test ACS against required flags for a given device | ||
2297 | * @pdev: device to test | ||
2298 | * @acs_flags: required PCI ACS flags | ||
2299 | * | ||
2300 | * Return true if the device supports the provided flags. Automatically | ||
2301 | * filters out flags that are not implemented on multifunction devices. | ||
2302 | */ | ||
2303 | bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags) | ||
2304 | { | ||
2305 | int pos, ret; | ||
2306 | u16 ctrl; | ||
2307 | |||
2308 | ret = pci_dev_specific_acs_enabled(pdev, acs_flags); | ||
2309 | if (ret >= 0) | ||
2310 | return ret > 0; | ||
2311 | |||
2312 | if (!pci_is_pcie(pdev)) | ||
2313 | return false; | ||
2314 | |||
2315 | /* Filter out flags not applicable to multifunction */ | ||
2316 | if (pdev->multifunction) | ||
2317 | acs_flags &= (PCI_ACS_RR | PCI_ACS_CR | | ||
2318 | PCI_ACS_EC | PCI_ACS_DT); | ||
2319 | |||
2320 | if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM || | ||
2321 | pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT || | ||
2322 | pdev->multifunction) { | ||
2323 | pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS); | ||
2324 | if (!pos) | ||
2325 | return false; | ||
2326 | |||
2327 | pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl); | ||
2328 | if ((ctrl & acs_flags) != acs_flags) | ||
2329 | return false; | ||
2330 | } | ||
2331 | |||
2332 | return true; | ||
2333 | } | ||
2334 | |||
2335 | /** | ||
2336 | * pci_acs_path_enable - test ACS flags from start to end in a hierarchy | ||
2337 | * @start: starting downstream device | ||
2338 | * @end: ending upstream device or NULL to search to the root bus | ||
2339 | * @acs_flags: required flags | ||
2340 | * | ||
2341 | * Walk up a device tree from start to end testing PCI ACS support. If | ||
2342 | * any step along the way does not support the required flags, return false. | ||
2343 | */ | ||
2344 | bool pci_acs_path_enabled(struct pci_dev *start, | ||
2345 | struct pci_dev *end, u16 acs_flags) | ||
2346 | { | ||
2347 | struct pci_dev *pdev, *parent = start; | ||
2348 | |||
2349 | do { | ||
2350 | pdev = parent; | ||
2351 | |||
2352 | if (!pci_acs_enabled(pdev, acs_flags)) | ||
2353 | return false; | ||
2354 | |||
2355 | if (pci_is_root_bus(pdev->bus)) | ||
2356 | return (end == NULL); | ||
2357 | |||
2358 | parent = pdev->bus->self; | ||
2359 | } while (pdev != end); | ||
2360 | |||
2361 | return true; | ||
2362 | } | ||
2363 | |||
2364 | /** | ||
2296 | * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge | 2365 | * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge |
2297 | * @dev: the PCI device | 2366 | * @dev: the PCI device |
2298 | * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTD, 4=INTD) | 2367 | * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTD, 4=INTD) |