Re: [PATCH v3 10/11] PCI: microchip: Partition inbound address translation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 2023-02-02 11:31, Lorenzo Pieralisi wrote:
[+CC Robin]

On Wed, Jan 11, 2023 at 12:53:22PM +0000, daire.mcnamara@xxxxxxxxxxxxx wrote:
From: Daire McNamara <daire.mcnamara@xxxxxxxxxxxxx>

On Microchip PolarFire SoC the PCIe Root Port is behind a set of Fabric
Interface Controller (FIC) buses that encapsulate buses like ABP/AHP,
AXI-S, and AXI-M. Depending on which FIC(s) the Root Port is wired
through to cpu space, the Root Port driver needs to take account of the
address translation done by a parent (e.g. fabric) node before setting
up its own inbound address translation tables from attached devices.

Parse the dma-range properties to determine how much address translation
to perform in the Root Port and how much is being provided by the
fabric.

Same reasoning as per patch (9), it requires some review from DT/DMA
maintainers.

From a quick look I'd imagine it's notionally correct. The definitions smell very much of it being a PLDA IP similar to the one we used in the Arm Juno SoC. If the kernel driver is in control of the whole thing and can't rely on it having previously been configured by firmware, then indeed it likely should need to configure outbound translation entries based on "ranges" and inbound ones based on "dma-ranges", much like dw_pcie_iatu_setup() for DWC drivers.

However, all the explicit parsing here looks a bit suspect - much like the DWC example again, the relevant information should already be provided in the bridge->windows and bridge->dma_ranges lists. If that doesn't work then it implies that either the DT is wrong, or devm_of_pci_get_host_bridge_resources() needs some work to avoid conflating the host bridge's own translations with any additional translations upstream.

Thanks,
Robin.


Lorenzo


Signed-off-by: Daire McNamara <daire.mcnamara@xxxxxxxxxxxxx>
Reviewed-by: Conor Dooley <conor.dooley@xxxxxxxxxxxxx>
---
  drivers/pci/controller/pcie-microchip-host.c | 178 ++++++++++++++++++-
  1 file changed, 172 insertions(+), 6 deletions(-)

diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c
index f3dfcdf39c8a..2eb70fd01879 100644
--- a/drivers/pci/controller/pcie-microchip-host.c
+++ b/drivers/pci/controller/pcie-microchip-host.c
@@ -22,6 +22,9 @@
  /* Number of MSI IRQs */
  #define MC_MAX_NUM_MSI_IRQS			32
+#define MC_MAX_NUM_INBOUND_WINDOWS 8
+#define MC_ATT_MASK				GENMASK_ULL(63, 31)
+
  /* PCIe Bridge Phy and Controller Phy offsets */
  #define MC_PCIE1_BRIDGE_ADDR			0x00008000u
  #define MC_PCIE1_CTRL_ADDR			0x0000a000u
@@ -86,10 +89,13 @@
  #define ISTATUS_MSI				0x194
#define ATR_WINDOW_DESC_SIZE 32
-#define ATR_PCIE_ATR_SIZE			0x25
  #define ATR_SIZE_SHIFT				1
  #define ATR_IMPL_ENABLE				1
+#define ATR_PCIE_WIN0_SRCADDR 0x80000000
+#define ATR_PCIE_ATR_SIZE			(512 * 1024 * 1024ul)
+#define ATR_PCIE_NUM_WINDOWS			8
+
  /* PCIe Master table init defines */
  #define ATR0_PCIE_WIN0_SRCADDR_PARAM		0x600u
  #define ATR0_PCIE_WIN0_SRC_ADDR			0x604u
@@ -278,6 +284,12 @@ struct mc_msi {
  	DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS);
  };
+struct inbound_windows {
+	u64 axi_addr;
+	u64 pci_addr;
+	u64 size;
+};
+
  struct mc_pcie {
  	void __iomem *axi_base_addr;
  	struct device *dev;
@@ -286,6 +298,8 @@ struct mc_pcie {
  	raw_spinlock_t lock;
  	struct mc_msi msi;
  	u64 outbound_range_offset;
+	u32 num_inbound_windows;
+	struct inbound_windows inbound_windows[MC_MAX_NUM_INBOUND_WINDOWS];
  };
struct cause {
@@ -948,6 +962,43 @@ static int mc_pcie_init_irq_domains(struct mc_pcie *port)
  	return mc_allocate_msi_domains(port);
  }
+static int mc_pcie_setup_inbound_ranges(struct platform_device *pdev, struct mc_pcie *port)
+{
+	void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
+	phys_addr_t pcie_addr;
+	phys_addr_t axi_addr;
+	u32 atr_size;
+	u32 val;
+	int i;
+
+	for (i = 0; i < port->num_inbound_windows; i++) {
+		atr_size = ilog2(port->inbound_windows[i].size) - 1;
+		atr_size &= GENMASK(5, 0);
+
+		pcie_addr = port->inbound_windows[i].pci_addr;
+
+		val = lower_32_bits(pcie_addr) & GENMASK(31, 12);
+		val |= (atr_size << ATR_SIZE_SHIFT);
+		val |= ATR_IMPL_ENABLE;
+		writel(val, bridge_base_addr +
+		       ATR0_PCIE_WIN0_SRCADDR_PARAM + (i * ATR_WINDOW_DESC_SIZE));
+		writel(upper_32_bits(pcie_addr), bridge_base_addr +
+		       ATR0_PCIE_WIN0_SRC_ADDR + (i * ATR_WINDOW_DESC_SIZE));
+
+		axi_addr = port->inbound_windows[i].axi_addr;
+
+		writel(lower_32_bits(axi_addr), bridge_base_addr +
+		       ATR0_PCIE_WIN0_TRSL_ADDR_LSB + (i * ATR_WINDOW_DESC_SIZE));
+		writel(upper_32_bits(axi_addr), bridge_base_addr +
+		       ATR0_PCIE_WIN0_TRSL_ADDR_UDW + (i * ATR_WINDOW_DESC_SIZE));
+
+		writel(TRSL_ID_AXI4_MASTER_0, bridge_base_addr +
+		       ATR0_PCIE_WIN0_TRSL_PARAM + (i * ATR_WINDOW_DESC_SIZE));
+	}
+
+	return 0;
+}
+
  static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
  				 phys_addr_t axi_addr, phys_addr_t pci_addr,
  				 size_t size)
@@ -979,11 +1030,6 @@ static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
  	val = upper_32_bits(pci_addr);
  	writel(val, bridge_base_addr + (index * ATR_WINDOW_DESC_SIZE) +
  	       ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
-
-	val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
-	val |= (ATR_PCIE_ATR_SIZE << ATR_SIZE_SHIFT);
-	writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
-	writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
  }
static int mc_pcie_setup_windows(struct platform_device *pdev,
@@ -1163,6 +1209,116 @@ static int mc_check_for_parent_range_handling(struct platform_device *pdev, stru
  	return 0;
  }
+static int mc_check_for_parent_dma_range_handling(struct platform_device *pdev,
+						  struct mc_pcie *port)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *dn = dev->of_node;
+	struct of_range_parser parser;
+	struct of_range range;
+	int num_parent_ranges = 0;
+	int num_ranges = 0;
+	struct inbound_windows ranges[MC_MAX_NUM_INBOUND_WINDOWS] = { 0 };
+	u64 start_axi = GENMASK_ULL(63, 0);
+	u64 end_axi = 0;
+	u64 start_pci = GENMASK_ULL(63, 0);
+	s64 size;
+	u64 window_size;
+	int i;
+
+	/* Find all dma-ranges */
+	if (of_pci_dma_range_parser_init(&parser, dn)) {
+		dev_err(dev, "missing dma-ranges property\n");
+		return -EINVAL;
+	}
+
+	for_each_of_range(&parser, &range) {
+		if (num_ranges > MC_MAX_NUM_INBOUND_WINDOWS) {
+			dev_err(dev, "too many inbound ranges; %d available tables\n",
+				MC_MAX_NUM_INBOUND_WINDOWS);
+			return -EINVAL;
+		}
+		ranges[num_ranges].axi_addr = range.cpu_addr;
+		ranges[num_ranges].pci_addr = range.pci_addr;
+		ranges[num_ranges].size = range.size;
+
+		num_ranges++;
+	}
+
+	/*
+	 * Check for one level up; will need to adjust address translation
+	 * tables for these
+	 */
+	dn = of_get_parent(dn);
+	if (dn) {
+		of_pci_dma_range_parser_init(&parser, dn);
+
+		for_each_of_range(&parser, &range) {
+			if (num_parent_ranges > MC_MAX_NUM_INBOUND_WINDOWS) {
+				dev_err(dev, "too many parent inbound ranges; %d available tables\n",
+					MC_MAX_NUM_INBOUND_WINDOWS);
+				return -EINVAL;
+			}
+			ranges[num_parent_ranges].axi_addr = range.pci_addr;
+			num_parent_ranges++;
+		}
+	}
+
+	if (num_parent_ranges) {
+		if (num_ranges != num_parent_ranges) {
+			dev_err(dev, "num parent inbound ranges must be 0 or match num inbound ranges\n");
+			return -EINVAL;
+		}
+	}
+
+	/* Merge ranges */
+	for (i = 0; i < num_ranges; i++) {
+		struct inbound_windows *range = &ranges[i];
+
+		if (range->axi_addr < start_axi) {
+			start_axi = range->axi_addr;
+			start_pci = range->pci_addr;
+		}
+
+		if (range->axi_addr + range->size > end_axi)
+			end_axi = range->axi_addr + range->size;
+	}
+
+	/* Move starts back as far as possible */
+	start_axi &= MC_ATT_MASK;
+	start_pci &= MC_ATT_MASK;
+
+	/* Adjust size to take account of that change */
+	size = end_axi - start_axi;
+
+	/* May need to adjust size up to the next largest power of 2 */
+	if (size < 1ull << ilog2(size))
+		size = 1ull << (ilog2(size) + 1);
+
+	window_size = 1ull << (ilog2(size) - 1);
+
+	/* Divide merged range into windows */
+	i = 0;
+	while (size > 0 && i < MC_MAX_NUM_INBOUND_WINDOWS) {
+		port->inbound_windows[i].axi_addr = start_axi;
+		port->inbound_windows[i].pci_addr = start_pci;
+		port->inbound_windows[i].size = window_size;
+
+		size -= window_size;
+		start_axi += window_size;
+		start_pci += window_size;
+		i++;
+		port->num_inbound_windows = i;
+	}
+
+	if (size < 0) {
+		dev_err(dev, "insufficient windows to map inbound ranges\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
  static int mc_platform_init(struct pci_config_window *cfg)
  {
  	struct device *dev = cfg->parent;
@@ -1180,6 +1336,11 @@ static int mc_platform_init(struct pci_config_window *cfg)
  	if (ret)
  		return ret;
+ /* And similarly, check for inbound address translation */
+	ret = mc_check_for_parent_dma_range_handling(pdev, port);
+	if (ret)
+		return ret;
+
  	/* Configure address translation table 0 for PCIe config space */
  	mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start - port->outbound_range_offset,
  			     cfg->res.start - port->outbound_range_offset,
@@ -1193,6 +1354,11 @@ static int mc_platform_init(struct pci_config_window *cfg)
  	if (ret)
  		return ret;
+ /* Configure inbound translation tables */
+	ret = mc_pcie_setup_inbound_ranges(pdev, port);
+	if (ret)
+		return ret;
+
  	/* Address translation is up; safe to enable interrupts */
  	ret = mc_init_interrupts(pdev, port);
  	if (ret)
--
2.25.1


_______________________________________________
linux-riscv mailing list
linux-riscv@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/linux-riscv



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux