diff options
Diffstat (limited to 'drivers/bus/fsl-mc/fsl-mc-bus.c')
-rw-r--r-- | drivers/bus/fsl-mc/fsl-mc-bus.c | 52 |
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: |