All of lore.kernel.org
 help / color / mirror / Atom feed
From: "J. German Rivera" <German.Rivera@freescale.com>
To: <gregkh@linuxfoundation.org>, <arnd@arndb.de>,
	<linux-kernel@vger.kernel.org>
Cc: <stuart.yoder@freescale.com>,
	<linuxppc-release@linux.freescale.net>,
	"J. German Rivera" <German.Rivera@freescale.com>
Subject: [RFC PATCH 3/4 v2] drivers/bus: Device driver for FSL-MC DPRC devices
Date: Tue, 19 Aug 2014 19:54:29 -0500	[thread overview]
Message-ID: <1408496070-6252-4-git-send-email-German.Rivera@freescale.com> (raw)
In-Reply-To: <1408496070-6252-1-git-send-email-German.Rivera@freescale.com>

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

A DPRC (Data Path Resource Container) is an isolation device
that contains a set of DPAA networking devices to be
assigned to an isolation domain (e.g., a virtual machine).

Signed-off-by: J. German Rivera <German.Rivera@freescale.com>
---
 drivers/bus/fsl-mc/Makefile      |    3 +-
 drivers/bus/fsl-mc/fsl_mc_dprc.c |  396 ++++++++++++++++++++++++++++++++++++++
 include/linux/fsl_mc_private.h   |    6 +
 3 files changed, 404 insertions(+), 1 deletion(-)
 create mode 100644 drivers/bus/fsl-mc/fsl_mc_dprc.c

diff --git a/drivers/bus/fsl-mc/Makefile b/drivers/bus/fsl-mc/Makefile
index f5f9033..4991999 100644
--- a/drivers/bus/fsl-mc/Makefile
+++ b/drivers/bus/fsl-mc/Makefile
@@ -5,7 +5,8 @@
 #
 # This file is released under the GPLv2
 #
-obj-$(CONFIG_FSL_MC_BUS) += fsl_mc_bus_driver.o
+obj-$(CONFIG_FSL_MC_BUS) += fsl_mc_bus_driver.o \
+			    fsl_mc_dprc.o

 fsl_mc_bus_driver-objs := fsl_mc_bus.o \
 			      mc_sys.o \
diff --git a/drivers/bus/fsl-mc/fsl_mc_dprc.c b/drivers/bus/fsl-mc/fsl_mc_dprc.c
new file mode 100644
index 0000000..5aaf3e6
--- /dev/null
+++ b/drivers/bus/fsl-mc/fsl_mc_dprc.c
@@ -0,0 +1,396 @@
+/*
+ * Freescale daata path resource container (DPRC) driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Author: German Rivera <German.Rivera@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/fsl_mc_sys.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "fsl_dprc_cmd.h"
+
+/**
+ * dprc_remove_all_devices - Does a bus remove for all objects of a DPRC
+ *
+ * @mc_bus: pointer to the logical bus that represents the DPRC
+ *
+ * Removes all devices from the logical bus (not from the physical MC)
+ */
+static void dprc_remove_all_devices(struct fsl_mc_bus *mc_bus)
+{
+	struct fsl_mc_device *mc_dev;
+	struct fsl_mc_device *next;
+
+	list_for_each_entry_safe(mc_dev, next, &mc_bus->child_list,
+				 dev_node) {
+		WARN_ON(mc_dev->dev.parent != &mc_bus->self->dev);
+		fsl_mc_device_remove(mc_dev);
+	}
+}
+
+/**
+ * dprc_remove_devices - Removes devices for objects removed from a DPRC
+ *
+ * @mc_bus: pointer to the logical bus that corresponds to the DPRC
+ * @obj_desc_array: array of object descriptors for child objects currently
+ * present in the DPRC in the MC.
+ * @num_child_objects_in_mc: number of entries in obj_desc_array
+ *
+ * Synchronizes the state of the Linux bus driver with the actual state of
+ * the MC by removing devices that represent MC objects that have
+ * been dynamically removed in the physical DPRC.
+ */
+static void dprc_remove_devices(struct fsl_mc_bus *mc_bus,
+				struct dprc_obj_desc *obj_desc_array,
+				int num_child_objects_in_mc)
+{
+	if (num_child_objects_in_mc != 0) {
+		/*
+		 * Remove child objects that are in the DPRC in Linux,
+		 * but not in the MC:
+		 */
+		int i;
+		struct fsl_mc_device *child_obj;
+		struct fsl_mc_device *next;
+
+		list_for_each_entry_safe(child_obj, next,
+					 &mc_bus->child_list, dev_node) {
+			for (i = 0; i < num_child_objects_in_mc; i++) {
+				if (strlen(obj_desc_array[i].type) != 0 &&
+				    FSL_MC_DEVICE_MATCH(child_obj,
+							&obj_desc_array[i]))
+					break;
+			}
+
+			if (i == num_child_objects_in_mc)
+				fsl_mc_device_remove(child_obj);
+		}
+	} else {
+		/*
+		 * There are no child objects for this DPRC in the MC.
+		 * So, remove all the child objects from Linux:
+		 */
+		dprc_remove_all_devices(mc_bus);
+	}
+}
+
+/**
+ * dprc_add_new_devices - Adds devices to the logical bus for a DPRC
+ *
+ * @mc_bus: pointer to the logical bus that corresponds to the DPRC
+ * @obj_desc_array: array of device descriptors for child devices currently
+ * present in the physical DPRC.
+ * @num_child_objects_in_mc: number of entries in obj_desc_array
+ *
+ * Synchronizes the state of the Linux bus driver with the actual
+ * state of the MC by adding objects that have been newly discovered
+ * in the physical DPRC.
+ */
+static void dprc_add_new_devices(struct fsl_mc_bus *mc_bus,
+				 struct dprc_obj_desc *obj_desc_array,
+				 int num_child_objects_in_mc)
+{
+	int error;
+	int i;
+	struct fsl_mc_device *mc_bus_dev = mc_bus->self;
+
+	for (i = 0; i < num_child_objects_in_mc; i++) {
+		struct dprc_region_desc region_desc;
+		struct fsl_mc_device *child_dev;
+		struct fsl_mc_io *mc_io = NULL;
+		struct dprc_obj_desc *obj_desc = &obj_desc_array[i];
+
+		if (strlen(obj_desc->type) == 0)
+			continue;
+		/*
+		 * Check if device is already known to Linux:
+		 */
+		child_dev = fsl_mc_device_lookup(obj_desc, mc_bus);
+		if (child_dev != NULL)
+			continue;
+
+		if (strcmp(obj_desc->type, "dprc") == 0) {
+			/*
+			 * Get the MC portal physical address for the device
+			 * (specified in the device's first MMIO region):
+			 */
+			if (WARN_ON(obj_desc->region_count == 0))
+				continue;
+
+			error = dprc_get_obj_region(mc_bus_dev->mc_io,
+						    mc_bus_dev->mc_handle,
+						    obj_desc->type,
+						    obj_desc->id,
+						    0, &region_desc);
+			if (error < 0) {
+				dev_err(&mc_bus_dev->dev,
+					"dprc_get_obj_region() failed: %d\n",
+					error);
+				continue;
+			}
+
+			if (region_desc.size !=
+					mc_bus_dev->mc_io->portal_size) {
+				error = -EINVAL;
+				dev_err(&mc_bus_dev->dev,
+					"Invalid MC portal size: %lu\n",
+					region_desc.size);
+				continue;
+			}
+
+			error = fsl_create_mc_io(region_desc.base_paddr,
+						 region_desc.size, 0, &mc_io);
+			if (error < 0)
+				continue;
+		}
+
+		error = fsl_mc_device_add(obj_desc, mc_io, &mc_bus_dev->dev,
+					  &child_dev);
+		if (error < 0) {
+			if (mc_io != NULL)
+				fsl_destroy_mc_io(mc_io);
+
+			continue;
+		}
+	}
+}
+
+/**
+ * dprc_scan_objects - Discover objects in a DPRC
+ *
+ * @mc_bus: pointer to the logical bus that corresponds to the DPRC
+ *
+ * Detects objects added and removed from a DPRC and synchronizes the
+ * state of the Linux bus driver, MC by adding and removing
+ * devices accordingly.
+ */
+int dprc_scan_objects(struct fsl_mc_bus *mc_bus)
+{
+	int num_child_objects;
+	int dprc_get_obj_failures;
+	int error = -EINVAL;
+	struct dprc_obj_desc *child_obj_desc_array = NULL;
+	struct fsl_mc_device *mc_bus_dev;
+
+	mc_bus_dev = mc_bus->self;
+	error = dprc_get_obj_count(mc_bus_dev->mc_io,
+				   mc_bus_dev->mc_handle,
+				   &num_child_objects);
+	if (error < 0) {
+		dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n",
+			error);
+		goto out;
+	}
+
+	if (num_child_objects != 0) {
+		int i;
+
+		child_obj_desc_array =
+		    kmalloc_array(num_child_objects,
+				  sizeof(*child_obj_desc_array), GFP_KERNEL);
+		if (child_obj_desc_array == NULL) {
+			error = -ENOMEM;
+			dev_err(&mc_bus_dev->dev,
+				"No memory to allocate obj_desc array\n");
+			goto out;
+		}
+
+		/*
+		 * Discover objects currently present in the physical DPRC:
+		 */
+		dprc_get_obj_failures = 0;
+		for (i = 0; i < num_child_objects; i++) {
+			struct dprc_obj_desc *obj_desc =
+			    &child_obj_desc_array[i];
+
+			error = dprc_get_obj(mc_bus_dev->mc_io,
+					     mc_bus_dev->mc_handle,
+					     i, obj_desc);
+			if (error < 0) {
+				dev_err(&mc_bus_dev->dev,
+					"dprc_get_obj(i=%d) failed: %d\n",
+					i, error);
+				/*
+				 * Mark the obj entry as "invalid", by using the
+				 * empty string as obj type:
+				 */
+				obj_desc->type[0] = '\0';
+				obj_desc->id = error;
+				dprc_get_obj_failures++;
+				continue;
+			}
+
+			dev_info(&mc_bus_dev->dev,
+				 "Discovered object: type %s, id %d\n",
+				 obj_desc->type, obj_desc->id);
+		}
+
+		if (dprc_get_obj_failures != 0) {
+			dev_err(&mc_bus_dev->dev,
+				"%d out of %d devices could not be retrieved\n",
+				dprc_get_obj_failures, num_child_objects);
+		}
+	}
+
+	dprc_remove_devices(mc_bus, child_obj_desc_array, num_child_objects);
+
+	dprc_add_new_devices(mc_bus, child_obj_desc_array, num_child_objects);
+
+	error = 0;
+out:
+	if (child_obj_desc_array != NULL)
+		kfree(child_obj_desc_array);
+
+	return error;
+}
+EXPORT_SYMBOL_GPL(dprc_scan_objects);
+
+/**
+ * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state
+ *
+ * @mc_bus: pointer to the logical bus that corresponds to the DPRC
+ *
+ * Scans the physical DPRC and synchronizes the state of the Linux
+ * bus driver with the actual state of the MC by adding and removing
+ * devices as appropriate.
+ */
+int dprc_scan_container(struct fsl_mc_bus *mc_bus)
+{
+	int error = -EINVAL;
+
+	error = dprc_scan_objects(mc_bus);
+	if (error < 0)
+		goto error;
+
+	return 0;
+error:
+	return error;
+}
+EXPORT_SYMBOL_GPL(dprc_scan_container);
+
+/**
+ * dprc_probe - callback invoked when a DPRC is being bound to this driver
+ *
+ * @mc_dev: Pointer to fsl-mc device representing a DPRC
+ *
+ * It creates the corresponding fsl_mc_bus object and opens the physical DPRC
+ * in the MC.
+ * It scans the DPRC to discover the MC objects contained in it.
+ */
+static int dprc_probe(struct fsl_mc_device *mc_dev)
+{
+	int irq_count;
+	struct fsl_mc_io *mc_io;
+	struct fsl_mc_bus *mc_bus = NULL;
+	int error = -EINVAL;
+	bool dprc_opened = false;
+
+	if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0))
+		goto error;
+
+	mc_io = mc_dev->mc_io;
+	irq_count = mc_dev->obj_desc.irq_count;
+	if (WARN_ON(irq_count == 0))
+		goto error;
+
+	mc_bus = devm_kzalloc(&mc_dev->dev, sizeof(*mc_bus), GFP_KERNEL);
+	if (mc_bus == NULL) {
+		error = -ENOMEM;
+		dev_err(&mc_dev->dev,
+			"Failed to allocate memory for dprc device\n");
+		goto error;
+	}
+
+	mc_dev->mc_dev_data = mc_bus;
+	mc_bus->self = mc_dev;
+	INIT_LIST_HEAD(&mc_bus->child_list);
+	mutex_init(&mc_bus->mutex);
+
+	error = dprc_open(mc_io, mc_dev->obj_desc.id, &mc_dev->mc_handle);
+	if (error < 0) {
+		dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error);
+		goto error;
+	}
+
+	dprc_opened = true;
+	error = dprc_scan_container(mc_bus);
+	if (error < 0)
+		goto error;
+
+	dev_info(&mc_dev->dev, "DPRC device bound to driver");
+	return 0;
+error:
+	if (mc_bus != NULL) {
+		if (dprc_opened)
+			(void)dprc_close(mc_io, mc_dev->mc_handle);
+
+		devm_kfree(&mc_dev->dev, mc_bus);
+	}
+
+	return error;
+}
+
+/**
+ * dprc_remove - callback invoked when a DPRC is being unbound from this driver
+ *
+ * @mc_dev: Pointer to fsl-mc device representing the DPRC
+ *
+ * It removes the DPRC's child objects from Linux (not from the MC) and
+ * closes the DPRC device in the MC.
+ * It destroys the corresponding fsl_mc_bus object.
+ */
+static int dprc_remove(struct fsl_mc_device *mc_dev)
+{
+	struct fsl_mc_bus *mc_bus;
+	int error = -EINVAL;
+
+	if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0))
+		goto error;
+	if (WARN_ON(mc_dev->mc_io == NULL))
+		goto error;
+
+	mc_bus = mc_dev->mc_dev_data;
+	dprc_remove_all_devices(mc_bus);
+	error = dprc_close(mc_dev->mc_io, mc_dev->mc_handle);
+	if (error < 0)
+		dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);
+
+	devm_kfree(&mc_dev->dev, mc_bus);
+
+	dev_info(&mc_dev->dev, "DPRC device unbound from driver");
+	return 0;
+error:
+	return error;
+}
+
+static const struct fsl_mc_device_match_id match_id_table[] = {
+	{
+	 .vendor = FSL_MC_VENDOR_FREESCALE,
+	 .obj_type = "dprc",
+	 .ver_major = DPRC_VER_MAJOR,
+	 .ver_minor = DPRC_VER_MINOR},
+	{.vendor = 0x0},
+};
+
+static struct fsl_mc_driver dprc_driver = {
+	.driver = {
+		   .name = FSL_MC_DPRC_DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .pm = NULL,
+		   },
+	.match_id_table = match_id_table,
+	.probe = dprc_probe,
+	.remove = dprc_remove,
+};
+
+module_fsl_mc_driver(dprc_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor Inc.");
+MODULE_DESCRIPTION("Freescale's DPRC container driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/fsl_mc_private.h b/include/linux/fsl_mc_private.h
index 624949d..bc2c0da 100644
--- a/include/linux/fsl_mc_private.h
+++ b/include/linux/fsl_mc_private.h
@@ -15,6 +15,8 @@
 #include <linux/mutex.h>
 #include <linux/stringify.h>

+#define FSL_MC_DPRC_DRIVER_NAME    "fsl_mc_dprc"
+
 #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)
@@ -52,4 +54,8 @@ struct fsl_mc_device *__must_check fsl_mc_device_lookup(struct dprc_obj_desc
 							struct fsl_mc_bus
 								*mc_bus);

+int __must_check dprc_scan_container(struct fsl_mc_bus *mc_bus);
+
+int __must_check dprc_discover_objects(struct fsl_mc_bus *mc_bus);
+
 #endif /* _FSL_MC_PRIVATE_H_ */
--
1.7.9.7


  parent reply	other threads:[~2014-08-20  0:55 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-08-20  0:54 [RFC PATCH 0/4 v2] drivers/bus: Freescale Management Complex bus driver patch series J. German Rivera
2014-08-20  0:54 ` [RFC PATCH 1/4 v2] drivers/bus: Added Freescale Management Complex APIs J. German Rivera
2014-08-20  1:19   ` Joe Perches
2014-08-20 16:20     ` German Rivera
2014-08-20  0:54 ` [RFC PATCH 2/4 v2] drivers/bus: Freescale Management Complex (fsl-mc) bus driver J. German Rivera
2014-08-22 11:16   ` Michal Marek
2014-08-20  0:54 ` J. German Rivera [this message]
2014-08-20  0:54 ` [RFC PATCH 4/4 v2] Update MAINTAINERS file J. German Rivera

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1408496070-6252-4-git-send-email-German.Rivera@freescale.com \
    --to=german.rivera@freescale.com \
    --cc=arnd@arndb.de \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linuxppc-release@linux.freescale.net \
    --cc=stuart.yoder@freescale.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.