aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeelesh Gupta <neelegup@linux.vnet.ibm.com>2014-03-07 00:30:24 -0500
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-03-23 18:45:22 -0400
commit8d724823220862ce289be3b50119235e03537597 (patch)
tree9f9ac9103c18b2947c4de4dfba09e62c722a7089
parentc7e64b9ce04aa2e3fad7396d92b5cb92056d16ac (diff)
powerpc/powernv: Infrastructure to support OPAL async completion
This patch adds support for notifying the clients of their request completion. Clients request for the token before making OPAL call and then wait for the response. This patch uses messaging infrastructure to pull the data to linux by registering itself for the message type OPAL_MSG_ASYNC_COMP. Signed-off-by: Neelesh Gupta <neelegup@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/include/asm/opal.h12
-rw-r--r--arch/powerpc/platforms/powernv/Makefile2
-rw-r--r--arch/powerpc/platforms/powernv/opal-async.c203
3 files changed, 215 insertions, 2 deletions
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 2636acfcd340..1e186ff55f3e 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -83,6 +83,8 @@ extern int opal_enter_rtas(struct rtas_args *args,
83#define OPAL_INTERNAL_ERROR -11 83#define OPAL_INTERNAL_ERROR -11
84#define OPAL_BUSY_EVENT -12 84#define OPAL_BUSY_EVENT -12
85#define OPAL_HARDWARE_FROZEN -13 85#define OPAL_HARDWARE_FROZEN -13
86#define OPAL_WRONG_STATE -14
87#define OPAL_ASYNC_COMPLETION -15
86 88
87/* API Tokens (in r0) */ 89/* API Tokens (in r0) */
88#define OPAL_CONSOLE_WRITE 1 90#define OPAL_CONSOLE_WRITE 1
@@ -253,7 +255,9 @@ enum OpalPendingState {
253}; 255};
254 256
255enum OpalMessageType { 257enum OpalMessageType {
256 OPAL_MSG_ASYNC_COMP = 0, 258 OPAL_MSG_ASYNC_COMP = 0, /* params[0] = token, params[1] = rc,
259 * additional params function-specific
260 */
257 OPAL_MSG_MEM_ERR, 261 OPAL_MSG_MEM_ERR,
258 OPAL_MSG_EPOW, 262 OPAL_MSG_EPOW,
259 OPAL_MSG_SHUTDOWN, 263 OPAL_MSG_SHUTDOWN,
@@ -880,6 +884,12 @@ extern void opal_notifier_update_evt(uint64_t evt_mask, uint64_t evt_val);
880extern int opal_get_chars(uint32_t vtermno, char *buf, int count); 884extern int opal_get_chars(uint32_t vtermno, char *buf, int count);
881extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len); 885extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len);
882 886
887extern int __opal_async_get_token(void);
888extern int opal_async_get_token_interruptible(void);
889extern int __opal_async_release_token(int token);
890extern int opal_async_release_token(int token);
891extern int opal_async_wait_response(uint64_t token, struct opal_msg *msg);
892
883extern void hvc_opal_init_early(void); 893extern void hvc_opal_init_early(void);
884 894
885struct rtc_time; 895struct rtc_time;
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index 5125caeb40f4..3f9309c4218c 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,4 +1,4 @@
1obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o 1obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o opal-async.o
2obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o 2obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
3obj-y += rng.o opal-elog.o opal-dump.o 3obj-y += rng.o opal-elog.o opal-dump.o
4 4
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 @@
1/*
2 * PowerNV OPAL asynchronous completion interfaces
3 *
4 * Copyright 2013 IBM Corp.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#undef DEBUG
13
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/slab.h>
17#include <linux/sched.h>
18#include <linux/semaphore.h>
19#include <linux/spinlock.h>
20#include <linux/wait.h>
21#include <linux/gfp.h>
22#include <linux/of.h>
23#include <asm/opal.h>
24
25#define N_ASYNC_COMPLETIONS 64
26
27static DECLARE_BITMAP(opal_async_complete_map, N_ASYNC_COMPLETIONS) = {~0UL};
28static DECLARE_BITMAP(opal_async_token_map, N_ASYNC_COMPLETIONS);
29static DECLARE_WAIT_QUEUE_HEAD(opal_async_wait);
30static DEFINE_SPINLOCK(opal_async_comp_lock);
31static struct semaphore opal_async_sem;
32static struct opal_msg *opal_async_responses;
33static unsigned int opal_max_async_tokens;
34
35int __opal_async_get_token(void)
36{
37 unsigned long flags;
38 int token;
39
40 spin_lock_irqsave(&opal_async_comp_lock, flags);
41 token = find_first_bit(opal_async_complete_map, opal_max_async_tokens);
42 if (token >= opal_max_async_tokens) {
43 token = -EBUSY;
44 goto out;
45 }
46
47 if (__test_and_set_bit(token, opal_async_token_map)) {
48 token = -EBUSY;
49 goto out;
50 }
51
52 __clear_bit(token, opal_async_complete_map);
53
54out:
55 spin_unlock_irqrestore(&opal_async_comp_lock, flags);
56 return token;
57}
58
59int opal_async_get_token_interruptible(void)
60{
61 int token;
62
63 /* Wait until a token is available */
64 if (down_interruptible(&opal_async_sem))
65 return -ERESTARTSYS;
66
67 token = __opal_async_get_token();
68 if (token < 0)
69 up(&opal_async_sem);
70
71 return token;
72}
73
74int __opal_async_release_token(int token)
75{
76 unsigned long flags;
77
78 if (token < 0 || token >= opal_max_async_tokens) {
79 pr_err("%s: Passed token is out of range, token %d\n",
80 __func__, token);
81 return -EINVAL;
82 }
83
84 spin_lock_irqsave(&opal_async_comp_lock, flags);
85 __set_bit(token, opal_async_complete_map);
86 __clear_bit(token, opal_async_token_map);
87 spin_unlock_irqrestore(&opal_async_comp_lock, flags);
88
89 return 0;
90}
91
92int opal_async_release_token(int token)
93{
94 int ret;
95
96 ret = __opal_async_release_token(token);
97 if (ret)
98 return ret;
99
100 up(&opal_async_sem);
101
102 return 0;
103}
104
105int opal_async_wait_response(uint64_t token, struct opal_msg *msg)
106{
107 if (token >= opal_max_async_tokens) {
108 pr_err("%s: Invalid token passed\n", __func__);
109 return -EINVAL;
110 }
111
112 if (!msg) {
113 pr_err("%s: Invalid message pointer passed\n", __func__);
114 return -EINVAL;
115 }
116
117 wait_event(opal_async_wait, test_bit(token, opal_async_complete_map));
118 memcpy(msg, &opal_async_responses[token], sizeof(*msg));
119
120 return 0;
121}
122
123static int opal_async_comp_event(struct notifier_block *nb,
124 unsigned long msg_type, void *msg)
125{
126 struct opal_msg *comp_msg = msg;
127 unsigned long flags;
128
129 if (msg_type != OPAL_MSG_ASYNC_COMP)
130 return 0;
131
132 memcpy(&opal_async_responses[comp_msg->params[0]], comp_msg,
133 sizeof(*comp_msg));
134 spin_lock_irqsave(&opal_async_comp_lock, flags);
135 __set_bit(comp_msg->params[0], opal_async_complete_map);
136 spin_unlock_irqrestore(&opal_async_comp_lock, flags);
137
138 wake_up(&opal_async_wait);
139
140 return 0;
141}
142
143static struct notifier_block opal_async_comp_nb = {
144 .notifier_call = opal_async_comp_event,
145 .next = NULL,
146 .priority = 0,
147};
148
149static int __init opal_async_comp_init(void)
150{
151 struct device_node *opal_node;
152 const __be32 *async;
153 int err;
154
155 opal_node = of_find_node_by_path("/ibm,opal");
156 if (!opal_node) {
157 pr_err("%s: Opal node not found\n", __func__);
158 err = -ENOENT;
159 goto out;
160 }
161
162 async = of_get_property(opal_node, "opal-msg-async-num", NULL);
163 if (!async) {
164 pr_err("%s: %s has no opal-msg-async-num\n",
165 __func__, opal_node->full_name);
166 err = -ENOENT;
167 goto out_opal_node;
168 }
169
170 opal_max_async_tokens = be32_to_cpup(async);
171 if (opal_max_async_tokens > N_ASYNC_COMPLETIONS)
172 opal_max_async_tokens = N_ASYNC_COMPLETIONS;
173
174 err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP,
175 &opal_async_comp_nb);
176 if (err) {
177 pr_err("%s: Can't register OPAL event notifier (%d)\n",
178 __func__, err);
179 goto out_opal_node;
180 }
181
182 opal_async_responses = kzalloc(
183 sizeof(*opal_async_responses) * opal_max_async_tokens,
184 GFP_KERNEL);
185 if (!opal_async_responses) {
186 pr_err("%s: Out of memory, failed to do asynchronous "
187 "completion init\n", __func__);
188 err = -ENOMEM;
189 goto out_opal_node;
190 }
191
192 /* Initialize to 1 less than the maximum tokens available, as we may
193 * require to pop one during emergency through synchronous call to
194 * __opal_async_get_token()
195 */
196 sema_init(&opal_async_sem, opal_max_async_tokens - 1);
197
198out_opal_node:
199 of_node_put(opal_node);
200out:
201 return err;
202}
203subsys_initcall(opal_async_comp_init);