Browse Source

staging: fsl-mc: Added serialization to mc_send_command()

When the same portal is used to call mc_send_command() from two
different threads or a thread and an interrupt handler, serialization
is required, as the MC only supports one outstanding command per MC
portal. Thus, a new command should not be sent to the MC until the
last command sent has been responded by the MC.

Signed-off-by: J. German Rivera <German.Rivera@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
J. German Rivera 9 years ago
parent
commit
63f2be5c3b
2 changed files with 51 additions and 3 deletions
  1. 22 3
      drivers/staging/fsl-mc/bus/mc-sys.c
  2. 29 0
      drivers/staging/fsl-mc/include/mc-sys.h

+ 22 - 3
drivers/staging/fsl-mc/bus/mc-sys.c

@@ -88,6 +88,11 @@ int __must_check fsl_create_mc_io(struct device *dev,
 	mc_io->flags = flags;
 	mc_io->flags = flags;
 	mc_io->portal_phys_addr = mc_portal_phys_addr;
 	mc_io->portal_phys_addr = mc_portal_phys_addr;
 	mc_io->portal_size = mc_portal_size;
 	mc_io->portal_size = mc_portal_size;
+	if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
+		spin_lock_init(&mc_io->spinlock);
+	else
+		mutex_init(&mc_io->mutex);
+
 	res = devm_request_mem_region(dev,
 	res = devm_request_mem_region(dev,
 				      mc_portal_phys_addr,
 				      mc_portal_phys_addr,
 				      mc_portal_size,
 				      mc_portal_size,
@@ -391,11 +396,17 @@ int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd)
 {
 {
 	int error;
 	int error;
 	enum mc_cmd_status status;
 	enum mc_cmd_status status;
+	unsigned long irq_flags = 0;
 
 
 	if (WARN_ON(in_irq() &&
 	if (WARN_ON(in_irq() &&
 		    !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)))
 		    !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)))
 		return -EINVAL;
 		return -EINVAL;
 
 
+	if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
+		spin_lock_irqsave(&mc_io->spinlock, irq_flags);
+	else
+		mutex_lock(&mc_io->mutex);
+
 	/*
 	/*
 	 * Send command to the MC hardware:
 	 * Send command to the MC hardware:
 	 */
 	 */
@@ -410,7 +421,7 @@ int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd)
 		error = mc_polling_wait_atomic(mc_io, cmd, &status);
 		error = mc_polling_wait_atomic(mc_io, cmd, &status);
 
 
 	if (error < 0)
 	if (error < 0)
-		return error;
+		goto common_exit;
 
 
 	if (status != MC_CMD_STATUS_OK) {
 	if (status != MC_CMD_STATUS_OK) {
 		pr_debug("MC command failed: portal: %#llx, obj handle: %#x, command: %#x, status: %s (%#x)\n",
 		pr_debug("MC command failed: portal: %#llx, obj handle: %#x, command: %#x, status: %s (%#x)\n",
@@ -420,9 +431,17 @@ int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd)
 			 mc_status_to_string(status),
 			 mc_status_to_string(status),
 			 (unsigned int)status);
 			 (unsigned int)status);
 
 
-		return mc_status_to_error(status);
+		error = mc_status_to_error(status);
+		goto common_exit;
 	}
 	}
 
 
-	return 0;
+	error = 0;
+common_exit:
+	if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
+		spin_unlock_irqrestore(&mc_io->spinlock, irq_flags);
+	else
+		mutex_unlock(&mc_io->mutex);
+
+	return error;
 }
 }
 EXPORT_SYMBOL(mc_send_command);
 EXPORT_SYMBOL(mc_send_command);

+ 29 - 0
drivers/staging/fsl-mc/include/mc-sys.h

@@ -39,6 +39,8 @@
 #include <linux/errno.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/io.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-mapping.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
 
 
 /**
 /**
  * Bit masks for a MC I/O object (struct fsl_mc_io) flags
  * Bit masks for a MC I/O object (struct fsl_mc_io) flags
@@ -56,6 +58,20 @@ struct mc_command;
  * @portal_phys_addr: MC command portal physical address
  * @portal_phys_addr: MC command portal physical address
  * @portal_virt_addr: MC command portal virtual address
  * @portal_virt_addr: MC command portal virtual address
  * @dpmcp_dev: pointer to the DPMCP device associated with the MC portal.
  * @dpmcp_dev: pointer to the DPMCP device associated with the MC portal.
+ *
+ * Fields are only meaningful if the FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is not
+ * set:
+ * @mutex: Mutex to serialize mc_send_command() calls that use the same MC
+ * portal, if the fsl_mc_io object was created with the
+ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag off. mc_send_command() calls for this
+ * fsl_mc_io object must be made only from non-atomic context.
+ *
+ * Fields are only meaningful if the FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is
+ * set:
+ * @spinlock: Spinlock to serialize mc_send_command() calls that use the same MC
+ * portal, if the fsl_mc_io object was created with the
+ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag on. mc_send_command() calls for this
+ * fsl_mc_io object can be made from atomic or non-atomic context.
  */
  */
 struct fsl_mc_io {
 struct fsl_mc_io {
 	struct device *dev;
 	struct device *dev;
@@ -64,6 +80,19 @@ struct fsl_mc_io {
 	phys_addr_t portal_phys_addr;
 	phys_addr_t portal_phys_addr;
 	void __iomem *portal_virt_addr;
 	void __iomem *portal_virt_addr;
 	struct fsl_mc_device *dpmcp_dev;
 	struct fsl_mc_device *dpmcp_dev;
+	union {
+		/*
+		 * This field is only meaningful if the
+		 * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is not set
+		 */
+		struct mutex mutex; /* serializes mc_send_command() */
+
+		/*
+		 * This field is only meaningful if the
+		 * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is set
+		 */
+		spinlock_t spinlock;	/* serializes mc_send_command() */
+	};
 };
 };
 
 
 int __must_check fsl_create_mc_io(struct device *dev,
 int __must_check fsl_create_mc_io(struct device *dev,