root/drivers/bus/fsl-mc/mc-sys.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mc_cmd_hdr_read_status
  2. mc_cmd_hdr_read_cmdid
  3. mc_status_to_error
  4. mc_status_to_string
  5. mc_write_command
  6. mc_read_response
  7. mc_polling_wait_preemptible
  8. mc_polling_wait_atomic
  9. mc_send_command

   1 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
   2 /*
   3  * Copyright 2013-2016 Freescale Semiconductor Inc.
   4  *
   5  * I/O services to send MC commands to the MC hardware
   6  *
   7  */
   8 
   9 #include <linux/delay.h>
  10 #include <linux/slab.h>
  11 #include <linux/ioport.h>
  12 #include <linux/device.h>
  13 #include <linux/io.h>
  14 #include <linux/io-64-nonatomic-hi-lo.h>
  15 #include <linux/fsl/mc.h>
  16 
  17 #include "fsl-mc-private.h"
  18 
  19 /**
  20  * Timeout in milliseconds to wait for the completion of an MC command
  21  */
  22 #define MC_CMD_COMPLETION_TIMEOUT_MS    500
  23 
  24 /*
  25  * usleep_range() min and max values used to throttle down polling
  26  * iterations while waiting for MC command completion
  27  */
  28 #define MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS    10
  29 #define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS    500
  30 
  31 static enum mc_cmd_status mc_cmd_hdr_read_status(struct fsl_mc_command *cmd)
  32 {
  33         struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header;
  34 
  35         return (enum mc_cmd_status)hdr->status;
  36 }
  37 
  38 static u16 mc_cmd_hdr_read_cmdid(struct fsl_mc_command *cmd)
  39 {
  40         struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header;
  41         u16 cmd_id = le16_to_cpu(hdr->cmd_id);
  42 
  43         return cmd_id;
  44 }
  45 
  46 static int mc_status_to_error(enum mc_cmd_status status)
  47 {
  48         static const int mc_status_to_error_map[] = {
  49                 [MC_CMD_STATUS_OK] = 0,
  50                 [MC_CMD_STATUS_AUTH_ERR] = -EACCES,
  51                 [MC_CMD_STATUS_NO_PRIVILEGE] = -EPERM,
  52                 [MC_CMD_STATUS_DMA_ERR] = -EIO,
  53                 [MC_CMD_STATUS_CONFIG_ERR] = -ENXIO,
  54                 [MC_CMD_STATUS_TIMEOUT] = -ETIMEDOUT,
  55                 [MC_CMD_STATUS_NO_RESOURCE] = -ENAVAIL,
  56                 [MC_CMD_STATUS_NO_MEMORY] = -ENOMEM,
  57                 [MC_CMD_STATUS_BUSY] = -EBUSY,
  58                 [MC_CMD_STATUS_UNSUPPORTED_OP] = -ENOTSUPP,
  59                 [MC_CMD_STATUS_INVALID_STATE] = -ENODEV,
  60         };
  61 
  62         if ((u32)status >= ARRAY_SIZE(mc_status_to_error_map))
  63                 return -EINVAL;
  64 
  65         return mc_status_to_error_map[status];
  66 }
  67 
  68 static const char *mc_status_to_string(enum mc_cmd_status status)
  69 {
  70         static const char *const status_strings[] = {
  71                 [MC_CMD_STATUS_OK] = "Command completed successfully",
  72                 [MC_CMD_STATUS_READY] = "Command ready to be processed",
  73                 [MC_CMD_STATUS_AUTH_ERR] = "Authentication error",
  74                 [MC_CMD_STATUS_NO_PRIVILEGE] = "No privilege",
  75                 [MC_CMD_STATUS_DMA_ERR] = "DMA or I/O error",
  76                 [MC_CMD_STATUS_CONFIG_ERR] = "Configuration error",
  77                 [MC_CMD_STATUS_TIMEOUT] = "Operation timed out",
  78                 [MC_CMD_STATUS_NO_RESOURCE] = "No resources",
  79                 [MC_CMD_STATUS_NO_MEMORY] = "No memory available",
  80                 [MC_CMD_STATUS_BUSY] = "Device is busy",
  81                 [MC_CMD_STATUS_UNSUPPORTED_OP] = "Unsupported operation",
  82                 [MC_CMD_STATUS_INVALID_STATE] = "Invalid state"
  83         };
  84 
  85         if ((unsigned int)status >= ARRAY_SIZE(status_strings))
  86                 return "Unknown MC error";
  87 
  88         return status_strings[status];
  89 }
  90 
  91 /**
  92  * mc_write_command - writes a command to a Management Complex (MC) portal
  93  *
  94  * @portal: pointer to an MC portal
  95  * @cmd: pointer to a filled command
  96  */
  97 static inline void mc_write_command(struct fsl_mc_command __iomem *portal,
  98                                     struct fsl_mc_command *cmd)
  99 {
 100         int i;
 101 
 102         /* copy command parameters into the portal */
 103         for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++)
 104                 /*
 105                  * Data is already in the expected LE byte-order. Do an
 106                  * extra LE -> CPU conversion so that the CPU -> LE done in
 107                  * the device io write api puts it back in the right order.
 108                  */
 109                 writeq_relaxed(le64_to_cpu(cmd->params[i]), &portal->params[i]);
 110 
 111         /* submit the command by writing the header */
 112         writeq(le64_to_cpu(cmd->header), &portal->header);
 113 }
 114 
 115 /**
 116  * mc_read_response - reads the response for the last MC command from a
 117  * Management Complex (MC) portal
 118  *
 119  * @portal: pointer to an MC portal
 120  * @resp: pointer to command response buffer
 121  *
 122  * Returns MC_CMD_STATUS_OK on Success; Error code otherwise.
 123  */
 124 static inline enum mc_cmd_status mc_read_response(struct fsl_mc_command __iomem
 125                                                   *portal,
 126                                                   struct fsl_mc_command *resp)
 127 {
 128         int i;
 129         enum mc_cmd_status status;
 130 
 131         /* Copy command response header from MC portal: */
 132         resp->header = cpu_to_le64(readq_relaxed(&portal->header));
 133         status = mc_cmd_hdr_read_status(resp);
 134         if (status != MC_CMD_STATUS_OK)
 135                 return status;
 136 
 137         /* Copy command response data from MC portal: */
 138         for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++)
 139                 /*
 140                  * Data is expected to be in LE byte-order. Do an
 141                  * extra CPU -> LE to revert the LE -> CPU done in
 142                  * the device io read api.
 143                  */
 144                 resp->params[i] =
 145                         cpu_to_le64(readq_relaxed(&portal->params[i]));
 146 
 147         return status;
 148 }
 149 
 150 /**
 151  * Waits for the completion of an MC command doing preemptible polling.
 152  * uslepp_range() is called between polling iterations.
 153  *
 154  * @mc_io: MC I/O object to be used
 155  * @cmd: command buffer to receive MC response
 156  * @mc_status: MC command completion status
 157  */
 158 static int mc_polling_wait_preemptible(struct fsl_mc_io *mc_io,
 159                                        struct fsl_mc_command *cmd,
 160                                        enum mc_cmd_status *mc_status)
 161 {
 162         enum mc_cmd_status status;
 163         unsigned long jiffies_until_timeout =
 164                 jiffies + msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS);
 165 
 166         /*
 167          * Wait for response from the MC hardware:
 168          */
 169         for (;;) {
 170                 status = mc_read_response(mc_io->portal_virt_addr, cmd);
 171                 if (status != MC_CMD_STATUS_READY)
 172                         break;
 173 
 174                 /*
 175                  * TODO: When MC command completion interrupts are supported
 176                  * call wait function here instead of usleep_range()
 177                  */
 178                 usleep_range(MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS,
 179                              MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS);
 180 
 181                 if (time_after_eq(jiffies, jiffies_until_timeout)) {
 182                         dev_dbg(mc_io->dev,
 183                                 "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n",
 184                                  &mc_io->portal_phys_addr,
 185                                  (unsigned int)mc_cmd_hdr_read_token(cmd),
 186                                  (unsigned int)mc_cmd_hdr_read_cmdid(cmd));
 187 
 188                         return -ETIMEDOUT;
 189                 }
 190         }
 191 
 192         *mc_status = status;
 193         return 0;
 194 }
 195 
 196 /**
 197  * Waits for the completion of an MC command doing atomic polling.
 198  * udelay() is called between polling iterations.
 199  *
 200  * @mc_io: MC I/O object to be used
 201  * @cmd: command buffer to receive MC response
 202  * @mc_status: MC command completion status
 203  */
 204 static int mc_polling_wait_atomic(struct fsl_mc_io *mc_io,
 205                                   struct fsl_mc_command *cmd,
 206                                   enum mc_cmd_status *mc_status)
 207 {
 208         enum mc_cmd_status status;
 209         unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000;
 210 
 211         BUILD_BUG_ON((MC_CMD_COMPLETION_TIMEOUT_MS * 1000) %
 212                      MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS != 0);
 213 
 214         for (;;) {
 215                 status = mc_read_response(mc_io->portal_virt_addr, cmd);
 216                 if (status != MC_CMD_STATUS_READY)
 217                         break;
 218 
 219                 udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS);
 220                 timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS;
 221                 if (timeout_usecs == 0) {
 222                         dev_dbg(mc_io->dev,
 223                                 "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n",
 224                                  &mc_io->portal_phys_addr,
 225                                  (unsigned int)mc_cmd_hdr_read_token(cmd),
 226                                  (unsigned int)mc_cmd_hdr_read_cmdid(cmd));
 227 
 228                         return -ETIMEDOUT;
 229                 }
 230         }
 231 
 232         *mc_status = status;
 233         return 0;
 234 }
 235 
 236 /**
 237  * Sends a command to the MC device using the given MC I/O object
 238  *
 239  * @mc_io: MC I/O object to be used
 240  * @cmd: command to be sent
 241  *
 242  * Returns '0' on Success; Error code otherwise.
 243  */
 244 int mc_send_command(struct fsl_mc_io *mc_io, struct fsl_mc_command *cmd)
 245 {
 246         int error;
 247         enum mc_cmd_status status;
 248         unsigned long irq_flags = 0;
 249 
 250         if (in_irq() && !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL))
 251                 return -EINVAL;
 252 
 253         if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
 254                 spin_lock_irqsave(&mc_io->spinlock, irq_flags);
 255         else
 256                 mutex_lock(&mc_io->mutex);
 257 
 258         /*
 259          * Send command to the MC hardware:
 260          */
 261         mc_write_command(mc_io->portal_virt_addr, cmd);
 262 
 263         /*
 264          * Wait for response from the MC hardware:
 265          */
 266         if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL))
 267                 error = mc_polling_wait_preemptible(mc_io, cmd, &status);
 268         else
 269                 error = mc_polling_wait_atomic(mc_io, cmd, &status);
 270 
 271         if (error < 0)
 272                 goto common_exit;
 273 
 274         if (status != MC_CMD_STATUS_OK) {
 275                 dev_dbg(mc_io->dev,
 276                         "MC command failed: portal: %pa, dprc handle: %#x, command: %#x, status: %s (%#x)\n",
 277                          &mc_io->portal_phys_addr,
 278                          (unsigned int)mc_cmd_hdr_read_token(cmd),
 279                          (unsigned int)mc_cmd_hdr_read_cmdid(cmd),
 280                          mc_status_to_string(status),
 281                          (unsigned int)status);
 282 
 283                 error = mc_status_to_error(status);
 284                 goto common_exit;
 285         }
 286 
 287         error = 0;
 288 common_exit:
 289         if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
 290                 spin_unlock_irqrestore(&mc_io->spinlock, irq_flags);
 291         else
 292                 mutex_unlock(&mc_io->mutex);
 293 
 294         return error;
 295 }
 296 EXPORT_SYMBOL_GPL(mc_send_command);

/* [<][>][^][v][top][bottom][index][help] */