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