1/* Copyright 2013-2014 Freescale Semiconductor Inc. 2 * 3 * I/O services to send MC commands to the MC hardware 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * * Neither the name of the above-listed copyright holders nor the 13 * names of any contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * 17 * ALTERNATIVELY, this software may be distributed under the terms of the 18 * GNU General Public License ("GPL") as published by the Free Software 19 * Foundation, either version 2 of that License or (at your option) any 20 * later version. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE 26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35#include "../include/mc-sys.h" 36#include "../include/mc-cmd.h" 37#include <linux/delay.h> 38#include <linux/slab.h> 39#include <linux/ioport.h> 40#include <linux/device.h> 41 42/** 43 * Timeout in jiffies to wait for the completion of an MC command 44 */ 45#define MC_CMD_COMPLETION_TIMEOUT_JIFFIES (HZ / 2) /* 500 ms */ 46 47/* 48 * usleep_range() min and max values used to throttle down polling 49 * iterations while waiting for MC command completion 50 */ 51#define MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS 10 52#define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500 53 54#define MC_CMD_HDR_READ_CMDID(_hdr) \ 55 ((uint16_t)mc_dec((_hdr), MC_CMD_HDR_CMDID_O, MC_CMD_HDR_CMDID_S)) 56 57/** 58 * Creates an MC I/O object 59 * 60 * @dev: device to be associated with the MC I/O object 61 * @mc_portal_phys_addr: physical address of the MC portal to use 62 * @mc_portal_size: size in bytes of the MC portal 63 * @resource: Pointer to MC bus object allocator resource associated 64 * with this MC I/O object or NULL if none. 65 * @flags: flags for the new MC I/O object 66 * @new_mc_io: Area to return pointer to newly created MC I/O object 67 * 68 * Returns '0' on Success; Error code otherwise. 69 */ 70int __must_check fsl_create_mc_io(struct device *dev, 71 phys_addr_t mc_portal_phys_addr, 72 uint32_t mc_portal_size, 73 struct fsl_mc_resource *resource, 74 uint32_t flags, struct fsl_mc_io **new_mc_io) 75{ 76 struct fsl_mc_io *mc_io; 77 void __iomem *mc_portal_virt_addr; 78 struct resource *res; 79 80 mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL); 81 if (!mc_io) 82 return -ENOMEM; 83 84 mc_io->dev = dev; 85 mc_io->flags = flags; 86 mc_io->portal_phys_addr = mc_portal_phys_addr; 87 mc_io->portal_size = mc_portal_size; 88 mc_io->resource = resource; 89 res = devm_request_mem_region(dev, 90 mc_portal_phys_addr, 91 mc_portal_size, 92 "mc_portal"); 93 if (!res) { 94 dev_err(dev, 95 "devm_request_mem_region failed for MC portal %#llx\n", 96 mc_portal_phys_addr); 97 return -EBUSY; 98 } 99 100 mc_portal_virt_addr = devm_ioremap_nocache(dev, 101 mc_portal_phys_addr, 102 mc_portal_size); 103 if (!mc_portal_virt_addr) { 104 dev_err(dev, 105 "devm_ioremap_nocache failed for MC portal %#llx\n", 106 mc_portal_phys_addr); 107 return -ENXIO; 108 } 109 110 mc_io->portal_virt_addr = mc_portal_virt_addr; 111 *new_mc_io = mc_io; 112 return 0; 113} 114EXPORT_SYMBOL_GPL(fsl_create_mc_io); 115 116/** 117 * Destroys an MC I/O object 118 * 119 * @mc_io: MC I/O object to destroy 120 */ 121void fsl_destroy_mc_io(struct fsl_mc_io *mc_io) 122{ 123 devm_iounmap(mc_io->dev, mc_io->portal_virt_addr); 124 devm_release_mem_region(mc_io->dev, 125 mc_io->portal_phys_addr, 126 mc_io->portal_size); 127 128 mc_io->portal_virt_addr = NULL; 129 devm_kfree(mc_io->dev, mc_io); 130} 131EXPORT_SYMBOL_GPL(fsl_destroy_mc_io); 132 133static int mc_status_to_error(enum mc_cmd_status status) 134{ 135 static const int mc_status_to_error_map[] = { 136 [MC_CMD_STATUS_OK] = 0, 137 [MC_CMD_STATUS_AUTH_ERR] = -EACCES, 138 [MC_CMD_STATUS_NO_PRIVILEGE] = -EPERM, 139 [MC_CMD_STATUS_DMA_ERR] = -EIO, 140 [MC_CMD_STATUS_CONFIG_ERR] = -ENXIO, 141 [MC_CMD_STATUS_TIMEOUT] = -ETIMEDOUT, 142 [MC_CMD_STATUS_NO_RESOURCE] = -ENAVAIL, 143 [MC_CMD_STATUS_NO_MEMORY] = -ENOMEM, 144 [MC_CMD_STATUS_BUSY] = -EBUSY, 145 [MC_CMD_STATUS_UNSUPPORTED_OP] = -ENOTSUPP, 146 [MC_CMD_STATUS_INVALID_STATE] = -ENODEV, 147 }; 148 149 if (WARN_ON((u32)status >= ARRAY_SIZE(mc_status_to_error_map))) 150 return -EINVAL; 151 152 return mc_status_to_error_map[status]; 153} 154 155static const char *mc_status_to_string(enum mc_cmd_status status) 156{ 157 static const char *const status_strings[] = { 158 [MC_CMD_STATUS_OK] = "Command completed successfully", 159 [MC_CMD_STATUS_READY] = "Command ready to be processed", 160 [MC_CMD_STATUS_AUTH_ERR] = "Authentication error", 161 [MC_CMD_STATUS_NO_PRIVILEGE] = "No privilege", 162 [MC_CMD_STATUS_DMA_ERR] = "DMA or I/O error", 163 [MC_CMD_STATUS_CONFIG_ERR] = "Configuration error", 164 [MC_CMD_STATUS_TIMEOUT] = "Operation timed out", 165 [MC_CMD_STATUS_NO_RESOURCE] = "No resources", 166 [MC_CMD_STATUS_NO_MEMORY] = "No memory available", 167 [MC_CMD_STATUS_BUSY] = "Device is busy", 168 [MC_CMD_STATUS_UNSUPPORTED_OP] = "Unsupported operation", 169 [MC_CMD_STATUS_INVALID_STATE] = "Invalid state" 170 }; 171 172 if ((unsigned int)status >= ARRAY_SIZE(status_strings)) 173 return "Unknown MC error"; 174 175 return status_strings[status]; 176} 177 178/** 179 * mc_write_command - writes a command to a Management Complex (MC) portal 180 * 181 * @portal: pointer to an MC portal 182 * @cmd: pointer to a filled command 183 */ 184static inline void mc_write_command(struct mc_command __iomem *portal, 185 struct mc_command *cmd) 186{ 187 int i; 188 189 /* copy command parameters into the portal */ 190 for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) 191 writeq(cmd->params[i], &portal->params[i]); 192 193 /* submit the command by writing the header */ 194 writeq(cmd->header, &portal->header); 195} 196 197/** 198 * mc_read_response - reads the response for the last MC command from a 199 * Management Complex (MC) portal 200 * 201 * @portal: pointer to an MC portal 202 * @resp: pointer to command response buffer 203 * 204 * Returns MC_CMD_STATUS_OK on Success; Error code otherwise. 205 */ 206static inline enum mc_cmd_status mc_read_response(struct mc_command __iomem * 207 portal, 208 struct mc_command *resp) 209{ 210 int i; 211 enum mc_cmd_status status; 212 213 /* Copy command response header from MC portal: */ 214 resp->header = readq(&portal->header); 215 status = MC_CMD_HDR_READ_STATUS(resp->header); 216 if (status != MC_CMD_STATUS_OK) 217 return status; 218 219 /* Copy command response data from MC portal: */ 220 for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) 221 resp->params[i] = readq(&portal->params[i]); 222 223 return status; 224} 225 226/** 227 * Sends an command to the MC device using the given MC I/O object 228 * 229 * @mc_io: MC I/O object to be used 230 * @cmd: command to be sent 231 * 232 * Returns '0' on Success; Error code otherwise. 233 * 234 * NOTE: This function cannot be invoked from from atomic contexts. 235 */ 236int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd) 237{ 238 enum mc_cmd_status status; 239 unsigned long jiffies_until_timeout = 240 jiffies + MC_CMD_COMPLETION_TIMEOUT_JIFFIES; 241 242 /* 243 * Send command to the MC hardware: 244 */ 245 mc_write_command(mc_io->portal_virt_addr, cmd); 246 247 /* 248 * Wait for response from the MC hardware: 249 */ 250 for (;;) { 251 status = mc_read_response(mc_io->portal_virt_addr, cmd); 252 if (status != MC_CMD_STATUS_READY) 253 break; 254 255 /* 256 * TODO: When MC command completion interrupts are supported 257 * call wait function here instead of usleep_range() 258 */ 259 usleep_range(MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS, 260 MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); 261 262 if (time_after_eq(jiffies, jiffies_until_timeout)) { 263 pr_debug("MC command timed out (portal: %#llx, obj handle: %#x, command: %#x)\n", 264 mc_io->portal_phys_addr, 265 (unsigned int) 266 MC_CMD_HDR_READ_TOKEN(cmd->header), 267 (unsigned int) 268 MC_CMD_HDR_READ_CMDID(cmd->header)); 269 270 return -ETIMEDOUT; 271 } 272 } 273 274 if (status != MC_CMD_STATUS_OK) { 275 pr_debug("MC command failed: portal: %#llx, obj handle: %#x, command: %#x, status: %s (%#x)\n", 276 mc_io->portal_phys_addr, 277 (unsigned int)MC_CMD_HDR_READ_TOKEN(cmd->header), 278 (unsigned int)MC_CMD_HDR_READ_CMDID(cmd->header), 279 mc_status_to_string(status), 280 (unsigned int)status); 281 282 return mc_status_to_error(status); 283 } 284 285 return 0; 286} 287EXPORT_SYMBOL(mc_send_command); 288