diff mbox series

[v10,04/10] PCI: dwc: Add helper dw_pcie_init_parent_bus_offset()

Message ID 20250310-pci_fixup_addr-v10-4-409dafc950d1@nxp.com (mailing list archive)
State Superseded
Headers show
Series PCI: Use device bus range info to cleanup RC Host/EP pci_fixup_addr() | expand

Commit Message

Frank Li March 10, 2025, 8:16 p.m. UTC
From: Bjorn Helgaas <bhelgaas@google.com>

Set pci->parent_bus_offset based on the parent bus address from the
"config" (Root complex mode) and "addr_space" (Endpoint mode).

.cpu_addr_fixup(cpu_phy_addr). (if implemented) should return the parent
bus address corresponding according to cpu_phy_addr.

Sets pp->parent_bus_offset, but doesn't use it, so no functional change
intended yet.

Add use_parent_dt_ranges to detect some fake bus translation at platform,
which have not .cpu_addr_fixup(). Set use_parent_dt_ranges true explicitly
at platform that have .cpu_addr_fixup() and fixed DTB's range. If not one
report "fake bus translation" for sometime, this flags can be removed
safely.

Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change from v9 to v10
v9: https://lore.kernel.org/imx/20250128-pci_fixup_addr-v9-4-3c4bb506f665@nxp.com/

- use help funtion dw_pcie_init_parent_bus_offset() because both EP and RC
use simular logic.
- still use use_parent_dt_ranges to detect fake bus translation for
no .cpu_addr_fixup()'s platfrom incase block exist platform.
---
 drivers/pci/controller/dwc/pcie-designware.c | 47 ++++++++++++++++++++++++++++
 drivers/pci/controller/dwc/pcie-designware.h | 13 ++++++++
 2 files changed, 60 insertions(+)

Comments

Bjorn Helgaas March 12, 2025, 10:16 p.m. UTC | #1
On Mon, Mar 10, 2025 at 04:16:42PM -0400, Frank Li wrote:
> From: Bjorn Helgaas <bhelgaas@google.com>
> 
> Set pci->parent_bus_offset based on the parent bus address from the
> "config" (Root complex mode) and "addr_space" (Endpoint mode).
> 
> .cpu_addr_fixup(cpu_phy_addr). (if implemented) should return the parent
> bus address corresponding according to cpu_phy_addr.
> 
> Sets pp->parent_bus_offset, but doesn't use it, so no functional change
> intended yet.
> 
> Add use_parent_dt_ranges to detect some fake bus translation at platform,
> which have not .cpu_addr_fixup(). Set use_parent_dt_ranges true explicitly
> at platform that have .cpu_addr_fixup() and fixed DTB's range. If not one
> report "fake bus translation" for sometime, this flags can be removed
> safely.

> +int dw_pcie_init_parent_bus_offset(struct dw_pcie *pci, const char *reg_name,
> +				   resource_size_t cpu_phy_addr)
> +{
> +	struct device *dev = pci->dev;
> +	struct device_node *np = dev->of_node;
> +	u64 (*fixup)(struct dw_pcie *pcie, u64 cpu_addr);
> +	u64 reg_addr, fixup_addr;
> +	int index;
> +
> +	/* Look up reg_name address on parent bus */
> +	index = of_property_match_string(np, "reg-names", reg_name);
> +
> +	if (index < 0) {
> +		dev_err(dev, "Missed reg-name: %s, Broken DTB file\n", reg_name);
> +		return -EINVAL;
> +	}
> +
> +	of_property_read_reg(np, index, &reg_addr, NULL);
> +
> +	fixup = pci->ops->cpu_addr_fixup;
> +	if (fixup) {
> +		fixup_addr = fixup(pci, cpu_phy_addr);
> +		if (reg_addr == fixup_addr) {
> +			dev_info(dev, "%#010llx %s reg[%d] == %#010llx; %ps is redundant\n",
> +				 cpu_phy_addr, reg_name, index,
> +				 fixup_addr, fixup);
> +		} else {
> +			dev_warn_once(dev, "%#010llx %s reg[%d] != %#010llx fixed up addr; DT is broken\n",
> +				      cpu_phy_addr, reg_name,
> +				      index, fixup_addr);
> +			reg_addr = fixup_addr;
> +		}
> +	} else if (!pci->use_parent_dt_ranges) {
> +		if (reg_addr != cpu_phy_addr) {
> +			dev_warn_once(dev, "Your DTB try to use fake translation, Please check parent's ranges property,");
> +			return 0;

IIUC the point of this is to catch a DT that describes a non-zero
offset when the driver did not implement .cpu_addr_fixup() and
therefore assumed "reg_addr == cpu_phy_addr".

I guess that makes sense.  But I think we should include both
addresses in the message to help understand the issue.

> +		}
> +	}
> +
> +	pci->parent_bus_offset = cpu_phy_addr - reg_addr;
> +	dev_info(dev, "%s parent bus offset is %#010llx\n",
> +		 reg_name, pci->parent_bus_offset);
> +
> +	return 0;
> +}

> +++ b/drivers/pci/controller/dwc/pcie-designware.h
> @@ -465,6 +466,16 @@ struct dw_pcie {
>  	struct reset_control_bulk_data	core_rsts[DW_PCIE_NUM_CORE_RSTS];
>  	struct gpio_desc		*pe_rst;
>  	bool			suspended;
> +	/*
> +	 * This flag indicates that the vendor driver uses devicetree 'ranges'
> +	 * property to allow iATU to use the Intermediate Address (IA) for
> +	 * outbound mapping. Using this flag also avoids the usage of
> +	 * 'cpu_addr_fixup' callback implementation in the driver.

This part of the comment is now wrong.

> +	 * If use_parent_dt_ranges is false, warning will print if IA is not
> +	 * equal to cpu physical address. Indicate dtb use a fake translation.
> +	 */
> +	bool			use_parent_dt_ranges;
>  };
diff mbox series

Patch

diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 9d0a5f75effcc..70b4d3369158a 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -16,6 +16,7 @@ 
 #include <linux/gpio/consumer.h>
 #include <linux/ioport.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/platform_device.h>
 #include <linux/sizes.h>
 #include <linux/types.h>
@@ -1105,3 +1106,49 @@  void dw_pcie_setup(struct dw_pcie *pci)
 
 	dw_pcie_link_set_max_link_width(pci, pci->num_lanes);
 }
+
+int dw_pcie_init_parent_bus_offset(struct dw_pcie *pci, const char *reg_name,
+				   resource_size_t cpu_phy_addr)
+{
+	struct device *dev = pci->dev;
+	struct device_node *np = dev->of_node;
+	u64 (*fixup)(struct dw_pcie *pcie, u64 cpu_addr);
+	u64 reg_addr, fixup_addr;
+	int index;
+
+	/* Look up reg_name address on parent bus */
+	index = of_property_match_string(np, "reg-names", reg_name);
+
+	if (index < 0) {
+		dev_err(dev, "Missed reg-name: %s, Broken DTB file\n", reg_name);
+		return -EINVAL;
+	}
+
+	of_property_read_reg(np, index, &reg_addr, NULL);
+
+	fixup = pci->ops->cpu_addr_fixup;
+	if (fixup) {
+		fixup_addr = fixup(pci, cpu_phy_addr);
+		if (reg_addr == fixup_addr) {
+			dev_info(dev, "%#010llx %s reg[%d] == %#010llx; %ps is redundant\n",
+				 cpu_phy_addr, reg_name, index,
+				 fixup_addr, fixup);
+		} else {
+			dev_warn_once(dev, "%#010llx %s reg[%d] != %#010llx fixed up addr; DT is broken\n",
+				      cpu_phy_addr, reg_name,
+				      index, fixup_addr);
+			reg_addr = fixup_addr;
+		}
+	} else if (!pci->use_parent_dt_ranges) {
+		if (reg_addr != cpu_phy_addr) {
+			dev_warn_once(dev, "Your DTB try to use fake translation, Please check parent's ranges property,");
+			return 0;
+		}
+	}
+
+	pci->parent_bus_offset = cpu_phy_addr - reg_addr;
+	dev_info(dev, "%s parent bus offset is %#010llx\n",
+		 reg_name, pci->parent_bus_offset);
+
+	return 0;
+}
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index d0d8c622a6e8b..5f941eab57110 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -445,6 +445,7 @@  struct dw_pcie {
 	void __iomem		*atu_base;
 	resource_size_t		atu_phys_addr;
 	size_t			atu_size;
+	resource_size_t		parent_bus_offset;
 	u32			num_ib_windows;
 	u32			num_ob_windows;
 	u32			region_align;
@@ -465,6 +466,16 @@  struct dw_pcie {
 	struct reset_control_bulk_data	core_rsts[DW_PCIE_NUM_CORE_RSTS];
 	struct gpio_desc		*pe_rst;
 	bool			suspended;
+	/*
+	 * This flag indicates that the vendor driver uses devicetree 'ranges'
+	 * property to allow iATU to use the Intermediate Address (IA) for
+	 * outbound mapping. Using this flag also avoids the usage of
+	 * 'cpu_addr_fixup' callback implementation in the driver.
+	 *
+	 * If use_parent_dt_ranges is false, warning will print if IA is not
+	 * equal to cpu physical address. Indicate dtb use a fake translation.
+	 */
+	bool			use_parent_dt_ranges;
 };
 
 #define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp)
@@ -500,6 +511,8 @@  void dw_pcie_setup(struct dw_pcie *pci);
 void dw_pcie_iatu_detect(struct dw_pcie *pci);
 int dw_pcie_edma_detect(struct dw_pcie *pci);
 void dw_pcie_edma_remove(struct dw_pcie *pci);
+int dw_pcie_init_parent_bus_offset(struct dw_pcie *pci, const char *reg_name,
+				   resource_size_t cpu_phy_addr);
 
 static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)
 {