diff options
author | Russell King (Oracle) <rmk+kernel@armlinux.org.uk> | 2021-12-30 13:49:12 +0000 |
---|---|---|
committer | Russell King (Oracle) <rmk+kernel@armlinux.org.uk> | 2025-04-04 15:00:14 +0100 |
commit | 7f7488c30e4ef1f146680aeec8d230e0e2521916 (patch) | |
tree | dedcd840f2c9af771a404d20d03e58be3e009212 | |
parent | 1caa830a6ef3924bb8bcbae9c76ba4befce27832 (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.c | 90 |
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 d1f3d327ddd1..e8a6fd21aa89 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -66,6 +66,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) @@ -1034,6 +1040,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_domain_for_dev(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, GFP_KERNEL); + 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, GFP_KERNEL); + 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 @@ -1085,6 +1172,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 |