18d724823SNeelesh Gupta /* 28d724823SNeelesh Gupta * PowerNV OPAL asynchronous completion interfaces 38d724823SNeelesh Gupta * 486cd6d98SCyril Bur * Copyright 2013-2017 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 2686cd6d98SCyril Bur enum opal_async_token_state { 2786cd6d98SCyril Bur ASYNC_TOKEN_UNALLOCATED = 0, 2886cd6d98SCyril Bur ASYNC_TOKEN_ALLOCATED, 2986cd6d98SCyril Bur ASYNC_TOKEN_COMPLETED 3086cd6d98SCyril Bur }; 318d724823SNeelesh Gupta 3286cd6d98SCyril Bur struct opal_async_token { 3386cd6d98SCyril Bur enum opal_async_token_state state; 3486cd6d98SCyril Bur struct opal_msg response; 3586cd6d98SCyril Bur }; 3686cd6d98SCyril Bur 378d724823SNeelesh Gupta static DECLARE_WAIT_QUEUE_HEAD(opal_async_wait); 388d724823SNeelesh Gupta static DEFINE_SPINLOCK(opal_async_comp_lock); 398d724823SNeelesh Gupta static struct semaphore opal_async_sem; 408d724823SNeelesh Gupta static unsigned int opal_max_async_tokens; 4186cd6d98SCyril Bur static struct opal_async_token *opal_async_tokens; 428d724823SNeelesh Gupta 4359cf9a1cSCyril Bur static int __opal_async_get_token(void) 448d724823SNeelesh Gupta { 458d724823SNeelesh Gupta unsigned long flags; 4686cd6d98SCyril Bur int i, token = -EBUSY; 478d724823SNeelesh Gupta 488d724823SNeelesh Gupta spin_lock_irqsave(&opal_async_comp_lock, flags); 4986cd6d98SCyril Bur 5086cd6d98SCyril Bur for (i = 0; i < opal_max_async_tokens; i++) { 5186cd6d98SCyril Bur if (opal_async_tokens[i].state == ASYNC_TOKEN_UNALLOCATED) { 5286cd6d98SCyril Bur opal_async_tokens[i].state = ASYNC_TOKEN_ALLOCATED; 5386cd6d98SCyril Bur token = i; 5486cd6d98SCyril Bur break; 5586cd6d98SCyril Bur } 568d724823SNeelesh Gupta } 578d724823SNeelesh Gupta 588d724823SNeelesh Gupta spin_unlock_irqrestore(&opal_async_comp_lock, flags); 598d724823SNeelesh Gupta return token; 608d724823SNeelesh Gupta } 618d724823SNeelesh Gupta 6286cd6d98SCyril Bur /* 6386cd6d98SCyril Bur * Note: If the returned token is used in an opal call and opal returns 6486cd6d98SCyril Bur * OPAL_ASYNC_COMPLETION you MUST call opal_async_wait_response() before 6586cd6d98SCyril Bur * calling another other opal_async_* function 6686cd6d98SCyril Bur */ 678d724823SNeelesh Gupta int opal_async_get_token_interruptible(void) 688d724823SNeelesh Gupta { 698d724823SNeelesh Gupta int token; 708d724823SNeelesh Gupta 718d724823SNeelesh Gupta /* Wait until a token is available */ 728d724823SNeelesh Gupta if (down_interruptible(&opal_async_sem)) 738d724823SNeelesh Gupta return -ERESTARTSYS; 748d724823SNeelesh Gupta 758d724823SNeelesh Gupta token = __opal_async_get_token(); 768d724823SNeelesh Gupta if (token < 0) 778d724823SNeelesh Gupta up(&opal_async_sem); 788d724823SNeelesh Gupta 798d724823SNeelesh Gupta return token; 808d724823SNeelesh Gupta } 8116b1d26eSNeelesh Gupta EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible); 828d724823SNeelesh Gupta 8359cf9a1cSCyril Bur static int __opal_async_release_token(int token) 848d724823SNeelesh Gupta { 858d724823SNeelesh Gupta unsigned long flags; 8686cd6d98SCyril Bur int rc; 878d724823SNeelesh Gupta 888d724823SNeelesh Gupta if (token < 0 || token >= opal_max_async_tokens) { 898d724823SNeelesh Gupta pr_err("%s: Passed token is out of range, token %d\n", 908d724823SNeelesh Gupta __func__, token); 918d724823SNeelesh Gupta return -EINVAL; 928d724823SNeelesh Gupta } 938d724823SNeelesh Gupta 948d724823SNeelesh Gupta spin_lock_irqsave(&opal_async_comp_lock, flags); 9586cd6d98SCyril Bur switch (opal_async_tokens[token].state) { 9686cd6d98SCyril Bur case ASYNC_TOKEN_COMPLETED: 9786cd6d98SCyril Bur case ASYNC_TOKEN_ALLOCATED: 9886cd6d98SCyril Bur opal_async_tokens[token].state = ASYNC_TOKEN_UNALLOCATED; 9986cd6d98SCyril Bur rc = 0; 10086cd6d98SCyril Bur break; 10186cd6d98SCyril Bur default: 10286cd6d98SCyril Bur rc = 1; 10386cd6d98SCyril Bur } 1048d724823SNeelesh Gupta spin_unlock_irqrestore(&opal_async_comp_lock, flags); 1058d724823SNeelesh Gupta 10686cd6d98SCyril Bur return rc; 1078d724823SNeelesh Gupta } 1088d724823SNeelesh Gupta 1098d724823SNeelesh Gupta int opal_async_release_token(int token) 1108d724823SNeelesh Gupta { 1118d724823SNeelesh Gupta int ret; 1128d724823SNeelesh Gupta 1138d724823SNeelesh Gupta ret = __opal_async_release_token(token); 11486cd6d98SCyril Bur if (!ret) 1158d724823SNeelesh Gupta up(&opal_async_sem); 1168d724823SNeelesh Gupta 11786cd6d98SCyril Bur return ret; 1188d724823SNeelesh Gupta } 11916b1d26eSNeelesh Gupta EXPORT_SYMBOL_GPL(opal_async_release_token); 1208d724823SNeelesh Gupta 1218d724823SNeelesh Gupta int opal_async_wait_response(uint64_t token, struct opal_msg *msg) 1228d724823SNeelesh Gupta { 1238d724823SNeelesh Gupta if (token >= opal_max_async_tokens) { 1248d724823SNeelesh Gupta pr_err("%s: Invalid token passed\n", __func__); 1258d724823SNeelesh Gupta return -EINVAL; 1268d724823SNeelesh Gupta } 1278d724823SNeelesh Gupta 1288d724823SNeelesh Gupta if (!msg) { 1298d724823SNeelesh Gupta pr_err("%s: Invalid message pointer passed\n", __func__); 1308d724823SNeelesh Gupta return -EINVAL; 1318d724823SNeelesh Gupta } 1328d724823SNeelesh Gupta 133a203658bSBenjamin Herrenschmidt /* Wakeup the poller before we wait for events to speed things 134a203658bSBenjamin Herrenschmidt * up on platforms or simulators where the interrupts aren't 135a203658bSBenjamin Herrenschmidt * functional. 136a203658bSBenjamin Herrenschmidt */ 137a203658bSBenjamin Herrenschmidt opal_wake_poller(); 13886cd6d98SCyril Bur wait_event(opal_async_wait, opal_async_tokens[token].state 13986cd6d98SCyril Bur == ASYNC_TOKEN_COMPLETED); 14086cd6d98SCyril Bur memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg)); 1418d724823SNeelesh Gupta 1428d724823SNeelesh Gupta return 0; 1438d724823SNeelesh Gupta } 14416b1d26eSNeelesh Gupta EXPORT_SYMBOL_GPL(opal_async_wait_response); 1458d724823SNeelesh Gupta 14686cd6d98SCyril Bur /* Called from interrupt context */ 1478d724823SNeelesh Gupta static int opal_async_comp_event(struct notifier_block *nb, 1488d724823SNeelesh Gupta unsigned long msg_type, void *msg) 1498d724823SNeelesh Gupta { 1508d724823SNeelesh Gupta struct opal_msg *comp_msg = msg; 1518d724823SNeelesh Gupta unsigned long flags; 152bb4398e1SAnton Blanchard uint64_t token; 1538d724823SNeelesh Gupta 1548d724823SNeelesh Gupta if (msg_type != OPAL_MSG_ASYNC_COMP) 1558d724823SNeelesh Gupta return 0; 1568d724823SNeelesh Gupta 157bb4398e1SAnton Blanchard token = be64_to_cpu(comp_msg->params[0]); 15886cd6d98SCyril Bur memcpy(&opal_async_tokens[token].response, comp_msg, sizeof(*comp_msg)); 1598d724823SNeelesh Gupta spin_lock_irqsave(&opal_async_comp_lock, flags); 16086cd6d98SCyril Bur opal_async_tokens[token].state = ASYNC_TOKEN_COMPLETED; 1618d724823SNeelesh Gupta spin_unlock_irqrestore(&opal_async_comp_lock, flags); 1628d724823SNeelesh Gupta 1638d724823SNeelesh Gupta wake_up(&opal_async_wait); 1648d724823SNeelesh Gupta 1658d724823SNeelesh Gupta return 0; 1668d724823SNeelesh Gupta } 1678d724823SNeelesh Gupta 1688d724823SNeelesh Gupta static struct notifier_block opal_async_comp_nb = { 1698d724823SNeelesh Gupta .notifier_call = opal_async_comp_event, 1708d724823SNeelesh Gupta .next = NULL, 1718d724823SNeelesh Gupta .priority = 0, 1728d724823SNeelesh Gupta }; 1738d724823SNeelesh Gupta 17496e023e7SAlistair Popple int __init opal_async_comp_init(void) 1758d724823SNeelesh Gupta { 1768d724823SNeelesh Gupta struct device_node *opal_node; 1778d724823SNeelesh Gupta const __be32 *async; 1788d724823SNeelesh Gupta int err; 1798d724823SNeelesh Gupta 1808d724823SNeelesh Gupta opal_node = of_find_node_by_path("/ibm,opal"); 1818d724823SNeelesh Gupta if (!opal_node) { 1828d724823SNeelesh Gupta pr_err("%s: Opal node not found\n", __func__); 1838d724823SNeelesh Gupta err = -ENOENT; 1848d724823SNeelesh Gupta goto out; 1858d724823SNeelesh Gupta } 1868d724823SNeelesh Gupta 1878d724823SNeelesh Gupta async = of_get_property(opal_node, "opal-msg-async-num", NULL); 1888d724823SNeelesh Gupta if (!async) { 189b7c670d6SRob Herring pr_err("%s: %pOF has no opal-msg-async-num\n", 190b7c670d6SRob Herring __func__, opal_node); 1918d724823SNeelesh Gupta err = -ENOENT; 1928d724823SNeelesh Gupta goto out_opal_node; 1938d724823SNeelesh Gupta } 1948d724823SNeelesh Gupta 1958d724823SNeelesh Gupta opal_max_async_tokens = be32_to_cpup(async); 19686cd6d98SCyril Bur opal_async_tokens = kcalloc(opal_max_async_tokens, 19786cd6d98SCyril Bur sizeof(*opal_async_tokens), GFP_KERNEL); 19886cd6d98SCyril Bur if (!opal_async_tokens) { 19986cd6d98SCyril Bur err = -ENOMEM; 20086cd6d98SCyril Bur goto out_opal_node; 20186cd6d98SCyril Bur } 2028d724823SNeelesh Gupta 2038d724823SNeelesh Gupta err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP, 2048d724823SNeelesh Gupta &opal_async_comp_nb); 2058d724823SNeelesh Gupta if (err) { 2068d724823SNeelesh Gupta pr_err("%s: Can't register OPAL event notifier (%d)\n", 2078d724823SNeelesh Gupta __func__, err); 20886cd6d98SCyril Bur kfree(opal_async_tokens); 2098d724823SNeelesh Gupta goto out_opal_node; 2108d724823SNeelesh Gupta } 2118d724823SNeelesh Gupta 21259cf9a1cSCyril Bur sema_init(&opal_async_sem, opal_max_async_tokens); 2138d724823SNeelesh Gupta 2148d724823SNeelesh Gupta out_opal_node: 2158d724823SNeelesh Gupta of_node_put(opal_node); 2168d724823SNeelesh Gupta out: 2178d724823SNeelesh Gupta return err; 2188d724823SNeelesh Gupta } 219