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 
3659cf9a1cSCyril Bur static 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);
4271e24d77SWilliam A. Kennington III 	token = find_first_zero_bit(opal_async_token_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 
4871e24d77SWilliam A. Kennington III 	if (!__test_and_clear_bit(token, opal_async_complete_map)) {
498d724823SNeelesh Gupta 		token = -EBUSY;
508d724823SNeelesh Gupta 		goto out;
518d724823SNeelesh Gupta 	}
528d724823SNeelesh Gupta 
5371e24d77SWilliam A. Kennington III 	__set_bit(token, opal_async_token_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 }
7416b1d26eSNeelesh Gupta EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible);
758d724823SNeelesh Gupta 
7659cf9a1cSCyril Bur static int __opal_async_release_token(int token)
778d724823SNeelesh Gupta {
788d724823SNeelesh Gupta 	unsigned long flags;
798d724823SNeelesh Gupta 
808d724823SNeelesh Gupta 	if (token < 0 || token >= opal_max_async_tokens) {
818d724823SNeelesh Gupta 		pr_err("%s: Passed token is out of range, token %d\n",
828d724823SNeelesh Gupta 				__func__, token);
838d724823SNeelesh Gupta 		return -EINVAL;
848d724823SNeelesh Gupta 	}
858d724823SNeelesh Gupta 
868d724823SNeelesh Gupta 	spin_lock_irqsave(&opal_async_comp_lock, flags);
878d724823SNeelesh Gupta 	__set_bit(token, opal_async_complete_map);
888d724823SNeelesh Gupta 	__clear_bit(token, opal_async_token_map);
898d724823SNeelesh Gupta 	spin_unlock_irqrestore(&opal_async_comp_lock, flags);
908d724823SNeelesh Gupta 
918d724823SNeelesh Gupta 	return 0;
928d724823SNeelesh Gupta }
938d724823SNeelesh Gupta 
948d724823SNeelesh Gupta int opal_async_release_token(int token)
958d724823SNeelesh Gupta {
968d724823SNeelesh Gupta 	int ret;
978d724823SNeelesh Gupta 
988d724823SNeelesh Gupta 	ret = __opal_async_release_token(token);
998d724823SNeelesh Gupta 	if (ret)
1008d724823SNeelesh Gupta 		return ret;
1018d724823SNeelesh Gupta 
1028d724823SNeelesh Gupta 	up(&opal_async_sem);
1038d724823SNeelesh Gupta 
1048d724823SNeelesh Gupta 	return 0;
1058d724823SNeelesh Gupta }
10616b1d26eSNeelesh Gupta EXPORT_SYMBOL_GPL(opal_async_release_token);
1078d724823SNeelesh Gupta 
1088d724823SNeelesh Gupta int opal_async_wait_response(uint64_t token, struct opal_msg *msg)
1098d724823SNeelesh Gupta {
1108d724823SNeelesh Gupta 	if (token >= opal_max_async_tokens) {
1118d724823SNeelesh Gupta 		pr_err("%s: Invalid token passed\n", __func__);
1128d724823SNeelesh Gupta 		return -EINVAL;
1138d724823SNeelesh Gupta 	}
1148d724823SNeelesh Gupta 
1158d724823SNeelesh Gupta 	if (!msg) {
1168d724823SNeelesh Gupta 		pr_err("%s: Invalid message pointer passed\n", __func__);
1178d724823SNeelesh Gupta 		return -EINVAL;
1188d724823SNeelesh Gupta 	}
1198d724823SNeelesh Gupta 
120a203658bSBenjamin Herrenschmidt 	/* Wakeup the poller before we wait for events to speed things
121a203658bSBenjamin Herrenschmidt 	 * up on platforms or simulators where the interrupts aren't
122a203658bSBenjamin Herrenschmidt 	 * functional.
123a203658bSBenjamin Herrenschmidt 	 */
124a203658bSBenjamin Herrenschmidt 	opal_wake_poller();
1258d724823SNeelesh Gupta 	wait_event(opal_async_wait, test_bit(token, opal_async_complete_map));
1268d724823SNeelesh Gupta 	memcpy(msg, &opal_async_responses[token], sizeof(*msg));
1278d724823SNeelesh Gupta 
1288d724823SNeelesh Gupta 	return 0;
1298d724823SNeelesh Gupta }
13016b1d26eSNeelesh Gupta EXPORT_SYMBOL_GPL(opal_async_wait_response);
1318d724823SNeelesh Gupta 
1328d724823SNeelesh Gupta static int opal_async_comp_event(struct notifier_block *nb,
1338d724823SNeelesh Gupta 		unsigned long msg_type, void *msg)
1348d724823SNeelesh Gupta {
1358d724823SNeelesh Gupta 	struct opal_msg *comp_msg = msg;
1368d724823SNeelesh Gupta 	unsigned long flags;
137bb4398e1SAnton Blanchard 	uint64_t token;
1388d724823SNeelesh Gupta 
1398d724823SNeelesh Gupta 	if (msg_type != OPAL_MSG_ASYNC_COMP)
1408d724823SNeelesh Gupta 		return 0;
1418d724823SNeelesh Gupta 
142bb4398e1SAnton Blanchard 	token = be64_to_cpu(comp_msg->params[0]);
143bb4398e1SAnton Blanchard 	memcpy(&opal_async_responses[token], comp_msg, sizeof(*comp_msg));
1448d724823SNeelesh Gupta 	spin_lock_irqsave(&opal_async_comp_lock, flags);
145bb4398e1SAnton Blanchard 	__set_bit(token, opal_async_complete_map);
1468d724823SNeelesh Gupta 	spin_unlock_irqrestore(&opal_async_comp_lock, flags);
1478d724823SNeelesh Gupta 
1488d724823SNeelesh Gupta 	wake_up(&opal_async_wait);
1498d724823SNeelesh Gupta 
1508d724823SNeelesh Gupta 	return 0;
1518d724823SNeelesh Gupta }
1528d724823SNeelesh Gupta 
1538d724823SNeelesh Gupta static struct notifier_block opal_async_comp_nb = {
1548d724823SNeelesh Gupta 		.notifier_call	= opal_async_comp_event,
1558d724823SNeelesh Gupta 		.next		= NULL,
1568d724823SNeelesh Gupta 		.priority	= 0,
1578d724823SNeelesh Gupta };
1588d724823SNeelesh Gupta 
15996e023e7SAlistair Popple int __init opal_async_comp_init(void)
1608d724823SNeelesh Gupta {
1618d724823SNeelesh Gupta 	struct device_node *opal_node;
1628d724823SNeelesh Gupta 	const __be32 *async;
1638d724823SNeelesh Gupta 	int err;
1648d724823SNeelesh Gupta 
1658d724823SNeelesh Gupta 	opal_node = of_find_node_by_path("/ibm,opal");
1668d724823SNeelesh Gupta 	if (!opal_node) {
1678d724823SNeelesh Gupta 		pr_err("%s: Opal node not found\n", __func__);
1688d724823SNeelesh Gupta 		err = -ENOENT;
1698d724823SNeelesh Gupta 		goto out;
1708d724823SNeelesh Gupta 	}
1718d724823SNeelesh Gupta 
1728d724823SNeelesh Gupta 	async = of_get_property(opal_node, "opal-msg-async-num", NULL);
1738d724823SNeelesh Gupta 	if (!async) {
174b7c670d6SRob Herring 		pr_err("%s: %pOF has no opal-msg-async-num\n",
175b7c670d6SRob Herring 				__func__, opal_node);
1768d724823SNeelesh Gupta 		err = -ENOENT;
1778d724823SNeelesh Gupta 		goto out_opal_node;
1788d724823SNeelesh Gupta 	}
1798d724823SNeelesh Gupta 
1808d724823SNeelesh Gupta 	opal_max_async_tokens = be32_to_cpup(async);
1818d724823SNeelesh Gupta 	if (opal_max_async_tokens > N_ASYNC_COMPLETIONS)
1828d724823SNeelesh Gupta 		opal_max_async_tokens = N_ASYNC_COMPLETIONS;
1838d724823SNeelesh Gupta 
1848d724823SNeelesh Gupta 	err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP,
1858d724823SNeelesh Gupta 			&opal_async_comp_nb);
1868d724823SNeelesh Gupta 	if (err) {
1878d724823SNeelesh Gupta 		pr_err("%s: Can't register OPAL event notifier (%d)\n",
1888d724823SNeelesh Gupta 				__func__, err);
1898d724823SNeelesh Gupta 		goto out_opal_node;
1908d724823SNeelesh Gupta 	}
1918d724823SNeelesh Gupta 
1928d724823SNeelesh Gupta 	opal_async_responses = kzalloc(
1938d724823SNeelesh Gupta 			sizeof(*opal_async_responses) * opal_max_async_tokens,
1948d724823SNeelesh Gupta 			GFP_KERNEL);
1958d724823SNeelesh Gupta 	if (!opal_async_responses) {
1968d724823SNeelesh Gupta 		pr_err("%s: Out of memory, failed to do asynchronous "
1978d724823SNeelesh Gupta 				"completion init\n", __func__);
1988d724823SNeelesh Gupta 		err = -ENOMEM;
1998d724823SNeelesh Gupta 		goto out_opal_node;
2008d724823SNeelesh Gupta 	}
2018d724823SNeelesh Gupta 
20259cf9a1cSCyril Bur 	sema_init(&opal_async_sem, opal_max_async_tokens);
2038d724823SNeelesh Gupta 
2048d724823SNeelesh Gupta out_opal_node:
2058d724823SNeelesh Gupta 	of_node_put(opal_node);
2068d724823SNeelesh Gupta out:
2078d724823SNeelesh Gupta 	return err;
2088d724823SNeelesh Gupta }
209