summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2021-12-30 13:49:12 +0000
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2022-01-18 10:19:14 +0000
commit9d9cfaf6528a80634c1aa12d94112e3adb05bdbb (patch)
treed63fe0fc5cb047882abf08d9e09650d6fd4991b0
parente6679ee56147224097e58b690c76d5aec4203e7f (diff)
bus: fsl-mc: add IOMMU mappings for MC firmware and DCFG
The MC firmware (which runs the networking subsystem) is loaded into RAM by U-Boot, and this region is omitted from the memory passed to the kernel via DT. Prior to booting the kernel, the MC processing is halted to allow IOMMU setup. When booting the kernel with IOMMU support enabled and without using both bypass and passthrough mode, the MC firmware crashes as soon as it is released, as the MC is unable to access the RAM that has been assigned to it for both the firmware image and other purposes, and also the DCFG to retrieve the SoC version. In order to avoid this, we need to setup identity mappings in the MC domain. For the MC RAM region, we read the firmware base address registers which tell us where the firmware is located. According to the MC design document, the firmware is loaded within the upper 512M of the MC RAM region, aligned to 512M, and the RAM region is also aligned to 512M. The lower 8 bits of the firmware base address low register tells us how large the RAM region is. Use this to calculate its size and location in order to create an indentity mapping. We also search DT for the DCFG node to retrieve its address, and create a read-only identity mapping to allow the MC firmware to read the SoC version. If we are unable to find the DCFG node, we use a default address for this. [XXX This needs to be improved XXX] This allows "arm-smmu.disable_bypass=1" to be dropped from the kernel command line for LX2160A platforms. Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-bus.c90
1 files changed, 90 insertions, 0 deletions
diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c
index 8fd4a356a86e..b968b85f5ff3 100644
--- a/drivers/bus/fsl-mc/fsl-mc-bus.c
+++ b/drivers/bus/fsl-mc/fsl-mc-bus.c
@@ -65,6 +65,12 @@ struct fsl_mc_addr_translation_range {
#define GCR1_P1_STOP BIT(31)
#define GCR1_P2_STOP BIT(30)
+#define FSL_MC_FBALR 0x20
+#define MCFBAR_LOW 0xe0000000
+#define MCFBAR_MEMSZ 0x000000ff
+#define FSL_MC_FBAHR 0x24
+#define MCFBAR_HIGH 0x0000ffff
+
#define FSL_MC_FAPR 0x28
#define MC_FAPR_PL BIT(18)
#define MC_FAPR_BMT BIT(17)
@@ -1100,6 +1106,87 @@ static int get_mc_addr_translation_ranges(struct device *dev,
return 0;
}
+static void fsl_mc_setup_iommu(struct device *dev, struct fsl_mc *mc)
+{
+ struct iommu_domain *domain = iommu_get_dma_domain(dev);
+ struct resource dcfg_res = DEFINE_RES_MEM(0x01e00000, 0x10000);
+ struct device_node *np;
+ u64 firmware_base;
+ u64 mc_ram_base;
+ u32 mc_ram_size;
+ u32 mcfbalr;
+ u32 mcfbahr;
+ int ret;
+
+ if (!domain) {
+ dev_err(dev, "Missing IOMMU domain - MC firmware will crash\n");
+ return;
+ }
+
+ mcfbalr = readl(mc->fsl_mc_regs + FSL_MC_FBALR);
+ mcfbahr = readl(mc->fsl_mc_regs + FSL_MC_FBAHR);
+
+ firmware_base = mcfbalr & MCFBAR_LOW;
+ firmware_base |= (u64)(mcfbahr & MCFBAR_HIGH) << 32;
+
+ /* Calculate the size of MC RAM. */
+ mc_ram_size = mcfbalr & MCFBAR_MEMSZ;
+ if (mc_ram_size != 255)
+ mc_ram_size = (mc_ram_size + 1) * SZ_256M;
+ else
+ mc_ram_size = SZ_128M;
+
+ /*
+ * Calculate base address of MC RAM. U-Boot says:
+ * "As per MC design document, MC initial base address should be least
+ * significant 512MB address of MC private memory, i.e. address should
+ * point to end address masked with 512MB offset in private DRAM block."
+ * and uses the following calculation:
+ *
+ * (gd->arch.resv_ram + mc_ram_size - 1) &
+ * MC_RAM_BASE_ADDR_ALIGNMENT_MASK
+ *
+ * where gd->arch.resv_ram is the start of the MC reserved RAM block,
+ * and is itself aligned to 512MB.
+ *
+ * Hence, if the reserved RAM starts at 0x2780000000 and is 0x70000000
+ * in size, then the firmware address will be 0x27e0000000. However,
+ * if it is 512M, then the reserved RAM and the firmware base addresses
+ * will be identical.
+ */
+ mc_ram_base = ALIGN(firmware_base - mc_ram_size + 1, SZ_512M);
+
+ /*
+ * Give MC firmware access to the MC RAM, which includes the MC
+ * firmware image itself.
+ */
+ ret = iommu_map(domain, mc_ram_base, mc_ram_base, mc_ram_size,
+ IOMMU_CACHE | IOMMU_WRITE | IOMMU_READ);
+ if (ret)
+ dev_err(dev, "Failed to setup IOMMU mapping for MC RAM: %pe\n",
+ ERR_PTR(ret));
+
+ /* Give firmware access to the DCFG so it can read the SVR register */
+ np = of_find_compatible_node(NULL, NULL, "fsl,lx2160a-dcfg");
+ if (np) {
+ ret = of_address_to_resource(np, 0, &dcfg_res);
+ if (ret) {
+ dev_err(dev, "Failed to get dcfg resource: %pe\n",
+ ERR_PTR(ret));
+ return;
+ }
+ } else {
+ dev_warn(dev,
+ "Failed to find dcfg node - using default addresses\n");
+ }
+
+ ret = iommu_map(domain, dcfg_res.start, dcfg_res.start,
+ resource_size(&dcfg_res), IOMMU_READ);
+ if (ret)
+ dev_err(dev, "Failed to setup IOMMU mapping for DCFG: %pe\n",
+ ERR_PTR(ret));
+}
+
/*
* fsl_mc_bus_probe - callback invoked when the root MC bus is being
* added
@@ -1151,6 +1238,9 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
error);
}
+ if (dev_of_node(&pdev->dev))
+ fsl_mc_setup_iommu(&pdev->dev, mc);
+
/*
* Some bootloaders pause the MC firmware before booting the
* kernel so that MC will not cause faults as soon as the