18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PowerNV OPAL asynchronous completion interfaces 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2013-2017 IBM Corp. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#undef DEBUG 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/sched.h> 148c2ecf20Sopenharmony_ci#include <linux/semaphore.h> 158c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 168c2ecf20Sopenharmony_ci#include <linux/wait.h> 178c2ecf20Sopenharmony_ci#include <linux/gfp.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <asm/machdep.h> 208c2ecf20Sopenharmony_ci#include <asm/opal.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cienum opal_async_token_state { 238c2ecf20Sopenharmony_ci ASYNC_TOKEN_UNALLOCATED = 0, 248c2ecf20Sopenharmony_ci ASYNC_TOKEN_ALLOCATED, 258c2ecf20Sopenharmony_ci ASYNC_TOKEN_DISPATCHED, 268c2ecf20Sopenharmony_ci ASYNC_TOKEN_ABANDONED, 278c2ecf20Sopenharmony_ci ASYNC_TOKEN_COMPLETED 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct opal_async_token { 318c2ecf20Sopenharmony_ci enum opal_async_token_state state; 328c2ecf20Sopenharmony_ci struct opal_msg response; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(opal_async_wait); 368c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(opal_async_comp_lock); 378c2ecf20Sopenharmony_cistatic struct semaphore opal_async_sem; 388c2ecf20Sopenharmony_cistatic unsigned int opal_max_async_tokens; 398c2ecf20Sopenharmony_cistatic struct opal_async_token *opal_async_tokens; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int __opal_async_get_token(void) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci unsigned long flags; 448c2ecf20Sopenharmony_ci int i, token = -EBUSY; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci spin_lock_irqsave(&opal_async_comp_lock, flags); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci for (i = 0; i < opal_max_async_tokens; i++) { 498c2ecf20Sopenharmony_ci if (opal_async_tokens[i].state == ASYNC_TOKEN_UNALLOCATED) { 508c2ecf20Sopenharmony_ci opal_async_tokens[i].state = ASYNC_TOKEN_ALLOCATED; 518c2ecf20Sopenharmony_ci token = i; 528c2ecf20Sopenharmony_ci break; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opal_async_comp_lock, flags); 578c2ecf20Sopenharmony_ci return token; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Note: If the returned token is used in an opal call and opal returns 628c2ecf20Sopenharmony_ci * OPAL_ASYNC_COMPLETION you MUST call one of opal_async_wait_response() or 638c2ecf20Sopenharmony_ci * opal_async_wait_response_interruptible() at least once before calling another 648c2ecf20Sopenharmony_ci * opal_async_* function 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ciint opal_async_get_token_interruptible(void) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci int token; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* Wait until a token is available */ 718c2ecf20Sopenharmony_ci if (down_interruptible(&opal_async_sem)) 728c2ecf20Sopenharmony_ci return -ERESTARTSYS; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci token = __opal_async_get_token(); 758c2ecf20Sopenharmony_ci if (token < 0) 768c2ecf20Sopenharmony_ci up(&opal_async_sem); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return token; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_async_get_token_interruptible); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int __opal_async_release_token(int token) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci unsigned long flags; 858c2ecf20Sopenharmony_ci int rc; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (token < 0 || token >= opal_max_async_tokens) { 888c2ecf20Sopenharmony_ci pr_err("%s: Passed token is out of range, token %d\n", 898c2ecf20Sopenharmony_ci __func__, token); 908c2ecf20Sopenharmony_ci return -EINVAL; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci spin_lock_irqsave(&opal_async_comp_lock, flags); 948c2ecf20Sopenharmony_ci switch (opal_async_tokens[token].state) { 958c2ecf20Sopenharmony_ci case ASYNC_TOKEN_COMPLETED: 968c2ecf20Sopenharmony_ci case ASYNC_TOKEN_ALLOCATED: 978c2ecf20Sopenharmony_ci opal_async_tokens[token].state = ASYNC_TOKEN_UNALLOCATED; 988c2ecf20Sopenharmony_ci rc = 0; 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci /* 1018c2ecf20Sopenharmony_ci * DISPATCHED and ABANDONED tokens must wait for OPAL to respond. 1028c2ecf20Sopenharmony_ci * Mark a DISPATCHED token as ABANDONED so that the response handling 1038c2ecf20Sopenharmony_ci * code knows no one cares and that it can free it then. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci case ASYNC_TOKEN_DISPATCHED: 1068c2ecf20Sopenharmony_ci opal_async_tokens[token].state = ASYNC_TOKEN_ABANDONED; 1078c2ecf20Sopenharmony_ci fallthrough; 1088c2ecf20Sopenharmony_ci default: 1098c2ecf20Sopenharmony_ci rc = 1; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opal_async_comp_lock, flags); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return rc; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ciint opal_async_release_token(int token) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci int ret; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci ret = __opal_async_release_token(token); 1218c2ecf20Sopenharmony_ci if (!ret) 1228c2ecf20Sopenharmony_ci up(&opal_async_sem); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return ret; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_async_release_token); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ciint opal_async_wait_response(uint64_t token, struct opal_msg *msg) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci if (token >= opal_max_async_tokens) { 1318c2ecf20Sopenharmony_ci pr_err("%s: Invalid token passed\n", __func__); 1328c2ecf20Sopenharmony_ci return -EINVAL; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (!msg) { 1368c2ecf20Sopenharmony_ci pr_err("%s: Invalid message pointer passed\n", __func__); 1378c2ecf20Sopenharmony_ci return -EINVAL; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* 1418c2ecf20Sopenharmony_ci * There is no need to mark the token as dispatched, wait_event() 1428c2ecf20Sopenharmony_ci * will block until the token completes. 1438c2ecf20Sopenharmony_ci * 1448c2ecf20Sopenharmony_ci * Wakeup the poller before we wait for events to speed things 1458c2ecf20Sopenharmony_ci * up on platforms or simulators where the interrupts aren't 1468c2ecf20Sopenharmony_ci * functional. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci opal_wake_poller(); 1498c2ecf20Sopenharmony_ci wait_event(opal_async_wait, opal_async_tokens[token].state 1508c2ecf20Sopenharmony_ci == ASYNC_TOKEN_COMPLETED); 1518c2ecf20Sopenharmony_ci memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg)); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_async_wait_response); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ciint opal_async_wait_response_interruptible(uint64_t token, struct opal_msg *msg) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci unsigned long flags; 1608c2ecf20Sopenharmony_ci int ret; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (token >= opal_max_async_tokens) { 1638c2ecf20Sopenharmony_ci pr_err("%s: Invalid token passed\n", __func__); 1648c2ecf20Sopenharmony_ci return -EINVAL; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!msg) { 1688c2ecf20Sopenharmony_ci pr_err("%s: Invalid message pointer passed\n", __func__); 1698c2ecf20Sopenharmony_ci return -EINVAL; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* 1738c2ecf20Sopenharmony_ci * The first time this gets called we mark the token as DISPATCHED 1748c2ecf20Sopenharmony_ci * so that if wait_event_interruptible() returns not zero and the 1758c2ecf20Sopenharmony_ci * caller frees the token, we know not to actually free the token 1768c2ecf20Sopenharmony_ci * until the response comes. 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * Only change if the token is ALLOCATED - it may have been 1798c2ecf20Sopenharmony_ci * completed even before the caller gets around to calling this 1808c2ecf20Sopenharmony_ci * the first time. 1818c2ecf20Sopenharmony_ci * 1828c2ecf20Sopenharmony_ci * There is also a dirty great comment at the token allocation 1838c2ecf20Sopenharmony_ci * function that if the opal call returns OPAL_ASYNC_COMPLETION to 1848c2ecf20Sopenharmony_ci * the caller then the caller *must* call this or the not 1858c2ecf20Sopenharmony_ci * interruptible version before doing anything else with the 1868c2ecf20Sopenharmony_ci * token. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_ci if (opal_async_tokens[token].state == ASYNC_TOKEN_ALLOCATED) { 1898c2ecf20Sopenharmony_ci spin_lock_irqsave(&opal_async_comp_lock, flags); 1908c2ecf20Sopenharmony_ci if (opal_async_tokens[token].state == ASYNC_TOKEN_ALLOCATED) 1918c2ecf20Sopenharmony_ci opal_async_tokens[token].state = ASYNC_TOKEN_DISPATCHED; 1928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opal_async_comp_lock, flags); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* 1968c2ecf20Sopenharmony_ci * Wakeup the poller before we wait for events to speed things 1978c2ecf20Sopenharmony_ci * up on platforms or simulators where the interrupts aren't 1988c2ecf20Sopenharmony_ci * functional. 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ci opal_wake_poller(); 2018c2ecf20Sopenharmony_ci ret = wait_event_interruptible(opal_async_wait, 2028c2ecf20Sopenharmony_ci opal_async_tokens[token].state == 2038c2ecf20Sopenharmony_ci ASYNC_TOKEN_COMPLETED); 2048c2ecf20Sopenharmony_ci if (!ret) 2058c2ecf20Sopenharmony_ci memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg)); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return ret; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_async_wait_response_interruptible); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* Called from interrupt context */ 2128c2ecf20Sopenharmony_cistatic int opal_async_comp_event(struct notifier_block *nb, 2138c2ecf20Sopenharmony_ci unsigned long msg_type, void *msg) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct opal_msg *comp_msg = msg; 2168c2ecf20Sopenharmony_ci enum opal_async_token_state state; 2178c2ecf20Sopenharmony_ci unsigned long flags; 2188c2ecf20Sopenharmony_ci uint64_t token; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (msg_type != OPAL_MSG_ASYNC_COMP) 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci token = be64_to_cpu(comp_msg->params[0]); 2248c2ecf20Sopenharmony_ci spin_lock_irqsave(&opal_async_comp_lock, flags); 2258c2ecf20Sopenharmony_ci state = opal_async_tokens[token].state; 2268c2ecf20Sopenharmony_ci opal_async_tokens[token].state = ASYNC_TOKEN_COMPLETED; 2278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opal_async_comp_lock, flags); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (state == ASYNC_TOKEN_ABANDONED) { 2308c2ecf20Sopenharmony_ci /* Free the token, no one else will */ 2318c2ecf20Sopenharmony_ci opal_async_release_token(token); 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci memcpy(&opal_async_tokens[token].response, comp_msg, sizeof(*comp_msg)); 2358c2ecf20Sopenharmony_ci wake_up(&opal_async_wait); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic struct notifier_block opal_async_comp_nb = { 2418c2ecf20Sopenharmony_ci .notifier_call = opal_async_comp_event, 2428c2ecf20Sopenharmony_ci .next = NULL, 2438c2ecf20Sopenharmony_ci .priority = 0, 2448c2ecf20Sopenharmony_ci}; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ciint __init opal_async_comp_init(void) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct device_node *opal_node; 2498c2ecf20Sopenharmony_ci const __be32 *async; 2508c2ecf20Sopenharmony_ci int err; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci opal_node = of_find_node_by_path("/ibm,opal"); 2538c2ecf20Sopenharmony_ci if (!opal_node) { 2548c2ecf20Sopenharmony_ci pr_err("%s: Opal node not found\n", __func__); 2558c2ecf20Sopenharmony_ci err = -ENOENT; 2568c2ecf20Sopenharmony_ci goto out; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci async = of_get_property(opal_node, "opal-msg-async-num", NULL); 2608c2ecf20Sopenharmony_ci if (!async) { 2618c2ecf20Sopenharmony_ci pr_err("%s: %pOF has no opal-msg-async-num\n", 2628c2ecf20Sopenharmony_ci __func__, opal_node); 2638c2ecf20Sopenharmony_ci err = -ENOENT; 2648c2ecf20Sopenharmony_ci goto out_opal_node; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci opal_max_async_tokens = be32_to_cpup(async); 2688c2ecf20Sopenharmony_ci opal_async_tokens = kcalloc(opal_max_async_tokens, 2698c2ecf20Sopenharmony_ci sizeof(*opal_async_tokens), GFP_KERNEL); 2708c2ecf20Sopenharmony_ci if (!opal_async_tokens) { 2718c2ecf20Sopenharmony_ci err = -ENOMEM; 2728c2ecf20Sopenharmony_ci goto out_opal_node; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP, 2768c2ecf20Sopenharmony_ci &opal_async_comp_nb); 2778c2ecf20Sopenharmony_ci if (err) { 2788c2ecf20Sopenharmony_ci pr_err("%s: Can't register OPAL event notifier (%d)\n", 2798c2ecf20Sopenharmony_ci __func__, err); 2808c2ecf20Sopenharmony_ci kfree(opal_async_tokens); 2818c2ecf20Sopenharmony_ci goto out_opal_node; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci sema_init(&opal_async_sem, opal_max_async_tokens); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ciout_opal_node: 2878c2ecf20Sopenharmony_ci of_node_put(opal_node); 2888c2ecf20Sopenharmony_ciout: 2898c2ecf20Sopenharmony_ci return err; 2908c2ecf20Sopenharmony_ci} 291