12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28d724823SNeelesh Gupta /*
38d724823SNeelesh Gupta  * PowerNV OPAL asynchronous completion interfaces
48d724823SNeelesh Gupta  *
586cd6d98SCyril Bur  * Copyright 2013-2017 IBM Corp.
68d724823SNeelesh Gupta  */
78d724823SNeelesh Gupta 
88d724823SNeelesh Gupta #undef DEBUG
98d724823SNeelesh Gupta 
108d724823SNeelesh Gupta #include <linux/kernel.h>
118d724823SNeelesh Gupta #include <linux/init.h>
128d724823SNeelesh Gupta #include <linux/slab.h>
138d724823SNeelesh Gupta #include <linux/sched.h>
148d724823SNeelesh Gupta #include <linux/semaphore.h>
158d724823SNeelesh Gupta #include <linux/spinlock.h>
168d724823SNeelesh Gupta #include <linux/wait.h>
178d724823SNeelesh Gupta #include <linux/gfp.h>
188d724823SNeelesh Gupta #include <linux/of.h>
19b14726c5SMichael Ellerman #include <asm/machdep.h>
208d724823SNeelesh Gupta #include <asm/opal.h>
218d724823SNeelesh Gupta 
2286cd6d98SCyril Bur enum opal_async_token_state {
2386cd6d98SCyril Bur 	ASYNC_TOKEN_UNALLOCATED = 0,
2486cd6d98SCyril Bur 	ASYNC_TOKEN_ALLOCATED,
259aab2449SCyril Bur 	ASYNC_TOKEN_DISPATCHED,
269aab2449SCyril Bur 	ASYNC_TOKEN_ABANDONED,
2786cd6d98SCyril Bur 	ASYNC_TOKEN_COMPLETED
2886cd6d98SCyril Bur };
298d724823SNeelesh Gupta 
3086cd6d98SCyril Bur struct opal_async_token {
3186cd6d98SCyril Bur 	enum opal_async_token_state state;
3286cd6d98SCyril Bur 	struct opal_msg response;
3386cd6d98SCyril Bur };
3486cd6d98SCyril Bur 
358d724823SNeelesh Gupta static DECLARE_WAIT_QUEUE_HEAD(opal_async_wait);
368d724823SNeelesh Gupta static DEFINE_SPINLOCK(opal_async_comp_lock);
378d724823SNeelesh Gupta static struct semaphore opal_async_sem;
388d724823SNeelesh Gupta static unsigned int opal_max_async_tokens;
3986cd6d98SCyril Bur static struct opal_async_token *opal_async_tokens;
408d724823SNeelesh Gupta 
__opal_async_get_token(void)4159cf9a1cSCyril Bur static int __opal_async_get_token(void)
428d724823SNeelesh Gupta {
438d724823SNeelesh Gupta 	unsigned long flags;
4486cd6d98SCyril Bur 	int i, token = -EBUSY;
458d724823SNeelesh Gupta 
468d724823SNeelesh Gupta 	spin_lock_irqsave(&opal_async_comp_lock, flags);
4786cd6d98SCyril Bur 
4886cd6d98SCyril Bur 	for (i = 0; i < opal_max_async_tokens; i++) {
4986cd6d98SCyril Bur 		if (opal_async_tokens[i].state == ASYNC_TOKEN_UNALLOCATED) {
5086cd6d98SCyril Bur 			opal_async_tokens[i].state = ASYNC_TOKEN_ALLOCATED;
5186cd6d98SCyril Bur 			token = i;
5286cd6d98SCyril Bur 			break;
5386cd6d98SCyril Bur 		}
548d724823SNeelesh Gupta 	}
558d724823SNeelesh Gupta 
568d724823SNeelesh Gupta 	spin_unlock_irqrestore(&opal_async_comp_lock, flags);
578d724823SNeelesh Gupta 	return token;
588d724823SNeelesh Gupta }
598d724823SNeelesh Gupta 
6086cd6d98SCyril Bur /*
6186cd6d98SCyril Bur  * Note: If the returned token is used in an opal call and opal returns
629aab2449SCyril Bur  * OPAL_ASYNC_COMPLETION you MUST call one of opal_async_wait_response() or
639aab2449SCyril Bur  * opal_async_wait_response_interruptible() at least once before calling another
649aab2449SCyril Bur  * opal_async_* function
6586cd6d98SCyril Bur  */
opal_async_get_token_interruptible(void)668d724823SNeelesh Gupta int opal_async_get_token_interruptible(void)
678d724823SNeelesh Gupta {
688d724823SNeelesh Gupta 	int token;
698d724823SNeelesh Gupta 
708d724823SNeelesh Gupta 	/* Wait until a token is available */
718d724823SNeelesh Gupta 	if (down_interruptible(&opal_async_sem))
728d724823SNeelesh Gupta 		return -ERESTARTSYS;
738d724823SNeelesh Gupta 
748d724823SNeelesh Gupta 	token = __opal_async_get_token();
758d724823SNeelesh Gupta 	if (token < 0)
768d724823SNeelesh Gupta 		up(&opal_async_sem);
778d724823SNeelesh Gupta 
788d724823SNeelesh Gupta 	return token;
798d724823SNeelesh Gupta }
8016b1d26eSNeelesh Gupta EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible);
818d724823SNeelesh Gupta 
__opal_async_release_token(int token)8259cf9a1cSCyril Bur static int __opal_async_release_token(int token)
838d724823SNeelesh Gupta {
848d724823SNeelesh Gupta 	unsigned long flags;
8586cd6d98SCyril Bur 	int rc;
868d724823SNeelesh Gupta 
878d724823SNeelesh Gupta 	if (token < 0 || token >= opal_max_async_tokens) {
888d724823SNeelesh Gupta 		pr_err("%s: Passed token is out of range, token %d\n",
898d724823SNeelesh Gupta 				__func__, token);
908d724823SNeelesh Gupta 		return -EINVAL;
918d724823SNeelesh Gupta 	}
928d724823SNeelesh Gupta 
938d724823SNeelesh Gupta 	spin_lock_irqsave(&opal_async_comp_lock, flags);
9486cd6d98SCyril Bur 	switch (opal_async_tokens[token].state) {
9586cd6d98SCyril Bur 	case ASYNC_TOKEN_COMPLETED:
9686cd6d98SCyril Bur 	case ASYNC_TOKEN_ALLOCATED:
9786cd6d98SCyril Bur 		opal_async_tokens[token].state = ASYNC_TOKEN_UNALLOCATED;
9886cd6d98SCyril Bur 		rc = 0;
9986cd6d98SCyril Bur 		break;
1009aab2449SCyril Bur 	/*
1019aab2449SCyril Bur 	 * DISPATCHED and ABANDONED tokens must wait for OPAL to respond.
1029aab2449SCyril Bur 	 * Mark a DISPATCHED token as ABANDONED so that the response handling
1039aab2449SCyril Bur 	 * code knows no one cares and that it can free it then.
1049aab2449SCyril Bur 	 */
1059aab2449SCyril Bur 	case ASYNC_TOKEN_DISPATCHED:
1069aab2449SCyril Bur 		opal_async_tokens[token].state = ASYNC_TOKEN_ABANDONED;
107*5e66a0cbSGustavo A. R. Silva 		fallthrough;
10886cd6d98SCyril Bur 	default:
10986cd6d98SCyril Bur 		rc = 1;
11086cd6d98SCyril Bur 	}
1118d724823SNeelesh Gupta 	spin_unlock_irqrestore(&opal_async_comp_lock, flags);
1128d724823SNeelesh Gupta 
11386cd6d98SCyril Bur 	return rc;
1148d724823SNeelesh Gupta }
1158d724823SNeelesh Gupta 
opal_async_release_token(int token)1168d724823SNeelesh Gupta int opal_async_release_token(int token)
1178d724823SNeelesh Gupta {
1188d724823SNeelesh Gupta 	int ret;
1198d724823SNeelesh Gupta 
1208d724823SNeelesh Gupta 	ret = __opal_async_release_token(token);
12186cd6d98SCyril Bur 	if (!ret)
1228d724823SNeelesh Gupta 		up(&opal_async_sem);
1238d724823SNeelesh Gupta 
12486cd6d98SCyril Bur 	return ret;
1258d724823SNeelesh Gupta }
12616b1d26eSNeelesh Gupta EXPORT_SYMBOL_GPL(opal_async_release_token);
1278d724823SNeelesh Gupta 
opal_async_wait_response(uint64_t token,struct opal_msg * msg)1288d724823SNeelesh Gupta int opal_async_wait_response(uint64_t token, struct opal_msg *msg)
1298d724823SNeelesh Gupta {
1308d724823SNeelesh Gupta 	if (token >= opal_max_async_tokens) {
1318d724823SNeelesh Gupta 		pr_err("%s: Invalid token passed\n", __func__);
1328d724823SNeelesh Gupta 		return -EINVAL;
1338d724823SNeelesh Gupta 	}
1348d724823SNeelesh Gupta 
1358d724823SNeelesh Gupta 	if (!msg) {
1368d724823SNeelesh Gupta 		pr_err("%s: Invalid message pointer passed\n", __func__);
1378d724823SNeelesh Gupta 		return -EINVAL;
1388d724823SNeelesh Gupta 	}
1398d724823SNeelesh Gupta 
1409aab2449SCyril Bur 	/*
1419aab2449SCyril Bur 	 * There is no need to mark the token as dispatched, wait_event()
1429aab2449SCyril Bur 	 * will block until the token completes.
1439aab2449SCyril Bur 	 *
1449aab2449SCyril Bur 	 * Wakeup the poller before we wait for events to speed things
145a203658bSBenjamin Herrenschmidt 	 * up on platforms or simulators where the interrupts aren't
146a203658bSBenjamin Herrenschmidt 	 * functional.
147a203658bSBenjamin Herrenschmidt 	 */
148a203658bSBenjamin Herrenschmidt 	opal_wake_poller();
14986cd6d98SCyril Bur 	wait_event(opal_async_wait, opal_async_tokens[token].state
15086cd6d98SCyril Bur 			== ASYNC_TOKEN_COMPLETED);
15186cd6d98SCyril Bur 	memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg));
1528d724823SNeelesh Gupta 
1538d724823SNeelesh Gupta 	return 0;
1548d724823SNeelesh Gupta }
15516b1d26eSNeelesh Gupta EXPORT_SYMBOL_GPL(opal_async_wait_response);
1568d724823SNeelesh Gupta 
opal_async_wait_response_interruptible(uint64_t token,struct opal_msg * msg)1579aab2449SCyril Bur int opal_async_wait_response_interruptible(uint64_t token, struct opal_msg *msg)
1589aab2449SCyril Bur {
1599aab2449SCyril Bur 	unsigned long flags;
1609aab2449SCyril Bur 	int ret;
1619aab2449SCyril Bur 
1629aab2449SCyril Bur 	if (token >= opal_max_async_tokens) {
1639aab2449SCyril Bur 		pr_err("%s: Invalid token passed\n", __func__);
1649aab2449SCyril Bur 		return -EINVAL;
1659aab2449SCyril Bur 	}
1669aab2449SCyril Bur 
1679aab2449SCyril Bur 	if (!msg) {
1689aab2449SCyril Bur 		pr_err("%s: Invalid message pointer passed\n", __func__);
1699aab2449SCyril Bur 		return -EINVAL;
1709aab2449SCyril Bur 	}
1719aab2449SCyril Bur 
1729aab2449SCyril Bur 	/*
1739aab2449SCyril Bur 	 * The first time this gets called we mark the token as DISPATCHED
1749aab2449SCyril Bur 	 * so that if wait_event_interruptible() returns not zero and the
1759aab2449SCyril Bur 	 * caller frees the token, we know not to actually free the token
1769aab2449SCyril Bur 	 * until the response comes.
1779aab2449SCyril Bur 	 *
1789aab2449SCyril Bur 	 * Only change if the token is ALLOCATED - it may have been
1799aab2449SCyril Bur 	 * completed even before the caller gets around to calling this
1809aab2449SCyril Bur 	 * the first time.
1819aab2449SCyril Bur 	 *
1829aab2449SCyril Bur 	 * There is also a dirty great comment at the token allocation
1839aab2449SCyril Bur 	 * function that if the opal call returns OPAL_ASYNC_COMPLETION to
1849aab2449SCyril Bur 	 * the caller then the caller *must* call this or the not
1859aab2449SCyril Bur 	 * interruptible version before doing anything else with the
1869aab2449SCyril Bur 	 * token.
1879aab2449SCyril Bur 	 */
1889aab2449SCyril Bur 	if (opal_async_tokens[token].state == ASYNC_TOKEN_ALLOCATED) {
1899aab2449SCyril Bur 		spin_lock_irqsave(&opal_async_comp_lock, flags);
1909aab2449SCyril Bur 		if (opal_async_tokens[token].state == ASYNC_TOKEN_ALLOCATED)
1919aab2449SCyril Bur 			opal_async_tokens[token].state = ASYNC_TOKEN_DISPATCHED;
1929aab2449SCyril Bur 		spin_unlock_irqrestore(&opal_async_comp_lock, flags);
1939aab2449SCyril Bur 	}
1949aab2449SCyril Bur 
1959aab2449SCyril Bur 	/*
1969aab2449SCyril Bur 	 * Wakeup the poller before we wait for events to speed things
1979aab2449SCyril Bur 	 * up on platforms or simulators where the interrupts aren't
1989aab2449SCyril Bur 	 * functional.
1999aab2449SCyril Bur 	 */
2009aab2449SCyril Bur 	opal_wake_poller();
2019aab2449SCyril Bur 	ret = wait_event_interruptible(opal_async_wait,
2029aab2449SCyril Bur 			opal_async_tokens[token].state ==
2039aab2449SCyril Bur 			ASYNC_TOKEN_COMPLETED);
2049aab2449SCyril Bur 	if (!ret)
2059aab2449SCyril Bur 		memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg));
2069aab2449SCyril Bur 
2079aab2449SCyril Bur 	return ret;
2089aab2449SCyril Bur }
2099aab2449SCyril Bur EXPORT_SYMBOL_GPL(opal_async_wait_response_interruptible);
2109aab2449SCyril Bur 
21186cd6d98SCyril Bur /* Called from interrupt context */
opal_async_comp_event(struct notifier_block * nb,unsigned long msg_type,void * msg)2128d724823SNeelesh Gupta static int opal_async_comp_event(struct notifier_block *nb,
2138d724823SNeelesh Gupta 		unsigned long msg_type, void *msg)
2148d724823SNeelesh Gupta {
2158d724823SNeelesh Gupta 	struct opal_msg *comp_msg = msg;
2169aab2449SCyril Bur 	enum opal_async_token_state state;
2178d724823SNeelesh Gupta 	unsigned long flags;
218bb4398e1SAnton Blanchard 	uint64_t token;
2198d724823SNeelesh Gupta 
2208d724823SNeelesh Gupta 	if (msg_type != OPAL_MSG_ASYNC_COMP)
2218d724823SNeelesh Gupta 		return 0;
2228d724823SNeelesh Gupta 
223bb4398e1SAnton Blanchard 	token = be64_to_cpu(comp_msg->params[0]);
2248d724823SNeelesh Gupta 	spin_lock_irqsave(&opal_async_comp_lock, flags);
2259aab2449SCyril Bur 	state = opal_async_tokens[token].state;
22686cd6d98SCyril Bur 	opal_async_tokens[token].state = ASYNC_TOKEN_COMPLETED;
2278d724823SNeelesh Gupta 	spin_unlock_irqrestore(&opal_async_comp_lock, flags);
2288d724823SNeelesh Gupta 
2299aab2449SCyril Bur 	if (state == ASYNC_TOKEN_ABANDONED) {
2309aab2449SCyril Bur 		/* Free the token, no one else will */
2319aab2449SCyril Bur 		opal_async_release_token(token);
2329aab2449SCyril Bur 		return 0;
2339aab2449SCyril Bur 	}
2349aab2449SCyril Bur 	memcpy(&opal_async_tokens[token].response, comp_msg, sizeof(*comp_msg));
2358d724823SNeelesh Gupta 	wake_up(&opal_async_wait);
2368d724823SNeelesh Gupta 
2378d724823SNeelesh Gupta 	return 0;
2388d724823SNeelesh Gupta }
2398d724823SNeelesh Gupta 
2408d724823SNeelesh Gupta static struct notifier_block opal_async_comp_nb = {
2418d724823SNeelesh Gupta 		.notifier_call	= opal_async_comp_event,
2428d724823SNeelesh Gupta 		.next		= NULL,
2438d724823SNeelesh Gupta 		.priority	= 0,
2448d724823SNeelesh Gupta };
2458d724823SNeelesh Gupta 
opal_async_comp_init(void)24696e023e7SAlistair Popple int __init opal_async_comp_init(void)
2478d724823SNeelesh Gupta {
2488d724823SNeelesh Gupta 	struct device_node *opal_node;
2498d724823SNeelesh Gupta 	const __be32 *async;
2508d724823SNeelesh Gupta 	int err;
2518d724823SNeelesh Gupta 
2528d724823SNeelesh Gupta 	opal_node = of_find_node_by_path("/ibm,opal");
2538d724823SNeelesh Gupta 	if (!opal_node) {
2548d724823SNeelesh Gupta 		pr_err("%s: Opal node not found\n", __func__);
2558d724823SNeelesh Gupta 		err = -ENOENT;
2568d724823SNeelesh Gupta 		goto out;
2578d724823SNeelesh Gupta 	}
2588d724823SNeelesh Gupta 
2598d724823SNeelesh Gupta 	async = of_get_property(opal_node, "opal-msg-async-num", NULL);
2608d724823SNeelesh Gupta 	if (!async) {
261b7c670d6SRob Herring 		pr_err("%s: %pOF has no opal-msg-async-num\n",
262b7c670d6SRob Herring 				__func__, opal_node);
2638d724823SNeelesh Gupta 		err = -ENOENT;
2648d724823SNeelesh Gupta 		goto out_opal_node;
2658d724823SNeelesh Gupta 	}
2668d724823SNeelesh Gupta 
2678d724823SNeelesh Gupta 	opal_max_async_tokens = be32_to_cpup(async);
26886cd6d98SCyril Bur 	opal_async_tokens = kcalloc(opal_max_async_tokens,
26986cd6d98SCyril Bur 			sizeof(*opal_async_tokens), GFP_KERNEL);
27086cd6d98SCyril Bur 	if (!opal_async_tokens) {
27186cd6d98SCyril Bur 		err = -ENOMEM;
27286cd6d98SCyril Bur 		goto out_opal_node;
27386cd6d98SCyril Bur 	}
2748d724823SNeelesh Gupta 
2758d724823SNeelesh Gupta 	err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP,
2768d724823SNeelesh Gupta 			&opal_async_comp_nb);
2778d724823SNeelesh Gupta 	if (err) {
2788d724823SNeelesh Gupta 		pr_err("%s: Can't register OPAL event notifier (%d)\n",
2798d724823SNeelesh Gupta 				__func__, err);
28086cd6d98SCyril Bur 		kfree(opal_async_tokens);
2818d724823SNeelesh Gupta 		goto out_opal_node;
2828d724823SNeelesh Gupta 	}
2838d724823SNeelesh Gupta 
28459cf9a1cSCyril Bur 	sema_init(&opal_async_sem, opal_max_async_tokens);
2858d724823SNeelesh Gupta 
2868d724823SNeelesh Gupta out_opal_node:
2878d724823SNeelesh Gupta 	of_node_put(opal_node);
2888d724823SNeelesh Gupta out:
2898d724823SNeelesh Gupta 	return err;
2908d724823SNeelesh Gupta }
291