diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2012-06-13 19:04:54 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2012-06-13 19:04:54 -0400 |
commit | cc2fa3fa320d5f40a12713c104bbe5d3da4636e4 (patch) | |
tree | 342445a784c566116505ab8c9e7a24803a6e70c4 /drivers | |
parent | 10c480933d0ad2ea27630cbaa723a5d33dbece00 (diff) | |
parent | a0dee2ed0cdc666b5622f1fc74979355a6b36850 (diff) |
Merge branch 'topic/alex-vfio-prep' into next
* topic/alex-vfio-prep:
PCI: misc pci_reg additions
PCI: create common pcibios_err_to_errno
PCI: export pci_user functions for use by other drivers
PCI: add ACS validation utility
PCI: add PCI DMA source ID quirk
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/access.c | 6 | ||||
-rw-r--r-- | drivers/pci/pci.c | 69 | ||||
-rw-r--r-- | drivers/pci/pci.h | 7 | ||||
-rw-r--r-- | drivers/pci/quirks.c | 84 | ||||
-rw-r--r-- | drivers/xen/xen-pciback/conf_space.c | 6 |
5 files changed, 160 insertions, 12 deletions
diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 2a581642c237..ba91a7e17519 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c | |||
@@ -162,7 +162,8 @@ int pci_user_read_config_##size \ | |||
162 | if (ret > 0) \ | 162 | if (ret > 0) \ |
163 | ret = -EINVAL; \ | 163 | ret = -EINVAL; \ |
164 | return ret; \ | 164 | return ret; \ |
165 | } | 165 | } \ |
166 | EXPORT_SYMBOL_GPL(pci_user_read_config_##size); | ||
166 | 167 | ||
167 | /* Returns 0 on success, negative values indicate error. */ | 168 | /* Returns 0 on success, negative values indicate error. */ |
168 | #define PCI_USER_WRITE_CONFIG(size,type) \ | 169 | #define PCI_USER_WRITE_CONFIG(size,type) \ |
@@ -181,7 +182,8 @@ int pci_user_write_config_##size \ | |||
181 | if (ret > 0) \ | 182 | if (ret > 0) \ |
182 | ret = -EINVAL; \ | 183 | ret = -EINVAL; \ |
183 | return ret; \ | 184 | return ret; \ |
184 | } | 185 | } \ |
186 | EXPORT_SYMBOL_GPL(pci_user_write_config_##size); | ||
185 | 187 | ||
186 | PCI_USER_READ_CONFIG(byte, u8) | 188 | PCI_USER_READ_CONFIG(byte, u8) |
187 | PCI_USER_READ_CONFIG(word, u16) | 189 | PCI_USER_READ_CONFIG(word, u16) |
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) |
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1c56ea8110b1..4884d77d33b6 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h | |||
@@ -86,13 +86,6 @@ static inline bool pci_is_bridge(struct pci_dev *pci_dev) | |||
86 | return !!(pci_dev->subordinate); | 86 | return !!(pci_dev->subordinate); |
87 | } | 87 | } |
88 | 88 | ||
89 | extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); | ||
90 | extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); | ||
91 | extern int pci_user_read_config_dword(struct pci_dev *dev, int where, u32 *val); | ||
92 | extern int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val); | ||
93 | extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val); | ||
94 | extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); | ||
95 | |||
96 | struct pci_vpd_ops { | 89 | struct pci_vpd_ops { |
97 | ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); | 90 | ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); |
98 | ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); | 91 | ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); |
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a7521677541..27e2c8f4ec73 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c | |||
@@ -3179,3 +3179,87 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe) | |||
3179 | 3179 | ||
3180 | return -ENOTTY; | 3180 | return -ENOTTY; |
3181 | } | 3181 | } |
3182 | |||
3183 | static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev) | ||
3184 | { | ||
3185 | if (!PCI_FUNC(dev->devfn)) | ||
3186 | return pci_dev_get(dev); | ||
3187 | |||
3188 | return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); | ||
3189 | } | ||
3190 | |||
3191 | static const struct pci_dev_dma_source { | ||
3192 | u16 vendor; | ||
3193 | u16 device; | ||
3194 | struct pci_dev *(*dma_source)(struct pci_dev *dev); | ||
3195 | } pci_dev_dma_source[] = { | ||
3196 | /* | ||
3197 | * https://bugzilla.redhat.com/show_bug.cgi?id=605888 | ||
3198 | * | ||
3199 | * Some Ricoh devices use the function 0 source ID for DMA on | ||
3200 | * other functions of a multifunction device. The DMA devices | ||
3201 | * is therefore function 0, which will have implications of the | ||
3202 | * iommu grouping of these devices. | ||
3203 | */ | ||
3204 | { PCI_VENDOR_ID_RICOH, 0xe822, pci_func_0_dma_source }, | ||
3205 | { PCI_VENDOR_ID_RICOH, 0xe230, pci_func_0_dma_source }, | ||
3206 | { PCI_VENDOR_ID_RICOH, 0xe832, pci_func_0_dma_source }, | ||
3207 | { PCI_VENDOR_ID_RICOH, 0xe476, pci_func_0_dma_source }, | ||
3208 | { 0 } | ||
3209 | }; | ||
3210 | |||
3211 | /* | ||
3212 | * IOMMUs with isolation capabilities need to be programmed with the | ||
3213 | * correct source ID of a device. In most cases, the source ID matches | ||
3214 | * the device doing the DMA, but sometimes hardware is broken and will | ||
3215 | * tag the DMA as being sourced from a different device. This function | ||
3216 | * allows that translation. Note that the reference count of the | ||
3217 | * returned device is incremented on all paths. | ||
3218 | */ | ||
3219 | struct pci_dev *pci_get_dma_source(struct pci_dev *dev) | ||
3220 | { | ||
3221 | const struct pci_dev_dma_source *i; | ||
3222 | |||
3223 | for (i = pci_dev_dma_source; i->dma_source; i++) { | ||
3224 | if ((i->vendor == dev->vendor || | ||
3225 | i->vendor == (u16)PCI_ANY_ID) && | ||
3226 | (i->device == dev->device || | ||
3227 | i->device == (u16)PCI_ANY_ID)) | ||
3228 | return i->dma_source(dev); | ||
3229 | } | ||
3230 | |||
3231 | return pci_dev_get(dev); | ||
3232 | } | ||
3233 | |||
3234 | static const struct pci_dev_acs_enabled { | ||
3235 | u16 vendor; | ||
3236 | u16 device; | ||
3237 | int (*acs_enabled)(struct pci_dev *dev, u16 acs_flags); | ||
3238 | } pci_dev_acs_enabled[] = { | ||
3239 | { 0 } | ||
3240 | }; | ||
3241 | |||
3242 | int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) | ||
3243 | { | ||
3244 | const struct pci_dev_acs_enabled *i; | ||
3245 | int ret; | ||
3246 | |||
3247 | /* | ||
3248 | * Allow devices that do not expose standard PCIe ACS capabilities | ||
3249 | * or control to indicate their support here. Multi-function express | ||
3250 | * devices which do not allow internal peer-to-peer between functions, | ||
3251 | * but do not implement PCIe ACS may wish to return true here. | ||
3252 | */ | ||
3253 | for (i = pci_dev_acs_enabled; i->acs_enabled; i++) { | ||
3254 | if ((i->vendor == dev->vendor || | ||
3255 | i->vendor == (u16)PCI_ANY_ID) && | ||
3256 | (i->device == dev->device || | ||
3257 | i->device == (u16)PCI_ANY_ID)) { | ||
3258 | ret = i->acs_enabled(dev, acs_flags); | ||
3259 | if (ret >= 0) | ||
3260 | return ret; | ||
3261 | } | ||
3262 | } | ||
3263 | |||
3264 | return -ENOTTY; | ||
3265 | } | ||
diff --git a/drivers/xen/xen-pciback/conf_space.c b/drivers/xen/xen-pciback/conf_space.c index 30d7be026c18..46ae0f9f02ad 100644 --- a/drivers/xen/xen-pciback/conf_space.c +++ b/drivers/xen/xen-pciback/conf_space.c | |||
@@ -124,7 +124,7 @@ static inline u32 merge_value(u32 val, u32 new_val, u32 new_val_mask, | |||
124 | return val; | 124 | return val; |
125 | } | 125 | } |
126 | 126 | ||
127 | static int pcibios_err_to_errno(int err) | 127 | static int xen_pcibios_err_to_errno(int err) |
128 | { | 128 | { |
129 | switch (err) { | 129 | switch (err) { |
130 | case PCIBIOS_SUCCESSFUL: | 130 | case PCIBIOS_SUCCESSFUL: |
@@ -202,7 +202,7 @@ out: | |||
202 | pci_name(dev), size, offset, value); | 202 | pci_name(dev), size, offset, value); |
203 | 203 | ||
204 | *ret_val = value; | 204 | *ret_val = value; |
205 | return pcibios_err_to_errno(err); | 205 | return xen_pcibios_err_to_errno(err); |
206 | } | 206 | } |
207 | 207 | ||
208 | int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) | 208 | int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) |
@@ -290,7 +290,7 @@ int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) | |||
290 | } | 290 | } |
291 | } | 291 | } |
292 | 292 | ||
293 | return pcibios_err_to_errno(err); | 293 | return xen_pcibios_err_to_errno(err); |
294 | } | 294 | } |
295 | 295 | ||
296 | void xen_pcibk_config_free_dyn_fields(struct pci_dev *dev) | 296 | void xen_pcibk_config_free_dyn_fields(struct pci_dev *dev) |