From: "J. German Rivera" <german.riv...@freescale.com>

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.riv...@freescale.com>
---
 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.riv...@freescale.com>
+ *
+ * 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, &region_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.riv...@freescale.com>
+ *
+ * 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.riv...@freescale.com>
+ *
+ * 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to