]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
PCI: dwc: Add dw_pcie_parent_bus_offset() checking and debug
authorFrank Li <Frank.Li@nxp.com>
Sat, 15 Mar 2025 20:15:41 +0000 (15:15 -0500)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 24 Mar 2025 19:58:01 +0000 (14:58 -0500)
dw_pcie_parent_bus_offset() looks up the parent bus address of a PCI
controller 'reg' property in devicetree.  If implemented, .cpu_addr_fixup()
is a hard-coded way to get the parent bus address corresponding to a CPU
physical address.

Add debug code to compare the address from .cpu_addr_fixup() with the
address from devicetree.  If they match, warn that .cpu_addr_fixup() is
redundant and should be removed; if they differ, warn that something is
wrong with the devicetree.

If .cpu_addr_fixup() is not implemented, the parent bus address should be
identical to the CPU physical address because we previously ignored the
parent bus address from devicetree.  If the devicetree has a different
parent bus address, warn about it being broken.

[bhelgaas: split debug to separate patch for easier future revert, commit
log]

Link: https://lore.kernel.org/r/20250315201548.858189-7-helgaas@kernel.org
Signed-off-by: Frank Li <Frank.Li@nxp.com>
[bhelgaas: squash Ioana Ciornei <ioana.ciornei@nxp.com> fix for NULL
pointer deref when driver doesn't supply dw_pcie_ops, e.g., layerscape-pcie
https://lore.kernel.org/r/20250319134339.3114817-1-ioana.ciornei@nxp.com]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
drivers/pci/controller/dwc/pcie-designware.c
drivers/pci/controller/dwc/pcie-designware.h

index 27b464a405a402efe3aa39962c2af9da01737c9a..4b442d1aa55b50ca527ab6776fd866849cab2b0e 100644 (file)
@@ -1114,7 +1114,8 @@ resource_size_t dw_pcie_parent_bus_offset(struct dw_pcie *pci,
        struct device *dev = pci->dev;
        struct device_node *np = dev->of_node;
        int index;
-       u64 reg_addr;
+       u64 reg_addr, fixup_addr;
+       u64 (*fixup)(struct dw_pcie *pcie, u64 cpu_addr);
 
        /* Look up reg_name address on parent bus */
        index = of_property_match_string(np, "reg-names", reg_name);
@@ -1126,5 +1127,42 @@ resource_size_t dw_pcie_parent_bus_offset(struct dw_pcie *pci,
 
        of_property_read_reg(np, index, &reg_addr, NULL);
 
+       fixup = pci->ops ? pci->ops->cpu_addr_fixup : NULL;
+       if (fixup) {
+               fixup_addr = fixup(pci, cpu_phys_addr);
+               if (reg_addr == fixup_addr) {
+                       dev_info(dev, "%s reg[%d] %#010llx == %#010llx == fixup(cpu %#010llx); %ps is redundant with this devicetree\n",
+                                reg_name, index, reg_addr, fixup_addr,
+                                (unsigned long long) cpu_phys_addr, fixup);
+               } else {
+                       dev_warn(dev, "%s reg[%d] %#010llx != %#010llx == fixup(cpu %#010llx); devicetree is broken\n",
+                                reg_name, index, reg_addr, fixup_addr,
+                                (unsigned long long) cpu_phys_addr);
+                       reg_addr = fixup_addr;
+               }
+
+               return cpu_phys_addr - reg_addr;
+       }
+
+       if (pci->use_parent_dt_ranges) {
+
+               /*
+                * This platform once had a fixup, presumably because it
+                * translates between CPU and PCI controller addresses.
+                * Log a note if devicetree didn't describe a translation.
+                */
+               if (reg_addr == cpu_phys_addr)
+                       dev_info(dev, "%s reg[%d] %#010llx == cpu %#010llx\n; no fixup was ever needed for this devicetree\n",
+                                reg_name, index, reg_addr,
+                                (unsigned long long) cpu_phys_addr);
+       } else {
+               if (reg_addr != cpu_phys_addr) {
+                       dev_warn(dev, "%s reg[%d] %#010llx != cpu %#010llx; no fixup and devicetree \"ranges\" is broken, assuming no translation\n",
+                                reg_name, index, reg_addr,
+                                (unsigned long long) cpu_phys_addr);
+                       return 0;
+               }
+       }
+
        return cpu_phys_addr - reg_addr;
 }
index 16548b01347d1553982dc08ebb3a57ff019523ac..f08d2852cfd590d0b1837645b5dbbcacd5d6cc46 100644 (file)
@@ -465,6 +465,19 @@ struct dw_pcie {
        struct reset_control_bulk_data  core_rsts[DW_PCIE_NUM_CORE_RSTS];
        struct gpio_desc                *pe_rst;
        bool                    suspended;
+
+       /*
+        * If iATU input addresses are offset from CPU physical addresses,
+        * we previously required .cpu_addr_fixup() to convert them.  We
+        * now rely on the devicetree instead.  If .cpu_addr_fixup()
+        * exists, we compare its results with devicetree.
+        *
+        * If .cpu_addr_fixup() does not exist, we assume the offset is
+        * zero and warn if devicetree claims otherwise.  If we know all
+        * devicetrees correctly describe the offset, set
+        * use_parent_dt_ranges to true to avoid this warning.
+        */
+       bool                    use_parent_dt_ranges;
 };
 
 #define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp)