Loading drivers/pci/access.c +4 −2 Original line number Diff line number Diff line Loading @@ -162,7 +162,8 @@ int pci_user_read_config_##size \ if (ret > 0) \ ret = -EINVAL; \ return ret; \ } } \ EXPORT_SYMBOL_GPL(pci_user_read_config_##size); /* Returns 0 on success, negative values indicate error. */ #define PCI_USER_WRITE_CONFIG(size,type) \ Loading @@ -181,7 +182,8 @@ int pci_user_write_config_##size \ if (ret > 0) \ ret = -EINVAL; \ return ret; \ } } \ EXPORT_SYMBOL_GPL(pci_user_write_config_##size); PCI_USER_READ_CONFIG(byte, u8) PCI_USER_READ_CONFIG(word, u16) Loading drivers/pci/pci.c +69 −0 Original line number Diff line number Diff line Loading @@ -2364,6 +2364,75 @@ void pci_enable_acs(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); } /** * pci_acs_enabled - test ACS against required flags for a given device * @pdev: device to test * @acs_flags: required PCI ACS flags * * Return true if the device supports the provided flags. Automatically * filters out flags that are not implemented on multifunction devices. */ bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags) { int pos, ret; u16 ctrl; ret = pci_dev_specific_acs_enabled(pdev, acs_flags); if (ret >= 0) return ret > 0; if (!pci_is_pcie(pdev)) return false; /* Filter out flags not applicable to multifunction */ if (pdev->multifunction) acs_flags &= (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC | PCI_ACS_DT); if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM || pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT || pdev->multifunction) { pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS); if (!pos) return false; pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl); if ((ctrl & acs_flags) != acs_flags) return false; } return true; } /** * pci_acs_path_enable - test ACS flags from start to end in a hierarchy * @start: starting downstream device * @end: ending upstream device or NULL to search to the root bus * @acs_flags: required flags * * Walk up a device tree from start to end testing PCI ACS support. If * any step along the way does not support the required flags, return false. */ bool pci_acs_path_enabled(struct pci_dev *start, struct pci_dev *end, u16 acs_flags) { struct pci_dev *pdev, *parent = start; do { pdev = parent; if (!pci_acs_enabled(pdev, acs_flags)) return false; if (pci_is_root_bus(pdev->bus)) return (end == NULL); parent = pdev->bus->self; } while (pdev != end); return true; } /** * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge * @dev: the PCI device Loading drivers/pci/pci.h +0 −7 Original line number Diff line number Diff line Loading @@ -86,13 +86,6 @@ static inline bool pci_is_bridge(struct pci_dev *pci_dev) return !!(pci_dev->subordinate); } extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); extern int pci_user_read_config_dword(struct pci_dev *dev, int where, u32 *val); extern int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val); extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val); extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); struct pci_vpd_ops { ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); Loading drivers/pci/quirks.c +84 −0 Original line number Diff line number Diff line Loading @@ -3205,3 +3205,87 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe) return -ENOTTY; } static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev) { if (!PCI_FUNC(dev->devfn)) return pci_dev_get(dev); return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); } static const struct pci_dev_dma_source { u16 vendor; u16 device; struct pci_dev *(*dma_source)(struct pci_dev *dev); } pci_dev_dma_source[] = { /* * https://bugzilla.redhat.com/show_bug.cgi?id=605888 * * Some Ricoh devices use the function 0 source ID for DMA on * other functions of a multifunction device. The DMA devices * is therefore function 0, which will have implications of the * iommu grouping of these devices. */ { PCI_VENDOR_ID_RICOH, 0xe822, pci_func_0_dma_source }, { PCI_VENDOR_ID_RICOH, 0xe230, pci_func_0_dma_source }, { PCI_VENDOR_ID_RICOH, 0xe832, pci_func_0_dma_source }, { PCI_VENDOR_ID_RICOH, 0xe476, pci_func_0_dma_source }, { 0 } }; /* * IOMMUs with isolation capabilities need to be programmed with the * correct source ID of a device. In most cases, the source ID matches * the device doing the DMA, but sometimes hardware is broken and will * tag the DMA as being sourced from a different device. This function * allows that translation. Note that the reference count of the * returned device is incremented on all paths. */ struct pci_dev *pci_get_dma_source(struct pci_dev *dev) { const struct pci_dev_dma_source *i; for (i = pci_dev_dma_source; i->dma_source; i++) { if ((i->vendor == dev->vendor || i->vendor == (u16)PCI_ANY_ID) && (i->device == dev->device || i->device == (u16)PCI_ANY_ID)) return i->dma_source(dev); } return pci_dev_get(dev); } static const struct pci_dev_acs_enabled { u16 vendor; u16 device; int (*acs_enabled)(struct pci_dev *dev, u16 acs_flags); } pci_dev_acs_enabled[] = { { 0 } }; int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) { const struct pci_dev_acs_enabled *i; int ret; /* * Allow devices that do not expose standard PCIe ACS capabilities * or control to indicate their support here. Multi-function express * devices which do not allow internal peer-to-peer between functions, * but do not implement PCIe ACS may wish to return true here. */ for (i = pci_dev_acs_enabled; i->acs_enabled; i++) { if ((i->vendor == dev->vendor || i->vendor == (u16)PCI_ANY_ID) && (i->device == dev->device || i->device == (u16)PCI_ANY_ID)) { ret = i->acs_enabled(dev, acs_flags); if (ret >= 0) return ret; } } return -ENOTTY; } drivers/xen/xen-pciback/conf_space.c +3 −3 Original line number Diff line number Diff line Loading @@ -124,7 +124,7 @@ static inline u32 merge_value(u32 val, u32 new_val, u32 new_val_mask, return val; } static int pcibios_err_to_errno(int err) static int xen_pcibios_err_to_errno(int err) { switch (err) { case PCIBIOS_SUCCESSFUL: Loading Loading @@ -202,7 +202,7 @@ int xen_pcibk_config_read(struct pci_dev *dev, int offset, int size, pci_name(dev), size, offset, value); *ret_val = value; return pcibios_err_to_errno(err); return xen_pcibios_err_to_errno(err); } int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) Loading Loading @@ -290,7 +290,7 @@ int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) } } return pcibios_err_to_errno(err); return xen_pcibios_err_to_errno(err); } void xen_pcibk_config_free_dyn_fields(struct pci_dev *dev) Loading Loading
drivers/pci/access.c +4 −2 Original line number Diff line number Diff line Loading @@ -162,7 +162,8 @@ int pci_user_read_config_##size \ if (ret > 0) \ ret = -EINVAL; \ return ret; \ } } \ EXPORT_SYMBOL_GPL(pci_user_read_config_##size); /* Returns 0 on success, negative values indicate error. */ #define PCI_USER_WRITE_CONFIG(size,type) \ Loading @@ -181,7 +182,8 @@ int pci_user_write_config_##size \ if (ret > 0) \ ret = -EINVAL; \ return ret; \ } } \ EXPORT_SYMBOL_GPL(pci_user_write_config_##size); PCI_USER_READ_CONFIG(byte, u8) PCI_USER_READ_CONFIG(word, u16) Loading
drivers/pci/pci.c +69 −0 Original line number Diff line number Diff line Loading @@ -2364,6 +2364,75 @@ void pci_enable_acs(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); } /** * pci_acs_enabled - test ACS against required flags for a given device * @pdev: device to test * @acs_flags: required PCI ACS flags * * Return true if the device supports the provided flags. Automatically * filters out flags that are not implemented on multifunction devices. */ bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags) { int pos, ret; u16 ctrl; ret = pci_dev_specific_acs_enabled(pdev, acs_flags); if (ret >= 0) return ret > 0; if (!pci_is_pcie(pdev)) return false; /* Filter out flags not applicable to multifunction */ if (pdev->multifunction) acs_flags &= (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC | PCI_ACS_DT); if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM || pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT || pdev->multifunction) { pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS); if (!pos) return false; pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl); if ((ctrl & acs_flags) != acs_flags) return false; } return true; } /** * pci_acs_path_enable - test ACS flags from start to end in a hierarchy * @start: starting downstream device * @end: ending upstream device or NULL to search to the root bus * @acs_flags: required flags * * Walk up a device tree from start to end testing PCI ACS support. If * any step along the way does not support the required flags, return false. */ bool pci_acs_path_enabled(struct pci_dev *start, struct pci_dev *end, u16 acs_flags) { struct pci_dev *pdev, *parent = start; do { pdev = parent; if (!pci_acs_enabled(pdev, acs_flags)) return false; if (pci_is_root_bus(pdev->bus)) return (end == NULL); parent = pdev->bus->self; } while (pdev != end); return true; } /** * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge * @dev: the PCI device Loading
drivers/pci/pci.h +0 −7 Original line number Diff line number Diff line Loading @@ -86,13 +86,6 @@ static inline bool pci_is_bridge(struct pci_dev *pci_dev) return !!(pci_dev->subordinate); } extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); extern int pci_user_read_config_dword(struct pci_dev *dev, int where, u32 *val); extern int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val); extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val); extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); struct pci_vpd_ops { ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); Loading
drivers/pci/quirks.c +84 −0 Original line number Diff line number Diff line Loading @@ -3205,3 +3205,87 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe) return -ENOTTY; } static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev) { if (!PCI_FUNC(dev->devfn)) return pci_dev_get(dev); return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); } static const struct pci_dev_dma_source { u16 vendor; u16 device; struct pci_dev *(*dma_source)(struct pci_dev *dev); } pci_dev_dma_source[] = { /* * https://bugzilla.redhat.com/show_bug.cgi?id=605888 * * Some Ricoh devices use the function 0 source ID for DMA on * other functions of a multifunction device. The DMA devices * is therefore function 0, which will have implications of the * iommu grouping of these devices. */ { PCI_VENDOR_ID_RICOH, 0xe822, pci_func_0_dma_source }, { PCI_VENDOR_ID_RICOH, 0xe230, pci_func_0_dma_source }, { PCI_VENDOR_ID_RICOH, 0xe832, pci_func_0_dma_source }, { PCI_VENDOR_ID_RICOH, 0xe476, pci_func_0_dma_source }, { 0 } }; /* * IOMMUs with isolation capabilities need to be programmed with the * correct source ID of a device. In most cases, the source ID matches * the device doing the DMA, but sometimes hardware is broken and will * tag the DMA as being sourced from a different device. This function * allows that translation. Note that the reference count of the * returned device is incremented on all paths. */ struct pci_dev *pci_get_dma_source(struct pci_dev *dev) { const struct pci_dev_dma_source *i; for (i = pci_dev_dma_source; i->dma_source; i++) { if ((i->vendor == dev->vendor || i->vendor == (u16)PCI_ANY_ID) && (i->device == dev->device || i->device == (u16)PCI_ANY_ID)) return i->dma_source(dev); } return pci_dev_get(dev); } static const struct pci_dev_acs_enabled { u16 vendor; u16 device; int (*acs_enabled)(struct pci_dev *dev, u16 acs_flags); } pci_dev_acs_enabled[] = { { 0 } }; int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) { const struct pci_dev_acs_enabled *i; int ret; /* * Allow devices that do not expose standard PCIe ACS capabilities * or control to indicate their support here. Multi-function express * devices which do not allow internal peer-to-peer between functions, * but do not implement PCIe ACS may wish to return true here. */ for (i = pci_dev_acs_enabled; i->acs_enabled; i++) { if ((i->vendor == dev->vendor || i->vendor == (u16)PCI_ANY_ID) && (i->device == dev->device || i->device == (u16)PCI_ANY_ID)) { ret = i->acs_enabled(dev, acs_flags); if (ret >= 0) return ret; } } return -ENOTTY; }
drivers/xen/xen-pciback/conf_space.c +3 −3 Original line number Diff line number Diff line Loading @@ -124,7 +124,7 @@ static inline u32 merge_value(u32 val, u32 new_val, u32 new_val_mask, return val; } static int pcibios_err_to_errno(int err) static int xen_pcibios_err_to_errno(int err) { switch (err) { case PCIBIOS_SUCCESSFUL: Loading Loading @@ -202,7 +202,7 @@ int xen_pcibk_config_read(struct pci_dev *dev, int offset, int size, pci_name(dev), size, offset, value); *ret_val = value; return pcibios_err_to_errno(err); return xen_pcibios_err_to_errno(err); } int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) Loading Loading @@ -290,7 +290,7 @@ int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) } } return pcibios_err_to_errno(err); return xen_pcibios_err_to_errno(err); } void xen_pcibk_config_free_dyn_fields(struct pci_dev *dev) Loading