diff options
Diffstat (limited to 'drivers/bus/fsl-mc/fsl-mc-bus.c')
| -rw-r--r-- | drivers/bus/fsl-mc/fsl-mc-bus.c | 357 |
1 files changed, 194 insertions, 163 deletions
diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index 09c8ab5e0959..25845c04e562 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -14,13 +14,14 @@ #include <linux/of_device.h> #include <linux/of_address.h> #include <linux/ioport.h> +#include <linux/platform_device.h> #include <linux/slab.h> #include <linux/limits.h> #include <linux/bitops.h> -#include <linux/msi.h> #include <linux/dma-mapping.h> #include <linux/acpi.h> #include <linux/iommu.h> +#include <linux/dma-map-ops.h> #include "fsl-mc-private.h" @@ -63,11 +64,14 @@ struct fsl_mc_addr_translation_range { #define FSL_MC_GCR1 0x0 #define GCR1_P1_STOP BIT(31) +#define GCR1_P2_STOP BIT(30) #define FSL_MC_FAPR 0x28 #define MC_FAPR_PL BIT(18) #define MC_FAPR_BMT BIT(17) +static phys_addr_t mc_portal_base_phys_addr; + /** * fsl_mc_bus_match - device to driver matching callback * @dev: the fsl-mc device to match against @@ -76,11 +80,11 @@ struct fsl_mc_addr_translation_range { * * Returns 1 on success, 0 otherwise. */ -static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv) +static int fsl_mc_bus_match(struct device *dev, const struct device_driver *drv) { const struct fsl_mc_device_id *id; struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); - struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv); + const struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv); bool found = false; /* When driver_override is set, only bind to the matching driver */ @@ -121,9 +125,9 @@ out: /* * fsl_mc_bus_uevent - callback invoked when a device is added */ -static int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) +static int fsl_mc_bus_uevent(const struct device *dev, struct kobj_uevent_env *env) { - struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + const struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); if (add_uevent_var(env, "MODALIAS=fsl-mc:v%08Xd%s", mc_dev->obj_desc.vendor, @@ -135,17 +139,36 @@ static int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) static int fsl_mc_dma_configure(struct device *dev) { + const struct device_driver *drv = READ_ONCE(dev->driver); struct device *dma_dev = dev; struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); u32 input_id = mc_dev->icid; + int ret; while (dev_is_fsl_mc(dma_dev)) dma_dev = dma_dev->parent; if (dev_of_node(dma_dev)) - return of_dma_configure_id(dev, dma_dev->of_node, 0, &input_id); + ret = of_dma_configure_id(dev, dma_dev->of_node, 0, &input_id); + else + ret = acpi_dma_configure_id(dev, DEV_DMA_COHERENT, &input_id); + + /* @drv may not be valid when we're called from the IOMMU layer */ + if (!ret && drv && !to_fsl_mc_driver(drv)->driver_managed_dma) { + ret = iommu_device_use_default_domain(dev); + if (ret) + arch_teardown_dma_ops(dev); + } + + return ret; +} + +static void fsl_mc_dma_cleanup(struct device *dev) +{ + struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); - return acpi_dma_configure_id(dev, DEV_DMA_COHERENT, &input_id); + if (!mc_drv->driver_managed_dma) + iommu_device_unuse_default_domain(dev); } static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, @@ -153,8 +176,8 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, { struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); - return sprintf(buf, "fsl-mc:v%08Xd%s\n", mc_dev->obj_desc.vendor, - mc_dev->obj_desc.type); + return sysfs_emit(buf, "fsl-mc:v%08Xd%s\n", mc_dev->obj_desc.vendor, + mc_dev->obj_desc.type); } static DEVICE_ATTR_RO(modalias); @@ -163,31 +186,14 @@ static ssize_t driver_override_store(struct device *dev, const char *buf, size_t count) { struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); - char *driver_override, *old = mc_dev->driver_override; - char *cp; + int ret; if (WARN_ON(dev->bus != &fsl_mc_bus_type)) return -EINVAL; - if (count >= (PAGE_SIZE - 1)) - return -EINVAL; - - driver_override = kstrndup(buf, count, GFP_KERNEL); - if (!driver_override) - return -ENOMEM; - - cp = strchr(driver_override, '\n'); - if (cp) - *cp = '\0'; - - if (strlen(driver_override)) { - mc_dev->driver_override = driver_override; - } else { - kfree(driver_override); - mc_dev->driver_override = NULL; - } - - kfree(old); + ret = driver_set_override(dev, &mc_dev->driver_override, buf, count); + if (ret) + return ret; return count; } @@ -197,7 +203,7 @@ static ssize_t driver_override_show(struct device *dev, { struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", mc_dev->driver_override); + return sysfs_emit(buf, "%s\n", mc_dev->driver_override); } static DEVICE_ATTR_RW(driver_override); @@ -220,14 +226,14 @@ static int scan_fsl_mc_bus(struct device *dev, void *data) root_mc_dev = to_fsl_mc_device(dev); root_mc_bus = to_fsl_mc_bus(root_mc_dev); mutex_lock(&root_mc_bus->scan_mutex); - dprc_scan_objects(root_mc_dev, NULL); + dprc_scan_objects(root_mc_dev, false); mutex_unlock(&root_mc_bus->scan_mutex); exit: return 0; } -static ssize_t rescan_store(struct bus_type *bus, +static ssize_t rescan_store(const struct bus_type *bus, const char *buf, size_t count) { unsigned long val; @@ -280,7 +286,7 @@ exit: return 0; } -static ssize_t autorescan_store(struct bus_type *bus, +static ssize_t autorescan_store(const struct bus_type *bus, const char *buf, size_t count) { bus_for_each_dev(bus, NULL, (void *)buf, fsl_mc_bus_set_autorescan); @@ -288,7 +294,7 @@ static ssize_t autorescan_store(struct bus_type *bus, return count; } -static ssize_t autorescan_show(struct bus_type *bus, char *buf) +static ssize_t autorescan_show(const struct bus_type *bus, char *buf) { bus_for_each_dev(bus, NULL, (void *)buf, fsl_mc_bus_get_autorescan); return strlen(buf); @@ -304,100 +310,101 @@ static struct attribute *fsl_mc_bus_attrs[] = { ATTRIBUTE_GROUPS(fsl_mc_bus); -struct bus_type fsl_mc_bus_type = { +const struct bus_type fsl_mc_bus_type = { .name = "fsl-mc", .match = fsl_mc_bus_match, .uevent = fsl_mc_bus_uevent, .dma_configure = fsl_mc_dma_configure, + .dma_cleanup = fsl_mc_dma_cleanup, .dev_groups = fsl_mc_dev_groups, .bus_groups = fsl_mc_bus_groups, }; EXPORT_SYMBOL_GPL(fsl_mc_bus_type); -struct device_type fsl_mc_bus_dprc_type = { +const struct device_type fsl_mc_bus_dprc_type = { .name = "fsl_mc_bus_dprc" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dprc_type); -struct device_type fsl_mc_bus_dpni_type = { +const struct device_type fsl_mc_bus_dpni_type = { .name = "fsl_mc_bus_dpni" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpni_type); -struct device_type fsl_mc_bus_dpio_type = { +const struct device_type fsl_mc_bus_dpio_type = { .name = "fsl_mc_bus_dpio" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpio_type); -struct device_type fsl_mc_bus_dpsw_type = { +const struct device_type fsl_mc_bus_dpsw_type = { .name = "fsl_mc_bus_dpsw" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpsw_type); -struct device_type fsl_mc_bus_dpbp_type = { +const struct device_type fsl_mc_bus_dpbp_type = { .name = "fsl_mc_bus_dpbp" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpbp_type); -struct device_type fsl_mc_bus_dpcon_type = { +const struct device_type fsl_mc_bus_dpcon_type = { .name = "fsl_mc_bus_dpcon" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpcon_type); -struct device_type fsl_mc_bus_dpmcp_type = { +const struct device_type fsl_mc_bus_dpmcp_type = { .name = "fsl_mc_bus_dpmcp" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpmcp_type); -struct device_type fsl_mc_bus_dpmac_type = { +const struct device_type fsl_mc_bus_dpmac_type = { .name = "fsl_mc_bus_dpmac" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpmac_type); -struct device_type fsl_mc_bus_dprtc_type = { +const struct device_type fsl_mc_bus_dprtc_type = { .name = "fsl_mc_bus_dprtc" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dprtc_type); -struct device_type fsl_mc_bus_dpseci_type = { +const struct device_type fsl_mc_bus_dpseci_type = { .name = "fsl_mc_bus_dpseci" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpseci_type); -struct device_type fsl_mc_bus_dpdmux_type = { +const struct device_type fsl_mc_bus_dpdmux_type = { .name = "fsl_mc_bus_dpdmux" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdmux_type); -struct device_type fsl_mc_bus_dpdcei_type = { +const struct device_type fsl_mc_bus_dpdcei_type = { .name = "fsl_mc_bus_dpdcei" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdcei_type); -struct device_type fsl_mc_bus_dpaiop_type = { +const struct device_type fsl_mc_bus_dpaiop_type = { .name = "fsl_mc_bus_dpaiop" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpaiop_type); -struct device_type fsl_mc_bus_dpci_type = { +const struct device_type fsl_mc_bus_dpci_type = { .name = "fsl_mc_bus_dpci" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpci_type); -struct device_type fsl_mc_bus_dpdmai_type = { +const struct device_type fsl_mc_bus_dpdmai_type = { .name = "fsl_mc_bus_dpdmai" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdmai_type); -struct device_type fsl_mc_bus_dpdbg_type = { +const struct device_type fsl_mc_bus_dpdbg_type = { .name = "fsl_mc_bus_dpdbg" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdbg_type); -static struct device_type *fsl_mc_get_device_type(const char *type) +static const struct device_type *fsl_mc_get_device_type(const char *type) { static const struct { - struct device_type *dev_type; + const struct device_type *dev_type; const char *type; } dev_types[] = { { &fsl_mc_bus_dprc_type, "dprc" }, @@ -449,13 +456,8 @@ static int fsl_mc_driver_remove(struct device *dev) { struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); - int error; - error = mc_drv->remove(mc_dev); - if (error < 0) { - dev_err(dev, "%s failed: %d\n", __func__, error); - return error; - } + mc_drv->remove(mc_dev); return 0; } @@ -703,14 +705,30 @@ static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev, * If base address is in the region_desc use it otherwise * revert to old mechanism */ - if (region_desc.base_address) + if (region_desc.base_address) { regions[i].start = region_desc.base_address + region_desc.base_offset; - else + } else { error = translate_mc_addr(mc_dev, mc_region_type, region_desc.base_offset, ®ions[i].start); + /* + * Some versions of the MC firmware wrongly report + * 0 for register base address of the DPMCP associated + * with child DPRC objects thus rendering them unusable. + * This is particularly troublesome in ACPI boot + * scenarios where the legacy way of extracting this + * base address from the device tree does not apply. + * Given that DPMCPs share the same base address, + * workaround this by using the base address extracted + * from the root DPRC container. + */ + if (is_fsl_mc_bus_dprc(mc_dev) && + regions[i].start == region_desc.base_offset) + regions[i].start += mc_portal_base_phys_addr; + } + if (error < 0) { dev_err(parent_dev, "Invalid MC offset: %#x (for %s.%d\'s region %d)\n", @@ -888,13 +906,17 @@ int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc, error_cleanup_dev: kfree(mc_dev->regions); - kfree(mc_bus); - kfree(mc_dev); + if (mc_bus) + kfree(mc_bus); + else + kfree(mc_dev); return error; } EXPORT_SYMBOL_GPL(fsl_mc_device_add); +static struct notifier_block fsl_mc_nb; + /** * fsl_mc_device_remove - Remove an fsl-mc device from being visible to * Linux @@ -914,17 +936,20 @@ void fsl_mc_device_remove(struct fsl_mc_device *mc_dev) } EXPORT_SYMBOL_GPL(fsl_mc_device_remove); -struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev) +struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev, + u16 if_id) { struct fsl_mc_device *mc_bus_dev, *endpoint; struct fsl_mc_obj_desc endpoint_desc = {{ 0 }}; struct dprc_endpoint endpoint1 = {{ 0 }}; struct dprc_endpoint endpoint2 = {{ 0 }}; + struct fsl_mc_bus *mc_bus; int state, err; mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); strcpy(endpoint1.type, mc_dev->obj_desc.type); endpoint1.id = mc_dev->obj_desc.id; + endpoint1.if_id = if_id; err = dprc_get_connection(mc_bus_dev->mc_io, 0, mc_bus_dev->mc_handle, @@ -942,89 +967,48 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev) strcpy(endpoint_desc.type, endpoint2.type); endpoint_desc.id = endpoint2.id; endpoint = fsl_mc_device_lookup(&endpoint_desc, mc_bus_dev); + if (endpoint) + return endpoint; /* * We know that the device has an endpoint because we verified by * interrogating the firmware. This is the case when the device was not * yet discovered by the fsl-mc bus, thus the lookup returned NULL. - * Differentiate this case by returning EPROBE_DEFER. + * Force a rescan of the devices in this container and retry the lookup. + */ + mc_bus = to_fsl_mc_bus(mc_bus_dev); + if (mutex_trylock(&mc_bus->scan_mutex)) { + err = dprc_scan_objects(mc_bus_dev, true); + mutex_unlock(&mc_bus->scan_mutex); + } + if (err < 0) + return ERR_PTR(err); + + endpoint = fsl_mc_device_lookup(&endpoint_desc, mc_bus_dev); + /* + * This means that the endpoint might reside in a different isolation + * context (DPRC/container). Not much to do, so return a permssion + * error. */ if (!endpoint) - return ERR_PTR(-EPROBE_DEFER); + return ERR_PTR(-EPERM); return endpoint; } EXPORT_SYMBOL_GPL(fsl_mc_get_endpoint); -static int parse_mc_ranges(struct device *dev, - int *paddr_cells, - int *mc_addr_cells, - int *mc_size_cells, - const __be32 **ranges_start) -{ - const __be32 *prop; - int range_tuple_cell_count; - int ranges_len; - int tuple_len; - struct device_node *mc_node = dev->of_node; - - *ranges_start = of_get_property(mc_node, "ranges", &ranges_len); - if (!(*ranges_start) || !ranges_len) { - dev_warn(dev, - "missing or empty ranges property for device tree node '%pOFn'\n", - mc_node); - return 0; - } - - *paddr_cells = of_n_addr_cells(mc_node); - - prop = of_get_property(mc_node, "#address-cells", NULL); - if (prop) - *mc_addr_cells = be32_to_cpup(prop); - else - *mc_addr_cells = *paddr_cells; - - prop = of_get_property(mc_node, "#size-cells", NULL); - if (prop) - *mc_size_cells = be32_to_cpup(prop); - else - *mc_size_cells = of_n_size_cells(mc_node); - - range_tuple_cell_count = *paddr_cells + *mc_addr_cells + - *mc_size_cells; - - tuple_len = range_tuple_cell_count * sizeof(__be32); - if (ranges_len % tuple_len != 0) { - dev_err(dev, "malformed ranges property '%pOFn'\n", mc_node); - return -EINVAL; - } - - return ranges_len / tuple_len; -} - static int get_mc_addr_translation_ranges(struct device *dev, struct fsl_mc_addr_translation_range **ranges, u8 *num_ranges) { - int ret; - int paddr_cells; - int mc_addr_cells; - int mc_size_cells; - int i; - const __be32 *ranges_start; - const __be32 *cell; - - ret = parse_mc_ranges(dev, - &paddr_cells, - &mc_addr_cells, - &mc_size_cells, - &ranges_start); - if (ret < 0) - return ret; + struct fsl_mc_addr_translation_range *r; + struct of_range_parser parser; + struct of_range range; - *num_ranges = ret; - if (!ret) { + of_range_parser_init(&parser, dev->of_node); + *num_ranges = of_range_count(&parser); + if (!*num_ranges) { /* * Missing or empty ranges property ("ranges;") for the * 'fsl,qoriq-mc' node. In this case, identity mapping @@ -1040,20 +1024,13 @@ static int get_mc_addr_translation_ranges(struct device *dev, if (!(*ranges)) return -ENOMEM; - cell = ranges_start; - for (i = 0; i < *num_ranges; ++i) { - struct fsl_mc_addr_translation_range *range = &(*ranges)[i]; - - range->mc_region_type = of_read_number(cell, 1); - range->start_mc_offset = of_read_number(cell + 1, - mc_addr_cells - 1); - cell += mc_addr_cells; - range->start_phys_addr = of_read_number(cell, paddr_cells); - cell += paddr_cells; - range->end_mc_offset = range->start_mc_offset + - of_read_number(cell, mc_size_cells); - - cell += mc_size_cells; + r = *ranges; + for_each_of_range(&parser, &range) { + r->mc_region_type = range.flags; + r->start_mc_offset = range.bus_addr; + r->end_mc_offset = range.bus_addr + range.size; + r->start_phys_addr = range.cpu_addr; + r++; } return 0; @@ -1089,17 +1066,6 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) } if (mc->fsl_mc_regs) { - /* - * Some bootloaders pause the MC firmware before booting the - * kernel so that MC will not cause faults as soon as the - * SMMU probes due to the fact that there's no configuration - * in place for MC. - * At this point MC should have all its SMMU setup done so make - * sure it is resumed. - */ - writel(readl(mc->fsl_mc_regs + FSL_MC_GCR1) & (~GCR1_P1_STOP), - mc->fsl_mc_regs + FSL_MC_GCR1); - if (IS_ENABLED(CONFIG_ACPI) && !dev_of_node(&pdev->dev)) { mc_stream_id = readl(mc->fsl_mc_regs + FSL_MC_FAPR); /* @@ -1113,19 +1079,38 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) error = acpi_dma_configure_id(&pdev->dev, DEV_DMA_COHERENT, &mc_stream_id); + if (error == -EPROBE_DEFER) + return error; if (error) dev_warn(&pdev->dev, "failed to configure dma: %d.\n", error); } + + /* + * Some bootloaders pause the MC firmware before booting the + * kernel so that MC will not cause faults as soon as the + * SMMU probes due to the fact that there's no configuration + * in place for MC. + * At this point MC should have all its SMMU setup done so make + * sure it is resumed. + */ + writel(readl(mc->fsl_mc_regs + FSL_MC_GCR1) & + (~(GCR1_P1_STOP | GCR1_P2_STOP)), + mc->fsl_mc_regs + FSL_MC_GCR1); } /* * Get physical address of MC portal for the root DPRC: */ plat_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!plat_res) + return -EINVAL; + mc_portal_phys_addr = plat_res->start; mc_portal_size = resource_size(plat_res); + mc_portal_base_phys_addr = mc_portal_phys_addr & ~0x3ffffff; + error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr, mc_portal_size, NULL, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &mc_io); @@ -1187,19 +1172,26 @@ error_cleanup_mc_io: * fsl_mc_bus_remove - callback invoked when the root MC bus is being * removed */ -static int fsl_mc_bus_remove(struct platform_device *pdev) +static void fsl_mc_bus_remove(struct platform_device *pdev) { struct fsl_mc *mc = platform_get_drvdata(pdev); + struct fsl_mc_io *mc_io; - if (!fsl_mc_is_root_dprc(&mc->root_mc_bus_dev->dev)) - return -EINVAL; - + mc_io = mc->root_mc_bus_dev->mc_io; fsl_mc_device_remove(mc->root_mc_bus_dev); + fsl_destroy_mc_io(mc_io); - fsl_destroy_mc_io(mc->root_mc_bus_dev->mc_io); - mc->root_mc_bus_dev->mc_io = NULL; + bus_unregister_notifier(&fsl_mc_bus_type, &fsl_mc_nb); - return 0; + if (mc->fsl_mc_regs) { + /* + * Pause the MC firmware so that it doesn't crash in certain + * scenarios, such as kexec. + */ + writel(readl(mc->fsl_mc_regs + FSL_MC_GCR1) | + (GCR1_P1_STOP | GCR1_P2_STOP), + mc->fsl_mc_regs + FSL_MC_GCR1); + } } static const struct of_device_id fsl_mc_bus_match_table[] = { @@ -1224,6 +1216,45 @@ static struct platform_driver fsl_mc_bus_driver = { }, .probe = fsl_mc_bus_probe, .remove = fsl_mc_bus_remove, + .shutdown = fsl_mc_bus_remove, +}; + +static int fsl_mc_bus_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct resource *res; + void __iomem *fsl_mc_regs; + + if (action != BUS_NOTIFY_ADD_DEVICE) + return 0; + + if (!of_match_device(fsl_mc_bus_match_table, dev) && + !acpi_match_device(fsl_mc_bus_acpi_match_table, dev)) + return 0; + + res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 1); + if (!res) + return 0; + + fsl_mc_regs = ioremap(res->start, resource_size(res)); + if (!fsl_mc_regs) + return 0; + + /* + * Make sure that the MC firmware is paused before the IOMMU setup for + * it is done or otherwise the firmware will crash right after the SMMU + * gets probed and enabled. + */ + writel(readl(fsl_mc_regs + FSL_MC_GCR1) | (GCR1_P1_STOP | GCR1_P2_STOP), + fsl_mc_regs + FSL_MC_GCR1); + iounmap(fsl_mc_regs); + + return 0; +} + +static struct notifier_block fsl_mc_nb = { + .notifier_call = fsl_mc_bus_notifier, }; static int __init fsl_mc_bus_driver_init(void) @@ -1250,7 +1281,7 @@ static int __init fsl_mc_bus_driver_init(void) if (error < 0) goto error_cleanup_dprc_driver; - return 0; + return bus_register_notifier(&platform_bus_type, &fsl_mc_nb); error_cleanup_dprc_driver: dprc_driver_exit(); |
