summaryrefslogtreecommitdiff
path: root/drivers/bus/fsl-mc/fsl-mc-bus.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bus/fsl-mc/fsl-mc-bus.c')
-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: