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 75 int __opal_async_release_token(int token) 76 { 77 unsigned long flags; 78 79 if (token < 0 || token >= opal_max_async_tokens) { 80 pr_err("%s: Passed token is out of range, token %d\n", 81 __func__, token); 82 return -EINVAL; 83 } 84 85 spin_lock_irqsave(&opal_async_comp_lock, flags); 86 __set_bit(token, opal_async_complete_map); 87 __clear_bit(token, opal_async_token_map); 88 spin_unlock_irqrestore(&opal_async_comp_lock, flags); 89 90 return 0; 91 } 92 93 int opal_async_release_token(int token) 94 { 95 int ret; 96 97 ret = __opal_async_release_token(token); 98 if (ret) 99 return ret; 100 101 up(&opal_async_sem); 102 103 return 0; 104 } 105 106 int opal_async_wait_response(uint64_t token, struct opal_msg *msg) 107 { 108 if (token >= opal_max_async_tokens) { 109 pr_err("%s: Invalid token passed\n", __func__); 110 return -EINVAL; 111 } 112 113 if (!msg) { 114 pr_err("%s: Invalid message pointer passed\n", __func__); 115 return -EINVAL; 116 } 117 118 wait_event(opal_async_wait, test_bit(token, opal_async_complete_map)); 119 memcpy(msg, &opal_async_responses[token], sizeof(*msg)); 120 121 return 0; 122 } 123 124 static int opal_async_comp_event(struct notifier_block *nb, 125 unsigned long msg_type, void *msg) 126 { 127 struct opal_msg *comp_msg = msg; 128 unsigned long flags; 129 uint64_t token; 130 131 if (msg_type != OPAL_MSG_ASYNC_COMP) 132 return 0; 133 134 token = be64_to_cpu(comp_msg->params[0]); 135 memcpy(&opal_async_responses[token], comp_msg, sizeof(*comp_msg)); 136 spin_lock_irqsave(&opal_async_comp_lock, flags); 137 __set_bit(token, opal_async_complete_map); 138 spin_unlock_irqrestore(&opal_async_comp_lock, flags); 139 140 wake_up(&opal_async_wait); 141 142 return 0; 143 } 144 145 static struct notifier_block opal_async_comp_nb = { 146 .notifier_call = opal_async_comp_event, 147 .next = NULL, 148 .priority = 0, 149 }; 150 151 static int __init opal_async_comp_init(void) 152 { 153 struct device_node *opal_node; 154 const __be32 *async; 155 int err; 156 157 opal_node = of_find_node_by_path("/ibm,opal"); 158 if (!opal_node) { 159 pr_err("%s: Opal node not found\n", __func__); 160 err = -ENOENT; 161 goto out; 162 } 163 164 async = of_get_property(opal_node, "opal-msg-async-num", NULL); 165 if (!async) { 166 pr_err("%s: %s has no opal-msg-async-num\n", 167 __func__, opal_node->full_name); 168 err = -ENOENT; 169 goto out_opal_node; 170 } 171 172 opal_max_async_tokens = be32_to_cpup(async); 173 if (opal_max_async_tokens > N_ASYNC_COMPLETIONS) 174 opal_max_async_tokens = N_ASYNC_COMPLETIONS; 175 176 err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP, 177 &opal_async_comp_nb); 178 if (err) { 179 pr_err("%s: Can't register OPAL event notifier (%d)\n", 180 __func__, err); 181 goto out_opal_node; 182 } 183 184 opal_async_responses = kzalloc( 185 sizeof(*opal_async_responses) * opal_max_async_tokens, 186 GFP_KERNEL); 187 if (!opal_async_responses) { 188 pr_err("%s: Out of memory, failed to do asynchronous " 189 "completion init\n", __func__); 190 err = -ENOMEM; 191 goto out_opal_node; 192 } 193 194 /* Initialize to 1 less than the maximum tokens available, as we may 195 * require to pop one during emergency through synchronous call to 196 * __opal_async_get_token() 197 */ 198 sema_init(&opal_async_sem, opal_max_async_tokens - 1); 199 200 out_opal_node: 201 of_node_put(opal_node); 202 out: 203 return err; 204 } 205 machine_subsys_initcall(powernv, opal_async_comp_init); 206