aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2012-06-13 19:04:54 -0400
committerBjorn Helgaas <bhelgaas@google.com>2012-06-13 19:04:54 -0400
commitcc2fa3fa320d5f40a12713c104bbe5d3da4636e4 (patch)
tree342445a784c566116505ab8c9e7a24803a6e70c4 /drivers
parent10c480933d0ad2ea27630cbaa723a5d33dbece00 (diff)
parenta0dee2ed0cdc666b5622f1fc74979355a6b36850 (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.c6
-rw-r--r--drivers/pci/pci.c69
-rw-r--r--drivers/pci/pci.h7
-rw-r--r--drivers/pci/quirks.c84
-rw-r--r--drivers/xen/xen-pciback/conf_space.c6
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} \
166EXPORT_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} \
186EXPORT_SYMBOL_GPL(pci_user_write_config_##size);
185 187
186PCI_USER_READ_CONFIG(byte, u8) 188PCI_USER_READ_CONFIG(byte, u8)
187PCI_USER_READ_CONFIG(word, u16) 189PCI_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 */
2303bool 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 */
2344bool 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
89extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val);
90extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val);
91extern int pci_user_read_config_dword(struct pci_dev *dev, int where, u32 *val);
92extern int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val);
93extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val);
94extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val);
95
96struct pci_vpd_ops { 89struct 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
3183static 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
3191static 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 */
3219struct 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
3234static 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
3242int 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
127static int pcibios_err_to_errno(int err) 127static 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
208int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) 208int 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
296void xen_pcibk_config_free_dyn_fields(struct pci_dev *dev) 296void xen_pcibk_config_free_dyn_fields(struct pci_dev *dev)