[RFC PATCH 2/4] drivers/bus: Freescale Management Complex (fsl-mc) bus driver
From: J. German Rivera
Date: Fri Aug 15 2014 - 18:09:55 EST
From: "J. German Rivera" <German.Rivera@xxxxxxxxxxxxx>
Platform device driver that sets up the basic bus infrastructure
for the fsl-mc bus type, including support for adding/removing
fsl-mc devices, register/unregister of fsl-mc drivers, and bus
match support to bind devices to drivers.
Signed-off-by: J. German Rivera <German.Rivera@xxxxxxxxxxxxx>
---
drivers/bus/Kconfig | 3 +
drivers/bus/Makefile | 3 +
drivers/bus/fsl-mc/Kconfig | 13 +
drivers/bus/fsl-mc/Makefile | 14 +
drivers/bus/fsl-mc/fsl_mc_bus.c | 734 +++++++++++++++++++++++++++++++++++++++
include/linux/fsl_mc.h | 177 ++++++++++
include/linux/fsl_mc_private.h | 85 +++++
7 files changed, 1029 insertions(+)
create mode 100644 drivers/bus/fsl-mc/Kconfig
create mode 100644 drivers/bus/fsl-mc/Makefile
create mode 100644 drivers/bus/fsl-mc/fsl_mc_bus.c
create mode 100644 include/linux/fsl_mc.h
create mode 100644 include/linux/fsl_mc_private.h
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 603eb1b..2fbb1fd 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -67,4 +67,7 @@ config VEXPRESS_CONFIG
help
Platform configuration infrastructure for the ARM Ltd.
Versatile Express.
+
+source "drivers/bus/fsl-mc/Kconfig"
+
endmenu
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 2973c18..6abcab1 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -15,3 +15,6 @@ obj-$(CONFIG_ARM_CCI) += arm-cci.o
obj-$(CONFIG_ARM_CCN) += arm-ccn.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
+
+# Freescale Management Complex (MC) bus drivers
+obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
diff --git a/drivers/bus/fsl-mc/Kconfig b/drivers/bus/fsl-mc/Kconfig
new file mode 100644
index 0000000..e3226f9
--- /dev/null
+++ b/drivers/bus/fsl-mc/Kconfig
@@ -0,0 +1,13 @@
+#
+# Freescale Management Complex (MC) bus drivers
+#
+# Copyright (C) 2014 Freescale Semiconductor, Inc.
+#
+# This file is released under the GPLv2
+#
+
+config FSL_MC_BUS
+ tristate "Freescale Management Complex (MC) bus driver"
+ help
+ Driver to enable the bus infrastructure for the Freescale
+ QorIQ Management Complex.
diff --git a/drivers/bus/fsl-mc/Makefile b/drivers/bus/fsl-mc/Makefile
new file mode 100644
index 0000000..f5f9033
--- /dev/null
+++ b/drivers/bus/fsl-mc/Makefile
@@ -0,0 +1,14 @@
+#
+# Freescale Management Complex (MC) bus drivers
+#
+# Copyright (C) 2014 Freescale Semiconductor, Inc.
+#
+# This file is released under the GPLv2
+#
+obj-$(CONFIG_FSL_MC_BUS) += fsl_mc_bus_driver.o
+
+fsl_mc_bus_driver-objs := fsl_mc_bus.o \
+ mc_sys.o \
+ dprc.o \
+ dpmng.o
+
diff --git a/drivers/bus/fsl-mc/fsl_mc_bus.c b/drivers/bus/fsl-mc/fsl_mc_bus.c
new file mode 100644
index 0000000..98f7add
--- /dev/null
+++ b/drivers/bus/fsl-mc/fsl_mc_bus.c
@@ -0,0 +1,734 @@
+/*
+ * Freescale Management Complex (MC) bus driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Author: German Rivera <German.Rivera@xxxxxxxxxxxxx>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/fsl_mc_private.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/limits.h>
+#include <linux/fsl_dpmng.h>
+#include <linux/fsl_mc_sys.h>
+#include "fsl_dprc_cmd.h"
+
+struct fsl_mc_bus *fsl_mc_bus;
+EXPORT_SYMBOL(fsl_mc_bus);
+
+static struct kmem_cache *mc_dev_cache;
+
+/**
+ * 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
+ * structures
+ *
+ * Returns 1 on success, 0 otherwise.
+ */
+static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv)
+{
+ const struct fsl_mc_device_match_id *id;
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv);
+ bool found = false;
+
+ if (WARN_ON(mc_dev->magic != FSL_MC_DEVICE_MAGIC))
+ goto out;
+ if (WARN_ON(mc_drv->magic != FSL_MC_DRIVER_MAGIC))
+ goto out;
+
+ if (mc_drv->match_id_table == NULL)
+ goto out;
+
+ /*
+ * If the object is not 'plugged' don't match.
+ * Only exception is the root DPRC, which is a special case.
+ */
+ if ((mc_dev->obj_desc.state & DPRC_OBJ_STATE_PLUGGED) == 0 &&
+ mc_dev->container != NULL)
+ goto out;
+
+ /*
+ * Traverse the match_id table of the given driver, trying to find
+ * a matching for the given MC object device.
+ */
+ for (id = mc_drv->match_id_table; id->vendor != 0x0; id++) {
+ if (id->vendor == mc_dev->obj_desc.vendor &&
+ strcmp(id->obj_type, mc_dev->obj_desc.type) == 0 &&
+ id->ver_major == mc_dev->obj_desc.ver_major &&
+ id->ver_minor == mc_dev->obj_desc.ver_minor) {
+ found = true;
+ break;
+ }
+ }
+
+out:
+ pr_debug("%s: %s %s\n", __func__, dev_name(dev),
+ found ? "matched" : "not matched");
+
+ return found;
+}
+
+/**
+ * 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)
+{
+ pr_debug("%s invoked\n", __func__);
+ return 0;
+}
+
+struct bus_type fsl_mc_bus_type = {
+ .name = "fsl-mc",
+ .match = fsl_mc_bus_match,
+ .uevent = fsl_mc_bus_uevent,
+ .drv_groups = NULL,
+ .dev_groups = NULL,
+ .bus_groups = NULL,
+ .pm = NULL,
+};
+EXPORT_SYMBOL(fsl_mc_bus_type);
+
+static int dprc_parse_dt_node(struct platform_device *pdev,
+ phys_addr_t *mc_portal_phys_addr,
+ uint32_t *mc_portal_size)
+{
+ struct resource res;
+ struct device_node *pdev_of_node = pdev->dev.of_node;
+ int error = -EINVAL;
+
+ error = of_address_to_resource(pdev_of_node, 0, &res);
+ if (error < 0) {
+ FSL_MC_ERROR(&pdev->dev,
+ "of_address_to_resource() failed for %s\n",
+ pdev_of_node->full_name);
+ goto out;
+ }
+
+ *mc_portal_phys_addr = res.start;
+ *mc_portal_size = resource_size(&res);
+ error = 0;
+out:
+ return error;
+}
+
+static int fsl_mc_driver_probe(struct device *dev)
+{
+ struct fsl_mc_driver *mc_drv;
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+ int error = -EINVAL;
+
+ if (WARN_ON(mc_dev->magic != FSL_MC_DEVICE_MAGIC))
+ goto error;
+ if (WARN_ON(mc_dev->driver != NULL))
+ goto error;
+ if (WARN_ON(dev->driver == NULL))
+ goto error;
+
+ mc_drv = to_fsl_mc_driver(dev->driver);
+ if (WARN_ON(mc_drv->magic != FSL_MC_DRIVER_MAGIC))
+ goto error;
+ if (WARN_ON(mc_drv->probe == NULL))
+ goto error;
+
+ mc_dev->driver = mc_drv;
+ error = mc_drv->probe(mc_dev);
+ if (error < 0) {
+ FSL_MC_ERROR(dev,
+ "MC object device probe callback failed: %d\n",
+ error);
+ mc_dev->driver = NULL;
+ goto error;
+ }
+
+ return 0;
+error:
+ return error;
+}
+
+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 = -EINVAL;
+
+ if (WARN_ON(dev->driver == NULL))
+ goto error;
+ if (WARN_ON(mc_drv->magic != FSL_MC_DRIVER_MAGIC))
+ goto error;
+ if (WARN_ON(mc_dev->magic != FSL_MC_DEVICE_MAGIC))
+ goto error;
+ if (WARN_ON(mc_dev->driver == NULL))
+ goto error;
+ if (WARN_ON(mc_dev->driver != mc_drv))
+ goto error;
+
+ error = mc_drv->remove(mc_dev);
+ if (error < 0) {
+ FSL_MC_ERROR(dev,
+ "MC object device remove callback failed: %d\n",
+ error);
+ goto error;
+ }
+
+ mc_dev->driver = NULL;
+ return 0;
+error:
+ return error;
+}
+
+static void fsl_mc_driver_shutdown(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);
+
+ mc_drv->shutdown(mc_dev);
+}
+
+/**
+ * __fsl_mc_driver_register - registers a child device driver with the
+ * MC bus
+ *
+ * This function is implicitly invoked from the registration function of
+ * fsl_mc device drivers, which is generated by the
+ * module_fsl_mc_driver() macro.
+ */
+int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver,
+ struct module *owner)
+{
+ struct fsl_mc_device *root_mc_dev;
+ int error = -EINVAL;
+
+ mc_driver->magic = FSL_MC_DRIVER_MAGIC;
+ mc_driver->driver.owner = owner;
+ mc_driver->driver.bus = &fsl_mc_bus_type;
+
+ if (fsl_mc_bus == NULL) {
+ error = -ENXIO;
+ FSL_MC_ERROR(NULL, "fsl-mc bus does not exist\n");
+ goto error;
+ }
+
+ if (WARN_ON(fsl_mc_bus->magic != FSL_MC_BUS_MAGIC))
+ goto error;
+
+ root_mc_dev = fsl_mc_bus->root_mc_dev;
+ if (root_mc_dev == NULL) {
+ error = -ENXIO;
+ FSL_MC_ERROR(NULL, "fsl-mc root DPRC device does not exist\n");
+ goto error;
+ }
+
+ if (WARN_ON(root_mc_dev->magic != FSL_MC_DEVICE_MAGIC))
+ goto error;
+
+ /*
+ * Set default driver callbacks, if not set by the child driver:
+ */
+ if (mc_driver->probe != NULL)
+ mc_driver->driver.probe = fsl_mc_driver_probe;
+
+ if (mc_driver->remove != NULL)
+ mc_driver->driver.remove = fsl_mc_driver_remove;
+
+ if (mc_driver->shutdown != NULL)
+ mc_driver->driver.shutdown = fsl_mc_driver_shutdown;
+
+ error = driver_register(&mc_driver->driver);
+ if (error < 0) {
+ FSL_MC_ERROR(&root_mc_dev->dev,
+ "driver_register() failed for %s: %d\n",
+ mc_driver->driver.name, error);
+ goto error;
+ }
+
+ pr_info("MC object device driver %s registered\n",
+ mc_driver->driver.name);
+ return 0;
+error:
+ return error;
+}
+EXPORT_SYMBOL_GPL(__fsl_mc_driver_register);
+
+/**
+ * fsl_mc_driver_unregister - unregisters a device driver from the
+ * MC bus
+ */
+void fsl_mc_driver_unregister(struct fsl_mc_driver *mc_driver)
+{
+ if (WARN_ON(mc_driver->magic != FSL_MC_DRIVER_MAGIC))
+ return;
+
+ driver_unregister(&mc_driver->driver);
+ mc_driver->magic = 0x0;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_driver_unregister);
+
+static int get_dprc_icid(struct fsl_mc_io *mc_io,
+ int container_id, uint16_t *icid)
+{
+ struct device *dev = &fsl_mc_bus->pdev->dev;
+ uint16_t dprc_handle;
+ struct dprc_attributes attr;
+ bool dprc_opened = false;
+ int error = -EINVAL;
+
+ error = dprc_open(mc_io, container_id, &dprc_handle);
+ if (error < 0) {
+ FSL_MC_ERROR(dev, "dprc_open() failed: %d\n", error);
+ goto out;
+ }
+
+ dprc_opened = true;
+ memset(&attr, 0, sizeof(attr));
+ error = dprc_get_attributes(mc_io, dprc_handle, &attr);
+ if (error < 0) {
+ FSL_MC_ERROR(dev, "dprc_get_attributes() failed: %d\n", error);
+ goto out;
+ }
+
+ *icid = attr.icid;
+ error = 0;
+out:
+ if (dprc_opened)
+ (void)dprc_close(mc_io, dprc_handle);
+
+ return error;
+}
+
+static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev,
+ struct fsl_mc_device *container_dev)
+{
+ int i;
+ int error;
+ struct fsl_mc_device_region *regions;
+ struct dprc_obj_desc *obj_desc = &mc_dev->obj_desc;
+ struct device *parent_dev = mc_dev->dev.parent;
+
+ regions = kmalloc_array(obj_desc->region_count,
+ sizeof(regions[0]), GFP_KERNEL);
+ if (regions == NULL) {
+ error = -ENOMEM;
+ FSL_MC_ERROR(parent_dev, "No memory to allocate regions[]\n");
+ goto error;
+ }
+
+ for (i = 0; i < obj_desc->region_count; i++) {
+ struct dprc_region_desc region_desc;
+
+ error = dprc_get_obj_region(container_dev->mc_io,
+ container_dev->mc_handle,
+ obj_desc->type,
+ obj_desc->id, i, ®ion_desc);
+ if (error < 0) {
+ FSL_MC_ERROR(parent_dev,
+ "dprc_get_obj_region() failed: %d\n",
+ error);
+ goto error;
+ }
+
+ WARN_ON(region_desc.base_paddr == 0x0);
+ WARN_ON(region_desc.size == 0);
+ regions[i].paddr = region_desc.base_paddr;
+ regions[i].size = region_desc.size;
+ }
+
+ mc_dev->regions = regions;
+ return 0;
+error:
+ kfree(regions);
+ return error;
+}
+
+/**
+ * Add a newly discovered MC object 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,
+ struct fsl_mc_dprc *container,
+ struct fsl_mc_device **new_mc_dev)
+{
+ int error = -EINVAL;
+ struct fsl_mc_device *mc_dev = NULL;
+ bool device_added = false;
+
+ /*
+ * DPRC devices must have a dedicated MC portal
+ */
+ if (WARN_ON(strcmp(obj_desc->type, "dprc") == 0 && mc_io == NULL))
+ goto error;
+
+ if (container != NULL)
+ if (WARN_ON(container->magic != FSL_MC_DPRC_MAGIC))
+ goto error;
+
+ mc_dev = kmem_cache_zalloc(mc_dev_cache, GFP_KERNEL);
+ if (mc_dev == NULL) {
+ error = -ENOMEM;
+ FSL_MC_ERROR(parent_dev,
+ "No memory to allocate fsl_mc_device\n");
+ goto error;
+ }
+
+ mc_dev->magic = FSL_MC_DEVICE_MAGIC;
+ mc_dev->container = container;
+ INIT_LIST_HEAD(&mc_dev->dev_node);
+ mc_dev->obj_desc = *obj_desc;
+ mc_dev->mc_io = mc_io;
+ device_initialize(&mc_dev->dev);
+ mc_dev->dev.parent = parent_dev;
+ mc_dev->dev.bus = &fsl_mc_bus_type;
+ dev_set_name(&mc_dev->dev, "%s.%x", obj_desc->type, obj_desc->id);
+
+ if (strcmp(obj_desc->type, "dprc") == 0) {
+ mc_dev->flags |= FSL_MC_IS_DPRC;
+ error = get_dprc_icid(mc_io, obj_desc->id, &mc_dev->icid);
+ if (error < 0)
+ goto error;
+ } else {
+ struct fsl_mc_device *container_dev = container->mc_dev;
+
+ if (WARN_ON(container_dev->magic != FSL_MC_DEVICE_MAGIC))
+ goto error;
+
+ mc_dev->icid = container_dev->icid;
+ mc_dev->dma_mask = 0xffffffff; /* 32bit */
+ mc_dev->dev.dma_mask = &mc_dev->dma_mask;
+ if (obj_desc->region_count != 0) {
+ error = fsl_mc_device_get_mmio_regions(mc_dev,
+ container_dev);
+ if (error < 0)
+ goto error;
+ }
+ }
+
+ /*
+ * The device-specific probe callback will get invoked by device_add()
+ */
+ error = device_add(&mc_dev->dev);
+ if (error < 0) {
+ FSL_MC_ERROR(parent_dev,
+ "device_add() failed for device %s: %d\n",
+ dev_name(&mc_dev->dev), error);
+ goto error;
+ }
+
+ get_device(&mc_dev->dev);
+ device_added = true;
+ if (container != NULL) {
+ mutex_lock(&container->mutex);
+ list_add_tail(&mc_dev->dev_node, &container->child_list);
+ mutex_unlock(&container->mutex);
+ }
+
+ dev_dbg(parent_dev, "Added MC object device %s\n",
+ dev_name(&mc_dev->dev));
+
+ *new_mc_dev = mc_dev;
+ return 0;
+error:
+ if (device_added) {
+ device_del(&mc_dev->dev);
+ put_device(&mc_dev->dev);
+ }
+
+ if (mc_dev != NULL) {
+ if (mc_dev->regions != NULL)
+ kfree(mc_dev->regions);
+
+ mc_dev->magic = 0x0;
+ kmem_cache_free(mc_dev_cache, mc_dev);
+ }
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_device_add);
+
+/**
+ * fsl_mc_device_remove - Remove a MC object device from being visible to
+ * Linux
+ *
+ * @mc_dev: Pointer to a MC object device object
+ */
+void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
+{
+ struct fsl_mc_dprc *container;
+
+ if (WARN_ON(mc_dev->magic != FSL_MC_DEVICE_MAGIC))
+ return;
+
+ if (mc_dev->regions != NULL)
+ kfree(mc_dev->regions);
+
+ container = mc_dev->container;
+ if (container != NULL) {
+ if (WARN_ON(container->magic != FSL_MC_DPRC_MAGIC))
+ return;
+
+ mutex_lock(&container->mutex);
+ list_del(&mc_dev->dev_node);
+ mutex_unlock(&container->mutex);
+ }
+
+ /*
+ * 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)
+ fsl_destroy_mc_io(mc_dev->mc_io);
+
+ mc_dev->mc_io = NULL;
+ mc_dev->magic = 0x0;
+ kmem_cache_free(mc_dev_cache, mc_dev);
+}
+EXPORT_SYMBOL_GPL(fsl_mc_device_remove);
+
+struct fsl_mc_device *fsl_mc_device_lookup(struct dprc_obj_desc *obj_desc,
+ struct fsl_mc_dprc *container)
+{
+ struct fsl_mc_device *mc_dev;
+
+ list_for_each_entry(mc_dev, &container->child_list, dev_node) {
+ if (FSL_MC_DEVICE_MATCH(mc_dev, obj_desc))
+ return mc_dev;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_device_lookup);
+
+/**
+ * fsl_mc_bus_probe - callback invoked when the MC bus is being
+ * added
+ */
+static int fsl_mc_bus_probe(struct platform_device *pdev)
+{
+ static DEFINE_SPINLOCK(lock);
+ struct dprc_obj_desc obj_desc;
+ int error = -EINVAL;
+ struct fsl_mc_bus *mc_bus = NULL;
+ struct fsl_mc_device *mc_dev = NULL;
+ struct fsl_mc_io *mc_io = NULL;
+ int container_id;
+ phys_addr_t mc_portal_phys_addr;
+ uint32_t mc_portal_size;
+ struct mc_version mc_version;
+
+ dev_info(&pdev->dev, "MC bus platform device probed");
+ mc_bus = devm_kzalloc(&pdev->dev, sizeof(*fsl_mc_bus), GFP_KERNEL);
+ if (mc_bus == NULL) {
+ error = -ENOMEM;
+ FSL_MC_ERROR(&pdev->dev,
+ "Failed to allocate memory for MC bus\n");
+ goto error;
+ }
+
+ spin_lock(&lock);
+ if (fsl_mc_bus == NULL) {
+ fsl_mc_bus = mc_bus;
+ } else {
+ spin_unlock(&lock);
+
+ /*
+ * Race between two concurrent invocations of
+ * fsl_mc_bus_probe()
+ */
+ error = -EEXIST;
+ FSL_MC_ERROR(&pdev->dev, "MC bus device already created\n");
+ goto error;
+ }
+ spin_unlock(&lock);
+
+ fsl_mc_bus->magic = FSL_MC_BUS_MAGIC;
+ platform_set_drvdata(pdev, fsl_mc_bus);
+ fsl_mc_bus->pdev = pdev;
+
+ error = dprc_parse_dt_node(pdev, &mc_portal_phys_addr, &mc_portal_size);
+ if (error < 0)
+ goto error;
+
+ error = fsl_create_mc_io(mc_portal_phys_addr,
+ mc_portal_size, 0, &mc_io);
+ if (error < 0)
+ goto error;
+
+ error = mc_get_version(mc_io, &mc_version);
+ if (error != 0) {
+ FSL_MC_ERROR(&pdev->dev,
+ "mc_get_version() failed with error %d\n", error);
+ goto error;
+ }
+
+ dev_info(&pdev->dev,
+ "Freescale Management Complex Firmware version: %u.%u.%u\n",
+ mc_version.major, mc_version.minor, mc_version.revision);
+
+ if (mc_version.major != MC_VER_MAJOR) {
+ FSL_MC_ERROR(&pdev->dev,
+ "ERROR: Firmware major version mismatch (found: %d, expected: %d)\n",
+ mc_version.major, MC_VER_MAJOR);
+ }
+
+ if (mc_version.minor != MC_VER_MINOR) {
+ FSL_MC_ERROR(&pdev->dev,
+ "WARNING: Firmware minor version mismatch (found: %d, expected: %d)\n",
+ mc_version.minor, MC_VER_MINOR);
+ }
+
+ error = dprc_get_container_id(mc_io, &container_id);
+ if (error < 0) {
+ FSL_MC_ERROR(&pdev->dev,
+ "dprc_get_container_id() failed: %d\n", error);
+ goto error;
+ }
+
+ obj_desc.vendor = FSL_MC_VENDOR_FREESCALE;
+ strcpy(obj_desc.type, "dprc");
+ obj_desc.id = container_id;
+ obj_desc.ver_major = DPRC_VER_MAJOR;
+ obj_desc.ver_minor = DPRC_VER_MINOR;
+ obj_desc.region_count = 0;
+
+ error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, NULL, &mc_dev);
+ if (error < 0)
+ goto error;
+
+ fsl_mc_bus->root_mc_dev = mc_dev;
+ return 0;
+error:
+ if (mc_dev != NULL)
+ fsl_mc_device_remove(mc_dev);
+ else if (mc_io != NULL)
+ fsl_destroy_mc_io(mc_io);
+
+ if (mc_bus != NULL) {
+ fsl_mc_bus->magic = 0x0;
+ devm_kfree(&pdev->dev, mc_bus);
+ if (fsl_mc_bus == mc_bus)
+ fsl_mc_bus = NULL;
+ }
+
+ return error;
+}
+
+/**
+ * fsl_mc_bus_remove - callback invoked when the MC bus is being
+ * removed
+ */
+static int fsl_mc_bus_remove(struct platform_device *pdev)
+{
+ int error = -EINVAL;
+ struct fsl_mc_bus *mc_bus = platform_get_drvdata(pdev);
+ struct fsl_mc_device *root_mc_dev;
+ struct fsl_mc_dprc *root_container;
+
+ dev_info(&pdev->dev, "MC bus removed");
+ if (WARN_ON(mc_bus != fsl_mc_bus))
+ goto error;
+ if (WARN_ON(mc_bus->magic != FSL_MC_BUS_MAGIC))
+ goto error;
+
+ root_mc_dev = fsl_mc_bus->root_mc_dev;
+ if (WARN_ON(root_mc_dev->magic != FSL_MC_DEVICE_MAGIC))
+ goto error;
+
+ root_container = root_mc_dev->mc_dev_data;
+ if (WARN_ON(root_container->magic != FSL_MC_DPRC_MAGIC))
+ goto error;
+
+ fsl_mc_device_remove(root_mc_dev);
+ fsl_mc_bus->magic = 0x0;
+ devm_kfree(&pdev->dev, fsl_mc_bus);
+ fsl_mc_bus = NULL;
+ return 0;
+error:
+ return error;
+}
+
+static const struct of_device_id fsl_mc_bus_match_table[] = {
+ {.compatible = "fsl,qoriq-mc",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, fsl_mc_bus_match_table);
+
+static struct platform_driver fsl_mc_bus_driver = {
+ .driver = {
+ .name = "fsl_mc_bus",
+ .owner = THIS_MODULE,
+ .pm = NULL,
+ .of_match_table = fsl_mc_bus_match_table,
+ },
+ .probe = fsl_mc_bus_probe,
+ .remove = fsl_mc_bus_remove,
+};
+
+static int __init fsl_mc_bus_driver_init(void)
+{
+ int error = -EINVAL;
+ bool bus_registered = false;
+
+ mc_dev_cache = kmem_cache_create("fsl_mc_device",
+ sizeof(struct fsl_mc_device), 0, 0,
+ NULL);
+ if (mc_dev_cache == NULL) {
+ error = -ENOMEM;
+ FSL_MC_ERROR(NULL, "Could not create fsl_mc_device cache\n");
+ goto error;
+ }
+
+ error = bus_register(&fsl_mc_bus_type);
+ if (error < 0) {
+ FSL_MC_ERROR(NULL, "MC bus registration failed: %d\n", error);
+ goto error;
+ }
+
+ bus_registered = true;
+ pr_info("MC bus (built " __DATE__ " " __TIME__ ") registered\n");
+
+ error = platform_driver_register(&fsl_mc_bus_driver);
+ if (error < 0) {
+ FSL_MC_ERROR(NULL,
+ "platform_driver_register() failed: %d\n", error);
+ goto error;
+ }
+
+ return 0;
+error:
+ if (bus_registered)
+ bus_unregister(&fsl_mc_bus_type);
+
+ if (mc_dev_cache != NULL)
+ kmem_cache_destroy(mc_dev_cache);
+
+ return error;
+}
+
+postcore_initcall(fsl_mc_bus_driver_init);
+
+static void __exit fsl_mc_bus_driver_exit(void)
+{
+ if (WARN_ON(mc_dev_cache == NULL))
+ return;
+
+ platform_driver_unregister(&fsl_mc_bus_driver);
+ bus_unregister(&fsl_mc_bus_type);
+ kmem_cache_destroy(mc_dev_cache);
+ pr_info("MC bus unregistered\n");
+}
+
+module_exit(fsl_mc_bus_driver_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor Inc.");
+MODULE_DESCRIPTION("Freescale Management Complex (MC) bus driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/fsl_mc.h b/include/linux/fsl_mc.h
new file mode 100644
index 0000000..37eb0b6
--- /dev/null
+++ b/include/linux/fsl_mc.h
@@ -0,0 +1,177 @@
+/*
+ * Freescale Management Complex (MC) bus public interface
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Author: German Rivera <German.Rivera@xxxxxxxxxxxxx>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef _FSL_MC_H_
+#define _FSL_MC_H_
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/fsl_dprc.h>
+
+#define FSL_MC_MAGIC(_a, _b, _c, _d) \
+ (((uint32_t)(_a) << 24) | \
+ ((uint32_t)(_b) << 16) | \
+ ((uint32_t)(_c) << 8) | \
+ (uint32_t)(_d))
+
+#define FSL_MC_VENDOR_FREESCALE 0x1957
+
+struct fsl_mc_device;
+struct fsl_mc_io;
+
+/**
+ * struct fsl_mc_driver - MC object device driver object
+ * @magic: marker to verify identity of this structure
+ * @probe: Function called when a device is added
+ * @remove: Function called when a device is removed
+ * @shutdown: Function called at shutdown time to quiesce the device
+ * @suspend: Function called when a device is stopped
+ * @resume: Function called when a device is resumed
+ * @driver: Generic device driver
+ * @match_id_table: table of supported device matching Ids
+ *
+ * Generic DPAA device driver object for device drivers that are registered
+ * with a DPRC bus. This structure is to be embedded in each device-specific
+ * driver structure.
+ */
+struct fsl_mc_driver {
+# define FSL_MC_DRIVER_MAGIC FSL_MC_MAGIC('L', 'D', 'R', 'V')
+ uint32_t magic;
+
+ int (*probe)(struct fsl_mc_device *dev);
+ int (*remove)(struct fsl_mc_device *dev);
+ void (*shutdown)(struct fsl_mc_device *dev);
+ int (*suspend)(struct fsl_mc_device *dev, pm_message_t state);
+ int (*resume)(struct fsl_mc_device *dev);
+ struct device_driver driver;
+ const struct fsl_mc_device_match_id *match_id_table;
+};
+
+#define to_fsl_mc_driver(_drv) \
+ container_of(_drv, struct fsl_mc_driver, driver)
+
+/**
+ * struct fsl_mc_device_match_id - MC object device Id entry for driver matching
+ * @vendor: vendor ID
+ * @obj_type: MC object type
+ * @ver_major: MC object version major number
+ * @ver_minor: MC object version minor number
+ *
+ * Type of entries in the "device Id" table for MC object devices supported by
+ * a MC object device driver. The last entry of the table has vendor set to 0x0
+ */
+struct fsl_mc_device_match_id {
+ uint16_t vendor;
+ const char obj_type[16];
+ uint32_t ver_major;
+ uint32_t ver_minor;
+};
+
+/**
+ * struct fsl_mc_device_region - MC object device MMIO region
+ * @addr: base physical address
+ * @size: size of the region in bytes
+ */
+struct fsl_mc_device_region {
+ phys_addr_t paddr;
+ uint32_t size;
+};
+
+/**
+ * Bit masks for a MC object device (struct fsl_mc_device) flags
+ */
+#define FSL_MC_IS_DPRC 0x0001
+
+/**
+ * struct fsl_mc_device - MC object device object
+ * @magic: marker to verify identity of this structure
+ * @flags: MC object device flags
+ * @icid: Isolation context ID for the device
+ * @mc_handle: MC handle for the corresponding MC object opened
+ * @mc_io: Pointer to MC IO object assigned to this device or
+ * NULL if none.
+ * @driver: Pointer to the MC object device driver for this device
+ * @container: Pointer to the DPRC device that contains this MC object device
+ * @dev_node: Node in the container's child list
+ * @obj_desc: MC description of the DPAA device
+ * @num_regions: Number of MMIO regions for this MC object device
+ * @regions: pointer to array of MMIO region entries
+ * @dma_mask: DMA mask for non-DPRC devices
+ * @dev: Linux driver model device object
+ * @mc_dev_data: Pointer to MC object device-specific data
+ *
+ * Generic device object for MC object devices that are "attached" to a
+ * MC bus.
+ *
+ * NOTES:
+ * - For a non-DPRC object its icid is the same as its parent DPRC's icid.
+ * We could have defined the icid field in the parent DPRC's fsl_mc_dprc
+ * structure instead of here, since non-DPRC objects have a pointer to their
+ * parent DPRC. However, we need to have it here, because the fsl_mc_dprc
+ * structure is created too late for the SMMU driver notifier callback to use
+ * it. The fsl_mc_dprc structure is created by the DPRC driver's
+ * probe callback (dprc_probe()). The SMMU notifier callback gets invoked
+ * after device_add() has been called for an MC object device, but before
+ * the device-specific probe callback gets called.
+ * - DP_OBJ_DPRC objects are the only MC objects that have built-in MC
+ * portals. For all other MC objects, their device drivers are responsible for
+ * allocating MC portals for them by calling fsl_mc_portal_allocate().
+ * - dev.drvdata is used by driver for keeping its own local information
+ * using that device and MC bus driver is not supposed to use
+ * dev.drvdata. So we have defined mc_dev_data for keeping dprc device
+ * information which will be used by MC bus and free dev.drvdata
+ * for driver local usages.
+ */
+struct fsl_mc_device {
+# define FSL_MC_DEVICE_MAGIC FSL_MC_MAGIC('L', 'D', 'E', 'V')
+ uint32_t magic;
+ uint16_t flags;
+ uint16_t icid;
+ uint16_t mc_handle;
+ struct fsl_mc_io *mc_io;
+ struct fsl_mc_driver *driver;
+ struct fsl_mc_dprc *container;
+ struct list_head dev_node;
+ struct dprc_obj_desc obj_desc;
+ struct fsl_mc_device_region *regions;
+ uint64_t dma_mask;
+ struct device dev;
+ void *mc_dev_data;
+};
+
+#define to_fsl_mc_device(_dev) \
+ container_of(_dev, struct fsl_mc_device, dev)
+
+/*
+ * module_fsl_mc_driver() - Helper macro for drivers that don't do
+ * anything special in module init/exit. This eliminates a lot of
+ * boilerplate. Each module may only use this macro once, and
+ * calling it replaces module_init() and module_exit()
+ */
+#define module_fsl_mc_driver(__fsl_mc_driver) \
+ module_driver(__fsl_mc_driver, fsl_mc_driver_register, \
+ fsl_mc_driver_unregister)
+
+/*
+ * Macro to avoid include chaining to get THIS_MODULE
+ */
+#define fsl_mc_driver_register(drv) \
+ __fsl_mc_driver_register(drv, THIS_MODULE)
+
+int __must_check __fsl_mc_driver_register(struct fsl_mc_driver *fsl_mc_driver,
+ struct module *owner);
+
+void fsl_mc_driver_unregister(struct fsl_mc_driver *driver);
+
+extern struct bus_type fsl_mc_bus_type;
+
+#endif /* _FSL_MC_H_ */
diff --git a/include/linux/fsl_mc_private.h b/include/linux/fsl_mc_private.h
new file mode 100644
index 0000000..dabd030
--- /dev/null
+++ b/include/linux/fsl_mc_private.h
@@ -0,0 +1,85 @@
+/*
+ * Freescale Management Complex (MC) bus private declarations
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Author: German Rivera <German.Rivera@xxxxxxxxxxxxx>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef _FSL_MC_PRIVATE_H_
+#define _FSL_MC_PRIVATE_H_
+
+#include <linux/fsl_mc.h>
+#include <linux/mutex.h>
+#include <linux/stringify.h>
+
+#define FSL_MC_ERROR(_dev, _fmt, ...) \
+ do { \
+ if ((_dev) != NULL) \
+ dev_err(_dev, "%s:" __stringify(__LINE__) " " \
+ _fmt, __func__, ##__VA_ARGS__); \
+ else \
+ pr_err("%s:" __stringify(__LINE__) " " _fmt, \
+ __func__, ##__VA_ARGS__); \
+ } while (0)
+
+#define FSL_MC_DEVICE_MATCH(_mc_dev, _obj_desc) \
+ (strcmp((_mc_dev)->obj_desc.type, (_obj_desc)->type) == 0 && \
+ (_mc_dev)->obj_desc.id == (_obj_desc)->id)
+
+#define FSL_MC_IS_ALLOCATABLE(_obj_type) \
+ (strcmp(_obj_type, "dpbp") == 0 || \
+ strcmp(_obj_type, "dpcon") == 0)
+
+/**
+ * struct fsl_mc_bus - Management Complex (MC) bus object
+ * @magic: marker to verify identity of this structure
+ * @pdev: platform device for this MC bus object
+ * @root_mc_dev: pointer to root MC object device for this MC bus.
+ */
+struct fsl_mc_bus {
+# define FSL_MC_BUS_MAGIC FSL_MC_MAGIC('L', 'B', 'U', 'S')
+ uint32_t magic;
+ struct platform_device *pdev;
+ struct fsl_mc_device *root_mc_dev;
+};
+
+/**
+ * struct fsl_mc_dprc - Data Path Resource Container (DPRC) object
+ * @magic: marker to verify identity of this structure
+ * @mc_dev: pointer to MC object device object for this DPRC
+ * @mutex: mutex to serialize access to the container.
+ * @child_device_count: have the count of devices in this DPRC
+ * @child_list: anchor node of list of child devices on this DPRC
+ */
+struct fsl_mc_dprc {
+# define FSL_MC_DPRC_MAGIC FSL_MC_MAGIC('D', 'P', 'R', 'C')
+ uint32_t magic;
+ struct fsl_mc_device *mc_dev;
+ struct mutex mutex; /* serializes access to fields below */
+ uint16_t child_device_count; /* Count of devices in this DPRC */
+ struct list_head child_list;
+};
+
+int __must_check fsl_mc_device_add(struct dprc_obj_desc *obj_desc,
+ struct fsl_mc_io *mc_io,
+ struct device *parent_dev,
+ struct fsl_mc_dprc *container,
+ struct fsl_mc_device **new_mc_dev);
+
+void fsl_mc_device_remove(struct fsl_mc_device *mc_dev);
+
+struct fsl_mc_device *__must_check fsl_mc_device_lookup(struct dprc_obj_desc
+ *obj_desc,
+ struct fsl_mc_dprc
+ *container);
+
+int __must_check dprc_scan_container(struct fsl_mc_dprc *container);
+
+int __must_check dprc_discover_container_objects(struct fsl_mc_dprc *container);
+
+extern struct fsl_mc_bus *fsl_mc_bus;
+
+#endif /* _FSL_MC_PRIVATE_H_ */
--
1.7.9.7
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/