From: Aksh Garg <a-garg7@ti.com>
To: Hans Zhang <18255117159@163.com>,
Bjorn Helgaas <helgaas@kernel.org>,
Bjorn Helgaas <bhelgaas@google.com>,
Siddharth Vadapalli <s-vadapalli@ti.com>
Cc: <sashiko@lists.linux.dev>, <linux-pci@vger.kernel.org>
Subject: Re: [PATCH] PCI: cadence: Use cdns_pcie_find_capability() to get PCIe Cap offset in host driver
Date: Thu, 7 May 2026 18:01:02 +0530 [thread overview]
Message-ID: <181c2204-dfb6-4484-b4e4-069d8c7445fd@ti.com> (raw)
In-Reply-To: <2e92d011-02fa-433f-92e3-142880d5d0d2@163.com>
On 07/05/26 09:18, Hans Zhang wrote:
>
>
> On 5/7/26 11:31, Hans Zhang wrote:
>>
>>
>> On 5/7/26 01:12, Bjorn Helgaas wrote:
>>> On Thu, May 07, 2026 at 12:04:34AM +0800, Hans Zhang wrote:
>>>> On 5/6/26 05:23, Bjorn Helgaas wrote:
>>>>> On Mon, May 04, 2026 at 04:22:26PM +0800, Hans Zhang wrote:
>>>>>> On 5/4/26 00:52, sashiko-bot@kernel.org wrote:
>>>>>>> Thank you for your contribution! Sashiko AI review found 1
>>>>>>> potential issue(s) to consider:
>>>>>>>
>>>>>>> - [Critical] cdns_pcie_find_capability() searches the wrong
>>>>>>> register space in Host mode and its return value is not validated,
>>>>>>> leading to Root Port configuration space corruption.
>>>>>>> --
>>>>>>>
>>>>>>> commit 09b3c64b8f5892eb365acd13030a8067652a15e8
>>>>>>> Author: Hans Zhang <18255117159@163.com>
>>>>>>>
>>>>>>> PCI: cadence: Use cdns_pcie_find_capability() to get PCIe Cap
>>>>>>> offset in host driver
>>>>>>>
>>>>>>> This commit replaces the hardcoded CDNS_PCIE_RP_CAP_OFFSET with a
>>>>>>> dynamic
>>>>>>> lookup using cdns_pcie_find_capability() in Cadence PCIe host-
>>>>>>> related
>>>>>>> functions. The goal is to ensure correct operation across various
>>>>>>> SoC designs
>>>>>>> where the capability might not reside at offset 0xC0.
>>>>>>>
>>>>>>>> diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-
>>>>>>>> common.c b/drivers/pci/controller/cadence/pcie-cadence-host-
>>>>>>>> common.c
>>>>>>>> index 2b0211870f02..5013d3fe23b5 100644
>>>>>>>> --- a/drivers/pci/controller/cadence/pcie-cadence-host-common.c
>>>>>>>> +++ b/drivers/pci/controller/cadence/pcie-cadence-host-common.c
>>>>>>>> @@ -26,14 +26,14 @@ EXPORT_SYMBOL_GPL(bar_max_size);
>>>>>>>> int cdns_pcie_host_training_complete(struct cdns_pcie *pcie)
>>>>>>>> {
>>>>>>>> - u32 pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET;
>>>>>>>> unsigned long end_jiffies;
>>>>>>>> u16 lnk_stat;
>>>>>>>> + u8 cap = cdns_pcie_find_capability(pcie, PCI_CAP_ID_EXP);
>>>>>>>
>>>>>>> Does this capability search scan the correct register space when
>>>>>>> operating in host mode?
>>>>>>>
>>>>>>> Looking at the helper function, it uses cdns_pcie_read_cfg() which
>>>>>>> reads directly from the base address without the Root Port offset:
>>>>>>>
>>>>>>> drivers/pci/controller/cadence/pcie-
>>>>>>> cadence.c:cdns_pcie_find_capability() {
>>>>>>> return PCI_FIND_NEXT_CAP(cdns_pcie_read_cfg,
>>>>>>> PCI_CAPABILITY_LIST,
>>>>>>> cap, NULL, pcie);
>>>>>>> }
>>>>>>>
>>>>>>> However, the Root Port configuration registers are physically
>>>>>>> located at pcie->reg_base + CDNS_PCIE_RP_BASE. Could this cause
>>>>>>> the search to scan the Local Management register space instead,
>>>>>>> failing to find PCI_CAP_ID_EXP and returning 0?
>>>>>>
>>>>>> I don't think we need to worry about this issue.
>>>>>>
>>>>>> As per PCIe r7.0, sec 7.5.1.1.11, Since all PCI Express Functions
>>>>>> are required to implement the PCI Express Capability structure,
>>>>>> which must be included somewhere in this linked list;
>>>>>>
>>>>>> So, it won't return 0 here.
>>>>>
>>>>> It's true that all Root Ports must have a PCIe Capability, but
>>>>> that's not related to this issue.
>>>>>
>>>>> cdns_pcie_host_init_root_port() accesses PCI_EXP_LNKCAP at the
>>>>> address:
>>>>>
>>>>> pcie->reg_base + CDNS_PCIE_RP_BASE + CDNS_PCIE_RP_CAP_OFFSET +
>>>>> PCI_EXP_LNKCAP
>>>>>
>>>>> but when we search with cdns_pcie_find_capability(pcie,
>>>>> PCI_CAP_ID_EXP),
>>>>> we start reading at:
>>>>>
>>>>> pcie->reg_base + PCI_CAPABILITY_LIST
>>>>>
>>>>> It should be starting at:
>>>>>
>>>>> pcie->reg_base + CDNS_PCIE_RP_BASE + PCI_CAPABILITY_LIST
>>>>>
>>>>> Previously, cdns_pcie_find_capability() and
>>>>> cdns_pcie_find_ext_capability() were only used for endpoints, and I
>>>>> assume they work fine there. There is pcie->is_rc, so there should be
>>>>> a way to make this work for both endpoints and Root Ports.
>>>>
>>>> The reason for using the "is_rc" tag is that for Cadence IP, it is
>>>> not only
>>>> applicable to the RC or EP mode, but also there are significant
>>>> differences
>>>> between the LGA and HPA generations of IP. Including register offset
>>>> values,
>>>> definitions, etc., it fails to achieve good compatibility. It is not
>>>> like
>>>> Synopsys IP, where compatibility has been handled very well. It was
>>>> truly
>>>> out of necessity.
>>>
>>> I think cdns_pcie_find_capability(pcie, PCI_CAP_ID_EXP) fails on Root
>>> Ports because it doesn't include the CDNS_PCIE_RP_BASE offset. Do you
>>> have hardware where you can test that?
>
> Hi Siddharth,
>
> Could you please test the functions mentioned above? It would be great
> if you could help test it and give us your feedback. (pci-j721e.c)
>
> I guess the following changes might need to be made.
>
> diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/
> pci/controller/cadence/pcie-cadence.h
> index 574e9cf4d003..bb01761749f1 100644
> --- a/drivers/pci/controller/cadence/pcie-cadence.h
> +++ b/drivers/pci/controller/cadence/pcie-cadence.h
> @@ -298,6 +298,9 @@ static inline int cdns_pcie_read_cfg_byte(struct
> cdns_pcie *pcie, int where,
> {
> void __iomem *addr = pcie->reg_base + where;
>
> + if ((pcie->is_rc) && (!pcie->is_hpa))
> + addr += CDNS_PCIE_RP_BASE;
> +
> *val = cdns_pcie_read_sz(addr, 0x1);
> return PCIBIOS_SUCCESSFUL;
> }
> @@ -307,6 +310,9 @@ static inline int cdns_pcie_read_cfg_word(struct
> cdns_pcie *pcie, int where,
> {
> void __iomem *addr = pcie->reg_base + where;
>
> + if ((pcie->is_rc) && (!pcie->is_hpa))
> + addr += CDNS_PCIE_RP_BASE;
> +
> *val = cdns_pcie_read_sz(addr, 0x2);
> return PCIBIOS_SUCCESSFUL;
> }
> @@ -314,7 +320,12 @@ static inline int cdns_pcie_read_cfg_word(struct
> cdns_pcie *pcie, int where,
> static inline int cdns_pcie_read_cfg_dword(struct cdns_pcie *pcie, int
> where,
> u32 *val)
> {
> - *val = cdns_pcie_readl(pcie, where);
> + void __iomem *addr = pcie->reg_base + where;
> +
> + if ((pcie->is_rc) && (!pcie->is_hpa))
> + addr += CDNS_PCIE_RP_BASE;
> +
> + *val = cdns_pcie_read_sz(addr, 0x4);
> return PCIBIOS_SUCCESSFUL;
> }
>
>
> "is_hpa" flag I added it in another series. As mentioned before, the
> compatibility of Cadence IP is not very good. It is divided into LGA and
> HPA IP. And there are many differences between the Root Port and the
> Endpoint.
>
> https://patchwork.kernel.org/project/linux-pci/
> patch/20260406103237.1203127-2-18255117159@163.com/
>
> Best regards,
> Hans
>
Hi Hans,
I have tested this on J7200 and J721E SoCs, which uses pci-j721e.c
driver for their PCIe controllers. The patch alone fails to find the
capabilities using cdns_pcie_find_capability() for Root Port.
With the "is_hpa" flag patch, and the patch "PCI: cadence: Use
cdns_pcie_find_capability() to get PCIe Cap offset in host driver" along
with the diff suggested above:
Tested-by: Aksh Garg <a-garg7@ti.com>
I would also like me to be included in the CC list for future
correspondence regarding Cadence PCIe Controller patches.
Regards,
Aksh Garg
>>
>> Hi Bjorn,
>>
>> The attachment contains the dmesg and lspci -vvv log from the test.
>>
>> Here is the test code:
>>
>> diff --git a/drivers/pci/controller/cadence/pci-sky1.c b/drivers/pci/
>> controller/cadence/pci-sky1.c
>> index cd55c64e58a9..0d0c42309127 100644
>> --- a/drivers/pci/controller/cadence/pci-sky1.c
>> +++ b/drivers/pci/controller/cadence/pci-sky1.c
>> @@ -130,6 +130,39 @@ static const struct cdns_pcie_ops sky1_pcie_ops = {
>> .link_up = sky1_pcie_link_up,
>> };
>>
>> +void cix_pcie_test_cap(struct sky1_pcie *pcie)
>> +{
>> + u16 offset;
>> +
>> + printk(KERN_EMERG"[HANS] fun = %s, line = %d ........... \n",
>> __func__, __LINE__);
>> + /* capability */
>> + offset = cdns_pcie_find_capability(pcie->cdns_pcie, PCI_CAP_ID_PM);
>> + printk(KERN_EMERG"[HANS]pm offset = 0x%x\n", offset);
>> + offset = cdns_pcie_find_capability(pcie->cdns_pcie, PCI_CAP_ID_MSI);
>> + printk(KERN_EMERG"[HANS]msi offset = 0x%x\n", offset);
>> + offset = cdns_pcie_find_capability(pcie->cdns_pcie,
>> PCI_CAP_ID_MSIX);
>> + printk(KERN_EMERG"[HANS]msix offset = 0x%x\n", offset);
>> + offset = cdns_pcie_find_capability(pcie->cdns_pcie, PCI_CAP_ID_EXP);
>> + printk(KERN_EMERG"[HANS]exp offset = 0x%x\n", offset);
>> +
>> + printk(KERN_EMERG"[HANS] fun = %s, line = %d ........... \n",
>> __func__, __LINE__);
>> + /* extend capability */
>> + offset = cdns_pcie_find_ext_capability(pcie->cdns_pcie,
>> PCI_EXT_CAP_ID_ERR);
>> + printk(KERN_EMERG"[HANS]aer offset = 0x%x\n", offset);
>> + offset = cdns_pcie_find_ext_capability(pcie->cdns_pcie,
>> PCI_EXT_CAP_ID_VC);
>> + printk(KERN_EMERG"[HANS]Virtual Channel offset = 0x%x\n", offset);
>> + offset = cdns_pcie_find_ext_capability(pcie->cdns_pcie,
>> PCI_EXT_CAP_ID_DSN);
>> + printk(KERN_EMERG"[HANS]Device Serial Number offset = 0x%x\n",
>> offset);
>> + offset = cdns_pcie_find_ext_capability(pcie->cdns_pcie,
>> PCI_EXT_CAP_ID_PWR);
>> + printk(KERN_EMERG"[HANS]pwr offset = 0x%x\n", offset);
>> + offset = cdns_pcie_find_ext_capability(pcie->cdns_pcie,
>> PCI_EXT_CAP_ID_REBAR);
>> + printk(KERN_EMERG"[HANS]resize bar offset = 0x%x\n", offset);
>> + offset = cdns_pcie_find_ext_capability(pcie->cdns_pcie,
>> PCI_EXT_CAP_ID_SECPCI);
>> + printk(KERN_EMERG"[HANS]second exp offset = 0x%x\n", offset);
>> + offset = cdns_pcie_find_ext_capability(pcie->cdns_pcie,
>> PCI_EXT_CAP_ID_L1SS);
>> + printk(KERN_EMERG"[HANS]L1ss offset = 0x%x\n", offset);
>> +}
>> +
>> static int sky1_pcie_probe(struct platform_device *pdev)
>> {
>> struct cdns_plat_pcie_of_data *reg_off;
>> @@ -141,6 +174,7 @@ static int sky1_pcie_probe(struct platform_device
>> *pdev)
>> struct sky1_pcie *pcie;
>> int ret;
>>
>> + printk(KERN_EMERG"[HANS] fun = %s, line = %d ........... \n",
>> __func__, __LINE__);
>> pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
>> if (!pcie)
>> return -ENOMEM;
>> @@ -202,6 +236,8 @@ static int sky1_pcie_probe(struct platform_device
>> *pdev)
>>
>> dev_set_drvdata(dev, pcie);
>>
>> + cix_pcie_test_cap(pcie);
>> +
>> ret = cdns_pcie_hpa_host_setup(rc);
>> if (ret < 0) {
>> pci_ecam_free(pcie->cfg);
>> @@ -230,7 +266,7 @@ static struct platform_driver sky1_pcie_driver = {
>> .driver = {
>> .name = "sky1-pcie",
>> .of_match_table = of_sky1_pcie_match,
>> - .probe_type = PROBE_PREFER_ASYNCHRONOUS,
>> + // .probe_type = PROBE_PREFER_ASYNCHRONOUS,
>> },
>> };
>> module_platform_driver(sky1_pcie_driver);
>>
>>
>> For the LGA IP, an offset address CDNS_PCIE_RP_BASE needs to be added.
>> And the HPA IP requires an offset address of CDNS_PCIE_HPA_RP_BASE,
>> but it is equal to 0;
>>
>> drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h
>>
>> /* Root Port register base address */
>> #define CDNS_PCIE_HPA_RP_BASE 0x0
>>
>> Therefore, for "drivers/pci/controller/cadence/pci-sky1.c", it is normal.
>>
>>
>>
>> Using the following code for work is also normal. Later, I will re-
>> unify it into the same operation.
>>
>> diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/
>> pci/controller/cadence/pcie-cadence.h
>> index 574e9cf4d003..723ea79fb02e 100644
>> --- a/drivers/pci/controller/cadence/pcie-cadence.h
>> +++ b/drivers/pci/controller/cadence/pcie-cadence.h
>> @@ -314,7 +314,9 @@ static inline int cdns_pcie_read_cfg_word(struct
>> cdns_pcie *pcie, int where,
>> static inline int cdns_pcie_read_cfg_dword(struct cdns_pcie *pcie,
>> int where,
>> u32 *val)
>> {
>> - *val = cdns_pcie_readl(pcie, where);
>> + void __iomem *addr = pcie->reg_base + where;
>> +
>> + *val = cdns_pcie_read_sz(addr, 0x4);
>> return PCIBIOS_SUCCESSFUL;
>> }
>>
>>
>> Best regards,
>> Hans
>>
>>
>
next prev parent reply other threads:[~2026-05-07 12:31 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-03 16:19 [PATCH] PCI: cadence: Use cdns_pcie_find_capability() to get PCIe Cap offset in host driver Hans Zhang
2026-05-03 16:52 ` sashiko-bot
2026-05-04 8:22 ` Hans Zhang
2026-05-05 21:23 ` Bjorn Helgaas
2026-05-06 16:04 ` Hans Zhang
2026-05-06 17:12 ` Bjorn Helgaas
2026-05-07 3:31 ` Hans Zhang
2026-05-07 3:48 ` Hans Zhang
2026-05-07 12:31 ` Aksh Garg [this message]
2026-05-07 15:21 ` Hans Zhang
2026-05-07 15:32 ` Bjorn Helgaas
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=181c2204-dfb6-4484-b4e4-069d8c7445fd@ti.com \
--to=a-garg7@ti.com \
--cc=18255117159@163.com \
--cc=bhelgaas@google.com \
--cc=helgaas@kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=s-vadapalli@ti.com \
--cc=sashiko@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox