linux/drivers/mfd/vexpress-config.c
<<
>>
Prefs
   1/*
   2 * This program is free software; you can redistribute it and/or modify
   3 * it under the terms of the GNU General Public License version 2 as
   4 * published by the Free Software Foundation.
   5 *
   6 * This program is distributed in the hope that it will be useful,
   7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
   8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   9 * GNU General Public License for more details.
  10 *
  11 * Copyright (C) 2012 ARM Limited
  12 */
  13
  14#define pr_fmt(fmt) "vexpress-config: " fmt
  15
  16#include <linux/bitops.h>
  17#include <linux/completion.h>
  18#include <linux/export.h>
  19#include <linux/init.h>
  20#include <linux/list.h>
  21#include <linux/of.h>
  22#include <linux/of_device.h>
  23#include <linux/slab.h>
  24#include <linux/string.h>
  25#include <linux/vexpress.h>
  26
  27
  28#define VEXPRESS_CONFIG_MAX_BRIDGES 2
  29
  30struct vexpress_config_bridge {
  31        struct device_node *node;
  32        struct vexpress_config_bridge_info *info;
  33        struct list_head transactions;
  34        spinlock_t transactions_lock;
  35} vexpress_config_bridges[VEXPRESS_CONFIG_MAX_BRIDGES];
  36
  37static DECLARE_BITMAP(vexpress_config_bridges_map,
  38                ARRAY_SIZE(vexpress_config_bridges));
  39static DEFINE_MUTEX(vexpress_config_bridges_mutex);
  40
  41struct vexpress_config_bridge *vexpress_config_bridge_register(
  42                struct device_node *node,
  43                struct vexpress_config_bridge_info *info)
  44{
  45        struct vexpress_config_bridge *bridge;
  46        int i;
  47
  48        pr_debug("Registering bridge '%s'\n", info->name);
  49
  50        mutex_lock(&vexpress_config_bridges_mutex);
  51        i = find_first_zero_bit(vexpress_config_bridges_map,
  52                        ARRAY_SIZE(vexpress_config_bridges));
  53        if (i >= ARRAY_SIZE(vexpress_config_bridges)) {
  54                pr_err("Can't register more bridges!\n");
  55                mutex_unlock(&vexpress_config_bridges_mutex);
  56                return NULL;
  57        }
  58        __set_bit(i, vexpress_config_bridges_map);
  59        bridge = &vexpress_config_bridges[i];
  60
  61        bridge->node = node;
  62        bridge->info = info;
  63        INIT_LIST_HEAD(&bridge->transactions);
  64        spin_lock_init(&bridge->transactions_lock);
  65
  66        mutex_unlock(&vexpress_config_bridges_mutex);
  67
  68        return bridge;
  69}
  70EXPORT_SYMBOL(vexpress_config_bridge_register);
  71
  72void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge)
  73{
  74        struct vexpress_config_bridge __bridge = *bridge;
  75        int i;
  76
  77        mutex_lock(&vexpress_config_bridges_mutex);
  78        for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++)
  79                if (&vexpress_config_bridges[i] == bridge)
  80                        __clear_bit(i, vexpress_config_bridges_map);
  81        mutex_unlock(&vexpress_config_bridges_mutex);
  82
  83        WARN_ON(!list_empty(&__bridge.transactions));
  84        while (!list_empty(&__bridge.transactions))
  85                cpu_relax();
  86}
  87EXPORT_SYMBOL(vexpress_config_bridge_unregister);
  88
  89
  90struct vexpress_config_func {
  91        struct vexpress_config_bridge *bridge;
  92        void *func;
  93};
  94
  95struct vexpress_config_func *__vexpress_config_func_get(struct device *dev,
  96                struct device_node *node)
  97{
  98        struct device_node *bridge_node;
  99        struct vexpress_config_func *func;
 100        int i;
 101
 102        if (WARN_ON(dev && node && dev->of_node != node))
 103                return NULL;
 104        if (dev && !node)
 105                node = dev->of_node;
 106
 107        func = kzalloc(sizeof(*func), GFP_KERNEL);
 108        if (!func)
 109                return NULL;
 110
 111        bridge_node = of_node_get(node);
 112        while (bridge_node) {
 113                const __be32 *prop = of_get_property(bridge_node,
 114                                "arm,vexpress,config-bridge", NULL);
 115
 116                if (prop) {
 117                        bridge_node = of_find_node_by_phandle(
 118                                        be32_to_cpup(prop));
 119                        break;
 120                }
 121
 122                bridge_node = of_get_next_parent(bridge_node);
 123        }
 124
 125        mutex_lock(&vexpress_config_bridges_mutex);
 126        for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) {
 127                struct vexpress_config_bridge *bridge =
 128                                &vexpress_config_bridges[i];
 129
 130                if (test_bit(i, vexpress_config_bridges_map) &&
 131                                bridge->node == bridge_node) {
 132                        func->bridge = bridge;
 133                        func->func = bridge->info->func_get(dev, node);
 134                        break;
 135                }
 136        }
 137        mutex_unlock(&vexpress_config_bridges_mutex);
 138
 139        if (!func->func) {
 140                of_node_put(node);
 141                kfree(func);
 142                return NULL;
 143        }
 144
 145        return func;
 146}
 147EXPORT_SYMBOL(__vexpress_config_func_get);
 148
 149void vexpress_config_func_put(struct vexpress_config_func *func)
 150{
 151        func->bridge->info->func_put(func->func);
 152        of_node_put(func->bridge->node);
 153        kfree(func);
 154}
 155EXPORT_SYMBOL(vexpress_config_func_put);
 156
 157struct vexpress_config_trans {
 158        struct vexpress_config_func *func;
 159        int offset;
 160        bool write;
 161        u32 *data;
 162        int status;
 163        struct completion completion;
 164        struct list_head list;
 165};
 166
 167static void vexpress_config_dump_trans(const char *what,
 168                struct vexpress_config_trans *trans)
 169{
 170        pr_debug("%s %s trans %p func 0x%p offset %d data 0x%x status %d\n",
 171                        what, trans->write ? "write" : "read", trans,
 172                        trans->func->func, trans->offset,
 173                        trans->data ? *trans->data : 0, trans->status);
 174}
 175
 176static int vexpress_config_schedule(struct vexpress_config_trans *trans)
 177{
 178        int status;
 179        struct vexpress_config_bridge *bridge = trans->func->bridge;
 180        unsigned long flags;
 181
 182        init_completion(&trans->completion);
 183        trans->status = -EFAULT;
 184
 185        spin_lock_irqsave(&bridge->transactions_lock, flags);
 186
 187        if (list_empty(&bridge->transactions)) {
 188                vexpress_config_dump_trans("Executing", trans);
 189                status = bridge->info->func_exec(trans->func->func,
 190                                trans->offset, trans->write, trans->data);
 191        } else {
 192                vexpress_config_dump_trans("Queuing", trans);
 193                status = VEXPRESS_CONFIG_STATUS_WAIT;
 194        }
 195
 196        switch (status) {
 197        case VEXPRESS_CONFIG_STATUS_DONE:
 198                vexpress_config_dump_trans("Finished", trans);
 199                trans->status = status;
 200                break;
 201        case VEXPRESS_CONFIG_STATUS_WAIT:
 202                list_add_tail(&trans->list, &bridge->transactions);
 203                break;
 204        }
 205
 206        spin_unlock_irqrestore(&bridge->transactions_lock, flags);
 207
 208        return status;
 209}
 210
 211void vexpress_config_complete(struct vexpress_config_bridge *bridge,
 212                int status)
 213{
 214        struct vexpress_config_trans *trans;
 215        unsigned long flags;
 216        const char *message = "Completed";
 217
 218        spin_lock_irqsave(&bridge->transactions_lock, flags);
 219
 220        trans = list_first_entry(&bridge->transactions,
 221                        struct vexpress_config_trans, list);
 222        trans->status = status;
 223
 224        do {
 225                vexpress_config_dump_trans(message, trans);
 226                list_del(&trans->list);
 227                complete(&trans->completion);
 228
 229                if (list_empty(&bridge->transactions))
 230                        break;
 231
 232                trans = list_first_entry(&bridge->transactions,
 233                                struct vexpress_config_trans, list);
 234                vexpress_config_dump_trans("Executing pending", trans);
 235                trans->status = bridge->info->func_exec(trans->func->func,
 236                                trans->offset, trans->write, trans->data);
 237                message = "Finished pending";
 238        } while (trans->status == VEXPRESS_CONFIG_STATUS_DONE);
 239
 240        spin_unlock_irqrestore(&bridge->transactions_lock, flags);
 241}
 242EXPORT_SYMBOL(vexpress_config_complete);
 243
 244int vexpress_config_wait(struct vexpress_config_trans *trans)
 245{
 246        wait_for_completion(&trans->completion);
 247
 248        return trans->status;
 249}
 250EXPORT_SYMBOL(vexpress_config_wait);
 251
 252int vexpress_config_read(struct vexpress_config_func *func, int offset,
 253                u32 *data)
 254{
 255        struct vexpress_config_trans trans = {
 256                .func = func,
 257                .offset = offset,
 258                .write = false,
 259                .data = data,
 260                .status = 0,
 261        };
 262        int status = vexpress_config_schedule(&trans);
 263
 264        if (status == VEXPRESS_CONFIG_STATUS_WAIT)
 265                status = vexpress_config_wait(&trans);
 266
 267        return status;
 268}
 269EXPORT_SYMBOL(vexpress_config_read);
 270
 271int vexpress_config_write(struct vexpress_config_func *func, int offset,
 272                u32 data)
 273{
 274        struct vexpress_config_trans trans = {
 275                .func = func,
 276                .offset = offset,
 277                .write = true,
 278                .data = &data,
 279                .status = 0,
 280        };
 281        int status = vexpress_config_schedule(&trans);
 282
 283        if (status == VEXPRESS_CONFIG_STATUS_WAIT)
 284                status = vexpress_config_wait(&trans);
 285
 286        return status;
 287}
 288EXPORT_SYMBOL(vexpress_config_write);
 289
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.