summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurentiu Tudor <laurentiu.tudor@nxp.com>2019-10-01 16:22:48 +0300
committerRussell King <rmk+kernel@armlinux.org.uk>2021-02-15 14:01:56 +0000
commitf60a08429a40b8fd12d886b5eb742d0254170b54 (patch)
treec67fdba5f940de5de127319056544bbb49d98602
parentcefd1e9c2018929bbd2dcccdf09a8f020ec1a091 (diff)
bus: fsl-mc: make mc work with smmu disable bypass on
Since this commit [1] booting kernel on MC based chips started to fail because this firmware starts before the kernel and as soon as SMMU is probed it starts to trigger contiguous faults. This is a workaround that allows MC firmware to run with SMMU in disable bypass mode. It consists of the following steps: 1. pause the firmware at early boot to get a chance to setup SMMU 2. request direct mapping for MC device 3. resume the firmware The workaround relies on the fact that no state is lost when pausing / resuming firmware, as per the docs. With this patch, platforms with MC firmware can now boot without having to change the default config to set: CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT=n [1] 954a03be033 ("iommu/arm-smmu: Break insecure users by disabling bypass by default") Signed-off-by: Laurentiu Tudor <laurentiu.tudor@nxp.com>
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-bus.c52
1 files changed, 52 insertions, 0 deletions
diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c
index b8e6acdf932e..9c08b82ecc3e 100644
--- a/drivers/bus/fsl-mc/fsl-mc-bus.c
+++ b/drivers/bus/fsl-mc/fsl-mc-bus.c
@@ -21,6 +21,7 @@
#include <linux/dma-mapping.h>
#include <linux/acpi.h>
#include <linux/iommu.h>
+#include <linux/io.h>
#include "fsl-mc-private.h"
@@ -947,6 +948,12 @@ static int get_mc_addr_translation_ranges(struct device *dev,
return 0;
}
+#define FSL_MC_GCR1 0x0
+#define GCR1_P1_STOP BIT(31)
+
+static u32 boot_gcr1;
+static void __iomem *fsl_mc_regs;
+
/**
* fsl_mc_bus_probe - callback invoked when the root MC bus is being
* added
@@ -963,6 +970,21 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
u32 mc_portal_size, mc_stream_id;
struct resource *plat_res;
+ /*
+ * The MC firmware requires full access to the whole address space plus
+ * it has no way of dealing with any kind of address translation, so
+ * request direct mapping for it.
+ */
+ error = iommu_request_dm_for_dev(&pdev->dev);
+ if (error)
+ dev_warn(&pdev->dev, "iommu_request_dm_for_dev() failed: %d\n",
+ error);
+
+ if (fsl_mc_regs) {
+ /* Resume the firmware */
+ writel(boot_gcr1 & ~GCR1_P1_STOP, fsl_mc_regs + FSL_MC_GCR1);
+ }
+
mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
if (!mc)
return -ENOMEM;
@@ -1082,6 +1104,13 @@ static int fsl_mc_bus_remove(struct platform_device *pdev)
if (!fsl_mc_is_root_dprc(&mc->root_mc_bus_dev->dev))
return -EINVAL;
+ /*
+ * Pause back the firmware so that it doesn't trigger faults by the
+ * time SMMU gets brought down.
+ */
+ writel(boot_gcr1 | GCR1_P1_STOP, fsl_mc_regs + FSL_MC_GCR1);
+ iounmap(fsl_mc_regs);
+
fsl_mc_device_remove(mc->root_mc_bus_dev);
fsl_destroy_mc_io(mc->root_mc_bus_dev->mc_io);
@@ -1117,6 +1146,8 @@ static struct platform_driver fsl_mc_bus_driver = {
static int __init fsl_mc_bus_driver_init(void)
{
int error;
+ struct device_node *np;
+ struct resource res;
error = bus_register(&fsl_mc_bus_type);
if (error < 0) {
@@ -1138,9 +1169,30 @@ static int __init fsl_mc_bus_driver_init(void)
if (error < 0)
goto error_cleanup_dprc_driver;
+ np = of_find_matching_node(NULL, fsl_mc_bus_match_table);
+ if (np && of_device_is_available(np)) {
+ error = of_address_to_resource(np, 1, &res);
+ if (error)
+ goto error_cleanup_dprc_driver;
+ fsl_mc_regs = ioremap(res.start, resource_size(&res));
+ if (!fsl_mc_regs) {
+ error = -ENXIO;
+ goto error_cleanup_dprc_driver;
+ }
+
+ boot_gcr1 = readl(fsl_mc_regs + FSL_MC_GCR1);
+ /*
+ * If found running, pause MC firmware in order to get a chance
+ * to setup SMMU for it.
+ */
+ if (!(boot_gcr1 & GCR1_P1_STOP))
+ writel(boot_gcr1 | GCR1_P1_STOP, fsl_mc_regs + FSL_MC_GCR1);
+ }
+
return 0;
error_cleanup_dprc_driver:
+ iounmap(fsl_mc_regs);
dprc_driver_exit();
error_cleanup_driver: