* Re: [PATCH 14/22] mm: set page table page type when allocated
From: Vishal Moola @ 2026-07-20 19:05 UTC (permalink / raw)
To: Kevin Brodsky
Cc: linux-mm, Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Pasha Tatashin, Russell King,
Catalin Marinas, Will Deacon, Ryan Roberts, linux-arm-kernel,
Huacai Chen, loongarch, James E.J. Bottomley, Helge Deller,
linux-parisc, Madhavan Srinivasan, Michael Ellerman, linuxppc-dev,
Paul Walmsley, Palmer Dabbelt, Albert Ou, linux-riscv,
Heiko Carstens, Vasily Gorbik, Alexander Gordeev, Gerald Schaefer,
linux-s390, David S. Miller, Andreas Larsson, sparclinux,
Richard Weinberger, Anton Ivanov, Johannes Berg, linux-um,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
H. Peter Anvin, Andy Lutomirski, Peter Zijlstra, Ning Sun, x86,
tboot-devel, Ard Biesheuvel, Ilias Apalodimas, linux-efi,
Alistair Popple, Matthew Wilcox (Oracle), linux-kernel,
linux-arch
In-Reply-To: <f3afa5df-73f3-49da-87a6-d75eb5f3610f@arm.com>
On Thu, Jul 16, 2026 at 11:49:31AM +0200, Kevin Brodsky wrote:
> On 14/07/2026 17:16, Vishal Moola wrote:
> > On Tue, Jul 14, 2026 at 03:04:03PM +0100, Kevin Brodsky wrote:
> >> As a step towards removing pagetable ctor/dtor calls for kernel
> >> mm's, move the page type updates from the ctor/dtor to
> >> pagetable_{alloc,free}.
> > Thanks for doing this patchset.
>
> Thanks for suggesting it :) It's turning out to be quite a bit more
> involved than I expected though! (There are multiple cases where we call
> the ctor/dtor without using pagetable_{alloc,free}, see my reply to the
> cover letter.)
>
> > We should switch over from __folio_set_pgtable() to __SetPagePgtable()
> > in this patch. Ptdescs and folios should be treated as distinctly
> > independent of each other.
>
> I sure remember the previous discussion, but my reading of your comments
> [1] is that we cannot really change this as long as pagetable_alloc()
> can be called with order > 0, right? Multiple archs still rely on that.
Kinda...
The concern was about callers accessing tail pages directly for that
information. Using the folio API ensures that we always look into the
head page.
Since we're now doing it at allocation time, we know we always have the
head page, so it's fine.
The dtor/free should be more questionable because the underlying api
uses virt_to_page() which could look at a tail page... oopsie. I
haven't seen any reports tied to it so its probably fine (the
setting/clearing has been using the folio API since ptdescs).
We can say thats a happy accident that makes distinguishing ptdescs
and folios easier :)
> > Eventually, we want to replace the memcg accounting as well, but
> > there's no existing API for that right now. Lets leave that as is for
> > now; this patchset is big enough already ;)
>
> Yep, looks like there was an attempt to improve this but it had to be
> reverted [2].
>
> - Kevin
>
> [1] https://lore.kernel.org/all/aha321ndGSdReBJR@fedora/
> [2]
> https://lore.kernel.org/all/20260225002434.2953895-1-axelrasmussen@google.com/
>
^ permalink raw reply
* Re: [PATCH 7/8] net: mv643xx: use platform_device_set_fwnode()
From: Andrew Lunn @ 2026-07-20 18:28 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Andi Shyti,
Joerg Roedel (AMD), Will Deacon, Robin Murphy, Andy Shevchenko,
Doug Berger, Florian Fainelli,
Broadcom internal kernel review list, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Lee Jones, Sebastian Hesselbarth,
Srinivas Kandagatla, driver-core, linuxppc-dev, linux-kernel,
linux-i2c, iommu, netdev, linux-pm, imx, linux-arm-kernel, mfd,
linux-arm-msm, linux-sound
In-Reply-To: <CAMRc=Mduv_KiB42d4AZddTrFSHmKogd2wh6vBDXomW6ssdfq9A@mail.gmail.com>
On Mon, Jul 20, 2026 at 06:01:37PM +0200, Bartosz Golaszewski wrote:
> On Mon, 20 Jul 2026 16:43:40 +0200, Andrew Lunn <andrew@lunn.ch> said:
> > On Mon, Jul 20, 2026 at 11:24:54AM +0200, Bartosz Golaszewski wrote:
> >> Prefer the higher-level platform_device_set_fwnode() over the
> >> OF-specific platform_device_set_of_node() for dynamically allocated
> >> platform devices.
> >>
> >> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> >> ---
> >> drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +-
> >> 1 file changed, 1 insertion(+), 1 deletion(-)
> >>
> >> diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c
> >> index 9caa1e47c174c9d7a161b7f2e2ee12a829b813d4..2f2d6cce8d852b9ec3ab42678a04a7915d1f00cc 100644
> >> --- a/drivers/net/ethernet/marvell/mv643xx_eth.c
> >> +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
> >> @@ -2780,7 +2780,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev,
> >> goto put_err;
> >> }
> >> ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
> >> - platform_device_set_of_node(ppdev, pnp);
> >> + platform_device_set_fwnode(ppdev, of_fwnode_handle(pnp));
> >
> > This is definitely an OF only driver. There are no other calls to
> > fwnode functions in this driver, so this is the wrong thing to do.
> >
> > Sorry, NACK.
> >
>
> I'm not going to die on this hill but drivers are OF-only until they're not.
> For example, Qualcomm is now working on a hybrid ACPI-OF approach for
> laptops[1] and we may end up needing to start converting drivers to fwnode
> after all.
>
> There's no real benefit to sticking to OF-specific APIs unless you need to
> iterate over all properties of a node or use some other functionality not
> available in fwnode. The overhead is minimal and it's never a hot path.
There is a lot of benefit to sticking to OF specific APIs, because
within the kernel OF is well maintained, has active maintainers, there
are tools to validate bindings, etc. ACPI is a Wild West, each driver
is a snow flake, there is no review, no binding documentation, no
validation tools etc.
I hope you allow plenty of time to convert any networking drivers,
where Linux is driving the hardware, to ACPI. Your first stop will be
the UEFI forum making a proposal for MDIO busses, because that
currently is not part of ACPI. You will then need to spend time
understanding the DT bindings, and figuring out which properties are
deprecated so need to stay OF only, and which can be converted to dual
OF/ACPI. I've seen too many naive attempts which blindly convert
everything, copying all the past errors in the DT binding into the
brand new ACPI binding. That will get NACKed.
OF != ACPI
Andrew
^ permalink raw reply
* Re: [RFC] cxl: Device protocol AER injection
From: Bowman, Terry @ 2026-07-20 18:12 UTC (permalink / raw)
To: Dave Jiang, Bjorn Helgaas, Dan Williams, Ira Weiny,
Jonathan Cameron, Robert Richter
Cc: linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
linuxppc-dev, Alejandro Lucero, Alison Schofield, Ben Cheatham,
Davidlohr Bueso, Gregory Price
In-Reply-To: <6ff8d646-61b1-496e-bdc0-f4a4011d1d58@intel.com>
On 7/20/2026 12:38 PM, Dave Jiang wrote:
>
>
> On 7/17/26 3:57 PM, Terry Bowman wrote:
>> This patch is intended to provide a method of testing the recently submitted
>> cxl series "cxl: Enable CXL PCIe Port Protocol Error handling and logging" found
>> here:
>>
>> https://lore.kernel.org/linux-cxl/20260717222706.3540281-1-terry.bowman@amd.com/T/#md90ec1fdd1b374bf1e32e7736e2b3e34b328c701
>>
>> The changes in this patch will allow CXL RAS protocol testing by injecting
>> AER errors using AER EINJ. The RAS register block status is updated
>> using a central function to augment RAS register block returned by
>> to_ras_base(). This supports all CXL devices including Root Ports,
>> Upstream Switch Ports, Downstream Switch Ports, Endpoints, and RCH
>> Downstream Ports.
>>
>> Add debugfs-based CXL protocol error injection for testing CXL RAS
>> error handling paths. Injects CXL RAS protocol errors using AER internal
>> error inject interface via /sys/kernel/debug/cxl/aer_einj_inject.
>>
>> RAS CXL status is set using to_ras_base() function override when kernel config
>> CONFIG_CXL_PROTO_AER_EINJ is enabled.
>>
>> Usage:
>> echo "DDDD:BB:DD.F [UCE|CE] AER_STATUS RAS_STATUS [RCH]" > \
>> /sys/kernel/debug/cxl/aer_einj_inject
>>
>> Move struct aer_error_inj and aer_inject() to linux/aer.h so CXL
>> can invoke AER injection directly. Export aer_inject() with
>> EXPORT_SYMBOL_GPL.
>>
>> Make cxl_debugfs non-static in port.c and declare it extern in
>> core.h so the debugfs file can be created under the existing CXL
>> debugfs root.
>>
>> Co-developed-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
>> Signed-off-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
>> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
>> ---
>> drivers/cxl/Kconfig | 13 +++
>> drivers/cxl/core/core.h | 21 ++++
>> drivers/cxl/core/port.c | 2 +-
>> drivers/cxl/core/ras.c | 208 ++++++++++++++++++++++++++++++++++
>> drivers/cxl/core/ras_rch.c | 12 ++
>> drivers/pci/pcie/aer_inject.c | 29 ++---
>> include/linux/aer.h | 15 +++
>> 7 files changed, 281 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
>> index 80aeb0d556bd7..ef449228b2549 100644
>> --- a/drivers/cxl/Kconfig
>> +++ b/drivers/cxl/Kconfig
>> @@ -238,6 +238,19 @@ config CXL_RAS
>> def_bool y
>> depends on ACPI_APEI_GHES && PCIEAER && CXL_BUS
>>
>> +config CXL_PROTO_AER_EINJ
>> + bool "CXL: RAS Protocol Error Injection using AER EINJ"
>> + depends on CXL_RAS
>> + depends on PCIEAER_INJECT
>> + help
>> + Enable debugfs-based CXL protocol error injection. Writes to
>> + /sys/kernel/debug/cxl/aer_einj_inject inject CXL RAS protocol
>> + errors using the AER internal error inject interface.
>> +
>> + This is a debug/test facility. Say N for production kernels.
>> +
>> + If unsure say N.
>> +
>> config CXL_ATL
>> def_bool y
>> depends on CXL_REGION
>> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
>> index a55a4e409feda..91910d2bb5d39 100644
>> --- a/drivers/cxl/core/core.h
>> +++ b/drivers/cxl/core/core.h
>> @@ -182,6 +182,9 @@ static inline struct device *dport_to_host(struct cxl_dport *dport)
>> return port->uport_dev;
>> return &port->dev;
>> }
>> +
>> +extern struct dentry *cxl_debugfs;
>> +
>> #ifdef CONFIG_CXL_RAS
>> void cxl_ras_init(void);
>> void cxl_ras_exit(void);
>> @@ -244,4 +247,22 @@ int cxl_set_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid,
>>
>> resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
>> struct cxl_dport *dport);
>> +
>> +#ifdef CONFIG_CXL_PROTO_AER_EINJ
>> +
>> +#define AER_REGISTER_SIZE 5
>> +#define RAS_REGISTER_SIZE (CXL_RAS_CAPABILITY_LENGTH / sizeof(u32))
>> +
>> +struct cxl_aer_einj {
>> + int correctable;
>> + bool is_rch;
>> + struct mutex *lock;
>> + struct device *dev;
>> + u32 aer_registers[AER_REGISTER_SIZE];
>> + u32 ras_registers[RAS_REGISTER_SIZE];
>> +};
>> +
>> +extern struct cxl_aer_einj cxl_aer_einj;
>> +#endif
>> +
>> #endif /* __CXL_CORE_H__ */
>> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
>> index a76f3ee05cba8..79657e5fddaac 100644
>> --- a/drivers/cxl/core/port.c
>> +++ b/drivers/cxl/core/port.c
>> @@ -2501,7 +2501,7 @@ const struct bus_type cxl_bus_type = {
>> };
>> EXPORT_SYMBOL_NS_GPL(cxl_bus_type, "CXL");
>>
>> -static struct dentry *cxl_debugfs;
>> +struct dentry *cxl_debugfs;
>>
>> struct dentry *cxl_debugfs_create_dir(const char *dir)
>> {
>> diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c
>> index d77208af41e03..d41deea899d30 100644
>> --- a/drivers/cxl/core/ras.c
>> +++ b/drivers/cxl/core/ras.c
>> @@ -3,6 +3,7 @@
>>
>> #include <linux/pci.h>
>> #include <linux/aer.h>
>> +#include <linux/debugfs.h>
>> #include <cxl/event.h>
>> #include <cxlmem.h>
>> #include <cxlpci.h>
>> @@ -117,6 +118,195 @@ static void cxl_cper_prot_err_work_fn(struct work_struct *work)
>> }
>> static DECLARE_WORK(cxl_cper_prot_err_work, cxl_cper_prot_err_work_fn);
>>
>> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
>
> Can this be moved to a header file? Prefer to not introduce #ifdef in the C source.
>
Yes, I will move to core.h.
>> +
>> +static DEFINE_MUTEX(cxl_aer_einj_mutex);
>> +
>> +struct cxl_aer_einj cxl_aer_einj = {
>> + .lock = &cxl_aer_einj_mutex,
>> +};
>> +
>> +static const char cxl_aer_einj_usage[] =
>> + "ssss:bb:dd.f [UCE|CE] AER_STATUS RAS_STATUS [RCH]\n";
>> +
>> +static int cxl_aer_inject_error(struct pci_dev *pdev, bool correctable,
>> + u32 aer_status, u32 ras_status)
>> +{
>> + /* RCD errors are signaled as internal errors on the associated RCEC */
>> + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END) {
>> + if (!pdev->rcec)
>> + return -ENODEV;
>> + pdev = pdev->rcec;
>> + }
>> +
>> + struct aer_error_inj einj = {
>> + .bus = pdev->bus->number,
>> + .dev = PCI_SLOT(pdev->devfn),
>> + .fn = PCI_FUNC(pdev->devfn),
>> + .domain = pci_domain_nr(pdev->bus),
>> + };
>> + int ret;
>> + int aer_offset;
>> + int ras_offset;
>> +
>> + if (correctable) {
>> + einj.cor_status = aer_status | PCI_ERR_COR_INTERNAL;
>> + aer_offset = PCI_ERR_COR_STATUS / sizeof(u32);
>> + ras_offset = CXL_RAS_CORRECTABLE_STATUS_OFFSET / sizeof(u32);
>> + } else {
>> + einj.uncor_status = aer_status | PCI_ERR_UNC_INTN;
>> + aer_offset = PCI_ERR_UNCOR_STATUS / sizeof(u32);
>> + ras_offset = CXL_RAS_UNCORRECTABLE_STATUS_OFFSET / sizeof(u32);
>> + }
>> +
>> + cxl_aer_einj.correctable = correctable;
>> + cxl_aer_einj.aer_registers[aer_offset] = aer_status;
>> + cxl_aer_einj.ras_registers[ras_offset] = ras_status;
>> +
>> + ret = aer_inject(&einj);
>> + if (ret) {
>> + pr_err("cxl-einj: aer_inject failed: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static ssize_t cxl_aer_einj_write(struct file *file,
>> + const char __user *ubuf,
>> + size_t count, loff_t *ppos)
>> +{
>> + char sbdf[16], severity[4], topology[4] = "";
>> + unsigned int domain, bus, dev, fn;
>> + u32 aer_status, ras_status;
>> + struct cxl_dport *dport;
>> + char buf[128];
>> + int nargs;
>> + int ret;
>> +
>> + if (!capable(CAP_SYS_ADMIN))
>> + return -EPERM;
>> +
>> + if (count >= sizeof(buf)) {
>> + pr_err("cxl-einj: input too long (%zu bytes, max %zu)\n", count, sizeof(buf) - 1);
>> + return -EINVAL;
>> + }
>> +
>> + if (copy_from_user(buf, ubuf, count)) {
>> + pr_err("cxl-einj: copy_from_user failed\n");
>> + return -EFAULT;
>> + }
>> + buf[count] = '\0';
>> +
>> + nargs = sscanf(buf, "%15s %3s %x %x %3s", sbdf, severity,
>> + &aer_status, &ras_status, topology);
>> + if (nargs < 4) {
>> + pr_err("cxl-einj: expected format: <SBDF> <UCE|CE> <aer_status> <ras_status>\n");
>> + return -EINVAL;
>> + }
>> +
>> + if (nargs == 5 && strcmp(topology, "RCH") != 0)
>> + return -EINVAL;
>> +
>> + if (strcmp(severity, "UCE") != 0 && strcmp(severity, "CE") != 0) {
>> + pr_err("cxl-einj: expected 'UCE' or 'CE', got '%s'\n", severity);
>> + return -EINVAL;
>> + }
>> +
>> + if (sscanf(sbdf, "%x:%x:%x.%x", &domain, &bus, &dev, &fn) != 4) {
>> + pr_err("cxl-einj: invalid SBDF format '%s', expected DDDD:BB:DD.F\n", sbdf);
>> + return -EINVAL;
>> + }
>> +
>> + struct pci_dev *pdev __free(pci_dev_put) =
>> + pci_get_domain_bus_and_slot(domain, bus, PCI_DEVFN(dev, fn));
>> + if (!pdev) {
>> + pr_err("cxl-einj: device %s not found\n", sbdf);
>> + return -ENODEV;
>> + }
>> +
>> + guard(mutex)(cxl_aer_einj.lock);
>> + cxl_aer_einj.dev = NULL;
>> +
>> + struct cxl_port *port __free(put_cxl_port) = find_cxl_port_by_dev(&pdev->dev, &dport);
>> + if (!port) {
>> + dev_err(&pdev->dev, "cxl-einj: Failed to find CXL Port.\n");
>> + return -ENODEV;
>> + }
>> +
>> + if (!to_ras_base(port, dport)) {
>> + dev_err(&pdev->dev, "cxl-einj: RAS not initialized.\n");
>> + return -ENODEV;
>> + }
>> +
>> + cxl_aer_einj.is_rch = (nargs == 5 && strcmp(topology, "RCH") == 0);
>> + if (!cxl_aer_einj.is_rch)
>> + pci_dev_get(pdev);
>> + cxl_aer_einj.dev = cxl_aer_einj.is_rch ? pdev->dev.parent : &pdev->dev;
>> + ret = cxl_aer_inject_error(pdev, strcmp(severity, "CE") == 0,
>> + aer_status, ras_status);
>> + if (ret) {
>> + if (!cxl_aer_einj.is_rch)
>> + pci_dev_put(pdev);
>> + cxl_aer_einj.dev = NULL;
>> + pr_err("cxl-einj: injection failed for %s: %d\n", sbdf, ret);
>> + return ret;
>> + }
>> +
>> + return count;
>> +}
>> +
>> +static ssize_t cxl_aer_einj_read(struct file *file, char __user *ubuf,
>> + size_t count, loff_t *ppos)
>> +{
>> + return simple_read_from_buffer(ubuf, count, ppos,
>> + cxl_aer_einj_usage,
>> + sizeof(cxl_aer_einj_usage) - 1);
>> +}
>> +
>> +static const struct file_operations cxl_ras_error_fops = {
>> + .owner = THIS_MODULE,
>> + .read = cxl_aer_einj_read,
>> + .write = cxl_aer_einj_write,
>> + .llseek = default_llseek,
>> +};
>> +
>> +static void cxl_ras_create_debugfs(struct dentry *dir)
>> +{
>> + debugfs_create_file("aer_einj_inject", 0600, dir, NULL,
>> + &cxl_ras_error_fops);
>> +}
>> +
>> +static void __iomem *to_einj_ras_base(struct cxl_port *port, struct cxl_dport *dport)
>> +{
>> + if (dport) {
>> + if (cxl_aer_einj.is_rch) {
>> + if (cxl_aer_einj.dev == dport->dport_dev) {
>> + cxl_aer_einj.dev = NULL;
>> + return (__force void __iomem *)cxl_aer_einj.ras_registers;
>> + }
>> + } else {
>> + if (cxl_aer_einj.dev == dport->dport_dev) {
>> + pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
>> + cxl_aer_einj.dev = NULL;
>> + return (__force void __iomem *)cxl_aer_einj.ras_registers;
>> + }
>> + }
>> + } else if (!cxl_aer_einj.is_rch) {
>> + struct device *dev = is_cxl_endpoint(port) ?
>> + port->uport_dev->parent : port->uport_dev;
>> +
>> + if (dev_is_pci(dev) && cxl_aer_einj.dev == dev) {
>> + pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
>> + cxl_aer_einj.dev = NULL;
>> + return (__force void __iomem *)cxl_aer_einj.ras_registers;
>> + }
>> + }
>> +
>> + return NULL;
>> +}
>> +#endif
>> +
>> static void cxl_unmask_proto_interrupts(struct device *dev)
>> {
>> struct pci_dev *pdev;
>> @@ -238,6 +428,14 @@ void __iomem *to_ras_base(struct cxl_port *port, struct cxl_dport *dport)
>> if (!port)
>> return NULL;
>>
>> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
>
> I think you can do something like below instead:
>
> if (IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ))
>
> DJ
>
Good idea. Thanks for pointing this out.
Terry
[snip]
^ permalink raw reply
* Re: [RFC] cxl: Device protocol AER injection
From: Dave Jiang @ 2026-07-20 17:38 UTC (permalink / raw)
To: Terry Bowman, Bjorn Helgaas, Dan Williams, Ira Weiny,
Jonathan Cameron, Len Brown, Rafael J . Wysocki, Robert Richter
Cc: linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
linuxppc-dev, Alejandro Lucero, Alison Schofield, Ankit Agrawal,
Ard Biesheuvel, Ben Cheatham, Borislav Petkov, Breno Leitao,
Davidlohr Bueso, Fabio M . De Francesco, Gregory Price,
Hanjun Guo, Jonathan Corbet, Kees Cook,
Kuppuswamy Sathyanarayanan, Li Ming, Mahesh J Salgaonkar,
Mauro Carvalho Chehab, Oliver O'Halloran, Shiju Jose,
Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck, Vishal Verma
In-Reply-To: <20260717225700.3543801-1-terry.bowman@amd.com>
On 7/17/26 3:57 PM, Terry Bowman wrote:
> This patch is intended to provide a method of testing the recently submitted
> cxl series "cxl: Enable CXL PCIe Port Protocol Error handling and logging" found
> here:
>
> https://lore.kernel.org/linux-cxl/20260717222706.3540281-1-terry.bowman@amd.com/T/#md90ec1fdd1b374bf1e32e7736e2b3e34b328c701
>
> The changes in this patch will allow CXL RAS protocol testing by injecting
> AER errors using AER EINJ. The RAS register block status is updated
> using a central function to augment RAS register block returned by
> to_ras_base(). This supports all CXL devices including Root Ports,
> Upstream Switch Ports, Downstream Switch Ports, Endpoints, and RCH
> Downstream Ports.
>
> Add debugfs-based CXL protocol error injection for testing CXL RAS
> error handling paths. Injects CXL RAS protocol errors using AER internal
> error inject interface via /sys/kernel/debug/cxl/aer_einj_inject.
>
> RAS CXL status is set using to_ras_base() function override when kernel config
> CONFIG_CXL_PROTO_AER_EINJ is enabled.
>
> Usage:
> echo "DDDD:BB:DD.F [UCE|CE] AER_STATUS RAS_STATUS [RCH]" > \
> /sys/kernel/debug/cxl/aer_einj_inject
>
> Move struct aer_error_inj and aer_inject() to linux/aer.h so CXL
> can invoke AER injection directly. Export aer_inject() with
> EXPORT_SYMBOL_GPL.
>
> Make cxl_debugfs non-static in port.c and declare it extern in
> core.h so the debugfs file can be created under the existing CXL
> debugfs root.
>
> Co-developed-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
> Signed-off-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
> ---
> drivers/cxl/Kconfig | 13 +++
> drivers/cxl/core/core.h | 21 ++++
> drivers/cxl/core/port.c | 2 +-
> drivers/cxl/core/ras.c | 208 ++++++++++++++++++++++++++++++++++
> drivers/cxl/core/ras_rch.c | 12 ++
> drivers/pci/pcie/aer_inject.c | 29 ++---
> include/linux/aer.h | 15 +++
> 7 files changed, 281 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
> index 80aeb0d556bd7..ef449228b2549 100644
> --- a/drivers/cxl/Kconfig
> +++ b/drivers/cxl/Kconfig
> @@ -238,6 +238,19 @@ config CXL_RAS
> def_bool y
> depends on ACPI_APEI_GHES && PCIEAER && CXL_BUS
>
> +config CXL_PROTO_AER_EINJ
> + bool "CXL: RAS Protocol Error Injection using AER EINJ"
> + depends on CXL_RAS
> + depends on PCIEAER_INJECT
> + help
> + Enable debugfs-based CXL protocol error injection. Writes to
> + /sys/kernel/debug/cxl/aer_einj_inject inject CXL RAS protocol
> + errors using the AER internal error inject interface.
> +
> + This is a debug/test facility. Say N for production kernels.
> +
> + If unsure say N.
> +
> config CXL_ATL
> def_bool y
> depends on CXL_REGION
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index a55a4e409feda..91910d2bb5d39 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -182,6 +182,9 @@ static inline struct device *dport_to_host(struct cxl_dport *dport)
> return port->uport_dev;
> return &port->dev;
> }
> +
> +extern struct dentry *cxl_debugfs;
> +
> #ifdef CONFIG_CXL_RAS
> void cxl_ras_init(void);
> void cxl_ras_exit(void);
> @@ -244,4 +247,22 @@ int cxl_set_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid,
>
> resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
> struct cxl_dport *dport);
> +
> +#ifdef CONFIG_CXL_PROTO_AER_EINJ
> +
> +#define AER_REGISTER_SIZE 5
> +#define RAS_REGISTER_SIZE (CXL_RAS_CAPABILITY_LENGTH / sizeof(u32))
> +
> +struct cxl_aer_einj {
> + int correctable;
> + bool is_rch;
> + struct mutex *lock;
> + struct device *dev;
> + u32 aer_registers[AER_REGISTER_SIZE];
> + u32 ras_registers[RAS_REGISTER_SIZE];
> +};
> +
> +extern struct cxl_aer_einj cxl_aer_einj;
> +#endif
> +
> #endif /* __CXL_CORE_H__ */
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index a76f3ee05cba8..79657e5fddaac 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -2501,7 +2501,7 @@ const struct bus_type cxl_bus_type = {
> };
> EXPORT_SYMBOL_NS_GPL(cxl_bus_type, "CXL");
>
> -static struct dentry *cxl_debugfs;
> +struct dentry *cxl_debugfs;
>
> struct dentry *cxl_debugfs_create_dir(const char *dir)
> {
> diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c
> index d77208af41e03..d41deea899d30 100644
> --- a/drivers/cxl/core/ras.c
> +++ b/drivers/cxl/core/ras.c
> @@ -3,6 +3,7 @@
>
> #include <linux/pci.h>
> #include <linux/aer.h>
> +#include <linux/debugfs.h>
> #include <cxl/event.h>
> #include <cxlmem.h>
> #include <cxlpci.h>
> @@ -117,6 +118,195 @@ static void cxl_cper_prot_err_work_fn(struct work_struct *work)
> }
> static DECLARE_WORK(cxl_cper_prot_err_work, cxl_cper_prot_err_work_fn);
>
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
Can this be moved to a header file? Prefer to not introduce #ifdef in the C source.
> +
> +static DEFINE_MUTEX(cxl_aer_einj_mutex);
> +
> +struct cxl_aer_einj cxl_aer_einj = {
> + .lock = &cxl_aer_einj_mutex,
> +};
> +
> +static const char cxl_aer_einj_usage[] =
> + "ssss:bb:dd.f [UCE|CE] AER_STATUS RAS_STATUS [RCH]\n";
> +
> +static int cxl_aer_inject_error(struct pci_dev *pdev, bool correctable,
> + u32 aer_status, u32 ras_status)
> +{
> + /* RCD errors are signaled as internal errors on the associated RCEC */
> + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END) {
> + if (!pdev->rcec)
> + return -ENODEV;
> + pdev = pdev->rcec;
> + }
> +
> + struct aer_error_inj einj = {
> + .bus = pdev->bus->number,
> + .dev = PCI_SLOT(pdev->devfn),
> + .fn = PCI_FUNC(pdev->devfn),
> + .domain = pci_domain_nr(pdev->bus),
> + };
> + int ret;
> + int aer_offset;
> + int ras_offset;
> +
> + if (correctable) {
> + einj.cor_status = aer_status | PCI_ERR_COR_INTERNAL;
> + aer_offset = PCI_ERR_COR_STATUS / sizeof(u32);
> + ras_offset = CXL_RAS_CORRECTABLE_STATUS_OFFSET / sizeof(u32);
> + } else {
> + einj.uncor_status = aer_status | PCI_ERR_UNC_INTN;
> + aer_offset = PCI_ERR_UNCOR_STATUS / sizeof(u32);
> + ras_offset = CXL_RAS_UNCORRECTABLE_STATUS_OFFSET / sizeof(u32);
> + }
> +
> + cxl_aer_einj.correctable = correctable;
> + cxl_aer_einj.aer_registers[aer_offset] = aer_status;
> + cxl_aer_einj.ras_registers[ras_offset] = ras_status;
> +
> + ret = aer_inject(&einj);
> + if (ret) {
> + pr_err("cxl-einj: aer_inject failed: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static ssize_t cxl_aer_einj_write(struct file *file,
> + const char __user *ubuf,
> + size_t count, loff_t *ppos)
> +{
> + char sbdf[16], severity[4], topology[4] = "";
> + unsigned int domain, bus, dev, fn;
> + u32 aer_status, ras_status;
> + struct cxl_dport *dport;
> + char buf[128];
> + int nargs;
> + int ret;
> +
> + if (!capable(CAP_SYS_ADMIN))
> + return -EPERM;
> +
> + if (count >= sizeof(buf)) {
> + pr_err("cxl-einj: input too long (%zu bytes, max %zu)\n", count, sizeof(buf) - 1);
> + return -EINVAL;
> + }
> +
> + if (copy_from_user(buf, ubuf, count)) {
> + pr_err("cxl-einj: copy_from_user failed\n");
> + return -EFAULT;
> + }
> + buf[count] = '\0';
> +
> + nargs = sscanf(buf, "%15s %3s %x %x %3s", sbdf, severity,
> + &aer_status, &ras_status, topology);
> + if (nargs < 4) {
> + pr_err("cxl-einj: expected format: <SBDF> <UCE|CE> <aer_status> <ras_status>\n");
> + return -EINVAL;
> + }
> +
> + if (nargs == 5 && strcmp(topology, "RCH") != 0)
> + return -EINVAL;
> +
> + if (strcmp(severity, "UCE") != 0 && strcmp(severity, "CE") != 0) {
> + pr_err("cxl-einj: expected 'UCE' or 'CE', got '%s'\n", severity);
> + return -EINVAL;
> + }
> +
> + if (sscanf(sbdf, "%x:%x:%x.%x", &domain, &bus, &dev, &fn) != 4) {
> + pr_err("cxl-einj: invalid SBDF format '%s', expected DDDD:BB:DD.F\n", sbdf);
> + return -EINVAL;
> + }
> +
> + struct pci_dev *pdev __free(pci_dev_put) =
> + pci_get_domain_bus_and_slot(domain, bus, PCI_DEVFN(dev, fn));
> + if (!pdev) {
> + pr_err("cxl-einj: device %s not found\n", sbdf);
> + return -ENODEV;
> + }
> +
> + guard(mutex)(cxl_aer_einj.lock);
> + cxl_aer_einj.dev = NULL;
> +
> + struct cxl_port *port __free(put_cxl_port) = find_cxl_port_by_dev(&pdev->dev, &dport);
> + if (!port) {
> + dev_err(&pdev->dev, "cxl-einj: Failed to find CXL Port.\n");
> + return -ENODEV;
> + }
> +
> + if (!to_ras_base(port, dport)) {
> + dev_err(&pdev->dev, "cxl-einj: RAS not initialized.\n");
> + return -ENODEV;
> + }
> +
> + cxl_aer_einj.is_rch = (nargs == 5 && strcmp(topology, "RCH") == 0);
> + if (!cxl_aer_einj.is_rch)
> + pci_dev_get(pdev);
> + cxl_aer_einj.dev = cxl_aer_einj.is_rch ? pdev->dev.parent : &pdev->dev;
> + ret = cxl_aer_inject_error(pdev, strcmp(severity, "CE") == 0,
> + aer_status, ras_status);
> + if (ret) {
> + if (!cxl_aer_einj.is_rch)
> + pci_dev_put(pdev);
> + cxl_aer_einj.dev = NULL;
> + pr_err("cxl-einj: injection failed for %s: %d\n", sbdf, ret);
> + return ret;
> + }
> +
> + return count;
> +}
> +
> +static ssize_t cxl_aer_einj_read(struct file *file, char __user *ubuf,
> + size_t count, loff_t *ppos)
> +{
> + return simple_read_from_buffer(ubuf, count, ppos,
> + cxl_aer_einj_usage,
> + sizeof(cxl_aer_einj_usage) - 1);
> +}
> +
> +static const struct file_operations cxl_ras_error_fops = {
> + .owner = THIS_MODULE,
> + .read = cxl_aer_einj_read,
> + .write = cxl_aer_einj_write,
> + .llseek = default_llseek,
> +};
> +
> +static void cxl_ras_create_debugfs(struct dentry *dir)
> +{
> + debugfs_create_file("aer_einj_inject", 0600, dir, NULL,
> + &cxl_ras_error_fops);
> +}
> +
> +static void __iomem *to_einj_ras_base(struct cxl_port *port, struct cxl_dport *dport)
> +{
> + if (dport) {
> + if (cxl_aer_einj.is_rch) {
> + if (cxl_aer_einj.dev == dport->dport_dev) {
> + cxl_aer_einj.dev = NULL;
> + return (__force void __iomem *)cxl_aer_einj.ras_registers;
> + }
> + } else {
> + if (cxl_aer_einj.dev == dport->dport_dev) {
> + pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
> + cxl_aer_einj.dev = NULL;
> + return (__force void __iomem *)cxl_aer_einj.ras_registers;
> + }
> + }
> + } else if (!cxl_aer_einj.is_rch) {
> + struct device *dev = is_cxl_endpoint(port) ?
> + port->uport_dev->parent : port->uport_dev;
> +
> + if (dev_is_pci(dev) && cxl_aer_einj.dev == dev) {
> + pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
> + cxl_aer_einj.dev = NULL;
> + return (__force void __iomem *)cxl_aer_einj.ras_registers;
> + }
> + }
> +
> + return NULL;
> +}
> +#endif
> +
> static void cxl_unmask_proto_interrupts(struct device *dev)
> {
> struct pci_dev *pdev;
> @@ -238,6 +428,14 @@ void __iomem *to_ras_base(struct cxl_port *port, struct cxl_dport *dport)
> if (!port)
> return NULL;
>
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
I think you can do something like below instead:
if (IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ))
DJ
> + if (cxl_aer_einj.dev) {
> + void __iomem *einj = to_einj_ras_base(port, dport);
> + if (einj)
> + return einj;
> + }
> +#endif
> +
> if (dport)
> return dport->regs.ras;
>
> @@ -458,10 +656,20 @@ void cxl_ras_init(void)
> cxl_cper_register_prot_err_work(&cxl_cper_prot_err_work);
> cxl_register_proto_err_work(&cxl_proto_err_work,
> cxl_proto_err_do_flush);
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> + cxl_ras_create_debugfs(cxl_debugfs);
> +#endif
> }
>
> void cxl_ras_exit(void)
> {
> cxl_unregister_proto_err_work();
> cxl_cper_unregister_prot_err_work();
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> + if (cxl_aer_einj.dev) {
> + if (!cxl_aer_einj.is_rch)
> + pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
> + cxl_aer_einj.dev = NULL;
> + }
> +#endif
> }
> diff --git a/drivers/cxl/core/ras_rch.c b/drivers/cxl/core/ras_rch.c
> index 14bb3bdb2d092..5071cf86e4a68 100644
> --- a/drivers/cxl/core/ras_rch.c
> +++ b/drivers/cxl/core/ras_rch.c
> @@ -110,6 +110,14 @@ void cxl_handle_rdport_errors(struct pci_dev *pdev)
> if (!dport)
> return;
>
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> + if (cxl_aer_einj.is_rch && cxl_aer_einj.dev) {
> + severity = cxl_aer_einj.correctable ?
> + AER_CORRECTABLE : AER_FATAL;
> + goto handle_ras;
> + }
> +#endif
> +
> if (!cxl_rch_get_aer_info(dport->regs.dport_aer, &aer_regs))
> return;
>
> @@ -117,6 +125,10 @@ void cxl_handle_rdport_errors(struct pci_dev *pdev)
> return;
>
> pci_print_aer(pdev, severity, &aer_regs);
> +
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> +handle_ras:
> +#endif
> if (severity == AER_CORRECTABLE)
> cxl_handle_cor_ras(dport->port, dport,
> to_ras_base(port, dport), pdev->dsn);
> diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c
> index 09bfc7194ef31..b313adef680ae 100644
> --- a/drivers/pci/pcie/aer_inject.c
> +++ b/drivers/pci/pcie/aer_inject.c
> @@ -14,6 +14,7 @@
>
> #define dev_fmt(fmt) "aer_inject: " fmt
>
> +#include <linux/aer.h>
> #include <linux/module.h>
> #include <linux/init.h>
> #include <linux/interrupt.h>
> @@ -31,19 +32,6 @@
> static bool aer_mask_override;
> module_param(aer_mask_override, bool, 0);
>
> -struct aer_error_inj {
> - u8 bus;
> - u8 dev;
> - u8 fn;
> - u32 uncor_status;
> - u32 cor_status;
> - u32 header_log0;
> - u32 header_log1;
> - u32 header_log2;
> - u32 header_log3;
> - u32 domain;
> -};
> -
> struct aer_error {
> struct list_head list;
> u32 domain;
> @@ -316,7 +304,7 @@ static int pci_bus_set_aer_ops(struct pci_bus *bus)
> return 0;
> }
>
> -static int aer_inject(struct aer_error_inj *einj)
> +int aer_inject(struct aer_error_inj *einj)
> {
> struct aer_error *err, *rperr;
> struct aer_error *err_alloc = NULL, *rperr_alloc = NULL;
> @@ -332,10 +320,14 @@ static int aer_inject(struct aer_error_inj *einj)
> dev = pci_get_domain_bus_and_slot(einj->domain, einj->bus, devfn);
> if (!dev)
> return -ENODEV;
> - rpdev = pcie_find_root_port(dev);
> - /* If Root Port not found, try to find an RCEC */
> - if (!rpdev)
> - rpdev = dev->rcec;
> + if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC)
> + rpdev = dev;
> + else {
> + rpdev = pcie_find_root_port(dev);
> + /* If Root Port not found, try to find an RCEC */
> + if (!rpdev)
> + rpdev = dev->rcec;
> + }
> if (!rpdev) {
> pci_err(dev, "Neither Root Port nor RCEC found\n");
> ret = -ENODEV;
> @@ -482,6 +474,7 @@ static int aer_inject(struct aer_error_inj *einj)
> pci_dev_put(dev);
> return ret;
> }
> +EXPORT_SYMBOL_GPL(aer_inject);
>
> static ssize_t aer_inject_write(struct file *filp, const char __user *ubuf,
> size_t usize, loff_t *off)
> diff --git a/include/linux/aer.h b/include/linux/aer.h
> index b3657b80564b9..65c22ba597657 100644
> --- a/include/linux/aer.h
> +++ b/include/linux/aer.h
> @@ -27,6 +27,21 @@
> struct pci_dev;
> struct work_struct;
>
> +struct aer_error_inj {
> + u8 bus;
> + u8 dev;
> + u8 fn;
> + u32 uncor_status;
> + u32 cor_status;
> + u32 header_log0;
> + u32 header_log1;
> + u32 header_log2;
> + u32 header_log3;
> + u32 domain;
> +};
> +
> +int aer_inject(struct aer_error_inj *einj);
> +
> struct pcie_tlp_log {
> union {
> u32 dw[PCIE_STD_MAX_TLP_HEADERLOG];
^ permalink raw reply
* Re: [PATCH 4/8] net: bcmgenet: use platform_device_set_fwnode()
From: Florian Fainelli @ 2026-07-20 16:57 UTC (permalink / raw)
To: Andrew Lunn, Bartosz Golaszewski
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
Broadcom internal kernel review list, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Lee Jones, Sebastian Hesselbarth,
Srinivas Kandagatla, brgl, driver-core, linuxppc-dev,
linux-kernel, linux-i2c, iommu, netdev, linux-pm, imx,
linux-arm-kernel, mfd, linux-arm-msm, linux-sound
In-Reply-To: <ae9da5dd-3528-4270-ada0-d17ed14478e0@lunn.ch>
[-- Attachment #1: Type: text/plain, Size: 668 bytes --]
On 7/20/26 07:38, Andrew Lunn wrote:
> On Mon, Jul 20, 2026 at 11:24:51AM +0200, Bartosz Golaszewski wrote:
>> Prefer the higher-level platform_device_set_fwnode() over the
>> OF-specific platform_device_set_of_node() for dynamically allocated
>> platform devices.
>
> Why?
>
> This driver is OF only. It does not support ACPI, and probably never
> will. In general, networking and ACPI don't go together, ACPI is not
> sufficiently advanced.
>
> What is you use case here?
The driver has been, or was used on ACPI-based platforms:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=99c6b06a37d4cab118c45448fef9d28df62d35d8
--
Florian
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5485 bytes --]
^ permalink raw reply
* Re: [PATCH v3 phy-next 8/8] phy: lynx-10g: use RCW override procedure for dynamic protocol change
From: Vinod Koul @ 2026-07-20 16:34 UTC (permalink / raw)
To: Vladimir Oltean
Cc: linux-phy, devicetree, linuxppc-dev, linux-arm-kernel,
Ioana Ciornei, Neil Armstrong, Tanjeff Moos,
Christophe Leroy (CS GROUP), Michael Walle, Shawn Guo, Frank Li,
linux-kernel
In-Reply-To: <20260720133642.136324-9-vladimir.oltean@nxp.com>
On 20-07-26, 16:36, Vladimir Oltean wrote:
> Up until this patch, the only protocol change supported was between
> 1000Base-X/SGMII and 2500Base-X. The others require an RCW override
> procedure which was lacking.
>
> Since now the guts driver provides the means of applying this procedure,
> make use of it and remove any comment which mentioned the limitation.
lgtm, is there any dependency. If not I can pick it
--
~Vinod
^ permalink raw reply
* Re: [PATCH 7/8] net: mv643xx: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20 16:01 UTC (permalink / raw)
To: Andrew Lunn
Cc: Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Andi Shyti,
Joerg Roedel (AMD), Will Deacon, Robin Murphy, Andy Shevchenko,
Doug Berger, Florian Fainelli,
Broadcom internal kernel review list, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Lee Jones, Sebastian Hesselbarth,
Srinivas Kandagatla, brgl, driver-core, linuxppc-dev,
linux-kernel, linux-i2c, iommu, netdev, linux-pm, imx,
linux-arm-kernel, mfd, linux-arm-msm, linux-sound
In-Reply-To: <86b2aba7-b049-47e6-bc94-6cb499b30ce4@lunn.ch>
On Mon, 20 Jul 2026 16:43:40 +0200, Andrew Lunn <andrew@lunn.ch> said:
> On Mon, Jul 20, 2026 at 11:24:54AM +0200, Bartosz Golaszewski wrote:
>> Prefer the higher-level platform_device_set_fwnode() over the
>> OF-specific platform_device_set_of_node() for dynamically allocated
>> platform devices.
>>
>> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
>> ---
>> drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +-
>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c
>> index 9caa1e47c174c9d7a161b7f2e2ee12a829b813d4..2f2d6cce8d852b9ec3ab42678a04a7915d1f00cc 100644
>> --- a/drivers/net/ethernet/marvell/mv643xx_eth.c
>> +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
>> @@ -2780,7 +2780,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev,
>> goto put_err;
>> }
>> ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
>> - platform_device_set_of_node(ppdev, pnp);
>> + platform_device_set_fwnode(ppdev, of_fwnode_handle(pnp));
>
> This is definitely an OF only driver. There are no other calls to
> fwnode functions in this driver, so this is the wrong thing to do.
>
> Sorry, NACK.
>
I'm not going to die on this hill but drivers are OF-only until they're not.
For example, Qualcomm is now working on a hybrid ACPI-OF approach for
laptops[1] and we may end up needing to start converting drivers to fwnode
after all.
There's no real benefit to sticking to OF-specific APIs unless you need to
iterate over all properties of a node or use some other functionality not
available in fwnode. The overhead is minimal and it's never a hot path.
Thanks,
Bartosz
[1] https://lore.kernel.org/all/20260623145225.143218-1-johannes.goede@oss.qualcomm.com/
^ permalink raw reply
* Re: [PATCH v18 11/13] PCI: Cache PCI DSN into pci_dev->dsn during probe
From: Bowman, Terry @ 2026-07-20 15:48 UTC (permalink / raw)
To: Lukas Wunner
Cc: Bjorn Helgaas, Dan Williams, Dave Jiang, Ira Weiny,
Jonathan Cameron, Len Brown, Rafael J . Wysocki, Robert Richter,
linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
linuxppc-dev, Alejandro Lucero, Alison Schofield, Ankit Agrawal,
Ard Biesheuvel, Ben Cheatham, Borislav Petkov, Breno Leitao,
Davidlohr Bueso, Fabio M . De Francesco, Gregory Price,
Hanjun Guo, Jonathan Corbet, Kees Cook,
Kuppuswamy Sathyanarayanan, Li Ming, Mahesh J Salgaonkar,
Mauro Carvalho Chehab, Oliver O'Halloran, Shiju Jose,
Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck, Vishal Verma,
linux-cxl@vger.kernel.org, linux-pci@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <alsk97jVqw9jbRdQ@wunner.de>
On 7/18/2026 2:02 AM, Lukas Wunner wrote:
> On Fri, Jul 17, 2026 at 05:27:04PM -0500, Terry Bowman wrote:
>> Add a u64 dsn field to struct pci_dev and populate it from pci_get_dsn()
>> during pci_init_capabilities() at probe time via pci_dsn_init(). Only
>> write dev->dsn when the read succeeds. The zero initial value from
>> pci_dev allocation already represents 'no DSN available.'
>
> The DSN is already cached on (natively handled) PCIe hotplug ports
> to detect device replacement during system sleep, see struct controller
> in drivers/pci/hotplug/pciehp.h.
>
> Please remove that member from struct controller, remove the two
> assignments to the member in pciehp_configure_device() and pcie_init()
> and change the comparison in pciehp_device_replaced() to use the new
> member in struct pci_dev. You can do this either as part of this patch
> or in a separate patch.
>
> Thanks,
>
> Lukas
Hi Lukas,
Thanks for reviewing. Is this the changes you want?
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index debc79b0adfb2..12ec050d8a0fb 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -90,7 +90,6 @@ extern int pciehp_poll_time;
*/
struct controller {
struct pcie_device *pcie;
- u64 dsn;
u32 slot_cap; /* capabilities and quirks */
unsigned int inband_presence_disabled:1;
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 4c62140a3cb44..c07957e0b37a8 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -587,7 +587,7 @@ bool pciehp_device_replaced(struct controller *ctrl)
reg != (pdev->subsystem_vendor | (pdev->subsystem_device << 16))))
return true;
- if (pci_get_dsn(pdev) != ctrl->dsn)
+ if (pci_get_dsn(pdev) != pdev->dsn)
return true;
return false;
@@ -1086,8 +1086,6 @@ struct controller *pcie_init(struct pcie_device *dev)
}
pdev = pci_get_slot(subordinate, PCI_DEVFN(0, 0));
- if (pdev)
- ctrl->dsn = pci_get_dsn(pdev);
pci_dev_put(pdev);
return ctrl;
diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c
index 65e50bee1a8c0..065e1f7c9a7bd 100644
--- a/drivers/pci/hotplug/pciehp_pci.c
+++ b/drivers/pci/hotplug/pciehp_pci.c
@@ -73,7 +73,6 @@ int pciehp_configure_device(struct controller *ctrl)
down_read_nested(&ctrl->reset_lock, ctrl->depth);
dev = pci_get_slot(parent, PCI_DEVFN(0, 0));
- ctrl->dsn = pci_get_dsn(dev);
pci_dev_put(dev);
out:
-Terry
^ permalink raw reply related
* [PATCH v3 16/16] PCI: dwc: ultrarisc: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
Inside .init we call dw_pcie_get_pcie_cap() to obtain the offset, because
the core has not yet cached it. Hardware is already enabled by the driver
before this point.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-ultrarisc, the call chain is:
static const struct dw_pcie_host_ops ultrarisc_pcie_host_ops = {
.init = ultrarisc_pcie_host_init,
};
ultrarisc_pcie_host_init()
-> dw_pcie_find_capability()
---
drivers/pci/controller/dwc/pcie-ultrarisc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-ultrarisc.c b/drivers/pci/controller/dwc/pcie-ultrarisc.c
index 6ee661ceff67..3f6e3ab0aa69 100644
--- a/drivers/pci/controller/dwc/pcie-ultrarisc.c
+++ b/drivers/pci/controller/dwc/pcie-ultrarisc.c
@@ -49,7 +49,7 @@ static int ultrarisc_pcie_host_init(struct dw_pcie_rp *pp)
FIELD_MODIFY(PORT_FLT_SF_MASK, &val, PORT_FLT_SF_VAL_64);
dw_pcie_writel_dbi(pci, PCIE_TIMER_CTRL_MAX_FUNC_NUM, val);
- cap_exp = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ cap_exp = dw_pcie_get_pcie_cap(pci);
val = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_LNKCTL2);
FIELD_MODIFY(PCI_EXP_LNKCTL2_TLS, &val, PCI_EXP_LNKCTL2_TLS_16_0GT);
dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_LNKCTL2, val);
--
2.34.1
^ permalink raw reply related
* [PATCH v3 11/16] PCI: dwc: qcom: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
dw_pcie_host_init() caches the offset after .init, so .post_init callbacks
and later functions (.link_up, icc_opp_update) can use pci->pcie_cap
directly. For .init itself, we must call dw_pcie_get_pcie_cap() inside
qcom_pcie_host_init() to obtain the offset (hardware is already enabled).
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-qcom, dw_pcie_find_capability() appears in multiple call chains:
static const struct dw_pcie_host_ops qcom_pcie_dw_ops = {
.init = qcom_pcie_host_init,
};
qcom_pcie_host_init()
-> qcom_pcie_clear_aspm_l0s()
-> dw_pcie_find_capability()
static const struct qcom_pcie_ops ops_* = {
.post_init = qcom_pcie_post_init_*,
};
qcom_pcie_post_init_*()
-> qcom_pcie_set_slot_nccs() (for many versions)
-> dw_pcie_find_capability()
-> For 2_3_3 and 2_9_0: also calls dw_pcie_find_capability() directly
static const struct dw_pcie_ops dw_pcie_ops = {
.link_up = qcom_pcie_link_up,
};
qcom_pcie_link_up()
-> dw_pcie_find_capability()
qcom_pcie_probe()
-> dw_pcie_host_init()
-> qcom_pcie_icc_opp_update()
-> dw_pcie_find_capability()
---
drivers/pci/controller/dwc/pcie-qcom.c | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index d8eb52857f69..bd383d7cb02b 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -341,13 +341,13 @@ static int qcom_pcie_start_link(struct dw_pcie *pci)
static void qcom_pcie_clear_aspm_l0s(struct dw_pcie *pci)
{
struct qcom_pcie *pcie = to_qcom_pcie(pci);
- u16 offset;
+ u8 offset;
u32 val;
if (!pcie->cfg->no_l0s)
return;
- offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ offset = dw_pcie_get_pcie_cap(pci);
dw_pcie_dbi_ro_wr_en(pci);
@@ -360,7 +360,6 @@ static void qcom_pcie_clear_aspm_l0s(struct dw_pcie *pci)
static void qcom_pcie_set_slot_nccs(struct dw_pcie *pci)
{
- u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
u32 val;
dw_pcie_dbi_ro_wr_en(pci);
@@ -370,9 +369,9 @@ static void qcom_pcie_set_slot_nccs(struct dw_pcie *pci)
* notifications for the Hot-Plug commands. So set the NCCS field to
* avoid waiting for the completions.
*/
- val = readl(pci->dbi_base + offset + PCI_EXP_SLTCAP);
+ val = readl(pci->dbi_base + pci->pcie_cap + PCI_EXP_SLTCAP);
val |= PCI_EXP_SLTCAP_NCCS;
- writel(val, pci->dbi_base + offset + PCI_EXP_SLTCAP);
+ writel(val, pci->dbi_base + pci->pcie_cap + PCI_EXP_SLTCAP);
dw_pcie_dbi_ro_wr_dis(pci);
}
@@ -935,7 +934,7 @@ static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie)
static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie)
{
struct dw_pcie *pci = pcie->pci;
- u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ u8 offset = pci->pcie_cap;
u32 val;
/* Force PHY out of lowest power state */
@@ -1257,7 +1256,7 @@ static int qcom_pcie_init_2_9_0(struct qcom_pcie *pcie)
static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie)
{
struct dw_pcie *pci = pcie->pci;
- u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ u8 offset = pci->pcie_cap;
u32 val;
int i;
@@ -1303,8 +1302,7 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie)
static bool qcom_pcie_link_up(struct dw_pcie *pci)
{
- u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
- u16 val = readw(pci->dbi_base + offset + PCI_EXP_LNKSTA);
+ u16 val = readw(pci->dbi_base + pci->pcie_cap + PCI_EXP_LNKSTA);
return val & PCI_EXP_LNKSTA_DLLLA;
}
@@ -1664,15 +1662,14 @@ static int qcom_pcie_icc_init(struct qcom_pcie *pcie)
static void qcom_pcie_icc_opp_update(struct qcom_pcie *pcie)
{
- u32 offset, status, width, speed;
+ u32 status, width, speed;
struct dw_pcie *pci = pcie->pci;
struct dev_pm_opp_key key = {};
unsigned long freq_kbps;
struct dev_pm_opp *opp;
int ret, freq_mbps;
- offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
- status = readw(pci->dbi_base + offset + PCI_EXP_LNKSTA);
+ status = readw(pci->dbi_base + pci->pcie_cap + PCI_EXP_LNKSTA);
/* Only update constraints if link is up. */
if (!(status & PCI_EXP_LNKSTA_DLLLA))
--
2.34.1
^ permalink raw reply related
* [PATCH v3 03/16] PCI: dwc: imx6: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
Thus, the cached offset is available when these functions are called, and
DBI access occurs only after hardware is enabled.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pci-imx6, dw_pcie_find_capability() is called in the following
call chain:
static const struct dw_pcie_ops dw_pcie_ops = {
.start_link = imx_pcie_start_link,
};
imx_pcie_start_link()
-> dw_pcie_find_capability()
-> imx_pcie_ltssm_enable()
-> dw_pcie_find_capability()
Replace these calls with the cached pci->pcie_cap. The call chain after
modification becomes:
dw_pcie_host_init()
-> pp->ops->init() [imx6_pcie_host_init]
-> dw_pcie_get_pcie_cap() (caches offset)
-> pci->ops->start_link = imx_pcie_start_link
-> uses pci->pcie_cap
-> imx_pcie_ltssm_enable()
-> uses pci->pcie_cap
---
drivers/pci/controller/dwc/pci-imx6.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 98e1db751132..74b28b4df484 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -966,10 +966,10 @@ static void imx_pcie_ltssm_enable(struct device *dev)
{
struct imx_pcie *imx_pcie = dev_get_drvdata(dev);
const struct imx_pcie_drvdata *drvdata = imx_pcie->drvdata;
- u8 offset = dw_pcie_find_capability(imx_pcie->pci, PCI_CAP_ID_EXP);
+ struct dw_pcie *pci = imx_pcie->pci;
u32 tmp;
- tmp = dw_pcie_readl_dbi(imx_pcie->pci, offset + PCI_EXP_LNKCAP);
+ tmp = dw_pcie_readl_dbi(pci, pci->pcie_cap + PCI_EXP_LNKCAP);
phy_set_speed(imx_pcie->phy, FIELD_GET(PCI_EXP_LNKCAP_SLS, tmp));
if (drvdata->ltssm_mask)
regmap_update_bits(imx_pcie->iomuxc_gpr, drvdata->ltssm_off, drvdata->ltssm_mask,
@@ -995,7 +995,7 @@ static int imx_pcie_start_link(struct dw_pcie *pci)
{
struct imx_pcie *imx_pcie = to_imx_pcie(pci);
struct device *dev = pci->dev;
- u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ u8 offset = pci->pcie_cap;
u32 tmp;
int ret;
--
2.34.1
^ permalink raw reply related
* [PATCH v3 09/16] PCI: dwc: intel-gw: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
dw_pcie_host_init() calls .init before caching the offset, so inside
.init we must call dw_pcie_get_pcie_cap() to obtain the offset (hardware
is already enabled). The helper will cache the result for later use.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-intel-gw, the call chain is:
static const struct dw_pcie_host_ops intel_pcie_dw_ops = {
.init = intel_pcie_rc_init,
};
intel_pcie_rc_init()
-> intel_pcie_host_setup()
-> intel_pcie_link_setup()
-> dw_pcie_find_capability()
---
drivers/pci/controller/dwc/pcie-intel-gw.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c
index 348e579e683f..a891c3f8eb01 100644
--- a/drivers/pci/controller/dwc/pcie-intel-gw.c
+++ b/drivers/pci/controller/dwc/pcie-intel-gw.c
@@ -119,7 +119,7 @@ static void intel_pcie_ltssm_disable(struct intel_pcie *pcie)
static void intel_pcie_link_setup(struct intel_pcie *pcie)
{
u32 val;
- u8 offset = dw_pcie_find_capability(&pcie->pci, PCI_CAP_ID_EXP);
+ u8 offset = dw_pcie_get_pcie_cap(&pcie->pci);
val = pcie_rc_cfg_rd(pcie, offset + PCI_EXP_LNKCTL);
--
2.34.1
^ permalink raw reply related
* [PATCH v3 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
Hi,
The DWC PCIe core and its many platform drivers repeatedly call
dw_pcie_find_capability(pci, PCI_CAP_ID_EXP) to obtain the offset of the
PCI Express Capability structure. This is wasteful and makes the code
verbose. Some drivers even search for the offset in suspend/resume paths.
Add a cached pcie_cap field in struct dw_pcie and a helper
dw_pcie_get_pcie_cap() to initialize it once at the point when the
hardware is ready. Then replace all explicit capability searches with
the cached value across the entire dwc subtree.
**Safety analysis: DBI access timing**
The PCIe Capability offset is read from DBI configuration space. DBI
registers are only accessible after the controller's clocks, resets, and
power are enabled. The following call graph demonstrates that all
dw_pcie_find_capability() replacements occur only after hardware is ready:
- For Root Complex mode:
dw_pcie_host_init()
...
if (pp->ops->init)
pp->ops->init [enables clocks/resets]
dw_pcie_get_pcie_cap() [caches offset]
...
dw_pcie_link_up
pci->ops->link_up
...
dw_pcie_start_link
pci->ops->start_link
...
pci_host_probe
if (pp->ops->post_init)
pp->ops->post_init
...
- For Endpoint mode:
The core no longer automatically caches the offset in dw_pcie_ep_init().
Instead, each EP driver must ensure hardware is enabled (clocks, power,
PHY) before calling dw_pcie_get_pcie_cap(). Drivers like qcom-ep and
tegra194 perform this in the PERST# deassert handler, where DBI is
accessible. The helper will then cache the offset for subsequent uses.
- Some platform drivers (e.g., layerscape-ep) enable hardware themselves
before calling dw_pcie_ep_init() and explicitly call
dw_pcie_get_pcie_cap() to cache the offset early. This is safe.
Thus, no DBI access occurs before hardware is operational.
Changes in v3:
- Remove automatic caching from dw_pcie_ep_init() to prevent unclocked
DBI access on EP platforms (qcom-ep, tegra194).
- Add explicit dw_pcie_get_pcie_cap() calls in qcom-ep and tegra194 EP
paths after hardware enablement.
- Keep Host mode caching unchanged.
- Update commit messages accordingly.
Changes in v2:
- Move dw_pcie_get_pcie_cap() from the beginning of dw_pcie_host_init()
and dw_pcie_ep_init() to after the hardware initialization callbacks
(host_init/pre_init). This can prevent the reading of DBI when the clock
is turned off or when the hardware is not ready.
- Convert all platform drivers to use the cached offset via
dw_pcie_get_pcie_cap() or directly pci->pcie_cap where safe.
- Fix type mismatches (u16 -> u8).
- Properly split per-driver modifications.
v1: https://patchwork.kernel.org/project/linux-pci/cover/20260509135152.2241235-1-18255117159@163.com/
v2: https://patchwork.kernel.org/project/linux-pci/patch/20260530153101.695580-1-18255117159@163.com/
---
**Why splitting into per-driver patches**
To facilitate review and potential bisection, each platform driver is
modified in its own patch. The core changes are in patches 1-2, and
follow-up patches convert individual glue drivers. If maintainers prefer
to squash them, please let me know and I will merge in v3.
This splitting patch of each controller was carried out solely for
the convenience of code review.
---
Hans Zhang (16):
PCI: dwc: Add pcie_cap field and helper in designware header
PCI: dwc: Use cached PCIe capability offset in core
PCI: dwc: imx6: Use cached PCIe capability offset
PCI: dwc: layerscape-ep: Use cached PCIe capability offset
PCI: dwc: meson: Use cached PCIe capability offset
PCI: dwc: rockchip: Use cached PCIe capability offset
PCI: dwc: eswin: Use cached PCIe capability offset
PCI: dwc: fu740: Use cached PCIe capability offset
PCI: dwc: intel-gw: Use cached PCIe capability offset
PCI: dwc: qcom-ep: Use cached PCIe capability offset
PCI: dwc: qcom: Use cached PCIe capability offset
PCI: dwc: sophgo: Use cached PCIe capability offset
PCI: dwc: spacemit-k1: Use cached PCIe capability offset
PCI: dwc: spear13xx: Use cached PCIe capability offset
PCI: dwc: tegra194: Use cached PCIe capability offset
PCI: dwc: ultrarisc: Use cached PCIe capability offset
drivers/pci/controller/dwc/pci-imx6.c | 6 +++---
.../pci/controller/dwc/pci-layerscape-ep.c | 9 +++-----
drivers/pci/controller/dwc/pci-meson.c | 4 ++--
.../pci/controller/dwc/pcie-designware-ep.c | 2 +-
.../pci/controller/dwc/pcie-designware-host.c | 2 ++
drivers/pci/controller/dwc/pcie-designware.c | 15 ++++++-------
drivers/pci/controller/dwc/pcie-designware.h | 17 +++++++++++++++
drivers/pci/controller/dwc/pcie-dw-rockchip.c | 2 +-
drivers/pci/controller/dwc/pcie-eswin.c | 3 +--
drivers/pci/controller/dwc/pcie-fu740.c | 2 +-
drivers/pci/controller/dwc/pcie-intel-gw.c | 2 +-
drivers/pci/controller/dwc/pcie-qcom-ep.c | 13 ++++++------
drivers/pci/controller/dwc/pcie-qcom.c | 21 ++++++++-----------
drivers/pci/controller/dwc/pcie-sophgo.c | 5 +++--
drivers/pci/controller/dwc/pcie-spacemit-k1.c | 2 +-
drivers/pci/controller/dwc/pcie-spear13xx.c | 2 +-
drivers/pci/controller/dwc/pcie-tegra194.c | 6 ++----
drivers/pci/controller/dwc/pcie-ultrarisc.c | 2 +-
18 files changed, 61 insertions(+), 54 deletions(-)
base-commit: 58717b2a1365d06c8c64b72aa948541b53fe31eb
--
2.34.1
^ permalink raw reply
* [PATCH v3 13/16] PCI: dwc: spacemit-k1: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
Because .init runs before core caching, we call dw_pcie_get_pcie_cap()
inside k1_pcie_disable_aspm_l1() to get the capability base, then add
PCI_EXP_LNKCAP. Hardware is already enabled at this point.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-spacemit-k1, the call chain is:
static const struct dw_pcie_host_ops k1_pcie_host_ops = {
.init = k1_pcie_init,
};
k1_pcie_init()
-> k1_pcie_disable_aspm_l1()
-> dw_pcie_find_capability()
---
drivers/pci/controller/dwc/pcie-spacemit-k1.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-spacemit-k1.c b/drivers/pci/controller/dwc/pcie-spacemit-k1.c
index 04241df8fd59..3e741324f44a 100644
--- a/drivers/pci/controller/dwc/pcie-spacemit-k1.c
+++ b/drivers/pci/controller/dwc/pcie-spacemit-k1.c
@@ -116,7 +116,7 @@ static void k1_pcie_disable_aspm_l1(struct k1_pcie *k1)
u8 offset;
u32 val;
- offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ offset = dw_pcie_get_pcie_cap(pci);
offset += PCI_EXP_LNKCAP;
dw_pcie_dbi_ro_wr_en(pci);
--
2.34.1
^ permalink raw reply related
* [PATCH v3 05/16] PCI: dwc: meson: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
dw_pcie_host_init() calls pp->ops->init (meson_pcie_host_init) before
caching the offset. Therefore, inside .init we must call
dw_pcie_get_pcie_cap() to obtain the offset (the helper will perform the
DBI read and cache the result). This is safe because the hardware is
already enabled by the driver's own initialization.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pci-meson, the call chain is:
static const struct dw_pcie_host_ops meson_pcie_host_ops = {
.init = meson_pcie_host_init,
};
meson_pcie_host_init()
-> meson_set_max_payload()
-> dw_pcie_find_capability()
-> meson_set_max_rd_req_size()
-> dw_pcie_find_capability()
---
drivers/pci/controller/dwc/pci-meson.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c
index 7a4da9fae1ea..c9d0388d262b 100644
--- a/drivers/pci/controller/dwc/pci-meson.c
+++ b/drivers/pci/controller/dwc/pci-meson.c
@@ -277,7 +277,7 @@ static void meson_set_max_payload(struct meson_pcie *mp, int size)
{
struct dw_pcie *pci = &mp->pci;
u32 val;
- u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ u8 offset = dw_pcie_get_pcie_cap(pci);
int max_payload_size = meson_size_to_payload(mp, size);
val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_DEVCTL);
@@ -293,7 +293,7 @@ static void meson_set_max_rd_req_size(struct meson_pcie *mp, int size)
{
struct dw_pcie *pci = &mp->pci;
u32 val;
- u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ u8 offset = dw_pcie_get_pcie_cap(pci);
int max_rd_req_size = meson_size_to_payload(mp, size);
val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_DEVCTL);
--
2.34.1
^ permalink raw reply related
* [PATCH v3 10/16] PCI: dwc: qcom-ep: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
dw_pcie_ep_init() no longer caches the offset automatically. For qcom-ep,
hardware (clocks, PHY) is enabled only after PERST# deassert. Therefore,
we must call dw_pcie_get_pcie_cap() in qcom_pcie_perst_deassert() after
the resources are enabled, before any DBI access. This ensures the cached
offset is valid for subsequent uses in interrupt handlers and ICC updates.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-qcom-ep, dw_pcie_find_capability() appears in:
qcom_pcie_ep_probe()
-> dw_pcie_ep_init()
-> qcom_pcie_ep_enable_irq_resources()
-> qcom_pcie_ep_global_irq_thread()
-> qcom_pcie_ep_icc_update()
-> dw_pcie_find_capability()
-> qcom_pcie_ep_perst_irq_thread()
-> qcom_pcie_perst_deassert()
-> offset = dw_pcie_find_capability() (called twice)
---
drivers/pci/controller/dwc/pcie-qcom-ep.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
index 56184e6ca6e6..16b0891f9571 100644
--- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
+++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
@@ -307,15 +307,13 @@ static void qcom_pcie_dw_write_dbi2(struct dw_pcie *pci, void __iomem *base,
static void qcom_pcie_ep_icc_update(struct qcom_pcie_ep *pcie_ep)
{
struct dw_pcie *pci = &pcie_ep->pci;
- u32 offset, status;
- int speed, width;
- int ret;
+ int speed, width, ret;
+ u32 status;
if (!pcie_ep->icc_mem)
return;
- offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
- status = readw(pci->dbi_base + offset + PCI_EXP_LNKSTA);
+ status = readw(pci->dbi_base + pci->pcie_cap + PCI_EXP_LNKSTA);
speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, status);
width = FIELD_GET(PCI_EXP_LNKSTA_NLW, status);
@@ -491,14 +489,15 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)
dw_pcie_dbi_ro_wr_en(pci);
+ dw_pcie_get_pcie_cap(pci);
+
/* Set the L0s Exit Latency to 2us-4us = 0x6 */
- offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ offset = pci->pcie_cap;
val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
FIELD_MODIFY(PCI_EXP_LNKCAP_L0SEL, &val, 0x6);
dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, val);
/* Set the L1 Exit Latency to be 32us-64 us = 0x6 */
- offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
FIELD_MODIFY(PCI_EXP_LNKCAP_L1EL, &val, 0x6);
dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, val);
--
2.34.1
^ permalink raw reply related
* [PATCH v3 12/16] PCI: dwc: sophgo: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
dw_pcie_host_init() calls .init before caching, so we must call
dw_pcie_get_pcie_cap() inside .init. The hardware is already enabled by
the driver's own initialization before this point. The helper will cache
the offset and avoid redundant searches.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-sophgo, the call chain is:
static const struct dw_pcie_host_ops sophgo_pcie_host_ops = {
.init = sophgo_pcie_host_init,
};
sophgo_pcie_host_init()
-> sophgo_pcie_disable_l0s_l1()
-> dw_pcie_find_capability()
---
drivers/pci/controller/dwc/pcie-sophgo.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-sophgo.c b/drivers/pci/controller/dwc/pcie-sophgo.c
index 044088898819..39703d2b7b5f 100644
--- a/drivers/pci/controller/dwc/pcie-sophgo.c
+++ b/drivers/pci/controller/dwc/pcie-sophgo.c
@@ -164,9 +164,10 @@ static void sophgo_pcie_msi_enable(struct dw_pcie_rp *pp)
static void sophgo_pcie_disable_l0s_l1(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
- u32 offset, val;
+ u8 offset;
+ u32 val;
- offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ offset = dw_pcie_get_pcie_cap(pci);
dw_pcie_dbi_ro_wr_en(pci);
--
2.34.1
^ permalink raw reply related
* [PATCH v3 15/16] PCI: dwc: tegra194: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
In host mode: tegra_pcie_dw_host_init() runs inside .start_link, which is
called after dw_pcie_host_init() has cached the offset. However, the
driver's own initialization already enables hardware, so calling
dw_pcie_get_pcie_cap() is safe and will return the cached value.
In endpoint mode: pex_ep_event_pex_rst_deassert() runs after hardware is
enabled (PERST# deassert). Because the core no longer caches automatically,
we call dw_pcie_get_pcie_cap() here to cache the offset for subsequent
accesses. The helper performs the DBI read only when hardware is ready.
Thus, the private pcie_cap_base is always valid and can still be used for
register accesses.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-tegra194, dw_pcie_find_capability() appears in:
static const struct dw_pcie_ops tegra_dw_pcie_ops = {
.start_link = tegra_pcie_dw_start_link,
};
tegra_pcie_dw_start_link()
-> tegra_pcie_dw_host_init()
-> dw_pcie_find_capability()
tegra_pcie_dw_probe()
case DW_PCIE_EP_TYPE:
tegra_pcie_config_ep()
-> pex_ep_event_pex_rst_deassert()
-> dw_pcie_find_capability()
---
drivers/pci/controller/dwc/pcie-tegra194.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 795cef5a915d..bf482bc66a92 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -905,8 +905,7 @@ static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp)
pp->bridge->ops = &tegra_pci_ops;
if (!pcie->pcie_cap_base)
- pcie->pcie_cap_base = dw_pcie_find_capability(&pcie->pci,
- PCI_CAP_ID_EXP);
+ pcie->pcie_cap_base = dw_pcie_get_pcie_cap(pci);
val = dw_pcie_readl_dbi(pci, PCI_IO_BASE);
val &= ~(IO_BASE_IO_DECODE | IO_BASE_IO_DECODE_BIT8);
@@ -1889,8 +1888,7 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
}
- pcie->pcie_cap_base = dw_pcie_find_capability(&pcie->pci,
- PCI_CAP_ID_EXP);
+ pcie->pcie_cap_base = dw_pcie_get_pcie_cap(pci);
/* Clear Slot Clock Configuration bit if SRNS configuration */
if (pcie->enable_srns) {
--
2.34.1
^ permalink raw reply related
* [PATCH v3 02/16] PCI: dwc: Use cached PCIe capability offset in core
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
Modify the DWC core functions to use the cached pcie_cap offset instead
of calling dw_pcie_find_capability() each time.
In the DWC core, dw_pcie_find_capability() is called at several locations:
- dw_pcie_ep_init_non_sticky_registers()
- dw_pcie_wait_for_link()
- dw_pcie_link_set_max_speed()
- dw_pcie_link_get_max_link_width()
- dw_pcie_link_set_max_link_width()
The cached offset is initialized after hardware is ready:
- In host mode: dw_pcie_host_init() calls pp->ops->host_init() (enables
clocks/resets), then dw_pcie_get_pcie_cap() caches the offset.
- In endpoint mode: the core no longer caches automatically. Instead,
drivers must call dw_pcie_get_pcie_cap() after hardware is enabled
(e.g., after PERST# deassert). dw_pcie_ep_init_non_sticky_registers()
is called after that point, so it can safely use pci->pcie_cap.
dw_pcie_ep_init_non_sticky_registers() now assumes pci->pcie_cap is
valid. if not, it prints a warning and skips the operation. The other
functions run after probe, so pci->pcie_cap is already valid and can
be used directly.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
drivers/pci/controller/dwc/pcie-designware-ep.c | 2 +-
drivers/pci/controller/dwc/pcie-designware-host.c | 2 ++
drivers/pci/controller/dwc/pcie-designware.c | 15 ++++++---------
3 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 7d2794945704..14228be8bbce 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -1246,7 +1246,7 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci)
* to all other functions as well.
*/
if (funcs > 1) {
- offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ offset = pci->pcie_cap;
func0_lnkcap = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
func0_lnkcap = FIELD_GET(PCI_EXP_LNKCAP_MLW |
PCI_EXP_LNKCAP_SLS, func0_lnkcap);
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 06722259d2e3..fee800ac56d4 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -593,6 +593,8 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
goto err_free_ecam;
}
+ dw_pcie_get_pcie_cap(pci);
+
if (pci_msi_enabled()) {
pp->use_imsi_rx = !(pp->ops->msi_init ||
of_property_present(np, "msi-parent") ||
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index ec4722ed9303..810729b91892 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -766,7 +766,7 @@ const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm)
*/
int dw_pcie_wait_for_link(struct dw_pcie *pci)
{
- u32 offset, val, ltssm;
+ u32 val, ltssm;
int retries;
/* Check if the link is up or not */
@@ -806,8 +806,7 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci)
pci_host_common_link_train_delay(pci->max_link_speed);
- offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
- val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA);
+ val = dw_pcie_readw_dbi(pci, pci->pcie_cap + PCI_EXP_LNKSTA);
dev_info(pci->dev, "PCIe Gen.%u x%u link up\n",
FIELD_GET(PCI_EXP_LNKSTA_CLS, val),
@@ -843,7 +842,7 @@ EXPORT_SYMBOL_GPL(dw_pcie_upconfig_setup);
static void dw_pcie_link_set_max_speed(struct dw_pcie *pci)
{
u32 cap, ctrl2, link_speed;
- u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ u8 offset = pci->pcie_cap;
cap = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
@@ -890,7 +889,7 @@ static void dw_pcie_link_set_max_speed(struct dw_pcie *pci)
int dw_pcie_link_get_max_link_width(struct dw_pcie *pci)
{
u8 cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
- u32 lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP);
+ u32 lnkcap = dw_pcie_readl_dbi(pci, pci->pcie_cap + PCI_EXP_LNKCAP);
return FIELD_GET(PCI_EXP_LNKCAP_MLW, lnkcap);
}
@@ -898,7 +897,6 @@ int dw_pcie_link_get_max_link_width(struct dw_pcie *pci)
static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes)
{
u32 lnkcap, lwsc, plc;
- u8 cap;
if (!num_lanes)
return;
@@ -935,10 +933,9 @@ static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes)
dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, plc);
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, lwsc);
- cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
- lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP);
+ lnkcap = dw_pcie_readl_dbi(pci, pci->pcie_cap + PCI_EXP_LNKCAP);
FIELD_MODIFY(PCI_EXP_LNKCAP_MLW, &lnkcap, num_lanes);
- dw_pcie_writel_dbi(pci, cap + PCI_EXP_LNKCAP, lnkcap);
+ dw_pcie_writel_dbi(pci, pci->pcie_cap + PCI_EXP_LNKCAP, lnkcap);
}
void dw_pcie_iatu_detect(struct dw_pcie *pci)
--
2.34.1
^ permalink raw reply related
* [PATCH v3 01/16] PCI: dwc: Add pcie_cap field and helper in designware header
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
Add a pcie_cap field to struct dw_pcie to store the offset of the
PCI Express Capability structure. Provide a helper dw_pcie_get_pcie_cap()
which performs the capability search on first call and caches the result.
This is a preparatory step for replacing repetitive capability searches
in both core and platform drivers.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
drivers/pci/controller/dwc/pcie-designware.h | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index de4b245b1758..8113efd33a24 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -591,6 +591,8 @@ struct dw_pcie {
* use_parent_dt_ranges to true to avoid this warning.
*/
bool use_parent_dt_ranges;
+
+ u8 pcie_cap; /* PCIe capability offset */
};
#define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp)
@@ -829,6 +831,21 @@ static inline void dw_pcie_dbi_ro_wr_dis(struct dw_pcie *pci)
dw_pcie_writel_dbi(pci, reg, val);
}
+/**
+ * dw_pcie_get_pcie_cap() - Return cached PCIe Capability offset
+ * @pci: DWC instance
+ *
+ * Finds and caches the offset of PCI_CAP_ID_EXP on first call.
+ * Returns 0 if the capability is not present.
+ */
+static inline u8 dw_pcie_get_pcie_cap(struct dw_pcie *pci)
+{
+ if (!pci->pcie_cap)
+ pci->pcie_cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+
+ return pci->pcie_cap;
+}
+
static inline int dw_pcie_start_link(struct dw_pcie *pci)
{
if (pci->ops && pci->ops->start_link)
--
2.34.1
^ permalink raw reply related
* [PATCH v3 06/16] PCI: dwc: rockchip: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang, Sebastian Reichel
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
dw_pcie_host_init() calls .init before caching the offset. So inside
.init we call dw_pcie_get_pcie_cap() to trigger caching. The helper will
perform the DBI read (hardware is already enabled) and cache the result.
Signed-off-by: Hans Zhang <18255117159@163.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
In pcie-dw-rockchip, the call chain is:
static const struct dw_pcie_host_ops rockchip_pcie_host_ops = {
.init = rockchip_pcie_host_init,
};
rockchip_pcie_host_init()
-> rockchip_pcie_enable_l0s()
-> dw_pcie_find_capability()
---
drivers/pci/controller/dwc/pcie-dw-rockchip.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
index 731d93663cca..be8b6187913d 100644
--- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
+++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
@@ -369,7 +369,7 @@ static void rockchip_pcie_enable_l0s(struct dw_pcie *pci)
u32 cap, lnkcap;
/* Enable L0S capability for all SoCs */
- cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ cap = dw_pcie_get_pcie_cap(pci);
if (cap) {
lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP);
lnkcap |= PCI_EXP_LNKCAP_ASPM_L0S;
--
2.34.1
^ permalink raw reply related
* [PATCH v3 08/16] PCI: dwc: fu740: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
This callback is invoked after dw_pcie_host_init() has cached the offset,
so we can use pci->pcie_cap directly.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-fu740, the call chain is:
static const struct dw_pcie_ops dw_pcie_ops = {
.start_link = fu740_pcie_start_link,
};
fu740_pcie_start_link()
-> dw_pcie_find_capability()
---
drivers/pci/controller/dwc/pcie-fu740.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-fu740.c b/drivers/pci/controller/dwc/pcie-fu740.c
index d0a34f680397..72c0a6316fff 100644
--- a/drivers/pci/controller/dwc/pcie-fu740.c
+++ b/drivers/pci/controller/dwc/pcie-fu740.c
@@ -178,7 +178,7 @@ static int fu740_pcie_start_link(struct dw_pcie *pci)
{
struct device *dev = pci->dev;
struct fu740_pcie *afp = dev_get_drvdata(dev);
- u8 cap_exp = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ u8 cap_exp = pci->pcie_cap;
int ret;
u32 orig, tmp;
--
2.34.1
^ permalink raw reply related
* [PATCH v3 14/16] PCI: dwc: spear13xx: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
Inside .init we must call dw_pcie_get_pcie_cap() to obtain the offset,
because the core has not yet cached it. The hardware is already enabled
by the driver's own initialization before this point.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-spear13xx, the call chain is:
static const struct dw_pcie_host_ops spear13xx_pcie_host_ops = {
.init = spear13xx_pcie_host_init,
};
spear13xx_pcie_host_init()
-> dw_pcie_find_capability()
---
drivers/pci/controller/dwc/pcie-spear13xx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-spear13xx.c b/drivers/pci/controller/dwc/pcie-spear13xx.c
index 01794a9d3ad2..6e4c11b497d4 100644
--- a/drivers/pci/controller/dwc/pcie-spear13xx.c
+++ b/drivers/pci/controller/dwc/pcie-spear13xx.c
@@ -122,7 +122,7 @@ static int spear13xx_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pci);
- u32 exp_cap_off = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ u8 exp_cap_off = dw_pcie_get_pcie_cap(pci);
u32 val;
spear13xx_pcie->app_base = pci->dbi_base + 0x2000;
--
2.34.1
^ permalink raw reply related
* [PATCH v3 07/16] PCI: dwc: eswin: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
This function is called after dw_pcie_host_init() has already cached the
offset (dw_pcie_link_up() is called after .init and after caching).
Therefore, we can directly use pci->pcie_cap.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-eswin, the call chain is:
static const struct dw_pcie_ops dw_pcie_ops = {
.link_up = eswin_pcie_link_up,
};
eswin_pcie_link_up()
-> dw_pcie_find_capability()
---
drivers/pci/controller/dwc/pcie-eswin.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-eswin.c b/drivers/pci/controller/dwc/pcie-eswin.c
index ce8d64f8a395..6a37fd5a8384 100644
--- a/drivers/pci/controller/dwc/pcie-eswin.c
+++ b/drivers/pci/controller/dwc/pcie-eswin.c
@@ -84,8 +84,7 @@ static int eswin_pcie_start_link(struct dw_pcie *pci)
static bool eswin_pcie_link_up(struct dw_pcie *pci)
{
- u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
- u16 val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA);
+ u16 val = dw_pcie_readw_dbi(pci, pci->pcie_cap + PCI_EXP_LNKSTA);
return val & PCI_EXP_LNKSTA_DLLLA;
}
--
2.34.1
^ permalink raw reply related
* [PATCH v3 04/16] PCI: dwc: layerscape-ep: Use cached PCIe capability offset
From: Hans Zhang @ 2026-07-20 15:06 UTC (permalink / raw)
To: bhelgaas, lpieralisi, kwilczynski, mani, jingoohan1
Cc: imx, linuxppc-dev, linux-amlogic, linux-rockchip, linux-arm-msm,
sophgo, linux-riscv, spacemit, linux-tegra, linux-pci,
linux-kernel, Hans Zhang
In-Reply-To: <20260720150619.24733-1-18255117159@163.com>
Replace these with dw_pcie_get_pcie_cap(). The hardware is already enabled
by the driver before ls_pcie_ep_probe() and before the interrupt handler
runs. dw_pcie_get_pcie_cap() will cache the offset on first call, and
subsequent calls (including inside dw_pcie_ep_init) will use the cached
value without re-searching.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pci-layerscape-ep, dw_pcie_find_capability() is called in:
ls_pcie_ep_probe()
-> offset = dw_pcie_find_capability()
-> pcie->lnkcap = dw_pcie_readl_dbi(offset + PCI_EXP_LNKCAP)
-> dw_pcie_ep_init()
-> ls_pcie_ep_interrupt_init()
-> devm_request_irq(..., ls_pcie_ep_event_handler)
-> ls_pcie_ep_event_handler()
-> dw_pcie_find_capability()
---
drivers/pci/controller/dwc/pci-layerscape-ep.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
index 8936975ff104..b2d0b51df3c5 100644
--- a/drivers/pci/controller/dwc/pci-layerscape-ep.c
+++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
@@ -74,7 +74,6 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id)
struct ls_pcie_ep *pcie = dev_id;
struct dw_pcie *pci = pcie->pci;
u32 val, cfg;
- u8 offset;
val = ls_pcie_pf_lut_readl(pcie, PEX_PF0_PME_MES_DR);
ls_pcie_pf_lut_writel(pcie, PEX_PF0_PME_MES_DR, val);
@@ -83,9 +82,6 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id)
return IRQ_NONE;
if (val & PEX_PF0_PME_MES_DR_LUD) {
-
- offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
-
/*
* The values of the Maximum Link Width and Supported Link
* Speed from the Link Capabilities Register will be lost
@@ -93,7 +89,8 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id)
* that configured by the Reset Configuration Word (RCW).
*/
dw_pcie_dbi_ro_wr_en(pci);
- dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, pcie->lnkcap);
+ dw_pcie_writel_dbi(pci, pci->pcie_cap + PCI_EXP_LNKCAP,
+ pcie->lnkcap);
dw_pcie_dbi_ro_wr_dis(pci);
cfg = ls_pcie_pf_lut_readl(pcie, PEX_PF0_CONFIG);
@@ -266,7 +263,7 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pcie);
- offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ offset = dw_pcie_get_pcie_cap(pci);
pcie->lnkcap = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
ret = dw_pcie_ep_init(&pci->ep);
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox