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 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 143 static struct notifier_block opal_async_comp_nb = { 144 .notifier_call = opal_async_comp_event, 145 .next = NULL, 146 .priority = 0, 147 }; 148 149 static 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 198 out_opal_node: 199 of_node_put(opal_node); 200 out: 201 return err; 202 } 203 subsys_initcall(opal_async_comp_init); 204