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 27 static DECLARE_BITMAP(opal_async_complete_map, N_ASYNC_COMPLETIONS) = {~0UL}; 28 static DECLARE_BITMAP(opal_async_token_map, N_ASYNC_COMPLETIONS); 29 static DECLARE_WAIT_QUEUE_HEAD(opal_async_wait); 30 static DEFINE_SPINLOCK(opal_async_comp_lock); 31 static struct semaphore opal_async_sem; 32 static struct opal_msg *opal_async_responses; 33 static unsigned int opal_max_async_tokens; 34 35 int __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 54 out: 55 spin_unlock_irqrestore(&opal_async_comp_lock, flags); 56 return token; 57 } 58 59 int 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 74 int __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 92 int 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 105 int 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 123 static 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 uint64_t token; 129 130 if (msg_type != OPAL_MSG_ASYNC_COMP) 131 return 0; 132 133 token = be64_to_cpu(comp_msg->params[0]); 134 memcpy(&opal_async_responses[token], comp_msg, sizeof(*comp_msg)); 135 spin_lock_irqsave(&opal_async_comp_lock, flags); 136 __set_bit(token, opal_async_complete_map); 137 spin_unlock_irqrestore(&opal_async_comp_lock, flags); 138 139 wake_up(&opal_async_wait); 140 141 return 0; 142 } 143 144 static struct notifier_block opal_async_comp_nb = { 145 .notifier_call = opal_async_comp_event, 146 .next = NULL, 147 .priority = 0, 148 }; 149 150 static int __init opal_async_comp_init(void) 151 { 152 struct device_node *opal_node; 153 const __be32 *async; 154 int err; 155 156 opal_node = of_find_node_by_path("/ibm,opal"); 157 if (!opal_node) { 158 pr_err("%s: Opal node not found\n", __func__); 159 err = -ENOENT; 160 goto out; 161 } 162 163 async = of_get_property(opal_node, "opal-msg-async-num", NULL); 164 if (!async) { 165 pr_err("%s: %s has no opal-msg-async-num\n", 166 __func__, opal_node->full_name); 167 err = -ENOENT; 168 goto out_opal_node; 169 } 170 171 opal_max_async_tokens = be32_to_cpup(async); 172 if (opal_max_async_tokens > N_ASYNC_COMPLETIONS) 173 opal_max_async_tokens = N_ASYNC_COMPLETIONS; 174 175 err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP, 176 &opal_async_comp_nb); 177 if (err) { 178 pr_err("%s: Can't register OPAL event notifier (%d)\n", 179 __func__, err); 180 goto out_opal_node; 181 } 182 183 opal_async_responses = kzalloc( 184 sizeof(*opal_async_responses) * opal_max_async_tokens, 185 GFP_KERNEL); 186 if (!opal_async_responses) { 187 pr_err("%s: Out of memory, failed to do asynchronous " 188 "completion init\n", __func__); 189 err = -ENOMEM; 190 goto out_opal_node; 191 } 192 193 /* Initialize to 1 less than the maximum tokens available, as we may 194 * require to pop one during emergency through synchronous call to 195 * __opal_async_get_token() 196 */ 197 sema_init(&opal_async_sem, opal_max_async_tokens - 1); 198 199 out_opal_node: 200 of_node_put(opal_node); 201 out: 202 return err; 203 } 204 subsys_initcall(opal_async_comp_init); 205