linux/arch/powerpc/platforms/powernv/opal-async.c
<<
>>
Prefs
   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
  28static DECLARE_BITMAP(opal_async_complete_map, N_ASYNC_COMPLETIONS) = {~0UL};
  29static DECLARE_BITMAP(opal_async_token_map, N_ASYNC_COMPLETIONS);
  30static DECLARE_WAIT_QUEUE_HEAD(opal_async_wait);
  31static DEFINE_SPINLOCK(opal_async_comp_lock);
  32static struct semaphore opal_async_sem;
  33static struct opal_msg *opal_async_responses;
  34static unsigned int opal_max_async_tokens;
  35
  36int __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
  55out:
  56        spin_unlock_irqrestore(&opal_async_comp_lock, flags);
  57        return token;
  58}
  59
  60int 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}
  74EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible);
  75
  76int __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
  94int 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}
 106EXPORT_SYMBOL_GPL(opal_async_release_token);
 107
 108int 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        /* Wakeup the poller before we wait for events to speed things
 121         * up on platforms or simulators where the interrupts aren't
 122         * functional.
 123         */
 124        opal_wake_poller();
 125        wait_event(opal_async_wait, test_bit(token, opal_async_complete_map));
 126        memcpy(msg, &opal_async_responses[token], sizeof(*msg));
 127
 128        return 0;
 129}
 130EXPORT_SYMBOL_GPL(opal_async_wait_response);
 131
 132static int opal_async_comp_event(struct notifier_block *nb,
 133                unsigned long msg_type, void *msg)
 134{
 135        struct opal_msg *comp_msg = msg;
 136        unsigned long flags;
 137        uint64_t token;
 138
 139        if (msg_type != OPAL_MSG_ASYNC_COMP)
 140                return 0;
 141
 142        token = be64_to_cpu(comp_msg->params[0]);
 143        memcpy(&opal_async_responses[token], comp_msg, sizeof(*comp_msg));
 144        spin_lock_irqsave(&opal_async_comp_lock, flags);
 145        __set_bit(token, opal_async_complete_map);
 146        spin_unlock_irqrestore(&opal_async_comp_lock, flags);
 147
 148        wake_up(&opal_async_wait);
 149
 150        return 0;
 151}
 152
 153static struct notifier_block opal_async_comp_nb = {
 154                .notifier_call  = opal_async_comp_event,
 155                .next           = NULL,
 156                .priority       = 0,
 157};
 158
 159int __init opal_async_comp_init(void)
 160{
 161        struct device_node *opal_node;
 162        const __be32 *async;
 163        int err;
 164
 165        opal_node = of_find_node_by_path("/ibm,opal");
 166        if (!opal_node) {
 167                pr_err("%s: Opal node not found\n", __func__);
 168                err = -ENOENT;
 169                goto out;
 170        }
 171
 172        async = of_get_property(opal_node, "opal-msg-async-num", NULL);
 173        if (!async) {
 174                pr_err("%s: %s has no opal-msg-async-num\n",
 175                                __func__, opal_node->full_name);
 176                err = -ENOENT;
 177                goto out_opal_node;
 178        }
 179
 180        opal_max_async_tokens = be32_to_cpup(async);
 181        if (opal_max_async_tokens > N_ASYNC_COMPLETIONS)
 182                opal_max_async_tokens = N_ASYNC_COMPLETIONS;
 183
 184        err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP,
 185                        &opal_async_comp_nb);
 186        if (err) {
 187                pr_err("%s: Can't register OPAL event notifier (%d)\n",
 188                                __func__, err);
 189                goto out_opal_node;
 190        }
 191
 192        opal_async_responses = kzalloc(
 193                        sizeof(*opal_async_responses) * opal_max_async_tokens,
 194                        GFP_KERNEL);
 195        if (!opal_async_responses) {
 196                pr_err("%s: Out of memory, failed to do asynchronous "
 197                                "completion init\n", __func__);
 198                err = -ENOMEM;
 199                goto out_opal_node;
 200        }
 201
 202        /* Initialize to 1 less than the maximum tokens available, as we may
 203         * require to pop one during emergency through synchronous call to
 204         * __opal_async_get_token()
 205         */
 206        sema_init(&opal_async_sem, opal_max_async_tokens - 1);
 207
 208out_opal_node:
 209        of_node_put(opal_node);
 210out:
 211        return err;
 212}
 213