summaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/powernv/opal-async.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-04-02 13:42:59 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-02 13:42:59 -0700
commit235c7b9feb8779c7c289ed614324baebf3651bf9 (patch)
tree477efafb74b56eb67f35312f2967ed8f4099d22e /arch/powerpc/platforms/powernv/opal-async.c
parentbdfc7cbdeef8cadba0e5793079ac0130b8e2220c (diff)
parentcd427485357c0c4b99f69719251baacf25946e11 (diff)
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
Pull main powerpc updates from Ben Herrenschmidt: "This time around, the powerpc merges are going to be a little bit more complicated than usual. This is the main pull request with most of the work for this merge window. I will describe it a bit more further down. There is some additional cpuidle driver work, however I haven't included it in this tree as it depends on some work in tip/timer-core which Thomas accidentally forgot to put in a topic branch. Since I didn't want to carry all of that tip timer stuff in powerpc -next, I setup a separate branch on top of Thomas tree with just that cpuidle driver in it, and Stephen has been carrying that in next separately for a while now. I'll send a separate pull request for it. Additionally, two new pieces in this tree add users for a sysfs API that Tejun and Greg have been deprecating in drivers-core-next. Thankfully Greg reverted the patch that removes the old API so this merge can happen cleanly, but once merged, I will send a patch adjusting our new code to the new API so that Greg can send you the removal patch. Now as for the content of this branch, we have a lot of perf work for power8 new counters including support for our new "nest" counters (also called 24x7) under pHyp (not natively yet). We have new functionality when running under the OPAL firmware (non-virtualized or KVM host), such as access to the firmware error logs and service processor dumps, system parameters and sensors, along with a hwmon driver for the latter. There's also a bunch of bug fixes accross the board, some LE fixes, and a nice set of selftests for validating our various types of copy loops. On the Freescale side, we see mostly new chip/board revisions, some clock updates, better support for machine checks and debug exceptions, etc..." * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (70 commits) powerpc/book3s: Fix CFAR clobbering issue in machine check handler. powerpc/compat: 32-bit little endian machine name is ppcle, not ppc powerpc/le: Big endian arguments for ppc_rtas() powerpc: Use default set of netfilter modules (CONFIG_NETFILTER_ADVANCED=n) powerpc/defconfigs: Enable THP in pseries defconfig powerpc/mm: Make sure a local_irq_disable prevent a parallel THP split powerpc: Rate-limit users spamming kernel log buffer powerpc/perf: Fix handling of L3 events with bank == 1 powerpc/perf/hv_{gpci, 24x7}: Add documentation of device attributes powerpc/perf: Add kconfig option for hypervisor provided counters powerpc/perf: Add support for the hv 24x7 interface powerpc/perf: Add support for the hv gpci (get performance counter info) interface powerpc/perf: Add macros for defining event fields & formats powerpc/perf: Add a shared interface to get gpci version and capabilities powerpc/perf: Add 24x7 interface headers powerpc/perf: Add hv_gpci interface header powerpc: Add hvcalls for 24x7 and gpci (Get Performance Counter Info) sysfs: create bin_attributes under the requested group powerpc/perf: Enable BHRB access for EBB events powerpc/perf: Add BHRB constraint and IFM MMCRA handling for EBB ...
Diffstat (limited to 'arch/powerpc/platforms/powernv/opal-async.c')
-rw-r--r--arch/powerpc/platforms/powernv/opal-async.c203
1 files changed, 203 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/powernv/opal-async.c b/arch/powerpc/platforms/powernv/opal-async.c
new file mode 100644
index 000000000000..cd0c1354d404
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-async.c
@@ -0,0 +1,203 @@
+/*
+ * PowerNV OPAL asynchronous completion interfaces
+ *
+ * Copyright 2013 IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/gfp.h>
+#include <linux/of.h>
+#include <asm/opal.h>
+
+#define N_ASYNC_COMPLETIONS 64
+
+static DECLARE_BITMAP(opal_async_complete_map, N_ASYNC_COMPLETIONS) = {~0UL};
+static DECLARE_BITMAP(opal_async_token_map, N_ASYNC_COMPLETIONS);
+static DECLARE_WAIT_QUEUE_HEAD(opal_async_wait);
+static DEFINE_SPINLOCK(opal_async_comp_lock);
+static struct semaphore opal_async_sem;
+static struct opal_msg *opal_async_responses;
+static unsigned int opal_max_async_tokens;
+
+int __opal_async_get_token(void)
+{
+ unsigned long flags;
+ int token;
+
+ spin_lock_irqsave(&opal_async_comp_lock, flags);
+ token = find_first_bit(opal_async_complete_map, opal_max_async_tokens);
+ if (token >= opal_max_async_tokens) {
+ token = -EBUSY;
+ goto out;
+ }
+
+ if (__test_and_set_bit(token, opal_async_token_map)) {
+ token = -EBUSY;
+ goto out;
+ }
+
+ __clear_bit(token, opal_async_complete_map);
+
+out:
+ spin_unlock_irqrestore(&opal_async_comp_lock, flags);
+ return token;
+}
+
+int opal_async_get_token_interruptible(void)
+{
+ int token;
+
+ /* Wait until a token is available */
+ if (down_interruptible(&opal_async_sem))
+ return -ERESTARTSYS;
+
+ token = __opal_async_get_token();
+ if (token < 0)
+ up(&opal_async_sem);
+
+ return token;
+}
+
+int __opal_async_release_token(int token)
+{
+ unsigned long flags;
+
+ if (token < 0 || token >= opal_max_async_tokens) {
+ pr_err("%s: Passed token is out of range, token %d\n",
+ __func__, token);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&opal_async_comp_lock, flags);
+ __set_bit(token, opal_async_complete_map);
+ __clear_bit(token, opal_async_token_map);
+ spin_unlock_irqrestore(&opal_async_comp_lock, flags);
+
+ return 0;
+}
+
+int opal_async_release_token(int token)
+{
+ int ret;
+
+ ret = __opal_async_release_token(token);
+ if (ret)
+ return ret;
+
+ up(&opal_async_sem);
+
+ return 0;
+}
+
+int opal_async_wait_response(uint64_t token, struct opal_msg *msg)
+{
+ if (token >= opal_max_async_tokens) {
+ pr_err("%s: Invalid token passed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!msg) {
+ pr_err("%s: Invalid message pointer passed\n", __func__);
+ return -EINVAL;
+ }
+
+ wait_event(opal_async_wait, test_bit(token, opal_async_complete_map));
+ memcpy(msg, &opal_async_responses[token], sizeof(*msg));
+
+ return 0;
+}
+
+static int opal_async_comp_event(struct notifier_block *nb,
+ unsigned long msg_type, void *msg)
+{
+ struct opal_msg *comp_msg = msg;
+ unsigned long flags;
+
+ if (msg_type != OPAL_MSG_ASYNC_COMP)
+ return 0;
+
+ memcpy(&opal_async_responses[comp_msg->params[0]], comp_msg,
+ sizeof(*comp_msg));
+ spin_lock_irqsave(&opal_async_comp_lock, flags);
+ __set_bit(comp_msg->params[0], opal_async_complete_map);
+ spin_unlock_irqrestore(&opal_async_comp_lock, flags);
+
+ wake_up(&opal_async_wait);
+
+ return 0;
+}
+
+static struct notifier_block opal_async_comp_nb = {
+ .notifier_call = opal_async_comp_event,
+ .next = NULL,
+ .priority = 0,
+};
+
+static int __init opal_async_comp_init(void)
+{
+ struct device_node *opal_node;
+ const __be32 *async;
+ int err;
+
+ opal_node = of_find_node_by_path("/ibm,opal");
+ if (!opal_node) {
+ pr_err("%s: Opal node not found\n", __func__);
+ err = -ENOENT;
+ goto out;
+ }
+
+ async = of_get_property(opal_node, "opal-msg-async-num", NULL);
+ if (!async) {
+ pr_err("%s: %s has no opal-msg-async-num\n",
+ __func__, opal_node->full_name);
+ err = -ENOENT;
+ goto out_opal_node;
+ }
+
+ opal_max_async_tokens = be32_to_cpup(async);
+ if (opal_max_async_tokens > N_ASYNC_COMPLETIONS)
+ opal_max_async_tokens = N_ASYNC_COMPLETIONS;
+
+ err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP,
+ &opal_async_comp_nb);
+ if (err) {
+ pr_err("%s: Can't register OPAL event notifier (%d)\n",
+ __func__, err);
+ goto out_opal_node;
+ }
+
+ opal_async_responses = kzalloc(
+ sizeof(*opal_async_responses) * opal_max_async_tokens,
+ GFP_KERNEL);
+ if (!opal_async_responses) {
+ pr_err("%s: Out of memory, failed to do asynchronous "
+ "completion init\n", __func__);
+ err = -ENOMEM;
+ goto out_opal_node;
+ }
+
+ /* Initialize to 1 less than the maximum tokens available, as we may
+ * require to pop one during emergency through synchronous call to
+ * __opal_async_get_token()
+ */
+ sema_init(&opal_async_sem, opal_max_async_tokens - 1);
+
+out_opal_node:
+ of_node_put(opal_node);
+out:
+ return err;
+}
+subsys_initcall(opal_async_comp_init);