diff options
author | Alex Williamson <alex.williamson@redhat.com> | 2014-02-03 16:27:33 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2014-02-11 15:49:56 -0500 |
commit | 2c74424470a0f98df8d6540642aff96d1a0b94b3 (patch) | |
tree | 61f24a066645ddd7884d8545b579e0754c2763ce | |
parent | 38dbfb59d1175ef458d006556061adeaa8751b72 (diff) |
PCI: Add device-specific PCI ACS enable
Some devices support PCI ACS-like features, but don't report it using the
standard PCIe capabilities. We already provide hooks for device-specific
testing of ACS, but not for device-specific enabling of ACS. This provides
that setup hook.
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-rw-r--r-- | drivers/pci/pci.c | 26 | ||||
-rw-r--r-- | drivers/pci/quirks.c | 25 | ||||
-rw-r--r-- | include/linux/pci.h | 2 |
3 files changed, 47 insertions, 6 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1febe90831b4..b89502ff3139 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -2180,21 +2180,18 @@ void pci_request_acs(void) | |||
2180 | } | 2180 | } |
2181 | 2181 | ||
2182 | /** | 2182 | /** |
2183 | * pci_enable_acs - enable ACS if hardware support it | 2183 | * pci_std_enable_acs - enable ACS on devices using standard ACS capabilites |
2184 | * @dev: the PCI device | 2184 | * @dev: the PCI device |
2185 | */ | 2185 | */ |
2186 | void pci_enable_acs(struct pci_dev *dev) | 2186 | static int pci_std_enable_acs(struct pci_dev *dev) |
2187 | { | 2187 | { |
2188 | int pos; | 2188 | int pos; |
2189 | u16 cap; | 2189 | u16 cap; |
2190 | u16 ctrl; | 2190 | u16 ctrl; |
2191 | 2191 | ||
2192 | if (!pci_acs_enable) | ||
2193 | return; | ||
2194 | |||
2195 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); | 2192 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); |
2196 | if (!pos) | 2193 | if (!pos) |
2197 | return; | 2194 | return -ENODEV; |
2198 | 2195 | ||
2199 | pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap); | 2196 | pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap); |
2200 | pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl); | 2197 | pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl); |
@@ -2212,6 +2209,23 @@ void pci_enable_acs(struct pci_dev *dev) | |||
2212 | ctrl |= (cap & PCI_ACS_UF); | 2209 | ctrl |= (cap & PCI_ACS_UF); |
2213 | 2210 | ||
2214 | pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); | 2211 | pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); |
2212 | |||
2213 | return 0; | ||
2214 | } | ||
2215 | |||
2216 | /** | ||
2217 | * pci_enable_acs - enable ACS if hardware support it | ||
2218 | * @dev: the PCI device | ||
2219 | */ | ||
2220 | void pci_enable_acs(struct pci_dev *dev) | ||
2221 | { | ||
2222 | if (!pci_acs_enable) | ||
2223 | return; | ||
2224 | |||
2225 | if (!pci_std_enable_acs(dev)) | ||
2226 | return; | ||
2227 | |||
2228 | pci_dev_specific_enable_acs(dev); | ||
2215 | } | 2229 | } |
2216 | 2230 | ||
2217 | static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) | 2231 | static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) |
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 5cb726c193de..f681fb08f5e4 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c | |||
@@ -3461,3 +3461,28 @@ int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) | |||
3461 | 3461 | ||
3462 | return -ENOTTY; | 3462 | return -ENOTTY; |
3463 | } | 3463 | } |
3464 | |||
3465 | static const struct pci_dev_enable_acs { | ||
3466 | u16 vendor; | ||
3467 | u16 device; | ||
3468 | int (*enable_acs)(struct pci_dev *dev); | ||
3469 | } pci_dev_enable_acs[] = { | ||
3470 | { 0 } | ||
3471 | }; | ||
3472 | |||
3473 | void pci_dev_specific_enable_acs(struct pci_dev *dev) | ||
3474 | { | ||
3475 | const struct pci_dev_enable_acs *i; | ||
3476 | int ret; | ||
3477 | |||
3478 | for (i = pci_dev_enable_acs; i->enable_acs; i++) { | ||
3479 | if ((i->vendor == dev->vendor || | ||
3480 | i->vendor == (u16)PCI_ANY_ID) && | ||
3481 | (i->device == dev->device || | ||
3482 | i->device == (u16)PCI_ANY_ID)) { | ||
3483 | ret = i->enable_acs(dev); | ||
3484 | if (ret >= 0) | ||
3485 | return; | ||
3486 | } | ||
3487 | } | ||
3488 | } | ||
diff --git a/include/linux/pci.h b/include/linux/pci.h index fb57c892b214..0f76d0f66365 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -1510,6 +1510,7 @@ enum pci_fixup_pass { | |||
1510 | void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); | 1510 | void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); |
1511 | struct pci_dev *pci_get_dma_source(struct pci_dev *dev); | 1511 | struct pci_dev *pci_get_dma_source(struct pci_dev *dev); |
1512 | int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); | 1512 | int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); |
1513 | void pci_dev_specific_enable_acs(struct pci_dev *dev); | ||
1513 | #else | 1514 | #else |
1514 | static inline void pci_fixup_device(enum pci_fixup_pass pass, | 1515 | static inline void pci_fixup_device(enum pci_fixup_pass pass, |
1515 | struct pci_dev *dev) { } | 1516 | struct pci_dev *dev) { } |
@@ -1522,6 +1523,7 @@ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev, | |||
1522 | { | 1523 | { |
1523 | return -ENOTTY; | 1524 | return -ENOTTY; |
1524 | } | 1525 | } |
1526 | static inline void pci_dev_specific_enable_acs(struct pci_dev *dev) { } | ||
1525 | #endif | 1527 | #endif |
1526 | 1528 | ||
1527 | void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen); | 1529 | void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen); |