From c1309040967e200d3ea6415ae54cf6a69d7ad996 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Mon, 31 Mar 2014 14:58:39 -0700 Subject: PCI: Use designated initialization in PCI_VDEVICE By using designated initialization in PCI_VDEVICE, like other similar macros, many "missing initializer" warnings that appear when compiling with W=2 can be silenced. Tested-by: Phil Schmitt Tested-by: Aaron Brown Tested-by: Kavindya Deegala Signed-off-by: Mark Rustad Signed-off-by: Jeff Kirsher Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux/pci.h') diff --git a/include/linux/pci.h b/include/linux/pci.h index aab57b4abe7..a95aac7ad37 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -680,8 +680,8 @@ struct pci_driver { /** * PCI_VDEVICE - macro used to describe a specific pci device in short form - * @vendor: the vendor name - * @device: the 16 bit PCI Device ID + * @vend: the vendor name + * @dev: the 16 bit PCI Device ID * * This macro is used to create a struct pci_device_id that matches a * specific PCI device. The subvendor, and subdevice fields will be set @@ -689,9 +689,9 @@ struct pci_driver { * private data. */ -#define PCI_VDEVICE(vendor, device) \ - PCI_VENDOR_ID_##vendor, (device), \ - PCI_ANY_ID, PCI_ANY_ID, 0, 0 +#define PCI_VDEVICE(vend, dev) \ + .vendor = PCI_VENDOR_ID_##vend, .device = (dev), \ + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0 /* these external functions are only available when PCI support is enabled */ #ifdef CONFIG_PCI -- cgit v1.2.3-70-g09d2 From 034cd97ebda4062eb4402a6cf963ccd262caa86a Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 14 Apr 2014 15:28:35 +0200 Subject: PCI/MSI: Remove pci_enable_msi_block() There are no users of pci_enable_msi_block() function left. Obsolete it in favor of pci_enable_msi_range() and pci_enable_msi_exact() functions. Previously, we called arch_setup_msi_irqs() once, requesting the same vector count we passed to arch_msi_check_device(). Now we may call it several times: if it returns failure, we may retry and request fewer vectors. We don't keep track of the vector count we initially passed to arch_msi_check_device(). We only keep track of the number of vectors successfully set up by arch_setup_msi_irqs(), and this is what we use to clean things up when disabling MSI. Therefore, we assume that arch_msi_check_device() does nothing that will have to be cleaned up later. [bhelgaas: changelog] Signed-off-by: Alexander Gordeev Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 79 ++++++++++++++++++++++------------------------------- include/linux/pci.h | 5 +--- 2 files changed, 34 insertions(+), 50 deletions(-) (limited to 'include/linux/pci.h') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 04130c3f9cf..36dd0caa175 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -879,50 +879,6 @@ int pci_msi_vec_count(struct pci_dev *dev) } EXPORT_SYMBOL(pci_msi_vec_count); -/** - * pci_enable_msi_block - configure device's MSI capability structure - * @dev: device to configure - * @nvec: number of interrupts to configure - * - * Allocate IRQs for a device with the MSI capability. - * This function returns a negative errno if an error occurs. If it - * is unable to allocate the number of interrupts requested, it returns - * the number of interrupts it might be able to allocate. If it successfully - * allocates at least the number of interrupts requested, it returns 0 and - * updates the @dev's irq member to the lowest new interrupt number; the - * other interrupt numbers allocated to this device are consecutive. - */ -int pci_enable_msi_block(struct pci_dev *dev, int nvec) -{ - int status, maxvec; - - if (dev->current_state != PCI_D0) - return -EINVAL; - - maxvec = pci_msi_vec_count(dev); - if (maxvec < 0) - return maxvec; - if (nvec > maxvec) - return maxvec; - - status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI); - if (status) - return status; - - WARN_ON(!!dev->msi_enabled); - - /* Check whether driver already requested MSI-X irqs */ - if (dev->msix_enabled) { - dev_info(&dev->dev, "can't enable MSI " - "(MSI-X already enabled)\n"); - return -EINVAL; - } - - status = msi_capability_init(dev, nvec); - return status; -} -EXPORT_SYMBOL(pci_enable_msi_block); - void pci_msi_shutdown(struct pci_dev *dev) { struct msi_desc *desc; @@ -1128,14 +1084,45 @@ void pci_msi_init_pci_dev(struct pci_dev *dev) **/ int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) { - int nvec = maxvec; + int nvec; int rc; + if (dev->current_state != PCI_D0) + return -EINVAL; + + WARN_ON(!!dev->msi_enabled); + + /* Check whether driver already requested MSI-X irqs */ + if (dev->msix_enabled) { + dev_info(&dev->dev, + "can't enable MSI (MSI-X already enabled)\n"); + return -EINVAL; + } + if (maxvec < minvec) return -ERANGE; + nvec = pci_msi_vec_count(dev); + if (nvec < 0) + return nvec; + else if (nvec < minvec) + return -EINVAL; + else if (nvec > maxvec) + nvec = maxvec; + + do { + rc = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI); + if (rc < 0) { + return rc; + } else if (rc > 0) { + if (rc < minvec) + return -ENOSPC; + nvec = rc; + } + } while (rc); + do { - rc = pci_enable_msi_block(dev, nvec); + rc = msi_capability_init(dev, nvec); if (rc < 0) { return rc; } else if (rc > 0) { diff --git a/include/linux/pci.h b/include/linux/pci.h index aab57b4abe7..499755e6dab 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1158,7 +1158,6 @@ struct msix_entry { #ifdef CONFIG_PCI_MSI int pci_msi_vec_count(struct pci_dev *dev); -int pci_enable_msi_block(struct pci_dev *dev, int nvec); void pci_msi_shutdown(struct pci_dev *dev); void pci_disable_msi(struct pci_dev *dev); int pci_msix_vec_count(struct pci_dev *dev); @@ -1188,8 +1187,6 @@ static inline int pci_enable_msix_exact(struct pci_dev *dev, } #else static inline int pci_msi_vec_count(struct pci_dev *dev) { return -ENOSYS; } -static inline int pci_enable_msi_block(struct pci_dev *dev, int nvec) -{ return -ENOSYS; } static inline void pci_msi_shutdown(struct pci_dev *dev) { } static inline void pci_disable_msi(struct pci_dev *dev) { } static inline int pci_msix_vec_count(struct pci_dev *dev) { return -ENOSYS; } @@ -1244,7 +1241,7 @@ static inline void pcie_set_ecrc_checking(struct pci_dev *dev) { } static inline void pcie_ecrc_get_policy(char *str) { } #endif -#define pci_enable_msi(pdev) pci_enable_msi_block(pdev, 1) +#define pci_enable_msi(pdev) pci_enable_msi_exact(pdev, 1) #ifdef CONFIG_HT_IRQ /* The functions a driver should call */ -- cgit v1.2.3-70-g09d2 From 9edbcd2252b5ef148177c9f2c11a56469cf5db52 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Thu, 17 Apr 2014 19:48:07 +0200 Subject: PCI: Remove pcibios_add_platform_entries() Remove pcibios_add_platform_entries(). Architecture-specific attributes can be achieved by setting pdev->dev.groups. Link: https://lkml.kernel.org/r/alpine.LFD.2.11.1404141101500.1529@denkbrett Signed-off-by: Sebastian Ott Signed-off-by: Bjorn Helgaas Acked-by: Greg Kroah-Hartman --- drivers/pci/pci-sysfs.c | 10 ---------- include/linux/pci.h | 1 - 2 files changed, 11 deletions(-) (limited to 'include/linux/pci.h') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 3db1c7ff5dd..b7333fa5f80 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1273,11 +1273,6 @@ static struct bin_attribute pcie_config_attr = { .write = pci_write_config, }; -int __weak pcibios_add_platform_entries(struct pci_dev *dev) -{ - return 0; -} - static ssize_t reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1393,11 +1388,6 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) pdev->rom_attr = attr; } - /* add platform-specific attributes */ - retval = pcibios_add_platform_entries(pdev); - if (retval) - goto err_rom_file; - /* add sysfs entries for various capabilities */ retval = pci_create_capabilities_sysfs(pdev); if (retval) diff --git a/include/linux/pci.h b/include/linux/pci.h index a95aac7ad37..84182b153b2 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1572,7 +1572,6 @@ extern unsigned long pci_hotplug_io_size; extern unsigned long pci_hotplug_mem_size; /* Architecture-specific versions may override these (weak) */ -int pcibios_add_platform_entries(struct pci_dev *dev); void pcibios_disable_device(struct pci_dev *dev); void pcibios_set_master(struct pci_dev *dev); int pcibios_set_pcie_reset_state(struct pci_dev *dev, -- cgit v1.2.3-70-g09d2 From 3ebe7f9f7e4a4fd1f6461ecd01ff2961317a483a Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Fri, 2 May 2014 10:40:42 -0600 Subject: PCI: Notify driver before and after device reset Notify a PCI device driver when its device's access is about to be disabled for an impending reset attempt, then after the attempt completes and device access is restored. The notification is via the pci_error_handlers interface. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 21 +++++++++++++++++++++ include/linux/pci.h | 3 +++ 2 files changed, 24 insertions(+) (limited to 'include/linux/pci.h') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7325d43bf03..43d87b26ec5 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3305,8 +3305,27 @@ static void pci_dev_unlock(struct pci_dev *dev) pci_cfg_access_unlock(dev); } +/** + * pci_reset_notify - notify device driver of reset + * @dev: device to be notified of reset + * @prepare: 'true' if device is about to be reset; 'false' if reset attempt + * completed + * + * Must be called prior to device access being disabled and after device + * access is restored. + */ +static void pci_reset_notify(struct pci_dev *dev, bool prepare) +{ + const struct pci_error_handlers *err_handler = + dev->driver ? dev->driver->err_handler : NULL; + if (err_handler && err_handler->reset_notify) + err_handler->reset_notify(dev, prepare); +} + static void pci_dev_save_and_disable(struct pci_dev *dev) { + pci_reset_notify(dev, true); + /* * Wake-up device prior to save. PM registers default to D0 after * reset and a simple register restore doesn't reliably return @@ -3328,6 +3347,7 @@ static void pci_dev_save_and_disable(struct pci_dev *dev) static void pci_dev_restore(struct pci_dev *dev) { pci_restore_state(dev); + pci_reset_notify(dev, false); } static int pci_dev_reset(struct pci_dev *dev, int probe) @@ -3344,6 +3364,7 @@ static int pci_dev_reset(struct pci_dev *dev, int probe) return rc; } + /** * __pci_reset_function - reset a PCI device function * @dev: PCI device to reset diff --git a/include/linux/pci.h b/include/linux/pci.h index aab57b4abe7..31c43093e53 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -603,6 +603,9 @@ struct pci_error_handlers { /* PCI slot has been reset */ pci_ers_result_t (*slot_reset)(struct pci_dev *dev); + /* PCI function reset prepare or completed */ + void (*reset_notify)(struct pci_dev *dev, bool prepare); + /* Device driver may resume normal operations */ void (*resume)(struct pci_dev *dev); }; -- cgit v1.2.3-70-g09d2 From 1c86438c9423a26cc9f7f74a8950d9cf9c93bc23 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Sun, 4 May 2014 12:23:37 +0800 Subject: PCI: Add new pci_is_bridge() interface Add a helper function to check a device's header type for PCI bridge or CardBus bridge. Requires: 326c1cdae741 PCI: Rename pci_is_bridge() to pci_has_subordinate() Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include/linux/pci.h') diff --git a/include/linux/pci.h b/include/linux/pci.h index aab57b4abe7..f2a5946ea0b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -477,6 +477,19 @@ static inline bool pci_is_root_bus(struct pci_bus *pbus) return !(pbus->parent); } +/** + * pci_is_bridge - check if the PCI device is a bridge + * @dev: PCI device + * + * Return true if the PCI device is bridge whether it has subordinate + * or not. + */ +static inline bool pci_is_bridge(struct pci_dev *dev) +{ + return dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || + dev->hdr_type == PCI_HEADER_TYPE_CARDBUS; +} + static inline struct pci_dev *pci_upstream_bridge(struct pci_dev *dev) { dev = pci_physfn(dev); -- cgit v1.2.3-70-g09d2 From a43ae58c848cfbadaba81c8d63202b4487f922a0 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 6 May 2014 11:29:52 +0800 Subject: PCI: Turn pcibios_penalize_isa_irq() into a weak function pcibios_penalize_isa_irq() is only implemented by x86 now, and legacy ISA is not used by some architectures. Make pcibios_penalize_isa_irq() a __weak function to simplify the code. This removes the need for new platforms to add stub implementations of pcibios_penalize_isa_irq(). [bhelgaas: changelog, comments] Signed-off-by: Hanjun Guo Signed-off-by: Bjorn Helgaas Acked-by: Arnd Bergmann --- arch/alpha/include/asm/pci.h | 5 ----- arch/arm/include/asm/pci.h | 5 ----- arch/blackfin/include/asm/pci.h | 5 ----- arch/cris/include/asm/pci.h | 1 - arch/frv/include/asm/pci.h | 2 -- arch/frv/mb93090-mb00/pci-irq.c | 4 ---- arch/ia64/include/asm/pci.h | 6 ------ arch/microblaze/include/asm/pci.h | 5 ----- arch/mips/include/asm/pci.h | 5 ----- arch/mn10300/include/asm/pci.h | 1 - arch/mn10300/unit-asb2305/pci-irq.c | 4 ---- arch/parisc/include/asm/pci.h | 5 ----- arch/powerpc/include/asm/pci.h | 5 ----- arch/sh/include/asm/pci.h | 5 ----- arch/sparc/include/asm/pci_32.h | 5 ----- arch/sparc/include/asm/pci_64.h | 5 ----- arch/unicore32/include/asm/pci.h | 5 ----- arch/x86/include/asm/pci.h | 1 - arch/xtensa/include/asm/pci.h | 5 ----- drivers/pci/pci.c | 11 +++++++++++ include/linux/pci.h | 1 + 21 files changed, 12 insertions(+), 79 deletions(-) (limited to 'include/linux/pci.h') diff --git a/arch/alpha/include/asm/pci.h b/arch/alpha/include/asm/pci.h index d01afb78919..f7f680f7457 100644 --- a/arch/alpha/include/asm/pci.h +++ b/arch/alpha/include/asm/pci.h @@ -59,11 +59,6 @@ struct pci_controller { extern void pcibios_set_master(struct pci_dev *dev); -extern inline void pcibios_penalize_isa_irq(int irq, int active) -{ - /* We don't do dynamic PCI IRQ allocation */ -} - /* IOMMU controls. */ /* The PCI address space does not equal the physical memory address space. diff --git a/arch/arm/include/asm/pci.h b/arch/arm/include/asm/pci.h index 680a83e9446..7e95d8535e2 100644 --- a/arch/arm/include/asm/pci.h +++ b/arch/arm/include/asm/pci.h @@ -31,11 +31,6 @@ static inline int pci_proc_domain(struct pci_bus *bus) } #endif /* CONFIG_PCI_DOMAINS */ -static inline void pcibios_penalize_isa_irq(int irq, int active) -{ - /* We don't do dynamic PCI IRQ allocation */ -} - /* * The PCI address space does equal the physical memory address space. * The networking and block device layers use this boolean for bounce diff --git a/arch/blackfin/include/asm/pci.h b/arch/blackfin/include/asm/pci.h index 74352c4597d..c737909fba4 100644 --- a/arch/blackfin/include/asm/pci.h +++ b/arch/blackfin/include/asm/pci.h @@ -10,9 +10,4 @@ #define PCIBIOS_MIN_IO 0x00001000 #define PCIBIOS_MIN_MEM 0x10000000 -static inline void pcibios_penalize_isa_irq(int irq) -{ - /* We don't do dynamic PCI IRQ allocation */ -} - #endif /* _ASM_BFIN_PCI_H */ diff --git a/arch/cris/include/asm/pci.h b/arch/cris/include/asm/pci.h index f666734926d..cc2399c175e 100644 --- a/arch/cris/include/asm/pci.h +++ b/arch/cris/include/asm/pci.h @@ -20,7 +20,6 @@ void pcibios_config_init(void); struct pci_bus * pcibios_scan_root(int bus); void pcibios_set_master(struct pci_dev *dev); -void pcibios_penalize_isa_irq(int irq); struct irq_routing_table *pcibios_get_irq_routing_table(void); int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq); diff --git a/arch/frv/include/asm/pci.h b/arch/frv/include/asm/pci.h index ef03baf5d89..2035a4d3f9b 100644 --- a/arch/frv/include/asm/pci.h +++ b/arch/frv/include/asm/pci.h @@ -24,8 +24,6 @@ struct pci_dev; extern void pcibios_set_master(struct pci_dev *dev); -extern void pcibios_penalize_isa_irq(int irq); - #ifdef CONFIG_MMU extern void *consistent_alloc(gfp_t gfp, size_t size, dma_addr_t *dma_handle); extern void consistent_free(void *vaddr); diff --git a/arch/frv/mb93090-mb00/pci-irq.c b/arch/frv/mb93090-mb00/pci-irq.c index c677b9d81d3..1c35c93f942 100644 --- a/arch/frv/mb93090-mb00/pci-irq.c +++ b/arch/frv/mb93090-mb00/pci-irq.c @@ -55,10 +55,6 @@ void __init pcibios_fixup_irqs(void) } } -void __init pcibios_penalize_isa_irq(int irq) -{ -} - void pcibios_enable_irq(struct pci_dev *dev) { pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h index 7d41cc08982..52af5ed9f60 100644 --- a/arch/ia64/include/asm/pci.h +++ b/arch/ia64/include/asm/pci.h @@ -50,12 +50,6 @@ struct pci_dev; extern unsigned long ia64_max_iommu_merge_mask; #define PCI_DMA_BUS_IS_PHYS (ia64_max_iommu_merge_mask == ~0UL) -static inline void -pcibios_penalize_isa_irq (int irq, int active) -{ - /* We don't do dynamic PCI IRQ allocation */ -} - #include #ifdef CONFIG_PCI diff --git a/arch/microblaze/include/asm/pci.h b/arch/microblaze/include/asm/pci.h index 935f9bec414..335524040ff 100644 --- a/arch/microblaze/include/asm/pci.h +++ b/arch/microblaze/include/asm/pci.h @@ -44,11 +44,6 @@ struct pci_dev; */ #define pcibios_assign_all_busses() 0 -static inline void pcibios_penalize_isa_irq(int irq, int active) -{ - /* We don't do dynamic PCI IRQ allocation */ -} - #ifdef CONFIG_PCI extern void set_pci_dma_ops(struct dma_map_ops *dma_ops); extern struct dma_map_ops *get_pci_dma_ops(void); diff --git a/arch/mips/include/asm/pci.h b/arch/mips/include/asm/pci.h index 12d6842962b..974b0e30896 100644 --- a/arch/mips/include/asm/pci.h +++ b/arch/mips/include/asm/pci.h @@ -73,11 +73,6 @@ extern unsigned long PCIBIOS_MIN_MEM; extern void pcibios_set_master(struct pci_dev *dev); -static inline void pcibios_penalize_isa_irq(int irq, int active) -{ - /* We don't do dynamic PCI IRQ allocation */ -} - #define HAVE_PCI_MMAP extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, diff --git a/arch/mn10300/include/asm/pci.h b/arch/mn10300/include/asm/pci.h index 16632382468..5f70af25c7d 100644 --- a/arch/mn10300/include/asm/pci.h +++ b/arch/mn10300/include/asm/pci.h @@ -48,7 +48,6 @@ extern void unit_pci_init(void); #define PCIBIOS_MIN_MEM 0xB8000000 void pcibios_set_master(struct pci_dev *dev); -void pcibios_penalize_isa_irq(int irq); /* Dynamic DMA mapping stuff. * i386 has everything mapped statically. diff --git a/arch/mn10300/unit-asb2305/pci-irq.c b/arch/mn10300/unit-asb2305/pci-irq.c index 77439da0467..fcb28ceb824 100644 --- a/arch/mn10300/unit-asb2305/pci-irq.c +++ b/arch/mn10300/unit-asb2305/pci-irq.c @@ -40,10 +40,6 @@ void __init pcibios_fixup_irqs(void) } } -void __init pcibios_penalize_isa_irq(int irq) -{ -} - void pcibios_enable_irq(struct pci_dev *dev) { pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); diff --git a/arch/parisc/include/asm/pci.h b/arch/parisc/include/asm/pci.h index 465154076d2..20df2b04fc0 100644 --- a/arch/parisc/include/asm/pci.h +++ b/arch/parisc/include/asm/pci.h @@ -215,11 +215,6 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev, } #endif -static inline void pcibios_penalize_isa_irq(int irq, int active) -{ - /* We don't need to penalize isa irq's */ -} - static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) { return channel ? 15 : 14; diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 95145a15c70..1b0739bc14b 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -46,11 +46,6 @@ struct pci_dev; #define pcibios_assign_all_busses() \ (pci_has_flag(PCI_REASSIGN_ALL_BUS)) -static inline void pcibios_penalize_isa_irq(int irq, int active) -{ - /* We don't do dynamic PCI IRQ allocation */ -} - #define HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) { diff --git a/arch/sh/include/asm/pci.h b/arch/sh/include/asm/pci.h index bff96c2e7d2..5b451155299 100644 --- a/arch/sh/include/asm/pci.h +++ b/arch/sh/include/asm/pci.h @@ -70,11 +70,6 @@ extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); extern void pcibios_set_master(struct pci_dev *dev); -static inline void pcibios_penalize_isa_irq(int irq, int active) -{ - /* We don't do dynamic PCI IRQ allocation */ -} - /* Dynamic DMA mapping stuff. * SuperH has everything mapped statically like x86. */ diff --git a/arch/sparc/include/asm/pci_32.h b/arch/sparc/include/asm/pci_32.h index dc503297481..53e9b4987db 100644 --- a/arch/sparc/include/asm/pci_32.h +++ b/arch/sparc/include/asm/pci_32.h @@ -16,11 +16,6 @@ #define PCI_IRQ_NONE 0xffffffff -static inline void pcibios_penalize_isa_irq(int irq, int active) -{ - /* We don't do dynamic PCI IRQ allocation */ -} - /* Dynamic DMA mapping stuff. */ #define PCI_DMA_BUS_IS_PHYS (0) diff --git a/arch/sparc/include/asm/pci_64.h b/arch/sparc/include/asm/pci_64.h index 1633b718d3b..c6c7396e762 100644 --- a/arch/sparc/include/asm/pci_64.h +++ b/arch/sparc/include/asm/pci_64.h @@ -16,11 +16,6 @@ #define PCI_IRQ_NONE 0xffffffff -static inline void pcibios_penalize_isa_irq(int irq, int active) -{ - /* We don't do dynamic PCI IRQ allocation */ -} - /* The PCI address space does not equal the physical memory * address space. The networking and block device layers use * this boolean for bounce buffer decisions. diff --git a/arch/unicore32/include/asm/pci.h b/arch/unicore32/include/asm/pci.h index f5e108f4a15..654407e9861 100644 --- a/arch/unicore32/include/asm/pci.h +++ b/arch/unicore32/include/asm/pci.h @@ -18,11 +18,6 @@ #include #include /* for PCIBIOS_MIN_* */ -static inline void pcibios_penalize_isa_irq(int irq, int active) -{ - /* We don't do dynamic PCI IRQ allocation */ -} - #ifdef CONFIG_PCI static inline void pci_dma_burst_advice(struct pci_dev *pdev, enum pci_dma_burst_strategy *strat, diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 96ae4f4040b..0892ea0e683 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -68,7 +68,6 @@ void pcibios_config_init(void); void pcibios_scan_root(int bus); void pcibios_set_master(struct pci_dev *dev); -void pcibios_penalize_isa_irq(int irq, int active); struct irq_routing_table *pcibios_get_irq_routing_table(void); int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq); diff --git a/arch/xtensa/include/asm/pci.h b/arch/xtensa/include/asm/pci.h index 614be031a79..5d52dc43dfe 100644 --- a/arch/xtensa/include/asm/pci.h +++ b/arch/xtensa/include/asm/pci.h @@ -22,11 +22,6 @@ extern struct pci_controller* pcibios_alloc_controller(void); -static inline void pcibios_penalize_isa_irq(int irq) -{ - /* We don't do dynamic PCI IRQ allocation */ -} - /* Assume some values. (We should revise them, if necessary) */ #define PCIBIOS_MIN_IO 0x2000 diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 39012831867..11f24912523 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1468,6 +1468,17 @@ void __weak pcibios_release_device(struct pci_dev *dev) {} */ void __weak pcibios_disable_device (struct pci_dev *dev) {} +/** + * pcibios_penalize_isa_irq - penalize an ISA IRQ + * @irq: ISA IRQ to penalize + * @active: IRQ active or not + * + * Permits the platform to provide architecture-specific functionality when + * penalizing ISA IRQs. This is the default implementation. Architecture + * implementations can override this. + */ +void __weak pcibios_penalize_isa_irq(int irq, int active) {} + static void do_pci_disable_device(struct pci_dev *dev) { u16 pci_command; diff --git a/include/linux/pci.h b/include/linux/pci.h index 84182b153b2..018877b8b4e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1578,6 +1578,7 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state); int pcibios_add_device(struct pci_dev *dev); void pcibios_release_device(struct pci_dev *dev); +void pcibios_penalize_isa_irq(int irq, int active); #ifdef CONFIG_HIBERNATE_CALLBACKS extern struct dev_pm_ops pcibios_pm_ops; -- cgit v1.2.3-70-g09d2 From d97ffe236894856d08146390ef3fbe6448a8ac2b Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Wed, 21 May 2014 15:23:30 +1000 Subject: PCI: Fix return value from pci_user_{read,write}_config_*() The PCI user-space config accessors pci_user_{read,write}_config_*() return negative error numbers, which were introduced by commit 34e3207205ef ("PCI: handle positive error codes"). That patch converted all positive error numbers from platform-specific PCI config accessors to -EINVAL, which means the callers don't know anything about the specific cause of the failure. The patch fixes the issue by converting the positive PCIBIOS_* error values to generic negative error numbers with pcibios_err_to_errno(). [bhelgaas: changelog] Signed-off-by: Gavin Shan Signed-off-by: Bjorn Helgaas Acked-by: Greg Thelen --- drivers/pci/access.c | 12 ++++-------- include/linux/pci.h | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) (limited to 'include/linux/pci.h') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 7f8b78c0887..8c148f39e8d 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -148,7 +148,7 @@ static noinline void pci_wait_cfg(struct pci_dev *dev) int pci_user_read_config_##size \ (struct pci_dev *dev, int pos, type *val) \ { \ - int ret = 0; \ + int ret = PCIBIOS_SUCCESSFUL; \ u32 data = -1; \ if (PCI_##size##_BAD) \ return -EINVAL; \ @@ -159,9 +159,7 @@ int pci_user_read_config_##size \ pos, sizeof(type), &data); \ raw_spin_unlock_irq(&pci_lock); \ *val = (type)data; \ - if (ret > 0) \ - ret = -EINVAL; \ - return ret; \ + return pcibios_err_to_errno(ret); \ } \ EXPORT_SYMBOL_GPL(pci_user_read_config_##size); @@ -170,7 +168,7 @@ EXPORT_SYMBOL_GPL(pci_user_read_config_##size); int pci_user_write_config_##size \ (struct pci_dev *dev, int pos, type val) \ { \ - int ret = -EIO; \ + int ret = PCIBIOS_SUCCESSFUL; \ if (PCI_##size##_BAD) \ return -EINVAL; \ raw_spin_lock_irq(&pci_lock); \ @@ -179,9 +177,7 @@ int pci_user_write_config_##size \ ret = dev->bus->ops->write(dev->bus, dev->devfn, \ pos, sizeof(type), val); \ raw_spin_unlock_irq(&pci_lock); \ - if (ret > 0) \ - ret = -EINVAL; \ - return ret; \ + return pcibios_err_to_errno(ret); \ } \ EXPORT_SYMBOL_GPL(pci_user_write_config_##size); diff --git a/include/linux/pci.h b/include/linux/pci.h index 018877b8b4e..322335aaa7e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -518,7 +518,7 @@ static inline int pcibios_err_to_errno(int err) case PCIBIOS_FUNC_NOT_SUPPORTED: return -ENOENT; case PCIBIOS_BAD_VENDOR_ID: - return -EINVAL; + return -ENOTTY; case PCIBIOS_DEVICE_NOT_FOUND: return -ENODEV; case PCIBIOS_BAD_REGISTER_NUMBER: @@ -529,7 +529,7 @@ static inline int pcibios_err_to_errno(int err) return -ENOSPC; } - return -ENOTTY; + return -ERANGE; } /* Low-level architecture-dependent routines */ -- cgit v1.2.3-70-g09d2 From c25dc82899e67a32fdcfb20dd72a37fc236fde2e Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 22 May 2014 17:07:30 -0600 Subject: PCI: Add DMA alias iterator In a mixed PCI/PCI-X/PCIe topology, bridges can take ownership of transactions, replacing the original requester ID with their own. Sometimes we just want to know the resulting device or resulting alias; other times we want each step in the chain. This iterator allows either usage. When an endpoint is connected via an unbroken chain of PCIe switches and root ports, it has no alias and its requester ID is visible to the root bus. When PCI/X get in the way, we pick up aliases for bridges. The reason why we potentially care about each step in the path is because of PCI-X. PCI-X has the concept of a requester ID, but bridges may or may not take ownership of various types of transactions. We therefore leave it to the consumer of this function to prune out what they don't care about rather than attempt to flatten the alias ourselves. Tested-by: George Spelvin Tested-by: Pat Erley Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/search.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 4 +++ 2 files changed, 74 insertions(+) (limited to 'include/linux/pci.h') diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 4a1b972efe7..5601cdb8bbb 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -17,6 +17,76 @@ DECLARE_RWSEM(pci_bus_sem); EXPORT_SYMBOL_GPL(pci_bus_sem); +/* + * pci_for_each_dma_alias - Iterate over DMA aliases for a device + * @pdev: starting downstream device + * @fn: function to call for each alias + * @data: opaque data to pass to @fn + * + * Starting @pdev, walk up the bus calling @fn for each possible alias + * of @pdev at the root bus. + */ +int pci_for_each_dma_alias(struct pci_dev *pdev, + int (*fn)(struct pci_dev *pdev, + u16 alias, void *data), void *data) +{ + struct pci_bus *bus; + int ret; + + ret = fn(pdev, PCI_DEVID(pdev->bus->number, pdev->devfn), data); + if (ret) + return ret; + + for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) { + struct pci_dev *tmp; + + /* Skip virtual buses */ + if (!bus->self) + continue; + + tmp = bus->self; + + /* + * PCIe-to-PCI/X bridges alias transactions from downstream + * devices using the subordinate bus number (PCI Express to + * PCI/PCI-X Bridge Spec, rev 1.0, sec 2.3). For all cases + * where the upstream bus is PCI/X we alias to the bridge + * (there are various conditions in the previous reference + * where the bridge may take ownership of transactions, even + * when the secondary interface is PCI-X). + */ + if (pci_is_pcie(tmp)) { + switch (pci_pcie_type(tmp)) { + case PCI_EXP_TYPE_ROOT_PORT: + case PCI_EXP_TYPE_UPSTREAM: + case PCI_EXP_TYPE_DOWNSTREAM: + continue; + case PCI_EXP_TYPE_PCI_BRIDGE: + ret = fn(tmp, + PCI_DEVID(tmp->subordinate->number, + PCI_DEVFN(0, 0)), data); + if (ret) + return ret; + continue; + case PCI_EXP_TYPE_PCIE_BRIDGE: + ret = fn(tmp, + PCI_DEVID(tmp->bus->number, + tmp->devfn), data); + if (ret) + return ret; + continue; + } + } else { + ret = fn(tmp, PCI_DEVID(tmp->bus->number, tmp->devfn), + data); + if (ret) + return ret; + } + } + + return ret; +} + /* * find the upstream PCIe-to-PCI bridge of a PCI device * if the device is PCIE, return NULL diff --git a/include/linux/pci.h b/include/linux/pci.h index aab57b4abe7..14b074bbc84 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1795,6 +1795,10 @@ static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev) } #endif +int pci_for_each_dma_alias(struct pci_dev *pdev, + int (*fn)(struct pci_dev *pdev, + u16 alias, void *data), void *data); + /** * pci_find_upstream_pcie_bridge - find upstream PCIe-to-PCI bridge of a device * @pdev: the PCI device -- cgit v1.2.3-70-g09d2 From 6b121592f8a3fd2bd0de128637b76a0d0864d993 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 22 May 2014 17:07:36 -0600 Subject: PCI: Convert pci_dev_flags definitions to bit shifts Convert the pci_dev_flags definitions from decimal constants to bit shifts. We're only a few entries away from where using the decimal value becomes cumbersome. No functional change. Tested-by: George Spelvin Tested-by: Pat Erley Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux/pci.h') diff --git a/include/linux/pci.h b/include/linux/pci.h index 14b074bbc84..545903df00d 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -164,13 +164,13 @@ enum pci_dev_flags { /* INTX_DISABLE in PCI_COMMAND register disables MSI * generation too. */ - PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) 1, + PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) (1 << 0), /* Device configuration is irrevocably lost if disabled into D3 */ - PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2, + PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) (1 << 1), /* Provide indication device is assigned by a Virtual Machine Manager */ - PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) 4, + PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) (1 << 2), /* Flag for quirk use to store if quirk-specific ACS is enabled */ - PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) 8, + PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) (1 << 3), }; enum pci_irq_reroute_variant { -- cgit v1.2.3-70-g09d2 From 31c2b8153c58f11ddb80dfd392c16f13c2d709c6 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 22 May 2014 17:07:43 -0600 Subject: PCI: Add support for DMA alias quirks Some devices are broken and use a requester ID other than their physical devfn. Add a byte, using an existing gap in the pci_dev structure, to store an alternate "alias" devfn. A bit in the dev_flags tells us when this is valid. We then add the alias as one more step in the pci_for_each_dma_alias() iterator. Tested-by: George Spelvin Tested-by: Pat Erley Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/search.c | 11 +++++++++++ include/linux/pci.h | 3 +++ 2 files changed, 14 insertions(+) (limited to 'include/linux/pci.h') diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 5601cdb8bbb..2c19f3f4062 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -37,6 +37,17 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, if (ret) return ret; + /* + * If the device is broken and uses an alias requester ID for + * DMA, iterate over that too. + */ + if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN)) { + ret = fn(pdev, PCI_DEVID(pdev->bus->number, + pdev->dma_alias_devfn), data); + if (ret) + return ret; + } + for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) { struct pci_dev *tmp; diff --git a/include/linux/pci.h b/include/linux/pci.h index 545903df00d..9d4035c276f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -171,6 +171,8 @@ enum pci_dev_flags { PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) (1 << 2), /* Flag for quirk use to store if quirk-specific ACS is enabled */ PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) (1 << 3), + /* Flag to indicate the device uses dma_alias_devfn */ + PCI_DEV_FLAGS_DMA_ALIAS_DEVFN = (__force pci_dev_flags_t) (1 << 4), }; enum pci_irq_reroute_variant { @@ -268,6 +270,7 @@ struct pci_dev { u8 rom_base_reg; /* which config register controls the ROM */ u8 pin; /* which interrupt pin this device uses */ u16 pcie_flags_reg; /* cached PCIe Capabilities Register */ + u8 dma_alias_devfn;/* devfn of DMA alias, if any */ struct pci_driver *driver; /* which driver has allocated this device */ u64 dma_mask; /* Mask of the bits of bus address this -- cgit v1.2.3-70-g09d2 From c8fe16e3f96a9bb95a10cedb19d2be2d2d580940 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 28 May 2014 14:57:02 -0600 Subject: PCI: Add support for PCIe-to-PCI bridge DMA alias quirks Several PCIe-to-PCI bridges fail to provide a PCIe capability, causing us to handle them as conventional PCI devices when they really use the requester ID of the secondary bus. We need to differentiate these from PCIe-to-PCI bridges that actually use the conventional PCI ID when a PCIe capability is not present, such as those found on the root complex of may Intel chipsets. Add a dev_flag bit to identify devices to be handled as standard PCIe-to-PCI bridges. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/search.c | 10 ++++++++-- include/linux/pci.h | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include/linux/pci.h') diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 2c19f3f4062..df38f73f091 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -88,8 +88,14 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, continue; } } else { - ret = fn(tmp, PCI_DEVID(tmp->bus->number, tmp->devfn), - data); + if (tmp->dev_flags & PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS) + ret = fn(tmp, + PCI_DEVID(tmp->subordinate->number, + PCI_DEVFN(0, 0)), data); + else + ret = fn(tmp, + PCI_DEVID(tmp->bus->number, + tmp->devfn), data); if (ret) return ret; } diff --git a/include/linux/pci.h b/include/linux/pci.h index 9d4035c276f..85ab35e974a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -173,6 +173,8 @@ enum pci_dev_flags { PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) (1 << 3), /* Flag to indicate the device uses dma_alias_devfn */ PCI_DEV_FLAGS_DMA_ALIAS_DEVFN = (__force pci_dev_flags_t) (1 << 4), + /* Use a PCIe-to-PCI bridge alias even if !pci_is_pcie */ + PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS = (__force pci_dev_flags_t) (1 << 5), }; enum pci_irq_reroute_variant { -- cgit v1.2.3-70-g09d2 From 782a985d7af26db39e86070d28f987cad21313c0 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 20 May 2014 08:53:21 -0600 Subject: PCI: Introduce new device binding path using pci_dev.driver_override The driver_override field allows us to specify the driver for a device rather than relying on the driver to provide a positive match of the device. This shortcuts the existing process of looking up the vendor and device ID, adding them to the driver new_id, binding the device, then removing the ID, but it also provides a couple advantages. First, the above existing process allows the driver to bind to any device matching the new_id for the window where it's enabled. This is often not desired, such as the case of trying to bind a single device to a meta driver like pci-stub or vfio-pci. Using driver_override we can do this deterministically using: echo pci-stub > /sys/bus/pci/devices/0000:03:00.0/driver_override echo 0000:03:00.0 > /sys/bus/pci/devices/0000:03:00.0/driver/unbind echo 0000:03:00.0 > /sys/bus/pci/drivers_probe Previously we could not invoke drivers_probe after adding a device to new_id for a driver as we get non-deterministic behavior whether the driver we intend or the standard driver will claim the device. Now it becomes a deterministic process, only the driver matching driver_override will probe the device. To return the device to the standard driver, we simply clear the driver_override and reprobe the device: echo > /sys/bus/pci/devices/0000:03:00.0/driver_override echo 0000:03:00.0 > /sys/bus/pci/devices/0000:03:00.0/driver/unbind echo 0000:03:00.0 > /sys/bus/pci/drivers_probe Another advantage to this approach is that we can specify a driver override to force a specific binding or prevent any binding. For instance when an IOMMU group is exposed to userspace through VFIO we require that all devices within that group are owned by VFIO. However, devices can be hot-added into an IOMMU group, in which case we want to prevent the device from binding to any driver (override driver = "none") or perhaps have it automatically bind to vfio-pci. With driver_override it's a simple matter for this field to be set internally when the device is first discovered to prevent driver matches. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas Reviewed-by: Konrad Rzeszutek Wilk Reviewed-by: Alexander Graf Acked-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-pci | 21 +++++++++++++++++ drivers/pci/pci-driver.c | 25 ++++++++++++++++++--- drivers/pci/pci-sysfs.c | 40 +++++++++++++++++++++++++++++++++ drivers/pci/probe.c | 1 + include/linux/pci.h | 1 + 5 files changed, 85 insertions(+), 3 deletions(-) (limited to 'include/linux/pci.h') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index a3c5a668503..898ddc4440e 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -250,3 +250,24 @@ Description: valid. For example, writing a 2 to this file when sriov_numvfs is not 0 and not 2 already will return an error. Writing a 10 when the value of sriov_totalvfs is 8 will return an error. + +What: /sys/bus/pci/devices/.../driver_override +Date: April 2014 +Contact: Alex Williamson +Description: + This file allows the driver for a device to be specified which + will override standard static and dynamic ID matching. When + specified, only a driver with a name matching the value written + to driver_override will have an opportunity to bind to the + device. The override is specified by writing a string to the + driver_override file (echo pci-stub > driver_override) and + may be cleared with an empty string (echo > driver_override). + This returns the device to standard matching rules binding. + Writing to driver_override does not automatically unbind the + device from its current driver or make any attempt to + automatically load the specified driver. If no driver with a + matching name is currently loaded in the kernel, the device + will not bind to any driver. This also allows devices to + opt-out of driver binding using a driver_override name such as + "none". Only a single driver may be specified in the override, + there is no support for parsing delimiters. diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index d911e0c1f35..4393c12e913 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -216,6 +216,13 @@ const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, return NULL; } +static const struct pci_device_id pci_device_id_any = { + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, +}; + /** * pci_match_device - Tell if a PCI device structure has a matching PCI device id structure * @drv: the PCI driver to match against @@ -229,18 +236,30 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv, struct pci_dev *dev) { struct pci_dynid *dynid; + const struct pci_device_id *found_id = NULL; + + /* When driver_override is set, only bind to the matching driver */ + if (dev->driver_override && strcmp(dev->driver_override, drv->name)) + return NULL; /* Look at the dynamic ids first, before the static ones */ spin_lock(&drv->dynids.lock); list_for_each_entry(dynid, &drv->dynids.list, node) { if (pci_match_one_device(&dynid->id, dev)) { - spin_unlock(&drv->dynids.lock); - return &dynid->id; + found_id = &dynid->id; + break; } } spin_unlock(&drv->dynids.lock); - return pci_match_id(drv->id_table, dev); + if (!found_id) + found_id = pci_match_id(drv->id_table, dev); + + /* driver_override will always match, send a dummy id */ + if (!found_id && dev->driver_override) + found_id = &pci_device_id_any; + + return found_id; } struct drv_dev_and_id { diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 4e0acefb756..faa4ab554d6 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -499,6 +499,45 @@ static struct device_attribute sriov_numvfs_attr = sriov_numvfs_show, sriov_numvfs_store); #endif /* CONFIG_PCI_IOV */ +static ssize_t driver_override_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + char *driver_override, *old = pdev->driver_override, *cp; + + if (count > PATH_MAX) + return -EINVAL; + + driver_override = kstrndup(buf, count, GFP_KERNEL); + if (!driver_override) + return -ENOMEM; + + cp = strchr(driver_override, '\n'); + if (cp) + *cp = '\0'; + + if (strlen(driver_override)) { + pdev->driver_override = driver_override; + } else { + kfree(driver_override); + pdev->driver_override = NULL; + } + + kfree(old); + + return count; +} + +static ssize_t driver_override_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + return sprintf(buf, "%s\n", pdev->driver_override); +} +static DEVICE_ATTR_RW(driver_override); + static struct attribute *pci_dev_attrs[] = { &dev_attr_resource.attr, &dev_attr_vendor.attr, @@ -521,6 +560,7 @@ static struct attribute *pci_dev_attrs[] = { #if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) &dev_attr_d3cold_allowed.attr, #endif + &dev_attr_driver_override.attr, NULL, }; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ef09f5f2fe6..54268de45f5 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1215,6 +1215,7 @@ static void pci_release_dev(struct device *dev) pci_release_of_node(pci_dev); pcibios_release_device(pci_dev); pci_bus_put(pci_dev->bus); + kfree(pci_dev->driver_override); kfree(pci_dev); } diff --git a/include/linux/pci.h b/include/linux/pci.h index aab57b4abe7..b72af276f59 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -365,6 +365,7 @@ struct pci_dev { #endif phys_addr_t rom; /* Physical address of ROM if it's not from the BAR */ size_t romlen; /* Length of ROM if it's not from the BAR */ + char *driver_override; /* Driver name to force a match */ }; static inline struct pci_dev *pci_physfn(struct pci_dev *dev) -- cgit v1.2.3-70-g09d2 From c893d133eaccdda2516a3e71cd05a7dac2e14b00 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Fri, 30 May 2014 11:01:03 +0800 Subject: PCI: Make pci_bus_add_device() void pci_bus_add_device() always returns 0, so there's no point in returning anything at all. Make it a void function and remove the tests of the return value from the callers. [bhelgaas: changelog, remove unused "err" from i82875p_setup_overfl_dev()] Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/edac/i82875p_edac.c | 8 +------- drivers/pci/bus.c | 10 ++-------- drivers/pci/iov.c | 2 +- drivers/platform/x86/asus-wmi.c | 3 +-- drivers/platform/x86/eeepc-laptop.c | 3 +-- include/linux/pci.h | 2 +- 6 files changed, 7 insertions(+), 21 deletions(-) (limited to 'include/linux/pci.h') diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index 8d0450b9b9a..4009077c883 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -275,7 +275,6 @@ static int i82875p_setup_overfl_dev(struct pci_dev *pdev, { struct pci_dev *dev; void __iomem *window; - int err; *ovrfl_pdev = NULL; *ovrfl_window = NULL; @@ -293,12 +292,7 @@ static int i82875p_setup_overfl_dev(struct pci_dev *pdev, if (dev == NULL) return 1; - err = pci_bus_add_device(dev); - if (err) { - i82875p_printk(KERN_ERR, - "%s(): pci_bus_add_device() Failed\n", - __func__); - } + pci_bus_add_device(dev); pci_bus_assign_resources(dev->bus); } diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index ba2bf55a38d..447d393725e 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -235,7 +235,7 @@ void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { } * * This adds add sysfs entries and start device drivers */ -int pci_bus_add_device(struct pci_dev *dev) +void pci_bus_add_device(struct pci_dev *dev) { int retval; @@ -252,8 +252,6 @@ int pci_bus_add_device(struct pci_dev *dev) WARN_ON(retval < 0); dev->is_added = 1; - - return 0; } /** @@ -266,16 +264,12 @@ void pci_bus_add_devices(const struct pci_bus *bus) { struct pci_dev *dev; struct pci_bus *child; - int retval; list_for_each_entry(dev, &bus->devices, bus_list) { /* Skip already-added devices */ if (dev->is_added) continue; - retval = pci_bus_add_device(dev); - if (retval) - dev_err(&dev->dev, "Error adding device (%d)\n", - retval); + pci_bus_add_device(dev); } list_for_each_entry(dev, &bus->devices, bus_list) { diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index de7a74782f9..cb6f24740ee 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -106,7 +106,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) pci_device_add(virtfn, virtfn->bus); mutex_unlock(&iov->dev->sriov->lock); - rc = pci_bus_add_device(virtfn); + pci_bus_add_device(virtfn); sprintf(buf, "virtfn%u", id); rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); if (rc) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index c5e082fb82f..91ef69a5226 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -642,8 +642,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus) dev = pci_scan_single_device(bus, 0); if (dev) { pci_bus_assign_resources(bus); - if (pci_bus_add_device(dev)) - pr_err("Unable to hotplug wifi\n"); + pci_bus_add_device(dev); } } else { dev = pci_get_slot(bus, 0); diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 399e8c56219..9b0c57cd1d4 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -633,8 +633,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle) dev = pci_scan_single_device(bus, 0); if (dev) { pci_bus_assign_resources(bus); - if (pci_bus_add_device(dev)) - pr_err("Unable to hotplug wifi\n"); + pci_bus_add_device(dev); } } else { dev = pci_get_slot(bus, 0); diff --git a/include/linux/pci.h b/include/linux/pci.h index 322335aaa7e..785149a6aec 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -764,7 +764,7 @@ int pci_scan_slot(struct pci_bus *bus, int devfn); struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn); void pci_device_add(struct pci_dev *dev, struct pci_bus *bus); unsigned int pci_scan_child_bus(struct pci_bus *bus); -int __must_check pci_bus_add_device(struct pci_dev *dev); +void pci_bus_add_device(struct pci_dev *dev); void pci_read_bridge_bases(struct pci_bus *child); struct resource *pci_find_parent_resource(const struct pci_dev *dev, struct resource *res); -- cgit v1.2.3-70-g09d2