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