18d724823SNeelesh Gupta /* 28d724823SNeelesh Gupta * PowerNV OPAL asynchronous completion interfaces 38d724823SNeelesh Gupta * 48d724823SNeelesh Gupta * Copyright 2013 IBM Corp. 58d724823SNeelesh Gupta * 68d724823SNeelesh Gupta * This program is free software; you can redistribute it and/or 78d724823SNeelesh Gupta * modify it under the terms of the GNU General Public License 88d724823SNeelesh Gupta * as published by the Free Software Foundation; either version 98d724823SNeelesh Gupta * 2 of the License, or (at your option) any later version. 108d724823SNeelesh Gupta */ 118d724823SNeelesh Gupta 128d724823SNeelesh Gupta #undef DEBUG 138d724823SNeelesh Gupta 148d724823SNeelesh Gupta #include <linux/kernel.h> 158d724823SNeelesh Gupta #include <linux/init.h> 168d724823SNeelesh Gupta #include <linux/slab.h> 178d724823SNeelesh Gupta #include <linux/sched.h> 188d724823SNeelesh Gupta #include <linux/semaphore.h> 198d724823SNeelesh Gupta #include <linux/spinlock.h> 208d724823SNeelesh Gupta #include <linux/wait.h> 218d724823SNeelesh Gupta #include <linux/gfp.h> 228d724823SNeelesh Gupta #include <linux/of.h> 23b14726c5SMichael Ellerman #include <asm/machdep.h> 248d724823SNeelesh Gupta #include <asm/opal.h> 258d724823SNeelesh Gupta 268d724823SNeelesh Gupta #define N_ASYNC_COMPLETIONS 64 278d724823SNeelesh Gupta 288d724823SNeelesh Gupta static DECLARE_BITMAP(opal_async_complete_map, N_ASYNC_COMPLETIONS) = {~0UL}; 298d724823SNeelesh Gupta static DECLARE_BITMAP(opal_async_token_map, N_ASYNC_COMPLETIONS); 308d724823SNeelesh Gupta static DECLARE_WAIT_QUEUE_HEAD(opal_async_wait); 318d724823SNeelesh Gupta static DEFINE_SPINLOCK(opal_async_comp_lock); 328d724823SNeelesh Gupta static struct semaphore opal_async_sem; 338d724823SNeelesh Gupta static struct opal_msg *opal_async_responses; 348d724823SNeelesh Gupta static unsigned int opal_max_async_tokens; 358d724823SNeelesh Gupta 368d724823SNeelesh Gupta int __opal_async_get_token(void) 378d724823SNeelesh Gupta { 388d724823SNeelesh Gupta unsigned long flags; 398d724823SNeelesh Gupta int token; 408d724823SNeelesh Gupta 418d724823SNeelesh Gupta spin_lock_irqsave(&opal_async_comp_lock, flags); 428d724823SNeelesh Gupta token = find_first_bit(opal_async_complete_map, opal_max_async_tokens); 438d724823SNeelesh Gupta if (token >= opal_max_async_tokens) { 448d724823SNeelesh Gupta token = -EBUSY; 458d724823SNeelesh Gupta goto out; 468d724823SNeelesh Gupta } 478d724823SNeelesh Gupta 488d724823SNeelesh Gupta if (__test_and_set_bit(token, opal_async_token_map)) { 498d724823SNeelesh Gupta token = -EBUSY; 508d724823SNeelesh Gupta goto out; 518d724823SNeelesh Gupta } 528d724823SNeelesh Gupta 538d724823SNeelesh Gupta __clear_bit(token, opal_async_complete_map); 548d724823SNeelesh Gupta 558d724823SNeelesh Gupta out: 568d724823SNeelesh Gupta spin_unlock_irqrestore(&opal_async_comp_lock, flags); 578d724823SNeelesh Gupta return token; 588d724823SNeelesh Gupta } 598d724823SNeelesh Gupta 608d724823SNeelesh Gupta int opal_async_get_token_interruptible(void) 618d724823SNeelesh Gupta { 628d724823SNeelesh Gupta int token; 638d724823SNeelesh Gupta 648d724823SNeelesh Gupta /* Wait until a token is available */ 658d724823SNeelesh Gupta if (down_interruptible(&opal_async_sem)) 668d724823SNeelesh Gupta return -ERESTARTSYS; 678d724823SNeelesh Gupta 688d724823SNeelesh Gupta token = __opal_async_get_token(); 698d724823SNeelesh Gupta if (token < 0) 708d724823SNeelesh Gupta up(&opal_async_sem); 718d724823SNeelesh Gupta 728d724823SNeelesh Gupta return token; 738d724823SNeelesh Gupta } 748d724823SNeelesh Gupta 758d724823SNeelesh Gupta int __opal_async_release_token(int token) 768d724823SNeelesh Gupta { 778d724823SNeelesh Gupta unsigned long flags; 788d724823SNeelesh Gupta 798d724823SNeelesh Gupta if (token < 0 || token >= opal_max_async_tokens) { 808d724823SNeelesh Gupta pr_err("%s: Passed token is out of range, token %d\n", 818d724823SNeelesh Gupta __func__, token); 828d724823SNeelesh Gupta return -EINVAL; 838d724823SNeelesh Gupta } 848d724823SNeelesh Gupta 858d724823SNeelesh Gupta spin_lock_irqsave(&opal_async_comp_lock, flags); 868d724823SNeelesh Gupta __set_bit(token, opal_async_complete_map); 878d724823SNeelesh Gupta __clear_bit(token, opal_async_token_map); 888d724823SNeelesh Gupta spin_unlock_irqrestore(&opal_async_comp_lock, flags); 898d724823SNeelesh Gupta 908d724823SNeelesh Gupta return 0; 918d724823SNeelesh Gupta } 928d724823SNeelesh Gupta 938d724823SNeelesh Gupta int opal_async_release_token(int token) 948d724823SNeelesh Gupta { 958d724823SNeelesh Gupta int ret; 968d724823SNeelesh Gupta 978d724823SNeelesh Gupta ret = __opal_async_release_token(token); 988d724823SNeelesh Gupta if (ret) 998d724823SNeelesh Gupta return ret; 1008d724823SNeelesh Gupta 1018d724823SNeelesh Gupta up(&opal_async_sem); 1028d724823SNeelesh Gupta 1038d724823SNeelesh Gupta return 0; 1048d724823SNeelesh Gupta } 1058d724823SNeelesh Gupta 1068d724823SNeelesh Gupta int opal_async_wait_response(uint64_t token, struct opal_msg *msg) 1078d724823SNeelesh Gupta { 1088d724823SNeelesh Gupta if (token >= opal_max_async_tokens) { 1098d724823SNeelesh Gupta pr_err("%s: Invalid token passed\n", __func__); 1108d724823SNeelesh Gupta return -EINVAL; 1118d724823SNeelesh Gupta } 1128d724823SNeelesh Gupta 1138d724823SNeelesh Gupta if (!msg) { 1148d724823SNeelesh Gupta pr_err("%s: Invalid message pointer passed\n", __func__); 1158d724823SNeelesh Gupta return -EINVAL; 1168d724823SNeelesh Gupta } 1178d724823SNeelesh Gupta 1188d724823SNeelesh Gupta wait_event(opal_async_wait, test_bit(token, opal_async_complete_map)); 1198d724823SNeelesh Gupta memcpy(msg, &opal_async_responses[token], sizeof(*msg)); 1208d724823SNeelesh Gupta 1218d724823SNeelesh Gupta return 0; 1228d724823SNeelesh Gupta } 1238d724823SNeelesh Gupta 1248d724823SNeelesh Gupta static int opal_async_comp_event(struct notifier_block *nb, 1258d724823SNeelesh Gupta unsigned long msg_type, void *msg) 1268d724823SNeelesh Gupta { 1278d724823SNeelesh Gupta struct opal_msg *comp_msg = msg; 1288d724823SNeelesh Gupta unsigned long flags; 129bb4398e1SAnton Blanchard uint64_t token; 1308d724823SNeelesh Gupta 1318d724823SNeelesh Gupta if (msg_type != OPAL_MSG_ASYNC_COMP) 1328d724823SNeelesh Gupta return 0; 1338d724823SNeelesh Gupta 134bb4398e1SAnton Blanchard token = be64_to_cpu(comp_msg->params[0]); 135bb4398e1SAnton Blanchard memcpy(&opal_async_responses[token], comp_msg, sizeof(*comp_msg)); 1368d724823SNeelesh Gupta spin_lock_irqsave(&opal_async_comp_lock, flags); 137bb4398e1SAnton Blanchard __set_bit(token, opal_async_complete_map); 1388d724823SNeelesh Gupta spin_unlock_irqrestore(&opal_async_comp_lock, flags); 1398d724823SNeelesh Gupta 1408d724823SNeelesh Gupta wake_up(&opal_async_wait); 1418d724823SNeelesh Gupta 1428d724823SNeelesh Gupta return 0; 1438d724823SNeelesh Gupta } 1448d724823SNeelesh Gupta 1458d724823SNeelesh Gupta static struct notifier_block opal_async_comp_nb = { 1468d724823SNeelesh Gupta .notifier_call = opal_async_comp_event, 1478d724823SNeelesh Gupta .next = NULL, 1488d724823SNeelesh Gupta .priority = 0, 1498d724823SNeelesh Gupta }; 1508d724823SNeelesh Gupta 1518d724823SNeelesh Gupta static int __init opal_async_comp_init(void) 1528d724823SNeelesh Gupta { 1538d724823SNeelesh Gupta struct device_node *opal_node; 1548d724823SNeelesh Gupta const __be32 *async; 1558d724823SNeelesh Gupta int err; 1568d724823SNeelesh Gupta 1578d724823SNeelesh Gupta opal_node = of_find_node_by_path("/ibm,opal"); 1588d724823SNeelesh Gupta if (!opal_node) { 1598d724823SNeelesh Gupta pr_err("%s: Opal node not found\n", __func__); 1608d724823SNeelesh Gupta err = -ENOENT; 1618d724823SNeelesh Gupta goto out; 1628d724823SNeelesh Gupta } 1638d724823SNeelesh Gupta 1648d724823SNeelesh Gupta async = of_get_property(opal_node, "opal-msg-async-num", NULL); 1658d724823SNeelesh Gupta if (!async) { 1668d724823SNeelesh Gupta pr_err("%s: %s has no opal-msg-async-num\n", 1678d724823SNeelesh Gupta __func__, opal_node->full_name); 1688d724823SNeelesh Gupta err = -ENOENT; 1698d724823SNeelesh Gupta goto out_opal_node; 1708d724823SNeelesh Gupta } 1718d724823SNeelesh Gupta 1728d724823SNeelesh Gupta opal_max_async_tokens = be32_to_cpup(async); 1738d724823SNeelesh Gupta if (opal_max_async_tokens > N_ASYNC_COMPLETIONS) 1748d724823SNeelesh Gupta opal_max_async_tokens = N_ASYNC_COMPLETIONS; 1758d724823SNeelesh Gupta 1768d724823SNeelesh Gupta err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP, 1778d724823SNeelesh Gupta &opal_async_comp_nb); 1788d724823SNeelesh Gupta if (err) { 1798d724823SNeelesh Gupta pr_err("%s: Can't register OPAL event notifier (%d)\n", 1808d724823SNeelesh Gupta __func__, err); 1818d724823SNeelesh Gupta goto out_opal_node; 1828d724823SNeelesh Gupta } 1838d724823SNeelesh Gupta 1848d724823SNeelesh Gupta opal_async_responses = kzalloc( 1858d724823SNeelesh Gupta sizeof(*opal_async_responses) * opal_max_async_tokens, 1868d724823SNeelesh Gupta GFP_KERNEL); 1878d724823SNeelesh Gupta if (!opal_async_responses) { 1888d724823SNeelesh Gupta pr_err("%s: Out of memory, failed to do asynchronous " 1898d724823SNeelesh Gupta "completion init\n", __func__); 1908d724823SNeelesh Gupta err = -ENOMEM; 1918d724823SNeelesh Gupta goto out_opal_node; 1928d724823SNeelesh Gupta } 1938d724823SNeelesh Gupta 1948d724823SNeelesh Gupta /* Initialize to 1 less than the maximum tokens available, as we may 1958d724823SNeelesh Gupta * require to pop one during emergency through synchronous call to 1968d724823SNeelesh Gupta * __opal_async_get_token() 1978d724823SNeelesh Gupta */ 1988d724823SNeelesh Gupta sema_init(&opal_async_sem, opal_max_async_tokens - 1); 1998d724823SNeelesh Gupta 2008d724823SNeelesh Gupta out_opal_node: 2018d724823SNeelesh Gupta of_node_put(opal_node); 2028d724823SNeelesh Gupta out: 2038d724823SNeelesh Gupta return err; 2048d724823SNeelesh Gupta } 205b14726c5SMichael Ellerman machine_subsys_initcall(powernv, opal_async_comp_init); 206