From 6f9a22bc5775d231ab8fbe2c2f3c88e45e3e7c28 Mon Sep 17 00:00:00 2001 From: Michael Hernandez Date: Thu, 18 May 2017 10:47:47 -0700 Subject: PCI/MSI: Ignore affinity if pre/post vector count is more than min_vecs min_vecs is the minimum amount of vectors needed to operate in MSI-X mode which may just include the vectors that don't need affinity. Disabling affinity settings causes the qla2xxx driver scsi_add_host() to fail when blk_mq is enabled as the blk_mq_pci_map_queues() expects affinity masks on each vector. Fixes: dfef358bd1be ("PCI/MSI: Don't apply affinity if there aren't enough vectors left") Signed-off-by: Michael Hernandez Signed-off-by: Himanshu Madhani Signed-off-by: Bjorn Helgaas Reviewed-by: Christoph Hellwig Cc: stable@vger.kernel.org # v4.10+ --- include/linux/interrupt.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index a6fba4804672..0991f973f8ca 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -291,7 +291,7 @@ extern int irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify); struct cpumask *irq_create_affinity_masks(int nvec, const struct irq_affinity *affd); -int irq_calc_affinity_vectors(int maxvec, const struct irq_affinity *affd); +int irq_calc_affinity_vectors(int minvec, int maxvec, const struct irq_affinity *affd); #else /* CONFIG_SMP */ @@ -331,7 +331,7 @@ irq_create_affinity_masks(int nvec, const struct irq_affinity *affd) } static inline int -irq_calc_affinity_vectors(int maxvec, const struct irq_affinity *affd) +irq_calc_affinity_vectors(int minvec, int maxvec, const struct irq_affinity *affd) { return maxvec; } -- cgit v1.2.3-71-gd317 From a4f4fa681add289ebfec6d776376ad7a2ffda669 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Tue, 30 May 2017 09:25:48 -0700 Subject: PCI: Cache PRI and PASID bits in pci_dev Device drivers need to check if an IOMMU enabled ATS, PRI and PASID in order to know when they can use the SVM API. Cache PRI and PASID bits in the pci_dev structure, similarly to what is currently done for ATS. Signed-off-by: Jean-Philippe Brucker Signed-off-by: Bjorn Helgaas --- drivers/pci/ats.c | 23 +++++++++++++++++++++++ include/linux/pci.h | 2 ++ 2 files changed, 25 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index eeb9fb2b47aa..21264976fa13 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -153,6 +153,9 @@ int pci_enable_pri(struct pci_dev *pdev, u32 reqs) u32 max_requests; int pos; + if (WARN_ON(pdev->pri_enabled)) + return -EBUSY; + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); if (!pos) return -EINVAL; @@ -170,6 +173,8 @@ int pci_enable_pri(struct pci_dev *pdev, u32 reqs) control |= PCI_PRI_CTRL_ENABLE; pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control); + pdev->pri_enabled = 1; + return 0; } EXPORT_SYMBOL_GPL(pci_enable_pri); @@ -185,6 +190,9 @@ void pci_disable_pri(struct pci_dev *pdev) u16 control; int pos; + if (WARN_ON(!pdev->pri_enabled)) + return; + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); if (!pos) return; @@ -192,6 +200,8 @@ void pci_disable_pri(struct pci_dev *pdev) pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); control &= ~PCI_PRI_CTRL_ENABLE; pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control); + + pdev->pri_enabled = 0; } EXPORT_SYMBOL_GPL(pci_disable_pri); @@ -207,6 +217,9 @@ int pci_reset_pri(struct pci_dev *pdev) u16 control; int pos; + if (WARN_ON(pdev->pri_enabled)) + return -EBUSY; + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); if (!pos) return -EINVAL; @@ -239,6 +252,9 @@ int pci_enable_pasid(struct pci_dev *pdev, int features) u16 control, supported; int pos; + if (WARN_ON(pdev->pasid_enabled)) + return -EBUSY; + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID); if (!pos) return -EINVAL; @@ -259,6 +275,8 @@ int pci_enable_pasid(struct pci_dev *pdev, int features) pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control); + pdev->pasid_enabled = 1; + return 0; } EXPORT_SYMBOL_GPL(pci_enable_pasid); @@ -273,11 +291,16 @@ void pci_disable_pasid(struct pci_dev *pdev) u16 control = 0; int pos; + if (WARN_ON(!pdev->pasid_enabled)) + return; + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID); if (!pos) return; pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control); + + pdev->pasid_enabled = 0; } EXPORT_SYMBOL_GPL(pci_disable_pasid); diff --git a/include/linux/pci.h b/include/linux/pci.h index 33c2b0b77429..f612c1d85863 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -356,6 +356,8 @@ struct pci_dev { unsigned int msix_enabled:1; unsigned int ari_enabled:1; /* ARI forwarding */ unsigned int ats_enabled:1; /* Address Translation Service */ + unsigned int pasid_enabled:1; /* Process Address Space ID */ + unsigned int pri_enabled:1; /* Page Request Interface */ unsigned int is_managed:1; unsigned int needs_freset:1; /* Dev requires fundamental reset */ unsigned int state_saved:1; -- cgit v1.2.3-71-gd317 From 4ebeb1ec56d4c54a56b6f43c2603d9a4688c83ba Mon Sep 17 00:00:00 2001 From: CQ Tang Date: Tue, 30 May 2017 09:25:49 -0700 Subject: PCI: Restore PRI and PASID state after Function-Level Reset After a Function-Level Reset, PCI states need to be restored. Save PASID features and PRI reqs cached. [bhelgaas: search for capability only if PRI/PASID were enabled] Signed-off-by: CQ Tang Signed-off-by: Ashok Raj Signed-off-by: Bjorn Helgaas Cc: Joerg Roedel Cc: Jean-Phillipe Brucker Cc: David Woodhouse --- drivers/pci/ats.c | 64 ++++++++++++++++++++++++++++++++++++------------- drivers/pci/pci.c | 3 +++ include/linux/pci-ats.h | 10 ++++++++ include/linux/pci.h | 6 +++++ 4 files changed, 67 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 21264976fa13..ad8ddbbbf245 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -160,17 +160,16 @@ int pci_enable_pri(struct pci_dev *pdev, u32 reqs) if (!pos) return -EINVAL; - pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status); - if ((control & PCI_PRI_CTRL_ENABLE) || - !(status & PCI_PRI_STATUS_STOPPED)) + if (!(status & PCI_PRI_STATUS_STOPPED)) return -EBUSY; pci_read_config_dword(pdev, pos + PCI_PRI_MAX_REQ, &max_requests); reqs = min(max_requests, reqs); + pdev->pri_reqs_alloc = reqs; pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ, reqs); - control |= PCI_PRI_CTRL_ENABLE; + control = PCI_PRI_CTRL_ENABLE; pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control); pdev->pri_enabled = 1; @@ -205,6 +204,28 @@ void pci_disable_pri(struct pci_dev *pdev) } EXPORT_SYMBOL_GPL(pci_disable_pri); +/** + * pci_restore_pri_state - Restore PRI + * @pdev: PCI device structure + */ +void pci_restore_pri_state(struct pci_dev *pdev) +{ + u16 control = PCI_PRI_CTRL_ENABLE; + u32 reqs = pdev->pri_reqs_alloc; + int pos; + + if (!pdev->pri_enabled) + return; + + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); + if (!pos) + return; + + pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ, reqs); + pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control); +} +EXPORT_SYMBOL_GPL(pci_restore_pri_state); + /** * pci_reset_pri - Resets device's PRI state * @pdev: PCI device structure @@ -224,12 +245,7 @@ int pci_reset_pri(struct pci_dev *pdev) if (!pos) return -EINVAL; - pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); - if (control & PCI_PRI_CTRL_ENABLE) - return -EBUSY; - - control |= PCI_PRI_CTRL_RESET; - + control = PCI_PRI_CTRL_RESET; pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control); return 0; @@ -259,12 +275,7 @@ int pci_enable_pasid(struct pci_dev *pdev, int features) if (!pos) return -EINVAL; - pci_read_config_word(pdev, pos + PCI_PASID_CTRL, &control); pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported); - - if (control & PCI_PASID_CTRL_ENABLE) - return -EINVAL; - supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV; /* User wants to enable anything unsupported? */ @@ -272,6 +283,7 @@ int pci_enable_pasid(struct pci_dev *pdev, int features) return -EINVAL; control = PCI_PASID_CTRL_ENABLE | features; + pdev->pasid_features = features; pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control); @@ -284,7 +296,6 @@ EXPORT_SYMBOL_GPL(pci_enable_pasid); /** * pci_disable_pasid - Disable the PASID capability * @pdev: PCI device structure - * */ void pci_disable_pasid(struct pci_dev *pdev) { @@ -304,6 +315,27 @@ void pci_disable_pasid(struct pci_dev *pdev) } EXPORT_SYMBOL_GPL(pci_disable_pasid); +/** + * pci_restore_pasid_state - Restore PASID capabilities + * @pdev: PCI device structure + */ +void pci_restore_pasid_state(struct pci_dev *pdev) +{ + u16 control; + int pos; + + if (!pdev->pasid_enabled) + return; + + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID); + if (!pos) + return; + + control = PCI_PASID_CTRL_ENABLE | pdev->pasid_features; + pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control); +} +EXPORT_SYMBOL_GPL(pci_restore_pasid_state); + /** * pci_pasid_features - Check which PASID features are supported * @pdev: PCI device structure diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b01bd5bba8e6..3b38e98e68df 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -1173,6 +1174,8 @@ void pci_restore_state(struct pci_dev *dev) /* PCI Express register must be restored first */ pci_restore_pcie_state(dev); + pci_restore_pasid_state(dev); + pci_restore_pri_state(dev); pci_restore_ats_state(dev); pci_restore_vc_state(dev); diff --git a/include/linux/pci-ats.h b/include/linux/pci-ats.h index 57e0b8250947..782fb8e0755f 100644 --- a/include/linux/pci-ats.h +++ b/include/linux/pci-ats.h @@ -7,6 +7,7 @@ int pci_enable_pri(struct pci_dev *pdev, u32 reqs); void pci_disable_pri(struct pci_dev *pdev); +void pci_restore_pri_state(struct pci_dev *pdev); int pci_reset_pri(struct pci_dev *pdev); #else /* CONFIG_PCI_PRI */ @@ -20,6 +21,10 @@ static inline void pci_disable_pri(struct pci_dev *pdev) { } +static inline void pci_restore_pri_state(struct pci_dev *pdev) +{ +} + static inline int pci_reset_pri(struct pci_dev *pdev) { return -ENODEV; @@ -31,6 +36,7 @@ static inline int pci_reset_pri(struct pci_dev *pdev) int pci_enable_pasid(struct pci_dev *pdev, int features); void pci_disable_pasid(struct pci_dev *pdev); +void pci_restore_pasid_state(struct pci_dev *pdev); int pci_pasid_features(struct pci_dev *pdev); int pci_max_pasids(struct pci_dev *pdev); @@ -45,6 +51,10 @@ static inline void pci_disable_pasid(struct pci_dev *pdev) { } +static inline void pci_restore_pasid_state(struct pci_dev *pdev) +{ +} + static inline int pci_pasid_features(struct pci_dev *pdev) { return -EINVAL; diff --git a/include/linux/pci.h b/include/linux/pci.h index f612c1d85863..c7cfdff2529c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -400,6 +400,12 @@ struct pci_dev { u16 ats_cap; /* ATS Capability offset */ u8 ats_stu; /* ATS Smallest Translation Unit */ atomic_t ats_ref_cnt; /* number of VFs with ATS enabled */ +#endif +#ifdef CONFIG_PCI_PRI + u32 pri_reqs_alloc; /* Number of PRI requests allocated */ +#endif +#ifdef CONFIG_PCI_PASID + u16 pasid_features; #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 */ -- cgit v1.2.3-71-gd317 From 99b3c58f7ba7fae801e501b45c5fcf6e08d9247f Mon Sep 17 00:00:00 2001 From: Piotr Gregor Date: Fri, 26 May 2017 22:02:25 +0100 Subject: PCI: Test INTx masking during enumeration, not at run-time The test for INTx masking via PCI_COMMAND_INTX_DISABLE performed in pci_intx_mask_supported() should be done before the device can be used. This is to avoid writing PCI_COMMAND while the driver owns the device, in case that has any effect on MSI/MSI-X interrupts. Move the content of pci_intx_mask_supported() to pci_intx_mask_broken() and call it from pci_setup_device(). The test result can be queried at any time later using the same pci_intx_mask_supported() interface as before (though with changed implementation), so callers (uio, vfio) should be unaffected. Signed-off-by: Piotr Gregor [bhelgaas: changelog, remove quirk check, remove locking, move dev->broken_intx_masking assignment to caller] Signed-off-by: Bjorn Helgaas Reviewed-by: Alex Williamson Acked-by: Michael S. Tsirkin --- drivers/pci/pci.c | 42 +----------------------------------------- drivers/pci/probe.c | 30 ++++++++++++++++++++++++++++++ include/linux/pci.h | 12 ++++++++++-- 3 files changed, 41 insertions(+), 43 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b01bd5bba8e6..7c4e1aa67c67 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3708,46 +3708,6 @@ void pci_intx(struct pci_dev *pdev, int enable) } EXPORT_SYMBOL_GPL(pci_intx); -/** - * pci_intx_mask_supported - probe for INTx masking support - * @dev: the PCI device to operate on - * - * Check if the device dev support INTx masking via the config space - * command word. - */ -bool pci_intx_mask_supported(struct pci_dev *dev) -{ - bool mask_supported = false; - u16 orig, new; - - if (dev->broken_intx_masking) - return false; - - pci_cfg_access_lock(dev); - - pci_read_config_word(dev, PCI_COMMAND, &orig); - pci_write_config_word(dev, PCI_COMMAND, - orig ^ PCI_COMMAND_INTX_DISABLE); - pci_read_config_word(dev, PCI_COMMAND, &new); - - /* - * There's no way to protect against hardware bugs or detect them - * reliably, but as long as we know what the value should be, let's - * go ahead and check it. - */ - if ((new ^ orig) & ~PCI_COMMAND_INTX_DISABLE) { - dev_err(&dev->dev, "Command register changed from 0x%x to 0x%x: driver or hardware bug?\n", - orig, new); - } else if ((new ^ orig) & PCI_COMMAND_INTX_DISABLE) { - mask_supported = true; - pci_write_config_word(dev, PCI_COMMAND, orig); - } - - pci_cfg_access_unlock(dev); - return mask_supported; -} -EXPORT_SYMBOL_GPL(pci_intx_mask_supported); - static bool pci_check_and_set_intx_mask(struct pci_dev *dev, bool mask) { struct pci_bus *bus = dev->bus; @@ -3798,7 +3758,7 @@ done: * @dev: the PCI device to operate on * * Check if the device dev has its INTx line asserted, mask it and - * return true in that case. False is returned if not interrupt was + * return true in that case. False is returned if no interrupt was * pending. */ bool pci_check_and_mask_intx(struct pci_dev *dev) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 19c8950c6c38..8b8826b9a398 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1329,6 +1329,34 @@ static void pci_msi_setup_pci_dev(struct pci_dev *dev) pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); } +/** + * pci_intx_mask_broken - test PCI_COMMAND_INTX_DISABLE writability + * @dev: PCI device + * + * Test whether PCI_COMMAND_INTX_DISABLE is writable for @dev. Check this + * at enumeration-time to avoid modifying PCI_COMMAND at run-time. + */ +static int pci_intx_mask_broken(struct pci_dev *dev) +{ + u16 orig, toggle, new; + + pci_read_config_word(dev, PCI_COMMAND, &orig); + toggle = orig ^ PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(dev, PCI_COMMAND, toggle); + pci_read_config_word(dev, PCI_COMMAND, &new); + + pci_write_config_word(dev, PCI_COMMAND, orig); + + /* + * PCI_COMMAND_INTX_DISABLE was reserved and read-only prior to PCI + * r2.3, so strictly speaking, a device is not *broken* if it's not + * writable. But we'll live with the misnomer for now. + */ + if (new != toggle) + return 1; + return 0; +} + /** * pci_setup_device - fill in class and map information of a device * @dev: the device structure to fill @@ -1399,6 +1427,8 @@ int pci_setup_device(struct pci_dev *dev) } } + dev->broken_intx_masking = pci_intx_mask_broken(dev); + switch (dev->hdr_type) { /* header type */ case PCI_HEADER_TYPE_NORMAL: /* standard header */ if (class == PCI_CLASS_BRIDGE_PCI) diff --git a/include/linux/pci.h b/include/linux/pci.h index 33c2b0b77429..4f0613d5d2d9 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -366,7 +366,7 @@ struct pci_dev { unsigned int is_thunderbolt:1; /* Thunderbolt controller */ unsigned int __aer_firmware_first_valid:1; unsigned int __aer_firmware_first:1; - unsigned int broken_intx_masking:1; + unsigned int broken_intx_masking:1; /* INTx masking can't be used */ unsigned int io_window_1k:1; /* Intel P2P bridge 1K I/O windows */ unsigned int irq_managed:1; unsigned int has_secondary_link:1; @@ -1003,6 +1003,15 @@ int __must_check pci_reenable_device(struct pci_dev *); int __must_check pcim_enable_device(struct pci_dev *pdev); void pcim_pin_device(struct pci_dev *pdev); +static inline bool pci_intx_mask_supported(struct pci_dev *pdev) +{ + /* + * INTx masking is supported if PCI_COMMAND_INTX_DISABLE is + * writable and no quirk has marked the feature broken. + */ + return !pdev->broken_intx_masking; +} + static inline int pci_is_enabled(struct pci_dev *pdev) { return (atomic_read(&pdev->enable_cnt) > 0); @@ -1026,7 +1035,6 @@ int __must_check pci_set_mwi(struct pci_dev *dev); int pci_try_set_mwi(struct pci_dev *dev); void pci_clear_mwi(struct pci_dev *dev); void pci_intx(struct pci_dev *dev, int enable); -bool pci_intx_mask_supported(struct pci_dev *dev); bool pci_check_and_mask_intx(struct pci_dev *dev); bool pci_check_and_unmask_intx(struct pci_dev *dev); int pci_wait_for_pending(struct pci_dev *dev, int pos, u16 mask); -- cgit v1.2.3-71-gd317 From dff79b91b8f3279cbe60727368adff1f3a5ab16e Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Wed, 28 Jun 2017 15:13:52 -0500 Subject: PCI: Add pci_free_host_bridge() interface Commit a52d1443bba1 ("PCI: Export host bridge registration interface") exported the pci_alloc_host_bridge() interface so that PCI host controllers drivers can make use of it. Introduce pci_alloc_host_bridge() kernel counterpart to free the host bridge data structures, pci_free_host_bridge(), export it and update kernel functions releasing host bridge objects allocated memory to make use of it. Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Cc: Arnd Bergmann --- drivers/pci/probe.c | 12 +++++++++--- include/linux/pci.h | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 586d83d8be4d..cbf0d0c1b009 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -517,9 +517,7 @@ static void pci_release_host_bridge_dev(struct device *dev) if (bridge->release_fn) bridge->release_fn(bridge); - pci_free_resource_list(&bridge->windows); - - kfree(bridge); + pci_free_host_bridge(bridge); } struct pci_host_bridge *pci_alloc_host_bridge(size_t priv) @@ -537,6 +535,14 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv) } EXPORT_SYMBOL(pci_alloc_host_bridge); +void pci_free_host_bridge(struct pci_host_bridge *bridge) +{ + pci_free_resource_list(&bridge->windows); + + kfree(bridge); +} +EXPORT_SYMBOL(pci_free_host_bridge); + static const unsigned char pcix_bus_speed[] = { PCI_SPEED_UNKNOWN, /* 0 */ PCI_SPEED_66MHz_PCIX, /* 1 */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 33c2b0b77429..9095b38c2fa3 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -458,6 +458,7 @@ static inline struct pci_host_bridge *pci_host_bridge_from_priv(void *priv) } struct pci_host_bridge *pci_alloc_host_bridge(size_t priv); +void pci_free_host_bridge(struct pci_host_bridge *bridge); int pci_register_host_bridge(struct pci_host_bridge *bridge); struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus); -- cgit v1.2.3-71-gd317 From 5c3f18cce08364ef68163228c0b42725d64cd353 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Wed, 28 Jun 2017 15:13:53 -0500 Subject: PCI: Add devm_pci_alloc_host_bridge() interface Struct pci_host_bridge can be allocated by PCI host bridge drivers which usually allocate and map memory through devm managed interfaces. Add a devm version for the pci_alloc_host_bridge() interface to simplify PCI host controller driver porting and simplify the driver failure paths. Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Cc: Arnd Bergmann --- Documentation/driver-model/devres.txt | 1 + drivers/pci/probe.c | 24 ++++++++++++++++++++++-- include/linux/pci.h | 2 ++ 3 files changed, 25 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index e72587fe477d..ffbc8913cc76 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -342,6 +342,7 @@ PER-CPU MEM devm_free_percpu() PCI + devm_pci_alloc_host_bridge() : managed PCI host bridge allocation devm_pci_remap_cfgspace() : ioremap PCI configuration space devm_pci_remap_cfg_resource() : ioremap PCI configuration space resource pcim_enable_device() : after success, all PCI ops become managed diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index cbf0d0c1b009..e3f69655ca87 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -510,14 +510,18 @@ static struct pci_bus *pci_alloc_bus(struct pci_bus *parent) return b; } -static void pci_release_host_bridge_dev(struct device *dev) +static void devm_pci_release_host_bridge_dev(struct device *dev) { struct pci_host_bridge *bridge = to_pci_host_bridge(dev); if (bridge->release_fn) bridge->release_fn(bridge); +} - pci_free_host_bridge(bridge); +static void pci_release_host_bridge_dev(struct device *dev) +{ + devm_pci_release_host_bridge_dev(dev); + pci_free_host_bridge(to_pci_host_bridge(dev)); } struct pci_host_bridge *pci_alloc_host_bridge(size_t priv) @@ -535,6 +539,22 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv) } EXPORT_SYMBOL(pci_alloc_host_bridge); +struct pci_host_bridge *devm_pci_alloc_host_bridge(struct device *dev, + size_t priv) +{ + struct pci_host_bridge *bridge; + + bridge = devm_kzalloc(dev, sizeof(*bridge) + priv, GFP_KERNEL); + if (!bridge) + return NULL; + + INIT_LIST_HEAD(&bridge->windows); + bridge->dev.release = devm_pci_release_host_bridge_dev; + + return bridge; +} +EXPORT_SYMBOL(devm_pci_alloc_host_bridge); + void pci_free_host_bridge(struct pci_host_bridge *bridge) { pci_free_resource_list(&bridge->windows); diff --git a/include/linux/pci.h b/include/linux/pci.h index 9095b38c2fa3..d39a66dc67aa 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -458,6 +458,8 @@ static inline struct pci_host_bridge *pci_host_bridge_from_priv(void *priv) } struct pci_host_bridge *pci_alloc_host_bridge(size_t priv); +struct pci_host_bridge *devm_pci_alloc_host_bridge(struct device *dev, + size_t priv); void pci_free_host_bridge(struct pci_host_bridge *bridge); int pci_register_host_bridge(struct pci_host_bridge *bridge); struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus); -- cgit v1.2.3-71-gd317 From 1228c4b6c19a76a2691cfb1403ad1eebf5852b76 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Wed, 28 Jun 2017 15:13:55 -0500 Subject: PCI: Add pci_scan_root_bus_bridge() interface The current pci_scan_root_bus() interface is made up of two main code paths: - pci_create_root_bus() - pci_scan_child_bus() pci_create_root_bus() is a wrapper function that allows to create a struct pci_host_bridge structure, initialize it with the passed parameters and register it with the kernel. As the struct pci_host_bridge require additional struct members, pci_create_root_bus() parameters list has grown in time, making it unwieldy to add further parameters to it in case the struct pci_host_bridge gains more members fields to augment its functionality. Since PCI core code provides functions to allocate struct pci_host_bridge, instead of forcing the pci_create_root_bus() interface to add new parameters to cater for new struct pci_host_bridge functionality, it is more suitable to add an interface in PCI core code to scan a PCI bus straight from a struct pci_host_bridge created and customized by each specific PCI host controller driver. Add a pci_scan_root_bus_bridge() function to allow PCI host controller drivers to create and initialize struct pci_host_bridge and scan the resulting bus. Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Cc: Arnd Bergmann --- drivers/pci/probe.c | 39 +++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 1 + 2 files changed, 40 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index e3f69655ca87..690f0b377a0f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2426,6 +2426,45 @@ void pci_bus_release_busn_res(struct pci_bus *b) res, ret ? "can not be" : "is"); } +int pci_scan_root_bus_bridge(struct pci_host_bridge *bridge) +{ + struct resource_entry *window; + bool found = false; + struct pci_bus *b; + int max, bus, ret; + + if (!bridge) + return -EINVAL; + + resource_list_for_each_entry(window, &bridge->windows) + if (window->res->flags & IORESOURCE_BUS) { + found = true; + break; + } + + ret = pci_register_host_bridge(bridge); + if (ret < 0) + return ret; + + b = bridge->bus; + bus = bridge->busnr; + + if (!found) { + dev_info(&b->dev, + "No busn resource found for root bus, will use [bus %02x-ff]\n", + bus); + pci_bus_insert_busn_res(b, bus, 255); + } + + max = pci_scan_child_bus(b); + + if (!found) + pci_bus_update_busn_res_end(b, max); + + return 0; +} +EXPORT_SYMBOL(pci_scan_root_bus_bridge); + struct pci_bus *pci_scan_root_bus_msi(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources, struct msi_controller *msi) diff --git a/include/linux/pci.h b/include/linux/pci.h index d39a66dc67aa..fe1eafd637c3 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -857,6 +857,7 @@ struct pci_bus *pci_scan_root_bus_msi(struct device *parent, int bus, struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources); +int pci_scan_root_bus_bridge(struct pci_host_bridge *bridge); struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr); void pcie_update_link_speed(struct pci_bus *bus, u16 link_status); -- cgit v1.2.3-71-gd317 From cea9bc0be624fb0dc488cb10df40be1323b6b758 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Wed, 28 Jun 2017 15:13:55 -0500 Subject: PCI: Make pci_register_host_bridge() PCI core internal With the introduction of pci_scan_root_bus_bridge() there is no need to export pci_register_host_bridge() to other kernel subsystems other than the PCI compilation unit that needs it. Make pci_register_host_bridge() static to its compilation unit and convert the existing drivers usage over to pci_scan_root_bus_bridge(). Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Cc: Arnd Bergmann --- drivers/pci/host/pci-ftpci100.c | 5 ++--- drivers/pci/host/pci-tegra.c | 4 +--- drivers/pci/probe.c | 3 +-- include/linux/pci.h | 1 - 4 files changed, 4 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/host/pci-ftpci100.c b/drivers/pci/host/pci-ftpci100.c index ce5700eec1da..2ee2ffb0a50b 100644 --- a/drivers/pci/host/pci-ftpci100.c +++ b/drivers/pci/host/pci-ftpci100.c @@ -527,14 +527,13 @@ static int faraday_pci_probe(struct platform_device *pdev) return ret; list_splice_init(&res, &host->windows); - ret = pci_register_host_bridge(host); + ret = pci_scan_root_bus_bridge(host); if (ret) { - dev_err(dev, "failed to register host: %d\n", ret); + dev_err(dev, "failed to scan host: %d\n", ret); return ret; } p->bus = host->bus; - pci_scan_child_bus(p->bus); pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); pci_bus_assign_resources(p->bus); pci_bus_add_devices(p->bus); diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 84c98a2bffeb..0383418457be 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -2285,14 +2285,12 @@ static int tegra_pcie_probe(struct platform_device *pdev) host->dev.parent = &pdev->dev; host->ops = &tegra_pcie_ops; - err = pci_register_host_bridge(host); + err = pci_scan_root_bus_bridge(host); if (err < 0) { dev_err(dev, "failed to register host: %d\n", err); goto disable_msi; } - pci_scan_child_bus(host->bus); - pci_fixup_irqs(pci_common_swizzle, tegra_pcie_map_irq); pci_bus_size_bridges(host->bus); pci_bus_assign_resources(host->bus); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 690f0b377a0f..5c457c17cf5c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -746,7 +746,7 @@ static void pci_set_bus_msi_domain(struct pci_bus *bus) dev_set_msi_domain(&bus->dev, d); } -int pci_register_host_bridge(struct pci_host_bridge *bridge) +static int pci_register_host_bridge(struct pci_host_bridge *bridge) { struct device *parent = bridge->dev.parent; struct resource_entry *window, *n; @@ -861,7 +861,6 @@ free: kfree(bus); return err; } -EXPORT_SYMBOL(pci_register_host_bridge); static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr) diff --git a/include/linux/pci.h b/include/linux/pci.h index fe1eafd637c3..b56dc13f47c2 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -461,7 +461,6 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv); struct pci_host_bridge *devm_pci_alloc_host_bridge(struct device *dev, size_t priv); void pci_free_host_bridge(struct pci_host_bridge *bridge); -int pci_register_host_bridge(struct pci_host_bridge *bridge); struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus); void pci_set_host_bridge_release(struct pci_host_bridge *bridge, -- cgit v1.2.3-71-gd317 From 9ee8a1c4a0e232e9b86e03f7c628ff0286444e00 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Wed, 28 Jun 2017 15:14:01 -0500 Subject: PCI: Remove pci_scan_root_bus_msi() The pci_scan_root_bus_bridge() function allows passing a parameterized struct pci_host_bridge and scanning the resulting PCI bus; since the struct msi_controller is part of the struct pci_host_bridge and the struct pci_host_bridge can now be passed to pci_scan_root_bus_bridge() explicitly, there is no need for a scan interface with a MSI controller parameter. With all PCI host controller drivers and platform code relying on pci_scan_root_bus_msi() converted over to pci_scan_root_bus_bridge() the pci_scan_root_bus_msi() becomes obsolete and can be removed. Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 27 +++++---------------------- include/linux/pci.h | 4 ---- 2 files changed, 5 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5c457c17cf5c..bd42ed42c199 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2324,9 +2324,8 @@ void __weak pcibios_remove_bus(struct pci_bus *bus) { } -static struct pci_bus *pci_create_root_bus_msi(struct device *parent, - int bus, struct pci_ops *ops, void *sysdata, - struct list_head *resources, struct msi_controller *msi) +struct pci_bus *pci_create_root_bus(struct device *parent, int bus, + struct pci_ops *ops, void *sysdata, struct list_head *resources) { int error; struct pci_host_bridge *bridge; @@ -2341,7 +2340,6 @@ static struct pci_bus *pci_create_root_bus_msi(struct device *parent, bridge->sysdata = sysdata; bridge->busnr = bus; bridge->ops = ops; - bridge->msi = msi; error = pci_register_host_bridge(bridge); if (error < 0) @@ -2353,13 +2351,6 @@ err_out: kfree(bridge); return NULL; } - -struct pci_bus *pci_create_root_bus(struct device *parent, int bus, - struct pci_ops *ops, void *sysdata, struct list_head *resources) -{ - return pci_create_root_bus_msi(parent, bus, ops, sysdata, resources, - NULL); -} EXPORT_SYMBOL_GPL(pci_create_root_bus); int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) @@ -2464,9 +2455,8 @@ int pci_scan_root_bus_bridge(struct pci_host_bridge *bridge) } EXPORT_SYMBOL(pci_scan_root_bus_bridge); -struct pci_bus *pci_scan_root_bus_msi(struct device *parent, int bus, - struct pci_ops *ops, void *sysdata, - struct list_head *resources, struct msi_controller *msi) +struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, + struct pci_ops *ops, void *sysdata, struct list_head *resources) { struct resource_entry *window; bool found = false; @@ -2479,7 +2469,7 @@ struct pci_bus *pci_scan_root_bus_msi(struct device *parent, int bus, break; } - b = pci_create_root_bus_msi(parent, bus, ops, sysdata, resources, msi); + b = pci_create_root_bus(parent, bus, ops, sysdata, resources); if (!b) return NULL; @@ -2497,13 +2487,6 @@ struct pci_bus *pci_scan_root_bus_msi(struct device *parent, int bus, return b; } - -struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, - struct pci_ops *ops, void *sysdata, struct list_head *resources) -{ - return pci_scan_root_bus_msi(parent, bus, ops, sysdata, resources, - NULL); -} EXPORT_SYMBOL(pci_scan_root_bus); struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops, diff --git a/include/linux/pci.h b/include/linux/pci.h index b56dc13f47c2..9022b542556a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -849,10 +849,6 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int busmax); int pci_bus_update_busn_res_end(struct pci_bus *b, int busmax); void pci_bus_release_busn_res(struct pci_bus *b); -struct pci_bus *pci_scan_root_bus_msi(struct device *parent, int bus, - struct pci_ops *ops, void *sysdata, - struct list_head *resources, - struct msi_controller *msi); struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources); -- cgit v1.2.3-71-gd317 From 3aa8a41e0bf5565946082d23ae589c1a8559494a Mon Sep 17 00:00:00 2001 From: Matthew Minter Date: Wed, 28 Jun 2017 15:14:02 -0500 Subject: PCI: Add IRQ mapping function pointers to pci_host_bridge struct In order to defer IRQ assignment arches must be able to register functions to map and swizzle interrupts. These registered functions are stored in the pci_host_bridge struct. Signed-off-by: Matthew Minter Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pci.h b/include/linux/pci.h index 9022b542556a..3c5d8b026d6e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -432,6 +432,8 @@ struct pci_host_bridge { void *sysdata; int busnr; struct list_head windows; /* resource_entry */ + u8 (*swizzle_irq)(struct pci_dev *, u8 *); /* platform IRQ swizzler */ + int (*map_irq)(const struct pci_dev *, u8, u8); void (*release_fn)(struct pci_host_bridge *); void *release_data; struct msi_controller *msi; -- cgit v1.2.3-71-gd317 From 47a650f2795b00297a5a3eab7aaa46bdb2bbe304 Mon Sep 17 00:00:00 2001 From: Matthew Minter Date: Wed, 28 Jun 2017 15:14:02 -0500 Subject: PCI: Add pci_assign_irq() function and have pci_fixup_irqs() use it Here we delete the static pdev_fixup_irq() function which is currently what pci_fixup_irqs() uses to actually assign the IRQs and replace it with the pci_assign_irq() function which changes the interface and uses the new function pointers stored in the host bridge structure. Eventually this will allow pci_fixup_irqs() to be removed entirely and the new deferred assignment code path will call pci_assign_irq() directly. However to ensure current users continue to work, a new implementation of pci_fixup_irqs() is introduced which simply wraps the functionality of pci_assign_irq(). Signed-off-by: Matthew Minter [lorenzo.pieralisi@arm.com: reworked comments/log] Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-irq.c | 45 +++++++++++++++++++++++++++++++++++---------- include/linux/pci.h | 1 + 2 files changed, 36 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c index 95c225be49d1..81eda3d93a5d 100644 --- a/drivers/pci/setup-irq.c +++ b/drivers/pci/setup-irq.c @@ -15,6 +15,7 @@ #include #include #include +#include "pci.h" void __weak pcibios_update_irq(struct pci_dev *dev, int irq) { @@ -22,12 +23,17 @@ void __weak pcibios_update_irq(struct pci_dev *dev, int irq) pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); } -static void pdev_fixup_irq(struct pci_dev *dev, - u8 (*swizzle)(struct pci_dev *, u8 *), - int (*map_irq)(const struct pci_dev *, u8, u8)) +void pci_assign_irq(struct pci_dev *dev) { - u8 pin, slot; + u8 pin; + u8 slot = -1; int irq = 0; + struct pci_host_bridge *hbrg = pci_find_host_bridge(dev->bus); + + if (!(hbrg->map_irq)) { + dev_dbg(&dev->dev, "runtime IRQ mapping not provided by arch\n"); + return; + } /* If this device is not on the primary bus, we need to figure out which interrupt pin it will come in on. We know which slot it @@ -40,17 +46,22 @@ static void pdev_fixup_irq(struct pci_dev *dev, if (pin > 4) pin = 1; - if (pin != 0) { + if (pin) { /* Follow the chain of bridges, swizzling as we go. */ - slot = (*swizzle)(dev, &pin); + if (hbrg->swizzle_irq) + slot = (*(hbrg->swizzle_irq))(dev, &pin); - irq = (*map_irq)(dev, slot, pin); + /* + * If a swizzling function is not used map_irq must + * ignore slot + */ + irq = (*(hbrg->map_irq))(dev, slot, pin); if (irq == -1) irq = 0; } dev->irq = irq; - dev_dbg(&dev->dev, "fixup irq: got %d\n", dev->irq); + dev_dbg(&dev->dev, "assign IRQ: got %d\n", dev->irq); /* Always tell the device, so the driver knows what is the real IRQ to use; the device does not use it. */ @@ -60,9 +71,23 @@ static void pdev_fixup_irq(struct pci_dev *dev, void pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *), int (*map_irq)(const struct pci_dev *, u8, u8)) { + /* + * Implement pci_fixup_irqs() through pci_assign_irq(). + * This code should be remove eventually, it is a wrapper + * around pci_assign_irq() interface to keep current + * pci_fixup_irqs() behaviour unchanged on architecture + * code still relying on its interface. + */ struct pci_dev *dev = NULL; + struct pci_host_bridge *hbrg = NULL; - for_each_pci_dev(dev) - pdev_fixup_irq(dev, swizzle, map_irq); + for_each_pci_dev(dev) { + hbrg = pci_find_host_bridge(dev->bus); + hbrg->swizzle_irq = swizzle; + hbrg->map_irq = map_irq; + pci_assign_irq(dev); + hbrg->swizzle_irq = NULL; + hbrg->map_irq = NULL; + } } EXPORT_SYMBOL_GPL(pci_fixup_irqs); diff --git a/include/linux/pci.h b/include/linux/pci.h index 3c5d8b026d6e..5c1c0ae38dd3 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1147,6 +1147,7 @@ void pdev_enable_device(struct pci_dev *); int pci_enable_resources(struct pci_dev *, int mask); void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *), int (*)(const struct pci_dev *, u8, u8)); +void pci_assign_irq(struct pci_dev *dev); struct resource *pci_find_resource(struct pci_dev *dev, struct resource *res); #define HAVE_PCI_REQ_REGIONS 2 int __must_check pci_request_regions(struct pci_dev *, const char *); -- cgit v1.2.3-71-gd317 From 775755ed3c65fb2d31f9268162495d76eaa2c281 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 1 Jun 2017 13:10:38 +0200 Subject: PCI: Split ->reset_notify() method into ->reset_prepare() and ->reset_done() The pci_error_handlers->reset_notify() method had a flag to indicate whether to prepare for or clean up after a reset. The prepare and done cases have no shared functionality whatsoever, so split them into separate methods. [bhelgaas: changelog, update locking comments] Link: http://lkml.kernel.org/r/20170601111039.8913-3-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Bjorn Helgaas --- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 36 +++++-------- drivers/net/wireless/marvell/mwifiex/pcie.c | 75 ++++++++++++++++------------ drivers/nvme/host/pci.c | 15 +++--- drivers/pci/pci.c | 34 ++++++------- include/linux/pci.h | 3 +- 5 files changed, 82 insertions(+), 81 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 3e26d27ad213..63784576ae8b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -2348,30 +2348,19 @@ static void fm10k_io_resume(struct pci_dev *pdev) netif_device_attach(netdev); } -/** - * fm10k_io_reset_notify - called when PCI function is reset - * @pdev: Pointer to PCI device - * - * This callback is called when the PCI function is reset such as from - * /sys/class/net//device/reset or similar. When prepare is true, it - * means we should prepare for a function reset. If prepare is false, it means - * the function reset just occurred. - */ -static void fm10k_io_reset_notify(struct pci_dev *pdev, bool prepare) +static void fm10k_io_reset_prepare(struct pci_dev *pdev) { - struct fm10k_intfc *interface = pci_get_drvdata(pdev); - int err = 0; - - if (prepare) { - /* warn incase we have any active VF devices */ - if (pci_num_vf(pdev)) - dev_warn(&pdev->dev, - "PCIe FLR may cause issues for any active VF devices\n"); + /* warn incase we have any active VF devices */ + if (pci_num_vf(pdev)) + dev_warn(&pdev->dev, + "PCIe FLR may cause issues for any active VF devices\n"); + fm10k_prepare_suspend(pci_get_drvdata(pdev)); +} - fm10k_prepare_suspend(interface); - } else { - err = fm10k_handle_resume(interface); - } +static void fm10k_io_reset_done(struct pci_dev *pdev) +{ + struct fm10k_intfc *interface = pci_get_drvdata(pdev); + int err = fm10k_handle_resume(interface); if (err) { dev_warn(&pdev->dev, @@ -2384,7 +2373,8 @@ static const struct pci_error_handlers fm10k_err_handler = { .error_detected = fm10k_io_error_detected, .slot_reset = fm10k_io_slot_reset, .resume = fm10k_io_resume, - .reset_notify = fm10k_io_reset_notify, + .reset_prepare = fm10k_io_reset_prepare, + .reset_done = fm10k_io_reset_done, }; static struct pci_driver fm10k_driver = { diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index ac62bce50e96..279adf124fc9 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -346,11 +346,13 @@ static const struct pci_device_id mwifiex_ids[] = { MODULE_DEVICE_TABLE(pci, mwifiex_ids); -static void mwifiex_pcie_reset_notify(struct pci_dev *pdev, bool prepare) +/* + * Cleanup all software without cleaning anything related to PCIe and HW. + */ +static void mwifiex_pcie_reset_prepare(struct pci_dev *pdev) { struct pcie_service_card *card = pci_get_drvdata(pdev); struct mwifiex_adapter *adapter = card->adapter; - int ret; if (!adapter) { dev_err(&pdev->dev, "%s: adapter structure is not valid\n", @@ -359,37 +361,48 @@ static void mwifiex_pcie_reset_notify(struct pci_dev *pdev, bool prepare) } mwifiex_dbg(adapter, INFO, - "%s: vendor=0x%4.04x device=0x%4.04x rev=%d %s\n", - __func__, pdev->vendor, pdev->device, - pdev->revision, - prepare ? "Pre-FLR" : "Post-FLR"); - - if (prepare) { - /* Kernel would be performing FLR after this notification. - * Cleanup all software without cleaning anything related to - * PCIe and HW. - */ - mwifiex_shutdown_sw(adapter); - adapter->surprise_removed = true; - clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &card->work_flags); - clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags); - } else { - /* Kernel stores and restores PCIe function context before and - * after performing FLR respectively. Reconfigure the software - * and firmware including firmware redownload - */ - adapter->surprise_removed = false; - ret = mwifiex_reinit_sw(adapter); - if (ret) { - dev_err(&pdev->dev, "reinit failed: %d\n", ret); - return; - } - } + "%s: vendor=0x%4.04x device=0x%4.04x rev=%d Pre-FLR\n", + __func__, pdev->vendor, pdev->device, pdev->revision); + + mwifiex_shutdown_sw(adapter); + adapter->surprise_removed = true; + clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &card->work_flags); + clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags); mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); } -static const struct pci_error_handlers mwifiex_pcie_err_handler[] = { - { .reset_notify = mwifiex_pcie_reset_notify, }, +/* + * Kernel stores and restores PCIe function context before and after performing + * FLR respectively. Reconfigure the software and firmware including firmware + * redownload. + */ +static void mwifiex_pcie_reset_done(struct pci_dev *pdev) +{ + struct pcie_service_card *card = pci_get_drvdata(pdev); + struct mwifiex_adapter *adapter = card->adapter; + int ret; + + if (!adapter) { + dev_err(&pdev->dev, "%s: adapter structure is not valid\n", + __func__); + return; + } + + mwifiex_dbg(adapter, INFO, + "%s: vendor=0x%4.04x device=0x%4.04x rev=%d Post-FLR\n", + __func__, pdev->vendor, pdev->device, pdev->revision); + + adapter->surprise_removed = false; + ret = mwifiex_reinit_sw(adapter); + if (ret) + dev_err(&pdev->dev, "reinit failed: %d\n", ret); + else + mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); +} + +static const struct pci_error_handlers mwifiex_pcie_err_handler = { + .reset_prepare = mwifiex_pcie_reset_prepare, + .reset_done = mwifiex_pcie_reset_done, }; #ifdef CONFIG_PM_SLEEP @@ -410,7 +423,7 @@ static struct pci_driver __refdata mwifiex_pcie = { }, #endif .shutdown = mwifiex_pcie_shutdown, - .err_handler = mwifiex_pcie_err_handler, + .err_handler = &mwifiex_pcie_err_handler, }; /* diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index fed803232edc..9a3d69b8df98 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2145,14 +2145,14 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) return result; } -static void nvme_reset_notify(struct pci_dev *pdev, bool prepare) +static void nvme_reset_prepare(struct pci_dev *pdev) { - struct nvme_dev *dev = pci_get_drvdata(pdev); + nvme_dev_disable(pci_get_drvdata(pdev), false); +} - if (prepare) - nvme_dev_disable(dev, false); - else - nvme_reset(dev); +static void nvme_reset_done(struct pci_dev *pdev) +{ + nvme_reset(pci_get_drvdata(pdev)); } static void nvme_shutdown(struct pci_dev *pdev) @@ -2275,7 +2275,8 @@ static const struct pci_error_handlers nvme_err_handler = { .error_detected = nvme_error_detected, .slot_reset = nvme_slot_reset, .resume = nvme_error_resume, - .reset_notify = nvme_reset_notify, + .reset_prepare = nvme_reset_prepare, + .reset_done = nvme_reset_done, }; static const struct pci_device_id nvme_id_table[] = { diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f4587f6f8739..56407eb1dc88 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4130,32 +4130,18 @@ 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) +static void pci_dev_save_and_disable(struct pci_dev *dev) { const struct pci_error_handlers *err_handler = dev->driver ? dev->driver->err_handler : NULL; /* - * dev->driver->err_handler->reset_notify() is protected against + * dev->driver->err_handler->reset_prepare() is protected against * races with ->remove() by the device lock, which must be held by * the caller. */ - 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); + if (err_handler && err_handler->reset_prepare) + err_handler->reset_prepare(dev); /* * Wake-up device prior to save. PM registers default to D0 after @@ -4177,8 +4163,18 @@ static void pci_dev_save_and_disable(struct pci_dev *dev) static void pci_dev_restore(struct pci_dev *dev) { + const struct pci_error_handlers *err_handler = + dev->driver ? dev->driver->err_handler : NULL; + pci_restore_state(dev); - pci_reset_notify(dev, false); + + /* + * dev->driver->err_handler->reset_done() is protected against + * races with ->remove() by the device lock, which must be held by + * the caller. + */ + if (err_handler && err_handler->reset_done) + err_handler->reset_done(dev); } static int pci_dev_reset(struct pci_dev *dev, int probe) diff --git a/include/linux/pci.h b/include/linux/pci.h index c7cfdff2529c..c5937ee7e774 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -698,7 +698,8 @@ struct pci_error_handlers { 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); + void (*reset_prepare)(struct pci_dev *dev); + void (*reset_done)(struct pci_dev *dev); /* Device driver may resume normal operations */ void (*resume)(struct pci_dev *dev); -- cgit v1.2.3-71-gd317 From 5e14e9fac308daf5607362f879e6de67e0b8dd5b Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Tue, 20 Jun 2017 10:17:40 +0200 Subject: PCI: tango: Add Sigma Designs Tango SMP8759 PCIe host bridge support This driver is required to work around several hardware bugs in the PCIe controller. The SMP8759 does not support legacy interrupts or IO space. Signed-off-by: Marc Gonzalez [bhelgaas: add CONFIG_BROKEN dependency, various cleanups] Signed-off-by: Bjorn Helgaas --- drivers/pci/host/Kconfig | 14 +++++ drivers/pci/host/Makefile | 1 + drivers/pci/host/pcie-tango.c | 141 ++++++++++++++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 2 + 4 files changed, 158 insertions(+) create mode 100644 drivers/pci/host/pcie-tango.c (limited to 'include/linux') diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 7f47cd5e10a5..8e54115681d8 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -180,6 +180,20 @@ config PCIE_ROCKCHIP There is 1 internal PCIe port available to support GEN2 with 4 slots. +config PCIE_TANGO_SMP8759 + bool "Tango SMP8759 PCIe controller (DANGEROUS)" + depends on ARCH_TANGO && PCI_MSI && OF + depends on BROKEN + select PCI_HOST_COMMON + help + Say Y here to enable PCIe controller support for Sigma Designs + Tango SMP8759-based systems. + + Note: The SMP8759 controller multiplexes PCI config and MMIO + accesses, and Linux doesn't provide a way to serialize them. + This can lead to data corruption if drivers perform concurrent + config and MMIO accesses. + config VMD depends on PCI_MSI && X86_64 && SRCU tristate "Intel Volume Management Device Driver" diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index cab879578003..eb48aa6377c8 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o +obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o obj-$(CONFIG_VMD) += vmd.o # The following drivers are for devices that use the generic ACPI diff --git a/drivers/pci/host/pcie-tango.c b/drivers/pci/host/pcie-tango.c new file mode 100644 index 000000000000..6bbb81f06a53 --- /dev/null +++ b/drivers/pci/host/pcie-tango.c @@ -0,0 +1,141 @@ +#include +#include +#include + +#define SMP8759_MUX 0x48 +#define SMP8759_TEST_OUT 0x74 + +struct tango_pcie { + void __iomem *base; +}; + +static int smp8759_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct pci_config_window *cfg = bus->sysdata; + struct tango_pcie *pcie = dev_get_drvdata(cfg->parent); + int ret; + + /* Reads in configuration space outside devfn 0 return garbage */ + if (devfn != 0) + return PCIBIOS_FUNC_NOT_SUPPORTED; + + /* + * PCI config and MMIO accesses are muxed. Linux doesn't have a + * mutual exclusion mechanism for config vs. MMIO accesses, so + * concurrent accesses may cause corruption. + */ + writel_relaxed(1, pcie->base + SMP8759_MUX); + ret = pci_generic_config_read(bus, devfn, where, size, val); + writel_relaxed(0, pcie->base + SMP8759_MUX); + + return ret; +} + +static int smp8759_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct pci_config_window *cfg = bus->sysdata; + struct tango_pcie *pcie = dev_get_drvdata(cfg->parent); + int ret; + + writel_relaxed(1, pcie->base + SMP8759_MUX); + ret = pci_generic_config_write(bus, devfn, where, size, val); + writel_relaxed(0, pcie->base + SMP8759_MUX); + + return ret; +} + +static struct pci_ecam_ops smp8759_ecam_ops = { + .bus_shift = 20, + .pci_ops = { + .map_bus = pci_ecam_map_bus, + .read = smp8759_config_read, + .write = smp8759_config_write, + } +}; + +static int tango_pcie_link_up(struct tango_pcie *pcie) +{ + void __iomem *test_out = pcie->base + SMP8759_TEST_OUT; + int i; + + writel_relaxed(16, test_out); + for (i = 0; i < 10; ++i) { + u32 ltssm_state = readl_relaxed(test_out) >> 8; + if ((ltssm_state & 0x1f) == 0xf) /* L0 */ + return 1; + usleep_range(3000, 4000); + } + + return 0; +} + +static int tango_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tango_pcie *pcie; + struct resource *res; + int ret; + + dev_warn(dev, "simultaneous PCI config and MMIO accesses may cause data corruption\n"); + add_taint(TAINT_CRAP, LOCKDEP_STILL_OK); + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + pcie->base = devm_ioremap_resource(dev, res); + if (IS_ERR(pcie->base)) + return PTR_ERR(pcie->base); + + platform_set_drvdata(pdev, pcie); + + if (!tango_pcie_link_up(pcie)) + return -ENODEV; + + return pci_host_common_probe(pdev, &smp8759_ecam_ops); +} + +static const struct of_device_id tango_pcie_ids[] = { + { .compatible = "sigma,smp8759-pcie" }, + { }, +}; + +static struct platform_driver tango_pcie_driver = { + .probe = tango_pcie_probe, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = tango_pcie_ids, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(tango_pcie_driver); + +/* + * The root complex advertises the wrong device class. + * Header Type 1 is for PCI-to-PCI bridges. + */ +static void tango_fixup_class(struct pci_dev *dev) +{ + dev->class = PCI_CLASS_BRIDGE_PCI << 8; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0024, tango_fixup_class); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0028, tango_fixup_class); + +/* + * The root complex exposes a "fake" BAR, which is used to filter + * bus-to-system accesses. Only accesses within the range defined by this + * BAR are forwarded to the host, others are ignored. + * + * By default, the DMA framework expects an identity mapping, and DRAM0 is + * mapped at 0x80000000. + */ +static void tango_fixup_bar(struct pci_dev *dev) +{ + dev->non_compliant_bars = true; + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0x80000000); +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0024, tango_fixup_bar); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0028, tango_fixup_bar); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 5f6b71d15393..c71e532da458 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1373,6 +1373,8 @@ #define PCI_DEVICE_ID_TTI_HPT374 0x0008 #define PCI_DEVICE_ID_TTI_HPT372N 0x0009 /* apparently a 372N variant? */ +#define PCI_VENDOR_ID_SIGMA 0x1105 + #define PCI_VENDOR_ID_VIA 0x1106 #define PCI_DEVICE_ID_VIA_8763_0 0x0198 #define PCI_DEVICE_ID_VIA_8380_0 0x0204 -- cgit v1.2.3-71-gd317