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        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}
 125EXPORT_SYMBOL_GPL(opal_async_wait_response);
 126
 127static 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
 148static struct notifier_block opal_async_comp_nb = {
 149                .notifier_call  = opal_async_comp_event,
 150                .next           = NULL,
 151                .priority       = 0,
 152};
 153
 154static 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
 203out_opal_node:
 204        of_node_put(opal_node);
 205out:
 206        return err;
 207}
 208machine_subsys_initcall(powernv, opal_async_comp_init);
 209