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,
299aab2449SCyril Bur 	ASYNC_TOKEN_DISPATCHED,
309aab2449SCyril Bur 	ASYNC_TOKEN_ABANDONED,
3186cd6d98SCyril Bur 	ASYNC_TOKEN_COMPLETED
3286cd6d98SCyril Bur };
338d724823SNeelesh Gupta 
3486cd6d98SCyril Bur struct opal_async_token {
3586cd6d98SCyril Bur 	enum opal_async_token_state state;
3686cd6d98SCyril Bur 	struct opal_msg response;
3786cd6d98SCyril Bur };
3886cd6d98SCyril Bur 
398d724823SNeelesh Gupta static DECLARE_WAIT_QUEUE_HEAD(opal_async_wait);
408d724823SNeelesh Gupta static DEFINE_SPINLOCK(opal_async_comp_lock);
418d724823SNeelesh Gupta static struct semaphore opal_async_sem;
428d724823SNeelesh Gupta static unsigned int opal_max_async_tokens;
4386cd6d98SCyril Bur static struct opal_async_token *opal_async_tokens;
448d724823SNeelesh Gupta 
4559cf9a1cSCyril Bur static int __opal_async_get_token(void)
468d724823SNeelesh Gupta {
478d724823SNeelesh Gupta 	unsigned long flags;
4886cd6d98SCyril Bur 	int i, token = -EBUSY;
498d724823SNeelesh Gupta 
508d724823SNeelesh Gupta 	spin_lock_irqsave(&opal_async_comp_lock, flags);
5186cd6d98SCyril Bur 
5286cd6d98SCyril Bur 	for (i = 0; i < opal_max_async_tokens; i++) {
5386cd6d98SCyril Bur 		if (opal_async_tokens[i].state == ASYNC_TOKEN_UNALLOCATED) {
5486cd6d98SCyril Bur 			opal_async_tokens[i].state = ASYNC_TOKEN_ALLOCATED;
5586cd6d98SCyril Bur 			token = i;
5686cd6d98SCyril Bur 			break;
5786cd6d98SCyril Bur 		}
588d724823SNeelesh Gupta 	}
598d724823SNeelesh Gupta 
608d724823SNeelesh Gupta 	spin_unlock_irqrestore(&opal_async_comp_lock, flags);
618d724823SNeelesh Gupta 	return token;
628d724823SNeelesh Gupta }
638d724823SNeelesh Gupta 
6486cd6d98SCyril Bur /*
6586cd6d98SCyril Bur  * Note: If the returned token is used in an opal call and opal returns
669aab2449SCyril Bur  * OPAL_ASYNC_COMPLETION you MUST call one of opal_async_wait_response() or
679aab2449SCyril Bur  * opal_async_wait_response_interruptible() at least once before calling another
689aab2449SCyril Bur  * opal_async_* function
6986cd6d98SCyril Bur  */
708d724823SNeelesh Gupta int opal_async_get_token_interruptible(void)
718d724823SNeelesh Gupta {
728d724823SNeelesh Gupta 	int token;
738d724823SNeelesh Gupta 
748d724823SNeelesh Gupta 	/* Wait until a token is available */
758d724823SNeelesh Gupta 	if (down_interruptible(&opal_async_sem))
768d724823SNeelesh Gupta 		return -ERESTARTSYS;
778d724823SNeelesh Gupta 
788d724823SNeelesh Gupta 	token = __opal_async_get_token();
798d724823SNeelesh Gupta 	if (token < 0)
808d724823SNeelesh Gupta 		up(&opal_async_sem);
818d724823SNeelesh Gupta 
828d724823SNeelesh Gupta 	return token;
838d724823SNeelesh Gupta }
8416b1d26eSNeelesh Gupta EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible);
858d724823SNeelesh Gupta 
8659cf9a1cSCyril Bur static int __opal_async_release_token(int token)
878d724823SNeelesh Gupta {
888d724823SNeelesh Gupta 	unsigned long flags;
8986cd6d98SCyril Bur 	int rc;
908d724823SNeelesh Gupta 
918d724823SNeelesh Gupta 	if (token < 0 || token >= opal_max_async_tokens) {
928d724823SNeelesh Gupta 		pr_err("%s: Passed token is out of range, token %d\n",
938d724823SNeelesh Gupta 				__func__, token);
948d724823SNeelesh Gupta 		return -EINVAL;
958d724823SNeelesh Gupta 	}
968d724823SNeelesh Gupta 
978d724823SNeelesh Gupta 	spin_lock_irqsave(&opal_async_comp_lock, flags);
9886cd6d98SCyril Bur 	switch (opal_async_tokens[token].state) {
9986cd6d98SCyril Bur 	case ASYNC_TOKEN_COMPLETED:
10086cd6d98SCyril Bur 	case ASYNC_TOKEN_ALLOCATED:
10186cd6d98SCyril Bur 		opal_async_tokens[token].state = ASYNC_TOKEN_UNALLOCATED;
10286cd6d98SCyril Bur 		rc = 0;
10386cd6d98SCyril Bur 		break;
1049aab2449SCyril Bur 	/*
1059aab2449SCyril Bur 	 * DISPATCHED and ABANDONED tokens must wait for OPAL to respond.
1069aab2449SCyril Bur 	 * Mark a DISPATCHED token as ABANDONED so that the response handling
1079aab2449SCyril Bur 	 * code knows no one cares and that it can free it then.
1089aab2449SCyril Bur 	 */
1099aab2449SCyril Bur 	case ASYNC_TOKEN_DISPATCHED:
1109aab2449SCyril Bur 		opal_async_tokens[token].state = ASYNC_TOKEN_ABANDONED;
1119aab2449SCyril Bur 		/* Fall through */
11286cd6d98SCyril Bur 	default:
11386cd6d98SCyril Bur 		rc = 1;
11486cd6d98SCyril Bur 	}
1158d724823SNeelesh Gupta 	spin_unlock_irqrestore(&opal_async_comp_lock, flags);
1168d724823SNeelesh Gupta 
11786cd6d98SCyril Bur 	return rc;
1188d724823SNeelesh Gupta }
1198d724823SNeelesh Gupta 
1208d724823SNeelesh Gupta int opal_async_release_token(int token)
1218d724823SNeelesh Gupta {
1228d724823SNeelesh Gupta 	int ret;
1238d724823SNeelesh Gupta 
1248d724823SNeelesh Gupta 	ret = __opal_async_release_token(token);
12586cd6d98SCyril Bur 	if (!ret)
1268d724823SNeelesh Gupta 		up(&opal_async_sem);
1278d724823SNeelesh Gupta 
12886cd6d98SCyril Bur 	return ret;
1298d724823SNeelesh Gupta }
13016b1d26eSNeelesh Gupta EXPORT_SYMBOL_GPL(opal_async_release_token);
1318d724823SNeelesh Gupta 
1328d724823SNeelesh Gupta int opal_async_wait_response(uint64_t token, struct opal_msg *msg)
1338d724823SNeelesh Gupta {
1348d724823SNeelesh Gupta 	if (token >= opal_max_async_tokens) {
1358d724823SNeelesh Gupta 		pr_err("%s: Invalid token passed\n", __func__);
1368d724823SNeelesh Gupta 		return -EINVAL;
1378d724823SNeelesh Gupta 	}
1388d724823SNeelesh Gupta 
1398d724823SNeelesh Gupta 	if (!msg) {
1408d724823SNeelesh Gupta 		pr_err("%s: Invalid message pointer passed\n", __func__);
1418d724823SNeelesh Gupta 		return -EINVAL;
1428d724823SNeelesh Gupta 	}
1438d724823SNeelesh Gupta 
1449aab2449SCyril Bur 	/*
1459aab2449SCyril Bur 	 * There is no need to mark the token as dispatched, wait_event()
1469aab2449SCyril Bur 	 * will block until the token completes.
1479aab2449SCyril Bur 	 *
1489aab2449SCyril Bur 	 * Wakeup the poller before we wait for events to speed things
149a203658bSBenjamin Herrenschmidt 	 * up on platforms or simulators where the interrupts aren't
150a203658bSBenjamin Herrenschmidt 	 * functional.
151a203658bSBenjamin Herrenschmidt 	 */
152a203658bSBenjamin Herrenschmidt 	opal_wake_poller();
15386cd6d98SCyril Bur 	wait_event(opal_async_wait, opal_async_tokens[token].state
15486cd6d98SCyril Bur 			== ASYNC_TOKEN_COMPLETED);
15586cd6d98SCyril Bur 	memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg));
1568d724823SNeelesh Gupta 
1578d724823SNeelesh Gupta 	return 0;
1588d724823SNeelesh Gupta }
15916b1d26eSNeelesh Gupta EXPORT_SYMBOL_GPL(opal_async_wait_response);
1608d724823SNeelesh Gupta 
1619aab2449SCyril Bur int opal_async_wait_response_interruptible(uint64_t token, struct opal_msg *msg)
1629aab2449SCyril Bur {
1639aab2449SCyril Bur 	unsigned long flags;
1649aab2449SCyril Bur 	int ret;
1659aab2449SCyril Bur 
1669aab2449SCyril Bur 	if (token >= opal_max_async_tokens) {
1679aab2449SCyril Bur 		pr_err("%s: Invalid token passed\n", __func__);
1689aab2449SCyril Bur 		return -EINVAL;
1699aab2449SCyril Bur 	}
1709aab2449SCyril Bur 
1719aab2449SCyril Bur 	if (!msg) {
1729aab2449SCyril Bur 		pr_err("%s: Invalid message pointer passed\n", __func__);
1739aab2449SCyril Bur 		return -EINVAL;
1749aab2449SCyril Bur 	}
1759aab2449SCyril Bur 
1769aab2449SCyril Bur 	/*
1779aab2449SCyril Bur 	 * The first time this gets called we mark the token as DISPATCHED
1789aab2449SCyril Bur 	 * so that if wait_event_interruptible() returns not zero and the
1799aab2449SCyril Bur 	 * caller frees the token, we know not to actually free the token
1809aab2449SCyril Bur 	 * until the response comes.
1819aab2449SCyril Bur 	 *
1829aab2449SCyril Bur 	 * Only change if the token is ALLOCATED - it may have been
1839aab2449SCyril Bur 	 * completed even before the caller gets around to calling this
1849aab2449SCyril Bur 	 * the first time.
1859aab2449SCyril Bur 	 *
1869aab2449SCyril Bur 	 * There is also a dirty great comment at the token allocation
1879aab2449SCyril Bur 	 * function that if the opal call returns OPAL_ASYNC_COMPLETION to
1889aab2449SCyril Bur 	 * the caller then the caller *must* call this or the not
1899aab2449SCyril Bur 	 * interruptible version before doing anything else with the
1909aab2449SCyril Bur 	 * token.
1919aab2449SCyril Bur 	 */
1929aab2449SCyril Bur 	if (opal_async_tokens[token].state == ASYNC_TOKEN_ALLOCATED) {
1939aab2449SCyril Bur 		spin_lock_irqsave(&opal_async_comp_lock, flags);
1949aab2449SCyril Bur 		if (opal_async_tokens[token].state == ASYNC_TOKEN_ALLOCATED)
1959aab2449SCyril Bur 			opal_async_tokens[token].state = ASYNC_TOKEN_DISPATCHED;
1969aab2449SCyril Bur 		spin_unlock_irqrestore(&opal_async_comp_lock, flags);
1979aab2449SCyril Bur 	}
1989aab2449SCyril Bur 
1999aab2449SCyril Bur 	/*
2009aab2449SCyril Bur 	 * Wakeup the poller before we wait for events to speed things
2019aab2449SCyril Bur 	 * up on platforms or simulators where the interrupts aren't
2029aab2449SCyril Bur 	 * functional.
2039aab2449SCyril Bur 	 */
2049aab2449SCyril Bur 	opal_wake_poller();
2059aab2449SCyril Bur 	ret = wait_event_interruptible(opal_async_wait,
2069aab2449SCyril Bur 			opal_async_tokens[token].state ==
2079aab2449SCyril Bur 			ASYNC_TOKEN_COMPLETED);
2089aab2449SCyril Bur 	if (!ret)
2099aab2449SCyril Bur 		memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg));
2109aab2449SCyril Bur 
2119aab2449SCyril Bur 	return ret;
2129aab2449SCyril Bur }
2139aab2449SCyril Bur EXPORT_SYMBOL_GPL(opal_async_wait_response_interruptible);
2149aab2449SCyril Bur 
21586cd6d98SCyril Bur /* Called from interrupt context */
2168d724823SNeelesh Gupta static int opal_async_comp_event(struct notifier_block *nb,
2178d724823SNeelesh Gupta 		unsigned long msg_type, void *msg)
2188d724823SNeelesh Gupta {
2198d724823SNeelesh Gupta 	struct opal_msg *comp_msg = msg;
2209aab2449SCyril Bur 	enum opal_async_token_state state;
2218d724823SNeelesh Gupta 	unsigned long flags;
222bb4398e1SAnton Blanchard 	uint64_t token;
2238d724823SNeelesh Gupta 
2248d724823SNeelesh Gupta 	if (msg_type != OPAL_MSG_ASYNC_COMP)
2258d724823SNeelesh Gupta 		return 0;
2268d724823SNeelesh Gupta 
227bb4398e1SAnton Blanchard 	token = be64_to_cpu(comp_msg->params[0]);
2288d724823SNeelesh Gupta 	spin_lock_irqsave(&opal_async_comp_lock, flags);
2299aab2449SCyril Bur 	state = opal_async_tokens[token].state;
23086cd6d98SCyril Bur 	opal_async_tokens[token].state = ASYNC_TOKEN_COMPLETED;
2318d724823SNeelesh Gupta 	spin_unlock_irqrestore(&opal_async_comp_lock, flags);
2328d724823SNeelesh Gupta 
2339aab2449SCyril Bur 	if (state == ASYNC_TOKEN_ABANDONED) {
2349aab2449SCyril Bur 		/* Free the token, no one else will */
2359aab2449SCyril Bur 		opal_async_release_token(token);
2369aab2449SCyril Bur 		return 0;
2379aab2449SCyril Bur 	}
2389aab2449SCyril Bur 	memcpy(&opal_async_tokens[token].response, comp_msg, sizeof(*comp_msg));
2398d724823SNeelesh Gupta 	wake_up(&opal_async_wait);
2408d724823SNeelesh Gupta 
2418d724823SNeelesh Gupta 	return 0;
2428d724823SNeelesh Gupta }
2438d724823SNeelesh Gupta 
2448d724823SNeelesh Gupta static struct notifier_block opal_async_comp_nb = {
2458d724823SNeelesh Gupta 		.notifier_call	= opal_async_comp_event,
2468d724823SNeelesh Gupta 		.next		= NULL,
2478d724823SNeelesh Gupta 		.priority	= 0,
2488d724823SNeelesh Gupta };
2498d724823SNeelesh Gupta 
25096e023e7SAlistair Popple int __init opal_async_comp_init(void)
2518d724823SNeelesh Gupta {
2528d724823SNeelesh Gupta 	struct device_node *opal_node;
2538d724823SNeelesh Gupta 	const __be32 *async;
2548d724823SNeelesh Gupta 	int err;
2558d724823SNeelesh Gupta 
2568d724823SNeelesh Gupta 	opal_node = of_find_node_by_path("/ibm,opal");
2578d724823SNeelesh Gupta 	if (!opal_node) {
2588d724823SNeelesh Gupta 		pr_err("%s: Opal node not found\n", __func__);
2598d724823SNeelesh Gupta 		err = -ENOENT;
2608d724823SNeelesh Gupta 		goto out;
2618d724823SNeelesh Gupta 	}
2628d724823SNeelesh Gupta 
2638d724823SNeelesh Gupta 	async = of_get_property(opal_node, "opal-msg-async-num", NULL);
2648d724823SNeelesh Gupta 	if (!async) {
265b7c670d6SRob Herring 		pr_err("%s: %pOF has no opal-msg-async-num\n",
266b7c670d6SRob Herring 				__func__, opal_node);
2678d724823SNeelesh Gupta 		err = -ENOENT;
2688d724823SNeelesh Gupta 		goto out_opal_node;
2698d724823SNeelesh Gupta 	}
2708d724823SNeelesh Gupta 
2718d724823SNeelesh Gupta 	opal_max_async_tokens = be32_to_cpup(async);
27286cd6d98SCyril Bur 	opal_async_tokens = kcalloc(opal_max_async_tokens,
27386cd6d98SCyril Bur 			sizeof(*opal_async_tokens), GFP_KERNEL);
27486cd6d98SCyril Bur 	if (!opal_async_tokens) {
27586cd6d98SCyril Bur 		err = -ENOMEM;
27686cd6d98SCyril Bur 		goto out_opal_node;
27786cd6d98SCyril Bur 	}
2788d724823SNeelesh Gupta 
2798d724823SNeelesh Gupta 	err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP,
2808d724823SNeelesh Gupta 			&opal_async_comp_nb);
2818d724823SNeelesh Gupta 	if (err) {
2828d724823SNeelesh Gupta 		pr_err("%s: Can't register OPAL event notifier (%d)\n",
2838d724823SNeelesh Gupta 				__func__, err);
28486cd6d98SCyril Bur 		kfree(opal_async_tokens);
2858d724823SNeelesh Gupta 		goto out_opal_node;
2868d724823SNeelesh Gupta 	}
2878d724823SNeelesh Gupta 
28859cf9a1cSCyril Bur 	sema_init(&opal_async_sem, opal_max_async_tokens);
2898d724823SNeelesh Gupta 
2908d724823SNeelesh Gupta out_opal_node:
2918d724823SNeelesh Gupta 	of_node_put(opal_node);
2928d724823SNeelesh Gupta out:
2938d724823SNeelesh Gupta 	return err;
2948d724823SNeelesh Gupta }
295