diff options
Diffstat (limited to 'drivers/staging/fsl-mc/bus/fsl-mc-bus.c')
-rw-r--r-- | drivers/staging/fsl-mc/bus/fsl-mc-bus.c | 318 |
1 files changed, 207 insertions, 111 deletions
diff --git a/drivers/staging/fsl-mc/bus/fsl-mc-bus.c b/drivers/staging/fsl-mc/bus/fsl-mc-bus.c index 44f64b6..30a48df 100644 --- a/drivers/staging/fsl-mc/bus/fsl-mc-bus.c +++ b/drivers/staging/fsl-mc/bus/fsl-mc-bus.c @@ -1,7 +1,7 @@ /* * Freescale Management Complex (MC) bus driver * - * Copyright (C) 2014 Freescale Semiconductor, Inc. + * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. * Author: German Rivera <German.Rivera@freescale.com> * * This file is licensed under the terms of the GNU General Public @@ -9,6 +9,8 @@ * warranty of any kind, whether express or implied. */ +#define pr_fmt(fmt) "fsl-mc: " fmt + #include <linux/module.h> #include <linux/of_device.h> #include <linux/of_address.h> @@ -25,8 +27,6 @@ #include "fsl-mc-private.h" #include "dprc-cmd.h" -static struct kmem_cache *mc_dev_cache; - /** * Default DMA mask for devices on a fsl-mc bus */ @@ -34,7 +34,7 @@ static struct kmem_cache *mc_dev_cache; /** * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device - * @root_mc_bus_dev: MC object device representing the root DPRC + * @root_mc_bus_dev: fsl-mc device representing the root DPRC * @num_translation_ranges: number of entries in addr_translation_ranges * @translation_ranges: array of bus to system address translation ranges */ @@ -62,8 +62,8 @@ struct fsl_mc_addr_translation_range { /** * fsl_mc_bus_match - device to driver matching callback - * @dev: the MC object device structure to match against - * @drv: the device driver to search for matching MC object device id + * @dev: the fsl-mc device to match against + * @drv: the device driver to search for matching fsl-mc object type * structures * * Returns 1 on success, 0 otherwise. @@ -75,8 +75,11 @@ static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv) struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv); bool found = false; - if (WARN_ON(!fsl_mc_bus_exists())) + /* When driver_override is set, only bind to the matching driver */ + if (mc_dev->driver_override) { + found = !strcmp(mc_dev->driver_override, mc_drv->driver.name); goto out; + } if (!mc_drv->match_id_table) goto out; @@ -91,7 +94,7 @@ static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv) /* * Traverse the match_id table of the given driver, trying to find - * a matching for the given MC object device. + * a matching for the given device. */ for (id = mc_drv->match_id_table; id->vendor != 0x0; id++) { if (id->vendor == mc_dev->obj_desc.vendor && @@ -132,23 +135,141 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(modalias); +static ssize_t rescan_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long val; + unsigned int irq_count; + struct fsl_mc_device *root_mc_dev; + struct fsl_mc_bus *root_mc_bus; + + if (!fsl_mc_is_root_dprc(dev)) + return -EINVAL; + + root_mc_dev = to_fsl_mc_device(dev); + root_mc_bus = to_fsl_mc_bus(root_mc_dev); + + if (kstrtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (val) { + mutex_lock(&root_mc_bus->scan_mutex); + dprc_scan_objects(root_mc_dev, NULL, &irq_count); + mutex_unlock(&root_mc_bus->scan_mutex); + } + + return count; +} +static DEVICE_ATTR_WO(rescan); + +static ssize_t driver_override_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + const char *driver_override, *old = mc_dev->driver_override; + char *cp; + + 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); + + return count; +} + +static ssize_t driver_override_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", mc_dev->driver_override); +} +static DEVICE_ATTR_RW(driver_override); + static struct attribute *fsl_mc_dev_attrs[] = { &dev_attr_modalias.attr, + &dev_attr_rescan.attr, + &dev_attr_driver_override.attr, NULL, }; ATTRIBUTE_GROUPS(fsl_mc_dev); +static int scan_fsl_mc_bus(struct device *dev, void *data) +{ + unsigned int irq_count; + struct fsl_mc_device *root_mc_dev; + struct fsl_mc_bus *root_mc_bus; + + if (fsl_mc_is_root_dprc(dev)) { + 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, &irq_count); + mutex_unlock(&root_mc_bus->scan_mutex); + } + + return 0; +} + +static ssize_t bus_rescan_store(struct bus_type *bus, + const char *buf, size_t count) +{ + unsigned long val; + + if (kstrtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (val) + bus_for_each_dev(bus, NULL, NULL, scan_fsl_mc_bus); + + return count; +} +static BUS_ATTR(rescan, (S_IWUSR | S_IWGRP), NULL, bus_rescan_store); + +static struct attribute *fsl_mc_bus_attrs[] = { + &bus_attr_rescan.attr, + NULL, +}; + +static const struct attribute_group fsl_mc_bus_group = { + .attrs = fsl_mc_bus_attrs, +}; + +static const struct attribute_group *fsl_mc_bus_groups[] = { + &fsl_mc_bus_group, + NULL, +}; + struct bus_type fsl_mc_bus_type = { .name = "fsl-mc", .match = fsl_mc_bus_match, .uevent = fsl_mc_bus_uevent, .dev_groups = fsl_mc_dev_groups, + .bus_groups = fsl_mc_bus_groups, }; EXPORT_SYMBOL_GPL(fsl_mc_bus_type); -static atomic_t root_dprc_count = ATOMIC_INIT(0); - static int fsl_mc_driver_probe(struct device *dev) { struct fsl_mc_driver *mc_drv; @@ -164,8 +285,7 @@ static int fsl_mc_driver_probe(struct device *dev) error = mc_drv->probe(mc_dev); if (error < 0) { - dev_err(dev, "MC object device probe callback failed: %d\n", - error); + dev_err(dev, "%s failed: %d\n", __func__, error); return error; } @@ -183,9 +303,7 @@ static int fsl_mc_driver_remove(struct device *dev) error = mc_drv->remove(mc_dev); if (error < 0) { - dev_err(dev, - "MC object device remove callback failed: %d\n", - error); + dev_err(dev, "%s failed: %d\n", __func__, error); return error; } @@ -232,8 +350,6 @@ int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver, return error; } - pr_info("MC object device driver %s registered\n", - mc_driver->driver.name); return 0; } EXPORT_SYMBOL_GPL(__fsl_mc_driver_register); @@ -249,15 +365,6 @@ void fsl_mc_driver_unregister(struct fsl_mc_driver *mc_driver) EXPORT_SYMBOL_GPL(fsl_mc_driver_unregister); /** - * fsl_mc_bus_exists - check if a root dprc exists - */ -bool fsl_mc_bus_exists(void) -{ - return atomic_read(&root_dprc_count) > 0; -} -EXPORT_SYMBOL_GPL(fsl_mc_bus_exists); - -/** * fsl_mc_get_root_dprc - function to traverse to the root dprc */ void fsl_mc_get_root_dprc(struct device *dev, @@ -315,21 +422,6 @@ static int get_dprc_icid(struct fsl_mc_io *mc_io, return error; } -static int get_dprc_version(struct fsl_mc_io *mc_io, - int container_id, u16 *major, u16 *minor) -{ - struct dprc_attributes attr; - int error; - - error = get_dprc_attr(mc_io, container_id, &attr); - if (error == 0) { - *major = attr.version.major; - *minor = attr.version.minor; - } - - return error; -} - static int translate_mc_addr(struct fsl_mc_device *mc_dev, enum dprc_region_type mc_region_type, u64 mc_offset, phys_addr_t *phys_addr) @@ -451,18 +543,37 @@ bool fsl_mc_is_root_dprc(struct device *dev) return dev == root_dprc_dev; } +static void fsl_mc_device_release(struct device *dev) +{ + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + struct fsl_mc_bus *mc_bus = NULL; + + kfree(mc_dev->regions); + + if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) + mc_bus = to_fsl_mc_bus(mc_dev); + + if (mc_bus) + kfree(mc_bus); + else + kfree(mc_dev); +} + /** - * Add a newly discovered MC object device to be visible in Linux + * Add a newly discovered fsl-mc device to be visible in Linux */ int fsl_mc_device_add(struct dprc_obj_desc *obj_desc, struct fsl_mc_io *mc_io, struct device *parent_dev, + const char *driver_override, struct fsl_mc_device **new_mc_dev) { int error; struct fsl_mc_device *mc_dev = NULL; struct fsl_mc_bus *mc_bus = NULL; struct fsl_mc_device *parent_mc_dev; + struct device *fsl_mc_platform_dev; + struct device_node *fsl_mc_platform_node; if (dev_is_fsl_mc(parent_dev)) parent_mc_dev = to_fsl_mc_device(parent_dev); @@ -473,7 +584,7 @@ int fsl_mc_device_add(struct dprc_obj_desc *obj_desc, /* * Allocate an MC bus device object: */ - mc_bus = devm_kzalloc(parent_dev, sizeof(*mc_bus), GFP_KERNEL); + mc_bus = kzalloc(sizeof(*mc_bus), GFP_KERNEL); if (!mc_bus) return -ENOMEM; @@ -482,16 +593,30 @@ int fsl_mc_device_add(struct dprc_obj_desc *obj_desc, /* * Allocate a regular fsl_mc_device object: */ - mc_dev = kmem_cache_zalloc(mc_dev_cache, GFP_KERNEL); + mc_dev = kzalloc(sizeof(*mc_dev), GFP_KERNEL); if (!mc_dev) return -ENOMEM; } mc_dev->obj_desc = *obj_desc; mc_dev->mc_io = mc_io; + + if (driver_override) { + /* + * We trust driver_override, so we don't need to use + * kstrndup() here + */ + mc_dev->driver_override = kstrdup(driver_override, GFP_KERNEL); + if (!mc_dev->driver_override) { + error = -ENOMEM; + goto error_cleanup_dev; + } + } + device_initialize(&mc_dev->dev); mc_dev->dev.parent = parent_dev; mc_dev->dev.bus = &fsl_mc_bus_type; + mc_dev->dev.release = fsl_mc_device_release; dev_set_name(&mc_dev->dev, "%s.%d", obj_desc->type, obj_desc->id); if (strcmp(obj_desc->type, "dprc") == 0) { @@ -524,8 +649,6 @@ int fsl_mc_device_add(struct dprc_obj_desc *obj_desc, } mc_io2 = mc_io; - - atomic_inc(&root_dprc_count); } error = get_dprc_icid(mc_io2, obj_desc->id, &mc_dev->icid); @@ -533,8 +656,8 @@ int fsl_mc_device_add(struct dprc_obj_desc *obj_desc, goto error_cleanup_dev; } else { /* - * A non-DPRC MC object device has to be a child of another - * MC object (specifically a DPRC object) + * A non-DPRC object has to be a child of a DPRC, use the + * parent's ICID and interrupt domain. */ mc_dev->icid = parent_mc_dev->icid; mc_dev->dma_mask = FSL_MC_DEFAULT_DMA_MASK; @@ -556,9 +679,14 @@ int fsl_mc_device_add(struct dprc_obj_desc *obj_desc, goto error_cleanup_dev; } - /* Objects are coherent, unless 'no shareability' flag set. */ - if (!(obj_desc->flags & DPRC_OBJ_FLAG_NO_MEM_SHAREABILITY)) - arch_setup_dma_ops(&mc_dev->dev, 0, 0, NULL, true); + fsl_mc_platform_dev = &mc_dev->dev; + while (dev_is_fsl_mc(fsl_mc_platform_dev)) + fsl_mc_platform_dev = fsl_mc_platform_dev->parent; + fsl_mc_platform_node = fsl_mc_platform_dev->of_node; + + /* Set up the iommu configuration for the devices. */ + fsl_mc_dma_configure(mc_dev, fsl_mc_platform_node, + !(obj_desc->flags & DPRC_OBJ_FLAG_NO_MEM_SHAREABILITY)); /* * The device-specific probe callback will get invoked by device_add() @@ -571,9 +699,7 @@ int fsl_mc_device_add(struct dprc_obj_desc *obj_desc, goto error_cleanup_dev; } - (void)get_device(&mc_dev->dev); - dev_dbg(parent_dev, "Added MC object device %s\n", - dev_name(&mc_dev->dev)); + dev_dbg(parent_dev, "added %s\n", dev_name(&mc_dev->dev)); *new_mc_dev = mc_dev; return 0; @@ -581,47 +707,34 @@ int fsl_mc_device_add(struct dprc_obj_desc *obj_desc, error_cleanup_dev: kfree(mc_dev->regions); if (mc_bus) - devm_kfree(parent_dev, mc_bus); + kfree(mc_bus); else - kmem_cache_free(mc_dev_cache, mc_dev); + kfree(mc_dev); return error; } EXPORT_SYMBOL_GPL(fsl_mc_device_add); /** - * fsl_mc_device_remove - Remove a MC object device from being visible to + * fsl_mc_device_remove - Remove an fsl-mc device from being visible to * Linux * - * @mc_dev: Pointer to a MC object device object + * @mc_dev: Pointer to an fsl-mc device */ void fsl_mc_device_remove(struct fsl_mc_device *mc_dev) { - struct fsl_mc_bus *mc_bus = NULL; - - kfree(mc_dev->regions); + kfree(mc_dev->driver_override); + mc_dev->driver_override = NULL; /* * The device-specific remove callback will get invoked by device_del() */ device_del(&mc_dev->dev); - put_device(&mc_dev->dev); - if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) { - mc_bus = to_fsl_mc_bus(mc_dev); + if (strcmp(mc_dev->obj_desc.type, "dprc") != 0) + mc_dev->dev.iommu_fwspec = NULL; - if (fsl_mc_is_root_dprc(&mc_dev->dev)) { - if (atomic_read(&root_dprc_count) > 0) - atomic_dec(&root_dprc_count); - else - WARN_ON(1); - } - } - - if (mc_bus) - devm_kfree(mc_dev->dev.parent, mc_bus); - else - kmem_cache_free(mc_dev_cache, mc_dev); + put_device(&mc_dev->dev); } EXPORT_SYMBOL_GPL(fsl_mc_device_remove); @@ -629,8 +742,7 @@ static int parse_mc_ranges(struct device *dev, int *paddr_cells, int *mc_addr_cells, int *mc_size_cells, - const __be32 **ranges_start, - u8 *num_ranges) + const __be32 **ranges_start) { const __be32 *prop; int range_tuple_cell_count; @@ -643,8 +755,6 @@ static int parse_mc_ranges(struct device *dev, dev_warn(dev, "missing or empty ranges property for device tree node '%s'\n", mc_node->name); - - *num_ranges = 0; return 0; } @@ -671,8 +781,7 @@ static int parse_mc_ranges(struct device *dev, return -EINVAL; } - *num_ranges = ranges_len / tuple_len; - return 0; + return ranges_len / tuple_len; } static int get_mc_addr_translation_ranges(struct device *dev, @@ -680,7 +789,7 @@ static int get_mc_addr_translation_ranges(struct device *dev, **ranges, u8 *num_ranges) { - int error; + int ret; int paddr_cells; int mc_addr_cells; int mc_size_cells; @@ -688,16 +797,16 @@ static int get_mc_addr_translation_ranges(struct device *dev, const __be32 *ranges_start; const __be32 *cell; - error = parse_mc_ranges(dev, + ret = parse_mc_ranges(dev, &paddr_cells, &mc_addr_cells, &mc_size_cells, - &ranges_start, - num_ranges); - if (error < 0) - return error; + &ranges_start); + if (ret < 0) + return ret; - if (!(*num_ranges)) { + *num_ranges = ret; + if (!ret) { /* * Missing or empty ranges property ("ranges;") for the * 'fsl,qoriq-mc' node. In this case, identity mapping @@ -749,8 +858,6 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) struct mc_version mc_version; struct resource res; - dev_info(&pdev->dev, "Root MC bus device probed"); - mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); if (!mc) return -ENOMEM; @@ -783,8 +890,7 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) goto error_cleanup_mc_io; } - dev_info(&pdev->dev, - "Freescale Management Complex Firmware version: %u.%u.%u\n", + dev_info(&pdev->dev, "MC firmware version: %u.%u.%u\n", mc_version.major, mc_version.minor, mc_version.revision); error = get_mc_addr_translation_ranges(&pdev->dev, @@ -793,16 +899,17 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) if (error < 0) goto error_cleanup_mc_io; - error = dpmng_get_container_id(mc_io, 0, &container_id); + error = dprc_get_container_id(mc_io, 0, &container_id); if (error < 0) { dev_err(&pdev->dev, - "dpmng_get_container_id() failed: %d\n", error); + "dprc_get_container_id() failed: %d\n", error); goto error_cleanup_mc_io; } memset(&obj_desc, 0, sizeof(struct dprc_obj_desc)); - error = get_dprc_version(mc_io, container_id, - &obj_desc.ver_major, &obj_desc.ver_minor); + error = dprc_get_api_version(mc_io, 0, + &obj_desc.ver_major, + &obj_desc.ver_minor); if (error < 0) goto error_cleanup_mc_io; @@ -812,7 +919,8 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) obj_desc.irq_count = 1; obj_desc.region_count = 0; - error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, &mc_bus_dev); + error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, NULL, + &mc_bus_dev); if (error < 0) goto error_cleanup_mc_io; @@ -840,7 +948,6 @@ static int fsl_mc_bus_remove(struct platform_device *pdev) fsl_destroy_mc_io(mc->root_mc_bus_dev->mc_io); mc->root_mc_bus_dev->mc_io = NULL; - dev_info(&pdev->dev, "Root MC bus device removed"); return 0; } @@ -865,22 +972,12 @@ static int __init fsl_mc_bus_driver_init(void) { int error; - mc_dev_cache = kmem_cache_create("fsl_mc_device", - sizeof(struct fsl_mc_device), 0, 0, - NULL); - if (!mc_dev_cache) { - pr_err("Could not create fsl_mc_device cache\n"); - return -ENOMEM; - } - error = bus_register(&fsl_mc_bus_type); if (error < 0) { - pr_err("fsl-mc bus type registration failed: %d\n", error); + pr_err("bus type registration failed: %d\n", error); goto error_cleanup_cache; } - pr_info("fsl-mc bus type registered\n"); - error = platform_driver_register(&fsl_mc_bus_driver); if (error < 0) { pr_err("platform_driver_register() failed: %d\n", error); @@ -914,7 +1011,6 @@ error_cleanup_bus: bus_unregister(&fsl_mc_bus_type); error_cleanup_cache: - kmem_cache_destroy(mc_dev_cache); return error; } postcore_initcall(fsl_mc_bus_driver_init); |