From addb30c5bd2755770e617e68bdcc0bf2a21770fa Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Fri, 7 Feb 2025 18:23:01 +0200 Subject: PCI: Cleanup dev->resource + resno to use pci_resource_n() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace pointer arithmetic in finding the correct resource entry with the pci_resource_n() helper. Link: https://lore.kernel.org/r/20250207162301.2842-1-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/iov.c | 2 +- drivers/pci/pci.c | 2 +- drivers/pci/quirks.c | 4 ++-- drivers/pci/setup-res.c | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 9e4770cdd4d5..121540f57d4b 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -952,7 +952,7 @@ void pci_iov_remove(struct pci_dev *dev) void pci_iov_update_resource(struct pci_dev *dev, int resno) { struct pci_sriov *iov = dev->is_physfn ? dev->sriov : NULL; - struct resource *res = dev->resource + resno; + struct resource *res = pci_resource_n(dev, resno); int vf_bar = resno - PCI_IOV_RESOURCES; struct pci_bus_region region; u16 cmd; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 869d204a70a3..c4f710f782f6 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1884,7 +1884,7 @@ static void pci_restore_rebar_state(struct pci_dev *pdev) pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; - res = pdev->resource + bar_idx; + res = pci_resource_n(pdev, bar_idx); size = pci_rebar_bytes_to_size(resource_size(res)); ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index b84ff7bade82..5cc4610201b7 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -621,7 +621,7 @@ static void quirk_io(struct pci_dev *dev, int pos, unsigned int size, { u32 region; struct pci_bus_region bus_region; - struct resource *res = dev->resource + pos; + struct resource *res = pci_resource_n(dev, pos); const char *res_name = pci_resource_name(dev, pos); pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (pos << 2), ®ion); @@ -671,7 +671,7 @@ static void quirk_io_region(struct pci_dev *dev, int port, { u16 region; struct pci_bus_region bus_region; - struct resource *res = dev->resource + nr; + struct resource *res = pci_resource_n(dev, nr); pci_read_config_word(dev, port, ®ion); region &= ~(size - 1); diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index ca14576bf2bf..ad6436007148 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -29,7 +29,7 @@ static void pci_std_update_resource(struct pci_dev *dev, int resno) u16 cmd; u32 new, check, mask; int reg; - struct resource *res = dev->resource + resno; + struct resource *res = pci_resource_n(dev, resno); const char *res_name = pci_resource_name(dev, resno); /* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */ @@ -262,7 +262,7 @@ resource_size_t __weak pcibios_align_resource(void *data, static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, int resno, resource_size_t size, resource_size_t align) { - struct resource *res = dev->resource + resno; + struct resource *res = pci_resource_n(dev, resno); resource_size_t min; int ret; @@ -325,7 +325,7 @@ static int _pci_assign_resource(struct pci_dev *dev, int resno, int pci_assign_resource(struct pci_dev *dev, int resno) { - struct resource *res = dev->resource + resno; + struct resource *res = pci_resource_n(dev, resno); const char *res_name = pci_resource_name(dev, resno); resource_size_t align, size; int ret; @@ -372,7 +372,7 @@ EXPORT_SYMBOL(pci_assign_resource); int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsize, resource_size_t min_align) { - struct resource *res = dev->resource + resno; + struct resource *res = pci_resource_n(dev, resno); const char *res_name = pci_resource_name(dev, resno); unsigned long flags; resource_size_t new_size; @@ -411,7 +411,7 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, void pci_release_resource(struct pci_dev *dev, int resno) { - struct resource *res = dev->resource + resno; + struct resource *res = pci_resource_n(dev, resno); const char *res_name = pci_resource_name(dev, resno); pci_info(dev, "%s %pR: releasing\n", res_name, res); @@ -428,7 +428,7 @@ EXPORT_SYMBOL(pci_release_resource); int pci_resize_resource(struct pci_dev *dev, int resno, int size) { - struct resource *res = dev->resource + resno; + struct resource *res = pci_resource_n(dev, resno); struct pci_host_bridge *host; int old, ret; u32 sizes; -- cgit v1.2.3-59-g8ed1b From 1a596ad00ffe9b37fc60a93cbdd4daead3bf95f3 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Wed, 4 Dec 2024 10:24:57 +0800 Subject: PCI: Use downstream bridges for distributing resources 7180c1d08639 ("PCI: Distribute available resources for root buses, too") breaks BAR assignment on some devices: pci 0006:03:00.0: BAR 0 [mem 0x6300c0000000-0x6300c1ffffff 64bit pref]: assigned pci 0006:03:00.1: BAR 0 [mem 0x6300c2000000-0x6300c3ffffff 64bit pref]: assigned pci 0006:03:00.2: BAR 0 [mem size 0x00800000 64bit pref]: can't assign; no space pci 0006:03:00.0: VF BAR 0 [mem size 0x02000000 64bit pref]: can't assign; no space pci 0006:03:00.1: VF BAR 0 [mem size 0x02000000 64bit pref]: can't assign; no space The apertures of domain 0006 before 7180c1d08639: 6300c0000000-63ffffffffff : PCI Bus 0006:00 6300c0000000-6300c9ffffff : PCI Bus 0006:01 6300c0000000-6300c9ffffff : PCI Bus 0006:02 # 160MB 6300c0000000-6300c8ffffff : PCI Bus 0006:03 # 144MB 6300c0000000-6300c1ffffff : 0006:03:00.0 # 32MB 6300c2000000-6300c3ffffff : 0006:03:00.1 # 32MB 6300c4000000-6300c47fffff : 0006:03:00.2 # 8MB 6300c4800000-6300c67fffff : 0006:03:00.0 # 32MB 6300c6800000-6300c87fffff : 0006:03:00.1 # 32MB 6300c9000000-6300c9bfffff : PCI Bus 0006:04 # 12MB 6300c9000000-6300c9bfffff : PCI Bus 0006:05 # 12MB 6300c9000000-6300c91fffff : PCI Bus 0006:06 # 2MB 6300c9200000-6300c93fffff : PCI Bus 0006:07 # 2MB 6300c9400000-6300c95fffff : PCI Bus 0006:08 # 2MB 6300c9600000-6300c97fffff : PCI Bus 0006:09 # 2MB After 7180c1d08639: 6300c0000000-63ffffffffff : PCI Bus 0006:00 6300c0000000-6300c9ffffff : PCI Bus 0006:01 6300c0000000-6300c9ffffff : PCI Bus 0006:02 # 160MB 6300c0000000-6300c43fffff : PCI Bus 0006:03 # 68MB 6300c0000000-6300c1ffffff : 0006:03:00.0 # 32MB 6300c2000000-6300c3ffffff : 0006:03:00.1 # 32MB --- no space --- : 0006:03:00.2 # 8MB --- no space --- : 0006:03:00.0 # 32MB --- no space --- : 0006:03:00.1 # 32MB 6300c4400000-6300c4dfffff : PCI Bus 0006:04 # 10MB 6300c4400000-6300c4dfffff : PCI Bus 0006:05 # 10MB 6300c4400000-6300c45fffff : PCI Bus 0006:06 # 2MB 6300c4600000-6300c47fffff : PCI Bus 0006:07 # 2MB 6300c4800000-6300c49fffff : PCI Bus 0006:08 # 2MB 6300c4a00000-6300c4bfffff : PCI Bus 0006:09 # 2MB We can see that the window to 0006:03 gets shrunken too much and 0006:04 eats away the window for 0006:03:00.2. The offending commit distributes the upstream bridge's resources multiple times to every downstream bridge, hence makes the aperture smaller than desired because calculation of io_per_b, mmio_per_b and mmio_pref_per_b becomes incorrect. Instead, distribute downstream bridges' own resources to resolve the issue. Link: https://lore.kernel.org/r/20241204022457.51322-1-kaihengf@nvidia.com Fixes: 7180c1d08639 ("PCI: Distribute available resources for root buses, too") Link: https://bugzilla.kernel.org/show_bug.cgi?id=219540 Signed-off-by: Kai-Heng Feng Signed-off-by: Bjorn Helgaas Tested-by: Chia-Lin Kao (AceLan) Reviewed-by: Mika Westerberg Cc: Carol Soto Cc: Jonathan Cameron Cc: Chris Chiu --- drivers/pci/setup-bus.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 5e00cecf1f1a..3d876d493faf 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -2102,8 +2102,7 @@ pci_root_bus_distribute_available_resources(struct pci_bus *bus, * in case of root bus. */ if (bridge && pci_bridge_resources_not_assigned(dev)) - pci_bridge_distribute_available_resources(bridge, - add_list); + pci_bridge_distribute_available_resources(dev, add_list); else pci_root_bus_distribute_available_resources(b, add_list); } -- cgit v1.2.3-59-g8ed1b From d06cc1e3809040e8250f69a4c656e3717e6b963c Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:08 +0200 Subject: PCI: Remove add_align overwrite unrelated to size0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 566f1dd52816 ("PCI: Relax bridge window tail sizing rules") relaxed bridge window tail alignment rule for the non-optional part (size0, no add_size/add_align). The change, however, also overwrote add_align, which is only related to case where optional size1 related entry is added into realloc head. Correct this by removing the add_align overwrite. Link: https://lore.kernel.org/r/20241216175632.4175-2-ilpo.jarvinen@linux.intel.com Fixes: 566f1dd52816 ("PCI: Relax bridge window tail sizing rules") Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 3d876d493faf..3a1fcaad142a 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1149,7 +1149,6 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, min_align = 1ULL << (max_order + __ffs(SZ_1M)); min_align = max(min_align, win_align); size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), win_align); - add_align = win_align; pci_info(bus->self, "bridge window %pR to %pR requires relaxed alignment rules\n", b_res, &bus->busn_res); } -- cgit v1.2.3-59-g8ed1b From 1f82b7e84a09cac05ec24cd8dbc68d7fe9ba0d97 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:09 +0200 Subject: PCI: Use min_align, not unrelated add_align, for size0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 566f1dd52816 ("PCI: Relax bridge window tail sizing rules") relaxed bridge window tail alignment rule for the non-optional part (size0, no add_size/add_align). The required alignment given for pbus_upstream_space_available(), however, was add_align which relates only to size1 alignment. As pbus_upstream_space_available() only selects between normal and relaxed tail alignment of the bridge window, the different alignment only makes relaxed tail alignment to be used more often than what was intended, which should be harmless because relaxed tail alignment itself should work in all cases. For consistency, change pbus_upstream_space_available() call to use min_align which is the alignment that is going to be used for the bridge window in the case where size0 sized allocation is attempted. Link: https://lore.kernel.org/r/20241216175632.4175-3-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 3a1fcaad142a..326badd4192a 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1145,7 +1145,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, if (bus->self && size0 && !pbus_upstream_space_available(bus, mask | IORESOURCE_PREFETCH, type, - size0, add_align)) { + size0, min_align)) { min_align = 1ULL << (max_order + __ffs(SZ_1M)); min_align = max(min_align, win_align); size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), win_align); -- cgit v1.2.3-59-g8ed1b From a55bf64b30e4ee04c8f690e2c3d0924beb7fbd62 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:10 +0200 Subject: PCI: Simplify size1 assignment logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In pbus_size_io() and pbus_size_mem(), a complex ?: operation is performed to set size1. Decompose this so it's easier to read. In the case of pbus_size_mem(), simply initializing size1 to zero ensures the size1 checks work as expected. Link: https://lore.kernel.org/r/20241216175632.4175-4-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 326badd4192a..fd78606526ec 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -927,9 +927,14 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, size0 = calculate_iosize(size, min_size, size1, 0, 0, resource_size(b_res), min_align); - size1 = (!realloc_head || (realloc_head && !add_size && !children_add_size)) ? size0 : - calculate_iosize(size, min_size, size1, add_size, children_add_size, - resource_size(b_res), min_align); + + size1 = size0; + if (realloc_head && (add_size > 0 || children_add_size > 0)) { + size1 = calculate_iosize(size, min_size, size1, add_size, + children_add_size, resource_size(b_res), + min_align); + } + if (!size0 && !size1) { if (bus->self && (b_res->start || b_res->end)) pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n", @@ -1058,7 +1063,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, struct list_head *realloc_head) { struct pci_dev *dev; - resource_size_t min_align, win_align, align, size, size0, size1; + resource_size_t min_align, win_align, align, size, size0, size1 = 0; resource_size_t aligns[24]; /* Alignments from 1MB to 8TB */ int order, max_order; struct resource *b_res = find_bus_resource_of_type(bus, @@ -1153,9 +1158,11 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, b_res, &bus->busn_res); } - size1 = (!realloc_head || (realloc_head && !add_size && !children_add_size)) ? size0 : - calculate_memsize(size, min_size, add_size, children_add_size, - resource_size(b_res), add_align); + if (realloc_head && (add_size > 0 || children_add_size > 0)) { + size1 = calculate_memsize(size, min_size, add_size, children_add_size, + resource_size(b_res), add_align); + } + if (!size0 && !size1) { if (bus->self && (b_res->start || b_res->end)) pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n", -- cgit v1.2.3-59-g8ed1b From 67f9085596ee55dd27b540ca6088ba0717ee511c Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:11 +0200 Subject: PCI: Allow relaxed bridge window tail sizing for optional resources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 566f1dd52816 ("PCI: Relax bridge window tail sizing rules") relaxed the bridge window requirements for non-optional size (size0) but pbus_size_mem() also handles optional sizes (IOV resources) using size1. This can manifest, e.g., as a failure to resize a BAR back to its original size after it was first shrunk when device has a VF BAR resource because the bridge window (size1) is enlarged beyond what is strictly required to fit the downstream resources. Allow using relaxed bridge window tail sizing rules also with the optional resources (size1) so that the remove/realloc cycle during BAR resize (smaller and back to the original size) does not fail unexpectedly due to increase in bridge window size demand. Also move add_align calculation to more logical place next to size1 assignment as they are strongly related to each other. Link: https://lore.kernel.org/r/20241216175632.4175-5-ilpo.jarvinen@linux.intel.com Fixes: 566f1dd52816 ("PCI: Relax bridge window tail sizing rules") Reported-by: Michał Winiarski Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index fd78606526ec..447f3aa1037c 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1146,7 +1146,6 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, min_align = calculate_mem_align(aligns, max_order); min_align = max(min_align, win_align); size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), min_align); - add_align = max(min_align, add_align); if (bus->self && size0 && !pbus_upstream_space_available(bus, mask | IORESOURCE_PREFETCH, type, @@ -1159,8 +1158,21 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, } if (realloc_head && (add_size > 0 || children_add_size > 0)) { + add_align = max(min_align, add_align); size1 = calculate_memsize(size, min_size, add_size, children_add_size, resource_size(b_res), add_align); + + if (bus->self && size1 && + !pbus_upstream_space_available(bus, mask | IORESOURCE_PREFETCH, type, + size1, add_align)) { + min_align = 1ULL << (max_order + __ffs(SZ_1M)); + min_align = max(min_align, win_align); + size1 = calculate_memsize(size, min_size, add_size, children_add_size, + resource_size(b_res), win_align); + pci_info(bus->self, + "bridge window %pR to %pR requires relaxed alignment rules\n", + b_res, &bus->busn_res); + } } if (!size0 && !size1) { -- cgit v1.2.3-59-g8ed1b From ff61f380de5652e723168341480cc7adf1dd6213 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:12 +0200 Subject: PCI: Fix old_size lower bound in calculate_iosize() too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 903534fa7d30 ("PCI: Fix resource double counting on remove & rescan") fixed double counting of mem resources because of old_size being applied too early. Fix a similar counting bug on the io resource side. Link: https://lore.kernel.org/r/20241216175632.4175-6-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 447f3aa1037c..7c966e3f5060 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -814,11 +814,9 @@ static resource_size_t calculate_iosize(resource_size_t size, size = (size & 0xff) + ((size & ~0xffUL) << 2); #endif size = size + size1; - if (size < old_size) - size = old_size; - size = ALIGN(max(size, add_size) + children_add_size, align); - return size; + size = max(size, add_size) + children_add_size; + return ALIGN(max(size, old_size), align); } static resource_size_t calculate_memsize(resource_size_t size, -- cgit v1.2.3-59-g8ed1b From 8986e7e6685f59859e0d2511ccbb9314ea534d25 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:13 +0200 Subject: PCI: Use SZ_* instead of literals in setup-bus.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert literals in setup-bus.c to SZ_* defines that make the size more human readable. As the code is now self-explanatory, eliminate comments about the size. Link: https://lore.kernel.org/r/20241216175632.4175-7-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 7c966e3f5060..fd6b89c22df3 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -841,9 +841,9 @@ resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus, return 1; } -#define PCI_P2P_DEFAULT_MEM_ALIGN 0x100000 /* 1MiB */ -#define PCI_P2P_DEFAULT_IO_ALIGN 0x1000 /* 4KiB */ -#define PCI_P2P_DEFAULT_IO_ALIGN_1K 0x400 /* 1KiB */ +#define PCI_P2P_DEFAULT_MEM_ALIGN SZ_1M +#define PCI_P2P_DEFAULT_IO_ALIGN SZ_4K +#define PCI_P2P_DEFAULT_IO_ALIGN_1K SZ_1K static resource_size_t window_alignment(struct pci_bus *bus, unsigned long type) { @@ -908,7 +908,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, continue; r_size = resource_size(r); - if (r_size < 0x400) + if (r_size < SZ_1K) /* Might be re-aligned for ISA */ size += r_size; else -- cgit v1.2.3-59-g8ed1b From ee4621b7e46ad857dd14bed13bf8804b42a80e66 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:14 +0200 Subject: PCI: Use resource_set_{range,size}() helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A few sites that could use resource_set_range/size() in setup-bus.c were not picked up earlier due to them no matching the usual pattern. Convert them now. These are more cases similar to 783602c920e9 ("PCI: Use resource_set_{range,size}() helpers"). Link: https://lore.kernel.org/r/20241216175632.4175-8-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen [bhelgaas: add 783602c920e9 history] Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index fd6b89c22df3..d273b5042017 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -413,10 +413,8 @@ static void __assign_resources_sorted(struct list_head *head, * consistent. */ if (add_align > dev_res->res->start) { - resource_size_t r_size = resource_size(dev_res->res); - - dev_res->res->start = add_align; - dev_res->res->end = add_align + r_size - 1; + resource_set_range(dev_res->res, add_align, + resource_size(dev_res->res)); list_for_each_entry(dev_res2, head, list) { align = pci_resource_alignment(dev_res2->dev, @@ -1100,7 +1098,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, if (realloc_head && i >= PCI_IOV_RESOURCES && i <= PCI_IOV_RESOURCE_END) { add_align = max(pci_resource_alignment(dev, r), add_align); - r->end = r->start - 1; + resource_set_size(r, 0); add_to_list(realloc_head, dev, r, r_size, 0 /* Don't care */); children_add_size += r_size; continue; @@ -1180,8 +1178,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, b_res->flags = 0; return 0; } - b_res->start = min_align; - b_res->end = size0 + min_align - 1; + + resource_set_range(b_res, min_align, size0); b_res->flags |= IORESOURCE_STARTALIGN; if (bus->self && size1 > size0 && realloc_head) { add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align); @@ -1656,8 +1654,7 @@ static void pci_bridge_release_resources(struct pci_bus *bus, pci_info(dev, "resource %d %pR released\n", PCI_BRIDGE_RESOURCES + idx, r); /* Keep the old size */ - r->end = resource_size(r) - 1; - r->start = 0; + resource_set_range(r, 0, resource_size(r)); r->flags = 0; /* Avoiding touch the one without PREF */ -- cgit v1.2.3-59-g8ed1b From cbd384389eac296de9d2088dac0617911361b2f4 Mon Sep 17 00:00:00 2001 From: Michał Winiarski Date: Mon, 16 Dec 2024 19:56:15 +0200 Subject: PCI: Add pci_resource_is_iov() to identify IOV resources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are multiple places where special handling is required for IOV resources. Extract the identification of IOV resources to pci_resource_is_iov() and drop a few ifdefs. Link: https://lore.kernel.org/r/20241216175632.4175-9-ilpo.jarvinen@linux.intel.com Signed-off-by: Michał Winiarski Signed-off-by: Bjorn Helgaas Reviewed-by: Ilpo Järvinen Reviewed-by: Christian König Tested-by: Xiaochun Lee --- drivers/pci/pci.h | 19 +++++++++++++++---- drivers/pci/setup-bus.c | 7 +++---- drivers/pci/setup-res.c | 4 +--- 3 files changed, 19 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 01e51db8d285..89599e63f60c 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -632,6 +632,10 @@ void pci_iov_update_resource(struct pci_dev *dev, int resno); resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno); void pci_restore_iov_state(struct pci_dev *dev); int pci_iov_bus_range(struct pci_bus *bus); +static inline bool pci_resource_is_iov(int resno) +{ + return resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END; +} extern const struct attribute_group sriov_pf_dev_attr_group; extern const struct attribute_group sriov_vf_dev_attr_group; #else @@ -641,12 +645,21 @@ static inline int pci_iov_init(struct pci_dev *dev) } static inline void pci_iov_release(struct pci_dev *dev) { } static inline void pci_iov_remove(struct pci_dev *dev) { } +static inline void pci_iov_update_resource(struct pci_dev *dev, int resno) { } +static inline resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, + int resno) +{ + return 0; +} static inline void pci_restore_iov_state(struct pci_dev *dev) { } static inline int pci_iov_bus_range(struct pci_bus *bus) { return 0; } - +static inline bool pci_resource_is_iov(int resno) +{ + return false; +} #endif /* CONFIG_PCI_IOV */ #ifdef CONFIG_PCIE_TPH @@ -680,12 +693,10 @@ unsigned long pci_cardbus_resource_alignment(struct resource *); static inline resource_size_t pci_resource_alignment(struct pci_dev *dev, struct resource *res) { -#ifdef CONFIG_PCI_IOV int resno = res - dev->resource; - if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END) + if (pci_resource_is_iov(resno)) return pci_sriov_resource_alignment(dev, resno); -#endif if (dev->class >> 8 == PCI_CLASS_BRIDGE_CARDBUS) return pci_cardbus_resource_alignment(res); return resource_alignment(res); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index d273b5042017..5f3215e0904d 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1093,17 +1093,16 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, (r->flags & mask) != type3)) continue; r_size = resource_size(r); -#ifdef CONFIG_PCI_IOV + /* Put SRIOV requested res to the optional list */ - if (realloc_head && i >= PCI_IOV_RESOURCES && - i <= PCI_IOV_RESOURCE_END) { + if (realloc_head && pci_resource_is_iov(i)) { add_align = max(pci_resource_alignment(dev, r), add_align); resource_set_size(r, 0); add_to_list(realloc_head, dev, r, r_size, 0 /* Don't care */); children_add_size += r_size; continue; } -#endif + /* * aligns[0] is for 1MB (since bridge memory * windows are always at least 1MB aligned), so diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index ad6436007148..83ffff7cc0a3 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -127,10 +127,8 @@ void pci_update_resource(struct pci_dev *dev, int resno) { if (resno <= PCI_ROM_RESOURCE) pci_std_update_resource(dev, resno); -#ifdef CONFIG_PCI_IOV - else if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END) + else if (pci_resource_is_iov(resno)) pci_iov_update_resource(dev, resno); -#endif } int pci_claim_resource(struct pci_dev *dev, int resource) -- cgit v1.2.3-59-g8ed1b From 2bd0c72117841f7fb82aab7f4a0d65e66ef9543e Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:16 +0200 Subject: PCI: Check resource_size() separately MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of chaining logic inside if () condition so that multiple lines are required, make !resource_size() a separate check and use continue. Link: https://lore.kernel.org/r/20241216175632.4175-10-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 5f3215e0904d..eaeaf09cd91d 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -285,8 +285,11 @@ static void assign_requested_resources_sorted(struct list_head *head, list_for_each_entry(dev_res, head, list) { res = dev_res->res; idx = res - &dev_res->dev->resource[0]; - if (resource_size(res) && - pci_assign_resource(dev_res->dev, idx)) { + + if (!resource_size(res)) + continue; + + if (pci_assign_resource(dev_res->dev, idx)) { if (fail_head) { /* * If the failed resource is a ROM BAR and -- cgit v1.2.3-59-g8ed1b From e4728eed24a32f275106c498669a9d3177f9611e Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:17 +0200 Subject: PCI: Add pci_resource_num() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A few places in PCI code, mainly in setup-bus.c, need to reverse lookup the index of a resource in pci_dev's resource array. Create pci_resource_num() helper to avoid repeating the pointer arithmetic trick used to calculate the index. Link: https://lore.kernel.org/r/20241216175632.4175-11-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/pci.h | 24 +++++++++++++++++++++++- drivers/pci/setup-bus.c | 10 +++++----- 2 files changed, 28 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 89599e63f60c..996185abd30c 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -334,6 +334,28 @@ void pci_walk_bus_locked(struct pci_bus *top, const char *pci_resource_name(struct pci_dev *dev, unsigned int i); +/** + * pci_resource_num - Reverse lookup resource number from device resources + * @dev: PCI device + * @res: Resource to lookup index for (MUST be a @dev's resource) + * + * Perform reverse lookup to determine the resource number for @res within + * @dev resource array. NOTE: The caller is responsible for ensuring @res is + * among @dev's resources! + * + * Returns: resource number. + */ +static inline int pci_resource_num(const struct pci_dev *dev, + const struct resource *res) +{ + int resno = res - &dev->resource[0]; + + /* Passing a resource that is not among dev's resources? */ + WARN_ON_ONCE(resno >= PCI_NUM_RESOURCES); + + return resno; +} + void pci_reassigndev_resource_alignment(struct pci_dev *dev); void pci_disable_bridge_window(struct pci_dev *dev); struct pci_bus *pci_bus_get(struct pci_bus *bus); @@ -693,7 +715,7 @@ unsigned long pci_cardbus_resource_alignment(struct resource *); static inline resource_size_t pci_resource_alignment(struct pci_dev *dev, struct resource *res) { - int resno = res - dev->resource; + int resno = pci_resource_num(dev, res); if (pci_resource_is_iov(resno)) return pci_sriov_resource_alignment(dev, resno); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index eaeaf09cd91d..524a6381b25b 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -242,7 +242,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head, if (!found_match) /* Just skip */ continue; - idx = res - &add_res->dev->resource[0]; + idx = pci_resource_num(add_res->dev, res); res_name = pci_resource_name(add_res->dev, idx); add_size = add_res->add_size; align = add_res->min_align; @@ -284,7 +284,7 @@ static void assign_requested_resources_sorted(struct list_head *head, list_for_each_entry(dev_res, head, list) { res = dev_res->res; - idx = res - &dev_res->dev->resource[0]; + idx = pci_resource_num(dev_res->dev, res); if (!resource_size(res)) continue; @@ -2210,7 +2210,7 @@ again: res->flags = fail_res->flags; if (pci_is_bridge(fail_res->dev)) { - idx = res - &fail_res->dev->resource[0]; + idx = pci_resource_num(fail_res->dev, res); if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END) res->flags = 0; @@ -2294,7 +2294,7 @@ again: res->flags = fail_res->flags; if (pci_is_bridge(fail_res->dev)) { - idx = res - &fail_res->dev->resource[0]; + idx = pci_resource_num(fail_res->dev, res); if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END) res->flags = 0; @@ -2401,7 +2401,7 @@ cleanup: struct resource *res = dev_res->res; bridge = dev_res->dev; - i = res - bridge->resource; + i = pci_resource_num(bridge, res); res->start = dev_res->start; res->end = dev_res->end; -- cgit v1.2.3-59-g8ed1b From 9b54578bc0323e3d4c66c37eb568ecc132fb56b5 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:18 +0200 Subject: PCI: Add dev & res local variables to resource assignment funcs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many PCI resource allocation related functions process struct pci_dev_resource items which hold the struct pci_dev and resource pointers. Reduce the number of lines that need indirection by adding 'dev' and 'res' local variable to hold the pointers. Link: https://lore.kernel.org/r/20241216175632.4175-12-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 66 +++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 524a6381b25b..916474af56ad 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -216,10 +216,11 @@ static inline void reset_resource(struct resource *res) static void reassign_resources_sorted(struct list_head *realloc_head, struct list_head *head) { - struct resource *res; - const char *res_name; struct pci_dev_resource *add_res, *tmp; struct pci_dev_resource *dev_res; + struct pci_dev *dev; + struct resource *res; + const char *res_name; resource_size_t add_size, align; int idx; @@ -227,6 +228,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head, bool found_match = false; res = add_res->res; + dev = add_res->dev; /* Skip resource that has been reset */ if (!res->flags) @@ -242,20 +244,19 @@ static void reassign_resources_sorted(struct list_head *realloc_head, if (!found_match) /* Just skip */ continue; - idx = pci_resource_num(add_res->dev, res); - res_name = pci_resource_name(add_res->dev, idx); + idx = pci_resource_num(dev, res); + res_name = pci_resource_name(dev, idx); add_size = add_res->add_size; align = add_res->min_align; if (!resource_size(res)) { resource_set_range(res, align, add_size); - if (pci_assign_resource(add_res->dev, idx)) + if (pci_assign_resource(dev, idx)) reset_resource(res); } else { res->flags |= add_res->flags & (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); - if (pci_reassign_resource(add_res->dev, idx, - add_size, align)) - pci_info(add_res->dev, "%s %pR: failed to add %llx\n", + if (pci_reassign_resource(dev, idx, add_size, align)) + pci_info(dev, "%s %pR: failed to add %llx\n", res_name, res, (unsigned long long) add_size); } @@ -278,18 +279,20 @@ out: static void assign_requested_resources_sorted(struct list_head *head, struct list_head *fail_head) { - struct resource *res; struct pci_dev_resource *dev_res; + struct resource *res; + struct pci_dev *dev; int idx; list_for_each_entry(dev_res, head, list) { res = dev_res->res; - idx = pci_resource_num(dev_res->dev, res); + dev = dev_res->dev; + idx = pci_resource_num(dev, res); if (!resource_size(res)) continue; - if (pci_assign_resource(dev_res->dev, idx)) { + if (pci_assign_resource(dev, idx)) { if (fail_head) { /* * If the failed resource is a ROM BAR and @@ -298,8 +301,7 @@ static void assign_requested_resources_sorted(struct list_head *head, */ if (!((idx == PCI_ROM_RESOURCE) && (!(res->flags & IORESOURCE_ROM_ENABLE)))) - add_to_list(fail_head, - dev_res->dev, res, + add_to_list(fail_head, dev, res, 0 /* don't care */, 0 /* don't care */); } @@ -377,6 +379,7 @@ static void __assign_resources_sorted(struct list_head *head, LIST_HEAD(local_fail_head); struct pci_dev_resource *save_res; struct pci_dev_resource *dev_res, *tmp_res, *dev_res2; + struct resource *res; unsigned long fail_type; resource_size_t add_align, align; @@ -394,8 +397,9 @@ static void __assign_resources_sorted(struct list_head *head, /* Update res in head list with add_size in realloc_head list */ list_for_each_entry_safe(dev_res, tmp_res, head, list) { - dev_res->res->end += get_res_add_size(realloc_head, - dev_res->res); + res = dev_res->res; + + res->end += get_res_add_size(realloc_head, res); /* * There are two kinds of additional resources in the list: @@ -403,10 +407,10 @@ static void __assign_resources_sorted(struct list_head *head, * 2. SR-IOV resource -- IORESOURCE_SIZEALIGN * Here just fix the additional alignment for bridge */ - if (!(dev_res->res->flags & IORESOURCE_STARTALIGN)) + if (!(res->flags & IORESOURCE_STARTALIGN)) continue; - add_align = get_res_add_align(realloc_head, dev_res->res); + add_align = get_res_add_align(realloc_head, res); /* * The "head" list is sorted by alignment so resources with @@ -415,9 +419,8 @@ static void __assign_resources_sorted(struct list_head *head, * need to reorder the list by alignment to make it * consistent. */ - if (add_align > dev_res->res->start) { - resource_set_range(dev_res->res, add_align, - resource_size(dev_res->res)); + if (add_align > res->start) { + resource_set_range(res, add_align, resource_size(res)); list_for_each_entry(dev_res2, head, list) { align = pci_resource_alignment(dev_res2->dev, @@ -448,24 +451,29 @@ static void __assign_resources_sorted(struct list_head *head, /* Check failed type */ fail_type = pci_fail_res_type_mask(&local_fail_head); /* Remove not need to be released assigned res from head list etc */ - list_for_each_entry_safe(dev_res, tmp_res, head, list) - if (dev_res->res->parent && - !pci_need_to_release(fail_type, dev_res->res)) { + list_for_each_entry_safe(dev_res, tmp_res, head, list) { + res = dev_res->res; + + if (res->parent && !pci_need_to_release(fail_type, res)) { /* Remove it from realloc_head list */ - remove_from_list(realloc_head, dev_res->res); - remove_from_list(&save_head, dev_res->res); + remove_from_list(realloc_head, res); + remove_from_list(&save_head, res); list_del(&dev_res->list); kfree(dev_res); } + } free_list(&local_fail_head); /* Release assigned resource */ - list_for_each_entry(dev_res, head, list) - if (dev_res->res->parent) - release_resource(dev_res->res); + list_for_each_entry(dev_res, head, list) { + res = dev_res->res; + + if (res->parent) + release_resource(res); + } /* Restore start/end/flags from saved list */ list_for_each_entry(save_res, &save_head, list) { - struct resource *res = save_res->res; + res = save_res->res; res->start = save_res->start; res->end = save_res->end; -- cgit v1.2.3-59-g8ed1b From 22fb2eda5478f55c8750eb76b7656d2c90de4b39 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:19 +0200 Subject: PCI: Converge return paths in __assign_resources_sorted() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All return paths want to free head list in __assign_resources_sorted(), so add a label and use goto. Link: https://lore.kernel.org/r/20241216175632.4175-13-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 916474af56ad..8fda0354e543 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -444,8 +444,7 @@ static void __assign_resources_sorted(struct list_head *head, list_for_each_entry(dev_res, head, list) remove_from_list(realloc_head, dev_res->res); free_list(&save_head); - free_list(head); - return; + goto out; } /* Check failed type */ @@ -488,6 +487,8 @@ requested_and_reassign: /* Try to satisfy any additional optional resource requests */ if (realloc_head) reassign_resources_sorted(realloc_head, head); + +out: free_list(head); } -- cgit v1.2.3-59-g8ed1b From 0aa089cdde945d19ab2e51a8e60a129e9944d312 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:20 +0200 Subject: PCI: Refactor pdev_sort_resources() & __dev_sort_resources() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce level of call nesting by calling pdev_sort_resources() directly and by moving the tests done inside __dev_sort_resources() into pdev_resources_assignable() helper. Link: https://lore.kernel.org/r/20241216175632.4175-14-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 8fda0354e543..21ed6084abc9 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -127,12 +127,33 @@ static resource_size_t get_res_add_align(struct list_head *head, return dev_res ? dev_res->min_align : 0; } +static bool pdev_resources_assignable(struct pci_dev *dev) +{ + u16 class = dev->class >> 8, command; + + /* Don't touch classless devices or host bridges or IOAPICs */ + if (class == PCI_CLASS_NOT_DEFINED || class == PCI_CLASS_BRIDGE_HOST) + return false; + + /* Don't touch IOAPIC devices already enabled by firmware */ + if (class == PCI_CLASS_SYSTEM_PIC) { + pci_read_config_word(dev, PCI_COMMAND, &command); + if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) + return false; + } + + return true; +} + /* Sort resources by alignment */ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) { struct resource *r; int i; + if (!pdev_resources_assignable(dev)) + return; + pci_dev_for_each_resource(dev, r, i) { const char *r_name = pci_resource_name(dev, i); struct pci_dev_resource *dev_res, *tmp; @@ -176,25 +197,6 @@ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) } } -static void __dev_sort_resources(struct pci_dev *dev, struct list_head *head) -{ - u16 class = dev->class >> 8; - - /* Don't touch classless devices or host bridges or IOAPICs */ - if (class == PCI_CLASS_NOT_DEFINED || class == PCI_CLASS_BRIDGE_HOST) - return; - - /* Don't touch IOAPIC devices already enabled by firmware */ - if (class == PCI_CLASS_SYSTEM_PIC) { - u16 command; - pci_read_config_word(dev, PCI_COMMAND, &command); - if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) - return; - } - - pdev_sort_resources(dev, head); -} - static inline void reset_resource(struct resource *res) { res->start = 0; @@ -498,7 +500,7 @@ static void pdev_assign_resources_sorted(struct pci_dev *dev, { LIST_HEAD(head); - __dev_sort_resources(dev, &head); + pdev_sort_resources(dev, &head); __assign_resources_sorted(&head, add_head, fail_head); } @@ -511,7 +513,7 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus, LIST_HEAD(head); list_for_each_entry(dev, &bus->devices, bus_list) - __dev_sort_resources(dev, &head); + pdev_sort_resources(dev, &head); __assign_resources_sorted(&head, realloc_head, fail_head); } -- cgit v1.2.3-59-g8ed1b From acba174d2e754346c07578ad2220258706a203e2 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:21 +0200 Subject: PCI: Use while loop and break instead of gotos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_assign_unassigned_root_bus_resources() and pci_assign_unassigned_bridge_resources() contain ad hoc loops using backwards goto and gotos out of the loop. Replace them with while loops and break statements. While reindenting the loop bodies, add braces & remove parenthesis. Link: https://lore.kernel.org/r/20241216175632.4175-15-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 212 ++++++++++++++++++++++++------------------------ 1 file changed, 106 insertions(+), 106 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 21ed6084abc9..31dd988cec6d 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -2161,78 +2161,79 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) max_depth, pci_try_num); } -again: - /* - * Last try will use add_list, otherwise will try good to have as must - * have, so can realloc parent bridge resource - */ - if (tried_times + 1 == pci_try_num) - add_list = &realloc_head; - /* - * Depth first, calculate sizes and alignments of all subordinate buses. - */ - __pci_bus_size_bridges(bus, add_list); + while (1) { + /* + * Last try will use add_list, otherwise will try good to + * have as must have, so can realloc parent bridge resource + */ + if (tried_times + 1 == pci_try_num) + add_list = &realloc_head; + /* + * Depth first, calculate sizes and alignments of all + * subordinate buses. + */ + __pci_bus_size_bridges(bus, add_list); - pci_root_bus_distribute_available_resources(bus, add_list); + pci_root_bus_distribute_available_resources(bus, add_list); - /* Depth last, allocate resources and update the hardware. */ - __pci_bus_assign_resources(bus, add_list, &fail_head); - if (add_list) - BUG_ON(!list_empty(add_list)); - tried_times++; + /* Depth last, allocate resources and update the hardware. */ + __pci_bus_assign_resources(bus, add_list, &fail_head); + if (add_list) + BUG_ON(!list_empty(add_list)); + tried_times++; - /* Any device complain? */ - if (list_empty(&fail_head)) - goto dump; + /* Any device complain? */ + if (list_empty(&fail_head)) + break; - if (tried_times >= pci_try_num) { - if (enable_local == undefined) - dev_info(&bus->dev, "Some PCI device resources are unassigned, try booting with pci=realloc\n"); - else if (enable_local == auto_enabled) - dev_info(&bus->dev, "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n"); + if (tried_times >= pci_try_num) { + if (enable_local == undefined) { + dev_info(&bus->dev, + "Some PCI device resources are unassigned, try booting with pci=realloc\n"); + } else if (enable_local == auto_enabled) { + dev_info(&bus->dev, + "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n"); + } + free_list(&fail_head); + break; + } - free_list(&fail_head); - goto dump; - } + dev_info(&bus->dev, "No. %d try to assign unassigned res\n", + tried_times + 1); - dev_info(&bus->dev, "No. %d try to assign unassigned res\n", - tried_times + 1); + /* Third times and later will not check if it is leaf */ + if (tried_times + 1 > 2) + rel_type = whole_subtree; + + /* + * Try to release leaf bridge's resources that doesn't fit + * resource of child device under that bridge. + */ + list_for_each_entry(fail_res, &fail_head, list) { + pci_bus_release_bridge_resources(fail_res->dev->bus, + fail_res->flags & PCI_RES_TYPE_MASK, + rel_type); + } - /* Third times and later will not check if it is leaf */ - if ((tried_times + 1) > 2) - rel_type = whole_subtree; + /* Restore size and flags */ + list_for_each_entry(fail_res, &fail_head, list) { + struct resource *res = fail_res->res; + int idx; - /* - * Try to release leaf bridge's resources that doesn't fit resource of - * child device under that bridge. - */ - list_for_each_entry(fail_res, &fail_head, list) - pci_bus_release_bridge_resources(fail_res->dev->bus, - fail_res->flags & PCI_RES_TYPE_MASK, - rel_type); + res->start = fail_res->start; + res->end = fail_res->end; + res->flags = fail_res->flags; - /* Restore size and flags */ - list_for_each_entry(fail_res, &fail_head, list) { - struct resource *res = fail_res->res; - int idx; - - res->start = fail_res->start; - res->end = fail_res->end; - res->flags = fail_res->flags; - - if (pci_is_bridge(fail_res->dev)) { - idx = pci_resource_num(fail_res->dev, res); - if (idx >= PCI_BRIDGE_RESOURCES && - idx <= PCI_BRIDGE_RESOURCE_END) - res->flags = 0; + if (pci_is_bridge(fail_res->dev)) { + idx = pci_resource_num(fail_res->dev, res); + if (idx >= PCI_BRIDGE_RESOURCES && + idx <= PCI_BRIDGE_RESOURCE_END) + res->flags = 0; + } } + free_list(&fail_head); } - free_list(&fail_head); - goto again; - -dump: - /* Dump the resource on buses */ pci_bus_dump_resources(bus); } @@ -2260,62 +2261,61 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) struct pci_dev_resource *fail_res; int retval; -again: - __pci_bus_size_bridges(parent, &add_list); + while (1) { + __pci_bus_size_bridges(parent, &add_list); - /* - * Distribute remaining resources (if any) equally between hotplug - * bridges below. This makes it possible to extend the hierarchy - * later without running out of resources. - */ - pci_bridge_distribute_available_resources(bridge, &add_list); + /* + * Distribute remaining resources (if any) equally between + * hotplug bridges below. This makes it possible to extend + * the hierarchy later without running out of resources. + */ + pci_bridge_distribute_available_resources(bridge, &add_list); - __pci_bridge_assign_resources(bridge, &add_list, &fail_head); - BUG_ON(!list_empty(&add_list)); - tried_times++; + __pci_bridge_assign_resources(bridge, &add_list, &fail_head); + BUG_ON(!list_empty(&add_list)); + tried_times++; - if (list_empty(&fail_head)) - goto enable_all; + if (list_empty(&fail_head)) + break; - if (tried_times >= 2) { - /* Still fail, don't need to try more */ - free_list(&fail_head); - goto enable_all; - } + if (tried_times >= 2) { + /* Still fail, don't need to try more */ + free_list(&fail_head); + break; + } - printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n", - tried_times + 1); + printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n", + tried_times + 1); - /* - * Try to release leaf bridge's resources that aren't big enough - * to contain child device resources. - */ - list_for_each_entry(fail_res, &fail_head, list) - pci_bus_release_bridge_resources(fail_res->dev->bus, - fail_res->flags & PCI_RES_TYPE_MASK, - whole_subtree); + /* + * Try to release leaf bridge's resources that aren't big + * enough to contain child device resources. + */ + list_for_each_entry(fail_res, &fail_head, list) { + pci_bus_release_bridge_resources(fail_res->dev->bus, + fail_res->flags & PCI_RES_TYPE_MASK, + whole_subtree); + } - /* Restore size and flags */ - list_for_each_entry(fail_res, &fail_head, list) { - struct resource *res = fail_res->res; - int idx; - - res->start = fail_res->start; - res->end = fail_res->end; - res->flags = fail_res->flags; - - if (pci_is_bridge(fail_res->dev)) { - idx = pci_resource_num(fail_res->dev, res); - if (idx >= PCI_BRIDGE_RESOURCES && - idx <= PCI_BRIDGE_RESOURCE_END) - res->flags = 0; + /* Restore size and flags */ + list_for_each_entry(fail_res, &fail_head, list) { + struct resource *res = fail_res->res; + int idx; + + res->start = fail_res->start; + res->end = fail_res->end; + res->flags = fail_res->flags; + + if (pci_is_bridge(fail_res->dev)) { + idx = pci_resource_num(fail_res->dev, res); + if (idx >= PCI_BRIDGE_RESOURCES && + idx <= PCI_BRIDGE_RESOURCE_END) + res->flags = 0; + } } + free_list(&fail_head); } - free_list(&fail_head); - - goto again; -enable_all: retval = pci_reenable_device(bridge); if (retval) pci_err(bridge, "Error reenabling bridge (%d)\n", retval); -- cgit v1.2.3-59-g8ed1b From 54181c13647225e494fb932184568b98c6f1c18e Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:22 +0200 Subject: PCI: Rename retval to ret MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename 'retval' to 'ret' in pci_assign_unassigned_bridge_resources(). Link: https://lore.kernel.org/r/20241216175632.4175-16-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 31dd988cec6d..badfecd599d8 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -2255,11 +2255,10 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) struct pci_bus *parent = bridge->subordinate; /* List of resources that want additional resources */ LIST_HEAD(add_list); - int tried_times = 0; LIST_HEAD(fail_head); struct pci_dev_resource *fail_res; - int retval; + int ret; while (1) { __pci_bus_size_bridges(parent, &add_list); @@ -2316,9 +2315,9 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) free_list(&fail_head); } - retval = pci_reenable_device(bridge); - if (retval) - pci_err(bridge, "Error reenabling bridge (%d)\n", retval); + ret = pci_reenable_device(bridge); + if (ret) + pci_err(bridge, "Error reenabling bridge (%d)\n", ret); pci_set_master(bridge); } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); -- cgit v1.2.3-59-g8ed1b From ca9097f9ce0383e46588bc96bda8f65b57d5d125 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:23 +0200 Subject: PCI: Consolidate assignment loop next round preparation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_assign_unassigned_root_bus_resources() and pci_assign_unassigned_bridge_resources() have a loop that may perform several rounds to assign resources. The code to prepare for the next round is identical. Consolidate the code that prepares for the next assignment round into pci_prepare_next_assign_round(). Link: https://lore.kernel.org/r/20241216175632.4175-17-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 104 +++++++++++++++++++----------------------------- 1 file changed, 42 insertions(+), 62 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index badfecd599d8..41f1d5129cac 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -2134,6 +2134,45 @@ pci_root_bus_distribute_available_resources(struct pci_bus *bus, } } +static void pci_prepare_next_assign_round(struct list_head *fail_head, + int tried_times, + enum release_type rel_type) +{ + struct pci_dev_resource *fail_res; + + pr_info("PCI: No. %d try to assign unassigned res\n", tried_times + 1); + + /* + * Try to release leaf bridge's resources that aren't big + * enough to contain child device resources. + */ + list_for_each_entry(fail_res, fail_head, list) { + pci_bus_release_bridge_resources(fail_res->dev->bus, + fail_res->flags & PCI_RES_TYPE_MASK, + rel_type); + } + + /* Restore size and flags */ + list_for_each_entry(fail_res, fail_head, list) { + struct resource *res = fail_res->res; + struct pci_dev *dev = fail_res->dev; + int idx = pci_resource_num(dev, res); + + res->start = fail_res->start; + res->end = fail_res->end; + res->flags = fail_res->flags; + + if (!pci_is_bridge(dev)) + continue; + + if (idx >= PCI_BRIDGE_RESOURCES && + idx <= PCI_BRIDGE_RESOURCE_END) + res->flags = 0; + } + + free_list(fail_head); +} + /* * First try will not touch PCI bridge res. * Second and later try will clear small leaf bridge res. @@ -2147,7 +2186,6 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) int tried_times = 0; enum release_type rel_type = leaf_only; LIST_HEAD(fail_head); - struct pci_dev_resource *fail_res; int pci_try_num = 1; enum enable_type enable_local; @@ -2198,40 +2236,11 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) break; } - dev_info(&bus->dev, "No. %d try to assign unassigned res\n", - tried_times + 1); - /* Third times and later will not check if it is leaf */ if (tried_times + 1 > 2) rel_type = whole_subtree; - /* - * Try to release leaf bridge's resources that doesn't fit - * resource of child device under that bridge. - */ - list_for_each_entry(fail_res, &fail_head, list) { - pci_bus_release_bridge_resources(fail_res->dev->bus, - fail_res->flags & PCI_RES_TYPE_MASK, - rel_type); - } - - /* Restore size and flags */ - list_for_each_entry(fail_res, &fail_head, list) { - struct resource *res = fail_res->res; - int idx; - - res->start = fail_res->start; - res->end = fail_res->end; - res->flags = fail_res->flags; - - if (pci_is_bridge(fail_res->dev)) { - idx = pci_resource_num(fail_res->dev, res); - if (idx >= PCI_BRIDGE_RESOURCES && - idx <= PCI_BRIDGE_RESOURCE_END) - res->flags = 0; - } - } - free_list(&fail_head); + pci_prepare_next_assign_round(&fail_head, tried_times, rel_type); } pci_bus_dump_resources(bus); @@ -2257,7 +2266,6 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) LIST_HEAD(add_list); int tried_times = 0; LIST_HEAD(fail_head); - struct pci_dev_resource *fail_res; int ret; while (1) { @@ -2283,36 +2291,8 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) break; } - printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n", - tried_times + 1); - - /* - * Try to release leaf bridge's resources that aren't big - * enough to contain child device resources. - */ - list_for_each_entry(fail_res, &fail_head, list) { - pci_bus_release_bridge_resources(fail_res->dev->bus, - fail_res->flags & PCI_RES_TYPE_MASK, - whole_subtree); - } - - /* Restore size and flags */ - list_for_each_entry(fail_res, &fail_head, list) { - struct resource *res = fail_res->res; - int idx; - - res->start = fail_res->start; - res->end = fail_res->end; - res->flags = fail_res->flags; - - if (pci_is_bridge(fail_res->dev)) { - idx = pci_resource_num(fail_res->dev, res); - if (idx >= PCI_BRIDGE_RESOURCES && - idx <= PCI_BRIDGE_RESOURCE_END) - res->flags = 0; - } - } - free_list(&fail_head); + pci_prepare_next_assign_round(&fail_head, tried_times, + whole_subtree); } ret = pci_reenable_device(bridge); -- cgit v1.2.3-59-g8ed1b From c8098ad8fb2e80dcb92920da795455d30ed5e292 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:24 +0200 Subject: PCI: Remove incorrect comment from pci_reassign_resource() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit a4ac9fea016f ("PCI : Calculate right add_size") removed including min_align into new_size in pci_reassign_resource() which is the correct thing to do. However, it also added a snakeoil comment that the resource would already be aligned with min_align which is incorrect. A resource that is assigned earlier is aligned with the old alignment, NOT with the new requested alignment (min_align) until later deep within the reassignment callchain. Thus, remove the incorrect comment. Link: https://lore.kernel.org/r/20241216175632.4175-18-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee Cc: Yinghai Lu --- drivers/pci/setup-res.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 83ffff7cc0a3..51ff9ab1f400 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -387,7 +387,6 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, return -EINVAL; } - /* already aligned with min_align */ new_size = resource_size(res) + addsize; ret = _pci_assign_resource(dev, resno, new_size, min_align); if (ret) { -- cgit v1.2.3-59-g8ed1b From 4e362abe482def0bbb4fcccbc8808f149c16cb75 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:25 +0200 Subject: PCI: Add restore_dev_resource() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resource fitting needs to restore the saved dev resources in a few places. Add a restore_dev_resource() helper for that. Link: https://lore.kernel.org/r/20241216175632.4175-19-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 41f1d5129cac..9a1c09b9efa2 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -127,6 +127,15 @@ static resource_size_t get_res_add_align(struct list_head *head, return dev_res ? dev_res->min_align : 0; } +static void restore_dev_resource(struct pci_dev_resource *dev_res) +{ + struct resource *res = dev_res->res; + + res->start = dev_res->start; + res->end = dev_res->end; + res->flags = dev_res->flags; +} + static bool pdev_resources_assignable(struct pci_dev *dev) { u16 class = dev->class >> 8, command; @@ -473,13 +482,8 @@ static void __assign_resources_sorted(struct list_head *head, release_resource(res); } /* Restore start/end/flags from saved list */ - list_for_each_entry(save_res, &save_head, list) { - res = save_res->res; - - res->start = save_res->start; - res->end = save_res->end; - res->flags = save_res->flags; - } + list_for_each_entry(save_res, &save_head, list) + restore_dev_resource(save_res); free_list(&save_head); requested_and_reassign: @@ -2158,9 +2162,7 @@ static void pci_prepare_next_assign_round(struct list_head *fail_head, struct pci_dev *dev = fail_res->dev; int idx = pci_resource_num(dev, res); - res->start = fail_res->start; - res->end = fail_res->end; - res->flags = fail_res->flags; + restore_dev_resource(fail_res); if (!pci_is_bridge(dev)) continue; @@ -2377,13 +2379,8 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) cleanup: /* Restore size and flags */ - list_for_each_entry(dev_res, &failed, list) { - struct resource *res = dev_res->res; - - res->start = dev_res->start; - res->end = dev_res->end; - res->flags = dev_res->flags; - } + list_for_each_entry(dev_res, &failed, list) + restore_dev_resource(dev_res); free_list(&failed); /* Revert to the old configuration */ @@ -2393,9 +2390,7 @@ cleanup: bridge = dev_res->dev; i = pci_resource_num(bridge, res); - res->start = dev_res->start; - res->end = dev_res->end; - res->flags = dev_res->flags; + restore_dev_resource(dev_res); pci_claim_resource(bridge, i); pci_setup_bridge(bridge->subordinate); -- cgit v1.2.3-59-g8ed1b From 9caf4ea2fd022e3febb8f70421b7f87e9788852e Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:26 +0200 Subject: PCI: Extend enable to check for any optional resource MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_enable_resources() checks if device's io and mem resources are all assigned and disallows enable if any resource failed to assign (*) but makes an exception for the case of disabled extension ROM. There are other optional resources, however. Add pci_resource_is_optional() and use it instead of pci_resource_is_disabled_rom() to cover also IOV resources that are also optional as per pbus_size_mem(). As there will be more users of pci_resource_is_optional() inside setup-bus.c in changes coming up after this one, the function is placed there. (*) In practice, resource fitting code calls reset_resource() for any resource it fails to assign which clears resource's ->flags causing pci_enable_resources() to never detect failed resource assignments. This seems undesirable internal logic inconsistency, effectively reset_resource() prevents pci_enable_resources() from functioning as intended. This is one step of many that will be needed towards removing reset_resource(). Link: https://lore.kernel.org/r/20241216175632.4175-20-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/pci.h | 1 + drivers/pci/setup-bus.c | 12 ++++++++++++ drivers/pci/setup-res.c | 3 +-- 3 files changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 996185abd30c..4e2ac06db3c4 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -333,6 +333,7 @@ void pci_walk_bus_locked(struct pci_bus *top, void *userdata); const char *pci_resource_name(struct pci_dev *dev, unsigned int i); +bool pci_resource_is_optional(const struct pci_dev *dev, int resno); /** * pci_resource_num - Reverse lookup resource number from device resources diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 9a1c09b9efa2..0b11069938b6 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -206,6 +206,18 @@ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) } } +bool pci_resource_is_optional(const struct pci_dev *dev, int resno) +{ + const struct resource *res = pci_resource_n(dev, resno); + + if (pci_resource_is_iov(resno)) + return true; + if (resno == PCI_ROM_RESOURCE && !(res->flags & IORESOURCE_ROM_ENABLE)) + return true; + + return false; +} + static inline void reset_resource(struct resource *res) { res->start = 0; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 51ff9ab1f400..b056acfda96c 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -494,8 +494,7 @@ int pci_enable_resources(struct pci_dev *dev, int mask) if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) continue; - if ((i == PCI_ROM_RESOURCE) && - (!(r->flags & IORESOURCE_ROM_ENABLE))) + if (pci_resource_is_optional(dev, i)) continue; if (r->flags & IORESOURCE_UNSET) { -- cgit v1.2.3-59-g8ed1b From b3281eb5ded17f88d7d4fa5fb39a709c195e56c2 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:27 +0200 Subject: PCI: Always have realloc_head in __assign_resources_sorted() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a dummy list to always have a non-NULL realloc head in __assign_resources_sorted() as it allows only checking list_empty(). In future, it would be good to ensure all callers provide a valid realloc_head but that is relatively complex to do in practice and not necessary for the subsequent optional resource handling fix. Link: https://lore.kernel.org/r/20241216175632.4175-21-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 0b11069938b6..aa092644808b 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -400,14 +400,18 @@ static void __assign_resources_sorted(struct list_head *head, */ LIST_HEAD(save_head); LIST_HEAD(local_fail_head); + LIST_HEAD(dummy_head); struct pci_dev_resource *save_res; struct pci_dev_resource *dev_res, *tmp_res, *dev_res2; struct resource *res; unsigned long fail_type; resource_size_t add_align, align; + if (!realloc_head) + realloc_head = &dummy_head; + /* Check if optional add_size is there */ - if (!realloc_head || list_empty(realloc_head)) + if (list_empty(realloc_head)) goto requested_and_reassign; /* Save original start, end, flags etc at first */ @@ -503,7 +507,7 @@ requested_and_reassign: assign_requested_resources_sorted(head, fail_head); /* Try to satisfy any additional optional resource requests */ - if (realloc_head) + if (!list_empty(realloc_head)) reassign_resources_sorted(realloc_head, head); out: -- cgit v1.2.3-59-g8ed1b From 07854e08cdf3e2c294ca7941a8958830d880eaf7 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:28 +0200 Subject: PCI: Indicate optional resource assignment failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add pci_dbg() to note that an assignment failure was for an optional resource and reword existing message about resource resize to say the change was optional. Link: https://lore.kernel.org/r/20241216175632.4175-22-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index aa092644808b..c80162d6aefd 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -273,13 +273,17 @@ static void reassign_resources_sorted(struct list_head *realloc_head, align = add_res->min_align; if (!resource_size(res)) { resource_set_range(res, align, add_size); - if (pci_assign_resource(dev, idx)) + if (pci_assign_resource(dev, idx)) { + pci_dbg(dev, + "%s %pR: ignoring failure in optional allocation\n", + res_name, res); reset_resource(res); + } } else { res->flags |= add_res->flags & (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); if (pci_reassign_resource(dev, idx, add_size, align)) - pci_info(dev, "%s %pR: failed to add %llx\n", + pci_info(dev, "%s %pR: failed to add optional %llx\n", res_name, res, (unsigned long long) add_size); } -- cgit v1.2.3-59-g8ed1b From 8884b5637b794ae541e8d6fb72102b1d8dba2b8d Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:29 +0200 Subject: PCI: Add debug print when releasing resources before retry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCI resource fitting is somewhat hard to track because it performs many actions without logging them. In the case inside __assign_resources_sorted(), the resources are released before resource assignment is going to be retried in a different order. That is just one level of retries the resource fitting performs overall so tracking it through repeated assignments or failures of a resource gets messy rather quickly. Simply announce the release explicitly using pci_dbg() so it is clear what is going on with each resource. Link: https://lore.kernel.org/r/20241216175632.4175-23-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index c80162d6aefd..5bebfeb6c417 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -408,6 +408,9 @@ static void __assign_resources_sorted(struct list_head *head, struct pci_dev_resource *save_res; struct pci_dev_resource *dev_res, *tmp_res, *dev_res2; struct resource *res; + struct pci_dev *dev; + const char *res_name; + int idx; unsigned long fail_type; resource_size_t add_align, align; @@ -497,9 +500,16 @@ static void __assign_resources_sorted(struct list_head *head, /* Release assigned resource */ list_for_each_entry(dev_res, head, list) { res = dev_res->res; + dev = dev_res->dev; + + if (!res->parent) + continue; + + idx = pci_resource_num(dev, res); + res_name = pci_resource_name(dev, idx); + pci_dbg(dev, "%s %pR: releasing\n", res_name, res); - if (res->parent) - release_resource(res); + release_resource(res); } /* Restore start/end/flags from saved list */ list_for_each_entry(save_res, &save_head, list) -- cgit v1.2.3-59-g8ed1b From e89df6d2beae847e931d84a190b192dfac41eba7 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:30 +0200 Subject: PCI: Use res->parent to check if resource is assigned MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reassign_resources_sorted() uses resource_size() to select between pci_assign_resource() and pci_reassign_resource(). Due to twisted way bridge window sizing in pbus_size_mem() sets resource sizes to 0, it works to match into IOV resources but that is going to be changed by an upcoming change. Replace resource_size() check with res->parent check that is the true dividing line in between whether assign or reassign function should be used for the resource. Link: https://lore.kernel.org/r/20241216175632.4175-24-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 5bebfeb6c417..ef1c82d2e58e 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -271,7 +271,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head, res_name = pci_resource_name(dev, idx); add_size = add_res->add_size; align = add_res->min_align; - if (!resource_size(res)) { + if (!res->parent) { resource_set_range(res, align, add_size); if (pci_assign_resource(dev, idx)) { pci_dbg(dev, -- cgit v1.2.3-59-g8ed1b From 96336ec702643aec2addb3b1cdb81d687fe362f0 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:31 +0200 Subject: PCI: Perform reset_resource() and build fail list in sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resetting a resource is problematic as it prevents attempting to allocate the resource later, unless something in between restores the resource. Similarly, if fail_head does not contain all resources that were reset, those resources cannot be restored later. The entire reset/restore cycle adds complexity and leaving resources in the reset state causes issues to other code such as for checks done in pci_enable_resources(). Take a small step towards not resetting resources by delaying reset until the end of resource assignment and build failure list (fail_head) in sync with the reset to avoid leaving behind resources that cannot be restored (for the case where the caller provides fail_head in the first place to allow restore somewhere in the callchain, as is not all callers pass non-NULL fail_head). Leave the Expansion ROM check temporarily in place while building the failure list until an upcoming change that reworks optional resource handling. Ideally, whole resource reset could be removed but doing that in one step would be non-tractable due to complexity of all related code. Link: https://lore.kernel.org/r/20241216175632.4175-25-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index ef1c82d2e58e..b0b146757943 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -252,9 +252,14 @@ static void reassign_resources_sorted(struct list_head *realloc_head, res = add_res->res; dev = add_res->dev; + idx = pci_resource_num(dev, res); - /* Skip resource that has been reset */ - if (!res->flags) + /* + * Skip resource that failed the earlier assignment and is + * not optional as it would just fail again. + */ + if (!res->parent && resource_size(res) && + !pci_resource_is_optional(dev, idx)) goto out; /* Skip this resource if not found in head list */ @@ -267,7 +272,6 @@ static void reassign_resources_sorted(struct list_head *realloc_head, if (!found_match) /* Just skip */ continue; - idx = pci_resource_num(dev, res); res_name = pci_resource_name(dev, idx); add_size = add_res->add_size; align = add_res->min_align; @@ -277,7 +281,6 @@ static void reassign_resources_sorted(struct list_head *realloc_head, pci_dbg(dev, "%s %pR: ignoring failure in optional allocation\n", res_name, res); - reset_resource(res); } } else { res->flags |= add_res->flags & @@ -332,7 +335,6 @@ static void assign_requested_resources_sorted(struct list_head *head, 0 /* don't care */, 0 /* don't care */); } - reset_resource(res); } } } @@ -518,13 +520,34 @@ static void __assign_resources_sorted(struct list_head *head, requested_and_reassign: /* Satisfy the must-have resource requests */ - assign_requested_resources_sorted(head, fail_head); + assign_requested_resources_sorted(head, NULL); /* Try to satisfy any additional optional resource requests */ if (!list_empty(realloc_head)) reassign_resources_sorted(realloc_head, head); out: + /* Reset any failed resource, cannot use fail_head as it can be NULL. */ + list_for_each_entry(dev_res, head, list) { + res = dev_res->res; + dev = dev_res->dev; + + if (res->parent) + continue; + + /* + * If the failed resource is a ROM BAR and it will + * be enabled later, don't add it to the list. + */ + if (fail_head && !pci_resource_is_disabled_rom(res, idx)) { + add_to_list(fail_head, dev, res, + 0 /* don't care */, + 0 /* don't care */); + } + + reset_resource(res); + } + free_list(head); } -- cgit v1.2.3-59-g8ed1b From 2499f53484314303f1b90f86424d9ec1bef9119d Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 19:56:32 +0200 Subject: PCI: Rework optional resource handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove and rescan cycle can result in failure to assign a bridge window if it becomes larger than before the remove. The bridge window size will include space for disabled Expansion ROM, which can causes the bridge window to not fit anymore into the same address space slot on rescan if the Expansion ROM resource was not assigned before the remove. In addition, the optional resource handling is not internally consistent. The resource fitting logic supports three main types of optional resources: - IOV BARs - Expansion ROMs - Bridge window size variation due to optional resources In addition to the above, resizable BARs beyond their current size will require handling optional variation in resource sizes within the resource fitting algorithm (not yet done by the resource fitting code). There are multiple inconsistencies related to optional resource handling: a) The allocation failure of disabled expansion ROM requires special case inside assign_requested_resources_sorted(). b) The optionality of disabled expansion ROM is not considered during bridge window sizing in pbus_size_mem(). c) Setting resource size to zero for optional resource in pbus_size_mem() is problematic because it makes also the alignment invalid, which is checked by pdev_sort_resources(). Optional IOV resources have their size set to zero by pbus_size_mem() but the information about size is stored externally in struct pci_sriov and complex call-chain trickery in pci_resource_alignment() ensures IOV resources return a valid alignment despite having zero resource size. A solution that is specific to IOV resources makes it hard to use the same solution for other types of resources such as expansion ROM. Simply changing pbus_size_mem() is not sufficient to fully address the main issue because it would introduce disparity between bridge window sizing and resource allocation. Due to size-based ordering of the resource list during assignment loop, an Expansion ROM resource could steal space from some other resource and make the other resource not fit if the Expansion ROM is larger than the other resource. Thus, the resource assignment functions need to be changed as well. Make optional resource handling more straightforward. Use pci_resource_is_optional() to determine if a resource is optional in both bridge window sizing and assignment failure classification to ensure they always align. Indicate with a parameter to assign_requested_resources_sorted() whether it should attempt to allocate optional resources or not. Always try first to assign all resources (also when realloc_head is not provided). This is required for calls from pci_assign_unassigned_root_bus_resources() that provide realloc_head only with some of its iterations. Non-bridge-window optional resources in realloc_head now have add_size 0. This condition has to be detected in reassign_resources_sorted() before reassigning them (which would fail as there is no size change). Removing add_size=0 optional resources entirely from realloc_head might eventually be doable but further rework in __assign_resources_sorted() is needed first to support such a change. Link: https://lore.kernel.org/r/20241216175632.4175-26-ilpo.jarvinen@linux.intel.com Reported-by: Jia Yao Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219547 Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Jia Yao Tested-by: Xiaochun Lee --- drivers/pci/setup-bus.c | 89 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index b0b146757943..aa36e2d15f32 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -276,13 +276,14 @@ static void reassign_resources_sorted(struct list_head *realloc_head, add_size = add_res->add_size; align = add_res->min_align; if (!res->parent) { - resource_set_range(res, align, add_size); + resource_set_range(res, align, + resource_size(res) + add_size); if (pci_assign_resource(dev, idx)) { pci_dbg(dev, "%s %pR: ignoring failure in optional allocation\n", res_name, res); } - } else { + } else if (add_size > 0) { res->flags |= add_res->flags & (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); if (pci_reassign_resource(dev, idx, add_size, align)) @@ -302,38 +303,38 @@ out: * @head: Head of the list tracking requests for resources * @fail_head: Head of the list tracking requests that could not be * allocated + * @optional: Assign also optional resources * * Satisfy resource requests of each element in the list. Add requests that * could not be satisfied to the failed_list. */ static void assign_requested_resources_sorted(struct list_head *head, - struct list_head *fail_head) + struct list_head *fail_head, + bool optional) { struct pci_dev_resource *dev_res; struct resource *res; struct pci_dev *dev; + bool optional_res; int idx; list_for_each_entry(dev_res, head, list) { res = dev_res->res; dev = dev_res->dev; idx = pci_resource_num(dev, res); + optional_res = pci_resource_is_optional(dev, idx); if (!resource_size(res)) continue; + if (!optional && optional_res) + continue; + if (pci_assign_resource(dev, idx)) { if (fail_head) { - /* - * If the failed resource is a ROM BAR and - * it will be enabled later, don't add it - * to the list. - */ - if (!((idx == PCI_ROM_RESOURCE) && - (!(res->flags & IORESOURCE_ROM_ENABLE)))) - add_to_list(fail_head, dev, res, - 0 /* don't care */, - 0 /* don't care */); + add_to_list(fail_head, dev, res, + 0 /* don't care */, + 0 /* don't care */); } } } @@ -379,6 +380,20 @@ static bool pci_need_to_release(unsigned long mask, struct resource *res) return false; /* Should not get here */ } +/* Return: @true if assignment of a required resource failed. */ +static bool pci_required_resource_failed(struct list_head *fail_head) +{ + struct pci_dev_resource *fail_res; + + list_for_each_entry(fail_res, fail_head, list) { + int idx = pci_resource_num(fail_res->dev, fail_res->res); + + if (!pci_resource_is_optional(fail_res->dev, idx)) + return true; + } + return false; +} + static void __assign_resources_sorted(struct list_head *head, struct list_head *realloc_head, struct list_head *fail_head) @@ -388,9 +403,11 @@ static void __assign_resources_sorted(struct list_head *head, * adjacent, so later reassign can not reallocate them one by one in * parent resource window. * - * Try to assign requested + add_size at beginning. If could do that, - * could get out early. If could not do that, we still try to assign - * requested at first, then try to reassign add_size for some resources. + * Try to assign required and any optional resources at beginning + * (add_size included). If all required resources were successfully + * assigned, get out early. If could not do that, we still try to + * assign required at first, then try to reassign some optional + * resources. * * Separate three resource type checking if we need to release * assigned resource after requested + add_size try. @@ -421,13 +438,13 @@ static void __assign_resources_sorted(struct list_head *head, /* Check if optional add_size is there */ if (list_empty(realloc_head)) - goto requested_and_reassign; + goto assign; /* Save original start, end, flags etc at first */ list_for_each_entry(dev_res, head, list) { if (add_to_list(&save_head, dev_res->dev, dev_res->res, 0, 0)) { free_list(&save_head); - goto requested_and_reassign; + goto assign; } } @@ -471,10 +488,10 @@ static void __assign_resources_sorted(struct list_head *head, } - /* Try updated head list with add_size added */ - assign_requested_resources_sorted(head, &local_fail_head); +assign: + assign_requested_resources_sorted(head, &local_fail_head, true); - /* All assigned with add_size? */ + /* All non-optional resources assigned? */ if (list_empty(&local_fail_head)) { /* Remove head list from realloc_head list */ list_for_each_entry(dev_res, head, list) @@ -483,6 +500,22 @@ static void __assign_resources_sorted(struct list_head *head, goto out; } + /* Without realloc_head and only optional fails, nothing more to do. */ + if (!pci_required_resource_failed(&local_fail_head) && + list_empty(realloc_head)) { + list_for_each_entry(save_res, &save_head, list) { + struct resource *res = save_res->res; + + if (res->parent) + continue; + + restore_dev_resource(save_res); + } + free_list(&local_fail_head); + free_list(&save_head); + goto out; + } + /* Check failed type */ fail_type = pci_fail_res_type_mask(&local_fail_head); /* Remove not need to be released assigned res from head list etc */ @@ -518,9 +551,8 @@ static void __assign_resources_sorted(struct list_head *head, restore_dev_resource(save_res); free_list(&save_head); -requested_and_reassign: /* Satisfy the must-have resource requests */ - assign_requested_resources_sorted(head, NULL); + assign_requested_resources_sorted(head, NULL, false); /* Try to satisfy any additional optional resource requests */ if (!list_empty(realloc_head)) @@ -535,11 +567,7 @@ out: if (res->parent) continue; - /* - * If the failed resource is a ROM BAR and it will - * be enabled later, don't add it to the list. - */ - if (fail_head && !pci_resource_is_disabled_rom(res, idx)) { + if (fail_head) { add_to_list(fail_head, dev, res, 0 /* don't care */, 0 /* don't care */); @@ -1166,10 +1194,9 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, r_size = resource_size(r); /* Put SRIOV requested res to the optional list */ - if (realloc_head && pci_resource_is_iov(i)) { + if (realloc_head && pci_resource_is_optional(dev, i)) { add_align = max(pci_resource_alignment(dev, r), add_align); - resource_set_size(r, 0); - add_to_list(realloc_head, dev, r, r_size, 0 /* Don't care */); + add_to_list(realloc_head, dev, r, 0, 0 /* Don't care */); children_add_size += r_size; continue; } -- cgit v1.2.3-59-g8ed1b From 7d5f1e615e69c234f9a9ec9c944a10f21aeeb2f8 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 18:10:09 +0200 Subject: PCI: shpchp: Remove logging from module init/exit functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The logging in shpchp module init/exit functions is not very useful. Remove it. Link: https://lore.kernel.org/r/20241216161012.1774-2-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/shpchp_core.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index a92e28b72908..a10ce7be7f51 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -324,20 +324,12 @@ static struct pci_driver shpc_driver = { static int __init shpcd_init(void) { - int retval; - - retval = pci_register_driver(&shpc_driver); - dbg("%s: pci_register_driver = %d\n", __func__, retval); - info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - - return retval; + return pci_register_driver(&shpc_driver); } static void __exit shpcd_cleanup(void) { - dbg("unload_shpchpd()\n"); pci_unregister_driver(&shpc_driver); - info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); } module_init(shpcd_init); -- cgit v1.2.3-59-g8ed1b From 49998220089285efd8dad91d7b249df6f5cfe1b4 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 18:10:10 +0200 Subject: PCI: shpchp: Change dbg() -> ctrl_dbg() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the last user of dbg() to use ctrl_dbg(). Link: https://lore.kernel.org/r/20241216161012.1774-3-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/shpchp_hpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c index 012b9e3fe5b0..bfbec7c1a6b1 100644 --- a/drivers/pci/hotplug/shpchp_hpc.c +++ b/drivers/pci/hotplug/shpchp_hpc.c @@ -675,7 +675,7 @@ static int shpc_get_cur_bus_speed(struct controller *ctrl) out: bus->cur_bus_speed = bus_speed; - dbg("Current bus speed = %d\n", bus_speed); + ctrl_dbg(ctrl, "Current bus speed = %d\n", bus_speed); return retval; } -- cgit v1.2.3-59-g8ed1b From b52ce0b6d54aa6b53ab0e3872cd35f51ba036368 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 17 Feb 2025 11:55:49 +0200 Subject: PCI: shpchp: Remove unused logging wrappers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shpchp hotplug driver defines logging wrapper with generic names which are just duplicates of existing generic printk() wrappers. They are also unused so remove them. Link: https://lore.kernel.org/r/20250217095550.2789-2-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/shpchp.h | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index f0e2d2d54d71..10ba0bfac419 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -33,18 +33,6 @@ extern bool shpchp_poll_mode; extern int shpchp_poll_time; extern bool shpchp_debug; -#define dbg(format, arg...) \ -do { \ - if (shpchp_debug) \ - printk(KERN_DEBUG "%s: " format, MY_NAME, ## arg); \ -} while (0) -#define err(format, arg...) \ - printk(KERN_ERR "%s: " format, MY_NAME, ## arg) -#define info(format, arg...) \ - printk(KERN_INFO "%s: " format, MY_NAME, ## arg) -#define warn(format, arg...) \ - printk(KERN_WARNING "%s: " format, MY_NAME, ## arg) - #define ctrl_dbg(ctrl, format, arg...) \ do { \ if (shpchp_debug) \ -- cgit v1.2.3-59-g8ed1b From 588021b28642a1509bdae65ae22329240b39d543 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 17 Feb 2025 11:55:50 +0200 Subject: PCI: shpchp: Remove 'shpchp_debug' module parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "shpchp_debug" module parameter is used to enable debug logging. The generic ability to turn on/off debug prints dynamically covers this use case already so there is no need for module specific debug handling. The ctrl_dbg() wrapper also uses a low-level pci_printk() despite always using KERN_DEBUG level. Remove "shpchp_debug" parameter and convert ctrl_dbg() to use pci_dbg(). From now on, shpchp can be debugged using the normal dynamic debugger by setting CONFIG_DYNAMIC_DEBUG=y and then either adding to kernel cmdline: dyndbg="file drivers/pci/hotplug/shpchp* +p" or using this command on a running kernel: echo 'file drivers/pci/hotplug/shpchp* +p' > /sys/kernel/debug/dynamic_debug/control Link: https://lore.kernel.org/r/20250217095550.2789-3-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/shpchp.h | 6 +----- drivers/pci/hotplug/shpchp_core.c | 3 --- 2 files changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index 10ba0bfac419..a425530e0939 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -34,11 +34,7 @@ extern int shpchp_poll_time; extern bool shpchp_debug; #define ctrl_dbg(ctrl, format, arg...) \ - do { \ - if (shpchp_debug) \ - pci_printk(KERN_DEBUG, ctrl->pci_dev, \ - format, ## arg); \ - } while (0) + pci_dbg(ctrl->pci_dev, format, ## arg) #define ctrl_err(ctrl, format, arg...) \ pci_err(ctrl->pci_dev, format, ## arg) #define ctrl_info(ctrl, format, arg...) \ diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index a10ce7be7f51..0c341453afc6 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -22,7 +22,6 @@ #include "shpchp.h" /* Global variables */ -bool shpchp_debug; bool shpchp_poll_mode; int shpchp_poll_time; @@ -33,10 +32,8 @@ int shpchp_poll_time; MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); -module_param(shpchp_debug, bool, 0644); module_param(shpchp_poll_mode, bool, 0644); module_param(shpchp_poll_time, int, 0644); -MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not"); MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not"); MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds"); -- cgit v1.2.3-59-g8ed1b From cbf937dcadfd571a434f8074d057b32cd14fbea5 Mon Sep 17 00:00:00 2001 From: Daniel Stodden Date: Sun, 22 Dec 2024 19:39:08 -0800 Subject: PCI/ASPM: Fix link state exit during switch upstream function removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before 456d8aa37d0f ("PCI/ASPM: Disable ASPM on MFD function removal to avoid use-after-free"), we would free the ASPM link only after the last function on the bus pertaining to the given link was removed. That was too late. If function 0 is removed before sibling function, link->downstream would point to free'd memory after. After above change, we freed the ASPM parent link state upon any function removal on the bus pertaining to a given link. That is too early. If the link is to a PCIe switch with MFD on the upstream port, then removing functions other than 0 first would free a link which still remains parent_link to the remaining downstream ports. The resulting GPFs are especially frequent during hot-unplug, because pciehp removes devices on the link bus in reverse order. On that switch, function 0 is the virtual P2P bridge to the internal bus. Free exactly when function 0 is removed -- before the parent link is obsolete, but after all subordinate links are gone. Link: https://lore.kernel.org/r/e12898835f25234561c9d7de4435590d957b85d9.1734924854.git.dns@arista.com Fixes: 456d8aa37d0f ("PCI/ASPM: Disable ASPM on MFD function removal to avoid use-after-free") Signed-off-by: Daniel Stodden Signed-off-by: Bjorn Helgaas [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/pcie/aspm.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index e0bc90597dca..25954cc89bf3 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1273,16 +1273,16 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) parent_link = link->parent; /* - * link->downstream is a pointer to the pci_dev of function 0. If - * we remove that function, the pci_dev is about to be deallocated, - * so we can't use link->downstream again. Free the link state to - * avoid this. + * Free the parent link state, no later than function 0 (i.e. + * link->downstream) being removed. * - * If we're removing a non-0 function, it's possible we could - * retain the link state, but PCIe r6.0, sec 7.5.3.7, recommends - * programming the same ASPM Control value for all functions of - * multi-function devices, so disable ASPM for all of them. + * Do not free the link state any earlier. If function 0 is a + * switch upstream port, this link state is parent_link to all + * subordinate ones. */ + if (pdev != link->downstream) + goto out; + pcie_config_aspm_link(link, 0); list_del(&link->sibling); free_link_state(link); @@ -1293,6 +1293,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) pcie_config_aspm_path(parent_link); } + out: mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); } -- cgit v1.2.3-59-g8ed1b From 957f40d039a98d630146f74f94b3f60a40a449e4 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 16 Jan 2025 19:39:11 +0530 Subject: PCI/pwrctrl: Move creation of pwrctrl devices to pci_scan_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current way of creating pwrctrl devices requires iterating through the child devicetree nodes of the PCI bridge in pci_pwrctrl_create_devices(). Even though it works, it creates confusion as there is no symmetry between this and pci_pwrctrl_unregister() function that removes the pwrctrl devices. So to make these two functions symmetric, move the creation of pwrctrl devices to pci_scan_device(). During the scan of each device in a slot, the devicetree node (if exists) for the PCI device will be checked. If it has the supplies populated, then the pwrctrl device will be created. Since the PCI device scan happens so early, there would be no "struct pci_dev" available for the device. So the host bridge is used as the parent of all pwrctrl devices. One nice side effect of this move is that, it is now possible to have pwrctrl devices for PCI bridges as well (to control the supplies of PCI slots). Suggested-by: Lukas Wunner Tested-by: Bartosz Golaszewski Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250116-pci-pwrctrl-slot-v3-1-827473c8fbf4@linaro.org [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/bus.c | 43 ------------------------------------------- drivers/pci/probe.c | 34 ++++++++++++++++++++++++++++++++++ drivers/pci/pwrctrl/core.c | 2 +- 3 files changed, 35 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 98910bc0fcc4..b6851101ac36 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -331,47 +331,6 @@ void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { } void __weak pcibios_bus_add_device(struct pci_dev *pdev) { } -/* - * Create pwrctrl devices (if required) for the PCI devices to handle the power - * state. - */ -static void pci_pwrctrl_create_devices(struct pci_dev *dev) -{ - struct device_node *np = dev_of_node(&dev->dev); - struct device *parent = &dev->dev; - struct platform_device *pdev; - - /* - * First ensure that we are starting from a PCI bridge and it has a - * corresponding devicetree node. - */ - if (np && pci_is_bridge(dev)) { - /* - * Now look for the child PCI device nodes and create pwrctrl - * devices for them. The pwrctrl device drivers will manage the - * power state of the devices. - */ - for_each_available_child_of_node_scoped(np, child) { - /* - * First check whether the pwrctrl device really - * needs to be created or not. This is decided - * based on at least one of the power supplies - * being defined in the devicetree node of the - * device. - */ - if (!of_pci_supply_present(child)) { - pci_dbg(dev, "skipping OF node: %s\n", child->name); - return; - } - - /* Now create the pwrctrl device */ - pdev = of_platform_device_create(child, NULL, parent); - if (!pdev) - pci_err(dev, "failed to create OF node: %s\n", child->name); - } - } -} - /** * pci_bus_add_device - start driver for a single device * @dev: device to add @@ -396,8 +355,6 @@ void pci_bus_add_device(struct pci_dev *dev) pci_proc_attach_device(dev); pci_bridge_d3_update(dev); - pci_pwrctrl_create_devices(dev); - /* * If the PCI device is associated with a pwrctrl device with a * power supply, create a device link between the PCI device and diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b6536ed599c3..95a91778bb5d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -2493,6 +2495,36 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, } EXPORT_SYMBOL(pci_bus_read_dev_vendor_id); +/* + * Create pwrctrl device (if required) for the PCI device to handle the power + * state. + */ +static void pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) +{ + struct pci_host_bridge *host = pci_find_host_bridge(bus); + struct platform_device *pdev; + struct device_node *np; + + np = of_pci_find_child_device(dev_of_node(&bus->dev), devfn); + if (!np || of_find_device_by_node(np)) + return; + + /* + * First check whether the pwrctrl device really needs to be created or + * not. This is decided based on at least one of the power supplies + * being defined in the devicetree node of the device. + */ + if (!of_pci_supply_present(np)) { + pr_debug("PCI/pwrctrl: Skipping OF node: %s\n", np->name); + return; + } + + /* Now create the pwrctrl device */ + pdev = of_platform_device_create(np, NULL, &host->dev); + if (!pdev) + pr_err("PCI/pwrctrl: Failed to create pwrctrl device for OF node: %s\n", np->name); +} + /* * Read the config data for a PCI device, sanity-check it, * and fill in the dev structure. @@ -2502,6 +2534,8 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) struct pci_dev *dev; u32 l; + pci_pwrctrl_create_device(bus, devfn); + if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000)) return NULL; diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index 2fb174db91e5..9cc7e2b7f2b5 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -44,7 +44,7 @@ static void rescan_work_func(struct work_struct *work) struct pci_pwrctrl, work); pci_lock_rescan_remove(); - pci_rescan_bus(to_pci_dev(pwrctrl->dev->parent)->bus); + pci_rescan_bus(to_pci_host_bridge(pwrctrl->dev->parent)->bus); pci_unlock_rescan_remove(); } -- cgit v1.2.3-59-g8ed1b From 2d923930f2e3fe1ecf060169f57980da819a191f Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 16 Jan 2025 19:39:12 +0530 Subject: PCI/pwrctrl: Move pci_pwrctrl_unregister() to pci_destroy_dev() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCI core will try to access the devices even after pci_stop_dev() for things like Data Object Exchange (DOE), ASPM, etc. So, move pci_pwrctrl_unregister() to the near end of pci_destroy_dev() to make sure that the devices are powered down only after the PCI core is done with them. Suggested-by: Lukas Wunner Reviewed-by: Lukas Wunner Tested-by: Bartosz Golaszewski Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250116-pci-pwrctrl-slot-v3-2-827473c8fbf4@linaro.org [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/remove.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index efc37fcb73e2..58859f9d92f7 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -41,7 +41,6 @@ static void pci_stop_dev(struct pci_dev *dev) if (!pci_dev_test_and_clear_added(dev)) return; - pci_pwrctrl_unregister(&dev->dev); device_release_driver(&dev->dev); pci_proc_detach_device(dev); pci_remove_sysfs_dev_files(dev); @@ -64,6 +63,7 @@ static void pci_destroy_dev(struct pci_dev *dev) pci_doe_destroy(dev); pcie_aspm_exit_link_state(dev); pci_bridge_d3_update(dev); + pci_pwrctrl_unregister(&dev->dev); pci_free_resources(dev); put_device(&dev->dev); } -- cgit v1.2.3-59-g8ed1b From 2489eeb777aff943cb93b287385f2e8c68978db0 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 16 Jan 2025 19:39:13 +0530 Subject: PCI/pwrctrl: Skip scanning for the device further if pwrctrl device is created MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pwrctrl core will rescan the bus once the device is powered on. So there is no need to continue scanning for the device further. Reviewed-by: Bartosz Golaszewski Tested-by: Bartosz Golaszewski Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250116-pci-pwrctrl-slot-v3-3-827473c8fbf4@linaro.org Signed-off-by: Krzysztof Wilczyński --- drivers/pci/probe.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 95a91778bb5d..1191b02ae9f8 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2495,11 +2495,7 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, } EXPORT_SYMBOL(pci_bus_read_dev_vendor_id); -/* - * Create pwrctrl device (if required) for the PCI device to handle the power - * state. - */ -static void pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) +static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) { struct pci_host_bridge *host = pci_find_host_bridge(bus); struct platform_device *pdev; @@ -2507,7 +2503,7 @@ static void pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) np = of_pci_find_child_device(dev_of_node(&bus->dev), devfn); if (!np || of_find_device_by_node(np)) - return; + return NULL; /* * First check whether the pwrctrl device really needs to be created or @@ -2516,13 +2512,17 @@ static void pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) */ if (!of_pci_supply_present(np)) { pr_debug("PCI/pwrctrl: Skipping OF node: %s\n", np->name); - return; + return NULL; } /* Now create the pwrctrl device */ pdev = of_platform_device_create(np, NULL, &host->dev); - if (!pdev) - pr_err("PCI/pwrctrl: Failed to create pwrctrl device for OF node: %s\n", np->name); + if (!pdev) { + pr_err("PCI/pwrctrl: Failed to create pwrctrl device for node: %s\n", np->name); + return NULL; + } + + return pdev; } /* @@ -2534,7 +2534,14 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) struct pci_dev *dev; u32 l; - pci_pwrctrl_create_device(bus, devfn); + /* + * Create pwrctrl device (if required) for the PCI device to handle the + * power state. If the pwrctrl device is created, then skip scanning + * further as the pwrctrl core will rescan the bus after powering on + * the device. + */ + if (pci_pwrctrl_create_device(bus, devfn)) + return NULL; if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000)) return NULL; -- cgit v1.2.3-59-g8ed1b From 25a3c220a2b46dbeec1e5a24904d0b038bbc0878 Mon Sep 17 00:00:00 2001 From: Easwar Hariharan Date: Fri, 7 Feb 2025 19:07:15 +0000 Subject: PCI: hv: Correct a comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VF driver controls an endpoint attached to the PCI HyperV controller. An invalidation sent by the PF driver in the host would be delivered *to* the endpoint driver by the controller driver. Thus, correct the wording within the comment. Signed-off-by: Easwar Hariharan Reviewed-by: Manivannan Sadhasivam Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/20250207190716.89995-1-eahariha@linux.microsoft.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pci-hyperv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 6084b38bdda1..3ae3a8a79dcf 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -1356,7 +1356,7 @@ static struct pci_ops hv_pcifront_ops = { * * If the PF driver wishes to initiate communication, it can "invalidate" one or * more of the first 64 blocks. This invalidation is delivered via a callback - * supplied by the VF driver by this driver. + * supplied to the VF driver by this driver. * * No protocol is implied, except that supplied by the PF and VF drivers. */ -- cgit v1.2.3-59-g8ed1b From 75996c92f4de309f855471927e6489f5a354cfd4 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 16 Jan 2025 19:39:15 +0530 Subject: PCI/pwrctrl: Add pwrctrl driver for PCI slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver is used to control the power state of the devices attached to the PCI slots. Currently, it controls the voltage rails of the PCI slots defined in the devicetree node of the root port. The voltage rails for PCI slots are documented in the DT-schema: https://github.com/devicetree-org/dt-schema/blob/v2024.11/dtschema/schemas/pci/pci-bus-common.yaml#L153 Since this driver has to work with different kind of slots (PCIe x1/x4/x8/x16, Mini PCIe, PCI, etc.), the driver is thus using the of_regulator_bulk_get_all() API to obtain the voltage regulators defined in the DT node, instead of hardcoding them. As such, the DT node of the root port should define the relevant supply properties corresponding to the voltage rails of the PCI slot. Tested-by: Bartosz Golaszewski Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250116-pci-pwrctrl-slot-v3-5-827473c8fbf4@linaro.org [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/pwrctrl/Kconfig | 11 ++++++ drivers/pci/pwrctrl/Makefile | 3 ++ drivers/pci/pwrctrl/slot.c | 93 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 drivers/pci/pwrctrl/slot.c (limited to 'drivers') diff --git a/drivers/pci/pwrctrl/Kconfig b/drivers/pci/pwrctrl/Kconfig index 54589bb2403b..990cab67d413 100644 --- a/drivers/pci/pwrctrl/Kconfig +++ b/drivers/pci/pwrctrl/Kconfig @@ -10,3 +10,14 @@ config PCI_PWRCTL_PWRSEQ tristate select POWER_SEQUENCING select PCI_PWRCTL + +config PCI_PWRCTL_SLOT + tristate "PCI Power Control driver for PCI slots" + select PCI_PWRCTL + help + Say Y here to enable the PCI Power Control driver to control the power + state of PCI slots. + + This is a generic driver that controls the power state of different + PCI slots. The voltage regulators powering the rails of the PCI slots + are expected to be defined in the devicetree node of the PCI bridge. diff --git a/drivers/pci/pwrctrl/Makefile b/drivers/pci/pwrctrl/Makefile index 75c7ce531c7e..ddfb12c5aadf 100644 --- a/drivers/pci/pwrctrl/Makefile +++ b/drivers/pci/pwrctrl/Makefile @@ -4,3 +4,6 @@ obj-$(CONFIG_PCI_PWRCTL) += pci-pwrctrl-core.o pci-pwrctrl-core-y := core.o obj-$(CONFIG_PCI_PWRCTL_PWRSEQ) += pci-pwrctrl-pwrseq.o + +obj-$(CONFIG_PCI_PWRCTL_SLOT) += pci-pwrctl-slot.o +pci-pwrctl-slot-y := slot.o diff --git a/drivers/pci/pwrctrl/slot.c b/drivers/pci/pwrctrl/slot.c new file mode 100644 index 000000000000..18becc144913 --- /dev/null +++ b/drivers/pci/pwrctrl/slot.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 Linaro Ltd. + * Author: Manivannan Sadhasivam + */ + +#include +#include +#include +#include +#include +#include +#include + +struct pci_pwrctrl_slot_data { + struct pci_pwrctrl ctx; + struct regulator_bulk_data *supplies; + int num_supplies; +}; + +static void devm_pci_pwrctrl_slot_power_off(void *data) +{ + struct pci_pwrctrl_slot_data *slot = data; + + regulator_bulk_disable(slot->num_supplies, slot->supplies); + regulator_bulk_free(slot->num_supplies, slot->supplies); +} + +static int pci_pwrctrl_slot_probe(struct platform_device *pdev) +{ + struct pci_pwrctrl_slot_data *slot; + struct device *dev = &pdev->dev; + int ret; + + slot = devm_kzalloc(dev, sizeof(*slot), GFP_KERNEL); + if (!slot) + return -ENOMEM; + + ret = of_regulator_bulk_get_all(dev, dev_of_node(dev), + &slot->supplies); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to get slot regulators\n"); + return ret; + } + + slot->num_supplies = ret; + ret = regulator_bulk_enable(slot->num_supplies, slot->supplies); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to enable slot regulators\n"); + goto err_regulator_free; + } + + ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_slot_power_off, + slot); + if (ret) + goto err_regulator_disable; + + pci_pwrctrl_init(&slot->ctx, dev); + + ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->ctx); + if (ret) + return dev_err_probe(dev, ret, "Failed to register pwrctrl driver\n"); + + return 0; + +err_regulator_disable: + regulator_bulk_disable(slot->num_supplies, slot->supplies); +err_regulator_free: + regulator_bulk_free(slot->num_supplies, slot->supplies); + + return ret; +} + +static const struct of_device_id pci_pwrctrl_slot_of_match[] = { + { + .compatible = "pciclass,0604", + }, + { } +}; +MODULE_DEVICE_TABLE(of, pci_pwrctrl_slot_of_match); + +static struct platform_driver pci_pwrctrl_slot_driver = { + .driver = { + .name = "pci-pwrctrl-slot", + .of_match_table = pci_pwrctrl_slot_of_match, + }, + .probe = pci_pwrctrl_slot_probe, +}; +module_platform_driver(pci_pwrctrl_slot_driver); + +MODULE_AUTHOR("Manivannan Sadhasivam "); +MODULE_DESCRIPTION("Generic PCI Power Control driver for PCI Slots"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From b6d7bb0d3bd74b491e2e6fd59c4d5110d06fd63b Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 1 Feb 2025 12:00:18 +0100 Subject: PCI: mediatek-gen3: Remove leftover mac_reset assert for Airoha EN7581 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove a leftover assert for mac_reset line in mtk_pcie_en7581_power_up(). This is not harmful since EN7581 does not requires mac_reset and mac_reset is not defined in EN7581 device tree. Signed-off-by: Lorenzo Bianconi Reviewed-by: Manivannan Sadhasivam Reviewed-by: Philipp Zabel Link: https://lore.kernel.org/r/20250201-pcie-en7581-remove-mac_reset-v2-1-a06786cdc683@kernel.org [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-mediatek-gen3.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index aa24ac9aaecc..0f64e76e2111 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -940,7 +940,6 @@ static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie) */ reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); - reset_control_assert(pcie->mac_reset); /* Wait for the time needed to complete the reset lines assert. */ msleep(PCIE_EN7581_RESET_TIME_MS); -- cgit v1.2.3-59-g8ed1b From 4f13dd9e2b1d2b317bb36704f8a7bd1d3017f7a2 Mon Sep 17 00:00:00 2001 From: Mrinmay Sarkar Date: Thu, 5 Dec 2024 12:24:20 +0530 Subject: PCI: epf-mhi: Update device ID for SA8775P MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update device ID for the Qcom SA8775P SoC. Signed-off-by: Mrinmay Sarkar Link: https://lore.kernel.org/r/20241205065422.2515086-3-quic_msarkar@quicinc.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/endpoint/functions/pci-epf-mhi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c index 54286a40bdfb..6643a88c7a0c 100644 --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c @@ -125,7 +125,7 @@ static const struct pci_epf_mhi_ep_info sm8450_info = { static struct pci_epf_header sa8775p_header = { .vendorid = PCI_VENDOR_ID_QCOM, - .deviceid = 0x0306, /* FIXME: Update deviceid for sa8775p EP */ + .deviceid = 0x0116, .baseclass_code = PCI_CLASS_OTHERS, .interrupt_pin = PCI_INTERRUPT_INTA, }; -- cgit v1.2.3-59-g8ed1b From 9cf8a952d57b422d3ff8a9a0163f8adf694f4b2b Mon Sep 17 00:00:00 2001 From: Tushar Dave Date: Thu, 6 Feb 2025 19:03:38 -0800 Subject: PCI/ACS: Fix 'pci=config_acs=' parameter Commit 47c8846a49ba ("PCI: Extend ACS configurability") introduced bugs that fail to configure ACS ctrl to the value specified by the kernel parameter. Essentially there are two bugs: 1) When ACS is configured for multiple PCI devices using 'config_acs' kernel parameter, it results into error "PCI: Can't parse ACS command line parameter". This is due to a bug that doesn't preserve the ACS mask, but instead overwrites the mask with value 0. For example, using 'config_acs' to configure ACS ctrl for multiple BDFs fails: Kernel command line: pci=config_acs=1111011@0020:02:00.0;101xxxx@0039:00:00.0 "dyndbg=file drivers/pci/pci.c +p" PCI: Can't parse ACS command line parameter pci 0020:02:00.0: ACS mask = 0x007f pci 0020:02:00.0: ACS flags = 0x007b pci 0020:02:00.0: Configured ACS to 0x007b After this fix: Kernel command line: pci=config_acs=1111011@0020:02:00.0;101xxxx@0039:00:00.0 "dyndbg=file drivers/pci/pci.c +p" pci 0020:02:00.0: ACS mask = 0x007f pci 0020:02:00.0: ACS flags = 0x007b pci 0020:02:00.0: ACS control = 0x005f pci 0020:02:00.0: ACS fw_ctrl = 0x0053 pci 0020:02:00.0: Configured ACS to 0x007b pci 0039:00:00.0: ACS mask = 0x0070 pci 0039:00:00.0: ACS flags = 0x0050 pci 0039:00:00.0: ACS control = 0x001d pci 0039:00:00.0: ACS fw_ctrl = 0x0000 pci 0039:00:00.0: Configured ACS to 0x0050 2) In the bit manipulation logic, we copy the bit from the firmware settings when mask bit 0. For example, 'disable_acs_redir' fails to clear all three ACS P2P redir bits due to the wrong bit fiddling: Kernel command line: pci=disable_acs_redir=0020:02:00.0;0030:02:00.0;0039:00:00.0 "dyndbg=file drivers/pci/pci.c +p" pci 0020:02:00.0: ACS mask = 0x002c pci 0020:02:00.0: ACS flags = 0xffd3 pci 0020:02:00.0: Configured ACS to 0xfffb pci 0030:02:00.0: ACS mask = 0x002c pci 0030:02:00.0: ACS flags = 0xffd3 pci 0030:02:00.0: Configured ACS to 0xffdf pci 0039:00:00.0: ACS mask = 0x002c pci 0039:00:00.0: ACS flags = 0xffd3 pci 0039:00:00.0: Configured ACS to 0xffd3 After this fix: Kernel command line: pci=disable_acs_redir=0020:02:00.0;0030:02:00.0;0039:00:00.0 "dyndbg=file drivers/pci/pci.c +p" pci 0020:02:00.0: ACS mask = 0x002c pci 0020:02:00.0: ACS flags = 0xffd3 pci 0020:02:00.0: ACS control = 0x007f pci 0020:02:00.0: ACS fw_ctrl = 0x007b pci 0020:02:00.0: Configured ACS to 0x0053 pci 0030:02:00.0: ACS mask = 0x002c pci 0030:02:00.0: ACS flags = 0xffd3 pci 0030:02:00.0: ACS control = 0x005f pci 0030:02:00.0: ACS fw_ctrl = 0x005f pci 0030:02:00.0: Configured ACS to 0x0053 pci 0039:00:00.0: ACS mask = 0x002c pci 0039:00:00.0: ACS flags = 0xffd3 pci 0039:00:00.0: ACS control = 0x001d pci 0039:00:00.0: ACS fw_ctrl = 0x0000 pci 0039:00:00.0: Configured ACS to 0x0000 Link: https://lore.kernel.org/r/20250207030338.456887-1-tdave@nvidia.com Fixes: 47c8846a49ba ("PCI: Extend ACS configurability") Signed-off-by: Tushar Dave Signed-off-by: Bjorn Helgaas Reviewed-by: Jason Gunthorpe Reviewed-by: Kuppuswamy Sathyanarayanan --- drivers/pci/pci.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 869d204a70a3..23609dd123f9 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -954,8 +954,10 @@ struct pci_acs { }; static void __pci_config_acs(struct pci_dev *dev, struct pci_acs *caps, - const char *p, u16 mask, u16 flags) + const char *p, const u16 acs_mask, const u16 acs_flags) { + u16 flags = acs_flags; + u16 mask = acs_mask; char *delimit; int ret = 0; @@ -963,7 +965,7 @@ static void __pci_config_acs(struct pci_dev *dev, struct pci_acs *caps, return; while (*p) { - if (!mask) { + if (!acs_mask) { /* Check for ACS flags */ delimit = strstr(p, "@"); if (delimit) { @@ -971,6 +973,8 @@ static void __pci_config_acs(struct pci_dev *dev, struct pci_acs *caps, u32 shift = 0; end = delimit - p - 1; + mask = 0; + flags = 0; while (end > -1) { if (*(p + end) == '0') { @@ -1027,10 +1031,14 @@ static void __pci_config_acs(struct pci_dev *dev, struct pci_acs *caps, pci_dbg(dev, "ACS mask = %#06x\n", mask); pci_dbg(dev, "ACS flags = %#06x\n", flags); + pci_dbg(dev, "ACS control = %#06x\n", caps->ctrl); + pci_dbg(dev, "ACS fw_ctrl = %#06x\n", caps->fw_ctrl); - /* If mask is 0 then we copy the bit from the firmware setting. */ - caps->ctrl = (caps->ctrl & ~mask) | (caps->fw_ctrl & mask); - caps->ctrl |= flags; + /* + * For mask bits that are 0, copy them from the firmware setting + * and apply flags for all the mask bits that are 1. + */ + caps->ctrl = (caps->fw_ctrl & ~mask) | (flags & mask); pci_info(dev, "Configured ACS to %#06x\n", caps->ctrl); } -- cgit v1.2.3-59-g8ed1b From fab874e12593b68f9a7fcb1a31a7dcf4829e88f7 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 16 Dec 2024 18:10:12 +0200 Subject: PCI/AER: Descope pci_printk() to aer_printk() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit include/linux/pci.h provides low-level pci_printk() interface that is only used by AER because it needs to print the same message with different levels depending on the error severity. No other PCI code uses that functionality and calls pci_() logging functions directly with the appropriate level. Descope pci_printk() into AER as aer_printk(). Link: https://lore.kernel.org/r/20241216161012.1774-5-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen [bhelgaas: retain pci_printk() for now since shpchp still uses it and I moved those patches to a different branch] Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 508474e17183..ad4206125b86 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -35,6 +36,9 @@ #include "../pci.h" #include "portdrv.h" +#define aer_printk(level, pdev, fmt, arg...) \ + dev_printk(level, &(pdev)->dev, fmt, ##arg) + #define AER_ERROR_SOURCES_MAX 128 #define AER_MAX_TYPEOF_COR_ERRS 16 /* as per PCI_ERR_COR_STATUS */ @@ -686,7 +690,7 @@ static void __aer_print_error(struct pci_dev *dev, if (!errmsg) errmsg = "Unknown Error Bit"; - pci_printk(level, dev, " [%2d] %-22s%s\n", i, errmsg, + aer_printk(level, dev, " [%2d] %-22s%s\n", i, errmsg, info->first_error == i ? " (First)" : ""); } pci_dev_aer_stats_incr(dev, info); @@ -709,11 +713,11 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) level = (info->severity == AER_CORRECTABLE) ? KERN_WARNING : KERN_ERR; - pci_printk(level, dev, "PCIe Bus Error: severity=%s, type=%s, (%s)\n", + aer_printk(level, dev, "PCIe Bus Error: severity=%s, type=%s, (%s)\n", aer_error_severity_string[info->severity], aer_error_layer[layer], aer_agent_string[agent]); - pci_printk(level, dev, " device [%04x:%04x] error status/mask=%08x/%08x\n", + aer_printk(level, dev, " device [%04x:%04x] error status/mask=%08x/%08x\n", dev->vendor, dev->device, info->status, info->mask); __aer_print_error(dev, info); -- cgit v1.2.3-59-g8ed1b From 79c731e20d7401cc39e4f0e633b135b966a04f62 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Fri, 7 Feb 2025 18:18:35 +0200 Subject: PCI: Track Flit Mode Status & print it with link status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCIe r6.0 added Flit mode, which mainly alters HW behavior, but there are some OS visible changes. The OS visible changes include differences in the layout of some capabilities and interpretation of the TLP headers (in diagnostics situations). To be able to determine which mode the PCIe Link is using, store the Flit Mode Status (PCIe r6.1 sec 7.5.3.20) information in addition to the Link speed into struct pci_bus in pcie_update_link_speed(). Link: https://lore.kernel.org/r/20250207161836.2755-2-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen [bhelgaas: use unsigned int:1 instead of bool, update flit_mode setting] Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_hpc.c | 5 +++-- drivers/pci/pci.c | 12 ++++++++---- drivers/pci/pci.h | 3 ++- drivers/pci/probe.c | 5 +++-- include/linux/pci.h | 1 + 5 files changed, 17 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index bb5a8d9f03ad..10130ac9f979 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -292,7 +292,7 @@ int pciehp_check_link_status(struct controller *ctrl) { struct pci_dev *pdev = ctrl_dev(ctrl); bool found; - u16 lnk_status; + u16 lnk_status, linksta2; if (!pcie_wait_for_link(pdev, true)) { ctrl_info(ctrl, "Slot(%s): No link\n", slot_name(ctrl)); @@ -319,7 +319,8 @@ int pciehp_check_link_status(struct controller *ctrl) return -1; } - __pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA2, &linksta2); + __pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status, linksta2); if (!found) { ctrl_info(ctrl, "Slot(%s): No device found\n", diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 869d204a70a3..313c66863752 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -6190,21 +6190,25 @@ void __pcie_print_link_status(struct pci_dev *dev, bool verbose) enum pci_bus_speed speed, speed_cap; struct pci_dev *limiting_dev = NULL; u32 bw_avail, bw_cap; + char *flit_mode = ""; bw_cap = pcie_bandwidth_capable(dev, &speed_cap, &width_cap); bw_avail = pcie_bandwidth_available(dev, &limiting_dev, &speed, &width); + if (dev->bus && dev->bus->flit_mode) + flit_mode = ", in Flit mode"; + if (bw_avail >= bw_cap && verbose) - pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth (%s x%d link)\n", + pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth (%s x%d link)%s\n", bw_cap / 1000, bw_cap % 1000, - pci_speed_string(speed_cap), width_cap); + pci_speed_string(speed_cap), width_cap, flit_mode); else if (bw_avail < bw_cap) - pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth, limited by %s x%d link at %s (capable of %u.%03u Gb/s with %s x%d link)\n", + pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth, limited by %s x%d link at %s (capable of %u.%03u Gb/s with %s x%d link)%s\n", bw_avail / 1000, bw_avail % 1000, pci_speed_string(speed), width, limiting_dev ? pci_name(limiting_dev) : "", bw_cap / 1000, bw_cap % 1000, - pci_speed_string(speed_cap), width_cap); + pci_speed_string(speed_cap), width_cap, flit_mode); } /** diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 01e51db8d285..9da7da58e0de 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -406,9 +406,10 @@ const char *pci_speed_string(enum pci_bus_speed speed); void __pcie_print_link_status(struct pci_dev *dev, bool verbose); void pcie_report_downtraining(struct pci_dev *dev); -static inline void __pcie_update_link_speed(struct pci_bus *bus, u16 linksta) +static inline void __pcie_update_link_speed(struct pci_bus *bus, u16 linksta, u16 linksta2) { bus->cur_bus_speed = pcie_link_speed[linksta & PCI_EXP_LNKSTA_CLS]; + bus->flit_mode = (linksta2 & PCI_EXP_LNKSTA2_FLIT) ? 1 : 0; } void pcie_update_link_speed(struct pci_bus *bus); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b6536ed599c3..e6f11498a345 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -788,10 +788,11 @@ EXPORT_SYMBOL_GPL(pci_speed_string); void pcie_update_link_speed(struct pci_bus *bus) { struct pci_dev *bridge = bus->self; - u16 linksta; + u16 linksta, linksta2; pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta); - __pcie_update_link_speed(bus, linksta); + pcie_capability_read_word(bridge, PCI_EXP_LNKSTA2, &linksta2); + __pcie_update_link_speed(bus, linksta, linksta2); } EXPORT_SYMBOL_GPL(pcie_update_link_speed); diff --git a/include/linux/pci.h b/include/linux/pci.h index 47b31ad724fa..e4bf67bf8172 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -681,6 +681,7 @@ struct pci_bus { struct bin_attribute *legacy_mem; /* Legacy mem */ unsigned int is_added:1; unsigned int unsafe_warn:1; /* warned about RW1C config write */ + unsigned int flit_mode:1; /* Link in Flit mode */ }; #define to_pci_bus(n) container_of(n, struct pci_bus, dev) -- cgit v1.2.3-59-g8ed1b From 7e077e6707b3428562ba30d883ff8f54e98dc18b Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Fri, 7 Feb 2025 18:18:36 +0200 Subject: PCI/ERR: Handle TLP Log in Flit mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flit mode introduced in PCIe r6.0 alters how the TLP Header Log is presented through AER and DPC Capability registers. The TLP Prefix Log Register is not present with Flit mode, and the register becomes an extension of the TLP Header Log (PCIe r6.1 secs 7.8.4.12 & 7.9.14.13). Adapt pcie_read_tlp_log() and struct pcie_tlp_log to read and store the extended TLP Header Log when the Link is in Flit mode. As the Prefix Log and Extended TLP Header are not present at the same time, a C union can be used. Determining whether the error occurred while the Link was in Flit mode is a bit complicated. In case of AER, the Advanced Error Capabilities and Control Register directly tells whether the error was logged in Flit mode or not (PCIe r6.1 sec 7.8.4.7). The DPC Capability (PCIe r6.1 sec 7.9.14), unfortunately, does not contain the same information. Unlike AER, the DPC Capability does not provide a way to discern whether the error was logged in Flit mode (this is confirmed by PCI WG to be an oversight in the spec). DPC will bring the Link down immediately following an error, which makes it impossible to acquire the Flit Mode Status directly from the Link Status 2 register because Flit Mode Status is only set in certain Link states (PCIe r6.1 sec 7.5.3.20). As a workaround, use the flit_mode value stored into the struct pci_bus. Link: https://lore.kernel.org/r/20250207161836.2755-3-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.h | 3 ++- drivers/pci/pcie/aer.c | 1 + drivers/pci/pcie/dpc.c | 18 +++++++++++--- drivers/pci/pcie/tlp.c | 56 +++++++++++++++++++++++++++++-------------- include/linux/aer.h | 12 ++++++++-- include/ras/ras_event.h | 12 +++++----- include/uapi/linux/pci_regs.h | 6 ++++- 7 files changed, 77 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 9da7da58e0de..b8911d1e10dc 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -554,7 +554,8 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info); void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2, - unsigned int tlp_len, struct pcie_tlp_log *log); + unsigned int tlp_len, bool flit, + struct pcie_tlp_log *log); unsigned int aer_tlp_log_len(struct pci_dev *dev, u32 aercc); void pcie_print_tlp_log(const struct pci_dev *dev, const struct pcie_tlp_log *log, const char *pfx); diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index ad4206125b86..9cff7069577e 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1249,6 +1249,7 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG, aer + PCI_ERR_PREFIX_LOG, aer_tlp_log_len(dev, aercc), + aercc & PCI_ERR_CAP_TLP_LOG_FLIT, &info->tlp); } } diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c index 242cabd5eeeb..df42f15c9829 100644 --- a/drivers/pci/pcie/dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -219,7 +219,9 @@ static void dpc_process_rp_pio_error(struct pci_dev *pdev) goto clear_status; pcie_read_tlp_log(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG, cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG, - dpc_tlp_log_len(pdev), &tlp_log); + dpc_tlp_log_len(pdev), + pdev->subordinate->flit_mode, + &tlp_log); pcie_print_tlp_log(pdev, &tlp_log, dev_fmt("")); if (pdev->dpc_rp_log_size < PCIE_STD_NUM_TLP_HEADERLOG + 1) @@ -398,11 +400,21 @@ void pci_dpc_init(struct pci_dev *pdev) /* Quirks may set dpc_rp_log_size if device or firmware is buggy */ if (!pdev->dpc_rp_log_size) { + u16 flags; + int ret; + + ret = pcie_capability_read_word(pdev, PCI_EXP_FLAGS, &flags); + if (ret) + return; + pdev->dpc_rp_log_size = FIELD_GET(PCI_EXP_DPC_RP_PIO_LOG_SIZE, cap); + if (FIELD_GET(PCI_EXP_FLAGS_FLIT, flags)) + pdev->dpc_rp_log_size += FIELD_GET(PCI_EXP_DPC_RP_PIO_LOG_SIZE4, + cap) << 4; + if (pdev->dpc_rp_log_size < PCIE_STD_NUM_TLP_HEADERLOG || - pdev->dpc_rp_log_size > PCIE_STD_NUM_TLP_HEADERLOG + 1 + - PCIE_STD_MAX_TLP_PREFIXLOG) { + pdev->dpc_rp_log_size > PCIE_STD_MAX_TLP_HEADERLOG + 1) { pci_err(pdev, "RP PIO log size %u is invalid\n", pdev->dpc_rp_log_size); pdev->dpc_rp_log_size = 0; diff --git a/drivers/pci/pcie/tlp.c b/drivers/pci/pcie/tlp.c index 0860b5da837f..890d5391d7f5 100644 --- a/drivers/pci/pcie/tlp.c +++ b/drivers/pci/pcie/tlp.c @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -21,6 +22,9 @@ */ unsigned int aer_tlp_log_len(struct pci_dev *dev, u32 aercc) { + if (aercc & PCI_ERR_CAP_TLP_LOG_FLIT) + return FIELD_GET(PCI_ERR_CAP_TLP_LOG_SIZE, aercc); + return PCIE_STD_NUM_TLP_HEADERLOG + ((aercc & PCI_ERR_CAP_PREFIX_LOG_PRESENT) ? dev->eetlp_prefix_max : 0); @@ -49,6 +53,7 @@ unsigned int dpc_tlp_log_len(struct pci_dev *dev) * @where: PCI Config offset of TLP Header Log * @where2: PCI Config offset of TLP Prefix Log * @tlp_len: TLP Log length (Header Log + TLP Prefix Log in DWORDs) + * @flit: TLP Logged in Flit mode * @log: TLP Log structure to fill * * Fill @log from TLP Header Log registers, e.g., AER or DPC. @@ -56,28 +61,34 @@ unsigned int dpc_tlp_log_len(struct pci_dev *dev) * Return: 0 on success and filled TLP Log structure, <0 on error. */ int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2, - unsigned int tlp_len, struct pcie_tlp_log *log) + unsigned int tlp_len, bool flit, struct pcie_tlp_log *log) { unsigned int i; int off, ret; - u32 *to; + + if (tlp_len > ARRAY_SIZE(log->dw)) + tlp_len = ARRAY_SIZE(log->dw); memset(log, 0, sizeof(*log)); for (i = 0; i < tlp_len; i++) { - if (i < PCIE_STD_NUM_TLP_HEADERLOG) { + if (i < PCIE_STD_NUM_TLP_HEADERLOG) off = where + i * 4; - to = &log->dw[i]; - } else { + else off = where2 + (i - PCIE_STD_NUM_TLP_HEADERLOG) * 4; - to = &log->prefix[i - PCIE_STD_NUM_TLP_HEADERLOG]; - } - ret = pci_read_config_dword(dev, off, to); + ret = pci_read_config_dword(dev, off, &log->dw[i]); if (ret) return pcibios_err_to_errno(ret); } + /* + * Hard-code non-Flit mode to 4 DWORDs, for now. The exact length + * can only be known if the TLP is parsed. + */ + log->header_len = flit ? tlp_len : 4; + log->flit = flit; + return 0; } @@ -94,22 +105,31 @@ int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2, void pcie_print_tlp_log(const struct pci_dev *dev, const struct pcie_tlp_log *log, const char *pfx) { - char buf[11 * (PCIE_STD_NUM_TLP_HEADERLOG + ARRAY_SIZE(log->prefix)) + - sizeof(EE_PREFIX_STR)]; + /* EE_PREFIX_STR fits the extended DW space needed for the Flit mode */ + char buf[11 * PCIE_STD_MAX_TLP_HEADERLOG + 1]; unsigned int i; int len; len = scnprintf(buf, sizeof(buf), "%#010x %#010x %#010x %#010x", log->dw[0], log->dw[1], log->dw[2], log->dw[3]); - if (log->prefix[0]) - len += scnprintf(buf + len, sizeof(buf) - len, EE_PREFIX_STR); - for (i = 0; i < ARRAY_SIZE(log->prefix); i++) { - if (!log->prefix[i]) - break; - len += scnprintf(buf + len, sizeof(buf) - len, - " %#010x", log->prefix[i]); + if (log->flit) { + for (i = PCIE_STD_NUM_TLP_HEADERLOG; i < log->header_len; i++) { + len += scnprintf(buf + len, sizeof(buf) - len, + " %#010x", log->dw[i]); + } + } else { + if (log->prefix[0]) + len += scnprintf(buf + len, sizeof(buf) - len, + EE_PREFIX_STR); + for (i = 0; i < ARRAY_SIZE(log->prefix); i++) { + if (!log->prefix[i]) + break; + len += scnprintf(buf + len, sizeof(buf) - len, + " %#010x", log->prefix[i]); + } } - pci_err(dev, "%sTLP Header: %s\n", pfx, buf); + pci_err(dev, "%sTLP Header%s: %s\n", pfx, + log->flit ? " (Flit)" : "", buf); } diff --git a/include/linux/aer.h b/include/linux/aer.h index 947b63091902..02940be66324 100644 --- a/include/linux/aer.h +++ b/include/linux/aer.h @@ -22,12 +22,20 @@ */ #define PCIE_STD_NUM_TLP_HEADERLOG 4 #define PCIE_STD_MAX_TLP_PREFIXLOG 4 +#define PCIE_STD_MAX_TLP_HEADERLOG (PCIE_STD_NUM_TLP_HEADERLOG + 10) struct pci_dev; struct pcie_tlp_log { - u32 dw[PCIE_STD_NUM_TLP_HEADERLOG]; - u32 prefix[PCIE_STD_MAX_TLP_PREFIXLOG]; + union { + u32 dw[PCIE_STD_MAX_TLP_HEADERLOG]; + struct { + u32 _do_not_use[PCIE_STD_NUM_TLP_HEADERLOG]; + u32 prefix[PCIE_STD_MAX_TLP_PREFIXLOG]; + }; + }; + u8 header_len; /* Length of the Logged TLP Header in DWORDs */ + bool flit; /* TLP was logged when in Flit mode */ }; struct aer_capability_regs { diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h index e5f7ee0864e7..14c9f943d53f 100644 --- a/include/ras/ras_event.h +++ b/include/ras/ras_event.h @@ -309,7 +309,7 @@ TRACE_EVENT(aer_event, __field( u32, status ) __field( u8, severity ) __field( u8, tlp_header_valid) - __array( u32, tlp_header, 4 ) + __array( u32, tlp_header, PCIE_STD_MAX_TLP_HEADERLOG) ), TP_fast_assign( @@ -318,10 +318,10 @@ TRACE_EVENT(aer_event, __entry->severity = severity; __entry->tlp_header_valid = tlp_header_valid; if (tlp_header_valid) { - __entry->tlp_header[0] = tlp->dw[0]; - __entry->tlp_header[1] = tlp->dw[1]; - __entry->tlp_header[2] = tlp->dw[2]; - __entry->tlp_header[3] = tlp->dw[3]; + int i; + + for (i = 0; i < PCIE_STD_MAX_TLP_HEADERLOG; i++) + __entry->tlp_header[i] = tlp->dw[i]; } ), @@ -334,7 +334,7 @@ TRACE_EVENT(aer_event, __print_flags(__entry->status, "|", aer_correctable_errors) : __print_flags(__entry->status, "|", aer_uncorrectable_errors), __entry->tlp_header_valid ? - __print_array(__entry->tlp_header, 4, 4) : + __print_array(__entry->tlp_header, PCIE_STD_MAX_TLP_HEADERLOG, 4) : "Not available") ); diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 3445c4970e4d..2c801c0c968a 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -486,6 +486,7 @@ #define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ #define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ #define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ +#define PCI_EXP_FLAGS_FLIT 0x8000 /* Flit Mode Supported */ #define PCI_EXP_DEVCAP 0x04 /* Device capabilities */ #define PCI_EXP_DEVCAP_PAYLOAD 0x00000007 /* Max_Payload_Size */ #define PCI_EXP_DEVCAP_PHANTOM 0x00000018 /* Phantom functions */ @@ -795,6 +796,8 @@ #define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ #define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ #define PCI_ERR_CAP_PREFIX_LOG_PRESENT 0x00000800 /* TLP Prefix Log Present */ +#define PCI_ERR_CAP_TLP_LOG_FLIT 0x00040000 /* TLP was logged in Flit Mode */ +#define PCI_ERR_CAP_TLP_LOG_SIZE 0x00f80000 /* Logged TLP Size (only in Flit mode) */ #define PCI_ERR_HEADER_LOG 0x1c /* Header Log Register (16 bytes) */ #define PCI_ERR_ROOT_COMMAND 0x2c /* Root Error Command */ #define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 /* Correctable Err Reporting Enable */ @@ -1061,8 +1064,9 @@ #define PCI_EXP_DPC_CAP_RP_EXT 0x0020 /* Root Port Extensions */ #define PCI_EXP_DPC_CAP_POISONED_TLP 0x0040 /* Poisoned TLP Egress Blocking Supported */ #define PCI_EXP_DPC_CAP_SW_TRIGGER 0x0080 /* Software Triggering Supported */ -#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0x0F00 /* RP PIO Log Size */ +#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0x0F00 /* RP PIO Log Size [3:0] */ #define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */ +#define PCI_EXP_DPC_RP_PIO_LOG_SIZE4 0x2000 /* RP PIO Log Size [4] */ #define PCI_EXP_DPC_CTL 0x06 /* DPC control */ #define PCI_EXP_DPC_CTL_EN_FATAL 0x0001 /* Enable trigger on ERR_FATAL message */ -- cgit v1.2.3-59-g8ed1b From 8ff4574cf73dba061d4a07e3b6094c5ecb2d2efe Mon Sep 17 00:00:00 2001 From: Guilherme Giacomo Simoes Date: Mon, 17 Feb 2025 15:56:38 -0300 Subject: PCI: cpcihp: Remove unused .get_power() and .set_power() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .get_power() and .set_power() function pointers in struct cpci_hp_controller_ops were declared but never implemented by any driver. Thus, to improve code readability and reduce resource usage, remove these pointers and the code that has never been used. Link: https://lore.kernel.org/r/20250217185638.398925-1-trintaeoitogc@gmail.com Signed-off-by: Guilherme Giacomo Simoes Signed-off-by: Bjorn Helgaas [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/hotplug/cpci_hotplug.h | 2 -- drivers/pci/hotplug/cpci_hotplug_core.c | 17 ++--------------- 2 files changed, 2 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/cpci_hotplug.h b/drivers/pci/hotplug/cpci_hotplug.h index 03fa39ab0c88..a31d6b62f523 100644 --- a/drivers/pci/hotplug/cpci_hotplug.h +++ b/drivers/pci/hotplug/cpci_hotplug.h @@ -44,8 +44,6 @@ struct cpci_hp_controller_ops { int (*enable_irq)(void); int (*disable_irq)(void); int (*check_irq)(void *dev_id); - u8 (*get_power)(struct slot *slot); - int (*set_power)(struct slot *slot, int value); }; struct cpci_hp_controller { diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c index d0559d2faf50..dd93e53ea7c2 100644 --- a/drivers/pci/hotplug/cpci_hotplug_core.c +++ b/drivers/pci/hotplug/cpci_hotplug_core.c @@ -71,13 +71,10 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) { struct slot *slot = to_slot(hotplug_slot); - int retval = 0; dbg("%s - physical_slot = %s", __func__, slot_name(slot)); - if (controller->ops->set_power) - retval = controller->ops->set_power(slot, 1); - return retval; + return 0; } static int @@ -109,12 +106,6 @@ disable_slot(struct hotplug_slot *hotplug_slot) } cpci_led_on(slot); - if (controller->ops->set_power) { - retval = controller->ops->set_power(slot, 0); - if (retval) - goto disable_error; - } - slot->adapter_status = 0; if (slot->extracting) { @@ -129,11 +120,7 @@ disable_error: static u8 cpci_get_power_status(struct slot *slot) { - u8 power = 1; - - if (controller->ops->get_power) - power = controller->ops->get_power(slot); - return power; + return 1; } static int -- cgit v1.2.3-59-g8ed1b From 9d52691f899b843d1e0afa8fca19496ace76b8c7 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 31 Dec 2024 18:32:24 +0530 Subject: PCI: qcom-ep: Mark BAR0/BAR2 as 64bit BARs and BAR1/BAR3 as RESERVED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On all Qcom endpoint SoCs, BAR0/BAR2 are 64bit BARs by default and software cannot change the type. So, mark the those BARs as 64bit BARs and also mark the successive BAR1/BAR3 as RESERVED BARs so that the EPF drivers cannot use them. Cc: stable+noautosel@kernel.org # depends on patch introducing only_64bit flag Fixes: f55fee56a631 ("PCI: qcom-ep: Add Qualcomm PCIe Endpoint controller driver") Reviewed-by: Dmitry Baryshkov Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20241231130224.38206-3-manivannan.sadhasivam@linaro.org [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/dwc/pcie-qcom-ep.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index c08f64d7a825..01d3862d7003 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -825,6 +825,10 @@ static const struct pci_epc_features qcom_pcie_epc_features = { .msi_capable = true, .msix_capable = false, .align = SZ_4K, + .bar[BAR_0] = { .only_64bit = true, }, + .bar[BAR_1] = { .type = BAR_RESERVED, }, + .bar[BAR_2] = { .only_64bit = true, }, + .bar[BAR_3] = { .type = BAR_RESERVED, }, }; static const struct pci_epc_features * -- cgit v1.2.3-59-g8ed1b From 2df181e1aea4628a8fd257f866026625d0519627 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 23 Jan 2025 00:29:55 +0200 Subject: PCI: brcmstb: Fix missing of_node_put() in brcm_pcie_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A call to of_parse_phandle() is incrementing the refcount, and as such, the of_node_put() must be called when the reference is no longer needed. Thus, refactor the existing code and add a missing of_node_put() call following the check to ensure that "msi_np" matches "pcie->np" and after MSI initialization, but only if the MSI support is enabled system-wide. Cc: stable@vger.kernel.org # v5.10+ Fixes: 40ca1bf580ef ("PCI: brcmstb: Add MSI support") Signed-off-by: Stanimir Varbanov Reviewed-by: Florian Fainelli Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250122222955.1752778-1-svarbanov@suse.de [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-brcmstb.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index e733a27dc8df..dcaaa7fd9faf 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1784,7 +1784,7 @@ static struct pci_ops brcm7425_pcie_ops = { static int brcm_pcie_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node, *msi_np; + struct device_node *np = pdev->dev.of_node; struct pci_host_bridge *bridge; const struct pcie_cfg_data *data; struct brcm_pcie *pcie; @@ -1888,9 +1888,14 @@ static int brcm_pcie_probe(struct platform_device *pdev) goto fail; } - msi_np = of_parse_phandle(pcie->np, "msi-parent", 0); - if (pci_msi_enabled() && msi_np == pcie->np) { - ret = brcm_pcie_enable_msi(pcie); + if (pci_msi_enabled()) { + struct device_node *msi_np = of_parse_phandle(pcie->np, "msi-parent", 0); + + if (msi_np == pcie->np) + ret = brcm_pcie_enable_msi(pcie); + + of_node_put(msi_np); + if (ret) { dev_err(pcie->dev, "probe of internal MSI failed"); goto fail; -- cgit v1.2.3-59-g8ed1b From 32c6c054661a9b454837e3df409e9bd8fe9ba5f9 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 24 Feb 2025 10:35:55 +0200 Subject: irqchip: Add Broadcom BCM2712 MSI-X interrupt controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an interrupt controller driver for MSI-X Interrupt Peripheral (MIP) hardware block found in BCM2712. The interrupt controller is used to handle MSI-X interrupts from peripherials behind PCIe endpoints like RPi1 south bridge found in RPi5. There are two MIPs on BCM2712, the first has 64 consecutive SPIs assigned to 64 output vectors, and the second has 17 SPIs, but only 8 of them are consecutive starting at the 8th output vector. Signed-off-by: Stanimir Varbanov Reviewed-by: Thomas Gleixner Tested-by: Ivan T. Ivanov Link: https://lore.kernel.org/r/20250224083559.47645-4-svarbanov@suse.de [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/irqchip/Kconfig | 16 +++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-bcm2712-mip.c | 292 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 drivers/irqchip/irq-bcm2712-mip.c (limited to 'drivers') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index be063bfb50c4..ed15a8798ebd 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -109,6 +109,22 @@ config I8259 bool select IRQ_DOMAIN +config BCM2712_MIP + tristate "Broadcom BCM2712 MSI-X Interrupt Peripheral support" + depends on ARCH_BRCMSTB || COMPILE_TEST + default m if ARCH_BRCMSTB + depends on ARM_GIC + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN_HIERARCHY + select GENERIC_MSI_IRQ + select IRQ_MSI_LIB + help + Enable support for the Broadcom BCM2712 MSI-X target peripheral + (MIP) needed by brcmstb PCIe to handle MSI-X interrupts on + Raspberry Pi 5. + + If unsure say n. + config BCM6345_L1_IRQ bool select GENERIC_IRQ_CHIP diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 25e9ad29b8c4..411385c4f3ad 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o obj-$(CONFIG_XILINX_INTC) += irq-xilinx-intc.o obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o +obj-$(CONFIG_BCM2712_MIP) += irq-bcm2712-mip.o obj-$(CONFIG_BCM6345_L1_IRQ) += irq-bcm6345-l1.o obj-$(CONFIG_BCM7038_L1_IRQ) += irq-bcm7038-l1.o obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o diff --git a/drivers/irqchip/irq-bcm2712-mip.c b/drivers/irqchip/irq-bcm2712-mip.c new file mode 100644 index 000000000000..49a19db2d1e1 --- /dev/null +++ b/drivers/irqchip/irq-bcm2712-mip.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 Raspberry Pi Ltd., All Rights Reserved. + * Copyright (c) 2024 SUSE + */ + +#include +#include +#include +#include +#include +#include + +#include "irq-msi-lib.h" + +#define MIP_INT_RAISE 0x00 +#define MIP_INT_CLEAR 0x10 +#define MIP_INT_CFGL_HOST 0x20 +#define MIP_INT_CFGH_HOST 0x30 +#define MIP_INT_MASKL_HOST 0x40 +#define MIP_INT_MASKH_HOST 0x50 +#define MIP_INT_MASKL_VPU 0x60 +#define MIP_INT_MASKH_VPU 0x70 +#define MIP_INT_STATUSL_HOST 0x80 +#define MIP_INT_STATUSH_HOST 0x90 +#define MIP_INT_STATUSL_VPU 0xa0 +#define MIP_INT_STATUSH_VPU 0xb0 + +/** + * struct mip_priv - MSI-X interrupt controller data + * @lock: Used to protect bitmap alloc/free + * @base: Base address of MMIO area + * @msg_addr: PCIe MSI-X address + * @msi_base: MSI base + * @num_msis: Count of MSIs + * @msi_offset: MSI offset + * @bitmap: A bitmap for hwirqs + * @parent: Parent domain (GIC) + * @dev: A device pointer + */ +struct mip_priv { + spinlock_t lock; + void __iomem *base; + u64 msg_addr; + u32 msi_base; + u32 num_msis; + u32 msi_offset; + unsigned long *bitmap; + struct irq_domain *parent; + struct device *dev; +}; + +static void mip_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) +{ + struct mip_priv *mip = irq_data_get_irq_chip_data(d); + + msg->address_hi = upper_32_bits(mip->msg_addr); + msg->address_lo = lower_32_bits(mip->msg_addr); + msg->data = d->hwirq; +} + +static struct irq_chip mip_middle_irq_chip = { + .name = "MIP", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_compose_msi_msg = mip_compose_msi_msg, +}; + +static int mip_alloc_hwirq(struct mip_priv *mip, unsigned int nr_irqs) +{ + guard(spinlock)(&mip->lock); + return bitmap_find_free_region(mip->bitmap, mip->num_msis, ilog2(nr_irqs)); +} + +static void mip_free_hwirq(struct mip_priv *mip, unsigned int hwirq, + unsigned int nr_irqs) +{ + guard(spinlock)(&mip->lock); + bitmap_release_region(mip->bitmap, hwirq, ilog2(nr_irqs)); +} + +static int mip_middle_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + struct mip_priv *mip = domain->host_data; + struct irq_fwspec fwspec = {0}; + unsigned int hwirq, i; + struct irq_data *irqd; + int irq, ret; + + irq = mip_alloc_hwirq(mip, nr_irqs); + if (irq < 0) + return irq; + + hwirq = irq + mip->msi_offset; + + fwspec.fwnode = domain->parent->fwnode; + fwspec.param_count = 3; + fwspec.param[0] = 0; + fwspec.param[1] = hwirq + mip->msi_base; + fwspec.param[2] = IRQ_TYPE_EDGE_RISING; + + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &fwspec); + if (ret) + goto err_free_hwirq; + + for (i = 0; i < nr_irqs; i++) { + irqd = irq_domain_get_irq_data(domain->parent, virq + i); + irqd->chip->irq_set_type(irqd, IRQ_TYPE_EDGE_RISING); + + ret = irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &mip_middle_irq_chip, mip); + if (ret) + goto err_free; + + irqd = irq_get_irq_data(virq + i); + irqd_set_single_target(irqd); + irqd_set_affinity_on_activate(irqd); + } + + return 0; + +err_free: + irq_domain_free_irqs_parent(domain, virq, nr_irqs); +err_free_hwirq: + mip_free_hwirq(mip, irq, nr_irqs); + return ret; +} + +static void mip_middle_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *irqd = irq_domain_get_irq_data(domain, virq); + struct mip_priv *mip; + unsigned int hwirq; + + if (!irqd) + return; + + mip = irq_data_get_irq_chip_data(irqd); + hwirq = irqd_to_hwirq(irqd); + irq_domain_free_irqs_parent(domain, virq, nr_irqs); + mip_free_hwirq(mip, hwirq - mip->msi_offset, nr_irqs); +} + +static const struct irq_domain_ops mip_middle_domain_ops = { + .select = msi_lib_irq_domain_select, + .alloc = mip_middle_domain_alloc, + .free = mip_middle_domain_free, +}; + +#define MIP_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS | \ + MSI_FLAG_PCI_MSI_MASK_PARENT) + +#define MIP_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_MULTI_PCI_MSI | \ + MSI_FLAG_PCI_MSIX) + +static const struct msi_parent_ops mip_msi_parent_ops = { + .supported_flags = MIP_MSI_FLAGS_SUPPORTED, + .required_flags = MIP_MSI_FLAGS_REQUIRED, + .bus_select_token = DOMAIN_BUS_GENERIC_MSI, + .bus_select_mask = MATCH_PCI_MSI, + .prefix = "MIP-MSI-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + +static int mip_init_domains(struct mip_priv *mip, struct device_node *np) +{ + struct irq_domain *middle; + + middle = irq_domain_add_hierarchy(mip->parent, 0, mip->num_msis, np, + &mip_middle_domain_ops, mip); + if (!middle) + return -ENOMEM; + + irq_domain_update_bus_token(middle, DOMAIN_BUS_GENERIC_MSI); + middle->dev = mip->dev; + middle->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; + middle->msi_parent_ops = &mip_msi_parent_ops; + + /* + * All MSI-X unmasked for the host, masked for the VPU, and edge-triggered. + */ + writel(0, mip->base + MIP_INT_MASKL_HOST); + writel(0, mip->base + MIP_INT_MASKH_HOST); + writel(~0, mip->base + MIP_INT_MASKL_VPU); + writel(~0, mip->base + MIP_INT_MASKH_VPU); + writel(~0, mip->base + MIP_INT_CFGL_HOST); + writel(~0, mip->base + MIP_INT_CFGH_HOST); + + return 0; +} + +static int mip_parse_dt(struct mip_priv *mip, struct device_node *np) +{ + struct of_phandle_args args; + u64 size; + int ret; + + ret = of_property_read_u32(np, "brcm,msi-offset", &mip->msi_offset); + if (ret) + mip->msi_offset = 0; + + ret = of_parse_phandle_with_args(np, "msi-ranges", "#interrupt-cells", + 0, &args); + if (ret) + return ret; + + ret = of_property_read_u32_index(np, "msi-ranges", args.args_count + 1, + &mip->num_msis); + if (ret) + goto err_put; + + ret = of_property_read_reg(np, 1, &mip->msg_addr, &size); + if (ret) + goto err_put; + + mip->msi_base = args.args[1]; + + mip->parent = irq_find_host(args.np); + if (!mip->parent) + ret = -EINVAL; + +err_put: + of_node_put(args.np); + return ret; +} + +static int __init mip_of_msi_init(struct device_node *node, struct device_node *parent) +{ + struct platform_device *pdev; + struct mip_priv *mip; + int ret; + + pdev = of_find_device_by_node(node); + of_node_put(node); + if (!pdev) + return -EPROBE_DEFER; + + mip = kzalloc(sizeof(*mip), GFP_KERNEL); + if (!mip) + return -ENOMEM; + + spin_lock_init(&mip->lock); + mip->dev = &pdev->dev; + + ret = mip_parse_dt(mip, node); + if (ret) + goto err_priv; + + mip->base = of_iomap(node, 0); + if (!mip->base) { + ret = -ENXIO; + goto err_priv; + } + + mip->bitmap = bitmap_zalloc(mip->num_msis, GFP_KERNEL); + if (!mip->bitmap) { + ret = -ENOMEM; + goto err_base; + } + + ret = mip_init_domains(mip, node); + if (ret) + goto err_map; + + dev_dbg(&pdev->dev, "MIP: MSI-X count: %u, base: %u, offset: %u, msg_addr: %llx\n", + mip->num_msis, mip->msi_base, mip->msi_offset, mip->msg_addr); + + return 0; + +err_map: + bitmap_free(mip->bitmap); +err_base: + iounmap(mip->base); +err_priv: + kfree(mip); + return ret; +} + +IRQCHIP_PLATFORM_DRIVER_BEGIN(mip_msi) +IRQCHIP_MATCH("brcm,bcm2712-mip", mip_of_msi_init) +IRQCHIP_PLATFORM_DRIVER_END(mip_msi) +MODULE_DESCRIPTION("Broadcom BCM2712 MSI-X interrupt controller"); +MODULE_AUTHOR("Phil Elwell "); +MODULE_AUTHOR("Stanimir Varbanov "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 42c812d07088777a3439e51bd1461d215151150e Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 21 Feb 2025 17:52:04 +0200 Subject: PCI: qcom-ep: Enable EP mode support for SAR2130P MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable PCIe Endpoint mode support for the Qualcomm SAR2130P platform. This is needed, as it is not possible to use a compatible fallback on any other platform since SAR2130P uses slightly different set of clocks. Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250221-sar2130p-pci-v3-6-61a0fdfb75b4@linaro.org Reviewed-by: Manivannan Sadhasivam Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/dwc/pcie-qcom-ep.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 01d3862d7003..d1f777945680 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -937,6 +937,7 @@ static const struct of_device_id qcom_pcie_ep_match[] = { { .compatible = "qcom,sa8775p-pcie-ep", .data = &cfg_1_34_0}, { .compatible = "qcom,sdx55-pcie-ep", }, { .compatible = "qcom,sm8450-pcie-ep", }, + { .compatible = "qcom,sar2130p-pcie-ep", }, { } }; MODULE_DEVICE_TABLE(of, qcom_pcie_ep_match); -- cgit v1.2.3-59-g8ed1b From 2294059118c550464dd8906286324d90c33b152b Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 24 Feb 2025 10:35:56 +0200 Subject: PCI: brcmstb: Add a softdep to MIP MSI-X driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Then the brcmstb PCIe driver and MIP MSI-X interrupt controller drivers are built as modules there could be a race in probing. To avoid this, add a softdep to MIP driver to guarantee that MIP driver will be load first. Signed-off-by: Stanimir Varbanov Reviewed-by: Florian Fainelli Tested-by: Ivan T. Ivanov Link: https://lore.kernel.org/r/20250224083559.47645-5-svarbanov@suse.de [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-brcmstb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index dcaaa7fd9faf..72299ec5c2d9 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1945,3 +1945,4 @@ module_platform_driver(brcm_pcie_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Broadcom STB PCIe RC driver"); MODULE_AUTHOR("Broadcom"); +MODULE_SOFTDEP("pre: irq_bcm2712_mip"); -- cgit v1.2.3-59-g8ed1b From 10dbedad3c8188ce8b68559d43b7aaee7dafba25 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 24 Feb 2025 10:35:57 +0200 Subject: PCI: brcmstb: Reuse pcie_cfg_data structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of copying fields from the pcie_cfg_data structure to brcm_pcie, reference it directly. Signed-off-by: Stanimir Varbanov Reviewed-by: Florian Fainelil Reviewed-by: Jim Quinlan Tested-by: Ivan T. Ivanov Link: https://lore.kernel.org/r/20250224083559.47645-6-svarbanov@suse.de [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-brcmstb.c | 72 ++++++++++++++++------------------- 1 file changed, 32 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 72299ec5c2d9..f2583dd7e0ce 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -191,11 +191,11 @@ #define SSC_STATUS_PLL_LOCK_MASK 0x800 #define PCIE_BRCM_MAX_MEMC 3 -#define IDX_ADDR(pcie) ((pcie)->reg_offsets[EXT_CFG_INDEX]) -#define DATA_ADDR(pcie) ((pcie)->reg_offsets[EXT_CFG_DATA]) -#define PCIE_RGR1_SW_INIT_1(pcie) ((pcie)->reg_offsets[RGR1_SW_INIT_1]) -#define HARD_DEBUG(pcie) ((pcie)->reg_offsets[PCIE_HARD_DEBUG]) -#define INTR2_CPU_BASE(pcie) ((pcie)->reg_offsets[PCIE_INTR2_CPU_BASE]) +#define IDX_ADDR(pcie) ((pcie)->cfg->offsets[EXT_CFG_INDEX]) +#define DATA_ADDR(pcie) ((pcie)->cfg->offsets[EXT_CFG_DATA]) +#define PCIE_RGR1_SW_INIT_1(pcie) ((pcie)->cfg->offsets[RGR1_SW_INIT_1]) +#define HARD_DEBUG(pcie) ((pcie)->cfg->offsets[PCIE_HARD_DEBUG]) +#define INTR2_CPU_BASE(pcie) ((pcie)->cfg->offsets[PCIE_INTR2_CPU_BASE]) /* Rescal registers */ #define PCIE_DVT_PMU_PCIE_PHY_CTRL 0xc700 @@ -276,8 +276,6 @@ struct brcm_pcie { int gen; u64 msi_target_addr; struct brcm_msi *msi; - const int *reg_offsets; - enum pcie_soc_base soc_base; struct reset_control *rescal; struct reset_control *perst_reset; struct reset_control *bridge_reset; @@ -285,17 +283,14 @@ struct brcm_pcie { int num_memc; u64 memc_size[PCIE_BRCM_MAX_MEMC]; u32 hw_rev; - int (*perst_set)(struct brcm_pcie *pcie, u32 val); - int (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val); struct subdev_regulators *sr; bool ep_wakeup_capable; - bool has_phy; - u8 num_inbound_wins; + const struct pcie_cfg_data *cfg; }; static inline bool is_bmips(const struct brcm_pcie *pcie) { - return pcie->soc_base == BCM7435 || pcie->soc_base == BCM7425; + return pcie->cfg->soc_base == BCM7435 || pcie->cfg->soc_base == BCM7425; } /* @@ -855,7 +850,7 @@ static int brcm_pcie_get_inbound_wins(struct brcm_pcie *pcie, * security considerations, and is not implemented in our modern * SoCs. */ - if (pcie->soc_base != BCM7712) + if (pcie->cfg->soc_base != BCM7712) add_inbound_win(b++, &n, 0, 0, 0); resource_list_for_each_entry(entry, &bridge->dma_ranges) { @@ -872,10 +867,10 @@ static int brcm_pcie_get_inbound_wins(struct brcm_pcie *pcie, * That being said, each BARs size must still be a power of * two. */ - if (pcie->soc_base == BCM7712) + if (pcie->cfg->soc_base == BCM7712) add_inbound_win(b++, &n, size, cpu_start, pcie_start); - if (n > pcie->num_inbound_wins) + if (n > pcie->cfg->num_inbound_wins) break; } @@ -889,7 +884,7 @@ static int brcm_pcie_get_inbound_wins(struct brcm_pcie *pcie, * that enables multiple memory controllers. As such, it can return * now w/o doing special configuration. */ - if (pcie->soc_base == BCM7712) + if (pcie->cfg->soc_base == BCM7712) return n; ret = of_property_read_variable_u64_array(pcie->np, "brcm,scb-sizes", pcie->memc_size, 1, @@ -1012,7 +1007,7 @@ static void set_inbound_win_registers(struct brcm_pcie *pcie, * 7712: * All of their BARs need to be set. */ - if (pcie->soc_base == BCM7712) { + if (pcie->cfg->soc_base == BCM7712) { /* BUS remap register settings */ reg_offset = brcm_ubus_reg_offset(i); tmp = lower_32_bits(cpu_addr) & ~0xfff; @@ -1036,15 +1031,15 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) int memc, ret; /* Reset the bridge */ - ret = pcie->bridge_sw_init_set(pcie, 1); + ret = pcie->cfg->bridge_sw_init_set(pcie, 1); if (ret) return ret; /* Ensure that PERST# is asserted; some bootloaders may deassert it. */ - if (pcie->soc_base == BCM2711) { - ret = pcie->perst_set(pcie, 1); + if (pcie->cfg->soc_base == BCM2711) { + ret = pcie->cfg->perst_set(pcie, 1); if (ret) { - pcie->bridge_sw_init_set(pcie, 0); + pcie->cfg->bridge_sw_init_set(pcie, 0); return ret; } } @@ -1052,7 +1047,7 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) usleep_range(100, 200); /* Take the bridge out of reset */ - ret = pcie->bridge_sw_init_set(pcie, 0); + ret = pcie->cfg->bridge_sw_init_set(pcie, 0); if (ret) return ret; @@ -1072,9 +1067,9 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) */ if (is_bmips(pcie)) burst = 0x1; /* 256 bytes */ - else if (pcie->soc_base == BCM2711) + else if (pcie->cfg->soc_base == BCM2711) burst = 0x0; /* 128 bytes */ - else if (pcie->soc_base == BCM7278) + else if (pcie->cfg->soc_base == BCM7278) burst = 0x3; /* 512 bytes */ else burst = 0x2; /* 512 bytes */ @@ -1199,7 +1194,7 @@ static void brcm_extend_rbus_timeout(struct brcm_pcie *pcie) u32 timeout_us = 4000000; /* 4 seconds, our setting for L1SS */ /* 7712 does not have this (RGR1) timer */ - if (pcie->soc_base == BCM7712) + if (pcie->cfg->soc_base == BCM7712) return; /* Each unit in timeout register is 1/216,000,000 seconds */ @@ -1277,7 +1272,7 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) int ret, i; /* Unassert the fundamental reset */ - ret = pcie->perst_set(pcie, 0); + ret = pcie->cfg->perst_set(pcie, 0); if (ret) return ret; @@ -1463,12 +1458,12 @@ static int brcm_phy_cntl(struct brcm_pcie *pcie, const int start) static inline int brcm_phy_start(struct brcm_pcie *pcie) { - return pcie->has_phy ? brcm_phy_cntl(pcie, 1) : 0; + return pcie->cfg->has_phy ? brcm_phy_cntl(pcie, 1) : 0; } static inline int brcm_phy_stop(struct brcm_pcie *pcie) { - return pcie->has_phy ? brcm_phy_cntl(pcie, 0) : 0; + return pcie->cfg->has_phy ? brcm_phy_cntl(pcie, 0) : 0; } static int brcm_pcie_turn_off(struct brcm_pcie *pcie) @@ -1479,7 +1474,7 @@ static int brcm_pcie_turn_off(struct brcm_pcie *pcie) if (brcm_pcie_link_up(pcie)) brcm_pcie_enter_l23(pcie); /* Assert fundamental reset */ - ret = pcie->perst_set(pcie, 1); + ret = pcie->cfg->perst_set(pcie, 1); if (ret) return ret; @@ -1494,7 +1489,7 @@ static int brcm_pcie_turn_off(struct brcm_pcie *pcie) writel(tmp, base + HARD_DEBUG(pcie)); /* Shutdown PCIe bridge */ - ret = pcie->bridge_sw_init_set(pcie, 1); + ret = pcie->cfg->bridge_sw_init_set(pcie, 1); return ret; } @@ -1582,7 +1577,7 @@ static int brcm_pcie_resume_noirq(struct device *dev) goto err_reset; /* Take bridge out of reset so we can access the SERDES reg */ - pcie->bridge_sw_init_set(pcie, 0); + pcie->cfg->bridge_sw_init_set(pcie, 0); /* SERDES_IDDQ = 0 */ tmp = readl(base + HARD_DEBUG(pcie)); @@ -1803,12 +1798,7 @@ static int brcm_pcie_probe(struct platform_device *pdev) pcie = pci_host_bridge_priv(bridge); pcie->dev = &pdev->dev; pcie->np = np; - pcie->reg_offsets = data->offsets; - pcie->soc_base = data->soc_base; - pcie->perst_set = data->perst_set; - pcie->bridge_sw_init_set = data->bridge_sw_init_set; - pcie->has_phy = data->has_phy; - pcie->num_inbound_wins = data->num_inbound_wins; + pcie->cfg = data; pcie->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pcie->base)) @@ -1843,7 +1833,7 @@ static int brcm_pcie_probe(struct platform_device *pdev) if (ret) return dev_err_probe(&pdev->dev, ret, "could not enable clock\n"); - pcie->bridge_sw_init_set(pcie, 0); + pcie->cfg->bridge_sw_init_set(pcie, 0); if (pcie->swinit_reset) { ret = reset_control_assert(pcie->swinit_reset); @@ -1882,7 +1872,8 @@ static int brcm_pcie_probe(struct platform_device *pdev) goto fail; pcie->hw_rev = readl(pcie->base + PCIE_MISC_REVISION); - if (pcie->soc_base == BCM4908 && pcie->hw_rev >= BRCM_PCIE_HW_REV_3_20) { + if (pcie->cfg->soc_base == BCM4908 && + pcie->hw_rev >= BRCM_PCIE_HW_REV_3_20) { dev_err(pcie->dev, "hardware revision with unsupported PERST# setup\n"); ret = -ENODEV; goto fail; @@ -1902,7 +1893,8 @@ static int brcm_pcie_probe(struct platform_device *pdev) } } - bridge->ops = pcie->soc_base == BCM7425 ? &brcm7425_pcie_ops : &brcm_pcie_ops; + bridge->ops = pcie->cfg->soc_base == BCM7425 ? + &brcm7425_pcie_ops : &brcm_pcie_ops; bridge->sysdata = pcie; platform_set_drvdata(pdev, pcie); -- cgit v1.2.3-59-g8ed1b From 25a98c727015638baffcfa236e3f37b70cedcf87 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 24 Feb 2025 10:35:58 +0200 Subject: PCI: brcmstb: Expand inbound window size up to 64GB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BCM2712 memory map can support up to 64GB of system memory, thus expand the inbound window size in calculation helper function. The change is safe for the currently supported SoCs that have smaller inbound window sizes. Signed-off-by: Stanimir Varbanov Reviewed-by: Florian Fainelli Reviewed-by: Jim Quinlan Tested-by: Ivan T. Ivanov Link: https://lore.kernel.org/r/20250224083559.47645-7-svarbanov@suse.de [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-brcmstb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index f2583dd7e0ce..9c45272ffcf0 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -304,8 +304,8 @@ static int brcm_pcie_encode_ibar_size(u64 size) if (log2_in >= 12 && log2_in <= 15) /* Covers 4KB to 32KB (inclusive) */ return (log2_in - 12) + 0x1c; - else if (log2_in >= 16 && log2_in <= 35) - /* Covers 64KB to 32GB, (inclusive) */ + else if (log2_in >= 16 && log2_in <= 36) + /* Covers 64KB to 64GB, (inclusive) */ return log2_in - 15; /* Something is awry so disable */ return 0; -- cgit v1.2.3-59-g8ed1b From 3b62449da444502d8b87bf090d8ec4a57d47eda5 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 24 Feb 2025 15:13:51 +0100 Subject: driver core: Introduce device_{add,remove}_of_node() An of_node can be set to a device using device_set_node(), which does not prevent any of_node and/or fwnode overwrites. When adding an of_node on an already present device, the following operations need to be done: - Attach the of_node only if no of_node is already attached - Attach the of_node as a fwnode if no fwnode were already attached This is the purpose of device_add_of_node(). device_remove_of_node() reverts the operations done by device_add_of_node(). Link: https://lore.kernel.org/r/20250224141356.36325-2-herve.codina@bootlin.com Signed-off-by: Herve Codina Signed-off-by: Bjorn Helgaas Reviewed-by: Rob Herring (Arm) Acked-by: Greg Kroah-Hartman --- drivers/base/core.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 2 ++ 2 files changed, 63 insertions(+) (limited to 'drivers') diff --git a/drivers/base/core.c b/drivers/base/core.c index 5a1f05198114..49eb5aaf19cc 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -5170,6 +5170,67 @@ void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode) } EXPORT_SYMBOL_GPL(set_secondary_fwnode); +/** + * device_remove_of_node - Remove an of_node from a device + * @dev: device whose device tree node is being removed + */ +void device_remove_of_node(struct device *dev) +{ + dev = get_device(dev); + if (!dev) + return; + + if (!dev->of_node) + goto end; + + if (dev->fwnode == of_fwnode_handle(dev->of_node)) + dev->fwnode = NULL; + + of_node_put(dev->of_node); + dev->of_node = NULL; + +end: + put_device(dev); +} +EXPORT_SYMBOL_GPL(device_remove_of_node); + +/** + * device_add_of_node - Add an of_node to an existing device + * @dev: device whose device tree node is being added + * @of_node: of_node to add + * + * Return: 0 on success or error code on failure. + */ +int device_add_of_node(struct device *dev, struct device_node *of_node) +{ + int ret; + + if (!of_node) + return -EINVAL; + + dev = get_device(dev); + if (!dev) + return -EINVAL; + + if (dev->of_node) { + dev_err(dev, "Cannot replace node %pOF with %pOF\n", + dev->of_node, of_node); + ret = -EBUSY; + goto end; + } + + dev->of_node = of_node_get(of_node); + + if (!dev->fwnode) + dev->fwnode = of_fwnode_handle(of_node); + + ret = 0; +end: + put_device(dev); + return ret; +} +EXPORT_SYMBOL_GPL(device_add_of_node); + /** * device_set_of_node_from_dev - reuse device-tree node of another device * @dev: device whose device-tree node is being set diff --git a/include/linux/device.h b/include/linux/device.h index 80a5b3268986..1244e5892292 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1191,6 +1191,8 @@ int device_online(struct device *dev); void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode); void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode); void device_set_node(struct device *dev, struct fwnode_handle *fwnode); +int device_add_of_node(struct device *dev, struct device_node *of_node); +void device_remove_of_node(struct device *dev); void device_set_of_node_from_dev(struct device *dev, const struct device *dev2); static inline struct device_node *dev_of_node(struct device *dev) -- cgit v1.2.3-59-g8ed1b From e2267841fe26d45d3c95958ddb71905a4cd4ac92 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 24 Feb 2025 15:13:52 +0100 Subject: PCI: of: Use device_{add,remove}_of_node() to attach of_node to existing device The commit 407d1a51921e ("PCI: Create device tree node for bridge") creates of_node for PCI devices. The newly created of_node is attached to an existing device. This is done setting directly pdev->dev.of_node in the code. Even if pdev->dev.of_node cannot be previously set, this doesn't handle the fwnode field of the struct device. Indeed, this field needs to be set if it hasn't already been set. device_{add,remove}_of_node() have been introduced to handle this case. Use them instead of the direct setting. Link: https://lore.kernel.org/r/20250224141356.36325-3-herve.codina@bootlin.com Signed-off-by: Herve Codina Signed-off-by: Bjorn Helgaas Reviewed-by: Rob Herring (Arm) --- drivers/pci/of.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 7a806f5c0d20..fb5e6da1ead0 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -653,8 +653,8 @@ void of_pci_remove_node(struct pci_dev *pdev) np = pci_device_to_OF_node(pdev); if (!np || !of_node_check_flag(np, OF_DYNAMIC)) return; - pdev->dev.of_node = NULL; + device_remove_of_node(&pdev->dev); of_changeset_revert(np->data); of_changeset_destroy(np->data); of_node_put(np); @@ -711,11 +711,18 @@ void of_pci_make_dev_node(struct pci_dev *pdev) goto out_free_node; np->data = cset; - pdev->dev.of_node = np; + + ret = device_add_of_node(&pdev->dev, np); + if (ret) + goto out_revert_cset; + kfree(name); return; +out_revert_cset: + np->data = NULL; + of_changeset_revert(cset); out_free_node: of_node_put(np); out_destroy_cset: -- cgit v1.2.3-59-g8ed1b From c5785a165f847c457bd398f496f7c108527b22bd Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 24 Feb 2025 15:13:53 +0100 Subject: PCI: of_property: Add support for NULL pdev in of_pci_set_address() The pdev (pointer to a struct pci_dev) parameter of of_pci_set_address() cannot be NULL. In order to use of_pci_set_address() when creating the PCI root bus node, it needs to support a NULL pdev parameter. Indeed, in the case of the PCI root bus node creation, no pdev is available and of_pci_set_address() will be used with the bridge windows. Allow to call of_pci_set_address() with a NULL pdev. Link: https://lore.kernel.org/r/20250224141356.36325-4-herve.codina@bootlin.com Signed-off-by: Herve Codina Signed-off-by: Bjorn Helgaas Reviewed-by: Rob Herring (Arm) --- drivers/pci/of_property.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/of_property.c b/drivers/pci/of_property.c index 58fbafac7c6a..a6acd64ff869 100644 --- a/drivers/pci/of_property.c +++ b/drivers/pci/of_property.c @@ -54,9 +54,13 @@ enum of_pci_prop_compatible { static void of_pci_set_address(struct pci_dev *pdev, u32 *prop, u64 addr, u32 reg_num, u32 flags, bool reloc) { - prop[0] = FIELD_PREP(OF_PCI_ADDR_FIELD_BUS, pdev->bus->number) | - FIELD_PREP(OF_PCI_ADDR_FIELD_DEV, PCI_SLOT(pdev->devfn)) | - FIELD_PREP(OF_PCI_ADDR_FIELD_FUNC, PCI_FUNC(pdev->devfn)); + if (pdev) { + prop[0] = FIELD_PREP(OF_PCI_ADDR_FIELD_BUS, pdev->bus->number) | + FIELD_PREP(OF_PCI_ADDR_FIELD_DEV, PCI_SLOT(pdev->devfn)) | + FIELD_PREP(OF_PCI_ADDR_FIELD_FUNC, PCI_FUNC(pdev->devfn)); + } else + prop[0] = 0; + prop[0] |= flags | reg_num; if (!reloc) { prop[0] |= OF_PCI_ADDR_FIELD_NONRELOC; -- cgit v1.2.3-59-g8ed1b From 3dc8adeeefa0256917d1e3978c8b4a06346816ed Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 24 Feb 2025 15:13:54 +0100 Subject: PCI: of_property: Constify parameter in of_pci_get_addr_flags() The res parameter has no reason to be a pointer to an un-const struct resource. Indeed, struct resource is not supposed to be modified by the function. Constify the res parameter. Link: https://lore.kernel.org/r/20250224141356.36325-5-herve.codina@bootlin.com Signed-off-by: Herve Codina Signed-off-by: Bjorn Helgaas Reviewed-by: Rob Herring (Arm) --- drivers/pci/of_property.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/of_property.c b/drivers/pci/of_property.c index a6acd64ff869..afc229843d07 100644 --- a/drivers/pci/of_property.c +++ b/drivers/pci/of_property.c @@ -69,7 +69,7 @@ static void of_pci_set_address(struct pci_dev *pdev, u32 *prop, u64 addr, } } -static int of_pci_get_addr_flags(struct resource *res, u32 *flags) +static int of_pci_get_addr_flags(const struct resource *res, u32 *flags) { u32 ss; -- cgit v1.2.3-59-g8ed1b From 1f340724419eda8ab07a20edcaf5ec8f70134231 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 24 Feb 2025 15:13:55 +0100 Subject: PCI: of: Create device tree PCI host bridge node PCI devices device tree nodes can be already created. This was introduced by commit 407d1a51921e ("PCI: Create device tree node for bridge"). In order to have device tree nodes related to PCI devices attached on their PCI root bus (the PCI bus handled by the PCI host bridge), a PCI root bus device tree node is needed. This root bus node will be used as the parent node of the first level devices scanned on the bus. On device tree based systems, this PCI root bus device tree node is set to the node of the related PCI host bridge. The PCI host bridge node is available in the device tree used to describe the hardware passed at boot. On non device tree based system (such as ACPI), a device tree node for the PCI host bridge or for the root bus does not exist. Indeed, the PCI host bridge is not described in a device tree used at boot simply because no device tree is passed at boot. The device tree PCI host bridge node creation needs to be done at runtime. This is done in the same way as for the creation of the PCI device nodes. I.e. node and properties are created based on computed information done by the PCI core. Also, as is done on device tree based systems, this PCI host bridge node is used for the PCI root bus. With this done, hardware available in a PCI device that doesn't follow the PCI model consisting in one PCI function handled by one driver can be described by a device tree overlay loaded by the PCI device driver on non device tree based systems. Those PCI devices provide a single PCI function that includes several functionalities that require different drivers. The device tree overlay describes the internal devices and their relationships. It allows to load drivers needed by those different devices in order to have functionalities handled. Link: https://lore.kernel.org/r/20250224141356.36325-6-herve.codina@bootlin.com Signed-off-by: Herve Codina Signed-off-by: Bjorn Helgaas Reviewed-by: Rob Herring (Arm) --- drivers/pci/of.c | 107 +++++++++++++++++++++++++++++++++++++++++++++- drivers/pci/of_property.c | 103 ++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 7 +++ drivers/pci/probe.c | 2 + drivers/pci/remove.c | 2 + 5 files changed, 220 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/of.c b/drivers/pci/of.c index fb5e6da1ead0..762d4e3fde3c 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -731,7 +731,112 @@ out_destroy_cset: out_free_name: kfree(name); } -#endif + +void of_pci_remove_host_bridge_node(struct pci_host_bridge *bridge) +{ + struct device_node *np; + + np = pci_bus_to_OF_node(bridge->bus); + if (!np || !of_node_check_flag(np, OF_DYNAMIC)) + return; + + device_remove_of_node(&bridge->bus->dev); + device_remove_of_node(&bridge->dev); + of_changeset_revert(np->data); + of_changeset_destroy(np->data); + of_node_put(np); +} + +void of_pci_make_host_bridge_node(struct pci_host_bridge *bridge) +{ + struct device_node *np = NULL; + struct of_changeset *cset; + const char *name; + int ret; + + /* + * If there is already a device tree node linked to the PCI bus handled + * by this bridge (i.e. the PCI root bus), nothing to do. + */ + if (pci_bus_to_OF_node(bridge->bus)) + return; + + /* + * The root bus has no node. Check that the host bridge has no node + * too + */ + if (bridge->dev.of_node) { + dev_err(&bridge->dev, "PCI host bridge of_node already set"); + return; + } + + /* Check if there is a DT root node to attach the created node */ + if (!of_root) { + pr_err("of_root node is NULL, cannot create PCI host bridge node\n"); + return; + } + + name = kasprintf(GFP_KERNEL, "pci@%x,%x", pci_domain_nr(bridge->bus), + bridge->bus->number); + if (!name) + return; + + cset = kmalloc(sizeof(*cset), GFP_KERNEL); + if (!cset) + goto out_free_name; + of_changeset_init(cset); + + np = of_changeset_create_node(cset, of_root, name); + if (!np) + goto out_destroy_cset; + + ret = of_pci_add_host_bridge_properties(bridge, cset, np); + if (ret) + goto out_free_node; + + /* + * This of_node will be added to an existing device. The of_node parent + * is the root OF node and so this node will be handled by the platform + * bus. Avoid any new device creation. + */ + of_node_set_flag(np, OF_POPULATED); + np->fwnode.dev = &bridge->dev; + fwnode_dev_initialized(&np->fwnode, true); + + ret = of_changeset_apply(cset); + if (ret) + goto out_free_node; + + np->data = cset; + + /* Add the of_node to host bridge and the root bus */ + ret = device_add_of_node(&bridge->dev, np); + if (ret) + goto out_revert_cset; + + ret = device_add_of_node(&bridge->bus->dev, np); + if (ret) + goto out_remove_bridge_dev_of_node; + + kfree(name); + + return; + +out_remove_bridge_dev_of_node: + device_remove_of_node(&bridge->dev); +out_revert_cset: + np->data = NULL; + of_changeset_revert(cset); +out_free_node: + of_node_put(np); +out_destroy_cset: + of_changeset_destroy(cset); + kfree(cset); +out_free_name: + kfree(name); +} + +#endif /* CONFIG_PCI_DYNAMIC_OF_NODES */ /** * of_pci_supply_present() - Check if the power supply is present for the PCI diff --git a/drivers/pci/of_property.c b/drivers/pci/of_property.c index afc229843d07..506fcd507113 100644 --- a/drivers/pci/of_property.c +++ b/drivers/pci/of_property.c @@ -394,3 +394,106 @@ int of_pci_add_properties(struct pci_dev *pdev, struct of_changeset *ocs, return 0; } + +static bool of_pci_is_range_resource(const struct resource *res, u32 *flags) +{ + if (!(resource_type(res) & IORESOURCE_MEM) && + !(resource_type(res) & IORESOURCE_MEM_64)) + return false; + + if (of_pci_get_addr_flags(res, flags)) + return false; + + return true; +} + +static int of_pci_host_bridge_prop_ranges(struct pci_host_bridge *bridge, + struct of_changeset *ocs, + struct device_node *np) +{ + struct resource_entry *window; + unsigned int ranges_sz = 0; + unsigned int n_range = 0; + struct resource *res; + int n_addr_cells; + u32 *ranges; + u64 val64; + u32 flags; + int ret; + + n_addr_cells = of_n_addr_cells(np); + if (n_addr_cells <= 0 || n_addr_cells > 2) + return -EINVAL; + + resource_list_for_each_entry(window, &bridge->windows) { + res = window->res; + if (!of_pci_is_range_resource(res, &flags)) + continue; + n_range++; + } + + if (!n_range) + return 0; + + ranges = kcalloc(n_range, + (OF_PCI_ADDRESS_CELLS + OF_PCI_SIZE_CELLS + + n_addr_cells) * sizeof(*ranges), + GFP_KERNEL); + if (!ranges) + return -ENOMEM; + + resource_list_for_each_entry(window, &bridge->windows) { + res = window->res; + if (!of_pci_is_range_resource(res, &flags)) + continue; + + /* PCI bus address */ + val64 = res->start; + of_pci_set_address(NULL, &ranges[ranges_sz], + val64 - window->offset, 0, flags, false); + ranges_sz += OF_PCI_ADDRESS_CELLS; + + /* Host bus address */ + if (n_addr_cells == 2) + ranges[ranges_sz++] = upper_32_bits(val64); + ranges[ranges_sz++] = lower_32_bits(val64); + + /* Size */ + val64 = resource_size(res); + ranges[ranges_sz] = upper_32_bits(val64); + ranges[ranges_sz + 1] = lower_32_bits(val64); + ranges_sz += OF_PCI_SIZE_CELLS; + } + + ret = of_changeset_add_prop_u32_array(ocs, np, "ranges", ranges, + ranges_sz); + kfree(ranges); + return ret; +} + +int of_pci_add_host_bridge_properties(struct pci_host_bridge *bridge, + struct of_changeset *ocs, + struct device_node *np) +{ + int ret; + + ret = of_changeset_add_prop_string(ocs, np, "device_type", "pci"); + if (ret) + return ret; + + ret = of_changeset_add_prop_u32(ocs, np, "#address-cells", + OF_PCI_ADDRESS_CELLS); + if (ret) + return ret; + + ret = of_changeset_add_prop_u32(ocs, np, "#size-cells", + OF_PCI_SIZE_CELLS); + if (ret) + return ret; + + ret = of_pci_host_bridge_prop_ranges(bridge, ocs, np); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 01e51db8d285..889cdb1e6fcb 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -876,9 +876,16 @@ void of_pci_make_dev_node(struct pci_dev *pdev); void of_pci_remove_node(struct pci_dev *pdev); int of_pci_add_properties(struct pci_dev *pdev, struct of_changeset *ocs, struct device_node *np); +void of_pci_make_host_bridge_node(struct pci_host_bridge *bridge); +void of_pci_remove_host_bridge_node(struct pci_host_bridge *bridge); +int of_pci_add_host_bridge_properties(struct pci_host_bridge *bridge, + struct of_changeset *ocs, + struct device_node *np); #else static inline void of_pci_make_dev_node(struct pci_dev *pdev) { } static inline void of_pci_remove_node(struct pci_dev *pdev) { } +static inline void of_pci_make_host_bridge_node(struct pci_host_bridge *bridge) { } +static inline void of_pci_remove_host_bridge_node(struct pci_host_bridge *bridge) { } #endif #ifdef CONFIG_PCIEAER diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b6536ed599c3..b74a12a0193a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1094,6 +1094,8 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) dev_info(&bus->dev, "root bus resource %pR%s\n", res, addr); } + of_pci_make_host_bridge_node(bridge); + down_write(&pci_bus_sem); list_add_tail(&bus->node, &pci_root_buses); up_write(&pci_bus_sem); diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index efc37fcb73e2..9f7df2b20183 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -163,6 +163,8 @@ void pci_stop_root_bus(struct pci_bus *bus) &bus->devices, bus_list) pci_stop_bus_device(child); + of_pci_remove_host_bridge_node(host_bridge); + /* stop the host bridge */ device_release_driver(&host_bridge->dev); } -- cgit v1.2.3-59-g8ed1b From 249b78298078448a699c39356d27d8183af4b281 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 25 Feb 2025 09:04:07 +0100 Subject: PCI: mediatek-gen3: Configure PBUS_CSR registers for EN7581 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Configure PBus base address and address mask to allow the hw to detect if a given address is accessible on PCIe controller. Fixes: f6ab898356dd ("PCI: mediatek-gen3: Add Airoha EN7581 support") Reviewed-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Bianconi Link: https://lore.kernel.org/r/20250225-en7581-pcie-pbus-csr-v4-2-24324382424a@kernel.org Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-mediatek-gen3.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 0f64e76e2111..3583e5481dc8 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ #include #include #include +#include #include #include "../pci.h" @@ -930,9 +932,13 @@ static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie) static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie) { + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); struct device *dev = pcie->dev; + struct resource_entry *entry; + struct regmap *pbus_regmap; + u32 val, args[2], size; + resource_size_t addr; int err; - u32 val; /* * The controller may have been left out of reset by the bootloader @@ -944,6 +950,26 @@ static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie) /* Wait for the time needed to complete the reset lines assert. */ msleep(PCIE_EN7581_RESET_TIME_MS); + /* + * Configure PBus base address and base address mask to allow the + * hw to detect if a given address is accessible on PCIe controller. + */ + pbus_regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, + "mediatek,pbus-csr", + ARRAY_SIZE(args), + args); + if (IS_ERR(pbus_regmap)) + return PTR_ERR(pbus_regmap); + + entry = resource_list_first_type(&host->windows, IORESOURCE_MEM); + if (!entry) + return -ENODEV; + + addr = entry->res->start - entry->offset; + regmap_write(pbus_regmap, args[0], lower_32_bits(addr)); + size = lower_32_bits(resource_size(entry->res)); + regmap_write(pbus_regmap, args[1], GENMASK(31, __fls(size))); + /* * Unlike the other MediaTek Gen3 controllers, the Airoha EN7581 * requires PHY initialization and power-on before PHY reset deassert. -- cgit v1.2.3-59-g8ed1b From 5d2b978ff9b13b1286c6e1c753b331f4bd98d7ff Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 21 Feb 2025 18:45:44 +0530 Subject: perf/dwc_pcie: Move common DWC struct definitions to 'pcie-dwc.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the common DWC struct definitions, which are shared across all the DesginWare PCIe IPs, to a new header file called 'pcie-dwc.h', so that other users e.g., debugfs, perf and sysfs can make use of them. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Shradha Todi Reviewed-by: Shuai Xue Reviewed-by: Fan Ni Tested-by: Hrishikesh Deleep Link: https://lore.kernel.org/r/20250221131548.59616-2-shradha.t@samsung.com [kwilczynski: commit log, tidy up the new header file] Signed-off-by: Krzysztof Wilczyński --- MAINTAINERS | 1 + drivers/perf/dwc_pcie_pmu.c | 25 +++---------------------- include/linux/pcie-dwc.h | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 22 deletions(-) create mode 100644 include/linux/pcie-dwc.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 896a307fa065..b4d09d52a750 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18123,6 +18123,7 @@ S: Maintained F: Documentation/devicetree/bindings/pci/snps,dw-pcie-ep.yaml F: Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml F: drivers/pci/controller/dwc/*designware* +F: include/linux/pcie-dwc.h PCI DRIVER FOR TI DRA7XX/J721E M: Vignesh Raghavendra diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c index cccecae9823f..da30f2c2d674 100644 --- a/drivers/perf/dwc_pcie_pmu.c +++ b/drivers/perf/dwc_pcie_pmu.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -99,26 +100,6 @@ struct dwc_pcie_dev_info { struct list_head dev_node; }; -struct dwc_pcie_pmu_vsec_id { - u16 vendor_id; - u16 vsec_id; - u8 vsec_rev; -}; - -/* - * VSEC IDs are allocated by the vendor, so a given ID may mean different - * things to different vendors. See PCIe r6.0, sec 7.9.5.2. - */ -static const struct dwc_pcie_pmu_vsec_id dwc_pcie_pmu_vsec_ids[] = { - { .vendor_id = PCI_VENDOR_ID_ALIBABA, - .vsec_id = 0x02, .vsec_rev = 0x4 }, - { .vendor_id = PCI_VENDOR_ID_AMPERE, - .vsec_id = 0x02, .vsec_rev = 0x4 }, - { .vendor_id = PCI_VENDOR_ID_QCOM, - .vsec_id = 0x02, .vsec_rev = 0x4 }, - {} /* terminator */ -}; - static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -529,14 +510,14 @@ static void dwc_pcie_unregister_pmu(void *data) static u16 dwc_pcie_des_cap(struct pci_dev *pdev) { - const struct dwc_pcie_pmu_vsec_id *vid; + const struct dwc_pcie_vsec_id *vid; u16 vsec; u32 val; if (!pci_is_pcie(pdev) || !(pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)) return 0; - for (vid = dwc_pcie_pmu_vsec_ids; vid->vendor_id; vid++) { + for (vid = dwc_pcie_rasdes_vsec_ids; vid->vendor_id; vid++) { vsec = pci_find_vsec_capability(pdev, vid->vendor_id, vid->vsec_id); if (vsec) { diff --git a/include/linux/pcie-dwc.h b/include/linux/pcie-dwc.h new file mode 100644 index 000000000000..6e4e1307ad6e --- /dev/null +++ b/include/linux/pcie-dwc.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021-2023 Alibaba Inc. + * Copyright (C) 2025 Linaro Ltd. + * + * Author: Manivannan Sadhasivam + */ + +#ifndef LINUX_PCIE_DWC_H +#define LINUX_PCIE_DWC_H + +#include + +struct dwc_pcie_vsec_id { + u16 vendor_id; + u16 vsec_id; + u8 vsec_rev; +}; + +/* + * VSEC IDs are allocated by the vendor, so a given ID may mean different + * things to different vendors. See PCIe r6.0, sec 7.9.5.2. + */ +static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = { + { .vendor_id = PCI_VENDOR_ID_ALIBABA, + .vsec_id = 0x02, .vsec_rev = 0x4 }, + { .vendor_id = PCI_VENDOR_ID_AMPERE, + .vsec_id = 0x02, .vsec_rev = 0x4 }, + { .vendor_id = PCI_VENDOR_ID_QCOM, + .vsec_id = 0x02, .vsec_rev = 0x4 }, + {} +}; + +#endif /* LINUX_PCIE_DWC_H */ -- cgit v1.2.3-59-g8ed1b From efaf16de43f59992afd37b4b8beb30ef511ddbfd Mon Sep 17 00:00:00 2001 From: Shradha Todi Date: Fri, 21 Feb 2025 18:45:45 +0530 Subject: PCI: dwc: Add helper to find the Vendor Specific Extended Capability (VSEC) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new dw_pcie_find_vsec_capability() helper will be used within different DWC APIs to find the VSEC capabilities like PTM, RAS, etc. Co-developed-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Signed-off-by: Shradha Todi Reviewed-by: Fan Ni Tested-by: Hrishikesh Deleep Link: https://lore.kernel.org/r/20250221131548.59616-3-shradha.t@samsung.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/dwc/pcie-designware.c | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 145e7f579072..a7c0671c6715 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -283,6 +284,45 @@ u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap) } EXPORT_SYMBOL_GPL(dw_pcie_find_ext_capability); +static u16 __dw_pcie_find_vsec_capability(struct dw_pcie *pci, u16 vendor_id, + u16 vsec_id) +{ + u16 vsec = 0; + u32 header; + + if (vendor_id != dw_pcie_readw_dbi(pci, PCI_VENDOR_ID)) + return 0; + + while ((vsec = dw_pcie_find_next_ext_capability(pci, vsec, + PCI_EXT_CAP_ID_VNDR))) { + header = dw_pcie_readl_dbi(pci, vsec + PCI_VNDR_HEADER); + if (PCI_VNDR_HEADER_ID(header) == vsec_id) + return vsec; + } + + return 0; +} + +static u16 dw_pcie_find_vsec_capability(struct dw_pcie *pci, + const struct dwc_pcie_vsec_id *vsec_ids) +{ + const struct dwc_pcie_vsec_id *vid; + u16 vsec; + u32 header; + + for (vid = vsec_ids; vid->vendor_id; vid++) { + vsec = __dw_pcie_find_vsec_capability(pci, vid->vendor_id, + vid->vsec_id); + if (vsec) { + header = dw_pcie_readl_dbi(pci, vsec + PCI_VNDR_HEADER); + if (PCI_VNDR_HEADER_REV(header) == vid->vsec_rev) + return vsec; + } + } + + return 0; +} + int dw_pcie_read(void __iomem *addr, int size, u32 *val) { if (!IS_ALIGNED((uintptr_t)addr, size)) { -- cgit v1.2.3-59-g8ed1b From 3ac47fbf4f6e8c3a7c3855fac68cc3246f90f850 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 15 Feb 2025 00:57:24 +0800 Subject: PCI: cadence-ep: Fix the driver to send MSG TLP for INTx without data payload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the Cadence's "PCIe Controller IP for AX14" user guide, Version 1.04, Section 9.1.7.1, "AXI Subordinate to PCIe Address Translation Registers", Table 9.4, the bit 16 of the AXI Subordinate Address (axi_s_awaddr) when set corresponds to MSG with data, and when not set, to MSG without data. However, the driver is currently doing the opposite and due to this, the INTx is never received on the host. So, fix the driver to reflect the documentation and also make INTx work. Fixes: 37dddf14f1ae ("PCI: cadence: Add EndPoint Controller driver for Cadence PCIe controller") Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Hans Zhang Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250214165724.184599-1-18255117159@163.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/cadence/pcie-cadence-ep.c | 3 +-- drivers/pci/controller/cadence/pcie-cadence.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index e0cc4560dfde..0bf4cde34f51 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -352,8 +352,7 @@ static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, u8 intx, spin_unlock_irqrestore(&ep->lock, flags); offset = CDNS_PCIE_NORMAL_MSG_ROUTING(MSG_ROUTING_LOCAL) | - CDNS_PCIE_NORMAL_MSG_CODE(msg_code) | - CDNS_PCIE_MSG_NO_DATA; + CDNS_PCIE_NORMAL_MSG_CODE(msg_code); writel(0, ep->irq_cpu_addr + offset); } diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index f5eeff834ec1..39ee9945c903 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -246,7 +246,7 @@ struct cdns_pcie_rp_ib_bar { #define CDNS_PCIE_NORMAL_MSG_CODE_MASK GENMASK(15, 8) #define CDNS_PCIE_NORMAL_MSG_CODE(code) \ (((code) << 8) & CDNS_PCIE_NORMAL_MSG_CODE_MASK) -#define CDNS_PCIE_MSG_NO_DATA BIT(16) +#define CDNS_PCIE_MSG_DATA BIT(16) struct cdns_pcie; -- cgit v1.2.3-59-g8ed1b From 377bced88c326499b05114722e0dcff29799edb0 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 24 Feb 2025 10:35:59 +0200 Subject: PCI: brcmstb: Add BCM2712 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a bare minimum amount of changes in order to support PCIe Root Complex hardware IP found on RPi5. The PCIe controller on BCM2712 is based on BCM7712 and as such it inherits register offsets, PERST# assertion, bridge_reset ops, and inbound windows count. Although, the implementation for BCM2712 needs a workaround related to the control of the bridge_reset where turning off of the Root Port must not shutdown the bridge_reset and this must be avoided. To implement this workaround a quirks field is introduced in pcie_cfg_data struct. The controller also needs adjustment of PHY PLL setup to use a 54MHz input refclk. The default input reference clock for the PHY PLL is 100Mhz, except for some devices where it is 54Mhz like BCM2712C1 and BCM2712D0. To implement those adjustments introduce a new .post_setup op in pcie_cfg_data and call it at the end of brcm_pcie_setup function. The BCM2712 .post_setup callback implements the required MDIO writes that switch the PLL refclk and also change PHY PM clock period. Without this RPi5 PCIex1 is unable to enumerate endpoint devices on the expansion connector. Signed-off-by: Stanimir Varbanov Reviewed-by: Florian Fainelli Reviewed-by: Jim Quinlan Tested-by: Ivan T. Ivanov Link: https://lore.kernel.org/r/20250224083559.47645-8-svarbanov@suse.de [commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-brcmstb.c | 69 ++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 9c45272ffcf0..d171ee61eab3 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -55,6 +55,10 @@ #define PCIE_RC_DL_MDIO_WR_DATA 0x1104 #define PCIE_RC_DL_MDIO_RD_DATA 0x1108 +#define PCIE_RC_PL_PHY_CTL_15 0x184c +#define PCIE_RC_PL_PHY_CTL_15_DIS_PLL_PD_MASK 0x400000 +#define PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK 0xff + #define PCIE_MISC_MISC_CTRL 0x4008 #define PCIE_MISC_MISC_CTRL_PCIE_RCB_64B_MODE_MASK 0x80 #define PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK 0x400 @@ -234,13 +238,24 @@ struct inbound_win { u64 cpu_addr; }; +/* + * The RESCAL block is tied to PCIe controller #1, regardless of the number of + * controllers, and turning off PCIe controller #1 prevents access to the RESCAL + * register blocks, therefore no other controller can access this register + * space, and depending upon the bus fabric we may get a timeout (UBUS/GISB), + * or a hang (AXI). + */ +#define CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN BIT(0) + struct pcie_cfg_data { const int *offsets; const enum pcie_soc_base soc_base; const bool has_phy; + const u32 quirks; u8 num_inbound_wins; int (*perst_set)(struct brcm_pcie *pcie, u32 val); int (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val); + int (*post_setup)(struct brcm_pcie *pcie); }; struct subdev_regulators { @@ -816,6 +831,38 @@ static int brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val) return 0; } +static int brcm_pcie_post_setup_bcm2712(struct brcm_pcie *pcie) +{ + const u16 data[] = { 0x50b9, 0xbda1, 0x0094, 0x97b4, 0x5030, 0x5030, 0x0007 }; + const u8 regs[] = { 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1e }; + int ret, i; + u32 tmp; + + /* Allow a 54MHz (xosc) refclk source */ + ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET, 0x1600); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, regs[i], data[i]); + if (ret < 0) + return ret; + } + + usleep_range(100, 200); + + /* + * Set L1SS sub-state timers to avoid lengthy state transitions, + * PM clock period is 18.52ns (1/54MHz, round down). + */ + tmp = readl(pcie->base + PCIE_RC_PL_PHY_CTL_15); + tmp &= ~PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK; + tmp |= 0x12; + writel(tmp, pcie->base + PCIE_RC_PL_PHY_CTL_15); + + return 0; +} + static void add_inbound_win(struct inbound_win *b, u8 *count, u64 size, u64 cpu_addr, u64 pci_offset) { @@ -1179,6 +1226,12 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK); writel(tmp, base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1); + if (pcie->cfg->post_setup) { + ret = pcie->cfg->post_setup(pcie); + if (ret < 0) + return ret; + } + return 0; } @@ -1488,8 +1541,9 @@ static int brcm_pcie_turn_off(struct brcm_pcie *pcie) u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK); writel(tmp, base + HARD_DEBUG(pcie)); - /* Shutdown PCIe bridge */ - ret = pcie->cfg->bridge_sw_init_set(pcie, 1); + if (!(pcie->cfg->quirks & CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN)) + /* Shutdown PCIe bridge */ + ret = pcie->cfg->bridge_sw_init_set(pcie, 1); return ret; } @@ -1699,6 +1753,16 @@ static const struct pcie_cfg_data bcm2711_cfg = { .num_inbound_wins = 3, }; +static const struct pcie_cfg_data bcm2712_cfg = { + .offsets = pcie_offsets_bcm7712, + .soc_base = BCM7712, + .perst_set = brcm_pcie_perst_set_7278, + .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic, + .post_setup = brcm_pcie_post_setup_bcm2712, + .quirks = CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN, + .num_inbound_wins = 10, +}; + static const struct pcie_cfg_data bcm4908_cfg = { .offsets = pcie_offsets, .soc_base = BCM4908, @@ -1750,6 +1814,7 @@ static const struct pcie_cfg_data bcm7712_cfg = { static const struct of_device_id brcm_pcie_match[] = { { .compatible = "brcm,bcm2711-pcie", .data = &bcm2711_cfg }, + { .compatible = "brcm,bcm2712-pcie", .data = &bcm2712_cfg }, { .compatible = "brcm,bcm4908-pcie", .data = &bcm4908_cfg }, { .compatible = "brcm,bcm7211-pcie", .data = &generic_cfg }, { .compatible = "brcm,bcm7216-pcie", .data = &bcm7216_cfg }, -- cgit v1.2.3-59-g8ed1b From 72d36589c6b7bef6b30eb99fcb7082f72faca37f Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:29 -0500 Subject: PCI: brcmstb: Set generation limit before PCIe link up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the user elects to limit the PCIe generation via the appropriate devicetree property, apply the settings before the PCIe link up, not after. Fixes: c0452137034b ("PCI: brcmstb: Add Broadcom STB PCIe host controller driver") Signed-off-by: Jim Quinlan Reviewed-by: Florian Fainelli Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250214173944.47506-2-james.quinlan@broadcom.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-brcmstb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index d171ee61eab3..2f1b2a4fe540 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1324,6 +1324,10 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) bool ssc_good = false; int ret, i; + /* Limit the generation if specified */ + if (pcie->gen) + brcm_pcie_set_gen(pcie, pcie->gen); + /* Unassert the fundamental reset */ ret = pcie->cfg->perst_set(pcie, 0); if (ret) @@ -1350,9 +1354,6 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) brcm_config_clkreq(pcie); - if (pcie->gen) - brcm_pcie_set_gen(pcie, pcie->gen); - if (pcie->ssc) { ret = brcm_pcie_set_ssc(pcie); if (ret == 0) -- cgit v1.2.3-59-g8ed1b From 0c97321e11e0e9e18546f828492758f6aaecec59 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:30 -0500 Subject: PCI: brcmstb: Use internal register to change link capability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver has been mistakenly writing to a read-only (RO) configuration space register (PCI_EXP_LNKCAP) to change the PCIe link capability. Although harmless in this case, the proper write destination is an internal register that is reflected by PCI_EXP_LNKCAP. Thus, fix the brcm_pcie_set_gen() function to correctly update the link capability. Fixes: c0452137034b ("PCI: brcmstb: Add Broadcom STB PCIe host controller driver") Signed-off-by: Jim Quinlan Reviewed-by: Florian Fainelli Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250214173944.47506-3-james.quinlan@broadcom.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-brcmstb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 2f1b2a4fe540..cabbf7fbb8eb 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -413,10 +413,10 @@ static int brcm_pcie_set_ssc(struct brcm_pcie *pcie) static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen) { u16 lnkctl2 = readw(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); - u32 lnkcap = readl(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP); + u32 lnkcap = readl(pcie->base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); lnkcap = (lnkcap & ~PCI_EXP_LNKCAP_SLS) | gen; - writel(lnkcap, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP); + writel(lnkcap, pcie->base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); lnkctl2 = (lnkctl2 & ~0xf) | gen; writew(lnkctl2, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); -- cgit v1.2.3-59-g8ed1b From b5e441793e07873397622ca917517a43aa84fe44 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:31 -0500 Subject: PCI: brcmstb: Do not assume that register field starts at LSB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When setting the LNKCAP and LNKCTL2 register fields, it was assumed that the field started at the LSB of the register. Although the masks do indeed start at the LSB, and this will probably not change, it is prudent to use a method that makes no assumption about the mask's placement in the register. Thus, use the u{16,32}p_replace_bits() helpers since they are already wildly used in this driver. Signed-off-by: Jim Quinlan Reviewed-by: Florian Fainelli Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250214173944.47506-4-james.quinlan@broadcom.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-brcmstb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index cabbf7fbb8eb..4e53d0da510b 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -415,10 +415,10 @@ static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen) u16 lnkctl2 = readw(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); u32 lnkcap = readl(pcie->base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); - lnkcap = (lnkcap & ~PCI_EXP_LNKCAP_SLS) | gen; + u32p_replace_bits(&lnkcap, gen, PCI_EXP_LNKCAP_SLS); writel(lnkcap, pcie->base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); - lnkctl2 = (lnkctl2 & ~0xf) | gen; + u16p_replace_bits(&lnkctl2, gen, PCI_EXP_LNKCTL2_TLS); writew(lnkctl2, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); } -- cgit v1.2.3-59-g8ed1b From 3651ad5249c51cf7eee078e12612557040a6bdb4 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:32 -0500 Subject: PCI: brcmstb: Fix error path after a call to regulator_bulk_get() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the regulator_bulk_get() returns an error and no regulators are created, we need to set their number to zero. If we don't do this and the PCIe link up fails, a call to the regulator_bulk_free() will result in a kernel panic. While at it, print the error value, as we cannot return an error upwards as the kernel will WARN() on an error from add_bus(). Fixes: 9e6be018b263 ("PCI: brcmstb: Enable child bus device regulators from DT") Signed-off-by: Jim Quinlan Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20250214173944.47506-5-james.quinlan@broadcom.com [kwilczynski: commit log, use comma in the message to match style with other similar messages] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-brcmstb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 4e53d0da510b..7d1c6dc599da 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1416,7 +1416,8 @@ static int brcm_pcie_add_bus(struct pci_bus *bus) ret = regulator_bulk_get(dev, sr->num_supplies, sr->supplies); if (ret) { - dev_info(dev, "No regulators for downstream device\n"); + dev_info(dev, "Did not get regulators, err=%d\n", ret); + pcie->sr = NULL; goto no_regulators; } -- cgit v1.2.3-59-g8ed1b From b7de1b60ecab2f7b6f05d8116e93228a0bbb8563 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:33 -0500 Subject: PCI: brcmstb: Fix potential premature regulator disabling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The platform supports enabling and disabling regulators only on ports below the Root Complex. Thus, we need to verify this both when adding and removing the bus, otherwise regulators may be disabled prematurely when a bus further down the topology is removed. Fixes: 9e6be018b263 ("PCI: brcmstb: Enable child bus device regulators from DT") Signed-off-by: Jim Quinlan Reviewed-by: Florian Fainelli Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250214173944.47506-6-james.quinlan@broadcom.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-brcmstb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 7d1c6dc599da..d565153d8a37 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1440,7 +1440,7 @@ static void brcm_pcie_remove_bus(struct pci_bus *bus) struct subdev_regulators *sr = pcie->sr; struct device *dev = &bus->dev; - if (!sr) + if (!sr || !bus->parent || !pci_is_root_bus(bus->parent)) return; if (regulator_bulk_disable(sr->num_supplies, sr->supplies)) -- cgit v1.2.3-59-g8ed1b From 42fd45be82bbb5b6cf0103e9e701d9801709c348 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:34 -0500 Subject: PCI: brcmstb: Use same constant table for config space access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The constants EXT_CFG_DATA and EXT_CFG_INDEX vary by SoC, where one of the map_bus methods used these constants, and the other used a different set of constants. Thankfully, there was no problem because the SoCs that used the latter map_bus method all had the same register constants. Thus, remove redundant constants and adjust the code to use the correct constants accordingly. While at it, update the value of EXT_CFG_DATA to use the 4k-page based configuration space access system, which is what the second map_bus method was already using. Signed-off-by: Jim Quinlan Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20250214173944.47506-7-james.quinlan@broadcom.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-brcmstb.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index d565153d8a37..76f51352709e 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -150,9 +150,6 @@ #define MSI_INT_MASK_SET 0x10 #define MSI_INT_MASK_CLR 0x14 -#define PCIE_EXT_CFG_DATA 0x8000 -#define PCIE_EXT_CFG_INDEX 0x9000 - #define PCIE_RGR1_SW_INIT_1_PERST_MASK 0x1 #define PCIE_RGR1_SW_INIT_1_PERST_SHIFT 0x0 @@ -727,8 +724,8 @@ static void __iomem *brcm_pcie_map_bus(struct pci_bus *bus, /* For devices, write to the config space index register */ idx = PCIE_ECAM_OFFSET(bus->number, devfn, 0); - writel(idx, pcie->base + PCIE_EXT_CFG_INDEX); - return base + PCIE_EXT_CFG_DATA + PCIE_ECAM_REG(where); + writel(idx, base + IDX_ADDR(pcie)); + return base + DATA_ADDR(pcie) + PCIE_ECAM_REG(where); } static void __iomem *brcm7425_pcie_map_bus(struct pci_bus *bus, @@ -1711,7 +1708,7 @@ static void brcm_pcie_remove(struct platform_device *pdev) static const int pcie_offsets[] = { [RGR1_SW_INIT_1] = 0x9210, [EXT_CFG_INDEX] = 0x9000, - [EXT_CFG_DATA] = 0x9004, + [EXT_CFG_DATA] = 0x8000, [PCIE_HARD_DEBUG] = 0x4204, [PCIE_INTR2_CPU_BASE] = 0x4300, }; @@ -1719,7 +1716,7 @@ static const int pcie_offsets[] = { static const int pcie_offsets_bcm7278[] = { [RGR1_SW_INIT_1] = 0xc010, [EXT_CFG_INDEX] = 0x9000, - [EXT_CFG_DATA] = 0x9004, + [EXT_CFG_DATA] = 0x8000, [PCIE_HARD_DEBUG] = 0x4204, [PCIE_INTR2_CPU_BASE] = 0x4300, }; @@ -1733,8 +1730,9 @@ static const int pcie_offsets_bcm7425[] = { }; static const int pcie_offsets_bcm7712[] = { + [RGR1_SW_INIT_1] = 0x9210, [EXT_CFG_INDEX] = 0x9000, - [EXT_CFG_DATA] = 0x9004, + [EXT_CFG_DATA] = 0x8000, [PCIE_HARD_DEBUG] = 0x4304, [PCIE_INTR2_CPU_BASE] = 0x4400, }; -- cgit v1.2.3-59-g8ed1b From a9ec9fb7385e519a793f2a33da5f748e17a0bc27 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:35 -0500 Subject: PCI: brcmstb: Make two changes in MDIO register fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hardware has been updated with two changes to the MDIO packet format. The CMD field used to be 12 bits and now is only 1 bit. This change is backwards compatible because the field's starting bit position is unchanged, and the only commands we've used have values 0 and 1. The PORT field's width has been changed from 4 bits to 5 bits. When written, the new bit is not contiguous with the other four. However, this change is backwards compatible because the driver never used anything other than 0 for the port field's value. Thus, update the existing code to handle new changes to the hardware in a backwards-compatible manner. Signed-off-by: Jim Quinlan Reviewed-by: Florian Fainelli Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250214173944.47506-8-james.quinlan@broadcom.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-brcmstb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 76f51352709e..ab25f674800d 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -175,8 +175,9 @@ #define MDIO_PORT0 0x0 #define MDIO_DATA_MASK 0x7fffffff #define MDIO_PORT_MASK 0xf0000 +#define MDIO_PORT_EXT_MASK 0x200000 #define MDIO_REGAD_MASK 0xffff -#define MDIO_CMD_MASK 0xfff00000 +#define MDIO_CMD_MASK 0x00100000 #define MDIO_CMD_READ 0x1 #define MDIO_CMD_WRITE 0x0 #define MDIO_DATA_DONE_MASK 0x80000000 @@ -327,6 +328,7 @@ static u32 brcm_pcie_mdio_form_pkt(int port, int regad, int cmd) { u32 pkt = 0; + pkt |= FIELD_PREP(MDIO_PORT_EXT_MASK, port >> 4); pkt |= FIELD_PREP(MDIO_PORT_MASK, port); pkt |= FIELD_PREP(MDIO_REGAD_MASK, regad); pkt |= FIELD_PREP(MDIO_CMD_MASK, cmd); -- cgit v1.2.3-59-g8ed1b From 174cfcf13daf98bc4411a5a24a797d2b2f5546cd Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:36 -0500 Subject: PCI: brcmstb: Make irq_domain_set_info() parameter cast explicit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the cast to the irq_hw_number_t type for the parameter passed to irq_domain_set_info() function explicit. Signed-off-by: Jim Quinlan Reviewed-by: Florian Fainelli Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250214173944.47506-9-james.quinlan@broadcom.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-brcmstb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index ab25f674800d..df944453c53a 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -559,7 +559,7 @@ static int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, return hwirq; for (i = 0; i < nr_irqs; i++) - irq_domain_set_info(domain, virq + i, hwirq + i, + irq_domain_set_info(domain, virq + i, (irq_hw_number_t)hwirq + i, &brcm_msi_bottom_irq_chip, domain->host_data, handle_edge_irq, NULL, NULL); return 0; -- cgit v1.2.3-59-g8ed1b From 800ce277f419a9b142a9ca0ec5a054225c5ff05b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 3 Mar 2025 14:42:20 -0600 Subject: PCI: Log debug messages about reset method Log pci_dbg() messages about the reset methods we attempt and any errors (-ENOTTY means "try the next method"). Set CONFIG_DYNAMIC_DEBUG=y and enable by booting with dyndbg="file drivers/pci/* +p" or enable at runtime: # echo "file drivers/pci/* +p" > /sys/kernel/debug/dynamic_debug/control Link: https://lore.kernel.org/r/20250303204220.197172-1-helgaas@kernel.org Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang --- drivers/pci/pci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 869d204a70a3..3d13bb8e5c53 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5230,6 +5230,7 @@ const struct pci_reset_fn_method pci_reset_fn_methods[] = { int __pci_reset_function_locked(struct pci_dev *dev) { int i, m, rc; + const struct pci_reset_fn_method *method; might_sleep(); @@ -5246,9 +5247,13 @@ int __pci_reset_function_locked(struct pci_dev *dev) if (!m) return -ENOTTY; - rc = pci_reset_fn_methods[m].reset_fn(dev, PCI_RESET_DO_RESET); + method = &pci_reset_fn_methods[m]; + pci_dbg(dev, "reset via %s\n", method->name); + rc = method->reset_fn(dev, PCI_RESET_DO_RESET); if (!rc) return 0; + + pci_dbg(dev, "%s failed with %d\n", method->name, rc); if (rc != -ENOTTY) return rc; } -- cgit v1.2.3-59-g8ed1b From 5c8265fa63e4c60cd80f28bb0a682cf97b8a60e9 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 25 Feb 2025 18:06:01 +0100 Subject: PCI: hotplug: Drop superfluous pci_hotplug_slot_list The PCI hotplug core keeps a list of all registered slots. Its sole purpose is to WARN() on slot removal if another slot is using the same name. But this can never happen because already on slot creation, an error is returned and multiple messages are emitted if a slot's name is duplicated: pci_hp_register() __pci_hp_register() __pci_hp_initialize() pci_create_slot() kobject_init_and_add() kobject_add_varg() kobject_add_internal() create_dir() sysfs_create_dir_ns() kernfs_create_dir_ns() sysfs_warn_dup() pr_warn("cannot create duplicate filename ...") pr_err("%s failed for %s with -EEXIST, ..."); Drop the superfluous list. Link: https://lore.kernel.org/r/603735bc50eb370bc7f1c358441ac671360bab25.1740501868.git.lukas@wunner.de Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pci_hotplug_core.c | 32 -------------------------------- include/linux/pci_hotplug.h | 2 -- 2 files changed, 34 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index 36236ac88fd5..9e3cde91c167 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -18,14 +18,12 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #include #include @@ -42,9 +40,6 @@ /* local variables */ static bool debug; -static LIST_HEAD(pci_hotplug_slot_list); -static DEFINE_MUTEX(pci_hp_mutex); - /* Weee, fun with macros... */ #define GET_STATUS(name, type) \ static int get_##name(struct hotplug_slot *slot, type *value) \ @@ -375,17 +370,6 @@ static void fs_remove_slot(struct pci_slot *pci_slot) pci_hp_remove_module_link(pci_slot); } -static struct hotplug_slot *get_slot_from_name(const char *name) -{ - struct hotplug_slot *slot; - - list_for_each_entry(slot, &pci_hotplug_slot_list, slot_list) { - if (strcmp(hotplug_slot_name(slot), name) == 0) - return slot; - } - return NULL; -} - /** * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem * @slot: pointer to the &struct hotplug_slot to register @@ -484,10 +468,6 @@ int pci_hp_add(struct hotplug_slot *slot) return result; kobject_uevent(&pci_slot->kobj, KOBJ_ADD); - mutex_lock(&pci_hp_mutex); - list_add(&slot->slot_list, &pci_hotplug_slot_list); - mutex_unlock(&pci_hp_mutex); - dbg("Added slot %s to the list\n", hotplug_slot_name(slot)); return 0; } EXPORT_SYMBOL_GPL(pci_hp_add); @@ -514,21 +494,9 @@ EXPORT_SYMBOL_GPL(pci_hp_deregister); */ void pci_hp_del(struct hotplug_slot *slot) { - struct hotplug_slot *temp; - if (WARN_ON(!slot)) return; - mutex_lock(&pci_hp_mutex); - temp = get_slot_from_name(hotplug_slot_name(slot)); - if (WARN_ON(temp != slot)) { - mutex_unlock(&pci_hp_mutex); - return; - } - - list_del(&slot->slot_list); - mutex_unlock(&pci_hp_mutex); - dbg("Removed slot %s from the list\n", hotplug_slot_name(slot)); fs_remove_slot(slot->pci_slot); } EXPORT_SYMBOL_GPL(pci_hp_del); diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 3a10d6ec3ee7..ec77ccf1fc4d 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -50,7 +50,6 @@ struct hotplug_slot_ops { /** * struct hotplug_slot - used to register a physical slot with the hotplug pci core * @ops: pointer to the &struct hotplug_slot_ops to be used for this slot - * @slot_list: internal list used to track hotplug PCI slots * @pci_slot: represents a physical slot * @owner: The module owner of this structure * @mod_name: The module name (KBUILD_MODNAME) of this structure @@ -59,7 +58,6 @@ struct hotplug_slot { const struct hotplug_slot_ops *ops; /* Variables below this are for use only by the hotplug pci core. */ - struct list_head slot_list; struct pci_slot *pci_slot; struct module *owner; const char *mod_name; -- cgit v1.2.3-59-g8ed1b From 666550a8066a5333ddc96ca2fd28cb49318e789e Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 25 Feb 2025 18:06:02 +0100 Subject: PCI: hotplug: Drop superfluous try_module_get() calls In December 2002, historic commit https://git.kernel.org/tglx/history/c/bec7aa00ffe5 ("[PATCH] more module warning fixes") amended the PCI hotplug core to acquire a reference on the hotplug driver module when a sysfs attribute is accessed. That was necessary because back in the day, sysfs code did not take any precautions to prevent module unloading when an attribute was accessed. Soon after in July 2003, historic commit https://git.kernel.org/tglx/history/c/1cf6d20f6078 ("[PATCH] SYSFS: add module referencing to sysfs attribute files.") addressed that deficiency. But the commit neglected to remove the now unnecessary reference acquisition from the PCI hotplug core. The commit acquired a module reference for the entire duration between open() and close() of a sysfs attribute. This made it impossible to unload a module while attributes were kept open by user space. That's possible today: When a hotplug driver module is unloaded, it removes sysfs attributes of all its hotplug slots by calling pci_hp_del(). This will wait for any concurrent user space operation to finish: pci_hp_del() fs_remove_slot() sysfs_remove_file() sysfs_remove_file_ns() kernfs_remove_by_name_ns() __kernfs_remove() kernfs_drain() A user space operation such as read() briefly acquires a reference on the attribute with kernfs_get_active(). kernfs_drain() waits until all such references are released before allowing attribute removal. Once the attribute is removed, any subsequent user space operation on a still open attribute file will return -ENODEV. Thus, reference acquisition by the PCI hotplug core is still unnecessary today. So drop it at long last. Link: https://lore.kernel.org/r/ed950fa2722967be4491146c7b867c1e7be11d37.1740501868.git.lukas@wunner.de Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pci_hotplug_core.c | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index 9e3cde91c167..de5b501b40be 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -14,7 +14,7 @@ * Scott Murray */ -#include /* try_module_get & module_put */ +#include #include #include #include @@ -46,11 +46,8 @@ static int get_##name(struct hotplug_slot *slot, type *value) \ { \ const struct hotplug_slot_ops *ops = slot->ops; \ int retval = 0; \ - if (!try_module_get(slot->owner)) \ - return -ENODEV; \ if (ops->get_##name) \ retval = ops->get_##name(slot, value); \ - module_put(slot->owner); \ return retval; \ } @@ -83,10 +80,6 @@ static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf, power = (u8)(lpower & 0xff); dbg("power = %d\n", power); - if (!try_module_get(slot->owner)) { - retval = -ENODEV; - goto exit; - } switch (power) { case 0: if (slot->ops->disable_slot) @@ -102,9 +95,7 @@ static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf, err("Illegal value specified for power\n"); retval = -EINVAL; } - module_put(slot->owner); -exit: if (retval) return retval; return count; @@ -141,15 +132,9 @@ static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf, attention = (u8)(lattention & 0xff); dbg(" - attention = %d\n", attention); - if (!try_module_get(slot->owner)) { - retval = -ENODEV; - goto exit; - } if (ops->set_attention_status) retval = ops->set_attention_status(slot, attention); - module_put(slot->owner); -exit: if (retval) return retval; return count; @@ -207,15 +192,9 @@ static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf, test = (u32)(ltest & 0xffffffff); dbg("test = %d\n", test); - if (!try_module_get(slot->owner)) { - retval = -ENODEV; - goto exit; - } if (slot->ops->hardware_test) retval = slot->ops->hardware_test(slot, test); - module_put(slot->owner); -exit: if (retval) return retval; return count; -- cgit v1.2.3-59-g8ed1b From 62460bcb5a2ac37bb416aada0167db3fe78ac385 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 25 Feb 2025 18:06:03 +0100 Subject: PCI: hotplug: Drop superfluous NULL pointer checks in has_*_file() The PCI hotplug core contains five has_*_file() functions to determine whether a certain sysfs file shall be added (or removed) for a given hotplug slot. The functions perform NULL pointer checks for the hotplug_slot and its hotplug_slot_ops. However the callers already perform these checks: pci_hp_register() __pci_hp_register() __pci_hp_initialize() pci_hp_deregister() pci_hp_del() The only way to actually trigger these checks is to call pci_hp_add() without having called pci_hp_initialize(). Amend pci_hp_add() to catch that and drop the now superfluous NULL pointer checks in has_*_file(). Drop the same superfluous checks from pci_hp_create_module_link(), which is (only) called from pci_hp_add(). Link: https://lore.kernel.org/r/37d1928edf8c3201a8b10794f1db3142e16e02b9.1740501868.git.lukas@wunner.de Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pci_hotplug_core.c | 17 ++++++----------- drivers/pci/slot.c | 2 -- 2 files changed, 6 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index de5b501b40be..d4c12451570b 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -209,8 +209,6 @@ static bool has_power_file(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; - if ((!slot) || (!slot->ops)) - return false; if ((slot->ops->enable_slot) || (slot->ops->disable_slot) || (slot->ops->get_power_status)) @@ -222,8 +220,6 @@ static bool has_attention_file(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; - if ((!slot) || (!slot->ops)) - return false; if ((slot->ops->set_attention_status) || (slot->ops->get_attention_status)) return true; @@ -234,8 +230,6 @@ static bool has_latch_file(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; - if ((!slot) || (!slot->ops)) - return false; if (slot->ops->get_latch_status) return true; return false; @@ -245,8 +239,6 @@ static bool has_adapter_file(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; - if ((!slot) || (!slot->ops)) - return false; if (slot->ops->get_adapter_status) return true; return false; @@ -256,8 +248,6 @@ static bool has_test_file(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; - if ((!slot) || (!slot->ops)) - return false; if (slot->ops->hardware_test) return true; return false; @@ -439,9 +429,14 @@ EXPORT_SYMBOL_GPL(__pci_hp_initialize); */ int pci_hp_add(struct hotplug_slot *slot) { - struct pci_slot *pci_slot = slot->pci_slot; + struct pci_slot *pci_slot; int result; + if (WARN_ON(!slot)) + return -EINVAL; + + pci_slot = slot->pci_slot; + result = fs_add_slot(pci_slot); if (result) return result; diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 36b44be0489d..dd6e80b7db09 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -340,8 +340,6 @@ void pci_hp_create_module_link(struct pci_slot *pci_slot) struct kobject *kobj = NULL; int ret; - if (!slot || !slot->ops) - return; kobj = kset_find_obj(module_kset, slot->mod_name); if (!kobj) return; -- cgit v1.2.3-59-g8ed1b From 34bd6141a62d21853b61e759f5c617059b2f3655 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 25 Feb 2025 18:06:04 +0100 Subject: PCI: hotplug: Avoid backpointer dereferencing in has_*_file() The PCI hotplug core contains five has_*_file() functions to determine whether a certain sysfs file shall be added (or removed) for a given hotplug slot. The functions receive a struct pci_slot pointer which they have to dereference back to a struct hotplug_slot. Avoid by passing them a struct hotplug_slot pointer directly. Link: https://lore.kernel.org/r/5b2f5b4ac45285953d00fd7637732a93fd40d26e.1740501868.git.lukas@wunner.de Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pci_hotplug_core.c | 56 ++++++++++++++-------------------- 1 file changed, 23 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index d4c12451570b..a992bf51af98 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -205,10 +205,8 @@ static struct pci_slot_attribute hotplug_slot_attr_test = { .store = test_write_file }; -static bool has_power_file(struct pci_slot *pci_slot) +static bool has_power_file(struct hotplug_slot *slot) { - struct hotplug_slot *slot = pci_slot->hotplug; - if ((slot->ops->enable_slot) || (slot->ops->disable_slot) || (slot->ops->get_power_status)) @@ -216,79 +214,71 @@ static bool has_power_file(struct pci_slot *pci_slot) return false; } -static bool has_attention_file(struct pci_slot *pci_slot) +static bool has_attention_file(struct hotplug_slot *slot) { - struct hotplug_slot *slot = pci_slot->hotplug; - if ((slot->ops->set_attention_status) || (slot->ops->get_attention_status)) return true; return false; } -static bool has_latch_file(struct pci_slot *pci_slot) +static bool has_latch_file(struct hotplug_slot *slot) { - struct hotplug_slot *slot = pci_slot->hotplug; - if (slot->ops->get_latch_status) return true; return false; } -static bool has_adapter_file(struct pci_slot *pci_slot) +static bool has_adapter_file(struct hotplug_slot *slot) { - struct hotplug_slot *slot = pci_slot->hotplug; - if (slot->ops->get_adapter_status) return true; return false; } -static bool has_test_file(struct pci_slot *pci_slot) +static bool has_test_file(struct hotplug_slot *slot) { - struct hotplug_slot *slot = pci_slot->hotplug; - if (slot->ops->hardware_test) return true; return false; } -static int fs_add_slot(struct pci_slot *pci_slot) +static int fs_add_slot(struct hotplug_slot *slot, struct pci_slot *pci_slot) { int retval = 0; /* Create symbolic link to the hotplug driver module */ pci_hp_create_module_link(pci_slot); - if (has_power_file(pci_slot)) { + if (has_power_file(slot)) { retval = sysfs_create_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr); if (retval) goto exit_power; } - if (has_attention_file(pci_slot)) { + if (has_attention_file(slot)) { retval = sysfs_create_file(&pci_slot->kobj, &hotplug_slot_attr_attention.attr); if (retval) goto exit_attention; } - if (has_latch_file(pci_slot)) { + if (has_latch_file(slot)) { retval = sysfs_create_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr); if (retval) goto exit_latch; } - if (has_adapter_file(pci_slot)) { + if (has_adapter_file(slot)) { retval = sysfs_create_file(&pci_slot->kobj, &hotplug_slot_attr_presence.attr); if (retval) goto exit_adapter; } - if (has_test_file(pci_slot)) { + if (has_test_file(slot)) { retval = sysfs_create_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr); if (retval) @@ -298,18 +288,18 @@ static int fs_add_slot(struct pci_slot *pci_slot) goto exit; exit_test: - if (has_adapter_file(pci_slot)) + if (has_adapter_file(slot)) sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_presence.attr); exit_adapter: - if (has_latch_file(pci_slot)) + if (has_latch_file(slot)) sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr); exit_latch: - if (has_attention_file(pci_slot)) + if (has_attention_file(slot)) sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_attention.attr); exit_attention: - if (has_power_file(pci_slot)) + if (has_power_file(slot)) sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr); exit_power: pci_hp_remove_module_link(pci_slot); @@ -317,23 +307,23 @@ exit: return retval; } -static void fs_remove_slot(struct pci_slot *pci_slot) +static void fs_remove_slot(struct hotplug_slot *slot, struct pci_slot *pci_slot) { - if (has_power_file(pci_slot)) + if (has_power_file(slot)) sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr); - if (has_attention_file(pci_slot)) + if (has_attention_file(slot)) sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_attention.attr); - if (has_latch_file(pci_slot)) + if (has_latch_file(slot)) sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr); - if (has_adapter_file(pci_slot)) + if (has_adapter_file(slot)) sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_presence.attr); - if (has_test_file(pci_slot)) + if (has_test_file(slot)) sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr); pci_hp_remove_module_link(pci_slot); @@ -437,7 +427,7 @@ int pci_hp_add(struct hotplug_slot *slot) pci_slot = slot->pci_slot; - result = fs_add_slot(pci_slot); + result = fs_add_slot(slot, pci_slot); if (result) return result; @@ -471,7 +461,7 @@ void pci_hp_del(struct hotplug_slot *slot) if (WARN_ON(!slot)) return; - fs_remove_slot(slot->pci_slot); + fs_remove_slot(slot, slot->pci_slot); } EXPORT_SYMBOL_GPL(pci_hp_del); -- cgit v1.2.3-59-g8ed1b From cc973ef13f8e31608d34c349c6ed85d44d716523 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 25 Feb 2025 18:06:05 +0100 Subject: PCI: hotplug: Inline pci_hp_{create,remove}_module_link() For no apparent reason, the pci_hp_{create,remove}_module_link() helpers live in slot.c, even though they're only called from two functions in pci_hotplug_core.c. Inline the helpers to reduce code size and number of exported symbols. Link: https://lore.kernel.org/r/c207f03cfe32ae9002d9b453001a1dd63d9ab3fb.1740501868.git.lukas@wunner.de Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pci_hotplug_core.c | 14 +++++++++--- drivers/pci/slot.c | 42 ---------------------------------- include/linux/pci.h | 5 ---- 3 files changed, 11 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index a992bf51af98..d30f1316c98e 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -245,10 +245,18 @@ static bool has_test_file(struct hotplug_slot *slot) static int fs_add_slot(struct hotplug_slot *slot, struct pci_slot *pci_slot) { + struct kobject *kobj; int retval = 0; /* Create symbolic link to the hotplug driver module */ - pci_hp_create_module_link(pci_slot); + kobj = kset_find_obj(module_kset, slot->mod_name); + if (kobj) { + retval = sysfs_create_link(&pci_slot->kobj, kobj, "module"); + if (retval) + dev_err(&pci_slot->bus->dev, + "Error creating sysfs link (%d)\n", retval); + kobject_put(kobj); + } if (has_power_file(slot)) { retval = sysfs_create_file(&pci_slot->kobj, @@ -302,7 +310,7 @@ exit_attention: if (has_power_file(slot)) sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr); exit_power: - pci_hp_remove_module_link(pci_slot); + sysfs_remove_link(&pci_slot->kobj, "module"); exit: return retval; } @@ -326,7 +334,7 @@ static void fs_remove_slot(struct hotplug_slot *slot, struct pci_slot *pci_slot) if (has_test_file(slot)) sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr); - pci_hp_remove_module_link(pci_slot); + sysfs_remove_link(&pci_slot->kobj, "module"); } /** diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index dd6e80b7db09..50fb3eb595fe 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -7,7 +7,6 @@ #include #include -#include #include #include #include "pci.h" @@ -325,47 +324,6 @@ void pci_destroy_slot(struct pci_slot *slot) } EXPORT_SYMBOL_GPL(pci_destroy_slot); -#if defined(CONFIG_HOTPLUG_PCI) || defined(CONFIG_HOTPLUG_PCI_MODULE) -#include -/** - * pci_hp_create_module_link - create symbolic link to hotplug driver module - * @pci_slot: struct pci_slot - * - * Helper function for pci_hotplug_core.c to create symbolic link to - * the hotplug driver module. - */ -void pci_hp_create_module_link(struct pci_slot *pci_slot) -{ - struct hotplug_slot *slot = pci_slot->hotplug; - struct kobject *kobj = NULL; - int ret; - - kobj = kset_find_obj(module_kset, slot->mod_name); - if (!kobj) - return; - ret = sysfs_create_link(&pci_slot->kobj, kobj, "module"); - if (ret) - dev_err(&pci_slot->bus->dev, "Error creating sysfs link (%d)\n", - ret); - kobject_put(kobj); -} -EXPORT_SYMBOL_GPL(pci_hp_create_module_link); - -/** - * pci_hp_remove_module_link - remove symbolic link to the hotplug driver - * module. - * @pci_slot: struct pci_slot - * - * Helper function for pci_hotplug_core.c to remove symbolic link to - * the hotplug driver module. - */ -void pci_hp_remove_module_link(struct pci_slot *pci_slot) -{ - sysfs_remove_link(&pci_slot->kobj, "module"); -} -EXPORT_SYMBOL_GPL(pci_hp_remove_module_link); -#endif - static int pci_slot_init(void) { struct kset *pci_bus_kset; diff --git a/include/linux/pci.h b/include/linux/pci.h index 47b31ad724fa..a0f5c8fcd9c7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2447,11 +2447,6 @@ static inline resource_size_t pci_iov_resource_size(struct pci_dev *dev, int res static inline void pci_vf_drivers_autoprobe(struct pci_dev *dev, bool probe) { } #endif -#if defined(CONFIG_HOTPLUG_PCI) || defined(CONFIG_HOTPLUG_PCI_MODULE) -void pci_hp_create_module_link(struct pci_slot *pci_slot); -void pci_hp_remove_module_link(struct pci_slot *pci_slot); -#endif - /** * pci_pcie_cap - get the saved PCIe capability offset * @dev: PCI device -- cgit v1.2.3-59-g8ed1b From 9d7db4db19827380e225914618c0c1bf435ed2f5 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Mon, 3 Mar 2025 10:36:30 +0800 Subject: PCI/portdrv: Only disable pciehp interrupts early when needed Firmware developers reported that Linux issues two PCIe hotplug commands in very short intervals on an ARM server, which doesn't comply with the PCIe spec. According to PCIe r6.1, sec 6.7.3.2, if the Command Completed event is supported, software must wait for a command to complete or wait at least 1 second before sending a new command. In the failure case, the first PCIe hotplug command is from get_port_device_capability(), which sends a command to disable PCIe hotplug interrupts without waiting for its completion, and the second command comes from pcie_enable_notification() of pciehp driver, which enables hotplug interrupts again. Fix this by only disabling the hotplug interrupts when the pciehp driver is not enabled. Link: https://lore.kernel.org/r/20250303023630.78397-1-feng.tang@linux.alibaba.com Fixes: 2bd50dd800b5 ("PCI: PCIe: Disable PCIe port services during port initialization") Suggested-by: Lukas Wunner Signed-off-by: Feng Tang [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Reviewed-by: Lukas Wunner Reviewed-by: Kuppuswamy Sathyanarayanan --- drivers/pci/pcie/portdrv.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c index 02e73099bad0..e8318fd5f6ed 100644 --- a/drivers/pci/pcie/portdrv.c +++ b/drivers/pci/pcie/portdrv.c @@ -228,10 +228,12 @@ static int get_port_device_capability(struct pci_dev *dev) /* * Disable hot-plug interrupts in case they have been enabled - * by the BIOS and the hot-plug service driver is not loaded. + * by the BIOS and the hot-plug service driver won't be loaded + * to handle them. */ - pcie_capability_clear_word(dev, PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE); + if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE)) + pcie_capability_clear_word(dev, PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE); } #ifdef CONFIG_PCIEAER -- cgit v1.2.3-59-g8ed1b From 479380efe1625e251008d24b2810283db60d6fcd Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Fri, 7 Feb 2025 14:56:00 -0600 Subject: PCI: Avoid reset when disabled via sysfs After d88f521da3ef ("PCI: Allow userspace to query and set device reset mechanism"), userspace can disable reset of specific PCI devices by writing an empty string to the sysfs reset_method file. However, pci_slot_resettable() does not check pci_reset_supported(), which means that pci_reset_function() will still reset the device even if userspace has disabled all the reset methods. I was able to reproduce this issue with a vfio device passed to a qemu guest, where I had disabled PCI reset via sysfs. Add an explicit check of pci_reset_supported() in both pci_slot_resettable() and pci_bus_resettable() to ensure both the reset status and reset execution are bypassed if an administrator disables it for a device. Link: https://lore.kernel.org/r/20250207205600.1846178-1-naravamudan@nvidia.com Fixes: d88f521da3ef ("PCI: Allow userspace to query and set device reset mechanism") Signed-off-by: Nishanth Aravamudan [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Cc: Alex Williamson Cc: Raphael Norwitz Cc: Amey Narkhede Cc: Jason Gunthorpe Cc: Yishai Hadas Cc: Shameer Kolothum Cc: Kevin Tian --- drivers/pci/pci.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 3d13bb8e5c53..c6c25b910bde 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5410,6 +5410,8 @@ static bool pci_bus_resettable(struct pci_bus *bus) return false; list_for_each_entry(dev, &bus->devices, bus_list) { + if (!pci_reset_supported(dev)) + return false; if (dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET || (dev->subordinate && !pci_bus_resettable(dev->subordinate))) return false; @@ -5486,6 +5488,8 @@ static bool pci_slot_resettable(struct pci_slot *slot) list_for_each_entry(dev, &slot->bus->devices, bus_list) { if (!dev->slot || dev->slot != slot) continue; + if (!pci_reset_supported(dev)) + return false; if (dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET || (dev->subordinate && !pci_bus_resettable(dev->subordinate))) return false; -- cgit v1.2.3-59-g8ed1b From 9a0f3c50bd51bd60cedbfeb37b55368fcb70b1b6 Mon Sep 17 00:00:00 2001 From: Zhang Zekun Date: Sat, 31 Aug 2024 12:04:08 +0800 Subject: PCI: kirin: Use helper function for_each_available_child_of_node_scoped() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The for_each_available_child_of_node_scoped() helper provides a scope-based clean-up functionality to put the device_node automatically, and as such, there is no need to call of_node_put() directly. Thus, use this helper to simplify the code. Signed-off-by: Zhang Zekun Reviewed-by: Jonathan Cameron Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20240831040413.126417-2-zhangzekun11@huawei.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/dwc/pcie-kirin.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c index 1b2088acb538..eeff8bfcdf8b 100644 --- a/drivers/pci/controller/dwc/pcie-kirin.c +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -452,7 +452,7 @@ static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie, struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *child, *node = dev->of_node; + struct device_node *node = dev->of_node; void __iomem *apb_base; int ret; @@ -477,17 +477,13 @@ static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie, return ret; /* Parse OF children */ - for_each_available_child_of_node(node, child) { + for_each_available_child_of_node_scoped(node, child) { ret = kirin_pcie_parse_port(kirin_pcie, pdev, child); if (ret) - goto put_node; + return ret; } return 0; - -put_node: - of_node_put(child); - return ret; } static void kirin_pcie_sideband_dbi_w_mode(struct kirin_pcie *kirin_pcie, -- cgit v1.2.3-59-g8ed1b From 98e87cc501c1018f11815e3e2fb20e4801243031 Mon Sep 17 00:00:00 2001 From: Charles Han Date: Wed, 5 Mar 2025 15:00:22 +0800 Subject: PCI: mediatek-gen3: Fix inconsistent indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following inconsistent indentation warning: drivers/pci/controller/pcie-mediatek-gen3.c:922 mtk_pcie_parse_port() warn: inconsistent indenting Found using Smatch. No functional changes intended. Signed-off-by: Charles Han Link: https://lore.kernel.org/r/20250305070022.4668-1-hanchunchao@inspur.com [kwilczynski: commit log, refactor if-statement around num_lanes to make it more readable, wrap overly long lines to fit 80 colums] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-mediatek-gen3.c | 35 ++++++++++++++++++----------- 1 file changed, 22 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 3583e5481dc8..9d52504acae4 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -354,7 +354,8 @@ static int mtk_pcie_set_trans_table(struct mtk_gen3_pcie *pcie, dev_dbg(pcie->dev, "set %s trans window[%d]: cpu_addr = %#llx, pci_addr = %#llx, size = %#llx\n", range_type, *num, (unsigned long long)cpu_addr, - (unsigned long long)pci_addr, (unsigned long long)table_size); + (unsigned long long)pci_addr, + (unsigned long long)table_size); cpu_addr += table_size; pci_addr += table_size; @@ -889,7 +890,8 @@ static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie) for (i = 0; i < num_resets; i++) pcie->phy_resets[i].id = pcie->soc->phy_resets.id[i]; - ret = devm_reset_control_bulk_get_optional_shared(dev, num_resets, pcie->phy_resets); + ret = devm_reset_control_bulk_get_optional_shared(dev, num_resets, + pcie->phy_resets); if (ret) { dev_err(dev, "failed to get PHY bulk reset\n"); return ret; @@ -919,13 +921,14 @@ static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie) return pcie->num_clks; } - ret = of_property_read_u32(dev->of_node, "num-lanes", &num_lanes); - if (ret == 0) { - if (num_lanes == 0 || num_lanes > 16 || (num_lanes != 1 && num_lanes % 2)) + ret = of_property_read_u32(dev->of_node, "num-lanes", &num_lanes); + if (ret == 0) { + if (num_lanes == 0 || num_lanes > 16 || + (num_lanes != 1 && num_lanes % 2)) dev_warn(dev, "invalid num-lanes, using controller defaults\n"); - else + else pcie->num_lanes = num_lanes; - } + } return 0; } @@ -986,7 +989,8 @@ static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie) goto err_phy_on; } - err = reset_control_bulk_deassert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); + err = reset_control_bulk_deassert(pcie->soc->phy_resets.num_resets, + pcie->phy_resets); if (err) { dev_err(dev, "failed to deassert PHYs\n"); goto err_phy_deassert; @@ -1031,7 +1035,8 @@ static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie) err_clk_prepare_enable: pm_runtime_put_sync(dev); pm_runtime_disable(dev); - reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); + reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, + pcie->phy_resets); err_phy_deassert: phy_power_off(pcie->phy); err_phy_on: @@ -1055,7 +1060,8 @@ static int mtk_pcie_power_up(struct mtk_gen3_pcie *pcie) usleep_range(PCIE_MTK_RESET_TIME_US, 2 * PCIE_MTK_RESET_TIME_US); /* PHY power on and enable pipe clock */ - err = reset_control_bulk_deassert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); + err = reset_control_bulk_deassert(pcie->soc->phy_resets.num_resets, + pcie->phy_resets); if (err) { dev_err(dev, "failed to deassert PHYs\n"); return err; @@ -1095,7 +1101,8 @@ err_clk_init: err_phy_on: phy_exit(pcie->phy); err_phy_init: - reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); + reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, + pcie->phy_resets); return err; } @@ -1110,7 +1117,8 @@ static void mtk_pcie_power_down(struct mtk_gen3_pcie *pcie) phy_power_off(pcie->phy); phy_exit(pcie->phy); - reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); + reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, + pcie->phy_resets); } static int mtk_pcie_get_controller_max_link_speed(struct mtk_gen3_pcie *pcie) @@ -1137,7 +1145,8 @@ static int mtk_pcie_setup(struct mtk_gen3_pcie *pcie) * Deassert the line in order to avoid unbalance in deassert_count * counter since the bulk is shared. */ - reset_control_bulk_deassert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); + reset_control_bulk_deassert(pcie->soc->phy_resets.num_resets, + pcie->phy_resets); /* Don't touch the hardware registers before power up */ err = pcie->soc->power_up(pcie); -- cgit v1.2.3-59-g8ed1b From 4fbfa17f9a075593281034f566ca79cbf4930c82 Mon Sep 17 00:00:00 2001 From: Shradha Todi Date: Fri, 21 Feb 2025 18:45:46 +0530 Subject: PCI: dwc: Add debugfs based Silicon Debug support for DWC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support to provide Silicon Debug interface to userspace. This set of debug registers are part of the RAS DES feature present in DesignWare PCIe controllers. Co-developed-by: Manivannan Sadhasivam Signed-off-by: Shradha Todi Reviewed-by: Manivannan Sadhasivam Reviewed-by: Fan Ni Tested-by: Hrishikesh Deleep Link: https://lore.kernel.org/r/20250221131548.59616-4-shradha.t@samsung.com [kwilczynski: commit log, tidy up Kconfig and drop "default y", tidy up code comments, squashed patch that fixes a NULL pointer dereference when debugfs is already unavailable during clean-up from https://lore.kernel.org/linux-pci/20250225171239.19574-2-manivannan.sadhasivam@linaro.org, refactor dwc_pcie_debugfs_init() to not return errors, squashed patch that changes how lack of the RAS DES capability is handled from https://lore.kernel.org/linux-pci/20250304151814.6xu7cbpwpqrvcad5@thinkpad] Signed-off-by: Krzysztof Wilczyński --- Documentation/ABI/testing/debugfs-dwc-pcie | 13 ++ drivers/pci/controller/dwc/Kconfig | 10 ++ drivers/pci/controller/dwc/Makefile | 1 + .../pci/controller/dwc/pcie-designware-debugfs.c | 186 +++++++++++++++++++++ drivers/pci/controller/dwc/pcie-designware-ep.c | 3 + drivers/pci/controller/dwc/pcie-designware-host.c | 4 + drivers/pci/controller/dwc/pcie-designware.c | 6 + drivers/pci/controller/dwc/pcie-designware.h | 20 +++ include/linux/pcie-dwc.h | 2 + 9 files changed, 245 insertions(+) create mode 100644 Documentation/ABI/testing/debugfs-dwc-pcie create mode 100644 drivers/pci/controller/dwc/pcie-designware-debugfs.c (limited to 'drivers') diff --git a/Documentation/ABI/testing/debugfs-dwc-pcie b/Documentation/ABI/testing/debugfs-dwc-pcie new file mode 100644 index 000000000000..5b87471dfee3 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-dwc-pcie @@ -0,0 +1,13 @@ +What: /sys/kernel/debug/dwc_pcie_/rasdes_debug/lane_detect +Date: February 2025 +Contact: Shradha Todi +Description: (RW) Write the lane number to be checked for detection. Read + will return whether PHY indicates receiver detection on the + selected lane. The default selected lane is Lane0. + +What: /sys/kernel/debug/dwc_pcie_/rasdes_debug/rx_valid +Date: February 2025 +Contact: Shradha Todi +Description: (RW) Write the lane number to be checked as valid or invalid. + Read will return the status of PIPE RXVALID signal of the + selected lane. The default selected lane is Lane0. diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index b6d6778b0698..3f13669015cc 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -6,6 +6,16 @@ menu "DesignWare-based PCIe controllers" config PCIE_DW bool +config PCIE_DW_DEBUGFS + bool "DesignWare PCIe debugfs entries" + depends on DEBUG_FS + depends on PCIE_DW_HOST || PCIE_DW_EP + help + Say Y here to enable debugfs entries for the PCIe controller. These + entries provide various debug features related to the controller and + expose the RAS DES capabilities such as Silicon Debug, Error Injection + and Statistical Counters. + config PCIE_DW_HOST bool select PCIE_DW diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index a8308d9ea986..54565eedc52c 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PCIE_DW) += pcie-designware.o +obj-$(CONFIG_PCIE_DW_DEBUGFS) += pcie-designware-debugfs.o obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c new file mode 100644 index 000000000000..c86befa2fdb0 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synopsys DesignWare PCIe controller debugfs driver + * + * Copyright (C) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Shradha Todi + */ + +#include + +#include "pcie-designware.h" + +#define SD_STATUS_L1LANE_REG 0xb0 +#define PIPE_RXVALID BIT(18) +#define PIPE_DETECT_LANE BIT(17) +#define LANE_SELECT GENMASK(3, 0) + +#define DWC_DEBUGFS_BUF_MAX 128 + +/** + * struct dwc_pcie_rasdes_info - Stores controller common information + * @ras_cap_offset: RAS DES vendor specific extended capability offset + * @reg_event_lock: Mutex used for RAS DES shadow event registers + * + * Any parameter constant to all files of the debugfs hierarchy for a single + * controller will be stored in this struct. It is allocated and assigned to + * controller specific struct dw_pcie during initialization. + */ +struct dwc_pcie_rasdes_info { + u32 ras_cap_offset; + struct mutex reg_event_lock; +}; + +static ssize_t lane_detect_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct dw_pcie *pci = file->private_data; + struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + char debugfs_buf[DWC_DEBUGFS_BUF_MAX]; + ssize_t pos; + u32 val; + + val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG); + val = FIELD_GET(PIPE_DETECT_LANE, val); + if (val) + pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "Lane Detected\n"); + else + pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "Lane Undetected\n"); + + return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos); +} + +static ssize_t lane_detect_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct dw_pcie *pci = file->private_data; + struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + u32 lane, val; + + val = kstrtou32_from_user(buf, count, 0, &lane); + if (val) + return val; + + val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG); + val &= ~(LANE_SELECT); + val |= FIELD_PREP(LANE_SELECT, lane); + dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG, val); + + return count; +} + +static ssize_t rx_valid_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct dw_pcie *pci = file->private_data; + struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + char debugfs_buf[DWC_DEBUGFS_BUF_MAX]; + ssize_t pos; + u32 val; + + val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG); + val = FIELD_GET(PIPE_RXVALID, val); + if (val) + pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "RX Valid\n"); + else + pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "RX Invalid\n"); + + return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos); +} + +static ssize_t rx_valid_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return lane_detect_write(file, buf, count, ppos); +} + +#define dwc_debugfs_create(name) \ +debugfs_create_file(#name, 0644, rasdes_debug, pci, \ + &dbg_ ## name ## _fops) + +#define DWC_DEBUGFS_FOPS(name) \ +static const struct file_operations dbg_ ## name ## _fops = { \ + .open = simple_open, \ + .read = name ## _read, \ + .write = name ## _write \ +} + +DWC_DEBUGFS_FOPS(lane_detect); +DWC_DEBUGFS_FOPS(rx_valid); + +static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci) +{ + struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + + mutex_destroy(&rinfo->reg_event_lock); +} + +static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir) +{ + struct dentry *rasdes_debug; + struct dwc_pcie_rasdes_info *rasdes_info; + struct device *dev = pci->dev; + int ras_cap; + + /* + * If a given SoC has no RAS DES capability, the following call is + * bound to return an error, breaking some existing platforms. So, + * return 0 here, as this is not necessarily an error. + */ + ras_cap = dw_pcie_find_rasdes_capability(pci); + if (!ras_cap) { + dev_dbg(dev, "no RAS DES capability available\n"); + return 0; + } + + rasdes_info = devm_kzalloc(dev, sizeof(*rasdes_info), GFP_KERNEL); + if (!rasdes_info) + return -ENOMEM; + + /* Create subdirectories for Debug, Error Injection, Statistics. */ + rasdes_debug = debugfs_create_dir("rasdes_debug", dir); + + mutex_init(&rasdes_info->reg_event_lock); + rasdes_info->ras_cap_offset = ras_cap; + pci->debugfs->rasdes_info = rasdes_info; + + /* Create debugfs files for Debug subdirectory. */ + dwc_debugfs_create(lane_detect); + dwc_debugfs_create(rx_valid); + + return 0; +} + +void dwc_pcie_debugfs_deinit(struct dw_pcie *pci) +{ + if (!pci->debugfs) + return; + + dwc_pcie_rasdes_debugfs_deinit(pci); + debugfs_remove_recursive(pci->debugfs->debug_dir); +} + +void dwc_pcie_debugfs_init(struct dw_pcie *pci) +{ + char dirname[DWC_DEBUGFS_BUF_MAX]; + struct device *dev = pci->dev; + struct debugfs_info *debugfs; + struct dentry *dir; + int err; + + /* Create main directory for each platform driver. */ + snprintf(dirname, DWC_DEBUGFS_BUF_MAX, "dwc_pcie_%s", dev_name(dev)); + dir = debugfs_create_dir(dirname, NULL); + debugfs = devm_kzalloc(dev, sizeof(*debugfs), GFP_KERNEL); + if (!debugfs) + return; + + debugfs->debug_dir = dir; + pci->debugfs = debugfs; + err = dwc_pcie_rasdes_debugfs_init(pci, dir); + if (err) + dev_err(dev, "failed to initialize RAS DES debugfs, err=%d\n", + err); +} diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 8e07d432e74f..11ff292ca87d 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -666,6 +666,7 @@ void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + dwc_pcie_debugfs_deinit(pci); dw_pcie_edma_remove(pci); } EXPORT_SYMBOL_GPL(dw_pcie_ep_cleanup); @@ -837,6 +838,8 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) dw_pcie_ep_init_non_sticky_registers(pci); + dwc_pcie_debugfs_init(pci); + return 0; err_remove_edma: diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index ffaded8f2df7..6501fb062c70 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -548,6 +548,8 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) if (pp->ops->post_init) pp->ops->post_init(pp); + dwc_pcie_debugfs_init(pci); + return 0; err_stop_link: @@ -572,6 +574,8 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + dwc_pcie_debugfs_deinit(pci); + pci_stop_root_bus(pp->bridge->bus); pci_remove_root_bus(pp->bridge->bus); diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index a7c0671c6715..3d1d95d9e380 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -323,6 +323,12 @@ static u16 dw_pcie_find_vsec_capability(struct dw_pcie *pci, return 0; } +u16 dw_pcie_find_rasdes_capability(struct dw_pcie *pci) +{ + return dw_pcie_find_vsec_capability(pci, dwc_pcie_rasdes_vsec_ids); +} +EXPORT_SYMBOL_GPL(dw_pcie_find_rasdes_capability); + int dw_pcie_read(void __iomem *addr, int size, u32 *val) { if (!IS_ALIGNED((uintptr_t)addr, size)) { diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 501d9ddfea16..dd129718fb41 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -437,6 +437,11 @@ struct dw_pcie_ops { void (*stop_link)(struct dw_pcie *pcie); }; +struct debugfs_info { + struct dentry *debug_dir; + void *rasdes_info; +}; + struct dw_pcie { struct device *dev; void __iomem *dbi_base; @@ -465,6 +470,7 @@ struct dw_pcie { struct reset_control_bulk_data core_rsts[DW_PCIE_NUM_CORE_RSTS]; struct gpio_desc *pe_rst; bool suspended; + struct debugfs_info *debugfs; }; #define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp) @@ -478,6 +484,7 @@ void dw_pcie_version_detect(struct dw_pcie *pci); u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap); u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap); +u16 dw_pcie_find_rasdes_capability(struct dw_pcie *pci); int dw_pcie_read(void __iomem *addr, int size, u32 *val); int dw_pcie_write(void __iomem *addr, int size, u32 val); @@ -806,4 +813,17 @@ dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no) return NULL; } #endif + +#ifdef CONFIG_PCIE_DW_DEBUGFS +void dwc_pcie_debugfs_init(struct dw_pcie *pci); +void dwc_pcie_debugfs_deinit(struct dw_pcie *pci); +#else +static inline void dwc_pcie_debugfs_init(struct dw_pcie *pci) +{ +} +static inline void dwc_pcie_debugfs_deinit(struct dw_pcie *pci) +{ +} +#endif + #endif /* _PCIE_DESIGNWARE_H */ diff --git a/include/linux/pcie-dwc.h b/include/linux/pcie-dwc.h index 6e4e1307ad6e..007b3f1b7b17 100644 --- a/include/linux/pcie-dwc.h +++ b/include/linux/pcie-dwc.h @@ -28,6 +28,8 @@ static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = { .vsec_id = 0x02, .vsec_rev = 0x4 }, { .vendor_id = PCI_VENDOR_ID_QCOM, .vsec_id = 0x02, .vsec_rev = 0x4 }, + { .vendor_id = PCI_VENDOR_ID_SAMSUNG, + .vsec_id = 0x02, .vsec_rev = 0x4 }, {} }; -- cgit v1.2.3-59-g8ed1b From d20ee8e2dbd6c41a1e48ac38c352689b1d3cbbe4 Mon Sep 17 00:00:00 2001 From: Shradha Todi Date: Fri, 21 Feb 2025 18:45:47 +0530 Subject: PCI: dwc: Add debugfs based Error Injection support for DWC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support to provide Error Injection interface to userspace. This set of debug registers are part of the RAS DES feature present in DesignWare PCIe controllers. Signed-off-by: Shradha Todi Reviewed-by: Manivannan Sadhasivam Reviewed-by: Fan Ni Tested-by: Hrishikesh Deleep Link: https://lore.kernel.org/r/20250221131548.59616-5-shradha.t@samsung.com [kwilczynski: commit log, tidy up code comments, update documentation, change debugfs property name from "duplicate_dllp" to "duplicate_tlp"] Signed-off-by: Krzysztof Wilczyński --- Documentation/ABI/testing/debugfs-dwc-pcie | 74 +++++++++ .../pci/controller/dwc/pcie-designware-debugfs.c | 168 ++++++++++++++++++++- 2 files changed, 240 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/debugfs-dwc-pcie b/Documentation/ABI/testing/debugfs-dwc-pcie index 5b87471dfee3..9a6782f25670 100644 --- a/Documentation/ABI/testing/debugfs-dwc-pcie +++ b/Documentation/ABI/testing/debugfs-dwc-pcie @@ -11,3 +11,77 @@ Contact: Shradha Todi Description: (RW) Write the lane number to be checked as valid or invalid. Read will return the status of PIPE RXVALID signal of the selected lane. The default selected lane is Lane0. + +What: /sys/kernel/debug/dwc_pcie_/rasdes_err_inj/ +Date: February 2025 +Contact: Shradha Todi +Description: The "rasdes_err_inj" is a directory which can be used to inject + errors into the system. The possible errors that can be injected + are: + + 1) tx_lcrc - TLP LCRC error injection TX Path + 2) b16_crc_dllp - 16b CRC error injection of ACK/NAK DLLP + 3) b16_crc_upd_fc - 16b CRC error injection of Update-FC DLLP + 4) tx_ecrc - TLP ECRC error injection TX Path + 5) fcrc_tlp - TLP's FCRC error injection TX Path + 6) parity_tsos - Parity error of TSOS + 7) parity_skpos - Parity error on SKPOS + 8) rx_lcrc - LCRC error injection RX Path + 9) rx_ecrc - ECRC error injection RX Path + 10) tlp_err_seq - TLPs SEQ# error + 11) ack_nak_dllp_seq - DLLPS ACK/NAK SEQ# error + 12) ack_nak_dllp - ACK/NAK DLLPs transmission block + 13) upd_fc_dllp - UpdateFC DLLPs transmission block + 14) nak_dllp - Always transmission for NAK DLLP + 15) inv_sync_hdr_sym - Invert SYNC header + 16) com_pad_ts1 - COM/PAD TS1 order set + 17) com_pad_ts2 - COM/PAD TS2 order set + 18) com_fts - COM/FTS FTS order set + 19) com_idl - COM/IDL E-idle order set + 20) end_edb - END/EDB symbol + 21) stp_sdp - STP/SDP symbol + 22) com_skp - COM/SKP SKP order set + 23) posted_tlp_hdr - Posted TLP Header credit value control + 24) non_post_tlp_hdr - Non-Posted TLP Header credit value control + 25) cmpl_tlp_hdr - Completion TLP Header credit value control + 26) posted_tlp_data - Posted TLP Data credit value control + 27) non_post_tlp_data - Non-Posted TLP Data credit value control + 28) cmpl_tlp_data - Completion TLP Data credit value control + 29) duplicate_tlp - Generates duplicate TLPs + 30) nullified_tlp - Generates Nullified TLPs + + (WO) Write to the attribute will prepare controller to inject + the respective error in the next transmission of data. + + Parameter required to write will change in the following ways: + + - Errors 9 and 10 are sequence errors. The write command: + + echo > /sys/kernel/debug/dwc_pcie_/rasdes_err_inj/ + + + Number of errors to be injected + + The difference to add or subtract from natural + sequence number to generate sequence error. + Allowed range from -4095 to 4095 + + - Errors 23 to 28 are credit value error insertions. The write + command: + + echo > /sys/kernel/debug/dwc_pcie_/rasdes_err_inj/ + + + Number of errors to be injected + + The difference to add or subtract from UpdateFC + credit value. Allowed range from -4095 to 4095 + + Target VC number + + - All other errors. The write command: + + echo > /sys/kernel/debug/dwc_pcie_/rasdes_err_inj/ + + + Number of errors to be injected diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index c86befa2fdb0..dc35db1cd49b 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -17,6 +17,20 @@ #define PIPE_DETECT_LANE BIT(17) #define LANE_SELECT GENMASK(3, 0) +#define ERR_INJ0_OFF 0x34 +#define EINJ_VAL_DIFF GENMASK(28, 16) +#define EINJ_VC_NUM GENMASK(14, 12) +#define EINJ_TYPE_SHIFT 8 +#define EINJ0_TYPE GENMASK(11, 8) +#define EINJ1_TYPE BIT(8) +#define EINJ2_TYPE GENMASK(9, 8) +#define EINJ3_TYPE GENMASK(10, 8) +#define EINJ4_TYPE GENMASK(10, 8) +#define EINJ5_TYPE BIT(8) +#define EINJ_COUNT GENMASK(7, 0) + +#define ERR_INJ_ENABLE_REG 0x30 + #define DWC_DEBUGFS_BUF_MAX 128 /** @@ -33,6 +47,74 @@ struct dwc_pcie_rasdes_info { struct mutex reg_event_lock; }; +/** + * struct dwc_pcie_rasdes_priv - Stores file specific private data information + * @pci: Reference to the dw_pcie structure + * @idx: Index of specific file related information in array of structs + * + * All debugfs files will have this struct as its private data. + */ +struct dwc_pcie_rasdes_priv { + struct dw_pcie *pci; + int idx; +}; + +/** + * struct dwc_pcie_err_inj - Store details about each error injection + * supported by DWC RAS DES + * @name: Name of the error that can be injected + * @err_inj_group: Group number to which the error belongs. The value + * can range from 0 to 5 + * @err_inj_type: Each group can have multiple types of error + */ +struct dwc_pcie_err_inj { + const char *name; + u32 err_inj_group; + u32 err_inj_type; +}; + +static const struct dwc_pcie_err_inj err_inj_list[] = { + {"tx_lcrc", 0x0, 0x0}, + {"b16_crc_dllp", 0x0, 0x1}, + {"b16_crc_upd_fc", 0x0, 0x2}, + {"tx_ecrc", 0x0, 0x3}, + {"fcrc_tlp", 0x0, 0x4}, + {"parity_tsos", 0x0, 0x5}, + {"parity_skpos", 0x0, 0x6}, + {"rx_lcrc", 0x0, 0x8}, + {"rx_ecrc", 0x0, 0xb}, + {"tlp_err_seq", 0x1, 0x0}, + {"ack_nak_dllp_seq", 0x1, 0x1}, + {"ack_nak_dllp", 0x2, 0x0}, + {"upd_fc_dllp", 0x2, 0x1}, + {"nak_dllp", 0x2, 0x2}, + {"inv_sync_hdr_sym", 0x3, 0x0}, + {"com_pad_ts1", 0x3, 0x1}, + {"com_pad_ts2", 0x3, 0x2}, + {"com_fts", 0x3, 0x3}, + {"com_idl", 0x3, 0x4}, + {"end_edb", 0x3, 0x5}, + {"stp_sdp", 0x3, 0x6}, + {"com_skp", 0x3, 0x7}, + {"posted_tlp_hdr", 0x4, 0x0}, + {"non_post_tlp_hdr", 0x4, 0x1}, + {"cmpl_tlp_hdr", 0x4, 0x2}, + {"posted_tlp_data", 0x4, 0x4}, + {"non_post_tlp_data", 0x4, 0x5}, + {"cmpl_tlp_data", 0x4, 0x6}, + {"duplicate_tlp", 0x5, 0x0}, + {"nullified_tlp", 0x5, 0x1}, +}; + +static const u32 err_inj_type_mask[] = { + EINJ0_TYPE, + EINJ1_TYPE, + EINJ2_TYPE, + EINJ3_TYPE, + EINJ4_TYPE, + EINJ5_TYPE, +}; + static ssize_t lane_detect_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -96,6 +178,64 @@ static ssize_t rx_valid_write(struct file *file, const char __user *buf, return lane_detect_write(file, buf, count, ppos); } +static ssize_t err_inj_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct dwc_pcie_rasdes_priv *pdata = file->private_data; + struct dw_pcie *pci = pdata->pci; + struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + u32 val, counter, vc_num, err_group, type_mask; + int val_diff = 0; + char *kern_buf; + + err_group = err_inj_list[pdata->idx].err_inj_group; + type_mask = err_inj_type_mask[err_group]; + + kern_buf = memdup_user_nul(buf, count); + if (IS_ERR(kern_buf)) + return PTR_ERR(kern_buf); + + if (err_group == 4) { + val = sscanf(kern_buf, "%u %d %u", &counter, &val_diff, &vc_num); + if ((val != 3) || (val_diff < -4095 || val_diff > 4095)) { + kfree(kern_buf); + return -EINVAL; + } + } else if (err_group == 1) { + val = sscanf(kern_buf, "%u %d", &counter, &val_diff); + if ((val != 2) || (val_diff < -4095 || val_diff > 4095)) { + kfree(kern_buf); + return -EINVAL; + } + } else { + val = kstrtou32(kern_buf, 0, &counter); + if (val) { + kfree(kern_buf); + return val; + } + } + + val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + ERR_INJ0_OFF + (0x4 * err_group)); + val &= ~(type_mask | EINJ_COUNT); + val |= ((err_inj_list[pdata->idx].err_inj_type << EINJ_TYPE_SHIFT) & type_mask); + val |= FIELD_PREP(EINJ_COUNT, counter); + + if (err_group == 1 || err_group == 4) { + val &= ~(EINJ_VAL_DIFF); + val |= FIELD_PREP(EINJ_VAL_DIFF, val_diff); + } + if (err_group == 4) { + val &= ~(EINJ_VC_NUM); + val |= FIELD_PREP(EINJ_VC_NUM, vc_num); + } + + dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + ERR_INJ0_OFF + (0x4 * err_group), val); + dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + ERR_INJ_ENABLE_REG, (0x1 << err_group)); + + kfree(kern_buf); + return count; +} + #define dwc_debugfs_create(name) \ debugfs_create_file(#name, 0644, rasdes_debug, pci, \ &dbg_ ## name ## _fops) @@ -110,6 +250,11 @@ static const struct file_operations dbg_ ## name ## _fops = { \ DWC_DEBUGFS_FOPS(lane_detect); DWC_DEBUGFS_FOPS(rx_valid); +static const struct file_operations dwc_pcie_err_inj_ops = { + .open = simple_open, + .write = err_inj_write, +}; + static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci) { struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; @@ -119,10 +264,11 @@ static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci) static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir) { - struct dentry *rasdes_debug; + struct dentry *rasdes_debug, *rasdes_err_inj; struct dwc_pcie_rasdes_info *rasdes_info; + struct dwc_pcie_rasdes_priv *priv_tmp; struct device *dev = pci->dev; - int ras_cap; + int ras_cap, i, ret; /* * If a given SoC has no RAS DES capability, the following call is @@ -141,6 +287,7 @@ static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir) /* Create subdirectories for Debug, Error Injection, Statistics. */ rasdes_debug = debugfs_create_dir("rasdes_debug", dir); + rasdes_err_inj = debugfs_create_dir("rasdes_err_inj", dir); mutex_init(&rasdes_info->reg_event_lock); rasdes_info->ras_cap_offset = ras_cap; @@ -150,7 +297,24 @@ static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir) dwc_debugfs_create(lane_detect); dwc_debugfs_create(rx_valid); + /* Create debugfs files for Error Injection subdirectory. */ + for (i = 0; i < ARRAY_SIZE(err_inj_list); i++) { + priv_tmp = devm_kzalloc(dev, sizeof(*priv_tmp), GFP_KERNEL); + if (!priv_tmp) { + ret = -ENOMEM; + goto err_deinit; + } + + priv_tmp->idx = i; + priv_tmp->pci = pci; + debugfs_create_file(err_inj_list[i].name, 0200, rasdes_err_inj, priv_tmp, + &dwc_pcie_err_inj_ops); + } return 0; + +err_deinit: + dwc_pcie_rasdes_debugfs_deinit(pci); + return ret; } void dwc_pcie_debugfs_deinit(struct dw_pcie *pci) -- cgit v1.2.3-59-g8ed1b From 27491ac2ccd7e111a9c058e7654d372cbfcdad4c Mon Sep 17 00:00:00 2001 From: Shradha Todi Date: Fri, 21 Feb 2025 18:45:48 +0530 Subject: PCI: dwc: Add debugfs based Statistical Counter support for DWC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support to provide Statistical Counter interface to userspace. This set of debug registers are part of the RAS DES feature present in DesignWare PCIe controllers. Co-developed-by: Manivannan Sadhasivam Signed-off-by: Shradha Todi Reviewed-by: Manivannan Sadhasivam Tested-by: Hrishikesh Deleep Link: https://lore.kernel.org/r/20250221131548.59616-6-shradha.t@samsung.com [kwilczynski: commit log, tidy up code comments, update documentation, squashed patch that checks if the event counter is supported from https://lore.kernel.org/linux-pci/20250225171239.19574-3-manivannan.sadhasivam@linaro.org] Signed-off-by: Krzysztof Wilczyński --- Documentation/ABI/testing/debugfs-dwc-pcie | 64 ++++++ .../pci/controller/dwc/pcie-designware-debugfs.c | 248 +++++++++++++++++++++ 2 files changed, 312 insertions(+) (limited to 'drivers') diff --git a/Documentation/ABI/testing/debugfs-dwc-pcie b/Documentation/ABI/testing/debugfs-dwc-pcie index 9a6782f25670..04cefe081512 100644 --- a/Documentation/ABI/testing/debugfs-dwc-pcie +++ b/Documentation/ABI/testing/debugfs-dwc-pcie @@ -85,3 +85,67 @@ Description: The "rasdes_err_inj" is a directory which can be used to inject Number of errors to be injected + +What: /sys/kernel/debug/dwc_pcie_/rasdes_event_counters//counter_enable +Date: February 2025 +Contact: Shradha Todi +Description: The "rasdes_event_counters" is the directory which can be used + to collect statistical data about the number of times a certain + event has occurred in the controller. The list of possible + events are: + + 1) EBUF Overflow + 2) EBUF Underrun + 3) Decode Error + 4) Running Disparity Error + 5) SKP OS Parity Error + 6) SYNC Header Error + 7) Rx Valid De-assertion + 8) CTL SKP OS Parity Error + 9) 1st Retimer Parity Error + 10) 2nd Retimer Parity Error + 11) Margin CRC and Parity Error + 12) Detect EI Infer + 13) Receiver Error + 14) RX Recovery Req + 15) N_FTS Timeout + 16) Framing Error + 17) Deskew Error + 18) Framing Error In L0 + 19) Deskew Uncompleted Error + 20) Bad TLP + 21) LCRC Error + 22) Bad DLLP + 23) Replay Number Rollover + 24) Replay Timeout + 25) Rx Nak DLLP + 26) Tx Nak DLLP + 27) Retry TLP + 28) FC Timeout + 29) Poisoned TLP + 30) ECRC Error + 31) Unsupported Request + 32) Completer Abort + 33) Completion Timeout + 34) EBUF SKP Add + 35) EBUF SKP Del + + (RW) Write 1 to enable the event counter and write 0 to disable + the event counter. Read will return whether the counter is + currently enabled or disabled. Counter is disabled by default. + +What: /sys/kernel/debug/dwc_pcie_/rasdes_event_counters//counter_value +Date: February 2025 +Contact: Shradha Todi +Description: (RO) Read will return the current value of the event counter. + To reset the counter, counter should be disabled first and then + enabled back using the "counter_enable" attribute. + +What: /sys/kernel/debug/dwc_pcie_/rasdes_event_counters//lane_select +Date: February 2025 +Contact: Shradha Todi +Description: (RW) Some lanes in the event list are lane specific events. + These include events from 1 to 11, as well as, 34 and 35. Write + the lane number for which you wish the counter to be enabled, + disabled, or value dumped. Read will return the current + selected lane number. Lane0 is selected by default. diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index dc35db1cd49b..6100a0d1c709 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -31,6 +31,17 @@ #define ERR_INJ_ENABLE_REG 0x30 +#define RAS_DES_EVENT_COUNTER_DATA_REG 0xc + +#define RAS_DES_EVENT_COUNTER_CTRL_REG 0x8 +#define EVENT_COUNTER_GROUP_SELECT GENMASK(27, 24) +#define EVENT_COUNTER_EVENT_SELECT GENMASK(23, 16) +#define EVENT_COUNTER_LANE_SELECT GENMASK(11, 8) +#define EVENT_COUNTER_STATUS BIT(7) +#define EVENT_COUNTER_ENABLE GENMASK(4, 2) +#define PER_EVENT_ON 0x3 +#define PER_EVENT_OFF 0x1 + #define DWC_DEBUGFS_BUF_MAX 128 /** @@ -115,6 +126,63 @@ static const u32 err_inj_type_mask[] = { EINJ5_TYPE, }; +/** + * struct dwc_pcie_event_counter - Store details about each event counter + * supported in DWC RAS DES + * @name: Name of the error counter + * @group_no: Group number that the event belongs to. The value can range + * from 0 to 4 + * @event_no: Event number of the particular event. The value ranges are: + * Group 0: 0 - 10 + * Group 1: 5 - 13 + * Group 2: 0 - 7 + * Group 3: 0 - 5 + * Group 4: 0 - 1 + */ +struct dwc_pcie_event_counter { + const char *name; + u32 group_no; + u32 event_no; +}; + +static const struct dwc_pcie_event_counter event_list[] = { + {"ebuf_overflow", 0x0, 0x0}, + {"ebuf_underrun", 0x0, 0x1}, + {"decode_err", 0x0, 0x2}, + {"running_disparity_err", 0x0, 0x3}, + {"skp_os_parity_err", 0x0, 0x4}, + {"sync_header_err", 0x0, 0x5}, + {"rx_valid_deassertion", 0x0, 0x6}, + {"ctl_skp_os_parity_err", 0x0, 0x7}, + {"retimer_parity_err_1st", 0x0, 0x8}, + {"retimer_parity_err_2nd", 0x0, 0x9}, + {"margin_crc_parity_err", 0x0, 0xA}, + {"detect_ei_infer", 0x1, 0x5}, + {"receiver_err", 0x1, 0x6}, + {"rx_recovery_req", 0x1, 0x7}, + {"n_fts_timeout", 0x1, 0x8}, + {"framing_err", 0x1, 0x9}, + {"deskew_err", 0x1, 0xa}, + {"framing_err_in_l0", 0x1, 0xc}, + {"deskew_uncompleted_err", 0x1, 0xd}, + {"bad_tlp", 0x2, 0x0}, + {"lcrc_err", 0x2, 0x1}, + {"bad_dllp", 0x2, 0x2}, + {"replay_num_rollover", 0x2, 0x3}, + {"replay_timeout", 0x2, 0x4}, + {"rx_nak_dllp", 0x2, 0x5}, + {"tx_nak_dllp", 0x2, 0x6}, + {"retry_tlp", 0x2, 0x7}, + {"fc_timeout", 0x3, 0x0}, + {"poisoned_tlp", 0x3, 0x1}, + {"ecrc_error", 0x3, 0x2}, + {"unsupported_request", 0x3, 0x3}, + {"completer_abort", 0x3, 0x4}, + {"completion_timeout", 0x3, 0x5}, + {"ebuf_skp_add", 0x4, 0x0}, + {"ebuf_skp_del", 0x4, 0x1}, +}; + static ssize_t lane_detect_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -236,6 +304,145 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf, return count; } +static void set_event_number(struct dwc_pcie_rasdes_priv *pdata, + struct dw_pcie *pci, struct dwc_pcie_rasdes_info *rinfo) +{ + u32 val; + + val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG); + val &= ~EVENT_COUNTER_ENABLE; + val &= ~(EVENT_COUNTER_GROUP_SELECT | EVENT_COUNTER_EVENT_SELECT); + val |= FIELD_PREP(EVENT_COUNTER_GROUP_SELECT, event_list[pdata->idx].group_no); + val |= FIELD_PREP(EVENT_COUNTER_EVENT_SELECT, event_list[pdata->idx].event_no); + dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG, val); +} + +static ssize_t counter_enable_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct dwc_pcie_rasdes_priv *pdata = file->private_data; + struct dw_pcie *pci = pdata->pci; + struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + char debugfs_buf[DWC_DEBUGFS_BUF_MAX]; + ssize_t pos; + u32 val; + + mutex_lock(&rinfo->reg_event_lock); + set_event_number(pdata, pci, rinfo); + val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG); + mutex_unlock(&rinfo->reg_event_lock); + val = FIELD_GET(EVENT_COUNTER_STATUS, val); + if (val) + pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "Counter Enabled\n"); + else + pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "Counter Disabled\n"); + + return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos); +} + +static ssize_t counter_enable_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct dwc_pcie_rasdes_priv *pdata = file->private_data; + struct dw_pcie *pci = pdata->pci; + struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + u32 val, enable; + + val = kstrtou32_from_user(buf, count, 0, &enable); + if (val) + return val; + + mutex_lock(&rinfo->reg_event_lock); + set_event_number(pdata, pci, rinfo); + val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG); + if (enable) + val |= FIELD_PREP(EVENT_COUNTER_ENABLE, PER_EVENT_ON); + else + val |= FIELD_PREP(EVENT_COUNTER_ENABLE, PER_EVENT_OFF); + + dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG, val); + + /* + * While enabling the counter, always read the status back to check if + * it is enabled or not. Return error if it is not enabled to let the + * users know that the counter is not supported on the platform. + */ + if (enable) { + val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + + RAS_DES_EVENT_COUNTER_CTRL_REG); + if (!FIELD_GET(EVENT_COUNTER_STATUS, val)) { + mutex_unlock(&rinfo->reg_event_lock); + return -EOPNOTSUPP; + } + } + + mutex_unlock(&rinfo->reg_event_lock); + + return count; +} + +static ssize_t counter_lane_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct dwc_pcie_rasdes_priv *pdata = file->private_data; + struct dw_pcie *pci = pdata->pci; + struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + char debugfs_buf[DWC_DEBUGFS_BUF_MAX]; + ssize_t pos; + u32 val; + + mutex_lock(&rinfo->reg_event_lock); + set_event_number(pdata, pci, rinfo); + val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG); + mutex_unlock(&rinfo->reg_event_lock); + val = FIELD_GET(EVENT_COUNTER_LANE_SELECT, val); + pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "Lane: %d\n", val); + + return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos); +} + +static ssize_t counter_lane_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct dwc_pcie_rasdes_priv *pdata = file->private_data; + struct dw_pcie *pci = pdata->pci; + struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + u32 val, lane; + + val = kstrtou32_from_user(buf, count, 0, &lane); + if (val) + return val; + + mutex_lock(&rinfo->reg_event_lock); + set_event_number(pdata, pci, rinfo); + val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG); + val &= ~(EVENT_COUNTER_LANE_SELECT); + val |= FIELD_PREP(EVENT_COUNTER_LANE_SELECT, lane); + dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG, val); + mutex_unlock(&rinfo->reg_event_lock); + + return count; +} + +static ssize_t counter_value_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct dwc_pcie_rasdes_priv *pdata = file->private_data; + struct dw_pcie *pci = pdata->pci; + struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + char debugfs_buf[DWC_DEBUGFS_BUF_MAX]; + ssize_t pos; + u32 val; + + mutex_lock(&rinfo->reg_event_lock); + set_event_number(pdata, pci, rinfo); + val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_DATA_REG); + mutex_unlock(&rinfo->reg_event_lock); + pos = scnprintf(debugfs_buf, DWC_DEBUGFS_BUF_MAX, "Counter value: %d\n", val); + + return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos); +} + #define dwc_debugfs_create(name) \ debugfs_create_file(#name, 0644, rasdes_debug, pci, \ &dbg_ ## name ## _fops) @@ -255,6 +462,23 @@ static const struct file_operations dwc_pcie_err_inj_ops = { .write = err_inj_write, }; +static const struct file_operations dwc_pcie_counter_enable_ops = { + .open = simple_open, + .read = counter_enable_read, + .write = counter_enable_write, +}; + +static const struct file_operations dwc_pcie_counter_lane_ops = { + .open = simple_open, + .read = counter_lane_read, + .write = counter_lane_write, +}; + +static const struct file_operations dwc_pcie_counter_value_ops = { + .open = simple_open, + .read = counter_value_read, +}; + static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci) { struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; @@ -265,6 +489,7 @@ static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci) static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir) { struct dentry *rasdes_debug, *rasdes_err_inj; + struct dentry *rasdes_event_counter, *rasdes_events; struct dwc_pcie_rasdes_info *rasdes_info; struct dwc_pcie_rasdes_priv *priv_tmp; struct device *dev = pci->dev; @@ -288,6 +513,7 @@ static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir) /* Create subdirectories for Debug, Error Injection, Statistics. */ rasdes_debug = debugfs_create_dir("rasdes_debug", dir); rasdes_err_inj = debugfs_create_dir("rasdes_err_inj", dir); + rasdes_event_counter = debugfs_create_dir("rasdes_event_counter", dir); mutex_init(&rasdes_info->reg_event_lock); rasdes_info->ras_cap_offset = ras_cap; @@ -310,6 +536,28 @@ static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir) debugfs_create_file(err_inj_list[i].name, 0200, rasdes_err_inj, priv_tmp, &dwc_pcie_err_inj_ops); } + + /* Create debugfs files for Statistical Counter subdirectory. */ + for (i = 0; i < ARRAY_SIZE(event_list); i++) { + priv_tmp = devm_kzalloc(dev, sizeof(*priv_tmp), GFP_KERNEL); + if (!priv_tmp) { + ret = -ENOMEM; + goto err_deinit; + } + + priv_tmp->idx = i; + priv_tmp->pci = pci; + rasdes_events = debugfs_create_dir(event_list[i].name, rasdes_event_counter); + if (event_list[i].group_no == 0 || event_list[i].group_no == 4) { + debugfs_create_file("lane_select", 0644, rasdes_events, + priv_tmp, &dwc_pcie_counter_lane_ops); + } + debugfs_create_file("counter_value", 0444, rasdes_events, priv_tmp, + &dwc_pcie_counter_value_ops); + debugfs_create_file("counter_enable", 0644, rasdes_events, priv_tmp, + &dwc_pcie_counter_enable_ops); + } + return 0; err_deinit: -- cgit v1.2.3-59-g8ed1b From f0f3044d2246495e01871212fc7ec5e206555338 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sun, 23 Feb 2025 22:18:48 +0800 Subject: PCI: dwc: Add debugfs property to provide LTSSM status of the PCIe link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the debugfs property to provide a view of the current link's LTSSM status from the Root Port device. Signed-off-by: Hans Zhang <18255117159@163.com> Reviewed-by: Manivannan Sadhasivam Tested-by: Niklas Cassel Tested-by: Hrishikesh Deleep Link: https://lore.kernel.org/r/20250223141848.231232-1-18255117159@163.com [kwilczynski: commit log, refactor dw_ltssm_sts_string() to avoid compilation errors on platforms that do not set CONFIG_PCIE_DW_HOST] Signed-off-by: Krzysztof Wilczyński --- Documentation/ABI/testing/debugfs-dwc-pcie | 6 ++ .../pci/controller/dwc/pcie-designware-debugfs.c | 79 ++++++++++++++++++++++ drivers/pci/controller/dwc/pcie-designware.h | 31 +++++++++ 3 files changed, 116 insertions(+) (limited to 'drivers') diff --git a/Documentation/ABI/testing/debugfs-dwc-pcie b/Documentation/ABI/testing/debugfs-dwc-pcie index 04cefe081512..92b76f52a408 100644 --- a/Documentation/ABI/testing/debugfs-dwc-pcie +++ b/Documentation/ABI/testing/debugfs-dwc-pcie @@ -149,3 +149,9 @@ Description: (RW) Some lanes in the event list are lane specific events. the lane number for which you wish the counter to be enabled, disabled, or value dumped. Read will return the current selected lane number. Lane0 is selected by default. + +What: /sys/kernel/debug/dwc_pcie_/ltssm_status +Date: February 2025 +Contact: Hans Zhang <18255117159@163.com> +Description: (RO) Read will return the current PCIe LTSSM state in both + string and raw value. diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index 6100a0d1c709..9e6f4d00f262 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -443,6 +443,72 @@ static ssize_t counter_value_read(struct file *file, char __user *buf, return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos); } +static const char *ltssm_status_string(enum dw_pcie_ltssm ltssm) +{ + const char *str; + + switch (ltssm) { +#define DW_PCIE_LTSSM_NAME(n) case n: str = #n; break + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_QUIET); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_ACT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_ACTIVE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_COMPLIANCE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_CONFIG); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_PRE_DETECT_QUIET); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_WAIT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_START); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_ACEPT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_WAI); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_ACEPT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_COMPLETE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_LOCK); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_SPEED); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_RCVRCFG); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0S); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L123_SEND_EIDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_WAKE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_ENTRY); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ENTRY); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ACTIVE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT_TIMEOUT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET_ENTRY); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ0); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ1); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ2); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ3); + default: + str = "DW_PCIE_LTSSM_UNKNOWN"; + break; + } + + return str + strlen("DW_PCIE_LTSSM_"); +} + +static int ltssm_status_show(struct seq_file *s, void *v) +{ + struct dw_pcie *pci = s->private; + enum dw_pcie_ltssm val; + + val = dw_pcie_get_ltssm(pci); + seq_printf(s, "%s (0x%02x)\n", ltssm_status_string(val), val); + + return 0; +} + +static int ltssm_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, ltssm_status_show, inode->i_private); +} + #define dwc_debugfs_create(name) \ debugfs_create_file(#name, 0644, rasdes_debug, pci, \ &dbg_ ## name ## _fops) @@ -479,6 +545,11 @@ static const struct file_operations dwc_pcie_counter_value_ops = { .read = counter_value_read, }; +static const struct file_operations dwc_pcie_ltssm_status_ops = { + .open = ltssm_status_open, + .read = seq_read, +}; + static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci) { struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; @@ -565,6 +636,12 @@ err_deinit: return ret; } +static void dwc_pcie_ltssm_debugfs_init(struct dw_pcie *pci, struct dentry *dir) +{ + debugfs_create_file("ltssm_status", 0444, dir, pci, + &dwc_pcie_ltssm_status_ops); +} + void dwc_pcie_debugfs_deinit(struct dw_pcie *pci) { if (!pci->debugfs) @@ -595,4 +672,6 @@ void dwc_pcie_debugfs_init(struct dw_pcie *pci) if (err) dev_err(dev, "failed to initialize RAS DES debugfs, err=%d\n", err); + + dwc_pcie_ltssm_debugfs_init(pci, dir); } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index dd129718fb41..a03b3799fb27 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -330,9 +330,40 @@ enum dw_pcie_ltssm { /* Need to align with PCIE_PORT_DEBUG0 bits 0:5 */ DW_PCIE_LTSSM_DETECT_QUIET = 0x0, DW_PCIE_LTSSM_DETECT_ACT = 0x1, + DW_PCIE_LTSSM_POLL_ACTIVE = 0x2, + DW_PCIE_LTSSM_POLL_COMPLIANCE = 0x3, + DW_PCIE_LTSSM_POLL_CONFIG = 0x4, + DW_PCIE_LTSSM_PRE_DETECT_QUIET = 0x5, DW_PCIE_LTSSM_DETECT_WAIT = 0x6, + DW_PCIE_LTSSM_CFG_LINKWD_START = 0x7, + DW_PCIE_LTSSM_CFG_LINKWD_ACEPT = 0x8, + DW_PCIE_LTSSM_CFG_LANENUM_WAI = 0x9, + DW_PCIE_LTSSM_CFG_LANENUM_ACEPT = 0xa, + DW_PCIE_LTSSM_CFG_COMPLETE = 0xb, + DW_PCIE_LTSSM_CFG_IDLE = 0xc, + DW_PCIE_LTSSM_RCVRY_LOCK = 0xd, + DW_PCIE_LTSSM_RCVRY_SPEED = 0xe, + DW_PCIE_LTSSM_RCVRY_RCVRCFG = 0xf, + DW_PCIE_LTSSM_RCVRY_IDLE = 0x10, DW_PCIE_LTSSM_L0 = 0x11, + DW_PCIE_LTSSM_L0S = 0x12, + DW_PCIE_LTSSM_L123_SEND_EIDLE = 0x13, + DW_PCIE_LTSSM_L1_IDLE = 0x14, DW_PCIE_LTSSM_L2_IDLE = 0x15, + DW_PCIE_LTSSM_L2_WAKE = 0x16, + DW_PCIE_LTSSM_DISABLED_ENTRY = 0x17, + DW_PCIE_LTSSM_DISABLED_IDLE = 0x18, + DW_PCIE_LTSSM_DISABLED = 0x19, + DW_PCIE_LTSSM_LPBK_ENTRY = 0x1a, + DW_PCIE_LTSSM_LPBK_ACTIVE = 0x1b, + DW_PCIE_LTSSM_LPBK_EXIT = 0x1c, + DW_PCIE_LTSSM_LPBK_EXIT_TIMEOUT = 0x1d, + DW_PCIE_LTSSM_HOT_RESET_ENTRY = 0x1e, + DW_PCIE_LTSSM_HOT_RESET = 0x1f, + DW_PCIE_LTSSM_RCVRY_EQ0 = 0x20, + DW_PCIE_LTSSM_RCVRY_EQ1 = 0x21, + DW_PCIE_LTSSM_RCVRY_EQ2 = 0x22, + DW_PCIE_LTSSM_RCVRY_EQ3 = 0x23, DW_PCIE_LTSSM_UNKNOWN = 0xFFFFFFFF, }; -- cgit v1.2.3-59-g8ed1b From 20bbb083bbc9d3f8db390f2e35e168f1b23dae8a Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 18 Feb 2025 10:21:21 +0100 Subject: PCI: Add Rockchip Vendor ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move PCI_VENDOR_ID_ROCKCHIP from pci_endpoint_test.c to pci_ids.h and reuse it in pcie-rockchip-host.c. Link: https://lore.kernel.org/r/20250218092120.2322784-2-cassel@kernel.org Signed-off-by: Shawn Lin Signed-off-by: Niklas Cassel Signed-off-by: Bjorn Helgaas Signed-off-by: Krzysztof Wilczyński --- drivers/misc/pci_endpoint_test.c | 1 - drivers/pci/controller/pcie-rockchip-host.c | 2 +- drivers/pci/controller/pcie-rockchip.h | 1 - include/linux/pci_ids.h | 2 ++ 4 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index d5ac71a49386..b002740acf8d 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -88,7 +88,6 @@ #define PCI_DEVICE_ID_RENESAS_R8A774E1 0x0025 #define PCI_DEVICE_ID_RENESAS_R8A779F0 0x0031 -#define PCI_VENDOR_ID_ROCKCHIP 0x1d87 #define PCI_DEVICE_ID_ROCKCHIP_RK3588 0x3588 static DEFINE_IDA(pci_endpoint_test_ida); diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index 5adac6adc046..6a46be17aa91 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -367,7 +367,7 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip) } } - rockchip_pcie_write(rockchip, ROCKCHIP_VENDOR_ID, + rockchip_pcie_write(rockchip, PCI_VENDOR_ID_ROCKCHIP, PCIE_CORE_CONFIG_VENDOR); rockchip_pcie_write(rockchip, PCI_CLASS_BRIDGE_PCI_NORMAL << 8, diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h index 11def598534b..14954f43e5e9 100644 --- a/drivers/pci/controller/pcie-rockchip.h +++ b/drivers/pci/controller/pcie-rockchip.h @@ -200,7 +200,6 @@ #define AXI_WRAPPER_NOR_MSG 0xc #define PCIE_RC_SEND_PME_OFF 0x11960 -#define ROCKCHIP_VENDOR_ID 0x1d87 #define PCIE_LINK_IS_L2(x) \ (((x) & PCIE_CLIENT_DEBUG_LTSSM_MASK) == PCIE_CLIENT_DEBUG_LTSSM_L2) #define PCIE_LINK_TRAINING_DONE(x) \ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index de5deb1a0118..e1a270e7e0c5 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2609,6 +2609,8 @@ #define PCI_VENDOR_ID_ZHAOXIN 0x1d17 +#define PCI_VENDOR_ID_ROCKCHIP 0x1d87 + #define PCI_VENDOR_ID_HYGON 0x1d94 #define PCI_VENDOR_ID_META 0x1d9b -- cgit v1.2.3-59-g8ed1b From d5233478548309cab6bb327ddaa3c26038bdd785 Mon Sep 17 00:00:00 2001 From: Zhang Zekun Date: Sat, 31 Aug 2024 12:04:09 +0800 Subject: PCI: kirin: Tidy up _probe() related function with dev_err_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The combination of dev_err() and the returned error code could be replaced by dev_err_probe() in driver's probe function. Thus, convert the code to use dev_err_probe() to make code simpler. Suggested-by: Jonathan Cameron Signed-off-by: Zhang Zekun Reviewed-by: Jonathan Cameron Link: https://lore.kernel.org/r/20240831040413.126417-3-zhangzekun11@huawei.com [kwilczynski: commit log, return -ETIMEDOUT from hi3660_pcie_phy_start() rather than -EINVAL for when the PIPE clock fails to become stable, drop redundant dev->of_node NULL check] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/dwc/pcie-kirin.c | 40 +++++++++++++-------------------- 1 file changed, 15 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c index eeff8bfcdf8b..d0e6a3811b00 100644 --- a/drivers/pci/controller/dwc/pcie-kirin.c +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -216,10 +216,9 @@ static int hi3660_pcie_phy_start(struct hi3660_pcie_phy *phy) usleep_range(PIPE_CLK_WAIT_MIN, PIPE_CLK_WAIT_MAX); reg_val = kirin_apb_phy_readl(phy, PCIE_APB_PHY_STATUS0); - if (reg_val & PIPE_CLK_STABLE) { - dev_err(dev, "PIPE clk is not stable\n"); - return -EINVAL; - } + if (reg_val & PIPE_CLK_STABLE) + return dev_err_probe(dev, -ETIMEDOUT, + "PIPE clk is not stable\n"); return 0; } @@ -371,10 +370,9 @@ static int kirin_pcie_get_gpio_enable(struct kirin_pcie *pcie, if (ret < 0) return 0; - if (ret > MAX_PCI_SLOTS) { - dev_err(dev, "Too many GPIO clock requests!\n"); - return -EINVAL; - } + if (ret > MAX_PCI_SLOTS) + return dev_err_probe(dev, -EINVAL, + "Too many GPIO clock requests!\n"); pcie->n_gpio_clkreq = ret; @@ -420,17 +418,16 @@ static int kirin_pcie_parse_port(struct kirin_pcie *pcie, "unable to get a valid reset gpio\n"); } - if (pcie->num_slots + 1 >= MAX_PCI_SLOTS) { - dev_err(dev, "Too many PCI slots!\n"); - return -EINVAL; - } + if (pcie->num_slots + 1 >= MAX_PCI_SLOTS) + return dev_err_probe(dev, -EINVAL, + "Too many PCI slots!\n"); + pcie->num_slots++; ret = of_pci_get_devfn(child); - if (ret < 0) { - dev_err(dev, "failed to parse devfn: %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to parse devfn\n"); slot = PCI_SLOT(ret); @@ -725,16 +722,9 @@ static int kirin_pcie_probe(struct platform_device *pdev) struct dw_pcie *pci; int ret; - if (!dev->of_node) { - dev_err(dev, "NULL node\n"); - return -EINVAL; - } - data = of_device_get_match_data(dev); - if (!data) { - dev_err(dev, "OF data missing\n"); - return -EINVAL; - } + if (!data) + return dev_err_probe(dev, -EINVAL, "OF data missing\n"); kirin_pcie = devm_kzalloc(dev, sizeof(struct kirin_pcie), GFP_KERNEL); if (!kirin_pcie) -- cgit v1.2.3-59-g8ed1b From a51adf82f87ba8ffe2e90660b2c63a9f6b773e41 Mon Sep 17 00:00:00 2001 From: Zhang Zekun Date: Sat, 31 Aug 2024 12:04:10 +0800 Subject: PCI: mediatek: Use helper function for_each_available_child_of_node_scoped() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The for_each_available_child_of_node_scoped() helper provides a scope-based clean-up functionality to put the device_node automatically, and as such, there is no need to call of_node_put() directly. Thus, use this helper to simplify the code. Signed-off-by: Zhang Zekun Reviewed-by: Jonathan Cameron Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20240831040413.126417-4-zhangzekun11@huawei.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-mediatek.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 3bcfc4e58ba2..811a8b4acd50 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -1041,24 +1041,22 @@ err_free_ck: static int mtk_pcie_setup(struct mtk_pcie *pcie) { struct device *dev = pcie->dev; - struct device_node *node = dev->of_node, *child; + struct device_node *node = dev->of_node; struct mtk_pcie_port *port, *tmp; int err, slot; slot = of_get_pci_domain_nr(dev->of_node); if (slot < 0) { - for_each_available_child_of_node(node, child) { + for_each_available_child_of_node_scoped(node, child) { err = of_pci_get_devfn(child); - if (err < 0) { - dev_err(dev, "failed to get devfn: %d\n", err); - goto error_put_node; - } + if (err < 0) + return dev_err_probe(dev, err, "failed to get devfn\n"); slot = PCI_SLOT(err); err = mtk_pcie_parse_port(pcie, child, slot); if (err) - goto error_put_node; + return err; } } else { err = mtk_pcie_parse_port(pcie, node, slot); @@ -1079,9 +1077,6 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie) mtk_pcie_subsys_powerdown(pcie); return 0; -error_put_node: - of_node_put(child); - return err; } static int mtk_pcie_probe(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From 8905f8b6f55f1ee94e564fbd6955406850e239d7 Mon Sep 17 00:00:00 2001 From: Zhang Zekun Date: Sat, 31 Aug 2024 12:04:11 +0800 Subject: PCI: mt7621: Use helper function for_each_available_child_of_node_scoped() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The for_each_available_child_of_node_scoped() helper provides a scope-based clean-up functionality to put the device_node automatically, and as such, there is no need to call of_node_put() directly. Thus, use this helper to simplify the code. Signed-off-by: Zhang Zekun Reviewed-by: Sergio Paracuellos Reviewed-by: Jonathan Cameron Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20240831040413.126417-5-zhangzekun11@huawei.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-mt7621.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-mt7621.c b/drivers/pci/controller/pcie-mt7621.c index 776caa0b1011..01ead2f92e87 100644 --- a/drivers/pci/controller/pcie-mt7621.c +++ b/drivers/pci/controller/pcie-mt7621.c @@ -258,30 +258,25 @@ static int mt7621_pcie_parse_dt(struct mt7621_pcie *pcie) { struct device *dev = pcie->dev; struct platform_device *pdev = to_platform_device(dev); - struct device_node *node = dev->of_node, *child; + struct device_node *node = dev->of_node; int err; pcie->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pcie->base)) return PTR_ERR(pcie->base); - for_each_available_child_of_node(node, child) { + for_each_available_child_of_node_scoped(node, child) { int slot; err = of_pci_get_devfn(child); - if (err < 0) { - of_node_put(child); - dev_err(dev, "failed to parse devfn: %d\n", err); - return err; - } + if (err < 0) + return dev_err_probe(dev, err, "failed to parse devfn\n"); slot = PCI_SLOT(err); err = mt7621_pcie_parse_port(pcie, child, slot); - if (err) { - of_node_put(child); + if (err) return err; - } } return 0; -- cgit v1.2.3-59-g8ed1b From f60b4e06a945f25d463ae065c6e41c6e24faee0a Mon Sep 17 00:00:00 2001 From: Zhang Zekun Date: Sat, 31 Aug 2024 12:04:12 +0800 Subject: PCI: apple: Use helper function for_each_child_of_node_scoped() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The for_each_available_child_of_node_scoped() helper provides a scope-based clean-up functionality to put the device_node automatically, and as such, there is no need to call of_node_put() directly. Thus, use this helper to simplify the code. Signed-off-by: Zhang Zekun Reviewed-by: Jonathan Cameron Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20240831040413.126417-6-zhangzekun11@huawei.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-apple.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index a7e51bc1c2fe..18e11b9a7f46 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -732,7 +732,6 @@ static int apple_pcie_init(struct pci_config_window *cfg) { struct device *dev = cfg->parent; struct platform_device *platform = to_platform_device(dev); - struct device_node *of_port; struct apple_pcie *pcie; int ret; @@ -755,11 +754,10 @@ static int apple_pcie_init(struct pci_config_window *cfg) if (ret) return ret; - for_each_child_of_node(dev->of_node, of_port) { + for_each_child_of_node_scoped(dev->of_node, of_port) { ret = apple_pcie_setup_port(pcie, of_port); if (ret) { dev_err(pcie->dev, "Port %pOF setup fail: %d\n", of_port, ret); - of_node_put(of_port); return ret; } } -- cgit v1.2.3-59-g8ed1b From bffc72387aefc4545af04200be8affb51c3726cf Mon Sep 17 00:00:00 2001 From: Zhang Zekun Date: Sat, 31 Aug 2024 12:04:13 +0800 Subject: PCI: tegra: Use helper function for_each_child_of_node_scoped() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The for_each_available_child_of_node_scoped() helper provides a scope-based clean-up functionality to put the device_node automatically, and as such, there is no need to call of_node_put() directly. Thus, use this helper to simplify the code. Signed-off-by: Zhang Zekun Reviewed-by: Jonathan Cameron Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20240831040413.126417-7-zhangzekun11@huawei.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pci-tegra.c | 80 +++++++++++++------------------------- 1 file changed, 28 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index b3cdbc5927de..d2f88997283a 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2106,47 +2106,39 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; - struct device_node *np = dev->of_node, *port; + struct device_node *np = dev->of_node; const struct tegra_pcie_soc *soc = pcie->soc; u32 lanes = 0, mask = 0; unsigned int lane = 0; int err; /* parse root ports */ - for_each_child_of_node(np, port) { + for_each_child_of_node_scoped(np, port) { struct tegra_pcie_port *rp; unsigned int index; u32 value; char *label; err = of_pci_get_devfn(port); - if (err < 0) { - dev_err(dev, "failed to parse address: %d\n", err); - goto err_node_put; - } + if (err < 0) + return dev_err_probe(dev, err, "failed to parse address\n"); index = PCI_SLOT(err); - if (index < 1 || index > soc->num_ports) { - dev_err(dev, "invalid port number: %d\n", index); - err = -EINVAL; - goto err_node_put; - } + if (index < 1 || index > soc->num_ports) + return dev_err_probe(dev, -EINVAL, + "invalid port number: %d\n", index); index--; err = of_property_read_u32(port, "nvidia,num-lanes", &value); - if (err < 0) { - dev_err(dev, "failed to parse # of lanes: %d\n", - err); - goto err_node_put; - } + if (err < 0) + return dev_err_probe(dev, err, + "failed to parse # of lanes\n"); - if (value > 16) { - dev_err(dev, "invalid # of lanes: %u\n", value); - err = -EINVAL; - goto err_node_put; - } + if (value > 16) + return dev_err_probe(dev, -EINVAL, + "invalid # of lanes: %u\n", value); lanes |= value << (index << 3); @@ -2159,16 +2151,12 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) lane += value; rp = devm_kzalloc(dev, sizeof(*rp), GFP_KERNEL); - if (!rp) { - err = -ENOMEM; - goto err_node_put; - } + if (!rp) + return -ENOMEM; err = of_address_to_resource(port, 0, &rp->regs); - if (err < 0) { - dev_err(dev, "failed to parse address: %d\n", err); - goto err_node_put; - } + if (err < 0) + return dev_err_probe(dev, err, "failed to parse address\n"); INIT_LIST_HEAD(&rp->list); rp->index = index; @@ -2177,16 +2165,12 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) rp->np = port; rp->base = devm_pci_remap_cfg_resource(dev, &rp->regs); - if (IS_ERR(rp->base)) { - err = PTR_ERR(rp->base); - goto err_node_put; - } + if (IS_ERR(rp->base)) + return PTR_ERR(rp->base); label = devm_kasprintf(dev, GFP_KERNEL, "pex-reset-%u", index); - if (!label) { - err = -ENOMEM; - goto err_node_put; - } + if (!label) + return -ENOMEM; /* * Returns -ENOENT if reset-gpios property is not populated @@ -2199,34 +2183,26 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) GPIOD_OUT_LOW, label); if (IS_ERR(rp->reset_gpio)) { - if (PTR_ERR(rp->reset_gpio) == -ENOENT) { + if (PTR_ERR(rp->reset_gpio) == -ENOENT) rp->reset_gpio = NULL; - } else { - dev_err(dev, "failed to get reset GPIO: %ld\n", - PTR_ERR(rp->reset_gpio)); - err = PTR_ERR(rp->reset_gpio); - goto err_node_put; - } + else + return dev_err_probe(dev, PTR_ERR(rp->reset_gpio), + "failed to get reset GPIO\n"); } list_add_tail(&rp->list, &pcie->ports); } err = tegra_pcie_get_xbar_config(pcie, lanes, &pcie->xbar_config); - if (err < 0) { - dev_err(dev, "invalid lane configuration\n"); - return err; - } + if (err < 0) + return dev_err_probe(dev, err, + "invalid lane configuration\n"); err = tegra_pcie_get_regulators(pcie, mask); if (err < 0) return err; return 0; - -err_node_put: - of_node_put(port); - return err; } /* -- cgit v1.2.3-59-g8ed1b From 60f2ee5f1472972918de7eb14c8240de176f6b8d Mon Sep 17 00:00:00 2001 From: "D M, Sharath Kumar" Date: Fri, 21 Feb 2025 11:04:52 -0600 Subject: PCI: altera: Add Agilex support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add PCIe Root Port controller support for the Agilex family of chips. The Agilex PCIe Hard IP has three variants that are mostly software compatible, except for a couple register offsets. The P-Tile variant supports Gen3/Gen4 1x16. The F-Tile variant supports Gen3/Gen4 4x4, 4x8, and 4x16. The R-Tile variant improves on the F-Tile variant by adding Gen5 support. To simplify the implementation of pci_ops read/write functions, ep_{read/write}_cfg() callbacks were added to struct altera_pci_ops to easily distinguish between hardware variants. Signed-off-by: D M, Sharath Kumar Signed-off-by: Matthew Gerlach Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250221170452.875419-3-matthew.gerlach@linux.intel.com [kwilczynski: tidy code comments] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-altera.c | 255 +++++++++++++++++++++++++++++++++-- 1 file changed, 246 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index eb55a7f8573a..0a4b70f2d43f 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -6,6 +6,7 @@ * Description: Altera PCIe host controller driver */ +#include #include #include #include @@ -77,9 +78,25 @@ #define S10_TLP_FMTTYPE_CFGWR0 0x45 #define S10_TLP_FMTTYPE_CFGWR1 0x44 +#define AGLX_RP_CFG_ADDR(pcie, reg) (((pcie)->hip_base) + (reg)) +#define AGLX_RP_SECONDARY(pcie) \ + readb(AGLX_RP_CFG_ADDR(pcie, PCI_SECONDARY_BUS)) + +#define AGLX_BDF_REG 0x00002004 +#define AGLX_ROOT_PORT_IRQ_STATUS 0x14c +#define AGLX_ROOT_PORT_IRQ_ENABLE 0x150 +#define CFG_AER BIT(4) + +#define AGLX_CFG_TARGET GENMASK(13, 12) +#define AGLX_CFG_TARGET_TYPE0 0 +#define AGLX_CFG_TARGET_TYPE1 1 +#define AGLX_CFG_TARGET_LOCAL_2000 2 +#define AGLX_CFG_TARGET_LOCAL_3000 3 + enum altera_pcie_version { ALTERA_PCIE_V1 = 0, ALTERA_PCIE_V2, + ALTERA_PCIE_V3, }; struct altera_pcie { @@ -102,6 +119,11 @@ struct altera_pcie_ops { int size, u32 *value); int (*rp_write_cfg)(struct altera_pcie *pcie, u8 busno, int where, int size, u32 value); + int (*ep_read_cfg)(struct altera_pcie *pcie, u8 busno, + unsigned int devfn, int where, int size, u32 *value); + int (*ep_write_cfg)(struct altera_pcie *pcie, u8 busno, + unsigned int devfn, int where, int size, u32 value); + void (*rp_isr)(struct irq_desc *desc); }; struct altera_pcie_data { @@ -112,6 +134,9 @@ struct altera_pcie_data { u32 cfgrd1; u32 cfgwr0; u32 cfgwr1; + u32 port_conf_offset; + u32 port_irq_status_offset; + u32 port_irq_enable_offset; }; struct tlp_rp_regpair_t { @@ -131,6 +156,28 @@ static inline u32 cra_readl(struct altera_pcie *pcie, const u32 reg) return readl_relaxed(pcie->cra_base + reg); } +static inline void cra_writew(struct altera_pcie *pcie, const u32 value, + const u32 reg) +{ + writew_relaxed(value, pcie->cra_base + reg); +} + +static inline u32 cra_readw(struct altera_pcie *pcie, const u32 reg) +{ + return readw_relaxed(pcie->cra_base + reg); +} + +static inline void cra_writeb(struct altera_pcie *pcie, const u32 value, + const u32 reg) +{ + writeb_relaxed(value, pcie->cra_base + reg); +} + +static inline u32 cra_readb(struct altera_pcie *pcie, const u32 reg) +{ + return readb_relaxed(pcie->cra_base + reg); +} + static bool altera_pcie_link_up(struct altera_pcie *pcie) { return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0); @@ -145,6 +192,15 @@ static bool s10_altera_pcie_link_up(struct altera_pcie *pcie) return !!(readw(addr) & PCI_EXP_LNKSTA_DLLLA); } +static bool aglx_altera_pcie_link_up(struct altera_pcie *pcie) +{ + void __iomem *addr = AGLX_RP_CFG_ADDR(pcie, + pcie->pcie_data->cap_offset + + PCI_EXP_LNKSTA); + + return (readw_relaxed(addr) & PCI_EXP_LNKSTA_DLLLA); +} + /* * Altera PCIe port uses BAR0 of RC's configuration space as the translation * from PCI bus to native BUS. Entire DDR region is mapped into PCIe space @@ -425,6 +481,103 @@ static int s10_rp_write_cfg(struct altera_pcie *pcie, u8 busno, return PCIBIOS_SUCCESSFUL; } +static int aglx_rp_read_cfg(struct altera_pcie *pcie, int where, + int size, u32 *value) +{ + void __iomem *addr = AGLX_RP_CFG_ADDR(pcie, where); + + switch (size) { + case 1: + *value = readb_relaxed(addr); + break; + case 2: + *value = readw_relaxed(addr); + break; + default: + *value = readl_relaxed(addr); + break; + } + + /* Interrupt PIN not programmed in hardware, set to INTA. */ + if (where == PCI_INTERRUPT_PIN && size == 1 && !(*value)) + *value = 0x01; + else if (where == PCI_INTERRUPT_LINE && !(*value & 0xff00)) + *value |= 0x0100; + + return PCIBIOS_SUCCESSFUL; +} + +static int aglx_rp_write_cfg(struct altera_pcie *pcie, u8 busno, + int where, int size, u32 value) +{ + void __iomem *addr = AGLX_RP_CFG_ADDR(pcie, where); + + switch (size) { + case 1: + writeb_relaxed(value, addr); + break; + case 2: + writew_relaxed(value, addr); + break; + default: + writel_relaxed(value, addr); + break; + } + + /* + * Monitor changes to PCI_PRIMARY_BUS register on Root Port + * and update local copy of root bus number accordingly. + */ + if (busno == pcie->root_bus_nr && where == PCI_PRIMARY_BUS) + pcie->root_bus_nr = value & 0xff; + + return PCIBIOS_SUCCESSFUL; +} + +static int aglx_ep_write_cfg(struct altera_pcie *pcie, u8 busno, + unsigned int devfn, int where, int size, u32 value) +{ + cra_writel(pcie, ((busno << 8) | devfn), AGLX_BDF_REG); + if (busno > AGLX_RP_SECONDARY(pcie)) + where |= FIELD_PREP(AGLX_CFG_TARGET, AGLX_CFG_TARGET_TYPE1); + + switch (size) { + case 1: + cra_writeb(pcie, value, where); + break; + case 2: + cra_writew(pcie, value, where); + break; + default: + cra_writel(pcie, value, where); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int aglx_ep_read_cfg(struct altera_pcie *pcie, u8 busno, + unsigned int devfn, int where, int size, u32 *value) +{ + cra_writel(pcie, ((busno << 8) | devfn), AGLX_BDF_REG); + if (busno > AGLX_RP_SECONDARY(pcie)) + where |= FIELD_PREP(AGLX_CFG_TARGET, AGLX_CFG_TARGET_TYPE1); + + switch (size) { + case 1: + *value = cra_readb(pcie, where); + break; + case 2: + *value = cra_readw(pcie, where); + break; + default: + *value = cra_readl(pcie, where); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno, unsigned int devfn, int where, int size, u32 *value) @@ -437,6 +590,10 @@ static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno, return pcie->pcie_data->ops->rp_read_cfg(pcie, where, size, value); + if (pcie->pcie_data->ops->ep_read_cfg) + return pcie->pcie_data->ops->ep_read_cfg(pcie, busno, devfn, + where, size, value); + switch (size) { case 1: byte_en = 1 << (where & 3); @@ -481,6 +638,10 @@ static int _altera_pcie_cfg_write(struct altera_pcie *pcie, u8 busno, return pcie->pcie_data->ops->rp_write_cfg(pcie, busno, where, size, value); + if (pcie->pcie_data->ops->ep_write_cfg) + return pcie->pcie_data->ops->ep_write_cfg(pcie, busno, devfn, + where, size, value); + switch (size) { case 1: data32 = (value & 0xff) << shift; @@ -659,7 +820,32 @@ static void altera_pcie_isr(struct irq_desc *desc) dev_err_ratelimited(dev, "unexpected IRQ, INT%d\n", bit); } } + chained_irq_exit(chip, desc); +} + +static void aglx_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct altera_pcie *pcie; + struct device *dev; + u32 status; + int ret; + + chained_irq_enter(chip, desc); + pcie = irq_desc_get_handler_data(desc); + dev = &pcie->pdev->dev; + + status = readl(pcie->hip_base + pcie->pcie_data->port_conf_offset + + pcie->pcie_data->port_irq_status_offset); + if (status & CFG_AER) { + writel(CFG_AER, (pcie->hip_base + pcie->pcie_data->port_conf_offset + + pcie->pcie_data->port_irq_status_offset)); + + ret = generic_handle_domain_irq(pcie->irq_domain, 0); + if (ret) + dev_err_ratelimited(dev, "unexpected IRQ %d\n", pcie->irq); + } chained_irq_exit(chip, desc); } @@ -694,9 +880,9 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie) if (IS_ERR(pcie->cra_base)) return PTR_ERR(pcie->cra_base); - if (pcie->pcie_data->version == ALTERA_PCIE_V2) { - pcie->hip_base = - devm_platform_ioremap_resource_byname(pdev, "Hip"); + if (pcie->pcie_data->version == ALTERA_PCIE_V2 || + pcie->pcie_data->version == ALTERA_PCIE_V3) { + pcie->hip_base = devm_platform_ioremap_resource_byname(pdev, "Hip"); if (IS_ERR(pcie->hip_base)) return PTR_ERR(pcie->hip_base); } @@ -706,7 +892,7 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie) if (pcie->irq < 0) return pcie->irq; - irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie); + irq_set_chained_handler_and_data(pcie->irq, pcie->pcie_data->ops->rp_isr, pcie); return 0; } @@ -719,6 +905,7 @@ static const struct altera_pcie_ops altera_pcie_ops_1_0 = { .tlp_read_pkt = tlp_read_packet, .tlp_write_pkt = tlp_write_packet, .get_link_status = altera_pcie_link_up, + .rp_isr = altera_pcie_isr, }; static const struct altera_pcie_ops altera_pcie_ops_2_0 = { @@ -727,6 +914,16 @@ static const struct altera_pcie_ops altera_pcie_ops_2_0 = { .get_link_status = s10_altera_pcie_link_up, .rp_read_cfg = s10_rp_read_cfg, .rp_write_cfg = s10_rp_write_cfg, + .rp_isr = altera_pcie_isr, +}; + +static const struct altera_pcie_ops altera_pcie_ops_3_0 = { + .rp_read_cfg = aglx_rp_read_cfg, + .rp_write_cfg = aglx_rp_write_cfg, + .get_link_status = aglx_altera_pcie_link_up, + .ep_read_cfg = aglx_ep_read_cfg, + .ep_write_cfg = aglx_ep_write_cfg, + .rp_isr = aglx_isr, }; static const struct altera_pcie_data altera_pcie_1_0_data = { @@ -749,11 +946,44 @@ static const struct altera_pcie_data altera_pcie_2_0_data = { .cfgwr1 = S10_TLP_FMTTYPE_CFGWR1, }; +static const struct altera_pcie_data altera_pcie_3_0_f_tile_data = { + .ops = &altera_pcie_ops_3_0, + .version = ALTERA_PCIE_V3, + .cap_offset = 0x70, + .port_conf_offset = 0x14000, + .port_irq_status_offset = AGLX_ROOT_PORT_IRQ_STATUS, + .port_irq_enable_offset = AGLX_ROOT_PORT_IRQ_ENABLE, +}; + +static const struct altera_pcie_data altera_pcie_3_0_p_tile_data = { + .ops = &altera_pcie_ops_3_0, + .version = ALTERA_PCIE_V3, + .cap_offset = 0x70, + .port_conf_offset = 0x104000, + .port_irq_status_offset = AGLX_ROOT_PORT_IRQ_STATUS, + .port_irq_enable_offset = AGLX_ROOT_PORT_IRQ_ENABLE, +}; + +static const struct altera_pcie_data altera_pcie_3_0_r_tile_data = { + .ops = &altera_pcie_ops_3_0, + .version = ALTERA_PCIE_V3, + .cap_offset = 0x70, + .port_conf_offset = 0x1300, + .port_irq_status_offset = 0x0, + .port_irq_enable_offset = 0x4, +}; + static const struct of_device_id altera_pcie_of_match[] = { {.compatible = "altr,pcie-root-port-1.0", .data = &altera_pcie_1_0_data }, {.compatible = "altr,pcie-root-port-2.0", .data = &altera_pcie_2_0_data }, + {.compatible = "altr,pcie-root-port-3.0-f-tile", + .data = &altera_pcie_3_0_f_tile_data }, + {.compatible = "altr,pcie-root-port-3.0-p-tile", + .data = &altera_pcie_3_0_p_tile_data }, + {.compatible = "altr,pcie-root-port-3.0-r-tile", + .data = &altera_pcie_3_0_r_tile_data }, {}, }; @@ -791,11 +1021,18 @@ static int altera_pcie_probe(struct platform_device *pdev) return ret; } - /* clear all interrupts */ - cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS); - /* enable all interrupts */ - cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE); - altera_pcie_host_init(pcie); + if (pcie->pcie_data->version == ALTERA_PCIE_V1 || + pcie->pcie_data->version == ALTERA_PCIE_V2) { + /* clear all interrupts */ + cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS); + /* enable all interrupts */ + cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE); + altera_pcie_host_init(pcie); + } else if (pcie->pcie_data->version == ALTERA_PCIE_V3) { + writel(CFG_AER, + pcie->hip_base + pcie->pcie_data->port_conf_offset + + pcie->pcie_data->port_irq_enable_offset); + } bridge->sysdata = pcie; bridge->busnr = pcie->root_bus_nr; -- cgit v1.2.3-59-g8ed1b From b4db6be0ceec490f639d2e47449ffe3dd6db7679 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Thu, 6 Mar 2025 17:52:08 +1000 Subject: PCI/DOE: Rename DOE protocol to feature DOE r1.1 replaced all occurrences of "protocol" with the term "feature" or "Data Object Type". PCIe r6.1 incorporated that change. Rename the existing terms protocol with feature. Link: https://lore.kernel.org/r/20250306075211.1855177-1-alistair@alistair23.me Signed-off-by: Alistair Francis Signed-off-by: Bjorn Helgaas Reviewed-by: Jonathan Cameron Reviewed-by: Lukas Wunner --- drivers/pci/doe.c | 88 +++++++++++++++++++++++++++---------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index 7bd7892c5222..2f36262e76f8 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -22,7 +22,7 @@ #include "pci.h" -#define PCI_DOE_PROTOCOL_DISCOVERY 0 +#define PCI_DOE_FEATURE_DISCOVERY 0 /* Timeout of 1 second from 6.30.2 Operation, PCI Spec r6.0 */ #define PCI_DOE_TIMEOUT HZ @@ -43,7 +43,7 @@ * * @pdev: PCI device this mailbox belongs to * @cap_offset: Capability offset - * @prots: Array of protocols supported (encoded as long values) + * @feats: Array of features supported (encoded as long values) * @wq: Wait queue for work item * @work_queue: Queue of pci_doe_work items * @flags: Bit array of PCI_DOE_FLAG_* flags @@ -51,14 +51,14 @@ struct pci_doe_mb { struct pci_dev *pdev; u16 cap_offset; - struct xarray prots; + struct xarray feats; wait_queue_head_t wq; struct workqueue_struct *work_queue; unsigned long flags; }; -struct pci_doe_protocol { +struct pci_doe_feature { u16 vid; u8 type; }; @@ -66,7 +66,7 @@ struct pci_doe_protocol { /** * struct pci_doe_task - represents a single query/response * - * @prot: DOE Protocol + * @feat: DOE Feature * @request_pl: The request payload * @request_pl_sz: Size of the request payload (bytes) * @response_pl: The response payload @@ -78,7 +78,7 @@ struct pci_doe_protocol { * @doe_mb: Used internally by the mailbox */ struct pci_doe_task { - struct pci_doe_protocol prot; + struct pci_doe_feature feat; const __le32 *request_pl; size_t request_pl_sz; __le32 *response_pl; @@ -183,8 +183,8 @@ static int pci_doe_send_req(struct pci_doe_mb *doe_mb, length = 0; /* Write DOE Header */ - val = FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_VID, task->prot.vid) | - FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, task->prot.type); + val = FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_VID, task->feat.vid) | + FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, task->feat.type); pci_write_config_dword(pdev, offset + PCI_DOE_WRITE, val); pci_write_config_dword(pdev, offset + PCI_DOE_WRITE, FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH, @@ -229,12 +229,12 @@ static int pci_doe_recv_resp(struct pci_doe_mb *doe_mb, struct pci_doe_task *tas int i = 0; u32 val; - /* Read the first dword to get the protocol */ + /* Read the first dword to get the feature */ pci_read_config_dword(pdev, offset + PCI_DOE_READ, &val); - if ((FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_VID, val) != task->prot.vid) || - (FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, val) != task->prot.type)) { - dev_err_ratelimited(&pdev->dev, "[%x] expected [VID, Protocol] = [%04x, %02x], got [%04x, %02x]\n", - doe_mb->cap_offset, task->prot.vid, task->prot.type, + if ((FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_VID, val) != task->feat.vid) || + (FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, val) != task->feat.type)) { + dev_err_ratelimited(&pdev->dev, "[%x] expected [VID, Feature] = [%04x, %02x], got [%04x, %02x]\n", + doe_mb->cap_offset, task->feat.vid, task->feat.type, FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_VID, val), FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, val)); return -EIO; @@ -396,7 +396,7 @@ static void pci_doe_task_complete(struct pci_doe_task *task) } static int pci_doe_discovery(struct pci_doe_mb *doe_mb, u8 capver, u8 *index, u16 *vid, - u8 *protocol) + u8 *feature) { u32 request_pl = FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX, *index) | @@ -407,7 +407,7 @@ static int pci_doe_discovery(struct pci_doe_mb *doe_mb, u8 capver, u8 *index, u1 u32 response_pl; int rc; - rc = pci_doe(doe_mb, PCI_VENDOR_ID_PCI_SIG, PCI_DOE_PROTOCOL_DISCOVERY, + rc = pci_doe(doe_mb, PCI_VENDOR_ID_PCI_SIG, PCI_DOE_FEATURE_DISCOVERY, &request_pl_le, sizeof(request_pl_le), &response_pl_le, sizeof(response_pl_le)); if (rc < 0) @@ -418,7 +418,7 @@ static int pci_doe_discovery(struct pci_doe_mb *doe_mb, u8 capver, u8 *index, u1 response_pl = le32_to_cpu(response_pl_le); *vid = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID, response_pl); - *protocol = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL, + *feature = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL, response_pl); *index = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX, response_pl); @@ -426,12 +426,12 @@ static int pci_doe_discovery(struct pci_doe_mb *doe_mb, u8 capver, u8 *index, u1 return 0; } -static void *pci_doe_xa_prot_entry(u16 vid, u8 prot) +static void *pci_doe_xa_feat_entry(u16 vid, u8 prot) { return xa_mk_value((vid << 8) | prot); } -static int pci_doe_cache_protocols(struct pci_doe_mb *doe_mb) +static int pci_doe_cache_features(struct pci_doe_mb *doe_mb) { u8 index = 0; u8 xa_idx = 0; @@ -450,11 +450,11 @@ static int pci_doe_cache_protocols(struct pci_doe_mb *doe_mb) return rc; pci_dbg(doe_mb->pdev, - "[%x] Found protocol %d vid: %x prot: %x\n", + "[%x] Found feature %d vid: %x prot: %x\n", doe_mb->cap_offset, xa_idx, vid, prot); - rc = xa_insert(&doe_mb->prots, xa_idx++, - pci_doe_xa_prot_entry(vid, prot), GFP_KERNEL); + rc = xa_insert(&doe_mb->feats, xa_idx++, + pci_doe_xa_feat_entry(vid, prot), GFP_KERNEL); if (rc) return rc; } while (index); @@ -478,7 +478,7 @@ static void pci_doe_cancel_tasks(struct pci_doe_mb *doe_mb) * @pdev: PCI device to create the DOE mailbox for * @cap_offset: Offset of the DOE mailbox * - * Create a single mailbox object to manage the mailbox protocol at the + * Create a single mailbox object to manage the mailbox feature at the * cap_offset specified. * * RETURNS: created mailbox object on success @@ -497,7 +497,7 @@ static struct pci_doe_mb *pci_doe_create_mb(struct pci_dev *pdev, doe_mb->pdev = pdev; doe_mb->cap_offset = cap_offset; init_waitqueue_head(&doe_mb->wq); - xa_init(&doe_mb->prots); + xa_init(&doe_mb->feats); doe_mb->work_queue = alloc_ordered_workqueue("%s %s DOE [%x]", 0, dev_bus_name(&pdev->dev), @@ -520,11 +520,11 @@ static struct pci_doe_mb *pci_doe_create_mb(struct pci_dev *pdev, /* * The state machine and the mailbox should be in sync now; - * Use the mailbox to query protocols. + * Use the mailbox to query features. */ - rc = pci_doe_cache_protocols(doe_mb); + rc = pci_doe_cache_features(doe_mb); if (rc) { - pci_err(pdev, "[%x] failed to cache protocols : %d\n", + pci_err(pdev, "[%x] failed to cache features : %d\n", doe_mb->cap_offset, rc); goto err_cancel; } @@ -533,7 +533,7 @@ static struct pci_doe_mb *pci_doe_create_mb(struct pci_dev *pdev, err_cancel: pci_doe_cancel_tasks(doe_mb); - xa_destroy(&doe_mb->prots); + xa_destroy(&doe_mb->feats); err_destroy_wq: destroy_workqueue(doe_mb->work_queue); err_free: @@ -551,31 +551,31 @@ err_free: static void pci_doe_destroy_mb(struct pci_doe_mb *doe_mb) { pci_doe_cancel_tasks(doe_mb); - xa_destroy(&doe_mb->prots); + xa_destroy(&doe_mb->feats); destroy_workqueue(doe_mb->work_queue); kfree(doe_mb); } /** - * pci_doe_supports_prot() - Return if the DOE instance supports the given - * protocol + * pci_doe_supports_feat() - Return if the DOE instance supports the given + * feature * @doe_mb: DOE mailbox capability to query - * @vid: Protocol Vendor ID - * @type: Protocol type + * @vid: Feature Vendor ID + * @type: Feature type * - * RETURNS: True if the DOE mailbox supports the protocol specified + * RETURNS: True if the DOE mailbox supports the feature specified */ -static bool pci_doe_supports_prot(struct pci_doe_mb *doe_mb, u16 vid, u8 type) +static bool pci_doe_supports_feat(struct pci_doe_mb *doe_mb, u16 vid, u8 type) { unsigned long index; void *entry; - /* The discovery protocol must always be supported */ - if (vid == PCI_VENDOR_ID_PCI_SIG && type == PCI_DOE_PROTOCOL_DISCOVERY) + /* The discovery feature must always be supported */ + if (vid == PCI_VENDOR_ID_PCI_SIG && type == PCI_DOE_FEATURE_DISCOVERY) return true; - xa_for_each(&doe_mb->prots, index, entry) - if (entry == pci_doe_xa_prot_entry(vid, type)) + xa_for_each(&doe_mb->feats, index, entry) + if (entry == pci_doe_xa_feat_entry(vid, type)) return true; return false; @@ -603,7 +603,7 @@ static bool pci_doe_supports_prot(struct pci_doe_mb *doe_mb, u16 vid, u8 type) static int pci_doe_submit_task(struct pci_doe_mb *doe_mb, struct pci_doe_task *task) { - if (!pci_doe_supports_prot(doe_mb, task->prot.vid, task->prot.type)) + if (!pci_doe_supports_feat(doe_mb, task->feat.vid, task->feat.type)) return -EINVAL; if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags)) @@ -649,8 +649,8 @@ int pci_doe(struct pci_doe_mb *doe_mb, u16 vendor, u8 type, { DECLARE_COMPLETION_ONSTACK(c); struct pci_doe_task task = { - .prot.vid = vendor, - .prot.type = type, + .feat.vid = vendor, + .feat.type = type, .request_pl = request, .request_pl_sz = request_sz, .response_pl = response, @@ -675,9 +675,9 @@ EXPORT_SYMBOL_GPL(pci_doe); * * @pdev: PCI device * @vendor: Vendor ID - * @type: Data Object Type + * @prot: Data Object Type * - * Find first DOE mailbox of a PCI device which supports the given protocol. + * Find first DOE mailbox of a PCI device which supports the given feature. * * RETURNS: Pointer to the DOE mailbox or NULL if none was found. */ @@ -688,7 +688,7 @@ struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor, unsigned long index; xa_for_each(&pdev->doe_mbs, index, doe_mb) - if (pci_doe_supports_prot(doe_mb, vendor, type)) + if (pci_doe_supports_feat(doe_mb, vendor, type)) return doe_mb; return NULL; -- cgit v1.2.3-59-g8ed1b From f810d17762fba58b0b547c5fdd77a24e7aa1e939 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Thu, 6 Mar 2025 17:52:09 +1000 Subject: PCI/DOE: Rename Discovery Response Data Object Contents to type PCIe r6.1, sec 6.30.1.1, describes a "Vendor ID", a "Data Object Type" and "Next Index" as the fields in the DOE Discovery Response Data Object. The DOE driver currently uses both the terms 'type' and 'prot' for the second element. Rename all uses of the DOE Discovery Response Data Object to use 'type' as the second element of the object header, instead of type/prot as it currently is. Link: https://lore.kernel.org/r/20250306075211.1855177-2-alistair@alistair23.me Signed-off-by: Alistair Francis Signed-off-by: Bjorn Helgaas Reviewed-by: Jonathan Cameron --- drivers/pci/doe.c | 18 +++++++++--------- include/uapi/linux/pci_regs.h | 5 ++++- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index 2f36262e76f8..f4508d75ce69 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -418,7 +418,7 @@ static int pci_doe_discovery(struct pci_doe_mb *doe_mb, u8 capver, u8 *index, u1 response_pl = le32_to_cpu(response_pl_le); *vid = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID, response_pl); - *feature = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL, + *feature = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE, response_pl); *index = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX, response_pl); @@ -426,9 +426,9 @@ static int pci_doe_discovery(struct pci_doe_mb *doe_mb, u8 capver, u8 *index, u1 return 0; } -static void *pci_doe_xa_feat_entry(u16 vid, u8 prot) +static void *pci_doe_xa_feat_entry(u16 vid, u8 type) { - return xa_mk_value((vid << 8) | prot); + return xa_mk_value((vid << 8) | type); } static int pci_doe_cache_features(struct pci_doe_mb *doe_mb) @@ -442,19 +442,19 @@ static int pci_doe_cache_features(struct pci_doe_mb *doe_mb) do { int rc; u16 vid; - u8 prot; + u8 type; rc = pci_doe_discovery(doe_mb, PCI_EXT_CAP_VER(hdr), &index, - &vid, &prot); + &vid, &type); if (rc) return rc; pci_dbg(doe_mb->pdev, - "[%x] Found feature %d vid: %x prot: %x\n", - doe_mb->cap_offset, xa_idx, vid, prot); + "[%x] Found feature %d vid: %x type: %x\n", + doe_mb->cap_offset, xa_idx, vid, type); rc = xa_insert(&doe_mb->feats, xa_idx++, - pci_doe_xa_feat_entry(vid, prot), GFP_KERNEL); + pci_doe_xa_feat_entry(vid, type), GFP_KERNEL); if (rc) return rc; } while (index); @@ -675,7 +675,7 @@ EXPORT_SYMBOL_GPL(pci_doe); * * @pdev: PCI device * @vendor: Vendor ID - * @prot: Data Object Type + * @type: Data Object Type * * Find first DOE mailbox of a PCI device which supports the given feature. * diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 3445c4970e4d..7f9af95e2e6a 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -1205,9 +1205,12 @@ #define PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX 0x000000ff #define PCI_DOE_DATA_OBJECT_DISC_REQ_3_VER 0x0000ff00 #define PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID 0x0000ffff -#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL 0x00ff0000 +#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE 0x00ff0000 #define PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX 0xff000000 +/* Deprecated old name, replaced with PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE */ +#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE + /* Compute Express Link (CXL r3.1, sec 8.1.5) */ #define PCI_DVSEC_CXL_PORT 3 #define PCI_DVSEC_CXL_PORT_CTL 0x0c -- cgit v1.2.3-59-g8ed1b From 5af473941b56189423a7d16c05efabaf77299847 Mon Sep 17 00:00:00 2001 From: Zhiyuan Dai Date: Fri, 7 Mar 2025 13:35:29 +0800 Subject: PCI: Increase Resizable BAR support from 512 GB to 128 TB Per PCIe r6.0, sec 7.8.6.2, devices can advertise Resizable BAR sizes up to 128 TB in the Resizable BAR Capability register. Larger sizes can be advertised via the Capability register, but that requires an API change. Update pci_rebar_get_possible_sizes() and pbus_size_mem() to increase the sizes we currently support from 512 GB to 128 TB. Link: https://lore.kernel.org/r/20250307053535.44918-1-daizhiyuan@phytium.com.cn Signed-off-by: Zhiyuan Dai Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 4 ++-- drivers/pci/setup-bus.c | 2 +- include/uapi/linux/pci_regs.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c4f710f782f6..72ac91e359aa 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3757,7 +3757,7 @@ static int pci_rebar_find_pos(struct pci_dev *pdev, int bar) * @bar: BAR to query * * Get the possible sizes of a resizable BAR as bitmask defined in the spec - * (bit 0=1MB, bit 19=512GB). Returns 0 if BAR isn't resizable. + * (bit 0=1MB, bit 31=128TB). Returns 0 if BAR isn't resizable. */ u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar) { @@ -3805,7 +3805,7 @@ int pci_rebar_get_current_size(struct pci_dev *pdev, int bar) * pci_rebar_set_size - set a new size for a BAR * @pdev: PCI device * @bar: BAR to set size to - * @size: new size as defined in the spec (0=1MB, 19=512GB) + * @size: new size as defined in the spec (0=1MB, 31=128TB) * * Set the new size of a BAR as defined in the spec. * Returns zero if resizing was successful, error code otherwise. diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index aa36e2d15f32..58f28e4e24b3 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1159,7 +1159,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, { struct pci_dev *dev; resource_size_t min_align, win_align, align, size, size0, size1 = 0; - resource_size_t aligns[24]; /* Alignments from 1MB to 8TB */ + resource_size_t aligns[28]; /* Alignments from 1MB to 128TB */ int order, max_order; struct resource *b_res = find_bus_resource_of_type(bus, mask | IORESOURCE_PREFETCH, type); diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 3445c4970e4d..3c2558b98d22 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -1013,7 +1013,7 @@ /* Resizable BARs */ #define PCI_REBAR_CAP 4 /* capability register */ -#define PCI_REBAR_CAP_SIZES 0x00FFFFF0 /* supported BAR sizes */ +#define PCI_REBAR_CAP_SIZES 0xFFFFFFF0 /* supported BAR sizes */ #define PCI_REBAR_CTRL 8 /* control register */ #define PCI_REBAR_CTRL_BAR_IDX 0x00000007 /* BAR index */ #define PCI_REBAR_CTRL_NBAR_MASK 0x000000E0 /* # of resizable BARs */ -- cgit v1.2.3-59-g8ed1b From e4cb29386ffc1d12885e412232adf361c77a93ac Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Fri, 7 Mar 2025 16:09:22 +0200 Subject: PCI: Do not claim to release resource falsely MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_release_resource() will print "... releasing" regardless of the resource being assigned or not. Move the print after the res->parent check to avoid claiming the kernel would be releasing an unassigned resource. Likely, none of the current callers pass a resource that is unassigned so this change is mostly to correct the non-sensical order than to remove errorneous printouts. Link: https://lore.kernel.org/r/20250307140922.5776-1-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-res.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index b056acfda96c..c6657cdd06f6 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -411,11 +411,11 @@ void pci_release_resource(struct pci_dev *dev, int resno) struct resource *res = pci_resource_n(dev, resno); const char *res_name = pci_resource_name(dev, resno); - pci_info(dev, "%s %pR: releasing\n", res_name, res); - if (!res->parent) return; + pci_info(dev, "%s %pR: releasing\n", res_name, res); + release_resource(res); res->end = resource_size(res) - 1; res->start = 0; -- cgit v1.2.3-59-g8ed1b From 2a93192d2058507b2e39b590fc1efa0e03344136 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 4 Feb 2025 12:06:41 +0100 Subject: misc: pci_endpoint_test: Fix pci_endpoint_test_bars_read_bar() error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit f26d37ee9bda ("misc: pci_endpoint_test: Fix IOCTL return value") changed the return value of pci_endpoint_test_bars_read_bar() from false to -EINVAL on error, however, it failed to update the error handling. Fixes: f26d37ee9bda ("misc: pci_endpoint_test: Fix IOCTL return value") Signed-off-by: Niklas Cassel Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250204110640.570823-2-cassel@kernel.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Krzysztof Wilczyński --- drivers/misc/pci_endpoint_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index d5ac71a49386..7584d1876859 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -382,7 +382,7 @@ static int pci_endpoint_test_bars_read_bar(struct pci_endpoint_test *test, static int pci_endpoint_test_bars(struct pci_endpoint_test *test) { enum pci_barno bar; - bool ret; + int ret; /* Write all BARs in order (without reading). */ for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) @@ -398,7 +398,7 @@ static int pci_endpoint_test_bars(struct pci_endpoint_test *test) for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { if (test->bar[bar]) { ret = pci_endpoint_test_bars_read_bar(test, bar); - if (!ret) + if (ret) return ret; } } -- cgit v1.2.3-59-g8ed1b From c727ebe94c0444953ac96fc78247520b13f7362d Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 23 Jan 2025 11:31:28 +0100 Subject: misc: pci_endpoint_test: Fix potential truncation in pci_endpoint_test_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Increase the size of the string buffer to avoid potential truncation in pci_endpoint_test_probe(). This fixes the following build warning when compiling with W=1: drivers/misc/pci_endpoint_test.c:29:49: note: directive argument in the range [0, 2147483647] 29 | #define DRV_MODULE_NAME "pci-endpoint-test" | ^~~~~~~~~~~~~~~~~~~ drivers/misc/pci_endpoint_test.c:998:38: note: in expansion of macro ‘DRV_MODULE_NAME’ 998 | snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id); | ^~~~~~~~~~~~~~~ drivers/misc/pci_endpoint_test.c:998:9: note: ‘snprintf’ output between 20 and 29 bytes into a destination of size 24 998 | snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Niklas Cassel Reviewed-by: Manivannan Sadhasivam Reviewed-by: Frank Li Link: https://lore.kernel.org/r/20250123103127.3581432-2-cassel@kernel.org Signed-off-by: Manivannan Sadhasivam [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/misc/pci_endpoint_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 7584d1876859..0fa5ddd1969b 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -910,7 +910,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, { int ret; int id; - char name[24]; + char name[29]; enum pci_barno bar; void __iomem *base; struct device *dev = &pdev->dev; -- cgit v1.2.3-59-g8ed1b From 7e80bbef1d697dbce7a39cfad0df770880fe3f29 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 23 Jan 2025 13:01:48 +0100 Subject: misc: pci_endpoint_test: Give disabled BARs a distinct error code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code returns -ENOMEM if test->bar[barno] is NULL. There can be two reasons why test->bar[barno] is NULL: 1) The pci_ioremap_bar() call in pci_endpoint_test_probe() failed. 2) The BAR was skipped, because it is disabled by the endpoint. Many PCI endpoint controller drivers will disable all BARs in their init function. A disabled BAR will have a size of 0. A PCI endpoint function driver will be able to enable any BAR that is not marked as BAR_RESERVED (which means that the BAR should not be touched by the EPF driver). Thus, perform check if the size is 0, before checking if test->bar[barno] is NULL, such that we can return different errors. This will allow the selftests to return SKIP instead of FAIL for disabled BARs. Signed-off-by: Niklas Cassel Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250123120147.3603409-3-cassel@kernel.org Signed-off-by: Manivannan Sadhasivam [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/misc/pci_endpoint_test.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 0fa5ddd1969b..705659f5f0e9 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -292,11 +292,13 @@ static int pci_endpoint_test_bar(struct pci_endpoint_test *test, void *read_buf __free(kfree) = NULL; struct pci_dev *pdev = test->pdev; + bar_size = pci_resource_len(pdev, barno); + if (!bar_size) + return -ENODATA; + if (!test->bar[barno]) return -ENOMEM; - bar_size = pci_resource_len(pdev, barno); - if (barno == test->test_reg_bar) bar_size = 0x4; -- cgit v1.2.3-59-g8ed1b From 7962c82a6e648d07bf0067796e4a0e69ba1fc702 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 24 Jan 2025 10:33:01 +0100 Subject: misc: pci_endpoint_test: Handle BAR sizes larger than INT_MAX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Running 'pcitest -b 0' fails with "TEST FAILED" when the BAR0 size is e.g. 8 GB. The return value of the pci_resource_len() macro can be larger than that of a signed integer type. Thus, when using 'pcitest' with an 8 GB BAR, the bar_size of the integer type will overflow. Change bar_size from integer to resource_size_t to prevent integer overflow for large BAR sizes with 32-bit compilers. In order to handle 64-bit resource_type_t on 32-bit platforms, we would have needed to use a function like div_u64() or similar. Instead, change the code to use addition instead of division. This avoids the need for div_u64() or similar, while also simplifying the code. Fixes: cda370ec6d1f ("misc: pci_endpoint_test: Avoid using hard-coded BAR sizes") Co-developed-by: Hans Zhang <18255117159@163.com> Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Niklas Cassel Reviewed-by: Manivannan Sadhasivam Tested-by: Jon Hunter Reviewed-by: Frank Li Link: https://lore.kernel.org/r/20250124093300.3629624-2-cassel@kernel.org [mani: added fixes tag] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Krzysztof Wilczyński --- drivers/misc/pci_endpoint_test.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 705659f5f0e9..a3d2caa7a6bb 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -272,9 +272,9 @@ static const u32 bar_test_pattern[] = { }; static int pci_endpoint_test_bar_memcmp(struct pci_endpoint_test *test, - enum pci_barno barno, int offset, - void *write_buf, void *read_buf, - int size) + enum pci_barno barno, + resource_size_t offset, void *write_buf, + void *read_buf, int size) { memset(write_buf, bar_test_pattern[barno], size); memcpy_toio(test->bar[barno] + offset, write_buf, size); @@ -287,10 +287,11 @@ static int pci_endpoint_test_bar_memcmp(struct pci_endpoint_test *test, static int pci_endpoint_test_bar(struct pci_endpoint_test *test, enum pci_barno barno) { - int j, bar_size, buf_size, iters; + resource_size_t bar_size, offset = 0; void *write_buf __free(kfree) = NULL; void *read_buf __free(kfree) = NULL; struct pci_dev *pdev = test->pdev; + int buf_size; bar_size = pci_resource_len(pdev, barno); if (!bar_size) @@ -316,11 +317,12 @@ static int pci_endpoint_test_bar(struct pci_endpoint_test *test, if (!read_buf) return -ENOMEM; - iters = bar_size / buf_size; - for (j = 0; j < iters; j++) - if (pci_endpoint_test_bar_memcmp(test, barno, buf_size * j, - write_buf, read_buf, buf_size)) + while (offset < bar_size) { + if (pci_endpoint_test_bar_memcmp(test, barno, offset, write_buf, + read_buf, buf_size)) return -EIO; + offset += buf_size; + } return 0; } -- cgit v1.2.3-59-g8ed1b From f6cb7828c8e17520d4f5afb416515d3fae1af9a9 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Tue, 25 Feb 2025 20:02:48 +0900 Subject: misc: pci_endpoint_test: Avoid issue of interrupts remaining after request_irq error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After devm_request_irq() fails with error in pci_endpoint_test_request_irq(), the pci_endpoint_test_free_irq_vectors() is called assuming that all IRQs have been released. However, some requested IRQs remain unreleased, so there are still /proc/irq/* entries remaining, and this results in WARN() with the following message: remove_proc_entry: removing non-empty directory 'irq/30', leaking at least 'pci-endpoint-test.0' WARNING: CPU: 0 PID: 202 at fs/proc/generic.c:719 remove_proc_entry +0x190/0x19c To solve this issue, set the number of remaining IRQs to test->num_irqs, and release IRQs in advance by calling pci_endpoint_test_release_irq(). Cc: stable@vger.kernel.org Fixes: e03327122e2c ("pci_endpoint_test: Add 2 ioctl commands") Reviewed-by: Manivannan Sadhasivam Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/20250225110252.28866-3-hayashi.kunihiko@socionext.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/misc/pci_endpoint_test.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index a3d2caa7a6bb..9e56d200d2f0 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -259,6 +259,9 @@ fail: break; } + test->num_irqs = i; + pci_endpoint_test_release_irq(test); + return ret; } -- cgit v1.2.3-59-g8ed1b From 919d14603dab6a9cf03ebbeb2cfa556df48737c8 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Tue, 25 Feb 2025 20:02:49 +0900 Subject: misc: pci_endpoint_test: Fix displaying 'irq_type' after 'request_irq' error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two variables that indicate the interrupt type to be used in the next test execution, global "irq_type" and "test->irq_type". The former is referenced from pci_endpoint_test_get_irq() to preserve the current type for ioctl(PCITEST_GET_IRQTYPE). In the pci_endpoint_test_request_irq(), since this global variable is referenced when an error occurs, the unintended error message is displayed. For example, after running "pcitest -i 2", the following message shows "MSI 3" even if the current IRQ type becomes "MSI-X": pci-endpoint-test 0000:01:00.0: Failed to request IRQ 30 for MSI 3 SET IRQ TYPE TO MSI-X: NOT OKAY Fix this issue by using "test->irq_type" instead of global "irq_type". Cc: stable@vger.kernel.org Fixes: b2ba9225e031 ("misc: pci_endpoint_test: Avoid using module parameter to determine irqtype") Reviewed-by: Manivannan Sadhasivam Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/20250225110252.28866-4-hayashi.kunihiko@socionext.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/misc/pci_endpoint_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 9e56d200d2f0..acf3d8dab131 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -242,7 +242,7 @@ static int pci_endpoint_test_request_irq(struct pci_endpoint_test *test) return 0; fail: - switch (irq_type) { + switch (test->irq_type) { case IRQ_TYPE_INTX: dev_err(dev, "Failed to request IRQ %d for Legacy\n", pci_irq_vector(pdev, i)); -- cgit v1.2.3-59-g8ed1b From 3c936e0ec0e412a3ce6072883da8682fb723d573 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Mon, 27 Jan 2025 17:12:42 +0100 Subject: PCI: endpoint: pci-epf-test: Handle endianness properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The struct pci_epf_test_reg is the actual data in pci-epf-test's test_reg BAR (usually BAR0), which the host uses to send commands (etc.), and which pci-epf-test uses to send back status codes. pci-epf-test currently reads and writes this data without any endianness conversion functions, which means that pci-epf-test is completely broken on big-endian endpoint systems. PCI devices are inherently little-endian, and the data stored in the PCI BARs should be in little-endian. Use endianness conversion functions when reading and writing data to struct pci_epf_test_reg so that pci-epf-test will behave correctly on big-endian endpoint systems. Fixes: 349e7a85b25f ("PCI: endpoint: functions: Add an EP function to test PCI") Reviewed-by: Frank Li Reviewed-by: Manivannan Sadhasivam Signed-off-by: Niklas Cassel Link: https://lore.kernel.org/r/20250127161242.104651-2-cassel@kernel.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Krzysztof Wilczyński --- drivers/pci/endpoint/functions/pci-epf-test.c | 126 +++++++++++++++----------- 1 file changed, 73 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index b94e205ae10b..2409787cf56d 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -66,17 +66,17 @@ struct pci_epf_test { }; struct pci_epf_test_reg { - u32 magic; - u32 command; - u32 status; - u64 src_addr; - u64 dst_addr; - u32 size; - u32 checksum; - u32 irq_type; - u32 irq_number; - u32 flags; - u32 caps; + __le32 magic; + __le32 command; + __le32 status; + __le64 src_addr; + __le64 dst_addr; + __le32 size; + __le32 checksum; + __le32 irq_type; + __le32 irq_number; + __le32 flags; + __le32 caps; } __packed; static struct pci_epf_header test_header = { @@ -324,13 +324,17 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test, struct pci_epc *epc = epf->epc; struct device *dev = &epf->dev; struct pci_epc_map src_map, dst_map; - u64 src_addr = reg->src_addr; - u64 dst_addr = reg->dst_addr; - size_t copy_size = reg->size; + u64 src_addr = le64_to_cpu(reg->src_addr); + u64 dst_addr = le64_to_cpu(reg->dst_addr); + size_t orig_size, copy_size; ssize_t map_size = 0; + u32 flags = le32_to_cpu(reg->flags); + u32 status = 0; void *copy_buf = NULL, *buf; - if (reg->flags & FLAG_USE_DMA) { + orig_size = copy_size = le32_to_cpu(reg->size); + + if (flags & FLAG_USE_DMA) { if (!dma_has_cap(DMA_MEMCPY, epf_test->dma_chan_tx->device->cap_mask)) { dev_err(dev, "DMA controller doesn't support MEMCPY\n"); ret = -EINVAL; @@ -350,7 +354,7 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test, src_addr, copy_size, &src_map); if (ret) { dev_err(dev, "Failed to map source address\n"); - reg->status = STATUS_SRC_ADDR_INVALID; + status = STATUS_SRC_ADDR_INVALID; goto free_buf; } @@ -358,7 +362,7 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test, dst_addr, copy_size, &dst_map); if (ret) { dev_err(dev, "Failed to map destination address\n"); - reg->status = STATUS_DST_ADDR_INVALID; + status = STATUS_DST_ADDR_INVALID; pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &src_map); goto free_buf; @@ -367,7 +371,7 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test, map_size = min_t(size_t, dst_map.pci_size, src_map.pci_size); ktime_get_ts64(&start); - if (reg->flags & FLAG_USE_DMA) { + if (flags & FLAG_USE_DMA) { ret = pci_epf_test_data_transfer(epf_test, dst_map.phys_addr, src_map.phys_addr, map_size, 0, DMA_MEM_TO_MEM); @@ -391,8 +395,8 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test, map_size = 0; } - pci_epf_test_print_rate(epf_test, "COPY", reg->size, &start, - &end, reg->flags & FLAG_USE_DMA); + pci_epf_test_print_rate(epf_test, "COPY", orig_size, &start, &end, + flags & FLAG_USE_DMA); unmap: if (map_size) { @@ -405,9 +409,10 @@ free_buf: set_status: if (!ret) - reg->status |= STATUS_COPY_SUCCESS; + status |= STATUS_COPY_SUCCESS; else - reg->status |= STATUS_COPY_FAIL; + status |= STATUS_COPY_FAIL; + reg->status = cpu_to_le32(status); } static void pci_epf_test_read(struct pci_epf_test *epf_test, @@ -423,9 +428,14 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test, struct pci_epc *epc = epf->epc; struct device *dev = &epf->dev; struct device *dma_dev = epf->epc->dev.parent; - u64 src_addr = reg->src_addr; - size_t src_size = reg->size; + u64 src_addr = le64_to_cpu(reg->src_addr); + size_t orig_size, src_size; ssize_t map_size = 0; + u32 flags = le32_to_cpu(reg->flags); + u32 checksum = le32_to_cpu(reg->checksum); + u32 status = 0; + + orig_size = src_size = le32_to_cpu(reg->size); src_buf = kzalloc(src_size, GFP_KERNEL); if (!src_buf) { @@ -439,12 +449,12 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test, src_addr, src_size, &map); if (ret) { dev_err(dev, "Failed to map address\n"); - reg->status = STATUS_SRC_ADDR_INVALID; + status = STATUS_SRC_ADDR_INVALID; goto free_buf; } map_size = map.pci_size; - if (reg->flags & FLAG_USE_DMA) { + if (flags & FLAG_USE_DMA) { dst_phys_addr = dma_map_single(dma_dev, buf, map_size, DMA_FROM_DEVICE); if (dma_mapping_error(dma_dev, dst_phys_addr)) { @@ -481,11 +491,11 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test, map_size = 0; } - pci_epf_test_print_rate(epf_test, "READ", reg->size, &start, - &end, reg->flags & FLAG_USE_DMA); + pci_epf_test_print_rate(epf_test, "READ", orig_size, &start, &end, + flags & FLAG_USE_DMA); - crc32 = crc32_le(~0, src_buf, reg->size); - if (crc32 != reg->checksum) + crc32 = crc32_le(~0, src_buf, orig_size); + if (crc32 != checksum) ret = -EIO; unmap: @@ -497,9 +507,10 @@ free_buf: set_status: if (!ret) - reg->status |= STATUS_READ_SUCCESS; + status |= STATUS_READ_SUCCESS; else - reg->status |= STATUS_READ_FAIL; + status |= STATUS_READ_FAIL; + reg->status = cpu_to_le32(status); } static void pci_epf_test_write(struct pci_epf_test *epf_test, @@ -514,9 +525,13 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test, struct pci_epc *epc = epf->epc; struct device *dev = &epf->dev; struct device *dma_dev = epf->epc->dev.parent; - u64 dst_addr = reg->dst_addr; - size_t dst_size = reg->size; + u64 dst_addr = le64_to_cpu(reg->dst_addr); + size_t orig_size, dst_size; ssize_t map_size = 0; + u32 flags = le32_to_cpu(reg->flags); + u32 status = 0; + + orig_size = dst_size = le32_to_cpu(reg->size); dst_buf = kzalloc(dst_size, GFP_KERNEL); if (!dst_buf) { @@ -524,7 +539,7 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test, goto set_status; } get_random_bytes(dst_buf, dst_size); - reg->checksum = crc32_le(~0, dst_buf, dst_size); + reg->checksum = cpu_to_le32(crc32_le(~0, dst_buf, dst_size)); buf = dst_buf; while (dst_size) { @@ -532,12 +547,12 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test, dst_addr, dst_size, &map); if (ret) { dev_err(dev, "Failed to map address\n"); - reg->status = STATUS_DST_ADDR_INVALID; + status = STATUS_DST_ADDR_INVALID; goto free_buf; } map_size = map.pci_size; - if (reg->flags & FLAG_USE_DMA) { + if (flags & FLAG_USE_DMA) { src_phys_addr = dma_map_single(dma_dev, buf, map_size, DMA_TO_DEVICE); if (dma_mapping_error(dma_dev, src_phys_addr)) { @@ -576,8 +591,8 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test, map_size = 0; } - pci_epf_test_print_rate(epf_test, "WRITE", reg->size, &start, - &end, reg->flags & FLAG_USE_DMA); + pci_epf_test_print_rate(epf_test, "WRITE", orig_size, &start, &end, + flags & FLAG_USE_DMA); /* * wait 1ms inorder for the write to complete. Without this delay L3 @@ -594,9 +609,10 @@ free_buf: set_status: if (!ret) - reg->status |= STATUS_WRITE_SUCCESS; + status |= STATUS_WRITE_SUCCESS; else - reg->status |= STATUS_WRITE_FAIL; + status |= STATUS_WRITE_FAIL; + reg->status = cpu_to_le32(status); } static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, @@ -605,39 +621,42 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, struct pci_epf *epf = epf_test->epf; struct device *dev = &epf->dev; struct pci_epc *epc = epf->epc; - u32 status = reg->status | STATUS_IRQ_RAISED; + u32 status = le32_to_cpu(reg->status); + u32 irq_number = le32_to_cpu(reg->irq_number); + u32 irq_type = le32_to_cpu(reg->irq_type); int count; /* * Set the status before raising the IRQ to ensure that the host sees * the updated value when it gets the IRQ. */ - WRITE_ONCE(reg->status, status); + status |= STATUS_IRQ_RAISED; + WRITE_ONCE(reg->status, cpu_to_le32(status)); - switch (reg->irq_type) { + switch (irq_type) { case IRQ_TYPE_INTX: pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, PCI_IRQ_INTX, 0); break; case IRQ_TYPE_MSI: count = pci_epc_get_msi(epc, epf->func_no, epf->vfunc_no); - if (reg->irq_number > count || count <= 0) { + if (irq_number > count || count <= 0) { dev_err(dev, "Invalid MSI IRQ number %d / %d\n", - reg->irq_number, count); + irq_number, count); return; } pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, - PCI_IRQ_MSI, reg->irq_number); + PCI_IRQ_MSI, irq_number); break; case IRQ_TYPE_MSIX: count = pci_epc_get_msix(epc, epf->func_no, epf->vfunc_no); - if (reg->irq_number > count || count <= 0) { + if (irq_number > count || count <= 0) { dev_err(dev, "Invalid MSIX IRQ number %d / %d\n", - reg->irq_number, count); + irq_number, count); return; } pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, - PCI_IRQ_MSIX, reg->irq_number); + PCI_IRQ_MSIX, irq_number); break; default: dev_err(dev, "Failed to raise IRQ, unknown type\n"); @@ -654,21 +673,22 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) struct device *dev = &epf->dev; enum pci_barno test_reg_bar = epf_test->test_reg_bar; struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; + u32 irq_type = le32_to_cpu(reg->irq_type); - command = READ_ONCE(reg->command); + command = le32_to_cpu(READ_ONCE(reg->command)); if (!command) goto reset_handler; WRITE_ONCE(reg->command, 0); WRITE_ONCE(reg->status, 0); - if ((READ_ONCE(reg->flags) & FLAG_USE_DMA) && + if ((le32_to_cpu(READ_ONCE(reg->flags)) & FLAG_USE_DMA) && !epf_test->dma_supported) { dev_err(dev, "Cannot transfer data using DMA\n"); goto reset_handler; } - if (reg->irq_type > IRQ_TYPE_MSIX) { + if (irq_type > IRQ_TYPE_MSIX) { dev_err(dev, "Failed to detect IRQ type\n"); goto reset_handler; } -- cgit v1.2.3-59-g8ed1b From 52132f3a63b33fd38ceef07392ed176db84d579f Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 31 Jan 2025 19:29:50 +0100 Subject: PCI: endpoint: Allow EPF drivers to configure the size of Resizable BARs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A resizable BAR is different from a normal BAR in a few ways: - The minimum size of a resizable BAR is 1 MB. - Each BAR that is resizable has a Capability and Control register in the Resizable BAR Capability structure. These registers contain the supported sizes and the currently selected size of a resizable BAR. The supported sizes is a bitmap of the supported sizes. The selected size is a single value that is equal to one of the supported sizes. A resizable BAR thus has to be configured differently than a BAR_PROGRAMMABLE BAR, which usually sets the BAR size/mask in a vendor specific way. The PCI endpoint framework currently does not support resizable BARs. Add a BAR type BAR_RESIZABLE, so that an EPC driver can support resizable BARs properly. Note that the pci_epc_set_bar() API takes a struct pci_epf_bar which tells the EPC driver how it wants to configure the BAR. struct pci_epf_bar only has a single size struct member. This means that an EPC driver will only be able to set a single supported size. This is perfectly fine, as we do not need the complexity of allowing a host to change the size of the BAR. If someone ever wants to support resizing a resizable BAR, the pci_epc_set_bar() API can be extended in the future. With these changes, we allow an EPF driver to configure the size of Resizable BARs, rather than forcing them to a 1 MB size. Signed-off-by: Niklas Cassel Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250131182949.465530-10-cassel@kernel.org Signed-off-by: Manivannan Sadhasivam [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/endpoint/pci-epc-core.c | 4 ++++ drivers/pci/endpoint/pci-epf-core.c | 4 ++++ include/linux/pci-epc.h | 4 ++++ 3 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 9e9ca5f8e8f8..10dfc716328e 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -609,6 +609,10 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, if (!epc_features) return -EINVAL; + if (epc_features->bar[bar].type == BAR_RESIZABLE && + (epf_bar->size < SZ_1M || (u64)epf_bar->size > (SZ_128G * 1024))) + return -EINVAL; + if (epc_features->bar[bar].type == BAR_FIXED && (epc_features->bar[bar].fixed_size != epf_bar->size)) return -EINVAL; diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 50bc2892a36c..394395c7f8de 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -274,6 +274,10 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar, if (size < 128) size = 128; + /* According to PCIe base spec, min size for a resizable BAR is 1 MB. */ + if (epc_features->bar[bar].type == BAR_RESIZABLE && size < SZ_1M) + size = SZ_1M; + if (epc_features->bar[bar].type == BAR_FIXED && bar_fixed_size) { if (size > bar_fixed_size) { dev_err(&epf->dev, diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index e818e3fdcded..91ce39dc0fd4 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -188,11 +188,15 @@ struct pci_epc { * enum pci_epc_bar_type - configurability of endpoint BAR * @BAR_PROGRAMMABLE: The BAR mask can be configured by the EPC. * @BAR_FIXED: The BAR mask is fixed by the hardware. + * @BAR_RESIZABLE: The BAR implements the PCI-SIG Resizable BAR Capability. + * NOTE: An EPC driver can currently only set a single supported + * size. * @BAR_RESERVED: The BAR should not be touched by an EPF driver. */ enum pci_epc_bar_type { BAR_PROGRAMMABLE = 0, BAR_FIXED, + BAR_RESIZABLE, BAR_RESERVED, }; -- cgit v1.2.3-59-g8ed1b From 4eb208424c9c49dc3298a45e8db7cf43fdf15600 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 31 Jan 2025 19:29:51 +0100 Subject: PCI: endpoint: Add pci_epc_bar_size_to_rebar_cap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a helper function to convert a size to the representation used by the Resizable BAR Capability Register. Signed-off-by: Niklas Cassel Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250131182949.465530-11-cassel@kernel.org [mani: squashed the change that added PCIe spec reference to comments from https://lore.kernel.org/linux-pci/20250219171454.2903059-2-cassel@kernel.org] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Krzysztof Wilczyński --- drivers/pci/endpoint/pci-epc-core.c | 27 +++++++++++++++++++++++++++ include/linux/pci-epc.h | 1 + 2 files changed, 28 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 10dfc716328e..88cb426d3aec 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -638,6 +638,33 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, } EXPORT_SYMBOL_GPL(pci_epc_set_bar); +/** + * pci_epc_bar_size_to_rebar_cap() - convert a size to the representation used + * by the Resizable BAR Capability Register + * @size: the size to convert + * @cap: where to store the result + * + * Returns 0 on success and a negative error code in case of error. + */ +int pci_epc_bar_size_to_rebar_cap(size_t size, u32 *cap) +{ + /* + * As per PCIe r6.0, sec 7.8.6.2, min size for a resizable BAR is 1 MB, + * thus disallow a requested BAR size smaller than 1 MB. + * Disallow a requested BAR size larger than 128 TB. + */ + if (size < SZ_1M || (u64)size > (SZ_128G * 1024)) + return -EINVAL; + + *cap = ilog2(size) - ilog2(SZ_1M); + + /* Sizes in REBAR_CAP start at BIT(4). */ + *cap = BIT(*cap + 4); + + return 0; +} +EXPORT_SYMBOL_GPL(pci_epc_bar_size_to_rebar_cap); + /** * pci_epc_write_header() - write standard configuration header * @epc: the EPC device to which the configuration header should be written diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 91ce39dc0fd4..713348322dea 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -275,6 +275,7 @@ void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf, enum pci_epc_interface_type type); int pci_epc_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_header *hdr); +int pci_epc_bar_size_to_rebar_cap(size_t size, u32 *cap); int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar); void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, -- cgit v1.2.3-59-g8ed1b From 30a172db9fa46add11883d66c4f34e194b741528 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 31 Jan 2025 19:29:52 +0100 Subject: PCI: dwc: ep: Move dw_pcie_ep_find_ext_capability() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move dw_pcie_ep_find_ext_capability() so that it is located next to dw_pcie_ep_find_capability(). Additionally, a follow-up commit requires this to be defined earlier in order to avoid a forward declaration. Reviewed-by: Manivannan Sadhasivam Signed-off-by: Niklas Cassel Link: https://lore.kernel.org/r/20250131182949.465530-12-cassel@kernel.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/dwc/pcie-designware-ep.c | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 8e07d432e74f..6b494781da42 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -102,6 +102,24 @@ static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap) return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap); } +static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap) +{ + u32 header; + int pos = PCI_CFG_SPACE_SIZE; + + while (pos) { + header = dw_pcie_readl_dbi(pci, pos); + if (PCI_EXT_CAP_ID(header) == cap) + return pos; + + pos = PCI_EXT_CAP_NEXT(header); + if (!pos) + break; + } + + return 0; +} + static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_header *hdr) { @@ -690,24 +708,6 @@ void dw_pcie_ep_deinit(struct dw_pcie_ep *ep) } EXPORT_SYMBOL_GPL(dw_pcie_ep_deinit); -static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap) -{ - u32 header; - int pos = PCI_CFG_SPACE_SIZE; - - while (pos) { - header = dw_pcie_readl_dbi(pci, pos); - if (PCI_EXT_CAP_ID(header) == cap) - return pos; - - pos = PCI_EXT_CAP_NEXT(header); - if (!pos) - break; - } - - return 0; -} - static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) { unsigned int offset; -- cgit v1.2.3-59-g8ed1b From 3a3d4cabe681bc5d4f34626739b67d4572b0770c Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 31 Jan 2025 19:29:53 +0100 Subject: PCI: dwc: ep: Allow EPF drivers to configure the size of Resizable BARs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DWC databook specifies three different BARn_SIZING_SCHEME_N as: - Fixed Mask (0) - Programmable Mask (1) - Resizable BAR (2) Each of these sizing schemes have different instructions for how to initialize the BAR. The DWC driver currently does not support resizable BARs. Instead, in order to somewhat support resizable BARs, the DWC EP driver currently has an ugly hack that force sets a resizable BAR to 1 MB, if such a BAR is detected. Additionally, this hack only works if the DWC glue driver also has lied in their EPC features, and claimed that the resizable BAR is a 1 MB fixed size BAR. This is unintuitive (as you somehow need to know that you need to lie in your EPC features), but other than that it is overly restrictive, since a resizable BAR is capable of supporting sizes different than 1 MB. Add proper support for resizable BARs in the DWC EP driver. Note that the pci_epc_set_bar() API takes a struct pci_epf_bar which tells the EPC driver how it wants to configure the BAR. struct pci_epf_bar only has a single size struct member. This means that an EPC driver will only be able to set a single supported size. This is perfectly fine, as we do not need the complexity of allowing a host to change the size of the BAR. If someone ever wants to support resizing a resizable BAR, the pci_epc_set_bar() API can be extended in the future. With these changes, we allow an EPF driver to configure the size of Resizable BARs, rather than forcing them to a 1 MB size. This means that an EPC driver does not need to lie in EPC features, and an EPF driver will be able to set an arbitrary size (not be forced to a 1 MB size), just like BAR_PROGRAMMABLE. Signed-off-by: Niklas Cassel Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250131182949.465530-13-cassel@kernel.org Signed-off-by: Manivannan Sadhasivam [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/dwc/pcie-designware-ep.c | 182 ++++++++++++++++++++++-- 1 file changed, 167 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 6b494781da42..72418160e658 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -223,6 +223,125 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, ep->bar_to_atu[bar] = 0; } +static unsigned int dw_pcie_ep_get_rebar_offset(struct dw_pcie *pci, + enum pci_barno bar) +{ + u32 reg, bar_index; + unsigned int offset, nbars; + int i; + + offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); + if (!offset) + return offset; + + reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); + nbars = (reg & PCI_REBAR_CTRL_NBAR_MASK) >> PCI_REBAR_CTRL_NBAR_SHIFT; + + for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) { + reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); + bar_index = reg & PCI_REBAR_CTRL_BAR_IDX; + if (bar_index == bar) + return offset; + } + + return 0; +} + +static int dw_pcie_ep_set_bar_resizable(struct dw_pcie_ep *ep, u8 func_no, + struct pci_epf_bar *epf_bar) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar = epf_bar->barno; + size_t size = epf_bar->size; + int flags = epf_bar->flags; + u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); + unsigned int rebar_offset; + u32 rebar_cap, rebar_ctrl; + int ret; + + rebar_offset = dw_pcie_ep_get_rebar_offset(pci, bar); + if (!rebar_offset) + return -EINVAL; + + ret = pci_epc_bar_size_to_rebar_cap(size, &rebar_cap); + if (ret) + return ret; + + dw_pcie_dbi_ro_wr_en(pci); + + /* + * A BAR mask should not be written for a resizable BAR. The BAR mask + * is automatically derived by the controller every time the "selected + * size" bits are updated, see "Figure 3-26 Resizable BAR Example for + * 32-bit Memory BAR0" in DWC EP databook 5.96a. We simply need to write + * BIT(0) to set the BAR enable bit. + */ + dw_pcie_ep_writel_dbi2(ep, func_no, reg, BIT(0)); + dw_pcie_ep_writel_dbi(ep, func_no, reg, flags); + + if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { + dw_pcie_ep_writel_dbi2(ep, func_no, reg + 4, 0); + dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0); + } + + /* + * Bits 31:0 in PCI_REBAR_CAP define "supported sizes" bits for sizes + * 1 MB to 128 TB. Bits 31:16 in PCI_REBAR_CTRL define "supported sizes" + * bits for sizes 256 TB to 8 EB. Disallow sizes 256 TB to 8 EB. + */ + rebar_ctrl = dw_pcie_readl_dbi(pci, rebar_offset + PCI_REBAR_CTRL); + rebar_ctrl &= ~GENMASK(31, 16); + dw_pcie_writel_dbi(pci, rebar_offset + PCI_REBAR_CTRL, rebar_ctrl); + + /* + * The "selected size" (bits 13:8) in PCI_REBAR_CTRL are automatically + * updated when writing PCI_REBAR_CAP, see "Figure 3-26 Resizable BAR + * Example for 32-bit Memory BAR0" in DWC EP databook 5.96a. + */ + dw_pcie_writel_dbi(pci, rebar_offset + PCI_REBAR_CAP, rebar_cap); + + dw_pcie_dbi_ro_wr_dis(pci); + + return 0; +} + +static int dw_pcie_ep_set_bar_programmable(struct dw_pcie_ep *ep, u8 func_no, + struct pci_epf_bar *epf_bar) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar = epf_bar->barno; + size_t size = epf_bar->size; + int flags = epf_bar->flags; + u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); + + dw_pcie_dbi_ro_wr_en(pci); + + dw_pcie_ep_writel_dbi2(ep, func_no, reg, lower_32_bits(size - 1)); + dw_pcie_ep_writel_dbi(ep, func_no, reg, flags); + + if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { + dw_pcie_ep_writel_dbi2(ep, func_no, reg + 4, upper_32_bits(size - 1)); + dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0); + } + + dw_pcie_dbi_ro_wr_dis(pci); + + return 0; +} + +static enum pci_epc_bar_type dw_pcie_ep_get_bar_type(struct dw_pcie_ep *ep, + enum pci_barno bar) +{ + const struct pci_epc_features *epc_features; + + if (!ep->ops->get_features) + return BAR_PROGRAMMABLE; + + epc_features = ep->ops->get_features(ep); + + return epc_features->bar[bar].type; +} + static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar) { @@ -230,9 +349,9 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct dw_pcie *pci = to_dw_pcie_from_ep(ep); enum pci_barno bar = epf_bar->barno; size_t size = epf_bar->size; + enum pci_epc_bar_type bar_type; int flags = epf_bar->flags; int ret, type; - u32 reg; /* * DWC does not allow BAR pairs to overlap, e.g. you cannot combine BARs @@ -264,19 +383,30 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, goto config_atu; } - reg = PCI_BASE_ADDRESS_0 + (4 * bar); - - dw_pcie_dbi_ro_wr_en(pci); - - dw_pcie_ep_writel_dbi2(ep, func_no, reg, lower_32_bits(size - 1)); - dw_pcie_ep_writel_dbi(ep, func_no, reg, flags); - - if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { - dw_pcie_ep_writel_dbi2(ep, func_no, reg + 4, upper_32_bits(size - 1)); - dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0); + bar_type = dw_pcie_ep_get_bar_type(ep, bar); + switch (bar_type) { + case BAR_FIXED: + /* + * There is no need to write a BAR mask for a fixed BAR (except + * to write 1 to the LSB of the BAR mask register, to enable the + * BAR). Write the BAR mask regardless. (The fixed bits in the + * BAR mask register will be read-only anyway.) + */ + fallthrough; + case BAR_PROGRAMMABLE: + ret = dw_pcie_ep_set_bar_programmable(ep, func_no, epf_bar); + break; + case BAR_RESIZABLE: + ret = dw_pcie_ep_set_bar_resizable(ep, func_no, epf_bar); + break; + default: + ret = -EINVAL; + dev_err(pci->dev, "Invalid BAR type\n"); + break; } - dw_pcie_dbi_ro_wr_dis(pci); + if (ret) + return ret; config_atu: if (!(flags & PCI_BASE_ADDRESS_SPACE)) @@ -710,9 +840,11 @@ EXPORT_SYMBOL_GPL(dw_pcie_ep_deinit); static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) { + struct dw_pcie_ep *ep = &pci->ep; unsigned int offset; unsigned int nbars; - u32 reg, i; + enum pci_barno bar; + u32 reg, i, val; offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); @@ -727,9 +859,29 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) * PCIe r6.0, sec 7.8.6.2 require us to support at least one * size in the range from 1 MB to 512 GB. Advertise support * for 1 MB BAR size only. + * + * For a BAR that has been configured via dw_pcie_ep_set_bar(), + * advertise support for only that size instead. */ - for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) - dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, BIT(4)); + for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) { + /* + * While the RESBAR_CAP_REG_* fields are sticky, the + * RESBAR_CTRL_REG_BAR_SIZE field is non-sticky (it is + * sticky in certain versions of DWC PCIe, but not all). + * + * RESBAR_CTRL_REG_BAR_SIZE is updated automatically by + * the controller when RESBAR_CAP_REG is written, which + * is why RESBAR_CAP_REG is written here. + */ + val = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); + bar = val & PCI_REBAR_CTRL_BAR_IDX; + if (ep->epf_bar[bar]) + pci_epc_bar_size_to_rebar_cap(ep->epf_bar[bar]->size, &val); + else + val = BIT(4); + + dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, val); + } } dw_pcie_setup(pci); -- cgit v1.2.3-59-g8ed1b From 6a6b66f7e607e8f6e5b48e246093ed911c8b31be Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 31 Jan 2025 19:29:54 +0100 Subject: PCI: keystone: Describe Resizable BARs as Resizable BARs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looking at section "12.2.2.4.15 PCIe Subsystem BAR Configuration" in the following Technical Reference Manual (TRM) for AM65x: https://www.ti.com/lit/ug/spruid7e/spruid7e.pdf We can see that BAR2 and BAR5 are not Fixed BARs, but actually Resizable BARs. Now when we actually have support for Resizable BARs, let's configure these BARs as such. Reviewed-by: Manivannan Sadhasivam Signed-off-by: Niklas Cassel Link: https://lore.kernel.org/r/20250131182949.465530-14-cassel@kernel.org Signed-off-by: Manivannan Sadhasivam [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/dwc/pci-keystone.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 63bd5003da45..fdc610ec7e5e 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -966,10 +966,10 @@ static const struct pci_epc_features ks_pcie_am654_epc_features = { .msix_capable = true, .bar[BAR_0] = { .type = BAR_RESERVED, }, .bar[BAR_1] = { .type = BAR_RESERVED, }, - .bar[BAR_2] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, + .bar[BAR_2] = { .type = BAR_RESIZABLE, }, .bar[BAR_3] = { .type = BAR_FIXED, .fixed_size = SZ_64K, }, .bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = 256, }, - .bar[BAR_5] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, + .bar[BAR_5] = { .type = BAR_RESIZABLE, }, .align = SZ_1M, }; -- cgit v1.2.3-59-g8ed1b From a2fa5f96140e30d5ff47c92c5164e0256d92c794 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 31 Jan 2025 19:29:55 +0100 Subject: PCI: keystone: Specify correct alignment requirement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The support for a specific iATU alignment was added in commit 2a9a801620ef ("PCI: endpoint: Add support to specify alignment for buffers allocated to BARs"). This commit specifically mentions both that the alignment by each DWC based EP driver should match CX_ATU_MIN_REGION_SIZE, and that AM65x specifically has a 64 KB alignment. This also matches the CX_ATU_MIN_REGION_SIZE value specified in the section "12.2.2.4.7 PCIe Subsystem Address Translation" of the Technical Reference Manual (TRM) for AM65x: https://www.ti.com/lit/ug/spruid7e/spruid7e.pdf This higher value, 1 MB, was obviously an ugly hack used to be able to handle Resizable BARs which have a minimum size of 1 MB. Now when we actually have support for Resizable BARs, let's configure the iATU alignment requirement to the actual requirement. (BARs described as Resizable will still get aligned to 1 MB.) Cc: stable+noautosel@kernel.org # Depends on PCI endpoint Resizable BARs series Fixes: 23284ad677a9 ("PCI: keystone: Add support for PCIe EP in AM654x Platforms") Signed-off-by: Niklas Cassel Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250131182949.465530-15-cassel@kernel.org Signed-off-by: Manivannan Sadhasivam [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/dwc/pci-keystone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index fdc610ec7e5e..76a37368ae4f 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -970,7 +970,7 @@ static const struct pci_epc_features ks_pcie_am654_epc_features = { .bar[BAR_3] = { .type = BAR_FIXED, .fixed_size = SZ_64K, }, .bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = 256, }, .bar[BAR_5] = { .type = BAR_RESIZABLE, }, - .align = SZ_1M, + .align = SZ_64K, }; static const struct pci_epc_features* -- cgit v1.2.3-59-g8ed1b From aba2b17810d7e2d2c4942c56fa3bb0f6e6d616ff Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 31 Jan 2025 19:29:56 +0100 Subject: PCI: dw-rockchip: Describe Resizable BARs as Resizable BARs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looking at section "11.4.4.29 USP_PCIE_RESBAR Registers Summary" in the Technical Reference Manual (TRM) for RK3588, we can see that none of the BARs are Fixed BARs, but actually Resizable BARs. I couldn't find any reference in the TRM for RK3568, but looking at the downstream PCIe endpoint driver, both RK3568 and RK3588 are treated as the same, so the BARs on RK3568 must also be Resizable BARs. Now when we actually have support for Resizable BARs, let's configure these BARs as such. Reviewed-by: Manivannan Sadhasivam Signed-off-by: Niklas Cassel Link: https://lore.kernel.org/r/20250131182949.465530-16-cassel@kernel.org Signed-off-by: Manivannan Sadhasivam [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 93698abff4d9..df2eaa35d045 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -273,12 +273,12 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = { .msi_capable = true, .msix_capable = true, .align = SZ_64K, - .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, - .bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, - .bar[BAR_2] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, - .bar[BAR_3] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, - .bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, - .bar[BAR_5] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, + .bar[BAR_0] = { .type = BAR_RESIZABLE, }, + .bar[BAR_1] = { .type = BAR_RESIZABLE, }, + .bar[BAR_2] = { .type = BAR_RESIZABLE, }, + .bar[BAR_3] = { .type = BAR_RESIZABLE, }, + .bar[BAR_4] = { .type = BAR_RESIZABLE, }, + .bar[BAR_5] = { .type = BAR_RESIZABLE, }, }; /* @@ -293,12 +293,12 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = { .msi_capable = true, .msix_capable = true, .align = SZ_64K, - .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, - .bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, - .bar[BAR_2] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, - .bar[BAR_3] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, + .bar[BAR_0] = { .type = BAR_RESIZABLE, }, + .bar[BAR_1] = { .type = BAR_RESIZABLE, }, + .bar[BAR_2] = { .type = BAR_RESIZABLE, }, + .bar[BAR_3] = { .type = BAR_RESIZABLE, }, .bar[BAR_4] = { .type = BAR_RESERVED, }, - .bar[BAR_5] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, + .bar[BAR_5] = { .type = BAR_RESIZABLE, }, }; static const struct pci_epc_features * -- cgit v1.2.3-59-g8ed1b From 22a01177c30fb4c0ea5e5f9f26473b5ee4660310 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Mon, 17 Feb 2025 20:26:46 +0800 Subject: PCI: endpoint: Remove unused devm_pci_epc_destroy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The static function devm_pci_epc_match() is only invoked within the devm_pci_epc_destroy(). However, since it was initially introduced, this new API has had no callers. Thus, remove both the unused API and the static function. Reviewed-by: Manivannan Sadhasivam Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20250217-remove_api-v2-1-b169c9117045@quicinc.com Signed-off-by: Manivannan Sadhasivam [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- Documentation/PCI/endpoint/pci-endpoint.rst | 7 +++---- drivers/pci/endpoint/pci-epc-core.c | 25 ------------------------- include/linux/pci-epc.h | 1 - 3 files changed, 3 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/Documentation/PCI/endpoint/pci-endpoint.rst b/Documentation/PCI/endpoint/pci-endpoint.rst index 35f82f2d45f5..599763aa01ca 100644 --- a/Documentation/PCI/endpoint/pci-endpoint.rst +++ b/Documentation/PCI/endpoint/pci-endpoint.rst @@ -57,11 +57,10 @@ by the PCI controller driver. The PCI controller driver can then create a new EPC device by invoking devm_pci_epc_create()/pci_epc_create(). -* devm_pci_epc_destroy()/pci_epc_destroy() +* pci_epc_destroy() - The PCI controller driver can destroy the EPC device created by either - devm_pci_epc_create() or pci_epc_create() using devm_pci_epc_destroy() or - pci_epc_destroy(). + The PCI controller driver can destroy the EPC device created by + pci_epc_create() using pci_epc_destroy(). * pci_epc_linkup() diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 88cb426d3aec..beabea00af91 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -25,13 +25,6 @@ static void devm_pci_epc_release(struct device *dev, void *res) pci_epc_destroy(epc); } -static int devm_pci_epc_match(struct device *dev, void *res, void *match_data) -{ - struct pci_epc **epc = res; - - return *epc == match_data; -} - /** * pci_epc_put() - release the PCI endpoint controller * @epc: epc returned by pci_epc_get() @@ -962,24 +955,6 @@ void pci_epc_destroy(struct pci_epc *epc) } EXPORT_SYMBOL_GPL(pci_epc_destroy); -/** - * devm_pci_epc_destroy() - destroy the EPC device - * @dev: device that wants to destroy the EPC - * @epc: the EPC device that has to be destroyed - * - * Invoke to destroy the devres associated with this - * pci_epc and destroy the EPC device. - */ -void devm_pci_epc_destroy(struct device *dev, struct pci_epc *epc) -{ - int r; - - r = devres_release(dev, devm_pci_epc_release, devm_pci_epc_match, - epc); - dev_WARN_ONCE(dev, r, "couldn't find PCI EPC resource\n"); -} -EXPORT_SYMBOL_GPL(devm_pci_epc_destroy); - static void pci_epc_release(struct device *dev) { kfree(to_pci_epc(dev)); diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 713348322dea..9970ae73c8df 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -261,7 +261,6 @@ __devm_pci_epc_create(struct device *dev, const struct pci_epc_ops *ops, struct pci_epc * __pci_epc_create(struct device *dev, const struct pci_epc_ops *ops, struct module *owner); -void devm_pci_epc_destroy(struct device *dev, struct pci_epc *epc); void pci_epc_destroy(struct pci_epc *epc); int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf, enum pci_epc_interface_type type); -- cgit v1.2.3-59-g8ed1b From 934e9d137d937706004c325fa1474f9e3f1ba10a Mon Sep 17 00:00:00 2001 From: Christian Bruel Date: Fri, 24 Jan 2025 13:30:43 +0100 Subject: PCI: endpoint: pci-epf-test: Fix double free that causes kernel to oops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a kernel oops found while testing the stm32_pcie Endpoint driver with handling of PERST# deassertion: During EP initialization, pci_epf_test_alloc_space() allocates all BARs, which are further freed if epc_set_bar() fails (for instance, due to no free inbound window). However, when pci_epc_set_bar() fails, the error path: pci_epc_set_bar() -> pci_epf_free_space() does not clear the previous assignment to epf_test->reg[bar]. Then, if the host reboots, the PERST# deassertion restarts the BAR allocation sequence with the same allocation failure (no free inbound window), creating a double free situation since epf_test->reg[bar] was deallocated and is still non-NULL. Thus, make sure that pci_epf_alloc_space() and pci_epf_free_space() invocations are symmetric, and as such, set epf_test->reg[bar] to NULL when memory is freed. Reviewed-by: Niklas Cassel Reviewed-by: Manivannan Sadhasivam Signed-off-by: Christian Bruel Link: https://lore.kernel.org/r/20250124123043.96112-1-christian.bruel@foss.st.com [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/endpoint/functions/pci-epf-test.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 2409787cf56d..bce3ae2c0f65 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -738,6 +738,7 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) if (ret) { pci_epf_free_space(epf, epf_test->reg[bar], bar, PRIMARY_INTERFACE); + epf_test->reg[bar] = NULL; dev_err(dev, "Failed to set BAR%d\n", bar); if (bar == test_reg_bar) return ret; @@ -929,6 +930,7 @@ static void pci_epf_test_free_space(struct pci_epf *epf) pci_epf_free_space(epf, epf_test->reg[bar], bar, PRIMARY_INTERFACE); + epf_test->reg[bar] = NULL; } } -- cgit v1.2.3-59-g8ed1b From a60a7084200591f57ad7e90a0497130d1c685670 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 21 Feb 2025 21:26:47 +0100 Subject: PCI: dwc: ep: Remove superfluous function dw_pcie_ep_find_ext_capability() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the superfluous function dw_pcie_ep_find_ext_capability(), as it is virtually identical to dw_pcie_find_ext_capability(). Signed-off-by: Niklas Cassel Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250221202646.395252-3-cassel@kernel.org Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/dwc/pcie-designware-ep.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 72418160e658..5729bf313a78 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -102,24 +102,6 @@ static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap) return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap); } -static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap) -{ - u32 header; - int pos = PCI_CFG_SPACE_SIZE; - - while (pos) { - header = dw_pcie_readl_dbi(pci, pos); - if (PCI_EXT_CAP_ID(header) == cap) - return pos; - - pos = PCI_EXT_CAP_NEXT(header); - if (!pos) - break; - } - - return 0; -} - static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_header *hdr) { @@ -230,7 +212,7 @@ static unsigned int dw_pcie_ep_get_rebar_offset(struct dw_pcie *pci, unsigned int offset, nbars; int i; - offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); + offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); if (!offset) return offset; @@ -846,7 +828,7 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) enum pci_barno bar; u32 reg, i, val; - offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); + offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); dw_pcie_dbi_ro_wr_en(pci); @@ -969,7 +951,7 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) if (ep->ops->init) ep->ops->init(ep); - ptm_cap_base = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM); + ptm_cap_base = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM); /* * PTM responder capability can be disabled only after disabling -- cgit v1.2.3-59-g8ed1b From f4e026f454d7bb6aa84901a37641132961054735 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 7 Mar 2025 17:17:15 -0600 Subject: PCI: Fix typos Fix typos and whitespace errors. Link: https://lore.kernel.org/r/20250307231715.438518-1-helgaas@kernel.org Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/cadence/pcie-cadence-ep.c | 8 +-- drivers/pci/controller/dwc/pcie-qcom-ep.c | 12 ++--- drivers/pci/controller/dwc/pcie-qcom.c | 12 ++--- drivers/pci/controller/pci-mvebu.c | 2 +- drivers/pci/controller/pci-thunder-ecam.c | 2 +- drivers/pci/controller/pci-xgene-msi.c | 2 +- drivers/pci/controller/pcie-altera.c | 2 +- drivers/pci/controller/pcie-brcmstb.c | 4 +- drivers/pci/controller/pcie-rcar-host.c | 10 ++-- drivers/pci/endpoint/Kconfig | 2 +- drivers/pci/endpoint/functions/pci-epf-test.c | 2 +- drivers/pci/hotplug/Kconfig | 2 +- drivers/pci/hotplug/pciehp_hpc.c | 2 +- drivers/pci/msi/api.c | 2 +- drivers/pci/of.c | 9 ++-- drivers/pci/pci.c | 2 +- drivers/pci/pcie/aer.c | 68 ++++++++++++------------ drivers/pci/setup-bus.c | 2 +- include/linux/pci-epf.h | 17 +++--- 19 files changed, 84 insertions(+), 78 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index e0cc4560dfde..a4f7ed04d38b 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -301,12 +301,12 @@ static int cdns_pcie_ep_set_msix(struct pci_epc *epc, u8 fn, u8 vfn, val |= interrupts; cdns_pcie_ep_fn_writew(pcie, fn, reg, val); - /* Set MSIX BAR and offset */ + /* Set MSI-X BAR and offset */ reg = cap + PCI_MSIX_TABLE; val = offset | bir; cdns_pcie_ep_fn_writel(pcie, fn, reg, val); - /* Set PBA BAR and offset. BAR must match MSIX BAR */ + /* Set PBA BAR and offset. BAR must match MSI-X BAR */ reg = cap + PCI_MSIX_PBA; val = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | bir; cdns_pcie_ep_fn_writel(pcie, fn, reg, val); @@ -573,8 +573,8 @@ static int cdns_pcie_ep_start(struct pci_epc *epc) /* * Next function field in ARI_CAP_AND_CTR register for last function - * should be 0. - * Clearing Next Function Number field for the last function used. + * should be 0. Clear Next Function Number field for the last + * function used. */ last_fn = find_last_bit(&epc->function_num_map, BITS_PER_LONG); reg = CDNS_PCIE_CORE_PF_I_ARI_CAP_AND_CTRL(last_fn); diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index c08f64d7a825..90819d528e7b 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -48,7 +48,7 @@ #define PARF_DBI_BASE_ADDR_HI 0x354 #define PARF_SLV_ADDR_SPACE_SIZE 0x358 #define PARF_SLV_ADDR_SPACE_SIZE_HI 0x35c -#define PARF_NO_SNOOP_OVERIDE 0x3d4 +#define PARF_NO_SNOOP_OVERRIDE 0x3d4 #define PARF_ATU_BASE_ADDR 0x634 #define PARF_ATU_BASE_ADDR_HI 0x638 #define PARF_SRIS_MODE 0x644 @@ -89,9 +89,9 @@ #define PARF_DEBUG_INT_CFG_BUS_MASTER_EN BIT(2) #define PARF_DEBUG_INT_RADM_PM_TURNOFF BIT(3) -/* PARF_NO_SNOOP_OVERIDE register fields */ -#define WR_NO_SNOOP_OVERIDE_EN BIT(1) -#define RD_NO_SNOOP_OVERIDE_EN BIT(3) +/* PARF_NO_SNOOP_OVERRIDE register fields */ +#define WR_NO_SNOOP_OVERRIDE_EN BIT(1) +#define RD_NO_SNOOP_OVERRIDE_EN BIT(3) /* PARF_DEVICE_TYPE register fields */ #define PARF_DEVICE_TYPE_EP 0x0 @@ -529,8 +529,8 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci) writel_relaxed(val, pcie_ep->parf + PARF_LTSSM); if (pcie_ep->cfg && pcie_ep->cfg->override_no_snoop) - writel_relaxed(WR_NO_SNOOP_OVERIDE_EN | RD_NO_SNOOP_OVERIDE_EN, - pcie_ep->parf + PARF_NO_SNOOP_OVERIDE); + writel_relaxed(WR_NO_SNOOP_OVERRIDE_EN | RD_NO_SNOOP_OVERRIDE_EN, + pcie_ep->parf + PARF_NO_SNOOP_OVERRIDE); return 0; diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index e4d3366ead1f..dc98ae63362d 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -61,7 +61,7 @@ #define PARF_DBI_BASE_ADDR_V2_HI 0x354 #define PARF_SLV_ADDR_SPACE_SIZE_V2 0x358 #define PARF_SLV_ADDR_SPACE_SIZE_V2_HI 0x35c -#define PARF_NO_SNOOP_OVERIDE 0x3d4 +#define PARF_NO_SNOOP_OVERRIDE 0x3d4 #define PARF_ATU_BASE_ADDR 0x634 #define PARF_ATU_BASE_ADDR_HI 0x638 #define PARF_DEVICE_TYPE 0x1000 @@ -135,9 +135,9 @@ #define PARF_INT_ALL_LINK_UP BIT(13) #define PARF_INT_MSI_DEV_0_7 GENMASK(30, 23) -/* PARF_NO_SNOOP_OVERIDE register fields */ -#define WR_NO_SNOOP_OVERIDE_EN BIT(1) -#define RD_NO_SNOOP_OVERIDE_EN BIT(3) +/* PARF_NO_SNOOP_OVERRIDE register fields */ +#define WR_NO_SNOOP_OVERRIDE_EN BIT(1) +#define RD_NO_SNOOP_OVERRIDE_EN BIT(3) /* PARF_DEVICE_TYPE register fields */ #define DEVICE_TYPE_RC 0x4 @@ -1007,8 +1007,8 @@ static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie) const struct qcom_pcie_cfg *pcie_cfg = pcie->cfg; if (pcie_cfg->override_no_snoop) - writel(WR_NO_SNOOP_OVERIDE_EN | RD_NO_SNOOP_OVERIDE_EN, - pcie->parf + PARF_NO_SNOOP_OVERIDE); + writel(WR_NO_SNOOP_OVERRIDE_EN | RD_NO_SNOOP_OVERRIDE_EN, + pcie->parf + PARF_NO_SNOOP_OVERRIDE); qcom_pcie_clear_aspm_l0s(pcie->pci); qcom_pcie_clear_hpc(pcie->pci); diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 665f35f9d826..b0e3bce10aa4 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -1422,7 +1422,7 @@ static void mvebu_pcie_powerdown(struct mvebu_pcie_port *port) } /* - * devm_of_pci_get_host_bridge_resources() only sets up translateable resources, + * devm_of_pci_get_host_bridge_resources() only sets up translatable resources, * so we need extra resource setup parsing our special DT properties encoding * the MEM and IO apertures. */ diff --git a/drivers/pci/controller/pci-thunder-ecam.c b/drivers/pci/controller/pci-thunder-ecam.c index b5bd10a62adb..08161065a89c 100644 --- a/drivers/pci/controller/pci-thunder-ecam.c +++ b/drivers/pci/controller/pci-thunder-ecam.c @@ -204,7 +204,7 @@ static int thunder_ecam_config_read(struct pci_bus *bus, unsigned int devfn, v = readl(addr); if (v & 0xff00) - pr_err("Bad MSIX cap header: %08x\n", v); + pr_err("Bad MSI-X cap header: %08x\n", v); v |= 0xbc00; /* next capability is EA at 0xbc */ set_val(v, where, size, val); return PCIBIOS_SUCCESSFUL; diff --git a/drivers/pci/controller/pci-xgene-msi.c b/drivers/pci/controller/pci-xgene-msi.c index 88c0977bc41a..7bce327897c9 100644 --- a/drivers/pci/controller/pci-xgene-msi.c +++ b/drivers/pci/controller/pci-xgene-msi.c @@ -154,7 +154,7 @@ static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors. To maintain * the expected behaviour of .set_affinity for each MSI interrupt, the 16 * MSI GIC IRQs are statically allocated to 8 X-Gene v1 cores (2 GIC IRQs - * for each core). The MSI vector is moved fom 1 MSI GIC IRQ to another + * for each core). The MSI vector is moved from 1 MSI GIC IRQ to another * MSI GIC IRQ to steer its MSI interrupt to correct X-Gene v1 core. As a * consequence, the total MSI vectors that X-Gene v1 supports will be * reduced to 256 (2048/8) vectors. diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index eb55a7f8573a..e5b3d5dad4bc 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -149,7 +149,7 @@ static bool s10_altera_pcie_link_up(struct altera_pcie *pcie) * Altera PCIe port uses BAR0 of RC's configuration space as the translation * from PCI bus to native BUS. Entire DDR region is mapped into PCIe space * using these registers, so it can be reached by DMA from EP devices. - * This BAR0 will also access to MSI vector when receiving MSI/MSIX interrupt + * This BAR0 will also access to MSI vector when receiving MSI/MSI-X interrupt * from EP devices, eventually trigger interrupt to GIC. The BAR0 of bridge * should be hidden during enumeration to avoid the sizing and resource * allocation by PCIe core. diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index e733a27dc8df..65176826f750 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -40,7 +40,7 @@ /* Broadcom STB PCIe Register Offsets */ #define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1 0x0188 #define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK 0xc -#define PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN 0x0 +#define PCIE_RC_CFG_VENDOR_SPECIFIC_REG1_LITTLE_ENDIAN 0x0 #define PCIE_RC_CFG_PRIV1_ID_VAL3 0x043c #define PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK 0xffffff @@ -1180,7 +1180,7 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) /* PCIe->SCB endian mode for inbound window */ tmp = readl(base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1); - u32p_replace_bits(&tmp, PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN, + u32p_replace_bits(&tmp, PCIE_RC_CFG_VENDOR_SPECIFIC_REG1_LITTLE_ENDIAN, PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK); writel(tmp, base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1); diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index 7c92eada04af..c32b803a47c7 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -178,8 +178,8 @@ static int rcar_pcie_config_access(struct rcar_pcie_host *host, * space, it's generally only accessible when in endpoint mode. * When in root complex mode, the controller is unable to target * itself with either type 0 or type 1 accesses, and indeed, any - * controller initiated target transfer to its own config space - * result in a completer abort. + * controller-initiated target transfer to its own config space + * results in a completer abort. * * Each channel effectively only supports a single device, but as * the same channel <-> device access works for any PCI_SLOT() @@ -775,7 +775,7 @@ static int rcar_pcie_enable_msi(struct rcar_pcie_host *host) if (err) return err; - /* Two irqs are for MSI, but they are also used for non-MSI irqs */ + /* Two IRQs are for MSI, but they are also used for non-MSI IRQs */ err = devm_request_irq(dev, msi->irq1, rcar_pcie_msi_irq, IRQF_SHARED | IRQF_NO_THREAD, rcar_msi_bottom_chip.name, host); @@ -792,7 +792,7 @@ static int rcar_pcie_enable_msi(struct rcar_pcie_host *host) goto err; } - /* disable all MSIs */ + /* Disable all MSIs */ rcar_pci_write_reg(pcie, 0, PCIEMSIIER); /* @@ -892,6 +892,7 @@ static int rcar_pcie_inbound_ranges(struct rcar_pcie *pcie, dev_err(pcie->dev, "Failed to map inbound regions!\n"); return -EINVAL; } + /* * If the size of the range is larger than the alignment of * the start address, we have to use multiple entries to @@ -903,6 +904,7 @@ static int rcar_pcie_inbound_ranges(struct rcar_pcie *pcie, size = min(size, alignment); } + /* Hardware supports max 4GiB inbound region */ size = min(size, 1ULL << 32); diff --git a/drivers/pci/endpoint/Kconfig b/drivers/pci/endpoint/Kconfig index 17bbdc9bbde0..1c5d82eb57d4 100644 --- a/drivers/pci/endpoint/Kconfig +++ b/drivers/pci/endpoint/Kconfig @@ -26,7 +26,7 @@ config PCI_ENDPOINT_CONFIGFS help This will enable the configfs entry that can be used to configure the endpoint function and used to bind the - function with a endpoint controller. + function with an endpoint controller. source "drivers/pci/endpoint/functions/Kconfig" diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index b94e205ae10b..ee1416c43f03 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -632,7 +632,7 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, case IRQ_TYPE_MSIX: count = pci_epc_get_msix(epc, epf->func_no, epf->vfunc_no); if (reg->irq_number > count || count <= 0) { - dev_err(dev, "Invalid MSIX IRQ number %d / %d\n", + dev_err(dev, "Invalid MSI-X IRQ number %d / %d\n", reg->irq_number, count); return; } diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index 123c4c7c2ab5..3207860b52e4 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -97,7 +97,7 @@ config HOTPLUG_PCI_CPCI_ZT5550 tristate "Ziatech ZT5550 CompactPCI Hotplug driver" depends on HOTPLUG_PCI_CPCI && X86 help - Say Y here if you have an Performance Technologies (formerly Intel, + Say Y here if you have a Performance Technologies (formerly Intel, formerly just Ziatech) Ziatech ZT5550 CompactPCI system card. To compile this driver as a module, choose M here: the diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index bb5a8d9f03ad..e17bebe2ceb3 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -430,7 +430,7 @@ void pciehp_get_latch_status(struct controller *ctrl, u8 *status) * removed immediately after the check so the caller may need to take * this into account. * - * It the hotplug controller itself is not available anymore returns + * If the hotplug controller itself is not available anymore returns * %-ENODEV. */ int pciehp_card_present(struct controller *ctrl) diff --git a/drivers/pci/msi/api.c b/drivers/pci/msi/api.c index b956ce591f96..17ec6332cb1d 100644 --- a/drivers/pci/msi/api.c +++ b/drivers/pci/msi/api.c @@ -162,7 +162,7 @@ struct msi_map pci_msix_alloc_irq_at(struct pci_dev *dev, unsigned int index, EXPORT_SYMBOL_GPL(pci_msix_alloc_irq_at); /** - * pci_msix_free_irq - Free an interrupt on a PCI/MSIX interrupt domain + * pci_msix_free_irq - Free an interrupt on a PCI/MSI-X interrupt domain * * @dev: The PCI device to operate on * @map: A struct msi_map describing the interrupt to free diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 7a806f5c0d20..0e4f9119f5f2 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -455,9 +455,9 @@ failed: * @out_irq: structure of_phandle_args filled by this function * * This function resolves the PCI interrupt for a given PCI device. If a - * device-node exists for a given pci_dev, it will use normal OF tree + * device node exists for a given pci_dev, it will use normal OF tree * walking. If not, it will implement standard swizzling and walk up the - * PCI tree until an device-node is found, at which point it will finish + * PCI tree until a device node is found, at which point it will finish * resolving using the OF tree walking. */ static int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq) @@ -517,13 +517,16 @@ static int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args * } /* - * Ok, we have found a parent with a device-node, hand over to + * Ok, we have found a parent with a device node, hand over to * the OF parsing code. + * * We build a unit address from the linux device to be used for * resolution. Note that we use the linux bus number which may * not match your firmware bus numbering. + * * Fortunately, in most cases, interrupt-map-mask doesn't * include the bus number as part of the matching. + * * You should still be careful about that though if you intend * to rely on this function (you ship a firmware that doesn't * create device nodes for all PCI devices). diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 869d204a70a3..2fcd1e583966 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4766,7 +4766,7 @@ static bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active, /* * PCIe r4.0 sec 6.6.1, a component must enter LTSSM Detect within 20ms, - * after which we should expect an link active if the reset was + * after which we should expect the link to be active if the reset was * successful. If so, software must wait a minimum 100ms before sending * configuration requests to devices downstream this port. * diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 508474e17183..de0e1ca3396b 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -2,7 +2,7 @@ /* * Implement the AER root port service driver. The driver registers an IRQ * handler. When a root port triggers an AER interrupt, the IRQ handler - * collects root port status and schedules work. + * collects Root Port status and schedules work. * * Copyright (C) 2006 Intel Corp. * Tom Long Nguyen (tom.l.nguyen@intel.com) @@ -56,9 +56,9 @@ struct aer_stats { /* * Fields for all AER capable devices. They indicate the errors * "as seen by this device". Note that this may mean that if an - * end point is causing problems, the AER counters may increment - * at its link partner (e.g. root port) because the errors will be - * "seen" by the link partner and not the problematic end point + * Endpoint is causing problems, the AER counters may increment + * at its link partner (e.g. Root Port) because the errors will be + * "seen" by the link partner and not the problematic Endpoint * itself (which may report all counters as 0 as it never saw any * problems). */ @@ -76,10 +76,10 @@ struct aer_stats { u64 dev_total_nonfatal_errs; /* - * Fields for Root ports & root complex event collectors only, these + * Fields for Root Ports & Root Complex Event Collectors only; these * indicate the total number of ERR_COR, ERR_FATAL, and ERR_NONFATAL - * messages received by the root port / event collector, INCLUDING the - * ones that are generated internally (by the rootport itself) + * messages received by the Root Port / Event Collector, INCLUDING the + * ones that are generated internally (by the Root Port itself) */ u64 rootport_total_cor_errs; u64 rootport_total_fatal_errs; @@ -138,7 +138,7 @@ static const char * const ecrc_policy_str[] = { * enable_ecrc_checking - enable PCIe ECRC checking for a device * @dev: the PCI device * - * Returns 0 on success, or negative on failure. + * Return: 0 on success, or negative on failure. */ static int enable_ecrc_checking(struct pci_dev *dev) { @@ -159,10 +159,10 @@ static int enable_ecrc_checking(struct pci_dev *dev) } /** - * disable_ecrc_checking - disables PCIe ECRC checking for a device + * disable_ecrc_checking - disable PCIe ECRC checking for a device * @dev: the PCI device * - * Returns 0 on success, or negative on failure. + * Return: 0 on success, or negative on failure. */ static int disable_ecrc_checking(struct pci_dev *dev) { @@ -283,10 +283,10 @@ void pci_aer_clear_fatal_status(struct pci_dev *dev) * pci_aer_raw_clear_status - Clear AER error registers. * @dev: the PCI device * - * Clearing AER error status registers unconditionally, regardless of + * Clear AER error status registers unconditionally, regardless of * whether they're owned by firmware or the OS. * - * Returns 0 on success, or negative on failure. + * Return: 0 on success, or negative on failure. */ int pci_aer_raw_clear_status(struct pci_dev *dev) { @@ -378,8 +378,8 @@ void pci_aer_init(struct pci_dev *dev) /* * We save/restore PCI_ERR_UNCOR_MASK, PCI_ERR_UNCOR_SEVER, * PCI_ERR_COR_MASK, and PCI_ERR_CAP. Root and Root Complex Event - * Collectors also implement PCI_ERR_ROOT_COMMAND (PCIe r5.0, sec - * 7.8.4). + * Collectors also implement PCI_ERR_ROOT_COMMAND (PCIe r6.0, sec + * 7.8.4.9). */ n = pcie_cap_has_rtctl(dev) ? 5 : 4; pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ERR, sizeof(u32) * n); @@ -825,8 +825,8 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) u16 reg16; /* - * When bus id is equal to 0, it might be a bad id - * reported by root port. + * When bus ID is equal to 0, it might be a bad ID + * reported by Root Port. */ if ((PCI_BUS_NUM(e_info->id) != 0) && !(dev->bus->bus_flags & PCI_BUS_FLAGS_NO_AERSID)) { @@ -834,15 +834,15 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) if (e_info->id == pci_dev_id(dev)) return true; - /* Continue id comparing if there is no multiple error */ + /* Continue ID comparing if there is no multiple error */ if (!e_info->multi_error_valid) return false; } /* * When either - * 1) bus id is equal to 0. Some ports might lose the bus - * id of error source id; + * 1) bus ID is equal to 0. Some ports might lose the bus + * ID of error source id; * 2) bus flag PCI_BUS_FLAGS_NO_AERSID is set * 3) There are multiple errors and prior ID comparing fails; * We check AER status registers to find possible reporter. @@ -894,9 +894,9 @@ static int find_device_iter(struct pci_dev *dev, void *data) /** * find_source_device - search through device hierarchy for source device * @parent: pointer to Root Port pci_dev data structure - * @e_info: including detailed error information such like id + * @e_info: including detailed error information such as ID * - * Return true if found. + * Return: true if found. * * Invoked by DPC when error is detected at the Root Port. * Caller of this function must set id, severity, and multi_error_valid of @@ -938,9 +938,9 @@ static bool find_source_device(struct pci_dev *parent, /** * pci_aer_unmask_internal_errors - unmask internal errors - * @dev: pointer to the pcie_dev data structure + * @dev: pointer to the pci_dev data structure * - * Unmasks internal errors in the Uncorrectable and Correctable Error + * Unmask internal errors in the Uncorrectable and Correctable Error * Mask registers. * * Note: AER must be enabled and supported by the device which must be @@ -1003,7 +1003,7 @@ static int cxl_rch_handle_error_iter(struct pci_dev *dev, void *data) if (!is_cxl_mem_dev(dev) || !cxl_error_is_native(dev)) return 0; - /* protect dev->driver */ + /* Protect dev->driver */ device_lock(&dev->dev); err_handler = dev->driver ? dev->driver->err_handler : NULL; @@ -1195,10 +1195,10 @@ EXPORT_SYMBOL_GPL(aer_recover_queue); /** * aer_get_device_error_info - read error status from dev and store it to info - * @dev: pointer to the device expected to have a error record + * @dev: pointer to the device expected to have an error record * @info: pointer to structure to store the error record * - * Return 1 on success, 0 on error. + * Return: 1 on success, 0 on error. * * Note that @info is reused among all error devices. Clear fields properly. */ @@ -1256,7 +1256,7 @@ static inline void aer_process_err_devices(struct aer_err_info *e_info) { int i; - /* Report all before handle them, not to lost records by reset etc. */ + /* Report all before handling them, to not lose records by reset etc. */ for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { if (aer_get_device_error_info(e_info->dev[i], e_info)) aer_print_error(e_info->dev[i], e_info); @@ -1268,8 +1268,8 @@ static inline void aer_process_err_devices(struct aer_err_info *e_info) } /** - * aer_isr_one_error - consume an error detected by root port - * @rpc: pointer to the root port which holds an error + * aer_isr_one_error - consume an error detected by Root Port + * @rpc: pointer to the Root Port which holds an error * @e_src: pointer to an error source */ static void aer_isr_one_error(struct aer_rpc *rpc, @@ -1319,11 +1319,11 @@ static void aer_isr_one_error(struct aer_rpc *rpc, } /** - * aer_isr - consume errors detected by root port + * aer_isr - consume errors detected by Root Port * @irq: IRQ assigned to Root Port * @context: pointer to Root Port data structure * - * Invoked, as DPC, when root port records new detected error + * Invoked, as DPC, when Root Port records new detected error */ static irqreturn_t aer_isr(int irq, void *context) { @@ -1383,7 +1383,7 @@ static void aer_disable_irq(struct pci_dev *pdev) int aer = pdev->aer_cap; u32 reg32; - /* Disable Root's interrupt in response to error messages */ + /* Disable Root Port's interrupt in response to error messages */ pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, ®32); reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, reg32); @@ -1583,9 +1583,9 @@ static struct pcie_port_service_driver aerdriver = { }; /** - * pcie_aer_init - register AER root service driver + * pcie_aer_init - register AER service driver * - * Invoked when AER root service driver is loaded. + * Invoked when AER service driver is loaded. */ int __init pcie_aer_init(void) { diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 5e00cecf1f1a..5b847f7b1f68 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1880,7 +1880,7 @@ static void remove_dev_resources(struct pci_dev *dev, struct resource *io, * Make sure prefetchable memory is reduced from * the correct resource. Specifically we put 32-bit * prefetchable memory in non-prefetchable window - * if there is an 64-bit prefetchable window. + * if there is a 64-bit prefetchable window. * * See comments in __pci_bus_size_bridges() for * more information. diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index ee6156bcbbd0..879d19cebd4f 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -38,7 +38,7 @@ enum pci_barno { * @baseclass_code: broadly classifies the type of function the device performs * @cache_line_size: specifies the system cacheline size in units of DWORDs * @subsys_vendor_id: vendor of the add-in card or subsystem - * @subsys_id: id specific to vendor + * @subsys_id: ID specific to vendor * @interrupt_pin: interrupt pin the device (or device function) uses */ struct pci_epf_header { @@ -59,7 +59,7 @@ struct pci_epf_header { * @bind: ops to perform when a EPC device has been bound to EPF device * @unbind: ops to perform when a binding has been lost between a EPC device * and EPF device - * @add_cfs: ops to initialize function specific configfs attributes + * @add_cfs: ops to initialize function-specific configfs attributes */ struct pci_epf_ops { int (*bind)(struct pci_epf *epf); @@ -138,7 +138,7 @@ struct pci_epf_bar { * @epc: the EPC device to which this EPF device is bound * @epf_pf: the physical EPF device to which this virtual EPF device is bound * @driver: the EPF driver to which this EPF device is bound - * @id: Pointer to the EPF device ID + * @id: pointer to the EPF device ID * @list: to add pci_epf as a list of PCI endpoint functions to pci_epc * @lock: mutex to protect pci_epf_ops * @sec_epc: the secondary EPC device to which this EPF device is bound @@ -151,7 +151,7 @@ struct pci_epf_bar { * @is_vf: true - virtual function, false - physical function * @vfunction_num_map: bitmap to manage virtual function number * @pci_vepf: list of virtual endpoint functions associated with this function - * @event_ops: Callbacks for capturing the EPC events + * @event_ops: callbacks for capturing the EPC events */ struct pci_epf { struct device dev; @@ -185,11 +185,12 @@ struct pci_epf { }; /** - * struct pci_epf_msix_tbl - represents the MSIX table entry structure - * @msg_addr: Writes to this address will trigger MSIX interrupt in host - * @msg_data: Data that should be written to @msg_addr to trigger MSIX interrupt + * struct pci_epf_msix_tbl - represents the MSI-X table entry structure + * @msg_addr: Writes to this address will trigger MSI-X interrupt in host + * @msg_data: Data that should be written to @msg_addr to trigger MSI-X + * interrupt * @vector_ctrl: Identifies if the function is prohibited from sending a message - * using this MSIX table entry + * using this MSI-X table entry */ struct pci_epf_msix_tbl { u64 msg_addr; -- cgit v1.2.3-59-g8ed1b From 3f8c4959fc18e477801386a625e726c59f52a2c4 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 3 Mar 2025 15:02:17 -0600 Subject: PCI: Enable Configuration RRS SV early Following a reset, a Function may respond to Config Requests with Request Retry Status (RRS) Completion Status to indicate that it is temporarily unable to process the Request, but will be able to process the Request in the future (PCIe r6.0, sec 2.3.1). If the Configuration RRS Software Visibility feature is enabled and a Root Complex receives RRS for a config read of the Vendor ID, the Root Complex completes the Request to the host by returning PCI_VENDOR_ID_PCI_SIG, 0x0001 (sec 2.3.2). The Config RRS SV feature applies only to Root Ports and is not directly related to pci_scan_bridge_extend(). Move the RRS SV enable to set_pcie_port_type() where we handle other PCIe-specific configuration. Link: https://lore.kernel.org/r/20250303210217.199504-1-helgaas@kernel.org Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b6536ed599c3..0b013b196d00 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1373,8 +1373,6 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT); - pci_enable_rrs_sv(dev); - if ((secondary || subordinate) && !pcibios_assign_all_busses() && !is_cardbus && !broken) { unsigned int cmax, buses; @@ -1615,6 +1613,11 @@ void set_pcie_port_type(struct pci_dev *pdev) pdev->pcie_cap = pos; pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); pdev->pcie_flags_reg = reg16; + + type = pci_pcie_type(pdev); + if (type == PCI_EXP_TYPE_ROOT_PORT) + pci_enable_rrs_sv(pdev); + pci_read_config_dword(pdev, pos + PCI_EXP_DEVCAP, &pdev->devcap); pdev->pcie_mpss = FIELD_GET(PCI_EXP_DEVCAP_PAYLOAD, pdev->devcap); @@ -1631,7 +1634,6 @@ void set_pcie_port_type(struct pci_dev *pdev) * correctly so detect impossible configurations here and correct * the port type accordingly. */ - type = pci_pcie_type(pdev); if (type == PCI_EXP_TYPE_DOWNSTREAM) { /* * If pdev claims to be downstream port but the parent -- cgit v1.2.3-59-g8ed1b From a7eb9124d92be1330afd18c98e4f28f297b50cb8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 14 Feb 2025 18:03:01 -0600 Subject: PCI: Cache offset of Resizable BAR capability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously most resizable BAR interfaces (pci_rebar_get_possible_sizes(), pci_rebar_set_size(), etc) as well as pci_restore_state() searched config space for a Resizable BAR capability. Most devices don't have such a capability, so this is wasted effort, especially for pci_restore_state(). Search for a Resizable BAR capability once at enumeration-time and cache the offset so we don't have to search every time we need it. No functional change intended. Link: https://lore.kernel.org/r/20250215000301.175097-3-helgaas@kernel.org Signed-off-by: Bjorn Helgaas Reviewed-by: Ilpo Järvinen --- drivers/pci/pci.c | 9 +++++++-- drivers/pci/pci.h | 1 + drivers/pci/probe.c | 1 + include/linux/pci.h | 1 + 4 files changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 869d204a70a3..cda4d9af50b5 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1871,7 +1871,7 @@ static void pci_restore_rebar_state(struct pci_dev *pdev) unsigned int pos, nbars, i; u32 ctrl; - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR); + pos = pdev->rebar_cap; if (!pos) return; @@ -3718,6 +3718,11 @@ void pci_acs_init(struct pci_dev *dev) pci_enable_acs(dev); } +void pci_rebar_init(struct pci_dev *pdev) +{ + pdev->rebar_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR); +} + /** * pci_rebar_find_pos - find position of resize ctrl reg for BAR * @pdev: PCI device @@ -3732,7 +3737,7 @@ static int pci_rebar_find_pos(struct pci_dev *pdev, int bar) unsigned int pos, nbars, i; u32 ctrl; - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR); + pos = pdev->rebar_cap; if (!pos) return -ENOTSUPP; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 01e51db8d285..d7b46ddfd6d2 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -799,6 +799,7 @@ static inline int acpi_get_rc_resources(struct device *dev, const char *hid, } #endif +void pci_rebar_init(struct pci_dev *pdev); int pci_rebar_get_current_size(struct pci_dev *pdev, int bar); int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size); static inline u64 pci_rebar_size_to_bytes(int size) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 0b013b196d00..5f04b8d9c736 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2566,6 +2566,7 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_rcec_init(dev); /* Root Complex Event Collector */ pci_doe_init(dev); /* Data Object Exchange */ pci_tph_init(dev); /* TLP Processing Hints */ + pci_rebar_init(dev); /* Resizable BAR */ pcie_report_downtraining(dev); pci_init_reset_methods(dev); diff --git a/include/linux/pci.h b/include/linux/pci.h index 47b31ad724fa..9e5bbd996c83 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -353,6 +353,7 @@ struct pci_dev { struct pci_dev *rcec; /* Associated RCEC device */ #endif u32 devcap; /* PCIe Device Capabilities */ + u16 rebar_cap; /* Resizable BAR capability offset */ u8 pcie_cap; /* PCIe capability offset */ u8 msi_cap; /* MSI capability offset */ u8 msix_cap; /* MSI-X capability offset */ -- cgit v1.2.3-59-g8ed1b From 804443c1f27883926de94c849d91f5b7d7d696e9 Mon Sep 17 00:00:00 2001 From: Ma Ke Date: Tue, 25 Feb 2025 10:14:40 +0800 Subject: PCI: Fix reference leak in pci_register_host_bridge() If device_register() fails, call put_device() to give up the reference to avoid a memory leak, per the comment at device_register(). Found by code review. Link: https://lore.kernel.org/r/20250225021440.3130264-1-make24@iscas.ac.cn Fixes: 37d6a0a6f470 ("PCI: Add pci_register_host_bridge() interface") Signed-off-by: Ma Ke [bhelgaas: squash Dan Carpenter's double free fix from https://lore.kernel.org/r/db806a6c-a91b-4e5a-a84b-6b7e01bdac85@stanley.mountain] Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org --- drivers/pci/probe.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5f04b8d9c736..dc37a3c0a977 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -953,6 +953,7 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) resource_size_t offset, next_offset; LIST_HEAD(resources); struct resource *res, *next_res; + bool bus_registered = false; char addr[64], *fmt; const char *name; int err; @@ -1017,6 +1018,7 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) name = dev_name(&bus->dev); err = device_register(&bus->dev); + bus_registered = true; if (err) goto unregister; @@ -1103,12 +1105,15 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) unregister: put_device(&bridge->dev); device_del(&bridge->dev); - free: #ifdef CONFIG_PCI_DOMAINS_GENERIC pci_bus_release_domain_nr(parent, bus->domain_nr); #endif - kfree(bus); + if (bus_registered) + put_device(&bus->dev); + else + kfree(bus); + return err; } -- cgit v1.2.3-59-g8ed1b From 1f2768b6a3ee77a295106e3a5d68458064923ede Mon Sep 17 00:00:00 2001 From: Ma Ke Date: Sun, 2 Feb 2025 14:23:57 +0800 Subject: PCI: Fix reference leak in pci_alloc_child_bus() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If device_register(&child->dev) fails, call put_device() to explicitly release child->dev, per the comment at device_register(). Found by code review. Link: https://lore.kernel.org/r/20250202062357.872971-1-make24@iscas.ac.cn Fixes: 4f535093cf8f ("PCI: Put pci_dev in device tree as early as possible") Signed-off-by: Ma Ke Signed-off-by: Bjorn Helgaas Reviewed-by: Ilpo Järvinen Cc: stable@vger.kernel.org --- drivers/pci/probe.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index dc37a3c0a977..088131a74d7d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1222,7 +1222,10 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, add_dev: pci_set_bus_msi_domain(child); ret = device_register(&child->dev); - WARN_ON(ret < 0); + if (WARN_ON(ret < 0)) { + put_device(&child->dev); + return NULL; + } pcibios_add_bus(child); -- cgit v1.2.3-59-g8ed1b From 6e8d06e5096c80cbf41313b4a204f43071ca42be Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 7 Mar 2025 11:46:34 +0300 Subject: PCI: Remove stray put_device() in pci_register_host_bridge() This put_device() was accidentally left over from when we changed the code from using device_register() to calling device_add(). Delete it. Link: https://lore.kernel.org/r/55b24870-89fb-4c91-b85d-744e35db53c2@stanley.mountain Fixes: 9885440b16b8 ("PCI: Fix pci_host_bridge struct device release/free handling") Signed-off-by: Dan Carpenter Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 088131a74d7d..1448f65cc191 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -996,10 +996,9 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) /* Temporarily move resources off the list */ list_splice_init(&bridge->windows, &resources); err = device_add(&bridge->dev); - if (err) { - put_device(&bridge->dev); + if (err) goto free; - } + bus->bridge = get_device(&bridge->dev); device_enable_async_suspend(bus->bridge); pci_set_bus_of_node(bus); -- cgit v1.2.3-59-g8ed1b From 57b0302240741e73fe51f88404b3866e0d2933ad Mon Sep 17 00:00:00 2001 From: Thippeswamy Havalige Date: Mon, 24 Feb 2025 21:20:22 +0530 Subject: PCI: xilinx-cpm: Fix IRQ domain leak in error path of probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IRQ domain allocated for the PCIe controller is not freed if resource_list_first_type() returns NULL, leading to a resource leak. This fix ensures properly cleaning up the allocated IRQ domain in the error path. Fixes: 49e427e6bdd1 ("Merge branch 'pci/host-probe-refactor'") Signed-off-by: Thippeswamy Havalige [kwilczynski: added missing Fixes: tag, refactored to use one of the goto labels] Signed-off-by: Krzysztof Wilczyński Link: https://lore.kernel.org/r/20250224155025.782179-2-thippeswamy.havalige@amd.com --- drivers/pci/controller/pcie-xilinx-cpm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-xilinx-cpm.c b/drivers/pci/controller/pcie-xilinx-cpm.c index 81e8bfae53d0..dc8ecdbee56c 100644 --- a/drivers/pci/controller/pcie-xilinx-cpm.c +++ b/drivers/pci/controller/pcie-xilinx-cpm.c @@ -583,15 +583,17 @@ static int xilinx_cpm_pcie_probe(struct platform_device *pdev) return err; bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS); - if (!bus) - return -ENODEV; + if (!bus) { + err = -ENODEV; + goto err_free_irq_domains; + } port->variant = of_device_get_match_data(dev); err = xilinx_cpm_pcie_parse_dt(port, bus->res); if (err) { dev_err(dev, "Parsing DT failed\n"); - goto err_parse_dt; + goto err_free_irq_domains; } xilinx_cpm_pcie_init_port(port); @@ -615,7 +617,7 @@ err_host_bridge: xilinx_cpm_free_interrupts(port); err_setup_irq: pci_ecam_free(port->cfg); -err_parse_dt: +err_free_irq_domains: xilinx_cpm_free_irq_domains(port); return err; } -- cgit v1.2.3-59-g8ed1b From ad3b7174d4d04b7e2ab81df5857c4da6b4bc1ade Mon Sep 17 00:00:00 2001 From: Thippeswamy Havalige Date: Mon, 24 Feb 2025 21:20:24 +0530 Subject: PCI: xilinx-cpm: Add support for Versal Net CPM5NC Root Port controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Versal Net ACAP (Adaptive Compute Acceleration Platform) devices incorporate the Coherency and PCIe Gen5 Module, specifically the Next-Generation Compact Module (CPM5NC). The integrated CPM5NC block, along with the built-in bridge, can function as a PCIe Root Port and supports the PCIe Gen5 protocol with data transfer rates of up to 32 GT/s, and is capable of supporting up to a x16 lane-width configuration. Bridge errors are managed using a specific interrupt line designed for CPM5N. The INTx interrupt support is not available. Currently in this commit platform specific bridge errors support is not added. Signed-off-by: Thippeswamy Havalige [kwilczynski: commit log, squashed patch to fix an if-statement condition to ensure that xilinx_cpm_pcie_init_port() does not run on the CPM5NC_HOST variant from https://lore.kernel.org/linux-pci/20250311072402.1049990-1-thippeswamy.havalige@amd.com] Signed-off-by: Krzysztof Wilczyński Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250224155025.782179-4-thippeswamy.havalige@amd.com --- drivers/pci/controller/pcie-xilinx-cpm.c | 40 +++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-xilinx-cpm.c b/drivers/pci/controller/pcie-xilinx-cpm.c index dc8ecdbee56c..d0ab187d917f 100644 --- a/drivers/pci/controller/pcie-xilinx-cpm.c +++ b/drivers/pci/controller/pcie-xilinx-cpm.c @@ -84,6 +84,7 @@ enum xilinx_cpm_version { CPM, CPM5, CPM5_HOST1, + CPM5NC_HOST, }; /** @@ -478,6 +479,9 @@ static void xilinx_cpm_pcie_init_port(struct xilinx_cpm_pcie *port) { const struct xilinx_cpm_variant *variant = port->variant; + if (variant->version == CPM5NC_HOST) + return; + if (cpm_pcie_link_up(port)) dev_info(port->dev, "PCIe Link is UP\n"); else @@ -578,9 +582,13 @@ static int xilinx_cpm_pcie_probe(struct platform_device *pdev) port->dev = dev; - err = xilinx_cpm_pcie_init_irq_domain(port); - if (err) - return err; + port->variant = of_device_get_match_data(dev); + + if (port->variant->version != CPM5NC_HOST) { + err = xilinx_cpm_pcie_init_irq_domain(port); + if (err) + return err; + } bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS); if (!bus) { @@ -588,8 +596,6 @@ static int xilinx_cpm_pcie_probe(struct platform_device *pdev) goto err_free_irq_domains; } - port->variant = of_device_get_match_data(dev); - err = xilinx_cpm_pcie_parse_dt(port, bus->res); if (err) { dev_err(dev, "Parsing DT failed\n"); @@ -598,10 +604,12 @@ static int xilinx_cpm_pcie_probe(struct platform_device *pdev) xilinx_cpm_pcie_init_port(port); - err = xilinx_cpm_setup_irq(port); - if (err) { - dev_err(dev, "Failed to set up interrupts\n"); - goto err_setup_irq; + if (port->variant->version != CPM5NC_HOST) { + err = xilinx_cpm_setup_irq(port); + if (err) { + dev_err(dev, "Failed to set up interrupts\n"); + goto err_setup_irq; + } } bridge->sysdata = port->cfg; @@ -614,11 +622,13 @@ static int xilinx_cpm_pcie_probe(struct platform_device *pdev) return 0; err_host_bridge: - xilinx_cpm_free_interrupts(port); + if (port->variant->version != CPM5NC_HOST) + xilinx_cpm_free_interrupts(port); err_setup_irq: pci_ecam_free(port->cfg); err_free_irq_domains: - xilinx_cpm_free_irq_domains(port); + if (port->variant->version != CPM5NC_HOST) + xilinx_cpm_free_irq_domains(port); return err; } @@ -641,6 +651,10 @@ static const struct xilinx_cpm_variant cpm5_host1 = { .ir_enable = XILINX_CPM_PCIE1_IR_ENABLE, }; +static const struct xilinx_cpm_variant cpm5n_host = { + .version = CPM5NC_HOST, +}; + static const struct of_device_id xilinx_cpm_pcie_of_match[] = { { .compatible = "xlnx,versal-cpm-host-1.00", @@ -654,6 +668,10 @@ static const struct of_device_id xilinx_cpm_pcie_of_match[] = { .compatible = "xlnx,versal-cpm5-host1", .data = &cpm5_host1, }, + { + .compatible = "xlnx,versal-cpm5nc-host", + .data = &cpm5n_host, + }, {} }; -- cgit v1.2.3-59-g8ed1b From f09d3937d400433080d17982bd1a540da53a156d Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Wed, 12 Mar 2025 09:06:34 +0100 Subject: PCI: Fix wrong length of devres array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The array for the iomapping cookie addresses has a length of PCI_STD_NUM_BARS. This constant, however, only describes standard BARs; while PCI can allow for additional, special BARs. The total number of PCI resources is described by constant PCI_NUM_RESOURCES, which is also used in, e.g., pci_select_bars(). Thus, the devres array has so far been too small. Change the length of the devres array to PCI_NUM_RESOURCES. Link: https://lore.kernel.org/r/20250312080634.13731-3-phasta@kernel.org Fixes: bbaff68bf4a4 ("PCI: Add managed partial-BAR request and map infrastructure") Signed-off-by: Philipp Stanner Signed-off-by: Bjorn Helgaas Signed-off-by: Krzysztof Wilczyński Cc: stable@vger.kernel.org # v6.11+ --- drivers/pci/devres.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/devres.c b/drivers/pci/devres.c index 3431a7df3e0d..728ed0c7f70a 100644 --- a/drivers/pci/devres.c +++ b/drivers/pci/devres.c @@ -40,7 +40,7 @@ * Legacy struct storing addresses to whole mapped BARs. */ struct pcim_iomap_devres { - void __iomem *table[PCI_STD_NUM_BARS]; + void __iomem *table[PCI_NUM_RESOURCES]; }; /* Used to restore the old INTx state on driver detach. */ -- cgit v1.2.3-59-g8ed1b From e3260237aaadc9799107ccb940c6688195c4518d Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 11 Mar 2025 07:27:32 +0100 Subject: PCI: pciehp: Avoid unnecessary device replacement check Hot-removal of nested PCI hotplug ports suffers from a long-standing race condition which can lead to a deadlock: A parent hotplug port acquires pci_lock_rescan_remove(), then waits for pciehp to unbind from a child hotplug port. Meanwhile that child hotplug port tries to acquire pci_lock_rescan_remove() as well in order to remove its own children. The deadlock only occurs if the parent acquires pci_lock_rescan_remove() first, not if the child happens to acquire it first. Several workarounds to avoid the issue have been proposed and discarded over the years, e.g.: https://lore.kernel.org/r/4c882e25194ba8282b78fe963fec8faae7cf23eb.1529173804.git.lukas@wunner.de/ A proper fix is being worked on, but needs more time as it is nontrivial and necessarily intrusive. Recent commit 9d573d19547b ("PCI: pciehp: Detect device replacement during system sleep") provokes more frequent occurrence of the deadlock when removing more than one Thunderbolt device during system sleep. The commit sought to detect device replacement, but also triggered on device removal. Differentiating reliably between replacement and removal is impossible because pci_get_dsn() returns 0 both if the device was removed, as well as if it was replaced with one lacking a Device Serial Number. Avoid the more frequent occurrence of the deadlock by checking whether the hotplug port itself was hot-removed. If so, there's no sense in checking whether its child device was replaced. This works because the ->resume_noirq() callback is invoked in top-down order for the entire hierarchy: A parent hotplug port detecting device replacement (or removal) marks all children as removed using pci_dev_set_disconnected() and a child hotplug port can then reliably detect being removed. Link: https://lore.kernel.org/r/02f166e24c87d6cde4085865cce9adfdfd969688.1741674172.git.lukas@wunner.de Fixes: 9d573d19547b ("PCI: pciehp: Detect device replacement during system sleep") Reported-by: Kenneth Crudup Closes: https://lore.kernel.org/r/83d9302a-f743-43e4-9de2-2dd66d91ab5b@panix.com/ Reported-by: Chia-Lin Kao (AceLan) Closes: https://lore.kernel.org/r/20240926125909.2362244-1-acelan.kao@canonical.com/ Tested-by: Kenneth Crudup Tested-by: Mika Westerberg Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg Reviewed-by: Kuppuswamy Sathyanarayanan Cc: stable@vger.kernel.org # v6.11+ --- drivers/pci/hotplug/pciehp_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index ff458e692fed..997841c69893 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -286,9 +286,12 @@ static int pciehp_suspend(struct pcie_device *dev) static bool pciehp_device_replaced(struct controller *ctrl) { - struct pci_dev *pdev __free(pci_dev_put); + struct pci_dev *pdev __free(pci_dev_put) = NULL; u32 reg; + if (pci_dev_is_disconnected(ctrl->pcie->port)) + return false; + pdev = pci_get_slot(ctrl->pcie->port->subordinate, PCI_DEVFN(0, 0)); if (!pdev) return true; -- cgit v1.2.3-59-g8ed1b From b1a7f99967fc0c052db8e65b449c7b32b1e9177f Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Wed, 12 Mar 2025 09:06:35 +0100 Subject: PCI: Check BAR index for validity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many functions in PCI use accessor macros such as pci_resource_len(), which take a BAR index. That index, however, is never checked for validity, potentially resulting in undefined behavior by overflowing the array pci_dev.resource in the macro pci_resource_n(). Since many users of those macros directly assign the accessed value to an unsigned integer, the macros cannot be changed easily anymore to return -EINVAL for invalid indexes. Consequently, the problem has to be mitigated in higher layers. Add pci_bar_index_valid(). Use it where appropriate. Link: https://lore.kernel.org/r/20250312080634.13731-4-phasta@kernel.org Closes: https://lore.kernel.org/all/adb53b1f-29e1-3d14-0e61-351fd2d3ff0d@linux.intel.com/ Reported-by: Bingbu Cao Signed-off-by: Philipp Stanner [kwilczynski: correct if-statement condition the pci_bar_index_is_valid() helper function uses, tidy up code comments] Signed-off-by: Krzysztof Wilczyński [bhelgaas: fix typo] Signed-off-by: Bjorn Helgaas --- drivers/pci/devres.c | 16 ++++++++++++++-- drivers/pci/iomap.c | 29 +++++++++++++++++++++-------- drivers/pci/pci.c | 6 ++++++ drivers/pci/pci.h | 16 ++++++++++++++++ 4 files changed, 57 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/devres.c b/drivers/pci/devres.c index 728ed0c7f70a..73047316889e 100644 --- a/drivers/pci/devres.c +++ b/drivers/pci/devres.c @@ -577,7 +577,7 @@ static int pcim_add_mapping_to_legacy_table(struct pci_dev *pdev, { void __iomem **legacy_iomap_table; - if (bar >= PCI_STD_NUM_BARS) + if (!pci_bar_index_is_valid(bar)) return -EINVAL; legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev); @@ -622,7 +622,7 @@ static void pcim_remove_bar_from_legacy_table(struct pci_dev *pdev, int bar) { void __iomem **legacy_iomap_table; - if (bar >= PCI_STD_NUM_BARS) + if (!pci_bar_index_is_valid(bar)) return; legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev); @@ -655,6 +655,9 @@ void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen) void __iomem *mapping; struct pcim_addr_devres *res; + if (!pci_bar_index_is_valid(bar)) + return NULL; + res = pcim_addr_devres_alloc(pdev); if (!res) return NULL; @@ -722,6 +725,9 @@ void __iomem *pcim_iomap_region(struct pci_dev *pdev, int bar, int ret; struct pcim_addr_devres *res; + if (!pci_bar_index_is_valid(bar)) + return IOMEM_ERR_PTR(-EINVAL); + res = pcim_addr_devres_alloc(pdev); if (!res) return IOMEM_ERR_PTR(-ENOMEM); @@ -823,6 +829,9 @@ static int _pcim_request_region(struct pci_dev *pdev, int bar, const char *name, int ret; struct pcim_addr_devres *res; + if (!pci_bar_index_is_valid(bar)) + return -EINVAL; + res = pcim_addr_devres_alloc(pdev); if (!res) return -ENOMEM; @@ -991,6 +1000,9 @@ void __iomem *pcim_iomap_range(struct pci_dev *pdev, int bar, void __iomem *mapping; struct pcim_addr_devres *res; + if (!pci_bar_index_is_valid(bar)) + return IOMEM_ERR_PTR(-EINVAL); + res = pcim_addr_devres_alloc(pdev); if (!res) return IOMEM_ERR_PTR(-ENOMEM); diff --git a/drivers/pci/iomap.c b/drivers/pci/iomap.c index 9fb7cacc15cd..fe706ed946df 100644 --- a/drivers/pci/iomap.c +++ b/drivers/pci/iomap.c @@ -9,6 +9,8 @@ #include +#include "pci.h" /* for pci_bar_index_is_valid() */ + /** * pci_iomap_range - create a virtual mapping cookie for a PCI BAR * @dev: PCI device that owns the BAR @@ -33,12 +35,19 @@ void __iomem *pci_iomap_range(struct pci_dev *dev, unsigned long offset, unsigned long maxlen) { - resource_size_t start = pci_resource_start(dev, bar); - resource_size_t len = pci_resource_len(dev, bar); - unsigned long flags = pci_resource_flags(dev, bar); + resource_size_t start, len; + unsigned long flags; + + if (!pci_bar_index_is_valid(bar)) + return NULL; + + start = pci_resource_start(dev, bar); + len = pci_resource_len(dev, bar); + flags = pci_resource_flags(dev, bar); if (len <= offset || !start) return NULL; + len -= offset; start += offset; if (maxlen && len > maxlen) @@ -77,16 +86,20 @@ void __iomem *pci_iomap_wc_range(struct pci_dev *dev, unsigned long offset, unsigned long maxlen) { - resource_size_t start = pci_resource_start(dev, bar); - resource_size_t len = pci_resource_len(dev, bar); - unsigned long flags = pci_resource_flags(dev, bar); + resource_size_t start, len; + unsigned long flags; - - if (flags & IORESOURCE_IO) + if (!pci_bar_index_is_valid(bar)) return NULL; + start = pci_resource_start(dev, bar); + len = pci_resource_len(dev, bar); + flags = pci_resource_flags(dev, bar); + if (len <= offset || !start) return NULL; + if (flags & IORESOURCE_IO) + return NULL; len -= offset; start += offset; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 869d204a70a3..da82d734d09c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3921,6 +3921,9 @@ EXPORT_SYMBOL(pci_enable_atomic_ops_to_root); */ void pci_release_region(struct pci_dev *pdev, int bar) { + if (!pci_bar_index_is_valid(bar)) + return; + /* * This is done for backwards compatibility, because the old PCI devres * API had a mode in which the function became managed if it had been @@ -3965,6 +3968,9 @@ EXPORT_SYMBOL(pci_release_region); static int __pci_request_region(struct pci_dev *pdev, int bar, const char *name, int exclusive) { + if (!pci_bar_index_is_valid(bar)) + return -EINVAL; + if (pci_is_managed(pdev)) { if (exclusive == IORESOURCE_EXCLUSIVE) return pcim_request_region_exclusive(pdev, bar, name); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 01e51db8d285..d22755de688b 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -167,6 +167,22 @@ static inline void pci_wakeup_event(struct pci_dev *dev) pm_wakeup_event(&dev->dev, 100); } +/** + * pci_bar_index_is_valid - Check whether a BAR index is within valid range + * @bar: BAR index + * + * Protects against overflowing &struct pci_dev.resource array. + * + * Return: true for valid index, false otherwise. + */ +static inline bool pci_bar_index_is_valid(int bar) +{ + if (bar >= 0 && bar < PCI_NUM_RESOURCES) + return true; + + return false; +} + static inline bool pci_has_subordinate(struct pci_dev *pci_dev) { return !!(pci_dev->subordinate); -- cgit v1.2.3-59-g8ed1b From baaef0a274cfb75f9b50eab3ef93205e604f662c Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Tue, 25 Feb 2025 20:02:50 +0900 Subject: misc: pci_endpoint_test: Fix 'irq_type' to convey the correct type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two variables that indicate the interrupt type to be used in the next test execution, "irq_type" as global and "test->irq_type". The global is referenced from pci_endpoint_test_get_irq() to preserve the current type for ioctl(PCITEST_GET_IRQTYPE). The type set in this function isn't reflected in the global "irq_type", so ioctl(PCITEST_GET_IRQTYPE) returns the previous type. As a result, the wrong type is displayed in old version of "pcitest" as follows: - Result of running "pcitest -i 0" SET IRQ TYPE TO LEGACY: OKAY - Result of running "pcitest -I" GET IRQ TYPE: MSI Whereas running the new version of "pcitest" in kselftest results in an error as follows: # RUN pci_ep_basic.LEGACY_IRQ_TEST ... # pci_endpoint_test.c:104:LEGACY_IRQ_TEST:Expected 0 (0) == ret (1) # pci_endpoint_test.c:104:LEGACY_IRQ_TEST:Can't get Legacy IRQ type Fix this issue by propagating the current type to the global "irq_type". Fixes: b2ba9225e031 ("misc: pci_endpoint_test: Avoid using module parameter to determine irqtype") Signed-off-by: Kunihiko Hayashi [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński Reviewed-by: Niklas Cassel Reviewed-by: Manivannan Sadhasivam Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20250225110252.28866-5-hayashi.kunihiko@socionext.com --- drivers/misc/pci_endpoint_test.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index acf3d8dab131..896392c428de 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -833,6 +833,7 @@ static int pci_endpoint_test_set_irq(struct pci_endpoint_test *test, return ret; } + irq_type = test->irq_type; return 0; } -- cgit v1.2.3-59-g8ed1b From a402006d48a9c4f94156c01c1293a426c3af7b4b Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Tue, 25 Feb 2025 20:02:51 +0900 Subject: misc: pci_endpoint_test: Remove global 'irq_type' and 'no_msi' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The global variable "irq_type" preserves the current value of ioctl(GET_IRQTYPE). However, all tests that use interrupts first call ioctl(SET_IRQTYPE) to set "test->irq_type", then write the value of test->irq_type into the register pointed by test_reg_bar, and request the interrupt to the endpoint. The endpoint function driver, pci-epf-test, refers to the register, and determine which type of interrupt to raise. The global variable "irq_type" is never used in the actual test, so remove the variable and replace it with "test->irq_type". Also, for the same reason, the variable "no_msi" can be removed. Initially, "test->irq_type" has IRQ_TYPE_UNDEFINED, and the ioctl(GET_IRQTYPE) before calling ioctl(SET_IRQTYPE) will return an error. Suggested-by: Niklas Cassel Suggested-by: Manivannan Sadhasivam Signed-off-by: Kunihiko Hayashi [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński Reviewed-by: Manivannan Sadhasivam Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20250225110252.28866-6-hayashi.kunihiko@socionext.com --- drivers/misc/pci_endpoint_test.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 896392c428de..326e8e467c42 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -96,14 +96,6 @@ static DEFINE_IDA(pci_endpoint_test_ida); #define to_endpoint_test(priv) container_of((priv), struct pci_endpoint_test, \ miscdev) -static bool no_msi; -module_param(no_msi, bool, 0444); -MODULE_PARM_DESC(no_msi, "Disable MSI interrupt in pci_endpoint_test"); - -static int irq_type = IRQ_TYPE_MSI; -module_param(irq_type, int, 0444); -MODULE_PARM_DESC(irq_type, "IRQ mode selection in pci_endpoint_test (0 - Legacy, 1 - MSI, 2 - MSI-X)"); - enum pci_barno { BAR_0, BAR_1, @@ -833,7 +825,6 @@ static int pci_endpoint_test_set_irq(struct pci_endpoint_test *test, return ret; } - irq_type = test->irq_type; return 0; } @@ -882,7 +873,7 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, ret = pci_endpoint_test_set_irq(test, arg); break; case PCITEST_GET_IRQTYPE: - ret = irq_type; + ret = test->irq_type; break; case PCITEST_CLEAR_IRQ: ret = pci_endpoint_test_clear_irq(test); @@ -939,15 +930,12 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, test->pdev = pdev; test->irq_type = IRQ_TYPE_UNDEFINED; - if (no_msi) - irq_type = IRQ_TYPE_INTX; - data = (struct pci_endpoint_test_data *)ent->driver_data; if (data) { test_reg_bar = data->test_reg_bar; test->test_reg_bar = test_reg_bar; test->alignment = data->alignment; - irq_type = data->irq_type; + test->irq_type = data->irq_type; } init_completion(&test->irq_raised); @@ -969,7 +957,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, pci_set_master(pdev); - ret = pci_endpoint_test_alloc_irq_vectors(test, irq_type); + ret = pci_endpoint_test_alloc_irq_vectors(test, test->irq_type); if (ret) goto err_disable_irq; -- cgit v1.2.3-59-g8ed1b From e1ec81ebfffbaf6b28be91ac179a5cc90d79436b Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Tue, 25 Feb 2025 20:02:52 +0900 Subject: misc: pci_endpoint_test: Do not use managed IRQ functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pci_endpoint_test_request_irq() and pci_endpoint_test_release_irq() are called repeatedly by the users through pci_endpoint_test_set_irq(). So using the managed version of IRQ functions within these functions has no effect. Suggested-by: Manivannan Sadhasivam Reviewed-by: Manivannan Sadhasivam Signed-off-by: Kunihiko Hayashi Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20250225110252.28866-7-hayashi.kunihiko@socionext.com Signed-off-by: Krzysztof Wilczyński --- drivers/misc/pci_endpoint_test.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 326e8e467c42..7a14114488a0 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -208,10 +208,9 @@ static void pci_endpoint_test_release_irq(struct pci_endpoint_test *test) { int i; struct pci_dev *pdev = test->pdev; - struct device *dev = &pdev->dev; for (i = 0; i < test->num_irqs; i++) - devm_free_irq(dev, pci_irq_vector(pdev, i), test); + free_irq(pci_irq_vector(pdev, i), test); test->num_irqs = 0; } @@ -224,9 +223,9 @@ static int pci_endpoint_test_request_irq(struct pci_endpoint_test *test) struct device *dev = &pdev->dev; for (i = 0; i < test->num_irqs; i++) { - ret = devm_request_irq(dev, pci_irq_vector(pdev, i), - pci_endpoint_test_irqhandler, - IRQF_SHARED, test->name, test); + ret = request_irq(pci_irq_vector(pdev, i), + pci_endpoint_test_irqhandler, IRQF_SHARED, + test->name, test); if (ret) goto fail; } -- cgit v1.2.3-59-g8ed1b From 64a7704ae16f5836ba74152553ae2a7338229a9d Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Mon, 10 Mar 2025 12:10:19 +0100 Subject: misc: pci_endpoint_test: Use IRQ_TYPE_* defines from UAPI header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the IRQ_TYPE_* defines from the UAPI header rather than duplicating these defines in the driver itself. No functional change. Signed-off-by: Niklas Cassel Signed-off-by: Krzysztof Wilczyński Link: https://lore.kernel.org/r/20250310111016.859445-11-cassel@kernel.org --- drivers/misc/pci_endpoint_test.c | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 7a14114488a0..abcf1d91de37 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -28,11 +28,6 @@ #define DRV_MODULE_NAME "pci-endpoint-test" -#define IRQ_TYPE_UNDEFINED -1 -#define IRQ_TYPE_INTX 0 -#define IRQ_TYPE_MSI 1 -#define IRQ_TYPE_MSIX 2 - #define PCI_ENDPOINT_TEST_MAGIC 0x0 #define PCI_ENDPOINT_TEST_COMMAND 0x4 @@ -158,7 +153,7 @@ static void pci_endpoint_test_free_irq_vectors(struct pci_endpoint_test *test) struct pci_dev *pdev = test->pdev; pci_free_irq_vectors(pdev); - test->irq_type = IRQ_TYPE_UNDEFINED; + test->irq_type = PCITEST_IRQ_TYPE_UNDEFINED; } static int pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test, @@ -169,7 +164,7 @@ static int pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test, struct device *dev = &pdev->dev; switch (type) { - case IRQ_TYPE_INTX: + case PCITEST_IRQ_TYPE_INTX: irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_INTX); if (irq < 0) { dev_err(dev, "Failed to get Legacy interrupt\n"); @@ -177,7 +172,7 @@ static int pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test, } break; - case IRQ_TYPE_MSI: + case PCITEST_IRQ_TYPE_MSI: irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI); if (irq < 0) { dev_err(dev, "Failed to get MSI interrupts\n"); @@ -185,7 +180,7 @@ static int pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test, } break; - case IRQ_TYPE_MSIX: + case PCITEST_IRQ_TYPE_MSIX: irq = pci_alloc_irq_vectors(pdev, 1, 2048, PCI_IRQ_MSIX); if (irq < 0) { dev_err(dev, "Failed to get MSI-X interrupts\n"); @@ -234,16 +229,16 @@ static int pci_endpoint_test_request_irq(struct pci_endpoint_test *test) fail: switch (test->irq_type) { - case IRQ_TYPE_INTX: + case PCITEST_IRQ_TYPE_INTX: dev_err(dev, "Failed to request IRQ %d for Legacy\n", pci_irq_vector(pdev, i)); break; - case IRQ_TYPE_MSI: + case PCITEST_IRQ_TYPE_MSI: dev_err(dev, "Failed to request IRQ %d for MSI %d\n", pci_irq_vector(pdev, i), i + 1); break; - case IRQ_TYPE_MSIX: + case PCITEST_IRQ_TYPE_MSIX: dev_err(dev, "Failed to request IRQ %d for MSI-X %d\n", pci_irq_vector(pdev, i), i + 1); @@ -409,7 +404,7 @@ static int pci_endpoint_test_intx_irq(struct pci_endpoint_test *test) u32 val; pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, - IRQ_TYPE_INTX); + PCITEST_IRQ_TYPE_INTX); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 0); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, COMMAND_RAISE_INTX_IRQ); @@ -429,7 +424,8 @@ static int pci_endpoint_test_msi_irq(struct pci_endpoint_test *test, int ret; pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, - msix ? IRQ_TYPE_MSIX : IRQ_TYPE_MSI); + msix ? PCITEST_IRQ_TYPE_MSIX : + PCITEST_IRQ_TYPE_MSI); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, msi_num); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, msix ? COMMAND_RAISE_MSIX_IRQ : @@ -505,7 +501,8 @@ static int pci_endpoint_test_copy(struct pci_endpoint_test *test, if (use_dma) flags |= FLAG_USE_DMA; - if (irq_type < IRQ_TYPE_INTX || irq_type > IRQ_TYPE_MSIX) { + if (irq_type < PCITEST_IRQ_TYPE_INTX || + irq_type > PCITEST_IRQ_TYPE_MSIX) { dev_err(dev, "Invalid IRQ type option\n"); return -EINVAL; } @@ -637,7 +634,8 @@ static int pci_endpoint_test_write(struct pci_endpoint_test *test, if (use_dma) flags |= FLAG_USE_DMA; - if (irq_type < IRQ_TYPE_INTX || irq_type > IRQ_TYPE_MSIX) { + if (irq_type < PCITEST_IRQ_TYPE_INTX || + irq_type > PCITEST_IRQ_TYPE_MSIX) { dev_err(dev, "Invalid IRQ type option\n"); return -EINVAL; } @@ -733,7 +731,8 @@ static int pci_endpoint_test_read(struct pci_endpoint_test *test, if (use_dma) flags |= FLAG_USE_DMA; - if (irq_type < IRQ_TYPE_INTX || irq_type > IRQ_TYPE_MSIX) { + if (irq_type < PCITEST_IRQ_TYPE_INTX || + irq_type > PCITEST_IRQ_TYPE_MSIX) { dev_err(dev, "Invalid IRQ type option\n"); return -EINVAL; } @@ -803,7 +802,8 @@ static int pci_endpoint_test_set_irq(struct pci_endpoint_test *test, struct device *dev = &pdev->dev; int ret; - if (req_irq_type < IRQ_TYPE_INTX || req_irq_type > IRQ_TYPE_MSIX) { + if (req_irq_type < PCITEST_IRQ_TYPE_INTX || + req_irq_type > PCITEST_IRQ_TYPE_MSIX) { dev_err(dev, "Invalid IRQ type option\n"); return -EINVAL; } @@ -927,7 +927,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, test->test_reg_bar = 0; test->alignment = 0; test->pdev = pdev; - test->irq_type = IRQ_TYPE_UNDEFINED; + test->irq_type = PCITEST_IRQ_TYPE_UNDEFINED; data = (struct pci_endpoint_test_data *)ent->driver_data; if (data) { @@ -1078,23 +1078,23 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) static const struct pci_endpoint_test_data default_data = { .test_reg_bar = BAR_0, .alignment = SZ_4K, - .irq_type = IRQ_TYPE_MSI, + .irq_type = PCITEST_IRQ_TYPE_MSI, }; static const struct pci_endpoint_test_data am654_data = { .test_reg_bar = BAR_2, .alignment = SZ_64K, - .irq_type = IRQ_TYPE_MSI, + .irq_type = PCITEST_IRQ_TYPE_MSI, }; static const struct pci_endpoint_test_data j721e_data = { .alignment = 256, - .irq_type = IRQ_TYPE_MSI, + .irq_type = PCITEST_IRQ_TYPE_MSI, }; static const struct pci_endpoint_test_data rk3588_data = { .alignment = SZ_64K, - .irq_type = IRQ_TYPE_MSI, + .irq_type = PCITEST_IRQ_TYPE_MSI, }; /* -- cgit v1.2.3-59-g8ed1b From 8189aa56dbed0bfb46b7b30d4d231f57ab17b3f4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 5 Mar 2025 18:00:07 +0300 Subject: PCI: dwc: ep: Return -ENOMEM for allocation failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the bitmap or memory allocations fail, then dw_pcie_ep_init_registers() will incorrectly return a success. Return -ENOMEM instead. Fixes: 869bc5253406 ("PCI: dwc: ep: Fix DBI access failure for drivers requiring refclk from host") Signed-off-by: Dan Carpenter [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński Reviewed-by: Krzysztof Wilczyński Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/36dcb6fc-f292-4dd5-bd45-a8c6f9dc3df7@stanley.mountain --- drivers/pci/controller/dwc/pcie-designware-ep.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 11ff292ca87d..2212609632c4 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -774,6 +774,7 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) if (ret) return ret; + ret = -ENOMEM; if (!ep->ib_window_map) { ep->ib_window_map = devm_bitmap_zalloc(dev, pci->num_ib_windows, GFP_KERNEL); -- cgit v1.2.3-59-g8ed1b From e3d6957f177936281278d259029a1a2184ba50d4 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Mon, 10 Mar 2025 10:48:27 +0100 Subject: PCI: dwc: ep: Add dw_pcie_ep_hide_ext_capability() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add dw_pcie_ep_hide_ext_capability() which can be used by an endpoint controller driver to hide a capability. This can be useful to hide a capability that is buggy, such that the host side does not try to enable the buggy capability. Suggested-by: Manivannan Sadhasivam Signed-off-by: Niklas Cassel Signed-off-by: Krzysztof Wilczyński Link: https://lore.kernel.org/r/20250310094826.842681-5-cassel@kernel.org --- drivers/pci/controller/dwc/pcie-designware-ep.c | 39 +++++++++++++++++++++++++ drivers/pci/controller/dwc/pcie-designware.h | 7 +++++ 2 files changed, 46 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 2212609632c4..5a6174e107c2 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -102,6 +102,45 @@ static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap) return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap); } +/** + * dw_pcie_ep_hide_ext_capability - Hide a capability from the linked list + * @pci: DWC PCI device + * @prev_cap: Capability preceding the capability that should be hidden + * @cap: Capability that should be hidden + * + * Return: 0 if success, errno otherwise. + */ +int dw_pcie_ep_hide_ext_capability(struct dw_pcie *pci, u8 prev_cap, u8 cap) +{ + u16 prev_cap_offset, cap_offset; + u32 prev_cap_header, cap_header; + + prev_cap_offset = dw_pcie_find_ext_capability(pci, prev_cap); + if (!prev_cap_offset) + return -EINVAL; + + prev_cap_header = dw_pcie_readl_dbi(pci, prev_cap_offset); + cap_offset = PCI_EXT_CAP_NEXT(prev_cap_header); + cap_header = dw_pcie_readl_dbi(pci, cap_offset); + + /* cap must immediately follow prev_cap. */ + if (PCI_EXT_CAP_ID(cap_header) != cap) + return -EINVAL; + + /* Clear next ptr. */ + prev_cap_header &= ~GENMASK(31, 20); + + /* Set next ptr to next ptr of cap. */ + prev_cap_header |= cap_header & GENMASK(31, 20); + + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writel_dbi(pci, prev_cap_offset, prev_cap_header); + dw_pcie_dbi_ro_wr_dis(pci); + + return 0; +} +EXPORT_SYMBOL_GPL(dw_pcie_ep_hide_ext_capability); + static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_header *hdr) { diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index a03b3799fb27..2d1de81d47b6 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -781,6 +781,7 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, int dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, u8 func_no, u16 interrupt_num); void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar); +int dw_pcie_ep_hide_ext_capability(struct dw_pcie *pci, u8 prev_cap, u8 cap); struct dw_pcie_ep_func * dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no); #else @@ -838,6 +839,12 @@ static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) { } +static inline int dw_pcie_ep_hide_ext_capability(struct dw_pcie *pci, + u8 prev_cap, u8 cap) +{ + return 0; +} + static inline struct dw_pcie_ep_func * dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no) { -- cgit v1.2.3-59-g8ed1b From 1f5a69f1b3132054d8d82b8d7546d0af6a2ed4f6 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Mon, 10 Mar 2025 10:48:28 +0100 Subject: PCI: dw-rockchip: Hide broken ATS capability for RK3588 running in EP mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running the RK3588 in Endpoint mode, with an Intel host with IOMMU enabled, the host side prints: DMAR: VT-d detected Invalidation Time-out Error: SID 0 When running the RK3588 in Endpoint mode, with an AMD host with IOMMU enabled, the host side prints: iommu ivhd0: AMD-Vi: Event logged [IOTLB_INV_TIMEOUT device=63:00.0 address=0x42b5b01a0] Rockchip has confirmed that the ATS support for RK3588 only works when running the PCIe controller in Root Complex (RC) mode, see: https://lore.kernel.org/linux-pci/93cdce39-1ae6-4939-a3fc-db10be7564e5@rock-chips.com Usually, to handle these issues, we add a quirk for the PCI vendor and device ID in drivers/pci/quirks.c with quirk_no_ats(). That is because we cannot usually modify the capabilities on the EP side. In this case, we can modify the capabilities on the EP side. Thus, hide the broken ATS capability on RK3588 when running in EP mode. That way, we don't need any quirk on the host side, and we see no errors on the host side, and we can run pci_endpoint_test successfully, with the IOMMU enabled on the host side. Acked-by: Shawn Lin Signed-off-by: Niklas Cassel [kwilczynski: commit log, tidy up code comments and error message] Signed-off-by: Krzysztof Wilczyński Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250310094826.842681-6-cassel@kernel.org --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 29 +++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 93698abff4d9..3aca62f3a864 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -240,6 +240,34 @@ static const struct dw_pcie_host_ops rockchip_pcie_host_ops = { .init = rockchip_pcie_host_init, }; +/* + * ATS does not work on RK3588 when running in EP mode. + * + * After the host has enabled ATS on the EP side, it will send an IOTLB + * invalidation request to the EP side. However, the RK3588 will never send + * a completion back and eventually the host will print an IOTLB_INV_TIMEOUT + * error, and the EP will not be operational. If we hide the ATS capability, + * things work as expected. + */ +static void rockchip_pcie_ep_hide_broken_ats_cap_rk3588(struct dw_pcie_ep *ep) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct device *dev = pci->dev; + + /* Only hide the ATS capability for RK3588 running in EP mode. */ + if (!of_device_is_compatible(dev->of_node, "rockchip,rk3588-pcie-ep")) + return; + + if (dw_pcie_ep_hide_ext_capability(pci, PCI_EXT_CAP_ID_SECPCI, + PCI_EXT_CAP_ID_ATS)) + dev_err(dev, "failed to hide ATS capability\n"); +} + +static void rockchip_pcie_ep_pre_init(struct dw_pcie_ep *ep) +{ + rockchip_pcie_ep_hide_broken_ats_cap_rk3588(ep); +} + static void rockchip_pcie_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); @@ -312,6 +340,7 @@ rockchip_pcie_get_features(struct dw_pcie_ep *ep) static const struct dw_pcie_ep_ops rockchip_pcie_ep_ops = { .init = rockchip_pcie_ep_init, + .pre_init = rockchip_pcie_ep_pre_init, .raise_irq = rockchip_pcie_raise_irq, .get_features = rockchip_pcie_get_features, }; -- cgit v1.2.3-59-g8ed1b From 81d1d214e171c9c4b283f9aeb9a97c4a88d0fcf6 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Wed, 26 Feb 2025 10:42:56 +0800 Subject: PCI: imx6: Identify controller via 'linux,pci-domain', not address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of testing the controller register address to distinguish controller 1 from controller 0 on i.MX8MQ platforms, use the PCI domain number, which comes from the devicetree 'linux,pci-domain' property. All relevant devicetrees should already supply 'linux,pci-domain', which was added by c0b70f05c87f ("arm64: dts: imx8mq: use_dt_domains for pci node"). Instead of being set directly in imx_pcie_probe(), pci->dbi_base will be set by the DWC core in dw_pcie_get_resources(). No functional changes intended. Signed-off-by: Richard Zhu Signed-off-by: Krzysztof Wilczyński [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Link: https://lore.kernel.org/r/20250226024256.1678103-3-hongxing.zhu@nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 90ace941090f..d165a7e36a35 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -41,7 +41,6 @@ #define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE BIT(11) #define IMX8MQ_GPR_PCIE_VREG_BYPASS BIT(12) #define IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE GENMASK(11, 8) -#define IMX8MQ_PCIE2_BASE_ADDR 0x33c00000 #define IMX95_PCIE_PHY_GEN_CTRL 0x0 #define IMX95_PCIE_REF_USE_PAD BIT(17) @@ -1474,9 +1473,8 @@ static int imx_pcie_probe(struct platform_device *pdev) struct dw_pcie *pci; struct imx_pcie *imx_pcie; struct device_node *np; - struct resource *dbi_base; struct device_node *node = dev->of_node; - int i, ret, req_cnt; + int i, ret, req_cnt, domain; u16 val; imx_pcie = devm_kzalloc(dev, sizeof(*imx_pcie), GFP_KERNEL); @@ -1515,10 +1513,6 @@ static int imx_pcie_probe(struct platform_device *pdev) return PTR_ERR(imx_pcie->phy_base); } - pci->dbi_base = devm_platform_get_and_ioremap_resource(pdev, 0, &dbi_base); - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - /* Fetch GPIOs */ imx_pcie->reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(imx_pcie->reset_gpiod)) @@ -1565,8 +1559,11 @@ static int imx_pcie_probe(struct platform_device *pdev) switch (imx_pcie->drvdata->variant) { case IMX8MQ: case IMX8MQ_EP: - if (dbi_base->start == IMX8MQ_PCIE2_BASE_ADDR) - imx_pcie->controller_id = 1; + domain = of_get_pci_domain_nr(node); + if (domain < 0 || domain > 1) + return dev_err_probe(dev, -ENODEV, "no \"linux,pci-domain\" property in devicetree\n"); + + imx_pcie->controller_id = domain; break; default: break; -- cgit v1.2.3-59-g8ed1b From f6a1fdfc78e203d2f7ccb9b34c00e5f0ac3d3a74 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Wed, 26 Feb 2025 10:56:28 +0800 Subject: PCI: imx6: Use devm_clk_bulk_get_all() to fetch clocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use devm_clk_bulk_get_all() helper to simplify clock handle code. No functional changes intended. Signed-off-by: Richard Zhu [kwilczynski: commit log, refactor to use dev_err_probe()] Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Link: https://lore.kernel.org/r/20250226025628.1681206-1-hongxing.zhu@nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 75 +++++++---------------------------- 1 file changed, 14 insertions(+), 61 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index d165a7e36a35..1467694952eb 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -108,7 +108,6 @@ enum imx_pcie_variants { #define imx_check_flag(pci, val) (pci->drvdata->flags & val) -#define IMX_PCIE_MAX_CLKS 6 #define IMX_PCIE_MAX_INSTANCES 2 struct imx_pcie; @@ -119,9 +118,6 @@ struct imx_pcie_drvdata { u32 flags; int dbi_length; const char *gpr; - const char * const *clk_names; - const u32 clks_cnt; - const u32 clks_optional_cnt; const u32 ltssm_off; const u32 ltssm_mask; const u32 mode_off[IMX_PCIE_MAX_INSTANCES]; @@ -136,7 +132,8 @@ struct imx_pcie_drvdata { struct imx_pcie { struct dw_pcie *pci; struct gpio_desc *reset_gpiod; - struct clk_bulk_data clks[IMX_PCIE_MAX_CLKS]; + struct clk_bulk_data *clks; + int num_clks; struct regmap *iomuxc_gpr; u16 msi_ctrl; u32 controller_id; @@ -469,13 +466,14 @@ static int imx_setup_phy_mpll(struct imx_pcie *imx_pcie) int mult, div; u16 val; int i; + struct clk_bulk_data *clks = imx_pcie->clks; if (!(imx_pcie->drvdata->flags & IMX_PCIE_FLAG_IMX_PHY)) return 0; - for (i = 0; i < imx_pcie->drvdata->clks_cnt; i++) - if (strncmp(imx_pcie->clks[i].id, "pcie_phy", 8) == 0) - phy_rate = clk_get_rate(imx_pcie->clks[i].clk); + for (i = 0; i < imx_pcie->num_clks; i++) + if (strncmp(clks[i].id, "pcie_phy", 8) == 0) + phy_rate = clk_get_rate(clks[i].clk); switch (phy_rate) { case 125000000: @@ -667,7 +665,7 @@ static int imx_pcie_clk_enable(struct imx_pcie *imx_pcie) struct device *dev = pci->dev; int ret; - ret = clk_bulk_prepare_enable(imx_pcie->drvdata->clks_cnt, imx_pcie->clks); + ret = clk_bulk_prepare_enable(imx_pcie->num_clks, imx_pcie->clks); if (ret) return ret; @@ -684,7 +682,7 @@ static int imx_pcie_clk_enable(struct imx_pcie *imx_pcie) return 0; err_ref_clk: - clk_bulk_disable_unprepare(imx_pcie->drvdata->clks_cnt, imx_pcie->clks); + clk_bulk_disable_unprepare(imx_pcie->num_clks, imx_pcie->clks); return ret; } @@ -693,7 +691,7 @@ static void imx_pcie_clk_disable(struct imx_pcie *imx_pcie) { if (imx_pcie->drvdata->enable_ref_clk) imx_pcie->drvdata->enable_ref_clk(imx_pcie, false); - clk_bulk_disable_unprepare(imx_pcie->drvdata->clks_cnt, imx_pcie->clks); + clk_bulk_disable_unprepare(imx_pcie->num_clks, imx_pcie->clks); } static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) @@ -1474,7 +1472,7 @@ static int imx_pcie_probe(struct platform_device *pdev) struct imx_pcie *imx_pcie; struct device_node *np; struct device_node *node = dev->of_node; - int i, ret, req_cnt, domain; + int ret, domain; u16 val; imx_pcie = devm_kzalloc(dev, sizeof(*imx_pcie), GFP_KERNEL); @@ -1520,20 +1518,11 @@ static int imx_pcie_probe(struct platform_device *pdev) "unable to get reset gpio\n"); gpiod_set_consumer_name(imx_pcie->reset_gpiod, "PCIe reset"); - if (imx_pcie->drvdata->clks_cnt >= IMX_PCIE_MAX_CLKS) - return dev_err_probe(dev, -ENOMEM, "clks_cnt is too big\n"); - - for (i = 0; i < imx_pcie->drvdata->clks_cnt; i++) - imx_pcie->clks[i].id = imx_pcie->drvdata->clk_names[i]; - /* Fetch clocks */ - req_cnt = imx_pcie->drvdata->clks_cnt - imx_pcie->drvdata->clks_optional_cnt; - ret = devm_clk_bulk_get(dev, req_cnt, imx_pcie->clks); - if (ret) - return ret; - imx_pcie->clks[req_cnt].clk = devm_clk_get_optional(dev, "ref"); - if (IS_ERR(imx_pcie->clks[req_cnt].clk)) - return PTR_ERR(imx_pcie->clks[req_cnt].clk); + imx_pcie->num_clks = devm_clk_bulk_get_all(dev, &imx_pcie->clks); + if (imx_pcie->num_clks < 0) + return dev_err_probe(dev, imx_pcie->num_clks, + "failed to get clocks\n"); if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_PHYDRV)) { imx_pcie->phy = devm_phy_get(dev, "pcie-phy"); @@ -1672,13 +1661,6 @@ static void imx_pcie_shutdown(struct platform_device *pdev) imx_pcie_assert_core_reset(imx_pcie); } -static const char * const imx6q_clks[] = {"pcie_bus", "pcie", "pcie_phy"}; -static const char * const imx8mm_clks[] = {"pcie_bus", "pcie", "pcie_aux"}; -static const char * const imx8mq_clks[] = {"pcie_bus", "pcie", "pcie_phy", "pcie_aux"}; -static const char * const imx6sx_clks[] = {"pcie_bus", "pcie", "pcie_phy", "pcie_inbound_axi"}; -static const char * const imx8q_clks[] = {"mstr", "slv", "dbi"}; -static const char * const imx95_clks[] = {"pcie_bus", "pcie", "pcie_phy", "pcie_aux", "ref"}; - static const struct imx_pcie_drvdata drvdata[] = { [IMX6Q] = { .variant = IMX6Q, @@ -1688,8 +1670,6 @@ static const struct imx_pcie_drvdata drvdata[] = { IMX_PCIE_FLAG_SUPPORTS_SUSPEND, .dbi_length = 0x200, .gpr = "fsl,imx6q-iomuxc-gpr", - .clk_names = imx6q_clks, - .clks_cnt = ARRAY_SIZE(imx6q_clks), .ltssm_off = IOMUXC_GPR12, .ltssm_mask = IMX6Q_GPR12_PCIE_CTL_2, .mode_off[0] = IOMUXC_GPR12, @@ -1704,8 +1684,6 @@ static const struct imx_pcie_drvdata drvdata[] = { IMX_PCIE_FLAG_IMX_SPEED_CHANGE | IMX_PCIE_FLAG_SUPPORTS_SUSPEND, .gpr = "fsl,imx6q-iomuxc-gpr", - .clk_names = imx6sx_clks, - .clks_cnt = ARRAY_SIZE(imx6sx_clks), .ltssm_off = IOMUXC_GPR12, .ltssm_mask = IMX6Q_GPR12_PCIE_CTL_2, .mode_off[0] = IOMUXC_GPR12, @@ -1722,8 +1700,6 @@ static const struct imx_pcie_drvdata drvdata[] = { IMX_PCIE_FLAG_SUPPORTS_SUSPEND, .dbi_length = 0x200, .gpr = "fsl,imx6q-iomuxc-gpr", - .clk_names = imx6q_clks, - .clks_cnt = ARRAY_SIZE(imx6q_clks), .ltssm_off = IOMUXC_GPR12, .ltssm_mask = IMX6Q_GPR12_PCIE_CTL_2, .mode_off[0] = IOMUXC_GPR12, @@ -1739,8 +1715,6 @@ static const struct imx_pcie_drvdata drvdata[] = { IMX_PCIE_FLAG_HAS_APP_RESET | IMX_PCIE_FLAG_HAS_PHY_RESET, .gpr = "fsl,imx7d-iomuxc-gpr", - .clk_names = imx6q_clks, - .clks_cnt = ARRAY_SIZE(imx6q_clks), .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .enable_ref_clk = imx7d_pcie_enable_ref_clk, @@ -1752,8 +1726,6 @@ static const struct imx_pcie_drvdata drvdata[] = { IMX_PCIE_FLAG_HAS_PHY_RESET | IMX_PCIE_FLAG_SUPPORTS_SUSPEND, .gpr = "fsl,imx8mq-iomuxc-gpr", - .clk_names = imx8mq_clks, - .clks_cnt = ARRAY_SIZE(imx8mq_clks), .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .mode_off[1] = IOMUXC_GPR12, @@ -1767,8 +1739,6 @@ static const struct imx_pcie_drvdata drvdata[] = { IMX_PCIE_FLAG_HAS_PHYDRV | IMX_PCIE_FLAG_HAS_APP_RESET, .gpr = "fsl,imx8mm-iomuxc-gpr", - .clk_names = imx8mm_clks, - .clks_cnt = ARRAY_SIZE(imx8mm_clks), .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .enable_ref_clk = imx8mm_pcie_enable_ref_clk, @@ -1779,8 +1749,6 @@ static const struct imx_pcie_drvdata drvdata[] = { IMX_PCIE_FLAG_HAS_PHYDRV | IMX_PCIE_FLAG_HAS_APP_RESET, .gpr = "fsl,imx8mp-iomuxc-gpr", - .clk_names = imx8mm_clks, - .clks_cnt = ARRAY_SIZE(imx8mm_clks), .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .enable_ref_clk = imx8mm_pcie_enable_ref_clk, @@ -1790,17 +1758,12 @@ static const struct imx_pcie_drvdata drvdata[] = { .flags = IMX_PCIE_FLAG_HAS_PHYDRV | IMX_PCIE_FLAG_CPU_ADDR_FIXUP | IMX_PCIE_FLAG_SUPPORTS_SUSPEND, - .clk_names = imx8q_clks, - .clks_cnt = ARRAY_SIZE(imx8q_clks), }, [IMX95] = { .variant = IMX95, .flags = IMX_PCIE_FLAG_HAS_SERDES | IMX_PCIE_FLAG_HAS_LUT | IMX_PCIE_FLAG_SUPPORTS_SUSPEND, - .clk_names = imx95_clks, - .clks_cnt = ARRAY_SIZE(imx95_clks), - .clks_optional_cnt = 1, .ltssm_off = IMX95_PE0_GEN_CTRL_3, .ltssm_mask = IMX95_PCIE_LTSSM_EN, .mode_off[0] = IMX95_PE0_GEN_CTRL_1, @@ -1813,8 +1776,6 @@ static const struct imx_pcie_drvdata drvdata[] = { IMX_PCIE_FLAG_HAS_PHY_RESET, .mode = DW_PCIE_EP_TYPE, .gpr = "fsl,imx8mq-iomuxc-gpr", - .clk_names = imx8mq_clks, - .clks_cnt = ARRAY_SIZE(imx8mq_clks), .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .mode_off[1] = IOMUXC_GPR12, @@ -1829,8 +1790,6 @@ static const struct imx_pcie_drvdata drvdata[] = { IMX_PCIE_FLAG_HAS_PHYDRV, .mode = DW_PCIE_EP_TYPE, .gpr = "fsl,imx8mm-iomuxc-gpr", - .clk_names = imx8mm_clks, - .clks_cnt = ARRAY_SIZE(imx8mm_clks), .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .epc_features = &imx8m_pcie_epc_features, @@ -1842,8 +1801,6 @@ static const struct imx_pcie_drvdata drvdata[] = { IMX_PCIE_FLAG_HAS_PHYDRV, .mode = DW_PCIE_EP_TYPE, .gpr = "fsl,imx8mp-iomuxc-gpr", - .clk_names = imx8mm_clks, - .clks_cnt = ARRAY_SIZE(imx8mm_clks), .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .epc_features = &imx8m_pcie_epc_features, @@ -1854,15 +1811,11 @@ static const struct imx_pcie_drvdata drvdata[] = { .flags = IMX_PCIE_FLAG_HAS_PHYDRV, .mode = DW_PCIE_EP_TYPE, .epc_features = &imx8q_pcie_epc_features, - .clk_names = imx8q_clks, - .clks_cnt = ARRAY_SIZE(imx8q_clks), }, [IMX95_EP] = { .variant = IMX95_EP, .flags = IMX_PCIE_FLAG_HAS_SERDES | IMX_PCIE_FLAG_SUPPORT_64BIT, - .clk_names = imx8mq_clks, - .clks_cnt = ARRAY_SIZE(imx8mq_clks), .ltssm_off = IMX95_PE0_GEN_CTRL_3, .ltssm_mask = IMX95_PCIE_LTSSM_EN, .mode_off[0] = IMX95_PE0_GEN_CTRL_1, -- cgit v1.2.3-59-g8ed1b From b36fb50701619efca5f5450b355d42575cf532ed Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 1 Mar 2025 19:42:54 +0100 Subject: PCI: histb: Fix an error handling path in histb_pcie_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If an error occurs after a successful phy_init() call, then phy_exit() should be called. Add the missing call, as already done in the remove function. Fixes: bbd11bddb398 ("PCI: hisi: Add HiSilicon STB SoC PCIe controller driver") Signed-off-by: Christophe JAILLET [kwilczynski: remove unnecessary hipcie->phy NULL check from histb_pcie_probe() and squash a patch that removes similar NULL check for hipcie-phy from histb_pcie_remove() from https://lore.kernel.org/linux-pci/c369b5d25e17a44984ae5a889ccc28a59a0737f7.1742058005.git.christophe.jaillet@wanadoo.fr] Signed-off-by: Krzysztof Wilczyński Link: https://lore.kernel.org/r/8301fc15cdea5d2dac21f57613e8e6922fb1ad95.1740854531.git.christophe.jaillet@wanadoo.fr --- drivers/pci/controller/dwc/pcie-histb.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c index 615a0e3e6d7e..1f2f4c28a949 100644 --- a/drivers/pci/controller/dwc/pcie-histb.c +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -409,16 +409,21 @@ static int histb_pcie_probe(struct platform_device *pdev) ret = histb_pcie_host_enable(pp); if (ret) { dev_err(dev, "failed to enable host\n"); - return ret; + goto err_exit_phy; } ret = dw_pcie_host_init(pp); if (ret) { dev_err(dev, "failed to initialize host\n"); - return ret; + goto err_exit_phy; } return 0; + +err_exit_phy: + phy_exit(hipcie->phy); + + return ret; } static void histb_pcie_remove(struct platform_device *pdev) @@ -427,8 +432,7 @@ static void histb_pcie_remove(struct platform_device *pdev) histb_pcie_host_disable(hipcie); - if (hipcie->phy) - phy_exit(hipcie->phy); + phy_exit(hipcie->phy); } static const struct of_device_id histb_pcie_of_match[] = { -- cgit v1.2.3-59-g8ed1b From 8f4a489b370e6612700aa16b9e4373b2d85d7503 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sat, 15 Mar 2025 15:15:36 -0500 Subject: PCI: dwc: Use resource start as ioremap() input in dw_pcie_pme_turn_off() The msg_res region translates writes into PCIe Message TLPs. Previously we mapped this region using atu.cpu_addr, the input address programmed into the ATU. "cpu_addr" is a misnomer because when a bus fabric translates addresses between the CPU and the ATU, the ATU input address is different from the CPU address. A future patch will rename "cpu_addr" and correct the value to be the ATU input address instead of the CPU physical address. Map the msg_res region before writing to it using the msg_res resource start, a CPU physical address. Link: https://lore.kernel.org/r/20250315201548.858189-2-helgaas@kernel.org Signed-off-by: Frank Li Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware-host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index ffaded8f2df7..ae3fd2a5dbf8 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -908,7 +908,7 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci) if (ret) return ret; - mem = ioremap(atu.cpu_addr, pci->region_align); + mem = ioremap(pci->pp.msg_res->start, pci->region_align); if (!mem) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From 513ef9c4965b0077037db97f8481d00b5c7ee994 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sat, 15 Mar 2025 15:15:37 -0500 Subject: PCI: dwc: Rename cpu_addr to parent_bus_addr for ATU configuration Rename 'cpu_addr' to 'parent_bus_addr' in the DesignWare ATU configuration. The ATU translates parent bus addresses to PCI addresses, which are often the same as CPU addresses but can differ in systems where the bus fabric translates addresses before passing them to the PCIe controller. This renaming clarifies the purpose and avoids confusion. Link: https://lore.kernel.org/r/20250315201548.858189-3-helgaas@kernel.org Signed-off-by: Frank Li Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware-ep.c | 8 +++--- drivers/pci/controller/dwc/pcie-designware-host.c | 12 ++++---- drivers/pci/controller/dwc/pcie-designware.c | 34 +++++++++++------------ drivers/pci/controller/dwc/pcie-designware.h | 7 +++-- 4 files changed, 31 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 8e07d432e74f..80ac2f9e88eb 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -128,7 +128,7 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, } static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, - dma_addr_t cpu_addr, enum pci_barno bar, + dma_addr_t parent_bus_addr, enum pci_barno bar, size_t size) { int ret; @@ -146,7 +146,7 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, } ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type, - cpu_addr, bar, size); + parent_bus_addr, bar, size); if (ret < 0) { dev_err(pci->dev, "Failed to program IB window\n"); return ret; @@ -181,7 +181,7 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, return ret; set_bit(free_win, ep->ob_window_map); - ep->outbound_addr[free_win] = atu->cpu_addr; + ep->outbound_addr[free_win] = atu->parent_bus_addr; return 0; } @@ -333,7 +333,7 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, atu.func_no = func_no; atu.type = PCIE_ATU_TYPE_MEM; - atu.cpu_addr = addr; + atu.parent_bus_addr = addr; atu.pci_addr = pci_addr; atu.size = size; ret = dw_pcie_ep_outbound_atu(ep, &atu); diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index ae3fd2a5dbf8..1206b26bff3f 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -616,7 +616,7 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, type = PCIE_ATU_TYPE_CFG1; atu.type = type; - atu.cpu_addr = pp->cfg0_base; + atu.parent_bus_addr = pp->cfg0_base; atu.pci_addr = busdev; atu.size = pp->cfg0_size; @@ -641,7 +641,7 @@ static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn, if (pp->cfg0_io_shared) { atu.type = PCIE_ATU_TYPE_IO; - atu.cpu_addr = pp->io_base; + atu.parent_bus_addr = pp->io_base; atu.pci_addr = pp->io_bus_addr; atu.size = pp->io_size; @@ -667,7 +667,7 @@ static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn, if (pp->cfg0_io_shared) { atu.type = PCIE_ATU_TYPE_IO; - atu.cpu_addr = pp->io_base; + atu.parent_bus_addr = pp->io_base; atu.pci_addr = pp->io_bus_addr; atu.size = pp->io_size; @@ -736,7 +736,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) atu.index = i; atu.type = PCIE_ATU_TYPE_MEM; - atu.cpu_addr = entry->res->start; + atu.parent_bus_addr = entry->res->start; atu.pci_addr = entry->res->start - entry->offset; /* Adjust iATU size if MSG TLP region was allocated before */ @@ -758,7 +758,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) if (pci->num_ob_windows > ++i) { atu.index = i; atu.type = PCIE_ATU_TYPE_IO; - atu.cpu_addr = pp->io_base; + atu.parent_bus_addr = pp->io_base; atu.pci_addr = pp->io_bus_addr; atu.size = pp->io_size; @@ -902,7 +902,7 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci) atu.size = resource_size(pci->pp.msg_res); atu.index = pci->pp.msg_atu_index; - atu.cpu_addr = pci->pp.msg_res->start; + atu.parent_bus_addr = pci->pp.msg_res->start; ret = dw_pcie_prog_outbound_atu(pci, &atu); if (ret) diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 145e7f579072..9d0a5f75effc 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -470,25 +470,25 @@ static inline u32 dw_pcie_enable_ecrc(u32 val) int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, const struct dw_pcie_ob_atu_cfg *atu) { - u64 cpu_addr = atu->cpu_addr; + u64 parent_bus_addr = atu->parent_bus_addr; u32 retries, val; u64 limit_addr; if (pci->ops && pci->ops->cpu_addr_fixup) - cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr); + parent_bus_addr = pci->ops->cpu_addr_fixup(pci, parent_bus_addr); - limit_addr = cpu_addr + atu->size - 1; + limit_addr = parent_bus_addr + atu->size - 1; - if ((limit_addr & ~pci->region_limit) != (cpu_addr & ~pci->region_limit) || - !IS_ALIGNED(cpu_addr, pci->region_align) || + if ((limit_addr & ~pci->region_limit) != (parent_bus_addr & ~pci->region_limit) || + !IS_ALIGNED(parent_bus_addr, pci->region_align) || !IS_ALIGNED(atu->pci_addr, pci->region_align) || !atu->size) { return -EINVAL; } dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LOWER_BASE, - lower_32_bits(cpu_addr)); + lower_32_bits(parent_bus_addr)); dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_BASE, - upper_32_bits(cpu_addr)); + upper_32_bits(parent_bus_addr)); dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LIMIT, lower_32_bits(limit_addr)); @@ -502,7 +502,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, upper_32_bits(atu->pci_addr)); val = atu->type | atu->routing | PCIE_ATU_FUNC_NUM(atu->func_no); - if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr) && + if (upper_32_bits(limit_addr) > upper_32_bits(parent_bus_addr) && dw_pcie_ver_is_ge(pci, 460A)) val |= PCIE_ATU_INCREASE_REGION_SIZE; if (dw_pcie_ver_is(pci, 490A)) @@ -545,13 +545,13 @@ static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg } int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type, - u64 cpu_addr, u64 pci_addr, u64 size) + u64 parent_bus_addr, u64 pci_addr, u64 size) { u64 limit_addr = pci_addr + size - 1; u32 retries, val; if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) || - !IS_ALIGNED(cpu_addr, pci->region_align) || + !IS_ALIGNED(parent_bus_addr, pci->region_align) || !IS_ALIGNED(pci_addr, pci->region_align) || !size) { return -EINVAL; } @@ -568,9 +568,9 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type, upper_32_bits(limit_addr)); dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET, - lower_32_bits(cpu_addr)); + lower_32_bits(parent_bus_addr)); dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET, - upper_32_bits(cpu_addr)); + upper_32_bits(parent_bus_addr)); val = type; if (upper_32_bits(limit_addr) > upper_32_bits(pci_addr) && @@ -597,18 +597,18 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type, } int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, - int type, u64 cpu_addr, u8 bar, size_t size) + int type, u64 parent_bus_addr, u8 bar, size_t size) { u32 retries, val; - if (!IS_ALIGNED(cpu_addr, pci->region_align) || - !IS_ALIGNED(cpu_addr, size)) + if (!IS_ALIGNED(parent_bus_addr, pci->region_align) || + !IS_ALIGNED(parent_bus_addr, size)) return -EINVAL; dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET, - lower_32_bits(cpu_addr)); + lower_32_bits(parent_bus_addr)); dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET, - upper_32_bits(cpu_addr)); + upper_32_bits(parent_bus_addr)); dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, type | PCIE_ATU_FUNC_NUM(func_no)); diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 501d9ddfea16..d0d8c622a6e8 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -343,7 +343,7 @@ struct dw_pcie_ob_atu_cfg { u8 func_no; u8 code; u8 routing; - u64 cpu_addr; + u64 parent_bus_addr; u64 pci_addr; u64 size; }; @@ -491,9 +491,10 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci); int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, const struct dw_pcie_ob_atu_cfg *atu); int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type, - u64 cpu_addr, u64 pci_addr, u64 size); + u64 parent_bus_addr, u64 pci_addr, u64 size); int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, - int type, u64 cpu_addr, u8 bar, size_t size); + int type, u64 parent_bus_addr, + u8 bar, size_t size); void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index); void dw_pcie_setup(struct dw_pcie *pci); void dw_pcie_iatu_detect(struct dw_pcie *pci); -- cgit v1.2.3-59-g8ed1b From 84f37c43d5fe3c71fbc72f37fb00c706650fc6a9 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sat, 15 Mar 2025 15:15:38 -0500 Subject: PCI: dwc: Call devm_pci_alloc_host_bridge() early in dw_pcie_host_init() Move devm_pci_alloc_host_bridge() to the beginning of dw_pcie_host_init(). devm_pci_alloc_host_bridge() is generic code that doesn't depend on any DWC resource, so moving it earlier keeps all the subsequent devicetree-related code together. [bhelgaas: reorder earlier in series] Link: https://lore.kernel.org/r/20250315201548.858189-4-helgaas@kernel.org Signed-off-by: Frank Li Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware-host.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 1206b26bff3f..5636243fb90e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -431,6 +431,12 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) raw_spin_lock_init(&pp->lock); + bridge = devm_pci_alloc_host_bridge(dev, 0); + if (!bridge) + return -ENOMEM; + + pp->bridge = bridge; + ret = dw_pcie_get_resources(pci); if (ret) return ret; @@ -448,12 +454,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) if (IS_ERR(pp->va_cfg0_base)) return PTR_ERR(pp->va_cfg0_base); - bridge = devm_pci_alloc_host_bridge(dev, 0); - if (!bridge) - return -ENOMEM; - - pp->bridge = bridge; - /* Get the I/O range from DT */ win = resource_list_first_type(&bridge->windows, IORESOURCE_IO); if (win) { -- cgit v1.2.3-59-g8ed1b From 2ce107e064574a619a82cb4adb829803533016db Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 15 Mar 2025 15:15:39 -0500 Subject: PCI: dwc: Consolidate devicetree handling in dw_pcie_host_get_resources() Consolidate devicetree resource handling in dw_pcie_host_get_resources(). No functional change intended. Link: https://lore.kernel.org/r/20250315201548.858189-5-helgaas@kernel.org Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li --- drivers/pci/controller/dwc/pcie-designware-host.c | 37 +++++++++++++++-------- 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 5636243fb90e..9ce06b1ee266 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -418,25 +418,15 @@ static void dw_pcie_host_request_msg_tlp_res(struct dw_pcie_rp *pp) } } -int dw_pcie_host_init(struct dw_pcie_rp *pp) +static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct device *dev = pci->dev; - struct device_node *np = dev->of_node; struct platform_device *pdev = to_platform_device(dev); struct resource_entry *win; - struct pci_host_bridge *bridge; struct resource *res; int ret; - raw_spin_lock_init(&pp->lock); - - bridge = devm_pci_alloc_host_bridge(dev, 0); - if (!bridge) - return -ENOMEM; - - pp->bridge = bridge; - ret = dw_pcie_get_resources(pci); if (ret) return ret; @@ -455,13 +445,36 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) return PTR_ERR(pp->va_cfg0_base); /* Get the I/O range from DT */ - win = resource_list_first_type(&bridge->windows, IORESOURCE_IO); + win = resource_list_first_type(&pp->bridge->windows, IORESOURCE_IO); if (win) { pp->io_size = resource_size(win->res); pp->io_bus_addr = win->res->start - win->offset; pp->io_base = pci_pio_to_address(win->res->start); } + return 0; +} + +int dw_pcie_host_init(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct device *dev = pci->dev; + struct device_node *np = dev->of_node; + struct pci_host_bridge *bridge; + int ret; + + raw_spin_lock_init(&pp->lock); + + bridge = devm_pci_alloc_host_bridge(dev, 0); + if (!bridge) + return -ENOMEM; + + pp->bridge = bridge; + + ret = dw_pcie_host_get_resources(pp); + if (ret) + return ret; + /* Set default bus ops */ bridge->ops = &dw_pcie_ops; bridge->child_ops = &dw_child_pcie_ops; -- cgit v1.2.3-59-g8ed1b From 18056a48669a040bef491e63b25896561ee14d90 Mon Sep 17 00:00:00 2001 From: Ryo Takakura Date: Tue, 18 Feb 2025 09:08:30 +0100 Subject: PCI: vmd: Make vmd_dev::cfg_lock a raw_spinlock_t type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The access to the PCI config space via pci_ops::read and pci_ops::write is a low-level hardware access. The functions can be accessed with disabled interrupts even on PREEMPT_RT. The pci_lock is a raw_spinlock_t for this purpose. A spinlock_t becomes a sleeping lock on PREEMPT_RT, so it cannot be acquired with disabled interrupts. The vmd_dev::cfg_lock is accessed in the same context as the pci_lock. Make vmd_dev::cfg_lock a raw_spinlock_t type so it can be used with interrupts disabled. This was reported as: BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48 Call Trace: rt_spin_lock+0x4e/0x130 vmd_pci_read+0x8d/0x100 [vmd] pci_user_read_config_byte+0x6f/0xe0 pci_read_config+0xfe/0x290 sysfs_kf_bin_read+0x68/0x90 Signed-off-by: Ryo Takakura Tested-by: Luis Claudio R. Goncalves Acked-by: Luis Claudio R. Goncalves [bigeasy: reword commit message] Signed-off-by: Sebastian Andrzej Siewior Tested-off-by: Luis Claudio R. Goncalves Link: https://lore.kernel.org/r/20250218080830.ufw3IgyX@linutronix.de [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński [bhelgaas: add back report info from https://lore.kernel.org/lkml/20241218115951.83062-1-ryotkkr98@gmail.com/] Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/vmd.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 9d9596947350..94ceec50a2b9 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -125,7 +125,7 @@ struct vmd_irq_list { struct vmd_dev { struct pci_dev *dev; - spinlock_t cfg_lock; + raw_spinlock_t cfg_lock; void __iomem *cfgbar; int msix_count; @@ -391,7 +391,7 @@ static int vmd_pci_read(struct pci_bus *bus, unsigned int devfn, int reg, if (!addr) return -EFAULT; - spin_lock_irqsave(&vmd->cfg_lock, flags); + raw_spin_lock_irqsave(&vmd->cfg_lock, flags); switch (len) { case 1: *value = readb(addr); @@ -406,7 +406,7 @@ static int vmd_pci_read(struct pci_bus *bus, unsigned int devfn, int reg, ret = -EINVAL; break; } - spin_unlock_irqrestore(&vmd->cfg_lock, flags); + raw_spin_unlock_irqrestore(&vmd->cfg_lock, flags); return ret; } @@ -426,7 +426,7 @@ static int vmd_pci_write(struct pci_bus *bus, unsigned int devfn, int reg, if (!addr) return -EFAULT; - spin_lock_irqsave(&vmd->cfg_lock, flags); + raw_spin_lock_irqsave(&vmd->cfg_lock, flags); switch (len) { case 1: writeb(value, addr); @@ -444,7 +444,7 @@ static int vmd_pci_write(struct pci_bus *bus, unsigned int devfn, int reg, ret = -EINVAL; break; } - spin_unlock_irqrestore(&vmd->cfg_lock, flags); + raw_spin_unlock_irqrestore(&vmd->cfg_lock, flags); return ret; } @@ -1009,7 +1009,7 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id) if (features & VMD_FEAT_OFFSET_FIRST_VECTOR) vmd->first_vec = 1; - spin_lock_init(&vmd->cfg_lock); + raw_spin_lock_init(&vmd->cfg_lock); pci_set_drvdata(dev, vmd); err = vmd_enable_domain(vmd, features); if (err) -- cgit v1.2.3-59-g8ed1b From a5fb3ff632876d63ee1fc5ed3af2464240145a00 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 20 Mar 2025 11:06:04 -0700 Subject: PCI: Allow PCI bridges to go to D3Hot on all non-x86 Currently, pci_bridge_d3_possible() encodes a variety of decision factors when deciding whether a given bridge can be put into D3. A particular one of note is for "recent enough PCIe ports." Per Rafael [0]: "There were hardware issues related to PM on x86 platforms predating the introduction of Connected Standby in Windows. For instance, programming a port into D3hot by writing to its PMCSR might cause the PCIe link behind it to go down and the only way to revive it was to power cycle the Root Complex. And similar." Thus, this function contains a DMI-based check for post-2015 BIOS. The above factors (Windows, x86) don't really apply to non-x86 systems, and also, many such systems don't have BIOS or DMI. However, we'd like to be able to suspend bridges on non-x86 systems too. Restrict the "recent enough" check to x86. If we find further incompatibilities, it probably makes sense to expand on the deny-list approach (i.e., bridge_d3_blacklist or similar). Link: https://lore.kernel.org/r/20250320110604.v6.1.Id0a0e78ab0421b6bce51c4b0b87e6aebdfc69ec7@changeid Link: https://lore.kernel.org/linux-pci/CAJZ5v0j_6jeMAQ7eFkZBe5Yi+USGzysxAgfemYh=-zq4h5W+Qg@mail.gmail.com/ [0] Link: https://lore.kernel.org/linux-pci/20240227225442.GA249898@bhelgaas/ [1] Link: https://lore.kernel.org/linux-pci/20240828210705.GA37859@bhelgaas/ [2] [Brian: rewrite to !X86 based on Rafael's suggestions] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Brian Norris Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 869d204a70a3..2b53219fda3b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3023,7 +3023,7 @@ static const struct dmi_system_id bridge_d3_blacklist[] = { * @bridge: Bridge to check * * This function checks if it is possible to move the bridge to D3. - * Currently we only allow D3 for recent enough PCIe ports and Thunderbolt. + * Currently we only allow D3 for some PCIe ports and for Thunderbolt. */ bool pci_bridge_d3_possible(struct pci_dev *bridge) { @@ -3067,10 +3067,10 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge) return false; /* - * It should be safe to put PCIe ports from 2015 or newer - * to D3. + * Out of caution, we only allow PCIe ports from 2015 or newer + * into D3 on x86. */ - if (dmi_get_bios_year() >= 2015) + if (!IS_ENABLED(CONFIG_X86) || dmi_get_bios_year() >= 2015) return true; break; } -- cgit v1.2.3-59-g8ed1b From 9ec19bfa78bd788945e2445b09de7b4482dee432 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Thu, 20 Mar 2025 16:28:37 +0200 Subject: PCI: Fix BAR resizing when VF BARs are assigned MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __resource_resize_store() attempts to release all resources of the device before attempting the resize. The loop, however, only covers standard BARs (< PCI_STD_NUM_BARS). If a device has VF BARs that are assigned, pci_reassign_bridge_resources() finds the bridge window still has some assigned child resources and returns -NOENT which makes pci_resize_resource() to detect an error and abort the resize. Change the release loop to cover all resources up to VF BARs which allows the resize operation to release the bridge windows and attempt to assigned them again with the different size. If SR-IOV is enabled, disallow resize as it requires releasing also IOV resources. Link: https://lore.kernel.org/r/20250320142837.8027-1-ilpo.jarvinen@linux.intel.com Fixes: 91fa127794ac ("PCI: Expose PCIe Resizable BAR support via sysfs") Reported-by: Michał Winiarski Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Reviewed-by: Alex Williamson --- drivers/pci/pci-sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index b46ce1a2c554..0e7eb2a42d88 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1556,7 +1556,7 @@ static ssize_t __resource_resize_store(struct device *dev, int n, return -EINVAL; device_lock(dev); - if (dev->driver) { + if (dev->driver || pci_num_vf(pdev)) { ret = -EBUSY; goto unlock; } @@ -1578,7 +1578,7 @@ static ssize_t __resource_resize_store(struct device *dev, int n, pci_remove_resource_files(pdev); - for (i = 0; i < PCI_STD_NUM_BARS; i++) { + for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { if (pci_resource_len(pdev, i) && pci_resource_flags(pdev, i) == flags) pci_release_resource(pdev, i); -- cgit v1.2.3-59-g8ed1b From 95c4e6d42c99237c0264d1a401a6223849477cb6 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Tue, 11 Mar 2025 19:46:58 +0200 Subject: PCI: Move pci_rescan_bus_bridge_resize() declaration to pci/pci.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_rescan_bus_bridge_resize() is only used by code inside PCI subsystem. The comment also falsely advertises it to be for hotplug drivers, yet the only caller is from sysfs store function. Move the function declaration into pci/pci.h. Link: https://lore.kernel.org/r/20250311174701.3586-1-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.h | 2 ++ include/linux/pci.h | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4e2ac06db3c4..34b8e2e6b176 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -309,6 +309,8 @@ enum pci_bar_type { struct device *pci_get_host_bridge_device(struct pci_dev *dev); void pci_put_host_bridge_device(struct device *dev); +unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge); + int pci_configure_extended_tags(struct pci_dev *dev, void *ign); bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, int rrs_timeout); diff --git a/include/linux/pci.h b/include/linux/pci.h index 47b31ad724fa..d788acf2686a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1455,7 +1455,6 @@ void set_pcie_port_type(struct pci_dev *pdev); void set_pcie_hotplug_bridge(struct pci_dev *pdev); /* Functions for PCI Hotplug drivers to use */ -unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge); unsigned int pci_rescan_bus(struct pci_bus *bus); void pci_lock_rescan_remove(void); void pci_unlock_rescan_remove(void); -- cgit v1.2.3-59-g8ed1b From 7d4bcc0f2631e4ee10b5bcfff24a423d1c3c02a3 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Tue, 11 Mar 2025 19:46:59 +0200 Subject: PCI: Move resource reassignment func declarations into pci/pci.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neither pci_reassign_bridge_resources() nor pci_reassign_resource() is used outside of the PCI subsystem. They seem to be naturally static functions but since resource fitting/assignment is split between setup-bus.c and setup-res.c, they fall into different sides of the divide and need to be declared. Move the declarations of pci_reassign_bridge_resources() and pci_reassign_resource() into pci/pci.h to keep them internal to PCI subsystem. Link: https://lore.kernel.org/r/20250311174701.3586-2-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.h | 2 ++ include/linux/pci.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 34b8e2e6b176..1b6081fa3ecd 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -310,6 +310,8 @@ struct device *pci_get_host_bridge_device(struct pci_dev *dev); void pci_put_host_bridge_device(struct device *dev); unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge); +int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type); +int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align); int pci_configure_extended_tags(struct pci_dev *dev, void *ign); bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, diff --git a/include/linux/pci.h b/include/linux/pci.h index d788acf2686a..c629962f4ccd 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1396,7 +1396,6 @@ void pci_reset_secondary_bus(struct pci_dev *dev); void pcibios_reset_secondary_bus(struct pci_dev *dev); void pci_update_resource(struct pci_dev *dev, int resno); int __must_check pci_assign_resource(struct pci_dev *dev, int i); -int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align); void pci_release_resource(struct pci_dev *dev, int resno); static inline int pci_rebar_bytes_to_size(u64 bytes) { @@ -1476,7 +1475,6 @@ void pci_assign_unassigned_resources(void); void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge); void pci_assign_unassigned_bus_resources(struct pci_bus *bus); void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus); -int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type); int pci_enable_resources(struct pci_dev *, int mask); void pci_assign_irq(struct pci_dev *dev); struct resource *pci_find_resource(struct pci_dev *dev, struct resource *res); -- cgit v1.2.3-59-g8ed1b From 2f255e299c676cbae4b0d164a497a0553b86845a Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Tue, 11 Mar 2025 19:47:00 +0200 Subject: PCI: Make pci_setup_bridge() static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_setup_bridge() is only used within setup-bus.c. Therefore, make it a static function. Link: https://lore.kernel.org/r/20250311174701.3586-3-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 2 +- include/linux/pci.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 58f28e4e24b3..c7140051e691 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -791,7 +791,7 @@ void __weak pcibios_setup_bridge(struct pci_bus *bus, unsigned long type) { } -void pci_setup_bridge(struct pci_bus *bus) +static void pci_setup_bridge(struct pci_bus *bus) { unsigned long type = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; diff --git a/include/linux/pci.h b/include/linux/pci.h index c629962f4ccd..9a703355ef06 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1634,7 +1634,6 @@ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata); int pci_cfg_space_size(struct pci_dev *dev); unsigned char pci_bus_max_busnr(struct pci_bus *bus); -void pci_setup_bridge(struct pci_bus *bus); resource_size_t pcibios_window_alignment(struct pci_bus *bus, unsigned long type); -- cgit v1.2.3-59-g8ed1b From cc7a371b0bf5e507b24c5a595068dfb4e2b3445b Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Tue, 11 Mar 2025 19:47:01 +0200 Subject: PCI: Move cardbus IO size declarations into pci/pci.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For some reason, cardbus related io/mem size declarations are in linux/pci.h, whereas non-cardbus sizes are already in pci/pci.h. Move all them into one place in pci/pci.h. Link: https://lore.kernel.org/r/20250311174701.3586-4-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.h | 2 ++ include/linux/pci.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1b6081fa3ecd..ad247c5f794d 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -266,6 +266,8 @@ extern unsigned long pci_hotplug_io_size; extern unsigned long pci_hotplug_mmio_size; extern unsigned long pci_hotplug_mmio_pref_size; extern unsigned long pci_hotplug_bus_size; +extern unsigned long pci_cardbus_io_size; +extern unsigned long pci_cardbus_mem_size; /** * pci_match_one_device - Tell if a PCI device structure has a matching diff --git a/include/linux/pci.h b/include/linux/pci.h index 9a703355ef06..f9424478a19a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2332,8 +2332,6 @@ extern int pci_pci_problems; #define PCIPCI_ALIMAGIK 32 /* Need low latency setting */ #define PCIAGP_FAIL 64 /* No PCI to AGP DMA */ -extern unsigned long pci_cardbus_io_size; -extern unsigned long pci_cardbus_mem_size; extern u8 pci_dfl_cache_line_size; extern u8 pci_cache_line_size; -- cgit v1.2.3-59-g8ed1b From 527664f738afb6f2c58022cd35e63801e5dc7aec Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Fri, 21 Mar 2025 18:21:14 +0200 Subject: PCI: pciehp: Don't enable HPIE when resuming in poll mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCIe hotplug can operate in poll mode without interrupt handlers using a polling kthread only. eb34da60edee ("PCI: pciehp: Disable hotplug interrupt during suspend") failed to consider that and enables HPIE (Hot-Plug Interrupt Enable) unconditionally when resuming the Port. Only set HPIE if non-poll mode is in use. This makes pcie_enable_interrupt() match how pcie_enable_notification() already handles HPIE. Link: https://lore.kernel.org/r/20250321162114.3939-1-ilpo.jarvinen@linux.intel.com Fixes: eb34da60edee ("PCI: pciehp: Disable hotplug interrupt during suspend") Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Reviewed-by: Lukas Wunner --- drivers/pci/hotplug/pciehp_hpc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index bb5a8d9f03ad..28ab393af1c0 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -842,7 +842,9 @@ void pcie_enable_interrupt(struct controller *ctrl) { u16 mask; - mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; + mask = PCI_EXP_SLTCTL_DLLSCE; + if (!pciehp_poll_mode) + mask |= PCI_EXP_SLTCTL_HPIE; pcie_write_cmd(ctrl, mask, mask); } -- cgit v1.2.3-59-g8ed1b From 026e4bffb0af9632f5a0bbf8d594f2aace44cf07 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Fri, 21 Mar 2025 18:31:03 +0200 Subject: PCI/bwctrl: Fix pcie_bwctrl_select_speed() return type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pcie_bwctrl_select_speed() should take __fls() of the speed bit, not return it as a raw value. Instead of directly returning 2.5GT/s speed bit, simply assign the fallback speed (2.5GT/s) into supported_speeds variable to share the normal return path that calls pcie_supported_speeds2target_speed() to calculate __fls(). This code path is not very likely to execute because pcie_get_supported_speeds() should provide valid ->supported_speeds but a spec violating device could fail to synthesize any speed in pcie_get_supported_speeds(). It could also happen in case the supported_speeds intersection is empty (also a violation of the current PCIe specs). Link: https://lore.kernel.org/r/20250321163103.5145-1-ilpo.jarvinen@linux.intel.com Fixes: de9a6c8d5dbf ("PCI/bwctrl: Add pcie_set_target_speed() to set PCIe Link Speed") Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/bwctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/bwctrl.c b/drivers/pci/pcie/bwctrl.c index 0a5e7efbce2c..58ba8142c9a3 100644 --- a/drivers/pci/pcie/bwctrl.c +++ b/drivers/pci/pcie/bwctrl.c @@ -113,7 +113,7 @@ static u16 pcie_bwctrl_select_speed(struct pci_dev *port, enum pci_bus_speed spe up_read(&pci_bus_sem); } if (!supported_speeds) - return PCI_EXP_LNKCAP2_SLS_2_5GB; + supported_speeds = PCI_EXP_LNKCAP2_SLS_2_5GB; return pcie_supported_speeds2target_speed(supported_speeds & desired_speeds); } -- cgit v1.2.3-59-g8ed1b From 04d50d953ab46d96b0b32d5ad955fceaa28622db Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Mon, 10 Mar 2025 10:45:24 +0200 Subject: PCI: Fix NULL dereference in SR-IOV VF creation error path Clean up when virtfn setup fails to prevent NULL pointer dereference during device removal. The kernel oops below occurred due to incorrect error handling flow when pci_setup_device() fails. Add pci_iov_scan_device(), which handles virtfn allocation and setup and cleans up if pci_setup_device() fails, so pci_iov_add_virtfn() doesn't need to call pci_stop_and_remove_bus_device(). This prevents accessing partially initialized virtfn devices during removal. BUG: kernel NULL pointer dereference, address: 00000000000000d0 RIP: 0010:device_del+0x3d/0x3d0 Call Trace: pci_remove_bus_device+0x7c/0x100 pci_iov_add_virtfn+0xfa/0x200 sriov_enable+0x208/0x420 mlx5_core_sriov_configure+0x6a/0x160 [mlx5_core] sriov_numvfs_store+0xae/0x1a0 Link: https://lore.kernel.org/r/20250310084524.599225-1-shayd@nvidia.com Fixes: e3f30d563a38 ("PCI: Make pci_destroy_dev() concurrent safe") Signed-off-by: Shay Drory [bhelgaas: commit log, return ERR_PTR(-ENOMEM) directly] Signed-off-by: Bjorn Helgaas Cc: Keith Busch --- drivers/pci/iov.c | 48 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 121540f57d4b..10693b5d7eb6 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -285,23 +285,16 @@ const struct attribute_group sriov_vf_dev_attr_group = { .is_visible = sriov_vf_attrs_are_visible, }; -int pci_iov_add_virtfn(struct pci_dev *dev, int id) +static struct pci_dev *pci_iov_scan_device(struct pci_dev *dev, int id, + struct pci_bus *bus) { - int i; - int rc = -ENOMEM; - u64 size; - struct pci_dev *virtfn; - struct resource *res; struct pci_sriov *iov = dev->sriov; - struct pci_bus *bus; - - bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id)); - if (!bus) - goto failed; + struct pci_dev *virtfn; + int rc; virtfn = pci_alloc_dev(bus); if (!virtfn) - goto failed0; + return ERR_PTR(-ENOMEM); virtfn->devfn = pci_iov_virtfn_devfn(dev, id); virtfn->vendor = dev->vendor; @@ -314,8 +307,35 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id) pci_read_vf_config_common(virtfn); rc = pci_setup_device(virtfn); - if (rc) - goto failed1; + if (rc) { + pci_dev_put(dev); + pci_bus_put(virtfn->bus); + kfree(virtfn); + return ERR_PTR(rc); + } + + return virtfn; +} + +int pci_iov_add_virtfn(struct pci_dev *dev, int id) +{ + struct pci_bus *bus; + struct pci_dev *virtfn; + struct resource *res; + int rc, i; + u64 size; + + bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id)); + if (!bus) { + rc = -ENOMEM; + goto failed; + } + + virtfn = pci_iov_scan_device(dev, id, bus); + if (IS_ERR(virtfn)) { + rc = PTR_ERR(virtfn); + goto failed0; + } virtfn->dev.parent = dev->dev.parent; virtfn->multifunction = 0; -- cgit v1.2.3-59-g8ed1b From 888bd8322dfc325dc5ad99184baba4e1fd91082d Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Wed, 26 Feb 2025 13:07:46 +0100 Subject: s390/pci: Introduce pdev->non_mappable_bars and replace VFIO_PCI_MMAP The ability to map PCI resources to user-space is controlled by global defines. For vfio there is VFIO_PCI_MMAP which is only disabled on s390 and controls mapping of PCI resources using vfio-pci with a fallback option via the pread()/pwrite() interface. For the PCI core there is ARCH_GENERIC_PCI_MMAP_RESOURCE which enables a generic implementation for mapping PCI resources plus the newer sysfs interface. Then there is HAVE_PCI_MMAP which can be used with custom definitions of pci_mmap_resource_range() and the historical /proc/bus/pci interface. Both mechanisms are all or nothing. For s390 mapping PCI resources is possible and useful for testing and certain applications such as QEMU's vfio-pci based user-space NVMe driver. For certain devices, however access to PCI resources via mappings to user-space is not possible and these must be excluded from the general PCI resource mapping mechanisms. Introduce pdev->non_mappable_bars to indicate that a PCI device's BARs can not be accessed via mappings to user-space. In the future this enables per-device restrictions of PCI resource mapping. For now, set this flag for all PCI devices on s390 in line with the existing, general disable of PCI resource mapping. As s390 is the only user of the VFI_PCI_MMAP Kconfig options this can already be replaced with a check of this new flag. Also add similar checks in the other code protected by HAVE_PCI_MMAP respectively ARCH_GENERIC_PCI_MMAP in preparation for enabling these for supported devices. Link: https://lore.kernel.org/lkml/20250212132808.08dcf03c.alex.williamson@redhat.com/ Link: https://lore.kernel.org/r/20250226-vfio_pci_mmap-v7-2-c5c0f1d26efd@linux.ibm.com Signed-off-by: Niklas Schnelle Signed-off-by: Bjorn Helgaas --- arch/s390/pci/pci.c | 1 + drivers/pci/pci-sysfs.c | 4 ++++ drivers/pci/proc.c | 4 ++++ drivers/vfio/pci/Kconfig | 4 ---- drivers/vfio/pci/vfio_pci_core.c | 2 +- include/linux/pci.h | 1 + 6 files changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 88f72745fa59..d14b8605a32c 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -590,6 +590,7 @@ int pcibios_device_add(struct pci_dev *pdev) zpci_zdev_get(zdev); if (pdev->is_physfn) pdev->no_vf_scan = 1; + pdev->non_mappable_bars = 1; zpci_map_resources(pdev); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 0e7eb2a42d88..7c0d30f6470b 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1257,6 +1257,10 @@ static int pci_create_resource_files(struct pci_dev *pdev) int i; int retval; + /* Skip devices with non-mappable BARs */ + if (pdev->non_mappable_bars) + return 0; + /* Expose the PCI resources from this device as files */ for (i = 0; i < PCI_STD_NUM_BARS; i++) { diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index f967709082d6..9348a0fb8084 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -251,6 +251,10 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) security_locked_down(LOCKDOWN_PCI_ACCESS)) return -EPERM; + /* Skip devices with non-mappable BARs */ + if (dev->non_mappable_bars) + return -EINVAL; + if (fpriv->mmap_state == pci_mmap_io) { if (!arch_can_pci_mmap_io()) return -EINVAL; diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig index bf50ffa10bde..c3bcb6911c53 100644 --- a/drivers/vfio/pci/Kconfig +++ b/drivers/vfio/pci/Kconfig @@ -7,10 +7,6 @@ config VFIO_PCI_CORE select VFIO_VIRQFD select IRQ_BYPASS_MANAGER -config VFIO_PCI_MMAP - def_bool y if !S390 - depends on VFIO_PCI_CORE - config VFIO_PCI_INTX def_bool y if !S390 depends on VFIO_PCI_CORE diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 586e49efb81b..c8586d47704c 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -116,7 +116,7 @@ static void vfio_pci_probe_mmaps(struct vfio_pci_core_device *vdev) res = &vdev->pdev->resource[bar]; - if (!IS_ENABLED(CONFIG_VFIO_PCI_MMAP)) + if (vdev->pdev->non_mappable_bars) goto no_mmap; if (!(res->flags & IORESOURCE_MEM)) diff --git a/include/linux/pci.h b/include/linux/pci.h index f9424478a19a..5b5ed5ec5f0a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -476,6 +476,7 @@ struct pci_dev { unsigned int no_command_memory:1; /* No PCI_COMMAND_MEMORY */ unsigned int rom_bar_overlap:1; /* ROM BAR disable broken */ unsigned int rom_attr_enabled:1; /* Display of ROM attribute enabled? */ + unsigned int non_mappable_bars:1; /* BARs can't be mapped to user-space */ pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ -- cgit v1.2.3-59-g8ed1b From aa9f168d55dc47c0de564f7dfe0e90467c9fee71 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Wed, 26 Feb 2025 13:07:47 +0100 Subject: s390/pci: Support mmap() of PCI resources except for ISM devices So far s390 does not allow mmap() of PCI resources to user-space via the usual mechanisms, though it does use it for RDMA. For the PCI sysfs resource files and /proc/bus/pci it defines neither HAVE_PCI_MMAP nor ARCH_GENERIC_PCI_MMAP_RESOURCE. For vfio-pci s390 previously relied on disabled VFIO_PCI_MMAP and now relies on setting pdev->non_mappable_bars for all devices. This is partly because access to mapped PCI resources from user-space requires special PCI load/store memory-I/O (MIO) instructions, or the special MMIO syscalls when these are not available. Still, such access is possible and useful not just for RDMA, in fact not being able to mmap() PCI resources has previously caused extra work when testing devices. One thing that doesn't work with PCI resources mapped to user-space though is the s390 specific virtual ISM device. Not only because the BAR size of 256 TiB prevents mapping the whole BAR but also because access requires use of the legacy PCI instructions which are not accessible to user-space on systems with the newer MIO PCI instructions. Now with the pdev->non_mappable_bars flag ISM can be excluded from mapping its resources while making this functionality available for all other PCI devices. To this end introduce a minimal implementation of PCI_QUIRKS and use that to set pdev->non_mappable_bars for ISM devices only. Then also set ARCH_GENERIC_PCI_MMAP_RESOURCE to take advantage of the generic implementation of pci_mmap_resource_range() enabling only the newer sysfs mmap() interface. This follows the recommendation in Documentation/PCI/sysfs-pci.rst. Link: https://lore.kernel.org/r/20250226-vfio_pci_mmap-v7-3-c5c0f1d26efd@linux.ibm.com Signed-off-by: Niklas Schnelle Signed-off-by: Bjorn Helgaas --- arch/s390/Kconfig | 4 +--- arch/s390/include/asm/pci.h | 3 +++ arch/s390/pci/Makefile | 2 +- arch/s390/pci/pci.c | 1 - arch/s390/pci/pci_fixup.c | 23 +++++++++++++++++++++++ drivers/s390/net/ism_drv.c | 1 - include/linux/pci_ids.h | 1 + 7 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 arch/s390/pci/pci_fixup.c (limited to 'drivers') diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 9c9ec08d78c7..e48741e00147 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -41,9 +41,6 @@ config AUDIT_ARCH config NO_IOPORT_MAP def_bool y -config PCI_QUIRKS - def_bool n - config ARCH_SUPPORTS_UPROBES def_bool y @@ -258,6 +255,7 @@ config S390 select PCI_DOMAINS if PCI select PCI_MSI if PCI select PCI_MSI_ARCH_FALLBACKS if PCI_MSI + select PCI_QUIRKS if PCI select SPARSE_IRQ select SWIOTLB select SYSCTL_EXCEPTION_TRACE diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index 474e1f8d1d3c..d2086af3434c 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -11,6 +11,9 @@ #include #include +#define ARCH_GENERIC_PCI_MMAP_RESOURCE 1 +#define arch_can_pci_mmap_wc() 1 + #define PCIBIOS_MIN_IO 0x1000 #define PCIBIOS_MIN_MEM 0x10000000 diff --git a/arch/s390/pci/Makefile b/arch/s390/pci/Makefile index df73c5182990..1810e0944a4e 100644 --- a/arch/s390/pci/Makefile +++ b/arch/s390/pci/Makefile @@ -5,6 +5,6 @@ obj-$(CONFIG_PCI) += pci.o pci_irq.o pci_clp.o \ pci_event.o pci_debug.o pci_insn.o pci_mmio.o \ - pci_bus.o pci_kvm_hook.o pci_report.o + pci_bus.o pci_kvm_hook.o pci_report.o pci_fixup.o obj-$(CONFIG_PCI_IOV) += pci_iov.o obj-$(CONFIG_SYSFS) += pci_sysfs.o diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index d14b8605a32c..88f72745fa59 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -590,7 +590,6 @@ int pcibios_device_add(struct pci_dev *pdev) zpci_zdev_get(zdev); if (pdev->is_physfn) pdev->no_vf_scan = 1; - pdev->non_mappable_bars = 1; zpci_map_resources(pdev); diff --git a/arch/s390/pci/pci_fixup.c b/arch/s390/pci/pci_fixup.c new file mode 100644 index 000000000000..35688b645098 --- /dev/null +++ b/arch/s390/pci/pci_fixup.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Exceptions for specific devices, + * + * Copyright IBM Corp. 2025 + * + * Author(s): + * Niklas Schnelle + */ +#include + +static void zpci_ism_bar_no_mmap(struct pci_dev *pdev) +{ + /* + * ISM's BAR is special. Drivers written for ISM know + * how to handle this but others need to be aware of their + * special nature e.g. to prevent attempts to mmap() it. + */ + pdev->non_mappable_bars = 1; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_IBM, + PCI_DEVICE_ID_IBM_ISM, + zpci_ism_bar_no_mmap); diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index e36e3ea165d3..d32633ed9fa8 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -20,7 +20,6 @@ MODULE_DESCRIPTION("ISM driver for s390"); MODULE_LICENSE("GPL"); -#define PCI_DEVICE_ID_IBM_ISM 0x04ED #define DRV_NAME "ism" static const struct pci_device_id ism_device_table[] = { diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index de5deb1a0118..e0cdc290dff5 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -518,6 +518,7 @@ #define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM 0x0251 #define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM_PCIE 0x0361 #define PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL 0x252 +#define PCI_DEVICE_ID_IBM_ISM 0x04ed #define PCI_SUBVENDOR_ID_IBM 0x1014 #define PCI_SUBDEVICE_ID_IBM_SATURN_SERIAL_ONE_PORT 0x03d4 -- cgit v1.2.3-59-g8ed1b From 2311ab1820fed26caf0095d38ade892770ea574d Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Thu, 6 Mar 2025 17:52:10 +1000 Subject: PCI/DOE: Expose DOE features via sysfs PCIe r6.0 added support for Data Object Exchange (DOE). When DOE is supported, the DOE Discovery Feature must be implemented per PCIe r6.1, sec 6.30.1.1. DOE allows a requester to obtain information about the other DOE features supported by the device. The kernel already queries the DOE features supported and caches the values. Expose the values in sysfs to allow user space to determine which DOE features are supported by the PCIe device. By exposing the information to userspace, tools like lspci can relay the information to users. By listing all of the supported features we can allow userspace to parse the list, which might include vendor specific features as well as yet to be supported features. As the DOE Discovery feature must always be supported we treat it as a special named attribute case. This allows the usual PCI attribute_group handling to correctly create the doe_features directory when registering pci_doe_sysfs_group (otherwise it doesn't and sysfs_add_file_to_group() will seg fault). After this patch is supported you can see something like this when attaching a DOE device: $ ls /sys/devices/pci0000:00/0000:00:02.0//doe* 0001:01 0001:02 doe_discovery Link: https://lore.kernel.org/r/20250306075211.1855177-3-alistair@alistair23.me Signed-off-by: Alistair Francis [bhelgaas: drop pci_doe_sysfs_init() stub return, make DEVICE_ATTR_RO(doe_discovery) static] Signed-off-by: Bjorn Helgaas Reviewed-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-pci | 29 ++++++ drivers/pci/doe.c | 153 ++++++++++++++++++++++++++++++++ drivers/pci/pci-sysfs.c | 3 + drivers/pci/pci.h | 9 ++ drivers/pci/probe.c | 2 + drivers/pci/remove.c | 1 + 6 files changed, 197 insertions(+) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index 5da6a14dc326..69f952fffec7 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -583,3 +583,32 @@ Description: enclosure-specific indications "specific0" to "specific7", hence the corresponding led class devices are unavailable if the DSM interface is used. + +What: /sys/bus/pci/devices/.../doe_features +Date: March 2025 +Contact: Linux PCI developers +Description: + This directory contains a list of the supported Data Object + Exchange (DOE) features. The features are the file name. + The contents of each file is the raw Vendor ID and data + object feature values. + + The value comes from the device and specifies the vendor and + data object type supported. The lower (RHS of the colon) is + the data object type in hex. The upper (LHS of the colon) + is the vendor ID. + + As all DOE devices must support the DOE discovery feature, + if DOE is supported you will at least see the doe_discovery + file, with this contents: + + # cat doe_features/doe_discovery + 0001:00 + + If the device supports other features you will see other + files as well. For example if CMA/SPDM and secure CMA/SPDM + are supported the doe_features directory will look like + this: + + # ls doe_features + 0001:01 0001:02 doe_discovery diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index f4508d75ce69..aae9a8a00406 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -14,10 +14,12 @@ #include #include +#include #include #include #include #include +#include #include #include "pci.h" @@ -47,6 +49,7 @@ * @wq: Wait queue for work item * @work_queue: Queue of pci_doe_work items * @flags: Bit array of PCI_DOE_FLAG_* flags + * @sysfs_attrs: Array of sysfs device attributes */ struct pci_doe_mb { struct pci_dev *pdev; @@ -56,6 +59,10 @@ struct pci_doe_mb { wait_queue_head_t wq; struct workqueue_struct *work_queue; unsigned long flags; + +#ifdef CONFIG_SYSFS + struct device_attribute *sysfs_attrs; +#endif }; struct pci_doe_feature { @@ -92,6 +99,152 @@ struct pci_doe_task { struct pci_doe_mb *doe_mb; }; +#ifdef CONFIG_SYSFS +static ssize_t doe_discovery_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "0001:00\n"); +} +static DEVICE_ATTR_RO(doe_discovery); + +static struct attribute *pci_doe_sysfs_feature_attrs[] = { + &dev_attr_doe_discovery.attr, + NULL +}; + +static bool pci_doe_features_sysfs_group_visible(struct kobject *kobj) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + + return !xa_empty(&pdev->doe_mbs); +} +DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(pci_doe_features_sysfs) + +const struct attribute_group pci_doe_sysfs_group = { + .name = "doe_features", + .attrs = pci_doe_sysfs_feature_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(pci_doe_features_sysfs), +}; + +static ssize_t pci_doe_sysfs_feature_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", attr->attr.name); +} + +static void pci_doe_sysfs_feature_remove(struct pci_dev *pdev, + struct pci_doe_mb *doe_mb) +{ + struct device_attribute *attrs = doe_mb->sysfs_attrs; + struct device *dev = &pdev->dev; + unsigned long i; + void *entry; + + if (!attrs) + return; + + doe_mb->sysfs_attrs = NULL; + xa_for_each(&doe_mb->feats, i, entry) { + if (attrs[i].show) + sysfs_remove_file_from_group(&dev->kobj, &attrs[i].attr, + pci_doe_sysfs_group.name); + kfree(attrs[i].attr.name); + } + kfree(attrs); +} + +static int pci_doe_sysfs_feature_populate(struct pci_dev *pdev, + struct pci_doe_mb *doe_mb) +{ + struct device *dev = &pdev->dev; + struct device_attribute *attrs; + unsigned long num_features = 0; + unsigned long vid, type; + unsigned long i; + void *entry; + int ret; + + xa_for_each(&doe_mb->feats, i, entry) + num_features++; + + attrs = kcalloc(num_features, sizeof(*attrs), GFP_KERNEL); + if (!attrs) { + pci_warn(pdev, "Failed allocating the device_attribute array\n"); + return -ENOMEM; + } + + doe_mb->sysfs_attrs = attrs; + xa_for_each(&doe_mb->feats, i, entry) { + sysfs_attr_init(&attrs[i].attr); + vid = xa_to_value(entry) >> 8; + type = xa_to_value(entry) & 0xFF; + + if (vid == PCI_VENDOR_ID_PCI_SIG && + type == PCI_DOE_FEATURE_DISCOVERY) { + + /* + * DOE Discovery, manually displayed by + * `dev_attr_doe_discovery` + */ + continue; + } + + attrs[i].attr.name = kasprintf(GFP_KERNEL, + "%04lx:%02lx", vid, type); + if (!attrs[i].attr.name) { + ret = -ENOMEM; + pci_warn(pdev, "Failed allocating the attribute name\n"); + goto fail; + } + + attrs[i].attr.mode = 0444; + attrs[i].show = pci_doe_sysfs_feature_show; + + ret = sysfs_add_file_to_group(&dev->kobj, &attrs[i].attr, + pci_doe_sysfs_group.name); + if (ret) { + attrs[i].show = NULL; + if (ret != -EEXIST) { + pci_warn(pdev, "Failed adding %s to sysfs group\n", + attrs[i].attr.name); + goto fail; + } else + kfree(attrs[i].attr.name); + } + } + + return 0; + +fail: + pci_doe_sysfs_feature_remove(pdev, doe_mb); + return ret; +} + +void pci_doe_sysfs_teardown(struct pci_dev *pdev) +{ + struct pci_doe_mb *doe_mb; + unsigned long index; + + xa_for_each(&pdev->doe_mbs, index, doe_mb) + pci_doe_sysfs_feature_remove(pdev, doe_mb); +} + +void pci_doe_sysfs_init(struct pci_dev *pdev) +{ + struct pci_doe_mb *doe_mb; + unsigned long index; + int ret; + + xa_for_each(&pdev->doe_mbs, index, doe_mb) { + ret = pci_doe_sysfs_feature_populate(pdev, doe_mb); + if (ret) + return; + } +} +#endif + static int pci_doe_wait(struct pci_doe_mb *doe_mb, unsigned long timeout) { if (wait_event_timeout(doe_mb->wq, diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index b46ce1a2c554..5e3874eaa3c1 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1804,6 +1804,9 @@ const struct attribute_group *pci_dev_attr_groups[] = { #endif #ifdef CONFIG_PCIEASPM &aspm_ctrl_attr_group, +#endif +#ifdef CONFIG_PCI_DOE + &pci_doe_sysfs_group, #endif NULL, }; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 01e51db8d285..9b956dc53adb 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -253,6 +253,7 @@ extern const struct attribute_group *pci_dev_groups[]; extern const struct attribute_group *pci_dev_attr_groups[]; extern const struct attribute_group *pcibus_groups[]; extern const struct attribute_group *pci_bus_groups[]; +extern const struct attribute_group pci_doe_sysfs_group; #else static inline int pci_create_sysfs_dev_files(struct pci_dev *pdev) { return 0; } static inline void pci_remove_sysfs_dev_files(struct pci_dev *pdev) { } @@ -456,6 +457,14 @@ static inline void pci_npem_create(struct pci_dev *dev) { } static inline void pci_npem_remove(struct pci_dev *dev) { } #endif +#if defined(CONFIG_PCI_DOE) && defined(CONFIG_SYSFS) +void pci_doe_sysfs_init(struct pci_dev *pci_dev); +void pci_doe_sysfs_teardown(struct pci_dev *pdev); +#else +static inline void pci_doe_sysfs_init(struct pci_dev *pdev) { } +static inline void pci_doe_sysfs_teardown(struct pci_dev *pdev) { } +#endif + /** * pci_dev_set_io_state - Set the new error state if possible. * diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b6536ed599c3..7cf1a727748d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2661,6 +2661,8 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) WARN_ON(ret < 0); pci_npem_create(dev); + + pci_doe_sysfs_init(dev); } struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn) diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index efc37fcb73e2..5813726214e6 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -53,6 +53,7 @@ static void pci_destroy_dev(struct pci_dev *dev) if (pci_dev_test_and_set_removed(dev)) return; + pci_doe_sysfs_teardown(dev); pci_npem_remove(dev); device_del(&dev->dev); -- cgit v1.2.3-59-g8ed1b From 6fc6ded50ffc09a5cb3a9ec22dd1976401ea0bbc Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Thu, 6 Mar 2025 17:52:11 +1000 Subject: PCI/DOE: Allow enabling DOE without CXL PCIe devices (not CXL) can support DOE as well, so allow DOE to be enabled even if CXL isn't. Link: https://lore.kernel.org/r/20250306075211.1855177-4-alistair@alistair23.me Signed-off-by: Alistair Francis Signed-off-by: Bjorn Helgaas Reviewed-by: Jonathan Cameron --- drivers/pci/Kconfig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 2fbd379923fd..fff4f3c6f6d3 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -122,7 +122,10 @@ config PCI_ATS bool config PCI_DOE - bool + bool "Enable PCI Data Object Exchange (DOE) support" + help + Say Y here if you want be able to communicate with PCIe DOE + mailboxes. config PCI_ECAM bool -- cgit v1.2.3-59-g8ed1b From 5f3de23d858edf5df89c397678ba492b96646df4 Mon Sep 17 00:00:00 2001 From: Thippeswamy Havalige Date: Fri, 28 Feb 2025 15:03:51 +0530 Subject: PCI: amd-mdb: Add AMD MDB Root Port driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for AMD MDB (Multimedia DMA Bridge) IP core as Root Port. The Versal2 devices include MDB Module. The integrated block for MDB along with the integrated bridge can function as PCIe Root Port controller at Gen5 32-GT/s operation per lane. Bridge supports error and INTx interrupts and are handled using platform specific interrupt line in Versal2. Signed-off-by: Thippeswamy Havalige Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20250228093351.923615-4-thippeswamy.havalige@amd.com [bhelgaas: only present on ARM64-based SoCs; squash Kconfig dependency on ARM64 from Geert Uytterhoeven : https://lore.kernel.org/r/eaef1dea7edcf146aa377d5e5c5c85a76ff56bae.1742306383.git.geert+renesas@glider.be] Signed-off-by: Bjorn Helgaas [kwilczynski: commit log, code comments and error messages clean-up, drop redundant "depends on PCI" from Kconfig, expose the error code as part of error messages where appropriatie, change "depends on" expression to match existing style from other drivers] Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/dwc/Kconfig | 11 + drivers/pci/controller/dwc/Makefile | 1 + drivers/pci/controller/dwc/pcie-amd-mdb.c | 476 ++++++++++++++++++++++++++++++ 3 files changed, 488 insertions(+) create mode 100644 drivers/pci/controller/dwc/pcie-amd-mdb.c (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index b6d6778b0698..8803fb8767a5 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -27,6 +27,17 @@ config PCIE_AL required only for DT-based platforms. ACPI platforms with the Annapurna Labs PCIe controller don't need to enable this. +config PCIE_AMD_MDB + bool "AMD MDB Versal2 PCIe controller" + depends on OF && (ARM64 || COMPILE_TEST) + depends on PCI_MSI + select PCIE_DW_HOST + help + Say Y here if you want to enable PCIe controller support on AMD + Versal2 SoCs. The AMD MDB Versal2 PCIe controller is based on + DesignWare IP and therefore the driver re-uses the DesignWare + core functions to implement the driver. + config PCI_MESON tristate "Amlogic Meson PCIe controller" default m if ARCH_MESON diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index a8308d9ea986..ae27eda6ec5e 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o +obj-$(CONFIG_PCIE_AMD_MDB) += pcie-amd-mdb.o obj-$(CONFIG_PCIE_BT1) += pcie-bt1.o obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o diff --git a/drivers/pci/controller/dwc/pcie-amd-mdb.c b/drivers/pci/controller/dwc/pcie-amd-mdb.c new file mode 100644 index 000000000000..4eb2a4e8189d --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-amd-mdb.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for AMD MDB PCIe Bridge + * + * Copyright (C) 2024-2025, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +#define AMD_MDB_TLP_IR_STATUS_MISC 0x4C0 +#define AMD_MDB_TLP_IR_MASK_MISC 0x4C4 +#define AMD_MDB_TLP_IR_ENABLE_MISC 0x4C8 +#define AMD_MDB_TLP_IR_DISABLE_MISC 0x4CC + +#define AMD_MDB_TLP_PCIE_INTX_MASK GENMASK(23, 16) + +#define AMD_MDB_PCIE_INTR_INTX_ASSERT(x) BIT((x) * 2) + +/* Interrupt registers definitions. */ +#define AMD_MDB_PCIE_INTR_CMPL_TIMEOUT 15 +#define AMD_MDB_PCIE_INTR_INTX 16 +#define AMD_MDB_PCIE_INTR_PM_PME_RCVD 24 +#define AMD_MDB_PCIE_INTR_PME_TO_ACK_RCVD 25 +#define AMD_MDB_PCIE_INTR_MISC_CORRECTABLE 26 +#define AMD_MDB_PCIE_INTR_NONFATAL 27 +#define AMD_MDB_PCIE_INTR_FATAL 28 + +#define IMR(x) BIT(AMD_MDB_PCIE_INTR_ ##x) +#define AMD_MDB_PCIE_IMR_ALL_MASK \ + ( \ + IMR(CMPL_TIMEOUT) | \ + IMR(PM_PME_RCVD) | \ + IMR(PME_TO_ACK_RCVD) | \ + IMR(MISC_CORRECTABLE) | \ + IMR(NONFATAL) | \ + IMR(FATAL) | \ + AMD_MDB_TLP_PCIE_INTX_MASK \ + ) + +/** + * struct amd_mdb_pcie - PCIe port information + * @pci: DesignWare PCIe controller structure + * @slcr: MDB System Level Control and Status Register (SLCR) base + * @intx_domain: INTx IRQ domain pointer + * @mdb_domain: MDB IRQ domain pointer + * @intx_irq: INTx IRQ interrupt number + */ +struct amd_mdb_pcie { + struct dw_pcie pci; + void __iomem *slcr; + struct irq_domain *intx_domain; + struct irq_domain *mdb_domain; + int intx_irq; +}; + +static const struct dw_pcie_host_ops amd_mdb_pcie_host_ops = { +}; + +static void amd_mdb_intx_irq_mask(struct irq_data *data) +{ + struct amd_mdb_pcie *pcie = irq_data_get_irq_chip_data(data); + struct dw_pcie *pci = &pcie->pci; + struct dw_pcie_rp *port = &pci->pp; + unsigned long flags; + u32 val; + + raw_spin_lock_irqsave(&port->lock, flags); + val = FIELD_PREP(AMD_MDB_TLP_PCIE_INTX_MASK, + AMD_MDB_PCIE_INTR_INTX_ASSERT(data->hwirq)); + + /* + * Writing '1' to a bit in AMD_MDB_TLP_IR_DISABLE_MISC disables that + * interrupt, writing '0' has no effect. + */ + writel_relaxed(val, pcie->slcr + AMD_MDB_TLP_IR_DISABLE_MISC); + raw_spin_unlock_irqrestore(&port->lock, flags); +} + +static void amd_mdb_intx_irq_unmask(struct irq_data *data) +{ + struct amd_mdb_pcie *pcie = irq_data_get_irq_chip_data(data); + struct dw_pcie *pci = &pcie->pci; + struct dw_pcie_rp *port = &pci->pp; + unsigned long flags; + u32 val; + + raw_spin_lock_irqsave(&port->lock, flags); + val = FIELD_PREP(AMD_MDB_TLP_PCIE_INTX_MASK, + AMD_MDB_PCIE_INTR_INTX_ASSERT(data->hwirq)); + + /* + * Writing '1' to a bit in AMD_MDB_TLP_IR_ENABLE_MISC enables that + * interrupt, writing '0' has no effect. + */ + writel_relaxed(val, pcie->slcr + AMD_MDB_TLP_IR_ENABLE_MISC); + raw_spin_unlock_irqrestore(&port->lock, flags); +} + +static struct irq_chip amd_mdb_intx_irq_chip = { + .name = "AMD MDB INTx", + .irq_mask = amd_mdb_intx_irq_mask, + .irq_unmask = amd_mdb_intx_irq_unmask, +}; + +/** + * amd_mdb_pcie_intx_map - Set the handler for the INTx and mark IRQ as valid + * @domain: IRQ domain + * @irq: Virtual IRQ number + * @hwirq: Hardware interrupt number + * + * Return: Always returns '0'. + */ +static int amd_mdb_pcie_intx_map(struct irq_domain *domain, + unsigned int irq, irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &amd_mdb_intx_irq_chip, + handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + irq_set_status_flags(irq, IRQ_LEVEL); + + return 0; +} + +/* INTx IRQ domain operations. */ +static const struct irq_domain_ops amd_intx_domain_ops = { + .map = amd_mdb_pcie_intx_map, +}; + +static irqreturn_t dw_pcie_rp_intx(int irq, void *args) +{ + struct amd_mdb_pcie *pcie = args; + unsigned long val; + int i, int_status; + + val = readl_relaxed(pcie->slcr + AMD_MDB_TLP_IR_STATUS_MISC); + int_status = FIELD_GET(AMD_MDB_TLP_PCIE_INTX_MASK, val); + + for (i = 0; i < PCI_NUM_INTX; i++) { + if (int_status & AMD_MDB_PCIE_INTR_INTX_ASSERT(i)) + generic_handle_domain_irq(pcie->intx_domain, i); + } + + return IRQ_HANDLED; +} + +#define _IC(x, s)[AMD_MDB_PCIE_INTR_ ## x] = { __stringify(x), s } + +static const struct { + const char *sym; + const char *str; +} intr_cause[32] = { + _IC(CMPL_TIMEOUT, "Completion timeout"), + _IC(PM_PME_RCVD, "PM_PME message received"), + _IC(PME_TO_ACK_RCVD, "PME_TO_ACK message received"), + _IC(MISC_CORRECTABLE, "Correctable error message"), + _IC(NONFATAL, "Non fatal error message"), + _IC(FATAL, "Fatal error message"), +}; + +static void amd_mdb_event_irq_mask(struct irq_data *d) +{ + struct amd_mdb_pcie *pcie = irq_data_get_irq_chip_data(d); + struct dw_pcie *pci = &pcie->pci; + struct dw_pcie_rp *port = &pci->pp; + unsigned long flags; + u32 val; + + raw_spin_lock_irqsave(&port->lock, flags); + val = BIT(d->hwirq); + writel_relaxed(val, pcie->slcr + AMD_MDB_TLP_IR_DISABLE_MISC); + raw_spin_unlock_irqrestore(&port->lock, flags); +} + +static void amd_mdb_event_irq_unmask(struct irq_data *d) +{ + struct amd_mdb_pcie *pcie = irq_data_get_irq_chip_data(d); + struct dw_pcie *pci = &pcie->pci; + struct dw_pcie_rp *port = &pci->pp; + unsigned long flags; + u32 val; + + raw_spin_lock_irqsave(&port->lock, flags); + val = BIT(d->hwirq); + writel_relaxed(val, pcie->slcr + AMD_MDB_TLP_IR_ENABLE_MISC); + raw_spin_unlock_irqrestore(&port->lock, flags); +} + +static struct irq_chip amd_mdb_event_irq_chip = { + .name = "AMD MDB RC-Event", + .irq_mask = amd_mdb_event_irq_mask, + .irq_unmask = amd_mdb_event_irq_unmask, +}; + +static int amd_mdb_pcie_event_map(struct irq_domain *domain, + unsigned int irq, irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &amd_mdb_event_irq_chip, + handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + irq_set_status_flags(irq, IRQ_LEVEL); + + return 0; +} + +static const struct irq_domain_ops event_domain_ops = { + .map = amd_mdb_pcie_event_map, +}; + +static irqreturn_t amd_mdb_pcie_event(int irq, void *args) +{ + struct amd_mdb_pcie *pcie = args; + unsigned long val; + int i; + + val = readl_relaxed(pcie->slcr + AMD_MDB_TLP_IR_STATUS_MISC); + val &= ~readl_relaxed(pcie->slcr + AMD_MDB_TLP_IR_MASK_MISC); + for_each_set_bit(i, &val, 32) + generic_handle_domain_irq(pcie->mdb_domain, i); + writel_relaxed(val, pcie->slcr + AMD_MDB_TLP_IR_STATUS_MISC); + + return IRQ_HANDLED; +} + +static void amd_mdb_pcie_free_irq_domains(struct amd_mdb_pcie *pcie) +{ + if (pcie->intx_domain) { + irq_domain_remove(pcie->intx_domain); + pcie->intx_domain = NULL; + } + + if (pcie->mdb_domain) { + irq_domain_remove(pcie->mdb_domain); + pcie->mdb_domain = NULL; + } +} + +static int amd_mdb_pcie_init_port(struct amd_mdb_pcie *pcie) +{ + unsigned long val; + + /* Disable all TLP interrupts. */ + writel_relaxed(AMD_MDB_PCIE_IMR_ALL_MASK, + pcie->slcr + AMD_MDB_TLP_IR_DISABLE_MISC); + + /* Clear pending TLP interrupts. */ + val = readl_relaxed(pcie->slcr + AMD_MDB_TLP_IR_STATUS_MISC); + val &= AMD_MDB_PCIE_IMR_ALL_MASK; + writel_relaxed(val, pcie->slcr + AMD_MDB_TLP_IR_STATUS_MISC); + + /* Enable all TLP interrupts. */ + writel_relaxed(AMD_MDB_PCIE_IMR_ALL_MASK, + pcie->slcr + AMD_MDB_TLP_IR_ENABLE_MISC); + + return 0; +} + +/** + * amd_mdb_pcie_init_irq_domains - Initialize IRQ domain + * @pcie: PCIe port information + * @pdev: Platform device + * + * Return: Returns '0' on success and error value on failure. + */ +static int amd_mdb_pcie_init_irq_domains(struct amd_mdb_pcie *pcie, + struct platform_device *pdev) +{ + struct dw_pcie *pci = &pcie->pci; + struct dw_pcie_rp *pp = &pci->pp; + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node; + int err; + + pcie_intc_node = of_get_next_child(node, NULL); + if (!pcie_intc_node) { + dev_err(dev, "No PCIe Intc node found\n"); + return -ENODEV; + } + + pcie->mdb_domain = irq_domain_add_linear(pcie_intc_node, 32, + &event_domain_ops, pcie); + if (!pcie->mdb_domain) { + err = -ENOMEM; + dev_err(dev, "Failed to add MDB domain\n"); + goto out; + } + + irq_domain_update_bus_token(pcie->mdb_domain, DOMAIN_BUS_NEXUS); + + pcie->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, + &amd_intx_domain_ops, pcie); + if (!pcie->intx_domain) { + err = -ENOMEM; + dev_err(dev, "Failed to add INTx domain\n"); + goto mdb_out; + } + + of_node_put(pcie_intc_node); + irq_domain_update_bus_token(pcie->intx_domain, DOMAIN_BUS_WIRED); + + raw_spin_lock_init(&pp->lock); + + return 0; +mdb_out: + amd_mdb_pcie_free_irq_domains(pcie); +out: + of_node_put(pcie_intc_node); + return err; +} + +static irqreturn_t amd_mdb_pcie_intr_handler(int irq, void *args) +{ + struct amd_mdb_pcie *pcie = args; + struct device *dev; + struct irq_data *d; + + dev = pcie->pci.dev; + + /* + * In the future, error reporting will be hooked to the AER subsystem. + * Currently, the driver prints a warning message to the user. + */ + d = irq_domain_get_irq_data(pcie->mdb_domain, irq); + if (intr_cause[d->hwirq].str) + dev_warn(dev, "%s\n", intr_cause[d->hwirq].str); + else + dev_warn_once(dev, "Unknown IRQ %ld\n", d->hwirq); + + return IRQ_HANDLED; +} + +static int amd_mdb_setup_irq(struct amd_mdb_pcie *pcie, + struct platform_device *pdev) +{ + struct dw_pcie *pci = &pcie->pci; + struct dw_pcie_rp *pp = &pci->pp; + struct device *dev = &pdev->dev; + int i, irq, err; + + amd_mdb_pcie_init_port(pcie); + + pp->irq = platform_get_irq(pdev, 0); + if (pp->irq < 0) + return pp->irq; + + for (i = 0; i < ARRAY_SIZE(intr_cause); i++) { + if (!intr_cause[i].str) + continue; + + irq = irq_create_mapping(pcie->mdb_domain, i); + if (!irq) { + dev_err(dev, "Failed to map MDB domain interrupt\n"); + return -ENOMEM; + } + + err = devm_request_irq(dev, irq, amd_mdb_pcie_intr_handler, + IRQF_NO_THREAD, intr_cause[i].sym, pcie); + if (err) { + dev_err(dev, "Failed to request IRQ %d, err=%d\n", + irq, err); + return err; + } + } + + pcie->intx_irq = irq_create_mapping(pcie->mdb_domain, + AMD_MDB_PCIE_INTR_INTX); + if (!pcie->intx_irq) { + dev_err(dev, "Failed to map INTx interrupt\n"); + return -ENXIO; + } + + err = devm_request_irq(dev, pcie->intx_irq, dw_pcie_rp_intx, + IRQF_NO_THREAD, NULL, pcie); + if (err) { + dev_err(dev, "Failed to request INTx IRQ %d, err=%d\n", + irq, err); + return err; + } + + /* Plug the main event handler. */ + err = devm_request_irq(dev, pp->irq, amd_mdb_pcie_event, IRQF_NO_THREAD, + "amd_mdb pcie_irq", pcie); + if (err) { + dev_err(dev, "Failed to request event IRQ %d, err=%d\n", + pp->irq, err); + return err; + } + + return 0; +} + +static int amd_mdb_add_pcie_port(struct amd_mdb_pcie *pcie, + struct platform_device *pdev) +{ + struct dw_pcie *pci = &pcie->pci; + struct dw_pcie_rp *pp = &pci->pp; + struct device *dev = &pdev->dev; + int err; + + pcie->slcr = devm_platform_ioremap_resource_byname(pdev, "slcr"); + if (IS_ERR(pcie->slcr)) + return PTR_ERR(pcie->slcr); + + err = amd_mdb_pcie_init_irq_domains(pcie, pdev); + if (err) + return err; + + err = amd_mdb_setup_irq(pcie, pdev); + if (err) { + dev_err(dev, "Failed to set up interrupts, err=%d\n", err); + goto out; + } + + pp->ops = &amd_mdb_pcie_host_ops; + + err = dw_pcie_host_init(pp); + if (err) { + dev_err(dev, "Failed to initialize host, err=%d\n", err); + goto out; + } + + return 0; + +out: + amd_mdb_pcie_free_irq_domains(pcie); + return err; +} + +static int amd_mdb_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct amd_mdb_pcie *pcie; + struct dw_pcie *pci; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pci = &pcie->pci; + pci->dev = dev; + + platform_set_drvdata(pdev, pcie); + + return amd_mdb_add_pcie_port(pcie, pdev); +} + +static const struct of_device_id amd_mdb_pcie_of_match[] = { + { + .compatible = "amd,versal2-mdb-host", + }, + {}, +}; + +static struct platform_driver amd_mdb_pcie_driver = { + .driver = { + .name = "amd-mdb-pcie", + .of_match_table = amd_mdb_pcie_of_match, + .suppress_bind_attrs = true, + }, + .probe = amd_mdb_pcie_probe, +}; + +builtin_platform_driver(amd_mdb_pcie_driver); -- cgit v1.2.3-59-g8ed1b From 2d72d81caccad516ece9f91f86ac65ff1f2c68a2 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 17 Mar 2025 14:34:56 +0000 Subject: PCI: brcmstb: Make const read-only arrays static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't populate the const read-only arrays "data" and "regs" on the stack at run time, instead make them static. Signed-off-by: Colin Ian King Reviewed-by: Florian Fainelli [kwilczynski: commit log, wrap overly long line to 80 columns] Signed-off-by: Krzysztof Wilczyński Link: https://lore.kernel.org/r/20250317143456.477901-1-colin.i.king@gmail.com --- drivers/pci/controller/pcie-brcmstb.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index df944453c53a..942c596e65bb 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -832,8 +832,9 @@ static int brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val) static int brcm_pcie_post_setup_bcm2712(struct brcm_pcie *pcie) { - const u16 data[] = { 0x50b9, 0xbda1, 0x0094, 0x97b4, 0x5030, 0x5030, 0x0007 }; - const u8 regs[] = { 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1e }; + static const u16 data[] = { 0x50b9, 0xbda1, 0x0094, 0x97b4, 0x5030, + 0x5030, 0x0007 }; + static const u8 regs[] = { 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1e }; int ret, i; u32 tmp; -- cgit v1.2.3-59-g8ed1b From 9e141923cf86b2e1c83d21b87fb4de3d14a20c99 Mon Sep 17 00:00:00 2001 From: Thippeswamy Havalige Date: Mon, 17 Mar 2025 18:11:36 +0530 Subject: PCI: xilinx-cpm: Add cpm_csr register mapping for CPM5_HOST1 variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the CPM5 check to include CPM5_HOST1 variant. Previously, only CPM5 was considered when mapping the "cpm_csr" register. With this change, CPM5_HOST1 is also supported, ensuring proper resource mapping for this variant. Signed-off-by: Thippeswamy Havalige [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński Link: https://lore.kernel.org/r/20250317124136.1317723-1-thippeswamy.havalige@amd.com --- drivers/pci/controller/pcie-xilinx-cpm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/controller/pcie-xilinx-cpm.c b/drivers/pci/controller/pcie-xilinx-cpm.c index d0ab187d917f..13ca493d22bd 100644 --- a/drivers/pci/controller/pcie-xilinx-cpm.c +++ b/drivers/pci/controller/pcie-xilinx-cpm.c @@ -542,7 +542,8 @@ static int xilinx_cpm_pcie_parse_dt(struct xilinx_cpm_pcie *port, if (IS_ERR(port->cfg)) return PTR_ERR(port->cfg); - if (port->variant->version == CPM5) { + if (port->variant->version == CPM5 || + port->variant->version == CPM5_HOST1) { port->reg_base = devm_platform_ioremap_resource_byname(pdev, "cpm_csr"); if (IS_ERR(port->reg_base)) -- cgit v1.2.3-59-g8ed1b From 667f053b05f00a007738cd7ed6fa1901de19dc7e Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 22 Mar 2025 19:52:08 +0100 Subject: PCI/bwctrl: Fix NULL pointer dereference on bus number exhaustion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When BIOS neglects to assign bus numbers to PCI bridges, the kernel attempts to correct that during PCI device enumeration. If it runs out of bus numbers, no pci_bus is allocated and the "subordinate" pointer in the bridge's pci_dev remains NULL. The PCIe bandwidth controller erroneously does not check for a NULL subordinate pointer and dereferences it on probe. Bandwidth control of unusable devices below the bridge is of questionable utility, so simply error out instead. This mirrors what PCIe hotplug does since commit 62e4492c3063 ("PCI: Prevent NULL dereference during pciehp probe"). The PCI core emits a message with KERN_INFO severity if it has run out of bus numbers. PCIe hotplug emits an additional message with KERN_ERR severity to inform the user that hotplug functionality is disabled at the bridge. A similar message for bandwidth control does not seem merited, given that its only purpose so far is to expose an up-to-date link speed in sysfs and throttle the link speed on certain laptops with limited Thermal Design Power. So error out silently. User-visible messages: pci 0000:16:02.0: bridge configuration invalid ([bus 00-00]), reconfiguring [...] pci_bus 0000:45: busn_res: [bus 45-74] end is updated to 74 pci 0000:16:02.0: devices behind bridge are unusable because [bus 45-74] cannot be assigned for them [...] pcieport 0000:16:02.0: pciehp: Hotplug bridge without secondary bus, ignoring [...] BUG: kernel NULL pointer dereference RIP: pcie_update_link_speed pcie_bwnotif_enable pcie_bwnotif_probe pcie_port_probe_service really_probe Fixes: 665745f27487 ("PCI/bwctrl: Re-add BW notification portdrv as PCIe BW controller") Reported-by: Wouter Bijlsma Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219906 Signed-off-by: Lukas Wunner Signed-off-by: Krzysztof Wilczyński Tested-by: Wouter Bijlsma Cc: stable@vger.kernel.org # v6.13+ Link: https://lore.kernel.org/r/3b6c8d973aedc48860640a9d75d20528336f1f3c.1742669372.git.lukas@wunner.de --- drivers/pci/pcie/bwctrl.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/pcie/bwctrl.c b/drivers/pci/pcie/bwctrl.c index 58ba8142c9a3..d8d2aa85a229 100644 --- a/drivers/pci/pcie/bwctrl.c +++ b/drivers/pci/pcie/bwctrl.c @@ -294,6 +294,10 @@ static int pcie_bwnotif_probe(struct pcie_device *srv) struct pci_dev *port = srv->port; int ret; + /* Can happen if we run out of bus numbers during enumeration. */ + if (!port->subordinate) + return -ENODEV; + struct pcie_bwctrl_data *data = devm_kzalloc(&srv->device, sizeof(*data), GFP_KERNEL); if (!data) -- cgit v1.2.3-59-g8ed1b From 9de3f3cd470b25fafd150cf243e3a51e40b036ee Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sat, 15 Mar 2025 15:15:40 -0500 Subject: PCI: dwc: Add dw_pcie_parent_bus_offset() Return the offset from CPU physical address to the parent bus address of the specified element of the devicetree 'reg' property. [bhelgaas: cpu_phy_addr -> cpu_phys_addr, return offset, split .cpu_addr_fixup() checking and debug to separate patch] Link: https://lore.kernel.org/r/20250315201548.858189-6-helgaas@kernel.org Signed-off-by: Frank Li Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware.c | 23 +++++++++++++++++++++++ drivers/pci/controller/dwc/pcie-designware.h | 3 +++ 2 files changed, 26 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 9d0a5f75effc..27b464a405a4 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -1105,3 +1106,25 @@ void dw_pcie_setup(struct dw_pcie *pci) dw_pcie_link_set_max_link_width(pci, pci->num_lanes); } + +resource_size_t dw_pcie_parent_bus_offset(struct dw_pcie *pci, + const char *reg_name, + resource_size_t cpu_phys_addr) +{ + struct device *dev = pci->dev; + struct device_node *np = dev->of_node; + int index; + u64 reg_addr; + + /* Look up reg_name address on parent bus */ + index = of_property_match_string(np, "reg-names", reg_name); + + if (index < 0) { + dev_err(dev, "No %s in devicetree \"reg\" property\n", reg_name); + return 0; + } + + of_property_read_reg(np, index, ®_addr, NULL); + + return cpu_phys_addr - reg_addr; +} diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index d0d8c622a6e8..16548b01347d 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -500,6 +500,9 @@ void dw_pcie_setup(struct dw_pcie *pci); void dw_pcie_iatu_detect(struct dw_pcie *pci); int dw_pcie_edma_detect(struct dw_pcie *pci); void dw_pcie_edma_remove(struct dw_pcie *pci); +resource_size_t dw_pcie_parent_bus_offset(struct dw_pcie *pci, + const char *reg_name, + resource_size_t cpu_phy_addr); static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) { -- cgit v1.2.3-59-g8ed1b From 3b69e1d3815f376eae785a71705b5c2a621eaf19 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sat, 15 Mar 2025 15:15:41 -0500 Subject: PCI: dwc: Add dw_pcie_parent_bus_offset() checking and debug dw_pcie_parent_bus_offset() looks up the parent bus address of a PCI controller 'reg' property in devicetree. If implemented, .cpu_addr_fixup() is a hard-coded way to get the parent bus address corresponding to a CPU physical address. Add debug code to compare the address from .cpu_addr_fixup() with the address from devicetree. If they match, warn that .cpu_addr_fixup() is redundant and should be removed; if they differ, warn that something is wrong with the devicetree. If .cpu_addr_fixup() is not implemented, the parent bus address should be identical to the CPU physical address because we previously ignored the parent bus address from devicetree. If the devicetree has a different parent bus address, warn about it being broken. [bhelgaas: split debug to separate patch for easier future revert, commit log] Link: https://lore.kernel.org/r/20250315201548.858189-7-helgaas@kernel.org Signed-off-by: Frank Li [bhelgaas: squash Ioana Ciornei fix for NULL pointer deref when driver doesn't supply dw_pcie_ops, e.g., layerscape-pcie https://lore.kernel.org/r/20250319134339.3114817-1-ioana.ciornei@nxp.com] Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware.c | 40 +++++++++++++++++++++++++++- drivers/pci/controller/dwc/pcie-designware.h | 13 +++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 27b464a405a4..4b442d1aa55b 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -1114,7 +1114,8 @@ resource_size_t dw_pcie_parent_bus_offset(struct dw_pcie *pci, struct device *dev = pci->dev; struct device_node *np = dev->of_node; int index; - u64 reg_addr; + u64 reg_addr, fixup_addr; + u64 (*fixup)(struct dw_pcie *pcie, u64 cpu_addr); /* Look up reg_name address on parent bus */ index = of_property_match_string(np, "reg-names", reg_name); @@ -1126,5 +1127,42 @@ resource_size_t dw_pcie_parent_bus_offset(struct dw_pcie *pci, of_property_read_reg(np, index, ®_addr, NULL); + fixup = pci->ops ? pci->ops->cpu_addr_fixup : NULL; + if (fixup) { + fixup_addr = fixup(pci, cpu_phys_addr); + if (reg_addr == fixup_addr) { + dev_info(dev, "%s reg[%d] %#010llx == %#010llx == fixup(cpu %#010llx); %ps is redundant with this devicetree\n", + reg_name, index, reg_addr, fixup_addr, + (unsigned long long) cpu_phys_addr, fixup); + } else { + dev_warn(dev, "%s reg[%d] %#010llx != %#010llx == fixup(cpu %#010llx); devicetree is broken\n", + reg_name, index, reg_addr, fixup_addr, + (unsigned long long) cpu_phys_addr); + reg_addr = fixup_addr; + } + + return cpu_phys_addr - reg_addr; + } + + if (pci->use_parent_dt_ranges) { + + /* + * This platform once had a fixup, presumably because it + * translates between CPU and PCI controller addresses. + * Log a note if devicetree didn't describe a translation. + */ + if (reg_addr == cpu_phys_addr) + dev_info(dev, "%s reg[%d] %#010llx == cpu %#010llx\n; no fixup was ever needed for this devicetree\n", + reg_name, index, reg_addr, + (unsigned long long) cpu_phys_addr); + } else { + if (reg_addr != cpu_phys_addr) { + dev_warn(dev, "%s reg[%d] %#010llx != cpu %#010llx; no fixup and devicetree \"ranges\" is broken, assuming no translation\n", + reg_name, index, reg_addr, + (unsigned long long) cpu_phys_addr); + return 0; + } + } + return cpu_phys_addr - reg_addr; } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 16548b01347d..f08d2852cfd5 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -465,6 +465,19 @@ struct dw_pcie { struct reset_control_bulk_data core_rsts[DW_PCIE_NUM_CORE_RSTS]; struct gpio_desc *pe_rst; bool suspended; + + /* + * If iATU input addresses are offset from CPU physical addresses, + * we previously required .cpu_addr_fixup() to convert them. We + * now rely on the devicetree instead. If .cpu_addr_fixup() + * exists, we compare its results with devicetree. + * + * If .cpu_addr_fixup() does not exist, we assume the offset is + * zero and warn if devicetree claims otherwise. If we know all + * devicetrees correctly describe the offset, set + * use_parent_dt_ranges to true to avoid this warning. + */ + bool use_parent_dt_ranges; }; #define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp) -- cgit v1.2.3-59-g8ed1b From 7db02f725df44b072e89f6b4ead4145d53be3c0f Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sat, 15 Mar 2025 15:15:42 -0500 Subject: PCI: dwc: Use devicetree 'reg[config]' to derive CPU -> ATU addr offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'ranges' property of a PCI controller's parent can indicate address translation information. Most system use 1:1 map between CPU physical and PCI controller input addresses. But some hardware, like i.MX8QXP, doesn't use 1:1 map. See below diagram: ┌─────────┐ ┌────────────┐ ┌─────┐ │ │ IA: 0x8ff8_0000 │ │ │ CPU ├───►│ ┌────►├─────────────────┐ │ PCI │ └─────┘ │ │ │ IA: 0x8ff0_0000 │ │ │ CPU Addr │ │ ┌─►├─────────────┐ │ │ Controller │ 0x7ff8_0000─┼───┘ │ │ │ │ │ │ │ │ │ │ │ │ │ PCI Addr 0x7ff0_0000─┼──────┘ │ │ └──► IOSpace ─┼────────────► │ │ │ │ │ 0 0x7000_0000─┼────────►├─────────┐ │ │ │ └─────────┘ │ └──────► CfgSpace ─┼────────────► Bus Fabric │ │ │ 0 │ │ │ └──────────► MemSpace ─┼────────────► IA: 0x8000_0000 │ │ 0x8000_0000 └────────────┘ bus@5f000000 { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; ranges = <0x80000000 0x0 0x70000000 0x10000000>; pcie@5f010000 { compatible = "fsl,imx8q-pcie"; reg = <0x5f010000 0x10000>, <0x8ff00000 0x80000>; reg-names = "dbi", "config"; ... }; }; Intermediate address (IA) here means the PCIe controller input address. The pcie@5f010000 'reg[config]' address is the parent bus (PCIe controller input) address of CfgSpace. The ATU in MemSpace is not explicitly described via devicetree, so we assume the offset from CPU address to intermediate MemSpace address is the same as that for CfgSpace. We could use bus@5f000000 'ranges' for the same purpose. Set parent_bus_offset using dw_pcie_init_parent_bus_offset(). The parent_bus_offset is not used yet, so no functional change intended. Link: https://lore.kernel.org/r/20250315201548.858189-8-helgaas@kernel.org Signed-off-by: Frank Li Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware-host.c | 6 ++++++ drivers/pci/controller/dwc/pcie-designware.h | 1 + 2 files changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 9ce06b1ee266..9e38ac7d1bcb 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -452,6 +452,12 @@ static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp) pp->io_base = pci_pio_to_address(win->res->start); } + /* + * visconti_pcie_cpu_addr_fixup() uses pp->io_base, so we have to + * call dw_pcie_parent_bus_offset() after setting pp->io_base. + */ + pci->parent_bus_offset = dw_pcie_parent_bus_offset(pci, "config", + pp->cfg0_base); return 0; } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index f08d2852cfd5..741c46926ce2 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -445,6 +445,7 @@ struct dw_pcie { void __iomem *atu_base; resource_size_t atu_phys_addr; size_t atu_size; + resource_size_t parent_bus_offset; u32 num_ib_windows; u32 num_ob_windows; u32 region_align; -- cgit v1.2.3-59-g8ed1b From 92eb132ad1b27a6850acadfe4b3f7b25ada749cb Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 15 Mar 2025 15:15:43 -0500 Subject: PCI: dwc: ep: Call epc_create() early in dw_pcie_ep_init() Move devm_pci_epc_create() to the beginning of dw_pcie_ep_init(). devm_pci_epc_create() is generic code that doesn't depend on any DWC resource, so moving it earlier keeps all the subsequent devicetree-related code together. Link: https://lore.kernel.org/r/20250315201548.858189-9-helgaas@kernel.org Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li --- drivers/pci/controller/dwc/pcie-designware-ep.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 80ac2f9e88eb..100d26466f05 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -904,6 +904,15 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) INIT_LIST_HEAD(&ep->func_list); + epc = devm_pci_epc_create(dev, &epc_ops); + if (IS_ERR(epc)) { + dev_err(dev, "Failed to create epc device\n"); + return PTR_ERR(epc); + } + + ep->epc = epc; + epc_set_drvdata(epc, ep); + ret = dw_pcie_get_resources(pci); if (ret) return ret; @@ -918,15 +927,6 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) if (ep->ops->pre_init) ep->ops->pre_init(ep); - epc = devm_pci_epc_create(dev, &epc_ops); - if (IS_ERR(epc)) { - dev_err(dev, "Failed to create epc device\n"); - return PTR_ERR(epc); - } - - ep->epc = epc; - epc_set_drvdata(epc, ep); - ret = of_property_read_u8(np, "max-functions", &epc->max_functions); if (ret < 0) epc->max_functions = 1; -- cgit v1.2.3-59-g8ed1b From d7ae671eba8b3aafed6af47a087d400eefbcff94 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 15 Mar 2025 15:15:44 -0500 Subject: PCI: dwc: ep: Consolidate devicetree handling in dw_pcie_ep_get_resources() Consolidate devicetree resource handling in dw_pcie_ep_get_resources(). No functional change intended. Link: https://lore.kernel.org/r/20250315201548.858189-10-helgaas@kernel.org Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li --- drivers/pci/controller/dwc/pcie-designware-ep.c | 44 ++++++++++++++++--------- 1 file changed, 29 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 100d26466f05..2db834345ec2 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -883,6 +883,34 @@ void dw_pcie_ep_linkdown(struct dw_pcie_ep *ep) } EXPORT_SYMBOL_GPL(dw_pcie_ep_linkdown); +static int dw_pcie_ep_get_resources(struct dw_pcie_ep *ep) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct device *dev = pci->dev; + struct platform_device *pdev = to_platform_device(dev); + struct device_node *np = dev->of_node; + struct pci_epc *epc = ep->epc; + struct resource *res; + int ret; + + ret = dw_pcie_get_resources(pci); + if (ret) + return ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); + if (!res) + return -EINVAL; + + ep->phys_base = res->start; + ep->addr_size = resource_size(res); + + ret = of_property_read_u8(np, "max-functions", &epc->max_functions); + if (ret < 0) + epc->max_functions = 1; + + return 0; +} + /** * dw_pcie_ep_init - Initialize the endpoint device * @ep: DWC EP device @@ -895,12 +923,9 @@ EXPORT_SYMBOL_GPL(dw_pcie_ep_linkdown); int dw_pcie_ep_init(struct dw_pcie_ep *ep) { int ret; - struct resource *res; struct pci_epc *epc; struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct device *dev = pci->dev; - struct platform_device *pdev = to_platform_device(dev); - struct device_node *np = dev->of_node; INIT_LIST_HEAD(&ep->func_list); @@ -913,24 +938,13 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) ep->epc = epc; epc_set_drvdata(epc, ep); - ret = dw_pcie_get_resources(pci); + ret = dw_pcie_ep_get_resources(ep); if (ret) return ret; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); - if (!res) - return -EINVAL; - - ep->phys_base = res->start; - ep->addr_size = resource_size(res); - if (ep->ops->pre_init) ep->ops->pre_init(ep); - ret = of_property_read_u8(np, "max-functions", &epc->max_functions); - if (ret < 0) - epc->max_functions = 1; - ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size, ep->page_size); if (ret < 0) { -- cgit v1.2.3-59-g8ed1b From f28b3c9c429d8841adecfca82e0702e791b11226 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sat, 15 Mar 2025 15:15:45 -0500 Subject: PCI: dwc: ep: Use devicetree 'reg[addr_space]' to derive CPU -> ATU addr offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Endpoint ┌───────────────────────────────────────────────┐ │ pcie-ep@5f010000 │ │ ┌────────────────┐│ │ │ Endpoint ││ │ │ PCIe ││ │ │ Controller ││ │ bus@5f000000 │ ┌────────► │ ┌──────────┐ │ │ ││dynamically │ │ │ Outbound Transfer │ ││allocated │┌─────┐ │ Bus ┼─────►│ ATU ───────┘ ││PCI Addr ││ │ │ Fabric │Bus │ ││ ││ CPU ├───►│ │Addr │ ││ ││ │CPU │ │0x8000_0000 ││ │└─────┘Addr└──────────┘ │ ││ │ 0x7000_0000 └────────────────┘│ └───────────────────────────────────────────────┘ bus@5f000000 { compatible = "simple-bus"; ranges = <0x80000000 0x0 0x70000000 0x10000000>; pcie-ep@5f010000 { reg = <0x80000000 0x10000000>; reg-names ="addr_space"; ... }; ... }; In the diagram above, CPU writes data to outbound window address 0x7000_0000, and the bus fabric maps it to 0x8000_0000. The ATU uses bus address 0x8000_0000 as input address and maps to some PCI address dynamically allocated by a PCI device driver on the host side. The pcie-ep@5f010000 'reg[addr_space]' is the parent bus address, which is the input of PCIe controller, including the ATU. Set parent_bus_offset, the offset from the CPU address to the PCIe controller input address using dw_pcie_init_parent_bus_offset(). The parent_bus_offset is not used yet, so no functional change intended. [bhelgaas: commit log] Link: https://lore.kernel.org/r/20250315201548.858189-11-helgaas@kernel.org Signed-off-by: Frank Li Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware-ep.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 2db834345ec2..bb87d0c5c665 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -904,6 +904,13 @@ static int dw_pcie_ep_get_resources(struct dw_pcie_ep *ep) ep->phys_base = res->start; ep->addr_size = resource_size(res); + /* + * artpec6_pcie_cpu_addr_fixup() uses ep->phys_base, so call + * dw_pcie_parent_bus_offset() after setting ep->phys_base. + */ + pci->parent_bus_offset = dw_pcie_parent_bus_offset(pci, "addr_space", + ep->phys_base); + ret = of_property_read_u8(np, "max-functions", &epc->max_functions); if (ret < 0) epc->max_functions = 1; -- cgit v1.2.3-59-g8ed1b From f3e1dccba0a0833fc9a05fb838ebeb6ea4ca0e1a Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sat, 15 Mar 2025 15:15:46 -0500 Subject: PCI: dwc: ep: Ensure proper iteration over outbound map windows Most systems' PCIe outbound map windows have non-zero physical addresses, but the possibility of encountering zero increased after following commit ("PCI: dwc: Use parent_bus_offset"). 'ep->outbound_addr[n]', representing 'parent_bus_address', might be 0 on some hardware, which trims high address bits through bus fabric before sending to the PCIe controller. Replace the iteration logic with 'for_each_set_bit()' to ensure only allocated map windows are iterated when determining the ATU index from a given address. Link: https://lore.kernel.org/r/20250315201548.858189-12-helgaas@kernel.org Signed-off-by: Frank Li Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware-ep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index bb87d0c5c665..2ef9964fa080 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -282,7 +282,7 @@ static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr, u32 index; struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - for (index = 0; index < pci->num_ob_windows; index++) { + for_each_set_bit(index, ep->ob_window_map, pci->num_ob_windows) { if (ep->outbound_addr[index] != addr) continue; *atu_index = index; -- cgit v1.2.3-59-g8ed1b From befc86a0b354285f49b6d0dccd50956e95f437c4 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sat, 15 Mar 2025 15:15:47 -0500 Subject: PCI: dwc: Use parent_bus_offset to remove need for .cpu_addr_fixup() We know the parent_bus_offset, either computed from a DT reg property (the offset is the CPU physical addr - the 'config'/'addr_space' address on the parent bus) or from a .cpu_addr_fixup() (which may have used a host bridge window offset). Apply that parent_bus_offset instead of calling .cpu_addr_fixup() when programming the ATU. This assumes all intermediate addresses are at the same offset from the CPU physical addresses. [bhelgaas: commit log] Link: https://lore.kernel.org/r/20250315201548.858189-13-helgaas@kernel.org Signed-off-by: Frank Li Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware-ep.c | 5 +++-- drivers/pci/controller/dwc/pcie-designware-host.c | 12 ++++++------ drivers/pci/controller/dwc/pcie-designware.c | 3 --- 3 files changed, 9 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 2ef9964fa080..c1feaadb046a 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -314,7 +314,8 @@ static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - ret = dw_pcie_find_index(ep, addr, &atu_index); + ret = dw_pcie_find_index(ep, addr - pci->parent_bus_offset, + &atu_index); if (ret < 0) return; @@ -333,7 +334,7 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, atu.func_no = func_no; atu.type = PCIE_ATU_TYPE_MEM; - atu.parent_bus_addr = addr; + atu.parent_bus_addr = addr - pci->parent_bus_offset; atu.pci_addr = pci_addr; atu.size = size; ret = dw_pcie_ep_outbound_atu(ep, &atu); diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 9e38ac7d1bcb..d760abcbb785 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -635,7 +635,7 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, type = PCIE_ATU_TYPE_CFG1; atu.type = type; - atu.parent_bus_addr = pp->cfg0_base; + atu.parent_bus_addr = pp->cfg0_base - pci->parent_bus_offset; atu.pci_addr = busdev; atu.size = pp->cfg0_size; @@ -660,7 +660,7 @@ static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn, if (pp->cfg0_io_shared) { atu.type = PCIE_ATU_TYPE_IO; - atu.parent_bus_addr = pp->io_base; + atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset; atu.pci_addr = pp->io_bus_addr; atu.size = pp->io_size; @@ -686,7 +686,7 @@ static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn, if (pp->cfg0_io_shared) { atu.type = PCIE_ATU_TYPE_IO; - atu.parent_bus_addr = pp->io_base; + atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset; atu.pci_addr = pp->io_bus_addr; atu.size = pp->io_size; @@ -755,7 +755,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) atu.index = i; atu.type = PCIE_ATU_TYPE_MEM; - atu.parent_bus_addr = entry->res->start; + atu.parent_bus_addr = entry->res->start - pci->parent_bus_offset; atu.pci_addr = entry->res->start - entry->offset; /* Adjust iATU size if MSG TLP region was allocated before */ @@ -777,7 +777,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) if (pci->num_ob_windows > ++i) { atu.index = i; atu.type = PCIE_ATU_TYPE_IO; - atu.parent_bus_addr = pp->io_base; + atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset; atu.pci_addr = pp->io_bus_addr; atu.size = pp->io_size; @@ -921,7 +921,7 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci) atu.size = resource_size(pci->pp.msg_res); atu.index = pci->pp.msg_atu_index; - atu.parent_bus_addr = pci->pp.msg_res->start; + atu.parent_bus_addr = pci->pp.msg_res->start - pci->parent_bus_offset; ret = dw_pcie_prog_outbound_atu(pci, &atu); if (ret) diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 4b442d1aa55b..151085343ade 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -475,9 +475,6 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u32 retries, val; u64 limit_addr; - if (pci->ops && pci->ops->cpu_addr_fixup) - parent_bus_addr = pci->ops->cpu_addr_fixup(pci, parent_bus_addr); - limit_addr = parent_bus_addr + atu->size - 1; if ((limit_addr & ~pci->region_limit) != (parent_bus_addr & ~pci->region_limit) || -- cgit v1.2.3-59-g8ed1b From b9812179f601590b1939dd46adc2b8f395afc771 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Sat, 15 Mar 2025 15:15:48 -0500 Subject: PCI: imx6: Remove imx_pcie_cpu_addr_fixup() Remove imx_pcie_cpu_addr_fixup, the .cpu_addr_fixup() method, because the dwc core driver already handles address translation based on the devicetree description. Link: https://lore.kernel.org/r/20250315201548.858189-14-helgaas@kernel.org Signed-off-by: Frank Li Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam Acked-by: Richard Zhu --- drivers/pci/controller/dwc/pci-imx6.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 90ace941090f..d1eb535df73e 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1217,22 +1217,6 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp) regulator_disable(imx_pcie->vpcie); } -static u64 imx_pcie_cpu_addr_fixup(struct dw_pcie *pcie, u64 cpu_addr) -{ - struct imx_pcie *imx_pcie = to_imx_pcie(pcie); - struct dw_pcie_rp *pp = &pcie->pp; - struct resource_entry *entry; - - if (!(imx_pcie->drvdata->flags & IMX_PCIE_FLAG_CPU_ADDR_FIXUP)) - return cpu_addr; - - entry = resource_list_first_type(&pp->bridge->windows, IORESOURCE_MEM); - if (!entry) - return cpu_addr; - - return cpu_addr - entry->offset; -} - /* * In old DWC implementations, PCIE_ATU_INHIBIT_PAYLOAD in iATU Ctrl2 * register is reserved, so the generic DWC implementation of sending the @@ -1263,7 +1247,6 @@ static const struct dw_pcie_host_ops imx_pcie_host_dw_pme_ops = { static const struct dw_pcie_ops dw_pcie_ops = { .start_link = imx_pcie_start_link, .stop_link = imx_pcie_stop_link, - .cpu_addr_fixup = imx_pcie_cpu_addr_fixup, }; static void imx_pcie_ep_init(struct dw_pcie_ep *ep) @@ -1645,6 +1628,7 @@ static int imx_pcie_probe(struct platform_device *pdev) if (ret) return ret; + pci->use_parent_dt_ranges = true; if (imx_pcie->drvdata->mode == DW_PCIE_EP_TYPE) { ret = imx_add_pcie_ep(imx_pcie, pdev); if (ret < 0) -- cgit v1.2.3-59-g8ed1b From 07ae413e169da3697e633dd4489db0d681a04460 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 5 Mar 2025 12:07:54 -0500 Subject: PCI: intel-gw: Remove intel_pcie_cpu_addr() Remove intel_pcie_cpu_addr(), the .cpu_addr_fixup() method, because the dwc core driver already handles address translation based on the devicetree description. [bhelgaas: this does require a minor dts change, but maintainer Lei Chuan Hua confirms that the driver is only used internally to Maxlinear and internal users will update dts: https://lore.kernel.org/r/BY3PR19MB507667CE7531D863E1E5F8AEBDD82@BY3PR19MB5076.namprd19.prod.outlook.com] Link: https://lore.kernel.org/r/20250305-intel-v1-1-40db3a685490@nxp.com Signed-off-by: Frank Li Signed-off-by: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-intel-gw.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index 9b53b8f6f268..c21906eced61 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -57,7 +57,6 @@ PCIE_APP_IRN_INTA | PCIE_APP_IRN_INTB | \ PCIE_APP_IRN_INTC | PCIE_APP_IRN_INTD) -#define BUS_IATU_OFFSET SZ_256M #define RESET_INTERVAL_MS 100 struct intel_pcie { @@ -381,13 +380,7 @@ static int intel_pcie_rc_init(struct dw_pcie_rp *pp) return intel_pcie_host_setup(pcie); } -static u64 intel_pcie_cpu_addr(struct dw_pcie *pcie, u64 cpu_addr) -{ - return cpu_addr + BUS_IATU_OFFSET; -} - static const struct dw_pcie_ops intel_pcie_ops = { - .cpu_addr_fixup = intel_pcie_cpu_addr, }; static const struct dw_pcie_host_ops intel_pcie_dw_ops = { @@ -409,6 +402,7 @@ static int intel_pcie_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcie); pci = &pcie->pci; pci->dev = dev; + pci->use_parent_dt_ranges = true; pp = &pci->pp; ret = intel_pcie_get_resources(pdev); -- cgit v1.2.3-59-g8ed1b From e55c67837a8cd42d6936e567f2ac75337b0e57f6 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Mon, 10 Mar 2025 12:10:22 +0100 Subject: PCI: dw-rockchip: Endpoint mode cannot raise INTx interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neither RK3568 or RK3588 supports INTx interrupts. Since epc_features is zero initialized, this is strictly not needed. However, setting intx_capable explicitly to false makes it more clear that neither RK3568 or RK3588 supports INTx interrupts. No functional change. Signed-off-by: Niklas Cassel Signed-off-by: Krzysztof Wilczyński Link: https://lore.kernel.org/r/20250310111016.859445-14-cassel@kernel.org --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 93698abff4d9..4aa25bcf9106 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -272,6 +272,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = { .linkup_notifier = true, .msi_capable = true, .msix_capable = true, + .intx_capable = false, .align = SZ_64K, .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, .bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, @@ -292,6 +293,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = { .linkup_notifier = true, .msi_capable = true, .msix_capable = true, + .intx_capable = false, .align = SZ_64K, .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, .bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, -- cgit v1.2.3-59-g8ed1b From 7c3b54cf6464b4d6ba5440ae5bb44552f099a078 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Mon, 10 Mar 2025 12:10:23 +0100 Subject: PCI: endpoint: pci-epf-test: Expose supported IRQ types in CAPS register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the supported IRQ types in the CAPS register. This way, the host side driver (drivers/misc/pci_endpoint_test.c) can know which IRQ types that the endpoint supports. The host side driver will make use of this information in a follow-up commit. Signed-off-by: Niklas Cassel Signed-off-by: Krzysztof Wilczyński Link: https://lore.kernel.org/r/20250310111016.859445-15-cassel@kernel.org --- drivers/pci/endpoint/functions/pci-epf-test.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index b94e205ae10b..1b1e92c1460f 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -45,6 +45,9 @@ #define TIMER_RESOLUTION 1 #define CAP_UNALIGNED_ACCESS BIT(0) +#define CAP_MSI BIT(1) +#define CAP_MSIX BIT(2) +#define CAP_INTX BIT(3) static struct workqueue_struct *kpcitest_workqueue; @@ -753,6 +756,15 @@ static void pci_epf_test_set_capabilities(struct pci_epf *epf) if (epc->ops->align_addr) caps |= CAP_UNALIGNED_ACCESS; + if (epf_test->epc_features->msi_capable) + caps |= CAP_MSI; + + if (epf_test->epc_features->msix_capable) + caps |= CAP_MSIX; + + if (epf_test->epc_features->intx_capable) + caps |= CAP_INTX; + reg->caps = cpu_to_le32(caps); } -- cgit v1.2.3-59-g8ed1b From 08818c6d7f276eb5895959f34c491a3911ae7820 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Mon, 10 Mar 2025 12:10:24 +0100 Subject: misc: pci_endpoint_test: Add support for PCITEST_IRQ_TYPE_AUTO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For PCITEST_MSI we really want to set PCITEST_SET_IRQTYPE explicitly to PCITEST_IRQ_TYPE_MSI, since we want to test if MSI works. For PCITEST_MSIX we really want to set PCITEST_SET_IRQTYPE explicitly to PCITEST_IRQ_TYPE_MSIX, since we want to test if MSI works. For PCITEST_LEGACY_IRQ we really want to set PCITEST_SET_IRQTYPE explicitly to PCITEST_IRQ_TYPE_INTX, since we want to test if INTx works. However, for PCITEST_WRITE, PCITEST_READ, PCITEST_COPY, we really don't care which IRQ type that is used, we just want to use a IRQ type that is supported by the EPC. The old behavior was to always use MSI for PCITEST_WRITE, PCITEST_READ, PCITEST_COPY, was to always set IRQ type to MSI before doing the actual test, however, there are EPC drivers that do not support MSI. Add a new PCITEST_IRQ_TYPE_AUTO, that will use the CAPS register to see which IRQ types the endpoint supports, and use one of the supported IRQ types. For backwards compatibility, if the endpoint does not expose any supported IRQ type in the CAPS register, simply fallback to using MSI, as it was unconditionally done before. Signed-off-by: Niklas Cassel Signed-off-by: Krzysztof Wilczyński Link: https://lore.kernel.org/r/20250310111016.859445-16-cassel@kernel.org --- drivers/misc/pci_endpoint_test.c | 25 +++++++++++++++++----- include/uapi/linux/pcitest.h | 1 + .../selftests/pci_endpoint/pci_endpoint_test.c | 12 +++++------ 3 files changed, 27 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index abcf1d91de37..1b57850dea9f 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -66,6 +66,9 @@ #define PCI_ENDPOINT_TEST_CAPS 0x30 #define CAP_UNALIGNED_ACCESS BIT(0) +#define CAP_MSI BIT(1) +#define CAP_MSIX BIT(2) +#define CAP_INTX BIT(3) #define PCI_DEVICE_ID_TI_AM654 0xb00c #define PCI_DEVICE_ID_TI_J7200 0xb00f @@ -113,6 +116,7 @@ struct pci_endpoint_test { struct miscdevice miscdev; enum pci_barno test_reg_bar; size_t alignment; + u32 ep_caps; const char *name; }; @@ -803,11 +807,23 @@ static int pci_endpoint_test_set_irq(struct pci_endpoint_test *test, int ret; if (req_irq_type < PCITEST_IRQ_TYPE_INTX || - req_irq_type > PCITEST_IRQ_TYPE_MSIX) { + req_irq_type > PCITEST_IRQ_TYPE_AUTO) { dev_err(dev, "Invalid IRQ type option\n"); return -EINVAL; } + if (req_irq_type == PCITEST_IRQ_TYPE_AUTO) { + if (test->ep_caps & CAP_MSI) + req_irq_type = PCITEST_IRQ_TYPE_MSI; + else if (test->ep_caps & CAP_MSIX) + req_irq_type = PCITEST_IRQ_TYPE_MSIX; + else if (test->ep_caps & CAP_INTX) + req_irq_type = PCITEST_IRQ_TYPE_INTX; + else + /* fallback to MSI if no caps defined */ + req_irq_type = PCITEST_IRQ_TYPE_MSI; + } + if (test->irq_type == req_irq_type) return 0; @@ -893,13 +909,12 @@ static void pci_endpoint_test_get_capabilities(struct pci_endpoint_test *test) { struct pci_dev *pdev = test->pdev; struct device *dev = &pdev->dev; - u32 caps; - caps = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CAPS); - dev_dbg(dev, "PCI_ENDPOINT_TEST_CAPS: %#x\n", caps); + test->ep_caps = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CAPS); + dev_dbg(dev, "PCI_ENDPOINT_TEST_CAPS: %#x\n", test->ep_caps); /* CAP_UNALIGNED_ACCESS is set if the EP can do unaligned access */ - if (caps & CAP_UNALIGNED_ACCESS) + if (test->ep_caps & CAP_UNALIGNED_ACCESS) test->alignment = 0; } diff --git a/include/uapi/linux/pcitest.h b/include/uapi/linux/pcitest.h index 304bf9be0f9a..d3aa8715a525 100644 --- a/include/uapi/linux/pcitest.h +++ b/include/uapi/linux/pcitest.h @@ -27,6 +27,7 @@ #define PCITEST_IRQ_TYPE_INTX 0 #define PCITEST_IRQ_TYPE_MSI 1 #define PCITEST_IRQ_TYPE_MSIX 2 +#define PCITEST_IRQ_TYPE_AUTO 3 #define PCITEST_FLAGS_USE_DMA 0x00000001 diff --git a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c index fdf4bc6aa9d2..ac26481d29d9 100644 --- a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c +++ b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c @@ -181,8 +181,8 @@ TEST_F(pci_ep_data_transfer, READ_TEST) if (variant->use_dma) param.flags = PCITEST_FLAGS_USE_DMA; - pci_ep_ioctl(PCITEST_SET_IRQTYPE, PCITEST_IRQ_TYPE_MSI); - ASSERT_EQ(0, ret) TH_LOG("Can't set MSI IRQ type"); + pci_ep_ioctl(PCITEST_SET_IRQTYPE, PCITEST_IRQ_TYPE_AUTO); + ASSERT_EQ(0, ret) TH_LOG("Can't set AUTO IRQ type"); for (i = 0; i < ARRAY_SIZE(test_size); i++) { param.size = test_size[i]; @@ -200,8 +200,8 @@ TEST_F(pci_ep_data_transfer, WRITE_TEST) if (variant->use_dma) param.flags = PCITEST_FLAGS_USE_DMA; - pci_ep_ioctl(PCITEST_SET_IRQTYPE, PCITEST_IRQ_TYPE_MSI); - ASSERT_EQ(0, ret) TH_LOG("Can't set MSI IRQ type"); + pci_ep_ioctl(PCITEST_SET_IRQTYPE, PCITEST_IRQ_TYPE_AUTO); + ASSERT_EQ(0, ret) TH_LOG("Can't set AUTO IRQ type"); for (i = 0; i < ARRAY_SIZE(test_size); i++) { param.size = test_size[i]; @@ -219,8 +219,8 @@ TEST_F(pci_ep_data_transfer, COPY_TEST) if (variant->use_dma) param.flags = PCITEST_FLAGS_USE_DMA; - pci_ep_ioctl(PCITEST_SET_IRQTYPE, PCITEST_IRQ_TYPE_MSI); - ASSERT_EQ(0, ret) TH_LOG("Can't set MSI IRQ type"); + pci_ep_ioctl(PCITEST_SET_IRQTYPE, PCITEST_IRQ_TYPE_AUTO); + ASSERT_EQ(0, ret) TH_LOG("Can't set AUTO IRQ type"); for (i = 0; i < ARRAY_SIZE(test_size); i++) { param.size = test_size[i]; -- cgit v1.2.3-59-g8ed1b From d66b5b336245b91681c2042e7eedf63ef7c2f6db Mon Sep 17 00:00:00 2001 From: Siddharth Vadapalli Date: Wed, 5 Mar 2025 18:50:18 +0530 Subject: PCI: j721e: Fix the value of .linkdown_irq_regfield for J784S4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit e49ad667815d ("PCI: j721e: Add TI J784S4 PCIe configuration") assigned the value of .linkdown_irq_regfield for the J784S4 SoC as the "LINK_DOWN" macro corresponding to BIT(1), and as a result, the Link Down interrupts on J784S4 SoC are missed. According to the Technical Reference Manual and Register Documentation for the J784S4 SoC[1], BIT(1) corresponds to "ENABLE_SYS_EN_PCIE_DPA_1", which is not the correct field for the link-state interrupt. Instead, it is BIT(10) of the "PCIE_INTD_ENABLE_REG_SYS_2" register that corresponds to the link-state field named as "ENABLE_SYS_EN_PCIE_LINK_STATE". Thus, set .linkdown_irq_regfield to the macro "J7200_LINK_DOWN", which expands to BIT(10) and was first defined for the J7200 SoC. Other SoCs already reuse this macro since it accurately represents the "link-state" field in their respective "PCIE_INTD_ENABLE_REG_SYS_2" register. 1: https://www.ti.com/lit/zip/spruj52 Fixes: e49ad667815d ("PCI: j721e: Add TI J784S4 PCIe configuration") Cc: stable@vger.kernel.org Signed-off-by: Siddharth Vadapalli [kwilczynski: commit log, add a missing .linkdown_irq_regfield member set to the J7200_LINK_DOWN macro to struct j7200_pcie_ep_data] Signed-off-by: Krzysztof Wilczyński Link: https://lore.kernel.org/r/20250305132018.2260771-1-s-vadapalli@ti.com --- drivers/pci/controller/cadence/pci-j721e.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index 0341d51d6aed..ef1cfdae33bb 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -355,6 +355,7 @@ static const struct j721e_pcie_data j7200_pcie_rc_data = { static const struct j721e_pcie_data j7200_pcie_ep_data = { .mode = PCI_MODE_EP, .quirk_detect_quiet_flag = true, + .linkdown_irq_regfield = J7200_LINK_DOWN, .quirk_disable_flr = true, .max_lanes = 2, }; @@ -376,13 +377,13 @@ static const struct j721e_pcie_data j784s4_pcie_rc_data = { .mode = PCI_MODE_RC, .quirk_retrain_flag = true, .byte_access_allowed = false, - .linkdown_irq_regfield = LINK_DOWN, + .linkdown_irq_regfield = J7200_LINK_DOWN, .max_lanes = 4, }; static const struct j721e_pcie_data j784s4_pcie_ep_data = { .mode = PCI_MODE_EP, - .linkdown_irq_regfield = LINK_DOWN, + .linkdown_irq_regfield = J7200_LINK_DOWN, .max_lanes = 4, }; -- cgit v1.2.3-59-g8ed1b From 4c8c0ffd41d16cf08ccb0d3626beb54adfe5450a Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Thu, 27 Mar 2025 17:19:49 +0200 Subject: PCI: layerscape: Fix arg_count to syscon_regmap_lookup_by_phandle_args() The arg_count parameter to syscon_regmap_lookup_by_phandle_args() represents the number of argument cells following the phandle. In this case, the number of arguments should be 1 instead of 2 since the dt property looks like this: fsl,pcie-scfg = <&scfg 0>; Without this fix, layerscape-pcie fails with the following message on LS1043A: OF: /soc/pcie@3500000: phandle scfg@1570000 needs 2, found 1 layerscape-pcie 3500000.pcie: No syscfg phandle specified layerscape-pcie 3500000.pcie: probe with driver layerscape-pcie failed with error -22 Link: https://lore.kernel.org/r/20250327151949.2765193-1-ioana.ciornei@nxp.com Fixes: 149fc35734e5 ("PCI: layerscape: Use syscon_regmap_lookup_by_phandle_args") Signed-off-by: Ioana Ciornei Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Reviewed-by: Krzysztof Kozlowski Acked-by: Roy Zang Cc: stable@vger.kernel.org --- drivers/pci/controller/dwc/pci-layerscape.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c index 239a05b36e8e..a44b5c256d6e 100644 --- a/drivers/pci/controller/dwc/pci-layerscape.c +++ b/drivers/pci/controller/dwc/pci-layerscape.c @@ -356,7 +356,7 @@ static int ls_pcie_probe(struct platform_device *pdev) if (pcie->drvdata->scfg_support) { pcie->scfg = syscon_regmap_lookup_by_phandle_args(dev->of_node, - "fsl,pcie-scfg", 2, + "fsl,pcie-scfg", 1, index); if (IS_ERR(pcie->scfg)) { dev_err(dev, "No syscfg phandle specified\n"); -- cgit v1.2.3-59-g8ed1b