18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2013-2016 Freescale Semiconductor Inc. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * I/O services to send MC commands to the MC hardware 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/ioport.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/io-64-nonatomic-hi-lo.h> 158c2ecf20Sopenharmony_ci#include <linux/fsl/mc.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "fsl-mc-private.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/** 208c2ecf20Sopenharmony_ci * Timeout in milliseconds to wait for the completion of an MC command 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci#define MC_CMD_COMPLETION_TIMEOUT_MS 500 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * usleep_range() min and max values used to throttle down polling 268c2ecf20Sopenharmony_ci * iterations while waiting for MC command completion 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci#define MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS 10 298c2ecf20Sopenharmony_ci#define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic enum mc_cmd_status mc_cmd_hdr_read_status(struct fsl_mc_command *cmd) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci return (enum mc_cmd_status)hdr->status; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic u16 mc_cmd_hdr_read_cmdid(struct fsl_mc_command *cmd) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header; 418c2ecf20Sopenharmony_ci u16 cmd_id = le16_to_cpu(hdr->cmd_id); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci return cmd_id; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int mc_status_to_error(enum mc_cmd_status status) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci static const int mc_status_to_error_map[] = { 498c2ecf20Sopenharmony_ci [MC_CMD_STATUS_OK] = 0, 508c2ecf20Sopenharmony_ci [MC_CMD_STATUS_AUTH_ERR] = -EACCES, 518c2ecf20Sopenharmony_ci [MC_CMD_STATUS_NO_PRIVILEGE] = -EPERM, 528c2ecf20Sopenharmony_ci [MC_CMD_STATUS_DMA_ERR] = -EIO, 538c2ecf20Sopenharmony_ci [MC_CMD_STATUS_CONFIG_ERR] = -ENXIO, 548c2ecf20Sopenharmony_ci [MC_CMD_STATUS_TIMEOUT] = -ETIMEDOUT, 558c2ecf20Sopenharmony_ci [MC_CMD_STATUS_NO_RESOURCE] = -ENAVAIL, 568c2ecf20Sopenharmony_ci [MC_CMD_STATUS_NO_MEMORY] = -ENOMEM, 578c2ecf20Sopenharmony_ci [MC_CMD_STATUS_BUSY] = -EBUSY, 588c2ecf20Sopenharmony_ci [MC_CMD_STATUS_UNSUPPORTED_OP] = -ENOTSUPP, 598c2ecf20Sopenharmony_ci [MC_CMD_STATUS_INVALID_STATE] = -ENODEV, 608c2ecf20Sopenharmony_ci }; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if ((u32)status >= ARRAY_SIZE(mc_status_to_error_map)) 638c2ecf20Sopenharmony_ci return -EINVAL; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return mc_status_to_error_map[status]; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic const char *mc_status_to_string(enum mc_cmd_status status) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci static const char *const status_strings[] = { 718c2ecf20Sopenharmony_ci [MC_CMD_STATUS_OK] = "Command completed successfully", 728c2ecf20Sopenharmony_ci [MC_CMD_STATUS_READY] = "Command ready to be processed", 738c2ecf20Sopenharmony_ci [MC_CMD_STATUS_AUTH_ERR] = "Authentication error", 748c2ecf20Sopenharmony_ci [MC_CMD_STATUS_NO_PRIVILEGE] = "No privilege", 758c2ecf20Sopenharmony_ci [MC_CMD_STATUS_DMA_ERR] = "DMA or I/O error", 768c2ecf20Sopenharmony_ci [MC_CMD_STATUS_CONFIG_ERR] = "Configuration error", 778c2ecf20Sopenharmony_ci [MC_CMD_STATUS_TIMEOUT] = "Operation timed out", 788c2ecf20Sopenharmony_ci [MC_CMD_STATUS_NO_RESOURCE] = "No resources", 798c2ecf20Sopenharmony_ci [MC_CMD_STATUS_NO_MEMORY] = "No memory available", 808c2ecf20Sopenharmony_ci [MC_CMD_STATUS_BUSY] = "Device is busy", 818c2ecf20Sopenharmony_ci [MC_CMD_STATUS_UNSUPPORTED_OP] = "Unsupported operation", 828c2ecf20Sopenharmony_ci [MC_CMD_STATUS_INVALID_STATE] = "Invalid state" 838c2ecf20Sopenharmony_ci }; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if ((unsigned int)status >= ARRAY_SIZE(status_strings)) 868c2ecf20Sopenharmony_ci return "Unknown MC error"; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return status_strings[status]; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/** 928c2ecf20Sopenharmony_ci * mc_write_command - writes a command to a Management Complex (MC) portal 938c2ecf20Sopenharmony_ci * 948c2ecf20Sopenharmony_ci * @portal: pointer to an MC portal 958c2ecf20Sopenharmony_ci * @cmd: pointer to a filled command 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistatic inline void mc_write_command(struct fsl_mc_command __iomem *portal, 988c2ecf20Sopenharmony_ci struct fsl_mc_command *cmd) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci int i; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* copy command parameters into the portal */ 1038c2ecf20Sopenharmony_ci for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) 1048c2ecf20Sopenharmony_ci /* 1058c2ecf20Sopenharmony_ci * Data is already in the expected LE byte-order. Do an 1068c2ecf20Sopenharmony_ci * extra LE -> CPU conversion so that the CPU -> LE done in 1078c2ecf20Sopenharmony_ci * the device io write api puts it back in the right order. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci writeq_relaxed(le64_to_cpu(cmd->params[i]), &portal->params[i]); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* submit the command by writing the header */ 1128c2ecf20Sopenharmony_ci writeq(le64_to_cpu(cmd->header), &portal->header); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/** 1168c2ecf20Sopenharmony_ci * mc_read_response - reads the response for the last MC command from a 1178c2ecf20Sopenharmony_ci * Management Complex (MC) portal 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * @portal: pointer to an MC portal 1208c2ecf20Sopenharmony_ci * @resp: pointer to command response buffer 1218c2ecf20Sopenharmony_ci * 1228c2ecf20Sopenharmony_ci * Returns MC_CMD_STATUS_OK on Success; Error code otherwise. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_cistatic inline enum mc_cmd_status mc_read_response(struct fsl_mc_command __iomem 1258c2ecf20Sopenharmony_ci *portal, 1268c2ecf20Sopenharmony_ci struct fsl_mc_command *resp) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci int i; 1298c2ecf20Sopenharmony_ci enum mc_cmd_status status; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Copy command response header from MC portal: */ 1328c2ecf20Sopenharmony_ci resp->header = cpu_to_le64(readq_relaxed(&portal->header)); 1338c2ecf20Sopenharmony_ci status = mc_cmd_hdr_read_status(resp); 1348c2ecf20Sopenharmony_ci if (status != MC_CMD_STATUS_OK) 1358c2ecf20Sopenharmony_ci return status; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Copy command response data from MC portal: */ 1388c2ecf20Sopenharmony_ci for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) 1398c2ecf20Sopenharmony_ci /* 1408c2ecf20Sopenharmony_ci * Data is expected to be in LE byte-order. Do an 1418c2ecf20Sopenharmony_ci * extra CPU -> LE to revert the LE -> CPU done in 1428c2ecf20Sopenharmony_ci * the device io read api. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci resp->params[i] = 1458c2ecf20Sopenharmony_ci cpu_to_le64(readq_relaxed(&portal->params[i])); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return status; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/** 1518c2ecf20Sopenharmony_ci * Waits for the completion of an MC command doing preemptible polling. 1528c2ecf20Sopenharmony_ci * uslepp_range() is called between polling iterations. 1538c2ecf20Sopenharmony_ci * 1548c2ecf20Sopenharmony_ci * @mc_io: MC I/O object to be used 1558c2ecf20Sopenharmony_ci * @cmd: command buffer to receive MC response 1568c2ecf20Sopenharmony_ci * @mc_status: MC command completion status 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_cistatic int mc_polling_wait_preemptible(struct fsl_mc_io *mc_io, 1598c2ecf20Sopenharmony_ci struct fsl_mc_command *cmd, 1608c2ecf20Sopenharmony_ci enum mc_cmd_status *mc_status) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci enum mc_cmd_status status; 1638c2ecf20Sopenharmony_ci unsigned long jiffies_until_timeout = 1648c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* 1678c2ecf20Sopenharmony_ci * Wait for response from the MC hardware: 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ci for (;;) { 1708c2ecf20Sopenharmony_ci status = mc_read_response(mc_io->portal_virt_addr, cmd); 1718c2ecf20Sopenharmony_ci if (status != MC_CMD_STATUS_READY) 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* 1758c2ecf20Sopenharmony_ci * TODO: When MC command completion interrupts are supported 1768c2ecf20Sopenharmony_ci * call wait function here instead of usleep_range() 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_ci usleep_range(MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS, 1798c2ecf20Sopenharmony_ci MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, jiffies_until_timeout)) { 1828c2ecf20Sopenharmony_ci dev_dbg(mc_io->dev, 1838c2ecf20Sopenharmony_ci "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n", 1848c2ecf20Sopenharmony_ci &mc_io->portal_phys_addr, 1858c2ecf20Sopenharmony_ci (unsigned int)mc_cmd_hdr_read_token(cmd), 1868c2ecf20Sopenharmony_ci (unsigned int)mc_cmd_hdr_read_cmdid(cmd)); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci *mc_status = status; 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/** 1978c2ecf20Sopenharmony_ci * Waits for the completion of an MC command doing atomic polling. 1988c2ecf20Sopenharmony_ci * udelay() is called between polling iterations. 1998c2ecf20Sopenharmony_ci * 2008c2ecf20Sopenharmony_ci * @mc_io: MC I/O object to be used 2018c2ecf20Sopenharmony_ci * @cmd: command buffer to receive MC response 2028c2ecf20Sopenharmony_ci * @mc_status: MC command completion status 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_cistatic int mc_polling_wait_atomic(struct fsl_mc_io *mc_io, 2058c2ecf20Sopenharmony_ci struct fsl_mc_command *cmd, 2068c2ecf20Sopenharmony_ci enum mc_cmd_status *mc_status) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci enum mc_cmd_status status; 2098c2ecf20Sopenharmony_ci unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci BUILD_BUG_ON((MC_CMD_COMPLETION_TIMEOUT_MS * 1000) % 2128c2ecf20Sopenharmony_ci MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS != 0); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci for (;;) { 2158c2ecf20Sopenharmony_ci status = mc_read_response(mc_io->portal_virt_addr, cmd); 2168c2ecf20Sopenharmony_ci if (status != MC_CMD_STATUS_READY) 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); 2208c2ecf20Sopenharmony_ci timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS; 2218c2ecf20Sopenharmony_ci if (timeout_usecs == 0) { 2228c2ecf20Sopenharmony_ci dev_dbg(mc_io->dev, 2238c2ecf20Sopenharmony_ci "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n", 2248c2ecf20Sopenharmony_ci &mc_io->portal_phys_addr, 2258c2ecf20Sopenharmony_ci (unsigned int)mc_cmd_hdr_read_token(cmd), 2268c2ecf20Sopenharmony_ci (unsigned int)mc_cmd_hdr_read_cmdid(cmd)); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci *mc_status = status; 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/** 2378c2ecf20Sopenharmony_ci * Sends a command to the MC device using the given MC I/O object 2388c2ecf20Sopenharmony_ci * 2398c2ecf20Sopenharmony_ci * @mc_io: MC I/O object to be used 2408c2ecf20Sopenharmony_ci * @cmd: command to be sent 2418c2ecf20Sopenharmony_ci * 2428c2ecf20Sopenharmony_ci * Returns '0' on Success; Error code otherwise. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ciint mc_send_command(struct fsl_mc_io *mc_io, struct fsl_mc_command *cmd) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci int error; 2478c2ecf20Sopenharmony_ci enum mc_cmd_status status; 2488c2ecf20Sopenharmony_ci unsigned long irq_flags = 0; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (in_irq() && !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) 2518c2ecf20Sopenharmony_ci return -EINVAL; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) 2548c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&mc_io->spinlock, irq_flags); 2558c2ecf20Sopenharmony_ci else 2568c2ecf20Sopenharmony_ci mutex_lock(&mc_io->mutex); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* 2598c2ecf20Sopenharmony_ci * Send command to the MC hardware: 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_ci mc_write_command(mc_io->portal_virt_addr, cmd); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* 2648c2ecf20Sopenharmony_ci * Wait for response from the MC hardware: 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) 2678c2ecf20Sopenharmony_ci error = mc_polling_wait_preemptible(mc_io, cmd, &status); 2688c2ecf20Sopenharmony_ci else 2698c2ecf20Sopenharmony_ci error = mc_polling_wait_atomic(mc_io, cmd, &status); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (error < 0) 2728c2ecf20Sopenharmony_ci goto common_exit; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (status != MC_CMD_STATUS_OK) { 2758c2ecf20Sopenharmony_ci dev_dbg(mc_io->dev, 2768c2ecf20Sopenharmony_ci "MC command failed: portal: %pa, dprc handle: %#x, command: %#x, status: %s (%#x)\n", 2778c2ecf20Sopenharmony_ci &mc_io->portal_phys_addr, 2788c2ecf20Sopenharmony_ci (unsigned int)mc_cmd_hdr_read_token(cmd), 2798c2ecf20Sopenharmony_ci (unsigned int)mc_cmd_hdr_read_cmdid(cmd), 2808c2ecf20Sopenharmony_ci mc_status_to_string(status), 2818c2ecf20Sopenharmony_ci (unsigned int)status); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci error = mc_status_to_error(status); 2848c2ecf20Sopenharmony_ci goto common_exit; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci error = 0; 2888c2ecf20Sopenharmony_cicommon_exit: 2898c2ecf20Sopenharmony_ci if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) 2908c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&mc_io->spinlock, irq_flags); 2918c2ecf20Sopenharmony_ci else 2928c2ecf20Sopenharmony_ci mutex_unlock(&mc_io->mutex); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return error; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mc_send_command); 297