From: <daire.mcnamara@microchip.com>
To: <conor.dooley@microchip.com>, <robh+dt@kernel.org>,
<krzysztof.kozlowski+dt@linaro.org>, <paul.walmsley@sifive.com>,
<palmer@dabbelt.com>, <aou@eecs.berkeley.edu>,
<lpieralisi@kernel.org>, <kw@linux.com>, <bhelgaas@google.com>,
<linux-riscv@lists.infradead.org>, <devicetree@vger.kernel.org>,
<linux-pci@vger.kernel.org>
Cc: Daire McNamara <daire.mcnamara@microchip.com>
Subject: [PATCH v2 8/9] PCI: microchip: Partition inbound address translation
Date: Wed, 21 Dec 2022 16:26:29 +0000 [thread overview]
Message-ID: <20221221162630.3632486-9-daire.mcnamara@microchip.com> (raw)
In-Reply-To: <20221221162630.3632486-1-daire.mcnamara@microchip.com>
From: Daire McNamara <daire.mcnamara@microchip.com>
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.
Signed-off-by: Daire McNamara <daire.mcnamara@microchip.com>
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
---
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 ee8796db461e..ea95a482e506 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(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 {
@@ -942,6 +956,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)
@@ -973,11 +1024,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,
@@ -1157,6 +1203,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(63, 0);
+ u64 end_axi = 0;
+ u64 start_pci = GENMASK(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;
@@ -1174,6 +1330,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,
@@ -1187,6 +1348,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
next prev parent reply other threads:[~2022-12-21 16:27 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-12-21 16:26 [PATCH v2 0/9] PCI: microchip: Partition address translations daire.mcnamara
2022-12-21 16:26 ` [PATCH v2 1/9] PCI: microchip: Correct the DED and SEC interrupt bit offsets daire.mcnamara
2022-12-21 16:26 ` [PATCH v2 2/9] PCI: microchip: Align register, offset, and mask names with hw docs daire.mcnamara
2022-12-21 16:26 ` [PATCH v2 3/9] PCI: microchip: Enable event handlers to access bridge and ctrl ptrs daire.mcnamara
2022-12-21 16:26 ` [PATCH v2 4/9] PCI: microchip: Clean up initialisation of interrupts daire.mcnamara
2022-12-21 16:26 ` [PATCH v2 5/9] PCI: microchip: Gather MSI information from hardware config registers daire.mcnamara
2022-12-21 16:26 ` [PATCH v2 6/9] PCI: microchip: Re-partition code between probe() and init() daire.mcnamara
2022-12-21 16:26 ` [PATCH v2 7/9] PCI: microchip: Partition outbound address translation daire.mcnamara
2022-12-21 16:26 ` daire.mcnamara [this message]
2022-12-22 5:30 ` [PATCH v2 8/9] PCI: microchip: Partition inbound " kernel test robot
2022-12-30 19:52 ` Bjorn Helgaas
2022-12-21 16:26 ` [PATCH v2 9/9] riscv: dts: microchip: add parent ranges and dma-ranges for IKRD v2022.09 daire.mcnamara
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=20221221162630.3632486-9-daire.mcnamara@microchip.com \
--to=daire.mcnamara@microchip.com \
--cc=aou@eecs.berkeley.edu \
--cc=bhelgaas@google.com \
--cc=conor.dooley@microchip.com \
--cc=devicetree@vger.kernel.org \
--cc=krzysztof.kozlowski+dt@linaro.org \
--cc=kw@linux.com \
--cc=linux-pci@vger.kernel.org \
--cc=linux-riscv@lists.infradead.org \
--cc=lpieralisi@kernel.org \
--cc=palmer@dabbelt.com \
--cc=paul.walmsley@sifive.com \
--cc=robh+dt@kernel.org \
/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