These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / staging / fsl-mc / bus / mc-sys.c
index 5737f59..6e14892 100644 (file)
 
 #include "../include/mc-sys.h"
 #include "../include/mc-cmd.h"
+#include "../include/mc.h"
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/ioport.h>
 #include <linux/device.h>
+#include "dpmcp.h"
 
 /**
- * Timeout in jiffies to wait for the completion of an MC command
+ * Timeout in milliseconds to wait for the completion of an MC command
  */
-#define MC_CMD_COMPLETION_TIMEOUT_JIFFIES   (HZ / 2)   /* 500 ms */
+#define MC_CMD_COMPLETION_TIMEOUT_MS   500
 
 /*
  * usleep_range() min and max values used to throttle down polling
@@ -52,7 +54,7 @@
 #define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS    500
 
 #define MC_CMD_HDR_READ_CMDID(_hdr) \
-       ((uint16_t)mc_dec((_hdr), MC_CMD_HDR_CMDID_O, MC_CMD_HDR_CMDID_S))
+       ((u16)mc_dec((_hdr), MC_CMD_HDR_CMDID_O, MC_CMD_HDR_CMDID_S))
 
 /**
  * Creates an MC I/O object
@@ -60,8 +62,8 @@
  * @dev: device to be associated with the MC I/O object
  * @mc_portal_phys_addr: physical address of the MC portal to use
  * @mc_portal_size: size in bytes of the MC portal
- * @resource: Pointer to MC bus object allocator resource associated
- * with this MC I/O object or NULL if none.
+ * @dpmcp-dev: Pointer to the DPMCP object associated with this MC I/O
+ * object or NULL if none.
  * @flags: flags for the new MC I/O object
  * @new_mc_io: Area to return pointer to newly created MC I/O object
  *
  */
 int __must_check fsl_create_mc_io(struct device *dev,
                                  phys_addr_t mc_portal_phys_addr,
-                                 uint32_t mc_portal_size,
-                                 struct fsl_mc_resource *resource,
-                                 uint32_t flags, struct fsl_mc_io **new_mc_io)
+                                 u32 mc_portal_size,
+                                 struct fsl_mc_device *dpmcp_dev,
+                                 u32 flags, struct fsl_mc_io **new_mc_io)
 {
+       int error;
        struct fsl_mc_io *mc_io;
        void __iomem *mc_portal_virt_addr;
        struct resource *res;
@@ -85,7 +88,11 @@ int __must_check fsl_create_mc_io(struct device *dev,
        mc_io->flags = flags;
        mc_io->portal_phys_addr = mc_portal_phys_addr;
        mc_io->portal_size = mc_portal_size;
-       mc_io->resource = resource;
+       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,
                                      mc_portal_phys_addr,
                                      mc_portal_size,
@@ -108,8 +115,18 @@ int __must_check fsl_create_mc_io(struct device *dev,
        }
 
        mc_io->portal_virt_addr = mc_portal_virt_addr;
+       if (dpmcp_dev) {
+               error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev);
+               if (error < 0)
+                       goto error_destroy_mc_io;
+       }
+
        *new_mc_io = mc_io;
        return 0;
+
+error_destroy_mc_io:
+       fsl_destroy_mc_io(mc_io);
+       return error;
 }
 EXPORT_SYMBOL_GPL(fsl_create_mc_io);
 
@@ -120,6 +137,11 @@ EXPORT_SYMBOL_GPL(fsl_create_mc_io);
  */
 void fsl_destroy_mc_io(struct fsl_mc_io *mc_io)
 {
+       struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
+
+       if (dpmcp_dev)
+               fsl_mc_io_unset_dpmcp(mc_io);
+
        devm_iounmap(mc_io->dev, mc_io->portal_virt_addr);
        devm_release_mem_region(mc_io->dev,
                                mc_io->portal_phys_addr,
@@ -130,6 +152,57 @@ void fsl_destroy_mc_io(struct fsl_mc_io *mc_io)
 }
 EXPORT_SYMBOL_GPL(fsl_destroy_mc_io);
 
+int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io,
+                       struct fsl_mc_device *dpmcp_dev)
+{
+       int error;
+
+       if (WARN_ON(!dpmcp_dev))
+               return -EINVAL;
+
+       if (WARN_ON(mc_io->dpmcp_dev))
+               return -EINVAL;
+
+       if (WARN_ON(dpmcp_dev->mc_io))
+               return -EINVAL;
+
+       error = dpmcp_open(mc_io,
+                          0,
+                          dpmcp_dev->obj_desc.id,
+                          &dpmcp_dev->mc_handle);
+       if (error < 0)
+               return error;
+
+       mc_io->dpmcp_dev = dpmcp_dev;
+       dpmcp_dev->mc_io = mc_io;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_io_set_dpmcp);
+
+void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io)
+{
+       int error;
+       struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
+
+       if (WARN_ON(!dpmcp_dev))
+               return;
+
+       if (WARN_ON(dpmcp_dev->mc_io != mc_io))
+               return;
+
+       error = dpmcp_close(mc_io,
+                           0,
+                           dpmcp_dev->mc_handle);
+       if (error < 0) {
+               dev_err(&dpmcp_dev->dev, "dpmcp_close() failed: %d\n",
+                       error);
+       }
+
+       mc_io->dpmcp_dev = NULL;
+       dpmcp_dev->mc_io = NULL;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_io_unset_dpmcp);
+
 static int mc_status_to_error(enum mc_cmd_status status)
 {
        static const int mc_status_to_error_map[] = {
@@ -224,25 +297,20 @@ static inline enum mc_cmd_status mc_read_response(struct mc_command __iomem *
 }
 
 /**
- * Sends an command to the MC device using the given MC I/O object
+ * Waits for the completion of an MC command doing preemptible polling.
+ * uslepp_range() is called between polling iterations.
  *
  * @mc_io: MC I/O object to be used
- * @cmd: command to be sent
- *
- * Returns '0' on Success; Error code otherwise.
- *
- * NOTE: This function cannot be invoked from from atomic contexts.
+ * @cmd: command buffer to receive MC response
+ * @mc_status: MC command completion status
  */
-int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd)
+static int mc_polling_wait_preemptible(struct fsl_mc_io *mc_io,
+                                      struct mc_command *cmd,
+                                      enum mc_cmd_status *mc_status)
 {
        enum mc_cmd_status status;
        unsigned long jiffies_until_timeout =
-           jiffies + MC_CMD_COMPLETION_TIMEOUT_JIFFIES;
-
-       /*
-        * Send command to the MC hardware:
-        */
-       mc_write_command(mc_io->portal_virt_addr, cmd);
+               jiffies + msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS);
 
        /*
         * Wait for response from the MC hardware:
@@ -271,6 +339,90 @@ int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd)
                }
        }
 
+       *mc_status = status;
+       return 0;
+}
+
+/**
+ * Waits for the completion of an MC command doing atomic polling.
+ * udelay() is called between polling iterations.
+ *
+ * @mc_io: MC I/O object to be used
+ * @cmd: command buffer to receive MC response
+ * @mc_status: MC command completion status
+ */
+static int mc_polling_wait_atomic(struct fsl_mc_io *mc_io,
+                                 struct mc_command *cmd,
+                                 enum mc_cmd_status *mc_status)
+{
+       enum mc_cmd_status status;
+       unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000;
+
+       BUILD_BUG_ON((MC_CMD_COMPLETION_TIMEOUT_MS * 1000) %
+                    MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS != 0);
+
+       for (;;) {
+               status = mc_read_response(mc_io->portal_virt_addr, cmd);
+               if (status != MC_CMD_STATUS_READY)
+                       break;
+
+               udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS);
+               timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS;
+               if (timeout_usecs == 0) {
+                       pr_debug("MC command timed out (portal: %#llx, obj handle: %#x, command: %#x)\n",
+                                mc_io->portal_phys_addr,
+                                (unsigned int)
+                                       MC_CMD_HDR_READ_TOKEN(cmd->header),
+                                (unsigned int)
+                                       MC_CMD_HDR_READ_CMDID(cmd->header));
+
+                       return -ETIMEDOUT;
+               }
+       }
+
+       *mc_status = status;
+       return 0;
+}
+
+/**
+ * Sends a command to the MC device using the given MC I/O object
+ *
+ * @mc_io: MC I/O object to be used
+ * @cmd: command to be sent
+ *
+ * Returns '0' on Success; Error code otherwise.
+ */
+int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd)
+{
+       int error;
+       enum mc_cmd_status status;
+       unsigned long irq_flags = 0;
+
+       if (WARN_ON(in_irq() &&
+                   !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)))
+               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:
+        */
+       mc_write_command(mc_io->portal_virt_addr, cmd);
+
+       /*
+        * Wait for response from the MC hardware:
+        */
+       if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL))
+               error = mc_polling_wait_preemptible(mc_io, cmd, &status);
+       else
+               error = mc_polling_wait_atomic(mc_io, cmd, &status);
+
+       if (error < 0)
+               goto common_exit;
+
        if (status != MC_CMD_STATUS_OK) {
                pr_debug("MC command failed: portal: %#llx, obj handle: %#x, command: %#x, status: %s (%#x)\n",
                         mc_io->portal_phys_addr,
@@ -279,9 +431,17 @@ int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd)
                         mc_status_to_string(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);