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/machdep.h> 24 #include <asm/opal.h> 25 26 #define N_ASYNC_COMPLETIONS 64 27 28 static DECLARE_BITMAP(opal_async_complete_map, N_ASYNC_COMPLETIONS) = {~0UL}; 29 static DECLARE_BITMAP(opal_async_token_map, N_ASYNC_COMPLETIONS); 30 static DECLARE_WAIT_QUEUE_HEAD(opal_async_wait); 31 static DEFINE_SPINLOCK(opal_async_comp_lock); 32 static struct semaphore opal_async_sem; 33 static struct opal_msg *opal_async_responses; 34 static unsigned int opal_max_async_tokens; 35 36 int __opal_async_get_token(void) 37 { 38 unsigned long flags; 39 int token; 40 41 spin_lock_irqsave(&opal_async_comp_lock, flags); 42 token = find_first_bit(opal_async_complete_map, opal_max_async_tokens); 43 if (token >= opal_max_async_tokens) { 44 token = -EBUSY; 45 goto out; 46 } 47 48 if (__test_and_set_bit(token, opal_async_token_map)) { 49 token = -EBUSY; 50 goto out; 51 } 52 53 __clear_bit(token, opal_async_complete_map); 54 55 out: 56 spin_unlock_irqrestore(&opal_async_comp_lock, flags); 57 return token; 58 } 59 60 int opal_async_get_token_interruptible(void) 61 { 62 int token; 63 64 /* Wait until a token is available */ 65 if (down_interruptible(&opal_async_sem)) 66 return -ERESTARTSYS; 67 68 token = __opal_async_get_token(); 69 if (token < 0) 70 up(&opal_async_sem); 71 72 return token; 73 } 74 EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible); 75 76 int __opal_async_release_token(int token) 77 { 78 unsigned long flags; 79 80 if (token < 0 || token >= opal_max_async_tokens) { 81 pr_err("%s: Passed token is out of range, token %d\n", 82 __func__, token); 83 return -EINVAL; 84 } 85 86 spin_lock_irqsave(&opal_async_comp_lock, flags); 87 __set_bit(token, opal_async_complete_map); 88 __clear_bit(token, opal_async_token_map); 89 spin_unlock_irqrestore(&opal_async_comp_lock, flags); 90 91 return 0; 92 } 93 94 int opal_async_release_token(int token) 95 { 96 int ret; 97 98 ret = __opal_async_release_token(token); 99 if (ret) 100 return ret; 101 102 up(&opal_async_sem); 103 104 return 0; 105 } 106 EXPORT_SYMBOL_GPL(opal_async_release_token); 107 108 int opal_async_wait_response(uint64_t token, struct opal_msg *msg) 109 { 110 if (token >= opal_max_async_tokens) { 111 pr_err("%s: Invalid token passed\n", __func__); 112 return -EINVAL; 113 } 114 115 if (!msg) { 116 pr_err("%s: Invalid message pointer passed\n", __func__); 117 return -EINVAL; 118 } 119 120 wait_event(opal_async_wait, test_bit(token, opal_async_complete_map)); 121 memcpy(msg, &opal_async_responses[token], sizeof(*msg)); 122 123 return 0; 124 } 125 EXPORT_SYMBOL_GPL(opal_async_wait_response); 126 127 static int opal_async_comp_event(struct notifier_block *nb, 128 unsigned long msg_type, void *msg) 129 { 130 struct opal_msg *comp_msg = msg; 131 unsigned long flags; 132 uint64_t token; 133 134 if (msg_type != OPAL_MSG_ASYNC_COMP) 135 return 0; 136 137 token = be64_to_cpu(comp_msg->params[0]); 138 memcpy(&opal_async_responses[token], comp_msg, sizeof(*comp_msg)); 139 spin_lock_irqsave(&opal_async_comp_lock, flags); 140 __set_bit(token, opal_async_complete_map); 141 spin_unlock_irqrestore(&opal_async_comp_lock, flags); 142 143 wake_up(&opal_async_wait); 144 145 return 0; 146 } 147 148 static struct notifier_block opal_async_comp_nb = { 149 .notifier_call = opal_async_comp_event, 150 .next = NULL, 151 .priority = 0, 152 }; 153 154 int __init opal_async_comp_init(void) 155 { 156 struct device_node *opal_node; 157 const __be32 *async; 158 int err; 159 160 opal_node = of_find_node_by_path("/ibm,opal"); 161 if (!opal_node) { 162 pr_err("%s: Opal node not found\n", __func__); 163 err = -ENOENT; 164 goto out; 165 } 166 167 async = of_get_property(opal_node, "opal-msg-async-num", NULL); 168 if (!async) { 169 pr_err("%s: %s has no opal-msg-async-num\n", 170 __func__, opal_node->full_name); 171 err = -ENOENT; 172 goto out_opal_node; 173 } 174 175 opal_max_async_tokens = be32_to_cpup(async); 176 if (opal_max_async_tokens > N_ASYNC_COMPLETIONS) 177 opal_max_async_tokens = N_ASYNC_COMPLETIONS; 178 179 err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP, 180 &opal_async_comp_nb); 181 if (err) { 182 pr_err("%s: Can't register OPAL event notifier (%d)\n", 183 __func__, err); 184 goto out_opal_node; 185 } 186 187 opal_async_responses = kzalloc( 188 sizeof(*opal_async_responses) * opal_max_async_tokens, 189 GFP_KERNEL); 190 if (!opal_async_responses) { 191 pr_err("%s: Out of memory, failed to do asynchronous " 192 "completion init\n", __func__); 193 err = -ENOMEM; 194 goto out_opal_node; 195 } 196 197 /* Initialize to 1 less than the maximum tokens available, as we may 198 * require to pop one during emergency through synchronous call to 199 * __opal_async_get_token() 200 */ 201 sema_init(&opal_async_sem, opal_max_async_tokens - 1); 202 203 out_opal_node: 204 of_node_put(opal_node); 205 out: 206 return err; 207 } 208